converted to Jolt physics

This commit is contained in:
2025-11-23 02:00:31 +03:00
parent 3f0484e87c
commit cd82fb0eed
52 changed files with 4302 additions and 1458 deletions

View File

@@ -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<Ogre::Bullet::DynamicsWorld> mDynWorld;
std::unique_ptr<Ogre::Bullet::DebugDrawer> mDbgDraw;
#endif
std::unique_ptr<Ogre::Root> mRoot;
std::unique_ptr<Ogre::SceneManager> mScnMgr;
#if 0
std::unique_ptr<btDynamicsWorld> mbtWorld;
#endif
std::unique_ptr<Ogre::PageManager> 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<btCollisionObject *> &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,

View File

@@ -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)

View File

@@ -1,7 +1,6 @@
#include <iostream>
#include <Ogre.h>
#include <OgreBullet.h>
#include <OgreApplicationContext.h>
#include <OgreOverlaySystem.h>
#include <OgreOverlayManager.h>
@@ -245,8 +244,6 @@ public:
void initGui();
};
class App : public OgreBites::ApplicationContext {
std::unique_ptr<Ogre::Bullet::DynamicsWorld> mDynWorld;
std::unique_ptr<Ogre::Bullet::DebugDrawer> 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();

View File

@@ -289,8 +289,6 @@ public:
void frameRendered(const Ogre::FrameEvent &evt) override;
};
class App : public OgreBites::ApplicationContext {
std::unique_ptr<Ogre::Bullet::DynamicsWorld> mDynWorld;
std::unique_ptr<Ogre::Bullet::DebugDrawer> 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<ECS::GUI>())
return;
/* Update window grab */
@@ -444,8 +440,10 @@ public:
}
ECS::update(delta);
#if 0
if (ECS::get<ECS::EngineData>().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<ECS::RenderWindow>(
{ getRenderWindow(), getDisplayDPI() });

View File

@@ -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<Ogre::Bullet::DynamicsWorld> mDynWorld;
std::unique_ptr<Ogre::Bullet::DebugDrawer> mDbgDraw;
std::unique_ptr<Ogre::Root> mRoot;
std::unique_ptr<Ogre::SceneManager> mScnMgr;
std::unique_ptr<btDynamicsWorld> mbtWorld;
std::unique_ptr<Ogre::PageManager> 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<btCompoundShape *>(cs)->addChildShape(
transform, shape);
btScalar masses[1] = { mass };
btTransform principal;
static_cast<btCompoundShape *>(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<btCollisionObject *> &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<btCompoundShape *>(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<btCompoundShape *>(mCollisionShape)
->addChildShape(transform, shape);
btScalar masses[1] = { 0 };
btTransform principal;
static_cast<btCompoundShape *>(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<btCollisionObject *>());

View File

@@ -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 )

View File

@@ -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 )

View File

@@ -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

View File

@@ -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',

Binary file not shown.

View File

@@ -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)

View File

@@ -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)

View File

@@ -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})

View File

@@ -1,369 +0,0 @@
#include <iostream>
#include <Ogre.h>
#include <OgreBullet.h>
#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<btCompoundShape *>(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<btCompoundShape *>(mCollisionShape)
->addChildShape(transform, shape);
btScalar masses[1] = { 0 };
btTransform principal;
static_cast<btCompoundShape *>(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<Ogre::Real>(
0,
std::max<Ogre::Real>(
yawToGoal,
yawAtSpeed)); //yawToGoal = Math::Clamp<Real>(yawToGoal, yawAtSpeed, 0);
else if (yawToGoal > 0)
yawToGoal = std::max<Ogre::Real>(
0,
std::min<Ogre::Real>(
yawToGoal,
yawAtSpeed)); //yawToGoal = Math::Clamp<Real>(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<btCollisionObject *>());
}
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<Ogre::Real>(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<Ogre::Real>(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();
}

View File

@@ -1,128 +0,0 @@
#include <Ogre.h>
#include <OgreFrameListener.h>
#include <OgreBullet.h>
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;
}
};

View File

