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
22namespace 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