Compare commits

...

3 Commits

Author SHA1 Message Date
cd91174f5d Better narration processing 2026-01-22 17:15:19 +03:00
4b24d85123 Action nodes support and lots of other updates 2026-01-19 00:07:03 +03:00
f86e7fd96c Furniture placement 2026-01-09 22:29:26 +03:00
50 changed files with 9769 additions and 3750 deletions

View File

@@ -77,6 +77,7 @@ add_subdirectory(src/tests)
add_subdirectory(src/physics)
add_subdirectory(src/editor)
add_subdirectory(assets/blender/buildings/parts)
add_subdirectory(resources)
add_executable(Game Game.cpp ${WATER_SRC})
target_include_directories(Game PRIVATE src/gamedata)
@@ -203,34 +204,8 @@ add_custom_target(stage_stories ALL DEPENDS stage_lua_scripts ${CMAKE_BINARY_DIR
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/resources.cfg
COMMAND cp ${CMAKE_SOURCE_DIR}/resources.cfg ${CMAKE_BINARY_DIR}/resources.cfg
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources/main
${CMAKE_BINARY_DIR}/resources/main
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources/shaderlib
${CMAKE_BINARY_DIR}/resources/shaderlib
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources/terrain
${CMAKE_BINARY_DIR}/resources/terrain
# COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/lua-scripts
# ${CMAKE_BINARY_DIR}/lua-scripts
# COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/skybox
# ${CMAKE_BINARY_DIR}/skybox
# COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/resources/debug
# ${CMAKE_BINARY_DIR}/resources/debug
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/resources.cfg ${CMAKE_BINARY_DIR}/resources.cfg
DEPENDS ${CMAKE_SOURCE_DIR}/resources.cfg)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/resources/terrain/world_map.png
COMMAND unzip -o ${CMAKE_SOURCE_DIR}/world_map.kra mergedimage.png -d ${CMAKE_BINARY_DIR}/world_map
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_BINARY_DIR}/world_map/mergedimage.png
${CMAKE_BINARY_DIR}/resources/terrain/world_map.png
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/world_map
DEPENDS ${CMAKE_SOURCE_DIR}/world_map.kra)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/resources/terrain/brushes.png
COMMAND unzip -o ${CMAKE_SOURCE_DIR}/brushes.kra mergedimage.png -d ${CMAKE_BINARY_DIR}/brushes
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_BINARY_DIR}/brushes/mergedimage.png
${CMAKE_BINARY_DIR}/resources/terrain/brushes.png
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/brushes
DEPENDS ${CMAKE_SOURCE_DIR}/brushes.kra)
set(SKYBOX_SRC
early_morning_bk.jpg
@@ -356,29 +331,11 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E touch ${CHARACTER_GLBS}
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py ${VRM_IMPORTED_BLENDS} ${EDITED_BLEND_TARGETS}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
#add_custom_command(
# OUTPUT ${CMAKE_SOURCE_DIR}/characters/female/vroid-normal-female.scene
# ${CMAKE_SOURCE_DIR}/characters/male/vroid-normal-male.scene
# ${CMAKE_SOURCE_DIR}/assets/blender/vrm-vroid-normal-female.blend
# ${CMAKE_SOURCE_DIR}/assets/blender/vrm-vroid-normal-male.blend
# ${CMAKE_SOURCE_DIR}/assets/blender/shapes/male/vrm-vroid-normal-male-chibi.blend
# COMMAND mkdir -p ${CREATE_DIRECTORIES}
# COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/import_vrm.py
# COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py
## COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_ogre_scene.py
# COMMAND echo rm -Rf ${CMAKE_SOURCE_DIR}/characters/male/*.material ${CMAKE_SOURCE_DIR}/characters/female/*.material
# COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/characters
# ${CMAKE_BINARY_DIR}/characters
# DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py ${CMAKE_SOURCE_DIR}/assets/blender/scripts/import_vrm.py
# ${CMAKE_SOURCE_DIR}/assets/vroid/buch1.vrm
# ${CMAKE_SOURCE_DIR}/assets/vroid/buch1-chibi.vrm
# ${CMAKE_SOURCE_DIR}/assets/vroid/jane2-dress.vrm
# WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
add_custom_target(stage_files ALL DEPENDS ${CMAKE_BINARY_DIR}/resources.cfg ${MATERIALS_OUTPUT}
${CMAKE_BINARY_DIR}/resources/terrain/world_map.png
${CMAKE_BINARY_DIR}/resources/terrain/brushes.png
edited-blends)
edited-blends stage_resources)
add_custom_target(remove_scenes COMMAND rm -f ${VRM_SOURCE} ${VRM_IMPORTED_BLENDS} ${CHARACTER_GLBS})

View File

@@ -15,7 +15,7 @@
#include "Components.h"
#include "CharacterModule.h"
#include "TerrainModule.h"
#include "GUIModule.h"
#include "GUIModuleCommon.h"
#include "AppModule.h"
#include "sound.h"
class App;
@@ -236,15 +236,14 @@ public:
else if (key == 'f')
control |= 64;
if (key == 'w' || key == 'a' || key == 's' || key == 'd' ||
key == 'e' || key == OgreBites::SDLK_LSHIFT)
key == 'e' || key == OgreBites::SDLK_LSHIFT || key == 'e' ||
key == 'f')
return true;
return false;
}
bool keyReleased(const OgreBites::KeyboardEvent &evt) override
{
OgreBites::Keycode key = evt.keysym.sym;
if (isGuiEnabled())
return false;
if (key == 'w')
control &= ~1;
else if (key == 'a')
@@ -255,8 +254,14 @@ public:
control &= ~8;
else if (key == OgreBites::SDLK_LSHIFT)
control &= ~16;
if (key == 'w' || key == 'a' || key == 's' || key == 'd' ||
key == OgreBites::SDLK_LSHIFT)
else if (key == 'e')
control &= ~32;
else if (key == 'f')
control &= ~64;
if (isGuiEnabled())
return false;
if (key == 'w' || key == 'a' || key == 's' || key == 'd' ||
key == OgreBites::SDLK_LSHIFT || key == 'e' || key == 'f')
return true;
return false;
}
@@ -474,14 +479,19 @@ public:
mDynWorld->getBtWorld()->stepSimulation(delta, 3);
#endif
if (!ECS::get().has<ECS::GUI>())
return;
/* Update window grab */
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();
if (gui.grabChanged) {
setWindowGrab(gui.grab);
gui.grabChanged = false;
ECS::get().modified<ECS::GUI>();
}
goto end;
{
/* Update window grab */
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();
if (gui.grabChanged) {
setWindowGrab(gui.grab);
gui.grabChanged = false;
ECS::get().modified<ECS::GUI>();
std::cout << "updateWorld " << gui.grabChanged
<< " " << gui.grab << std::endl;
}
}
end:
ECS::update(delta);
#if 0

View File

@@ -1,5 +1,6 @@
project(building-parts)
set(PARTS_FILES pier.blend)
set(FURNITURE_FILES furniture.blend furniture-sofa.blend)
set(PARTS_OUTPUT_DIRS)
foreach(PARTS_FILE ${PARTS_FILES})
get_filename_component(FILE_NAME ${PARTS_FILE} NAME_WE)
@@ -14,4 +15,17 @@ foreach(PARTS_FILE ${PARTS_FILES})
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${PARTS_FILE} ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_building_parts.py)
list(APPEND PARTS_OUTPUT_DIRS ${PARTS_OUTPUT_DIR})
endforeach()
foreach(FURNITURE_FILE ${FURNITURE_FILES})
get_filename_component(FILE_NAME ${FURNITURE_FILE} NAME_WE)
set(PARTS_OUTPUT_DIR ${CMAKE_BINARY_DIR}/resources/buildings/parts/${FILE_NAME})
add_custom_command(
OUTPUT ${PARTS_OUTPUT_DIR}
COMMAND ${CMAKE_COMMAND} -E make_directory ${PARTS_OUTPUT_DIR}
COMMAND ${BLENDER} ${CMAKE_CURRENT_SOURCE_DIR}/${FURNITURE_FILE}
-b -Y -P
${CMAKE_CURRENT_SOURCE_DIR}/export_furniture_parts.py
-- ${PARTS_OUTPUT_DIR}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${PARTS_FILE} ${CMAKE_CURRENT_SOURCE_DIR}/export_furniture_parts.py)
list(APPEND PARTS_OUTPUT_DIRS ${PARTS_OUTPUT_DIR})
endforeach()
add_custom_target(import_building_parts ALL DEPENDS ${PARTS_OUTPUT_DIRS})

View File

@@ -0,0 +1,182 @@
import bpy
import os, sys, time
import json
from mathutils import Matrix, Quaternion
argv = sys.argv
argv = argv[argv.index("--") + 1:]
incpath = os.path.dirname(__file__)
sys.path.insert(0, incpath)
outdir = argv[0]
basis_change = Matrix((
(1, 0, 0, 0),
(0, 0, 1, 0),
(0, -1, 0, 0),
(0, 0, 0, 1)
))
def export_root_objects_to_gltf(output_dir):
# Ensure the output directory exists
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# Deselect all objects
bpy.ops.object.select_all(action='DESELECT')
# Iterate through all objects in the scene
for obj in bpy.context.scene.objects:
# Check if the object has no parent and has children
# The original request specifies "objects having no parent with children".
# This condition captures those that are explicitly root objects of a hierarchy.
if obj.parent is None:
# Select the root object and all its children
print(obj.name)
if not obj.name.startswith("furniture-"):
continue
desc = {}
desc["name"] = obj.name.replace("furniture-","")
desc["mesh"] = obj.name + ".glb"
if "furniture_tags" in obj:
mtags = obj["furniture_tags"]
if "," in mtags:
atags = [m.strip() for m in mtags.split(",")]
desc["tags"] = atags
else:
desc["tags"] = [mtags]
else:
desc["tags"] = []
desc["sensors"] = []
desc["actions"] = []
desc["positions"] = []
for child in obj.children:
if child.name.startswith("action-"):
if not "action" in child:
continue
if not "height" in child:
continue
if not "radius" in child:
continue
local_matrix = child.matrix_local
ogre_local_matrix = basis_change @ local_matrix @ basis_change.inverted()
local_pos_d = ogre_local_matrix.to_translation()
local_rot_d = ogre_local_matrix.to_quaternion()
local_pos = [round(x, 4) for x in local_pos_d]
local_rot = [round(x, 4) for x in local_rot_d]
action = {}
action["position_x"] = local_pos[0]
action["position_y"] = local_pos[1]
action["position_z"] = local_pos[2]
action["rotation_w"] = local_rot[0]
action["rotation_x"] = local_rot[1]
action["rotation_y"] = local_rot[2]
action["rotation_z"] = local_rot[3]
action["height"] = child["height"]
action["radius"] = child["radius"]
action["action"] = child["action"]
if "action_text" in child:
action["action_text"] = child["action_text"]
else:
action["action_text"] = child["action"].capitalize()
if "name" in child:
action["name"] = child["name"]
else:
action["name"] = desc["name"] + "_" + str(len(desc["actions"]))
action["furniture"] = {}
action["furniture"]["name"] = desc["name"]
action["furniture"]["tags"] = desc["tags"]
action["positions"] = []
for schild in child.children:
if schild.name.startswith("position-"):
local_matrix = schild.matrix_local
ogre_local_matrix = basis_change @ local_matrix @ basis_change.inverted()
local_pos_d = ogre_local_matrix.to_translation()
local_rot_d = ogre_local_matrix.to_quaternion()
local_pos = [round(x, 4) for x in local_pos_d]
local_rot = [round(x, 4) for x in local_rot_d]
position = {}
if "name" in schild:
position["name"] = schild["name"]
if "type" in schild:
position["type"] = schild["type"]
position["position_x"] = local_pos[0]
position["position_y"] = local_pos[2]
position["position_z"] = local_pos[1]
position["rotation_w"] = local_rot[0]
position["rotation_x"] = local_rot[1]
position["rotation_y"] = local_rot[2]
position["rotation_z"] = local_rot[3]
action["positions"].append(position)
desc["actions"].append(action)
if child.name.startswith("position-"):
local_matrix = child.matrix_local
ogre_local_matrix = basis_change @ local_matrix @ basis_change.inverted()
local_pos_d = ogre_local_matrix.to_translation()
local_rot_d = ogre_local_matrix.to_quaternion()
local_pos = [round(x, 4) for x in local_pos_d]
local_rot = [round(x, 4) for x in local_rot_d]
position = {}
if "name" in child:
position["name"] = child["name"]
if "type" in child:
position["type"] = child["type"]
position["position_x"] = local_pos[0]
position["position_y"] = local_pos[2]
position["position_z"] = local_pos[1]
position["rotation_w"] = local_rot[0]
position["rotation_x"] = local_rot[1]
position["rotation_y"] = local_rot[2]
position["rotation_z"] = local_rot[3]
desc["positions"].append(position)
obj.select_set(True)
for child in obj.children_recursive:
child.select_set(True)
obj.location = (0.0, 0.0, 0.0)
# Set the root object as the active object for the export operator
bpy.context.view_layer.objects.active = obj
# Define the output file path
file_path = os.path.join(output_dir, f"{obj.name}.glb")
# Export the selected objects to a glTF file
bpy.ops.export_scene.gltf(filepath=file_path,
use_selection=True,
check_existing=False,
export_format='GLB',
export_texture_dir='textures', export_texcoords=True,
export_normals=True,
export_tangents=True,
export_materials='EXPORT',
export_colors=True,
use_mesh_edges=False,
use_mesh_vertices=False,
export_cameras=False,
use_visible=False,
use_renderable=False,
export_yup=True,
export_apply=True,
export_animations=True,
export_force_sampling=True,
export_def_bones=False,
export_current_frame=False,
export_morph=True,
export_morph_animation=False,
export_morph_normal=True,
export_morph_tangent=True,
export_lights=False,
export_skins=True)
# Deselect all objects for the next iteration
bpy.ops.object.select_all(action='DESELECT')
print(f"Exported {obj.name} and its children to {file_path}")
with open(file_path + ".json", "w") as json_data:
json.dump(desc, json_data)
export_root_objects_to_gltf(outdir)

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,71 @@
import bpy
import os, sys, time
argv = sys.argv
argv = argv[argv.index("--") + 1:]
incpath = os.path.dirname(__file__)
sys.path.insert(0, incpath)
outdir = argv[0]
def export_root_objects_to_gltf(output_dir):
# Ensure the output directory exists
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# Deselect all objects
bpy.ops.object.select_all(action='DESELECT')
# Iterate through all objects in the scene
for obj in bpy.context.scene.objects:
# Check if the object has no parent and has children
# The original request specifies "objects having no parent with children".
# This condition captures those that are explicitly root objects of a hierarchy.
if obj.parent is None:
# Select the root object and all its children
print(obj.name)
obj.select_set(True)
for child in obj.children_recursive:
child.select_set(True)
# Set the root object as the active object for the export operator
bpy.context.view_layer.objects.active = obj
# Define the output file path
file_path = os.path.join(output_dir, f"{obj.name}.glb")
# Export the selected objects to a glTF file
bpy.ops.export_scene.gltf(filepath=file_path,
use_selection=True,
check_existing=False,
export_format='GLB',
export_texture_dir='textures', export_texcoords=True,
export_normals=True,
export_tangents=True,
export_materials='EXPORT',
export_colors=True,
use_mesh_edges=False,
use_mesh_vertices=False,
export_cameras=False,
use_visible=False,
use_renderable=False,
export_yup=True,
export_apply=True,
export_animations=True,
export_force_sampling=True,
export_def_bones=False,
export_current_frame=False,
export_morph=True,
export_morph_animation=False,
export_morph_normal=True,
export_morph_tangent=True,
export_lights=False,
export_skins=True)
# Deselect all objects for the next iteration
bpy.ops.object.select_all(action='DESELECT')
print(f"Exported {obj.name} and its children to {file_path}")
export_root_objects_to_gltf(outdir)

