From 847aab6ed085352446967323583ee65aef04c258 Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Tue, 2 Sep 2025 16:07:03 +0300 Subject: [PATCH] Converted to ECS, physics fix --- CMakeLists.txt | 2 - Game.cpp | 25 +- src/characters/CMakeLists.txt | 6 +- src/gamedata/CMakeLists.txt | 2 +- src/gamedata/CharacterModule.cpp | 533 ++++++++++++++++++++++++++++++ src/gamedata/CharacterModule.h | 55 ++++ src/gamedata/Components.h | 50 +++ src/gamedata/GameData.cpp | 538 +------------------------------ src/gamedata/GameData.h | 88 ----- src/gamedata/WaterModule.cpp | 87 +++++ src/gamedata/WaterModule.h | 17 + src/terrain/terrain.cpp | 2 +- 12 files changed, 766 insertions(+), 639 deletions(-) create mode 100644 src/gamedata/CharacterModule.cpp create mode 100644 src/gamedata/CharacterModule.h create mode 100644 src/gamedata/Components.h create mode 100644 src/gamedata/WaterModule.cpp create mode 100644 src/gamedata/WaterModule.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 29cd3aa..4e451f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -87,7 +87,6 @@ add_dependencies(0_Bootstrap stage_files import_vrm) add_executable(Editor Editor.cpp ${TERRAIN_SRC} ${WATER_SRC}) target_link_libraries(Editor OgreBites OgreBullet OgrePaging OgreTerrain OgreMeshLodGenerator OgreProcedural::OgreProcedural ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY} - controller -Wl,--as-needed ) if(OGRE_STATIC) @@ -99,7 +98,6 @@ target_include_directories(Game PRIVATE src/gamedata) target_link_libraries(Game OgreBites OgreBullet OgrePaging OgreTerrain OgreMeshLodGenerator OgreProcedural::OgreProcedural ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY} - controller GameData sound flecs::flecs_static diff --git a/Game.cpp b/Game.cpp index 8d1664a..1000a77 100644 --- a/Game.cpp +++ b/Game.cpp @@ -12,8 +12,9 @@ #include "src/terrain/terrain.h" #include "water/water.h" -#include "src/characters/controller.h" #include "GameData.h" +#include "Components.h" +#include "CharacterModule.h" #include "sound.h" class App; class SkyRenderer : public Ogre::SceneManager::Listener { @@ -246,13 +247,12 @@ public: float panel_width; void initGui(); }; -#define WATER +#undef WATER class App : public OgreBites::ApplicationContext { std::unique_ptr mDynWorld; std::unique_ptr mDbgDraw; Ogre::SceneNode *mCameraNode, *mCameraPivot, *mCameraGoal; Ogre::Camera *mCamera; - CharacterController *mCharacterController; Ogre::Real mPivotPitch; Ogre::SceneManager *mScnMgr; OgreBites::InputListenerChain mInput; @@ -284,6 +284,7 @@ class App : public OgreBites::ApplicationContext { , mApp(app) , gui_active(false) , fast(false) + , control(0) { } bool keyPressed(const OgreBites::KeyboardEvent &evt) override @@ -391,8 +392,7 @@ class App : public OgreBites::ApplicationContext { fps_timer.reset(); } update(evt.timeSinceLastFrame); - if (mApp->getCharacterController() && !gui_active && - mApp->isTerrainReady()) { + if (!gui_active && mApp->isTerrainReady()) { OgreAssert(mApp->isTerrainReady(), "terrain is not ready"); } @@ -424,7 +424,6 @@ public: , mDynWorld(new Ogre::Bullet::DynamicsWorld( Ogre::Vector3(0, -9.8, 0))) , m_terrain(mDynWorld->getBtWorld()) - , mCharacterController(nullptr) , mTerrainReady(false) { } @@ -529,9 +528,6 @@ public: void setupPlayer() { OgreAssert(mDynWorld.get(), "No physics world controller"); - mCharacterController = new CharacterController( - mCameraNode, mCamera, mScnMgr, mDynWorld.get()); - OgreAssert(mCharacterController, "No character controller"); } Ogre::SceneManager *getSceneManager() { @@ -632,9 +628,10 @@ public: } void setupInput() { - mInput = OgreBites::InputListenerChain( - { getImGuiInputListener(), &mKbd, - mCharacterController }); + mInput = OgreBites::InputListenerChain({ + getImGuiInputListener(), + &mKbd, + }); addInputListener(&mInput); } void createContent() @@ -698,10 +695,6 @@ public: m_terrain.setupTerrain(mCamera, mSun, mDynWorld.get(), mDbgDraw.get()); } - CharacterController *getCharacterController() - { - return mCharacterController; - } flecs::entity getPlayer() const { flecs::entity player = diff --git a/src/characters/CMakeLists.txt b/src/characters/CMakeLists.txt index e74c6ba..3701c94 100644 --- a/src/characters/CMakeLists.txt +++ b/src/characters/CMakeLists.txt @@ -1,5 +1,5 @@ project(characters) find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain CONFIG) -add_library(controller STATIC controller.cpp) -target_link_libraries(controller PUBLIC OgreMain OgreBites PRIVATE GameData) -target_include_directories(controller PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file +# add_library(controller STATIC controller.cpp) +# target_link_libraries(controller PUBLIC OgreMain OgreBites PRIVATE GameData) +# target_include_directories(controller PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/gamedata/CMakeLists.txt b/src/gamedata/CMakeLists.txt index 976ec90..29289e9 100644 --- a/src/gamedata/CMakeLists.txt +++ b/src/gamedata/CMakeLists.txt @@ -1,5 +1,5 @@ project(gamedata) find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain CONFIG) -add_library(GameData STATIC GameData.cpp) +add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp) target_link_libraries(GameData PUBLIC OgreMain OgreBullet flecs::flecs_static) target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/src/gamedata/CharacterModule.cpp b/src/gamedata/CharacterModule.cpp new file mode 100644 index 0000000..5ae0fcb --- /dev/null +++ b/src/gamedata/CharacterModule.cpp @@ -0,0 +1,533 @@ +#include +#include +#include +#include "GameData.h" +#include "CharacterModule.h" +#include "Components.h" +namespace ECS +{ +CharacterModule::CharacterModule(flecs::world &ecs) +{ + ecs.module(); + player = ecs.entity("player"); + player.set({ AnimationControl::ANIM_NONE, + AnimationControl::ANIM_NONE, false, + false }); + player.set( + { "normal-male.glb", nullptr, nullptr, nullptr }); + player.set( + { nullptr, nullptr, nullptr, { 0, 0, 0 }, false, false }); + player.add(); + player.add(); + ecs.system("HandleInput") + .kind(flecs::OnUpdate) + .each([this](Input &input, Camera &camera) { + /* handle input */ + // if (input.control == input.control_prev) + // return; + uint32_t pressed = input.control & ~input.control_prev; + uint32_t released = input.control_prev & ~input.control; + uint32_t active = input.control; + float zaxis = input.motion.z; + zaxis *= 0.9f; + if (pressed & 1) + std::cout << "W pressed\n"; + if (released & 1) + std::cout << "W released\n"; + if (active & 1) + zaxis -= 1.0f; + if (active & 4) + zaxis += 1.0f; + if (zaxis > -1.0f && zaxis < 1.0f) + zaxis = 0.0f; + else + zaxis = Ogre::Math::Sign(zaxis); + input.motion.z = zaxis; + float xaxis = input.motion.x; + xaxis *= 0.9f; + if (active & 2) + xaxis = -1.0f; + if (active & 8) + xaxis += 1.0f; + if (xaxis > -1.0f && xaxis < 1.0f) + xaxis = 0.0f; + else + xaxis = Ogre::Math::Sign(xaxis); + input.motion.x = xaxis; + if (active & 16) + input.fast = true; + else + input.fast = false; + input.control_prev = input.control; + if (input.mouse_moved) { + updateCameraGoal(camera, -0.18f * input.mouse.x, + -0.12f * input.mouse.y, 0); + input.mouse_moved = false; + input.mouse.x = 0; + input.mouse.y = 0; + } + if (input.wheel_moved) { + updateCameraGoal(camera, 0, 0, + -0.15f * input.wheel_y); + input.wheel_moved = false; + input.wheel_y = 0; + } + ECS::get().modified(); + }); + ecs.system("HandleAnimations") + .kind(flecs::OnUpdate) + .each([this](flecs::entity e, const CharacterBase &ch, + AnimationControl &anim) { + if (!anim.configured && ch.mSkeleton) { + int i, j; + ch.mSkeleton->setBlendMode( + Ogre::ANIMBLEND_CUMULATIVE); + Ogre::String + animNames[AnimationControl::NUM_ANIMS] = { + "idle", "walking", "running" + }; + for (i = 0; i < AnimationControl::NUM_ANIMS; + i++) { + anim.mAnims[i] = + ch.mBodyEnt->getAnimationState( + animNames[i]); + anim.mAnims[i]->setLoop(true); + anim.mAnims[i]->setEnabled(true); + anim.mAnims[i]->setWeight(0); + anim.mFadingIn[i] = false; + anim.mFadingOut[i] = false; + anim.mSkelAnimations[i] = + ch.mSkeleton->getAnimation( + animNames[i]); + for (const auto &it : + anim.mSkelAnimations[i] + ->_getNodeTrackList()) { + Ogre::NodeAnimationTrack *track = + it.second; + Ogre::String trackName = + track->getAssociatedNode() + ->getName(); + if (trackName == + "mixamorig:Hips") { + anim.mHipsTracks[i] = + track; + } else if (trackName == + "Root") { + anim.mRootTracks[i] = + track; + // mRootTracks[i]->removeAllKeyFrames(); + } + } + Ogre::Vector3 delta = + Ogre::Vector3::ZERO; + Ogre::Vector3 motion = + Ogre::Vector3::ZERO; + for (j = 0; + j < anim.mRootTracks[i] + ->getNumKeyFrames(); + j++) { + Ogre::Vector3 trans = + anim.mRootTracks[i] + ->getNodeKeyFrame( + j) + ->getTranslate(); + if (j == 0) + delta = trans; + else + delta = trans - motion; + anim.mRootTracks[i] + ->getNodeKeyFrame(j) + ->setTranslate(delta); + motion = trans; + } + } + anim.nextAnim = AnimationControl::ANIM_IDLE; + setAnimation(anim); + anim.configured = true; + } + }); + ecs.system("HandleAnimations0") + .kind(flecs::OnUpdate) + .each([this](flecs::entity e, AnimationControl &anim) { + if (anim.currentAnim != anim.nextAnim) + setAnimation(anim); + }); + ecs.system( + "HandleAnimations1") + .kind(flecs::OnUpdate) + .each([this](EngineData &eng, CharacterBase &ch, + AnimationControl &anim) { + float delta = eng.delta; + Ogre::Real animSpeed = 1; + if (anim.currentAnim != AnimationControl::ANIM_NONE) { + if (anim.currentAnim == + AnimationControl::ANIM_WALK) + anim.mAnims[anim.currentAnim]->addTime( + delta * 1.0f); + else + anim.mAnims[anim.currentAnim]->addTime( + delta * animSpeed); + } + fadeAnimations(anim, delta); + if (!ch.mRootBone) + return; + ch.mBoneMotion = ch.mRootBone->getPosition(); + }); + ecs.system( + "HandleRootMotion") + .kind(flecs::OnUpdate) + .each([this](flecs::entity e, CharacterBase &ch, + CharacterBody &body, AnimationControl &anim) { + float delta = e.world().delta_time(); + if (!ch.mBodyNode) + return; + Ogre::Quaternion rot = ch.mBodyNode->getOrientation(); + Ogre::Vector3 pos = ch.mBodyNode->getPosition(); + Ogre::Vector3 boneMotion = ch.mBoneMotion; + Ogre::Vector3 velocity = rot * boneMotion / delta; + OgreAssert(delta > 0.0f, "Zero delta"); + int maxPen = 0; + Ogre::Vector3 colNormal; + bool is_on_floor = false; + bool penetration = false; + Ogre::Vector3 gravity(0, -9.8, 0); + body.gvelocity += gravity * delta; + velocity += body.gvelocity; + Ogre::Vector3 rotMotion = velocity * delta; + btVector3 currentPosition = + body.mGhostObject->getWorldTransform() + .getOrigin(); + is_on_floor = body.mController->isOnFloor(); + penetration = body.mController->isPenetrating(); + if (is_on_floor) + body.gvelocity = + Ogre::Vector3(0.0f, 0.0f, 0.0f); + + btTransform from( + Ogre::Bullet::convert( + ch.mBodyNode->getOrientation()), + Ogre::Bullet::convert( + ch.mBodyNode->getPosition())); + ch.mBodyNode->setPosition(ch.mBodyNode->getPosition() + + rotMotion); + ch.mBoneMotion = Ogre::Vector3(0, 0, 0); + }); + ecs.system("HandlePlayerAnimations") + .kind(flecs::OnUpdate) + .with() + .with() + .each([](const Input &input, AnimationControl &anim) { + if (!anim.configured) + return; + bool controls_idle = input.motion.zeroLength(); + bool anim_is_idle = anim.currentAnim == + AnimationControl::ANIM_IDLE; + bool anim_is_walking = anim.currentAnim == + AnimationControl::ANIM_WALK; + bool anim_is_running = anim.currentAnim == + AnimationControl::ANIM_RUN; + bool anim_is_motion = anim_is_walking || + anim_is_running; + if (!controls_idle && anim_is_idle) { + anim.reset = true; + if (input.fast) + anim.nextAnim = + AnimationControl::ANIM_RUN; + else + anim.nextAnim = + AnimationControl::ANIM_WALK; + } else + anim.reset = false; + if (controls_idle && anim_is_motion) + anim.nextAnim = AnimationControl::ANIM_IDLE; + else if (!controls_idle && anim_is_motion) { + if (input.fast && anim_is_walking) + anim.nextAnim = + AnimationControl::ANIM_RUN; + else if (!input.fast && anim_is_running) + anim.nextAnim = + AnimationControl::ANIM_WALK; + } + }); + ecs.system( + "UpdateCharacterBase") + .kind(flecs::OnUpdate) + .with() + .each([](const EngineData &eng, CharacterBase &ch, + CharacterBody &body) { + if (!ch.mBodyNode) { + ch.mBodyEnt = eng.mScnMgr->createEntity( + "normal-male.glb"); + ch.mBodyNode = eng.mScnMgr->getRootSceneNode() + ->createChildSceneNode(); + ch.mBodyNode->attachObject(ch.mBodyEnt); + ch.mSkeleton = ch.mBodyEnt->getSkeleton(); + body.mGhostObject = + new btPairCachingGhostObject(); + body.mCollisionShape = new btCompoundShape; + body.mGhostObject->setCollisionShape( + body.mCollisionShape); + { + btVector3 inertia(0, 0, 0); + // mCollisionShape = new btCompoundShape(); + btScalar height = 1.0f; + btScalar radius = 0.3f; + btCapsuleShape *shape = + new btCapsuleShape( + radius, + 2 * height - + 2 * radius); + btTransform transform; + transform.setIdentity(); + transform.setOrigin(btVector3(0, 1, 0)); + static_cast( + body.mCollisionShape) + ->addChildShape(transform, + shape); + btScalar masses[1] = { 0 }; + btTransform principal; + static_cast( + body.mCollisionShape) + ->calculatePrincipalAxisTransform( + masses, principal, + inertia); + } + body.mGhostObject->setCollisionFlags( + btCollisionObject::CF_KINEMATIC_OBJECT | + btCollisionObject:: + CF_NO_CONTACT_RESPONSE); + body.mGhostObject->setActivationState( + DISABLE_DEACTIVATION); + eng.mWorld->attachCollisionObject( + body.mGhostObject, ch.mBodyEnt, 1, + 0x7FFFFFFF); + body.mController = + new Ogre::Bullet::KinematicMotionSimple( + body.mGhostObject, + ch.mBodyNode); + OgreAssert(body.mGhostObject, + "Need GhostObject"); + OgreAssert(body.mController, "Need controller"); + eng.mWorld->getBtWorld()->addAction( + body.mController); + + OgreAssert(body.mCollisionShape, + "No collision shape"); + OgreAssert(ch.mSkeleton->hasBone("Root"), + "No root bone"); + ch.mRootBone = ch.mSkeleton->getBone("Root"); + OgreAssert(ch.mRootBone, "No root bone"); + } + }); +#define CAM_HEIGHT 1.6f // height of camera above character's center of mass + ecs.system( + "UpdateCamera") + .kind(flecs::OnUpdate) + .with() + .each([](const EngineData &eng, Camera &camera, + const CharacterBase &ch) { + float delta = eng.delta; + if (!camera.configured) { + // create a pivot at roughly the character's shoulder + camera.mCameraPivot = + eng.mScnMgr->getRootSceneNode() + ->createChildSceneNode(); + camera.mCameraGoal = + camera.mCameraPivot + ->createChildSceneNode( + Ogre::Vector3(0, 2, 3)); + camera.mCameraNode->setPosition( + camera.mCameraPivot->getPosition() + + camera.mCameraGoal->getPosition()); + camera.mCameraPivot->setFixedYawAxis(true); + camera.mCameraGoal->setFixedYawAxis(true); + camera.mCameraNode->setFixedYawAxis(true); + // our model is quite small, so reduce the clipping planes + camera.mCamera->setNearClipDistance(0.1f); + camera.mCamera->setFarClipDistance(700); + + camera.mPivotPitch = 0; + camera.configured = true; + } else { + // place the camera pivot roughly at the character's shoulder + camera.mCameraPivot->setPosition( + ch.mBodyNode->getPosition() + + Ogre::Vector3::UNIT_Y * CAM_HEIGHT); + // move the camera smoothly to the goal + Ogre::Vector3 goalOffset = + camera.mCameraGoal + ->_getDerivedPosition() - + camera.mCameraNode->getPosition(); + camera.mCameraNode->translate(goalOffset * + delta * 9.0f); + // always look at the pivot + camera.mCameraNode->lookAt( + camera.mCameraPivot + ->_getDerivedPosition(), + Ogre::Node::TS_PARENT); + } + }); +#define TURN_SPEED 500.0f // character turning in degrees per second + ecs.system("UpdateBody") + .kind(flecs::OnUpdate) + .with() + .with() + .each([](flecs::entity e, const Input &input, + const Camera &camera, CharacterBase &ch) { + 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 + ch.mGoalDirection += + input.motion.z * + camera.mCameraNode->getOrientation() + .zAxis(); + ch.mGoalDirection += + input.motion.x * + camera.mCameraNode->getOrientation() + .xAxis(); + ch.mGoalDirection.y = 0; + ch.mGoalDirection.normalise(); + + Ogre::Quaternion toGoal = + ch.mBodyNode->getOrientation() + .zAxis() + .getRotationTo( + ch.mGoalDirection); + // calculate how much the character has to turn to face goal direction + Ogre::Real yawToGoal = + toGoal.getYaw().valueDegrees(); + // this is how much the character CAN turn this frame + Ogre::Real yawAtSpeed = + yawToGoal / Ogre::Math::Abs(yawToGoal) * + delta * TURN_SPEED; + // reduce "turnability" if we're in midair + // if (mBaseAnimID == ANIM_JUMP_LOOP) yawAtSpeed *= 0.2f; + if (yawToGoal < 0) + yawToGoal = std::min( + 0, + std::max( + yawToGoal, + yawAtSpeed)); //yawToGoal = Math::Clamp(yawToGoal, yawAtSpeed, 0); + else if (yawToGoal > 0) + yawToGoal = std::max( + 0, + std::min( + yawToGoal, + yawAtSpeed)); //yawToGoal = Math::Clamp(yawToGoal, 0, yawAtSpeed); + ch.mBodyNode->yaw(Ogre::Degree(yawToGoal)); + } + }); + class ClosestNotMeRayResultCallback + : public btCollisionWorld::ClosestRayResultCallback { + btCollisionObject *mMe; + + public: + ClosestNotMeRayResultCallback(btCollisionObject *me, + const btVector3 &from, + const btVector3 &to) + : btCollisionWorld::ClosestRayResultCallback(from, to) + { + mMe = me; + } + virtual btScalar + addSingleResult(btCollisionWorld::LocalRayResult &rayResult, + bool normalInWorldSpace) + { + if (rayResult.m_collisionObject == mMe) + return 1.0f; + return ClosestRayResultCallback::addSingleResult( + rayResult, normalInWorldSpace); + } + }; + ecs.system("CheckGround") + .kind(flecs::OnUpdate) + .with() + .each([](const EngineData &eng, CharacterBody &body) { + if (body.checkGround) { + btVector3 from = + body.mGhostObject->getWorldTransform() + .getOrigin() + + btVector3(0, 0.2f, 0); + btVector3 to = from + btVector3(0, -3000.0f, 0); + ClosestNotMeRayResultCallback resultCallback( + body.mGhostObject, from, to); + body.mGhostObject->rayTest(from, to, + resultCallback); + body.checkGroundResult = + resultCallback.hasHit(); + body.checkGround = false; + } + }); +} + +void CharacterModule::setAnimation(AnimationControl &anim) +{ + OgreAssert(anim.nextAnim >= 0 && + anim.nextAnim < AnimationControl::NUM_ANIMS, + "Bad animation"); + if (anim.currentAnim != AnimationControl::ANIM_NONE) { + anim.mFadingIn[anim.currentAnim] = false; + anim.mFadingOut[anim.currentAnim] = true; + } + if (anim.nextAnim != AnimationControl::ANIM_NONE) { + anim.mAnims[anim.nextAnim]->setEnabled(true); + anim.mAnims[anim.nextAnim]->setWeight(0); + anim.mFadingOut[anim.nextAnim] = false; + anim.mFadingIn[anim.nextAnim] = true; + if (anim.reset) + anim.mAnims[anim.nextAnim]->setTimePosition(0); + } + anim.currentAnim = anim.nextAnim; + anim.reset = false; +} +#define ANIM_FADE_SPEED \ + 7.5f // animation crossfade speed in % of full weight per second + +void CharacterModule::fadeAnimations(AnimationControl &anim, Ogre::Real delta) +{ + int i; + for (i = 0; i < AnimationControl::NUM_ANIMS; i++) { + if (anim.mFadingIn[i]) { + // slowly fade this animation in until it has full weight + Ogre::Real newWeight = anim.mAnims[i]->getWeight() + + delta * ANIM_FADE_SPEED; + anim.mAnims[i]->setWeight( + Ogre::Math::Clamp(newWeight, 0, 1)); + if (newWeight >= 1) + anim.mFadingIn[i] = false; + } else if (anim.mFadingOut[i]) { + // slowly fade this animation out until it has no weight, and then disable it + Ogre::Real newWeight = anim.mAnims[i]->getWeight() - + delta * ANIM_FADE_SPEED; + anim.mAnims[i]->setWeight( + Ogre::Math::Clamp(newWeight, 0, 1)); + if (newWeight <= 0) { + anim.mAnims[i]->setEnabled(false); + anim.mFadingOut[i] = false; + } + } + } +} +void CharacterModule::updateCameraGoal(Camera &camera, Ogre::Real deltaYaw, + Ogre::Real deltaPitch, + Ogre::Real deltaZoom) +{ + camera.mCameraPivot->yaw(Ogre::Degree(deltaYaw), Ogre::Node::TS_PARENT); + if (!(camera.mPivotPitch + deltaPitch > 25 && deltaPitch > 0) && + !(camera.mPivotPitch + deltaPitch < -60 && deltaPitch < 0)) { + camera.mCameraPivot->pitch(Ogre::Degree(deltaPitch), + Ogre::Node::TS_LOCAL); + camera.mPivotPitch += deltaPitch; + } + Ogre::Real dist = camera.mCameraGoal->_getDerivedPosition().distance( + camera.mCameraPivot->_getDerivedPosition()); + Ogre::Real distChange = deltaZoom * dist; + + // bound the zoom + if (!(dist + distChange < 8 && distChange < 0) && + !(dist + distChange > 25 && distChange > 0)) + camera.mCameraGoal->translate(0, 0, distChange, + Ogre::Node::TS_LOCAL); +} +} \ No newline at end of file diff --git a/src/gamedata/CharacterModule.h b/src/gamedata/CharacterModule.h new file mode 100644 index 0000000..484f773 --- /dev/null +++ b/src/gamedata/CharacterModule.h @@ -0,0 +1,55 @@ +#ifndef CHARACTER_MODULE_H_ +#define CHARACTER_MODULE_H_ +#include +namespace ECS +{ +struct Camera; +/* character */ +struct Character {}; /* tag */ +struct Player {}; /* tag */ +struct CharacterBase { + Ogre::String type; + Ogre::SceneNode *mBodyNode; + Ogre::Entity *mBodyEnt; + Ogre::Skeleton *mSkeleton; + Ogre::Node *mRootBone; + Ogre::Vector3 mBoneMotion; + Ogre::Vector3 mGoalDirection; // actual intended direction in world-space +}; +struct CharacterBody { + btPairCachingGhostObject *mGhostObject; + btCompoundShape *mCollisionShape; + Ogre::Bullet::KinematicMotionSimple *mController; + Ogre::Vector3 gvelocity; + bool checkGround; + bool checkGroundResult; +}; +struct AnimationControl { + enum AnimID { + ANIM_IDLE = 0, + ANIM_WALK, + ANIM_RUN, + NUM_ANIMS, + ANIM_NONE = NUM_ANIMS + }; + AnimID currentAnim; + AnimID nextAnim; + bool reset; + bool configured; + Ogre::AnimationState *mAnims[NUM_ANIMS]; // master animation list + Ogre::Animation *mSkelAnimations[NUM_ANIMS]; + bool mFadingIn[NUM_ANIMS]; // which animations are fading in + bool mFadingOut[NUM_ANIMS]; // which animations are fading out + Ogre::NodeAnimationTrack *mHipsTracks[NUM_ANIMS]; + Ogre::NodeAnimationTrack *mRootTracks[NUM_ANIMS]; +}; +struct CharacterModule { + flecs::entity player; + CharacterModule(flecs::world &ecs); + void setAnimation(AnimationControl &anim); + void fadeAnimations(AnimationControl &anim, Ogre::Real deltaTime); + void updateCameraGoal(Camera &camera, Ogre::Real deltaYaw, + Ogre::Real deltaPitch, Ogre::Real deltaZoom); +}; +} +#endif \ No newline at end of file diff --git a/src/gamedata/Components.h b/src/gamedata/Components.h new file mode 100644 index 0000000..79e4970 --- /dev/null +++ b/src/gamedata/Components.h @@ -0,0 +1,50 @@ +#ifndef COMPONENTS_H_ +#define COMPONENTS_H_ +#include +#include +namespace ECS +{ +struct GameData { + int tmp; +}; +struct EngineData { + Ogre::SceneManager *mScnMgr; + Ogre::Bullet::DynamicsWorld *mWorld; + float delta; +}; +struct Vector3 { + float x; + float y; + float z; + bool zeroLength() const; +}; +struct Vector2 { + float x, y; +}; +struct Input { + uint32_t control; + uint32_t control_prev; + Vector3 motion; + Vector2 mouse; + float wheel_y; + bool mouse_moved; + bool wheel_moved; + bool fast; + Input() + : control(0) + , control_prev(0) + , motion({ 0, 0, 0 }) + , fast(false) + { + } +}; +struct Camera { + Ogre::SceneNode *mCameraNode; + Ogre::Camera *mCamera; + bool configured; + Ogre::SceneNode *mCameraPivot; + Ogre::SceneNode *mCameraGoal; + Ogre::Real mPivotPitch; +}; +} +#endif \ No newline at end of file diff --git a/src/gamedata/GameData.cpp b/src/gamedata/GameData.cpp index 940ef96..96e84d3 100644 --- a/src/gamedata/GameData.cpp +++ b/src/gamedata/GameData.cpp @@ -1,537 +1,13 @@ #include #include #include "GameData.h" +#include "Components.h" +#include "CharacterModule.h" +#include "WaterModule.h" namespace ECS { static flecs::world ecs; -CharacterModule::CharacterModule(flecs::world &ecs) -{ - ecs.module(); - player = ecs.entity("player"); - player.set({ AnimationControl::ANIM_NONE, - AnimationControl::ANIM_NONE, false, - false }); - player.set( - { "normal-male.glb", nullptr, nullptr, nullptr }); - player.set( - { nullptr, nullptr, nullptr, { 0, 0, 0 }, false, false }); - player.add(); - player.add(); - ecs.system("HandleInput") - .kind(flecs::OnUpdate) - .each([this](Input &input, Camera &camera) { - /* handle input */ - // if (input.control == input.control_prev) - // return; - uint32_t pressed = input.control & ~input.control_prev; - uint32_t released = input.control_prev & ~input.control; - uint32_t active = input.control; - float zaxis = input.motion.z; - zaxis *= 0.9f; - if (pressed & 1) - std::cout << "W pressed\n"; - if (released & 1) - std::cout << "W released\n"; - if (active & 1) - zaxis -= 1.0f; - if (active & 4) - zaxis += 1.0f; - if (zaxis > -1.0f && zaxis < 1.0f) - zaxis = 0.0f; - else - zaxis = Ogre::Math::Sign(zaxis); - input.motion.z = zaxis; - float xaxis = input.motion.x; - xaxis *= 0.9f; - if (active & 2) - xaxis = -1.0f; - if (active & 8) - xaxis += 1.0f; - if (xaxis > -1.0f && xaxis < 1.0f) - xaxis = 0.0f; - else - xaxis = Ogre::Math::Sign(xaxis); - input.motion.x = xaxis; - if (active & 16) - input.fast = true; - else - input.fast = false; - input.control_prev = input.control; - if (input.mouse_moved) { - updateCameraGoal(camera, -0.18f * input.mouse.x, - -0.12f * input.mouse.y, 0); - input.mouse_moved = false; - input.mouse.x = 0; - input.mouse.y = 0; - } - if (input.wheel_moved) { - updateCameraGoal(camera, 0, 0, - -0.15f * input.wheel_y); - input.wheel_moved = false; - input.wheel_y = 0; - } - ECS::get().modified(); - }); - ecs.system("HandleAnimations") - .kind(flecs::OnUpdate) - .each([this](flecs::entity e, const CharacterBase &ch, - AnimationControl &anim) { - if (!anim.configured && ch.mSkeleton) { - int i, j; - ch.mSkeleton->setBlendMode( - Ogre::ANIMBLEND_CUMULATIVE); - Ogre::String - animNames[AnimationControl::NUM_ANIMS] = { - "idle", "walking", "running" - }; - for (i = 0; i < AnimationControl::NUM_ANIMS; - i++) { - anim.mAnims[i] = - ch.mBodyEnt->getAnimationState( - animNames[i]); - anim.mAnims[i]->setLoop(true); - anim.mAnims[i]->setEnabled(true); - anim.mAnims[i]->setWeight(0); - anim.mFadingIn[i] = false; - anim.mFadingOut[i] = false; - anim.mSkelAnimations[i] = - ch.mSkeleton->getAnimation( - animNames[i]); - for (const auto &it : - anim.mSkelAnimations[i] - ->_getNodeTrackList()) { - Ogre::NodeAnimationTrack *track = - it.second; - Ogre::String trackName = - track->getAssociatedNode() - ->getName(); - if (trackName == - "mixamorig:Hips") { - anim.mHipsTracks[i] = - track; - } else if (trackName == - "Root") { - anim.mRootTracks[i] = - track; - // mRootTracks[i]->removeAllKeyFrames(); - } - } - Ogre::Vector3 delta = - Ogre::Vector3::ZERO; - Ogre::Vector3 motion = - Ogre::Vector3::ZERO; - for (j = 0; - j < anim.mRootTracks[i] - ->getNumKeyFrames(); - j++) { - Ogre::Vector3 trans = - anim.mRootTracks[i] - ->getNodeKeyFrame( - j) - ->getTranslate(); - if (j == 0) - delta = trans; - else - delta = trans - motion; - anim.mRootTracks[i] - ->getNodeKeyFrame(j) - ->setTranslate(delta); - motion = trans; - } - } - anim.nextAnim = AnimationControl::ANIM_IDLE; - setAnimation(anim); - anim.configured = true; - } - }); - ecs.system("HandleAnimations0") - .kind(flecs::OnUpdate) - .each([this](flecs::entity e, AnimationControl &anim) { - if (anim.currentAnim != anim.nextAnim) - setAnimation(anim); - }); - ecs.system("HandleAnimations1") - .kind(flecs::OnUpdate) - .each([this](flecs::entity e, CharacterBase &ch, - AnimationControl &anim) { - float delta = e.world().delta_time(); - Ogre::Real animSpeed = 1; - if (anim.currentAnim != AnimationControl::ANIM_NONE) { - if (anim.currentAnim == - AnimationControl::ANIM_WALK) - anim.mAnims[anim.currentAnim]->addTime( - delta * 1.0f); - else - anim.mAnims[anim.currentAnim]->addTime( - delta * animSpeed); - } - if (!ch.mRootBone) - return; - ch.mBoneMotion = ch.mRootBone->getPosition(); - }); - ecs.system("HandleAnimations2") - .kind(flecs::OnUpdate) - .each([this](flecs::entity e, AnimationControl &anim) { - float delta = e.world().delta_time(); - fadeAnimations(anim, delta); - }); - ecs.system( - "HandleRootMotion") - .kind(flecs::OnUpdate) - .each([this](flecs::entity e, CharacterBase &ch, - CharacterBody &body, AnimationControl &anim) { - float delta = e.world().delta_time(); - if (!ch.mBodyNode) - return; - Ogre::Quaternion rot = ch.mBodyNode->getOrientation(); - Ogre::Vector3 pos = ch.mBodyNode->getPosition(); - Ogre::Vector3 boneMotion = ch.mBoneMotion; - Ogre::Vector3 velocity = rot * boneMotion / delta; - OgreAssert(delta > 0.0f, "Zero delta"); - int maxPen = 0; - Ogre::Vector3 colNormal; - bool is_on_floor = false; - bool penetration = false; - Ogre::Vector3 gravity(0, -9.8, 0); - body.gvelocity += gravity * delta; - velocity += body.gvelocity; - Ogre::Vector3 rotMotion = velocity * delta; - btVector3 currentPosition = - body.mGhostObject->getWorldTransform() - .getOrigin(); - is_on_floor = body.mController->isOnFloor(); - penetration = body.mController->isPenetrating(); - if (is_on_floor) - body.gvelocity = - Ogre::Vector3(0.0f, 0.0f, 0.0f); - - btTransform from( - Ogre::Bullet::convert( - ch.mBodyNode->getOrientation()), - Ogre::Bullet::convert( - ch.mBodyNode->getPosition())); - ch.mBodyNode->setPosition(ch.mBodyNode->getPosition() + - rotMotion); - ch.mBoneMotion = Ogre::Vector3(0, 0, 0); - }); - ecs.system("HandlePlayerAnimations") - .kind(flecs::OnUpdate) - .with() - .with() - .each([](const Input &input, AnimationControl &anim) { - if (!anim.configured) - return; - bool controls_idle = input.motion.zeroLength(); - bool anim_is_idle = anim.currentAnim == - AnimationControl::ANIM_IDLE; - bool anim_is_walking = anim.currentAnim == - AnimationControl::ANIM_WALK; - bool anim_is_running = anim.currentAnim == - AnimationControl::ANIM_RUN; - bool anim_is_motion = anim_is_walking || - anim_is_running; - if (!controls_idle && anim_is_idle) { - anim.reset = true; - if (input.fast) - anim.nextAnim = - AnimationControl::ANIM_RUN; - else - anim.nextAnim = - AnimationControl::ANIM_WALK; - } else - anim.reset = false; - if (controls_idle && anim_is_motion) - anim.nextAnim = AnimationControl::ANIM_IDLE; - else if (!controls_idle && anim_is_motion) { - if (input.fast && anim_is_walking) - anim.nextAnim = - AnimationControl::ANIM_RUN; - else if (!input.fast && anim_is_running) - anim.nextAnim = - AnimationControl::ANIM_WALK; - } - }); - ecs.system( - "UpdateCharacterBase") - .kind(flecs::OnUpdate) - .with() - .each([](const EngineData &eng, CharacterBase &ch, - CharacterBody &body) { - if (!ch.mBodyNode) { - ch.mBodyEnt = eng.mScnMgr->createEntity( - "normal-male.glb"); - ch.mBodyNode = eng.mScnMgr->getRootSceneNode() - ->createChildSceneNode(); - ch.mBodyNode->attachObject(ch.mBodyEnt); - ch.mSkeleton = ch.mBodyEnt->getSkeleton(); - body.mGhostObject = - new btPairCachingGhostObject(); - body.mCollisionShape = new btCompoundShape; - body.mGhostObject->setCollisionShape( - body.mCollisionShape); - { - btVector3 inertia(0, 0, 0); - // mCollisionShape = new btCompoundShape(); - btScalar height = 1.0f; - btScalar radius = 0.3f; - btCapsuleShape *shape = - new btCapsuleShape( - radius, - 2 * height - - 2 * radius); - btTransform transform; - transform.setIdentity(); - transform.setOrigin(btVector3(0, 1, 0)); - static_cast( - body.mCollisionShape) - ->addChildShape(transform, - shape); - btScalar masses[1] = { 0 }; - btTransform principal; - static_cast( - body.mCollisionShape) - ->calculatePrincipalAxisTransform( - masses, principal, - inertia); - } - body.mGhostObject->setCollisionFlags( - btCollisionObject::CF_KINEMATIC_OBJECT | - btCollisionObject:: - CF_NO_CONTACT_RESPONSE); - body.mGhostObject->setActivationState( - DISABLE_DEACTIVATION); - eng.mWorld->attachCollisionObject( - body.mGhostObject, ch.mBodyEnt, 1, - 0x7FFFFFF); - body.mController = - new Ogre::Bullet::KinematicMotionSimple( - body.mGhostObject, - ch.mBodyNode); - OgreAssert(body.mGhostObject, - "Need GhostObject"); - OgreAssert(body.mController, "Need controller"); - eng.mWorld->getBtWorld()->addAction( - body.mController); - - OgreAssert(body.mCollisionShape, - "No collision shape"); - OgreAssert(ch.mSkeleton->hasBone("Root"), - "No root bone"); - ch.mRootBone = ch.mSkeleton->getBone("Root"); - OgreAssert(ch.mRootBone, "No root bone"); - } - }); -#define CAM_HEIGHT 1.6f // height of camera above character's center of mass - ecs.system( - "UpdateCamera") - .kind(flecs::OnUpdate) - .each([](flecs::entity e, const EngineData &eng, Camera &camera, - const CharacterBase &ch) { - float delta = e.world().delta_time(); - if (!camera.configured) { - // create a pivot at roughly the character's shoulder - camera.mCameraPivot = - eng.mScnMgr->getRootSceneNode() - ->createChildSceneNode(); - camera.mCameraGoal = - camera.mCameraPivot - ->createChildSceneNode( - Ogre::Vector3(0, 2, 3)); - camera.mCameraNode->setPosition( - camera.mCameraPivot->getPosition() + - camera.mCameraGoal->getPosition()); - camera.mCameraPivot->setFixedYawAxis(true); - camera.mCameraGoal->setFixedYawAxis(true); - camera.mCameraNode->setFixedYawAxis(true); - // our model is quite small, so reduce the clipping planes - camera.mCamera->setNearClipDistance(0.1f); - camera.mCamera->setFarClipDistance(700); - - camera.mPivotPitch = 0; - camera.configured = true; - } else { - // place the camera pivot roughly at the character's shoulder - camera.mCameraPivot->setPosition( - ch.mBodyNode->getPosition() + - Ogre::Vector3::UNIT_Y * CAM_HEIGHT); - // move the camera smoothly to the goal - Ogre::Vector3 goalOffset = - camera.mCameraGoal - ->_getDerivedPosition() - - camera.mCameraNode->getPosition(); - camera.mCameraNode->translate(goalOffset * - delta * 9.0f); - // always look at the pivot - camera.mCameraNode->lookAt( - camera.mCameraPivot - ->_getDerivedPosition(), - Ogre::Node::TS_PARENT); - } - }); -#define TURN_SPEED 500.0f // character turning in degrees per second - ecs.system("UpdateBody") - .kind(flecs::OnUpdate) - .with() - .each([](flecs::entity e, const Input &input, - const Camera &camera, CharacterBase &ch) { - 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 - ch.mGoalDirection += - input.motion.z * - camera.mCameraNode->getOrientation() - .zAxis(); - ch.mGoalDirection += - input.motion.x * - camera.mCameraNode->getOrientation() - .xAxis(); - ch.mGoalDirection.y = 0; - ch.mGoalDirection.normalise(); - - Ogre::Quaternion toGoal = - ch.mBodyNode->getOrientation() - .zAxis() - .getRotationTo( - ch.mGoalDirection); - // calculate how much the character has to turn to face goal direction - Ogre::Real yawToGoal = - toGoal.getYaw().valueDegrees(); - // this is how much the character CAN turn this frame - Ogre::Real yawAtSpeed = - yawToGoal / Ogre::Math::Abs(yawToGoal) * - delta * TURN_SPEED; - // reduce "turnability" if we're in midair - // if (mBaseAnimID == ANIM_JUMP_LOOP) yawAtSpeed *= 0.2f; - if (yawToGoal < 0) - yawToGoal = std::min( - 0, - std::max( - yawToGoal, - yawAtSpeed)); //yawToGoal = Math::Clamp(yawToGoal, yawAtSpeed, 0); - else if (yawToGoal > 0) - yawToGoal = std::max( - 0, - std::min( - yawToGoal, - yawAtSpeed)); //yawToGoal = Math::Clamp(yawToGoal, 0, yawAtSpeed); - ch.mBodyNode->yaw(Ogre::Degree(yawToGoal)); - } - }); - class ClosestNotMeRayResultCallback - : public btCollisionWorld::ClosestRayResultCallback { - btCollisionObject *mMe; - - public: - ClosestNotMeRayResultCallback(btCollisionObject *me, - const btVector3 &from, - const btVector3 &to) - : btCollisionWorld::ClosestRayResultCallback(from, to) - { - mMe = me; - } - virtual btScalar - addSingleResult(btCollisionWorld::LocalRayResult &rayResult, - bool normalInWorldSpace) - { - if (rayResult.m_collisionObject == mMe) - return 1.0f; - return ClosestRayResultCallback::addSingleResult( - rayResult, normalInWorldSpace); - } - }; - ecs.system("CheckGround") - .kind(flecs::OnUpdate) - .with() - .each([](const EngineData &eng, CharacterBody &body) { - if (body.checkGround) { - btVector3 from = - body.mGhostObject->getWorldTransform() - .getOrigin() + - btVector3(0, 0.2f, 0); - btVector3 to = from + btVector3(0, -3000.0f, 0); - ClosestNotMeRayResultCallback resultCallback( - body.mGhostObject, from, to); - body.mGhostObject->rayTest(from, to, - resultCallback); - body.checkGroundResult = - resultCallback.hasHit(); - body.checkGround = false; - } - }); -} - -void CharacterModule::setAnimation(AnimationControl &anim) -{ - OgreAssert(anim.nextAnim >= 0 && - anim.nextAnim < AnimationControl::NUM_ANIMS, - "Bad animation"); - if (anim.currentAnim != AnimationControl::ANIM_NONE) { - anim.mFadingIn[anim.currentAnim] = false; - anim.mFadingOut[anim.currentAnim] = true; - } - if (anim.nextAnim != AnimationControl::ANIM_NONE) { - anim.mAnims[anim.nextAnim]->setEnabled(true); - anim.mAnims[anim.nextAnim]->setWeight(0); - anim.mFadingOut[anim.nextAnim] = false; - anim.mFadingIn[anim.nextAnim] = true; - if (anim.reset) - anim.mAnims[anim.nextAnim]->setTimePosition(0); - } - anim.currentAnim = anim.nextAnim; - anim.reset = false; -} -#define ANIM_FADE_SPEED \ - 7.5f // animation crossfade speed in % of full weight per second - -void CharacterModule::fadeAnimations(AnimationControl &anim, Ogre::Real delta) -{ - int i; - for (i = 0; i < AnimationControl::NUM_ANIMS; i++) { - if (anim.mFadingIn[i]) { - // slowly fade this animation in until it has full weight - Ogre::Real newWeight = anim.mAnims[i]->getWeight() + - delta * ANIM_FADE_SPEED; - anim.mAnims[i]->setWeight( - Ogre::Math::Clamp(newWeight, 0, 1)); - if (newWeight >= 1) - anim.mFadingIn[i] = false; - } else if (anim.mFadingOut[i]) { - // slowly fade this animation out until it has no weight, and then disable it - Ogre::Real newWeight = anim.mAnims[i]->getWeight() - - delta * ANIM_FADE_SPEED; - anim.mAnims[i]->setWeight( - Ogre::Math::Clamp(newWeight, 0, 1)); - if (newWeight <= 0) { - anim.mAnims[i]->setEnabled(false); - anim.mFadingOut[i] = false; - } - } - } -} -void CharacterModule::updateCameraGoal(Camera &camera, Ogre::Real deltaYaw, - Ogre::Real deltaPitch, - Ogre::Real deltaZoom) -{ - camera.mCameraPivot->yaw(Ogre::Degree(deltaYaw), Ogre::Node::TS_PARENT); - if (!(camera.mPivotPitch + deltaPitch > 25 && deltaPitch > 0) && - !(camera.mPivotPitch + deltaPitch < -60 && deltaPitch < 0)) { - camera.mCameraPivot->pitch(Ogre::Degree(deltaPitch), - Ogre::Node::TS_LOCAL); - camera.mPivotPitch += deltaPitch; - } - Ogre::Real dist = camera.mCameraGoal->_getDerivedPosition().distance( - camera.mCameraPivot->_getDerivedPosition()); - Ogre::Real distChange = deltaZoom * dist; - - // bound the zoom - if (!(dist + distChange < 8 && distChange < 0) && - !(dist + distChange > 25 && distChange > 0)) - camera.mCameraGoal->translate(0, 0, distChange, - Ogre::Node::TS_LOCAL); -} - void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world, Ogre::SceneNode *cameraNode, Ogre::Camera *camera) { @@ -539,11 +15,17 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world, ecs.component().add(flecs::Singleton); ecs.component().add(flecs::Singleton); ecs.component().add(flecs::Singleton); - ecs.set({ scnMgr, world }); + ecs.set({ scnMgr, world, 0.0f }); ecs.set({ cameraNode, camera, false }); ecs.add(); ecs.add(); + ecs.system("UpdateDelta") + .kind(flecs::OnUpdate) + .each([](EngineData &eng) { + eng.delta = ECS::get().delta_time(); + }); ecs.import (); + ecs.import (); } void update(float delta) { diff --git a/src/gamedata/GameData.h b/src/gamedata/GameData.h index e4ff24a..dc79c13 100644 --- a/src/gamedata/GameData.h +++ b/src/gamedata/GameData.h @@ -4,94 +4,6 @@ #include namespace ECS { -struct GameData { - int tmp; -}; -struct EngineData { - Ogre::SceneManager *mScnMgr; - Ogre::Bullet::DynamicsWorld *mWorld; -}; -struct Vector3 { - float x; - float y; - float z; - bool zeroLength() const; -}; -struct Vector2 { - float x, y; -}; -struct Input { - uint32_t control; - uint32_t control_prev; - Vector3 motion; - Vector2 mouse; - float wheel_y; - bool mouse_moved; - bool wheel_moved; - bool fast; - Input() - : control(0) - , control_prev(0) - , motion({ 0, 0, 0 }) - , fast(false) - { - } -}; -/* character */ -struct Character {}; /* tag */ -struct Player {}; /* tag */ -struct CharacterBase { - Ogre::String type; - Ogre::SceneNode *mBodyNode; - Ogre::Entity *mBodyEnt; - Ogre::Skeleton *mSkeleton; - Ogre::Node *mRootBone; - Ogre::Vector3 mBoneMotion; - Ogre::Vector3 mGoalDirection; // actual intended direction in world-space -}; -struct CharacterBody { - btPairCachingGhostObject *mGhostObject; - btCompoundShape *mCollisionShape; - Ogre::Bullet::KinematicMotionSimple *mController; - Ogre::Vector3 gvelocity; - bool checkGround; - bool checkGroundResult; -}; -struct Camera { - Ogre::SceneNode *mCameraNode; - Ogre::Camera *mCamera; - bool configured; - Ogre::SceneNode *mCameraPivot; - Ogre::SceneNode *mCameraGoal; - Ogre::Real mPivotPitch; -}; -struct AnimationControl { - enum AnimID { - ANIM_IDLE = 0, - ANIM_WALK, - ANIM_RUN, - NUM_ANIMS, - ANIM_NONE = NUM_ANIMS - }; - AnimID currentAnim; - AnimID nextAnim; - bool reset; - bool configured; - Ogre::AnimationState *mAnims[NUM_ANIMS]; // master animation list - Ogre::Animation *mSkelAnimations[NUM_ANIMS]; - bool mFadingIn[NUM_ANIMS]; // which animations are fading in - bool mFadingOut[NUM_ANIMS]; // which animations are fading out - Ogre::NodeAnimationTrack *mHipsTracks[NUM_ANIMS]; - Ogre::NodeAnimationTrack *mRootTracks[NUM_ANIMS]; -}; -struct CharacterModule { - flecs::entity player; - CharacterModule(flecs::world &ecs); - void setAnimation(AnimationControl &anim); - void fadeAnimations(AnimationControl &anim, Ogre::Real deltaTime); - void updateCameraGoal(Camera &camera, Ogre::Real deltaYaw, - Ogre::Real deltaPitch, Ogre::Real deltaZoom); -}; void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world, Ogre::SceneNode *cameraNode, Ogre::Camera *camera); void update(float delta); diff --git a/src/gamedata/WaterModule.cpp b/src/gamedata/WaterModule.cpp new file mode 100644 index 0000000..39b0c9e --- /dev/null +++ b/src/gamedata/WaterModule.cpp @@ -0,0 +1,87 @@ +#include +#include "GameData.h" +#include "Components.h" +#include "WaterModule.h" +namespace ECS +{ +static const uint32_t WATER_MASK = 0xF00; +WaterModule::WaterModule(flecs::world &ecs) +{ + ecs.module(); + ecs.component().add(flecs::Singleton); + ecs.component().add(flecs::Singleton); + ecs.set({ nullptr, nullptr }); + ecs.set({ nullptr }); + ecs.system("UpdateWater") + .kind(flecs::OnUpdate) + .each([](const EngineData &eng, const Camera &camera, + WaterSurface &water) { + float delta = eng.delta; + if (!water.mWaterEnt || !water.mWaterNode) { + water.mWaterNode = + eng.mScnMgr->getRootSceneNode() + ->createChildSceneNode("Water"); + + /* + auto mat = Ogre::MaterialManager::getSingleton() + .getByName("Water/Above"); + mat->load(); + mat->setReceiveShadows(false); + auto mat2 = + Ogre::MaterialManager::getSingleton() + .getByName("Water/Below"); + mat2->load(); + mat2->setReceiveShadows(false); + */ + Ogre::Vector3 cameraPosition = + camera.mCameraNode->getPosition(); + water.mWaterEnt = eng.mScnMgr->createEntity( + "Ocean", "sea.glb"); + water.mWaterEnt->setVisibilityFlags(WATER_MASK); + water.mWaterEnt->setCastShadows(true); + // water.mWaterEnt->setMaterialName("Water/Above"); + // water.mWaterEnt->setMaterial(mat); + water.mWaterNode->attachObject(water.mWaterEnt); + // mDynWorld->attachCollisionObject( + // mWaterBody, water_ent, 1, 0x7FFFFFFF); + } + 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); + }); + ecs.system( + "UpdateWaterBody") + .kind(flecs::OnUpdate) + .each([](const EngineData &eng, const WaterSurface &water, + WaterBody &body) { + if (!body.mWaterBody) { + body.mWaterBody = new btGhostObject; + btBoxShape *boxShape = new btBoxShape( + btVector3(1000, 1000, 1000)); + btCompoundShape *shape = new btCompoundShape; + shape->addChildShape( + btTransform(btQuaternion(), + btVector3(0, -1000, 0)), + boxShape); + body.mWaterBody->setCollisionShape(shape); + body.mWaterBody->setCollisionFlags( + body.mWaterBody->getCollisionFlags() | + btCollisionObject::CF_NO_CONTACT_RESPONSE | + btCollisionObject::CF_STATIC_OBJECT); + eng.mWorld->attachCollisionObject( + body.mWaterBody, water.mWaterEnt, 16, + 0x7fffffff & ~2); + } + }); +} +} \ No newline at end of file diff --git a/src/gamedata/WaterModule.h b/src/gamedata/WaterModule.h new file mode 100644 index 0000000..2c2b631 --- /dev/null +++ b/src/gamedata/WaterModule.h @@ -0,0 +1,17 @@ +#ifndef WATER_MODULE_H +#define WATER_MODULE_H +#include +namespace ECS +{ +struct WaterSurface { + Ogre::SceneNode *mWaterNode; + Ogre::Entity *mWaterEnt; +}; +struct WaterBody { + btGhostObject *mWaterBody; +}; +struct WaterModule { + WaterModule(flecs::world &ecs); +}; +} +#endif \ No newline at end of file diff --git a/src/terrain/terrain.cpp b/src/terrain/terrain.cpp index 633b2a2..3666989 100644 --- a/src/terrain/terrain.cpp +++ b/src/terrain/terrain.cpp @@ -260,7 +260,7 @@ public: btRigidBody *body = mWorld->addTerrainRigidBody( group, x, y, 2, - 0x7ffffffd); + 0x7ffffffd & (~16)); Ogre::LogManager::getSingleton().logError( "created rigid body " + Ogre::StringConverter::toString(