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 "Renderer/BsRendererUtility.h"
4#include "RenderAPI/BsRenderAPI.h"
5#include "Mesh/BsMesh.h"
6#include "RenderAPI/BsVertexDataDesc.h"
7#include "Material/BsMaterial.h"
8#include "Material/BsGpuParamsSet.h"
9#include "Material/BsPass.h"
10#include "RenderAPI/BsGpuParams.h"
11#include "Utility/BsShapeMeshes3D.h"
12#include "Renderer/BsLight.h"
13#include "Material/BsShader.h"
14#include "Renderer/BsIBLUtility.h"
15#include "Math/BsAABox.h"
16
17namespace bs { namespace ct
18{
19 RendererUtility::RendererUtility()
20 {
21 {
22 mFullscreenQuadVDesc = bs_shared_ptr_new<VertexDataDesc>();
23 mFullscreenQuadVDesc->addVertElem(VET_FLOAT3, VES_POSITION);
24 mFullscreenQuadVDesc->addVertElem(VET_FLOAT2, VES_TEXCOORD);
25
26 INDEX_BUFFER_DESC ibDesc;
27 ibDesc.indexType = IT_32BIT;
28 ibDesc.numIndices = 6;
29 ibDesc.usage = GBU_DYNAMIC;
30
31 mFullScreenQuadIB = IndexBuffer::create(ibDesc);
32 mFullscreenQuadVDecl = VertexDeclaration::create(mFullscreenQuadVDesc);
33
34 VERTEX_BUFFER_DESC vbDesc;
35 vbDesc.vertexSize = mFullscreenQuadVDecl->getProperties().getVertexSize(0);
36 vbDesc.numVerts = 4 * NUM_QUAD_VB_SLOTS;
37 vbDesc.usage = GBU_DYNAMIC;
38
39 mFullScreenQuadVB = VertexBuffer::create(vbDesc);
40
41 UINT32 indices[] { 0, 1, 2, 1, 3, 2 };
42 mFullScreenQuadIB->writeData(0, sizeof(indices), indices, BWT_DISCARD);
43 }
44
45 {
46 SPtr<VertexDataDesc> vertexDesc = bs_shared_ptr_new<VertexDataDesc>();
47 vertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
48
49 UINT32 numVertices = 0;
50 UINT32 numIndices = 0;
51
52 ShapeMeshes3D::getNumElementsSphere(3, numVertices, numIndices);
53 SPtr<MeshData> meshData = bs_shared_ptr_new<MeshData>(numVertices, numIndices, vertexDesc);
54
55 UINT32* indexData = meshData->getIndices32();
56 UINT8* positionData = meshData->getElementData(VES_POSITION);
57
58 Sphere localSphere(Vector3::ZERO, 1.0f);
59 ShapeMeshes3D::solidSphere(localSphere, positionData, nullptr, nullptr, 0,
60 vertexDesc->getVertexStride(), indexData, 0, 3);
61
62 mUnitSphereStencilMesh = Mesh::create(meshData);
63 }
64
65 {
66 SPtr<VertexDataDesc> vertexDesc = bs_shared_ptr_new<VertexDataDesc>();
67 vertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
68
69 UINT32 numVertices = 0;
70 UINT32 numIndices = 0;
71
72 ShapeMeshes3D::getNumElementsAABox(numVertices, numIndices);
73 SPtr<MeshData> meshData = bs_shared_ptr_new<MeshData>(numVertices, numIndices, vertexDesc);
74
75 UINT32* indexData = meshData->getIndices32();
76 UINT8* positionData = meshData->getElementData(VES_POSITION);
77
78 AABox localBox(-Vector3::ONE, Vector3::ONE);
79 ShapeMeshes3D::solidAABox(localBox, positionData, nullptr, nullptr, 0,
80 vertexDesc->getVertexStride(), indexData, 0);
81
82 mUnitBoxStencilMesh = Mesh::create(meshData);
83 }
84
85 {
86 UINT32 numSides = Light::LIGHT_CONE_NUM_SIDES;
87 UINT32 numSlices = Light::LIGHT_CONE_NUM_SLICES;
88
89 SPtr<VertexDataDesc> vertexDesc = bs_shared_ptr_new<VertexDataDesc>();
90 vertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
91
92 UINT32 numVertices = numSides * numSlices * 2;
93 UINT32 numIndices = ((numSides * 2) * (numSlices - 1) * 2) * 3;
94
95 SPtr<MeshData> meshData = bs_shared_ptr_new<MeshData>(numVertices, numIndices, vertexDesc);
96
97 UINT32* indexData = meshData->getIndices32();
98 UINT8* positionData = meshData->getElementData(VES_POSITION);
99 UINT32 stride = vertexDesc->getVertexStride();
100
101 // Dummy vertex positions, actual ones generated in shader
102 for (UINT32 i = 0; i < numVertices; i++)
103 {
104 memcpy(positionData, &Vector3::ZERO, sizeof(Vector3));
105 positionData += stride;
106 }
107
108 // Cone indices
109 UINT32 curIdx = 0;
110 for (UINT32 sliceIdx = 0; sliceIdx < (numSlices - 1); sliceIdx++)
111 {
112 for (UINT32 sideIdx = 0; sideIdx < numSides; sideIdx++)
113 {
114 indexData[curIdx++] = sliceIdx * numSides + sideIdx;
115 indexData[curIdx++] = sliceIdx * numSides + (sideIdx + 1) % numSides;
116 indexData[curIdx++] = (sliceIdx + 1) * numSides + sideIdx;
117
118 indexData[curIdx++] = sliceIdx * numSides + (sideIdx + 1) % numSides;
119 indexData[curIdx++] = (sliceIdx + 1) * numSides + (sideIdx + 1) % numSides;
120 indexData[curIdx++] = (sliceIdx + 1) * numSides + sideIdx;
121 }
122 }
123
124 // Sphere cap indices
125 UINT32 coneOffset = numSides * numSlices;
126 for (UINT32 sliceIdx = 0; sliceIdx < (numSlices - 1); sliceIdx++)
127 {
128 for (UINT32 sideIdx = 0; sideIdx < numSides; sideIdx++)
129 {
130 indexData[curIdx++] = coneOffset + sliceIdx * numSides + sideIdx;
131 indexData[curIdx++] = coneOffset + sliceIdx * numSides + (sideIdx + 1) % numSides;
132 indexData[curIdx++] = coneOffset + (sliceIdx + 1) * numSides + sideIdx;
133
134 indexData[curIdx++] = coneOffset + sliceIdx * numSides + (sideIdx + 1) % numSides;
135 indexData[curIdx++] = coneOffset + (sliceIdx + 1) * numSides + (sideIdx + 1) % numSides;
136 indexData[curIdx++] = coneOffset + (sliceIdx + 1) * numSides + sideIdx;
137 }
138 }
139
140 mSpotLightStencilMesh = Mesh::create(meshData);
141 }
142
143 {
144 SPtr<VertexDataDesc> vertexDesc = bs_shared_ptr_new<VertexDataDesc>();
145 vertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
146
147 UINT32 numVertices = 0;
148 UINT32 numIndices = 0;
149
150 ShapeMeshes3D::getNumElementsAABox(numVertices, numIndices);
151 SPtr<MeshData> meshData = bs_shared_ptr_new<MeshData>(numVertices, numIndices, vertexDesc);
152
153 UINT32* indexData = meshData->getIndices32();
154 UINT8* positionData = meshData->getElementData(VES_POSITION);
155
156 AABox localBox(-Vector3::ONE * 1500.0f, Vector3::ONE * 1500.0f);
157 ShapeMeshes3D::solidAABox(localBox, positionData, nullptr, nullptr, 0,
158 vertexDesc->getVertexStride(), indexData, 0);
159
160 mSkyBoxMesh = Mesh::create(meshData);
161 }
162 }
163
164 void RendererUtility::setPass(const SPtr<Material>& material, UINT32 passIdx, UINT32 techniqueIdx)
165 {
166 RenderAPI& rapi = RenderAPI::instance();
167
168 SPtr<Pass> pass = material->getPass(passIdx, techniqueIdx);
169 rapi.setGraphicsPipeline(pass->getGraphicsPipelineState());
170 rapi.setStencilRef(pass->getStencilRefValue());
171 }
172
173 void RendererUtility::setComputePass(const SPtr<Material>& material, UINT32 passIdx)
174 {
175 RenderAPI& rapi = RenderAPI::instance();
176
177 SPtr<Pass> pass = material->getPass(passIdx);
178 rapi.setComputePipeline(pass->getComputePipelineState());
179 }
180
181 void RendererUtility::setPassParams(const SPtr<GpuParamsSet>& params, UINT32 passIdx)
182 {
183 SPtr<GpuParams> gpuParams = params->getGpuParams(passIdx);
184 if (gpuParams == nullptr)
185 return;
186
187 RenderAPI& rapi = RenderAPI::instance();
188 rapi.setGpuParams(gpuParams);
189 }
190
191 void RendererUtility::draw(const SPtr<MeshBase>& mesh, UINT32 numInstances)
192 {
193 draw(mesh, mesh->getProperties().getSubMesh(0), numInstances);
194 }
195
196 void RendererUtility::draw(const SPtr<MeshBase>& mesh, const SubMesh& subMesh, UINT32 numInstances)
197 {
198 RenderAPI& rapi = RenderAPI::instance();
199 SPtr<VertexData> vertexData = mesh->getVertexData();
200
201 rapi.setVertexDeclaration(mesh->getVertexData()->vertexDeclaration);
202
203 auto& vertexBuffers = vertexData->getBuffers();
204 if (vertexBuffers.size() > 0)
205 {
206 SPtr<VertexBuffer> buffers[BS_MAX_BOUND_VERTEX_BUFFERS];
207
208 UINT32 endSlot = 0;
209 UINT32 startSlot = BS_MAX_BOUND_VERTEX_BUFFERS;
210 for (auto iter = vertexBuffers.begin(); iter != vertexBuffers.end(); ++iter)
211 {
212 if (iter->first >= BS_MAX_BOUND_VERTEX_BUFFERS)
213 BS_EXCEPT(InvalidParametersException, "Buffer index out of range");
214
215 startSlot = std::min(iter->first, startSlot);
216 endSlot = std::max(iter->first, endSlot);
217 }
218
219 for (auto iter = vertexBuffers.begin(); iter != vertexBuffers.end(); ++iter)
220 {
221 buffers[iter->first - startSlot] = iter->second;
222 }
223
224 rapi.setVertexBuffers(startSlot, buffers, endSlot - startSlot + 1);
225 }
226
227 SPtr<IndexBuffer> indexBuffer = mesh->getIndexBuffer();
228 rapi.setIndexBuffer(indexBuffer);
229
230 rapi.setDrawOperation(subMesh.drawOp);
231
232 UINT32 indexCount = subMesh.indexCount;
233 rapi.drawIndexed(subMesh.indexOffset + mesh->getIndexOffset(), indexCount, mesh->getVertexOffset(),
234 vertexData->vertexCount, numInstances);
235
236 mesh->_notifyUsedOnGPU();
237 }
238
239 void RendererUtility::drawMorph(const SPtr<MeshBase>& mesh, const SubMesh& subMesh,
240 const SPtr<VertexBuffer>& morphVertices, const SPtr<VertexDeclaration>& morphVertexDeclaration)
241 {
242 // Bind buffers and draw
243 RenderAPI& rapi = RenderAPI::instance();
244
245 SPtr<VertexData> vertexData = mesh->getVertexData();
246 rapi.setVertexDeclaration(morphVertexDeclaration);
247
248 auto& meshBuffers = vertexData->getBuffers();
249 SPtr<VertexBuffer> allBuffers[BS_MAX_BOUND_VERTEX_BUFFERS];
250
251 UINT32 endSlot = 0;
252 UINT32 startSlot = BS_MAX_BOUND_VERTEX_BUFFERS;
253 for (auto iter = meshBuffers.begin(); iter != meshBuffers.end(); ++iter)
254 {
255 if (iter->first >= BS_MAX_BOUND_VERTEX_BUFFERS)
256 BS_EXCEPT(InvalidParametersException, "Buffer index out of range");
257
258 startSlot = std::min(iter->first, startSlot);
259 endSlot = std::max(iter->first, endSlot);
260 }
261
262 startSlot = std::min(1U, startSlot);
263 endSlot = std::max(1U, endSlot);
264
265 for (auto iter = meshBuffers.begin(); iter != meshBuffers.end(); ++iter)
266 allBuffers[iter->first - startSlot] = iter->second;
267
268 allBuffers[1] = morphVertices;
269 rapi.setVertexBuffers(startSlot, allBuffers, endSlot - startSlot + 1);
270
271 SPtr<IndexBuffer> indexBuffer = mesh->getIndexBuffer();
272 rapi.setIndexBuffer(indexBuffer);
273
274 rapi.setDrawOperation(subMesh.drawOp);
275
276 UINT32 indexCount = subMesh.indexCount;
277 rapi.drawIndexed(subMesh.indexOffset + mesh->getIndexOffset(), indexCount, mesh->getVertexOffset(),
278 vertexData->vertexCount, 1);
279
280 mesh->_notifyUsedOnGPU();
281 }
282
283 void RendererUtility::blit(const SPtr<Texture>& texture, const Rect2I& area, bool flipUV, bool isDepth)
284 {
285 auto& texProps = texture->getProperties();
286
287 Rect2 fArea((float)area.x, (float)area.y, (float)area.width, (float)area.height);
288 if (area.width == 0 || area.height == 0)
289 {
290 fArea.x = 0.0f;
291 fArea.y = 0.0f;
292 fArea.width = (float)texProps.getWidth();
293 fArea.height = (float)texProps.getHeight();
294 }
295
296 BlitMat* blitMat = BlitMat::getVariation(texProps.getNumSamples(), !isDepth);
297 blitMat->execute(texture, fArea, flipUV);
298 }
299
300 void RendererUtility::drawScreenQuad(const Rect2& uv, const Vector2I& textureSize, UINT32 numInstances, bool flipUV)
301 {
302 // Note: Consider drawing the quad using a single large triangle for possibly better performance
303 // Note2: Consider setting quad size in shader instead of rebuilding the mesh every time
304
305 const Conventions& rapiConventions = gCaps().conventions;
306 Vector3 vertices[4];
307
308 if (rapiConventions.ndcYAxis == Conventions::Axis::Down)
309 {
310 vertices[0] = Vector3(-1.0f, -1.0f, 0.0f);
311 vertices[1] = Vector3(1.0f, -1.0f, 0.0f);
312 vertices[2] = Vector3(-1.0f, 1.0f, 0.0f);
313 vertices[3] = Vector3(1.0f, 1.0f, 0.0f);
314 }
315 else
316 {
317 vertices[0] = Vector3(-1.0f, 1.0f, 0.0f);
318 vertices[1] = Vector3(1.0f, 1.0f, 0.0f);
319 vertices[2] = Vector3(-1.0f, -1.0f, 0.0f);
320 vertices[3] = Vector3(1.0f, -1.0f, 0.0f);
321 }
322
323 Vector2 uvs[4];
324 if ((rapiConventions.uvYAxis == Conventions::Axis::Up) ^ flipUV)
325 {
326 uvs[0] = Vector2(uv.x, uv.y + uv.height);
327 uvs[1] = Vector2(uv.x + uv.width, uv.y + uv.height);
328 uvs[2] = Vector2(uv.x, uv.y);
329 uvs[3] = Vector2(uv.x + uv.width, uv.y);
330 }
331 else
332 {
333 uvs[0] = Vector2(uv.x, uv.y);
334 uvs[1] = Vector2(uv.x + uv.width, uv.y);
335 uvs[2] = Vector2(uv.x, uv.y + uv.height);
336 uvs[3] = Vector2(uv.x + uv.width, uv.y + uv.height);
337 }
338
339 for (int i = 0; i < 4; i++)
340 {
341 uvs[i].x /= (float)textureSize.x;
342 uvs[i].y /= (float)textureSize.y;
343 }
344
345 SPtr<MeshData> meshData = bs_shared_ptr_new<MeshData>(4, 6, mFullscreenQuadVDesc);
346
347 auto vecIter = meshData->getVec3DataIter(VES_POSITION);
348 for (UINT32 i = 0; i < 4; i++)
349 vecIter.addValue(vertices[i]);
350
351 auto uvIter = meshData->getVec2DataIter(VES_TEXCOORD);
352 for (UINT32 i = 0; i < 4; i++)
353 uvIter.addValue(uvs[i]);
354
355 UINT32 bufferSize = meshData->getStreamSize(0);
356 UINT8* srcVertBufferData = meshData->getStreamData(0);
357
358 void* dstData = mFullScreenQuadVB->lock(mNextQuadVBSlot * bufferSize, bufferSize, GBL_WRITE_ONLY_NO_OVERWRITE);
359 memcpy(dstData, srcVertBufferData, bufferSize);
360 mFullScreenQuadVB->unlock();
361
362 RenderAPI& rapi = RenderAPI::instance();
363
364 rapi.setVertexDeclaration(mFullscreenQuadVDecl);
365 rapi.setVertexBuffers(0, &mFullScreenQuadVB, 1);
366 rapi.setIndexBuffer(mFullScreenQuadIB);
367 rapi.setDrawOperation(DOT_TRIANGLE_LIST);
368 rapi.drawIndexed(0, 6, mNextQuadVBSlot * 4, 4, numInstances);
369
370 mNextQuadVBSlot = (mNextQuadVBSlot + 1) % NUM_QUAD_VB_SLOTS;
371 }
372
373 void RendererUtility::clear(UINT32 value)
374 {
375 ClearMat* clearMat = ClearMat::get();
376 clearMat->execute(value);
377 }
378
379 RendererUtility& gRendererUtility()
380 {
381 return RendererUtility::instance();
382 }
383
384 BlitMat::BlitMat()
385 {
386 mParams->getTextureParam(GPT_FRAGMENT_PROGRAM, "gSource", mSource);
387 }
388
389 void BlitMat::execute(const SPtr<Texture>& source, const Rect2& area, bool flipUV)
390 {
391 BS_RENMAT_PROFILE_BLOCK
392
393 mSource.set(source);
394 bind();
395
396 gRendererUtility().drawScreenQuad(area, Vector2I(1, 1), 1, flipUV);
397 }
398
399 BlitMat* BlitMat::getVariation(UINT32 msaaCount, bool isColor)
400 {
401 if (msaaCount > 1)
402 {
403 if(isColor)
404 {
405 switch(msaaCount)
406 {
407 case 2:
408 return get(getVariation<2, true>());
409 case 4:
410 return get(getVariation<4, true>());
411 default:
412 case 8:
413 return get(getVariation<8, true>());
414 }
415 }
416 else
417 {
418 switch(msaaCount)
419 {
420 case 2:
421 return get(getVariation<2, false>());
422 case 4:
423 return get(getVariation<4, false>());
424 default:
425 case 8:
426 return get(getVariation<8, false>());
427 }
428 }
429 }
430 else
431 return get(getVariation<1, true>());
432 }
433
434 ClearParamDef gClearParamDef;
435
436 ClearMat::ClearMat()
437 {
438 mParamBuffer = gClearParamDef.createBuffer();
439 mParams->setParamBlockBuffer("Params", mParamBuffer);
440 }
441
442 void ClearMat::execute(UINT32 value)
443 {
444 BS_RENMAT_PROFILE_BLOCK
445
446 gClearParamDef.gClearValue.set(mParamBuffer, value);
447
448 bind();
449 gRendererUtility().drawScreenQuad();
450 }
451}}
452