@@ -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<BoatModule>();
ecs.component<BoatBase>();
ecs.component<BoatBody>();
ecs.component<BoatType>();
ecs.component<SpawnBoat>();
ecs.observer<const EngineData, const BoatType>("CreateBoat")
.event(flecs::OnSet)
.without<BoatBase>()
.without<SpawnBoat>()
.each([&](flecs::entity e, const EngineData &eng,
const BoatType &type) { e.add<SpawnBoat>(); });
ecs.system<const EngineData, const BoatType>("CreateBoat")
.kind(flecs::OnUpdate)
.without<BoatBase>()
.with<WaterReady>()
.with<SpawnBoat>()
.each([&](flecs::entity e, const EngineData &eng,
const BoatType &type) {
if (type.resourceName.find(".scene") !=
std::string::npos) {
BoatBase &boat = e.ensure<BoatBase>();
int i;
std::vector<Ogre::SceneNode *> colliderTarget;
boat.mNode =
ECS::get<EngineData>()
.mScnMgr->getRootSceneNode()
->createChildSceneNode(
type.position,
type.orientation);
auto pnodes = boat.mNode->getChildren();
for (i = 0; i < pnodes.size(); i++) {
Ogre::SceneNode *pnode =
static_cast<Ogre::SceneNode *>(
pnodes[i]);
Ogre::Any any =
pnode->getUserObjectBindings()
.getUserAny("type");
if (!any.has_value())
continue;
Ogre::String obj_type =
Ogre::any_cast<Ogre::String>(
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<Ogre::Node *> v =
attachment->getChildren();
OgreAssert(v.size() == 1,
"Bad root nodes count in " +
type.resourceName);
Ogre::Any any =
static_cast<Ogre::SceneNode *>(
v[0])
->getUserObjectBindings()
.getUserAny("type");
OgreAssert(any.has_value(),
"no \"type\" costom prop");
Ogre::String obj_type =
Ogre::any_cast<Ogre::String>(
any);
std::cout << "type: " << obj_type
<< std::endl;
OgreAssert(obj_type == "boat",
"not a boat");
boat.mNode =
static_cast<Ogre::SceneNode *>(
v[0]);
boat.mNode->_setDerivedPosition(
type.position);
boat.mNode->_setDerivedOrientation(
type.orientation);
boat.mEnt = static_cast<Ogre::Entity *>(
boat.mNode->getAttachedObject(
std::to_string(
(int)e.raw_id()) +
"/boat"));
loader.setupPhysicsBody(boat.mNode,
boat.mEnt, e);
e.remove<SpawnBoat>();
e.modified<BoatBase>();
}
// BoatBody &body = e.ensure<BoatBody>();
// e.modified<BoatBase>();
}
// ECS::get_mut<ECS::EngineData>().enableDbgDraw = true;
// ECS::modified<ECS::EngineData>();
// e.modified<BoatBody>();
});
#if 0
ecs.system<const EngineData, BoatType, Body2Entity>("CreateBoat")
.kind(flecs::OnUpdate)
.without<BoatBase>()
@@ -33,6 +134,7 @@ BoatModule::BoatModule(flecs::world &ecs)
type.orientation);
boat.mNode->attachObject(boat.mEnt);
#if 0
BoatBody &body = e.ensure<BoatBody>();
body.body =
ECS::get<EngineData>()
@@ -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<BoatBody>();
#if 0
Ogre::Any bodyA =
boat.mNode->getUserObjectBindings()
.getUserAny("bodyPointer");
if (bodyA.has_value())
body.body =
Ogre::any_cast<btRigidBody *>(
bodyA);
#if 0
body.body =
ECS::get<EngineData>()
.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<Ogre::Node *> slots =
boat.mNode->getChildren();
for (i = 0; i < slots.size(); i++) {
@@ -167,7 +283,10 @@ BoatModule::BoatModule(flecs::world &ecs)
}
}
}
// ECS::get_mut<ECS::EngineData>().enableDbgDraw = true;
// ECS::modified<ECS::EngineData>();
e.modified<BoatBody>();
});
#endif
}
}

View File

@@ -18,9 +18,7 @@ struct BoatBase {
Ogre::Entity *mEnt;
Ogre::SceneNode *mNode;
};
struct BoatBody {
btRigidBody *body;
};
struct SpawnBoat {};
struct BoatModule {
BoatModule(flecs::world &ecs);
};

View File

@@ -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)

View File

