329 lines
14 KiB
C++
329 lines
14 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "StaticMesh.h"
|
|
#include "Importer.h"
|
|
|
|
namespace Lightmass
|
|
{
|
|
|
|
/**
|
|
* Functions used for transforming a static mesh component based on a spline.
|
|
* This needs to be updated if the spline functionality changes!
|
|
*/
|
|
|
|
static float SmoothStep(float A, float B, float X)
|
|
{
|
|
if (X < A)
|
|
{
|
|
return 0.0f;
|
|
}
|
|
else if (X >= B)
|
|
{
|
|
return 1.0f;
|
|
}
|
|
const float InterpFraction = (X - A) / (B - A);
|
|
return InterpFraction * InterpFraction * (3.0f - 2.0f * InterpFraction);
|
|
}
|
|
|
|
static FVector3f SplineEvalPos(const FVector3f& StartPos, const FVector3f& StartTangent, const FVector3f& EndPos, const FVector3f& EndTangent, float A)
|
|
{
|
|
const float A2 = A * A;
|
|
const float A3 = A2 * A;
|
|
|
|
return (((2*A3)-(3*A2)+1) * StartPos) + ((A3-(2*A2)+A) * StartTangent) + ((A3-A2) * EndTangent) + (((-2*A3)+(3*A2)) * EndPos);
|
|
}
|
|
|
|
static FVector3f SplineEvalDir(const FVector3f& StartPos, const FVector3f& StartTangent, const FVector3f& EndPos, const FVector3f& EndTangent, float A)
|
|
{
|
|
const FVector3f C = (6*StartPos) + (3*StartTangent) + (3*EndTangent) - (6*EndPos);
|
|
const FVector3f D = (-6*StartPos) - (4*StartTangent) - (2*EndTangent) + (6*EndPos);
|
|
const FVector3f E = StartTangent;
|
|
|
|
const float A2 = A * A;
|
|
|
|
return ((C * A2) + (D * A) + E).GetSafeNormal();
|
|
}
|
|
|
|
/** Calculate full transform that defines frame along spline, given the Z of a vertex. */
|
|
/** Note: This is mirrored from USplineMeshComponent::CalcSliceTransform() and LocalVertexShader.usf. If you update one of these, please update them all! */
|
|
static FMatrix44f CalcSliceTransform(float ZPos, const FSplineMeshParams& SplineParams)
|
|
{
|
|
// Find how far 'along' mesh we are
|
|
const float Alpha = (ZPos - SplineParams.MeshMinZ) / SplineParams.MeshRangeZ;
|
|
|
|
// Apply hermite interp to Alpha if desired
|
|
const float HermiteAlpha = SplineParams.bSmoothInterpRollScale ? SmoothStep(0.0, 1.0, Alpha) : Alpha;
|
|
|
|
// Then find the point and direction of the spline at this point along
|
|
FVector3f SplinePos = SplineEvalPos( SplineParams.StartPos, SplineParams.StartTangent, SplineParams.EndPos, SplineParams.EndTangent, Alpha );
|
|
const FVector3f SplineDir = SplineEvalDir( SplineParams.StartPos, SplineParams.StartTangent, SplineParams.EndPos, SplineParams.EndTangent, Alpha );
|
|
|
|
// Find base frenet frame
|
|
const FVector3f BaseXVec = (SplineParams.SplineUpDir ^ SplineDir).GetSafeNormal();
|
|
const FVector3f BaseYVec = (SplineDir ^ BaseXVec).GetSafeNormal();
|
|
|
|
// Offset the spline by the desired amount
|
|
const FVector2f SliceOffset = FMath::Lerp<FVector2f>(SplineParams.StartOffset, SplineParams.EndOffset, HermiteAlpha);
|
|
SplinePos += SliceOffset.X * BaseXVec;
|
|
SplinePos += SliceOffset.Y * BaseYVec;
|
|
|
|
// Apply roll to frame around spline
|
|
const float UseRoll = FMath::Lerp(SplineParams.StartRoll, SplineParams.EndRoll, HermiteAlpha);
|
|
const float CosAng = FMath::Cos(UseRoll);
|
|
const float SinAng = FMath::Sin(UseRoll);
|
|
const FVector3f XVec = (CosAng * BaseXVec) - (SinAng * BaseYVec);
|
|
const FVector3f YVec = (CosAng * BaseYVec) + (SinAng * BaseXVec);
|
|
|
|
// Find scale at this point along spline
|
|
const FVector2f UseScale = FMath::Lerp(SplineParams.StartScale, SplineParams.EndScale, HermiteAlpha);
|
|
|
|
// Build overall transform
|
|
FMatrix44f SliceTransform;
|
|
switch (SplineParams.ForwardAxis)
|
|
{
|
|
case ESplineMeshAxis::X:
|
|
SliceTransform = FMatrix44f(SplineDir, UseScale.X * XVec, UseScale.Y * YVec, SplinePos);
|
|
break;
|
|
case ESplineMeshAxis::Y:
|
|
SliceTransform = FMatrix44f(UseScale.Y * YVec, SplineDir, UseScale.X * XVec, SplinePos);
|
|
break;
|
|
case ESplineMeshAxis::Z:
|
|
SliceTransform = FMatrix44f(UseScale.X * XVec, UseScale.Y * YVec, SplineDir, SplinePos);
|
|
break;
|
|
default:
|
|
check(0);
|
|
break;
|
|
}
|
|
|
|
return SliceTransform;
|
|
}
|
|
|
|
/** Calculate rotation matrix that defines frame along spline, given the Z of a vertex. */
|
|
static FMatrix44f CalcSliceRot(float ZPos, const FSplineMeshParams& SplineParams)
|
|
{
|
|
// Find how far 'along' mesh we are
|
|
const float Alpha = (ZPos - SplineParams.MeshMinZ) / SplineParams.MeshRangeZ;
|
|
|
|
// Apply hermite interp to Alpha if desired
|
|
const float HermiteAlpha = SplineParams.bSmoothInterpRollScale ? SmoothStep(0.0, 1.0, Alpha) : Alpha;
|
|
|
|
// Then find the point and direction of the spline at this point along
|
|
const FVector3f SplineDir = SplineEvalDir( SplineParams.StartPos, SplineParams.StartTangent, SplineParams.EndPos, SplineParams.EndTangent, Alpha );
|
|
|
|
// Find base frenet frame
|
|
const FVector3f BaseXVec = (SplineParams.SplineUpDir ^ SplineDir).GetSafeNormal();
|
|
const FVector3f BaseYVec = (SplineDir ^ BaseXVec).GetSafeNormal();
|
|
|
|
// Apply roll to frame around spline
|
|
const float UseRoll = FMath::Lerp(SplineParams.StartRoll, SplineParams.EndRoll, HermiteAlpha);
|
|
const float CosAng = FMath::Cos(UseRoll);
|
|
const float SinAng = FMath::Sin(UseRoll);
|
|
const FVector3f XVec = (CosAng * BaseXVec) - (SinAng * BaseYVec);
|
|
const FVector3f YVec = (CosAng * BaseYVec) + (SinAng * BaseXVec);
|
|
|
|
// Build rotation transform
|
|
FMatrix44f SliceTransform;
|
|
switch (SplineParams.ForwardAxis)
|
|
{
|
|
case ESplineMeshAxis::X:
|
|
SliceTransform = FMatrix44f(SplineDir, XVec, YVec, FVector3f(0,0,0));
|
|
break;
|
|
case ESplineMeshAxis::Y:
|
|
SliceTransform = FMatrix44f(YVec, SplineDir, XVec, FVector3f(0,0,0));
|
|
break;
|
|
case ESplineMeshAxis::Z:
|
|
SliceTransform = FMatrix44f(XVec, YVec, SplineDir, FVector3f(0,0,0));
|
|
break;
|
|
default:
|
|
check(0);
|
|
break;
|
|
}
|
|
|
|
return SliceTransform;
|
|
}
|
|
|
|
/**
|
|
* Creates a static lighting vertex to represent the given static mesh vertex.
|
|
* @param VertexBuffer - The static mesh's vertex buffer.
|
|
* @param VertexIndex - The index of the static mesh vertex to access.
|
|
* @param OutVertex - Upon return, contains a static lighting vertex representing the specified static mesh vertex.
|
|
*/
|
|
static void GetStaticLightingVertex(
|
|
const FStaticMeshVertex& InVertex,
|
|
const FMatrix44f& LocalToWorld,
|
|
const FMatrix44f& LocalToWorldInverseTranspose,
|
|
bool bIsSplineMesh,
|
|
const FSplineMeshParams& SplineParams,
|
|
FStaticLightingVertex& OutVertex
|
|
)
|
|
{
|
|
if (bIsSplineMesh)
|
|
{
|
|
// Make transform for this point along spline
|
|
const FMatrix44f SliceTransform = CalcSliceTransform(InVertex.Position[SplineParams.ForwardAxis], SplineParams);
|
|
|
|
// Remove Z (transform will move us along spline)
|
|
FVector4f SlicePos = InVertex.Position;
|
|
SlicePos[SplineParams.ForwardAxis] = 0;
|
|
|
|
// Transform into mesh space
|
|
const FVector4f LocalPos = SliceTransform.TransformPosition(SlicePos);
|
|
|
|
// Transform from mesh to world space
|
|
OutVertex.WorldPosition = LocalToWorld.TransformPosition(LocalPos);
|
|
|
|
const FMatrix44f SliceRot = CalcSliceRot(InVertex.Position[SplineParams.ForwardAxis], SplineParams);
|
|
|
|
const FVector4f LocalSpaceTangentX = SliceRot.TransformVector(InVertex.TangentX);
|
|
const FVector4f LocalSpaceTangentY = SliceRot.TransformVector(InVertex.TangentY);
|
|
const FVector4f LocalSpaceTangentZ = SliceRot.TransformVector(InVertex.TangentZ);
|
|
|
|
OutVertex.WorldTangentX = LocalToWorld.TransformVector(LocalSpaceTangentX).GetSafeNormal();
|
|
OutVertex.WorldTangentY = LocalToWorld.TransformVector(LocalSpaceTangentY).GetSafeNormal();
|
|
OutVertex.WorldTangentZ = LocalToWorldInverseTranspose.TransformVector(LocalSpaceTangentZ).GetSafeNormal();
|
|
}
|
|
else
|
|
{
|
|
OutVertex.WorldPosition = LocalToWorld.TransformPosition(InVertex.Position);
|
|
OutVertex.WorldTangentX = LocalToWorld.TransformVector(InVertex.TangentX).GetSafeNormal();
|
|
OutVertex.WorldTangentY = LocalToWorld.TransformVector(InVertex.TangentY).GetSafeNormal();
|
|
OutVertex.WorldTangentZ = LocalToWorldInverseTranspose.TransformVector(InVertex.TangentZ).GetSafeNormal();
|
|
}
|
|
|
|
// WorldTangentZ can end up a 0 vector if it was small to begin with and LocalToWorld contains large scale factors.
|
|
if (!OutVertex.WorldTangentZ.IsUnit3())
|
|
{
|
|
OutVertex.WorldTangentZ = (OutVertex.WorldTangentX ^ OutVertex.WorldTangentY).GetSafeNormal();
|
|
}
|
|
|
|
for(uint32 LightmapTextureCoordinateIndex = 0; LightmapTextureCoordinateIndex < UE_ARRAY_COUNT(InVertex.UVs); LightmapTextureCoordinateIndex++)
|
|
{
|
|
OutVertex.TextureCoordinates[LightmapTextureCoordinateIndex] = InVertex.UVs[LightmapTextureCoordinateIndex];
|
|
}
|
|
}
|
|
|
|
// FStaticLightingMesh interface.
|
|
|
|
void FStaticMeshStaticLightingMesh::GetTriangle(int32 TriangleIndex,FStaticLightingVertex& OutV0,FStaticLightingVertex& OutV1,FStaticLightingVertex& OutV2,int32& ElementIndex) const
|
|
{
|
|
const FStaticMeshLOD& LODRenderData = StaticMesh->GetLOD(GetMeshLODIndex());
|
|
|
|
// Lookup the triangle's vertex indices.
|
|
const uint32 I0 = LODRenderData.GetIndex(TriangleIndex * 3 + 0);
|
|
const uint32 I1 = LODRenderData.GetIndex(TriangleIndex * 3 + (bReverseWinding ? 2 : 1));
|
|
const uint32 I2 = LODRenderData.GetIndex(TriangleIndex * 3 + (bReverseWinding ? 1 : 2));
|
|
|
|
// Translate the triangle's static mesh vertices to static lighting vertices.
|
|
GetStaticLightingVertex(LODRenderData.GetVertex(I0), LocalToWorld, LocalToWorldInverseTranspose, bIsSplineMesh, SplineParameters, OutV0);
|
|
GetStaticLightingVertex(LODRenderData.GetVertex(I1), LocalToWorld, LocalToWorldInverseTranspose, bIsSplineMesh, SplineParameters, OutV1);
|
|
GetStaticLightingVertex(LODRenderData.GetVertex(I2), LocalToWorld, LocalToWorldInverseTranspose, bIsSplineMesh, SplineParameters, OutV2);
|
|
|
|
const FStaticMeshLOD& MeshLOD = StaticMesh->GetLOD(GetMeshLODIndex());
|
|
ElementIndex = INDEX_NONE;
|
|
for (uint32 MeshElementIndex = 0; MeshElementIndex < MeshLOD.NumElements; MeshElementIndex++)
|
|
{
|
|
const FStaticMeshElement& CurrentElement = MeshLOD.GetElement(MeshElementIndex);
|
|
if ((uint32)TriangleIndex >= CurrentElement.FirstIndex / 3 && (uint32)TriangleIndex < CurrentElement.FirstIndex / 3 + CurrentElement.NumTriangles)
|
|
{
|
|
ElementIndex = MeshElementIndex;
|
|
break;
|
|
}
|
|
}
|
|
check(ElementIndex >= 0);
|
|
}
|
|
|
|
void FStaticMeshStaticLightingMesh::GetNonTransformedTriangle(int32 TriangleIndex, FStaticLightingVertex& OutV0, FStaticLightingVertex& OutV1, FStaticLightingVertex& OutV2, int32& ElementIndex) const
|
|
{
|
|
check(!bIsSplineMesh);
|
|
|
|
const FStaticMeshLOD& LODRenderData = StaticMesh->GetLOD(GetMeshLODIndex());
|
|
|
|
// Lookup the triangle's vertex indices.
|
|
const uint32 I0 = LODRenderData.GetIndex(TriangleIndex * 3 + 0);
|
|
const uint32 I1 = LODRenderData.GetIndex(TriangleIndex * 3 + 1);
|
|
const uint32 I2 = LODRenderData.GetIndex(TriangleIndex * 3 + 2);
|
|
|
|
// Translate the triangle's static mesh vertices to static lighting vertices.
|
|
GetStaticLightingVertex(LODRenderData.GetVertex(I0), FMatrix44f::Identity, FMatrix44f::Identity, false, SplineParameters, OutV0);
|
|
GetStaticLightingVertex(LODRenderData.GetVertex(I1), FMatrix44f::Identity, FMatrix44f::Identity, false, SplineParameters, OutV1);
|
|
GetStaticLightingVertex(LODRenderData.GetVertex(I2), FMatrix44f::Identity, FMatrix44f::Identity, false, SplineParameters, OutV2);
|
|
|
|
const FStaticMeshLOD& MeshLOD = StaticMesh->GetLOD(GetMeshLODIndex());
|
|
ElementIndex = INDEX_NONE;
|
|
for (uint32 MeshElementIndex = 0; MeshElementIndex < MeshLOD.NumElements; MeshElementIndex++)
|
|
{
|
|
const FStaticMeshElement& CurrentElement = MeshLOD.GetElement(MeshElementIndex);
|
|
if ((uint32)TriangleIndex >= CurrentElement.FirstIndex / 3 && (uint32)TriangleIndex < CurrentElement.FirstIndex / 3 + CurrentElement.NumTriangles)
|
|
{
|
|
ElementIndex = MeshElementIndex;
|
|
break;
|
|
}
|
|
}
|
|
check(ElementIndex >= 0);
|
|
}
|
|
|
|
void FStaticMeshStaticLightingMesh::GetTriangleIndices(int32 TriangleIndex,int32& OutI0,int32& OutI1,int32& OutI2) const
|
|
{
|
|
const FStaticMeshLOD& LODRenderData = StaticMesh->GetLOD(GetMeshLODIndex());
|
|
|
|
// Lookup the triangle's vertex indices.
|
|
OutI0 = LODRenderData.GetIndex(TriangleIndex * 3 + 0);
|
|
OutI1 = LODRenderData.GetIndex(TriangleIndex * 3 + (bReverseWinding ? 2 : 1));
|
|
OutI2 = LODRenderData.GetIndex(TriangleIndex * 3 + (bReverseWinding ? 1 : 2));
|
|
}
|
|
|
|
void FStaticMeshStaticLightingMesh::GetNonTransformedTriangleIndices(int32 TriangleIndex, int32& OutI0, int32& OutI1, int32& OutI2) const
|
|
{
|
|
const FStaticMeshLOD& LODRenderData = StaticMesh->GetLOD(GetMeshLODIndex());
|
|
|
|
// Lookup the triangle's vertex indices.
|
|
OutI0 = LODRenderData.GetIndex(TriangleIndex * 3 + 0);
|
|
OutI1 = LODRenderData.GetIndex(TriangleIndex * 3 + 1);
|
|
OutI2 = LODRenderData.GetIndex(TriangleIndex * 3 + 2);
|
|
}
|
|
|
|
bool FStaticMeshStaticLightingMesh::IsElementCastingShadow(int32 ElementIndex) const
|
|
{
|
|
const FStaticMeshLOD& LODRenderData = StaticMesh->GetLOD(GetMeshLODIndex());
|
|
const FStaticMeshElement& Element = LODRenderData.GetElement( ElementIndex );
|
|
return Element.bEnableShadowCasting;
|
|
}
|
|
|
|
void FStaticMeshStaticLightingMesh::Import( FLightmassImporter& Importer )
|
|
{
|
|
// import super class
|
|
FStaticLightingMesh::Import( Importer );
|
|
|
|
// import the shared data
|
|
// Importer.ImportData( (FStaticMeshStaticLightingMeshData*) this );
|
|
FStaticMeshStaticLightingMeshData TempSMSLMD;
|
|
Importer.ImportData(&TempSMSLMD);
|
|
EncodedLODIndices = TempSMSLMD.EncodedLODIndices;
|
|
EncodedHLODRange = TempSMSLMD.EncodedHLODRange;
|
|
LocalToWorld = TempSMSLMD.LocalToWorld;
|
|
bReverseWinding = TempSMSLMD.bReverseWinding;
|
|
bShouldSelfShadow = TempSMSLMD.bShouldSelfShadow;
|
|
StaticMeshGuid = TempSMSLMD.StaticMeshGuid;
|
|
bIsSplineMesh = TempSMSLMD.bIsSplineMesh;
|
|
SplineParameters = TempSMSLMD.SplineParameters;
|
|
|
|
// calculate the inverse transpose
|
|
WorldToLocal = LocalToWorld.InverseFast();
|
|
LocalToWorldInverseTranspose = LocalToWorld.InverseFast().GetTransposed();
|
|
|
|
// we have the guid for the mesh, now hook it up to the actual static mesh
|
|
StaticMesh = Importer.ConditionalImportObject<FStaticMesh>(StaticMeshGuid, LM_STATICMESH_VERSION, LM_STATICMESH_EXTENSION, LM_STATICMESH_CHANNEL_FLAGS, Importer.GetStaticMeshes());
|
|
checkf(StaticMesh, TEXT("Failed to import static mesh with GUID %s"), *StaticMeshGuid.ToString());
|
|
check(GetMeshLODIndex() >= 0 && GetMeshLODIndex() < (int32)StaticMesh->NumLODs);
|
|
checkf(StaticMesh->GetLOD(GetMeshLODIndex()).NumElements == MaterialElements.Num(), TEXT("Static mesh element count did not match mesh instance element count!"));
|
|
}
|
|
|
|
void FStaticMeshStaticLightingTextureMapping::Import( FLightmassImporter& Importer )
|
|
{
|
|
// import the super class
|
|
FStaticLightingTextureMapping::Import(Importer);
|
|
check(Mesh);
|
|
}
|
|
|
|
} //namespace Lightmass
|