Compare commits

...

6 Commits

45 changed files with 2522 additions and 2260 deletions

View File

@@ -29,6 +29,7 @@ find_package(assimp REQUIRED CONFIG)
find_package(OgreProcedural REQUIRED CONFIG)
find_package(pugixml REQUIRED CONFIG)
find_package(flecs REQUIRED CONFIG)
find_package(Tracy REQUIRED CONFIG)
add_library(fix::assimp INTERFACE IMPORTED)
set_target_properties(fix::assimp PROPERTIES
@@ -81,11 +82,12 @@ add_executable(Game Game.cpp ${WATER_SRC})
target_include_directories(Game PRIVATE src/gamedata)
target_link_libraries(Game OgreBites OgrePaging OgreTerrain OgreMeshLodGenerator
OgreProcedural::OgreProcedural
OgreCrowd
GameData
sound
sceneloader physics
OgreCrowd
sceneloader physics lua
flecs::flecs_static
Tracy::TracyClient
-Wl,--as-needed
)
if(OGRE_STATIC)
@@ -244,7 +246,7 @@ add_custom_target(stage_files ALL DEPENDS ${CMAKE_BINARY_DIR}/resources.cfg ${MA
add_custom_target(remove_scenes COMMAND rm -f ${VRM_SOURCE} ${VRM_IMPORTED_BLENDS} ${CHARACTER_GLBS})
target_compile_definitions(Game PRIVATE FLECS_CPP_NO_AUTO_REGISTRATION JPH_PROFILE_ENABLED)
target_compile_definitions(Game PRIVATE FLECS_CPP_NO_AUTO_REGISTRATION JPH_PROFILE_ENABLED JPH_DEBUG_RENDERER JPH_PROFILE_ENABLED JPH_DOUBLE_PRECISION)
install(TARGETS Game DESTINATION bin)
install(TARGETS Editor DESTINATION bin)

View File

@@ -10,7 +10,6 @@
#include <OgreTimer.h>
#include <OgreMeshLodGenerator.h>
// #include "water/water.h"
#include "GameData.h"
#include "Components.h"
#include "CharacterModule.h"
@@ -21,6 +20,7 @@
#include "PhysicsModule.h"
#include "physics.h"
#include "sound.h"
#include <tracy/Tracy.hpp>
class App;
class SkyRenderer : public Ogre::SceneManager::Listener {
protected:
@@ -477,6 +477,7 @@ public:
}
void updateWorld(float delta)
{
ZoneScoped;
if (!ECS::get().has<ECS::GUI>())
goto end;
{
@@ -628,11 +629,11 @@ end:
void setupInput()
{
}
JoltPhysicsWrapper *mJolt;
JoltPhysicsWrapper *mJolt;
void createContent()
{
int i;
mJolt = new JoltPhysicsWrapper(mScnMgr, mCameraNode);
mJolt = new JoltPhysicsWrapper(mScnMgr, mCameraNode);
sky = new SkyBoxRenderer(getSceneManager());
bool drawFirst = true;
@@ -738,40 +739,50 @@ end:
};
void KeyboardListener::frameRendered(const Ogre::FrameEvent &evt)
{
if (fps_timer.getMilliseconds() > 1000.0f) {
std::cout << "FPS: "
<< mApp->getRenderWindow()->getStatistics().lastFPS
<< " ";
std::cout << "Draw calls: "
<< mApp->getRenderWindow()->getStatistics().batchCount
<< " ";
fps_timer.reset();
std::cout << "Drops: "
<< mApp->getRenderWindow()
->getStatistics()
.vBlankMissCount
<< "\n";
fps_timer.reset();
}
if (!isGuiEnabled() ||
(isGuiEnabled() && ECS::get<ECS::GUI>().narrationBox)) {
mApp->updateWorld(evt.timeSinceLastFrame);
if (mInitDelay >= 0.0f)
mInitDelay -= evt.timeSinceLastFrame;
}
{
ZoneScopedN("frameRendered");
if (fps_timer.getMilliseconds() > 1000.0f) {
std::cout << "FPS: "
<< mApp->getRenderWindow()
->getStatistics()
.lastFPS
<< " ";
std::cout << "Draw calls: "
<< mApp->getRenderWindow()
->getStatistics()
.batchCount
<< " ";
fps_timer.reset();
std::cout << "Drops: "
<< mApp->getRenderWindow()
->getStatistics()
.vBlankMissCount
<< "\n";
fps_timer.reset();
}
if (!isGuiEnabled() ||
(isGuiEnabled() && ECS::get<ECS::GUI>().narrationBox)) {
mApp->updateWorld(evt.timeSinceLastFrame);
if (mInitDelay >= 0.0f)
mInitDelay -= evt.timeSinceLastFrame;
}
if (!isGuiEnabled() && ECS::get().has<ECS::Input>()) {
ECS::Input &input = ECS::get().get_mut<ECS::Input>();
input.control = control;
input.mouse = mouse;
input.mouse_abs = mouse_abs;
mouse.x = 0;
mouse.y = 0;
input.wheel_y = wheel_y;
wheel_y = 0;
input.mouse_moved = mouse_moved;
input.wheel_moved = wheel_moved;
if (!isGuiEnabled() && ECS::get().has<ECS::Input>()) {
ECS::Input &input = ECS::get().get_mut<ECS::Input>();
input.control = control;
input.mouse = mouse;
input.mouse_abs = mouse_abs;
mouse.x = 0;
mouse.y = 0;
input.wheel_y = wheel_y;
wheel_y = 0;
input.mouse_moved = mouse_moved;
input.wheel_moved = wheel_moved;
}
}
#ifdef USE_RENDER_LOOP
FrameMark;
#endif
}
int main()
@@ -784,7 +795,23 @@ int main()
// KeyHandler keyHandler;
// ctx.addInputListener(&keyHandler);
ctx.enableDbgDraw(false);
#ifdef USE_RENDER_LOOP
ctx.getRoot()->startRendering();
#else
auto renderSystem = Ogre::Root::getSingleton().getRenderSystem();
OgreAssert(renderSystem, "no RenderSystem");
renderSystem->_initRenderTargets();
Ogre::Root::getSingleton().clearEventTimes();
Ogre::Root::getSingleton().queueEndRendering(false);
while (!Ogre::Root::getSingleton().endRenderingQueued()) {
{
ZoneScopedN("render");
if (!Ogre::Root::getSingleton().renderOneFrame())
break;
}
FrameMark;
}
#endif
ctx.setWindowGrab(false);
ctx.closeApp();
return 0;

View File

@@ -18,14 +18,20 @@ 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 ${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}_baked.blend
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${FURNITURE_FILE} ${CMAKE_CURRENT_BINARY_DIR}/${FURNITURE_FILE}
COMMAND ${BLENDER} -b ${CMAKE_CURRENT_BINARY_DIR}/${FURNITURE_FILE} -Y -P
${CMAKE_CURRENT_SOURCE_DIR}/bake_furniture.py
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${FURNITURE_FILE} ${CMAKE_CURRENT_SOURCE_DIR}/bake_furniture.py)
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
COMMAND ${BLENDER} -b ${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}_baked.blend
-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)
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${FILE_NAME}_baked.blend ${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,125 @@
import bpy
def get_all_children_recursive(obj, mesh_list):
"""Recursively finds all mesh objects in the hierarchy."""
for child in obj.children:
if child.type == 'MESH' and child.data.uv_layers:
mesh_list.append(child)
get_all_children_recursive(child, mesh_list)
def setup_furniture_atlas():
# 1. Gather valid objects: Children of empties starting with "furniture-"
furniture_meshes = []
roots = [o for o in bpy.data.objects if o.name.startswith("furniture-")]
for root in roots:
# If the root itself is a mesh with UVs, add it
if root.type == 'MESH' and root.data.uv_layers:
furniture_meshes.append(root)
# Find all nested children
get_all_children_recursive(root, furniture_meshes)
# Remove duplicates if any (in case of complex parenting)
furniture_meshes = list(set(furniture_meshes))
if not furniture_meshes:
print("No recursive mesh objects found with UVs.")
return
furniture_objs = furniture_meshes
if not furniture_objs:
print("No valid furniture mesh objects found with UVs.")
return
# 2. Manage the Atlas Image (FurnitureColor)
image_name = "FurnitureColor"
if image_name in bpy.data.images:
atlas_img = bpy.data.images[image_name]
else:
atlas_img = bpy.data.images.new(image_name, width=2048, height=2048)
# 3. Setup Materials & Nodes
processed_mats = set()
for obj in furniture_objs:
for slot in obj.material_slots:
mat = slot.material
if mat and mat not in processed_mats:
mat.use_nodes = True
nodes = mat.node_tree.nodes
# Find or create the target node
bake_node = nodes.get("ATLAS_TARGET")
if not bake_node:
bake_node = nodes.new('ShaderNodeTexImage')
bake_node.name = "ATLAS_TARGET"
bake_node.image = atlas_img
nodes.active = bake_node # Essential for the Bake operator
processed_mats.add(mat)
# 4. Selection & Context Setup
bpy.ops.object.select_all(action='DESELECT')
for obj in furniture_objs:
obj.select_set(True)
bpy.context.view_layer.objects.active = furniture_objs[0]
# 5. Bake Configuration (Cycles, Diffuse, Color only)
scene = bpy.context.scene
scene.render.engine = 'CYCLES'
scene.cycles.device = 'CPU'
scene.cycles.samples = 10
scene.render.bake.use_pass_direct = False
scene.render.bake.use_pass_indirect = False
scene.render.bake.use_pass_color = True
scene.render.bake.target = 'IMAGE_TEXTURES'
print("Baking Furniture Atlas...")
override = {
'active_object': bpy.context.view_layer.objects.active,
'selected_objects': bpy.context.selected_objects,
}
with bpy.context.temp_override(**override):
bpy.ops.object.bake(type='DIFFUSE', margin=2)
atlas_img.pack()
# 6. Create and Assign Final Atlas Material
atlas_mat_name = "M_Furniture_Atlas"
if atlas_mat_name in bpy.data.materials:
atlas_mat = bpy.data.materials[atlas_mat_name]
else:
atlas_mat = bpy.data.materials.new(name=atlas_mat_name)
atlas_mat.use_nodes = True
bsdf = atlas_mat.node_tree.nodes.get("Principled BSDF")
tex_node = atlas_mat.node_tree.nodes.new('ShaderNodeTexImage')
tex_node.image = atlas_img
atlas_mat.node_tree.links.new(tex_node.outputs['Color'], bsdf.inputs['Base Color'])
nodes = atlas_mat.node_tree.nodes
bsdf = nodes.get("Principled BSDF")
if bsdf:
bsdf.inputs['Roughness'].default_value = 1.0
# Reassign all valid objects to the new material
for obj in furniture_objs:
obj.data.materials.clear()
obj.data.materials.append(atlas_mat)
print("Process Complete. Atlas material applied.")
for mat in bpy.data.materials:
if mat.users == 0:
bpy.data.materials.remove(mat)
bpy.ops.outliner.orphans_purge(do_local_ids=True, do_linked_ids=True, do_recursive=True)
scene.render.engine = 'BLENDER_EEVEE'
current_path = bpy.data.filepath
if current_path:
new_path = current_path.replace(".blend", "_baked.blend")
bpy.ops.wm.save_as_mainfile(filepath=new_path)
print(f"File saved to: {new_path}")
else:
print("Warning: File not saved (unsaved blend file).")
setup_furniture_atlas()

View File

@@ -81,7 +81,7 @@ class TEST_PT_ObjectPanel(bpy.types.Panel):
# Link button with preset arguments
row = layout.row(align=True)
op = row.operator("test.link_and_play", text="Link & Sit", icon='PLAY')
op.filepath = "//../../edited-normal-male.blend" # SET YOUR FILEPATH
op.filepath = "//../../characters/edited-normal-male.blend" # SET YOUR FILEPATH
op.rig_name = "male" # SET YOUR RIG NAME
op.action_name = "sitting-chair" # SET YOUR ACTION NAME

View File

@@ -11,18 +11,24 @@ add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/edited-normal-${EDITED_BLEND}.blend
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/edited-normal-${EDITED_BLEND}.blend
${CMAKE_BINARY_DIR}/assets/blender/vrm-vroid-normal-${EDITED_BLEND}.blend
${CMAKE_CURRENT_SOURCE_DIR}/copy_animations.py
${CMAKE_BINARY_DIR}/assets/blender/mixamo
COMMAND ${CMAKE_COMMAND}
-E copy ${CMAKE_CURRENT_SOURCE_DIR}/edited-normal-${EDITED_BLEND}.blend
${CMAKE_CURRENT_BINARY_DIR}/edited-normal-${EDITED_BLEND}.blend
COMMAND ${BLENDER} -b -Y
${CMAKE_CURRENT_BINARY_DIR}/edited-normal-${EDITED_BLEND}.blend
-P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/copy_animations.py --
-P ${CMAKE_CURRENT_SOURCE_DIR}/copy_animations.py --
${CMAKE_BINARY_DIR}/assets/blender/vrm-vroid-normal-${EDITED_BLEND}.blend ${EDITED_BLEND}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
list(APPEND EDITED_BLEND_TARGETS ${CMAKE_BINARY_DIR}/assets/blender/characters/edited-normal-${EDITED_BLEND}.blend)
list(APPEND CHARACTER_GLBS ${CMAKE_BINARY_DIR}/characters/${EDITED_BLEND}/normal-${EDITED_BLEND}.glb)
endforeach()
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/assets/blender/mixamo
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets/blender/mixamo ${CMAKE_BINARY_DIR}/assets/blender/mixamo
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/mixamo
)
set(VRM_IMPORTED_BLENDS
${CMAKE_BINARY_DIR}/assets/blender/vrm-vroid-normal-female.blend
@@ -30,16 +36,48 @@ set(VRM_IMPORTED_BLENDS
${CMAKE_BINARY_DIR}/assets/blender/shapes/male/vrm-vroid-normal-male-chibi.blend
)
#add_custom_command(
# OUTPUT ${CHARACTER_GLBS}
# COMMAND ${CMAKE_COMMAND} -E make_directory ${CREATE_DIRECTORIES}
# COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py
# COMMAND ${CMAKE_COMMAND} -D FILE=${CMAKE_BINARY_DIR}/characters/male/normal-male.glb -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/check_file_size.cmake
# COMMAND ${CMAKE_COMMAND} -D FILE=${CMAKE_BINARY_DIR}/characters/female/normal-female.glb -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/check_file_size.cmake
# COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${CHARACTER_GLBS}
# DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py ${VRM_IMPORTED_BLENDS} ${EDITED_BLEND_TARGETS}
# WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set(FEMALE_OBJECTS "Body;Hair;Face;BackHair;Tops;Bottoms;Shoes;Accessory")
set(MALE_OBJECTS "Body;Hair;Face;BackHair;Tops;Bottoms;Shoes;Accessory")
add_custom_command(
OUTPUT ${CHARACTER_GLBS}
COMMAND ${CMAKE_COMMAND} -E make_directory ${CREATE_DIRECTORIES}
COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py
COMMAND ${CMAKE_COMMAND} -D FILE=${CMAKE_BINARY_DIR}/characters/male/normal-male.glb -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/check_file_size.cmake
COMMAND ${CMAKE_COMMAND} -D FILE=${CMAKE_BINARY_DIR}/characters/female/normal-female.glb -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/check_file_size.cmake
COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${CHARACTER_GLBS}
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models.py ${VRM_IMPORTED_BLENDS} ${EDITED_BLEND_TARGETS}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
OUTPUT ${CMAKE_BINARY_DIR}/characters/male/normal-male.glb
COMMAND ${CMAKE_COMMAND} -E make_directory ${CREATE_DIRECTORIES}
COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models2.py --
${CMAKE_CURRENT_BINARY_DIR}/edited-normal-male.blend
${CMAKE_BINARY_DIR}/characters/male/normal-male.glb
"${MALE_OBJECTS}"
"male"
tmp-edited-male.blend
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models2.py
${VRM_IMPORTED_BLENDS}
${CMAKE_CURRENT_BINARY_DIR}/edited-normal-male.blend
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
VERBATIM
)
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/characters/female/normal-female.glb
COMMAND ${CMAKE_COMMAND} -E make_directory ${CREATE_DIRECTORIES}
COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models2.py --
${CMAKE_CURRENT_BINARY_DIR}/edited-normal-female.blend
${CMAKE_BINARY_DIR}/characters/female/normal-female.glb
"${FEMALE_OBJECTS}"
"female"
tmp-edited-female.blend
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/export_models2.py
${VRM_IMPORTED_BLENDS}
${CMAKE_CURRENT_BINARY_DIR}/edited-normal-female.blend
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
VERBATIM
)
set(VRM_SOURCE)
@@ -84,7 +122,7 @@ function(blender_import_vrm BLEND VRM EDITABLE RIG)
get_filename_component(VRM_NAME ${VRM} NAME_WE)
add_custom_command(OUTPUT ${BLEND}
COMMAND ${CMAKE_COMMAND} -E make_directory ${CREATE_DIRECTORIES}
COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/import_vrm2.py -- ${VRM_NAME}.vrm ${TARGET_NAME}.blend ${EDITABLE} ${RIG}
COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/import_vrm2.py -- ${VRM_NAME}.vrm ${BLEND} ${EDITABLE} ${RIG}
COMMAND ${CMAKE_COMMAND} -D FILE=${BLEND} -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/check_file_size.cmake
COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${BLEND}
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/scripts/import_vrm2.py

View File

@@ -0,0 +1,64 @@
import bpy
import sys
import os
argv = sys.argv
argv = argv[argv.index("--") + 1:]
sys.path.insert(0, os.getcwd() + "/assets/blender/scripts")
source_filepath = argv[0]
armature_name = argv[1]
print("starting...")
def copy_actions_and_create_nla_tracks(source_filepath, target_object_name):
"""
Copies actions from a source Blender file that do not exist in the current
file, creates NLA tracks for them on a target object, and saves the file.
Args:
source_filepath (str): The full path to the source Blender file.
target_object_name (str): The name of the object in the current file
to which the actions and NLA tracks will be applied.
"""
# 1. Link or Append Actions from the Source File
with bpy.data.libraries.load(source_filepath, link=False) as (data_from, data_to):
source_action_names = data_from.actions
current_action_names = {action.name for action in bpy.data.actions}
actions_to_append = [name for name in source_action_names if name not in current_action_names]
data_to.actions = actions_to_append
print(actions_to_append)
# Get the target object
target_object = bpy.data.objects.get(target_object_name)
if not target_object:
print(f"Error: Object '{target_object_name}' not found in the current file.")
return
# Ensure the object has an NLA editor
if not target_object.animation_data:
target_object.animation_data_create()
# 2. Iterate through newly imported actions and create NLA tracks
for action in data_to.actions:
# Check if an action with the same name already exists in the current file
# Add the action to the NLA editor as a strip
nla_track = target_object.animation_data.nla_tracks.new()
nla_track.name = f"NLA_Track_{action.name}"
nla_track.strips.new(name=action.name, start=1, action=action)
print(f"Created NLA track for action: {action.name}")
# 3. Save the current Blender file
bpy.ops.wm.save_as_mainfile(filepath=bpy.context.blend_data.filepath)
print(f"File saved: {bpy.context.blend_data.filepath}")
# --- Usage Example ---
if __name__ == "__main__":
# Replace with your actual source file path and target object name
source_file = source_filepath
target_obj = armature_name
copy_actions_and_create_nla_tracks(source_file, target_obj)

View File

@@ -18,7 +18,10 @@ sys.path.insert(1, incpath + "/blender2ogre")
import io_ogre
io_ogre.register()
try:
io_ogre.register()
except:
pass
gltf_file = argv[0]
print("Exporting to " + gltf_file)

View File

@@ -0,0 +1,296 @@
#!/usr/bin/env python
import os, sys, time
import bpy
from math import pi
import glob
import shutil
from mathutils import Vector, Matrix
from math import radians, pi
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
#from settings import ExportMappingFemale, ExportMappingMale, ExportMappingMaleBabyShape, ExportMappingMaleEdited, ExportMappingFemaleEdited, ExportMappingMaleTestShapeEdited, ExportMappingMaleBaseShapeEdited
argv = sys.argv
argv = argv[argv.index("--") + 1:]
basepath = os.getcwd()
def check_bone(bname):
ok = True
baddie = ["ctrl_", "mch_", "MCH_", "Ctrl_", "Mch_"]
for bd in baddie:
if bname.startswith(bd):
ok = False
break
return ok
def prepare_armature(mapping):
print("Preparing...")
bpy.ops.object.armature_add(enter_editmode=False)
new_armature = bpy.context.object
orig_armature = bpy.data.objects[mapping.armature_name]
armature_name = orig_armature.name
orig_armature.name = orig_armature.name + "_orig"
new_armature.name = armature_name
queue = []
if new_armature.animation_data is None:
new_armature.animation_data_create()
bpy.context.view_layer.objects.active = new_armature
bpy.ops.object.mode_set(mode='EDIT')
for b in new_armature.data.edit_bones:
new_armature.data.edit_bones.remove(b)
bpy.context.view_layer.objects.active = orig_armature
bpy.ops.object.mode_set(mode='EDIT')
for b in orig_armature.data.edit_bones:
print(b.name)
if b.parent is None:
queue.append(b.name)
print("Copying bones...")
while len(queue) > 0:
item = queue.pop(0)
print(item)
itemb = orig_armature.data.edit_bones[item]
if not itemb.use_deform and not check_bone(item):
continue
for cb in orig_armature.data.edit_bones:
if cb.parent == itemb:
queue.append(cb.name)
nb = new_armature.data.edit_bones.new(item)
nb.name = item
nb.head = itemb.head
nb.tail = itemb.tail
nb.matrix = itemb.matrix
nb.use_deform = itemb.use_deform
if itemb.parent is not None:
ok = True
pname = itemb.parent.name
while not check_bone(pname):
bparent = itemb.parent.parent
if bparent is None:
ok = False
break
pname = bparent.name
if ok:
nb.parent = new_armature.data.edit_bones[itemb.parent.name]
else:
nb.parent = None
else:
nb.parent = None
nb.use_connect = itemb.use_connect
# drivers_data = new_armature.animation_data.drivers
print("Creating constraints...")
bpy.context.view_layer.objects.active = new_armature
bpy.ops.object.mode_set(mode='OBJECT')
bpy.context.view_layer.objects.active = orig_armature
bpy.ops.object.mode_set(mode='OBJECT')
for b in new_armature.pose.bones:
print(b.name)
c = b.constraints.new(type='COPY_TRANSFORMS')
c.target = orig_armature
c.subtarget = b.name
for obj in bpy.data.objects:
if obj.parent == orig_armature:
obj.parent = new_armature
for mod in obj.modifiers:
if mod.type == 'ARMATURE':
mod.object = new_armature
print("Baking actions...")
bpy.context.view_layer.objects.active = new_armature
bpy.ops.object.mode_set(mode='POSE')
for track in orig_armature.animation_data.nla_tracks:
print(track.name)
for s in track.strips:
action = s.action
print(action.name)
orig_armature.animation_data.action = action
new_armature.animation_data.action = None
bpy.context.view_layer.objects.active = new_armature
firstFrame = int(s.action_frame_start)
lastFrame = int(s.action_frame_end)
bpy.ops.nla.bake(frame_start=firstFrame, frame_end=lastFrame, step=5, only_selected=False, visual_keying=True, clear_constraints=False, clean_curves=True, use_current_action=False, bake_types={'POSE'})
aname = orig_armature.animation_data.action.name
orig_armature.animation_data.action.name = "bake_" + aname
new_armature.animation_data.action.name = aname
track = new_armature.animation_data.nla_tracks.new()
track.name = aname
track.strips.new(track.name, int(new_armature.animation_data.action.frame_range[0]), new_armature.animation_data.action)
track.mute = True
track.lock = True
print("Removing constraints...")
for b in new_armature.pose.bones:
for c in b.constraints:
b.constraints.remove(c)
new_armature.animation_data.action = bpy.data.actions[mapping.default_action]
bpy.context.view_layer.objects.active = new_armature
bpy.ops.object.mode_set(mode='OBJECT')
# track = new_armature.animation_data.nla_tracks.new()
# track.name = action.name
def clamp_angle_deg(angle, min_angle_deg, max_angle_deg):
min_angle = radians(min_angle_deg)
max_angle = radians(max_angle_deg)
if angle < min_angle:
angle = min_angle
if angle > max_angle:
angle = max_angle
return angle
def angle_to_linear(angle, divider):
if angle < 0.0:
return angle / divider
else:
return 0.0
def angle_to_linear_x(bone, angle):
skel = bpy.data.objects["skeleton_orig"]
left_base = "ctrl_base_upperleg.L.001"
right_base = "ctrl_base_upperleg.R.001"
base = ""
if base == "":
for e in ["_R", ".R"]:
if bone.name.endswith(e):
base = right_base
break
if base == "":
for e in ["_L", ".L"]:
if bone.name.endswith(e):
base = left_base
break
if base == "":
for e in ["_R.", ".R."]:
if bone.name.find(e) >= 0:
base = right_base
break
if base == "":
for e in ["_L.", ".L."]:
if bone.name.find(e) >= 0:
base = left_base
break
mul = skel.pose.bones[base]["to_linear_x_base"]
offset = skel.pose.bones[base]["angle_offset"]
# print("bone: ", bone.name, "base: ", base, "mul: ", mul)
# print("angle: ", angle, " angle_offset: ", offset, " angle_sum: ", angle + offset)
print("offset: ", mul * (angle + offset), "bone: ", base, "angle: ", angle)
return (angle + offset) * mul
def extra_linear(angle, offset):
ret = 0.0
offt = offset * angle * 2.0 / -radians(-90)
if angle * 2.0 < -radians(65):
if angle * 2.0 > -radians(65):
ret += offset
else:
ret += offt
return ret
mapping_blend_path = argv[0]
mapping_gltf_path = argv[1]
mapping_objects = argv[2]
mapping_armature_name = argv[3]
mapping_outfile = argv[4]
#for mapping in [ExportMappingFemale(), ExportMappingMale(), ExportMappingMaleBabyShape(), ExportMappingMaleEdited(), ExportMappingFemaleEdited(), ExportMappingMaleTestShapeEdited(), ExportMappingMaleBaseShapeEdited()]:
class CommandLineMapping:
blend_path = mapping_blend_path
gltf_path = mapping_gltf_path
# ogre_scene = "characters/female/vroid-normal-female.scene"
inner_path = "Object"
# objs = ["male", "Body", "Hair", "Face", "BackHair", "Tops", "Bottoms", "Shoes", "Accessory"]
# objs = ["female", "Body", "Hair", "Face", "BackHair", "Tops", "Bottoms", "Shoes", "Accessory"]
objs = []
armature_name = mapping_armature_name
outfile = mapping_outfile
default_action = 'default'
def __init__(self):
self.objs = [mapping_armature_name]
if len(mapping_objects) > 0:
self.objs += [o.strip() for o in mapping_objects.split(";")]
self.files = []
for fobj in self.objs:
self.files.append({"name": fobj})
for mapping in[CommandLineMapping()]:
if not os.path.exists(mapping.blend_path):
print("Skipping mapping: " + mapping.blend_path)
continue
print("Processing mapping: from: " + mapping.blend_path + " to: " + mapping.gltf_path)
print("Initializing...")
bpy.ops.wm.read_homefile(use_empty=True)
print("Preparing driver setup...")
bpy.app.driver_namespace["clamp_angle_deg"] = clamp_angle_deg
bpy.app.driver_namespace["angle_to_linear"] = angle_to_linear
bpy.app.driver_namespace["extra_linear"] = extra_linear
bpy.app.driver_namespace["angle_to_linear_x"] = angle_to_linear_x
print("Driver setup done...")
bpy.ops.wm.append(
filepath=os.path.join(mapping.blend_path, mapping.inner_path),
directory=os.path.join(mapping.blend_path, mapping.inner_path),
files=mapping.files)
print("Append done...")
prepare_armature(mapping)
print("Armature done...")
print("Remove junk...")
for ob in bpy.data.objects:
if ob.name.startswith("cs_"):
bpy.data.objects.remove(ob)
elif ob.name.startswith("Face") and ob.name != "Face":
bpy.data.objects.remove(ob)
print("Removing original armature and actions...")
orig_arm = bpy.data.objects[mapping.armature_name + '_orig']
bpy.data.objects.remove(orig_arm)
for act in bpy.data.actions:
if act.name.startswith("bake_"):
act.name = act.name + "-noimp"
for act in bpy.data.actions:
if act.name.startswith("bake_"):
bpy.data.actions.remove(act)
for act in bpy.data.actions:
if act.name.startswith("bake_"):
bpy.data.actions.remove(act)
for obj in bpy.data.objects:
if obj.type == 'MESH':
if not obj.name in mapping.objs and obj.parent is None:
if not obj.name.endswith("-noimp"):
obj.name = obj.name + "-noimp"
bpy.ops.wm.save_as_mainfile(filepath=(basepath + "/assets/blender/scripts/" + mapping.outfile))
os.makedirs(os.path.dirname(mapping.gltf_path), exist_ok=True)
bpy.ops.export_scene.gltf(filepath=mapping.gltf_path,
use_selection=False,
check_existing=False,
# export_format='GLTF_SEPARATE',
export_format='GLB',
export_texture_dir='textures', export_texcoords=True,
export_animation_mode='NLA_TRACKS',
export_normals=True,
export_tangents=True,
export_materials='EXPORT',
# export_all_vertex_colors=True,
# colors_type='SRGB',
# export_vertex_colors=True,
export_colors=True,
use_mesh_edges=False,
use_mesh_vertices=False,
export_cameras=False,
use_visible=False,
use_renderable=False,
export_yup=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)
print("exported to: " + mapping.gltf_path)
bpy.ops.wm.read_homefile(use_empty=True)
time.sleep(2)
bpy.ops.wm.quit_blender()

View File

@@ -207,5 +207,5 @@ main_armature.animation_data.action = default_action
main_armature.select_set(False)
bpy.context.view_layer.objects.active = None
bpy.ops.wm.save_as_mainfile(filepath=(basepath + "/assets/blender/" + imp.outfile))
bpy.ops.wm.save_as_mainfile(filepath=(imp.outfile))

View File

@@ -49,6 +49,7 @@ target_link_libraries(Editor PRIVATE
physics
lua
flecs::flecs_static
Tracy::TracyClient
)
if(OGRE_STATIC)
target_link_options(Editor PRIVATE -static-libstdc++ -static-libgcc)

View File

@@ -23,6 +23,7 @@
#include "PhysicsModule.h"
#include "physics.h"
#include "sound.h"
#include <tracy/Tracy.hpp>
class App;
class SkyRenderer : public Ogre::SceneManager::Listener {
@@ -683,52 +684,60 @@ public:
};
void KeyboardListener::frameRendered(const Ogre::FrameEvent &evt)
{
if (fps_timer.getMilliseconds() > 1000.0f) {
std::cout << "FPS: "
<< mApp->getRenderWindow()->getStatistics().lastFPS
<< " ";
std::cout << "Draw calls: "
<< mApp->getRenderWindow()->getStatistics().batchCount
<< " ";
fps_timer.reset();
std::cout << "Drops: "
<< mApp->getRenderWindow()
->getStatistics()
.vBlankMissCount
<< "\n";
fps_timer.reset();
}
/* for editor we always update world */
/* TODO: implement pause */
mApp->updateWorld(evt.timeSinceLastFrame);
if (mInitDelay >= 0.0f)
mInitDelay -= evt.timeSinceLastFrame;
{
ZoneScopedN("frameRendered");
if (fps_timer.getMilliseconds() > 1000.0f) {
std::cout << "FPS: "
<< mApp->getRenderWindow()
->getStatistics()
.lastFPS
<< " ";
std::cout << "Draw calls: "
<< mApp->getRenderWindow()
->getStatistics()
.batchCount
<< " ";
fps_timer.reset();
std::cout << "Drops: "
<< mApp->getRenderWindow()
->getStatistics()
.vBlankMissCount
<< "\n";
fps_timer.reset();
}
/* for editor we always update world */
/* TODO: implement pause */
mApp->updateWorld(evt.timeSinceLastFrame);
if (mInitDelay >= 0.0f)
mInitDelay -= evt.timeSinceLastFrame;
if (!isGuiEnabled() && ECS::get().has<ECS::Input>()) {
ECS::Input &input = ECS::get().get_mut<ECS::Input>();
input.control = control;
input.mouse = mouse;
input.mouse_abs = mouse_abs;
mouse.x = 0;
mouse.y = 0;
input.wheel_y = wheel_y;
wheel_y = 0;
input.mouse_moved = mouse_moved;
input.wheel_moved = wheel_moved;
ECS::modified<ECS::Input>();
} else {
ECS::Input &input = ECS::get().get_mut<ECS::Input>();
input.control = control;
input.mouse = mouse;
input.mouse_abs = mouse_abs;
mouse.x = 0;
mouse.y = 0;
input.wheel_y = wheel_y;
wheel_y = 0;
input.mouse_moved = mouse_moved;
input.wheel_moved = wheel_moved;
ECS::modified<ECS::Input>();
if (!isGuiEnabled() && ECS::get().has<ECS::Input>()) {
ECS::Input &input = ECS::get().get_mut<ECS::Input>();
input.control = control;
input.mouse = mouse;
input.mouse_abs = mouse_abs;
mouse.x = 0;
mouse.y = 0;
input.wheel_y = wheel_y;
wheel_y = 0;
input.mouse_moved = mouse_moved;
input.wheel_moved = wheel_moved;
ECS::modified<ECS::Input>();
} else {
ECS::Input &input = ECS::get().get_mut<ECS::Input>();
input.control = control;
input.mouse = mouse;
input.mouse_abs = mouse_abs;
mouse.x = 0;
mouse.y = 0;
input.wheel_y = wheel_y;
wheel_y = 0;
input.mouse_moved = mouse_moved;
input.wheel_moved = wheel_moved;
ECS::modified<ECS::Input>();
}
}
FrameMark;
}
int main()

View File

@@ -0,0 +1,68 @@
#include <iostream>
#include "AnimationSystem.h"
#include <tracy/Tracy.hpp>
namespace AnimationSystem
{
bool AnimationSystem::addTime(float time)
{
int i;
ZoneScopedN("AnimationSystem::addTime");
preUpdateTriggers();
bool ret = m_builder.animation_nodes[0]->addTime(time);
for (i = 0; i < m_builder.animationNodeList.size(); i++) {
ZoneScoped;
AnimationNodeAnimation *anim = m_builder.animationNodeList[i];
OgreAssert(anim->mAnimation, "No animation");
float weight = anim->getWeight();
anim->mAnimation->increaseAccWeight(weight);
#ifdef VDEBUG
if (debug)
std::cout << i
<< " node: " << anim->mAnimation->getName()
<< " " << weight << std::endl;
#endif
if (anim->getWeight() > 0.01f) {
ZoneScoped;
ZoneText("builder", strlen("builder"));
ZoneText(anim->mAnimation->getName().c_str(),
anim->mAnimation->getName().length());
ZoneValue(anim->getWeight() * 100.0f);
}
}
for (i = 0; i < vanimation_list.size(); i++) {
ZoneScoped;
float weight = vanimation_list[i]->getAccWeight();
vanimation_list[i]->setWeight(weight);
vanimation_list[i]->resetAccWeight();
// #define VDEBUG
#ifdef VDEBUG
if (debug && vanimation_list[i]->getEnabled())
std::cout << i << " animation: "
<< vanimation_list[i]->getName() << " "
<< weight << std::endl;
#endif
#undef VDEBUG
if (vanimation_list[i]->mAnimationState->getEnabled()) {
ZoneScoped;
ZoneText("animation", strlen("animation"));
ZoneText(vanimation_list[i]->getName().c_str(),
vanimation_list[i]->getName().length());
ZoneValue(vanimation_list[i]->getWeight() * 100.0f);
}
}
postUpdateTriggers(time);
return ret;
}
Ogre::Vector3 AnimationSystem::getRootMotionDelta()
{
ZoneScopedN("AnimationSystem::getRootMotionDelta");
Ogre::Vector3 motionDelta(0, 0, 0);
int i;
for (i = 0; i < vanimation_list.size(); i++)
motionDelta += vanimation_list[i]->getRootMotionDelta();
return motionDelta;
}
}

View File

@@ -0,0 +1,900 @@
#ifndef ANIMATIONSYSTEM_H
#define ANIMATIONSYSTEM_H
#include <iostream>
#include <Ogre.h>
#include <flecs.h>
#include <tracy/Tracy.hpp>
#include "GameData.h"
#include "EventModule.h"
namespace AnimationSystem
{
struct AnimationTrigger;
struct AnimationTriggerSubscriber {
virtual void operator()(const AnimationTrigger *trigger) = 0;
};
class RootMotionListener : public Ogre::NodeAnimationTrack::Listener {
public:
RootMotionListener()
: Ogre::NodeAnimationTrack::Listener()
{
}
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;
Ogre::TransformKeyFrame *k1, *k2;
unsigned short firstKeyIndex;
float tm = t->getKeyFramesAtTime(timeIndex, &kf1, &kf2,
&firstKeyIndex);
k1 = static_cast<Ogre::TransformKeyFrame *>(kf1);
k2 = static_cast<Ogre::TransformKeyFrame *>(kf2);
Ogre::Vector3 translation;
Ogre::Quaternion rotation;
Ogre::Vector3 deltaMotion;
Ogre::Vector3 prevMotion;
if (tm == 0.0f) {
rotation = k1->getRotation();
translation = k1->getTranslate();
deltaMotion = translation;
} else {
rotation = Ogre::Quaternion::nlerp(
tm, k1->getRotation(), k2->getRotation(), true);
translation =
k1->getTranslate() +
(k2->getTranslate() - k1->getTranslate()) * tm;
deltaMotion = translation - prevTranslation;
if (deltaMotion.squaredLength() >
translation.squaredLength())
deltaMotion = translation;
}
vkf->setTranslate(deltaMotion);
vkf->setRotation(rotation);
vkf->setScale(Ogre::Vector3(1, 1, 1));
prevTranslation = translation;
e.get_mut<CharacterBase>().mBoneMotion = deltaMotion;
e.get_mut<CharacterBase>().mBonePrevMotion = prevTranslation;
e.modified<CharacterBase>();
return true;
#endif
return false;
}
};
struct AnimationTrigger {
Ogre::String name;
float time;
float weight;
std::vector<AnimationTriggerSubscriber *> subscriber_list;
float getTriggerTime() const
{
return time;
}
float getMinWeight() const
{
return weight;
}
const Ogre::String &getName() const
{
return name;
}
void notify(float weight)
{
int i;
if (weight < this->weight)
return;
for (i = 0; i < subscriber_list.size(); i++)
(*subscriber_list[i])(this);
}
void addSubscriber(AnimationTriggerSubscriber *sub)
{
if (std::find(subscriber_list.begin(), subscriber_list.end(),
sub) != subscriber_list.end())
return;
subscriber_list.push_back(sub);
}
void removeSubscriber(AnimationTriggerSubscriber *sub)
{
auto it = std::find(subscriber_list.begin(),
subscriber_list.end(), sub);
if (it != subscriber_list.end())
subscriber_list.erase(it);
}
void clearSubscribers()
{
subscriber_list.clear();
}
AnimationTrigger(const Ogre::String name, float time, float weight)
: name(name)
, time(time)
, weight(weight)
{
}
};
struct Animation {
struct SetupTracks {
Ogre::NodeAnimationTrack *mHipsTrack;
Ogre::NodeAnimationTrack *mRootTrack;
RootMotionListener *mListener;
Ogre::Vector3 mRootTranslation;
SetupTracks(Ogre::Skeleton *skeleton,
Ogre::Animation *animation)
: mListener(OGRE_NEW RootMotionListener())
{
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;
}
}
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);
}
Ogre::TransformKeyFrame *tkfBeg =
(Ogre::TransformKeyFrame *)
mRootTrack->getKeyFrame(0);
Ogre::TransformKeyFrame *tkfEnd =
(Ogre::TransformKeyFrame *)
mRootTrack->getKeyFrame(
mRootTrack->getNumKeyFrames() -
1);
mRootTranslation =
tkfEnd->getTranslate() - tkfBeg->getTranslate();
}
};
Ogre::AnimationState *mAnimationState;
Ogre::Animation *mSkelAnimation;
SetupTracks *mTracks;
float m_weight;
float m_accWeight;
Ogre::Skeleton *mSkeleton;
Animation(Ogre::Skeleton *skeleton, Ogre::AnimationState *animState,
Ogre::Animation *skelAnimation)
: mTracks(OGRE_NEW SetupTracks(skeleton, skelAnimation))
, mAnimationState(animState)
, mSkelAnimation(skelAnimation)
, m_weight(0)
, m_accWeight(0)
, mSkeleton(skeleton)
{
}
Ogre::String getName()
{
return mAnimationState->getAnimationName();
}
void setLoop(bool loop)
{
mAnimationState->setLoop(loop);
}
bool getLoop() const
{
return mAnimationState->getLoop();
}
void setEnabled(bool enabled)
{
mAnimationState->setEnabled(enabled);
}
bool getEnabled() const
{
return mAnimationState->getEnabled();
}
void setWeight(float weight)
{
bool enabled = weight > 0.001f;
setEnabled(enabled);
mAnimationState->setWeight(weight);
m_weight = weight;
}
float getWeight() const
{
return m_weight;
}
Ogre::Vector3 rootMotion;
Ogre::Vector3 getRootMotionDelta()
{
if (mAnimationState->getEnabled())
return rootMotion * mAnimationState->getWeight();
else
return Ogre::Vector3(0, 0, 0);
}
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;
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);
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();
rootMotion = (thisRootPos - lastRootPos) +
(loops * mTracks->mRootTranslation);
getKeyframeIndices(mAnimationState->getTimePosition(),
&next_index);
// updateRootMotion(mAnimationState->getTimePosition());
return prev_index != next_index;
}
void reset()
{
mAnimationState->setTimePosition(0);
}
void resetAccWeight()
{
m_accWeight = 0;
}
void increaseAccWeight(float weight)
{
m_accWeight += weight;
}
float getAccWeight() const
{
return m_accWeight;
}
float getLength() const
{
return mAnimationState->getLength();
if (getEnabled())
return mAnimationState->getLength();
else
return 0.0f;
}
float getTimePosition() const
{
return mAnimationState->getTimePosition();
if (getEnabled())
return mAnimationState->getTimePosition();
else
return 0.0f;
}
};
struct AnimationNode {
std::vector<AnimationNode *> children;
float m_weight;
Ogre::String m_name;
std::multimap<float, AnimationTrigger *> trigger_list;
AnimationNode()
: m_weight(0)
{
}
virtual bool addTime(float time) = 0;
virtual void setWeight(float weight) = 0;
virtual void reset() = 0;
virtual float getLength() const = 0;
virtual float getTimePosition() const = 0;
float getWeight()
{
return m_weight;
}
const Ogre::String &getName()
{
return m_name;
}
void setName(const Ogre::String &name)
{
m_name = name;
}
virtual float getTime() const
{
float l = getLength();
if (l > 0.0f)
return getTimePosition() / l;
return 0.0f;
}
void addTrigger(AnimationTrigger *trigger)
{
trigger_list.insert(std::pair<float, AnimationTrigger *>(
trigger->getTriggerTime(), trigger));
}
void clearTriggers()
{
auto it = trigger_list.begin();
while (it != trigger_list.end()) {
delete it->second;
it++;
}
trigger_list.clear();
}
float mpreUpdateTime;
void preUpdateTriggers()
{
mpreUpdateTime = getTime();
}
void postUpdateTriggers(float delta)
{
float postUpdateTime = getTime();
bool positive = delta >= 0.0f;
if (positive)
updateTriggers(mpreUpdateTime, postUpdateTime);
else
updateTriggers(postUpdateTime, mpreUpdateTime);
}
void updateTriggers(float currentTime, float nextTime)
{
int i;
float weight = getWeight();
if (currentTime <= nextTime) {
auto it = trigger_list.lower_bound(currentTime);
while (it != trigger_list.end()) {
if (nextTime <=
it->second->getTriggerTime()) // in future, sorrted by time
return;
it->second->notify(weight);
it++;
}
} else {
updateTriggers(currentTime, 1);
updateTriggers(0, nextTime);
}
}
};
struct AnimationNodeAnimation : AnimationNode {
Animation *mAnimation;
bool enabled;
AnimationNodeAnimation(Animation *animation)
: AnimationNode()
, mAnimation(animation)
{
}
bool addTime(float time)
{
bool ret;
preUpdateTriggers();
ret = mAnimation->addTime(time);
postUpdateTriggers(time);
return ret;
}
void setWeight(float weight)
{
m_weight = weight;
enabled = weight > 0.001f;
}
void reset()
{
mAnimation->reset();
}
float getLength() const
{
return mAnimation->getLength();
if (enabled)
return mAnimation->getLength();
else
return 0.0f;
}
float getTimePosition() const
{
return mAnimation->getTimePosition();
if (enabled)
return mAnimation->getTimePosition();
else
return 0.0f;
}
};
struct AnimationNodeStateMachineState : AnimationNode {
AnimationNodeStateMachineState()
: AnimationNode()
{
}
bool addTime(float time)
{
bool ret;
preUpdateTriggers();
ret = children[0]->addTime(time);
postUpdateTriggers(time);
return ret;
}
void setWeight(float weight)
{
m_weight = weight;
bool enabled = weight > 0.001f;
children[0]->setWeight(weight);
}
void reset()
{
children[0]->reset();
}
float getLength() const
{
return children[0]->getLength();
}
float getTimePosition() const
{
return children[0]->getTimePosition();
}
};
struct AnimationNodeSpeed : AnimationNode {
float m_speed;
bool enabled;
AnimationNodeSpeed(float speed)
: AnimationNode()
, m_speed(speed)
, enabled(false)
{
}
bool addTime(float time)
{
bool ret;
preUpdateTriggers();
ret = children[0]->addTime(time * m_speed);
postUpdateTriggers(time);
return ret;
}
void setWeight(float weight)
{
m_weight = weight;
children[0]->setWeight(weight);
}
void reset()
{
children[0]->reset();
}
float getLength() const
{
if (m_speed > 0.0f || m_speed < 0.0f)
return children[0]->getLength() / m_speed;
return 0.0f;
}
float getTimePosition() const
{
if (m_speed > 0.0f || m_speed < 0.0f)
return children[0]->getTimePosition() / m_speed;
return 0.0f;
}
float getTime() const override
{
float l = children[0]->getLength();
if (l > 0.0f)
return children[0]->getTimePosition() / l;
return 0.0f;
}
};
struct AnimationNodeStateMachine : AnimationNode {
std::map<Ogre::String, AnimationNode *> stateMap;
std::set<AnimationNode *> fade_in, fade_out;
AnimationNode *currentAnim, *nextAnim;
float fade_speed;
Ogre::String mCurrentStateName;
bool configured;
bool debug;
AnimationNodeStateMachine(float fade_speed, bool debug = false)
: AnimationNode()
, currentAnim(nullptr)
, nextAnim(nullptr)
, fade_speed(fade_speed)
, mCurrentStateName("")
, configured(false)
, debug(debug)
{
m_weight = 1.0f;
}
bool addTime(float time)
{
int i;
preUpdateTriggers();
if (!configured) {
configure();
configured = true;
}
#ifdef VDEBUG
if (debug) {
std::cout << "state machine addTime" << std::endl;
std::cout
<< "state machine children: " << children.size()
<< std::endl;
}
#endif
for (i = 0; i < children.size(); i++) {
#ifdef VDEBUG
if (debug)
std::cout << "child weight: " << i << " "
<< children[i]->getWeight()
<< std::endl;
#endif
AnimationNode *child = children[i];
if (fade_in.find(child) != fade_in.end()) {
Ogre::Real newWeight =
child->getWeight() + time * fade_speed;
child->setWeight(Ogre::Math::Clamp<Ogre::Real>(
newWeight * m_weight, 0, m_weight));
#ifdef VDEBUG
if (debug) {
std::cout << "fade in: " << newWeight
<< std::endl;
std::cout << "m_weight: " << m_weight
<< std::endl;
}
#endif
if (newWeight >= 1)
fade_in.erase(child);
}
if (fade_out.find(child) != fade_out.end()) {
Ogre::Real newWeight =
child->getWeight() - time * fade_speed;
child->setWeight(Ogre::Math::Clamp<Ogre::Real>(
newWeight * m_weight, 0, 1));
if (newWeight <= 0)
fade_out.erase(child);
}
}
OgreAssert(currentAnim, "bad current anim");
bool ret = false;
if (currentAnim)
ret = currentAnim->addTime(time);
postUpdateTriggers(time);
return ret;
}
void setWeight(float weight)
{
int i;
if (weight > m_weight && currentAnim)
fade_in.insert(currentAnim);
if (weight < m_weight && currentAnim &&
currentAnim->getWeight() > weight)
currentAnim->setWeight(weight);
m_weight = weight;
bool enabled = weight > 0.001f;
/* do not update child state yet */
}
void addState(AnimationNode *state)
{
const Ogre::String &name = state->getName();
stateMap[name] = state;
state->setWeight(0);
fade_in.erase(state);
fade_out.erase(state);
std::cout << "registered state: " << name << std::endl;
}
void configure()
{
int i;
if (debug)
std::cout << "children: " << children.size()
<< std::endl;
for (i = 0; i < children.size(); i++)
addState(children[i]);
if (debug)
std::cout << "configure called" << std::endl;
}
void reset()
{
int i;
for (i = 0; i < children.size(); i++)
children[i]->reset();
}
void setAnimation(const Ogre::String &anim_state, bool reset = false)
{
if (!configured) {
configure();
configured = true;
}
OgreAssert(stateMap.find(anim_state) != stateMap.end(),
"Bad animation state: " + anim_state);
nextAnim = stateMap[anim_state];
if (nextAnim == currentAnim)
return;
if (currentAnim != nullptr) {
fade_out.insert(currentAnim);
fade_in.erase(currentAnim);
}
fade_out.erase(nextAnim);
fade_in.insert(nextAnim);
nextAnim->setWeight(0);
if (reset)
nextAnim->reset();
currentAnim = nextAnim;
mCurrentStateName = anim_state;
}
const Ogre::String &getCurrentState() const
{
return mCurrentStateName;
}
float getLength() const
{
if (currentAnim)
return currentAnim->getLength();
else
return 0.0f;
}
float getTimePosition() const
{
if (currentAnim)
return currentAnim->getTimePosition();
else
return 0.0f;
}
};
struct AnimationSystem : AnimationNode {
bool debug;
#define ANIM_FADE_SPEED \
7.5f // animation crossfade speed in % of full weight per second
struct AnimationNodeOutput : AnimationNode {
float m_weight;
float m_speed;
AnimationNodeOutput()
: AnimationNode()
, m_weight(1.0f)
, m_speed(1.0f)
{
}
bool addTime(float time)
{
bool ret;
preUpdateTriggers();
ret = children[0]->addTime(time * m_speed);
postUpdateTriggers(time);
return ret;
}
void setWeight(float weight)
{
m_weight = weight;
bool enabled = weight > 0.001f;
children[0]->setWeight(weight);
}
void reset()
{
children[0]->reset();
}
float getLength() const
{
return children[0]->getLength();
}
float getTimePosition() const
{
return children[0]->getTimePosition();
}
};
AnimationSystem(bool debug = false)
: debug(debug)
, m_builder(this, debug)
{
}
std::unordered_map<Ogre::String, Animation *> animation_list;
std::vector<Animation *> vanimation_list;
void add_animation(const Ogre::String &name, Animation *animation)
{
OgreAssert(animation, "No animation " + name);
animation_list[name] = animation;
vanimation_list.push_back(animation);
}
void clear_animations()
{
animation_list.clear();
vanimation_list.clear();
}
struct AnimationSystemBuilder {
AnimationSystem *mAnimationSystem;
std::vector<AnimationNode *> animation_nodes;
AnimationNode *parent;
std::list<AnimationNode *> parent_stack;
std::unordered_map<Ogre::String, AnimationNode *> nodeMap;
std::vector<AnimationNodeAnimation *> animationNodeList;
bool debug;
AnimationSystemBuilder(AnimationSystem *animationSystem,
bool debug = false)
: mAnimationSystem(animationSystem)
, debug(debug)
{
}
AnimationSystemBuilder *output()
{
AnimationNodeOutput *onode = new AnimationNodeOutput();
animation_nodes.push_back(onode);
parent = onode;
return this;
}
AnimationSystemBuilder *
animation(const Ogre::String &animation_name)
{
OgreAssert(parent, "bad parent");
Animation *animation =
mAnimationSystem->animation_list[animation_name];
OgreAssert(animation,
"bad animation " + animation_name);
AnimationNodeAnimation *onode =
new AnimationNodeAnimation(animation);
OgreAssert(onode, "bad animation");
OgreAssert(onode->mAnimation, "bad animation");
animation_nodes.push_back(onode);
parent->children.push_back(onode);
animationNodeList.push_back(onode);
return this;
}
/* FIXME: need to remove flecs dependency */
AnimationSystemBuilder *
trigger_entity(flecs::entity e, const Ogre::String &name,
float time, const Ogre::String &event)
{
struct EntityEventSubscriber
: AnimationTriggerSubscriber {
Ogre::String event;
flecs::entity ent;
void operator()(const AnimationTrigger *trigger)
{
ent.get_mut<ECS::EventData>().add(
ent, event, ent, ent);
}
EntityEventSubscriber(flecs::entity e,
const Ogre::String &event)
: event(event)
, ent(e)
{
}
};
OgreAssert(parent, "bad parent");
AnimationTrigger *trigger =
new AnimationTrigger(name, time, 0.1f);
EntityEventSubscriber *sub =
new EntityEventSubscriber(e, event);
trigger->addSubscriber(sub);
parent->addTrigger(trigger);
return this;
} // leaf too...
AnimationSystemBuilder *
transition_end(const Ogre::String &state_from,
const Ogre::String &state_to)
{
struct EndTransitionSubscriber
: AnimationTriggerSubscriber {
AnimationNodeStateMachine *sm;
Ogre::String next_state;
bool reset;
void operator()(const AnimationTrigger *trigger)
{
sm->setAnimation(next_state, reset);
}
EndTransitionSubscriber(
AnimationNodeStateMachine *sm,
const Ogre::String &next_state,
bool reset = true)
: sm(sm)
, next_state(next_state)
, reset(reset)
{
}
};
OgreAssert(parent, "no parent");
AnimationNodeStateMachine *sm =
static_cast<AnimationNodeStateMachine *>(
parent);
OgreAssert(sm, "no state machine");
AnimationTrigger *trigger = new AnimationTrigger(
"transition:" + state_from + "_" + state_to,
0.99f, 0.1f);
EndTransitionSubscriber *sub =
new EndTransitionSubscriber(sm, state_to);
int i;
bool ok = false;
for (i = 0; i < sm->children.size(); i++) {
if (sm->children[i]->getName() == state_from) {
trigger->addSubscriber(sub);
sm->children[i]->addTrigger(trigger);
ok = true;
break;
}
}
OgreAssert(ok, "Failed to set transition");
return this;
}
AnimationSystemBuilder *speed(float speed,
const Ogre::String &anchor = "")
{
OgreAssert(parent, "bad parent");
AnimationNodeSpeed *onode =
new AnimationNodeSpeed(speed);
animation_nodes.push_back(onode);
parent->children.push_back(onode);
parent_stack.push_back(parent);
parent = onode;
if (anchor.length() > 0)
nodeMap[anchor] = onode;
return this;
}
AnimationSystemBuilder *
state_machine(float fade_time = ANIM_FADE_SPEED,
const Ogre::String &anchor = "")
{
OgreAssert(parent, "bad parent");
AnimationNodeStateMachine *onode =
new AnimationNodeStateMachine(fade_time, debug);
animation_nodes.push_back(onode);
parent->children.push_back(onode);
parent_stack.push_back(parent);
parent = onode;
if (anchor.length() > 0)
nodeMap[anchor] = onode;
return this;
}
AnimationSystemBuilder *state(const Ogre::String &state_name)
{
OgreAssert(parent, "bad parent");
AnimationNodeStateMachineState *onode =
new AnimationNodeStateMachineState;
animation_nodes.push_back(onode);
parent->children.push_back(onode);
parent_stack.push_back(parent);
parent = onode;
onode->setName(state_name);
return this;
}
AnimationSystemBuilder *end()
{
parent = parent_stack.back();
parent_stack.pop_back();
return this;
}
};
AnimationSystemBuilder m_builder;
Ogre::Vector3 getRootMotionDelta();
bool addTime(float time);
void setWeight(float weight)
{
m_builder.animation_nodes[0]->setWeight(weight);
}
void reset()
{
m_builder.animation_nodes[0]->reset();
}
AnimationSystemBuilder *builder()
{
m_builder.animation_nodes.reserve(8);
m_builder.parent = nullptr;
return &m_builder;
}
template <class T> T *get(const Ogre::String &name)
{
return static_cast<T *>(m_builder.nodeMap[name]);
}
float getLength() const
{
return m_builder.animation_nodes[0]->getLength();
}
float getTimePosition() const
{
return m_builder.animation_nodes[0]->getTimePosition();
}
};
}
#endif // ANIMATIONSYSTEM_H

