Linux multi-monitor fullscreen support
[ryzomcore.git] / ryzom / client / src / decal.cpp
blob24e3e756b923f95f2c1b7d71eae0e03f8cf84ecc
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2020 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 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "stdpch.h"
22 #include "decal.h"
24 #include "nel/3d/shadow_map.h"
25 #include "nel/3d/texture_file.h"
26 #include "nel/3d/scene.h"
27 #include "nel/3d/driver_user.h"
28 #include "nel/3d/landscape.h"
29 #include "nel/3d/landscape_model.h"
30 #include "nel/3d/landscape_user.h"
31 #include "nel/3d/scene_user.h"
32 #include "nel/3d/texture_user.h"
34 // TMP TMP
35 #include "nel/3d/texture_mem.h"
37 #include "nel/misc/aabbox.h"
38 #include "nel/misc/vector_2f.h"
39 #include "nel/misc/plane.h"
41 #include "global.h"
43 #include "interface_v3/interface_manager.h"
45 using namespace NL3D;
46 using namespace NLMISC;
48 #ifdef DEBUG_NEW
49 #define new DEBUG_NEW
50 #endif
52 CDecalRenderList DecalRenderList;
54 extern uint SkipFrame;
56 NL3D::CVertexBuffer CDecal::_VB;
57 bool CDecal::_VBInitialized = false;
61 static const char *DecalAttenuationVertexProgramCode =
62 "!!VP1.0 \n\
63 DP4 o[HPOS].x, c[0], v[0]; #transform vertex in view space \n\
64 DP4 o[HPOS].y, c[1], v[0]; \n\
65 DP4 o[HPOS].z, c[2], v[0]; \n\
66 DP4 o[HPOS].w, c[3], v[0]; \n\
67 # transform texcoord 0 \n\
68 DP4 o[TEX0].x, c[4], v[0]; \n\
69 DP4 o[TEX0].y, c[5], v[0]; \n\
70 #compute distance from camera \n\
71 ADD R0, v[0], -c[6]; \n\
72 DP3 R0.x, R0, R0; \n\
73 RSQ R0.x, R0.x; \n\
74 RCP R0.x, R0.x; \n\
75 MUL o[COL0].xyz, c[8], v[3]; \n\
76 #compute attenuation with distance \n\
77 MAD R0.w, R0.x, c[7].x, c[7].y; \n\
78 # clamp in [0, 1] \n\
79 MIN R0.w, R0.w, c[7].w; \n\
80 MAX R0.w, R0.w, c[7].z; \n\
81 #compute bottom blend \n\
82 MAD R1.x, v[0].z, c[11].x, c[11].y; \n\
83 MIN R1.x, R1.x, c[7].w; \n\
84 MAX R1.x, R1.x, c[7].z; \n\
85 MUL R0.w, R1.x, R0.w; \n\
86 #compute top blend \n\
87 MAD R1.x, v[0].z, c[11].z, c[11].w; \n\
88 MIN R1.x, R1.x, c[7].w; \n\
89 MAX R1.x, R1.x, c[7].z; \n\
90 MUL R0.w, R1.x, R0.w; \n\
91 #apply vertex alpha \n\
92 MUL o[COL0].w, v[3], R0.w; \n\
93 END \n";
95 class CVertexProgramDecalAttenuation : public CVertexProgram
97 public:
98 struct CIdx
100 // 0-3 mvp
101 uint WorldToUV0; // 4
102 uint WorldToUV1; // 5
103 uint RefCamDist; // 6
104 uint DistScaleBias; // 7
105 uint Diffuse; // 8
106 // 9
107 // 10
108 uint BlendScale; // 11
110 CVertexProgramDecalAttenuation()
112 // nelvp
114 CSource *source = new CSource();
115 source->Profile = nelvp;
116 source->DisplayName = "nelvp/DecalAttenuation";
117 source->setSourcePtr(DecalAttenuationVertexProgramCode);
118 source->ParamIndices["modelViewProjection"] = 0;
119 source->ParamIndices["worldToUV0"] = 4;
120 source->ParamIndices["worldToUV1"] = 5;
121 source->ParamIndices["refCamDist"] = 6;
122 source->ParamIndices["distScaleBias"] = 7;
123 source->ParamIndices["diffuse"] = 8;
124 source->ParamIndices["blendScale"] = 11;
125 addSource(source);
127 // TODO_VP_GLSL
129 ~CVertexProgramDecalAttenuation()
133 virtual void buildInfo()
135 m_Idx.WorldToUV0 = getUniformIndex("worldToUV0");
136 nlassert(m_Idx.WorldToUV0 != std::numeric_limits<uint>::max());
137 m_Idx.WorldToUV1 = getUniformIndex("worldToUV1");
138 nlassert(m_Idx.WorldToUV1 != std::numeric_limits<uint>::max());
139 m_Idx.RefCamDist = getUniformIndex("refCamDist");
140 nlassert(m_Idx.RefCamDist != std::numeric_limits<uint>::max());
141 m_Idx.DistScaleBias = getUniformIndex("distScaleBias");
142 nlassert(m_Idx.DistScaleBias != std::numeric_limits<uint>::max());
143 m_Idx.Diffuse = getUniformIndex("diffuse");
144 nlassert(m_Idx.Diffuse != std::numeric_limits<uint>::max());
145 m_Idx.BlendScale = getUniformIndex("blendScale");
146 nlassert(m_Idx.BlendScale != std::numeric_limits<uint>::max());
148 inline const CIdx &idx() const { return m_Idx; }
149 private:
150 CIdx m_Idx;
153 static NLMISC::CSmartPtr<CVertexProgramDecalAttenuation> DecalAttenuationVertexProgram;
156 typedef CShadowPolyReceiver::CRGBAVertex CRGBAVertex;
158 // ****************************************************************************
159 CDecal::CDecal()
161 if (!DecalAttenuationVertexProgram)
163 DecalAttenuationVertexProgram = new CVertexProgramDecalAttenuation();
166 // initialized in render() as depends on scene
167 _ShadowMap = NULL;
168 _Material.initUnlit();
169 _Diffuse = CRGBA::White;
170 _Emissive = CRGBA::Black;
172 _Material.setBlend(true);
173 _Material.setSrcBlend(CMaterial::srcalpha);
174 _Material.setDstBlend(CMaterial::invsrcalpha);
175 _Material.setZWrite(false);
176 _Material.setDoubleSided(true);
177 // diffuse color applied at first stage
178 _Material.texEnvOpRGB(0, CMaterial::Modulate);
179 _Material.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);
180 _Material.texEnvArg1RGB(0, CMaterial::Diffuse, CMaterial::SrcColor);
181 _Material.texEnvOpAlpha(0, CMaterial::Modulate);
182 _Material.texEnvArg0Alpha(0, CMaterial::Diffuse, CMaterial::SrcAlpha);
183 _Material.texEnvArg1Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha);
185 _Material.texEnvOpRGB(1, CMaterial::Add);
186 _Material.texEnvArg0RGB(1, CMaterial::Previous, CMaterial::SrcColor);
187 _Material.texEnvArg1RGB(1, CMaterial::Constant, CMaterial::SrcColor);
188 _Material.texEnvOpAlpha(1, CMaterial::Modulate);
189 _Material.texEnvArg0Alpha(1, CMaterial::Previous, CMaterial::SrcAlpha);
190 _Material.texEnvArg1Alpha(1, CMaterial::Constant, CMaterial::SrcAlpha);
192 setEmissive(CRGBA::Black);
193 setDiffuse(CRGBA::White);
195 _Material.setAlphaTest(true);
196 _Material.setAlphaTestThreshold(1.f / 255.f);
198 _Touched = true;
199 _ClipDownFacing = false;
200 _WorldMatrix.get(_WorldMatrixFlat);
201 setWorldMatrix(_WorldMatrix);
203 _BottomBlendZMin = -10100.f;
204 _BottomBlendZMax = -10000.f;
205 _TopBlendZMin = 10000.f;
206 _TopBlendZMax = 10100.f;
209 // ****************************************************************************
210 void CDecal::setCustomUVMatrix(bool on, const NLMISC::CMatrix &matrix)
212 if (_CustomUVMatrix.set(on, matrix))
214 _Touched = true;
218 // ****************************************************************************
219 const std::string &CDecal::getTextureFileName() const
221 CTextureFile *tf = dynamic_cast<CTextureFile *>(_Material.getTexture(0));
222 if (tf) return tf->getFileName();
223 static std::string emptyString;
224 return emptyString;
227 // ****************************************************************************
228 void CDecal::setupMaterialColor()
230 _Material.texConstantColor(1, NLMISC::CRGBA(_Emissive.R, _Emissive.G, _Emissive.B, _Diffuse.A));
233 // ****************************************************************************
234 void CDecal::setEmissive(NLMISC::CRGBA emissive)
236 _Emissive = emissive;
237 setupMaterialColor();
240 // ****************************************************************************
241 void CDecal::setDiffuse(NLMISC::CRGBA diffuse)
243 _Diffuse = diffuse;
244 setupMaterialColor();
247 // ****************************************************************************
248 CRGBA CDecal::getDiffuse() const
250 return _Diffuse;
253 // ****************************************************************************
254 CDecal::~CDecal()
256 if (_ShadowMap)
258 delete _ShadowMap;
259 _ShadowMap = NULL;
263 // ****************************************************************************
264 void CDecal::setTexture(const std::string &fileName, bool clampU, bool clampV, bool filtered)
266 if (getTextureFileName() != fileName)
268 CInterfaceManager *im = CInterfaceManager::getInstance();
269 CViewRenderer &vr = *CViewRenderer::getInstance();
270 UTexture *tex = vr.getGlobalTexture(fileName);
271 if (tex != NULL)
273 _Material.setTexture(0, (dynamic_cast<NL3D::CTextureUser *>(tex))->getITexture());
275 else
277 _Material.setTexture(0, fileName.empty() ? NULL : new CTextureFile(fileName));
279 if (_Material.getTexture(0))
281 _Material.getTexture(0)->setUploadFormat(ITexture::RGBA8888); // don't want ugly dxtc mipmaps for most decals
284 if (_Material.getTexture(0))
286 _Material.getTexture(0)->setWrapS(clampU ? ITexture::Clamp : ITexture::Repeat);
287 _Material.getTexture(0)->setWrapT(clampV ? ITexture::Clamp : ITexture::Repeat);
288 if (filtered)
290 _Material.getTexture(0)->setFilterMode(ITexture::Linear , ITexture::LinearMipMapLinear);
292 else
294 _Material.getTexture(0)->setFilterMode(ITexture::Nearest, ITexture::NearestMipMapOff);
296 _Material.setTexture(1, _Material.getTexture(0));
298 else
300 _Material.setTexture(1, NULL);
304 // ****************************************************************************
305 void CDecal::setWorldMatrix(const NLMISC::CMatrix &matrix)
307 float newMat[16];
308 matrix.get(newMat);
309 if (std::equal(newMat, newMat + 16, _WorldMatrixFlat)) return;
310 _WorldMatrix = matrix;
311 _WorldMatrix.get(_WorldMatrixFlat);
312 _InvertedWorldMatrix = matrix.inverted();
313 _Touched = true;
314 const float bboxHeight = 10000.f;
315 static const NLMISC::CVector corners[8] =
317 CVector(0.f, 0.f, - bboxHeight),
318 CVector(1.f, 0.f, - bboxHeight),
319 CVector(0.f, 1.f, - bboxHeight),
320 CVector(1.f, 1.f, - bboxHeight),
321 CVector(0.f, 0.f, bboxHeight),
322 CVector(1.f, 0.f, bboxHeight),
323 CVector(0.f, 1.f, bboxHeight),
324 CVector(1.f, 1.f, bboxHeight),
326 for(uint k = 0; k < 8; ++k)
328 _ClipCorners[k] = _WorldMatrix * corners[k];
332 // ****************************************************************************
333 bool CDecal::clipFront(const NLMISC::CPlane &p) const
335 for(uint k = 0; k < 8; ++k)
337 if (p * _ClipCorners[k] <= 0.f) return false;
339 return true;
342 // ****************************************************************************
343 void CDecal::setWorldMatrixForArrow(const NLMISC::CVector2f &start, const NLMISC::CVector2f &end, float halfWidth)
345 CMatrix matrix;
346 CVector I = CVector(end.x, end.y, 0.f) - CVector(start.x, start.y, 0.f);
347 CVector J = 2.f * halfWidth * CVector::K ^ I.normed();
348 matrix.setRot(I, J, CVector::K);
349 matrix.setPos(start - 0.5f * J);
350 setWorldMatrix(matrix);
353 // ****************************************************************************
354 void CDecal::setWorldMatrixForSpot(const NLMISC::CVector2f &pos, float radius, float angleInRadians)
356 CMatrix matrix;
357 matrix.rotateZ(angleInRadians);
358 matrix.setScale(2.f * radius);
359 matrix.setPos(pos - radius * CVector2f(1.f, 1.f));
360 setWorldMatrix(matrix);
364 NLMISC::CVector r2MaskOffset(1.f / 4.f, 1.f / 4.f, 0.f);
367 // ****************************************************************************
368 void CDecal::renderTriCache(NL3D::IDriver &drv, NL3D::CShadowPolyReceiver &/* receiver */, bool useVertexProgram)
370 if (_TriCache.empty()) return;
371 if (!_VBInitialized)
373 _VB.setPreferredMemory(CVertexBuffer::AGPVolatile, false);
374 _VB.setVertexFormat(CVertexBuffer::PositionFlag|CVertexBuffer::PrimaryColorFlag);
375 _VBInitialized = true;
377 CMatrix modelMat;
378 modelMat.setPos(_RefPosition);
379 drv.setupModelMatrix(modelMat);
380 if (useVertexProgram)
382 CVertexProgramDecalAttenuation *program = DecalAttenuationVertexProgram;
384 CVertexBufferReadWrite vba;
385 _VB.setNumVertices((uint32)_TriCache.size());
386 _VB.lock(vba);
387 memcpy(vba.getVertexCoordPointer(), &_TriCache[0], sizeof(CRGBAVertex) * _TriCache.size());
389 drv.activeVertexBuffer(_VB);
390 drv.setUniformMatrix(IDriver::VertexProgram, program->getUniformIndex(CProgramIndex::ModelViewProjection), NL3D::IDriver::ModelViewProjection, NL3D::IDriver::Identity);
391 drv.setUniform4f(IDriver::VertexProgram, program->idx().WorldToUV0, _WorldToUVMatrix[0][0], _WorldToUVMatrix[1][0], _WorldToUVMatrix[2][0], _WorldToUVMatrix[3][0]);
392 drv.setUniform4f(IDriver::VertexProgram, program->idx().WorldToUV1, _WorldToUVMatrix[0][1], _WorldToUVMatrix[1][1], _WorldToUVMatrix[2][1], _WorldToUVMatrix[3][1]);
393 drv.setUniform4f(IDriver::VertexProgram, program->idx().Diffuse, _Diffuse.R * (1.f / 255.f), _Diffuse.G * (1.f / 255.f), _Diffuse.B * (1.f / 255.f), 1.f);
394 const NLMISC::CVector &camPos = MainCam.getMatrix().getPos();
395 drv.setUniform4f(IDriver::VertexProgram, program->idx().RefCamDist, camPos.x - _RefPosition.x, camPos.y - _RefPosition.y, camPos.z - _RefPosition.z, 1.f);
396 // bottom & top blend
397 float bottomBlendScale = 1.f / favoid0(_BottomBlendZMax - _BottomBlendZMin);
398 float topBlendScale = 1.f / favoid0(_TopBlendZMin - _TopBlendZMax);
399 drv.setUniform4f(IDriver::VertexProgram, program->idx().BlendScale, bottomBlendScale, bottomBlendScale * (_RefPosition.z - _BottomBlendZMin),
400 topBlendScale, topBlendScale * (_RefPosition.z - _TopBlendZMax));
402 static volatile bool wantSimpleMat = false;
403 if (wantSimpleMat)
405 static CMaterial simpleMat;
406 static volatile bool disableStencil = false;
407 if (disableStencil)
409 drv.enableStencilTest(false);
411 simpleMat.initUnlit();
412 simpleMat.setTexture(0, _Material.getTexture(0));
413 simpleMat.texEnvOpRGB(0, CMaterial::Replace);
414 simpleMat.texEnvArg0RGB(0, CMaterial::Constant, CMaterial::SrcColor);
415 simpleMat.setDoubleSided(true);
416 simpleMat.texConstantColor(0, CRGBA::White);
417 drv.renderRawTriangles(simpleMat, 0, (uint32)_TriCache.size() / 3);
418 IDriver::TPolygonMode pm = drv.getPolygonMode();
419 drv.setPolygonMode(IDriver::Line);
420 simpleMat.texConstantColor(0, CRGBA::Red);
421 drv.renderRawTriangles(simpleMat, 0, (uint32)_TriCache.size() / 3);
422 drv.setPolygonMode(pm);
424 else
426 drv.renderRawTriangles(_Material, 0, (uint32)_TriCache.size() / 3);
429 else
432 CVertexBufferReadWrite vba;
433 _VB.setNumVertices((uint32)_TriCache.size());
434 _VB.lock(vba);
435 NLMISC::CRGBA col = _Diffuse;
436 if (drv.getVertexColorFormat()==CVertexBuffer::TBGRA)
438 std::swap(col.R, col.B);
440 CRGBAVertex *dest = (CRGBAVertex *) vba.getVertexCoordPointer();
441 const CRGBAVertex *destEnd = dest + _TriCache.size();
442 const CRGBAVertex *srcVert = &_TriCache[0];
443 const NLMISC::CVector camPos = MainCam.getMatrix().getPos() - _RefPosition;
444 float scale = 255.f * CDecalRenderList::getInstance()._DistScale;
445 float bias = 255.f * CDecalRenderList::getInstance()._DistBias;
446 float bottomBlendScale = 1.f / favoid0(_BottomBlendZMax - _BottomBlendZMin);
447 float bottomBlendBias = bottomBlendScale * (_RefPosition.z - _BottomBlendZMin);
450 dest->V = srcVert->V;
451 float dist = (camPos - srcVert->V).norm();
452 float intensity = scale * dist + bias;
453 float bottomBlend = srcVert->V.z * bottomBlendScale + bottomBlendBias;
454 clamp(bottomBlend, 0.f, 1.f);
455 clamp(intensity, 0.f, 255.f);
456 intensity *= bottomBlend;
457 col.A = (uint8) (((uint16) intensity * (uint16) srcVert->Color.A) >> 8);
458 dest->Color = col;
459 ++dest;
460 ++srcVert;
462 while (dest != destEnd);
464 drv.activeVertexBuffer(_VB);
465 static volatile bool wantSimpleMat2 = false;
466 if (wantSimpleMat2)
468 static CMaterial simpleMat2;
469 simpleMat2.initUnlit();
470 simpleMat2.setDoubleSided(true);
471 drv.renderRawTriangles(simpleMat2, 0, (uint32)_TriCache.size() / 3);
473 else
475 drv.renderRawTriangles(_Material, 0, (uint32)_TriCache.size() / 3);
480 // ****************************************************************************
481 void CDecal::render(NL3D::UDriver &/* drv */,
482 NL3D::CShadowPolyReceiver &receiver,
483 const std::vector<CPlane> &worldPyramid,
484 const std::vector<NLMISC::CVector> &pyramidCorners,
485 bool useVertexProgram
488 const NLMISC::CVector &camPos = MainCam.getMatrix().getPos();
489 if ((camPos - _LastCamPos).norm() >= 4.f)
491 _Touched = true;
493 if (_Touched)
495 _LastCamPos = camPos;
497 // if out of only 1 plane, entirely out.
498 for(sint i=0;i<(sint)worldPyramid.size();i++)
500 if (clipFront(worldPyramid[i])) return;
502 // do finer clip
503 uint inside = 0;
504 for(uint l = 0; l < pyramidCorners.size(); ++l)
506 NLMISC::CVector localCorner = _InvertedWorldMatrix * pyramidCorners[l];
507 if (localCorner.x >= 0.f) inside |= 1;
508 if (localCorner.x <= 1.f) inside |= 2;
509 if (localCorner.y >= 0.f) inside |= 4;
510 if (localCorner.y <= 1.f) inside |= 8;
512 if(inside != 0xf) return;
514 // must setup attenuation texture each frame
516 _Material.enableUserTexMat(1, true);
517 _Material.setTexCoordGen(1, true);
518 _Material.setTexCoordGenMode(1, CMaterial::TexCoordGenObjectSpace);
519 // object is in world space
520 float scale = 1.f / tileNear;
521 CMatrix attenMat;
522 attenMat.setScale(CVector(0.5f * scale, tileNear, 0.f));
523 attenMat.setPos(CVector(-0.5f * (1.f - camPos.x * scale), -0.5f * (1.f - camPos.y * scale), 0.f));
524 _Material.setUserTexMat(1, attenMat);
527 if (!_Touched)
529 NL3D::IDriver *drvInternal = ((CDriverUser *) Driver)->getDriver();
530 renderTriCache(*drvInternal, receiver, useVertexProgram);
531 return;
534 float tileNear = Landscape->getTileNear();
536 if (!_ShadowMap)
538 _ShadowMap = new CShadowMap(&(((CSceneUser *) Scene)->getScene().getRenderTrav().getShadowMapManager()));
539 nlassert(_ShadowMap);
542 _ShadowMap->LocalClipPlanes.resize(4);
543 CVector corners[4] =
545 _WorldMatrix * CVector(0.f, 1.f, 0.f),
546 _WorldMatrix * CVector(1.f, 1.f, 0.f),
547 _WorldMatrix * CVector(1.f, 0.f, 0.f),
548 _WorldMatrix * CVector(0.f, 0.f, 0.f)
550 CAABBox bbox;
551 _ShadowMap->LocalBoundingBox.setMinMax(corners[0], corners[1]);
552 _ShadowMap->LocalBoundingBox.extend(corners[2]);
553 _ShadowMap->LocalBoundingBox.extend(corners[3]);
555 for(uint k = 0; k < 4; ++k)
557 _ShadowMap->LocalClipPlanes[k].make(corners[k], corners[(k + 1) & 3], corners[k] + (corners[(k + 1) & 3] - corners[k]).norm() * CVector::K);
558 _ShadowMap->LocalClipPlanes[k].invert();
561 _RefPosition = MainCam.getMatrix().getPos();
564 // set uv matrix to match the world matrix
565 // matrix to map (x, y ) = (0, 0) to (u, v) = (0, 1) & (x, y ) = (0, 1) to (u, v) = (0, 0) in local decal space
566 CMatrix reverseUVMatrix;
567 reverseUVMatrix.setRot(CVector::I, -CVector::J, CVector::K);
568 reverseUVMatrix.setPos(CVector::J);
569 CMatrix worldToUVMatrix = _CustomUVMatrix.On ? _CustomUVMatrix.Matrix :
570 (_TextureMatrix * reverseUVMatrix * _InvertedWorldMatrix);
571 CMatrix refPosMatrix;
572 refPosMatrix.setPos(_RefPosition);
573 worldToUVMatrix = worldToUVMatrix * refPosMatrix;
575 worldToUVMatrix.get((float *) _WorldToUVMatrix);
576 // stage 0
577 if (useVertexProgram)
579 _Material.enableUserTexMat(0, false);
581 else
583 _Material.enableUserTexMat(0, true);
584 _Material.setTexCoordGen(0, true);
585 _Material.setTexCoordGenMode(0, CMaterial::TexCoordGenObjectSpace);
586 _Material.setUserTexMat(0, worldToUVMatrix);
590 static NLMISC::CPolygon clipPoly;
591 static NLMISC::CPolygon2D clipPoly2D;
592 clipPoly.Vertices.resize(4);
593 std::copy(corners, corners + 4, clipPoly.Vertices.begin());
594 // clip with by "near tiles" for better selection (avoid unwanted wrapping during triangle selection ...)
595 CPlane planes[4];
596 planes[0].make(CVector::J, camPos + tileNear * CVector::J),
597 planes[1].make(-CVector::J, camPos - tileNear * CVector::J),
598 planes[2].make(CVector::I, camPos + tileNear * CVector::I),
599 planes[3].make(-CVector::I, camPos - tileNear * CVector::I);
600 uint numVerts = (uint)clipPoly.Vertices.size();
601 clipPoly2D.Vertices.resize(numVerts);
602 for (uint k = 0; k < numVerts; ++k)
604 clipPoly2D.Vertices[k].set(clipPoly.Vertices[k].x, clipPoly.Vertices[k].y);
606 NL3D::IDriver *drvInternal = ((CDriverUser *) Driver)->getDriver();
608 // rebuild the triangle cache
609 if (SkipFrame == 0) // don't update just after a tp because landscape hasn't been updated yet ...
611 _Touched = false;
613 // compute tris near the camera to avoid precision z-preision problems due to huge translation in the world matrix)
614 receiver.computeClippedTrisWithPolyClip(_ShadowMap, CVector::Null, - _RefPosition, clipPoly2D, _TriCache, _ClipDownFacing);
615 _Material.setZBias(-0.06f);
616 renderTriCache(*drvInternal, receiver, useVertexProgram);
619 // ****************************************************************************
620 void CDecalRenderList::renderAllDecals()
622 if (_Empty) return;
624 if( !Landscape)
626 return;
628 Driver->enableFog(false);
629 MainCam.buildCameraPyramid(_WorldCamPyramid, false);
630 MainCam.buildCameraPyramidCorners(_WorldCamPyramidCorners, false);
631 Driver->setModelMatrix(CMatrix::Identity);
632 CLandscapeModel *landscapeModel = ((CLandscapeUser *) Landscape)->getLandscape();
633 CShadowPolyReceiver &shadowPolyReceiver = landscapeModel->Landscape.getShadowPolyReceiver();
635 float maxDist = Landscape->getTileNear() * 0.9f;
636 const float threshold = 0.8f; // ratio over the whole dist at which the fade out begins
637 float factor = 1.f / (1.f - threshold);
638 _DistScale = - factor / maxDist;
639 _DistBias = factor;
641 bool useVertexProgram = false;
642 NL3D::IDriver *drvInternal = ((CDriverUser *) Driver)->getDriver();
644 static volatile bool forceNoVertexProgram = false;
645 if (!forceNoVertexProgram && drvInternal->compileVertexProgram(DecalAttenuationVertexProgram))
647 drvInternal->activeVertexProgram(DecalAttenuationVertexProgram);
648 //drvInternal->setCons/tantMatrix(0, NL3D::IDriver::ModelViewProjection, NL3D::IDriver::Identity);
649 drvInternal->setUniform4f(IDriver::VertexProgram, DecalAttenuationVertexProgram->idx().DistScaleBias, _DistScale, _DistBias, 0.f, 1.f);
650 useVertexProgram = true;
652 else
654 drvInternal->activeVertexProgram(NULL);
656 for(uint k = 0; k < DECAL_NUM_PRIORITIES; ++k)
658 std::vector<CDecal::TRefPtr> &renderList = _RenderList[k];
659 for(uint l = 0; l < renderList.size(); ++l)
661 if (renderList[l])
663 renderList[l]->render(*Driver, shadowPolyReceiver, _WorldCamPyramid, _WorldCamPyramidCorners, useVertexProgram);
667 if (useVertexProgram)
669 drvInternal->activeVertexProgram(NULL);
673 // ****************************************************************************
674 void CDecalRenderList::clearRenderList()
676 for(uint k = 0; k < DECAL_NUM_PRIORITIES; ++k)
678 _RenderList[k].clear();
680 _Empty = true;
683 // ****************************************************************************
684 void CDecal::addToRenderList(uint priority /*=0*/)
686 if( !Landscape)
688 return;
690 nlassert(priority < DECAL_NUM_PRIORITIES);
691 CDecalRenderList &drl = CDecalRenderList::getInstance();
692 drl._RenderList[priority].push_back(this);
693 drl._Empty = false;
696 // ****************************************************************************
697 bool CDecal::contains(const NLMISC::CVector2f &pos) const
699 CVector posIn = _InvertedWorldMatrix * CVector(pos.x, pos.y, 0.f);
700 return posIn.x >= 0.f && posIn.x <= 1.f && posIn.y >= 0.f && posIn.y <= 1.f;
703 // ****************************************************************************
704 void CDecal::setClipDownFacing(bool clipDownFacing)
706 if (clipDownFacing != _ClipDownFacing)
708 _Touched = true;
709 _ClipDownFacing = clipDownFacing;
713 // ****************************************************************************
714 void CDecal::setBottomBlend(float zMin, float zMax)
716 if (zMin > zMax) std::swap(zMin, zMax);
717 _BottomBlendZMin = zMin;
718 _BottomBlendZMax = zMax;
721 // ****************************************************************************
722 void CDecal::setTopBlend(float zMin, float zMax)
724 if (zMin > zMax) std::swap(zMin, zMax);
725 _TopBlendZMin = zMin;
726 _TopBlendZMax = zMax;