Characters are fully functional now

This commit is contained in:
2026-04-20 12:23:31 +03:00
parent a2173114b9
commit 4313d190f9
18 changed files with 287 additions and 77 deletions

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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; }
};

View File

@@ -560,6 +560,7 @@ class Physics {
std::set<JPH::Character *> characters;
std::set<JPH::BodyID> 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<JPH::BodyID> &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<JPH::Vec3>(gravity));
}
void JoltPhysicsWrapper::broadphaseQuery(float dt,
const Ogre::Vector3 &position,
std::set<JPH::BodyID> &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)
{

View File

@@ -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<JPH::BodyID> &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<void(const JoltPhysics::ContactListener::

View File

@@ -2,10 +2,12 @@
#include "../components/Transform.hpp"
#include "../components/Renderable.hpp"
#include "../components/CharacterSlots.hpp"
#include "../components/Character.hpp"
#include <OgreEntity.h>
#include <OgreLogManager.h>
#include <OgreSceneNode.h>
#include <cmath>
#include <iostream>
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<CharacterComponent>()) {
auto &cc = e.get_mut<CharacterComponent>();
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) {

View File

@@ -82,7 +82,7 @@ private:
std::vector<std::pair<Ogre::String, Ogre::String>> 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,

View File

@@ -2682,7 +2682,10 @@ void CellGridSystem::buildPhysicsColliders(flecs::entity entity)
physicsParent.child_of(entity);
physicsParent.set<TransformComponent>({nullptr, parentPos, parentRot,
parentScale});
physicsParent.set<RigidBodyComponent>(RigidBodyComponent());
RigidBodyComponent cellGridRb;
if (entity.has<CellGridComponent>())
cellGridRb.friction = entity.get<CellGridComponent>().friction;
physicsParent.set<RigidBodyComponent>(cellGridRb);
physicsParent.add<GeneratedPhysicsTag>();
meshData.physicsParentEntity = physicsParent;
@@ -2741,7 +2744,10 @@ void CellGridSystem::buildPhysicsColliders(flecs::entity entity)
physicsParent.child_of(entity);
physicsParent.set<TransformComponent>({nullptr, parentPos, parentRot,
parentScale});
physicsParent.set<RigidBodyComponent>(RigidBodyComponent());
RigidBodyComponent plazaRb;
if (entity.has<DistrictComponent>())
plazaRb.friction = entity.get<DistrictComponent>().friction;
physicsParent.set<RigidBodyComponent>(plazaRb);
physicsParent.add<GeneratedPhysicsTag>();
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<TransformComponent>({nullptr, parentPos, parentRot,
parentScale});
physicsParent.set<RigidBodyComponent>(RigidBodyComponent());
RigidBodyComponent lotRb;
if (entity.has<LotComponent>())
lotRb.friction = entity.get<LotComponent>().friction;
physicsParent.set<RigidBodyComponent>(lotRb);
physicsParent.add<GeneratedPhysicsTag>();
lotIt->second.physicsParentEntity = physicsParent;
addPhysicsCollider(physicsParent, lotIt->second.meshName,

View File

@@ -1,5 +1,6 @@
#include "CharacterSlotSystem.hpp"
#include "../components/Transform.hpp"
#include "../components/AnimationTree.hpp"
#include <OgreDataStream.h>
#include <OgreEntity.h>
#include <OgreLogManager.h>
@@ -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<AnimationTreeComponent>())
e.get_mut<AnimationTreeComponent>().dirty = true;
} catch (const Ogre::Exception &ex) {
std::cout << " FAILED to load master mesh: "
<< ex.getDescription() << std::endl;

View File

@@ -1,6 +1,7 @@
#include "CharacterSystem.hpp"
#include <Jolt/Physics/Character/Character.h>
#include <OgreLogManager.h>
#include <iostream>
CharacterSystem::CharacterSystem(flecs::world &world,
Ogre::SceneManager *sceneMgr)
@@ -159,6 +160,11 @@ void CharacterSystem::setupEntity(flecs::entity e,
auto *ch = static_cast<JPH::Character *>(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<JPH::Vec3>(
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 */

View File

@@ -7,7 +7,7 @@
#include <memory>
#include <unordered_map>
#include "../../physics/physics.h"
#include "../physics/physics.h"
#include "../components/Character.hpp"
#include "../components/Transform.hpp"
#include "../components/PhysicsCollider.hpp"

View File

@@ -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<CharacterComponent>()) {
json["character"] = serializeCharacter(entity);
}
if (entity.has<CharacterSlotsComponent>()) {
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<CharacterComponent>();
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<float>(),
json["offset"][1].get<float>(),
json["offset"][2].get<float>());
}
if (json.contains("linearVelocity") &&
json["linearVelocity"].is_array() &&
json["linearVelocity"].size() >= 3) {
cc.linearVelocity = Ogre::Vector3(
json["linearVelocity"][0].get<float>(),
json["linearVelocity"][1].get<float>(),
json["linearVelocity"][2].get<float>());
}
cc.dirty = true;
entity.set<CharacterComponent>(cc);
}
nlohmann::json SceneSerializer::serializeCharacterSlots(flecs::entity entity)
{
auto& cs = entity.get<CharacterSlotsComponent>();
@@ -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()) {

View File

@@ -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);

View File

@@ -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();

View File

@@ -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;

View File

@@ -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();

View File

@@ -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};