@@ -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<CharacterDisablePhysics>() &&
!e.has<CharacterInActuator>()) {
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<const EngineData, const AnimationControl,
const CharacterBase, CharacterVelocity>("HandleSwimming")
.kind(flecs::OnUpdate)
@@ -271,12 +278,13 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
h * eng.delta;
}
});
ecs.system<const EngineData, CharacterBase, CharacterBody,
AnimationControl, CharacterVelocity>("HandleRootMotion")
#endif
ecs.system<const EngineData, CharacterBase, AnimationControl,
CharacterVelocity>("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<CharacterVelocity, CharacterBase>("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<const Input, const CharacterBase, AnimationControl>(
"HandleNPCAnimations")
@@ -576,6 +588,7 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
<< std::endl;
});
#endif
#if 0
ecs.system<const EngineData, const CharacterBase, CharacterBody>(
"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<GameWorld::Parameter *> &args)
override

View File

@@ -1,10 +1,10 @@
#include <iostream>
#include <Ogre.h>
#include <OgreBullet.h>
#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<Character>();
ecs.component<Player>();
ecs.component<CharacterBase>();
ecs.component<CharacterVelocity>();
ecs.component<CharacterBody>();
ecs.component<CharacterGravity>();
ecs.component<CharacterLocation>();
ecs.component<CharacterBuoyancy>();
ecs.component<CharacterConf>();
ecs.component<CharacterDisablePhysics>();
ecs.component<CharacterUpdatePhysicsState>();
ecs.component<CharacterInActuator>();
@@ -125,6 +127,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
else if (current_subm < 0.8f)
ch.is_submerged = false;
});
#if 0
ecs.system<const EngineData, const CharacterBase, CharacterVelocity>(
"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<const EngineData, const CharacterBase, CharacterVelocity>(
"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<const Input, const Camera, CharacterBase>("UpdateBody")
.kind(flecs::OnUpdate)
@@ -235,6 +241,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
ch.mBodyNode->yaw(Ogre::Degree(yawToGoal));
}
});
#if 0
ecs.system<const EngineData, CharacterLocation, CharacterBase,
CharacterBody>("UpdateCharacterBase")
.kind(flecs::OnUpdate)
@@ -251,6 +258,40 @@ CharacterModule::CharacterModule(flecs::world &ecs)
ch.mBodyNode->_getDerivedPosition();
}
});
#endif
ecs.observer<const EngineData, const CharacterLocation,
const CharacterConf>("SetupCharacterM")
.event(flecs::OnSet)
.with<Character>()
.without<CharacterBase>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterLocation &loc,
const CharacterConf &conf) {
CharacterBase &ch = e.ensure<CharacterBase>();
AnimationControl &anim = e.ensure<AnimationControl>();
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<CharacterVelocity>(
{ { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } });
e.add<CharacterGravity>();
e.add<CharacterBuoyancy>();
anim.configured = false;
});
#if 0
ecs.system<const EngineData, const CharacterLocation,
const CharacterConf, Body2Entity>("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<CharacterVelocity>(
{ { 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<CharacterGravity>();
e.add<CharacterBuoyancy>();
anim.configured = false;
OgreAssert(body.mGhostObject->hasContactResponse(),
"need contact response");
// OgreAssert(body.mGhostObject->hasContactResponse(),
// "need contact response");
});
#endif
#if 0
ecs.system<const EngineData, CharacterBase, CharacterBody>(
"UpdateCharacterPhysics")
.kind(flecs::OnUpdate)
@@ -334,17 +379,21 @@ CharacterModule::CharacterModule(flecs::world &ecs)
.with<WaterReady>()
.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<const EngineData, Camera, const CharacterBase>(
"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<const EngineData, CharacterBody>("CheckGround")
#endif
ecs.system<const EngineData, CharacterBase>("CheckGround")
.kind(flecs::OnUpdate)
.with<Character>()
.with<Player>()
.without<GroundCheckReady>()
.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<GroundCheckReady>();
}
#endif
ECS::get().add<GroundCheckReady>();
});
#if 0
ecs.system<const WaterBody, const CharacterBase, CharacterBody>(
"CharacterWater1")
.kind(flecs::OnUpdate)
@@ -445,11 +500,13 @@ CharacterModule::CharacterModule(flecs::world &ecs)
.without<InWater>()
.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<InWater>();
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<const WaterBody, const CharacterBase, CharacterBody>(
"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<InWater>();
else if (!waterb.isInWater(body.mGhostObject) &&
h > 0.05f)
e.remove<InWater>();
#endif
});
#endif
#if 0
ecs.system<const EngineData, CharacterBase, CharacterBody>(
"DisplayPlayerPos")
@@ -483,39 +545,14 @@ CharacterModule::CharacterModule(flecs::world &ecs)
<< "\n";
});
#endif
ecs.system<const EngineData, const CharacterBase, ParentSlot>(
"UpdatePhysics2")
.kind(flecs::OnUpdate)
.with<CharacterDisablePhysics>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterBase &ch, ParentSlot &slot) {
if (slot.parent_e.has<ObjectSlots>()) {
const ObjectSlots &slots =
slot.parent_e.get<ObjectSlots>();
if (slots.slots.find(slot.slot_name) ==
slots.slots.end())
// invalid setting
e.remove<ParentSlot>();
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<const EngineData, const CharacterBody>("UpdatePhysics")
.kind(flecs::OnUpdate)
.with<CharacterUpdatePhysicsState>()
.write<CharacterUpdatePhysicsState>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterBody &body) {
#if 0
if (e.has<CharacterDisablePhysics>()) {
eng.mWorld->getBtWorld()->removeAction(
body.mController);
@@ -523,8 +560,10 @@ CharacterModule::CharacterModule(flecs::world &ecs)
eng.mWorld->getBtWorld()->addAction(
body.mController);
}
#endif
e.remove<CharacterUpdatePhysicsState>();
});
#endif
}
void CharacterModule::updateCameraGoal(Camera &camera, Ogre::Real deltaYaw,
@@ -726,4 +765,4 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
}
});
}
}
}

View File

@@ -1,6 +1,7 @@
#ifndef CHARACTER_MODULE_H_
#define CHARACTER_MODULE_H_
#include <flecs.h>
#include <Ogre.h>
#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
#endif

View File

@@ -2,7 +2,6 @@
#define COMPONENTS_H_
#include <flecs.h>
#include <Ogre.h>
#include <OgreBullet.h>
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<OgreBites::InputListener *> 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<Ogre::String,
std::pair<Ogre::String, Ogre::SceneNode *> >
slots;
};
struct Body2Entity {
std::unordered_map<btCollisionObject *, flecs::entity> entities;
/* std::unordered_map<btCollisionObject *, flecs::entity> entities; */
};
}
#endif

View File

@@ -1,11 +1,14 @@
#include <iostream>
#include <OgreBullet.h>
#include <OgreMeshManager.h>
#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<EventTriggerModule>();
ecs.component<EventTrigger>();
ecs.component<EventTriggerData>();
ecs.observer<const EngineData, const EventTrigger>("CreateTrigger")
.event(flecs::OnSet)
.each([](flecs::entity e, const EngineData &eng,
const EventTrigger &trigger) {
e.set<EventTriggerData>({});
ECS::get<LuaBase>().mLua->call_handler(
"actuator_created", e, e);
});
#if 0
ecs.component<EventTriggerData>();
ecs.component<TriggerBody>().on_add([](flecs::entity e,
TriggerBody &body) {
@@ -311,4 +326,5 @@ ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs)
it++;
}
});
#endif
}

