diff --git a/Bootstrap.cpp b/Bootstrap.cpp index f43e4c0..edac345 100644 --- a/Bootstrap.cpp +++ b/Bootstrap.cpp @@ -7,9 +7,6 @@ #include "Ogre.h" #include "OgreApplicationContext.h" -#include "Bullet/OgreBullet.h" -#include "BulletCollision/CollisionDispatch/btGhostObject.h" -#include "LinearMath/btTransform.h" #include "OgrePageManager.h" #define CAM_HEIGHT 1.6f // height of camera above character's center of mass @@ -21,11 +18,15 @@ using Real = Ogre::Real; using Math = Ogre::Math; class WorldData { +#if 0 std::unique_ptr mDynWorld; std::unique_ptr mDbgDraw; +#endif std::unique_ptr mRoot; std::unique_ptr mScnMgr; +#if 0 std::unique_ptr mbtWorld; +#endif std::unique_ptr mPageManager; Ogre::PagedWorld *mPagedWorld; @@ -61,13 +62,14 @@ private: DummyPageProvider mDummyPageProvider; WorldData(Ogre::Root *root, Ogre::SceneManager *scnMgr) - : mDynWorld(new Ogre::Bullet::DynamicsWorld( + : /*mDynWorld(new Ogre::Bullet::DynamicsWorld( Ogre::Vector3(0, -9.8, 0))) , mDbgDraw(new Ogre::Bullet::DebugDrawer( scnMgr->getRootSceneNode(), mDynWorld->getBtWorld())) - , mRoot(root) + , */ + mRoot(root) , mScnMgr(scnMgr) - , mbtWorld(mDynWorld->getBtWorld()) + /*, mbtWorld(mDynWorld->getBtWorld()) */ , mPageManager(nullptr) , mPagedWorld(nullptr) { @@ -122,6 +124,7 @@ public: #endif return ghost; } +#if 0 btRigidBody *addRigidBody(float mass, Ogre::Entity *ent, Ogre::Bullet::ColliderType ct, int group = 1, int mask = 0xFFFF) @@ -182,11 +185,14 @@ public: { return mDynWorld.get(); } +#endif void update(float delta) { +#if 0 WorldData::get_singleton()->getBtWorld()->stepSimulation(delta, 10); mDbgDraw->update(); +#endif } void initPagedWorld(Ogre::Camera *camera) { @@ -200,7 +206,7 @@ public: WorldData *WorldData::singleton = nullptr; class MainWorld : public Ogre::FrameListener { - btRigidBody *mFloorBody; + // btRigidBody *mFloorBody; public: void setup() @@ -219,14 +225,18 @@ public: WorldData::get_singleton()->getSceneManager(); Ogre::Entity *floor = scnMgr->createEntity("Floor", "floor"); scnMgr->getRootSceneNode()->attachObject(floor); +#if 0 mFloorBody = WorldData::get_singleton()->addRigidBody( 0, floor, Ogre::Bullet::CT_TRIMESH); +#endif } +#if 0 btRigidBody *addCharacter(Ogre::Entity *ent, float mass) { return WorldData::get_singleton()->addKinematicRigidBody( mass, ent, Ogre::Bullet::CT_COMPOUND); } +#endif bool frameStarted(const Ogre::FrameEvent &evt) override; }; class CharacterController : public OgreBites::InputListener, @@ -265,8 +275,10 @@ class CharacterController : public OgreBites::InputListener, Ogre::Vector3 rootMotion; Ogre::Quaternion rootRotation; // btRigidBody *mRigidBody; +#if 0 btCompoundShape *mCollisionShape; btPairCachingGhostObject *mGhostObject; +#endif public: CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam, @@ -315,6 +327,7 @@ private: recoverResult *recover_result, const std::set &exclude); #endif +#if 0 inline btQuaternion convert(const Ogre::Quaternion &q) { return btQuaternion(q.x, q.y, q.z, q.w); @@ -344,6 +357,7 @@ private: q = convert(from.getRotation()); v = convert(from.getOrigin()); } +#endif }; CharacterController::CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam, diff --git a/CMakeLists.txt b/CMakeLists.txt index a8c01e1..e06de69 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,11 +27,10 @@ file(GLOB WATER_SRC ${CMAKE_SOURCE_DIR}/water/*.cpp) # The COMPONENTS part checks that OGRE was built the way we need it # The CONFIG flag makes sure we get OGRE instead of OGRE-next -find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain CONFIG) +find_package(OGRE REQUIRED COMPONENTS Bites Paging Terrain CONFIG) find_package(ZLIB) find_package(SDL2) find_package(assimp REQUIRED CONFIG) -find_package(Bullet) find_package(OgreProcedural REQUIRED CONFIG) find_package(pugixml REQUIRED CONFIG) find_package(flecs REQUIRED CONFIG) @@ -66,46 +65,24 @@ set_target_properties(fix::pugixml PROPERTIES add_subdirectory(src/lua) -add_subdirectory(src/characters) add_subdirectory(src/gamedata) add_subdirectory(src/miniaudio) add_subdirectory(src/sound) add_subdirectory(src/sceneloader) add_subdirectory(audio/gui) -add_subdirectory(tests) add_subdirectory(lua-scripts) add_subdirectory(morph) add_subdirectory(src/world) add_subdirectory(src/tests) +add_subdirectory(src/physics) -# add the source files as usual -add_executable(0_Bootstrap Bootstrap.cpp) - -# this also sets the includes and pulls third party dependencies -target_link_libraries(0_Bootstrap OgreBites OgreBullet OgrePaging ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY} ${ASSIMP_LIBRARIES} - -Wl,--as-needed - ) -if(OGRE_STATIC) -target_link_options(0_Bootstrap PRIVATE -static-libstdc++ -static-libgcc) -endif() -add_dependencies(0_Bootstrap stage_files import_vrm) - -add_executable(Editor Editor.cpp ${WATER_SRC}) -target_link_libraries(Editor OgreBites OgreBullet OgrePaging OgreTerrain OgreMeshLodGenerator OgreProcedural::OgreProcedural ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY} - -Wl,--as-needed - ) -if(OGRE_STATIC) -target_link_options(Editor PRIVATE -static-libstdc++ -static-libgcc) -endif() -add_dependencies(Editor stage_files import_buildings import_water_stuff import_vehicles import_vrm) add_executable(Game Game.cpp ${WATER_SRC}) target_include_directories(Game PRIVATE src/gamedata) -target_link_libraries(Game OgreBites OgreBullet OgrePaging OgreTerrain OgreMeshLodGenerator - OgreProcedural::OgreProcedural ${BULLET_DYNAMICS_LIBRARY} - ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY} +target_link_libraries(Game OgreBites OgrePaging OgreTerrain OgreMeshLodGenerator + OgreProcedural::OgreProcedural GameData sound - sceneloader + sceneloader physics flecs::flecs_static -Wl,--as-needed ) @@ -115,9 +92,8 @@ endif() add_dependencies(Game stage_files import_buildings import_water_stuff import_vehicles import_vrm audio_data_gui) add_executable(Procedural Procedural.cpp) -target_link_libraries(Procedural OgreBites OgreBullet OgrePaging OgreTerrain - OgreProcedural::OgreProcedural ${BULLET_DYNAMICS_LIBRARY} - ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY} +target_link_libraries(Procedural OgreBites OgrePaging OgreTerrain + OgreProcedural::OgreProcedural -Wl,--as-needed ) if(OGRE_STATIC) @@ -205,7 +181,7 @@ list(APPEND WATER_STUFF ${CMAKE_BINARY_DIR}/water/sea.glb) add_custom_target(import_water_stuff ALL DEPENDS ${WATER_STUFF}) add_executable(TerrainTest terrain.cpp) -target_link_libraries(TerrainTest OgreBites OgreBullet OgrePaging OgreTerrain lua ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY} +target_link_libraries(TerrainTest OgreBites OgrePaging OgreTerrain lua -Wl,--as-needed ) target_include_directories(TerrainTest PRIVATE . src/terrain src/lua src/lua/lua-5.4.8/src) @@ -213,8 +189,6 @@ if(OGRE_STATIC) target_link_libraries(TerrainTest fix::assimp pugixml) target_link_options(TerrainTest PRIVATE -static-libstdc++ -static-libgcc) target_link_libraries(Procedural fix::assimp pugixml) - target_link_libraries(0_Bootstrap fix::assimp pugixml) - target_link_libraries(Editor fix::assimp pugixml) endif() add_dependencies(TerrainTest stage_lua_scripts stage_files) @@ -407,6 +381,7 @@ add_custom_target(stage_files ALL DEPENDS ${CMAKE_BINARY_DIR}/resources.cfg ${MA add_custom_target(remove_scenes COMMAND rm -f ${VRM_SOURCE} ${VRM_IMPORTED_BLENDS} ${CHARACTER_GLBS}) add_custom_target(import_vrm DEPENDS ${CHARACTER_GLBS}) +target_compile_definitions(Game PRIVATE FLECS_CPP_NO_AUTO_REGISTRATION) -install(TARGETS Editor DESTINATION bin) +install(TARGETS Game DESTINATION bin) diff --git a/Editor.cpp b/Editor.cpp index 0676873..f67d235 100644 --- a/Editor.cpp +++ b/Editor.cpp @@ -1,7 +1,6 @@ #include #include -#include #include #include #include @@ -245,8 +244,6 @@ public: void initGui(); }; class App : public OgreBites::ApplicationContext { - std::unique_ptr mDynWorld; - std::unique_ptr mDbgDraw; Ogre::SceneNode *mCameraNode, *mCameraPivot, *mCameraGoal; Ogre::Camera *mCamera; Ogre::Real mPivotPitch; @@ -376,8 +373,6 @@ public: : OgreBites::ApplicationContext("ChoroEditor") , mKbd(this) , m_edit_ui(this) - , mDynWorld(new Ogre::Bullet::DynamicsWorld( - Ogre::Vector3(0, -9.8, 0))) { } virtual ~App() @@ -393,8 +388,6 @@ public: mScnMgr->addRenderQueueListener(pOverlaySystem); // mTrayMgr = new OgreBites::TrayManager("AppTrays", // getRenderWindow()); - mDbgDraw.reset(new Ogre::Bullet::DebugDrawer( - mScnMgr->getRootSceneNode(), mDynWorld->getBtWorld())); } void locateResources() override { @@ -455,8 +448,10 @@ public: std::cout << "Init camera" << "\n"; initCamera(); std::cout << "Set up water" << "\n"; +#if 0 m_water.createWater(getRenderWindow(), mCamera, mDynWorld.get()); +#endif std::cout << "Set up cursor" << "\n"; Ogre::ResourceGroupManager::getSingleton() .initialiseAllResourceGroups(); diff --git a/Game.cpp b/Game.cpp index 2d46a8a..8d36f9d 100644 --- a/Game.cpp +++ b/Game.cpp @@ -289,8 +289,6 @@ public: void frameRendered(const Ogre::FrameEvent &evt) override; }; class App : public OgreBites::ApplicationContext { - std::unique_ptr mDynWorld; - std::unique_ptr mDbgDraw; Ogre::SceneNode *mCameraNode, *mCameraPivot, *mCameraGoal; Ogre::Camera *mCamera; Ogre::Real mPivotPitch; @@ -304,8 +302,6 @@ public: App() : OgreBites::ApplicationContext("ChoroGame") , mKbd(this) - , mDynWorld(new Ogre::Bullet::DynamicsWorld( - Ogre::Vector3(0, -9.8, 0))) , mGrab(false) { } @@ -322,8 +318,6 @@ public: mScnMgr->addRenderQueueListener(pOverlaySystem); // mTrayMgr = new OgreBites::TrayManager("AppTrays", // getRenderWindow()); - mDbgDraw.reset(new Ogre::Bullet::DebugDrawer( - mScnMgr->getRootSceneNode(), mDynWorld->getBtWorld())); } bool isWindowGrab() { @@ -432,7 +426,9 @@ public: } void updateWorld(float delta) { +#if 0 mDynWorld->getBtWorld()->stepSimulation(delta, 3); +#endif if (!ECS::get().has()) return; /* Update window grab */ @@ -444,8 +440,10 @@ public: } ECS::update(delta); +#if 0 if (ECS::get().enableDbgDraw) mDbgDraw->update(); +#endif } class InputListenerChainFlexible : public OgreBites::InputListener { protected: @@ -598,7 +596,7 @@ public: "Skybox/Dynamic", "General"); OgreAssert(m, "Sky box material not found."); m->load(); - ECS::setup(mScnMgr, mDynWorld.get(), mCameraNode, mCamera, + ECS::setup(mScnMgr, /*mDynWorld.get(), */ mCameraNode, mCamera, getRenderWindow()); ECS::get().set( { getRenderWindow(), getDisplayDPI() }); diff --git a/Procedural.cpp b/Procedural.cpp index 4e00d3d..bd6af7f 100644 --- a/Procedural.cpp +++ b/Procedural.cpp @@ -7,9 +7,6 @@ #include "Ogre.h" #include "OgreApplicationContext.h" -#include "Bullet/OgreBullet.h" -#include "BulletCollision/CollisionDispatch/btGhostObject.h" -#include "LinearMath/btTransform.h" #include "OgrePageManager.h" #include "Procedural.h" @@ -22,11 +19,8 @@ using Real = Ogre::Real; using Math = Ogre::Math; class WorldData { - std::unique_ptr mDynWorld; - std::unique_ptr mDbgDraw; std::unique_ptr mRoot; std::unique_ptr mScnMgr; - std::unique_ptr mbtWorld; std::unique_ptr mPageManager; Ogre::PagedWorld *mPagedWorld; @@ -62,13 +56,8 @@ private: 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())) - , mRoot(root) + : mRoot(root) , mScnMgr(scnMgr) - , mbtWorld(mDynWorld->getBtWorld()) , mPageManager(nullptr) , mPagedWorld(nullptr) { @@ -100,94 +89,8 @@ public: void createTrimesh(Ogre::Entity *entity) { } - 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(); - ghost->setCollisionShape(shape); - ghost->setCollisionFlags( - ghost->getCollisionFlags() | - btCollisionObject::CF_NO_CONTACT_RESPONSE | - btCollisionObject::CF_CHARACTER_OBJECT); - getWorld()->attachCollisionObject(ghost, ent, group, mask); -#if 0 - getBtWorld() - ->getBroadphase()->getOverlappingPairCache() - ->setInternalGhostPairCallback(new btGhostPairCallback()); - 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) - { - btDynamicsWorld *world = mDynWorld->getBtWorld(); - Ogre::SceneNode *node = ent->getParentSceneNode(); - Ogre::Bullet::RigidBodyState *state = - new Ogre::Bullet::RigidBodyState(node); - btCollisionShape *cs; - btCollisionShape *shape; - btVector3 inertia(0, 0, 0); - 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(false); - btScalar height = 1.0f; - btScalar radius = 0.3f; - 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 }; - btTransform principal; - static_cast(cs) - ->calculatePrincipalAxisTransform( - masses, principal, inertia); - } break; - default: - assert(false); - break; - } - btRigidBody *body = new btRigidBody(mass, state, cs, inertia); - getWorld()->attachRigidBody(body, ent, nullptr, group, mask); -#if 0 - body->setUserPointer(new EntityCollisionListener{ent, nullptr}); -// btRigidBody *body = mDynWorld->addRigidBody(0, ent, Ogre::Bullet::CT_TRIMESH); -#endif - return body; - } - btRigidBody *addKinematicRigidBody(float mass, Ogre::Entity *ent, - Ogre::Bullet::ColliderType ct, - int group = 1, int mask = 0xFFFF) - { - return mDynWorld->addKinematicRigidBody(ent, ct, group, mask); - } - btDynamicsWorld *getBtWorld() - { - return mDynWorld->getBtWorld(); - } - Ogre::Bullet::DynamicsWorld *getWorld() - { - return mDynWorld.get(); - } void update(float delta) { - WorldData::get_singleton()->getBtWorld()->stepSimulation(delta, - 10); - mDbgDraw->update(); } void initPagedWorld(Ogre::Camera *camera) { @@ -201,8 +104,6 @@ public: WorldData *WorldData::singleton = nullptr; class MainWorld : public Ogre::FrameListener { - btRigidBody *mFloorBody; - public: void setup() { @@ -220,13 +121,6 @@ public: 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; }; @@ -266,8 +160,6 @@ class CharacterController : public OgreBites::InputListener, Ogre::Vector3 rootMotion; Ogre::Quaternion rootRotation; // btRigidBody *mRigidBody; - btCompoundShape *mCollisionShape; - btPairCachingGhostObject *mGhostObject; public: CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam, @@ -316,35 +208,6 @@ 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()); - } }; CharacterController::CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam, @@ -358,8 +221,6 @@ CharacterController::CharacterController(Ogre::SceneNode *camNode, , mAnimID(ANIM_NONE) , mRunning(false) , world(world) - , mCollisionShape(nullptr) - , mGhostObject(nullptr) { setupBody(); setupCamera(); @@ -374,72 +235,6 @@ void CharacterController::setupBody() 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); - - assert(mCollisionShape); -#if 0 - if (mRigidBody->getMass() == 0) { -#if 0 - mRigidBody->setCollisionFlags(mRigidBody->getCollisionFlags() - | btCollisionObject::CF_KINEMATIC_OBJECT - | btCollisionObject::CF_NO_CONTACT_RESPONSE - ); -#endif -#if 0 - mGhostObject->setWorldTransform(mRigidBody->getWorldTransform()); - WorldData::get_singleton()->getBtWorld() - ->getBroadphase()->getOverlappingPairCache() - ->setInternalGhostPairCallback(new btGhostPairCallback()); -#endif - } -#endif -#if 0 - mRigidBody->setActivationState(DISABLE_DEACTIVATION); -#endif -#if 0 - { - Ogre::Entity *e2 = mScnMgr->createEntity("normal-male.glb"); - Ogre::SceneNode *e2node = mScnMgr->getRootSceneNode()->createChildSceneNode(); - e2node->attachObject(e2); - mGhostObject = WorldData::get_singleton()->addGhostObject(e2, mCollisionShape); - mController = new btKinematicCharacterController(mGhostObject, mCollisionShape, 0.5f); - WorldData::get_singleton()->getBtWorld()->addAction(mController); - } -#endif assert(mSkeleton->hasBone("Root")); mRootBone = mSkeleton->getBone("Root"); assert(mRootBone); @@ -740,8 +535,6 @@ 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())); mBodyNode->setPosition(mBodyNode->getPosition() + rotMotion); // WorldData::get_singleton()->getWorld()->testBodyMotion(mRigidBody, from, Ogre::Bullet::convert(rotMotion), true, // nullptr, false, std::set()); diff --git a/assets/blender/scripts/blender2ogre/archived_code/terrain.py b/assets/blender/scripts/blender2ogre/archived_code/terrain.py index 3bd42f5..eb4ce59 100644 --- a/assets/blender/scripts/blender2ogre/archived_code/terrain.py +++ b/assets/blender/scripts/blender2ogre/archived_code/terrain.py @@ -92,8 +92,8 @@ class OgreCollisionOp(bpy.types.Operator): def get_subcollisions( self, ob, create=True ): r = get_subcollisions( ob ) if not r and create: - method = getattr(self, 'create_%s'%ob.collision_mode) - p = method(ob) +# method = getattr(self, 'create_%s'%ob.collision_mode) +# p = method(ob) p.name = '%s.%s' %(ob.collision_mode, ob.name) p.subcollision = True r.append( p ) diff --git a/assets/blender/scripts/blender2ogre/io_ogre/ogre/scene.py b/assets/blender/scripts/blender2ogre/io_ogre/ogre/scene.py index bf5f9a8..bb4bd2c 100644 --- a/assets/blender/scripts/blender2ogre/io_ogre/ogre/scene.py +++ b/assets/blender/scripts/blender2ogre/io_ogre/ogre/scene.py @@ -52,7 +52,7 @@ def dot_scene(path, scene_name=None): linkedgroups = [] invalidnamewarnings = [] for ob in bpy.context.scene.objects: - if ob.subcollision: + if ob.subcollision or "collision_type" in ob: continue if ((config.get("EXPORT_HIDDEN") is False) and (ob not in bpy.context.visible_objects)): continue @@ -184,6 +184,7 @@ def dot_scene(path, scene_name=None): mesh_collision_prims = {} mesh_collision_files = {} + compound_collision_shapes = {} # Export the objects in the scene for root in roots: @@ -193,6 +194,7 @@ def dot_scene(path, scene_name=None): meshes = meshes, mesh_collision_prims = mesh_collision_prims, mesh_collision_files = mesh_collision_files, + compound_collision_shapes = compound_collision_shapes, exported_armatures = exported_armatures, prefix = prefix, objects = objects, @@ -655,14 +657,16 @@ def dot_scene_skybox_export( path ): # Recursive Node export def dot_scene_node_export( ob, path, doc=None, rex=None, - exported_meshes=[], meshes=[], mesh_collision_prims={}, mesh_collision_files={}, + exported_meshes=[], meshes=[], mesh_collision_prims={}, mesh_collision_files={}, compound_collision_shapes={}, exported_armatures=[], prefix='', objects=[], xmlparent=None ): + print("Nodes... " + ob.name) o = _ogre_node_helper( doc, ob ) xmlparent.appendChild(o) # if config.get('EXPORT_USER') is True: # Custom user props + user = None if len(ob.items()) > 0: user = doc.createElement('userData') o.appendChild(user) @@ -672,46 +676,116 @@ def dot_scene_node_export( ob, path, doc=None, rex=None, if not propname.startswith('_'): _property_helper(doc, user, propname, propvalue) + if ob.type == 'MESH' or ob.type == 'EMPTY': + if not user: + user = doc.createElement('userData') + o.appendChild(user) + if ob.rigid_body: + _property_helper(doc, user, "blenderCollisionType", ob.rigid_body.type) + _property_helper(doc, user, "blenderCollisionShape", ob.rigid_body.collision_shape) + _property_helper(doc, user, "blenderCollisionEnabled", ob.rigid_body.enabled) + _property_helper(doc, user, "blenderCollisionKinematic", ob.rigid_body.kinematic) + _property_helper(doc, user, "blenderCollisionMass", ob.rigid_body.mass) + if ob.rigid_body.collision_shape == "CONVEX_HULL": + _property_helper(doc, user, "collisionType", "convexHull") + elif ob.rigid_body.collision_shape == "MESH": + _property_helper(doc, user, "collisionType", "mesh") + elif ob.rigid_body.collision_shape == "COMPOUND": + _property_helper(doc, user, "collisionType", "compound") + _property_helper(doc, user, "collisionBodyMass", ob.rigid_body.mass) + if ob.type == 'MESH': + if (not ob.parent) or (ob.parent and not ob.parent.rigid_body) or (ob.parent and ob.parent.rigid_body and ob.parent.rigid_body.collision_shape != "COMPOUND"): + # no compound parent + if ob.rigid_body.collision_shape in ["CONVEX_HULL", "MESH"]: + mesh.dot_mesh(ob, path, force_name='%s_collision_%s' % (prefix, ob.data.name) ) + skeleton.dot_skeleton(ob, path) + _property_helper(doc, user, "collisionFile", '%s_collision_%s.mesh' % (prefix, ob.data.name)) + + elif ob.parent and ob.parent.rigid_body.collision_shape == "COMPOUND": + # has compound parent + mesh.dot_mesh(ob, path, force_name='%s_collision_%s_%s' % (prefix, ob.parent.data.name, ob.data.name) ) + skeleton.dot_skeleton(ob, path) + _property_helper(doc, user, "collisionFile", '%s_collision_%s_%s.mesh' % (prefix, ob.parent.data.name, ob.data.name)) + if (not ob.parent) or (ob.parent and not ob.parent.rigid_body) or (ob.parent and ob.parent.rigid_body and ob.parent.rigid_body.collision_shape != "COMPOUND"): + if ob.rigid_body.type == 'ACTIVE': + if not ob.rigid_body.kinematic: + _property_helper(doc, user, "collisionBodyType", "dynamic") + else: + _property_helper(doc, user, "collisionBodyType", "ghost") + else: + if not ob.rigid_body.kinematic: + _property_helper(doc, user, "collisionBodyType", "static") + else: + _property_helper(doc, user, "collisionBodyType", "kinematic") + + if ob.type == 'MESH': # ob.data.tessfaces is empty. always until the following call ob.data.update() ob.data.calc_loop_triangles() # if it has no faces at all, the object itself will not be exported, BUT # it might have children - - if ob.type == 'MESH' and len(ob.data.loop_triangles): + print("Loopie..." + ob.data.name + " ", ob.data.loop_triangles) + compoundParent = False + hasPhysics = False + if ob.rigid_body: + hasPhysics = True + if ob.parent: + if ob.parent.rigid_body: + if ob.parent.rigid_body.collision_shape == "COMPOUND": + compoundParent = True + createEntity = not (compoundParent and hasPhysics) + if ob.type == 'MESH' and len(ob.data.loop_triangles) and createEntity: collisionFile = None collisionPrim = None + compoundColliders = None if ob.data.name in mesh_collision_prims: collisionPrim = mesh_collision_prims[ ob.data.name ] if ob.data.name in mesh_collision_files: collisionFile = mesh_collision_files[ ob.data.name ] - - e = doc.createElement('entity') - o.appendChild(e); e.setAttribute('name', ob.name) - prefix = '' - e.setAttribute('meshFile', '%s%s.mesh' % (prefix, clean_object_name(ob.data.name)) ) - - # Set the instancing attribute if the object belongs to the correct group - _mesh_instance_helper(e, ob, "static") - _mesh_instance_helper(e, ob, "instanced") + if ob.data.name in compound_collision_shapes: + compoundColliders = compound_collision_shapes[ ob.data.name ] + print("Meshie..." + ob.data.name) + if True: + e = doc.createElement('entity') + o.appendChild(e); e.setAttribute('name', ob.name) + prefix = '' + e.setAttribute('meshFile', '%s%s.mesh' % (prefix, clean_object_name(ob.data.name)) ) + + # Set the instancing attribute if the object belongs to the correct group + _mesh_instance_helper(e, ob, "static") + _mesh_instance_helper(e, ob, "instanced") + if not collisionPrim and not collisionFile: + print("Collisions..." + ob.data.name) + for child in ob.children: + if child.type == 'MESH': + print("\tCollisions... " + child.data.name) + #if child.rigid_body: + # print("physics body") + # if child.rigid_body.collision_shape == 'CONVEX_HULL': + # collisionFile = '%s_collision_%s.mesh' % (prefix, ob.data.name) + # elif child.rigid_body.collision_shape == 'MESH': + # collisionFile = '%s_collision_%s.mesh' % (prefix, ob.data.name) + # break + if child.subcollision and child.name.startswith('DECIMATE'): + collisionFile = '%s_collision_%s.mesh' % (prefix, ob.data.name) + break + elif "collision_type" in child and child.name.startswith('DECIMATE'): + collisionFile = '%s_collision_%s.mesh' % (prefix, ob.data.name) + break + elif "collision_type" in child: + collisionFile = '%s_collision_%s.mesh' % (prefix, ob.data.name) + break + if collisionFile: + print("CollisionFile:") + mesh_collision_files[ ob.data.name ] = collisionFile + mesh.dot_mesh(child, path, force_name='%s_collision_%s' % (prefix, ob.data.name) ) + skeleton.dot_skeleton(child, path) - if not collisionPrim and not collisionFile: - for child in ob.children: - if child.subcollision and child.name.startswith('DECIMATE'): - collisionFile = '%s_collision_%s.mesh' % (prefix, ob.data.name) - break - if child.name.endswith("-collision") - collisionFile = '%s_collision_%s.mesh' % (prefix, ob.data.name) - if collisionFile: - mesh_collision_files[ ob.data.name ] = collisionFile - mesh.dot_mesh(child, path, force_name='%s_collision_%s' % (prefix, ob.data.name) ) - skeleton.dot_skeleton(child, path) - - if collisionPrim: - e.setAttribute('collisionPrim', collisionPrim ) - elif collisionFile: - e.setAttribute('collisionFile', collisionFile ) + if collisionPrim: + e.setAttribute('collisionPrim', collisionPrim ) + elif collisionFile: + e.setAttribute('collisionFile', collisionFile ) #if config.get('EXPORT_USER') is True: _mesh_entity_helper( doc, ob, e ) diff --git a/assets/blender/scripts/blender2ogre/io_ogre/util.py b/assets/blender/scripts/blender2ogre/io_ogre/util.py index 5a23969..bdc4692 100644 --- a/assets/blender/scripts/blender2ogre/io_ogre/util.py +++ b/assets/blender/scripts/blender2ogre/io_ogre/util.py @@ -769,14 +769,21 @@ def get_subcollision_meshes(): ''' returns all collision meshes found in the scene ''' r = [] for ob in bpy.context.scene.objects: - if ob.type=='MESH' and ob.subcollision: r.append( ob ) + if ob.type=='MESH' and (ob.subcollision or "collision_type" in ob): + r.append( ob ) return r def get_objects_with_subcollision(): ''' returns objects that have active sub-collisions ''' r = [] for ob in bpy.context.scene.objects: - if ob.type=='MESH' and ob.collision_mode not in ('NONE', 'PRIMITIVE'): + if ob.type != 'MESH': + continue + if not ob.rigid_body: + continue + if ob.rigid_body.collision_shape in ('CONVEX_HULL', 'MESH'): + r.append( ob ) + elif ob.type=='MESH' and not ob.collision_mode in ('NONE', 'PRIMITIVE'): r.append( ob ) return r @@ -784,7 +791,7 @@ def get_subcollisions(ob): prefix = '%s.' %ob.collision_mode r = [] for child in ob.children: - if child.subcollision and child.name.startswith( prefix ): + if (child.subcollision or "collision_type" in ob) and child.name.startswith( prefix ): r.append( child ) return r diff --git a/assets/blender/scripts/export_buildings.py b/assets/blender/scripts/export_buildings.py index d559076..a9474dc 100644 --- a/assets/blender/scripts/export_buildings.py +++ b/assets/blender/scripts/export_buildings.py @@ -11,13 +11,18 @@ from math import radians, pi argv = sys.argv argv = argv[argv.index("--") + 1:] -sys.path.insert(0, os.getcwd() + "/assets/blender/scripts") -sys.path.insert(1, os.getcwd() + "/assets/blender/scripts/blender2ogre") +incpath = os.path.dirname(__file__) + +sys.path.insert(0, incpath) +sys.path.insert(1, incpath + "/blender2ogre") + + import io_ogre +io_ogre.register() gltf_file = argv[0] print("Exporting to " + gltf_file) -basepath = os.getcwd() +basepath = incpath # bpy.ops.export_scene.gltf(filepath="", check_existing=True, # export_import_convert_lighting_mode='SPEC', gltf_export_id="", # export_format='GLB', ui_tab='GENERAL', export_copyright="", export_image_format='AUTO', @@ -47,6 +52,7 @@ for obj in bpy.data.objects: bpy.data.objects.remove(obj) scene_file = gltf_file.replace(".glb", "").replace(".gltf", "") + ".scene" + bpy.ops.ogre.export(filepath=scene_file, EX_SWAP_AXIS='xz-y', EX_V2_MESH_TOOL_VERSION='v2', diff --git a/assets/blender/vehicles/boat.blend b/assets/blender/vehicles/boat.blend index 0f7b50f..f8f1291 100644 --- a/assets/blender/vehicles/boat.blend +++ b/assets/blender/vehicles/boat.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc5ba6b5020370d9b60c9b2650f8003b8f8b6cce471f6c521325c4db4b6579b7 -size 503378 +oid sha256:e601f89fd12f060fa92778b6f6554c67b9b411c72967b1538cddb9b55608a34c +size 509752 diff --git a/lua-scripts/data.lua b/lua-scripts/data.lua index 6aee552..912a07a 100644 --- a/lua-scripts/data.lua +++ b/lua-scripts/data.lua @@ -277,6 +277,7 @@ function BoatControlQuest() ecs_character("set-actuator", ent, "sitting") ecs_set_slot(this.boat.boat_id, ent, "seat1") ecs_character("set-actuator", ent, "sitting") + print("SLOT", "captain_seat", "boo1") ecs_set_slot(this.boat.boat_id, ent, "captain_seat") ecs_character("set-actuator", ent, "sitting") end @@ -420,6 +421,7 @@ function create_boat() ecs_character("physics-control", npc_id, false) ecs_character("params-set", npc_id, "gravity", false) ecs_character("params-set", npc_id, "buoyancy", false) + print("SLOT", "captain_seat", "boo") ecs_set_slot(boat.boat_id, npc_id, "captain_seat") ecs_character("set-actuator", npc_id, "sitting") -- ecs_set_animation_state(npc_id, "main", "actuator", true) @@ -466,6 +468,7 @@ function create_actuator2(ent) end table.insert(this.contained, what) this.activated = true + print("position: ", _ecs.position(what).y) end, exit = function(this, what) print(this.id, "left by", what) diff --git a/morph/CMakeLists.txt b/morph/CMakeLists.txt index 9e45c89..e13655d 100644 --- a/morph/CMakeLists.txt +++ b/morph/CMakeLists.txt @@ -1,6 +1,6 @@ project(morph) add_executable(MorphTargetsResearch MorphTargetsResearch.cpp) -target_link_libraries(MorphTargetsResearch OgreBites OgreBullet OgrePaging ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY} ${ASSIMP_LIBRARIES} +target_link_libraries(MorphTargetsResearch OgreBites OgrePaging ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY} ${ASSIMP_LIBRARIES} -Wl,--as-needed ) if(OGRE_STATIC) diff --git a/src/characters/CMakeLists.txt b/src/characters/CMakeLists.txt deleted file mode 100644 index 3701c94..0000000 --- a/src/characters/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -project(characters) -find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain CONFIG) -# add_library(controller STATIC controller.cpp) -# target_link_libraries(controller PUBLIC OgreMain OgreBites PRIVATE GameData) -# target_include_directories(controller PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/characters/character.cpp b/src/characters/character.cpp deleted file mode 100644 index 9338f63..0000000 --- a/src/characters/character.cpp +++ /dev/null @@ -1,369 +0,0 @@ -#include -#include -#include -#include "BulletCollision/CollisionDispatch/btGhostObject.h" -#include "LinearMath/btTransform.h" -#include "character.h" - -#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 - -Character::Character(Ogre::SceneManager *scnMgr, const Ogre::String &model, - Ogre::Bullet::DynamicsWorld *world) - : mModelName(model) - , mScnMgr(scnMgr) - , mPivotPitch(0) - , mVerticalVelocity(0) - , mAnimID(ANIM_NONE) - , mRunning(false) - , mCollisionShape(nullptr) - , mGhostObject(nullptr) - , mWorld(world) - , mGoalDirection(0, 0, 0) - , mUpdate(false) -{ - setupBody(); - setupAnimations(); -} - -Character::~Character() -{ -} - -bool Character::frameStarted(const Ogre::FrameEvent &evt) -{ - return true; -} - -bool Character::frameRenderingQueued(const Ogre::FrameEvent &evt) -{ - if (mUpdate) { - updateBody(evt.timeSinceLastFrame); - updateAnimations(evt.timeSinceLastFrame); - if (evt.timeSinceLastFrame > 0) - updateRootMotion(evt.timeSinceLastFrame); - } - return true; -} - -bool Character::frameEnded(const Ogre::FrameEvent &evt) -{ - return true; -} - -void Character::setupBody() -{ - mBodyEnt = mScnMgr->createEntity(mModelName); - 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); - mWorld->attachCollisionObject(mGhostObject, mBodyEnt, - btBroadphaseProxy::AllFilter, - btBroadphaseProxy::AllFilter); - mWorld->getBtWorld()->addAction(controller); - - assert(mCollisionShape); -#if 0 - if (mRigidBody->getMass() == 0) { -#if 0 - mRigidBody->setCollisionFlags(mRigidBody->getCollisionFlags() - | btCollisionObject::CF_KINEMATIC_OBJECT - | btCollisionObject::CF_NO_CONTACT_RESPONSE - ); -#endif -#if 0 - mGhostObject->setWorldTransform(mRigidBody->getWorldTransform()); - WorldData::get_singleton()->getBtWorld() - ->getBroadphase()->getOverlappingPairCache() - ->setInternalGhostPairCallback(new btGhostPairCallback()); -#endif - } -#endif -#if 0 - mRigidBody->setActivationState(DISABLE_DEACTIVATION); -#endif -#if 0 - { - Ogre::Entity *e2 = mScnMgr->createEntity("normal-male.glb"); - Ogre::SceneNode *e2node = mScnMgr->getRootSceneNode()->createChildSceneNode(); - e2node->attachObject(e2); - mGhostObject = WorldData::get_singleton()->addGhostObject(e2, mCollisionShape); - mController = new btKinematicCharacterController(mGhostObject, mCollisionShape, 0.5f); - WorldData::get_singleton()->getBtWorld()->addAction(mController); - } -#endif - assert(mSkeleton->hasBone("Root")); - mRootBone = mSkeleton->getBone("Root"); - assert(mRootBone); -} -void Character::setupAnimations() -{ - int i, j; - mSkeleton->setBlendMode(Ogre::ANIMBLEND_CUMULATIVE); - Ogre::String animNames[NUM_ANIMS] = { "idle", "walking", "running" }; - for (i = 0; i < NUM_ANIMS; i++) { - mAnims[i] = mBodyEnt->getAnimationState(animNames[i]); - mAnims[i]->setLoop(true); - mAnims[i]->setEnabled(true); - mAnims[i]->setWeight(0); - mFadingIn[i] = false; - mFadingOut[i] = false; - mSkelAnimations[i] = mSkeleton->getAnimation(animNames[i]); - for (const auto &it : mSkelAnimations[i]->_getNodeTrackList()) { - Ogre::NodeAnimationTrack *track = it.second; - Ogre::String trackName = - track->getAssociatedNode()->getName(); - if (trackName == "mixamorig:Hips") { - mHipsTracks[i] = track; - } else if (trackName == "Root") { - mRootTracks[i] = track; - // mRootTracks[i]->removeAllKeyFrames(); - } - } - Ogre::Vector3 delta = Ogre::Vector3::ZERO; - Ogre::Vector3 motion = Ogre::Vector3::ZERO; - for (j = 0; j < mRootTracks[i]->getNumKeyFrames(); j++) { - Ogre::Vector3 trans = mRootTracks[i] - ->getNodeKeyFrame(j) - ->getTranslate(); - if (j == 0) - delta = trans; - else - delta = trans - motion; - mRootTracks[i]->getNodeKeyFrame(j)->setTranslate(delta); - motion = trans; - } - } -#if 0 - for(i = 0; i < NUM_ANIMS - 1; i++) { - // need to cache - int j; - Ogre::String animName = mAnims[i]->getAnimationName(); - Ogre::Animation *anim = mSkeleton->getAnimation(animName); - Ogre::NodeAnimationTrack *hips_track = nullptr, *root_track = nullptr; - Ogre::Node *root_node = nullptr; - for (const auto& it : anim->_getNodeTrackList()) { - Ogre::NodeAnimationTrack* track = it.second; - Ogre::String trackName = track->getAssociatedNode()->getName(); - std::cout << animName << " track: " << trackName << "\n"; - if (trackName == "mixamorig:Hips") - hips_track = track; - else if (trackName == "Root") { - root_track = track; - root_node = track->getAssociatedNode(); - } - } - assert(false); - root_track->removeAllKeyFrames(); - std::cout << hips_track << " " << root_track << "\n"; - std::cout << hips_track->getNumKeyFrames() << " " << root_track->getNumKeyFrames() << "\n"; - assert(hips_track && root_track); - Ogre::Vector3 delta = Ogre::Vector3::ZERO; - for(j = 0; j < hips_track->getNumKeyFrames(); j++) { - float timePos = hips_track->getNodeKeyFrame(j)->getTime(); - Ogre::Vector3 trans = hips_track->getNodeKeyFrame(j)->getTranslate(); - Ogre::Vector3 hips_trans(0, 0, 0); - Ogre::Vector3 root_trans(0, 0, 0); - hips_track->getNodeKeyFrame(j)->setTranslate(hips_trans); - Ogre::TransformKeyFrame *nk = root_track->createNodeKeyFrame(timePos); - nk->setTranslate(root_trans - delta); - nk->setScale(Ogre::Vector3(1, 1, 1)); - nk->setRotation(Ogre::Quaternion()); - std::cout << animName << " delta: " << j << " " << timePos << " " << root_trans - delta << "\n"; - delta = root_trans; - } - for(j = 0; j < root_track->getNumKeyFrames(); j++) { - float timePos = hips_track->getNodeKeyFrame(j)->getTime(); - Ogre::Vector3 root_trans = hips_track->getNodeKeyFrame(j)->getTranslate(); - std::cout << animName << " delta: root: " << j << " " << timePos << " " << root_trans << "\n"; - } - } -// assert(false); -#endif - setAnimation(ANIM_IDLE); -} -void Character::updateBody(Ogre::Real delta) -{ - Ogre::Quaternion toGoal = - mBodyNode->getOrientation().zAxis().getRotationTo( - mGoalDirection); - // calculate how much the character has to turn to face goal direction - Ogre::Real yawToGoal = toGoal.getYaw().valueDegrees(); - // this is how much the character CAN turn this frame - Ogre::Real yawAtSpeed = - yawToGoal / Ogre::Math::Abs(yawToGoal) * delta * TURN_SPEED; - // reduce "turnability" if we're in midair - // if (mBaseAnimID == ANIM_JUMP_LOOP) yawAtSpeed *= 0.2f; - if (yawToGoal < 0) - yawToGoal = std::min( - 0, - std::max( - yawToGoal, - yawAtSpeed)); //yawToGoal = Math::Clamp(yawToGoal, yawAtSpeed, 0); - else if (yawToGoal > 0) - yawToGoal = std::max( - 0, - std::min( - yawToGoal, - yawAtSpeed)); //yawToGoal = Math::Clamp(yawToGoal, 0, yawAtSpeed); - mBodyNode->yaw(Ogre::Degree(yawToGoal)); -} -void Character::updateAnimations(Ogre::Real delta) -{ - int i, j, k; - Ogre::Real animSpeed = 1; - mTimer += delta; - { - Ogre::Quaternion rot = mBodyNode->getOrientation(); - OgreAssert(!Ogre::Math::isNaN(rot.x), "NaN"); - OgreAssert(!Ogre::Math::isNaN(rot.y), "NaN"); - OgreAssert(!Ogre::Math::isNaN(rot.z), "NaN"); - } - if (mAnimID != ANIM_NONE) { - if (mAnimID == ANIM_WALK) - mAnims[mAnimID]->addTime(delta * 1.0f); - else - mAnims[mAnimID]->addTime(delta * animSpeed); - } - fadeAnimations(delta); -} -void Character::updateRootMotion(Ogre::Real delta) -{ - Ogre::Vector3 boneMotion = mRootBone->getPosition(); - OgreAssert(delta > 0.0f, "Zero delta"); - Ogre::Vector3 motion = boneMotion - rootMotion; - if (motion.squaredLength() > 0.1f * 0.1f) - motion = Ogre::Vector3(); - rootMotion = boneMotion; -#if 0 - float mass = mRigidBody->getMass(); - std::cout << "Root bone position: " << boneMotion << "\n"; - std::cout << "body mass: " << mass << "\n"; -#endif - /* Kinematic motion */ - Ogre::Quaternion rot = mBodyNode->getOrientation(); - // Ogre::Vector3 gravity(0, -9.8, 0); - Ogre::Vector3 gravity(0, 0, 0); - Ogre::Vector3 velocity = rot * boneMotion / delta; - velocity += gravity * delta; - Ogre::Vector3 rotMotion = velocity * delta; - 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()); -} -void Character::fadeAnimations(Ogre::Real delta) -{ - int i; - for (i = 0; i < NUM_ANIMS; i++) { - if (mFadingIn[i]) { - // slowly fade this animation in until it has full weight - Ogre::Real newWeight = mAnims[i]->getWeight() + - delta * ANIM_FADE_SPEED; - mAnims[i]->setWeight( - Ogre::Math::Clamp(newWeight, 0, 1)); - if (newWeight >= 1) - mFadingIn[i] = false; - } else if (mFadingOut[i]) { - // slowly fade this animation out until it has no weight, and then disable it - Ogre::Real newWeight = mAnims[i]->getWeight() - - delta * ANIM_FADE_SPEED; - mAnims[i]->setWeight( - Ogre::Math::Clamp(newWeight, 0, 1)); - if (newWeight <= 0) { - mAnims[i]->setEnabled(false); - mFadingOut[i] = false; - } - } - } -} -void Character::setAnimation(AnimID id, bool reset) -{ - assert(id >= 0 && id < NUM_ANIMS); - if (mAnimID != ANIM_NONE) { - mFadingIn[mAnimID] = false; - mFadingOut[mAnimID] = true; - } - mAnimID = id; - if (id != ANIM_NONE) { - mAnims[id]->setEnabled(true); - mAnims[id]->setWeight(0); - mFadingOut[id] = false; - mFadingIn[id] = true; - if (reset) - mAnims[id]->setTimePosition(0); - } -} - -bool Character::act_run() -{ - if (mAnimID == ANIM_IDLE) - setAnimation(ANIM_RUN, true); - else if (mAnimID == ANIM_WALK) - setAnimation(ANIM_RUN); - return true; -} - -bool Character::act_walk() -{ - if (mAnimID == ANIM_IDLE) - setAnimation(ANIM_WALK, true); - else if (mAnimID == ANIM_RUN) - setAnimation(ANIM_WALK); - return true; -} - -bool Character::act_idle() -{ - setAnimation(ANIM_IDLE); - return true; -} - -bool Character::isRunning() -{ - return mAnimID == ANIM_RUN; -} - -bool Character::isWalking() -{ - return mAnimID == ANIM_WALK; -} - -Ogre::Vector3 Character::getPosition() -{ - return mBodyNode->_getDerivedPosition(); -} diff --git a/src/characters/character.h b/src/characters/character.h deleted file mode 100644 index fe56c3b..0000000 --- a/src/characters/character.h +++ /dev/null @@ -1,128 +0,0 @@ -#include -#include -#include -class btCompoundShape; -class btPairCachingGhostObject; -class Character : public Ogre::FrameListener { - enum AnimID { - ANIM_IDLE = 0, - ANIM_WALK, - ANIM_RUN, - NUM_ANIMS, - ANIM_NONE = NUM_ANIMS - }; - Ogre::String mModelName; - Ogre::Node *mRootBone; - Ogre::SceneManager *mScnMgr; - - Ogre::SceneNode *mCameraPivot; - Ogre::SceneNode *mCameraGoal, *mBodyNode; - Ogre::Entity *mBodyEnt; - Ogre::Real mPivotPitch; - Ogre::Real mVerticalVelocity; - Ogre::Vector3 mGoalDirection; // actual intended direction in world-space - Ogre::AnimationState *mAnims[NUM_ANIMS]; // master animation list - Ogre::Animation *mSkelAnimations[NUM_ANIMS]; - Ogre::NodeAnimationTrack *mHipsTracks[NUM_ANIMS]; - Ogre::NodeAnimationTrack *mRootTracks[NUM_ANIMS]; - AnimID mAnimID; - bool mFadingIn[NUM_ANIMS]; // which animations are fading in - bool mFadingOut[NUM_ANIMS]; // which animations are fading out - Ogre::Real - mTimer; // general timer to see how long animations have been playing - Ogre::Skeleton *mSkeleton; - bool mRunning; - Ogre::Vector3 rootMotion; - Ogre::Quaternion rootRotation; - // btRigidBody *mRigidBody; - btCompoundShape *mCollisionShape; - btPairCachingGhostObject *mGhostObject; - Ogre::Bullet::DynamicsWorld *mWorld; - bool mUpdate; - -public: - Character(Ogre::SceneManager *scnMgr, const Ogre::String &modelName, - Ogre::Bullet::DynamicsWorld *world); - ~Character(); - -private: - void setupBody(); - void setupAnimations(); - -public: - bool frameStarted(const Ogre::FrameEvent &evt) override; - bool frameEnded(const Ogre::FrameEvent &evt) override; - bool frameRenderingQueued(const Ogre::FrameEvent &evt) override; - -private: - void updateBody(Ogre::Real deltaTime); - void updateAnimations(Ogre::Real deltaTime); - void updateRootMotion(Ogre::Real deltaTime); - void fadeAnimations(Ogre::Real deltaTime); - void setAnimation(AnimID id, bool reset = false); - 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()); - } - -public: - bool isIdle() - { - return mAnimID == ANIM_IDLE; - } - bool act_run(); - bool act_walk(); - bool act_idle(); - bool isRunning(); - bool isWalking(); - bool isMoving() - { - return isRunning() || isWalking(); - } - void setGoalDirection(const Ogre::Vector3 &goalDirection) - { - mGoalDirection = goalDirection; - } - Ogre::Vector3 getGoalDirection() - { - return mGoalDirection; - } - Ogre::Vector3 getPosition(); - void enableUpdates() - { - mUpdate = true; - } - void disableUpdates() - { - mUpdate = false; - } - bool getUpdates() - { - return mUpdate; - } -}; diff --git a/src/gamedata/BoatModule.cpp b/src/gamedata/BoatModule.cpp index 24a133a..a52cb36 100644 --- a/src/gamedata/BoatModule.cpp +++ b/src/gamedata/BoatModule.cpp @@ -5,6 +5,7 @@ #include "loader.h" #include "Components.h" #include "GameData.h" +#include "PhysicsModule.h" #include "BoatModule.h" namespace ECS @@ -14,7 +15,107 @@ BoatModule::BoatModule(flecs::world &ecs) { ecs.module(); ecs.component(); - ecs.component(); + ecs.component(); + ecs.component(); + ecs.observer("CreateBoat") + .event(flecs::OnSet) + .without() + .without() + .each([&](flecs::entity e, const EngineData &eng, + const BoatType &type) { e.add(); }); + ecs.system("CreateBoat") + .kind(flecs::OnUpdate) + .without() + .with() + .with() + .each([&](flecs::entity e, const EngineData &eng, + const BoatType &type) { + if (type.resourceName.find(".scene") != + std::string::npos) { + BoatBase &boat = e.ensure(); + int i; + std::vector colliderTarget; + boat.mNode = + ECS::get() + .mScnMgr->getRootSceneNode() + ->createChildSceneNode( + type.position, + type.orientation); + auto pnodes = boat.mNode->getChildren(); + for (i = 0; i < pnodes.size(); i++) { + Ogre::SceneNode *pnode = + static_cast( + pnodes[i]); + Ogre::Any any = + pnode->getUserObjectBindings() + .getUserAny("type"); + if (!any.has_value()) + continue; + Ogre::String obj_type = + Ogre::any_cast( + any); + if (obj_type == "hull-collider") { + /* FIXME */ + } + } + { + Ogre::SceneNode *attachment = + eng.mScnMgr->getRootSceneNode() + ->createChildSceneNode(); + Ogre::DataStreamPtr scene = + Ogre::ResourceGroupManager::getSingleton() + .openResource( + type.resourceName, + Ogre::ResourceGroupManager:: + AUTODETECT_RESOURCE_GROUP_NAME); + SceneLoader loader; + loader.load(scene, "General", + attachment, e); + // attachment->loadChildren(type.resourceName); + std::vector v = + attachment->getChildren(); + OgreAssert(v.size() == 1, + "Bad root nodes count in " + + type.resourceName); + Ogre::Any any = + static_cast( + v[0]) + ->getUserObjectBindings() + .getUserAny("type"); + OgreAssert(any.has_value(), + "no \"type\" costom prop"); + Ogre::String obj_type = + Ogre::any_cast( + any); + std::cout << "type: " << obj_type + << std::endl; + OgreAssert(obj_type == "boat", + "not a boat"); + boat.mNode = + static_cast( + v[0]); + boat.mNode->_setDerivedPosition( + type.position); + boat.mNode->_setDerivedOrientation( + type.orientation); + boat.mEnt = static_cast( + boat.mNode->getAttachedObject( + std::to_string( + (int)e.raw_id()) + + "/boat")); + loader.setupPhysicsBody(boat.mNode, + boat.mEnt, e); + e.remove(); + e.modified(); + } + // BoatBody &body = e.ensure(); + // e.modified(); + } + // ECS::get_mut().enableDbgDraw = true; + // ECS::modified(); + // e.modified(); + }); +#if 0 ecs.system("CreateBoat") .kind(flecs::OnUpdate) .without() @@ -33,6 +134,7 @@ BoatModule::BoatModule(flecs::world &ecs) type.orientation); boat.mNode->attachObject(boat.mEnt); +#if 0 BoatBody &body = e.ensure(); body.body = ECS::get() @@ -41,6 +143,7 @@ BoatModule::BoatModule(flecs::world &ecs) Ogre::Bullet::CT_TRIMESH, nullptr, 2, 0x7fffffff); b2e.entities[body.body] = e; +#endif } else if (type.resourceName.find(".scene") != std::string::npos) { int i; @@ -105,14 +208,27 @@ BoatModule::BoatModule(flecs::world &ecs) boat.mNode->getAttachedObject( std::to_string((int)e.raw_id()) + "/boat")); + loader.setupPhysicsBody(boat.mNode, boat.mEnt); BoatBody &body = e.ensure(); +#if 0 + Ogre::Any bodyA = + boat.mNode->getUserObjectBindings() + .getUserAny("bodyPointer"); + if (bodyA.has_value()) + body.body = + Ogre::any_cast( + bodyA); +#if 0 body.body = ECS::get() .mWorld->addRigidBody( 0, boat.mEnt, Ogre::Bullet::CT_TRIMESH, nullptr, 2, 0x7fffffff); +#endif + OgreAssert(body.body, "No body :()"); b2e.entities[body.body] = e; +#endif std::vector slots = boat.mNode->getChildren(); for (i = 0; i < slots.size(); i++) { @@ -167,7 +283,10 @@ BoatModule::BoatModule(flecs::world &ecs) } } } + // ECS::get_mut().enableDbgDraw = true; + // ECS::modified(); e.modified(); }); +#endif } } diff --git a/src/gamedata/BoatModule.h b/src/gamedata/BoatModule.h index 20f3b6c..352ad15 100644 --- a/src/gamedata/BoatModule.h +++ b/src/gamedata/BoatModule.h @@ -18,9 +18,7 @@ struct BoatBase { Ogre::Entity *mEnt; Ogre::SceneNode *mNode; }; -struct BoatBody { - btRigidBody *body; -}; +struct SpawnBoat {}; struct BoatModule { BoatModule(flecs::world &ecs); }; diff --git a/src/gamedata/CMakeLists.txt b/src/gamedata/CMakeLists.txt index 8aeef74..a52bf94 100644 --- a/src/gamedata/CMakeLists.txt +++ b/src/gamedata/CMakeLists.txt @@ -1,9 +1,11 @@ project(gamedata) +set(CMAKE_CXX_STANDARD 17) find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain Overlay CONFIG) find_package(Bullet REQUIRED) add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp SunModule.cpp TerrainModule.cpp GUIModule.cpp LuaData.cpp WorldMapModule.cpp - BoatModule.cpp EventTriggerModule.cpp CharacterAnimationModule.cpp SmartObject.cpp goap.cpp) -target_link_libraries(GameData PUBLIC OgreMain OgreBites OgreBullet - OgrePaging OgreTerrain OgreOverlay flecs::flecs_static - lua ${BULLET_LIBRARIES} PRIVATE sceneloader world-build) -target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${BULLET_INCLUDE_DIR}) + BoatModule.cpp EventTriggerModule.cpp CharacterAnimationModule.cpp PhysicsModule.cpp SmartObject.cpp SlotsModule.cpp goap.cpp) +target_link_libraries(GameData PUBLIC lua flecs::flecs_static OgreMain OgreBites + OgrePaging OgreTerrain OgreOverlay + PRIVATE sceneloader world-build physics) +target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${BULLET_INCLUDE_DIR} ../luaaa) +target_compile_definitions(GameData PRIVATE FLECS_CPP_NO_AUTO_REGISTRATION) diff --git a/src/gamedata/CharacterAnimationModule.cpp b/src/gamedata/CharacterAnimationModule.cpp index d81f675..ad36275 100644 --- a/src/gamedata/CharacterAnimationModule.cpp +++ b/src/gamedata/CharacterAnimationModule.cpp @@ -2,6 +2,7 @@ #include "Components.h" #include "EventTriggerModule.h" #include "CharacterModule.h" +#include "PhysicsModule.h" #include "CharacterAnimationModule.h" #include "world-build.h" namespace ECS @@ -228,6 +229,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) } #endif v.velocity = rot * boneMotion / safeDelta; +#if 0 if (!e.has() && !e.has()) { if (eng.startupDelay <= 0.0f) @@ -235,6 +237,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) v.velocity.y = Ogre::Math::Clamp(v.velocity.y, -10.5f, 10.0f); } +#endif // if (v.velocity.squaredLength() > 1.4f * 1.4f) // v.velocity = v.velocity.normalisedCopy() * 1.4f; // ch.mBoneMotion = Ogre::Vector3::ZERO; @@ -246,7 +249,11 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) Ogre::Math::Clamp(v.velocity.z, -16.0f, 16.0f); v.velocity.y = Ogre::Math::Clamp(v.velocity.y, -10.5f, 10.0f); +#if 0 + v.velocity.y = 0.0f; +#endif }); +#if 0 ecs.system("HandleSwimming") .kind(flecs::OnUpdate) @@ -271,12 +278,13 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) h * eng.delta; } }); - ecs.system("HandleRootMotion") +#endif + ecs.system("HandleRootMotion") .kind(flecs::OnUpdate) .each([this](flecs::entity e, const EngineData &eng, - CharacterBase &ch, CharacterBody &body, - AnimationControl &anim, CharacterVelocity &v) { + CharacterBase &ch, AnimationControl &anim, + CharacterVelocity &v) { if (!ch.mBodyNode) return; if (eng.delta < 0.0000001f) @@ -286,6 +294,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) Ogre::Vector3 colNormal; bool is_on_floor = false; bool penetration = false; +#if 0 if (eng.startupDelay < 0.0f) { if (body.mController) { Ogre::Vector3 rotMotion = @@ -321,7 +330,9 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) rotMotion); } } +#endif }); +#if 0 ecs.system("HandleRootMotionEnd") .kind(flecs::OnUpdate) .each([this](flecs::entity e, CharacterVelocity &v, @@ -330,6 +341,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) // v.velocity = Ogre::Vector3::ZERO; // ch.mBoneMotion = Ogre::Vector3::ZERO; }); +#endif ecs.system( "HandleNPCAnimations") @@ -576,6 +588,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) << std::endl; }); #endif +#if 0 ecs.system( "UpdateBodyCast") .kind(flecs::OnUpdate) @@ -655,6 +668,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs) result.m_collisionObject))); } }); +#endif struct AnimationSetCommand : public GameWorld::Command { int operator()(const std::vector &args) override diff --git a/src/gamedata/CharacterModule.cpp b/src/gamedata/CharacterModule.cpp index 1f5306d..0d6c27d 100644 --- a/src/gamedata/CharacterModule.cpp +++ b/src/gamedata/CharacterModule.cpp @@ -1,10 +1,10 @@ #include #include -#include #include "GameData.h" #include "WaterModule.h" #include "TerrainModule.h" #include "Components.h" +#include "PhysicsModule.h" #include "CharacterAnimationModule.h" #include "CharacterModule.h" #include "goap.h" @@ -17,8 +17,10 @@ CharacterModule::CharacterModule(flecs::world &ecs) ecs.component(); ecs.component(); ecs.component(); - ecs.component(); - ecs.component(); + ecs.component(); + ecs.component(); + ecs.component(); + ecs.component(); ecs.component(); ecs.component(); ecs.component(); @@ -125,6 +127,7 @@ CharacterModule::CharacterModule(flecs::world &ecs) else if (current_subm < 0.8f) ch.is_submerged = false; }); +#if 0 ecs.system( "HandleGravityBouyanceWater") .kind(flecs::OnUpdate) @@ -164,6 +167,8 @@ CharacterModule::CharacterModule(flecs::world &ecs) gr.gvelocity *= (1.0 - eng.delta); gr.velocity.y *= (1.0 - eng.delta); }); +#endif +#if 0 ecs.system( "HandleGravityNoWater") .kind(flecs::OnUpdate) @@ -183,6 +188,7 @@ CharacterModule::CharacterModule(flecs::world &ecs) gr.gvelocity *= (1.0 - eng.delta); gr.velocity.y *= (1.0 - eng.delta); }); +#endif #define TURN_SPEED 500.0f // character turning in degrees per second ecs.system("UpdateBody") .kind(flecs::OnUpdate) @@ -235,6 +241,7 @@ CharacterModule::CharacterModule(flecs::world &ecs) ch.mBodyNode->yaw(Ogre::Degree(yawToGoal)); } }); +#if 0 ecs.system("UpdateCharacterBase") .kind(flecs::OnUpdate) @@ -251,6 +258,40 @@ CharacterModule::CharacterModule(flecs::world &ecs) ch.mBodyNode->_getDerivedPosition(); } }); +#endif + ecs.observer("SetupCharacterM") + .event(flecs::OnSet) + .with() + .without() + .each([](flecs::entity e, const EngineData &eng, + const CharacterLocation &loc, + const CharacterConf &conf) { + CharacterBase &ch = e.ensure(); + AnimationControl &anim = e.ensure(); + ch.mBodyEnt = eng.mScnMgr->createEntity(conf.type); + ch.mBodyNode = eng.mScnMgr->getRootSceneNode() + ->createChildSceneNode(); + ch.mBodyNode->setOrientation(loc.orientation); + ch.mBodyNode->setPosition(loc.position); + ch.mBodyNode->attachObject(ch.mBodyEnt); + ch.mSkeleton = ch.mBodyEnt->getSkeleton(); + OgreAssert(ch.mBodyEnt->getSkeleton()->hasBone("Root"), + "No root bone"); + OgreAssert(ch.mSkeleton->hasBone("Root"), + "No root bone"); + ch.mRootBone = ch.mSkeleton->getBone("Root"); + OgreAssert(ch.mRootBone, "No root bone"); + // body.mController = nullptr; + ch.mBoneMotion = Ogre::Vector3::ZERO; + ch.mBonePrevMotion = Ogre::Vector3::ZERO; + e.set( + { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }); + e.add(); + e.add(); + anim.configured = false; + }); +#if 0 ecs.system("SetupCharacter") .kind(flecs::OnUpdate) @@ -276,13 +317,14 @@ CharacterModule::CharacterModule(flecs::world &ecs) "No root bone"); ch.mRootBone = ch.mSkeleton->getBone("Root"); OgreAssert(ch.mRootBone, "No root bone"); - body.mController = nullptr; + // body.mController = nullptr; ch.mBoneMotion = Ogre::Vector3::ZERO; ch.mBonePrevMotion = Ogre::Vector3::ZERO; e.set( { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } }); body.checkGround = false; body.checkGroundResult = false; +#if 0 body.mCollisionShape = nullptr; body.mGhostObject = nullptr; body.mController = nullptr; @@ -320,12 +362,15 @@ CharacterModule::CharacterModule(flecs::world &ecs) body.mGhostObject, ch.mBodyEnt, 1, 0x7FFFFFFF); OgreAssert(body.mGhostObject, "Need GhostObject"); OgreAssert(body.mCollisionShape, "No collision shape"); +#endif e.add(); e.add(); anim.configured = false; - OgreAssert(body.mGhostObject->hasContactResponse(), - "need contact response"); + // OgreAssert(body.mGhostObject->hasContactResponse(), + // "need contact response"); }); +#endif +#if 0 ecs.system( "UpdateCharacterPhysics") .kind(flecs::OnUpdate) @@ -334,17 +379,21 @@ CharacterModule::CharacterModule(flecs::world &ecs) .with() .each([](const EngineData &eng, CharacterBase &ch, CharacterBody &body) { +#if 0 if (ch.mBodyNode && !body.mController && eng.startupDelay < 0.0f) { body.mController = new Ogre::Bullet::KinematicMotionSimple( body.mGhostObject, ch.mBodyNode); + body.mController->enableManualNarrowPhase(true); eng.mWorld->getBtWorld()->addAction( body.mController); OgreAssert(body.mController, "Need controller"); } +#endif }); +#endif #define CAM_HEIGHT 1.6f // height of camera above character's center of mass ecs.system( "UpdateCamera") @@ -394,6 +443,7 @@ CharacterModule::CharacterModule(flecs::world &ecs) Ogre::Node::TS_PARENT); } }); +#if 0 class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback { btCollisionObject *mMe; @@ -416,12 +466,14 @@ CharacterModule::CharacterModule(flecs::world &ecs) rayResult, normalInWorldSpace); } }; - ecs.system("CheckGround") +#endif + ecs.system("CheckGround") .kind(flecs::OnUpdate) .with() .with() .without() - .each([](const EngineData &eng, CharacterBody &body) { + .each([](const EngineData &eng, CharacterBase &ch) { +#if 0 if (body.mGhostObject) { btVector3 from = body.mGhostObject->getWorldTransform() @@ -437,7 +489,10 @@ CharacterModule::CharacterModule(flecs::world &ecs) if (resultCallback.hasHit()) ECS::get().add(); } +#endif + ECS::get().add(); }); +#if 0 ecs.system( "CharacterWater1") .kind(flecs::OnUpdate) @@ -445,11 +500,13 @@ CharacterModule::CharacterModule(flecs::world &ecs) .without() .each([](flecs::entity e, const WaterBody &waterb, const CharacterBase &ch, CharacterBody &body) { +#if 0 if (waterb.isInWater(body.mGhostObject) && ch.mBodyNode->_getDerivedPosition().y < -0.05f) { e.add(); std::cout << "Big Splash\n"; } +#endif #if 0 if (waterb.mInWater.find(body.mGhostObject) == waterb.mInWater.end()) @@ -457,6 +514,8 @@ CharacterModule::CharacterModule(flecs::world &ecs) std::cout << waterb.mInWater.size() << " InWater\n"; #endif }); +#endif +#if 0 ecs.system( "CharacterWater2") .kind(flecs::OnUpdate) @@ -465,12 +524,15 @@ CharacterModule::CharacterModule(flecs::world &ecs) .each([](flecs::entity e, const WaterBody &waterb, const CharacterBase &ch, CharacterBody &body) { float h = ch.mBodyNode->_getDerivedPosition().y; +#if 0 if (waterb.isInWater(body.mGhostObject) && h > 0.05f) e.remove(); else if (!waterb.isInWater(body.mGhostObject) && h > 0.05f) e.remove(); +#endif }); +#endif #if 0 ecs.system( "DisplayPlayerPos") @@ -483,39 +545,14 @@ CharacterModule::CharacterModule(flecs::world &ecs) << "\n"; }); #endif - ecs.system( - "UpdatePhysics2") - .kind(flecs::OnUpdate) - .with() - .each([](flecs::entity e, const EngineData &eng, - const CharacterBase &ch, ParentSlot &slot) { - if (slot.parent_e.has()) { - const ObjectSlots &slots = - slot.parent_e.get(); - if (slots.slots.find(slot.slot_name) == - slots.slots.end()) - // invalid setting - e.remove(); - if (slot.activated) - return; - Ogre::SceneNode *slot_base = - slots.slots.at(slot.slot_name).second; - Ogre::Vector3 position = - slot_base->_getDerivedPosition(); - Ogre::Quaternion orientation = - slot_base->_getDerivedOrientation(); - ch.mBodyNode->_setDerivedPosition(position); - ch.mBodyNode->_setDerivedOrientation( - orientation); - slot.activated = true; - } - }); +#if 0 ecs.system("UpdatePhysics") .kind(flecs::OnUpdate) .with() .write() .each([](flecs::entity e, const EngineData &eng, const CharacterBody &body) { +#if 0 if (e.has()) { eng.mWorld->getBtWorld()->removeAction( body.mController); @@ -523,8 +560,10 @@ CharacterModule::CharacterModule(flecs::world &ecs) eng.mWorld->getBtWorld()->addAction( body.mController); } +#endif e.remove(); }); +#endif } void CharacterModule::updateCameraGoal(Camera &camera, Ogre::Real deltaYaw, @@ -726,4 +765,4 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs) } }); } -} \ No newline at end of file +} diff --git a/src/gamedata/CharacterModule.h b/src/gamedata/CharacterModule.h index eb0d86e..7ad75d0 100644 --- a/src/gamedata/CharacterModule.h +++ b/src/gamedata/CharacterModule.h @@ -1,6 +1,7 @@ #ifndef CHARACTER_MODULE_H_ #define CHARACTER_MODULE_H_ #include +#include #include "goap.h" namespace ECS { @@ -34,17 +35,6 @@ struct CharacterLocation { struct CharacterConf { Ogre::String type; }; -struct CharacterBody { - btPairCachingGhostObject *mGhostObject; - btCompoundShape *mCollisionShape; - Ogre::Bullet::KinematicMotionSimple *mController; - bool checkGround; - bool checkGroundResult; -}; -struct CharacterVelocity { - Ogre::Vector3 gvelocity; - Ogre::Vector3 velocity; -}; struct CharacterInActuator { Ogre::String animationState; Vector3 prevMotion; @@ -73,4 +63,4 @@ struct CharacterAIModule { CharacterAIModule(flecs::world &ecs); }; } -#endif \ No newline at end of file +#endif diff --git a/src/gamedata/Components.h b/src/gamedata/Components.h index 3d0a62d..22cef6d 100644 --- a/src/gamedata/Components.h +++ b/src/gamedata/Components.h @@ -2,7 +2,6 @@ #define COMPONENTS_H_ #include #include -#include namespace Ogre { class ImGuiOverlay; @@ -20,7 +19,6 @@ struct GameData { }; struct EngineData { Ogre::SceneManager *mScnMgr; - Ogre::Bullet::DynamicsWorld *mWorld; float delta; float startupDelay; int width; @@ -75,24 +73,15 @@ struct App { std::vector listeners; }; struct CollisionShape { - btCollisionShape *shape; + void *shape; }; struct InWater {}; struct TerrainReady {}; +struct WaterAlmostReady {}; struct WaterReady {}; struct GroundCheckReady {}; -struct ParentSlot { - flecs::entity parent_e; - Ogre::String slot_name; - bool activated; -}; -struct ObjectSlots { - std::unordered_map > - slots; -}; struct Body2Entity { - std::unordered_map entities; + /* std::unordered_map entities; */ }; } #endif \ No newline at end of file diff --git a/src/gamedata/EventTriggerModule.cpp b/src/gamedata/EventTriggerModule.cpp index 24c55ce..fc1bff6 100644 --- a/src/gamedata/EventTriggerModule.cpp +++ b/src/gamedata/EventTriggerModule.cpp @@ -1,11 +1,14 @@ #include -#include #include #include "Components.h" #include "GameData.h" #include "LuaData.h" #include "EventTriggerModule.h" +struct TriggerBody { + void *data; +}; +#if 0 struct TriggerBody { btPairCachingGhostObject *mBody; btCylinderShape *shape; @@ -58,10 +61,22 @@ struct DeepPenetrationContactResultCallback : public btManifoldResult { } } }; +#endif ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs) { ecs.module(); + ecs.component(); + ecs.component(); + ecs.observer("CreateTrigger") + .event(flecs::OnSet) + .each([](flecs::entity e, const EngineData &eng, + const EventTrigger &trigger) { + e.set({}); + ECS::get().mLua->call_handler( + "actuator_created", e, e); + }); +#if 0 ecs.component(); ecs.component().on_add([](flecs::entity e, TriggerBody &body) { @@ -311,4 +326,5 @@ ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs) it++; } }); +#endif } diff --git a/src/gamedata/GameData.cpp b/src/gamedata/GameData.cpp index e22ce5e..a0b720f 100644 --- a/src/gamedata/GameData.cpp +++ b/src/gamedata/GameData.cpp @@ -12,6 +12,7 @@ #include "BoatModule.h" #include "EventTriggerModule.h" #include "CharacterAnimationModule.h" +#include "PhysicsModule.h" #include "world-build.h" namespace ECS @@ -27,6 +28,7 @@ void setup_minimal() ecs.import (); ecs.component(); ecs.component().add(flecs::Singleton); + ecs.component().add(flecs::Singleton); ecs.component().add(flecs::Singleton); ecs.component() .on_add([](App &app) { @@ -37,26 +39,24 @@ void setup_minimal() .add(flecs::Singleton); /* lots of things depend on it */ ecs.component().add(flecs::Singleton); - ecs.component(); - ecs.component(); ecs.component().add(flecs::Singleton); } -void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world, - Ogre::SceneNode *cameraNode, Ogre::Camera *camera, - Ogre::RenderWindow *window) +void setup(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, + Ogre::Camera *camera, Ogre::RenderWindow *window) { std::cout << "Setup GameData\n"; setup_minimal(); - ecs.import (); + ecs.component().add(flecs::Singleton); ecs.import (); - ecs.import (); + ecs.import (); + ecs.import (); + ecs.import (); ecs.import (); + ecs.import (); ecs.import (); + ecs.import (); ecs.import (); ecs.import (); - ecs.import (); - ecs.import (); - ecs.import (); ecs.import (); ecs.system("UpdateDelta") @@ -89,9 +89,8 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world, std::cout << "ground check ready\n"; #endif }); - ecs.set({ scnMgr, world, 0.0f, 5.0f, - (int)window->getWidth(), (int)window->getHeight(), - false }); + ecs.set({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(), + (int)window->getHeight(), false }); ecs.set({ cameraNode, camera, false }); ecs.add(); ecs.add(); @@ -105,7 +104,7 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world, nullptr, false, { 0, 0, 0 } }); - ecs.set({}); + // ecs.set({}); std::cout << "Setup GameData done\n"; /* Create player */ @@ -129,4 +128,4 @@ bool Vector3::zeroLength() const float l = x * x + y * y + z * z; return (l < 1e-06 * 1e-06); } -} \ No newline at end of file +} diff --git a/src/gamedata/GameData.h b/src/gamedata/GameData.h index 73147a8..86ed828 100644 --- a/src/gamedata/GameData.h +++ b/src/gamedata/GameData.h @@ -1,14 +1,12 @@ #ifndef GAMEDATA_H #define GAMEDATA_H -#include #include namespace ECS { extern flecs::entity player; void setup_minimal(); -void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world, - Ogre::SceneNode *cameraNode, Ogre::Camera *camera, - Ogre::RenderWindow *window); +void setup(Ogre::SceneManager *scnMgr, 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/LuaData.cpp b/src/gamedata/LuaData.cpp index 333411c..c3d9907 100644 --- a/src/gamedata/LuaData.cpp +++ b/src/gamedata/LuaData.cpp @@ -2,15 +2,67 @@ #include "GameData.h" #include "Components.h" #include "GUIModule.h" +#include "PhysicsModule.h" #include "CharacterModule.h" #include "CharacterAnimationModule.h" #include "BoatModule.h" #include "EventTriggerModule.h" +#include "SlotsModule.h" #include "world-build.h" #include "LuaData.h" - +#include "luaaa.hpp" extern "C" { int luaopen_lpeg(lua_State *L); +} +struct FooPosition { + Ogre::Vector3 value; + Ogre::Vector3 &get() + { + return value; + } +}; +namespace luaaa +{ +template <> struct LuaStack { + inline static FooPosition get(lua_State *L, int idx) + { + FooPosition result; + if (lua_istable(L, idx)) { + lua_pushnil(L); + while (0 != lua_next(L, idx)) { + const int top = lua_gettop(L); + const char *name = + LuaStack::get(L, top - 1); + if (strncmp(name, "x", 1) == 0) + result.value.x = + LuaStack::get(L, top); + else if (strncmp(name, "y", 1) == 0) + result.value.y = + LuaStack::get(L, top); + else if (strncmp(name, "z", 1) == 0) + result.value.z = + LuaStack::get(L, top); + lua_pop(L, 1); + } + lua_pop(L, 0); + } + return result; + } + inline static void put(lua_State *L, const FooPosition &v) + { + lua_newtable(L); + LuaStack::put(L, "x"); + LuaStack::put(L, v.value.x); + lua_rawset(L, -3); + LuaStack::put(L, "y"); + LuaStack::put(L, v.value.y); + lua_rawset(L, -3); + LuaStack::put(L, "z"); + LuaStack::put(L, v.value.z); + lua_rawset(L, -3); + } +}; + } namespace ECS { @@ -194,6 +246,46 @@ LuaData::LuaData() #endif installLibraryLoader(L); lua_pop(L, 1); + +#if 0 + luaaa::LuaClass luaVector3(L, "Vector3"); + luaVector3.ctor(); + luaVector3.get("x", [](Ogre::Vector3 &v) -> float { return v.x; }); + luaVector3.set("x", [](Ogre::Vector3 &v, float value) -> void { + v.x = value; + }); + luaVector3.get("y", [](Ogre::Vector3 &v) -> float { return v.y; }); + luaVector3.set("y", [](Ogre::Vector3 &v, float value) -> void { + v.y = value; + }); + luaVector3.get("z", [](Ogre::Vector3 &v) -> float { return v.z; }); + luaVector3.set("z", [](Ogre::Vector3 &v, float value) -> void { + v.z = value; + }); + luaVector3.fun("squaredLength", &Ogre::Vector3::squaredLength); + luaVector3.fun("length", &Ogre::Vector3::length); + luaVector3.fun("squaredDistance", &Ogre::Vector3::squaredDistance); + luaVector3.fun("distance", &Ogre::Vector3::distance); +#endif + luaaa::LuaModule luaECS(L, "_ecs"); + luaECS.fun("position", [](int id) -> FooPosition { +#if 0 + flecs::entity e = idmap.get_entity(id); + if (e.has()) { + const CharacterBase &cb = e.get(); + return cb.mBodyNode->_getDerivedPosition(); + } else if (e.has()) { + const BoatBase &boat = e.get(); + return boat.mNode->_getDerivedPosition(); + } else if (e.has()) { + const EventTrigger &trigger = e.get(); + return trigger.position; + } else + return Ogre::Vector3(0, 0, 0); +#endif + return FooPosition(); + }); + lua_pushcfunction(L, [](lua_State *L) -> int { OgreAssert(false, "Crash function called"); return 0; @@ -567,7 +659,9 @@ LuaData::LuaData() Ogre::String slot = lua_tostring(L, 3); flecs::entity parent_e = idmap.get_entity(parent); flecs::entity object_e = idmap.get_entity(object); - object_e.set({ parent_e, slot, false }); + if (!object_e.has()) + object_e.add(); + object_e.set({ parent_e, slot }); return 0; }); lua_setglobal(L, "ecs_set_slot"); @@ -737,6 +831,7 @@ void LuaData::lateSetup() LuaModule::LuaModule(flecs::world &ecs) { ecs.module(); + ecs.import (); ecs.component(); ecs.component() .on_add([](LuaBase &lua) { diff --git a/src/gamedata/PhysicsModule.cpp b/src/gamedata/PhysicsModule.cpp new file mode 100644 index 0000000..03ee4b2 --- /dev/null +++ b/src/gamedata/PhysicsModule.cpp @@ -0,0 +1,535 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "Components.h" +#include "GameData.h" +#include "CharacterModule.h" +#include "WaterModule.h" +#include "BoatModule.h" +#include "EventTriggerModule.h" +#include "physics.h" +#include "loader.h" +#include "PhysicsModule.h" +namespace ECS +{ +struct PhysicsShape { + JPH::ShapeRefC shape; +}; +struct ConvexHull {}; +struct WaterBody { + std::set inWater; + bool isInWater(const JPH::BodyID &id) const; +}; +struct TriggerBody { + void *foo; +}; + +PhysicsModule::PhysicsModule(flecs::world &ecs) +{ + ecs.module(); + ecs.import (); + ecs.import (); + flecs::entity PhysicsPreUpdate = + ecs.entity().add(flecs::Phase).depends_on(flecs::OnUpdate); + flecs::entity PhysicsUpdate = + ecs.entity().add(flecs::Phase).depends_on(PhysicsPreUpdate); + flecs::entity PhysicsPostUpdate = + ecs.entity().add(flecs::Phase).depends_on(PhysicsUpdate); + ecs.component().add(flecs::Singleton); + ecs.component().member("mID"); + /* for terrain */ + ecs.component(); + ecs.component(); + ecs.component(); + ecs.component(); + ecs.component(); + ecs.component(); + + ecs.component(); + ecs.component(); + ecs.component(); + ecs.component().add(flecs::Singleton); + ecs.system("physics_init") + .kind(PhysicsPreUpdate) + .without() + .each([&](const EngineData &e, const Camera &c) { + Physics &ph = ECS::get().ensure(); + ph.physics = new JoltPhysicsWrapper(e.mScnMgr, + c.mCameraNode); + ECS::modified(); + }); +#if 0 + ecs.system("create_body") + .kind(flecs::OnUpdate) + .without() + .each([&](flecs::entity e, PhysicsBody &rb) { + JPH::BodyID id = + JoltPhysicsWrapper::getSingleton().createBody( + rb.shape.get(), rb.node, + (JPH::EMotionType)rb.motion, + (JPH::ObjectLayer)rb.layer); + e.set(id); + }); +#endif + ecs.system("physics_update") + .kind(PhysicsUpdate) + .each([&](EngineData &e, Physics &ph) { + ph.physics->update(e.delta); + }); + ecs.observer( + "create_shape_mesh_name") + .event(flecs::OnSet) + .without() + .with() + .write() + .each([&](flecs::entity e, const EngineData &eng, + PhysicsMeshName &name) { + Ogre::DefaultHardwareBufferManagerBase dmgr; + Ogre::MeshPtr mesh = + Ogre::MeshManager::getSingleton().getByName( + name.meshName); + mesh->setHardwareBufferManager(&dmgr); + mesh->load(); + JPH::ShapeRefC shape = + JoltPhysicsWrapper::getSingleton() + .createMeshShape(mesh); + PhysicsShape &s = e.ensure(); + s.shape = shape; + e.modified(); + }); + ecs.observer("create_shape_mesh_ptr") + .event(flecs::OnSet) + .without() + .with() + .write() + .each([&](flecs::entity e, const EngineData &eng, + PhysicsMeshPtr &meshPtr) { + Ogre::DefaultHardwareBufferManager dmgr; + Ogre::MeshPtr mesh = meshPtr.mesh; + if (!mesh->isLoaded()) { + mesh->setHardwareBufferManager(&dmgr); + mesh->load(); + } + JPH::ShapeRefC shape = + JoltPhysicsWrapper::getSingleton() + .createMeshShape(mesh); + PhysicsShape &s = e.ensure(); + s.shape = shape; + e.modified(); + }); + ecs.observer( + "create_shape_heightfield") + .event(flecs::OnSet) + .without() + .with() + .write() + .each([&](flecs::entity e, const EngineData &eng, + PhysicsHeightfieldData &hfd) { + JPH::ShapeRefC shape = + JoltPhysicsWrapper::getSingleton() + .createHeightfieldShape( + hfd.samples, hfd.offset, + hfd.scale, hfd.sampleCount); + PhysicsShape &s = e.ensure(); + s.shape = shape; + e.modified(); + delete hfd.samples; + e.remove(); + }); +#if 1 + ecs.observer("create_body_from_shape") + .event(flecs::OnSet) + .without() + .with() + .write() + .write() + .each([&](flecs::entity e, const EngineData &eng, + const PhysicsShape &shape, const PhysicsNode &node, + const PhysicsBody &body) { + JPH::BodyID id = + JoltPhysicsWrapper::getSingleton().createBody( + shape.shape.GetPtr(), 0.0f, node.node, + (JPH::EMotionType)body.motion, + (JPH::ObjectLayer)body.layer); + e.set(id); + JoltPhysicsWrapper::getSingleton().addBody( + id, JPH::EActivation::Activate); + }); +#endif + ecs.observer("remove_body") + .event(flecs::OnRemove) + .each([&](flecs::entity e, const JPH::BodyID &id) { + JoltPhysicsWrapper::getSingleton().removeBody(id); + JoltPhysicsWrapper::getSingleton().destroyBody(id); + std::cout << "body destroyed" << std::endl; + }); + ecs.observer("SetupCharacterPh") + .event(flecs::OnSet) + .with() + .without() + .write() + .each([](flecs::entity e, const EngineData &eng, + const CharacterBase &base) { + CharacterBody &b = e.ensure(); + b.ch.reset(JoltPhysicsWrapper::getSingleton() + .createCharacter(base.mBodyNode, + 1.75f, 0.23f)); + if (!e.has()) + static_cast(b.ch.get()) + ->AddToPhysicsSystem( + JPH::EActivation::Activate); + e.modified(); + }); + ecs.observer( + "CreateTriggerPhysics") + .event(flecs::OnSet) + .without() + .each([](flecs::entity e, const EngineData &eng, + const EventTrigger &trigger) { + JPH::ShapeRefC shape = + JoltPhysicsWrapper::getSingleton() + .createCylinderShape(trigger.halfheight, + trigger.radius); + JPH::BodyID id = + JoltPhysicsWrapper::getSingleton().createSensor( + shape.GetPtr(), trigger.node, + JPH::EMotionType::Kinematic, + Layers::MOVING); + e.set(id); + // JoltPhysicsWrapper::getSingleton().setDebugDraw(true); + JoltPhysicsWrapper::getSingleton().addBody( + id, JPH::EActivation::Activate); + }); +#if 0 + ecs.system( + "UpdateTriggerPhysicsPre") + .kind(PhysicsPreUpdate) + .with() + .with() + .with() + .each([](flecs::entity e, const EngineData &eng, + const EventTrigger &trigger, const JPH::BodyID &id) { + /* FIXME: update positions for triggers, probably need to move somewhere */ + JoltPhysicsWrapper::getSingleton() + .setPositionAndRotation( + id, trigger.node->_getDerivedPosition(), + trigger.node->_getDerivedOrientation()); +#if 0 + std::cout << trigger.node->_getDerivedPosition() << " " + << trigger.node->getPosition() << " " + << trigger.node->getParent()->getName() + << ": " << trigger.node->getName() + << std::endl; + // OgreAssert(false, "update triggers"); +#endif + }); + ecs.system( + "UpdateTriggerPhysicsPost") + .kind(PhysicsPostUpdate) + .with() + .with() + .with() + .each([](flecs::entity e, const EngineData &eng, + const EventTrigger &trigger, const JPH::BodyID &id) { + /* FIXME: update positions for triggers, probably need to move somewhere */ + Ogre::Vector3 position; + Ogre::Quaternion rotation; + JoltPhysicsWrapper::getSingleton() + .getPositionAndRotation(id, position, rotation); + trigger.node->_setDerivedPosition(position); + trigger.node->_setDerivedOrientation(rotation); + }); +#endif + ecs.system("init_water") + .kind(PhysicsPreUpdate) + .with() + .with() + .without() + .each([this](const EngineData &eng) { + ECS::get().set({}); + }); + ecs.system("update_water") + .kind(PhysicsPostUpdate) + .with() + .with() + .each([this](const EngineData &eng, WaterBody &body) { + const WaterSurface &water = ECS::get(); + JoltPhysicsWrapper::getSingleton().broadphaseQuery( + eng.delta, + water.mWaterNode->_getDerivedPosition(), + body.inWater); +#if 0 + for (JPH::BodyID inBodyID : body.inWater) { + if (JoltPhysicsWrapper::getSingleton().isActive( + inBodyID)) { + float my = + JoltPhysicsWrapper::getSingleton() + .getPosition(inBodyID) + .y; + float myv = + JoltPhysicsWrapper::getSingleton() + .getLinearVelocity( + inBodyID) + .y; + float b = 1.0f; + float drag = 0.5f; + float adrag = 0.05f; + float level = -1.3f; + float mdec = 1.0f; + float minc = 1.0f; + float h = -my + level; + if (h < 0) + h = 0; + if (my < level - 0.1f) + b *= 1.1f * (1.0f + 1.2f * h); + else if (my > level + 0.1f) { + b *= 0.8f; + if (myv > 0.1f) + b *= 0.9f; + if (my > level + 0.4f) + b *= 0.5f; + } + if (myv < 0.0f) + drag = 0.7f; + JoltPhysicsWrapper::getSingleton().applyBuoyancyImpulse( + inBodyID, + water.mWaterNode->_getDerivedPosition() - + Ogre::Vector3(0, 0.1f, + 0), + Ogre::Vector3::UNIT_Y, b, drag, + adrag, Ogre::Vector3::ZERO, + eng.delta); + // std::cout << b << std::endl; +#if 0 + std::cout << "addHit: " + << JoltPhysics::convert( + body.GetPosition()) + << std::endl; +#endif + } + } +#endif + ECS::get().add(); + }); + ecs.system("update_water_status1") + .kind(PhysicsPostUpdate) + .with() + .with() + .with() + .each([this](flecs::entity e, const JPH::BodyID &id, + const WaterBody &body) { + if (!body.isInWater(id)) + e.remove(); + }); + ecs.system("update_water_status2") + .kind(PhysicsPostUpdate) + .with() + .with() + .without() + .each([this](flecs::entity e, const JPH::BodyID &id, + const WaterBody &body) { + if (body.isInWater(id)) + e.add(); + }); + ecs.system( + "update_water_character1") + .kind(PhysicsPostUpdate) + .with() + .with() + .with() + .each([this](flecs::entity e, const CharacterBody &ch, + const WaterBody &body) { + JPH::Character *chptr = + static_cast(ch.ch.get()); + if (!body.isInWater(chptr->GetBodyID())) + e.remove(); + }); + ecs.system( + "update_water_character2") + .kind(PhysicsPostUpdate) + .with() + .with() + .without() + .each([this](flecs::entity e, const CharacterBody &ch, + const WaterBody &body) { + JPH::Character *chptr = + static_cast(ch.ch.get()); + if (body.isInWater(chptr->GetBodyID())) + e.add(); + }); + ecs.system("update_water_boat_enable") + .kind(PhysicsPreUpdate) + .with() + .with() + .each([this](flecs::entity e, const EngineData &eng, + const BoatBase &boat, const WaterBody &body, + const JPH::BodyID &id) { + if (!JoltPhysicsWrapper::getSingleton().isAdded(id)) + JoltPhysicsWrapper::getSingleton().addBody( + id, JPH::EActivation::Activate); + }); + ecs.system("update_water_boat_activation") + .kind(PhysicsPreUpdate) + .with() + .with() + .with() + .each([this](flecs::entity e, const EngineData &eng, + const BoatBase &boat, const WaterBody &body, + const JPH::BodyID &id) { + if (!JoltPhysicsWrapper::getSingleton().isActive(id)) + JoltPhysicsWrapper::getSingleton().activate(id); + }); + ecs.system("update_water_boat_buoyancy") + .kind(PhysicsPreUpdate) + .with() + .with() + .with() + .each([this](flecs::entity e, const EngineData &eng, + const BoatBase &boat, const WaterBody &body, + const JPH::BodyID &id) { + const WaterSurface &water = ECS::get(); + float b = 1.0f, drag = 0.5f, adrag = 0.5f; + float level = 0.1f; + float my = JoltPhysicsWrapper::getSingleton() + .getPosition(id) + .y; + float myv = JoltPhysicsWrapper::getSingleton() + .getLinearVelocity(id) + .y; +#if 0 + if (my < level && myv < 0) + b = 10.0f; + else if (my > level && myv > 0) + b = 0.8f; +#endif + if (my < level) + b = 1.7f; + else if (my > level) + b = 0.9f; + // std::cout << my << std::endl; + JoltPhysicsWrapper::getSingleton().applyBuoyancyImpulse( + id, water.mWaterNode->_getDerivedPosition(), + Ogre::Vector3::UNIT_Y, b, drag, adrag, + Ogre::Vector3::ZERO, eng.delta); + }); + ecs.system( + "update_water_character_buoyancy") + .kind(PhysicsPreUpdate) + .with() + .with() + .with() + .without() + .with() + .each([this](flecs::entity e, const EngineData &eng, + const CharacterBody &ch, const WaterBody &body) { + JPH::Character *chptr = + static_cast(ch.ch.get()); + JPH::BodyID id = chptr->GetBodyID(); + if (JoltPhysicsWrapper::getSingleton().isActive(id)) { + const WaterSurface &water = + ECS::get(); + float my = JoltPhysicsWrapper::getSingleton() + .getPosition(id) + .y; + float myv = JoltPhysicsWrapper::getSingleton() + .getLinearVelocity(id) + .y; + float b = 1.0f; + float drag = 0.5f; + float adrag = 0.05f; + float level = -1.3f; + float mdec = 1.0f; + float minc = 1.0f; + float h = -my + level; + if (h < 0) + h = 0; + if (my < level - 0.1f) + b *= 1.1f * (1.0f + 1.2f * h); + else if (my > level + 0.1f) { + b *= 0.8f; + if (myv > 0.1f) + b *= 0.9f; + if (my > level + 0.4f) + b *= 0.5f; + } + if (myv < 0.0f) + drag = 0.7f; + JoltPhysicsWrapper::getSingleton() + .applyBuoyancyImpulse( + id, + water.mWaterNode + ->_getDerivedPosition(), + Ogre::Vector3::UNIT_Y, b, drag, + adrag, Ogre::Vector3::ZERO, + eng.delta); + // std::cout << b << std::endl; +#if 0 + std::cout << "addHit: " + << JoltPhysics::convert( + body.GetPosition()) + << std::endl; +#endif + } + }); + ecs.system( + "HandleVelocity") + .kind(PhysicsPostUpdate) + .with() + .with() + .without() + .without() + .each([this](flecs::entity e, const EngineData &eng, + const CharacterBody &body, CharacterVelocity &gr) { + Ogre::Vector3 v = gr.velocity; + v.y = 0.0f; + JPH::Character *ch = + static_cast(body.ch.get()); + if (!e.has()) { + if (ch->IsSupported()) { + v.y = gr.velocity.y; + gr.gvelocity.y = 0; + } else { + v.y = gr.velocity.y; + v.y += gr.gvelocity.y; + gr.gvelocity.y += -9.8f * eng.delta; + } + } else { + v = JoltPhysics::convert( + ch->GetLinearVelocity()); + v.x = gr.velocity.x; + v.z = gr.velocity.z; + } + // gr.velocity.y = 0.0f; + // v.y = 0.0f; + ch->SetLinearVelocity(JoltPhysics::convert(v)); + gr.velocity = Ogre::Vector3::ZERO; + }); +} +flecs::entity PhysicsModule::createTerrainChunkBody(Ogre::SceneNode *node, + float *samples, + const Ogre::Vector3 &offset, + const Ogre::Vector3 &scale, + int sampleCount) +{ + flecs::entity e = ECS::get().entity(); + e.set({ samples, offset, scale, sampleCount }); + e.set({ (uint32_t)JPH::EMotionType::Static, + (uint32_t)Layers::NON_MOVING }); + e.set({ node }); + + return e; +} +bool WaterBody::isInWater(const JPH::BodyID &id) const +{ + return inWater.find(id) != inWater.end(); +} +} \ No newline at end of file diff --git a/src/gamedata/PhysicsModule.h b/src/gamedata/PhysicsModule.h new file mode 100644 index 0000000..232fc81 --- /dev/null +++ b/src/gamedata/PhysicsModule.h @@ -0,0 +1,57 @@ +#ifndef _PHYSICS_MODULE_H_ +#define _PHYSICS_MODULE_H_ +#include +class JoltPhysicsWrapper; +namespace JPH +{ +class Shape; +class CharacterBase; +} +namespace Ogre +{ +class TerrainGroup; +} +namespace ECS +{ +struct Physics { + JoltPhysicsWrapper *physics; +}; +struct PhysicsNode { + Ogre::SceneNode *node; +}; +struct PhysicsMeshName { + Ogre::String meshName; +}; +struct PhysicsMeshPtr { + Ogre::MeshPtr mesh; +}; +struct PhysicsBody { + uint32_t motion; + uint32_t layer; +}; +struct CharacterBody { + std::shared_ptr ch; +}; +struct BoatBody { + void *tmp; +}; +struct PhysicsHeightfieldData { + const float *samples; + Ogre::Vector3 offset; + Ogre::Vector3 scale; + int sampleCount; +}; +struct CharacterVelocity { + Ogre::Vector3 gvelocity; + Ogre::Vector3 velocity; +}; +struct PhysicsModule { + PhysicsModule(flecs::world &ecs); + static flecs::entity createTerrainChunkBody(Ogre::SceneNode *node, + float *samples, + const Ogre::Vector3 &offset, + const Ogre::Vector3 &scale, + int sampleCount); +}; +} +#endif \ No newline at end of file diff --git a/src/gamedata/SlotsModule.cpp b/src/gamedata/SlotsModule.cpp new file mode 100644 index 0000000..d9d7592 --- /dev/null +++ b/src/gamedata/SlotsModule.cpp @@ -0,0 +1,153 @@ +#include +#include "Components.h" +#include "GameData.h" +#include "CharacterModule.h" +#include "BoatModule.h" +#include "SlotsModule.h" +namespace ECS +{ +SlotsModule::SlotsModule(flecs::world &ecs) +{ + ecs.module(); + ecs.import (); + ecs.component(); + ecs.component(); + ecs.component(); + ecs.observer("CreateBoatSlots") + .event(flecs::OnSet) + .each([](flecs::entity e, const EngineData &eng, + const BoatBase &boat) { + int i; + std::vector slots = + boat.mNode->getChildren(); + for (i = 0; i < slots.size(); i++) { + Ogre::Any any = + static_cast(slots[i]) + ->getUserObjectBindings() + .getUserAny("type"); + if (!any.has_value()) + continue; + Ogre::String obj_type = + Ogre::any_cast(any); + std::cout << "child type: " << obj_type + << std::endl; + } + if (slots.size() > 0) { + ObjectSlots &vs = e.ensure(); + for (i = 0; i < slots.size(); i++) { + Ogre::Any any = + static_cast( + slots[i]) + ->getUserObjectBindings() + .getUserAny("type"); + if (!any.has_value()) + continue; + Ogre::String obj_type = + Ogre::any_cast( + any); + any = static_cast( + slots[i]) + ->getUserObjectBindings() + .getUserAny("name"); + if (!any.has_value()) + continue; + Ogre::String obj_name = + Ogre::any_cast( + any); + vs.slots[obj_name] = { + obj_type, + static_cast( + slots[i]) + }; + } + } + }); + ecs.system("UpdatePhysics2a") + .kind(flecs::OnUpdate) + .with() + .each([](flecs::entity e, const EngineData &eng, + const CharacterBase &ch, ParentSlot &slot, + ParentSlotData &psdata) { + if (slot.slot_name == "") { + e.remove(); + e.remove(); + } + + if (slot.parent_e.has()) { + const ObjectSlots &slots = + slot.parent_e.get(); + if (slots.slots.find(slot.slot_name) == + slots.slots.end()) { + // invalid setting + e.remove(); + return; + } + Ogre::SceneNode *slot_base = + slots.slots.at(slot.slot_name).second; +#if 0 + ch.mBodyNode->getParent()->removeChild( + ch.mBodyNode); +#endif + slot_base->addChild(ch.mBodyNode); + Ogre::Vector3 position = + slot_base->_getDerivedPosition(); + Ogre::Quaternion orientation = + slot_base->_getDerivedOrientation(); + ch.mBodyNode->_setDerivedPosition(position); + ch.mBodyNode->_setDerivedOrientation( + orientation); + psdata.parent_e = slot.parent_e; + psdata.parentNode = slot_base; + psdata.slot_name = slot.slot_name; + e.remove(); + e.modified(); + std::cout << "base: " << slot_base->getName(); + } + }); + ecs.system( + "UpdatePhysics2") + .kind(flecs::OnUpdate) + .with() + .without() + .with() + .each([](flecs::entity e, const EngineData &eng, + const CharacterBase &ch, ParentSlot &slot) { + std::cout << e.name() << std::endl; + if (!slot.parent_e.has()) + return; + OgreAssert(slot.parent_e.has(), + "parent has no slots"); + OgreAssert(e.has(), "not a character"); + if (slot.parent_e.has()) { + const ObjectSlots &slots = + slot.parent_e.get(); + if (slot.slot_name == "") + return; + if (slots.slots.find(slot.slot_name) == + slots.slots.end()) { + // invalid setting + e.remove(); + OgreAssert(false, "bad slot"); + return; + } + ParentSlotData &psdata = + e.ensure(); + Ogre::SceneNode *slot_base = + slots.slots.at(slot.slot_name).second; + Ogre::Vector3 position = + slot_base->_getDerivedPosition(); + Ogre::Quaternion orientation = + slot_base->_getDerivedOrientation(); + ch.mBodyNode->_setDerivedPosition(position); + ch.mBodyNode->_setDerivedOrientation( + orientation); + psdata.parent_e = slot.parent_e; + psdata.parentNode = slot_base; + psdata.slot_name = slot.slot_name; + e.remove(); + e.modified(); + } + }); +} +} diff --git a/src/gamedata/SlotsModule.h b/src/gamedata/SlotsModule.h new file mode 100644 index 0000000..39d61de --- /dev/null +++ b/src/gamedata/SlotsModule.h @@ -0,0 +1,25 @@ +#ifndef _SLOTS_MODULE_H_ +#define _SLOTS_MODULE_H_ +#include +#include +namespace ECS +{ +struct ParentSlot { + flecs::entity parent_e; + Ogre::String slot_name; +}; +struct ParentSlotData { + Ogre::String slot_name; + flecs::entity parent_e; + Ogre::SceneNode *parentNode; +}; +struct ObjectSlots { + std::unordered_map > + slots; +}; +struct SlotsModule { + SlotsModule(flecs::world &ecs); +}; +} +#endif \ No newline at end of file diff --git a/src/gamedata/TerrainModule.cpp b/src/gamedata/TerrainModule.cpp index 90cf908..0612871 100644 --- a/src/gamedata/TerrainModule.cpp +++ b/src/gamedata/TerrainModule.cpp @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -14,6 +13,7 @@ #include "Components.h" #include "CharacterModule.h" #include "SunModule.h" +#include "PhysicsModule.h" #include "TerrainModule.h" #define TERRAIN_SIZE 129 @@ -173,7 +173,7 @@ class FlatTerrainDefiner : public Ogre::TerrainPagedWorldSection::TerrainDefiner, public Ogre::FrameListener { Ogre::SceneManager *mScnMgr; - Ogre::Bullet::DynamicsWorld *mWorld; + // Ogre::Bullet::DynamicsWorld *mWorld; struct gen_collider { Ogre::TerrainGroup *group; long x; @@ -182,18 +182,44 @@ class FlatTerrainDefiner std::deque collider_queue; public: - FlatTerrainDefiner(Ogre::SceneManager *scm, - Ogre::Bullet::DynamicsWorld *world) + FlatTerrainDefiner(Ogre::SceneManager * + scm /*, Ogre::Bullet::DynamicsWorld *world */) : Ogre::TerrainPagedWorldSection::TerrainDefiner() , Ogre::FrameListener() , mScnMgr(scm) - , mWorld(world) + // , mWorld(world) { Ogre::Root::getSingleton().addFrameListener(this); } private: public: + void createTerrainChunk(Ogre::TerrainGroup *terrainGroup, long x, + long y) + { + Ogre::Terrain *terrain = terrainGroup->getTerrain(x, y); + float minH = terrain->getMinHeight(); + float maxH = terrain->getMaxHeight(); + uint16_t terrainSize = terrainGroup->getTerrainSize(); + OgreAssert(terrain && terrain->getHeightData() && + terrain->isLoaded(), + "invalid terrain supplied"); + uint16_t size = terrain->getSize(); + float *heightData = terrain->getHeightData(); + Ogre::SceneNode *node = terrain->_getRootSceneNode(); + float worldSize = terrain->getWorldSize(); + float scaled = worldSize / (size - 1); + Ogre::Vector3 bodyPosition = terrain->getPosition(); + bodyPosition.y += (maxH + minH) / 2.0f; + bodyPosition.x += worldSize / 2.0f; + bodyPosition.z += worldSize / 2.0f; + Ogre::Vector3 offset = + node->_getDerivedPosition() - bodyPosition; + flecs::entity e = PhysicsModule::createTerrainChunkBody( + node, heightData, offset, + Ogre::Vector3(scaled, 1, scaled), size); + node->getUserObjectBindings().setUserAny("_collider", e); + } void define(Ogre::TerrainGroup *terrainGroup, long x, long y) override { uint16_t terrainSize = terrainGroup->getTerrainSize(); @@ -258,6 +284,10 @@ public: float maxH = terrain->getMaxHeight(); int size = terrain->getSize(); float worldSize = terrain->getWorldSize(); + { + createTerrainChunk(group, x, y); + } +#if 0 if (true) { btRigidBody *body = mWorld->addTerrainRigidBody( @@ -297,6 +327,7 @@ public: y)); created = true; } +#endif collider_queue.pop_front(); // FIXME: create entities and components instead Ogre::SceneNode *items = @@ -317,11 +348,6 @@ public: what->attachObject(ent); what->setOrientation(item.rotation); what->setPosition(item.position); - ECS::get() - .mWorld->addRigidBody( - 0, ent, - Ogre::Bullet::CT_TRIMESH, - nullptr, 2, 0x7fffffff); } } else { output.push_back(collider_queue.front()); @@ -338,13 +364,10 @@ public: }; class DummyPageProvider : public Ogre::PageProvider { public: - DummyPageProvider(btDynamicsWorld *world) + DummyPageProvider(/* btDynamicsWorld *world */) : Ogre::PageProvider() - , mBtWorld(world) { } - std::unordered_map body; - btDynamicsWorld *mBtWorld; bool prepareProceduralPage(Ogre::Page *page, Ogre::PagedWorldSection *section) { @@ -377,6 +400,7 @@ TerrainModule::TerrainModule(flecs::world &ecs) ecs.component().add(flecs::Singleton); ecs.component().add(flecs::Singleton); ecs.component().add(flecs::Singleton); + ecs.component(); ecs.set({ nullptr, {} }); ecs.system("SetupUpdateTerrain") @@ -389,8 +413,8 @@ TerrainModule::TerrainModule(flecs::world &ecs) if (!priv.mDummyPageProvider) priv.mDummyPageProvider = new DummyPageProvider( - eng.mWorld - ->getBtWorld()); + /* eng.mWorld + ->getBtWorld() */); terrain.mTerrainGlobals = OGRE_NEW Ogre::TerrainGlobalOptions(); @@ -477,7 +501,7 @@ TerrainModule::TerrainModule(flecs::world &ecs) terrain.mTerrainPagedWorldSection->setDefiner( OGRE_NEW FlatTerrainDefiner( - eng.mScnMgr, eng.mWorld)); + eng.mScnMgr /*, eng.mWorld */)); terrain.mTerrainGroup->freeTemporaryResources(); std::cout << "Terrain setup done\n"; diff --git a/src/gamedata/WaterModule.cpp b/src/gamedata/WaterModule.cpp index 70efedd..5027723 100644 --- a/src/gamedata/WaterModule.cpp +++ b/src/gamedata/WaterModule.cpp @@ -12,6 +12,7 @@ #include "WaterModule.h" namespace ECS { +#if 0 class WaterPhysicsAction : public btActionInterface { btPairCachingGhostObject *mWaterBody; btManifoldArray mManifoldArray; @@ -53,13 +54,13 @@ public: return (mInWater.find(body) != mInWater.end()); } }; +#endif static const uint32_t WATER_MASK = 0xF00; WaterModule::WaterModule(flecs::world &ecs) { ecs.module(); ecs.component() .on_add([](flecs::entity e, WaterSurface &water) { - ECS::get().add(); water.mAbove = false; water.mDepthMaterial = nullptr; water.mDepthTech = nullptr; @@ -457,10 +458,14 @@ WaterModule::WaterModule(flecs::world &ecs) eng.mScnMgr->getRenderQueue()->setRenderableListener( &water.mRenderTargetListener); std::cout << "Water setup done\n"; + ECS::get().add(); }) .add(flecs::Singleton); +#if 0 + ecs.component().add(flecs::Singleton); ecs.component() .on_add([this](WaterBody &body) { +#if 0 body.mShapeAabbMax = btVector3(0, 0, 0); body.mShapeAabbMin = btVector3(0, 0, 0); body.mSurface.clear(); @@ -489,9 +494,11 @@ WaterModule::WaterModule(flecs::world &ecs) .get() .mWorld->getBtWorld() ->addAction(body.action); +#endif ECS::get().add(); }) .add(flecs::Singleton); +#endif ecs.system("UpdateWater") .kind(flecs::OnUpdate) .with() @@ -520,6 +527,7 @@ WaterModule::WaterModule(flecs::world &ecs) // water.mRenderTargetListener.mInDepth = false; // water.mWaterEnt->setVisible(true); }); +#if 0 ecs.system( "UpdateWaterBody") .kind(flecs::OnUpdate) @@ -527,6 +535,7 @@ WaterModule::WaterModule(flecs::world &ecs) .each([this](const EngineData &eng, const WaterSurface &water, WaterBody &body) { int i; +#if 0 OgreAssert(body.mWaterBody, "Water not ready"); std::set currentOverlaps; Ogre::Vector3 waterPos = @@ -542,6 +551,7 @@ WaterModule::WaterModule(flecs::world &ecs) body.mWaterBody->getWorldTransform().setOrigin( Ogre::Bullet::convert(waterBodyPos + d)); +#endif #if 0 btCompoundShape *mshape = static_cast( @@ -646,6 +656,7 @@ WaterModule::WaterModule(flecs::world &ecs) } #endif }); +#endif } struct shapeParams { float offsetX, offsetY, offsetZ; @@ -654,6 +665,7 @@ struct shapeParams { static struct shapeParams childShapes[] = { { 0.0f, 0.0f, 0.0f, 100.0f, 100.0f, 100.0f }, }; +#if 0 void WaterModule::createWaterShape(WaterBody *water) { int i = 0; @@ -684,6 +696,7 @@ void WaterModule::createWaterShape(WaterBody *water) shape->recalculateLocalAabb(); water->mWaterShape = shape; } +#endif void WaterSurface::RenderTextureListener::preRenderTargetUpdate( const Ogre::RenderTargetEvent &evt) { @@ -712,6 +725,7 @@ bool WaterSurface::RenderTextureListener::renderableQueued( *ppTech = mSurface->mDepthTech; return true; } +#if 0 struct DeepPenetrationContactResultCallback : public btManifoldResult { DeepPenetrationContactResultCallback( const btCollisionObjectWrapper *body0Wrap, @@ -954,8 +968,11 @@ void WaterPhysicsAction::debugDraw(btIDebugDraw *debugDrawer) void WaterPhysicsAction::setupBody() { } +#endif +#if 0 bool WaterBody::isInWater(const btCollisionObject *body) const { return static_cast(action)->isInWater(body); } +#endif } \ No newline at end of file diff --git a/src/gamedata/WaterModule.h b/src/gamedata/WaterModule.h index 888455d..3405d37 100644 --- a/src/gamedata/WaterModule.h +++ b/src/gamedata/WaterModule.h @@ -37,20 +37,10 @@ struct WaterSurface { mRefractionClipPlaneBelow; Ogre::Viewport *mViewports[4]; }; -struct WaterBody { - std::vector mChildShapes; - btCollisionShape *mWaterShape; - btPairCachingGhostObject *mWaterBody; - std::unordered_map mSurface; - btVector3 mShapeAabbMin, mShapeAabbMax; - int count; - btActionInterface *action; - bool isInWater(const btCollisionObject *body) const; -}; struct WaterModule { - btPairCachingGhostObject *mGhostObject; + // btPairCachingGhostObject *mGhostObject; WaterModule(flecs::world &ecs); - void createWaterShape(WaterBody *water); + // void createWaterShape(WaterBody *water); }; } #endif \ No newline at end of file diff --git a/src/gamedata/goap.h b/src/gamedata/goap.h index f7e7d51..1e56eb9 100644 --- a/src/gamedata/goap.h +++ b/src/gamedata/goap.h @@ -2,6 +2,7 @@ #define H_GOAP_H_ #include #include +#include #include namespace ECS { @@ -346,4 +347,4 @@ template struct DeclareAction : public BaseAction { } }; -#endif \ No newline at end of file +#endif diff --git a/src/physics/CMakeLists.txt b/src/physics/CMakeLists.txt new file mode 100644 index 0000000..63561a3 --- /dev/null +++ b/src/physics/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.13.0) +project(jolt-physics) + +set(CMAKE_CXX_STANDARD 17) +# The COMPONENTS part checks that OGRE was built the way we need it +# The CONFIG flag makes sure we get OGRE instead of OGRE-next +find_package(Jolt REQUIRED) +find_package(OGRE REQUIRED COMPONENTS Bites Paging Terrain CONFIG) +find_package(ZLIB) +find_package(SDL2) +find_package(assimp REQUIRED CONFIG) +find_package(OgreProcedural REQUIRED CONFIG) +find_package(pugixml REQUIRED CONFIG) +find_package(flecs REQUIRED CONFIG) + +add_library(physics STATIC physics.cpp) +target_link_libraries(physics PUBLIC OgreMain Jolt::Jolt) +target_include_directories(physics PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/physics/physics.cpp b/src/physics/physics.cpp new file mode 100644 index 0000000..f72e7f6 --- /dev/null +++ b/src/physics/physics.cpp @@ -0,0 +1,1660 @@ +// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics) +// SPDX-FileCopyrightText: 2025 Jorrit Rouwe +// SPDX-License-Identifier: CC0-1.0 +// This file is in the public domain. It serves as an example to start building your own application using Jolt Physics. Feel free to copy paste without attribution! + +// The Jolt headers don't include Jolt.h. Always include Jolt.h before including any other Jolt header. +// You can use Jolt.h in your precompiled header to speed up compilation. +#include +#include +#include + +// Jolt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// STL includes +#include +#include +#include +#include "physics.h" + +// Disable common warnings triggered by Jolt, you can use JPH_SUPPRESS_WARNING_PUSH / JPH_SUPPRESS_WARNING_POP to store and restore the warning state +JPH_SUPPRESS_WARNINGS + +// All Jolt symbols are in the JPH namespace + +// Callback for traces, connect this to your own trace function if you have one +static void TraceImpl(const char *inFMT, ...) +{ + // Format the message + va_list list; + va_start(list, inFMT); + char buffer[1024]; + vsnprintf(buffer, sizeof(buffer), inFMT, list); + va_end(list); + + // Print to the TTY + std::cout << buffer << std::endl; +} + +#ifdef JPH_ENABLE_ASSERTS + +// Callback for asserts, connect this to your own assert handler if you have one +static bool AssertFailedImpl(const char *inExpression, const char *inMessage, + const char *inFile, uint inLine) +{ + // Print to the TTY + std::cout << inFile << ":" << inLine << ": (" << inExpression << ") " + << (inMessage != nullptr ? inMessage : "") << std::endl; + + // Breakpoint + return true; +}; + +#endif // JPH_ENABLE_ASSERTS + +/// Class that determines if two object layers can collide +class ObjectLayerPairFilterImpl : public JPH::ObjectLayerPairFilter { +public: + virtual bool ShouldCollide(JPH::ObjectLayer inObject1, + JPH::ObjectLayer inObject2) const override + { + switch (inObject1) { + case Layers::NON_MOVING: + return inObject2 == + Layers::MOVING; // Non moving only collides with moving + case Layers::MOVING: + return true; // Moving collides with everything + default: + JPH_ASSERT(false); + return false; + } + } +}; + +// BroadPhaseLayerInterface implementation +// This defines a mapping between object and broadphase layers. +class BPLayerInterfaceImpl final : public JPH::BroadPhaseLayerInterface { +public: + BPLayerInterfaceImpl() + { + // Create a mapping table from object to broad phase layer + mObjectToBroadPhase[Layers::NON_MOVING] = + BroadPhaseLayers::NON_MOVING; + mObjectToBroadPhase[Layers::MOVING] = BroadPhaseLayers::MOVING; + } + + virtual uint GetNumBroadPhaseLayers() const override + { + return BroadPhaseLayers::NUM_LAYERS; + } + + virtual JPH::BroadPhaseLayer + GetBroadPhaseLayer(JPH::ObjectLayer inLayer) const override + { + JPH_ASSERT(inLayer < Layers::NUM_LAYERS); + return mObjectToBroadPhase[inLayer]; + } + +#if defined(JPH_EXTERNAL_PROFILE) || defined(JPH_PROFILE_ENABLED) + virtual const char * + GetBroadPhaseLayerName(JPH::BroadPhaseLayer inLayer) const override + { + switch ((JPH::BroadPhaseLayer::Type)inLayer) { + case (JPH::BroadPhaseLayer::Type)BroadPhaseLayers::NON_MOVING: + return "NON_MOVING"; + case (JPH::BroadPhaseLayer::Type)BroadPhaseLayers::MOVING: + return "MOVING"; + default: + JPH_ASSERT(false); + return "INVALID"; + } + } +#endif // JPH_EXTERNAL_PROFILE || JPH_PROFILE_ENABLED + +private: + JPH::BroadPhaseLayer mObjectToBroadPhase[Layers::NUM_LAYERS]; +}; + +/// Class that determines if an object layer can collide with a broadphase layer +class ObjectVsBroadPhaseLayerFilterImpl + : public JPH::ObjectVsBroadPhaseLayerFilter { +public: + virtual bool ShouldCollide(JPH::ObjectLayer inLayer1, + JPH::BroadPhaseLayer inLayer2) const override + { + switch (inLayer1) { + case Layers::NON_MOVING: + return inLayer2 == BroadPhaseLayers::MOVING; + case Layers::MOVING: + return true; + default: + JPH_ASSERT(false); + return false; + } + } +}; + +// An example contact listener +class MyContactListener : public JPH::ContactListener { +public: + // See: ContactListener + virtual JPH::ValidateResult OnContactValidate( + const JPH::Body &inBody1, const JPH::Body &inBody2, + JPH::RVec3Arg inBaseOffset, + const JPH::CollideShapeResult &inCollisionResult) override + { + std::cout << "Contact validate callback" << std::endl; + + // Allows you to ignore a contact before it is created (using layers to not make objects collide is cheaper!) + return JPH::ValidateResult::AcceptAllContactsForThisBodyPair; + } + + virtual void OnContactAdded(const JPH::Body &inBody1, + const JPH::Body &inBody2, + const JPH::ContactManifold &inManifold, + JPH::ContactSettings &ioSettings) override + { + std::cout << "A contact was added" << std::endl; + } + + virtual void + OnContactPersisted(const JPH::Body &inBody1, const JPH::Body &inBody2, + const JPH::ContactManifold &inManifold, + JPH::ContactSettings &ioSettings) override + { + std::cout << "A contact was persisted" << std::endl; + } + + virtual void + OnContactRemoved(const JPH::SubShapeIDPair &inSubShapePair) override + { + std::cout << "A contact was removed" << std::endl; + } +}; + +// An example activation listener +class MyBodyActivationListener : public JPH::BodyActivationListener { +public: + virtual void OnBodyActivated(const JPH::BodyID &inBodyID, + JPH::uint64 inBodyUserData) override + { + std::cout << "A body got activated" << std::endl; + } + + virtual void OnBodyDeactivated(const JPH::BodyID &inBodyID, + JPH::uint64 inBodyUserData) override + { + std::cout << "A body went to sleep" << std::endl; + } +}; +class MyCollector : public JPH::CollideShapeBodyCollector { +public: + MyCollector(JPH::PhysicsSystem *inSystem, + JPH::RVec3Arg inSurfacePosition, + JPH::Vec3Arg inSurfaceNormal, float inDeltaTime) + : mSystem(inSystem) + , mSurfacePosition(inSurfacePosition) + , mSurfaceNormal(inSurfaceNormal) + , mDeltaTime(inDeltaTime) + { + } + + virtual void AddHit(const JPH::BodyID &inBodyID) override + { + mInWater.insert(inBodyID); + } + +private: + JPH::PhysicsSystem *mSystem; + JPH::RVec3 mSurfacePosition; + float mDeltaTime; + +public: + std::set mInWater; + JPH::Vec3 mSurfaceNormal; +}; + +class DebugRenderer final : public JPH::DebugRendererSimple { + Ogre::ManualObject *mObject; + Ogre::SceneManager *mScnMgr; + Ogre::SceneNode *mCameraNode; + Ogre::MaterialPtr mat; + struct line { + Ogre::Vector3 from; + Ogre::Vector3 to; + Ogre::ColourValue c; + }; + std::vector mLines; + struct tri { + Ogre::Vector3 p[3]; + Ogre::ColourValue c; + }; + std::vector mTriangles; + +public: + DebugRenderer(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode); + ~DebugRenderer() override; + void DrawLine(JPH::RVec3Arg inFrom, JPH::RVec3Arg inTo, + JPH::ColorArg inColor) override + { + JPH::Vec4 color = inColor.ToVec4(); + mLines.push_back({ { inFrom[0], inFrom[1], inFrom[2] }, + { inTo[0], inTo[1], inTo[2] }, + Ogre::ColourValue(color[0], color[1], + color[2], color[3]) }); + } + void DrawTriangle(JPH::RVec3Arg inV1, JPH::RVec3Arg inV2, + JPH::RVec3Arg inV3, JPH::ColorArg inColor, + ECastShadow inCastShadow = ECastShadow::Off) override + { + Ogre::Vector3 d = mCameraNode->_getDerivedOrientation() * + Ogre::Vector3(0, 0, -1); + JPH::Vec4 color = inColor.ToVec4(); + Ogre::Vector3 p1 = JoltPhysics::convert(inV1); + Ogre::Vector3 p2 = JoltPhysics::convert(inV2); + Ogre::Vector3 p3 = JoltPhysics::convert(inV3); + Ogre::ColourValue cv(color[0], color[1], color[2], color[3]); + + float dproj1 = p1.dotProduct(d); + float dproj2 = p2.dotProduct(d); + float dproj3 = p3.dotProduct(d); + if (dproj1 < 0 && dproj2 < 0 && dproj3 < 0) + return; + if (dproj1 > 50 && dproj2 > 50 && dproj3 > 50) + return; + mLines.push_back({ p1, p2, cv }); +#if 0 + mTriangles.push_back({ { { inV1[0], inV1[1], inV1[2] }, + { inV2[0], inV2[1], inV2[2] }, + { inV3[0], inV3[1], inV3[2] } }, + Ogre::ColourValue(color[0], color[1], + color[2], color[3]) }); +#endif + } +#if 0 + Batch CreateTriangleBatch(const Triangle *inTriangles, + int inTriangleCount) override + { + return Batch(); + } + Batch CreateTriangleBatch(const Vertex *inVertices, int inVertexCount, + const JPH::uint32 *inIndices, + int inIndexCount) override + { + return Batch(); + } +#endif + void DrawText3D(JPH::RVec3Arg inPosition, + const std::string_view &inString, + JPH::ColorArg inColor = JPH::Color::sWhite, + float inHeight = 0.5f) override + { + std::cout << "text\n"; + } +#if 0 + void DrawGeometry(JPH::RMat44Arg inModelMatrix, + const JPH::AABox &inWorldSpaceBounds, + float inLODScaleSq, JPH::ColorArg inModelColor, + const GeometryRef &inGeometry, + ECullMode inCullMode = ECullMode::CullBackFace, + ECastShadow inCastShadow = ECastShadow::On, + EDrawMode inDrawMode = EDrawMode::Solid) override + { + std::cout << "geometry\n"; + } +#endif + void finish() + { + Ogre::Vector3 d = mCameraNode->_getDerivedOrientation() * + Ogre::Vector3(0, 0, -1); + int i; + mObject->clear(); + mObject->begin(mat, Ogre::RenderOperation::OT_LINE_LIST); + for (i = 0; i < mLines.size(); i++) { +#if 0 + float dproj1 = mLines[i].from.dotProduct(d); + float dproj2 = mLines[i].to.dotProduct(d); + if (dproj1 < 0 && dproj2 < 0) + continue; + if (dproj1 > 50 && dproj2 > 50) + continue; +#endif + mObject->position(mLines[i].from); + mObject->colour(mLines[i].c); + mObject->position(mLines[i].to); + mObject->colour(mLines[i].c); + } + mObject->end(); + mLines.clear(); +#if 0 + mObject->begin(mat, Ogre::RenderOperation::OT_TRIANGLE_LIST); + for (i = 0; i < mTriangles.size(); i++) { + mObject->position(mTriangles[i].p[0]); + mObject->colour(mTriangles[i].c); + mObject->position(mTriangles[i].p[1]); + mObject->colour(mTriangles[i].c); + mObject->position(mTriangles[i].p[2]); + mObject->colour(mTriangles[i].c); + mObject->triangle(0, 1, 2); + mObject->triangle(2, 1, 0); + std::cout << mTriangles[i].p[0] << " "; + std::cout << mTriangles[i].p[1] << " "; + std::cout << mTriangles[i].p[2] << " " << std::endl; + } + mObject->end(); +#else +#if 0 + mObject->begin(mat, Ogre::RenderOperation::OT_LINE_LIST); + for (i = 0; i < mTriangles.size(); i++) { + float dproj1 = mTriangles[i].p[0].dotProduct(d); + float dproj2 = mTriangles[i].p[1].dotProduct(d); + float dproj3 = mTriangles[i].p[2].dotProduct(d); + if (dproj1 < 0 && dproj2 < 0 && dproj3 < 0) + continue; + if (dproj1 > 50 && dproj2 > 50 && dproj3 > 50) + continue; + mObject->position(mTriangles[i].p[0]); + mObject->colour(mTriangles[i].c); + mObject->position(mTriangles[i].p[1]); + mObject->colour(mTriangles[i].c); + mObject->position(mTriangles[i].p[1]); + mObject->colour(mTriangles[i].c); + mObject->position(mTriangles[i].p[2]); + mObject->colour(mTriangles[i].c); + mObject->position(mTriangles[i].p[2]); + mObject->colour(mTriangles[i].c); + mObject->position(mTriangles[i].p[0]); + mObject->colour(mTriangles[i].c); + } + mObject->end(); +#endif +#endif + mTriangles.clear(); + } +}; +DebugRenderer::DebugRenderer(Ogre::SceneManager *scnMgr, + Ogre::SceneNode *cameraNode) + : mObject(scnMgr->createManualObject("joltDebugRenderer")) + , mScnMgr(scnMgr) + , mCameraNode(cameraNode) + , mat(Ogre::MaterialManager::getSingleton().create("joltDebugDraw", + "General")) +{ + Ogre::Technique *technique = mat->getTechnique(0); + Ogre::Pass *pass = technique->getPass(0); + Ogre::ColourValue color(1, 0, 0, 1); + pass->setCullingMode(Ogre::CullingMode::CULL_NONE); + pass->setVertexColourTracking(Ogre::TVC_AMBIENT); + pass->setLightingEnabled(false); + DebugRenderer::Initialize(); + scnMgr->getRootSceneNode()->attachObject(mObject); + mLines.reserve(6000); + mObject->estimateVertexCount(64000); + mObject->estimateIndexCount(8000); +} +DebugRenderer::~DebugRenderer() +{ + mObject->getParentSceneNode()->detachObject(mObject); + mScnMgr->destroyManualObject(mObject); +} + +namespace JoltPhysics +{ +struct ShapeData { + JPH::ShapeRefC shape; +}; +Ogre::Vector3 convert(const JPH::Vec3Arg &vec) +{ + return { vec[0], vec[1], vec[2] }; +} +JPH::Vec3 convert(const Ogre::Vector3 &vec) +{ + return { vec.x, vec.y, vec.z }; +} +Ogre::Quaternion convert(const JPH::QuatArg &rot) +{ + return { rot.GetW(), rot.GetX(), rot.GetY(), rot.GetZ() }; +} +JPH::Quat convert(const Ogre::Quaternion &rot) +{ + return { rot.x, rot.y, rot.z, rot.w }; +} +void CompoundShapeBuilder::addShape(JPH::ShapeRefC shape, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation) +{ + shapeSettings.AddShape(JoltPhysics::convert(position), + JoltPhysics::convert(rotation), shape.GetPtr()); +} +JPH::ShapeRefC CompoundShapeBuilder::build() +{ + JPH::ShapeSettings::ShapeResult result = shapeSettings.Create(); + return result.Get(); +} +} + +class Physics { + JPH::PhysicsSystem physics_system; + // We need a temp allocator for temporary allocations during the physics update. We're + // pre-allocating 10 MB to avoid having to do allocations during the physics update. + // B.t.w. 10 MB is way too much for this example but it is a typical value you can use. + // If you don't want to pre-allocate you can also use TempAllocatorMalloc to fall back to + // malloc / free. + JPH::TempAllocatorImpl temp_allocator; + // We need a job system that will execute physics jobs on multiple threads. Typically + // you would implement the JobSystem interface yourself and let Jolt Physics run on top + // of your own job scheduler. JobSystemThreadPool is an example implementation. + JPH::JobSystemThreadPool job_system; + + // Create mapping table from object layer to broadphase layer + // Note: As this is an interface, PhysicsSystem will take a reference to this so this instance needs to stay alive! + // Also have a look at BroadPhaseLayerInterfaceTable or BroadPhaseLayerInterfaceMask for a simpler interface. + BPLayerInterfaceImpl broad_phase_layer_interface; + + // Create class that filters object vs broadphase layers + // Note: As this is an interface, PhysicsSystem will take a reference to this so this instance needs to stay alive! + // Also have a look at ObjectVsBroadPhaseLayerFilterTable or ObjectVsBroadPhaseLayerFilterMask for a simpler interface. + ObjectVsBroadPhaseLayerFilterImpl object_vs_broadphase_layer_filter; + + // Create class that filters object vs object layers + // Note: As this is an interface, PhysicsSystem will take a reference to this so this instance needs to stay alive! + // Also have a look at ObjectLayerPairFilterTable or ObjectLayerPairFilterMask for a simpler interface. + ObjectLayerPairFilterImpl object_vs_object_layer_filter; + + DebugRenderer *mDebugRenderer; + std::map id2node; + std::map node2id; + std::set characters; + std::set characterBodies; + bool debugDraw; + +public: + class ActivationListener : public JPH::BodyActivationListener { + public: + virtual void OnBodyActivated(const JPH::BodyID &inBodyID, + JPH::uint64 inBodyUserData) = 0; + virtual void OnBodyDeactivated(const JPH::BodyID &inBodyID, + JPH::uint64 inBodyUserData) = 0; + }; + class ContactListener : public JPH::ContactListener { + public: + virtual JPH::ValidateResult OnContactValidate( + const JPH::Body &inBody1, const JPH::Body &inBody2, + JPH::RVec3Arg inBaseOffset, + const JPH::CollideShapeResult &inCollisionResult) = 0; + virtual void + OnContactAdded(const JPH::Body &inBody1, + const JPH::Body &inBody2, + const JPH::ContactManifold &inManifold, + JPH::ContactSettings &ioSettings) = 0; + virtual void + OnContactPersisted(const JPH::Body &inBody1, + const JPH::Body &inBody2, + const JPH::ContactManifold &inManifold, + JPH::ContactSettings &ioSettings) = 0; + + virtual void + OnContactRemoved(const JPH::SubShapeIDPair &inSubShapePair) = 0; + }; + Physics(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode, + ActivationListener *activationListener = nullptr, + ContactListener *contactListener = nullptr) + : temp_allocator(10 * 1024 * 1024) + , job_system(JPH::cMaxPhysicsJobs, JPH::cMaxPhysicsBarriers, + std::thread::hardware_concurrency() - 1) + , mDebugRenderer(new DebugRenderer(scnMgr, cameraNode)) + , debugDraw(false) + { + // Create a factory, this class is responsible for creating instances of classes based on their name or hash and is mainly used for deserialization of saved data. + // It is not directly used in this example but still required. + JPH::Factory::sInstance = new JPH::Factory(); + + // Register all physics types with the factory and install their collision handlers with the CollisionDispatch class. + // If you have your own custom shape types you probably need to register their handlers with the CollisionDispatch before calling this function. + // If you implement your own default material (PhysicsMaterial::sDefault) make sure to initialize it before this function or else this function will create one for you. + JPH::RegisterTypes(); + + // This is the max amount of rigid bodies that you can add to the physics system. If you try to add more you'll get an error. + // Note: This value is low because this is a simple test. For a real project use something in the order of 65536. + const uint cMaxBodies = 1024; + + // This determines how many mutexes to allocate to protect rigid bodies from concurrent access. Set it to 0 for the default settings. + const uint cNumBodyMutexes = 0; + + // This is the max amount of body pairs that can be queued at any time (the broad phase will detect overlapping + // body pairs based on their bounding boxes and will insert them into a queue for the narrowphase). If you make this buffer + // too small the queue will fill up and the broad phase jobs will start to do narrow phase work. This is slightly less efficient. + // Note: This value is low because this is a simple test. For a real project use something in the order of 65536. + const uint cMaxBodyPairs = 1024; + + // This is the maximum size of the contact constraint buffer. If more contacts (collisions between bodies) are detected than this + // number then these contacts will be ignored and bodies will start interpenetrating / fall through the world. + // Note: This value is low because this is a simple test. For a real project use something in the order of 10240. + const uint cMaxContactConstraints = 1024; + + // Now we can create the actual physics system. + physics_system.Init(cMaxBodies, cNumBodyMutexes, cMaxBodyPairs, + cMaxContactConstraints, + broad_phase_layer_interface, + object_vs_broadphase_layer_filter, + object_vs_object_layer_filter); + + // A body activation listener gets notified when bodies activate and go to sleep + // Note that this is called from a job so whatever you do here needs to be thread safe. + // Registering one is entirely optional. + if (activationListener) + physics_system.SetBodyActivationListener( + activationListener); + + // A contact listener gets notified when bodies (are about to) collide, and when they separate again. + // Note that this is called from a job so whatever you do here needs to be thread safe. + // Registering one is entirely optional. + if (contactListener) + physics_system.SetContactListener(contactListener); + + // The main way to interact with the bodies in the physics system is through the body interface. There is a locking and a non-locking + // variant of this. We're going to use the locking version (even though we're not planning to access bodies from multiple threads) + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); +#if 0 + + // Next we can create a rigid body to serve as the floor, we make a large box + // Create the settings for the collision volume (the shape). + // Note that for simple shapes (like boxes) you can also directly construct a BoxShape. + JPH::BoxShapeSettings floor_shape_settings( + JPH::Vec3(100.0f, 1.0f, 100.0f)); + floor_shape_settings + .SetEmbedded(); // A ref counted object on the stack (base class RefTarget) should be marked as such to prevent it from being freed when its reference count goes to 0. + + // Create the shape + JPH::ShapeSettings::ShapeResult floor_shape_result = + floor_shape_settings.Create(); + JPH::ShapeRefC floor_shape = + floor_shape_result + .Get(); // We don't expect an error here, but you can check floor_shape_result for HasError() / GetError() + + // Create the settings for the body itself. Note that here you can also set other properties like the restitution / friction. + JPH::Body *floor; + JPH::BodyID sphere_id; + { + using namespace JPH::literals; + JPH::BodyCreationSettings floor_settings( + floor_shape, JPH::RVec3(0.0_r, -1.0_r, 0.0_r), + JPH::Quat::sIdentity(), + JPH::EMotionType::Static, Layers::NON_MOVING); + floor = body_interface.CreateBody( + floor_settings); // Note that if we run out of bodies this can return nullptr + // Create the actual rigid body + // Add it to the world + body_interface.AddBody(floor->GetID(), + JPH::EActivation::DontActivate); + + // Now create a dynamic body to bounce on the floor + // Note that this uses the shorthand version of creating and adding a body to the world + JPH::BodyCreationSettings sphere_settings( + new JPH::SphereShape(0.5f), + JPH::RVec3(0.0_r, 2.0_r, 0.0_r), + JPH::Quat::sIdentity(), + JPH::EMotionType::Dynamic, Layers::MOVING); + sphere_id = body_interface.CreateAndAddBody( + sphere_settings, JPH::EActivation::Activate); + + // Now you can interact with the dynamic body, in this case we're going to give it a velocity. + // (note that if we had used CreateBody then we could have set the velocity straight on the body before adding it to the physics system) + body_interface.SetLinearVelocity( + sphere_id, JPH::Vec3(0.0f, -5.0f, 0.0f)); + } + + // We simulate the physics world in discrete time steps. 60 Hz is a good rate to update the physics system. + const float cDeltaTime = 1.0f / 60.0f; + + // Optional step: Before starting the physics simulation you can optimize the broad phase. This improves collision detection performance (it's pointless here because we only have 2 bodies). + // You should definitely not call this every frame or when e.g. streaming in a new level section as it is an expensive operation. + // Instead insert all new objects in batches instead of 1 at a time to keep the broad phase efficient. + physics_system.OptimizeBroadPhase(); + + // Now we're ready to simulate the body, keep simulating until it goes to sleep + uint step = 0; + while (body_interface.IsActive(sphere_id)) { + // Next step + ++step; + + // Output current position and velocity of the sphere + JPH::RVec3 position = + body_interface.GetCenterOfMassPosition( + sphere_id); + JPH::Vec3 velocity = + body_interface.GetLinearVelocity(sphere_id); + std::cout << "Step " << step << ": Position = (" + << position.GetX() << ", " << position.GetY() + << ", " << position.GetZ() + << "), Velocity = (" << velocity.GetX() + << ", " << velocity.GetY() << ", " + << velocity.GetZ() << ")" << std::endl; + + // If you take larger steps than 1 / 60th of a second you need to do multiple collision steps in order to keep the simulation stable. Do 1 collision step per 1 / 60th of a second (round up). + const int cCollisionSteps = 1; + + // Step the world + physics_system.Update(cDeltaTime, cCollisionSteps, + &temp_allocator, &job_system); + } + + // Remove the sphere from the physics system. Note that the sphere itself keeps all of its state and can be re-added at any time. + body_interface.RemoveBody(sphere_id); + + // Destroy the sphere. After this the sphere ID is no longer valid. + body_interface.DestroyBody(sphere_id); + + // Remove and destroy the floor + body_interface.RemoveBody(floor->GetID()); + body_interface.DestroyBody(floor->GetID()); +#endif + physics_system.SetGravity(JPH::Vec3(0, -0.1f, 0)); + } + ~Physics() + { + // Unregisters all types with the factory and cleans up the default material + JPH::UnregisterTypes(); + + // Destroy the factory + delete JPH::Factory::sInstance; + JPH::Factory::sInstance = nullptr; + } + float timeAccumulator = 0.0f; + float fixedDeltaTime = 1.0f / 60.0f; + void update(float dt) + { + JPH::BodyIDVector bodies; + physics_system.GetBodies(bodies); + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + for (JPH::BodyID bID : bodies) { + if (id2node.find(bID) == id2node.end()) + continue; + if (body_interface.GetMotionType(bID) == + JPH::EMotionType::Kinematic) { + Ogre::SceneNode *node = id2node[bID]; + body_interface.SetPositionAndRotationWhenChanged( + bID, + JoltPhysics::convert( + node->_getDerivedPosition()), + JoltPhysics::convert( + node->_getDerivedOrientation()), + JPH::EActivation::Activate); + } + } + for (JPH::Character *ch : characters) { + JPH::BodyID bID = ch->GetBodyID(); + if (id2node.find(bID) == id2node.end()) + continue; + Ogre::SceneNode *node = id2node[bID]; + ch->SetRotation(JoltPhysics::convert( + node->_getDerivedOrientation())); + } + int cCollisionSteps = 1; + timeAccumulator += dt; + if (debugDraw) + cCollisionSteps = 4; + while (timeAccumulator >= fixedDeltaTime) { + physics_system.Update(dt, cCollisionSteps, + &temp_allocator, &job_system); + timeAccumulator -= fixedDeltaTime; + } + for (JPH::BodyID bID : bodies) { + JPH::Vec3 p; + JPH::Quat q; + if (id2node.find(bID) == id2node.end()) + continue; + if (!body_interface.IsAdded(bID)) + continue; + if (!body_interface.IsActive(bID)) + continue; + if (body_interface.GetMotionType(bID) != + JPH::EMotionType::Dynamic) + continue; + body_interface.GetPositionAndRotation(bID, p, q); + Ogre::SceneNode *node = id2node[bID]; + node->_setDerivedPosition(JoltPhysics::convert(p)); + node->_setDerivedOrientation(JoltPhysics::convert(q)); + } + for (JPH::Character *ch : characters) + ch->PostSimulation(0.1f); + + if (debugDraw) + physics_system.DrawBodies( + JPH::BodyManager::DrawSettings(), + mDebugRenderer); + mDebugRenderer->finish(); + mDebugRenderer->NextFrame(); +#if 0 + std::cout << "bodies: " << physics_system.GetNumBodies() + << " / " + << physics_system.GetNumActiveBodies( + JPH::EBodyType::RigidBody) + << std::endl; +#endif + } + void setDebugDraw(bool enable) + { + debugDraw = enable; + } + static JPH::ShapeRefC createBoxShape(float x, float y, float z) + { + return new JPH::BoxShape(JPH::RVec3(x, y, z)); + } + static JPH::ShapeRefC createCylinderShape(float halfHeight, + float radius) + { + return new JPH::CylinderShape(halfHeight, radius); + } + JPH::BodyCreationSettings createBodyCreationSettings( + JPH::Shape *shape, JPH::Vec3 &position, JPH::Quat &rotation, + JPH::EMotionType motionType, JPH::ObjectLayer layer) + { + JPH::BodyCreationSettings body_settings( + shape, position, rotation, motionType, layer); + return body_settings; + } + JPH::BodyCreationSettings + createBodyCreationSettings(JPH::ShapeSettings *shapeSettings, + JPH::Vec3 &position, JPH::Quat &rotation, + JPH::EMotionType motionType, + JPH::ObjectLayer layer) + { + JPH::BodyCreationSettings body_settings( + shapeSettings, position, rotation, motionType, layer); + return body_settings; + } + void addBody(const JPH::BodyID &body, JPH::EActivation activation) + { + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + body_interface.AddBody(body, activation); + } + bool isAdded(const JPH::BodyID &body) + { + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + return body_interface.IsAdded(body); + } + void addAngularImpulse(const JPH::BodyID &id, + const Ogre::Vector3 &impulse) + { + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + body_interface.AddAngularImpulse(id, + JoltPhysics::convert(impulse)); + } + JPH::BodyID createBody(const JPH::BodyCreationSettings &settings, + ActivationListener *listener = nullptr) + { + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + JPH::Body *body = body_interface.CreateBody(settings); + return body->GetID(); + } + void removeBody(const JPH::BodyID &id) + { + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + body_interface.RemoveBody(id); + } + void destroyBody(const JPH::BodyID &id) + { + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + body_interface.DestroyBody(id); + node2id.erase(id2node[id]); + id2node.erase(id); + } + JPH::ShapeRefC createShape(const JPH::ShapeSettings &settings) + { + JPH::ShapeSettings::ShapeResult result = settings.Create(); + JPH::ShapeRefC shape = result.Get(); + return shape; + } + JPH::BodyID createBody(const JPH::Shape *shape, float mass, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + JPH::EMotionType motion, JPH::ObjectLayer layer, + ActivationListener *listener = nullptr) + { + JPH::BodyCreationSettings bodySettings( + shape, JoltPhysics::convert(position), + JoltPhysics::convert(rotation), motion, layer); + if (mass > 0.001f) { + JPH::MassProperties msp; + msp.ScaleToMass(mass); + bodySettings.mMassPropertiesOverride = msp; + } + return createBody(bodySettings, listener); + } + JPH::BodyID createBody(const JPH::Shape *shape, float mass, + Ogre::SceneNode *node, JPH::EMotionType motion, + JPH::ObjectLayer layer, + ActivationListener *listener = nullptr) + { + const Ogre::Vector3 &position = node->_getDerivedPosition(); + const Ogre::Quaternion &rotation = + node->_getDerivedOrientation(); + std::cout << "body position: " << position << std::endl; + JPH::BodyCreationSettings bodySettings( + shape, JoltPhysics::convert(position), + JoltPhysics::convert(rotation), motion, layer); + if (mass > 0.001f) { + JPH::MassProperties msp; + msp.ScaleToMass(mass); + bodySettings.mMassPropertiesOverride = msp; + } + JPH::BodyID id = createBody(bodySettings, listener); + if (shape->GetType() == JPH::EShapeType::HeightField) { + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + body_interface.SetFriction(id, 0.7f); + } + id2node[id] = node; + node2id[node] = id; + return id; + } + JPH::BodyID createSensor(const JPH::Shape *shape, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + JPH::EMotionType motion, + JPH::ObjectLayer layer) + { + JPH::BodyCreationSettings bodySettings( + shape, JoltPhysics::convert(position), + JoltPhysics::convert(rotation), motion, layer); + bodySettings.mIsSensor = true; + return createBody(bodySettings, nullptr); + } + JPH::BodyID createSensor(const JPH::Shape *shape, Ogre::SceneNode *node, + JPH::EMotionType motion, + JPH::ObjectLayer layer) + { + const Ogre::Vector3 &position = node->_getDerivedPosition(); + const Ogre::Quaternion &rotation = + node->_getDerivedOrientation(); + std::cout << "body position: " << position << std::endl; + JPH::BodyCreationSettings bodySettings( + shape, JoltPhysics::convert(position), + JoltPhysics::convert(rotation), motion, layer); + bodySettings.mIsSensor = true; + JPH::BodyID id = createBody(bodySettings); + if (shape->GetType() == JPH::EShapeType::HeightField) { + JPH::BodyInterface &body_interface = + physics_system.GetBodyInterface(); + body_interface.SetFriction(id, 0.7f); + } + id2node[id] = node; + node2id[node] = id; + return id; + } + void addShapeToCompound(JPH::Ref compoundShape, + JPH::ShapeRefC childShape, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation) + { + JPH::MutableCompoundShape *master = + static_cast( + compoundShape.GetPtr()); + master->AddShape(JoltPhysics::convert(position), + JoltPhysics::convert(rotation), + childShape.GetPtr()); + } + class CharacterListener : public JPH::ContactListener {}; + JPH::CharacterBase *createCharacter(Ogre::SceneNode *node, + float characterHeight, + float characterRadius) + { + JPH::CharacterSettings settings; + settings.mLayer = Layers::MOVING; + float characterRadiusStanding = 0.2f; + settings.mSupportingVolume = + JPH::Plane(JPH::Vec3::sAxisY(), -0.2f); + settings.mShape = + JPH::RotatedTranslatedShapeSettings( + JPH::Vec3(0, + 0.5f * characterHeight + + characterRadius, + 0), + JPH::Quat::sIdentity(), + new JPH::CapsuleShape(0.5f * characterHeight, + characterRadius)) + .Create() + .Get(); + settings.mSupportingVolume = + JPH::Plane(JPH::Vec3::sAxisY(), -characterRadius); + JPH::Character *ch = new JPH::Character( + &settings, + JoltPhysics::convert(node->_getDerivedPosition()), + JoltPhysics::convert(node->_getDerivedOrientation()), 0, + &physics_system); + JPH::BodyID id = ch->GetBodyID(); + id2node[id] = node; + node2id[node] = id; + characterBodies.insert(id); + characters.insert(ch); + return ch; + } + JPH::ShapeRefC createBoxShape(Ogre::Vector3 extents) + { + JPH::Vec3 h(extents.x, extents.y, extents.z); + return new JPH::BoxShape(h); + } + JPH::ShapeRefC createSphereShape(float radius) + { + return new JPH::SphereShape(radius); + } + JPH::ShapeRefC createCapsuleShape(float halfHeightOfCylinder, + float radius) + { + return new JPH::CapsuleShape(halfHeightOfCylinder, radius); + } + JPH::ShapeRefC createMeshShape(Ogre::MeshPtr mesh) + { + JPH::VertexList vertices; + JPH::IndexedTriangleList triangles; + std::vector indices; + int count = mesh->getNumSubMeshes(); + int i, j; + int indexCount = 0; + int vertexCount = 0; + int sharedVertexOffset = 0; + for (i = 0; i < count; i++) { + Ogre::SubMesh *submesh = mesh->getSubMesh(i); + indexCount += submesh->indexData->indexCount; + if (submesh->useSharedVertices) + vertexCount += + mesh->sharedVertexData->vertexCount; + else + vertexCount += submesh->vertexData->vertexCount; + } + indices.reserve(indexCount); + vertices.reserve(vertexCount); + size_t currentVertexOffset = 0; + bool added_shared = false; + for (i = 0; i < count; i++) { + Ogre::SubMesh *submesh = mesh->getSubMesh(i); + Ogre::VertexData *vertex_data = + submesh->useSharedVertices ? + mesh->sharedVertexData : + submesh->vertexData; + bool add_vertices = + (submesh->useSharedVertices && !added_shared) || + !submesh->useSharedVertices; + if (add_vertices) { + if (submesh->useSharedVertices) + sharedVertexOffset = vertices.size(); + const Ogre::VertexDeclaration *decl = + vertex_data->vertexDeclaration; + const Ogre::VertexBufferBinding *bind = + vertex_data->vertexBufferBinding; + const Ogre::VertexElement *position_element = + decl->findElementBySemantic( + Ogre::VES_POSITION); + if (!position_element) + continue; + Ogre::HardwareVertexBufferSharedPtr vbuf = + bind->getBuffer( + position_element->getSource()); + unsigned char *vertex_buffer = static_cast< + unsigned char *>(vbuf->lock( + Ogre::HardwareBuffer::HBL_READ_ONLY)); + int vertexSize = vbuf->getVertexSize(); + for (j = 0; j < vertex_data->vertexCount; j++) { + float *position_data; + position_element + ->baseVertexPointerToElement( + vertex_buffer, + &position_data); + vertices.push_back( + { position_data[0], + position_data[1], + position_data[2] }); + vertex_buffer += vertexSize; + } + if (submesh->useSharedVertices) + added_shared = true; + vbuf->unlock(); + } + Ogre::HardwareIndexBufferSharedPtr ibuf = + submesh->indexData->indexBuffer; + size_t numIndices = submesh->indexData->indexCount; + size_t vertexOffset = submesh->useSharedVertices ? + sharedVertexOffset : + currentVertexOffset; + if (ibuf->getType() == + Ogre::HardwareIndexBuffer::IT_32BIT) { + unsigned int *pIndices = static_cast< + unsigned int *>(ibuf->lock( + Ogre::HardwareBuffer::HBL_READ_ONLY)); + for (j = 0; j < numIndices; j++) { + indices.push_back( + (uint32_t)pIndices[j] + + vertexOffset); + } + ibuf->unlock(); + } else { + unsigned short *pIndices = static_cast< + unsigned short *>(ibuf->lock( + Ogre::HardwareBuffer::HBL_READ_ONLY)); + for (j = 0; j < numIndices; j++) { + indices.push_back( + (uint32_t)pIndices[j] + + vertexOffset); + } + ibuf->unlock(); + } + currentVertexOffset = vertices.size(); + } + triangles.resize(indices.size() / 3); + for (j = 0; j < indices.size() / 3; j++) + triangles[j] = { indices[j * 3 + 0], indices[j * 3 + 1], + indices[j * 3 + 2] }; + JPH::MeshShapeSettings mesh_shape_settings(vertices, triangles); + JPH::ShapeSettings::ShapeResult result = + mesh_shape_settings.Create(); + OgreAssert(result.Get(), "Can not create mesh shape"); + return result.Get(); + } + JPH::ShapeRefC createMeshShape(Ogre::String meshName) + { + Ogre::DefaultHardwareBufferManager dmgr; + Ogre::MeshPtr mesh = + Ogre::MeshManager::getSingleton().getByName(meshName); + if (!mesh->isLoaded()) { + mesh->setHardwareBufferManager(&dmgr); + mesh->load(); + } + return createMeshShape(mesh); + } + JPH::ShapeRefC createConvexHullShape(Ogre::MeshPtr mesh) + { + std::vector vertices; + JPH::IndexedTriangleList triangles; + std::vector indices; + int count = mesh->getNumSubMeshes(); + int i, j; + int indexCount = 0; + int vertexCount = 0; + int sharedVertexOffset = 0; + for (i = 0; i < count; i++) { + Ogre::SubMesh *submesh = mesh->getSubMesh(i); + indexCount += submesh->indexData->indexCount; + if (submesh->useSharedVertices) + vertexCount += + mesh->sharedVertexData->vertexCount; + else + vertexCount += submesh->vertexData->vertexCount; + } + indices.reserve(indexCount); + vertices.reserve(vertexCount); + size_t currentVertexOffset = 0; + bool added_shared = false; + for (i = 0; i < count; i++) { + Ogre::SubMesh *submesh = mesh->getSubMesh(i); + Ogre::VertexData *vertex_data = + submesh->useSharedVertices ? + mesh->sharedVertexData : + submesh->vertexData; + bool add_vertices = + (submesh->useSharedVertices && !added_shared) || + !submesh->useSharedVertices; + if (add_vertices) { + if (submesh->useSharedVertices) + sharedVertexOffset = vertices.size(); + const Ogre::VertexDeclaration *decl = + vertex_data->vertexDeclaration; + const Ogre::VertexBufferBinding *bind = + vertex_data->vertexBufferBinding; + const Ogre::VertexElement *position_element = + decl->findElementBySemantic( + Ogre::VES_POSITION); + if (!position_element) + continue; + Ogre::HardwareVertexBufferSharedPtr vbuf = + bind->getBuffer( + position_element->getSource()); + unsigned char *vertex_buffer = static_cast< + unsigned char *>(vbuf->lock( + Ogre::HardwareBuffer::HBL_READ_ONLY)); + int vertexSize = vbuf->getVertexSize(); + for (j = 0; j < vertex_data->vertexCount; j++) { + float *position_data; + position_element + ->baseVertexPointerToElement( + vertex_buffer, + &position_data); + vertices.push_back( + { position_data[0], + position_data[1], + position_data[2] }); + vertex_buffer += vertexSize; + } + if (submesh->useSharedVertices) + added_shared = true; + vbuf->unlock(); + } + Ogre::HardwareIndexBufferSharedPtr ibuf = + submesh->indexData->indexBuffer; + size_t numIndices = submesh->indexData->indexCount; + size_t vertexOffset = submesh->useSharedVertices ? + sharedVertexOffset : + currentVertexOffset; + if (ibuf->getType() == + Ogre::HardwareIndexBuffer::IT_32BIT) { + unsigned int *pIndices = static_cast< + unsigned int *>(ibuf->lock( + Ogre::HardwareBuffer::HBL_READ_ONLY)); + for (j = 0; j < numIndices; j++) { + indices.push_back( + (uint32_t)pIndices[j] + + vertexOffset); + } + ibuf->unlock(); + } else { + unsigned short *pIndices = static_cast< + unsigned short *>(ibuf->lock( + Ogre::HardwareBuffer::HBL_READ_ONLY)); + for (j = 0; j < numIndices; j++) { + indices.push_back( + (uint32_t)pIndices[j] + + vertexOffset); + } + ibuf->unlock(); + } + currentVertexOffset = vertices.size(); + } + triangles.resize(indices.size() / 3); + for (j = 0; j < indices.size() / 3; j++) + triangles[j] = { indices[j * 3 + 0], indices[j * 3 + 1], + indices[j * 3 + 2] }; + JPH::ConvexHullShapeSettings mesh_shape_settings( + vertices.data(), vertices.size()); + JPH::ShapeSettings::ShapeResult result = + mesh_shape_settings.Create(); + OgreAssert(result.Get(), "Can not create mesh shape"); + return result.Get(); + } + JPH::ShapeRefC createConvexHullShape(Ogre::String meshName) + { + auto p = Ogre::DefaultHardwareBufferManager::getSingletonPtr(); + if (!p) { + new Ogre::DefaultHardwareBufferManager; + p = Ogre::DefaultHardwareBufferManager::getSingletonPtr(); + } + Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().load( + meshName, "General"); + if (mesh.get()) { + if (!mesh->isLoaded()) { + mesh->setHardwareBufferManager(p); + mesh->load(); + } + return createConvexHullShape(mesh); + } + OgreAssert(mesh.get(), "No file " + meshName); + return JPH::ShapeRefC(); + } + /// Create a height field shape of inSampleCount * inSampleCount vertices. + /// The height field is a surface defined by: inOffset + inScale * (x, inSamples[y * inSampleCount + x], y). + /// where x and y are integers in the range x and y e [0, inSampleCount - 1]. + /// inSampleCount: inSampleCount / mBlockSize must be minimally 2 and a power of 2 is the most efficient in terms of performance and storage. + /// inSamples: inSampleCount^2 vertices. + /// inMaterialIndices: (inSampleCount - 1)^2 indices that index into inMaterialList. + JPH::ShapeRefC createHeightfieldShape(const float *samples, + Ogre::Vector3 offset, + Ogre::Vector3 scale, + int sampleCount) + { + int i; + JPH::HeightFieldShapeSettings heightfieldSettings( + samples, JoltPhysics::convert(offset), + JoltPhysics::convert(scale), (uint32_t)sampleCount); + for (i = 0; i < sampleCount; i++) { + memcpy(heightfieldSettings.mHeightSamples.data() + + sampleCount * i, + samples + sampleCount * (sampleCount - i - 1), + sizeof(float) * sampleCount); + } + JPH::ShapeSettings::ShapeResult result = + heightfieldSettings.Create(); + OgreAssert(result.Get(), "Can not create heightfield shape"); + return result.Get(); + } + JPH::ShapeRefC createMutableCompoundShape( + const std::vector &shapes, + const std::vector &positions, + const std::vector &rotations) + { + int i; + OgreAssert(shapes.size() == positions.size() && + shapes.size() == rotations.size(), + "bad parameters"); + JPH::MutableCompoundShapeSettings settings; + for (i = 0; i < shapes.size(); i++) + settings.AddShape(JoltPhysics::convert(positions[i]), + JoltPhysics::convert(rotations[i]), + shapes[i].GetPtr()); + JPH::ShapeSettings::ShapeResult result = settings.Create(); + OgreAssert(result.Get(), "Can not create compound shape"); + return result.Get(); + } + JPH::ShapeRefC createStaticCompoundShape( + const std::vector &shapes, + const std::vector &positions, + const std::vector &rotations) + { + int i; + OgreAssert(shapes.size() == positions.size() && + shapes.size() == rotations.size(), + "bad parameters"); + JPH::StaticCompoundShapeSettings settings; + for (i = 0; i < shapes.size(); i++) + settings.AddShape(JoltPhysics::convert(positions[i]), + JoltPhysics::convert(rotations[i]), + shapes[i].GetPtr()); + JPH::ShapeSettings::ShapeResult result = settings.Create(); + OgreAssert(result.Get(), "Can not create compound shape"); + return result.Get(); + } + JPH::ShapeRefC + createOffsetCenterOfMassShape(const Ogre::Vector3 &offset, + JPH::ShapeRefC shape) + { + JPH::OffsetCenterOfMassShapeSettings settings( + JoltPhysics::convert(offset), shape.GetPtr()); + JPH::ShapeSettings::ShapeResult result = settings.Create(); + OgreAssert(result.Get(), "Can not create com offset shape"); + return result.Get(); + } + void applyBuoyancyImpulse(JPH::BodyID id, + const Ogre::Vector3 &surfacePosition, + const Ogre::Vector3 &surfaceNormal, + float buoyancy, float linearDrag, + float angularDrag, + const Ogre::Vector3 &fluidVelocity, + const Ogre::Vector3 &gravity, float dt) + { + JPH::BodyLockWrite lock(physics_system.GetBodyLockInterface(), + id); + JPH::Body &body = lock.GetBody(); + body.ApplyBuoyancyImpulse(JoltPhysics::convert(surfacePosition), + JoltPhysics::convert(surfaceNormal), + buoyancy, linearDrag, angularDrag, + JoltPhysics::convert(fluidVelocity), + JoltPhysics::convert(gravity), dt); + } + void applyBuoyancyImpulse(JPH::BodyID id, + const Ogre::Vector3 &surfacePosition, + const Ogre::Vector3 &surfaceNormal, + float buoyancy, float linearDrag, + float angularDrag, + const Ogre::Vector3 &fluidVelocity, float dt) + { + JPH::BodyLockWrite lock(physics_system.GetBodyLockInterface(), + id); + JPH::Body &body = lock.GetBody(); + body.ApplyBuoyancyImpulse(JoltPhysics::convert(surfacePosition), + JoltPhysics::convert(surfaceNormal), + buoyancy, linearDrag, angularDrag, + JoltPhysics::convert(fluidVelocity), + physics_system.GetGravity(), dt); + } + bool isActive(JPH::BodyID id) + { + return physics_system.GetBodyInterface().IsActive(id); + } + void activate(JPH::BodyID id) + { + return physics_system.GetBodyInterface().ActivateBody(id); + } + Ogre::Vector3 getPosition(JPH::BodyID id) + { + return JoltPhysics::convert( + physics_system.GetBodyInterface().GetPosition(id)); + } + Ogre::Quaternion getRotation(JPH::BodyID id) + { + return JoltPhysics::convert( + physics_system.GetBodyInterface().GetRotation(id)); + } + void getPositionAndRotation(JPH::BodyID id, Ogre::Vector3 &position, + Ogre::Quaternion &rotation) + { + JPH::Vec3 _position; + JPH::Quat _rotation; + physics_system.GetBodyInterface().GetPositionAndRotation( + id, _position, _rotation); + position = JoltPhysics::convert(_position); + rotation = JoltPhysics::convert(_rotation); + } + void setPosition(JPH::BodyID id, const Ogre::Vector3 &position, + bool activate = true) + { + physics_system.GetBodyInterface().SetPosition( + id, JoltPhysics::convert(position), + activate ? JPH::EActivation::Activate : + JPH::EActivation::DontActivate); + } + void setRotation(JPH::BodyID id, const Ogre::Quaternion &rotation, + bool activate = true) + { + physics_system.GetBodyInterface().SetRotation( + id, JoltPhysics::convert(rotation), + activate ? JPH::EActivation::Activate : + JPH::EActivation::DontActivate); + } + void setPositionAndRotation(JPH::BodyID id, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + bool activate = true) + { + physics_system.GetBodyInterface().SetPositionAndRotation( + id, JoltPhysics::convert(position), + JoltPhysics::convert(rotation), + activate ? JPH::EActivation::Activate : + JPH::EActivation::DontActivate); + } + Ogre::Vector3 getLinearVelocity(JPH::BodyID id) + { + return JoltPhysics::convert( + physics_system.GetBodyInterface().GetLinearVelocity( + id)); + } + void broadphaseQuery(float dt, const Ogre::Vector3 &position, + std::set &inWater) + { + JPH::Vec3 surface_point = JoltPhysics::convert( + position + Ogre::Vector3(0, -0.1f, 0)); + + MyCollector collector(&physics_system, surface_point, + JPH::Vec3::sAxisY(), dt); + // Apply buoyancy to all bodies that intersect with the water + JPH::AABox water_box(-JPH::Vec3(1000, 1000, 1000), + JPH::Vec3(1000, 0.1f, 1000)); + water_box.Translate(JPH::Vec3(surface_point)); + physics_system.GetBroadPhaseQuery().CollideAABox( + water_box, collector, + JPH::SpecifiedBroadPhaseLayerFilter( + BroadPhaseLayers::MOVING), + JPH::SpecifiedObjectLayerFilter(Layers::MOVING)); + inWater.clear(); + for (JPH::BodyID inBodyID : collector.mInWater) { + inWater.insert(inBodyID); +#if 0 + std::cout << "addHit: " + << JoltPhysics::convert( + body.GetPosition()) + << std::endl; +#endif + } + } +}; + +void physics() +{ + // Physics physics; + // physics.update(1.0f / 60.0f); +} + +Physics *phys = nullptr; +JoltPhysicsWrapper::JoltPhysicsWrapper(Ogre::SceneManager *scnMgr, + Ogre::SceneNode *cameraNode) + : Ogre::Singleton() +{ + // Register allocation hook. In this example we'll just let Jolt use malloc / free but you can override these if you want (see Memory.h). + // This needs to be done before any other Jolt function is called. + JPH::RegisterDefaultAllocator(); + + // Install trace and assert callbacks + JPH::Trace = TraceImpl; + JPH_IF_ENABLE_ASSERTS(JPH::AssertFailed = AssertFailedImpl;) + + phys = new Physics(scnMgr, cameraNode); +} + +JoltPhysicsWrapper::~JoltPhysicsWrapper() +{ + if (phys) + delete phys; +} + +void JoltPhysicsWrapper::update(float dt) +{ + phys->update(dt); +} + +void JoltPhysicsWrapper::addBody(const JPH::BodyID &body, + JPH::EActivation activation) +{ + phys->addBody(body, activation); +} + +bool JoltPhysicsWrapper::isAdded(const JPH::BodyID &body) +{ + return phys->isAdded(body); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createBoxShape(const Ogre::Vector3 &extents) +{ + return phys->createBoxShape(extents); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createSphereShape(float radius) +{ + return phys->createSphereShape(radius); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createCylinderShape(float halfHeight, + float radius) +{ + return phys->createCylinderShape(halfHeight, radius); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createMeshShape(Ogre::MeshPtr mesh) +{ + return phys->createMeshShape(mesh); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createMeshShape(Ogre::String meshName) +{ + return phys->createMeshShape(meshName); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createConvexHullShape(Ogre::MeshPtr mesh) +{ + return phys->createConvexHullShape(mesh); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createConvexHullShape(Ogre::String meshName) +{ + return phys->createConvexHullShape(meshName); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createHeightfieldShape(const float *samples, + Ogre::Vector3 offset, + Ogre::Vector3 scale, + int sampleCount) +{ + return phys->createHeightfieldShape(samples, offset, scale, + sampleCount); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createMutableCompoundShape( + const std::vector &shapes, + const std::vector &positions, + const std::vector &rotations) +{ + return phys->createMutableCompoundShape(shapes, positions, rotations); +} + +JPH::ShapeRefC JoltPhysicsWrapper::createStaticCompoundShape( + const std::vector &shapes, + const std::vector &positions, + const std::vector &rotations) +{ + return phys->createStaticCompoundShape(shapes, positions, rotations); +} + +JPH::ShapeRefC +JoltPhysicsWrapper::createOffsetCenterOfMassShape(const Ogre::Vector3 &offset, + JPH::ShapeRefC shape) +{ + return phys->createOffsetCenterOfMassShape(offset, shape); +} + +JPH::BodyID +JoltPhysicsWrapper::createBody(const JPH::BodyCreationSettings &settings) +{ + return phys->createBody(settings); +} +JPH::BodyID JoltPhysicsWrapper::createBody(const JPH::Shape *shape, float mass, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + JPH::EMotionType motion, + JPH::ObjectLayer layer) +{ + return phys->createBody(shape, mass, position, rotation, motion, layer); +} +JPH::BodyID JoltPhysicsWrapper::createBody(const JPH::Shape *shape, float mass, + Ogre::SceneNode *node, + JPH::EMotionType motion, + JPH::ObjectLayer layer) +{ + return phys->createBody(shape, mass, node, motion, layer); +} +JPH::BodyID JoltPhysicsWrapper::createSensor(const JPH::Shape *shape, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + JPH::EMotionType motion, + JPH::ObjectLayer layer) +{ + return phys->createSensor(shape, position, rotation, motion, layer); +} +JPH::BodyID JoltPhysicsWrapper::createSensor(const JPH::Shape *shape, + Ogre::SceneNode *node, + JPH::EMotionType motion, + JPH::ObjectLayer layer) +{ + return phys->createSensor(shape, node, motion, layer); +} +JPH::CharacterBase *JoltPhysicsWrapper::createCharacter(Ogre::SceneNode *node, + float characterHeight, + float characterRadius) +{ + return phys->createCharacter(node, characterHeight, characterRadius); +} +void JoltPhysicsWrapper::addShapeToCompound(JPH::Ref compoundShape, + JPH::ShapeRefC childShape, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation) +{ + phys->addShapeToCompound(compoundShape, childShape, position, rotation); +} +void JoltPhysicsWrapper::removeBody(const JPH::BodyID &id) +{ + phys->removeBody(id); +} +void JoltPhysicsWrapper::destroyBody(const JPH::BodyID &id) +{ + phys->destroyBody(id); +} +void JoltPhysicsWrapper::setDebugDraw(bool enable) +{ + phys->setDebugDraw(enable); +} +void JoltPhysicsWrapper::broadphaseQuery(float dt, + const Ogre::Vector3 &position, + std::set &inWater) +{ + phys->broadphaseQuery(dt, position, inWater); +} +void JoltPhysicsWrapper::applyBuoyancyImpulse( + JPH::BodyID id, const Ogre::Vector3 &surfacePosition, + const Ogre::Vector3 &surfaceNormal, float buoyancy, float linearDrag, + float angularDrag, const Ogre::Vector3 &fluidVelocity, + const Ogre::Vector3 &gravity, float dt) +{ + phys->applyBuoyancyImpulse(id, surfacePosition, surfaceNormal, buoyancy, + linearDrag, angularDrag, fluidVelocity, + gravity, dt); +} +void JoltPhysicsWrapper::applyBuoyancyImpulse( + JPH::BodyID id, const Ogre::Vector3 &surfacePosition, + const Ogre::Vector3 &surfaceNormal, float buoyancy, float linearDrag, + float angularDrag, const Ogre::Vector3 &fluidVelocity, float dt) +{ + phys->applyBuoyancyImpulse(id, surfacePosition, surfaceNormal, buoyancy, + linearDrag, angularDrag, fluidVelocity, dt); +} +bool JoltPhysicsWrapper::isActive(JPH::BodyID id) +{ + return phys->isActive(id); +} +void JoltPhysicsWrapper::activate(JPH::BodyID id) +{ + phys->activate(id); +} +Ogre::Vector3 JoltPhysicsWrapper::getPosition(JPH::BodyID id) +{ + return phys->getPosition(id); +} +void JoltPhysicsWrapper::setPosition(JPH::BodyID id, + const Ogre::Vector3 &position, + bool activate) +{ + return phys->setPosition(id, position, activate); +} +void JoltPhysicsWrapper::setRotation(JPH::BodyID id, + const Ogre::Quaternion &rotation, + bool activate) +{ + phys->setRotation(id, rotation, activate); +} +void JoltPhysicsWrapper::getPositionAndRotation(JPH::BodyID id, + Ogre::Vector3 &position, + Ogre::Quaternion &rotation) +{ + phys->getPositionAndRotation(id, position, rotation); +} +void JoltPhysicsWrapper::setPositionAndRotation( + JPH::BodyID id, const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, bool activate) +{ + phys->setPositionAndRotation(id, position, rotation, activate); +} +Ogre::Vector3 JoltPhysicsWrapper::getLinearVelocity(JPH::BodyID id) +{ + return phys->getLinearVelocity(id); +} +void JoltPhysicsWrapper::addAngularImpulse(const JPH::BodyID &id, + const Ogre::Vector3 &impulse) +{ + return phys->addAngularImpulse(id, impulse); +} +template <> +JoltPhysicsWrapper *Ogre::Singleton::msSingleton = 0; \ No newline at end of file diff --git a/src/physics/physics.h b/src/physics/physics.h new file mode 100644 index 0000000..dda5b12 --- /dev/null +++ b/src/physics/physics.h @@ -0,0 +1,143 @@ +#ifndef __PHYSICS_H_ +#define __PHYSICS_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +void physics(); +namespace JPH +{ +class CharacterBase; +} +// Layer that objects can be in, determines which other objects it can collide with +// Typically you at least want to have 1 layer for moving bodies and 1 layer for static bodies, but you can have more +// layers if you want. E.g. you could have a layer for high detail collision (which is not used by the physics simulation +// but only if you do collision testing). +namespace Layers +{ +static constexpr JPH::ObjectLayer NON_MOVING = 0; +static constexpr JPH::ObjectLayer MOVING = 1; +static constexpr JPH::ObjectLayer NUM_LAYERS = 2; +}; + +// Each broadphase layer results in a separate bounding volume tree in the broad phase. You at least want to have +// a layer for non-moving and moving objects to avoid having to update a tree full of static objects every frame. +// You can have a 1-on-1 mapping between object layers and broadphase layers (like in this case) but if you have +// many object layers you'll be creating many broad phase trees, which is not efficient. If you want to fine tune +// your broadphase layers define JPH_TRACK_BROADPHASE_STATS and look at the stats reported on the TTY. +namespace BroadPhaseLayers +{ +static constexpr JPH::BroadPhaseLayer NON_MOVING(0); +static constexpr JPH::BroadPhaseLayer MOVING(1); +static constexpr uint NUM_LAYERS(2); +}; + +namespace JoltPhysics +{ +Ogre::Vector3 convert(const JPH::Vec3Arg &vec); +JPH::Vec3 convert(const Ogre::Vector3 &vec); +Ogre::Quaternion convert(const JPH::QuatArg &rot); +JPH::Quat convert(const Ogre::Quaternion &rot); +struct ShapeData; +struct CompoundShapeBuilder { + JPH::StaticCompoundShapeSettings shapeSettings; + void addShape(JPH::ShapeRefC shape, const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation); + JPH::ShapeRefC build(); +}; +} + +class JoltPhysicsWrapper : public Ogre::Singleton { +public: + JoltPhysicsWrapper(Ogre::SceneManager *scnMgr, + Ogre::SceneNode *cameraNode); + ~JoltPhysicsWrapper(); + void update(float dt); + void addBody(const JPH::BodyID &body, JPH::EActivation activation); + bool isAdded(const JPH::BodyID &body); + JPH::ShapeRefC createBoxShape(const Ogre::Vector3 &extents); + JPH::ShapeRefC createSphereShape(float radius); + JPH::ShapeRefC createCylinderShape(float halfHeight, float radius); + JPH::ShapeRefC createMeshShape(Ogre::MeshPtr mesh); + JPH::ShapeRefC createMeshShape(Ogre::String meshName); + JPH::ShapeRefC createConvexHullShape(Ogre::MeshPtr mesh); + JPH::ShapeRefC createConvexHullShape(Ogre::String meshName); + JPH::ShapeRefC createHeightfieldShape(const float *samples, + Ogre::Vector3 offset, + Ogre::Vector3 scale, + int sampleCount); + JPH::ShapeRefC createMutableCompoundShape( + const std::vector &shapes, + const std::vector &positions, + const std::vector &rotations); + JPH::ShapeRefC createStaticCompoundShape( + const std::vector &shapes, + const std::vector &positions, + const std::vector &rotations); + JPH::ShapeRefC + createOffsetCenterOfMassShape(const Ogre::Vector3 &offset, + JPH::ShapeRefC shape); + JPH::BodyID createBody(const JPH::BodyCreationSettings &settings); + JPH::BodyID createBody(const JPH::Shape *shape, float mass, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + JPH::EMotionType motion, JPH::ObjectLayer layer); + JPH::BodyID createBody(const JPH::Shape *shape, float mass, + Ogre::SceneNode *node, JPH::EMotionType motion, + JPH::ObjectLayer layer); + JPH::BodyID createSensor(const JPH::Shape *shape, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + JPH::EMotionType motion, + JPH::ObjectLayer layer); + JPH::BodyID createSensor(const JPH::Shape *shape, Ogre::SceneNode *node, + JPH::EMotionType motion, + JPH::ObjectLayer layer); + JPH::CharacterBase *createCharacter(Ogre::SceneNode *node, + float characterHeight, + float characterRadius); + void addShapeToCompound(JPH::Ref compoundShape, + JPH::ShapeRefC childShape, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation); + void removeBody(const JPH::BodyID &id); + void destroyBody(const JPH::BodyID &id); + void setDebugDraw(bool enable); + void broadphaseQuery(float dt, const Ogre::Vector3 &position, + std::set &inWater); + void applyBuoyancyImpulse(JPH::BodyID id, + const Ogre::Vector3 &surfacePosition, + const Ogre::Vector3 &surfaceNormal, + float buoyancy, float linearDrag, + float angularDrag, + const Ogre::Vector3 &fluidVelocity, + const Ogre::Vector3 &gravity, float dt); + void applyBuoyancyImpulse(JPH::BodyID id, + const Ogre::Vector3 &surfacePosition, + const Ogre::Vector3 &surfaceNormal, + float buoyancy, float linearDrag, + float angularDrag, + const Ogre::Vector3 &fluidVelocity, float dt); + bool isActive(JPH::BodyID id); + void activate(JPH::BodyID id); + Ogre::Vector3 getPosition(JPH::BodyID id); + void setPosition(JPH::BodyID id, const Ogre::Vector3 &position, + bool activate = true); + void setRotation(JPH::BodyID id, const Ogre::Quaternion &rotation, + bool activate = true); + void getPositionAndRotation(JPH::BodyID id, Ogre::Vector3 &position, + Ogre::Quaternion &rotation); + void setPositionAndRotation(JPH::BodyID id, + const Ogre::Vector3 &position, + const Ogre::Quaternion &rotation, + bool activate = true); + Ogre::Vector3 getLinearVelocity(JPH::BodyID id); + void addAngularImpulse(const JPH::BodyID &id, + const Ogre::Vector3 &impulse); +}; +#endif \ No newline at end of file diff --git a/src/sceneloader/CMakeLists.txt b/src/sceneloader/CMakeLists.txt index 8ac4aca..48f1b5d 100644 --- a/src/sceneloader/CMakeLists.txt +++ b/src/sceneloader/CMakeLists.txt @@ -1,6 +1,7 @@ project(sceneloader) +set(CMAKE_CXX_STANDARD 17) add_library(sceneloader STATIC loader.cpp) target_include_directories(sceneloader PUBLIC .) -target_link_libraries(sceneloader PUBLIC OgreMain PRIVATE pugixml GameData) +target_link_libraries(sceneloader PUBLIC OgreMain PRIVATE pugixml GameData physics) target_link_libraries(sceneloader PUBLIC OgreTerrain) diff --git a/src/sceneloader/loader.cpp b/src/sceneloader/loader.cpp index 9ab715e..ff9f45b 100644 --- a/src/sceneloader/loader.cpp +++ b/src/sceneloader/loader.cpp @@ -4,7 +4,9 @@ #include #include #include "GameData.h" +#include "Components.h" #include "EventTriggerModule.h" +#include "physics.h" #include "loader.h" using namespace Ogre; @@ -440,6 +442,511 @@ void SceneLoader::processCamera(pugi::xml_node &XMLNode, SceneNode *pParent) ->getUserObjectBindings()); } +namespace SceneLoaderPhysics +{ +typedef std::vector Vector3Array; +#if 0 +struct EntityCollisionListener { + const MovableObject *entity; + Ogre::Bullet::CollisionListener *listener; +}; + +/// wrapper with automatic memory management +class CollisionObject { +protected: + btCollisionObject *mBtBody; + btCollisionWorld *mBtWorld; + +public: + CollisionObject(btCollisionObject *btBody, btCollisionWorld *btWorld) + : mBtBody(btBody) + , mBtWorld(btWorld) + { + } + virtual ~CollisionObject() + { + mBtWorld->removeCollisionObject(mBtBody); + delete mBtBody->getCollisionShape(); + delete mBtBody; + } +}; +class RigidBody : public CollisionObject { +public: + using CollisionObject::CollisionObject; + + ~RigidBody() + { + delete (EntityCollisionListener *)(mBtBody)->getUserPointer(); + delete ((btRigidBody *)mBtBody)->getMotionState(); + } +}; + +class VertexIndexToShape { +public: + VertexIndexToShape(const Affine3 &transform = Affine3::IDENTITY); + VertexIndexToShape(Ogre::Renderable *rend, + const Affine3 &transform = Affine3::IDENTITY); + VertexIndexToShape(const Ogre::Entity *entity, + const Affine3 &transform = Affine3::IDENTITY); + VertexIndexToShape(const Ogre::MeshPtr &mesh, + const Affine3 &transform = Affine3::IDENTITY) + : VertexIndexToShape(transform) + { + addMesh(mesh); + } + ~VertexIndexToShape(); + + Real getRadius(); + Vector3 getSize(); + + btBvhTriangleMeshShape *createTrimesh(); + btConvexHullShape *createConvex(); + + void addEntity(const Entity *entity, + const Affine3 &transform = Affine3::IDENTITY); + void addMesh(const MeshPtr &mesh, + const Affine3 &transform = Affine3::IDENTITY); + + const Vector3 *getVertices() + { + return mVertexBuffer; + } + unsigned int getVertexCount() + { + return mVertexCount; + }; + +private: + void addStaticVertexData(const VertexData *vertex_data); + + void addAnimatedVertexData(const VertexData *vertex_data, + const VertexData *blended_data, + const Mesh::IndexMap *indexMap); + + void addIndexData(IndexData *data, const unsigned int offset = 0); + + Vector3 *mVertexBuffer; + unsigned int *mIndexBuffer; + unsigned int mVertexCount; + unsigned int mIndexCount; + + Vector3 mBounds; + Real mBoundRadius; + + typedef std::map *> BoneIndex; + BoneIndex *mBoneIndex; + + Affine3 mTransform; + + Vector3 mScale; +}; +void VertexIndexToShape::addStaticVertexData(const VertexData *vertex_data) +{ + if (!vertex_data) + return; + + const VertexData *data = vertex_data; + + const unsigned int prev_size = mVertexCount; + mVertexCount += (unsigned int)data->vertexCount; + + Vector3 *tmp_vert = new Vector3[mVertexCount]; + if (mVertexBuffer) { + memcpy(tmp_vert, mVertexBuffer, sizeof(Vector3) * prev_size); + delete[] mVertexBuffer; + } + mVertexBuffer = tmp_vert; + + // Get the positional buffer element + { + const VertexElement *posElem = + data->vertexDeclaration->findElementBySemantic( + VES_POSITION); + HardwareVertexBufferSharedPtr vbuf = + data->vertexBufferBinding->getBuffer( + posElem->getSource()); + const unsigned int vSize = (unsigned int)vbuf->getVertexSize(); + + unsigned char *vertex = static_cast( + vbuf->lock(HardwareBuffer::HBL_READ_ONLY)); + float *pReal; + Vector3 *curVertices = &mVertexBuffer[prev_size]; + const unsigned int vertexCount = + (unsigned int)data->vertexCount; + for (unsigned int j = 0; j < vertexCount; ++j) { + posElem->baseVertexPointerToElement(vertex, &pReal); + vertex += vSize; + + curVertices->x = (*pReal++); + curVertices->y = (*pReal++); + curVertices->z = (*pReal++); + + *curVertices = mTransform * (*curVertices); + + curVertices++; + } + vbuf->unlock(); + } +} +//------------------------------------------------------------------------------------------------ +void VertexIndexToShape::addAnimatedVertexData(const VertexData *vertex_data, + const VertexData *blend_data, + const Mesh::IndexMap *indexMap) +{ + // Get the bone index element + assert(vertex_data); + + const VertexData *data = blend_data; + const unsigned int prev_size = mVertexCount; + mVertexCount += (unsigned int)data->vertexCount; + Vector3 *tmp_vert = new Vector3[mVertexCount]; + if (mVertexBuffer) { + memcpy(tmp_vert, mVertexBuffer, sizeof(Vector3) * prev_size); + delete[] mVertexBuffer; + } + mVertexBuffer = tmp_vert; + + // Get the positional buffer element + { + const VertexElement *posElem = + data->vertexDeclaration->findElementBySemantic( + VES_POSITION); + assert(posElem); + HardwareVertexBufferSharedPtr vbuf = + data->vertexBufferBinding->getBuffer( + posElem->getSource()); + const unsigned int vSize = (unsigned int)vbuf->getVertexSize(); + + unsigned char *vertex = static_cast( + vbuf->lock(HardwareBuffer::HBL_READ_ONLY)); + float *pReal; + Vector3 *curVertices = &mVertexBuffer[prev_size]; + const unsigned int vertexCount = + (unsigned int)data->vertexCount; + for (unsigned int j = 0; j < vertexCount; ++j) { + posElem->baseVertexPointerToElement(vertex, &pReal); + vertex += vSize; + + curVertices->x = (*pReal++); + curVertices->y = (*pReal++); + curVertices->z = (*pReal++); + + *curVertices = mTransform * (*curVertices); + + curVertices++; + } + vbuf->unlock(); + } + { + const VertexElement *bneElem = + vertex_data->vertexDeclaration->findElementBySemantic( + VES_BLEND_INDICES); + assert(bneElem); + + HardwareVertexBufferSharedPtr vbuf = + vertex_data->vertexBufferBinding->getBuffer( + bneElem->getSource()); + const unsigned int vSize = (unsigned int)vbuf->getVertexSize(); + unsigned char *vertex = static_cast( + vbuf->lock(HardwareBuffer::HBL_READ_ONLY)); + + unsigned char *pBone; + + if (!mBoneIndex) + mBoneIndex = new BoneIndex(); + BoneIndex::iterator i; + + Vector3 *curVertices = &mVertexBuffer[prev_size]; + + const unsigned int vertexCount = + (unsigned int)vertex_data->vertexCount; + for (unsigned int j = 0; j < vertexCount; ++j) { + bneElem->baseVertexPointerToElement(vertex, &pBone); + vertex += vSize; + + const unsigned char currBone = + (indexMap) ? (*indexMap)[*pBone] : *pBone; + i = mBoneIndex->find(currBone); + Vector3Array *l = 0; + if (i == mBoneIndex->end()) { + l = new Vector3Array; + mBoneIndex->emplace(currBone, l); + } else { + l = i->second; + } + + l->push_back(*curVertices); + + curVertices++; + } + vbuf->unlock(); + } +} +//------------------------------------------------------------------------------------------------ +void VertexIndexToShape::addIndexData(IndexData *data, + const unsigned int offset) +{ + const unsigned int prev_size = mIndexCount; + mIndexCount += (unsigned int)data->indexCount; + + unsigned int *tmp_ind = new unsigned int[mIndexCount]; + if (mIndexBuffer) { + memcpy(tmp_ind, mIndexBuffer, sizeof(unsigned int) * prev_size); + delete[] mIndexBuffer; + } + mIndexBuffer = tmp_ind; + + const unsigned int numTris = (unsigned int)data->indexCount / 3; + HardwareIndexBufferSharedPtr ibuf = data->indexBuffer; + const bool use32bitindexes = + (ibuf->getType() == HardwareIndexBuffer::IT_32BIT); + unsigned int index_offset = prev_size; + + if (use32bitindexes) { + const unsigned int *pInt = static_cast( + ibuf->lock(HardwareBuffer::HBL_READ_ONLY)); + for (unsigned int k = 0; k < numTris; ++k) { + mIndexBuffer[index_offset++] = offset + *pInt++; + mIndexBuffer[index_offset++] = offset + *pInt++; + mIndexBuffer[index_offset++] = offset + *pInt++; + } + ibuf->unlock(); + } else { + const unsigned short *pShort = static_cast( + ibuf->lock(HardwareBuffer::HBL_READ_ONLY)); + for (unsigned int k = 0; k < numTris; ++k) { + mIndexBuffer[index_offset++] = + offset + static_cast(*pShort++); + mIndexBuffer[index_offset++] = + offset + static_cast(*pShort++); + mIndexBuffer[index_offset++] = + offset + static_cast(*pShort++); + } + ibuf->unlock(); + } +} +//------------------------------------------------------------------------------------------------ +Real VertexIndexToShape::getRadius() +{ + if (mBoundRadius == (-1)) { + getSize(); + mBoundRadius = + (std::max(mBounds.x, std::max(mBounds.y, mBounds.z)) * + 0.5); + } + return mBoundRadius; +} +//------------------------------------------------------------------------------------------------ +Vector3 VertexIndexToShape::getSize() +{ + const unsigned int vCount = getVertexCount(); + if (mBounds == Vector3(-1, -1, -1) && vCount > 0) { + const Vector3 *const v = getVertices(); + + Vector3 vmin(v[0]); + Vector3 vmax(v[0]); + + for (unsigned int j = 1; j < vCount; j++) { + vmin.x = std::min(vmin.x, v[j].x); + vmin.y = std::min(vmin.y, v[j].y); + vmin.z = std::min(vmin.z, v[j].z); + + vmax.x = std::max(vmax.x, v[j].x); + vmax.y = std::max(vmax.y, v[j].y); + vmax.z = std::max(vmax.z, v[j].z); + } + + mBounds.x = vmax.x - vmin.x; + mBounds.y = vmax.y - vmin.y; + mBounds.z = vmax.z - vmin.z; + } + + return mBounds; +} +//------------------------------------------------------------------------------------------------ +btConvexHullShape *VertexIndexToShape::createConvex() +{ + assert(mVertexCount && (mIndexCount >= 6) && + ("Mesh must have some vertices and at least 6 indices (2 triangles)")); + + btConvexHullShape *shape = new btConvexHullShape( + (btScalar *)&mVertexBuffer[0].x, mVertexCount, sizeof(Vector3)); + + shape->setLocalScaling(Ogre::Bullet::convert(mScale)); + + return shape; +} +//------------------------------------------------------------------------------------------------ +btBvhTriangleMeshShape *VertexIndexToShape::createTrimesh() +{ + assert(mVertexCount && (mIndexCount >= 6) && + ("Mesh must have some vertices and at least 6 indices (2 triangles)")); + + unsigned int numFaces = mIndexCount / 3; + + btTriangleMesh *trimesh = new btTriangleMesh(); + unsigned int *indices = mIndexBuffer; + Vector3 *vertices = mVertexBuffer; + + btVector3 vertexPos[3]; + for (unsigned int n = 0; n < numFaces; ++n) { + { + const Vector3 &vec = vertices[*indices]; + vertexPos[0][0] = vec.x; + vertexPos[0][1] = vec.y; + vertexPos[0][2] = vec.z; + } + { + const Vector3 &vec = vertices[*(indices + 1)]; + vertexPos[1][0] = vec.x; + vertexPos[1][1] = vec.y; + vertexPos[1][2] = vec.z; + } + { + const Vector3 &vec = vertices[*(indices + 2)]; + vertexPos[2][0] = vec.x; + vertexPos[2][1] = vec.y; + vertexPos[2][2] = vec.z; + } + + indices += 3; + + trimesh->addTriangle(vertexPos[0], vertexPos[1], vertexPos[2]); + } + + const bool useQuantizedAABB = true; + btBvhTriangleMeshShape *shape = + new btBvhTriangleMeshShape(trimesh, useQuantizedAABB); + + shape->setLocalScaling(Ogre::Bullet::convert(mScale)); + + return shape; +} +//------------------------------------------------------------------------------------------------ +VertexIndexToShape::~VertexIndexToShape() +{ + delete[] mVertexBuffer; + delete[] mIndexBuffer; + + if (mBoneIndex) { + for (auto &i : *mBoneIndex) { + delete i.second; + } + delete mBoneIndex; + } +} +//------------------------------------------------------------------------------------------------ +VertexIndexToShape::VertexIndexToShape(const Affine3 &transform) + : mVertexBuffer(0) + , mIndexBuffer(0) + , mVertexCount(0) + , mIndexCount(0) + , mBounds(Vector3(-1, -1, -1)) + , mBoundRadius(-1) + , mBoneIndex(0) + , mTransform(transform) + , mScale(1) +{ +} +//------------------------------------------------------------------------------------------------ +VertexIndexToShape::VertexIndexToShape(const Entity *entity, + const Affine3 &transform) + : VertexIndexToShape(transform) +{ + addEntity(entity, transform); +} +//------------------------------------------------------------------------------------------------ +VertexIndexToShape::VertexIndexToShape(Renderable *rend, + const Affine3 &transform) + : VertexIndexToShape(transform) +{ + RenderOperation op; + rend->getRenderOperation(op); + addStaticVertexData(op.vertexData); + if (op.useIndexes) + addIndexData(op.indexData); +} +//------------------------------------------------------------------------------------------------ +void VertexIndexToShape::addEntity(const Entity *entity, + const Affine3 &transform) +{ + // Each entity added need to reset size and radius + // next time getRadius and getSize are asked, they're computed. + mBounds = Vector3(-1, -1, -1); + mBoundRadius = -1; + + auto node = entity->getParentSceneNode(); + mTransform = transform; + mScale = node ? node->getScale() : Vector3(1, 1, 1); + + bool hasSkeleton = entity->hasSkeleton(); + + if (entity->getMesh()->sharedVertexData) { + if (hasSkeleton) + addAnimatedVertexData( + entity->getMesh()->sharedVertexData, + entity->_getSkelAnimVertexData(), + &entity->getMesh() + ->sharedBlendIndexToBoneIndexMap); + else + addStaticVertexData( + entity->getMesh()->sharedVertexData); + } + + for (unsigned int i = 0; i < entity->getNumSubEntities(); ++i) { + SubMesh *sub_mesh = entity->getSubEntity(i)->getSubMesh(); + + if (!sub_mesh->useSharedVertices) { + addIndexData(sub_mesh->indexData, mVertexCount); + + if (hasSkeleton) + addAnimatedVertexData( + sub_mesh->vertexData, + entity->getSubEntity(i) + ->_getSkelAnimVertexData(), + &sub_mesh->blendIndexToBoneIndexMap); + else + addStaticVertexData(sub_mesh->vertexData); + } else { + addIndexData(sub_mesh->indexData); + } + } +} +//------------------------------------------------------------------------------------------------ +void VertexIndexToShape::addMesh(const MeshPtr &mesh, const Affine3 &transform) +{ + // Each entity added need to reset size and radius + // next time getRadius and getSize are asked, they're computed. + mBounds = Vector3(-1, -1, -1); + mBoundRadius = -1; + + mTransform = transform; + + if (mesh->hasSkeleton()) + LogManager::getSingleton().logWarning( + "Mesh " + mesh->getName() + + " has a skeleton but added non animated"); + + if (mesh->sharedVertexData) { + VertexIndexToShape::addStaticVertexData(mesh->sharedVertexData); + } + + for (unsigned int i = 0; i < mesh->getNumSubMeshes(); ++i) { + SubMesh *sub_mesh = mesh->getSubMesh(i); + + if (!sub_mesh->useSharedVertices) { + VertexIndexToShape::addIndexData(sub_mesh->indexData, + mVertexCount); + VertexIndexToShape::addStaticVertexData( + sub_mesh->vertexData); + } else { + VertexIndexToShape::addIndexData(sub_mesh->indexData); + } + } +} +#endif +} + void SceneLoader::processNode(pugi::xml_node &XMLNode, SceneNode *pParent) { // Construct the node's name @@ -511,6 +1018,310 @@ void SceneLoader::processNode(pugi::xml_node &XMLNode, SceneNode *pParent) if (auto pElement = XMLNode.child("trackTarget")) processTrackTarget(pElement, pNode); + // process physics + { + if (auto e = XMLNode.child("userData")) { + String collisionType; + String collisionFile; + Ogre::String parentCollisionType; + JPH::ShapeRefC shape; + JPH::ShapeRefC parentShape; + for (auto pElement : e.children("property")) { + String name = getAttrib(pElement, "name"); + String type = getAttrib(pElement, "type"); + String data = getAttrib(pElement, "data"); + Ogre::Any value; + if (type == "bool") + value = StringConverter::parseBool( + data); + else if (type == "float") + value = StringConverter::parseReal( + data); + else if (type == "int") + value = StringConverter::parseInt(data); + else + value = data; + if (name == "collisionType") { + collisionType = + Ogre::any_cast( + value); + pNode->getUserObjectBindings() + .setUserAny("collisionType", + collisionType); + } else if (name == "collisionFile") + collisionFile = + Ogre::any_cast( + value); + } + if (collisionFile.length() > 0) { + if (collisionType == "convexHull") { + shape = JoltPhysicsWrapper::getSingleton() + .createConvexHullShape( + collisionFile); + pNode->getUserObjectBindings() + .setUserAny("_btCollisionShape", + shape); + OgreAssert(shape.GetPtr(), + "bad shape "); + } else if (collisionType == "mesh") { + shape = JoltPhysicsWrapper::getSingleton() + .createMeshShape( + collisionFile); + pNode->getUserObjectBindings() + .setUserAny("_btCollisionShape", + shape); + OgreAssert(shape.GetPtr(), + "bad shape "); + } + } + if (pParent) + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + name + + " parent name: " + pParent->getName()); + else + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + name); + if (collisionType.length() > 0) { + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + collisionType); + } + if (collisionType.length() > 0) { + if (collisionType != "compound") { + OgreAssert(shape.GetPtr(), + "bad shape "); + } + if (pParent) { + Ogre::Any parentCollisionTypeA = + pParent->getUserObjectBindings() + .getUserAny( + "collisionType"); + if (parentCollisionTypeA.has_value()) + parentCollisionType = Ogre::any_cast< + Ogre::String>( + parentCollisionTypeA); + Ogre::Any parentShapeA = + pParent->getUserObjectBindings() + .getUserAny( + "_btCollisionShape"); + if (parentShapeA.has_value()) { + parentShape = Ogre::any_cast< + JPH::ShapeRefC>( + parentShapeA); + } + } + if (pParent) + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + name + + " parent name: " + + pParent->getName()); + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + name + + " parentCollisionType: " + + parentCollisionType); + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + name + + " collisionType: " + collisionType); + if (collisionType == "compound") { + shape = JoltPhysicsWrapper::getSingleton() + .createMutableCompoundShape( + std::vector< + JPH::ShapeRefC>(), + std::vector< + Ogre::Vector3>(), + std::vector< + Ogre::Quaternion>()); + pNode->getUserObjectBindings() + .setUserAny("_btCollisionShape", + shape); + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + name + + " created compound shape"); + + } else if (pParent) { + if (parentCollisionType == "compound") { + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + + name + + " before addChildShape: " + + pParent->getName()); + OgreAssert(parentShape.GetPtr(), + "Bad parent shape"); + OgreAssert( + parentShape->GetType() == + JPH::EShapeType:: + Compound, + "Bad parent shape"); + OgreAssert(shape.GetPtr(), + "Bad shape"); + JPH::Ref parentShapeMutable( + const_cast( + parentShape + .GetPtr())); + JoltPhysicsWrapper::getSingleton() + .addShapeToCompound( + parentShapeMutable, + shape, + pNode->getPosition(), + pNode->getOrientation()); + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + + name + + " addChildShape: " + + pParent->getName()); + } + } + } + } + } +#if 0 + { + if (auto e = XMLNode.child("userData")) { + String collisionType; + String collisionFile; + Ogre::String parentCollisionType; + std::shared_ptr shape; + std::shared_ptr parentShape; + for (auto pElement : e.children("property")) { + String name = getAttrib(pElement, "name"); + String type = getAttrib(pElement, "type"); + String data = getAttrib(pElement, "data"); + Ogre::Any value; + if (type == "bool") + value = StringConverter::parseBool( + data); + else if (type == "float") + value = StringConverter::parseReal( + data); + else if (type == "int") + value = StringConverter::parseInt(data); + else + value = data; + if (name == "collisionType") { + collisionType = + Ogre::any_cast( + value); + pNode->getUserObjectBindings() + .setUserAny("collisionType", + collisionType); + } else if (name == "collisionFile") + collisionFile = + Ogre::any_cast( + value); + } + if (collisionFile.length() > 0) { + Ogre::MeshPtr mesh = + MeshManager::getSingletonPtr()->load( + collisionFile, m_sGroupName); + SceneLoaderPhysics::VertexIndexToShape toshape( + mesh); + if (collisionType == "convexHull") { + shape.reset(toshape.createConvex()); + pNode->getUserObjectBindings() + .setUserAny("_btCollisionShape", + shape); + OgreAssert(shape.get(), "bad shape "); + } else if (collisionType == "mesh") { + shape.reset(toshape.createTrimesh()); + pNode->getUserObjectBindings() + .setUserAny("_btCollisionShape", + shape); + OgreAssert(shape.get(), "bad shape "); + } + } + if (pParent) + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + name + + " parent name: " + pParent->getName()); + else + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + name); + if (collisionType.length() > 0) { + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + collisionType); + } + if (collisionType.length() > 0) { + if (collisionType != "compound") { + OgreAssert(shape.get(), "bad shape "); + } + if (pParent) { + Ogre::Any parentCollisionTypeA = + pParent->getUserObjectBindings() + .getUserAny( + "collisionType"); + if (parentCollisionTypeA.has_value()) + parentCollisionType = Ogre::any_cast< + Ogre::String>( + parentCollisionTypeA); + Ogre::Any parentShapeA = + pParent->getUserObjectBindings() + .getUserAny( + "_btCollisionShape"); + if (parentShapeA.has_value()) { + parentShape = Ogre::any_cast< + std::shared_ptr< + btCollisionShape> >( + parentShapeA); + } + } + if (pParent) + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + name + + " parent name: " + + pParent->getName()); + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + name + + " parentCollisionType: " + + parentCollisionType); + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + name + + " collisionType: " + collisionType); + if (collisionType == "compound") { + shape.reset(new btCompoundShape); + pNode->getUserObjectBindings() + .setUserAny("_btCollisionShape", + shape); + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + name + + " created compound shape"); + + } else if (pParent) { + if (parentCollisionType == "compound") { + btTransform transform; + transform.setIdentity(); + transform.setOrigin(Ogre::Bullet::convert( + pNode->getPosition())); + transform.setRotation(Ogre::Bullet::convert( + pNode->getOrientation())); + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + + name + + " before addChildShape: " + + pParent->getName()); + OgreAssert(parentShape.get(), + "Bad parent shape"); + OgreAssert( + parentShape + ->isCompound(), + "Bad parent shape"); + OgreAssert(shape.get(), + "Bad shape"); + static_cast( + parentShape.get()) + ->addChildShape( + transform, + shape.get()); + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + + name + + " addChildShape: " + + pParent->getName()); + } + } + } + } + } +#endif + // Process node (*) for (auto pElement : XMLNode.children("node")) { processNode(pElement, pNode); @@ -553,6 +1364,18 @@ void SceneLoader::processNode(pugi::xml_node &XMLNode, SceneNode *pParent) // Process node animations (?) if (auto pElement = XMLNode.child("animations")) processNodeAnimations(pElement, pNode); + // -- physics + { + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + pNode->getName() + + " position: " + + Ogre::StringConverter::toString(pNode->getPosition())); + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + pNode->getName() + + " orientation: " + + Ogre::StringConverter::toString( + pNode->getOrientation())); + } Ogre::Any triggerAny = pNode->getUserObjectBindings().getUserAny("trigger"); if (triggerAny.has_value()) { @@ -981,6 +1804,198 @@ void SceneLoader::processLightSourceSize(pugi::xml_node &XMLNode, Light *pLight) pLight->setSourceSize(width, height); } +void SceneLoader::setupPhysicsBody(Ogre::SceneNode *node, + Ogre::MovableObject *entity, + flecs::entity base_e) +{ + Ogre::SceneNode *pNode = node; + Ogre::String name = node->getName(); + Ogre::String collisionType, collisionBodyType; + JPH::ShapeRefC shape; + float mass = 0.0f; + JPH::ObjectLayer layer = Layers::MOVING; + JPH::EMotionType motion = JPH::EMotionType::Static; + Ogre::Any collisionTypeA = + pNode->getUserObjectBindings().getUserAny("collisionType"); + Ogre::Any collisionBodyTypeA = + pNode->getUserObjectBindings().getUserAny("collisionBodyType"); + Ogre::Any collisionBodyMassA = + pNode->getUserObjectBindings().getUserAny("collisionBodyMass"); + Ogre::Any collisionShapeA = + pNode->getUserObjectBindings().getUserAny("_btCollisionShape"); + if (!collisionTypeA.has_value()) + return; + if (collisionBodyMassA.has_value()) + mass = Ogre::any_cast(collisionBodyMassA); + if (collisionTypeA.has_value() && collisionShapeA.has_value()) + collisionType = Ogre::any_cast(collisionTypeA); + if (collisionBodyTypeA.has_value()) { + collisionBodyType = + Ogre::any_cast(collisionBodyTypeA); + /* we can only create RigidBody where the entity is because how it works */ + if (collisionShapeA.has_value()) + shape = Ogre::any_cast(collisionShapeA); + if (collisionBodyType == "dynamic" || + collisionBodyType == "kinematic" || + collisionBodyType == "static") { + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + name + + " collision body type: " + collisionBodyType); + if (collisionBodyType == "static" || + collisionBodyType == "kinematic") { + mass = 0; + } + OgreAssert(shape.GetPtr(), "Bad shape"); + if (shape->GetType() == JPH::EShapeType::Compound) { + const JPH::CompoundShape *cshape = + static_cast( + shape.GetPtr()); + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + + pNode->getName() + " numChildShapes: " + + Ogre::StringConverter::toString( + cshape->GetNumSubShapes())); + } + if (collisionBodyType == "dynamic") { + motion = JPH::EMotionType::Dynamic; + layer = Layers::MOVING; + } else if (collisionBodyType == "kinematic") { + motion = JPH::EMotionType::Kinematic; + layer = Layers::MOVING; + mass = 0.0f; + } else if (collisionBodyType == "static") { + motion = JPH::EMotionType::Static; + layer = Layers::NON_MOVING; + mass = 0.0f; + } + JPH::ShapeRefC com_shape = + JoltPhysicsWrapper::getSingleton() + .createOffsetCenterOfMassShape( + Ogre::Vector3(0, -5.5f, 0.5f), + shape); + JPH::BodyID id = + JoltPhysicsWrapper::getSingleton().createBody( + com_shape.GetPtr(), mass, pNode, motion, + layer); + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + pNode->getName() + + " btRigidBody created: " + collisionBodyType); + auto objWrapper = std::make_shared(id); + pNode->getUserObjectBindings().setUserAny( + "BtCollisionObject", objWrapper); + pNode->getUserObjectBindings().setUserAny("bodyPointer", + id); + /* do not add body to physics yet */ + base_e.set(id); + } else if (collisionBodyType == "ghost") { + /* TODO: later */ + } + } +#if 0 + Ogre::SceneNode *pNode = node; + Ogre::String name = node->getName(); + Ogre::String collisionType, collisionBodyType; + std::shared_ptr shape; + float mass = 0.0f; + int group = 2; + int mask = 0x7fffffff; + btVector3 inertia(0, 0, 0); + Ogre::Any collisionTypeA = + pNode->getUserObjectBindings().getUserAny("collisionType"); + Ogre::Any collisionBodyTypeA = + pNode->getUserObjectBindings().getUserAny("collisionBodyType"); + Ogre::Any collisionBodyMassA = + pNode->getUserObjectBindings().getUserAny("collisionBodyMass"); + Ogre::Any collisionShapeA = + pNode->getUserObjectBindings().getUserAny("_btCollisionShape"); + if (!collisionTypeA.has_value()) + return; + if (collisionBodyMassA.has_value()) + mass = Ogre::any_cast(collisionBodyMassA); + if (collisionTypeA.has_value() && collisionShapeA.has_value()) + collisionType = Ogre::any_cast(collisionTypeA); + if (collisionBodyTypeA.has_value()) { + collisionBodyType = + Ogre::any_cast(collisionBodyTypeA); + /* we can only create RigidBody where the entity is because how it works */ + if (collisionShapeA.has_value()) + shape = Ogre::any_cast< + std::shared_ptr >( + collisionShapeA); + if (collisionBodyType == "dynamic" || + collisionBodyType == "kinematic" || + collisionBodyType == "static") { + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + name + + " collision body type: " + collisionBodyType); + if (collisionBodyType == "static" || + collisionBodyType == "kinematic") { + mass = 0; + inertia = btVector3(0, 0, 0); + } + if (mass != 0) { + shape->calculateLocalInertia(mass, inertia); + } + OgreAssert(shape.get(), "Bad shape"); + Ogre::Bullet::RigidBodyState *state = + new Ogre::Bullet::RigidBodyState(pNode); + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + pNode->getName() + + " isCompound: " + + Ogre::StringConverter::toString( + shape->isCompound())); + if (shape->isCompound()) + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + + pNode->getName() + " numChildShapes: " + + Ogre::StringConverter::toString( + static_cast( + shape.get()) + ->getNumChildShapes())); + btRigidBody *body = OGRE_NEW btRigidBody( + mass, state, shape.get(), inertia); + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + pNode->getName() + + " btRigidBody created: " + collisionBodyType); +#if 1 + if (collisionBodyType == "kinematic") + body->setCollisionFlags( + body->getCollisionFlags() | + btCollisionObject::CF_KINEMATIC_OBJECT); + else if (collisionBodyType == "static") + body->setCollisionFlags( + body->getCollisionFlags() | + btCollisionObject::CF_STATIC_OBJECT); +#endif + ECS::get() + .mWorld->getBtWorld() + ->addRigidBody(body, 3, 0x7fffffff); + body->setActivationState(DISABLE_DEACTIVATION); + LogManager::getSingleton().logMessage( + "[SceneLoader] Node: " + name + + " btRigidBody added to world: " + + collisionBodyType + " group: " + + Ogre::StringConverter::toString(group) + + " mash: " + + Ogre::StringConverter::toString(mask)); + body->setUserPointer(OGRE_NEW SceneLoaderPhysics:: + EntityCollisionListener{ + entity, nullptr }); + auto objWrapper = + std::make_shared( + body, ECS::get() + .mWorld->getBtWorld()); + pNode->getUserObjectBindings().setUserAny( + "BtCollisionObject", objWrapper); + pNode->getUserObjectBindings().setUserAny("bodyPointer", + body); + } else if (collisionBodyType == "ghost") { + /* TODO: later */ + } + } +#endif +} + void SceneLoader::processUserData(pugi::xml_node &XMLNode, UserObjectBindings &userData) { diff --git a/src/sceneloader/loader.h b/src/sceneloader/loader.h index 9d5e621..1f439ee 100644 --- a/src/sceneloader/loader.h +++ b/src/sceneloader/loader.h @@ -1,12 +1,13 @@ #ifndef LOADER_H_ #define LOADER_H_ +#include +#include #include #include #include #include #include #include -#include class SceneLoader { public: @@ -23,6 +24,9 @@ public: { return mBackgroundColour; } + static void setupPhysicsBody(Ogre::SceneNode *node, + Ogre::MovableObject *entity, + flecs::entity base_e); protected: void writeNode(pugi::xml_node &parentXML, const Ogre::SceneNode *node); @@ -68,7 +72,6 @@ protected: Ogre::Light *pLight); void processLightSourceSize(pugi::xml_node &XMLNode, Ogre::Light *pLight); - Ogre::SceneManager *mSceneMgr; Ogre::SceneNode *mAttachNode; Ogre::String m_sGroupName; diff --git a/src/terrain/terrain.cpp b/src/terrain/terrain.cpp index 5f22d4b..7b2dff1 100644 --- a/src/terrain/terrain.cpp +++ b/src/terrain/terrain.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include "terrain.h" diff --git a/src/terrain/terrain.h b/src/terrain/terrain.h index 32cb946..b2ec492 100644 --- a/src/terrain/terrain.h +++ b/src/terrain/terrain.h @@ -4,8 +4,6 @@ #include #include #include -#include -#include #include #include #include @@ -404,9 +402,11 @@ public: return body; } #endif - bool isLoadedAt(const Ogre::Vector3 &position) const { + bool isLoadedAt(const Ogre::Vector3 &position) const + { long x, y; - mTerrainGroup->convertWorldPositionToTerrainSlot(position, &x, &y); + mTerrainGroup->convertWorldPositionToTerrainSlot(position, &x, + &y); if (mTerrainGroup->getTerrain(x, y)) return mTerrainGroup->getTerrain(x, y)->isLoaded(); return false; diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 70ef308..ff8d490 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -7,4 +7,6 @@ add_executable(check_uv check_uv.cpp) target_link_libraries(check_uv ${ASSIMP_LIBRARIES}) add_executable(ogre_check_uv ogre_check_uv.cpp) target_link_libraries(ogre_check_uv OgreBites OgreMain) +add_executable(check_scene_loader check_scene_loader.cpp) +target_link_libraries(check_scene_loader OgreBites OgreMain) diff --git a/src/tests/check_scene_loader.cpp b/src/tests/check_scene_loader.cpp new file mode 100644 index 0000000..c027cdb --- /dev/null +++ b/src/tests/check_scene_loader.cpp @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include +#include + +int main() +{ + Ogre::LogManager logMgr; + logMgr.createLog("messages.log", true, true, true); + Ogre::DefaultHardwareBufferManager + bufferManager; // needed because we don't have a rendersystem + Ogre::Root *ogre = new Ogre::Root("", "", ""); + Ogre::ConfigFile pluginsCfg; + Ogre::FileSystemLayer fsLayer("Ogre3D"); + pluginsCfg.load(fsLayer.getConfigFilePath("plugins.cfg")); + + auto pluginDir = Ogre::FileSystemLayer::resolveBundlePath( + pluginsCfg.getSetting("PluginFolder") + "/"); + ogre->loadPlugin(pluginDir + "/Codec_Assimp"); + ogre->loadPlugin(pluginDir + "/Plugin_DotScene"); + Ogre::MaterialManager::getSingleton().initialise(); + Ogre::RTShader::ShaderGenerator::initialize(); + Ogre::DefaultTextureManager texMgr; + + auto &shadergen = Ogre::RTShader::ShaderGenerator::getSingleton(); + shadergen.setTargetLanguage( + "glsl"); // must be valid, but otherwise arbitrary + shadergen.getRenderState(Ogre::MSN_SHADERGEN) + ->setLightCountAutoUpdate(false); + shadergen.validateScheme(Ogre::MSN_SHADERGEN); + Ogre::SceneManager *sceneManager = + ogre->createSceneManager("DefaultSceneManager"); + Ogre::ResourceGroupManager::getSingleton().addResourceLocation( + "resources/vehicles", "FileSystem", "Generic", true, true); + Ogre::ResourceGroupManager::getSingleton().addResourceLocation( + "../resources/vehicles", "FileSystem", "Generic", true, true); + Ogre::ResourceGroupManager::getSingleton().addResourceLocation( + "../../resources/vehicles", "FileSystem", "Generic", true, + true); + std::map options; + options["prefix"] = Ogre::String("0/"); + Ogre::SceneNode *p1 = + sceneManager->getRootSceneNode()->createChildSceneNode(); + Ogre::Any optionsAny1 = options; + p1->getUserObjectBindings().setUserAny("_DotSceneLoaderOptions", + optionsAny1); + p1->loadChildren("boat.scene"); + options["prefix"] = Ogre::String("1/"); + Ogre::SceneNode *p2 = + sceneManager->getRootSceneNode()->createChildSceneNode(); + Ogre::Any optionsAny2 = options; + p2->getUserObjectBindings().setUserAny("_DotSceneLoaderOptions", + optionsAny2); + p2->loadChildren("boat.scene"); + // Ogre::DataStreamPtr sceneData = + // Ogre::ResourceGroupManager::getSingleton().openResource( + // "boat.scene", "General"); + // auto codec = Ogre::Codec::getCodec("scene"); + // codec->decode(sceneData, sceneManager->getRootSceneNode()); + return 0; +} diff --git a/src/world/CMakeLists.txt b/src/world/CMakeLists.txt index 4709072..f16a1da 100644 --- a/src/world/CMakeLists.txt +++ b/src/world/CMakeLists.txt @@ -1,18 +1,20 @@ project(world) +find_package(Bullet) find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain Overlay CONFIG) -add_library(action action.cpp) -target_link_libraries(action PUBLIC GameData OgreBullet) +add_library(action STATIC action.cpp) +target_link_libraries(action PRIVATE GameData) target_include_directories(action PUBLIC .) -add_library(world-build world-build.cpp) -target_link_libraries(world-build PUBLIC GameData OgreBullet) +add_library(world-build STATIC world-build.cpp) +target_link_libraries(world-build PRIVATE GameData) target_include_directories(world-build PUBLIC .) + add_executable(test test.cpp) -target_link_libraries(test PRIVATE OgreMain OgreBullet action world-build lua) +target_link_libraries(test PRIVATE action world-build lua GameData OgreMain) add_executable(test2 test2.cpp) -target_link_libraries(test2 PRIVATE OgreMain OgreBullet action world-build lua) +target_link_libraries(test2 PRIVATE action world-build lua GameData OgreMain) add_executable(mark_harbors mark_harbors.cpp) -target_link_libraries(mark_harbors PRIVATE OgreMain OgreBullet lua) +target_link_libraries(mark_harbors PRIVATE lua OgreMain OgreRTShaderSystem) add_custom_target(world ALL DEPENDS test test2) diff --git a/src/world/test.cpp b/src/world/test.cpp index 5067d24..03a0822 100644 --- a/src/world/test.cpp +++ b/src/world/test.cpp @@ -150,7 +150,7 @@ int main() ecs.component(); ecs.component(); ecs.component(); - ecs.set({ nullptr, nullptr, 0.0f, 5.0f, 0, 0, false }); + ecs.set({ nullptr, 0.0f, 5.0f, 0, 0, false }); flecs::entity e1 = ecs.entity("e1"); e1.set({ e1, 100, 100, 100, 0 }); diff --git a/src/world/test2.cpp b/src/world/test2.cpp index b3f4bc4..1296f29 100644 --- a/src/world/test2.cpp +++ b/src/world/test2.cpp @@ -443,7 +443,7 @@ int main() ecs.import (); ecs.import (); ecs.import (); - ecs.set({ nullptr, nullptr, 0.0f, 5.0f, 0, 0, false }); + ecs.set({ nullptr, 0.0f, 5.0f, 0, 0, false }); CopulationGoal goal_copulation; SustainabilityGoal goal_sustainability; ECS::Planner &planner = ecs.ensure(); diff --git a/terrain.cpp b/terrain.cpp index e0fd08e..f415e45 100644 --- a/terrain.cpp +++ b/terrain.cpp @@ -6,9 +6,6 @@ // #include #include "OgreApplicationContext.h" -#include "Bullet/OgreBullet.h" -#include "BulletCollision/CollisionDispatch/btGhostObject.h" -#include "LinearMath/btTransform.h" #include "OgrePageManager.h" #include "lua_control.h" @@ -21,11 +18,8 @@ using Real = Ogre::Real; using Math = Ogre::Math; class WorldData { - std::unique_ptr mDynWorld; - std::unique_ptr mDbgDraw; std::unique_ptr mRoot; std::unique_ptr mScnMgr; - std::unique_ptr mbtWorld; std::unique_ptr mPageManager; Ogre::PagedWorld *mPagedWorld; float fps_time; @@ -64,13 +58,8 @@ private: 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) + : mRoot(root) , mScnMgr(scnMgr) - , mbtWorld(mDynWorld->getBtWorld()) , mPageManager(nullptr) , mPagedWorld(nullptr) , mRenderWindow(renderWindow) @@ -105,94 +94,8 @@ public: void createTrimesh(Ogre::Entity *entity) { } - 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(); - ghost->setCollisionShape(shape); - ghost->setCollisionFlags( - ghost->getCollisionFlags() | - btCollisionObject::CF_NO_CONTACT_RESPONSE | - btCollisionObject::CF_CHARACTER_OBJECT); - getWorld()->attachCollisionObject(ghost, ent, group, mask); -#if 0 - getBtWorld() - ->getBroadphase()->getOverlappingPairCache() - ->setInternalGhostPairCallback(new btGhostPairCallback()); - 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) - { - btDynamicsWorld *world = mDynWorld->getBtWorld(); - Ogre::SceneNode *node = ent->getParentSceneNode(); - Ogre::Bullet::RigidBodyState *state = - new Ogre::Bullet::RigidBodyState(node); - btCollisionShape *cs; - btCollisionShape *shape; - btVector3 inertia(0, 0, 0); - 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(false); - btScalar height = 1.0f; - btScalar radius = 0.3f; - 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 }; - btTransform principal; - static_cast(cs) - ->calculatePrincipalAxisTransform( - masses, principal, inertia); - } break; - default: - assert(false); - break; - } - btRigidBody *body = new btRigidBody(mass, state, cs, inertia); - getWorld()->attachRigidBody(body, ent, nullptr, group, mask); -#if 0 - body->setUserPointer(new EntityCollisionListener{ent, nullptr}); -// btRigidBody *body = mDynWorld->addRigidBody(0, ent, Ogre::Bullet::CT_TRIMESH); -#endif - return body; - } - btRigidBody *addKinematicRigidBody(float mass, Ogre::Entity *ent, - Ogre::Bullet::ColliderType ct, - int group = 1, int mask = 0xFFFF) - { - return mDynWorld->addKinematicRigidBody(ent, ct, group, mask); - } - btDynamicsWorld *getBtWorld() - { - return mDynWorld->getBtWorld(); - } - Ogre::Bullet::DynamicsWorld *getWorld() - { - return mDynWorld.get(); - } void update(float delta) { - WorldData::get_singleton()->getBtWorld()->stepSimulation(delta, - 50); - mDbgDraw->update(); fps_time += delta; if (fps_time > 1.0f) { fps_time -= 1.0f; @@ -213,8 +116,6 @@ public: WorldData *WorldData::singleton = nullptr; class MainWorld : public Ogre::FrameListener { - btRigidBody *mFloorBody; - public: void setup() { @@ -232,13 +133,6 @@ public: 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; }; @@ -278,8 +172,6 @@ class CharacterController : public OgreBites::InputListener, Ogre::Vector3 rootMotion; Ogre::Quaternion rootRotation; // btRigidBody *mRigidBody; - btCompoundShape *mCollisionShape; - btPairCachingGhostObject *mGhostObject; public: CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam, @@ -328,35 +220,6 @@ 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()); - } }; CharacterController::CharacterController(Ogre::SceneNode *camNode, Ogre::Camera *cam, @@ -370,8 +233,6 @@ CharacterController::CharacterController(Ogre::SceneNode *camNode, , mAnimID(ANIM_NONE) , mRunning(false) , world(world) - , mCollisionShape(nullptr) - , mGhostObject(nullptr) { setupBody(); setupCamera(); @@ -388,70 +249,6 @@ void CharacterController::setupBody() 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); - - assert(mCollisionShape); -#if 0 - if (mRigidBody->getMass() == 0) { -#if 0 - mRigidBody->setCollisionFlags(mRigidBody->getCollisionFlags() - | btCollisionObject::CF_KINEMATIC_OBJECT - | btCollisionObject::CF_NO_CONTACT_RESPONSE - ); -#endif -#if 0 - mGhostObject->setWorldTransform(mRigidBody->getWorldTransform()); - WorldData::get_singleton()->getBtWorld() - ->getBroadphase()->getOverlappingPairCache() - ->setInternalGhostPairCallback(new btGhostPairCallback()); -#endif - } -#endif -#if 0 - mRigidBody->setActivationState(DISABLE_DEACTIVATION); -#endif -#if 0 - { - Ogre::Entity *e2 = mScnMgr->createEntity("normal-male.glb"); - Ogre::SceneNode *e2node = mScnMgr->getRootSceneNode()->createChildSceneNode(); - e2node->attachObject(e2); - mGhostObject = WorldData::get_singleton()->addGhostObject(e2, mCollisionShape); - mController = new btKinematicCharacterController(mGhostObject, mCollisionShape, 0.5f); - WorldData::get_singleton()->getBtWorld()->addAction(mController); - } -#endif assert(mSkeleton->hasBone("Root")); mRootBone = mSkeleton->getBone("Root"); assert(mRootBone); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt deleted file mode 100644 index 6304fce..0000000 --- a/tests/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index 4d7c0ae..0000000 --- a/tests/compound_shapes.cpp +++ /dev/null @@ -1,290 +0,0 @@ -#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"; - } -} diff --git a/water/water.cpp b/water/water.cpp index 0af0864..cb65150 100644 --- a/water/water.cpp +++ b/water/water.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "water.h" /* TODO: use blender glb model for water shape. @@ -32,8 +31,10 @@ Water::Water() , mRefractionClipPlaneBelow(Ogre::Vector3(0.0, 1.0, 0.0), 0.0f /* water height */ - 2.0) , mAbove(true) +#if 0 , mWaterBody(nullptr) , mDynWorld(nullptr) +#endif { } @@ -43,6 +44,7 @@ Water::~Water() mScnMgr->destroySceneNode(mWaterNode); if (mReflectionTexture) mReflectionTexture->removeAllListeners(); +#if 0 if (mWaterBody) { if (mWaterBody->getWorldArrayIndex() >= 0) if (mDynWorld) @@ -51,6 +53,7 @@ Water::~Water() delete mWaterBody; mWaterBody = nullptr; } +#endif } void Water::create_cameras() @@ -167,11 +170,13 @@ void Water::init() water_ent->setMaterial(mat); mWaterNode->attachObject(water_ent); mWaterMeshes.push_back(water_ent); +#if 0 mDynWorld->attachCollisionObject(mWaterBody, water_ent, 1, 0x7FFFFFFF); +#endif } -void Water::createWater(Ogre::RenderWindow *window, Ogre::Camera *camera, - Ogre::Bullet::DynamicsWorld *world) +void Water::createWater(Ogre::RenderWindow *window, Ogre::Camera *camera /*, + Ogre::Bullet::DynamicsWorld *world */) { int i; mCamera = camera; @@ -181,6 +186,7 @@ void Water::createWater(Ogre::RenderWindow *window, Ogre::Camera *camera, mWindow = window; mCameraPosition = mCameraNode->getPosition(); create_textures(); +#if 0 mDynWorld = world; mWaterBody = new btGhostObject; btBoxShape *boxShape = new btBoxShape(btVector3(1000, 1000, 1000)); @@ -192,6 +198,7 @@ void Water::createWater(Ogre::RenderWindow *window, Ogre::Camera *camera, mWaterBody->getCollisionFlags() | btCollisionObject::CF_NO_CONTACT_RESPONSE | btCollisionObject::CF_STATIC_OBJECT); +#endif } void Water::updateWater(float delta) @@ -228,11 +235,13 @@ void Water::updateWater(float delta) mViewports[i]->update(); for (i = 0; i < mWaterMeshes.size(); i++) mWaterMeshes[i]->setVisible(true); +#if 0 mOverlaps.clear(); for (i = 0; i < mWaterBody->getNumOverlappingObjects(); i++) { btCollisionObject *sb = mWaterBody->getOverlappingObject(i); mOverlaps.insert(sb); } +#endif } void Water::preRenderTargetUpdate(const Ogre::RenderTargetEvent &evt) diff --git a/water/water.h b/water/water.h index 72b27c6..a1a7014 100644 --- a/water/water.h +++ b/water/water.h @@ -24,17 +24,20 @@ class Water : public /* Ogre::FrameListener, */ Ogre::RenderTargetListener { bool mInRefTexUpdate; Ogre::Timer mtexture_dump; void create_cameras(); +#if 0 btGhostObject *mWaterBody; Ogre::Bullet::DynamicsWorld *mDynWorld; std::set mOverlaps; +#endif public: Water(); virtual ~Water(); void create_textures(); void dump_textures(); - void createWater(Ogre::RenderWindow *window, Ogre::Camera *camera, - Ogre::Bullet::DynamicsWorld *world); + void createWater( + Ogre::RenderWindow *window, + Ogre::Camera *camera /*, Ogre::Bullet::DynamicsWorld *world */); void init(); void updateWater(float delta); /* bool frameEnded(const Ogre::FrameEvent &evt) override; */ @@ -44,10 +47,12 @@ public: postRenderTargetUpdate(const Ogre::RenderTargetEvent &evt) override; void add_submerged_entity(Ogre::Entity *ent); void add_surface_entity(Ogre::Entity *ent); +#if 0 bool isInWater(const btCollisionObject *body) const { btCollisionObject *test = const_cast(body); return mOverlaps.find(test) != mOverlaps.end(); } +#endif }; #endif