You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
ADDED Support for importing GeometryCache assets from Alembic files ADDED CalculateRawMeshTangets function that only calculates the tangents without building the RawMesh [CL 2591762 by Jurre DeBaare in Main branch]
446 lines
12 KiB
C++
446 lines
12 KiB
C++
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "GeometryCacheModulePrivatePCH.h"
|
|
#include "GeometryCacheComponent.h"
|
|
|
|
#include "GeometryCacheSceneProxy.h"
|
|
#include "GeometryCacheTrack.h"
|
|
#include "GeometryCacheTrackFlipbookAnimation.h"
|
|
|
|
DECLARE_CYCLE_STAT(TEXT("GeometryCacheTick"), STAT_GeometryCacheComponent_TickComponent, STATGROUP_GeometryCache);
|
|
|
|
UGeometryCacheComponent::UGeometryCacheComponent(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
PrimaryComponentTick.bCanEverTick = true;
|
|
ElapsedTime = 0.0f;
|
|
bRunning = false;
|
|
bLooping = true;
|
|
PlayDirection = 1.0f;
|
|
StartTimeOffset = 0.0f;
|
|
PlaybackSpeed = 1.0f;
|
|
Duration = 0.0f;
|
|
}
|
|
|
|
void UGeometryCacheComponent::BeginDestroy()
|
|
{
|
|
Super::BeginDestroy();
|
|
ReleaseResources();
|
|
}
|
|
|
|
void UGeometryCacheComponent::OnRegister()
|
|
{
|
|
NumTracks = 0;
|
|
TrackMatrixSampleIndices.Empty();
|
|
TrackMatrixSampleIndices.Empty();
|
|
TrackSections.Empty();
|
|
|
|
if (GeometryCache != NULL)
|
|
{
|
|
// Refresh NumTracks and clear Index Arrays
|
|
NumTracks = GeometryCache->Tracks.Num();
|
|
TrackMeshSampleIndices.Empty(GeometryCache->Tracks.Num());
|
|
TrackMatrixSampleIndices.Empty(GeometryCache->Tracks.Num());
|
|
|
|
Duration = 0.0f;
|
|
// Create mesh sections for each GeometryCacheTrack
|
|
for (int32 TrackIndex = 0; TrackIndex < NumTracks; ++TrackIndex )
|
|
{
|
|
UGeometryCacheTrack* Track = GeometryCache->Tracks[TrackIndex];
|
|
FMatrix WorldMatrix;
|
|
int32 MeshSampleIndex = -1;
|
|
int32 MatrixSampleIndex = -1;
|
|
FGeometryCacheMeshData* MeshData = NULL;
|
|
|
|
// Retrieve the matrix/mesh data and the appropriate sample indices
|
|
Track->UpdateMatrixData(ElapsedTime + StartTimeOffset, bLooping, MatrixSampleIndex, WorldMatrix);
|
|
Track->UpdateMeshData(ElapsedTime + StartTimeOffset, bLooping, MeshSampleIndex, MeshData);
|
|
|
|
// First time so create rather than update the mesh sections
|
|
CreateTrackSection(TrackIndex, WorldMatrix, MeshData);
|
|
|
|
// Store the sample indices for both the mesh and matrix data
|
|
TrackMeshSampleIndices.Add(MeshSampleIndex);
|
|
TrackMatrixSampleIndices.Add(MatrixSampleIndex);
|
|
|
|
const float TrackMaxSampleTime = Track->GetMaxSampleTime();
|
|
Duration = ( Duration > TrackMaxSampleTime ) ? Duration : TrackMaxSampleTime;
|
|
}
|
|
}
|
|
|
|
Super::OnRegister();
|
|
}
|
|
|
|
void UGeometryCacheComponent::OnUnregister()
|
|
{
|
|
Super::OnUnregister();
|
|
|
|
NumTracks = 0;
|
|
TrackMatrixSampleIndices.Empty();
|
|
TrackMatrixSampleIndices.Empty();
|
|
TrackSections.Empty();
|
|
}
|
|
|
|
void UGeometryCacheComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
|
|
{
|
|
SCOPE_CYCLE_COUNTER(STAT_GeometryCacheComponent_TickComponent);
|
|
if (GeometryCache && bRunning)
|
|
{
|
|
// Increase total elapsed time since BeginPlay according to PlayDirection and speed
|
|
ElapsedTime += (DeltaTime * PlayDirection * PlaybackSpeed);
|
|
|
|
// QQ change this for reverse playing? extra float for runningtime vs sample time?
|
|
if (ElapsedTime < 0.0f)
|
|
{
|
|
ElapsedTime += Duration;
|
|
}
|
|
|
|
for (int32 TrackIndex = 0; TrackIndex < NumTracks; ++TrackIndex)
|
|
{
|
|
// Determine if and which part of the section we have to update
|
|
UGeometryCacheTrack* Track = GeometryCache->Tracks[TrackIndex];
|
|
FMatrix WorldMatrix;
|
|
FGeometryCacheMeshData* MeshData = NULL;
|
|
|
|
const bool bUpdateMatrix = Track->UpdateMatrixData(ElapsedTime + StartTimeOffset, bLooping, TrackMatrixSampleIndices[TrackIndex], WorldMatrix);
|
|
const bool bUpdateMesh = Track->UpdateMeshData(ElapsedTime + StartTimeOffset, bLooping, TrackMeshSampleIndices[TrackIndex], MeshData);
|
|
|
|
// Update sections according what is required
|
|
if (bUpdateMatrix)
|
|
{
|
|
UpdateTrackSectionMatrixData(TrackIndex, WorldMatrix);
|
|
}
|
|
|
|
if (bUpdateMesh)
|
|
{
|
|
UpdateTrackSectionMeshData(TrackIndex, MeshData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FBoxSphereBounds UGeometryCacheComponent::CalcBounds(const FTransform& LocalToWorld) const
|
|
{
|
|
return LocalBounds.TransformBy(LocalToWorld);
|
|
}
|
|
void UGeometryCacheComponent::UpdateLocalBounds()
|
|
{
|
|
FBox LocalBox(0);
|
|
|
|
for (const FTrackRenderData& Section : TrackSections)
|
|
{
|
|
// Use World matrix per section for correct bounding box
|
|
LocalBox += (Section.MeshData->BoundingBox.TransformBy(Section.WorldMatrix));
|
|
}
|
|
|
|
LocalBounds = LocalBox.IsValid ? FBoxSphereBounds(LocalBox) : FBoxSphereBounds(FVector(0, 0, 0), FVector(0, 0, 0), 0); // fallback to reset box sphere bounds
|
|
|
|
UpdateBounds();
|
|
}
|
|
|
|
FPrimitiveSceneProxy* UGeometryCacheComponent::CreateSceneProxy()
|
|
{
|
|
SceneProxy = new FGeometryCacheSceneProxy(this);
|
|
return SceneProxy;
|
|
}
|
|
|
|
void UGeometryCacheComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
|
{
|
|
InvalidateTrackSampleIndices();
|
|
MarkRenderStateDirty();
|
|
Super::PostEditChangeProperty(PropertyChangedEvent);
|
|
}
|
|
|
|
int32 UGeometryCacheComponent::GetNumMaterials() const
|
|
{
|
|
// Sum the number of materials per track
|
|
int32 TotalNumMaterial = 0;
|
|
if ( GeometryCache )
|
|
{
|
|
for (UGeometryCacheTrack* Track : GeometryCache->Tracks)
|
|
{
|
|
TotalNumMaterial += Track->GetNumMaterials();
|
|
}
|
|
}
|
|
|
|
return TotalNumMaterial;
|
|
}
|
|
|
|
void UGeometryCacheComponent::CreateTrackSection(int32 SectionIndex, const FMatrix& WorldMatrix, FGeometryCacheMeshData* MeshData)
|
|
{
|
|
// Ensure sections array is long enough
|
|
if (TrackSections.Num() <= SectionIndex)
|
|
{
|
|
TrackSections.SetNum(SectionIndex + 1, false);
|
|
}
|
|
|
|
// Reset this section (in case it already existed)
|
|
FTrackRenderData& NewSection = TrackSections[SectionIndex];
|
|
NewSection.Reset();
|
|
|
|
// Number of Vertices
|
|
const int32 NumVerts = MeshData->Vertices.Num();
|
|
|
|
// Copy index buffer (clamping to vertex range)
|
|
const int32 NumTriIndices = NumVerts;
|
|
NewSection.IndexBuffer.Reset();
|
|
NewSection.IndexBuffer.AddUninitialized(NumTriIndices);
|
|
for (int32 IndexIdx = 0; IndexIdx < NumTriIndices; IndexIdx++)
|
|
{
|
|
NewSection.IndexBuffer[IndexIdx] = FMath::Min(IndexIdx, NumVerts - 1);
|
|
}
|
|
|
|
// Store Matrix and MeshData-pointer
|
|
NewSection.WorldMatrix = WorldMatrix;
|
|
NewSection.MeshData = MeshData;
|
|
|
|
UpdateLocalBounds(); // Update overall bounds
|
|
MarkRenderStateDirty(); // Recreate scene proxy
|
|
}
|
|
|
|
void UGeometryCacheComponent::UpdateTrackSectionMeshData(int32 SectionIndex, FGeometryCacheMeshData* MeshData)
|
|
{
|
|
// Check if the index is in range and Vertices contains any data
|
|
check(SectionIndex < TrackSections.Num() && MeshData != nullptr && "Invalid SectionIndex or Mesh Data");
|
|
// Reset this section (in case it already existed)
|
|
FTrackRenderData& UpdateSection = TrackSections[SectionIndex];
|
|
|
|
// Number of Vertices
|
|
const int32 NumVerts = MeshData->Vertices.Num();
|
|
// Determine if we actually need a new index buffer
|
|
if (UpdateSection.IndexBuffer.Num() != MeshData->Vertices.Num())
|
|
{
|
|
// Copy index buffer (clamping to vertex range)
|
|
const int32 NumTriIndices = NumVerts;
|
|
UpdateSection.IndexBuffer.Reset();
|
|
UpdateSection.IndexBuffer.AddUninitialized(NumTriIndices);
|
|
for (int32 IndexIdx = 0; IndexIdx < NumTriIndices; IndexIdx++)
|
|
{
|
|
UpdateSection.IndexBuffer[IndexIdx] = FMath::Min(IndexIdx, NumVerts - 1);
|
|
}
|
|
|
|
if (SceneProxy)
|
|
{
|
|
UpdateTrackSectionIndexbuffer(SectionIndex, UpdateSection.IndexBuffer);
|
|
}
|
|
}
|
|
|
|
// Update MeshDataPointer
|
|
UpdateSection.MeshData = MeshData;
|
|
|
|
if (SceneProxy)
|
|
{
|
|
UpdateTrackSectionVertexbuffer(SectionIndex, MeshData);
|
|
// Will update the bounds (used for frustum culling)
|
|
MarkRenderTransformDirty();
|
|
}
|
|
else
|
|
{
|
|
// Recreate scene proxy
|
|
MarkRenderStateDirty();
|
|
}
|
|
|
|
MarkRenderStateDirty();
|
|
|
|
// Update overall bounds
|
|
UpdateLocalBounds();
|
|
}
|
|
|
|
void UGeometryCacheComponent::UpdateTrackSectionMatrixData(int32 SectionIndex, const FMatrix& WorldMatrix)
|
|
{
|
|
check(SectionIndex < TrackSections.Num() && "Invalid SectionIndex");
|
|
// Reset this section (in case it already existed)
|
|
FTrackRenderData& UpdateSection = TrackSections[SectionIndex];
|
|
|
|
// Update the WorldMatrix only
|
|
UpdateSection.WorldMatrix = WorldMatrix;
|
|
|
|
if (!IsRenderStateDirty() && SceneProxy != NULL)
|
|
{
|
|
// Update it within the scene proxy
|
|
SceneProxy->UpdateSectionWorldMatrix(SectionIndex, WorldMatrix);
|
|
}
|
|
|
|
// Update local bounds
|
|
UpdateLocalBounds();
|
|
|
|
// Mark transform Dirty
|
|
MarkRenderTransformDirty();
|
|
}
|
|
|
|
void UGeometryCacheComponent::UpdateTrackSectionVertexbuffer(int32 SectionIndex, FGeometryCacheMeshData* MeshData)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
|
|
FUpdateVertexBufferCommand,
|
|
FGeometryCacheSceneProxy*, SceneProxy, SceneProxy,
|
|
const int32, Index, SectionIndex,
|
|
FGeometryCacheMeshData*, Data, MeshData,
|
|
{
|
|
SceneProxy->UpdateSectionVertexBuffer(Index, Data);
|
|
});
|
|
}
|
|
|
|
void UGeometryCacheComponent::UpdateTrackSectionIndexbuffer(int32 SectionIndex, const TArray<int32>& Indices)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
|
|
FUpdateVertexBufferCommand,
|
|
FGeometryCacheSceneProxy*, SceneProxy, SceneProxy,
|
|
const int32, Index, SectionIndex,
|
|
const TArray<int32>&, Data, Indices,
|
|
{
|
|
SceneProxy->UpdateSectionIndexBuffer(Index, Data);
|
|
});
|
|
}
|
|
|
|
void UGeometryCacheComponent::OnObjectReimported(UGeometryCache* ImportedGeometryCache)
|
|
{
|
|
if (GeometryCache == ImportedGeometryCache)
|
|
{
|
|
ReleaseResources();
|
|
GeometryCache = ImportedGeometryCache;
|
|
|
|
if (GeometryCache != NULL)
|
|
{
|
|
// Refresh NumTracks and clear Index Arrays
|
|
NumTracks = GeometryCache->Tracks.Num();
|
|
TrackMeshSampleIndices.Empty(GeometryCache->Tracks.Num());
|
|
TrackMatrixSampleIndices.Empty(GeometryCache->Tracks.Num());
|
|
|
|
Duration = 0.0f;
|
|
// Create mesh sections for each GeometryCacheTrack
|
|
for (int32 TrackIndex = 0; TrackIndex < NumTracks; ++TrackIndex)
|
|
{
|
|
UGeometryCacheTrack* Track = GeometryCache->Tracks[TrackIndex];
|
|
FMatrix WorldMatrix;
|
|
int32 MeshSampleIndex = -1;
|
|
int32 MatrixSampleIndex = -1;
|
|
FGeometryCacheMeshData* MeshData = NULL;
|
|
|
|
// Retrieve the matrix/mesh data and the appropriate sample indices
|
|
Track->UpdateMatrixData(ElapsedTime + StartTimeOffset, bLooping, MatrixSampleIndex, WorldMatrix);
|
|
Track->UpdateMeshData(ElapsedTime + StartTimeOffset, bLooping, MeshSampleIndex, MeshData);
|
|
|
|
// First time so create rather than update the mesh sections
|
|
CreateTrackSection(TrackIndex, WorldMatrix, MeshData);
|
|
|
|
// Store the sample indices for both the mesh and matrix data
|
|
TrackMeshSampleIndices.Add(MeshSampleIndex);
|
|
TrackMatrixSampleIndices.Add(MatrixSampleIndex);
|
|
|
|
const float TrackMaxSampleTime = Track->GetMaxSampleTime();
|
|
Duration = (Duration > TrackMaxSampleTime) ? Duration : TrackMaxSampleTime;
|
|
}
|
|
}
|
|
|
|
if (SceneProxy)
|
|
{
|
|
SceneProxy->ClearSections();
|
|
}
|
|
|
|
|
|
MarkRenderStateDirty();
|
|
}
|
|
}
|
|
|
|
void UGeometryCacheComponent::Play()
|
|
{
|
|
bRunning = true;
|
|
PlayDirection = 1.0f;
|
|
}
|
|
|
|
void UGeometryCacheComponent::PlayFromStart()
|
|
{
|
|
ElapsedTime = 0.0f;
|
|
bRunning = true;
|
|
PlayDirection = 1.0f;
|
|
}
|
|
|
|
void UGeometryCacheComponent::Pause()
|
|
{
|
|
bRunning = !bRunning;
|
|
}
|
|
|
|
void UGeometryCacheComponent::Stop()
|
|
{
|
|
bRunning = false;
|
|
}
|
|
|
|
bool UGeometryCacheComponent::IsPlaying() const
|
|
{
|
|
return bRunning;
|
|
}
|
|
|
|
bool UGeometryCacheComponent::IsLooping() const
|
|
{
|
|
return bLooping;
|
|
}
|
|
|
|
void UGeometryCacheComponent::SetLooping(const bool bNewLooping)
|
|
{
|
|
bLooping = bNewLooping;
|
|
}
|
|
|
|
bool UGeometryCacheComponent::IsPlayingReversed() const
|
|
{
|
|
return FMath::IsNearlyEqual( PlayDirection, -1.0f );
|
|
}
|
|
|
|
float UGeometryCacheComponent::GetPlaybackSpeed() const
|
|
{
|
|
return PlaybackSpeed;
|
|
}
|
|
|
|
void UGeometryCacheComponent::SetPlaybackSpeed(const float NewPlaybackSpeed)
|
|
{
|
|
// Currently only positive play back speeds are supported
|
|
PlaybackSpeed = fabs( NewPlaybackSpeed );
|
|
}
|
|
|
|
void UGeometryCacheComponent::PlayReversedFromEnd()
|
|
{
|
|
ElapsedTime = Duration;
|
|
PlayDirection = -1.0f;
|
|
bRunning = true;
|
|
}
|
|
|
|
void UGeometryCacheComponent::PlayReversed()
|
|
{
|
|
PlayDirection = -1.0f;
|
|
bRunning = true;
|
|
}
|
|
|
|
void UGeometryCacheComponent::InvalidateTrackSampleIndices()
|
|
{
|
|
for (int32& Index : TrackMeshSampleIndices)
|
|
{
|
|
Index = -1;
|
|
}
|
|
|
|
for (int32& Index : TrackMatrixSampleIndices)
|
|
{
|
|
Index = -1;
|
|
}
|
|
}
|
|
|
|
void UGeometryCacheComponent::ReleaseResources()
|
|
{
|
|
GeometryCache = NULL;
|
|
NumTracks = 0;
|
|
TrackMatrixSampleIndices.Empty();
|
|
TrackMatrixSampleIndices.Empty();
|
|
TrackSections.Empty();
|
|
DetachFence.BeginFence();
|
|
}
|
|
|
|
void UGeometryCacheComponent::PreEditUndo()
|
|
{
|
|
InvalidateTrackSampleIndices();
|
|
MarkRenderStateDirty();
|
|
}
|
|
|
|
void UGeometryCacheComponent::PostEditUndo()
|
|
{
|
|
InvalidateTrackSampleIndices();
|
|
MarkRenderStateDirty();
|
|
}
|