1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "nel/misc/hierarchical_timer.h"
21 #include "nel/3d/landscape_model.h"
22 #include "nel/3d/landscape.h"
23 #include "nel/3d/cluster.h"
24 #include "nel/3d/scene.h"
25 #include "nel/3d/light_trav.h"
28 using namespace NLMISC
;
39 // ***************************************************************************
40 void CLandscapeModel::registerBasic()
42 CScene::registerModel(LandscapeModelId
, TransformId
, CLandscapeModel::creator
);
46 // ***************************************************************************
47 CLandscapeModel::CLandscapeModel()
49 Landscape
.OwnerModel
= this;
51 _ActiveAdditive
=false;
54 // The model is renderable
55 CTransform::setIsRenderable(true);
57 // RenderFilter: We are a Landscape
58 _RenderFilterType
= UScene::FilterLandscape
;
60 // Mesh support shadow map receiving only
61 CTransform::setIsShadowMapReceiver(true);
63 _RefineCenterUser
= CVector::Null
;
64 _RefineCenterAuto
= true;
68 // ***************************************************************************
69 void CLandscapeModel::initModel()
71 CTransform::initModel();
73 // Enable the landscape to be clipped by the Cluster System.
74 getOwnerScene()->getRoot()->clipDelChild(this);
75 getOwnerScene()->getRootCluster()->clipAddChild(this);
77 // After creating the landscape (and so the VegetableManager in the ctor).
78 // we must init correclty the VegetableManager.
79 Landscape
.createVegetableBlendLayersModels(getOwnerScene());
83 // ***************************************************************************
84 void CLandscapeModel::traverseHrc()
87 CTransform::traverseHrc();
89 /* Compile Animate PointLight here.
90 NB: Animated PointLight are important for Dynamic Object lighted by landscape lights.
91 The landscape and vegetable are not influenced by thoses animation (as if the light were not animated)
92 NB: important to do before the LightTrav. Also even if landscape is hidden, still must animate those light?
94 Landscape
.setPointLightFactor(*getOwnerScene());
98 // ***************************************************************************
99 void CLandscapeModel::traverseClip()
101 CClipTrav
&clipTrav
= getOwnerScene()->getClipTrav();
103 /// if already clipped in this frame, This means that we are visible from 2 CCluster.
104 if (_ClipDate
== clipTrav
.CurrentDate
)
106 // We may set the WorldFrustumPyramid. But do this only if not already set.
107 if(!ClusteredPyramidIsFrustum
)
109 /* If the current pyramid is different from the last stored pyramid, this mean per exemple
110 that the player is in a room and look the landscape from 2 windows through portals.
111 The other case is that the camera is in 2 clusters, so the clusterSystem start from this 2, and so we arrive
112 here 2 times (but through the same portal => same pyramid obviously...)
114 if( ClusteredPyramid
!=clipTrav
.WorldPyramid
)
116 // in this rare case take the Whole frustum pyramid (a more correct code is to make an Union of the 2 pyramids...)
117 ClusteredPyramid
= clipTrav
.WorldFrustumPyramid
;
118 ClusteredPyramidIsFrustum
= true;
124 // If the camera is in Rootcluster or visible through a protal from one Room Cluster only, use the clustered pyramid.
125 ClusteredPyramid
= clipTrav
.WorldPyramid
;
126 // We are not sure we are the FrustumPyramid
127 ClusteredPyramidIsFrustum
= false;
130 // Call normal CTransform traverseClip to insert this model in render list etc...
131 // NB: CTransform::traverseClip() update _ClipDate...
132 CTransform::traverseClip();
136 // ***************************************************************************
137 void CLandscapeModel::clipAndRenderLandscape()
140 NB: For best Optimisation, it is important that the Clip act at same time as the Render.
141 Why? For complex GPU/CPU synchronisation.
143 Since the landscape is rendered at end of the opaque pass, the next clip of the next frame will arise
144 too early (if we assume no transparent or out-scene rendering), RESULTING IN A LOCK => A CPU STALL!!
146 In a "landscape only" program, this is not easily avoidable. But in common programs with other
147 opaque meshs, skinning etc... we'll have lot of CPU/GPU Work between this clip and the Landscape Render.
148 And since the clip() lock the VB, and stall, all those tasks won't be parralelized.
150 I have already seen such a stall (3 ms).
152 Therefore, all Landscape VB Lock arise at the same time=> The GPU will render the landscape while the CPU
153 will prepare render and do skinning of the next opaque frame for instance.
159 // The real Landscape clip is done here, after std clip
160 H_AUTO( NL3D_Landscape_Clip
);
162 // Should be unlocked
163 nlassert (!Landscape
.isLocked());
165 CClipTrav
&clipTrav
= getOwnerScene()->getClipTrav();
166 CRenderTrav
&renderTrav
= getOwnerScene()->getRenderTrav();
168 // Yes, this is ugly, but the clip pass is finished in render(), for clipping TessBlocks.
169 // This saves another Landscape patch traversal, so this is faster...
170 // Order them in order which clip faster (first horizontal, then vertical).
171 // NB: TessBlock are ALWAYS clipped with the frustum pyramid, not the clustered one (faster clip for most common cases).
172 CurrentPyramid
[0]= clipTrav
.WorldFrustumPyramid
[NL3D_CLIP_PLANE_LEFT
];
173 CurrentPyramid
[1]= clipTrav
.WorldFrustumPyramid
[NL3D_CLIP_PLANE_RIGHT
];
174 CurrentPyramid
[2]= clipTrav
.WorldFrustumPyramid
[NL3D_CLIP_PLANE_TOP
];
175 CurrentPyramid
[3]= clipTrav
.WorldFrustumPyramid
[NL3D_CLIP_PLANE_BOTTOM
];
176 nlassert(NL3D_TESSBLOCK_NUM_CLIP_PLANE
==4);
178 // Before Landscape clip, must setup Driver, for good VB allocation.
179 Landscape
.setDriver(getOwnerScene()->getRenderTrav().getDriver());
181 // get the refineCenter.
182 CVector refineCenter
;
183 if(_RefineCenterAuto
)
184 refineCenter
= clipTrav
.CamPos
;
186 refineCenter
= _RefineCenterUser
;
188 Landscape
.lockBuffers ();
190 // Use the Clustered pyramid for Patch, but Frustum pyramid for TessBlocks.
191 // We are sure that pyramid has normalized plane normals.
192 Landscape
.clip(refineCenter
, ClusteredPyramid
);
198 // Change the landscape center. All Geomorphed pos (in VertexBuffer only or during VertexProgram)
199 // subtract this position.
200 Landscape
.setPZBModelPosition(renderTrav
.CamPos
);
202 /* setup the model matrix
203 ZBuffer Precision: set the modelMatrix to the current landscape PZBModelPosition.
204 NB: don't use renderTrav.CamPos directly because setPZBModelPosition() may modify the position
206 _RenderWorldMatrix
.identity();
207 _RenderWorldMatrix
.setPos(Landscape
.getPZBModelPosition());
208 renderTrav
.getDriver()->setupModelMatrix(_RenderWorldMatrix
);
211 // Scene Time/Lighting Mgt.
212 CScene
*scene
= getOwnerScene();
215 // For vegetable, set the animation Time.
216 Landscape
.setVegetableTime(scene
->getCurrentTime());
218 // For vegetable updateLighting, set the system Time.
219 Landscape
.setVegetableUpdateLightingTime(scene
->getCurrentSystemTime());
222 H_BEFORE( NL3D_Landscape_UpdateLighting
);
223 Landscape
.updateLighting(scene
->getCurrentSystemTime());
224 H_AFTER( NL3D_Landscape_UpdateLighting
);
226 // if SceneLighting enabled
227 if(scene
->isLightingSystemEnabled())
229 H_AUTO( NL3D_Landscape_DynamicLighting
);
231 // For vegetable, set the lighting
232 Landscape
.setupVegetableLighting(scene
->getSunAmbient(), scene
->getSunDiffuse(),
233 scene
->getSunDirection());
235 // current visible Dynamic light list are registered in LightTrav::LightingManager
236 CLightTrav
&lightTrav
= scene
->getLightTrav();
237 // Get all dynamic light list, and light landscape with it.
238 Landscape
.computeDynamicLighting(lightTrav
.LightingManager
.getAllDynamicLightList());
243 H_BEFORE( NL3D_Landscape_Refine
);
244 Landscape
.refine(refineCenter
);
245 H_AFTER( NL3D_Landscape_Refine
);
248 H_BEFORE( NL3D_Landscape_Render
);
249 Landscape
.render(refineCenter
, renderTrav
.CamLook
, CurrentPyramid
, isAdditive ());
250 H_AFTER( NL3D_Landscape_Render
);
252 // Should be unlocked by render
253 nlassert (!Landscape
.isLocked());
256 // ***************************************************************************
257 void CLandscapeModel::traverseRender()
259 CRenderTrav
&renderTRav
= getOwnerScene()->getRenderTrav();
261 // No-Op. But delay the clip and render to the end of Opaque Rendering. For VBLock optim
262 renderTRav
.addRenderLandscape(this);
264 // If the landscape receive shadow, then add it.
265 if(canReceiveShadowMap())
266 renderTRav
.getShadowMapManager().addShadowReceiver(this);
269 // ***************************************************************************
270 void CLandscapeModel::profileRender()
272 Landscape
.profileRender();
276 // ***************************************************************************
277 // ***************************************************************************
278 // Dynamic ShadowMaping
279 // ***************************************************************************
280 // ***************************************************************************
283 // ***************************************************************************
284 void CLandscapeModel::getReceiverBBox(CAABBox
&bbox
)
286 // Build a dummy bbox around the TileNear distance.
287 bbox
.setCenter(Landscape
.getOldRefineCenter());
288 float hs
= Landscape
.getTileNear() * 1.5f
; // Enlarge a little because may have some triangles outside this dist.
289 bbox
.setHalfSize(CVector(hs
,hs
,hs
));
292 // ***************************************************************************
293 void CLandscapeModel::receiveShadowMap(CShadowMap
*shadowMap
, const CVector
&casterPos
, const CMaterial
&shadowMat
)
295 IDriver
*drv
= getOwnerScene()->getRenderTrav().getDriver();
297 // Setup the matrix. The same than one used for render (ie PZB included)
298 drv
->setupModelMatrix(_RenderWorldMatrix
);
301 Landscape
.receiveShadowMap(drv
, shadowMap
, casterPos
, shadowMat
, Landscape
.getPZBModelPosition());