View File

@@ -655,3 +655,54 @@ setup_handler(function(event, trigger_entity, what_entity)
--]]
end
end)
--[[
active_dialogues = {}
setup_action_handler("talk", function(town, index, word)
local ret = ""
local choices = {}
local have_choice = false
local have_paragraph = false
local book = narrator.parse_file('stories.talk')
local story = narrator.init_story(book)
if story == nil then
crash()
end
story:begin()
narrate("Boo!!!!")
if story.can_continue() then
local paragraph = story:continue(1)
print(dump(paragraph))
if story:can_choose() then
have_choice = true
local ch = story:get_choices()
for i, choice in ipairs(ch) do
table.insert(choices, choice.text)
print(i, dump(choice))
end
if #choices == 1 and choices[1] == "Ascend" then
story:choose(1)
choices = {}
end
if #choices == 1 and choices[1] == "Continue" then
this.story:choose(1)
choices = {}
end
end
end
if (#choices > 0) then
print("choices!!!")
narrate(ret, choices)
else
narrate(ret)
end
if not have_choice and not have_paragraph then
-- complete
crash()
else
print("can continue")
end
end)
]]--

View File

@@ -0,0 +1,6 @@
Dialogue...
Whatever...
* [Ascend]
~ crash()
- ->END

View File

@@ -21,6 +21,7 @@ FileSystem=resources/terrain
FileSystem=skybox
FileSystem=resources/buildings
FileSystem=resources/buildings/parts/pier
FileSystem=resources/buildings/parts/furniture
FileSystem=resources/vehicles
FileSystem=resources/debug
FileSystem=resources/fonts

34
resources/CMakeLists.txt Normal file
View File

@@ -0,0 +1,34 @@
project(resources)
set(DIRECTORY_LIST
main
shaderlib
terrain
)
set(TARGET_PATHS)
foreach(DIR_NAME ${DIRECTORY_LIST})
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME}
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/${DIR_NAME}
${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${DIR_NAME})
list(APPEND TARGET_PATHS ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME})
endforeach()
#add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/terrain/world_map.png
# COMMAND unzip -o ${CMAKE_CURRENT_SOURCE_DIR}/world_map.kra mergedimage.png -d ${CMAKE_CURRENT_BINARY_DIR}/world_map
# COMMAND ${CMAKE_COMMAND} -E copy
# ${CMAKE_BINARY_DIR}/world_map/mergedimage.png
# ${CMAKE_BINARY_DIR}/resources/terrain/world_map.png
# COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/world_map
# DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/world_map.kra)
#list(APPEND TARGET_PATHS ${CMAKE_CURRENT_BINARY_DIR}/terrain/world_map.png)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/terrain/brushes.png
COMMAND unzip -o ${CMAKE_CURRENT_SOURCE_DIR}/brushes.kra mergedimage.png -d ${CMAKE_CURRENT_BINARY_DIR}/brushes
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_BINARY_DIR}/brushes/mergedimage.png
${CMAKE_CURRENT_BINARY_DIR}/terrain/brushes.png
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_BINARY_DIR}/brushes
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/brushes.kra)
list(APPEND TARGET_PATHS ${CMAKE_CURRENT_BINARY_DIR}/terrain/brushes.png)
add_custom_target(stage_resources ALL DEPENDS ${TARGET_PATHS})

View File

@@ -0,0 +1,16 @@
// This file is part of the OGRE project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at https://www.ogre3d.org/licensing.
// SPDX-License-Identifier: MIT
#ifdef USE_LINEAR_COLOURS
#define ENABLE_LINEAR_COLOUR(colour) colour.rgb = pow(colour.rgb, vec3_splat(2.2))
#else
#define ENABLE_LINEAR_COLOUR(colour)
#endif
#if defined(USE_LINEAR_COLOURS) && !defined(TARGET_CONSUMES_LINEAR)
#define COLOUR_TRANSFER(colour) colour.rgb = pow(colour.rgb, vec3_splat(1.0/2.2))
#else
#define COLOUR_TRANSFER(colour)
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

File diff suppressed because it is too large Load Diff

22
src/FastNoiseLite/LICENSE Normal file
View File

@@ -0,0 +1,22 @@
MIT License
Copyright(c) 2020 Jordan Peck (jordan.me2@gmail.com)
Copyright(c) 2020 Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,23 @@
## Getting Started
Here's an example for creating a 128x128 array of OpenSimplex2 noise
```cpp
// Create and configure FastNoise object
FastNoiseLite noise;
noise.SetNoiseType(FastNoiseLite::NoiseType_OpenSimplex2);
// Gather noise data
std::vector<float> noiseData(128 * 128);
int index = 0;
for (int y = 0; y < 128; y++)
{
for (int x = 0; x < 128; x++)
{
noiseData[index++] = noise.GetNoise((float)x, (float)y);
}
}
// Do something with this data...
```

View File

@@ -9,7 +9,7 @@ find_package(OgreProcedural REQUIRED CONFIG)
find_package(pugixml REQUIRED CONFIG)
find_package(flecs REQUIRED CONFIG)
set(COPY_DIRECTORIES resources skybox water resources/buildings/parts)
set(COPY_DIRECTORIES characters resources skybox water resources/buildings/parts)
set(INSTALL_DEPS ${CMAKE_CURRENT_BINARY_DIR}/resources.cfg)
foreach(DIR_NAME ${COPY_DIRECTORIES})
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME}

View File

@@ -16,7 +16,7 @@
#include "Components.h"
#include "CharacterModule.h"
#include "TerrainModule.h"
#include "GUIModule.h"
#include "GUIModuleCommon.h"
#include "AppModule.h"
#include "EditorGizmoModule.h"
#include "EditorInputModule.h"

View File

