// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. #include "GeometryCacheModulePrivatePCH.h" #include "GeometryCacheSceneProxy.h" #include "GeometryCacheComponent.h" #include "GeometryCacheMeshData.h" FGeometryCacheSceneProxy::FGeometryCacheSceneProxy(UGeometryCacheComponent* Component) : FPrimitiveSceneProxy(Component) , MaterialRelevance(Component->GetMaterialRelevance(GetScene().GetFeatureLevel())) { // Copy each section const int32 NumSections = Component->TrackSections.Num(); Sections.AddZeroed(NumSections); for (int SectionIdx = 0; SectionIdx < NumSections; SectionIdx++) { FTrackRenderData& SrcSection = Component->TrackSections[SectionIdx]; if (SrcSection.IndexBuffer.Num() > 0) { FGeomCacheTrackProxy* NewSection = new FGeomCacheTrackProxy(); NewSection->WorldMatrix = SrcSection.WorldMatrix; FGeometryCacheMeshData* MeshData = NewSection->MeshData = SrcSection.MeshData; // Copy data from vertex buffer const int32 NumVerts = MeshData->Vertices.Num(); // Allocate verts NewSection->VertexBuffer.Vertices.Empty(NumVerts); // Copy verts NewSection->VertexBuffer.Vertices.Append(MeshData->Vertices); // Copy index buffer NewSection->IndexBuffer.Indices = SrcSection.IndexBuffer; // Init vertex factory NewSection->VertexFactory.Init(&NewSection->VertexBuffer); // Enqueue initialization of render resource BeginInitResource(&NewSection->VertexBuffer); BeginInitResource(&NewSection->IndexBuffer); BeginInitResource(&NewSection->VertexFactory); // Grab materials for (FGeometryCacheMeshBatchInfo& BatchInfo : MeshData->BatchesInfo) { UMaterialInterface* Material = Component->GetMaterial(BatchInfo.MaterialIndex); if (Material == NULL) { Material = UMaterial::GetDefaultMaterial(MD_Surface); } NewSection->Materials.Push(Material); } // Save ref to new section Sections[SectionIdx] = NewSection; } } } FGeometryCacheSceneProxy::~FGeometryCacheSceneProxy() { for (FGeomCacheTrackProxy* Section : Sections) { if (Section != nullptr) { Section->VertexBuffer.ReleaseResource(); Section->IndexBuffer.ReleaseResource(); Section->VertexFactory.ReleaseResource(); delete Section; } } Sections.Empty(); } void FGeometryCacheSceneProxy::GetDynamicMeshElements(const TArray& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const { SCOPE_CYCLE_COUNTER(STAT_GeometryCacheSceneProxy_GetMeshElements); // Set up wireframe material (if needed) const bool bWireframe = AllowDebugViewmodes() && ViewFamily.EngineShowFlags.Wireframe; FColoredMaterialRenderProxy* WireframeMaterialInstance = NULL; if (bWireframe) { WireframeMaterialInstance = new FColoredMaterialRenderProxy( GEngine->WireframeMaterial ? GEngine->WireframeMaterial->GetRenderProxy(IsSelected()) : NULL, FLinearColor(0, 0.5f, 1.f) ); Collector.RegisterOneFrameMaterialProxy(WireframeMaterialInstance); } // Iterate over sections for (const FGeomCacheTrackProxy* TrackProxy : Sections ) { // QQQ if (TrackProxy != nullptr) { INC_DWORD_STAT_BY(STAT_GeometryCacheSceneProxy_MeshBatchCount, TrackProxy->MeshData->BatchesInfo.Num()); int32 BatchIndex = 0; for (FGeometryCacheMeshBatchInfo& BatchInfo : TrackProxy->MeshData->BatchesInfo) { FMaterialRenderProxy* MaterialProxy = bWireframe ? WireframeMaterialInstance : TrackProxy->Materials[BatchIndex]->GetRenderProxy(IsSelected()); for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { if (VisibilityMap & (1 << ViewIndex)) { const FSceneView* View = Views[ViewIndex]; // Draw the mesh. FMeshBatch& Mesh = Collector.AllocateMesh(); FMeshBatchElement& BatchElement = Mesh.Elements[0]; BatchElement.IndexBuffer = &TrackProxy->IndexBuffer; Mesh.bWireframe = bWireframe; Mesh.VertexFactory = &TrackProxy->VertexFactory; Mesh.MaterialRenderProxy = MaterialProxy; BatchElement.PrimitiveUniformBuffer = CreatePrimitiveUniformBufferImmediate(TrackProxy->WorldMatrix * GetLocalToWorld(), GetBounds(), GetLocalBounds(), true, UseEditorDepthTest()); BatchElement.FirstIndex = BatchInfo.StartIndex; BatchElement.NumPrimitives = BatchInfo.NumTriangles; BatchElement.MinVertexIndex = 0; BatchElement.MaxVertexIndex = TrackProxy->VertexBuffer.Vertices.Num() - 1; Mesh.ReverseCulling = IsLocalToWorldDeterminantNegative(); Mesh.Type = PT_TriangleList; Mesh.DepthPriorityGroup = SDPG_World; Mesh.bCanApplyViewModeOverrides = false; Collector.AddMesh(ViewIndex, Mesh); INC_DWORD_STAT_BY(STAT_GeometryCacheSceneProxy_TriangleCount, BatchElement.NumPrimitives); } } ++BatchIndex; } } } // Draw bounds #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++) { if (VisibilityMap & (1 << ViewIndex)) { // Render bounds RenderBounds(Collector.GetPDI(ViewIndex), ViewFamily.EngineShowFlags, GetBounds(), IsSelected()); } } #endif } FPrimitiveViewRelevance FGeometryCacheSceneProxy::GetViewRelevance(const FSceneView* View) { FPrimitiveViewRelevance Result; Result.bDrawRelevance = IsShown(View); Result.bShadowRelevance = IsShadowCast(View); Result.bDynamicRelevance = true; MaterialRelevance.SetPrimitiveViewRelevance(Result); return Result; } bool FGeometryCacheSceneProxy::CanBeOccluded() const { return !MaterialRelevance.bDisableDepthTest; } uint32 FGeometryCacheSceneProxy::GetMemoryFootprint(void) const { return(sizeof(*this) + GetAllocatedSize()); } uint32 FGeometryCacheSceneProxy::GetAllocatedSize(void) const { return(FPrimitiveSceneProxy::GetAllocatedSize()); } void FGeometryCacheSceneProxy::UpdateSectionWorldMatrix(const int32 SectionIndex, const FMatrix& WorldMatrix) { check(SectionIndex < Sections.Num() && "Section Index out of range"); Sections[SectionIndex]->WorldMatrix = WorldMatrix; } void FGeometryCacheSceneProxy::UpdateSectionVertexBuffer(const int32 SectionIndex, FGeometryCacheMeshData* MeshData) { check(SectionIndex < Sections.Num() && "Section Index out of range"); check(IsInRenderingThread()); Sections[SectionIndex]->MeshData = MeshData; const bool bRecreate = Sections[SectionIndex]->VertexBuffer.Vertices.Num() != Sections[SectionIndex]->MeshData->Vertices.Num(); Sections[SectionIndex]->VertexBuffer.Vertices.Empty(Sections[SectionIndex]->MeshData->Vertices.Num()); Sections[SectionIndex]->VertexBuffer.Vertices.Append(Sections[SectionIndex]->MeshData->Vertices); if (bRecreate) { Sections[SectionIndex]->VertexBuffer.InitRHI(); } else { Sections[SectionIndex]->VertexBuffer.UpdateRHI(); } } void FGeometryCacheSceneProxy::UpdateSectionIndexBuffer(const int32 SectionIndex, const TArray& Indices) { check(SectionIndex < Sections.Num() && "Section Index out of range"); check(IsInRenderingThread()); const bool bRecreate = Sections[SectionIndex]->IndexBuffer.Indices.Num() != Indices.Num(); Sections[SectionIndex]->IndexBuffer.Indices.Empty(Indices.Num()); Sections[SectionIndex]->IndexBuffer.Indices.Append(Indices); if (bRecreate) { Sections[SectionIndex]->IndexBuffer.InitRHI(); } else { Sections[SectionIndex]->IndexBuffer.UpdateRHI(); } } void FGeometryCacheSceneProxy::ClearSections() { Sections.Empty(); } FGeomCacheVertexFactory::FGeomCacheVertexFactory() { } void FGeomCacheVertexFactory::Init_RenderThread(const FGeomCacheVertexBuffer* VertexBuffer) { check(IsInRenderingThread()); // Initialize the vertex factory's stream components. DataType NewData; NewData.PositionComponent = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(VertexBuffer, FDynamicMeshVertex, Position, VET_Float3); NewData.TextureCoordinates.Add( FVertexStreamComponent(VertexBuffer, STRUCT_OFFSET(FDynamicMeshVertex, TextureCoordinate), sizeof(FDynamicMeshVertex), VET_Float2) ); NewData.TangentBasisComponents[0] = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(VertexBuffer, FDynamicMeshVertex, TangentX, VET_PackedNormal); NewData.TangentBasisComponents[1] = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(VertexBuffer, FDynamicMeshVertex, TangentZ, VET_PackedNormal); NewData.ColorComponent = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(VertexBuffer, FDynamicMeshVertex, Color, VET_Color); SetData(NewData); } void FGeomCacheVertexFactory::Init(const FGeomCacheVertexBuffer* VertexBuffer) { if (IsInRenderingThread()) { Init_RenderThread(VertexBuffer); } else { ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER( InitGeomCacheVertexFactory, FGeomCacheVertexFactory*, VertexFactory, this, const FGeomCacheVertexBuffer*, VertexBuffer, VertexBuffer, { VertexFactory->Init_RenderThread(VertexBuffer); }); } } void FGeomCacheIndexBuffer::InitRHI() { FRHIResourceCreateInfo CreateInfo; void* Buffer = nullptr; IndexBufferRHI = RHICreateAndLockIndexBuffer(sizeof(int32), Indices.Num() * sizeof(int32), BUF_Static, CreateInfo, Buffer); // Write the indices to the index buffer. FMemory::Memcpy(Buffer, Indices.GetData(), Indices.Num() * sizeof(int32)); RHIUnlockIndexBuffer(IndexBufferRHI); } void FGeomCacheIndexBuffer::UpdateRHI() { // Copy the index data into the index buffer. void* Buffer = RHILockIndexBuffer(IndexBufferRHI, 0, Indices.Num() * sizeof(int32), RLM_WriteOnly); FMemory::Memcpy(Buffer, Indices.GetData(), Indices.Num() * sizeof(int32)); RHIUnlockIndexBuffer(IndexBufferRHI); } void FGeomCacheVertexBuffer::InitRHI() { const uint32 SizeInBytes = Vertices.Num() * sizeof(FDynamicMeshVertex); FGeomCacheVertexResourceArray ResourceArray(Vertices.GetData(), SizeInBytes); FRHIResourceCreateInfo CreateInfo(&ResourceArray); VertexBufferRHI = RHICreateVertexBuffer(SizeInBytes, BUF_Static, CreateInfo); } void FGeomCacheVertexBuffer::UpdateRHI() { // Copy the vertex data into the vertex buffer. void* VertexBufferData = RHILockVertexBuffer(VertexBufferRHI, 0, Vertices.Num() * sizeof(FDynamicMeshVertex), RLM_WriteOnly); FMemory::Memcpy(VertexBufferData, Vertices.GetData(), Vertices.Num() * sizeof(FDynamicMeshVertex)); RHIUnlockVertexBuffer(VertexBufferRHI); } FGeomCacheVertexResourceArray::FGeomCacheVertexResourceArray(void* InData, uint32 InSize) : Data(InData) , Size(InSize) { } const void* FGeomCacheVertexResourceArray::GetResourceData() const { return Data; } uint32 FGeomCacheVertexResourceArray::GetResourceDataSize() const { return Size; } void FGeomCacheVertexResourceArray::Discard() { } bool FGeomCacheVertexResourceArray::IsStatic() const { return false; } bool FGeomCacheVertexResourceArray::GetAllowCPUAccess() const { return false; } void FGeomCacheVertexResourceArray::SetAllowCPUAccess(bool bInNeedsCPUAccess) { }