/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #include #include #include #include #include #include #include namespace AZ { namespace SceneAPI { SceneSystem::SceneSystem() : m_unitSizeInMeters(1.0f), m_originalUnitSizeInMeters(1.0f), m_adjustTransform(nullptr), m_adjustTransformInverse(nullptr) { } void SceneSystem::Set(const SDKScene::SceneWrapperBase* scene) { static constexpr const char* s_ReadRootTransformKey = "/O3DE/Preferences/SceneAPI/AssImpReadRootTransform"; bool readRootTransform = false; if (AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get()) { settingsRegistry->Get(readRootTransform, s_ReadRootTransformKey); } // Get unit conversion factor to meter. if (!azrtti_istypeof(scene)) { return; } const AssImpSDKWrapper::AssImpSceneWrapper* assImpScene = azrtti_cast(scene); DataTypes::MatrixType rootTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(assImpScene->GetAssImpScene()->mRootNode->mTransformation); /* Check if metadata has information about "UnitScaleFactor" or "OriginalUnitScaleFactor". * This particular metadata is FBX format only. */ if (assImpScene->GetAssImpScene()->mMetaData->HasKey("UnitScaleFactor") || assImpScene->GetAssImpScene()->mMetaData->HasKey("OriginalUnitScaleFactor")) { // If either metadata piece is not available, the default of 1 will be used. assImpScene->GetAssImpScene()->mMetaData->Get("UnitScaleFactor", m_unitSizeInMeters); assImpScene->GetAssImpScene()->mMetaData->Get("OriginalUnitScaleFactor", m_originalUnitSizeInMeters); /* Conversion factor for converting from centimeters to meters. * This applies to an FBX format in which the default unit is a centimeter. */ m_unitSizeInMeters = m_unitSizeInMeters * .01f; } else { // Some file formats (like DAE) embed the scale in the root transformation, so extract that scale from here. m_unitSizeInMeters = rootTransform.ExtractScale().GetMaxElement(); rootTransform /= m_unitSizeInMeters; } if (readRootTransform) { AZ::Matrix3x4 adjustmatrix = AZ::Matrix3x4::CreateZero(); // for FBX, root transform is not converted where there are no meshes in the scene. if ((assImpScene->GetAssImpScene()->mFlags & AI_SCENE_FLAGS_INCOMPLETE) && assImpScene->GetAssImpScene()->mMetaData->HasKey("UpAxis")) { auto upAxisAndSign = assImpScene->GetUpVectorAndSign(); auto frontAxisAndSign = assImpScene->GetFrontVectorAndSign(); auto rightAxisAndSign = assImpScene->GetRightVectorAndSign(); // Set transform matrix with basis vector (-right, front, up) adjustmatrix.SetElement(0, (int)rightAxisAndSign.first, -(float)rightAxisAndSign.second); adjustmatrix.SetElement(1, (int)frontAxisAndSign.first, (float)frontAxisAndSign.second); adjustmatrix.SetElement(2, (int)upAxisAndSign.first, (float)upAxisAndSign.second); } else { // AssImp SDK internally uses a Y-up coordinate system, so we need to adjust the coordinate system to match the O3DE coordinate system (Z-up). adjustmatrix = AZ::Matrix3x4::CreateFromRows( AZ::Vector4(-1, 0, 0, 0), AZ::Vector4(0, 0, 1, 0), AZ::Vector4(0, 1, 0, 0) ); } m_adjustTransform.reset(new DataTypes::MatrixType(adjustmatrix * rootTransform)); m_adjustTransformInverse.reset(new DataTypes::MatrixType(m_adjustTransform->GetInverseFull())); return; } AZStd::pair upAxisAndSign = assImpScene->GetUpVectorAndSign(); if (upAxisAndSign.second <= 0) { AZ_TracePrintf(SceneAPI::Utilities::ErrorWindow, "Negative scene orientation is not a currently supported orientation."); return; } AZStd::pair frontAxisAndSign = assImpScene->GetFrontVectorAndSign(); if (upAxisAndSign.first != AssImpSDKWrapper::AssImpSceneWrapper::AxisVector::Z && upAxisAndSign.first != AssImpSDKWrapper::AssImpSceneWrapper::AxisVector::Unknown) { AZ::Matrix4x4 currentCoordMatrix = AZ::Matrix4x4::CreateIdentity(); //(UpVector = +Z, FrontVector = +Y, CoordSystem = -X(RightHanded)) AZ::Matrix4x4 targetCoordMatrix = AZ::Matrix4x4::CreateFromColumns( AZ::Vector4(-1, 0, 0, 0), AZ::Vector4(0, 0, 1, 0), AZ::Vector4(0, 1, 0, 0), AZ::Vector4(0, 0, 0, 1)); switch (upAxisAndSign.first) { case AssImpSDKWrapper::AssImpSceneWrapper::AxisVector::X: { if (frontAxisAndSign.second == 1) { currentCoordMatrix = AZ::Matrix4x4::CreateFromColumns( AZ::Vector4(0, -1, 0, 0), AZ::Vector4(1, 0, 0, 0), AZ::Vector4(0, 0, 1, 0), AZ::Vector4(0, 0, 0, 1)); } else { currentCoordMatrix = AZ::Matrix4x4::CreateFromColumns( AZ::Vector4(0, 1, 0, 0), AZ::Vector4(1, 0, 0, 0), AZ::Vector4(0, 0, -1, 0), AZ::Vector4(0, 0, 0, 1)); } } break; case AssImpSDKWrapper::AssImpSceneWrapper::AxisVector::Y: { if (frontAxisAndSign.second == 1) { currentCoordMatrix = AZ::Matrix4x4::CreateFromColumns( AZ::Vector4(1, 0, 0, 0), AZ::Vector4(0, 1, 0, 0), AZ::Vector4(0, 0, 1, 0), AZ::Vector4(0, 0, 0, 1)); } else { currentCoordMatrix = AZ::Matrix4x4::CreateFromColumns( AZ::Vector4(-1, 0, 0, 0), AZ::Vector4(0, 1, 0, 0), AZ::Vector4(0, 0, -1, 0), AZ::Vector4(0, 0, 0, 1)); } } break; } AZ::Matrix4x4 adjustmatrix = targetCoordMatrix * currentCoordMatrix.GetInverseTransform(); m_adjustTransform.reset(new DataTypes::MatrixType(AssImpSDKWrapper::AssImpTypeConverter::ToTransform(adjustmatrix))); m_adjustTransformInverse.reset(new DataTypes::MatrixType(m_adjustTransform->GetInverseFull())); } } void SceneSystem::SwapVec3ForUpAxis(Vector3& swapVector) const { if (m_adjustTransform) { swapVector = *m_adjustTransform * swapVector; } } void SceneSystem::SwapTransformForUpAxis(DataTypes::MatrixType& inOutTransform) const { if (m_adjustTransform) { inOutTransform = (*m_adjustTransform * inOutTransform) * *m_adjustTransformInverse; } } void SceneSystem::ConvertUnit(Vector3& scaleVector) const { scaleVector *= m_unitSizeInMeters; } void SceneSystem::ConvertUnit(DataTypes::MatrixType& inOutTransform) const { Vector3 translation = inOutTransform.GetTranslation(); translation *= m_unitSizeInMeters; inOutTransform.SetTranslation(translation); } void SceneSystem::ConvertBoneUnit(DataTypes::MatrixType& inOutTransform) const { // Need to scale translation explicitly as MultiplyByScale won't change the translation component // and we need to convert to meter unit Vector3 translation = inOutTransform.GetTranslation(); translation *= m_unitSizeInMeters; inOutTransform.SetTranslation(translation); } } }