Water works!

This commit is contained in:
2025-09-15 01:51:38 +03:00
parent 4d0fb8f60f
commit 5c03f0cd2c
18 changed files with 2435 additions and 1501 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -71,6 +71,7 @@ add_subdirectory(src/gamedata)
add_subdirectory(src/miniaudio)
add_subdirectory(src/sound)
add_subdirectory(audio/gui)
add_subdirectory(tests)
# add the source files as usual
add_executable(0_Bootstrap Bootstrap.cpp)

425
Game.cpp
View File

@@ -167,6 +167,127 @@ public:
mSkyBoxGenParameters.skyBoxDistance = distance;
}
};
class App;
class KeyboardListener : public OgreBites::InputListener {
App *mApp;
uint32_t control;
ECS::Vector2 mouse;
float wheel_y;
bool mouse_moved, wheel_moved;
float mInitDelay;
public:
Ogre::Timer fps_timer;
bool fast;
KeyboardListener(App *app)
: OgreBites::InputListener()
, mApp(app)
, fast(false)
, control(0)
, mouse({ 0, 0 })
, wheel_y(0.0f)
, mouse_moved(false)
, wheel_moved(false)
, mInitDelay(1.0)
{
}
bool isGuiEnabled()
{
if (!ECS::get().has<ECS::GUI>())
return false;
return (ECS::get().get<ECS::GUI>().enabled);
}
void setGuiEnabled(bool value)
{
if (!ECS::get().has<ECS::GUI>())
return;
ECS::get().get_mut<ECS::GUI>().enabled = value;
ECS::get().modified<ECS::GUI>();
}
bool keyPressed(const OgreBites::KeyboardEvent &evt) override
{
bool updated = false;
if (isGuiEnabled())
return false;
if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) {
OgreAssert(ECS::get().has<ECS::GUI>(), "");
setGuiEnabled(true);
if (ECS::get().has<ECS::GUI>())
ECS::get<ECS::GUI>().setWindowGrab(false);
return true;
}
OgreBites::Keycode key = evt.keysym.sym;
if (key == 'w')
control |= 1;
else if (key == 'a')
control |= 2;
else if (key == 's')
control |= 4;
else if (key == 'd')
control |= 8;
else if (key == OgreBites::SDLK_LSHIFT)
control |= 16;
if (key == 'w' || key == 'a' || key == 's' || key == 'd' ||
key == OgreBites::SDLK_LSHIFT)
return true;
return false;
}
bool keyReleased(const OgreBites::KeyboardEvent &evt) override
{
OgreBites::Keycode key = evt.keysym.sym;
if (isGuiEnabled())
return false;
if (key == 'w')
control &= ~1;
else if (key == 'a')
control &= ~2;
else if (key == 's')
control &= ~4;
else if (key == 'd')
control &= ~8;
else if (key == OgreBites::SDLK_LSHIFT)
control &= ~16;
if (key == 'w' || key == 'a' || key == 's' || key == 'd' ||
key == OgreBites::SDLK_LSHIFT)
return true;
return false;
}
bool mouseMoved(const OgreBites::MouseMotionEvent &evt) override
{
if (isGuiEnabled())
return false;
mouse.x = evt.xrel;
mouse.y = evt.yrel;
mouse_moved = true;
/* no special mouse handling */
return true;
}
bool mouseWheelRolled(const OgreBites::MouseWheelEvent &evt) override
{
if (isGuiEnabled())
return false;
/* no special mouse wheel handling */
wheel_y = evt.y;
wheel_moved = true;
return true;
}
bool mousePressed(const OgreBites::MouseButtonEvent &evt) override
{
std::cout << "Mouse press " << (int)evt.button << " "
<< (int)evt.clicks << "\n";
if ((int)evt.button == 1)
control |= 256;
else
control &= ~256;
return false;
}
void update(float delta)
{
return;
}
void frameRendered(const Ogre::FrameEvent &evt) override;
};
class App : public OgreBites::ApplicationContext {
std::unique_ptr<Ogre::Bullet::DynamicsWorld> mDynWorld;
std::unique_ptr<Ogre::Bullet::DebugDrawer> mDbgDraw;
@@ -177,171 +298,8 @@ class App : public OgreBites::ApplicationContext {
Ogre::Viewport *mViewport;
SkyBoxRenderer *sky;
bool mGrab;
class KeyboardListener : public OgreBites::InputListener,
public Ogre::FrameListener {
App *mApp;
uint32_t control;
ECS::Vector2 mouse{ 0, 0 };
float wheel_y;
bool mouse_moved = false, wheel_moved = false;
float mInitDelay;
public:
Ogre::Timer fps_timer;
bool fast;
KeyboardListener(App *app)
: OgreBites::InputListener()
, Ogre::FrameListener()
, mApp(app)
, fast(false)
, control(0)
, mInitDelay(1.0)
{
}
bool isGuiEnabled()
{
if (!ECS::get().has<ECS::GUI>())
return false;
return (ECS::get().get<ECS::GUI>().enabled);
}
void setGuiEnabled(bool value)
{
if (!ECS::get().has<ECS::GUI>())
return;
ECS::get().get_mut<ECS::GUI>().enabled = value;
ECS::get().modified<ECS::GUI>();
}
bool keyPressed(const OgreBites::KeyboardEvent &evt) override
{
bool updated = false;
if (isGuiEnabled())
return false;
if (evt.keysym.sym == OgreBites::SDLK_ESCAPE) {
OgreAssert(ECS::get().has<ECS::GUI>(), "");
setGuiEnabled(true);
if (ECS::get().has<ECS::GUI>())
ECS::get<ECS::GUI>().setWindowGrab(
false);
return true;
}
OgreBites::Keycode key = evt.keysym.sym;
if (key == 'w')
control |= 1;
else if (key == 'a')
control |= 2;
else if (key == 's')
control |= 4;
else if (key == 'd')
control |= 8;
else if (key == OgreBites::SDLK_LSHIFT)
control |= 16;
if (key == 'w' || key == 'a' || key == 's' ||
key == 'd' || key == OgreBites::SDLK_LSHIFT)
return true;
return false;
}
bool keyReleased(const OgreBites::KeyboardEvent &evt) override
{
OgreBites::Keycode key = evt.keysym.sym;
if (isGuiEnabled())
return false;
if (key == 'w')
control &= ~1;
else if (key == 'a')
control &= ~2;
else if (key == 's')
control &= ~4;
else if (key == 'd')
control &= ~8;
else if (key == OgreBites::SDLK_LSHIFT)
control &= ~16;
if (key == 'w' || key == 'a' || key == 's' ||
key == 'd' || key == OgreBites::SDLK_LSHIFT)
return true;
return false;
}
bool mouseMoved(const OgreBites::MouseMotionEvent &evt) override
{
if (isGuiEnabled())
return false;
mouse.x = evt.xrel;
mouse.y = evt.yrel;
mouse_moved = true;
/* no special mouse handling */
return true;
}
bool
mouseWheelRolled(const OgreBites::MouseWheelEvent &evt) override
{
if (isGuiEnabled())
return false;
/* no special mouse wheel handling */
wheel_y = evt.y;
wheel_moved = true;
return true;
}
bool
mousePressed(const OgreBites::MouseButtonEvent &evt) override
{
std::cout << "Mouse press " << (int)evt.button << " "
<< (int)evt.clicks << "\n";
if ((int)evt.button == 1)
control |= 256;
else
control &= ~256;
return false;
}
void update(float delta)
{
return;
}
void frameRendered(const Ogre::FrameEvent &evt) override
{
if (fps_timer.getMilliseconds() > 1000.0f) {
std::cout << "FPS: "
<< mApp->getRenderWindow()
->getStatistics()
.lastFPS
<< " ";
std::cout << "Draw calls: "
<< mApp->getRenderWindow()
->getStatistics()
.batchCount
<< " ";
fps_timer.reset();
std::cout << "Drops: "
<< mApp->getRenderWindow()
->getStatistics()
.vBlankMissCount
<< "\n";
fps_timer.reset();
}
update(evt.timeSinceLastFrame);
if (!isGuiEnabled() && mApp->isTerrainReady()) {
OgreAssert(mApp->isTerrainReady(),
"terrain is not ready");
}
if (!isGuiEnabled()) {
mApp->updateWorld(evt.timeSinceLastFrame);
if (mInitDelay >= 0.0f)
mInitDelay -= evt.timeSinceLastFrame;
}
if (!isGuiEnabled() && ECS::get().has<ECS::Input>()) {
ECS::Input &input =
ECS::get().get_mut<ECS::Input>();
input.control = control;
input.mouse = mouse;
mouse.x = 0;
mouse.y = 0;
input.wheel_y = wheel_y;
wheel_y = 0;
input.mouse_moved = mouse_moved;
input.wheel_moved = wheel_moved;
}
}
};
KeyboardListener mKbd;
bool enabldDbgDraw;
public:
App()
@@ -350,6 +308,7 @@ public:
, mDynWorld(new Ogre::Bullet::DynamicsWorld(
Ogre::Vector3(0, -9.8, 0)))
, mGrab(false)
, enabldDbgDraw(false)
{
}
virtual ~App()
@@ -439,11 +398,13 @@ public:
.initialiseAllResourceGroups();
// OgreBites::ApplicationContext::loadResources();
// setupCursor();
std::cout << "Create content" << "\n";
createContent();
std::cout << "Setup input" << "\n";
setupInput();
std::cout << "Create content" << "\n";
createContent();
std::cout << "Setup done" << "\n";
mDbgDraw->setDebugMode(mDbgDraw->getDebugMode() |
btIDebugDraw::DBG_DrawContactPoints);
}
Ogre::SceneManager *getSceneManager()
{
@@ -474,9 +435,10 @@ public:
ECS::get().get_mut<ECS::GUI>().grabChanged = false;
ECS::get().modified<ECS::GUI>();
}
ECS::update(delta);
// mDbgDraw->update();
if (enabldDbgDraw)
mDbgDraw->update();
}
class InputListenerChainFlexible : public OgreBites::InputListener {
protected:
@@ -609,7 +571,30 @@ public:
flecs::entity find_wait_gui;
void setupInput()
{
ECS::App &p = ECS::get().get_mut<ECS::App>();
}
void createContent()
{
int i;
sky = new SkyBoxRenderer(getSceneManager());
bool drawFirst = true;
uint8_t renderQueue = drawFirst ?
Ogre::RENDER_QUEUE_SKIES_EARLY :
Ogre::RENDER_QUEUE_SKIES_LATE;
sky->create("Skybox/Dynamic", 450, renderQueue,
Ogre::Quaternion::IDENTITY,
Ogre::ResourceGroupManager::
AUTODETECT_RESOURCE_GROUP_NAME);
sky->setEnabled(true);
Ogre::MaterialPtr m =
Ogre::MaterialManager::getSingleton().getByName(
"Skybox/Dynamic", "General");
OgreAssert(m, "Sky box material not found.");
m->load();
ECS::setup(mScnMgr, mDynWorld.get(), mCameraNode, mCamera,
getRenderWindow());
ECS::get().set<ECS::RenderWindow>(
{ getRenderWindow(), getDisplayDPI() });
ECS::get()
.observer<ECS::GUI>("UpdateGrab")
.event(flecs::OnSet)
@@ -619,11 +604,6 @@ public:
std::cout << "grab: " << gui.grab << "\n";
std::cout << "GUI enabled: " << gui.enabled
<< "\n";
std::cout << "grabbed: " << isWindowGrab()
<< "\n";
std::cout
<< "UI active: " << mKbd.isGuiEnabled()
<< "\n";
});
ECS::get()
.observer<ECS::App>("UpdateInputListener")
@@ -632,8 +612,9 @@ public:
if (app.mInput)
removeInputListener(app.mInput);
delete app.mInput;
app.mInput = new OgreBites::InputListenerChain(
app.listeners);
app.mInput =
OGRE_NEW OgreBites::InputListenerChain(
app.listeners);
addInputListener(app.mInput);
std::cout << "Update listeners\n";
});
@@ -707,52 +688,10 @@ public:
<< "\n";
});
#endif
p.listeners.clear();
p.listeners.push_back(&mKbd);
ECS::get().modified<ECS::App>();
}
void createContent()
{
int i;
std::cout << "addFrameListener\n";
getRoot()->addFrameListener(&mKbd);
sky = new SkyBoxRenderer(getSceneManager());
bool drawFirst = true;
uint8_t renderQueue = drawFirst ?
Ogre::RENDER_QUEUE_SKIES_EARLY :
Ogre::RENDER_QUEUE_SKIES_LATE;
sky->create("Skybox/Dynamic", 450, renderQueue,
Ogre::Quaternion::IDENTITY,
Ogre::ResourceGroupManager::
AUTODETECT_RESOURCE_GROUP_NAME);
sky->setEnabled(true);
Ogre::MaterialPtr m =
Ogre::MaterialManager::getSingleton().getByName(
"Skybox/Dynamic", "General");
OgreAssert(m, "Sky box material not found.");
m->load();
ECS::setup(mScnMgr, mDynWorld.get(), mCameraNode, mCamera);
ECS::get()
.system<const ECS::GUI, ECS::App>("SetInputListener")
.kind(flecs::OnUpdate)
.each([this](const ECS::GUI &gui, ECS::App &app) {
if (gui.mGuiInpitListener &&
app.listeners.size() == 1) {
app.listeners.clear();
app.listeners.push_back(
gui.mGuiInpitListener);
app.listeners.push_back(&mKbd);
ECS::modified<ECS::App>();
}
});
ECS::get().set<ECS::RenderWindow>(
{ getRenderWindow(), getDisplayDPI() });
ECS::get().set<ECS::App>(
{ static_cast<OgreBites::ApplicationContext *>(this),
{ initialiseImGui(),
nullptr,
{} });
{ getImGuiInputListener(), &mKbd } });
Sound::setup();
Sound::ding();
}
@@ -792,7 +731,51 @@ public:
ECS::get().lookup("ECS::CharacterModule::player");
return player;
}
void enableDbgDraw(bool enable)
{
enabldDbgDraw = enable;
}
bool isEnabledDbgDraw() const
{
return enabldDbgDraw;
}
};
void KeyboardListener::frameRendered(const Ogre::FrameEvent &evt)
{
if (fps_timer.getMilliseconds() > 1000.0f) {
std::cout << "FPS: "
<< mApp->getRenderWindow()->getStatistics().lastFPS
<< " ";
std::cout << "Draw calls: "
<< mApp->getRenderWindow()->getStatistics().batchCount
<< " ";
fps_timer.reset();
std::cout << "Drops: "
<< mApp->getRenderWindow()
->getStatistics()
.vBlankMissCount
<< "\n";
fps_timer.reset();
}
update(evt.timeSinceLastFrame);
if (!isGuiEnabled()) {
mApp->updateWorld(evt.timeSinceLastFrame);
if (mInitDelay >= 0.0f)
mInitDelay -= evt.timeSinceLastFrame;
}
if (!isGuiEnabled() && ECS::get().has<ECS::Input>()) {
ECS::Input &input = ECS::get().get_mut<ECS::Input>();
input.control = control;
input.mouse = mouse;
mouse.x = 0;
mouse.y = 0;
input.wheel_y = wheel_y;
wheel_y = 0;
input.mouse_moved = mouse_moved;
input.wheel_moved = wheel_moved;
}
}
int main()
{
@@ -803,7 +786,7 @@ int main()
// register for input events
// KeyHandler keyHandler;
// ctx.addInputListener(&keyHandler);
ctx.enableDbgDraw(false);
ctx.getRoot()->startRendering();
ctx.setWindowGrab(false);
ctx.closeApp();

View File

@@ -141,7 +141,7 @@ public:
cs->calculateLocalInertia(mass, inertia);
} break;
case Ogre::Bullet::CT_CAPSULE: {
cs = new btCompoundShape();
cs = new btCompoundShape(false);
btScalar height = 1.0f;
btScalar radius = 0.3f;
shape = new btCapsuleShape(radius,
@@ -377,7 +377,7 @@ void CharacterController::setupBody()
// mRigidBody = world->addCharacter(mBodyEnt, 0);
// mCollisionShape = static_cast<btCompoundShape *>(mRigidBody->getCollisionShape());
mGhostObject = new btPairCachingGhostObject();
mCollisionShape = new btCompoundShape;
mCollisionShape = new btCompoundShape(false);
mGhostObject->setCollisionShape(mCollisionShape);
{

View File

@@ -4,28 +4,31 @@
#include "GameData.h"
#include "CharacterModule.h"
#include "WaterModule.h"
#include "TerrainModule.h"
#include "Components.h"
namespace ECS
{
CharacterModule::CharacterModule(flecs::world &ecs)
{
ecs.module<CharacterModule>();
player = ecs.entity("player");
player.set<AnimationControl>({ AnimationControl::ANIM_NONE,
AnimationControl::ANIM_NONE, false,
false });
player.set<CharacterBase>(
{ "normal-male.glb", 0.0f, nullptr, nullptr, nullptr });
player.set<CharacterBody>(
{ nullptr, nullptr, nullptr, { 0, 0, 0 }, false, false });
player.add<Character>();
player.add<Player>();
ecs.component<Character>();
ecs.component<Player>();
ecs.component<CharacterBase>();
ecs.component<CharacterVelocity>();
ecs.component<CharacterBody>().on_add(
[](flecs::entity e, CharacterBody &body) {
e.set<CharacterVelocity>(
{ { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } });
body.checkGround = false;
body.checkGroundResult = false;
body.mCollisionShape = nullptr;
body.mGhostObject = nullptr;
body.mController = nullptr;
});
ecs.system<EngineData, CharacterBase>("UpdateTimer")
.kind(flecs::OnUpdate)
.each([this](EngineData &eng, CharacterBase &ch) {
ch.mTimer += eng.delta;
if (eng.startupDelay >= 0.0f)
eng.startupDelay -= eng.delta;
});
ecs.system<Input, Camera>("HandleInput")
.kind(flecs::OnUpdate)
@@ -38,6 +41,8 @@ CharacterModule::CharacterModule(flecs::world &ecs)
uint32_t active = input.control;
float zaxis = input.motion.z;
zaxis *= 0.9f;
if (!camera.mCameraPivot || !camera.mCameraGoal)
return;
if (pressed & 1)
std::cout << "W pressed\n";
if (released & 1)
@@ -181,25 +186,15 @@ CharacterModule::CharacterModule(flecs::world &ecs)
return;
ch.mBoneMotion = ch.mRootBone->getPosition();
});
ecs.system<const EngineData, CharacterBase, CharacterBody,
AnimationControl>("HandleRootMotion")
ecs.system<const EngineData, const CharacterBase, CharacterVelocity>(
"HandleGravity")
.kind(flecs::OnUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.each([this](flecs::entity e, const EngineData &eng,
CharacterBase &ch, CharacterBody &body,
AnimationControl &anim) {
float delta = e.world().delta_time();
if (!ch.mBodyNode)
return;
Ogre::Quaternion rot = ch.mBodyNode->getOrientation();
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
Ogre::Vector3 boneMotion = ch.mBoneMotion;
Ogre::Vector3 velocity = rot * boneMotion / delta;
OgreAssert(delta > 0.0f, "Zero delta");
int maxPen = 0;
Ogre::Vector3 colNormal;
bool is_on_floor = false;
bool penetration = false;
const CharacterBase &ch, CharacterVelocity &gr) {
Ogre::Vector3 gravity(0, -9.8f, 0);
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
if (e.has<InWater>()) {
float volume = 2.0f * 0.5f * 0.5f;
float density = 900.0f;
@@ -216,33 +211,80 @@ CharacterModule::CharacterModule(flecs::world &ecs)
Ogre::Vector3 b = -gravity * density * volume *
multiplier * current_subm /
full_subm / mass;
body.gvelocity += (gravity + b) * delta;
body.gvelocity.y = Ogre::Math::Clamp(
body.gvelocity.y, -2.5f, 2.5f);
} else
body.gvelocity += gravity * delta;
gr.gvelocity += (gravity + b) * eng.delta;
gr.gvelocity.y = Ogre::Math::Clamp(
gr.gvelocity.y, -2.5f, 2.5f);
} else {
gr.gvelocity += gravity * eng.delta;
if (pos.y < -1.2) {
gr.gvelocity.y = 0.0f;
}
}
gr.gvelocity *= 0.99;
});
ecs.system<const EngineData, const CharacterBase, CharacterVelocity>(
"HandleRootMotionVelocity")
.kind(flecs::OnUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.each([this](flecs::entity e, const EngineData &eng,
const CharacterBase &ch, CharacterVelocity &v) {
if (eng.delta < 0.0000001f)
return;
if (!ch.mBodyNode)
return;
Ogre::Quaternion rot = ch.mBodyNode->getOrientation();
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
Ogre::Vector3 boneMotion = ch.mBoneMotion;
v.velocity = rot * boneMotion / eng.delta;
if (eng.startupDelay < 0.0f)
v.velocity += v.gvelocity;
v.velocity.y = Ogre::Math::Clamp(v.velocity.y, -10.5f,
1000000.0f);
});
ecs.system<const EngineData, CharacterBase, CharacterBody,
AnimationControl, CharacterVelocity>("HandleRootMotion")
.kind(flecs::OnUpdate)
.each([this](flecs::entity e, const EngineData &eng,
CharacterBase &ch, CharacterBody &body,
AnimationControl &anim, CharacterVelocity &v) {
if (!ch.mBodyNode)
return;
if (eng.delta < 0.0000001f)
return;
OgreAssert(eng.delta > 0.0f, "Zero delta");
int maxPen = 0;
Ogre::Vector3 colNormal;
bool is_on_floor = false;
bool penetration = false;
if (eng.startupDelay < 0.0f) {
body.gvelocity *= 0.99;
velocity += body.gvelocity;
Ogre::Vector3 rotMotion = velocity * delta;
btVector3 currentPosition =
body.mGhostObject->getWorldTransform()
.getOrigin();
is_on_floor = body.mController->isOnFloor();
penetration = body.mController->isPenetrating();
if (is_on_floor)
body.gvelocity =
Ogre::Vector3(0.0f, 0.0f, 0.0f);
if (body.mController) {
Ogre::Vector3 rotMotion =
v.velocity * eng.delta;
btVector3 currentPosition =
body.mGhostObject
->getWorldTransform()
.getOrigin();
is_on_floor =
body.mController->isOnFloor();
penetration = body.mController
->isPenetrating();
if (is_on_floor)
v.gvelocity = Ogre::Vector3(
0.0f, 0.0f, 0.0f);
btTransform from(
Ogre::Bullet::convert(
ch.mBodyNode->getOrientation()),
Ogre::Bullet::convert(
ch.mBodyNode->getPosition()));
ch.mBodyNode->setPosition(
ch.mBodyNode->getPosition() +
rotMotion);
ch.mBoneMotion = Ogre::Vector3(0, 0, 0);
btTransform from(
Ogre::Bullet::convert(
ch.mBodyNode
->getOrientation()),
Ogre::Bullet::convert(
ch.mBodyNode
->getPosition()));
ch.mBodyNode->setPosition(
ch.mBodyNode->getPosition() +
rotMotion);
ch.mBoneMotion = Ogre::Vector3(0, 0, 0);
}
}
});
ecs.system<const Input, AnimationControl>("HandlePlayerAnimations")
@@ -289,6 +331,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
.each([](const EngineData &eng, CharacterBase &ch,
CharacterBody &body) {
if (!ch.mBodyNode) {
body.mController = nullptr;
ch.mBodyEnt = eng.mScnMgr->createEntity(
"normal-male.glb");
ch.mBodyNode = eng.mScnMgr->getRootSceneNode()
@@ -297,7 +340,8 @@ CharacterModule::CharacterModule(flecs::world &ecs)
ch.mSkeleton = ch.mBodyEnt->getSkeleton();
body.mGhostObject =
new btPairCachingGhostObject();
body.mCollisionShape = new btCompoundShape;
body.mCollisionShape =
new btCompoundShape(false);
body.mGhostObject->setCollisionShape(
body.mCollisionShape);
{
@@ -333,16 +377,8 @@ CharacterModule::CharacterModule(flecs::world &ecs)
eng.mWorld->attachCollisionObject(
body.mGhostObject, ch.mBodyEnt, 1,
0x7FFFFFFF);
body.mController =
new Ogre::Bullet::KinematicMotionSimple(
body.mGhostObject,
ch.mBodyNode);
OgreAssert(body.mGhostObject,
"Need GhostObject");
OgreAssert(body.mController, "Need controller");
eng.mWorld->getBtWorld()->addAction(
body.mController);
OgreAssert(body.mCollisionShape,
"No collision shape");
OgreAssert(ch.mSkeleton->hasBone("Root"),
@@ -351,11 +387,31 @@ CharacterModule::CharacterModule(flecs::world &ecs)
OgreAssert(ch.mRootBone, "No root bone");
}
});
ecs.system<const EngineData, CharacterBase, CharacterBody>(
"UpdateCharacterPhysics")
.kind(flecs::OnUpdate)
.with<Character>()
.with<TerrainReady>()
.with<WaterReady>()
.each([](const EngineData &eng, CharacterBase &ch,
CharacterBody &body) {
if (ch.mBodyNode && !body.mController &&
eng.startupDelay < 0.0f) {
body.mController =
new Ogre::Bullet::KinematicMotionSimple(
body.mGhostObject,
ch.mBodyNode);
eng.mWorld->getBtWorld()->addAction(
body.mController);
OgreAssert(body.mController, "Need controller");
}
});
#define CAM_HEIGHT 1.6f // height of camera above character's center of mass
ecs.system<const EngineData, Camera, const CharacterBase>(
"UpdateCamera")
.kind(flecs::OnUpdate)
.with<Player>()
.with<GroundCheckReady>()
.each([](const EngineData &eng, Camera &camera,
const CharacterBase &ch) {
float delta = eng.delta;
@@ -475,8 +531,10 @@ CharacterModule::CharacterModule(flecs::world &ecs)
ecs.system<const EngineData, CharacterBody>("CheckGround")
.kind(flecs::OnUpdate)
.with<Character>()
.with<Player>()
.without<GroundCheckReady>()
.each([](const EngineData &eng, CharacterBody &body) {
if (body.checkGround) {
if (body.mGhostObject) {
btVector3 from =
body.mGhostObject->getWorldTransform()
.getOrigin() +
@@ -488,7 +546,8 @@ CharacterModule::CharacterModule(flecs::world &ecs)
resultCallback);
body.checkGroundResult =
resultCallback.hasHit();
body.checkGround = false;
if (resultCallback.hasHit())
ECS::get().add<GroundCheckReady>();
}
});
ecs.system<const WaterBody, const CharacterBase, CharacterBody>(
@@ -498,8 +557,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
.without<InWater>()
.each([](flecs::entity e, const WaterBody &waterb,
const CharacterBase &ch, CharacterBody &body) {
if (waterb.mInWater.find(body.mGhostObject) !=
waterb.mInWater.end() &&
if (waterb.isInWater(body.mGhostObject) &&
ch.mBodyNode->_getDerivedPosition().y < -0.05f) {
e.add<InWater>();
std::cout << "Big Splash\n";
@@ -518,8 +576,7 @@ CharacterModule::CharacterModule(flecs::world &ecs)
.with<InWater>()
.each([](flecs::entity e, const WaterBody &waterb,
const CharacterBase &ch, CharacterBody &body) {
if (waterb.mInWater.find(body.mGhostObject) ==
waterb.mInWater.end() &&
if (waterb.isInWater(body.mGhostObject) &&
ch.mBodyNode->_getDerivedPosition().y > 0.05f)
e.remove<InWater>();
});
@@ -535,6 +592,16 @@ CharacterModule::CharacterModule(flecs::world &ecs)
<< "\n";
});
#endif
/* Create player */
player = ecs.entity("player");
player.set<AnimationControl>({ AnimationControl::ANIM_NONE,
AnimationControl::ANIM_NONE, false,
false });
player.set<CharacterBase>(
{ "normal-male.glb", 0.0f, nullptr, nullptr, nullptr });
player.set<CharacterBody>({ nullptr, nullptr, nullptr, false, false });
player.add<Character>();
player.add<Player>();
}
void CharacterModule::setAnimation(AnimationControl &anim)

View File

@@ -21,10 +21,13 @@ struct CharacterBody {
btPairCachingGhostObject *mGhostObject;
btCompoundShape *mCollisionShape;
Ogre::Bullet::KinematicMotionSimple *mController;
Ogre::Vector3 gvelocity;
bool checkGround;
bool checkGroundResult;
};
struct CharacterVelocity {
Ogre::Vector3 gvelocity;
Ogre::Vector3 velocity;
};
struct AnimationControl {
enum AnimID {
ANIM_IDLE = 0,

View File

@@ -2,6 +2,10 @@
#define COMPONENTS_H_
#include <Ogre.h>
#include <OgreBullet.h>
namespace Ogre
{
class ImGuiOverlay;
}
namespace OgreBites
{
class ApplicationContextSDL;
@@ -18,6 +22,8 @@ struct EngineData {
Ogre::Bullet::DynamicsWorld *mWorld;
float delta;
float startupDelay;
int width;
int height;
};
struct Vector3 {
float x;
@@ -58,10 +64,13 @@ struct RenderWindow {
float dpi;
};
struct App {
OgreBites::ApplicationContextSDL *app;
Ogre::ImGuiOverlay *mGuiOverlay;
OgreBites::InputListenerChain *mInput;
std::vector<OgreBites::InputListener *> listeners;
};
struct InWater {};
struct TerrainReady {};
struct WaterReady {};
struct GroundCheckReady {};
}
#endif

View File

@@ -144,6 +144,27 @@ struct GUIListener : public Ogre::RenderTargetListener {
{
int i;
Ogre::ImGuiOverlay::NewFrame();
if (ECS::get().get<EngineData>().startupDelay > 0.0f) {
ImVec2 size = ImGui::GetMainViewport()->Size;
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(size.x, size.y),
ImGuiCond_Always);
ImGui::Begin(
"StartupScreen", nullptr,
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoInputs);
// if (ECS::get().get<GUI>().enabled)
// ECS::get().get<App>().app->setWindowGrab(true);
ImGui::Text(
"This game does not autosave. Please use save function to keep your state");
ImGui::End();
}
if (ECS::get().get<GUI>().enabled) {
buttons_panel();
buildings_editor();
@@ -258,9 +279,21 @@ struct GUIListener : public Ogre::RenderTargetListener {
GUIModule::GUIModule(flecs::world &ecs)
{
ecs.component<GUI>().add(flecs::Singleton);
ecs.component<GUIData>().add(flecs::Singleton);
ecs.set<GUI>({ false, true, false, nullptr });
ecs.component<GUI>()
.on_add([](GUI &gui) {
gui.enabled = false;
gui.grab = false;
gui.grabChanged = false;
})
.add(flecs::Singleton);
ecs.component<GUIData>()
.on_add([](GUIData &priv) {
priv.glb_names.clear();
priv.mGUIListener = nullptr;
priv.mGuiOverlay = nullptr;
})
.add(flecs::Singleton);
ecs.set<GUI>({ false, true, false });
ecs.set<GUIData>({ nullptr, {}, nullptr });
ui_wait =
ecs.system<const RenderWindow, App, GUIData>("SetupGUI")
@@ -275,8 +308,9 @@ GUIModule::GUIModule(flecs::world &ecs)
Ogre::OverlayManager::getSingleton()
.setPixelRatio(vpScale);
std::cout << "GUI configure\n";
gui.mGuiOverlay =
app.app->initialiseImGui();
OgreAssert(app.mGuiOverlay,
"No ImGUI overlay");
gui.mGuiOverlay = app.mGuiOverlay;
gui.mGuiOverlay->setZOrder(300);
gui.mGuiOverlay->show();
gui.mGUIListener = new GUIListener();
@@ -299,10 +333,6 @@ GUIModule::GUIModule(flecs::world &ecs)
names.begin(),
names.end());
}
ECS::get_mut<ECS::GUI>()
.mGuiInpitListener =
new OgreBites::
ImGuiInputListener();
ECS::modified<ECS::GUI>();
std::cout << "GUI configure finished\n";
}

View File

@@ -3,7 +3,6 @@
#include <flecs.h>
namespace OgreBites
{
class InputListener;
}
namespace ECS
{
@@ -11,7 +10,6 @@ struct GUI {
bool enabled;
bool grab;
bool grabChanged;
OgreBites::InputListener *mGuiInpitListener;
static void setWindowGrab(bool g = true)
{
ECS::GUI &gui = ECS::get().get_mut<ECS::GUI>();

View File

@@ -12,7 +12,8 @@ namespace ECS
{
static flecs::world ecs;
void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
Ogre::SceneNode *cameraNode, Ogre::Camera *camera)
Ogre::SceneNode *cameraNode, Ogre::Camera *camera,
Ogre::RenderWindow *window)
{
std::cout << "Setup GameData\n";
ecs.component<EngineData>().add(flecs::Singleton);
@@ -20,23 +21,60 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
ecs.component<Input>().add(flecs::Singleton);
ecs.component<Camera>().add(flecs::Singleton);
ecs.component<InWater>();
ecs.component<App>().add(flecs::Singleton);
ecs.component<WaterReady>().add(flecs::Singleton);
ecs.component<GroundCheckReady>().add(flecs::Singleton);
ecs.component<App>()
.on_add([](App &app) {
app.mInput = nullptr;
app.mGuiOverlay = nullptr;
app.listeners.clear();
})
.add(flecs::Singleton);
/* lots of things depend on it */
ecs.component<TerrainReady>().add(flecs::Singleton);
ecs.import <WaterModule>();
ecs.import <CharacterModule>();
ecs.import <SunModule>();
ecs.import <TerrainModule>();
ecs.import <SunModule>();
ecs.import <GUIModule>();
ecs.system<EngineData>("UpdateDelta")
.kind(flecs::OnUpdate)
.each([](EngineData &eng) {
eng.delta = ECS::get().delta_time();
});
ecs.set<EngineData>({ scnMgr, world, 0.0f, 0.0f });
ecs.system<EngineData>("UpdateDelay")
.kind(flecs::OnUpdate)
.with<TerrainReady>()
.with<WaterReady>()
.with<GroundCheckReady>()
.each([](EngineData &eng) {
if (eng.startupDelay >= 0.0f)
eng.startupDelay -= eng.delta;
#ifdef VDEBUG
if (ECS::get().has<GroundCheckReady>())
std::cout << "ground check ready\n";
#endif
});
ecs.system<EngineData>("CheckStatus")
.kind(flecs::OnUpdate)
.run([](flecs::iter &it) {
#ifdef VDEBUG
if (ECS::get().has<WaterReady>())
std::cout << "water ready\n";
if (ECS::get().has<TerrainReady>())
std::cout << "terrain ready\n";
if (ECS::get().has<GroundCheckReady>())
std::cout << "ground check ready\n";
#endif
});
ecs.set<EngineData>({ scnMgr, world, 0.0f, 5.0f,
(int)window->getWidth(),
(int)window->getHeight() });
ecs.set<Camera>({ cameraNode, camera, false });
ecs.add<GameData>();
ecs.add<Input>();
ecs.set<WaterSurface>({ nullptr, nullptr, nullptr, nullptr, nullptr });
ecs.set<WaterBody>({ nullptr });
ecs.add<WaterSurface>();
ecs.set<Sun>({ nullptr, nullptr, nullptr, nullptr });
ecs.set<Terrain>({ nullptr,
nullptr,
nullptr,

View File

@@ -5,7 +5,8 @@
namespace ECS
{
void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
Ogre::SceneNode *cameraNode, Ogre::Camera *camera);
Ogre::SceneNode *cameraNode, Ogre::Camera *camera,
Ogre::RenderWindow *window);
void update(float delta);
flecs::world get();
template <class T> const T &get()

View File

@@ -7,7 +7,6 @@ namespace ECS
SunModule::SunModule(flecs::world &ecs)
{
ecs.component<Sun>().add(flecs::Singleton);
ecs.set<Sun>({ nullptr, nullptr, nullptr, nullptr });
ecs.system<const EngineData, Sun>("UpdateSetupSun")
.kind(flecs::OnUpdate)
.each([](const EngineData &eng, Sun &sun) {

View File

@@ -237,11 +237,14 @@ public:
float maxH = terrain->getMaxHeight();
int size = terrain->getSize();
float worldSize = terrain->getWorldSize();
if (!created || true) {
if (true) {
btRigidBody *body =
mWorld->addTerrainRigidBody(
group, x, y, 2,
0x7ffffffd & (~16));
OgreAssert(
body,
"Could not create RigidBody");
Ogre::LogManager::getSingleton().logError(
"created rigid body " +
Ogre::StringConverter::toString(
@@ -278,6 +281,11 @@ public:
output.push_back(collider_queue.front());
collider_queue.pop_front();
}
if (collider_queue.empty() &&
!ECS::get<Terrain>().mTerrainReady) {
ECS::get_mut<Terrain>().mTerrainReady = true;
ECS::modified<Terrain>();
}
}
collider_queue = output;
}
@@ -328,7 +336,7 @@ TerrainModule::TerrainModule(flecs::world &ecs)
.each([](const EngineData &eng, const Camera &camera,
const Sun &sun, Terrain &terrain,
TerrainPrivate &priv) {
if (!terrain.mTerrainGroup) {
if (!terrain.mTerrainGroup && sun.mSun && eng.mScnMgr) {
std::cout << "Terrain setup\n";
if (!priv.mDummyPageProvider)
priv.mDummyPageProvider =
@@ -338,6 +346,9 @@ TerrainModule::TerrainModule(flecs::world &ecs)
terrain.mTerrainGlobals =
OGRE_NEW Ogre::TerrainGlobalOptions();
OgreAssert(terrain.mTerrainGlobals,
"Failed to allocate global options");
Ogre::LogManager::getSingleton().setMinLogLevel(
Ogre::LML_TRIVIAL);
@@ -423,47 +434,14 @@ TerrainModule::TerrainModule(flecs::world &ecs)
terrain.mTerrainGroup->freeTemporaryResources();
std::cout << "Terrain setup done\n";
}
bool playerCheck = false;
ECS::get()
.query_builder<CharacterBase, CharacterBody>()
.with<Character>()
.with<Player>()
.each([&playerCheck,
terrain](flecs::entity e,
CharacterBase &ch,
CharacterBody &body) {
if (!body.checkGround)
body.checkGround = true;
if (ch.mBodyNode &&
body.checkGroundResult) {
long x, y;
Ogre::Vector3 pos =
ch.mBodyNode
->getPosition();
terrain.mTerrainGroup
->convertWorldPositionToTerrainSlot(
pos, &x, &y);
if (terrain.mTerrainGroup
->getTerrain(x,
y) &&
terrain.mTerrainGroup
->getTerrain(x, y)
->isLoaded())
playerCheck = true;
}
});
if (playerCheck)
terrain.mTerrainReady = true;
if (priv.mSunUpdate.getMilliseconds() > 1000) {
Ogre::TerrainGlobalOptions::getSingleton()
.setCompositeMapAmbient(
eng.mScnMgr->getAmbientLight());
Ogre::TerrainGlobalOptions::getSingleton()
.setCompositeMapDiffuse(
sun.mSun->getDiffuseColour());
Ogre::TerrainGlobalOptions::getSingleton()
.setLightMapDirection(
sun.mSun->getDerivedDirection());
if (sun.mSun &&
priv.mSunUpdate.getMilliseconds() > 1000) {
terrain.mTerrainGlobals->setCompositeMapAmbient(
eng.mScnMgr->getAmbientLight());
terrain.mTerrainGlobals->setCompositeMapDiffuse(
sun.mSun->getDiffuseColour());
terrain.mTerrainGlobals->setLightMapDirection(
sun.mSun->getDerivedDirection());
std::cout << "sun pitch: "
<< sun.mSunNode->getOrientation()
.getPitch()
@@ -471,5 +449,24 @@ TerrainModule::TerrainModule(flecs::world &ecs)
priv.mSunUpdate.reset();
}
});
ecs.system<const CharacterBase, const Terrain>("UpdateTerrainStatus")
.kind(flecs::OnUpdate)
.without<TerrainReady>()
.each([](const CharacterBase &ch, const Terrain &terrain) {
std::cout << "mTerrainReady: " << terrain.mTerrainReady
<< "\n";
std::cout << "mBodyNode: " << ch.mBodyNode << "\n";
if (ch.mBodyNode && terrain.mTerrainReady) {
long x, y;
Ogre::Vector3 pos = ch.mBodyNode->getPosition();
terrain.mTerrainGroup
->convertWorldPositionToTerrainSlot(
pos, &x, &y);
if (terrain.mTerrainGroup->getTerrain(x, y) &&
terrain.mTerrainGroup->getTerrain(x, y)
->isLoaded())
ECS::get().add<TerrainReady>();
}
});
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -38,14 +38,19 @@ struct WaterSurface {
Ogre::Viewport *mViewports[4];
};
struct WaterBody {
std::vector<btCollisionShape *> mChildShapes;
btCollisionShape *mWaterShape;
btPairCachingGhostObject *mWaterBody;
std::set<btCollisionObject *> mInWater;
std::unordered_map<btCollisionObject *, float> mSurface;
btManifoldArray mManifoldArray;
btVector3 mShapeAabbMin, mShapeAabbMax;
int count;
btActionInterface *action;
bool isInWater(const btCollisionObject *body) const;
};
struct WaterModule {
btPairCachingGhostObject *mGhostObject;
WaterModule(flecs::world &ecs);
void createWaterShape(WaterBody *water);
};
}
#endif

File diff suppressed because it is too large Load Diff

8
tests/CMakeLists.txt Normal file
View File

@@ -0,0 +1,8 @@
project(tests)
find_package(OGRE REQUIRED COMPONENTS Bullet CONFIG)
find_package(Bullet REQUIRED)
add_executable(compound_shapes compound_shapes.cpp)
target_link_libraries(compound_shapes OgreBullet ${BULLET_DYNAMICS_LIBRARY} ${BULLET_COLLISION_LIBRARY} ${BULLET_MATH_LIBRARY})
include_directories(${BULLET_INCLUDE_DIRS})
add_custom_target(tests ALL DEPENDS compound_shapes)

290
tests/compound_shapes.cpp Normal file
View File

@@ -0,0 +1,290 @@
#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";
}
}