@@ -6,9 +6,10 @@ find_package(nlohmann_json REQUIRED)
find_package(OgreProcedural REQUIRED CONFIG)
add_subdirectory(items)
add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp SunModule.cpp TerrainModule.cpp
GUIModule.cpp LuaData.cpp WorldMapModule.cpp BoatModule.cpp EventTriggerModule.cpp
GUIModule.cpp EditorGUIModule.cpp LuaData.cpp WorldMapModule.cpp BoatModule.cpp EventTriggerModule.cpp
CharacterAnimationModule.cpp PhysicsModule.cpp EventModule.cpp CharacterManagerModule.cpp
VehicleManagerModule.cpp AppModule.cpp StaticGeometryModule.cpp SmartObject.cpp SlotsModule.cpp goap.cpp)
VehicleManagerModule.cpp AppModule.cpp StaticGeometryModule.cpp SmartObject.cpp SlotsModule.cpp
PlayerActionModule.cpp goap.cpp)
target_link_libraries(GameData PUBLIC
lua
flecs::flecs_static

View File

@@ -22,38 +22,28 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, const CharacterBase &ch,
AnimationControl &anim) {
if (!anim.configured && ch.mSkeleton) {
if (!anim.configured) {
int i, j;
e.set<EventData>({});
ch.mSkeleton->setBlendMode(
ch.mBodyEnt->getSkeleton()->setBlendMode(
Ogre::ANIMBLEND_CUMULATIVE);
Ogre::String animNames[] = {
"idle",
"walking",
"running",
"treading_water",
"swimming",
"hanging-idle",
"hanging-climb",
"swimming-hold-edge",
"swimming-edge-climb",
"character-talk",
"pass-character",
"idle-act",
"sitting-chair",
"sitting-ground"
};
int state_count = sizeof(animNames) /
sizeof(animNames[0]);
anim.mAnimationSystem =
new AnimationSystem(false);
for (i = 0; i < state_count; i++) {
Ogre::AnimationStateSet *animStateSet =
ch.mBodyEnt->getAllAnimationStates();
const Ogre::AnimationStateMap &animMap =
animStateSet->getAnimationStates();
anim.mAnimationSystem =
new AnimationSystem(false);
ch.mBodyEnt->getSkeleton()
->getBone("Root")
->removeAllChildren();
for (auto it = animMap.begin();
it != animMap.end(); it++) {
Animation *animation = new Animation(
ch.mSkeleton,
ch.mBodyEnt->getAnimationState(
animNames[i]),
ch.mSkeleton->getAnimation(
animNames[i]),
ch.mBodyEnt->getSkeleton(),
it->second,
ch.mBodyEnt->getSkeleton()
->getAnimation(
it->first),
e);
#ifdef VDEBUG
std::cout
@@ -62,7 +52,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
#endif
animation->setLoop(true);
anim.mAnimationSystem->add_animation(
animNames[i], animation);
it->first, animation);
}
anim.mAnimationSystem
->builder()
@@ -157,10 +147,39 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
CharacterBase &ch, AnimationControl &anim) {
float delta = eng.delta;
// ch.mBoneMotion = Ogre::Vector3::ZERO;
bool result = anim.mAnimationSystem->addTime(delta);
if (!ch.mRootBone)
if (!anim.mAnimationSystem)
return;
// The value we get is interpolated value. When result is true it is new step
#if 0
ch.mBodyEnt->getSkeleton()->getBone("Root")->reset();
ch.mBodyEnt->getSkeleton()->getBone("Root")->setPosition(
Ogre::Vector3::ZERO);
#endif
bool result = anim.mAnimationSystem->addTime(delta);
Ogre::Vector3 rootMotion =
anim.mAnimationSystem->getRootMotionDelta();
ch.mBonePrevMotion = ch.mBoneMotion;
ch.mBoneMotion = rootMotion;
ch.mBodyEnt->_updateAnimation();
ch.mBodyEnt->getSkeleton()->getBone("Root")->setPosition(
Ogre::Vector3::ZERO);
#if 0
Ogre::Vector3 deltaMotion;
ch.mBodyEnt->_updateAnimation();
std::cout << "motion: " << ch.mBoneMotion << " "
<< rootMotion << " " << ch.mBonePrevMotion
<< std::endl;
rootMotion = ch.mBodyEnt->getSkeleton()
->getBone("Root")
->getPosition();
if (rootMotion.squaredLength() <
ch.mBoneMotion.squaredLength())
deltaMotion = rootMotion;
else
deltaMotion = rootMotion - ch.mBoneMotion;
ch.mBonePrevMotion = ch.mBoneMotion;
ch.mBoneMotion = deltaMotion;
#endif
// The value we get is interpolated value. When result is true it is new step
#if 0
Ogre::Vector3 offset = ch.mRootBone->getPosition();
ch.mBoneMotion = static_cast<RootMotionListener *>(
@@ -302,54 +321,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
Ogre::Vector3 colNormal;
bool is_on_floor = false;
bool penetration = false;
#if 0
if (eng.startupDelay < 0.0f) {
if (body.mController) {
Ogre::Vector3 rotMotion =
v.velocity * eng.delta;
rotMotion.x = Ogre::Math::Clamp(
rotMotion.x, -0.04f, 0.04f);
rotMotion.y = Ogre::Math::Clamp(
rotMotion.y, -0.025f, 0.1f);
rotMotion.z = Ogre::Math::Clamp(
rotMotion.z, -0.04f, 0.04f);
btVector3 currentPosition =
body.mGhostObject
->getWorldTransform()
.getOrigin();
is_on_floor =
body.mController->isOnFloor();
penetration = body.mController
->isPenetrating();
if (is_on_floor)
v.gvelocity =
Ogre::Vector3::ZERO;
btTransform from(
Ogre::Bullet::convert(
ch.mBodyNode
->getOrientation()),
Ogre::Bullet::convert(
ch.mBodyNode
->getPosition()));
ch.mBodyNode->_setDerivedPosition(
ch.mBodyNode
->_getDerivedPosition() +
rotMotion);
}
}
#endif
});
#if 0
ecs.system<CharacterVelocity, CharacterBase>("HandleRootMotionEnd")
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, CharacterVelocity &v,
CharacterBase &ch) {
// zero the velocity;
// v.velocity = Ogre::Vector3::ZERO;
// ch.mBoneMotion = Ogre::Vector3::ZERO;
});
#endif
ecs.system<const Input, const CharacterBase, AnimationControl>(
"HandleNPCAnimations")
@@ -360,6 +332,8 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
const CharacterBase &ch, AnimationControl &anim) {
if (!anim.configured)
return;
if (!anim.mAnimationSystem)
return;
AnimationNodeStateMachine *state_machine =
anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
@@ -405,6 +379,8 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
AnimationControl &anim) {
if (!anim.configured)
return;
if (!anim.mAnimationSystem)
return;
AnimationNodeStateMachine *main_sm =
anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
@@ -568,15 +544,6 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
act.prevMotion.z = input.motion.z;
}
#if 0
if (!controls_idle) {
if (Ogre::Math::Abs(input.motion.z - act.prevMotion.z) > 0.001f) {
if (input.motion.z < 0)
ECS::get_mut<LuaData>().call_handler("actuator_forward", e);
}
ECS::get_mut<LuaData>().call_handler("actuator_controls_update");
}
#endif
act.prevMotion = input.motion;
});
ecs.system<EventData>("UpdateEvents")
@@ -616,87 +583,6 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
<< "h=" << ch.mBodyNode->_getDerivedPosition().y
<< std::endl;
});
#endif
#if 0
ecs.system<const EngineData, const CharacterBase, CharacterBody>(
"UpdateBodyCast")
.kind(flecs::OnUpdate)
.without<CharacterInActuator>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterBase &ch, CharacterBody &body) {
struct ResultCallback
: public btCollisionWorld::RayResultCallback {
btCollisionObject *m_me;
btVector3 m_from, m_to, m_hitNormalWorld,
m_hitPointWorld;
ResultCallback(btCollisionObject *me,
const btVector3 &from,
const btVector3 &to)
: m_me(me)
, m_from(from)
, m_to(to)
{
}
btScalar addSingleResult(
btCollisionWorld::LocalRayResult
&rayResult,
bool normalInWorldSpace) override
{
if (rayResult.m_collisionObject == m_me)
return 1.0f;
if (!btPairCachingGhostObject::upcast(
rayResult.m_collisionObject))
return 1.0f;
if (!(rayResult.m_collisionObject
->getCollisionFlags() &
btCollisionObject::
CF_CHARACTER_OBJECT))
return 1.0f;
m_closestHitFraction =
rayResult.m_hitFraction;
m_collisionObject =
rayResult.m_collisionObject;
if (normalInWorldSpace)
m_hitNormalWorld =
rayResult
.m_hitNormalLocal;
else
m_hitNormalWorld =
m_collisionObject
->getWorldTransform()
.getBasis() *
rayResult
.m_hitNormalLocal;
m_hitPointWorld.setInterpolate3(
m_from, m_to,
rayResult.m_hitFraction);
return rayResult.m_hitFraction;
}
};
Ogre::Vector3 offset(0.0f, 0.5f, 0.0f);
float dist = 0.5f;
btVector3 a = Ogre::Bullet::convert(
ch.mBodyNode->getPosition() + offset),
b(Ogre::Bullet::convert(
ch.mBodyNode->getPosition() +
ch.mBodyNode->getOrientation() *
Ogre::Vector3(0, 0, dist) +
offset));
ResultCallback result(body.mGhostObject, a, b);
// body.mGhostObject->rayTest(a, b, result);
eng.mWorld->getBtWorld()->rayTest(a, b, result);
if (result.hasHit()) {
std::cout << "Hit!!! " << result.m_hitPointWorld
<< std::endl;
e.set<CharacterInActuator>(
{ "idle", { 0, 0, 0 } });
ECS::get<LuaBase>().mLua->call_handler(
"character_enter", e,
ECS::get<Body2Entity>().entities.at(
const_cast<btCollisionObject *>(
result.m_collisionObject)));
}
});
#endif
struct AnimationSetCommand : public GameWorld::Command {
int operator()(const std::vector<GameWorld::Parameter *> &args)

View File

@@ -9,22 +9,16 @@
namespace ECS
{
class RootMotionListener : public Ogre::NodeAnimationTrack::Listener {
Ogre::Vector3 prevTranslation;
mutable Ogre::Vector3 deltaMotion;
flecs::entity e;
public:
RootMotionListener(flecs::entity e)
: Ogre::NodeAnimationTrack::Listener()
, e(e)
, prevTranslation(Ogre::Vector3::ZERO)
, deltaMotion(Ogre::Vector3::ZERO)
{
}
bool getInterpolatedKeyFrame(const Ogre::AnimationTrack *t,
const Ogre::TimeIndex &timeIndex,
Ogre::KeyFrame *kf) override
{
#if 0
Ogre::TransformKeyFrame *vkf =
static_cast<Ogre::TransformKeyFrame *>(kf);
Ogre::KeyFrame *kf1, *kf2;
@@ -36,14 +30,12 @@ public:
k2 = static_cast<Ogre::TransformKeyFrame *>(kf2);
Ogre::Vector3 translation;
Ogre::Quaternion rotation;
if (tm == 0.0f) {
Ogre::Vector3 deltaMotion;
Ogre::Vector3 prevMotion;
if (tm == 0.0f) {
rotation = k1->getRotation();
translation = k1->getTranslate();
deltaMotion = translation;
// vkf->setRotation(k1->getRotation());
// vkf->setTranslate(k1->getTranslate());
// vkf->setScale(k1->getScale());
} else {
rotation = Ogre::Quaternion::nlerp(
tm, k1->getRotation(), k2->getRotation(), true);
@@ -55,14 +47,7 @@ public:
translation.squaredLength())
deltaMotion = translation;
}
#if 0
std::cout << "time: " << tm
<< " Position: " << deltaMotion;
std::cout << " Quaternion: " << rotation;
std::cout << std::endl;
#endif
vkf->setTranslate(deltaMotion);
// vkf->setTranslate(translation);
vkf->setRotation(rotation);
vkf->setScale(Ogre::Vector3(1, 1, 1));
prevTranslation = translation;
@@ -70,7 +55,9 @@ public:
e.get_mut<CharacterBase>().mBonePrevMotion = prevTranslation;
e.modified<CharacterBase>();
return true;
}
#endif
return false;
}
};
struct AnimationTrigger;
struct AnimationTriggerSubscriber {
@@ -126,46 +113,66 @@ struct AnimationTrigger {
{
}
};
struct SetupTracks {
Ogre::NodeAnimationTrack *mHipsTrack;
Ogre::NodeAnimationTrack *mRootTrack;
RootMotionListener *mListener;
Ogre::Vector3 mRootTranslation;
SetupTracks(flecs::entity e, Ogre::Skeleton *skeleton,
Ogre::Animation *animation)
: mListener(OGRE_NEW RootMotionListener(e))
{
mHipsTrack = nullptr;
mRootTrack = nullptr;
for (const auto &it : animation->_getNodeTrackList()) {
Ogre::NodeAnimationTrack *track = it.second;
Ogre::String trackName =
track->getAssociatedNode()->getName();
if (trackName == "mixamorig:Hips") {
mHipsTrack = track;
} else if (trackName == "Root") {
mRootTrack = track;
// mRootTracks[i]->removeAllKeyFrames();
}
}
if (!mRootTrack) {
Ogre::Bone *bone = skeleton->getBone("Root");
mRootTrack = animation->createNodeTrack(
bone->getHandle(), bone);
Ogre::TransformKeyFrame *kf =
mRootTrack->createNodeKeyFrame(0.0f);
kf->setTranslate(Ogre::Vector3::ZERO);
kf->setRotation(Ogre::Quaternion::IDENTITY);
}
// if (e.has<Player>()) // FIXME
// mRootTrack->setListener(mListener);
Ogre::TransformKeyFrame *tkfBeg =
(Ogre::TransformKeyFrame *)mRootTrack->getKeyFrame(0);
Ogre::TransformKeyFrame *tkfEnd =
(Ogre::TransformKeyFrame *)mRootTrack->getKeyFrame(
mRootTrack->getNumKeyFrames() - 1);
mRootTranslation =
tkfEnd->getTranslate() - tkfBeg->getTranslate();
}
};
struct Animation {
Ogre::AnimationState *mAnimationState;
Ogre::Animation *mSkelAnimation;
Ogre::NodeAnimationTrack *mHipsTrack;
Ogre::NodeAnimationTrack *mRootTrack;
RootMotionListener *mListener;
SetupTracks *mTracks;
float m_weight;
float m_accWeight;
flecs::entity e;
Ogre::Skeleton *mSkeleton;
Animation(Ogre::Skeleton *skeleton, Ogre::AnimationState *animState,
Ogre::Animation *skelAnimation, flecs::entity e)
: mAnimationState(animState)
: mTracks(OGRE_NEW SetupTracks(e, skeleton, skelAnimation))
, mAnimationState(animState)
, mSkelAnimation(skelAnimation)
, mListener(OGRE_NEW RootMotionListener(e))
, m_weight(0)
, m_accWeight(0)
, e(e)
, mSkeleton(skeleton)
{
int j;
mRootTrack = nullptr;
mHipsTrack = nullptr;
for (const auto &it : mSkelAnimation->_getNodeTrackList()) {
Ogre::NodeAnimationTrack *track = it.second;
Ogre::String trackName =
track->getAssociatedNode()->getName();
if (trackName == "mixamorig:Hips") {
mHipsTrack = track;
} else if (trackName == "Root") {
mRootTrack = track;
// mRootTracks[i]->removeAllKeyFrames();
}
}
if (!mRootTrack) {
Ogre::Bone *bone = skeleton->getBone("Root");
mRootTrack = mSkelAnimation->createNodeTrack(
bone->getHandle(), bone);
Ogre::TransformKeyFrame *kf =
mRootTrack->createNodeKeyFrame(0.0f);
kf->setTranslate(Ogre::Vector3::ZERO);
kf->setRotation(Ogre::Quaternion::IDENTITY);
}
mRootTrack->setListener(mListener);
}
Ogre::String getName()
{
@@ -198,22 +205,134 @@ struct Animation {
{
return m_weight;
}
bool addTime(float time)
Ogre::Vector3 rootMotion;
Ogre::Vector3 getRootMotionDelta()
{
#if 0
Ogre::KeyFrame *kf1, *kf2;
Ogre::TransformKeyFrame *k1, *k2;
unsigned short firstKeyIndex;
Ogre::Real timePos = mAnimationState->getTimePosition();
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(timePos);
float tm = mTracks->mRootTrack->getKeyFramesAtTime(
index, &kf1, &kf2, &firstKeyIndex);
k1 = static_cast<Ogre::TransformKeyFrame *>(kf1);
k2 = static_cast<Ogre::TransformKeyFrame *>(kf2);
Ogre::Vector3 translation;
Ogre::Quaternion rotation;
if (tm == 0.0f) {
rotation = k1->getRotation();
translation = k1->getTranslate();
} else {
rotation = Ogre::Quaternion::nlerp(
tm, k1->getRotation(),
k2->getRotation(), true);
translation = k1->getTranslate() +
(k2->getTranslate() -
k1->getTranslate()) *
tm;
}
return translation * mAnimationState->getWeight();
#endif
if (mAnimationState->getEnabled())
return rootMotion * mAnimationState->getWeight();
else
return Ogre::Vector3(0, 0, 0);
}
#if 0
void updateRootMotion(Ogre::Real timePos)
{
Ogre::KeyFrame *kf1, *kf2;
Ogre::TransformKeyFrame *k1, *k2;
unsigned short firstKeyIndex;
mSkeleton->getBone("Root")->setManuallyControlled(true);
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(timePos);
float tm = mTracks->mRootTrack->getKeyFramesAtTime(
index, &kf1, &kf2, &firstKeyIndex);
k1 = static_cast<Ogre::TransformKeyFrame *>(kf1);
k2 = static_cast<Ogre::TransformKeyFrame *>(kf2);
Ogre::Vector3 translation;
Ogre::Quaternion rotation;
Ogre::Vector3 deltaMotion =
e.get_mut<CharacterBase>().mBoneMotion;
Ogre::Vector3 prevTranslation =
e.get_mut<CharacterBase>().mBonePrevMotion;
if (tm == 0.0f) {
rotation = k1->getRotation() * m_weight;
translation = k1->getTranslate() * m_weight;
deltaMotion = translation;
} else {
rotation = Ogre::Quaternion::nlerp(
tm, k1->getRotation() * m_weight,
k2->getRotation() * m_weight, true);
translation = k1->getTranslate() * m_weight +
(k2->getTranslate() * m_weight -
k1->getTranslate() * m_weight) *
tm;
deltaMotion = translation - prevTranslation;
if (deltaMotion.squaredLength() >
translation.squaredLength())
deltaMotion = translation;
}
e.get_mut<CharacterBase>().mBoneMotion = deltaMotion;
e.get_mut<CharacterBase>().mBonePrevMotion = prevTranslation;
e.modified<CharacterBase>();
#if 1
if (timePos > 0.5f && m_weight > 0.2 && translation.squaredLength() > 0.0f) {
std::cout << timePos << " " << m_weight << " "
<< e.get<CharacterBase>()
.mBodyEnt->getMesh()
->getName()
<< " " << deltaMotion << " "
<< prevTranslation << std::endl;
std::cout << translation << " " << rotation << std::endl;
// OgreAssert(false, "updateRootMotion");
}
#endif
mTracks->mRootTrack->getAssociatedNode()->setPosition(
Ogre::Vector3());
mSkeleton->getBone("Root")->reset();
}
#endif
void getKeyframeIndices(Ogre::Real timePos, unsigned short *pindex)
{
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(timePos);
Ogre::KeyFrame *kf1, *kf2;
mTracks->mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, pindex);
}
bool addTime(float time)
{
bool result = mAnimationState->getEnabled();
if (!result)
return result;
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(
mAnimationState->getTimePosition());
Ogre::KeyFrame *kf1, *kf2;
unsigned short prev_index, next_index;
mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, &prev_index);
unsigned short prev_index, next_index;
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(mAnimationState->getTimePosition());
getKeyframeIndices(mAnimationState->getTimePosition(), &prev_index);
unsigned int previous_frame = index.getKeyIndex();
float lastTime = mAnimationState->getTimePosition();
mAnimationState->addTime(time);
index = mSkelAnimation->_getTimeIndex(
mAnimationState->getTimePosition());
mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, &next_index);
return prev_index != next_index;
float thisTime = mAnimationState->getTimePosition();
float length = mAnimationState->getLength();
bool loop = mAnimationState->getLoop();
int loops = loop ? (int)std::round((lastTime + time - thisTime) / length) : 0;
Ogre::TransformKeyFrame tkf(0, 0);
mTracks->mRootTrack->getInterpolatedKeyFrame(lastTime, &tkf);
Ogre::Vector3 lastRootPos = tkf.getTranslate();
mTracks->mRootTrack->getInterpolatedKeyFrame(thisTime, &tkf);
Ogre::Vector3 thisRootPos = tkf.getTranslate();
#if 0
if (thisTime > lastTime)
rootMotion = thisRootPos - lastRootPos;
else
rootMotion = mTracks->mRootTranslation + thisRootPos - lastRootPos;
#else
rootMotion = (thisRootPos - lastRootPos) + (loops * mTracks->mRootTranslation);
#endif
getKeyframeIndices(mAnimationState->getTimePosition(), &next_index);
// updateRootMotion(mAnimationState->getTimePosition());
return prev_index != next_index;
}
void reset()
{
@@ -827,6 +946,14 @@ struct AnimationSystem : AnimationNode {
}
};
AnimationSystemBuilder m_builder;
Ogre::Vector3 getRootMotionDelta()
{
Ogre::Vector3 motionDelta(0, 0, 0);
int i;
for (i = 0; i < vanimation_list.size(); i++)
motionDelta += vanimation_list[i]->getRootMotionDelta();
return motionDelta;
}
bool addTime(float time)
{
int i;

View File

@@ -4,15 +4,190 @@
#include "Components.h"
#include "CharacterModule.h"
#include "CharacterAnimationModule.h"
#include "StaticGeometryModule.h"
#include "PhysicsModule.h"
#include "PlayerActionModule.h"
#include "items.h"
#include "CharacterManagerModule.h"
namespace ECS
{
struct TownNPCs {
struct NPCData {
flecs::entity e;
nlohmann::json props;
Ogre::Vector3 position;
Ogre::Quaternion orientation;
Ogre::String model;
};
std::map<int, NPCData> npcs;
};
struct LivesIn {};
void createNPCActionNodes(flecs::entity town, flecs::entity e, int index)
{
NPCActionNodes &anodes = e.get_mut<NPCActionNodes>();
const CharacterBase &ch = e.get<CharacterBase>();
Ogre::Vector3 characterPos = ch.mBodyNode->_getDerivedPosition();
Ogre::Quaternion characterRot = ch.mBodyNode->_getDerivedOrientation();
if (anodes.anodes.size() > 0) {
int i;
for (i = 0; i < anodes.anodes.size(); i++) {
auto &anode = anodes.anodes[i];
Ogre::Vector3 offset = Ogre::Vector3::UNIT_Z * 0.3f +
Ogre::Vector3::UNIT_Y;
if (i == 1)
offset = Ogre::Vector3::NEGATIVE_UNIT_Z * 0.3f +
Ogre::Vector3::UNIT_Y;
anode.position = characterPos + characterRot * offset;
anode.rotation = characterRot;
to_json(anode.props["position"], anode.position);
to_json(anode.props["rotation"], anode.rotation);
}
e.modified<NPCActionNodes>();
return;
}
{
ActionNodeList::ActionNode anode;
anode.props["action"] = "talk";
anode.props["action_text"] = "Talk";
anode.action = "talk";
anode.action_text = "Talk";
Ogre::Vector3 offset = Ogre::Vector3::UNIT_Z * 0.25f +
Ogre::Vector3::UNIT_Y * 1.5f;
anode.position = characterPos + characterRot * offset;
anode.rotation = characterRot;
anode.radius = 0.6f;
anode.height = 1.0f;
to_json(anode.props["position"], anode.position);
to_json(anode.props["rotation"], anode.rotation);
anode.props["radius"] = anode.radius;
anode.props["height"] = anode.height;
anode.props["town"] = town.id();
anode.props["index"] = index;
anodes.anodes.push_back(anode);
}
{
ActionNodeList::ActionNode anode;
anode.props["action"] = "action";
anode.props["action_text"] = "Action";
anode.action = "action";
anode.action_text = "Action";
Ogre::Vector3 offset = Ogre::Vector3::NEGATIVE_UNIT_Z * 0.2f +
Ogre::Vector3::UNIT_Y;
anode.position = characterPos + characterRot * offset;
anode.rotation = characterRot;
anode.radius = 0.3f;
anode.height = 1.0f;
to_json(anode.props["position"], anode.position);
to_json(anode.props["rotation"], anode.rotation);
anode.props["radius"] = anode.radius;
anode.props["height"] = anode.height;
anode.props["town"] = town.id();
anode.props["index"] = index;
anodes.anodes.push_back(anode);
}
e.modified<NPCActionNodes>();
}
CharacterManagerModule::CharacterManagerModule(flecs::world &ecs)
{
ecs.module<CharacterManagerModule>();
ecs.import <CharacterModule>();
ecs.import <CharacterAnimationModule>();
ecs.import <PhysicsModule>();
ecs.import <PlayerActionModule>();
ecs.component<TownCharacterHolder>();
ecs.component<TownNPCs>();
ecs.component<LivesIn>();
ecs.component<NPCActionNodes>().on_add(
[](flecs::entity e, NPCActionNodes &anodes) {
anodes.anodes.clear();
});
ecs.system<TerrainItem, TownNPCs>("UpdateCharacters")
.immediate()
.kind(flecs::OnUpdate)
.interval(1.0f)
.write<CharacterBase>()
.write<NPCActionNodes>()
.write<CharacterLocation>()
.write<CharacterConf>()
.write<Character>()
.write<LivesIn>()
.each([this](flecs::entity town, TerrainItem &item,
TownNPCs &npcs) {
if (!player.is_valid())
return;
if (!player.has<CharacterBase>())
return;
ECS::get().defer_suspend();
Ogre::Vector3 cameraPos =
player.get<CharacterBase>()
.mBodyNode->_getDerivedPosition();
for (auto &npc : npcs.npcs) {
int index = npc.first;
TownNPCs::NPCData &data = npc.second;
Ogre::Vector3 npcPosition = data.position;
Ogre::Quaternion npcOrientation =
data.orientation;
if (cameraPos.squaredDistance(npcPosition) <
10000.0f) {
if (!data.e.is_valid()) {
data.e = createCharacterData(
data.model,
data.position,
data.orientation);
data.e.add<LivesIn>(town);
break;
}
}
if (cameraPos.squaredDistance(npcPosition) >
22500.0f) {
if (data.e.is_valid()) {
data.e.destruct();
data.e = flecs::entity();
break;
}
}
}
ECS::get().defer_resume();
});
ecs.system<TerrainItem, TownNPCs>("UpdateCharacters2")
.immediate()
.kind(flecs::OnUpdate)
.write<CharacterBase>()
.write<NPCActionNodes>()
.write<CharacterLocation>()
.write<CharacterConf>()
.write<Character>()
.write<LivesIn>()
.each([this](flecs::entity town, TerrainItem &item,
TownNPCs &npcs) {
if (!player.is_valid())
return;
if (!player.has<CharacterBase>())
return;
Ogre::Vector3 cameraPos =
player.get<CharacterBase>()
.mBodyNode->_getDerivedPosition();
for (auto &npc : npcs.npcs) {
int index = npc.first;
TownNPCs::NPCData &data = npc.second;
Ogre::Vector3 npcPosition = data.position;
Ogre::Quaternion npcOrientation =
data.orientation;
if (cameraPos.squaredDistance(npcPosition) <
10000.0f) {
if (data.e.is_valid()) {
if (data.e.has<CharacterBase>() &&
data.e.has<LivesIn>(town))
createNPCActionNodes(
town, data.e,
index);
}
}
}
});
}
flecs::entity
CharacterManagerModule::createPlayer(const Ogre::Vector3 &position,
@@ -43,7 +218,44 @@ CharacterManagerModule::createCharacterData(const Ogre::String model,
.entity()
.set<CharacterLocation>({ rotation, position })
.set<CharacterConf>({ model })
.add<Character>();
return e;
.add<Character>()
.add<NPCActionNodes>();
return e;
}
void CharacterManagerModule::registerTownCharacters(flecs::entity town)
{
Ogre::MeshManager::getSingleton().load("normal-male.glb", "General");
Ogre::MeshManager::getSingleton().load("normal-female.glb", "General");
Ogre::String props = StaticGeometryModule::getItemProperties(town);
nlohmann::json j = nlohmann::json::parse(props);
nlohmann::json npcs = nlohmann::json::array();
if (town.has<TownNPCs>())
return;
if (j.find("npcs") != j.end())
npcs = j["npcs"];
std::cout << npcs.dump(4) << std::endl;
int index = 0;
std::map<int, TownNPCs::NPCData> npcMap;
for (auto &npc : npcs) {
const char *models[] = { "normal-male.glb",
"normal-female.glb" };
int sex = npc["sex"].get<int>();
Ogre::Vector3 npcPosition;
Ogre::Quaternion npcOrientation;
from_json(npc["position"], npcPosition);
from_json(npc["orientation"], npcOrientation);
Ogre::String model = models[sex];
TownNPCs::NPCData npcData;
npcData.e = flecs::entity();
npcData.model = model;
npcData.orientation = npcOrientation;
npcData.position = npcPosition;
npcData.props = npc;
npcMap[index] = npcData;
index++;
}
town.set<TownNPCs>({ npcMap });
}
}

View File

@@ -3,6 +3,7 @@
#include <flecs.h>
namespace ECS
{
struct TownCharacterHolder{int index;};
struct CharacterManagerModule {
std::set<flecs::entity> characters;
flecs::entity player;
@@ -17,6 +18,10 @@ struct CharacterManagerModule {
{
return player;
}
void registerTownCharacters(flecs::entity town);
void setTownCharacter(flecs::entity town, int index, bool enable);
CharacterManagerModule(CharacterManagerModule &&) = delete;
CharacterManagerModule &operator=(CharacterManagerModule&&) = delete;
};
}
#endif

View File

@@ -282,13 +282,8 @@ CharacterModule::CharacterModule(flecs::world &ecs)
ch.mBodyNode->setOrientation(loc.orientation);
ch.mBodyNode->setPosition(loc.position);
ch.mBodyNode->attachObject(ch.mBodyEnt);
ch.mSkeleton = ch.mBodyEnt->getSkeleton();
OgreAssert(ch.mBodyEnt->getSkeleton()->hasBone("Root"),
"No root bone");
OgreAssert(ch.mSkeleton->hasBone("Root"),
"No root bone");
ch.mRootBone = ch.mSkeleton->getBone("Root");
OgreAssert(ch.mRootBone, "No root bone");
ch.mBoneMotion = Ogre::Vector3::ZERO;
ch.mBonePrevMotion = Ogre::Vector3::ZERO;
});

View File

@@ -21,8 +21,6 @@ struct CharacterBase {
float mTimer;
Ogre::SceneNode *mBodyNode;
Ogre::Entity *mBodyEnt;
Ogre::Skeleton *mSkeleton;
Ogre::Node *mRootBone;
Ogre::Vector3 mBoneMotion;
Ogre::Vector3 mBonePrevMotion;
Ogre::Vector3 mGoalDirection; // actual intended direction in world-space

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
#ifndef __EDITORGUIMODULE_H__
#define __EDITORGUIMODULE_H__
namespace ECS {
struct EditorGUIModule {
EditorGUIModule(flecs::world &ecs);
};
}
#endif // EDITORGUIMODULE_H

View File

@@ -26,4 +26,4 @@ struct EventModule {
flecs::entity to);
};
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -6,40 +6,9 @@ namespace OgreBites
}
namespace ECS
{
struct GUI {
bool enabled;
bool grab;
bool grabChanged;
bool narrationBox;
bool mainMenu;
Ogre::String narrationText;
std::vector<Ogre::String> choices;
int narration_answer;
static void setWindowGrab(bool g = true)
{
ECS::GUI &gui = ECS::get().get_mut<GUI>();
if (gui.grab != g) {
gui.grab = g;
gui.grabChanged = true;
ECS::get().modified<GUI>();
}
}
static void finish()
{
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();
gui.enabled = false;
gui.mainMenu = false;
gui.narrationBox = false;
ECS::get().modified<ECS::GUI>();
setWindowGrab(true);
}
};
struct GUIModule {
flecs::entity ui_wait;
GUIModule(flecs::world &ecs);
};
struct EditorGUIModule {
EditorGUIModule(flecs::world &ecs);
};
}
#endif
#endif

View File

@@ -0,0 +1,127 @@
#ifndef __GUIMODULECOMMON_H__
#define __GUIMODULECOMMON_H__
#include <iostream>
#include <Ogre.h>
#include "Components.h"
#include "GameData.h"
namespace ECS
{
struct GUI {
bool enabled;
bool grab;
bool grabChanged;
bool narrationBox;
bool mainMenu;
Ogre::String narrationText;
std::vector<Ogre::String> choices;
int narration_answer;
struct NarrationHandler {
private:
Ogre::String mnarrationText;
std::vector<Ogre::String> mchoices;
int narration_answer;
private:
bool complete;
bool active;
public:
bool is_complete()
{
return complete;
}
bool is_active()
{
return active;
}
const Ogre::String &getNarrationText() const
{
return mnarrationText;
}
const std::vector<Ogre::String> &getChoices() const
{
return mchoices;
}
void progress()
{
_event("narration_progress");
}
void setNarrationAnswer(int answer)
{
narration_answer = answer;
_event("narration_answered");
}
int getNarrationAnswer() const
{
return narration_answer;
}
NarrationHandler(): complete(false), active(false) {}
private:
virtual void finish() = 0;
virtual void activate() = 0;
virtual void event(const Ogre::String &event) = 0;
protected:
void _activate()
{
activate();
active = true;
}
void _finish()
{
finish();
complete = true;
}
void _narration(const Ogre::String &text, const std::vector<Ogre::String> &choices)
{
mnarrationText = text;
mchoices = choices;
}
void _clear_narration()
{
mnarrationText = "";
mchoices.clear();
}
public:
void _event(const Ogre::String &ev)
{
if (!active && !complete)
_activate();
event(ev);
}
virtual ~NarrationHandler() {}
};
static void setWindowGrab(bool g = true)
{
ECS::GUI &gui = ECS::get().get_mut<GUI>();
if (gui.grab != g) {
gui.grab = g;
gui.grabChanged = true;
ECS::get().modified<GUI>();
}
}
static void finish()
{
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();
gui.enabled = false;
gui.mainMenu = false;
gui.narrationBox = false;
ECS::get().modified<ECS::GUI>();
setWindowGrab(true);
}
std::vector<NarrationHandler *> narrationHandlers;
void addNarrationHandler(struct NarrationHandler *handler)
{
narrationHandlers.push_back(handler);
}
void removeNarrationHandler(struct NarrationHandler *handler)
{
auto it = std::find(narrationHandlers.begin(), narrationHandlers.end(), handler);
narrationHandlers.erase(it);
}
};
}
#endif // GUIMODULECOMMON_H

View File

@@ -10,7 +10,9 @@
#include "WaterModule.h"
#include "TerrainModule.h"
#include "SunModule.h"
#include "GUIModuleCommon.h"
#include "GUIModule.h"
#include "EditorGUIModule.h"
#include "LuaData.h"
#include "WorldMapModule.h"
#include "BoatModule.h"
@@ -20,6 +22,7 @@
#include "EventModule.h"
#include "CharacterManagerModule.h"
#include "VehicleManagerModule.h"
#include "PlayerActionModule.h"
#include "AppModule.h"
#include "world-build.h"
@@ -42,7 +45,7 @@ void setup_minimal()
ecs.component<Body2Entity>().add(flecs::Singleton);
}
void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
Ogre::Camera *camera, Ogre::RenderWindow *window)
Ogre::Camera *camera, Ogre::RenderWindow *window)
{
std::cout << "Setup GameData\n";
setup_minimal();
@@ -53,11 +56,13 @@ void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
ecs.import <WaterModule>();
ecs.import <SunModule>();
ecs.import <TerrainModule>();
ecs.import <GUIModule>();
ecs.import <GUIModule>();
ecs.import <EventTriggerModule>();
ecs.import <LuaModule>();
ecs.import <WorldMapModule>();
ecs.import <CharacterAnimationModule>();
ecs.import <PlayerActionModule>();
ecs.add<ActionNodeList>();
ecs.system<EngineData>("UpdateDelta")
.kind(flecs::OnUpdate)
@@ -90,7 +95,7 @@ void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
#endif
});
ecs.set<EngineData>({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(),
(int)window->getHeight(), false });
(int)window->getHeight(), false });
ecs.set<Camera>({ cameraNode, camera, false });
ecs.add<GameData>();
ecs.add<Input>();
@@ -168,7 +173,7 @@ void setupInventoryScene(Ogre::SceneManager *scnMgr,
}
void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
Ogre::Camera *camera, Ogre::RenderWindow *window)
Ogre::Camera *camera, Ogre::RenderWindow *window)
{
std::cout << "Setup Editor\n";
setup_minimal();
@@ -185,6 +190,8 @@ void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
ecs.import <LuaModule>();
// ecs.import <WorldMapModule>();
ecs.import <CharacterAnimationModule>();
ecs.import <PlayerActionModule>();
ecs.add<ActionNodeList>();
ecs.system<EngineData>("UpdateDelta")
.kind(flecs::OnUpdate)
@@ -217,7 +224,7 @@ void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
#endif
});
ecs.set<EngineData>({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(),
(int)window->getHeight(), false });
(int)window->getHeight(), false });
ecs.set<Camera>({ cameraNode, camera, false });
#if 0
ecs.set<EditorSceneSwitch>({ 0 });