View File

@@ -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 <GameWorldModule>();
ecs.component<InWater>();
ecs.component<WaterReady>().add(flecs::Singleton);
ecs.component<WaterAlmostReady>().add(flecs::Singleton);
ecs.component<GroundCheckReady>().add(flecs::Singleton);
ecs.component<App>()
.on_add([](App &app) {
@@ -37,26 +39,24 @@ void setup_minimal()
.add(flecs::Singleton);
/* lots of things depend on it */
ecs.component<TerrainReady>().add(flecs::Singleton);
ecs.component<ParentSlot>();
ecs.component<ObjectSlots>();
ecs.component<Body2Entity>().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 <WaterModule>();
ecs.component<RenderWindow>().add(flecs::Singleton);
ecs.import <CharacterModule>();
ecs.import <TerrainModule>();
ecs.import <BoatModule>();
ecs.import <PhysicsModule>();
ecs.import <WaterModule>();
ecs.import <SunModule>();
ecs.import <TerrainModule>();
ecs.import <GUIModule>();
ecs.import <EventTriggerModule>();
ecs.import <LuaModule>();
ecs.import <WorldMapModule>();
ecs.import <LuaModule>();
ecs.import <BoatModule>();
ecs.import <EventTriggerModule>();
ecs.import <CharacterAnimationModule>();
ecs.system<EngineData>("UpdateDelta")
@@ -89,9 +89,8 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
std::cout << "ground check ready\n";
#endif
});
ecs.set<EngineData>({ scnMgr, world, 0.0f, 5.0f,
(int)window->getWidth(), (int)window->getHeight(),
false });
ecs.set<EngineData>({ scnMgr, 0.0f, 5.0f, (int)window->getWidth(),
(int)window->getHeight(), false });
ecs.set<Camera>({ cameraNode, camera, false });
ecs.add<GameData>();
ecs.add<Input>();
@@ -105,7 +104,7 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
nullptr,
false,
{ 0, 0, 0 } });
ecs.set<Body2Entity>({});
// ecs.set<Body2Entity>({});
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);
}
}
}

View File

@@ -1,14 +1,12 @@
#ifndef GAMEDATA_H
#define GAMEDATA_H
#include <OgreBullet.h>
#include <flecs.h>
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 <class T> const T &get()

View File

