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