diff --git a/.vscode/settings.json b/.vscode/settings.json index 276f234..b6a972b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,6 +8,7 @@ "files.associations": { "istream": "cpp", "variant": "cpp", - "tuple": "cpp" + "tuple": "cpp", + "iostream": "cpp" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index fcb7b9d..29cd3aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,19 +78,21 @@ add_executable(0_Bootstrap Bootstrap.cpp) # this also sets the includes and pulls third party dependencies target_link_libraries(0_Bootstrap OgreBites OgreBullet OgrePaging ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY} ${ASSIMP_LIBRARIES} - -static-libgcc - -static-libstdc++ -Wl,--as-needed ) +if(OGRE_STATIC) +target_link_options(0_Bootstrap PRIVATE -static-libstdc++ -static-libgcc) +endif() 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 - -static-libgcc - -static-libstdc++ -Wl,--as-needed ) +if(OGRE_STATIC) +target_link_options(Editor PRIVATE -static-libstdc++ -static-libgcc) +endif() add_dependencies(Editor stage_files import_buildings import_water_stuff import_vehicles import_vrm) add_executable(Game Game.cpp ${TERRAIN_SRC} ${WATER_SRC}) target_include_directories(Game PRIVATE src/gamedata) @@ -112,10 +114,11 @@ add_executable(Procedural Procedural.cpp ${TERRAIN_SRC}) target_link_libraries(Procedural OgreBites OgreBullet OgrePaging OgreTerrain OgreProcedural::OgreProcedural ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY} - -static-libgcc - -static-libstdc++ -Wl,--as-needed ) +if(OGRE_STATIC) +target_link_options(Procedural PRIVATE -static-libstdc++ -static-libgcc) +endif() add_dependencies(Procedural stage_files import_buildings) file(GLOB BUILDINGS_SRC ${CMAKE_SOURCE_DIR}/assets/blender/buildings/*.blend) set(BUILDING_OUTPUT_FILES) @@ -167,13 +170,12 @@ add_custom_target(import_water_stuff ALL DEPENDS ${WATER_STUFF}) add_executable(TerrainTest terrain.cpp ${TERRAIN_SRC}) target_link_libraries(TerrainTest OgreBites OgreBullet OgrePaging OgreTerrain lua ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY} - -static-libgcc - -static-libstdc++ -Wl,--as-needed ) target_include_directories(TerrainTest PRIVATE . src/terrain src/lua src/lua/lua-5.4.8/src) if(OGRE_STATIC) target_link_libraries(TerrainTest fix::assimp pugixml) + target_link_options(TerrainTest PRIVATE -static-libstdc++ -static-libgcc) target_link_libraries(Procedural fix::assimp pugixml) target_link_libraries(0_Bootstrap fix::assimp pugixml) target_link_libraries(Editor fix::assimp pugixml) diff --git a/Game.cpp b/Game.cpp index 5c587c4..8d3fd02 100644 --- a/Game.cpp +++ b/Game.cpp @@ -386,9 +386,6 @@ public: // getRenderWindow()); mDbgDraw.reset(new Ogre::Bullet::DebugDrawer( mScnMgr->getRootSceneNode(), mDynWorld->getBtWorld())); - ECS::setup(); - Sound::setup(); - Sound::ding(); } void locateResources() override { @@ -535,7 +532,8 @@ public: Ogre::Timer mTerrainUpd; void updateTerrain(float delta) { - Ogre::Vector3 pos = mCharacterController->getPosition(); + // Ogre::Vector3 pos = mCharacterController->getPosition(); + Ogre::Vector3 pos(0, 0, 0); if (!mTerrainReady && m_terrain.isLoadedAt(pos) && mCharacterController->checkGround()) { std::cout << "terrain ready\n"; @@ -595,6 +593,9 @@ public: "Skybox/Dynamic", "General"); OgreAssert(m, "Sky box material not found."); m->load(); + ECS::setup(mScnMgr, mDynWorld.get(), mCameraNode, mCamera); + Sound::setup(); + Sound::ding(); } void create_entity_node(const Ogre::String &name, int key) { diff --git a/src/characters/controller.cpp b/src/characters/controller.cpp index c6cd1a1..828b8de 100644 --- a/src/characters/controller.cpp +++ b/src/characters/controller.cpp @@ -3,169 +3,6 @@ #include "GameData.h" #include "character.h" #include "controller.h" -#if 0 -#define CAM_HEIGHT 1.6f // height of camera above character's center of mass -CharacterController::CharacterController(Ogre::SceneNode *camNode, - Ogre::Camera *cam, - Ogre::SceneManager *scnMgr, - Character *character) - : mCameraNode(camNode) - , mCamera(cam) - , mScnMgr(scnMgr) - , mPivotPitch(0) - , mVerticalVelocity(0) - , mRunning(false) - , mCharacter(character) - , mCameraPivot(nullptr) -{ - setupCamera(); -} -CharacterController::~CharacterController() -{ -} -void CharacterController::setupCamera() -{ - // create a pivot at roughly the character's shoulder - mCameraPivot = mScnMgr->getRootSceneNode()->createChildSceneNode(); - mCameraGoal = - mCameraPivot->createChildSceneNode(Ogre::Vector3(0, 2, 3)); - mCameraNode->setPosition(mCameraPivot->getPosition() + - mCameraGoal->getPosition()); - mCameraPivot->setFixedYawAxis(true); - mCameraGoal->setFixedYawAxis(true); - mCameraNode->setFixedYawAxis(true); - // our model is quite small, so reduce the clipping planes - mCamera->setNearClipDistance(0.1f); - mCamera->setFarClipDistance(700); - - mPivotPitch = 0; - mKeyDirection = Ogre::Vector3::ZERO; - mVerticalVelocity = 0; - std::cout << "ORIGINAL: " << mCameraNode->getPosition() << "\n"; -} -bool CharacterController::keyPressed(const OgreBites::KeyboardEvent &evt) -{ - OgreBites::Keycode key = evt.keysym.sym; - if (key == 'q') { - /* ... */ - } else if (key == 'e') { - } else if (key == 'w') - mKeyDirection.z = -1; - else if (key == 'a') - mKeyDirection.x = -1; - else if (key == 's') - mKeyDirection.z = 1; - else if (key == 'd') - mKeyDirection.x = 1; - if (key == OgreBites::SDLK_LSHIFT) - mRunning = true; - if (!mKeyDirection.isZeroLength() && mCharacter->isIdle()) { - if (mRunning) - mCharacter->act_run(); - else - mCharacter->act_walk(); - // std::cout << "Walking\n"; - } else if (!mKeyDirection.isZeroLength() && mCharacter->isWalking() && - mRunning) - mCharacter->act_run(); - return true; -} -bool CharacterController::keyReleased(const OgreBites::KeyboardEvent &evt) -{ - OgreBites::Keycode key = evt.keysym.sym; - if (key == 'w' && mKeyDirection.z == -1) - mKeyDirection.z = 0; - else if (key == 'a' && mKeyDirection.x == -1) - mKeyDirection.x = 0; - else if (key == 's' && mKeyDirection.z == 1) - mKeyDirection.z = 0; - else if (key == 'd' && mKeyDirection.x == 1) - mKeyDirection.x = 0; - if (key == OgreBites::SDLK_LSHIFT) - mRunning = false; - - if (mKeyDirection.isZeroLength() && mCharacter->isMoving()) - mCharacter->act_idle(); - else if (!mKeyDirection.isZeroLength() && mCharacter->isRunning() && - !mRunning) - mCharacter->act_walk(); - - return true; -} -bool CharacterController::mouseMoved(const OgreBites::MouseMotionEvent &evt) -{ - // update camera goal based on mouse movement - updateCameraGoal(-0.18f * evt.xrel, -0.12f * evt.yrel, 0); - return true; -} -bool CharacterController::mouseWheelRolled(const OgreBites::MouseWheelEvent &evt) -{ - // update camera goal based on mouse movement - updateCameraGoal(0, 0, -0.15f * evt.y); - return true; -} -bool CharacterController::mousePressed(const OgreBites::MouseButtonEvent &evt) -{ - std::cout << "Mouse press\n"; - return false; -} -void CharacterController::frameRendered(const Ogre::FrameEvent &evt) -{ - Ogre::Vector3 goalDirection = Ogre::Vector3::ZERO; - updateCamera(evt.timeSinceLastFrame); - if (mKeyDirection != Ogre::Vector3::ZERO) { - // calculate actually goal direction in world based on player's key directions - goalDirection += - mKeyDirection.z * mCameraNode->getOrientation().zAxis(); - goalDirection += - mKeyDirection.x * mCameraNode->getOrientation().xAxis(); - goalDirection.y = 0; - goalDirection.normalise(); - mCharacter->setGoalDirection(goalDirection); - } -} -bool CharacterController::frameStarted(const Ogre::FrameEvent &evt) -{ - return true; -} -void CharacterController::updateCameraGoal(Ogre::Real deltaYaw, - Ogre::Real deltaPitch, - Ogre::Real deltaZoom) -{ - mCameraPivot->yaw(Ogre::Degree(deltaYaw), Ogre::Node::TS_PARENT); - if (!(mPivotPitch + deltaPitch > 25 && deltaPitch > 0) && - !(mPivotPitch + deltaPitch < -60 && deltaPitch < 0)) { - mCameraPivot->pitch(Ogre::Degree(deltaPitch), - Ogre::Node::TS_LOCAL); - mPivotPitch += deltaPitch; - } - Ogre::Real dist = mCameraGoal->_getDerivedPosition().distance( - mCameraPivot->_getDerivedPosition()); - Ogre::Real distChange = deltaZoom * dist; - - // bound the zoom - if (!(dist + distChange < 8 && distChange < 0) && - !(dist + distChange > 25 && distChange > 0)) - mCameraGoal->translate(0, 0, distChange, Ogre::Node::TS_LOCAL); -} -void CharacterController::updateCamera(Ogre::Real delta) -{ - // static int count = 0; - // place the camera pivot roughly at the character's shoulder - Ogre::Vector3 pivotOffset = - mCharacter->getPosition() - mCameraPivot->_getDerivedPosition(); - Ogre::Vector3 pivotPos = mCameraPivot->_getDerivedPosition(); - mCameraPivot->setPosition(pivotPos + pivotOffset * 0.1f * delta + - Ogre::Vector3::UNIT_Y * CAM_HEIGHT); - // move the camera smoothly to the goal - Ogre::Vector3 goalOffset = - mCameraGoal->_getDerivedPosition() - mCameraNode->getPosition(); - mCameraNode->translate(goalOffset * delta * 9.0f); - // always look at the pivot - mCameraNode->lookAt(mCameraPivot->_getDerivedPosition(), - Ogre::Node::TS_PARENT); -} -#endif #define CAM_HEIGHT 1.6f // height of camera above character's center of mass #define RUN_SPEED 17 // character running speed in units per second #define TURN_SPEED 500.0f // character turning in degrees per second @@ -179,265 +16,63 @@ CharacterController::CharacterController(Ogre::SceneNode *camNode, , mCamera(cam) , mScnMgr(scnMgr) , mPivotPitch(0) - , mVerticalVelocity(0) - , mAnimID(ANIM_NONE) - , mRunning(false) , mWorld(world) - , mCollisionShape(nullptr) - , mGhostObject(nullptr) - , gvelocity(0.0f, 0.0f, 0.0f) , mUpdate(false) { - setupBody(); setupCamera(); - setupAnimations(); Ogre::Root::getSingleton().addFrameListener(this); } CharacterController::~CharacterController() { } -void CharacterController::setupBody() -{ - mBodyEnt = mScnMgr->createEntity("normal-male.glb"); - mBodyNode = mScnMgr->getRootSceneNode()->createChildSceneNode(); - mBodyNode->attachObject(mBodyEnt); - mSkeleton = mBodyEnt->getSkeleton(); - // mRigidBody = world->addCharacter(mBodyEnt, 0); - // mCollisionShape = static_cast(mRigidBody->getCollisionShape()); - mGhostObject = new btPairCachingGhostObject(); - mCollisionShape = new btCompoundShape; - mGhostObject->setCollisionShape(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(mCollisionShape) - ->addChildShape(transform, shape); - btScalar masses[1] = { 0 }; - btTransform principal; - static_cast(mCollisionShape) - ->calculatePrincipalAxisTransform(masses, principal, - inertia); - } - mGhostObject->setCollisionFlags( - btCollisionObject::CF_KINEMATIC_OBJECT | - btCollisionObject::CF_NO_CONTACT_RESPONSE); - mGhostObject->setActivationState(DISABLE_DEACTIVATION); - mWorld->attachCollisionObject(mGhostObject, mBodyEnt, 1, 0x7FFFFFF); - mController = new Ogre::Bullet::KinematicMotionSimple(mGhostObject, - mBodyNode); - OgreAssert(mGhostObject, "Need GhostObject"); - OgreAssert(mController, "Need controller"); - mWorld->getBtWorld()->addAction(mController); - - OgreAssert(mCollisionShape, "No collision shape"); -#if 0 - if (mRigidBody->getMass() == 0) { -#if 0 - mRigidBody->setCollisionFlags(mRigidBody->getCollisionFlags() - | btCollisionObject::CF_KINEMATIC_OBJECT - | btCollisionObject::CF_NO_CONTACT_RESPONSE - ); -#endif -#if 0 - mGhostObject->setWorldTransform(mRigidBody->getWorldTransform()); - WorldData::get_singleton()->getBtWorld() - ->getBroadphase()->getOverlappingPairCache() - ->setInternalGhostPairCallback(new btGhostPairCallback()); -#endif - } -#endif -#if 0 - mRigidBody->setActivationState(DISABLE_DEACTIVATION); -#endif -#if 0 - { - Ogre::Entity *e2 = mScnMgr->createEntity("normal-male.glb"); - Ogre::SceneNode *e2node = mScnMgr->getRootSceneNode()->createChildSceneNode(); - e2node->attachObject(e2); - mGhostObject = WorldData::get_singleton()->addGhostObject(e2, mCollisionShape); - mController = new btKinematicCharacterController(mGhostObject, mCollisionShape, 0.5f); - WorldData::get_singleton()->getBtWorld()->addAction(mController); - } -#endif - OgreAssert(mSkeleton->hasBone("Root"), "No root bone"); - mRootBone = mSkeleton->getBone("Root"); - OgreAssert(mRootBone, "No root bone"); -} void CharacterController::setupCamera() { - // create a pivot at roughly the character's shoulder - mCameraPivot = mScnMgr->getRootSceneNode()->createChildSceneNode(); - mCameraGoal = - mCameraPivot->createChildSceneNode(Ogre::Vector3(0, 2, 3)); - mCameraNode->setPosition(mCameraPivot->getPosition() + - mCameraGoal->getPosition()); - mCameraPivot->setFixedYawAxis(true); - mCameraGoal->setFixedYawAxis(true); - mCameraNode->setFixedYawAxis(true); - // our model is quite small, so reduce the clipping planes - mCamera->setNearClipDistance(0.1f); - mCamera->setFarClipDistance(700); - - mPivotPitch = 0; - mKeyDirection = Ogre::Vector3::ZERO; - mVerticalVelocity = 0; -} -void CharacterController::setupAnimations() -{ - int i, j; - mSkeleton->setBlendMode(Ogre::ANIMBLEND_CUMULATIVE); - Ogre::String animNames[NUM_ANIMS] = { "idle", "walking", "running" }; - for (i = 0; i < NUM_ANIMS; i++) { - mAnims[i] = mBodyEnt->getAnimationState(animNames[i]); - mAnims[i]->setLoop(true); - mAnims[i]->setEnabled(true); - mAnims[i]->setWeight(0); - mFadingIn[i] = false; - mFadingOut[i] = false; - mSkelAnimations[i] = mSkeleton->getAnimation(animNames[i]); - for (const auto &it : mSkelAnimations[i]->_getNodeTrackList()) { - Ogre::NodeAnimationTrack *track = it.second; - Ogre::String trackName = - track->getAssociatedNode()->getName(); - if (trackName == "mixamorig:Hips") { - mHipsTracks[i] = track; - } else if (trackName == "Root") { - mRootTracks[i] = track; - // mRootTracks[i]->removeAllKeyFrames(); - } - } - Ogre::Vector3 delta = Ogre::Vector3::ZERO; - Ogre::Vector3 motion = Ogre::Vector3::ZERO; - for (j = 0; j < mRootTracks[i]->getNumKeyFrames(); j++) { - Ogre::Vector3 trans = mRootTracks[i] - ->getNodeKeyFrame(j) - ->getTranslate(); - if (j == 0) - delta = trans; - else - delta = trans - motion; - mRootTracks[i]->getNodeKeyFrame(j)->setTranslate(delta); - motion = trans; - } - } -#if 0 - for(i = 0; i < NUM_ANIMS - 1; i++) { - // need to cache - int j; - Ogre::String animName = mAnims[i]->getAnimationName(); - Ogre::Animation *anim = mSkeleton->getAnimation(animName); - Ogre::NodeAnimationTrack *hips_track = nullptr, *root_track = nullptr; - Ogre::Node *root_node = nullptr; - for (const auto& it : anim->_getNodeTrackList()) { - Ogre::NodeAnimationTrack* track = it.second; - Ogre::String trackName = track->getAssociatedNode()->getName(); - std::cout << animName << " track: " << trackName << "\n"; - if (trackName == "mixamorig:Hips") - hips_track = track; - else if (trackName == "Root") { - root_track = track; - root_node = track->getAssociatedNode(); - } - } - assert(false); - root_track->removeAllKeyFrames(); - std::cout << hips_track << " " << root_track << "\n"; - std::cout << hips_track->getNumKeyFrames() << " " << root_track->getNumKeyFrames() << "\n"; - assert(hips_track && root_track); - Ogre::Vector3 delta = Ogre::Vector3::ZERO; - for(j = 0; j < hips_track->getNumKeyFrames(); j++) { - float timePos = hips_track->getNodeKeyFrame(j)->getTime(); - Ogre::Vector3 trans = hips_track->getNodeKeyFrame(j)->getTranslate(); - Ogre::Vector3 hips_trans(0, 0, 0); - Ogre::Vector3 root_trans(0, 0, 0); - hips_track->getNodeKeyFrame(j)->setTranslate(hips_trans); - Ogre::TransformKeyFrame *nk = root_track->createNodeKeyFrame(timePos); - nk->setTranslate(root_trans - delta); - nk->setScale(Ogre::Vector3(1, 1, 1)); - nk->setRotation(Ogre::Quaternion()); - std::cout << animName << " delta: " << j << " " << timePos << " " << root_trans - delta << "\n"; - delta = root_trans; - } - for(j = 0; j < root_track->getNumKeyFrames(); j++) { - float timePos = hips_track->getNodeKeyFrame(j)->getTime(); - Ogre::Vector3 root_trans = hips_track->getNodeKeyFrame(j)->getTranslate(); - std::cout << animName << " delta: root: " << j << " " << timePos << " " << root_trans << "\n"; - } - } -// assert(false); -#endif - setAnimation(ANIM_IDLE); } +static uint32_t control; +static ECS::Vector2 mouse{ 0, 0 }; +static float wheel_y; +static bool mouse_moved = false, wheel_moved = false; bool CharacterController::keyPressed(const OgreBites::KeyboardEvent &evt) { - ECS::Input &input = ECS::get().get_mut(); OgreBites::Keycode key = evt.keysym.sym; - if (key == 'q' && (mAnimID == ANIM_IDLE)) { - /* ... */ - mTimer = 0; - } else if (key == 'e') { - } else if (key == 'w') - mKeyDirection.z = -1; + if (key == 'w') + control |= 1; else if (key == 'a') - mKeyDirection.x = -1; + control |= 2; else if (key == 's') - mKeyDirection.z = 1; + control |= 4; else if (key == 'd') - mKeyDirection.x = 1; - if (key == OgreBites::SDLK_LSHIFT) - mRunning = true; - if (!mKeyDirection.isZeroLength() && mAnimID == ANIM_IDLE) { - if (mRunning) - setAnimation(ANIM_RUN, true); - else - setAnimation(ANIM_WALK, true); - // std::cout << "Walking\n"; - } else if (!mKeyDirection.isZeroLength() && mAnimID == ANIM_WALK && - mRunning) - setAnimation(ANIM_RUN); + control |= 8; + else if (key == OgreBites::SDLK_LSHIFT) + control |= 16; return true; } bool CharacterController::keyReleased(const OgreBites::KeyboardEvent &evt) { OgreBites::Keycode key = evt.keysym.sym; - if (key == 'w' && mKeyDirection.z == -1) - mKeyDirection.z = 0; - else if (key == 'a' && mKeyDirection.x == -1) - mKeyDirection.x = 0; - else if (key == 's' && mKeyDirection.z == 1) - mKeyDirection.z = 0; - else if (key == 'd' && mKeyDirection.x == 1) - mKeyDirection.x = 0; - if (key == OgreBites::SDLK_LSHIFT) - mRunning = false; - - if (mKeyDirection.isZeroLength() && - (mAnimID == ANIM_WALK || mAnimID == ANIM_RUN)) - setAnimation(ANIM_IDLE); - else if (!mKeyDirection.isZeroLength() && mAnimID == ANIM_RUN && - !mRunning) - setAnimation(ANIM_WALK); - + if (key == 'w') + control &= ~1; + else if (key == 'a') + control &= ~2; + else if (key == 's') + control &= ~4; + else if (key == 'd') + control &= ~8; + else if (key == OgreBites::SDLK_LSHIFT) + control &= ~16; return true; } bool CharacterController::mouseMoved(const OgreBites::MouseMotionEvent &evt) { - // update camera goal based on mouse movement - updateCameraGoal(-0.18f * evt.xrel, -0.12f * evt.yrel, 0); + mouse.x = evt.xrel; + mouse.y = evt.yrel; + mouse_moved = true; return true; } bool CharacterController::mouseWheelRolled(const OgreBites::MouseWheelEvent &evt) { - // update camera goal based on mouse movement - updateCameraGoal(0, 0, -0.15f * evt.y); + wheel_y = evt.y; + wheel_moved = true; return true; } bool CharacterController::mousePressed(const OgreBites::MouseButtonEvent &evt) @@ -449,208 +84,29 @@ void CharacterController::frameRendered(const Ogre::FrameEvent &evt) { if (mUpdate) { updateBody(evt.timeSinceLastFrame); - updateAnimations(evt.timeSinceLastFrame); updateCamera(evt.timeSinceLastFrame); - if (evt.timeSinceLastFrame > 0) - updateRootMotion(evt.timeSinceLastFrame); } } bool CharacterController::frameStarted(const Ogre::FrameEvent &evt) { return true; } -void CharacterController::updateCameraGoal(Ogre::Real deltaYaw, - Ogre::Real deltaPitch, - Ogre::Real deltaZoom) -{ - mCameraPivot->yaw(Ogre::Degree(deltaYaw), Ogre::Node::TS_PARENT); - if (!(mPivotPitch + deltaPitch > 25 && deltaPitch > 0) && - !(mPivotPitch + deltaPitch < -60 && deltaPitch < 0)) { - mCameraPivot->pitch(Ogre::Degree(deltaPitch), - Ogre::Node::TS_LOCAL); - mPivotPitch += deltaPitch; - } - Ogre::Real dist = mCameraGoal->_getDerivedPosition().distance( - mCameraPivot->_getDerivedPosition()); - Ogre::Real distChange = deltaZoom * dist; - - // bound the zoom - if (!(dist + distChange < 8 && distChange < 0) && - !(dist + distChange > 25 && distChange > 0)) - mCameraGoal->translate(0, 0, distChange, Ogre::Node::TS_LOCAL); -} void CharacterController::updateBody(Ogre::Real delta) { - mGoalDirection = Ogre::Vector3::ZERO; - if (mKeyDirection != Ogre::Vector3::ZERO) { - // calculate actually goal direction in world based on player's key directions - mGoalDirection += - mKeyDirection.z * mCameraNode->getOrientation().zAxis(); - mGoalDirection += - mKeyDirection.x * mCameraNode->getOrientation().xAxis(); - mGoalDirection.y = 0; - mGoalDirection.normalise(); - - Ogre::Quaternion toGoal = - mBodyNode->getOrientation().zAxis().getRotationTo( - 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); - mBodyNode->yaw(Ogre::Degree(yawToGoal)); - // move in current body direction (not the goal direction) -// mBodyNode->translate(0, 0, delta * RUN_SPEED * mAnims[mAnimID]->getWeight(), -// Ogre::Node::TS_LOCAL); -#if 0 - if (mBaseAnimID == ANIM_JUMP_LOOP) - { - // if we're jumping, add a vertical offset too, and apply gravity - mBodyNode->translate(0, mVerticalVelocity * deltaTime, 0, Node::TS_LOCAL); - mVerticalVelocity -= GRAVITY * deltaTime; - - Vector3 pos = mBodyNode->getPosition(); - if (pos.y <= CHAR_HEIGHT) - { - // if we've hit the ground, change to landing state - pos.y = CHAR_HEIGHT; - mBodyNode->setPosition(pos); - setBaseAnimation(ANIM_JUMP_END, true); - mTimer = 0; - } - } -#endif - } -} -void CharacterController::updateAnimations(Ogre::Real delta) -{ - int i, j, k; - Ogre::Real animSpeed = 1; - mTimer += delta; - if (mAnimID != ANIM_NONE) { - if (mAnimID == ANIM_WALK) - mAnims[mAnimID]->addTime(delta * 1.0f); - else - mAnims[mAnimID]->addTime(delta * animSpeed); - } - fadeAnimations(delta); + ECS::Input &input = ECS::get().get_mut(); + input.control = control; + input.mouse = mouse; + mouse.x = 0; + mouse.y = 0; + input.wheel_y = wheel_y; + wheel_y = 0; + input.mouse_moved = mouse_moved; + input.wheel_moved = wheel_moved; } struct EntityCollisionListener { const Ogre::MovableObject *entity; Ogre::Bullet::CollisionListener *listener; }; -void CharacterController::updateRootMotion(Ogre::Real delta) -{ - int i, j, k; - Ogre::Quaternion rot = mBodyNode->getOrientation(); - Ogre::Vector3 pos = mBodyNode->getPosition(); - Ogre::Vector3 boneMotion = mRootBone->getPosition(); - 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); - gvelocity += gravity * delta; - velocity += gvelocity; - Ogre::Vector3 rotMotion = velocity * delta; - btVector3 currentPosition = - mGhostObject->getWorldTransform().getOrigin(); - is_on_floor = mController->isOnFloor(); - penetration = mController->isPenetrating(); - if (is_on_floor) - gvelocity = Ogre::Vector3(0.0f, 0.0f, 0.0f); - - btTransform from(convert(mBodyNode->getOrientation()), - convert(mBodyNode->getPosition())); - mBodyNode->setPosition(mBodyNode->getPosition() + rotMotion); -#if 0 - std::cout << "rotMotion: " << rotMotion - << " isOnFloor: " << is_on_floor - << " pairs: " << mGhostObject->getOverlappingPairCache()->getNumOverlappingPairs() - << " manifolds: " << mController->getManifolds() - << " penetration: " << penetration << "\n"; - std::cout << " position: " << mBodyNode->getPosition() << "\n"; -#endif -#if 0 - std::cout - << " velocity: " << velocity - << " penetration: " << penetration - << " rotMotion: " << rotMotion - << "\n"; - std::cout << "old position: " << pos - << " new position: " << mBodyNode->getPosition() - << "\n"; -#endif -} -void CharacterController::fadeAnimations(Ogre::Real delta) -{ - int i; - for (i = 0; i < NUM_ANIMS; i++) { - if (mFadingIn[i]) { - // slowly fade this animation in until it has full weight - Ogre::Real newWeight = mAnims[i]->getWeight() + - delta * ANIM_FADE_SPEED; - mAnims[i]->setWeight( - Ogre::Math::Clamp(newWeight, 0, 1)); - if (newWeight >= 1) - mFadingIn[i] = false; - } else if (mFadingOut[i]) { - // slowly fade this animation out until it has no weight, and then disable it - Ogre::Real newWeight = mAnims[i]->getWeight() - - delta * ANIM_FADE_SPEED; - mAnims[i]->setWeight( - Ogre::Math::Clamp(newWeight, 0, 1)); - if (newWeight <= 0) { - mAnims[i]->setEnabled(false); - mFadingOut[i] = false; - } - } - } -} void CharacterController::updateCamera(Ogre::Real delta) { - // place the camera pivot roughly at the character's shoulder - mCameraPivot->setPosition(mBodyNode->getPosition() + - Ogre::Vector3::UNIT_Y * CAM_HEIGHT); - // move the camera smoothly to the goal - Ogre::Vector3 goalOffset = - mCameraGoal->_getDerivedPosition() - mCameraNode->getPosition(); - mCameraNode->translate(goalOffset * delta * 9.0f); - // always look at the pivot - mCameraNode->lookAt(mCameraPivot->_getDerivedPosition(), - Ogre::Node::TS_PARENT); -} -void CharacterController::setAnimation(AnimID id, bool reset) -{ - assert(id >= 0 && id < NUM_ANIMS); - if (mAnimID != ANIM_NONE) { - mFadingIn[mAnimID] = false; - mFadingOut[mAnimID] = true; - } - mAnimID = id; - if (id != ANIM_NONE) { - mAnims[id]->setEnabled(true); - mAnims[id]->setWeight(0); - mFadingOut[id] = false; - mFadingIn[id] = true; - if (reset) - mAnims[id]->setTimePosition(0); - } -} +} \ No newline at end of file diff --git a/src/characters/controller.h b/src/characters/controller.h index 4c6e373..4ccad2a 100644 --- a/src/characters/controller.h +++ b/src/characters/controller.h @@ -1,8 +1,8 @@ +#include #include #include #include -#if 0 -class Character; +#include "GameData.h" class CharacterController : public OgreBites::InputListener, Ogre::FrameListener { Ogre::SceneNode *mCameraNode; @@ -12,78 +12,17 @@ class CharacterController : public OgreBites::InputListener, Ogre::SceneNode *mCameraPivot; Ogre::SceneNode *mCameraGoal; Ogre::Real mPivotPitch; - Ogre::Real mVerticalVelocity; - Ogre::Vector3 - mKeyDirection; // player's local intended direction based on WASD keys - bool mRunning; - Character *mCharacter; - -public: - CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam, - Ogre::SceneManager *scnMgr, Character *character); - ~CharacterController(); - -private: - void setupCamera(); - -public: - bool keyPressed(const OgreBites::KeyboardEvent &evt) override; - bool keyReleased(const OgreBites::KeyboardEvent &evt) override; - bool mouseMoved(const OgreBites::MouseMotionEvent &evt) override; - bool mouseWheelRolled(const OgreBites::MouseWheelEvent &evt) override; - bool mousePressed(const OgreBites::MouseButtonEvent &evt) override; - bool frameStarted(const Ogre::FrameEvent &evt) override; - void frameRendered(const Ogre::FrameEvent &evt) override; - -private: - void updateCamera(Ogre::Real deltaTime); - void updateCameraGoal(Ogre::Real deltaYaw, Ogre::Real deltaPitch, - Ogre::Real deltaZoom); -}; -#endif -class CharacterController : public OgreBites::InputListener, - Ogre::FrameListener { - enum AnimID { - ANIM_IDLE = 0, - ANIM_WALK, - ANIM_RUN, - NUM_ANIMS, - ANIM_NONE = NUM_ANIMS - }; - Ogre::Node *mRootBone; - Ogre::SceneNode *mCameraNode; - Ogre::Camera *mCamera; - Ogre::SceneManager *mScnMgr; - - Ogre::SceneNode *mCameraPivot; - Ogre::SceneNode *mCameraGoal, *mBodyNode; - Ogre::Entity *mBodyEnt; - Ogre::Real mPivotPitch; - Ogre::Real mVerticalVelocity; - Ogre::Vector3 - mKeyDirection; // player's local intended direction based on WASD keys - Ogre::Vector3 mGoalDirection; // actual intended direction in world-space - Ogre::AnimationState *mAnims[NUM_ANIMS]; // master animation list - Ogre::Animation *mSkelAnimations[NUM_ANIMS]; - Ogre::NodeAnimationTrack *mHipsTracks[NUM_ANIMS]; - Ogre::NodeAnimationTrack *mRootTracks[NUM_ANIMS]; - AnimID mAnimID; - bool mFadingIn[NUM_ANIMS]; // which animations are fading in - bool mFadingOut[NUM_ANIMS]; // which animations are fading out Ogre::Real mTimer; // general timer to see how long animations have been playing - Ogre::Skeleton *mSkeleton; - bool mRunning; Ogre::Bullet::DynamicsWorld *mWorld; Ogre::Vector3 rootMotion; Ogre::Quaternion rootRotation; // btRigidBody *mRigidBody; - btCompoundShape *mCollisionShape; - btPairCachingGhostObject *mGhostObject; +#if 0 Ogre::Vector3 gvelocity; +#endif bool mUpdate; - Ogre::Bullet::KinematicMotionSimple *mController; public: CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam, @@ -92,9 +31,7 @@ public: ~CharacterController(); private: - void setupBody(); void setupCamera(); - void setupAnimations(); public: bool keyPressed(const OgreBites::KeyboardEvent &evt) override; @@ -107,33 +44,7 @@ public: private: void updateBody(Ogre::Real deltaTime); - void updateAnimations(Ogre::Real deltaTime); - void updateRootMotion(Ogre::Real deltaTime); - void fadeAnimations(Ogre::Real deltaTime); void updateCamera(Ogre::Real deltaTime); - void updateCameraGoal(Ogre::Real deltaYaw, Ogre::Real deltaPitch, - Ogre::Real deltaZoom); - void setAnimation(AnimID id, bool reset = false); -#if 0 - struct testMotionResult { - }; - struct recoverResult { - }; - - bool bodyTestMotion(btRigidBody *body, - const btTransform &from, - const btVector3 &motion, bool infinite_inertia, - textMotionResult *result, - bool excludeRaycastShapes, - const std::set &exclude); - bool recoverFromPenetration(btRigidBody *body, - const btTransform &body_position, - btScalar recover_movement_scale, - bool infinite_inertia, - btVector3 &delta_recover_movement, - recoverResult *recover_result, - const std::set &exclude); -#endif inline btQuaternion convert(const Ogre::Quaternion &q) { return btQuaternion(q.x, q.y, q.z, q.w); @@ -163,6 +74,7 @@ private: q = convert(from.getRotation()); v = convert(from.getOrigin()); } + public: void enableUpdates() { @@ -178,36 +90,53 @@ public: } Ogre::Vector3 getPosition() const { - return mBodyNode->getPosition(); + flecs::entity player = + ECS::get().lookup("ECS::CharacterModule::player"); + return player.get().mBodyNode->getPosition(); } Ogre::Quaternion getRotation() const { - return mBodyNode->getOrientation(); + flecs::entity player = + ECS::get().lookup("ECS::CharacterModule::player"); + return player.get() + .mBodyNode->getOrientation(); } - class ClosestNotMeRayResultCallback: public btCollisionWorld::ClosestRayResultCallback { + class ClosestNotMeRayResultCallback + : public btCollisionWorld::ClosestRayResultCallback { btCollisionObject *mMe; + public: - ClosestNotMeRayResultCallback( - btCollisionObject *me, - const btVector3 &from, - const btVector3 &to): - btCollisionWorld::ClosestRayResultCallback(from, to) { + ClosestNotMeRayResultCallback(btCollisionObject *me, + const btVector3 &from, + const btVector3 &to) + : btCollisionWorld::ClosestRayResultCallback(from, to) + { mMe = me; } - virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult &rayResult, bool normalInWorldSpace) + virtual btScalar + addSingleResult(btCollisionWorld::LocalRayResult &rayResult, + bool normalInWorldSpace) { if (rayResult.m_collisionObject == mMe) return 1.0f; - return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace); + return ClosestRayResultCallback::addSingleResult( + rayResult, normalInWorldSpace); } }; bool checkGround() { - btVector3 from = mGhostObject->getWorldTransform().getOrigin() + btVector3(0, 0.2f, 0); + flecs::entity player = + ECS::get().lookup("ECS::CharacterModule::player"); + btVector3 from = player.get() + .mGhostObject->getWorldTransform() + .getOrigin() + + btVector3(0, 0.2f, 0); btVector3 to = from + btVector3(0, -3000.0f, 0); - ClosestNotMeRayResultCallback - resultCallback(mGhostObject, from, to); - mGhostObject->rayTest(from, to, resultCallback); + ClosestNotMeRayResultCallback resultCallback( + player.get().mGhostObject, from, + to); + player.get().mGhostObject->rayTest( + from, to, resultCallback); // std::cout << (resultCallback.hasHit() ? "hit" : "no hit"); // if (resultCallback.hasHit()) // std::cout << " " << Ogre::Bullet::convert(resultCallback.m_hitPointWorld); diff --git a/src/gamedata/GameData.cpp b/src/gamedata/GameData.cpp index bd693c8..e2e511d 100644 --- a/src/gamedata/GameData.cpp +++ b/src/gamedata/GameData.cpp @@ -1,3 +1,4 @@ +#include #include #include "GameData.h" @@ -6,24 +7,513 @@ namespace ECS static flecs::world ecs; CharacterModule::CharacterModule(flecs::world &ecs) { - ecs.component(); - ecs.add(); - ecs.system("HandleInput").kind(flecs::OnUpdate).run([](flecs::iter &it) { - /* handle input */ - }); + 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({}); + 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)); + } + }); } -void setup() +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) +{ + ecs.component().add(flecs::Singleton); + ecs.component().add(flecs::Singleton); + ecs.component().add(flecs::Singleton); + ecs.component().add(flecs::Singleton); + ecs.set({ scnMgr, world }); + ecs.set({ cameraNode, camera, false }); ecs.add(); + ecs.add(); ecs.import (); } void update(float delta) { ecs.progress(delta); } -flecs::world &get() +flecs::world get() { return ecs; } +bool Vector3::zeroLength() const +{ + float l = x * x + y * y + z * z; + return (l < 1e-06 * 1e-06); +} } \ No newline at end of file diff --git a/src/gamedata/GameData.h b/src/gamedata/GameData.h index 1eff8c3..b422b5a 100644 --- a/src/gamedata/GameData.h +++ b/src/gamedata/GameData.h @@ -5,32 +5,94 @@ namespace ECS { struct GameData { - flecs::entity player; + 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() - : motion({ 0, 0, 0 }) + : 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::DynamicsWorld *mWorld; + Ogre::Bullet::KinematicMotionSimple *mController; + Ogre::Vector3 gvelocity; +}; +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(); +void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world, + Ogre::SceneNode *cameraNode, Ogre::Camera *camera); void update(float delta); -flecs::world &get(); +flecs::world get(); } #endif \ No newline at end of file