Files
UnrealEngine/Engine/Plugins/Interchange/Runtime/Source/Parsers/Fbx/Private/FbxMesh.h
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

232 lines
8.5 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "FbxAPI.h"
#include "FbxHelper.h"
#include "FbxInclude.h"
/** Forward declarations */
struct FMeshDescription;
class UInterchangeBaseNodeContainer;
class UInterchangeMeshNode;
namespace UE
{
namespace Interchange
{
#if WITH_ENGINE
struct FMeshPayloadData;
#endif
namespace Private
{
//Helper to generate and cache per joint MeshIdToGlobalBindPose transforms.
struct FFbxJointMeshBindPoseGenerator
{
TMap<const FbxNode*, TMap<FString, FMatrix>> JointToMeshIdToGlobalBindPoseReferenceMap;
TMap<const FbxNode*, TMap<FString, FMatrix>> JointToMeshIdToGlobalBindPoseJointMap;
TMap<const FbxNode*, FbxAMatrix> JointToBindPoseMap;
FFbxJointMeshBindPoseGenerator(FbxScene* SDKScene, FFbxParser& Parser);
TMap<FString, FMatrix>& GetMeshIdToGlobalBindPoseReferenceMap(const FbxNode* Joint);
TMap<FString, FMatrix>& GetMeshIdToGlobalBindPoseJointMap(const FbxNode* Joint);
FbxAMatrix GetBindPoseFbxAMatrix(const FbxNode* Joint, FbxAMatrix& DefaultValue);
};
struct FMorphTargetAnimationBuildingData
{
double StartTime;
double StopTime;
UInterchangeMeshNode* InterchangeMeshNode;
int32 GeometryIndex;
int32 AnimationIndex;
FbxAnimLayer* AnimLayer;
int32 MorphTargetIndex;
int32 ChannelIndex;
FString MorphTargetNodeUid;
TArray<FString> InbetweenCurveNames;
TArray<float> InbetweenFullWeights;
FMorphTargetAnimationBuildingData(double InStartTime
, double InStopTime
, UInterchangeMeshNode* InInterchangeMeshNode
, int32 InGeometryIndex
, int32 InAnimationIndex
, FbxAnimLayer* InAnimLayer
, int32 InMorphTargetIndex
, int32 InChannelIndex
, FString InMorphTargetNodeUid)
: StartTime(InStartTime)
, StopTime(InStopTime)
, InterchangeMeshNode(InInterchangeMeshNode)
, GeometryIndex(InGeometryIndex)
, AnimationIndex(InAnimationIndex)
, AnimLayer(InAnimLayer)
, MorphTargetIndex(InMorphTargetIndex)
, ChannelIndex(InChannelIndex)
, MorphTargetNodeUid(InMorphTargetNodeUid)
{
}
FMorphTargetAnimationBuildingData(double InStartTime
, double InStopTime
, UInterchangeMeshNode* InInterchangeMeshNode
, int32 InGeometryIndex
, int32 InAnimationIndex
, FbxAnimLayer* InAnimLayer
, int32 InMorphTargetIndex
, int32 InChannelIndex
, FString InMorphTargetNodeUid
, TArray<FString> InInbetweenCurveNames
, TArray<float> InInbetweenFullWeights)
: StartTime(InStartTime)
, StopTime(InStopTime)
, InterchangeMeshNode(InInterchangeMeshNode)
, GeometryIndex(InGeometryIndex)
, AnimationIndex(InAnimationIndex)
, AnimLayer(InAnimLayer)
, MorphTargetIndex(InMorphTargetIndex)
, ChannelIndex(InChannelIndex)
, MorphTargetNodeUid(InMorphTargetNodeUid)
, InbetweenCurveNames(InInbetweenCurveNames)
, InbetweenFullWeights(InInbetweenFullWeights)
{
}
};
class FFbxMesh;
class FMeshDescriptionImporter
{
public:
FMeshDescriptionImporter(FFbxParser& InParser, FMeshDescription* InMeshDescription, FbxScene* InSDKScene, FbxGeometryConverter* InSDKGeometryConverter);
/*
* Fill the mesh description using the Mesh parameter.
*/
bool FillStaticMeshDescriptionFromFbxMesh(FbxMesh* Mesh, const FTransform& MeshGlobalTransform);
/*
* Fill the mesh description using the Mesh parameter, and also fill the OutJointNodeUniqueIDs so the MeshDescription bone Index can be mapped to the correct Interchange joint scene node.
*/
bool FillSkinnedMeshDescriptionFromFbxMesh(FbxMesh* Mesh, const FTransform& MeshGlobalTransform, TArray<FString>& OutJointUniqueNames);
/*
* Fill the mesh description using the Shape parameter.
*/
bool FillMeshDescriptionFromFbxShape(FbxShape* Shape, const FTransform& MeshGlobalTransform);
/**
* Add messages to the message log.
*/
template <typename T>
T* AddMessage(FbxGeometryBase* FbxNode) const
{
T* Item = Parser.AddMessage<T>();
Item->MeshName = Parser.GetFbxHelper()->GetMeshName(FbxNode);
Item->InterchangeKey = Parser.GetFbxHelper()->GetMeshUniqueID(FbxNode);
return Item;
}
private:
enum class EMeshType : uint8
{
None = 0, //No mesh type to import
Static = 1, //static mesh
Skinned = 2, //skinned mesh with joints
};
bool FillMeshDescriptionFromFbxMesh(FbxMesh* Mesh, const FTransform& MeshGlobalTransform, TArray<FString>& OutJointUniqueNames, EMeshType MeshType);
bool IsOddNegativeScale(FbxAMatrix& TotalMatrix);
//TODO move the real function from RenderCore to FVector, so we do not have to add render core to compute such a simple thing
float FbxGetBasisDeterminantSign(const FVector3f& XAxis, const FVector3f& YAxis, const FVector3f& ZAxis)
{
FMatrix44f Basis(
FPlane4f(XAxis, 0),
FPlane4f(YAxis, 0),
FPlane4f(ZAxis, 0),
FPlane4f(0, 0, 0, 1)
);
return (Basis.Determinant() < 0) ? -1.0f : +1.0f;
}
FFbxParser& Parser;
FMeshDescription* MeshDescription;
FbxScene* SDKScene;
FbxGeometryConverter* SDKGeometryConverter;
bool bInitialized = false;
};
class FMeshPayloadContext : public FPayloadContextBase
{
public:
virtual ~FMeshPayloadContext() {}
virtual FString GetPayloadType() const override { return TEXT("Mesh-PayloadContext"); }
virtual bool FetchMeshPayloadToFile(FFbxParser& Parser, const FTransform& MeshGlobalTransform, const FString& PayloadFilepath) override;
#if WITH_ENGINE
virtual bool FetchMeshPayload(FFbxParser& Parser, const FTransform& MeshGlobalTransform, FMeshPayloadData& OutMeshPayloadData) override;
#endif
bool bIsSkinnedMesh = false;
FbxMesh* Mesh = nullptr;
FbxScene* SDKScene = nullptr;
FbxGeometryConverter* SDKGeometryConverter = nullptr;
private:
bool FetchMeshPayloadInternal(FFbxParser& Parser
, const FTransform& MeshGlobalTransform
, FMeshDescription& OutMeshDescription
, TArray<FString>& OutJointNames);
};
class FMorphTargetPayloadContext : public FPayloadContextBase
{
public:
virtual ~FMorphTargetPayloadContext() {}
virtual FString GetPayloadType() const override { return TEXT("MorphTarget-PayloadContext"); }
virtual bool FetchMeshPayloadToFile(FFbxParser& Parser, const FTransform& MeshGlobalTransform, const FString& PayloadFilepath) override;
#if WITH_ENGINE
virtual bool FetchMeshPayload(FFbxParser& Parser, const FTransform& MeshGlobalTransform, FMeshPayloadData& OutMeshPayloadData) override;
#endif
FbxShape* Shape = nullptr;
FbxScene* SDKScene = nullptr;
FbxGeometryConverter* SDKGeometryConverter = nullptr;
private:
bool FetchMeshPayloadInternal(FFbxParser& Parser
, const FTransform& MeshGlobalTransform
, FMeshDescription& OutMeshDescription);
};
class FFbxMesh
{
public:
explicit FFbxMesh(FFbxParser& InParser)
: Parser(InParser)
{}
void AddAllMeshes(FbxScene* SDKScene, FbxGeometryConverter* SDKGeometryConverter, UInterchangeBaseNodeContainer& NodeContainer, TMap<FString, TSharedPtr<FPayloadContextBase>>& PayloadContexts);
static bool GetGlobalJointBindPoseTransform(FFbxParser* Parser, FbxScene* SDKScene, FbxNode* Joint, FFbxJointMeshBindPoseGenerator& FbxJointMeshBindPoseGenerator,
FbxAMatrix& GlobalBindPoseJointMatrix, TMap<FString, FMatrix>& MeshIdToGlobalBindPoseJointMap, TMap<FString, FMatrix>& MeshIdToGlobalBindPoseReferenceMap,
bool& bBadBindPoseMessageDisplay, const bool bIsOriginalJoint, const FbxAMatrix& JointOrientationMatrix);
TArray<FMorphTargetAnimationBuildingData>& GetMorphTargetAnimationsBuildingData() { return MorphTargetAnimationsBuildingData; }
protected:
/** Add joints to the Interchange mesh node joint dependencies. Return false if there is no valid joint (not a valid skinned mesh). */
bool ExtractSkinnedMeshNodeJoints(FbxScene* SDKScene, UInterchangeBaseNodeContainer& NodeContainer, FbxMesh* Mesh, UInterchangeMeshNode* MeshNode);
UInterchangeMeshNode* CreateMeshNode(UInterchangeBaseNodeContainer& NodeContainer, const FString& NodeName, const FString& NodeUniqueID);
private:
FFbxParser& Parser;
//In order to appropriately identify the Skeleton Node Uids we have to process the MorphTarget animations once the hierarchy is processed
TArray<FMorphTargetAnimationBuildingData> MorphTargetAnimationsBuildingData;
};
}//ns Private
}//ns Interchange
}//ns UE