View File

@@ -4,19 +4,22 @@ find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain Overlay CONFIG
find_package(Bullet REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(OgreProcedural REQUIRED CONFIG)
find_package(Tracy REQUIRED CONFIG)
add_subdirectory(items)
add_subdirectory(LuaModule)
add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp SunModule.cpp TerrainModule.cpp
GUIModule.cpp EditorGUIModule.cpp LuaData.cpp WorldMapModule.cpp BoatModule.cpp EventTriggerModule.cpp
GUIModule.cpp EditorGUIModule.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 QuestModule.cpp
PlayerActionModule.cpp CharacterAIModule.cpp goap.cpp)
PlayerActionModule.cpp CharacterAIModule.cpp goap.cpp AnimationSystem.cpp)
target_link_libraries(GameData PUBLIC
lua
items luamodule
flecs::flecs_static
nlohmann_json::nlohmann_json
OgreMain
OgreBites
OgrePaging OgreTerrain OgreOverlay OgreProcedural::OgreProcedural items
PRIVATE sceneloader world-build physics editor)
target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${BULLET_INCLUDE_DIR} ../luaaa ../aitoolkit/include)
target_compile_definitions(GameData PRIVATE FLECS_CPP_NO_AUTO_REGISTRATION)
OgrePaging OgreTerrain OgreOverlay OgreProcedural::OgreProcedural
PRIVATE sceneloader world-build physics editor Tracy::TracyClient
)
target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${BULLET_INCLUDE_DIR})
target_compile_definitions(GameData PRIVATE FLECS_CPP_NO_AUTO_REGISTRATION PUBLIC TRACY_ENABLE)