@@ -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<FooPosition> {
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<const char *>::get(L, top - 1);
if (strncmp(name, "x", 1) == 0)
result.value.x =
LuaStack<float>::get(L, top);
else if (strncmp(name, "y", 1) == 0)
result.value.y =
LuaStack<float>::get(L, top);
else if (strncmp(name, "z", 1) == 0)
result.value.z =
LuaStack<float>::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<const char *>::put(L, "x");
LuaStack<float>::put(L, v.value.x);
lua_rawset(L, -3);
LuaStack<const char *>::put(L, "y");
LuaStack<float>::put(L, v.value.y);
lua_rawset(L, -3);
LuaStack<const char *>::put(L, "z");
LuaStack<float>::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<Ogre::Vector3> luaVector3(L, "Vector3");
luaVector3.ctor<float, float, float>();
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<CharacterBase>()) {
const CharacterBase &cb = e.get<CharacterBase>();
return cb.mBodyNode->_getDerivedPosition();
} else if (e.has<BoatBase>()) {
const BoatBase &boat = e.get<BoatBase>();
return boat.mNode->_getDerivedPosition();
} else if (e.has<EventTrigger>()) {
const EventTrigger &trigger = e.get<EventTrigger>();
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<ParentSlot>({ parent_e, slot, false });
if (!object_e.has<CharacterDisablePhysics>())
object_e.add<CharacterDisablePhysics>();
object_e.set<ParentSlot>({ 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<LuaModule>();
ecs.import <SlotsModule>();
ecs.component<LuaChildEventTrigger>();
ecs.component<LuaBase>()
.on_add([](LuaBase &lua) {

View File

@@ -0,0 +1,535 @@
#include <iostream>
#include <Ogre.h>
#include <Jolt/Jolt.h>
#include <Jolt/Physics/Body/BodyID.h>
#include <Jolt/Physics/Character/CharacterBase.h>
#include <Jolt/Physics/Character/Character.h>
#include <Jolt/Physics/Collision/BroadPhase/BroadPhase.h>
#include <Jolt/Physics/Body/BodyLock.h>
#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<JPH::BodyID> inWater;
bool isInWater(const JPH::BodyID &id) const;
};
struct TriggerBody {
void *foo;
};
PhysicsModule::PhysicsModule(flecs::world &ecs)
{
ecs.module<PhysicsModule>();
ecs.import <EventTriggerModule>();
ecs.import <BoatModule>();
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<Physics>().add(flecs::Singleton);
ecs.component<JPH::BodyID>().member<uint32_t>("mID");
/* for terrain */
ecs.component<PhysicsBody>();
ecs.component<PhysicsShape>();
ecs.component<PhysicsNode>();
ecs.component<PhysicsMeshName>();
ecs.component<PhysicsMeshPtr>();
ecs.component<PhysicsHeightfieldData>();
ecs.component<CharacterBody>();
ecs.component<TriggerBody>();
ecs.component<CharacterVelocity>();
ecs.component<WaterBody>().add(flecs::Singleton);
ecs.system<const EngineData, const Camera>("physics_init")
.kind(PhysicsPreUpdate)
.without<Physics>()
.each([&](const EngineData &e, const Camera &c) {
Physics &ph = ECS::get().ensure<Physics>();
ph.physics = new JoltPhysicsWrapper(e.mScnMgr,
c.mCameraNode);
ECS::modified<Physics>();
});
#if 0
ecs.system<PhysicsBody>("create_body")
.kind(flecs::OnUpdate)
.without<JPH::BodyID>()
.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<JPH::BodyID>(id);
});
#endif
ecs.system<EngineData, Physics>("physics_update")
.kind(PhysicsUpdate)
.each([&](EngineData &e, Physics &ph) {
ph.physics->update(e.delta);
});
ecs.observer<const EngineData, PhysicsMeshName>(
"create_shape_mesh_name")
.event(flecs::OnSet)
.without<PhysicsShape>()
.with<Physics>()
.write<PhysicsShape>()
.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<PhysicsShape>();
s.shape = shape;
e.modified<PhysicsShape>();
});
ecs.observer<const EngineData, PhysicsMeshPtr>("create_shape_mesh_ptr")
.event(flecs::OnSet)
.without<PhysicsShape>()
.with<Physics>()
.write<PhysicsShape>()
.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<PhysicsShape>();
s.shape = shape;
e.modified<PhysicsShape>();
});
ecs.observer<const EngineData, PhysicsHeightfieldData>(
"create_shape_heightfield")
.event(flecs::OnSet)
.without<PhysicsShape>()
.with<Physics>()
.write<PhysicsShape>()
.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<PhysicsShape>();
s.shape = shape;
e.modified<PhysicsShape>();
delete hfd.samples;
e.remove<PhysicsHeightfieldData>();
});
#if 1
ecs.observer<const EngineData, const PhysicsShape, const PhysicsNode,
const PhysicsBody>("create_body_from_shape")
.event(flecs::OnSet)
.without<JPH::BodyID>()
.with<Physics>()
.write<PhysicsShape>()
.write<JPH::BodyID>()
.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<JPH::BodyID>(id);
JoltPhysicsWrapper::getSingleton().addBody(
id, JPH::EActivation::Activate);
});
#endif
ecs.observer<const JPH::BodyID>("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<const EngineData, const CharacterBase>("SetupCharacterPh")
.event(flecs::OnSet)
.with<Character>()
.without<CharacterBody>()
.write<CharacterBody>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterBase &base) {
CharacterBody &b = e.ensure<CharacterBody>();
b.ch.reset(JoltPhysicsWrapper::getSingleton()
.createCharacter(base.mBodyNode,
1.75f, 0.23f));
if (!e.has<CharacterDisablePhysics>())
static_cast<JPH::Character *>(b.ch.get())
->AddToPhysicsSystem(
JPH::EActivation::Activate);
e.modified<CharacterBody>();
});
ecs.observer<const EngineData, const EventTrigger>(
"CreateTriggerPhysics")
.event(flecs::OnSet)
.without<JPH::BodyID>()
.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<JPH::BodyID>(id);
// JoltPhysicsWrapper::getSingleton().setDebugDraw(true);
JoltPhysicsWrapper::getSingleton().addBody(
id, JPH::EActivation::Activate);
});
#if 0
ecs.system<const EngineData, const EventTrigger, const JPH::BodyID>(
"UpdateTriggerPhysicsPre")
.kind(PhysicsPreUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<WaterBody>()
.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<const EngineData, const EventTrigger, const JPH::BodyID>(
"UpdateTriggerPhysicsPost")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<WaterBody>()
.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<const EngineData>("init_water")
.kind(PhysicsPreUpdate)
.with<TerrainReady>()
.with<WaterAlmostReady>()
.without<WaterBody>()
.each([this](const EngineData &eng) {
ECS::get().set<WaterBody>({});
});
ecs.system<const EngineData, WaterBody>("update_water")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
.with<WaterAlmostReady>()
.each([this](const EngineData &eng, WaterBody &body) {
const WaterSurface &water = ECS::get<WaterSurface>();
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<WaterReady>();
});
ecs.system<const JPH::BodyID, const WaterBody>("update_water_status1")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.each([this](flecs::entity e, const JPH::BodyID &id,
const WaterBody &body) {
if (!body.isInWater(id))
e.remove<InWater>();
});
ecs.system<const JPH::BodyID, const WaterBody>("update_water_status2")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.without<InWater>()
.each([this](flecs::entity e, const JPH::BodyID &id,
const WaterBody &body) {
if (body.isInWater(id))
e.add<InWater>();
});
ecs.system<const CharacterBody, const WaterBody>(
"update_water_character1")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.each([this](flecs::entity e, const CharacterBody &ch,
const WaterBody &body) {
JPH::Character *chptr =
static_cast<JPH::Character *>(ch.ch.get());
if (!body.isInWater(chptr->GetBodyID()))
e.remove<InWater>();
});
ecs.system<const CharacterBody, const WaterBody>(
"update_water_character2")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.without<InWater>()
.each([this](flecs::entity e, const CharacterBody &ch,
const WaterBody &body) {
JPH::Character *chptr =
static_cast<JPH::Character *>(ch.ch.get());
if (body.isInWater(chptr->GetBodyID()))
e.add<InWater>();
});
ecs.system<const EngineData, const BoatBase, const WaterBody,
const JPH::BodyID>("update_water_boat_enable")
.kind(PhysicsPreUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.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<const EngineData, const BoatBase, const WaterBody,
const JPH::BodyID>("update_water_boat_activation")
.kind(PhysicsPreUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.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<const EngineData, const BoatBase, const WaterBody,
const JPH::BodyID>("update_water_boat_buoyancy")
.kind(PhysicsPreUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.each([this](flecs::entity e, const EngineData &eng,
const BoatBase &boat, const WaterBody &body,
const JPH::BodyID &id) {
const WaterSurface &water = ECS::get<WaterSurface>();
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<const EngineData, const CharacterBody, const WaterBody>(
"update_water_character_buoyancy")
.kind(PhysicsPreUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<InWater>()
.without<CharacterDisablePhysics>()
.with<CharacterBuoyancy>()
.each([this](flecs::entity e, const EngineData &eng,
const CharacterBody &ch, const WaterBody &body) {
JPH::Character *chptr =
static_cast<JPH::Character *>(ch.ch.get());
JPH::BodyID id = chptr->GetBodyID();
if (JoltPhysicsWrapper::getSingleton().isActive(id)) {
const WaterSurface &water =
ECS::get<WaterSurface>();
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<const EngineData, const CharacterBody, CharacterVelocity>(
"HandleVelocity")
.kind(PhysicsPostUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.without<CharacterDisablePhysics>()
.without<CharacterUpdatePhysicsState>()
.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<JPH::Character *>(body.ch.get());
if (!e.has<InWater>()) {
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<PhysicsHeightfieldData>({ samples, offset, scale, sampleCount });
e.set<PhysicsBody>({ (uint32_t)JPH::EMotionType::Static,
(uint32_t)Layers::NON_MOVING });
e.set<PhysicsNode>({ node });
return e;
}
bool WaterBody::isInWater(const JPH::BodyID &id) const
{
return inWater.find(id) != inWater.end();
}
}

View File

@@ -0,0 +1,57 @@
#ifndef _PHYSICS_MODULE_H_
#define _PHYSICS_MODULE_H_
#include <flecs.h>
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<JPH::CharacterBase> 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

View File

@@ -0,0 +1,153 @@
#include <iostream>
#include "Components.h"
#include "GameData.h"
#include "CharacterModule.h"
#include "BoatModule.h"
#include "SlotsModule.h"
namespace ECS
{
SlotsModule::SlotsModule(flecs::world &ecs)
{
ecs.module<SlotsModule>();
ecs.import <CharacterModule>();
ecs.component<ParentSlot>();
ecs.component<ParentSlotData>();
ecs.component<ObjectSlots>();
ecs.observer<const EngineData, const BoatBase>("CreateBoatSlots")
.event(flecs::OnSet)
.each([](flecs::entity e, const EngineData &eng,
const BoatBase &boat) {
int i;
std::vector<Ogre::Node *> slots =
boat.mNode->getChildren();
for (i = 0; i < slots.size(); i++) {
Ogre::Any any =
static_cast<Ogre::SceneNode *>(slots[i])
->getUserObjectBindings()
.getUserAny("type");
if (!any.has_value())
continue;
Ogre::String obj_type =
Ogre::any_cast<Ogre::String>(any);
std::cout << "child type: " << obj_type
<< std::endl;
}
if (slots.size() > 0) {
ObjectSlots &vs = e.ensure<ObjectSlots>();
for (i = 0; i < slots.size(); i++) {
Ogre::Any any =
static_cast<Ogre::SceneNode *>(
slots[i])
->getUserObjectBindings()
.getUserAny("type");
if (!any.has_value())
continue;
Ogre::String obj_type =
Ogre::any_cast<Ogre::String>(
any);
any = static_cast<Ogre::SceneNode *>(
slots[i])
->getUserObjectBindings()
.getUserAny("name");
if (!any.has_value())
continue;
Ogre::String obj_name =
Ogre::any_cast<Ogre::String>(
any);
vs.slots[obj_name] = {
obj_type,
static_cast<Ogre::SceneNode *>(
slots[i])
};
}
}
});
ecs.system<const EngineData, const CharacterBase, ParentSlot,
ParentSlotData>("UpdatePhysics2a")
.kind(flecs::OnUpdate)
.with<CharacterDisablePhysics>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterBase &ch, ParentSlot &slot,
ParentSlotData &psdata) {
if (slot.slot_name == "") {
e.remove<ParentSlot>();
e.remove<ParentSlotData>();
}
if (slot.parent_e.has<ObjectSlots>()) {
const ObjectSlots &slots =
slot.parent_e.get<ObjectSlots>();
if (slots.slots.find(slot.slot_name) ==
slots.slots.end()) {
// invalid setting
e.remove<ParentSlot>();
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<ParentSlot>();
e.modified<ParentSlotData>();
std::cout << "base: " << slot_base->getName();
}
});
ecs.system<const EngineData, const CharacterBase, ParentSlot>(
"UpdatePhysics2")
.kind(flecs::OnUpdate)
.with<CharacterDisablePhysics>()
.without<ParentSlotData>()
.with<WaterReady>()
.each([](flecs::entity e, const EngineData &eng,
const CharacterBase &ch, ParentSlot &slot) {
std::cout << e.name() << std::endl;
if (!slot.parent_e.has<ObjectSlots>())
return;
OgreAssert(slot.parent_e.has<ObjectSlots>(),
"parent has no slots");
OgreAssert(e.has<Character>(), "not a character");
if (slot.parent_e.has<ObjectSlots>()) {
const ObjectSlots &slots =
slot.parent_e.get<ObjectSlots>();
if (slot.slot_name == "")
return;
if (slots.slots.find(slot.slot_name) ==
slots.slots.end()) {
// invalid setting
e.remove<ParentSlot>();
OgreAssert(false, "bad slot");
return;
}
ParentSlotData &psdata =
e.ensure<ParentSlotData>();
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<ParentSlot>();
e.modified<ParentSlotData>();
}
});
}
}

View File

@@ -0,0 +1,25 @@
#ifndef _SLOTS_MODULE_H_
#define _SLOTS_MODULE_H_
#include <flecs.h>
#include <Ogre.h>
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<Ogre::String,
std::pair<Ogre::String, Ogre::SceneNode *> >
slots;
};
struct SlotsModule {
SlotsModule(flecs::world &ecs);
};
}
#endif

View File

@@ -1,7 +1,6 @@
#include <unordered_set>
#include <iostream>
#include <Ogre.h>
#include <OgreBullet.h>
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
#include <OgreTerrain.h>
#include <OgreTerrainGroup.h>
@@ -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<struct gen_collider> 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<EngineData>()
.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<Ogre::PageID, btRigidBody *> body;
btDynamicsWorld *mBtWorld;
bool prepareProceduralPage(Ogre::Page *page,
Ogre::PagedWorldSection *section)
{
@@ -377,6 +400,7 @@ TerrainModule::TerrainModule(flecs::world &ecs)
ecs.component<CanSetPlayerPosition>().add(flecs::Singleton);
ecs.component<Terrain>().add(flecs::Singleton);
ecs.component<TerrainPrivate>().add(flecs::Singleton);
ecs.component<PlacementObjects>();
ecs.set<TerrainPrivate>({ nullptr, {} });
ecs.system<const EngineData, const Camera, const Sun, Terrain,
TerrainPrivate>("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";

View File

@@ -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<WaterModule>();
ecs.component<WaterSurface>()
.on_add([](flecs::entity e, WaterSurface &water) {
ECS::get().add<WaterBody>();
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<WaterAlmostReady>();
})
.add(flecs::Singleton);
#if 0
ecs.component<WaterBody>().add(flecs::Singleton);
ecs.component<WaterBody>()
.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<EngineData>()
.mWorld->getBtWorld()
->addAction(body.action);
#endif
ECS::get().add<WaterReady>();
})
.add(flecs::Singleton);
#endif
ecs.system<const EngineData, const Camera, WaterSurface>("UpdateWater")
.kind(flecs::OnUpdate)
.with<WaterReady>()
@@ -520,6 +527,7 @@ WaterModule::WaterModule(flecs::world &ecs)
// water.mRenderTargetListener.mInDepth = false;
// water.mWaterEnt->setVisible(true);
});
#if 0
ecs.system<const EngineData, const WaterSurface, WaterBody>(
"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<btCollisionObject *> 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<btCompoundShape *>(
@@ -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<WaterPhysicsAction *>(action)->isInWater(body);
}
#endif
}

View File

@@ -37,20 +37,10 @@ struct WaterSurface {
mRefractionClipPlaneBelow;
Ogre::Viewport *mViewports[4];
};
struct WaterBody {
std::vector<btCollisionShape *> mChildShapes;
btCollisionShape *mWaterShape;
btPairCachingGhostObject *mWaterBody;
std::unordered_map<btCollisionObject *, float> 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

View File

@@ -2,6 +2,7 @@
#define H_GOAP_H_
#include <string>
#include <memory>
#include <set>
#include <flecs.h>
namespace ECS
{
@@ -346,4 +347,4 @@ template <class RunnerType> struct DeclareAction : public BaseAction {
}
};
#endif
#endif

View File

@@ -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})

1660
src/physics/physics.cpp Normal file

File diff suppressed because it is too large Load Diff

143
src/physics/physics.h Normal file
View File

@@ -0,0 +1,143 @@
#ifndef __PHYSICS_H_
#define __PHYSICS_H_
#include <Ogre.h>
#include <OgreSingleton.h>
#include <Jolt/Jolt.h>
#include <Jolt/Physics/Collision/Shape/Shape.h>
#include <Jolt/Physics/Collision/Shape/StaticCompoundShape.h>
#include <Jolt/Physics/Collision/ObjectLayer.h>
#include <Jolt/Physics/Collision/BroadPhase/BroadPhaseLayer.h>
#include <Jolt/Physics/Body/BodyCreationSettings.h>
#include <Jolt/Physics/EActivation.h>
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<JoltPhysicsWrapper> {
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<JPH::ShapeRefC> &shapes,
const std::vector<Ogre::Vector3> &positions,
const std::vector<Ogre::Quaternion> &rotations);
JPH::ShapeRefC createStaticCompoundShape(
const std::vector<JPH::ShapeRefC> &shapes,
const std::vector<Ogre::Vector3> &positions,
const std::vector<Ogre::Quaternion> &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<JPH::Shape> 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<JPH::BodyID> &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

View File

@@ -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)

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,13 @@
#ifndef LOADER_H_
#define LOADER_H_
#include <pugixml.hpp>
#include <flecs.h>
#include <OgreColourValue.h>
#include <OgreQuaternion.h>
#include <OgreResourceGroupManager.h>
#include <OgreString.h>
#include <OgrePlugin.h>
#include <OgreCodec.h>
#include <flecs.h>
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;

View File

@@ -13,7 +13,6 @@
#include <OgreMaterialManager.h>
#include <OgreTerrainAutoUpdateLod.h>
#include <OgreTerrainPagedWorldSection.h>
#include <OgreBullet.h>
#include <OgreMath.h>
#include <OgreLogManager.h>
#include "terrain.h"

View File

@@ -4,8 +4,6 @@
#include <unordered_set>
#include <iostream>
#include <Ogre.h>
#include <OgreBullet.h>
#include <BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h>
#include <OgreTerrain.h>
#include <OgreTerrainGroup.h>
#include <OgrePageManager.h>
@@ -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;

View File

@@ -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)

View File

@@ -0,0 +1,64 @@
#include <iostream>
#include <Ogre.h>
#include <OgreCodec.h>
#include <OgreFileSystem.h>
#include <OgreFileSystemLayer.h>
#include <OgreMaterialManager.h>
#include <OgreShaderGenerator.h>
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<Ogre::String, Ogre::Any> 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;
}

View File

@@ -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)