View File

@@ -6,13 +6,13 @@ namespace ECS
extern flecs::entity player;
void setup_minimal();
void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
Ogre::Camera *camera, Ogre::RenderWindow *window);
Ogre::Camera *camera, Ogre::RenderWindow *window);
void setupInteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
Ogre::Camera *camera, Ogre::RenderWindow *window);
void setupInventoryScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
Ogre::Camera *camera, Ogre::RenderWindow *window);
void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
Ogre::Camera *camera, Ogre::RenderWindow *window);
Ogre::Camera *camera, Ogre::RenderWindow *window);
void setupEditorAlt(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
Ogre::Camera *camera, Ogre::RenderWindow *window);
void update(float delta);

View File

@@ -1,7 +1,7 @@
#include <OgreFileSystemLayer.h>
#include "GameData.h"
#include "Components.h"
#include "GUIModule.h"
#include "GUIModuleCommon.h"
#include "PhysicsModule.h"
#include "CharacterModule.h"
#include "CharacterAnimationModule.h"
@@ -11,6 +11,7 @@
#include "EventTriggerModule.h"
#include "SlotsModule.h"
#include "world-build.h"
#include "PlayerActionModule.h"
#include "LuaData.h"
#include "luaaa.hpp"
extern "C" {
@@ -299,7 +300,20 @@ LuaData::LuaData()
return 0;
});
lua_setglobal(L, "setup_handler");
lua_pushcfunction(L, [](lua_State *L) -> int {
lua_pushcfunction(L, [](lua_State *L) -> int {
luaL_checktype(L, 1, LUA_TSTRING);
luaL_checktype(L, 2, LUA_TFUNCTION);
ECS::get_mut<PlayerActionModule>().setupLuaActionHandler(L);
ECS::modified<PlayerActionModule>();
return 0;
});
lua_setglobal(L, "setup_action_handler");
lua_pushcfunction(L, [](lua_State *L) -> int {
// FIXME
return 0;
});
lua_setglobal(L, "setup_narration_handler");
lua_pushcfunction(L, [](lua_State *L) -> int {
int args = lua_gettop(L);
if (args < 1)
return 0;
@@ -846,6 +860,7 @@ LuaModule::LuaModule(flecs::world &ecs)
ecs.module<LuaModule>();
ecs.import <SlotsModule>();
ecs.import <VehicleManagerModule>();
ecs.import <PlayerActionModule>();
ecs.component<LuaChildEventTrigger>();
ecs.component<LuaBase>()
.on_add([](LuaBase &lua) {
@@ -911,4 +926,4 @@ LuaModule::LuaModule(flecs::world &ecs)
}
});
}
}
}

