ProceduralSkinnedMeshUtils.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <ProceduralSkinnedMesh.h>
  9. #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
  10. #include <Atom/RPI.Reflect/Buffer/BufferAssetCreator.h>
  11. #include <Atom/RPI.Reflect/ResourcePoolAssetCreator.h>
  12. #include <Atom/RPI.Reflect/Model/ModelAssetCreator.h>
  13. #include <Atom/RPI.Reflect/Model/ModelLodAssetCreator.h>
  14. #include <Atom/RPI.Reflect/Model/SkinJointIdPadding.h>
  15. #include <Atom/Feature/SkinnedMesh/SkinnedMeshInputBuffers.h>
  16. namespace
  17. {
  18. static const char* const DefaultSkinnedMeshMaterial = "shaders/debugvertexnormals.azmaterial";
  19. }
  20. namespace AtomSampleViewer
  21. {
  22. static AZ::Data::Asset<AZ::RPI::BufferAsset> CreateBufferAsset(
  23. const void* data, const AZ::RHI::BufferViewDescriptor& bufferViewDescriptor)
  24. {
  25. AZ::RPI::BufferAssetCreator creator;
  26. AZ::Data::AssetId assetId;
  27. assetId.m_guid = AZ::Uuid::CreateRandom();
  28. creator.Begin(assetId);
  29. AZ::RHI::BufferDescriptor bufferDescriptor;
  30. bufferDescriptor.m_bindFlags = AZ::RHI::BufferBindFlags::InputAssembly | AZ::RHI::BufferBindFlags::ShaderRead;
  31. bufferDescriptor.m_byteCount = bufferViewDescriptor.m_elementSize * bufferViewDescriptor.m_elementCount;
  32. creator.SetBuffer(data, bufferDescriptor.m_byteCount, bufferDescriptor);
  33. creator.SetBufferViewDescriptor(bufferViewDescriptor);
  34. creator.SetUseCommonPool(AZ::RPI::CommonBufferPoolType::StaticInputAssembly);
  35. AZ::Data::Asset<AZ::RPI::BufferAsset> bufferAsset;
  36. creator.End(bufferAsset);
  37. return bufferAsset;
  38. }
  39. static AZ::Data::Asset<AZ::RPI::BufferAsset> CreateTypedBufferAsset(
  40. const void* data, const size_t elementCount, AZ::RHI::Format format)
  41. {
  42. AZ::RHI::BufferViewDescriptor bufferViewDescriptor =
  43. AZ::RHI::BufferViewDescriptor::CreateTyped(0, static_cast<uint32_t>(elementCount), format);
  44. return CreateBufferAsset(data, bufferViewDescriptor);
  45. }
  46. static AZ::Data::Asset<AZ::RPI::BufferAsset> CreateRawBufferAsset(
  47. const void* data, size_t elementCount, size_t elementSizeInBytes)
  48. {
  49. AZ::RHI::BufferViewDescriptor bufferViewDescriptor =
  50. AZ::RHI::BufferViewDescriptor::CreateRaw(0, static_cast<uint32_t>(elementCount * elementSizeInBytes));
  51. return CreateBufferAsset(data, bufferViewDescriptor);
  52. }
  53. // Create a buffer view descriptor based on the properties of the lod buffer, but using the sub-mesh's element count and offset
  54. static AZ::RHI::BufferViewDescriptor CreateSubmeshBufferViewDescriptor(const AZ::Data::Asset<AZ::RPI::BufferAsset>& lodBufferAsset, uint32_t elementCount, uint32_t elementOffset)
  55. {
  56. AZ::RHI::BufferViewDescriptor viewDescriptor = lodBufferAsset->GetBufferViewDescriptor();
  57. viewDescriptor.m_elementCount = elementCount;
  58. viewDescriptor.m_elementOffset = elementOffset;
  59. return viewDescriptor;
  60. }
  61. template <typename T>
  62. static void DuplicateVertices(T& vertices, uint32_t elementsPerSubMesh, uint32_t subMeshCount)
  63. {
  64. // Increase the size of the vertex buffer, and then copy the original vertex buffer data into the new elements
  65. vertices.resize(elementsPerSubMesh * subMeshCount);
  66. for (uint32_t i = 1; i < subMeshCount; ++i)
  67. {
  68. AZStd::copy(vertices.begin(), vertices.begin() + elementsPerSubMesh, vertices.begin() + elementsPerSubMesh * i);
  69. }
  70. }
  71. AZ::Data::Instance<AZ::RPI::Model> CreateModelFromProceduralSkinnedMesh(ProceduralSkinnedMesh& proceduralMesh)
  72. {
  73. using namespace AZ;
  74. Data::AssetId assetId;
  75. assetId.m_guid = AZ::Uuid::CreateRandom();
  76. // Each model gets a unique, random ID, so if the same source model is used for multiple instances, multiple target models will be created.
  77. RPI::ModelAssetCreator modelCreator;
  78. modelCreator.Begin(Uuid::CreateRandom());
  79. modelCreator.SetName(AZStd::string("ProceduralSkinnedMesh_" + assetId.m_guid.ToString<AZStd::string>()));
  80. uint32_t submeshCount = proceduralMesh.GetSubMeshCount();
  81. uint32_t verticesPerSubmesh = aznumeric_caster(proceduralMesh.m_positions.size());
  82. uint32_t totalVertices = verticesPerSubmesh * submeshCount;
  83. uint32_t jointIdCountPerSubmesh = verticesPerSubmesh * proceduralMesh.GetInfluencesPerVertex();
  84. uint32_t extraJointIdCount = AZ::RPI::CalculateJointIdPaddingCount(jointIdCountPerSubmesh);
  85. uint32_t extraPackedIdCount = extraJointIdCount / 2;
  86. // Copy the original buffer data n-times to create the data for extra sub-meshes
  87. DuplicateVertices(proceduralMesh.m_indices, aznumeric_caster(proceduralMesh.m_indices.size()), submeshCount);
  88. DuplicateVertices(proceduralMesh.m_positions, verticesPerSubmesh, submeshCount);
  89. DuplicateVertices(proceduralMesh.m_normals, verticesPerSubmesh, submeshCount);
  90. DuplicateVertices(proceduralMesh.m_tangents, verticesPerSubmesh, submeshCount);
  91. DuplicateVertices(proceduralMesh.m_bitangents, verticesPerSubmesh, submeshCount);
  92. DuplicateVertices(proceduralMesh.m_uvs, verticesPerSubmesh, submeshCount);
  93. DuplicateVertices(proceduralMesh.m_blendWeights, verticesPerSubmesh * proceduralMesh.GetInfluencesPerVertex(), submeshCount);
  94. // Insert the jointId padding first before duplicating
  95. AZStd::vector<uint32_t> extraIds(extraPackedIdCount, 0);
  96. // Track the count of 32-byte 'elements' (packed) and offsets for creating sub-mesh views
  97. uint32_t jointIdElementCountPerSubmesh = aznumeric_caster(proceduralMesh.m_blendIndices.size());
  98. uint32_t jointIdOffsetElementsPerSubmesh = jointIdElementCountPerSubmesh + extraPackedIdCount;
  99. proceduralMesh.m_blendIndices.insert(proceduralMesh.m_blendIndices.end(), extraIds.begin(), extraIds.end());
  100. DuplicateVertices(
  101. proceduralMesh.m_blendIndices, aznumeric_caster(proceduralMesh.m_blendIndices.size()), submeshCount);
  102. // Offset duplicate positions in the +y direction, so each sub-mesh ends up in a unique position
  103. for (uint32_t subMeshIndex = 1; subMeshIndex < submeshCount; ++subMeshIndex)
  104. {
  105. for (uint32_t i = 0; i < verticesPerSubmesh; ++i)
  106. {
  107. proceduralMesh.m_positions[subMeshIndex*verticesPerSubmesh + i][1] +=
  108. aznumeric_cast<float>(subMeshIndex) * proceduralMesh.GetSubMeshYOffset();
  109. }
  110. }
  111. auto indexBuffer = CreateTypedBufferAsset(proceduralMesh.m_indices.data(), proceduralMesh.m_indices.size(), AZ::RHI::Format::R32_FLOAT);
  112. auto positionBuffer = CreateTypedBufferAsset(proceduralMesh.m_positions.data(), proceduralMesh.m_positions.size(), AZ::RHI::Format::R32G32B32_FLOAT);
  113. auto normalBuffer = CreateTypedBufferAsset(proceduralMesh.m_normals.data(), proceduralMesh.m_normals.size(), AZ::RHI::Format::R32G32B32_FLOAT);
  114. auto tangentBuffer = CreateTypedBufferAsset(proceduralMesh.m_tangents.data(), proceduralMesh.m_tangents.size(), AZ::RHI::Format::R32G32B32A32_FLOAT);
  115. auto bitangentBuffer = CreateTypedBufferAsset(proceduralMesh.m_bitangents.data(), proceduralMesh.m_bitangents.size(), AZ::RHI::Format::R32G32B32_FLOAT);
  116. auto uvBuffer = CreateTypedBufferAsset(proceduralMesh.m_uvs.data(), proceduralMesh.m_uvs.size(), AZ::RHI::Format::R32G32_FLOAT);
  117. auto skinJointIdBuffer = CreateRawBufferAsset(proceduralMesh.m_blendIndices.data(), proceduralMesh.m_blendIndices.size(), sizeof(proceduralMesh.m_blendIndices[0]));
  118. auto skinJointWeightBuffer = CreateTypedBufferAsset(proceduralMesh.m_blendWeights.data(), proceduralMesh.m_blendWeights.size(), AZ::RHI::Format::R32_FLOAT);
  119. //
  120. // Lod
  121. //
  122. RPI::ModelLodAssetCreator modelLodCreator;
  123. modelLodCreator.Begin(Data::AssetId(Uuid::CreateRandom()));
  124. modelLodCreator.AddLodStreamBuffer(indexBuffer);
  125. modelLodCreator.AddLodStreamBuffer(positionBuffer);
  126. modelLodCreator.AddLodStreamBuffer(normalBuffer);
  127. modelLodCreator.AddLodStreamBuffer(tangentBuffer);
  128. modelLodCreator.AddLodStreamBuffer(bitangentBuffer);
  129. modelLodCreator.AddLodStreamBuffer(uvBuffer);
  130. modelLodCreator.AddLodStreamBuffer(skinJointIdBuffer);
  131. modelLodCreator.AddLodStreamBuffer(skinJointWeightBuffer);
  132. for (uint32_t submeshIndex = 0; submeshIndex < submeshCount; ++submeshIndex)
  133. {
  134. //
  135. // Submesh
  136. //
  137. modelLodCreator.BeginMesh();
  138. // Set the index buffer view
  139. RHI::BufferViewDescriptor indexBufferDescriptor = indexBuffer->GetBufferViewDescriptor();
  140. uint32_t lodTriangleCount = indexBufferDescriptor.m_elementCount / 3;
  141. uint32_t meshTriangleCount = lodTriangleCount / submeshCount;
  142. uint32_t indexOffset = meshTriangleCount * submeshIndex * 3;
  143. if (submeshIndex == submeshCount - 1)
  144. {
  145. meshTriangleCount += lodTriangleCount % submeshCount;
  146. }
  147. uint32_t indexCount = meshTriangleCount * 3;
  148. modelLodCreator.SetMeshIndexBuffer(AZ::RPI::BufferAssetView{ indexBuffer, CreateSubmeshBufferViewDescriptor(indexBuffer, indexCount, indexOffset) });
  149. // Get the element count and offset for this sub-mesh
  150. uint32_t elementCount = verticesPerSubmesh;
  151. uint32_t elementOffset = verticesPerSubmesh * submeshIndex;
  152. // Include any truncated vertices if this is the last mesh
  153. if (submeshIndex == submeshCount - 1)
  154. {
  155. elementCount += totalVertices % verticesPerSubmesh;
  156. }
  157. modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "POSITION" }, AZ::Name(), AZ::RPI::BufferAssetView{ positionBuffer, CreateSubmeshBufferViewDescriptor(positionBuffer, elementCount, elementOffset) });
  158. modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "NORMAL" }, AZ::Name(), AZ::RPI::BufferAssetView{ normalBuffer, CreateSubmeshBufferViewDescriptor(normalBuffer, elementCount, elementOffset) });
  159. modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "TANGENT" }, AZ::Name(), AZ::RPI::BufferAssetView{ tangentBuffer, CreateSubmeshBufferViewDescriptor(tangentBuffer, elementCount, elementOffset) });
  160. modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "BITANGENT" }, AZ::Name(), AZ::RPI::BufferAssetView{ bitangentBuffer, CreateSubmeshBufferViewDescriptor(bitangentBuffer, elementCount, elementOffset) });
  161. modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "UV" }, AZ::Name(), AZ::RPI::BufferAssetView{ uvBuffer, CreateSubmeshBufferViewDescriptor(uvBuffer, elementCount, elementOffset) });
  162. modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "SKIN_JOINTINDICES" }, AZ::Name(), AZ::RPI::BufferAssetView{ skinJointIdBuffer, CreateSubmeshBufferViewDescriptor(skinJointIdBuffer, jointIdElementCountPerSubmesh, jointIdOffsetElementsPerSubmesh * submeshIndex) });
  163. uint32_t jointWeightElementCount = elementCount * proceduralMesh.GetInfluencesPerVertex();
  164. uint32_t jointWeightOffset = elementOffset * proceduralMesh.GetInfluencesPerVertex();
  165. modelLodCreator.AddMeshStreamBuffer(RHI::ShaderSemantic{ "SKIN_WEIGHTS" }, AZ::Name(), AZ::RPI::BufferAssetView{ skinJointWeightBuffer, CreateSubmeshBufferViewDescriptor(skinJointWeightBuffer, jointWeightElementCount, jointWeightOffset) });
  166. AZ::Aabb localAabb = proceduralMesh.m_aabb;
  167. modelLodCreator.SetMeshAabb(AZStd::move(localAabb));
  168. RPI::ModelMaterialSlot::StableId slotId = 0;
  169. modelCreator.AddMaterialSlot(RPI::ModelMaterialSlot{slotId, AZ::Name{}, AZ::RPI::AssetUtils::LoadAssetByProductPath<AZ::RPI::MaterialAsset>(DefaultSkinnedMeshMaterial)});
  170. modelLodCreator.SetMeshMaterialSlot(slotId);
  171. modelLodCreator.EndMesh();
  172. }
  173. Data::Asset<RPI::ModelLodAsset> lodAsset;
  174. modelLodCreator.End(lodAsset);
  175. modelCreator.AddLodAsset(AZStd::move(lodAsset));
  176. Data::Asset<RPI::ModelAsset> modelAsset;
  177. modelCreator.End(modelAsset);
  178. return RPI::Model::FindOrCreate(modelAsset);
  179. }
  180. AZStd::intrusive_ptr<AZ::Render::SkinnedMeshInputBuffers> CreateSkinnedMeshInputBuffersFromProceduralSkinnedMesh(ProceduralSkinnedMesh& proceduralMesh)
  181. {
  182. AZStd::intrusive_ptr<AZ::Render::SkinnedMeshInputBuffers> skinnedMeshInputBuffers = aznew AZ::Render::SkinnedMeshInputBuffers;
  183. AZ::Data::AssetId assetId;
  184. assetId.m_guid = AZ::Uuid::CreateRandom();
  185. AZ::Data::Instance<AZ::RPI::Model> model = CreateModelFromProceduralSkinnedMesh(proceduralMesh);
  186. skinnedMeshInputBuffers->CreateFromModelAsset(model->GetModelAsset());
  187. return skinnedMeshInputBuffers;
  188. }
  189. AZ::Data::Instance<AZ::RPI::Buffer> CreateBoneTransformBufferFromProceduralSkinnedMesh(const ProceduralSkinnedMesh& proceduralMesh)
  190. {
  191. AZ::RHI::BufferViewDescriptor bufferViewDescriptor = AZ::RHI::BufferViewDescriptor::CreateStructured(0, aznumeric_cast<uint32_t>(proceduralMesh.m_boneMatrices.size()), sizeof(AZ::Matrix3x4));
  192. AZStd::vector<float> boneFloats(proceduralMesh.m_boneMatrices.size() * 12);
  193. for (int i = 0; i < proceduralMesh.m_boneMatrices.size(); ++i)
  194. {
  195. proceduralMesh.m_boneMatrices[i].StoreToRowMajorFloat12(&boneFloats[i * 12]);
  196. }
  197. const uint32_t bufferSize = bufferViewDescriptor.m_elementCount * bufferViewDescriptor.m_elementSize;
  198. AZ::Data::Asset<AZ::RPI::ResourcePoolAsset> bufferPoolAsset;
  199. {
  200. auto bufferPoolDesc = AZStd::make_unique<AZ::RHI::BufferPoolDescriptor>();
  201. bufferPoolDesc->m_bindFlags = AZ::RHI::BufferBindFlags::ShaderRead;
  202. bufferPoolDesc->m_heapMemoryLevel = AZ::RHI::HeapMemoryLevel::Device;
  203. AZ::RPI::ResourcePoolAssetCreator creator;
  204. creator.Begin(AZ::Uuid::CreateRandom());
  205. creator.SetPoolDescriptor(AZStd::move(bufferPoolDesc));
  206. creator.SetPoolName("ActorPool");
  207. creator.End(bufferPoolAsset);
  208. }
  209. AZ::Data::Asset<AZ::RPI::BufferAsset> asset;
  210. {
  211. AZ::RHI::BufferDescriptor bufferDescriptor;
  212. bufferDescriptor.m_bindFlags = AZ::RHI::BufferBindFlags::ShaderRead;
  213. bufferDescriptor.m_byteCount = bufferSize;
  214. AZ::RPI::BufferAssetCreator creator;
  215. creator.Begin(AZ::Uuid::CreateRandom());
  216. creator.SetPoolAsset(bufferPoolAsset);
  217. creator.SetBuffer(boneFloats.data(), bufferDescriptor.m_byteCount, bufferDescriptor);
  218. creator.SetBufferViewDescriptor(bufferViewDescriptor);
  219. creator.End(asset);
  220. }
  221. return AZ::RPI::Buffer::FindOrCreate(asset);
  222. }
  223. }