Files
ogre-prototype/src/gamedata/WaterModule.cpp
2025-09-07 21:15:00 +03:00

634 lines
21 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.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) {
std::cout << "Water setup\n";
water.mDepthMaterial =
Ogre::MaterialManager::getSingleton()
.getByName("Water/Depth");
OgreAssert(water.mDepthMaterial,
"Water Depth material not found.");
water.mDepthMaterial->load();
water.mDepthTech =
water.mDepthMaterial->getBestTechnique();
OgreAssert(water.mDepthTech,
"Bad material technique");
water.mAbove = false;
water.mInRefTexUpdate = false;
water.mRenderTargetListener.mSurface = &water;
const Ogre::String renderTargetName =
"ReflectionRefractionTexture";
water.mWaterPlane =
Ogre::Plane(Ogre::Vector3::UNIT_Y, 0);
water.mReflectionPlane = Ogre::Plane(
Ogre::Vector3(0.0, 1.0, 0.0),
0.5f /* water height */);
float h = 0.0f;
water.mReflectionClipPlaneAbove = Ogre::Plane(
Ogre::Vector3(0.0, 1.0, 0.0), -h);
water.mReflectionClipPlaneBelow = Ogre::Plane(
Ogre::Vector3(0.0, -1.0, 0.0), h);
water.mRefractionClipPlaneAbove = Ogre::Plane(
Ogre::Vector3(0.0, -1.0, 0.0), h);
water.mRefractionClipPlaneBelow = Ogre::Plane(
Ogre::Vector3(0.0, 1.0, 0.0), -h);
#if 0
if (Ogre::TextureManager::getSingleton()
.resourceExists(renderTargetName))
Ogre::TextureManager::getSingleton()
.remove(renderTargetName);
#endif
Ogre::TexturePtr reflectionTexture =
Ogre::TextureManager::getSingleton().createManual(
renderTargetName,
Ogre::ResourceGroupManager::
DEFAULT_RESOURCE_GROUP_NAME,
Ogre::TEX_TYPE_2D, 512, 512, 0,
Ogre::PF_R8G8B8A8,
Ogre::TU_RENDERTARGET);
Ogre::MaterialPtr debug_mat =
Ogre::MaterialManager::getSingleton()
.getByName("Water/Debug",
"Water");
if (!debug_mat) {
debug_mat =
Ogre::MaterialManager::getSingleton()
.create("Water/Debug",
"Water");
Ogre::Technique *tech =
debug_mat->getTechnique(0);
Ogre::Pass *pass = tech->getPass(0);
pass->setLightingEnabled(false);
pass->setAmbient(
Ogre::ColourValue(1, 1, 1, 1));
pass->setDiffuse(Ogre::ColourValue(
0.0f, 0.2f, 0.5f, 1.0f));
pass->setDepthCheckEnabled(false);
pass->setDepthWriteEnabled(false);
pass->setAlphaRejectFunction(
Ogre::CMPF_ALWAYS_PASS);
pass->setCullingMode(Ogre::CULL_NONE);
pass->setManualCullingMode(
Ogre::MANUAL_CULL_NONE);
Ogre::TextureUnitState *texture_unit =
pass->createTextureUnitState();
texture_unit->setTextureName(
"ReflectionRefractionTexture");
Ogre::Sampler::UVWAddressingMode uvw;
uvw.u = Ogre::TextureUnitState::TAM_MIRROR;
uvw.v = Ogre::TextureUnitState::TAM_MIRROR;
uvw.w = Ogre::TextureUnitState::TAM_MIRROR;
texture_unit->setTextureAddressingMode(
uvw);
texture_unit->setTextureFiltering(
Ogre::FT_MIN, Ogre::FO_LINEAR);
texture_unit->setTextureFiltering(
Ogre::FT_MAG, Ogre::FO_LINEAR);
texture_unit->setTextureFiltering(
Ogre::FT_MIP, Ogre::FO_LINEAR);
}
// create a frosted screen in front of the camera, using our dynamic texture to "thaw" certain areas
Ogre::Entity *ent = eng.mScnMgr->createEntity(
"WaterDebugPlane",
Ogre::SceneManager::PT_PLANE);
ent->setMaterialName("Water/Debug", "Water");
ent->setVisibilityFlags(WATER_MASK);
Ogre::SceneNode *node =
camera.mCameraNode
->createChildSceneNode();
node->setPosition(-150, 60, -400);
node->attachObject(ent);
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);
Ogre::TextureUnitState *texture_unit2 =
pass->createTextureUnitState();
texture_unit2->setTextureName(
"waves2.png");
texture_unit2->setTextureAddressingMode(
uvw);
texture_unit2->setTextureFiltering(
Ogre::FT_MIN, Ogre::FO_LINEAR);
texture_unit2->setTextureFiltering(
Ogre::FT_MAG, Ogre::FO_LINEAR);
texture_unit2->setTextureFiltering(
Ogre::FT_MIP, Ogre::FO_NONE);
#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");
water.mReflectionDepthCamera =
eng.mScnMgr->createCamera(
"ReflectionDepthCamera");
water.mReflectionCameraNode =
camera.mCamera->getParentSceneNode()
->createChildSceneNode(
"ReflectionCameraNode");
water.mReflectionCameraNode->setPosition(
Ogre::Vector3(0, 1.0f, 0));
water.mReflectionCameraNode->attachObject(
water.mReflectionCamera);
water.mReflectionCameraNode->attachObject(
water.mReflectionDepthCamera);
water.mReflectionCamera->setAspectRatio(
camera.mCamera->getAspectRatio());
water.mReflectionCamera->setNearClipDistance(
camera.mCamera->getNearClipDistance());
water.mReflectionCamera->setFarClipDistance(
camera.mCamera->getFarClipDistance());
water.mReflectionCamera
->enableCustomNearClipPlane(
water.mReflectionClipPlaneAbove);
water.mReflectionCamera->enableReflection(
water.mReflectionPlane);
water.mReflectionDepthCamera->setAspectRatio(
camera.mCamera->getAspectRatio());
water.mReflectionDepthCamera->setNearClipDistance(
camera.mCamera->getNearClipDistance());
water.mReflectionDepthCamera->setFarClipDistance(
camera.mCamera->getFarClipDistance());
water.mReflectionDepthCamera
->enableCustomNearClipPlane(
water.mReflectionClipPlaneAbove);
water.mReflectionDepthCamera->enableReflection(
water.mReflectionPlane);
Ogre::Viewport *reflectionViewport =
water.mReflectionTexture->addViewport(
water.mReflectionCamera, 0, 0,
0, 0.5f, 0.5f);
reflectionViewport->setClearEveryFrame(true);
reflectionViewport->setBackgroundColour(
Ogre::ColourValue(0.0, 0.0, 1.0, 1.0));
reflectionViewport->setOverlaysEnabled(false);
reflectionViewport->setSkiesEnabled(true);
reflectionViewport->setAutoUpdated(false);
reflectionViewport->setVisibilityMask(
~WATER_MASK);
water.mViewports[0] = reflectionViewport;
Ogre::Viewport *reflectionDepthViewport =
water.mReflectionTexture->addViewport(
water.mReflectionDepthCamera, 2,
0, 0.5f, 0.5f, 0.5f);
reflectionDepthViewport->setClearEveryFrame(
true);
reflectionDepthViewport->setBackgroundColour(
Ogre::ColourValue(0.0, 0.0, 0.0, 0.0));
reflectionDepthViewport->setOverlaysEnabled(
false);
reflectionDepthViewport->setSkiesEnabled(true);
reflectionDepthViewport->setAutoUpdated(false);
reflectionDepthViewport->setVisibilityMask(
~WATER_MASK);
water.mViewports[2] = reflectionDepthViewport;
water.mRefractionCamera =
eng.mScnMgr->createCamera(
"RefractionCamera");
water.mRefractionDepthCamera =
eng.mScnMgr->createCamera(
"RefractionDepthCamera");
water.mRefractionCameraNode =
camera.mCamera->getParentSceneNode()
->createChildSceneNode(
"RefractionCameraNode");
water.mRefractionCameraNode->attachObject(
water.mRefractionCamera);
water.mRefractionCameraNode->attachObject(
water.mRefractionDepthCamera);
water.mRefractionCamera->setAspectRatio(
camera.mCamera->getAspectRatio());
water.mRefractionCamera->setNearClipDistance(
camera.mCamera->getNearClipDistance());
water.mRefractionCamera->setFarClipDistance(
camera.mCamera->getFarClipDistance());
water.mRefractionCamera
->enableCustomNearClipPlane(
water.mRefractionClipPlaneAbove);
water.mRefractionDepthCamera->setAspectRatio(
camera.mCamera->getAspectRatio());
water.mRefractionDepthCamera->setNearClipDistance(
camera.mCamera->getNearClipDistance());
water.mRefractionDepthCamera->setFarClipDistance(
camera.mCamera->getFarClipDistance());
water.mRefractionDepthCamera
->enableCustomNearClipPlane(
water.mRefractionClipPlaneAbove);
Ogre::Viewport *refractionViewport =
water.mReflectionTexture->addViewport(
water.mRefractionCamera, 1, 0.5,
0, 0.5f, 0.5f);
refractionViewport->setClearEveryFrame(true);
refractionViewport->setBackgroundColour(
Ogre::ColourValue(0.0, 0.5, 1.0, 1.0));
refractionViewport->setOverlaysEnabled(false);
refractionViewport->setSkiesEnabled(false);
refractionViewport->setAutoUpdated(false);
refractionViewport->setVisibilityMask(
~WATER_MASK);
water.mViewports[1] = refractionViewport;
Ogre::Viewport *refractionDepthViewport =
water.mReflectionTexture->addViewport(
water.mRefractionDepthCamera, 3,
0.5, 0.5, 0.5f, 0.5f);
refractionDepthViewport->setClearEveryFrame(
true);
refractionDepthViewport->setBackgroundColour(
Ogre::ColourValue(0.0, 0.0, 0.0, 0.0));
refractionDepthViewport->setOverlaysEnabled(
false);
refractionDepthViewport->setSkiesEnabled(false);
refractionDepthViewport->setAutoUpdated(false);
refractionDepthViewport->setVisibilityMask(
~WATER_MASK);
water.mViewports[3] = refractionDepthViewport;
water.mRenderTargetListener.mInDepth = false;
eng.mScnMgr->getRenderQueue()
->setRenderableListener(
&water.mRenderTargetListener);
std::cout << "Water setup done\n";
}
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.mRenderTargetListener.mInDepth = true;
water.mViewports[2]->update();
water.mViewports[3]->update();
water.mRenderTargetListener.mInDepth = false;
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 *boxShape0 = new btBoxShape(
btVector3(1000, 10, 1000));
btBoxShape *boxShape1 = new btBoxShape(
btVector3(1000, 20, 1000));
btBoxShape *boxShape2 = new btBoxShape(
btVector3(1000, 100, 1000));
btBoxShape *boxShape3 = new btBoxShape(
btVector3(1000, 1000, 1000));
btTransform boxShapeXform0(btQuaternion(),
btVector3(0, -10.2f,
0));
btTransform boxShapeXform1(btQuaternion(),
btVector3(0, -30.2f,
0));
btTransform boxShapeXform2(btQuaternion(),
btVector3(0, -130.2f,
0));
btTransform boxShapeXform3(
btQuaternion(),
btVector3(0, -1120.2f, 0));
shape->addChildShape(boxShapeXform0, boxShape0);
shape->addChildShape(boxShapeXform1, boxShape1);
shape->addChildShape(boxShapeXform2, boxShape2);
shape->addChildShape(boxShapeXform3, boxShape3);
body.mWaterBody = new btPairCachingGhostObject;
body.mWaterBody->setCollisionShape(shape);
body.mWaterBody->setCollisionFlags(
body.mWaterBody->getCollisionFlags() |
btCollisionObject::CF_NO_CONTACT_RESPONSE |
btCollisionObject::CF_STATIC_OBJECT);
body.mWaterBody->setActivationState(
DISABLE_DEACTIVATION);
body.mWaterBody->getWorldTransform().setOrigin(
btVector3(0, 0, 0));
eng.mWorld->attachCollisionObject(
body.mWaterBody, water.mWaterEnt, 16,
0x7fffffff & ~2);
}
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;
if (d.squaredLength() > 10.0f * 10.0f)
body.mWaterBody->getWorldTransform().setOrigin(
Ogre::Bullet::convert(waterBodyPos +
d));
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);
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;
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;
}
bool WaterSurface::RenderTextureListener::renderableQueued(
Ogre::Renderable *rend, Ogre::uint8 groupID, ushort priority,
Ogre::Technique **ppTech, Ogre::RenderQueue *pQueue)
{
if (mInDepth)
*ppTech = mSurface->mDepthTech;
return true;
}
}