View File

@@ -11,7 +11,7 @@ struct LuaData {
lua_State *L;
std::vector<int> setup_handlers;
int setup_handler();
int call_handler(const Ogre::String &event);
int call_handler(const Ogre::String &event);
int call_handler(const Ogre::String &event, flecs::entity e,
flecs::entity o);

View File

@@ -172,14 +172,18 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.event(flecs::OnRemove)
.each([&](flecs::entity e, const JPH::BodyID &id) {
JoltPhysicsWrapper::getSingleton().removeBody(id);
JoltPhysicsWrapper::getSingleton().destroyBody(id);
if (e.has<CharacterBase>() || e.has<Character>())
return;
JoltPhysicsWrapper::getSingleton().destroyBody(id);
std::cout << "body destroyed" << std::endl;
});
ecs.observer<const EngineData, const CharacterBase>("SetupCharacterPh")
.event(flecs::OnSet)
ecs.system<const EngineData, const CharacterBase>("SetupCharacterPh")
.kind(flecs::OnUpdate)
.with<Character>()
.without<CharacterBody>()
.write<CharacterBody>()
.without<CharacterBody>()
.without<JPH::BodyID>()
.write<CharacterBody>()
.write<JPH::BodyID>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterBase &base) {
CharacterBody &b = e.ensure<CharacterBody>();

View File

@@ -0,0 +1,320 @@
#include <nanoflann.hpp>
#include <vector>
#include <iostream>
#include <flecs.h>
#include <nlohmann/json.hpp>
#include "Components.h"
#include "GameData.h"
#include "CharacterManagerModule.h"
#include "CharacterModule.h"
#include "items.h"
#include "GUIModule.h"
#include "GUIModuleCommon.h"
#include "LuaData.h"
#include "PlayerActionModule.h"
namespace ECS
{
struct OgreVector3Adaptor {
const std::vector<ActionNodeList::ActionNode> &nodes;
OgreVector3Adaptor(const std::vector<ActionNodeList::ActionNode> &nodes)
: nodes(nodes)
{
}
// Required by nanoflann: Number of data points
inline size_t kdtree_get_point_count() const
{
return nodes.size();
}
// Required by nanoflann: Returns the distance between the vector and a point
// Using squared distance is standard for performance
inline float kdtree_get_pt(const size_t idx, const size_t dim) const
{
return nodes[idx].position[dim];
}
// Optional: bounding box optimization (return false if not implemented)
template <class BBOX> bool kdtree_get_bbox(BBOX & /*bb*/) const
{
return false;
}
};
typedef nanoflann::KDTreeSingleIndexAdaptor<
nanoflann::L2_Simple_Adaptor<float, OgreVector3Adaptor>,
OgreVector3Adaptor, 3 /* dimensionality */
>
OgreKDTree;
struct ActionNodeList::indexObject {
OgreVector3Adaptor adaptor;
OgreKDTree index;
indexObject(const std::vector<ActionNodeList::ActionNode> &nodes)
: adaptor(nodes)
, index(3, adaptor,
nanoflann::KDTreeSingleIndexAdaptorParams(10))
{
}
};
struct TestNarrativeHandler : GUI::NarrationHandler {
int count;
TestNarrativeHandler()
: GUI::NarrationHandler()
, count(0)
{
}
void finish() override
{
_clear_narration();
}
void activate() override
{
_narration("Dialogue...", {});
count = 0;
}
void event(const Ogre::String &evt) override
{
if (evt == "narration_progress" ||
evt == "narration_answered") {
count++;
if (count == 1) {
_narration(
"Question..." +
Ogre::StringConverter::toString(
count),
{ "Answer1", "Answer2" });
} else {
_narration(
"Whatever..." +
Ogre::StringConverter::toString(
count),
{});
}
if (count > 5)
_finish();
}
if (evt == "narration_answered")
std::cout << "answer: " << getNarrationAnswer()
<< std::endl;
}
};
struct SimpleWordHandler : PlayerActionModule::ActionWordHandler {
void operator()(flecs::entity town, int index,
const Ogre::String &word) override
{
TestNarrativeHandler *handle = OGRE_NEW TestNarrativeHandler();
ECS::get_mut<GUI>().addNarrationHandler(handle);
ECS::modified<GUI>();
}
};
PlayerActionModule::PlayerActionModule(flecs::world &ecs)
{
ecs.module<PlayerActionModule>();
ecs.import <CharacterManagerModule>();
ecs.component<ActionNodeList>()
.on_add([](flecs::entity e, ActionNodeList &alist) {
alist.dirty = true;
alist.nodes.reserve(1000);
alist.dynamicNodes.reserve(1000);
alist.selected = -1;
alist.busy = false;
})
.add(flecs::Singleton);
#if 0
ecs.system<ActionNodeList>("testNodeList")
.kind(flecs::OnUpdate)
.each([](ActionNodeList &list) {
if (list.nodes.size() > 0) {
Ogre::Vector3 queryPos =
ECS::get<Camera>()
.mCameraNode
->_getDerivedPosition();
std::vector<size_t> points;
list.query(queryPos, points);
for (auto &p : points)
std::cout << p << std::endl
<< list.nodes[p].props.dump()
<< std::endl;
OgreAssert(points.size() == 0, "got result");
}
});
#endif
ecs.system<ActionNodeList>("updateNodeList")
.kind(flecs::OnUpdate)
.each([](ActionNodeList &list) {
if (list.busy)
return;
if (list.nodes.size() > 0) {
Ogre::SceneNode *cameraNode =
ECS::get<Camera>().mCameraNode;
Ogre::Vector3 cameraPos =
cameraNode->_getDerivedPosition();
flecs::entity player =
ECS::get<CharacterManagerModule>()
.getPlayer();
if (player.is_valid()) {
Ogre::Vector3 playerPos =
player.get<CharacterBase>()
.mBodyNode
->_getDerivedPosition();
list.query(playerPos, list.points,
list.distances);
} else {
list.query(cameraPos, list.points,
list.distances);
}
}
});
ecs.system<ActionNodeList, const Input>("ActivateActionNode")
.kind(flecs::OnUpdate)
.each([this](ActionNodeList &list, const Input &input) {
if (input.control & 32)
std::cout << "act pressed" << std::endl;
if (list.busy)
return;
if (input.act_pressed && list.selected >= 0) {
std::cout << list.dynamicNodes[list.selected]
.props.dump(4)
<< std::endl;
flecs::entity_t townid =
list.dynamicNodes[list.selected]
.props["town"]
.get<flecs::entity_t>();
flecs::entity town = ECS::get().entity(townid);
int index = list.dynamicNodes[list.selected]
.props["index"]
.get<int>();
for (auto it = actionWords.begin();
it != actionWords.end(); it++) {
if (it->first ==
list.dynamicNodes[list.selected]
.action) {
(*it->second)(
town, index,
list.dynamicNodes
[list.selected]
.action);
list.busy = true;
}
}
}
if (!ECS::get<GUI>().enabled)
list.busy = false;
});
SimpleWordHandler *handler = OGRE_NEW SimpleWordHandler;
addWordHandler("talk", handler);
}
void PlayerActionModule::addWordHandler(const Ogre::String &word,
ActionWordHandler *handler)
{
actionWords.insert({ word, handler });
}
void PlayerActionModule::removeWordHandler(const Ogre::String &word,
ActionWordHandler *handler)
{
for (auto it = actionWords.begin(); it != actionWords.end();) {
if (it->first == word && it->second == handler)
it = actionWords.erase(it);
else
it++;
}
}
struct LuaWordHandler : PlayerActionModule::ActionWordHandler {
lua_State *L;
int ref;
void operator()(flecs::entity town, int index,
const Ogre::String &word) override
{
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
lua_pushinteger(L, town.id());
lua_pushinteger(L, index);
lua_pushstring(L, word.c_str());
if (lua_pcall(L, 3, 0, 0)) {
Ogre::LogManager::getSingleton().stream()
<< lua_tostring(L, -1);
OgreAssert(false, "Lua error");
}
}
};
void PlayerActionModule::addLuaWordHandler(const Ogre::String &word,
lua_State *L, int ref)
{
struct LuaWordHandler *handler = OGRE_NEW LuaWordHandler;
handler->L = L;
handler->ref = ref;
addWordHandler(word, handler);
}
void PlayerActionModule::removeLuaWordHandler(const Ogre::String &word,
lua_State *L, int ref)
{
for (auto it = actionWords.begin(); it != actionWords.end();) {
LuaWordHandler *handler =
static_cast<LuaWordHandler *>(it->second);
if (it->first == word && handler->L == L && handler->ref == ref)
it = actionWords.erase(it);
else
it++;
}
}
int PlayerActionModule::setupLuaActionHandler(lua_State *L)
{
luaL_checktype(L, 1, LUA_TSTRING);
luaL_checktype(L, 2, LUA_TFUNCTION);
Ogre::String word = lua_tostring(L, 1);
lua_pushvalue(L, 2);
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
addLuaWordHandler(word, L, ref);
return 0;
}
void ActionNodeList::build()
{
dynamicNodes.clear();
dynamicNodes.insert(dynamicNodes.end(), nodes.begin(), nodes.end());
ECS::get().query_builder<const NPCActionNodes>().each(
[&](flecs::entity e, const NPCActionNodes &anodes) {
dynamicNodes.insert(dynamicNodes.end(),
anodes.anodes.begin(),
anodes.anodes.end());
});
indexObj = std::make_shared<ActionNodeList::indexObject>(dynamicNodes);
indexObj->index.buildIndex();
dirty = false;
}
bool ActionNodeList::query(const Ogre::Vector3 &position,
std::vector<size_t> &points,
std::vector<float> &distances)
{
build();
std::vector<size_t> tmppoints;
std::vector<float> tmpdistances;
points.clear();
points.reserve(4);
distances.clear();
distances.reserve(4);
tmppoints.resize(4);
tmpdistances.resize(4);
nanoflann::KNNResultSet<float> resultSet(4);
resultSet.init(tmppoints.data(), tmpdistances.data());
bool ret = indexObj->index.findNeighbors(resultSet, &position.x,
nanoflann::SearchParameters());
int i;
for (i = 0; i < resultSet.size(); i++)
if (tmpdistances[i] < 25.0f) {
points.push_back(tmppoints[i]);
distances.push_back(tmpdistances[i]);
}
return ret;
}
}

View File

@@ -0,0 +1,63 @@
#ifndef PLAYERACTIONMODULE_H
#define PLAYERACTIONMODULE_H
#include <flecs.h>
#include <nlohmann/json.hpp>
#include <lua.h>
#include <Ogre.h>
namespace ECS {
struct ActionNodeList {
struct indexObject;
struct ActionNode {
Ogre::String action;
Ogre::String action_text;
Ogre::Vector3 position;
Ogre::Quaternion rotation;
float height;
float radius;
nlohmann::json props;
};
std::vector<ActionNode> nodes, dynamicNodes;
std::shared_ptr<indexObject> indexObj;
std::vector<size_t> points;
std::vector<float> distances;
int selected;
bool dirty;
bool busy;
void build();
bool query(const Ogre::Vector3 &position, std::vector<size_t> &points, std::vector<float> &distances);
int addNode(struct ActionNodeList::ActionNode &node)
{
int index = nodes.size();
nodes.push_back(node);
dirty = true;
return index;
}
void removeNode(int index)
{
nodes.erase(nodes.begin() + index);
}
};
struct NPCActionNodes {
std::vector<ActionNodeList::ActionNode> anodes;
};
struct PlayerActionModule {
struct ActionWordHandler {
virtual void operator()(flecs::entity town, int index,
const Ogre::String &word) = 0;
};
std::multimap<Ogre::String, ActionWordHandler *>
actionWords;
PlayerActionModule(flecs::world &ecs);
void addWordHandler(const Ogre::String &word, ActionWordHandler *handler);
void removeWordHandler(const Ogre::String &word, ActionWordHandler *handler);
void addLuaWordHandler(const Ogre::String &word, lua_State *L, int ref);
void removeLuaWordHandler(const Ogre::String &word, lua_State *L, int ref);
int setupLuaActionHandler(lua_State *L);
};
}
#endif // PLAYERACTIONMODULE_H

View File

@@ -6,6 +6,7 @@
#include <OgreRTShaderSystem.h>
#include <OgreStaticGeometry.h>
#include <OgreMeshLodGenerator.h>
#include <OgreWorkQueue.h>
#include <Procedural.h>
#include "Components.h"
#include "GameData.h"
@@ -19,13 +20,42 @@ namespace ECS
{
static bool itemsLoaded = false;
static bool furnitureLoaded = false;
static bool templatesLoaded = false;
static std::list<std::pair<long, long> > addQueue;
StaticGeometryModule::StaticGeometryModule(flecs::world &ecs)
{
ecs.module<StaticGeometryModule>();
ecs.component<TerrainSlotParent>();
ecs.component<TerrainItem>();
ecs.component<TerrainItemNode>().on_remove([](flecs::entity e,
ecs.component<FurnitureItem>();
ecs.component<FurnitureInstance>()
.on_remove([](flecs::entity e, FurnitureInstance &instance) {
if (instance.furniture) {
instance.furniture
->destroyAllChildrenAndObjects();
instance.furniture->getCreator()
->destroySceneNode(instance.furniture);
instance.furniture = nullptr;
}
})
.on_set([](flecs::entity e, FurnitureInstance &instance) {
if (instance.furniture !=
e.get<FurnitureInstance>().furniture) {
FurnitureInstance &f =
e.get_mut<FurnitureInstance>();
if (f.furniture) {
f.furniture
->destroyAllChildrenAndObjects();
f.furniture->getCreator()
->destroySceneNode(f.furniture);
}
}
})
.on_add([](flecs::entity e, FurnitureInstance &instance) {
instance.furniture = nullptr;
});
ecs.component<TerrainItemNode>().on_remove([](flecs::entity e,
TerrainItemNode &item) {
if (item.itemNode) {
item.itemNode->destroyAllChildrenAndObjects();
@@ -40,6 +70,7 @@ StaticGeometryModule::StaticGeometryModule(flecs::world &ecs)
}
});
ecs.component<TerrainItemMeshNode>();
ecs.component<GeometryUpdateItem>();
ecs.import <TerrainModule>();
ecs.observer<const Terrain>("LoadTerrainItems")
.event(flecs::OnSet)
@@ -60,6 +91,14 @@ StaticGeometryModule::StaticGeometryModule(flecs::world &ecs)
itemsLoaded = true;
return;
}
if (!furnitureLoaded) {
loadFurniture();
furnitureLoaded = true;
}
if (!templatesLoaded) {
loadTemplates();
templatesLoaded = true;
}
std::list<std::pair<long, long> > output;
while (!addQueue.empty()) {
std::pair<long, long> item = addQueue.front();
@@ -157,8 +196,48 @@ void StaticGeometryModule::setItemProperties(flecs::entity id,
const Ogre::String &StaticGeometryModule::getItemProperties(flecs::entity id)
{
OgreAssert(id.is_valid(), "bad id");
return id.get<TerrainItem>().properties;
return id.get<TerrainItem>().properties;
}
nlohmann::json templates;
void StaticGeometryModule::loadTemplates()
{
if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
"templates.list"))
return;
Ogre::String group =
Ogre::ResourceGroupManager::getSingleton()
.findGroupContainingResource("templates.list");
Ogre::DataStreamPtr stream =
Ogre::ResourceGroupManager::getSingleton().openResource(
"templates.list", group);
Ogre::String json = stream->getAsString();
nlohmann::json jtemplates = nlohmann::json::parse(json);
templates = jtemplates;
}
void StaticGeometryModule::saveTemplates()
{
Ogre::String path = "resources/buildings/templates.list";
if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
"templates.list")) {
Ogre::String group =
Ogre::ResourceGroupManager::getSingleton()
.findGroupContainingResource("templates.list");
Ogre::FileInfoListPtr fileInfoList(
Ogre::ResourceGroupManager::getSingleton()
.findResourceFileInfo(group, "templates.list"));
OgreAssert(fileInfoList->size() == 1,
"templates.list should be there and only once");
path = fileInfoList->at(0).archive->getName() + "/" +
"templates.list";
Ogre::FileSystemLayer::removeFile(path);
}
std::fstream fout(path.c_str(), std::ios::out);
fout << templates.dump();
fout.close();
}
void StaticGeometryModule::saveItems()
{
Ogre::String path = "resources/buildings/items.list";
@@ -271,463 +350,457 @@ void StaticGeometryModule::loadItems()
<< std::endl;
std::cout << "position: " << item.id() << " " << position
<< std::endl;
}
}
}
void StaticGeometryModule::saveFurniture()
{
/* No saving - furniture is generated by blender */
}
void StaticGeometryModule::loadFurniture()
{
ECS::get().delete_with<FurnitureItem>();
static std::vector<Ogre::String> glb_names;
const std::vector<Ogre::String> &groups =
Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
if (glb_names.size() == 0) {
int i;
for (i = 0; i < groups.size(); i++) {
std::vector<Ogre::String> names =
*Ogre::ResourceGroupManager::getSingleton()
.findResourceNames(
groups[i],
"furniture-*.glb.json");
glb_names.insert(glb_names.end(), names.begin(),
names.end());
}
}
for (auto &g : glb_names) {
Ogre::String group = Ogre::ResourceGroupManager::getSingleton()
.findGroupContainingResource(g);
Ogre::DataStreamPtr stream =
Ogre::ResourceGroupManager::getSingleton().openResource(
g, group);
Ogre::String json = stream->getAsString();
nlohmann::json jdata = nlohmann::json::parse(json);
std::vector<Ogre::String> tags;
for (auto &tag : jdata["tags"]) {
Ogre::String stag = tag.get<Ogre::String>();
tags.push_back(stag);
}
Ogre::String meshName = jdata["mesh"].get<Ogre::String>();
Ogre::MeshPtr mesh =
Ogre::MeshManager::getSingleton().getByName(meshName);
if (mesh) {
Ogre::LodConfig meshconf(mesh);
Geometry::setupLods(meshconf);
}
ECS::get().entity().set<FurnitureItem>({ json, tags });
std::cout << "path: " << g << std::endl;
}
}
void StaticGeometryModule::getItemPositionPerSlot(
long x, long y, std::list<Ogre::Vector3> *positions)
long x, long y, std::list<Ogre::Vector3> *positions)
{
std::pair<long, long> pos{ x, y };
if (!positions)
return;
flecs::entity slot =
ECS::get().query_builder<const TerrainSlotParent>().build().find(
[&](const TerrainSlotParent &slot) -> bool {
return slot.slot == pos;
std::pair<long, long> pos{ x, y };
if (!positions)
return;
flecs::entity slot =
ECS::get().query_builder<const TerrainSlotParent>().build().find(
[&](const TerrainSlotParent &slot) -> bool {
return slot.slot == pos;
});
if (!slot.is_valid())
return;
ECS::get()
.query_builder<const TerrainItem>()
.with(flecs::ChildOf, slot)
.build()
.each([&](flecs::entity e, const TerrainItem &item) {
positions->push_back(item.position);
});
if (!slot.is_valid())
return;
ECS::get()
.query_builder<const TerrainItem>()
.with(flecs::ChildOf, slot)
.build()
.each([&](flecs::entity e, const TerrainItem &item) {
positions->push_back(item.position);
});
}
void StaticGeometryModule::getItemPositions(std::list<Ogre::Vector3> *positions)
{
ECS::get().query_builder<const TerrainItem>().build().each(
[&](flecs::entity e, const TerrainItem &item) {
positions->push_back(item.position);
});
ECS::get().query_builder<const TerrainItem>().build().each(
[&](flecs::entity e, const TerrainItem &item) {
positions->push_back(item.position);
});
}
void StaticGeometryModule::getItemPositionAndRotation(
flecs::entity e, Ogre::Vector3 &position, Ogre::Quaternion &orientation)
flecs::entity e, Ogre::Vector3 &position, Ogre::Quaternion &orientation)
{
position = e.get<TerrainItem>().position;
orientation = e.get<TerrainItem>().orientation;
position = e.get<TerrainItem>().position;
orientation = e.get<TerrainItem>().orientation;
}
void StaticGeometryModule::getItemsProperties(
std::list<std::pair<flecs::entity, Ogre::String> > *items)
std::list<std::pair<flecs::entity, Ogre::String> > *items)
{
ECS::get().query_builder<const TerrainItem>().build().each(
[&](flecs::entity e, const TerrainItem &item) {
items->push_back({ e, item.properties });
});
ECS::get().query_builder<const TerrainItem>().build().each(
[&](flecs::entity e, const TerrainItem &item) {
items->push_back({ e, item.properties });
});
}
void StaticGeometryModule::createItemGeometry(flecs::entity e)
{
Geometry::createItemGeometry(e);
Geometry::createItemGeometry(e);
}
void StaticGeometryModule::destroyItemGeometry(flecs::entity e)
{
Geometry::destroyItemGeometry(e);
Geometry::destroyItemGeometry(e);
}
nlohmann::json &StaticGeometryModule::getTemplates()
{
return templates;
}
void StaticGeometryModule::updateItemGeometry(flecs::entity e)
{
if (e.has<GeometryUpdateItem>())
return;
e.add<GeometryUpdateItem>();
Ogre::Root::getSingleton().getWorkQueue()->addTask([e]() {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask(
[e]() {
Geometry::updateItemGeometry(e);
e.remove<GeometryUpdateItem>();
});
});
}
void StaticGeometryModule::addTriangleBufferWork(
const Ogre::String &meshName, Ogre::StaticGeometry *geo,
const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
const Procedural::TriangleBuffer &tb)
{
struct WorkData {
Ogre::String meshName;
Ogre::StaticGeometry *geo;
Ogre::Vector3 position;
Ogre::Quaternion rotation;
Procedural::TriangleBuffer tb;
};
WorkData data = { meshName, geo, position, rotation, tb };
Ogre::Root::getSingleton().getWorkQueue()->addTask([captData = std::move(
data)]() {
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask(
[captData]() {
Ogre::MeshPtr mesh =
captData.tb.transformToMesh(
captData.meshName);
Ogre::Entity *ent =
ECS::get<EngineData>()
.mScnMgr->createEntity(mesh);
captData.geo->addEntity(ent, captData.position,
captData.rotation);
ECS::get<EngineData>().mScnMgr->destroyEntity(
ent);
});
});
}
struct TiledMeshes {
struct Tile {
Ogre::String materialName;
std::shared_ptr<Ogre::VertexData> vertexData;
std::shared_ptr<Ogre::IndexData> indexData;
std::set<uint32_t> positions;
};
std::map<Ogre::String, Tile> tiles;
uint32_t packKey(const Ogre::Vector3i &position)
struct Tile {
Ogre::String materialName;
std::shared_ptr<Ogre::VertexData> vertexData;
std::shared_ptr<Ogre::IndexData> indexData;
std::set<uint32_t> positions;
};
std::map<Ogre::String, Tile> tiles;
uint32_t packKey(const Ogre::Vector3i &position)
{
uint32_t key = 0;
key |= (uint32_t)(position[2] + 512) << 20;
key |= (uint32_t)(position[1] + 512) << 10;
key |= (uint32_t)(position[0] + 512) << 0;
return key;
uint32_t key = 0;
key |= (uint32_t)(position[2] + 512) << 20;
key |= (uint32_t)(position[1] + 512) << 10;
key |= (uint32_t)(position[0] + 512) << 0;
return key;
}
void unpackKey(uint32_t key, Ogre::Vector3i &position)
void unpackKey(uint32_t key, Ogre::Vector3i &position)
{
uint32_t mask = 0x3ff;
position[0] = (int)(key & mask) - 512;
position[1] = (int)((key >> 10) & mask) - 512;
position[2] = (int)((key >> 20) & mask) - 512;
uint32_t mask = 0x3ff;
position[0] = (int)(key & mask) - 512;
position[1] = (int)((key >> 10) & mask) - 512;
position[2] = (int)((key >> 20) & mask) - 512;
}
void setTile(const Ogre::String &name, const Ogre::Vector3i &position)
void setTile(const Ogre::String &name, const Ogre::Vector3i &position)
{
if (tiles.find(name) == tiles.end())
return;
tiles[name].positions.insert(packKey(position));
if (tiles.find(name) == tiles.end())
return;
tiles[name].positions.insert(packKey(position));
}
void clearTile(const Ogre::String &name, const Ogre::Vector3i &position)
void clearTile(const Ogre::String &name, const Ogre::Vector3i &position)
{
if (tiles.find(name) == tiles.end())
return;
tiles[name].positions.erase(packKey(position));
#if 0
auto pos = std::find(tiles[name].positions.begin(),
tiles[name].positions.end(),
packKey(position));
if (pos != tiles[name].positions.end())
tiles[name].positions.erase(pos);
#endif
}
void addTile(const Ogre::String &name, Ogre::MeshPtr mesh)
{
if (mesh->getSubMeshes().size() != 1)
return;
Ogre::SubMesh *submesh = mesh->getSubMesh(0);
Ogre::VertexData *vertexData;
if (submesh->useSharedVertices)
vertexData = mesh->sharedVertexData->clone();
else
vertexData = submesh->vertexData->clone();
tiles[name] = { submesh->getMaterialName(),
std::shared_ptr<Ogre::VertexData>(vertexData),
std::shared_ptr<Ogre::IndexData>(
submesh->indexData->clone()),
{} };
#if 0
std::vector<Ogre::Vector3> vertices;
std::vector<uint32_t> indices;
int count = mesh->getNumSubMeshes();
int i, j;
int indexCount = 0;
int vertexCount = 0;
int sharedVertexOffset = 0;
for (i = 0; i < count; i++) {
Ogre::SubMesh *submesh = mesh->getSubMesh(i);
indexCount += submesh->indexData->indexCount;
if (submesh->useSharedVertices)
vertexCount +=
mesh->sharedVertexData->vertexCount;
else
vertexCount += submesh->vertexData->vertexCount;
}
indices.reserve(indexCount);
vertices.reserve(vertexCount);
size_t currentVertexOffset = 0;
bool added_shared = false;
for (i = 0; i < count; i++) {
Ogre::SubMesh *submesh = mesh->getSubMesh(i);
Ogre::VertexData *vertex_data =
submesh->useSharedVertices ?
mesh->sharedVertexData :
submesh->vertexData;
bool add_vertices =
(submesh->useSharedVertices && !added_shared) ||
!submesh->useSharedVertices;
if (add_vertices) {
if (submesh->useSharedVertices)
sharedVertexOffset = vertices.size();
const Ogre::VertexDeclaration *decl =
vertex_data->vertexDeclaration;
const Ogre::VertexBufferBinding *bind =
vertex_data->vertexBufferBinding;
const Ogre::VertexElement *position_element =
decl->findElementBySemantic(
Ogre::VES_POSITION);
if (!position_element)
continue;
Ogre::HardwareVertexBufferSharedPtr vbuf =
bind->getBuffer(
position_element->getSource());
unsigned char *vertex_buffer = static_cast<
unsigned char *>(vbuf->lock(
Ogre::HardwareBuffer::HBL_READ_ONLY));
int vertexSize = vbuf->getVertexSize();
for (j = 0; j < vertex_data->vertexCount; j++) {
float *position_data;
position_element
->baseVertexPointerToElement(
vertex_buffer,
&position_data);
vertices.push_back(
{ position_data[0],
position_data[1],
position_data[2] });
vertex_buffer += vertexSize;
}
if (submesh->useSharedVertices)
added_shared = true;
vbuf->unlock();
}
Ogre::HardwareIndexBufferSharedPtr ibuf =
submesh->indexData->indexBuffer;
size_t numIndices = submesh->indexData->indexCount;
size_t vertexOffset = submesh->useSharedVertices ?
sharedVertexOffset :
currentVertexOffset;
if (ibuf->getType() ==
Ogre::HardwareIndexBuffer::IT_32BIT) {
unsigned int *pIndices = static_cast<
unsigned int *>(ibuf->lock(
Ogre::HardwareBuffer::HBL_READ_ONLY));
for (j = 0; j < numIndices; j++) {
indices.push_back(
(uint32_t)pIndices[j] +
vertexOffset);
}
ibuf->unlock();
} else {
unsigned short *pIndices = static_cast<
unsigned short *>(ibuf->lock(
Ogre::HardwareBuffer::HBL_READ_ONLY));
for (j = 0; j < numIndices; j++) {
indices.push_back(
(uint32_t)pIndices[j] +
vertexOffset);
}
ibuf->unlock();
}
currentVertexOffset = vertices.size();
}
#endif
}
struct buildSettings {
Ogre::String meshName;
Ogre::String materialName;
Ogre::MeshPtr mesh;
Ogre::SubMesh *sm;
int vertexCount, vertexOffset;
int indexCount, indexOffset;
Ogre::VertexDeclaration *vdecl;
Ogre::HardwareVertexBufferSharedPtr vbuf;
Ogre::AxisAlignedBox bounds;
bool setBounds;
};
void configureSettings(struct buildSettings &settings)
{
settings.mesh = Ogre::MeshManager::getSingleton().createManual(
settings.meshName,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
settings.mesh->createVertexData();
settings.sm = settings.mesh->createSubMesh();
settings.vertexCount = 0;
settings.indexCount = 0;
settings.vdecl = nullptr;
for (const auto &tile : tiles) {
settings.vertexCount +=
tile.second.vertexData->vertexCount *
tile.second.positions.size();
settings.indexCount +=
tile.second.indexData->indexCount *
tile.second.positions.size();
if (!settings.vdecl) {
settings.vdecl =
tile.second.vertexData
->vertexDeclaration->clone();
settings.materialName =
tile.second.materialName;
if (tiles.find(name) == tiles.end())
return;
tiles[name].positions.erase(packKey(position));
}
void addTile(const Ogre::String &name, Ogre::MeshPtr mesh)
{
if (mesh->getSubMeshes().size() != 1)
return;
Ogre::SubMesh *submesh = mesh->getSubMesh(0);
Ogre::VertexData *vertexData;
if (submesh->useSharedVertices)
vertexData = mesh->sharedVertexData->clone();
else
vertexData = submesh->vertexData->clone();
tiles[name] = { submesh->getMaterialName(),
std::shared_ptr<Ogre::VertexData>(vertexData),
std::shared_ptr<Ogre::IndexData>(
submesh->indexData->clone()),
{} };
}
struct buildSettings {
Ogre::String meshName;
Ogre::String materialName;
Ogre::MeshPtr mesh;
Ogre::SubMesh *sm;
int vertexCount, vertexOffset;
int indexCount, indexOffset;
Ogre::VertexDeclaration *vdecl;
Ogre::HardwareVertexBufferSharedPtr vbuf;
Ogre::AxisAlignedBox bounds;
bool setBounds;
};
void configureSettings(struct buildSettings &settings)
{
settings.mesh = Ogre::MeshManager::getSingleton().createManual(
settings.meshName,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
settings.mesh->createVertexData();
settings.sm = settings.mesh->createSubMesh();
settings.vertexCount = 0;
settings.indexCount = 0;
settings.vdecl = nullptr;
for (const auto &tile : tiles) {
settings.vertexCount +=
tile.second.vertexData->vertexCount *
tile.second.positions.size();
settings.indexCount +=
tile.second.indexData->indexCount *
tile.second.positions.size();
if (!settings.vdecl) {
settings.vdecl =
tile.second.vertexData
->vertexDeclaration->clone();
settings.materialName =
tile.second.materialName;
}
}
settings.mesh->sharedVertexData->vertexStart = 0;
settings.mesh->sharedVertexData->vertexCount =
settings.vertexCount;
settings.mesh->sharedVertexData->vertexDeclaration =
settings.vdecl;
settings.vbuf =
Ogre::HardwareBufferManager::getSingleton()
.createVertexBuffer(
settings.vdecl->getVertexSize(0),
settings.vertexCount,
Ogre::HBU_GPU_ONLY);
settings.mesh->sharedVertexData->vertexBufferBinding->setBinding(
0, settings.vbuf);
settings.sm->indexData->indexStart = 0;
settings.sm->indexData->indexCount = settings.indexCount;
settings.sm->indexData->indexBuffer =
Ogre::HardwareBufferManager::getSingleton()
.createIndexBuffer(
Ogre::HardwareIndexBuffer::IT_32BIT,
settings.sm->indexData->indexCount * 8,
Ogre::HBU_GPU_ONLY);
settings.sm->setMaterialName(settings.materialName);
settings.setBounds = true;
}
void processIndex(struct buildSettings &settings,
const struct Tile &tile, unsigned int *dstIndices)
{
int j;
std::shared_ptr<Ogre::IndexData> srcIndexData = tile.indexData;
int srcIndexCount = srcIndexData->indexCount;
std::shared_ptr<Ogre::VertexData> srcVertexData =
tile.vertexData;
int srcVertexCount = srcVertexData->vertexCount;
Ogre::HardwareIndexBufferSharedPtr srcIbuf =
srcIndexData->indexBuffer;
Ogre::HardwareBufferLockGuard srcIndexLock(
srcIbuf, Ogre::HardwareBuffer::HBL_READ_ONLY);
if (srcIndexData->indexBuffer->getType() ==
Ogre::HardwareIndexBuffer::IT_32BIT) {
unsigned int *indices =
static_cast<unsigned int *>(srcIndexLock.pData);
for (j = 0; j < srcIndexCount; j++)
dstIndices[settings.indexOffset + j] =
indices[j] + settings.vertexOffset;
} else if (srcIndexData->indexBuffer->getType() ==
Ogre::HardwareIndexBuffer::IT_16BIT) {
unsigned short *indices = static_cast<unsigned short *>(
srcIndexLock.pData);
for (j = 0; j < srcIndexCount; j++)
dstIndices[settings.indexOffset + j] =
indices[j] + settings.vertexOffset;
}
}
void processSingleVertex(
struct buildSettings &settings,
const Ogre::VertexDeclaration::VertexElementList &srcElements,
uint32_t offset, unsigned char *srcData, unsigned char *dstData)
{
for (const auto &srcElement : srcElements) {
unsigned char *srcPtr, *dstPtr;
const Ogre::VertexElement *destElement =
settings.vdecl->findElementBySemantic(
srcElement.getSemantic(),
srcElement.getIndex());
if (!destElement)
goto out;
if (srcElement.getType() != destElement->getType() ||
srcElement.getSize() != destElement->getSize())
goto out;
srcPtr = srcData + srcElement.getOffset();
dstPtr = dstData + destElement->getOffset();
if (destElement->getSemantic() == Ogre::VES_POSITION) {
float *srcPositionData =
reinterpret_cast<float *>(srcPtr);
float *dstPositionData =
reinterpret_cast<float *>(dstPtr);
Ogre::Vector3 position(srcPositionData[0],
srcPositionData[1],
srcPositionData[2]);
Ogre::Vector3i offsetv;
unpackKey(offset, offsetv);
position.x += (float)offsetv[0];
position.y += (float)offsetv[1];
position.z += (float)offsetv[2];
dstPositionData[0] = position.x;
dstPositionData[1] = position.y;
dstPositionData[2] = position.z;
if (settings.setBounds) {
settings.bounds.setMinimum(position);
settings.bounds.setMaximum(position);
settings.setBounds = false;
settings.mesh->sharedVertexData->vertexStart = 0;
settings.mesh->sharedVertexData->vertexCount =
settings.vertexCount;
settings.mesh->sharedVertexData->vertexDeclaration =
settings.vdecl;
settings.vbuf =
Ogre::HardwareBufferManager::getSingleton()
.createVertexBuffer(
settings.vdecl->getVertexSize(0),
settings.vertexCount,
Ogre::HBU_GPU_ONLY);
settings.mesh->sharedVertexData->vertexBufferBinding->setBinding(
0, settings.vbuf);
settings.sm->indexData->indexStart = 0;
settings.sm->indexData->indexCount = settings.indexCount;
settings.sm->indexData->indexBuffer =
Ogre::HardwareBufferManager::getSingleton()
.createIndexBuffer(
Ogre::HardwareIndexBuffer::IT_32BIT,
settings.sm->indexData->indexCount * 8,
Ogre::HBU_GPU_ONLY);
settings.sm->setMaterialName(settings.materialName);
settings.setBounds = true;
}
void processIndex(struct buildSettings &settings,
const struct Tile &tile, unsigned int *dstIndices)
{
int j;
std::shared_ptr<Ogre::IndexData> srcIndexData = tile.indexData;
int srcIndexCount = srcIndexData->indexCount;
std::shared_ptr<Ogre::VertexData> srcVertexData =
tile.vertexData;
int srcVertexCount = srcVertexData->vertexCount;
Ogre::HardwareIndexBufferSharedPtr srcIbuf =
srcIndexData->indexBuffer;
Ogre::HardwareBufferLockGuard srcIndexLock(
srcIbuf, Ogre::HardwareBuffer::HBL_READ_ONLY);
if (srcIndexData->indexBuffer->getType() ==
Ogre::HardwareIndexBuffer::IT_32BIT) {
unsigned int *indices =
static_cast<unsigned int *>(srcIndexLock.pData);
for (j = 0; j < srcIndexCount; j++)
dstIndices[settings.indexOffset + j] =
indices[j] + settings.vertexOffset;
} else if (srcIndexData->indexBuffer->getType() ==
Ogre::HardwareIndexBuffer::IT_16BIT) {
unsigned short *indices = static_cast<unsigned short *>(
srcIndexLock.pData);
for (j = 0; j < srcIndexCount; j++)
dstIndices[settings.indexOffset + j] =
indices[j] + settings.vertexOffset;
}
}
void processSingleVertex(
struct buildSettings &settings,
const Ogre::VertexDeclaration::VertexElementList &srcElements,
uint32_t offset, unsigned char *srcData, unsigned char *dstData)
{
for (const auto &srcElement : srcElements) {
unsigned char *srcPtr, *dstPtr;
const Ogre::VertexElement *destElement =
settings.vdecl->findElementBySemantic(
srcElement.getSemantic(),
srcElement.getIndex());
if (!destElement)
goto out;
if (srcElement.getType() != destElement->getType() ||
srcElement.getSize() != destElement->getSize())
goto out;
srcPtr = srcData + srcElement.getOffset();
dstPtr = dstData + destElement->getOffset();
if (destElement->getSemantic() == Ogre::VES_POSITION) {
float *srcPositionData =
reinterpret_cast<float *>(srcPtr);
float *dstPositionData =
reinterpret_cast<float *>(dstPtr);
Ogre::Vector3 position(srcPositionData[0],
srcPositionData[1],
srcPositionData[2]);
Ogre::Vector3i offsetv;
unpackKey(offset, offsetv);
position.x += (float)offsetv[0];
position.y += (float)offsetv[1];
position.z += (float)offsetv[2];
dstPositionData[0] = position.x;
dstPositionData[1] = position.y;
dstPositionData[2] = position.z;
if (settings.setBounds) {
settings.bounds.setMinimum(position);
settings.bounds.setMaximum(position);
settings.setBounds = false;
} else
settings.bounds.merge(position);
} else if (destElement->getSemantic() ==
Ogre::VES_NORMAL) {
float *srcNormalData =
reinterpret_cast<float *>(srcPtr);
float *dstNormalData =
reinterpret_cast<float *>(dstPtr);
Ogre::Vector3 normal(srcNormalData[0],
srcNormalData[1],
srcNormalData[2]);
dstNormalData[0] = normal.x;
dstNormalData[1] = normal.y;
dstNormalData[2] = normal.z;
} else
memcpy(dstPtr, srcPtr, srcElement.getSize());
settings.bounds.merge(position);
} else if (destElement->getSemantic() ==
Ogre::VES_NORMAL) {
float *srcNormalData =
reinterpret_cast<float *>(srcPtr);
float *dstNormalData =
reinterpret_cast<float *>(dstPtr);
Ogre::Vector3 normal(srcNormalData[0],
srcNormalData[1],
srcNormalData[2]);
dstNormalData[0] = normal.x;
dstNormalData[1] = normal.y;
dstNormalData[2] = normal.z;
} else
memcpy(dstPtr, srcPtr, srcElement.getSize());
out:;
}
}
void processTile(struct buildSettings &settings,
const struct Tile &tile, uint32_t position,
unsigned char *dstpData)
{
std::shared_ptr<Ogre::VertexData> srcVertexData =
tile.vertexData;
const Ogre::VertexDeclaration *srcDecl =
srcVertexData->vertexDeclaration;
const Ogre::VertexBufferBinding *srcBind =
srcVertexData->vertexBufferBinding;
int srcVertexCount = srcVertexData->vertexCount;
Ogre::HardwareVertexBufferSharedPtr srcVbuf =
srcBind->getBuffer(0);
std::shared_ptr<Ogre::IndexData> srcIndexData = tile.indexData;
int srcIndexCount = srcIndexData->indexCount;
}
}
void processTile(struct buildSettings &settings,
const struct Tile &tile, uint32_t position,
unsigned char *dstpData)
{
std::shared_ptr<Ogre::VertexData> srcVertexData =
tile.vertexData;
const Ogre::VertexDeclaration *srcDecl =
srcVertexData->vertexDeclaration;
const Ogre::VertexBufferBinding *srcBind =
srcVertexData->vertexBufferBinding;
int srcVertexCount = srcVertexData->vertexCount;
Ogre::HardwareVertexBufferSharedPtr srcVbuf =
srcBind->getBuffer(0);
std::shared_ptr<Ogre::IndexData> srcIndexData = tile.indexData;
int srcIndexCount = srcIndexData->indexCount;
Ogre::HardwareBufferLockGuard srcVertexLock(
srcVbuf, 0, srcVertexCount * srcDecl->getVertexSize(0),
Ogre::HardwareBuffer::HBL_READ_ONLY);
const Ogre::VertexDeclaration::VertexElementList &srcElements =
srcDecl->getElements();
int j;
unsigned char *srcpData =
static_cast<unsigned char *>(srcVertexLock.pData);
Ogre::HardwareBufferLockGuard srcVertexLock(
srcVbuf, 0, srcVertexCount * srcDecl->getVertexSize(0),
Ogre::HardwareBuffer::HBL_READ_ONLY);
const Ogre::VertexDeclaration::VertexElementList &srcElements =
srcDecl->getElements();
int j;
unsigned char *srcpData =
static_cast<unsigned char *>(srcVertexLock.pData);
for (j = 0; j < srcVertexCount; j++) {
unsigned char *srcData =
srcpData + j * srcVbuf->getVertexSize();
unsigned char *dstData =
dstpData + (settings.vertexOffset +
j) * settings.vbuf->getVertexSize();
processSingleVertex(settings, srcElements, position,
srcData, dstData);
for (j = 0; j < srcVertexCount; j++) {
unsigned char *srcData =
srcpData + j * srcVbuf->getVertexSize();
unsigned char *dstData =
dstpData + (settings.vertexOffset +
j) * settings.vbuf->getVertexSize();
processSingleVertex(settings, srcElements, position,
srcData, dstData);
}
}
Ogre::MeshPtr build(const Ogre::String &meshName)
{
buildSettings settings;
settings.meshName = meshName;
configureSettings(settings);
}
Ogre::MeshPtr build(const Ogre::String &meshName)
{
buildSettings settings;
settings.meshName = meshName;
configureSettings(settings);
{
Ogre::HardwareBufferLockGuard vertexLock(
settings.vbuf, 0,
settings.vertexCount *
settings.vdecl->getVertexSize(0),
Ogre::HardwareBuffer::HBL_NO_OVERWRITE);
Ogre::HardwareBufferLockGuard indexLock(
settings.sm->indexData->indexBuffer,
Ogre::HardwareBuffer::HBL_NO_OVERWRITE);
settings.vertexOffset = 0;
settings.indexOffset = 0;
unsigned char *dstpData =
static_cast<unsigned char *>(vertexLock.pData);
unsigned int *dstIndices =
static_cast<unsigned int *>(indexLock.pData);
Ogre::HardwareBufferLockGuard vertexLock(
settings.vbuf, 0,
settings.vertexCount *
settings.vdecl->getVertexSize(0),
Ogre::HardwareBuffer::HBL_NO_OVERWRITE);
Ogre::HardwareBufferLockGuard indexLock(
settings.sm->indexData->indexBuffer,
Ogre::HardwareBuffer::HBL_NO_OVERWRITE);
settings.vertexOffset = 0;
settings.indexOffset = 0;
unsigned char *dstpData =
static_cast<unsigned char *>(vertexLock.pData);
unsigned int *dstIndices =
static_cast<unsigned int *>(indexLock.pData);
for (const auto &tile : tiles) {
std::shared_ptr<Ogre::IndexData> srcIndexData =
tile.second.indexData;
int srcIndexCount = srcIndexData->indexCount;
std::shared_ptr<Ogre::VertexData> srcVertexData =
tile.second.vertexData;
int srcVertexCount = srcVertexData->vertexCount;
for (const auto &position :
tile.second.positions) {
processTile(settings, tile.second,
position, dstpData);
processIndex(settings, tile.second,
dstIndices);
settings.vertexOffset += srcVertexCount;
settings.indexOffset += srcIndexCount;
Ogre::Vector3i vposition;
unpackKey(position, vposition);
std::cout << "position: " << position
<< " " << vposition
<< std::endl;
for (const auto &tile : tiles) {
std::shared_ptr<Ogre::IndexData> srcIndexData =
tile.second.indexData;
int srcIndexCount = srcIndexData->indexCount;
std::shared_ptr<Ogre::VertexData> srcVertexData =
tile.second.vertexData;
int srcVertexCount = srcVertexData->vertexCount;
for (const auto &position :
tile.second.positions) {
processTile(settings, tile.second,
position, dstpData);
processIndex(settings, tile.second,
dstIndices);
settings.vertexOffset += srcVertexCount;
settings.indexOffset += srcIndexCount;
Ogre::Vector3i vposition;
unpackKey(position, vposition);
std::cout << "position: " << position
<< " " << vposition
<< std::endl;
}
}
settings.mesh->_setBounds(settings.bounds);
}
Ogre::LodConfig config(settings.mesh);
// config.advanced.useCompression = false;
// config.advanced.useVertexNormals = true;
config.advanced.preventPunchingHoles = true;
config.advanced.preventBreakingLines = true;
config.createGeneratedLodLevel(2, 0.15f);
config.createGeneratedLodLevel(20, 0.49f);
#if 0
}
settings.mesh->_setBounds(settings.bounds);
}
Ogre::LodConfig config(settings.mesh);
// config.advanced.useCompression = false;
// config.advanced.useVertexNormals = true;
config.advanced.preventPunchingHoles = true;
config.advanced.preventBreakingLines = true;
config.createGeneratedLodLevel(2, 0.15f);
config.createGeneratedLodLevel(20, 0.49f);
config.createGeneratedLodLevel(15, 0.49f);
config.createGeneratedLodLevel(150, 0.75f);
#endif
config.advanced.useBackgroundQueue = false;
Ogre::MeshLodGenerator::getSingleton().generateLodLevels(
config);
return settings.mesh;
}
void removeTile(const Ogre::String &name)
{
tiles.erase(name);
}
TiledMeshes()
{
}
config.advanced.useBackgroundQueue = false;
Ogre::MeshLodGenerator::getSingleton().generateLodLevels(
config);
return settings.mesh;
}
void removeTile(const Ogre::String &name)
{
tiles.erase(name);
}
TiledMeshes()
{
}
};
}

View File

@@ -3,6 +3,10 @@
#include <flecs.h>
#include <nlohmann/json.hpp>
#include <Ogre.h>
namespace Procedural
{
class TriangleBuffer;
}
namespace Ogre
{
struct LodConfig;
@@ -25,6 +29,15 @@ struct TerrainItemMeshNode {
Ogre::SceneNode *itemNode;
Ogre::StaticGeometry *geo;
};
struct FurnitureItem {
Ogre::String properties;
std::vector<Ogre::String> tags;
};
struct FurnitureInstance {
Ogre::SceneNode *furniture;
};
struct GeometryUpdateItem {};
struct TownCollider {};
struct StaticGeometryModule {
@@ -37,8 +50,12 @@ struct StaticGeometryModule {
static void setItemProperties(flecs::entity id,
Ogre::String properties);
static const Ogre::String &getItemProperties(flecs::entity id);
static void saveItems();
static void loadTemplates();
static void saveTemplates();
static void saveItems();
static void loadItems();
static void saveFurniture();
static void loadFurniture();
static void getItemPositionPerSlot(long x, long y,
std::list<Ogre::Vector3> *positions);
static void getItemPositions(std::list<Ogre::Vector3> *positions);
@@ -49,6 +66,13 @@ struct StaticGeometryModule {
std::list<std::pair<flecs::entity, Ogre::String> > *items);
static void createItemGeometry(flecs::entity e);
static void destroyItemGeometry(flecs::entity e);
static nlohmann::json &getTemplates();
static void updateItemGeometry(flecs::entity e);
static void addTriangleBufferWork(const Ogre::String &meshName,
Ogre::StaticGeometry *geo,
const Ogre::Vector3 &position,
const Ogre::Quaternion &rotation,
const Procedural::TriangleBuffer &tb);
};
}
#endif

View File

@@ -4,7 +4,7 @@ find_package(Bullet REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(OgreProcedural REQUIRED CONFIG)
add_library(items STATIC items.cpp harbour.cpp temple.cpp town.cpp)
target_include_directories(items PUBLIC .)
target_include_directories(items PUBLIC . ${CMAKE_SOURCE_DIR}/src/FastNoiseLite)
target_link_libraries(items PRIVATE
flecs::flecs_static
nlohmann_json::nlohmann_json
@@ -13,4 +13,4 @@ target_link_libraries(items PRIVATE
OgreBites
editor
physics
)
)

View File

@@ -22,6 +22,22 @@ namespace ECS
{
namespace Items
{
void runScriptsForAllTowns()
{
std::pair<flecs::entity, Ogre::String> selected_item;
std::list<std::pair<flecs::entity, Ogre::String> > items;
StaticGeometryModule::getItemsProperties(&items);
for (const auto &item : items) {
nlohmann::json j = nlohmann::json::parse(item.second);
Ogre::String itemType = j["type"].get<Ogre::String>();
if (itemType == "town") {
Items::runAllScriptsForTown(item.first);
if (item.first.has<TerrainItemNode>())
Geometry::updateItemGeometry(item.first);
}
}
StaticGeometryModule::saveItems();
}
void showItemPopup(const std::pair<flecs::entity, Ogre::String> &item)
{
Ogre::String popupLabel =
@@ -131,8 +147,7 @@ void showItemPopup(const std::pair<flecs::entity, Ogre::String> &item)
orientation;
item.first.modified<TerrainItem>();
StaticGeometryModule::saveItems();
StaticGeometryModule::destroyItemGeometry(item.first);
StaticGeometryModule::createItemGeometry(item.first);
StaticGeometryModule::updateItemGeometry(item.first);
}
if (itemType == "harbour")
createHarbourPopup(item);
@@ -194,17 +209,39 @@ namespace Geometry
{
void setupLods(Ogre::LodConfig &config)
{
int count = 0;
// config.advanced.useCompression = false;
config.advanced.useVertexNormals = true;
config.advanced.preventPunchingHoles = true;
config.advanced.preventBreakingLines = true;
config.createGeneratedLodLevel(10, 0.15f);
config.createGeneratedLodLevel(50, 0.25f);
config.createGeneratedLodLevel(100, 0.36f);
config.createGeneratedLodLevel(200, 0.50f);
// config.createGeneratedLodLevel(50, 0.25f);
// config.createGeneratedLodLevel(100, 0.36f);
// config.createGeneratedLodLevel(200, 0.50f);
config.createGeneratedLodLevel(500, 0.85f);
config.advanced.useBackgroundQueue = false;
Ogre::MeshLodGenerator::getSingleton().generateLodLevels(config);
std::cout << "mesh name: " << config.mesh->getName() << std::endl;
bool crash = false;
for (count = 0; count < config.mesh->getSubMeshes().size(); count++) {
Ogre::SubMesh *submesh = config.mesh->getSubMeshes()[count];
std::cout << "unprocessed submesh: " << count << " " << submesh
<< std::endl;
if (submesh)
submesh->parent = config.mesh.get();
else
crash = true;
}
Ogre::MeshLodGenerator::getSingleton().generateLodLevels(config);
for (count = 0; count < config.mesh->getSubMeshes().size(); count++) {
Ogre::SubMesh *submesh = config.mesh->getSubMeshes()[count];
std::cout << "submesh: " << count << " " << submesh
<< std::endl;
if (submesh)
submesh->parent = config.mesh.get();
else
crash = true;
}
OgreAssert(!crash, "no submesh");
}
Ogre::StaticGeometry *createStaticGeometry(flecs::entity e)
@@ -294,6 +331,14 @@ void destroyItemGeometry(flecs::entity e)
#endif
e.remove<TerrainItemNode>();
}
void updateItemGeometry(flecs::entity e)
{
OgreAssert(e.has<TerrainItem>(), "not terrain item");
if (e.has<TerrainItemNode>())
destroyItemGeometry(e);
createItemGeometry(e);
}
flecs::entity createMeshGeometry(const Ogre::String &meshName,
flecs::entity parente,
Ogre::SceneNode *sceneNode,
@@ -322,7 +367,7 @@ flecs::entity createMeshGeometry(const Ogre::String &meshName,
.set<TerrainItemMeshNode>({ sceneNode, geo });
JoltPhysicsWrapper::getSingleton().addBody(id,
JPH::EActivation::Activate);
return e;
return e;
}
}

View File

@@ -9,6 +9,7 @@ namespace Items
void showItemPopup(const std::pair<flecs::entity, Ogre::String> &item);
void showItemButtons(const std::pair<flecs::entity, Ogre::String> &item);
void createItemsMenu();
void runScriptsForAllTowns();
}
namespace Geometry
{
@@ -53,6 +54,7 @@ struct harbourMaker {
};
void createItemGeometry(flecs::entity e);
void destroyItemGeometry(flecs::entity e);
void updateItemGeometry(flecs::entity e);
flecs::entity createMeshGeometry(const Ogre::String &meshName,
flecs::entity parente,
Ogre::SceneNode *sceneNode,

File diff suppressed because it is too large Load Diff

View File

@@ -12,25 +12,15 @@ namespace Items
void createTownItem();
void createTownMenu();
void createTownPopup(const std::pair<flecs::entity, Ogre::String> item);
void runAllScriptsForTown(flecs::entity e);
}
namespace Geometry
{
void clampUV(flecs::entity e, Procedural::TriangleBuffer &tb,
const Ogre::String &rectKey);
Ogre::MaterialPtr createTownMaterial(flecs::entity e, bool force = false);
void createCells(flecs::entity e, const nlohmann::json &jdistrict, int index,
Ogre::SceneNode *sceneNode, Ogre::StaticGeometry *geo);
void createTown(flecs::entity e, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo);
void createTownPlazza(flecs::entity e, const nlohmann::json &jdistrict,
int index, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo);
void createTownLots(flecs::entity e, const nlohmann::json &jdistrict,
int index, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo);
void createTownRoofs(flecs::entity e, const nlohmann::json &jdistrict,
int index, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo);
}
}
#endif

View File

@@ -90,7 +90,10 @@ public:
Layers::MOVING; // Non moving only collides with moving
case Layers::MOVING:
return true; // Moving collides with everything
default:
case Layers::SENSORS:
return inObject2 ==
Layers::MOVING; // Non moving only collides with moving
default:
JPH_ASSERT(false);
return false;
}
@@ -107,7 +110,8 @@ public:
mObjectToBroadPhase[Layers::NON_MOVING] =
BroadPhaseLayers::NON_MOVING;
mObjectToBroadPhase[Layers::MOVING] = BroadPhaseLayers::MOVING;
}
mObjectToBroadPhase[Layers::SENSORS] = BroadPhaseLayers::MOVING;
}
virtual uint GetNumBroadPhaseLayers() const override
{
@@ -1330,7 +1334,18 @@ public:
OgreAssert(result.Get(), "Can not create com offset shape");
return result.Get();
}
void applyBuoyancyImpulse(JPH::BodyID id,
JPH::ShapeRefC
createRotatedTranslatedShape(const Ogre::Vector3 &offset,
const Ogre::Quaternion rotation,
JPH::ShapeRefC shape)
{
return JPH::RotatedTranslatedShapeSettings(
JoltPhysics::convert(offset),
JoltPhysics::convert(rotation), shape)
.Create()
.Get();
}
void applyBuoyancyImpulse(JPH::BodyID id,
const Ogre::Vector3 &surfacePosition,
const Ogre::Vector3 &surfaceNormal,
float buoyancy, float linearDrag,
@@ -1597,7 +1612,14 @@ JPH::ShapeRefC
JoltPhysicsWrapper::createOffsetCenterOfMassShape(const Ogre::Vector3 &offset,
JPH::ShapeRefC shape)
{
return phys->createOffsetCenterOfMassShape(offset, shape);
return phys->createOffsetCenterOfMassShape(offset, shape);
}
JPH::ShapeRefC JoltPhysicsWrapper::createRotatedTranslatedShape(
const Ogre::Vector3 &offset, const Ogre::Quaternion rotation,
JPH::ShapeRefC shape)
{
return phys->createRotatedTranslatedShape(offset, rotation, shape);
}
JPH::BodyID

View File

@@ -26,7 +26,8 @@ namespace Layers
{
static constexpr JPH::ObjectLayer NON_MOVING = 0;
static constexpr JPH::ObjectLayer MOVING = 1;
static constexpr JPH::ObjectLayer NUM_LAYERS = 2;
static constexpr JPH::ObjectLayer SENSORS = 2;
static constexpr JPH::ObjectLayer NUM_LAYERS = 3;
};
// Each broadphase layer results in a separate bounding volume tree in the broad phase. You at least want to have
@@ -134,7 +135,10 @@ public:
JPH::ShapeRefC
createOffsetCenterOfMassShape(const Ogre::Vector3 &offset,
JPH::ShapeRefC shape);
JPH::BodyID createBody(const JPH::BodyCreationSettings &settings);
JPH::ShapeRefC
createRotatedTranslatedShape(const Ogre::Vector3 &offset, const Ogre::Quaternion rotation,
JPH::ShapeRefC shape);
JPH::BodyID createBody(const JPH::BodyCreationSettings &settings);
JPH::BodyID createBody(const JPH::Shape *shape, float mass,
const Ogre::Vector3 &position,
const Ogre::Quaternion &rotation,