1 /* Copyright (C) 2023 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
18 #include "precompiled.h"
20 #include "renderer/DebugRenderer.h"
22 #include "graphics/Camera.h"
23 #include "graphics/Color.h"
24 #include "graphics/ShaderManager.h"
25 #include "graphics/ShaderProgram.h"
27 #include "maths/BoundingBoxAligned.h"
28 #include "maths/Brush.h"
29 #include "maths/Matrix3D.h"
30 #include "maths/Vector3D.h"
31 #include "ps/CStrInternStatic.h"
32 #include "renderer/backend/IDeviceCommandContext.h"
33 #include "renderer/Renderer.h"
34 #include "renderer/SceneRenderer.h"
38 void CDebugRenderer::Initialize()
40 const std::array
<Renderer::Backend::SVertexAttributeFormat
, 1> attributes
{{
41 {Renderer::Backend::VertexAttributeStream::POSITION
,
42 Renderer::Backend::Format::R32G32B32_SFLOAT
, 0, sizeof(float) * 3,
43 Renderer::Backend::VertexAttributeRate::PER_VERTEX
, 0}
45 m_VertexInputLayout
= g_Renderer
.GetVertexInputLayout(attributes
);
48 void CDebugRenderer::DrawLine(
49 const CVector3D
& from
, const CVector3D
& to
, const CColor
& color
,
50 const float width
, const bool depthTestEnabled
)
55 DrawLine({from
, to
}, color
, width
, depthTestEnabled
);
58 void CDebugRenderer::DrawLine(
59 const std::vector
<CVector3D
>& line
, const CColor
& color
,
60 const float width
, const bool depthTestEnabled
)
65 Renderer::Backend::IDeviceCommandContext
* deviceCommandContext
=
66 g_Renderer
.GetDeviceCommandContext();
68 CShaderTechniquePtr debugLineTech
=
69 GetShaderTechnique(str_debug_line
, color
, depthTestEnabled
);
70 deviceCommandContext
->SetGraphicsPipelineState(
71 debugLineTech
->GetGraphicsPipelineState());
72 deviceCommandContext
->BeginPass();
74 const CCamera
& viewCamera
= g_Renderer
.GetSceneRenderer().GetViewCamera();
76 Renderer::Backend::IShaderProgram
* debugLineShader
= debugLineTech
->GetShader();
77 const CMatrix3D transform
= viewCamera
.GetViewProjection();
78 deviceCommandContext
->SetUniform(
79 debugLineShader
->GetBindingSlot(str_transform
), transform
.AsFloatArray());
80 deviceCommandContext
->SetUniform(
81 debugLineShader
->GetBindingSlot(str_color
), color
.AsFloatArray());
83 const CVector3D cameraIn
= viewCamera
.GetOrientation().GetIn();
85 std::vector
<float> vertices
;
86 vertices
.reserve(line
.size() * 6 * 3);
87 #define ADD(position) \
88 vertices.emplace_back((position).X); \
89 vertices.emplace_back((position).Y); \
90 vertices.emplace_back((position).Z);
92 for (size_t idx
= 1; idx
< line
.size(); ++idx
)
94 const CVector3D from
= line
[idx
- 1];
95 const CVector3D to
= line
[idx
];
96 const CVector3D direction
= (to
- from
).Normalized();
97 const CVector3D view
= direction
.Dot(cameraIn
) > 0.9f
?
98 CVector3D(0.0f
, 1.0f
, 0.0f
) :
100 const CVector3D offset
= view
.Cross(direction
).Normalized() * width
;
112 deviceCommandContext
->SetVertexInputLayout(m_VertexInputLayout
);
113 deviceCommandContext
->SetVertexBufferData(
114 0, vertices
.data(), vertices
.size() * sizeof(vertices
[0]));
116 deviceCommandContext
->Draw(0, vertices
.size() / 3);
118 deviceCommandContext
->EndPass();
121 void CDebugRenderer::DrawCircle(const CVector3D
& origin
, const float radius
, const CColor
& color
)
123 CShaderTechniquePtr debugCircleTech
=
124 GetShaderTechnique(str_debug_line
, color
);
126 Renderer::Backend::IDeviceCommandContext
* deviceCommandContext
=
127 g_Renderer
.GetDeviceCommandContext();
129 deviceCommandContext
->SetGraphicsPipelineState(
130 debugCircleTech
->GetGraphicsPipelineState());
131 deviceCommandContext
->BeginPass();
133 const CCamera
& camera
= g_Renderer
.GetSceneRenderer().GetViewCamera();
135 Renderer::Backend::IShaderProgram
* debugCircleShader
= debugCircleTech
->GetShader();
137 const CMatrix3D transform
= camera
.GetViewProjection();
138 deviceCommandContext
->SetUniform(
139 debugCircleShader
->GetBindingSlot(str_transform
), transform
.AsFloatArray());
140 deviceCommandContext
->SetUniform(
141 debugCircleShader
->GetBindingSlot(str_color
), color
.AsFloatArray());
143 const CVector3D cameraUp
= camera
.GetOrientation().GetUp();
144 const CVector3D cameraLeft
= camera
.GetOrientation().GetLeft();
146 std::vector
<float> vertices
;
147 #define ADD(position) \
148 vertices.emplace_back((position).X); \
149 vertices.emplace_back((position).Y); \
150 vertices.emplace_back((position).Z);
152 constexpr size_t segments
= 16;
153 for (size_t idx
= 0; idx
<= segments
; ++idx
)
155 const float angle
= M_PI
* 2.0f
* idx
/ segments
;
156 const CVector3D offset
= cameraUp
* sin(angle
) - cameraLeft
* cos(angle
);
157 const float nextAngle
= M_PI
* 2.0f
* (idx
+ 1) / segments
;
158 const CVector3D nextOffset
= cameraUp
* sin(nextAngle
) - cameraLeft
* cos(nextAngle
);
160 ADD(origin
+ offset
* radius
)
161 ADD(origin
+ nextOffset
* radius
)
166 deviceCommandContext
->SetVertexInputLayout(m_VertexInputLayout
);
167 deviceCommandContext
->SetVertexBufferData(
168 0, vertices
.data(), vertices
.size() * sizeof(vertices
[0]));
170 deviceCommandContext
->Draw(0, vertices
.size() / 3);
172 deviceCommandContext
->EndPass();
175 void CDebugRenderer::DrawCameraFrustum(const CCamera
& camera
, const CColor
& color
, int intermediates
, bool wireframe
)
177 CCamera::Quad nearPoints
;
178 CCamera::Quad farPoints
;
180 camera
.GetViewQuad(camera
.GetNearPlane(), nearPoints
);
181 camera
.GetViewQuad(camera
.GetFarPlane(), farPoints
);
182 for (int i
= 0; i
< 4; ++i
)
184 nearPoints
[i
] = camera
.m_Orientation
.Transform(nearPoints
[i
]);
185 farPoints
[i
] = camera
.m_Orientation
.Transform(farPoints
[i
]);
188 CShaderTechniquePtr overlayTech
=
189 GetShaderTechnique(str_debug_line
, color
, true, wireframe
);
191 Renderer::Backend::IDeviceCommandContext
* deviceCommandContext
=
192 g_Renderer
.GetDeviceCommandContext();
193 deviceCommandContext
->SetGraphicsPipelineState(
194 overlayTech
->GetGraphicsPipelineState());
195 deviceCommandContext
->BeginPass();
197 Renderer::Backend::IShaderProgram
* overlayShader
= overlayTech
->GetShader();
199 const CMatrix3D transform
= g_Renderer
.GetSceneRenderer().GetViewCamera().GetViewProjection();
200 deviceCommandContext
->SetUniform(
201 overlayShader
->GetBindingSlot(str_transform
), transform
.AsFloatArray());
202 deviceCommandContext
->SetUniform(
203 overlayShader
->GetBindingSlot(str_color
), color
.AsFloatArray());
205 std::vector
<float> vertices
;
206 #define ADD(position) \
207 vertices.emplace_back((position).X); \
208 vertices.emplace_back((position).Y); \
209 vertices.emplace_back((position).Z);
227 // Intermediate planes.
228 CVector3D intermediatePoints
[4];
229 for (int i
= 0; i
< intermediates
; ++i
)
231 const float t
= (i
+ 1.0f
) / (intermediates
+ 1.0f
);
233 for (int j
= 0; j
< 4; ++j
)
234 intermediatePoints
[j
] = nearPoints
[j
] * t
+ farPoints
[j
] * (1.0f
- t
);
236 ADD(intermediatePoints
[0]);
237 ADD(intermediatePoints
[1]);
238 ADD(intermediatePoints
[2]);
239 ADD(intermediatePoints
[0]);
240 ADD(intermediatePoints
[2]);
241 ADD(intermediatePoints
[3]);
244 deviceCommandContext
->SetVertexInputLayout(m_VertexInputLayout
);
245 deviceCommandContext
->SetVertexBufferData(
246 0, vertices
.data(), vertices
.size() * sizeof(vertices
[0]));
248 deviceCommandContext
->Draw(0, vertices
.size() / 3);
253 for (int i
= 0; i
< 4; ++i
)
255 const int nextI
= (i
+ 1) % 4;
257 ADD(farPoints
[nextI
]);
260 ADD(nearPoints
[nextI
]);
261 ADD(farPoints
[nextI
]);
264 deviceCommandContext
->SetVertexInputLayout(m_VertexInputLayout
);
265 deviceCommandContext
->SetVertexBufferData(
266 0, vertices
.data(), vertices
.size() * sizeof(vertices
[0]));
268 deviceCommandContext
->Draw(0, vertices
.size() / 3);
271 deviceCommandContext
->EndPass();
274 void CDebugRenderer::DrawBoundingBox(
275 const CBoundingBoxAligned
& boundingBox
, const CColor
& color
,
280 g_Renderer
.GetSceneRenderer().GetViewCamera().GetViewProjection(), wireframe
);
283 void CDebugRenderer::DrawBoundingBox(
284 const CBoundingBoxAligned
& boundingBox
, const CColor
& color
,
285 const CMatrix3D
& transform
, bool wireframe
)
287 CShaderTechniquePtr shaderTech
=
288 GetShaderTechnique(str_debug_line
, color
, true, wireframe
);
290 Renderer::Backend::IDeviceCommandContext
* deviceCommandContext
=
291 g_Renderer
.GetDeviceCommandContext();
292 deviceCommandContext
->SetGraphicsPipelineState(
293 shaderTech
->GetGraphicsPipelineState());
294 deviceCommandContext
->BeginPass();
296 Renderer::Backend::IShaderProgram
* shader
= shaderTech
->GetShader();
298 deviceCommandContext
->SetUniform(
299 shader
->GetBindingSlot(str_transform
), transform
.AsFloatArray());
300 deviceCommandContext
->SetUniform(
301 shader
->GetBindingSlot(str_color
), color
.AsFloatArray());
303 std::vector
<float> data
;
305 #define ADD_FACE(x, y, z) \
306 ADD_PT(0, 0, x, y, z); ADD_PT(1, 0, x, y, z); ADD_PT(1, 1, x, y, z); \
307 ADD_PT(1, 1, x, y, z); ADD_PT(0, 1, x, y, z); ADD_PT(0, 0, x, y, z);
308 #define ADD_PT(u_, v_, x, y, z) \
309 STMT(int u = u_; int v = v_; \
310 data.push_back(boundingBox[x].X); \
311 data.push_back(boundingBox[y].Y); \
312 data.push_back(boundingBox[z].Z); \
324 deviceCommandContext
->SetVertexInputLayout(m_VertexInputLayout
);
325 deviceCommandContext
->SetVertexBufferData(
326 0, data
.data(), data
.size() * sizeof(data
[0]));
328 deviceCommandContext
->Draw(0, 6 * 6);
330 deviceCommandContext
->EndPass();
333 void CDebugRenderer::DrawBrush(const CBrush
& brush
, const CColor
& color
, bool wireframe
)
335 CShaderTechniquePtr shaderTech
=
336 GetShaderTechnique(str_debug_line
, color
, true, wireframe
);
338 Renderer::Backend::IDeviceCommandContext
* deviceCommandContext
=
339 g_Renderer
.GetDeviceCommandContext();
340 deviceCommandContext
->SetGraphicsPipelineState(
341 shaderTech
->GetGraphicsPipelineState());
342 deviceCommandContext
->BeginPass();
344 Renderer::Backend::IShaderProgram
* shader
= shaderTech
->GetShader();
346 const CMatrix3D transform
= g_Renderer
.GetSceneRenderer().GetViewCamera().GetViewProjection();
347 deviceCommandContext
->SetUniform(
348 shader
->GetBindingSlot(str_transform
), transform
.AsFloatArray());
349 deviceCommandContext
->SetUniform(
350 shader
->GetBindingSlot(str_color
), color
.AsFloatArray());
352 std::vector
<float> data
;
354 std::vector
<std::vector
<size_t>> faces
;
355 brush
.GetFaces(faces
);
357 #define ADD_VERT(a) \
359 data.push_back(brush.GetVertices()[faces[i][a]].X); \
360 data.push_back(brush.GetVertices()[faces[i][a]].Y); \
361 data.push_back(brush.GetVertices()[faces[i][a]].Z); \
364 for (size_t i
= 0; i
< faces
.size(); ++i
)
366 // Triangulate into (0,1,2), (0,2,3), ...
367 for (size_t j
= 1; j
< faces
[i
].size() - 2; ++j
)
377 deviceCommandContext
->SetVertexInputLayout(m_VertexInputLayout
);
378 deviceCommandContext
->SetVertexBufferData(
379 0, data
.data(), data
.size() * sizeof(data
[0]));
381 deviceCommandContext
->Draw(0, data
.size() / 5);
383 deviceCommandContext
->EndPass();
386 size_t CDebugRenderer::ShaderTechniqueKeyHash::operator()(
387 const ShaderTechniqueKey
& key
) const
390 hash_combine(seed
, key
.name
.GetHash());
391 hash_combine(seed
, key
.transparent
);
392 hash_combine(seed
, key
.depthTestEnabled
);
393 hash_combine(seed
, key
.wireframe
);
397 bool CDebugRenderer::ShaderTechniqueKeyEqual::operator()(
398 const ShaderTechniqueKey
& lhs
, const ShaderTechniqueKey
& rhs
) const
401 lhs
.name
== rhs
.name
&& lhs
.transparent
== rhs
.transparent
&&
402 lhs
.depthTestEnabled
== rhs
.depthTestEnabled
&&
403 lhs
.wireframe
== rhs
.wireframe
;
406 const CShaderTechniquePtr
& CDebugRenderer::GetShaderTechnique(
407 const CStrIntern name
, const CColor
& color
, const bool depthTestEnabled
,
408 const bool wireframe
)
410 const ShaderTechniqueKey key
{
411 name
, color
.a
!= 1.0f
, depthTestEnabled
, wireframe
};
412 CShaderTechniquePtr
& shaderTechnique
= m_ShaderTechniqueMapping
[key
];
414 return shaderTechnique
;
416 shaderTechnique
= g_Renderer
.GetShaderManager().LoadEffect(
418 [key
](Renderer::Backend::SGraphicsPipelineStateDesc
& pipelineStateDesc
)
420 pipelineStateDesc
.depthStencilState
.depthTestEnabled
= key
.depthTestEnabled
;
423 pipelineStateDesc
.blendState
.enabled
= true;
424 pipelineStateDesc
.blendState
.srcColorBlendFactor
= pipelineStateDesc
.blendState
.srcAlphaBlendFactor
=
425 Renderer::Backend::BlendFactor::SRC_ALPHA
;
426 pipelineStateDesc
.blendState
.dstColorBlendFactor
= pipelineStateDesc
.blendState
.dstAlphaBlendFactor
=
427 Renderer::Backend::BlendFactor::ONE_MINUS_SRC_ALPHA
;
428 pipelineStateDesc
.blendState
.colorBlendOp
= pipelineStateDesc
.blendState
.alphaBlendOp
=
429 Renderer::Backend::BlendOp::ADD
;
432 pipelineStateDesc
.blendState
.enabled
= false;
434 pipelineStateDesc
.rasterizationState
.polygonMode
= Renderer::Backend::PolygonMode::LINE
;
435 pipelineStateDesc
.rasterizationState
.cullMode
= Renderer::Backend::CullMode::NONE
;
437 return shaderTechnique
;