Files
UnrealEngine/Engine/Plugins/Experimental/ChaosRigidAsset/Source/ChaosRigidAssetEditor/Private/DataflowRendering.cpp
Brandyn / Techy fcc1b09210 init
2026-04-04 15:40:51 -05:00

336 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DataflowRendering.h"
#include "PhysicsEngine/AggregateGeom.h"
#include "PhysicsEngine/BoxElem.h"
#include "GeometryCollection/Facades/CollectionRenderingFacade.h"
#include "Engine/SkeletalMesh.h"
#include "ReferenceSkeleton.h"
#include "PhysicsEngine/SkeletalBodySetup.h"
#include "BoneSelection.h"
#include "PhysicsAssetDataflowState.h"
namespace UE::Chaos::RigidAsset
{
void RenderAggGeom(GeometryCollection::Facades::FRenderingFacade& RenderingFacade, const UE::Dataflow::FGraphRenderingState& State, const FKAggregateGeom& AggGeom, const FTransform& BaseTm, FName BoneName, const FLinearColor& InColor)
{
const int32 NumElems = AggGeom.GetElementCount();
if(NumElems == 0)
{
// Nothing to actually draw - return before opening a geometry group
return;
}
for(int32 Index = 0; Index < NumElems; ++Index)
{
FString GeometryName = FString::Printf(TEXT("%s_%s_%d"), *State.GetGuid().ToString(), *BoneName.ToString(), Index);
const int32 GeometryGroup = RenderingFacade.StartGeometryGroup(GeometryName);
const FKShapeElem* Elem = AggGeom.GetElement(Index);
if(!Elem)
{
break;
}
switch(Elem->GetShapeType())
{
case EAggCollisionShape::Box:
{
const FKBoxElem* AsBox = static_cast<const FKBoxElem*>(Elem);
const FVector3f Extent = FVector3f{ AsBox->X, AsBox->Y, AsBox->Z } *0.5f;
RenderingFacade.SetGroupTransform(GeometryGroup, Elem->GetTransform() * BaseTm);
RenderingFacade.AddBox({ -Extent, Extent });
break;
}
case EAggCollisionShape::Sphere:
{
const FKSphereElem* AsSphere = static_cast<const FKSphereElem*>(Elem);
// Transform placed in the group - always draw at (0,0,0)
RenderingFacade.SetGroupTransform(GeometryGroup, Elem->GetTransform() * BaseTm);
RenderingFacade.AddSphere({0.0f, 0.0f, 0.0f}, AsSphere->Radius, InColor);
break;
}
case EAggCollisionShape::Sphyl:
{
const FKSphylElem* AsSphyl = static_cast<const FKSphylElem*>(Elem);
// AddCapsule uses the capsule mesh generator which has the origin of the capsule at one end of
// the segment, we need to transform first to the centre of the capsule, then apply the elem transform
const FTransform CapsuleCollisionAdjust(FQuat::Identity, FVector(0.0f, 0.0f, -AsSphyl->Length / 2));
RenderingFacade.SetGroupTransform(GeometryGroup, CapsuleCollisionAdjust * Elem->GetTransform() * BaseTm);
RenderingFacade.AddCapsule(AsSphyl->Length, AsSphyl->Radius);
break;
}
case EAggCollisionShape::Convex:
{
const FKConvexElem* AsConvex = static_cast<const FKConvexElem*>(Elem);
const int32 NumIndices = AsConvex->IndexData.Num();
if(NumIndices == 0 || NumIndices % 3 != 0)
{
// Empty or invalid convex index data.
break;
}
RenderingFacade.SetGroupTransform(GeometryGroup, Elem->GetTransform() * BaseTm);
const int32 NumFaces = NumIndices / 3;
TArray<FVector3f> Positions;
Positions.Reserve(AsConvex->VertexData.Num());
for(const FVector& Position : AsConvex->VertexData)
{
Positions.Add(static_cast<FVector3f>(Position));
}
TArray<FIntVector> Indices;
Indices.Reserve(NumFaces);
for(int32 FaceIndex = 0; FaceIndex < AsConvex->IndexData.Num(); FaceIndex += 3)
{
Indices.Add({
AsConvex->IndexData[FaceIndex + 0],
AsConvex->IndexData[FaceIndex + 2],
AsConvex->IndexData[FaceIndex + 1]
});
}
TArray<FLinearColor> Colors;
Colors.SetNum(AsConvex->VertexData.Num());
for(FLinearColor& Color : Colors)
{
Color = FLinearColor::White;
}
RenderingFacade.AddFaces(Positions, Indices, Colors);
break;
}
default:
break;
}
RenderingFacade.EndGeometryGroup(GeometryGroup);
}
}
UE::Dataflow::FRenderKey FAggregateGeometryGeomRenderCallbacks::StaticGetRenderKey()
{
return { "GeomRender", FName("FKAggregateGeom") };
}
UE::Dataflow::FRenderKey FAggregateGeometryGeomRenderCallbacks::GetRenderKey() const
{
return StaticGetRenderKey();
}
bool FAggregateGeometryGeomRenderCallbacks::CanRender(const UE::Dataflow::IDataflowConstructionViewMode& ViewMode) const
{
return true;
}
void FAggregateGeometryGeomRenderCallbacks::Render(GeometryCollection::Facades::FRenderingFacade& RenderingFacade, const UE::Dataflow::FGraphRenderingState& State)
{
if(State.GetRenderOutputs().Num() > 0)
{
FKAggregateGeom Default;
const FKAggregateGeom& AggGeom = State.GetValue<FKAggregateGeom>(State.GetRenderOutputs()[0], Default);
RenderAggGeom(RenderingFacade, State, AggGeom, FTransform::Identity, NAME_None, FLinearColor::White);
}
}
UE::Dataflow::FRenderKey FBoneSelectionRenderCallbacks::StaticGetRenderKey()
{
return { "GeomRender", FName("FBoneSelection") };
}
UE::Dataflow::FRenderKey FBoneSelectionRenderCallbacks::GetRenderKey() const
{
return StaticGetRenderKey();
}
bool FBoneSelectionRenderCallbacks::CanRender(const UE::Dataflow::IDataflowConstructionViewMode& ViewMode) const
{
return true;
}
// GeometryCore perp vectors impl, not currently exposed in that module
template <typename RealType>
void MakePerpVectors(const UE::Math::TVector<RealType>& Normal, UE::Math::TVector<RealType>& OutPerp1, UE::Math::TVector<RealType>& OutPerp2)
{
// Duff et al method, from https://graphics.pixar.com/library/OrthonormalB/paper.pdf
if(Normal.Z < (RealType)0)
{
RealType A = (RealType)1 / ((RealType)1 - Normal.Z);
RealType B = Normal.X * Normal.Y * A;
OutPerp1.X = (RealType)1 - Normal.X * Normal.X * A;
OutPerp1.Y = -B;
OutPerp1.Z = Normal.X;
OutPerp2.X = B;
OutPerp2.Y = Normal.Y * Normal.Y * A - (RealType)1;
OutPerp2.Z = -Normal.Y;
}
else
{
RealType A = (RealType)1 / ((RealType)1 + Normal.Z);
RealType B = -Normal.X * Normal.Y * A;
OutPerp1.X = (RealType)1 - Normal.X * Normal.X * A;
OutPerp1.Y = B;
OutPerp1.Z = -Normal.X;
OutPerp2.X = B;
OutPerp2.Y = (RealType)1 - Normal.Y * Normal.Y * A;
OutPerp2.Z = -Normal.Y;
}
}
void DrawBone(GeometryCollection::Facades::FRenderingFacade& RenderingFacade, const FVector3f& From, const FVector3f& To, const FLinearColor& InColor)
{
float Length;
FVector3f Axes[3];
const FVector3f Segment = To - From;
Segment.ToDirectionAndLength(Axes[2], Length);
MakePerpVectors(Axes[2], Axes[0], Axes[1]);
constexpr float WidePoint = 0.3f;
constexpr float Thickness = 2.5f;
TArray<FVector3f> Verts;
TArray<FLinearColor> Colors;
Verts.SetNumUninitialized(6);
Colors.SetNumUninitialized(6);
Verts[0] = From;
Verts[5] = To;
Verts[1] = From + (Axes[0] + Axes[1]) * Thickness + Axes[2] * (WidePoint * Length);
Verts[2] = From + (Axes[0] + -Axes[1]) * Thickness + Axes[2] * (WidePoint * Length);
Verts[3] = From + (-Axes[0] + -Axes[1]) * Thickness + Axes[2] * (WidePoint * Length);
Verts[4] = From + (-Axes[0] + Axes[1]) * Thickness + Axes[2] * (WidePoint * Length);
TArray<FIntVector> Indices;
Indices.SetNumUninitialized(8);
// Top Section
Indices[0] = FIntVector{ 0, 2, 1 };
Indices[1] = FIntVector{ 0, 3, 2 };
Indices[2] = FIntVector{ 0, 4, 3 };
Indices[3] = FIntVector{ 0, 1, 4 };
// Bottom Section
Indices[4] = FIntVector{ 5, 1, 2 };
Indices[5] = FIntVector{ 5, 2, 3 };
Indices[6] = FIntVector{ 5, 3, 4 };
Indices[7] = FIntVector{ 5, 4, 1 };
for(FLinearColor& Color : Colors)
{
Color = InColor;
}
RenderingFacade.AddFaces(Verts, Indices, Colors);
}
void FBoneSelectionRenderCallbacks::Render(GeometryCollection::Facades::FRenderingFacade& RenderingFacade, const UE::Dataflow::FGraphRenderingState& State)
{
if(State.GetRenderOutputs().Num() > 0)
{
FRigidAssetBoneSelection Default;
const FRigidAssetBoneSelection& Selection = State.GetValue<FRigidAssetBoneSelection>(State.GetRenderOutputs()[0], Default);
if(!Selection.Mesh || !Selection.Skeleton)
{
return;
}
const FReferenceSkeleton& RefSkel = Selection.Mesh->GetRefSkeleton();
TArray<FTransform> BoneTransforms;
TArray<int32> ParentIndices;
TArray<bool> SelectedIndices;
RefSkel.GetBoneAbsoluteTransforms(BoneTransforms);
const int32 NumBones = BoneTransforms.Num();
SelectedIndices.SetNumZeroed(NumBones);
for(const FRigidAssetBoneInfo& Selected : Selection.SelectedBones)
{
SelectedIndices[Selected.Index] = true;
}
ParentIndices.SetNum(NumBones);
for(int32 BoneIndex = 0; BoneIndex < NumBones; ++BoneIndex)
{
ParentIndices[BoneIndex] = RefSkel.GetParentIndex(BoneIndex);
}
int32 Group = RenderingFacade.StartGeometryGroup(TEXT("BoneSelection"));
for(int32 BoneIndex = 0; BoneIndex < NumBones; ++BoneIndex)
{
const FTransform& BoneTransform = BoneTransforms[BoneIndex];
RenderingFacade.AddPoint((FVector3f)BoneTransform.GetTranslation());
const int32 ParentIndex = ParentIndices[BoneIndex];
if(ParentIndex != INDEX_NONE)
{
const FLinearColor BoneColor = SelectedIndices[ParentIndex] ? FLinearColor(1.0f, 0.6f, 0.0f) : FLinearColor::Gray;
DrawBone(RenderingFacade, (FVector3f)BoneTransforms[ParentIndex].GetTranslation(), (FVector3f)BoneTransforms[BoneIndex].GetTranslation(), BoneColor);
}
}
RenderingFacade.EndGeometryGroup(Group);
}
}
UE::Dataflow::FRenderKey FPhysAssetStateRenderCallbacks::StaticGetRenderKey()
{
return { "GeomRender", FName("FPhysicsAssetDataflowState") };
}
UE::Dataflow::FRenderKey FPhysAssetStateRenderCallbacks::GetRenderKey() const
{
return StaticGetRenderKey();
}
bool FPhysAssetStateRenderCallbacks::CanRender(const UE::Dataflow::IDataflowConstructionViewMode& ViewMode) const
{
return true;
}
void FPhysAssetStateRenderCallbacks::Render(GeometryCollection::Facades::FRenderingFacade& RenderingFacade, const UE::Dataflow::FGraphRenderingState& State)
{
if(State.GetRenderOutputs().Num() > 0)
{
FPhysicsAssetDataflowState Default;
const FPhysicsAssetDataflowState& AssetState = State.GetValue<FPhysicsAssetDataflowState>(State.GetRenderOutputs()[0], Default);
if(AssetState.HasData())
{
const FReferenceSkeleton& RefSkeleton = AssetState.TargetMesh->GetRefSkeleton();
for(TObjectPtr<USkeletalBodySetup> Body : AssetState.Bodies)
{
const int32 BoneIndex = RefSkeleton.FindBoneIndex(Body->BoneName);
if(BoneIndex != INDEX_NONE)
{
FTransform BoneTransform = RefSkeleton.GetBoneAbsoluteTransform(BoneIndex);
RenderAggGeom(RenderingFacade, State, Body->AggGeom, BoneTransform, Body->BoneName, FLinearColor(1.0f, 1.0f, 1.0f, 0.5f));
}
}
}
}
}
}