1 /*
2 * Copyright (c) 2024 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_builder.h"
17
18 #include <algorithm>
19 #include <optional>
20 #include <securec.h>
21
22 #include <3d/ecs/components/mesh_component.h>
23 #include <3d/ecs/components/render_handle_component.h>
24 #include <3d/util/intf_picking.h>
25 #include <base/containers/type_traits.h>
26 #include <base/math/float_packer.h>
27 #include <base/math/mathf.h>
28 #include <base/math/vector_util.h>
29 #include <core/ecs/entity.h>
30 #include <core/ecs/entity_reference.h>
31 #include <core/ecs/intf_ecs.h>
32 #include <core/ecs/intf_entity_manager.h>
33 #include <core/intf_engine.h>
34 #include <core/log.h>
35 #include <core/namespace.h>
36 #include <core/plugin/intf_class_factory.h>
37 #include <core/plugin/intf_class_register.h>
38 #include <core/property/intf_property_handle.h>
39 #include <render/datastore/intf_render_data_store_default_staging.h>
40 #include <render/datastore/intf_render_data_store_manager.h>
41 #include <render/device/intf_device.h>
42 #include <render/device/intf_gpu_resource_manager.h>
43 #include <render/implementation_uids.h>
44 #include <render/intf_render_context.h>
45
46 #include "util/mesh_util.h"
47
48 namespace {
49 #include "3d/shaders/common/morph_target_structs.h"
50 } // namespace
51
52 CORE3D_BEGIN_NAMESPACE()
53 using namespace BASE_NS;
54 using namespace CORE_NS;
55 using namespace RENDER_NS;
56
57 namespace {
58 constexpr uint32_t BUFFER_ALIGN = 0x100; // on Nvidia = 0x20, on Mali and Intel = 0x10, SBO on Mali = 0x100
59 constexpr auto POSITION_FORMAT = BASE_FORMAT_R32G32B32_SFLOAT;
60 #if defined(CORE_MORPH_USE_PACKED_NOR_TAN)
61 constexpr auto NORMAL_FORMAT = BASE_FORMAT_R16G16B16_SFLOAT;
62 constexpr auto TANGENT_FORMAT = BASE_FORMAT_R16G16B16A16_SFLOAT;
63 #else
64 constexpr auto NORMAL_FORMAT = BASE_FORMAT_R32G32B32_SFLOAT;
65 constexpr auto TANGENT_FORMAT = BASE_FORMAT_R32G32B32_SFLOAT;
66 #endif
67
68 constexpr auto R = 0;
69 constexpr auto G = 1;
70 constexpr auto B = 2; // 2 : idx
71
72 constexpr auto RG = 2; // 2 : idx
73 constexpr auto RGB = 3; // 3 : idx
74 constexpr auto RGBA = 4; // 4 : idx
75
76 template<typename Container>
77 using ContainerValueType = typename remove_reference_t<Container>::value_type;
78
79 template<typename Container>
80 constexpr const auto SIZE_OF_VALUE_TYPE_V = sizeof(ContainerValueType<Container>);
81
Align(size_t value,size_t align)82 constexpr inline size_t Align(size_t value, size_t align) noexcept
83 {
84 if (align == 0U) {
85 return value;
86 }
87
88 return ((value + align - 1U) / align) * align;
89 }
90
GetVertexAttributeDescription(uint32_t location,const array_view<const VertexInputDeclaration::VertexInputAttributeDescription> & attributeDescriptions)91 const VertexInputDeclaration::VertexInputAttributeDescription* GetVertexAttributeDescription(uint32_t location,
92 const array_view<const VertexInputDeclaration::VertexInputAttributeDescription>& attributeDescriptions) noexcept
93 {
94 const auto cmpLocationIndex = [location](auto const& attribute) { return attribute.location == location; };
95 if (const auto pos = std::find_if(attributeDescriptions.begin(), attributeDescriptions.end(), cmpLocationIndex);
96 pos != attributeDescriptions.end()) {
97 return pos.ptr();
98 }
99
100 return nullptr;
101 }
102
GetVertexBindingeDescription(uint32_t bindingIndex,const array_view<const VertexInputDeclaration::VertexInputBindingDescription> & bindingDescriptions)103 const VertexInputDeclaration::VertexInputBindingDescription* GetVertexBindingeDescription(uint32_t bindingIndex,
104 const array_view<const VertexInputDeclaration::VertexInputBindingDescription>& bindingDescriptions) noexcept
105 {
106 const auto cmpBindingIndex = [bindingIndex](auto const& binding) { return binding.binding == bindingIndex; };
107 if (const auto pos = std::find_if(bindingDescriptions.begin(), bindingDescriptions.end(), cmpBindingIndex);
108 pos != bindingDescriptions.end()) {
109 return pos.ptr();
110 }
111
112 return nullptr;
113 }
114
115 struct Intermediate {
116 float data[RGBA];
117 };
118
119 using ToIntermediate = float (*)(const uint8_t* src) noexcept;
120 using FromIntermediate = void (*)(uint8_t* dst, float) noexcept;
121
122 struct FormatProperties {
123 size_t componentCount;
124 size_t componentByteSize;
125 Format format;
126 bool isNormalized;
127 bool isSigned;
128 ToIntermediate toIntermediate;
129 FromIntermediate fromIntermediate;
130 };
131
132 struct OutputBuffer {
133 BASE_NS::Format format;
134 uint32_t stride;
135 BASE_NS::array_view<uint8_t> buffer;
136 };
137
138 template<typename T, size_t N, size_t M>
GatherMin(T (& minimum)[N],const T (& value)[M])139 inline void GatherMin(T (&minimum)[N], const T (&value)[M])
140 {
141 for (size_t i = 0; i < Math::min(N, M); ++i) {
142 minimum[i] = Math::min(minimum[i], value[i]);
143 }
144 }
145
146 template<typename T, size_t N, size_t M>
GatherMax(T (& minimum)[N],const T (& value)[M])147 inline void GatherMax(T (&minimum)[N], const T (&value)[M])
148 {
149 for (size_t i = 0; i < Math::min(N, M); ++i) {
150 minimum[i] = Math::max(minimum[i], value[i]);
151 }
152 }
153
154 // floating point to signed normalized integer
155 template<typename T, typename = enable_if_t<is_signed_v<T>>>
Snorm(float v)156 constexpr inline T Snorm(float v) noexcept
157 {
158 const float round = v >= 0.f ? +.5f : -.5f; // 0.5 : half
159 v = v < -1.f ? -1.f : (v > 1.f ? 1.f : v);
160 return static_cast<T>(v * static_cast<float>(std::numeric_limits<T>::max()) + round);
161 }
162
163 // signed normalized integer to floating point
164 template<typename T, typename = enable_if_t<is_signed_v<T> && is_integral_v<T>>>
Snorm(T v)165 constexpr inline float Snorm(T v) noexcept
166 {
167 return static_cast<float>(v) / static_cast<float>(std::numeric_limits<T>::max());
168 }
169
170 // floating point to unsigned normalized integer
171 template<typename T, typename = enable_if_t<is_unsigned_v<T>>>
Unorm(float v)172 constexpr inline T Unorm(float v) noexcept
173 {
174 v = v < 0.f ? 0.f : (v > 1.f ? 1.f : v);
175 return static_cast<T>(v * static_cast<float>(std::numeric_limits<T>::max()) + 0.5f); // 0.5 : half
176 }
177
178 // unsigned normalized integer to floating point
179 template<typename T, typename = enable_if_t<is_unsigned_v<T> && is_integral_v<T>>>
Unorm(T v)180 constexpr inline float Unorm(T v) noexcept
181 {
182 return static_cast<float>(v) / static_cast<float>(std::numeric_limits<T>::max());
183 }
184
185 // floating point to signed integer
186 template<typename T, typename = enable_if_t<is_signed_v<T>>>
Sint(float v)187 constexpr inline T Sint(float v) noexcept
188 {
189 const float round = v >= 0.f ? +.5f : -.5f; // 0.5 : half
190 constexpr auto l = static_cast<float>(std::numeric_limits<T>::lowest());
191 constexpr auto h = static_cast<float>(std::numeric_limits<T>::max());
192 v = v < l ? l : (v > h ? h : v);
193 return static_cast<T>(v + round);
194 }
195
196 // signed integer to floating point
197 template<typename T, typename = enable_if_t<is_signed_v<T> && is_integral_v<T>>>
Sint(T v)198 constexpr inline float Sint(T v) noexcept
199 {
200 return static_cast<float>(v);
201 }
202
203 // floating point to unsigned integer
204 template<typename T, typename = enable_if_t<is_unsigned_v<T>>>
Uint(float v)205 constexpr inline T Uint(float v) noexcept
206 {
207 constexpr auto h = static_cast<float>(std::numeric_limits<T>::max());
208 v = v < 0.f ? 0.f : (v > h ? h : v);
209 return static_cast<T>(v + 0.5f); // 0.5 : half
210 }
211
212 // unsigned integer to floating point
213 template<typename T, typename = enable_if_t<is_unsigned_v<T> && is_integral_v<T>>>
Uint(T v)214 constexpr inline float Uint(T v) noexcept
215 {
216 return static_cast<float>(v);
217 }
218
219 // helpers for ingeter - integer conversions
220 template<typename T, typename U>
221 struct IntegerNorm {
222 using InType = T;
223 using OutType = U;
224
convert__anon7f2c29810211::IntegerNorm225 static U convert(T v) noexcept
226 {
227 constexpr auto dstH = std::numeric_limits<U>::max();
228 constexpr auto srcH = std::numeric_limits<T>::max();
229 if constexpr (is_signed_v<T>) {
230 auto round = v >= 0 ? (srcH - 1) : (-srcH + 1);
231 return static_cast<U>(((static_cast<int32_t>(v) * dstH) + round) / srcH);
232 } else {
233 return static_cast<U>(((static_cast<uint32_t>(v) * dstH) + (srcH - 1)) / srcH);
234 }
235 }
236 };
237
238 template<typename T, typename U>
239 struct IntegerToInt {
240 using InType = T;
241 using OutType = U;
242
convert__anon7f2c29810211::IntegerToInt243 static U convert(T v) noexcept
244 {
245 return static_cast<U>(v);
246 }
247 };
248
249 template<typename Converter, size_t components>
Convert(uint8_t * dstPtr,size_t dstStride,const uint8_t * srcPtr,size_t srcStride,size_t elements)250 void Convert(uint8_t* dstPtr, size_t dstStride, const uint8_t* srcPtr, size_t srcStride, size_t elements)
251 {
252 while (elements--) {
253 for (auto i = 0U; i < components; ++i) {
254 reinterpret_cast<typename Converter::OutType*>(dstPtr)[i] = static_cast<typename Converter::OutType>(
255 Converter::convert(reinterpret_cast<const typename Converter::InType*>(srcPtr)[i]));
256 }
257 srcPtr += srcStride;
258 dstPtr += dstStride;
259 }
260 }
261
262 // helpers for type T - float - type U conversions
263 template<typename T>
264 struct Norm {
265 using Type = T;
266
To__anon7f2c29810211::Norm267 static T To(float f) noexcept
268 {
269 if constexpr (is_signed_v<T>) {
270 return Snorm<T>(f);
271 } else {
272 return Unorm<T>(f);
273 }
274 }
275
From__anon7f2c29810211::Norm276 static float From(T v) noexcept
277 {
278 if constexpr (is_signed_v<T>) {
279 return Snorm<T>(v);
280 } else {
281 return Unorm<T>(v);
282 }
283 }
284 };
285
286 template<typename T>
287 struct Int {
288 using Type = T;
289
To__anon7f2c29810211::Int290 static T To(float f)
291 {
292 if constexpr (is_signed_v<T>) {
293 return Sint<T>(f);
294 } else {
295 return Uint<T>(f);
296 }
297 }
298
From__anon7f2c29810211::Int299 static float From(T v)
300 {
301 if constexpr (is_signed_v<T>) {
302 return Sint<T>(v);
303 } else {
304 return Uint<T>(v);
305 }
306 }
307 };
308
309 template<typename T>
310 struct Float {
311 using Type = T;
312
To__anon7f2c29810211::Float313 static T To(float f)
314 {
315 if constexpr (is_same_v<T, float>) {
316 return f;
317 }
318 if constexpr (sizeof(T) == sizeof(uint16_t)) {
319 return Math::F32ToF16(f);
320 }
321 }
322
From__anon7f2c29810211::Float323 static float From(T v)
324 {
325 if constexpr (is_same_v<T, float>) {
326 return v;
327 }
328 if constexpr (sizeof(T) == sizeof(uint16_t)) {
329 return Math::F16ToF32(v);
330 }
331 }
332 };
333
334 template<typename SourceFn, typename DestFn, size_t components>
Convert(uint8_t * dstPtr,size_t dstStride,const uint8_t * srcPtr,size_t srcStride,size_t elements)335 void Convert(uint8_t* dstPtr, size_t dstStride, const uint8_t* srcPtr, size_t srcStride, size_t elements)
336 {
337 while (elements--) {
338 for (auto i = 0U; i < components; ++i) {
339 reinterpret_cast<typename DestFn::Type*>(dstPtr)[i] =
340 DestFn::To(SourceFn::From(reinterpret_cast<const typename SourceFn::Type*>(srcPtr)[i]));
341 }
342 srcPtr += srcStride;
343 dstPtr += dstStride;
344 }
345 }
346
347 template<typename SourceFn>
From(const uint8_t * src)348 float From(const uint8_t* src) noexcept
349 {
350 return SourceFn::From(reinterpret_cast<const typename SourceFn::Type*>(src)[R]);
351 }
352
353 template<typename DestFn>
To(uint8_t * dst,float f)354 void To(uint8_t* dst, float f) noexcept
355 {
356 reinterpret_cast<typename DestFn::Type*>(dst)[R] = DestFn::To(f);
357 }
358
359 static constexpr const FormatProperties DATA_FORMATS[] = {
360 { 0, 0, BASE_FORMAT_UNDEFINED, false, false, nullptr, nullptr },
361
362 { 1, 1, BASE_FORMAT_R8_UNORM, true, false, From<Norm<uint8_t>>, To<Norm<uint8_t>> },
363 { 1, 1, BASE_FORMAT_R8_SNORM, true, true, From<Norm<int8_t>>, To<Norm<int8_t>> },
364 { 1, 1, BASE_FORMAT_R8_UINT, false, false, From<Int<uint8_t>>, To<Int<uint8_t>> },
365
366 { 3, 1, BASE_FORMAT_R8G8B8_SNORM, true, false, From<Norm<int8_t>>, To<Norm<int8_t>> }, // 3 : param
367
368 { 4, 1, BASE_FORMAT_R8G8B8A8_UNORM, true, false, From<Norm<uint8_t>>, To<Norm<uint8_t>> }, // 4 : param
369 { 4, 1, BASE_FORMAT_R8G8B8A8_SNORM, true, false, From<Norm<int8_t>>, To<Norm<int8_t>> }, // 4 : param
370 { 4, 1, BASE_FORMAT_R8G8B8A8_UINT, false, false, From<Int<uint8_t>>, To<Int<uint8_t>> }, // 4 : param
371
372 { 1, 2, BASE_FORMAT_R16_UINT, false, false, From<Int<uint16_t>>, To<Int<uint16_t>> }, // 2 : param
373
374 { 2, 2, BASE_FORMAT_R16G16_UNORM, true, false, From<Norm<uint16_t>>, To<Norm<uint16_t>> }, // 2 : param
375 { 2, 2, BASE_FORMAT_R16G16_UINT, false, true, From<Int<uint16_t>>, To<Int<uint16_t>> }, // 2 : param
376 { 2, 2, BASE_FORMAT_R16G16_SFLOAT, false, true, From<Float<uint16_t>>, To<Float<uint16_t>> }, // 2 : param
377
378 { 3, 2, BASE_FORMAT_R16G16B16_UINT, true, true, From<Int<uint16_t>>, To<Int<uint16_t>> }, // 3,2 : param
379 { 3, 2, BASE_FORMAT_R16G16B16_SINT, true, true, From<Int<int16_t>>, To<Int<int16_t>> }, // 3,2 : param
380 { 3, 2, BASE_FORMAT_R16G16B16_SFLOAT, false, true, From<Float<uint16_t>>, To<Float<uint16_t>> }, // 3,2 param
381
382 { 4, 2, BASE_FORMAT_R16G16B16A16_UNORM, true, true, From<Norm<uint16_t>>, To<Norm<uint16_t>> }, // 4,2 : param
383 { 4, 2, BASE_FORMAT_R16G16B16A16_SNORM, true, true, From<Norm<int16_t>>, To<Norm<int16_t>> }, // 4,2 : param
384 { 4, 2, BASE_FORMAT_R16G16B16A16_UINT, false, false, From<Int<uint16_t>>, To<Int<uint16_t>> }, // 4,2 : param
385 { 4, 2, BASE_FORMAT_R16G16B16A16_SFLOAT, false, true, From<Float<uint16_t>>, To<Float<uint16_t>> }, // 4, 2 : param
386
387 { 1, 4, BASE_FORMAT_R32_UINT, false, false, From<Int<uint32_t>>, To<Int<uint32_t>> }, // 4 : param
388 { 2, 4, BASE_FORMAT_R32G32_SFLOAT, false, true, From<Float<float>>, To<Float<float>> }, // 2,4 : param
389 { 3, 4, BASE_FORMAT_R32G32B32_SFLOAT, false, true, From<Float<float>>, To<Float<float>> }, // 3,4 : param
390 { 4, 4, BASE_FORMAT_R32G32B32A32_SFLOAT, false, true, From<Float<float>>, To<Float<float>> }, // 4 : param
391 };
392
393 template<class It, class T, class Pred>
LowerBound(It first,const It last,const T & val,Pred pred)394 constexpr It LowerBound(It first, const It last, const T& val, Pred pred)
395 {
396 auto count = std::distance(first, last);
397
398 while (count > 0) {
399 const auto half = count / 2; // 2 : half
400 const auto mid = std::next(first, half);
401 if (pred(*mid, val)) {
402 first = mid + 1;
403 count -= half + 1;
404 } else {
405 count = half;
406 }
407 }
408 return first;
409 }
410
GetFormatSpec(Format format)411 constexpr const FormatProperties& GetFormatSpec(Format format)
412 {
413 #if defined(__cpp_lib_constexpr_algorithms) && (__cpp_lib_constexpr_algorithms >= 201806L) // 201806 : id
414 static_assert(std::is_sorted(std::begin(DATA_FORMATS), std::end(DATA_FORMATS),
415 [](const FormatProperties& lhs, const FormatProperties& rhs) { return lhs.format < rhs.format; }));
416 #endif
417 if (auto pos = LowerBound(std::begin(DATA_FORMATS), std::end(DATA_FORMATS), format,
418 [](const FormatProperties& element, Format value) { return element.format < value; });
419 (pos != std::end(DATA_FORMATS)) && (pos->format == format)) {
420 return *pos;
421 }
422 return DATA_FORMATS[0];
423 }
424
GetVertexAttributeByteSize(const uint32_t vertexAttributeLocation,const VertexInputDeclarationView & vertexInputDeclaration)425 size_t GetVertexAttributeByteSize(
426 const uint32_t vertexAttributeLocation, const VertexInputDeclarationView& vertexInputDeclaration)
427 {
428 if (const auto* vertexAttributeDesc =
429 GetVertexAttributeDescription(vertexAttributeLocation, vertexInputDeclaration.attributeDescriptions);
430 vertexAttributeDesc) {
431 const FormatProperties& properties = GetFormatSpec(vertexAttributeDesc->format);
432
433 CORE_ASSERT_MSG(
434 properties.format != BASE_FORMAT_UNDEFINED, "Format not supported (%u).", vertexAttributeDesc->format);
435 return properties.componentCount * properties.componentByteSize;
436 }
437 return 0;
438 }
439
440 // For each joint 6 values defining the min and max bounds (world space AABB) of the vertices affected by the joint.
441 constexpr const size_t JOINT_BOUNDS_COMPONENTS = 6u; // 6 : size
442
GetVertexBufferDesc(size_t byteSize,IMeshBuilder::GpuBufferCreateInfo additionalFlags,bool morphTarget)443 GpuBufferDesc GetVertexBufferDesc(size_t byteSize, IMeshBuilder::GpuBufferCreateInfo additionalFlags, bool morphTarget)
444 {
445 // NOTE: storage buffer usage is currently enabled for all
446 // there's no API to define flags for auto loaded and build meshes
447 // one might always want more (and e.g. with particle cases we should use texel storage for auto formats)
448 GpuBufferDesc desc;
449 desc.usageFlags = BufferUsageFlagBits::CORE_BUFFER_USAGE_INDEX_BUFFER_BIT |
450 BufferUsageFlagBits::CORE_BUFFER_USAGE_VERTEX_BUFFER_BIT |
451 BufferUsageFlagBits::CORE_BUFFER_USAGE_TRANSFER_DST_BIT |
452 BufferUsageFlagBits::CORE_BUFFER_USAGE_STORAGE_BUFFER_BIT;
453 desc.usageFlags |= additionalFlags.usage;
454 desc.engineCreationFlags = additionalFlags.engineCreation;
455 // EngineBufferCreationFlagBits::CORE_ENGINE_BUFFER_CREATION_ENABLE_MEMORY_OPTIMIZATIONS;
456 if (morphTarget) {
457 desc.engineCreationFlags |= CORE_ENGINE_BUFFER_CREATION_DYNAMIC_BARRIERS;
458 }
459
460 desc.memoryPropertyFlags = additionalFlags.memoryFlags
461 ? additionalFlags.memoryFlags
462 : MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
463
464 desc.byteSize = (uint32_t)byteSize;
465
466 return desc;
467 }
468
CreateRenderHandleComponent(IEntityManager & entityManager,IRenderHandleComponentManager & handleManager,const RenderHandleReference & bufferHandle)469 EntityReference CreateRenderHandleComponent(IEntityManager& entityManager, IRenderHandleComponentManager& handleManager,
470 const RenderHandleReference& bufferHandle)
471 {
472 auto sharedEntity = entityManager.CreateReferenceCounted();
473 handleManager.Create(sharedEntity);
474 handleManager.Write(sharedEntity)->reference = bufferHandle;
475 return sharedEntity;
476 }
477
CreateGpuBuffers(IRenderContext & renderContext,size_t vertexDataSize,size_t indexDataSize,size_t jointDataSize,size_t targetDataSize,const MeshBuilder::GpuBufferCreateInfo & createInfo)478 MeshBuilder::BufferHandles CreateGpuBuffers(IRenderContext& renderContext, size_t vertexDataSize, size_t indexDataSize,
479 size_t jointDataSize, size_t targetDataSize, const MeshBuilder::GpuBufferCreateInfo& createInfo)
480 {
481 MeshBuilder::BufferHandles handles;
482
483 auto& gpuResourceManager = renderContext.GetDevice().GetGpuResourceManager();
484
485 // targetDataSize is zero when there's no morph targets
486 GpuBufferDesc vertexBufferDesc = GetVertexBufferDesc(
487 vertexDataSize + indexDataSize + jointDataSize + targetDataSize, createInfo, targetDataSize != 0U);
488
489 handles.vertexBuffer = gpuResourceManager.Create(vertexBufferDesc);
490
491 return handles;
492 }
493
StageToBuffers(IRenderContext & renderContext,size_t vertexDataSize,size_t indexDataSize,size_t jointDataSize,size_t targetDataSize,const MeshBuilder::BufferHandles & handles,const RenderHandleReference & stagingBuffer)494 void StageToBuffers(IRenderContext& renderContext, size_t vertexDataSize, size_t indexDataSize, size_t jointDataSize,
495 size_t targetDataSize, const MeshBuilder::BufferHandles& handles, const RenderHandleReference& stagingBuffer)
496 {
497 static constexpr const string_view RENDER_DATA_STORE_DEFAULT_STAGING = "RenderDataStoreDefaultStaging";
498 auto staging = refcnt_ptr<IRenderDataStoreDefaultStaging>(
499 renderContext.GetRenderDataStoreManager().GetRenderDataStore(RENDER_DATA_STORE_DEFAULT_STAGING.data()));
500 BufferCopy copyData { 0U, 0U,
501 static_cast<uint32_t>(vertexDataSize + indexDataSize + jointDataSize + targetDataSize) };
502 if (copyData.size) {
503 staging->CopyBufferToBuffer(stagingBuffer, handles.vertexBuffer, copyData);
504 }
505 }
506
FillSubmeshBuffers(array_view<MeshComponent::Submesh> submeshes,const MeshBuilder::BufferEntities & bufferEntities)507 void FillSubmeshBuffers(array_view<MeshComponent::Submesh> submeshes, const MeshBuilder::BufferEntities& bufferEntities)
508 {
509 for (MeshComponent::Submesh& submesh : submeshes) {
510 submesh.indexBuffer.buffer = bufferEntities.indexBuffer;
511 submesh.morphTargetBuffer.buffer = bufferEntities.morphBuffer;
512
513 submesh.bufferAccess[MeshComponent::Submesh::DM_VB_POS].buffer = bufferEntities.vertexBuffer;
514 submesh.bufferAccess[MeshComponent::Submesh::DM_VB_NOR].buffer = bufferEntities.vertexBuffer;
515 submesh.bufferAccess[MeshComponent::Submesh::DM_VB_UV0].buffer = bufferEntities.vertexBuffer;
516
517 if (submesh.flags & MeshComponent::Submesh::FlagBits::SECOND_TEXCOORD_BIT) {
518 submesh.bufferAccess[MeshComponent::Submesh::DM_VB_UV1].buffer = bufferEntities.vertexBuffer;
519 }
520
521 if (submesh.flags & MeshComponent::Submesh::FlagBits::TANGENTS_BIT) {
522 submesh.bufferAccess[MeshComponent::Submesh::DM_VB_TAN].buffer = bufferEntities.vertexBuffer;
523 }
524 if (submesh.flags & MeshComponent::Submesh::FlagBits::VERTEX_COLORS_BIT) {
525 submesh.bufferAccess[MeshComponent::Submesh::DM_VB_COL].buffer = bufferEntities.vertexBuffer;
526 }
527
528 if (submesh.flags & MeshComponent::Submesh::FlagBits::SKIN_BIT) {
529 submesh.bufferAccess[MeshComponent::Submesh::DM_VB_JOI].buffer = bufferEntities.vertexBuffer;
530 submesh.bufferAccess[MeshComponent::Submesh::DM_VB_JOW].buffer = bufferEntities.vertexBuffer;
531 }
532 }
533 }
534
CalculateAabb(array_view<const MeshComponent::Submesh> submeshes)535 MinAndMax CalculateAabb(array_view<const MeshComponent::Submesh> submeshes)
536 {
537 MinAndMax minMax;
538 for (const auto& submesh : submeshes) {
539 minMax.minAABB = min(minMax.minAABB, submesh.aabbMin);
540 minMax.maxAABB = max(minMax.maxAABB, submesh.aabbMax);
541 }
542 return minMax;
543 }
544
545 struct Conversion {
546 BASE_NS::Format srcFormat;
547 BASE_NS::Format dstFormat;
548 void (*converter)(uint8_t* dstPtr, size_t dstStride, const uint8_t* srcPtr, size_t srcStride, size_t elements);
549 };
550
551 constexpr Conversion CONVERSIONS[] = {
552 { BASE_FORMAT_R8_UINT, BASE_FORMAT_R16_UINT, Convert<IntegerToInt<uint8_t, uint16_t>, 1U> },
553 { BASE_FORMAT_R8G8B8_SNORM, BASE_FORMAT_R16G16B16A16_SNORM, Convert<IntegerNorm<int8_t, int16_t>, RGB> },
554 { BASE_FORMAT_R8G8B8_SNORM, BASE_FORMAT_R16G16B16_SFLOAT, Convert<Norm<int8_t>, Float<uint16_t>, RGB> },
555 { BASE_FORMAT_R8G8B8_SNORM, BASE_FORMAT_R32G32B32_SFLOAT, Convert<Norm<int8_t>, Float<float>, RGB> },
556 { BASE_FORMAT_R8G8B8A8_SNORM, BASE_FORMAT_R16G16B16A16_SNORM, Convert<IntegerNorm<int8_t, int16_t>, RGBA> },
557 { BASE_FORMAT_R16G16_UNORM, BASE_FORMAT_R16G16_SFLOAT, Convert<Norm<uint16_t>, Float<uint16_t>, RG> },
558 { BASE_FORMAT_R16G16_UINT, BASE_FORMAT_R16G16_SFLOAT, Convert<Int<uint16_t>, Float<uint16_t>, RG> },
559 { BASE_FORMAT_R16G16_SFLOAT, BASE_FORMAT_R32G32_SFLOAT, Convert<Float<uint16_t>, Float<float>, RG> },
560 { BASE_FORMAT_R16G16B16_UINT, BASE_FORMAT_R32G32B32_SFLOAT, Convert<Int<uint16_t>, Float<float>, RGB> },
561 { BASE_FORMAT_R16G16B16A16_UNORM, BASE_FORMAT_R8G8B8A8_UNORM, Convert<IntegerNorm<uint16_t, uint8_t>, RGBA> },
562 { BASE_FORMAT_R16G16B16A16_SNORM, BASE_FORMAT_R32G32B32_SFLOAT, Convert<Norm<int16_t>, Float<float>, RGB> },
563 { BASE_FORMAT_R16G16B16A16_UINT, BASE_FORMAT_R8G8B8A8_UINT, Convert<IntegerToInt<uint16_t, uint8_t>, RGBA> },
564 { BASE_FORMAT_R16G16B16A16_SFLOAT, BASE_FORMAT_R16G16B16A16_SNORM, Convert<Float<uint16_t>, Norm<int16_t>, RGBA> },
565 { BASE_FORMAT_R32_UINT, BASE_FORMAT_R16_UINT, Convert<IntegerToInt<uint32_t, uint16_t>, 1U> },
566 { BASE_FORMAT_R32G32_SFLOAT, BASE_FORMAT_R16G16_SFLOAT, Convert<Float<float>, Float<uint16_t>, RG> },
567 { BASE_FORMAT_R32G32B32_SFLOAT, BASE_FORMAT_R16G16B16_SFLOAT, Convert<Float<float>, Float<uint16_t>, RGB> },
568 { BASE_FORMAT_R32G32B32_SFLOAT, BASE_FORMAT_R16G16B16A16_SNORM, Convert<Float<float>, Norm<int16_t>, RGB> },
569 { BASE_FORMAT_R32G32B32A32_SFLOAT, BASE_FORMAT_R8G8B8A8_UNORM, Convert<Float<float>, Norm<uint8_t>, RGBA> },
570 { BASE_FORMAT_R32G32B32A32_SFLOAT, BASE_FORMAT_R16G16B16A16_SNORM, Convert<Float<float>, Norm<int16_t>, RGBA> },
571 { BASE_FORMAT_R32G32B32A32_SFLOAT, BASE_FORMAT_R16G16B16A16_SFLOAT, Convert<Float<float>, Float<uint16_t>, RGBA> },
572 };
573
GenericConversion(OutputBuffer & dstData,const MeshBuilder::DataBuffer & srcData,size_t count,const FormatProperties & dstFormat,const FormatProperties & srcFormat)574 void GenericConversion(OutputBuffer& dstData, const MeshBuilder::DataBuffer& srcData, size_t count,
575 const FormatProperties& dstFormat, const FormatProperties& srcFormat)
576 {
577 CORE_LOG_V("%u %u", srcData.format, dstData.format);
578 auto dstPtr = dstData.buffer.data();
579 auto srcPtr = srcData.buffer.data();
580 const auto components = Math::min(srcFormat.componentCount, dstFormat.componentCount);
581 while (count--) {
582 for (auto i = 0U; i < components; ++i) {
583 dstFormat.fromIntermediate(dstPtr + i * dstFormat.componentByteSize,
584 srcFormat.toIntermediate(srcPtr + i * srcFormat.componentByteSize));
585 }
586 auto intermediate = srcFormat.toIntermediate(srcPtr);
587 dstFormat.fromIntermediate(dstPtr, intermediate);
588 srcPtr += srcData.stride;
589 dstPtr += dstData.stride;
590 }
591 }
592
Fill(OutputBuffer & dstData,const MeshBuilder::DataBuffer & srcData,size_t count)593 void Fill(OutputBuffer& dstData, const MeshBuilder::DataBuffer& srcData, size_t count)
594 {
595 if (!count || (dstData.stride > (dstData.buffer.size() / count)) ||
596 (srcData.stride > (srcData.buffer.size() / count))) {
597 return;
598 }
599 const auto dstFormat = GetFormatSpec(dstData.format);
600 if (dstFormat.format == BASE_FORMAT_UNDEFINED) {
601 CORE_LOG_E("destination format (%u) not supported", dstData.format);
602 return;
603 }
604 const auto srcFormat = GetFormatSpec(srcData.format);
605 if (srcFormat.format == BASE_FORMAT_UNDEFINED) {
606 CORE_LOG_E("source format (%u) not supported", srcData.format);
607 return;
608 }
609 const auto dstElementSize = dstFormat.componentCount * dstFormat.componentByteSize;
610 const auto srcElementSize = srcFormat.componentCount * srcFormat.componentByteSize;
611 if ((dstElementSize > dstData.stride) || (srcElementSize > srcData.stride)) {
612 return;
613 }
614 if (dstData.format == srcData.format) {
615 // no conversion required
616 if (dstData.stride == srcData.stride && dstData.stride == dstElementSize) {
617 // strides match and no padding
618 CloneData(dstData.buffer.data(), dstData.buffer.size(), srcData.buffer.data(), srcElementSize * count);
619 } else {
620 // stride mismatch or padding
621 auto dstPtr = dstData.buffer.data();
622 auto dstSize = dstData.buffer.size();
623 auto srcPtr = srcData.buffer.data();
624 while (count--) {
625 CloneData(dstPtr, dstSize, srcPtr, srcElementSize);
626 dstPtr += dstData.stride;
627 srcPtr += srcData.stride;
628 dstSize -= dstData.stride;
629 }
630 }
631 } else if (!srcFormat.toIntermediate || !dstFormat.fromIntermediate) {
632 CORE_LOG_E("missing conversion from %u to %u", srcFormat.format, dstFormat.format);
633 } else {
634 // must convert between formats
635 // attempt to inline commonly used conversions
636 auto pos = std::find_if(std::begin(CONVERSIONS), std::end(CONVERSIONS),
637 [src = srcData.format, dst = dstData.format](const Conversion& conversion) {
638 return (conversion.srcFormat == src) && (conversion.dstFormat == dst);
639 });
640 if (pos != std::end(CONVERSIONS)) {
641 pos->converter(dstData.buffer.data(), dstData.stride, srcData.buffer.data(), srcData.stride, count);
642 } else {
643 GenericConversion(dstData, srcData, count, dstFormat, srcFormat);
644 }
645 }
646 }
647
GetPrimitiveTopologyAdvanceCount(const PrimitiveTopology pt)648 constexpr uint32_t GetPrimitiveTopologyAdvanceCount(const PrimitiveTopology pt)
649 {
650 if (pt == CORE_PRIMITIVE_TOPOLOGY_POINT_LIST) {
651 return 1U;
652 } else if ((pt == CORE_PRIMITIVE_TOPOLOGY_LINE_LIST) || (pt == CORE_PRIMITIVE_TOPOLOGY_LINE_STRIP) ||
653 (pt == CORE_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY) ||
654 (pt == CORE_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY)) {
655 return 2U; // 2 : id
656 } else if ((pt == CORE_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) || (pt == CORE_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP) ||
657 (pt == CORE_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN) ||
658 (pt == CORE_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY) ||
659 (pt == CORE_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY)) {
660 return 3U; // 3 : id
661 } else {
662 // CORE_PRIMITIVE_TOPOLOGY_PATCH_LIST
663 // not processed default triangle_list
664 return 3U; // 3 : id
665 }
666 }
667
668 template<typename T>
SmoothNormal(const PrimitiveTopology pt,array_view<const T> indices,const Math::Vec3 * posPtr,Math::Vec3 * norPtr)669 void SmoothNormal(const PrimitiveTopology pt, array_view<const T> indices, const Math::Vec3* posPtr, Math::Vec3* norPtr)
670 {
671 const uint32_t ac = GetPrimitiveTopologyAdvanceCount(pt);
672 // not processed for e.g. points with count of 1
673 // only basic types supported
674 #if (CORE3D_VALIDATION_ENABLED == 1)
675 bool processed = false;
676 #endif
677 if ((ac == 2U) && (indices.size() % 2U == 0U)) { // 2 : id
678 #if (CORE3D_VALIDATION_ENABLED == 1)
679 processed = true;
680 #endif
681 for (auto i = 0U; i < indices.size(); i += 2U) { // 2 : step
682 const auto aa = indices[i];
683 const auto bb = indices[i + 1];
684 const auto& pos1 = posPtr[aa];
685 const auto& pos2 = posPtr[bb];
686 auto faceNorm = Math::Normalize(pos2 - pos1);
687 norPtr[aa] += faceNorm;
688 norPtr[bb] += faceNorm;
689 }
690 } else if ((ac == 3U) && ((indices.size() % 3U) == 0U)) { // 3 : size
691 #if (CORE3D_VALIDATION_ENABLED == 1)
692 processed = true;
693 #endif
694 for (auto i = 0U; i < indices.size(); i += 3) { // 3 : step
695 const auto aa = indices[i];
696 const auto bb = indices[i + 1];
697 const auto cc = indices[i + 2]; // 2 : step
698 const auto& pos1 = posPtr[aa];
699 const auto& pos2 = posPtr[bb];
700 const auto& pos3 = posPtr[cc];
701 auto faceNorm = Math::Cross(pos2 - pos1, pos3 - pos1);
702 norPtr[aa] += faceNorm;
703 norPtr[bb] += faceNorm;
704 norPtr[cc] += faceNorm;
705 }
706 }
707 #if (CORE3D_VALIDATION_ENABLED == 1)
708 if (!processed) {
709 CORE_LOG_W("MeshBuilder did not smooth normals for primitive topology (%u)", static_cast<uint32_t>(pt));
710 }
711 #endif
712 }
713
GenerateDefaultNormals(vector<uint8_t> & generatedNormals,const IMeshBuilder::DataBuffer & indices,const IMeshBuilder::DataBuffer & positions,uint32_t vertexCount,const PrimitiveTopology primitiveTopology)714 void GenerateDefaultNormals(vector<uint8_t>& generatedNormals, const IMeshBuilder::DataBuffer& indices,
715 const IMeshBuilder::DataBuffer& positions, uint32_t vertexCount, const PrimitiveTopology primitiveTopology)
716 {
717 auto offset = generatedNormals.size();
718 generatedNormals.resize(generatedNormals.size() + sizeof(Math::Vec3) * vertexCount);
719 auto* norPtr = reinterpret_cast<Math::Vec3*>(generatedNormals.data() + offset);
720 auto* posPtr = reinterpret_cast<const Math::Vec3*>(positions.buffer.data());
721 if (indices.buffer.empty() && GetPrimitiveTopologyAdvanceCount(primitiveTopology) == 3U) { // 3 : count
722 // Mesh without indices will have flat normals
723 for (auto i = 0U; i < vertexCount; i += 3U) { // 3 : step
724 CORE_ASSERT((i + 2U) < vertexCount); // 2 : step
725 const auto& pos1 = posPtr[i];
726 const auto& pos2 = posPtr[i + 1U];
727 const auto& pos3 = posPtr[i + 2U]; // 2 : step
728 auto faceNorm = Math::Normalize(Math::Cross(pos2 - pos1, pos3 - pos1));
729 norPtr[i] = faceNorm;
730 norPtr[i + 1] = faceNorm;
731 norPtr[i + 2] = faceNorm; // 2:index
732 }
733 } else {
734 // With indexed data flat normals would require duplicating shared vertices. Instead calculate smooth
735 // normals.
736 if (indices.stride == sizeof(uint16_t)) {
737 auto view = array_view(
738 reinterpret_cast<const uint16_t*>(indices.buffer.data()), indices.buffer.size() / indices.stride);
739 SmoothNormal(primitiveTopology, view, posPtr, norPtr);
740 } else if (indices.stride == sizeof(uint32_t)) {
741 auto view = array_view(
742 reinterpret_cast<const uint32_t*>(indices.buffer.data()), indices.buffer.size() / indices.stride);
743 SmoothNormal(primitiveTopology, view, posPtr, norPtr);
744 }
745 for (auto& nor : array_view(norPtr, vertexCount)) {
746 nor = Math::Normalize(nor);
747 }
748 }
749 }
750
GenerateDefaultUvs(vector<uint8_t> & generatedUvs,uint32_t vertexCount)751 void GenerateDefaultUvs(vector<uint8_t>& generatedUvs, uint32_t vertexCount)
752 {
753 auto offset = generatedUvs.size();
754 generatedUvs.resize(generatedUvs.size() + sizeof(Math::Vec2) * vertexCount);
755 auto* ptr = reinterpret_cast<Math::Vec2*>(generatedUvs.data() + offset);
756 std::fill(ptr, ptr + vertexCount, Math::Vec2(0.0f, 0.0f));
757 }
758
GenerateDefaultTangents(IMeshBuilder::DataBuffer & tangents,vector<uint8_t> & generatedTangents,const IMeshBuilder::DataBuffer & indices,const IMeshBuilder::DataBuffer & positions,const IMeshBuilder::DataBuffer & normals,const IMeshBuilder::DataBuffer & uvs,uint32_t vertexCount)759 void GenerateDefaultTangents(IMeshBuilder::DataBuffer& tangents, vector<uint8_t>& generatedTangents,
760 const IMeshBuilder::DataBuffer& indices, const IMeshBuilder::DataBuffer& positions,
761 const IMeshBuilder::DataBuffer& normals, const IMeshBuilder::DataBuffer& uvs, uint32_t vertexCount)
762 {
763 auto offset = generatedTangents.size();
764 generatedTangents.resize(generatedTangents.size() + sizeof(Math::Vec4) * vertexCount);
765 tangents.format = BASE_FORMAT_R32G32B32A32_SFLOAT;
766 tangents.stride = sizeof(Math::Vec4);
767 tangents.buffer = generatedTangents;
768
769 auto posView = array_view(reinterpret_cast<const Math::Vec3*>(positions.buffer.data()), vertexCount);
770 auto norView = array_view(reinterpret_cast<const Math::Vec3*>(normals.buffer.data()), vertexCount);
771 auto uvsView = array_view(reinterpret_cast<const Math::Vec2*>(uvs.buffer.data()), vertexCount);
772
773 auto outTangents = array_view(reinterpret_cast<Math::Vec4*>(generatedTangents.data() + offset), vertexCount);
774
775 vector<uint8_t> indexData(indices.buffer.size());
776
777 const auto indexCountCount = indices.buffer.size() / indices.stride;
778 switch (indices.stride) {
779 case sizeof(uint8_t): {
780 auto indicesView = array_view(reinterpret_cast<const uint8_t*>(indices.buffer.data()), indexCountCount);
781 MeshUtil::CalculateTangents(indicesView, posView, norView, uvsView, outTangents);
782 break;
783 }
784 case sizeof(uint16_t): {
785 auto indicesView = array_view(reinterpret_cast<const uint16_t*>(indices.buffer.data()), indexCountCount);
786 MeshUtil::CalculateTangents(indicesView, posView, norView, uvsView, outTangents);
787 break;
788 }
789 case sizeof(uint32_t): {
790 auto indicesView = array_view(reinterpret_cast<const uint32_t*>(indices.buffer.data()), indexCountCount);
791 MeshUtil::CalculateTangents(indicesView, posView, norView, uvsView, outTangents);
792 break;
793 }
794 default:
795 CORE_ASSERT_MSG(false, "Invalid elementSize %u", indices.stride);
796 }
797 }
798
Verify(const MeshBuilder::DataBuffer & dataBuffer,const uint32_t vertexCount)799 std::optional<std::reference_wrapper<const FormatProperties>> Verify(
800 const MeshBuilder::DataBuffer& dataBuffer, const uint32_t vertexCount)
801 {
802 if (vertexCount == 0) {
803 return std::nullopt;
804 }
805 if (dataBuffer.stride > (dataBuffer.buffer.size() / vertexCount)) {
806 return std::nullopt;
807 }
808 const auto& formatProperties = GetFormatSpec(dataBuffer.format);
809 if (formatProperties.format == BASE_FORMAT_UNDEFINED) {
810 CORE_LOG_E("format (%u) not supported", formatProperties.format);
811 return std::nullopt;
812 }
813 if (const auto elementSize = formatProperties.componentCount * formatProperties.componentByteSize;
814 elementSize > dataBuffer.stride) {
815 return std::nullopt;
816 }
817 return formatProperties;
818 }
819
ConvertAttribute(const MeshBuilder::DataBuffer & dataBuffer,const FormatProperties & formatProperties,uint32_t vertexIndex)820 inline Math::Vec3 ConvertAttribute(
821 const MeshBuilder::DataBuffer& dataBuffer, const FormatProperties& formatProperties, uint32_t vertexIndex)
822 {
823 Math::Vec3 value;
824 auto* ptr = dataBuffer.buffer.data() + dataBuffer.stride * vertexIndex;
825 for (auto i = 0U; i < Math::min(countof(value.data), formatProperties.componentCount); ++i) {
826 value[i] = formatProperties.toIntermediate(ptr + i * formatProperties.componentByteSize);
827 }
828 return value;
829 }
830
GatherDeltasR32G32B32(MeshBuilder::SubmeshExt & submesh,uint8_t * dst,uint32_t baseOffset,uint32_t indexOffset,uint32_t targetSize,const MeshBuilder::DataBuffer & targetPositions)831 void GatherDeltasR32G32B32(MeshBuilder::SubmeshExt& submesh, uint8_t* dst, uint32_t baseOffset, uint32_t indexOffset,
832 uint32_t targetSize, const MeshBuilder::DataBuffer& targetPositions)
833 {
834 // Target data starts after base
835 uint32_t targetOffset = baseOffset + targetSize;
836
837 auto index = reinterpret_cast<uint32_t*>(dst + indexOffset);
838 // special case which matches glTF 2.0. morph targets are three float components.
839 for (uint32_t trg = 0; trg < submesh.info.morphTargetCount; trg++) {
840 submesh.morphTargets[trg].offset = targetOffset;
841 const auto startTarget = reinterpret_cast<MorphInputData*>(dst + targetOffset);
842 auto target = startTarget;
843 for (uint32_t vertex = 0; vertex < submesh.info.vertexCount; ++vertex) {
844 // for each vertex in target check that position, normal and tangent deltas are non-zero.
845 const auto vertexIndex = vertex + (trg * submesh.info.vertexCount);
846 auto pos = *reinterpret_cast<const Math::Vec3*>(
847 targetPositions.buffer.data() + targetPositions.stride * vertexIndex);
848 const auto zeroDelta = (pos == Math::Vec3 {});
849 // store offset for each non-zero
850 *index++ = zeroDelta ? UINT32_MAX : ((targetOffset - baseOffset) / sizeof(MorphInputData));
851 if (zeroDelta) {
852 continue;
853 }
854 targetOffset += sizeof(MorphInputData);
855
856 target->pos = Math::Vec4(pos, static_cast<float>(vertex));
857 ++target;
858 }
859 // Store the size and indexOffset of the gathered deltas.
860 const auto byteSize =
861 static_cast<uint32_t>(reinterpret_cast<ptrdiff_t>(target) - reinterpret_cast<ptrdiff_t>(startTarget));
862 submesh.morphTargets[trg].byteSize = byteSize;
863 }
864 }
865
GatherDeltasR32G32B32(MeshBuilder::SubmeshExt & submesh,uint8_t * dst,uint32_t baseOffset,uint32_t indexOffset,uint32_t targetSize,const MeshBuilder::DataBuffer & targetPositions,const MeshBuilder::DataBuffer & targetNormals)866 void GatherDeltasR32G32B32(MeshBuilder::SubmeshExt& submesh, uint8_t* dst, uint32_t baseOffset, uint32_t indexOffset,
867 uint32_t targetSize, const MeshBuilder::DataBuffer& targetPositions, const MeshBuilder::DataBuffer& targetNormals)
868 {
869 // Target data starts after base
870 uint32_t targetOffset = baseOffset + targetSize;
871
872 auto index = reinterpret_cast<uint32_t*>(dst + indexOffset);
873 // special case which matches glTF 2.0. morph targets are three float components.
874 for (uint32_t trg = 0; trg < submesh.info.morphTargetCount; trg++) {
875 submesh.morphTargets[trg].offset = targetOffset;
876 const auto startTarget = reinterpret_cast<MorphInputData*>(dst + targetOffset);
877 auto target = startTarget;
878 for (uint32_t vertex = 0; vertex < submesh.info.vertexCount; ++vertex) {
879 // for each vertex in target check that position and normal deltas are non-zero.
880 const auto vertexIndex = vertex + (trg * submesh.info.vertexCount);
881 auto pos = *reinterpret_cast<const Math::Vec3*>(
882 targetPositions.buffer.data() + targetPositions.stride * vertexIndex);
883 auto nor =
884 *reinterpret_cast<const Math::Vec3*>(targetNormals.buffer.data() + targetNormals.stride * vertexIndex);
885 const auto zeroDelta = (pos == Math::Vec3 {} && nor == Math::Vec3 {});
886 // store offset for each non-zero
887 *index++ = zeroDelta ? UINT32_MAX : ((targetOffset - baseOffset) / sizeof(MorphInputData));
888 if (zeroDelta) {
889 continue;
890 }
891 targetOffset += sizeof(MorphInputData);
892
893 target->pos = Math::Vec4(pos, static_cast<float>(vertex));
894 #if defined(CORE_MORPH_USE_PACKED_NOR_TAN)
895 target->nortan.x = Math::PackHalf2X16({ nor.x, nor.y });
896 target->nortan.y = Math::PackHalf2X16({ nor.z, 0.f });
897 #else
898 target->nor = Math::Vec4(nor, 0.f);
899 #endif
900 ++target;
901 }
902 // Store the size of the gathered deltas.
903 const auto byteSize =
904 static_cast<uint32_t>(reinterpret_cast<ptrdiff_t>(target) - reinterpret_cast<ptrdiff_t>(startTarget));
905 submesh.morphTargets[trg].byteSize = byteSize;
906 }
907 }
908
GatherDeltasR32G32B32(MeshBuilder::SubmeshExt & submesh,uint8_t * dst,uint32_t baseOffset,uint32_t indexOffset,uint32_t targetSize,const MeshBuilder::DataBuffer & targetPositions,const MeshBuilder::DataBuffer & targetNormals,const MeshBuilder::DataBuffer & targetTangents)909 void GatherDeltasR32G32B32(MeshBuilder::SubmeshExt& submesh, uint8_t* dst, uint32_t baseOffset, uint32_t indexOffset,
910 uint32_t targetSize, const MeshBuilder::DataBuffer& targetPositions, const MeshBuilder::DataBuffer& targetNormals,
911 const MeshBuilder::DataBuffer& targetTangents)
912 {
913 // Target data starts after base
914 uint32_t targetOffset = baseOffset + targetSize;
915
916 auto index = reinterpret_cast<uint32_t*>(dst + indexOffset);
917 // special case which matches glTF 2.0. morph targets are three float components.
918 for (uint32_t trg = 0; trg < submesh.info.morphTargetCount; trg++) {
919 submesh.morphTargets[trg].offset = targetOffset;
920 const auto startTarget = reinterpret_cast<MorphInputData*>(dst + targetOffset);
921 auto target = startTarget;
922 for (uint32_t vertex = 0; vertex < submesh.info.vertexCount; ++vertex) {
923 // for each vertex in target check that position, normal and tangent deltas are non-zero.
924 const auto vertexIndex = vertex + (trg * submesh.info.vertexCount);
925 auto pos = *reinterpret_cast<const Math::Vec3*>(
926 targetPositions.buffer.data() + targetPositions.stride * vertexIndex);
927 auto nor =
928 *reinterpret_cast<const Math::Vec3*>(targetNormals.buffer.data() + targetNormals.stride * vertexIndex);
929 auto tan = *reinterpret_cast<const Math::Vec3*>(
930 targetTangents.buffer.data() + targetTangents.stride * vertexIndex);
931 const auto zeroDelta = (pos == Math::Vec3 {} && nor == Math::Vec3 {} && tan == Math::Vec3 {});
932 // store offset for each non-zero
933 *index++ = zeroDelta ? UINT32_MAX : ((targetOffset - baseOffset) / sizeof(MorphInputData));
934 if (zeroDelta) {
935 continue;
936 }
937 targetOffset += sizeof(MorphInputData);
938 target->pos = Math::Vec4(pos, static_cast<float>(vertex));
939
940 #if defined(CORE_MORPH_USE_PACKED_NOR_TAN)
941 target->nortan.x = Math::PackHalf2X16({ nor.x, nor.y });
942 target->nortan.y = Math::PackHalf2X16({ nor.z, 0.f });
943 target->nortan.z = Math::PackHalf2X16({ tan.x, tan.y });
944 target->nortan.w = Math::PackHalf2X16({ tan.z, 0.f });
945 #else
946 target->nor = Math::Vec4(nor, 0.f);
947 target->tan = Math::Vec4(tan, 0.f);
948 #endif
949 ++target;
950 }
951 // Store the size of the gathered deltas.
952 const auto byteSize =
953 static_cast<uint32_t>(reinterpret_cast<ptrdiff_t>(target) - reinterpret_cast<ptrdiff_t>(startTarget));
954 submesh.morphTargets[trg].byteSize = byteSize;
955 }
956 }
957 } // namespace
958
MeshBuilder(IRenderContext & renderContext)959 MeshBuilder::MeshBuilder(IRenderContext& renderContext) : renderContext_(renderContext) {}
960
961 // Public interface from IMeshBuilder
Initialize(const VertexInputDeclarationView & vertexInputDeclaration,size_t submeshCount)962 void MeshBuilder::Initialize(const VertexInputDeclarationView& vertexInputDeclaration, size_t submeshCount)
963 {
964 Initialize(Configuration { vertexInputDeclaration, submeshCount, 0U });
965 }
966
Initialize(const IMeshBuilder::Configuration & config)967 void MeshBuilder::Initialize(const IMeshBuilder::Configuration& config)
968 {
969 submeshInfos_.clear();
970 submeshInfos_.reserve(config.submeshCount);
971 submeshes_.clear();
972 submeshes_.resize(config.submeshCount);
973
974 flags_ = config.flags;
975 vertexCount_ = 0;
976 indexCount_ = 0;
977
978 vertexDataSize_ = 0;
979 indexDataSize_ = 0;
980 jointDataSize_ = 0;
981 targetDataSize_ = 0;
982
983 jointBoundsData_.clear();
984
985 if (vertexPtr_) {
986 vertexPtr_ = nullptr;
987 auto& gpuResourceManager = renderContext_.GetDevice().GetGpuResourceManager();
988 gpuResourceManager.UnmapBuffer(bufferHandles_.vertexBuffer);
989 }
990 bufferHandles_ = {};
991 if (stagingPtr_) {
992 stagingPtr_ = nullptr;
993 auto& gpuResourceManager = renderContext_.GetDevice().GetGpuResourceManager();
994 gpuResourceManager.UnmapBuffer(stagingBuffer_);
995 }
996 stagingBuffer_ = {};
997 vertexInputDeclaration_ = config.vertexInputDeclaration;
998 }
999
AddSubmesh(const Submesh & info)1000 void MeshBuilder::AddSubmesh(const Submesh& info)
1001 {
1002 submeshInfos_.push_back(SubmeshExt { info, {}, {}, {} });
1003 }
1004
GetSubmesh(size_t index) const1005 const MeshBuilder::Submesh& MeshBuilder::GetSubmesh(size_t index) const
1006 {
1007 return submeshInfos_[index].info;
1008 }
1009
Allocate()1010 void MeshBuilder::Allocate()
1011 {
1012 BufferSizesInBytes bufferSizes = CalculateSizes();
1013 bufferSizes.indexBuffer = Align(bufferSizes.indexBuffer, BUFFER_ALIGN);
1014 bufferSizes.jointBuffer = Align(bufferSizes.jointBuffer, BUFFER_ALIGN);
1015 bufferSizes.morphVertexData = Align(bufferSizes.morphVertexData, BUFFER_ALIGN);
1016
1017 indexCount_ = (uint32_t)bufferSizes.indexBuffer / sizeof(uint32_t);
1018
1019 uint32_t vertexBufferSizeInBytes = 0;
1020
1021 // Set binding offsets.
1022 for (auto const& bindingDesc : vertexInputDeclaration_.bindingDescriptions) {
1023 for (auto& submesh : submeshInfos_) {
1024 submesh.vertexBindingOffset[bindingDesc.binding] = vertexBufferSizeInBytes;
1025 vertexBufferSizeInBytes += submesh.vertexBindingByteSize[bindingDesc.binding];
1026 }
1027 }
1028
1029 vertexDataSize_ = vertexBufferSizeInBytes;
1030 indexDataSize_ = bufferSizes.indexBuffer;
1031 jointDataSize_ = bufferSizes.jointBuffer;
1032 targetDataSize_ = bufferSizes.morphVertexData;
1033
1034 if (flags_ & ConfigurationFlagBits::NO_STAGING_BUFFER) {
1035 // Create host accessible buffers where data can be written directly.
1036 static constexpr IMeshBuilder::GpuBufferCreateInfo flags { 0U,
1037 EngineBufferCreationFlagBits::CORE_ENGINE_BUFFER_CREATION_CREATE_IMMEDIATE |
1038 EngineBufferCreationFlagBits::CORE_ENGINE_BUFFER_CREATION_MAP_OUTSIDE_RENDERER,
1039 MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
1040 MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_HOST_COHERENT_BIT };
1041 bufferHandles_ =
1042 CreateGpuBuffers(renderContext_, vertexDataSize_, indexDataSize_, jointDataSize_, targetDataSize_, flags);
1043 auto& gpuResourceManager = renderContext_.GetDevice().GetGpuResourceManager();
1044 if (bufferHandles_.vertexBuffer) {
1045 vertexPtr_ = static_cast<uint8_t*>(gpuResourceManager.MapBufferMemory(bufferHandles_.vertexBuffer));
1046 }
1047 } else {
1048 auto& gpuResourceManager = renderContext_.GetDevice().GetGpuResourceManager();
1049 GpuBufferDesc gpuBufferDesc;
1050 gpuBufferDesc.usageFlags = BufferUsageFlagBits::CORE_BUFFER_USAGE_TRANSFER_SRC_BIT;
1051 gpuBufferDesc.memoryPropertyFlags = MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
1052 MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_HOST_COHERENT_BIT;
1053 gpuBufferDesc.engineCreationFlags =
1054 EngineBufferCreationFlagBits::CORE_ENGINE_BUFFER_CREATION_CREATE_IMMEDIATE |
1055 EngineBufferCreationFlagBits::CORE_ENGINE_BUFFER_CREATION_MAP_OUTSIDE_RENDERER |
1056 EngineBufferCreationFlagBits::CORE_ENGINE_BUFFER_CREATION_DEFERRED_DESTROY;
1057 gpuBufferDesc.byteSize =
1058 static_cast<uint32_t>(vertexDataSize_ + indexDataSize_ + jointDataSize_ + targetDataSize_);
1059 if (gpuBufferDesc.byteSize) {
1060 stagingBuffer_ = gpuResourceManager.Create(gpuBufferDesc);
1061 stagingPtr_ = static_cast<uint8_t*>(gpuResourceManager.MapBufferMemory(stagingBuffer_));
1062 }
1063 }
1064 }
1065
CalculateSizes()1066 MeshBuilder::BufferSizesInBytes MeshBuilder::CalculateSizes()
1067 {
1068 BufferSizesInBytes bufferSizes {};
1069
1070 const size_t jointIndexSizeInBytes =
1071 GetVertexAttributeByteSize(MeshComponent::Submesh::DM_VB_JOI, vertexInputDeclaration_);
1072 const size_t jointWeightSizeInBytes =
1073 GetVertexAttributeByteSize(MeshComponent::Submesh::DM_VB_JOW, vertexInputDeclaration_);
1074
1075 for (auto& submesh : submeshInfos_) {
1076 // Calculate vertex binding sizes.
1077 submesh.vertexBindingByteSize.resize(vertexInputDeclaration_.bindingDescriptions.size());
1078 submesh.vertexBindingOffset.resize(vertexInputDeclaration_.bindingDescriptions.size());
1079 for (auto const& bindingDesc : vertexInputDeclaration_.bindingDescriptions) {
1080 submesh.vertexBindingByteSize[bindingDesc.binding] =
1081 static_cast<uint32_t>(Align(bindingDesc.stride * submesh.info.vertexCount, BUFFER_ALIGN));
1082 }
1083
1084 submesh.indexBufferOffset = (uint32_t)Align(bufferSizes.indexBuffer, BUFFER_ALIGN);
1085 submesh.jointBufferOffset = (uint32_t)Align(bufferSizes.jointBuffer, BUFFER_ALIGN);
1086 submesh.morphTargetBufferOffset = (uint32_t)Align(bufferSizes.morphVertexData, BUFFER_ALIGN);
1087
1088 if (submesh.info.indexType == CORE_INDEX_TYPE_UINT16) {
1089 bufferSizes.indexBuffer = submesh.indexBufferOffset + (submesh.info.indexCount * sizeof(uint16_t));
1090 } else {
1091 bufferSizes.indexBuffer = submesh.indexBufferOffset + (submesh.info.indexCount * sizeof(uint32_t));
1092 }
1093
1094 if (submesh.info.joints) {
1095 const size_t currJointIndexByteSize = Align(jointIndexSizeInBytes * submesh.info.vertexCount, BUFFER_ALIGN);
1096 const size_t currJointWeightByteSize =
1097 Align(jointWeightSizeInBytes * submesh.info.vertexCount, BUFFER_ALIGN);
1098 // joint index and joint weight bytesizes both need to be aligned
1099 bufferSizes.jointBuffer = submesh.jointBufferOffset + currJointIndexByteSize + currJointWeightByteSize;
1100 }
1101
1102 if (submesh.info.morphTargetCount > 0) {
1103 submesh.morphTargets.resize(submesh.info.morphTargetCount);
1104 // vertexCount * uint32_t * morphTargetCount, index/indexOffset to sparse target data
1105 // vertexCount * MorphInputData, base data
1106 // vertexCount * MorphInputData * morphTargetCount, target data
1107 const uint32_t indexSize = (uint32_t)Align(
1108 submesh.info.vertexCount * submesh.info.morphTargetCount * sizeof(uint32_t), BUFFER_ALIGN);
1109 const uint32_t targetSize = (uint32_t)Align(
1110 submesh.info.vertexCount * sizeof(MorphInputData) * (submesh.info.morphTargetCount + 1u), BUFFER_ALIGN);
1111 bufferSizes.morphVertexData = submesh.morphTargetBufferOffset + indexSize + targetSize;
1112 }
1113
1114 vertexCount_ += submesh.info.vertexCount;
1115 }
1116 return bufferSizes;
1117 }
1118
SetVertexData(size_t submeshIndex,const DataBuffer & positions,const DataBuffer & normals,const DataBuffer & texcoords0,const DataBuffer & texcoords1,const DataBuffer & tangents,const DataBuffer & colors)1119 void MeshBuilder::SetVertexData(size_t submeshIndex, const DataBuffer& positions, const DataBuffer& normals,
1120 const DataBuffer& texcoords0, const DataBuffer& texcoords1, const DataBuffer& tangents, const DataBuffer& colors)
1121 {
1122 auto buffer = (flags_ & ConfigurationFlagBits::NO_STAGING_BUFFER) ? vertexPtr_ : stagingPtr_;
1123 if (buffer) {
1124 // *Vertex data* | index data | joint data | morph data
1125 SubmeshExt& submesh = submeshInfos_[submeshIndex];
1126
1127 // Submesh info for this submesh.
1128 MeshComponent::Submesh& submeshDesc = submeshes_[submeshIndex];
1129
1130 submeshDesc.material = submesh.info.material;
1131 submeshDesc.vertexCount = submesh.info.vertexCount;
1132 submeshDesc.instanceCount = submesh.info.instanceCount;
1133 submeshDesc.inputAssembly = submesh.info.inputAssembly;
1134
1135 // If we need to generate tangents we need float copies of position, normal and uv0
1136 const bool generateTangents = submesh.info.tangents && tangents.buffer.empty();
1137
1138 // Process position.
1139 {
1140 auto& acc = submeshDesc.bufferAccess[MeshComponent::Submesh::DM_VB_POS];
1141 WriteData(positions, submesh, MeshComponent::Submesh::DM_VB_POS, acc.offset, acc.byteSize, buffer);
1142 if (normals.buffer.empty() || generateTangents) {
1143 auto offset = vertexData_.size();
1144 vertexData_.resize(offset + sizeof(Math::Vec3) * submeshDesc.vertexCount);
1145 OutputBuffer dst { BASE_FORMAT_R32G32B32_SFLOAT, sizeof(Math::Vec3),
1146 { vertexData_.data() + offset, sizeof(Math::Vec3) * submeshDesc.vertexCount } };
1147 Fill(dst, positions, submeshDesc.vertexCount);
1148 submesh.positionOffset = static_cast<int32_t>(offset);
1149 submesh.positionSize = sizeof(Math::Vec3) * submeshDesc.vertexCount;
1150 }
1151 }
1152
1153 // Process normal.
1154 if (!normals.buffer.empty()) {
1155 auto& acc = submeshDesc.bufferAccess[MeshComponent::Submesh::DM_VB_NOR];
1156 WriteData(normals, submesh, MeshComponent::Submesh::DM_VB_NOR, acc.offset, acc.byteSize, buffer);
1157 submesh.hasNormals = true;
1158 if (generateTangents) {
1159 auto offset = vertexData_.size();
1160 vertexData_.resize(offset + sizeof(Math::Vec3) * submeshDesc.vertexCount);
1161 OutputBuffer dst { BASE_FORMAT_R32G32B32_SFLOAT, sizeof(Math::Vec3),
1162 { vertexData_.data() + offset, sizeof(Math::Vec3) * submeshDesc.vertexCount } };
1163 Fill(dst, normals, submeshDesc.vertexCount);
1164 submesh.normalOffset = static_cast<int32_t>(offset);
1165 submesh.normalSize = sizeof(Math::Vec3) * submeshDesc.vertexCount;
1166 }
1167 }
1168
1169 // Process uv.
1170 if (!texcoords0.buffer.empty()) {
1171 auto& acc = submeshDesc.bufferAccess[MeshComponent::Submesh::DM_VB_UV0];
1172 WriteData(texcoords0, submesh, MeshComponent::Submesh::DM_VB_UV0, acc.offset, acc.byteSize, buffer);
1173 submesh.hasUv0 = true;
1174 if (generateTangents) {
1175 auto offset = vertexData_.size();
1176 vertexData_.resize(offset + sizeof(Math::Vec2) * submeshDesc.vertexCount);
1177 OutputBuffer dst { BASE_FORMAT_R32G32_SFLOAT, sizeof(Math::Vec2),
1178 { vertexData_.data() + offset, sizeof(Math::Vec2) * submeshDesc.vertexCount } };
1179 Fill(dst, texcoords0, submeshDesc.vertexCount);
1180 submesh.uvOffset = static_cast<int32_t>(offset);
1181 submesh.uvSize = sizeof(Math::Vec2) * submeshDesc.vertexCount;
1182 }
1183 }
1184
1185 if (!texcoords1.buffer.empty()) {
1186 auto& acc = submeshDesc.bufferAccess[MeshComponent::Submesh::DM_VB_UV1];
1187 if (WriteData(texcoords1, submesh, MeshComponent::Submesh::DM_VB_UV1, acc.offset, acc.byteSize, buffer)) {
1188 submeshDesc.flags |= MeshComponent::Submesh::FlagBits::SECOND_TEXCOORD_BIT;
1189 }
1190 } else {
1191 const auto& uv0 = submeshDesc.bufferAccess[MeshComponent::Submesh::DM_VB_UV0];
1192 auto& uv1 = submeshDesc.bufferAccess[MeshComponent::Submesh::DM_VB_UV1];
1193 uv1 = uv0;
1194 }
1195
1196 // Process tangent.
1197 if (!tangents.buffer.empty() && submesh.info.tangents) {
1198 auto& acc = submeshDesc.bufferAccess[MeshComponent::Submesh::DM_VB_TAN];
1199 if (WriteData(tangents, submesh, MeshComponent::Submesh::DM_VB_TAN, acc.offset, acc.byteSize, buffer)) {
1200 submeshDesc.flags |= MeshComponent::Submesh::FlagBits::TANGENTS_BIT;
1201 submesh.hasTangents = true;
1202 }
1203 }
1204
1205 // Process vertex colors.
1206 if (!colors.buffer.empty() && submesh.info.colors) {
1207 auto& acc = submeshDesc.bufferAccess[MeshComponent::Submesh::DM_VB_COL];
1208 if (WriteData(colors, submesh, MeshComponent::Submesh::DM_VB_COL, acc.offset, acc.byteSize, buffer)) {
1209 submeshDesc.flags |= MeshComponent::Submesh::FlagBits::VERTEX_COLORS_BIT;
1210 }
1211 }
1212 }
1213 }
1214
SetIndexData(size_t submeshIndex,const DataBuffer & indices)1215 void MeshBuilder::SetIndexData(size_t submeshIndex, const DataBuffer& indices)
1216 {
1217 auto buffer = (flags_ & ConfigurationFlagBits::NO_STAGING_BUFFER) ? vertexPtr_ : stagingPtr_;
1218 if (buffer) {
1219 // Vertex data | *index data* | joint data | morph data
1220 SubmeshExt& submesh = submeshInfos_[submeshIndex];
1221 const auto indexCount = submesh.info.indexCount;
1222 const auto indexType = submesh.info.indexType;
1223 const auto bufferOffset = static_cast<uint32_t>(submesh.indexBufferOffset + vertexDataSize_);
1224 MeshComponent::Submesh& submeshDesc = submeshes_[submeshIndex];
1225 submeshDesc.indexCount = indexCount;
1226 submeshDesc.indexBuffer.indexType = indexType;
1227 submeshDesc.indexBuffer.offset = bufferOffset;
1228
1229 OutputBuffer output = [](RENDER_NS::IndexType indexType) {
1230 if (indexType == IndexType::CORE_INDEX_TYPE_UINT16) {
1231 return OutputBuffer { BASE_FORMAT_R16_UINT, sizeof(uint16_t), {} };
1232 }
1233 return OutputBuffer { BASE_FORMAT_R32_UINT, sizeof(uint32_t), {} };
1234 }(submesh.info.indexType);
1235 const auto bufferSize = output.stride * indexCount;
1236 submeshDesc.indexBuffer.byteSize = bufferSize;
1237
1238 // If we need to generate normals or tangents we need CPU copies of the indices
1239 const bool needCpuCopy = !submesh.hasNormals || (submesh.info.tangents && !submesh.hasTangents);
1240 if (needCpuCopy) {
1241 // First convert and write to a plain vector
1242 const auto offset = indexData_.size();
1243 submesh.indexOffset = static_cast<int32_t>(offset);
1244 submesh.indexSize = bufferSize;
1245 indexData_.resize(offset + bufferSize);
1246 output.buffer = { indexData_.data() + offset, bufferSize };
1247 } else {
1248 // Convert directly to the staging buffer.
1249 output.buffer = { buffer + bufferOffset, bufferSize };
1250 }
1251
1252 Fill(output, indices, indexCount);
1253
1254 if (needCpuCopy) {
1255 // Then copy the data to staging buffer.
1256 std::copy(output.buffer.data(), output.buffer.data() + bufferSize, buffer + bufferOffset);
1257 }
1258 }
1259 }
1260
SetJointData(size_t submeshIndex,const DataBuffer & jointData,const DataBuffer & weightData,const DataBuffer & vertexPositions)1261 void MeshBuilder::SetJointData(
1262 size_t submeshIndex, const DataBuffer& jointData, const DataBuffer& weightData, const DataBuffer& vertexPositions)
1263 {
1264 auto buffer = (flags_ & ConfigurationFlagBits::NO_STAGING_BUFFER) ? vertexPtr_ : stagingPtr_;
1265 if (buffer) {
1266 // Vertex data | index data | *joint data* | morph data
1267 MeshComponent::Submesh& submeshDesc = submeshes_[submeshIndex];
1268 const SubmeshExt& submesh = submeshInfos_[submeshIndex];
1269 if (const auto* indexAttributeDesc = GetVertexAttributeDescription(
1270 MeshComponent::Submesh::DM_VB_JOI, vertexInputDeclaration_.attributeDescriptions);
1271 indexAttributeDesc) {
1272 if (const VertexInputDeclaration::VertexInputBindingDescription* bindingDesc = GetVertexBindingeDescription(
1273 indexAttributeDesc->binding, vertexInputDeclaration_.bindingDescriptions);
1274 bindingDesc) {
1275 auto& jointIndexAcc = submeshDesc.bufferAccess[MeshComponent::Submesh::DM_VB_JOI];
1276 jointIndexAcc.offset = static_cast<uint32_t>(
1277 indexDataSize_ + vertexDataSize_ + Align(submesh.jointBufferOffset, BUFFER_ALIGN));
1278 jointIndexAcc.byteSize = (uint32_t)bindingDesc->stride * submesh.info.vertexCount;
1279
1280 OutputBuffer dstData { indexAttributeDesc->format, bindingDesc->stride,
1281 { buffer + jointIndexAcc.offset, jointIndexAcc.byteSize } };
1282 Fill(dstData, jointData, submesh.info.vertexCount);
1283 }
1284 }
1285 if (const auto* weightAttributeDesc = GetVertexAttributeDescription(
1286 MeshComponent::Submesh::DM_VB_JOW, vertexInputDeclaration_.attributeDescriptions);
1287 weightAttributeDesc) {
1288 if (const VertexInputDeclaration::VertexInputBindingDescription* bindingDesc = GetVertexBindingeDescription(
1289 weightAttributeDesc->binding, vertexInputDeclaration_.bindingDescriptions);
1290 bindingDesc) {
1291 auto& jointIndexAcc = submeshDesc.bufferAccess[MeshComponent::Submesh::DM_VB_JOI];
1292 // Process joint weights.
1293 auto& jointWeightAcc = submeshDesc.bufferAccess[MeshComponent::Submesh::DM_VB_JOW];
1294 // index aligned offset + index bytesize -> aligned to offset
1295 jointWeightAcc.offset = (uint32_t)Align(jointIndexAcc.offset + jointIndexAcc.byteSize, BUFFER_ALIGN);
1296 jointWeightAcc.byteSize = (uint32_t)bindingDesc->stride * submesh.info.vertexCount;
1297
1298 OutputBuffer dstData { weightAttributeDesc->format, bindingDesc->stride,
1299 { buffer + jointWeightAcc.offset, jointWeightAcc.byteSize } };
1300 Fill(dstData, weightData, submesh.info.vertexCount);
1301 }
1302 }
1303 submeshDesc.flags |= MeshComponent::Submesh::FlagBits::SKIN_BIT;
1304 CalculateJointBounds(jointData, weightData, vertexPositions);
1305 }
1306 }
1307
SetMorphTargetData(size_t submeshIndex,const DataBuffer & basePositions,const DataBuffer & baseNormals,const DataBuffer & baseTangents,const DataBuffer & targetPositions,const DataBuffer & targetNormals,const DataBuffer & targetTangents)1308 void MeshBuilder::SetMorphTargetData(size_t submeshIndex, const DataBuffer& basePositions,
1309 const DataBuffer& baseNormals, const DataBuffer& baseTangents, const DataBuffer& targetPositions,
1310 const DataBuffer& targetNormals, const DataBuffer& targetTangents)
1311 {
1312 // Submesh info for this submesh.
1313 SubmeshExt& submesh = submeshInfos_[submeshIndex];
1314 if (submesh.info.morphTargetCount > 0) {
1315 auto buffer = (flags_ & ConfigurationFlagBits::NO_STAGING_BUFFER) ? vertexPtr_ : stagingPtr_;
1316 if (buffer) {
1317 // Vertex data | index data | joint data | *morph data*
1318 const auto bufferOffset = static_cast<uint32_t>(vertexDataSize_ + indexDataSize_ + jointDataSize_);
1319
1320 // Offset to morph index data is previous offset + size (or zero for the first submesh)
1321 uint32_t indexOffset = 0U;
1322 if (submeshIndex) {
1323 indexOffset += static_cast<uint32_t>(Align(submeshInfos_[submeshIndex - 1u].morphTargetBufferOffset +
1324 submeshInfos_[submeshIndex - 1u].morphTargetBufferSize,
1325 BUFFER_ALIGN));
1326 }
1327 submesh.morphTargetBufferOffset = indexOffset;
1328
1329 indexOffset += bufferOffset;
1330
1331 // 32bit index/offset for each vertex in each morph target
1332 const uint32_t indexSize = sizeof(uint32_t) * submesh.info.vertexCount;
1333 const uint32_t totalIndexSize =
1334 static_cast<uint32_t>(Align(indexSize * submesh.info.morphTargetCount, BUFFER_ALIGN));
1335
1336 // Data struct (pos, nor, tan) for each vertex. total amount is target size for each target data and one
1337 // base data
1338 const uint32_t targetSize = submesh.info.vertexCount * sizeof(MorphInputData);
1339
1340 // Base data starts after index data
1341 const uint32_t baseOffset = indexOffset + totalIndexSize;
1342 {
1343 OutputBuffer dstData { POSITION_FORMAT, sizeof(MorphInputData), { buffer + baseOffset, targetSize } };
1344 Fill(dstData, basePositions, submesh.info.vertexCount);
1345 }
1346
1347 if (!baseNormals.buffer.empty()) {
1348 #if defined(CORE_MORPH_USE_PACKED_NOR_TAN)
1349 OutputBuffer dstData { NORMAL_FORMAT, sizeof(MorphInputData),
1350 { buffer + baseOffset + offsetof(MorphInputData, nortan), targetSize } };
1351 #else
1352 OutputBuffer dstData { NORMAL_FORMAT, sizeof(MorphInputData),
1353 { buffer + baseOffset + offsetof(MorphInputData, nor), targetSize } };
1354 #endif
1355 Fill(dstData, baseNormals, submesh.info.vertexCount);
1356 }
1357 if (!baseTangents.buffer.empty()) {
1358 #if defined(CORE_MORPH_USE_PACKED_NOR_TAN)
1359 OutputBuffer dstData { TANGENT_FORMAT, sizeof(MorphInputData),
1360 { buffer + baseOffset + offsetof(MorphInputData, nortan) + 8U, targetSize } }; // 8 : step
1361 #else
1362 OutputBuffer dstData { TANGENT_FORMAT, sizeof(MorphInputData),
1363 { buffer + baseOffset + offsetof(MorphInputData, tan), targetSize } };
1364 #endif
1365 Fill(dstData, baseTangents, submesh.info.vertexCount);
1366 }
1367 // Gather non-zero deltas.
1368 if (targetNormals.buffer.empty() && targetTangents.buffer.empty()) {
1369 GatherDeltasP(submesh, buffer, baseOffset, indexOffset, targetSize, targetPositions);
1370 } else if (!targetNormals.buffer.empty() && targetTangents.buffer.empty()) {
1371 GatherDeltasPN(submesh, buffer, baseOffset, indexOffset, targetSize, targetPositions, targetNormals);
1372 } else if (targetNormals.buffer.empty() && !targetTangents.buffer.empty()) {
1373 GatherDeltasPT(submesh, buffer, baseOffset, indexOffset, targetSize, targetPositions, targetTangents);
1374 } else {
1375 GatherDeltasPNT(submesh, buffer, baseOffset, indexOffset, targetSize, targetPositions, targetNormals,
1376 targetTangents);
1377 }
1378
1379 // Actual buffer size based on the offset and size of the last morph target.
1380 submesh.morphTargetBufferSize = submesh.morphTargets[submesh.info.morphTargetCount - 1].offset -
1381 indexOffset +
1382 submesh.morphTargets[submesh.info.morphTargetCount - 1].byteSize;
1383
1384 // Clamp to actual size which might be less than what was asked for before gathering the non-zero deltas.
1385 targetDataSize_ = submesh.morphTargetBufferOffset + submesh.morphTargetBufferSize;
1386
1387 MeshComponent::Submesh& submeshDesc = submeshes_[submeshIndex];
1388 submeshDesc.morphTargetBuffer.offset = submesh.morphTargetBufferOffset + bufferOffset;
1389 submeshDesc.morphTargetBuffer.byteSize = submesh.morphTargetBufferSize;
1390 submeshDesc.morphTargetCount = static_cast<uint32_t>(submesh.morphTargets.size());
1391 }
1392 }
1393 }
1394
SetAABB(size_t submeshIndex,const Math::Vec3 & min,const Math::Vec3 & max)1395 void MeshBuilder::SetAABB(size_t submeshIndex, const Math::Vec3& min, const Math::Vec3& max)
1396 {
1397 MeshComponent::Submesh& submeshDesc = submeshes_[submeshIndex];
1398 submeshDesc.aabbMin = min;
1399 submeshDesc.aabbMax = max;
1400 }
1401
CalculateAABB(size_t submeshIndex,const DataBuffer & positions)1402 void MeshBuilder::CalculateAABB(size_t submeshIndex, const DataBuffer& positions)
1403 {
1404 const auto posFormat = GetFormatSpec(positions.format);
1405 if (posFormat.format == BASE_FORMAT_UNDEFINED) {
1406 CORE_LOG_E("position format (%u) not supported", posFormat.format);
1407 return;
1408 }
1409 const auto posElementSize = posFormat.componentCount * posFormat.componentByteSize;
1410 if (posElementSize > positions.stride) {
1411 return;
1412 }
1413 auto count = positions.buffer.size() / positions.stride;
1414
1415 constexpr float maxLimits = std::numeric_limits<float>::max();
1416 constexpr float minLimits = -std::numeric_limits<float>::max();
1417
1418 Math::Vec3 finalMinimum = { maxLimits, maxLimits, maxLimits };
1419 Math::Vec3 finalMaximum = { minLimits, minLimits, minLimits };
1420
1421 auto srcPtr = positions.buffer.data();
1422 switch (posFormat.format) {
1423 case BASE_FORMAT_R16G16_UNORM: {
1424 uint16_t minimum[RG] { std::numeric_limits<uint16_t>::max() };
1425 uint16_t maximum[RG] { std::numeric_limits<uint16_t>::lowest() };
1426 while (count--) {
1427 uint16_t value[RG] = { reinterpret_cast<const uint16_t*>(srcPtr)[R],
1428 reinterpret_cast<const uint16_t*>(srcPtr)[G] };
1429 srcPtr += positions.stride;
1430 GatherMin(minimum, value);
1431 GatherMax(maximum, value);
1432 }
1433 for (auto i = 0U; i < RG; ++i) {
1434 finalMinimum[i] = Norm<uint16_t>::From(minimum[i]);
1435 }
1436 finalMinimum[B] = 0.f;
1437 for (auto i = 0U; i < RG; ++i) {
1438 finalMaximum[i] = Norm<uint16_t>::From(maximum[i]);
1439 }
1440 finalMaximum[B] = 0.f;
1441 } break;
1442
1443 case BASE_FORMAT_R8G8B8_SNORM: {
1444 int8_t minimum[RGB] { std::numeric_limits<int8_t>::max() };
1445 int8_t maximum[RGB] { std::numeric_limits<int8_t>::lowest() };
1446 while (count--) {
1447 int8_t value[RGB] = { reinterpret_cast<const int8_t*>(srcPtr)[R],
1448 reinterpret_cast<const int8_t*>(srcPtr)[G], reinterpret_cast<const int8_t*>(srcPtr)[B] };
1449 srcPtr += positions.stride;
1450 GatherMin(minimum, value);
1451 GatherMax(maximum, value);
1452 }
1453 for (auto i = 0U; i < RGB; ++i) {
1454 finalMinimum[i] = Norm<int8_t>::From(minimum[i]);
1455 }
1456 for (auto i = 0U; i < RGB; ++i) {
1457 finalMaximum[i] = Norm<int8_t>::From(maximum[i]);
1458 }
1459 } break;
1460
1461 case BASE_FORMAT_R16G16B16_UINT:
1462 case BASE_FORMAT_R16G16B16A16_UINT: {
1463 uint16_t minimum[RGB] { std::numeric_limits<uint16_t>::max() };
1464 uint16_t maximum[RGB] { std::numeric_limits<uint16_t>::lowest() };
1465 while (count--) {
1466 uint16_t value[RGB] = { reinterpret_cast<const uint16_t*>(srcPtr)[R],
1467 reinterpret_cast<const uint16_t*>(srcPtr)[G], reinterpret_cast<const uint16_t*>(srcPtr)[B] };
1468 srcPtr += positions.stride;
1469 GatherMin(minimum, value);
1470 GatherMax(maximum, value);
1471 }
1472 for (auto i = 0U; i < RGB; ++i) {
1473 finalMinimum[i] = Int<uint16_t>::From(minimum[i]);
1474 }
1475 for (auto i = 0U; i < RGB; ++i) {
1476 finalMaximum[i] = Int<uint16_t>::From(maximum[i]);
1477 }
1478 } break;
1479
1480 case BASE_FORMAT_R32G32_SFLOAT: {
1481 while (count--) {
1482 float value[RG] = { reinterpret_cast<const float*>(srcPtr)[R],
1483 reinterpret_cast<const float*>(srcPtr)[G] };
1484 srcPtr += positions.stride;
1485 GatherMin(finalMinimum.data, value);
1486 GatherMax(finalMaximum.data, value);
1487 }
1488 } break;
1489
1490 case BASE_FORMAT_R32G32B32_SFLOAT:
1491 case BASE_FORMAT_R32G32B32A32_SFLOAT: {
1492 while (count--) {
1493 float value[RGB] = { reinterpret_cast<const float*>(srcPtr)[R],
1494 reinterpret_cast<const float*>(srcPtr)[G], reinterpret_cast<const float*>(srcPtr)[B] };
1495 srcPtr += positions.stride;
1496 GatherMin(finalMinimum.data, value);
1497 GatherMax(finalMaximum.data, value);
1498 }
1499 } break;
1500
1501 default:
1502 CORE_LOG_W("CalculateAABB: position format %u not handled.", posFormat.format);
1503 break;
1504 }
1505 SetAABB(submeshIndex, finalMinimum, finalMaximum);
1506 }
1507
GetVertexData() const1508 array_view<const uint8_t> MeshBuilder::GetVertexData() const
1509 {
1510 return array_view<const uint8_t>(stagingPtr_, vertexDataSize_);
1511 }
1512
GetIndexData() const1513 array_view<const uint8_t> MeshBuilder::GetIndexData() const
1514 {
1515 return array_view<const uint8_t>(stagingPtr_ ? (stagingPtr_ + vertexDataSize_) : nullptr, indexDataSize_);
1516 }
1517
GetJointData() const1518 array_view<const uint8_t> MeshBuilder::GetJointData() const
1519 {
1520 return array_view<const uint8_t>(
1521 stagingPtr_ ? (stagingPtr_ + vertexDataSize_ + indexDataSize_) : nullptr, jointDataSize_);
1522 }
1523
GetMorphTargetData() const1524 array_view<const uint8_t> MeshBuilder::GetMorphTargetData() const
1525 {
1526 return array_view<const uint8_t>(
1527 stagingPtr_ ? (stagingPtr_ + vertexDataSize_ + indexDataSize_ + jointDataSize_) : nullptr, targetDataSize_);
1528 }
1529
GetJointBoundsData() const1530 array_view<const float> MeshBuilder::GetJointBoundsData() const
1531 {
1532 return array_view(reinterpret_cast<const float*>(jointBoundsData_.data()),
1533 jointBoundsData_.size() * SIZE_OF_VALUE_TYPE_V<decltype(jointBoundsData_)> / sizeof(float));
1534 }
1535
GetSubmeshes() const1536 array_view<const MeshComponent::Submesh> MeshBuilder::GetSubmeshes() const
1537 {
1538 return array_view<const MeshComponent::Submesh>(submeshes_);
1539 }
1540
GetVertexCount() const1541 uint32_t MeshBuilder::GetVertexCount() const
1542 {
1543 return vertexCount_;
1544 }
1545
GetIndexCount() const1546 uint32_t MeshBuilder::GetIndexCount() const
1547 {
1548 return indexCount_;
1549 }
1550
CreateGpuResources(const GpuBufferCreateInfo & createInfo)1551 void MeshBuilder::CreateGpuResources(const GpuBufferCreateInfo& createInfo)
1552 {
1553 GenerateMissingAttributes();
1554
1555 if (!(flags_ & ConfigurationFlagBits::NO_STAGING_BUFFER)) {
1556 bufferHandles_ = CreateGpuBuffers(
1557 renderContext_, vertexDataSize_, indexDataSize_, jointDataSize_, targetDataSize_, createInfo);
1558
1559 StageToBuffers(renderContext_, vertexDataSize_, indexDataSize_, jointDataSize_, targetDataSize_, bufferHandles_,
1560 stagingBuffer_);
1561 }
1562 }
1563
CreateGpuResources()1564 void MeshBuilder::CreateGpuResources()
1565 {
1566 CreateGpuResources({});
1567 }
1568
CreateMesh(IEcs & ecs) const1569 Entity MeshBuilder::CreateMesh(IEcs& ecs) const
1570 {
1571 if (!vertexDataSize_) {
1572 return {};
1573 }
1574 return CreateMesh(ecs, ecs.GetEntityManager().Create());
1575 }
1576
CreateMesh(IEcs & ecs,Entity meshEntity) const1577 Entity MeshBuilder::CreateMesh(IEcs& ecs, Entity meshEntity) const
1578 {
1579 if (!vertexDataSize_) {
1580 return {};
1581 }
1582
1583 auto meshManager = GetManager<IMeshComponentManager>(ecs);
1584 if (!meshManager) {
1585 return {};
1586 }
1587
1588 if (!meshManager->HasComponent(meshEntity)) {
1589 meshManager->Create(meshEntity);
1590 }
1591
1592 if (auto meshHandle = meshManager->Write(meshEntity); meshHandle) {
1593 MeshComponent& mesh = *meshHandle;
1594
1595 // Copy skin joint bounding boxes.
1596 const size_t jointBoundsDataSize = jointBoundsData_.size();
1597 if (jointBoundsDataSize != 0) {
1598 mesh.jointBounds.reserve(mesh.jointBounds.size() + jointBoundsDataSize * JOINT_BOUNDS_COMPONENTS);
1599 for (const auto& bounds : jointBoundsData_) {
1600 for (const auto& f : bounds.min.data) {
1601 mesh.jointBounds.push_back(f);
1602 }
1603 for (const auto& f : bounds.max.data) {
1604 mesh.jointBounds.push_back(f);
1605 }
1606 }
1607 }
1608
1609 // Only the new submeshes should have new buffers, so assign them before appending.
1610 FillSubmeshBuffers(submeshes_, CreateBuffers(ecs));
1611 mesh.submeshes.append(submeshes_.cbegin(), submeshes_.cend());
1612
1613 // Recalculate AABB for the whole mesh.
1614 const auto minMax = CalculateAabb(mesh.submeshes);
1615 mesh.aabbMin = minMax.minAABB;
1616 mesh.aabbMax = minMax.maxAABB;
1617 }
1618 return meshEntity;
1619 }
1620
GetInterface(const Uid & uid) const1621 const IInterface* MeshBuilder::GetInterface(const Uid& uid) const
1622 {
1623 if ((uid == IMeshBuilder::UID) || (uid == IInterface::UID)) {
1624 return this;
1625 }
1626 return nullptr;
1627 }
1628
GetInterface(const Uid & uid)1629 IInterface* MeshBuilder::GetInterface(const Uid& uid)
1630 {
1631 if ((uid == IMeshBuilder::UID) || (uid == IInterface::UID)) {
1632 return this;
1633 }
1634 return nullptr;
1635 }
1636
Ref()1637 void MeshBuilder::Ref()
1638 {
1639 refCount_++;
1640 }
1641
Unref()1642 void MeshBuilder::Unref()
1643 {
1644 if (--refCount_ == 0) {
1645 delete this;
1646 }
1647 }
1648
EnablePrimitiveRestart(size_t index)1649 void MeshBuilder::EnablePrimitiveRestart(size_t index)
1650 {
1651 if (index < submeshInfos_.size()) {
1652 submeshInfos_[index].info.inputAssembly.enablePrimitiveRestart = true;
1653 }
1654 if (index < submeshes_.size()) {
1655 submeshes_[index].inputAssembly.enablePrimitiveRestart = true;
1656 }
1657 }
1658
1659 // Private methods
CreateBuffers(IEcs & ecs) const1660 MeshBuilder::BufferEntities MeshBuilder::CreateBuffers(IEcs& ecs) const
1661 {
1662 BufferEntities entities;
1663
1664 GenerateMissingAttributes();
1665 BufferHandles handles;
1666 if (bufferHandles_.vertexBuffer) {
1667 handles = bufferHandles_;
1668 } else {
1669 handles =
1670 CreateGpuBuffers(renderContext_, vertexDataSize_, indexDataSize_, jointDataSize_, targetDataSize_, {});
1671 if (!(flags_ & ConfigurationFlagBits::NO_STAGING_BUFFER)) {
1672 StageToBuffers(renderContext_, vertexDataSize_, indexDataSize_, jointDataSize_, targetDataSize_, handles,
1673 stagingBuffer_);
1674 }
1675 }
1676
1677 auto renderHandleManager = GetManager<IRenderHandleComponentManager>(ecs);
1678 if (!renderHandleManager) {
1679 return entities;
1680 }
1681
1682 auto& em = ecs.GetEntityManager();
1683
1684 // Create vertex buffer for this mesh.
1685 entities.vertexBuffer = CreateRenderHandleComponent(em, *renderHandleManager, handles.vertexBuffer);
1686
1687 if (indexDataSize_) {
1688 entities.indexBuffer = entities.vertexBuffer;
1689 }
1690 if (jointDataSize_) {
1691 entities.jointBuffer = entities.vertexBuffer;
1692 }
1693 if (targetDataSize_) {
1694 entities.morphBuffer = entities.vertexBuffer;
1695 }
1696 return entities;
1697 }
1698
GenerateMissingAttributes() const1699 void MeshBuilder::GenerateMissingAttributes() const
1700 {
1701 auto* buffer = (flags_ & ConfigurationFlagBits::NO_STAGING_BUFFER) ? vertexPtr_ : stagingPtr_;
1702 if (!buffer || vertexData_.empty()) {
1703 return;
1704 }
1705 auto submeshIt = submeshes_.begin();
1706 for (auto& submesh : submeshInfos_) {
1707 MeshComponent::Submesh& submeshDesc = *submeshIt++;
1708 if (submesh.hasNormals && submesh.hasUv0 && (submesh.hasTangents || !submesh.info.tangents)) {
1709 continue;
1710 }
1711 // Reserve space for the to be generated normals and uvs
1712 vertexData_.reserve(vertexData_.size() +
1713 (submesh.hasNormals ? 0U : submesh.info.vertexCount * sizeof(Math::Vec3)) +
1714 (submesh.hasUv0 ? 0U : submesh.info.vertexCount * sizeof(Math::Vec2)));
1715
1716 const DataBuffer indexData {
1717 (submesh.info.indexType == IndexType::CORE_INDEX_TYPE_UINT16) ? BASE_FORMAT_R16_UINT : BASE_FORMAT_R32_UINT,
1718 static_cast<uint32_t>(
1719 (submesh.info.indexType == IndexType::CORE_INDEX_TYPE_UINT16) ? sizeof(uint16_t) : sizeof(uint32_t)),
1720 { indexData_.data() + submesh.indexOffset, submesh.indexSize }
1721 };
1722
1723 const DataBuffer positionData { BASE_FORMAT_R32G32B32_SFLOAT, sizeof(Math::Vec3),
1724 { vertexData_.data() + submesh.positionOffset, submesh.positionSize } };
1725
1726 DataBuffer normalData { BASE_FORMAT_R32G32B32_SFLOAT, sizeof(Math::Vec3),
1727 { vertexData_.data() + submesh.normalOffset, submesh.normalSize } };
1728
1729 if (!submesh.hasNormals) {
1730 const auto offset = vertexData_.size();
1731 GenerateDefaultNormals(vertexData_, indexData, positionData, submesh.info.vertexCount,
1732 submesh.info.inputAssembly.primitiveTopology);
1733 submesh.normalOffset = static_cast<int32_t>(offset);
1734 submesh.normalSize = static_cast<uint32_t>(vertexData_.size() - offset);
1735 normalData.buffer = { vertexData_.data() + submesh.normalOffset, submesh.normalSize };
1736
1737 auto& acc = submeshDesc.bufferAccess[MeshComponent::Submesh::DM_VB_NOR];
1738 WriteData(normalData, submesh, MeshComponent::Submesh::DM_VB_NOR, acc.offset, acc.byteSize, buffer);
1739 submesh.hasNormals = true;
1740 }
1741
1742 DataBuffer uvData { BASE_FORMAT_R32G32_SFLOAT, sizeof(Math::Vec2),
1743 { vertexData_.data() + submesh.uvOffset, submesh.uvSize } };
1744 if (!submesh.hasUv0) {
1745 const auto offset = vertexData_.size();
1746 GenerateDefaultUvs(vertexData_, submesh.info.vertexCount);
1747 submesh.uvOffset = static_cast<int32_t>(offset);
1748 submesh.uvSize = static_cast<uint32_t>(vertexData_.size() - offset);
1749 uvData.buffer = { vertexData_.data() + submesh.uvOffset, submesh.uvSize };
1750
1751 auto& acc = submeshDesc.bufferAccess[MeshComponent::Submesh::DM_VB_UV0];
1752 WriteData(uvData, submesh, MeshComponent::Submesh::DM_VB_UV0, acc.offset, acc.byteSize, buffer);
1753 submesh.hasUv0 = true;
1754 }
1755
1756 if (submesh.info.tangents && !submesh.hasTangents) {
1757 DataBuffer tangentData;
1758 vector<uint8_t> generatedTangents;
1759 GenerateDefaultTangents(
1760 tangentData, generatedTangents, indexData, positionData, normalData, uvData, submesh.info.vertexCount);
1761
1762 auto& acc = submeshDesc.bufferAccess[MeshComponent::Submesh::DM_VB_TAN];
1763 if (WriteData(tangentData, submesh, MeshComponent::Submesh::DM_VB_TAN, acc.offset, acc.byteSize, buffer)) {
1764 submeshDesc.flags |= MeshComponent::Submesh::FlagBits::TANGENTS_BIT;
1765 submesh.hasTangents = true;
1766 }
1767 }
1768 }
1769 }
1770
GatherDeltasP(SubmeshExt & submesh,uint8_t * dst,uint32_t baseOffset,uint32_t indexOffset,uint32_t targetSize,const MeshBuilder::DataBuffer & targetPositions)1771 void MeshBuilder::GatherDeltasP(SubmeshExt& submesh, uint8_t* dst, uint32_t baseOffset, uint32_t indexOffset,
1772 uint32_t targetSize, const MeshBuilder::DataBuffer& targetPositions)
1773 {
1774 const auto optionalPosFormat = Verify(targetPositions, submesh.info.vertexCount);
1775 if (!optionalPosFormat) {
1776 return;
1777 }
1778 const auto& posFormat = optionalPosFormat.value().get();
1779
1780 if (posFormat.format == BASE_FORMAT_R32G32B32_SFLOAT) {
1781 // special case which matches glTF 2.0. morph targets are three float components.
1782 GatherDeltasR32G32B32(submesh, dst, baseOffset, indexOffset, targetSize, targetPositions);
1783 } else {
1784 // Target data starts after base
1785 uint32_t targetOffset = baseOffset + targetSize;
1786
1787 auto index = reinterpret_cast<uint32_t*>(dst + indexOffset);
1788 for (uint32_t trg = 0; trg < submesh.info.morphTargetCount; trg++) {
1789 submesh.morphTargets[trg].offset = targetOffset;
1790 const auto startTarget = reinterpret_cast<MorphInputData*>(dst + targetOffset);
1791 auto target = startTarget;
1792 for (uint32_t vertex = 0; vertex < submesh.info.vertexCount; ++vertex) {
1793 // for each vertex in target check that position, normal and tangent deltas are non-zero.
1794 const auto vertexIndex = vertex + (trg * submesh.info.vertexCount);
1795 const Math::Vec3 pos = ConvertAttribute(targetPositions, posFormat, vertexIndex);
1796 const auto zeroDelta = (pos == Math::ZERO_VEC3);
1797 // store offset for each non-zero
1798 *index++ = zeroDelta ? UINT32_MAX : ((targetOffset - baseOffset) / sizeof(MorphInputData));
1799 if (zeroDelta) {
1800 continue;
1801 }
1802 targetOffset += sizeof(MorphInputData);
1803
1804 target->pos = Math::Vec4(pos, static_cast<float>(vertex));
1805 ++target;
1806 }
1807 // Store the size and indexOffset of the gathered deltas.
1808 submesh.morphTargets[trg].byteSize =
1809 static_cast<uint32_t>(reinterpret_cast<ptrdiff_t>(target) - reinterpret_cast<ptrdiff_t>(startTarget));
1810 }
1811 }
1812 }
1813
GatherDeltasPN(SubmeshExt & submesh,uint8_t * dst,uint32_t baseOffset,uint32_t indexOffset,uint32_t targetSize,const MeshBuilder::DataBuffer & targetPositions,const MeshBuilder::DataBuffer & targetNormals)1814 void MeshBuilder::GatherDeltasPN(SubmeshExt& submesh, uint8_t* dst, uint32_t baseOffset, uint32_t indexOffset,
1815 uint32_t targetSize, const MeshBuilder::DataBuffer& targetPositions, const MeshBuilder::DataBuffer& targetNormals)
1816 {
1817 const auto optionalPosFormat = Verify(targetPositions, submesh.info.vertexCount);
1818 if (!optionalPosFormat) {
1819 return;
1820 }
1821
1822 const auto optionalNorFormat = Verify(targetNormals, submesh.info.vertexCount);
1823 if (!optionalNorFormat) {
1824 return;
1825 }
1826
1827 const auto& posFormat = optionalPosFormat.value().get();
1828 const auto& norFormat = optionalNorFormat.value().get();
1829
1830 // Target data starts after base
1831 uint32_t targetOffset = baseOffset + targetSize;
1832
1833 auto index = reinterpret_cast<uint32_t*>(dst + indexOffset);
1834 if (posFormat.format == BASE_FORMAT_R32G32B32_SFLOAT && norFormat.format == BASE_FORMAT_R32G32B32_SFLOAT) {
1835 // special case which matches glTF 2.0. morph targets are three float components.
1836 GatherDeltasR32G32B32(submesh, dst, baseOffset, indexOffset, targetSize, targetPositions, targetNormals);
1837 } else {
1838 for (uint32_t trg = 0; trg < submesh.info.morphTargetCount; trg++) {
1839 submesh.morphTargets[trg].offset = targetOffset;
1840 const auto startTarget = reinterpret_cast<MorphInputData*>(dst + targetOffset);
1841 auto target = startTarget;
1842 for (uint32_t vertex = 0; vertex < submesh.info.vertexCount; ++vertex) {
1843 // for each vertex in target check that position and normal deltas are non-zero.
1844 const auto vertexIndex = vertex + (trg * submesh.info.vertexCount);
1845 const Math::Vec3 pos = ConvertAttribute(targetPositions, posFormat, vertexIndex);
1846 const Math::Vec3 nor = ConvertAttribute(targetNormals, norFormat, vertexIndex);
1847 const auto zeroDelta = (pos == Math::ZERO_VEC3 && nor == Math::ZERO_VEC3);
1848 // store offset for each non-zero
1849 *index++ = zeroDelta ? UINT32_MAX : ((targetOffset - baseOffset) / sizeof(MorphInputData));
1850 if (zeroDelta) {
1851 continue;
1852 }
1853 targetOffset += sizeof(MorphInputData);
1854
1855 target->pos = Math::Vec4(pos, static_cast<float>(vertex));
1856 #if defined(CORE_MORPH_USE_PACKED_NOR_TAN)
1857 target->nortan.x = Math::PackHalf2X16({ nor.data[R], nor.data[G] });
1858 target->nortan.y = Math::PackHalf2X16({ nor.data[B], 0.f });
1859 #else
1860 target->nor = Math::Vec4(nor.data[R], nor.data[G], nor.data[B], 0.f);
1861 #endif
1862 ++target;
1863 }
1864 // Store the size of the gathered deltas.
1865 submesh.morphTargets[trg].byteSize =
1866 static_cast<uint32_t>(reinterpret_cast<ptrdiff_t>(target) - reinterpret_cast<ptrdiff_t>(startTarget));
1867 }
1868 }
1869 }
1870
GatherDeltasPT(SubmeshExt & submesh,uint8_t * dst,uint32_t baseOffset,uint32_t indexOffset,uint32_t targetSize,const MeshBuilder::DataBuffer & targetPositions,const MeshBuilder::DataBuffer & targetTangents)1871 void MeshBuilder::GatherDeltasPT(SubmeshExt& submesh, uint8_t* dst, uint32_t baseOffset, uint32_t indexOffset,
1872 uint32_t targetSize, const MeshBuilder::DataBuffer& targetPositions, const MeshBuilder::DataBuffer& targetTangents)
1873 {}
1874
GatherDeltasPNT(SubmeshExt & submesh,uint8_t * dst,uint32_t baseOffset,uint32_t indexOffset,uint32_t targetSize,const MeshBuilder::DataBuffer & targetPositions,const MeshBuilder::DataBuffer & targetNormals,const MeshBuilder::DataBuffer & targetTangents)1875 void MeshBuilder::GatherDeltasPNT(SubmeshExt& submesh, uint8_t* dst, uint32_t baseOffset, uint32_t indexOffset,
1876 uint32_t targetSize, const MeshBuilder::DataBuffer& targetPositions, const MeshBuilder::DataBuffer& targetNormals,
1877 const MeshBuilder::DataBuffer& targetTangents)
1878 {
1879 const auto optionalPosFormat = Verify(targetPositions, submesh.info.vertexCount);
1880 if (!optionalPosFormat) {
1881 return;
1882 }
1883
1884 const auto optionalNorFormat = Verify(targetNormals, submesh.info.vertexCount);
1885 if (!optionalNorFormat) {
1886 return;
1887 }
1888
1889 const auto optionalTanFormat = Verify(targetTangents, submesh.info.vertexCount);
1890 if (!optionalTanFormat) {
1891 return;
1892 }
1893
1894 const auto& posFormat = optionalPosFormat.value().get();
1895 const auto& norFormat = optionalNorFormat.value().get();
1896 const auto& tanFormat = optionalTanFormat.value().get();
1897
1898 // Target data starts after base
1899 uint32_t targetOffset = baseOffset + targetSize;
1900
1901 auto index = reinterpret_cast<uint32_t*>(dst + indexOffset);
1902 if (posFormat.format == BASE_FORMAT_R32G32B32_SFLOAT && norFormat.format == BASE_FORMAT_R32G32B32_SFLOAT &&
1903 tanFormat.format == BASE_FORMAT_R32G32B32_SFLOAT) {
1904 // special case which matches glTF 2.0. morph targets are three float components.
1905 GatherDeltasR32G32B32(
1906 submesh, dst, baseOffset, indexOffset, targetSize, targetPositions, targetNormals, targetTangents);
1907 } else {
1908 for (uint32_t trg = 0; trg < submesh.info.morphTargetCount; trg++) {
1909 submesh.morphTargets[trg].offset = targetOffset;
1910 const auto startTarget = reinterpret_cast<MorphInputData*>(dst + targetOffset);
1911 auto target = startTarget;
1912 for (uint32_t vertex = 0; vertex < submesh.info.vertexCount; ++vertex) {
1913 // for each vertex in target check that position, normal and tangent deltas are non-zero.
1914 const auto vertexIndex = vertex + (trg * submesh.info.vertexCount);
1915 const Math::Vec3 pos = ConvertAttribute(targetPositions, posFormat, vertexIndex);
1916 const Math::Vec3 nor = ConvertAttribute(targetNormals, norFormat, vertexIndex);
1917 const Math::Vec3 tan = ConvertAttribute(targetTangents, tanFormat, vertexIndex);
1918 const auto zeroDelta = (pos == Math::ZERO_VEC3 && nor == Math::ZERO_VEC3 && tan == Math::ZERO_VEC3);
1919 // store offset for each non-zero
1920 *index++ = zeroDelta ? UINT32_MAX : ((targetOffset - baseOffset) / sizeof(MorphInputData));
1921 if (zeroDelta) {
1922 continue;
1923 }
1924 targetOffset += sizeof(MorphInputData);
1925
1926 target->pos = Math::Vec4(pos, static_cast<float>(vertex));
1927 #if defined(CORE_MORPH_USE_PACKED_NOR_TAN)
1928 target->nortan.x = Math::PackHalf2X16({ nor.data[R], nor.data[G] });
1929 target->nortan.y = Math::PackHalf2X16({ nor.data[B], 0.f });
1930 target->nortan.z = Math::PackHalf2X16({ tan.data[R], tan.data[G] });
1931 target->nortan.w = Math::PackHalf2X16({ tan.data[B], 0.f });
1932 #else
1933 target->nor = Math::Vec4(nor.data[R], nor.data[G], nor.data[B], 0.f);
1934 target->tan = Math::Vec4(tan.data[R], tan.data[G], tan.data[B], 0.f);
1935 #endif
1936 ++target;
1937 }
1938 // Store the size of the gathered deltas.
1939 submesh.morphTargets[trg].byteSize =
1940 static_cast<uint32_t>(reinterpret_cast<ptrdiff_t>(target) - reinterpret_cast<ptrdiff_t>(startTarget));
1941 }
1942 }
1943 }
1944
CalculateJointBounds(const DataBuffer & jointData,const DataBuffer & weightData,const DataBuffer & positionData)1945 void MeshBuilder::CalculateJointBounds(
1946 const DataBuffer& jointData, const DataBuffer& weightData, const DataBuffer& positionData)
1947 {
1948 // Calculate joint bounds as the bounds of the vertices that the joint references.
1949
1950 const auto jointFormat = GetFormatSpec(jointData.format);
1951 if (jointFormat.format == BASE_FORMAT_UNDEFINED) {
1952 return;
1953 }
1954 if (const auto jointElementSize = jointFormat.componentCount * jointFormat.componentByteSize;
1955 jointElementSize > jointData.stride) {
1956 return;
1957 }
1958
1959 const auto weightFormat = GetFormatSpec(weightData.format);
1960 if (weightFormat.format == BASE_FORMAT_UNDEFINED) {
1961 return;
1962 }
1963 if (const auto weightElementSize = weightFormat.componentCount * weightFormat.componentByteSize;
1964 weightElementSize > weightData.stride) {
1965 return;
1966 }
1967
1968 const auto positionFormat = GetFormatSpec(positionData.format);
1969 if (positionFormat.format == BASE_FORMAT_UNDEFINED) {
1970 return;
1971 }
1972 if (const auto positionElementSize = positionFormat.componentCount * positionFormat.componentByteSize;
1973 positionElementSize > positionData.stride) {
1974 return;
1975 }
1976
1977 const auto* weights = weightData.buffer.data();
1978 const auto* joints = jointData.buffer.data();
1979
1980 const size_t jointIndexCount = jointData.buffer.size() / jointData.stride;
1981
1982 // Find the amount of referenced joints
1983 size_t maxJointIndex = 0;
1984 for (size_t i = 0; i < jointIndexCount; ++i) {
1985 float fWeights[4U];
1986 auto j = 0U;
1987 for (; j < weightFormat.componentCount; ++j) {
1988 fWeights[j] = weightFormat.toIntermediate(weights + j * weightFormat.componentByteSize);
1989 }
1990 weights += weightData.stride;
1991 for (size_t w = 0; w < j; ++w) {
1992 // Ignore joints with weight that is effectively 0.0
1993 if (fWeights[w] >= Math::EPSILON) {
1994 const uint8_t jointIndex = joints[jointFormat.componentByteSize * w];
1995
1996 if (jointIndex > maxJointIndex) {
1997 maxJointIndex = jointIndex;
1998 }
1999 }
2000 }
2001 joints += jointData.stride;
2002 }
2003
2004 // Make sure bounds data is big enough. Initialize new bounds to min and max values.
2005 const size_t oldSize = jointBoundsData_.size();
2006 const size_t newSize = (maxJointIndex + 1);
2007 if (newSize > 0 && newSize > oldSize) {
2008 constexpr float floatMin = std::numeric_limits<float>::lowest();
2009 constexpr float floatMax = std::numeric_limits<float>::max();
2010
2011 constexpr const Bounds minMax = { { floatMax, floatMax, floatMax }, { floatMin, floatMin, floatMin } };
2012 jointBoundsData_.resize(newSize, minMax);
2013 }
2014
2015 weights = weightData.buffer.data();
2016 joints = jointData.buffer.data();
2017 const auto* positions = positionData.buffer.data();
2018 for (auto i = 0U; i < jointIndexCount; ++i) {
2019 // Each vertex can reference 4 joint indices.
2020 Math::Vec3 pos;
2021 auto ptr = positions + i * positionData.stride;
2022 for (auto j = 0U; j < positionFormat.componentCount; ++j) {
2023 pos[j] = positionFormat.toIntermediate(ptr + j * positionFormat.componentByteSize);
2024 }
2025
2026 float fWeights[4U]; // 4 : size
2027 for (auto j = 0U; j < weightFormat.componentCount; ++j) {
2028 fWeights[j] = weightFormat.toIntermediate(weights + j * weightFormat.componentByteSize);
2029 }
2030 weights += weightData.stride;
2031 for (size_t w = 0; w < countof(fWeights); ++w) {
2032 if (fWeights[w] < Math::EPSILON) {
2033 // Ignore joints with weight that is effectively 0.0
2034 continue;
2035 }
2036
2037 auto& boundsData = jointBoundsData_[joints[w]];
2038 boundsData.min = Math::min(boundsData.min, pos);
2039 boundsData.max = Math::max(boundsData.max, pos);
2040 }
2041 joints += jointData.stride;
2042 }
2043 }
2044
WriteData(const DataBuffer & srcData,const SubmeshExt & submesh,uint32_t attributeLocation,uint32_t & byteOffset,uint32_t & byteSize,uint8_t * dst) const2045 bool MeshBuilder::WriteData(const DataBuffer& srcData, const SubmeshExt& submesh, uint32_t attributeLocation,
2046 uint32_t& byteOffset, uint32_t& byteSize, uint8_t* dst) const
2047 {
2048 if (const VertexInputDeclaration::VertexInputAttributeDescription* attributeDesc =
2049 GetVertexAttributeDescription(attributeLocation, vertexInputDeclaration_.attributeDescriptions);
2050 attributeDesc) {
2051 if (const VertexInputDeclaration::VertexInputBindingDescription* bindingDesc =
2052 GetVertexBindingeDescription(attributeDesc->binding, vertexInputDeclaration_.bindingDescriptions);
2053 bindingDesc) {
2054 // this offset and size should be aligned
2055 byteOffset = submesh.vertexBindingOffset[bindingDesc->binding] + attributeDesc->offset;
2056 byteSize = submesh.info.vertexCount * bindingDesc->stride;
2057 OutputBuffer dstData { attributeDesc->format, bindingDesc->stride, { dst + byteOffset, byteSize } };
2058 Fill(dstData, srcData, submesh.info.vertexCount);
2059 return true;
2060 }
2061 }
2062 return false;
2063 }
2064 CORE3D_END_NAMESPACE()
2065