/src/ogre/OgreMain/src/OgreTextureShadowRenderer.cpp
Line | Count | Source |
1 | | // This file is part of the OGRE project. |
2 | | // It is subject to the license terms in the LICENSE file found in the top-level directory |
3 | | // of this distribution and at https://www.ogre3d.org/licensing. |
4 | | // SPDX-License-Identifier: MIT |
5 | | |
6 | | #include "OgreStableHeaders.h" |
7 | | #include "OgreShadowCameraSetup.h" |
8 | | #include "OgreViewport.h" |
9 | | #include "OgreHardwarePixelBuffer.h" |
10 | | #include "OgreCompositorManager.h" |
11 | | #include "OgreCompositor.h" |
12 | | |
13 | | namespace Ogre { |
14 | | |
15 | | SceneManager::TextureShadowRenderer::TextureShadowRenderer(SceneManager* owner) : |
16 | 0 | mSceneManager(owner), |
17 | 0 | mShadowCasterPlainBlackPass(0), |
18 | 0 | mShadowReceiverPass(0), |
19 | 0 | mShadowTextureCustomCasterPass(0), |
20 | 0 | mShadowTextureCustomReceiverPass(0), |
21 | 0 | mDefaultShadowFarDist(0), |
22 | 0 | mDefaultShadowFarDistSquared(0), |
23 | 0 | mShadowTextureOffset(0.6), |
24 | 0 | mShadowTextureFadeStart(0.7), |
25 | 0 | mShadowTextureFadeEnd(0.9), |
26 | 0 | mShadowTextureSelfShadow(false), |
27 | 0 | mShadowTextureConfigDirty(true), |
28 | 0 | mShadowCasterRenderBackFaces(true) |
29 | 0 | { |
30 | | // set up default shadow camera setup |
31 | 0 | mDefaultShadowCameraSetup = DefaultShadowCameraSetup::create(); |
32 | |
|
33 | 0 | mCullCameraSetup = DefaultShadowCameraSetup::create(); |
34 | | |
35 | | // init shadow texture count per type. |
36 | 0 | mShadowTextureCountPerType[Light::LT_POINT] = 1; |
37 | 0 | mShadowTextureCountPerType[Light::LT_DIRECTIONAL] = 1; |
38 | 0 | mShadowTextureCountPerType[Light::LT_SPOTLIGHT] = 1; |
39 | 0 | } |
40 | | |
41 | 0 | SceneManager::TextureShadowRenderer::~TextureShadowRenderer() {} |
42 | | |
43 | | void SceneManager::TextureShadowRenderer::render(RenderQueueGroup* group, |
44 | | QueuedRenderableCollection::OrganisationMode om) |
45 | 0 | { |
46 | | // Receiver pass(es) |
47 | 0 | if (mSceneManager->isShadowTechniqueAdditive()) |
48 | 0 | { |
49 | | // Auto-additive |
50 | 0 | renderAdditiveTextureShadowedQueueGroupObjects(group, om); |
51 | 0 | return; |
52 | 0 | } |
53 | | |
54 | | // Modulative |
55 | 0 | renderModulativeTextureShadowedQueueGroupObjects(group, om); |
56 | 0 | } |
57 | | |
58 | | size_t SceneManager::TextureShadowRenderer::getShadowTexIndex(size_t startLightIndex) |
59 | 0 | { |
60 | 0 | size_t shadowTexIndex = mShadowTextures.size(); |
61 | 0 | if (mShadowTextureIndexLightList.size() > startLightIndex) |
62 | 0 | shadowTexIndex = mShadowTextureIndexLightList[startLightIndex]; |
63 | 0 | return shadowTexIndex; |
64 | 0 | } |
65 | | //----------------------------------------------------------------------- |
66 | | void SceneManager::TextureShadowRenderer::renderTextureShadowCasterQueueGroupObjects( |
67 | | RenderQueueGroup* pGroup, |
68 | | QueuedRenderableCollection::OrganisationMode om) |
69 | 0 | { |
70 | | // This is like the basic group render, except we skip all transparents |
71 | | // and we also render any non-shadowed objects |
72 | | // Note that non-shadow casters will have already been eliminated during |
73 | | // _findVisibleObjects |
74 | | |
75 | | // Iterate through priorities |
76 | | |
77 | | // Override auto param ambient to force vertex programs and fixed function to |
78 | 0 | ColourValue currAmbient = mSceneManager->getAmbientLight(); |
79 | 0 | if (mSceneManager->isShadowTechniqueAdditive()) |
80 | 0 | { |
81 | | // Use simple black / white mask if additive |
82 | 0 | mSceneManager->setAmbientLight(ColourValue::Black); |
83 | 0 | } |
84 | 0 | else |
85 | 0 | { |
86 | | // Use shadow colour as caster colour if modulative |
87 | 0 | mSceneManager->setAmbientLight(mSceneManager->getShadowColour()); |
88 | 0 | } |
89 | |
|
90 | 0 | auto visitor = mSceneManager->getQueuedRenderableVisitor(); |
91 | 0 | for (const auto& pg : pGroup->getPriorityGroups()) |
92 | 0 | { |
93 | 0 | RenderPriorityGroup* pPriorityGrp = pg.second; |
94 | | |
95 | | // Sort the queue first |
96 | 0 | pPriorityGrp->sort(mSceneManager->mCameraInProgress); |
97 | | |
98 | | // Do solids, override light list incase any vertex programs use them |
99 | 0 | visitor->renderObjects(pPriorityGrp->getSolidsBasic(), om, false, false); |
100 | 0 | visitor->renderObjects(pPriorityGrp->getSolidsNoShadowReceive(), om, false, false); |
101 | | // Do unsorted transparents that cast shadows |
102 | 0 | visitor->renderObjects(pPriorityGrp->getTransparentsUnsorted(), om, false, false, nullptr, true); |
103 | | // Do transparents that cast shadows |
104 | 0 | visitor->renderObjects(pPriorityGrp->getTransparents(), QueuedRenderableCollection::OM_SORT_DESCENDING, false, |
105 | 0 | false, nullptr, true); |
106 | |
|
107 | 0 | }// for each priority |
108 | | |
109 | | // reset ambient light |
110 | 0 | mSceneManager->setAmbientLight(currAmbient); |
111 | 0 | } |
112 | | //----------------------------------------------------------------------- |
113 | | void SceneManager::TextureShadowRenderer::renderModulativeTextureShadowedQueueGroupObjects( |
114 | | RenderQueueGroup* pGroup, |
115 | | QueuedRenderableCollection::OrganisationMode om) |
116 | 0 | { |
117 | | /* For each light, we need to render all the solids from each group, |
118 | | then do the modulative shadows, then render the transparents from |
119 | | each group. |
120 | | Now, this means we are going to reorder things more, but that it required |
121 | | if the shadows are to look correct. The overall order is preserved anyway, |
122 | | it's just that all the transparents are at the end instead of them being |
123 | | interleaved as in the normal rendering loop. |
124 | | */ |
125 | | // Iterate through priorities |
126 | 0 | auto visitor = mSceneManager->getQueuedRenderableVisitor(); |
127 | 0 | for (const auto& pg : pGroup->getPriorityGroups()) |
128 | 0 | { |
129 | 0 | RenderPriorityGroup* pPriorityGrp = pg.second; |
130 | | |
131 | | // Sort the queue first |
132 | 0 | pPriorityGrp->sort(mSceneManager->mCameraInProgress); |
133 | | |
134 | | // Do solids |
135 | 0 | visitor->renderObjects(pPriorityGrp->getSolidsBasic(), om, true, true); |
136 | 0 | visitor->renderObjects(pPriorityGrp->getSolidsNoShadowReceive(), om, true, true); |
137 | 0 | } |
138 | | |
139 | | |
140 | | // Iterate over lights, render received shadows |
141 | | // only perform this if we're in the 'normal' render stage, to avoid |
142 | | // doing it during the render to texture |
143 | 0 | if (mSceneManager->mIlluminationStage == IRS_NONE) |
144 | 0 | { |
145 | 0 | mSceneManager->mIlluminationStage = IRS_RENDER_RECEIVER_PASS; |
146 | |
|
147 | 0 | LightList::const_iterator i, iend; |
148 | 0 | iend = mSceneManager->_getLightsAffectingFrustum().end(); |
149 | |
|
150 | 0 | size_t si = 0; |
151 | 0 | for (i = mSceneManager->_getLightsAffectingFrustum().begin(); |
152 | 0 | i != iend && si < mShadowTextures.size(); ++i) |
153 | 0 | { |
154 | 0 | Light* l = *i; |
155 | |
|
156 | 0 | if (!l->getCastShadows()) |
157 | 0 | continue; |
158 | | |
159 | | // Get camera for current shadow texture |
160 | 0 | Camera *cam = mShadowTextures[si]->getBuffer()->getRenderTarget()->getViewport(0)->getCamera(); |
161 | | // Hook up receiver texture |
162 | 0 | Pass* targetPass = mShadowTextureCustomReceiverPass ? |
163 | 0 | mShadowTextureCustomReceiverPass : mShadowReceiverPass; |
164 | | |
165 | | // if this light is a spotlight, we need to add the spot fader layer |
166 | | // BUT not if using a custom projection matrix, since then it will be |
167 | | // inappropriately shaped most likely |
168 | 0 | if (l->getType() == Light::LT_SPOTLIGHT && !cam->isCustomProjectionMatrixEnabled()) |
169 | 0 | { |
170 | | // remove all TUs except 0 & 1 |
171 | | // (only an issue if additive shadows have been used) |
172 | 0 | while(targetPass->getNumTextureUnitStates() > 2) |
173 | 0 | targetPass->removeTextureUnitState(2); |
174 | |
|
175 | 0 | TextureUnitState* t = NULL; |
176 | | // Add spot fader if not present already |
177 | 0 | if (targetPass->getNumTextureUnitStates() == 2 && |
178 | 0 | targetPass->getTextureUnitState(1)->_getTexturePtr() == mSpotFadeTexture) |
179 | 0 | { |
180 | | // Just set |
181 | 0 | t = targetPass->getTextureUnitState(1); |
182 | 0 | } |
183 | 0 | else |
184 | 0 | { |
185 | | // Remove any non-conforming spot layers |
186 | 0 | while(targetPass->getNumTextureUnitStates() > 1) |
187 | 0 | targetPass->removeTextureUnitState(1); |
188 | |
|
189 | 0 | t = targetPass->createTextureUnitState(); |
190 | 0 | t->setTexture(mSpotFadeTexture); |
191 | 0 | t->setColourOperation(LBO_ADD); |
192 | 0 | t->setTextureAddressingMode(TextureUnitState::TAM_CLAMP); |
193 | 0 | } |
194 | |
|
195 | 0 | t->setProjectiveTexturing(!targetPass->hasVertexProgram(), cam); |
196 | 0 | mSceneManager->mAutoParamDataSource->setTextureProjector(cam, 1); |
197 | 0 | } |
198 | 0 | else |
199 | 0 | { |
200 | | // remove all TUs except 0 including spot |
201 | 0 | while(targetPass->getNumTextureUnitStates() > 1) |
202 | 0 | targetPass->removeTextureUnitState(1); |
203 | |
|
204 | 0 | } |
205 | | |
206 | | // account for the RTSS |
207 | 0 | if (auto betterTechnique = targetPass->getParent()->getParent()->getBestTechnique()) |
208 | 0 | { |
209 | 0 | targetPass = betterTechnique->getPass(0); |
210 | 0 | } |
211 | |
|
212 | 0 | TextureUnitState* texUnit = targetPass->getTextureUnitState(0); |
213 | | // clamp to border colour in case this is a custom material |
214 | 0 | texUnit->setSampler(mBorderSampler); |
215 | 0 | resolveShadowTexture(texUnit, si, 0); |
216 | | |
217 | | // Set lighting / blending modes |
218 | 0 | targetPass->setSceneBlending(SBF_DEST_COLOUR, SBF_ZERO); |
219 | 0 | targetPass->setLightingEnabled(false); |
220 | |
|
221 | 0 | targetPass->_load(); |
222 | | |
223 | | // Fire pre-receiver event |
224 | 0 | fireShadowTexturesPreReceiver(l, cam); |
225 | |
|
226 | 0 | renderTextureShadowReceiverQueueGroupObjects(pGroup, om); |
227 | |
|
228 | 0 | ++si; |
229 | |
|
230 | 0 | } // for each light |
231 | |
|
232 | 0 | mSceneManager->mIlluminationStage = IRS_NONE; |
233 | |
|
234 | 0 | } |
235 | 0 | } |
236 | | //----------------------------------------------------------------------- |
237 | | void SceneManager::TextureShadowRenderer::renderAdditiveTextureShadowedQueueGroupObjects( |
238 | | RenderQueueGroup* pGroup, |
239 | | QueuedRenderableCollection::OrganisationMode om) |
240 | 0 | { |
241 | 0 | LightList lightList(1); |
242 | 0 | auto visitor = mSceneManager->getQueuedRenderableVisitor(); |
243 | |
|
244 | 0 | for (const auto& pg : pGroup->getPriorityGroups()) |
245 | 0 | { |
246 | 0 | RenderPriorityGroup* pPriorityGrp = pg.second; |
247 | | |
248 | | // Sort the queue first |
249 | 0 | pPriorityGrp->sort(mSceneManager->mCameraInProgress); |
250 | | |
251 | | // Render all the ambient passes first, no light iteration, no lights |
252 | 0 | visitor->renderObjects(pPriorityGrp->getSolidsBasic(), om, false, false); |
253 | | // Also render any objects which have receive shadows disabled |
254 | 0 | visitor->renderObjects(pPriorityGrp->getSolidsNoShadowReceive(), om, true, true); |
255 | | |
256 | | |
257 | | // only perform this next part if we're in the 'normal' render stage, to avoid |
258 | | // doing it during the render to texture |
259 | 0 | if (mSceneManager->mIlluminationStage == IRS_NONE) |
260 | 0 | { |
261 | | // Iterate over lights, render masked |
262 | 0 | size_t si = 0; |
263 | |
|
264 | 0 | for (Light* l : mSceneManager->_getLightsAffectingFrustum()) |
265 | 0 | { |
266 | 0 | if (l->getCastShadows() && si < mShadowTextures.size()) |
267 | 0 | { |
268 | | // Hook up receiver texture |
269 | 0 | Pass* targetPass = mShadowTextureCustomReceiverPass ? |
270 | 0 | mShadowTextureCustomReceiverPass : mShadowReceiverPass; |
271 | | |
272 | | // account for the RTSS |
273 | 0 | if (auto betterTechnique = targetPass->getParent()->getParent()->getBestTechnique()) |
274 | 0 | { |
275 | 0 | targetPass = betterTechnique->getPass(0); |
276 | 0 | } |
277 | |
|
278 | 0 | TextureUnitState* texUnit = targetPass->getTextureUnitState(0); |
279 | | // clamp to border colour in case this is a custom material |
280 | 0 | texUnit->setSampler(mBorderSampler); |
281 | 0 | resolveShadowTexture(texUnit, si, 0); |
282 | | |
283 | | // Remove any spot fader layer |
284 | 0 | if (targetPass->getNumTextureUnitStates() > 1 && |
285 | 0 | targetPass->getTextureUnitState(1)->getTextureName() == "spot_shadow_fade.dds") |
286 | 0 | { |
287 | | // remove spot fader layer (should only be there if |
288 | | // we previously used modulative shadows) |
289 | 0 | targetPass->removeTextureUnitState(1); |
290 | 0 | } |
291 | | // Set lighting / blending modes |
292 | 0 | targetPass->setSceneBlending(SBF_ONE, SBF_ONE); |
293 | 0 | targetPass->setLightingEnabled(true); |
294 | 0 | targetPass->_load(); |
295 | | |
296 | | // increment shadow texture since used |
297 | 0 | ++si; |
298 | |
|
299 | 0 | mSceneManager->mIlluminationStage = IRS_RENDER_RECEIVER_PASS; |
300 | |
|
301 | 0 | } |
302 | 0 | else |
303 | 0 | { |
304 | 0 | mSceneManager->mIlluminationStage = IRS_NONE; |
305 | 0 | } |
306 | | |
307 | | // render lighting passes for this light |
308 | 0 | lightList[0] = l; |
309 | | |
310 | | // set up light scissoring, always useful in additive modes |
311 | 0 | ClipResult scissored = mSceneManager->buildAndSetScissor(lightList, mSceneManager->mCameraInProgress); |
312 | 0 | ClipResult clipped = CLIPPED_NONE; |
313 | 0 | if(mSceneManager->getShadowUseLightClipPlanes()) |
314 | 0 | clipped = mSceneManager->buildAndSetLightClip(lightList); |
315 | | // skip if entirely clipped |
316 | 0 | if(scissored == CLIPPED_ALL || clipped == CLIPPED_ALL) |
317 | 0 | continue; |
318 | | |
319 | 0 | visitor->renderObjects(pPriorityGrp->getSolidsDiffuseSpecular(), om, false, false, &lightList); |
320 | 0 | if (scissored == CLIPPED_SOME) |
321 | 0 | mSceneManager->resetScissor(); |
322 | 0 | if (clipped == CLIPPED_SOME) |
323 | 0 | mSceneManager->resetLightClip(); |
324 | |
|
325 | 0 | }// for each light |
326 | |
|
327 | 0 | mSceneManager->mIlluminationStage = IRS_NONE; |
328 | | |
329 | | // Now render decal passes, no need to set lights as lighting will be disabled |
330 | 0 | visitor->renderObjects(pPriorityGrp->getSolidsDecal(), om, false, false); |
331 | |
|
332 | 0 | } |
333 | | |
334 | |
|
335 | 0 | }// for each priority |
336 | 0 | } |
337 | | //----------------------------------------------------------------------- |
338 | | void SceneManager::TextureShadowRenderer::renderTextureShadowReceiverQueueGroupObjects( |
339 | | RenderQueueGroup* pGroup, |
340 | | QueuedRenderableCollection::OrganisationMode om) |
341 | 0 | { |
342 | | // Iterate through priorities |
343 | | |
344 | | // Override auto param ambient to force vertex programs to go full-bright |
345 | 0 | ColourValue currAmbient = mSceneManager->getAmbientLight(); |
346 | 0 | mSceneManager->setAmbientLight(ColourValue::White); |
347 | 0 | auto visitor = mSceneManager->getQueuedRenderableVisitor(); |
348 | |
|
349 | 0 | for (const auto& pg : pGroup->getPriorityGroups()) |
350 | 0 | { |
351 | 0 | RenderPriorityGroup* pPriorityGrp = pg.second; |
352 | | |
353 | | // Do solids, override light list incase any vertex programs use them |
354 | 0 | visitor->renderObjects(pPriorityGrp->getSolidsBasic(), om, false, false); |
355 | | |
356 | | // Don't render transparents or passes which have shadow receipt disabled |
357 | |
|
358 | 0 | }// for each priority |
359 | | |
360 | | // reset ambient |
361 | 0 | mSceneManager->setAmbientLight(currAmbient); |
362 | 0 | } |
363 | | //--------------------------------------------------------------------- |
364 | | void SceneManager::TextureShadowRenderer::setupRenderTarget(const String& camName, RenderTarget* rendTarget, |
365 | | uint16 depthBufferId) |
366 | 0 | { |
367 | 0 | if (rendTarget->getDepthBufferPool() != RBP_NONE) |
368 | 0 | { |
369 | 0 | rendTarget->setDepthBufferPool(depthBufferId); |
370 | 0 | } |
371 | | |
372 | | // Don't update automatically - we'll do it when required |
373 | 0 | rendTarget->setAutoUpdated(false); |
374 | |
|
375 | 0 | float aspectRatio = (Real)rendTarget->getWidth() / (Real)rendTarget->getHeight(); |
376 | | |
377 | | // Create camera for this texture, but note that we have to rebind |
378 | | // in prepareShadowTextures to coexist with multiple SMs |
379 | 0 | Camera* cam = mSceneManager->createCamera(camName); |
380 | 0 | cam->setAspectRatio(aspectRatio); |
381 | 0 | auto camNode = mSceneManager->getRootSceneNode()->createChildSceneNode(); |
382 | 0 | camNode->attachObject(cam); |
383 | 0 | mShadowTextureCameras.push_back(cam); |
384 | | // insert dummy camera-light combination |
385 | 0 | mShadowCamLightMapping[cam] = NULL; |
386 | | |
387 | | // use separate culling camera, in case a focused shadow setup is used |
388 | | // in which case we want to keep the original light frustum for culling |
389 | 0 | Camera* cullCam = mSceneManager->createCamera(camName+"/Cull"); |
390 | 0 | cullCam->setAspectRatio(aspectRatio); |
391 | 0 | cam->setCullingFrustum(cullCam); |
392 | 0 | camNode->attachObject(cullCam); |
393 | | |
394 | | // Create a viewport, if not there already |
395 | 0 | if (rendTarget->getNumViewports() != 0) |
396 | 0 | return; |
397 | | |
398 | | // Note camera assignment is transient when multiple SMs |
399 | 0 | Viewport *v = rendTarget->addViewport(NULL); |
400 | 0 | v->setClearEveryFrame(true); |
401 | 0 | v->setOverlaysEnabled(false); |
402 | 0 | v->setBackgroundColour(ColourValue::White); |
403 | 0 | } |
404 | | |
405 | | void SceneManager::TextureShadowRenderer::ensureShadowTexturesCreated() |
406 | 0 | { |
407 | 0 | if(!mBorderSampler) |
408 | 0 | { |
409 | 0 | mBorderSampler = TextureManager::getSingleton().createSampler(); |
410 | 0 | mBorderSampler->setAddressingMode(TAM_BORDER); |
411 | 0 | mBorderSampler->setBorderColour(ColourValue::White); |
412 | 0 | mBorderSampler->setFiltering(FT_MIP, FO_NONE); // we do not have mips. GLES2 is particularly picky here. |
413 | 0 | } |
414 | |
|
415 | 0 | if (mShadowTextureConfigDirty) |
416 | 0 | { |
417 | 0 | destroyShadowTextures(); |
418 | 0 | ShadowTextureManager::getSingleton().getShadowTextures(mShadowTextureConfigList, mShadowTextures); |
419 | | |
420 | | // clear shadow cam - light mapping |
421 | 0 | mShadowCamLightMapping.clear(); |
422 | | |
423 | | //Used to get the depth buffer ID setting for each RTT |
424 | 0 | size_t __i = 0; |
425 | | |
426 | | // Recreate shadow textures |
427 | 0 | for (auto& shadowTex : mShadowTextures) |
428 | 0 | { |
429 | 0 | uint16 depthBufferId = mShadowTextureConfigList[__i].depthBufferPoolId; |
430 | 0 | for(uint32 i = 0; i < shadowTex->getNumLayers(); i++) |
431 | 0 | { |
432 | 0 | String camName = StringUtil::format("%sCam%d", shadowTex->getName().c_str(), i); |
433 | 0 | auto rtidx = shadowTex->getUsage() & TU_TARGET_ALL_LAYERS ? 0 : i; |
434 | 0 | setupRenderTarget(camName, shadowTex->getRenderTarget(rtidx), depthBufferId); |
435 | 0 | } |
436 | |
|
437 | 0 | ++__i; |
438 | 0 | } |
439 | | |
440 | | // Assume first one is representative |
441 | 0 | if (!mShadowTextureConfigList.empty()) |
442 | 0 | mNoShadowTexture = |
443 | 0 | ShadowTextureManager::getSingleton().getNoShadowTexture(mShadowTextureConfigList[0].format); |
444 | |
|
445 | 0 | mShadowTextureConfigDirty = false; |
446 | 0 | } |
447 | |
|
448 | 0 | } |
449 | | //--------------------------------------------------------------------- |
450 | | void SceneManager::TextureShadowRenderer::destroyShadowTextures(void) |
451 | 0 | { |
452 | 0 | for (auto cam : mShadowTextureCameras) |
453 | 0 | { |
454 | 0 | mSceneManager->getRootSceneNode()->removeAndDestroyChild(cam->getParentSceneNode()); |
455 | | |
456 | | // Always destroy camera since they are local to this SM |
457 | 0 | if(auto cullcam = dynamic_cast<Camera*>(cam->getCullingFrustum())) |
458 | 0 | mSceneManager->destroyCamera(cullcam); |
459 | |
|
460 | 0 | mSceneManager->destroyCamera(cam); |
461 | 0 | } |
462 | 0 | mShadowTextures.clear(); |
463 | 0 | mShadowTextureCameras.clear(); |
464 | | |
465 | | // set by render*TextureShadowedQueueGroupObjects |
466 | 0 | mSceneManager->mAutoParamDataSource->setTextureProjector(NULL, 0); |
467 | | |
468 | | // Will destroy if no other scene managers referencing |
469 | 0 | ShadowTextureManager::getSingleton().clearUnused(); |
470 | |
|
471 | 0 | mShadowTextureConfigDirty = true; |
472 | 0 | } |
473 | | //--------------------------------------------------------------------- |
474 | | void SceneManager::TextureShadowRenderer::prepareTexCam(Camera* texCam, Camera* cam, Viewport* vp, Light* light, |
475 | | size_t j) |
476 | 0 | { |
477 | | // Associate main view camera as LOD camera |
478 | 0 | texCam->setLodCamera(cam); |
479 | | // set base |
480 | 0 | if (light->getType() != Light::LT_POINT) |
481 | 0 | { |
482 | | #ifdef OGRE_NODELESS_POSITIONING |
483 | | texCam->getParentSceneNode()->setDirection(light->getDerivedDirection(), Node::TS_WORLD); |
484 | | #else |
485 | 0 | texCam->getParentSceneNode()->setOrientation(light->getParentNode()->_getDerivedOrientation()); |
486 | 0 | #endif |
487 | 0 | } |
488 | 0 | if (light->getType() != Light::LT_DIRECTIONAL) |
489 | 0 | texCam->getParentSceneNode()->setPosition(light->getDerivedPosition()); |
490 | | |
491 | | // update shadow cam - light mapping |
492 | 0 | ShadowCamLightMapping::iterator camLightIt = mShadowCamLightMapping.find(texCam); |
493 | 0 | assert(camLightIt != mShadowCamLightMapping.end()); |
494 | 0 | camLightIt->second = light; |
495 | |
|
496 | 0 | if (!light->getCustomShadowCameraSetup()) |
497 | 0 | mDefaultShadowCameraSetup->getShadowCamera(mSceneManager, cam, vp, light, texCam, j); |
498 | 0 | else |
499 | 0 | light->getCustomShadowCameraSetup()->getShadowCamera(mSceneManager, cam, vp, light, texCam, j); |
500 | | |
501 | | // Fire shadow caster update, callee can alter camera settings |
502 | 0 | fireShadowTexturesPreCaster(light, texCam, j); |
503 | 0 | } |
504 | | void SceneManager::TextureShadowRenderer::updateShadowTextures(Camera* cam, Viewport* vp, const LightList* lightList) |
505 | 0 | { |
506 | | // Determine far shadow distance |
507 | 0 | Real shadowDist = mDefaultShadowFarDist; |
508 | 0 | if (!shadowDist) |
509 | 0 | { |
510 | | // need a shadow distance, make one up |
511 | 0 | shadowDist = cam->getNearClipDistance() * 300; |
512 | 0 | } |
513 | 0 | Real shadowOffset = shadowDist * mShadowTextureOffset; |
514 | | // Precalculate fading info |
515 | 0 | Real shadowEnd = shadowDist + shadowOffset; |
516 | 0 | Real fadeStart = shadowEnd * mShadowTextureFadeStart; |
517 | 0 | Real fadeEnd = shadowEnd * mShadowTextureFadeEnd; |
518 | | // Additive lighting should not use fogging, since it will overbrighten; use border clamp |
519 | 0 | if (!mSceneManager->isShadowTechniqueAdditive()) |
520 | 0 | { |
521 | | // set fogging to hide the shadow edge |
522 | 0 | mShadowReceiverPass->setFog(true, FOG_LINEAR, ColourValue::White, 0, fadeStart, fadeEnd); |
523 | 0 | } |
524 | 0 | else |
525 | 0 | { |
526 | | // disable fogging explicitly |
527 | 0 | mShadowReceiverPass->setFog(true, FOG_NONE); |
528 | 0 | } |
529 | | |
530 | | // Iterate over the lights we've found, max out at the limit of light textures |
531 | | // Note that the light sorting must now place shadow casting lights at the |
532 | | // start of the light list, therefore we do not need to deal with potential |
533 | | // mismatches in the light<->shadow texture list any more |
534 | |
|
535 | 0 | LightList::const_iterator i, iend; |
536 | 0 | ShadowTextureList::iterator si, siend; |
537 | 0 | CameraList::iterator ci; |
538 | 0 | iend = lightList->end(); |
539 | 0 | siend = mShadowTextures.end(); |
540 | 0 | ci = mShadowTextureCameras.begin(); |
541 | 0 | mShadowTextureIndexLightList.clear(); |
542 | 0 | size_t shadowTextureIndex = 0; |
543 | |
|
544 | 0 | for (i = lightList->begin(), si = mShadowTextures.begin(); i != iend && si != siend; ++i) |
545 | 0 | { |
546 | 0 | Light* light = *i; |
547 | | |
548 | | // skip light if shadows are disabled |
549 | 0 | if (!light->getCastShadows()) |
550 | 0 | continue; |
551 | | |
552 | 0 | mDestRenderSystem->_setDepthClamp(light->getType() == Light::LT_DIRECTIONAL); |
553 | | |
554 | | // texture iteration per light. |
555 | 0 | size_t textureCountPerLight = mShadowTextureCountPerType[light->getType()]; |
556 | 0 | for (size_t j = 0; j < textureCountPerLight && si != siend; ++j) |
557 | 0 | { |
558 | 0 | TexturePtr &shadowTex = *si; |
559 | |
|
560 | 0 | size_t layer = j < shadowTex->getDepth() ? j : 0; |
561 | 0 | size_t face = j < shadowTex->getNumFaces() ? j : 0; |
562 | 0 | RenderTarget *shadowRTT = shadowTex->getBuffer(face)->getRenderTarget(layer); |
563 | 0 | Viewport *shadowView = shadowRTT->getViewport(0); |
564 | 0 | Camera *texCam = *ci; |
565 | | // rebind camera, incase another SM in use which has switched to its cam |
566 | 0 | shadowView->setCamera(texCam); |
567 | | |
568 | | // also update culling camera |
569 | 0 | auto cullCam = dynamic_cast<Camera*>(texCam->getCullingFrustum()); |
570 | 0 | cullCam->_notifyViewport(shadowView); |
571 | 0 | mCullCameraSetup->getShadowCamera(mSceneManager, cam, vp, light, cullCam, j); |
572 | | |
573 | | // Use the material scheme of the main viewport |
574 | | // This is required to pick up the correct shadow_caster_material and similar properties. |
575 | 0 | shadowView->setMaterialScheme(vp->getMaterialScheme()); |
576 | | |
577 | | // Set the viewport visibility flags |
578 | 0 | shadowView->setVisibilityMask(light->getLightMask() & vp->getVisibilityMask()); |
579 | |
|
580 | 0 | bool allLayers = shadowTex->getUsage() & TU_TARGET_ALL_LAYERS; |
581 | 0 | size_t layers = std::max(shadowTex->getDepth(), shadowTex->getNumFaces()); |
582 | |
|
583 | 0 | prepareTexCam(texCam, cam, vp, light, j); |
584 | 0 | if(allLayers && layers <= textureCountPerLight) |
585 | 0 | { |
586 | 0 | std::vector<const Camera*> cams = {texCam}; |
587 | 0 | j++; |
588 | 0 | for(; j < layers; ++j) |
589 | 0 | { |
590 | 0 | texCam = *(++ci); // next camera |
591 | 0 | texCam->_notifyViewport(shadowView); |
592 | 0 | prepareTexCam(texCam, cam, vp, light, j); |
593 | 0 | cams.push_back(texCam); |
594 | 0 | } |
595 | 0 | mSceneManager->setVPRTCameras(cams); |
596 | 0 | } |
597 | | |
598 | | // Update target |
599 | 0 | shadowRTT->update(); |
600 | |
|
601 | 0 | if((j + 1) >= layers) |
602 | 0 | ++si; // next shadow texture |
603 | 0 | ++ci; // next camera |
604 | 0 | } |
605 | |
|
606 | 0 | mDestRenderSystem->_setDepthClamp(false); |
607 | | |
608 | | // set the first shadow texture index for this light. |
609 | 0 | mShadowTextureIndexLightList.push_back(shadowTextureIndex); |
610 | 0 | shadowTextureIndex += textureCountPerLight; |
611 | 0 | } |
612 | |
|
613 | 0 | fireShadowTexturesUpdated(std::min(lightList->size(), mShadowTextures.size())); |
614 | |
|
615 | 0 | ShadowTextureManager::getSingleton().clearUnused(); |
616 | 0 | } |
617 | | |
618 | | |
619 | | void SceneManager::TextureShadowRenderer::setShadowTextureCasterMaterial(const MaterialPtr& mat) |
620 | 0 | { |
621 | 0 | if(!mat) { |
622 | 0 | mShadowTextureCustomCasterPass = 0; |
623 | 0 | return; |
624 | 0 | } |
625 | | |
626 | 0 | mat->load(); |
627 | 0 | if (!mat->getBestTechnique()) |
628 | 0 | { |
629 | | // unsupported |
630 | 0 | mShadowTextureCustomCasterPass = 0; |
631 | 0 | } |
632 | 0 | else |
633 | 0 | { |
634 | 0 | OgreAssert(!mat->getTechnique(0)->getPasses().empty(), "technique 0 has no passes"); |
635 | 0 | mShadowTextureCustomCasterPass = mat->getTechnique(0)->getPass(0); |
636 | 0 | } |
637 | 0 | } |
638 | | //--------------------------------------------------------------------- |
639 | | void SceneManager::TextureShadowRenderer::setShadowTextureReceiverMaterial(const MaterialPtr& mat) |
640 | 0 | { |
641 | 0 | if(!mat) { |
642 | 0 | mShadowTextureCustomReceiverPass = 0; |
643 | 0 | return; |
644 | 0 | } |
645 | | |
646 | 0 | mat->load(); |
647 | 0 | if (!mat->getBestTechnique()) |
648 | 0 | { |
649 | | // unsupported |
650 | 0 | mShadowTextureCustomReceiverPass = 0; |
651 | 0 | } |
652 | 0 | else |
653 | 0 | { |
654 | 0 | OgreAssert(!mat->getTechnique(0)->getPasses().empty(), "technique 0 has no passes"); |
655 | 0 | mShadowTextureCustomReceiverPass = mat->getTechnique(0)->getPass(0); |
656 | 0 | } |
657 | 0 | } |
658 | | //--------------------------------------------------------------------- |
659 | | void SceneManager::TextureShadowRenderer::setShadowTechnique(ShadowTechnique technique) |
660 | 0 | { |
661 | 0 | if (mSceneManager->getShadowTechnique() == SHADOWTYPE_TEXTURE_MODULATIVE && !mSpotFadeTexture) |
662 | 0 | mSpotFadeTexture = TextureManager::getSingleton().load("spot_shadow_fade.dds", RGN_INTERNAL); |
663 | |
|
664 | 0 | if (!mSceneManager->isShadowTechniqueTextureBased()) |
665 | 0 | { |
666 | | // Destroy shadow textures to optimise resource usage |
667 | 0 | destroyShadowTextures(); |
668 | 0 | mSpotFadeTexture.reset(); |
669 | 0 | } |
670 | 0 | else |
671 | 0 | { |
672 | | // assure no custom shadow matrix is used accidentally in case we switch |
673 | | // from a custom shadow mapping type to a non-custom (uniform shadow mapping) |
674 | 0 | for ( Camera* texCam : mShadowTextureCameras) |
675 | 0 | { |
676 | 0 | texCam->setCustomViewMatrix(false); |
677 | 0 | texCam->setCustomProjectionMatrix(false); |
678 | 0 | } |
679 | 0 | } |
680 | |
|
681 | 0 | if(!mSceneManager->getShadowTechnique()) |
682 | 0 | return; |
683 | | |
684 | | // init shadow caster material for texture shadows |
685 | 0 | if (!mShadowCasterPlainBlackPass) |
686 | 0 | { |
687 | 0 | MaterialPtr matPlainBlack = MaterialManager::getSingleton().getByName("Ogre/TextureShadowCaster"); |
688 | 0 | matPlainBlack->load(); |
689 | 0 | mShadowCasterPlainBlackPass = matPlainBlack->getTechnique(0)->getPass(0); |
690 | 0 | } |
691 | |
|
692 | 0 | if (!mShadowReceiverPass) |
693 | 0 | { |
694 | 0 | MaterialPtr matShadRec = MaterialManager::getSingleton().getByName("Ogre/TextureShadowReceiver", RGN_INTERNAL); |
695 | 0 | if (!matShadRec) |
696 | 0 | { |
697 | 0 | matShadRec = MaterialManager::getSingleton().create("Ogre/TextureShadowReceiver", RGN_INTERNAL); |
698 | 0 | mShadowReceiverPass = matShadRec->getTechnique(0)->getPass(0); |
699 | | // Don't set lighting and blending modes here, depends on additive / modulative |
700 | 0 | TextureUnitState* t = mShadowReceiverPass->createTextureUnitState(); |
701 | 0 | t->setProjectiveTexturing(true, NULL); // will be set later, but the RTSS needs to know about this |
702 | 0 | } |
703 | 0 | else |
704 | 0 | { |
705 | 0 | mShadowReceiverPass = matShadRec->getTechnique(0)->getPass(0); |
706 | 0 | } |
707 | 0 | } |
708 | 0 | } |
709 | | |
710 | | |
711 | | const Pass* SceneManager::TextureShadowRenderer::deriveShadowCasterPass(const Pass* pass) |
712 | 0 | { |
713 | 0 | Pass* retPass; |
714 | 0 | if (pass->getParent()->getShadowCasterMaterial()) |
715 | 0 | { |
716 | 0 | auto bestTech = pass->getParent()->getShadowCasterMaterial()->getBestTechnique(); |
717 | 0 | if (bestTech && bestTech->getNumPasses() > 0) { |
718 | 0 | return bestTech->getPass(0); |
719 | 0 | } |
720 | 0 | } |
721 | 0 | retPass = mShadowTextureCustomCasterPass ? |
722 | 0 | mShadowTextureCustomCasterPass : mShadowCasterPlainBlackPass; |
723 | | |
724 | | // Special case alpha-blended passes |
725 | 0 | if ((pass->getSourceBlendFactor() == SBF_SOURCE_ALPHA && |
726 | 0 | pass->getDestBlendFactor() == SBF_ONE_MINUS_SOURCE_ALPHA) |
727 | 0 | || pass->getAlphaRejectFunction() != CMPF_ALWAYS_PASS) |
728 | 0 | { |
729 | | // Alpha blended passes must retain their transparency |
730 | 0 | retPass->setAlphaRejectSettings(pass->getAlphaRejectFunction(), |
731 | 0 | pass->getAlphaRejectValue()); |
732 | 0 | retPass->setSceneBlending(pass->getSourceBlendFactor(), pass->getDestBlendFactor()); |
733 | 0 | retPass->getParent()->getParent()->setTransparencyCastsShadows(true); |
734 | | |
735 | | // So we allow the texture units, but override the colour functions |
736 | | // Copy texture state, shift up one since 0 is shadow texture |
737 | 0 | unsigned short origPassTUCount = pass->getNumTextureUnitStates(); |
738 | 0 | for (unsigned short t = 0; t < origPassTUCount; ++t) |
739 | 0 | { |
740 | 0 | TextureUnitState* tex; |
741 | 0 | if (retPass->getNumTextureUnitStates() <= t) |
742 | 0 | { |
743 | 0 | tex = retPass->createTextureUnitState(); |
744 | 0 | } |
745 | 0 | else |
746 | 0 | { |
747 | 0 | tex = retPass->getTextureUnitState(t); |
748 | 0 | } |
749 | | // copy base state |
750 | 0 | (*tex) = *(pass->getTextureUnitState(t)); |
751 | | // override colour function |
752 | 0 | tex->setColourOperationEx(LBX_SOURCE1, LBS_MANUAL, LBS_CURRENT, |
753 | 0 | mSceneManager->isShadowTechniqueAdditive() ? ColourValue::Black : mSceneManager->getShadowColour()); |
754 | 0 | } |
755 | | // Remove any extras |
756 | 0 | while (retPass->getNumTextureUnitStates() > origPassTUCount) |
757 | 0 | { |
758 | 0 | retPass->removeTextureUnitState(origPassTUCount); |
759 | 0 | } |
760 | |
|
761 | 0 | } |
762 | 0 | else |
763 | 0 | { |
764 | | // reset |
765 | 0 | retPass->setSceneBlending(SBT_REPLACE); |
766 | 0 | retPass->setAlphaRejectFunction(CMPF_ALWAYS_PASS); |
767 | 0 | while (retPass->getNumTextureUnitStates() > 0) |
768 | 0 | { |
769 | 0 | retPass->removeTextureUnitState(0); |
770 | 0 | } |
771 | 0 | } |
772 | | |
773 | | // give the RTSS a chance to generate a better technique |
774 | 0 | retPass->getParent()->getParent()->load(); |
775 | |
|
776 | 0 | Technique* btech = retPass->getParent()->getParent()->getBestTechnique(); |
777 | 0 | if( btech ) |
778 | 0 | { |
779 | 0 | retPass = btech->getPass(0); |
780 | 0 | } |
781 | | |
782 | | // Propagate culling modes |
783 | 0 | retPass->setCullingMode(pass->getCullingMode()); |
784 | 0 | retPass->setManualCullingMode(pass->getManualCullingMode()); |
785 | |
|
786 | 0 | return retPass; |
787 | 0 | } |
788 | | //--------------------------------------------------------------------- |
789 | | const Pass* SceneManager::TextureShadowRenderer::deriveShadowReceiverPass(const Pass* pass) |
790 | 0 | { |
791 | 0 | if (pass->getParent()->getShadowReceiverMaterial()) |
792 | 0 | { |
793 | 0 | return pass->getParent()->getShadowReceiverMaterial()->getBestTechnique()->getPass(0); |
794 | 0 | } |
795 | | |
796 | 0 | Pass* retPass = mShadowTextureCustomReceiverPass ? mShadowTextureCustomReceiverPass : mShadowReceiverPass; |
797 | |
|
798 | 0 | unsigned short keepTUCount; |
799 | | // If additive, need lighting parameters & standard programs |
800 | 0 | if (mSceneManager->isShadowTechniqueAdditive()) |
801 | 0 | { |
802 | 0 | retPass->setLightingEnabled(true); |
803 | 0 | retPass->setAmbient(pass->getAmbient()); |
804 | 0 | retPass->setSelfIllumination(pass->getSelfIllumination()); |
805 | 0 | retPass->setDiffuse(pass->getDiffuse()); |
806 | 0 | retPass->setSpecular(pass->getSpecular()); |
807 | 0 | retPass->setShininess(pass->getShininess()); |
808 | 0 | retPass->setLightMask(pass->getLightMask()); |
809 | | |
810 | | // We need to keep alpha rejection settings |
811 | 0 | retPass->setAlphaRejectSettings(pass->getAlphaRejectFunction(), |
812 | 0 | pass->getAlphaRejectValue()); |
813 | | // Copy texture state, shift up one since 0 is shadow texture |
814 | 0 | unsigned short origPassTUCount = pass->getNumTextureUnitStates(); |
815 | 0 | for (unsigned short t = 0; t < origPassTUCount; ++t) |
816 | 0 | { |
817 | 0 | unsigned short targetIndex = t+1; |
818 | 0 | TextureUnitState* tex; |
819 | 0 | if (retPass->getNumTextureUnitStates() <= targetIndex) |
820 | 0 | { |
821 | 0 | tex = retPass->createTextureUnitState(); |
822 | 0 | } |
823 | 0 | else |
824 | 0 | { |
825 | 0 | tex = retPass->getTextureUnitState(targetIndex); |
826 | 0 | } |
827 | 0 | (*tex) = *(pass->getTextureUnitState(t)); |
828 | | // If programmable, have to adjust the texcoord sets too |
829 | | // D3D insists that texcoordsets match tex unit in programmable mode |
830 | 0 | if (retPass->hasVertexProgram()) |
831 | 0 | tex->setTextureCoordSet(targetIndex); |
832 | 0 | } |
833 | 0 | keepTUCount = origPassTUCount + 1; |
834 | 0 | }// additive lighting |
835 | 0 | else |
836 | 0 | { |
837 | | // need to keep spotlight fade etc |
838 | 0 | keepTUCount = retPass->getNumTextureUnitStates(); |
839 | 0 | } |
840 | | |
841 | | // re-/set light iteration settings |
842 | 0 | retPass->setIteratePerLight(pass->getIteratePerLight(), pass->getRunOnlyForOneLightType(), |
843 | 0 | pass->getOnlyLightType()); |
844 | | |
845 | | // Remove any extra texture units |
846 | 0 | while (retPass->getNumTextureUnitStates() > keepTUCount) |
847 | 0 | { |
848 | 0 | retPass->removeTextureUnitState(keepTUCount); |
849 | 0 | } |
850 | | |
851 | | // give the RTSS a chance to generate a better technique |
852 | 0 | retPass->getParent()->getParent()->load(); |
853 | |
|
854 | 0 | Technique* btech = retPass->getParent()->getParent()->getBestTechnique(); |
855 | 0 | if( btech ) |
856 | 0 | { |
857 | 0 | retPass = btech->getPass(0); |
858 | 0 | } |
859 | |
|
860 | 0 | return retPass; |
861 | 0 | } |
862 | | |
863 | | const Pass* SceneManager::TextureShadowRenderer::deriveTextureShadowPass(const Pass* pass) |
864 | 0 | { |
865 | 0 | if (mSceneManager->_getCurrentRenderStage() == IRS_RENDER_TO_TEXTURE) |
866 | 0 | { |
867 | | // Derive a special shadow caster pass from this one |
868 | 0 | return deriveShadowCasterPass(pass); |
869 | 0 | } |
870 | | |
871 | 0 | if (mSceneManager->_getCurrentRenderStage() == IRS_RENDER_RECEIVER_PASS) |
872 | 0 | { |
873 | 0 | return deriveShadowReceiverPass(pass); |
874 | 0 | } |
875 | | |
876 | 0 | return pass; |
877 | 0 | } |
878 | | |
879 | | //--------------------------------------------------------------------- |
880 | | const VisibleObjectsBoundsInfo& |
881 | | SceneManager::TextureShadowRenderer::getShadowCasterBoundsInfo( const Light* light, size_t iteration ) const |
882 | 0 | { |
883 | 0 | static VisibleObjectsBoundsInfo nullBox; |
884 | | |
885 | | // find light |
886 | 0 | unsigned int foundCount = 0; |
887 | 0 | for (auto& m : mShadowCamLightMapping) |
888 | 0 | { |
889 | 0 | if (m.second == light ) |
890 | 0 | { |
891 | 0 | if (foundCount == iteration) |
892 | 0 | { |
893 | | // search the camera-aab list for the texture cam |
894 | 0 | auto camIt = mSceneManager->mCamVisibleObjectsMap.find(m.first); |
895 | |
|
896 | 0 | if (camIt == mSceneManager->mCamVisibleObjectsMap.end()) |
897 | 0 | { |
898 | 0 | return nullBox; |
899 | 0 | } |
900 | 0 | else |
901 | 0 | { |
902 | 0 | return camIt->second; |
903 | 0 | } |
904 | 0 | } |
905 | 0 | else |
906 | 0 | { |
907 | | // multiple shadow textures per light, keep searching |
908 | 0 | ++foundCount; |
909 | 0 | } |
910 | 0 | } |
911 | 0 | } |
912 | | |
913 | | // AAB not available |
914 | 0 | return nullBox; |
915 | 0 | } |
916 | | |
917 | | |
918 | | //--------------------------------------------------------------------- |
919 | | void SceneManager::TextureShadowRenderer::setShadowTextureConfig(size_t shadowIndex, unsigned short width, |
920 | | unsigned short height, PixelFormat format, unsigned short fsaa, uint16 depthBufferPoolId ) |
921 | 0 | { |
922 | 0 | ShadowTextureConfig conf; |
923 | 0 | conf.width = width; |
924 | 0 | conf.height = height; |
925 | 0 | conf.format = format; |
926 | 0 | conf.fsaa = fsaa; |
927 | 0 | conf.depthBufferPoolId = depthBufferPoolId; |
928 | |
|
929 | 0 | setShadowTextureConfig(shadowIndex, conf); |
930 | | |
931 | |
|
932 | 0 | } |
933 | | //--------------------------------------------------------------------- |
934 | | void SceneManager::TextureShadowRenderer::setShadowTextureConfig(size_t shadowIndex, |
935 | | const ShadowTextureConfig& config) |
936 | 0 | { |
937 | 0 | if (shadowIndex >= mShadowTextureConfigList.size()) |
938 | 0 | { |
939 | 0 | OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, |
940 | 0 | "shadowIndex out of bounds", |
941 | 0 | "SceneManager::setShadowTextureConfig"); |
942 | 0 | } |
943 | 0 | mShadowTextureConfigList[shadowIndex] = config; |
944 | |
|
945 | 0 | mShadowTextureConfigDirty = true; |
946 | 0 | } |
947 | | |
948 | | void SceneManager::TextureShadowRenderer::setShadowTextureCompositor(const String& compositorName, |
949 | | const String& resourceGroup) |
950 | 0 | { |
951 | 0 | auto compositor = CompositorManager::getSingleton().getByName(compositorName, resourceGroup); |
952 | 0 | OgreAssert(compositor, "Compositor not found"); |
953 | 0 | OgreAssert(compositor->getNumTechniques() > 0, "Compositor has no techniques"); |
954 | | |
955 | 0 | auto compositorTech = compositor->getTechnique(0); |
956 | 0 | const auto& textures = compositorTech->getTextureDefinitions(); |
957 | 0 | OgreAssert(!textures.empty(), "Compositor technique has no textures"); |
958 | | |
959 | 0 | mShadowTextureConfigList.clear(); |
960 | 0 | mShadowTextureConfigDirty = true; |
961 | |
|
962 | 0 | for(const auto& texture : textures) |
963 | 0 | { |
964 | 0 | OgreAssert(texture->width && texture->height, "Compositor texture definition must have absolute size"); |
965 | 0 | OgreAssert(texture->formatList.size() == 1, "Compositor texture definition must have exactly one format"); |
966 | | |
967 | 0 | ShadowTextureConfig cfg; |
968 | 0 | cfg.width = texture->width; |
969 | 0 | cfg.height = texture->height; |
970 | 0 | cfg.format = texture->formatList.front(); |
971 | 0 | cfg.fsaa = texture->fsaa > 1 ? texture->fsaa : 0; |
972 | 0 | cfg.depthBufferPoolId = texture->depthBufferId; |
973 | 0 | cfg.type = texture->type; |
974 | 0 | cfg.depth = texture->depth; |
975 | |
|
976 | 0 | mShadowTextureConfigList.push_back(cfg); |
977 | 0 | } |
978 | 0 | } |
979 | | |
980 | | //--------------------------------------------------------------------- |
981 | | void SceneManager::TextureShadowRenderer::setShadowTextureSize(unsigned short size) |
982 | 0 | { |
983 | | // default all current |
984 | 0 | for (auto & i : mShadowTextureConfigList) |
985 | 0 | { |
986 | 0 | if (i.width != size || i.height != size) |
987 | 0 | { |
988 | 0 | i.width = i.height = size; |
989 | 0 | mShadowTextureConfigDirty = true; |
990 | 0 | } |
991 | 0 | } |
992 | |
|
993 | 0 | } |
994 | | //--------------------------------------------------------------------- |
995 | | void SceneManager::TextureShadowRenderer::setShadowTextureCount(size_t count) |
996 | 0 | { |
997 | | // Change size, any new items will need defaults |
998 | 0 | if (count != mShadowTextureConfigList.size()) |
999 | 0 | { |
1000 | | // if no entries yet, use the defaults |
1001 | 0 | if (mShadowTextureConfigList.empty()) |
1002 | 0 | { |
1003 | 0 | mShadowTextureConfigList.resize(count); |
1004 | 0 | } |
1005 | 0 | else |
1006 | 0 | { |
1007 | | // create new instances with the same settings as the last item in the list |
1008 | 0 | mShadowTextureConfigList.resize(count, *mShadowTextureConfigList.rbegin()); |
1009 | 0 | } |
1010 | 0 | mShadowTextureConfigDirty = true; |
1011 | 0 | } |
1012 | 0 | } |
1013 | | //--------------------------------------------------------------------- |
1014 | | void SceneManager::TextureShadowRenderer::setShadowTexturePixelFormat(PixelFormat fmt) |
1015 | 0 | { |
1016 | 0 | for (auto & i : mShadowTextureConfigList) |
1017 | 0 | { |
1018 | 0 | if (i.format != fmt) |
1019 | 0 | { |
1020 | 0 | i.format = fmt; |
1021 | 0 | mShadowTextureConfigDirty = true; |
1022 | 0 | } |
1023 | 0 | } |
1024 | 0 | } |
1025 | | void SceneManager::TextureShadowRenderer::setShadowTextureFSAA(unsigned short fsaa) |
1026 | 0 | { |
1027 | 0 | for (auto & i : mShadowTextureConfigList) |
1028 | 0 | { |
1029 | 0 | if (i.fsaa != fsaa) |
1030 | 0 | { |
1031 | 0 | i.fsaa = fsaa; |
1032 | 0 | mShadowTextureConfigDirty = true; |
1033 | 0 | } |
1034 | 0 | } |
1035 | 0 | } |
1036 | | //--------------------------------------------------------------------- |
1037 | | void SceneManager::TextureShadowRenderer::setShadowTextureSettings(unsigned short size, |
1038 | | unsigned short count, PixelFormat fmt, unsigned short fsaa, uint16 depthBufferPoolId) |
1039 | 0 | { |
1040 | 0 | setShadowTextureCount(count); |
1041 | 0 | for (auto & i : mShadowTextureConfigList) |
1042 | 0 | { |
1043 | 0 | if (i.width != size || i.height != size || i.format != fmt || i.fsaa != fsaa) |
1044 | 0 | { |
1045 | 0 | i.width = i.height = size; |
1046 | 0 | i.format = fmt; |
1047 | 0 | i.fsaa = fsaa; |
1048 | 0 | i.depthBufferPoolId = depthBufferPoolId; |
1049 | 0 | mShadowTextureConfigDirty = true; |
1050 | 0 | } |
1051 | 0 | } |
1052 | 0 | } |
1053 | | //--------------------------------------------------------------------- |
1054 | | const TexturePtr& SceneManager::TextureShadowRenderer::getShadowTexture(size_t shadowIndex) |
1055 | 0 | { |
1056 | 0 | mSceneManager->ensureShadowTexturesCreated(); |
1057 | |
|
1058 | 0 | OgreAssert(shadowIndex < mShadowTextures.size(), ""); |
1059 | 0 | return mShadowTextures[shadowIndex]; |
1060 | 0 | } |
1061 | | |
1062 | | void SceneManager::TextureShadowRenderer::resolveShadowTexture(TextureUnitState* tu, size_t shadowIndex, size_t shadowTexUnitIndex) const |
1063 | 0 | { |
1064 | 0 | TexturePtr shadowTex; |
1065 | 0 | if (shadowIndex < mShadowTextures.size()) |
1066 | 0 | { |
1067 | 0 | shadowTex = mShadowTextures[shadowIndex]; |
1068 | | // Enable projective texturing if fixed-function, but also need to |
1069 | | // disable it explicitly for program pipeline. |
1070 | 0 | tu->setProjectiveTexturing(!tu->getParent()->hasVertexProgram(), mShadowTextureCameras[shadowIndex]); |
1071 | 0 | } |
1072 | 0 | else |
1073 | 0 | { |
1074 | | // Use fallback no-shadow texture |
1075 | | // no projection since all uniform colour anyway |
1076 | 0 | shadowTex = mNoShadowTexture; |
1077 | 0 | tu->setProjectiveTexturing(false); |
1078 | 0 | } |
1079 | |
|
1080 | 0 | tu->_setTexturePtr(shadowTex); |
1081 | | |
1082 | | // set up projectors for all layers, fixed function does not support layered textures anyway |
1083 | 0 | size_t layers = std::max(shadowTex->getDepth(), shadowTex->getNumFaces()); |
1084 | 0 | layers = std::min(layers, mShadowTextureCameras.size() - shadowIndex); // clamp |
1085 | 0 | for (size_t l = 0; l < layers; ++l) |
1086 | 0 | mSceneManager->mAutoParamDataSource->setTextureProjector(mShadowTextureCameras[shadowIndex + l], |
1087 | 0 | shadowTexUnitIndex + l); |
1088 | 0 | } |
1089 | | |
1090 | | namespace |
1091 | | { |
1092 | | /** Default sorting routine which sorts lights which cast shadows |
1093 | | to the front of a list, sub-sorting by distance. |
1094 | | |
1095 | | Since shadow textures are generated from lights based on the |
1096 | | frustum rather than individual objects, a shadow and camera-wise sort is |
1097 | | required to pick the best lights near the start of the list. Up to |
1098 | | the number of shadow textures will be generated from this. |
1099 | | */ |
1100 | | struct lightsForShadowTextureLess |
1101 | | { |
1102 | | bool operator()(const Light* l1, const Light* l2) const |
1103 | 0 | { |
1104 | 0 | if (l1 == l2) |
1105 | 0 | return false; |
1106 | | |
1107 | | // sort shadow casting lights ahead of non-shadow casting |
1108 | 0 | if (l1->getCastShadows() != l2->getCastShadows()) |
1109 | 0 | { |
1110 | 0 | return l1->getCastShadows(); |
1111 | 0 | } |
1112 | | |
1113 | | // otherwise sort by distance (directional lights will have 0 here) |
1114 | 0 | return l1->tempSquareDist < l2->tempSquareDist; |
1115 | 0 | } |
1116 | | }; |
1117 | | } // namespace |
1118 | | |
1119 | | void SceneManager::TextureShadowRenderer::sortLightsAffectingFrustum(LightList& lightList) const |
1120 | 0 | { |
1121 | | // Sort the lights if using texture shadows, since the first 'n' will be |
1122 | | // used to generate shadow textures and we should pick the most appropriate |
1123 | |
|
1124 | 0 | ListenerList listenersCopy = mListeners; // copy in case of listeners removing themselves |
1125 | | |
1126 | | // Allow a Listener to override light sorting |
1127 | | // Reverse iterate so last takes precedence |
1128 | 0 | for (auto ri = listenersCopy.rbegin(); ri != listenersCopy.rend(); ++ri) |
1129 | 0 | { |
1130 | 0 | if((*ri)->sortLightsAffectingFrustum(lightList)) |
1131 | 0 | return; |
1132 | 0 | } |
1133 | | |
1134 | | // default sort (stable to preserve directional light ordering |
1135 | 0 | std::stable_sort(lightList.begin(), lightList.end(), lightsForShadowTextureLess()); |
1136 | 0 | } |
1137 | | //--------------------------------------------------------------------- |
1138 | | void SceneManager::TextureShadowRenderer::fireShadowTexturesUpdated(size_t numberOfShadowTextures) |
1139 | 0 | { |
1140 | 0 | ListenerList listenersCopy = mListeners; |
1141 | |
|
1142 | 0 | for (auto *l : listenersCopy) |
1143 | 0 | { |
1144 | 0 | l->shadowTexturesUpdated(numberOfShadowTextures); |
1145 | 0 | } |
1146 | 0 | } |
1147 | | //--------------------------------------------------------------------- |
1148 | | void SceneManager::TextureShadowRenderer::fireShadowTexturesPreCaster(Light* light, Camera* camera, size_t iteration) |
1149 | 0 | { |
1150 | 0 | auto listenersCopy = mListeners; |
1151 | 0 | for (auto l : listenersCopy) |
1152 | 0 | { |
1153 | 0 | l->shadowTextureCasterPreViewProj(light, camera, iteration); |
1154 | 0 | } |
1155 | 0 | } |
1156 | | //--------------------------------------------------------------------- |
1157 | | void SceneManager::TextureShadowRenderer::fireShadowTexturesPreReceiver(Light* light, Frustum* f) |
1158 | 0 | { |
1159 | 0 | ListenerList listenersCopy = mListeners; |
1160 | 0 | for (auto *l : listenersCopy) |
1161 | 0 | { |
1162 | 0 | l->shadowTextureReceiverPreViewProj(light, f); |
1163 | 0 | } |
1164 | 0 | } |
1165 | | } |