From 5c03f0cd2c4c86c260e10b883f0da18c743bd6c7 Mon Sep 17 00:00:00 2001 From: Sergey Lapin Date: Mon, 15 Sep 2025 01:51:38 +0300 Subject: [PATCH] Water works! --- Bootstrap.cpp | 819 +++++++++++---------- CMakeLists.txt | 1 + Game.cpp | 425 ++++++----- Procedural.cpp | 4 +- src/gamedata/CharacterModule.cpp | 203 ++++-- src/gamedata/CharacterModule.h | 5 +- src/gamedata/Components.h | 11 +- src/gamedata/GUIModule.cpp | 48 +- src/gamedata/GUIModule.h | 2 - src/gamedata/GameData.cpp | 50 +- src/gamedata/GameData.h | 3 +- src/gamedata/SunModule.cpp | 1 - src/gamedata/TerrainModule.cpp | 83 ++- src/gamedata/WaterModule.cpp | 1144 +++++++++++++++++++----------- src/gamedata/WaterModule.h | 9 +- terrain.cpp | 830 ++++++++++++---------- tests/CMakeLists.txt | 8 + tests/compound_shapes.cpp | 290 ++++++++ 18 files changed, 2435 insertions(+), 1501 deletions(-) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/compound_shapes.cpp diff --git a/Bootstrap.cpp b/Bootstrap.cpp index 93330d9..f43e4c0 100644 --- a/Bootstrap.cpp +++ b/Bootstrap.cpp @@ -12,10 +12,11 @@ #include "LinearMath/btTransform.h" #include "OgrePageManager.h" -#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 -#define ANIM_FADE_SPEED 7.5f // animation crossfade speed in % of full weight per second +#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 +#define ANIM_FADE_SPEED \ + 7.5f // animation crossfade speed in % of full weight per second using Real = Ogre::Real; using Math = Ogre::Math; @@ -27,21 +28,43 @@ class WorldData { std::unique_ptr mbtWorld; std::unique_ptr mPageManager; Ogre::PagedWorld *mPagedWorld; + private: static WorldData *singleton; - class DummyPageProvider : public Ogre::PageProvider - { + class DummyPageProvider : public Ogre::PageProvider { public: - bool prepareProceduralPage(Ogre::Page* page, Ogre::PagedWorldSection* section) override { return true; } - bool loadProceduralPage(Ogre::Page* page, Ogre::PagedWorldSection* section) override { return true; } - bool unloadProceduralPage(Ogre::Page* page, Ogre::PagedWorldSection* section) override { return true; } - bool unprepareProceduralPage(Ogre::Page* page, Ogre::PagedWorldSection* section) override { return true; } + bool + prepareProceduralPage(Ogre::Page *page, + Ogre::PagedWorldSection *section) override + { + return true; + } + bool + loadProceduralPage(Ogre::Page *page, + Ogre::PagedWorldSection *section) override + { + return true; + } + bool + unloadProceduralPage(Ogre::Page *page, + Ogre::PagedWorldSection *section) override + { + return true; + } + bool unprepareProceduralPage( + Ogre::Page *page, + Ogre::PagedWorldSection *section) override + { + return true; + } }; DummyPageProvider mDummyPageProvider; WorldData(Ogre::Root *root, Ogre::SceneManager *scnMgr) - : mDynWorld(new Ogre::Bullet::DynamicsWorld(Ogre::Vector3(0, -9.8, 0))) - , mDbgDraw(new Ogre::Bullet::DebugDrawer(scnMgr->getRootSceneNode(), mDynWorld->getBtWorld())) + : mDynWorld(new Ogre::Bullet::DynamicsWorld( + Ogre::Vector3(0, -9.8, 0))) + , mDbgDraw(new Ogre::Bullet::DebugDrawer( + scnMgr->getRootSceneNode(), mDynWorld->getBtWorld())) , mRoot(root) , mScnMgr(scnMgr) , mbtWorld(mDynWorld->getBtWorld()) @@ -49,6 +72,7 @@ private: , mPagedWorld(nullptr) { } + public: static void init(Ogre::Root *root, Ogre::SceneManager *scnMgr) { @@ -75,16 +99,20 @@ public: void createTrimesh(Ogre::Entity *entity) { } - btPairCachingGhostObject *addGhostObject(Ogre::Entity *ent, btCollisionShape *shape, int group = 1, int mask = 0xFFFF) + btPairCachingGhostObject *addGhostObject(Ogre::Entity *ent, + btCollisionShape *shape, + int group = 1, + int mask = 0xFFFF) { btDynamicsWorld *world = mDynWorld->getBtWorld(); Ogre::SceneNode *node = ent->getParentSceneNode(); - btPairCachingGhostObject *ghost = new btPairCachingGhostObject(); + btPairCachingGhostObject *ghost = + new btPairCachingGhostObject(); ghost->setCollisionShape(shape); - ghost->setCollisionFlags(ghost->getCollisionFlags() - | btCollisionObject::CF_NO_CONTACT_RESPONSE - | btCollisionObject::CF_CHARACTER_OBJECT - ); + ghost->setCollisionFlags( + ghost->getCollisionFlags() | + btCollisionObject::CF_NO_CONTACT_RESPONSE | + btCollisionObject::CF_CHARACTER_OBJECT); getWorld()->attachCollisionObject(ghost, ent, group, mask); #if 0 getBtWorld() @@ -93,34 +121,40 @@ public: ghost->setUserPointer(new EntityCollisionListener{ent, nullptr}); #endif return ghost; - } - btRigidBody *addRigidBody(float mass, Ogre::Entity *ent, Ogre::Bullet::ColliderType ct, int group = 1, int mask = 0xFFFF) + btRigidBody *addRigidBody(float mass, Ogre::Entity *ent, + Ogre::Bullet::ColliderType ct, int group = 1, + int mask = 0xFFFF) { btDynamicsWorld *world = mDynWorld->getBtWorld(); Ogre::SceneNode *node = ent->getParentSceneNode(); - Ogre::Bullet::RigidBodyState *state = new Ogre::Bullet::RigidBodyState(node); + Ogre::Bullet::RigidBodyState *state = + new Ogre::Bullet::RigidBodyState(node); btCollisionShape *cs; btCollisionShape *shape; btVector3 inertia(0, 0, 0); - switch(ct) { + switch (ct) { case Ogre::Bullet::CT_TRIMESH: { cs = Ogre::Bullet::createTrimeshCollider(ent); if (mass != 0) cs->calculateLocalInertia(mass, inertia); } break; case Ogre::Bullet::CT_CAPSULE: { - cs = new btCompoundShape(); - btScalar height = 1.0f; + cs = new btCompoundShape(false); + btScalar height = 1.0f; btScalar radius = 0.3f; - shape = new btCapsuleShape(radius, 2 * height - 2 * radius); + shape = new btCapsuleShape(radius, + 2 * height - 2 * radius); btTransform transform; transform.setIdentity(); transform.setOrigin(btVector3(0, 1, 0)); - static_cast(cs)->addChildShape(transform, shape); - btScalar masses[1] = {mass}; + static_cast(cs)->addChildShape( + transform, shape); + btScalar masses[1] = { mass }; btTransform principal; - static_cast(cs)->calculatePrincipalAxisTransform(masses, principal, inertia); + static_cast(cs) + ->calculatePrincipalAxisTransform( + masses, principal, inertia); } break; default: assert(false); @@ -134,7 +168,9 @@ public: #endif return body; } - btRigidBody *addKinematicRigidBody(float mass, Ogre::Entity *ent, Ogre::Bullet::ColliderType ct, int group = 1, int mask = 0xFFFF) + btRigidBody *addKinematicRigidBody(float mass, Ogre::Entity *ent, + Ogre::Bullet::ColliderType ct, + int group = 1, int mask = 0xFFFF) { return mDynWorld->addKinematicRigidBody(ent, ct, group, mask); } @@ -148,9 +184,9 @@ public: } void update(float delta) { - WorldData::get_singleton() - ->getBtWorld()->stepSimulation(delta, 10); - mDbgDraw->update(); + WorldData::get_singleton()->getBtWorld()->stepSimulation(delta, + 10); + mDbgDraw->update(); } void initPagedWorld(Ogre::Camera *camera) { @@ -163,90 +199,102 @@ public: }; WorldData *WorldData::singleton = nullptr; -class MainWorld : public Ogre::FrameListener -{ - btRigidBody *mFloorBody; -public: - void setup() - { -// mScnMgr = scnMgr; -// mDynWorld.reset(new Ogre::Bullet::DynamicsWorld(Ogre::Vector3(0, -9.8, 0))); -// mDbgDraw.reset(new Ogre::Bullet::DebugDrawer(mScnMgr->getRootSceneNode(), mDynWorld->getBtWorld())); - Ogre::MeshManager::getSingleton().createPlane("floor", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::Plane(Ogre::Vector3::UNIT_Y, 0), 100, 100, 10, 10, true, 1, 10, 10, Ogre::Vector3::UNIT_Z); +class MainWorld : public Ogre::FrameListener { + btRigidBody *mFloorBody; - // create a floor entity, give it a material, and place it at the origin - Ogre::SceneManager *scnMgr = WorldData::get_singleton()->getSceneManager(); - Ogre::Entity *floor = scnMgr->createEntity("Floor", "floor"); - scnMgr->getRootSceneNode()->attachObject(floor); - mFloorBody = WorldData::get_singleton()->addRigidBody(0, floor, Ogre::Bullet::CT_TRIMESH); - } - btRigidBody *addCharacter(Ogre::Entity *ent, float mass) - { - return WorldData::get_singleton()->addKinematicRigidBody(mass, ent, Ogre::Bullet::CT_COMPOUND); - } - bool frameStarted(const Ogre::FrameEvent& evt) override; +public: + void setup() + { + // mScnMgr = scnMgr; + // mDynWorld.reset(new Ogre::Bullet::DynamicsWorld(Ogre::Vector3(0, -9.8, 0))); + // mDbgDraw.reset(new Ogre::Bullet::DebugDrawer(mScnMgr->getRootSceneNode(), mDynWorld->getBtWorld())); + Ogre::MeshManager::getSingleton().createPlane( + "floor", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::Plane(Ogre::Vector3::UNIT_Y, 0), 100, 100, 10, 10, + true, 1, 10, 10, Ogre::Vector3::UNIT_Z); + + // create a floor entity, give it a material, and place it at the origin + Ogre::SceneManager *scnMgr = + WorldData::get_singleton()->getSceneManager(); + Ogre::Entity *floor = scnMgr->createEntity("Floor", "floor"); + scnMgr->getRootSceneNode()->attachObject(floor); + mFloorBody = WorldData::get_singleton()->addRigidBody( + 0, floor, Ogre::Bullet::CT_TRIMESH); + } + btRigidBody *addCharacter(Ogre::Entity *ent, float mass) + { + return WorldData::get_singleton()->addKinematicRigidBody( + mass, ent, Ogre::Bullet::CT_COMPOUND); + } + bool frameStarted(const Ogre::FrameEvent &evt) override; }; -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; +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; + Real mPivotPitch; + 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 + Real mTimer; // general timer to see how long animations have been playing + Ogre::Skeleton *mSkeleton; + bool mRunning; + MainWorld *world; + Ogre::Vector3 rootMotion; + Ogre::Quaternion rootRotation; + // btRigidBody *mRigidBody; + btCompoundShape *mCollisionShape; + btPairCachingGhostObject *mGhostObject; - Ogre::SceneNode *mCameraPivot; - Ogre::SceneNode *mCameraGoal, *mBodyNode; - Ogre::Entity *mBodyEnt; - Real mPivotPitch; - 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 - Real mTimer; // general timer to see how long animations have been playing - Ogre::Skeleton *mSkeleton; - bool mRunning; - MainWorld *world; - Ogre::Vector3 rootMotion; - Ogre::Quaternion rootRotation; -// btRigidBody *mRigidBody; - btCompoundShape *mCollisionShape; - btPairCachingGhostObject *mGhostObject; public: - CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam, Ogre::SceneManager *scnMgr, MainWorld *world); - ~CharacterController(); + CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam, + Ogre::SceneManager *scnMgr, MainWorld *world); + ~CharacterController(); + private: - void setupBody(); - void setupCamera(); - void setupAnimations(); + void setupBody(); + void setupCamera(); + void setupAnimations(); + 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; + 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 updateBody(Ogre::Real deltaTime); - void updateAnimations(Real deltaTime); - void updateRootMotion(Real deltaTime); - void fadeAnimations(Real deltaTime); - void updateCamera(Real deltaTime); - void updateCameraGoal(Real deltaYaw, Real deltaPitch, Real deltaZoom); - void setAnimation(AnimID id, bool reset = false); + void updateBody(Ogre::Real deltaTime); + void updateAnimations(Real deltaTime); + void updateRootMotion(Real deltaTime); + void fadeAnimations(Real deltaTime); + void updateCamera(Real deltaTime); + void updateCameraGoal(Real deltaYaw, Real deltaPitch, Real deltaZoom); + void setAnimation(AnimID id, bool reset = false); #if 0 struct testMotionResult { }; @@ -267,51 +315,51 @@ private: 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); - } - inline btVector3 convert(const Ogre::Vector3& v) - { - return btVector3(v.x, v.y, v.z); - } - inline btTransform convert(const Ogre::Quaternion& q, const Ogre::Vector3& v) - { - btQuaternion mq = convert(q); - btVector3 mv = convert(v); - return btTransform(mq, mv); - } - inline Ogre::Quaternion convert(const btQuaternion& q) - { - return Ogre::Quaternion(q.w(), q.x(), q.y(), q.z()); - } - inline Ogre::Vector3 convert(const btVector3& v) - { - return Ogre::Vector3(v.x(), v.y(), v.z()); - } - inline void convert(const btTransform &from, Ogre::Quaternion &q, Ogre::Vector3 &v) - { - q = convert(from.getRotation()); - v = convert(from.getOrigin()); - } + inline btQuaternion convert(const Ogre::Quaternion &q) + { + return btQuaternion(q.x, q.y, q.z, q.w); + } + inline btVector3 convert(const Ogre::Vector3 &v) + { + return btVector3(v.x, v.y, v.z); + } + inline btTransform convert(const Ogre::Quaternion &q, + const Ogre::Vector3 &v) + { + btQuaternion mq = convert(q); + btVector3 mv = convert(v); + return btTransform(mq, mv); + } + inline Ogre::Quaternion convert(const btQuaternion &q) + { + return Ogre::Quaternion(q.w(), q.x(), q.y(), q.z()); + } + inline Ogre::Vector3 convert(const btVector3 &v) + { + return Ogre::Vector3(v.x(), v.y(), v.z()); + } + inline void convert(const btTransform &from, Ogre::Quaternion &q, + Ogre::Vector3 &v) + { + q = convert(from.getRotation()); + v = convert(from.getOrigin()); + } }; -CharacterController::CharacterController( - Ogre::SceneNode *camNode, - Ogre::Camera *cam, - Ogre::SceneManager *scnMgr, - MainWorld *world) -: mCameraNode(camNode) -, mCamera(cam) -, mScnMgr(scnMgr) -, mPivotPitch(0) -, mVerticalVelocity(0) -, mAnimID(ANIM_NONE) -, mRunning(false) -, world(world) -, mCollisionShape(nullptr) -, mGhostObject(nullptr) +CharacterController::CharacterController(Ogre::SceneNode *camNode, + Ogre::Camera *cam, + Ogre::SceneManager *scnMgr, + MainWorld *world) + : mCameraNode(camNode) + , mCamera(cam) + , mScnMgr(scnMgr) + , mPivotPitch(0) + , mVerticalVelocity(0) + , mAnimID(ANIM_NONE) + , mRunning(false) + , world(world) + , mCollisionShape(nullptr) + , mGhostObject(nullptr) { - setupBody(); setupCamera(); setupAnimations(); @@ -321,37 +369,47 @@ 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); + 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(false); + 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); - Ogre::Bullet::KinematicMotionSimple *controller = new Ogre::Bullet::KinematicMotionSimple(mGhostObject, mBodyNode); - WorldData::get_singleton()->getWorld()->attachCollisionObject(mGhostObject, mBodyEnt, btBroadphaseProxy::AllFilter, btBroadphaseProxy::AllFilter); - WorldData::get_singleton()->getBtWorld()->addAction(controller); + { + 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); + Ogre::Bullet::KinematicMotionSimple *controller = + new Ogre::Bullet::KinematicMotionSimple(mGhostObject, + mBodyNode); + WorldData::get_singleton()->getWorld()->attachCollisionObject( + mGhostObject, mBodyEnt, btBroadphaseProxy::AllFilter, + btBroadphaseProxy::AllFilter); + WorldData::get_singleton()->getBtWorld()->addAction(controller); - assert(mCollisionShape); + assert(mCollisionShape); #if 0 if (mRigidBody->getMass() == 0) { #if 0 @@ -381,61 +439,66 @@ void CharacterController::setupBody() WorldData::get_singleton()->getBtWorld()->addAction(mController); } #endif - assert(mSkeleton->hasBone("Root")); - mRootBone = mSkeleton->getBone("Root"); - assert(mRootBone); + assert(mSkeleton->hasBone("Root")); + mRootBone = mSkeleton->getBone("Root"); + assert(mRootBone); } 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); + // 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; + 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(); + 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(); + // 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(); + } + 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++) { @@ -483,70 +546,77 @@ void CharacterController::setupAnimations() } // assert(false); #endif - setAnimation(ANIM_IDLE); + setAnimation(ANIM_IDLE); } -bool CharacterController::keyPressed(const OgreBites::KeyboardEvent& evt) +bool CharacterController::keyPressed(const OgreBites::KeyboardEvent &evt) { OgreBites::Keycode key = evt.keysym.sym; - if (key == 'q' && (mAnimID == ANIM_IDLE)) { + if (key == 'q' && (mAnimID == ANIM_IDLE)) { /* ... */ mTimer = 0; } else if (key == 'e') { } else if (key == 'w') mKeyDirection.z = -1; - else if (key == 'a') + else if (key == 'a') mKeyDirection.x = -1; - else if (key == 's') + else if (key == 's') mKeyDirection.z = 1; - else if (key == 'd') + else if (key == 'd') mKeyDirection.x = 1; if (key == OgreBites::SDLK_LSHIFT) mRunning = true; - if (!mKeyDirection.isZeroLength() && mAnimID == ANIM_IDLE) { + 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) + } else if (!mKeyDirection.isZeroLength() && mAnimID == ANIM_WALK && + mRunning) setAnimation(ANIM_RUN); return true; } -bool CharacterController::keyReleased(const OgreBites::KeyboardEvent& evt) +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 == '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) + if (mKeyDirection.isZeroLength() && + (mAnimID == ANIM_WALK || mAnimID == ANIM_RUN)) + setAnimation(ANIM_IDLE); + else if (!mKeyDirection.isZeroLength() && mAnimID == ANIM_RUN && + !mRunning) setAnimation(ANIM_WALK); - return true; + return true; } -bool CharacterController::mouseMoved(const OgreBites::MouseMotionEvent& evt) +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) +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) +bool CharacterController::mousePressed(const OgreBites::MouseButtonEvent &evt) { std::cout << "Mouse press\n"; return false; } -void CharacterController::frameRendered(const Ogre::FrameEvent& evt) +void CharacterController::frameRendered(const Ogre::FrameEvent &evt) { updateBody(evt.timeSinceLastFrame); updateAnimations(evt.timeSinceLastFrame); @@ -554,24 +624,27 @@ void CharacterController::frameRendered(const Ogre::FrameEvent& evt) if (evt.timeSinceLastFrame > 0) updateRootMotion(evt.timeSinceLastFrame); } -bool CharacterController::frameStarted(const Ogre::FrameEvent& evt) +bool CharacterController::frameStarted(const Ogre::FrameEvent &evt) { return true; } -void CharacterController::updateCameraGoal(Real deltaYaw, Real deltaPitch, Real deltaZoom) +void CharacterController::updateCameraGoal(Real deltaYaw, Real deltaPitch, + 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 < -60 && deltaPitch < 0)) { + mCameraPivot->pitch(Ogre::Degree(deltaPitch), + Ogre::Node::TS_LOCAL); mPivotPitch += deltaPitch; } - Real dist = mCameraGoal->_getDerivedPosition().distance(mCameraPivot->_getDerivedPosition()); + Real dist = mCameraGoal->_getDerivedPosition().distance( + mCameraPivot->_getDerivedPosition()); Real distChange = deltaZoom * dist; // bound the zoom if (!(dist + distChange < 8 && distChange < 0) && - !(dist + distChange > 25 && distChange > 0)) + !(dist + distChange > 25 && distChange > 0)) mCameraGoal->translate(0, 0, distChange, Ogre::Node::TS_LOCAL); } void CharacterController::updateBody(Real delta) @@ -579,22 +652,35 @@ void CharacterController::updateBody(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 += + mKeyDirection.z * mCameraNode->getOrientation().zAxis(); + mGoalDirection += + mKeyDirection.x * mCameraNode->getOrientation().xAxis(); mGoalDirection.y = 0; mGoalDirection.normalise(); - Ogre::Quaternion toGoal = mBodyNode->getOrientation().zAxis().getRotationTo(mGoalDirection); + Ogre::Quaternion toGoal = + mBodyNode->getOrientation().zAxis().getRotationTo( + mGoalDirection); // calculate how much the character has to turn to face goal direction Real yawToGoal = toGoal.getYaw().valueDegrees(); // this is how much the character CAN turn this frame - Real yawAtSpeed = yawToGoal / Math::Abs(yawToGoal) * delta * TURN_SPEED; + Real yawAtSpeed = + yawToGoal / 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); + 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); + 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(), @@ -624,7 +710,7 @@ void CharacterController::updateAnimations(Real delta) int i, j, k; Real animSpeed = 1; mTimer += delta; - if (mAnimID != ANIM_NONE) { + if (mAnimID != ANIM_NONE) { if (mAnimID == ANIM_WALK) mAnims[mAnimID]->addTime(delta * 1.0f); else @@ -653,45 +739,49 @@ void CharacterController::updateRootMotion(Real delta) Ogre::Vector3 velocity = rot * boneMotion / delta; velocity += gravity * delta; Ogre::Vector3 rotMotion = velocity * delta; - btTransform from(convert(mBodyNode->getOrientation()), convert(mBodyNode->getPosition())); + btTransform from(convert(mBodyNode->getOrientation()), + convert(mBodyNode->getPosition())); mBodyNode->setPosition(mBodyNode->getPosition() + rotMotion); -// WorldData::get_singleton()->getWorld()->testBodyMotion(mRigidBody, from, Ogre::Bullet::convert(rotMotion), true, -// nullptr, false, std::set()); + // WorldData::get_singleton()->getWorld()->testBodyMotion(mRigidBody, from, Ogre::Bullet::convert(rotMotion), true, + // nullptr, false, std::set()); } void CharacterController::fadeAnimations(Real delta) { int i; for (i = 0; i < NUM_ANIMS; i++) { - if (mFadingIn[i]) - { - // slowly fade this animation in until it has full weight - Real newWeight = mAnims[i]->getWeight() + delta * ANIM_FADE_SPEED; - mAnims[i]->setWeight(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 - Real newWeight = mAnims[i]->getWeight() - delta * ANIM_FADE_SPEED; - mAnims[i]->setWeight(Math::Clamp(newWeight, 0, 1)); - if (newWeight <= 0) - { - mAnims[i]->setEnabled(false); - mFadingOut[i] = false; - } - } - } + if (mFadingIn[i]) { + // slowly fade this animation in until it has full weight + Real newWeight = mAnims[i]->getWeight() + + delta * ANIM_FADE_SPEED; + mAnims[i]->setWeight( + 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 + Real newWeight = mAnims[i]->getWeight() - + delta * ANIM_FADE_SPEED; + mAnims[i]->setWeight( + Math::Clamp(newWeight, 0, 1)); + if (newWeight <= 0) { + mAnims[i]->setEnabled(false); + mFadingOut[i] = false; + } + } + } } void CharacterController::updateCamera(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); + // 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) { @@ -747,44 +837,41 @@ bool CharacterController::bodyTestMotion(btRigidBody *body, } #endif -class KeyHandler : public OgreBites::InputListener -{ - bool keyPressed(const OgreBites::KeyboardEvent& evt) override - { - if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) - { - Ogre::Root::getSingleton().queueEndRendering(); - } - return true; - } +class KeyHandler : public OgreBites::InputListener { + bool keyPressed(const OgreBites::KeyboardEvent &evt) override + { + if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) { + Ogre::Root::getSingleton().queueEndRendering(); + } + return true; + } }; -bool MainWorld::frameStarted(const Ogre::FrameEvent& evt) +bool MainWorld::frameStarted(const Ogre::FrameEvent &evt) { WorldData::get_singleton()->update(evt.timeSinceLastFrame); return true; } -class App - : public OgreBites::ApplicationContext -{ - Ogre::SceneNode *mCameraNode; - Ogre::SceneManager *mScnMgr; - std::unique_ptr mCharacter; - KeyHandler mKeyHandler; - MainWorld mWorld; -public: - App(); - virtual ~App(); +class App : public OgreBites::ApplicationContext { + Ogre::SceneNode *mCameraNode; + Ogre::SceneManager *mScnMgr; + std::unique_ptr mCharacter; + KeyHandler mKeyHandler; + MainWorld mWorld; - void setup(); - void locateResources(); - void initCamera(); - Ogre::SceneManager *getSceneManager() - { - return mScnMgr; - } - void createContent(); - void createCharacter(); - void setupWorld(); +public: + App(); + virtual ~App(); + + void setup(); + void locateResources(); + void initCamera(); + Ogre::SceneManager *getSceneManager() + { + return mScnMgr; + } + void createContent(); + void createCharacter(); + void setupWorld(); }; App::App() @@ -807,72 +894,76 @@ App::~App() } void App::initCamera() { - // also need to tell where we are - mCameraNode = mScnMgr->getRootSceneNode()->createChildSceneNode(); - mCameraNode->setPosition(0, 2, 3); - mCameraNode->lookAt(Ogre::Vector3(0, 1, -1), Ogre::Node::TS_PARENT); + // also need to tell where we are + mCameraNode = mScnMgr->getRootSceneNode()->createChildSceneNode(); + mCameraNode->setPosition(0, 2, 3); + mCameraNode->lookAt(Ogre::Vector3(0, 1, -1), Ogre::Node::TS_PARENT); - // create the camera - Ogre::Camera *cam = mScnMgr->createCamera("tps_camera"); - cam->setNearClipDistance(0.1f); // specific to this sample - cam->setAutoAspectRatio(true); - mCameraNode->attachObject(cam); + // create the camera + Ogre::Camera *cam = mScnMgr->createCamera("tps_camera"); + cam->setNearClipDistance(0.1f); // specific to this sample + cam->setAutoAspectRatio(true); + mCameraNode->attachObject(cam); - // and tell it to render into the main window - getRenderWindow()->addViewport(cam); + // and tell it to render into the main window + getRenderWindow()->addViewport(cam); } void App::setupWorld() { - addInputListener(&mKeyHandler); + addInputListener(&mKeyHandler); mWorld.setup(); - getRoot()->addFrameListener(&mWorld); + getRoot()->addFrameListener(&mWorld); } void App::createCharacter() { - Ogre::Camera *cam = static_cast(mCameraNode->getAttachedObject("tps_camera")); - mCharacter = std::make_unique(mCameraNode, cam, mScnMgr, &mWorld); -// mInputListenerChain = TouchAgnosticInputListenerChain(getRenderWindow(), {&mKeyHandler, mCharacter.get()}); - addInputListener(mCharacter.get()); - WorldData::get_singleton()->initPagedWorld(cam); + Ogre::Camera *cam = static_cast( + mCameraNode->getAttachedObject("tps_camera")); + mCharacter = std::make_unique(mCameraNode, cam, + mScnMgr, &mWorld); + // mInputListenerChain = TouchAgnosticInputListenerChain(getRenderWindow(), {&mKeyHandler, mCharacter.get()}); + addInputListener(mCharacter.get()); + WorldData::get_singleton()->initPagedWorld(cam); } void App::createContent() { - // without light we would just get a black screen - Ogre::Light* light = mScnMgr->createLight("MainLight"); - Ogre::SceneNode* lightNode = mScnMgr->getRootSceneNode()->createChildSceneNode(); -// lightNode->setPosition(0, 10, 15); - lightNode->setDirection(Ogre::Vector3(0.55, -0.3, 0.75).normalisedCopy()); - lightNode->attachObject(light); - light->setType(Ogre::Light::LT_DIRECTIONAL); - light->setDiffuseColour(Ogre::ColourValue::White); - light->setSpecularColour(Ogre::ColourValue(0.4, 0.4, 0.4)); - mScnMgr->setSkyBox(true, "Skybox", 490); + // without light we would just get a black screen + Ogre::Light *light = mScnMgr->createLight("MainLight"); + Ogre::SceneNode *lightNode = + mScnMgr->getRootSceneNode()->createChildSceneNode(); + // lightNode->setPosition(0, 10, 15); + lightNode->setDirection( + Ogre::Vector3(0.55, -0.3, 0.75).normalisedCopy()); + lightNode->attachObject(light); + light->setType(Ogre::Light::LT_DIRECTIONAL); + light->setDiffuseColour(Ogre::ColourValue::White); + light->setSpecularColour(Ogre::ColourValue(0.4, 0.4, 0.4)); + mScnMgr->setSkyBox(true, "Skybox", 490); } int main(int argc, char *argv[]) { - App ctx; - ctx.initApp(); - // get a pointer to the already created root - Ogre::Root* root = ctx.getRoot(); - Ogre::SceneManager* scnMgr = ctx.getSceneManager(); + App ctx; + ctx.initApp(); + // get a pointer to the already created root + Ogre::Root *root = ctx.getRoot(); + Ogre::SceneManager *scnMgr = ctx.getSceneManager(); - // register our scene with the RTSS - Ogre::RTShader::ShaderGenerator* shadergen = Ogre::RTShader::ShaderGenerator::getSingletonPtr(); - shadergen->addSceneManager(scnMgr); - WorldData::init(root, scnMgr); - ctx.setWindowGrab(true); - ctx.createContent(); - ctx.initCamera(); - ctx.setupWorld(); - ctx.createCharacter(); - // register for input events -// KeyHandler keyHandler; -// ctx.addInputListener(&keyHandler); + // register our scene with the RTSS + Ogre::RTShader::ShaderGenerator *shadergen = + Ogre::RTShader::ShaderGenerator::getSingletonPtr(); + shadergen->addSceneManager(scnMgr); + WorldData::init(root, scnMgr); + ctx.setWindowGrab(true); + ctx.createContent(); + ctx.initCamera(); + ctx.setupWorld(); + ctx.createCharacter(); + // register for input events + // KeyHandler keyHandler; + // ctx.addInputListener(&keyHandler); - ctx.getRoot()->startRendering(); - ctx.setWindowGrab(false); - ctx.closeApp(); - WorldData::cleanup(); - return 0; + ctx.getRoot()->startRendering(); + ctx.setWindowGrab(false); + ctx.closeApp(); + WorldData::cleanup(); + return 0; } - diff --git a/CMakeLists.txt b/CMakeLists.txt index 374c148..0997037 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,7 @@ add_subdirectory(src/gamedata) add_subdirectory(src/miniaudio) add_subdirectory(src/sound) add_subdirectory(audio/gui) +add_subdirectory(tests) # add the source files as usual add_executable(0_Bootstrap Bootstrap.cpp) diff --git a/Game.cpp b/Game.cpp index 41fc7fc..013cb3d 100644 --- a/Game.cpp +++ b/Game.cpp @@ -167,6 +167,127 @@ public: mSkyBoxGenParameters.skyBoxDistance = distance; } }; +class App; +class KeyboardListener : public OgreBites::InputListener { + App *mApp; + + uint32_t control; + ECS::Vector2 mouse; + float wheel_y; + bool mouse_moved, wheel_moved; + float mInitDelay; + +public: + Ogre::Timer fps_timer; + bool fast; + KeyboardListener(App *app) + : OgreBites::InputListener() + , mApp(app) + , fast(false) + , control(0) + , mouse({ 0, 0 }) + , wheel_y(0.0f) + , mouse_moved(false) + , wheel_moved(false) + , mInitDelay(1.0) + { + } + bool isGuiEnabled() + { + if (!ECS::get().has()) + return false; + return (ECS::get().get().enabled); + } + void setGuiEnabled(bool value) + { + if (!ECS::get().has()) + return; + ECS::get().get_mut().enabled = value; + ECS::get().modified(); + } + bool keyPressed(const OgreBites::KeyboardEvent &evt) override + { + bool updated = false; + if (isGuiEnabled()) + return false; + if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) { + OgreAssert(ECS::get().has(), ""); + setGuiEnabled(true); + if (ECS::get().has()) + ECS::get().setWindowGrab(false); + return true; + } + OgreBites::Keycode key = evt.keysym.sym; + 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; + if (key == 'w' || key == 'a' || key == 's' || key == 'd' || + key == OgreBites::SDLK_LSHIFT) + return true; + return false; + } + bool keyReleased(const OgreBites::KeyboardEvent &evt) override + { + OgreBites::Keycode key = evt.keysym.sym; + if (isGuiEnabled()) + return false; + 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; + if (key == 'w' || key == 'a' || key == 's' || key == 'd' || + key == OgreBites::SDLK_LSHIFT) + return true; + return false; + } + bool mouseMoved(const OgreBites::MouseMotionEvent &evt) override + { + if (isGuiEnabled()) + return false; + mouse.x = evt.xrel; + mouse.y = evt.yrel; + mouse_moved = true; + /* no special mouse handling */ + return true; + } + bool mouseWheelRolled(const OgreBites::MouseWheelEvent &evt) override + { + if (isGuiEnabled()) + return false; + /* no special mouse wheel handling */ + wheel_y = evt.y; + wheel_moved = true; + return true; + } + bool mousePressed(const OgreBites::MouseButtonEvent &evt) override + { + std::cout << "Mouse press " << (int)evt.button << " " + << (int)evt.clicks << "\n"; + if ((int)evt.button == 1) + control |= 256; + else + control &= ~256; + return false; + } + void update(float delta) + { + return; + } + void frameRendered(const Ogre::FrameEvent &evt) override; +}; class App : public OgreBites::ApplicationContext { std::unique_ptr mDynWorld; std::unique_ptr mDbgDraw; @@ -177,171 +298,8 @@ class App : public OgreBites::ApplicationContext { Ogre::Viewport *mViewport; SkyBoxRenderer *sky; bool mGrab; - class KeyboardListener : public OgreBites::InputListener, - public Ogre::FrameListener { - App *mApp; - - uint32_t control; - ECS::Vector2 mouse{ 0, 0 }; - float wheel_y; - bool mouse_moved = false, wheel_moved = false; - float mInitDelay; - - public: - Ogre::Timer fps_timer; - bool fast; - KeyboardListener(App *app) - : OgreBites::InputListener() - , Ogre::FrameListener() - , mApp(app) - , fast(false) - , control(0) - , mInitDelay(1.0) - { - } - bool isGuiEnabled() - { - if (!ECS::get().has()) - return false; - return (ECS::get().get().enabled); - } - void setGuiEnabled(bool value) - { - if (!ECS::get().has()) - return; - ECS::get().get_mut().enabled = value; - ECS::get().modified(); - } - bool keyPressed(const OgreBites::KeyboardEvent &evt) override - { - bool updated = false; - if (isGuiEnabled()) - return false; - if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) { - OgreAssert(ECS::get().has(), ""); - setGuiEnabled(true); - if (ECS::get().has()) - ECS::get().setWindowGrab( - false); - return true; - } - OgreBites::Keycode key = evt.keysym.sym; - 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; - if (key == 'w' || key == 'a' || key == 's' || - key == 'd' || key == OgreBites::SDLK_LSHIFT) - return true; - return false; - } - bool keyReleased(const OgreBites::KeyboardEvent &evt) override - { - OgreBites::Keycode key = evt.keysym.sym; - if (isGuiEnabled()) - return false; - 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; - if (key == 'w' || key == 'a' || key == 's' || - key == 'd' || key == OgreBites::SDLK_LSHIFT) - return true; - return false; - } - bool mouseMoved(const OgreBites::MouseMotionEvent &evt) override - { - if (isGuiEnabled()) - return false; - mouse.x = evt.xrel; - mouse.y = evt.yrel; - mouse_moved = true; - /* no special mouse handling */ - return true; - } - bool - mouseWheelRolled(const OgreBites::MouseWheelEvent &evt) override - { - if (isGuiEnabled()) - return false; - /* no special mouse wheel handling */ - wheel_y = evt.y; - wheel_moved = true; - return true; - } - bool - mousePressed(const OgreBites::MouseButtonEvent &evt) override - { - std::cout << "Mouse press " << (int)evt.button << " " - << (int)evt.clicks << "\n"; - if ((int)evt.button == 1) - control |= 256; - else - control &= ~256; - return false; - } - void update(float delta) - { - return; - } - void frameRendered(const Ogre::FrameEvent &evt) override - { - if (fps_timer.getMilliseconds() > 1000.0f) { - std::cout << "FPS: " - << mApp->getRenderWindow() - ->getStatistics() - .lastFPS - << " "; - std::cout << "Draw calls: " - << mApp->getRenderWindow() - ->getStatistics() - .batchCount - << " "; - fps_timer.reset(); - std::cout << "Drops: " - << mApp->getRenderWindow() - ->getStatistics() - .vBlankMissCount - << "\n"; - fps_timer.reset(); - } - update(evt.timeSinceLastFrame); - if (!isGuiEnabled() && mApp->isTerrainReady()) { - OgreAssert(mApp->isTerrainReady(), - "terrain is not ready"); - } - if (!isGuiEnabled()) { - mApp->updateWorld(evt.timeSinceLastFrame); - if (mInitDelay >= 0.0f) - mInitDelay -= evt.timeSinceLastFrame; - } - if (!isGuiEnabled() && ECS::get().has()) { - ECS::Input &input = - ECS::get().get_mut(); - input.control = control; - input.mouse = mouse; - mouse.x = 0; - mouse.y = 0; - input.wheel_y = wheel_y; - wheel_y = 0; - input.mouse_moved = mouse_moved; - input.wheel_moved = wheel_moved; - } - } - }; KeyboardListener mKbd; + bool enabldDbgDraw; public: App() @@ -350,6 +308,7 @@ public: , mDynWorld(new Ogre::Bullet::DynamicsWorld( Ogre::Vector3(0, -9.8, 0))) , mGrab(false) + , enabldDbgDraw(false) { } virtual ~App() @@ -439,11 +398,13 @@ public: .initialiseAllResourceGroups(); // OgreBites::ApplicationContext::loadResources(); // setupCursor(); - std::cout << "Create content" << "\n"; - createContent(); std::cout << "Setup input" << "\n"; setupInput(); + std::cout << "Create content" << "\n"; + createContent(); std::cout << "Setup done" << "\n"; + mDbgDraw->setDebugMode(mDbgDraw->getDebugMode() | + btIDebugDraw::DBG_DrawContactPoints); } Ogre::SceneManager *getSceneManager() { @@ -474,9 +435,10 @@ public: ECS::get().get_mut().grabChanged = false; ECS::get().modified(); } - ECS::update(delta); - // mDbgDraw->update(); + + if (enabldDbgDraw) + mDbgDraw->update(); } class InputListenerChainFlexible : public OgreBites::InputListener { protected: @@ -609,7 +571,30 @@ public: flecs::entity find_wait_gui; void setupInput() { - ECS::App &p = ECS::get().get_mut(); + } + void createContent() + { + int i; + sky = new SkyBoxRenderer(getSceneManager()); + bool drawFirst = true; + uint8_t renderQueue = drawFirst ? + Ogre::RENDER_QUEUE_SKIES_EARLY : + Ogre::RENDER_QUEUE_SKIES_LATE; + sky->create("Skybox/Dynamic", 450, renderQueue, + Ogre::Quaternion::IDENTITY, + Ogre::ResourceGroupManager:: + AUTODETECT_RESOURCE_GROUP_NAME); + sky->setEnabled(true); + + Ogre::MaterialPtr m = + Ogre::MaterialManager::getSingleton().getByName( + "Skybox/Dynamic", "General"); + OgreAssert(m, "Sky box material not found."); + m->load(); + ECS::setup(mScnMgr, mDynWorld.get(), mCameraNode, mCamera, + getRenderWindow()); + ECS::get().set( + { getRenderWindow(), getDisplayDPI() }); ECS::get() .observer("UpdateGrab") .event(flecs::OnSet) @@ -619,11 +604,6 @@ public: std::cout << "grab: " << gui.grab << "\n"; std::cout << "GUI enabled: " << gui.enabled << "\n"; - std::cout << "grabbed: " << isWindowGrab() - << "\n"; - std::cout - << "UI active: " << mKbd.isGuiEnabled() - << "\n"; }); ECS::get() .observer("UpdateInputListener") @@ -632,8 +612,9 @@ public: if (app.mInput) removeInputListener(app.mInput); delete app.mInput; - app.mInput = new OgreBites::InputListenerChain( - app.listeners); + app.mInput = + OGRE_NEW OgreBites::InputListenerChain( + app.listeners); addInputListener(app.mInput); std::cout << "Update listeners\n"; }); @@ -707,52 +688,10 @@ public: << "\n"; }); #endif - - p.listeners.clear(); - p.listeners.push_back(&mKbd); - ECS::get().modified(); - } - void createContent() - { - int i; - std::cout << "addFrameListener\n"; - getRoot()->addFrameListener(&mKbd); - sky = new SkyBoxRenderer(getSceneManager()); - bool drawFirst = true; - uint8_t renderQueue = drawFirst ? - Ogre::RENDER_QUEUE_SKIES_EARLY : - Ogre::RENDER_QUEUE_SKIES_LATE; - sky->create("Skybox/Dynamic", 450, renderQueue, - Ogre::Quaternion::IDENTITY, - Ogre::ResourceGroupManager:: - AUTODETECT_RESOURCE_GROUP_NAME); - sky->setEnabled(true); - - Ogre::MaterialPtr m = - Ogre::MaterialManager::getSingleton().getByName( - "Skybox/Dynamic", "General"); - OgreAssert(m, "Sky box material not found."); - m->load(); - ECS::setup(mScnMgr, mDynWorld.get(), mCameraNode, mCamera); - ECS::get() - .system("SetInputListener") - .kind(flecs::OnUpdate) - .each([this](const ECS::GUI &gui, ECS::App &app) { - if (gui.mGuiInpitListener && - app.listeners.size() == 1) { - app.listeners.clear(); - app.listeners.push_back( - gui.mGuiInpitListener); - app.listeners.push_back(&mKbd); - ECS::modified(); - } - }); - ECS::get().set( - { getRenderWindow(), getDisplayDPI() }); ECS::get().set( - { static_cast(this), + { initialiseImGui(), nullptr, - {} }); + { getImGuiInputListener(), &mKbd } }); Sound::setup(); Sound::ding(); } @@ -792,7 +731,51 @@ public: ECS::get().lookup("ECS::CharacterModule::player"); return player; } + void enableDbgDraw(bool enable) + { + enabldDbgDraw = enable; + } + bool isEnabledDbgDraw() const + { + return enabldDbgDraw; + } }; +void KeyboardListener::frameRendered(const Ogre::FrameEvent &evt) +{ + if (fps_timer.getMilliseconds() > 1000.0f) { + std::cout << "FPS: " + << mApp->getRenderWindow()->getStatistics().lastFPS + << " "; + std::cout << "Draw calls: " + << mApp->getRenderWindow()->getStatistics().batchCount + << " "; + fps_timer.reset(); + std::cout << "Drops: " + << mApp->getRenderWindow() + ->getStatistics() + .vBlankMissCount + << "\n"; + fps_timer.reset(); + } + update(evt.timeSinceLastFrame); + if (!isGuiEnabled()) { + mApp->updateWorld(evt.timeSinceLastFrame); + if (mInitDelay >= 0.0f) + mInitDelay -= evt.timeSinceLastFrame; + } + + if (!isGuiEnabled() && ECS::get().has()) { + ECS::Input &input = ECS::get().get_mut(); + input.control = control; + input.mouse = mouse; + mouse.x = 0; + mouse.y = 0; + input.wheel_y = wheel_y; + wheel_y = 0; + input.mouse_moved = mouse_moved; + input.wheel_moved = wheel_moved; + } +} int main() { @@ -803,7 +786,7 @@ int main() // register for input events // KeyHandler keyHandler; // ctx.addInputListener(&keyHandler); - + ctx.enableDbgDraw(false); ctx.getRoot()->startRendering(); ctx.setWindowGrab(false); ctx.closeApp(); diff --git a/Procedural.cpp b/Procedural.cpp index b5c592e..4e00d3d 100644 --- a/Procedural.cpp +++ b/Procedural.cpp @@ -141,7 +141,7 @@ public: cs->calculateLocalInertia(mass, inertia); } break; case Ogre::Bullet::CT_CAPSULE: { - cs = new btCompoundShape(); + cs = new btCompoundShape(false); btScalar height = 1.0f; btScalar radius = 0.3f; shape = new btCapsuleShape(radius, @@ -377,7 +377,7 @@ void CharacterController::setupBody() // mRigidBody = world->addCharacter(mBodyEnt, 0); // mCollisionShape = static_cast(mRigidBody->getCollisionShape()); mGhostObject = new btPairCachingGhostObject(); - mCollisionShape = new btCompoundShape; + mCollisionShape = new btCompoundShape(false); mGhostObject->setCollisionShape(mCollisionShape); { diff --git a/src/gamedata/CharacterModule.cpp b/src/gamedata/CharacterModule.cpp index aac9492..b476581 100644 --- a/src/gamedata/CharacterModule.cpp +++ b/src/gamedata/CharacterModule.cpp @@ -4,28 +4,31 @@ #include "GameData.h" #include "CharacterModule.h" #include "WaterModule.h" +#include "TerrainModule.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", 0.0f, nullptr, nullptr, nullptr }); - player.set( - { nullptr, nullptr, nullptr, { 0, 0, 0 }, false, false }); - player.add(); - player.add(); + ecs.component(); + ecs.component(); + ecs.component(); + ecs.component(); + ecs.component().on_add( + [](flecs::entity e, CharacterBody &body) { + e.set( + { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }); + body.checkGround = false; + body.checkGroundResult = false; + body.mCollisionShape = nullptr; + body.mGhostObject = nullptr; + body.mController = nullptr; + }); ecs.system("UpdateTimer") .kind(flecs::OnUpdate) .each([this](EngineData &eng, CharacterBase &ch) { ch.mTimer += eng.delta; - if (eng.startupDelay >= 0.0f) - eng.startupDelay -= eng.delta; }); ecs.system("HandleInput") .kind(flecs::OnUpdate) @@ -38,6 +41,8 @@ CharacterModule::CharacterModule(flecs::world &ecs) uint32_t active = input.control; float zaxis = input.motion.z; zaxis *= 0.9f; + if (!camera.mCameraPivot || !camera.mCameraGoal) + return; if (pressed & 1) std::cout << "W pressed\n"; if (released & 1) @@ -181,25 +186,15 @@ CharacterModule::CharacterModule(flecs::world &ecs) return; ch.mBoneMotion = ch.mRootBone->getPosition(); }); - ecs.system("HandleRootMotion") + ecs.system( + "HandleGravity") .kind(flecs::OnUpdate) + .with() + .with() .each([this](flecs::entity e, const EngineData &eng, - 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; + const CharacterBase &ch, CharacterVelocity &gr) { Ogre::Vector3 gravity(0, -9.8f, 0); + Ogre::Vector3 pos = ch.mBodyNode->getPosition(); if (e.has()) { float volume = 2.0f * 0.5f * 0.5f; float density = 900.0f; @@ -216,33 +211,80 @@ CharacterModule::CharacterModule(flecs::world &ecs) Ogre::Vector3 b = -gravity * density * volume * multiplier * current_subm / full_subm / mass; - body.gvelocity += (gravity + b) * delta; - body.gvelocity.y = Ogre::Math::Clamp( - body.gvelocity.y, -2.5f, 2.5f); - } else - body.gvelocity += gravity * delta; + gr.gvelocity += (gravity + b) * eng.delta; + gr.gvelocity.y = Ogre::Math::Clamp( + gr.gvelocity.y, -2.5f, 2.5f); + } else { + gr.gvelocity += gravity * eng.delta; + if (pos.y < -1.2) { + gr.gvelocity.y = 0.0f; + } + } + gr.gvelocity *= 0.99; + }); + ecs.system( + "HandleRootMotionVelocity") + .kind(flecs::OnUpdate) + .with() + .with() + .each([this](flecs::entity e, const EngineData &eng, + const CharacterBase &ch, CharacterVelocity &v) { + if (eng.delta < 0.0000001f) + return; + if (!ch.mBodyNode) + return; + Ogre::Quaternion rot = ch.mBodyNode->getOrientation(); + Ogre::Vector3 pos = ch.mBodyNode->getPosition(); + Ogre::Vector3 boneMotion = ch.mBoneMotion; + v.velocity = rot * boneMotion / eng.delta; + if (eng.startupDelay < 0.0f) + v.velocity += v.gvelocity; + v.velocity.y = Ogre::Math::Clamp(v.velocity.y, -10.5f, + 1000000.0f); + }); + ecs.system("HandleRootMotion") + .kind(flecs::OnUpdate) + .each([this](flecs::entity e, const EngineData &eng, + CharacterBase &ch, CharacterBody &body, + AnimationControl &anim, CharacterVelocity &v) { + if (!ch.mBodyNode) + return; + if (eng.delta < 0.0000001f) + return; + OgreAssert(eng.delta > 0.0f, "Zero delta"); + int maxPen = 0; + Ogre::Vector3 colNormal; + bool is_on_floor = false; + bool penetration = false; if (eng.startupDelay < 0.0f) { - body.gvelocity *= 0.99; - 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); + if (body.mController) { + Ogre::Vector3 rotMotion = + v.velocity * eng.delta; + btVector3 currentPosition = + body.mGhostObject + ->getWorldTransform() + .getOrigin(); + is_on_floor = + body.mController->isOnFloor(); + penetration = body.mController + ->isPenetrating(); + if (is_on_floor) + v.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); + 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") @@ -289,6 +331,7 @@ CharacterModule::CharacterModule(flecs::world &ecs) .each([](const EngineData &eng, CharacterBase &ch, CharacterBody &body) { if (!ch.mBodyNode) { + body.mController = nullptr; ch.mBodyEnt = eng.mScnMgr->createEntity( "normal-male.glb"); ch.mBodyNode = eng.mScnMgr->getRootSceneNode() @@ -297,7 +340,8 @@ CharacterModule::CharacterModule(flecs::world &ecs) ch.mSkeleton = ch.mBodyEnt->getSkeleton(); body.mGhostObject = new btPairCachingGhostObject(); - body.mCollisionShape = new btCompoundShape; + body.mCollisionShape = + new btCompoundShape(false); body.mGhostObject->setCollisionShape( body.mCollisionShape); { @@ -333,16 +377,8 @@ CharacterModule::CharacterModule(flecs::world &ecs) 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"), @@ -351,11 +387,31 @@ CharacterModule::CharacterModule(flecs::world &ecs) OgreAssert(ch.mRootBone, "No root bone"); } }); + ecs.system( + "UpdateCharacterPhysics") + .kind(flecs::OnUpdate) + .with() + .with() + .with() + .each([](const EngineData &eng, CharacterBase &ch, + CharacterBody &body) { + if (ch.mBodyNode && !body.mController && + eng.startupDelay < 0.0f) { + body.mController = + new Ogre::Bullet::KinematicMotionSimple( + body.mGhostObject, + ch.mBodyNode); + eng.mWorld->getBtWorld()->addAction( + body.mController); + OgreAssert(body.mController, "Need controller"); + } + }); #define CAM_HEIGHT 1.6f // height of camera above character's center of mass ecs.system( "UpdateCamera") .kind(flecs::OnUpdate) .with() + .with() .each([](const EngineData &eng, Camera &camera, const CharacterBase &ch) { float delta = eng.delta; @@ -475,8 +531,10 @@ CharacterModule::CharacterModule(flecs::world &ecs) ecs.system("CheckGround") .kind(flecs::OnUpdate) .with() + .with() + .without() .each([](const EngineData &eng, CharacterBody &body) { - if (body.checkGround) { + if (body.mGhostObject) { btVector3 from = body.mGhostObject->getWorldTransform() .getOrigin() + @@ -488,7 +546,8 @@ CharacterModule::CharacterModule(flecs::world &ecs) resultCallback); body.checkGroundResult = resultCallback.hasHit(); - body.checkGround = false; + if (resultCallback.hasHit()) + ECS::get().add(); } }); ecs.system( @@ -498,8 +557,7 @@ CharacterModule::CharacterModule(flecs::world &ecs) .without() .each([](flecs::entity e, const WaterBody &waterb, const CharacterBase &ch, CharacterBody &body) { - if (waterb.mInWater.find(body.mGhostObject) != - waterb.mInWater.end() && + if (waterb.isInWater(body.mGhostObject) && ch.mBodyNode->_getDerivedPosition().y < -0.05f) { e.add(); std::cout << "Big Splash\n"; @@ -518,8 +576,7 @@ CharacterModule::CharacterModule(flecs::world &ecs) .with() .each([](flecs::entity e, const WaterBody &waterb, const CharacterBase &ch, CharacterBody &body) { - if (waterb.mInWater.find(body.mGhostObject) == - waterb.mInWater.end() && + if (waterb.isInWater(body.mGhostObject) && ch.mBodyNode->_getDerivedPosition().y > 0.05f) e.remove(); }); @@ -535,6 +592,16 @@ CharacterModule::CharacterModule(flecs::world &ecs) << "\n"; }); #endif + /* Create player */ + player = ecs.entity("player"); + player.set({ AnimationControl::ANIM_NONE, + AnimationControl::ANIM_NONE, false, + false }); + player.set( + { "normal-male.glb", 0.0f, nullptr, nullptr, nullptr }); + player.set({ nullptr, nullptr, nullptr, false, false }); + player.add(); + player.add(); } void CharacterModule::setAnimation(AnimationControl &anim) diff --git a/src/gamedata/CharacterModule.h b/src/gamedata/CharacterModule.h index 18607c3..62c0960 100644 --- a/src/gamedata/CharacterModule.h +++ b/src/gamedata/CharacterModule.h @@ -21,10 +21,13 @@ struct CharacterBody { btPairCachingGhostObject *mGhostObject; btCompoundShape *mCollisionShape; Ogre::Bullet::KinematicMotionSimple *mController; - Ogre::Vector3 gvelocity; bool checkGround; bool checkGroundResult; }; +struct CharacterVelocity { + Ogre::Vector3 gvelocity; + Ogre::Vector3 velocity; +}; struct AnimationControl { enum AnimID { ANIM_IDLE = 0, diff --git a/src/gamedata/Components.h b/src/gamedata/Components.h index 17d1105..62df8d9 100644 --- a/src/gamedata/Components.h +++ b/src/gamedata/Components.h @@ -2,6 +2,10 @@ #define COMPONENTS_H_ #include #include +namespace Ogre +{ +class ImGuiOverlay; +} namespace OgreBites { class ApplicationContextSDL; @@ -18,6 +22,8 @@ struct EngineData { Ogre::Bullet::DynamicsWorld *mWorld; float delta; float startupDelay; + int width; + int height; }; struct Vector3 { float x; @@ -58,10 +64,13 @@ struct RenderWindow { float dpi; }; struct App { - OgreBites::ApplicationContextSDL *app; + Ogre::ImGuiOverlay *mGuiOverlay; OgreBites::InputListenerChain *mInput; std::vector listeners; }; struct InWater {}; +struct TerrainReady {}; +struct WaterReady {}; +struct GroundCheckReady {}; } #endif \ No newline at end of file diff --git a/src/gamedata/GUIModule.cpp b/src/gamedata/GUIModule.cpp index cdfd0c6..fb89a91 100644 --- a/src/gamedata/GUIModule.cpp +++ b/src/gamedata/GUIModule.cpp @@ -144,6 +144,27 @@ struct GUIListener : public Ogre::RenderTargetListener { { int i; Ogre::ImGuiOverlay::NewFrame(); + if (ECS::get().get().startupDelay > 0.0f) { + ImVec2 size = ImGui::GetMainViewport()->Size; + ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always); + ImGui::SetNextWindowSize(ImVec2(size.x, size.y), + ImGuiCond_Always); + ImGui::Begin( + "StartupScreen", nullptr, + ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoDecoration | + + ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoCollapse | + ImGuiWindowFlags_NoFocusOnAppearing | + ImGuiWindowFlags_NoInputs); + // if (ECS::get().get().enabled) + // ECS::get().get().app->setWindowGrab(true); + ImGui::Text( + "This game does not autosave. Please use save function to keep your state"); + ImGui::End(); + } if (ECS::get().get().enabled) { buttons_panel(); buildings_editor(); @@ -258,9 +279,21 @@ struct GUIListener : public Ogre::RenderTargetListener { GUIModule::GUIModule(flecs::world &ecs) { - ecs.component().add(flecs::Singleton); - ecs.component().add(flecs::Singleton); - ecs.set({ false, true, false, nullptr }); + ecs.component() + .on_add([](GUI &gui) { + gui.enabled = false; + gui.grab = false; + gui.grabChanged = false; + }) + .add(flecs::Singleton); + ecs.component() + .on_add([](GUIData &priv) { + priv.glb_names.clear(); + priv.mGUIListener = nullptr; + priv.mGuiOverlay = nullptr; + }) + .add(flecs::Singleton); + ecs.set({ false, true, false }); ecs.set({ nullptr, {}, nullptr }); ui_wait = ecs.system("SetupGUI") @@ -275,8 +308,9 @@ GUIModule::GUIModule(flecs::world &ecs) Ogre::OverlayManager::getSingleton() .setPixelRatio(vpScale); std::cout << "GUI configure\n"; - gui.mGuiOverlay = - app.app->initialiseImGui(); + OgreAssert(app.mGuiOverlay, + "No ImGUI overlay"); + gui.mGuiOverlay = app.mGuiOverlay; gui.mGuiOverlay->setZOrder(300); gui.mGuiOverlay->show(); gui.mGUIListener = new GUIListener(); @@ -299,10 +333,6 @@ GUIModule::GUIModule(flecs::world &ecs) names.begin(), names.end()); } - ECS::get_mut() - .mGuiInpitListener = - new OgreBites:: - ImGuiInputListener(); ECS::modified(); std::cout << "GUI configure finished\n"; } diff --git a/src/gamedata/GUIModule.h b/src/gamedata/GUIModule.h index e0429e7..fdd5a78 100644 --- a/src/gamedata/GUIModule.h +++ b/src/gamedata/GUIModule.h @@ -3,7 +3,6 @@ #include namespace OgreBites { -class InputListener; } namespace ECS { @@ -11,7 +10,6 @@ struct GUI { bool enabled; bool grab; bool grabChanged; - OgreBites::InputListener *mGuiInpitListener; static void setWindowGrab(bool g = true) { ECS::GUI &gui = ECS::get().get_mut(); diff --git a/src/gamedata/GameData.cpp b/src/gamedata/GameData.cpp index 23dc6d2..0055670 100644 --- a/src/gamedata/GameData.cpp +++ b/src/gamedata/GameData.cpp @@ -12,7 +12,8 @@ namespace ECS { static flecs::world ecs; void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world, - Ogre::SceneNode *cameraNode, Ogre::Camera *camera) + Ogre::SceneNode *cameraNode, Ogre::Camera *camera, + Ogre::RenderWindow *window) { std::cout << "Setup GameData\n"; ecs.component().add(flecs::Singleton); @@ -20,23 +21,60 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world, ecs.component().add(flecs::Singleton); ecs.component().add(flecs::Singleton); ecs.component(); - ecs.component().add(flecs::Singleton); + ecs.component().add(flecs::Singleton); + ecs.component().add(flecs::Singleton); + ecs.component() + .on_add([](App &app) { + app.mInput = nullptr; + app.mGuiOverlay = nullptr; + app.listeners.clear(); + }) + .add(flecs::Singleton); + /* lots of things depend on it */ + ecs.component().add(flecs::Singleton); ecs.import (); ecs.import (); - ecs.import (); ecs.import (); + ecs.import (); ecs.import (); ecs.system("UpdateDelta") .kind(flecs::OnUpdate) .each([](EngineData &eng) { eng.delta = ECS::get().delta_time(); }); - ecs.set({ scnMgr, world, 0.0f, 0.0f }); + ecs.system("UpdateDelay") + .kind(flecs::OnUpdate) + .with() + .with() + .with() + .each([](EngineData &eng) { + if (eng.startupDelay >= 0.0f) + eng.startupDelay -= eng.delta; +#ifdef VDEBUG + if (ECS::get().has()) + std::cout << "ground check ready\n"; +#endif + }); + ecs.system("CheckStatus") + .kind(flecs::OnUpdate) + .run([](flecs::iter &it) { +#ifdef VDEBUG + if (ECS::get().has()) + std::cout << "water ready\n"; + if (ECS::get().has()) + std::cout << "terrain ready\n"; + if (ECS::get().has()) + std::cout << "ground check ready\n"; +#endif + }); + ecs.set({ scnMgr, world, 0.0f, 5.0f, + (int)window->getWidth(), + (int)window->getHeight() }); ecs.set({ cameraNode, camera, false }); ecs.add(); ecs.add(); - ecs.set({ nullptr, nullptr, nullptr, nullptr, nullptr }); - ecs.set({ nullptr }); + ecs.add(); + ecs.set({ nullptr, nullptr, nullptr, nullptr }); ecs.set({ nullptr, nullptr, nullptr, diff --git a/src/gamedata/GameData.h b/src/gamedata/GameData.h index d6186d7..31d0a3b 100644 --- a/src/gamedata/GameData.h +++ b/src/gamedata/GameData.h @@ -5,7 +5,8 @@ namespace ECS { void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world, - Ogre::SceneNode *cameraNode, Ogre::Camera *camera); + Ogre::SceneNode *cameraNode, Ogre::Camera *camera, + Ogre::RenderWindow *window); void update(float delta); flecs::world get(); template const T &get() diff --git a/src/gamedata/SunModule.cpp b/src/gamedata/SunModule.cpp index 1ae3a4a..a9f656a 100644 --- a/src/gamedata/SunModule.cpp +++ b/src/gamedata/SunModule.cpp @@ -7,7 +7,6 @@ namespace ECS SunModule::SunModule(flecs::world &ecs) { ecs.component().add(flecs::Singleton); - ecs.set({ nullptr, nullptr, nullptr, nullptr }); ecs.system("UpdateSetupSun") .kind(flecs::OnUpdate) .each([](const EngineData &eng, Sun &sun) { diff --git a/src/gamedata/TerrainModule.cpp b/src/gamedata/TerrainModule.cpp index e214d05..0ec1fcb 100644 --- a/src/gamedata/TerrainModule.cpp +++ b/src/gamedata/TerrainModule.cpp @@ -237,11 +237,14 @@ public: float maxH = terrain->getMaxHeight(); int size = terrain->getSize(); float worldSize = terrain->getWorldSize(); - if (!created || true) { + if (true) { btRigidBody *body = mWorld->addTerrainRigidBody( group, x, y, 2, 0x7ffffffd & (~16)); + OgreAssert( + body, + "Could not create RigidBody"); Ogre::LogManager::getSingleton().logError( "created rigid body " + Ogre::StringConverter::toString( @@ -278,6 +281,11 @@ public: output.push_back(collider_queue.front()); collider_queue.pop_front(); } + if (collider_queue.empty() && + !ECS::get().mTerrainReady) { + ECS::get_mut().mTerrainReady = true; + ECS::modified(); + } } collider_queue = output; } @@ -328,7 +336,7 @@ TerrainModule::TerrainModule(flecs::world &ecs) .each([](const EngineData &eng, const Camera &camera, const Sun &sun, Terrain &terrain, TerrainPrivate &priv) { - if (!terrain.mTerrainGroup) { + if (!terrain.mTerrainGroup && sun.mSun && eng.mScnMgr) { std::cout << "Terrain setup\n"; if (!priv.mDummyPageProvider) priv.mDummyPageProvider = @@ -338,6 +346,9 @@ TerrainModule::TerrainModule(flecs::world &ecs) terrain.mTerrainGlobals = OGRE_NEW Ogre::TerrainGlobalOptions(); + OgreAssert(terrain.mTerrainGlobals, + "Failed to allocate global options"); + Ogre::LogManager::getSingleton().setMinLogLevel( Ogre::LML_TRIVIAL); @@ -423,47 +434,14 @@ TerrainModule::TerrainModule(flecs::world &ecs) terrain.mTerrainGroup->freeTemporaryResources(); std::cout << "Terrain setup done\n"; } - bool playerCheck = false; - ECS::get() - .query_builder() - .with() - .with() - .each([&playerCheck, - terrain](flecs::entity e, - CharacterBase &ch, - CharacterBody &body) { - if (!body.checkGround) - body.checkGround = true; - if (ch.mBodyNode && - body.checkGroundResult) { - long x, y; - Ogre::Vector3 pos = - ch.mBodyNode - ->getPosition(); - terrain.mTerrainGroup - ->convertWorldPositionToTerrainSlot( - pos, &x, &y); - if (terrain.mTerrainGroup - ->getTerrain(x, - y) && - terrain.mTerrainGroup - ->getTerrain(x, y) - ->isLoaded()) - playerCheck = true; - } - }); - if (playerCheck) - terrain.mTerrainReady = true; - if (priv.mSunUpdate.getMilliseconds() > 1000) { - Ogre::TerrainGlobalOptions::getSingleton() - .setCompositeMapAmbient( - eng.mScnMgr->getAmbientLight()); - Ogre::TerrainGlobalOptions::getSingleton() - .setCompositeMapDiffuse( - sun.mSun->getDiffuseColour()); - Ogre::TerrainGlobalOptions::getSingleton() - .setLightMapDirection( - sun.mSun->getDerivedDirection()); + if (sun.mSun && + priv.mSunUpdate.getMilliseconds() > 1000) { + terrain.mTerrainGlobals->setCompositeMapAmbient( + eng.mScnMgr->getAmbientLight()); + terrain.mTerrainGlobals->setCompositeMapDiffuse( + sun.mSun->getDiffuseColour()); + terrain.mTerrainGlobals->setLightMapDirection( + sun.mSun->getDerivedDirection()); std::cout << "sun pitch: " << sun.mSunNode->getOrientation() .getPitch() @@ -471,5 +449,24 @@ TerrainModule::TerrainModule(flecs::world &ecs) priv.mSunUpdate.reset(); } }); + ecs.system("UpdateTerrainStatus") + .kind(flecs::OnUpdate) + .without() + .each([](const CharacterBase &ch, const Terrain &terrain) { + std::cout << "mTerrainReady: " << terrain.mTerrainReady + << "\n"; + std::cout << "mBodyNode: " << ch.mBodyNode << "\n"; + if (ch.mBodyNode && terrain.mTerrainReady) { + long x, y; + Ogre::Vector3 pos = ch.mBodyNode->getPosition(); + terrain.mTerrainGroup + ->convertWorldPositionToTerrainSlot( + pos, &x, &y); + if (terrain.mTerrainGroup->getTerrain(x, y) && + terrain.mTerrainGroup->getTerrain(x, y) + ->isLoaded()) + ECS::get().add(); + } + }); } } \ No newline at end of file diff --git a/src/gamedata/WaterModule.cpp b/src/gamedata/WaterModule.cpp index b10ce9e..19c0e46 100644 --- a/src/gamedata/WaterModule.cpp +++ b/src/gamedata/WaterModule.cpp @@ -4,150 +4,194 @@ #include #include #include +#include +#include +#include #include "GameData.h" #include "Components.h" #include "WaterModule.h" namespace ECS { +class WaterPhysicsAction : public btActionInterface { + btPairCachingGhostObject *mWaterBody; + btManifoldArray mManifoldArray; + +public: + btVector3 mShapeAabbMin, mShapeAabbMax; + btGjkEpaPenetrationDepthSolver *gjk_epa_pen_solver; + btVoronoiSimplexSolver *gjk_simplex_solver; + struct contact { + const btCollisionObject *body; + float mDistance; + btVector3 mNormal; + btVector3 mPoint; + bool mHasResult; + }; + std::vector mContacts; + std::set mInWater; + WaterPhysicsAction(btPairCachingGhostObject *waterBody) + : btActionInterface() + , mWaterBody(waterBody) + , mShapeAabbMin(0, 0, 0) + , mShapeAabbMax(0, 0, 0) + , gjk_epa_pen_solver(OGRE_NEW btGjkEpaPenetrationDepthSolver) + , gjk_simplex_solver(OGRE_NEW btVoronoiSimplexSolver) + { + mManifoldArray.reserve(160); + mManifoldArray.resize(0); + setupBody(); + } + ~WaterPhysicsAction() + { + } + void updateAction(btCollisionWorld *collisionWorld, + btScalar deltaTimeStep) override; + void debugDraw(btIDebugDraw *debugDrawer) override; + void setupBody(); + bool isInWater(const btCollisionObject *body) const + { + return (mInWater.find(body) != mInWater.end()); + } +}; 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.system("UpdateWater") - .kind(flecs::OnUpdate) - .each([](const EngineData &eng, const Camera &camera, - WaterSurface &water) { - float delta = eng.delta; - if (!water.mWaterEnt || !water.mWaterNode) { - std::cout << "Water setup\n"; - water.mDepthMaterial = - Ogre::MaterialManager::getSingleton() - .getByName("Water/Depth"); - OgreAssert(water.mDepthMaterial, - "Water Depth material not found."); - water.mDepthMaterial->load(); - water.mDepthTech = - water.mDepthMaterial->getBestTechnique(); - OgreAssert(water.mDepthTech, - "Bad material technique"); - water.mAbove = false; - water.mInRefTexUpdate = false; - water.mRenderTargetListener.mSurface = &water; - const Ogre::String renderTargetName = - "ReflectionRefractionTexture"; - water.mWaterPlane = - Ogre::Plane(Ogre::Vector3::UNIT_Y, 0); - water.mReflectionPlane = Ogre::Plane( - Ogre::Vector3(0.0, 1.0, 0.0), - 0.5f /* water height */); - float h = 0.0f; - water.mReflectionClipPlaneAbove = Ogre::Plane( - Ogre::Vector3(0.0, 1.0, 0.0), -h); - water.mReflectionClipPlaneBelow = Ogre::Plane( - Ogre::Vector3(0.0, -1.0, 0.0), h); - water.mRefractionClipPlaneAbove = Ogre::Plane( - Ogre::Vector3(0.0, -1.0, 0.0), h); - water.mRefractionClipPlaneBelow = Ogre::Plane( - Ogre::Vector3(0.0, 1.0, 0.0), -h); + ecs.component() + .on_add([](flecs::entity e, WaterSurface &water) { + ECS::get().add(); + water.mAbove = false; + water.mDepthMaterial = nullptr; + water.mDepthTech = nullptr; + water.mInRefTexUpdate = false; + water.mReflectionCamera = nullptr; + water.mReflectionCameraNode = nullptr; + water.mReflectionDepthCamera = nullptr; + water.mReflectionTexture = nullptr; + water.mRefractionCamera = nullptr; + water.mRefractionCameraNode = nullptr; + water.mRefractionDepthCamera = nullptr; + water.mWaterEnt = nullptr; + water.mWaterNode = nullptr; + + const EngineData &eng = ECS::get(); + const Camera &camera = ECS::get(); + + std::cout << "Water setup\n"; + water.mDepthMaterial = + Ogre::MaterialManager::getSingleton().getByName( + "Water/Depth"); + OgreAssert(water.mDepthMaterial, + "Water Depth material not found."); + water.mDepthMaterial->load(); + water.mDepthTech = + water.mDepthMaterial->getBestTechnique(); + OgreAssert(water.mDepthTech, "Bad material technique"); + water.mAbove = false; + water.mInRefTexUpdate = false; + water.mRenderTargetListener.mSurface = &water; + const Ogre::String renderTargetName = + "ReflectionRefractionTexture"; + water.mWaterPlane = + Ogre::Plane(Ogre::Vector3::UNIT_Y, 0); + water.mReflectionPlane = + Ogre::Plane(Ogre::Vector3(0.0, 1.0, 0.0), + 0.5f /* water height */); + float h = 0.0f; + water.mReflectionClipPlaneAbove = + Ogre::Plane(Ogre::Vector3(0.0, 1.0, 0.0), -h); + water.mReflectionClipPlaneBelow = + Ogre::Plane(Ogre::Vector3(0.0, -1.0, 0.0), h); + water.mRefractionClipPlaneAbove = + Ogre::Plane(Ogre::Vector3(0.0, -1.0, 0.0), h); + water.mRefractionClipPlaneBelow = + Ogre::Plane(Ogre::Vector3(0.0, 1.0, 0.0), -h); #if 0 if (Ogre::TextureManager::getSingleton() .resourceExists(renderTargetName)) Ogre::TextureManager::getSingleton() .remove(renderTargetName); #endif - Ogre::TexturePtr reflectionTexture = - Ogre::TextureManager::getSingleton().createManual( - renderTargetName, - Ogre::ResourceGroupManager:: - DEFAULT_RESOURCE_GROUP_NAME, - Ogre::TEX_TYPE_2D, 512, 512, 0, - Ogre::PF_R8G8B8A8, - Ogre::TU_RENDERTARGET); - Ogre::MaterialPtr debug_mat = + Ogre::TexturePtr reflectionTexture = + Ogre::TextureManager::getSingleton().createManual( + renderTargetName, + Ogre::ResourceGroupManager:: + DEFAULT_RESOURCE_GROUP_NAME, + Ogre::TEX_TYPE_2D, 512, 512, 0, + Ogre::PF_R8G8B8A8, + Ogre::TU_RENDERTARGET); + Ogre::MaterialPtr debug_mat = + Ogre::MaterialManager::getSingleton().getByName( + "Water/Debug", "Water"); + if (!debug_mat) { + debug_mat = Ogre::MaterialManager::getSingleton() - .getByName("Water/Debug", - "Water"); - if (!debug_mat) { - debug_mat = - Ogre::MaterialManager::getSingleton() - .create("Water/Debug", - "Water"); - Ogre::Technique *tech = - debug_mat->getTechnique(0); - Ogre::Pass *pass = tech->getPass(0); - pass->setLightingEnabled(false); - pass->setAmbient( - Ogre::ColourValue(1, 1, 1, 1)); - pass->setDiffuse(Ogre::ColourValue( - 0.0f, 0.2f, 0.5f, 1.0f)); - pass->setDepthCheckEnabled(false); - pass->setDepthWriteEnabled(false); - pass->setAlphaRejectFunction( - Ogre::CMPF_ALWAYS_PASS); - pass->setCullingMode(Ogre::CULL_NONE); - pass->setManualCullingMode( - Ogre::MANUAL_CULL_NONE); - Ogre::TextureUnitState *texture_unit = - pass->createTextureUnitState(); - texture_unit->setTextureName( - "ReflectionRefractionTexture"); - Ogre::Sampler::UVWAddressingMode uvw; - uvw.u = Ogre::TextureUnitState::TAM_MIRROR; - uvw.v = Ogre::TextureUnitState::TAM_MIRROR; - uvw.w = Ogre::TextureUnitState::TAM_MIRROR; - texture_unit->setTextureAddressingMode( - uvw); - texture_unit->setTextureFiltering( - Ogre::FT_MIN, Ogre::FO_LINEAR); - texture_unit->setTextureFiltering( - Ogre::FT_MAG, Ogre::FO_LINEAR); - texture_unit->setTextureFiltering( - Ogre::FT_MIP, Ogre::FO_LINEAR); - } - // create a frosted screen in front of the camera, using our dynamic texture to "thaw" certain areas - Ogre::Entity *ent = eng.mScnMgr->createEntity( - "WaterDebugPlane", - Ogre::SceneManager::PT_PLANE); - ent->setMaterialName("Water/Debug", "Water"); - ent->setVisibilityFlags(WATER_MASK); - Ogre::SceneNode *node = - camera.mCameraNode - ->createChildSceneNode(); - node->setPosition(-150, 60, -400); - node->attachObject(ent); + .create("Water/Debug", "Water"); + Ogre::Technique *tech = + debug_mat->getTechnique(0); + Ogre::Pass *pass = tech->getPass(0); + pass->setLightingEnabled(false); + pass->setAmbient(Ogre::ColourValue(1, 1, 1, 1)); + pass->setDiffuse(Ogre::ColourValue(0.0f, 0.2f, + 0.5f, 1.0f)); + pass->setDepthCheckEnabled(false); + pass->setDepthWriteEnabled(false); + pass->setAlphaRejectFunction( + Ogre::CMPF_ALWAYS_PASS); + pass->setCullingMode(Ogre::CULL_NONE); + pass->setManualCullingMode( + Ogre::MANUAL_CULL_NONE); + Ogre::TextureUnitState *texture_unit = + pass->createTextureUnitState(); + texture_unit->setTextureName( + "ReflectionRefractionTexture"); + Ogre::Sampler::UVWAddressingMode uvw; + uvw.u = Ogre::TextureUnitState::TAM_MIRROR; + uvw.v = Ogre::TextureUnitState::TAM_MIRROR; + uvw.w = Ogre::TextureUnitState::TAM_MIRROR; + texture_unit->setTextureAddressingMode(uvw); + texture_unit->setTextureFiltering( + Ogre::FT_MIN, Ogre::FO_LINEAR); + texture_unit->setTextureFiltering( + Ogre::FT_MAG, Ogre::FO_LINEAR); + texture_unit->setTextureFiltering( + Ogre::FT_MIP, Ogre::FO_LINEAR); + } + // create a frosted screen in front of the camera, using our dynamic texture to "thaw" certain areas + Ogre::Entity *ent = eng.mScnMgr->createEntity( + "WaterDebugPlane", + Ogre::SceneManager::PT_PLANE); + ent->setMaterialName("Water/Debug", "Water"); + ent->setVisibilityFlags(WATER_MASK); + Ogre::SceneNode *node = + camera.mCameraNode->createChildSceneNode(); + node->setPosition(-150, 60, -400); + node->attachObject(ent); - water.mReflectionTexture = - reflectionTexture->getBuffer() - ->getRenderTarget(); - water.mReflectionTexture->setAutoUpdated(false); - water.mWaterNode = - eng.mScnMgr->getRootSceneNode() - ->createChildSceneNode("Water"); - Ogre::MaterialPtr mat = - Ogre::MaterialManager::getSingleton() - .getByName("Water/Above", - "Water"); - if (!mat) { - mat = Ogre::MaterialManager::getSingleton() - .create("Water/Above", - "Water"); - Ogre::Technique *tech = - mat->getTechnique(0); - Ogre::Pass *pass = tech->getPass(0); - pass->setLightingEnabled(true); - pass->setAmbient( - Ogre::ColourValue(1, 1, 1, 1)); - pass->setDiffuse(Ogre::ColourValue( - 0.0f, 0.2f, 0.5f, 1.0f)); - pass->setCullingMode(Ogre::CULL_NONE); - pass->setManualCullingMode( - Ogre::MANUAL_CULL_NONE); - pass->setVertexProgram( - "Water/water_vp"); - pass->setFragmentProgram( - "Water/water_fp"); + water.mReflectionTexture = + reflectionTexture->getBuffer() + ->getRenderTarget(); + water.mReflectionTexture->setAutoUpdated(false); + water.mWaterNode = + eng.mScnMgr->getRootSceneNode() + ->createChildSceneNode("Water"); + Ogre::MaterialPtr mat = + Ogre::MaterialManager::getSingleton().getByName( + "Water/Above", "Water"); + if (!mat) { + mat = Ogre::MaterialManager::getSingleton() + .create("Water/Above", "Water"); + Ogre::Technique *tech = mat->getTechnique(0); + Ogre::Pass *pass = tech->getPass(0); + pass->setLightingEnabled(true); + pass->setAmbient(Ogre::ColourValue(1, 1, 1, 1)); + pass->setDiffuse(Ogre::ColourValue(0.0f, 0.2f, + 0.5f, 1.0f)); + pass->setCullingMode(Ogre::CULL_NONE); + pass->setManualCullingMode( + Ogre::MANUAL_CULL_NONE); + pass->setVertexProgram("Water/water_vp"); + pass->setFragmentProgram("Water/water_fp"); #if 0 Ogre::GpuProgramPtr water_vp = Ogre::GpuProgramManager::getSingleton() @@ -206,34 +250,31 @@ WaterModule::WaterModule(flecs::world &ecs) Ogre::GpuProgramParametersSharedPtr paramsFP = water_fp->getDefaultParameters(); #endif - Ogre::TextureUnitState *texture_unit = - pass->createTextureUnitState(); - texture_unit->setTextureName( - "ReflectionRefractionTexture"); - Ogre::Sampler::UVWAddressingMode uvw; - uvw.u = Ogre::TextureUnitState::TAM_MIRROR; - uvw.v = Ogre::TextureUnitState::TAM_MIRROR; - uvw.w = Ogre::TextureUnitState::TAM_MIRROR; - texture_unit->setTextureAddressingMode( - uvw); - texture_unit->setTextureFiltering( - Ogre::FT_MIN, Ogre::FO_LINEAR); - texture_unit->setTextureFiltering( - Ogre::FT_MAG, Ogre::FO_LINEAR); - texture_unit->setTextureFiltering( - Ogre::FT_MIP, Ogre::FO_LINEAR); - Ogre::TextureUnitState *texture_unit2 = - pass->createTextureUnitState(); - texture_unit2->setTextureName( - "waves2.png"); - texture_unit2->setTextureAddressingMode( - uvw); - texture_unit2->setTextureFiltering( - Ogre::FT_MIN, Ogre::FO_LINEAR); - texture_unit2->setTextureFiltering( - Ogre::FT_MAG, Ogre::FO_LINEAR); - texture_unit2->setTextureFiltering( - Ogre::FT_MIP, Ogre::FO_LINEAR); + Ogre::TextureUnitState *texture_unit = + pass->createTextureUnitState(); + texture_unit->setTextureName( + "ReflectionRefractionTexture"); + Ogre::Sampler::UVWAddressingMode uvw; + uvw.u = Ogre::TextureUnitState::TAM_MIRROR; + uvw.v = Ogre::TextureUnitState::TAM_MIRROR; + uvw.w = Ogre::TextureUnitState::TAM_MIRROR; + texture_unit->setTextureAddressingMode(uvw); + texture_unit->setTextureFiltering( + Ogre::FT_MIN, Ogre::FO_LINEAR); + texture_unit->setTextureFiltering( + Ogre::FT_MAG, Ogre::FO_LINEAR); + texture_unit->setTextureFiltering( + Ogre::FT_MIP, Ogre::FO_LINEAR); + Ogre::TextureUnitState *texture_unit2 = + pass->createTextureUnitState(); + texture_unit2->setTextureName("waves2.png"); + texture_unit2->setTextureAddressingMode(uvw); + texture_unit2->setTextureFiltering( + Ogre::FT_MIN, Ogre::FO_LINEAR); + texture_unit2->setTextureFiltering( + Ogre::FT_MAG, Ogre::FO_LINEAR); + texture_unit2->setTextureFiltering( + Ogre::FT_MIP, Ogre::FO_LINEAR); #if 0 bool success = Ogre::RTShader::ShaderGenerator::getSingletonPtr() @@ -260,175 +301,199 @@ WaterModule::WaterModule(flecs::world &ecs) renderState->addTemplateSubRenderState( perPixelLightModel); #endif - } - + } #if 0 mat = Ogre::MaterialManager::getSingleton() .getByName("Water/Above"); mat->load(); #endif - mat->load(); - mat->setReceiveShadows(false); - /* + 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); - water.mReflectionTexture->addListener( - &water.mRenderTargetListener); - water.mReflectionCamera = - eng.mScnMgr->createCamera( - "ReflectionCamera"); - water.mReflectionDepthCamera = - eng.mScnMgr->createCamera( - "ReflectionDepthCamera"); - water.mReflectionCameraNode = - camera.mCamera->getParentSceneNode() - ->createChildSceneNode( - "ReflectionCameraNode"); - water.mReflectionCameraNode->setPosition( - Ogre::Vector3(0, 1.0f, 0)); - water.mReflectionCameraNode->attachObject( - water.mReflectionCamera); - water.mReflectionCameraNode->attachObject( - water.mReflectionDepthCamera); - water.mReflectionCamera->setAspectRatio( - camera.mCamera->getAspectRatio()); - water.mReflectionCamera->setNearClipDistance( - camera.mCamera->getNearClipDistance()); - water.mReflectionCamera->setFarClipDistance( - camera.mCamera->getFarClipDistance()); - water.mReflectionCamera - ->enableCustomNearClipPlane( - water.mReflectionClipPlaneAbove); - water.mReflectionCamera->enableReflection( - water.mReflectionPlane); + 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); + water.mReflectionTexture->addListener( + &water.mRenderTargetListener); + water.mReflectionCamera = + eng.mScnMgr->createCamera("ReflectionCamera"); + water.mReflectionDepthCamera = + eng.mScnMgr->createCamera( + "ReflectionDepthCamera"); + water.mReflectionCameraNode = + camera.mCamera->getParentSceneNode() + ->createChildSceneNode( + "ReflectionCameraNode"); + water.mReflectionCameraNode->setPosition( + Ogre::Vector3(0, 1.0f, 0)); + water.mReflectionCameraNode->attachObject( + water.mReflectionCamera); + water.mReflectionCameraNode->attachObject( + water.mReflectionDepthCamera); + water.mReflectionCamera->setAspectRatio( + camera.mCamera->getAspectRatio()); + water.mReflectionCamera->setNearClipDistance( + camera.mCamera->getNearClipDistance()); + water.mReflectionCamera->setFarClipDistance( + camera.mCamera->getFarClipDistance()); + water.mReflectionCamera->enableCustomNearClipPlane( + water.mReflectionClipPlaneAbove); + water.mReflectionCamera->enableReflection( + water.mReflectionPlane); - water.mReflectionDepthCamera->setAspectRatio( - camera.mCamera->getAspectRatio()); - water.mReflectionDepthCamera->setNearClipDistance( - camera.mCamera->getNearClipDistance()); - water.mReflectionDepthCamera->setFarClipDistance( - camera.mCamera->getFarClipDistance()); - water.mReflectionDepthCamera - ->enableCustomNearClipPlane( - water.mReflectionClipPlaneAbove); - water.mReflectionDepthCamera->enableReflection( - water.mReflectionPlane); + water.mReflectionDepthCamera->setAspectRatio( + camera.mCamera->getAspectRatio()); + water.mReflectionDepthCamera->setNearClipDistance( + camera.mCamera->getNearClipDistance()); + water.mReflectionDepthCamera->setFarClipDistance( + camera.mCamera->getFarClipDistance()); + water.mReflectionDepthCamera->enableCustomNearClipPlane( + water.mReflectionClipPlaneAbove); + water.mReflectionDepthCamera->enableReflection( + water.mReflectionPlane); - Ogre::Viewport *reflectionViewport = - water.mReflectionTexture->addViewport( - water.mReflectionCamera, 0, 0, - 0, 0.5f, 0.5f); - reflectionViewport->setClearEveryFrame(true); - reflectionViewport->setBackgroundColour( - Ogre::ColourValue(0.0, 0.0, 1.0, 1.0)); - reflectionViewport->setOverlaysEnabled(false); - reflectionViewport->setSkiesEnabled(true); - reflectionViewport->setAutoUpdated(false); - reflectionViewport->setVisibilityMask( - ~WATER_MASK); - water.mViewports[0] = reflectionViewport; + Ogre::Viewport *reflectionViewport = + water.mReflectionTexture->addViewport( + water.mReflectionCamera, 0, 0, 0, 0.5f, + 0.5f); + reflectionViewport->setClearEveryFrame(true); + reflectionViewport->setBackgroundColour( + Ogre::ColourValue(0.0, 0.0, 1.0, 1.0)); + reflectionViewport->setOverlaysEnabled(false); + reflectionViewport->setSkiesEnabled(true); + reflectionViewport->setAutoUpdated(false); + reflectionViewport->setVisibilityMask(~WATER_MASK); + water.mViewports[0] = reflectionViewport; - Ogre::Viewport *reflectionDepthViewport = - water.mReflectionTexture->addViewport( - water.mReflectionDepthCamera, 2, - 0, 0.5f, 0.5f, 0.5f); - reflectionDepthViewport->setClearEveryFrame( - true); - reflectionDepthViewport->setBackgroundColour( - Ogre::ColourValue(0.0, 0.0, 0.0, 0.0)); - reflectionDepthViewport->setOverlaysEnabled( - false); - reflectionDepthViewport->setSkiesEnabled(true); - reflectionDepthViewport->setAutoUpdated(false); - reflectionDepthViewport->setVisibilityMask( - ~WATER_MASK); - water.mViewports[2] = reflectionDepthViewport; + Ogre::Viewport *reflectionDepthViewport = + water.mReflectionTexture->addViewport( + water.mReflectionDepthCamera, 2, 0, + 0.5f, 0.5f, 0.5f); + reflectionDepthViewport->setClearEveryFrame(true); + reflectionDepthViewport->setBackgroundColour( + Ogre::ColourValue(0.0, 0.0, 0.0, 0.0)); + reflectionDepthViewport->setOverlaysEnabled(false); + reflectionDepthViewport->setSkiesEnabled(true); + reflectionDepthViewport->setAutoUpdated(false); + reflectionDepthViewport->setVisibilityMask(~WATER_MASK); + water.mViewports[2] = reflectionDepthViewport; - water.mRefractionCamera = - eng.mScnMgr->createCamera( - "RefractionCamera"); - water.mRefractionDepthCamera = - eng.mScnMgr->createCamera( - "RefractionDepthCamera"); - water.mRefractionCameraNode = - camera.mCamera->getParentSceneNode() - ->createChildSceneNode( - "RefractionCameraNode"); - water.mRefractionCameraNode->attachObject( - water.mRefractionCamera); - water.mRefractionCameraNode->attachObject( - water.mRefractionDepthCamera); - water.mRefractionCamera->setAspectRatio( - camera.mCamera->getAspectRatio()); - water.mRefractionCamera->setNearClipDistance( - camera.mCamera->getNearClipDistance()); - water.mRefractionCamera->setFarClipDistance( - camera.mCamera->getFarClipDistance()); - water.mRefractionCamera - ->enableCustomNearClipPlane( - water.mRefractionClipPlaneAbove); + water.mRefractionCamera = + eng.mScnMgr->createCamera("RefractionCamera"); + water.mRefractionDepthCamera = + eng.mScnMgr->createCamera( + "RefractionDepthCamera"); + water.mRefractionCameraNode = + camera.mCamera->getParentSceneNode() + ->createChildSceneNode( + "RefractionCameraNode"); + water.mRefractionCameraNode->attachObject( + water.mRefractionCamera); + water.mRefractionCameraNode->attachObject( + water.mRefractionDepthCamera); + water.mRefractionCamera->setAspectRatio( + camera.mCamera->getAspectRatio()); + water.mRefractionCamera->setNearClipDistance( + camera.mCamera->getNearClipDistance()); + water.mRefractionCamera->setFarClipDistance( + camera.mCamera->getFarClipDistance()); + water.mRefractionCamera->enableCustomNearClipPlane( + water.mRefractionClipPlaneAbove); - water.mRefractionDepthCamera->setAspectRatio( - camera.mCamera->getAspectRatio()); - water.mRefractionDepthCamera->setNearClipDistance( - camera.mCamera->getNearClipDistance()); - water.mRefractionDepthCamera->setFarClipDistance( - camera.mCamera->getFarClipDistance()); - water.mRefractionDepthCamera - ->enableCustomNearClipPlane( - water.mRefractionClipPlaneAbove); + water.mRefractionDepthCamera->setAspectRatio( + camera.mCamera->getAspectRatio()); + water.mRefractionDepthCamera->setNearClipDistance( + camera.mCamera->getNearClipDistance()); + water.mRefractionDepthCamera->setFarClipDistance( + camera.mCamera->getFarClipDistance()); + water.mRefractionDepthCamera->enableCustomNearClipPlane( + water.mRefractionClipPlaneAbove); - Ogre::Viewport *refractionViewport = - water.mReflectionTexture->addViewport( - water.mRefractionCamera, 1, 0.5, - 0, 0.5f, 0.5f); - refractionViewport->setClearEveryFrame(true); - refractionViewport->setBackgroundColour( - Ogre::ColourValue(0.0, 0.5, 1.0, 1.0)); - refractionViewport->setOverlaysEnabled(false); - refractionViewport->setSkiesEnabled(false); - refractionViewport->setAutoUpdated(false); - refractionViewport->setVisibilityMask( - ~WATER_MASK); - water.mViewports[1] = refractionViewport; + Ogre::Viewport *refractionViewport = + water.mReflectionTexture->addViewport( + water.mRefractionCamera, 1, 0.5, 0, + 0.5f, 0.5f); + refractionViewport->setClearEveryFrame(true); + refractionViewport->setBackgroundColour( + Ogre::ColourValue(0.0, 0.5, 1.0, 1.0)); + refractionViewport->setOverlaysEnabled(false); + refractionViewport->setSkiesEnabled(false); + refractionViewport->setAutoUpdated(false); + refractionViewport->setVisibilityMask(~WATER_MASK); + water.mViewports[1] = refractionViewport; - Ogre::Viewport *refractionDepthViewport = - water.mReflectionTexture->addViewport( - water.mRefractionDepthCamera, 3, - 0.5, 0.5, 0.5f, 0.5f); - refractionDepthViewport->setClearEveryFrame( - true); - refractionDepthViewport->setBackgroundColour( - Ogre::ColourValue(0.0, 0.0, 0.0, 0.0)); - refractionDepthViewport->setOverlaysEnabled( - false); - refractionDepthViewport->setSkiesEnabled(false); - refractionDepthViewport->setAutoUpdated(false); - refractionDepthViewport->setVisibilityMask( - ~WATER_MASK); - water.mViewports[3] = refractionDepthViewport; + Ogre::Viewport *refractionDepthViewport = + water.mReflectionTexture->addViewport( + water.mRefractionDepthCamera, 3, 0.5, + 0.5, 0.5f, 0.5f); + refractionDepthViewport->setClearEveryFrame(true); + refractionDepthViewport->setBackgroundColour( + Ogre::ColourValue(0.0, 0.0, 0.0, 0.0)); + refractionDepthViewport->setOverlaysEnabled(false); + refractionDepthViewport->setSkiesEnabled(false); + refractionDepthViewport->setAutoUpdated(false); + refractionDepthViewport->setVisibilityMask(~WATER_MASK); + water.mViewports[3] = refractionDepthViewport; - water.mRenderTargetListener.mInDepth = false; - eng.mScnMgr->getRenderQueue() - ->setRenderableListener( - &water.mRenderTargetListener); - std::cout << "Water setup done\n"; - } + water.mRenderTargetListener.mInDepth = false; + eng.mScnMgr->getRenderQueue()->setRenderableListener( + &water.mRenderTargetListener); + std::cout << "Water setup done\n"; + }) + .add(flecs::Singleton); + ecs.component() + .on_add([this](WaterBody &body) { + body.mShapeAabbMax = btVector3(0, 0, 0); + body.mShapeAabbMin = btVector3(0, 0, 0); + body.mSurface.clear(); + body.mWaterBody = OGRE_NEW btPairCachingGhostObject(); + createWaterShape(&body); + body.mWaterBody->setCollisionShape(body.mWaterShape); + btTransform bodyTransform; + bodyTransform.setIdentity(); + body.mWaterBody->setWorldTransform(bodyTransform); + body.mWaterBody->setCollisionFlags( + body.mWaterBody->getCollisionFlags() | + btCollisionObject::CF_KINEMATIC_OBJECT | + btCollisionObject::CF_NO_CONTACT_RESPONSE); + body.mWaterBody->setActivationState( + DISABLE_DEACTIVATION); + const EngineData &eng = ECS::get(); + const WaterSurface &water = ECS::get(); + + eng.mWorld->attachCollisionObject(body.mWaterBody, + water.mWaterEnt, 16, + 0x7fffffff & ~2); + WaterPhysicsAction *action = + OGRE_NEW WaterPhysicsAction(body.mWaterBody); + body.action = action; + ECS::get() + .get() + .mWorld->getBtWorld() + ->addAction(body.action); + ECS::get().add(); + }) + .add(flecs::Singleton); + ecs.system("UpdateWater") + .kind(flecs::OnUpdate) + .with() + .each([](const EngineData &eng, const Camera &camera, + WaterSurface &water) { + float delta = eng.delta; Ogre::Vector3 mCameraPos = camera.mCameraNode->_getDerivedPosition(); Ogre::Vector3 waterPos = @@ -454,49 +519,12 @@ WaterModule::WaterModule(flecs::world &ecs) ecs.system( "UpdateWaterBody") .kind(flecs::OnUpdate) + .with() .each([this](const EngineData &eng, const WaterSurface &water, WaterBody &body) { int i; - if (!body.mWaterBody) { - btCompoundShape *shape = new btCompoundShape; - btBoxShape *boxShape0 = new btBoxShape( - btVector3(1000, 10, 1000)); - btBoxShape *boxShape1 = new btBoxShape( - btVector3(1000, 20, 1000)); - btBoxShape *boxShape2 = new btBoxShape( - btVector3(1000, 100, 1000)); - btBoxShape *boxShape3 = new btBoxShape( - btVector3(1000, 1000, 1000)); - btTransform boxShapeXform0(btQuaternion(), - btVector3(0, -10.2f, - 0)); - btTransform boxShapeXform1(btQuaternion(), - btVector3(0, -30.2f, - 0)); - btTransform boxShapeXform2(btQuaternion(), - btVector3(0, -130.2f, - 0)); - btTransform boxShapeXform3( - btQuaternion(), - btVector3(0, -1120.2f, 0)); - shape->addChildShape(boxShapeXform0, boxShape0); - shape->addChildShape(boxShapeXform1, boxShape1); - shape->addChildShape(boxShapeXform2, boxShape2); - shape->addChildShape(boxShapeXform3, boxShape3); - body.mWaterBody = new btPairCachingGhostObject; - body.mWaterBody->setCollisionShape(shape); - body.mWaterBody->setCollisionFlags( - body.mWaterBody->getCollisionFlags() | - btCollisionObject::CF_NO_CONTACT_RESPONSE | - btCollisionObject::CF_STATIC_OBJECT); - body.mWaterBody->setActivationState( - DISABLE_DEACTIVATION); - body.mWaterBody->getWorldTransform().setOrigin( - btVector3(0, 0, 0)); - eng.mWorld->attachCollisionObject( - body.mWaterBody, water.mWaterEnt, 16, - 0x7fffffff & ~2); - } + OgreAssert(body.mWaterBody, "Water not ready"); + std::set currentOverlaps; Ogre::Vector3 waterPos = water.mWaterNode->_getDerivedPosition(); Ogre::Vector3 waterBodyPos = Ogre::Bullet::convert( @@ -510,88 +538,97 @@ WaterModule::WaterModule(flecs::world &ecs) body.mWaterBody->getWorldTransform().setOrigin( Ogre::Bullet::convert(waterBodyPos + d)); +#if 0 btCompoundShape *mshape = static_cast( body.mWaterBody->getCollisionShape()); + btDispatcher *dispatch = + eng.mWorld->getBtWorld()->getDispatcher(); + btHashedOverlappingPairCache *cache = + body.mWaterBody->getOverlappingPairCache(); + btBroadphasePairArray &collisionPairs = + cache->getOverlappingPairArray(); + const int numObjects = collisionPairs.size(); + std::cout << "numObjects: " << numObjects << "\n"; + std::cout + << "numObjects: " + << body.mWaterBody->getOverlappingPairs().size() + << "\n"; + for (int i = 0; i < numObjects; i++) { + const btBroadphasePair &collisionPair = + collisionPairs[i]; + } +#endif +#if 0 body.mShapeAabbMin = body.mWaterBody->getWorldTransform() .getOrigin() + - btVector3(-1000, -1000, -1000); + btVector3(-100, -100, -100); body.mShapeAabbMax = body.mWaterBody->getWorldTransform() .getOrigin() + - btVector3(1000, -0.2, 1000); - btDispatcher *dispatch = - eng.mWorld->getBtWorld()->getDispatcher(); - eng.mWorld->getBtWorld()->getBroadphase()->setAabb( - body.mWaterBody->getBroadphaseHandle(), - body.mShapeAabbMin, body.mShapeAabbMax, - eng.mWorld->getBtWorld()->getDispatcher()); - dispatch->dispatchAllCollisionPairs( - body.mWaterBody->getOverlappingPairCache(), - eng.mWorld->getBtWorld()->getDispatchInfo(), - dispatch); - btBroadphasePairArray &collisionPairs = - body.mWaterBody->getOverlappingPairCache() - ->getOverlappingPairArray(); - std::set currentOverlaps; - std::set::iterator it; - const int numObjects = collisionPairs.size(); - for (int i = 0; i < numObjects; i++) { - body.mManifoldArray.resize(0); - const btBroadphasePair &collisionPair = - collisionPairs[i]; - if (collisionPair.m_algorithm) - collisionPair.m_algorithm - ->getAllContactManifolds( - body.mManifoldArray); - for (int j = 0; j < body.mManifoldArray.size(); - j++) { - btPersistentManifold *manifold = - body.mManifoldArray[j]; - if (manifold->getNumContacts() == 0) - continue; - const btCollisionObject *obj = - (manifold->getBody0() == - body.mWaterBody) ? - manifold->getBody1() : - manifold->getBody0(); - btCollisionObject *nobj = - const_cast( - obj); + btVector3(100, 100, 100); +#if 0 + body.mWaterShape->getAabb( + body.mWaterBody->getWorldTransform(), + body.mShapeAabbMin, body.mShapeAabbMax); +#endif + std::cout << "manifolds: " + << static_cast( + body.action) + ->mManifoldArray.size() + << "\n"; + for (int j = 0; + j < static_cast(body.action) + ->mManifoldArray.size(); + j++) { + btPersistentManifold *manifold = + static_cast( + body.action) + ->mManifoldArray[j]; + std::cout << "contacts: " + << manifold->getNumContacts() << "\n"; + if (manifold->getNumContacts() == 0) + continue; + const btCollisionObject *obj = + (manifold->getBody0() == + body.mWaterBody) ? + manifold->getBody1() : + manifold->getBody0(); + btCollisionObject *nobj = + const_cast(obj); +#if 0 if (obj->getCollisionFlags() & btCollisionObject::CF_STATIC_OBJECT) continue; - bool ok = false; - Ogre::Vector3 contactPosA, contactPosB; - float minDist = 0.0f; - for (int p = 0; - p < manifold->getNumContacts(); - p++) { - const btManifoldPoint &pt = - manifold->getContactPoint( - p); - float dist = pt.getDistance(); - if (dist < minDist) { - minDist = dist; - ok = true; - } +#endif + bool ok = false; + Ogre::Vector3 contactPosA, contactPosB; + float minDist = 0.0f; + for (int p = 0; p < manifold->getNumContacts(); + p++) { + const btManifoldPoint &pt = + manifold->getContactPoint(p); + float dist = pt.getDistance(); + if (dist < minDist) { + minDist = dist; + ok = true; } - if (ok) { - currentOverlaps.insert(nobj); - if (body.mInWater.find(nobj) == - body.mInWater.end()) { - /* new body */ - body.mInWater.insert( - nobj); - /* calculate proj surface */ - body.mSurface[nobj] = - 100.0f; - } + } + if (ok) { + currentOverlaps.insert(nobj); + if (body.mInWater.find(nobj) == + body.mInWater.end()) { + /* new body */ + body.mInWater.insert(nobj); + /* calculate proj surface */ + body.mSurface[nobj] = 100.0f; + body.count++; } } } - for (it = body.mInWater.begin(); + for (std::set::iterator it = + body.mInWater.begin(); it != body.mInWater.end();) { btCollisionObject *obj = *it; if (currentOverlaps.find(obj) == @@ -599,11 +636,50 @@ WaterModule::WaterModule(flecs::world &ecs) /* remove body */ it = body.mInWater.erase(it); body.mSurface.erase(obj); + body.count--; } else it++; } +#endif }); } +struct shapeParams { + float offsetX, offsetY, offsetZ; + float boxX, boxY, boxZ; +}; +static struct shapeParams childShapes[] = { + { 0.0f, 0.0f, 0.0f, 100.0f, 100.0f, 100.0f }, +}; +void WaterModule::createWaterShape(WaterBody *water) +{ + int i = 0; + btCompoundShape *shape = OGRE_NEW btCompoundShape(); + shape->setMargin(0.2f); + { + btVector3 inertia(0, 0, 0); + std::vector masses; + btTransform principal; + principal.setIdentity(); + for (i = 0; i < sizeof(childShapes) / sizeof(childShapes[0]); + i++) { + btTransform xform; + xform.setIdentity(); + xform.setOrigin(btVector3(childShapes[i].offsetX, + childShapes[i].offsetY, + childShapes[i].offsetZ)); + btBoxShape *box = OGRE_NEW btBoxShape(btVector3( + childShapes[i].boxX, childShapes[i].boxY, + childShapes[i].boxZ)); + water->mChildShapes.push_back(box); + shape->addChildShape(xform, box); + masses.push_back(0); + } + shape->calculatePrincipalAxisTransform(masses.data(), principal, + inertia); + } + shape->recalculateLocalAabb(); + water->mWaterShape = shape; +} void WaterSurface::RenderTextureListener::preRenderTargetUpdate( const Ogre::RenderTargetEvent &evt) { @@ -632,4 +708,250 @@ bool WaterSurface::RenderTextureListener::renderableQueued( *ppTech = mSurface->mDepthTech; return true; } +struct DeepPenetrationContactResultCallback : public btManifoldResult { + DeepPenetrationContactResultCallback( + const btCollisionObjectWrapper *body0Wrap, + const btCollisionObjectWrapper *body1Wrap) + : btManifoldResult(body0Wrap, body1Wrap) + , mPenetrationDistance(0) + , mOtherIndex(0) + { + } + float mPenetrationDistance; + int mOtherIndex; + btVector3 mNormal, mPoint; + void reset() + { + mPenetrationDistance = 0.0f; + } + bool hasHit() + { + return mPenetrationDistance < 0.0f; + } + virtual void addContactPoint(const btVector3 &normalOnBInWorld, + const btVector3 &pointInWorldOnB, + btScalar depth) + { +#ifdef VDEBUG + std::cout + << "contact: " << Ogre::Bullet::convert(pointInWorldOnB) + << " " << Ogre::Bullet::convert(normalOnBInWorld) + << "\n"; +#endif + if (mPenetrationDistance > depth) { // Has penetration? + + const bool isSwapped = + m_manifoldPtr->getBody0() != + m_body0Wrap->getCollisionObject(); + mPenetrationDistance = depth; + mOtherIndex = isSwapped ? m_index0 : m_index1; + mPoint = isSwapped ? (pointInWorldOnB + + (normalOnBInWorld * depth)) : + pointInWorldOnB; + + mNormal = isSwapped ? normalOnBInWorld * -1 : + normalOnBInWorld; + } + } +}; +void WaterPhysicsAction::updateAction(btCollisionWorld *collisionWorld, + btScalar deltaTimeStep) +{ + collisionWorld->updateSingleAabb(mWaterBody); + mWaterBody->getCollisionShape()->getAabb( + mWaterBody->getWorldTransform(), mShapeAabbMin, mShapeAabbMax); + btDispatcher *dispatch = collisionWorld->getDispatcher(); + btHashedOverlappingPairCache *cache = + mWaterBody->getOverlappingPairCache(); + btBroadphasePairArray &collisionPairs = + cache->getOverlappingPairArray(); + btVector3 a, b; + collisionWorld->getBroadphase()->getAabb( + mWaterBody->getBroadphaseHandle(), a, b); + collisionWorld->getBroadphase()->setAabb( + mWaterBody->getBroadphaseHandle(), mShapeAabbMin, mShapeAabbMax, + dispatch); + btDispatcherInfo &dispatchInfo = collisionWorld->getDispatchInfo(); + dispatch->dispatchAllCollisionPairs(cache, dispatchInfo, dispatch); + std::set currentOverlaps; + std::set::iterator it; + const int numObjects = collisionPairs.size(); +#ifdef VDEBUG + std::cout << "collision pairs: " << numObjects << "\n"; + std::cout << "MIN: " << Ogre::Bullet::convert(mShapeAabbMin) << "\n"; + std::cout << "MAX: " << Ogre::Bullet::convert(mShapeAabbMax) << "\n"; + std::cout << "MIN: " << Ogre::Bullet::convert(a) << "\n"; + std::cout << "MAX: " << Ogre::Bullet::convert(b) << "\n"; +#endif + std::set mCurrentInWater; + /* perform narrow phase */ + for (int i = 0; i < numObjects; i++) { + int j; + const btBroadphasePair *collisionPairPtr = + collisionWorld->getBroadphase() + ->getOverlappingPairCache() + ->findPair(collisionPairs[i].m_pProxy0, + collisionPairs[i].m_pProxy1); + if (!collisionPairPtr) + continue; +#ifndef USE_MANIFOLD + const btBroadphasePair &collisionPair = *collisionPairPtr; + const btCollisionObject *objA = + static_cast( + collisionPair.m_pProxy0->m_clientObject); + const btCollisionObject *objB = + static_cast( + collisionPair.m_pProxy1->m_clientObject); +#ifdef VDEBUG + std::cout << "bodies: " << objA << " " << objB << "\n"; + std::cout << "bodies: " << objA->getCollisionShape()->getName() + << " " << objB->getCollisionShape()->getName() + << "\n"; + std::cout << "pair: " << i << " " << collisionPair.m_algorithm + << "\n"; +#endif + const btCollisionObject *me, *other; + if (objA == static_cast(mWaterBody)) { + me = objA; + other = objB; + } else { + me = objB; + other = objA; + } + const btCollisionShape *my_shape = me->getCollisionShape(); + const btCollisionShape *other_shape = + other->getCollisionShape(); + btCollisionObjectWrapper obA(NULL, my_shape, mWaterBody, + mWaterBody->getWorldTransform(), + -1, j); + btCollisionObjectWrapper obB(NULL, other_shape, other, + other->getWorldTransform(), -1, 0); + btCollisionAlgorithm *algorithm = dispatch->findAlgorithm( + &obA, &obB, NULL, BT_CONTACT_POINT_ALGORITHMS); +#else + btCollisionAlgorithm *algorithm = collisionPairPtr->m_algorithm; + OgreAssert(algorithm, "No algorithm found"); +#endif +#ifdef VDEBUG + std::cout << "algorithm: " << algorithm << "\n"; +#endif +#ifndef USE_MANIFOLD + DeepPenetrationContactResultCallback contactPointResult(&obA, + &obB); +#ifdef VDEBUG + std::cout << "process collision\n"; +#endif + algorithm->processCollision(&obA, &obB, + collisionWorld->getDispatchInfo(), + &contactPointResult); + algorithm->~btCollisionAlgorithm(); + dispatch->freeCollisionAlgorithm(algorithm); + if (contactPointResult.hasHit()) { + mCurrentInWater.insert(other); + mInWater.insert(other); + } +#ifdef VDEBUG + std::cout << "process collision done\n"; +#endif +#else + if (collisionPairPtr->m_algorithm) + collisionPairPtr->m_algorithm->getAllContactManifolds( + mManifoldArray); + std::cout << "action: manifold: " << mManifoldArray.size() + << "\n"; +#endif +#if 0 + std::cout << " " + << Ogre::Bullet::convert( + collisionPair.m_pProxy0->m_aabbMin) + << " -> " + << Ogre::Bullet::convert( + collisionPair.m_pProxy0->m_aabbMax) + << " VS " + << Ogre::Bullet::convert( + collisionPair.m_pProxy1->m_aabbMin) + << " -> " + << Ogre::Bullet::convert( + collisionPair.m_pProxy1->m_aabbMax) + << "\n"; + std::cout << "group: 0 " << std::dec + << collisionPair.m_pProxy0->m_collisionFilterGroup + << "mask: " << std::hex + << collisionPair.m_pProxy0->m_collisionFilterMask + << "\n"; + std::cout << "group: 1 " << std::dec + << collisionPair.m_pProxy1->m_collisionFilterGroup + << "mask: " << std::hex + << collisionPair.m_pProxy1->m_collisionFilterMask + << std::dec << "\n"; + if (collisionPair.m_algorithm) + collisionPair.m_algorithm->getAllContactManifolds( + mManifoldArray); + std::cout << "action: manifold: " << mManifoldArray.size() + << "\n"; +#endif +#ifdef USE_MANIFOLD + for (int j = 0; j < mManifoldArray.size(); j++) { + btPersistentManifold *manifold = mManifoldArray[j]; + std::cout << "contacts: " << manifold->getNumContacts() + << "\n"; + if (manifold->getNumContacts() == 0) + continue; + const btCollisionObject *obj = + (manifold->getBody0() == mWaterBody) ? + manifold->getBody1() : + manifold->getBody0(); + btCollisionObject *nobj = + const_cast(obj); +#if 1 + if (obj->getCollisionFlags() & + btCollisionObject::CF_STATIC_OBJECT) + continue; +#endif + bool ok = false; + Ogre::Vector3 contactPosA, contactPosB; + float minDist = 0.0f; + for (int p = 0; p < manifold->getNumContacts(); p++) { + const btManifoldPoint &pt = + manifold->getContactPoint(p); + float dist = pt.getDistance(); + if (dist < minDist) { + minDist = dist; + ok = true; + } + } + if (ok) { + currentOverlaps.insert(nobj); + if (mInWater.find(nobj) == mInWater.end()) { + /* new body */ + mInWater.insert(nobj); + } + } + } +#endif + } + for (std::set::iterator it = + mInWater.begin(); + it != mInWater.end();) { + const btCollisionObject *obj = *it; + if (mCurrentInWater.find(obj) == mCurrentInWater.end()) { + /* remove body */ + it = mInWater.erase(it); + } else + it++; + } +#ifdef VDEBUG + std::cout << "water count: " << mInWater.size() << "\n"; +#endif +} +void WaterPhysicsAction::debugDraw(btIDebugDraw *debugDrawer) +{ +} +void WaterPhysicsAction::setupBody() +{ +} +bool WaterBody::isInWater(const btCollisionObject *body) const +{ + return static_cast(action)->isInWater(body); +} } \ No newline at end of file diff --git a/src/gamedata/WaterModule.h b/src/gamedata/WaterModule.h index af79175..888455d 100644 --- a/src/gamedata/WaterModule.h +++ b/src/gamedata/WaterModule.h @@ -38,14 +38,19 @@ struct WaterSurface { Ogre::Viewport *mViewports[4]; }; struct WaterBody { + std::vector mChildShapes; + btCollisionShape *mWaterShape; btPairCachingGhostObject *mWaterBody; - std::set mInWater; std::unordered_map mSurface; - btManifoldArray mManifoldArray; btVector3 mShapeAabbMin, mShapeAabbMax; + int count; + btActionInterface *action; + bool isInWater(const btCollisionObject *body) const; }; struct WaterModule { + btPairCachingGhostObject *mGhostObject; WaterModule(flecs::world &ecs); + void createWaterShape(WaterBody *water); }; } #endif \ No newline at end of file diff --git a/terrain.cpp b/terrain.cpp index 1740249..e0fd08e 100644 --- a/terrain.cpp +++ b/terrain.cpp @@ -12,10 +12,11 @@ #include "OgrePageManager.h" #include "lua_control.h" -#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 -#define ANIM_FADE_SPEED 7.5f // animation crossfade speed in % of full weight per second +#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 +#define ANIM_FADE_SPEED \ + 7.5f // animation crossfade speed in % of full weight per second using Real = Ogre::Real; using Math = Ogre::Math; @@ -29,21 +30,44 @@ class WorldData { Ogre::PagedWorld *mPagedWorld; float fps_time; Ogre::RenderWindow *mRenderWindow; + private: static WorldData *singleton; - class DummyPageProvider : public Ogre::PageProvider - { + class DummyPageProvider : public Ogre::PageProvider { public: - bool prepareProceduralPage(Ogre::Page* page, Ogre::PagedWorldSection* section) override { return true; } - bool loadProceduralPage(Ogre::Page* page, Ogre::PagedWorldSection* section) override { return true; } - bool unloadProceduralPage(Ogre::Page* page, Ogre::PagedWorldSection* section) override { return true; } - bool unprepareProceduralPage(Ogre::Page* page, Ogre::PagedWorldSection* section) override { return true; } + bool + prepareProceduralPage(Ogre::Page *page, + Ogre::PagedWorldSection *section) override + { + return true; + } + bool + loadProceduralPage(Ogre::Page *page, + Ogre::PagedWorldSection *section) override + { + return true; + } + bool + unloadProceduralPage(Ogre::Page *page, + Ogre::PagedWorldSection *section) override + { + return true; + } + bool unprepareProceduralPage( + Ogre::Page *page, + Ogre::PagedWorldSection *section) override + { + return true; + } }; DummyPageProvider mDummyPageProvider; - WorldData(Ogre::Root *root, Ogre::SceneManager *scnMgr, Ogre::RenderWindow *renderWindow) - : mDynWorld(new Ogre::Bullet::DynamicsWorld(Ogre::Vector3(0, -9.8, 0))) - , mDbgDraw(new Ogre::Bullet::DebugDrawer(scnMgr->getRootSceneNode(), mDynWorld->getBtWorld())) + WorldData(Ogre::Root *root, Ogre::SceneManager *scnMgr, + Ogre::RenderWindow *renderWindow) + : mDynWorld(new Ogre::Bullet::DynamicsWorld( + Ogre::Vector3(0, -9.8, 0))) + , mDbgDraw(new Ogre::Bullet::DebugDrawer( + scnMgr->getRootSceneNode(), mDynWorld->getBtWorld())) , mRoot(root) , mScnMgr(scnMgr) , mbtWorld(mDynWorld->getBtWorld()) @@ -53,8 +77,10 @@ private: { } + public: - static void init(Ogre::Root *root, Ogre::SceneManager *scnMgr, Ogre::RenderWindow *renderWindow) + static void init(Ogre::Root *root, Ogre::SceneManager *scnMgr, + Ogre::RenderWindow *renderWindow) { singleton = new WorldData(root, scnMgr, renderWindow); } @@ -79,16 +105,20 @@ public: void createTrimesh(Ogre::Entity *entity) { } - btPairCachingGhostObject *addGhostObject(Ogre::Entity *ent, btCollisionShape *shape, int group = 1, int mask = 0xFFFF) + btPairCachingGhostObject *addGhostObject(Ogre::Entity *ent, + btCollisionShape *shape, + int group = 1, + int mask = 0xFFFF) { btDynamicsWorld *world = mDynWorld->getBtWorld(); Ogre::SceneNode *node = ent->getParentSceneNode(); - btPairCachingGhostObject *ghost = new btPairCachingGhostObject(); + btPairCachingGhostObject *ghost = + new btPairCachingGhostObject(); ghost->setCollisionShape(shape); - ghost->setCollisionFlags(ghost->getCollisionFlags() - | btCollisionObject::CF_NO_CONTACT_RESPONSE - | btCollisionObject::CF_CHARACTER_OBJECT - ); + ghost->setCollisionFlags( + ghost->getCollisionFlags() | + btCollisionObject::CF_NO_CONTACT_RESPONSE | + btCollisionObject::CF_CHARACTER_OBJECT); getWorld()->attachCollisionObject(ghost, ent, group, mask); #if 0 getBtWorld() @@ -97,34 +127,40 @@ public: ghost->setUserPointer(new Ogre::Bullet::EntityCollisionListener{ent, nullptr}); #endif return ghost; - } - btRigidBody *addRigidBody(float mass, Ogre::Entity *ent, Ogre::Bullet::ColliderType ct, int group = 1, int mask = 0xFFFF) + btRigidBody *addRigidBody(float mass, Ogre::Entity *ent, + Ogre::Bullet::ColliderType ct, int group = 1, + int mask = 0xFFFF) { btDynamicsWorld *world = mDynWorld->getBtWorld(); Ogre::SceneNode *node = ent->getParentSceneNode(); - Ogre::Bullet::RigidBodyState *state = new Ogre::Bullet::RigidBodyState(node); + Ogre::Bullet::RigidBodyState *state = + new Ogre::Bullet::RigidBodyState(node); btCollisionShape *cs; btCollisionShape *shape; btVector3 inertia(0, 0, 0); - switch(ct) { + switch (ct) { case Ogre::Bullet::CT_TRIMESH: { cs = Ogre::Bullet::createTrimeshCollider(ent); if (mass != 0) cs->calculateLocalInertia(mass, inertia); } break; case Ogre::Bullet::CT_CAPSULE: { - cs = new btCompoundShape(); - btScalar height = 1.0f; + cs = new btCompoundShape(false); + btScalar height = 1.0f; btScalar radius = 0.3f; - shape = new btCapsuleShape(radius, 2 * height - 2 * radius); + shape = new btCapsuleShape(radius, + 2 * height - 2 * radius); btTransform transform; transform.setIdentity(); transform.setOrigin(btVector3(0, 1, 0)); - static_cast(cs)->addChildShape(transform, shape); - btScalar masses[1] = {mass}; + static_cast(cs)->addChildShape( + transform, shape); + btScalar masses[1] = { mass }; btTransform principal; - static_cast(cs)->calculatePrincipalAxisTransform(masses, principal, inertia); + static_cast(cs) + ->calculatePrincipalAxisTransform( + masses, principal, inertia); } break; default: assert(false); @@ -138,7 +174,9 @@ public: #endif return body; } - btRigidBody *addKinematicRigidBody(float mass, Ogre::Entity *ent, Ogre::Bullet::ColliderType ct, int group = 1, int mask = 0xFFFF) + btRigidBody *addKinematicRigidBody(float mass, Ogre::Entity *ent, + Ogre::Bullet::ColliderType ct, + int group = 1, int mask = 0xFFFF) { return mDynWorld->addKinematicRigidBody(ent, ct, group, mask); } @@ -152,13 +190,14 @@ public: } void update(float delta) { - WorldData::get_singleton() - ->getBtWorld()->stepSimulation(delta, 50); - mDbgDraw->update(); + WorldData::get_singleton()->getBtWorld()->stepSimulation(delta, + 50); + mDbgDraw->update(); fps_time += delta; if (fps_time > 1.0f) { fps_time -= 1.0f; - const Ogre::RenderTarget::FrameStats &stats = mRenderWindow->getStatistics(); + const Ogre::RenderTarget::FrameStats &stats = + mRenderWindow->getStatistics(); std::cout << "FPS: " << stats.lastFPS << "\n"; } } @@ -173,90 +212,102 @@ public: }; WorldData *WorldData::singleton = nullptr; -class MainWorld : public Ogre::FrameListener -{ - btRigidBody *mFloorBody; -public: - void setup() - { -// mScnMgr = scnMgr; -// mDynWorld.reset(new Ogre::Bullet::DynamicsWorld(Ogre::Vector3(0, -9.8, 0))); -// mDbgDraw.reset(new Ogre::Bullet::DebugDrawer(mScnMgr->getRootSceneNode(), mDynWorld->getBtWorld())); - Ogre::MeshManager::getSingleton().createPlane("floor", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, - Ogre::Plane(Ogre::Vector3::UNIT_Y, 0), 100, 100, 10, 10, true, 1, 10, 10, Ogre::Vector3::UNIT_Z); +class MainWorld : public Ogre::FrameListener { + btRigidBody *mFloorBody; - // create a floor entity, give it a material, and place it at the origin - Ogre::SceneManager *scnMgr = WorldData::get_singleton()->getSceneManager(); - Ogre::Entity *floor = scnMgr->createEntity("Floor", "floor"); - scnMgr->getRootSceneNode()->attachObject(floor); - mFloorBody = WorldData::get_singleton()->addRigidBody(0, floor, Ogre::Bullet::CT_TRIMESH); - } - btRigidBody *addCharacter(Ogre::Entity *ent, float mass) - { - return WorldData::get_singleton()->addKinematicRigidBody(mass, ent, Ogre::Bullet::CT_COMPOUND); - } - bool frameStarted(const Ogre::FrameEvent& evt) override; +public: + void setup() + { + // mScnMgr = scnMgr; + // mDynWorld.reset(new Ogre::Bullet::DynamicsWorld(Ogre::Vector3(0, -9.8, 0))); + // mDbgDraw.reset(new Ogre::Bullet::DebugDrawer(mScnMgr->getRootSceneNode(), mDynWorld->getBtWorld())); + Ogre::MeshManager::getSingleton().createPlane( + "floor", + Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, + Ogre::Plane(Ogre::Vector3::UNIT_Y, 0), 100, 100, 10, 10, + true, 1, 10, 10, Ogre::Vector3::UNIT_Z); + + // create a floor entity, give it a material, and place it at the origin + Ogre::SceneManager *scnMgr = + WorldData::get_singleton()->getSceneManager(); + Ogre::Entity *floor = scnMgr->createEntity("Floor", "floor"); + scnMgr->getRootSceneNode()->attachObject(floor); + mFloorBody = WorldData::get_singleton()->addRigidBody( + 0, floor, Ogre::Bullet::CT_TRIMESH); + } + btRigidBody *addCharacter(Ogre::Entity *ent, float mass) + { + return WorldData::get_singleton()->addKinematicRigidBody( + mass, ent, Ogre::Bullet::CT_COMPOUND); + } + bool frameStarted(const Ogre::FrameEvent &evt) override; }; -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; +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; + Real mPivotPitch; + 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 + Real mTimer; // general timer to see how long animations have been playing + Ogre::Skeleton *mSkeleton; + bool mRunning; + MainWorld *world; + Ogre::Vector3 rootMotion; + Ogre::Quaternion rootRotation; + // btRigidBody *mRigidBody; + btCompoundShape *mCollisionShape; + btPairCachingGhostObject *mGhostObject; - Ogre::SceneNode *mCameraPivot; - Ogre::SceneNode *mCameraGoal, *mBodyNode; - Ogre::Entity *mBodyEnt; - Real mPivotPitch; - 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 - Real mTimer; // general timer to see how long animations have been playing - Ogre::Skeleton *mSkeleton; - bool mRunning; - MainWorld *world; - Ogre::Vector3 rootMotion; - Ogre::Quaternion rootRotation; -// btRigidBody *mRigidBody; - btCompoundShape *mCollisionShape; - btPairCachingGhostObject *mGhostObject; public: - CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam, Ogre::SceneManager *scnMgr, MainWorld *world); - ~CharacterController(); + CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam, + Ogre::SceneManager *scnMgr, MainWorld *world); + ~CharacterController(); + private: - void setupBody(); - void setupCamera(); - void setupAnimations(); + void setupBody(); + void setupCamera(); + void setupAnimations(); + 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; + 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 updateBody(Ogre::Real deltaTime); - void updateAnimations(Real deltaTime); - void updateRootMotion(Real deltaTime); - void fadeAnimations(Real deltaTime); - void updateCamera(Real deltaTime); - void updateCameraGoal(Real deltaYaw, Real deltaPitch, Real deltaZoom); - void setAnimation(AnimID id, bool reset = false); + void updateBody(Ogre::Real deltaTime); + void updateAnimations(Real deltaTime); + void updateRootMotion(Real deltaTime); + void fadeAnimations(Real deltaTime); + void updateCamera(Real deltaTime); + void updateCameraGoal(Real deltaYaw, Real deltaPitch, Real deltaZoom); + void setAnimation(AnimID id, bool reset = false); #if 0 struct testMotionResult { }; @@ -277,51 +328,51 @@ private: 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); - } - inline btVector3 convert(const Ogre::Vector3& v) - { - return btVector3(v.x, v.y, v.z); - } - inline btTransform convert(const Ogre::Quaternion& q, const Ogre::Vector3& v) - { - btQuaternion mq = convert(q); - btVector3 mv = convert(v); - return btTransform(mq, mv); - } - inline Ogre::Quaternion convert(const btQuaternion& q) - { - return Ogre::Quaternion(q.w(), q.x(), q.y(), q.z()); - } - inline Ogre::Vector3 convert(const btVector3& v) - { - return Ogre::Vector3(v.x(), v.y(), v.z()); - } - inline void convert(const btTransform &from, Ogre::Quaternion &q, Ogre::Vector3 &v) - { - q = convert(from.getRotation()); - v = convert(from.getOrigin()); - } + inline btQuaternion convert(const Ogre::Quaternion &q) + { + return btQuaternion(q.x, q.y, q.z, q.w); + } + inline btVector3 convert(const Ogre::Vector3 &v) + { + return btVector3(v.x, v.y, v.z); + } + inline btTransform convert(const Ogre::Quaternion &q, + const Ogre::Vector3 &v) + { + btQuaternion mq = convert(q); + btVector3 mv = convert(v); + return btTransform(mq, mv); + } + inline Ogre::Quaternion convert(const btQuaternion &q) + { + return Ogre::Quaternion(q.w(), q.x(), q.y(), q.z()); + } + inline Ogre::Vector3 convert(const btVector3 &v) + { + return Ogre::Vector3(v.x(), v.y(), v.z()); + } + inline void convert(const btTransform &from, Ogre::Quaternion &q, + Ogre::Vector3 &v) + { + q = convert(from.getRotation()); + v = convert(from.getOrigin()); + } }; -CharacterController::CharacterController( - Ogre::SceneNode *camNode, - Ogre::Camera *cam, - Ogre::SceneManager *scnMgr, - MainWorld *world) -: mCameraNode(camNode) -, mCamera(cam) -, mScnMgr(scnMgr) -, mPivotPitch(0) -, mVerticalVelocity(0) -, mAnimID(ANIM_NONE) -, mRunning(false) -, world(world) -, mCollisionShape(nullptr) -, mGhostObject(nullptr) +CharacterController::CharacterController(Ogre::SceneNode *camNode, + Ogre::Camera *cam, + Ogre::SceneManager *scnMgr, + MainWorld *world) + : mCameraNode(camNode) + , mCamera(cam) + , mScnMgr(scnMgr) + , mPivotPitch(0) + , mVerticalVelocity(0) + , mAnimID(ANIM_NONE) + , mRunning(false) + , world(world) + , mCollisionShape(nullptr) + , mGhostObject(nullptr) { - setupBody(); setupCamera(); setupAnimations(); @@ -331,37 +382,47 @@ 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); + 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); - Ogre::Bullet::KinematicMotionSimple *controller = new Ogre::Bullet::KinematicMotionSimple(mGhostObject, mBodyNode); - WorldData::get_singleton()->getWorld()->attachCollisionObject(mGhostObject, mBodyEnt, btBroadphaseProxy::AllFilter, btBroadphaseProxy::AllFilter); - WorldData::get_singleton()->getBtWorld()->addAction(controller); + { + 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); + Ogre::Bullet::KinematicMotionSimple *controller = + new Ogre::Bullet::KinematicMotionSimple(mGhostObject, + mBodyNode); + WorldData::get_singleton()->getWorld()->attachCollisionObject( + mGhostObject, mBodyEnt, btBroadphaseProxy::AllFilter, + btBroadphaseProxy::AllFilter); + WorldData::get_singleton()->getBtWorld()->addAction(controller); - assert(mCollisionShape); + assert(mCollisionShape); #if 0 if (mRigidBody->getMass() == 0) { #if 0 @@ -391,61 +452,66 @@ void CharacterController::setupBody() WorldData::get_singleton()->getBtWorld()->addAction(mController); } #endif - assert(mSkeleton->hasBone("Root")); - mRootBone = mSkeleton->getBone("Root"); - assert(mRootBone); + assert(mSkeleton->hasBone("Root")); + mRootBone = mSkeleton->getBone("Root"); + assert(mRootBone); } 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); + // 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; + 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(); + 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(); + // 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(); + } + 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++) { @@ -493,70 +559,77 @@ void CharacterController::setupAnimations() } // assert(false); #endif - setAnimation(ANIM_IDLE); + setAnimation(ANIM_IDLE); } -bool CharacterController::keyPressed(const OgreBites::KeyboardEvent& evt) +bool CharacterController::keyPressed(const OgreBites::KeyboardEvent &evt) { OgreBites::Keycode key = evt.keysym.sym; - if (key == 'q' && (mAnimID == ANIM_IDLE)) { + if (key == 'q' && (mAnimID == ANIM_IDLE)) { /* ... */ mTimer = 0; } else if (key == 'e') { } else if (key == 'w') mKeyDirection.z = -1; - else if (key == 'a') + else if (key == 'a') mKeyDirection.x = -1; - else if (key == 's') + else if (key == 's') mKeyDirection.z = 1; - else if (key == 'd') + else if (key == 'd') mKeyDirection.x = 1; if (key == OgreBites::SDLK_LSHIFT) mRunning = true; - if (!mKeyDirection.isZeroLength() && mAnimID == ANIM_IDLE) { + 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) + } else if (!mKeyDirection.isZeroLength() && mAnimID == ANIM_WALK && + mRunning) setAnimation(ANIM_RUN); return true; } -bool CharacterController::keyReleased(const OgreBites::KeyboardEvent& evt) +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 == '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) + if (mKeyDirection.isZeroLength() && + (mAnimID == ANIM_WALK || mAnimID == ANIM_RUN)) + setAnimation(ANIM_IDLE); + else if (!mKeyDirection.isZeroLength() && mAnimID == ANIM_RUN && + !mRunning) setAnimation(ANIM_WALK); - return true; + return true; } -bool CharacterController::mouseMoved(const OgreBites::MouseMotionEvent& evt) +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) +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) +bool CharacterController::mousePressed(const OgreBites::MouseButtonEvent &evt) { std::cout << "Mouse press\n"; return false; } -void CharacterController::frameRendered(const Ogre::FrameEvent& evt) +void CharacterController::frameRendered(const Ogre::FrameEvent &evt) { updateBody(evt.timeSinceLastFrame); updateAnimations(evt.timeSinceLastFrame); @@ -564,24 +637,27 @@ void CharacterController::frameRendered(const Ogre::FrameEvent& evt) if (evt.timeSinceLastFrame > 0) updateRootMotion(evt.timeSinceLastFrame); } -bool CharacterController::frameStarted(const Ogre::FrameEvent& evt) +bool CharacterController::frameStarted(const Ogre::FrameEvent &evt) { return true; } -void CharacterController::updateCameraGoal(Real deltaYaw, Real deltaPitch, Real deltaZoom) +void CharacterController::updateCameraGoal(Real deltaYaw, Real deltaPitch, + 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 < -60 && deltaPitch < 0)) { + mCameraPivot->pitch(Ogre::Degree(deltaPitch), + Ogre::Node::TS_LOCAL); mPivotPitch += deltaPitch; } - Real dist = mCameraGoal->_getDerivedPosition().distance(mCameraPivot->_getDerivedPosition()); + Real dist = mCameraGoal->_getDerivedPosition().distance( + mCameraPivot->_getDerivedPosition()); Real distChange = deltaZoom * dist; // bound the zoom if (!(dist + distChange < 8 && distChange < 0) && - !(dist + distChange > 25 && distChange > 0)) + !(dist + distChange > 25 && distChange > 0)) mCameraGoal->translate(0, 0, distChange, Ogre::Node::TS_LOCAL); } void CharacterController::updateBody(Real delta) @@ -589,22 +665,35 @@ void CharacterController::updateBody(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 += + mKeyDirection.z * mCameraNode->getOrientation().zAxis(); + mGoalDirection += + mKeyDirection.x * mCameraNode->getOrientation().xAxis(); mGoalDirection.y = 0; mGoalDirection.normalise(); - Ogre::Quaternion toGoal = mBodyNode->getOrientation().zAxis().getRotationTo(mGoalDirection); + Ogre::Quaternion toGoal = + mBodyNode->getOrientation().zAxis().getRotationTo( + mGoalDirection); // calculate how much the character has to turn to face goal direction Real yawToGoal = toGoal.getYaw().valueDegrees(); // this is how much the character CAN turn this frame - Real yawAtSpeed = yawToGoal / Math::Abs(yawToGoal) * delta * TURN_SPEED; + Real yawAtSpeed = + yawToGoal / 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); + 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); + 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(), @@ -634,7 +723,7 @@ void CharacterController::updateAnimations(Real delta) int i, j, k; Real animSpeed = 1; mTimer += delta; - if (mAnimID != ANIM_NONE) { + if (mAnimID != ANIM_NONE) { if (mAnimID == ANIM_WALK) mAnims[mAnimID]->addTime(delta * 1.0f); else @@ -669,43 +758,46 @@ void CharacterController::updateRootMotion(Real delta) btTransform from(convert(mBodyNode->getOrientation()), convert(mBodyNode->getPosition())); mBodyNode->setPosition(mBodyNode->getPosition() + rotMotion); #endif -// WorldData::get_singleton()->getWorld()->testBodyMotion(mRigidBody, from, Ogre::Bullet::convert(rotMotion), true, -// nullptr, false, std::set()); + // WorldData::get_singleton()->getWorld()->testBodyMotion(mRigidBody, from, Ogre::Bullet::convert(rotMotion), true, + // nullptr, false, std::set()); } void CharacterController::fadeAnimations(Real delta) { int i; for (i = 0; i < NUM_ANIMS; i++) { - if (mFadingIn[i]) - { - // slowly fade this animation in until it has full weight - Real newWeight = mAnims[i]->getWeight() + delta * ANIM_FADE_SPEED; - mAnims[i]->setWeight(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 - Real newWeight = mAnims[i]->getWeight() - delta * ANIM_FADE_SPEED; - mAnims[i]->setWeight(Math::Clamp(newWeight, 0, 1)); - if (newWeight <= 0) - { - mAnims[i]->setEnabled(false); - mFadingOut[i] = false; - } - } - } + if (mFadingIn[i]) { + // slowly fade this animation in until it has full weight + Real newWeight = mAnims[i]->getWeight() + + delta * ANIM_FADE_SPEED; + mAnims[i]->setWeight( + 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 + Real newWeight = mAnims[i]->getWeight() - + delta * ANIM_FADE_SPEED; + mAnims[i]->setWeight( + Math::Clamp(newWeight, 0, 1)); + if (newWeight <= 0) { + mAnims[i]->setEnabled(false); + mFadingOut[i] = false; + } + } + } } void CharacterController::updateCamera(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); + // 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) { @@ -761,45 +853,42 @@ bool CharacterController::bodyTestMotion(btRigidBody *body, } #endif -class KeyHandler : public OgreBites::InputListener -{ - bool keyPressed(const OgreBites::KeyboardEvent& evt) override - { - if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) - { - Ogre::Root::getSingleton().queueEndRendering(); - } - return true; - } +class KeyHandler : public OgreBites::InputListener { + bool keyPressed(const OgreBites::KeyboardEvent &evt) override + { + if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) { + Ogre::Root::getSingleton().queueEndRendering(); + } + return true; + } }; -bool MainWorld::frameStarted(const Ogre::FrameEvent& evt) +bool MainWorld::frameStarted(const Ogre::FrameEvent &evt) { WorldData::get_singleton()->update(evt.timeSinceLastFrame); return true; } -class App - : public OgreBites::ApplicationContext -{ - Ogre::SceneNode *mCameraNode; - Ogre::SceneManager *mScnMgr; - std::unique_ptr mCharacter; - KeyHandler mKeyHandler; - MainWorld mWorld; - LuaControl lua; -public: - App(); - virtual ~App(); +class App : public OgreBites::ApplicationContext { + Ogre::SceneNode *mCameraNode; + Ogre::SceneManager *mScnMgr; + std::unique_ptr mCharacter; + KeyHandler mKeyHandler; + MainWorld mWorld; + LuaControl lua; - void setup(); - void locateResources(); - void initCamera(); - Ogre::SceneManager *getSceneManager() - { - return mScnMgr; - } - void createContent(); - void createCharacter(); - void setupWorld(); +public: + App(); + virtual ~App(); + + void setup(); + void locateResources(); + void initCamera(); + Ogre::SceneManager *getSceneManager() + { + return mScnMgr; + } + void createContent(); + void createCharacter(); + void setupWorld(); }; App::App() @@ -822,38 +911,40 @@ App::~App() } void App::initCamera() { - // also need to tell where we are - mCameraNode = mScnMgr->getRootSceneNode()->createChildSceneNode(); - mCameraNode->setPosition(0, 2, 3); - mCameraNode->lookAt(Ogre::Vector3(0, 1, -1), Ogre::Node::TS_PARENT); + // also need to tell where we are + mCameraNode = mScnMgr->getRootSceneNode()->createChildSceneNode(); + mCameraNode->setPosition(0, 2, 3); + mCameraNode->lookAt(Ogre::Vector3(0, 1, -1), Ogre::Node::TS_PARENT); - // create the camera - Ogre::Camera *cam = mScnMgr->createCamera("tps_camera"); - cam->setNearClipDistance(0.1f); // specific to this sample - cam->setAutoAspectRatio(true); - mCameraNode->attachObject(cam); + // create the camera + Ogre::Camera *cam = mScnMgr->createCamera("tps_camera"); + cam->setNearClipDistance(0.1f); // specific to this sample + cam->setAutoAspectRatio(true); + mCameraNode->attachObject(cam); - // and tell it to render into the main window - getRenderWindow()->addViewport(cam); + // and tell it to render into the main window + getRenderWindow()->addViewport(cam); } void App::setupWorld() { - addInputListener(&mKeyHandler); + addInputListener(&mKeyHandler); mWorld.setup(); - getRoot()->addFrameListener(&mWorld); + getRoot()->addFrameListener(&mWorld); } void App::createCharacter() { - Ogre::Camera *cam = static_cast(mCameraNode->getAttachedObject("tps_camera")); - mCharacter = std::make_unique(mCameraNode, cam, mScnMgr, &mWorld); -// mInputListenerChain = TouchAgnosticInputListenerChain(getRenderWindow(), {&mKeyHandler, mCharacter.get()}); - addInputListener(mCharacter.get()); - WorldData::get_singleton()->initPagedWorld(cam); + Ogre::Camera *cam = static_cast( + mCameraNode->getAttachedObject("tps_camera")); + mCharacter = std::make_unique(mCameraNode, cam, + mScnMgr, &mWorld); + // mInputListenerChain = TouchAgnosticInputListenerChain(getRenderWindow(), {&mKeyHandler, mCharacter.get()}); + addInputListener(mCharacter.get()); + WorldData::get_singleton()->initPagedWorld(cam); } void App::createContent() { - lua.setup(mScnMgr); - /* + lua.setup(mScnMgr); + /* Ogre::OverlaySystem* overlaySystem = Ogre::OverlaySystem::getSingletonPtr(); Ogre::OverlayElement* fpsOverlayElement = overlaySystem->getOverlayManager()->getOverlay("MyFpsOverlay"); if (fpsOverlayElement != nullptr) { @@ -864,42 +955,43 @@ void App::createContent() } } */ - // without light we would just get a black screen - Ogre::Light* light = mScnMgr->createLight(lua.dlight_name); - Ogre::SceneNode* lightNode = mScnMgr->getRootSceneNode()->createChildSceneNode(); -// lightNode->setPosition(0, 10, 15); - lightNode->setDirection(lua.dlight_direction); - lightNode->attachObject(light); - light->setType(Ogre::Light::LT_DIRECTIONAL); - light->setDiffuseColour(lua.diffuse); - light->setSpecularColour(lua.specular); - mScnMgr->setSkyBox(true, lua.skybox_name, lua.skybox_size); + // without light we would just get a black screen + Ogre::Light *light = mScnMgr->createLight(lua.dlight_name); + Ogre::SceneNode *lightNode = + mScnMgr->getRootSceneNode()->createChildSceneNode(); + // lightNode->setPosition(0, 10, 15); + lightNode->setDirection(lua.dlight_direction); + lightNode->attachObject(light); + light->setType(Ogre::Light::LT_DIRECTIONAL); + light->setDiffuseColour(lua.diffuse); + light->setSpecularColour(lua.specular); + mScnMgr->setSkyBox(true, lua.skybox_name, lua.skybox_size); } int main(int argc, char *argv[]) { - App ctx; - ctx.initApp(); - // get a pointer to the already created root - Ogre::Root* root = ctx.getRoot(); - Ogre::SceneManager* scnMgr = ctx.getSceneManager(); + App ctx; + ctx.initApp(); + // get a pointer to the already created root + Ogre::Root *root = ctx.getRoot(); + Ogre::SceneManager *scnMgr = ctx.getSceneManager(); - // register our scene with the RTSS - Ogre::RTShader::ShaderGenerator* shadergen = Ogre::RTShader::ShaderGenerator::getSingletonPtr(); - shadergen->addSceneManager(scnMgr); - WorldData::init(root, scnMgr, ctx.getRenderWindow()); - ctx.setWindowGrab(true); - ctx.createContent(); - ctx.initCamera(); - ctx.setupWorld(); - ctx.createCharacter(); - // register for input events -// KeyHandler keyHandler; -// ctx.addInputListener(&keyHandler); + // register our scene with the RTSS + Ogre::RTShader::ShaderGenerator *shadergen = + Ogre::RTShader::ShaderGenerator::getSingletonPtr(); + shadergen->addSceneManager(scnMgr); + WorldData::init(root, scnMgr, ctx.getRenderWindow()); + ctx.setWindowGrab(true); + ctx.createContent(); + ctx.initCamera(); + ctx.setupWorld(); + ctx.createCharacter(); + // register for input events + // KeyHandler keyHandler; + // ctx.addInputListener(&keyHandler); - ctx.getRoot()->startRendering(); - ctx.setWindowGrab(false); - ctx.closeApp(); - WorldData::cleanup(); - return 0; + ctx.getRoot()->startRendering(); + ctx.setWindowGrab(false); + ctx.closeApp(); + WorldData::cleanup(); + return 0; } - diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..6304fce --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,8 @@ +project(tests) +find_package(OGRE REQUIRED COMPONENTS Bullet CONFIG) +find_package(Bullet REQUIRED) +add_executable(compound_shapes compound_shapes.cpp) +target_link_libraries(compound_shapes OgreBullet ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY}) +include_directories(${BULLET_INCLUDE_DIRS}) + +add_custom_target(tests ALL DEPENDS compound_shapes) diff --git a/tests/compound_shapes.cpp b/tests/compound_shapes.cpp new file mode 100644 index 0000000..4d7c0ae --- /dev/null +++ b/tests/compound_shapes.cpp @@ -0,0 +1,290 @@ +#include +#include +#include +#include +#include + +struct objects { + btDynamicsWorld *world; + btRigidBody *groundBody; + btPairCachingGhostObject *characterBody; + btPairCachingGhostObject *waterBody; +}; +void setupGround(btDynamicsWorld *world, struct objects *objects) +{ + // Static ground + btCollisionShape *groundShape = + new btBoxShape(btVector3(1000, 1, 1000)); + btDefaultMotionState *groundMotionState = new btDefaultMotionState( + btTransform(btQuaternion(0, 0, 0, 1), btVector3(0, -10, 0))); + btRigidBody::btRigidBodyConstructionInfo groundRigidBodyCI( + 0, groundMotionState, groundShape, btVector3(0, 0, 0)); + btRigidBody *groundRigidBody = new btRigidBody(groundRigidBodyCI); + world->addRigidBody(groundRigidBody, 2, 0x7fffffff & ~16); + objects->groundBody = groundRigidBody; +} +void setupCharacter(btDynamicsWorld *world, struct objects *objects) +{ + btCompoundShape *shape = new btCompoundShape(); + btCapsuleShape *subshape = new btCapsuleShape(0.2f, 1.5f); + btTransform shapePos; + shapePos.setIdentity(); + shapePos.setOrigin(btVector3(0, 0.75f, 0)); + shape->addChildShape(shapePos, subshape); + btPairCachingGhostObject *characterBody = + new btPairCachingGhostObject(); + characterBody->setCollisionFlags( + characterBody->getCollisionFlags() | + btCollisionObject::CF_NO_CONTACT_RESPONSE | + btCollisionObject::CF_KINEMATIC_OBJECT); + btTransform bodyPos; + bodyPos.setIdentity(); + bodyPos.setOrigin(btVector3(0, 0.4f, 0)); + characterBody->setWorldTransform(bodyPos); + characterBody->setCollisionShape(shape); + world->addCollisionObject(characterBody, 1, 0x7fffffff); + objects->characterBody = characterBody; +} +void setupWater(btDynamicsWorld *world, struct objects *objects) +{ + btCompoundShape *shape = new btCompoundShape(); + btBoxShape *subshape = + new btBoxShape(btVector3(1000.0f, 100.0f, 1000.0f)); + btTransform shapePos; + shapePos.setIdentity(); + shapePos.setOrigin(btVector3(0, -100, 0)); + shape->addChildShape(shapePos, subshape); + btPairCachingGhostObject *waterBody = new btPairCachingGhostObject(); + waterBody->setCollisionFlags(waterBody->getCollisionFlags() | + btCollisionObject::CF_NO_CONTACT_RESPONSE | + btCollisionObject::CF_KINEMATIC_OBJECT); + btTransform bodyPos; + bodyPos.setIdentity(); + waterBody->setWorldTransform(bodyPos); + waterBody->setCollisionShape(shape); + world->addCollisionObject(waterBody, 16, 0x7fffffff & ~2); + objects->waterBody = waterBody; +} +struct DeepPenetrationContactResultCallback : public btManifoldResult { + DeepPenetrationContactResultCallback( + const btCollisionObjectWrapper *body0Wrap, + const btCollisionObjectWrapper *body1Wrap) + : btManifoldResult(body0Wrap, body1Wrap) + , mPenetrationDistance(0) + , mOtherIndex(0) + { + } + float mPenetrationDistance; + int mOtherIndex; + btVector3 mNormal, mPoint; + void reset() + { + mPenetrationDistance = 0.0f; + } + bool hasHit() + { + return mPenetrationDistance < 0.0f; + } + virtual void addContactPoint(const btVector3 &normalOnBInWorld, + const btVector3 &pointInWorldOnB, + btScalar depth) + { + std::cout + << "contact: " << Ogre::Bullet::convert(pointInWorldOnB) + << " " << Ogre::Bullet::convert(normalOnBInWorld) + << "\n"; + if (mPenetrationDistance > depth) { // Has penetration? + + const bool isSwapped = + m_manifoldPtr->getBody0() != + m_body0Wrap->getCollisionObject(); + mPenetrationDistance = depth; + mOtherIndex = isSwapped ? m_index0 : m_index1; + mPoint = isSwapped ? (pointInWorldOnB + + (normalOnBInWorld * depth)) : + pointInWorldOnB; + + mNormal = isSwapped ? normalOnBInWorld * -1 : + normalOnBInWorld; + } + } +}; +void dumpCompoundShape(const char *what, const btCollisionObject *body) +{ + if (body->getCollisionShape()->isCompound()) { + const btCompoundShape *shape = + static_cast( + body->getCollisionShape()); + int i; + for (i = 0; i < shape->getNumChildShapes(); i++) { + btTransform transform = body->getWorldTransform() * + shape->getChildTransform(i); + std::cout << what << ": " << " shape: " << i << ": " + << transform.getOrigin().getX() << ", "; + std::cout << transform.getOrigin().getY() << ", "; + std::cout << transform.getOrigin().getZ(); + std::cout << " convex: " + << shape->getChildShape(i)->isConvex(); + std::cout << " shape name: " + << shape->getChildShape(i)->getName(); + switch (shape->getChildShape(i)->getShapeType()) { + case 0: { + const btBoxShape *box = + static_cast( + shape->getChildShape(i)); + btVector3 hextents = + box->getHalfExtentsWithoutMargin(); + std::cout << " box: " << hextents.getX() << ", " + << hextents.getY() << ", " + << hextents.getZ(); + } break; + case 10: { + const btCapsuleShape *capsule = + static_cast( + shape->getChildShape(i)); + float hh = capsule->getHalfHeight(); + float r = capsule->getRadius(); + std::cout << " capsule: " << hh << ", " << r; + } break; + default: + std::cout << " shape type: " + << shape->getChildShape(i) + ->getShapeType(); + break; + } + std::cout << std::endl; + } + } +} +void showContacts(btCollisionWorld *world, struct objects *objects) +{ + int numManifolds = world->getDispatcher()->getNumManifolds(); + if (numManifolds == 0) + std::cout << "No contacts in world\n"; + for (int i = 0; i < numManifolds; i++) { + btPersistentManifold *contactManifold = + world->getDispatcher()->getManifoldByIndexInternal(i); + const btCollisionObject *obA = contactManifold->getBody0(); + const btCollisionObject *obB = contactManifold->getBody1(); + + int numContacts = contactManifold->getNumContacts(); + for (int j = 0; j < numContacts; j++) { + btManifoldPoint &pt = + contactManifold->getContactPoint(j); + if (pt.getDistance() < 0.f) { + const btVector3 &ptA = pt.getPositionWorldOnA(); + const btVector3 &ptB = pt.getPositionWorldOnB(); + const btVector3 &normalOnB = + pt.m_normalWorldOnB; + std::cout << "contact: " << i << " " << j << " " + << ptA.getX() << ", " << ptA.getY() + << ", " << ptA.getZ() << std::endl; + } + } + } +} +int main() +{ + struct objects objects; + btDefaultCollisionConfiguration collisionConfig; + btCollisionDispatcher dispatcher(&collisionConfig); + btCollisionDispatcher *dispatch = &dispatcher; + btDbvtBroadphase broadphase; + btSequentialImpulseConstraintSolver solver; + btDiscreteDynamicsWorld world(&dispatcher, &broadphase, &solver, + &collisionConfig); + world.setGravity(btVector3(0, -9.81, 0)); + setupGround(&world, &objects); + setupCharacter(&world, &objects); + setupWater(&world, &objects); + +#if 0 + btCompoundShape *compoundShape = new btCompoundShape(); + + // Add shapes to the compound shape + for (int i = 0; i < 2; ++i) { + btBoxShape *box = new btBoxShape(btVector3(1, 1, 1)); + btTransform transform; + transform.setIdentity(); + transform.setOrigin(btVector3(0, 1 - i, 0)); + compoundShape->addChildShape(transform, box); + } + + btDefaultMotionState *compoundMotionState = new btDefaultMotionState( + btTransform(btQuaternion(0, 0, 0, 1), btVector3(0, 10, 0))); + btScalar mass = 5.0; + btVector3 inertia(0, 0, 0); + compoundShape->calculateLocalInertia(mass, inertia); + btRigidBody::btRigidBodyConstructionInfo compoundRigidBodyCI( + mass, compoundMotionState, compoundShape, inertia); + btRigidBody *compoundRigidBody = new btRigidBody(compoundRigidBodyCI); + world.addRigidBody(compoundRigidBody); +#endif + + // Simulation + btVector3 velocity(0, 0, 0); + for (int i = 0; i < 300; i++) { + world.stepSimulation(1.f / 60.f, 10); + showContacts(&world, &objects); + +#if 0 + btTransform transform; + compoundRigidBody->getMotionState()->getWorldTransform( + transform); + std::cout << "Step " << i + << ": Position = " << transform.getOrigin().getX() + << ", " << transform.getOrigin().getY() << ", " + << transform.getOrigin().getZ() << std::endl; + std::cout << "water overlaps:" + << objects.waterBody->getOverlappingPairCache() + ->getNumOverlappingPairs() + << "\n"; +#endif + btTransform transform; + transform = objects.characterBody->getWorldTransform(); + std::cout << "Step " << i << " "; + std::cout << "Character: " + << "Position = " << transform.getOrigin().getX() + << ", " << transform.getOrigin().getY() << ", " + << transform.getOrigin().getZ() << std::endl; + transform = objects.waterBody->getWorldTransform(); + std::cout << "Water: " + << "Position = " << transform.getOrigin().getX() + << ", " << transform.getOrigin().getY() << ", " + << transform.getOrigin().getZ() << std::endl; + std::cout << "water overlaps:" + << objects.waterBody->getOverlappingPairCache() + ->getNumOverlappingPairs() + << "\n"; + dumpCompoundShape("Character", objects.characterBody); + dumpCompoundShape("Water", objects.waterBody); + btCollisionObjectWrapper obA( + NULL, objects.waterBody->getCollisionShape(), + objects.waterBody, + objects.waterBody->getWorldTransform(), -1, 0); + btCollisionObjectWrapper obB( + NULL, objects.characterBody->getCollisionShape(), + objects.characterBody, + objects.characterBody->getWorldTransform(), -1, 0); + std::cout << __func__ << ": call findAlgorithm: "; + btCollisionAlgorithm *algorithm = dispatch->findAlgorithm( + &obA, &obB, NULL, BT_CONTACT_POINT_ALGORITHMS); + std::cout << "found algorithm: " << algorithm << "\n"; + if (!algorithm) + continue; + std::cout << "algorithm: " << algorithm << "\n"; + DeepPenetrationContactResultCallback contactPointResult(&obA, + &obB); + std::cout << "process collision\n"; + algorithm->processCollision(&obA, &obB, world.getDispatchInfo(), + &contactPointResult); + algorithm->~btCollisionAlgorithm(); + dispatch->freeCollisionAlgorithm(algorithm); + if (contactPointResult.hasHit()) { + std::cout << "InWater!!1\n"; + } + velocity += btVector3(0, -9.8, 0) * 1.0f / 16.0f; + objects.characterBody->getWorldTransform().getOrigin() += velocity * 1.0f / 16.0f; + std::cout << "process collision done\n"; + } +}