• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "mesh_util.h"
17 
18 #include <algorithm>
19 
20 #include <3d/ecs/components/material_component.h>
21 #include <3d/ecs/components/name_component.h>
22 #include <3d/ecs/components/render_mesh_component.h>
23 #include <3d/ecs/components/uri_component.h>
24 #include <3d/ecs/systems/intf_node_system.h>
25 #include <3d/implementation_uids.h>
26 #include <3d/render/default_material_constants.h>
27 #include <3d/util/intf_mesh_builder.h>
28 #include <base/containers/vector.h>
29 #include <base/math/quaternion_util.h>
30 #include <base/math/vector.h>
31 #include <core/ecs/intf_ecs.h>
32 #include <core/intf_engine.h>
33 #include <core/log.h>
34 #include <core/namespace.h>
35 #include <core/plugin/intf_class_factory.h>
36 #include <render/device/intf_shader_manager.h>
37 #include <render/implementation_uids.h>
38 #include <render/intf_render_context.h>
39 
40 #include "util/uri_lookup.h"
41 
42 CORE3D_BEGIN_NAMESPACE()
43 using namespace BASE_NS;
44 using namespace CORE_NS;
45 using namespace RENDER_NS;
46 
47 namespace {
48 constexpr Math::Vec3 PLANE_NORM[6u] = {
49     Math::Vec3(0.0f, 1.0f, 0.0f),
50     Math::Vec3(0.0f, 1.0f, 0.0f),
51     Math::Vec3(0.0f, 1.0f, 0.0f),
52 
53     Math::Vec3(0.0f, 1.0f, 0.0f),
54     Math::Vec3(0.0f, 1.0f, 0.0f),
55     Math::Vec3(0.0f, 1.0f, 0.0f),
56 };
57 
58 constexpr Math::Vec2 PLANE_UV[6u] = {
59     Math::Vec2(1.0f, 1.0f),
60     Math::Vec2(1.0f, 0.0f),
61     Math::Vec2(0.0f, 1.0f),
62 
63     Math::Vec2(1.0f, 0.0f),
64     Math::Vec2(0.0f, 0.0f),
65     Math::Vec2(0.0f, 1.0f),
66 };
67 
68 constexpr uint16_t PLANE_IND[] = { 0u, 1u, 2u, 3u, 4u, 5u };
69 
70 constexpr Math::Vec3 CUBE_POS[8u] = {
71     Math::Vec3(-0.5f, -0.5f, -0.5f), //
72     Math::Vec3(0.5f, -0.5f, -0.5f),  //
73     Math::Vec3(0.5f, 0.5f, -0.5f),   //
74     Math::Vec3(-0.5f, 0.5f, -0.5f),  //
75     Math::Vec3(-0.5f, -0.5f, 0.5f),  //
76     Math::Vec3(0.5f, -0.5f, 0.5f),   //
77     Math::Vec3(0.5f, 0.5f, 0.5f),    //
78     Math::Vec3(-0.5f, 0.5f, 0.5f)    //
79 };
80 
81 constexpr Math::Vec2 CUBE_UV[4u] = { Math::Vec2(1.0f, 1.0f), Math::Vec2(0.0f, 1.0f), Math::Vec2(0.0f, 0.0f),
82     Math::Vec2(1.0f, 0.0f) };
83 
84 constexpr uint16_t CUBE_INDICES[6u * 6u] = {
85     0, 3, 1, 3, 2, 1, //
86     1, 2, 5, 2, 6, 5, //
87     5, 6, 4, 6, 7, 4, //
88     4, 7, 0, 7, 3, 0, //
89     3, 7, 2, 7, 6, 2, //
90     4, 0, 5, 0, 1, 5  //
91 };
92 
93 constexpr uint32_t CUBE_UV_INDICES[6u] = { 0, 3, 1, 3, 2, 1 };
94 
95 constexpr float TWO_PI = Math::PI * 2.0f;
96 
97 template<typename IndexType>
98 struct Geometry {
99     vector<Math::Vec3>& vertices;
100     vector<Math::Vec3>& normals;
101     vector<Math::Vec2>& uvs;
102     vector<IndexType>& indices;
103 };
104 
GenerateCubeGeometry(float width,float height,float depth,Geometry<uint16_t> geometry)105 void GenerateCubeGeometry(float width, float height, float depth, Geometry<uint16_t> geometry)
106 {
107     vector<Math::Vec3>& vertices = geometry.vertices;
108     vector<Math::Vec3>& normals = geometry.normals;
109     vector<Math::Vec2>& uvs = geometry.uvs;
110     vector<uint16_t>& indices = geometry.indices;
111 
112     vertices.reserve(countof(CUBE_INDICES));
113     normals.reserve(countof(CUBE_INDICES));
114     uvs.reserve(countof(CUBE_INDICES));
115     indices.reserve(countof(CUBE_INDICES));
116 
117     constexpr size_t triangleCount = countof(CUBE_INDICES) / 3u;
118     for (size_t i = 0; i < triangleCount; ++i) {
119         const size_t vertexIndex = i * 3u;
120 
121         const Math::Vec3 v0 = CUBE_POS[CUBE_INDICES[vertexIndex + 0u]];
122         const Math::Vec3 v1 = CUBE_POS[CUBE_INDICES[vertexIndex + 1u]];
123         const Math::Vec3 v2 = CUBE_POS[CUBE_INDICES[vertexIndex + 2u]];
124 
125         vertices.emplace_back(v0.x * width, v0.y * height, v0.z * depth);
126         vertices.emplace_back(v1.x * width, v1.y * height, v1.z * depth);
127         vertices.emplace_back(v2.x * width, v2.y * height, v2.z * depth);
128 
129         const Math::Vec3 normal = Math::Normalize(Math::Cross((v1 - v0), (v2 - v0)));
130         normals.append(3u, normal);
131 
132         uvs.emplace_back(
133             CUBE_UV[CUBE_UV_INDICES[(vertexIndex + 0u) % 6u]].x, CUBE_UV[CUBE_UV_INDICES[(vertexIndex + 0u) % 6u]].y);
134         uvs.emplace_back(
135             CUBE_UV[CUBE_UV_INDICES[(vertexIndex + 1u) % 6u]].x, CUBE_UV[CUBE_UV_INDICES[(vertexIndex + 1u) % 6u]].y);
136         uvs.emplace_back(
137             CUBE_UV[CUBE_UV_INDICES[(vertexIndex + 2u) % 6u]].x, CUBE_UV[CUBE_UV_INDICES[(vertexIndex + 2u) % 6u]].y);
138     }
139 
140     for (uint16_t i = 0u; i < countof(CUBE_INDICES); ++i) {
141         indices.push_back(i);
142     }
143 }
144 
GenerateSphereGeometry(float radius,uint32_t rings,uint32_t sectors,Geometry<uint32_t> geometry)145 void GenerateSphereGeometry(float radius, uint32_t rings, uint32_t sectors, Geometry<uint32_t> geometry)
146 {
147     vector<Math::Vec3>& vertices = geometry.vertices;
148     vector<Math::Vec3>& normals = geometry.normals;
149     vector<Math::Vec2>& uvs = geometry.uvs;
150     vector<uint32_t>& indices = geometry.indices;
151 
152     const size_t maxVertexCount = rings * sectors;
153     const size_t maxIndexCount = (rings - 1) * sectors * 6u;
154 
155     vertices.reserve(maxVertexCount);
156     normals.reserve(maxVertexCount);
157     uvs.reserve(maxVertexCount);
158     indices.reserve(maxIndexCount);
159 
160     const float r = 1.0f / static_cast<float>(rings - 1);
161     const float s = 1.0f / static_cast<float>(sectors - 1);
162 
163     constexpr float pi = Math::PI;
164     constexpr float halfPi = Math::PI * 0.5f;
165 
166     for (uint32_t ring = 0; ring < rings; ++ring) {
167         const auto ringF = static_cast<float>(ring);
168         for (uint32_t sector = 0; sector < sectors; ++sector) {
169             const auto sectorF = static_cast<float>(sector);
170             const float y = Math::sin(-halfPi + pi * ringF * r);
171             const float x = Math::cos(TWO_PI * sectorF * s) * Math::sin(pi * ringF * r);
172             const float z = Math::sin(TWO_PI * sectorF * s) * Math::sin(pi * ringF * r);
173 
174             vertices.emplace_back(x * radius, y * radius, z * radius);
175             normals.emplace_back(x, y, z);
176             uvs.emplace_back(sectorF * s, ringF * r);
177 
178             if (ring < rings - 1) {
179                 const uint32_t curRow = ring * sectors;
180                 const uint32_t nextRow = (ring + 1) * sectors;
181                 const uint32_t nextS = (sector + 1) % sectors;
182 
183                 indices.push_back(curRow + sector);
184                 indices.push_back(nextRow + sector);
185                 indices.push_back(nextRow + nextS);
186 
187                 indices.push_back(curRow + sector);
188                 indices.push_back(nextRow + nextS);
189                 indices.push_back(curRow + nextS);
190             }
191         }
192     }
193 }
194 
GenerateConeCap(float radius,float length,uint32_t sectors,Geometry<uint32_t> geometry,const vector<Math::Vec2> & unitCoords)195 void GenerateConeCap(
196     float radius, float length, uint32_t sectors, Geometry<uint32_t> geometry, const vector<Math::Vec2>& unitCoords)
197 {
198     vector<Math::Vec3>& vertices = geometry.vertices;
199     vector<Math::Vec3>& normals = geometry.normals;
200     vector<Math::Vec2>& uvs = geometry.uvs;
201     vector<uint32_t>& indices = geometry.indices;
202 
203     // Already generated vertices: tip + sectors
204     uint32_t startVertex = 1U + sectors;
205 
206     // Cap center vert.
207     const uint32_t bottomIndex = startVertex;
208     vertices.emplace_back(0.0f, 0.0f, length);
209     normals.emplace_back(0.0f, 0.0f, 1.0f);
210     uvs.emplace_back(0.5f, 0.5f);
211 
212     ++startVertex;
213 
214     // Cap ring and triangles.
215     for (uint32_t idx = 0; idx < sectors; ++idx) {
216         const uint32_t vertexIndex = startVertex + idx;
217 
218         const Math::Vec2& coords = unitCoords[idx];
219 
220         vertices.emplace_back(coords.x * radius, coords.y * radius, length);
221         normals.emplace_back(0.0f, 0.0f, 1.0f);
222 
223         float uvx = (coords.x + 1.0f) * 0.5f;
224         float uvy = 1.0f - (coords.y + 1.0f) * 0.5f;
225         uvs.emplace_back(uvx, uvy);
226 
227         const uint32_t nextVertexIndex = startVertex + ((idx + 1) % sectors);
228 
229         indices.push_back(vertexIndex);
230         indices.push_back(nextVertexIndex);
231         indices.push_back(bottomIndex);
232     }
233 }
234 
GenerateConeGeometry(float radius,float length,uint32_t sectors,Geometry<uint32_t> geometry)235 void GenerateConeGeometry(float radius, float length, uint32_t sectors, Geometry<uint32_t> geometry)
236 {
237     vector<Math::Vec3>& vertices = geometry.vertices;
238     vector<Math::Vec3>& normals = geometry.normals;
239     vector<Math::Vec2>& uvs = geometry.uvs;
240     vector<uint32_t>& indices = geometry.indices;
241 
242     const float s = (sectors > 0U) ? (1.0f / static_cast<float>(sectors)) : 1.0f;
243 
244     const size_t maxVertexCount = (2 * static_cast<size_t>(sectors)) + 2u;
245     const size_t maxIndexCount = static_cast<size_t>(sectors) * 6u;
246 
247     vertices.reserve(maxVertexCount);
248     normals.reserve(maxVertexCount);
249     uvs.reserve(maxVertexCount);
250     indices.reserve(maxIndexCount);
251 
252     vector<Math::Vec2> unitCoords;
253     unitCoords.reserve(sectors);
254 
255     vertices.emplace_back(0.0f, 0.0f, 0.0f);
256     normals.emplace_back(0.0f, 0.0f, -1.0f);
257     uvs.emplace_back(0.5f, 0.5f);
258 
259     // Bottom ring vertices and side triangles, with given radius
260     const uint32_t startVertex = 1U;
261     for (uint32_t idx = 0; idx < sectors; ++idx) {
262         const auto idxF = static_cast<float>(idx);
263         const float x = Math::cos(idxF * s * TWO_PI);
264         const float y = Math::sin(idxF * s * TWO_PI);
265         unitCoords.emplace_back(x, y);
266 
267         vertices.emplace_back(x * radius, y * radius, length);
268         normals.emplace_back(x, y, 0.f);
269 
270         float uvx = (x + 1.0f) * 0.5f;
271         float uvy = 1.0f - (y + 1.0f) * 0.5f;
272         uvs.emplace_back(uvx, uvy);
273 
274         const uint32_t v0 = 0;
275         const uint32_t v1 = startVertex + idx;
276         const uint32_t v2 = startVertex + ((idx + 1) % sectors);
277 
278         indices.push_back(v0);
279         indices.push_back(v2);
280         indices.push_back(v1);
281     }
282 
283     constexpr bool generateCapping = true;
284     if constexpr (generateCapping) {
285         GenerateConeCap(radius, length, sectors, geometry, unitCoords);
286     }
287 }
288 
GenerateTorusSlices(float minorRadius,uint32_t minorSectors,float minorStep)289 vector<Math::Vec3> GenerateTorusSlices(float minorRadius, uint32_t minorSectors, float minorStep)
290 {
291     vector<Math::Vec3> tubeSlice;
292     tubeSlice.reserve(minorSectors);
293     for (uint32_t tube = 0; tube < minorSectors; tube++) {
294         const float minorRadians = static_cast<float>(tube) * minorStep;
295         const float x = 0.0f;
296         const float y = Math::cos(minorRadians) * minorRadius;
297         const float z = Math::sin(minorRadians) * minorRadius;
298         tubeSlice.emplace_back(x, y, z);
299     }
300     return tubeSlice;
301 }
302 
GenerateTorusGeometry(float majorRadius,float minorRadius,uint32_t majorSectors,uint32_t minorSectors,Geometry<uint32_t> geometry)303 void GenerateTorusGeometry(
304     float majorRadius, float minorRadius, uint32_t majorSectors, uint32_t minorSectors, Geometry<uint32_t> geometry)
305 {
306     vector<Math::Vec3>& vertices = geometry.vertices;
307     vector<Math::Vec3>& normals = geometry.normals;
308     vector<Math::Vec2>& uvs = geometry.uvs;
309     vector<uint32_t>& indices = geometry.indices;
310 
311     const float majorStep = TWO_PI / static_cast<float>(majorSectors);
312     const float minorStep = TWO_PI / static_cast<float>(minorSectors);
313 
314     const size_t maxVertexCount = static_cast<size_t>(majorSectors) * static_cast<size_t>(minorSectors);
315     const size_t maxIndexCount = maxVertexCount * 6u;
316 
317     vertices.reserve(maxVertexCount);
318     normals.reserve(maxVertexCount);
319     uvs.reserve(maxVertexCount);
320     indices.reserve(maxIndexCount);
321 
322     const vector<Math::Vec3> tubeSlice = GenerateTorusSlices(minorRadius, minorSectors, minorStep);
323 
324     uint32_t currentVertexIndex = 0;
325     for (uint32_t ring = 0; ring < majorSectors; ring++) {
326         const float majorRadians = static_cast<float>(ring) * majorStep;
327         const auto rotation = Math::AngleAxis(majorRadians, { 0.0f, 1.0f, 0.0f });
328         const auto translation = Math::Vec3(0.0f, 0.0f, 1.0f) * majorRadius;
329 
330         for (uint32_t vertexIndex = 0; vertexIndex < minorSectors; vertexIndex++) {
331             const auto& ringVertex = tubeSlice[vertexIndex];
332 
333             const auto tubeCenter = rotation * translation;
334 
335             vertices.push_back(rotation * ringVertex + tubeCenter);
336 
337             normals.push_back(Math::Normalize(rotation * ringVertex));
338 
339             const float minorRadians = static_cast<float>(vertexIndex) * minorStep;
340             const float tx = 1.0f - Math::abs(majorRadians / TWO_PI * 2.0f - 1.0f);
341             const float ty = 1.0f - Math::abs(minorRadians / TWO_PI * 2.0f - 1.0f);
342             uvs.emplace_back(tx, ty);
343 
344             const uint32_t i0 = currentVertexIndex;
345             const uint32_t i1 = (i0 + 1) % maxVertexCount;
346             const uint32_t i2 = (i0 + minorSectors) % maxVertexCount;
347             const uint32_t i3 = (i2 + 1) % maxVertexCount;
348 
349             indices.push_back(i0);
350             indices.push_back(i1);
351             indices.push_back(i2);
352 
353             indices.push_back(i1);
354             indices.push_back(i3);
355             indices.push_back(i2);
356 
357             currentVertexIndex++;
358         }
359     }
360 }
361 } // namespace
362 
363 template<typename IndexType>
CalculateTangentBitangent(const array_view<const IndexType> & indices,const array_view<const Math::Vec3> & positions,const array_view<const Math::Vec2> & uvs,array_view<Math::Vec3> outTan,array_view<Math::Vec3> outBitan)364 void CalculateTangentBitangent(const array_view<const IndexType>& indices,
365     const array_view<const Math::Vec3>& positions, const array_view<const Math::Vec2>& uvs,
366     array_view<Math::Vec3> outTan, array_view<Math::Vec3> outBitan)
367 {
368     const auto indexCount = (indices.size() / 3U) * 3U;
369     for (size_t i = 0; i < indexCount; i += 3U) {
370         const IndexType aa = indices[i + 0U];
371         const IndexType bb = indices[i + 1U];
372         const IndexType cc = indices[i + 2U];
373 
374         const Math::Vec2& uv1 = uvs[aa];
375         const Math::Vec2& uv2 = uvs[bb];
376         const Math::Vec2& uv3 = uvs[cc];
377 
378         const auto st1 = uv2 - uv1;
379         const auto st2 = uv3 - uv1;
380 
381         auto d = Math::Cross(st1, st2);
382         if (Math::abs(d) < Math::EPSILON) {
383             d = Math::EPSILON;
384         }
385         const float r = 1.0f / d;
386 
387         const Math::Vec3& v1 = positions[aa];
388         const Math::Vec3& v2 = positions[bb];
389         const Math::Vec3& v3 = positions[cc];
390 
391         const auto e1 = v2 - v1;
392         const auto e2 = v3 - v1;
393 
394         const Math::Vec3 sdir { (e1 * st2.y - e2 * st1.y) * r };
395         outTan[aa] += sdir;
396         outTan[bb] += sdir;
397         outTan[cc] += sdir;
398 
399         const Math::Vec3 tdir { (e2 * st1.x - e1 * st2.x) * r };
400 
401         outBitan[aa] += tdir;
402         outBitan[bb] += tdir;
403         outBitan[cc] += tdir;
404     }
405 }
CalculateFinalTangent(const array_view<const Math::Vec3> & normals,array_view<const Math::Vec3> tan,array_view<const Math::Vec3> bitan,array_view<Math::Vec4> & outTangents)406 void CalculateFinalTangent(const array_view<const Math::Vec3>& normals, array_view<const Math::Vec3> tan,
407     array_view<const Math::Vec3> bitan, array_view<Math::Vec4>& outTangents)
408 {
409     for (size_t i = 0; i < normals.size(); i++) {
410         const Math::Vec3& n = normals[i];
411         const Math::Vec3& t = tan[i];
412 
413         // Gram-Schmidt orthogonalize
414         const Math::Vec3 tmp = Math::Normalize(t - n * Math::Dot(n, t));
415 
416         // Calculate handedness
417         const float w = (Math::Dot(Math::Cross(n, t), bitan[i]) < 0.0F) ? 1.0F : -1.0F;
418 
419         outTangents[i] = Math::Vec4(tmp.x, tmp.y, tmp.z, w);
420     }
421 }
422 
423 template<typename IndexType>
CalculateTangentImpl(const array_view<const IndexType> & indices,const array_view<const Math::Vec3> & positions,const array_view<const Math::Vec3> & normals,const array_view<const Math::Vec2> & uvs,array_view<Math::Vec4> & outTangents)424 void CalculateTangentImpl(const array_view<const IndexType>& indices, const array_view<const Math::Vec3>& positions,
425     const array_view<const Math::Vec3>& normals, const array_view<const Math::Vec2>& uvs,
426     array_view<Math::Vec4>& outTangents)
427 {
428     if (indices.size() < 3U) {
429         return;
430     }
431 
432     // http://www.terathon.com/code/tangent.html
433     vector<Math::Vec3> tan(positions.size(), { 0, 0, 0 });
434     vector<Math::Vec3> bitan(positions.size(), { 0, 0, 0 });
435 
436     CalculateTangentBitangent(indices, positions, uvs, tan, bitan);
437     CalculateFinalTangent(normals, tan, bitan, outTangents);
438 }
439 
440 template<typename IndexType>
CalculateTangentImplStrip(const array_view<const IndexType> & indices,const array_view<const Math::Vec3> & positions,const array_view<const Math::Vec3> & normals,const array_view<const Math::Vec2> & uvs,array_view<Math::Vec4> & outTangents)441 void CalculateTangentImplStrip(const array_view<const IndexType>& indices,
442     const array_view<const Math::Vec3>& positions, const array_view<const Math::Vec3>& normals,
443     const array_view<const Math::Vec2>& uvs, array_view<Math::Vec4>& outTangents)
444 {
445     if (indices.size() < 3U) {
446         return;
447     }
448     // http://www.terathon.com/code/tangent.html
449     vector<Math::Vec3> tan(positions.size(), { 0, 0, 0 });
450     vector<Math::Vec3> bitan(positions.size(), { 0, 0, 0 });
451 
452     CalculateTangentBitangent(array_view(indices.data(), 3U), positions, uvs, tan, bitan);
453 
454     for (size_t i = 2U; i < indices.size(); ++i) {
455         const IndexType aa = (i % 2U) ? indices[i - 1U] : indices[i - 2U];
456         const IndexType bb = (i % 2U) ? indices[i - 2U] : indices[i - 1U];
457         const IndexType cc = indices[i];
458 
459         const Math::Vec2& uv1 = uvs[aa];
460         const Math::Vec2& uv2 = uvs[bb];
461         const Math::Vec2& uv3 = uvs[cc];
462 
463         const auto st1 = uv2 - uv1;
464         const auto st2 = uv3 - uv1;
465 
466         auto d = Math::Cross(st1, st2);
467         if (Math::abs(d) < Math::EPSILON) {
468             d = Math::EPSILON;
469         }
470         const float r = 1.0f / d;
471 
472         const Math::Vec3& v1 = positions[aa];
473         const Math::Vec3& v2 = positions[bb];
474         const Math::Vec3& v3 = positions[cc];
475 
476         const auto e1 = v2 - v1;
477         const auto e2 = v3 - v1;
478 
479         const Math::Vec3 sdir { (e1 * st2.y - e2 * st1.y) * r };
480         tan[aa] += sdir;
481         tan[bb] += sdir;
482         tan[cc] += sdir;
483 
484         const Math::Vec3 tdir { (e2 * st1.x - e1 * st2.x) * r };
485 
486         bitan[aa] += tdir;
487         bitan[bb] += tdir;
488         bitan[cc] += tdir;
489     }
490     CalculateFinalTangent(normals, tan, bitan, outTangents);
491 }
492 
CalculateTangents(const array_view<const uint32_t> & indices,const array_view<const Math::Vec3> & positions,const array_view<const Math::Vec3> & normals,const array_view<const Math::Vec2> & uvs,PrimitiveTopology topology,array_view<Math::Vec4> outTangents)493 void MeshUtil::CalculateTangents(const array_view<const uint32_t>& indices,
494     const array_view<const Math::Vec3>& positions, const array_view<const Math::Vec3>& normals,
495     const array_view<const Math::Vec2>& uvs, PrimitiveTopology topology, array_view<Math::Vec4> outTangents)
496 {
497     if (topology == PrimitiveTopology::CORE_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP) {
498         CalculateTangentImplStrip(indices, positions, normals, uvs, outTangents);
499     } else {
500         CalculateTangentImpl(indices, positions, normals, uvs, outTangents);
501     }
502 }
503 
CalculateTangents(const array_view<const uint16_t> & indices,const array_view<const Math::Vec3> & positions,const array_view<const Math::Vec3> & normals,const array_view<const Math::Vec2> & uvs,PrimitiveTopology topology,array_view<Math::Vec4> outTangents)504 void MeshUtil::CalculateTangents(const array_view<const uint16_t>& indices,
505     const array_view<const Math::Vec3>& positions, const array_view<const Math::Vec3>& normals,
506     const array_view<const Math::Vec2>& uvs, PrimitiveTopology topology, array_view<Math::Vec4> outTangents)
507 {
508     if (topology == PrimitiveTopology::CORE_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP) {
509         CalculateTangentImplStrip(indices, positions, normals, uvs, outTangents);
510     } else {
511         CalculateTangentImpl(indices, positions, normals, uvs, outTangents);
512     }
513 }
514 
CalculateTangents(const array_view<const uint8_t> & indices,const array_view<const Math::Vec3> & positions,const array_view<const Math::Vec3> & normals,const array_view<const Math::Vec2> & uvs,PrimitiveTopology topology,array_view<Math::Vec4> outTangents)515 void MeshUtil::CalculateTangents(const array_view<const uint8_t>& indices,
516     const array_view<const Math::Vec3>& positions, const array_view<const Math::Vec3>& normals,
517     const array_view<const Math::Vec2>& uvs, PrimitiveTopology topology, array_view<Math::Vec4> outTangents)
518 {
519     if (topology == PrimitiveTopology::CORE_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP) {
520         CalculateTangentImplStrip(indices, positions, normals, uvs, outTangents);
521     } else {
522         CalculateTangentImpl(indices, positions, normals, uvs, outTangents);
523     }
524 }
525 
526 template<typename T>
FillData(array_view<const T> c)527 constexpr inline IMeshBuilder::DataBuffer FillData(array_view<const T> c) noexcept
528 {
529     Format format = BASE_FORMAT_UNDEFINED;
530     if constexpr (is_same_v<T, Math::Vec2>) {
531         format = BASE_FORMAT_R32G32_SFLOAT;
532     } else if constexpr (is_same_v<T, Math::Vec3>) {
533         format = BASE_FORMAT_R32G32B32_SFLOAT;
534     } else if constexpr (is_same_v<T, Math::Vec4>) {
535         format = BASE_FORMAT_R32G32B32A32_SFLOAT;
536     } else if constexpr (is_same_v<T, uint16_t>) {
537         format = BASE_FORMAT_R16_UINT;
538     } else if constexpr (is_same_v<T, uint32_t>) {
539         format = BASE_FORMAT_R32_UINT;
540     }
541     return IMeshBuilder::DataBuffer { format, sizeof(T),
542         { reinterpret_cast<const uint8_t*>(c.data()), c.size() * sizeof(T) } };
543 }
544 
545 template<typename T, size_t N>
FillData(const T (& c)[N])546 constexpr inline IMeshBuilder::DataBuffer FillData(const T (&c)[N]) noexcept
547 {
548     return FillData(array_view(c, N));
549 }
550 
551 template<typename T>
FillData(const vector<T> & c)552 constexpr inline IMeshBuilder::DataBuffer FillData(const vector<T>& c) noexcept
553 {
554     return FillData(array_view<const T> { c });
555 }
556 
GeneratePlaneMesh(const IEcs & ecs,const string_view name,Entity material,float width,float depth)557 Entity MeshUtil::GeneratePlaneMesh(const IEcs& ecs, const string_view name, Entity material, float width, float depth)
558 {
559     const float extentX = width * 0.5f;
560     const float extentZ = depth * 0.5f;
561 
562     const Math::Vec3 pos[6u] = {
563         Math::Vec3(-extentX, 0.0f, -extentZ),
564         Math::Vec3(-extentX, 0.0f, extentZ),
565         Math::Vec3(extentX, 0.0f, -extentZ),
566 
567         Math::Vec3(-extentX, 0.0f, extentZ),
568         Math::Vec3(extentX, 0.0f, extentZ),
569         Math::Vec3(extentX, 0.0f, -extentZ),
570     };
571     vector<Math::Vec4> tangents(countof(pos));
572     {
573         constexpr auto indicesView = array_view(PLANE_IND);
574         const auto positionsView = array_view(pos);
575         constexpr auto normalsView = array_view(PLANE_NORM);
576         constexpr auto uvsView = array_view(PLANE_UV);
577 
578         CalculateTangents(indicesView, positionsView, normalsView, uvsView,
579             PrimitiveTopology::CORE_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, tangents);
580     }
581 
582     IMeshBuilder::Submesh submesh;
583     submesh.material = material;
584     submesh.vertexCount = 6u;
585     submesh.indexCount = 6u;
586     submesh.indexType = CORE_INDEX_TYPE_UINT16;
587     submesh.tangents = true;
588 
589     auto builder = InitializeBuilder(submesh);
590 
591     auto positionData = FillData(pos);
592     auto normalData = FillData(PLANE_NORM);
593     auto uvData = FillData(PLANE_UV);
594     auto tangentData = FillData(tangents);
595     IMeshBuilder::DataBuffer dummy {};
596     builder->SetVertexData(0, positionData, normalData, uvData, dummy, tangentData, dummy);
597 
598     builder->CalculateAABB(0, positionData);
599 
600     auto indices = FillData(PLANE_IND);
601     builder->SetIndexData(0, indices);
602 
603     return CreateMesh(ecs, *builder, name);
604 }
605 
GenerateSphereMesh(const IEcs & ecs,const string_view name,Entity material,float radius,uint32_t rings,uint32_t sectors)606 Entity MeshUtil::GenerateSphereMesh(
607     const IEcs& ecs, const string_view name, Entity material, float radius, uint32_t rings, uint32_t sectors)
608 {
609     vector<Math::Vec3> vertices;
610     vector<Math::Vec3> normals;
611     vector<Math::Vec2> uvs;
612     vector<uint32_t> indices;
613     GenerateSphereGeometry(radius, rings, sectors, { vertices, normals, uvs, indices });
614 
615     vector<Math::Vec4> tangents(vertices.size());
616     CalculateTangents(
617         indices, vertices, normals, uvs, PrimitiveTopology::CORE_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, tangents);
618 
619     IMeshBuilder::Submesh submesh;
620     submesh.material = material;
621     submesh.vertexCount = static_cast<uint32_t>(vertices.size());
622     submesh.indexCount = static_cast<uint32_t>(indices.size());
623     submesh.indexType = submesh.vertexCount <= UINT16_MAX ? CORE_INDEX_TYPE_UINT16 : CORE_INDEX_TYPE_UINT32;
624     submesh.tangents = true;
625 
626     auto builder = InitializeBuilder(submesh);
627 
628     auto positionData = FillData(vertices);
629     auto normalData = FillData(normals);
630     auto uvData = FillData(uvs);
631     auto tangentData = FillData(tangents);
632     IMeshBuilder::DataBuffer dummy {};
633     builder->SetVertexData(0, positionData, normalData, uvData, dummy, tangentData, dummy);
634 
635     builder->CalculateAABB(0, positionData);
636 
637     auto indexData = FillData(indices);
638     builder->SetIndexData(0, indexData);
639 
640     return CreateMesh(ecs, *builder, name);
641 }
642 
GenerateConeMesh(const IEcs & ecs,const string_view name,Entity material,float radius,float length,uint32_t sectors)643 Entity MeshUtil::GenerateConeMesh(
644     const IEcs& ecs, const string_view name, Entity material, float radius, float length, uint32_t sectors)
645 {
646     vector<Math::Vec3> vertices;
647     vector<Math::Vec3> normals;
648     vector<Math::Vec2> uvs;
649     vector<uint32_t> indices;
650     GenerateConeGeometry(radius, length, sectors, { vertices, normals, uvs, indices });
651 
652     IMeshBuilder::Submesh submesh;
653     submesh.material = material;
654     submesh.vertexCount = static_cast<uint32_t>(vertices.size());
655     submesh.indexCount = static_cast<uint32_t>(indices.size());
656     submesh.indexType = submesh.vertexCount <= UINT16_MAX ? CORE_INDEX_TYPE_UINT16 : CORE_INDEX_TYPE_UINT32;
657     submesh.tangents = true;
658 
659     vector<Math::Vec4> tangents(vertices.size());
660     CalculateTangents(
661         indices, vertices, normals, uvs, PrimitiveTopology::CORE_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, tangents);
662 
663     auto builder = InitializeBuilder(submesh);
664 
665     auto positionData = FillData(vertices);
666     auto normalData = FillData(normals);
667     auto uvData = FillData(uvs);
668     auto tangentData = FillData(tangents);
669     IMeshBuilder::DataBuffer dummy {};
670     builder->SetVertexData(0, positionData, normalData, uvData, dummy, tangentData, dummy);
671 
672     builder->CalculateAABB(0, positionData);
673 
674     auto indexData = FillData(indices);
675     builder->SetIndexData(0, indexData);
676 
677     return CreateMesh(ecs, *builder, name);
678 }
679 
GenerateTorusMesh(const IEcs & ecs,const string_view name,Entity material,float majorRadius,float minorRadius,uint32_t majorSectors,uint32_t minorSectors)680 Entity MeshUtil::GenerateTorusMesh(const IEcs& ecs, const string_view name, Entity material, float majorRadius,
681     float minorRadius, uint32_t majorSectors, uint32_t minorSectors)
682 {
683     vector<Math::Vec3> vertices;
684     vector<Math::Vec3> normals;
685     vector<Math::Vec2> uvs;
686     vector<uint32_t> indices;
687     GenerateTorusGeometry(majorRadius, minorRadius, majorSectors, minorSectors, { vertices, normals, uvs, indices });
688 
689     vector<Math::Vec4> tangents(vertices.size());
690     CalculateTangents(
691         indices, vertices, normals, uvs, PrimitiveTopology::CORE_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, tangents);
692 
693     IMeshBuilder::Submesh submesh;
694     submesh.material = material;
695     submesh.vertexCount = static_cast<uint32_t>(vertices.size());
696     submesh.indexCount = static_cast<uint32_t>(indices.size());
697     submesh.indexType = submesh.vertexCount <= UINT16_MAX ? CORE_INDEX_TYPE_UINT16 : CORE_INDEX_TYPE_UINT32;
698     submesh.tangents = true;
699 
700     auto builder = InitializeBuilder(submesh);
701 
702     auto positionData = FillData(vertices);
703     auto normalData = FillData(normals);
704     auto uvData = FillData(uvs);
705     auto tangentData = FillData(tangents);
706     IMeshBuilder::DataBuffer dummy {};
707     builder->SetVertexData(0, positionData, normalData, uvData, dummy, tangentData, dummy);
708 
709     builder->CalculateAABB(0, positionData);
710 
711     auto indexData = FillData(indices);
712     builder->SetIndexData(0, indexData);
713 
714     return CreateMesh(ecs, *builder, name);
715 }
716 
GenerateCubeMesh(const IEcs & ecs,const string_view name,Entity material,float width,float height,float depth)717 Entity MeshUtil::GenerateCubeMesh(
718     const IEcs& ecs, const string_view name, Entity material, float width, float height, float depth)
719 {
720     vector<Math::Vec3> positions;
721     vector<Math::Vec3> normals;
722     vector<Math::Vec2> uvs;
723     vector<uint16_t> indices;
724     GenerateCubeGeometry(width, height, depth, { positions, normals, uvs, indices });
725 
726     vector<Math::Vec4> tangents(positions.size());
727     CalculateTangents(
728         indices, positions, normals, uvs, PrimitiveTopology::CORE_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, tangents);
729 
730     IMeshBuilder::Submesh submesh;
731     submesh.material = material;
732     submesh.vertexCount = static_cast<uint32_t>(countof(CUBE_INDICES));
733     submesh.indexCount = static_cast<uint32_t>(countof(CUBE_INDICES));
734     submesh.indexType = CORE_INDEX_TYPE_UINT16;
735     submesh.tangents = true;
736 
737     auto builder = InitializeBuilder(submesh);
738 
739     auto positionData = FillData(positions);
740     auto normalData = FillData(normals);
741     auto uvData = FillData(uvs);
742     auto tangentData = FillData(tangents);
743     IMeshBuilder::DataBuffer dummy {};
744     builder->SetVertexData(0, positionData, normalData, uvData, dummy, tangentData, dummy);
745 
746     builder->CalculateAABB(0, positionData);
747 
748     auto indexData = FillData(indices);
749     builder->SetIndexData(0, indexData);
750 
751     return CreateMesh(ecs, *builder, name);
752 }
753 
GenerateEntity(const IEcs & ecs,const string_view name,Entity meshHandle)754 Entity MeshUtil::GenerateEntity(const IEcs& ecs, const string_view name, Entity meshHandle)
755 {
756     INodeSystem* nodesystem = GetSystem<INodeSystem>(ecs);
757     CORE_ASSERT(nodesystem);
758 
759     // Create node to scene.
760     ISceneNode* node = nodesystem->CreateNode();
761     if (!node) {
762         return Entity {};
763     }
764 
765     node->SetName(name);
766 
767     // Add render mesh component.
768     IRenderMeshComponentManager* renderMeshManager = GetManager<IRenderMeshComponentManager>(ecs);
769     CORE_ASSERT(renderMeshManager);
770 
771     RenderMeshComponent component = CreateComponent(*renderMeshManager, node->GetEntity());
772     component.mesh = meshHandle;
773     renderMeshManager->Set(node->GetEntity(), component);
774 
775     return node->GetEntity();
776 }
777 
GenerateCube(const IEcs & ecs,const string_view name,Entity material,float width,float height,float depth)778 Entity MeshUtil::GenerateCube(
779     const IEcs& ecs, const string_view name, Entity material, float width, float height, float depth)
780 {
781     const Entity meshHandle = GenerateCubeMesh(ecs, name, material, width, height, depth);
782     return GenerateEntity(ecs, name, meshHandle);
783 }
784 
GeneratePlane(const IEcs & ecs,const string_view name,Entity material,float width,float depth)785 Entity MeshUtil::GeneratePlane(const IEcs& ecs, const string_view name, Entity material, float width, float depth)
786 {
787     const Entity meshHandle = GeneratePlaneMesh(ecs, name, material, width, depth);
788     return GenerateEntity(ecs, name, meshHandle);
789 }
790 
GenerateSphere(const IEcs & ecs,const string_view name,Entity material,float radius,uint32_t rings,uint32_t sectors)791 Entity MeshUtil::GenerateSphere(
792     const IEcs& ecs, const string_view name, Entity material, float radius, uint32_t rings, uint32_t sectors)
793 {
794     const Entity meshHandle = GenerateSphereMesh(ecs, name, material, radius, rings, sectors);
795     return GenerateEntity(ecs, name, meshHandle);
796 }
797 
GenerateCone(const IEcs & ecs,const string_view name,Entity material,float radius,float length,uint32_t sectors)798 Entity MeshUtil::GenerateCone(
799     const IEcs& ecs, const string_view name, Entity material, float radius, float length, uint32_t sectors)
800 {
801     const Entity meshHandle = GenerateConeMesh(ecs, name, material, radius, length, sectors);
802     return GenerateEntity(ecs, name, meshHandle);
803 }
804 
GenerateTorus(const IEcs & ecs,const string_view name,Entity material,float majorRadius,float minorRadius,uint32_t majorSectors,uint32_t minorSectors)805 Entity MeshUtil::GenerateTorus(const IEcs& ecs, const string_view name, Entity material, float majorRadius,
806     float minorRadius, uint32_t majorSectors, uint32_t minorSectors)
807 {
808     const Entity meshHandle =
809         GenerateTorusMesh(ecs, name, material, majorRadius, minorRadius, majorSectors, minorSectors);
810     return GenerateEntity(ecs, name, meshHandle);
811 }
812 
InitializeBuilder(const IMeshBuilder::Submesh & submesh) const813 IMeshBuilder::Ptr MeshUtil::InitializeBuilder(const IMeshBuilder::Submesh& submesh) const
814 {
815     IMeshBuilder::Ptr builder;
816     if (IClassRegister* classRegister = factory_.GetInterface<IClassRegister>(); classRegister) {
817         auto renderContext = GetInstance<IRenderContext>(*classRegister, UID_RENDER_CONTEXT);
818         IShaderManager& shaderManager = renderContext->GetDevice().GetShaderManager();
819         const VertexInputDeclarationView vertexInputDeclaration =
820             shaderManager.GetVertexInputDeclarationView(shaderManager.GetVertexInputDeclarationHandle(
821                 DefaultMaterialShaderConstants::VERTEX_INPUT_DECLARATION_FORWARD));
822         builder = CreateInstance<IMeshBuilder>(*renderContext, UID_MESH_BUILDER);
823         builder->Initialize(vertexInputDeclaration, 1);
824 
825         builder->AddSubmesh(submesh);
826         builder->Allocate();
827     }
828 
829     return builder;
830 }
831 
CreateMesh(const IEcs & ecs,const IMeshBuilder & builder,const string_view name) const832 Entity MeshUtil::CreateMesh(const IEcs& ecs, const IMeshBuilder& builder, const string_view name) const
833 {
834     auto meshEntity = builder.CreateMesh(const_cast<IEcs&>(ecs));
835     if (!name.empty()) {
836         GetManager<IUriComponentManager>(ecs)->Set(meshEntity, { string(name) });
837         GetManager<INameComponentManager>(ecs)->Set(meshEntity, { string(name) });
838     }
839     return meshEntity;
840 }
841 
MeshUtil(IClassFactory & factory)842 MeshUtil::MeshUtil(IClassFactory& factory) : factory_(factory) {}
843 CORE3D_END_NAMESPACE()
844