Cogs.Core
TerrainRenderer.cpp
1#include "TerrainRenderer.h"
2
3#include "RenderContext.h"
4#include "Terrain.h"
5
6#include "Systems/Core/TransformSystem.h"
7#include "Systems/Core/CameraSystem.h"
8
9#include "Components/Core/RenderComponent.h"
10
11#include "Resources/MaterialManager.h"
12
13#include "Renderer/RenderStateUpdater.h"
14#include "Renderer/RenderMaterialInstance.h"
15#include "Renderer/RenderEffect.h"
16#include "Renderer/RenderTarget.h"
17#include "Renderer/Tasks/RenderListTask.h"
18#include "Renderer/Tasks/GenerateListTask.h"
19
20#include "Context.h"
21
22#include "TerrainSystem.h"
23#include "OceanSystem.h"
24
25#include "Renderer/IRenderer.h"
26
27Cogs::Core::TerrainRenderer::~TerrainRenderer()
28{
29}
31{
32 this->device = device;
33 this->context = context;
34
35 terrainSystem = ExtensionRegistry::getExtensionSystem<TerrainSystem>(context);
36}
37
38void Cogs::Core::TerrainRenderer::handleEvent(uint32_t eventId, const DrawContext * renderingContext)
39{
40 if (!renderingContext) return;
41
42 switch (eventId) {
44 for (auto & terrain : terrainSystem->pool) {
45 preRender(terrain, renderingContext);
46 }
47 break;
49 for (auto & terrain : terrainSystem->pool) {
50 postRender(terrain, renderingContext);
51 }
52 break;
53 default:
54 break;
55 }
56}
57
58namespace Cogs
59{
60 namespace Core
61 {
62 void updateTerrainContext(TerrainData & terrainData, const RenderTaskContext * renderingContext, const RenderTarget * renderTarget, const CameraData & cameraData)
63 {
64 auto & renderContext = terrainData.renderContext;
65
66 renderContext.renderTarget = renderTarget->renderTargetHandle;
67 renderContext.depthStencilTarget = renderTarget->depthTargetHandle;
68
69 renderContext.isOIT = renderingContext->renderer->getSettings().transparencyAlgorithm == RenderSettings::TransparencyAlgorithm::OIT;
70
71 renderContext.offsetEnabled = false;
72 renderContext.device = renderingContext->renderer->getDevice();
73 renderContext.context = renderingContext->renderer->getDevice()->getImmediateContext();
74 if (terrainData.viewportFromTarget) {
75 renderContext.width = static_cast<uint16_t>(renderTarget->width);
76 renderContext.height = static_cast<uint16_t>(renderTarget->height);
77 renderContext.viewportX = 0;
78 renderContext.viewportY = 0;
79 }
80 else {
81 renderContext.width = static_cast<uint16_t>(cameraData.viewportSize.x);
82 renderContext.height = static_cast<uint16_t>(cameraData.viewportSize.y);
83 renderContext.viewportX = static_cast<uint16_t>(cameraData.viewportOrigin.x);
84 renderContext.viewportY = static_cast<uint16_t>(cameraData.viewportOrigin.y);
85 }
86 renderContext.cullVolume.frustum = cameraData.frustum;
87
88 renderContext.origin = renderingContext->context->transformSystem->getOrigin();
89 renderContext.cullOrigin = renderContext.origin;
90 renderContext.offset = terrainData.offset;
91
92 renderContext.scene.projectionMatrix = cameraData.projectionMatrix;
93 renderContext.scene.viewMatrix = cameraData.viewMatrix;
94 renderContext.scene.viewMatrixInverse = cameraData.inverseViewMatrix;
95
96 renderContext.wireframe = renderingContext->renderer->getMode() == RenderMode::Wireframe;
97
98 renderContext.maxAppliedTilesPerFrame = terrainData.maxTilesPerFrame;
99 renderContext.offsetEnabled = false;
100
101 renderContext.reverseDepth = renderingContext->context->variables->get("renderer.reverseDepth", false);
102
103 renderContext.rayPickIndex = cameraData.rayPickId;
104 }
105 }
106}
107
108void Cogs::Core::TerrainRenderer::generateCommands(const RenderTaskContext * renderingContext, RenderList * renderList)
109{
110 for (auto & terrain : terrainSystem->pool) {
111 auto & terrainData = terrainSystem->getData(&terrain);
112
113 if (!terrainData.initialized) continue;
114
115 auto renderComponent = terrain.getComponent<RenderComponent>();
116
117 terrainData.visible = renderComponent->isVisible();
118
119 float dummy = 0.f;
120 terrainData.getBounds->getBounds(context, terrainData.bbox, dummy);
121
122 auto & terrainItem = renderList->createCustom(&terrainData.terrainStreamsLayout);
123 terrainItem.materialInstance = terrain.material.resolve();
124
125 if (terrainItem.materialInstance) {
126 terrainItem.drawOrder = terrainItem.materialInstance->options.drawOrder;
127 }
128 getTransparencyState(*renderingContext->states, terrainItem.materialInstance, terrainItem);
129 if (terrainItem.blendState != static_cast<uint16_t>(BlendMode::None)) {
130 terrainItem.flags |= RenderItemFlags::Transparent;
131 }
132 terrainItem.layer = renderComponent->layer;
133 terrainItem.flags |= RenderItemFlags::Custom;
134 terrainItem.flags |= renderComponent->castShadows() ? RenderItemFlags::CastShadows : RenderItemFlags::None;
135 terrainItem.objectId = renderComponent->objectId;
136 terrainItem.setBounds(&terrainData.bbox);
137
138 terrainItem.setCallbackData(&terrainData);
139 terrainItem.callback = [](RenderTaskContext * renderingContext, DrawContext * drawContext, const RenderItem * terrainItem)
140 {
141 TerrainData & terrainData = *terrainItem->getCallbackData<TerrainData>();
142
143 RenderMaterialInstance * renderMaterial = renderingContext->renderer->getRenderResources().getRenderMaterialInstance(terrainItem->materialInstance);
144 RenderMaterialInstance * depthRenderMaterial = renderingContext->renderer->getRenderResources().getRenderMaterialInstance(terrainData.depthMaterial);
145
146 if (!renderMaterial || !depthRenderMaterial) return;
147
148 auto bindings = terrainItem->binding;
149 auto forwardPermutation = renderingContext->renderer->getEnginePermutations().get("Forward");
150
151 RenderPassOptions passOptions = drawContext->cameraData->passOptions ? *drawContext->cameraData->passOptions : RenderPassOptions{};
152 passOptions.updateHash();
153
154 bool depthReady = depthRenderMaterial->checkReady(terrainData.terrainStreamsLayout, forwardPermutation, passOptions, ClipShapeType(0)) != nullptr;
155 if (!depthReady) return;
156
157 auto depthBindings = depthRenderMaterial->getBindings(terrainData.terrainStreamsLayout, forwardPermutation, passOptions, ClipShapeType(0));
158 if (!depthBindings || !depthBindings->renderEffect || depthBindings->renderEffect->isDelayed()) {
159 return;
160 }
161
162 if (!bindings->renderEffect->isActive() || !depthBindings->renderEffect->isActive()) return;
163 if (!HandleIsValid(bindings->sceneBufferBinding) || !HandleIsValid(depthBindings->sceneBufferBinding)) return;
164
165 updateTerrainContext(terrainData, renderingContext, drawContext->renderTarget, *drawContext->cameraData);
166
167 const size_t permutation = drawContext->permutationIndex;
168 terrainData.renderContext.permutation = permutation;
169 auto & permutationRenderContextData = terrainData.renderContext.getPermutationDependentRenderContextData(permutation);
170 permutationRenderContextData.clipmapEffect = bindings->renderEffect->effectHandle;
171 permutationRenderContextData.clipmapDepthEffect = depthBindings->renderEffect->effectHandle;
172
173 auto pass = drawContext->cameraData->renderPass;
174 switch (pass) {
175 case RenderPass::Refraction:
176 terrainData.renderContext.renderPass = TerrainRenderPass::Refraction;
177 break;
178 case RenderPass::Reflection:
179 terrainData.renderContext.renderPass = TerrainRenderPass::Reflection;
180 break;
181 case RenderPass::Other:
182 terrainData.renderContext.renderPass = TerrainRenderPass::Other;
183 break;
184 default:
185 terrainData.renderContext.renderPass = TerrainRenderPass::Main;
186 break;
187 }
188
189 terrainData.renderContext.callback = [&](Cogs::EffectHandle effect) {
190 const bool isRegularPass = effect == bindings->renderEffect->effectHandle;
191
192 if (isRegularPass) {
193 drawContext->deviceContext->setBlendState(drawContext->renderer->getRenderStates().blendStates[terrainItem->blendState].handle);
194
195 updateSceneBindings(drawContext, drawContext->cameraData, bindings);
196 drawContext->task->applyMaterial(*drawContext, *terrainItem);
197 updateMaterialBindings(drawContext, renderMaterial, bindings, true);
198 applyMaterialPerObject(drawContext, renderMaterial, *terrainItem);
199 } else {
200 drawContext->deviceContext->setBlendState(drawContext->renderer->getRenderStates().blendStates[size_t(BlendMode::None)].handle);
201
202 updateSceneBindings(drawContext, drawContext->cameraData, depthRenderMaterial->getBindings(terrainData.terrainStreamsLayout, forwardPermutation, passOptions, ClipShapeType(0)));
203 applyMaterialPerObject(drawContext, depthRenderMaterial, *terrainItem);
204 }
205 };
206
207 if (terrainData.visible) {
208 if (terrainData.renderContext.renderPass == TerrainRenderPass::Main) {
209 auto & camSystem = renderingContext->context->cameraSystem;
210 auto mainCam = camSystem->getMainCamera();
211
212 auto oldData = drawContext->cameraData;
213 for (auto & cameraComponent : camSystem->pool) {
214 if (mainCam == &cameraComponent) continue;
215 if ((cameraComponent.flags & CameraFlags::EnablePicking) == 0) continue;
216
217 auto & cameraData = renderingContext->context->cameraSystem->getData(&cameraComponent);
218 drawContext->cameraData = &cameraData;
219
220 updateEngineBuffers(renderingContext, drawContext->renderTarget, &cameraData, nullptr);
221 updateTerrainContext(terrainData, renderingContext, drawContext->renderTarget, cameraData);
222 terrainDepthRender(terrainData.context, &terrainData.renderContext);
223 }
224
225 drawContext->cameraData = oldData;
226 updateEngineBuffers(renderingContext, drawContext->renderTarget, drawContext->cameraData, nullptr);
227 updateTerrainContext(terrainData, renderingContext, drawContext->renderTarget, *drawContext->cameraData);
228 }
229
230 terrainDepthRender(terrainData.context, &terrainData.renderContext);
231 terrainRender(terrainData.context, &terrainData.renderContext);
232 }
233 else {
234 if (terrainData.oceanData) {
235 // If ocean is visible we still need pre-render.
236 auto oceanRenderComponent = terrainData.oceanData->oceanComponent->getComponent<RenderComponent>();
237
238 if (oceanRenderComponent && oceanRenderComponent->isVisible()) {
239 terrainDepthRender(terrainData.context, &terrainData.renderContext);
240 }
241 }
242 }
243 };
244
245 if (terrainData.renderContext.oceanEnabled) {
246 auto & oceanData = *terrainData.oceanData;
247
248 if (!oceanData.initialized) break;
249
250 auto renderComponent_ = oceanData.oceanComponent->getComponent<RenderComponent>();
251
252 if (!renderComponent_->isVisible()) continue;
253
254 auto & oceanItem = renderList->createCustom(&oceanData.streamsLayout);
255 oceanItem.layer = RenderLayers::Ocean;
256
257 oceanItem.flags |= RenderItemFlags::Transparent;
258 oceanItem.blendState = (uint16_t)BlendMode::Blend;
259 if ((terrainItem.flags & RenderItemFlags::Transparent) != 0) {
260 oceanItem.drawOrder = terrainItem.drawOrder + 1;
261 }
262 else {
263 oceanItem.drawOrder = 0;
264 }
265 oceanItem.materialInstance = oceanData.material.resolve();
266 oceanItem.objectId = renderComponent_->objectId;
267
268 float dummy_ = 0.f;
269 oceanData.bounds->getBounds(context, oceanData.bbox, dummy_);
270
271 oceanItem.setBounds(&oceanData.bbox);
272
273 oceanItem.setCallbackData(&oceanData);
274 oceanItem.callback = [](RenderTaskContext * renderingContext, DrawContext * drawContext, const RenderItem * oceanItem)
275 {
276 auto & oceanData = *oceanItem->getCallbackData<OceanData>();
277
278 if (drawContext->permutation->isShadowPass()) return;
279
280 RenderMaterialInstance * oceanRenderMaterial = renderingContext->renderer->getRenderResources().getRenderMaterialInstance(oceanData.material);
281
282 if (!oceanRenderMaterial) return;
283
284 const EffectBinding * oceanBindings = oceanItem->binding;
285
286 if (!oceanBindings || !oceanBindings->renderEffect) return;
287
288 if (oceanBindings->renderEffect->isDelayed()) {
289 oceanBindings->renderEffect->requestLoad();
290
291 return;
292 }
293
294 if (!oceanBindings->renderEffect->isActive()) return;
295 if (!HandleIsValid(oceanBindings->sceneBufferBinding)) return;
296
297 auto & terrainData = *oceanData.terrainData;
298
299 updateTerrainContext(terrainData, renderingContext, drawContext->renderTarget, *drawContext->cameraData);
300
301 const size_t permutation = drawContext->permutationIndex;
302 terrainData.renderContext.permutation = permutation;
303 auto & permutationRenderContextData = terrainData.renderContext.getPermutationDependentRenderContextData(permutation);
304 permutationRenderContextData.oceanEffect = oceanBindings->renderEffect->effectHandle;
305
306 terrainData.renderContext.oceanCallback = [&](Cogs::EffectHandle /*effect*/) {
307 drawContext->task->applyMaterial(*drawContext, *oceanItem);
308 updateMaterialBindings(drawContext, oceanRenderMaterial, oceanBindings, false);
309 updateSceneBindings(drawContext, drawContext->cameraData, oceanBindings);
310 applyMaterialPerObject(drawContext, oceanRenderMaterial, *oceanItem);
311 };
312
313 oceanRender(terrainData.context, &terrainData.renderContext);
314 };
315 }
316 }
317}
318
319void Cogs::Core::TerrainRenderer::preRender(TerrainComponent & terrain, const DrawContext * drawContext)
320{
321 // Only pre-render for main camera.
322 if (drawContext->cameraData != &context->cameraSystem->getMainCameraData()) return;
323
324 auto & terrainData = terrainSystem->getData(&terrain);
325
326 if (!terrainData.initialized) return;
327
328 auto renderComponent = terrain.getComponent<RenderComponent>();
329
330 if (!renderComponent->isVisible()) {
331 if (terrainData.oceanData) {
332 // If ocean is visible we still need pre-render.
333 auto renderComponent_ = terrainData.oceanData->oceanComponent->getComponent<RenderComponent>();
334
335 if (!renderComponent_->isVisible()) return;
336 }
337 else {
338 return;
339 }
340 }
341
342 updateTerrainContext(terrainData, drawContext->taskContext, drawContext->renderTarget, *drawContext->cameraData);
343
344 terrainPreRender(terrainData.context, &terrainData.renderContext);
345}
346void Cogs::Core::TerrainRenderer::postRender(TerrainComponent & terrain, const DrawContext * renderingContext)
347{
348 auto & terrainData = terrainSystem->getData(&terrain);
349
350 if (!terrainData.initialized) return;
351 if (!terrainData.visible && !terrainData.oceanData) return;
352 if (!terrainData.renderContext.context) return;
353
354 terrainPostRender(terrainData.context, &terrainData.renderContext);
355
356 if (terrainRequiresRedraw(terrainData.context)) {
357 renderingContext->context->engine->setDirty();
358 }
359}
A Context instance contains all the services, systems and runtime components needed to use Cogs.
Definition: Context.h:83
std::unique_ptr< class Variables > variables
Variables service instance.
Definition: Context.h:180
IGraphicsDevice * getDevice() override
Get the graphics device used by the renderer.
Definition: Renderer.h:45
RenderMode getMode() const override
Get the Rendering mode used by the renderer.
Definition: Renderer.h:39
const RenderSettings & getSettings() const override
Get the settings of the renderer.
Definition: Renderer.h:46
void initialize(Context *context, IGraphicsDevice *device) override
Initialize the extension using the given context and device.
void handleEvent(uint32_t eventId, const DrawContext *renderingContext) override
Called when rendering events occur.
glm::dvec3 getOrigin() const
Gets the Origin offset of the scene.
Represents a graphics device used to manage graphics resources and issue drawing commands.
virtual IContext * getImmediateContext()=0
Get a pointer to the immediate context used to issue commands to the graphics device.
ClipShapeType
Specifices what kind of shape a clip shape has.
bool HandleIsValid(const ResourceHandle_t< T > &handle)
Check if the given resource is valid, that is not equal to NoHandle or InvalidHandle.
@ EnablePicking
Supports picking.
@ Blend
Render with regular alpha blending.
@ None
No blending enabled for opaque shapes, defaults to Blend for transparent shapes.
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
Contains data describing a Camera instance and its derived data structured such as matrix data and vi...
Definition: CameraSystem.h:67
@ PostRender
Rendering has finished for a given rendering context.
Definition: IRenderer.h:101
@ PreRender
Pre rendering happening for a given rendering context.
Definition: IRenderer.h:93