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 "Utility/BsDrawHelper.h"
4#include "Mesh/BsMesh.h"
5#include "Math/BsAABox.h"
6#include "Math/BsSphere.h"
7#include "RenderAPI/BsVertexDataDesc.h"
8#include "Utility/BsShapeMeshes3D.h"
9#include "Text/BsTextData.h"
10#include "Math/BsVector2.h"
11#include "Math/BsQuaternion.h"
12#include "String/BsUnicode.h"
13#include "Renderer/BsCamera.h"
14
15namespace bs
16{
17 const UINT32 DrawHelper::VERTEX_BUFFER_GROWTH = 4096;
18 const UINT32 DrawHelper::INDEX_BUFFER_GROWTH = 4096 * 2;
19
20 DrawHelper::DrawHelper()
21 :mLayer(1)
22 {
23 mTransform = Matrix4::IDENTITY;
24
25 mSolidVertexDesc = bs_shared_ptr_new<VertexDataDesc>();
26 mSolidVertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
27 mSolidVertexDesc->addVertElem(VET_FLOAT3, VES_NORMAL);
28 mSolidVertexDesc->addVertElem(VET_COLOR, VES_COLOR);
29
30 mWireVertexDesc = bs_shared_ptr_new<VertexDataDesc>();
31 mWireVertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
32 mWireVertexDesc->addVertElem(VET_COLOR, VES_COLOR);
33
34 mLineVertexDesc = bs_shared_ptr_new<VertexDataDesc>();
35 mLineVertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
36 mLineVertexDesc->addVertElem(VET_COLOR, VES_COLOR);
37
38 mTextVertexDesc = bs_shared_ptr_new<VertexDataDesc>();
39 mTextVertexDesc->addVertElem(VET_FLOAT3, VES_POSITION);
40 mTextVertexDesc->addVertElem(VET_FLOAT2, VES_TEXCOORD);
41 mTextVertexDesc->addVertElem(VET_COLOR, VES_COLOR);
42 }
43
44 void DrawHelper::setColor(const Color& color)
45 {
46 mColor = color;
47 }
48
49 void DrawHelper::setTransform(const Matrix4& transform)
50 {
51 mTransform = transform;
52 }
53
54 void DrawHelper::setLayer(UINT64 layer)
55 {
56 mLayer = layer;
57 }
58
59 void DrawHelper::cube(const Vector3& position, const Vector3& extents)
60 {
61 mSolidCubeData.push_back(CubeData());
62 CubeData& cubeData = mSolidCubeData.back();
63
64 cubeData.position = position;
65 cubeData.extents = extents;
66 cubeData.color = mColor;
67 cubeData.transform = mTransform;
68 cubeData.layer = mLayer;
69 cubeData.center = mTransform.multiplyAffine(position);
70 }
71
72 void DrawHelper::sphere(const Vector3& position, float radius, UINT32 quality)
73 {
74 mSolidSphereData.push_back(SphereData());
75 SphereData& sphereData = mSolidSphereData.back();
76
77 sphereData.position = position;
78 sphereData.radius = radius;
79 sphereData.quality = quality;
80 sphereData.color = mColor;
81 sphereData.transform = mTransform;
82 sphereData.layer = mLayer;
83 sphereData.center = mTransform.multiplyAffine(position);
84 }
85
86 void DrawHelper::wireCube(const Vector3& position, const Vector3& extents)
87 {
88 mWireCubeData.push_back(CubeData());
89 CubeData& cubeData = mWireCubeData.back();
90
91 cubeData.position = position;
92 cubeData.extents = extents;
93 cubeData.color = mColor;
94 cubeData.transform = mTransform;
95 cubeData.layer = mLayer;
96 cubeData.center = mTransform.multiplyAffine(position);
97 }
98
99 void DrawHelper::wireSphere(const Vector3& position, float radius, UINT32 quality)
100 {
101 mWireSphereData.push_back(SphereData());
102 SphereData& sphereData = mWireSphereData.back();
103
104 sphereData.position = position;
105 sphereData.radius = radius;
106 sphereData.quality = quality;
107 sphereData.color = mColor;
108 sphereData.transform = mTransform;
109 sphereData.layer = mLayer;
110 sphereData.center = mTransform.multiplyAffine(position);
111 }
112
113 void DrawHelper::wireHemisphere(const Vector3& position, float radius, UINT32 quality)
114 {
115 mWireHemisphereData.push_back(SphereData());
116 SphereData& sphereData = mWireHemisphereData.back();
117
118 sphereData.position = position;
119 sphereData.radius = radius;
120 sphereData.quality = quality;
121 sphereData.color = mColor;
122 sphereData.transform = mTransform;
123 sphereData.layer = mLayer;
124 sphereData.center = mTransform.multiplyAffine(position);
125 }
126
127 void DrawHelper::line(const Vector3& start, const Vector3& end)
128 {
129 mLineData.push_back(LineData());
130 LineData& lineData = mLineData.back();
131
132 lineData.start = start;
133 lineData.end = end;
134 lineData.color = mColor;
135 lineData.transform = mTransform;
136 lineData.layer = mLayer;
137 lineData.center = mTransform.multiplyAffine((start + end) * 0.5f);
138 }
139
140 void DrawHelper::lineList(const Vector<Vector3>& lines)
141 {
142 if (lines.size() < 2)
143 return;
144
145 mLineListData.push_back(LineListData());
146 LineListData& lineListData = mLineListData.back();
147
148 Vector3 center(BsZero);
149 for (auto& point : lines)
150 center += point;
151
152 lineListData.lines = lines;
153 lineListData.color = mColor;
154 lineListData.transform = mTransform;
155 lineListData.layer = mLayer;
156 lineListData.center = center / (float)lines.size();;
157 }
158
159 void DrawHelper::frustum(const Vector3& position, float aspect, Degree FOV, float near, float far)
160 {
161 mFrustumData.push_back(FrustumData());
162 FrustumData& frustumData = mFrustumData.back();
163
164 frustumData.position = position;
165 frustumData.aspect = aspect;
166 frustumData.FOV = FOV;
167 frustumData.nearDist = near;
168 frustumData.farDist = far;
169 frustumData.color = mColor;
170 frustumData.transform = mTransform;
171 frustumData.layer = mLayer;
172 frustumData.center = mTransform.multiplyAffine(position);
173 }
174
175 void DrawHelper::cone(const Vector3& base, const Vector3& normal, float height, float radius, const Vector2& scale,
176 UINT32 quality)
177 {
178 mConeData.push_back(ConeData());
179 ConeData& coneData = mConeData.back();
180
181 coneData.base = base;
182 coneData.normal = normal;
183 coneData.height = height;
184 coneData.radius = radius;
185 coneData.scale = scale;
186 coneData.quality = quality;
187 coneData.color = mColor;
188 coneData.transform = mTransform;
189 coneData.layer = mLayer;
190 coneData.center = mTransform.multiplyAffine(base + normal * height * 0.5f);
191 }
192
193 void DrawHelper::wireCone(const Vector3& base, const Vector3& normal, float height, float radius, const Vector2& scale,
194 UINT32 quality)
195 {
196 mWireConeData.push_back(ConeData());
197 ConeData& coneData = mWireConeData.back();
198
199 coneData.base = base;
200 coneData.normal = normal;
201 coneData.height = height;
202 coneData.radius = radius;
203 coneData.scale = scale;
204 coneData.quality = quality;
205 coneData.color = mColor;
206 coneData.transform = mTransform;
207 coneData.layer = mLayer;
208 coneData.center = mTransform.multiplyAffine(base + normal * height * 0.5f);
209 }
210
211 void DrawHelper::disc(const Vector3& position, const Vector3& normal, float radius, UINT32 quality)
212 {
213 mDiscData.push_back(DiscData());
214 DiscData& discData = mDiscData.back();
215
216 discData.position = position;
217 discData.normal = normal;
218 discData.radius = radius;
219 discData.quality = quality;
220 discData.color = mColor;
221 discData.transform = mTransform;
222 discData.layer = mLayer;
223 discData.center = mTransform.multiplyAffine(position);
224 }
225
226 void DrawHelper::wireDisc(const Vector3& position, const Vector3& normal, float radius, UINT32 quality)
227 {
228 mWireDiscData.push_back(DiscData());
229 DiscData& discData = mWireDiscData.back();
230
231 discData.position = position;
232 discData.normal = normal;
233 discData.radius = radius;
234 discData.quality = quality;
235 discData.color = mColor;
236 discData.transform = mTransform;
237 discData.layer = mLayer;
238 discData.center = mTransform.multiplyAffine(position);
239 }
240
241 void DrawHelper::arc(const Vector3& position, const Vector3& normal, float radius,
242 Degree startAngle, Degree amountAngle, UINT32 quality)
243 {
244 mArcData.push_back(ArcData());
245 ArcData& arcData = mArcData.back();
246
247 arcData.position = position;
248 arcData.normal = normal;
249 arcData.radius = radius;
250 arcData.startAngle = startAngle;
251 arcData.amountAngle = amountAngle;
252 arcData.quality = quality;
253 arcData.color = mColor;
254 arcData.transform = mTransform;
255 arcData.layer = mLayer;
256 arcData.center = mTransform.multiplyAffine(position);
257 }
258
259 void DrawHelper::wireArc(const Vector3& position, const Vector3& normal, float radius,
260 Degree startAngle, Degree amountAngle, UINT32 quality)
261 {
262 mWireArcData.push_back(ArcData());
263 ArcData& arcData = mWireArcData.back();
264
265 arcData.position = position;
266 arcData.normal = normal;
267 arcData.radius = radius;
268 arcData.startAngle = startAngle;
269 arcData.amountAngle = amountAngle;
270 arcData.quality = quality;
271 arcData.color = mColor;
272 arcData.transform = mTransform;
273 arcData.layer = mLayer;
274 arcData.center = mTransform.multiplyAffine(position);
275 }
276
277 void DrawHelper::rectangle(const Rect3& area)
278 {
279 mRect3Data.push_back(Rect3Data());
280 Rect3Data& rectData = mRect3Data.back();
281
282 rectData.area = area;
283 rectData.color = mColor;
284 rectData.transform = mTransform;
285 rectData.layer = mLayer;
286 rectData.center = mTransform.multiplyAffine(area.getCenter());
287 }
288
289 void DrawHelper::text(const Vector3& position, const String& text, const HFont& font, UINT32 size)
290 {
291 if (!font.isLoaded() || text.empty())
292 return;
293
294 mText2DData.push_back(Text2DData());
295 Text2DData& textData = mText2DData.back();
296
297 textData.position = position;
298 textData.color = mColor;
299 textData.transform = mTransform;
300 textData.layer = mLayer;
301 textData.center = mTransform.multiplyAffine(position);
302 textData.text = text;
303 textData.font = font;
304 textData.size = size;
305 }
306
307 void DrawHelper::wireMesh(const SPtr<MeshData>& meshData)
308 {
309 if (meshData == nullptr)
310 return;
311
312 mWireMeshData.push_back(WireMeshData());
313 WireMeshData& wireMeshData = mWireMeshData.back();
314
315 wireMeshData.meshData = meshData;
316 wireMeshData.color = mColor;
317 wireMeshData.transform = mTransform;
318 wireMeshData.layer = mLayer;
319 wireMeshData.center = mTransform.multiplyAffine(Vector3::ZERO);
320 }
321
322 void DrawHelper::clear()
323 {
324 mSolidCubeData.clear();
325 mWireCubeData.clear();
326 mSolidSphereData.clear();
327 mWireSphereData.clear();
328 mWireHemisphereData.clear();
329 mLineData.clear();
330 mLineListData.clear();
331 mRect3Data.clear();
332 mFrustumData.clear();
333 mDiscData.clear();
334 mWireDiscData.clear();
335 mArcData.clear();
336 mWireArcData.clear();
337 mConeData.clear();
338 mWireConeData.clear();
339 mText2DData.clear();
340 mWireMeshData.clear();
341 }
342
343 Vector<DrawHelper::ShapeMeshData> DrawHelper::buildMeshes(SortType sorting, const Camera* camera, UINT64 layers)
344 {
345 Vector<ShapeMeshData> meshInfos;
346
347 enum class ShapeType
348 {
349 Cube, Sphere, WireCube, WireSphere, WireCone, Line, LineList, Frustum,
350 Cone, Disc, WireDisc, Arc, WireArc, Rectangle, Text, WireMesh, WireHemisphere
351 };
352
353 struct RawData
354 {
355 ShapeType shapeType;
356 MeshType meshType;
357 UINT32 idx;
358 UINT32 textIdx;
359 float distance;
360 UINT32 numVertices;
361 UINT32 numIndices;
362 };
363
364 /************************************************************************/
365 /* Sort everything according to specified sorting rule */
366 /************************************************************************/
367
368 UINT32 idx = 0;
369 Vector<RawData> allShapes;
370 Vector3 reference = Vector3::ZERO;
371
372 if(camera)
373 reference = camera->getTransform().getPosition();
374
375 UINT32 localIdx = 0;
376 for (auto& shapeData : mSolidCubeData)
377 {
378 if ((shapeData.layer & layers) == 0)
379 {
380 localIdx++;
381 continue;
382 }
383
384 allShapes.push_back(RawData());
385 RawData& rawData = allShapes.back();
386
387 rawData.idx = localIdx++;
388 rawData.textIdx = 0;
389 rawData.meshType = MeshType::Solid;
390 rawData.shapeType = ShapeType::Cube;
391 rawData.distance = shapeData.center.distance(reference);
392
393 ShapeMeshes3D::getNumElementsAABox(rawData.numVertices, rawData.numIndices);
394 }
395
396 localIdx = 0;
397 for (auto& shapeData : mSolidSphereData)
398 {
399 if ((shapeData.layer & layers) == 0)
400 {
401 localIdx++;
402 continue;
403 }
404
405 allShapes.push_back(RawData());
406 RawData& rawData = allShapes.back();
407
408 rawData.idx = localIdx++;
409 rawData.textIdx = 0;
410 rawData.meshType = MeshType::Solid;
411 rawData.shapeType = ShapeType::Sphere;
412 rawData.distance = shapeData.center.distance(reference);
413
414 ShapeMeshes3D::getNumElementsSphere(shapeData.quality,
415 rawData.numVertices, rawData.numIndices);
416 }
417
418 localIdx = 0;
419 for (auto& shapeData : mConeData)
420 {
421 if ((shapeData.layer & layers) == 0)
422 {
423 localIdx++;
424 continue;
425 }
426
427 allShapes.push_back(RawData());
428 RawData& rawData = allShapes.back();
429
430 rawData.idx = localIdx++;
431 rawData.textIdx = 0;
432 rawData.meshType = MeshType::Solid;
433 rawData.shapeType = ShapeType::Cone;
434 rawData.distance = shapeData.center.distance(reference);
435
436 ShapeMeshes3D::getNumElementsCone(shapeData.quality,
437 rawData.numVertices, rawData.numIndices);
438 }
439
440 localIdx = 0;
441 for (auto& shapeData : mDiscData)
442 {
443 if ((shapeData.layer & layers) == 0)
444 {
445 localIdx++;
446 continue;
447 }
448
449 allShapes.push_back(RawData());
450 RawData& rawData = allShapes.back();
451
452 rawData.idx = localIdx++;
453 rawData.textIdx = 0;
454 rawData.meshType = MeshType::Solid;
455 rawData.shapeType = ShapeType::Disc;
456 rawData.distance = shapeData.center.distance(reference);
457
458 ShapeMeshes3D::getNumElementsDisc(shapeData.quality,
459 rawData.numVertices, rawData.numIndices);
460 }
461
462 localIdx = 0;
463 for (auto& shapeData : mArcData)
464 {
465 if ((shapeData.layer & layers) == 0)
466 {
467 localIdx++;
468 continue;
469 }
470
471 allShapes.push_back(RawData());
472 RawData& rawData = allShapes.back();
473
474 rawData.idx = localIdx++;
475 rawData.textIdx = 0;
476 rawData.meshType = MeshType::Solid;
477 rawData.shapeType = ShapeType::Arc;
478 rawData.distance = shapeData.center.distance(reference);
479
480 ShapeMeshes3D::getNumElementsArc(shapeData.quality,
481 rawData.numVertices, rawData.numIndices);
482 }
483
484 localIdx = 0;
485 for (auto& shapeData : mRect3Data)
486 {
487 if ((shapeData.layer & layers) == 0)
488 {
489 localIdx++;
490 continue;
491 }
492
493 allShapes.push_back(RawData());
494 RawData& rawData = allShapes.back();
495
496 rawData.idx = localIdx++;
497 rawData.textIdx = 0;
498 rawData.meshType = MeshType::Solid;
499 rawData.shapeType = ShapeType::Rectangle;
500 rawData.distance = shapeData.center.distance(reference);
501
502 ShapeMeshes3D::getNumElementsQuad(rawData.numVertices, rawData.numIndices);
503 }
504
505 localIdx = 0;
506 for (auto& shapeData : mWireCubeData)
507 {
508 if ((shapeData.layer & layers) == 0)
509 {
510 localIdx++;
511 continue;
512 }
513
514 allShapes.push_back(RawData());
515 RawData& rawData = allShapes.back();
516
517 rawData.idx = localIdx++;
518 rawData.textIdx = 0;
519 rawData.meshType = MeshType::Line;
520 rawData.shapeType = ShapeType::WireCube;
521 rawData.distance = shapeData.center.distance(reference);
522
523 ShapeMeshes3D::getNumElementsWireAABox(rawData.numVertices, rawData.numIndices);
524 }
525
526 localIdx = 0;
527 for (auto& shapeData : mWireSphereData)
528 {
529 if ((shapeData.layer & layers) == 0)
530 {
531 localIdx++;
532 continue;
533 }
534
535 allShapes.push_back(RawData());
536 RawData& rawData = allShapes.back();
537
538 rawData.idx = localIdx++;
539 rawData.textIdx = 0;
540 rawData.meshType = MeshType::Line;
541 rawData.shapeType = ShapeType::WireSphere;
542 rawData.distance = shapeData.center.distance(reference);
543
544 ShapeMeshes3D::getNumElementsWireSphere(shapeData.quality,
545 rawData.numVertices, rawData.numIndices);
546 }
547
548 localIdx = 0;
549 for (auto& shapeData : mWireHemisphereData)
550 {
551 if ((shapeData.layer & layers) == 0)
552 {
553 localIdx++;
554 continue;
555 }
556
557 allShapes.push_back(RawData());
558 RawData& rawData = allShapes.back();
559
560 rawData.idx = localIdx++;
561 rawData.textIdx = 0;
562 rawData.meshType = MeshType::Line;
563 rawData.shapeType = ShapeType::WireHemisphere;
564 rawData.distance = shapeData.center.distance(reference);
565
566 ShapeMeshes3D::getNumElementsWireHemisphere(shapeData.quality,
567 rawData.numVertices, rawData.numIndices);
568 }
569
570 localIdx = 0;
571 for (auto& shapeData : mWireConeData)
572 {
573 if ((shapeData.layer & layers) == 0)
574 {
575 localIdx++;
576 continue;
577 }
578
579 allShapes.push_back(RawData());
580 RawData& rawData = allShapes.back();
581
582 rawData.idx = localIdx++;
583 rawData.textIdx = 0;
584 rawData.meshType = MeshType::Line;
585 rawData.shapeType = ShapeType::WireCone;
586 rawData.distance = shapeData.center.distance(reference);
587
588 ShapeMeshes3D::getNumElementsWireCone(shapeData.quality,
589 rawData.numVertices, rawData.numIndices);
590 }
591
592 localIdx = 0;
593 for (auto& shapeData : mLineData)
594 {
595 if ((shapeData.layer & layers) == 0)
596 {
597 localIdx++;
598 continue;
599 }
600
601 allShapes.push_back(RawData());
602 RawData& rawData = allShapes.back();
603
604 rawData.idx = localIdx++;
605 rawData.textIdx = 0;
606 rawData.meshType = MeshType::Line;
607 rawData.shapeType = ShapeType::Line;
608 rawData.distance = shapeData.center.distance(reference);
609 rawData.numVertices = 2;
610 rawData.numIndices = 2;
611 }
612
613 localIdx = 0;
614 for (auto& shapeData : mLineListData)
615 {
616 if ((shapeData.layer & layers) == 0)
617 {
618 localIdx++;
619 continue;
620 }
621
622 allShapes.push_back(RawData());
623 RawData& rawData = allShapes.back();
624
625 UINT32 numLines = (UINT32)shapeData.lines.size() / 2;
626 rawData.idx = localIdx++;
627 rawData.textIdx = 0;
628 rawData.meshType = MeshType::Line;
629 rawData.shapeType = ShapeType::LineList;
630 rawData.distance = shapeData.center.distance(reference);
631 rawData.numVertices = numLines * 2;
632 rawData.numIndices = numLines * 2;
633 }
634
635 localIdx = 0;
636 for (auto& shapeData : mFrustumData)
637 {
638 if ((shapeData.layer & layers) == 0)
639 {
640 localIdx++;
641 continue;
642 }
643
644 allShapes.push_back(RawData());
645 RawData& rawData = allShapes.back();
646
647 rawData.idx = localIdx++;
648 rawData.textIdx = 0;
649 rawData.meshType = MeshType::Line;
650 rawData.shapeType = ShapeType::Frustum;
651 rawData.distance = shapeData.center.distance(reference);
652
653 ShapeMeshes3D::getNumElementsFrustum(rawData.numVertices, rawData.numIndices);
654 }
655
656 localIdx = 0;
657 for (auto& shapeData : mWireDiscData)
658 {
659 if ((shapeData.layer & layers) == 0)
660 {
661 localIdx++;
662 continue;
663 }
664
665 allShapes.push_back(RawData());
666 RawData& rawData = allShapes.back();
667
668 rawData.idx = localIdx++;
669 rawData.textIdx = 0;
670 rawData.meshType = MeshType::Line;
671 rawData.shapeType = ShapeType::WireDisc;
672 rawData.distance = shapeData.center.distance(reference);
673
674 ShapeMeshes3D::getNumElementsWireDisc(shapeData.quality,
675 rawData.numVertices, rawData.numIndices);
676 }
677
678 localIdx = 0;
679 for (auto& shapeData : mWireArcData)
680 {
681 if ((shapeData.layer & layers) == 0)
682 {
683 localIdx++;
684 continue;
685 }
686
687 allShapes.push_back(RawData());
688 RawData& rawData = allShapes.back();
689
690 rawData.idx = localIdx++;
691 rawData.textIdx = 0;
692 rawData.meshType = MeshType::Line;
693 rawData.shapeType = ShapeType::WireArc;
694 rawData.distance = shapeData.center.distance(reference);
695
696 ShapeMeshes3D::getNumElementsWireArc(shapeData.quality,
697 rawData.numVertices, rawData.numIndices);
698 }
699
700 localIdx = 0;
701 for (auto& shapeData : mWireMeshData)
702 {
703 if ((shapeData.layer & layers) == 0)
704 {
705 localIdx++;
706 continue;
707 }
708
709 allShapes.push_back(RawData());
710 RawData& rawData = allShapes.back();
711
712 rawData.idx = localIdx++;
713 rawData.textIdx = 0;
714 rawData.meshType = MeshType::Wire;
715 rawData.shapeType = ShapeType::WireMesh;
716 rawData.distance = shapeData.center.distance(reference);
717 rawData.numVertices = shapeData.meshData->getNumVertices();
718 rawData.numIndices = shapeData.meshData->getNumIndices();
719 }
720
721 struct TextRenderData
722 {
723 UINT32 page;
724 SPtr<TextData<>> textData;
725 };
726
727 UnorderedMap<UINT32, TextRenderData> textRenderData;
728 UINT32 textIdx = 0;
729
730 localIdx = 0;
731 for (auto& shapeData : mText2DData)
732 {
733 if ((shapeData.layer & layers) == 0)
734 {
735 localIdx++;
736 continue;
737 }
738
739 U32String utf32text = UTF8::toUTF32(shapeData.text);
740 SPtr<TextData<>> textData = bs_shared_ptr_new<TextData<>>(utf32text, shapeData.font, shapeData.size);
741
742 UINT32 numPages = textData->getNumPages();
743 for (UINT32 j = 0; j < numPages; j++)
744 {
745 UINT32 numQuads = textData->getNumQuadsForPage(j);
746
747 allShapes.push_back(RawData());
748 RawData& rawData = allShapes.back();
749
750 rawData.idx = localIdx;
751 rawData.textIdx = textIdx;
752 rawData.meshType = MeshType::Text;
753 rawData.shapeType = ShapeType::Text;
754 rawData.distance = shapeData.center.distance(reference);
755 rawData.numVertices = numQuads * 4;
756 rawData.numIndices = numQuads * 6;
757
758 TextRenderData& renderData = textRenderData[textIdx];
759 renderData.page = j;
760 renderData.textData = textData;
761
762 textIdx++;
763 idx++;
764 }
765
766 localIdx++;
767 }
768
769 if (sorting == SortType::FrontToBack)
770 {
771 std::sort(begin(allShapes), end(allShapes),
772 [&](const RawData& x, const RawData& y)
773 {
774 return x.distance < y.distance;
775 });
776 }
777 else if (sorting == SortType::BackToFront)
778 {
779 std::sort(begin(allShapes), end(allShapes),
780 [&](const RawData& x, const RawData& y)
781 {
782 return y.distance < x.distance;
783 });
784 }
785
786 /************************************************************************/
787 /* Create batches */
788 /************************************************************************/
789 struct Batch
790 {
791 MeshType type;
792 HTexture texture;
793 UINT32 startIdx;
794 UINT32 endIdx;
795 UINT32 numVertices;
796 UINT32 numIndices;
797 };
798
799 UINT32 numShapes = (UINT32)allShapes.size();
800
801 Vector<Batch> batches;
802 if (numShapes > 0)
803 {
804 batches.push_back(Batch());
805
806 {
807 Batch& currentBatch = batches.back();
808 currentBatch.startIdx = 0;
809 currentBatch.type = allShapes[0].meshType;
810 currentBatch.numVertices = allShapes[0].numVertices;
811 currentBatch.numIndices = allShapes[0].numIndices;
812
813 if (allShapes[0].meshType == MeshType::Text)
814 {
815 TextRenderData& renderData = textRenderData[allShapes[0].textIdx];
816 currentBatch.texture = renderData.textData->getTextureForPage(renderData.page);
817 }
818 }
819
820 for (UINT32 i = 1; i < numShapes; i++)
821 {
822 Batch& currentBatch = batches.back();
823
824 HTexture texture;
825 if (allShapes[i].meshType == MeshType::Text)
826 {
827 TextRenderData& renderData = textRenderData[allShapes[i].textIdx];
828 texture = renderData.textData->getTextureForPage(renderData.page);
829 }
830
831 bool startNewBatch = allShapes[i].meshType != currentBatch.type || texture != currentBatch.texture;
832 if (startNewBatch)
833 {
834 currentBatch.endIdx = i - 1;
835
836 batches.push_back(Batch());
837
838 Batch& newBatch = batches.back();
839 newBatch.startIdx = i;
840 newBatch.type = allShapes[i].meshType;
841 newBatch.numVertices = allShapes[i].numVertices;
842 newBatch.numIndices = allShapes[i].numIndices;
843 newBatch.texture = texture;
844 }
845 else
846 {
847 currentBatch.endIdx = i;
848 currentBatch.numVertices += allShapes[i].numVertices;
849 currentBatch.numIndices += allShapes[i].numIndices;
850 }
851 }
852
853 {
854 Batch& currentBatch = batches.back();
855 currentBatch.endIdx = numShapes - 1;
856 }
857 }
858
859 // Allocate space for all the batch vertices/indices, per type
860 UINT32 vertexCount[4] = { 0, 0, 0, 0 };
861 UINT32 indexCount[4] = { 0, 0, 0, 0 };
862 for (auto& batch : batches)
863 {
864 UINT32 typeIdx = (UINT32)batch.type;
865
866 vertexCount[typeIdx] += batch.numVertices;
867 indexCount[typeIdx] += batch.numIndices;
868 }
869
870 SPtr<VertexDataDesc> vertexDesc[4] = { mSolidVertexDesc, mWireVertexDesc, mLineVertexDesc, mTextVertexDesc };
871 SPtr<MeshData> meshData[4];
872 for(UINT32 i = 0; i < 4; i++)
873 {
874 if(vertexCount[i] > 0 && indexCount[i] > 0)
875 meshData[i] = MeshData::create(vertexCount[i], indexCount[i], vertexDesc[i]);
876 }
877
878 /************************************************************************/
879 /* Generate geometry for each batch */
880 /************************************************************************/
881 UINT32 vertexOffset[4] = { 0, 0, 0, 0 };
882 UINT32 indexOffset[4] = { 0, 0, 0, 0 };
883
884 VertexElemIter<Vector3> positionIter[4];
885 VertexElemIter<UINT32> colorIter[4];
886
887 for(UINT32 i = 0; i < 4; i++)
888 {
889 if(!meshData[i])
890 continue;
891
892 positionIter[i] = meshData[i]->getVec3DataIter(VES_POSITION);
893 colorIter[i] = meshData[i]->getDWORDDataIter(VES_COLOR);
894 }
895
896 VertexElemIter<Vector3> solidNormalIter;
897 if(meshData[0])
898 solidNormalIter = meshData[0]->getVec3DataIter(VES_NORMAL);
899
900 VertexElemIter<Vector2> textUVIter;
901
902 if(meshData[3])
903 textUVIter = meshData[3]->getVec2DataIter(VES_TEXCOORD);
904
905 for (auto& batch : batches)
906 {
907 UINT32 typeIdx = (UINT32)batch.type;
908
909 if (batch.type == MeshType::Solid)
910 {
911 meshInfos.push_back(ShapeMeshData());
912 ShapeMeshData& newMesh = meshInfos.back();
913 newMesh.subMesh.indexOffset = indexOffset[typeIdx];
914 newMesh.subMesh.indexCount = batch.numIndices;
915 newMesh.subMesh.drawOp = DOT_TRIANGLE_LIST;
916 newMesh.type = MeshType::Solid;
917
918 for (UINT32 i = batch.startIdx; i <= batch.endIdx; i++)
919 {
920 RawData& shapeData = allShapes[i];
921
922 Matrix4* transform = nullptr;
923 RGBA color = 0;
924
925 switch (shapeData.shapeType)
926 {
927 case ShapeType::Cube:
928 {
929 CubeData& cubeData = mSolidCubeData[shapeData.idx];
930 AABox box(cubeData.position - cubeData.extents, cubeData.position + cubeData.extents);
931 ShapeMeshes3D::solidAABox(box, meshData[typeIdx], vertexOffset[typeIdx], indexOffset[typeIdx]);
932
933 transform = &cubeData.transform;
934 color = cubeData.color.getAsRGBA();
935 }
936 break;
937 case ShapeType::Sphere:
938 {
939 SphereData& sphereData = mSolidSphereData[shapeData.idx];
940 Sphere sphere(sphereData.position, sphereData.radius);
941 ShapeMeshes3D::solidSphere(sphere, meshData[typeIdx], vertexOffset[typeIdx], indexOffset[typeIdx],
942 sphereData.quality);
943
944 transform = &sphereData.transform;
945 color = sphereData.color.getAsRGBA();
946 }
947 break;
948 case ShapeType::Cone:
949 {
950 ConeData& coneData = mConeData[shapeData.idx];
951 ShapeMeshes3D::solidCone(coneData.base, coneData.normal, coneData.height, coneData.radius,
952 coneData.scale, meshData[typeIdx], vertexOffset[typeIdx], indexOffset[typeIdx], coneData.quality);
953
954 transform = &coneData.transform;
955 color = coneData.color.getAsRGBA();
956 }
957 break;
958 case ShapeType::Disc:
959 {
960 DiscData& discData = mDiscData[shapeData.idx];
961 ShapeMeshes3D::solidDisc(discData.position, discData.radius, discData.normal,
962 meshData[typeIdx], vertexOffset[typeIdx], indexOffset[typeIdx], discData.quality);
963
964 transform = &discData.transform;
965 color = discData.color.getAsRGBA();
966 }
967 break;
968 case ShapeType::Arc:
969 {
970 ArcData& arcData = mArcData[shapeData.idx];
971 ShapeMeshes3D::solidArc(arcData.position, arcData.radius, arcData.normal,
972 arcData.startAngle, arcData.amountAngle, meshData[typeIdx], vertexOffset[typeIdx],
973 indexOffset[typeIdx], arcData.quality);
974
975 transform = &arcData.transform;
976 color = arcData.color.getAsRGBA();
977 }
978 break;
979 case ShapeType::Rectangle:
980 {
981 Rect3Data& rectData = mRect3Data[shapeData.idx];
982 ShapeMeshes3D::solidQuad(rectData.area, meshData[typeIdx], vertexOffset[typeIdx],
983 indexOffset[typeIdx]);
984
985 transform = &rectData.transform;
986 color = rectData.color.getAsRGBA();
987 }
988 break;
989 default:
990 break;
991 }
992
993 Matrix4 transformIT = transform->inverseAffine().transpose();
994 for (UINT32 i = 0; i < shapeData.numVertices; i++)
995 {
996 Vector3 worldPos = transform->multiplyAffine(positionIter[typeIdx].getValue());
997 Vector3 worldNormal = transformIT.multiplyAffine(solidNormalIter.getValue());
998
999 positionIter[typeIdx].addValue(worldPos);
1000 solidNormalIter.addValue(worldNormal);
1001 colorIter[typeIdx].addValue(color);
1002 }
1003
1004 vertexOffset[typeIdx] += shapeData.numVertices;
1005 indexOffset[typeIdx] += shapeData.numIndices;
1006 }
1007 }
1008 else if (batch.type == MeshType::Wire)
1009 {
1010 meshInfos.push_back(ShapeMeshData());
1011 ShapeMeshData& newMesh = meshInfos.back();
1012 newMesh.subMesh.indexOffset = indexOffset[typeIdx];
1013 newMesh.subMesh.indexCount = batch.numIndices;
1014 newMesh.subMesh.drawOp = DOT_TRIANGLE_LIST;
1015 newMesh.type = MeshType::Wire;
1016
1017 for (UINT32 i = batch.startIdx; i <= batch.endIdx; i++)
1018 {
1019 RawData& shapeData = allShapes[i];
1020
1021 Matrix4* transform = nullptr;
1022 RGBA color = 0;
1023
1024 switch (shapeData.shapeType)
1025 {
1026 case ShapeType::WireMesh:
1027 {
1028 WireMeshData& wireMeshData = mWireMeshData[shapeData.idx];
1029
1030 transform = &wireMeshData.transform;
1031 color = wireMeshData.color.getAsRGBA();
1032
1033 auto vertIterRead = wireMeshData.meshData->getVec3DataIter(VES_POSITION);
1034 for (UINT32 j = 0; j < vertIterRead.getNumElements(); j++)
1035 {
1036 Vector3 worldPos = transform->multiplyAffine(vertIterRead.getValue());
1037
1038 positionIter[typeIdx].addValue(worldPos);
1039 colorIter[typeIdx].addValue(color);
1040
1041 vertIterRead.moveNext();
1042 }
1043
1044 UINT32* srcIndexData = wireMeshData.meshData->getIndices32();
1045 UINT32* destIndexData = meshData[typeIdx]->getIndices32() + indexOffset[typeIdx];
1046
1047 for(UINT32 j = 0; j < shapeData.numIndices; j++)
1048 destIndexData[j] = srcIndexData[j] + vertexOffset[typeIdx];
1049
1050 vertexOffset[typeIdx] += shapeData.numVertices;
1051 indexOffset[typeIdx] += shapeData.numIndices;
1052 }
1053 break;
1054 default:
1055 break;
1056 }
1057 }
1058 }
1059 else if(batch.type == MeshType::Line)
1060 {
1061 meshInfos.push_back(ShapeMeshData());
1062 ShapeMeshData& newMesh = meshInfos.back();
1063 newMesh.subMesh.indexOffset = indexOffset[typeIdx];
1064 newMesh.subMesh.indexCount = batch.numIndices;
1065 newMesh.subMesh.drawOp = DOT_LINE_LIST;
1066 newMesh.type = MeshType::Line;
1067
1068 for (UINT32 i = batch.startIdx; i <= batch.endIdx; i++)
1069 {
1070 RawData& shapeData = allShapes[i];
1071
1072 Matrix4* transform = nullptr;
1073 RGBA color = 0;
1074
1075 switch (shapeData.shapeType)
1076 {
1077 case ShapeType::WireCube:
1078 {
1079 CubeData& cubeData = mWireCubeData[shapeData.idx];
1080
1081 AABox box(cubeData.position - cubeData.extents, cubeData.position + cubeData.extents);
1082 ShapeMeshes3D::wireAABox(box, meshData[typeIdx], vertexOffset[typeIdx], indexOffset[typeIdx]);
1083
1084 transform = &cubeData.transform;
1085 color = cubeData.color.getAsRGBA();
1086 }
1087 break;
1088 case ShapeType::WireSphere:
1089 {
1090 SphereData& sphereData = mWireSphereData[shapeData.idx];
1091
1092 Sphere sphere(sphereData.position, sphereData.radius);
1093 ShapeMeshes3D::wireSphere(sphere, meshData[typeIdx], vertexOffset[typeIdx], indexOffset[typeIdx],
1094 sphereData.quality);
1095
1096 transform = &sphereData.transform;
1097 color = sphereData.color.getAsRGBA();
1098 }
1099 break;
1100 case ShapeType::WireHemisphere:
1101 {
1102 SphereData& sphereData = mWireHemisphereData[shapeData.idx];
1103
1104 Sphere sphere(sphereData.position, sphereData.radius);
1105 ShapeMeshes3D::wireHemisphere(sphere, meshData[typeIdx], vertexOffset[typeIdx], indexOffset[typeIdx],
1106 sphereData.quality);
1107
1108 transform = &sphereData.transform;
1109 color = sphereData.color.getAsRGBA();
1110 }
1111 break;
1112 case ShapeType::WireCone:
1113 {
1114 ConeData& coneData = mWireConeData[shapeData.idx];
1115 ShapeMeshes3D::wireCone(coneData.base, coneData.normal, coneData.height, coneData.radius,
1116 coneData.scale, meshData[typeIdx], vertexOffset[typeIdx], indexOffset[typeIdx],
1117 coneData.quality);
1118
1119 transform = &coneData.transform;
1120 color = coneData.color.getAsRGBA();
1121 }
1122 break;
1123 case ShapeType::Line:
1124 {
1125 LineData& lineData = mLineData[shapeData.idx];
1126
1127 ShapeMeshes3D::pixelLine(lineData.start, lineData.end, meshData[typeIdx], vertexOffset[typeIdx],
1128 indexOffset[typeIdx]);
1129
1130 transform = &lineData.transform;
1131 color = lineData.color.getAsRGBA();
1132 }
1133 break;
1134 case ShapeType::LineList:
1135 {
1136 LineListData& lineListData = mLineListData[shapeData.idx];
1137
1138 ShapeMeshes3D::pixelLineList(lineListData.lines, meshData[typeIdx], vertexOffset[typeIdx],
1139 indexOffset[typeIdx]);
1140
1141 transform = &lineListData.transform;
1142 color = lineListData.color.getAsRGBA();
1143 }
1144 break;
1145 case ShapeType::Frustum:
1146 {
1147 FrustumData& frustumData = mFrustumData[shapeData.idx];
1148
1149 ShapeMeshes3D::wireFrustum(frustumData.position, frustumData.aspect, frustumData.FOV, frustumData.nearDist,
1150 frustumData.farDist, meshData[typeIdx], vertexOffset[typeIdx], indexOffset[typeIdx]);
1151
1152 transform = &frustumData.transform;
1153 color = frustumData.color.getAsRGBA();
1154 }
1155 break;
1156 case ShapeType::WireDisc:
1157 {
1158 DiscData& discData = mWireDiscData[shapeData.idx];
1159
1160 ShapeMeshes3D::wireDisc(discData.position, discData.radius, discData.normal,
1161 meshData[typeIdx], vertexOffset[typeIdx], indexOffset[typeIdx], discData.quality);
1162
1163 transform = &discData.transform;
1164 color = discData.color.getAsRGBA();
1165 }
1166 break;
1167 case ShapeType::WireArc:
1168 {
1169 ArcData& arcData = mWireArcData[shapeData.idx];
1170
1171 ShapeMeshes3D::wireArc(arcData.position, arcData.radius, arcData.normal,
1172 arcData.startAngle, arcData.amountAngle, meshData[typeIdx], vertexOffset[typeIdx],
1173 indexOffset[typeIdx], arcData.quality);
1174
1175 transform = &arcData.transform;
1176 color = arcData.color.getAsRGBA();
1177 }
1178 break;
1179 default:
1180 break;
1181 }
1182
1183 for (UINT32 i = 0; i < shapeData.numVertices; i++)
1184 {
1185 Vector3 worldPos = transform->multiplyAffine(positionIter[typeIdx].getValue());
1186
1187 positionIter[typeIdx].addValue(worldPos);
1188 colorIter[typeIdx].addValue(color);
1189 }
1190
1191 vertexOffset[typeIdx] += shapeData.numVertices;
1192 indexOffset[typeIdx] += shapeData.numIndices;
1193 }
1194 }
1195 else // Text
1196 {
1197 // Text drawing requires a camera in order to determine screen space coordinates
1198 if(!camera)
1199 continue;
1200
1201 meshInfos.push_back(ShapeMeshData());
1202 ShapeMeshData& newMesh = meshInfos.back();
1203 newMesh.subMesh.indexOffset = indexOffset[typeIdx];
1204 newMesh.subMesh.indexCount = batch.numIndices;
1205 newMesh.subMesh.drawOp = DOT_TRIANGLE_LIST;
1206 newMesh.type = MeshType::Text;
1207 newMesh.texture = batch.texture;
1208
1209 for (UINT32 i = batch.startIdx; i <= batch.endIdx; i++)
1210 {
1211 RawData& shapeData = allShapes[i];
1212 Text2DData& text2DData = mText2DData[shapeData.idx];
1213
1214 TextRenderData& renderData = textRenderData[shapeData.textIdx];
1215 UINT32 numQuads = renderData.textData->getNumQuadsForPage(renderData.page);
1216
1217 UINT32* indices = meshData[typeIdx]->getIndices32() + indexOffset[typeIdx];
1218
1219 // Note: Need temporary buffers because TextLine doesn't support arbitrary vertex stride. Eventually
1220 // that should be supported (should be almost trivial to implement)
1221 Vector2* tempVertices = bs_stack_alloc<Vector2>(shapeData.numVertices);
1222 Vector2* tempUVs = bs_stack_alloc<Vector2>(shapeData.numVertices);
1223
1224 UINT32 numLines = renderData.textData->getNumLines();
1225 UINT32 quadOffset = 0;
1226 for (UINT32 j = 0; j < numLines; j++)
1227 {
1228 const TextDataBase::TextLine& line = renderData.textData->getLine(j);
1229 UINT32 writtenQuads = line.fillBuffer(renderData.page, tempVertices, tempUVs, indices, quadOffset, numQuads);
1230
1231 quadOffset += writtenQuads;
1232 }
1233
1234 for(UINT32 j = 0; j < shapeData.numIndices; j++)
1235 indices[j] += vertexOffset[typeIdx];
1236
1237 Vector3 worldSpacePos = text2DData.transform.multiplyAffine(text2DData.position);
1238 Vector2I screenPos = camera->worldToScreenPoint(worldSpacePos);
1239 screenPos.x -= renderData.textData->getWidth() / 2;
1240 screenPos.y -= renderData.textData->getHeight() / 2;
1241
1242 float z = camera->projectPoint(camera->worldToViewPoint(worldSpacePos)).z;
1243
1244 for (UINT32 j = 0; j < shapeData.numVertices; j++)
1245 {
1246 Vector3 vertexPos(screenPos.x + tempVertices[j].x, screenPos.y + tempVertices[j].y, z);
1247
1248 positionIter[typeIdx].addValue(vertexPos);
1249 textUVIter.addValue(tempUVs[j]);
1250 colorIter[typeIdx].addValue(text2DData.color.getAsRGBA());
1251 }
1252
1253 bs_stack_free(tempUVs);
1254 bs_stack_free(tempVertices);
1255
1256 vertexOffset[typeIdx] += shapeData.numVertices;
1257 indexOffset[typeIdx] += shapeData.numIndices;
1258 }
1259 }
1260 }
1261
1262 SPtr<Mesh> meshes[4];
1263 for(UINT32 i = 0; i < 4; i++)
1264 {
1265 if(meshData[i])
1266 meshes[i] = Mesh::_createPtr(meshData[i]);
1267 }
1268
1269 for(auto& entry : meshInfos)
1270 {
1271 switch(entry.type)
1272 {
1273 case MeshType::Solid:
1274 entry.mesh = meshes[0];
1275 break;
1276 case MeshType::Wire:
1277 entry.mesh = meshes[1];
1278 break;
1279 case MeshType::Line:
1280 entry.mesh = meshes[2];
1281 break;
1282 case MeshType::Text:
1283 entry.mesh = meshes[3];
1284 break;
1285 default: ;
1286 }
1287 }
1288
1289 return meshInfos;
1290 }
1291}
1292