Better narration processing
This commit is contained in:
@@ -9,7 +9,7 @@ find_package(OgreProcedural REQUIRED CONFIG)
|
||||
find_package(pugixml REQUIRED CONFIG)
|
||||
find_package(flecs REQUIRED CONFIG)
|
||||
|
||||
set(COPY_DIRECTORIES resources skybox water resources/buildings/parts)
|
||||
set(COPY_DIRECTORIES characters resources skybox water resources/buildings/parts)
|
||||
set(INSTALL_DEPS ${CMAKE_CURRENT_BINARY_DIR}/resources.cfg)
|
||||
foreach(DIR_NAME ${COPY_DIRECTORIES})
|
||||
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${DIR_NAME}
|
||||
|
||||
@@ -22,38 +22,28 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](flecs::entity e, const CharacterBase &ch,
|
||||
AnimationControl &anim) {
|
||||
if (!anim.configured && ch.mSkeleton) {
|
||||
if (!anim.configured) {
|
||||
int i, j;
|
||||
e.set<EventData>({});
|
||||
ch.mSkeleton->setBlendMode(
|
||||
ch.mBodyEnt->getSkeleton()->setBlendMode(
|
||||
Ogre::ANIMBLEND_CUMULATIVE);
|
||||
Ogre::String animNames[] = {
|
||||
"idle",
|
||||
"walking",
|
||||
"running",
|
||||
"treading_water",
|
||||
"swimming",
|
||||
"hanging-idle",
|
||||
"hanging-climb",
|
||||
"swimming-hold-edge",
|
||||
"swimming-edge-climb",
|
||||
"character-talk",
|
||||
"pass-character",
|
||||
"idle-act",
|
||||
"sitting-chair",
|
||||
"sitting-ground"
|
||||
};
|
||||
int state_count = sizeof(animNames) /
|
||||
sizeof(animNames[0]);
|
||||
anim.mAnimationSystem =
|
||||
new AnimationSystem(false);
|
||||
for (i = 0; i < state_count; i++) {
|
||||
Ogre::AnimationStateSet *animStateSet =
|
||||
ch.mBodyEnt->getAllAnimationStates();
|
||||
const Ogre::AnimationStateMap &animMap =
|
||||
animStateSet->getAnimationStates();
|
||||
anim.mAnimationSystem =
|
||||
new AnimationSystem(false);
|
||||
ch.mBodyEnt->getSkeleton()
|
||||
->getBone("Root")
|
||||
->removeAllChildren();
|
||||
for (auto it = animMap.begin();
|
||||
it != animMap.end(); it++) {
|
||||
Animation *animation = new Animation(
|
||||
ch.mSkeleton,
|
||||
ch.mBodyEnt->getAnimationState(
|
||||
animNames[i]),
|
||||
ch.mSkeleton->getAnimation(
|
||||
animNames[i]),
|
||||
ch.mBodyEnt->getSkeleton(),
|
||||
it->second,
|
||||
ch.mBodyEnt->getSkeleton()
|
||||
->getAnimation(
|
||||
it->first),
|
||||
e);
|
||||
#ifdef VDEBUG
|
||||
std::cout
|
||||
@@ -62,7 +52,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
#endif
|
||||
animation->setLoop(true);
|
||||
anim.mAnimationSystem->add_animation(
|
||||
animNames[i], animation);
|
||||
it->first, animation);
|
||||
}
|
||||
anim.mAnimationSystem
|
||||
->builder()
|
||||
@@ -157,10 +147,39 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
CharacterBase &ch, AnimationControl &anim) {
|
||||
float delta = eng.delta;
|
||||
// ch.mBoneMotion = Ogre::Vector3::ZERO;
|
||||
bool result = anim.mAnimationSystem->addTime(delta);
|
||||
if (!ch.mRootBone)
|
||||
if (!anim.mAnimationSystem)
|
||||
return;
|
||||
// The value we get is interpolated value. When result is true it is new step
|
||||
#if 0
|
||||
ch.mBodyEnt->getSkeleton()->getBone("Root")->reset();
|
||||
ch.mBodyEnt->getSkeleton()->getBone("Root")->setPosition(
|
||||
Ogre::Vector3::ZERO);
|
||||
#endif
|
||||
bool result = anim.mAnimationSystem->addTime(delta);
|
||||
Ogre::Vector3 rootMotion =
|
||||
anim.mAnimationSystem->getRootMotionDelta();
|
||||
ch.mBonePrevMotion = ch.mBoneMotion;
|
||||
ch.mBoneMotion = rootMotion;
|
||||
ch.mBodyEnt->_updateAnimation();
|
||||
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 *>(
|
||||
@@ -302,54 +321,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
Ogre::Vector3 colNormal;
|
||||
bool is_on_floor = false;
|
||||
bool penetration = false;
|
||||
#if 0
|
||||
if (eng.startupDelay < 0.0f) {
|
||||
if (body.mController) {
|
||||
Ogre::Vector3 rotMotion =
|
||||
v.velocity * eng.delta;
|
||||
rotMotion.x = Ogre::Math::Clamp(
|
||||
rotMotion.x, -0.04f, 0.04f);
|
||||
rotMotion.y = Ogre::Math::Clamp(
|
||||
rotMotion.y, -0.025f, 0.1f);
|
||||
rotMotion.z = Ogre::Math::Clamp(
|
||||
rotMotion.z, -0.04f, 0.04f);
|
||||
btVector3 currentPosition =
|
||||
body.mGhostObject
|
||||
->getWorldTransform()
|
||||
.getOrigin();
|
||||
is_on_floor =
|
||||
body.mController->isOnFloor();
|
||||
penetration = body.mController
|
||||
->isPenetrating();
|
||||
if (is_on_floor)
|
||||
v.gvelocity =
|
||||
Ogre::Vector3::ZERO;
|
||||
|
||||
btTransform from(
|
||||
Ogre::Bullet::convert(
|
||||
ch.mBodyNode
|
||||
->getOrientation()),
|
||||
Ogre::Bullet::convert(
|
||||
ch.mBodyNode
|
||||
->getPosition()));
|
||||
ch.mBodyNode->_setDerivedPosition(
|
||||
ch.mBodyNode
|
||||
->_getDerivedPosition() +
|
||||
rotMotion);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
});
|
||||
#if 0
|
||||
ecs.system<CharacterVelocity, CharacterBase>("HandleRootMotionEnd")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](flecs::entity e, CharacterVelocity &v,
|
||||
CharacterBase &ch) {
|
||||
// zero the velocity;
|
||||
// v.velocity = Ogre::Vector3::ZERO;
|
||||
// ch.mBoneMotion = Ogre::Vector3::ZERO;
|
||||
});
|
||||
#endif
|
||||
|
||||
ecs.system<const Input, const CharacterBase, AnimationControl>(
|
||||
"HandleNPCAnimations")
|
||||
@@ -360,6 +332,8 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
const CharacterBase &ch, AnimationControl &anim) {
|
||||
if (!anim.configured)
|
||||
return;
|
||||
if (!anim.mAnimationSystem)
|
||||
return;
|
||||
AnimationNodeStateMachine *state_machine =
|
||||
anim.mAnimationSystem
|
||||
->get<AnimationNodeStateMachine>(
|
||||
@@ -405,6 +379,8 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
AnimationControl &anim) {
|
||||
if (!anim.configured)
|
||||
return;
|
||||
if (!anim.mAnimationSystem)
|
||||
return;
|
||||
AnimationNodeStateMachine *main_sm =
|
||||
anim.mAnimationSystem
|
||||
->get<AnimationNodeStateMachine>(
|
||||
@@ -568,15 +544,6 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
act.prevMotion.z = input.motion.z;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (!controls_idle) {
|
||||
if (Ogre::Math::Abs(input.motion.z - act.prevMotion.z) > 0.001f) {
|
||||
if (input.motion.z < 0)
|
||||
ECS::get_mut<LuaData>().call_handler("actuator_forward", e);
|
||||
}
|
||||
ECS::get_mut<LuaData>().call_handler("actuator_controls_update");
|
||||
}
|
||||
#endif
|
||||
act.prevMotion = input.motion;
|
||||
});
|
||||
ecs.system<EventData>("UpdateEvents")
|
||||
@@ -616,87 +583,6 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
<< "h=" << ch.mBodyNode->_getDerivedPosition().y
|
||||
<< std::endl;
|
||||
});
|
||||
#endif
|
||||
#if 0
|
||||
ecs.system<const EngineData, const CharacterBase, CharacterBody>(
|
||||
"UpdateBodyCast")
|
||||
.kind(flecs::OnUpdate)
|
||||
.without<CharacterInActuator>()
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
const CharacterBase &ch, CharacterBody &body) {
|
||||
struct ResultCallback
|
||||
: public btCollisionWorld::RayResultCallback {
|
||||
btCollisionObject *m_me;
|
||||
btVector3 m_from, m_to, m_hitNormalWorld,
|
||||
m_hitPointWorld;
|
||||
ResultCallback(btCollisionObject *me,
|
||||
const btVector3 &from,
|
||||
const btVector3 &to)
|
||||
: m_me(me)
|
||||
, m_from(from)
|
||||
, m_to(to)
|
||||
{
|
||||
}
|
||||
btScalar addSingleResult(
|
||||
btCollisionWorld::LocalRayResult
|
||||
&rayResult,
|
||||
bool normalInWorldSpace) override
|
||||
{
|
||||
if (rayResult.m_collisionObject == m_me)
|
||||
return 1.0f;
|
||||
if (!btPairCachingGhostObject::upcast(
|
||||
rayResult.m_collisionObject))
|
||||
return 1.0f;
|
||||
if (!(rayResult.m_collisionObject
|
||||
->getCollisionFlags() &
|
||||
btCollisionObject::
|
||||
CF_CHARACTER_OBJECT))
|
||||
return 1.0f;
|
||||
m_closestHitFraction =
|
||||
rayResult.m_hitFraction;
|
||||
m_collisionObject =
|
||||
rayResult.m_collisionObject;
|
||||
if (normalInWorldSpace)
|
||||
m_hitNormalWorld =
|
||||
rayResult
|
||||
.m_hitNormalLocal;
|
||||
else
|
||||
m_hitNormalWorld =
|
||||
m_collisionObject
|
||||
->getWorldTransform()
|
||||
.getBasis() *
|
||||
rayResult
|
||||
.m_hitNormalLocal;
|
||||
m_hitPointWorld.setInterpolate3(
|
||||
m_from, m_to,
|
||||
rayResult.m_hitFraction);
|
||||
return rayResult.m_hitFraction;
|
||||
}
|
||||
};
|
||||
Ogre::Vector3 offset(0.0f, 0.5f, 0.0f);
|
||||
float dist = 0.5f;
|
||||
btVector3 a = Ogre::Bullet::convert(
|
||||
ch.mBodyNode->getPosition() + offset),
|
||||
b(Ogre::Bullet::convert(
|
||||
ch.mBodyNode->getPosition() +
|
||||
ch.mBodyNode->getOrientation() *
|
||||
Ogre::Vector3(0, 0, dist) +
|
||||
offset));
|
||||
ResultCallback result(body.mGhostObject, a, b);
|
||||
// body.mGhostObject->rayTest(a, b, result);
|
||||
eng.mWorld->getBtWorld()->rayTest(a, b, result);
|
||||
if (result.hasHit()) {
|
||||
std::cout << "Hit!!! " << result.m_hitPointWorld
|
||||
<< std::endl;
|
||||
e.set<CharacterInActuator>(
|
||||
{ "idle", { 0, 0, 0 } });
|
||||
ECS::get<LuaBase>().mLua->call_handler(
|
||||
"character_enter", e,
|
||||
ECS::get<Body2Entity>().entities.at(
|
||||
const_cast<btCollisionObject *>(
|
||||
result.m_collisionObject)));
|
||||
}
|
||||
});
|
||||
#endif
|
||||
struct AnimationSetCommand : public GameWorld::Command {
|
||||
int operator()(const std::vector<GameWorld::Parameter *> &args)
|
||||
|
||||
@@ -9,22 +9,16 @@
|
||||
namespace ECS
|
||||
{
|
||||
class RootMotionListener : public Ogre::NodeAnimationTrack::Listener {
|
||||
Ogre::Vector3 prevTranslation;
|
||||
mutable Ogre::Vector3 deltaMotion;
|
||||
flecs::entity e;
|
||||
|
||||
public:
|
||||
RootMotionListener(flecs::entity e)
|
||||
: Ogre::NodeAnimationTrack::Listener()
|
||||
, e(e)
|
||||
, prevTranslation(Ogre::Vector3::ZERO)
|
||||
, deltaMotion(Ogre::Vector3::ZERO)
|
||||
{
|
||||
}
|
||||
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;
|
||||
@@ -36,14 +30,12 @@ public:
|
||||
k2 = static_cast<Ogre::TransformKeyFrame *>(kf2);
|
||||
Ogre::Vector3 translation;
|
||||
Ogre::Quaternion rotation;
|
||||
if (tm == 0.0f) {
|
||||
Ogre::Vector3 deltaMotion;
|
||||
Ogre::Vector3 prevMotion;
|
||||
if (tm == 0.0f) {
|
||||
rotation = k1->getRotation();
|
||||
translation = k1->getTranslate();
|
||||
deltaMotion = translation;
|
||||
|
||||
// vkf->setRotation(k1->getRotation());
|
||||
// vkf->setTranslate(k1->getTranslate());
|
||||
// vkf->setScale(k1->getScale());
|
||||
} else {
|
||||
rotation = Ogre::Quaternion::nlerp(
|
||||
tm, k1->getRotation(), k2->getRotation(), true);
|
||||
@@ -55,14 +47,7 @@ public:
|
||||
translation.squaredLength())
|
||||
deltaMotion = translation;
|
||||
}
|
||||
#if 0
|
||||
std::cout << "time: " << tm
|
||||
<< " Position: " << deltaMotion;
|
||||
std::cout << " Quaternion: " << rotation;
|
||||
std::cout << std::endl;
|
||||
#endif
|
||||
vkf->setTranslate(deltaMotion);
|
||||
// vkf->setTranslate(translation);
|
||||
vkf->setRotation(rotation);
|
||||
vkf->setScale(Ogre::Vector3(1, 1, 1));
|
||||
prevTranslation = translation;
|
||||
@@ -70,7 +55,9 @@ public:
|
||||
e.get_mut<CharacterBase>().mBonePrevMotion = prevTranslation;
|
||||
e.modified<CharacterBase>();
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
};
|
||||
struct AnimationTrigger;
|
||||
struct AnimationTriggerSubscriber {
|
||||
@@ -126,46 +113,66 @@ struct AnimationTrigger {
|
||||
{
|
||||
}
|
||||
};
|
||||
struct SetupTracks {
|
||||
Ogre::NodeAnimationTrack *mHipsTrack;
|
||||
Ogre::NodeAnimationTrack *mRootTrack;
|
||||
RootMotionListener *mListener;
|
||||
Ogre::Vector3 mRootTranslation;
|
||||
SetupTracks(flecs::entity e, Ogre::Skeleton *skeleton,
|
||||
Ogre::Animation *animation)
|
||||
: mListener(OGRE_NEW RootMotionListener(e))
|
||||
{
|
||||
mHipsTrack = nullptr;
|
||||
mRootTrack = nullptr;
|
||||
for (const auto &it : animation->_getNodeTrackList()) {
|
||||
Ogre::NodeAnimationTrack *track = it.second;
|
||||
Ogre::String trackName =
|
||||
track->getAssociatedNode()->getName();
|
||||
if (trackName == "mixamorig:Hips") {
|
||||
mHipsTrack = track;
|
||||
} else if (trackName == "Root") {
|
||||
mRootTrack = track;
|
||||
// mRootTracks[i]->removeAllKeyFrames();
|
||||
}
|
||||
}
|
||||
if (!mRootTrack) {
|
||||
Ogre::Bone *bone = skeleton->getBone("Root");
|
||||
mRootTrack = animation->createNodeTrack(
|
||||
bone->getHandle(), bone);
|
||||
Ogre::TransformKeyFrame *kf =
|
||||
mRootTrack->createNodeKeyFrame(0.0f);
|
||||
kf->setTranslate(Ogre::Vector3::ZERO);
|
||||
kf->setRotation(Ogre::Quaternion::IDENTITY);
|
||||
}
|
||||
// if (e.has<Player>()) // FIXME
|
||||
// mRootTrack->setListener(mListener);
|
||||
Ogre::TransformKeyFrame *tkfBeg =
|
||||
(Ogre::TransformKeyFrame *)mRootTrack->getKeyFrame(0);
|
||||
Ogre::TransformKeyFrame *tkfEnd =
|
||||
(Ogre::TransformKeyFrame *)mRootTrack->getKeyFrame(
|
||||
mRootTrack->getNumKeyFrames() - 1);
|
||||
mRootTranslation =
|
||||
tkfEnd->getTranslate() - tkfBeg->getTranslate();
|
||||
}
|
||||
};
|
||||
struct Animation {
|
||||
Ogre::AnimationState *mAnimationState;
|
||||
Ogre::Animation *mSkelAnimation;
|
||||
Ogre::NodeAnimationTrack *mHipsTrack;
|
||||
Ogre::NodeAnimationTrack *mRootTrack;
|
||||
RootMotionListener *mListener;
|
||||
SetupTracks *mTracks;
|
||||
float m_weight;
|
||||
float m_accWeight;
|
||||
flecs::entity e;
|
||||
Ogre::Skeleton *mSkeleton;
|
||||
Animation(Ogre::Skeleton *skeleton, Ogre::AnimationState *animState,
|
||||
Ogre::Animation *skelAnimation, flecs::entity e)
|
||||
: mAnimationState(animState)
|
||||
: mTracks(OGRE_NEW SetupTracks(e, skeleton, skelAnimation))
|
||||
, mAnimationState(animState)
|
||||
, mSkelAnimation(skelAnimation)
|
||||
, mListener(OGRE_NEW RootMotionListener(e))
|
||||
, m_weight(0)
|
||||
, m_accWeight(0)
|
||||
, e(e)
|
||||
, mSkeleton(skeleton)
|
||||
{
|
||||
int j;
|
||||
mRootTrack = nullptr;
|
||||
mHipsTrack = nullptr;
|
||||
for (const auto &it : mSkelAnimation->_getNodeTrackList()) {
|
||||
Ogre::NodeAnimationTrack *track = it.second;
|
||||
Ogre::String trackName =
|
||||
track->getAssociatedNode()->getName();
|
||||
if (trackName == "mixamorig:Hips") {
|
||||
mHipsTrack = track;
|
||||
} else if (trackName == "Root") {
|
||||
mRootTrack = track;
|
||||
// mRootTracks[i]->removeAllKeyFrames();
|
||||
}
|
||||
}
|
||||
if (!mRootTrack) {
|
||||
Ogre::Bone *bone = skeleton->getBone("Root");
|
||||
mRootTrack = mSkelAnimation->createNodeTrack(
|
||||
bone->getHandle(), bone);
|
||||
Ogre::TransformKeyFrame *kf =
|
||||
mRootTrack->createNodeKeyFrame(0.0f);
|
||||
kf->setTranslate(Ogre::Vector3::ZERO);
|
||||
kf->setRotation(Ogre::Quaternion::IDENTITY);
|
||||
}
|
||||
mRootTrack->setListener(mListener);
|
||||
}
|
||||
Ogre::String getName()
|
||||
{
|
||||
@@ -198,22 +205,134 @@ struct Animation {
|
||||
{
|
||||
return m_weight;
|
||||
}
|
||||
bool addTime(float time)
|
||||
Ogre::Vector3 rootMotion;
|
||||
Ogre::Vector3 getRootMotionDelta()
|
||||
{
|
||||
#if 0
|
||||
Ogre::KeyFrame *kf1, *kf2;
|
||||
Ogre::TransformKeyFrame *k1, *k2;
|
||||
unsigned short firstKeyIndex;
|
||||
Ogre::Real timePos = mAnimationState->getTimePosition();
|
||||
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(timePos);
|
||||
float tm = mTracks->mRootTrack->getKeyFramesAtTime(
|
||||
index, &kf1, &kf2, &firstKeyIndex);
|
||||
k1 = static_cast<Ogre::TransformKeyFrame *>(kf1);
|
||||
k2 = static_cast<Ogre::TransformKeyFrame *>(kf2);
|
||||
Ogre::Vector3 translation;
|
||||
Ogre::Quaternion rotation;
|
||||
if (tm == 0.0f) {
|
||||
rotation = k1->getRotation();
|
||||
translation = k1->getTranslate();
|
||||
} else {
|
||||
rotation = Ogre::Quaternion::nlerp(
|
||||
tm, k1->getRotation(),
|
||||
k2->getRotation(), true);
|
||||
translation = k1->getTranslate() +
|
||||
(k2->getTranslate() -
|
||||
k1->getTranslate()) *
|
||||
tm;
|
||||
}
|
||||
return translation * mAnimationState->getWeight();
|
||||
#endif
|
||||
if (mAnimationState->getEnabled())
|
||||
return rootMotion * mAnimationState->getWeight();
|
||||
else
|
||||
return Ogre::Vector3(0, 0, 0);
|
||||
}
|
||||
#if 0
|
||||
void updateRootMotion(Ogre::Real timePos)
|
||||
{
|
||||
Ogre::KeyFrame *kf1, *kf2;
|
||||
Ogre::TransformKeyFrame *k1, *k2;
|
||||
unsigned short firstKeyIndex;
|
||||
mSkeleton->getBone("Root")->setManuallyControlled(true);
|
||||
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(timePos);
|
||||
float tm = mTracks->mRootTrack->getKeyFramesAtTime(
|
||||
index, &kf1, &kf2, &firstKeyIndex);
|
||||
k1 = static_cast<Ogre::TransformKeyFrame *>(kf1);
|
||||
k2 = static_cast<Ogre::TransformKeyFrame *>(kf2);
|
||||
Ogre::Vector3 translation;
|
||||
Ogre::Quaternion rotation;
|
||||
Ogre::Vector3 deltaMotion =
|
||||
e.get_mut<CharacterBase>().mBoneMotion;
|
||||
Ogre::Vector3 prevTranslation =
|
||||
e.get_mut<CharacterBase>().mBonePrevMotion;
|
||||
if (tm == 0.0f) {
|
||||
rotation = k1->getRotation() * m_weight;
|
||||
translation = k1->getTranslate() * m_weight;
|
||||
deltaMotion = translation;
|
||||
} else {
|
||||
rotation = Ogre::Quaternion::nlerp(
|
||||
tm, k1->getRotation() * m_weight,
|
||||
k2->getRotation() * m_weight, true);
|
||||
translation = k1->getTranslate() * m_weight +
|
||||
(k2->getTranslate() * m_weight -
|
||||
k1->getTranslate() * m_weight) *
|
||||
tm;
|
||||
deltaMotion = translation - prevTranslation;
|
||||
if (deltaMotion.squaredLength() >
|
||||
translation.squaredLength())
|
||||
deltaMotion = translation;
|
||||
}
|
||||
e.get_mut<CharacterBase>().mBoneMotion = deltaMotion;
|
||||
e.get_mut<CharacterBase>().mBonePrevMotion = prevTranslation;
|
||||
e.modified<CharacterBase>();
|
||||
#if 1
|
||||
if (timePos > 0.5f && m_weight > 0.2 && translation.squaredLength() > 0.0f) {
|
||||
std::cout << timePos << " " << m_weight << " "
|
||||
<< e.get<CharacterBase>()
|
||||
.mBodyEnt->getMesh()
|
||||
->getName()
|
||||
<< " " << deltaMotion << " "
|
||||
<< prevTranslation << std::endl;
|
||||
std::cout << translation << " " << rotation << std::endl;
|
||||
// OgreAssert(false, "updateRootMotion");
|
||||
}
|
||||
#endif
|
||||
mTracks->mRootTrack->getAssociatedNode()->setPosition(
|
||||
Ogre::Vector3());
|
||||
mSkeleton->getBone("Root")->reset();
|
||||
}
|
||||
#endif
|
||||
void getKeyframeIndices(Ogre::Real timePos, unsigned short *pindex)
|
||||
{
|
||||
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(timePos);
|
||||
Ogre::KeyFrame *kf1, *kf2;
|
||||
mTracks->mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, pindex);
|
||||
|
||||
}
|
||||
bool addTime(float time)
|
||||
{
|
||||
bool result = mAnimationState->getEnabled();
|
||||
if (!result)
|
||||
return result;
|
||||
Ogre::TimeIndex index = mSkelAnimation->_getTimeIndex(
|
||||
mAnimationState->getTimePosition());
|
||||
Ogre::KeyFrame *kf1, *kf2;
|
||||
unsigned short prev_index, next_index;
|
||||
mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, &prev_index);
|
||||
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);
|
||||
index = mSkelAnimation->_getTimeIndex(
|
||||
mAnimationState->getTimePosition());
|
||||
mRootTrack->getKeyFramesAtTime(index, &kf1, &kf2, &next_index);
|
||||
return prev_index != next_index;
|
||||
float thisTime = mAnimationState->getTimePosition();
|
||||
float length = mAnimationState->getLength();
|
||||
bool loop = mAnimationState->getLoop();
|
||||
int loops = loop ? (int)std::round((lastTime + time - thisTime) / length) : 0;
|
||||
Ogre::TransformKeyFrame tkf(0, 0);
|
||||
mTracks->mRootTrack->getInterpolatedKeyFrame(lastTime, &tkf);
|
||||
Ogre::Vector3 lastRootPos = tkf.getTranslate();
|
||||
mTracks->mRootTrack->getInterpolatedKeyFrame(thisTime, &tkf);
|
||||
Ogre::Vector3 thisRootPos = tkf.getTranslate();
|
||||
#if 0
|
||||
if (thisTime > lastTime)
|
||||
rootMotion = thisRootPos - lastRootPos;
|
||||
else
|
||||
rootMotion = mTracks->mRootTranslation + thisRootPos - lastRootPos;
|
||||
#else
|
||||
rootMotion = (thisRootPos - lastRootPos) + (loops * mTracks->mRootTranslation);
|
||||
|
||||
#endif
|
||||
getKeyframeIndices(mAnimationState->getTimePosition(), &next_index);
|
||||
// updateRootMotion(mAnimationState->getTimePosition());
|
||||
return prev_index != next_index;
|
||||
}
|
||||
void reset()
|
||||
{
|
||||
@@ -827,6 +946,14 @@ struct AnimationSystem : AnimationNode {
|
||||
}
|
||||
};
|
||||
AnimationSystemBuilder m_builder;
|
||||
Ogre::Vector3 getRootMotionDelta()
|
||||
{
|
||||
Ogre::Vector3 motionDelta(0, 0, 0);
|
||||
int i;
|
||||
for (i = 0; i < vanimation_list.size(); i++)
|
||||
motionDelta += vanimation_list[i]->getRootMotionDelta();
|
||||
return motionDelta;
|
||||
}
|
||||
bool addTime(float time)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -4,15 +4,190 @@
|
||||
#include "Components.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "CharacterAnimationModule.h"
|
||||
#include "StaticGeometryModule.h"
|
||||
#include "PhysicsModule.h"
|
||||
#include "PlayerActionModule.h"
|
||||
#include "items.h"
|
||||
#include "CharacterManagerModule.h"
|
||||
|
||||
namespace ECS
|
||||
{
|
||||
struct TownNPCs {
|
||||
struct NPCData {
|
||||
flecs::entity e;
|
||||
nlohmann::json props;
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Quaternion orientation;
|
||||
Ogre::String model;
|
||||
};
|
||||
|
||||
std::map<int, NPCData> npcs;
|
||||
};
|
||||
struct LivesIn {};
|
||||
void createNPCActionNodes(flecs::entity town, flecs::entity e, int index)
|
||||
{
|
||||
NPCActionNodes &anodes = e.get_mut<NPCActionNodes>();
|
||||
const CharacterBase &ch = e.get<CharacterBase>();
|
||||
Ogre::Vector3 characterPos = ch.mBodyNode->_getDerivedPosition();
|
||||
Ogre::Quaternion characterRot = ch.mBodyNode->_getDerivedOrientation();
|
||||
if (anodes.anodes.size() > 0) {
|
||||
int i;
|
||||
for (i = 0; i < anodes.anodes.size(); i++) {
|
||||
auto &anode = anodes.anodes[i];
|
||||
Ogre::Vector3 offset = Ogre::Vector3::UNIT_Z * 0.3f +
|
||||
Ogre::Vector3::UNIT_Y;
|
||||
if (i == 1)
|
||||
offset = Ogre::Vector3::NEGATIVE_UNIT_Z * 0.3f +
|
||||
Ogre::Vector3::UNIT_Y;
|
||||
anode.position = characterPos + characterRot * offset;
|
||||
anode.rotation = characterRot;
|
||||
to_json(anode.props["position"], anode.position);
|
||||
to_json(anode.props["rotation"], anode.rotation);
|
||||
}
|
||||
e.modified<NPCActionNodes>();
|
||||
return;
|
||||
}
|
||||
{
|
||||
ActionNodeList::ActionNode anode;
|
||||
anode.props["action"] = "talk";
|
||||
anode.props["action_text"] = "Talk";
|
||||
anode.action = "talk";
|
||||
anode.action_text = "Talk";
|
||||
Ogre::Vector3 offset = Ogre::Vector3::UNIT_Z * 0.25f +
|
||||
Ogre::Vector3::UNIT_Y * 1.5f;
|
||||
anode.position = characterPos + characterRot * offset;
|
||||
anode.rotation = characterRot;
|
||||
anode.radius = 0.6f;
|
||||
anode.height = 1.0f;
|
||||
to_json(anode.props["position"], anode.position);
|
||||
to_json(anode.props["rotation"], anode.rotation);
|
||||
|
||||
anode.props["radius"] = anode.radius;
|
||||
anode.props["height"] = anode.height;
|
||||
anode.props["town"] = town.id();
|
||||
anode.props["index"] = index;
|
||||
anodes.anodes.push_back(anode);
|
||||
}
|
||||
{
|
||||
ActionNodeList::ActionNode anode;
|
||||
anode.props["action"] = "action";
|
||||
anode.props["action_text"] = "Action";
|
||||
anode.action = "action";
|
||||
anode.action_text = "Action";
|
||||
Ogre::Vector3 offset = Ogre::Vector3::NEGATIVE_UNIT_Z * 0.2f +
|
||||
Ogre::Vector3::UNIT_Y;
|
||||
anode.position = characterPos + characterRot * offset;
|
||||
anode.rotation = characterRot;
|
||||
anode.radius = 0.3f;
|
||||
anode.height = 1.0f;
|
||||
to_json(anode.props["position"], anode.position);
|
||||
to_json(anode.props["rotation"], anode.rotation);
|
||||
anode.props["radius"] = anode.radius;
|
||||
anode.props["height"] = anode.height;
|
||||
anode.props["town"] = town.id();
|
||||
anode.props["index"] = index;
|
||||
anodes.anodes.push_back(anode);
|
||||
}
|
||||
e.modified<NPCActionNodes>();
|
||||
}
|
||||
CharacterManagerModule::CharacterManagerModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<CharacterManagerModule>();
|
||||
ecs.import <CharacterModule>();
|
||||
ecs.import <CharacterAnimationModule>();
|
||||
ecs.import <PhysicsModule>();
|
||||
ecs.import <PlayerActionModule>();
|
||||
ecs.component<TownCharacterHolder>();
|
||||
ecs.component<TownNPCs>();
|
||||
ecs.component<LivesIn>();
|
||||
ecs.component<NPCActionNodes>().on_add(
|
||||
[](flecs::entity e, NPCActionNodes &anodes) {
|
||||
anodes.anodes.clear();
|
||||
});
|
||||
ecs.system<TerrainItem, TownNPCs>("UpdateCharacters")
|
||||
.immediate()
|
||||
.kind(flecs::OnUpdate)
|
||||
.interval(1.0f)
|
||||
.write<CharacterBase>()
|
||||
.write<NPCActionNodes>()
|
||||
.write<CharacterLocation>()
|
||||
.write<CharacterConf>()
|
||||
.write<Character>()
|
||||
.write<LivesIn>()
|
||||
.each([this](flecs::entity town, TerrainItem &item,
|
||||
TownNPCs &npcs) {
|
||||
if (!player.is_valid())
|
||||
return;
|
||||
if (!player.has<CharacterBase>())
|
||||
return;
|
||||
ECS::get().defer_suspend();
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
ECS::get().defer_resume();
|
||||
});
|
||||
ecs.system<TerrainItem, TownNPCs>("UpdateCharacters2")
|
||||
.immediate()
|
||||
.kind(flecs::OnUpdate)
|
||||
.write<CharacterBase>()
|
||||
.write<NPCActionNodes>()
|
||||
.write<CharacterLocation>()
|
||||
.write<CharacterConf>()
|
||||
.write<Character>()
|
||||
.write<LivesIn>()
|
||||
.each([this](flecs::entity town, TerrainItem &item,
|
||||
TownNPCs &npcs) {
|
||||
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()) {
|
||||
if (data.e.has<CharacterBase>() &&
|
||||
data.e.has<LivesIn>(town))
|
||||
createNPCActionNodes(
|
||||
town, data.e,
|
||||
index);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
flecs::entity
|
||||
CharacterManagerModule::createPlayer(const Ogre::Vector3 &position,
|
||||
@@ -43,7 +218,44 @@ CharacterManagerModule::createCharacterData(const Ogre::String model,
|
||||
.entity()
|
||||
.set<CharacterLocation>({ rotation, position })
|
||||
.set<CharacterConf>({ model })
|
||||
.add<Character>();
|
||||
return e;
|
||||
.add<Character>()
|
||||
.add<NPCActionNodes>();
|
||||
return e;
|
||||
}
|
||||
|
||||
void CharacterManagerModule::registerTownCharacters(flecs::entity town)
|
||||
{
|
||||
Ogre::MeshManager::getSingleton().load("normal-male.glb", "General");
|
||||
Ogre::MeshManager::getSingleton().load("normal-female.glb", "General");
|
||||
Ogre::String props = StaticGeometryModule::getItemProperties(town);
|
||||
nlohmann::json j = nlohmann::json::parse(props);
|
||||
nlohmann::json npcs = nlohmann::json::array();
|
||||
if (town.has<TownNPCs>())
|
||||
return;
|
||||
if (j.find("npcs") != j.end())
|
||||
npcs = j["npcs"];
|
||||
std::cout << npcs.dump(4) << std::endl;
|
||||
int index = 0;
|
||||
std::map<int, TownNPCs::NPCData> npcMap;
|
||||
for (auto &npc : npcs) {
|
||||
const char *models[] = { "normal-male.glb",
|
||||
"normal-female.glb" };
|
||||
int sex = npc["sex"].get<int>();
|
||||
Ogre::Vector3 npcPosition;
|
||||
Ogre::Quaternion npcOrientation;
|
||||
from_json(npc["position"], npcPosition);
|
||||
from_json(npc["orientation"], npcOrientation);
|
||||
Ogre::String model = models[sex];
|
||||
TownNPCs::NPCData npcData;
|
||||
npcData.e = flecs::entity();
|
||||
npcData.model = model;
|
||||
npcData.orientation = npcOrientation;
|
||||
npcData.position = npcPosition;
|
||||
npcData.props = npc;
|
||||
npcMap[index] = npcData;
|
||||
index++;
|
||||
}
|
||||
town.set<TownNPCs>({ npcMap });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <flecs.h>
|
||||
namespace ECS
|
||||
{
|
||||
struct TownCharacterHolder{int index;};
|
||||
struct CharacterManagerModule {
|
||||
std::set<flecs::entity> characters;
|
||||
flecs::entity player;
|
||||
@@ -17,6 +18,10 @@ struct CharacterManagerModule {
|
||||
{
|
||||
return player;
|
||||
}
|
||||
void registerTownCharacters(flecs::entity town);
|
||||
void setTownCharacter(flecs::entity town, int index, bool enable);
|
||||
CharacterManagerModule(CharacterManagerModule &&) = delete;
|
||||
CharacterManagerModule &operator=(CharacterManagerModule&&) = delete;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -282,13 +282,8 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
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");
|
||||
ch.mBoneMotion = Ogre::Vector3::ZERO;
|
||||
ch.mBonePrevMotion = Ogre::Vector3::ZERO;
|
||||
});
|
||||
|
||||
@@ -21,8 +21,6 @@ struct CharacterBase {
|
||||
float mTimer;
|
||||
Ogre::SceneNode *mBodyNode;
|
||||
Ogre::Entity *mBodyEnt;
|
||||
Ogre::Skeleton *mSkeleton;
|
||||
Ogre::Node *mRootBone;
|
||||
Ogre::Vector3 mBoneMotion;
|
||||
Ogre::Vector3 mBonePrevMotion;
|
||||
Ogre::Vector3 mGoalDirection; // actual intended direction in world-space
|
||||
|
||||
@@ -22,7 +22,10 @@
|
||||
#include "EditorGizmoModule.h"
|
||||
#include "PhysicsModule.h"
|
||||
#include "PlayerActionModule.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "CharacterManagerModule.h"
|
||||
#include "items.h"
|
||||
#include "physics.h"
|
||||
#include "GUIModule.h"
|
||||
#include "GUIModuleCommon.h"
|
||||
namespace ECS
|
||||
@@ -263,60 +266,105 @@ struct GUIListener : public Ogre::RenderTargetListener {
|
||||
"This game does not autosave. Please use save function to keep your state");
|
||||
ImGui::PopFont();
|
||||
ImGui::End();
|
||||
} else if (ECS::get().get<GUI>().enabled) {
|
||||
if (ECS::get().get<GUI>().narrationBox) {
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
ImGui::SetNextWindowPos(ImVec2(0,
|
||||
size.y * 0.75f),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(size.x,
|
||||
size.y * 0.25f),
|
||||
ImGuiCond_Always);
|
||||
ImGui::Begin("Narration...", NULL,
|
||||
ImGuiWindowFlags_NoTitleBar);
|
||||
ImGui::PushFont(midFont);
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
ImGui::TextWrapped(
|
||||
"%s", ECS::get()
|
||||
.get<GUI>()
|
||||
.narrationText.c_str());
|
||||
if (ECS::get().get<GUI>().choices.size() == 0) {
|
||||
ImGui::SetCursorScreenPos(p);
|
||||
if (ImGui::InvisibleButton(
|
||||
"Background",
|
||||
ImGui::GetWindowSize()))
|
||||
ECS::get<LuaBase>().mLua->call_handler(
|
||||
"narration_progress");
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < ECS::get()
|
||||
.get<GUI>()
|
||||
.choices.size();
|
||||
i++) {
|
||||
if (ImGui::Button(
|
||||
ECS::get()
|
||||
.get<GUI>()
|
||||
.choices[i]
|
||||
.c_str())) {
|
||||
ECS::get()
|
||||
.get_mut<GUI>()
|
||||
.narration_answer =
|
||||
i + 1;
|
||||
std::cout << "answer: "
|
||||
<< i + 1
|
||||
<< std::endl;
|
||||
ECS::modified<GUI>();
|
||||
ECS::get<LuaBase>()
|
||||
.mLua
|
||||
->call_handler(
|
||||
"narration_answered");
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::PopFont();
|
||||
ImGui::End();
|
||||
} else if (ECS::get().get<GUI>().mainMenu) {
|
||||
} else if (ECS::get<GUI>().narrationHandlers.size() > 0) {
|
||||
ECS::get_mut<GUI>().grab = false;
|
||||
ECS::get_mut<GUI>().grabChanged = true;
|
||||
ECS::get_mut<GUI>().enabled = true;
|
||||
ECS::get_mut<GUI>().narrationBox = true;
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
ImGui::SetNextWindowPos(ImVec2(0, size.y * 0.75f),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(size.x, size.y * 0.25f),
|
||||
ImGuiCond_Always);
|
||||
ImGui::Begin("Narration...", NULL,
|
||||
ImGuiWindowFlags_NoTitleBar);
|
||||
ImGui::PushFont(midFont);
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
GUI::NarrationHandler *narration =
|
||||
ECS::get<GUI>().narrationHandlers.front();
|
||||
if (!narration->is_active())
|
||||
narration->_event("narration_activate");
|
||||
ImGui::TextWrapped(
|
||||
"%s", narration->getNarrationText().c_str());
|
||||
if (narration->getChoices().size() == 0) {
|
||||
ImGui::SetCursorScreenPos(p);
|
||||
if (ImGui::InvisibleButton(
|
||||
"Background",
|
||||
ImGui::GetWindowSize()))
|
||||
narration->progress();
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0; i < narration->getChoices().size();
|
||||
i++) {
|
||||
if (ImGui::Button(
|
||||
narration->getChoices()[i]
|
||||
.c_str())) {
|
||||
narration->setNarrationAnswer(
|
||||
i + 1);
|
||||
std::cout << "answer: " << i + 1
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (narration->is_complete()) {
|
||||
ECS::get_mut<GUI>().enabled = false;
|
||||
ECS::get_mut<GUI>().narrationBox = false;
|
||||
ECS::get_mut<GUI>().grab = true;
|
||||
ECS::get_mut<GUI>().grabChanged = true;
|
||||
ECS::get_mut<GUI>().removeNarrationHandler(
|
||||
narration);
|
||||
delete narration;
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::PopFont();
|
||||
ImGui::End();
|
||||
ECS::modified<GUI>();
|
||||
} else if (ECS::get().get<GUI>().narrationBox) {
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
ImGui::SetNextWindowPos(ImVec2(0, size.y * 0.75f),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(size.x, size.y * 0.25f),
|
||||
ImGuiCond_Always);
|
||||
ImGui::Begin("Narration...", NULL,
|
||||
ImGuiWindowFlags_NoTitleBar);
|
||||
ImGui::PushFont(midFont);
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
ImGui::TextWrapped(
|
||||
"%s",
|
||||
ECS::get().get<GUI>().narrationText.c_str());
|
||||
if (ECS::get().get<GUI>().choices.size() == 0) {
|
||||
ImGui::SetCursorScreenPos(p);
|
||||
if (ImGui::InvisibleButton(
|
||||
"Background",
|
||||
ImGui::GetWindowSize()))
|
||||
ECS::get<LuaBase>().mLua->call_handler(
|
||||
"narration_progress");
|
||||
} else {
|
||||
int i;
|
||||
for (i = 0;
|
||||
i < ECS::get().get<GUI>().choices.size();
|
||||
i++) {
|
||||
if (ImGui::Button(ECS::get()
|
||||
.get<GUI>()
|
||||
.choices[i]
|
||||
.c_str())) {
|
||||
ECS::get()
|
||||
.get_mut<GUI>()
|
||||
.narration_answer =
|
||||
i + 1;
|
||||
std::cout << "answer: " << i + 1
|
||||
<< std::endl;
|
||||
ECS::modified<GUI>();
|
||||
ECS::get<LuaBase>().mLua->call_handler(
|
||||
"narration_answered");
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::Spacing();
|
||||
ImGui::PopFont();
|
||||
ImGui::End();
|
||||
} else if (ECS::get().get<GUI>().enabled) {
|
||||
if (ECS::get().get<GUI>().mainMenu) {
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0),
|
||||
ImGuiCond_Always);
|
||||
@@ -495,27 +543,103 @@ struct GUIListener : public Ogre::RenderTargetListener {
|
||||
} else {
|
||||
ECS::ActionNodeList &list =
|
||||
ECS::get_mut<ECS::ActionNodeList>();
|
||||
if (list.nodes.size() > 0) {
|
||||
Ogre::Vector3 queryPos =
|
||||
ECS::get<Camera>()
|
||||
.mCameraNode
|
||||
->_getDerivedPosition();
|
||||
std::vector<size_t> points;
|
||||
list.query(queryPos, points);
|
||||
for (auto &p : points) {
|
||||
std::cout << p << std::endl
|
||||
<< list.nodes[p].props.dump()
|
||||
<< std::endl;
|
||||
if (list.dynamicNodes.size() > 0) {
|
||||
int j;
|
||||
Ogre::Vector3 queryPos;
|
||||
std::vector<size_t> points = list.points;
|
||||
std::vector<float> distances = list.distances;
|
||||
|
||||
Ogre::SceneNode *cameraNode =
|
||||
ECS::get<Camera>().mCameraNode;
|
||||
Ogre::Vector3 cameraPos =
|
||||
cameraNode->_getDerivedPosition();
|
||||
float minDistance = 25.0f;
|
||||
int i;
|
||||
list.selected = -1;
|
||||
for (i = 0; i < points.size(); i++) {
|
||||
size_t p = points[i];
|
||||
float distance = distances[i];
|
||||
float radius = list.dynamicNodes[p]
|
||||
.props["radius"]
|
||||
.get<float>();
|
||||
float height = list.dynamicNodes[p]
|
||||
.props["height"]
|
||||
.get<float>();
|
||||
float actDistance = radius * radius +
|
||||
height * height;
|
||||
float textDistance =
|
||||
actDistance + 2.0f * 2.0f;
|
||||
Ogre::Vector2 screenPos =
|
||||
projectToScreen(
|
||||
list.nodes[p].position);
|
||||
list.dynamicNodes[p]
|
||||
.position);
|
||||
if (screenPos.x < 0)
|
||||
continue;
|
||||
std::cout << list.nodes[p].position
|
||||
<< " " << screenPos
|
||||
<< std::endl;
|
||||
ImGui::SetNextWindowPos(ImVec2(
|
||||
screenPos.x, screenPos.y));
|
||||
// FIXME: move this to system to filter points there
|
||||
Ogre::Vector3 hitPosition;
|
||||
JPH::BodyID hitBody;
|
||||
bool hit =
|
||||
JoltPhysicsWrapper::getSingleton()
|
||||
.raycastQuery(
|
||||
list.dynamicNodes[p]
|
||||
.position,
|
||||
cameraPos,
|
||||
hitPosition,
|
||||
hitBody);
|
||||
if (hit)
|
||||
continue;
|
||||
|
||||
if (list.selected == -1) {
|
||||
if (distance < actDistance)
|
||||
list.selected = p;
|
||||
}
|
||||
ImDrawList *drawList =
|
||||
ImGui::GetBackgroundDrawList();
|
||||
ImVec2 center = ImVec2(screenPos.x,
|
||||
screenPos.y);
|
||||
float circleRadius = 8.0f;
|
||||
ImColor circleColor(
|
||||
ImVec4(0.3f, 0.3f, 0.3f, 1.0f));
|
||||
if (p == list.selected) {
|
||||
circleRadius = 16.0f;
|
||||
circleColor = ImColor(
|
||||
ImVec4(0, 0, 1, 1));
|
||||
}
|
||||
drawList->AddCircleFilled(center,
|
||||
circleRadius,
|
||||
circleColor);
|
||||
drawList->AddCircle(
|
||||
center, circleRadius,
|
||||
IM_COL32(64, 64, 255, 255));
|
||||
ImVec2 textSize = ImGui::CalcTextSize(
|
||||
list.dynamicNodes[p]
|
||||
.action_text.c_str());
|
||||
ImVec2 textPos = ImVec2(
|
||||
center.x - (textSize.x * 0.5f),
|
||||
center.y + circleRadius + 4.0f);
|
||||
if (distance < textDistance) {
|
||||
drawList->AddText(
|
||||
ImVec2(textPos.x + 1,
|
||||
textPos.y + 1),
|
||||
IM_COL32(0, 0, 0, 200),
|
||||
list.dynamicNodes[p]
|
||||
.action_text
|
||||
.c_str());
|
||||
drawList->AddText(
|
||||
ImVec2(textPos.x,
|
||||
textPos.y),
|
||||
IM_COL32(255, 32, 64,
|
||||
255),
|
||||
list.dynamicNodes[p]
|
||||
.action_text
|
||||
.c_str());
|
||||
}
|
||||
#if 0
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(screenPos.x,
|
||||
screenPos.y),
|
||||
ImGuiCond_Always,
|
||||
ImVec2(0.5f, 0.5f));
|
||||
ImGui::Begin(
|
||||
("SensorLabel##" +
|
||||
Ogre::StringConverter::toString(
|
||||
@@ -524,10 +648,27 @@ struct GUIListener : public Ogre::RenderTargetListener {
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoBackground |
|
||||
ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_AlwaysAutoResize |
|
||||
ImGuiWindowFlags_NoInputs);
|
||||
ImGui::TextColored(ImVec4(1, 0, 0, 1),
|
||||
"SENSOR TRIGGERED");
|
||||
ImDrawList *drawList =
|
||||
ImGui::GetWindowDrawList();
|
||||
ImVec2 cursor =
|
||||
ImGui::GetCursorScreenPos();
|
||||
drawList->AddCircleFilled(
|
||||
ImVec2(cursor.x, cursor.y),
|
||||
16.0f,
|
||||
IM_COL32(0, 0, 255, 255));
|
||||
drawList->AddCircle(
|
||||
ImVec2(cursor.x, cursor.y),
|
||||
16.0f,
|
||||
IM_COL32(32, 32, 255, 255));
|
||||
|
||||
ImGui::TextColored(
|
||||
ImVec4(1, 0, 0, 1), "%s",
|
||||
list.nodes[p]
|
||||
.action_text.c_str());
|
||||
ImGui::End();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#ifndef __GUIMODULECOMMON_H__
|
||||
#define __GUIMODULECOMMON_H__
|
||||
#include <iostream>
|
||||
#include <Ogre.h>
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
namespace ECS
|
||||
{
|
||||
|
||||
@@ -12,6 +16,83 @@ struct GUI {
|
||||
Ogre::String narrationText;
|
||||
std::vector<Ogre::String> choices;
|
||||
int narration_answer;
|
||||
struct NarrationHandler {
|
||||
private:
|
||||
Ogre::String mnarrationText;
|
||||
std::vector<Ogre::String> mchoices;
|
||||
int narration_answer;
|
||||
|
||||
private:
|
||||
bool complete;
|
||||
bool active;
|
||||
public:
|
||||
bool is_complete()
|
||||
{
|
||||
return complete;
|
||||
}
|
||||
bool is_active()
|
||||
{
|
||||
return active;
|
||||
}
|
||||
const Ogre::String &getNarrationText() const
|
||||
{
|
||||
return mnarrationText;
|
||||
}
|
||||
const std::vector<Ogre::String> &getChoices() const
|
||||
{
|
||||
return mchoices;
|
||||
}
|
||||
void progress()
|
||||
{
|
||||
_event("narration_progress");
|
||||
}
|
||||
void setNarrationAnswer(int answer)
|
||||
{
|
||||
narration_answer = answer;
|
||||
_event("narration_answered");
|
||||
}
|
||||
int getNarrationAnswer() const
|
||||
{
|
||||
return narration_answer;
|
||||
}
|
||||
|
||||
NarrationHandler(): complete(false), active(false) {}
|
||||
private:
|
||||
virtual void finish() = 0;
|
||||
virtual void activate() = 0;
|
||||
virtual void event(const Ogre::String &event) = 0;
|
||||
protected:
|
||||
void _activate()
|
||||
{
|
||||
activate();
|
||||
active = true;
|
||||
}
|
||||
void _finish()
|
||||
{
|
||||
finish();
|
||||
complete = true;
|
||||
}
|
||||
void _narration(const Ogre::String &text, const std::vector<Ogre::String> &choices)
|
||||
{
|
||||
mnarrationText = text;
|
||||
mchoices = choices;
|
||||
|
||||
}
|
||||
void _clear_narration()
|
||||
{
|
||||
mnarrationText = "";
|
||||
mchoices.clear();
|
||||
}
|
||||
public:
|
||||
void _event(const Ogre::String &ev)
|
||||
{
|
||||
if (!active && !complete)
|
||||
_activate();
|
||||
event(ev);
|
||||
}
|
||||
virtual ~NarrationHandler() {}
|
||||
};
|
||||
|
||||
static void setWindowGrab(bool g = true)
|
||||
{
|
||||
ECS::GUI &gui = ECS::get().get_mut<GUI>();
|
||||
@@ -30,6 +111,16 @@ struct GUI {
|
||||
ECS::get().modified<ECS::GUI>();
|
||||
setWindowGrab(true);
|
||||
}
|
||||
std::vector<NarrationHandler *> narrationHandlers;
|
||||
void addNarrationHandler(struct NarrationHandler *handler)
|
||||
{
|
||||
narrationHandlers.push_back(handler);
|
||||
}
|
||||
void removeNarrationHandler(struct NarrationHandler *handler)
|
||||
{
|
||||
auto it = std::find(narrationHandlers.begin(), narrationHandlers.end(), handler);
|
||||
narrationHandlers.erase(it);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -190,6 +190,8 @@ void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
ecs.import <LuaModule>();
|
||||
// ecs.import <WorldMapModule>();
|
||||
ecs.import <CharacterAnimationModule>();
|
||||
ecs.import <PlayerActionModule>();
|
||||
ecs.add<ActionNodeList>();
|
||||
|
||||
ecs.system<EngineData>("UpdateDelta")
|
||||
.kind(flecs::OnUpdate)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "EventTriggerModule.h"
|
||||
#include "SlotsModule.h"
|
||||
#include "world-build.h"
|
||||
#include "PlayerActionModule.h"
|
||||
#include "LuaData.h"
|
||||
#include "luaaa.hpp"
|
||||
extern "C" {
|
||||
@@ -299,7 +300,20 @@ LuaData::LuaData()
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "setup_handler");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
luaL_checktype(L, 1, LUA_TSTRING);
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||
ECS::get_mut<PlayerActionModule>().setupLuaActionHandler(L);
|
||||
ECS::modified<PlayerActionModule>();
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "setup_action_handler");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
// FIXME
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "setup_narration_handler");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
int args = lua_gettop(L);
|
||||
if (args < 1)
|
||||
return 0;
|
||||
@@ -846,6 +860,7 @@ LuaModule::LuaModule(flecs::world &ecs)
|
||||
ecs.module<LuaModule>();
|
||||
ecs.import <SlotsModule>();
|
||||
ecs.import <VehicleManagerModule>();
|
||||
ecs.import <PlayerActionModule>();
|
||||
ecs.component<LuaChildEventTrigger>();
|
||||
ecs.component<LuaBase>()
|
||||
.on_add([](LuaBase &lua) {
|
||||
|
||||
@@ -11,7 +11,7 @@ struct LuaData {
|
||||
lua_State *L;
|
||||
std::vector<int> setup_handlers;
|
||||
int setup_handler();
|
||||
int call_handler(const Ogre::String &event);
|
||||
int call_handler(const Ogre::String &event);
|
||||
int call_handler(const Ogre::String &event, flecs::entity e,
|
||||
flecs::entity o);
|
||||
|
||||
|
||||
@@ -172,14 +172,18 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
|
||||
.event(flecs::OnRemove)
|
||||
.each([&](flecs::entity e, const JPH::BodyID &id) {
|
||||
JoltPhysicsWrapper::getSingleton().removeBody(id);
|
||||
JoltPhysicsWrapper::getSingleton().destroyBody(id);
|
||||
if (e.has<CharacterBase>() || e.has<Character>())
|
||||
return;
|
||||
JoltPhysicsWrapper::getSingleton().destroyBody(id);
|
||||
std::cout << "body destroyed" << std::endl;
|
||||
});
|
||||
ecs.observer<const EngineData, const CharacterBase>("SetupCharacterPh")
|
||||
.event(flecs::OnSet)
|
||||
ecs.system<const EngineData, const CharacterBase>("SetupCharacterPh")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.without<CharacterBody>()
|
||||
.write<CharacterBody>()
|
||||
.without<CharacterBody>()
|
||||
.without<JPH::BodyID>()
|
||||
.write<CharacterBody>()
|
||||
.write<JPH::BodyID>()
|
||||
.each([](flecs::entity e, const EngineData &eng,
|
||||
const CharacterBase &base) {
|
||||
CharacterBody &b = e.ensure<CharacterBody>();
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
#include <nanoflann.hpp>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <flecs.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
#include "CharacterManagerModule.h"
|
||||
#include "CharacterModule.h"
|
||||
#include "items.h"
|
||||
#include "GUIModule.h"
|
||||
#include "GUIModuleCommon.h"
|
||||
#include "LuaData.h"
|
||||
#include "PlayerActionModule.h"
|
||||
|
||||
namespace ECS
|
||||
@@ -51,13 +59,69 @@ struct ActionNodeList::indexObject {
|
||||
}
|
||||
};
|
||||
|
||||
struct TestNarrativeHandler : GUI::NarrationHandler {
|
||||
int count;
|
||||
TestNarrativeHandler()
|
||||
: GUI::NarrationHandler()
|
||||
, count(0)
|
||||
{
|
||||
}
|
||||
void finish() override
|
||||
{
|
||||
_clear_narration();
|
||||
}
|
||||
void activate() override
|
||||
{
|
||||
_narration("Dialogue...", {});
|
||||
count = 0;
|
||||
}
|
||||
void event(const Ogre::String &evt) override
|
||||
{
|
||||
if (evt == "narration_progress" ||
|
||||
evt == "narration_answered") {
|
||||
count++;
|
||||
if (count == 1) {
|
||||
_narration(
|
||||
"Question..." +
|
||||
Ogre::StringConverter::toString(
|
||||
count),
|
||||
{ "Answer1", "Answer2" });
|
||||
} else {
|
||||
_narration(
|
||||
"Whatever..." +
|
||||
Ogre::StringConverter::toString(
|
||||
count),
|
||||
{});
|
||||
}
|
||||
if (count > 5)
|
||||
_finish();
|
||||
}
|
||||
if (evt == "narration_answered")
|
||||
std::cout << "answer: " << getNarrationAnswer()
|
||||
<< std::endl;
|
||||
}
|
||||
};
|
||||
struct SimpleWordHandler : PlayerActionModule::ActionWordHandler {
|
||||
void operator()(flecs::entity town, int index,
|
||||
const Ogre::String &word) override
|
||||
{
|
||||
TestNarrativeHandler *handle = OGRE_NEW TestNarrativeHandler();
|
||||
ECS::get_mut<GUI>().addNarrationHandler(handle);
|
||||
ECS::modified<GUI>();
|
||||
}
|
||||
};
|
||||
|
||||
PlayerActionModule::PlayerActionModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<PlayerActionModule>();
|
||||
ecs.import <CharacterManagerModule>();
|
||||
ecs.component<ActionNodeList>()
|
||||
.on_add([](flecs::entity e, ActionNodeList &alist) {
|
||||
alist.dirty = true;
|
||||
alist.nodes.reserve(1000);
|
||||
alist.dynamicNodes.reserve(1000);
|
||||
alist.selected = -1;
|
||||
alist.busy = false;
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
#if 0
|
||||
@@ -79,25 +143,166 @@ PlayerActionModule::PlayerActionModule(flecs::world &ecs)
|
||||
}
|
||||
});
|
||||
#endif
|
||||
ecs.system<ActionNodeList>("updateNodeList")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](ActionNodeList &list) {
|
||||
if (list.busy)
|
||||
return;
|
||||
if (list.nodes.size() > 0) {
|
||||
Ogre::SceneNode *cameraNode =
|
||||
ECS::get<Camera>().mCameraNode;
|
||||
Ogre::Vector3 cameraPos =
|
||||
cameraNode->_getDerivedPosition();
|
||||
flecs::entity player =
|
||||
ECS::get<CharacterManagerModule>()
|
||||
.getPlayer();
|
||||
if (player.is_valid()) {
|
||||
Ogre::Vector3 playerPos =
|
||||
player.get<CharacterBase>()
|
||||
.mBodyNode
|
||||
->_getDerivedPosition();
|
||||
list.query(playerPos, list.points,
|
||||
list.distances);
|
||||
} else {
|
||||
list.query(cameraPos, list.points,
|
||||
list.distances);
|
||||
}
|
||||
}
|
||||
});
|
||||
ecs.system<ActionNodeList, const Input>("ActivateActionNode")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](ActionNodeList &list, const Input &input) {
|
||||
if (input.control & 32)
|
||||
std::cout << "act pressed" << std::endl;
|
||||
if (list.busy)
|
||||
return;
|
||||
if (input.act_pressed && list.selected >= 0) {
|
||||
std::cout << list.dynamicNodes[list.selected]
|
||||
.props.dump(4)
|
||||
<< std::endl;
|
||||
flecs::entity_t townid =
|
||||
list.dynamicNodes[list.selected]
|
||||
.props["town"]
|
||||
.get<flecs::entity_t>();
|
||||
flecs::entity town = ECS::get().entity(townid);
|
||||
int index = list.dynamicNodes[list.selected]
|
||||
.props["index"]
|
||||
.get<int>();
|
||||
for (auto it = actionWords.begin();
|
||||
it != actionWords.end(); it++) {
|
||||
if (it->first ==
|
||||
list.dynamicNodes[list.selected]
|
||||
.action) {
|
||||
(*it->second)(
|
||||
town, index,
|
||||
list.dynamicNodes
|
||||
[list.selected]
|
||||
.action);
|
||||
list.busy = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ECS::get<GUI>().enabled)
|
||||
list.busy = false;
|
||||
});
|
||||
SimpleWordHandler *handler = OGRE_NEW SimpleWordHandler;
|
||||
addWordHandler("talk", handler);
|
||||
}
|
||||
|
||||
void PlayerActionModule::addWordHandler(const Ogre::String &word,
|
||||
ActionWordHandler *handler)
|
||||
{
|
||||
actionWords.insert({ word, handler });
|
||||
}
|
||||
|
||||
void PlayerActionModule::removeWordHandler(const Ogre::String &word,
|
||||
ActionWordHandler *handler)
|
||||
{
|
||||
for (auto it = actionWords.begin(); it != actionWords.end();) {
|
||||
if (it->first == word && it->second == handler)
|
||||
it = actionWords.erase(it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
struct LuaWordHandler : PlayerActionModule::ActionWordHandler {
|
||||
lua_State *L;
|
||||
int ref;
|
||||
void operator()(flecs::entity town, int index,
|
||||
const Ogre::String &word) override
|
||||
{
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
lua_pushinteger(L, town.id());
|
||||
lua_pushinteger(L, index);
|
||||
lua_pushstring(L, word.c_str());
|
||||
if (lua_pcall(L, 3, 0, 0)) {
|
||||
Ogre::LogManager::getSingleton().stream()
|
||||
<< lua_tostring(L, -1);
|
||||
OgreAssert(false, "Lua error");
|
||||
}
|
||||
}
|
||||
};
|
||||
void PlayerActionModule::addLuaWordHandler(const Ogre::String &word,
|
||||
lua_State *L, int ref)
|
||||
{
|
||||
struct LuaWordHandler *handler = OGRE_NEW LuaWordHandler;
|
||||
handler->L = L;
|
||||
handler->ref = ref;
|
||||
addWordHandler(word, handler);
|
||||
}
|
||||
|
||||
void PlayerActionModule::removeLuaWordHandler(const Ogre::String &word,
|
||||
lua_State *L, int ref)
|
||||
{
|
||||
for (auto it = actionWords.begin(); it != actionWords.end();) {
|
||||
LuaWordHandler *handler =
|
||||
static_cast<LuaWordHandler *>(it->second);
|
||||
if (it->first == word && handler->L == L && handler->ref == ref)
|
||||
it = actionWords.erase(it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
int PlayerActionModule::setupLuaActionHandler(lua_State *L)
|
||||
{
|
||||
luaL_checktype(L, 1, LUA_TSTRING);
|
||||
luaL_checktype(L, 2, LUA_TFUNCTION);
|
||||
Ogre::String word = lua_tostring(L, 1);
|
||||
lua_pushvalue(L, 2);
|
||||
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
addLuaWordHandler(word, L, ref);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ActionNodeList::build()
|
||||
{
|
||||
indexObj = std::make_shared<ActionNodeList::indexObject>(nodes);
|
||||
dynamicNodes.clear();
|
||||
dynamicNodes.insert(dynamicNodes.end(), nodes.begin(), nodes.end());
|
||||
ECS::get().query_builder<const NPCActionNodes>().each(
|
||||
[&](flecs::entity e, const NPCActionNodes &anodes) {
|
||||
dynamicNodes.insert(dynamicNodes.end(),
|
||||
anodes.anodes.begin(),
|
||||
anodes.anodes.end());
|
||||
});
|
||||
|
||||
indexObj = std::make_shared<ActionNodeList::indexObject>(dynamicNodes);
|
||||
indexObj->index.buildIndex();
|
||||
dirty = false;
|
||||
std::cout << "index built" << std::endl;
|
||||
}
|
||||
|
||||
bool ActionNodeList::query(const Ogre::Vector3 &position,
|
||||
std::vector<size_t> &points)
|
||||
std::vector<size_t> &points,
|
||||
std::vector<float> &distances)
|
||||
{
|
||||
if (dirty)
|
||||
build();
|
||||
build();
|
||||
std::vector<size_t> tmppoints;
|
||||
std::vector<float> tmpdistances;
|
||||
points.clear();
|
||||
points.reserve(4);
|
||||
distances.clear();
|
||||
distances.reserve(4);
|
||||
tmppoints.resize(4);
|
||||
tmpdistances.resize(4);
|
||||
nanoflann::KNNResultSet<float> resultSet(4);
|
||||
@@ -106,9 +311,10 @@ bool ActionNodeList::query(const Ogre::Vector3 &position,
|
||||
nanoflann::SearchParameters());
|
||||
int i;
|
||||
for (i = 0; i < resultSet.size(); i++)
|
||||
if (tmpdistances[i] < 25.0f)
|
||||
if (tmpdistances[i] < 25.0f) {
|
||||
points.push_back(tmppoints[i]);
|
||||
distances.push_back(tmpdistances[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define PLAYERACTIONMODULE_H
|
||||
#include <flecs.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <lua.h>
|
||||
#include <Ogre.h>
|
||||
|
||||
namespace ECS {
|
||||
@@ -13,13 +14,19 @@ struct ActionNodeList {
|
||||
Ogre::String action_text;
|
||||
Ogre::Vector3 position;
|
||||
Ogre::Quaternion rotation;
|
||||
float height;
|
||||
float radius;
|
||||
nlohmann::json props;
|
||||
};
|
||||
std::vector<ActionNode> nodes;
|
||||
std::vector<ActionNode> nodes, dynamicNodes;
|
||||
std::shared_ptr<indexObject> indexObj;
|
||||
std::vector<size_t> points;
|
||||
std::vector<float> distances;
|
||||
int selected;
|
||||
bool dirty;
|
||||
bool busy;
|
||||
void build();
|
||||
bool query(const Ogre::Vector3 &position, std::vector<size_t> &points);
|
||||
bool query(const Ogre::Vector3 &position, std::vector<size_t> &points, std::vector<float> &distances);
|
||||
int addNode(struct ActionNodeList::ActionNode &node)
|
||||
{
|
||||
int index = nodes.size();
|
||||
@@ -32,10 +39,24 @@ struct ActionNodeList {
|
||||
nodes.erase(nodes.begin() + index);
|
||||
}
|
||||
};
|
||||
struct NPCActionNodes {
|
||||
std::vector<ActionNodeList::ActionNode> anodes;
|
||||
};
|
||||
struct PlayerActionModule {
|
||||
struct ActionWordHandler {
|
||||
virtual void operator()(flecs::entity town, int index,
|
||||
const Ogre::String &word) = 0;
|
||||
};
|
||||
|
||||
std::multimap<Ogre::String, ActionWordHandler *>
|
||||
actionWords;
|
||||
|
||||
struct PlayerActionModule
|
||||
{
|
||||
PlayerActionModule(flecs::world &ecs);
|
||||
void addWordHandler(const Ogre::String &word, ActionWordHandler *handler);
|
||||
void removeWordHandler(const Ogre::String &word, ActionWordHandler *handler);
|
||||
void addLuaWordHandler(const Ogre::String &word, lua_State *L, int ref);
|
||||
void removeLuaWordHandler(const Ogre::String &word, lua_State *L, int ref);
|
||||
int setupLuaActionHandler(lua_State *L);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "PhysicsModule.h"
|
||||
#include "LuaData.h"
|
||||
#include "PlayerActionModule.h"
|
||||
#include "CharacterManagerModule.h"
|
||||
#include "items.h"
|
||||
#include "town.h"
|
||||
|
||||
@@ -2088,6 +2089,65 @@ void runAllScriptsForTown(flecs::entity e)
|
||||
j["districts"] = districts;
|
||||
StaticGeometryModule::setItemProperties(e, j.dump());
|
||||
}
|
||||
bool editNPCs(nlohmann::json &npcs)
|
||||
{
|
||||
bool changed = false;
|
||||
ImGui::Text("NPC");
|
||||
int id = 0;
|
||||
for (auto &npc : npcs) {
|
||||
ImGui::Text("%s", npc["lastName"].get<Ogre::String>().c_str());
|
||||
if (ImGui::SmallButton(
|
||||
("Spawn##" + Ogre::StringConverter::toString(id))
|
||||
.c_str())) {
|
||||
int sex = npc["sex"].get<int>();
|
||||
const char *models[] = { "normal-male.glb",
|
||||
"normal-female.glb" };
|
||||
Ogre::Vector3 npcPosition;
|
||||
Ogre::Quaternion npcOrientation;
|
||||
from_json(npc["position"], npcPosition);
|
||||
from_json(npc["orientation"], npcOrientation);
|
||||
|
||||
// FIXME: create TownCharacterManager and register NPCs through there
|
||||
ECS::get_mut<CharacterManagerModule>()
|
||||
.createCharacterData(models[sex], npcPosition,
|
||||
npcOrientation);
|
||||
}
|
||||
if (ImGui::SmallButton(
|
||||
("Delete##" + Ogre::StringConverter::toString(id))
|
||||
.c_str())) {
|
||||
npcs.erase(id);
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
id++;
|
||||
}
|
||||
ImGui::Text("New NPC");
|
||||
static char lastName[64] = { 0 };
|
||||
ImGui::InputText("Last name", lastName, sizeof(lastName));
|
||||
static char firstName[64] = { 0 };
|
||||
ImGui::InputText("First name", firstName, sizeof(firstName));
|
||||
static char tags[256] = { 0 };
|
||||
ImGui::InputText("Tags", tags, sizeof(firstName));
|
||||
static int selection = 0;
|
||||
const char *items[] = { "Male", "Female" };
|
||||
ImGui::Combo("Sex", &selection, items, 2);
|
||||
if (ImGui::SmallButton("Add NPC")) {
|
||||
nlohmann::json npc;
|
||||
npc["lastName"] = Ogre::String(lastName);
|
||||
Ogre::Vector3 npcPosition =
|
||||
ECS::get<EditorGizmo>().sceneNode->_getDerivedPosition();
|
||||
Ogre::Quaternion npcOrientation =
|
||||
ECS::get<EditorGizmo>()
|
||||
.sceneNode->_getDerivedOrientation();
|
||||
to_json(npc["position"], npcPosition);
|
||||
to_json(npc["orientation"], npcOrientation);
|
||||
npc["sex"] = selection;
|
||||
npcs.push_back(npc);
|
||||
changed = true;
|
||||
}
|
||||
ImGui::Separator();
|
||||
return changed;
|
||||
}
|
||||
void createTownPopup(const std::pair<flecs::entity, Ogre::String> item)
|
||||
{
|
||||
Ogre::String prop = StaticGeometryModule::getItemProperties(item.first);
|
||||
@@ -2124,6 +2184,11 @@ void createTownPopup(const std::pair<flecs::entity, Ogre::String> item)
|
||||
changed = changed || editColorRects(colorRects);
|
||||
ImGui::Separator();
|
||||
}
|
||||
nlohmann::json npcs = nlohmann::json::array();
|
||||
if (j.find("npcs") != j.end())
|
||||
npcs = j["npcs"];
|
||||
|
||||
changed = changed || editNPCs(npcs);
|
||||
nlohmann::json districts = nlohmann::json::array();
|
||||
for (auto &district : j["districts"])
|
||||
districts.push_back(district);
|
||||
@@ -2168,6 +2233,7 @@ void createTownPopup(const std::pair<flecs::entity, Ogre::String> item)
|
||||
ImGui::Separator();
|
||||
ImGui::Text("%s", j.dump(4).c_str());
|
||||
if (changed) {
|
||||
j["npcs"] = npcs;
|
||||
j["districts"] = districts;
|
||||
j["colorRects"] = colorRects;
|
||||
j["lotTemplates"] = lotTemplates;
|
||||
@@ -5458,6 +5524,7 @@ struct TownDecorateFurniture : TownTask {
|
||||
.child_of(e)
|
||||
.set<FurnitureInstance>(
|
||||
{ node });
|
||||
#if 0
|
||||
if (furniture.find(
|
||||
"sensors") !=
|
||||
furniture.end()) {
|
||||
@@ -5599,6 +5666,93 @@ struct TownDecorateFurniture : TownTask {
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (furniture.find(
|
||||
"actions") !=
|
||||
furniture.end()) {
|
||||
for (const auto &
|
||||
action :
|
||||
furniture["actions"]) {
|
||||
std::cout
|
||||
<< "SENSOR: "
|
||||
<< action.dump()
|
||||
<< std::endl;
|
||||
std::cout
|
||||
<< furniture
|
||||
.dump()
|
||||
<< std::endl;
|
||||
Ogre::Vector3
|
||||
actionPosition;
|
||||
actionPosition
|
||||
.x =
|
||||
action["position_x"]
|
||||
.get<float>();
|
||||
actionPosition
|
||||
.y =
|
||||
action["position_y"]
|
||||
.get<float>();
|
||||
actionPosition
|
||||
.z =
|
||||
action["position_z"]
|
||||
.get<float>();
|
||||
Ogre::Quaternion worldSensorRotation =
|
||||
worldCenterOrientation *
|
||||
Ogre::Quaternion(
|
||||
Ogre::Degree(
|
||||
90.0f *
|
||||
(float)rotation),
|
||||
Ogre::Vector3::
|
||||
UNIT_Y);
|
||||
Ogre::Vector3 worldSensorPosition =
|
||||
worldCenterPosition +
|
||||
offsetX +
|
||||
offsetZ +
|
||||
offsetY +
|
||||
offset +
|
||||
worldSensorRotation *
|
||||
actionPosition;
|
||||
float height =
|
||||
action["height"]
|
||||
.get<float>();
|
||||
float radius =
|
||||
action["radius"]
|
||||
.get<float>();
|
||||
if (ECS::get()
|
||||
.has<ActionNodeList>()) {
|
||||
ActionNodeList::ActionNode
|
||||
anode;
|
||||
anode.action =
|
||||
action["action"]
|
||||
.get<Ogre::String>();
|
||||
anode.action_text =
|
||||
action["action_text"]
|
||||
.get<Ogre::String>();
|
||||
anode.radius =
|
||||
action["radius"]
|
||||
.get<float>();
|
||||
anode.height =
|
||||
action["height"]
|
||||
.get<float>();
|
||||
anode.props =
|
||||
action;
|
||||
anode.position =
|
||||
worldSensorPosition;
|
||||
anode.rotation =
|
||||
worldSensorRotation;
|
||||
ECS::get_mut<
|
||||
ActionNodeList>()
|
||||
.addNode(
|
||||
anode);
|
||||
std::cout
|
||||
<< "action: "
|
||||
<< action.dump(
|
||||
4)
|
||||
<< std::endl;
|
||||
ECS::modified<
|
||||
ActionNodeList>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5620,6 +5774,11 @@ struct TownDecorateFurniture : TownTask {
|
||||
}
|
||||
};
|
||||
|
||||
void registerTownNPCs(flecs::entity e)
|
||||
{
|
||||
ECS::get_mut<CharacterManagerModule>().registerTownCharacters(e);
|
||||
ECS::modified<CharacterManagerModule>();
|
||||
}
|
||||
void createTown(flecs::entity e, Ogre::SceneNode *sceneNode,
|
||||
Ogre::StaticGeometry *geo)
|
||||
{
|
||||
@@ -5693,6 +5852,7 @@ void createTown(flecs::entity e, Ogre::SceneNode *sceneNode,
|
||||
geo->build();
|
||||
});
|
||||
});
|
||||
registerTownNPCs(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user