View File

@@ -150,7 +150,7 @@ int main()
ecs.component<Female>();
ecs.component<Position>();
ecs.component<Target>();
ecs.set<ECS::EngineData>({ nullptr, nullptr, 0.0f, 5.0f, 0, 0, false });
ecs.set<ECS::EngineData>({ nullptr, 0.0f, 5.0f, 0, 0, false });
flecs::entity e1 = ecs.entity("e1");
e1.set<Blackboard>({ e1, 100, 100, 100, 0 });

View File

@@ -443,7 +443,7 @@ int main()
ecs.import <ECS::CharacterAIModule>();
ecs.import <ECS::CharacterAIExtraModule>();
ecs.import <ECS::GameWorldModule>();
ecs.set<ECS::EngineData>({ nullptr, nullptr, 0.0f, 5.0f, 0, 0, false });
ecs.set<ECS::EngineData>({ nullptr, 0.0f, 5.0f, 0, 0, false });
CopulationGoal goal_copulation;
SustainabilityGoal goal_sustainability;
ECS::Planner &planner = ecs.ensure<ECS::Planner>();

View File

@@ -6,9 +6,6 @@
// #include <OgreRenderTarget.h>
#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<Ogre::Bullet::DynamicsWorld> mDynWorld;
std::unique_ptr<Ogre::Bullet::DebugDrawer> mDbgDraw;
std::unique_ptr<Ogre::Root> mRoot;
std::unique_ptr<Ogre::SceneManager> mScnMgr;
std::unique_ptr<btDynamicsWorld> mbtWorld;
std::unique_ptr<Ogre::PageManager> 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<btCompoundShape *>(cs)->addChildShape(
transform, shape);
btScalar masses[1] = { mass };
btTransform principal;
static_cast<btCompoundShape *>(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<btCollisionObject *> &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<btCompoundShape *>(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<btCompoundShape *>(mCollisionShape)
->addChildShape(transform, shape);
btScalar masses[1] = { 0 };
btTransform principal;
static_cast<btCompoundShape *>(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);

View File

@@ -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)

View File

@@ -1,290 +0,0 @@
#include <iostream>
#include <OgreBullet.h>
#include <BulletCollision/CollisionDispatch/btGhostObject.h>
#include <btBulletCollisionCommon.h>
#include <btBulletDynamicsCommon.h>
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<const btCompoundShape *>(
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<const btBoxShape *>(
shape->getChildShape(i));
btVector3 hextents =
box->getHalfExtentsWithoutMargin();
std::cout << " box: " << hextents.getX() << ", "
<< hextents.getY() << ", "
<< hextents.getZ();
} break;
case 10: {
const btCapsuleShape *capsule =
static_cast<const btCapsuleShape *>(
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";
}
}

View File

@@ -3,7 +3,6 @@
#include <OgreMeshLodGenerator.h>
#include <OgreCompositorManager.h>
#include <OgreMaterialManager.h>
#include <OgreBullet.h>
#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)

View File

@@ -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<btCollisionObject *> 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<btCollisionObject *>(body);
return mOverlaps.find(test) != mOverlaps.end();
}
#endif
};
#endif