Line data Source code
1 : /* Copyright (C) 2023 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "renderer/TerrainRenderer.h"
21 :
22 : #include "graphics/Camera.h"
23 : #include "graphics/Canvas2D.h"
24 : #include "graphics/Decal.h"
25 : #include "graphics/GameView.h"
26 : #include "graphics/LightEnv.h"
27 : #include "graphics/LOSTexture.h"
28 : #include "graphics/Patch.h"
29 : #include "graphics/Model.h"
30 : #include "graphics/ShaderManager.h"
31 : #include "graphics/TerritoryTexture.h"
32 : #include "graphics/TextRenderer.h"
33 : #include "graphics/TextureManager.h"
34 : #include "maths/MathUtil.h"
35 : #include "maths/Vector2D.h"
36 : #include "ps/CLogger.h"
37 : #include "ps/CStrInternStatic.h"
38 : #include "ps/Filesystem.h"
39 : #include "ps/Game.h"
40 : #include "ps/Profile.h"
41 : #include "ps/World.h"
42 : #include "renderer/backend/IDevice.h"
43 : #include "renderer/backend/PipelineState.h"
44 : #include "renderer/DecalRData.h"
45 : #include "renderer/PatchRData.h"
46 : #include "renderer/Renderer.h"
47 : #include "renderer/RenderingOptions.h"
48 : #include "renderer/SceneRenderer.h"
49 : #include "renderer/ShadowMap.h"
50 : #include "renderer/SkyManager.h"
51 : #include "renderer/VertexArray.h"
52 : #include "renderer/WaterManager.h"
53 :
54 : #include <memory>
55 :
56 : /**
57 : * TerrainRenderer keeps track of which phase it is in, to detect
58 : * when Submit, PrepareForRendering etc. are called in the wrong order.
59 : */
60 : enum Phase
61 : {
62 : Phase_Submit,
63 : Phase_Render
64 : };
65 :
66 :
67 : /**
68 : * Struct TerrainRendererInternals: Internal variables used by the TerrainRenderer class.
69 : */
70 12 : struct TerrainRendererInternals
71 : {
72 : /// Which phase (submitting or rendering patches) are we in right now?
73 : Phase phase;
74 :
75 : /// Patches that were submitted for this frame
76 : std::vector<CPatchRData*> visiblePatches[CSceneRenderer::CULL_MAX];
77 :
78 : /// Decals that were submitted for this frame
79 : std::vector<CDecalRData*> visibleDecals[CSceneRenderer::CULL_MAX];
80 :
81 : /// Fancy water shader
82 : CShaderTechniquePtr fancyWaterTech;
83 :
84 : CShaderTechniquePtr shaderTechniqueSolid, shaderTechniqueSolidDepthTest;
85 :
86 : Renderer::Backend::IVertexInputLayout* overlayVertexInputLayout = nullptr;
87 : Renderer::Backend::IVertexInputLayout* decalsVertexInputLayout = nullptr;
88 :
89 : Renderer::Backend::IVertexInputLayout* baseVertexInputLayout = nullptr;
90 : Renderer::Backend::IVertexInputLayout* blendVertexInputLayout = nullptr;
91 : Renderer::Backend::IVertexInputLayout* streamVertexInputLayout = nullptr;
92 : Renderer::Backend::IVertexInputLayout* streamWithPositionAsTexCoordVertexInputLayout = nullptr;
93 : Renderer::Backend::IVertexInputLayout* sideVertexInputLayout = nullptr;
94 :
95 : Renderer::Backend::IVertexInputLayout* waterSurfaceVertexInputLayout = nullptr;
96 : Renderer::Backend::IVertexInputLayout* waterSurfaceWithDataVertexInputLayout = nullptr;
97 : Renderer::Backend::IVertexInputLayout* waterShoreVertexInputLayout = nullptr;
98 :
99 : CSimulation2* simulation;
100 : };
101 :
102 :
103 :
104 : ///////////////////////////////////////////////////////////////////
105 : // Construction/Destruction
106 6 : TerrainRenderer::TerrainRenderer()
107 : {
108 6 : m = new TerrainRendererInternals();
109 6 : m->phase = Phase_Submit;
110 6 : }
111 :
112 12 : TerrainRenderer::~TerrainRenderer()
113 : {
114 6 : delete m;
115 6 : }
116 :
117 6 : void TerrainRenderer::Initialize()
118 : {
119 6 : const std::array<Renderer::Backend::SVertexAttributeFormat, 2> overlayAttributes{{
120 : {Renderer::Backend::VertexAttributeStream::POSITION,
121 : Renderer::Backend::Format::R32G32B32_SFLOAT, 0, sizeof(float) * 3,
122 : Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0},
123 : {Renderer::Backend::VertexAttributeStream::UV0,
124 : Renderer::Backend::Format::R32G32B32_SFLOAT, 0, sizeof(float) * 3,
125 : Renderer::Backend::VertexAttributeRate::PER_VERTEX, 0}
126 : }};
127 6 : m->overlayVertexInputLayout = g_Renderer.GetVertexInputLayout(overlayAttributes);
128 :
129 6 : m->decalsVertexInputLayout = CDecalRData::GetVertexInputLayout();
130 :
131 6 : m->baseVertexInputLayout = CPatchRData::GetBaseVertexInputLayout();
132 6 : m->blendVertexInputLayout = CPatchRData::GetBlendVertexInputLayout();
133 6 : m->streamVertexInputLayout = CPatchRData::GetStreamVertexInputLayout(false);
134 12 : m->streamWithPositionAsTexCoordVertexInputLayout =
135 6 : CPatchRData::GetStreamVertexInputLayout(true);
136 6 : m->sideVertexInputLayout = CPatchRData::GetSideVertexInputLayout();
137 :
138 6 : m->waterSurfaceVertexInputLayout = CPatchRData::GetWaterSurfaceVertexInputLayout(false);
139 6 : m->waterSurfaceWithDataVertexInputLayout = CPatchRData::GetWaterSurfaceVertexInputLayout(true);
140 6 : m->waterShoreVertexInputLayout = CPatchRData::GetWaterShoreVertexInputLayout();
141 6 : }
142 :
143 0 : void TerrainRenderer::SetSimulation(CSimulation2* simulation)
144 : {
145 0 : m->simulation = simulation;
146 0 : }
147 :
148 : ///////////////////////////////////////////////////////////////////
149 : // Submit a patch for rendering
150 0 : void TerrainRenderer::Submit(int cullGroup, CPatch* patch)
151 : {
152 0 : ENSURE(m->phase == Phase_Submit);
153 :
154 0 : CPatchRData* data = (CPatchRData*)patch->GetRenderData();
155 0 : if (data == 0)
156 : {
157 : // no renderdata for patch, create it now
158 0 : data = new CPatchRData(patch, m->simulation);
159 0 : patch->SetRenderData(data);
160 : }
161 0 : data->Update(m->simulation);
162 :
163 0 : m->visiblePatches[cullGroup].push_back(data);
164 0 : }
165 :
166 : ///////////////////////////////////////////////////////////////////
167 : // Submit a decal for rendering
168 0 : void TerrainRenderer::Submit(int cullGroup, CModelDecal* decal)
169 : {
170 0 : ENSURE(m->phase == Phase_Submit);
171 :
172 0 : CDecalRData* data = (CDecalRData*)decal->GetRenderData();
173 0 : if (data == 0)
174 : {
175 : // no renderdata for decal, create it now
176 0 : data = new CDecalRData(decal, m->simulation);
177 0 : decal->SetRenderData(data);
178 : }
179 0 : data->Update(m->simulation);
180 :
181 0 : m->visibleDecals[cullGroup].push_back(data);
182 0 : }
183 :
184 : ///////////////////////////////////////////////////////////////////
185 : // Prepare for rendering
186 0 : void TerrainRenderer::PrepareForRendering()
187 : {
188 0 : ENSURE(m->phase == Phase_Submit);
189 :
190 0 : m->phase = Phase_Render;
191 0 : }
192 :
193 : ///////////////////////////////////////////////////////////////////
194 : // Clear submissions lists
195 0 : void TerrainRenderer::EndFrame()
196 : {
197 0 : ENSURE(m->phase == Phase_Render || m->phase == Phase_Submit);
198 :
199 0 : for (int i = 0; i < CSceneRenderer::CULL_MAX; ++i)
200 : {
201 0 : m->visiblePatches[i].clear();
202 0 : m->visibleDecals[i].clear();
203 : }
204 :
205 0 : m->phase = Phase_Submit;
206 0 : }
207 :
208 0 : void TerrainRenderer::RenderTerrainOverlayTexture(
209 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
210 : int cullGroup, const CVector2D& textureTransform,
211 : Renderer::Backend::ITexture* texture)
212 : {
213 0 : ENSURE(m->phase == Phase_Render);
214 :
215 0 : std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
216 :
217 : CShaderTechniquePtr debugOverlayTech =
218 0 : g_Renderer.GetShaderManager().LoadEffect(str_debug_overlay);
219 0 : deviceCommandContext->SetGraphicsPipelineState(
220 0 : debugOverlayTech->GetGraphicsPipelineState());
221 0 : deviceCommandContext->BeginPass();
222 0 : Renderer::Backend::IShaderProgram* debugOverlayShader = debugOverlayTech->GetShader();
223 :
224 0 : deviceCommandContext->SetTexture(
225 0 : debugOverlayShader->GetBindingSlot(str_baseTex), texture);
226 : const CMatrix3D transform =
227 0 : g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
228 0 : deviceCommandContext->SetUniform(
229 0 : debugOverlayShader->GetBindingSlot(str_transform), transform.AsFloatArray());
230 0 : deviceCommandContext->SetUniform(
231 0 : debugOverlayShader->GetBindingSlot(str_textureTransform), textureTransform.AsFloatArray());
232 0 : CPatchRData::RenderStreams(
233 0 : deviceCommandContext, m->streamWithPositionAsTexCoordVertexInputLayout, visiblePatches);
234 :
235 : // To make the overlay visible over water, render an additional map-sized
236 : // water-height patch.
237 0 : CBoundingBoxAligned waterBounds;
238 0 : for (CPatchRData* data : visiblePatches)
239 0 : waterBounds += data->GetWaterBounds();
240 0 : if (!waterBounds.IsEmpty())
241 : {
242 : // Add a delta to avoid z-fighting.
243 0 : const float height = g_Renderer.GetSceneRenderer().GetWaterManager().m_WaterHeight + 0.05f;
244 : const float waterPos[] =
245 : {
246 0 : waterBounds[0].X, height, waterBounds[0].Z,
247 0 : waterBounds[1].X, height, waterBounds[0].Z,
248 0 : waterBounds[1].X, height, waterBounds[1].Z,
249 0 : waterBounds[0].X, height, waterBounds[0].Z,
250 0 : waterBounds[1].X, height, waterBounds[1].Z,
251 0 : waterBounds[0].X, height, waterBounds[1].Z
252 0 : };
253 :
254 0 : deviceCommandContext->SetVertexInputLayout(m->overlayVertexInputLayout);
255 :
256 0 : deviceCommandContext->SetVertexBufferData(
257 0 : 0, waterPos, std::size(waterPos) * sizeof(waterPos[0]));
258 :
259 0 : deviceCommandContext->Draw(0, 6);
260 : }
261 :
262 0 : deviceCommandContext->EndPass();
263 0 : }
264 :
265 :
266 : ///////////////////////////////////////////////////////////////////
267 :
268 : /**
269 : * Set up all the uniforms for a shader pass.
270 : */
271 0 : void TerrainRenderer::PrepareShader(
272 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
273 : Renderer::Backend::IShaderProgram* shader, ShadowMap* shadow)
274 : {
275 0 : CSceneRenderer& sceneRenderer = g_Renderer.GetSceneRenderer();
276 :
277 0 : const CMatrix3D transform = sceneRenderer.GetViewCamera().GetViewProjection();
278 0 : deviceCommandContext->SetUniform(
279 0 : shader->GetBindingSlot(str_transform), transform.AsFloatArray());
280 0 : deviceCommandContext->SetUniform(
281 0 : shader->GetBindingSlot(str_cameraPos),
282 0 : sceneRenderer.GetViewCamera().GetOrientation().GetTranslation().AsFloatArray());
283 :
284 0 : const CLightEnv& lightEnv = sceneRenderer.GetLightEnv();
285 :
286 0 : if (shadow)
287 0 : shadow->BindTo(deviceCommandContext, shader);
288 :
289 0 : CLOSTexture& los = sceneRenderer.GetScene().GetLOSTexture();
290 0 : deviceCommandContext->SetTexture(
291 0 : shader->GetBindingSlot(str_losTex), los.GetTextureSmooth());
292 0 : deviceCommandContext->SetUniform(
293 0 : shader->GetBindingSlot(str_losTransform),
294 0 : los.GetTextureMatrix()[0], los.GetTextureMatrix()[12]);
295 :
296 0 : deviceCommandContext->SetUniform(
297 0 : shader->GetBindingSlot(str_ambient),
298 0 : lightEnv.m_AmbientColor.AsFloatArray());
299 0 : deviceCommandContext->SetUniform(
300 0 : shader->GetBindingSlot(str_sunColor),
301 0 : lightEnv.m_SunColor.AsFloatArray());
302 0 : deviceCommandContext->SetUniform(
303 0 : shader->GetBindingSlot(str_sunDir),
304 0 : lightEnv.GetSunDir().AsFloatArray());
305 :
306 0 : deviceCommandContext->SetUniform(
307 0 : shader->GetBindingSlot(str_fogColor),
308 0 : lightEnv.m_FogColor.AsFloatArray());
309 0 : deviceCommandContext->SetUniform(
310 0 : shader->GetBindingSlot(str_fogParams),
311 0 : lightEnv.m_FogFactor, lightEnv.m_FogMax);
312 0 : }
313 :
314 0 : void TerrainRenderer::RenderTerrainShader(
315 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
316 : const CShaderDefines& context, int cullGroup, ShadowMap* shadow)
317 : {
318 0 : ENSURE(m->phase == Phase_Render);
319 :
320 0 : std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
321 0 : std::vector<CDecalRData*>& visibleDecals = m->visibleDecals[cullGroup];
322 0 : if (visiblePatches.empty() && visibleDecals.empty())
323 0 : return;
324 :
325 0 : if (!m->shaderTechniqueSolid)
326 : {
327 0 : m->shaderTechniqueSolid = g_Renderer.GetShaderManager().LoadEffect(
328 : str_solid, {},
329 0 : [](Renderer::Backend::SGraphicsPipelineStateDesc& pipelineStateDesc)
330 : {
331 0 : pipelineStateDesc.rasterizationState.cullMode = Renderer::Backend::CullMode::NONE;
332 0 : });
333 : }
334 :
335 : // render the solid black sides of the map first
336 0 : deviceCommandContext->SetGraphicsPipelineState(
337 0 : m->shaderTechniqueSolid->GetGraphicsPipelineState());
338 0 : deviceCommandContext->BeginPass();
339 :
340 0 : Renderer::Backend::IShaderProgram* shaderSolid = m->shaderTechniqueSolid->GetShader();
341 : const CMatrix3D transform =
342 0 : g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
343 0 : deviceCommandContext->SetUniform(
344 0 : shaderSolid->GetBindingSlot(str_transform), transform.AsFloatArray());
345 0 : deviceCommandContext->SetUniform(
346 0 : shaderSolid->GetBindingSlot(str_color), 0.0f, 0.0f, 0.0f, 1.0f);
347 :
348 0 : CPatchRData::RenderSides(
349 0 : deviceCommandContext, m->sideVertexInputLayout, visiblePatches);
350 :
351 0 : deviceCommandContext->EndPass();
352 :
353 0 : CPatchRData::RenderBases(
354 0 : deviceCommandContext, m->baseVertexInputLayout, visiblePatches, context, shadow);
355 :
356 : // render blend passes for each patch
357 0 : CPatchRData::RenderBlends(
358 0 : deviceCommandContext, m->blendVertexInputLayout, visiblePatches, context, shadow);
359 :
360 0 : CDecalRData::RenderDecals(
361 0 : deviceCommandContext, m->decalsVertexInputLayout, visibleDecals, context, shadow);
362 : }
363 :
364 : ///////////////////////////////////////////////////////////////////
365 : // Render un-textured patches as polygons
366 0 : void TerrainRenderer::RenderPatches(
367 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
368 : int cullGroup, const CShaderDefines& defines, const CColor& color)
369 : {
370 0 : ENSURE(m->phase == Phase_Render);
371 :
372 0 : std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
373 0 : if (visiblePatches.empty())
374 0 : return;
375 :
376 0 : GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain patches");
377 :
378 0 : CShaderTechniquePtr solidTech = g_Renderer.GetShaderManager().LoadEffect(str_terrain_solid, defines);
379 0 : deviceCommandContext->SetGraphicsPipelineState(
380 0 : solidTech->GetGraphicsPipelineState());
381 0 : deviceCommandContext->BeginPass();
382 :
383 0 : Renderer::Backend::IShaderProgram* solidShader = solidTech->GetShader();
384 :
385 : const CMatrix3D transform =
386 0 : g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
387 0 : deviceCommandContext->SetUniform(
388 0 : solidShader->GetBindingSlot(str_transform), transform.AsFloatArray());
389 0 : deviceCommandContext->SetUniform(
390 0 : solidShader->GetBindingSlot(str_color), color.AsFloatArray());
391 :
392 0 : CPatchRData::RenderStreams(
393 0 : deviceCommandContext, m->streamVertexInputLayout, visiblePatches);
394 0 : deviceCommandContext->EndPass();
395 : }
396 :
397 :
398 : ///////////////////////////////////////////////////////////////////
399 : // Render outlines of submitted patches as lines
400 0 : void TerrainRenderer::RenderOutlines(
401 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
402 : int cullGroup)
403 : {
404 0 : ENSURE(m->phase == Phase_Render);
405 :
406 0 : std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
407 0 : if (visiblePatches.empty())
408 0 : return;
409 :
410 0 : GPU_SCOPED_LABEL(deviceCommandContext, "Render terrain outlines");
411 :
412 0 : for (size_t i = 0; i < visiblePatches.size(); ++i)
413 0 : visiblePatches[i]->RenderOutline();
414 : }
415 :
416 :
417 : ///////////////////////////////////////////////////////////////////
418 : // Scissor rectangle of water patches
419 0 : CBoundingBoxAligned TerrainRenderer::ScissorWater(int cullGroup, const CCamera& camera)
420 : {
421 0 : CBoundingBoxAligned scissor;
422 0 : for (const CPatchRData* data : m->visiblePatches[cullGroup])
423 : {
424 0 : const CBoundingBoxAligned& waterBounds = data->GetWaterBounds();
425 0 : if (waterBounds.IsEmpty())
426 0 : continue;
427 :
428 : const CBoundingBoxAligned waterBoundsInViewPort =
429 0 : camera.GetBoundsInViewPort(waterBounds);
430 0 : if (!waterBoundsInViewPort.IsEmpty())
431 0 : scissor += waterBoundsInViewPort;
432 : }
433 0 : if (scissor.IsEmpty())
434 0 : return scissor;
435 : return CBoundingBoxAligned(
436 0 : CVector3D(Clamp(scissor[0].X, -1.0f, 1.0f), Clamp(scissor[0].Y, -1.0f, 1.0f), -1.0f),
437 0 : CVector3D(Clamp(scissor[1].X, -1.0f, 1.0f), Clamp(scissor[1].Y, -1.0f, 1.0f), 1.0f));
438 : }
439 :
440 : // Render fancy water
441 0 : bool TerrainRenderer::RenderFancyWater(
442 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
443 : const CShaderDefines& context, int cullGroup, ShadowMap* shadow)
444 : {
445 0 : PROFILE3_GPU("fancy water");
446 0 : GPU_SCOPED_LABEL(deviceCommandContext, "Render fancy water");
447 :
448 0 : CSceneRenderer& sceneRenderer = g_Renderer.GetSceneRenderer();
449 :
450 0 : WaterManager& waterManager = sceneRenderer.GetWaterManager();
451 0 : CShaderDefines defines = context;
452 :
453 : // If we're using fancy water, make sure its shader is loaded
454 0 : if (!m->fancyWaterTech || waterManager.m_NeedsReloading)
455 : {
456 0 : if (waterManager.m_WaterRealDepth)
457 0 : defines.Add(str_USE_REAL_DEPTH, str_1);
458 0 : if (waterManager.m_WaterFancyEffects)
459 0 : defines.Add(str_USE_FANCY_EFFECTS, str_1);
460 0 : if (waterManager.m_WaterRefraction)
461 0 : defines.Add(str_USE_REFRACTION, str_1);
462 0 : if (waterManager.m_WaterReflection)
463 0 : defines.Add(str_USE_REFLECTION, str_1);
464 :
465 0 : m->fancyWaterTech = g_Renderer.GetShaderManager().LoadEffect(str_water_high, defines);
466 :
467 0 : if (!m->fancyWaterTech)
468 : {
469 0 : LOGERROR("Failed to load water shader. Falling back to a simple water.\n");
470 0 : waterManager.m_RenderWater = false;
471 0 : return false;
472 : }
473 0 : waterManager.m_NeedsReloading = false;
474 : }
475 :
476 0 : CLOSTexture& losTexture = sceneRenderer.GetScene().GetLOSTexture();
477 :
478 : // Calculating the advanced informations about Foam and all if the quality calls for it.
479 : /*if (WaterMgr->m_NeedInfoUpdate && (WaterMgr->m_WaterFoam || WaterMgr->m_WaterCoastalWaves))
480 : {
481 : WaterMgr->m_NeedInfoUpdate = false;
482 : WaterMgr->CreateSuperfancyInfo();
483 : }*/
484 :
485 0 : const double time = waterManager.m_WaterTexTimer;
486 0 : const float repeatPeriod = waterManager.m_RepeatPeriod;
487 :
488 0 : deviceCommandContext->SetGraphicsPipelineState(
489 0 : m->fancyWaterTech->GetGraphicsPipelineState());
490 0 : deviceCommandContext->BeginPass();
491 0 : Renderer::Backend::IShaderProgram* fancyWaterShader = m->fancyWaterTech->GetShader();
492 :
493 0 : const CCamera& camera = g_Renderer.GetSceneRenderer().GetViewCamera();
494 :
495 0 : const double period = 8.0;
496 : // TODO: move uploading to a prepare function during loading.
497 0 : const CTexturePtr& currentNormalTexture = waterManager.m_NormalMap[waterManager.GetCurrentTextureIndex(period)];
498 0 : const CTexturePtr& nextNormalTexture = waterManager.m_NormalMap[waterManager.GetNextTextureIndex(period)];
499 :
500 0 : currentNormalTexture->UploadBackendTextureIfNeeded(deviceCommandContext);
501 0 : nextNormalTexture->UploadBackendTextureIfNeeded(deviceCommandContext);
502 :
503 0 : deviceCommandContext->SetTexture(
504 0 : fancyWaterShader->GetBindingSlot(str_normalMap),
505 0 : currentNormalTexture->GetBackendTexture());
506 0 : deviceCommandContext->SetTexture(
507 0 : fancyWaterShader->GetBindingSlot(str_normalMap2),
508 0 : nextNormalTexture->GetBackendTexture());
509 :
510 0 : if (waterManager.m_WaterFancyEffects)
511 : {
512 0 : deviceCommandContext->SetTexture(
513 0 : fancyWaterShader->GetBindingSlot(str_waterEffectsTex),
514 0 : waterManager.m_FancyTexture.get());
515 : }
516 :
517 0 : if (waterManager.m_WaterRefraction && waterManager.m_WaterRealDepth)
518 : {
519 0 : deviceCommandContext->SetTexture(
520 0 : fancyWaterShader->GetBindingSlot(str_depthTex),
521 0 : waterManager.m_RefrFboDepthTexture.get());
522 0 : deviceCommandContext->SetUniform(
523 0 : fancyWaterShader->GetBindingSlot(str_projInvTransform),
524 0 : waterManager.m_RefractionProjInvMatrix.AsFloatArray());
525 0 : deviceCommandContext->SetUniform(
526 0 : fancyWaterShader->GetBindingSlot(str_viewInvTransform),
527 0 : waterManager.m_RefractionViewInvMatrix.AsFloatArray());
528 : }
529 :
530 0 : if (waterManager.m_WaterRefraction)
531 : {
532 0 : deviceCommandContext->SetTexture(
533 0 : fancyWaterShader->GetBindingSlot(str_refractionMap),
534 0 : waterManager.m_RefractionTexture.get());
535 : }
536 0 : if (waterManager.m_WaterReflection)
537 : {
538 0 : deviceCommandContext->SetTexture(
539 0 : fancyWaterShader->GetBindingSlot(str_reflectionMap),
540 0 : waterManager.m_ReflectionTexture.get());
541 : }
542 0 : deviceCommandContext->SetTexture(
543 0 : fancyWaterShader->GetBindingSlot(str_losTex), losTexture.GetTextureSmooth());
544 :
545 0 : const CLightEnv& lightEnv = sceneRenderer.GetLightEnv();
546 :
547 0 : const CMatrix3D transform = sceneRenderer.GetViewCamera().GetViewProjection();
548 0 : deviceCommandContext->SetUniform(
549 0 : fancyWaterShader->GetBindingSlot(str_transform), transform.AsFloatArray());
550 :
551 0 : deviceCommandContext->SetTexture(
552 0 : fancyWaterShader->GetBindingSlot(str_skyCube),
553 0 : sceneRenderer.GetSkyManager().GetSkyCube());
554 : // TODO: check that this rotates in the right direction.
555 0 : CMatrix3D skyBoxRotation;
556 0 : skyBoxRotation.SetIdentity();
557 0 : skyBoxRotation.RotateY(M_PI + lightEnv.GetRotation());
558 0 : deviceCommandContext->SetUniform(
559 0 : fancyWaterShader->GetBindingSlot(str_skyBoxRot),
560 0 : skyBoxRotation.AsFloatArray());
561 :
562 0 : if (waterManager.m_WaterRefraction)
563 : {
564 0 : deviceCommandContext->SetUniform(
565 0 : fancyWaterShader->GetBindingSlot(str_refractionMatrix),
566 0 : waterManager.m_RefractionMatrix.AsFloatArray());
567 : }
568 0 : if (waterManager.m_WaterReflection)
569 : {
570 0 : deviceCommandContext->SetUniform(
571 0 : fancyWaterShader->GetBindingSlot(str_reflectionMatrix),
572 0 : waterManager.m_ReflectionMatrix.AsFloatArray());
573 : }
574 :
575 0 : deviceCommandContext->SetUniform(
576 0 : fancyWaterShader->GetBindingSlot(str_ambient), lightEnv.m_AmbientColor.AsFloatArray());
577 0 : deviceCommandContext->SetUniform(
578 0 : fancyWaterShader->GetBindingSlot(str_sunDir), lightEnv.GetSunDir().AsFloatArray());
579 0 : deviceCommandContext->SetUniform(
580 0 : fancyWaterShader->GetBindingSlot(str_sunColor), lightEnv.m_SunColor.AsFloatArray());
581 0 : deviceCommandContext->SetUniform(
582 0 : fancyWaterShader->GetBindingSlot(str_color), waterManager.m_WaterColor.AsFloatArray());
583 0 : deviceCommandContext->SetUniform(
584 0 : fancyWaterShader->GetBindingSlot(str_tint), waterManager.m_WaterTint.AsFloatArray());
585 0 : deviceCommandContext->SetUniform(
586 0 : fancyWaterShader->GetBindingSlot(str_waviness), waterManager.m_Waviness);
587 0 : deviceCommandContext->SetUniform(
588 0 : fancyWaterShader->GetBindingSlot(str_murkiness), waterManager.m_Murkiness);
589 0 : deviceCommandContext->SetUniform(
590 0 : fancyWaterShader->GetBindingSlot(str_windAngle), waterManager.m_WindAngle);
591 0 : deviceCommandContext->SetUniform(
592 0 : fancyWaterShader->GetBindingSlot(str_repeatScale), 1.0f / repeatPeriod);
593 :
594 0 : deviceCommandContext->SetUniform(
595 0 : fancyWaterShader->GetBindingSlot(str_losTransform),
596 0 : losTexture.GetTextureMatrix()[0], losTexture.GetTextureMatrix()[12]);
597 0 : deviceCommandContext->SetUniform(
598 0 : fancyWaterShader->GetBindingSlot(str_cameraPos),
599 0 : camera.GetOrientation().GetTranslation().AsFloatArray());
600 :
601 0 : deviceCommandContext->SetUniform(
602 0 : fancyWaterShader->GetBindingSlot(str_fogColor),
603 0 : lightEnv.m_FogColor.AsFloatArray());
604 0 : deviceCommandContext->SetUniform(
605 0 : fancyWaterShader->GetBindingSlot(str_fogParams),
606 0 : lightEnv.m_FogFactor, lightEnv.m_FogMax);
607 0 : deviceCommandContext->SetUniform(
608 0 : fancyWaterShader->GetBindingSlot(str_time), static_cast<float>(time));
609 0 : deviceCommandContext->SetUniform(
610 0 : fancyWaterShader->GetBindingSlot(str_screenSize),
611 0 : static_cast<float>(g_Renderer.GetWidth()),
612 0 : static_cast<float>(g_Renderer.GetHeight()));
613 :
614 0 : if (waterManager.m_WaterType == L"clap")
615 : {
616 0 : deviceCommandContext->SetUniform(
617 0 : fancyWaterShader->GetBindingSlot(str_waveParams1),
618 0 : 30.0f, 1.5f, 20.0f, 0.03f);
619 0 : deviceCommandContext->SetUniform(
620 0 : fancyWaterShader->GetBindingSlot(str_waveParams2),
621 0 : 0.5f, 0.0f, 0.0f, 0.0f);
622 : }
623 0 : else if (waterManager.m_WaterType == L"lake")
624 : {
625 0 : deviceCommandContext->SetUniform(
626 0 : fancyWaterShader->GetBindingSlot(str_waveParams1),
627 0 : 8.5f, 1.5f, 15.0f, 0.03f);
628 0 : deviceCommandContext->SetUniform(
629 0 : fancyWaterShader->GetBindingSlot(str_waveParams2),
630 0 : 0.2f, 0.0f, 0.0f, 0.07f);
631 : }
632 : else
633 : {
634 0 : deviceCommandContext->SetUniform(
635 0 : fancyWaterShader->GetBindingSlot(str_waveParams1),
636 0 : 15.0f, 0.8f, 10.0f, 0.1f);
637 0 : deviceCommandContext->SetUniform(
638 0 : fancyWaterShader->GetBindingSlot(str_waveParams2),
639 0 : 0.3f, 0.0f, 0.1f, 0.3f);
640 : }
641 :
642 0 : if (shadow)
643 0 : shadow->BindTo(deviceCommandContext, fancyWaterShader);
644 :
645 0 : for (CPatchRData* data : m->visiblePatches[cullGroup])
646 : {
647 0 : data->RenderWaterSurface(
648 0 : deviceCommandContext, m->waterSurfaceWithDataVertexInputLayout);
649 0 : if (waterManager.m_WaterFancyEffects)
650 0 : data->RenderWaterShore(deviceCommandContext, m->waterShoreVertexInputLayout);
651 : }
652 0 : deviceCommandContext->EndPass();
653 :
654 0 : return true;
655 : }
656 :
657 0 : void TerrainRenderer::RenderSimpleWater(
658 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
659 : int cullGroup)
660 : {
661 0 : PROFILE3_GPU("simple water");
662 0 : GPU_SCOPED_LABEL(deviceCommandContext, "Render Simple Water");
663 :
664 0 : const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager();
665 0 : CLOSTexture& losTexture = g_Renderer.GetSceneRenderer().GetScene().GetLOSTexture();
666 :
667 0 : const double time = waterManager.m_WaterTexTimer;
668 :
669 0 : CShaderDefines context;
670 0 : if (g_Renderer.GetSceneRenderer().GetWaterRenderMode() == WIREFRAME)
671 0 : context.Add(str_MODE_WIREFRAME, str_1);
672 :
673 : CShaderTechniquePtr waterSimpleTech =
674 0 : g_Renderer.GetShaderManager().LoadEffect(str_water_simple, context);
675 0 : deviceCommandContext->SetGraphicsPipelineState(
676 0 : waterSimpleTech->GetGraphicsPipelineState());
677 0 : deviceCommandContext->BeginPass();
678 0 : Renderer::Backend::IShaderProgram* waterSimpleShader = waterSimpleTech->GetShader();
679 :
680 0 : const CTexturePtr& waterTexture = waterManager.m_WaterTexture[waterManager.GetCurrentTextureIndex(1.6)];
681 0 : waterTexture->UploadBackendTextureIfNeeded(deviceCommandContext);
682 :
683 0 : deviceCommandContext->SetTexture(
684 0 : waterSimpleShader->GetBindingSlot(str_baseTex), waterTexture->GetBackendTexture());
685 0 : deviceCommandContext->SetTexture(
686 0 : waterSimpleShader->GetBindingSlot(str_losTex), losTexture.GetTextureSmooth());
687 :
688 : const CMatrix3D transform =
689 0 : g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
690 0 : deviceCommandContext->SetUniform(
691 0 : waterSimpleShader->GetBindingSlot(str_transform), transform.AsFloatArray());
692 :
693 0 : deviceCommandContext->SetUniform(
694 0 : waterSimpleShader->GetBindingSlot(str_losTransform),
695 0 : losTexture.GetTextureMatrix()[0], losTexture.GetTextureMatrix()[12]);
696 0 : deviceCommandContext->SetUniform(
697 0 : waterSimpleShader->GetBindingSlot(str_time), static_cast<float>(time));
698 0 : deviceCommandContext->SetUniform(
699 0 : waterSimpleShader->GetBindingSlot(str_color), waterManager.m_WaterColor.AsFloatArray());
700 :
701 0 : for (CPatchRData* data : m->visiblePatches[cullGroup])
702 : {
703 0 : data->RenderWaterSurface(
704 0 : deviceCommandContext, m->waterSurfaceVertexInputLayout);
705 : }
706 :
707 0 : deviceCommandContext->EndPass();
708 0 : }
709 :
710 : ///////////////////////////////////////////////////////////////////
711 : // Render water that is part of the terrain
712 0 : void TerrainRenderer::RenderWater(
713 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
714 : const CShaderDefines& context, int cullGroup, ShadowMap* shadow)
715 : {
716 0 : const WaterManager& waterManager = g_Renderer.GetSceneRenderer().GetWaterManager();
717 :
718 0 : if (!waterManager.WillRenderFancyWater())
719 0 : RenderSimpleWater(deviceCommandContext, cullGroup);
720 : else
721 0 : RenderFancyWater(deviceCommandContext, context, cullGroup, shadow);
722 0 : }
723 :
724 0 : void TerrainRenderer::RenderWaterFoamOccluders(
725 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext,
726 : int cullGroup)
727 : {
728 0 : CSceneRenderer& sceneRenderer = g_Renderer.GetSceneRenderer();
729 0 : const WaterManager& waterManager = sceneRenderer.GetWaterManager();
730 0 : if (!waterManager.WillRenderFancyWater())
731 0 : return;
732 :
733 0 : if (!m->shaderTechniqueSolidDepthTest)
734 : {
735 0 : m->shaderTechniqueSolidDepthTest = g_Renderer.GetShaderManager().LoadEffect(
736 : str_solid, {},
737 0 : [](Renderer::Backend::SGraphicsPipelineStateDesc& pipelineStateDesc)
738 : {
739 0 : pipelineStateDesc.depthStencilState.depthTestEnabled = true;
740 0 : pipelineStateDesc.rasterizationState.cullMode = Renderer::Backend::CullMode::NONE;
741 0 : });
742 : }
743 :
744 0 : GPU_SCOPED_LABEL(deviceCommandContext, "Render water foam occluders");
745 :
746 : Renderer::Backend::IFramebuffer* framebuffer =
747 0 : waterManager.m_FancyEffectsOccludersFramebuffer.get();
748 0 : deviceCommandContext->BeginFramebufferPass(framebuffer);
749 0 : Renderer::Backend::IDeviceCommandContext::Rect viewportRect{};
750 0 : viewportRect.width = framebuffer->GetWidth();
751 0 : viewportRect.height = framebuffer->GetHeight();
752 0 : deviceCommandContext->SetViewports(1, &viewportRect);
753 :
754 : // Overwrite waves that would be behind the ground.
755 0 : deviceCommandContext->SetGraphicsPipelineState(
756 0 : m->shaderTechniqueSolidDepthTest->GetGraphicsPipelineState());
757 0 : deviceCommandContext->BeginPass();
758 :
759 0 : Renderer::Backend::IShaderProgram* dummyShader = m->shaderTechniqueSolidDepthTest->GetShader();
760 :
761 0 : const CMatrix3D transform = sceneRenderer.GetViewCamera().GetViewProjection();
762 0 : deviceCommandContext->SetUniform(
763 0 : dummyShader->GetBindingSlot(str_transform), transform.AsFloatArray());
764 0 : deviceCommandContext->SetUniform(
765 0 : dummyShader->GetBindingSlot(str_color), 0.0f, 0.0f, 0.0f, 0.0f);
766 :
767 0 : for (CPatchRData* data : m->visiblePatches[cullGroup])
768 0 : data->RenderWaterShore(deviceCommandContext, m->waterShoreVertexInputLayout);
769 :
770 0 : deviceCommandContext->EndPass();
771 :
772 0 : deviceCommandContext->EndFramebufferPass();
773 : }
774 :
775 0 : void TerrainRenderer::RenderPriorities(CCanvas2D& canvas, int cullGroup)
776 : {
777 0 : PROFILE("priorities");
778 :
779 0 : ENSURE(m->phase == Phase_Render);
780 :
781 0 : CTextRenderer textRenderer;
782 0 : textRenderer.SetCurrentFont(CStrIntern("mono-stroke-10"));
783 0 : textRenderer.SetCurrentColor(CColor(1.0f, 1.0f, 0.0f, 1.0f));
784 :
785 0 : std::vector<CPatchRData*>& visiblePatches = m->visiblePatches[cullGroup];
786 0 : for (size_t i = 0; i < visiblePatches.size(); ++i)
787 0 : visiblePatches[i]->RenderPriorities(textRenderer);
788 :
789 0 : canvas.DrawText(textRenderer);
790 3 : }
|