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