From 74a1adfb27e8799e4987b28f2a2fc8d59c3de5ce Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Thu, 12 Feb 2026 14:00:05 +0300 Subject: [PATCH] Intagrated Tracy, debugged animations --- CMakeLists.txt | 6 +- Game.cpp | 92 +- src/editor/CMakeLists.txt | 1 + src/editor/main.cpp | 97 +- src/gamedata/AnimationSystem.cpp | 68 ++ src/gamedata/AnimationSystem.h | 900 ++++++++++++++++++ src/gamedata/CMakeLists.txt | 17 +- src/gamedata/CharacterAIModule.cpp | 185 ++-- src/gamedata/CharacterAnimationModule.cpp | 200 ++-- src/gamedata/CharacterAnimationModule.h | 1013 +-------------------- src/gamedata/CharacterManagerModule.cpp | 111 +-- src/gamedata/CharacterModule.cpp | 109 +-- src/gamedata/GUIModule.cpp | 4 +- src/gamedata/GameData.cpp | 6 +- src/gamedata/LuaModule/CMakeLists.txt | 13 + src/gamedata/{ => LuaModule}/LuaData.cpp | 52 +- src/gamedata/{ => LuaModule}/LuaData.h | 0 src/gamedata/PhysicsModule.cpp | 106 ++- src/gamedata/PlayerActionModule.cpp | 5 + src/gamedata/StaticGeometryModule.cpp | 43 +- src/gamedata/WaterModule.cpp | 595 +----------- src/gamedata/items/CMakeLists.txt | 2 + src/gamedata/items/town.cpp | 11 + src/physics/physics.cpp | 23 + src/physics/physics.h | 3 + 25 files changed, 1553 insertions(+), 2109 deletions(-) create mode 100644 src/gamedata/AnimationSystem.cpp create mode 100644 src/gamedata/AnimationSystem.h create mode 100644 src/gamedata/LuaModule/CMakeLists.txt rename src/gamedata/{ => LuaModule}/LuaData.cpp (96%) rename src/gamedata/{ => LuaModule}/LuaData.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 01970fe..924ab26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/Game.cpp b/Game.cpp index 6afc0f2..cd19d24 100644 --- a/Game.cpp +++ b/Game.cpp @@ -21,6 +21,7 @@ #include "PhysicsModule.h" #include "physics.h" #include "sound.h" +#include class App; class SkyRenderer : public Ogre::SceneManager::Listener { protected: @@ -477,6 +478,7 @@ public: } void updateWorld(float delta) { + ZoneScoped; if (!ECS::get().has()) goto end; { @@ -738,40 +740,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().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().narrationBox)) { + mApp->updateWorld(evt.timeSinceLastFrame); + if (mInitDelay >= 0.0f) + mInitDelay -= evt.timeSinceLastFrame; + } - if (!isGuiEnabled() && ECS::get().has()) { - ECS::Input &input = ECS::get().get_mut(); - 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 &input = ECS::get().get_mut(); + 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 +796,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; diff --git a/src/editor/CMakeLists.txt b/src/editor/CMakeLists.txt index 8b6c719..fffcef6 100644 --- a/src/editor/CMakeLists.txt +++ b/src/editor/CMakeLists.txt @@ -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) diff --git a/src/editor/main.cpp b/src/editor/main.cpp index 66d805d..c3f41e6 100644 --- a/src/editor/main.cpp +++ b/src/editor/main.cpp @@ -23,6 +23,7 @@ #include "PhysicsModule.h" #include "physics.h" #include "sound.h" +#include 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 &input = ECS::get().get_mut(); - 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(); - } else { - ECS::Input &input = ECS::get().get_mut(); - 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(); + if (!isGuiEnabled() && ECS::get().has()) { + ECS::Input &input = ECS::get().get_mut(); + 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(); + } else { + ECS::Input &input = ECS::get().get_mut(); + 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(); + } } + FrameMark; } int main() diff --git a/src/gamedata/AnimationSystem.cpp b/src/gamedata/AnimationSystem.cpp new file mode 100644 index 0000000..bd66eec --- /dev/null +++ b/src/gamedata/AnimationSystem.cpp @@ -0,0 +1,68 @@ +#include +#include "AnimationSystem.h" +#include + +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; +} + +} diff --git a/src/gamedata/AnimationSystem.h b/src/gamedata/AnimationSystem.h new file mode 100644 index 0000000..112d3bb --- /dev/null +++ b/src/gamedata/AnimationSystem.h @@ -0,0 +1,900 @@ +#ifndef ANIMATIONSYSTEM_H +#define ANIMATIONSYSTEM_H + +#include +#include +#include +#include +#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(kf); + Ogre::KeyFrame *kf1, *kf2; + Ogre::TransformKeyFrame *k1, *k2; + unsigned short firstKeyIndex; + float tm = t->getKeyFramesAtTime(timeIndex, &kf1, &kf2, + &firstKeyIndex); + k1 = static_cast(kf1); + k2 = static_cast(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().mBoneMotion = deltaMotion; + e.get_mut().mBonePrevMotion = prevTranslation; + e.modified(); + return true; +#endif + return false; + } +}; +struct AnimationTrigger { + Ogre::String name; + float time; + float weight; + std::vector 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 children; + float m_weight; + Ogre::String m_name; + std::multimap 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( + 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 stateMap; + std::set 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( + 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( + 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 animation_list; + std::vector 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 animation_nodes; + AnimationNode *parent; + std::list parent_stack; + std::unordered_map nodeMap; + std::vector 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().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( + 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 T *get(const Ogre::String &name) + { + return static_cast(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 diff --git a/src/gamedata/CMakeLists.txt b/src/gamedata/CMakeLists.txt index f921c60..923451e 100644 --- a/src/gamedata/CMakeLists.txt +++ b/src/gamedata/CMakeLists.txt @@ -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) diff --git a/src/gamedata/CharacterAIModule.cpp b/src/gamedata/CharacterAIModule.cpp index 45f229f..cd06ed5 100644 --- a/src/gamedata/CharacterAIModule.cpp +++ b/src/gamedata/CharacterAIModule.cpp @@ -7,6 +7,7 @@ #include "CharacterModule.h" #include "items.h" #include "CharacterAIModule.h" +#include 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(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() == "") { } 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 *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 lock(ecs_mutex); + createBlackboards(town, npcs, ai); }); ecs.system("UpdateDynamicActions") .kind(flecs::OnUpdate) .each([](flecs::entity e, ActionNodeList &alist, TownAI &ai, TownNPCs &npcs) { + ZoneScopedN("UpdateDynamicActions"); std::lock_guard 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 lock(ecs_mutex); ECS::get_mut().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 elapsed = + end - start; + std::cout << what << " " << elapsed.count() + << std::endl; + } + }; ecs.system("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 lock( - ecs_mutex); + ZoneScopedN("UpdateBlackboards"); - updateBlackboards(town, alist, - npcs, ai); - } - Ogre::Root::getSingleton() - .getWorkQueue() - ->addMainThreadTask([this, town, - &alist]() { - town.modified(); - town.modified(); - ECS::modified< - ActionNodeList>(); - }); - }); + Ogre::Root::getSingleton().getWorkQueue()->addTask([this, + town, + &alist, + npcs, + &ai]() { + { + std::lock_guard lock( + ecs_mutex); + ZoneScopedN( + "UpdateBlackboards::Thread"); + + updateBlackboards(town, alist, npcs, + ai); + } + Ogre::Root::getSingleton() + .getWorkQueue() + ->addMainThreadTask([this, town, + &alist]() { + ZoneScopedN( + "UpdateBlackboards::MainThread"); + town.modified(); + town.modified(); + ECS::modified(); + }); + }); }); ecs.system("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 lock( - ecs_mutex); - buildPlans(town, npcs, ai); - Ogre::Root::getSingleton() - .getWorkQueue() - ->addMainThreadTask([this, - town]() { - town.modified(); - }); - }); + ZoneScopedN("PlanAI"); + Ogre::Root::getSingleton().getWorkQueue()->addTask([this, + town, + npcs, + &ai]() { + ZoneScopedN("PlanAI::Thread"); + std::lock_guard lock(ecs_mutex); + buildPlans(town, npcs, ai); + Ogre::Root::getSingleton() + .getWorkQueue() + ->addMainThreadTask([this, town]() { + ZoneScopedN( + "PlanAI::MainThread"); + town.modified(); + }); + }); }); static std::unordered_map plan_exec; ecs.system("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()) { @@ -758,6 +812,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 lock(*ai.mutex); for (auto it = npcs.npcs.begin(); it != npcs.npcs.end(); it++) { @@ -871,6 +926,7 @@ void CharacterAIModule::updateBlackboards(flecs::entity town, ActionNodeList &alist, const TownNPCs &npcs, TownAI &ai) { + ZoneScopedN("updateBlackboards"); std::lock_guard lock(*ai.mutex); OgreAssert(town.is_valid(), "Bad town entity"); alist.build(); @@ -1124,7 +1180,8 @@ void Blackboard::query_ai() const TownNPCs &npcs = town.get(); 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()) position = npcs.npcs.at(index) .e.get() .mBodyNode->_getDerivedPosition(); diff --git a/src/gamedata/CharacterAnimationModule.cpp b/src/gamedata/CharacterAnimationModule.cpp index b65b84b..8055eb7 100644 --- a/src/gamedata/CharacterAnimationModule.cpp +++ b/src/gamedata/CharacterAnimationModule.cpp @@ -8,6 +8,8 @@ #include "TerrainModule.h" #include "WaterModule.h" #include "world-build.h" +#include "AnimationSystem.h" +#include namespace ECS { CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) @@ -21,7 +23,8 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) ecs.system("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({}); @@ -32,19 +35,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 +99,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 +133,13 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) /* clang-format on */ anim.mAnimationSystem - ->get("main") + ->get( + "main") ->setAnimation("locomotion", true); anim.mAnimationSystem - ->get( + ->get( "locomotion-state") ->setAnimation("idle", true); anim.configured = true; @@ -152,69 +161,21 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) // 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 + ZoneScopedN("HandleAnimations1"); 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( - 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 +185,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( "HandleRootMotionVelocity") @@ -240,7 +197,8 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) return; if (!ch.mBodyNode) return; - Ogre::Quaternion rot = ch.mBodyNode->getOrientation(); + ZoneScopedN("HandleRootMotionVelocity"); + Ogre::Quaternion rot = ch.mBodyNode->getOrientation(); Ogre::Vector3 pos = ch.mBodyNode->getPosition(); Ogre::Vector3 boneMotion = ch.mBoneMotion; v.velocity = Ogre::Vector3::ZERO; @@ -273,33 +231,9 @@ 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("HandleSwimming") - .kind(flecs::OnUpdate) - .with() - .with() - .with() - .with() - .without() - .without() - .each([this](flecs::entity e, const EngineData &eng, - const AnimationControl &anim, - const CharacterBase &ch, CharacterVelocity &gr) { - if (anim.mAnimationSystem - ->get( - "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("HandleRootMotion") .kind(flecs::OnUpdate) @@ -328,19 +262,28 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) return; if (!anim.mAnimationSystem) return; - AnimationNodeStateMachine *state_machine = - anim.mAnimationSystem - ->get( - "locomotion-state"); + ZoneScopedNC("HandleNPCAnimations", 0xFF2020); + 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()) next_state = "treading_water"; - if (current_state != "idle" && !ch.is_submerged) + if (current_state != "idle" && !e.has()) 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()); + } }); ecs.system("HandlePlayerAnimationsActuator") @@ -349,15 +292,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( + ->get( "main"); - AnimationNodeStateMachine *actuator_sm = + AnimationSystem::AnimationNodeStateMachine *actuator_sm = anim.mAnimationSystem - ->get( + ->get( "actuator-state"); Ogre::String current_state = main_sm->getCurrentState(); if (current_state != "actuator") @@ -372,13 +318,15 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) .without() .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( + ->get( "main"); Ogre::String current_state = main_sm->getCurrentState(); if (current_state != "locomotion") @@ -395,10 +343,12 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) const CharacterBase &ch, AnimationControl &anim) { if (!anim.configured) return; - AnimationNodeStateMachine *state_machine = - anim.mAnimationSystem - ->get( - "locomotion-state"); + ZoneScopedN("HandlePlayerAnimations"); + 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 +424,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 +498,8 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) .kind(flecs::OnUpdate) .with() .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 */ @@ -600,10 +552,11 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) true) { const AnimationControl &control = param_e->get().get(); - AnimationNodeStateMachine *sm = - control.mAnimationSystem - ->get( - param_node->get()); + AnimationSystem::AnimationNodeStateMachine *sm = + control.mAnimationSystem->get< + AnimationSystem:: + AnimationNodeStateMachine>( + param_node->get()); bool reset = false; if (args.size() == 4) { GameWorld::ValueParameter @@ -622,6 +575,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) } }; ECS::get_mut().add_command( - "set_animation_state"); + "set_animation_state"); } + } diff --git a/src/gamedata/CharacterAnimationModule.h b/src/gamedata/CharacterAnimationModule.h index 9711de3..0e1d487 100644 --- a/src/gamedata/CharacterAnimationModule.h +++ b/src/gamedata/CharacterAnimationModule.h @@ -4,1021 +4,12 @@ #include #include "GameData.h" #include "CharacterModule.h" -#include "LuaData.h" -#include "EventModule.h" +#include "AnimationSystem.h" namespace ECS { -class RootMotionListener : public Ogre::NodeAnimationTrack::Listener { -public: - RootMotionListener(flecs::entity e) - : Ogre::NodeAnimationTrack::Listener() - { - } - bool getInterpolatedKeyFrame(const Ogre::AnimationTrack *t, - const Ogre::TimeIndex &timeIndex, - Ogre::KeyFrame *kf) override - { -#if 0 - Ogre::TransformKeyFrame *vkf = - static_cast(kf); - Ogre::KeyFrame *kf1, *kf2; - Ogre::TransformKeyFrame *k1, *k2; - unsigned short firstKeyIndex; - float tm = t->getKeyFramesAtTime(timeIndex, &kf1, &kf2, - &firstKeyIndex); - k1 = static_cast(kf1); - k2 = static_cast(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().mBoneMotion = deltaMotion; - e.get_mut().mBonePrevMotion = prevTranslation; - e.modified(); - return true; -#endif - return false; - } -}; -struct AnimationTrigger; -struct AnimationTriggerSubscriber { - virtual void operator()(const AnimationTrigger *trigger) = 0; -}; -struct AnimationTrigger { - Ogre::String name; - float time; - float weight; - std::vector 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 SetupTracks { - Ogre::NodeAnimationTrack *mHipsTrack; - Ogre::NodeAnimationTrack *mRootTrack; - RootMotionListener *mListener; - Ogre::Vector3 mRootTranslation; - SetupTracks(flecs::entity e, Ogre::Skeleton *skeleton, - Ogre::Animation *animation) - : mListener(OGRE_NEW RootMotionListener(e)) - { - mHipsTrack = nullptr; - mRootTrack = nullptr; - for (const auto &it : animation->_getNodeTrackList()) { - Ogre::NodeAnimationTrack *track = it.second; - Ogre::String trackName = - track->getAssociatedNode()->getName(); - if (trackName == "mixamorig:Hips") { - mHipsTrack = track; - } else if (trackName == "Root") { - mRootTrack = track; - // mRootTracks[i]->removeAllKeyFrames(); - } - } - if (!mRootTrack) { - Ogre::Bone *bone = skeleton->getBone("Root"); - mRootTrack = animation->createNodeTrack( - bone->getHandle(), bone); - Ogre::TransformKeyFrame *kf = - mRootTrack->createNodeKeyFrame(0.0f); - kf->setTranslate(Ogre::Vector3::ZERO); - kf->setRotation(Ogre::Quaternion::IDENTITY); - } - // if (e.has()) // FIXME - // mRootTrack->setListener(mListener); - Ogre::TransformKeyFrame *tkfBeg = - (Ogre::TransformKeyFrame *)mRootTrack->getKeyFrame(0); - Ogre::TransformKeyFrame *tkfEnd = - (Ogre::TransformKeyFrame *)mRootTrack->getKeyFrame( - mRootTrack->getNumKeyFrames() - 1); - mRootTranslation = - tkfEnd->getTranslate() - tkfBeg->getTranslate(); - } -}; -struct Animation { - Ogre::AnimationState *mAnimationState; - Ogre::Animation *mSkelAnimation; - SetupTracks *mTracks; - float m_weight; - float m_accWeight; - flecs::entity e; - Ogre::Skeleton *mSkeleton; - Animation(Ogre::Skeleton *skeleton, Ogre::AnimationState *animState, - Ogre::Animation *skelAnimation, flecs::entity e) - : mTracks(OGRE_NEW SetupTracks(e, skeleton, skelAnimation)) - , mAnimationState(animState) - , mSkelAnimation(skelAnimation) - , m_weight(0) - , m_accWeight(0) - , e(e) - , 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 0 - Ogre::KeyFrame *kf1, *kf2; - Ogre::TransformKeyFrame *k1, *k2; - unsigned short firstKeyIndex; - Ogre::Real timePos = mAnimationState->getTimePosition(); - Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(timePos); - float tm = mTracks->mRootTrack->getKeyFramesAtTime( - index, &kf1, &kf2, &firstKeyIndex); - k1 = static_cast(kf1); - k2 = static_cast(kf2); - Ogre::Vector3 translation; - Ogre::Quaternion rotation; - if (tm == 0.0f) { - rotation = k1->getRotation(); - translation = k1->getTranslate(); - } else { - rotation = Ogre::Quaternion::nlerp( - tm, k1->getRotation(), - k2->getRotation(), true); - translation = k1->getTranslate() + - (k2->getTranslate() - - k1->getTranslate()) * - tm; - } - return translation * mAnimationState->getWeight(); -#endif - if (mAnimationState->getEnabled()) - return rootMotion * mAnimationState->getWeight(); - else - return Ogre::Vector3(0, 0, 0); - } -#if 0 - void updateRootMotion(Ogre::Real timePos) - { - Ogre::KeyFrame *kf1, *kf2; - Ogre::TransformKeyFrame *k1, *k2; - unsigned short firstKeyIndex; - mSkeleton->getBone("Root")->setManuallyControlled(true); - Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(timePos); - float tm = mTracks->mRootTrack->getKeyFramesAtTime( - index, &kf1, &kf2, &firstKeyIndex); - k1 = static_cast(kf1); - k2 = static_cast(kf2); - Ogre::Vector3 translation; - Ogre::Quaternion rotation; - Ogre::Vector3 deltaMotion = - e.get_mut().mBoneMotion; - Ogre::Vector3 prevTranslation = - e.get_mut().mBonePrevMotion; - if (tm == 0.0f) { - rotation = k1->getRotation() * m_weight; - translation = k1->getTranslate() * m_weight; - deltaMotion = translation; - } else { - rotation = Ogre::Quaternion::nlerp( - tm, k1->getRotation() * m_weight, - k2->getRotation() * m_weight, true); - translation = k1->getTranslate() * m_weight + - (k2->getTranslate() * m_weight - - k1->getTranslate() * m_weight) * - tm; - deltaMotion = translation - prevTranslation; - if (deltaMotion.squaredLength() > - translation.squaredLength()) - deltaMotion = translation; - } - e.get_mut().mBoneMotion = deltaMotion; - e.get_mut().mBonePrevMotion = prevTranslation; - e.modified(); -#if 1 - if (timePos > 0.5f && m_weight > 0.2 && translation.squaredLength() > 0.0f) { - std::cout << timePos << " " << m_weight << " " - << e.get() - .mBodyEnt->getMesh() - ->getName() - << " " << deltaMotion << " " - << prevTranslation << std::endl; - std::cout << translation << " " << rotation << std::endl; -// OgreAssert(false, "updateRootMotion"); - } -#endif - mTracks->mRootTrack->getAssociatedNode()->setPosition( - Ogre::Vector3()); - mSkeleton->getBone("Root")->reset(); - } -#endif - void getKeyframeIndices(Ogre::Real timePos, unsigned short *pindex) - { - Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(timePos); - Ogre::KeyFrame *kf1, *kf2; - mTracks->mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, pindex); - - } - bool addTime(float time) - { - bool result = mAnimationState->getEnabled(); - if (!result) - return result; - 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(); -#if 0 - if (thisTime > lastTime) - rootMotion = thisRootPos - lastRootPos; - else - rootMotion = mTracks->mRootTranslation + thisRootPos - lastRootPos; -#else - rootMotion = (thisRootPos - lastRootPos) + (loops * mTracks->mRootTranslation); - -#endif - getKeyframeIndices(mAnimationState->getTimePosition(), &next_index); - // updateRootMotion(mAnimationState->getTimePosition()); - return prev_index != next_index; - } - void reset() - { - 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 children; - float m_weight; - Ogre::String m_name; - std::multimap 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( - 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 stateMap; - std::set 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( - 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( - 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; - } -}; - -#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(); - } -}; - -struct AnimationSystem : AnimationNode { - bool debug; - AnimationSystem(bool debug = false) - : debug(debug) - , m_builder(this, debug) - { - } - std::unordered_map animation_list; - std::vector 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 animation_nodes; - AnimationNode *parent; - std::list parent_stack; - std::unordered_map nodeMap; - std::vector 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; - } - AnimationSystemBuilder *trigger(flecs::entity e, - const Ogre::String &name, - float time, - const Ogre::String &event) - { - struct EventSubscriber : AnimationTriggerSubscriber { - flecs::entity ent; - Ogre::String event; - void operator()(const AnimationTrigger *trigger) - { - ent.get_mut().add(ent, event, - ent, ent); - } - EventSubscriber(flecs::entity e, - const Ogre::String &event) - : ent(e) - , event(event) - { - } - }; - OgreAssert(parent, "bad parent"); - AnimationTrigger *trigger = - new AnimationTrigger(name, time, 0.1f); - EventSubscriber *sub = new EventSubscriber(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( - 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() - { - Ogre::Vector3 motionDelta(0, 0, 0); - int i; - for (i = 0; i < vanimation_list.size(); i++) - motionDelta += vanimation_list[i]->getRootMotionDelta(); - return motionDelta; - } - bool addTime(float time) - { - int i; - preUpdateTriggers(); - bool ret = m_builder.animation_nodes[0]->addTime(time); - for (i = 0; i < m_builder.animationNodeList.size(); i++) { - 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 - } - for (i = 0; i < vanimation_list.size(); i++) { - 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 - } - postUpdateTriggers(time); - return ret; - } - 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 T *get(const Ogre::String &name) - { - return static_cast(m_builder.nodeMap[name]); - } - float getLength() const - { - return m_builder.animation_nodes[0]->getLength(); - } - float getTimePosition() const - { - return m_builder.animation_nodes[0]->getTimePosition(); - } -}; - struct AnimationControl { bool configured; - AnimationSystem *mAnimationSystem; + AnimationSystem::AnimationSystem *mAnimationSystem; }; struct DefaultAnimation { std::vector > animations; diff --git a/src/gamedata/CharacterManagerModule.cpp b/src/gamedata/CharacterManagerModule.cpp index 8378328..ecd4048 100644 --- a/src/gamedata/CharacterManagerModule.cpp +++ b/src/gamedata/CharacterManagerModule.cpp @@ -9,6 +9,7 @@ #include "PlayerActionModule.h" #include "items.h" #include "CharacterManagerModule.h" +#include namespace ECS { @@ -102,76 +103,64 @@ CharacterManagerModule::CharacterManagerModule(flecs::world &ecs) .write() .each([this](flecs::entity town, TerrainItem &item, TownNPCs &npcs) { + ZoneScopedN("UpdateCharacters"); if (!player.is_valid()) return; if (!player.has()) return; - Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([this, - town]() { - flecs::entity player = - ECS::get() - .getPlayer(); - if (!player.is_valid()) - return; - if (!player.has()) - return; - TownNPCs &npcs = town.get_mut(); - Ogre::Vector3 cameraPos = - player.get() - .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( - 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().getPlayer(); + if (!player.is_valid()) + return; + if (!player.has()) + return; + Ogre::Vector3 cameraPos = + player.get() + .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(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( - 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(); - }); + } + 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() && + data.e.has(town)) + createNPCActionNodes( + town, index); + } + } + } + town.modified(); }); } flecs::entity diff --git a/src/gamedata/CharacterModule.cpp b/src/gamedata/CharacterModule.cpp index 2db6bb7..bd2a437 100644 --- a/src/gamedata/CharacterModule.cpp +++ b/src/gamedata/CharacterModule.cpp @@ -9,6 +9,7 @@ #include "CharacterAnimationModule.h" #include "CharacterManagerModule.h" #include "CharacterModule.h" +#include namespace ECS { CharacterModule::CharacterModule(flecs::world &ecs) @@ -78,11 +79,13 @@ CharacterModule::CharacterModule(flecs::world &ecs) ecs.system("UpdateTimer") .kind(flecs::OnUpdate) .each([this](EngineData &eng, CharacterBase &ch) { - ch.mTimer += eng.delta; + ZoneScopedN("UpdateTimer"); + ch.mTimer += eng.delta; }); ecs.system("HandleInput") .kind(flecs::OnUpdate) .each([this](Input &input, Camera &camera) { + ZoneScopedN("HandleInput"); flecs::entity player = ECS::get().getPlayer(); if (!player.is_valid()) @@ -157,24 +160,6 @@ CharacterModule::CharacterModule(flecs::world &ecs) } ECS::get().modified(); }); - ecs.system() - .kind(flecs::OnUpdate) - .with() - .with() - .with() - .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("UpdateBody") .kind(flecs::OnUpdate) @@ -184,7 +169,8 @@ CharacterModule::CharacterModule(flecs::world &ecs) .without() .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 +239,6 @@ CharacterModule::CharacterModule(flecs::world &ecs) anim.configured = false; }); -#if 0 - ecs.system("SetupCharacter") - .kind(flecs::OnUpdate) - .with() - .without() - .without() - .each([](flecs::entity e, const EngineData &eng, - const CharacterLocation &loc, - const CharacterConf &conf, Body2Entity &b2e) { - CharacterBase &ch = e.ensure(); - CharacterBody &body = e.ensure(); - AnimationControl &anim = e.ensure(); - 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( - { { 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( - body.mCollisionShape) - ->addChildShape(transform, shape); - btScalar masses[1] = { 0 }; - btTransform principal; - static_cast( - 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(); - e.add(); - 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( "UpdateCamera") @@ -340,7 +247,8 @@ CharacterModule::CharacterModule(flecs::world &ecs) .with() .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 +319,7 @@ CharacterModule::CharacterModule(flecs::world &ecs) .with() .without() .each([](const EngineData &eng, CharacterBase &ch) { + ZoneScopedN("CheckGround"); #if 0 if (body.mGhostObject) { btVector3 from = diff --git a/src/gamedata/GUIModule.cpp b/src/gamedata/GUIModule.cpp index 369aa96..0c940c6 100644 --- a/src/gamedata/GUIModule.cpp +++ b/src/gamedata/GUIModule.cpp @@ -29,6 +29,7 @@ #include "QuestModule.h" #include "GUIModule.h" #include "GUIModuleCommon.h" +#include namespace ECS { struct GUIListener; @@ -79,7 +80,8 @@ struct GUIListener : public Ogre::RenderTargetListener { void preViewportUpdate(const Ogre::RenderTargetViewportEvent &evt) override { - preview(evt); + ZoneScopedN("GUIModule::preViewportUpdate"); + preview(evt); } void buttons_panel() { diff --git a/src/gamedata/GameData.cpp b/src/gamedata/GameData.cpp index 9c7c27a..910037e 100644 --- a/src/gamedata/GameData.cpp +++ b/src/gamedata/GameData.cpp @@ -28,6 +28,7 @@ #include "QuestModule.h" #include "world-build.h" #include "physics.h" +#include namespace ECS { @@ -286,7 +287,10 @@ void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, void update(float delta) { - ecs.progress(delta); + { + ZoneScoped; + ecs.progress(delta); + } } flecs::world get() { diff --git a/src/gamedata/LuaModule/CMakeLists.txt b/src/gamedata/LuaModule/CMakeLists.txt new file mode 100644 index 0000000..eca29cb --- /dev/null +++ b/src/gamedata/LuaModule/CMakeLists.txt @@ -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 +) diff --git a/src/gamedata/LuaData.cpp b/src/gamedata/LuaModule/LuaData.cpp similarity index 96% rename from src/gamedata/LuaData.cpp rename to src/gamedata/LuaModule/LuaData.cpp index 0f8111b..590211d 100644 --- a/src/gamedata/LuaData.cpp +++ b/src/gamedata/LuaModule/LuaData.cpp @@ -1,3 +1,4 @@ +#include "lua.hpp" #include #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 extern "C" { int luaopen_lpeg(lua_State *L); } @@ -25,51 +25,6 @@ struct FooPosition { return value; } }; -#if 0 -namespace luaaa -{ -template <> struct LuaStack { - 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::get(L, top - 1); - if (strncmp(name, "x", 1) == 0) - result.value.x = - LuaStack::get(L, top); - else if (strncmp(name, "y", 1) == 0) - result.value.y = - LuaStack::get(L, top); - else if (strncmp(name, "z", 1) == 0) - result.value.z = - LuaStack::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::put(L, "x"); - LuaStack::put(L, v.value.x); - lua_rawset(L, -3); - LuaStack::put(L, "y"); - LuaStack::put(L, v.value.y); - lua_rawset(L, -3); - LuaStack::put(L, "z"); - LuaStack::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(); +#endif + OgreAssert(false, "Not implemented"); return 0; } else if (command == "params-set") { diff --git a/src/gamedata/LuaData.h b/src/gamedata/LuaModule/LuaData.h similarity index 100% rename from src/gamedata/LuaData.h rename to src/gamedata/LuaModule/LuaData.h diff --git a/src/gamedata/PhysicsModule.cpp b/src/gamedata/PhysicsModule.cpp index 5cbaa15..522a042 100644 --- a/src/gamedata/PhysicsModule.cpp +++ b/src/gamedata/PhysicsModule.cpp @@ -19,6 +19,7 @@ #include "EventModule.h" #include "TerrainModule.h" #include "PhysicsModule.h" +#include namespace ECS { struct PhysicsShape { @@ -54,8 +55,18 @@ PhysicsModule::PhysicsModule(flecs::world &ecs) ecs.component(); ecs.component(); - ecs.component(); - ecs.component(); + ecs.component().on_remove([](flecs::entity e, + CharacterBody &body) { + JPH::Character *ch = + static_cast(body.ch.get()); + if (ch) { + if (e.has()) + e.remove(); + JoltPhysicsWrapper::getSingleton().destroyCharacter(ch); + body.ch = nullptr; + } + }); + ecs.component(); ecs.component(); ecs.component().add(flecs::Singleton); ecs.component(); @@ -64,6 +75,7 @@ PhysicsModule::PhysicsModule(flecs::world &ecs) ecs.system("physics_update") .kind(PhysicsUpdate) .each([&](EngineData &e, Physics &ph) { + ZoneScopedN("physics"); ph.physics->update(e.delta); }); ecs.observer( @@ -152,6 +164,9 @@ PhysicsModule::PhysicsModule(flecs::world &ecs) JoltPhysicsWrapper::getSingleton().removeBody(id); if (e.has() || e.has()) return; + if (JoltPhysicsWrapper::getSingleton().bodyIsCharacter( + id)) + return; JoltPhysicsWrapper::getSingleton().destroyBody(id); std::cout << "body destroyed" << std::endl; }); @@ -164,7 +179,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs) .write() .each([](flecs::entity e, const EngineData &eng, const CharacterBase &base) { - CharacterBody &b = e.ensure(); + ZoneScopedN("SetupCharacterPh"); + CharacterBody &b = e.ensure(); b.ch.reset(JoltPhysicsWrapper::getSingleton() .createCharacter(base.mBodyNode, 1.75f, 0.23f)); @@ -267,20 +283,23 @@ PhysicsModule::PhysicsModule(flecs::world &ecs) ECS::modified(); }); }); + // FIXME: convert to normal configure to prevent multiple instances ecs.system("init_water") .kind(PhysicsPreUpdate) .with() .with() .without() .each([this](const EngineData &eng) { - ECS::get().set({}); + ZoneScopedN("init_water"); + ECS::get().set({}); }); ecs.system("update_water") .kind(PhysicsPostUpdate) .with() .with() .each([this](const EngineData &eng, WaterBody &body) { - const WaterSurface &water = ECS::get(); + ZoneScopedN("update_water"); + const WaterSurface &water = ECS::get(); body.inWater.clear(); JoltPhysicsWrapper::getSingleton().broadphaseQuery( eng.delta, @@ -295,8 +314,11 @@ PhysicsModule::PhysicsModule(flecs::world &ecs) .with() .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(); + ZoneTextF("in water"); + } }); ecs.system("update_water_status2") .kind(PhysicsPostUpdate) @@ -305,8 +327,11 @@ PhysicsModule::PhysicsModule(flecs::world &ecs) .without() .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(); + ZoneTextF("not in water"); + } }); ecs.system( "update_water_character1") @@ -316,10 +341,13 @@ PhysicsModule::PhysicsModule(flecs::world &ecs) .with() .each([this](flecs::entity e, const CharacterBody &ch, const WaterBody &body) { - JPH::Character *chptr = + ZoneScopedN("update_water_character1"); + JPH::Character *chptr = static_cast(ch.ch.get()); - if (!body.isInWater(chptr->GetBodyID())) + if (!body.isInWater(chptr->GetBodyID())) { e.remove(); + ZoneTextF("not in water"); + } }); ecs.system( "update_water_character2") @@ -329,10 +357,13 @@ PhysicsModule::PhysicsModule(flecs::world &ecs) .without() .each([this](flecs::entity e, const CharacterBody &ch, const WaterBody &body) { - JPH::Character *chptr = + ZoneScopedN("update_water_character2"); + JPH::Character *chptr = static_cast(ch.ch.get()); - if (body.isInWater(chptr->GetBodyID())) + if (body.isInWater(chptr->GetBodyID())) { e.add(); + ZoneTextF("in water"); + } }); ecs.system("update_water_boat_enable") @@ -342,7 +373,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 +387,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(); + ZoneScopedN("update_water_boat_buoyancy"); + const WaterSurface &water = ECS::get(); float b = 1.0f, drag = 0.5f, adrag = 0.5f; float level = 0.25f; float my = JoltPhysicsWrapper::getSingleton() @@ -425,7 +459,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs) .with() .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(ch.ch.get()); JPH::BodyID id = chptr->GetBodyID(); if (JoltPhysicsWrapper::getSingleton().isActive(id)) { @@ -467,13 +502,15 @@ PhysicsModule::PhysicsModule(flecs::world &ecs) eng.delta); } }); - ecs.system("UpdatePhysics") + ecs.system( + "UpdateCharacterPhysicsState") .kind(flecs::OnUpdate) .with() .write() .each([](flecs::entity e, const EngineData &eng, const CharacterBody &body) { - if (e.has()) + ZoneScopedN("UpdateCharacterPhysicsState"); + if (e.has()) PhysicsModule::controlPhysics(e, false); else PhysicsModule::controlPhysics(e, true); @@ -489,7 +526,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() && + ZoneScopedN("HandleVelocity"); + if (e.has() && chbase.mBodyNode->_getDerivedPosition().y > -0.5f) e.remove(); Ogre::Vector3 v = gr.velocity; @@ -519,7 +557,27 @@ PhysicsModule::PhysicsModule(flecs::world &ecs) JoltPhysics::convert(v)); gr.velocity = Ogre::Vector3::ZERO; }); - ecs.system("HandleSubmerge") + .kind(flecs::OnUpdate) + .with() + .with() + .with() + .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("HandleVelocityNoPhysics") .kind(PhysicsPostUpdate) .with() @@ -529,7 +587,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 +607,12 @@ PhysicsModule::PhysicsModule(flecs::world &ecs) ch.mBodyNode->_getDerivedPosition().y > -0.5f) { e.remove(); ch.is_submerged = false; - } - if (!e.has() && ch.is_submerged) + ZoneTextF("remove in water"); + } + if (!e.has() && ch.is_submerged) { ch.is_submerged = false; + ZoneTextF("not submerged"); + } }); } flecs::entity PhysicsModule::createTerrainChunkBody(Ogre::SceneNode *node, diff --git a/src/gamedata/PlayerActionModule.cpp b/src/gamedata/PlayerActionModule.cpp index 705b8e8..1e17e82 100644 --- a/src/gamedata/PlayerActionModule.cpp +++ b/src/gamedata/PlayerActionModule.cpp @@ -14,6 +14,7 @@ #include "LuaData.h" #include "PhysicsModule.h" #include "PlayerActionModule.h" +#include namespace ECS { @@ -297,6 +298,7 @@ PlayerActionModule::PlayerActionModule(flecs::world &ecs) .each([](ActionNodeList &list) { if (list.isBusy()) return; + ZoneScopedN("updateNodeList"); if (list.nodes.size() > 0) { Ogre::SceneNode *cameraNode = ECS::get().mCameraNode; @@ -320,6 +322,7 @@ PlayerActionModule::PlayerActionModule(flecs::world &ecs) .kind(flecs::OnUpdate) .each([this](ActionNodeList &list, const Input &input) { std::lock_guard lock(*list.nodeMutex); + ZoneScopedN("ActivateActionNode"); if (input.control & 32) std::cout << "act pressed" << std::endl; if (list.isBusy()) @@ -365,6 +368,7 @@ PlayerActionModule::PlayerActionModule(flecs::world &ecs) ecs.system("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); @@ -745,6 +749,7 @@ void ActionNodeList::updateDynamicNodes() void ActionNodeList::build() { + ZoneScoped; std::lock_guard lock(*nodeMutex); indexObj = std::make_shared(dynamicNodes); indexObj->index.buildIndex(); diff --git a/src/gamedata/StaticGeometryModule.cpp b/src/gamedata/StaticGeometryModule.cpp index 64dde6f..18e8172 100644 --- a/src/gamedata/StaticGeometryModule.cpp +++ b/src/gamedata/StaticGeometryModule.cpp @@ -15,6 +15,7 @@ #include "PhysicsModule.h" #include "items.h" #include "StaticGeometryModule.h" +#include namespace ECS { @@ -461,16 +462,14 @@ nlohmann::json &StaticGeometryModule::getTemplates() void StaticGeometryModule::updateItemGeometry(flecs::entity e) { - if (e.has()) - return; - e.add(); - 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(); - }); + // We add this as task to reduce UI load + Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([e]() { + ZoneScopedN("updateItemGeometry"); + if (e.has()) + return; + e.add(); + Geometry::updateItemGeometry(e); + e.remove(); }); } @@ -487,22 +486,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() - .mScnMgr->createEntity(mesh); - captData.geo->addEntity(ent, captData.position, - captData.rotation); - ECS::get().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().mScnMgr->createEntity(mesh); + data.geo->addEntity(ent, data.position, data.rotation); + ECS::get().mScnMgr->destroyEntity(ent); }); } struct TiledMeshes { diff --git a/src/gamedata/WaterModule.cpp b/src/gamedata/WaterModule.cpp index a0bf13a..3a19cf1 100644 --- a/src/gamedata/WaterModule.cpp +++ b/src/gamedata/WaterModule.cpp @@ -10,6 +10,7 @@ #include "GameData.h" #include "Components.h" #include "WaterModule.h" +#include 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().add(flecs::Singleton); - ecs.component() - .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(); - const WaterSurface &water = ECS::get(); - - eng.mWorld->attachCollisionObject(body.mWaterBody, - water.mWaterEnt, 16, - 0x7fffffff & ~2); - WaterPhysicsAction *action = - OGRE_NEW WaterPhysicsAction(body.mWaterBody); - body.action = action; - ECS::get() - .get() - .mWorld->getBtWorld() - ->addAction(body.action); -#endif - ECS::get().add(); - }) - .add(flecs::Singleton); -#endif - ecs.system("UpdateWater") + ecs.system( + "UpdateWaterPosition") .kind(flecs::OnUpdate) .with() + .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("UpdateWater") + .kind(flecs::OnUpdate) + .with() + .each([](WaterSurface &water) { + static int updateViewport = 0; + ZoneScopedN("UpdateWaterRender"); + water.mViewports[updateViewport++]->update(); + if (updateViewport > 1) + updateViewport = 0; }); -#if 0 - ecs.system( - "UpdateWaterBody") - .kind(flecs::OnUpdate) - .with() - .each([this](const EngineData &eng, const WaterSurface &water, - WaterBody &body) { - int i; -#if 0 - OgreAssert(body.mWaterBody, "Water not ready"); - std::set 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( - 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( - body.action) - ->mManifoldArray.size() - << "\n"; - for (int j = 0; - j < static_cast(body.action) - ->mManifoldArray.size(); - j++) { - btPersistentManifold *manifold = - static_cast( - 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(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::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 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 currentOverlaps; - std::set::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 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( - collisionPair.m_pProxy0->m_clientObject); - const btCollisionObject *objB = - static_cast( - 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(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(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::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(action)->isInWater(body); -} -#endif -} \ No newline at end of file diff --git a/src/gamedata/items/CMakeLists.txt b/src/gamedata/items/CMakeLists.txt index d765a64..0b9fff8 100644 --- a/src/gamedata/items/CMakeLists.txt +++ b/src/gamedata/items/CMakeLists.txt @@ -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 ) diff --git a/src/gamedata/items/town.cpp b/src/gamedata/items/town.cpp index dd418d7..e1f0edb 100644 --- a/src/gamedata/items/town.cpp +++ b/src/gamedata/items/town.cpp @@ -22,6 +22,7 @@ #include "CharacterAIModule.h" #include "items.h" #include "town.h" +#include /* * TODO: Create doors and handle them via script. @@ -3363,6 +3364,7 @@ struct TownPlazza : TownTask { int index, Ogre::SceneNode *sceneNode, Ogre::StaticGeometry *geo) { + ZoneScopedN("createTownPlazza"); struct QueueData { Procedural::TriangleBuffer tb; Ogre::Vector3 position; @@ -3460,6 +3462,8 @@ struct TownPlazza : TownTask { .getWorkQueue() ->addMainThreadTask([queueData, localPosition, localRotation]() { + ZoneScopedN( + "createTownPlazza::MainThread"); Ogre::Vector3 worldPlazzaCenter = queueData->position + localPosition; @@ -3504,6 +3508,7 @@ struct TownLots : TownTask { int index, Ogre::SceneNode *sceneNode, Ogre::StaticGeometry *geo) { + ZoneScopedN("createTownLots"); struct QueueData { Procedural::TriangleBuffer tb; Ogre::Vector3 position; @@ -3647,6 +3652,8 @@ struct TownLots : TownTask { Ogre::Root::getSingleton() .getWorkQueue() ->addMainThreadTask([promise, radius]() { + ZoneScopedN( + "createTownLots::MainThread"); float distance = radius; for (auto &lot : lots) { std::cout @@ -3877,6 +3884,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; @@ -4803,6 +4811,7 @@ struct TownRoofs : TownTask { } Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([radius, promise]() { + ZoneScopedN("createTownRoofs::MainThread"); { int count = 0; for (auto lot : lots) { @@ -5862,6 +5871,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 townTasks; @@ -5924,6 +5934,7 @@ void createTown(flecs::entity e, Ogre::SceneNode *sceneNode, m->wait(); Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask( [geo]() { + ZoneScopedN("createTown::MainThread"); for (auto m : townTasks) { delete m; } diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp index 33780f7..9011ff6 100644 --- a/src/physics/physics.cpp +++ b/src/physics/physics.cpp @@ -1510,6 +1510,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() @@ -1806,5 +1819,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::msSingleton = 0; diff --git a/src/physics/physics.h b/src/physics/physics.h index 48d59c2..8e1a316 100644 --- a/src/physics/physics.h +++ b/src/physics/physics.h @@ -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