ProceduralSkinnedMeshUtils.cpp 16 KB

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