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/DebugRenderer.h"
21 :
22 : #include "graphics/Camera.h"
23 : #include "graphics/Color.h"
24 : #include "graphics/ShaderManager.h"
25 : #include "graphics/ShaderProgram.h"
26 : #include "lib/hash.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"
35 :
36 : #include <cmath>
37 :
38 6 : void CDebugRenderer::Initialize()
39 : {
40 6 : 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}
44 : }};
45 6 : m_VertexInputLayout = g_Renderer.GetVertexInputLayout(attributes);
46 6 : }
47 :
48 0 : void CDebugRenderer::DrawLine(
49 : const CVector3D& from, const CVector3D& to, const CColor& color,
50 : const float width, const bool depthTestEnabled)
51 : {
52 0 : if (from == to)
53 0 : return;
54 :
55 0 : DrawLine({from, to}, color, width, depthTestEnabled);
56 : }
57 :
58 0 : void CDebugRenderer::DrawLine(
59 : const std::vector<CVector3D>& line, const CColor& color,
60 : const float width, const bool depthTestEnabled)
61 : {
62 0 : if (line.size() <= 1)
63 0 : return;
64 :
65 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext =
66 0 : g_Renderer.GetDeviceCommandContext();
67 :
68 : CShaderTechniquePtr debugLineTech =
69 0 : GetShaderTechnique(str_debug_line, color, depthTestEnabled);
70 0 : deviceCommandContext->SetGraphicsPipelineState(
71 0 : debugLineTech->GetGraphicsPipelineState());
72 0 : deviceCommandContext->BeginPass();
73 :
74 0 : const CCamera& viewCamera = g_Renderer.GetSceneRenderer().GetViewCamera();
75 :
76 0 : Renderer::Backend::IShaderProgram* debugLineShader = debugLineTech->GetShader();
77 0 : const CMatrix3D transform = viewCamera.GetViewProjection();
78 0 : deviceCommandContext->SetUniform(
79 0 : debugLineShader->GetBindingSlot(str_transform), transform.AsFloatArray());
80 0 : deviceCommandContext->SetUniform(
81 0 : debugLineShader->GetBindingSlot(str_color), color.AsFloatArray());
82 :
83 0 : const CVector3D cameraIn = viewCamera.GetOrientation().GetIn();
84 :
85 0 : std::vector<float> vertices;
86 0 : 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);
91 :
92 0 : for (size_t idx = 1; idx < line.size(); ++idx)
93 : {
94 0 : const CVector3D from = line[idx - 1];
95 0 : const CVector3D to = line[idx];
96 0 : const CVector3D direction = (to - from).Normalized();
97 0 : const CVector3D view = direction.Dot(cameraIn) > 0.9f ?
98 : CVector3D(0.0f, 1.0f, 0.0f) :
99 0 : cameraIn;
100 0 : const CVector3D offset = view.Cross(direction).Normalized() * width;
101 :
102 0 : ADD(from + offset)
103 0 : ADD(to - offset)
104 0 : ADD(to + offset)
105 0 : ADD(from + offset)
106 0 : ADD(from - offset)
107 0 : ADD(to - offset)
108 : }
109 :
110 : #undef ADD
111 :
112 0 : deviceCommandContext->SetVertexInputLayout(m_VertexInputLayout);
113 0 : deviceCommandContext->SetVertexBufferData(
114 0 : 0, vertices.data(), vertices.size() * sizeof(vertices[0]));
115 :
116 0 : deviceCommandContext->Draw(0, vertices.size() / 3);
117 :
118 0 : deviceCommandContext->EndPass();
119 : }
120 :
121 0 : void CDebugRenderer::DrawCircle(const CVector3D& origin, const float radius, const CColor& color)
122 : {
123 : CShaderTechniquePtr debugCircleTech =
124 0 : GetShaderTechnique(str_debug_line, color);
125 :
126 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext =
127 0 : g_Renderer.GetDeviceCommandContext();
128 :
129 0 : deviceCommandContext->SetGraphicsPipelineState(
130 0 : debugCircleTech->GetGraphicsPipelineState());
131 0 : deviceCommandContext->BeginPass();
132 :
133 0 : const CCamera& camera = g_Renderer.GetSceneRenderer().GetViewCamera();
134 :
135 0 : Renderer::Backend::IShaderProgram* debugCircleShader = debugCircleTech->GetShader();
136 :
137 0 : const CMatrix3D transform = camera.GetViewProjection();
138 0 : deviceCommandContext->SetUniform(
139 0 : debugCircleShader->GetBindingSlot(str_transform), transform.AsFloatArray());
140 0 : deviceCommandContext->SetUniform(
141 0 : debugCircleShader->GetBindingSlot(str_color), color.AsFloatArray());
142 :
143 0 : const CVector3D cameraUp = camera.GetOrientation().GetUp();
144 0 : const CVector3D cameraLeft = camera.GetOrientation().GetLeft();
145 :
146 0 : 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);
151 :
152 0 : constexpr size_t segments = 16;
153 0 : for (size_t idx = 0; idx <= segments; ++idx)
154 : {
155 0 : const float angle = M_PI * 2.0f * idx / segments;
156 0 : const CVector3D offset = cameraUp * sin(angle) - cameraLeft * cos(angle);
157 0 : const float nextAngle = M_PI * 2.0f * (idx + 1) / segments;
158 0 : const CVector3D nextOffset = cameraUp * sin(nextAngle) - cameraLeft * cos(nextAngle);
159 0 : ADD(origin)
160 0 : ADD(origin + offset * radius)
161 0 : ADD(origin + nextOffset * radius)
162 : }
163 :
164 : #undef ADD
165 :
166 0 : deviceCommandContext->SetVertexInputLayout(m_VertexInputLayout);
167 0 : deviceCommandContext->SetVertexBufferData(
168 0 : 0, vertices.data(), vertices.size() * sizeof(vertices[0]));
169 :
170 0 : deviceCommandContext->Draw(0, vertices.size() / 3);
171 :
172 0 : deviceCommandContext->EndPass();
173 0 : }
174 :
175 0 : void CDebugRenderer::DrawCameraFrustum(const CCamera& camera, const CColor& color, int intermediates, bool wireframe)
176 : {
177 0 : CCamera::Quad nearPoints;
178 0 : CCamera::Quad farPoints;
179 :
180 0 : camera.GetViewQuad(camera.GetNearPlane(), nearPoints);
181 0 : camera.GetViewQuad(camera.GetFarPlane(), farPoints);
182 0 : for (int i = 0; i < 4; ++i)
183 : {
184 0 : nearPoints[i] = camera.m_Orientation.Transform(nearPoints[i]);
185 0 : farPoints[i] = camera.m_Orientation.Transform(farPoints[i]);
186 : }
187 :
188 : CShaderTechniquePtr overlayTech =
189 0 : GetShaderTechnique(str_debug_line, color, true, wireframe);
190 :
191 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext =
192 0 : g_Renderer.GetDeviceCommandContext();
193 0 : deviceCommandContext->SetGraphicsPipelineState(
194 0 : overlayTech->GetGraphicsPipelineState());
195 0 : deviceCommandContext->BeginPass();
196 :
197 0 : Renderer::Backend::IShaderProgram* overlayShader = overlayTech->GetShader();
198 :
199 0 : const CMatrix3D transform = g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
200 0 : deviceCommandContext->SetUniform(
201 0 : overlayShader->GetBindingSlot(str_transform), transform.AsFloatArray());
202 0 : deviceCommandContext->SetUniform(
203 0 : overlayShader->GetBindingSlot(str_color), color.AsFloatArray());
204 :
205 0 : 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);
210 :
211 : // Near plane.
212 0 : ADD(nearPoints[0]);
213 0 : ADD(nearPoints[1]);
214 0 : ADD(nearPoints[2]);
215 0 : ADD(nearPoints[0]);
216 0 : ADD(nearPoints[2]);
217 0 : ADD(nearPoints[3]);
218 :
219 : // Far plane.
220 0 : ADD(farPoints[0]);
221 0 : ADD(farPoints[1]);
222 0 : ADD(farPoints[2]);
223 0 : ADD(farPoints[0]);
224 0 : ADD(farPoints[2]);
225 0 : ADD(farPoints[3]);
226 :
227 : // Intermediate planes.
228 0 : CVector3D intermediatePoints[4];
229 0 : for (int i = 0; i < intermediates; ++i)
230 : {
231 0 : const float t = (i + 1.0f) / (intermediates + 1.0f);
232 :
233 0 : for (int j = 0; j < 4; ++j)
234 0 : intermediatePoints[j] = nearPoints[j] * t + farPoints[j] * (1.0f - t);
235 :
236 0 : ADD(intermediatePoints[0]);
237 0 : ADD(intermediatePoints[1]);
238 0 : ADD(intermediatePoints[2]);
239 0 : ADD(intermediatePoints[0]);
240 0 : ADD(intermediatePoints[2]);
241 0 : ADD(intermediatePoints[3]);
242 : }
243 :
244 0 : deviceCommandContext->SetVertexInputLayout(m_VertexInputLayout);
245 0 : deviceCommandContext->SetVertexBufferData(
246 0 : 0, vertices.data(), vertices.size() * sizeof(vertices[0]));
247 :
248 0 : deviceCommandContext->Draw(0, vertices.size() / 3);
249 :
250 0 : vertices.clear();
251 :
252 : // Connection lines.
253 0 : for (int i = 0; i < 4; ++i)
254 : {
255 0 : const int nextI = (i + 1) % 4;
256 0 : ADD(nearPoints[i]);
257 0 : ADD(farPoints[nextI]);
258 0 : ADD(farPoints[i]);
259 0 : ADD(nearPoints[i]);
260 0 : ADD(nearPoints[nextI]);
261 0 : ADD(farPoints[nextI]);
262 : }
263 :
264 0 : deviceCommandContext->SetVertexInputLayout(m_VertexInputLayout);
265 0 : deviceCommandContext->SetVertexBufferData(
266 0 : 0, vertices.data(), vertices.size() * sizeof(vertices[0]));
267 :
268 0 : deviceCommandContext->Draw(0, vertices.size() / 3);
269 : #undef ADD
270 :
271 0 : deviceCommandContext->EndPass();
272 0 : }
273 :
274 0 : void CDebugRenderer::DrawBoundingBox(
275 : const CBoundingBoxAligned& boundingBox, const CColor& color,
276 : bool wireframe)
277 : {
278 0 : DrawBoundingBox(
279 : boundingBox, color,
280 0 : g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection(), wireframe);
281 0 : }
282 :
283 0 : void CDebugRenderer::DrawBoundingBox(
284 : const CBoundingBoxAligned& boundingBox, const CColor& color,
285 : const CMatrix3D& transform, bool wireframe)
286 : {
287 : CShaderTechniquePtr shaderTech =
288 0 : GetShaderTechnique(str_debug_line, color, true, wireframe);
289 :
290 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext =
291 0 : g_Renderer.GetDeviceCommandContext();
292 0 : deviceCommandContext->SetGraphicsPipelineState(
293 0 : shaderTech->GetGraphicsPipelineState());
294 0 : deviceCommandContext->BeginPass();
295 :
296 0 : Renderer::Backend::IShaderProgram* shader = shaderTech->GetShader();
297 :
298 0 : deviceCommandContext->SetUniform(
299 0 : shader->GetBindingSlot(str_transform), transform.AsFloatArray());
300 0 : deviceCommandContext->SetUniform(
301 0 : shader->GetBindingSlot(str_color), color.AsFloatArray());
302 :
303 0 : std::vector<float> data;
304 :
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); \
313 : )
314 :
315 0 : ADD_FACE(u, v, 0);
316 0 : ADD_FACE(0, u, v);
317 0 : ADD_FACE(u, 0, 1-v);
318 0 : ADD_FACE(u, 1-v, 1);
319 0 : ADD_FACE(1, u, 1-v);
320 0 : ADD_FACE(u, 1, v);
321 :
322 : #undef ADD_FACE
323 :
324 0 : deviceCommandContext->SetVertexInputLayout(m_VertexInputLayout);
325 0 : deviceCommandContext->SetVertexBufferData(
326 0 : 0, data.data(), data.size() * sizeof(data[0]));
327 :
328 0 : deviceCommandContext->Draw(0, 6 * 6);
329 :
330 0 : deviceCommandContext->EndPass();
331 0 : }
332 :
333 0 : void CDebugRenderer::DrawBrush(const CBrush& brush, const CColor& color, bool wireframe)
334 : {
335 : CShaderTechniquePtr shaderTech =
336 0 : GetShaderTechnique(str_debug_line, color, true, wireframe);
337 :
338 : Renderer::Backend::IDeviceCommandContext* deviceCommandContext =
339 0 : g_Renderer.GetDeviceCommandContext();
340 0 : deviceCommandContext->SetGraphicsPipelineState(
341 0 : shaderTech->GetGraphicsPipelineState());
342 0 : deviceCommandContext->BeginPass();
343 :
344 0 : Renderer::Backend::IShaderProgram* shader = shaderTech->GetShader();
345 :
346 0 : const CMatrix3D transform = g_Renderer.GetSceneRenderer().GetViewCamera().GetViewProjection();
347 0 : deviceCommandContext->SetUniform(
348 0 : shader->GetBindingSlot(str_transform), transform.AsFloatArray());
349 0 : deviceCommandContext->SetUniform(
350 0 : shader->GetBindingSlot(str_color), color.AsFloatArray());
351 :
352 0 : std::vector<float> data;
353 :
354 0 : std::vector<std::vector<size_t>> faces;
355 0 : brush.GetFaces(faces);
356 :
357 : #define ADD_VERT(a) \
358 : STMT( \
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); \
362 : )
363 :
364 0 : for (size_t i = 0; i < faces.size(); ++i)
365 : {
366 : // Triangulate into (0,1,2), (0,2,3), ...
367 0 : for (size_t j = 1; j < faces[i].size() - 2; ++j)
368 : {
369 0 : ADD_VERT(0);
370 0 : ADD_VERT(j);
371 0 : ADD_VERT(j+1);
372 : }
373 : }
374 :
375 : #undef ADD_VERT
376 :
377 0 : deviceCommandContext->SetVertexInputLayout(m_VertexInputLayout);
378 0 : deviceCommandContext->SetVertexBufferData(
379 0 : 0, data.data(), data.size() * sizeof(data[0]));
380 :
381 0 : deviceCommandContext->Draw(0, data.size() / 5);
382 :
383 0 : deviceCommandContext->EndPass();
384 0 : }
385 :
386 0 : size_t CDebugRenderer::ShaderTechniqueKeyHash::operator()(
387 : const ShaderTechniqueKey& key) const
388 : {
389 0 : size_t seed = 0;
390 0 : hash_combine(seed, key.name.GetHash());
391 0 : hash_combine(seed, key.transparent);
392 0 : hash_combine(seed, key.depthTestEnabled);
393 0 : hash_combine(seed, key.wireframe);
394 0 : return seed;
395 : }
396 :
397 0 : bool CDebugRenderer::ShaderTechniqueKeyEqual::operator()(
398 : const ShaderTechniqueKey& lhs, const ShaderTechniqueKey& rhs) const
399 : {
400 : return
401 0 : lhs.name == rhs.name && lhs.transparent == rhs.transparent &&
402 0 : lhs.depthTestEnabled == rhs.depthTestEnabled &&
403 0 : lhs.wireframe == rhs.wireframe;
404 : }
405 :
406 0 : const CShaderTechniquePtr& CDebugRenderer::GetShaderTechnique(
407 : const CStrIntern name, const CColor& color, const bool depthTestEnabled,
408 : const bool wireframe)
409 : {
410 : const ShaderTechniqueKey key{
411 0 : name, color.a != 1.0f, depthTestEnabled, wireframe};
412 0 : CShaderTechniquePtr& shaderTechnique = m_ShaderTechniqueMapping[key];
413 0 : if (shaderTechnique)
414 0 : return shaderTechnique;
415 :
416 0 : shaderTechnique = g_Renderer.GetShaderManager().LoadEffect(
417 : name, {},
418 0 : [key](Renderer::Backend::SGraphicsPipelineStateDesc& pipelineStateDesc)
419 : {
420 0 : pipelineStateDesc.depthStencilState.depthTestEnabled = key.depthTestEnabled;
421 0 : if (key.transparent)
422 : {
423 0 : pipelineStateDesc.blendState.enabled = true;
424 0 : pipelineStateDesc.blendState.srcColorBlendFactor = pipelineStateDesc.blendState.srcAlphaBlendFactor =
425 : Renderer::Backend::BlendFactor::SRC_ALPHA;
426 0 : pipelineStateDesc.blendState.dstColorBlendFactor = pipelineStateDesc.blendState.dstAlphaBlendFactor =
427 : Renderer::Backend::BlendFactor::ONE_MINUS_SRC_ALPHA;
428 0 : pipelineStateDesc.blendState.colorBlendOp = pipelineStateDesc.blendState.alphaBlendOp =
429 : Renderer::Backend::BlendOp::ADD;
430 : }
431 : else
432 0 : pipelineStateDesc.blendState.enabled = false;
433 0 : if (key.wireframe)
434 0 : pipelineStateDesc.rasterizationState.polygonMode = Renderer::Backend::PolygonMode::LINE;
435 0 : pipelineStateDesc.rasterizationState.cullMode = Renderer::Backend::CullMode::NONE;
436 0 : });
437 0 : return shaderTechnique;
438 3 : }
|