1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
3 | #include "BsRendererScene.h" |
4 | #include "Renderer/BsCamera.h" |
5 | #include "Renderer/BsLight.h" |
6 | #include "Renderer/BsSkybox.h" |
7 | #include "Renderer/BsReflectionProbe.h" |
8 | #include "Renderer/BsRenderer.h" |
9 | #include "Particles/BsParticleManager.h" |
10 | #include "Mesh/BsMesh.h" |
11 | #include "Material/BsPass.h" |
12 | #include "Material/BsGpuParamsSet.h" |
13 | #include "Utility/BsSamplerOverrides.h" |
14 | #include "BsRenderBeastOptions.h" |
15 | #include "BsRenderBeast.h" |
16 | #include "BsRendererDecal.h" |
17 | #include "Image/BsSpriteTexture.h" |
18 | #include "Shading/BsGpuParticleSimulation.h" |
19 | #include "Renderer/BsDecal.h" |
20 | #include "Renderer/BsRendererUtility.h" |
21 | |
22 | namespace bs { namespace ct |
23 | { |
24 | PerFrameParamDef gPerFrameParamDef; |
25 | |
26 | static const ShaderVariation* DECAL_VAR_LOOKUP[2][3] = |
27 | { |
28 | { |
29 | &getDecalShaderVariation<false, MSAAMode::None>(), |
30 | &getDecalShaderVariation<false, MSAAMode::Single>(), |
31 | &getDecalShaderVariation<false, MSAAMode::Full>() |
32 | }, |
33 | { |
34 | &getDecalShaderVariation<true, MSAAMode::None>(), |
35 | &getDecalShaderVariation<true, MSAAMode::Single>(), |
36 | &getDecalShaderVariation<true, MSAAMode::Full>() |
37 | } |
38 | }; |
39 | |
40 | RendererScene::RendererScene(const SPtr<RenderBeastOptions>& options) |
41 | :mOptions(options) |
42 | { |
43 | mPerFrameParamBuffer = gPerFrameParamDef.createBuffer(); |
44 | } |
45 | |
46 | RendererScene::~RendererScene() |
47 | { |
48 | for (auto& entry : mInfo.renderables) |
49 | bs_delete(entry); |
50 | |
51 | for (auto& entry : mInfo.views) |
52 | bs_delete(entry); |
53 | |
54 | assert(mSamplerOverrides.empty()); |
55 | } |
56 | |
57 | void RendererScene::registerCamera(Camera* camera) |
58 | { |
59 | RENDERER_VIEW_DESC viewDesc = createViewDesc(camera); |
60 | |
61 | RendererView* view = bs_new<RendererView>(viewDesc); |
62 | view->setRenderSettings(camera->getRenderSettings()); |
63 | view->updatePerViewBuffer(); |
64 | |
65 | UINT32 viewIdx = (UINT32)mInfo.views.size(); |
66 | mInfo.views.push_back(view); |
67 | |
68 | mInfo.cameraToView[camera] = viewIdx; |
69 | camera->setRendererId(viewIdx); |
70 | |
71 | updateCameraRenderTargets(camera); |
72 | } |
73 | |
74 | void RendererScene::updateCamera(Camera* camera, UINT32 updateFlag) |
75 | { |
76 | UINT32 cameraId = camera->getRendererId(); |
77 | RendererView* view = mInfo.views[cameraId]; |
78 | |
79 | UINT32 updateEverythingFlag = (UINT32)ActorDirtyFlag::Everything |
80 | | (UINT32)ActorDirtyFlag::Active |
81 | | (UINT32)CameraDirtyFlag::Viewport; |
82 | |
83 | if((updateFlag & updateEverythingFlag) != 0) |
84 | { |
85 | RENDERER_VIEW_DESC viewDesc = createViewDesc(camera); |
86 | |
87 | view->setView(viewDesc); |
88 | view->setRenderSettings(camera->getRenderSettings()); |
89 | |
90 | updateCameraRenderTargets(camera); |
91 | } |
92 | else if((updateFlag & (UINT32)CameraDirtyFlag::RenderSettings) != 0) |
93 | { |
94 | view->setRenderSettings(camera->getRenderSettings()); |
95 | } |
96 | else // Transform |
97 | { |
98 | view = mInfo.views[cameraId]; |
99 | |
100 | const Transform& tfrm = camera->getTransform(); |
101 | view->setTransform( |
102 | tfrm.getPosition(), |
103 | tfrm.getForward(), |
104 | camera->getViewMatrix(), |
105 | camera->getProjectionMatrixRS(), |
106 | camera->getWorldFrustum()); |
107 | } |
108 | |
109 | view->updatePerViewBuffer(); |
110 | } |
111 | |
112 | void RendererScene::unregisterCamera(Camera* camera) |
113 | { |
114 | UINT32 cameraId = camera->getRendererId(); |
115 | |
116 | Camera* lastCamera = mInfo.views.back()->getSceneCamera(); |
117 | UINT32 lastCameraId = lastCamera->getRendererId(); |
118 | |
119 | if (cameraId != lastCameraId) |
120 | { |
121 | // Swap current last element with the one we want to erase |
122 | std::swap(mInfo.views[cameraId], mInfo.views[lastCameraId]); |
123 | lastCamera->setRendererId(cameraId); |
124 | |
125 | mInfo.cameraToView[lastCamera] = cameraId; |
126 | } |
127 | |
128 | // Last element is the one we want to erase |
129 | RendererView* view = mInfo.views[mInfo.views.size() - 1]; |
130 | bs_delete(view); |
131 | |
132 | mInfo.views.erase(mInfo.views.end() - 1); |
133 | |
134 | auto iterFind = mInfo.cameraToView.find(camera); |
135 | if(iterFind != mInfo.cameraToView.end()) |
136 | mInfo.cameraToView.erase(iterFind); |
137 | |
138 | updateCameraRenderTargets(camera, true); |
139 | } |
140 | |
141 | void RendererScene::registerLight(Light* light) |
142 | { |
143 | if (light->getType() == LightType::Directional) |
144 | { |
145 | UINT32 lightId = (UINT32)mInfo.directionalLights.size(); |
146 | light->setRendererId(lightId); |
147 | |
148 | mInfo.directionalLights.push_back(RendererLight(light)); |
149 | } |
150 | else |
151 | { |
152 | if (light->getType() == LightType::Radial) |
153 | { |
154 | UINT32 lightId = (UINT32)mInfo.radialLights.size(); |
155 | light->setRendererId(lightId); |
156 | |
157 | mInfo.radialLights.push_back(RendererLight(light)); |
158 | mInfo.radialLightWorldBounds.push_back(light->getBounds()); |
159 | } |
160 | else // Spot |
161 | { |
162 | UINT32 lightId = (UINT32)mInfo.spotLights.size(); |
163 | light->setRendererId(lightId); |
164 | |
165 | mInfo.spotLights.push_back(RendererLight(light)); |
166 | mInfo.spotLightWorldBounds.push_back(light->getBounds()); |
167 | } |
168 | } |
169 | } |
170 | |
171 | void RendererScene::updateLight(Light* light) |
172 | { |
173 | UINT32 lightId = light->getRendererId(); |
174 | |
175 | if (light->getType() == LightType::Radial) |
176 | mInfo.radialLightWorldBounds[lightId] = light->getBounds(); |
177 | else if(light->getType() == LightType::Spot) |
178 | mInfo.spotLightWorldBounds[lightId] = light->getBounds(); |
179 | } |
180 | |
181 | void RendererScene::unregisterLight(Light* light) |
182 | { |
183 | UINT32 lightId = light->getRendererId(); |
184 | if (light->getType() == LightType::Directional) |
185 | { |
186 | Light* lastLight = mInfo.directionalLights.back().internal; |
187 | UINT32 lastLightId = lastLight->getRendererId(); |
188 | |
189 | if (lightId != lastLightId) |
190 | { |
191 | // Swap current last element with the one we want to erase |
192 | std::swap(mInfo.directionalLights[lightId], mInfo.directionalLights[lastLightId]); |
193 | lastLight->setRendererId(lightId); |
194 | } |
195 | |
196 | // Last element is the one we want to erase |
197 | mInfo.directionalLights.erase(mInfo.directionalLights.end() - 1); |
198 | } |
199 | else |
200 | { |
201 | if (light->getType() == LightType::Radial) |
202 | { |
203 | Light* lastLight = mInfo.radialLights.back().internal; |
204 | UINT32 lastLightId = lastLight->getRendererId(); |
205 | |
206 | if (lightId != lastLightId) |
207 | { |
208 | // Swap current last element with the one we want to erase |
209 | std::swap(mInfo.radialLights[lightId], mInfo.radialLights[lastLightId]); |
210 | std::swap(mInfo.radialLightWorldBounds[lightId], mInfo.radialLightWorldBounds[lastLightId]); |
211 | |
212 | lastLight->setRendererId(lightId); |
213 | } |
214 | |
215 | // Last element is the one we want to erase |
216 | mInfo.radialLights.erase(mInfo.radialLights.end() - 1); |
217 | mInfo.radialLightWorldBounds.erase(mInfo.radialLightWorldBounds.end() - 1); |
218 | } |
219 | else // Spot |
220 | { |
221 | Light* lastLight = mInfo.spotLights.back().internal; |
222 | UINT32 lastLightId = lastLight->getRendererId(); |
223 | |
224 | if (lightId != lastLightId) |
225 | { |
226 | // Swap current last element with the one we want to erase |
227 | std::swap(mInfo.spotLights[lightId], mInfo.spotLights[lastLightId]); |
228 | std::swap(mInfo.spotLightWorldBounds[lightId], mInfo.spotLightWorldBounds[lastLightId]); |
229 | |
230 | lastLight->setRendererId(lightId); |
231 | } |
232 | |
233 | // Last element is the one we want to erase |
234 | mInfo.spotLights.erase(mInfo.spotLights.end() - 1); |
235 | mInfo.spotLightWorldBounds.erase(mInfo.spotLightWorldBounds.end() - 1); |
236 | } |
237 | } |
238 | } |
239 | |
240 | void RendererScene::registerRenderable(Renderable* renderable) |
241 | { |
242 | UINT32 renderableId = (UINT32)mInfo.renderables.size(); |
243 | |
244 | renderable->setRendererId(renderableId); |
245 | |
246 | mInfo.renderables.push_back(bs_new<RendererRenderable>()); |
247 | mInfo.renderableCullInfos.push_back(CullInfo(renderable->getBounds(), renderable->getLayer(), renderable->getCullDistanceFactor())); |
248 | |
249 | RendererRenderable* rendererRenderable = mInfo.renderables.back(); |
250 | rendererRenderable->renderable = renderable; |
251 | rendererRenderable->updatePerObjectBuffer(); |
252 | |
253 | SPtr<Mesh> mesh = renderable->getMesh(); |
254 | if (mesh != nullptr) |
255 | { |
256 | const MeshProperties& meshProps = mesh->getProperties(); |
257 | SPtr<VertexDeclaration> vertexDecl = mesh->getVertexData()->vertexDeclaration; |
258 | |
259 | for (UINT32 i = 0; i < meshProps.getNumSubMeshes(); i++) |
260 | { |
261 | rendererRenderable->elements.push_back(RenderableElement()); |
262 | RenderableElement& renElement = rendererRenderable->elements.back(); |
263 | |
264 | renElement.type = (UINT32)RenderElementType::Renderable; |
265 | renElement.mesh = mesh; |
266 | renElement.subMesh = meshProps.getSubMesh(i); |
267 | renElement.animType = renderable->getAnimType(); |
268 | renElement.animationId = renderable->getAnimationId(); |
269 | renElement.morphShapeVersion = 0; |
270 | renElement.morphShapeBuffer = renderable->getMorphShapeBuffer(); |
271 | renElement.boneMatrixBuffer = renderable->getBoneMatrixBuffer(); |
272 | renElement.morphVertexDeclaration = renderable->getMorphVertexDeclaration(); |
273 | |
274 | renElement.material = renderable->getMaterial(i); |
275 | if (renElement.material == nullptr) |
276 | renElement.material = renderable->getMaterial(0); |
277 | |
278 | if (renElement.material != nullptr && renElement.material->getShader() == nullptr) |
279 | renElement.material = nullptr; |
280 | |
281 | // If no material use the default material |
282 | if (renElement.material == nullptr) |
283 | renElement.material = Material::create(DefaultMaterial::get()->getShader()); |
284 | |
285 | // Determine which technique to use |
286 | static_assert((UINT32)RenderableAnimType::Count == 4, "RenderableAnimType is expected to have four sequential entries." ); |
287 | |
288 | ShaderFlags shaderFlags = renElement.material->getShader()->getFlags(); |
289 | const bool useForwardRendering = shaderFlags.isSet(ShaderFlag::Forward) || shaderFlags.isSet(ShaderFlag::Transparent); |
290 | |
291 | RenderableAnimType animType = renderable->getAnimType(); |
292 | |
293 | static const ShaderVariation* VAR_LOOKUP[4]; |
294 | if(useForwardRendering) |
295 | { |
296 | bool supportsClusteredForward = gRenderBeast()->getFeatureSet() == RenderBeastFeatureSet::Desktop; |
297 | |
298 | if(supportsClusteredForward) |
299 | { |
300 | VAR_LOOKUP[0] = &getForwardRenderingVariation<false, false, true>(); |
301 | VAR_LOOKUP[1] = &getForwardRenderingVariation<true, false, true>(); |
302 | VAR_LOOKUP[2] = &getForwardRenderingVariation<false, true, true>(); |
303 | VAR_LOOKUP[3] = &getForwardRenderingVariation<true, true, true>(); |
304 | } |
305 | else |
306 | { |
307 | VAR_LOOKUP[0] = &getForwardRenderingVariation<false, false, false>(); |
308 | VAR_LOOKUP[1] = &getForwardRenderingVariation<true, false, false>(); |
309 | VAR_LOOKUP[2] = &getForwardRenderingVariation<false, true, false>(); |
310 | VAR_LOOKUP[3] = &getForwardRenderingVariation<true, true, false>(); |
311 | } |
312 | } |
313 | else |
314 | { |
315 | VAR_LOOKUP[0] = &getVertexInputVariation<false, false>(); |
316 | VAR_LOOKUP[1] = &getVertexInputVariation<true, false>(); |
317 | VAR_LOOKUP[2] = &getVertexInputVariation<false, true>(); |
318 | VAR_LOOKUP[3] = &getVertexInputVariation<true, true>(); |
319 | } |
320 | |
321 | const ShaderVariation* variation = VAR_LOOKUP[(int)animType]; |
322 | |
323 | FIND_TECHNIQUE_DESC findDesc; |
324 | findDesc.variation = variation; |
325 | findDesc.override = true; |
326 | |
327 | UINT32 techniqueIdx = renElement.material->findTechnique(findDesc); |
328 | |
329 | if (techniqueIdx == (UINT32)-1) |
330 | techniqueIdx = renElement.material->getDefaultTechnique(); |
331 | |
332 | renElement.techniqueIdx = techniqueIdx; |
333 | |
334 | // Make sure the technique shaders are compiled |
335 | const SPtr<Technique>& technique = renElement.material->getTechnique(techniqueIdx); |
336 | if(technique) |
337 | technique->compile(); |
338 | |
339 | #if BS_DEBUG_MODE |
340 | // Validate mesh <-> shader vertex bindings |
341 | if (renElement.material != nullptr) |
342 | { |
343 | UINT32 numPasses = renElement.material->getNumPasses(techniqueIdx); |
344 | for (UINT32 j = 0; j < numPasses; j++) |
345 | { |
346 | SPtr<Pass> pass = renElement.material->getPass(j, techniqueIdx); |
347 | SPtr<GraphicsPipelineState> graphicsPipeline = pass->getGraphicsPipelineState(); |
348 | |
349 | SPtr<VertexDeclaration> shaderDecl = graphicsPipeline->getVertexProgram()->getInputDeclaration(); |
350 | if (shaderDecl && !vertexDecl->isCompatible(shaderDecl)) |
351 | { |
352 | Vector<VertexElement> missingElements = vertexDecl->getMissingElements(shaderDecl); |
353 | |
354 | // If using morph shapes ignore POSITION1 and NORMAL1 missing since we assign them from within the renderer |
355 | if (animType == RenderableAnimType::Morph || animType == RenderableAnimType::SkinnedMorph) |
356 | { |
357 | auto removeIter = std::remove_if(missingElements.begin(), missingElements.end(), [](const VertexElement& x) |
358 | { |
359 | return (x.getSemantic() == VES_POSITION && x.getSemanticIdx() == 1) || |
360 | (x.getSemantic() == VES_NORMAL && x.getSemanticIdx() == 1); |
361 | }); |
362 | |
363 | missingElements.erase(removeIter, missingElements.end()); |
364 | } |
365 | |
366 | if (!missingElements.empty()) |
367 | { |
368 | StringStream wrnStream; |
369 | wrnStream << "Provided mesh is missing required vertex attributes to render with the \ |
370 | provided shader. Missing elements: " << std::endl; |
371 | |
372 | for (auto& entry : missingElements) |
373 | wrnStream << "\t" << toString(entry.getSemantic()) << entry.getSemanticIdx() << std::endl; |
374 | |
375 | LOGWRN(wrnStream.str()); |
376 | break; |
377 | } |
378 | } |
379 | } |
380 | } |
381 | #endif |
382 | |
383 | // Generate or assigned renderer specific data for the material |
384 | renElement.params = renElement.material->createParamsSet(techniqueIdx); |
385 | renElement.material->updateParamsSet(renElement.params, 0.0f, true); |
386 | |
387 | // Generate or assign sampler state overrides |
388 | renElement.samplerOverrides = allocSamplerStateOverrides(renElement); |
389 | } |
390 | } |
391 | |
392 | // Prepare all parameter bindings |
393 | for(auto& element : rendererRenderable->elements) |
394 | { |
395 | SPtr<Shader> shader = element.material->getShader(); |
396 | if (shader == nullptr) |
397 | { |
398 | LOGWRN("Missing shader on material." ); |
399 | continue; |
400 | } |
401 | |
402 | SPtr<GpuParams> gpuParams = element.params->getGpuParams(); |
403 | |
404 | // Note: Perhaps perform buffer validation to ensure expected buffer has the same size and layout as the |
405 | // provided buffer, and show a warning otherwise. But this is perhaps better handled on a higher level. |
406 | gpuParams->setParamBlockBuffer("PerFrame" , mPerFrameParamBuffer); |
407 | gpuParams->setParamBlockBuffer("PerObject" , rendererRenderable->perObjectParamBuffer); |
408 | gpuParams->setParamBlockBuffer("PerCall" , rendererRenderable->perCallParamBuffer); |
409 | |
410 | gpuParams->getParamInfo()->getBindings( |
411 | GpuPipelineParamInfoBase::ParamType::ParamBlock, |
412 | "PerCamera" , |
413 | element.perCameraBindings |
414 | ); |
415 | |
416 | if (gpuParams->hasBuffer(GPT_VERTEX_PROGRAM, "boneMatrices" )) |
417 | gpuParams->setBuffer(GPT_VERTEX_PROGRAM, "boneMatrices" , element.boneMatrixBuffer); |
418 | |
419 | ShaderFlags shaderFlags = shader->getFlags(); |
420 | const bool useForwardRendering = shaderFlags.isSet(ShaderFlag::Forward) || shaderFlags.isSet(ShaderFlag::Transparent); |
421 | |
422 | if (useForwardRendering) |
423 | { |
424 | const bool supportsClusteredForward = gRenderBeast()->getFeatureSet() == RenderBeastFeatureSet::Desktop; |
425 | |
426 | element.forwardLightingParams.populate(gpuParams, supportsClusteredForward); |
427 | element.imageBasedParams.populate(gpuParams, GPT_FRAGMENT_PROGRAM, true, supportsClusteredForward, |
428 | supportsClusteredForward); |
429 | } |
430 | } |
431 | } |
432 | |
433 | void RendererScene::updateRenderable(Renderable* renderable) |
434 | { |
435 | UINT32 renderableId = renderable->getRendererId(); |
436 | |
437 | mInfo.renderables[renderableId]->updatePerObjectBuffer(); |
438 | mInfo.renderableCullInfos[renderableId].bounds = renderable->getBounds(); |
439 | mInfo.renderableCullInfos[renderableId].cullDistanceFactor = renderable->getCullDistanceFactor(); |
440 | } |
441 | |
442 | void RendererScene::unregisterRenderable(Renderable* renderable) |
443 | { |
444 | UINT32 renderableId = renderable->getRendererId(); |
445 | Renderable* lastRenerable = mInfo.renderables.back()->renderable; |
446 | UINT32 lastRenderableId = lastRenerable->getRendererId(); |
447 | |
448 | RendererRenderable* rendererRenderable = mInfo.renderables[renderableId]; |
449 | Vector<RenderableElement>& elements = rendererRenderable->elements; |
450 | for (auto& element : elements) |
451 | { |
452 | freeSamplerStateOverrides(element); |
453 | element.samplerOverrides = nullptr; |
454 | } |
455 | |
456 | if (renderableId != lastRenderableId) |
457 | { |
458 | // Swap current last element with the one we want to erase |
459 | std::swap(mInfo.renderables[renderableId], mInfo.renderables[lastRenderableId]); |
460 | std::swap(mInfo.renderableCullInfos[renderableId], mInfo.renderableCullInfos[lastRenderableId]); |
461 | |
462 | lastRenerable->setRendererId(renderableId); |
463 | } |
464 | |
465 | // Last element is the one we want to erase |
466 | mInfo.renderables.erase(mInfo.renderables.end() - 1); |
467 | mInfo.renderableCullInfos.erase(mInfo.renderableCullInfos.end() - 1); |
468 | |
469 | bs_delete(rendererRenderable); |
470 | } |
471 | |
472 | void RendererScene::registerReflectionProbe(ReflectionProbe* probe) |
473 | { |
474 | UINT32 probeId = (UINT32)mInfo.reflProbes.size(); |
475 | probe->setRendererId(probeId); |
476 | |
477 | mInfo.reflProbes.push_back(RendererReflectionProbe(probe)); |
478 | RendererReflectionProbe& probeInfo = mInfo.reflProbes.back(); |
479 | |
480 | mInfo.reflProbeWorldBounds.push_back(probe->getBounds()); |
481 | |
482 | // Find a spot in cubemap array |
483 | UINT32 numArrayEntries = (UINT32)mInfo.reflProbeCubemapArrayUsedSlots.size(); |
484 | for(UINT32 i = 0; i < numArrayEntries; i++) |
485 | { |
486 | if(!mInfo.reflProbeCubemapArrayUsedSlots[i]) |
487 | { |
488 | setReflectionProbeArrayIndex(probeId, i, false); |
489 | mInfo.reflProbeCubemapArrayUsedSlots[i] = true; |
490 | break; |
491 | } |
492 | } |
493 | |
494 | // No empty slot was found |
495 | if (probeInfo.arrayIdx == (UINT32)-1) |
496 | { |
497 | setReflectionProbeArrayIndex(probeId, numArrayEntries, false); |
498 | mInfo.reflProbeCubemapArrayUsedSlots.push_back(true); |
499 | } |
500 | |
501 | if(probeInfo.arrayIdx > MaxReflectionCubemaps) |
502 | { |
503 | LOGERR("Reached the maximum number of allowed reflection probe cubemaps at once. " |
504 | "Ignoring reflection probe data." ); |
505 | } |
506 | } |
507 | |
508 | void RendererScene::updateReflectionProbe(ReflectionProbe* probe, bool texture) |
509 | { |
510 | // Should only get called if transform changes, any other major changes and ReflProbeInfo entry gets rebuild |
511 | UINT32 probeId = probe->getRendererId(); |
512 | mInfo.reflProbeWorldBounds[probeId] = probe->getBounds(); |
513 | |
514 | if (texture) |
515 | { |
516 | RendererReflectionProbe& probeInfo = mInfo.reflProbes[probeId]; |
517 | probeInfo.arrayDirty = true; |
518 | } |
519 | } |
520 | |
521 | void RendererScene::unregisterReflectionProbe(ReflectionProbe* probe) |
522 | { |
523 | UINT32 probeId = probe->getRendererId(); |
524 | UINT32 arrayIdx = mInfo.reflProbes[probeId].arrayIdx; |
525 | |
526 | if (arrayIdx != (UINT32)-1) |
527 | mInfo.reflProbeCubemapArrayUsedSlots[arrayIdx] = false; |
528 | |
529 | ReflectionProbe* lastProbe = mInfo.reflProbes.back().probe; |
530 | UINT32 lastProbeId = lastProbe->getRendererId(); |
531 | |
532 | if (probeId != lastProbeId) |
533 | { |
534 | // Swap current last element with the one we want to erase |
535 | std::swap(mInfo.reflProbes[probeId], mInfo.reflProbes[lastProbeId]); |
536 | std::swap(mInfo.reflProbeWorldBounds[probeId], mInfo.reflProbeWorldBounds[lastProbeId]); |
537 | |
538 | lastProbe->setRendererId(probeId); |
539 | } |
540 | |
541 | // Last element is the one we want to erase |
542 | mInfo.reflProbes.erase(mInfo.reflProbes.end() - 1); |
543 | mInfo.reflProbeWorldBounds.erase(mInfo.reflProbeWorldBounds.end() - 1); |
544 | } |
545 | |
546 | void RendererScene::setReflectionProbeArrayIndex(UINT32 probeIdx, UINT32 arrayIdx, bool markAsClean) |
547 | { |
548 | RendererReflectionProbe* probe = &mInfo.reflProbes[probeIdx]; |
549 | probe->arrayIdx = arrayIdx; |
550 | |
551 | if (markAsClean) |
552 | probe->arrayDirty = false; |
553 | } |
554 | |
555 | void RendererScene::registerLightProbeVolume(LightProbeVolume* volume) |
556 | { |
557 | mInfo.lightProbes.notifyAdded(volume); |
558 | } |
559 | |
560 | void RendererScene::updateLightProbeVolume(LightProbeVolume* volume) |
561 | { |
562 | mInfo.lightProbes.notifyDirty(volume); |
563 | } |
564 | |
565 | void RendererScene::unregisterLightProbeVolume(LightProbeVolume* volume) |
566 | { |
567 | mInfo.lightProbes.notifyRemoved(volume); |
568 | } |
569 | |
570 | void RendererScene::updateLightProbes() |
571 | { |
572 | mInfo.lightProbes.updateProbes(); |
573 | } |
574 | |
575 | void RendererScene::registerSkybox(Skybox* skybox) |
576 | { |
577 | mInfo.skybox = skybox; |
578 | } |
579 | |
580 | void RendererScene::unregisterSkybox(Skybox* skybox) |
581 | { |
582 | if (mInfo.skybox == skybox) |
583 | mInfo.skybox = nullptr; |
584 | } |
585 | |
586 | void RendererScene::registerParticleSystem(ParticleSystem* particleSystem) |
587 | { |
588 | const auto rendererId = (UINT32)mInfo.particleSystems.size(); |
589 | particleSystem->setRendererId(rendererId); |
590 | |
591 | mInfo.particleSystems.push_back(RendererParticles()); |
592 | mInfo.particleSystemCullInfos.push_back(CullInfo(Bounds(), particleSystem->getLayer())); |
593 | |
594 | RendererParticles& rendererParticles = mInfo.particleSystems.back(); |
595 | rendererParticles.particleSystem = particleSystem; |
596 | |
597 | updateParticleSystem(particleSystem, false); |
598 | } |
599 | |
600 | void RendererScene::updateParticleSystem(ParticleSystem* particleSystem, bool tfrmOnly) |
601 | { |
602 | const UINT32 rendererId = particleSystem->getRendererId(); |
603 | RendererParticles& rendererParticles = mInfo.particleSystems[rendererId]; |
604 | |
605 | const ParticleSystemSettings& settings = particleSystem->getSettings(); |
606 | const UINT32 layer = Bitwise::mostSignificantBit(particleSystem->getLayer()); |
607 | Matrix4 localToWorldNoScale; |
608 | if (settings.simulationSpace == ParticleSimulationSpace::Local) |
609 | { |
610 | const Transform& tfrm = particleSystem->getTransform(); |
611 | |
612 | rendererParticles.localToWorld = tfrm.getMatrix(); |
613 | localToWorldNoScale = Matrix4::TRS(tfrm.getPosition(), tfrm.getRotation(), Vector3::ONE); |
614 | } |
615 | else |
616 | { |
617 | rendererParticles.localToWorld = Matrix4::IDENTITY; |
618 | localToWorldNoScale = Matrix4::IDENTITY; |
619 | } |
620 | |
621 | if(tfrmOnly) |
622 | { |
623 | SPtr<GpuParamBlockBuffer>& paramBuffer = rendererParticles.perObjectParamBuffer; |
624 | PerObjectBuffer::update(paramBuffer, rendererParticles.localToWorld, localToWorldNoScale, layer); |
625 | |
626 | return; |
627 | } |
628 | |
629 | SPtr<GpuParamBlockBuffer> perObjectParamBuffer = gPerObjectParamDef.createBuffer(); |
630 | SPtr<GpuParamBlockBuffer> particlesParamBuffer = gParticlesParamDef.createBuffer(); |
631 | PerObjectBuffer::update(perObjectParamBuffer, rendererParticles.localToWorld, localToWorldNoScale, layer); |
632 | |
633 | Vector3 axisForward = settings.orientationPlaneNormal; |
634 | |
635 | Vector3 axisUp = Vector3::UNIT_Y; |
636 | if (axisForward.dot(axisUp) > 0.9998f) |
637 | axisUp = Vector3::UNIT_Z; |
638 | |
639 | Vector3 axisRight = axisUp.cross(axisForward); |
640 | Vector3::orthonormalize(axisRight, axisUp, axisForward); |
641 | |
642 | gParticlesParamDef.gAxisUp.set(particlesParamBuffer, axisUp); |
643 | gParticlesParamDef.gAxisRight.set(particlesParamBuffer, axisRight); |
644 | |
645 | rendererParticles.perObjectParamBuffer = perObjectParamBuffer; |
646 | rendererParticles.particlesParamBuffer = particlesParamBuffer; |
647 | |
648 | // Initialize the variant of the particle system for GPU simulation, if needed |
649 | if (settings.gpuSimulation) |
650 | { |
651 | if (!rendererParticles.gpuParticleSystem) |
652 | rendererParticles.gpuParticleSystem = bs_pool_new<GpuParticleSystem>(particleSystem); |
653 | } |
654 | else |
655 | { |
656 | if (rendererParticles.gpuParticleSystem) |
657 | { |
658 | bs_pool_delete(rendererParticles.gpuParticleSystem); |
659 | rendererParticles.gpuParticleSystem = nullptr; |
660 | } |
661 | } |
662 | |
663 | ParticlesRenderElement& renElement = rendererParticles.renderElement; |
664 | renElement.type = (UINT32)RenderElementType::Particle; |
665 | |
666 | renElement.material = settings.material; |
667 | |
668 | if (renElement.material != nullptr && renElement.material->getShader() == nullptr) |
669 | renElement.material = nullptr; |
670 | |
671 | // If no material use the default material |
672 | if (renElement.material == nullptr) |
673 | renElement.material = Material::create(DefaultParticlesMat::get()->getShader()); |
674 | |
675 | const SPtr<Shader> shader = renElement.material->getShader(); |
676 | |
677 | SpriteTexture* spriteTexture = nullptr; |
678 | if (shader->hasTextureParam("gTexture" )) |
679 | spriteTexture = renElement.material->getSpriteTexture("gTexture" ).get(); |
680 | |
681 | if(!spriteTexture && shader->hasTextureParam("gAlbedoTex" )) |
682 | spriteTexture = renElement.material->getSpriteTexture("gAlbedoTex" ).get(); |
683 | |
684 | if (spriteTexture) |
685 | { |
686 | gParticlesParamDef.gUVOffset.set(particlesParamBuffer, spriteTexture->getOffset()); |
687 | gParticlesParamDef.gUVScale.set(particlesParamBuffer, spriteTexture->getScale()); |
688 | |
689 | const SpriteSheetGridAnimation& anim = spriteTexture->getAnimation(); |
690 | gParticlesParamDef.gSubImageSize.set(particlesParamBuffer, |
691 | Vector4((float)anim.numColumns, (float)anim.numRows, 1.0f / anim.numColumns, 1.0f / anim.numRows)); |
692 | } |
693 | else |
694 | { |
695 | gParticlesParamDef.gUVOffset.set(particlesParamBuffer, Vector2::ZERO); |
696 | gParticlesParamDef.gUVScale.set(particlesParamBuffer, Vector2::ONE); |
697 | gParticlesParamDef.gSubImageSize.set(particlesParamBuffer, Vector4(1.0f, 1.0f, 1.0f, 1.0f)); |
698 | } |
699 | |
700 | const ParticleOrientation orientation = settings.orientation; |
701 | const bool lockY = settings.orientationLockY; |
702 | const bool gpu = settings.gpuSimulation; |
703 | const bool is3d = settings.renderMode == ParticleRenderMode::Mesh; |
704 | |
705 | ShaderFlags shaderFlags = shader->getFlags(); |
706 | const bool requiresForwardLighting = shaderFlags.isSet(ShaderFlag::Forward); |
707 | const bool supportsClusteredForward = gRenderBeast()->getFeatureSet() == RenderBeastFeatureSet::Desktop; |
708 | |
709 | ParticleForwardLightingType forwardLightingType; |
710 | if(requiresForwardLighting) |
711 | { |
712 | forwardLightingType = supportsClusteredForward |
713 | ? ParticleForwardLightingType::Clustered |
714 | : ParticleForwardLightingType::Standard; |
715 | } |
716 | else |
717 | forwardLightingType = ParticleForwardLightingType::None; |
718 | |
719 | const ShaderVariation* variation = &getParticleShaderVariation(orientation, lockY, gpu, is3d, forwardLightingType); |
720 | |
721 | FIND_TECHNIQUE_DESC findDesc; |
722 | findDesc.variation = variation; |
723 | findDesc.override = true; |
724 | |
725 | UINT32 techniqueIdx = renElement.material->findTechnique(findDesc); |
726 | |
727 | if (techniqueIdx == (UINT32)-1) |
728 | techniqueIdx = renElement.material->getDefaultTechnique(); |
729 | |
730 | renElement.techniqueIdx = techniqueIdx; |
731 | |
732 | // Make sure the technique shaders are compiled |
733 | const SPtr<Technique>& technique = renElement.material->getTechnique(techniqueIdx); |
734 | if (technique) |
735 | technique->compile(); |
736 | |
737 | // Generate or assigned renderer specific data for the material |
738 | renElement.params = renElement.material->createParamsSet(techniqueIdx); |
739 | renElement.material->updateParamsSet(renElement.params, 0.0f, true); |
740 | |
741 | SPtr<GpuParams> gpuParams = renElement.params->getGpuParams(); |
742 | |
743 | if (gpu) |
744 | { |
745 | gpuParams->getTextureParam(GPT_VERTEX_PROGRAM, "gPositionTimeTex" , |
746 | renElement.paramsGPU.positionTimeTexture); |
747 | gpuParams->getTextureParam(GPT_VERTEX_PROGRAM, "gSizeRotationTex" , |
748 | renElement.paramsGPU.sizeRotationTexture); |
749 | gpuParams->getTextureParam(GPT_VERTEX_PROGRAM, "gCurvesTex" , |
750 | renElement.paramsGPU.curvesTexture); |
751 | |
752 | rendererParticles.gpuParticlesParamBuffer = gGpuParticlesParamDef.createBuffer(); |
753 | renElement.is3D = false; |
754 | } |
755 | else |
756 | { |
757 | switch (settings.renderMode) |
758 | { |
759 | case ParticleRenderMode::Billboard: |
760 | gpuParams->getTextureParam(GPT_VERTEX_PROGRAM, "gPositionAndRotTex" , |
761 | renElement.paramsCPUBillboard.positionAndRotTexture); |
762 | gpuParams->getTextureParam(GPT_VERTEX_PROGRAM, "gColorTex" , |
763 | renElement.paramsCPUBillboard.colorTexture); |
764 | gpuParams->getTextureParam(GPT_VERTEX_PROGRAM, "gSizeAndFrameIdxTex" , |
765 | renElement.paramsCPUBillboard.sizeAndFrameIdxTexture); |
766 | |
767 | renElement.is3D = false; |
768 | break; |
769 | case ParticleRenderMode::Mesh: |
770 | gpuParams->getTextureParam(GPT_VERTEX_PROGRAM, "gPositionTex" , |
771 | renElement.paramsCPUMesh.positionTexture); |
772 | gpuParams->getTextureParam(GPT_VERTEX_PROGRAM, "gColorTex" , |
773 | renElement.paramsCPUMesh.colorTexture); |
774 | gpuParams->getTextureParam(GPT_VERTEX_PROGRAM, "gSizeTex" , |
775 | renElement.paramsCPUMesh.sizeTexture); |
776 | gpuParams->getTextureParam(GPT_VERTEX_PROGRAM, "gRotationTex" , |
777 | renElement.paramsCPUMesh.rotationTexture); |
778 | |
779 | renElement.is3D = true; |
780 | renElement.mesh = settings.mesh; |
781 | break; |
782 | default: |
783 | break; |
784 | } |
785 | |
786 | rendererParticles.gpuParticlesParamBuffer = nullptr; |
787 | } |
788 | |
789 | // Note: Perhaps perform buffer validation to ensure expected buffer has the same size and layout as the |
790 | // provided buffer, and show a warning otherwise. But this is perhaps better handled on a higher level. |
791 | gpuParams->setParamBlockBuffer("ParticleParams" , rendererParticles.particlesParamBuffer); |
792 | gpuParams->setParamBlockBuffer("PerObject" , rendererParticles.perObjectParamBuffer); |
793 | gpuParams->setParamBlockBuffer("GpuParticleParams" , rendererParticles.gpuParticlesParamBuffer); |
794 | |
795 | gpuParams->getBufferParam(GPT_VERTEX_PROGRAM, "gIndices" , renElement.indicesBuffer); |
796 | |
797 | gpuParams->getParamInfo()->getBindings( |
798 | GpuPipelineParamInfoBase::ParamType::ParamBlock, |
799 | "PerCamera" , |
800 | renElement.perCameraBindings |
801 | ); |
802 | |
803 | if (gpu) |
804 | { |
805 | // Allocate curves |
806 | GpuParticleCurves& curves = GpuParticleSimulation::instance().getResources().getCurveTexture(); |
807 | curves.free(rendererParticles.colorCurveAlloc); |
808 | curves.free(rendererParticles.sizeScaleFrameIdxCurveAlloc); |
809 | |
810 | static constexpr UINT32 NUM_CURVE_SAMPLES = 128; |
811 | Color samples[NUM_CURVE_SAMPLES]; |
812 | |
813 | const ParticleGpuSimulationSettings& gpuSimSettings = particleSystem->getGpuSimulationSettings(); |
814 | |
815 | // Write color over lifetime curve |
816 | LookupTable colorLookup = gpuSimSettings.colorOverLifetime.toLookupTable(NUM_CURVE_SAMPLES, true); |
817 | |
818 | for (UINT32 i = 0; i < NUM_CURVE_SAMPLES; i++) |
819 | { |
820 | const float* sample = colorLookup.getSample(i); |
821 | samples[i] = Color(sample[0], sample[1], sample[2], sample[3]); |
822 | } |
823 | |
824 | rendererParticles.colorCurveAlloc = curves.alloc(samples, NUM_CURVE_SAMPLES); |
825 | |
826 | // Write size over lifetime / sprite animation curve |
827 | LookupTable sizeLookup = gpuSimSettings.sizeScaleOverLifetime.toLookupTable(NUM_CURVE_SAMPLES, true); |
828 | |
829 | float frameSamples[NUM_CURVE_SAMPLES]; |
830 | if (spriteTexture && spriteTexture->getAnimationPlayback() != SpriteAnimationPlayback::None) |
831 | { |
832 | const SpriteSheetGridAnimation& anim = spriteTexture->getAnimation(); |
833 | for (UINT32 i = 0; i < NUM_CURVE_SAMPLES; i++) |
834 | { |
835 | const float t = i / (float)(NUM_CURVE_SAMPLES - 1); |
836 | frameSamples[i] = t * (anim.count - 1); |
837 | } |
838 | } |
839 | else |
840 | memset(frameSamples, 0, sizeof(frameSamples)); |
841 | |
842 | for (UINT32 i = 0; i < NUM_CURVE_SAMPLES; i++) |
843 | { |
844 | const float* sample = sizeLookup.getSample(i); |
845 | samples[i] = Color(sample[0], sample[1], frameSamples[i], 0.0f); |
846 | } |
847 | |
848 | rendererParticles.sizeScaleFrameIdxCurveAlloc = curves.alloc(samples, NUM_CURVE_SAMPLES); |
849 | |
850 | const Vector2 colorUVOffset = GpuParticleCurves::getUVOffset(rendererParticles.colorCurveAlloc); |
851 | const float colorUVScale = GpuParticleCurves::getUVScale(rendererParticles.colorCurveAlloc); |
852 | |
853 | const Vector2 sizeScaleFrameIdxUVOffset = |
854 | GpuParticleCurves::getUVOffset(rendererParticles.sizeScaleFrameIdxCurveAlloc); |
855 | const float sizeScaleFrameIdxUVScale = |
856 | GpuParticleCurves::getUVScale(rendererParticles.sizeScaleFrameIdxCurveAlloc); |
857 | |
858 | const SPtr<GpuParamBlockBuffer>& gpuParticlesParamBuffer = rendererParticles.gpuParticlesParamBuffer; |
859 | gGpuParticlesParamDef.gColorCurveOffset.set(gpuParticlesParamBuffer, colorUVOffset); |
860 | gGpuParticlesParamDef.gColorCurveScale.set(gpuParticlesParamBuffer, Vector2(colorUVScale, 0.0f)); |
861 | gGpuParticlesParamDef.gSizeScaleFrameIdxCurveOffset.set(gpuParticlesParamBuffer, |
862 | sizeScaleFrameIdxUVOffset); |
863 | gGpuParticlesParamDef.gSizeScaleFrameIdxCurveScale.set(gpuParticlesParamBuffer, |
864 | Vector2(sizeScaleFrameIdxUVScale, 0.0f)); |
865 | |
866 | // Write sprite animation curve |
867 | if (spriteTexture) |
868 | { |
869 | gParticlesParamDef.gUVOffset.set(particlesParamBuffer, spriteTexture->getOffset()); |
870 | gParticlesParamDef.gUVScale.set(particlesParamBuffer, spriteTexture->getScale()); |
871 | |
872 | const SpriteSheetGridAnimation& anim = spriteTexture->getAnimation(); |
873 | gParticlesParamDef.gSubImageSize.set(particlesParamBuffer, |
874 | Vector4((float)anim.numColumns, (float)anim.numRows, 1.0f / anim.numColumns, 1.0f / anim.numRows)); |
875 | } |
876 | } |
877 | |
878 | // Set up buffers for lighting |
879 | const bool useForwardRendering = shaderFlags.isSet(ShaderFlag::Forward); |
880 | if (useForwardRendering) |
881 | { |
882 | renElement.forwardLightingParams.populate(gpuParams, supportsClusteredForward); |
883 | renElement.imageBasedParams.populate(gpuParams, GPT_FRAGMENT_PROGRAM, true, supportsClusteredForward, |
884 | supportsClusteredForward); |
885 | } |
886 | |
887 | const bool isTransparent = shaderFlags.isSet(ShaderFlag::Transparent); |
888 | if(isTransparent) |
889 | { |
890 | // Optional depth buffer input if requested |
891 | if (gpuParams->hasTexture(GPT_FRAGMENT_PROGRAM, "gDepthBufferTex" )) |
892 | gpuParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gDepthBufferTex" , renElement.depthInputTexture); |
893 | } |
894 | } |
895 | |
896 | void RendererScene::unregisterParticleSystem(ParticleSystem* particleSystem) |
897 | { |
898 | const UINT32 rendererId = particleSystem->getRendererId(); |
899 | RendererParticles& rendererParticles = mInfo.particleSystems[rendererId]; |
900 | |
901 | // Free curves |
902 | GpuParticleCurves& curves = GpuParticleSimulation::instance().getResources().getCurveTexture(); |
903 | curves.free(rendererParticles.colorCurveAlloc); |
904 | curves.free(rendererParticles.sizeScaleFrameIdxCurveAlloc); |
905 | |
906 | if (rendererParticles.gpuParticleSystem) |
907 | { |
908 | bs_pool_delete(rendererParticles.gpuParticleSystem); |
909 | rendererParticles.gpuParticleSystem = nullptr; |
910 | } |
911 | |
912 | ParticleSystem* lastSystem = mInfo.particleSystems.back().particleSystem; |
913 | const UINT32 lastRendererId = lastSystem->getRendererId(); |
914 | |
915 | if (rendererId != lastRendererId) |
916 | { |
917 | // Swap current last element with the one we want to erase |
918 | std::swap(mInfo.particleSystems[rendererId], mInfo.particleSystems[lastRendererId]); |
919 | std::swap(mInfo.particleSystemCullInfos[rendererId], mInfo.particleSystemCullInfos[lastRendererId]); |
920 | |
921 | lastSystem->setRendererId(rendererId); |
922 | } |
923 | |
924 | // Last element is the one we want to erase |
925 | mInfo.particleSystems.erase(mInfo.particleSystems.end() - 1); |
926 | mInfo.particleSystemCullInfos.erase(mInfo.particleSystemCullInfos.end() - 1); |
927 | } |
928 | |
929 | void RendererScene::registerDecal(Decal* decal) |
930 | { |
931 | const auto renderableId = (UINT32)mInfo.decals.size(); |
932 | decal->setRendererId(renderableId); |
933 | |
934 | mInfo.decals.emplace_back(); |
935 | mInfo.decalCullInfos.push_back(CullInfo(decal->getBounds(), decal->getLayer())); |
936 | |
937 | RendererDecal& rendererDecal = mInfo.decals.back(); |
938 | rendererDecal.decal = decal; |
939 | rendererDecal.updatePerObjectBuffer(); |
940 | |
941 | DecalRenderElement& renElement = rendererDecal.renderElement; |
942 | renElement.type = (UINT32)RenderElementType::Decal; |
943 | renElement.mesh = RendererUtility::instance().getBoxStencil(); |
944 | renElement.subMesh = renElement.mesh->getProperties().getSubMesh(); |
945 | |
946 | renElement.material = decal->getMaterial(); |
947 | |
948 | if (renElement.material != nullptr && renElement.material->getShader() == nullptr) |
949 | renElement.material = nullptr; |
950 | |
951 | // If no material use the default material |
952 | if (renElement.material == nullptr) |
953 | renElement.material = Material::create(DefaultDecalMat::get()->getShader()); |
954 | |
955 | for(UINT32 i = 0; i < 2; i++) |
956 | { |
957 | for(UINT32 j = 0; j < 3; j++) |
958 | { |
959 | FIND_TECHNIQUE_DESC findDesc; |
960 | findDesc.variation = DECAL_VAR_LOOKUP[i][j]; |
961 | findDesc.override = true; |
962 | |
963 | UINT32 techniqueIdx = renElement.material->findTechnique(findDesc); |
964 | if(techniqueIdx == (UINT32)-1) |
965 | techniqueIdx = 0; |
966 | |
967 | const SPtr<Technique>& technique = renElement.material->getTechnique(techniqueIdx); |
968 | if (technique) |
969 | technique->compile(); |
970 | |
971 | renElement.techniqueIndices[i][j] = techniqueIdx; |
972 | } |
973 | } |
974 | |
975 | renElement.techniqueIdx = renElement.techniqueIndices[0][0]; |
976 | |
977 | // Generate or assigned renderer specific data for the material |
978 | // Note: This makes the assumption that all variations of the material share the same parameter set |
979 | renElement.params = renElement.material->createParamsSet(renElement.techniqueIdx); |
980 | renElement.material->updateParamsSet(renElement.params, 0.0f, true); |
981 | |
982 | // Generate or assign sampler state overrides |
983 | renElement.samplerOverrides = allocSamplerStateOverrides(renElement); |
984 | |
985 | // Prepare all parameter bindings |
986 | SPtr<GpuParams> gpuParams = renElement.params->getGpuParams(); |
987 | |
988 | // Note: Perhaps perform buffer validation to ensure expected buffer has the same size and layout as the |
989 | // provided buffer, and show a warning otherwise. But this is perhaps better handled on a higher level. |
990 | gpuParams->setParamBlockBuffer("PerFrame" , mPerFrameParamBuffer); |
991 | gpuParams->setParamBlockBuffer("DecalParams" , rendererDecal.decalParamBuffer); |
992 | gpuParams->setParamBlockBuffer("PerObject" , rendererDecal.perObjectParamBuffer); |
993 | gpuParams->setParamBlockBuffer("PerCall" , rendererDecal.perCallParamBuffer); |
994 | |
995 | gpuParams->getParamInfo()->getBindings( |
996 | GpuPipelineParamInfoBase::ParamType::ParamBlock, |
997 | "PerCamera" , |
998 | renElement.perCameraBindings |
999 | ); |
1000 | |
1001 | if (gpuParams->hasTexture(GPT_FRAGMENT_PROGRAM, "gDepthBufferTex" )) |
1002 | gpuParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gDepthBufferTex" , renElement.depthInputTexture); |
1003 | |
1004 | if (gpuParams->hasTexture(GPT_FRAGMENT_PROGRAM, "gMaskTex" )) |
1005 | gpuParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gMaskTex" , renElement.maskInputTexture); |
1006 | } |
1007 | |
1008 | void RendererScene::updateDecal(Decal* decal) |
1009 | { |
1010 | const UINT32 rendererId = decal->getRendererId(); |
1011 | |
1012 | mInfo.decals[rendererId].updatePerObjectBuffer(); |
1013 | mInfo.decalCullInfos[rendererId].bounds = decal->getBounds(); |
1014 | } |
1015 | |
1016 | void RendererScene::unregisterDecal(Decal* decal) |
1017 | { |
1018 | const UINT32 rendererId = decal->getRendererId(); |
1019 | Decal* lastDecal = mInfo.decals.back().decal; |
1020 | const UINT32 lastDecalId = lastDecal->getRendererId(); |
1021 | |
1022 | RendererDecal& rendererDecal = mInfo.decals[rendererId]; |
1023 | DecalRenderElement& renElement = rendererDecal.renderElement; |
1024 | |
1025 | // Unregister sampler overrides |
1026 | freeSamplerStateOverrides(renElement); |
1027 | renElement.samplerOverrides = nullptr; |
1028 | |
1029 | if (rendererId != lastDecalId) |
1030 | { |
1031 | // Swap current last element with the one we want to erase |
1032 | std::swap(mInfo.decals[rendererId], mInfo.decals[lastDecalId]); |
1033 | std::swap(mInfo.decalCullInfos[rendererId], mInfo.decalCullInfos[lastDecalId]); |
1034 | |
1035 | lastDecal->setRendererId(rendererId); |
1036 | } |
1037 | |
1038 | // Last element is the one we want to erase |
1039 | mInfo.decals.erase(mInfo.decals.end() - 1); |
1040 | mInfo.decalCullInfos.erase(mInfo.decalCullInfos.end() - 1); |
1041 | } |
1042 | |
1043 | void RendererScene::setOptions(const SPtr<RenderBeastOptions>& options) |
1044 | { |
1045 | mOptions = options; |
1046 | |
1047 | for (auto& entry : mInfo.views) |
1048 | entry->setStateReductionMode(mOptions->stateReductionMode); |
1049 | } |
1050 | |
1051 | RENDERER_VIEW_DESC RendererScene::createViewDesc(Camera* camera) const |
1052 | { |
1053 | SPtr<Viewport> viewport = camera->getViewport(); |
1054 | ClearFlags clearFlags = viewport->getClearFlags(); |
1055 | RENDERER_VIEW_DESC viewDesc; |
1056 | |
1057 | viewDesc.target.clearFlags = 0; |
1058 | if (clearFlags.isSet(ClearFlagBits::Color)) |
1059 | viewDesc.target.clearFlags |= FBT_COLOR; |
1060 | |
1061 | if (clearFlags.isSet(ClearFlagBits::Depth)) |
1062 | viewDesc.target.clearFlags |= FBT_DEPTH; |
1063 | |
1064 | if (clearFlags.isSet(ClearFlagBits::Stencil)) |
1065 | viewDesc.target.clearFlags |= FBT_STENCIL; |
1066 | |
1067 | viewDesc.target.clearColor = viewport->getClearColorValue(); |
1068 | viewDesc.target.clearDepthValue = viewport->getClearDepthValue(); |
1069 | viewDesc.target.clearStencilValue = viewport->getClearStencilValue(); |
1070 | |
1071 | viewDesc.target.target = viewport->getTarget(); |
1072 | viewDesc.target.nrmViewRect = viewport->getArea(); |
1073 | viewDesc.target.viewRect = viewport->getPixelArea(); |
1074 | |
1075 | if (viewDesc.target.target != nullptr) |
1076 | { |
1077 | viewDesc.target.targetWidth = viewDesc.target.target->getProperties().width; |
1078 | viewDesc.target.targetHeight = viewDesc.target.target->getProperties().height; |
1079 | } |
1080 | else |
1081 | { |
1082 | viewDesc.target.targetWidth = 0; |
1083 | viewDesc.target.targetHeight = 0; |
1084 | } |
1085 | |
1086 | viewDesc.target.numSamples = camera->getMSAACount(); |
1087 | |
1088 | viewDesc.mainView = camera->isMain(); |
1089 | viewDesc.triggerCallbacks = true; |
1090 | viewDesc.runPostProcessing = true; |
1091 | viewDesc.capturingReflections = false; |
1092 | |
1093 | viewDesc.cullFrustum = camera->getWorldFrustum(); |
1094 | viewDesc.visibleLayers = camera->getLayers(); |
1095 | viewDesc.nearPlane = camera->getNearClipDistance(); |
1096 | viewDesc.farPlane = camera->getFarClipDistance(); |
1097 | viewDesc.flipView = false; |
1098 | |
1099 | const Transform& tfrm = camera->getTransform(); |
1100 | viewDesc.viewOrigin = tfrm.getPosition(); |
1101 | viewDesc.viewDirection = tfrm.getForward(); |
1102 | viewDesc.projTransform = camera->getProjectionMatrixRS(); |
1103 | viewDesc.viewTransform = camera->getViewMatrix(); |
1104 | viewDesc.projType = camera->getProjectionType(); |
1105 | |
1106 | viewDesc.stateReduction = mOptions->stateReductionMode; |
1107 | viewDesc.sceneCamera = camera; |
1108 | |
1109 | return viewDesc; |
1110 | } |
1111 | |
1112 | void RendererScene::updateCameraRenderTargets(Camera* camera, bool remove) |
1113 | { |
1114 | SPtr<RenderTarget> renderTarget = camera->getViewport()->getTarget(); |
1115 | |
1116 | // Remove from render target list |
1117 | int rtChanged = 0; // 0 - No RT, 1 - RT found, 2 - RT changed |
1118 | for (auto iterTarget = mInfo.renderTargets.begin(); iterTarget != mInfo.renderTargets.end(); ++iterTarget) |
1119 | { |
1120 | RendererRenderTarget& target = *iterTarget; |
1121 | for (auto iterCam = target.cameras.begin(); iterCam != target.cameras.end(); ++iterCam) |
1122 | { |
1123 | if (camera == *iterCam) |
1124 | { |
1125 | if(remove) |
1126 | { |
1127 | target.cameras.erase(iterCam); |
1128 | rtChanged = 1; |
1129 | |
1130 | } |
1131 | else |
1132 | { |
1133 | if (renderTarget != target.target) |
1134 | { |
1135 | target.cameras.erase(iterCam); |
1136 | rtChanged = 2; |
1137 | |
1138 | } |
1139 | else |
1140 | rtChanged = 1; |
1141 | } |
1142 | |
1143 | break; |
1144 | } |
1145 | } |
1146 | |
1147 | if (target.cameras.empty()) |
1148 | { |
1149 | mInfo.renderTargets.erase(iterTarget); |
1150 | break; |
1151 | } |
1152 | } |
1153 | |
1154 | // Register in render target list |
1155 | if (renderTarget != nullptr && !remove && (rtChanged == 0 || rtChanged == 2)) |
1156 | { |
1157 | auto findIter = std::find_if(mInfo.renderTargets.begin(), mInfo.renderTargets.end(), |
1158 | [&](const RendererRenderTarget& x) { return x.target == renderTarget; }); |
1159 | |
1160 | if (findIter != mInfo.renderTargets.end()) |
1161 | { |
1162 | findIter->cameras.push_back(camera); |
1163 | } |
1164 | else |
1165 | { |
1166 | mInfo.renderTargets.push_back(RendererRenderTarget()); |
1167 | RendererRenderTarget& renderTargetData = mInfo.renderTargets.back(); |
1168 | |
1169 | renderTargetData.target = renderTarget; |
1170 | renderTargetData.cameras.push_back(camera); |
1171 | } |
1172 | |
1173 | // Sort render targets based on priority |
1174 | auto cameraComparer = [&](const Camera* a, const Camera* b) { return a->getPriority() > b->getPriority(); }; |
1175 | auto renderTargetInfoComparer = [&](const RendererRenderTarget& a, const RendererRenderTarget& b) |
1176 | { return a.target->getProperties().priority > b.target->getProperties().priority; }; |
1177 | std::sort(begin(mInfo.renderTargets), end(mInfo.renderTargets), renderTargetInfoComparer); |
1178 | |
1179 | for (auto& camerasPerTarget : mInfo.renderTargets) |
1180 | { |
1181 | Vector<Camera*>& cameras = camerasPerTarget.cameras; |
1182 | |
1183 | std::sort(begin(cameras), end(cameras), cameraComparer); |
1184 | } |
1185 | } |
1186 | } |
1187 | |
1188 | void RendererScene::refreshSamplerOverrides(bool force) |
1189 | { |
1190 | bool anyDirty = false; |
1191 | for (auto& entry : mSamplerOverrides) |
1192 | { |
1193 | SPtr<MaterialParams> materialParams = entry.first.material->_getInternalParams(); |
1194 | |
1195 | MaterialSamplerOverrides* materialOverrides = entry.second; |
1196 | for(UINT32 i = 0; i < materialOverrides->numOverrides; i++) |
1197 | { |
1198 | SamplerOverride& override = materialOverrides->overrides[i]; |
1199 | const MaterialParamsBase::ParamData* materialParamData = materialParams->getParamData(override.paramIdx); |
1200 | |
1201 | SPtr<SamplerState> samplerState; |
1202 | materialParams->getSamplerState(*materialParamData, samplerState); |
1203 | |
1204 | UINT64 hash = 0; |
1205 | if (samplerState != nullptr) |
1206 | hash = samplerState->getProperties().getHash(); |
1207 | |
1208 | if (hash != override.originalStateHash || force) |
1209 | { |
1210 | if (samplerState != nullptr) |
1211 | override.state = SamplerOverrideUtility::generateSamplerOverride(samplerState, mOptions); |
1212 | else |
1213 | override.state = SamplerOverrideUtility::generateSamplerOverride(SamplerState::getDefault(), mOptions); |
1214 | |
1215 | override.originalStateHash = override.state->getProperties().getHash(); |
1216 | materialOverrides->isDirty = true; |
1217 | } |
1218 | |
1219 | // Dirty flag can also be set externally, so check here even though we assign it above |
1220 | if (materialOverrides->isDirty) |
1221 | anyDirty = true; |
1222 | } |
1223 | } |
1224 | |
1225 | // Early exit if possible |
1226 | if (!anyDirty) |
1227 | return; |
1228 | |
1229 | UINT32 numRenderables = (UINT32)mInfo.renderables.size(); |
1230 | for (UINT32 i = 0; i < numRenderables; i++) |
1231 | { |
1232 | for(auto& element : mInfo.renderables[i]->elements) |
1233 | { |
1234 | MaterialSamplerOverrides* overrides = element.samplerOverrides; |
1235 | if(overrides != nullptr && overrides->isDirty) |
1236 | { |
1237 | UINT32 numPasses = element.material->getNumPasses(); |
1238 | for(UINT32 j = 0; j < numPasses; j++) |
1239 | { |
1240 | SPtr<GpuParams> params = element.params->getGpuParams(j); |
1241 | |
1242 | const UINT32 numStages = 6; |
1243 | for (UINT32 k = 0; k < numStages; k++) |
1244 | { |
1245 | GpuProgramType type = (GpuProgramType)k; |
1246 | |
1247 | SPtr<GpuParamDesc> paramDesc = params->getParamDesc(type); |
1248 | if (paramDesc == nullptr) |
1249 | continue; |
1250 | |
1251 | for (auto& samplerDesc : paramDesc->samplers) |
1252 | { |
1253 | UINT32 set = samplerDesc.second.set; |
1254 | UINT32 slot = samplerDesc.second.slot; |
1255 | |
1256 | UINT32 overrideIndex = overrides->passes[j].stateOverrides[set][slot]; |
1257 | if (overrideIndex == (UINT32)-1) |
1258 | continue; |
1259 | |
1260 | params->setSamplerState(set, slot, overrides->overrides[overrideIndex].state); |
1261 | } |
1262 | } |
1263 | } |
1264 | } |
1265 | } |
1266 | } |
1267 | |
1268 | for (auto& entry : mSamplerOverrides) |
1269 | entry.second->isDirty = false; |
1270 | } |
1271 | |
1272 | void RendererScene::setParamFrameParams(float time) |
1273 | { |
1274 | gPerFrameParamDef.gTime.set(mPerFrameParamBuffer, time); |
1275 | } |
1276 | |
1277 | void RendererScene::prepareRenderable(UINT32 idx, const FrameInfo& frameInfo) |
1278 | { |
1279 | if (mInfo.renderableReady[idx]) |
1280 | return; |
1281 | |
1282 | // Note: Before uploading bone matrices perhaps check if they has actually been changed since last frame |
1283 | if(frameInfo.perFrameData.animation != nullptr) |
1284 | mInfo.renderables[idx]->renderable->updateAnimationBuffers(*frameInfo.perFrameData.animation); |
1285 | |
1286 | // Note: Could this step be moved in notifyRenderableUpdated, so it only triggers when material actually gets |
1287 | // changed? Although it shouldn't matter much because if the internal versions keeping track of dirty params. |
1288 | for (auto& element : mInfo.renderables[idx]->elements) |
1289 | element.material->updateParamsSet(element.params, element.materialAnimationTime); |
1290 | |
1291 | mInfo.renderables[idx]->perObjectParamBuffer->flushToGPU(); |
1292 | mInfo.renderableReady[idx] = true; |
1293 | } |
1294 | |
1295 | void RendererScene::prepareParticleSystem(UINT32 idx, const FrameInfo& frameInfo) |
1296 | { |
1297 | ParticlesRenderElement& renElement = mInfo.particleSystems[idx].renderElement; |
1298 | renElement.material->updateParamsSet(renElement.params, 0.0f); |
1299 | |
1300 | mInfo.particleSystems[idx].perObjectParamBuffer->flushToGPU(); |
1301 | } |
1302 | |
1303 | void RendererScene::prepareDecal(UINT32 idx, const FrameInfo& frameInfo) |
1304 | { |
1305 | DecalRenderElement& renElement = mInfo.decals[idx].renderElement; |
1306 | renElement.material->updateParamsSet(renElement.params, renElement.materialAnimationTime); |
1307 | |
1308 | mInfo.decals[idx].perObjectParamBuffer->flushToGPU(); |
1309 | } |
1310 | |
1311 | void RendererScene::updateParticleSystemBounds(const ParticlePerFrameData* particleRenderData) |
1312 | { |
1313 | // Note: Avoid updating bounds for deterministic particle systems every frame. Also see if this can be copied |
1314 | // over in a faster way (or ideally just assigned) |
1315 | |
1316 | for(auto& entry : mInfo.particleSystems) |
1317 | { |
1318 | const UINT32 rendererId = entry.particleSystem->getRendererId(); |
1319 | |
1320 | AABox worldAABox = AABox::INF_BOX; |
1321 | const auto iterFind = particleRenderData->cpuData.find(entry.particleSystem->getId()); |
1322 | if(iterFind != particleRenderData->cpuData.end()) |
1323 | worldAABox = iterFind->second->bounds; |
1324 | else if(entry.gpuParticleSystem) |
1325 | worldAABox = entry.gpuParticleSystem->getBounds(); |
1326 | |
1327 | const ParticleSystemSettings& settings = entry.particleSystem->getSettings(); |
1328 | if (settings.simulationSpace == ParticleSimulationSpace::Local) |
1329 | worldAABox.transformAffine(entry.localToWorld); |
1330 | |
1331 | const Sphere worldSphere(worldAABox.getCenter(), worldAABox.getRadius()); |
1332 | mInfo.particleSystemCullInfos[rendererId].bounds = Bounds(worldAABox, worldSphere); |
1333 | } |
1334 | } |
1335 | |
1336 | MaterialSamplerOverrides* RendererScene::allocSamplerStateOverrides(RenderElement& elem) |
1337 | { |
1338 | SamplerOverrideKey samplerKey(elem.material, elem.techniqueIdx); |
1339 | auto iterFind = mSamplerOverrides.find(samplerKey); |
1340 | if (iterFind != mSamplerOverrides.end()) |
1341 | { |
1342 | iterFind->second->refCount++; |
1343 | return iterFind->second; |
1344 | } |
1345 | else |
1346 | { |
1347 | SPtr<Shader> shader = elem.material->getShader(); |
1348 | MaterialSamplerOverrides* samplerOverrides = SamplerOverrideUtility::generateSamplerOverrides(shader, |
1349 | elem.material->_getInternalParams(), elem.params, mOptions); |
1350 | |
1351 | mSamplerOverrides[samplerKey] = samplerOverrides; |
1352 | |
1353 | samplerOverrides->refCount++; |
1354 | return samplerOverrides; |
1355 | } |
1356 | } |
1357 | |
1358 | void RendererScene::freeSamplerStateOverrides(RenderElement& elem) |
1359 | { |
1360 | SamplerOverrideKey samplerKey(elem.material, elem.techniqueIdx); |
1361 | |
1362 | auto iterFind = mSamplerOverrides.find(samplerKey); |
1363 | assert(iterFind != mSamplerOverrides.end()); |
1364 | |
1365 | MaterialSamplerOverrides* samplerOverrides = iterFind->second; |
1366 | samplerOverrides->refCount--; |
1367 | if (samplerOverrides->refCount == 0) |
1368 | { |
1369 | SamplerOverrideUtility::destroySamplerOverrides(samplerOverrides); |
1370 | mSamplerOverrides.erase(iterFind); |
1371 | } |
1372 | } |
1373 | }} |
1374 | |