Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / render_trav.cpp
bloba1e1b69be68ee4a40255c9674ddacca0a537de31
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) 2012-2020 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/3d/render_trav.h"
23 #include "nel/3d/hrc_trav.h"
24 #include "nel/3d/clip_trav.h"
25 #include "nel/3d/light_trav.h"
26 #include "nel/3d/driver.h"
27 #include "nel/3d/light.h"
28 #include "nel/3d/skeleton_model.h"
29 #include "nel/3d/scene.h"
30 #include "nel/3d/coarse_mesh_manager.h"
31 #include "nel/3d/lod_character_manager.h"
32 #include "nel/3d/water_model.h"
33 #include "nel/3d/water_shape.h"
34 #include "nel/misc/hierarchical_timer.h"
36 #include "nel/3d/transform.h"
37 #include "nel/misc/fast_floor.h"
38 #include "nel/3d/vertex_stream_manager.h"
39 #include "nel/3d/landscape_model.h"
40 #include "nel/3d/shape_bank.h"
42 using namespace std;
43 using namespace NLMISC;
45 #ifdef DEBUG_NEW
46 #define new DEBUG_NEW
47 #endif
49 namespace NL3D
52 // default is undefined, allows to see which CTransformShape are displayed in a scene, useful for debugging
53 //#define NL_DEBUG_RENDER_TRAV
60 // ***************************************************************************
61 // ***************************************************************************
62 // CRenderTrav
63 // ***************************************************************************
64 // ***************************************************************************
67 // ***************************************************************************
68 CRenderTrav::CRenderTrav()
70 RenderList.resize(1024);
71 _CurrentNumVisibleModels= 0;
72 _MaxTransparencyPriority = 0;
73 OrderOpaqueList.init(1024);
74 setupTransparencySorting();
75 Driver = NULL;
76 _CurrentPassOpaque = true;
78 _CacheLightContribution= NULL;
80 // Default light Setup.
81 LightingSystemEnabled= false;
82 AmbientGlobal= CRGBA(50, 50, 50);
83 SunAmbient= CRGBA::Black;
84 SunDiffuse= SunSpecular= CRGBA::White;
85 _SunDirection.set(0, 0.5, -0.5);
86 _SunDirection.normalize();
88 _StrongestLightTouched = true;
90 _MeshSkinManager= NULL;
91 _ShadowMeshSkinManager= NULL;
93 _LayersRenderingOrder= true;
94 _FirstWaterModel = NULL;
101 // ***************************************************************************
102 void CRenderTrav::traverse(UScene::TRenderPart renderPart, bool newRender, bool generateShadows)
104 #ifdef NL_DEBUG_RENDER_TRAV
105 nlwarning("Render trave begin");
106 #endif
107 H_AUTO( NL3D_TravRender );
108 if (getDriver()->isLost()) return; // device is lost so no need to render anything
109 CTravCameraScene::update();
110 // Bind to Driver.
111 setupDriverCamera();
112 getDriver()->setupViewport(_Viewport);
114 // reset the light setup, and set global ambient.
115 resetLightSetup();
116 if (newRender)
119 // reset the Skin manager, if needed
120 if(_MeshSkinManager)
122 if(Driver!=_MeshSkinManager->getDriver())
124 _MeshSkinManager->release();
125 _MeshSkinManager->init(Driver,
126 NL3D_MESH_SKIN_MANAGER_VERTEXFORMAT,
127 NL3D_MESH_SKIN_MANAGER_MAXVERTICES,
128 NL3D_MESH_SKIN_MANAGER_NUMVB,
129 "MRMSkinVB", true);
133 // Same For Shadow ones. NB: use AuxDriver here!!!
134 if(_ShadowMeshSkinManager)
136 if(getAuxDriver()!=_ShadowMeshSkinManager->getDriver())
138 _ShadowMeshSkinManager->release();
139 _ShadowMeshSkinManager->init(getAuxDriver(),
140 NL3D_SHADOW_MESH_SKIN_MANAGER_VERTEXFORMAT,
141 NL3D_SHADOW_MESH_SKIN_MANAGER_MAXVERTICES,
142 NL3D_SHADOW_MESH_SKIN_MANAGER_NUMVB,
143 "ShadowSkinVB", true);
148 // Fill OT with models, for both Opaque and transparent pass
149 // =============================
151 // Sort the models by distance from camera
152 // This is done here and not in the addRenderModel because of the LoadBalancing traversal which can modify
153 // the transparency flag (multi lod for instance)
155 // clear the OTs, and prepare to allocate max element space
156 OrderOpaqueList.reset(_CurrentNumVisibleModels);
157 for(uint k = 0; k <= (uint) _MaxTransparencyPriority; ++k)
159 _OrderTransparentListByPriority[k].reset(_CurrentNumVisibleModels); // all table share the same allocator (CLayeredOrderingTable::shareAllocator has been called)
160 // and an object can be only inserted in one table, so we only need to init the main allocator
163 // fill the OTs.
164 CTransform **itRdrModel= NULL;
165 uint32 nNbModels = _CurrentNumVisibleModels;
166 if(nNbModels)
167 itRdrModel= &RenderList[0];
168 float rPseudoZ, rPseudoZ2;
170 // Some precalc
171 float OOFar= 1.0f / this->Far;
172 uint32 opaqueOtSize= OrderOpaqueList.getSize();
173 uint32 opaqueOtMax= OrderOpaqueList.getSize()-1;
174 uint32 transparentOtSize= _OrderTransparentListByPriority[0].getSize(); // there is at least one list, and all list have the same number of entries
175 uint32 transparentOtMax= _OrderTransparentListByPriority[0].getSize()-1;
176 uint32 otId;
177 // fast floor
178 NLMISC::OptFastFloorBegin();
179 // For all rdr models
180 for( ; nNbModels>0; itRdrModel++, nNbModels-- )
182 CTransform *pTransform = *itRdrModel;
184 // if this entry was killed by removeRenderModel(), skip!
185 if(!pTransform)
186 continue;
188 // Yoyo: skins are rendered through skeletons, so models WorldMatrix are all good here (even sticked objects)
189 rPseudoZ = (pTransform->getWorldMatrix().getPos() - CamPos).norm();
191 // rPseudoZ from 0.0 -> 1.0
192 rPseudoZ = sqrtf( rPseudoZ * OOFar );
194 if( pTransform->isOpaque() )
196 // since norm, we are sure that rPseudoZ>=0
197 rPseudoZ2 = rPseudoZ * opaqueOtSize;
198 otId= NLMISC::OptFastFloor(rPseudoZ2);
199 otId= min(otId, opaqueOtMax);
200 OrderOpaqueList.insert( otId, pTransform );
202 if( pTransform->isTransparent() )
204 // since norm, we are sure that rPseudoZ>=0
205 rPseudoZ2 = rPseudoZ * transparentOtSize;
206 otId= NLMISC::OptFastFloor(rPseudoZ2);
207 otId= min(otId, transparentOtMax);
208 // must invert id, because transparent, sort from back to front
209 _OrderTransparentListByPriority[std::min(pTransform->getTransparencyPriority(), _MaxTransparencyPriority)].insert( pTransform->getOrderingLayer(), pTransform, transparentOtMax-otId );
213 // fast floor
214 NLMISC::OptFastFloorEnd();
217 if (renderPart & UScene::RenderOpaque)
219 // Render Opaque stuff.
220 // =============================
222 // TestYoyo
223 //OrderOpaqueList.reset(0);
224 //OrderTransparentList.reset(0);
226 // Clear any landscape
227 clearRenderLandscapeList();
229 // Start LodCharacter Manager render.
230 CLodCharacterManager *clodMngr= Scene->getLodCharacterManager();
231 if(clodMngr)
232 clodMngr->beginRender(getDriver(), CamPos);
234 // Render the opaque materials
235 _CurrentPassOpaque = true;
236 OrderOpaqueList.begin();
237 while( OrderOpaqueList.get() != NULL )
239 CTransform *tr= OrderOpaqueList.get();
240 #ifdef NL_DEBUG_RENDER_TRAV
241 CTransformShape *trShape = dynamic_cast<CTransformShape *>(tr);
242 if (trShape)
244 const std::string *shapeName = Scene->getShapeBank()->getShapeNameFromShapePtr(trShape->Shape);
245 if (shapeName)
247 nlwarning("Displaying %s", shapeName->c_str());
250 #endif
251 tr->traverseRender();
252 OrderOpaqueList.next();
255 /* Render MeshBlock Manager.
256 Some Meshs may be render per block. Interesting to remove VertexBuffer and Material setup overhead.
257 Faster if rendered before lods, for ZBuffer optimisation: render first near objects then far.
258 Lods are usually far objects.
260 MeshBlockManager.flush(Driver, Scene, this);
263 // End LodCharacter Manager render.
264 if(clodMngr)
265 clodMngr->endRender();
268 /* Render Scene CoarseMeshManager.
269 Important to render them at end of Opaque rendering, because coarses instances are created/removed during
270 this model opaque rendering pass.
272 if( Scene->getCoarseMeshManager() )
273 Scene->getCoarseMeshManager()->flushRender(Driver);
275 /* Render ShadowMaps.
276 Important to render them at end of Opaque rendering, because alphaBlended objects must blend with opaque
277 objects shadowed.
278 Therefore, transparent objects neither can't cast or receive shadows...
280 NB: Split in 2 calls and interleave Landscape Rendering between the 2. WHY???
281 Because it is far more efficient for VBLock (but not for ZBuffer optim...) because in renderGenerate()
282 the ShadowMeshSkinManager do lot of VBLocks that really stall (because only 2 VBHard with swap scheme).
284 Therefore the first Lock that stall will wait not only for the first MeshSkin to finish but also for the
285 preceding landscape render to finish too! => big STALL.
288 // Generate ShadowMaps
289 if (generateShadows)
290 _ShadowMapManager.renderGenerate(Scene);
292 // Render the Landscape
293 renderLandscapes();
295 // Project ShadowMaps.
296 if(Scene->getLandscapePolyDrawingCallback() != NULL)
298 Scene->getLandscapePolyDrawingCallback()->beginPolyDrawing();
300 _ShadowMapManager.renderProject(Scene);
301 if(Scene->getLandscapePolyDrawingCallback())
303 Scene->getLandscapePolyDrawingCallback()->endPolyDrawing();
306 // Profile this frame?
307 if(Scene->isNextRenderProfile())
309 OrderOpaqueList.begin();
310 while( OrderOpaqueList.get() != NULL )
312 OrderOpaqueList.get()->profileRender();
313 OrderOpaqueList.next();
319 if (renderPart & UScene::RenderTransparent)
321 if (_FirstWaterModel) // avoid a lock if no water is to be rendered
323 // setup water models
324 CWaterModel *curr = _FirstWaterModel;
325 uint numWantedVertices = 0;
326 while (curr)
328 numWantedVertices += curr->getNumWantedVertices();
329 curr = curr->_Next;
331 if (numWantedVertices != 0)
333 CWaterModel::setupVertexBuffer(Scene->getWaterVB(), numWantedVertices, getDriver());
336 CVertexBufferReadWrite vbrw;
337 Scene->getWaterVB().lock(vbrw);
338 CWaterModel *curr = _FirstWaterModel;
339 void *datas = vbrw.getVertexCoordPointer(0);
341 uint tri = 0;
342 while (curr)
344 tri = curr->fillVB(datas, tri, *getDriver());
345 nlassert(tri <= numWantedVertices);
346 curr = curr->_Next;
348 nlassert(tri * 3 == numWantedVertices);
351 // Unlink all water model
352 clearWaterModelList();
356 if ((renderPart & UScene::RenderTransparent) &&
357 (renderPart & UScene::RenderFlare)
360 // Render all transparent stuffs including flares.
361 // =============================
362 // Render transparent materials (draw higher priority last, because their appear in front)
363 _CurrentPassOpaque = false;
364 for(std::vector<CLayeredOrderingTable<CTransform> >::iterator it = _OrderTransparentListByPriority.begin(); it != _OrderTransparentListByPriority.end(); ++it)
366 it->begin(_LayersRenderingOrder);
367 while( it->get() != NULL )
369 #ifdef NL_DEBUG_RENDER_TRAV
370 CTransformShape *trShape = dynamic_cast<CTransformShape *>(it->get());
371 if (trShape)
373 const std::string *shapeName = Scene->getShapeBank()->getShapeNameFromShapePtr(trShape->Shape);
374 if (shapeName)
376 nlwarning("Displaying %s", shapeName->c_str());
379 #endif
380 it->get()->traverseRender();
381 it->next();
385 // Profile this frame?
386 if(Scene->isNextRenderProfile())
388 for(std::vector<CLayeredOrderingTable<CTransform> >::iterator it = _OrderTransparentListByPriority.begin(); it != _OrderTransparentListByPriority.end(); ++it)
390 it->begin();
391 while( it->get() != NULL )
393 it->get()->profileRender();
394 it->next();
399 else if (renderPart & UScene::RenderTransparent)
401 // Render all transparent stuffs, don't render flares
402 // =============================
403 _CurrentPassOpaque = false;
404 for(std::vector<CLayeredOrderingTable<CTransform> >::iterator it = _OrderTransparentListByPriority.begin(); it != _OrderTransparentListByPriority.end(); ++it)
406 it->begin(_LayersRenderingOrder);
407 while( it->get() != NULL )
409 if (!it->get()->isFlare())
411 #ifdef NL_DEBUG_RENDER_TRAV
412 CTransformShape *trShape = dynamic_cast<CTransformShape *>(it->get());
413 if (trShape)
415 const std::string *shapeName = Scene->getShapeBank()->getShapeNameFromShapePtr(trShape->Shape);
416 if (shapeName)
418 nlwarning("Displaying %s", shapeName->c_str());
421 #endif
422 it->get()->traverseRender();
424 it->next();
428 // Profile this frame?
429 if(Scene->isNextRenderProfile())
431 for(std::vector<CLayeredOrderingTable<CTransform> >::iterator it = _OrderTransparentListByPriority.begin(); it != _OrderTransparentListByPriority.end(); ++it)
433 it->begin();
434 while( it->get() != NULL )
436 if (!it->get()->isFlare())
438 it->get()->profileRender();
440 it->next();
445 else if (renderPart & UScene::RenderFlare)
447 // Render flares only
448 // =============================
449 _CurrentPassOpaque = false;
450 for(std::vector<CLayeredOrderingTable<CTransform> >::iterator it = _OrderTransparentListByPriority.begin(); it != _OrderTransparentListByPriority.end(); ++it)
452 it->begin(_LayersRenderingOrder);
453 while( it->get() != NULL )
455 if (it->get()->isFlare())
457 #ifdef NL_DEBUG_RENDER_TRAV
458 CTransformShape *trShape = dynamic_cast<CTransformShape *>(it->get());
459 if (trShape)
461 const std::string *shapeName = Scene->getShapeBank()->getShapeNameFromShapePtr(trShape->Shape);
462 if (shapeName)
464 nlwarning("Displaying %s", shapeName->c_str());
467 #endif
468 it->get()->traverseRender();
470 it->next();
474 // Profile this frame?
475 if(Scene->isNextRenderProfile())
477 for(std::vector<CLayeredOrderingTable<CTransform> >::iterator it = _OrderTransparentListByPriority.begin(); it != _OrderTransparentListByPriority.end(); ++it)
479 it->begin();
480 while( it->get() != NULL )
482 if (it->get()->isFlare())
484 it->get()->profileRender();
486 it->next();
492 // END!
493 // =============================
495 // clean: reset the light setup
496 resetLightSetup();
500 // ***************************************************************************
501 void CRenderTrav::setupDriverCamera()
503 getDriver()->setFrustum(Left, Right, Bottom, Top, Near, Far, Perspective);
504 // Use setupViewMatrixEx() for ZBuffer precision.
505 getDriver()->setupViewMatrixEx(ViewMatrix, CamPos);
508 // ***************************************************************************
509 void CRenderTrav::clearRenderList()
511 _CurrentNumVisibleModels= 0;
515 // ***************************************************************************
516 void CRenderTrav::setSunDirection(const CVector &dir)
518 _SunDirection= dir;
519 _SunDirection.normalize();
523 // ***************************************************************************
524 void CRenderTrav::setMeshSkinManager(CVertexStreamManager *msm)
526 _MeshSkinManager= msm;
529 // ***************************************************************************
530 void CRenderTrav::setShadowMeshSkinManager(CVertexStreamManager *msm)
532 _ShadowMeshSkinManager= msm;
535 // ***************************************************************************
536 void CRenderTrav::reserveRenderList(uint numModels)
538 // enlarge only.
539 if(numModels>RenderList.size())
540 RenderList.resize(numModels);
543 // ***************************************************************************
544 void CRenderTrav::removeRenderModel(CTransform *m)
546 // NB: storing a 8 bit in CTransform, instead of a 32 bits, is just to save space.
547 uint lsb= m->_IndexLSBInRenderList;
549 // this method is rarely called, so don't bother the slow down
550 // btw, we parse the entire list / 256!!! which is surely fast!!
551 for(uint i=lsb;i<_CurrentNumVisibleModels;i+=256)
553 // if i am really this entry, then set NULL
554 if(RenderList[i]==m)
556 RenderList[i]= NULL;
557 break;
563 // ***************************************************************************
564 // ***************************************************************************
565 // LightSetup
566 // ***************************************************************************
567 // ***************************************************************************
570 // ***************************************************************************
571 void CRenderTrav::resetLightSetup()
573 // If lighting System disabled, skip
574 if(!LightingSystemEnabled)
576 // Dont modify Driver lights, but setup default lighting For VertexProgram Lighting.
577 _NumLightEnabled= 1;
578 // Setup A default directionnal.
579 CVector defDir(-0.5f, 0.0, -0.85f);
580 defDir.normalize();
581 CRGBA aday= CRGBA(130, 105, 119);
582 CRGBA dday= CRGBA(238, 225, 204);
583 _DriverLight[0].setupDirectional(aday, dday, dday, defDir);
585 return;
587 else
589 uint i;
591 // Disable all lights.
592 for(i=0; i<Driver->getMaxLight(); ++i)
594 Driver->enableLight(uint8(i), false);
598 // setup the precise cache, and setup lights according to this cache?
599 // setup blackSun (factor==0).
600 _LastSunFactor= 0;
601 _LastFinalAmbient.set(0,0,0,255);
602 _DriverLight[0].setupDirectional(CRGBA::Black, CRGBA::Black, CRGBA::Black, _SunDirection);
603 Driver->setLight(0, _DriverLight[0]);
604 // setup NULL point lights (=> cache will fail), so no need to setup other lights in Driver.
605 for(i=0; i<NL3D_MAX_LIGHT_CONTRIBUTION; i++)
607 _LastPointLight[i]= NULL;
611 // Set the global ambientColor
612 Driver->setAmbientColor(AmbientGlobal);
615 // clear the cache.
616 _CacheLightContribution= NULL;
617 _NumLightEnabled= 0;
619 _StrongestLightTouched = true;
624 // ***************************************************************************
625 void CRenderTrav::changeLightSetup(CLightContribution *lightContribution, bool useLocalAttenuation)
627 // If lighting System disabled, skip
628 if(!LightingSystemEnabled)
629 return;
631 uint i;
633 // if same lightContribution, no-op.
634 if (_CacheLightContribution == lightContribution && (lightContribution == NULL || _LastLocalAttenuation == useLocalAttenuation))
635 return;
636 // else, must setup the lights into driver.
637 else
639 _StrongestLightTouched = true;
640 // if the setup is !NULL
641 if(lightContribution)
643 // Compute SunAmbient / LocalAmbient
644 //-----------
645 // Take the current model ambient
646 CRGBA finalAmbient= lightContribution->computeCurrentAmbient(SunAmbient);
647 // If use the mergedPointLight, add it to final Ambient
648 if(lightContribution->UseMergedPointLight)
649 finalAmbient.addRGBOnly(finalAmbient, lightContribution->MergedPointLight);
650 // Force Alpha to 255 for good cache test.
651 finalAmbient.A= 255;
654 // Setup the directionnal Sunlight.
655 //-----------
656 // expand 0..255 to 0..256, to avoid loss of precision.
657 uint ufactor= lightContribution->SunContribution;
658 // different SunLight as in cache ??
659 // NB: sunSetup can't change during renderPass, so need only to test factor.
660 if(ufactor != _LastSunFactor || finalAmbient != _LastFinalAmbient)
662 // cache (before expanding!!)
663 _LastSunFactor= ufactor;
664 // Cache final ambient light
665 _LastFinalAmbient= finalAmbient;
667 // expand to 0..256.
668 ufactor+= ufactor>>7; // add 0 or 1.
669 // modulate color with factor of the lightContribution.
670 CRGBA sunDiffuse, sunSpecular;
671 sunDiffuse.modulateFromuiRGBOnly(SunDiffuse, ufactor);
672 sunSpecular.modulateFromuiRGBOnly(SunSpecular, ufactor);
673 // setup driver light
674 _DriverLight[0].setupDirectional(finalAmbient, sunDiffuse, sunSpecular, _SunDirection);
675 Driver->setLight(0, _DriverLight[0]);
679 // Setup other point lights
680 //-----------
681 uint plId=0;
682 // for the list of light.
683 while(lightContribution->PointLight[plId]!=NULL)
685 CPointLight *pl= lightContribution->PointLight[plId];
686 uint inf;
687 if(useLocalAttenuation)
688 inf= lightContribution->Factor[plId];
689 else
690 inf= lightContribution->AttFactor[plId];
692 // different PointLight setup than in cache??
693 // NB: pointLight setup can't change during renderPass, so need only to test pointer,
694 // attenuation mode and factor.
695 if( pl!=_LastPointLight[plId] ||
696 inf!=_LastPointLightFactor[plId] ||
697 useLocalAttenuation!=_LastPointLightLocalAttenuation[plId] )
699 // need to resetup the light. Cache it.
700 _LastPointLight[plId]= pl;
701 _LastPointLightFactor[plId]= uint8(inf);
702 _LastPointLightLocalAttenuation[plId]= useLocalAttenuation;
704 // compute the driver light
705 if(useLocalAttenuation)
706 pl->setupDriverLight(_DriverLight[plId+1], uint8(inf));
707 else
708 // Compute it with user Attenuation
709 pl->setupDriverLightUserAttenuation(_DriverLight[plId+1], uint8(inf));
711 // setup driver. decal+1 because of sun.
712 Driver->setLight(uint8(plId+1), _DriverLight[plId+1]);
715 // next light?
716 plId++;
717 if(plId>=NL3D_MAX_LIGHT_CONTRIBUTION)
718 break;
722 // Disable olds, enable news, and cache.
723 //-----------
724 // count new number of light enabled.
725 uint newNumLightEnabled;
726 // number of pointLight + the sun
727 newNumLightEnabled= plId + 1;
729 // enable lights which are used now and were not before.
730 for(i=_NumLightEnabled; i<newNumLightEnabled; i++)
732 Driver->enableLight(uint8(i), true);
735 // disable lights which are no more used.
736 for(i=newNumLightEnabled; i<_NumLightEnabled; i++)
738 Driver->enableLight(uint8(i), false);
741 // cache the setup.
742 _CacheLightContribution = lightContribution;
743 _NumLightEnabled= newNumLightEnabled;
744 _LastLocalAttenuation= useLocalAttenuation;
746 else
748 // Disable old lights, and cache.
749 //-----------
750 // disable lights which are no more used.
751 for(i=0; i<_NumLightEnabled; i++)
753 Driver->enableLight(uint8(i), false);
756 // cache the setup.
757 _CacheLightContribution = NULL;
758 _NumLightEnabled= 0;
765 // ***************************************************************************
766 // ***************************************************************************
767 // VertexProgram LightSetup
768 // ***************************************************************************
769 // ***************************************************************************
771 void CRenderTrav::prepareVPLightSetup()
773 nlassert(MaxVPLight==4);
774 _VPNumLights= min(_NumLightEnabled, (uint)MaxVPLight);
775 // Must force real light setup at least the first time, in changeVPLightSetupMaterial()
776 _VPMaterialCacheDirty= true;
779 // ***************************************************************************
780 void CRenderTrav::beginVPLightSetup(CVertexProgramLighted *program, const CMatrix &invObjectWM)
782 uint i;
783 // nlassert(MaxVPLight==4);
784 // _VPNumLights= min(_NumLightEnabled, (uint)MaxVPLight);
785 // _VPCurrentCtStart= ctStart;
786 // _VPSupportSpecular= supportSpecular;
787 _VPCurrent = program;
788 bool supportSpecular = program->featuresLighted().SupportSpecular;
790 // Prepare Colors (to be multiplied by material)
791 //================
792 // Ambient. _VPCurrentCtStart+0
793 _VPFinalAmbient= AmbientGlobal;
794 for(i=0; i<_VPNumLights; i++)
796 _VPFinalAmbient+= _DriverLight[i].getAmbiant();
798 // Diffuse. _VPCurrentCtStart+1 to 4
799 for(i=0; i<_VPNumLights; i++)
801 _VPLightDiffuse[i]= _DriverLight[i].getDiffuse();
803 // reset other to 0.
804 for(; i<MaxVPLight; i++)
806 _VPLightDiffuse[i] = CRGBA::Black;
807 if (program->idxLighted().Diffuse[i] != std::numeric_limits<uint>::max())
809 Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Diffuse[i], 0.f, 0.f, 0.f, 0.f);
812 // Specular. _VPCurrentCtStart+5 to 8 (only if supportSpecular)
813 if(supportSpecular)
815 for(i=0; i<_VPNumLights; i++)
817 _VPLightSpecular[i]= _DriverLight[i].getSpecular();
819 // reset other to 0.
820 for(; i<MaxVPLight; i++)
822 _VPLightSpecular[i]= CRGBA::Black;
823 if (program->idxLighted().Specular[i] != std::numeric_limits<uint>::max())
825 Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Specular[i], 0.f, 0.f, 0.f, 0.f);
831 // Compute Eye position in Object space.
832 CVector eye= invObjectWM * CamPos;
835 // Setup Sun Directionnal light.
836 //================
837 CVector lightDir;
838 // in objectSpace.
839 lightDir= invObjectWM.mulVector(_DriverLight[0].getDirection());
840 lightDir.normalize();
841 lightDir= -lightDir;
842 Driver->setUniform3f(IDriver::VertexProgram, program->idxLighted().DirOrPos[0], lightDir); // The sun is the same for every instance.
845 // Setup PointLights
846 //================
847 uint startPLPos;
848 if (supportSpecular)
850 // Setup eye in objectSpace for localViewer
851 Driver->setUniform3f(IDriver::VertexProgram, program->idxLighted().EyePosition, eye);
853 // For all pointLight enabled (other are black: don't matter)
854 for(i=1; i<_VPNumLights; i++)
856 // Setup position of light.
857 CVector lightPos;
858 lightPos = invObjectWM * _DriverLight[i].getPosition();
859 Driver->setUniform3f(IDriver::VertexProgram, program->idxLighted().DirOrPos[i], lightPos);
863 // Must force real light setup at least the first time, in changeVPLightSetupMaterial()
864 _VPMaterialCacheDirty= true;
867 // ***************************************************************************
868 void CRenderTrav::changeVPLightSetupMaterial(const CMaterial &mat, bool excludeStrongest)
870 CVertexProgramLighted *program = _VPCurrent;
871 nlassert(program);
873 // Must test if at least done one time.
874 if(!_VPMaterialCacheDirty)
876 // Must test if same as in cache
877 if( _VPMaterialCacheEmissive == mat.getEmissive().getPacked() &&
878 _VPMaterialCacheAmbient == mat.getAmbient().getPacked() &&
879 _VPMaterialCacheDiffuse == mat.getDiffuse().getPacked() )
881 // Same Diffuse part, test if same specular if necessary
882 if( !program->featuresLighted().SupportSpecular ||
883 ( _VPMaterialCacheSpecular == mat.getSpecular().getPacked() &&
884 _VPMaterialCacheShininess == mat.getShininess() ) )
886 // Then ok, skip.
887 return;
892 // If not skiped, cache now. cache all for simplification
893 _VPMaterialCacheDirty= false;
894 _VPMaterialCacheEmissive= mat.getEmissive().getPacked();
895 _VPMaterialCacheAmbient= mat.getDiffuse().getPacked();
896 _VPMaterialCacheDiffuse= mat.getDiffuse().getPacked();
897 _VPMaterialCacheSpecular= mat.getSpecular().getPacked();
898 _VPMaterialCacheShininess= mat.getShininess();
900 // Setup constants
901 CRGBAF color;
902 uint i;
903 CRGBAF matDiff= mat.getDiffuse();
904 CRGBAF matSpec= mat.getSpecular();
905 float specExp= mat.getShininess();
907 uint strongestLightIndex = excludeStrongest ? getStrongestLightIndex() : _VPNumLights;
909 // setup Ambient + Emissive
910 color= _VPFinalAmbient * mat.getAmbient();
911 color+= mat.getEmissive();
912 Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Ambient, color);
915 // is the strongest light is not excluded, its index should have been setup to _VPNumLights
917 // setup Diffuse.
918 for(i = 0; i < strongestLightIndex; ++i)
920 color= _VPLightDiffuse[i] * matDiff;
921 Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Diffuse[i], color);
924 nlassert(_VPNumLights <= MaxVPLight);
925 if (i != _VPNumLights)
927 color= _VPLightDiffuse[i] * matDiff;
928 _StrongestLightDiffuse.set((uint8) (255.f * color.R), (uint8) (255.f * color.G), (uint8) (255.f * color.B), (uint8) (255.f * color.A));
929 // setup strongest light to black for the gouraud part
930 Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Diffuse[i], 0.f, 0.f, 0.f, 0.f);
931 ++i;
932 // setup other lights
933 for(; i < _VPNumLights; i++)
935 color= _VPLightDiffuse[i] * matDiff;
936 Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Diffuse[i], color);
940 // setup Specular
941 if (program->featuresLighted().SupportSpecular)
943 for(i = 0; i < strongestLightIndex; ++i)
945 color= _VPLightSpecular[i] * matSpec;
946 color.A= specExp;
947 Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Specular[i], color);
950 if (i != _VPNumLights)
952 color= _VPLightSpecular[i] * matSpec;
953 _StrongestLightSpecular.set((uint8) (255.f * color.R), (uint8) (255.f * color.G), (uint8) (255.f * color.B), (uint8) (255.f * color.A));
955 // setup strongest light to black (for gouraud part)
956 Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Specular[i], 0.f, 0.f, 0.f, 0.f);
957 ++i;
958 // setup other lights
959 for(; i < _VPNumLights; i++)
961 color= _VPLightSpecular[i] * matSpec;
962 color.A= specExp;
963 Driver->setUniform4f(IDriver::VertexProgram, program->idxLighted().Specular[i], color);
968 // setup alpha.
969 static float alphaCte[4]= {0,0,1,0};
970 alphaCte[3]= matDiff.A;
971 // setup at good place
972 Driver->setUniform4fv(IDriver::VertexProgram, program->idxLighted().DiffuseAlpha, 1, alphaCte);
975 // ***************************************************************************
976 sint CRenderTrav::getStrongestLightIndex() const
978 if (!_StrongestLightTouched) return -1;
979 uint vpNumLights = min(_NumLightEnabled, (uint)MaxVPLight);
980 // If there is only a directionnal light, use it
981 // If there is any point light, use the nearest, or the directionnal light if it is brighter
982 if (vpNumLights == 0) return -1;
983 if (vpNumLights == 1) return 0;
984 // First point light is brightest ?
985 float lumDir = _VPLightDiffuse[0].R + _VPLightDiffuse[0].G + _VPLightDiffuse[0].B + _VPLightDiffuse[0].A
986 + _VPLightSpecular[0].R + _VPLightSpecular[0].G + _VPLightSpecular[0].B + _VPLightSpecular[0].A;
987 float lumOmni = _VPLightDiffuse[1].R + _VPLightDiffuse[1].G + _VPLightDiffuse[1].B + _VPLightDiffuse[1].A
988 + _VPLightSpecular[1].R + _VPLightSpecular[1].G + _VPLightSpecular[1].B + _VPLightSpecular[1].A;
989 return lumDir > lumOmni ? 0 : 1;
992 // ***************************************************************************
993 void CRenderTrav::getStrongestLightColors(NLMISC::CRGBA &diffuse, NLMISC::CRGBA &specular)
995 sint strongestLightIndex = getStrongestLightIndex();
996 if (strongestLightIndex == -1)
998 diffuse = specular = NLMISC::CRGBA::Black;
1000 else
1002 diffuse = _StrongestLightDiffuse;
1003 specular = _StrongestLightSpecular;
1008 // ***************************************************************************
1009 static const char* LightingVPFragmentNormalize=
1010 " # normalize normal \n\
1011 DP3 R6.w, R6, R6; \n\
1012 RSQ R6.w, R6.w; \n\
1013 MUL R6, R6, R6.w; \n\
1017 // ***************************************************************************
1018 // NB: all CTS+x are replaced with good cte index.
1019 static const char* LightingVPFragmentNoSpecular_Begin=
1020 " \n\
1021 # Global Ambient. \n\
1022 MOV R2, c[CTS+0]; \n\
1024 # Diffuse Sun \n\
1025 DP3 R0.x, R6, c[CTS+5]; # R0.x= normal*-lightDir \n\
1026 LIT R0.y, R0.xxxx; # R0.y= R0.x clamped \n\
1027 MAD R2, R0.y, c[CTS+1], R2; # R2= summed vertex color. \n\
1030 // The 3 point Light code.
1031 static const char* LightingVPFragmentNoSpecular_PL[]=
1033 " # Diffuse PointLight 0. \n\
1034 ADD R0, c[CTS+6], -R5; # R0= lightPos-vertex \n\
1035 DP3 R0.w, R0, R0; # normalize R0. \n\
1036 RSQ R0.w, R0.w; \n\
1037 MUL R0, R0, R0.w; \n\
1038 DP3 R0.x, R6, R0; # R0.x= normal*lightDir \n\
1039 LIT R0.y, R0.xxxx; # R0.y= R0.x clamped \n\
1040 MAD R2, R0.y, c[CTS+2], R2; # R2= summed vertex color. \n\
1042 " # Diffuse PointLight 1. \n\
1043 ADD R0, c[CTS+7], -R5; # R0= lightPos-vertex \n\
1044 DP3 R0.w, R0, R0; # normalize R0. \n\
1045 RSQ R0.w, R0.w; \n\
1046 MUL R0, R0, R0.w; \n\
1047 DP3 R0.x, R6, R0; # R0.x= normal*lightDir \n\
1048 LIT R0.y, R0; # R0.y= R0.x clamped \n\
1049 MAD R2, R0.y, c[CTS+3], R2; # R2= summed vertex color. \n\
1051 " # Diffuse PointLight 2. \n\
1052 ADD R0, c[CTS+8], -R5; # R0= lightPos-vertex \n\
1053 DP3 R0.w, R0, R0; # normalize R0. \n\
1054 RSQ R0.w, R0.w; \n\
1055 MUL R0, R0, R0.w; \n\
1056 DP3 R0.x, R6, R0; # R0.x= normal*lightDir \n\
1057 LIT R0.y, R0; # R0.y= R0.x clamped \n\
1058 MAD R2, R0.y, c[CTS+4], R2; # R2= summed vertex color. \n\
1062 // The End code.
1063 static const char* LightingVPFragmentNoSpecular_End=
1064 " # output to o[COL0] only, replacing alpha with material alpha. \n\
1065 MAD o[COL0], R2, c[CTS+9].zzzx, c[CTS+9].xxxw; \n\
1069 // ***************************************************************************
1070 // NB: all CTS+x are replaced with good cte index.
1071 static const char* LightingVPFragmentSpecular_Begin=
1072 " \n\
1073 # Global Ambient. \n\
1074 MOV R2, c[CTS+0]; \n\
1076 # Always keep Specular exponent in R0.w \n\
1077 MOV R0.w, c[CTS+5].w; \n\
1079 # Compute vertex-to-eye vector normed. \n\
1080 ADD R4, c[CTS+11], -R5; \n\
1081 DP3 R1.w, R4, R4; \n\
1082 RSQ R1.w, R1.w; \n\
1083 MUL R4, R4, R1.w; \n\
1085 # Diffuse-Specular Sun \n\
1086 # Compute R1= halfAngleVector= (lightDir+R4).normed(). \n\
1087 ADD R1.xyz, c[CTS+9], R4; # R1= halfAngleVector \n\
1088 DP3 R1.w, R1, R1; # normalize R1. \n\
1089 RSQ R1.w, R1.w; \n\
1090 MUL R1.xyz, R1, R1.w; \n\
1091 # Compute Factors and colors. \n\
1092 DP3 R0.x, R6, c[CTS+9]; # R0.x= normal*-lightDir \n\
1093 DP3 R0.yz, R6, R1; # R0.yz= normal*halfAngleVector \n\
1094 LIT R0.yz, R0; # R0.y= R0.x clamped, R0.z= pow(spec, R0.w) clamp \n\
1095 MAD R2, R0.y, c[CTS+1], R2; # R2= summed vertex color. \n\
1096 MUL R3, R0.z, c[CTS+5]; # R3= specular color. \n\
1099 // The 3 point Light code.
1100 static const char* LightingVPFragmentSpecular_PL[]=
1102 " # Diffuse-Specular PointLight 0. \n\
1103 # Compute R0= (lightPos-vertex).normed(). \n\
1104 ADD R0.xyz, c[CTS+12], -R5; # R0= lightPos-vertex \n\
1105 DP3 R1.w, R0, R0; # normalize R0. \n\
1106 RSQ R1.w, R1.w; \n\
1107 MUL R0.xyz, R0, R1.w; \n\
1108 # Compute R1= halfAngleVector= (R0+R4).normed(). \n\
1109 ADD R1.xyz, R0, R4; # R1= halfAngleVector \n\
1110 DP3 R1.w, R1, R1; # normalize R1. \n\
1111 RSQ R1.w, R1.w; \n\
1112 MUL R1.xyz, R1, R1.w; \n\
1113 # Compute Factors and colors. \n\
1114 DP3 R0.x, R6, R0; # R0.x= normal*lightDir \n\
1115 DP3 R0.yz, R6, R1; # R0.yz= normal*halfAngleVector \n\
1116 LIT R0.yz, R0; # R0.y= R0.x clamped, R0.z= pow(spec, R0.w) clamp \n\
1117 MAD R2, R0.y, c[CTS+2], R2; # R2= summed vertex color. \n\
1118 MAD R3, R0.z, c[CTS+6], R3; # R3= summed specular color. \n\
1120 " # Diffuse-Specular PointLight 1. \n\
1121 # Compute R0= (lightPos-vertex).normed(). \n\
1122 ADD R0.xyz, c[CTS+13], -R5; # R0= lightPos-vertex \n\
1123 DP3 R1.w, R0, R0; # normalize R0. \n\
1124 RSQ R1.w, R1.w; \n\
1125 MUL R0.xyz, R0, R1.w; \n\
1126 # Compute R1= halfAngleVector= (R0+R4).normed(). \n\
1127 ADD R1.xyz, R0, R4; # R1= halfAngleVector \n\
1128 DP3 R1.w, R1, R1; # normalize R1. \n\
1129 RSQ R1.w, R1.w; \n\
1130 MUL R1.xyz, R1, R1.w; \n\
1131 # Compute Factors and colors. \n\
1132 DP3 R0.x, R6, R0; # R0.x= normal*lightDir \n\
1133 DP3 R0.yz, R6, R1; # R0.yz= normal*halfAngleVector \n\
1134 LIT R0.yz, R0; # R0.y= R0.x clamped, R0.z= pow(spec, R0.w) clamp \n\
1135 MAD R2, R0.y, c[CTS+3], R2; # R2= summed vertex color. \n\
1136 MAD R3, R0.z, c[CTS+7], R3; # R3= summed specular color. \n\
1138 " # Diffuse-Specular PointLight 2. \n\
1139 # Compute R0= (lightPos-vertex).normed(). \n\
1140 ADD R0.xyz, c[CTS+14], -R5; # R0= lightPos-vertex \n\
1141 DP3 R1.w, R0, R0; # normalize R0. \n\
1142 RSQ R1.w, R1.w; \n\
1143 MUL R0.xyz, R0, R1.w; \n\
1144 # Compute R1= halfAngleVector= (R0+R4).normed(). \n\
1145 ADD R1.xyz, R0, R4; # R1= halfAngleVector \n\
1146 DP3 R1.w, R1, R1; # normalize R1. \n\
1147 RSQ R1.w, R1.w; \n\
1148 MUL R1.xyz, R1, R1.w; \n\
1149 # Compute Factors and colors. \n\
1150 DP3 R0.x, R6, R0; # R0.x= normal*lightDir \n\
1151 DP3 R0.yz, R6, R1; # R0.yz= normal*halfAngleVector \n\
1152 LIT R0.yz, R0; # R0.y= R0.x clamped, R0.z= pow(spec, R0.w) clamp \n\
1153 MAD R2, R0.y, c[CTS+4], R2; # R2= summed vertex color. \n\
1158 // The End code.
1159 static const char* LightingVPFragmentSpecular_End=
1160 " # output directly to secondary color. \n\
1161 MAD o[COL1], R0.z, c[CTS+8], R3; # final summed specular color. \n\
1163 # output diffuse to o[COL0], replacing alpha with material alpha. \n\
1164 MAD o[COL0], R2, c[CTS+10].zzzx, c[CTS+10].xxxw; \n\
1167 // ***************************************************************************
1168 static void strReplaceAll(string &strInOut, const string &tokenSrc, const string &tokenDst)
1170 string::size_type pos;
1171 string::difference_type srcLen= tokenSrc.size();
1172 while( (pos=strInOut.find(tokenSrc)) != string::npos)
1174 strInOut.replace(pos, srcLen, tokenDst);
1178 void CVertexProgramLighted::buildInfo()
1180 CVertexProgram::buildInfo();
1181 if (profile() == nelvp)
1183 // Fixed uniform locations
1184 m_IdxLighted.Ambient = m_FeaturesLighted.CtStartNeLVP + 0;
1185 for (uint i = 0; i < MaxLight; ++i)
1187 m_IdxLighted.Diffuse[i] = m_FeaturesLighted.CtStartNeLVP + 1 + i;
1189 if (m_FeaturesLighted.SupportSpecular)
1191 for (uint i = 0; i < MaxLight; ++i)
1193 m_IdxLighted.Specular[i] = m_FeaturesLighted.CtStartNeLVP + 5 + i;
1195 m_IdxLighted.DirOrPos[0] = 9;
1196 for (uint i = 1; i < MaxLight; ++i)
1198 m_IdxLighted.DirOrPos[i] = m_FeaturesLighted.CtStartNeLVP + (12 - 1) + i;
1200 m_IdxLighted.DiffuseAlpha = m_FeaturesLighted.CtStartNeLVP + 10;
1201 m_IdxLighted.EyePosition = m_FeaturesLighted.CtStartNeLVP + 11;
1203 else
1205 for (uint i = 0; i < MaxLight; ++i)
1207 m_IdxLighted.Specular[i] = ~0;
1209 for (uint i = 0; i < MaxLight; ++i)
1211 m_IdxLighted.DirOrPos[i] = m_FeaturesLighted.CtStartNeLVP + 5 + i;
1213 m_IdxLighted.DiffuseAlpha = m_FeaturesLighted.CtStartNeLVP + 9;
1214 m_IdxLighted.EyePosition = ~0;
1217 else
1219 // Named uniform locations
1220 // TODO_VP_GLSL
1221 // m_IdxLighted.Ambient = getUniformIndex("ambient");
1222 // etc
1225 nlassert(m_IdxLighted.Diffuse[0] != std::numeric_limits<uint>::max());
1226 if (m_FeaturesLighted.SupportSpecular)
1228 nlassert(m_IdxLighted.Specular[0] != std::numeric_limits<uint>::max());
1229 nlassert(m_IdxLighted.EyePosition != std::numeric_limits<uint>::max());
1231 nlassert(m_IdxLighted.DirOrPos[0] != std::numeric_limits<uint>::max());
1232 nlassert(m_IdxLighted.DiffuseAlpha != std::numeric_limits<uint>::max());
1235 // generates the lighting part of a vertex program, nelvp profile
1236 // ***************************************************************************
1237 std::string CRenderTrav::getLightVPFragmentNeLVP(uint numActivePointLights, uint ctStart, bool supportSpecular, bool normalize)
1239 string ret;
1241 // Code frag written for 4 light max.
1242 nlassert(MaxVPLight==4);
1243 nlassert(numActivePointLights<=MaxVPLight);
1245 // Add LightingVPFragmentNormalize fragment?
1246 if(normalize)
1247 ret+= LightingVPFragmentNormalize;
1249 // Which fragment to use...
1250 if(supportSpecular)
1252 // Add start of VP.
1253 ret+= LightingVPFragmentSpecular_Begin;
1255 // Add needed pointLights.
1256 for(uint i=0;i<numActivePointLights;i++)
1258 ret+= LightingVPFragmentSpecular_PL[i];
1261 // Add end of VP.
1262 ret+= LightingVPFragmentSpecular_End;
1264 else
1266 // Add start of VP.
1267 ret+= LightingVPFragmentNoSpecular_Begin;
1269 // Add needed pointLights.
1270 for(uint i=0;i<numActivePointLights;i++)
1272 ret+= LightingVPFragmentNoSpecular_PL[i];
1275 // Add end of VP.
1276 ret+= LightingVPFragmentNoSpecular_End;
1279 // Replace all CTS+x with good index. do it for 15 possible indices: 0 to 14 if specular.
1280 // run from 14 to 0 so CTS+14 will not be taken for a CTS+1 !!
1281 for(sint i=14; i>=0; i--)
1283 char tokenSrc[256];
1284 sprintf(tokenSrc, "CTS+%d", i);
1285 char tokenDst[256];
1286 sprintf(tokenDst, "%d", ctStart+i);
1287 // replace all in the string
1288 strReplaceAll(ret, tokenSrc, tokenDst);
1291 // verify no CTS+ leaved... (not all ctes parsed!!!)
1292 nlassert( ret.find("CTS+")==string::npos );
1294 return ret;
1298 // ***************************************************************************
1299 // ***************************************************************************
1300 // ***************************************************************************
1301 // ***************************************************************************
1303 // ***************************************************************************
1304 void CRenderTrav::clearRenderLandscapeList()
1306 _LandscapeRenderList.clear();
1309 // ***************************************************************************
1310 void CRenderTrav::addRenderLandscape(CLandscapeModel *model)
1312 _LandscapeRenderList.push_back(model);
1315 // ***************************************************************************
1316 void CRenderTrav::renderLandscapes()
1318 // Render Each Landscapes.
1319 for(uint i=0;i<_LandscapeRenderList.size();i++)
1321 _LandscapeRenderList[i]->clipAndRenderLandscape();
1325 // ***************************************************************************
1326 void CRenderTrav::setupTransparencySorting(uint8 maxPriority /*=0*/,uint NbDistanceEntries /*=1024*/)
1328 NLMISC::contReset(_OrderTransparentListByPriority); // avoid useless object copy when vector is resized (every element is reseted anyway)
1329 _OrderTransparentListByPriority.resize((uint) maxPriority + 1);
1330 for(uint k = 0; k < _OrderTransparentListByPriority.size(); ++k)
1332 _OrderTransparentListByPriority[k].init(NbDistanceEntries);
1333 if (k != 0) _OrderTransparentListByPriority[k].shareAllocator(_OrderTransparentListByPriority[0]); // node allocator is shared between all layers
1335 _MaxTransparencyPriority = maxPriority;
1338 // ***************************************************************************
1339 void CRenderTrav::clearWaterModelList()
1341 while (_FirstWaterModel)
1343 _FirstWaterModel->unlink();
1347 // ***************************************************************************
1348 void CRenderTrav::debugWaterModelMemory(const char *tag, bool dumpList)
1350 // Test Memory of water model render list (because someone crash it...)
1351 // Yoyo: this crash seems to be fixed, but i leave the code, in case of.....
1353 if(dumpList)
1354 _DebugWaterModelList.clear();
1356 CWaterModel *curr= _FirstWaterModel;
1357 while(curr)
1359 // the model in the list Must have not empty clipped poly
1360 CWaterModelDump dmp;
1361 dmp.Address= (void*)curr;
1362 curr->debugDumpMem(dmp.ClippedPolyBegin, dmp.ClippedPolyEnd);
1363 // if same ptr (begin==end), error!!
1364 if(dmp.ClippedPolyBegin==dmp.ClippedPolyEnd)
1366 // Before crash, do some log
1367 nlwarning("******* WaterModelList crash after %s", tag);
1368 nlwarning("Current: Ptr:%p. List:%p/%p", dmp.Address, dmp.ClippedPolyBegin, dmp.ClippedPolyEnd);
1369 // Log also the list bkuped (to do comparisons)
1370 for(uint i=0;i<_DebugWaterModelList.size();i++)
1372 CWaterModelDump &bkup= _DebugWaterModelList[i];
1373 nlwarning("List%02d: Ptr:%p. Array:%p/%p", i, bkup.Address, bkup.ClippedPolyBegin, bkup.ClippedPolyEnd);
1376 // crash (assert not stop for clearness)
1377 nlassert(dmp.ClippedPolyBegin!=dmp.ClippedPolyEnd);
1380 // bkup infos for future log
1381 if(dumpList)
1382 _DebugWaterModelList.push_back(dmp);
1384 // next
1385 curr= curr->_Next;