1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2012-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
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"
43 using namespace NLMISC
;
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 // ***************************************************************************
63 // ***************************************************************************
64 // ***************************************************************************
67 // ***************************************************************************
68 CRenderTrav::CRenderTrav()
70 RenderList
.resize(1024);
71 _CurrentNumVisibleModels
= 0;
72 _MaxTransparencyPriority
= 0;
73 OrderOpaqueList
.init(1024);
74 setupTransparencySorting();
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");
107 H_AUTO( NL3D_TravRender
);
108 if (getDriver()->isLost()) return; // device is lost so no need to render anything
109 CTravCameraScene::update();
112 getDriver()->setupViewport(_Viewport
);
114 // reset the light setup, and set global ambient.
119 // reset the Skin manager, if needed
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
,
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
164 CTransform
**itRdrModel
= NULL
;
165 uint32 nNbModels
= _CurrentNumVisibleModels
;
167 itRdrModel
= &RenderList
[0];
168 float rPseudoZ
, rPseudoZ2
;
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;
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!
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
);
214 NLMISC::OptFastFloorEnd();
217 if (renderPart
& UScene::RenderOpaque
)
219 // Render Opaque stuff.
220 // =============================
223 //OrderOpaqueList.reset(0);
224 //OrderTransparentList.reset(0);
226 // Clear any landscape
227 clearRenderLandscapeList();
229 // Start LodCharacter Manager render.
230 CLodCharacterManager
*clodMngr
= Scene
->getLodCharacterManager();
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
);
244 const std::string
*shapeName
= Scene
->getShapeBank()->getShapeNameFromShapePtr(trShape
->Shape
);
247 nlwarning("Displaying %s", shapeName
->c_str());
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.
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
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
290 _ShadowMapManager
.renderGenerate(Scene
);
292 // Render the Landscape
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;
328 numWantedVertices
+= curr
->getNumWantedVertices();
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);
344 tri
= curr
->fillVB(datas
, tri
, *getDriver());
345 nlassert(tri
<= numWantedVertices
);
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());
373 const std::string
*shapeName
= Scene
->getShapeBank()->getShapeNameFromShapePtr(trShape
->Shape
);
376 nlwarning("Displaying %s", shapeName
->c_str());
380 it
->get()->traverseRender();
385 // Profile this frame?
386 if(Scene
->isNextRenderProfile())
388 for(std::vector
<CLayeredOrderingTable
<CTransform
> >::iterator it
= _OrderTransparentListByPriority
.begin(); it
!= _OrderTransparentListByPriority
.end(); ++it
)
391 while( it
->get() != NULL
)
393 it
->get()->profileRender();
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());
415 const std::string
*shapeName
= Scene
->getShapeBank()->getShapeNameFromShapePtr(trShape
->Shape
);
418 nlwarning("Displaying %s", shapeName
->c_str());
422 it
->get()->traverseRender();
428 // Profile this frame?
429 if(Scene
->isNextRenderProfile())
431 for(std::vector
<CLayeredOrderingTable
<CTransform
> >::iterator it
= _OrderTransparentListByPriority
.begin(); it
!= _OrderTransparentListByPriority
.end(); ++it
)
434 while( it
->get() != NULL
)
436 if (!it
->get()->isFlare())
438 it
->get()->profileRender();
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());
461 const std::string
*shapeName
= Scene
->getShapeBank()->getShapeNameFromShapePtr(trShape
->Shape
);
464 nlwarning("Displaying %s", shapeName
->c_str());
468 it
->get()->traverseRender();
474 // Profile this frame?
475 if(Scene
->isNextRenderProfile())
477 for(std::vector
<CLayeredOrderingTable
<CTransform
> >::iterator it
= _OrderTransparentListByPriority
.begin(); it
!= _OrderTransparentListByPriority
.end(); ++it
)
480 while( it
->get() != NULL
)
482 if (it
->get()->isFlare())
484 it
->get()->profileRender();
493 // =============================
495 // clean: reset the light setup
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
)
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
)
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
563 // ***************************************************************************
564 // ***************************************************************************
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.
578 // Setup A default directionnal.
579 CVector
defDir(-0.5f
, 0.0, -0.85f
);
581 CRGBA aday
= CRGBA(130, 105, 119);
582 CRGBA dday
= CRGBA(238, 225, 204);
583 _DriverLight
[0].setupDirectional(aday
, dday
, dday
, defDir
);
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).
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
);
616 _CacheLightContribution
= NULL
;
619 _StrongestLightTouched
= true;
624 // ***************************************************************************
625 void CRenderTrav::changeLightSetup(CLightContribution
*lightContribution
, bool useLocalAttenuation
)
627 // If lighting System disabled, skip
628 if(!LightingSystemEnabled
)
633 // if same lightContribution, no-op.
634 if (_CacheLightContribution
== lightContribution
&& (lightContribution
== NULL
|| _LastLocalAttenuation
== useLocalAttenuation
))
636 // else, must setup the lights into driver.
639 _StrongestLightTouched
= true;
640 // if the setup is !NULL
641 if(lightContribution
)
643 // Compute SunAmbient / LocalAmbient
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.
654 // Setup the directionnal Sunlight.
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
;
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
682 // for the list of light.
683 while(lightContribution
->PointLight
[plId
]!=NULL
)
685 CPointLight
*pl
= lightContribution
->PointLight
[plId
];
687 if(useLocalAttenuation
)
688 inf
= lightContribution
->Factor
[plId
];
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
));
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]);
717 if(plId
>=NL3D_MAX_LIGHT_CONTRIBUTION
)
722 // Disable olds, enable news, and cache.
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);
742 _CacheLightContribution
= lightContribution
;
743 _NumLightEnabled
= newNumLightEnabled
;
744 _LastLocalAttenuation
= useLocalAttenuation
;
748 // Disable old lights, and cache.
750 // disable lights which are no more used.
751 for(i
=0; i
<_NumLightEnabled
; i
++)
753 Driver
->enableLight(uint8(i
), false);
757 _CacheLightContribution
= NULL
;
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
)
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)
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();
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)
815 for(i
=0; i
<_VPNumLights
; i
++)
817 _VPLightSpecular
[i
]= _DriverLight
[i
].getSpecular();
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.
839 lightDir
= invObjectWM
.mulVector(_DriverLight
[0].getDirection());
840 lightDir
.normalize();
842 Driver
->setUniform3f(IDriver::VertexProgram
, program
->idxLighted().DirOrPos
[0], lightDir
); // The sun is the same for every instance.
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.
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
;
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() ) )
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();
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
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
);
932 // setup other lights
933 for(; i
< _VPNumLights
; i
++)
935 color
= _VPLightDiffuse
[i
] * matDiff
;
936 Driver
->setUniform4f(IDriver::VertexProgram
, program
->idxLighted().Diffuse
[i
], color
);
941 if (program
->featuresLighted().SupportSpecular
)
943 for(i
= 0; i
< strongestLightIndex
; ++i
)
945 color
= _VPLightSpecular
[i
] * matSpec
;
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
);
958 // setup other lights
959 for(; i
< _VPNumLights
; i
++)
961 color
= _VPLightSpecular
[i
] * matSpec
;
963 Driver
->setUniform4f(IDriver::VertexProgram
, program
->idxLighted().Specular
[i
], color
);
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
;
1002 diffuse
= _StrongestLightDiffuse
;
1003 specular
= _StrongestLightSpecular
;
1008 // ***************************************************************************
1009 static const char* LightingVPFragmentNormalize
=
1010 " # normalize normal \n\
1011 DP3 R6.w, R6, R6; \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
=
1021 # Global Ambient. \n\
1022 MOV R2, c[CTS+0]; \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\
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\
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\
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\
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
=
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\
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\
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\
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\
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\
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\
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\
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\
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\
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;
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;
1219 // Named uniform locations
1221 // m_IdxLighted.Ambient = getUniformIndex("ambient");
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
)
1241 // Code frag written for 4 light max.
1242 nlassert(MaxVPLight
==4);
1243 nlassert(numActivePointLights
<=MaxVPLight
);
1245 // Add LightingVPFragmentNormalize fragment?
1247 ret
+= LightingVPFragmentNormalize
;
1249 // Which fragment to use...
1253 ret
+= LightingVPFragmentSpecular_Begin
;
1255 // Add needed pointLights.
1256 for(uint i
=0;i
<numActivePointLights
;i
++)
1258 ret
+= LightingVPFragmentSpecular_PL
[i
];
1262 ret
+= LightingVPFragmentSpecular_End
;
1267 ret
+= LightingVPFragmentNoSpecular_Begin
;
1269 // Add needed pointLights.
1270 for(uint i
=0;i
<numActivePointLights
;i
++)
1272 ret
+= LightingVPFragmentNoSpecular_PL
[i
];
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
--)
1284 sprintf(tokenSrc
, "CTS+%d", i
);
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
);
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.....
1354 _DebugWaterModelList
.clear();
1356 CWaterModel
*curr
= _FirstWaterModel
;
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
1382 _DebugWaterModelList
.push_back(dmp
);