Linux multi-monitor fullscreen support
[ryzomcore.git] / nel / src / 3d / water_model.cpp
blob7c1cc6c4508d1fb8d5de2b00eb3c3df318ab6913
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "std3d.h"
22 #include "nel/misc/vector_2f.h"
23 #include "nel/misc/vector_h.h"
24 #include "nel/misc/hierarchical_timer.h"
25 #include "nel/3d/animation_time.h"
26 #include "nel/3d/water_model.h"
27 #include "nel/3d/water_shape.h"
28 #include "nel/3d/water_pool_manager.h"
29 #include "nel/3d/water_height_map.h"
30 #include "nel/3d/dru.h"
31 #include "nel/3d/scene.h"
32 #include "nel/3d/driver.h"
33 #include "nel/3d/render_trav.h"
34 #include "nel/3d/anim_detail_trav.h"
35 #include "nel/3d/texture_emboss.h"
36 #include "nel/3d/texture_bump.h"
37 #include "nel/3d/water_env_map.h"
40 using NLMISC::CVector2f;
42 #ifdef DEBUG_NEW
43 #define new DEBUG_NEW
44 #endif
46 namespace NL3D {
48 // for normal rendering
49 CMaterial CWaterModel::_WaterMat;
50 // for simple rendering
51 CMaterial CWaterModel::_SimpleWaterMat;
52 const uint WATER_MODEL_DEFAULT_NUM_VERTICES = 5000;
54 NLMISC::CRefPtr<IDriver> CWaterModel::_CurrDrv;
58 // TMP
59 volatile bool forceWaterSimpleRender = false;
61 //=======================================================================
62 void CWaterModel::setupVertexBuffer(CVertexBuffer &vb, uint numWantedVertices, IDriver *drv)
64 if (!numWantedVertices) return;
65 if (vb.getNumVertices() == 0 || drv != _CurrDrv) // not setupped yet, or driver changed ?
67 vb.setNumVertices(0);
68 vb.setName("Water");
69 vb.setPreferredMemory(CVertexBuffer::AGPPreferred, false);
70 if (drv->supportWaterShader())
72 vb.setVertexFormat(CVertexBuffer::PositionFlag);
74 else
76 vb.setVertexFormat(CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag);
78 _CurrDrv = drv;
80 uint numVerts = std::max(numWantedVertices, WATER_MODEL_DEFAULT_NUM_VERTICES);
81 if (numVerts > vb.getNumVertices())
83 const uint vb_INCREASE_SIZE = 1000;
84 numVerts = vb_INCREASE_SIZE * ((numVerts + (vb_INCREASE_SIZE - 1)) / vb_INCREASE_SIZE); // snap size
85 vb.setNumVertices((uint32) numVerts);
89 //=======================================================================
90 CWaterModel::CWaterModel()
92 setOpacity(false);
93 setTransparency(true);
94 setOrderingLayer(1);
95 // RenderFilter: We are a SegRemanece
96 _RenderFilterType= UScene::FilterWater;
97 _Prev = NULL;
98 _Next = NULL;
99 _MatrixUpdateDate = 0;
102 //=======================================================================
103 CWaterModel::~CWaterModel()
105 CScene *scene = getOwnerScene();
106 if (scene && scene->getWaterCallback())
108 nlassert(Shape);
109 CWaterShape *ws = NLMISC::safe_cast<CWaterShape *>((IShape *) Shape);
110 scene->getWaterCallback()->waterSurfaceRemoved(ws->getUseSceneWaterEnvMap(0) || ws->getUseSceneWaterEnvMap(1));
112 // should be already unlinked, but security
113 unlink();
116 //=======================================================================
117 void CWaterModel::registerBasic()
119 CScene::registerModel(WaterModelClassId, TransformShapeId, CWaterModel::creator);
123 //=======================================================================
124 ITrack* CWaterModel::getDefaultTrack (uint valueId)
126 nlassert(Shape);
127 CWaterShape *ws = NLMISC::safe_cast<CWaterShape *>((IShape *) Shape);
128 switch (valueId)
130 case PosValue: return ws->getDefaultPos(); break;
131 case ScaleValue: return ws->getDefaultScale(); break;
132 case RotQuatValue: return ws->getDefaultRotQuat(); break;
133 default: // delegate to parent
134 return CTransformShape::getDefaultTrack(valueId);
135 break;
140 //=======================================================================
141 uint32 CWaterModel::getWaterHeightMapID() const
143 CWaterShape *ws = NLMISC::safe_cast<CWaterShape *>((IShape *) Shape);
144 return ws->_WaterPoolID;
147 //=======================================================================
148 float CWaterModel::getHeightFactor() const
150 CWaterShape *ws = NLMISC::safe_cast<CWaterShape *>((IShape *) Shape);
151 return ws->_WaveHeightFactor;
155 //=======================================================================
156 float CWaterModel::getHeight(const CVector2f &pos)
158 CWaterShape *ws = NLMISC::safe_cast<CWaterShape *>((IShape *) Shape);
159 CWaterHeightMap &whm = GetWaterPoolManager().getPoolByID(ws->_WaterPoolID);
160 const float height = whm.getHeight(pos);
161 return height * ws->_WaveHeightFactor + this->getPos().z;
164 //=======================================================================
165 float CWaterModel::getAttenuatedHeight(const CVector2f &pos, const NLMISC::CVector &viewer)
167 CWaterShape *ws = NLMISC::safe_cast<CWaterShape *>((IShape *) Shape);
168 CWaterHeightMap &whm = GetWaterPoolManager().getPoolByID(ws->_WaterPoolID);
169 const float maxDist = whm.getUnitSize() * (whm.getSize() >> 1);
170 const NLMISC::CVector planePos(pos.x, pos.y, this->getMatrix().getPos().z);
171 const float userDist = (planePos - viewer).norm();
173 if (userDist > maxDist)
175 return this->getMatrix().getPos().z;
177 else
179 const float height = whm.getHeight(pos);
180 return ws->_WaveHeightFactor * height * (1.f - userDist / maxDist) + this->getMatrix().getPos().z;
185 //=======================================================================
187 // perform a bilinear on 4 values
188 // 0---1
189 // | |
190 // 3---2
193 static float inline BilinFilter(float v0, float v1, float v2, float v3, float u, float v)
195 float g = v * v3 + (1.f - v) * v0;
196 float h = v * v2 + (1.f - v) * v1;
197 return u * h + (1.f - u) * g;
202 //=======================================================================
204 /// store a value in a water vertex buffer, and increment the pointer
206 static void inline FillWaterVB(uint8 *&vbPointer, float x, float y, float z, float nx, float ny)
208 * (float *) vbPointer = x;
209 ((float *) vbPointer)[1] = y;
210 ((float *) vbPointer)[2] = z;
211 *((float *) (vbPointer + 3 * sizeof(float))) = nx;
212 *((float *) (vbPointer + 4 * sizeof(float))) = ny;
213 vbPointer += 5 * sizeof(float);
217 // ***************************************************************************************************************
219 #ifdef NL_OS_WINDOWS
220 __forceinline
221 #endif
222 static void SetupWaterVertex( sint qLeft,
223 sint qRight,
224 sint qUp,
225 sint qDown,
226 sint qSubLeft,
227 sint qSubDown,
228 const NLMISC::CVector &inter,
229 float invWaterRatio,
230 sint doubleWaterHeightMapSize,
231 CWaterHeightMap &whm,
232 uint8 *&vbPointer,
233 float offsetX,
234 float offsetY
237 const float wXf = invWaterRatio * (inter.x + offsetX);
238 const float wYf = invWaterRatio * (inter.y + offsetY);
240 sint wx = (sint) floorf(wXf);
241 sint wy = (sint) floorf(wYf);
245 if (!
246 (wx >= qLeft && wx < qRight && wy < qUp && wy >= qDown)
249 // no perturbation is visible
250 FillWaterVB(vbPointer, inter.x, inter.y, 0, 0, 0);
252 else
256 // filter height and gradient at the given point
257 const sint stride = doubleWaterHeightMapSize;
260 const uint xm = (uint) (wx - qSubLeft);
261 const uint ym = (uint) (wy - qSubDown);
262 const sint offset = xm + stride * ym;
263 const float *ptWater = whm.getPointer() + offset;
266 float deltaU = wXf - wx;
267 float deltaV = wYf - wy;
268 //nlassert(deltaU >= 0.f && deltaU <= 1.f && deltaV >= 0.f && deltaV <= 1.f);
270 const float *ptWaterPrev = whm.getPrevPointer() + offset;
274 float g0x, g1x, g2x, g3x; // x gradient for current
275 float g0xp, g1xp, g2xp, g3xp;
277 float gradCurrX, gradCurrY;
279 float g0y, g1y, g2y, g3y; // y gradient for previous map
280 float g0yp, g1yp, g2yp, g3yp;
282 float gradPrevX, gradPrevY;
284 /// curr gradient
286 g0x = ptWater[ 1] - ptWater[ - 1];
287 g1x = ptWater[ 2] - ptWater[ 0 ];
288 g2x = ptWater[ 2 + stride] - ptWater[ stride];
289 g3x = ptWater[ 1 + stride] - ptWater[ - 1 + stride];
291 gradCurrX = BilinFilter(g0x, g1x, g2x, g3x, deltaU, deltaV);
294 g0y = ptWater[ stride] - ptWater[ - stride];
295 g1y = ptWater[ stride + 1] - ptWater[ - stride + 1];
296 g2y = ptWater[ (stride << 1) + 1] - ptWater[ 1];
297 g3y = ptWater[ (stride << 1)] - ptWater[0];
299 gradCurrY = BilinFilter(g0y, g1y, g2y, g3y, deltaU, deltaV);
301 /// prev gradient
303 g0xp = ptWaterPrev[ 1] - ptWaterPrev[ - 1];
304 g1xp = ptWaterPrev[ 2] - ptWaterPrev[ 0 ];
305 g2xp = ptWaterPrev[ 2 + stride] - ptWaterPrev[ + stride];
306 g3xp = ptWaterPrev[ 1 + stride] - ptWaterPrev[ - 1 + stride];
308 gradPrevX = BilinFilter(g0xp, g1xp, g2xp, g3xp, deltaU, deltaV);
311 g0yp = ptWaterPrev[ stride] - ptWaterPrev[ - stride];
312 g1yp = ptWaterPrev[ stride + 1] - ptWaterPrev[ - stride + 1];
313 g2yp = ptWaterPrev[ (stride << 1) + 1] - ptWaterPrev[ 1 ];
314 g3yp = ptWaterPrev[ (stride << 1)] - ptWaterPrev[ 0 ];
316 gradPrevY = BilinFilter(g0yp, g1yp, g2yp, g3yp, deltaU, deltaV);
319 /// current height
320 float h = BilinFilter(ptWater[ 0 ], ptWater[ + 1], ptWater[ 1 + stride], ptWater[stride], deltaU, deltaV);
322 /// previous height
323 float hPrev = BilinFilter(ptWaterPrev[ 0 ], ptWaterPrev[ 1], ptWaterPrev[ 1 + stride], ptWaterPrev[stride], deltaU, deltaV);
326 float timeRatio = whm.getBufferRatio();
329 FillWaterVB(vbPointer, inter.x, inter.y, timeRatio * h + (1.f - timeRatio) * hPrev,
330 4.5f * (timeRatio * gradCurrX + (1.f - timeRatio) * gradPrevX),
331 4.5f * (timeRatio * gradCurrY + (1.f - timeRatio) * gradPrevY)
334 //NLMISC::CVector2f *ptGrad = whm.getGradPointer() + offset;
340 // *****************************************************************************************************
342 static void DrawPoly2D(CVertexBuffer &vb, IDriver *drv, const NLMISC::CMatrix &mat, const NLMISC::CPolygon &p)
344 uint k;
347 CVertexBufferReadWrite vba;
348 vb.lock (vba);
349 for (k = 0; k < p.Vertices.size(); ++k)
351 NLMISC::CVector tPos = mat * NLMISC::CVector(p.Vertices[k].x, p.Vertices[k].y, 0);
352 vba.setValueFloat3Ex (WATER_VB_POS, k, tPos.x, tPos.y, tPos.z);
353 vba.setValueFloat2Ex (WATER_VB_DX, k, 0, 0);
356 static CIndexBuffer ib;
357 ib.setNumIndexes(3 * p.Vertices.size());
359 CIndexBufferReadWrite ibaWrite;
360 ib.lock (ibaWrite);
361 uint32 *ptr = ibaWrite.getPtr();
362 for (k = 0; k < p.Vertices.size() - 2; ++k)
364 ptr[ k * 3 ] = 0;
365 ptr[ k * 3 + 1 ] = k + 1;
366 ptr[ k * 3 + 2 ] = k + 2;
369 drv->activeIndexBuffer(ib);
370 drv->renderSimpleTriangles(0, p.Vertices.size() - 2);
375 // ***************************************************************************************************************
377 void CWaterModel::traverseRender()
379 H_AUTO( NL3D_Water_Render );
381 CRenderTrav &renderTrav = getOwnerScene()->getRenderTrav();
382 IDriver *drv = renderTrav.getDriver();
385 #ifndef FORCE_SIMPLE_WATER_RENDER
386 if (!drv->supportWaterShader())
387 #endif
389 doSimpleRender(drv);
390 return;
393 CWaterShape *shape = NLMISC::safe_cast<CWaterShape *>((IShape *) Shape);
396 if (shape->_GridSizeTouched)
398 shape->setupVertexBuffer();
401 // inverted object world matrix
402 //NLMISC::CMatrix invObjMat = getWorldMatrix().inverted();
404 // viewer pos in world space
405 const NLMISC::CVector &obsPos = renderTrav.CamPos;
407 // camera matrix in world space
408 const NLMISC::CMatrix &camMat = renderTrav.CamMatrix;
410 // view matrix (inverted cam matrix)
411 const NLMISC::CMatrix &viewMat = renderTrav.ViewMatrix;
413 // compute the camera matrix such as there is no rotation around the y axis
414 NLMISC::CMatrix camMatUp;
415 ComputeUpMatrix(camMat.getJ(), camMatUp, camMat);
416 camMatUp.setPos(camMat.getPos());
418 const NLMISC::CMatrix matViewUp = camMatUp.inverted();
420 // plane z pos in world
421 const float zHeight = getWorldMatrix().getPos().z;
423 const sint numStepX = CWaterShape::getScreenXGridSize();
424 const sint numStepY = CWaterShape::getScreenYGridSize();
426 const float invNumStepX = 1.f / numStepX;
427 const float invNumStepY = 1.f / numStepY;
429 const uint rotBorderSize = (shape->_MaxGridSize + (shape->_XGridBorder << 1) - numStepX) >> 1;
431 const sint isAbove = obsPos.z > zHeight ? 1 : 0;
434 #ifdef NO_WATER_TESSEL
435 const float transitionDist = renderTrav.Near * 0.99f;
436 #else
437 const float transitionDist = shape->_TransitionRatio * renderTrav.Far;
438 #endif
441 NLMISC::CMatrix modelMat;
442 modelMat.setPos(NLMISC::CVector(obsPos.x, obsPos.y, zHeight));
443 drv->setupModelMatrix(modelMat);
445 //==================//
446 // material setup //
447 //==================//
449 CWaterHeightMap &whm = GetWaterPoolManager().getPoolByID(shape->_WaterPoolID);
451 setupMaterialNVertexShader(drv, shape, obsPos, isAbove > 0, whm.getUnitSize() * (whm.getSize() >> 1), zHeight);
454 drv->setupMaterial(CWaterModel::_WaterMat);
456 sint numPass = drv->beginMaterialMultiPass();
457 nlassert(numPass == 1); // for now, we assume water is always rendered in a single pass !
458 drv->setupMaterialPass(0);
461 //setAttenuationFactor(drv, false, obsPos, camMat.getJ(), farDist);
462 //disableAttenuation(drv);
465 //================================//
466 // Vertex buffer setup //
467 //================================//
469 drv->activeVertexBuffer(shape->_VB);
471 //================================//
472 // tesselated part of the poly //
473 //================================//
475 if (_ClippedPoly.Vertices.size())
477 //======================================//
478 // Polygon projection on the near plane //
479 //======================================//
481 static NLMISC::CPolygon2D projPoly; // projected poly
482 projPoly.Vertices.resize(_ClippedPoly.Vertices.size());
483 const float Near = renderTrav.Near;
486 const float xFactor = numStepX * Near / (renderTrav.Right - renderTrav.Left);
487 const float xOffset = numStepX * (-renderTrav.Left / (renderTrav.Right - renderTrav.Left)) + 0.5f;
488 const float yFactor = numStepY * Near / (renderTrav.Bottom - renderTrav.Top);
489 const float yOffset = numStepY * (-renderTrav.Top / (renderTrav.Bottom - renderTrav.Top)) - 0.5f * isAbove;
491 const NLMISC::CMatrix projMat = matViewUp * getWorldMatrix();
492 uint k;
493 for (k = 0; k < _ClippedPoly.Vertices.size(); ++k)
495 // project points in the view
496 NLMISC::CVector t = projMat * _ClippedPoly.Vertices[k];
497 float invY = 1.f / t.y;
498 projPoly.Vertices[k].set(xFactor * t.x * invY + xOffset, yFactor * t.z * invY + yOffset);
501 //=============================================//
502 // compute borders of poly at a low resolution //
503 //=============================================//
505 NLMISC::CPolygon2D::TRasterVect rasters;
506 sint startY;
507 projPoly.computeBorders(rasters, startY);
509 if (!rasters.empty())
511 //===========================//
512 // perform Water animation //
513 //===========================//
515 const float WaterRatio = whm.getUnitSize();
516 const float invWaterRatio = 1.f / WaterRatio;
517 const uint WaterHeightMapSize = whm.getSize();
518 const uint doubleWaterHeightMapSize = (WaterHeightMapSize << 1);
521 sint64 idate = getOwnerScene()->getHrcTrav().CurrentDate;
525 if (idate != whm.Date)
527 whm.setUserPos((sint) (obsPos.x * invWaterRatio) - (WaterHeightMapSize >> 1),
528 (sint) (obsPos.y * invWaterRatio) - (WaterHeightMapSize >> 1)
530 nlassert(getOwnerScene()); // this object should have been created from a CWaterShape!
531 whm.animate((float) (getOwnerScene()->getEllapsedTime()));
532 whm.Date = idate;
535 //float startDate = (float) (1000.f * NLMISC::CTime::ticksToSecond(NLMISC::CTime::getPerformanceTime()));
537 //=====================================//
538 // compute heightmap useful area //
539 //=====================================//
541 // We don't store a heighmap for a complete Water area
542 // we just consider the height of Water columns that are near the observer
544 // Compute a quad in Water height field space that contains the useful heights
545 // This helps us to decide whether we should do a lookup in the height map
547 sint mapPosX, mapPosY;
549 /// get the pos used in the height map (may not be the same than our current pos, has it is taken in account
550 /// every 'PropagationTime' second
551 whm.getUserPos(mapPosX, mapPosY);
553 const uint mapBorder = 3;
555 const sint qRight = (sint) mapPosX + WaterHeightMapSize - mapBorder;
556 sint qLeft = (sint) mapPosX;
557 const sint qUp = (sint) mapPosY + WaterHeightMapSize - mapBorder;
558 sint qDown = (sint) mapPosY;
560 /// 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
561 const sint qSubLeft = qLeft - (uint) qLeft % WaterHeightMapSize;
562 const sint qSubDown = qDown - (uint) qDown % WaterHeightMapSize;
564 qLeft += mapBorder;
565 qDown += mapBorder;
567 //==============================================//
568 // setup rays to be traced, and their increment //
569 //==============================================//
572 // compute camera rays in world space
573 NLMISC::CVector currHV = renderTrav.Left * camMatUp.getI() + renderTrav.Near * camMatUp.getJ() + renderTrav.Top * camMatUp.getK(); // current border vector, incremented at each line
574 NLMISC::CVector currV; // current ray vector
575 NLMISC::CVector xStep = (renderTrav.Right - renderTrav.Left) * invNumStepX * camMatUp.getI(); // xStep for the ray vector
576 NLMISC::CVector yStep = (renderTrav.Bottom - renderTrav.Top) * invNumStepY * camMatUp.getK(); // yStep for the ray vector
578 //===============================================//
579 // perform display //
580 //===============================================//
582 // scale currHV at the top of the poly
583 currHV += (startY - 0.5f * isAbove) * yStep;
585 // current index buffer used. We swap each time a row has been drawn
586 CIndexBuffer *currIB = &CWaterShape::_IBUpDown, *otherIB = &CWaterShape::_IBDownUp;
589 sint vIndex = 0; // index in vertices
591 // current raster position
592 sint oldStartX, oldEndX, realStartX, realEndX;
593 //float invNearWidth = numStepX / (renderTrav.Right - renderTrav.Left);
595 //nlinfo("size = %d, maxSize = ", rasters.size(), numStepY);
598 const uint wqHeight = rasters.size();
599 if (wqHeight)
601 // denominator of the intersection equation
602 const float denom = - obsPos.z + zHeight;
603 // test the upper raster
604 // if it is above the horizon, we modify it to reach the correct location
605 const float horizonEpsilon = 10E-4f; // we must be a little below the horizon
607 // distance from the viewer along the traced ray
608 float t;
610 NLMISC::CPolygon2D::TRasterVect::const_iterator it = rasters.begin();
611 for (uint l = 0; l <= wqHeight; ++l)
613 //nlinfo("start = %d, end = %d", it->first, it->second);
614 const sint startX = it->first;
615 const sint endX = (it->second + 1);
617 nlassert(startX >= - (sint) rotBorderSize);
618 nlassert(endX <= (sint) (numStepX + rotBorderSize));
620 if (l != 0)
622 realStartX = std::min(startX, oldStartX);
623 realEndX = std::max(endX, oldEndX);
625 else
627 realStartX = startX;
628 realEndX = endX;
632 // current view vector
633 currV = currHV + (realStartX - 0.5f) * xStep;
635 if (l == 0)
637 if (isAbove)
639 // test whether the first row is out of horizon.
640 // if this is the case, we make a correction
641 if (denom * currV.z <= 0)
643 // correct for the first line only by adding a y offset
644 currV += yStep * ((denom > 0 ? horizonEpsilon : - horizonEpsilon) - currV.z) / yStep.z;
647 // now, for the transition, check whether the first raster does not go over the transition dist
649 t = denom / currV.z;
650 const float VJ = camMat.getJ() * currV;
651 if ( t * VJ > transitionDist)
653 float delta = (1.f / yStep.z) * ( denom * VJ / transitionDist - currV.z);
654 // correct the first line to reach that position
655 currV += delta * yStep;
662 CVertexBufferReadWrite vba;
663 shape->_VB.lock (vba);
664 uint8 *vbPointer = (uint8 *) vba.getVertexCoordPointer() + shape->_VB.getVertexSize() * (vIndex + realStartX + rotBorderSize);
667 for (sint k = realStartX; k <= realEndX; ++k)
669 t = denom / currV.z;
670 // compute intersection with plane
671 NLMISC::CVector inter = t * currV;
672 inter.z += obsPos.z;
673 SetupWaterVertex(qLeft, qRight, qUp, qDown, qSubLeft, qSubDown, inter, invWaterRatio, doubleWaterHeightMapSize, whm, vbPointer, obsPos.x, obsPos.y);
674 currV += xStep;
678 if (l != 0) // 2 line of the ib done ?
680 sint count = oldEndX - oldStartX;
681 if (count > 0)
683 drv->activeIndexBuffer(*currIB);
684 drv->renderSimpleTriangles((oldStartX + rotBorderSize) * 6, 2 * count );
688 oldStartX = startX;
689 oldEndX = endX;
690 currHV += yStep;
691 vIndex = (numStepX + 2 * rotBorderSize + 1) - vIndex; // swap first row and second row
692 std::swap(currIB, otherIB);
693 if (l < (wqHeight - 1))
695 ++it;
697 else
699 if (!isAbove)
701 // last line
702 // test whether we are out of horizon
703 if (denom * currHV.z <= 0)
705 // correct for the first line only by adding a y offset
706 currHV += yStep * ((denom > 0 ? horizonEpsilon : - horizonEpsilon) - currHV.z) / yStep.z;
709 // now, for the transition, check whether the first raster does not go over the transition dist
711 t = denom / currHV.z;
712 const float VJ = camMat.getJ() * currHV;
713 if ( t * VJ > transitionDist)
715 float delta = (1.f / yStep.z) * ( denom * VJ / transitionDist - currHV.z);
716 // correct the first line to reach that position
717 currHV += delta * yStep;
725 //nlinfo("display: %f ms", (float) (1000.f * NLMISC::CTime::ticksToSecond(NLMISC::CTime::getPerformanceTime()) - startDate));
729 //=========================================//
730 // display end poly //
731 //=========================================//
733 if (_EndClippedPoly.Vertices.size() != 0)
736 CMatrix xform = _WorldMatrix;
737 xform.movePos(NLMISC::CVector(- obsPos.x, - obsPos.y, _WorldMatrix.getPos().z));
738 DrawPoly2D(shape->_VB, drv, xform, _EndClippedPoly);
741 drv->endMaterialMultiPass();
744 drv->activeVertexProgram(NULL);
749 // ***********************
750 // Water MATERIAL SETUP //
751 // ***********************
753 void CWaterModel::setupMaterialNVertexShader(IDriver *drv, CWaterShape *shape, const NLMISC::CVector &obsPos, bool above, float maxDist, float zHeight)
755 static bool matSetupped = false;
756 if (!matSetupped)
758 _WaterMat.setLighting(false);
759 _WaterMat.setDoubleSided(true);
760 _WaterMat.setColor(NLMISC::CRGBA::White);
761 _WaterMat.setBlend(true);
762 _WaterMat.setSrcBlend(CMaterial::srcalpha);
763 _WaterMat.setDstBlend(CMaterial::invsrcalpha);
764 _WaterMat.setZWrite(true);
765 _WaterMat.setShader(CMaterial::Water);
769 const uint cstOffset = 4; // 4 places for the matrix
770 NLMISC::CVectorH cst[13];
773 //=========================//
774 // setup Water material //
775 //=========================//
777 CWaterModel::_WaterMat.setTexture(0, shape->_BumpMap[0]);
778 CWaterModel::_WaterMat.setTexture(1, shape->_BumpMap[1]);
779 CWaterModel::_WaterMat.setTexture(3, shape->_ColorMap);
781 CScene *scene = getOwnerScene();
782 if (!above && shape->_EnvMap[1])
784 if (shape->_UsesSceneWaterEnvMap[1])
786 if (scene->getWaterEnvMap())
788 CWaterModel::_WaterMat.setTexture(2, scene->getWaterEnvMap()->getEnvMap2D());
790 else
792 CWaterModel::_WaterMat.setTexture(2, shape->_EnvMap[1]);
795 else
797 CWaterModel::_WaterMat.setTexture(2, shape->_EnvMap[1]);
800 else
802 if (shape->_UsesSceneWaterEnvMap[0])
804 if (scene->getWaterEnvMap())
806 CWaterModel::_WaterMat.setTexture(2, scene->getWaterEnvMap()->getEnvMap2D());
808 else
810 CWaterModel::_WaterMat.setTexture(2, shape->_EnvMap[0]);
813 else
815 CWaterModel::_WaterMat.setTexture(2, shape->_EnvMap[0]);
819 shape->envMapUpdate();
821 const uint alphaMapStage = 3;
822 if (shape->_ColorMap)
824 //WaterMat.setTexture(alphaMapStage, shape->_ColorMap);
825 //if (shape->_ColorMap->supportSharing()) nlinfo(shape->_ColorMap->getShareName().c_str());
828 // setup 2x3 matrix for lookup in diffuse map
829 updateDiffuseMapMatrix();
830 cst[13 - cstOffset].set(_ColorMapMatColumn0.x, _ColorMapMatColumn1.x, 0, _ColorMapMatColumn0.x * obsPos.x + _ColorMapMatColumn1.x * obsPos.y + _ColorMapMatPos.x);
831 cst[14 - cstOffset].set(_ColorMapMatColumn0.y, _ColorMapMatColumn1.y, 0, _ColorMapMatColumn0.y * obsPos.x + _ColorMapMatColumn1.y * obsPos.y + _ColorMapMatPos.y);
833 else
835 cst[13 - cstOffset].set(0, 0, 0, 0);
836 cst[14 - cstOffset].set(0, 0, 0, 0);
839 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
840 // 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
842 cst[5 - cstOffset].set(0.f, 0.f, 0.f, 0.f); // claping negative values to 0
844 // slope of attenuation of normal / height with distance
845 const float invMaxDist = shape->_WaveHeightFactor / maxDist;
846 cst[6 - cstOffset].set(invMaxDist, shape->_WaveHeightFactor, 0, 0);
848 /// set matrix
849 drv->setConstantMatrix(0, IDriver::ModelViewProjection, IDriver::Identity);
850 drv->setConstantFog(18);
852 // retrieve current time
853 float date = 0.001f * (NLMISC::CTime::getLocalTime() & 0xffffff); // must keep some precision.
854 // set bumpmaps pos
855 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
856 cst[10 - cstOffset].set(shape->_HeightMapScale[0].x, shape->_HeightMapScale[0].y, 0, 0); // bump map 0 scale
857 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
858 cst[12 - cstOffset].set(shape->_HeightMapScale[1].x, shape->_HeightMapScale[1].y, 0, 0); // bump map 1 scale
860 cst[4 - cstOffset].set(1.f, 1.f, 1.f, 1.f); // use with min man, and to get the 1 constant
861 cst[7 - cstOffset].set(0, 0, obsPos.z - zHeight, 1.f);
862 cst[8 - cstOffset].set(0.5f, 0.5f, 0.f, 1.f); // used to scale reflected ray into the envmap
864 /// set all our constants in one call
865 drv->setConstant(4, sizeof(cst) / sizeof(cst[0]), (float *) &cst[0]);
867 shape->initVertexProgram();
868 bool result;
870 //if (useBumpedVersion)
872 // if (!useEMBM)
873 // {
874 // result = shape->getColorMap() ? drv->activeVertexProgram((shape->_VertexProgramBump2Diffuse).get())
875 // : drv->activeVertexProgram((shape->_VertexProgramBump2).get());
876 // }
877 // else
878 // {
879 // result = shape->getColorMap() ? drv->activeVertexProgram((shape->_VertexProgramBump1Diffuse).get())
880 // : drv->activeVertexProgram((shape->_VertexProgramBump1).get());
881 // }
883 //else
885 // result = shape->getColorMap() ? drv->activeVertexProgram((shape->_VertexProgramNoBumpDiffuse).get())
886 // : drv->activeVertexProgram((shape->_VertexProgramNoBump).get());
889 //result = shape->getColorMap() ? drv->activeVertexProgram((shape->_VertexProgramBump2Diffuse).get())
890 // : drv->activeVertexProgram((shape->_VertexProgramBump2).get());
892 //if (!result) nlwarning("no vertex program setuped");
898 void CWaterModel::setupMaterialNVertexShader(IDriver *drv, CWaterShape *shape, const NLMISC::CVector &obsPos, bool above, float zHeight)
900 static bool matSetupped = false;
901 if (!matSetupped)
903 _WaterMat.setLighting(false);
904 _WaterMat.setDoubleSided(true);
905 _WaterMat.setColor(NLMISC::CRGBA::White);
906 _WaterMat.setBlend(true);
907 _WaterMat.setSrcBlend(CMaterial::srcalpha);
908 _WaterMat.setDstBlend(CMaterial::invsrcalpha);
909 _WaterMat.setZWrite(true);
910 _WaterMat.setShader(CMaterial::Water);
912 //=========================//
913 // setup Water material //
914 //=========================//
915 shape->initVertexProgram();
916 CVertexProgramWaterVPNoWave *program = shape->_ColorMap ? CWaterShape::_VertexProgramNoWaveDiffuse : CWaterShape::_VertexProgramNoWave;
917 drv->activeVertexProgram(program);
918 CWaterModel::_WaterMat.setTexture(0, shape->_BumpMap[0]);
919 CWaterModel::_WaterMat.setTexture(1, shape->_BumpMap[1]);
920 CWaterModel::_WaterMat.setTexture(3, shape->_ColorMap);
921 CScene *scene = getOwnerScene();
922 if (!above && shape->_EnvMap[1])
924 if (shape->_UsesSceneWaterEnvMap[1])
926 if (scene->getWaterEnvMap())
928 CWaterModel::_WaterMat.setTexture(2, scene->getWaterEnvMap()->getEnvMap2D());
930 else
932 CWaterModel::_WaterMat.setTexture(2, shape->_EnvMap[1]);
935 else
937 CWaterModel::_WaterMat.setTexture(2, shape->_EnvMap[1]);
940 else
942 if (shape->_UsesSceneWaterEnvMap[0])
944 if (scene->getWaterEnvMap())
946 CWaterModel::_WaterMat.setTexture(2, scene->getWaterEnvMap()->getEnvMap2D());
948 else
950 CWaterModel::_WaterMat.setTexture(2, shape->_EnvMap[0]);
953 else
955 CWaterModel::_WaterMat.setTexture(2, shape->_EnvMap[0]);
958 shape->envMapUpdate();
959 if (shape->_ColorMap)
961 // setup 2x3 matrix for lookup in diffuse map
962 updateDiffuseMapMatrix();
963 drv->setUniform4f(IDriver::VertexProgram, program->idx().DiffuseMapVector0, _ColorMapMatColumn0.x, _ColorMapMatColumn1.x, 0, _ColorMapMatColumn0.x * obsPos.x + _ColorMapMatColumn1.x * obsPos.y + _ColorMapMatPos.x);
964 drv->setUniform4f(IDriver::VertexProgram, program->idx().DiffuseMapVector1, _ColorMapMatColumn0.y, _ColorMapMatColumn1.y, 0, _ColorMapMatColumn0.y * obsPos.x + _ColorMapMatColumn1.y * obsPos.y + _ColorMapMatPos.y);
966 // set builtins
967 drv->setUniformMatrix(IDriver::VertexProgram, program->getUniformIndex(CProgramIndex::ModelViewProjection), IDriver::ModelViewProjection, IDriver::Identity);
968 drv->setUniformFog(IDriver::VertexProgram, program->getUniformIndex(CProgramIndex::Fog));
969 // retrieve current time
970 double date = scene->getCurrentTime();
971 // set bumpmaps pos
972 drv->setUniform4f(IDriver::VertexProgram, program->idx().BumpMap0Offset, 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
973 drv->setUniform4f(IDriver::VertexProgram, program->idx().BumpMap0Scale, shape->_HeightMapScale[0].x, shape->_HeightMapScale[0].y, 0, 0); // bump map 0 scale
974 drv->setUniform4f(IDriver::VertexProgram, program->idx().BumpMap1Offset, 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
975 drv->setUniform4f(IDriver::VertexProgram, program->idx().BumpMap1Scale, shape->_HeightMapScale[1].x, shape->_HeightMapScale[1].y, 0, 0); // bump map 1 scale
976 drv->setUniform4f(IDriver::VertexProgram, program->idx().ObserverHeight, 0, 0, obsPos.z - zHeight, 1.f);
977 drv->setUniform4f(IDriver::VertexProgram, program->idx().ScaleReflectedRay, 0.5f, 0.5f, 0.f, 1.f); // used to scale reflected ray into the envmap
980 //================================================
981 void CWaterModel::setupSimpleRender(CWaterShape *shape, const NLMISC::CVector &obsPos, bool above)
983 // rendering of water when no vertex / pixel shaders are available
984 static bool init = false;
985 if (!init)
987 // setup the material, no special shader is used here
988 _SimpleWaterMat.setLighting(false);
989 _SimpleWaterMat.setDoubleSided(true);
990 _SimpleWaterMat.setColor(NLMISC::CRGBA::White);
992 _SimpleWaterMat.setBlend(true);
993 _SimpleWaterMat.setSrcBlend(CMaterial::srcalpha);
994 _SimpleWaterMat.setDstBlend(CMaterial::invsrcalpha);
995 _SimpleWaterMat.setZWrite(true);
996 _SimpleWaterMat.setShader(CMaterial::Normal);
998 // stage 0
999 _SimpleWaterMat.texEnvOpRGB(0, CMaterial::Replace);
1000 _SimpleWaterMat.texEnvOpAlpha(0, CMaterial::Replace);
1001 _SimpleWaterMat.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
1002 _SimpleWaterMat.texEnvArg0Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha);
1004 // stage 1
1005 _SimpleWaterMat.texEnvOpRGB(1, CMaterial::Modulate);
1006 _SimpleWaterMat.texEnvOpAlpha(1, CMaterial::Modulate);
1007 _SimpleWaterMat.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
1008 _SimpleWaterMat.texEnvArg0Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha);
1009 _SimpleWaterMat.texEnvArg1RGB(0, CMaterial::Previous, CMaterial::SrcColor);
1010 _SimpleWaterMat.texEnvArg1Alpha(0, CMaterial::Previous, CMaterial::SrcAlpha);
1012 init = true;
1014 // envmap is always present and is in stage 0
1015 CScene *scene = getOwnerScene();
1016 if (!above && shape->_EnvMap[1])
1018 if (shape->_UsesSceneWaterEnvMap[1])
1020 if (scene->getWaterEnvMap())
1022 _SimpleWaterMat.setTexture(0, scene->getWaterEnvMap()->getEnvMap2D());
1024 else
1026 _SimpleWaterMat.setTexture(0, shape->_EnvMap[1]);
1029 else
1031 _SimpleWaterMat.setTexture(0, shape->_EnvMap[1]);
1034 else
1036 if (shape->_UsesSceneWaterEnvMap[0])
1038 if (scene->getWaterEnvMap())
1040 _SimpleWaterMat.setTexture(0, scene->getWaterEnvMap()->getEnvMap2D());
1042 else
1044 _SimpleWaterMat.setTexture(0, shape->_EnvMap[0]);
1047 else
1049 _SimpleWaterMat.setTexture(0, shape->_EnvMap[0]);
1053 if (shape->_ColorMap == NULL)
1055 // version with no color map
1056 if (!_EmbossTexture)
1058 _EmbossTexture = new CTextureEmboss;
1059 _EmbossTexture->setSlopeFactor(4.f);
1061 if (shape->_BumpMap[1] && shape->_BumpMap[1]->isBumpMap())
1063 CTextureBump *bm = static_cast<CTextureBump *>((ITexture *) shape->_BumpMap[1]);
1064 if (bm->getHeightMap())
1066 _EmbossTexture->setHeightMap(bm->getHeightMap());
1069 _SimpleWaterMat.setTexture(1, _EmbossTexture);
1070 _SimpleWaterMat.setTexCoordGen(1, true);
1071 _SimpleWaterMat.setTexCoordGenMode(1, CMaterial::TexCoordGenObjectSpace);
1072 double date = scene->getCurrentTime();
1073 CMatrix texMat;
1074 texMat.scale(CVector(shape->_HeightMapScale[1].x, shape->_HeightMapScale[1].y, 1.f));
1075 texMat.setPos(CVector(fmodf(shape->_HeightMapScale[1].x * obsPos.x, 1.f) + (float) fmod(date * shape->_HeightMapSpeed[1].x, 1),
1076 fmodf(shape->_HeightMapScale[1].y * obsPos.y, 1.f) + (float) fmod(date * shape->_HeightMapSpeed[1].y, 1),
1077 1.f)
1079 _SimpleWaterMat.enableUserTexMat(1, true);
1080 _SimpleWaterMat.setUserTexMat(1, texMat);
1082 else
1084 updateDiffuseMapMatrix();
1085 // version with a color map : it remplace the emboss texture
1086 _SimpleWaterMat.setTexture(1, shape->_ColorMap);
1087 _SimpleWaterMat.setTexCoordGen(1, true);
1088 _SimpleWaterMat.setTexCoordGenMode(1, CMaterial::TexCoordGenObjectSpace);
1089 CMatrix texMat;
1091 float mat[16] =
1093 _ColorMapMatColumn0.x, _ColorMapMatColumn1.x, 0, _ColorMapMatColumn0.x * obsPos.x + _ColorMapMatColumn1.x * obsPos.y + _ColorMapMatPos.x},
1094 _ColorMapMatColumn0.y, _ColorMapMatColumn1.y, 0, _ColorMapMatColumn0.y * obsPos.x + _ColorMapMatColumn1.y * obsPos.y + _ColorMapMatPos.y,
1095 0.f, 0.f, 1.f, 0.f,
1096 0.f, 0.f, 0.f, 1.f
1099 float mat[16] =
1101 _ColorMapMatColumn0.x, _ColorMapMatColumn0.y, 0.f, 0.f,
1102 _ColorMapMatColumn1.x, _ColorMapMatColumn1.y, 0.f, 0.f,
1103 0.f, 0.f, 1.f, 0.f,
1104 _ColorMapMatColumn0.x * obsPos.x + _ColorMapMatColumn1.x * obsPos.y + _ColorMapMatPos.x, _ColorMapMatColumn0.y * obsPos.x + _ColorMapMatColumn1.y * obsPos.y + _ColorMapMatPos.y, 0.f, 1.f
1106 texMat.set(mat);
1107 _SimpleWaterMat.enableUserTexMat(1, true);
1108 _SimpleWaterMat.setUserTexMat(1, texMat);
1113 //================================================
1114 void CWaterModel::computeClippedPoly()
1116 CWaterShape *shape = NLMISC::safe_cast<CWaterShape *>((IShape *) Shape);
1117 const std::vector<CPlane> &worldPyramid = getOwnerScene()->getClipTrav().WorldFrustumPyramid;
1118 _ClippedPoly.Vertices.resize(shape->_Poly.Vertices.size());
1119 uint k;
1120 for (k = 0; k < shape->_Poly.Vertices.size(); ++k)
1122 _ClippedPoly.Vertices[k].set(shape->_Poly.Vertices[k].x,
1123 shape->_Poly.Vertices[k].y,
1128 NLMISC::CPlane plvect[6];
1129 const NLMISC::CMatrix &viewMat = clipTrav.ViewMatrix;
1131 const sint numStepX = CWaterShape::getScreenXGridSize();
1132 const sint numStepY = CWaterShape::getScreenYGridSize();
1133 // 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
1134 float centerX = 0.5f * (clipTrav.Right + clipTrav.Left);
1135 const float fRight = centerX + (clipTrav.Right - centerX) * (-(float) CWaterShape::_XGridBorder + (float) numStepX) / numStepX;
1136 const float fLeft = centerX + (clipTrav.Left - centerX) * (-(float) CWaterShape::_XGridBorder + (float) numStepX) / numStepX;
1137 float centerY = 0.5f * (clipTrav.Bottom + clipTrav.Top);
1138 const float fTop = centerY + (clipTrav.Top - centerY) * (-(float) CWaterShape::_YGridBorder + (float) numStepY) / numStepY;
1139 const float fBottom = centerY + (clipTrav.Bottom - centerY) * (-(float) CWaterShape::_YGridBorder + (float) numStepY) / numStepY;
1140 // build pyramid corners
1141 const float nearDist = clipTrav.Near;
1142 const float farDist = clipTrav.Far;
1144 const NLMISC::CVector pfoc(0,0,0);
1145 const NLMISC::CVector lb( fLeft, nearDist, fBottom );
1146 const NLMISC::CVector lt( fLeft, nearDist, fTop );
1147 const NLMISC::CVector rb( fRight, nearDist, fBottom );
1148 const NLMISC::CVector rt(fRight, nearDist, fTop );
1149 const NLMISC::CVector lbfarDist(fLeft, farDist, fBottom);
1150 const NLMISC::CVector ltfarDist(fLeft, farDist, fTop );
1151 const NLMISC::CVector rtfarDist(fRight , farDist, fTop );
1153 plvect[0].make(lt, lb, rt); // near plane
1154 plvect[1].make(lbfarDist, ltfarDist, rtfarDist); // far plane
1155 plvect[2].make(pfoc, lt, lb);
1156 plvect[3].make(pfoc, rt, lt);
1157 plvect[4].make(pfoc, rb, rt);
1158 plvect[5].make(pfoc, lb, rb);
1159 const NLMISC::CMatrix pyramidMat = viewMat * getWorldMatrix();
1160 for (k = 0; k < worldPyramid.size(); ++k)
1162 plvect[k] = plvect[k] * pyramidMat; // put the plane in object space
1164 _ClippedPoly.clip(plvect, 6);
1166 static std::vector<CPlane> tp;
1167 tp.resize(worldPyramid.size());
1168 for(uint k = 0; k < tp.size(); ++k)
1170 tp[k] = worldPyramid[k] * getWorldMatrix();
1172 _ClippedPoly.clip(tp);
1175 // ***********************************************************************************************************
1176 void CWaterModel::unlink()
1178 if (!_Prev)
1180 nlassert(!_Next);
1181 return;
1183 if (_Next)
1185 _Next->_Prev = _Prev;
1187 *_Prev = _Next;
1188 _Next = NULL;
1189 _Prev = NULL;
1192 // ***********************************************************************************************************
1193 void CWaterModel::link()
1195 nlassert(_Next == NULL);
1196 CScene *scene = getOwnerScene();
1197 nlassert(scene);
1198 CRenderTrav &rt = scene->getRenderTrav();
1199 _Prev = &rt._FirstWaterModel;
1200 _Next = rt._FirstWaterModel;
1201 if (_Next)
1203 _Next->_Prev = &_Next;
1205 rt._FirstWaterModel = this;
1210 // ***********************************************************************************************************
1211 uint CWaterModel::getNumWantedVertices()
1213 H_AUTO( NL3D_Water_Render );
1214 nlassert(!_ClippedPoly.Vertices.empty());
1216 CRenderTrav &renderTrav = getOwnerScene()->getRenderTrav();
1217 if (!renderTrav.Perspective || forceWaterSimpleRender) return 0;
1218 // viewer pos in world space
1219 const NLMISC::CVector &obsPos = renderTrav.CamPos;
1220 // view matrix (inverted cam matrix)
1221 const NLMISC::CMatrix &viewMat = renderTrav.ViewMatrix;
1222 // plane z pos in world
1223 const float zHeight = getWorldMatrix().getPos().z;
1224 const sint numStepX = CWaterShape::getScreenXGridSize();
1225 const sint numStepY = CWaterShape::getScreenYGridSize();
1226 NLMISC::CMatrix modelMat;
1227 modelMat.setPos(NLMISC::CVector(obsPos.x, obsPos.y, zHeight));
1228 static NLMISC::CPolygon2D projPoly; // projected poly
1229 projPoly.Vertices.resize(_ClippedPoly.Vertices.size());
1230 // factor to project to grid units
1231 const float xFactor = numStepX * renderTrav.Near / (renderTrav.Right - renderTrav.Left);
1232 const float yFactor = numStepY * renderTrav.Near / (renderTrav.Top - renderTrav.Bottom);
1233 // project poly on near plane
1234 const NLMISC::CMatrix &projMat = viewMat * getWorldMatrix();
1235 uint k;
1236 for (k = 0; k < _ClippedPoly.Vertices.size(); ++k)
1238 // project points in the view
1239 NLMISC::CVector t = projMat * _ClippedPoly.Vertices[k];
1240 float invY = 1.f / t.y;
1241 projPoly.Vertices[k].set(xFactor * t.x * invY, yFactor * t.z * invY);
1243 // compute grid cells that are entirely inside
1244 projPoly.computeInnerBorders(_Inside, _MinYInside);
1245 // compute grid cells that are touched
1246 static NLMISC::CPolygon2D::TRasterVect border;
1247 sint minYBorder;
1248 projPoly.computeOuterBorders(border, minYBorder);
1249 // border - inside -> gives grid cells that must be clipped to fit the shape boundaries
1250 // Make sure that rasters array for inside has the same size that raster array for borders (by inserting NULL rasters)
1251 sint height = (sint)border.size();
1252 if (_Inside.empty())
1254 _MinYInside = minYBorder;
1256 sint bottomGap = (sint)(border.size() - _Inside.size());
1257 _Inside.resize(height);
1258 nlassert(minYBorder == _MinYInside);
1260 nlassert(bottomGap >= 0);
1261 if (bottomGap)
1263 for(sint y = height - bottomGap; y < height; ++y)
1265 nlassert (y >= 0 && y < (sint)_Inside.size());
1266 _Inside[y].first = border[y].first;
1267 _Inside[y].second = border[y].first - 1; // insert null raster
1271 for(sint y = 0; y < height - bottomGap; ++y)
1273 if (_Inside[y].first > _Inside[y].second)
1275 nlassert (y >= 0 && y < (sint)_Inside.size());
1276 _Inside[y].first = border[y].first;
1277 _Inside[y].second = border[y].first - 1;
1279 else if (border[y].first > border[y].second)
1281 nlassert (y >= 0 && y < (sint)_Inside.size());
1282 border[y].first = _Inside[y].first;
1283 border[y].second = _Inside[y].first - 1;
1286 // compute clip planes
1287 static std::vector<CPlane> clipPlanes;
1289 const CVector2f *prevVert = &projPoly.Vertices.back();
1290 const CVector2f *currVert = &projPoly.Vertices.front();
1291 uint numVerts = (uint)projPoly.Vertices.size();
1292 bool ccw = projPoly.isCCWOriented();
1293 clipPlanes.resize(numVerts);
1294 for(uint k = 0; k < numVerts; ++k)
1296 NLMISC::CVector v0;
1297 NLMISC::CVector v1;
1298 NLMISC::CVector v2;
1299 v0.set(prevVert->x, prevVert->y, 0.f);
1300 v1.set(currVert->x, currVert->y, 0.f);
1301 v2.set(prevVert->x, prevVert->y, (*currVert - *prevVert).norm());
1302 clipPlanes[k].make(v0, v1, v2);
1303 if (!ccw)
1305 clipPlanes[k].invert();
1307 prevVert = currVert;
1308 ++ currVert;
1310 // compute clipped tris
1311 _ClippedTriNumVerts.clear();
1312 _ClippedTris.clear();
1313 static NLMISC::CPolygon clipPoly;
1314 uint totalNumVertices = 0;
1315 // compute number of vertices for whole grid cells
1316 for(sint k = 0; k < (sint) border.size(); ++k)
1318 // left clipped blocks
1319 for (sint x = border[k].first; x < _Inside[k].first; ++x)
1321 clipPoly.Vertices.resize(4);
1322 clipPoly.Vertices[0].set((float) x, (float) (k + _MinYInside), 0.f);
1323 clipPoly.Vertices[1].set((float) (x + 1), (float) (k + _MinYInside), 0.f);
1324 clipPoly.Vertices[2].set((float) (x + 1), (float) (k + _MinYInside + 1), 0.f);
1325 clipPoly.Vertices[3].set((float) x, (float) (k + _MinYInside + 1), 0.f);
1326 clipPoly.clip(clipPlanes);
1327 if (!clipPoly.Vertices.empty())
1329 // backup result (will be unprojected later)
1330 _ClippedTriNumVerts.push_back((uint)clipPoly.Vertices.size());
1331 uint prevSize = (uint)_ClippedTris.size();
1332 _ClippedTris.resize(_ClippedTris.size() + clipPoly.Vertices.size());
1333 std::copy(clipPoly.Vertices.begin(), clipPoly.Vertices.end(), _ClippedTris.begin() + prevSize); // append to packed list
1334 totalNumVertices += ((uint)clipPoly.Vertices.size() - 2) * 3;
1337 // middle block, are not clipped, but count the number of wanted vertices
1338 if (_Inside[k].first <= _Inside[k].second)
1340 totalNumVertices += 6 * (_Inside[k].second - _Inside[k].first + 1);
1342 // right clipped blocks
1343 for (sint x = _Inside[k].second + 1; x <= border[k].second; ++x)
1345 clipPoly.Vertices.resize(4);
1346 clipPoly.Vertices[0].set((float) x, (float) (k + _MinYInside), 0.f);
1347 clipPoly.Vertices[1].set((float) (x + 1), (float) (k + _MinYInside), 0.f);
1348 clipPoly.Vertices[2].set((float) (x + 1), (float) (k + _MinYInside + 1), 0.f);
1349 clipPoly.Vertices[3].set((float) x, (float) (k + _MinYInside + 1), 0.f);
1350 clipPoly.clip(clipPlanes);
1351 if (!clipPoly.Vertices.empty())
1353 // backup result (will be unprojected later)
1354 _ClippedTriNumVerts.push_back((uint)clipPoly.Vertices.size());
1355 uint prevSize = (uint)_ClippedTris.size();
1356 _ClippedTris.resize(_ClippedTris.size() + clipPoly.Vertices.size());
1357 std::copy(clipPoly.Vertices.begin(), clipPoly.Vertices.end(), _ClippedTris.begin() + prevSize); // append to packed list
1358 totalNumVertices += ((uint)clipPoly.Vertices.size() - 2) * 3;
1362 return totalNumVertices;
1365 // ***********************************************************************************************************
1366 uint CWaterModel::fillVB(void *datas, uint startTri, IDriver &drv)
1368 H_AUTO( NL3D_Water_Render );
1369 if (drv.supportWaterShader())
1371 return fillVBHard(datas, startTri);
1373 else
1375 return fillVBSoft(datas, startTri);
1381 static const double WATER_WAVE_SPEED = 1.7;
1382 static const double WATER_WAVE_SCALE = 0.05;
1383 static const double WATER_WAVE_FREQ = 0.3;
1384 static const float WATER_WAVE_ATTEN = 0.2f;
1389 // compute single water vertex in software mode
1390 static
1391 #ifndef NL_DEBUG
1392 inline
1393 #endif
1394 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)
1396 CVector d = px * camI + py * camK + camJ;
1397 //nlassert(d.z > 0.f);
1398 float intersectionDist = denom / d.z;
1399 pos.x = intersectionDist * d.x;
1400 pos.y = intersectionDist * d.y;
1401 pos.z = 0.f;
1403 CVector R(- pos.x,
1404 - pos.y,
1405 - denom
1407 float dist = R.norm();
1408 if (dist)
1410 R /= dist;
1412 envMapTexCoord.set(- 0.5f * R.x + 0.5f, - 0.5f * R.y + 0.5f);
1413 if (dist)
1415 float invDist = 1.f / (WATER_WAVE_ATTEN * dist);
1416 if (invDist > 1.f) invDist = 1.f;
1417 // TODO : optimize cos if need (for now there are not much call per frame ...)
1418 envMapTexCoord.x += (float) (invDist * WATER_WAVE_SCALE * (float) cos(date + WATER_WAVE_FREQ * (camPos.x + pos.x)));
1422 // ***********************************************************************************************************
1423 uint CWaterModel::fillVBSoft(void *datas, uint startTri)
1425 _StartTri = (uint32) startTri;
1426 CRenderTrav &renderTrav = getOwnerScene()->getRenderTrav();
1427 const NLMISC::CMatrix &camMat = renderTrav.CamMatrix;
1428 const sint numStepX = CWaterShape::getScreenXGridSize();
1429 const sint numStepY = CWaterShape::getScreenYGridSize();
1430 CVector camI = camMat.getI() * (1.f / numStepX) * (renderTrav.Right - renderTrav.Left) / renderTrav.Near;
1431 CVector camJ = camMat.getJ();
1432 CVector camK = camMat.getK() * (1.f / numStepY) * (renderTrav.Top - renderTrav.Bottom) / renderTrav.Near;
1433 float obsZ = camMat.getPos().z;
1434 float denom = getWorldMatrix().getPos().z - obsZ;
1435 uint8 *dest = (uint8 *) datas + startTri * 3 * WATER_VERTEX_SOFT_SIZE;
1436 /*NLMISC::CVector eye = renderTrav.CamPos;
1437 eye.z -= getWorldMatrix().getPos().z; */
1438 NLMISC::CVector eye(0.f, 0.f, - denom);
1439 CVector R;
1440 CScene *scene = getOwnerScene();
1441 double date = WATER_WAVE_SPEED * scene->getCurrentTime();
1442 if (!_ClippedTriNumVerts.empty())
1444 const CVector2f *currVert = &_ClippedTris.front();
1445 static std::vector<CVector> unprojectedTriSoft;
1446 static std::vector<CVector2f> envMap;
1447 for(uint k = 0; k < _ClippedTriNumVerts.size(); ++k)
1449 unprojectedTriSoft.resize(_ClippedTriNumVerts[k]);
1450 envMap.resize(_ClippedTriNumVerts[k]);
1451 uint numVerts = _ClippedTriNumVerts[k];
1452 for(uint l = 0; l < _ClippedTriNumVerts[k]; ++l)
1454 computeWaterVertexSoft(currVert->x, currVert->y, unprojectedTriSoft[l], envMap[l], camI, camJ, camK, denom, date, camMat.getPos());
1455 ++ currVert;
1457 for(uint l = 0; l < numVerts - 2; ++l)
1459 *(CVector *) dest = unprojectedTriSoft[0];
1460 dest += sizeof(float[3]);
1461 *(CVector2f *) dest = envMap[0];
1462 dest += sizeof(float[2]);
1463 *(CVector *) dest = unprojectedTriSoft[l + 1];
1464 dest += sizeof(float[3]);
1465 *(CVector2f *) dest = envMap[l + 1];
1466 dest += sizeof(float[2]);
1467 *(CVector *) dest = unprojectedTriSoft[l + 2];
1468 dest += sizeof(float[3]);
1469 *(CVector2f *) dest = envMap[l + 2];
1470 dest += sizeof(float[2]);
1474 // TODO : optimize if needed
1475 for(sint k = 0; k < (sint) _Inside.size(); ++k)
1477 sint y = k + _MinYInside;
1478 CVector proj[4];
1479 CVector2f envMap[4];
1480 if (_Inside[k].first <= _Inside[k].second)
1482 // middle block, are not clipped, but count the number of wanted vertices
1483 for(sint x = _Inside[k].first; x <= _Inside[k].second; ++x)
1485 computeWaterVertexSoft((float) x, (float) y, proj[0], envMap[0], camI, camJ, camK, denom, date, camMat.getPos());
1486 computeWaterVertexSoft((float)(x + 1), (float) y, proj[1], envMap[1], camI, camJ, camK, denom, date, camMat.getPos());
1487 computeWaterVertexSoft((float) (x + 1), (float) (y + 1), proj[2], envMap[2], camI, camJ, camK, denom, date, camMat.getPos());
1488 computeWaterVertexSoft((float) x, (float) (y + 1), proj[3], envMap[3], camI, camJ, camK, denom, date, camMat.getPos());
1490 *(CVector *) dest = proj[0];
1491 dest += sizeof(float[3]);
1492 *(CVector2f *) dest = envMap[0];
1493 dest += sizeof(float[2]);
1494 *(CVector *) dest = proj[2];
1495 dest += sizeof(float[3]);
1496 *(CVector2f *) dest = envMap[2];
1497 dest += sizeof(float[2]);
1498 *(CVector *) dest = proj[1];
1499 dest += sizeof(float[3]);
1500 *(CVector2f *) dest = envMap[1];
1501 dest += sizeof(float[2]);
1502 *(CVector *) dest = proj[0];
1503 dest += sizeof(float[3]);
1504 *(CVector2f *) dest = envMap[0];
1505 dest += sizeof(float[2]);
1506 *(CVector *) dest = proj[3];
1507 dest += sizeof(float[3]);
1508 *(CVector2f *) dest = envMap[3];
1509 dest += sizeof(float[2]);
1510 *(CVector *) dest = proj[2];
1511 dest += sizeof(float[3]);
1512 *(CVector2f *) dest = envMap[2];
1513 dest += sizeof(float[2]);
1517 nlassert((dest - (uint8 * ) datas) % (3 * WATER_VERTEX_SOFT_SIZE) == 0);
1518 uint endTri = (uint)(dest - (uint8 * ) datas) / (3 * WATER_VERTEX_SOFT_SIZE);
1519 _NumTris = endTri - _StartTri;
1520 return endTri;
1523 // compute single water vertex for hardware render
1524 static
1525 #ifndef NL_DEBUG
1526 inline
1527 #endif
1528 void computeWaterVertexHard(float px, float py, CVector &pos, const CVector &camI, const CVector &camJ, const CVector &camK, float denom)
1530 CVector d = px * camI + py * camK + camJ;
1531 float intersectionDist = denom / d.z;
1532 pos.x = intersectionDist * d.x;
1533 pos.y = intersectionDist * d.y;
1534 pos.z = 0.f;
1537 // ***********************************************************************************************************
1538 uint CWaterModel::fillVBHard(void *datas, uint startTri)
1540 _StartTri = (uint32) startTri;
1541 CRenderTrav &renderTrav = getOwnerScene()->getRenderTrav();
1542 const NLMISC::CMatrix &camMat = renderTrav.CamMatrix;
1543 const sint numStepX = CWaterShape::getScreenXGridSize();
1544 const sint numStepY = CWaterShape::getScreenYGridSize();
1545 CVector camI = camMat.getI() * (1.f / numStepX) * (renderTrav.Right - renderTrav.Left) / renderTrav.Near;
1546 CVector camJ = camMat.getJ();
1547 CVector camK = camMat.getK() * (1.f / numStepY) * (renderTrav.Top - renderTrav.Bottom) / renderTrav.Near;
1548 float obsZ = camMat.getPos().z;
1549 float denom = getWorldMatrix().getPos().z - obsZ;
1550 uint8 *dest = (uint8 *) datas + startTri * WATER_VERTEX_HARD_SIZE * 3;
1551 if (!_ClippedTriNumVerts.empty())
1553 const CVector2f *currVert = &_ClippedTris.front();
1554 static std::vector<CVector> unprojectedTri;
1555 for(uint k = 0; k < _ClippedTriNumVerts.size(); ++k)
1557 unprojectedTri.resize(_ClippedTriNumVerts[k]);
1558 uint numVerts = _ClippedTriNumVerts[k];
1559 for(uint l = 0; l < _ClippedTriNumVerts[k]; ++l)
1561 computeWaterVertexHard(currVert->x, currVert->y, unprojectedTri[l], camI, camJ, camK, denom);
1562 ++ currVert;
1564 for(uint l = 0; l < numVerts - 2; ++l)
1566 *(CVector *) dest = unprojectedTri[0];
1567 dest += WATER_VERTEX_HARD_SIZE;
1568 *(CVector *) dest = unprojectedTri[l + 1];
1569 dest += WATER_VERTEX_HARD_SIZE;
1570 *(CVector *) dest = unprojectedTri[l + 2];
1571 dest += WATER_VERTEX_HARD_SIZE;
1575 // TODO : optimize if needed
1576 for(sint k = 0; k < (sint) _Inside.size(); ++k)
1578 sint y = k + _MinYInside;
1579 CVector proj[4];
1580 if (_Inside[k].first <= _Inside[k].second)
1582 // middle block, are not clipped, but count the number of wanted vertices
1583 for(sint x = _Inside[k].first; x <= _Inside[k].second; ++x)
1585 computeWaterVertexHard((float) x, (float) y, proj[0], camI, camJ, camK, denom);
1586 computeWaterVertexHard((float) (x + 1), (float) y, proj[1], camI, camJ, camK, denom);
1587 computeWaterVertexHard((float) (x + 1), (float) (y + 1), proj[2], camI, camJ, camK, denom);
1588 computeWaterVertexHard((float) x, (float) (y + 1), proj[3], camI, camJ, camK, denom);
1590 *(CVector *) dest = proj[0];
1591 dest += WATER_VERTEX_HARD_SIZE;
1592 *(CVector *) dest = proj[2];
1593 dest += WATER_VERTEX_HARD_SIZE;
1594 *(CVector *) dest = proj[1];
1595 dest += WATER_VERTEX_HARD_SIZE;
1596 *(CVector *) dest = proj[0];
1597 dest += WATER_VERTEX_HARD_SIZE;
1598 *(CVector *) dest = proj[3];
1599 dest += WATER_VERTEX_HARD_SIZE;
1600 *(CVector *) dest = proj[2];
1601 dest += WATER_VERTEX_HARD_SIZE;
1605 nlassert((dest - (uint8 * ) datas) % (3 * WATER_VERTEX_HARD_SIZE) == 0);
1606 uint endTri = (uint)(dest - (uint8 * ) datas) / (3 * WATER_VERTEX_HARD_SIZE);
1607 _NumTris = endTri - _StartTri;
1608 return endTri;
1615 // ***************************************************************************************************************
1616 void CWaterModel::traverseRender()
1618 H_AUTO( NL3D_Water_Render );
1620 CRenderTrav &renderTrav = getOwnerScene()->getRenderTrav();
1621 IDriver *drv = renderTrav.getDriver();
1622 CWaterShape *shape = NLMISC::safe_cast<CWaterShape *>((IShape *) Shape);
1623 const NLMISC::CVector &obsPos = renderTrav.CamPos;
1624 const float zHeight = getWorldMatrix().getPos().z;
1626 if (!renderTrav.Perspective || forceWaterSimpleRender)
1628 // not supported, simple uniform render
1629 drv->setupModelMatrix(getWorldMatrix());
1630 static CMaterial waterMat;
1631 static bool initDone = false;
1632 if (!initDone)
1634 waterMat.initUnlit();
1635 waterMat.setBlend(true);
1636 waterMat.setSrcBlend(CMaterial::srcalpha);
1637 waterMat.setDstBlend(CMaterial::invsrcalpha);
1638 waterMat.setBlend(true);
1639 waterMat.setDoubleSided(true);
1640 waterMat.setLighting(false);
1642 waterMat.setColor(shape->computeEnvMapMeanColor());
1643 static std::vector<NLMISC::CTriangleUV> tris;
1644 const NLMISC::CPolygon2D &poly = shape->getShape();
1645 tris.clear();
1646 for(sint k = 0; k < (sint) poly.Vertices.size() - 2; ++k)
1648 NLMISC::CTriangleUV truv;
1649 truv.Uv0.set(0.f, 0.f);
1650 truv.Uv1.set(0.f, 0.f);
1651 truv.Uv2.set(0.f, 0.f);
1652 truv.V0.set(poly.Vertices[0].x, poly.Vertices[0].y, 0.f);
1653 truv.V1.set(poly.Vertices[k + 1].x, poly.Vertices[k + 1].y, 0.f);
1654 truv.V2.set(poly.Vertices[k + 2].x, poly.Vertices[k + 2].y, 0.f);
1655 tris.push_back(truv);
1657 CDRU::drawTrianglesUnlit(tris, waterMat, *drv);
1659 else
1661 NLMISC::CMatrix modelMat;
1662 modelMat.setPos(NLMISC::CVector(obsPos.x, obsPos.y, zHeight));
1663 drv->setupModelMatrix(modelMat);
1664 bool isAbove = obsPos.z > getWorldMatrix().getPos().z;
1665 CVertexBuffer &vb = renderTrav.Scene->getWaterVB();
1666 if (drv->supportWaterShader())
1668 setupMaterialNVertexShader(drv, shape, obsPos, isAbove, zHeight);
1669 nlassert(vb.getNumVertices() > 0);
1670 drv->activeVertexBuffer(vb);
1671 drv->renderRawTriangles(CWaterModel::_WaterMat, _StartTri, _NumTris);
1672 drv->activeVertexProgram(NULL);
1674 else
1676 setupSimpleRender(shape, obsPos, isAbove);
1677 drv->activeVertexBuffer(vb);
1678 drv->activeVertexProgram(NULL);
1679 drv->renderRawTriangles(CWaterModel::_SimpleWaterMat, _StartTri, _NumTris);
1686 // ***********************************************************************************************************
1687 bool CWaterModel::clip()
1689 H_AUTO( NL3D_Water_Render );
1690 CRenderTrav &renderTrav= getOwnerScene()->getRenderTrav();
1691 if (renderTrav.CamPos.z == getWorldMatrix().getPos().z) return false;
1692 if(Shape)
1694 computeClippedPoly();
1695 if (_ClippedPoly.Vertices.empty()) return false;
1696 // unlink from water model list
1697 unlink();
1698 // link into water model list
1699 link();
1700 return true;
1702 else
1703 return false;
1708 // struct used to build vertices for the simple shader
1709 struct CSimpleVertexInfo
1711 NLMISC::CVector XFormPos;
1712 NLMISC::CUV UV;
1716 // ***********************************************************************************************************
1718 void CWaterModel::doSimpleRender(IDriver *drv)
1720 if (_ClippedPoly.Vertices.empty()) return;
1721 // rendering of water when no vertex / pixel shaders are available
1722 CWaterShape *shape = NLMISC::safe_cast<CWaterShape *>((IShape *) Shape);
1723 CRenderTrav &renderTrav = getOwnerScene()->getRenderTrav();
1724 static bool init = false;
1725 if (!init)
1727 // setup the material, no special shader is used here
1728 _SimpleWaterMat.setLighting(false);
1729 _SimpleWaterMat.setDoubleSided(true);
1730 _SimpleWaterMat.setColor(NLMISC::CRGBA::White);
1732 _SimpleWaterMat.setBlend(true);
1733 _SimpleWaterMat.setSrcBlend(CMaterial::srcalpha);
1734 _SimpleWaterMat.setDstBlend(CMaterial::invsrcalpha);
1735 _SimpleWaterMat.setZWrite(true);
1736 _SimpleWaterMat.setShader(CMaterial::Normal);
1738 // stage 0
1739 _SimpleWaterMat.texEnvOpRGB(0, CMaterial::Replace);
1740 _SimpleWaterMat.texEnvOpAlpha(0, CMaterial::Replace);
1741 _SimpleWaterMat.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
1742 _SimpleWaterMat.texEnvArg0Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha);
1744 // stage 1
1745 _SimpleWaterMat.texEnvOpRGB(1, CMaterial::Modulate);
1746 _SimpleWaterMat.texEnvOpAlpha(1, CMaterial::Modulate);
1747 _SimpleWaterMat.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
1748 _SimpleWaterMat.texEnvArg0Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha);
1749 _SimpleWaterMat.texEnvArg1RGB(0, CMaterial::Previous, CMaterial::SrcColor);
1750 _SimpleWaterMat.texEnvArg1Alpha(0, CMaterial::Previous, CMaterial::SrcAlpha);
1752 // setup the vb : one position & two tex coords
1753 _SimpleRenderVB.setVertexFormat(CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag | CVertexBuffer::TexCoord1Flag);
1754 init = true;
1757 const NLMISC::CMatrix &worldMatrix = getWorldMatrix();
1758 const NLMISC::CVector &obsPos = renderTrav.CamPos;
1760 // setup the material
1761 bool isAbove = obsPos.z > worldMatrix.getPos().z;
1763 // envmap is always present and is in stage 0
1764 CScene *scene = getOwnerScene();
1765 if (!isAbove && shape->_EnvMap[1])
1767 if (shape->_UsesSceneWaterEnvMap[1])
1769 if (scene->getWaterEnvMap())
1771 _SimpleWaterMat.setTexture(0, scene->getWaterEnvMap()->getEnvMap2D());
1773 else
1775 _SimpleWaterMat.setTexture(0, shape->_EnvMap[1]);
1778 else
1780 _SimpleWaterMat.setTexture(0, shape->_EnvMap[1]);
1783 else
1785 if (shape->_UsesSceneWaterEnvMap[0])
1787 if (scene->getWaterEnvMap())
1789 _SimpleWaterMat.setTexture(0, scene->getWaterEnvMap()->getEnvMap2D());
1791 else
1793 _SimpleWaterMat.setTexture(0, shape->_EnvMap[0]);
1796 else
1798 _SimpleWaterMat.setTexture(0, shape->_EnvMap[0]);
1802 static std::vector<CSimpleVertexInfo> verts;
1803 static CIndexBuffer indices;
1805 NLMISC::CPolygon2D &poly = shape->_Poly;
1806 uint numVerts = poly.Vertices.size();
1807 uint k;
1809 if (shape->_ColorMap == NULL)
1811 // version with no color map
1812 if (!_EmbossTexture)
1814 _EmbossTexture = new CTextureEmboss;
1815 _EmbossTexture->setSlopeFactor(4.f);
1817 if (shape->_BumpMap[1] && shape->_BumpMap[1]->isBumpMap())
1819 CTextureBump *bm = static_cast<CTextureBump *>((ITexture *) shape->_BumpMap[1]);
1820 if (bm->getHeightMap())
1822 _EmbossTexture->setHeightMap(bm->getHeightMap());
1825 _SimpleWaterMat.setTexture(1, _EmbossTexture);
1826 _SimpleRenderVB.setNumVertices(numVerts);
1827 // retrieve current time
1828 float date = 0.001f * (NLMISC::CTime::getLocalTime() & 0xffffff); // must keep some precision.
1829 // Compute tex coordinates for emboss first.
1830 // On some 3D chip, textures coords can't grow too mush or texture filtering loose accuracy.
1831 // So we must keep texCoord as low as possible.
1833 verts.resize(numVerts);
1834 for(k = 0; k < numVerts; ++k)
1836 verts[k].XFormPos = worldMatrix * NLMISC::CVector(poly.Vertices[k].x, poly.Vertices[k].y ,0.f);
1837 verts[k].UV.U = shape->_HeightMapScale[0].x * verts[k].XFormPos.x + date * shape->_HeightMapSpeed[0].x;
1838 verts[k].UV.V = shape->_HeightMapScale[0].y * verts[k].XFormPos.y + date * shape->_HeightMapSpeed[0].y;
1840 // get min tex coords
1841 float minU = verts[0].UV.U;
1842 float minV = verts[0].UV.V;
1843 for(k = 1; k < numVerts; ++k)
1845 minU = std::min(minU, verts[k].UV.U);
1846 minV = std::min(minV, verts[k].UV.V);
1849 minU = floorf(minU);
1850 minV = floorf(minV);
1852 CVertexBufferReadWrite vba;
1853 _SimpleRenderVB.lock (vba);
1854 uint8 *data = (uint8 *) vba.getVertexCoordPointer();
1855 for(k = 0; k < numVerts; ++k)
1857 ((NLMISC::CVector *) data)->set(poly.Vertices[k].x, poly.Vertices[k].y, 0.f);
1858 data += sizeof(NLMISC::CVector);
1859 // texture coord 0 is reflected vector into envmap
1860 // xform position in world space to compute the reflection
1861 CVector surfToEye = (obsPos - verts[k].XFormPos).normed();
1862 // we assume that normal is (0, 0, 1)
1863 * (float *) data = 0.5f - 0.5f * surfToEye.x;
1864 ((float *) data)[1] = 0.5f - 0.5f * surfToEye.y;
1865 data += sizeof(float[2]);
1866 // texture coord 1 is the embossed map
1867 * (float *) data = verts[k].UV.U - minU;
1868 ((float *) data)[1] = verts[k].UV.V - minV;
1869 data += sizeof(float[2]);
1872 else
1874 // version with a color map : it remplace the emboss texture
1875 _SimpleWaterMat.setTexture(1, shape->_ColorMap);
1876 _SimpleRenderVB.setNumVertices(numVerts);
1877 CVertexBufferReadWrite vba;
1878 _SimpleRenderVB.lock (vba);
1880 uint8 *data = (uint8 *) vba.getVertexCoordPointer();
1881 for(k = 0; k < numVerts; ++k)
1883 * (NLMISC::CVector *) data = poly.Vertices[k];
1884 data += sizeof(CVector);
1885 // texture coord 0 is reflected vector into envmap
1886 // xform position in world space to compute the reflection
1887 NLMISC::CVector xformPos = worldMatrix * poly.Vertices[k];
1888 NLMISC::CVector surfToEye = (obsPos - xformPos).normed();
1889 // we assume that normal is (0, 0, 1)
1890 * (float *) data = 0.5f - 0.5f * surfToEye.x;
1891 ((float *) data)[1] = 0.5f * - 0.5f * surfToEye.y;
1892 data += sizeof(float[2]);
1893 // texture coord 1 is the color map
1894 * (float *) data = shape->_ColorMapMatColumn0.x * xformPos.x + shape->_ColorMapMatColumn1.x * xformPos.y + shape->_ColorMapMatPos.x;
1895 ((float *) data)[1] = shape->_ColorMapMatColumn0.y * xformPos.x + shape->_ColorMapMatColumn1.y * xformPos.y + shape->_ColorMapMatPos.y;
1896 data += sizeof(float[2]);
1900 drv->activeVertexProgram(NULL);
1901 drv->setupModelMatrix(worldMatrix);
1902 drv->activeVertexBuffer(_SimpleRenderVB);
1904 // create an index buffer to do the display
1905 indices.setNumIndexes((numVerts - 2) * 3);
1907 CIndexBufferReadWrite ibaWrite;
1908 indices.lock (ibaWrite);
1909 uint32 *ptr = ibaWrite.getPtr();
1910 for(k = 0; k < (numVerts - 2); ++k)
1913 ptr[ k * 3 ] = 0;
1914 ptr[ k * 3 + 1 ] = k + 1;
1915 ptr[ k * 3 + 2 ] = k + 2;
1918 drv->setupMaterial(_SimpleWaterMat);
1919 drv->activeIndexBuffer(indices);
1920 drv->renderSimpleTriangles(0, numVerts - 2);
1924 // ***********************************************************************************************************
1925 void CWaterModel::updateDiffuseMapMatrix(bool force /* = false*/)
1927 if (compareMatrixDate(_MatrixUpdateDate) ||force)
1929 CWaterShape *shape = NLMISC::safe_cast<CWaterShape *>((IShape *) Shape);
1930 if (shape)
1932 _MatrixUpdateDate = getMatrixDate();
1933 // update the uv matrix
1934 CMatrix uvMat;
1935 uvMat.setRot(CVector(shape->_ColorMapMatColumn0.x, shape->_ColorMapMatColumn0.y, 0.f),
1936 CVector(shape->_ColorMapMatColumn1.x, shape->_ColorMapMatColumn1.y, 0.f),
1937 CVector(shape->_ColorMapMatPos.x, shape->_ColorMapMatPos.y, 1.f));
1938 CMatrix xformMat;
1939 CMatrix invMat = this->getWorldMatrix().inverted();
1940 xformMat.setRot(CVector(invMat.getI().x, invMat.getI().y, 0.f),
1941 CVector(invMat.getJ().x, invMat.getJ().y, 0.f),
1942 CVector(invMat.getPos().x, invMat.getPos().y, 1.f));
1943 uvMat = uvMat * xformMat;
1944 _ColorMapMatColumn0.set(uvMat.getI().x, uvMat.getI().y);
1945 _ColorMapMatColumn1.set(uvMat.getJ().x, uvMat.getJ().y);
1946 _ColorMapMatPos.set(uvMat.getK().x, uvMat.getK().y);
1951 // ***************************************************************************
1952 void CWaterModel::debugDumpMem(void* &clippedPolyBegin, void* &clippedPolyEnd)
1954 clippedPolyBegin= (void*)(&*_ClippedPoly.Vertices.begin());
1955 clippedPolyEnd= (void*)(&*_ClippedPoly.Vertices.end());
1958 // ***************************************************************************
1959 void CWaterModel::debugClearClippedPoly()
1961 _ClippedPoly.Vertices.clear();
1964 //=======================================================================================
1965 // wave maker implementation
1966 //=======================================================================================
1968 CWaveMakerModel::CWaveMakerModel() : _Time(0)
1970 // AnimDetail behavior: Must be traversed in AnimDetail, even if no channel mixer registered
1971 CTransform::setIsForceAnimDetail(true);
1974 //================================================
1976 void CWaveMakerModel::registerBasic()
1978 CScene::registerModel(WaveMakerModelClassId, TransformShapeId, CWaveMakerModel::creator);
1981 //================================================
1983 ITrack* CWaveMakerModel::getDefaultTrack (uint valueId)
1985 nlassert(Shape);
1986 CWaveMakerShape *ws = NLMISC::safe_cast<CWaveMakerShape *>((IShape *) Shape);
1987 switch (valueId)
1989 case PosValue: return ws->getDefaultPos(); break;
1990 default: // delegate to parent
1991 return CTransformShape::getDefaultTrack(valueId);
1992 break;
1996 //================================================
1997 void CWaveMakerModel::traverseAnimDetail()
1999 CTransformShape::traverseAnimDetail();
2000 nlassert(getOwnerScene());
2001 /// get the shape
2002 CWaveMakerShape *wms = NLMISC::safe_cast<CWaveMakerShape *>((IShape *) Shape);
2003 const NLMISC::CVector worldPos = getWorldMatrix().getPos();
2004 const CVector2f pos2d(worldPos.x, worldPos.y);
2005 /// get the water height map
2006 CWaterHeightMap &whm = GetWaterPoolManager().getPoolByID(wms->_PoolID);
2007 // get the time delta
2008 const TAnimationTime deltaT = std::min(getOwnerScene()->getEllapsedTime(), (TAnimationTime) whm.getPropagationTime());
2009 _Time += deltaT;
2010 if (!wms->_ImpulsionMode)
2012 whm.perturbate(pos2d, wms->_Intensity * cosf(2.f / wms->_Period * (float) NLMISC::Pi * _Time), wms->_Radius);
2014 else
2016 if (_Time > wms->_Period)
2018 _Time -= wms->_Period;
2019 whm.perturbate(pos2d, wms->_Intensity, wms->_Radius);
2024 } // NL3D