Intagrated Tracy, debugged animations

This commit is contained in:
2026-02-12 14:00:05 +03:00
parent 7947690e80
commit 74a1adfb27
25 changed files with 1553 additions and 2109 deletions

View File

@@ -29,6 +29,7 @@ find_package(assimp REQUIRED CONFIG)
find_package(OgreProcedural REQUIRED CONFIG)
find_package(pugixml REQUIRED CONFIG)
find_package(flecs REQUIRED CONFIG)
find_package(Tracy REQUIRED CONFIG)
add_library(fix::assimp INTERFACE IMPORTED)
set_target_properties(fix::assimp PROPERTIES
@@ -81,11 +82,12 @@ add_executable(Game Game.cpp ${WATER_SRC})
target_include_directories(Game PRIVATE src/gamedata)
target_link_libraries(Game OgreBites OgrePaging OgreTerrain OgreMeshLodGenerator
OgreProcedural::OgreProcedural
OgreCrowd
GameData
sound
sceneloader physics
OgreCrowd
sceneloader physics lua
flecs::flecs_static
Tracy::TracyClient
-Wl,--as-needed
)
if(OGRE_STATIC)

View File

@@ -21,6 +21,7 @@
#include "PhysicsModule.h"
#include "physics.h"
#include "sound.h"
#include <tracy/Tracy.hpp>
class App;
class SkyRenderer : public Ogre::SceneManager::Listener {
protected:
@@ -477,6 +478,7 @@ public:
}
void updateWorld(float delta)
{
ZoneScoped;
if (!ECS::get().has<ECS::GUI>())
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<ECS::GUI>().narrationBox)) {
mApp->updateWorld(evt.timeSinceLastFrame);
if (mInitDelay >= 0.0f)
mInitDelay -= evt.timeSinceLastFrame;
}
{
ZoneScopedN("frameRendered");
if (fps_timer.getMilliseconds() > 1000.0f) {
std::cout << "FPS: "
<< mApp->getRenderWindow()
->getStatistics()
.lastFPS
<< " ";
std::cout << "Draw calls: "
<< mApp->getRenderWindow()
->getStatistics()
.batchCount
<< " ";
fps_timer.reset();
std::cout << "Drops: "
<< mApp->getRenderWindow()
->getStatistics()
.vBlankMissCount
<< "\n";
fps_timer.reset();
}
if (!isGuiEnabled() ||
(isGuiEnabled() && ECS::get<ECS::GUI>().narrationBox)) {
mApp->updateWorld(evt.timeSinceLastFrame);
if (mInitDelay >= 0.0f)
mInitDelay -= evt.timeSinceLastFrame;
}
if (!isGuiEnabled() && ECS::get().has<ECS::Input>()) {
ECS::Input &input = ECS::get().get_mut<ECS::Input>();
input.control = control;
input.mouse = mouse;
input.mouse_abs = mouse_abs;
mouse.x = 0;
mouse.y = 0;
input.wheel_y = wheel_y;
wheel_y = 0;
input.mouse_moved = mouse_moved;
input.wheel_moved = wheel_moved;
if (!isGuiEnabled() && ECS::get().has<ECS::Input>()) {
ECS::Input &input = ECS::get().get_mut<ECS::Input>();
input.control = control;
input.mouse = mouse;
input.mouse_abs = mouse_abs;
mouse.x = 0;
mouse.y = 0;
input.wheel_y = wheel_y;
wheel_y = 0;
input.mouse_moved = mouse_moved;
input.wheel_moved = wheel_moved;
}
}
#ifdef USE_RENDER_LOOP
FrameMark;
#endif
}
int main()
@@ -784,7 +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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,8 @@
#include "TerrainModule.h"
#include "WaterModule.h"
#include "world-build.h"
#include "AnimationSystem.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
@@ -21,7 +23,8 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
ecs.system<const CharacterBase, AnimationControl>("HandleAnimations")
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, const CharacterBase &ch,
AnimationControl &anim) {
AnimationControl &anim) {
ZoneScopedN("HandleAnimations");
if (!anim.configured) {
int i, j;
e.set<EventData>({});
@@ -32,19 +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<AnimationNodeStateMachine>("main")
->get<AnimationSystem::
AnimationNodeStateMachine>(
"main")
->setAnimation("locomotion", true);
anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
->get<AnimationSystem::
AnimationNodeStateMachine>(
"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<RootMotionListener *>(
anim.mListener)
->getDeltaMotion();
ch.mRootBone->setPosition(Ogre::Vector3::ZERO);
Ogre::Vector3 d = offset - ch.mBonePrevMotion;
ch.mBonePrevMotion = offset;
std::cout << "length: " << d.length() << std::endl;
if (d.squaredLength() > 0.02f * 0.02f)
d = offset;
if (d.squaredLength() > 0.02f * 0.02f)
d = Ogre::Vector3::ZERO;
std::cout << "length2: " << d.length() << std::endl;
OgreAssert(d.length() < 0.5f, "bad offset");
ch.mBoneMotion = d;
#endif
#if 0
if (result) {
if (d.squaredLength() > 0.0f)
ch.mBoneMotion =
ch.mRootBone->getPosition() -
ch.mBoneMotion;
else
ch.mBoneMotion =
ch.mRootBone->getPosition();
} else {
ch.mBoneMotion = ch.mRootBone->getPosition() -
ch.mBoneMotion;
}
#endif
#undef VDEBUG
#ifdef VDEBUG
@@ -224,10 +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<const EngineData, CharacterBase, CharacterVelocity>(
"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<const EngineData, const AnimationControl,
const CharacterBase, CharacterVelocity>("HandleSwimming")
.kind(flecs::OnUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.with<CharacterBuoyancy>()
.without<CharacterDisablePhysics>()
.without<CharacterInActuator>()
.each([this](flecs::entity e, const EngineData &eng,
const AnimationControl &anim,
const CharacterBase &ch, CharacterVelocity &gr) {
if (anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
"locomotion-state")
->getCurrentState() == "swimming") {
float h = Ogre::Math::Clamp(
0.0f - ch.mBodyNode->getPosition().y,
0.0f, 2000.0f);
if (h > 0.05 && h < 2.0f)
gr.gvelocity.y += 0.1f * (h + 1.0f) *
h * eng.delta;
}
});
#endif
ecs.system<const EngineData, CharacterBase, AnimationControl,
CharacterVelocity>("HandleRootMotion")
.kind(flecs::OnUpdate)
@@ -328,19 +262,28 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
return;
if (!anim.mAnimationSystem)
return;
AnimationNodeStateMachine *state_machine =
anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
"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<InWater>())
next_state = "treading_water";
if (current_state != "idle" && !ch.is_submerged)
if (current_state != "idle" && !e.has<InWater>())
next_state = "idle";
state_machine->setAnimation(next_state);
{
ZoneScoped;
ZoneTextF("animation: next_state: %s %d %d",
next_state.c_str(),
(int)ch.is_submerged,
(int)e.has<InWater>());
}
});
ecs.system<const CharacterBase, const CharacterInActuator,
AnimationControl>("HandlePlayerAnimationsActuator")
@@ -349,15 +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<AnimationNodeStateMachine>(
->get<AnimationSystem::
AnimationNodeStateMachine>(
"main");
AnimationNodeStateMachine *actuator_sm =
AnimationSystem::AnimationNodeStateMachine *actuator_sm =
anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
->get<AnimationSystem::
AnimationNodeStateMachine>(
"actuator-state");
Ogre::String current_state = main_sm->getCurrentState();
if (current_state != "actuator")
@@ -372,13 +318,15 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
.without<CharacterControlDisable>()
.each([](flecs::entity e, const CharacterBase &ch,
AnimationControl &anim) {
if (!anim.configured)
ZoneScopedN("HandlePlayerAnimationsNoActuator");
if (!anim.configured)
return;
if (!anim.mAnimationSystem)
return;
AnimationNodeStateMachine *main_sm =
AnimationSystem::AnimationNodeStateMachine *main_sm =
anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
->get<AnimationSystem::
AnimationNodeStateMachine>(
"main");
Ogre::String current_state = main_sm->getCurrentState();
if (current_state != "locomotion")
@@ -395,10 +343,12 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
const CharacterBase &ch, AnimationControl &anim) {
if (!anim.configured)
return;
AnimationNodeStateMachine *state_machine =
anim.mAnimationSystem
->get<AnimationNodeStateMachine>(
"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<Character>()
.each([](flecs::entity e, EventData &evt) {
for (auto ev : evt.events) {
ZoneScopedN("HandleEvents");
for (auto ev : evt.events) {
std::cout << "character event: " << ev.event
<< std::endl;
/* parse character events */
@@ -600,10 +552,11 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
true) {
const AnimationControl &control =
param_e->get().get<AnimationControl>();
AnimationNodeStateMachine *sm =
control.mAnimationSystem
->get<AnimationNodeStateMachine>(
param_node->get());
AnimationSystem::AnimationNodeStateMachine *sm =
control.mAnimationSystem->get<
AnimationSystem::
AnimationNodeStateMachine>(
param_node->get());
bool reset = false;
if (args.size() == 4) {
GameWorld::ValueParameter<bool>
@@ -622,6 +575,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
}
};
ECS::get_mut<GameWorld>().add_command<AnimationSetCommand>(
"set_animation_state");
"set_animation_state");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@
#include "PlayerActionModule.h"
#include "items.h"
#include "CharacterManagerModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
@@ -102,76 +103,64 @@ CharacterManagerModule::CharacterManagerModule(flecs::world &ecs)
.write<LivesIn>()
.each([this](flecs::entity town, TerrainItem &item,
TownNPCs &npcs) {
ZoneScopedN("UpdateCharacters");
if (!player.is_valid())
return;
if (!player.has<CharacterBase>())
return;
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([this,
town]() {
flecs::entity player =
ECS::get<CharacterManagerModule>()
.getPlayer();
if (!player.is_valid())
return;
if (!player.has<CharacterBase>())
return;
TownNPCs &npcs = town.get_mut<TownNPCs>();
Ogre::Vector3 cameraPos =
player.get<CharacterBase>()
.mBodyNode
->_getDerivedPosition();
for (auto &npc : npcs.npcs) {
int index = npc.first;
TownNPCs::NPCData &data = npc.second;
Ogre::Vector3 npcPosition =
data.position;
Ogre::Quaternion npcOrientation =
data.orientation;
if (cameraPos.squaredDistance(
npcPosition) < 10000.0f) {
if (!data.e.is_valid()) {
data.e = createCharacterData(
data.model,
data.position,
data.orientation);
data.e.add<LivesIn>(
town);
break;
}
}
if (cameraPos.squaredDistance(
npcPosition) > 22500.0f) {
if (data.e.is_valid()) {
data.e.destruct();
data.e =
flecs::entity();
break;
}
flecs::entity player =
ECS::get<CharacterManagerModule>().getPlayer();
if (!player.is_valid())
return;
if (!player.has<CharacterBase>())
return;
Ogre::Vector3 cameraPos =
player.get<CharacterBase>()
.mBodyNode->_getDerivedPosition();
for (auto &npc : npcs.npcs) {
int index = npc.first;
TownNPCs::NPCData &data = npc.second;
Ogre::Vector3 npcPosition = data.position;
Ogre::Quaternion npcOrientation =
data.orientation;
if (cameraPos.squaredDistance(npcPosition) <
10000.0f) {
if (!data.e.is_valid()) {
data.e = createCharacterData(
data.model,
data.position,
data.orientation);
data.e.add<LivesIn>(town);
break;
}
}
for (auto &npc : npcs.npcs) {
int index = npc.first;
TownNPCs::NPCData &data = npc.second;
Ogre::Vector3 npcPosition =
data.position;
Ogre::Quaternion npcOrientation =
data.orientation;
if (cameraPos.squaredDistance(
npcPosition) < 10000.0f) {
if (data.e.is_valid()) {
if (data.e.has<
CharacterBase>() &&
data.e.has<LivesIn>(
town))
createNPCActionNodes(
town,
index);
}
if (cameraPos.squaredDistance(npcPosition) >
22500.0f) {
if (data.e.is_valid()) {
data.e.destruct();
data.e = flecs::entity();
break;
}
}
town.modified<TownNPCs>();
});
}
for (auto &npc : npcs.npcs) {
int index = npc.first;
TownNPCs::NPCData &data = npc.second;
Ogre::Vector3 npcPosition = data.position;
Ogre::Quaternion npcOrientation =
data.orientation;
if (cameraPos.squaredDistance(npcPosition) <
10000.0f) {
if (data.e.is_valid()) {
if (data.e.has<CharacterBase>() &&
data.e.has<LivesIn>(town))
createNPCActionNodes(
town, index);
}
}
}
town.modified<TownNPCs>();
});
}
flecs::entity

View File

@@ -9,6 +9,7 @@
#include "CharacterAnimationModule.h"
#include "CharacterManagerModule.h"
#include "CharacterModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
CharacterModule::CharacterModule(flecs::world &ecs)
@@ -78,11 +79,13 @@ CharacterModule::CharacterModule(flecs::world &ecs)
ecs.system<EngineData, CharacterBase>("UpdateTimer")
.kind(flecs::OnUpdate)
.each([this](EngineData &eng, CharacterBase &ch) {
ch.mTimer += eng.delta;
ZoneScopedN("UpdateTimer");
ch.mTimer += eng.delta;
});
ecs.system<Input, Camera>("HandleInput")
.kind(flecs::OnUpdate)
.each([this](Input &input, Camera &camera) {
ZoneScopedN("HandleInput");
flecs::entity player =
ECS::get<CharacterManagerModule>().getPlayer();
if (!player.is_valid())
@@ -157,24 +160,6 @@ CharacterModule::CharacterModule(flecs::world &ecs)
}
ECS::get().modified<ECS::Input>();
});
ecs.system<CharacterBase>()
.kind(flecs::OnUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.each([this](flecs::entity e, CharacterBase &ch) {
float full_subm = 2.0f;
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
float current_subm = -Ogre::Math::Clamp(
pos.y + Ogre::Math::Sin(ch.mTimer * 0.13f +
130.0f) *
0.07f,
-full_subm, 0.0f);
if (current_subm > 0.9f)
ch.is_submerged = true;
else if (current_subm < 0.8f)
ch.is_submerged = false;
});
#define TURN_SPEED 500.0f // character turning in degrees per second
ecs.system<const Input, const Camera, CharacterBase>("UpdateBody")
.kind(flecs::OnUpdate)
@@ -184,7 +169,8 @@ CharacterModule::CharacterModule(flecs::world &ecs)
.without<CharacterControlDisable>()
.each([](flecs::entity e, const Input &input,
const Camera &camera, CharacterBase &ch) {
ch.mGoalDirection = Ogre::Vector3::ZERO;
ZoneScopedN("UpdateBody");
ch.mGoalDirection = Ogre::Vector3::ZERO;
float delta = e.world().delta_time();
if (!input.motion.zeroLength()) {
// calculate actually goal direction in world based on player's key directions
@@ -253,85 +239,6 @@ CharacterModule::CharacterModule(flecs::world &ecs)
anim.configured = false;
});
#if 0
ecs.system<const EngineData, const CharacterLocation,
const CharacterConf, Body2Entity>("SetupCharacter")
.kind(flecs::OnUpdate)
.with<Character>()
.without<CharacterBase>()
.without<CharacterBody>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterLocation &loc,
const CharacterConf &conf, Body2Entity &b2e) {
CharacterBase &ch = e.ensure<CharacterBase>();
CharacterBody &body = e.ensure<CharacterBody>();
AnimationControl &anim = e.ensure<AnimationControl>();
ch.mBodyEnt = eng.mScnMgr->createEntity(conf.type);
ch.mBodyNode = eng.mScnMgr->getRootSceneNode()
->createChildSceneNode();
ch.mBodyNode->setOrientation(loc.orientation);
ch.mBodyNode->setPosition(loc.position);
ch.mBodyNode->attachObject(ch.mBodyEnt);
ch.mSkeleton = ch.mBodyEnt->getSkeleton();
OgreAssert(ch.mBodyEnt->getSkeleton()->hasBone("Root"),
"No root bone");
OgreAssert(ch.mSkeleton->hasBone("Root"),
"No root bone");
ch.mRootBone = ch.mSkeleton->getBone("Root");
OgreAssert(ch.mRootBone, "No root bone");
// body.mController = nullptr;
ch.mBoneMotion = Ogre::Vector3::ZERO;
ch.mBonePrevMotion = Ogre::Vector3::ZERO;
e.set<CharacterVelocity>(
{ { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } });
body.checkGround = false;
body.checkGroundResult = false;
#if 0
body.mCollisionShape = nullptr;
body.mGhostObject = nullptr;
body.mController = nullptr;
body.mGhostObject = new btPairCachingGhostObject();
b2e.entities[body.mGhostObject] = e;
body.mCollisionShape = new btCompoundShape(false);
body.mGhostObject->setCollisionShape(
body.mCollisionShape);
{
btVector3 inertia(0, 0, 0);
// mCollisionShape = new btCompoundShape();
btScalar height = 1.0f;
btScalar radius = 0.3f;
btCapsuleShape *shape = new btCapsuleShape(
radius, 2 * height - 2 * radius);
btTransform transform;
transform.setIdentity();
transform.setOrigin(btVector3(0, 1, 0));
static_cast<btCompoundShape *>(
body.mCollisionShape)
->addChildShape(transform, shape);
btScalar masses[1] = { 0 };
btTransform principal;
static_cast<btCompoundShape *>(
body.mCollisionShape)
->calculatePrincipalAxisTransform(
masses, principal, inertia);
}
body.mGhostObject->setCollisionFlags(body.mGhostObject->getCollisionFlags() | btCollisionObject::CF_CHARACTER_OBJECT | btCollisionObject::CF_KINEMATIC_OBJECT
/*btCollisionObject::CF_KINEMATIC_OBJECT |
btCollisionObject::CF_NO_CONTACT_RESPONSE */);
body.mGhostObject->setActivationState(
DISABLE_DEACTIVATION);
eng.mWorld->attachCollisionObject(
body.mGhostObject, ch.mBodyEnt, 1, 0x7FFFFFFF);
OgreAssert(body.mGhostObject, "Need GhostObject");
OgreAssert(body.mCollisionShape, "No collision shape");
#endif
e.add<CharacterGravity>();
e.add<CharacterBuoyancy>();
anim.configured = false;
// OgreAssert(body.mGhostObject->hasContactResponse(),
// "need contact response");
});
#endif
#define CAM_HEIGHT 1.6f // height of camera above character's center of mass
ecs.system<const EngineData, Camera, const CharacterBase>(
"UpdateCamera")
@@ -340,7 +247,8 @@ CharacterModule::CharacterModule(flecs::world &ecs)
.with<GroundCheckReady>()
.each([](const EngineData &eng, Camera &camera,
const CharacterBase &ch) {
float delta = eng.delta;
ZoneScopedN("UpdateCamera");
float delta = eng.delta;
if (!camera.configured) {
// create a pivot at roughly the character's shoulder
camera.mCameraPivot =
@@ -411,6 +319,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
.with<Player>()
.without<GroundCheckReady>()
.each([](const EngineData &eng, CharacterBase &ch) {
ZoneScopedN("CheckGround");
#if 0
if (body.mGhostObject) {
btVector3 from =

View File

@@ -29,6 +29,7 @@
#include "QuestModule.h"
#include "GUIModule.h"
#include "GUIModuleCommon.h"
#include <tracy/Tracy.hpp>
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()
{

View File

@@ -28,6 +28,7 @@
#include "QuestModule.h"
#include "world-build.h"
#include "physics.h"
#include <tracy/Tracy.hpp>
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()
{

View File

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

View File

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

View File

@@ -19,6 +19,7 @@
#include "EventModule.h"
#include "TerrainModule.h"
#include "PhysicsModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
struct PhysicsShape {
@@ -54,8 +55,18 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
ecs.component<PhysicsMeshPtr>();
ecs.component<PhysicsHeightfieldData>();
ecs.component<CharacterBody>();
ecs.component<TriggerBody>();
ecs.component<CharacterBody>().on_remove([](flecs::entity e,
CharacterBody &body) {
JPH::Character *ch =
static_cast<JPH::Character *>(body.ch.get());
if (ch) {
if (e.has<JPH::BodyID>())
e.remove<JPH::BodyID>();
JoltPhysicsWrapper::getSingleton().destroyCharacter(ch);
body.ch = nullptr;
}
});
ecs.component<TriggerBody>();
ecs.component<CharacterVelocity>();
ecs.component<WaterBody>().add(flecs::Singleton);
ecs.component<CachedMass>();
@@ -64,6 +75,7 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
ecs.system<EngineData, Physics>("physics_update")
.kind(PhysicsUpdate)
.each([&](EngineData &e, Physics &ph) {
ZoneScopedN("physics");
ph.physics->update(e.delta);
});
ecs.observer<const EngineData, PhysicsMeshName>(
@@ -152,6 +164,9 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
JoltPhysicsWrapper::getSingleton().removeBody(id);
if (e.has<CharacterBase>() || e.has<Character>())
return;
if (JoltPhysicsWrapper::getSingleton().bodyIsCharacter(
id))
return;
JoltPhysicsWrapper::getSingleton().destroyBody(id);
std::cout << "body destroyed" << std::endl;
});
@@ -164,7 +179,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.write<JPH::BodyID>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterBase &base) {
CharacterBody &b = e.ensure<CharacterBody>();
ZoneScopedN("SetupCharacterPh");
CharacterBody &b = e.ensure<CharacterBody>();
b.ch.reset(JoltPhysicsWrapper::getSingleton()
.createCharacter(base.mBodyNode,
1.75f, 0.23f));
@@ -267,20 +283,23 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
ECS::modified<LuaEvent>();
});
});
// FIXME: convert to normal configure to prevent multiple instances
ecs.system<const EngineData>("init_water")
.kind(PhysicsPreUpdate)
.with<TerrainReady>()
.with<WaterAlmostReady>()
.without<WaterBody>()
.each([this](const EngineData &eng) {
ECS::get().set<WaterBody>({});
ZoneScopedN("init_water");
ECS::get().set<WaterBody>({});
});
ecs.system<const EngineData, WaterBody>("update_water")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
.with<WaterAlmostReady>()
.each([this](const EngineData &eng, WaterBody &body) {
const WaterSurface &water = ECS::get<WaterSurface>();
ZoneScopedN("update_water");
const WaterSurface &water = ECS::get<WaterSurface>();
body.inWater.clear();
JoltPhysicsWrapper::getSingleton().broadphaseQuery(
eng.delta,
@@ -295,8 +314,11 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.with<InWater>()
.each([this](flecs::entity e, const JPH::BodyID &id,
const WaterBody &body) {
if (!body.isInWater(id))
ZoneScopedN("update_water_status1");
if (!body.isInWater(id)) {
e.remove<InWater>();
ZoneTextF("in water");
}
});
ecs.system<const JPH::BodyID, const WaterBody>("update_water_status2")
.kind(PhysicsPostUpdate)
@@ -305,8 +327,11 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.without<InWater>()
.each([this](flecs::entity e, const JPH::BodyID &id,
const WaterBody &body) {
if (body.isInWater(id))
ZoneScopedN("update_water_status2");
if (body.isInWater(id)) {
e.add<InWater>();
ZoneTextF("not in water");
}
});
ecs.system<const CharacterBody, const WaterBody>(
"update_water_character1")
@@ -316,10 +341,13 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.with<InWater>()
.each([this](flecs::entity e, const CharacterBody &ch,
const WaterBody &body) {
JPH::Character *chptr =
ZoneScopedN("update_water_character1");
JPH::Character *chptr =
static_cast<JPH::Character *>(ch.ch.get());
if (!body.isInWater(chptr->GetBodyID()))
if (!body.isInWater(chptr->GetBodyID())) {
e.remove<InWater>();
ZoneTextF("not in water");
}
});
ecs.system<const CharacterBody, const WaterBody>(
"update_water_character2")
@@ -329,10 +357,13 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.without<InWater>()
.each([this](flecs::entity e, const CharacterBody &ch,
const WaterBody &body) {
JPH::Character *chptr =
ZoneScopedN("update_water_character2");
JPH::Character *chptr =
static_cast<JPH::Character *>(ch.ch.get());
if (body.isInWater(chptr->GetBodyID()))
if (body.isInWater(chptr->GetBodyID())) {
e.add<InWater>();
ZoneTextF("in water");
}
});
ecs.system<const EngineData, const BoatBase, const WaterBody,
const JPH::BodyID>("update_water_boat_enable")
@@ -342,7 +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<const EngineData, const BoatBase, const WaterBody,
@@ -368,7 +401,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.each([this](flecs::entity e, const EngineData &eng,
const BoatBase &boat, const WaterBody &body,
const JPH::BodyID &id, const CachedMass &mass) {
const WaterSurface &water = ECS::get<WaterSurface>();
ZoneScopedN("update_water_boat_buoyancy");
const WaterSurface &water = ECS::get<WaterSurface>();
float b = 1.0f, drag = 0.5f, adrag = 0.5f;
float level = 0.25f;
float my = JoltPhysicsWrapper::getSingleton()
@@ -425,7 +459,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
.with<CharacterBuoyancy>()
.each([this](flecs::entity e, const EngineData &eng,
const CharacterBody &ch, const WaterBody &body) {
JPH::Character *chptr =
ZoneScopedN("update_water_character_buoyancy");
JPH::Character *chptr =
static_cast<JPH::Character *>(ch.ch.get());
JPH::BodyID id = chptr->GetBodyID();
if (JoltPhysicsWrapper::getSingleton().isActive(id)) {
@@ -467,13 +502,15 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
eng.delta);
}
});
ecs.system<const EngineData, const CharacterBody>("UpdatePhysics")
ecs.system<const EngineData, const CharacterBody>(
"UpdateCharacterPhysicsState")
.kind(flecs::OnUpdate)
.with<CharacterUpdatePhysicsState>()
.write<CharacterUpdatePhysicsState>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterBody &body) {
if (e.has<CharacterDisablePhysics>())
ZoneScopedN("UpdateCharacterPhysicsState");
if (e.has<CharacterDisablePhysics>())
PhysicsModule::controlPhysics(e, false);
else
PhysicsModule::controlPhysics(e, true);
@@ -489,7 +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<InWater>() &&
ZoneScopedN("HandleVelocity");
if (e.has<InWater>() &&
chbase.mBodyNode->_getDerivedPosition().y > -0.5f)
e.remove<InWater>();
Ogre::Vector3 v = gr.velocity;
@@ -519,7 +557,27 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
JoltPhysics::convert<JPH::Vec3>(v));
gr.velocity = Ogre::Vector3::ZERO;
});
ecs.system<const EngineData, CharacterBase, const CharacterBody,
ecs.system<CharacterBase>("HandleSubmerge")
.kind(flecs::OnUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.each([this](flecs::entity e, CharacterBase &ch) {
ZoneScopedNC("HandleSubmerge", 0xFF3030);
float full_subm = 2.0f;
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
float current_subm = -Ogre::Math::Clamp(
pos.y + Ogre::Math::Sin(ch.mTimer * 0.13f +
130.0f) *
0.07f,
-full_subm, 0.0f);
if (current_subm > 0.9f)
ch.is_submerged = true;
else if (current_subm < 0.8f)
ch.is_submerged = false;
ZoneTextF("is submerged: %d", (int)ch.is_submerged);
});
ecs.system<const EngineData, CharacterBase, const CharacterBody,
CharacterVelocity>("HandleVelocityNoPhysics")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
@@ -529,7 +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<InWater>();
ch.is_submerged = false;
}
if (!e.has<InWater>() && ch.is_submerged)
ZoneTextF("remove in water");
}
if (!e.has<InWater>() && ch.is_submerged) {
ch.is_submerged = false;
ZoneTextF("not submerged");
}
});
}
flecs::entity PhysicsModule::createTerrainChunkBody(Ogre::SceneNode *node,

View File

@@ -14,6 +14,7 @@
#include "LuaData.h"
#include "PhysicsModule.h"
#include "PlayerActionModule.h"
#include <tracy/Tracy.hpp>
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<Camera>().mCameraNode;
@@ -320,6 +322,7 @@ PlayerActionModule::PlayerActionModule(flecs::world &ecs)
.kind(flecs::OnUpdate)
.each([this](ActionNodeList &list, const Input &input) {
std::lock_guard<std::mutex> 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<const EngineData>("UpdateActivatedWords")
.kind(flecs::OnUpdate)
.each([this](const EngineData &eng) {
ZoneScopedN("UpdateActivatedWords");
for (auto it = activatedWords.begin();
it != activatedWords.end();) {
int ret = it->second->_update(eng.delta);
@@ -745,6 +749,7 @@ void ActionNodeList::updateDynamicNodes()
void ActionNodeList::build()
{
ZoneScoped;
std::lock_guard<std::mutex> lock(*nodeMutex);
indexObj = std::make_shared<ActionNodeList::indexObject>(dynamicNodes);
indexObj->index.buildIndex();

View File

@@ -15,6 +15,7 @@
#include "PhysicsModule.h"
#include "items.h"
#include "StaticGeometryModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
{
@@ -461,16 +462,14 @@ nlohmann::json &StaticGeometryModule::getTemplates()
void StaticGeometryModule::updateItemGeometry(flecs::entity e)
{
if (e.has<GeometryUpdateItem>())
return;
e.add<GeometryUpdateItem>();
Ogre::Root::getSingleton().getWorkQueue()->addTask([e]() {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask(
[e]() {
Geometry::updateItemGeometry(e);
e.remove<GeometryUpdateItem>();
});
// We add this as task to reduce UI load
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([e]() {
ZoneScopedN("updateItemGeometry");
if (e.has<GeometryUpdateItem>())
return;
e.add<GeometryUpdateItem>();
Geometry::updateItemGeometry(e);
e.remove<GeometryUpdateItem>();
});
}
@@ -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<EngineData>()
.mScnMgr->createEntity(mesh);
captData.geo->addEntity(ent, captData.position,
captData.rotation);
ECS::get<EngineData>().mScnMgr->destroyEntity(
ent);
});
// We add this as task to reduce UI load
Ogre::Root::getSingleton().getWorkQueue()->addMainThreadTask([data]() {
ZoneScopedN("addTriangleBufferWork");
Ogre::MeshPtr mesh = data.tb.transformToMesh(data.meshName);
Ogre::Entity *ent =
ECS::get<EngineData>().mScnMgr->createEntity(mesh);
data.geo->addEntity(ent, data.position, data.rotation);
ECS::get<EngineData>().mScnMgr->destroyEntity(ent);
});
}
struct TiledMeshes {

View File

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

View File

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

View File

@@ -22,6 +22,7 @@
#include "CharacterAIModule.h"
#include "items.h"
#include "town.h"
#include <tracy/Tracy.hpp>
/*
* TODO: Create doors and handle them via script.
@@ -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<TownTask *> 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;
}

View File

@@ -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<JoltPhysicsWrapper>::msSingleton = 0;

View File

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