/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ #include #include #include #include #include #include #include #include namespace UnitTest { ActorHelper::ActorHelper(const char* name) : EMotionFX::Actor(name) { } AZ::u32 ActorHelper::AddJoint( const AZStd::string& name, const AZ::Transform localTransform, const AZStd::string& parentName) { EMotionFX::Node* parentNode = GetSkeleton()->FindNodeByNameNoCase(parentName.c_str()); auto node = AddNode( GetNumNodes(), name.c_str(), (parentNode) ? parentNode->GetNodeIndex() : MCORE_INVALIDINDEX32); GetBindPose()->SetLocalSpaceTransform(node->GetNodeIndex(), localTransform); return node->GetNodeIndex(); } void ActorHelper::AddClothCollider(const Physics::CharacterColliderNodeConfiguration& colliderNode) { GetPhysicsSetup()->GetConfig().m_clothConfig.m_nodes.push_back(colliderNode); } void ActorHelper::FinishSetup() { SetID(0); GetSkeleton()->UpdateNodeIndexValues(0); ResizeTransformData(); PostCreateInit(); } AZ::Data::Asset CreateAssetFromActor( AZStd::unique_ptr actor) { AZ::Data::AssetId assetId(AZ::Uuid::CreateRandom()); AZ::Data::Asset actorAsset = AZ::Data::AssetManager::Instance().CreateAsset(assetId); actorAsset.GetAs()->SetData(AZStd::move(actor)); return actorAsset; } Physics::CharacterColliderNodeConfiguration CreateSphereCollider( const AZStd::string& jointName, float radius, const AZ::Transform& offset) { auto colliderConf = AZStd::make_shared(); colliderConf->m_position = offset.GetTranslation(); colliderConf->m_rotation = AZ::Quaternion::CreateFromTransform(offset); auto shapeConf = AZStd::make_shared(radius); Physics::CharacterColliderNodeConfiguration collider; collider.m_name = jointName; collider.m_shapes.emplace_back(colliderConf, shapeConf); return collider; } Physics::CharacterColliderNodeConfiguration CreateCapsuleCollider( const AZStd::string& jointName, float height, float radius, const AZ::Transform& offset) { auto colliderConf = AZStd::make_shared(); colliderConf->m_position = offset.GetTranslation(); colliderConf->m_rotation = AZ::Quaternion::CreateFromTransform(offset); auto shapeConf = AZStd::make_shared(height, radius); Physics::CharacterColliderNodeConfiguration collider; collider.m_name = jointName; collider.m_shapes.emplace_back(colliderConf, shapeConf); return collider; } Physics::CharacterColliderNodeConfiguration CreateBoxCollider( const AZStd::string& jointName, const AZ::Vector3& dimensions, const AZ::Transform& offset) { auto colliderConf = AZStd::make_shared(); colliderConf->m_position = offset.GetTranslation(); colliderConf->m_rotation = AZ::Quaternion::CreateFromTransform(offset); auto shapeConf = AZStd::make_shared(dimensions); Physics::CharacterColliderNodeConfiguration collider; collider.m_name = jointName; collider.m_shapes.emplace_back(colliderConf, shapeConf); return collider; } EMotionFX::Mesh* CreateEMotionFXMesh( AZ::u32 nodeIndex, const AZStd::vector& vertices, const AZStd::vector& indices, const AZStd::vector& skinningInfo, const AZStd::vector& uvs, const AZStd::vector& clothData) { const AZ::u32 vertCount = aznumeric_cast(vertices.size()); const AZ::u32 faceCount = aznumeric_cast(indices.size()) / 3; auto meshBuilder = MCore::MemoryObjectUniquePtr{ EMotionFX::MeshBuilder::Create(nodeIndex, vertCount, false) }; // Skinning info if (!skinningInfo.empty() && skinningInfo.size() == vertices.size()) { EMotionFX::MeshBuilderSkinningInfo* skinningInfoBuilder = EMotionFX::MeshBuilderSkinningInfo::Create(vertCount); for (size_t vertex = 0; vertex < skinningInfo.size(); ++vertex) { for (const auto& influence : skinningInfo[vertex]) { skinningInfoBuilder->AddInfluence(static_cast(vertex), influence); } } skinningInfoBuilder->Optimize(); meshBuilder->SetSkinningInfo(skinningInfoBuilder); } // Original vertex numbers EMotionFX::MeshBuilderVertexAttributeLayerUInt32* orgVtxLayer = EMotionFX::MeshBuilderVertexAttributeLayerUInt32::Create( vertCount, EMotionFX::Mesh::ATTRIB_ORGVTXNUMBERS, false, false ); meshBuilder->AddLayer(orgVtxLayer); // The positions layer EMotionFX::MeshBuilderVertexAttributeLayerVector3* posLayer = EMotionFX::MeshBuilderVertexAttributeLayerVector3::Create( vertCount, EMotionFX::Mesh::ATTRIB_POSITIONS, false, true ); meshBuilder->AddLayer(posLayer); // The normals layer EMotionFX::MeshBuilderVertexAttributeLayerVector3* normalsLayer = EMotionFX::MeshBuilderVertexAttributeLayerVector3::Create( vertCount, EMotionFX::Mesh::ATTRIB_NORMALS, false, true ); meshBuilder->AddLayer(normalsLayer); // The UVs layer. EMotionFX::MeshBuilderVertexAttributeLayerVector2* uvsLayer = nullptr; if (!uvs.empty() && uvs.size() == vertices.size()) { uvsLayer = EMotionFX::MeshBuilderVertexAttributeLayerVector2::Create(vertCount, EMotionFX::Mesh::ATTRIB_UVCOORDS, false, false); meshBuilder->AddLayer(uvsLayer); } // The cloth layer. EMotionFX::MeshBuilderVertexAttributeLayerUInt32* clothLayer = nullptr; if (!clothData.empty() && clothData.size() == vertices.size()) { clothLayer = EMotionFX::MeshBuilderVertexAttributeLayerUInt32::Create(vertCount, EMotionFX::Mesh::ATTRIB_CLOTH_DATA, false, false); meshBuilder->AddLayer(clothLayer); } const int materialId = 0; AZStd::vector normals; AZStd::vector particles(vertices.size()); for (size_t i = 0; i < vertices.size(); ++i) { particles[i] = AZ::Vector4::CreateFromVector3(vertices[i]); } AZ::Interface::Get()->CalculateNormals( particles, indices, normals); for (AZ::u32 faceNum = 0; faceNum < faceCount; ++faceNum) { meshBuilder->BeginPolygon(materialId); for (AZ::u32 vertexOfFace = 0; vertexOfFace < 3; ++vertexOfFace) { AZ::u32 vertexNum = faceNum * 3 + vertexOfFace; AZ::u32 vertexIndex = indices[vertexNum]; orgVtxLayer->SetCurrentVertexValue(&vertexNum); posLayer->SetCurrentVertexValue(&vertices[vertexIndex]); normalsLayer->SetCurrentVertexValue(&normals[vertexIndex]); if (!uvs.empty()) { uvsLayer->SetCurrentVertexValue(&uvs[vertexIndex]); } if (!clothData.empty()) { const AZ::u32 clothVertexDataU32 = clothData[vertexIndex].ToU32(); clothLayer->SetCurrentVertexValue(&clothVertexDataU32); } meshBuilder->AddPolygonVertex(vertexNum); } meshBuilder->EndPolygon(); } return meshBuilder->ConvertToEMotionFXMesh(); } } // namespace UnitTest