diff --git a/resources.cfg b/resources.cfg index e2b30cc..4c65caf 100644 --- a/resources.cfg +++ b/resources.cfg @@ -73,8 +73,6 @@ FileSystem=resources/fonts [LuaScripts] FileSystem=lua-scripts -#[Characters] -#FileSystem=./characters [Audio] FileSystem=./audio/gui diff --git a/src/features/editScene/EditorApp.cpp b/src/features/editScene/EditorApp.cpp index 59e3249..4613240 100644 --- a/src/features/editScene/EditorApp.cpp +++ b/src/features/editScene/EditorApp.cpp @@ -447,71 +447,57 @@ bool EditorApp::frameRenderingQueued(const Ogre::FrameEvent &evt) m_camera->update(evt.timeSinceLastFrame); } - // Update character physics (before main physics step) - if (m_characterSystem) { - m_characterSystem->update(evt.timeSinceLastFrame); - } - - // Update physics - if (m_physicsSystem) { - m_physicsSystem->update(evt.timeSinceLastFrame); - } - - // Update lights - if (m_lightSystem) { - m_lightSystem->update(); - } - - // Update cameras - if (m_cameraSystem) { - m_cameraSystem->update(); - } - - // Update LOD system - if (m_lodSystem) { - m_lodSystem->update(); - } - - // Update StaticGeometry system - if (m_staticGeometrySystem) { - m_staticGeometrySystem->update(); - } - - // Update ProceduralTexture system - if (m_proceduralTextureSystem) { - m_proceduralTextureSystem->update(); - } - - // Update ProceduralMaterial system - if (m_proceduralMaterialSystem) { - m_proceduralMaterialSystem->update(); - } - - // Update CharacterSlot system + /* --- Visual mesh setup (must run before animation) --- */ if (m_characterSlotSystem) { m_characterSlotSystem->update(); } - // Update AnimationTree system + /* --- Animation / procedural generation --- */ if (m_animationTreeSystem) { m_animationTreeSystem->update(evt.timeSinceLastFrame); } - - // Update ProceduralMesh system if (m_proceduralMeshSystem) { m_proceduralMeshSystem->update(); } - // Update RoomLayout system FIRST (generates cells for CellGrid) + /* --- Static world generation (meshes + physics) --- */ if (m_roomLayoutSystem) { m_roomLayoutSystem->update(); } - - // Update CellGrid system (builds mesh from cells) if (m_cellGridSystem) { m_cellGridSystem->update(); } + /* --- Dynamic physics (characters after static world) --- */ + if (m_characterSystem) { + m_characterSystem->update(evt.timeSinceLastFrame); + } + + /* --- Main physics step --- */ + if (m_physicsSystem) { + m_physicsSystem->update(evt.timeSinceLastFrame); + } + + /* --- Rendering support systems --- */ + if (m_lightSystem) { + m_lightSystem->update(); + } + if (m_cameraSystem) { + m_cameraSystem->update(); + } + if (m_lodSystem) { + m_lodSystem->update(); + } + if (m_staticGeometrySystem) { + m_staticGeometrySystem->update(); + } + if (m_proceduralTextureSystem) { + m_proceduralTextureSystem->update(); + } + if (m_proceduralMaterialSystem) { + m_proceduralMaterialSystem->update(); + } + // Don't call base class - it crashes when iterating input listeners return true; } diff --git a/src/features/editScene/components/CellGrid.hpp b/src/features/editScene/components/CellGrid.hpp index bc7e6fc..d4fa887 100644 --- a/src/features/editScene/components/CellGrid.hpp +++ b/src/features/editScene/components/CellGrid.hpp @@ -145,6 +145,9 @@ struct CellGridComponent { std::string roofTopRectName; std::string roofSideRectName; + // Physics properties for generated colliders + float friction = 0.5f; + // Dirty flag - triggers rebuild bool dirty = true; unsigned int version = 0; @@ -419,6 +422,9 @@ struct LotComponent { // Texture rectangle name from ProceduralTexture for UV mapping std::string textureRectName; + // Physics properties for generated colliders + float friction = 0.5f; + // Dirty flag bool dirty = true; @@ -451,6 +457,9 @@ struct DistrictComponent { // Texture rectangle name from ProceduralTexture for UV mapping std::string textureRectName; + // Physics properties for generated colliders + float friction = 0.5f; + // Dirty flag bool dirty = true; diff --git a/src/features/editScene/components/Character.hpp b/src/features/editScene/components/Character.hpp index 4d8593a..2fdef13 100644 --- a/src/features/editScene/components/Character.hpp +++ b/src/features/editScene/components/Character.hpp @@ -30,6 +30,11 @@ struct CharacterComponent { /* Dirty flag — triggers rebuild of the Jolt character */ bool dirty = true; + /* Floor detection: raycast downward to find ground before enabling gravity */ + bool hasFloor = false; + float floorCheckDistance = 2.0f; + bool useGravity = true; + float getHalfHeight() const { return height * 0.5f; } float getTotalHeight() const { return height + 2.0f * radius; } }; diff --git a/src/features/editScene/physics/physics.cpp b/src/features/editScene/physics/physics.cpp index 0ac2cc2..d3978bf 100644 --- a/src/features/editScene/physics/physics.cpp +++ b/src/features/editScene/physics/physics.cpp @@ -560,6 +560,7 @@ class Physics { std::set characters; std::set characterBodies; bool debugDraw; + JPH::Vec3 gravity = JPH::Vec3(0.0f, -9.8f, 0.0f); public: class ActivationListener : public JPH::BodyActivationListener { @@ -720,7 +721,7 @@ public: body_interface.RemoveBody(floor->GetID()); body_interface.DestroyBody(floor->GetID()); #endif - physics_system.SetGravity(JPH::Vec3(0, -0.1f, 0)); + physics_system.SetGravity(gravity); } ~Physics() { @@ -767,7 +768,7 @@ public: if (debugDraw) cCollisionSteps = 4; while (timeAccumulator >= fixedDeltaTime) { - physics_system.Update(dt, cCollisionSteps, + physics_system.Update(fixedDeltaTime, cCollisionSteps, &temp_allocator, &job_system); timeAccumulator -= fixedDeltaTime; } @@ -789,8 +790,13 @@ public: node->_setDerivedOrientation(JoltPhysics::convert(q)); } for (JPH::Character *ch : characters) { - if (body_interface.IsAdded(ch->GetBodyID())) + if (body_interface.IsAdded(ch->GetBodyID())) { ch->PostSimulation(0.1f); + Ogre::SceneNode *node = id2node[ch->GetBodyID()]; + if (node) + node->_setDerivedPosition( + JoltPhysics::convert(ch->GetPosition())); + } } if (debugDraw) @@ -811,6 +817,15 @@ public: { debugDraw = enable; } + JPH::Vec3 getGravity() const + { + return gravity; + } + void setGravity(const JPH::Vec3 &g) + { + gravity = g; + physics_system.SetGravity(gravity); + } static JPH::ShapeRefC createBoxShape(float x, float y, float z) { return new JPH::BoxShape(JPH::Vec3(x, y, z)); @@ -1531,6 +1546,15 @@ public: return physics_system.GetBodyInterface().SetFriction(id, friction); } + float getGravityFactor(JPH::BodyID id) + { + return physics_system.GetBodyInterface().GetGravityFactor(id); + } + void setGravityFactor(JPH::BodyID id, float factor) + { + return physics_system.GetBodyInterface().SetGravityFactor(id, + factor); + } void broadphaseQuery(float dt, const Ogre::Vector3 &position, std::set &inWater) { @@ -1790,6 +1814,14 @@ void JoltPhysicsWrapper::setDebugDraw(bool enable) { phys->setDebugDraw(enable); } +Ogre::Vector3 JoltPhysicsWrapper::getGravity() const +{ + return JoltPhysics::convert(phys->getGravity()); +} +void JoltPhysicsWrapper::setGravity(const Ogre::Vector3 &gravity) +{ + phys->setGravity(JoltPhysics::convert(gravity)); +} void JoltPhysicsWrapper::broadphaseQuery(float dt, const Ogre::Vector3 &position, std::set &inWater) @@ -1870,6 +1902,14 @@ void JoltPhysicsWrapper::setFriction(JPH::BodyID id, float friction) { phys->setFriction(id, friction); } +float JoltPhysicsWrapper::getGravityFactor(JPH::BodyID id) +{ + return phys->getGravityFactor(id); +} +void JoltPhysicsWrapper::setGravityFactor(JPH::BodyID id, float factor) +{ + phys->setGravityFactor(id, factor); +} void JoltPhysicsWrapper::addAngularImpulse(const JPH::BodyID &id, const Ogre::Vector3 &impulse) { diff --git a/src/features/editScene/physics/physics.h b/src/features/editScene/physics/physics.h index ea61750..26383ad 100644 --- a/src/features/editScene/physics/physics.h +++ b/src/features/editScene/physics/physics.h @@ -177,6 +177,8 @@ public: void removeBody(const JPH::BodyID &id); void destroyBody(const JPH::BodyID &id); void setDebugDraw(bool enable); + Ogre::Vector3 getGravity() const; + void setGravity(const Ogre::Vector3 &gravity); void broadphaseQuery(float dt, const Ogre::Vector3 &position, std::set &inWater); void applyBuoyancyImpulse(JPH::BodyID id, @@ -210,6 +212,8 @@ public: Ogre::Vector3 getAngularVelocity(JPH::BodyID id); float getFriction(JPH::BodyID id); void setFriction(JPH::BodyID id, float friction); + float getGravityFactor(JPH::BodyID id); + void setGravityFactor(JPH::BodyID id, float factor); void addAngularImpulse(const JPH::BodyID &id, const Ogre::Vector3 &impulse); void setDispatch(std::function #include #include #include +#include AnimationTreeSystem::AnimationTreeSystem(flecs::world &world, Ogre::SceneManager *sceneMgr) @@ -70,17 +72,23 @@ Ogre::Entity *AnimationTreeSystem::findAnimatedEntity(flecs::entity e) return nullptr; } -void AnimationTreeSystem::setupEntity(flecs::entity e, +bool AnimationTreeSystem::setupEntity(flecs::entity e, AnimationTreeComponent &at) { Ogre::Entity *ent = findAnimatedEntity(e); + std::cout << "AnimationTreeSystem::setupEntity: entity=" << e.id() + << " ent=" << ent + << " hasSkeleton=" << (ent ? ent->hasSkeleton() : false) + << std::endl; if (!ent || !ent->hasSkeleton()) { teardownEntity(e); - return; + return false; } EntityAnimTreeState &state = m_states[e.id()]; state.ogreEntity = ent; + state.rootBone = nullptr; + state.animations.clear(); Ogre::SkeletonInstance *skel = ent->getSkeleton(); @@ -161,6 +169,10 @@ void AnimationTreeSystem::setupEntity(flecs::entity e, /* Initialize default state machine states */ initializeTreeStates(at.root, at); + std::cout << " setupEntity done: animations=" << state.animations.size() + << " rootBone=" << (state.rootBone ? state.rootBone->getName() : "null") + << std::endl; + return true; } void AnimationTreeSystem::teardownEntity(flecs::entity e) @@ -225,14 +237,27 @@ void AnimationTreeSystem::update(float deltaTime) [this, deltaTime](flecs::entity e, AnimationTreeComponent &at) { if (at.dirty) { - setupEntity(e, at); - at.dirty = false; + if (setupEntity(e, at)) + at.dirty = false; } auto it = m_states.find(e.id()); if (it == m_states.end()) return; + /* Validate cached entity pointer - + * CharacterSlotSystem rebuilds can destroy and + * recreate the Ogre::Entity */ + Ogre::Entity *currentEnt = findAnimatedEntity(e); + if (currentEnt != it->second.ogreEntity) { + if (!setupEntity(e, at)) { + return; + } + it = m_states.find(e.id()); + if (it == m_states.end()) + return; + } + EntityAnimTreeState &state = it->second; if (!state.ogreEntity) return; @@ -323,9 +348,22 @@ void AnimationTreeSystem::update(float deltaTime) } } - if (at.useRootMotion && sceneNode) - sceneNode->translate(totalRootMotion, + if (at.useRootMotion && sceneNode) { + if (e.has()) { + auto &cc = e.get_mut(); + if (deltaTime > 0.0000001f) { + float safeDelta = Ogre::Math::Clamp(deltaTime, 0.005f, 0.99f); + Ogre::Quaternion worldRot = sceneNode->_getDerivedOrientation(); + cc.linearVelocity = worldRot * totalRootMotion / safeDelta; + cc.linearVelocity.x = Ogre::Math::Clamp(cc.linearVelocity.x, -16.0f, 16.0f); + cc.linearVelocity.z = Ogre::Math::Clamp(cc.linearVelocity.z, -16.0f, 16.0f); + cc.linearVelocity.y = Ogre::Math::Clamp(cc.linearVelocity.y, -10.5f, 10.0f); + } + } else { + sceneNode->translate(totalRootMotion, Ogre::Node::TS_LOCAL); + } + } /* Reset root bone to binding pose */ if (state.rootBone) { diff --git a/src/features/editScene/systems/AnimationTreeSystem.hpp b/src/features/editScene/systems/AnimationTreeSystem.hpp index f4393e4..8464d2d 100644 --- a/src/features/editScene/systems/AnimationTreeSystem.hpp +++ b/src/features/editScene/systems/AnimationTreeSystem.hpp @@ -82,7 +82,7 @@ private: std::vector> endChecks; }; - void setupEntity(flecs::entity e, AnimationTreeComponent &at); + bool setupEntity(flecs::entity e, AnimationTreeComponent &at); void teardownEntity(flecs::entity e); void disableAllAnimations(EntityAnimTreeState &state); void initializeTreeStates(const AnimationTreeNode &node, diff --git a/src/features/editScene/systems/CellGridSystem.cpp b/src/features/editScene/systems/CellGridSystem.cpp index 82ec8ee..c239ed0 100644 --- a/src/features/editScene/systems/CellGridSystem.cpp +++ b/src/features/editScene/systems/CellGridSystem.cpp @@ -2682,7 +2682,10 @@ void CellGridSystem::buildPhysicsColliders(flecs::entity entity) physicsParent.child_of(entity); physicsParent.set({nullptr, parentPos, parentRot, parentScale}); - physicsParent.set(RigidBodyComponent()); + RigidBodyComponent cellGridRb; + if (entity.has()) + cellGridRb.friction = entity.get().friction; + physicsParent.set(cellGridRb); physicsParent.add(); meshData.physicsParentEntity = physicsParent; @@ -2741,7 +2744,10 @@ void CellGridSystem::buildPhysicsColliders(flecs::entity entity) physicsParent.child_of(entity); physicsParent.set({nullptr, parentPos, parentRot, parentScale}); - physicsParent.set(RigidBodyComponent()); + RigidBodyComponent plazaRb; + if (entity.has()) + plazaRb.friction = entity.get().friction; + physicsParent.set(plazaRb); physicsParent.add(); plazaIt->second.physicsParentEntity = physicsParent; addPhysicsCollider(physicsParent, plazaIt->second.meshName, @@ -2760,7 +2766,10 @@ void CellGridSystem::buildPhysicsColliders(flecs::entity entity) physicsParent.child_of(entity); physicsParent.set({nullptr, parentPos, parentRot, parentScale}); - physicsParent.set(RigidBodyComponent()); + RigidBodyComponent lotRb; + if (entity.has()) + lotRb.friction = entity.get().friction; + physicsParent.set(lotRb); physicsParent.add(); lotIt->second.physicsParentEntity = physicsParent; addPhysicsCollider(physicsParent, lotIt->second.meshName, diff --git a/src/features/editScene/systems/CharacterSlotSystem.cpp b/src/features/editScene/systems/CharacterSlotSystem.cpp index b585875..6a6c3c4 100644 --- a/src/features/editScene/systems/CharacterSlotSystem.cpp +++ b/src/features/editScene/systems/CharacterSlotSystem.cpp @@ -1,5 +1,6 @@ #include "CharacterSlotSystem.hpp" #include "../components/Transform.hpp" +#include "../components/AnimationTree.hpp" #include #include #include @@ -218,6 +219,14 @@ void CharacterSlotSystem::buildCharacter(flecs::entity e, cs.masterEntity = masterEnt; std::cout << " master loaded: " << masterEnt->getName() << std::endl; + std::cout << " node=" << transform.node->getName() + << " pos=" << transform.node->_getDerivedPosition() + << " attached=" << transform.node->numAttachedObjects() + << std::endl; + + /* Notify AnimationTreeSystem that entity changed */ + if (e.has()) + e.get_mut().dirty = true; } catch (const Ogre::Exception &ex) { std::cout << " FAILED to load master mesh: " << ex.getDescription() << std::endl; diff --git a/src/features/editScene/systems/CharacterSystem.cpp b/src/features/editScene/systems/CharacterSystem.cpp index 3e27561..71e2bd5 100644 --- a/src/features/editScene/systems/CharacterSystem.cpp +++ b/src/features/editScene/systems/CharacterSystem.cpp @@ -1,6 +1,7 @@ #include "CharacterSystem.hpp" #include #include +#include CharacterSystem::CharacterSystem(flecs::world &world, Ogre::SceneManager *sceneMgr) @@ -159,6 +160,11 @@ void CharacterSystem::setupEntity(flecs::entity e, auto *ch = static_cast(base); ch->AddToPhysicsSystem(); + m_physics->setGravityFactor(ch->GetBodyID(), 0.0f); + cc.hasFloor = false; + std::cout << "CharacterSystem::setupEntity: entity=" << e.id() + << " nodePos=" << transform.node->_getDerivedPosition() + << std::endl; CharacterState state; state.character = ch; @@ -223,15 +229,38 @@ void CharacterSystem::update(float deltaTime) if (diff.squaredLength() > 0.001f) { state.character->SetPosition( JoltPhysics::convert(nodePos)); - charPos = nodePos; } - /* Integrate velocity */ - if (cc.linearVelocity.squaredLength() > 0.0f) { - Ogre::Vector3 newPos = charPos + - cc.linearVelocity * deltaTime; - state.character->SetPosition( - JoltPhysics::convert(newPos)); + /* Apply velocity via Jolt linear velocity (matches + * original game code) */ + state.character->SetLinearVelocity( + JoltPhysics::convert( + cc.linearVelocity)); + if (cc.linearVelocity.squaredLength() > 0.0001f) { + std::cout << "CharacterSystem::update: entity=" << e.id() + << " vel=" << cc.linearVelocity + << std::endl; + } + + /* Floor detection: raycast downward to find ground */ + if (cc.useGravity && !cc.hasFloor) { + Ogre::Vector3 rayStart = charPos; + Ogre::Vector3 rayEnd = + rayStart + + Ogre::Vector3(0.0f, -cc.floorCheckDistance, 0.0f); + Ogre::Vector3 hitPos; + JPH::BodyID hitBody; + if (m_physics->raycastQuery(rayStart, rayEnd, + hitPos, hitBody)) { + cc.hasFloor = true; + m_physics->setGravityFactor( + state.character->GetBodyID(), 1.0f); + std::cout << "CharacterSystem::floor detected: entity=" + << e.id() + << " dist=" + << (charPos - hitPos).length() + << std::endl; + } } /* Sync rotation from scene node */ diff --git a/src/features/editScene/systems/CharacterSystem.hpp b/src/features/editScene/systems/CharacterSystem.hpp index 412a3a4..38796f6 100644 --- a/src/features/editScene/systems/CharacterSystem.hpp +++ b/src/features/editScene/systems/CharacterSystem.hpp @@ -7,7 +7,7 @@ #include #include -#include "../../physics/physics.h" +#include "../physics/physics.h" #include "../components/Character.hpp" #include "../components/Transform.hpp" #include "../components/PhysicsCollider.hpp" diff --git a/src/features/editScene/systems/SceneSerializer.cpp b/src/features/editScene/systems/SceneSerializer.cpp index e6a17e0..8d9b804 100644 --- a/src/features/editScene/systems/SceneSerializer.cpp +++ b/src/features/editScene/systems/SceneSerializer.cpp @@ -15,6 +15,7 @@ #include "../components/ProceduralMaterial.hpp" #include "../components/Primitive.hpp" #include "../components/TriangleBuffer.hpp" +#include "../components/Character.hpp" #include "../components/CharacterSlots.hpp" #include "../components/AnimationTree.hpp" #include "../components/CellGrid.hpp" @@ -180,6 +181,10 @@ nlohmann::json SceneSerializer::serializeEntity(flecs::entity entity) json["triangleBuffer"] = serializeTriangleBuffer(entity); } + if (entity.has()) { + json["character"] = serializeCharacter(entity); + } + if (entity.has()) { json["characterSlots"] = serializeCharacterSlots(entity); } @@ -304,6 +309,10 @@ void SceneSerializer::deserializeEntity(const nlohmann::json& json, flecs::entit deserializePrimitive(entity, json["primitive"]); } + if (json.contains("character")) { + deserializeCharacter(entity, json["character"]); + } + if (json.contains("characterSlots")) { deserializeCharacterSlots(entity, json["characterSlots"]); } @@ -1281,6 +1290,49 @@ void SceneSerializer::deserializeTriangleBuffer(flecs::entity entity, const nloh } +nlohmann::json SceneSerializer::serializeCharacter(flecs::entity entity) +{ + auto& cc = entity.get(); + nlohmann::json json; + + json["enabled"] = cc.enabled; + json["radius"] = cc.radius; + json["height"] = cc.height; + json["offset"] = { + cc.offset.x, cc.offset.y, cc.offset.z + }; + json["linearVelocity"] = { + cc.linearVelocity.x, cc.linearVelocity.y, cc.linearVelocity.z + }; + + return json; +} + +void SceneSerializer::deserializeCharacter(flecs::entity entity, const nlohmann::json& json) +{ + CharacterComponent cc; + cc.enabled = json.value("enabled", true); + cc.radius = json.value("radius", 0.3f); + cc.height = json.value("height", 1.8f); + if (json.contains("offset") && json["offset"].is_array() && + json["offset"].size() >= 3) { + cc.offset = Ogre::Vector3( + json["offset"][0].get(), + json["offset"][1].get(), + json["offset"][2].get()); + } + if (json.contains("linearVelocity") && + json["linearVelocity"].is_array() && + json["linearVelocity"].size() >= 3) { + cc.linearVelocity = Ogre::Vector3( + json["linearVelocity"][0].get(), + json["linearVelocity"][1].get(), + json["linearVelocity"][2].get()); + } + cc.dirty = true; + entity.set(cc); +} + nlohmann::json SceneSerializer::serializeCharacterSlots(flecs::entity entity) { auto& cs = entity.get(); @@ -1409,6 +1461,7 @@ nlohmann::json SceneSerializer::serializeCellGrid(flecs::entity entity) json["intWindowFrameRectName"] = grid.intWindowFrameRectName; json["roofTopRectName"] = grid.roofTopRectName; json["roofSideRectName"] = grid.roofSideRectName; + json["friction"] = grid.friction; // Serialize cells nlohmann::json cellsJson = nlohmann::json::array(); @@ -1482,6 +1535,7 @@ nlohmann::json SceneSerializer::serializeDistrict(flecs::entity entity) json["lotTemplateNames"] = district.lotTemplateNames; json["proceduralMaterialEntityId"] = district.proceduralMaterialEntityId; json["textureRectName"] = district.textureRectName; + json["friction"] = district.friction; return json; } @@ -1500,6 +1554,7 @@ nlohmann::json SceneSerializer::serializeLot(flecs::entity entity) json["templateName"] = lot.templateName; json["proceduralMaterialEntityId"] = lot.proceduralMaterialEntityId; json["textureRectName"] = lot.textureRectName; + json["friction"] = lot.friction; return json; } @@ -1609,6 +1664,7 @@ void SceneSerializer::deserializeCellGrid(flecs::entity entity, const nlohmann:: grid.intWindowFrameRectName = json.value("intWindowFrameRectName", ""); grid.roofTopRectName = json.value("roofTopRectName", ""); grid.roofSideRectName = json.value("roofSideRectName", ""); + grid.friction = json.value("friction", 0.5f); // Deserialize cells if (json.contains("cells") && json["cells"].is_array()) { @@ -1702,6 +1758,7 @@ void SceneSerializer::deserializeDistrict(flecs::entity entity, const nlohmann:: district.isPlaza = json.value("isPlaza", false); district.proceduralMaterialEntityId = json.value("proceduralMaterialEntityId", ""); district.textureRectName = json.value("textureRectName", ""); + district.friction = json.value("friction", 0.5f); // Resolve material entity reference if (!district.proceduralMaterialEntityId.empty()) { @@ -1741,6 +1798,7 @@ void SceneSerializer::deserializeLot(flecs::entity entity, const nlohmann::json& lot.templateName = json.value("templateName", ""); lot.proceduralMaterialEntityId = json.value("proceduralMaterialEntityId", ""); lot.textureRectName = json.value("textureRectName", ""); + lot.friction = json.value("friction", 0.5f); // Resolve material entity reference if (!lot.proceduralMaterialEntityId.empty()) { diff --git a/src/features/editScene/systems/SceneSerializer.hpp b/src/features/editScene/systems/SceneSerializer.hpp index 0b9c22e..9134e44 100644 --- a/src/features/editScene/systems/SceneSerializer.hpp +++ b/src/features/editScene/systems/SceneSerializer.hpp @@ -54,6 +54,7 @@ private: nlohmann::json serializeProceduralMaterial(flecs::entity entity); nlohmann::json serializePrimitive(flecs::entity entity); nlohmann::json serializeTriangleBuffer(flecs::entity entity); + nlohmann::json serializeCharacter(flecs::entity entity); nlohmann::json serializeCharacterSlots(flecs::entity entity); nlohmann::json serializeAnimationTree(flecs::entity entity); @@ -83,6 +84,7 @@ private: void deserializeProceduralMaterial(flecs::entity entity, const nlohmann::json& json); void deserializePrimitive(flecs::entity entity, const nlohmann::json& json); void deserializeTriangleBuffer(flecs::entity entity, const nlohmann::json& json); + void deserializeCharacter(flecs::entity entity, const nlohmann::json& json); void deserializeCharacterSlots(flecs::entity entity, const nlohmann::json& json); void deserializeAnimationTree(flecs::entity entity, const nlohmann::json& json); diff --git a/src/features/editScene/ui/CellGridEditor.cpp b/src/features/editScene/ui/CellGridEditor.cpp index 35eecfa..cedae43 100644 --- a/src/features/editScene/ui/CellGridEditor.cpp +++ b/src/features/editScene/ui/CellGridEditor.cpp @@ -30,6 +30,9 @@ bool CellGridEditor::renderComponent(flecs::entity entity, CellGridComponent& gr if (ImGui::DragFloat("Cell Height", &grid.cellHeight, 0.1f, 0.5f, 20.0f)) { modified = true; } + if (ImGui::SliderFloat("Friction", &grid.friction, 0.0f, 1.0f, "%.2f")) { + modified = true; + } ImGui::Separator(); diff --git a/src/features/editScene/ui/CharacterEditor.cpp b/src/features/editScene/ui/CharacterEditor.cpp index f43201f..3845ff6 100644 --- a/src/features/editScene/ui/CharacterEditor.cpp +++ b/src/features/editScene/ui/CharacterEditor.cpp @@ -7,18 +7,18 @@ bool CharacterEditor::renderComponent(flecs::entity entity, (void)entity; bool modified = false; - if (ImGui::Checkbox("Enabled", &cc.enabled)) + if (ImGui::Checkbox("Enabled##Character", &cc.enabled)) modified = true; ImGui::Separator(); ImGui::Text("Capsule"); - if (ImGui::SliderFloat("Radius", &cc.radius, 0.05f, 2.0f, - "%.2f")) { + if (ImGui::SliderFloat("Radius##Character", &cc.radius, 0.05f, 2.0f, + "%.2f")) { modified = true; } - if (ImGui::SliderFloat("Height", &cc.height, 0.1f, 5.0f, - "%.2f")) { + if (ImGui::SliderFloat("Height##Character", &cc.height, 0.1f, 5.0f, + "%.2f")) { modified = true; } @@ -28,7 +28,7 @@ bool CharacterEditor::renderComponent(flecs::entity entity, ImGui::Separator(); ImGui::Text("Offset"); float off[3] = { cc.offset.x, cc.offset.y, cc.offset.z }; - if (ImGui::InputFloat3("Position", off, "%.2f")) { + if (ImGui::InputFloat3("Position##Character", off, "%.2f")) { cc.offset = Ogre::Vector3(off[0], off[1], off[2]); modified = true; } @@ -37,12 +37,26 @@ bool CharacterEditor::renderComponent(flecs::entity entity, ImGui::Text("Velocity"); float vel[3] = { cc.linearVelocity.x, cc.linearVelocity.y, cc.linearVelocity.z }; - if (ImGui::InputFloat3("Linear (m/s)", vel, "%.2f")) { + if (ImGui::InputFloat3("Linear (m/s)##Character", vel, "%.2f")) { cc.linearVelocity = Ogre::Vector3(vel[0], vel[1], vel[2]); modified = true; } + ImGui::Separator(); + ImGui::Text("Floor Detection"); + if (ImGui::Checkbox("Use Gravity##Character", &cc.useGravity)) { + modified = true; + } + if (ImGui::Checkbox("Has Floor##Character", &cc.hasFloor)) { + modified = true; + } + if (ImGui::SliderFloat("Check Distance##Character", + &cc.floorCheckDistance, 0.1f, 10.0f, + "%.2f")) { + modified = true; + } + if (modified) cc.dirty = true; diff --git a/src/features/editScene/ui/DistrictEditor.cpp b/src/features/editScene/ui/DistrictEditor.cpp index 87104df..0b8b554 100644 --- a/src/features/editScene/ui/DistrictEditor.cpp +++ b/src/features/editScene/ui/DistrictEditor.cpp @@ -26,6 +26,9 @@ bool DistrictEditor::renderComponent(flecs::entity entity, DistrictComponent& di "DistrictEditor: Is Plaza changed to " + std::string(district.isPlaza ? "true" : "false") + " for entity " + std::to_string(entity.id())); } + if (ImGui::SliderFloat("Friction", &district.friction, 0.0f, 1.0f, "%.2f")) { + modified = true; + } // District material and texture settings ImGui::Separator(); diff --git a/src/features/editScene/ui/LotEditor.cpp b/src/features/editScene/ui/LotEditor.cpp index d6ce63c..993ff3f 100644 --- a/src/features/editScene/ui/LotEditor.cpp +++ b/src/features/editScene/ui/LotEditor.cpp @@ -32,6 +32,9 @@ bool LotEditor::renderComponent(flecs::entity entity, LotComponent& lot) if (ImGui::DragFloat("Offset Z", &lot.offsetZ, 0.1f)) { modified = true; } + if (ImGui::SliderFloat("Friction", &lot.friction, 0.0f, 1.0f, "%.2f")) { + modified = true; + } // Template char templateName[128] = {0};