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