// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "std3d.h"
#include "nel/misc/vector_2f.h"
#include "nel/misc/vector_h.h"
#include "nel/misc/hierarchical_timer.h"
#include "nel/3d/animation_time.h"
#include "nel/3d/water_model.h"
#include "nel/3d/water_shape.h"
#include "nel/3d/water_pool_manager.h"
#include "nel/3d/water_height_map.h"
#include "nel/3d/dru.h"
#include "nel/3d/scene.h"
#include "nel/3d/driver.h"
#include "nel/3d/render_trav.h"
#include "nel/3d/anim_detail_trav.h"
#include "nel/3d/texture_emboss.h"
#include "nel/3d/texture_bump.h"
#include "nel/3d/water_env_map.h"
using NLMISC::CVector2f;
namespace NL3D {
// for normal rendering
CMaterial CWaterModel::_WaterMat;
// for simple rendering
CMaterial CWaterModel::_SimpleWaterMat;
const uint WATER_MODEL_DEFAULT_NUM_VERTICES = 5000;
NLMISC::CRefPtr CWaterModel::_CurrDrv;
// TMP
volatile bool forceWaterSimpleRender = false;
//=======================================================================
void CWaterModel::setupVertexBuffer(CVertexBuffer &vb, uint numWantedVertices, IDriver *drv)
{
if (!numWantedVertices) return;
if (vb.getNumVertices() == 0 || drv != _CurrDrv) // not setupped yet, or driver changed ?
{
vb.setNumVertices(0);
vb.setName("Water");
vb.setPreferredMemory(CVertexBuffer::AGPPreferred, false);
if (drv->isWaterShaderSupported())
{
vb.setVertexFormat(CVertexBuffer::PositionFlag);
}
else
{
vb.setVertexFormat(CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag);
}
_CurrDrv = drv;
}
uint numVerts = std::max(numWantedVertices, WATER_MODEL_DEFAULT_NUM_VERTICES);
if (numVerts > vb.getNumVertices())
{
const uint vb_INCREASE_SIZE = 1000;
numVerts = vb_INCREASE_SIZE * ((numVerts + (vb_INCREASE_SIZE - 1)) / vb_INCREASE_SIZE); // snap size
vb.setNumVertices((uint32) numVerts);
}
}
//=======================================================================
CWaterModel::CWaterModel()
{
setOpacity(false);
setTransparency(true);
setOrderingLayer(1);
// RenderFilter: We are a SegRemanece
_RenderFilterType= UScene::FilterWater;
_Prev = NULL;
_Next = NULL;
_MatrixUpdateDate = 0;
}
//=======================================================================
CWaterModel::~CWaterModel()
{
CScene *scene = getOwnerScene();
if (scene && scene->getWaterCallback())
{
nlassert(Shape);
CWaterShape *ws = NLMISC::safe_cast((IShape *) Shape);
scene->getWaterCallback()->waterSurfaceRemoved(ws->getUseSceneWaterEnvMap(0) || ws->getUseSceneWaterEnvMap(1));
}
// should be already unlinked, but security
unlink();
}
//=======================================================================
void CWaterModel::registerBasic()
{
CScene::registerModel(WaterModelClassId, TransformShapeId, CWaterModel::creator);
}
//=======================================================================
ITrack* CWaterModel::getDefaultTrack (uint valueId)
{
nlassert(Shape);
CWaterShape *ws = NLMISC::safe_cast((IShape *) Shape);
switch (valueId)
{
case PosValue: return ws->getDefaultPos(); break;
case ScaleValue: return ws->getDefaultScale(); break;
case RotQuatValue: return ws->getDefaultRotQuat(); break;
default: // delegate to parent
return CTransformShape::getDefaultTrack(valueId);
break;
}
}
//=======================================================================
uint32 CWaterModel::getWaterHeightMapID() const
{
CWaterShape *ws = NLMISC::safe_cast((IShape *) Shape);
return ws->_WaterPoolID;
}
//=======================================================================
float CWaterModel::getHeightFactor() const
{
CWaterShape *ws = NLMISC::safe_cast((IShape *) Shape);
return ws->_WaveHeightFactor;
}
//=======================================================================
float CWaterModel::getHeight(const CVector2f &pos)
{
CWaterShape *ws = NLMISC::safe_cast((IShape *) Shape);
CWaterHeightMap &whm = GetWaterPoolManager().getPoolByID(ws->_WaterPoolID);
const float height = whm.getHeight(pos);
return height * ws->_WaveHeightFactor + this->getPos().z;
}
//=======================================================================
float CWaterModel::getAttenuatedHeight(const CVector2f &pos, const NLMISC::CVector &viewer)
{
CWaterShape *ws = NLMISC::safe_cast((IShape *) Shape);
CWaterHeightMap &whm = GetWaterPoolManager().getPoolByID(ws->_WaterPoolID);
const float maxDist = whm.getUnitSize() * (whm.getSize() >> 1);
const NLMISC::CVector planePos(pos.x, pos.y, this->getMatrix().getPos().z);
const float userDist = (planePos - viewer).norm();
if (userDist > maxDist)
{
return this->getMatrix().getPos().z;
}
else
{
const float height = whm.getHeight(pos);
return ws->_WaveHeightFactor * height * (1.f - userDist / maxDist) + this->getMatrix().getPos().z;
}
}
//=======================================================================
// perform a bilinear on 4 values
// 0---1
// | |
// 3---2
/*
static float inline BilinFilter(float v0, float v1, float v2, float v3, float u, float v)
{
float g = v * v3 + (1.f - v) * v0;
float h = v * v2 + (1.f - v) * v1;
return u * h + (1.f - u) * g;
}
*/
//=======================================================================
/// store a value in a water vertex buffer, and increment the pointer
/*
static void inline FillWaterVB(uint8 *&vbPointer, float x, float y, float z, float nx, float ny)
{
* (float *) vbPointer = x;
((float *) vbPointer)[1] = y;
((float *) vbPointer)[2] = z;
*((float *) (vbPointer + 3 * sizeof(float))) = nx;
*((float *) (vbPointer + 4 * sizeof(float))) = ny;
vbPointer += 5 * sizeof(float);
}
*/
// ***************************************************************************************************************
/*
#ifdef NL_OS_WINDOWS
__forceinline
#endif
static void SetupWaterVertex( sint qLeft,
sint qRight,
sint qUp,
sint qDown,
sint qSubLeft,
sint qSubDown,
const NLMISC::CVector &inter,
float invWaterRatio,
sint doubleWaterHeightMapSize,
CWaterHeightMap &whm,
uint8 *&vbPointer,
float offsetX,
float offsetY
)
{
const float wXf = invWaterRatio * (inter.x + offsetX);
const float wYf = invWaterRatio * (inter.y + offsetY);
sint wx = (sint) floorf(wXf);
sint wy = (sint) floorf(wYf);
if (!
(wx >= qLeft && wx < qRight && wy < qUp && wy >= qDown)
)
{
// no perturbation is visible
FillWaterVB(vbPointer, inter.x, inter.y, 0, 0, 0);
}
else
{
// filter height and gradient at the given point
const sint stride = doubleWaterHeightMapSize;
const uint xm = (uint) (wx - qSubLeft);
const uint ym = (uint) (wy - qSubDown);
const sint offset = xm + stride * ym;
const float *ptWater = whm.getPointer() + offset;
float deltaU = wXf - wx;
float deltaV = wYf - wy;
//nlassert(deltaU >= 0.f && deltaU <= 1.f && deltaV >= 0.f && deltaV <= 1.f);
const float *ptWaterPrev = whm.getPrevPointer() + offset;
float g0x, g1x, g2x, g3x; // x gradient for current
float g0xp, g1xp, g2xp, g3xp;
float gradCurrX, gradCurrY;
float g0y, g1y, g2y, g3y; // y gradient for previous map
float g0yp, g1yp, g2yp, g3yp;
float gradPrevX, gradPrevY;
/// curr gradient
g0x = ptWater[ 1] - ptWater[ - 1];
g1x = ptWater[ 2] - ptWater[ 0 ];
g2x = ptWater[ 2 + stride] - ptWater[ stride];
g3x = ptWater[ 1 + stride] - ptWater[ - 1 + stride];
gradCurrX = BilinFilter(g0x, g1x, g2x, g3x, deltaU, deltaV);
g0y = ptWater[ stride] - ptWater[ - stride];
g1y = ptWater[ stride + 1] - ptWater[ - stride + 1];
g2y = ptWater[ (stride << 1) + 1] - ptWater[ 1];
g3y = ptWater[ (stride << 1)] - ptWater[0];
gradCurrY = BilinFilter(g0y, g1y, g2y, g3y, deltaU, deltaV);
/// prev gradient
g0xp = ptWaterPrev[ 1] - ptWaterPrev[ - 1];
g1xp = ptWaterPrev[ 2] - ptWaterPrev[ 0 ];
g2xp = ptWaterPrev[ 2 + stride] - ptWaterPrev[ + stride];
g3xp = ptWaterPrev[ 1 + stride] - ptWaterPrev[ - 1 + stride];
gradPrevX = BilinFilter(g0xp, g1xp, g2xp, g3xp, deltaU, deltaV);
g0yp = ptWaterPrev[ stride] - ptWaterPrev[ - stride];
g1yp = ptWaterPrev[ stride + 1] - ptWaterPrev[ - stride + 1];
g2yp = ptWaterPrev[ (stride << 1) + 1] - ptWaterPrev[ 1 ];
g3yp = ptWaterPrev[ (stride << 1)] - ptWaterPrev[ 0 ];
gradPrevY = BilinFilter(g0yp, g1yp, g2yp, g3yp, deltaU, deltaV);
/// current height
float h = BilinFilter(ptWater[ 0 ], ptWater[ + 1], ptWater[ 1 + stride], ptWater[stride], deltaU, deltaV);
/// previous height
float hPrev = BilinFilter(ptWaterPrev[ 0 ], ptWaterPrev[ 1], ptWaterPrev[ 1 + stride], ptWaterPrev[stride], deltaU, deltaV);
float timeRatio = whm.getBufferRatio();
FillWaterVB(vbPointer, inter.x, inter.y, timeRatio * h + (1.f - timeRatio) * hPrev,
4.5f * (timeRatio * gradCurrX + (1.f - timeRatio) * gradPrevX),
4.5f * (timeRatio * gradCurrY + (1.f - timeRatio) * gradPrevY)
);
//NLMISC::CVector2f *ptGrad = whm.getGradPointer() + offset;
}
}
*/
// *****************************************************************************************************
/*
static void DrawPoly2D(CVertexBuffer &vb, IDriver *drv, const NLMISC::CMatrix &mat, const NLMISC::CPolygon &p)
{
uint k;
{
CVertexBufferReadWrite vba;
vb.lock (vba);
for (k = 0; k < p.Vertices.size(); ++k)
{
NLMISC::CVector tPos = mat * NLMISC::CVector(p.Vertices[k].x, p.Vertices[k].y, 0);
vba.setValueFloat3Ex (WATER_VB_POS, k, tPos.x, tPos.y, tPos.z);
vba.setValueFloat2Ex (WATER_VB_DX, k, 0, 0);
}
}
static CIndexBuffer ib;
ib.setNumIndexes(3 * p.Vertices.size());
{
CIndexBufferReadWrite ibaWrite;
ib.lock (ibaWrite);
uint32 *ptr = ibaWrite.getPtr();
for (k = 0; k < p.Vertices.size() - 2; ++k)
{
ptr[ k * 3 ] = 0;
ptr[ k * 3 + 1 ] = k + 1;
ptr[ k * 3 + 2 ] = k + 2;
}
}
drv->activeIndexBuffer(ib);
drv->renderSimpleTriangles(0, p.Vertices.size() - 2);
}
*/
// ***************************************************************************************************************
/*
void CWaterModel::traverseRender()
{
H_AUTO( NL3D_Water_Render );
CRenderTrav &renderTrav = getOwnerScene()->getRenderTrav();
IDriver *drv = renderTrav.getDriver();
#ifndef FORCE_SIMPLE_WATER_RENDER
if (!drv->isWaterShaderSupported())
#endif
{
doSimpleRender(drv);
return;
}
CWaterShape *shape = NLMISC::safe_cast((IShape *) Shape);
if (shape->_GridSizeTouched)
{
shape->setupVertexBuffer();
}
// inverted object world matrix
//NLMISC::CMatrix invObjMat = getWorldMatrix().inverted();
// viewer pos in world space
const NLMISC::CVector &obsPos = renderTrav.CamPos;
// camera matrix in world space
const NLMISC::CMatrix &camMat = renderTrav.CamMatrix;
// view matrix (inverted cam matrix)
const NLMISC::CMatrix &viewMat = renderTrav.ViewMatrix;
// compute the camera matrix such as there is no rotation around the y axis
NLMISC::CMatrix camMatUp;
ComputeUpMatrix(camMat.getJ(), camMatUp, camMat);
camMatUp.setPos(camMat.getPos());
const NLMISC::CMatrix matViewUp = camMatUp.inverted();
// plane z pos in world
const float zHeight = getWorldMatrix().getPos().z;
const sint numStepX = CWaterShape::getScreenXGridSize();
const sint numStepY = CWaterShape::getScreenYGridSize();
const float invNumStepX = 1.f / numStepX;
const float invNumStepY = 1.f / numStepY;
const uint rotBorderSize = (shape->_MaxGridSize + (shape->_XGridBorder << 1) - numStepX) >> 1;
const sint isAbove = obsPos.z > zHeight ? 1 : 0;
#ifdef NO_WATER_TESSEL
const float transitionDist = renderTrav.Near * 0.99f;
#else
const float transitionDist = shape->_TransitionRatio * renderTrav.Far;
#endif
NLMISC::CMatrix modelMat;
modelMat.setPos(NLMISC::CVector(obsPos.x, obsPos.y, zHeight));
drv->setupModelMatrix(modelMat);
//==================//
// material setup //
//==================//
CWaterHeightMap &whm = GetWaterPoolManager().getPoolByID(shape->_WaterPoolID);
setupMaterialNVertexShader(drv, shape, obsPos, isAbove > 0, whm.getUnitSize() * (whm.getSize() >> 1), zHeight);
drv->setupMaterial(CWaterModel::_WaterMat);
sint numPass = drv->beginMaterialMultiPass();
nlassert(numPass == 1); // for now, we assume water is always rendered in a single pass !
drv->setupMaterialPass(0);
//setAttenuationFactor(drv, false, obsPos, camMat.getJ(), farDist);
//disableAttenuation(drv);
//================================//
// Vertex buffer setup //
//================================//
drv->activeVertexBuffer(shape->_VB);
//================================//
// tesselated part of the poly //
//================================//
if (_ClippedPoly.Vertices.size())
{
//======================================//
// Polygon projection on the near plane //
//======================================//
static NLMISC::CPolygon2D projPoly; // projected poly
projPoly.Vertices.resize(_ClippedPoly.Vertices.size());
const float Near = renderTrav.Near;
const float xFactor = numStepX * Near / (renderTrav.Right - renderTrav.Left);
const float xOffset = numStepX * (-renderTrav.Left / (renderTrav.Right - renderTrav.Left)) + 0.5f;
const float yFactor = numStepY * Near / (renderTrav.Bottom - renderTrav.Top);
const float yOffset = numStepY * (-renderTrav.Top / (renderTrav.Bottom - renderTrav.Top)) - 0.5f * isAbove;
const NLMISC::CMatrix projMat = matViewUp * getWorldMatrix();
uint k;
for (k = 0; k < _ClippedPoly.Vertices.size(); ++k)
{
// project points in the view
NLMISC::CVector t = projMat * _ClippedPoly.Vertices[k];
float invY = 1.f / t.y;
projPoly.Vertices[k].set(xFactor * t.x * invY + xOffset, yFactor * t.z * invY + yOffset);
}
//=============================================//
// compute borders of poly at a low resolution //
//=============================================//
NLMISC::CPolygon2D::TRasterVect rasters;
sint startY;
projPoly.computeBorders(rasters, startY);
if (rasters.size())
{
//===========================//
// perform Water animation //
//===========================//
const float WaterRatio = whm.getUnitSize();
const float invWaterRatio = 1.f / WaterRatio;
const uint WaterHeightMapSize = whm.getSize();
const uint doubleWaterHeightMapSize = (WaterHeightMapSize << 1);
sint64 idate = getOwnerScene()->getHrcTrav().CurrentDate;
if (idate != whm.Date)
{
whm.setUserPos((sint) (obsPos.x * invWaterRatio) - (WaterHeightMapSize >> 1),
(sint) (obsPos.y * invWaterRatio) - (WaterHeightMapSize >> 1)
);
nlassert(getOwnerScene()); // this object should have been created from a CWaterShape!
whm.animate((float) (getOwnerScene()->getEllapsedTime()));
whm.Date = idate;
}
//float startDate = (float) (1000.f * NLMISC::CTime::ticksToSecond(NLMISC::CTime::getPerformanceTime()));
//=====================================//
// compute heightmap useful area //
//=====================================//
// We don't store a heighmap for a complete Water area
// we just consider the height of Water columns that are near the observer
//
// Compute a quad in Water height field space that contains the useful heights
// This helps us to decide whether we should do a lookup in the height map
sint mapPosX, mapPosY;
/// get the pos used in the height map (may not be the same than our current pos, has it is taken in account
/// every 'PropagationTime' second
whm.getUserPos(mapPosX, mapPosY);
const uint mapBorder = 3;
const sint qRight = (sint) mapPosX + WaterHeightMapSize - mapBorder;
sint qLeft = (sint) mapPosX;
const sint qUp = (sint) mapPosY + WaterHeightMapSize - mapBorder;
sint qDown = (sint) mapPosY;
/// Compute the origin of the area of Water covered by the height map. We use this to converted from object space to 2d map space
const sint qSubLeft = qLeft - (uint) qLeft % WaterHeightMapSize;
const sint qSubDown = qDown - (uint) qDown % WaterHeightMapSize;
qLeft += mapBorder;
qDown += mapBorder;
//==============================================//
// setup rays to be traced, and their increment //
//==============================================//
// compute camera rays in world space
NLMISC::CVector currHV = renderTrav.Left * camMatUp.getI() + renderTrav.Near * camMatUp.getJ() + renderTrav.Top * camMatUp.getK(); // current border vector, incremented at each line
NLMISC::CVector currV; // current ray vector
NLMISC::CVector xStep = (renderTrav.Right - renderTrav.Left) * invNumStepX * camMatUp.getI(); // xStep for the ray vector
NLMISC::CVector yStep = (renderTrav.Bottom - renderTrav.Top) * invNumStepY * camMatUp.getK(); // yStep for the ray vector
//===============================================//
// perform display //
//===============================================//
// scale currHV at the top of the poly
currHV += (startY - 0.5f * isAbove) * yStep;
// current index buffer used. We swap each time a row has been drawn
CIndexBuffer *currIB = &CWaterShape::_IBUpDown, *otherIB = &CWaterShape::_IBDownUp;
sint vIndex = 0; // index in vertices
// current raster position
sint oldStartX, oldEndX, realStartX, realEndX;
//float invNearWidth = numStepX / (renderTrav.Right - renderTrav.Left);
//nlinfo("size = %d, maxSize = ", rasters.size(), numStepY);
const uint wqHeight = rasters.size();
if (wqHeight)
{
// denominator of the intersection equation
const float denom = - obsPos.z + zHeight;
// test the upper raster
// if it is above the horizon, we modify it to reach the correct location
const float horizonEpsilon = 10E-4f; // we must be a little below the horizon
// distance from the viewer along the traced ray
float t;
NLMISC::CPolygon2D::TRasterVect::const_iterator it = rasters.begin();
for (uint l = 0; l <= wqHeight; ++l)
{
//nlinfo("start = %d, end = %d", it->first, it->second);
const sint startX = it->first;
const sint endX = (it->second + 1);
nlassert(startX >= - (sint) rotBorderSize);
nlassert(endX <= (sint) (numStepX + rotBorderSize));
if (l != 0)
{
realStartX = std::min(startX, oldStartX);
realEndX = std::max(endX, oldEndX);
}
else
{
realStartX = startX;
realEndX = endX;
}
// current view vector
currV = currHV + (realStartX - 0.5f) * xStep;
if (l == 0)
{
if (isAbove)
{
// test whether the first row is out of horizon.
// if this is the case, we make a correction
if (denom * currV.z <= 0)
{
// correct for the first line only by adding a y offset
currV += yStep * ((denom > 0 ? horizonEpsilon : - horizonEpsilon) - currV.z) / yStep.z;
}
// now, for the transition, check whether the first raster does not go over the transition dist
t = denom / currV.z;
const float VJ = camMat.getJ() * currV;
if ( t * VJ > transitionDist)
{
float delta = (1.f / yStep.z) * ( denom * VJ / transitionDist - currV.z);
// correct the first line to reach that position
currV += delta * yStep;
}
}
}
{
CVertexBufferReadWrite vba;
shape->_VB.lock (vba);
uint8 *vbPointer = (uint8 *) vba.getVertexCoordPointer() + shape->_VB.getVertexSize() * (vIndex + realStartX + rotBorderSize);
for (sint k = realStartX; k <= realEndX; ++k)
{
t = denom / currV.z;
// compute intersection with plane
NLMISC::CVector inter = t * currV;
inter.z += obsPos.z;
SetupWaterVertex(qLeft, qRight, qUp, qDown, qSubLeft, qSubDown, inter, invWaterRatio, doubleWaterHeightMapSize, whm, vbPointer, obsPos.x, obsPos.y);
currV += xStep;
}
}
if (l != 0) // 2 line of the ib done ?
{
sint count = oldEndX - oldStartX;
if (count > 0)
{
drv->activeIndexBuffer(*currIB);
drv->renderSimpleTriangles((oldStartX + rotBorderSize) * 6, 2 * count );
}
}
oldStartX = startX;
oldEndX = endX;
currHV += yStep;
vIndex = (numStepX + 2 * rotBorderSize + 1) - vIndex; // swap first row and second row
std::swap(currIB, otherIB);
if (l < (wqHeight - 1))
{
++it;
}
else
{
if (!isAbove)
{
// last line
// test whether we are out of horizon
if (denom * currHV.z <= 0)
{
// correct for the first line only by adding a y offset
currHV += yStep * ((denom > 0 ? horizonEpsilon : - horizonEpsilon) - currHV.z) / yStep.z;
}
// now, for the transition, check whether the first raster does not go over the transition dist
t = denom / currHV.z;
const float VJ = camMat.getJ() * currHV;
if ( t * VJ > transitionDist)
{
float delta = (1.f / yStep.z) * ( denom * VJ / transitionDist - currHV.z);
// correct the first line to reach that position
currHV += delta * yStep;
}
}
}
}
}
//nlinfo("display: %f ms", (float) (1000.f * NLMISC::CTime::ticksToSecond(NLMISC::CTime::getPerformanceTime()) - startDate));
}
}
//=========================================//
// display end poly //
//=========================================//
if (_EndClippedPoly.Vertices.size() != 0)
{
CMatrix xform = _WorldMatrix;
xform.movePos(NLMISC::CVector(- obsPos.x, - obsPos.y, _WorldMatrix.getPos().z));
DrawPoly2D(shape->_VB, drv, xform, _EndClippedPoly);
}
drv->endMaterialMultiPass();
drv->activeVertexProgram(NULL);
}
*/
// ***********************
// Water MATERIAL SETUP //
// ***********************
/*
void CWaterModel::setupMaterialNVertexShader(IDriver *drv, CWaterShape *shape, const NLMISC::CVector &obsPos, bool above, float maxDist, float zHeight)
{
static bool matSetupped = false;
if (!matSetupped)
{
_WaterMat.setLighting(false);
_WaterMat.setDoubleSided(true);
_WaterMat.setColor(NLMISC::CRGBA::White);
_WaterMat.setBlend(true);
_WaterMat.setSrcBlend(CMaterial::srcalpha);
_WaterMat.setDstBlend(CMaterial::invsrcalpha);
_WaterMat.setZWrite(true);
_WaterMat.setShader(CMaterial::Water);
}
const uint cstOffset = 4; // 4 places for the matrix
NLMISC::CVectorH cst[13];
//=========================//
// setup Water material //
//=========================//
CWaterModel::_WaterMat.setTexture(0, shape->_BumpMap[0]);
CWaterModel::_WaterMat.setTexture(1, shape->_BumpMap[1]);
CWaterModel::_WaterMat.setTexture(3, shape->_ColorMap);
CScene *scene = getOwnerScene();
if (!above && shape->_EnvMap[1])
{
if (shape->_UsesSceneWaterEnvMap[1])
{
if (scene->getWaterEnvMap())
{
CWaterModel::_WaterMat.setTexture(2, scene->getWaterEnvMap()->getEnvMap2D());
}
else
{
CWaterModel::_WaterMat.setTexture(2, shape->_EnvMap[1]);
}
}
else
{
CWaterModel::_WaterMat.setTexture(2, shape->_EnvMap[1]);
}
}
else
{
if (shape->_UsesSceneWaterEnvMap[0])
{
if (scene->getWaterEnvMap())
{
CWaterModel::_WaterMat.setTexture(2, scene->getWaterEnvMap()->getEnvMap2D());
}
else
{
CWaterModel::_WaterMat.setTexture(2, shape->_EnvMap[0]);
}
}
else
{
CWaterModel::_WaterMat.setTexture(2, shape->_EnvMap[0]);
}
}
shape->envMapUpdate();
const uint alphaMapStage = 3;
if (shape->_ColorMap)
{
//WaterMat.setTexture(alphaMapStage, shape->_ColorMap);
//if (shape->_ColorMap->supportSharing()) nlinfo(shape->_ColorMap->getShareName().c_str());
// setup 2x3 matrix for lookup in diffuse map
updateDiffuseMapMatrix();
cst[13 - cstOffset].set(_ColorMapMatColumn0.x, _ColorMapMatColumn1.x, 0, _ColorMapMatColumn0.x * obsPos.x + _ColorMapMatColumn1.x * obsPos.y + _ColorMapMatPos.x);
cst[14 - cstOffset].set(_ColorMapMatColumn0.y, _ColorMapMatColumn1.y, 0, _ColorMapMatColumn0.y * obsPos.x + _ColorMapMatColumn1.y * obsPos.y + _ColorMapMatPos.y);
}
else
{
cst[13 - cstOffset].set(0, 0, 0, 0);
cst[14 - cstOffset].set(0, 0, 0, 0);
}
cst[16 - cstOffset].set(0.1f, 0.1f, 0.1f, 0.1f); // used to avoid imprecision when performing a RSQ to get distance from the origin
// cst[16 - cstOffset].set(0.0f, 0.0f, 0.0f, 0.0f); // used to avoid imprecision when performing a RSQ to get distance from the origin
cst[5 - cstOffset].set(0.f, 0.f, 0.f, 0.f); // claping negative values to 0
// slope of attenuation of normal / height with distance
const float invMaxDist = shape->_WaveHeightFactor / maxDist;
cst[6 - cstOffset].set(invMaxDist, shape->_WaveHeightFactor, 0, 0);
/// set matrix
drv->setConstantMatrix(0, IDriver::ModelViewProjection, IDriver::Identity);
drv->setConstantFog(18);
// retrieve current time
float date = 0.001f * (NLMISC::CTime::getLocalTime() & 0xffffff); // must keep some precision.
// set bumpmaps pos
cst[9 - cstOffset].set(fmodf(obsPos.x * shape->_HeightMapScale[0].x, 1.f) + fmodf(date * shape->_HeightMapSpeed[0].x, 1.f), fmodf(shape->_HeightMapScale[0].y * obsPos.y, 1.f) + fmodf(date * shape->_HeightMapSpeed[0].y, 1.f), 0.f, 1.f); // bump map 0 offset
cst[10 - cstOffset].set(shape->_HeightMapScale[0].x, shape->_HeightMapScale[0].y, 0, 0); // bump map 0 scale
cst[11 - cstOffset].set(fmodf(shape->_HeightMapScale[1].x * obsPos.x, 1.f) + fmodf(date * shape->_HeightMapSpeed[1].x, 1.f), fmodf(shape->_HeightMapScale[1].y * obsPos.y, 1.f) + fmodf(date * shape->_HeightMapSpeed[1].y, 1.f), 0.f, 1.f); // bump map 1 offset
cst[12 - cstOffset].set(shape->_HeightMapScale[1].x, shape->_HeightMapScale[1].y, 0, 0); // bump map 1 scale
cst[4 - cstOffset].set(1.f, 1.f, 1.f, 1.f); // use with min man, and to get the 1 constant
cst[7 - cstOffset].set(0, 0, obsPos.z - zHeight, 1.f);
cst[8 - cstOffset].set(0.5f, 0.5f, 0.f, 1.f); // used to scale reflected ray into the envmap
/// set all our constants in one call
drv->setConstant(4, sizeof(cst) / sizeof(cst[0]), (float *) &cst[0]);
shape->initVertexProgram();
bool result;
//if (useBumpedVersion)
//{
// if (!useEMBM)
// {
// result = shape->getColorMap() ? drv->activeVertexProgram((shape->_VertexProgramBump2Diffuse).get())
// : drv->activeVertexProgram((shape->_VertexProgramBump2).get());
// }
// else
// {
// result = shape->getColorMap() ? drv->activeVertexProgram((shape->_VertexProgramBump1Diffuse).get())
// : drv->activeVertexProgram((shape->_VertexProgramBump1).get());
// }
//}
//else
//{
// result = shape->getColorMap() ? drv->activeVertexProgram((shape->_VertexProgramNoBumpDiffuse).get())
// : drv->activeVertexProgram((shape->_VertexProgramNoBump).get());
//}
//result = shape->getColorMap() ? drv->activeVertexProgram((shape->_VertexProgramBump2Diffuse).get())
// : drv->activeVertexProgram((shape->_VertexProgramBump2).get());
//
//if (!result) nlwarning("no vertex program setuped");
}
*/
void CWaterModel::setupMaterialNVertexShader(IDriver *drv, CWaterShape *shape, const NLMISC::CVector &obsPos, bool above, float zHeight)
{
static bool matSetupped = false;
if (!matSetupped)
{
_WaterMat.setLighting(false);
_WaterMat.setDoubleSided(true);
_WaterMat.setColor(NLMISC::CRGBA::White);
_WaterMat.setBlend(true);
_WaterMat.setSrcBlend(CMaterial::srcalpha);
_WaterMat.setDstBlend(CMaterial::invsrcalpha);
_WaterMat.setZWrite(true);
_WaterMat.setShader(CMaterial::Water);
}
const uint cstOffset = 5; // 4 places for the matrix
NLMISC::CVectorH cst[13];
//=========================//
// setup Water material //
//=========================//
CWaterModel::_WaterMat.setTexture(0, shape->_BumpMap[0]);
CWaterModel::_WaterMat.setTexture(1, shape->_BumpMap[1]);
CWaterModel::_WaterMat.setTexture(3, shape->_ColorMap);
CScene *scene = getOwnerScene();
if (!above && shape->_EnvMap[1])
{
if (shape->_UsesSceneWaterEnvMap[1])
{
if (scene->getWaterEnvMap())
{
CWaterModel::_WaterMat.setTexture(2, scene->getWaterEnvMap()->getEnvMap2D());
}
else
{
CWaterModel::_WaterMat.setTexture(2, shape->_EnvMap[1]);
}
}
else
{
CWaterModel::_WaterMat.setTexture(2, shape->_EnvMap[1]);
}
}
else
{
if (shape->_UsesSceneWaterEnvMap[0])
{
if (scene->getWaterEnvMap())
{
CWaterModel::_WaterMat.setTexture(2, scene->getWaterEnvMap()->getEnvMap2D());
}
else
{
CWaterModel::_WaterMat.setTexture(2, shape->_EnvMap[0]);
}
}
else
{
CWaterModel::_WaterMat.setTexture(2, shape->_EnvMap[0]);
}
}
shape->envMapUpdate();
if (shape->_ColorMap)
{
// setup 2x3 matrix for lookup in diffuse map
updateDiffuseMapMatrix();
cst[11].set(_ColorMapMatColumn0.x, _ColorMapMatColumn1.x, 0, _ColorMapMatColumn0.x * obsPos.x + _ColorMapMatColumn1.x * obsPos.y + _ColorMapMatPos.x);
cst[12].set(_ColorMapMatColumn0.y, _ColorMapMatColumn1.y, 0, _ColorMapMatColumn0.y * obsPos.x + _ColorMapMatColumn1.y * obsPos.y + _ColorMapMatPos.y);
}
/// set matrix
drv->setConstantMatrix(0, IDriver::ModelViewProjection, IDriver::Identity);
// retrieve current time
double date = scene->getCurrentTime();
// set bumpmaps pos
cst[6].set(fmodf(obsPos.x * shape->_HeightMapScale[0].x, 1.f) + (float) fmod(date * shape->_HeightMapSpeed[0].x, 1), fmodf(shape->_HeightMapScale[0].y * obsPos.y, 1.f) + (float) fmod(date * shape->_HeightMapSpeed[0].y, 1), 0.f, 1.f); // bump map 0 offset
cst[5].set(shape->_HeightMapScale[0].x, shape->_HeightMapScale[0].y, 0, 0); // bump map 0 scale
cst[8].set(fmodf(shape->_HeightMapScale[1].x * obsPos.x, 1.f) + (float) fmod(date * shape->_HeightMapSpeed[1].x, 1), fmodf(shape->_HeightMapScale[1].y * obsPos.y, 1.f) + (float) fmod(date * shape->_HeightMapSpeed[1].y, 1), 0.f, 1.f); // bump map 1 offset
cst[7].set(shape->_HeightMapScale[1].x, shape->_HeightMapScale[1].y, 0, 0); // bump map 1 scale
cst[9].set(0, 0, obsPos.z - zHeight, 1.f);
cst[10].set(0.5f, 0.5f, 0.f, 1.f); // used to scale reflected ray into the envmap
/// set all our constants in one call
drv->setConstant(cstOffset, sizeof(cst) / sizeof(cst[0]) - cstOffset, (float *) &cst[cstOffset]);
shape->initVertexProgram();
drv->activeVertexProgram(shape->_ColorMap ? CWaterShape::_VertexProgramNoWaveDiffuse.get() : CWaterShape::_VertexProgramNoWave.get());
drv->setConstantFog(4);
}
//================================================
void CWaterModel::setupSimpleRender(CWaterShape *shape, const NLMISC::CVector &obsPos, bool above)
{
// rendering of water when no vertex / pixel shaders are available
static bool init = false;
if (!init)
{
// setup the material, no special shader is used here
_SimpleWaterMat.setLighting(false);
_SimpleWaterMat.setDoubleSided(true);
_SimpleWaterMat.setColor(NLMISC::CRGBA::White);
_SimpleWaterMat.setBlend(true);
_SimpleWaterMat.setSrcBlend(CMaterial::srcalpha);
_SimpleWaterMat.setDstBlend(CMaterial::invsrcalpha);
_SimpleWaterMat.setZWrite(true);
_SimpleWaterMat.setShader(CMaterial::Normal);
// stage 0
_SimpleWaterMat.texEnvOpRGB(0, CMaterial::Replace);
_SimpleWaterMat.texEnvOpAlpha(0, CMaterial::Replace);
_SimpleWaterMat.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
_SimpleWaterMat.texEnvArg0Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha);
// stage 1
_SimpleWaterMat.texEnvOpRGB(1, CMaterial::Modulate);
_SimpleWaterMat.texEnvOpAlpha(1, CMaterial::Modulate);
_SimpleWaterMat.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
_SimpleWaterMat.texEnvArg0Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha);
_SimpleWaterMat.texEnvArg1RGB(0, CMaterial::Previous, CMaterial::SrcColor);
_SimpleWaterMat.texEnvArg1Alpha(0, CMaterial::Previous, CMaterial::SrcAlpha);
init = true;
}
// envmap is always present and is in stage 0
CScene *scene = getOwnerScene();
if (!above && shape->_EnvMap[1])
{
if (shape->_UsesSceneWaterEnvMap[1])
{
if (scene->getWaterEnvMap())
{
_SimpleWaterMat.setTexture(0, scene->getWaterEnvMap()->getEnvMap2D());
}
else
{
_SimpleWaterMat.setTexture(0, shape->_EnvMap[1]);
}
}
else
{
_SimpleWaterMat.setTexture(0, shape->_EnvMap[1]);
}
}
else
{
if (shape->_UsesSceneWaterEnvMap[0])
{
if (scene->getWaterEnvMap())
{
_SimpleWaterMat.setTexture(0, scene->getWaterEnvMap()->getEnvMap2D());
}
else
{
_SimpleWaterMat.setTexture(0, shape->_EnvMap[0]);
}
}
else
{
_SimpleWaterMat.setTexture(0, shape->_EnvMap[0]);
}
}
//
if (shape->_ColorMap == NULL)
{
// version with no color map
if (!_EmbossTexture)
{
_EmbossTexture = new CTextureEmboss;
_EmbossTexture->setSlopeFactor(4.f);
}
if (shape->_BumpMap[1] && shape->_BumpMap[1]->isBumpMap())
{
CTextureBump *bm = static_cast((ITexture *) shape->_BumpMap[1]);
if (bm->getHeightMap())
{
_EmbossTexture->setHeightMap(bm->getHeightMap());
}
}
_SimpleWaterMat.setTexture(1, _EmbossTexture);
_SimpleWaterMat.setTexCoordGen(1, true);
_SimpleWaterMat.setTexCoordGenMode(1, CMaterial::TexCoordGenObjectSpace);
double date = scene->getCurrentTime();
CMatrix texMat;
texMat.scale(CVector(shape->_HeightMapScale[1].x, shape->_HeightMapScale[1].y, 1.f));
texMat.setPos(CVector(fmodf(shape->_HeightMapScale[1].x * obsPos.x, 1.f) + (float) fmod(date * shape->_HeightMapSpeed[1].x, 1),
fmodf(shape->_HeightMapScale[1].y * obsPos.y, 1.f) + (float) fmod(date * shape->_HeightMapSpeed[1].y, 1),
1.f)
);
_SimpleWaterMat.enableUserTexMat(1, true);
_SimpleWaterMat.setUserTexMat(1, texMat);
}
else
{
updateDiffuseMapMatrix();
// version with a color map : it remplace the emboss texture
_SimpleWaterMat.setTexture(1, shape->_ColorMap);
_SimpleWaterMat.setTexCoordGen(1, true);
_SimpleWaterMat.setTexCoordGenMode(1, CMaterial::TexCoordGenObjectSpace);
CMatrix texMat;
/*
float mat[16] =
{
_ColorMapMatColumn0.x, _ColorMapMatColumn1.x, 0, _ColorMapMatColumn0.x * obsPos.x + _ColorMapMatColumn1.x * obsPos.y + _ColorMapMatPos.x},
_ColorMapMatColumn0.y, _ColorMapMatColumn1.y, 0, _ColorMapMatColumn0.y * obsPos.x + _ColorMapMatColumn1.y * obsPos.y + _ColorMapMatPos.y,
0.f, 0.f, 1.f, 0.f,
0.f, 0.f, 0.f, 1.f
}
*/
float mat[16] =
{
_ColorMapMatColumn0.x, _ColorMapMatColumn0.y, 0.f, 0.f,
_ColorMapMatColumn1.x, _ColorMapMatColumn1.y, 0.f, 0.f,
0.f, 0.f, 1.f, 0.f,
_ColorMapMatColumn0.x * obsPos.x + _ColorMapMatColumn1.x * obsPos.y + _ColorMapMatPos.x, _ColorMapMatColumn0.y * obsPos.x + _ColorMapMatColumn1.y * obsPos.y + _ColorMapMatPos.y, 0.f, 1.f
};
texMat.set(mat);
_SimpleWaterMat.enableUserTexMat(1, true);
_SimpleWaterMat.setUserTexMat(1, texMat);
}
}
//================================================
void CWaterModel::computeClippedPoly()
{
CWaterShape *shape = NLMISC::safe_cast((IShape *) Shape);
const std::vector &worldPyramid = getOwnerScene()->getClipTrav().WorldFrustumPyramid;
_ClippedPoly.Vertices.resize(shape->_Poly.Vertices.size());
uint k;
for (k = 0; k < shape->_Poly.Vertices.size(); ++k)
{
_ClippedPoly.Vertices[k].set(shape->_Poly.Vertices[k].x,
shape->_Poly.Vertices[k].y,
0.f
);
}
/*
NLMISC::CPlane plvect[6];
const NLMISC::CMatrix &viewMat = clipTrav.ViewMatrix;
const sint numStepX = CWaterShape::getScreenXGridSize();
const sint numStepY = CWaterShape::getScreenYGridSize();
// Build the view pyramid. We need to rebuild it because we use a wider one to avoid holes on the border of the screen due to water animation
float centerX = 0.5f * (clipTrav.Right + clipTrav.Left);
const float fRight = centerX + (clipTrav.Right - centerX) * (-(float) CWaterShape::_XGridBorder + (float) numStepX) / numStepX;
const float fLeft = centerX + (clipTrav.Left - centerX) * (-(float) CWaterShape::_XGridBorder + (float) numStepX) / numStepX;
float centerY = 0.5f * (clipTrav.Bottom + clipTrav.Top);
const float fTop = centerY + (clipTrav.Top - centerY) * (-(float) CWaterShape::_YGridBorder + (float) numStepY) / numStepY;
const float fBottom = centerY + (clipTrav.Bottom - centerY) * (-(float) CWaterShape::_YGridBorder + (float) numStepY) / numStepY;
// build pyramid corners
const float nearDist = clipTrav.Near;
const float farDist = clipTrav.Far;
//
const NLMISC::CVector pfoc(0,0,0);
const NLMISC::CVector lb( fLeft, nearDist, fBottom );
const NLMISC::CVector lt( fLeft, nearDist, fTop );
const NLMISC::CVector rb( fRight, nearDist, fBottom );
const NLMISC::CVector rt(fRight, nearDist, fTop );
const NLMISC::CVector lbfarDist(fLeft, farDist, fBottom);
const NLMISC::CVector ltfarDist(fLeft, farDist, fTop );
const NLMISC::CVector rtfarDist(fRight , farDist, fTop );
//
plvect[0].make(lt, lb, rt); // near plane
plvect[1].make(lbfarDist, ltfarDist, rtfarDist); // far plane
plvect[2].make(pfoc, lt, lb);
plvect[3].make(pfoc, rt, lt);
plvect[4].make(pfoc, rb, rt);
plvect[5].make(pfoc, lb, rb);
const NLMISC::CMatrix pyramidMat = viewMat * getWorldMatrix();
for (k = 0; k < worldPyramid.size(); ++k)
{
plvect[k] = plvect[k] * pyramidMat; // put the plane in object space
}
_ClippedPoly.clip(plvect, 6);
*/
static std::vector tp;
tp.resize(worldPyramid.size());
for(uint k = 0; k < tp.size(); ++k)
{
tp[k] = worldPyramid[k] * getWorldMatrix();
}
_ClippedPoly.clip(tp);
}
// ***********************************************************************************************************
void CWaterModel::unlink()
{
if (!_Prev)
{
nlassert(!_Next);
return;
}
if (_Next)
{
_Next->_Prev = _Prev;
}
*_Prev = _Next;
_Next = NULL;
_Prev = NULL;
}
// ***********************************************************************************************************
void CWaterModel::link()
{
nlassert(_Next == NULL);
CScene *scene = getOwnerScene();
nlassert(scene);
CRenderTrav &rt = scene->getRenderTrav();
_Prev = &rt._FirstWaterModel;
_Next = rt._FirstWaterModel;
if (_Next)
{
_Next->_Prev = &_Next;
}
rt._FirstWaterModel = this;
}
// ***********************************************************************************************************
uint CWaterModel::getNumWantedVertices()
{
H_AUTO( NL3D_Water_Render );
nlassert(!_ClippedPoly.Vertices.empty());
//
CRenderTrav &renderTrav = getOwnerScene()->getRenderTrav();
if (!renderTrav.Perspective || forceWaterSimpleRender) return 0;
// viewer pos in world space
const NLMISC::CVector &obsPos = renderTrav.CamPos;
// view matrix (inverted cam matrix)
const NLMISC::CMatrix &viewMat = renderTrav.ViewMatrix;
// plane z pos in world
const float zHeight = getWorldMatrix().getPos().z;
const sint numStepX = CWaterShape::getScreenXGridSize();
const sint numStepY = CWaterShape::getScreenYGridSize();
NLMISC::CMatrix modelMat;
modelMat.setPos(NLMISC::CVector(obsPos.x, obsPos.y, zHeight));
static NLMISC::CPolygon2D projPoly; // projected poly
projPoly.Vertices.resize(_ClippedPoly.Vertices.size());
// factor to project to grid units
const float xFactor = numStepX * renderTrav.Near / (renderTrav.Right - renderTrav.Left);
const float yFactor = numStepY * renderTrav.Near / (renderTrav.Top - renderTrav.Bottom);
// project poly on near plane
const NLMISC::CMatrix &projMat = viewMat * getWorldMatrix();
uint k;
for (k = 0; k < _ClippedPoly.Vertices.size(); ++k)
{
// project points in the view
NLMISC::CVector t = projMat * _ClippedPoly.Vertices[k];
float invY = 1.f / t.y;
projPoly.Vertices[k].set(xFactor * t.x * invY, yFactor * t.z * invY);
}
// compute grid cells that are entirely inside
projPoly.computeInnerBorders(_Inside, _MinYInside);
// compute grid cells that are touched
static NLMISC::CPolygon2D::TRasterVect border;
sint minYBorder;
projPoly.computeOuterBorders(border, minYBorder);
// border - inside -> gives grid cells that must be clipped to fit the shape boundaries
// Make sure that rasters array for inside has the same size that raster array for borders (by inserting NULL rasters)
sint height = (sint)border.size();
if (_Inside.empty())
{
_MinYInside = minYBorder;
}
sint bottomGap = (sint)(border.size() - _Inside.size());
_Inside.resize(height);
nlassert(minYBorder == _MinYInside);
nlassert(bottomGap >= 0);
if (bottomGap)
{
for(sint y = height - bottomGap; y < height; ++y)
{
nlassert (y >= 0 && y < (sint)_Inside.size());
_Inside[y].first = border[y].first;
_Inside[y].second = border[y].first - 1; // insert null raster
}
}
//
for(sint y = 0; y < height - bottomGap; ++y)
{
if (_Inside[y].first > _Inside[y].second)
{
nlassert (y >= 0 && y < (sint)_Inside.size());
_Inside[y].first = border[y].first;
_Inside[y].second = border[y].first - 1;
}
else if (border[y].first > border[y].second)
{
nlassert (y >= 0 && y < (sint)_Inside.size());
border[y].first = _Inside[y].first;
border[y].second = _Inside[y].first - 1;
}
}
// compute clip planes
static std::vector clipPlanes;
const CVector2f *prevVert = &projPoly.Vertices.back();
const CVector2f *currVert = &projPoly.Vertices.front();
uint numVerts = (uint)projPoly.Vertices.size();
bool ccw = projPoly.isCCWOriented();
clipPlanes.resize(numVerts);
for(uint k = 0; k < numVerts; ++k)
{
NLMISC::CVector v0;
NLMISC::CVector v1;
NLMISC::CVector v2;
v0.set(prevVert->x, prevVert->y, 0.f);
v1.set(currVert->x, currVert->y, 0.f);
v2.set(prevVert->x, prevVert->y, (*currVert - *prevVert).norm());
clipPlanes[k].make(v0, v1, v2);
if (!ccw)
{
clipPlanes[k].invert();
}
prevVert = currVert;
++ currVert;
}
// compute clipped tris
_ClippedTriNumVerts.clear();
_ClippedTris.clear();
static NLMISC::CPolygon clipPoly;
uint totalNumVertices = 0;
// compute number of vertices for whole grid cells
for(sint k = 0; k < (sint) border.size(); ++k)
{
// left clipped blocks
for (sint x = border[k].first; x < _Inside[k].first; ++x)
{
clipPoly.Vertices.resize(4);
clipPoly.Vertices[0].set((float) x, (float) (k + _MinYInside), 0.f);
clipPoly.Vertices[1].set((float) (x + 1), (float) (k + _MinYInside), 0.f);
clipPoly.Vertices[2].set((float) (x + 1), (float) (k + _MinYInside + 1), 0.f);
clipPoly.Vertices[3].set((float) x, (float) (k + _MinYInside + 1), 0.f);
clipPoly.clip(clipPlanes);
if (!clipPoly.Vertices.empty())
{
// backup result (will be unprojected later)
_ClippedTriNumVerts.push_back((uint)clipPoly.Vertices.size());
uint prevSize = (uint)_ClippedTris.size();
_ClippedTris.resize(_ClippedTris.size() + clipPoly.Vertices.size());
std::copy(clipPoly.Vertices.begin(), clipPoly.Vertices.end(), _ClippedTris.begin() + prevSize); // append to packed list
totalNumVertices += ((uint)clipPoly.Vertices.size() - 2) * 3;
}
}
// middle block, are not clipped, but count the number of wanted vertices
if (_Inside[k].first <= _Inside[k].second)
{
totalNumVertices += 6 * (_Inside[k].second - _Inside[k].first + 1);
}
// right clipped blocks
for (sint x = _Inside[k].second + 1; x <= border[k].second; ++x)
{
clipPoly.Vertices.resize(4);
clipPoly.Vertices[0].set((float) x, (float) (k + _MinYInside), 0.f);
clipPoly.Vertices[1].set((float) (x + 1), (float) (k + _MinYInside), 0.f);
clipPoly.Vertices[2].set((float) (x + 1), (float) (k + _MinYInside + 1), 0.f);
clipPoly.Vertices[3].set((float) x, (float) (k + _MinYInside + 1), 0.f);
clipPoly.clip(clipPlanes);
if (!clipPoly.Vertices.empty())
{
// backup result (will be unprojected later)
_ClippedTriNumVerts.push_back((uint)clipPoly.Vertices.size());
uint prevSize = (uint)_ClippedTris.size();
_ClippedTris.resize(_ClippedTris.size() + clipPoly.Vertices.size());
std::copy(clipPoly.Vertices.begin(), clipPoly.Vertices.end(), _ClippedTris.begin() + prevSize); // append to packed list
totalNumVertices += ((uint)clipPoly.Vertices.size() - 2) * 3;
}
}
}
return totalNumVertices;
}
// ***********************************************************************************************************
uint CWaterModel::fillVB(void *datas, uint startTri, IDriver &drv)
{
H_AUTO( NL3D_Water_Render );
if (drv.isWaterShaderSupported())
{
return fillVBHard(datas, startTri);
}
else
{
return fillVBSoft(datas, startTri);
}
}
static const double WATER_WAVE_SPEED = 1.7;
static const double WATER_WAVE_SCALE = 0.05;
static const double WATER_WAVE_FREQ = 0.3;
static const float WATER_WAVE_ATTEN = 0.2f;
// compute single water vertex in software mode
static
#ifndef NL_DEBUG
inline
#endif
void computeWaterVertexSoft(float px, float py, CVector &pos, CVector2f &envMapTexCoord, const CVector &camI, const CVector &camJ, const CVector &camK, float denom, double date, const CVector &camPos)
{
CVector d = px * camI + py * camK + camJ;
//nlassert(d.z > 0.f);
float intersectionDist = denom / d.z;
pos.x = intersectionDist * d.x;
pos.y = intersectionDist * d.y;
pos.z = 0.f;
//
CVector R(- pos.x,
- pos.y,
- denom
);
float dist = R.norm();
if (dist)
{
R /= dist;
}
envMapTexCoord.set(- 0.5f * R.x + 0.5f, - 0.5f * R.y + 0.5f);
if (dist)
{
float invDist = 1.f / (WATER_WAVE_ATTEN * dist);
if (invDist > 1.f) invDist = 1.f;
// TODO : optimize cos if need (for now there are not much call per frame ...)
envMapTexCoord.x += (float) (invDist * WATER_WAVE_SCALE * (float) cos(date + WATER_WAVE_FREQ * (camPos.x + pos.x)));
}
}
// ***********************************************************************************************************
uint CWaterModel::fillVBSoft(void *datas, uint startTri)
{
_StartTri = (uint32) startTri;
CRenderTrav &renderTrav = getOwnerScene()->getRenderTrav();
const NLMISC::CMatrix &camMat = renderTrav.CamMatrix;
const sint numStepX = CWaterShape::getScreenXGridSize();
const sint numStepY = CWaterShape::getScreenYGridSize();
CVector camI = camMat.getI() * (1.f / numStepX) * (renderTrav.Right - renderTrav.Left) / renderTrav.Near;
CVector camJ = camMat.getJ();
CVector camK = camMat.getK() * (1.f / numStepY) * (renderTrav.Top - renderTrav.Bottom) / renderTrav.Near;
float obsZ = camMat.getPos().z;
float denom = getWorldMatrix().getPos().z - obsZ;
uint8 *dest = (uint8 *) datas + startTri * 3 * WATER_VERTEX_SOFT_SIZE;
/*NLMISC::CVector eye = renderTrav.CamPos;
eye.z -= getWorldMatrix().getPos().z; */
NLMISC::CVector eye(0.f, 0.f, - denom);
CVector R;
CScene *scene = getOwnerScene();
double date = WATER_WAVE_SPEED * scene->getCurrentTime();
if (!_ClippedTriNumVerts.empty())
{
const CVector2f *currVert = &_ClippedTris.front();
static std::vector unprojectedTriSoft;
static std::vector envMap;
for(uint k = 0; k < _ClippedTriNumVerts.size(); ++k)
{
unprojectedTriSoft.resize(_ClippedTriNumVerts[k]);
envMap.resize(_ClippedTriNumVerts[k]);
uint numVerts = _ClippedTriNumVerts[k];
for(uint l = 0; l < _ClippedTriNumVerts[k]; ++l)
{
computeWaterVertexSoft(currVert->x, currVert->y, unprojectedTriSoft[l], envMap[l], camI, camJ, camK, denom, date, camMat.getPos());
++ currVert;
}
for(uint l = 0; l < numVerts - 2; ++l)
{
*(CVector *) dest = unprojectedTriSoft[0];
dest += sizeof(float[3]);
*(CVector2f *) dest = envMap[0];
dest += sizeof(float[2]);
*(CVector *) dest = unprojectedTriSoft[l + 1];
dest += sizeof(float[3]);
*(CVector2f *) dest = envMap[l + 1];
dest += sizeof(float[2]);
*(CVector *) dest = unprojectedTriSoft[l + 2];
dest += sizeof(float[3]);
*(CVector2f *) dest = envMap[l + 2];
dest += sizeof(float[2]);
}
}
}
// TODO : optimize if needed
for(sint k = 0; k < (sint) _Inside.size(); ++k)
{
sint y = k + _MinYInside;
CVector proj[4];
CVector2f envMap[4];
if (_Inside[k].first <= _Inside[k].second)
{
// middle block, are not clipped, but count the number of wanted vertices
for(sint x = _Inside[k].first; x <= _Inside[k].second; ++x)
{
computeWaterVertexSoft((float) x, (float) y, proj[0], envMap[0], camI, camJ, camK, denom, date, camMat.getPos());
computeWaterVertexSoft((float)(x + 1), (float) y, proj[1], envMap[1], camI, camJ, camK, denom, date, camMat.getPos());
computeWaterVertexSoft((float) (x + 1), (float) (y + 1), proj[2], envMap[2], camI, camJ, camK, denom, date, camMat.getPos());
computeWaterVertexSoft((float) x, (float) (y + 1), proj[3], envMap[3], camI, camJ, camK, denom, date, camMat.getPos());
//
*(CVector *) dest = proj[0];
dest += sizeof(float[3]);
*(CVector2f *) dest = envMap[0];
dest += sizeof(float[2]);
*(CVector *) dest = proj[2];
dest += sizeof(float[3]);
*(CVector2f *) dest = envMap[2];
dest += sizeof(float[2]);
*(CVector *) dest = proj[1];
dest += sizeof(float[3]);
*(CVector2f *) dest = envMap[1];
dest += sizeof(float[2]);
*(CVector *) dest = proj[0];
dest += sizeof(float[3]);
*(CVector2f *) dest = envMap[0];
dest += sizeof(float[2]);
*(CVector *) dest = proj[3];
dest += sizeof(float[3]);
*(CVector2f *) dest = envMap[3];
dest += sizeof(float[2]);
*(CVector *) dest = proj[2];
dest += sizeof(float[3]);
*(CVector2f *) dest = envMap[2];
dest += sizeof(float[2]);
}
}
}
nlassert((dest - (uint8 * ) datas) % (3 * WATER_VERTEX_SOFT_SIZE) == 0);
uint endTri = (uint)(dest - (uint8 * ) datas) / (3 * WATER_VERTEX_SOFT_SIZE);
_NumTris = endTri - _StartTri;
return endTri;
}
// compute single water vertex for hardware render
static
#ifndef NL_DEBUG
inline
#endif
void computeWaterVertexHard(float px, float py, CVector &pos, const CVector &camI, const CVector &camJ, const CVector &camK, float denom)
{
CVector d = px * camI + py * camK + camJ;
float intersectionDist = denom / d.z;
pos.x = intersectionDist * d.x;
pos.y = intersectionDist * d.y;
pos.z = 0.f;
}
// ***********************************************************************************************************
uint CWaterModel::fillVBHard(void *datas, uint startTri)
{
_StartTri = (uint32) startTri;
CRenderTrav &renderTrav = getOwnerScene()->getRenderTrav();
const NLMISC::CMatrix &camMat = renderTrav.CamMatrix;
const sint numStepX = CWaterShape::getScreenXGridSize();
const sint numStepY = CWaterShape::getScreenYGridSize();
CVector camI = camMat.getI() * (1.f / numStepX) * (renderTrav.Right - renderTrav.Left) / renderTrav.Near;
CVector camJ = camMat.getJ();
CVector camK = camMat.getK() * (1.f / numStepY) * (renderTrav.Top - renderTrav.Bottom) / renderTrav.Near;
float obsZ = camMat.getPos().z;
float denom = getWorldMatrix().getPos().z - obsZ;
uint8 *dest = (uint8 *) datas + startTri * WATER_VERTEX_HARD_SIZE * 3;
if (!_ClippedTriNumVerts.empty())
{
const CVector2f *currVert = &_ClippedTris.front();
static std::vector unprojectedTri;
for(uint k = 0; k < _ClippedTriNumVerts.size(); ++k)
{
unprojectedTri.resize(_ClippedTriNumVerts[k]);
uint numVerts = _ClippedTriNumVerts[k];
for(uint l = 0; l < _ClippedTriNumVerts[k]; ++l)
{
computeWaterVertexHard(currVert->x, currVert->y, unprojectedTri[l], camI, camJ, camK, denom);
++ currVert;
}
for(uint l = 0; l < numVerts - 2; ++l)
{
*(CVector *) dest = unprojectedTri[0];
dest += WATER_VERTEX_HARD_SIZE;
*(CVector *) dest = unprojectedTri[l + 1];
dest += WATER_VERTEX_HARD_SIZE;
*(CVector *) dest = unprojectedTri[l + 2];
dest += WATER_VERTEX_HARD_SIZE;
}
}
}
// TODO : optimize if needed
for(sint k = 0; k < (sint) _Inside.size(); ++k)
{
sint y = k + _MinYInside;
CVector proj[4];
if (_Inside[k].first <= _Inside[k].second)
{
// middle block, are not clipped, but count the number of wanted vertices
for(sint x = _Inside[k].first; x <= _Inside[k].second; ++x)
{
computeWaterVertexHard((float) x, (float) y, proj[0], camI, camJ, camK, denom);
computeWaterVertexHard((float) (x + 1), (float) y, proj[1], camI, camJ, camK, denom);
computeWaterVertexHard((float) (x + 1), (float) (y + 1), proj[2], camI, camJ, camK, denom);
computeWaterVertexHard((float) x, (float) (y + 1), proj[3], camI, camJ, camK, denom);
//
*(CVector *) dest = proj[0];
dest += WATER_VERTEX_HARD_SIZE;
*(CVector *) dest = proj[2];
dest += WATER_VERTEX_HARD_SIZE;
*(CVector *) dest = proj[1];
dest += WATER_VERTEX_HARD_SIZE;
*(CVector *) dest = proj[0];
dest += WATER_VERTEX_HARD_SIZE;
*(CVector *) dest = proj[3];
dest += WATER_VERTEX_HARD_SIZE;
*(CVector *) dest = proj[2];
dest += WATER_VERTEX_HARD_SIZE;
}
}
}
nlassert((dest - (uint8 * ) datas) % (3 * WATER_VERTEX_HARD_SIZE) == 0);
uint endTri = (uint)(dest - (uint8 * ) datas) / (3 * WATER_VERTEX_HARD_SIZE);
_NumTris = endTri - _StartTri;
return endTri;
}
// ***************************************************************************************************************
void CWaterModel::traverseRender()
{
H_AUTO( NL3D_Water_Render );
CRenderTrav &renderTrav = getOwnerScene()->getRenderTrav();
IDriver *drv = renderTrav.getDriver();
CWaterShape *shape = NLMISC::safe_cast((IShape *) Shape);
const NLMISC::CVector &obsPos = renderTrav.CamPos;
const float zHeight = getWorldMatrix().getPos().z;
if (!renderTrav.Perspective || forceWaterSimpleRender)
{
// not supported, simple uniform render
drv->setupModelMatrix(getWorldMatrix());
static CMaterial waterMat;
static bool initDone = false;
if (!initDone)
{
waterMat.initUnlit();
waterMat.setBlend(true);
waterMat.setSrcBlend(CMaterial::srcalpha);
waterMat.setDstBlend(CMaterial::invsrcalpha);
waterMat.setBlend(true);
waterMat.setDoubleSided(true);
waterMat.setLighting(false);
}
waterMat.setColor(shape->computeEnvMapMeanColor());
static std::vector tris;
const NLMISC::CPolygon2D &poly = shape->getShape();
tris.clear();
for(sint k = 0; k < (sint) poly.Vertices.size() - 2; ++k)
{
NLMISC::CTriangleUV truv;
truv.V0.set(poly.Vertices[0].x, poly.Vertices[0].y, 0.f);
truv.V1.set(poly.Vertices[k + 1].x, poly.Vertices[k + 1].y, 0.f);
truv.V2.set(poly.Vertices[k + 2].x, poly.Vertices[k + 2].y, 0.f);
tris.push_back(truv);
}
CDRU::drawTrianglesUnlit(tris, waterMat, *drv);
}
else
{
NLMISC::CMatrix modelMat;
modelMat.setPos(NLMISC::CVector(obsPos.x, obsPos.y, zHeight));
drv->setupModelMatrix(modelMat);
bool isAbove = obsPos.z > getWorldMatrix().getPos().z;
CVertexBuffer &vb = renderTrav.Scene->getWaterVB();
if (drv->isWaterShaderSupported())
{
setupMaterialNVertexShader(drv, shape, obsPos, isAbove, zHeight);
nlassert(vb.getNumVertices() > 0);
drv->activeVertexBuffer(vb);
drv->renderRawTriangles(CWaterModel::_WaterMat, _StartTri, _NumTris);
drv->activeVertexProgram(NULL);
}
else
{
setupSimpleRender(shape, obsPos, isAbove);
drv->activeVertexBuffer(vb);
drv->activeVertexProgram(NULL);
drv->renderRawTriangles(CWaterModel::_SimpleWaterMat, _StartTri, _NumTris);
}
}
}
// ***********************************************************************************************************
bool CWaterModel::clip()
{
H_AUTO( NL3D_Water_Render );
CRenderTrav &renderTrav= getOwnerScene()->getRenderTrav();
if (renderTrav.CamPos.z == getWorldMatrix().getPos().z) return false;
if(Shape)
{
computeClippedPoly();
if (_ClippedPoly.Vertices.empty()) return false;
// unlink from water model list
unlink();
// link into water model list
link();
return true;
}
else
return false;
}
/*
// struct used to build vertices for the simple shader
struct CSimpleVertexInfo
{
NLMISC::CVector XFormPos;
NLMISC::CUV UV;
};
*/
// ***********************************************************************************************************
/*
void CWaterModel::doSimpleRender(IDriver *drv)
{
if (_ClippedPoly.Vertices.empty()) return;
// rendering of water when no vertex / pixel shaders are available
CWaterShape *shape = NLMISC::safe_cast((IShape *) Shape);
CRenderTrav &renderTrav = getOwnerScene()->getRenderTrav();
static bool init = false;
if (!init)
{
// setup the material, no special shader is used here
_SimpleWaterMat.setLighting(false);
_SimpleWaterMat.setDoubleSided(true);
_SimpleWaterMat.setColor(NLMISC::CRGBA::White);
_SimpleWaterMat.setBlend(true);
_SimpleWaterMat.setSrcBlend(CMaterial::srcalpha);
_SimpleWaterMat.setDstBlend(CMaterial::invsrcalpha);
_SimpleWaterMat.setZWrite(true);
_SimpleWaterMat.setShader(CMaterial::Normal);
// stage 0
_SimpleWaterMat.texEnvOpRGB(0, CMaterial::Replace);
_SimpleWaterMat.texEnvOpAlpha(0, CMaterial::Replace);
_SimpleWaterMat.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
_SimpleWaterMat.texEnvArg0Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha);
// stage 1
_SimpleWaterMat.texEnvOpRGB(1, CMaterial::Modulate);
_SimpleWaterMat.texEnvOpAlpha(1, CMaterial::Modulate);
_SimpleWaterMat.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
_SimpleWaterMat.texEnvArg0Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha);
_SimpleWaterMat.texEnvArg1RGB(0, CMaterial::Previous, CMaterial::SrcColor);
_SimpleWaterMat.texEnvArg1Alpha(0, CMaterial::Previous, CMaterial::SrcAlpha);
// setup the vb : one position & two tex coords
_SimpleRenderVB.setVertexFormat(CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag | CVertexBuffer::TexCoord1Flag);
init = true;
}
const NLMISC::CMatrix &worldMatrix = getWorldMatrix();
const NLMISC::CVector &obsPos = renderTrav.CamPos;
// setup the material
bool isAbove = obsPos.z > worldMatrix.getPos().z;
// envmap is always present and is in stage 0
CScene *scene = getOwnerScene();
if (!isAbove && shape->_EnvMap[1])
{
if (shape->_UsesSceneWaterEnvMap[1])
{
if (scene->getWaterEnvMap())
{
_SimpleWaterMat.setTexture(0, scene->getWaterEnvMap()->getEnvMap2D());
}
else
{
_SimpleWaterMat.setTexture(0, shape->_EnvMap[1]);
}
}
else
{
_SimpleWaterMat.setTexture(0, shape->_EnvMap[1]);
}
}
else
{
if (shape->_UsesSceneWaterEnvMap[0])
{
if (scene->getWaterEnvMap())
{
_SimpleWaterMat.setTexture(0, scene->getWaterEnvMap()->getEnvMap2D());
}
else
{
_SimpleWaterMat.setTexture(0, shape->_EnvMap[0]);
}
}
else
{
_SimpleWaterMat.setTexture(0, shape->_EnvMap[0]);
}
}
//
static std::vector verts;
static CIndexBuffer indices;
//
NLMISC::CPolygon2D &poly = shape->_Poly;
uint numVerts = poly.Vertices.size();
uint k;
//
if (shape->_ColorMap == NULL)
{
// version with no color map
if (!_EmbossTexture)
{
_EmbossTexture = new CTextureEmboss;
_EmbossTexture->setSlopeFactor(4.f);
}
if (shape->_BumpMap[1] && shape->_BumpMap[1]->isBumpMap())
{
CTextureBump *bm = static_cast((ITexture *) shape->_BumpMap[1]);
if (bm->getHeightMap())
{
_EmbossTexture->setHeightMap(bm->getHeightMap());
}
}
_SimpleWaterMat.setTexture(1, _EmbossTexture);
_SimpleRenderVB.setNumVertices(numVerts);
// retrieve current time
float date = 0.001f * (NLMISC::CTime::getLocalTime() & 0xffffff); // must keep some precision.
// Compute tex coordinates for emboss first.
// On some 3D chip, textures coords can't grow too mush or texture filtering loose accuracy.
// So we must keep texCoord as low as possible.
//
verts.resize(numVerts);
for(k = 0; k < numVerts; ++k)
{
verts[k].XFormPos = worldMatrix * NLMISC::CVector(poly.Vertices[k].x, poly.Vertices[k].y ,0.f);
verts[k].UV.U = shape->_HeightMapScale[0].x * verts[k].XFormPos.x + date * shape->_HeightMapSpeed[0].x;
verts[k].UV.V = shape->_HeightMapScale[0].y * verts[k].XFormPos.y + date * shape->_HeightMapSpeed[0].y;
}
// get min tex coords
float minU = verts[0].UV.U;
float minV = verts[0].UV.V;
for(k = 1; k < numVerts; ++k)
{
minU = std::min(minU, verts[k].UV.U);
minV = std::min(minV, verts[k].UV.V);
}
//
minU = floorf(minU);
minV = floorf(minV);
//
CVertexBufferReadWrite vba;
_SimpleRenderVB.lock (vba);
uint8 *data = (uint8 *) vba.getVertexCoordPointer();
for(k = 0; k < numVerts; ++k)
{
((NLMISC::CVector *) data)->set(poly.Vertices[k].x, poly.Vertices[k].y, 0.f);
data += sizeof(NLMISC::CVector);
// texture coord 0 is reflected vector into envmap
// xform position in world space to compute the reflection
CVector surfToEye = (obsPos - verts[k].XFormPos).normed();
// we assume that normal is (0, 0, 1)
* (float *) data = 0.5f - 0.5f * surfToEye.x;
((float *) data)[1] = 0.5f - 0.5f * surfToEye.y;
data += sizeof(float[2]);
// texture coord 1 is the embossed map
* (float *) data = verts[k].UV.U - minU;
((float *) data)[1] = verts[k].UV.V - minV;
data += sizeof(float[2]);
}
}
else
{
// version with a color map : it remplace the emboss texture
_SimpleWaterMat.setTexture(1, shape->_ColorMap);
_SimpleRenderVB.setNumVertices(numVerts);
CVertexBufferReadWrite vba;
_SimpleRenderVB.lock (vba);
//
uint8 *data = (uint8 *) vba.getVertexCoordPointer();
for(k = 0; k < numVerts; ++k)
{
* (NLMISC::CVector *) data = poly.Vertices[k];
data += sizeof(CVector);
// texture coord 0 is reflected vector into envmap
// xform position in world space to compute the reflection
NLMISC::CVector xformPos = worldMatrix * poly.Vertices[k];
NLMISC::CVector surfToEye = (obsPos - xformPos).normed();
// we assume that normal is (0, 0, 1)
* (float *) data = 0.5f - 0.5f * surfToEye.x;
((float *) data)[1] = 0.5f * - 0.5f * surfToEye.y;
data += sizeof(float[2]);
// texture coord 1 is the color map
* (float *) data = shape->_ColorMapMatColumn0.x * xformPos.x + shape->_ColorMapMatColumn1.x * xformPos.y + shape->_ColorMapMatPos.x;
((float *) data)[1] = shape->_ColorMapMatColumn0.y * xformPos.x + shape->_ColorMapMatColumn1.y * xformPos.y + shape->_ColorMapMatPos.y;
data += sizeof(float[2]);
}
}
drv->activeVertexProgram(NULL);
drv->setupModelMatrix(worldMatrix);
drv->activeVertexBuffer(_SimpleRenderVB);
// create an index buffer to do the display
indices.setNumIndexes((numVerts - 2) * 3);
{
CIndexBufferReadWrite ibaWrite;
indices.lock (ibaWrite);
uint32 *ptr = ibaWrite.getPtr();
for(k = 0; k < (numVerts - 2); ++k)
{
ptr[ k * 3 ] = 0;
ptr[ k * 3 + 1 ] = k + 1;
ptr[ k * 3 + 2 ] = k + 2;
}
}
drv->setupMaterial(_SimpleWaterMat);
drv->activeIndexBuffer(indices);
drv->renderSimpleTriangles(0, numVerts - 2);
}
*/
// ***********************************************************************************************************
void CWaterModel::updateDiffuseMapMatrix(bool force /* = false*/)
{
if (compareMatrixDate(_MatrixUpdateDate) ||force)
{
CWaterShape *shape = NLMISC::safe_cast((IShape *) Shape);
if (shape)
{
_MatrixUpdateDate = getMatrixDate();
// update the uv matrix
CMatrix uvMat;
uvMat.setRot(CVector(shape->_ColorMapMatColumn0.x, shape->_ColorMapMatColumn0.y, 0.f),
CVector(shape->_ColorMapMatColumn1.x, shape->_ColorMapMatColumn1.y, 0.f),
CVector(shape->_ColorMapMatPos.x, shape->_ColorMapMatPos.y, 1.f));
CMatrix xformMat;
CMatrix invMat = this->getWorldMatrix().inverted();
xformMat.setRot(CVector(invMat.getI().x, invMat.getI().y, 0.f),
CVector(invMat.getJ().x, invMat.getJ().y, 0.f),
CVector(invMat.getPos().x, invMat.getPos().y, 1.f));
uvMat = uvMat * xformMat;
_ColorMapMatColumn0.set(uvMat.getI().x, uvMat.getI().y);
_ColorMapMatColumn1.set(uvMat.getJ().x, uvMat.getJ().y);
_ColorMapMatPos.set(uvMat.getK().x, uvMat.getK().y);
}
}
}
// ***************************************************************************
void CWaterModel::debugDumpMem(void* &clippedPolyBegin, void* &clippedPolyEnd)
{
clippedPolyBegin= (void*)(&*_ClippedPoly.Vertices.begin());
clippedPolyEnd= (void*)(&*_ClippedPoly.Vertices.end());
}
// ***************************************************************************
void CWaterModel::debugClearClippedPoly()
{
_ClippedPoly.Vertices.clear();
}
//=======================================================================================
// wave maker implementation
//=======================================================================================
CWaveMakerModel::CWaveMakerModel() : _Time(0)
{
// AnimDetail behavior: Must be traversed in AnimDetail, even if no channel mixer registered
CTransform::setIsForceAnimDetail(true);
}
//================================================
void CWaveMakerModel::registerBasic()
{
CScene::registerModel(WaveMakerModelClassId, TransformShapeId, CWaveMakerModel::creator);
}
//================================================
ITrack* CWaveMakerModel::getDefaultTrack (uint valueId)
{
nlassert(Shape);
CWaveMakerShape *ws = NLMISC::safe_cast((IShape *) Shape);
switch (valueId)
{
case PosValue: return ws->getDefaultPos(); break;
default: // delegate to parent
return CTransformShape::getDefaultTrack(valueId);
break;
}
}
//================================================
void CWaveMakerModel::traverseAnimDetail()
{
CTransformShape::traverseAnimDetail();
nlassert(getOwnerScene());
/// get the shape
CWaveMakerShape *wms = NLMISC::safe_cast((IShape *) Shape);
const NLMISC::CVector worldPos = getWorldMatrix().getPos();
const CVector2f pos2d(worldPos.x, worldPos.y);
/// get the water height map
CWaterHeightMap &whm = GetWaterPoolManager().getPoolByID(wms->_PoolID);
// get the time delta
const TAnimationTime deltaT = std::min(getOwnerScene()->getEllapsedTime(), (TAnimationTime) whm.getPropagationTime());
_Time += deltaT;
if (!wms->_ImpulsionMode)
{
whm.perturbate(pos2d, wms->_Intensity * cosf(2.f / wms->_Period * (float) NLMISC::Pi * _Time), wms->_Radius);
}
else
{
if (_Time > wms->_Period)
{
_Time -= wms->_Period;
whm.perturbate(pos2d, wms->_Intensity, wms->_Radius);
}
}
}
} // NL3D