497 lines
16 KiB
C++
497 lines
16 KiB
C++
#include <iostream>
|
|
#include <Ogre.h>
|
|
#include <OgreColourValue.h>
|
|
#include <OgreShaderSubRenderState.h>
|
|
#include <OgreShaderGenerator.h>
|
|
#include <OgreRTShaderSystem.h>
|
|
#include "GameData.h"
|
|
#include "Components.h"
|
|
#include "WaterModule.h"
|
|
namespace ECS
|
|
{
|
|
static const uint32_t WATER_MASK = 0xF00;
|
|
WaterModule::WaterModule(flecs::world &ecs)
|
|
{
|
|
ecs.module<WaterModule>();
|
|
ecs.component<WaterSurface>().add(flecs::Singleton);
|
|
ecs.component<WaterBody>().add(flecs::Singleton);
|
|
ecs.set<WaterSurface>({ nullptr, nullptr, nullptr });
|
|
ecs.set<WaterBody>({ nullptr });
|
|
ecs.system<const EngineData, const Camera, WaterSurface>("UpdateWater")
|
|
.kind(flecs::OnUpdate)
|
|
.each([](const EngineData &eng, const Camera &camera,
|
|
WaterSurface &water) {
|
|
float delta = eng.delta;
|
|
if (!water.mWaterEnt || !water.mWaterNode) {
|
|
water.mAbove = false;
|
|
water.mInRefTexUpdate = false;
|
|
water.mRenderTargetListener.mSurface = &water;
|
|
const Ogre::String renderTargetName =
|
|
"ReflectionRefractionTexture";
|
|
water.mWaterPlane =
|
|
Ogre::Plane(Ogre::Vector3::UNIT_Y, 0);
|
|
water.mReflectionPlane = Ogre::Plane(
|
|
Ogre::Vector3(0.0, 1.0, 0.0),
|
|
0.0f /* water height */);
|
|
water.mReflectionClipPlaneAbove = Ogre::Plane(
|
|
Ogre::Vector3(0.0, 1.0, 0.0),
|
|
0.0f /* water height */ - 2.0f);
|
|
water.mReflectionClipPlaneBelow = Ogre::Plane(
|
|
Ogre::Vector3(0.0, -1.0, 0.0),
|
|
-(0.0f /* water height */ + 2.0));
|
|
water.mRefractionClipPlaneAbove = Ogre::Plane(
|
|
Ogre::Vector3(0.0, -1.0, 0.0),
|
|
-(0.0f /* water height */ + 2.0));
|
|
water.mRefractionClipPlaneBelow = Ogre::Plane(
|
|
Ogre::Vector3(0.0, 1.0, 0.0),
|
|
0.0f /* water height */ - 2.0);
|
|
#if 0
|
|
if (Ogre::TextureManager::getSingleton()
|
|
.resourceExists(renderTargetName))
|
|
Ogre::TextureManager::getSingleton()
|
|
.remove(renderTargetName);
|
|
#endif
|
|
Ogre::TexturePtr reflectionTexture =
|
|
Ogre::TextureManager::getSingleton().createManual(
|
|
renderTargetName,
|
|
Ogre::ResourceGroupManager::
|
|
DEFAULT_RESOURCE_GROUP_NAME,
|
|
Ogre::TEX_TYPE_2D, 512, 512, 0,
|
|
Ogre::PF_R8G8B8A8,
|
|
Ogre::TU_RENDERTARGET);
|
|
|
|
water.mReflectionTexture =
|
|
reflectionTexture->getBuffer()
|
|
->getRenderTarget();
|
|
water.mReflectionTexture->setAutoUpdated(false);
|
|
water.mWaterNode =
|
|
eng.mScnMgr->getRootSceneNode()
|
|
->createChildSceneNode("Water");
|
|
Ogre::MaterialPtr mat =
|
|
Ogre::MaterialManager::getSingleton()
|
|
.getByName("Water/Above",
|
|
"Water");
|
|
if (!mat) {
|
|
mat = Ogre::MaterialManager::getSingleton()
|
|
.create("Water/Above",
|
|
"Water");
|
|
Ogre::Technique *tech =
|
|
mat->getTechnique(0);
|
|
Ogre::Pass *pass = tech->getPass(0);
|
|
pass->setLightingEnabled(true);
|
|
pass->setAmbient(
|
|
Ogre::ColourValue(1, 1, 1, 1));
|
|
pass->setDiffuse(Ogre::ColourValue(
|
|
0.0f, 0.2f, 0.5f, 1.0f));
|
|
pass->setCullingMode(Ogre::CULL_NONE);
|
|
pass->setManualCullingMode(
|
|
Ogre::MANUAL_CULL_NONE);
|
|
pass->setVertexProgram(
|
|
"Water/water_vp");
|
|
pass->setFragmentProgram(
|
|
"Water/water_fp");
|
|
#if 0
|
|
Ogre::GpuProgramPtr water_vp =
|
|
Ogre::GpuProgramManager::getSingleton()
|
|
.getByName(
|
|
"Water/water_vp",
|
|
Ogre::RGN_AUTODETECT);
|
|
OgreAssert(water_vp != nullptr,
|
|
"VP failed");
|
|
pass->setGpuProgram(
|
|
Ogre::GPT_VERTEX_PROGRAM,
|
|
water_vp);
|
|
OgreAssert(water_vp->isSupported(),
|
|
"VP not supported");
|
|
Ogre::GpuProgramPtr water_fp =
|
|
Ogre::GpuProgramManager::getSingleton()
|
|
.getByName(
|
|
"Water/water_fp",
|
|
Ogre::RGN_AUTODETECT);
|
|
OgreAssert(water_vp != nullptr,
|
|
"FP failed");
|
|
pass->setGpuProgram(
|
|
Ogre::GPT_FRAGMENT_PROGRAM,
|
|
water_fp);
|
|
OgreAssert(water_fp->isSupported(),
|
|
"FP not supported");
|
|
Ogre::GpuProgramParametersSharedPtr paramsVP =
|
|
water_vp->getDefaultParameters();
|
|
paramsVP->setNamedAutoConstant(
|
|
"world",
|
|
Ogre::GpuProgramParameters::
|
|
ACT_WORLD_MATRIX);
|
|
paramsVP->setNamedAutoConstant(
|
|
"worldViewProj",
|
|
Ogre::GpuProgramParameters::
|
|
ACT_WORLDVIEWPROJ_MATRIX);
|
|
paramsVP->setNamedAutoConstant(
|
|
"textureProjMatrix",
|
|
Ogre::GpuProgramParameters::
|
|
ACT_TEXTURE_WORLDVIEWPROJ_MATRIX);
|
|
paramsVP->setNamedAutoConstant(
|
|
"eyePosition",
|
|
Ogre::GpuProgramParameters::
|
|
ACT_CAMERA_POSITION_OBJECT_SPACE);
|
|
paramsVP->setNamedAutoConstant(
|
|
"normalMatrix",
|
|
Ogre::GpuProgramParameters::
|
|
ACT_NORMAL_MATRIX);
|
|
paramsVP->setNamedAutoConstant(
|
|
"worldView",
|
|
Ogre::GpuProgramParameters::
|
|
ACT_WORLDVIEW_MATRIX);
|
|
paramsVP->setNamedAutoConstant(
|
|
"viewProj",
|
|
Ogre::GpuProgramParameters::
|
|
ACT_VIEWPROJ_MATRIX);
|
|
Ogre::GpuProgramParametersSharedPtr paramsFP =
|
|
water_fp->getDefaultParameters();
|
|
#endif
|
|
Ogre::TextureUnitState *texture_unit =
|
|
pass->createTextureUnitState();
|
|
texture_unit->setTextureName(
|
|
"ReflectionRefractionTexture");
|
|
Ogre::Sampler::UVWAddressingMode uvw;
|
|
uvw.u = Ogre::TextureUnitState::TAM_MIRROR;
|
|
uvw.v = Ogre::TextureUnitState::TAM_MIRROR;
|
|
uvw.w = Ogre::TextureUnitState::TAM_MIRROR;
|
|
texture_unit->setTextureAddressingMode(
|
|
uvw);
|
|
texture_unit->setTextureFiltering(
|
|
Ogre::FT_MIN, Ogre::FO_LINEAR);
|
|
texture_unit->setTextureFiltering(
|
|
Ogre::FT_MAG, Ogre::FO_LINEAR);
|
|
texture_unit->setTextureFiltering(
|
|
Ogre::FT_MIP, Ogre::FO_LINEAR);
|
|
#if 0
|
|
bool success =
|
|
Ogre::RTShader::ShaderGenerator::getSingletonPtr()
|
|
->createShaderBasedTechnique(
|
|
*mat,
|
|
Ogre::MSN_DEFAULT,
|
|
Ogre::MSN_SHADERGEN);
|
|
OgreAssert(
|
|
success,
|
|
"createShaderBasedTechnique");
|
|
Ogre::RTShader::RenderState *renderState =
|
|
Ogre::RTShader::ShaderGenerator::
|
|
getSingletonPtr()
|
|
->getRenderState(
|
|
Ogre::MSN_SHADERGEN,
|
|
*mat,
|
|
0);
|
|
|
|
Ogre::RTShader::SubRenderState *perPixelLightModel =
|
|
Ogre::RTShader::ShaderGenerator::getSingletonPtr()
|
|
->createSubRenderState(
|
|
Ogre::RTShader::
|
|
SRS_PER_PIXEL_LIGHTING);
|
|
renderState->addTemplateSubRenderState(
|
|
perPixelLightModel);
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
mat = Ogre::MaterialManager::getSingleton()
|
|
.getByName("Water/Above");
|
|
mat->load();
|
|
#endif
|
|
mat->setReceiveShadows(false);
|
|
/*
|
|
auto mat2 =
|
|
Ogre::MaterialManager::getSingleton()
|
|
.getByName("Water/Below");
|
|
mat2->load();
|
|
mat2->setReceiveShadows(false);
|
|
*/
|
|
Ogre::Vector3 cameraPosition =
|
|
camera.mCameraNode->getPosition();
|
|
water.mWaterEnt = eng.mScnMgr->createEntity(
|
|
"Ocean", "sea.glb");
|
|
water.mWaterEnt->setVisibilityFlags(WATER_MASK);
|
|
water.mWaterEnt->setCastShadows(true);
|
|
// water.mWaterEnt->setMaterialName("Water/Above");
|
|
water.mWaterEnt->setMaterial(mat);
|
|
water.mWaterNode->attachObject(water.mWaterEnt);
|
|
// mDynWorld->attachCollisionObject(
|
|
// mWaterBody, water_ent, 1, 0x7FFFFFFF);
|
|
water.mReflectionTexture->addListener(
|
|
&water.mRenderTargetListener);
|
|
water.mReflectionCamera =
|
|
eng.mScnMgr->createCamera(
|
|
"ReflectionCamera");
|
|
camera.mCamera->getParentSceneNode()
|
|
->attachObject(water.mReflectionCamera);
|
|
water.mReflectionCamera->setAspectRatio(
|
|
camera.mCamera->getAspectRatio());
|
|
water.mReflectionCamera->setNearClipDistance(
|
|
camera.mCamera->getNearClipDistance());
|
|
water.mReflectionCamera->setFarClipDistance(
|
|
camera.mCamera->getFarClipDistance());
|
|
water.mReflectionCamera
|
|
->enableCustomNearClipPlane(
|
|
water.mReflectionClipPlaneAbove);
|
|
water.mReflectionCamera->enableReflection(
|
|
water.mReflectionPlane);
|
|
|
|
Ogre::Viewport *reflectionViewport =
|
|
water.mReflectionTexture->addViewport(
|
|
water.mReflectionCamera, 0, 0,
|
|
0, 0.5f, 1.0f);
|
|
reflectionViewport->setClearEveryFrame(true);
|
|
reflectionViewport->setBackgroundColour(
|
|
Ogre::ColourValue(0.0, 0.0, 1.0, 1.0));
|
|
reflectionViewport->setOverlaysEnabled(false);
|
|
reflectionViewport->setSkiesEnabled(true);
|
|
reflectionViewport->setAutoUpdated(false);
|
|
water.mViewports[0] = reflectionViewport;
|
|
|
|
water.mRefractionCamera =
|
|
eng.mScnMgr->createCamera(
|
|
"RefractionCamera");
|
|
camera.mCamera->getParentSceneNode()
|
|
->attachObject(water.mRefractionCamera);
|
|
water.mRefractionCamera->setAspectRatio(
|
|
camera.mCamera->getAspectRatio());
|
|
water.mRefractionCamera->setNearClipDistance(
|
|
camera.mCamera->getNearClipDistance());
|
|
water.mRefractionCamera->setFarClipDistance(
|
|
camera.mCamera->getFarClipDistance());
|
|
water.mRefractionCamera
|
|
->enableCustomNearClipPlane(
|
|
water.mRefractionClipPlaneAbove);
|
|
|
|
Ogre::Viewport *refractionViewport =
|
|
water.mReflectionTexture->addViewport(
|
|
water.mRefractionCamera, 1, 0.5,
|
|
0, 0.5f, 1.0f);
|
|
refractionViewport->setClearEveryFrame(true);
|
|
refractionViewport->setBackgroundColour(
|
|
Ogre::ColourValue(0.0, 0.5, 1.0, 1.0));
|
|
refractionViewport->setOverlaysEnabled(false);
|
|
refractionViewport->setSkiesEnabled(false);
|
|
refractionViewport->setAutoUpdated(false);
|
|
water.mViewports[1] = refractionViewport;
|
|
}
|
|
Ogre::Vector3 mCameraPos =
|
|
camera.mCameraNode->_getDerivedPosition();
|
|
Ogre::Vector3 waterPos =
|
|
water.mWaterNode->_getDerivedPosition();
|
|
mCameraPos.y = 0;
|
|
waterPos.y = 0;
|
|
Ogre::Vector3 d = mCameraPos - waterPos;
|
|
// Ogre::Vector3 waterPosition = mCameraPos;
|
|
// mWaterNode->setPosition(waterPosition);
|
|
if (d.squaredLength() < 100.0f * 100.0f)
|
|
water.mWaterNode->translate(d * 3.0f * delta);
|
|
else
|
|
water.mWaterNode->translate(d);
|
|
water.mWaterEnt->setVisible(false);
|
|
water.mViewports[0]->update();
|
|
water.mViewports[1]->update();
|
|
water.mWaterEnt->setVisible(true);
|
|
});
|
|
ecs.system<const EngineData, const WaterSurface, WaterBody>(
|
|
"UpdateWaterBody")
|
|
.kind(flecs::OnUpdate)
|
|
.each([this](const EngineData &eng, const WaterSurface &water,
|
|
WaterBody &body) {
|
|
int i;
|
|
if (!body.mWaterBody) {
|
|
btCompoundShape *shape = new btCompoundShape;
|
|
btBoxShape *boxShape1 = new btBoxShape(
|
|
btVector3(1000, 20, 1000));
|
|
btBoxShape *boxShape2 = new btBoxShape(
|
|
btVector3(1000, 100, 1000));
|
|
btBoxShape *boxShape3 = new btBoxShape(
|
|
btVector3(1000, 1000, 1000));
|
|
btTransform boxShapeXform1(btQuaternion(),
|
|
btVector3(0, -20.2f,
|
|
0));
|
|
btTransform boxShapeXform2(btQuaternion(),
|
|
btVector3(0, -120.2f,
|
|
0));
|
|
btTransform boxShapeXform3(
|
|
btQuaternion(),
|
|
btVector3(0, -1120.2f, 0));
|
|
shape->addChildShape(boxShapeXform1, boxShape1);
|
|
shape->addChildShape(boxShapeXform2, boxShape2);
|
|
shape->addChildShape(boxShapeXform3, boxShape3);
|
|
body.mWaterBody = new btPairCachingGhostObject;
|
|
body.mWaterBody->setCollisionShape(shape);
|
|
body.mWaterBody->setCollisionFlags(
|
|
body.mWaterBody->getCollisionFlags() |
|
|
btCollisionObject::CF_NO_CONTACT_RESPONSE |
|
|
btCollisionObject::CF_STATIC_OBJECT);
|
|
body.mWaterBody->setActivationState(
|
|
DISABLE_DEACTIVATION);
|
|
body.mWaterBody->getWorldTransform().setOrigin(
|
|
btVector3(0, 0, 0));
|
|
eng.mWorld->attachCollisionObject(
|
|
body.mWaterBody, water.mWaterEnt, 16,
|
|
0x7fffffff & ~2);
|
|
}
|
|
Ogre::Vector3 waterPos =
|
|
water.mWaterNode->_getDerivedPosition();
|
|
Ogre::Vector3 waterBodyPos = Ogre::Bullet::convert(
|
|
body.mWaterBody->getWorldTransform()
|
|
.getOrigin());
|
|
waterPos.y = 0;
|
|
waterBodyPos.y = 0;
|
|
Ogre::Vector3 d = waterPos - waterBodyPos;
|
|
d.y = 0;
|
|
std::cout << "aabb: "
|
|
<< Ogre::Bullet::convert(body.mShapeAabbMin)
|
|
<< " "
|
|
<< Ogre::Bullet::convert(body.mShapeAabbMax)
|
|
<< "\n";
|
|
// Ogre::Vector3 waterPosition = mCameraPos;
|
|
// mWaterNode->setPosition(waterPosition);
|
|
if (d.squaredLength() > 10.0f * 10.0f)
|
|
body.mWaterBody->getWorldTransform().setOrigin(
|
|
Ogre::Bullet::convert(waterBodyPos +
|
|
d));
|
|
std::cout
|
|
<< "node: " << water.mWaterNode->getPosition()
|
|
<< " body: "
|
|
<< Ogre::Bullet::convert(
|
|
body.mWaterBody->getWorldTransform()
|
|
.getOrigin())
|
|
<< "\n";
|
|
btCompoundShape *mshape =
|
|
static_cast<btCompoundShape *>(
|
|
body.mWaterBody->getCollisionShape());
|
|
body.mShapeAabbMin =
|
|
body.mWaterBody->getWorldTransform()
|
|
.getOrigin() +
|
|
btVector3(-1000, -1000, -1000);
|
|
body.mShapeAabbMax =
|
|
body.mWaterBody->getWorldTransform()
|
|
.getOrigin() +
|
|
btVector3(1000, -0.2, 1000);
|
|
#if 0
|
|
mshape->getChildShape(0)->getAabb(
|
|
body.mWaterBody->getWorldTransform() *
|
|
mshape->getChildTransform(0),
|
|
body.mShapeAabbMin, body.mShapeAabbMax);
|
|
#endif
|
|
btDispatcher *dispatch =
|
|
eng.mWorld->getBtWorld()->getDispatcher();
|
|
eng.mWorld->getBtWorld()->getBroadphase()->setAabb(
|
|
body.mWaterBody->getBroadphaseHandle(),
|
|
body.mShapeAabbMin, body.mShapeAabbMax,
|
|
eng.mWorld->getBtWorld()->getDispatcher());
|
|
dispatch->dispatchAllCollisionPairs(
|
|
body.mWaterBody->getOverlappingPairCache(),
|
|
eng.mWorld->getBtWorld()->getDispatchInfo(),
|
|
dispatch);
|
|
btBroadphasePairArray &collisionPairs =
|
|
body.mWaterBody->getOverlappingPairCache()
|
|
->getOverlappingPairArray();
|
|
std::set<btCollisionObject *> currentOverlaps;
|
|
std::set<btCollisionObject *>::iterator it;
|
|
const int numObjects = collisionPairs.size();
|
|
for (int i = 0; i < numObjects; i++) {
|
|
body.mManifoldArray.resize(0);
|
|
const btBroadphasePair &collisionPair =
|
|
collisionPairs[i];
|
|
if (collisionPair.m_algorithm)
|
|
collisionPair.m_algorithm
|
|
->getAllContactManifolds(
|
|
body.mManifoldArray);
|
|
for (int j = 0; j < body.mManifoldArray.size();
|
|
j++) {
|
|
btPersistentManifold *manifold =
|
|
body.mManifoldArray[j];
|
|
if (manifold->getNumContacts() == 0)
|
|
continue;
|
|
const btCollisionObject *obj =
|
|
(manifold->getBody0() ==
|
|
body.mWaterBody) ?
|
|
manifold->getBody1() :
|
|
manifold->getBody0();
|
|
btCollisionObject *nobj =
|
|
const_cast<btCollisionObject *>(
|
|
obj);
|
|
if (obj->getCollisionFlags() &
|
|
btCollisionObject::CF_STATIC_OBJECT)
|
|
continue;
|
|
bool ok = false;
|
|
Ogre::Vector3 contactPosA, contactPosB;
|
|
float minDist = 0.0f;
|
|
for (int p = 0;
|
|
p < manifold->getNumContacts();
|
|
p++) {
|
|
const btManifoldPoint &pt =
|
|
manifold->getContactPoint(
|
|
p);
|
|
float dist = pt.getDistance();
|
|
if (dist < minDist) {
|
|
minDist = dist;
|
|
std::cout
|
|
<< " distance: "
|
|
<< dist << "\n";
|
|
contactPosA = Ogre::Bullet::convert(
|
|
pt.getPositionWorldOnA());
|
|
contactPosB = Ogre::Bullet::convert(
|
|
pt.getPositionWorldOnB());
|
|
std::cout
|
|
<< "positionA: "
|
|
<< contactPosA
|
|
<< "\n";
|
|
std::cout
|
|
<< "positionB: "
|
|
<< contactPosA
|
|
<< "\n";
|
|
ok = true;
|
|
}
|
|
}
|
|
if (ok) {
|
|
currentOverlaps.insert(nobj);
|
|
if (body.mInWater.find(nobj) ==
|
|
body.mInWater.end()) {
|
|
/* new body */
|
|
body.mInWater.insert(
|
|
nobj);
|
|
/* calculate proj surface */
|
|
body.mSurface[nobj] =
|
|
100.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (it = body.mInWater.begin();
|
|
it != body.mInWater.end();) {
|
|
btCollisionObject *obj = *it;
|
|
if (currentOverlaps.find(obj) ==
|
|
currentOverlaps.end()) {
|
|
/* remove body */
|
|
it = body.mInWater.erase(it);
|
|
body.mSurface.erase(obj);
|
|
} else
|
|
it++;
|
|
}
|
|
});
|
|
}
|
|
void WaterSurface::RenderTextureListener::preRenderTargetUpdate(
|
|
const Ogre::RenderTargetEvent &evt)
|
|
{
|
|
int i;
|
|
if (evt.source == mSurface->mReflectionTexture) {
|
|
mSurface->mWaterEnt->setVisible(false);
|
|
if (evt.source == mSurface->mReflectionTexture)
|
|
mSurface->mInRefTexUpdate = true;
|
|
} else {
|
|
mSurface->mWaterEnt->setVisible(true);
|
|
mSurface->mInRefTexUpdate = false;
|
|
}
|
|
}
|
|
void WaterSurface::RenderTextureListener::postRenderTargetUpdate(
|
|
const Ogre::RenderTargetEvent &evt)
|
|
{
|
|
int i;
|
|
mSurface->mWaterEnt->setVisible(true);
|
|
mSurface->mInRefTexUpdate = false;
|
|
}
|
|
} |