View File

@@ -7,6 +7,7 @@
#include "CharacterModule.h"
#include "items.h"
#include "CharacterAIModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
struct ActionExec {
@@ -231,7 +232,6 @@ public:
Ogre::Vector3 direction =
(targetPosition - npc.position).normalisedCopy();
targetPosition -= direction * radius;
std::cout << action->get_name();
}
public:
@@ -245,21 +245,20 @@ public:
private:
int update(float delta) override
{
OgreAssert(false, "update");
return OK;
}
void finish(int rc) override
{
if (rc == OK)
bb.apply(action->effects);
OgreAssert(false, "finish");
}
void activate() override
{
std::cout << action->get_name();
const ActionNodeActions::RunActionNode *runaction =
static_cast<const ActionNodeActions::RunActionNode
*>(action);
if (runaction)
std::cout << "Is Run" << std::endl;
OgreAssert(false, "activate");
}
@@ -329,14 +328,32 @@ public:
jactionPrereq["is_seated"] = 0;
jactionEffect["is_seated"] = 1;
} else if (action == "use") {
if (props["tags"].find("toilet") !=
props["tags"].end()) {
std::cout << "toilet" << std::endl;
OgreAssert(false, "props: " + props.dump(4));
OgreAssert(props["tags"].is_array(), "bad formed tags");
const nlohmann::json &tags = props["tags"];
if (tags.size() == 1 &&
tags[0].get<Ogre::String>() == "") {
} else {
std::cout << "use: " << props.dump(4)
<< std::endl;
// OgreAssert(false, "props: " + props.dump(4));
bool have_bits = false;
if (std::find(tags.begin(), tags.end(),
"dance-pole") != tags.end()) {
jactionPrereq["is_pole_dancing"] = 0;
jactionEffect["is_pole_dancing"] = 1;
have_bits = true;
}
if (std::find(tags.begin(), tags.end(),
"toilet") != tags.end()) {
jactionPrereq["toilet"] = 1;
jactionEffect["toilet"] = 0;
have_bits = true;
}
if (!have_bits) {
std::cout << "use: " << props.dump(4)
<< std::endl;
// OgreAssert(false, "props: " + props.dump(4));
OgreAssert(tags.size() == 0,
"Some tags: " +
props.dump(4));
}
}
} else {
OgreAssert(false, "props: " + props.dump(4));
@@ -363,27 +380,32 @@ public:
};
struct ActionExecCommon : ActionExec {
private:
float delay;
int update(float delta) override
{
std::cout << "running: " << action->get_name() << std::endl;
return OK;
delay -= delta;
if (delay > 0.0f)
return BUSY;
else
return OK;
}
void finish(int rc) override
{
if (rc == OK)
bb.apply(action->effects);
std::cout << "finish: " << action->get_name() << std::endl;
}
void activate() override
{
std::cout << action->get_name();
std::cout << "!";
delay = 1.0f;
}
public:
ActionExecCommon(ActionExec::PlanExecData &data,
goap::BaseAction<Blackboard> *action)
: ActionExec(data, action)
, delay(0.0f)
{
}
};
@@ -439,20 +461,30 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
{ "thirsty", 1 } } },
{ { { "thirsty", 0 } } },
10 },
#if 0
{ "DrinkWater",
{ { { "have_water", 1 }, { "thirsty", 1 } } },
{ { { "have_water", 1 },
{ "thirsty", 1 },
{ "is_seated", 0 } } },
{ { { "thirsty", 0 } } },
2000 },
#endif
{ "EatMedicine",
{ { { "have_medicine", 1 }, { "healthy", 0 } } },
{ "EatMedicineSeated",
{ { { "have_medicine", 1 },
{ "healthy", 0 },
{ "is_seated", 1 } } },
{ { { "healthy", 1 } } },
100 },
{ "EatMedicine",
{ { { "have_medicine", 1 },
{ "healthy", 0 },
{ "is_seated", 0 } } },
{ { { "healthy", 1 } } },
10000 },
#if 0
{ "UseToilet",
{ { { "toilet", 1 } } },
{ { { "toilet", 0 } } },
100 },
#endif
{ "GetFood",
{ { { "have_food", 0 } } },
{ { { "have_food", 1 } } },
@@ -478,25 +510,15 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
.kind(flecs::OnUpdate)
.each([this](flecs::entity town, TownAI &ai,
const TownNPCs &npcs) {
Ogre::Root::getSingleton().getWorkQueue()->addTask(
[this, town, npcs, &ai]() {
Ogre::Root::getSingleton()
.getWorkQueue()
->addMainThreadTask([this, town,
npcs,
&ai]() {
std::lock_guard<
std::mutex>
lock(ecs_mutex);
createBlackboards(
town, npcs, ai);
});
});
ZoneScopedN("CreateBlackboards");
std::lock_guard<std::mutex> lock(ecs_mutex);
createBlackboards(town, npcs, ai);
});
ecs.system<ActionNodeList, TownAI, TownNPCs>("UpdateDynamicActions")
.kind(flecs::OnUpdate)
.each([](flecs::entity e, ActionNodeList &alist, TownAI &ai,
TownNPCs &npcs) {
ZoneScopedN("UpdateDynamicActions");
std::lock_guard<std::mutex> lock(ecs_mutex);
if (ai.nodeActions.size() > 0)
return;
@@ -527,12 +549,31 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
.interval(0.1f)
.each([this](flecs::entity town, ActionNodeList &alist,
TownAI &ai, TownNPCs &npcs) {
ZoneScopedN("UpdateDynamicNodes");
std::lock_guard<std::mutex> lock(ecs_mutex);
ECS::get_mut<ActionNodeList>().updateDynamicNodes();
});
struct MeasureTime {
std::chrono::system_clock::time_point start;
std::string what;
MeasureTime(const std::string &s)
: start(std::chrono::high_resolution_clock::now())
, what(s)
{
}
~MeasureTime()
{
auto end = std::chrono::high_resolution_clock::now();
std::chrono::duration<float, std::milli> elapsed =
end - start;
std::cout << what << " " << elapsed.count()
<< std::endl;
}
};
ecs.system<TownNPCs>("UpdateNPCPositions")
.kind(flecs::OnUpdate)
.each([](flecs::entity e, TownNPCs &npcs) {
ZoneScopedN("UpdateNPCPositions");
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end();
it++) {
auto &npc = npcs.npcs.at(it->first);
@@ -549,49 +590,62 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
.interval(0.1f)
.each([this](flecs::entity town, ActionNodeList &alist,
TownAI &ai, const TownNPCs &npcs) {
Ogre::Root::getSingleton().getWorkQueue()->addTask(
[this, town, &alist, npcs, &ai]() {
{
std::lock_guard<std::mutex> lock(
ecs_mutex);
ZoneScopedN("UpdateBlackboards");
updateBlackboards(town, alist,
npcs, ai);
}
Ogre::Root::getSingleton()
.getWorkQueue()
->addMainThreadTask([this, town,
&alist]() {
town.modified<TownAI>();
town.modified<TownNPCs>();
ECS::modified<
ActionNodeList>();
});
});
Ogre::Root::getSingleton().getWorkQueue()->addTask([this,
town,
&alist,
npcs,
&ai]() {
{
ZoneScopedN(
"UpdateBlackboards::Thread");
std::lock_guard<std::mutex> lock(
ecs_mutex);
updateBlackboards(town, alist, npcs,
ai);
}
Ogre::Root::getSingleton()
.getWorkQueue()
->addMainThreadTask([this, town,
&alist]() {
ZoneScopedN(
"UpdateBlackboards::MainThread");
town.modified<TownAI>();
town.modified<TownNPCs>();
ECS::modified<ActionNodeList>();
});
});
});
ecs.system<TownAI, TownNPCs>("PlanAI")
.kind(flecs::OnUpdate)
.interval(0.5f)
.each([&](flecs::entity town, TownAI &ai,
const TownNPCs &npcs) {
Ogre::Root::getSingleton().getWorkQueue()->addTask(
[this, town, npcs, &ai]() {
std::lock_guard<std::mutex> lock(
ecs_mutex);
buildPlans(town, npcs, ai);
Ogre::Root::getSingleton()
.getWorkQueue()
->addMainThreadTask([this,
town]() {
town.modified<TownAI>();
});
});
ZoneScopedN("PlanAI");
Ogre::Root::getSingleton().getWorkQueue()->addTask([this,
town,
npcs,
&ai]() {
ZoneScopedN("PlanAI::Thread");
std::lock_guard<std::mutex> lock(ecs_mutex);
buildPlans(town, npcs, ai);
Ogre::Root::getSingleton()
.getWorkQueue()
->addMainThreadTask([this, town]() {
ZoneScopedN(
"PlanAI::MainThread");
town.modified<TownAI>();
});
});
});
static std::unordered_map<int, struct PlanExec> plan_exec;
ecs.system<const EngineData, TownNPCs, TownAI>("RunPLAN")
.kind(flecs::OnUpdate)
.each([&](flecs::entity town, const EngineData &eng,
TownNPCs &npcs, TownAI &ai) {
ZoneScopedN("RunPLAN");
for (const auto &plans : ai.plans) {
if (plan_exec.find(plans.first) !=
plan_exec.end()) {
@@ -712,6 +766,7 @@ static std::deque<PlanTask> plan_tasks;
void CharacterAIModule::buildPlans(flecs::entity town, const TownNPCs &npcs,
TownAI &ai)
{
ZoneScopedN("buildPlans");
OgreAssert(town.is_valid(), "Bad town entity");
std::lock_guard<std::mutex> lock(*ai.mutex);
auto planner = ai.planner;
@@ -758,6 +813,7 @@ void CharacterAIModule::buildPlans(flecs::entity town, const TownNPCs &npcs,
void CharacterAIModule::createBlackboards(flecs::entity town,
const TownNPCs &npcs, TownAI &ai)
{
ZoneScopedN("createBlackboards");
OgreAssert(town.is_valid(), "Bad town entity");
std::lock_guard<std::mutex> lock(*ai.mutex);
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end(); it++) {
@@ -871,6 +927,7 @@ void CharacterAIModule::updateBlackboards(flecs::entity town,
ActionNodeList &alist,
const TownNPCs &npcs, TownAI &ai)
{
ZoneScopedN("updateBlackboards");
std::lock_guard<std::mutex> lock(*ai.mutex);
OgreAssert(town.is_valid(), "Bad town entity");
alist.build();
@@ -1124,7 +1181,8 @@ void Blackboard::query_ai()
const TownNPCs &npcs = town.get<TownNPCs>();
const float distance = 10000.0f;
Ogre::Vector3 position(0, 0, 0);
if (npcs.npcs.at(index).e.is_valid())
if (npcs.npcs.at(index).e.is_valid() &&
npcs.npcs.at(index).e.has<CharacterBase>())
position = npcs.npcs.at(index)
.e.get<CharacterBase>()
.mBodyNode->_getDerivedPosition();

View File

@@ -8,10 +8,13 @@
#include "TerrainModule.h"
#include "WaterModule.h"
#include "world-build.h"
#include "AnimationSystem.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
{
ZoneScoped;
ecs.module<CharacterAnimationModule>();
ecs.component<AnimationControl>();
ecs.import <EventModule>();
@@ -21,7 +24,8 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
ecs.system<const CharacterBase, AnimationControl>("HandleAnimations")
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, const CharacterBase &ch,
AnimationControl &anim) {
AnimationControl &anim) {
ZoneScopedN("HandleAnimations");
if (!anim.configured) {
int i, j;
e.set<EventData>({});
@@ -32,19 +36,22 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
const Ogre::AnimationStateMap &animMap =
animStateSet->getAnimationStates();
anim.mAnimationSystem =
new AnimationSystem(false);
new AnimationSystem::AnimationSystem(
false);
ch.mBodyEnt->getSkeleton()
->getBone("Root")
->removeAllChildren();
for (auto it = animMap.begin();
it != animMap.end(); it++) {
Animation *animation = new Animation(
ch.mBodyEnt->getSkeleton(),
it->second,
ch.mBodyEnt->getSkeleton()
->getAnimation(
it->first),
e);
AnimationSystem::Animation *animation =
new AnimationSystem::Animation(
ch.mBodyEnt
->getSkeleton(),
it->second,
ch.mBodyEnt
->getSkeleton()
->getAnimation(
it->first));
#ifdef VDEBUG
std::cout
<< "animation: " << animNames[i]
@@ -93,18 +100,18 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
->end()
->state("swimming-edge-climb")
->animation("swimming-edge-climb")
->trigger(e, "end_of_climb", 0.99f, "animation:swimming-edge-climb:end")
->trigger_entity(e, "end_of_climb", 0.99f, "animation:swimming-edge-climb:end")
->end()
->state("hanging-climb")
->animation("hanging-climb")
->trigger(e, "end_of_climb2", 0.99f, "animation:hanging-climb:end")
->trigger_entity(e, "end_of_climb2", 0.99f, "animation:hanging-climb:end")
->end()
->state("idle")
->animation("idle-act")
->end()
->state("pass-character")
->animation("pass-character")
->trigger(e, "pass-character", 0.99f, "animation:pass-character:end")
->trigger_entity(e, "pass-character", 0.99f, "animation:pass-character:end")
->end()
->state("character-talk")
->animation("character-talk")
@@ -127,10 +134,13 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
/* clang-format on */
anim.mAnimationSystem
->get<AnimationNodeStateMachine>("main")
->get<AnimationSystem::
AnimationNodeStateMachine>(
"main")
->setAnimation("locomotion", true);
anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
->get<AnimationSystem::
AnimationNodeStateMachine>(
"locomotion-state")
->setAnimation("idle", true);
anim.configured = true;
@@ -148,73 +158,25 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, const EngineData &eng,
CharacterBase &ch, AnimationControl &anim) {
float delta = eng.delta;
ZoneScopedN("HandleAnimations1");
float delta = eng.delta;
// ch.mBoneMotion = Ogre::Vector3::ZERO;
if (!anim.mAnimationSystem)
return;
#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();
#if 0
{
ZoneScopedN("Ogre::Entity::_updateAnimation");
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 *>(
anim.mListener)
->getDeltaMotion();
ch.mRootBone->setPosition(Ogre::Vector3::ZERO);
Ogre::Vector3 d = offset - ch.mBonePrevMotion;
ch.mBonePrevMotion = offset;
std::cout << "length: " << d.length() << std::endl;
if (d.squaredLength() > 0.02f * 0.02f)
d = offset;
if (d.squaredLength() > 0.02f * 0.02f)
d = Ogre::Vector3::ZERO;
std::cout << "length2: " << d.length() << std::endl;
OgreAssert(d.length() < 0.5f, "bad offset");
ch.mBoneMotion = d;
#endif
#if 0
if (result) {
if (d.squaredLength() > 0.0f)
ch.mBoneMotion =
ch.mRootBone->getPosition() -
ch.mBoneMotion;
else
ch.mBoneMotion =
ch.mRootBone->getPosition();
} else {
ch.mBoneMotion = ch.mRootBone->getPosition() -
ch.mBoneMotion;
}
#endif
#undef VDEBUG
#ifdef VDEBUG
@@ -224,10 +186,6 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
<< " result: " << result << std::endl;
#endif
#undef VDEBUG
#if 0
// ch.mRootBone->setPosition(Ogre::Vector3::ZERO);
ch.mBonePrevMotion = offset;
#endif
});
ecs.system<const EngineData, CharacterBase, CharacterVelocity>(
"HandleRootMotionVelocity")
@@ -236,11 +194,12 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
.with<WaterReady>()
.each([this](flecs::entity e, const EngineData &eng,
CharacterBase &ch, CharacterVelocity &v) {
if (eng.delta < 0.0000001f)
ZoneScopedN("HandleRootMotionVelocity");
if (eng.delta < 0.0000001f)
return;
if (!ch.mBodyNode)
return;
Ogre::Quaternion rot = ch.mBodyNode->getOrientation();
Ogre::Quaternion rot = ch.mBodyNode->getOrientation();
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
Ogre::Vector3 boneMotion = ch.mBoneMotion;
v.velocity = Ogre::Vector3::ZERO;
@@ -273,39 +232,16 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
#if 0
v.velocity.y = 0.0f;
#endif
OgreAssert(v.velocity.squaredLength() < 1000.0f,
"Bad velocity setting");
});
#if 0
ecs.system<const EngineData, const AnimationControl,
const CharacterBase, CharacterVelocity>("HandleSwimming")
.kind(flecs::OnUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.with<CharacterBuoyancy>()
.without<CharacterDisablePhysics>()
.without<CharacterInActuator>()
.each([this](flecs::entity e, const EngineData &eng,
const AnimationControl &anim,
const CharacterBase &ch, CharacterVelocity &gr) {
if (anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
"locomotion-state")
->getCurrentState() == "swimming") {
float h = Ogre::Math::Clamp(
0.0f - ch.mBodyNode->getPosition().y,
0.0f, 2000.0f);
if (h > 0.05 && h < 2.0f)
gr.gvelocity.y += 0.1f * (h + 1.0f) *
h * eng.delta;
}
});
#endif
ecs.system<const EngineData, CharacterBase, AnimationControl,
CharacterVelocity>("HandleRootMotion")
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, const EngineData &eng,
CharacterBase &ch, AnimationControl &anim,
CharacterVelocity &v) {
ZoneScopedN("HandleRootMotion");
if (!ch.mBodyNode)
return;
if (eng.delta < 0.0000001f)
@@ -324,23 +260,32 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
.without<Player>()
.each([](flecs::entity e, const Input &input,
const CharacterBase &ch, AnimationControl &anim) {
if (!anim.configured)
ZoneScopedNC("HandleNPCAnimations", 0xFF2020);
if (!anim.configured)
return;
if (!anim.mAnimationSystem)
return;
AnimationNodeStateMachine *state_machine =
anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
"locomotion-state");
AnimationSystem::AnimationNodeStateMachine
*state_machine = anim.mAnimationSystem->get<
AnimationSystem::
AnimationNodeStateMachine>(
"locomotion-state");
Ogre::String current_state =
state_machine->getCurrentState();
Ogre::String next_state = "idle";
if (current_state != "treading_water" &&
ch.is_submerged)
e.has<InWater>())
next_state = "treading_water";
if (current_state != "idle" && !ch.is_submerged)
if (current_state != "idle" && !e.has<InWater>())
next_state = "idle";
state_machine->setAnimation(next_state);
{
ZoneScoped;
ZoneTextF("animation: next_state: %s %d %d",
next_state.c_str(),
(int)ch.is_submerged,
(int)e.has<InWater>());
}
});
ecs.system<const CharacterBase, const CharacterInActuator,
AnimationControl>("HandlePlayerAnimationsActuator")
@@ -349,15 +294,18 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
.each([](flecs::entity e, const CharacterBase &ch,
const CharacterInActuator &inact,
AnimationControl &anim) {
if (!anim.configured)
ZoneScopedN("HandlePlayerAnimationsActuator");
if (!anim.configured)
return;
AnimationNodeStateMachine *main_sm =
AnimationSystem::AnimationNodeStateMachine *main_sm =
anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
->get<AnimationSystem::
AnimationNodeStateMachine>(
"main");
AnimationNodeStateMachine *actuator_sm =
AnimationSystem::AnimationNodeStateMachine *actuator_sm =
anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
->get<AnimationSystem::
AnimationNodeStateMachine>(
"actuator-state");
Ogre::String current_state = main_sm->getCurrentState();
if (current_state != "actuator")
@@ -372,13 +320,15 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
.without<CharacterControlDisable>()
.each([](flecs::entity e, const CharacterBase &ch,
AnimationControl &anim) {
if (!anim.configured)
ZoneScopedN("HandlePlayerAnimationsNoActuator");
if (!anim.configured)
return;
if (!anim.mAnimationSystem)
return;
AnimationNodeStateMachine *main_sm =
AnimationSystem::AnimationNodeStateMachine *main_sm =
anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
->get<AnimationSystem::
AnimationNodeStateMachine>(
"main");
Ogre::String current_state = main_sm->getCurrentState();
if (current_state != "locomotion")
@@ -393,12 +343,14 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
.without<CharacterControlDisable>()
.each([](flecs::entity e, const Input &input,
const CharacterBase &ch, AnimationControl &anim) {
if (!anim.configured)
ZoneScopedN("HandlePlayerAnimations");
if (!anim.configured)
return;
AnimationNodeStateMachine *state_machine =
anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
"locomotion-state");
AnimationSystem::AnimationNodeStateMachine
*state_machine = anim.mAnimationSystem->get<
AnimationSystem::
AnimationNodeStateMachine>(
"locomotion-state");
Ogre::String current_state =
state_machine->getCurrentState();
bool controls_idle = input.motion.zeroLength();
@@ -474,6 +426,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
.each([](flecs::entity e, const Input &input,
const CharacterBase &ch, AnimationControl &anim,
CharacterInActuator &act) {
ZoneScopedN("HandlePlayerAnimations2");
bool controls_idle = input.motion.zeroLength();
if (!controls_idle) {
std::cout << "motion.z: "
@@ -547,7 +500,8 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
.kind(flecs::OnUpdate)
.with<Character>()
.each([](flecs::entity e, EventData &evt) {
for (auto ev : evt.events) {
ZoneScopedN("HandleEvents");
for (auto ev : evt.events) {
std::cout << "character event: " << ev.event
<< std::endl;
/* parse character events */
@@ -585,6 +539,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
int operator()(const std::vector<GameWorld::Parameter *> &args)
override
{
ZoneScoped;
GameWorld::ValueParameter<flecs::entity> *param_e =
static_cast<GameWorld::ValueParameter<
flecs::entity> *>(args[0]);
@@ -600,10 +555,11 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
true) {
const AnimationControl &control =
param_e->get().get<AnimationControl>();
AnimationNodeStateMachine *sm =
control.mAnimationSystem
->get<AnimationNodeStateMachine>(
param_node->get());
AnimationSystem::AnimationNodeStateMachine *sm =
control.mAnimationSystem->get<
AnimationSystem::
AnimationNodeStateMachine>(
param_node->get());
bool reset = false;
if (args.size() == 4) {
GameWorld::ValueParameter<bool>
@@ -622,6 +578,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
}
};
ECS::get_mut<GameWorld>().add_command<AnimationSetCommand>(
"set_animation_state");
"set_animation_state");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,11 +9,13 @@
#include "PlayerActionModule.h"
#include "items.h"
#include "CharacterManagerModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
void createNPCActionNodes(flecs::entity town, int index)
{
ZoneScoped;
TownNPCs &npcs = town.get_mut<TownNPCs>();
TownNPCs::NPCData &npc = npcs.npcs.at(index);
flecs::entity e = npc.e;
@@ -102,82 +104,71 @@ CharacterManagerModule::CharacterManagerModule(flecs::world &ecs)
.write<LivesIn>()
.each([this](flecs::entity town, TerrainItem &item,
TownNPCs &npcs) {
ZoneScopedN("UpdateCharacters");
if (!player.is_valid())
return;
if (!player.has<CharacterBase>())
return;
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([this,
town]() {
flecs::entity player =
ECS::get<CharacterManagerModule>()
.getPlayer();
if (!player.is_valid())
return;
if (!player.has<CharacterBase>())
return;
TownNPCs &npcs = town.get_mut<TownNPCs>();
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;
}
flecs::entity player =
ECS::get<CharacterManagerModule>().getPlayer();
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()) {
data.e = createCharacterData(
data.model,
data.position,
data.orientation);
data.e.add<LivesIn>(town);
break;
}
}
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,
index);
}
if (cameraPos.squaredDistance(npcPosition) >
22500.0f) {
if (data.e.is_valid()) {
data.e.destruct();
data.e = flecs::entity();
break;
}
}
town.modified<TownNPCs>();
});
}
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, index);
}
}
}
town.modified<TownNPCs>();
});
}
flecs::entity
CharacterManagerModule::createPlayer(const Ogre::Vector3 &position,
const Ogre::Quaternion &rotation)
{
ZoneScoped;
static int count = 0;
OgreAssert(count == 0, "overspawn");
OgreAssert(!player.is_valid(), "Player already created");
@@ -197,6 +188,7 @@ CharacterManagerModule::createCharacterData(const Ogre::String model,
const Ogre::Vector3 &position,
const Ogre::Quaternion &rotation)
{
ZoneScoped;
flecs::entity e = ECS::get().entity();
ECS::get_mut<CharacterModule>().createCharacter(e, position, rotation,
model);
@@ -206,6 +198,7 @@ CharacterManagerModule::createCharacterData(const Ogre::String model,
void CharacterManagerModule::registerTownCharacters(flecs::entity town)
{
ZoneScoped;
Ogre::MeshManager::getSingleton().load("normal-male.glb", "General");
Ogre::MeshManager::getSingleton().load("normal-female.glb", "General");
Ogre::String props = StaticGeometryModule::getItemProperties(town);

View File

@@ -9,16 +9,19 @@
#include "CharacterAnimationModule.h"
#include "CharacterManagerModule.h"
#include "CharacterModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
CharacterModule::CharacterModule(flecs::world &ecs)
{
ZoneScoped;
struct TriggerPhysicsChange {};
ecs.module<CharacterModule>();
ecs.component<Character>();
ecs.component<Player>();
ecs.component<CharacterBase>()
.on_remove([this](flecs::entity e, CharacterBase &ch) {
ZoneScoped;
// FIXME: clean up data
if (characterEntities.find(e) !=
characterEntities.end() ||
@@ -30,6 +33,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
})
.on_add([this](flecs::entity e, CharacterBase &ch) {
if (characterNodes.find(e) == characterNodes.end()) {
ZoneScoped;
OgreAssert(characterModels.find(e) !=
characterModels.end(),
"no model set");
@@ -60,6 +64,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
ecs.component<CharacterGravity>();
ecs.component<CharacterLocation>().on_set(
[this](flecs::entity e, CharacterLocation &loc) {
ZoneScoped;
characterOrientations[e] = loc.orientation;
characterPositions[e] = loc.position;
ECS::modified<CharacterModule>();
@@ -78,11 +83,13 @@ CharacterModule::CharacterModule(flecs::world &ecs)
ecs.system<EngineData, CharacterBase>("UpdateTimer")
.kind(flecs::OnUpdate)
.each([this](EngineData &eng, CharacterBase &ch) {
ch.mTimer += eng.delta;
ZoneScopedN("UpdateTimer");
ch.mTimer += eng.delta;
});
ecs.system<Input, Camera>("HandleInput")
.kind(flecs::OnUpdate)
.each([this](Input &input, Camera &camera) {
ZoneScopedN("HandleInput");
flecs::entity player =
ECS::get<CharacterManagerModule>().getPlayer();
if (!player.is_valid())
@@ -142,39 +149,24 @@ CharacterModule::CharacterModule(flecs::world &ecs)
else
input.fast = false;
input.control_prev = input.control;
float a = 0, b = 0, c = 0;
if (input.mouse_moved) {
updateCameraGoal(camera, -0.18f * input.mouse.x,
-0.12f * input.mouse.y, 0);
a += -0.18f * input.mouse.x;
b += -0.12f * input.mouse.y;
input.mouse_moved = false;
input.mouse.x = 0;
input.mouse.y = 0;
}
updateCameraGoal(camera, a, b, c);
}
if (input.wheel_moved) {
updateCameraGoal(camera, 0, 0,
-0.15f * input.wheel_y);
c += -0.15f * input.wheel_y;
input.wheel_moved = false;
input.wheel_y = 0;
}
updateCameraGoal(camera, a, b, c);
}
ECS::get().modified<ECS::Input>();
});
ecs.system<CharacterBase>()
.kind(flecs::OnUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.each([this](flecs::entity e, CharacterBase &ch) {
float full_subm = 2.0f;
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
float current_subm = -Ogre::Math::Clamp(
pos.y + Ogre::Math::Sin(ch.mTimer * 0.13f +
130.0f) *
0.07f,
-full_subm, 0.0f);
if (current_subm > 0.9f)
ch.is_submerged = true;
else if (current_subm < 0.8f)
ch.is_submerged = false;
});
#define TURN_SPEED 500.0f // character turning in degrees per second
ecs.system<const Input, const Camera, CharacterBase>("UpdateBody")
.kind(flecs::OnUpdate)
@@ -184,7 +176,8 @@ CharacterModule::CharacterModule(flecs::world &ecs)
.without<CharacterControlDisable>()
.each([](flecs::entity e, const Input &input,
const Camera &camera, CharacterBase &ch) {
ch.mGoalDirection = Ogre::Vector3::ZERO;
ZoneScopedN("UpdateBody");
ch.mGoalDirection = Ogre::Vector3::ZERO;
float delta = e.world().delta_time();
if (!input.motion.zeroLength()) {
// calculate actually goal direction in world based on player's key directions
@@ -253,85 +246,6 @@ CharacterModule::CharacterModule(flecs::world &ecs)
anim.configured = false;
});
#if 0
ecs.system<const EngineData, const CharacterLocation,
const CharacterConf, Body2Entity>("SetupCharacter")
.kind(flecs::OnUpdate)
.with<Character>()
.without<CharacterBase>()
.without<CharacterBody>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterLocation &loc,
const CharacterConf &conf, Body2Entity &b2e) {
CharacterBase &ch = e.ensure<CharacterBase>();
CharacterBody &body = e.ensure<CharacterBody>();
AnimationControl &anim = e.ensure<AnimationControl>();
ch.mBodyEnt = eng.mScnMgr->createEntity(conf.type);
ch.mBodyNode = eng.mScnMgr->getRootSceneNode()
->createChildSceneNode();
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");
// body.mController = nullptr;
ch.mBoneMotion = Ogre::Vector3::ZERO;
ch.mBonePrevMotion = Ogre::Vector3::ZERO;
e.set<CharacterVelocity>(
{ { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } });
body.checkGround = false;
body.checkGroundResult = false;
#if 0
body.mCollisionShape = nullptr;
body.mGhostObject = nullptr;
body.mController = nullptr;
body.mGhostObject = new btPairCachingGhostObject();
b2e.entities[body.mGhostObject] = e;
body.mCollisionShape = new btCompoundShape(false);
body.mGhostObject->setCollisionShape(
body.mCollisionShape);
{
btVector3 inertia(0, 0, 0);
// mCollisionShape = new btCompoundShape();
btScalar height = 1.0f;
btScalar radius = 0.3f;
btCapsuleShape *shape = new btCapsuleShape(
radius, 2 * height - 2 * radius);
btTransform transform;
transform.setIdentity();
transform.setOrigin(btVector3(0, 1, 0));
static_cast<btCompoundShape *>(
body.mCollisionShape)
->addChildShape(transform, shape);
btScalar masses[1] = { 0 };
btTransform principal;
static_cast<btCompoundShape *>(
body.mCollisionShape)
->calculatePrincipalAxisTransform(
masses, principal, inertia);
}
body.mGhostObject->setCollisionFlags(body.mGhostObject->getCollisionFlags() | btCollisionObject::CF_CHARACTER_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT
/*btCollisionObject::CF_KINEMATIC_OBJECT |
btCollisionObject::CF_NO_CONTACT_RESPONSE */);
body.mGhostObject->setActivationState(
DISABLE_DEACTIVATION);
eng.mWorld->attachCollisionObject(
body.mGhostObject, ch.mBodyEnt, 1, 0x7FFFFFFF);
OgreAssert(body.mGhostObject, "Need GhostObject");
OgreAssert(body.mCollisionShape, "No collision shape");
#endif
e.add<CharacterGravity>();
e.add<CharacterBuoyancy>();
anim.configured = false;
// OgreAssert(body.mGhostObject->hasContactResponse(),
// "need contact response");
});
#endif
#define CAM_HEIGHT 1.6f // height of camera above character's center of mass
ecs.system<const EngineData, Camera, const CharacterBase>(
"UpdateCamera")
@@ -340,7 +254,8 @@ CharacterModule::CharacterModule(flecs::world &ecs)
.with<GroundCheckReady>()
.each([](const EngineData &eng, Camera &camera,
const CharacterBase &ch) {
float delta = eng.delta;
ZoneScopedN("UpdateCamera");
float delta = eng.delta;
if (!camera.configured) {
// create a pivot at roughly the character's shoulder
camera.mCameraPivot =
@@ -411,6 +326,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
.with<Player>()
.without<GroundCheckReady>()
.each([](const EngineData &eng, CharacterBase &ch) {
ZoneScopedN("CheckGround");
#if 0
if (body.mGhostObject) {
btVector3 from =
@@ -491,6 +407,7 @@ void CharacterModule::updateCameraGoal(Camera &camera, Ogre::Real deltaYaw,
Ogre::Real deltaPitch,
Ogre::Real deltaZoom)
{
ZoneScoped;
static float canonDist = 0;
camera.mCameraPivot->yaw(Ogre::Degree(deltaYaw), Ogre::Node::TS_PARENT);
if (!(camera.mPivotPitch + deltaPitch > 25 && deltaPitch > 0) &&
@@ -549,6 +466,7 @@ void applyWeightBasedScale(Ogre::Entity *ent,
const Ogre::String &targetBoneName,
const Ogre::Vector3 &scale)
{
ZoneScoped;
Ogre::MeshPtr mesh = ent->getMesh();
Ogre::SkeletonInstance *skel = ent->getSkeleton();
Ogre::Bone *targetBone = skel->getBone(targetBoneName);
@@ -636,6 +554,7 @@ void CharacterModule::createCharacter(flecs::entity e,
const Ogre::Quaternion &rotation,
const Ogre::String model)
{
ZoneScoped;
if (e.has<CharacterBase>() || e.has<AnimationControl>())
return;
if (characterNodes.find(e) != characterNodes.end())

View File

@@ -5,6 +5,7 @@
#include "LuaData.h"
#include "EventModule.h"
#include "EventTriggerModule.h"
#include <tracy/Tracy.hpp>
struct TriggerBody {
void *data;
@@ -77,6 +78,7 @@ ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs)
.event(flecs::OnSet)
.each([](flecs::entity e, const EngineData &eng,
const EventTrigger &trigger) {
ZoneScoped;
e.set<EventTriggerData>({});
e.set<EventData>({});
});
@@ -84,13 +86,15 @@ ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs)
.event(flecs::OnSet)
.each([](flecs::entity e, const EventTrigger &trigger,
EventData &evt) {
evt.add(e, "actuator_created", e, e);
ZoneScoped;
evt.add(e, "actuator_created", e, e);
});
ecs.system<const EventTrigger, EventData>("HandleEventSystem")
.kind(flecs::OnUpdate)
.each([](flecs::entity e, const EventTrigger &trigger,
EventData &evt) {
if (e.parent().is_valid() &&
ZoneScoped;
if (e.parent().is_valid() &&
e.parent().has<EventData>()) {
bool added = false;
for (auto ev : evt.events) {

View File

@@ -29,6 +29,7 @@
#include "QuestModule.h"
#include "GUIModule.h"
#include "GUIModuleCommon.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
struct GUIListener;
@@ -46,6 +47,7 @@ struct GUIListener : public Ogre::RenderTargetListener {
GUIListener()
: Ogre::RenderTargetListener()
{
ZoneScoped;
_midFont = createFont("midFont", "General",
"Jupiteroid-Regular.ttf", 24.0f);
_smallFont = createFont("smallFont", "General",
@@ -66,7 +68,8 @@ struct GUIListener : public Ogre::RenderTargetListener {
const Ogre::String &group,
const Ogre::String &ttfname, float fontSize)
{
Ogre::FontPtr ret =
ZoneScoped;
Ogre::FontPtr ret =
Ogre::FontManager::getSingleton().create(name, group);
ret->setType(Ogre::FontType::FT_TRUETYPE);
ret->setSource(ttfname);
@@ -79,10 +82,12 @@ struct GUIListener : public Ogre::RenderTargetListener {
void
preViewportUpdate(const Ogre::RenderTargetViewportEvent &evt) override
{
preview(evt);
ZoneScopedN("GUIModule::preViewportUpdate");
preview(evt);
}
void buttons_panel()
{
ZoneScoped;
bool enableDebugRender = ECS::get<EngineData>().enableDbgDraw;
ImVec2 size = ImGui::GetMainViewport()->Size;
float window_width = size.x * 0.2f;
@@ -120,7 +125,8 @@ struct GUIListener : public Ogre::RenderTargetListener {
}
void create_entity_node(const Ogre::String &name, int key)
{
Ogre::Entity *ent =
ZoneScoped;
Ogre::Entity *ent =
ECS::get().get<EngineData>().mScnMgr->createEntity(
name);
Ogre::SceneNode *pnode =
@@ -147,7 +153,8 @@ struct GUIListener : public Ogre::RenderTargetListener {
}
void buildings_editor()
{
int i;
ZoneScoped;
int i;
ImVec2 size = ImGui::GetMainViewport()->Size;
float window_width = size.x * 0.2f;
if (window_width > panel_width)
@@ -174,7 +181,8 @@ struct GUIListener : public Ogre::RenderTargetListener {
}
void position_editor(Ogre::SceneNode *node)
{
Ogre::Vector3 position = node->getPosition();
ZoneScoped;
Ogre::Vector3 position = node->getPosition();
float v[3] = { position.x, position.y, position.z };
ImGui::InputFloat3("position", v);
position.x = v[0];
@@ -184,7 +192,8 @@ struct GUIListener : public Ogre::RenderTargetListener {
}
void orientation_editor(Ogre::SceneNode *node)
{
Ogre::Quaternion q = node->getOrientation();
ZoneScoped;
Ogre::Quaternion q = node->getOrientation();
float yaw = Ogre::Radian(q.getYaw()).valueDegrees();
float pitch = Ogre::Radian(q.getPitch()).valueDegrees();
float roll = Ogre::Radian(q.getRoll()).valueDegrees();
@@ -203,7 +212,8 @@ struct GUIListener : public Ogre::RenderTargetListener {
}
void attachments_editor(Ogre::SceneNode *node)
{
const Ogre::SceneNode::ObjectMap &pmap =
ZoneScoped;
const Ogre::SceneNode::ObjectMap &pmap =
node->getAttachedObjects();
int i;
for (i = 0; i < pmap.size(); i++) {
@@ -217,6 +227,7 @@ struct GUIListener : public Ogre::RenderTargetListener {
}
Ogre::Vector2 projectToScreen(const Ogre::Vector3 &worldPoint)
{
ZoneScoped;
ImVec2 size = ImGui::GetMainViewport()->Size;
float width = size.x;
float height = size.y;
@@ -241,7 +252,8 @@ struct GUIListener : public Ogre::RenderTargetListener {
}
void preview(const Ogre::RenderTargetViewportEvent &evt)
{
int i;
ZoneScoped;
int i;
Ogre::ImGuiOverlay::NewFrame();
if (ECS::get().get<EngineData>().startupDelay > 0.0f &&
@@ -742,6 +754,7 @@ GUIModule::GUIModule(flecs::world &ecs)
void GUIModule::configure()
{
ZoneScoped;
ECS::get().set<GUIData>({ nullptr, {}, nullptr });
const RenderWindow &window = ECS::get<RenderWindow>();
GUIData &gui = ECS::get_mut<GUIData>();

View File

@@ -28,12 +28,14 @@
#include "QuestModule.h"
#include "world-build.h"
#include "physics.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
static flecs::world ecs;
void setup_minimal()
{
ZoneScoped;
ecs.component<EngineData>().add(flecs::Singleton);
ecs.component<GameData>().add(flecs::Singleton);
ecs.component<Input>().add(flecs::Singleton);
@@ -50,6 +52,7 @@ void setup_minimal()
void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
Ogre::Camera *camera, Ogre::RenderWindow *window)
{
ZoneScoped;
std::cout << "Setup GameData\n";
setup_minimal();
@@ -92,19 +95,19 @@ void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
std::cout << "ground check ready\n";
#endif
});
ecs.system<EngineData>("CheckStatus")
#ifdef VDEBUG
ecs.system<EngineData>("CheckStatus")
.kind(flecs::OnUpdate)
.run([](flecs::iter &it) {
#ifdef VDEBUG
if (ECS::get().has<WaterReady>())
std::cout << "water ready\n";
if (ECS::get().has<TerrainReady>())
std::cout << "terrain ready\n";
if (ECS::get().has<GroundCheckReady>())
std::cout << "ground check ready\n";
#endif
});
ecs.set<EngineData>({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(),
#endif
ecs.set<EngineData>({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(),
(int)window->getHeight(), false });
ecs.set<Camera>({ cameraNode, camera, false });
ecs.add<GameData>();
@@ -112,6 +115,7 @@ void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
ecs.observer<GameState>("Game_Start_Scen_Startup")
.event(flecs::OnAdd)
.each([](GameState &game) {
ZoneScopedN("Game_Start_Scen_Startup");
ECS::get().add<WaterSurface>();
ECS::get().set<Sun>(
{ nullptr, nullptr, nullptr, nullptr });
@@ -136,6 +140,7 @@ void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
.kind(flecs::OnUpdate)
.interval(0.5f)
.each([&](Terrain &mterrain, GameState &game) {
ZoneScopedN("SpawnPlayer");
flecs::entity player =
ECS::get<CharacterManagerModule>().getPlayer();
if (!player.is_valid()) {
@@ -182,6 +187,7 @@ void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
.kind(flecs::OnUpdate)
.interval(0.5f)
.each([&](GameState &game) {
ZoneScopedN("NewHame");
flecs::entity player =
ECS::get<CharacterManagerModule>()
.getPlayer();
@@ -210,6 +216,7 @@ void setupInventoryScene(Ogre::SceneManager *scnMgr,
void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
Ogre::Camera *camera, Ogre::RenderWindow *window)
{
ZoneScoped;
setup_minimal();
ecs.component<RenderWindow>().add(flecs::Singleton);
ecs.component<EditorSceneSwitch>().add(flecs::Singleton);
@@ -245,19 +252,19 @@ void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
std::cout << "ground check ready\n";
#endif
});
ecs.system<EngineData>("CheckStatus")
#ifdef VDEBUG
ecs.system<EngineData>("CheckStatus")
.kind(flecs::OnUpdate)
.run([](flecs::iter &it) {
#ifdef VDEBUG
if (ECS::get().has<WaterReady>())
std::cout << "water ready\n";
if (ECS::get().has<TerrainReady>())
std::cout << "terrain ready\n";
if (ECS::get().has<GroundCheckReady>())
std::cout << "ground check ready\n";
#endif
});
ecs.set<EngineData>({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(),
#endif
ecs.set<EngineData>({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(),
(int)window->getHeight(), false });
ecs.set<Camera>({ cameraNode, camera, false });
#if 0
@@ -286,7 +293,10 @@ void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
void update(float delta)
{
ecs.progress(delta);
{
ZoneScopedN("ECS");
ecs.progress(delta);
}
}
flecs::world get()
{

View File

@@ -0,0 +1,13 @@
project(LuaModule)
find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain Overlay CONFIG)
find_package(flecs REQUIRED CONFIG)
find_package(Tracy REQUIRED CONFIG)
add_library(luamodule STATIC LuaData.cpp)
target_include_directories(luamodule PUBLIC .)
target_link_libraries(luamodule PUBLIC
lua
OgreMain
flecs::flecs_static
PRIVATE GameData Tracy::TracyClient
)

View File

@@ -1,3 +1,4 @@
#include "lua.hpp"
#include <OgreFileSystemLayer.h>
#include "GameData.h"
#include "Components.h"
@@ -10,11 +11,10 @@
#include "BoatModule.h"
#include "EventTriggerModule.h"
#include "SlotsModule.h"
#include "world-build.h"
#include "PlayerActionModule.h"
#include "QuestModule.h"
#include "LuaData.h"
#include "lua.hpp"
#include <tracy/Tracy.hpp>
extern "C" {
int luaopen_lpeg(lua_State *L);
}
@@ -25,51 +25,6 @@ struct FooPosition {
return value;
}
};
#if 0
namespace luaaa
{
template <> struct LuaStack<FooPosition> {
inline static FooPosition get(lua_State *L, int idx)
{
FooPosition result;
if (lua_istable(L, idx)) {
lua_pushnil(L);
while (0 != lua_next(L, idx)) {
const int top = lua_gettop(L);
const char *name =
LuaStack<const char *>::get(L, top - 1);
if (strncmp(name, "x", 1) == 0)
result.value.x =
LuaStack<float>::get(L, top);
else if (strncmp(name, "y", 1) == 0)
result.value.y =
LuaStack<float>::get(L, top);
else if (strncmp(name, "z", 1) == 0)
result.value.z =
LuaStack<float>::get(L, top);
lua_pop(L, 1);
}
lua_pop(L, 0);
}
return result;
}
inline static void put(lua_State *L, const FooPosition &v)
{
lua_newtable(L);
LuaStack<const char *>::put(L, "x");
LuaStack<float>::put(L, v.value.x);
lua_rawset(L, -3);
LuaStack<const char *>::put(L, "y");
LuaStack<float>::put(L, v.value.y);
lua_rawset(L, -3);
LuaStack<const char *>::put(L, "z");
LuaStack<float>::put(L, v.value.z);
lua_rawset(L, -3);
}
};
}
#endif
namespace ECS
{
struct LuaEcsEntity {
@@ -635,6 +590,7 @@ LuaData::LuaData()
flecs::entity object_e = idmap.get_entity(object);
Ogre::String nodeName = lua_tostring(L, 3);
Ogre::String stateName = lua_tostring(L, 4);
#if 0
bool reset = false;
if (lua_gettop(L) == 5)
reset = lua_toboolean(L, 5);
@@ -654,6 +610,8 @@ LuaData::LuaData()
{ obj, obj_node,
obj_state });
ECS::modified<GameWorld>();
#endif
OgreAssert(false, "Not implemented");
return 0;
} else if (command == "params-set") {

View File

@@ -19,6 +19,7 @@
#include "EventModule.h"
#include "TerrainModule.h"
#include "PhysicsModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
struct PhysicsShape {
@@ -34,6 +35,7 @@ struct TriggerBody {
};
PhysicsModule::PhysicsModule(flecs::world &ecs)
{
ZoneScoped;
ecs.module<PhysicsModule>();
ecs.import <EventModule>();
ecs.import <EventTriggerModule>();
@@ -54,8 +56,18 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
ecs.component<PhysicsMeshPtr>();
ecs.component<PhysicsHeightfieldData>();
ecs.component<CharacterBody>();
ecs.component<TriggerBody>();
ecs.component<CharacterBody>().on_remove([](flecs::entity e,
CharacterBody &body) {
JPH::Character *ch =
static_cast<JPH::Character *>(body.ch.get());
if (ch) {
if (e.has<JPH::BodyID>())
e.remove<JPH::BodyID>();
JoltPhysicsWrapper::getSingleton().destroyCharacter(ch);
body.ch = nullptr;
}
});
ecs.component<TriggerBody>();
ecs.component<CharacterVelocity>();
ecs.component<WaterBody>().add(flecs::Singleton);
ecs.component<CachedMass>();
@@ -64,6 +76,7 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
ecs.system<EngineData, Physics>("physics_update")
.kind(PhysicsUpdate)
.each([&](EngineData &e, Physics &ph) {
ZoneScopedN("physics_update");
ph.physics->update(e.delta);
});
ecs.observer<const EngineData, PhysicsMeshName>(
@@ -74,6 +87,7 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.write<PhysicsShape>()
.each([&](flecs::entity e, const EngineData &eng,
PhysicsMeshName &name) {
ZoneScopedN("create_shape_mesh_name");
Ogre::DefaultHardwareBufferManagerBase dmgr;
Ogre::MeshPtr mesh =
Ogre::MeshManager::getSingleton().getByName(
@@ -94,7 +108,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.write<PhysicsShape>()
.each([&](flecs::entity e, const EngineData &eng,
PhysicsMeshPtr &meshPtr) {
Ogre::DefaultHardwareBufferManager dmgr;
ZoneScopedN("create_shape_mesh_ptr");
Ogre::DefaultHardwareBufferManager dmgr;
Ogre::MeshPtr mesh = meshPtr.mesh;
if (!mesh->isLoaded()) {
mesh->setHardwareBufferManager(&dmgr);
@@ -115,7 +130,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.write<PhysicsShape>()
.each([&](flecs::entity e, const EngineData &eng,
PhysicsHeightfieldData &hfd) {
JPH::ShapeRefC shape =
ZoneScopedN("create_shape_heightfield");
JPH::ShapeRefC shape =
JoltPhysicsWrapper::getSingleton()
.createHeightfieldShape(
hfd.samples, hfd.offset,
@@ -136,7 +152,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.each([&](flecs::entity e, const EngineData &eng,
const PhysicsShape &shape, const PhysicsNode &node,
const PhysicsBody &body) {
JPH::BodyID id =
ZoneScopedN("create_body_from_shape");
JPH::BodyID id =
JoltPhysicsWrapper::getSingleton().createBody(
shape.shape.GetPtr(), 0.0f, node.node,
(JPH::EMotionType)body.motion,
@@ -149,9 +166,13 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
ecs.observer<const JPH::BodyID>("remove_body")
.event(flecs::OnRemove)
.each([&](flecs::entity e, const JPH::BodyID &id) {
JoltPhysicsWrapper::getSingleton().removeBody(id);
ZoneScopedN("remove_body");
JoltPhysicsWrapper::getSingleton().removeBody(id);
if (e.has<CharacterBase>() || e.has<Character>())
return;
if (JoltPhysicsWrapper::getSingleton().bodyIsCharacter(
id))
return;
JoltPhysicsWrapper::getSingleton().destroyBody(id);
std::cout << "body destroyed" << std::endl;
});
@@ -164,7 +185,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.write<JPH::BodyID>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterBase &base) {
CharacterBody &b = e.ensure<CharacterBody>();
ZoneScopedN("SetupCharacterPh");
CharacterBody &b = e.ensure<CharacterBody>();
b.ch.reset(JoltPhysicsWrapper::getSingleton()
.createCharacter(base.mBodyNode,
1.75f, 0.23f));
@@ -183,7 +205,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.without<JPH::BodyID>()
.each([](flecs::entity e, const EngineData &eng,
const EventTrigger &trigger) {
JPH::ShapeRefC shape =
ZoneScopedN("CreateTriggerPhysics");
JPH::ShapeRefC shape =
JoltPhysicsWrapper::getSingleton()
.createCylinderShape(trigger.halfheight,
trigger.radius);
@@ -267,20 +290,23 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
ECS::modified<LuaEvent>();
});
});
// FIXME: convert to normal configure to prevent multiple instances
ecs.system<const EngineData>("init_water")
.kind(PhysicsPreUpdate)
.with<TerrainReady>()
.with<WaterAlmostReady>()
.without<WaterBody>()
.each([this](const EngineData &eng) {
ECS::get().set<WaterBody>({});
ZoneScopedN("init_water");
ECS::get().set<WaterBody>({});
});
ecs.system<const EngineData, WaterBody>("update_water")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
.with<WaterAlmostReady>()
.each([this](const EngineData &eng, WaterBody &body) {
const WaterSurface &water = ECS::get<WaterSurface>();
ZoneScopedN("update_water");
const WaterSurface &water = ECS::get<WaterSurface>();
body.inWater.clear();
JoltPhysicsWrapper::getSingleton().broadphaseQuery(
eng.delta,
@@ -295,8 +321,11 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.with<InWater>()
.each([this](flecs::entity e, const JPH::BodyID &id,
const WaterBody &body) {
if (!body.isInWater(id))
ZoneScopedN("update_water_status1");
if (!body.isInWater(id)) {
e.remove<InWater>();
ZoneTextF("in water");
}
});
ecs.system<const JPH::BodyID, const WaterBody>("update_water_status2")
.kind(PhysicsPostUpdate)
@@ -305,8 +334,11 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.without<InWater>()
.each([this](flecs::entity e, const JPH::BodyID &id,
const WaterBody &body) {
if (body.isInWater(id))
ZoneScopedN("update_water_status2");
if (body.isInWater(id)) {
e.add<InWater>();
ZoneTextF("not in water");
}
});
ecs.system<const CharacterBody, const WaterBody>(
"update_water_character1")
@@ -316,10 +348,13 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.with<InWater>()
.each([this](flecs::entity e, const CharacterBody &ch,
const WaterBody &body) {
JPH::Character *chptr =
ZoneScopedN("update_water_character1");
JPH::Character *chptr =
static_cast<JPH::Character *>(ch.ch.get());
if (!body.isInWater(chptr->GetBodyID()))
if (!body.isInWater(chptr->GetBodyID())) {
e.remove<InWater>();
ZoneTextF("not in water");
}
});
ecs.system<const CharacterBody, const WaterBody>(
"update_water_character2")
@@ -329,10 +364,13 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.without<InWater>()
.each([this](flecs::entity e, const CharacterBody &ch,
const WaterBody &body) {
JPH::Character *chptr =
ZoneScopedN("update_water_character2");
JPH::Character *chptr =
static_cast<JPH::Character *>(ch.ch.get());
if (body.isInWater(chptr->GetBodyID()))
if (body.isInWater(chptr->GetBodyID())) {
e.add<InWater>();
ZoneTextF("in water");
}
});
ecs.system<const EngineData, const BoatBase, const WaterBody,
const JPH::BodyID>("update_water_boat_enable")
@@ -342,7 +380,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.each([this](flecs::entity e, const EngineData &eng,
const BoatBase &boat, const WaterBody &body,
const JPH::BodyID &id) {
if (!JoltPhysicsWrapper::getSingleton().isAdded(id))
ZoneScopedN("update_water_boat_enable");
if (!JoltPhysicsWrapper::getSingleton().isAdded(id))
JoltPhysicsWrapper::getSingleton().addBody(
id, JPH::EActivation::Activate);
});
@@ -355,7 +394,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.each([this](flecs::entity e, const EngineData &eng,
const BoatBase &boat, const WaterBody &body,
const JPH::BodyID &id) {
if (!JoltPhysicsWrapper::getSingleton().isActive(id))
ZoneScopedN("update_water_boat_activation");
if (!JoltPhysicsWrapper::getSingleton().isActive(id))
JoltPhysicsWrapper::getSingleton().activate(id);
});
ecs.system<const EngineData, const BoatBase, const WaterBody,
@@ -368,7 +408,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.each([this](flecs::entity e, const EngineData &eng,
const BoatBase &boat, const WaterBody &body,
const JPH::BodyID &id, const CachedMass &mass) {
const WaterSurface &water = ECS::get<WaterSurface>();
ZoneScopedN("update_water_boat_buoyancy");
const WaterSurface &water = ECS::get<WaterSurface>();
float b = 1.0f, drag = 0.5f, adrag = 0.5f;
float level = 0.25f;
float my = JoltPhysicsWrapper::getSingleton()
@@ -425,7 +466,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.with<CharacterBuoyancy>()
.each([this](flecs::entity e, const EngineData &eng,
const CharacterBody &ch, const WaterBody &body) {
JPH::Character *chptr =
ZoneScopedN("update_water_character_buoyancy");
JPH::Character *chptr =
static_cast<JPH::Character *>(ch.ch.get());
JPH::BodyID id = chptr->GetBodyID();
if (JoltPhysicsWrapper::getSingleton().isActive(id)) {
@@ -467,13 +509,15 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
eng.delta);
}
});
ecs.system<const EngineData, const CharacterBody>("UpdatePhysics")
ecs.system<const EngineData, const CharacterBody>(
"UpdateCharacterPhysicsState")
.kind(flecs::OnUpdate)
.with<CharacterUpdatePhysicsState>()
.write<CharacterUpdatePhysicsState>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterBody &body) {
if (e.has<CharacterDisablePhysics>())
ZoneScopedN("UpdateCharacterPhysicsState");
if (e.has<CharacterDisablePhysics>())
PhysicsModule::controlPhysics(e, false);
else
PhysicsModule::controlPhysics(e, true);
@@ -489,7 +533,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.each([this](flecs::entity e, const EngineData &eng,
const CharacterBase &chbase,
const CharacterBody &body, CharacterVelocity &gr) {
if (e.has<InWater>() &&
ZoneScopedN("HandleVelocity");
if (e.has<InWater>() &&
chbase.mBodyNode->_getDerivedPosition().y > -0.5f)
e.remove<InWater>();
Ogre::Vector3 v = gr.velocity;
@@ -519,7 +564,27 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
JoltPhysics::convert<JPH::Vec3>(v));
gr.velocity = Ogre::Vector3::ZERO;
});
ecs.system<const EngineData, CharacterBase, const CharacterBody,
ecs.system<CharacterBase>("HandleSubmerge")
.kind(flecs::OnUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.each([this](flecs::entity e, CharacterBase &ch) {
ZoneScopedNC("HandleSubmerge", 0xFF3030);
float full_subm = 2.0f;
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
float current_subm = -Ogre::Math::Clamp(
pos.y + Ogre::Math::Sin(ch.mTimer * 0.13f +
130.0f) *
0.07f,
-full_subm, 0.0f);
if (current_subm > 0.9f)
ch.is_submerged = true;
else if (current_subm < 0.8f)
ch.is_submerged = false;
ZoneTextF("is submerged: %d", (int)ch.is_submerged);
});
ecs.system<const EngineData, CharacterBase, const CharacterBody,
CharacterVelocity>("HandleVelocityNoPhysics")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
@@ -529,7 +594,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.each([this](flecs::entity e, const EngineData &eng,
CharacterBase &ch, const CharacterBody &body,
CharacterVelocity &gr) {
Ogre::Vector3 v = gr.velocity;
ZoneScopedNC("HandleVelocityNoPhysics", 0xFF4040);
Ogre::Vector3 v = gr.velocity;
// v.y = 0.0f;
ch.mBodyNode->_setDerivedPosition(
ch.mBodyNode->_getDerivedPosition() +
@@ -548,9 +614,12 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
ch.mBodyNode->_getDerivedPosition().y > -0.5f) {
e.remove<InWater>();
ch.is_submerged = false;
}
if (!e.has<InWater>() && ch.is_submerged)
ZoneTextF("remove in water");
}
if (!e.has<InWater>() && ch.is_submerged) {
ch.is_submerged = false;
ZoneTextF("not submerged");
}
});
}
flecs::entity PhysicsModule::createTerrainChunkBody(Ogre::SceneNode *node,
@@ -559,6 +628,7 @@ flecs::entity PhysicsModule::createTerrainChunkBody(Ogre::SceneNode *node,
const Ogre::Vector3 &scale,
int sampleCount)
{
ZoneScoped;
flecs::entity e = ECS::get().entity();
e.set<PhysicsHeightfieldData>({ samples, offset, scale, sampleCount });
e.set<PhysicsBody>({ (uint32_t)JPH::EMotionType::Static,
@@ -569,6 +639,7 @@ flecs::entity PhysicsModule::createTerrainChunkBody(Ogre::SceneNode *node,
}
void PhysicsModule::controlPhysics(flecs::entity e, bool enable)
{
ZoneScoped;
if (enable) {
if (e.has<CharacterBase>()) {
e.remove<CharacterDisablePhysics>();
@@ -616,6 +687,7 @@ bool PhysicsModule::raycastQuery(const Ogre::Vector3 &startPos,
const Ogre::Vector3 &endPos,
Ogre::Vector3 &position, JPH::BodyID &id)
{
ZoneScoped;
return JoltPhysicsWrapper::getSingleton().raycastQuery(startPos, endPos,
position, id);
}
@@ -627,6 +699,7 @@ void PhysicsModule::setDebugDraw(bool enable)
bool WaterBody::isInWater(const JPH::BodyID &id) const
{
ZoneScoped;
#if 0
flecs::entity e =
ECS::get().query_builder<const JPH::BodyID>().build().find(

View File

@@ -14,6 +14,7 @@
#include "LuaData.h"
#include "PhysicsModule.h"
#include "PlayerActionModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
@@ -70,16 +71,19 @@ struct TestNarrativeHandler : GUI::NarrationHandler {
}
void finish() override
{
ZoneScoped;
_clear_narration();
}
void activate() override
{
ZoneScoped;
_narration("Greetings...", {});
std::cout << getPropsJSON().dump(4) << std::endl;
count = 0;
}
void event(const Ogre::String &evt) override
{
ZoneScoped;
if (evt == "narration_progress" ||
evt == "narration_answered") {
count++;
@@ -111,6 +115,7 @@ struct LuaNarrationHandler : GUI::NarrationHandler {
: ref(ref)
, L(L)
{
ZoneScoped;
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
lua_pushlightuserdata(L, this);
lua_pushcclosure(
@@ -214,6 +219,7 @@ struct LuaNarrationHandler : GUI::NarrationHandler {
}
void finish() override
{
ZoneScoped;
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
int type = lua_getfield(L, -1, "finish");
OgreAssert(type == LUA_TFUNCTION, "bad finish()");
@@ -227,6 +233,7 @@ struct LuaNarrationHandler : GUI::NarrationHandler {
}
void activate() override
{
ZoneScoped;
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
int type = lua_getfield(L, -1, "activate");
OgreAssert(type == LUA_TFUNCTION, "bad activate()");
@@ -239,6 +246,7 @@ struct LuaNarrationHandler : GUI::NarrationHandler {
}
void event(const Ogre::String &evt) override
{
ZoneScoped;
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
int type = lua_getfield(L, -1, "event");
OgreAssert(type == LUA_TFUNCTION, "bad event()");
@@ -256,6 +264,7 @@ struct SimpleWordHandler : PlayerActionModule::ActionWordHandler {
void operator()(int actor, flecs::entity town, int index,
const Ogre::String &word, int actionNode) override
{
ZoneScoped;
if (index >= 0) {
TestNarrativeHandler *handle =
OGRE_NEW TestNarrativeHandler();
@@ -280,10 +289,12 @@ struct SimpleWordHandler : PlayerActionModule::ActionWordHandler {
PlayerActionModule::PlayerActionModule(flecs::world &ecs)
{
ZoneScoped;
ecs.module<PlayerActionModule>();
ecs.import <CharacterManagerModule>();
ecs.component<ActionNodeList>()
.on_add([](flecs::entity e, ActionNodeList &alist) {
ZoneScoped;
alist.nodeMutex = std::make_shared<std::mutex>();
alist.setDirty();
alist.nodes.reserve(1000);
@@ -295,6 +306,7 @@ PlayerActionModule::PlayerActionModule(flecs::world &ecs)
ecs.system<ActionNodeList>("updateNodeList")
.kind(flecs::OnUpdate)
.each([](ActionNodeList &list) {
ZoneScopedN("updateNodeList");
if (list.isBusy())
return;
if (list.nodes.size() > 0) {
@@ -319,6 +331,7 @@ PlayerActionModule::PlayerActionModule(flecs::world &ecs)
ecs.system<ActionNodeList, const Input>("ActivateActionNode")
.kind(flecs::OnUpdate)
.each([this](ActionNodeList &list, const Input &input) {
ZoneScopedN("ActivateActionNode");
std::lock_guard<std::mutex> lock(*list.nodeMutex);
if (input.control & 32)
std::cout << "act pressed" << std::endl;
@@ -365,6 +378,7 @@ PlayerActionModule::PlayerActionModule(flecs::world &ecs)
ecs.system<const EngineData>("UpdateActivatedWords")
.kind(flecs::OnUpdate)
.each([this](const EngineData &eng) {
ZoneScopedN("UpdateActivatedWords");
for (auto it = activatedWords.begin();
it != activatedWords.end();) {
int ret = it->second->_update(eng.delta);
@@ -380,12 +394,14 @@ PlayerActionModule::PlayerActionModule(flecs::world &ecs)
void PlayerActionModule::addWordHandler(const Ogre::String &word,
ActionWordHandler *handler)
{
ZoneScoped;
actionWords.insert({ word, handler });
}
void PlayerActionModule::removeWordHandler(const Ogre::String &word,
ActionWordHandler *handler)
{
ZoneScoped;
for (auto it = actionWords.begin(); it != actionWords.end();) {
if (it->first == word && it->second == handler)
it = actionWords.erase(it);
@@ -418,6 +434,7 @@ struct TestActivatedWordHandler : PlayerActionModule::ActivatedWordHandler {
, state(0)
, delay(0)
{
ZoneScoped;
activeActors.insert(actor);
// dynamic nodes can disappear on us so to avoid that use a copy
anode = ECS::get<ActionNodeList>().dynamicNodes[actionNode];
@@ -450,6 +467,7 @@ out:;
}
void teleport(const Ogre::String &place)
{
ZoneScoped;
if (placeLocalOffset.find(place) == placeLocalOffset.end())
return;
std::cout << "local offset: " << placeLocalOffset[place]
@@ -476,6 +494,7 @@ out:;
}
int update(float delta)
{
ZoneScoped;
switch (state) {
case 0:
if (ECS::get<Input>().act)
@@ -579,6 +598,7 @@ out:;
}
void enter()
{
ZoneScoped;
delay = 0.0f;
state = 0;
ECS::get_mut<GUI>().enableActions = false;
@@ -587,6 +607,7 @@ out:;
}
void exit(int result)
{
ZoneScoped;
ch.remove<CharacterInActuator>();
ch.remove<CharacterControlDisable>();
PhysicsModule::controlPhysics(ch, true);
@@ -599,6 +620,7 @@ out:;
}
virtual ~TestActivatedWordHandler()
{
ZoneScoped;
activeActors.erase(actor);
}
};
@@ -609,6 +631,7 @@ struct LuaWordHandler : PlayerActionModule::ActionWordHandler {
void operator()(int actor, flecs::entity town, int index,
const Ogre::String &word, int actionNode) override
{
ZoneScoped;
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
if (lua_type(L, -1) == LUA_TFUNCTION) {
luaL_checktype(L, -1, LUA_TFUNCTION);
@@ -662,6 +685,7 @@ struct LuaWordHandler : PlayerActionModule::ActionWordHandler {
void PlayerActionModule::addLuaWordHandler(const Ogre::String &word,
lua_State *L, int ref)
{
ZoneScoped;
struct LuaWordHandler *handler = OGRE_NEW LuaWordHandler;
handler->L = L;
handler->ref = ref;
@@ -671,6 +695,7 @@ void PlayerActionModule::addLuaWordHandler(const Ogre::String &word,
void PlayerActionModule::removeLuaWordHandler(const Ogre::String &word,
lua_State *L, int ref)
{
ZoneScoped;
for (auto it = actionWords.begin(); it != actionWords.end();) {
LuaWordHandler *handler =
static_cast<LuaWordHandler *>(it->second);
@@ -683,6 +708,7 @@ void PlayerActionModule::removeLuaWordHandler(const Ogre::String &word,
int PlayerActionModule::setupLuaActionHandler(lua_State *L)
{
ZoneScoped;
luaL_checktype(L, 1, LUA_TSTRING);
if (lua_type(L, 2) == LUA_TFUNCTION) {
luaL_checktype(L, 2, LUA_TFUNCTION);
@@ -703,6 +729,7 @@ int PlayerActionModule::setupLuaActionHandler(lua_State *L)
void PlayerActionModule::addActivatedWordHandler(const Ogre::String &word,
ActivatedWordHandler *handler)
{
ZoneScoped;
for (auto it = activatedWords.begin(); it != activatedWords.end();
it++) {
}
@@ -712,6 +739,7 @@ void PlayerActionModule::addActivatedWordHandler(const Ogre::String &word,
void PlayerActionModule::removeActivatedWordHandler(
const Ogre::String &word, ActivatedWordHandler *handler)
{
ZoneScoped;
for (auto it = activatedWords.begin(); it != activatedWords.end();) {
if (it->first == word && it->second == handler)
it = activatedWords.erase(it);
@@ -722,29 +750,42 @@ void PlayerActionModule::removeActivatedWordHandler(
void ActionNodeList::updateDynamicNodes()
{
ZoneScoped;
std::lock_guard<std::mutex> lock(*nodeMutex);
if (dynamicNodes.size() > nodes.size())
dynamicNodes.resize(nodes.size());
else {
dynamicNodes.clear();
dynamicNodes.insert(dynamicNodes.end(), nodes.begin(),
nodes.end());
{
ZoneScopedN("static");
ZoneTextF("before: dynamicNodes: %d nodes: %d",
(int)dynamicNodes.size(), (int)nodes.size());
// Do not constantly re-create static nodes
if (dynamicNodes.size() > nodes.size())
dynamicNodes.resize(nodes.size());
else if (dynamicNodes.size() < nodes.size()) {
dynamicNodes.clear();
dynamicNodes.insert(dynamicNodes.end(), nodes.begin(),
nodes.end());
ZoneTextF("updated: dynamicNodes: %d nodes: %d",
(int)dynamicNodes.size(), (int)nodes.size());
}
}
{
ZoneScopedN("dynamic");
ECS::get().query_builder<const TownNPCs>().each(
[this](flecs::entity town, const TownNPCs &npcs) {
for (auto it = npcs.npcs.begin();
it != npcs.npcs.end(); it++) {
dynamicNodes.insert(
dynamicNodes.end(),
it->second.actionNodes.begin(),
it->second.actionNodes.end());
}
});
}
ECS::get().query_builder<const TownNPCs>().each(
[this](flecs::entity town, const TownNPCs &npcs) {
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end();
it++) {
dynamicNodes.insert(
dynamicNodes.end(),
it->second.actionNodes.begin(),
it->second.actionNodes.end());
}
});
dirty = true;
}
void ActionNodeList::build()
{
ZoneScoped;
std::lock_guard<std::mutex> lock(*nodeMutex);
indexObj = std::make_shared<ActionNodeList::indexObject>(dynamicNodes);
indexObj->index.buildIndex();
@@ -755,6 +796,7 @@ bool ActionNodeList::_query(const Ogre::Vector3 &position,
std::vector<size_t> &points,
std::vector<float> &distances)
{
ZoneScoped;
std::vector<size_t> tmppoints;
std::vector<float> tmpdistances;
points.clear();
@@ -784,6 +826,7 @@ bool ActionNodeList::query_ai(const Ogre::Vector3 &position, float distance,
std::vector<size_t> &points,
std::vector<float> &distances)
{
ZoneScoped;
std::lock_guard<std::mutex> lock(*nodeMutex);
std::vector<size_t> tmppoints;
std::vector<float> tmpdistances;
@@ -812,6 +855,7 @@ bool ActionNodeList::query_ai(const Ogre::Vector3 &position, float distance,
int ActionNodeList::addNode(ActionNode &node)
{
ZoneScoped;
std::lock_guard<std::mutex> lock(*nodeMutex);
int index = nodes.size();
nodes.push_back(node);
@@ -821,6 +865,7 @@ int ActionNodeList::addNode(ActionNode &node)
void ActionNodeList::removeNode(int index)
{
ZoneScoped;
std::lock_guard<std::mutex> lock(*nodeMutex);
nodes.erase(nodes.begin() + index);
dirty = true;
@@ -828,12 +873,14 @@ void ActionNodeList::removeNode(int index)
const ActionNodeList::UIData &ActionNodeList::getUIData()
{
ZoneScoped;
std::lock_guard<std::mutex> lock(*uidata.mutex);
return uidata;
}
void ActionNodeList::setUISelected(int selected)
{
ZoneScoped;
std::lock_guard<std::mutex> lock(*uidata.mutex);
uidata.selected = selected;
@@ -842,6 +889,7 @@ void ActionNodeList::setUISelected(int selected)
void ActionNodeList::setUIPoints(const std::vector<size_t> &points,
const std::vector<float> &distances)
{
ZoneScoped;
std::lock_guard<std::mutex> lock(*uidata.mutex);
uidata.points = points;
uidata.distances = distances;
@@ -849,6 +897,7 @@ void ActionNodeList::setUIPoints(const std::vector<size_t> &points,
void ActionNodeList::UIquery(const Ogre::Vector3 &position)
{
ZoneScoped;
bool needBuild = false;
{

View File

@@ -3,16 +3,21 @@
#include "GameData.h"
#include "GUIModuleCommon.h"
#include "QuestModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
QuestModule::QuestModule(flecs::world &ecs)
{
ZoneScoped;
ecs.module<QuestModule>();
ecs.observer<GameState>("EnableQuests")
.event(flecs::OnAdd)
.each([this](GameState &game) {
ZoneScoped;
if (quest_update.is_valid())
return;
quest_update =
ECS::get()
.system<const EngineData>(
@@ -28,6 +33,7 @@ QuestModule::QuestModule(flecs::world &ecs)
ecs.observer<GameState>("DisableQuests")
.event(flecs::OnRemove)
.each([this](GameState &game) {
ZoneScoped;
if (quest_update.is_valid())
quest_update.destruct();
});
@@ -35,11 +41,13 @@ QuestModule::QuestModule(flecs::world &ecs)
void QuestModule::addQuest(Quest *quest)
{
ZoneScoped;
quests.insert(quest);
}
void QuestModule::removeQuest(Quest *quest)
{
ZoneScoped;
quests.erase(quests.find(quest));
}
struct LuaNarrationHandler : GUI::NarrationHandler {
@@ -49,6 +57,7 @@ struct LuaNarrationHandler : GUI::NarrationHandler {
: ref(ref)
, L(L)
{
ZoneScoped;
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
lua_pushlightuserdata(L, this);
lua_pushcclosure(
@@ -125,6 +134,7 @@ struct LuaNarrationHandler : GUI::NarrationHandler {
}
void finish() override
{
ZoneScoped;
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
int type = lua_getfield(L, -1, "finish");
OgreAssert(type == LUA_TFUNCTION, "bad finish()");
@@ -138,6 +148,7 @@ struct LuaNarrationHandler : GUI::NarrationHandler {
}
void activate() override
{
ZoneScoped;
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
int type = lua_getfield(L, -1, "activate");
OgreAssert(type == LUA_TFUNCTION, "bad activate()");
@@ -150,6 +161,7 @@ struct LuaNarrationHandler : GUI::NarrationHandler {
}
void event(const Ogre::String &evt) override
{
ZoneScoped;
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
int type = lua_getfield(L, -1, "event");
OgreAssert(type == LUA_TFUNCTION, "bad event()");
@@ -171,6 +183,7 @@ struct LuaQuest : QuestModule::Quest {
, L(L)
, ref(ref)
{
ZoneScoped;
OgreAssert(L, "bad Lua state");
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
lua_pushlightuserdata(L, this);
@@ -206,6 +219,7 @@ struct LuaQuest : QuestModule::Quest {
}
void finish(int rc) override
{
ZoneScoped;
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
int type = lua_getfield(L, -1, "finish");
OgreAssert(type == LUA_TFUNCTION, "bad finish()");
@@ -219,6 +233,7 @@ struct LuaQuest : QuestModule::Quest {
}
void activate() override
{
ZoneScoped;
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
int type = lua_getfield(L, -1, "activate");
OgreAssert(type == LUA_TFUNCTION, "bad activate()");
@@ -231,6 +246,7 @@ struct LuaQuest : QuestModule::Quest {
}
void event(const Ogre::String &evt) override
{
ZoneScoped;
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
int type = lua_getfield(L, -1, "event");
OgreAssert(type == LUA_TFUNCTION, "bad event()");
@@ -244,6 +260,7 @@ struct LuaQuest : QuestModule::Quest {
}
int update(float delta) override
{
ZoneScoped;
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
int type = lua_getfield(L, -1, "finish");
OgreAssert(type == LUA_TFUNCTION, "bad finish()");
@@ -263,6 +280,7 @@ struct LuaQuest : QuestModule::Quest {
void QuestModule::addLuaQuest(lua_State *L)
{
ZoneScoped;
luaL_checktype(L, 1, LUA_TSTRING);
luaL_checktype(L, 2, LUA_TTABLE);
Ogre::String name = lua_tostring(L, 1);
@@ -274,12 +292,14 @@ void QuestModule::addLuaQuest(lua_State *L)
void QuestModule::Quest::_activate()
{
ZoneScoped;
activate();
active = true;
}
void QuestModule::Quest::_finish(int rc)
{
ZoneScoped;
finish(rc);
active = false;
if (rc == OK)
@@ -301,6 +321,7 @@ QuestModule::Quest::~Quest()
int QuestModule::Quest::_update(float delta)
{
ZoneScoped;
if (!active && !_can_activate())
return ERROR;
if (!active)
@@ -320,6 +341,7 @@ int QuestModule::Quest::_update(float delta)
void QuestModule::Quest::_event(const std::string &evt)
{
ZoneScoped;
event(evt);
}

View File

@@ -6,11 +6,13 @@
#include "WaterModule.h"
#include "BoatModule.h"
#include "SlotsModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
SlotsModule::SlotsModule(flecs::world &ecs)
{
ecs.module<SlotsModule>();
ZoneScoped;
ecs.module<SlotsModule>();
ecs.import <CharacterModule>();
ecs.component<ParentSlot>();
ecs.component<ParentSlotData>();
@@ -20,7 +22,10 @@ SlotsModule::SlotsModule(flecs::world &ecs)
ecs.observer<const EngineData, const BoatBase>("CreateBoatSlots")
.event(flecs::OnSet)
.each([&](flecs::entity e, const EngineData &eng,
const BoatBase &boat) { createBoatSlots(e); });
const BoatBase &boat) {
ZoneScoped;
createBoatSlots(e);
});
#if 1
ecs.system<const EngineData, const CharacterBase, ParentSlot>(
"UpdateSlotData")
@@ -29,6 +34,7 @@ SlotsModule::SlotsModule(flecs::world &ecs)
.without<ParentSlotData>()
.each([&](flecs::entity e, const EngineData &eng,
const CharacterBase &ch, ParentSlot &slot) {
ZoneScoped;
if (slot.slot_name == "") {
slot.removeSlot(e);
return;
@@ -77,7 +83,8 @@ SlotsModule::SlotsModule(flecs::world &ecs)
}
void SlotsModule::createBoatSlots(flecs::entity e)
{
const EngineData &eng = e.world().get<EngineData>();
ZoneScoped;
const EngineData &eng = e.world().get<EngineData>();
const BoatBase &boat = e.get<BoatBase>();
int i;
std::vector<Ogre::Node *> slots = boat.mNode->getChildren();
@@ -115,7 +122,8 @@ void SlotsModule::createBoatSlots(flecs::entity e)
}
void ParentSlot::createSlot(flecs::entity e)
{
if (e.has<CharacterBase>()) {
ZoneScoped;
if (e.has<CharacterBase>()) {
createCharacterSlot(e);
}
}
@@ -124,14 +132,16 @@ void ParentSlot::createCharacterSlot(flecs::entity e)
}
void ParentSlot::removeSlot(flecs::entity e)
{
if (e.has<ParentSlot>())
ZoneScoped;
if (e.has<ParentSlot>())
e.remove<ParentSlot>();
if (e.has<ParentSlot>())
e.remove<ParentSlotData>();
}
bool ParentSlot::check() const
{
if (!parent_e.has<ObjectSlots>())
ZoneScoped;
if (!parent_e.has<ObjectSlots>())
return false;
const ObjectSlots &slots = parent_e.get<ObjectSlots>();
if (!slots.exists(slot_name))
@@ -140,13 +150,15 @@ bool ParentSlot::check() const
}
bool ParentSlot::parentIsValid()
{
if (!parent_e.has<ObjectSlots>())
ZoneScoped;
if (!parent_e.has<ObjectSlots>())
return false;
return true;
}
Ogre::SceneNode *ParentSlot::getSlotBase() const
{
if (!check())
ZoneScoped;
if (!check())
return nullptr;
const ObjectSlots &slots = parent_e.get<ObjectSlots>();
Ogre::SceneNode *slot_base = slots.slots.at(slot_name).second;
@@ -154,7 +166,8 @@ Ogre::SceneNode *ParentSlot::getSlotBase() const
}
void ParentSlot::addChild(Ogre::SceneNode *childNode)
{
Ogre::SceneNode *parentNode = getSlotBase();
ZoneScoped;
Ogre::SceneNode *parentNode = getSlotBase();
if (childNode->getParentSceneNode())
childNode->getParentSceneNode()->removeChild(childNode);
parentNode->addChild(childNode);
@@ -163,7 +176,8 @@ void ParentSlot::addChild(Ogre::SceneNode *childNode)
}
void ParentSlot::createSlotData(flecs::entity e)
{
const ObjectSlots &slots = parent_e.get<ObjectSlots>();
ZoneScoped;
const ObjectSlots &slots = parent_e.get<ObjectSlots>();
ParentSlotData &psdata = e.ensure<ParentSlotData>();
Ogre::SceneNode *slot_base = getSlotBase();
// Ogre::Vector3 position = slot_base->_getDerivedPosition();
@@ -178,6 +192,7 @@ void ParentSlot::createSlotData(flecs::entity e)
}
bool ObjectSlots::exists(const Ogre::String &name) const
{
return slots.find(name) != slots.end();
ZoneScoped;
return slots.find(name) != slots.end();
}
}

View File

@@ -1,10 +1,12 @@
#include "SmartObject.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
SmartObjectModule::SmartObjectModule(flecs::world &ecs)
{
ZoneScoped;
ecs.module<SmartObjectModule>();
ecs.component<SmartObjectManager>().add(flecs::Singleton);
ecs.component<SmartObject>();
}
}
}

View File

@@ -15,6 +15,7 @@
#include "PhysicsModule.h"
#include "items.h"
#include "StaticGeometryModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
@@ -25,12 +26,14 @@ static bool templatesLoaded = false;
static std::list<std::pair<long, long> > addQueue;
StaticGeometryModule::StaticGeometryModule(flecs::world &ecs)
{
ZoneScoped;
ecs.module<StaticGeometryModule>();
ecs.component<TerrainSlotParent>();
ecs.component<TerrainItem>();
ecs.component<FurnitureItem>();
ecs.component<FurnitureInstance>()
.on_remove([](flecs::entity e, FurnitureInstance &instance) {
ZoneScoped;
if (instance.furniture) {
instance.furniture
->destroyAllChildrenAndObjects();
@@ -40,6 +43,7 @@ StaticGeometryModule::StaticGeometryModule(flecs::world &ecs)
}
})
.on_set([](flecs::entity e, FurnitureInstance &instance) {
ZoneScoped;
if (instance.furniture !=
e.get<FurnitureInstance>().furniture) {
FurnitureInstance &f =
@@ -53,11 +57,13 @@ StaticGeometryModule::StaticGeometryModule(flecs::world &ecs)
}
})
.on_add([](flecs::entity e, FurnitureInstance &instance) {
ZoneScoped;
instance.furniture = nullptr;
});
ecs.component<TerrainItemNode>().on_remove([](flecs::entity e,
TerrainItemNode &item) {
if (item.itemNode) {
ZoneScoped;
if (item.itemNode) {
item.itemNode->destroyAllChildrenAndObjects();
item.itemNode->getCreator()->destroySceneNode(
item.itemNode);
@@ -75,7 +81,8 @@ StaticGeometryModule::StaticGeometryModule(flecs::world &ecs)
ecs.observer<const Terrain>("LoadTerrainItems")
.event(flecs::OnSet)
.each([&](const Terrain &terrain) {
if (terrain.mTerrainGroup && !itemsLoaded) {
ZoneScopedN("LoadTerrainItems");
if (terrain.mTerrainGroup && !itemsLoaded) {
loadItems();
itemsLoaded = true;
}
@@ -83,7 +90,8 @@ StaticGeometryModule::StaticGeometryModule(flecs::world &ecs)
if (!Ogre::MeshLodGenerator::getSingletonPtr())
new Ogre::MeshLodGenerator();
ecs.system("AddGeometryQueue").kind(flecs::OnUpdate).run([&](flecs::iter &it) {
std::list<flecs::entity> items;
ZoneScopedN("AddGeometryQueue");
std::list<flecs::entity> items;
if (!ECS::get().has<Terrain>())
return;
if (!ECS::get<Terrain>().mTerrainGroup)
@@ -142,11 +150,13 @@ StaticGeometryModule::StaticGeometryModule(flecs::world &ecs)
}
void StaticGeometryModule::addGeometryForSlot(long x, long y)
{
addQueue.push_back({ x, y });
ZoneScoped;
addQueue.push_back({ x, y });
}
void StaticGeometryModule::removeGeometryForSlot(long x, long y)
{
std::pair<long, long> slot = { x, y };
ZoneScoped;
std::pair<long, long> slot = { x, y };
flecs::entity parent =
ECS::get().query_builder<const TerrainSlotParent>().build().find(
[&slot](const TerrainSlotParent &parent) {
@@ -169,7 +179,8 @@ StaticGeometryModule::createItem(const Ogre::Vector3 &position,
const Ogre::Quaternion &orientation,
const Ogre::String &type)
{
long x, y;
ZoneScoped;
long x, y;
ECS::get<Terrain>().mTerrainGroup->convertWorldPositionToTerrainSlot(
position, &x, &y);
std::pair<long, long> pos{ x, y };
@@ -191,19 +202,22 @@ StaticGeometryModule::createItem(const Ogre::Vector3 &position,
void StaticGeometryModule::setItemProperties(flecs::entity id,
Ogre::String properties)
{
OgreAssert(id.is_valid(), "bad id");
ZoneScoped;
OgreAssert(id.is_valid(), "bad id");
id.get_mut<TerrainItem>().properties = properties;
id.modified<TerrainItem>();
}
const Ogre::String &StaticGeometryModule::getItemProperties(flecs::entity id)
{
OgreAssert(id.is_valid(), "bad id");
ZoneScoped;
OgreAssert(id.is_valid(), "bad id");
return id.get<TerrainItem>().properties;
}
nlohmann::json templates;
void StaticGeometryModule::loadTemplates()
{
ZoneScoped;
if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
"templates.list"))
return;
@@ -220,6 +234,7 @@ void StaticGeometryModule::loadTemplates()
void StaticGeometryModule::saveTemplates()
{
ZoneScoped;
Ogre::String path = "resources/buildings/templates.list";
if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
"templates.list")) {
@@ -242,7 +257,8 @@ void StaticGeometryModule::saveTemplates()
void StaticGeometryModule::saveItems()
{
Ogre::String path = "resources/buildings/items.list";
ZoneScoped;
Ogre::String path = "resources/buildings/items.list";
if (Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
"items.list")) {
Ogre::String group =
@@ -288,7 +304,8 @@ void StaticGeometryModule::saveItems()
}
void StaticGeometryModule::loadItems()
{
if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
ZoneScoped;
if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup(
"items.list"))
return;
Ogre::String group = Ogre::ResourceGroupManager::getSingleton()
@@ -362,6 +379,7 @@ void StaticGeometryModule::saveFurniture()
void StaticGeometryModule::loadFurniture()
{
ZoneScoped;
ECS::get().delete_with<FurnitureItem>();
static std::vector<Ogre::String> glb_names;
const std::vector<Ogre::String> &groups =
@@ -405,6 +423,7 @@ void StaticGeometryModule::loadFurniture()
void StaticGeometryModule::getItemPositionPerSlot(
long x, long y, std::list<Ogre::Vector3> *positions)
{
ZoneScoped;
std::pair<long, long> pos{ x, y };
if (!positions)
return;
@@ -425,6 +444,7 @@ void StaticGeometryModule::getItemPositionPerSlot(
}
void StaticGeometryModule::getItemPositions(std::list<Ogre::Vector3> *positions)
{
ZoneScoped;
ECS::get().query_builder<const TerrainItem>().build().each(
[&](flecs::entity e, const TerrainItem &item) {
positions->push_back(item.position);
@@ -433,12 +453,14 @@ void StaticGeometryModule::getItemPositions(std::list<Ogre::Vector3> *positions)
void StaticGeometryModule::getItemPositionAndRotation(
flecs::entity e, Ogre::Vector3 &position, Ogre::Quaternion &orientation)
{
ZoneScoped;
position = e.get<TerrainItem>().position;
orientation = e.get<TerrainItem>().orientation;
}
void StaticGeometryModule::getItemsProperties(
std::list<std::pair<flecs::entity, Ogre::String> > *items)
{
ZoneScoped;
ECS::get().query_builder<const TerrainItem>().build().each(
[&](flecs::entity e, const TerrainItem &item) {
items->push_back({ e, item.properties });
@@ -446,11 +468,13 @@ void StaticGeometryModule::getItemsProperties(
}
void StaticGeometryModule::createItemGeometry(flecs::entity e)
{
ZoneScoped;
Geometry::createItemGeometry(e);
}
void StaticGeometryModule::destroyItemGeometry(flecs::entity e)
{
ZoneScoped;
Geometry::destroyItemGeometry(e);
}
@@ -461,16 +485,15 @@ nlohmann::json &StaticGeometryModule::getTemplates()
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>();
});
ZoneScoped;
// We add this as task to reduce UI load
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([e]() {
ZoneScopedN("updateItemGeometry");
if (e.has<GeometryUpdateItem>())
return;
e.add<GeometryUpdateItem>();
Geometry::updateItemGeometry(e);
e.remove<GeometryUpdateItem>();
});
}
@@ -479,6 +502,7 @@ void StaticGeometryModule::addTriangleBufferWork(
const Ogre::Vector3 &position, const Ogre::Quaternion &rotation,
const Procedural::TriangleBuffer &tb)
{
ZoneScoped;
struct WorkData {
Ogre::String meshName;
Ogre::StaticGeometry *geo;
@@ -487,22 +511,14 @@ void StaticGeometryModule::addTriangleBufferWork(
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);
});
// We add this as task to reduce UI load
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([data]() {
ZoneScopedN("addTriangleBufferWork");
Ogre::MeshPtr mesh = data.tb.transformToMesh(data.meshName);
Ogre::Entity *ent =
ECS::get<EngineData>().mScnMgr->createEntity(mesh);
data.geo->addEntity(ent, data.position, data.rotation);
ECS::get<EngineData>().mScnMgr->destroyEntity(ent);
});
}
struct TiledMeshes {

View File

@@ -2,16 +2,19 @@
#include <Ogre.h>
#include "Components.h"
#include "SunModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
SunModule::SunModule(flecs::world &ecs)
{
ecs.component<Sun>().add(flecs::Singleton);
ZoneScoped;
ecs.component<Sun>().add(flecs::Singleton);
ecs.system<const EngineData, Sun>("UpdateSetupSun")
.kind(flecs::OnUpdate)
.each([](const EngineData &eng, Sun &sun) {
if (!sun.mSun) {
ZoneScopedN("UpdateSetupSun");
if (!sun.mSun) {
Ogre::Light *light =
eng.mScnMgr->createLight("Sun");
sun.mSunNode = eng.mScnMgr->getRootSceneNode()

View File

@@ -18,6 +18,7 @@
#include "PhysicsModule.h"
#include "StaticGeometryModule.h"
#include "TerrainModule.h"
#include <tracy/Tracy.hpp>
#define TERRAIN_SIZE 65
#define TERRAIN_WORLD_SIZE 500.0f
@@ -48,6 +49,7 @@ struct HeightData {
static HeightData *singleton;
HeightData()
{
ZoneScoped;
img.load(
"world_map.png",
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
@@ -60,13 +62,15 @@ struct HeightData {
}
static HeightData *get_singleton()
{
if (!singleton)
ZoneScoped;
if (!singleton)
singleton = new HeightData();
return singleton;
}
float get_brush_height(int id, int x, int y)
{
int m = 0;
ZoneScoped;
int m = 0;
switch (id) {
case 0:
m = 0;
@@ -110,7 +114,8 @@ struct HeightData {
}
void save_heightmap()
{
Ogre::String group =
ZoneScoped;
Ogre::String group =
Ogre::ResourceGroupManager::getSingleton()
.findGroupContainingResource("world_map.png");
Ogre::FileInfoListPtr fileInfoList(
@@ -125,7 +130,7 @@ struct HeightData {
}
float get_base_height(long world_x, long world_y)
{
float height = 0.0f;
float height = 0.0f;
int world_img_x =
world_x + (int)img.getWidth() * BRUSH_SIZE / 2;
int world_img_y =
@@ -155,7 +160,7 @@ out:
}
float get_noise_height(long world_x, long world_y)
{
int h;
int h;
Ogre::Vector2 noisePoint;
struct noise_types {
@@ -192,7 +197,7 @@ out:
float get_height(Ogre::TerrainGroup *terrainGroup, long world_x,
long world_y)
{
long grid_center_x = img.getWidth() * BRUSH_SIZE / 2;
long grid_center_x = img.getWidth() * BRUSH_SIZE / 2;
long grid_center_y = img.getHeight() * BRUSH_SIZE / 2;
long world_grid_x = world_x + grid_center_x;
long world_grid_y = world_y + grid_center_y;
@@ -265,6 +270,7 @@ public:
void createTerrainChunk(Ogre::TerrainGroup *terrainGroup, long x,
long y)
{
ZoneScoped;
Ogre::Terrain *terrain = terrainGroup->getTerrain(x, y);
float minH = terrain->getMinHeight();
float maxH = terrain->getMaxHeight();
@@ -290,7 +296,8 @@ public:
}
void define(Ogre::TerrainGroup *terrainGroup, long x, long y) override
{
std::lock_guard<std::mutex> guard(mtx);
ZoneScoped;
std::lock_guard<std::mutex> guard(mtx);
uint16_t terrainSize = terrainGroup->getTerrainSize();
float *heightMap = OGRE_ALLOC_T(float, terrainSize *terrainSize,
MEMCATEGORY_GEOMETRY);
@@ -332,13 +339,15 @@ public:
}
bool frameStarted(const Ogre::FrameEvent &evt) override
{
(void)evt;
ZoneScoped;
(void)evt;
update();
return true;
}
void update()
{
std::lock_guard<std::mutex> guard(mtx);
ZoneScoped;
std::lock_guard<std::mutex> guard(mtx);
static bool created = false;
while (!collider_queue.empty()) {
Ogre::TerrainGroup *group =
@@ -478,7 +487,8 @@ public:
bool unloadProceduralPage(Ogre::Page *page,
Ogre::PagedWorldSection *section)
{
long x, y;
ZoneScoped;
long x, y;
ECS::get<Terrain>().mTerrainGroup->unpackIndex(page->CHUNK_ID,
&x, &y);
StaticGeometryModule::removeGeometryForSlot(x, y);
@@ -492,7 +502,8 @@ public:
};
TerrainModule::TerrainModule(flecs::world &ecs)
{
struct CanSetPlayerPosition {};
ZoneScoped;
struct CanSetPlayerPosition {};
ecs.module<TerrainModule>();
ecs.component<CanSetPlayerPosition>().add(flecs::Singleton);
ecs.component<Terrain>().add(flecs::Singleton);
@@ -509,7 +520,8 @@ TerrainModule::TerrainModule(flecs::world &ecs)
.each([](const EngineData &eng, const Camera &camera,
const Sun &sun, Terrain &terrain,
TerrainPrivate &priv) {
if (!terrain.mTerrainGroup && sun.mSun && eng.mScnMgr) {
ZoneScoped;
if (!terrain.mTerrainGroup && sun.mSun && eng.mScnMgr) {
std::cout << "Terrain setup\n";
if (!priv.mDummyPageProvider)
priv.mDummyPageProvider =
@@ -635,7 +647,8 @@ TerrainModule::TerrainModule(flecs::world &ecs)
.kind(flecs::OnUpdate)
.without<TerrainReady>()
.each([](const ECS::Camera &cam, const Terrain &terrain) {
std::cout << "mTerrainReady: " << terrain.mTerrainReady
ZoneScoped;
std::cout << "mTerrainReady: " << terrain.mTerrainReady
<< "\n";
if (cam.mCameraNode && terrain.mTerrainReady) {
long x, y;
@@ -653,7 +666,8 @@ TerrainModule::TerrainModule(flecs::world &ecs)
ecs.system<const Terrain, PlacementObjects>("UpdatePlacementObjects")
.kind(flecs::OnUpdate)
.each([](const Terrain &terrain, PlacementObjects &placement) {
if (placement.altar_items.size() == 0) {
ZoneScoped;
if (placement.altar_items.size() == 0) {
struct PlacementObjects::item item;
int i, j;
int worldSize = terrain.mTerrainGroup
@@ -766,7 +780,8 @@ TerrainModule::TerrainModule(flecs::world &ecs)
.kind(flecs::OnUpdate)
.interval(2.0f)
.each([](const Terrain &terrain) {
if (!terrain.mTerrainGroup
ZoneScoped;
if (!terrain.mTerrainGroup
->isDerivedDataUpdateInProgress())
terrain.mTerrainGroup->update(false);
});

View File

@@ -10,6 +10,7 @@
#include "GameData.h"
#include "Components.h"
#include "WaterModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
#if 0
@@ -112,12 +113,6 @@ WaterModule::WaterModule(flecs::world &ecs)
Ogre::Plane(Ogre::Vector3(0.0, -1.0, 0.0), h);
water.mRefractionClipPlaneBelow =
Ogre::Plane(Ogre::Vector3(0.0, 1.0, 0.0), -h);
#if 0
if (Ogre::TextureManager::getSingleton()
.resourceExists(renderTargetName))
Ogre::TextureManager::getSingleton()
.remove(renderTargetName);
#endif
Ogre::TexturePtr reflectionTexture =
Ogre::TextureManager::getSingleton().createManual(
renderTargetName,
@@ -201,64 +196,6 @@ WaterModule::WaterModule(flecs::world &ecs)
Ogre::MANUAL_CULL_NONE);
pass->setVertexProgram("Water/water_vp");
pass->setFragmentProgram("Water/water_fp");
#if 0
Ogre::GpuProgramPtr water_vp =
Ogre::GpuProgramManager::getSingleton()
.getByName(
"Water/water_vp",
Ogre::RGN_AUTODETECT);
OgreAssert(water_vp != nullptr,
"VP failed");
pass->setGpuProgram(
Ogre::GPT_VERTEX_PROGRAM,
water_vp);
OgreAssert(water_vp->isSupported(),
"VP not supported");
Ogre::GpuProgramPtr water_fp =
Ogre::GpuProgramManager::getSingleton()
.getByName(
"Water/water_fp",
Ogre::RGN_AUTODETECT);
OgreAssert(water_vp != nullptr,
"FP failed");
pass->setGpuProgram(
Ogre::GPT_FRAGMENT_PROGRAM,
water_fp);
OgreAssert(water_fp->isSupported(),
"FP not supported");
Ogre::GpuProgramParametersSharedPtr paramsVP =
water_vp->getDefaultParameters();
paramsVP->setNamedAutoConstant(
"world",
Ogre::GpuProgramParameters::
ACT_WORLD_MATRIX);
paramsVP->setNamedAutoConstant(
"worldViewProj",
Ogre::GpuProgramParameters::
ACT_WORLDVIEWPROJ_MATRIX);
paramsVP->setNamedAutoConstant(
"textureProjMatrix",
Ogre::GpuProgramParameters::
ACT_TEXTURE_WORLDVIEWPROJ_MATRIX);
paramsVP->setNamedAutoConstant(
"eyePosition",
Ogre::GpuProgramParameters::
ACT_CAMERA_POSITION_OBJECT_SPACE);
paramsVP->setNamedAutoConstant(
"normalMatrix",
Ogre::GpuProgramParameters::
ACT_NORMAL_MATRIX);
paramsVP->setNamedAutoConstant(
"worldView",
Ogre::GpuProgramParameters::
ACT_WORLDVIEW_MATRIX);
paramsVP->setNamedAutoConstant(
"viewProj",
Ogre::GpuProgramParameters::
ACT_VIEWPROJ_MATRIX);
Ogre::GpuProgramParametersSharedPtr paramsFP =
water_fp->getDefaultParameters();
#endif
Ogre::TextureUnitState *texture_unit =
pass->createTextureUnitState();
texture_unit->setTextureName(
@@ -284,38 +221,7 @@ WaterModule::WaterModule(flecs::world &ecs)
Ogre::FT_MAG, Ogre::FO_LINEAR);
texture_unit2->setTextureFiltering(
Ogre::FT_MIP, Ogre::FO_LINEAR);
#if 0
bool success =
Ogre::RTShader::ShaderGenerator::getSingletonPtr()
->createShaderBasedTechnique(
*mat,
Ogre::MSN_DEFAULT,
Ogre::MSN_SHADERGEN);
OgreAssert(
success,
"createShaderBasedTechnique");
Ogre::RTShader::RenderState *renderState =
Ogre::RTShader::ShaderGenerator::
getSingletonPtr()
->getRenderState(
Ogre::MSN_SHADERGEN,
*mat,
0);
Ogre::RTShader::SubRenderState *perPixelLightModel =
Ogre::RTShader::ShaderGenerator::getSingletonPtr()
->createSubRenderState(
Ogre::RTShader::
SRS_PER_PIXEL_LIGHTING);
renderState->addTemplateSubRenderState(
perPixelLightModel);
#endif
}
#if 0
mat = Ogre::MaterialManager::getSingleton()
.getByName("Water/Above");
mat->load();
#endif
mat->load();
mat->setReceiveShadows(false);
/*
@@ -494,202 +400,37 @@ WaterModule::WaterModule(flecs::world &ecs)
"Water/Above");
})
.add(flecs::Singleton);
#if 0
ecs.component<WaterBody>().add(flecs::Singleton);
ecs.component<WaterBody>()
.on_add([this](WaterBody &body) {
#if 0
body.mShapeAabbMax = btVector3(0, 0, 0);
body.mShapeAabbMin = btVector3(0, 0, 0);
body.mSurface.clear();
body.mWaterBody = OGRE_NEW btPairCachingGhostObject();
createWaterShape(&body);
body.mWaterBody->setCollisionShape(body.mWaterShape);
btTransform bodyTransform;
bodyTransform.setIdentity();
body.mWaterBody->setWorldTransform(bodyTransform);
body.mWaterBody->setCollisionFlags(
body.mWaterBody->getCollisionFlags() |
btCollisionObject::CF_KINEMATIC_OBJECT |
btCollisionObject::CF_NO_CONTACT_RESPONSE);
body.mWaterBody->setActivationState(
DISABLE_DEACTIVATION);
const EngineData &eng = ECS::get<EngineData>();
const WaterSurface &water = ECS::get<WaterSurface>();
eng.mWorld->attachCollisionObject(body.mWaterBody,
water.mWaterEnt, 16,
0x7fffffff & ~2);
WaterPhysicsAction *action =
OGRE_NEW WaterPhysicsAction(body.mWaterBody);
body.action = action;
ECS::get()
.get<EngineData>()
.mWorld->getBtWorld()
->addAction(body.action);
#endif
ECS::get().add<WaterReady>();
})
.add(flecs::Singleton);
#endif
ecs.system<const EngineData, const Camera, WaterSurface>("UpdateWater")
ecs.system<const EngineData, const Camera, WaterSurface>(
"UpdateWaterPosition")
.kind(flecs::OnUpdate)
.with<WaterReady>()
.interval(0.3f)
.each([](const EngineData &eng, const Camera &camera,
WaterSurface &water) {
float delta = eng.delta;
Ogre::Vector3 mCameraPos =
camera.mCameraNode->_getDerivedPosition();
Ogre::Vector3 waterPos =
water.mWaterNode->_getDerivedPosition();
mCameraPos.y = 0;
waterPos.y = 0;
Ogre::Vector3 d = mCameraPos - waterPos;
// Ogre::Vector3 waterPosition = mCameraPos;
// mWaterNode->setPosition(waterPosition);
if (d.squaredLength() < 100.0f * 100.0f)
water.mWaterNode->translate(d * 3.0f * delta);
else
water.mWaterNode->translate(d);
// water.mWaterEnt->setVisible(false);
water.mViewports[0]->update();
water.mViewports[1]->update();
// water.mRenderTargetListener.mInDepth = true;
// water.mViewports[2]->update();
// water.mViewports[3]->update();
// water.mRenderTargetListener.mInDepth = false;
// water.mWaterEnt->setVisible(true);
ZoneScopedN("UpdateWaterPosition");
float delta = eng.delta;
Ogre::Vector3 mCameraPos =
camera.mCameraNode->_getDerivedPosition();
Ogre::Vector3 waterPos =
water.mWaterNode->_getDerivedPosition();
mCameraPos.y = 0;
waterPos.y = 0;
Ogre::Vector3 d = mCameraPos - waterPos;
if (d.squaredLength() < 100.0f * 100.0f)
water.mWaterNode->translate(d * 3.0f * delta);
else
water.mWaterNode->translate(d);
});
ecs.system<WaterSurface>("UpdateWater")
.kind(flecs::OnUpdate)
.with<WaterReady>()
.each([](WaterSurface &water) {
static int updateViewport = 0;
ZoneScopedN("UpdateWaterRender");
water.mViewports[updateViewport++]->update();
if (updateViewport > 1)
updateViewport = 0;
});
#if 0
ecs.system<const EngineData, const WaterSurface, WaterBody>(
"UpdateWaterBody")
.kind(flecs::OnUpdate)
.with<WaterReady>()
.each([this](const EngineData &eng, const WaterSurface &water,
WaterBody &body) {
int i;
#if 0
OgreAssert(body.mWaterBody, "Water not ready");
std::set<btCollisionObject *> currentOverlaps;
Ogre::Vector3 waterPos =
water.mWaterNode->_getDerivedPosition();
Ogre::Vector3 waterBodyPos = Ogre::Bullet::convert(
body.mWaterBody->getWorldTransform()
.getOrigin());
waterPos.y = 0;
waterBodyPos.y = 0;
Ogre::Vector3 d = waterPos - waterBodyPos;
d.y = 0;
if (d.squaredLength() > 10.0f * 10.0f)
body.mWaterBody->getWorldTransform().setOrigin(
Ogre::Bullet::convert(waterBodyPos +
d));
#endif
#if 0
btCompoundShape *mshape =
static_cast<btCompoundShape *>(
body.mWaterBody->getCollisionShape());
btDispatcher *dispatch =
eng.mWorld->getBtWorld()->getDispatcher();
btHashedOverlappingPairCache *cache =
body.mWaterBody->getOverlappingPairCache();
btBroadphasePairArray &collisionPairs =
cache->getOverlappingPairArray();
const int numObjects = collisionPairs.size();
std::cout << "numObjects: " << numObjects << "\n";
std::cout
<< "numObjects: "
<< body.mWaterBody->getOverlappingPairs().size()
<< "\n";
for (int i = 0; i < numObjects; i++) {
const btBroadphasePair &collisionPair =
collisionPairs[i];
}
#endif
#if 0
body.mShapeAabbMin =
body.mWaterBody->getWorldTransform()
.getOrigin() +
btVector3(-100, -100, -100);
body.mShapeAabbMax =
body.mWaterBody->getWorldTransform()
.getOrigin() +
btVector3(100, 100, 100);
#if 0
body.mWaterShape->getAabb(
body.mWaterBody->getWorldTransform(),
body.mShapeAabbMin, body.mShapeAabbMax);
#endif
std::cout << "manifolds: "
<< static_cast<WaterPhysicsAction *>(
body.action)
->mManifoldArray.size()
<< "\n";
for (int j = 0;
j < static_cast<WaterPhysicsAction *>(body.action)
->mManifoldArray.size();
j++) {
btPersistentManifold *manifold =
static_cast<WaterPhysicsAction *>(
body.action)
->mManifoldArray[j];
std::cout << "contacts: "
<< manifold->getNumContacts() << "\n";
if (manifold->getNumContacts() == 0)
continue;
const btCollisionObject *obj =
(manifold->getBody0() ==
body.mWaterBody) ?
manifold->getBody1() :
manifold->getBody0();
btCollisionObject *nobj =
const_cast<btCollisionObject *>(obj);
#if 0
if (obj->getCollisionFlags() &
btCollisionObject::CF_STATIC_OBJECT)
continue;
#endif
bool ok = false;
Ogre::Vector3 contactPosA, contactPosB;
float minDist = 0.0f;
for (int p = 0; p < manifold->getNumContacts();
p++) {
const btManifoldPoint &pt =
manifold->getContactPoint(p);
float dist = pt.getDistance();
if (dist < minDist) {
minDist = dist;
ok = true;
}
}
if (ok) {
currentOverlaps.insert(nobj);
if (body.mInWater.find(nobj) ==
body.mInWater.end()) {
/* new body */
body.mInWater.insert(nobj);
/* calculate proj surface */
body.mSurface[nobj] = 100.0f;
body.count++;
}
}
}
for (std::set<btCollisionObject *>::iterator it =
body.mInWater.begin();
it != body.mInWater.end();) {
btCollisionObject *obj = *it;
if (currentOverlaps.find(obj) ==
currentOverlaps.end()) {
/* remove body */
it = body.mInWater.erase(it);
body.mSurface.erase(obj);
body.count--;
} else
it++;
}
#endif
});
#endif
}
struct shapeParams {
float offsetX, offsetY, offsetZ;
@@ -698,38 +439,6 @@ struct shapeParams {
static struct shapeParams childShapes[] = {
{ 0.0f, 0.0f, 0.0f, 100.0f, 100.0f, 100.0f },
};
#if 0
void WaterModule::createWaterShape(WaterBody *water)
{
int i = 0;
btCompoundShape *shape = OGRE_NEW btCompoundShape();
shape->setMargin(0.2f);
{
btVector3 inertia(0, 0, 0);
std::vector<btScalar> masses;
btTransform principal;
principal.setIdentity();
for (i = 0; i < sizeof(childShapes) / sizeof(childShapes[0]);
i++) {
btTransform xform;
xform.setIdentity();
xform.setOrigin(btVector3(childShapes[i].offsetX,
childShapes[i].offsetY,
childShapes[i].offsetZ));
btBoxShape *box = OGRE_NEW btBoxShape(btVector3(
childShapes[i].boxX, childShapes[i].boxY,
childShapes[i].boxZ));
water->mChildShapes.push_back(box);
shape->addChildShape(xform, box);
masses.push_back(0);
}
shape->calculatePrincipalAxisTransform(masses.data(), principal,
inertia);
}
shape->recalculateLocalAabb();
water->mWaterShape = shape;
}
#endif
void WaterSurface::RenderTextureListener::preRenderTargetUpdate(
const Ogre::RenderTargetEvent &evt)
{
@@ -758,254 +467,4 @@ bool WaterSurface::RenderTextureListener::renderableQueued(
*ppTech = mSurface->mDepthTech;
return true;
}
#if 0
struct DeepPenetrationContactResultCallback : public btManifoldResult {
DeepPenetrationContactResultCallback(
const btCollisionObjectWrapper *body0Wrap,
const btCollisionObjectWrapper *body1Wrap)
: btManifoldResult(body0Wrap, body1Wrap)
, mPenetrationDistance(0)
, mOtherIndex(0)
{
}
float mPenetrationDistance;
int mOtherIndex;
btVector3 mNormal, mPoint;
void reset()
{
mPenetrationDistance = 0.0f;
}
bool hasHit()
{
return mPenetrationDistance < 0.0f;
}
virtual void addContactPoint(const btVector3 &normalOnBInWorld,
const btVector3 &pointInWorldOnB,
btScalar depth)
{
#ifdef VDEBUG
std::cout
<< "contact: " << Ogre::Bullet::convert(pointInWorldOnB)
<< " " << Ogre::Bullet::convert(normalOnBInWorld)
<< "\n";
#endif
if (mPenetrationDistance > depth) { // Has penetration?
const bool isSwapped =
m_manifoldPtr->getBody0() !=
m_body0Wrap->getCollisionObject();
mPenetrationDistance = depth;
mOtherIndex = isSwapped ? m_index0 : m_index1;
mPoint = isSwapped ? (pointInWorldOnB +
(normalOnBInWorld * depth)) :
pointInWorldOnB;
mNormal = isSwapped ? normalOnBInWorld * -1 :
normalOnBInWorld;
}
}
};
void WaterPhysicsAction::updateAction(btCollisionWorld *collisionWorld,
btScalar deltaTimeStep)
{
collisionWorld->updateSingleAabb(mWaterBody);
mWaterBody->getCollisionShape()->getAabb(
mWaterBody->getWorldTransform(), mShapeAabbMin, mShapeAabbMax);
btDispatcher *dispatch = collisionWorld->getDispatcher();
btHashedOverlappingPairCache *cache =
mWaterBody->getOverlappingPairCache();
btBroadphasePairArray &collisionPairs =
cache->getOverlappingPairArray();
btVector3 a, b;
collisionWorld->getBroadphase()->getAabb(
mWaterBody->getBroadphaseHandle(), a, b);
collisionWorld->getBroadphase()->setAabb(
mWaterBody->getBroadphaseHandle(), mShapeAabbMin, mShapeAabbMax,
dispatch);
btDispatcherInfo &dispatchInfo = collisionWorld->getDispatchInfo();
dispatch->dispatchAllCollisionPairs(cache, dispatchInfo, dispatch);
std::set<btCollisionObject *> currentOverlaps;
std::set<btCollisionObject *>::iterator it;
const int numObjects = collisionPairs.size();
#ifdef VDEBUG
std::cout << "collision pairs: " << numObjects << "\n";
std::cout << "MIN: " << Ogre::Bullet::convert(mShapeAabbMin) << "\n";
std::cout << "MAX: " << Ogre::Bullet::convert(mShapeAabbMax) << "\n";
std::cout << "MIN: " << Ogre::Bullet::convert(a) << "\n";
std::cout << "MAX: " << Ogre::Bullet::convert(b) << "\n";
#endif
std::set<const btCollisionObject *> mCurrentInWater;
/* perform narrow phase */
for (int i = 0; i < numObjects; i++) {
int j;
const btBroadphasePair *collisionPairPtr =
collisionWorld->getBroadphase()
->getOverlappingPairCache()
->findPair(collisionPairs[i].m_pProxy0,
collisionPairs[i].m_pProxy1);
if (!collisionPairPtr)
continue;
#ifndef USE_MANIFOLD
const btBroadphasePair &collisionPair = *collisionPairPtr;
const btCollisionObject *objA =
static_cast<btCollisionObject *>(
collisionPair.m_pProxy0->m_clientObject);
const btCollisionObject *objB =
static_cast<btCollisionObject *>(
collisionPair.m_pProxy1->m_clientObject);
#ifdef VDEBUG
std::cout << "bodies: " << objA << " " << objB << "\n";
std::cout << "bodies: " << objA->getCollisionShape()->getName()
<< " " << objB->getCollisionShape()->getName()
<< "\n";
std::cout << "pair: " << i << " " << collisionPair.m_algorithm
<< "\n";
#endif
const btCollisionObject *me, *other;
if (objA == static_cast<btCollisionObject *>(mWaterBody)) {
me = objA;
other = objB;
} else {
me = objB;
other = objA;
}
const btCollisionShape *my_shape = me->getCollisionShape();
const btCollisionShape *other_shape =
other->getCollisionShape();
btCollisionObjectWrapper obA(NULL, my_shape, mWaterBody,
mWaterBody->getWorldTransform(),
-1, j);
btCollisionObjectWrapper obB(NULL, other_shape, other,
other->getWorldTransform(), -1, 0);
btCollisionAlgorithm *algorithm = dispatch->findAlgorithm(
&obA, &obB, NULL, BT_CONTACT_POINT_ALGORITHMS);
#else
btCollisionAlgorithm *algorithm = collisionPairPtr->m_algorithm;
OgreAssert(algorithm, "No algorithm found");
#endif
#ifdef VDEBUG
std::cout << "algorithm: " << algorithm << "\n";
#endif
#ifndef USE_MANIFOLD
DeepPenetrationContactResultCallback contactPointResult(&obA,
&obB);
#ifdef VDEBUG
std::cout << "process collision\n";
#endif
algorithm->processCollision(&obA, &obB,
collisionWorld->getDispatchInfo(),
&contactPointResult);
algorithm->~btCollisionAlgorithm();
dispatch->freeCollisionAlgorithm(algorithm);
if (contactPointResult.hasHit()) {
mCurrentInWater.insert(other);
mInWater.insert(other);
}
#ifdef VDEBUG
std::cout << "process collision done\n";
#endif
#else
if (collisionPairPtr->m_algorithm)
collisionPairPtr->m_algorithm->getAllContactManifolds(
mManifoldArray);
std::cout << "action: manifold: " << mManifoldArray.size()
<< "\n";
#endif
#if 0
std::cout << " "
<< Ogre::Bullet::convert(
collisionPair.m_pProxy0->m_aabbMin)
<< " -> "
<< Ogre::Bullet::convert(
collisionPair.m_pProxy0->m_aabbMax)
<< " VS "
<< Ogre::Bullet::convert(
collisionPair.m_pProxy1->m_aabbMin)
<< " -> "
<< Ogre::Bullet::convert(
collisionPair.m_pProxy1->m_aabbMax)
<< "\n";
std::cout << "group: 0 " << std::dec
<< collisionPair.m_pProxy0->m_collisionFilterGroup
<< "mask: " << std::hex
<< collisionPair.m_pProxy0->m_collisionFilterMask
<< "\n";
std::cout << "group: 1 " << std::dec
<< collisionPair.m_pProxy1->m_collisionFilterGroup
<< "mask: " << std::hex
<< collisionPair.m_pProxy1->m_collisionFilterMask
<< std::dec << "\n";
if (collisionPair.m_algorithm)
collisionPair.m_algorithm->getAllContactManifolds(
mManifoldArray);
std::cout << "action: manifold: " << mManifoldArray.size()
<< "\n";
#endif
#ifdef USE_MANIFOLD
for (int j = 0; j < mManifoldArray.size(); j++) {
btPersistentManifold *manifold = mManifoldArray[j];
std::cout << "contacts: " << manifold->getNumContacts()
<< "\n";
if (manifold->getNumContacts() == 0)
continue;
const btCollisionObject *obj =
(manifold->getBody0() == mWaterBody) ?
manifold->getBody1() :
manifold->getBody0();
btCollisionObject *nobj =
const_cast<btCollisionObject *>(obj);
#if 1
if (obj->getCollisionFlags() &
btCollisionObject::CF_STATIC_OBJECT)
continue;
#endif
bool ok = false;
Ogre::Vector3 contactPosA, contactPosB;
float minDist = 0.0f;
for (int p = 0; p < manifold->getNumContacts(); p++) {
const btManifoldPoint &pt =
manifold->getContactPoint(p);
float dist = pt.getDistance();
if (dist < minDist) {
minDist = dist;
ok = true;
}
}
if (ok) {
currentOverlaps.insert(nobj);
if (mInWater.find(nobj) == mInWater.end()) {
/* new body */
mInWater.insert(nobj);
}
}
}
#endif
}
for (std::set<const btCollisionObject *>::iterator it =
mInWater.begin();
it != mInWater.end();) {
const btCollisionObject *obj = *it;
if (mCurrentInWater.find(obj) == mCurrentInWater.end()) {
/* remove body */
it = mInWater.erase(it);
} else
it++;
}
#ifdef VDEBUG
std::cout << "water count: " << mInWater.size() << "\n";
#endif
}
void WaterPhysicsAction::debugDraw(btIDebugDraw *debugDrawer)
{
}
void WaterPhysicsAction::setupBody()
{
}
#endif
#if 0
bool WaterBody::isInWater(const btCollisionObject *body) const
{
return static_cast<WaterPhysicsAction *>(action)->isInWater(body);
}
#endif
}

View File

@@ -3,6 +3,7 @@ find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain Overlay CONFIG
find_package(Bullet REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(OgreProcedural REQUIRED CONFIG)
find_package(flecs REQUIRED CONFIG)
add_library(items STATIC items.cpp harbour.cpp temple.cpp town.cpp)
target_include_directories(items PUBLIC . ${CMAKE_SOURCE_DIR}/src/FastNoiseLite)
target_link_libraries(items PRIVATE
@@ -13,4 +14,5 @@ target_link_libraries(items PRIVATE
OgreBites
editor
physics
Tracy::TracyClient
)

View File

@@ -12,6 +12,7 @@
#include "StaticGeometryModule.h"
#include "items.h"
#include "harbour.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
namespace Items
@@ -19,6 +20,7 @@ namespace Items
/* This is editor function */
static bool findPierOffset(float &offset)
{
ZoneScoped;
Ogre::Vector3 basePos =
ECS::get<EditorGizmo>().sceneNode->_getDerivedPosition();
Ogre::Quaternion baseRot =
@@ -41,7 +43,8 @@ static bool findPierOffset(float &offset)
static bool findPierOffsetAndLengthAndDepth(float &offset, float &length,
float &depth)
{
if (!findPierOffset(offset))
ZoneScoped;
if (!findPierOffset(offset))
return false;
Ogre::Vector3 basePos =
ECS::get<EditorGizmo>().sceneNode->_getDerivedPosition();
@@ -66,7 +69,8 @@ static bool findPierOffsetAndLengthAndDepth(float &offset, float &length,
}
static void findPierHeight(float maxLength, float &height)
{
Ogre::Vector3 basePos =
ZoneScoped;
Ogre::Vector3 basePos =
ECS::get<EditorGizmo>().sceneNode->_getDerivedPosition();
Ogre::Quaternion baseRot =
ECS::get<EditorGizmo>().sceneNode->_getDerivedOrientation();
@@ -86,7 +90,8 @@ static void findPierHeight(float maxLength, float &height)
}
static void findPierPath(float pathLength, std::vector<Ogre::Vector3> &path)
{
float minHeight = 0.2f;
ZoneScoped;
float minHeight = 0.2f;
int i;
Ogre::Vector3 basePos =
ECS::get<EditorGizmo>().sceneNode->_getDerivedPosition();
@@ -146,7 +151,8 @@ static void findPierPath(float pathLength, std::vector<Ogre::Vector3> &path)
}
bool editHarbourDistrict(nlohmann::json &jitem)
{
float plazzaRadius = 5.0f;
ZoneScoped;
float plazzaRadius = 5.0f;
float plazzaHeight = 0.2f;
float plazzaElevation = 0.0f;
float centerOffset = 0.0f;
@@ -280,7 +286,8 @@ bool editHarbourDistrict(nlohmann::json &jitem)
}
void createHarbourPopup(const std::pair<flecs::entity, Ogre::String> item)
{
bool lighthouse = false;
ZoneScoped;
bool lighthouse = false;
float lighthouseDistance = 0.0f;
float lighthouseAngle = 0.0f;
float centerOffset = 0.0f;
@@ -328,7 +335,8 @@ void createHarbourPopup(const std::pair<flecs::entity, Ogre::String> item)
}
void createHarbourItem()
{
Ogre::Vector3 itemPosition =
ZoneScoped;
Ogre::Vector3 itemPosition =
ECS::get<EditorGizmo>().sceneNode->_getDerivedPosition();
Ogre::Quaternion itemOrientation =
ECS::get<EditorGizmo>().sceneNode->_getDerivedOrientation();
@@ -364,7 +372,8 @@ void createHarbourItem()
}
void createHarbourMenu()
{
if (ImGui::MenuItem("Create"))
ZoneScoped;
if (ImGui::MenuItem("Create"))
createHarbourItem();
}
}
@@ -373,7 +382,8 @@ namespace Geometry
void createBridge(flecs::entity e, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
int i;
ZoneScoped;
int i;
Procedural::TriangleBuffer tb;
Ogre::MaterialPtr harbourMaterial;
harbourMaterial = Ogre::MaterialManager::getSingleton().getByName(
@@ -545,7 +555,8 @@ void createBridge(flecs::entity e, Ogre::SceneNode *sceneNode,
void createPier(flecs::entity e, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
Ogre::MaterialPtr harbourMaterial;
ZoneScoped;
Ogre::MaterialPtr harbourMaterial;
harbourMaterial = Ogre::MaterialManager::getSingleton().getByName(
"proceduralMaterialHarbour" +
Ogre::StringConverter::toString(e.id()));
@@ -705,7 +716,8 @@ void createPier(flecs::entity e, Ogre::SceneNode *sceneNode,
void createPlazza(flecs::entity e, const nlohmann::json &district,
Ogre::SceneNode *sceneNode, Ogre::StaticGeometry *geo)
{
Ogre::MaterialPtr harbourMaterial;
ZoneScoped;
Ogre::MaterialPtr harbourMaterial;
harbourMaterial = Ogre::MaterialManager::getSingleton().getByName(
"proceduralMaterialHarbour" +
Ogre::StringConverter::toString(e.id()));
@@ -784,7 +796,8 @@ void createPlazza(flecs::entity e, const nlohmann::json &district,
void createBuildings(flecs::entity e, const nlohmann::json &district,
Ogre::SceneNode *sceneNode, Ogre::StaticGeometry *geo)
{
Ogre::MaterialPtr harbourMaterial;
ZoneScoped;
Ogre::MaterialPtr harbourMaterial;
harbourMaterial = Ogre::MaterialManager::getSingleton().getByName(
"proceduralMaterialHarbour" +
Ogre::StringConverter::toString(e.id()));
@@ -865,7 +878,8 @@ void createBuildings(flecs::entity e, const nlohmann::json &district,
void createHarbour(flecs::entity e, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
std::cout << "createHarbour " << e.id() << std::endl;
ZoneScoped;
std::cout << "createHarbour " << e.id() << std::endl;
Ogre::MaterialPtr harbourMaterial;
harbourMaterial = Ogre::MaterialManager::getSingleton().getByName(
"proceduralMaterialHarbour" +
@@ -980,4 +994,4 @@ void createHarbour(flecs::entity e, Ogre::SceneNode *sceneNode,
geo->build();
}
}
}
}

View File

@@ -18,12 +18,14 @@
#include "temple.h"
#include "town.h"
#include "items.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
namespace Items
{
void runScriptsForAllTowns()
{
ZoneScoped;
std::pair<flecs::entity, Ogre::String> selected_item;
std::list<std::pair<flecs::entity, Ogre::String> > items;
StaticGeometryModule::getItemsProperties(&items);
@@ -40,7 +42,8 @@ void runScriptsForAllTowns()
}
void showItemPopup(const std::pair<flecs::entity, Ogre::String> &item)
{
Ogre::String popupLabel =
ZoneScoped;
Ogre::String popupLabel =
"EditPopup" + Ogre::StringConverter::toString(item.first.id());
if (ImGui::BeginPopup(popupLabel.c_str())) {
Ogre::String prop =
@@ -160,7 +163,8 @@ void showItemPopup(const std::pair<flecs::entity, Ogre::String> &item)
}
void showItemButtons(const std::pair<flecs::entity, Ogre::String> &item)
{
ImGui::SameLine();
ZoneScoped;
ImGui::SameLine();
Ogre::String upd_label =
"Update Height##" +
Ogre::StringConverter::toString(item.first.id());
@@ -190,7 +194,8 @@ void showItemButtons(const std::pair<flecs::entity, Ogre::String> &item)
}
void createItemsMenu()
{
if (ImGui::BeginMenu("Harbour")) {
ZoneScoped;
if (ImGui::BeginMenu("Harbour")) {
Items::createHarbourMenu();
ImGui::EndMenu();
}
@@ -209,6 +214,7 @@ namespace Geometry
{
void setupLods(Ogre::LodConfig &config)
{
ZoneScoped;
int count = 0;
// config.advanced.useCompression = false;
config.advanced.useVertexNormals = true;
@@ -246,7 +252,8 @@ void setupLods(Ogre::LodConfig &config)
Ogre::StaticGeometry *createStaticGeometry(flecs::entity e)
{
Ogre::String props = e.get<TerrainItem>().properties;
ZoneScoped;
Ogre::String props = e.get<TerrainItem>().properties;
nlohmann::json jp = nlohmann::json::parse(props);
if (jp.find("type") != jp.end()) {
Ogre::String itemType = jp["type"].get<Ogre::String>();
@@ -264,7 +271,8 @@ Ogre::StaticGeometry *createStaticGeometry(flecs::entity e)
}
void createItemGeometry(flecs::entity e)
{
OgreAssert(!e.has<TerrainItemNode>(), "Geometry already created");
ZoneScoped;
OgreAssert(!e.has<TerrainItemNode>(), "Geometry already created");
std::cout << "creating geometry for item: " << e.id() << std::endl;
Ogre::String props = e.get<TerrainItem>().properties;
nlohmann::json jp = nlohmann::json::parse(props);
@@ -322,7 +330,8 @@ void createItemGeometry(flecs::entity e)
void destroyItemGeometry(flecs::entity e)
{
OgreAssert(e.has<TerrainItemNode>(), "No geometry created");
ZoneScoped;
OgreAssert(e.has<TerrainItemNode>(), "No geometry created");
#if 0
ECS::get<EngineData>().mScnMgr->destroyStaticGeometry()
e.get<TerrainItemNode>().geo->destroy();
@@ -333,6 +342,7 @@ void destroyItemGeometry(flecs::entity e)
}
void updateItemGeometry(flecs::entity e)
{
ZoneScoped;
OgreAssert(e.has<TerrainItem>(), "not terrain item");
if (e.has<TerrainItemNode>())
destroyItemGeometry(e);
@@ -344,7 +354,8 @@ flecs::entity createMeshGeometry(const Ogre::String &meshName,
Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().load(
ZoneScoped;
Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().load(
meshName,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
OgreAssert(mesh, "mesh " + meshName + " not found");

View File

@@ -14,12 +14,14 @@
#include "PhysicsModule.h"
#include "items.h"
#include "temple.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
namespace Items
{
void createTempleItem()
{
ZoneScoped;
Ogre::Vector3 itemPosition =
ECS::get<EditorGizmo>().sceneNode->_getDerivedPosition();
Ogre::Quaternion itemOrientation =
@@ -38,12 +40,14 @@ void createTempleItem()
}
void createTempleMenu()
{
if (ImGui::MenuItem("Create"))
ZoneScoped;
if (ImGui::MenuItem("Create"))
createTempleItem();
}
void createTemplePopup(const std::pair<flecs::entity, Ogre::String> item)
{
float size = 40.0f;
ZoneScoped;
float size = 40.0f;
float pillarRadius = 0.5f;
float pillarHeight = 16.0f;
int pillarCount = 20;
@@ -88,7 +92,8 @@ namespace Geometry
void createTemple(flecs::entity e, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
Ogre::String props = e.get<TerrainItem>().properties;
ZoneScoped;
Ogre::String props = e.get<TerrainItem>().properties;
nlohmann::json jp = nlohmann::json::parse(props);
float size = 20.0f;
int pillarCount = 4;

View File

@@ -22,6 +22,7 @@
#include "CharacterAIModule.h"
#include "items.h"
#include "town.h"
#include <tracy/Tracy.hpp>
/*
* TODO: Create doors and handle them via script.
@@ -32,6 +33,7 @@
void serializeMesh(Ogre::MeshPtr mesh, const Ogre::String &meshName)
{
ZoneScoped;
Ogre::MeshSerializer meshSerializer;
meshSerializer.exportMesh(mesh, meshName,
@@ -43,12 +45,14 @@ namespace Items
{
int64_t makeCellKey(int kx, int ky, int kz)
{
ZoneScoped;
int64_t key =
(int64_t)kx + 1024 * (int64_t)ky + 1024 * 1024 * (int64_t)kz;
return key;
}
int64_t makeCellKey(const nlohmann::json &cell)
{
ZoneScoped;
int64_t kx = cell["x"].get<int>();
int64_t ky = cell["y"].get<int>();
int64_t kz = cell["z"].get<int>();
@@ -59,6 +63,7 @@ void displayGrid(flecs::entity e, nlohmann::json &lot, int index,
Ogre::SceneNode *sceneNode, const Ogre::Vector3 &localPosition,
const Ogre::Quaternion &localRotation)
{
ZoneScoped;
if (lot.find("edited") == lot.end()) {
lot["edited"] = true;
int width = lot["width"].get<int>();
@@ -99,7 +104,8 @@ void displayGrid(flecs::entity e, nlohmann::json &lot, int index,
}
void createTownItem()
{
Ogre::Vector3 itemPosition =
ZoneScoped;
Ogre::Vector3 itemPosition =
ECS::get<EditorGizmo>().sceneNode->_getDerivedPosition();
Ogre::Quaternion itemOrientation =
ECS::get<EditorGizmo>().sceneNode->_getDerivedOrientation();
@@ -113,11 +119,13 @@ void createTownItem()
}
void createTownMenu()
{
if (ImGui::MenuItem("Create"))
ZoneScoped;
if (ImGui::MenuItem("Create"))
createTownItem();
}
bool editCell(const Ogre::String &cellLabel, nlohmann::json &cell)
{
ZoneScoped;
bool changed = false;
auto checkboxBit = [cellLabel, &changed](Ogre::String label,
nlohmann::json &cell,
@@ -190,6 +198,7 @@ struct CellsScript {
, cells(lot["cells"])
, fucells(lot["furniture_cells"])
{
ZoneScoped;
noise.SetNoiseType(FastNoiseLite::NoiseType_OpenSimplex2);
noise.SetSeed(310);
noise.SetFrequency(0.01f);
@@ -484,6 +493,7 @@ struct CellsScript {
}
static std::string trim(const std::string &str)
{
ZoneScoped;
// Find the first character that is not a whitespace character
const std::string ws =
" \t\n\r\f\v"; // Common whitespace characters
@@ -503,6 +513,7 @@ struct CellsScript {
static std::vector<std::string> split_and_trim(const std::string &s,
char delimiter)
{
ZoneScoped;
std::vector<std::string> tokens;
std::string token;
std::stringstream ss(s); // Use stringstream to parse the string
@@ -520,16 +531,19 @@ struct CellsScript {
}
void buildCellIndex()
{
ZoneScoped;
for (auto &cell : cells)
cell["id"] = makeCellKey(cell);
}
void buildFCellIndex()
{
ZoneScoped;
for (auto &fucell : fucells)
fucell["id"] = makeCellKey(fucell);
}
int findCell(int X, int Y, int Z)
{
ZoneScoped;
int64_t key = makeCellKey(X, Y, Z);
int result = -1;
int i;
@@ -544,6 +558,7 @@ struct CellsScript {
}
int findFCell(int X, int Y, int Z)
{
ZoneScoped;
int64_t key = makeCellKey(X, Y, Z);
int result = -1;
int i;
@@ -558,6 +573,7 @@ struct CellsScript {
}
void bitCmd(const Ogre::String &cmd, const Ogre::String &bit)
{
ZoneScoped;
uint64_t flags = 0;
std::vector<Ogre::String> bitconv = {
"floor", "ceiling", "wallx-", "wallx+",
@@ -589,6 +605,7 @@ struct CellsScript {
}
bool isBit(const Ogre::String &bit)
{
ZoneScoped;
uint64_t flags = 0;
std::vector<Ogre::String> bitconv = {
"floor", "ceiling", "wallx-", "wallx+",
@@ -616,6 +633,7 @@ struct CellsScript {
}
static bool isBit(const nlohmann::json &cell, const Ogre::String &bit)
{
ZoneScoped;
uint64_t flags = 0;
std::vector<Ogre::String> bitconv = {
"floor", "ceiling", "wallx-", "wallx+",
@@ -641,6 +659,7 @@ struct CellsScript {
}
void cell(uint64_t flags)
{
ZoneScoped;
if (findCell(currentX, currentY, currentZ) == -1) {
nlohmann::json cell;
buildCellIndex();
@@ -658,12 +677,14 @@ struct CellsScript {
}
void clear()
{
ZoneScoped;
int index = findCell(currentX, currentY, currentZ);
if (index >= 0)
cells.erase(index);
}
void clear_area(int minX, int minZ, int sizeX, int sizeZ)
{
ZoneScoped;
int i, j;
for (i = 0; i < sizeZ; i++)
for (j = 0; j < sizeX; j++) {
@@ -674,12 +695,14 @@ struct CellsScript {
}
void clear_furniture()
{
ZoneScoped;
int index = findFCell(currentX, currentY, currentZ);
if (index >= 0)
fucells.erase(index);
}
void clear_furniture_area(int minX, int minZ, int sizeX, int sizeZ)
{
ZoneScoped;
int i, j;
for (i = 0; i < sizeZ; i++)
for (j = 0; j < sizeX; j++) {
@@ -690,6 +713,7 @@ struct CellsScript {
}
int room(int minX, int minZ, int sizeX, int sizeZ)
{
ZoneScoped;
int i, j, ret;
clear_area(minX, minZ, sizeX, sizeZ);
for (i = 0; i < sizeZ; i++)
@@ -725,6 +749,7 @@ struct CellsScript {
int roof(int minX, int minZ, int sizeX, int sizeZ, float height,
int type)
{
ZoneScoped;
nlohmann::json roofs = nlohmann::json::array();
if (lot.find("roofs") != lot.end())
roofs = lot["roofs"];
@@ -747,11 +772,13 @@ struct CellsScript {
}
void clear_roofs()
{
ZoneScoped;
lot["roofs"] = nlohmann::json::array();
}
void fcell(const std::vector<Ogre::String> &tags,
nlohmann::json furniture, int rotation)
{
ZoneScoped;
nlohmann::json jtags = nlohmann::json::array();
for (const auto &tag : tags)
jtags.push_back(tag);
@@ -778,6 +805,7 @@ struct CellsScript {
nlohmann::json select_furniture(const std::vector<Ogre::String> &tags,
const std::vector<Ogre::String> &notags)
{
ZoneScoped;
nlohmann::json adata = nlohmann::json::array();
ECS::get().query_builder<const FurnitureItem>().build().each(
[&](flecs::entity e, const FurnitureItem &item) {
@@ -826,6 +854,7 @@ struct CellsScript {
}
bool place_furniture(int room, Ogre::String tags)
{
ZoneScoped;
bool ret = true;
std::vector<Ogre::String> atags = split_and_trim(tags, ',');
int i, j;
@@ -1025,6 +1054,7 @@ struct CellsScript {
std::vector<std::pair<int, int> > roomEdge(int room)
{
ZoneScoped;
std::vector<std::pair<int, int> > seg;
std::set<std::pair<int, int> > seen;
int i;
@@ -1075,12 +1105,14 @@ out:
}
bool adjacent(std::pair<int, int> c1, std::pair<int, int> c2)
{
ZoneScoped;
int dx = std::abs(c1.first - c2.first);
int dz = std::abs(c1.second - c2.second);
return (dx == 0 && dz == 1) || (dx == 1 && dz == 0);
}
void dumpSeg(const std::vector<std::pair<int, int> > &seg)
{
ZoneScoped;
int count = 0;
for (auto &cell : seg) {
std::cout << count << " " << cell.first << " "
@@ -1092,6 +1124,7 @@ out:
std::vector<std::pair<int, int> > &ret1,
std::vector<std::pair<int, int> > &ret2)
{
ZoneScoped;
std::vector<std::pair<int, int> > room_seg = roomEdge(room);
std::vector<std::pair<int, int> > other_seg = roomEdge(other);
std::vector<std::pair<int, int> > ret;
@@ -1109,6 +1142,7 @@ out:
}
void createExit0(int room)
{
ZoneScoped;
int posX = rooms[room].minX;
int posZ = rooms[room].minZ;
int i;
@@ -1137,6 +1171,7 @@ out:
}
void createExit1(int room)
{
ZoneScoped;
int posX = rooms[room].minX;
int posZ = rooms[room].minZ + rooms[room].sizeZ - 1;
int i;
@@ -1165,6 +1200,7 @@ out:
}
void createExit2(int room)
{
ZoneScoped;
int posX = rooms[room].minX + rooms[room].sizeX - 1;
int posZ = rooms[room].minZ;
int i;
@@ -1193,6 +1229,7 @@ out:
}
void createExit3(int room)
{
ZoneScoped;
int posX = rooms[room].minX;
int posZ = rooms[room].minZ;
int i;
@@ -1221,6 +1258,7 @@ out:
}
void createWindows(int room)
{
ZoneScoped;
int posX = rooms[room].minX;
int posZ = rooms[room].minZ;
int i;
@@ -1273,6 +1311,7 @@ out:
}
void createExterior()
{
ZoneScoped;
int room;
for (room = 0; room < rooms.size(); room++) {
int posX = rooms[room].minX;
@@ -1340,6 +1379,7 @@ out:
}
void connectRooms(int r1, int r2)
{
ZoneScoped;
std::vector<std::pair<int, int> > seg1, seg2;
int index = 0;
adjacentCells(r1, r2, seg1, seg2);
@@ -1459,10 +1499,12 @@ out:
}
virtual ~CellsScript()
{
ZoneScoped;
lua_close(L);
}
void run()
{
ZoneScoped;
int result = luaL_dostring(L, cellScript.c_str());
if (result != LUA_OK) {
std::cerr << "Lua script execution failed: "
@@ -1474,6 +1516,7 @@ out:
};
bool editRoofs(const Ogre::String &lotLabel, nlohmann::json &lot)
{
ZoneScoped;
bool changed = false;
static int roofPosition[3] = { 0, 0 };
static int roofSize[2] = { 1, 1 };
@@ -1624,6 +1667,7 @@ bool editRoofs(const Ogre::String &lotLabel, nlohmann::json &lot)
}
bool editLot(const Ogre::String &lotLabel, nlohmann::json &lot)
{
ZoneScoped;
bool changed = false;
float angle = lot["angle"].get<float>();
if (ImGui::SliderFloat(("Lot Angle##" + lotLabel).c_str(), &angle,
@@ -1793,6 +1837,7 @@ bool editLot(const Ogre::String &lotLabel, nlohmann::json &lot)
}
void commandEraseLot(nlohmann::json &district, int lotIndex)
{
ZoneScoped;
nlohmann::json lots = nlohmann::json::array();
for (const auto &lot : district["lots"])
lots.push_back(lot);
@@ -1801,6 +1846,7 @@ void commandEraseLot(nlohmann::json &district, int lotIndex)
}
void cleanupLot(nlohmann::json &lot)
{
ZoneScoped;
lot.erase("cells");
lot.erase("furniture_cells");
lot.erase("roofs");
@@ -1808,6 +1854,7 @@ void cleanupLot(nlohmann::json &lot)
void addLotTemplate(nlohmann::json &district, nlohmann::json &lotTemplates,
int lotIndex)
{
ZoneScoped;
if (district.find("lots") != district.end()) {
nlohmann::json &lots = district["lots"];
nlohmann::json lotTemplate = lots[lotIndex];
@@ -1819,6 +1866,7 @@ void addLotFromTemplate(nlohmann::json &district,
const nlohmann::json &lotTemplates,
int lotTemplateIndex)
{
ZoneScoped;
nlohmann::json lots = nlohmann::json::array();
OgreAssert(lotTemplateIndex < lotTemplates.size() &&
lotTemplates.size() > 0,
@@ -1854,6 +1902,7 @@ void addLotFromTemplate(nlohmann::json &district,
void addLot(nlohmann::json &district, float angle, int width, int depth,
float elevation, const Ogre::String &script)
{
ZoneScoped;
nlohmann::json lots = nlohmann::json::array();
for (const auto &lot : district["lots"])
lots.push_back(lot);
@@ -1872,6 +1921,7 @@ void addLot(nlohmann::json &district, float angle, int width, int depth,
bool editDistrict(const Ogre::String &districtLabel, nlohmann::json &district,
nlohmann::json &lotTemplates)
{
ZoneScoped;
bool changed = false;
nlohmann::json lots = nlohmann::json::array();
for (const auto &lot : district["lots"])
@@ -1972,6 +2022,7 @@ bool editDistrict(const Ogre::String &districtLabel, nlohmann::json &district,
}
bool editColorRects(nlohmann::json &rects)
{
ZoneScoped;
bool changed = false;
ImGui::Text("Color Rects");
for (auto it = rects.begin(); it != rects.end(); it++) {
@@ -2071,6 +2122,7 @@ bool editColorRects(nlohmann::json &rects)
}
void runAllScriptsForTown(flecs::entity e)
{
ZoneScoped;
Ogre::String prop = StaticGeometryModule::getItemProperties(e);
nlohmann::json j = nlohmann::json::parse(prop);
auto &districts = j["districts"];
@@ -2092,6 +2144,7 @@ void runAllScriptsForTown(flecs::entity e)
}
bool editNPCs(nlohmann::json &npcs)
{
ZoneScoped;
bool changed = false;
ImGui::Text("NPC");
int id = 0;
@@ -2220,6 +2273,7 @@ bool editNPCs(nlohmann::json &npcs)
}
void createTownPopup(const std::pair<flecs::entity, Ogre::String> item)
{
ZoneScoped;
Ogre::String prop = StaticGeometryModule::getItemProperties(item.first);
Ogre::Vector3 townPosition;
Ogre::Quaternion townRotation;
@@ -2340,6 +2394,7 @@ struct ProcessCells {
std::vector<struct BitSet> bits_int_corners2;
ProcessCells()
{
ZoneScoped;
bits_ext_corners = {
{ 4 | 16, // external wall
4.0f,
@@ -2954,6 +3009,7 @@ struct ProcessCells {
void clampUV(flecs::entity e, Procedural::TriangleBuffer &tb,
const Ogre::String &rectKey)
{
ZoneScoped;
Ogre::String props = e.get<TerrainItem>().properties;
nlohmann::json jp = nlohmann::json::parse(props);
nlohmann::json colorRects = nlohmann::json::object();
@@ -2978,6 +3034,7 @@ void clampUV(flecs::entity e, Procedural::TriangleBuffer &tb,
}
Ogre::MaterialPtr createTownMaterial(flecs::entity e, bool force)
{
ZoneScoped;
Ogre::MaterialPtr townMaterial;
Ogre::String props = e.get<TerrainItem>().properties;
nlohmann::json jp = nlohmann::json::parse(props);
@@ -3100,6 +3157,7 @@ Ogre::MaterialPtr createTownMaterial(flecs::entity e, bool force)
}
void createTownWindows(flecs::entity e)
{
ZoneScoped;
Ogre::MeshPtr frame1mesh =
Ogre::MeshManager::getSingleton().getByName("window-frame1");
if (!frame1mesh) {
@@ -3195,6 +3253,7 @@ void createTownWindows(flecs::entity e)
}
void createTownDoors(flecs::entity e)
{
ZoneScoped;
Ogre::MeshPtr extframe1mesh =
Ogre::MeshManager::getSingleton().getByName(
"external-door-frame1");
@@ -3334,6 +3393,7 @@ struct TownTask {
const Ogre::Vector3 &position,
const Ogre::Quaternion &orientation)
{
ZoneScoped;
Ogre::Entity *ent =
ECS::get<EngineData>().mScnMgr->createEntity(mesh);
ent->setMaterial(material);
@@ -3344,6 +3404,7 @@ struct TownTask {
const Ogre::Vector3 &position,
const Ogre::Quaternion &orientation)
{
ZoneScoped;
JPH::ShapeRefC shape =
JoltPhysicsWrapper::getSingleton().createMeshShape(
mesh);
@@ -3363,6 +3424,7 @@ struct TownPlazza : TownTask {
int index, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
ZoneScopedN("createTownPlazza");
struct QueueData {
Procedural::TriangleBuffer tb;
Ogre::Vector3 position;
@@ -3409,6 +3471,7 @@ struct TownPlazza : TownTask {
queueData,
localPosition,
localRotation]() {
ZoneScoped;
const nlohmann::json &jp = queueData->jp;
float radius = 5.0f;
float height = 0.2f;
@@ -3460,6 +3523,8 @@ struct TownPlazza : TownTask {
.getWorkQueue()
->addMainThreadTask([queueData, localPosition,
localRotation]() {
ZoneScopedN(
"createTownPlazza::MainThread");
Ogre::Vector3 worldPlazzaCenter =
queueData->position +
localPosition;
@@ -3487,10 +3552,12 @@ struct TownPlazza : TownTask {
int index, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
ZoneScoped;
createTownPlazza(e, jdistrict, index, sceneNode, geo);
}
void wait()
{
ZoneScoped;
if (townPlazzaComplete.valid() &&
townPlazzaComplete.wait_for(std::chrono::seconds(0)) !=
std::future_status::ready)
@@ -3504,6 +3571,7 @@ struct TownLots : TownTask {
int index, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
ZoneScopedN("createTownLots");
struct QueueData {
Procedural::TriangleBuffer tb;
Ogre::Vector3 position;
@@ -3606,6 +3674,7 @@ struct TownLots : TownTask {
Ogre::Root::getSingleton().getWorkQueue()->addTask([radius,
baseHeight,
promise]() {
ZoneScoped;
std::cout << "lots count: " << lots.size() << std::endl;
OgreAssert(lots.size() > 0, "bad lots count");
for (auto &lot : lots) {
@@ -3647,6 +3716,8 @@ struct TownLots : TownTask {
Ogre::Root::getSingleton()
.getWorkQueue()
->addMainThreadTask([promise, radius]() {
ZoneScopedN(
"createTownLots::MainThread");
float distance = radius;
for (auto &lot : lots) {
std::cout
@@ -3699,10 +3770,12 @@ struct TownLots : TownTask {
int index, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
ZoneScoped;
createTownLots(e, jdistrict, index, sceneNode, geo);
}
void wait()
{
ZoneScoped;
if (townLotsComplete.valid() &&
townLotsComplete.wait_for(std::chrono::seconds(0)) !=
std::future_status::ready)
@@ -3715,6 +3788,7 @@ struct TownCells : TownTask {
int index, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
ZoneScoped;
struct QueueData {
Procedural::TriangleBuffer tb;
Ogre::Vector3 position;
@@ -3831,6 +3905,7 @@ struct TownCells : TownTask {
baseHeight,
promise]() {
{
ZoneScoped;
int count = 0;
for (auto &lot : lots) {
for (auto &jcell : lot.jlot["cells"]) {
@@ -3877,6 +3952,7 @@ struct TownCells : TownTask {
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([radius,
baseHeight,
promise]() {
ZoneScopedN("createCells::MainThread");
int count = 0;
for (auto &lot : lots) {
float distance = radius;
@@ -4012,10 +4088,12 @@ struct TownCells : TownTask {
int index, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
ZoneScoped;
createCells(e, jdistrict, index, sceneNode, geo);
}
void wait()
{
ZoneScoped;
if (townCellsComplete.valid() &&
townCellsComplete.wait_for(std::chrono::seconds(0)) !=
std::future_status::ready)
@@ -4029,6 +4107,7 @@ struct TownRoofs : TownTask {
int index, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
ZoneScoped;
struct QueueData {
Procedural::TriangleBuffer tb;
Ogre::Vector3 position;
@@ -4146,6 +4225,7 @@ struct TownRoofs : TownTask {
}
Ogre::Root::getSingleton().getWorkQueue()->addTask([radius,
promise]() {
ZoneScoped;
for (auto &lot : lots) {
for (auto &jroof : lot.jlot["roofs"]) {
int roofType = jroof["type"].get<int>();
@@ -4803,6 +4883,7 @@ struct TownRoofs : TownTask {
}
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([radius,
promise]() {
ZoneScopedN("createTownRoofs::MainThread");
{
int count = 0;
for (auto lot : lots) {
@@ -4878,10 +4959,12 @@ struct TownRoofs : TownTask {
int index, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
ZoneScoped;
createTownRoofs(e, jdistrict, index, sceneNode, geo);
}
void wait()
{
ZoneScoped;
if (townRoofsComplete.valid() &&
townRoofsComplete.wait_for(std::chrono::seconds(0)) !=
std::future_status::ready)
@@ -4896,6 +4979,7 @@ struct TownDecorateWindows : TownTask {
Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
ZoneScoped;
// FIXME: make furniture placement a background task.
Ogre::MaterialPtr townMaterial = createTownMaterial(e);
const nlohmann::json &jp = jdistrict;
@@ -5109,10 +5193,12 @@ struct TownDecorateWindows : TownTask {
int index, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
ZoneScoped;
createDecorateWindows(e, jdistrict, index, sceneNode, geo);
}
void wait()
{
ZoneScoped;
if (townDecorateWindowsComplete.valid() &&
townDecorateWindowsComplete.wait_for(std::chrono::seconds(
0)) != std::future_status::ready)
@@ -5126,6 +5212,7 @@ struct TownDecorateDoors : TownTask {
Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
ZoneScoped;
// FIXME: make furniture placement a background task.
Ogre::MaterialPtr townMaterial = createTownMaterial(e);
const nlohmann::json &jp = jdistrict;
@@ -5423,10 +5510,12 @@ struct TownDecorateDoors : TownTask {
int index, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
ZoneScoped;
createDecorateDoors(e, jdistrict, index, sceneNode, geo);
}
void wait()
{
ZoneScoped;
if (townDecorateDoorsComplete.valid() &&
townDecorateDoorsComplete.wait_for(std::chrono::seconds(
0)) != std::future_status::ready)
@@ -5440,6 +5529,7 @@ struct TownDecorateFurniture : TownTask {
Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
ZoneScoped;
// FIXME: make furniture placement a background task.
Ogre::MaterialPtr townMaterial = createTownMaterial(e);
const nlohmann::json &jp = jdistrict;
@@ -5862,6 +5952,7 @@ void registerTownNPCs(flecs::entity e)
void createTown(flecs::entity e, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo)
{
ZoneScopedN("createTown");
std::cout << "createTown " << e.id() << std::endl;
Ogre::MaterialPtr townMaterial = createTownMaterial(e);
static std::vector<TownTask *> townTasks;
@@ -5920,10 +6011,12 @@ void createTown(flecs::entity e, Ogre::SceneNode *sceneNode,
}
}
Ogre::Root::getSingleton().getWorkQueue()->addTask([geo]() {
ZoneScoped;
for (auto m : townTasks)
m->wait();
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask(
[geo]() {
ZoneScopedN("createTown::MainThread");
for (auto m : townTasks) {
delete m;
}

View File

@@ -16,5 +16,5 @@ find_package(flecs REQUIRED CONFIG)
add_library(physics STATIC physics.cpp)
target_link_libraries(physics PUBLIC OgreMain Jolt::Jolt)
target_include_directories(physics PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(physics PRIVATE JPH_PROFILE_ENABLED)
target_compile_definitions(physics PUBLIC JPH_DEBUG_RENDERER JPH_PROFILE_ENABLED JPH_DOUBLE_PRECISION)

View File

@@ -523,7 +523,6 @@ void ContactListener::update()
}
class Physics {
JPH::PhysicsSystem physics_system;
// We need a temp allocator for temporary allocations during the physics update. We're
// pre-allocating 10 MB to avoid having to do allocations during the physics update.
// B.t.w. 10 MB is way too much for this example but it is a typical value you can use.
@@ -550,6 +549,7 @@ class Physics {
// Also have a look at ObjectLayerPairFilterTable or ObjectLayerPairFilterMask for a simpler interface.
ObjectLayerPairFilterImpl object_vs_object_layer_filter;
JPH::PhysicsSystem physics_system;
DebugRenderer *mDebugRenderer;
std::map<JPH::BodyID, Ogre::SceneNode *> id2node;
std::map<Ogre::SceneNode *, JPH::BodyID> node2id;
@@ -573,17 +573,13 @@ public:
std::thread::hardware_concurrency() - 1)
, mDebugRenderer(new DebugRenderer(scnMgr, cameraNode))
, object_vs_broadphase_layer_filter{}
, object_vs_object_layer_filter{}
, object_vs_object_layer_filter{}
, debugDraw(false)
{
static int instanceCount = 0;
OgreAssert(instanceCount == 0, "Bad initialisation");
instanceCount++;
// Register all physics types with the factory and install their collision handlers with the CollisionDispatch class.
// If you have your own custom shape types you probably need to register their handlers with the CollisionDispatch before calling this function.
// If you implement your own default material (PhysicsMaterial::sDefault) make sure to initialize it before this function or else this function will create one for you.
JPH::RegisterTypes();
// This is the max amount of rigid bodies that you can add to the physics system. If you try to add more you'll get an error.
// Note: This value is low because this is a simple test. For a real project use something in the order of 65536.
@@ -1510,6 +1506,19 @@ public:
}
return hadHit;
}
bool bodyIsCharacter(JPH::BodyID id) const
{
return characterBodies.find(id) != characterBodies.end();
}
void destroyCharacter(JPH::Character *ch)
{
characterBodies.erase(characterBodies.find(ch->GetBodyID()));
characters.erase(ch);
Ogre::SceneNode *node = id2node[ch->GetBodyID()];
id2node.erase(ch->GetBodyID());
node2id.erase(node);
OGRE_DELETE ch;
}
};
void physics()
@@ -1518,7 +1527,7 @@ void physics()
// physics.update(1.0f / 60.0f);
}
Physics *phys = nullptr;
static std::unique_ptr<Physics> phys = nullptr;
JoltPhysicsWrapper::JoltPhysicsWrapper(Ogre::SceneManager *scnMgr,
Ogre::SceneNode *cameraNode)
: Ogre::Singleton<JoltPhysicsWrapper>()
@@ -1527,21 +1536,24 @@ JoltPhysicsWrapper::JoltPhysicsWrapper(Ogre::SceneManager *scnMgr,
// This needs to be done before any other Jolt function is called.
JPH::RegisterDefaultAllocator();
// Create a factory, this class is responsible for creating instances of classes based on their name or hash and is mainly used for deserialization of saved data.
// It is not directly used in this example but still required.
JPH::Factory::sInstance = new JPH::Factory();
// Install trace and assert callbacks
JPH::Trace = TraceImpl;
JPH_IF_ENABLE_ASSERTS(JPH::AssertFailed = AssertFailedImpl;)
phys = new Physics(scnMgr, cameraNode, nullptr, &contacts);
// Create a factory, this class is responsible for creating instances of classes based on their name or hash and is mainly used for deserialization of saved data.
// It is not directly used in this example but still required.
JPH::Factory::sInstance = new JPH::Factory();
// Register all physics types with the factory and install their collision handlers with the CollisionDispatch class.
// If you have your own custom shape types you probably need to register their handlers with the CollisionDispatch before calling this function.
// If you implement your own default material (PhysicsMaterial::sDefault) make sure to initialize it before this function or else this function will create one for you.
JPH::RegisterTypes();
phys = std::make_unique<Physics>(scnMgr, cameraNode, nullptr, &contacts);
}
JoltPhysicsWrapper::~JoltPhysicsWrapper()
{
if (phys)
delete phys;
}
void JoltPhysicsWrapper::update(float dt)
@@ -1806,5 +1818,15 @@ bool JoltPhysicsWrapper::raycastQuery(Ogre::Vector3 startPoint,
{
return phys->raycastQuery(startPoint, endPoint, position, id);
}
bool JoltPhysicsWrapper::bodyIsCharacter(JPH::BodyID id) const
{
return phys->bodyIsCharacter(id);
}
void JoltPhysicsWrapper::destroyCharacter(JPH::Character *ch)
{
phys->destroyCharacter(ch);
}
template <>
JoltPhysicsWrapper *Ogre::Singleton<JoltPhysicsWrapper>::msSingleton = 0;

View File

@@ -14,6 +14,7 @@ void physics();
namespace JPH
{
class CharacterBase;
class Character;
class ContactManifold;
class ContactSettings;
class SubShapeIDPair;
@@ -217,5 +218,7 @@ public:
void removeContactListener(const JPH::BodyID &id);
bool raycastQuery(Ogre::Vector3 startPoint, Ogre::Vector3 endPoint,
Ogre::Vector3 &position, JPH::BodyID &id);
bool bodyIsCharacter(JPH::BodyID id) const;
void destroyCharacter(JPH::Character *ch);
};
#endif