[ue4] Removed runtime mesh component dependency, using procedural mesh component again. All samples are broken because of serialization issues.

This commit is contained in:
badlogic 2016-12-15 10:32:11 +01:00
parent 686eab6fd4
commit 014e8d4425
37 changed files with 5 additions and 10696 deletions

View File

@ -6,7 +6,7 @@
#define LOCTEXT_NAMESPACE "Spine"
USpineSkeletonRendererComponent::USpineSkeletonRendererComponent (const FObjectInitializer& ObjectInitializer)
: URuntimeMeshComponent(ObjectInitializer) {
: UProceduralMeshComponent(ObjectInitializer) {
bWantsBeginPlay = true;
PrimaryComponentTick.bCanEverTick = true;
bTickInEditor = true;
@ -132,7 +132,7 @@ void USpineSkeletonRendererComponent::TickComponent (float DeltaTime, ELevelTick
void USpineSkeletonRendererComponent::Flush (int &Idx, TArray<FVector> &Vertices, TArray<int32> &Indices, TArray<FVector2D> &Uvs, TArray<FColor> &Colors, UMaterialInstanceDynamic* Material) {
if (Vertices.Num() == 0) return;
SetMaterial(Idx, Material);
CreateMeshSection(Idx, Vertices, Indices, TArray<FVector>(), Uvs, Colors, TArray<FRuntimeMeshTangent>(), false);
CreateMeshSection(Idx, Vertices, Indices, TArray<FVector>(), Uvs, Colors, TArray<FProcMeshTangent>(), false);
Vertices.SetNum(0);
Indices.SetNum(0);
Uvs.SetNum(0);

View File

@ -3,13 +3,13 @@
#pragma once
#include "Components/ActorComponent.h"
#include "RuntimeMeshComponent.h"
#include "ProceduralMeshComponent.h"
#include "SpineSkeletonAnimationComponent.h"
#include "SpineSkeletonRendererComponent.generated.h"
UCLASS(ClassGroup=(Spine), meta=(BlueprintSpawnableComponent))
class SPINEPLUGIN_API USpineSkeletonRendererComponent: public URuntimeMeshComponent {
class SPINEPLUGIN_API USpineSkeletonRendererComponent: public UProceduralMeshComponent {
GENERATED_BODY()
public:

View File

@ -8,7 +8,7 @@ namespace UnrealBuildTool.Rules
{
PublicIncludePaths.AddRange(new string[] { "SpinePlugin/Public", "SpinePlugin/Public/spine-c/include" });
PrivateIncludePaths.AddRange(new string[] { "SpinePlugin/Private", "SpinePlugin/Public/spine-c/include" });
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "RHI", "RenderCore", "ShaderCore", "RuntimeMeshComponent" });
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "RHI", "RenderCore", "ShaderCore", "ProceduralMeshComponent" });
OptimizeCode = CodeOptimization.Never;
}
}

View File

@ -1,58 +0,0 @@
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
*.ipa
# These project files can be generated by the engine
*.xcodeproj
*.sln
*.suo
*.opensdf
*.sdf
# Precompiled Assets
SourceArt/**/*.png
SourceArt/**/*.tga
# Binary Files
Binaries/*
# Builds
Build/*
# Don't ignore icon files in Build
!Build/**/*.ico
# Configuration files generated by the Editor
Saved/*
# Compiled source files for the engine to use
Intermediate/*
# Cache files for the editor to use
DerivedDataCache/*

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2016 Chris Conway (Koderz)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,49 +0,0 @@
# UE4 Runtime Mesh Component
**Branch with Slicer Support can be found here https://github.com/Koderz/UnrealEngine/tree/RMC_With_Slicer**
**Examples project can be found here https://github.com/Koderz/UE4RuntimeMeshComponentExamples**
**The RuntimeMeshComponent, or RMC for short, is a component designed specifically to support rendering and collision on meshes generated at runtime. This could be anything from voxel engines like Minecraft, to custom model viewers, or just supporting loading user models for things like modding. It has numerous different features to support most of the normal rendering needs of a game, as well as ability to support both static collision for things such as terrain, as well as dynamic collision for things that need to be able to move and bounce around!**
**Now, the RMC is very similar in purpose to the ProceduralMeshComponent or CustomMeshComponent currently found in UE4, but it far surpasses both in features, and efficiency! It on average uses half the memory of the ProceduralMeshComponent, while also being more efficient to render, and far faster to update mesh data. This is shown by the ability to update a 600k+ vertex mesh in real time! The RMC is also nearly 100% compatible with the ProceduralMeshComponent, while adding many features above what the PMC offers.**
*Current list of features in the RMC*
* Slicer Support!! You can now use
* Collision cooking speed improvements.** (new)
* High precision normals support (new)
* Tessellation support (new)
* Navigation mesh support (new)
* Fully configurable vertex structure (new)
* Ability to save individual sections or the entire RMC to disk (new)
* RMC <-> StaticMesHComponent conversions. SMC -> RMC at runtime or in editor. RMC -> SMC in editor. (new)
* Normal/Tangent calculation. (new) (will be getting speed improvements soon)
* Multiple UV channel support (up to 8 channels)
* Fast path updates for meshes that need to update as fast as frame-by-frame
* Static render path for meshes that don't update frequently, this provides a slightly faster rendering performance.
* Collision only mesh sections.
* 50%+ memory reduction over the ProceduralMeshComponent and CustomMeshComponent
* Visibility and shadowing are configurable per section.
* Separate vertex positions for cases of only needing to update the position.
* Collision has lower overhead compared to ProceduralMeshComponent
**The RMC has picked up the collision cooking improvements done in UE4.14. This means that by default you'll see far faster collision updates, but at the cost of a little lower performance collision. You do however have the option to prioritize quality, which will slow down updates, but make actual collision detection a little faster**
**As a part of V2, there has also been some preliminary work done on threaded cooking. This can help to unblock the game thread from collision with large meshes. This is still a very new part, and not heavily tested or complete. To use this you'll have to use a source build of the engine. More information to come.**
**Some requested features that I'm looking into: (These aren't guaranteed to be added)**
* LOD (Potentially with dithering support)
* Dithered transitions for mesh updates.
* Mesh sharing, to allow multiple RMCs to have the same copy of the mesh to reduce memory overhead. This is much like how the StaticMeshComponent works.
* Instancing support.
* Multiple vertex buffer (In Addition to the current separate position vertex buffer)
* Mesh replication
**Supported Engine Versions:**
v1.2 supports engine versions 4.10+
v2.0 supports engine versions 4.12+
*The Runtime Mesh Component should support all UE4 platforms.*
*Collision MAY NOT be available on some platforms (HTML5, Mobile)*

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@ -1,29 +0,0 @@
{
"FileVersion": 3,
"Version": 2,
"VersionName": "2.0",
"FriendlyName": "Runtime Mesh Component",
"Description": "A renderable component with utilities for creating and modifying mesh geometry procedurally at runtime.",
"Category": "Rendering",
"CreatedBy" : "Chris Conway (Koderz)",
"CreatedByURL" : "https://forums.unrealengine.com/member.php?141752-Koderz",
"DocsURL": "https://github.com/Koderz/UE4RuntimeMeshComponent/wiki",
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/content/ad0b45a6a31e462aba7cf7c790a9c125",
"SupportURL": "https://github.com/Koderz/UE4RuntimeMeshComponent/issues",
"EnabledByDefault": true,
"CanContainContent": false,
"IsBetaVersion": false,
"Installed": false,
"Modules": [
{
"Name": "RuntimeMeshComponent",
"Type": "Runtime",
"LoadingPhase": "Default"
},
{
"Name" : "RuntimeMeshComponentEditor",
"Type" : "Editor",
"LoadingPhase" : "Default"
}
]
}

View File

@ -1,38 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#include "RuntimeMeshComponentPluginPrivatePCH.h"
#include "RuntimeMeshVersion.h"
#include "RuntimeMeshComponentPlugin.h"
// Register the custom version with core
FCustomVersionRegistration GRegisterRuntimeMeshCustomVersion(FRuntimeMeshVersion::GUID, FRuntimeMeshVersion::LatestVersion, TEXT("RuntimeMesh"));
class FRuntimeMeshComponentPlugin : public IRuntimeMeshComponentPlugin
{
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
IMPLEMENT_MODULE(FRuntimeMeshComponentPlugin, RuntimeMeshComponent)
void FRuntimeMeshComponentPlugin::StartupModule()
{
}
void FRuntimeMeshComponentPlugin::ShutdownModule()
{
}
DEFINE_LOG_CATEGORY(RuntimeMeshLog);

View File

@ -1,14 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#pragma once
#include "CoreUObject.h"
#include "Engine.h"
#include "RuntimeMeshComponentPlugin.h"
#include "RuntimeMeshComponent.h"
#include "PhysicsEngine/BodySetup.h"

View File

@ -1,51 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#include "RuntimeMeshComponentPluginPrivatePCH.h"
#include "RuntimeMeshCore.h"
FRuntimeMeshVertexTypeRegistrationContainer& FRuntimeMeshVertexTypeRegistrationContainer::GetInstance()
{
static FRuntimeMeshVertexTypeRegistrationContainer Instance;
return Instance;
}
void FRuntimeMeshVertexTypeRegistrationContainer::Register(const FRuntimeMeshVertexTypeInfo* InType)
{
if (auto ExistingRegistration = Registrations.Find(InType->TypeGuid))
{
// This path only exists to support hotreload
// If you hit this then you've probably either:
// * Changed registration details during hotreload.
// * Accidentally copy-and-pasted an FRuntimeMeshVertexTypeRegistration object.
ensureMsgf(ExistingRegistration->TypeInfo->TypeName == InType->TypeName,
TEXT("Runtime mesh vertex registrations cannot change between hotreloads - \"%s\" is being reregistered as \"%s\""),
*ExistingRegistration->TypeInfo->TypeName, *InType->TypeName);
ExistingRegistration->ReferenceCount++;
}
else
{
Registrations.Add(InType->TypeGuid, VertexRegistration(InType));
}
}
void FRuntimeMeshVertexTypeRegistrationContainer::UnRegister(const FRuntimeMeshVertexTypeInfo* InType)
{
auto ExistingRegistration = Registrations.Find(InType->TypeGuid);
check(ExistingRegistration);
ExistingRegistration->ReferenceCount--;
if (ExistingRegistration->ReferenceCount == 0)
{
Registrations.Remove(InType->TypeGuid);
}
}
const FRuntimeMeshVertexTypeInfo* FRuntimeMeshVertexTypeRegistrationContainer::GetVertexType(FGuid Key) const
{
auto Registration = Registrations.Find(Key);
return (Registration ? Registration->TypeInfo : nullptr);
}

View File

@ -1,30 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#include "RuntimeMeshComponentPluginPrivatePCH.h"
#include "RuntimeMeshGenericVertex.h"
#include "RuntimeMeshBuilder.h"
// Finish all the built in vertex types.
DEFINE_RUNTIME_MESH_VERTEX(FRuntimeMeshVertexSimple);
DEFINE_RUNTIME_MESH_VERTEX(FRuntimeMeshVertexDualUV);
DEFINE_RUNTIME_MESH_VERTEX(FRuntimeMeshVertexNoPosition);
DEFINE_RUNTIME_MESH_VERTEX(FRuntimeMeshVertexNoPositionDualUV);
DEFINE_RUNTIME_MESH_VERTEX(FRuntimeMeshVertexHiPrecisionNormals);
DEFINE_RUNTIME_MESH_VERTEX(FRuntimeMeshVertexDualUVHiPrecisionNormals);
DEFINE_RUNTIME_MESH_VERTEX(FRuntimeMeshVertexNoPositionHiPrecisionNormals);
DEFINE_RUNTIME_MESH_VERTEX(FRuntimeMeshVertexNoPositionDualUVHiPrecisionNormals);
const FRuntimeMeshVertexTypeInfo* FRuntimeMeshComponentVerticesBuilder::GetVertexType() const
{
if (HasUVComponent(1))
{
return &FRuntimeMeshVertexDualUV::TypeInfo;
}
else
{
return &FRuntimeMeshVertexSimple::TypeInfo;
}
}

View File

@ -1,561 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#include "RuntimeMeshComponentPluginPrivatePCH.h"
#include "RuntimeMeshLibrary.h"
#include "MessageLog.h"
#include "UObjectToken.h"
#include "StaticMeshResources.h"
#include "TessellationUtilities.h"
#include "RuntimeMeshBuilder.h"
#include "RuntimeMeshComponent.h"
#define LOCTEXT_NAMESPACE "RuntimeMeshLibrary"
URuntimeMeshLibrary::URuntimeMeshLibrary(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
void URuntimeMeshLibrary::ConvertQuadToTriangles(TArray<int32>& Triangles, int32 Vert0, int32 Vert1, int32 Vert2, int32 Vert3)
{
Triangles.Add(Vert0);
Triangles.Add(Vert1);
Triangles.Add(Vert3);
Triangles.Add(Vert1);
Triangles.Add(Vert2);
Triangles.Add(Vert3);
}
void URuntimeMeshLibrary::CreateGridMeshTriangles(int32 NumX, int32 NumY, bool bWinding, TArray<int32>& Triangles)
{
Triangles.Reset();
if (NumX >= 2 && NumY >= 2)
{
// Build Quads
for (int XIdx = 0; XIdx < NumX - 1; XIdx++)
{
for (int YIdx = 0; YIdx < NumY - 1; YIdx++)
{
const int32 I0 = (XIdx + 0)*NumY + (YIdx + 0);
const int32 I1 = (XIdx + 1)*NumY + (YIdx + 0);
const int32 I2 = (XIdx + 1)*NumY + (YIdx + 1);
const int32 I3 = (XIdx + 0)*NumY + (YIdx + 1);
if (bWinding)
{
ConvertQuadToTriangles(Triangles, I0, I1, I2, I3);
}
else
{
ConvertQuadToTriangles(Triangles, I0, I3, I2, I1);
}
}
}
}
}
void URuntimeMeshLibrary::CreateBoxMesh(FVector BoxRadius, TArray<FVector>& Vertices, TArray<int32>& Triangles, TArray<FVector>& Normals, TArray<FVector2D>& UVs, TArray<FRuntimeMeshTangent>& Tangents)
{
// Generate verts
FVector BoxVerts[8];
BoxVerts[0] = FVector(-BoxRadius.X, BoxRadius.Y, BoxRadius.Z);
BoxVerts[1] = FVector(BoxRadius.X, BoxRadius.Y, BoxRadius.Z);
BoxVerts[2] = FVector(BoxRadius.X, -BoxRadius.Y, BoxRadius.Z);
BoxVerts[3] = FVector(-BoxRadius.X, -BoxRadius.Y, BoxRadius.Z);
BoxVerts[4] = FVector(-BoxRadius.X, BoxRadius.Y, -BoxRadius.Z);
BoxVerts[5] = FVector(BoxRadius.X, BoxRadius.Y, -BoxRadius.Z);
BoxVerts[6] = FVector(BoxRadius.X, -BoxRadius.Y, -BoxRadius.Z);
BoxVerts[7] = FVector(-BoxRadius.X, -BoxRadius.Y, -BoxRadius.Z);
// Generate triangles (from quads)
Triangles.Reset();
const int32 NumVerts = 24; // 6 faces x 4 verts per face
Vertices.Reset();
Vertices.AddUninitialized(NumVerts);
Normals.Reset();
Normals.AddUninitialized(NumVerts);
Tangents.Reset();
Tangents.AddUninitialized(NumVerts);
Vertices[0] = BoxVerts[0];
Vertices[1] = BoxVerts[1];
Vertices[2] = BoxVerts[2];
Vertices[3] = BoxVerts[3];
ConvertQuadToTriangles(Triangles, 0, 1, 2, 3);
Normals[0] = Normals[1] = Normals[2] = Normals[3] = FVector(0, 0, 1);
Tangents[0] = Tangents[1] = Tangents[2] = Tangents[3] = FRuntimeMeshTangent(0.f, -1.f, 0.f);
Vertices[4] = BoxVerts[4];
Vertices[5] = BoxVerts[0];
Vertices[6] = BoxVerts[3];
Vertices[7] = BoxVerts[7];
ConvertQuadToTriangles(Triangles, 4, 5, 6, 7);
Normals[4] = Normals[5] = Normals[6] = Normals[7] = FVector(-1, 0, 0);
Tangents[4] = Tangents[5] = Tangents[6] = Tangents[7] = FRuntimeMeshTangent(0.f, -1.f, 0.f);
Vertices[8] = BoxVerts[5];
Vertices[9] = BoxVerts[1];
Vertices[10] = BoxVerts[0];
Vertices[11] = BoxVerts[4];
ConvertQuadToTriangles(Triangles, 8, 9, 10, 11);
Normals[8] = Normals[9] = Normals[10] = Normals[11] = FVector(0, 1, 0);
Tangents[8] = Tangents[9] = Tangents[10] = Tangents[11] = FRuntimeMeshTangent(-1.f, 0.f, 0.f);
Vertices[12] = BoxVerts[6];
Vertices[13] = BoxVerts[2];
Vertices[14] = BoxVerts[1];
Vertices[15] = BoxVerts[5];
ConvertQuadToTriangles(Triangles, 12, 13, 14, 15);
Normals[12] = Normals[13] = Normals[14] = Normals[15] = FVector(1, 0, 0);
Tangents[12] = Tangents[13] = Tangents[14] = Tangents[15] = FRuntimeMeshTangent(0.f, 1.f, 0.f);
Vertices[16] = BoxVerts[7];
Vertices[17] = BoxVerts[3];
Vertices[18] = BoxVerts[2];
Vertices[19] = BoxVerts[6];
ConvertQuadToTriangles(Triangles, 16, 17, 18, 19);
Normals[16] = Normals[17] = Normals[18] = Normals[19] = FVector(0, -1, 0);
Tangents[16] = Tangents[17] = Tangents[18] = Tangents[19] = FRuntimeMeshTangent(1.f, 0.f, 0.f);
Vertices[20] = BoxVerts[7];
Vertices[21] = BoxVerts[6];
Vertices[22] = BoxVerts[5];
Vertices[23] = BoxVerts[4];
ConvertQuadToTriangles(Triangles, 20, 21, 22, 23);
Normals[20] = Normals[21] = Normals[22] = Normals[23] = FVector(0, 0, -1);
Tangents[20] = Tangents[21] = Tangents[22] = Tangents[23] = FRuntimeMeshTangent(0.f, 1.f, 0.f);
// UVs
UVs.Reset();
UVs.AddUninitialized(NumVerts);
UVs[0] = UVs[4] = UVs[8] = UVs[12] = UVs[16] = UVs[20] = FVector2D(0.f, 0.f);
UVs[1] = UVs[5] = UVs[9] = UVs[13] = UVs[17] = UVs[21] = FVector2D(0.f, 1.f);
UVs[2] = UVs[6] = UVs[10] = UVs[14] = UVs[18] = UVs[22] = FVector2D(1.f, 1.f);
UVs[3] = UVs[7] = UVs[11] = UVs[15] = UVs[19] = UVs[23] = FVector2D(1.f, 0.f);
}
void FindVertOverlaps(int32 TestVertIndex, const IRuntimeMeshVerticesBuilder* Vertices, TArray<int32>& VertOverlaps)
{
// Check if Verts is empty or test is outside range
if (TestVertIndex < Vertices->Length())
{
const FVector TestVert = Vertices->GetPosition(TestVertIndex);
for (int32 VertIdx = 0; VertIdx < Vertices->Length(); VertIdx++)
{
// First see if we overlap, and smoothing groups are the same
if (TestVert.Equals(Vertices->GetPosition(VertIdx)))
{
// If it, so we are at least considered an 'overlap' for normal gen
VertOverlaps.Add(VertIdx);
}
}
}
}
void URuntimeMeshLibrary::CalculateTangentsForMesh(IRuntimeMeshVerticesBuilder* Vertices, const FRuntimeMeshIndicesBuilder* Triangles)
{
if (Vertices->Length() == 0) return;
// Number of triangles
const int32 NumTris = Triangles->TriangleLength();
// Number of verts
const int32 NumVerts = Vertices->Length();
// Map of vertex to triangles in Triangles array
TMultiMap<int32, int32> VertToTriMap;
// Map of vertex to triangles to consider for normal calculation
TMultiMap<int32, int32> VertToTriSmoothMap;
// Normal/tangents for each face
TArray<FVector> FaceTangentX, FaceTangentY, FaceTangentZ;
FaceTangentX.AddUninitialized(NumTris);
FaceTangentY.AddUninitialized(NumTris);
FaceTangentZ.AddUninitialized(NumTris);
// Iterate over triangles
for (int TriIdx = 0; TriIdx < NumTris; TriIdx++)
{
int32 CornerIndex[3];
FVector P[3];
for (int32 CornerIdx = 0; CornerIdx < 3; CornerIdx++)
{
// Find vert index (clamped within range)
int32 VertIndex = FMath::Min(Triangles->GetIndex((TriIdx * 3) + CornerIdx), NumVerts - 1);
CornerIndex[CornerIdx] = VertIndex;
P[CornerIdx] = Vertices->GetPosition(VertIndex);
// Find/add this vert to index buffer
TArray<int32> VertOverlaps;
FindVertOverlaps(VertIndex, Vertices, VertOverlaps);
// Remember which triangles map to this vert
VertToTriMap.AddUnique(VertIndex, TriIdx);
VertToTriSmoothMap.AddUnique(VertIndex, TriIdx);
// Also update map of triangles that 'overlap' this vert (ie don't match UV, but do match smoothing) and should be considered when calculating normal
for (int32 OverlapIdx = 0; OverlapIdx < VertOverlaps.Num(); OverlapIdx++)
{
// For each vert we overlap..
int32 OverlapVertIdx = VertOverlaps[OverlapIdx];
// Add this triangle to that vert
VertToTriSmoothMap.AddUnique(OverlapVertIdx, TriIdx);
// And add all of its triangles to us
TArray<int32> OverlapTris;
VertToTriMap.MultiFind(OverlapVertIdx, OverlapTris);
for (int32 OverlapTriIdx = 0; OverlapTriIdx < OverlapTris.Num(); OverlapTriIdx++)
{
VertToTriSmoothMap.AddUnique(VertIndex, OverlapTris[OverlapTriIdx]);
}
}
}
// Calculate triangle edge vectors and normal
const FVector Edge21 = P[1] - P[2];
const FVector Edge20 = P[0] - P[2];
const FVector TriNormal = (Edge21 ^ Edge20).GetSafeNormal();
// If we have UVs, use those to calc
if (Vertices->HasUVComponent(0))
{
const FVector2D T1 = Vertices->GetUV(CornerIndex[0]);
const FVector2D T2 = Vertices->GetUV(CornerIndex[1]);
const FVector2D T3 = Vertices->GetUV(CornerIndex[2]);
FMatrix ParameterToLocal(
FPlane(P[1].X - P[0].X, P[1].Y - P[0].Y, P[1].Z - P[0].Z, 0),
FPlane(P[2].X - P[0].X, P[2].Y - P[0].Y, P[2].Z - P[0].Z, 0),
FPlane(P[0].X, P[0].Y, P[0].Z, 0),
FPlane(0, 0, 0, 1)
);
FMatrix ParameterToTexture(
FPlane(T2.X - T1.X, T2.Y - T1.Y, 0, 0),
FPlane(T3.X - T1.X, T3.Y - T1.Y, 0, 0),
FPlane(T1.X, T1.Y, 1, 0),
FPlane(0, 0, 0, 1)
);
// Use InverseSlow to catch singular matrices. Inverse can miss this sometimes.
const FMatrix TextureToLocal = ParameterToTexture.Inverse() * ParameterToLocal;
FaceTangentX[TriIdx] = TextureToLocal.TransformVector(FVector(1, 0, 0)).GetSafeNormal();
FaceTangentY[TriIdx] = TextureToLocal.TransformVector(FVector(0, 1, 0)).GetSafeNormal();
}
else
{
FaceTangentX[TriIdx] = Edge20.GetSafeNormal();
FaceTangentY[TriIdx] = (FaceTangentX[TriIdx] ^ TriNormal).GetSafeNormal();
}
FaceTangentZ[TriIdx] = TriNormal;
}
// Arrays to accumulate tangents into
TArray<FVector> VertexTangentXSum, VertexTangentYSum, VertexTangentZSum;
VertexTangentXSum.AddZeroed(NumVerts);
VertexTangentYSum.AddZeroed(NumVerts);
VertexTangentZSum.AddZeroed(NumVerts);
// For each vertex..
for (int VertxIdx = 0; VertxIdx < Vertices->Length(); VertxIdx++)
{
// Find relevant triangles for normal
TArray<int32> SmoothTris;
VertToTriSmoothMap.MultiFind(VertxIdx, SmoothTris);
for (int i = 0; i < SmoothTris.Num(); i++)
{
int32 TriIdx = SmoothTris[i];
VertexTangentZSum[VertxIdx] += FaceTangentZ[TriIdx];
}
// Find relevant triangles for tangents
TArray<int32> TangentTris;
VertToTriMap.MultiFind(VertxIdx, TangentTris);
for (int i = 0; i < TangentTris.Num(); i++)
{
int32 TriIdx = TangentTris[i];
VertexTangentXSum[VertxIdx] += FaceTangentX[TriIdx];
VertexTangentYSum[VertxIdx] += FaceTangentY[TriIdx];
}
}
// Finally, normalize tangents and build output arrays
for (int VertxIdx = 0; VertxIdx < NumVerts; VertxIdx++)
{
FVector& TangentX = VertexTangentXSum[VertxIdx];
FVector& TangentY = VertexTangentYSum[VertxIdx];
FVector& TangentZ = VertexTangentZSum[VertxIdx];
TangentX.Normalize();
TangentZ.Normalize();
// Use Gram-Schmidt orthogonalization to make sure X is orth with Z
TangentX -= TangentZ * (TangentZ | TangentX);
TangentX.Normalize();
Vertices->SetTangents(VertxIdx, TangentX, TangentY, TangentZ);
}
}
void URuntimeMeshLibrary::CalculateTangentsForMesh(const TArray<FVector>& Vertices, const TArray<int32>& Triangles, const TArray<FVector2D>& UVs, TArray<FVector>& Normals, TArray<FRuntimeMeshTangent>& Tangents)
{
FRuntimeMeshComponentVerticesBuilder VerticesBuilder(const_cast<TArray<FVector>*>(&Vertices), &Normals, &Tangents, nullptr, const_cast<TArray<FVector2D>*>(&UVs));
FRuntimeMeshIndicesBuilder IndicesBuilder(const_cast<TArray<int32>*>(&Triangles));
CalculateTangentsForMesh(&VerticesBuilder, &IndicesBuilder);
}
void URuntimeMeshLibrary::GenerateTessellationIndexBuffer(const IRuntimeMeshVerticesBuilder* Vertices, const FRuntimeMeshIndicesBuilder* Indices, FRuntimeMeshIndicesBuilder* OutTessellationIndices)
{
TessellationUtilities::CalculateTessellationIndices(Vertices, Indices, OutTessellationIndices);
}
void URuntimeMeshLibrary::GenerateTessellationIndexBuffer(const TArray<FVector>& Vertices, const TArray<int32>& Triangles, const TArray<FVector2D>& UVs, TArray<FVector>& Normals, TArray<FRuntimeMeshTangent>& Tangents, TArray<int32>& OutTessTriangles)
{
FRuntimeMeshComponentVerticesBuilder VerticesBuilder(const_cast<TArray<FVector>*>(&Vertices), &Normals, &Tangents, nullptr, const_cast<TArray<FVector2D>*>(&UVs));
FRuntimeMeshIndicesBuilder IndicesBuilder(const_cast<TArray<int32>*>(&Triangles));
FRuntimeMeshIndicesBuilder OutIndicesBuilder(&OutTessTriangles);
GenerateTessellationIndexBuffer(&VerticesBuilder, &IndicesBuilder, &OutIndicesBuilder);
}
static int32 GetNewIndexForOldVertIndex(int32 MeshVertIndex, TMap<int32, int32>& MeshToSectionVertMap, const FPositionVertexBuffer* PosBuffer, const FStaticMeshVertexBuffer* VertBuffer, const FColorVertexBuffer* ColorBuffer, IRuntimeMeshVerticesBuilder* Vertices)
{
int32* NewIndexPtr = MeshToSectionVertMap.Find(MeshVertIndex);
if (NewIndexPtr != nullptr)
{
return *NewIndexPtr;
}
else
{
// Copy position
int32 SectionVertIndex = Vertices->MoveNextOrAdd();
Vertices->SetPosition(PosBuffer->VertexPosition(MeshVertIndex));
Vertices->SetNormal(VertBuffer->VertexTangentZ(MeshVertIndex));
Vertices->SetTangent(VertBuffer->VertexTangentX(MeshVertIndex));
if (ColorBuffer && ColorBuffer->GetNumVertices())
{
Vertices->SetColor(ColorBuffer->VertexColor(MeshVertIndex));
}
// copy all uv channels
for (uint32 Index = 0; Index < VertBuffer->GetNumTexCoords(); Index++)
{
Vertices->SetUV(Index, VertBuffer->GetVertexUV(MeshVertIndex, Index));
}
MeshToSectionVertMap.Add(MeshVertIndex, SectionVertIndex);
return SectionVertIndex;
}
}
void URuntimeMeshLibrary::GetSectionFromStaticMesh(UStaticMesh* InMesh, int32 LODIndex, int32 SectionIndex,
IRuntimeMeshVerticesBuilder* Vertices, FRuntimeMeshIndicesBuilder* Triangles, FRuntimeMeshIndicesBuilder* AdjacencyTriangles)
{
if (InMesh)
{
#if !WITH_EDITOR && (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 13)
if (!InMesh->bAllowCPUAccess)
{
FMessageLog("PIE").Warning()
->AddToken(FTextToken::Create(LOCTEXT("GetSectionFromStaticMeshStart", "Calling GetSectionFromStaticMesh on")))
->AddToken(FUObjectToken::Create(InMesh))
->AddToken(FTextToken::Create(LOCTEXT("GetSectionFromStaticMeshEnd", "but 'Allow CPU Access' is not enabled. This is required for converting StaticMesh to RuntimeMeshComponent in cooked builds.")));
}
else
#endif
#if WITH_EDITOR || (ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 13)
if (InMesh->RenderData != nullptr && InMesh->RenderData->LODResources.IsValidIndex(LODIndex))
{
const FStaticMeshLODResources& LOD = InMesh->RenderData->LODResources[LODIndex];
if (LOD.Sections.IsValidIndex(SectionIndex))
{
// Empty output buffers
Vertices->Reset();
Triangles->Reset();
// Map from vert buffer for whole mesh to vert buffer for section of interest
TMap<int32, int32> MeshToSectionVertMap;
const FStaticMeshSection& Section = LOD.Sections[SectionIndex];
const uint32 OnePastLastIndex = Section.FirstIndex + Section.NumTriangles * 3;
FIndexArrayView Indices = LOD.IndexBuffer.GetArrayView();
// Iterate over section index buffer, copying verts as needed
for (uint32 i = Section.FirstIndex; i < OnePastLastIndex; i++)
{
uint32 MeshVertIndex = Indices[i];
// See if we have this vert already in our section vert buffer, and copy vert in if not
int32 SectionVertIndex = GetNewIndexForOldVertIndex(MeshVertIndex, MeshToSectionVertMap, &LOD.PositionVertexBuffer, &LOD.VertexBuffer, &LOD.ColorVertexBuffer, Vertices);
// Add to index buffer
Triangles->AddIndex(SectionVertIndex);
}
if (AdjacencyTriangles != nullptr)
{
AdjacencyTriangles->Reset();
// Adjacency indices use 12 per triangle instead of 3. So start position and length both need to be multiplied by 4
const uint32 SectionAdjacencyFirstIndex = Section.FirstIndex * 4;
const uint32 SectionAdjacencyOnePastLastIndex = SectionAdjacencyFirstIndex + Section.NumTriangles * (3 * 4);
FIndexArrayView AdjacencyIndices = LOD.AdjacencyIndexBuffer.GetArrayView();
// Iterate over section adjacency index buffer, copying any new verts as needed
for (uint32 i = SectionAdjacencyFirstIndex; i < SectionAdjacencyOnePastLastIndex; i++)
{
uint32 MeshVertIndex = AdjacencyIndices[i];
// See if we have this vert already in our section vert buffer, and copy vert in if not
int32 SectionVertIndex = GetNewIndexForOldVertIndex(MeshVertIndex, MeshToSectionVertMap, &LOD.PositionVertexBuffer, &LOD.VertexBuffer, &LOD.ColorVertexBuffer, Vertices);
// Add to index buffer
AdjacencyTriangles->AddIndex(SectionVertIndex);
}
}
}
}
#endif
}
}
void URuntimeMeshLibrary::GetSectionFromStaticMesh(UStaticMesh* InMesh, int32 LODIndex, int32 SectionIndex, TArray<FVector>& Vertices,
TArray<int32>& Triangles, TArray<FVector>& Normals, TArray<FVector2D>& UVs, TArray<FRuntimeMeshTangent>& Tangents)
{
FRuntimeMeshComponentVerticesBuilder VerticesBuilder(&Vertices, &Normals, &Tangents, nullptr, &UVs);
FRuntimeMeshIndicesBuilder IndicesBuilder(&Triangles);
GetSectionFromStaticMesh(InMesh, LODIndex, SectionIndex, &VerticesBuilder, &IndicesBuilder, nullptr);
}
void URuntimeMeshLibrary::CopyRuntimeMeshFromStaticMeshComponent(UStaticMeshComponent* StaticMeshComp, int32 LODIndex,
URuntimeMeshComponent* RuntimeMeshComp, bool bShouldCreateCollision)
{
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
UStaticMesh* StaticMesh = StaticMeshComp->GetStaticMesh();
#else
UStaticMesh* StaticMesh = StaticMeshComp->StaticMesh;
#endif
if (StaticMeshComp != nullptr && StaticMesh != nullptr && RuntimeMeshComp != nullptr)
{
//// MESH DATA
// Make sure LOD index is valid
if (StaticMesh->RenderData == nullptr || !StaticMesh->RenderData->LODResources.IsValidIndex(LODIndex))
{
return;
}
// Get specified LOD
const FStaticMeshLODResources& LOD = StaticMesh->RenderData->LODResources[LODIndex];
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 13
int32 NumSections = StaticMesh->GetNumSections(LODIndex);
#else
int32 NumSections = LOD.Sections.Num();
#endif
for (int32 SectionIndex = 0; SectionIndex < NumSections; SectionIndex++)
{
// Buffers for copying geom data
if (LOD.Sections.IsValidIndex(SectionIndex))
{
int32 NumUVChannels = LOD.GetNumTexCoords();
FRuntimeMeshIndicesBuilder AdjacencyTriangles;
if (NumUVChannels <= 1)
{
FRuntimeMeshPackedVerticesBuilder<FRuntimeMeshVertexSimple> Vertices;
FRuntimeMeshIndicesBuilder Triangles;
// Get geom data from static mesh
GetSectionFromStaticMesh(StaticMesh, LODIndex, SectionIndex, &Vertices, &Triangles, &AdjacencyTriangles);
// Create section using data
RuntimeMeshComp->CreateMeshSection(SectionIndex, *Vertices.GetVertices(), *Triangles.GetIndices(),
bShouldCreateCollision, EUpdateFrequency::Infrequent, ESectionUpdateFlags::MoveArrays);
}
else
{
FRuntimeMeshPackedVerticesBuilder<FRuntimeMeshVertexDualUV> Vertices;
FRuntimeMeshIndicesBuilder Triangles;
// Get geom data from static mesh
GetSectionFromStaticMesh(StaticMesh, LODIndex, SectionIndex, &Vertices, &Triangles, &AdjacencyTriangles);
// Create section using data
RuntimeMeshComp->CreateMeshSection(SectionIndex, *Vertices.GetVertices(), *Triangles.GetIndices(),
bShouldCreateCollision, EUpdateFrequency::Infrequent, ESectionUpdateFlags::MoveArrays);
}
}
}
//// SIMPLE COLLISION
// Clear any existing collision hulls
RuntimeMeshComp->ClearCollisionConvexMeshes();
if (StaticMesh->BodySetup != nullptr)
{
// Iterate over all convex hulls on static mesh..
const int32 NumConvex = StaticMesh->BodySetup->AggGeom.ConvexElems.Num();
for (int ConvexIndex = 0; ConvexIndex < NumConvex; ConvexIndex++)
{
// Copy convex verts to ProcMesh
FKConvexElem& MeshConvex = StaticMesh->BodySetup->AggGeom.ConvexElems[ConvexIndex];
RuntimeMeshComp->AddCollisionConvexMesh(MeshConvex.VertexData);
}
}
//// MATERIALS
for (int32 MatIndex = 0; MatIndex < StaticMeshComp->GetNumMaterials(); MatIndex++)
{
RuntimeMeshComp->SetMaterial(MatIndex, StaticMeshComp->GetMaterial(MatIndex));
}
}
}
#undef LOCTEXT_NAMESPACE

View File

@ -1,156 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#include "RuntimeMeshComponentPluginPrivatePCH.h"
#include "TessellationUtilities.h"
const uint32 EdgesPerTriangle = 3;
const uint32 IndicesPerTriangle = 3;
const uint32 VerticesPerTriangle = 3;
const uint32 DuplicateIndexCount = 3;
const uint32 PnAenDomCorner_IndicesPerPatch = 12;
void TessellationUtilities::AddIfLeastUV(PositionDictionary& PosDict, const Vertex& Vert, uint32 Index)
{
auto* Pos = PosDict.Find(Vert.Position);
if (Pos == nullptr)
{
PosDict.Add(Vert.Position, Corner(Index, Vert.TexCoord));
}
else if (Vert.TexCoord < Pos->TexCoord)
{
PosDict[Vert.Position] = Corner(Index, Vert.TexCoord);
}
}
void TessellationUtilities::CalculateTessellationIndices(const IRuntimeMeshVerticesBuilder* Vertices, const FRuntimeMeshIndicesBuilder* Indices, FRuntimeMeshIndicesBuilder* TessellationIndices)
{
EdgeDictionary EdgeDict;
EdgeDict.Reserve(Indices->Length());
PositionDictionary PosDict;
PosDict.Reserve(Indices->Length());
TessellationIndices->Reset(PnAenDomCorner_IndicesPerPatch * Indices->Length() / IndicesPerTriangle);
ExpandIB(Vertices, Indices, EdgeDict, PosDict, TessellationIndices);
ReplacePlaceholderIndices(Vertices, Indices, EdgeDict, PosDict, TessellationIndices);
}
void TessellationUtilities::ExpandIB(const IRuntimeMeshVerticesBuilder* Vertices, const FRuntimeMeshIndicesBuilder* Indices,
EdgeDictionary& OutEdgeDict, PositionDictionary& OutPosDict, FRuntimeMeshIndicesBuilder* OutIndices)
{
const uint32 TriangleCount = Indices->Length() / IndicesPerTriangle;
for (uint32 U = 0; U < TriangleCount; U++)
{
const uint32 StartInIndex = U * IndicesPerTriangle;
const uint32 StartOutIndex = U * PnAenDomCorner_IndicesPerPatch;
Indices->Seek(StartInIndex);
const uint32 Index0 = Indices->ReadOne();
const uint32 Index1 = Indices->ReadOne();
const uint32 Index2 = Indices->ReadOne();
Vertices->Seek(Index0);
const Vertex Vertex0(Vertices->GetPosition(), Vertices->GetUV(0));
Vertices->Seek(Index1);
const Vertex Vertex1(Vertices->GetPosition(), Vertices->GetUV(0));
Vertices->Seek(Index2);
const Vertex Vertex2(Vertices->GetPosition(), Vertices->GetUV(0));
Triangle Tri(Index0, Index1, Index2, Vertex0, Vertex1, Vertex2);
OutIndices->Seek(StartOutIndex);
OutIndices->AddTriangle(Tri.GetIndex(0), Tri.GetIndex(1), Tri.GetIndex(2));
OutIndices->AddIndex(Tri.GetIndex(0));
OutIndices->AddIndex(Tri.GetIndex(1));
OutIndices->AddIndex(Tri.GetIndex(1));
OutIndices->AddIndex(Tri.GetIndex(2));
OutIndices->AddIndex(Tri.GetIndex(2));
OutIndices->AddIndex(Tri.GetIndex(0));
OutIndices->AddTriangle(Tri.GetIndex(0), Tri.GetIndex(1), Tri.GetIndex(2));
Edge Rev0 = Tri.GetEdge(0).GetReverse();
Edge Rev1 = Tri.GetEdge(1).GetReverse();
Edge Rev2 = Tri.GetEdge(2).GetReverse();
OutEdgeDict.Add(Rev0, Rev0);
OutEdgeDict.Add(Rev1, Rev1);
OutEdgeDict.Add(Rev2, Rev2);
AddIfLeastUV(OutPosDict, Vertex0, Index0);
AddIfLeastUV(OutPosDict, Vertex1, Index1);
AddIfLeastUV(OutPosDict, Vertex2, Index2);
}
}
void TessellationUtilities::ReplacePlaceholderIndices(const IRuntimeMeshVerticesBuilder* Vertices, const FRuntimeMeshIndicesBuilder* Indices,
EdgeDictionary& EdgeDict, PositionDictionary& PosDict, FRuntimeMeshIndicesBuilder* OutIndices)
{
const uint32 TriangleCount = Indices->Length() / PnAenDomCorner_IndicesPerPatch;
for (uint32 U = 0; U < TriangleCount; U++)
{
const uint32 StartOutIndex = U * PnAenDomCorner_IndicesPerPatch;
OutIndices->Seek(StartOutIndex);
const uint32 Index0 = OutIndices->ReadOne();
const uint32 Index1 = OutIndices->ReadOne();
const uint32 Index2 = OutIndices->ReadOne();
Vertices->Seek(Index0);
const Vertex Vertex0(Vertices->GetPosition(), Vertices->GetUV(0));
Vertices->Seek(Index1);
const Vertex Vertex1(Vertices->GetPosition(), Vertices->GetUV(0));
Vertices->Seek(Index2);
const Vertex Vertex2(Vertices->GetPosition(), Vertices->GetUV(0));
Triangle Tri(Index0, Index1, Index2, Vertex0, Vertex1, Vertex2);
Edge* Ed = EdgeDict.Find(Tri.GetEdge(0));
if (Ed != nullptr)
{
OutIndices->Seek(StartOutIndex + 3);
OutIndices->AddIndex(Ed->GetIndex(0));
OutIndices->AddIndex(Ed->GetIndex(1));
}
Ed = EdgeDict.Find(Tri.GetEdge(1));
if (Ed != nullptr)
{
OutIndices->Seek(StartOutIndex + 5);
OutIndices->AddIndex(Ed->GetIndex(0));
OutIndices->AddIndex(Ed->GetIndex(1));
}
Ed = EdgeDict.Find(Tri.GetEdge(2));
if (Ed != nullptr)
{
OutIndices->Seek(StartOutIndex + 7);
OutIndices->AddIndex(Ed->GetIndex(0));
OutIndices->AddIndex(Ed->GetIndex(1));
}
// Deal with dominant positions.
for (uint32 V = 0; V < VerticesPerTriangle; V++)
{
Corner* Corn = PosDict.Find(Tri.GetEdge(V).GetVertex(0).Position);
if (Corn != nullptr)
{
OutIndices->Seek(StartOutIndex + 9 + V);
OutIndices->AddIndex(Corn->Index);
}
}
}
}

View File

@ -1,178 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#pragma once
#include "RuntimeMeshBuilder.h"
/**
*
*/
class TessellationUtilities
{
public:
static void CalculateTessellationIndices(const IRuntimeMeshVerticesBuilder* Vertices, const FRuntimeMeshIndicesBuilder* Indices, FRuntimeMeshIndicesBuilder* TessellationIndices);
private:
struct Vertex
{
FVector Position;
FVector2D TexCoord;
Vertex() { }
Vertex(const FVector& InPosition, const FVector2D& InTexCoord)
: Position(InPosition), TexCoord(InTexCoord)
{ }
FORCEINLINE bool operator==(const Vertex& Other) const
{
return Position == Other.Position;
}
FORCEINLINE bool operator<(const Vertex& Other) const
{
return Position.X < Other.Position.X
|| Position.Y < Other.Position.Y
|| Position.Z < Other.Position.Z;
}
};
static FORCEINLINE uint32 HashValue(const FVector& Vec)
{
return 31337 * GetTypeHash(Vec.X) + 13 * GetTypeHash(Vec.Y) + 3 * GetTypeHash(Vec.Z);
}
static FORCEINLINE uint32 HashValue(const Vertex& Vert)
{
return HashValue(Vert.Position);
}
struct Edge
{
private:
uint32 IndexFrom;
uint32 IndexTo;
Vertex VertexFrom;
Vertex VertexTo;
uint32 CachedHash;
public:
Edge() : CachedHash(0) { }
Edge(uint32 InIndexFrom, uint32 InIndexTo, const Vertex& InVertexFrom, const Vertex& InVertexTo)
: IndexFrom(InIndexFrom), IndexTo(InIndexTo), VertexFrom(InVertexFrom), VertexTo(InVertexTo)
{
// Hash should only consider position, not index.
// We want values with different indices to compare true.
CachedHash = 7 * HashValue(VertexFrom) + 2 * HashValue(VertexTo);
}
Vertex GetVertex(uint32 I) const
{
switch (I)
{
case 0:
return VertexFrom;
case 1:
return VertexTo;
default:
checkNoEntry();
return Vertex();
}
}
uint32 GetIndex(uint32 I) const
{
switch (I)
{
case 0:
return IndexFrom;
case 1:
return IndexTo;
default:
checkNoEntry();
return 0;
}
}
Edge GetReverse() const
{
return Edge(IndexTo, IndexFrom, VertexTo, VertexFrom);
}
FORCEINLINE bool operator<(const Edge& Other) const
{
// Quick out, otherwise we have to compare vertices
if (IndexFrom == Other.IndexFrom && IndexTo == Other.IndexTo)
{
return false;
}
return VertexFrom < Other.VertexFrom || VertexTo < Other.VertexTo;
}
FORCEINLINE bool operator==(const Edge& Other) const
{
return (IndexFrom == Other.IndexFrom && IndexTo == Other.IndexTo) ||
(VertexFrom == Other.VertexFrom && VertexTo == Other.VertexTo);
}
friend FORCEINLINE uint32 GetTypeHash(const Edge& E)
{
return E.CachedHash;
}
};
struct Corner
{
uint32 Index;
FVector2D TexCoord;
Corner() : Index(0) { }
Corner(uint32 InIndex, FVector2D InTexCoord)
: Index(InIndex), TexCoord(InTexCoord)
{ }
};
class Triangle
{
Edge Edge0;
Edge Edge1;
Edge Edge2;
public:
Triangle(uint32 Index0, uint32 Index1, uint32 Index2, const Vertex& Vertex0, const Vertex& Vertex1, const Vertex& Vertex2)
: Edge0(Index0, Index1, Vertex0, Vertex1)
, Edge1(Index1, Index2, Vertex1, Vertex2)
, Edge2(Index2, Index0, Vertex2, Vertex0)
{ }
FORCEINLINE bool operator<(const Triangle& Other) const
{
return Edge0 < Other.Edge0 || Edge1 < Other.Edge1 || Edge2 < Other.Edge2;
}
FORCEINLINE const Edge& GetEdge(uint32 I)
{
return ((Edge*)&Edge0)[I];
}
FORCEINLINE uint32 GetIndex(uint32 I)
{
return GetEdge(I).GetIndex(0);
}
};
using EdgeDictionary = TMap<Edge, Edge>;
using PositionDictionary = TMap<FVector, Corner>;
static void AddIfLeastUV(PositionDictionary& PosDict, const Vertex& Vert, uint32 Index);
static void ReplacePlaceholderIndices(const IRuntimeMeshVerticesBuilder* Vertices, const FRuntimeMeshIndicesBuilder* Indices,
EdgeDictionary& EdgeDict, PositionDictionary& PosDict, FRuntimeMeshIndicesBuilder* OutIndices);
static void ExpandIB(const IRuntimeMeshVerticesBuilder* Vertices, const FRuntimeMeshIndicesBuilder* Indices,
EdgeDictionary& OutEdgeDict, PositionDictionary& OutPosDict, FRuntimeMeshIndicesBuilder* OutIndices);
};

View File

@ -1,983 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#pragma once
#include "RuntimeMeshCore.h"
#include "RuntimeMeshGenericVertex.h"
//////////////////////////////////////////////////////////////////////////
// This is a work in progress, it's functional, but could use some improvement
//////////////////////////////////////////////////////////////////////////
enum class ERuntimeMeshVerticesBuilderType : uint8
{
Component,
Packed
};
class IRuntimeMeshVerticesBuilder
{
public:
IRuntimeMeshVerticesBuilder() { }
IRuntimeMeshVerticesBuilder(const IRuntimeMeshVerticesBuilder& Other) = delete;
IRuntimeMeshVerticesBuilder& operator=(const IRuntimeMeshVerticesBuilder& Other) = delete;
virtual ~IRuntimeMeshVerticesBuilder() { }
virtual ERuntimeMeshVerticesBuilderType GetBuilderType() const = 0;
virtual const FRuntimeMeshVertexTypeInfo* GetVertexType() const = 0;
virtual bool HasPositionComponent() const = 0;
virtual bool HasNormalComponent() const = 0;
virtual bool HasTangentComponent() const = 0;
virtual bool HasColorComponent() const = 0;
virtual bool HasUVComponent(int32 Index) const = 0;
virtual bool HasHighPrecisionNormals() const = 0;
virtual bool HasHighPrecisionUVs() const = 0;
virtual void SetPosition(const FVector& InPosition) = 0;
virtual void SetNormal(const FVector4& InNormal) = 0;
virtual void SetTangent(const FVector& InTangent) = 0;
virtual void SetColor(const FColor& InColor) = 0;
virtual void SetUV(int32 Index, const FVector2D& InUV) = 0;
virtual void SetPosition(int32 VertexIndex, const FVector& InPosition) = 0;
virtual void SetNormal(int32 VertexIndex, const FVector4& InNormal) = 0;
virtual void SetTangent(int32 VertexIndex, const FVector& InTangent) = 0;
virtual void SetColor(int32 VertexIndex, const FColor& InColor) = 0;
virtual void SetUV(int32 VertexIndex, int32 Index, const FVector2D& InUV) = 0;
void SetTangents(const FVector& InTangentX, const FVector& InTangentY, const FVector& InTangentZ)
{
SetNormal(FVector4(InTangentZ, GetBasisDeterminantSign(InTangentX, InTangentY, InTangentZ)));
SetTangent(InTangentX);
}
void SetTangents(int32 VertexIndex, const FVector& InTangentX, const FVector& InTangentY, const FVector& InTangentZ)
{
Seek(VertexIndex);
SetNormal(FVector4(InTangentZ, GetBasisDeterminantSign(InTangentX, InTangentY, InTangentZ)));
SetTangent(InTangentX);
}
virtual FVector GetPosition() const = 0;
virtual FVector4 GetNormal() const = 0;
virtual FVector GetTangent() const = 0;
virtual FColor GetColor() const = 0;
virtual FVector2D GetUV(int32 Index) const = 0;
virtual FVector GetPosition(int32 VertexIndex) const = 0;
virtual FVector4 GetNormal(int32 VertexIndex) const = 0;
virtual FVector GetTangent(int32 VertexIndex) const = 0;
virtual FColor GetColor(int32 VertexIndex) const = 0;
virtual FVector2D GetUV(int32 VertexIndex, int32 Index) const = 0;
virtual int32 Length() const = 0;
virtual void Seek(int32 Position) const = 0;
void SeekEnd() const
{
Seek(Length() - 1);
}
virtual int32 MoveNext() const = 0;
virtual int32 MoveNextOrAdd() = 0;
virtual void Reset() = 0;
virtual IRuntimeMeshVerticesBuilder* Clone(bool bIncludeData = true) const = 0;
virtual bool WantsSeparatePositionBuffer() const
{
return false;
}
};
template<typename VertexType>
class FRuntimeMeshPackedVerticesBuilder : public IRuntimeMeshVerticesBuilder
{
private:
TArray<VertexType>* Vertices;
TArray<FVector>* Positions;
int32 CurrentPosition;
bool bOwnsVertexArray;
public:
FRuntimeMeshPackedVerticesBuilder(bool bWantsSeparatePositions = false)
: Vertices(new TArray<VertexType>())
, Positions(bWantsSeparatePositions? new TArray<FVector>() : nullptr)
, CurrentPosition(-1)
, bOwnsVertexArray(true)
{ }
FRuntimeMeshPackedVerticesBuilder(TArray<VertexType>* InVertices, TArray<FVector>* InPositions = nullptr)
: Vertices(InVertices)
, Positions(InPositions)
, CurrentPosition(-1)
, bOwnsVertexArray(false)
{ }
FRuntimeMeshPackedVerticesBuilder(const FRuntimeMeshPackedVerticesBuilder& Other) = delete;
FRuntimeMeshPackedVerticesBuilder& operator=(const FRuntimeMeshPackedVerticesBuilder& Other) = delete;
virtual ~FRuntimeMeshPackedVerticesBuilder() override
{
if (bOwnsVertexArray)
{
delete Vertices;
if (Positions)
{
delete Positions;
}
}
}
virtual ERuntimeMeshVerticesBuilderType GetBuilderType() const
{
return ERuntimeMeshVerticesBuilderType::Packed;
}
virtual const FRuntimeMeshVertexTypeInfo* GetVertexType() const
{
return &VertexType::TypeInfo;
}
virtual bool HasPositionComponent() const override { return Positions != nullptr || FRuntimeMeshVertexTraits<VertexType>::HasPosition; }
virtual bool HasNormalComponent() const override { return FRuntimeMeshVertexTraits<VertexType>::HasNormal; }
virtual bool HasTangentComponent() const override { return FRuntimeMeshVertexTraits<VertexType>::HasTangent; }
virtual bool HasColorComponent() const override { return FRuntimeMeshVertexTraits<VertexType>::HasColor; }
virtual bool HasUVComponent(int32 Index) const override
{
switch (Index)
{
case 0:
return FRuntimeMeshVertexTraits<VertexType>::HasUV0;
case 1:
return FRuntimeMeshVertexTraits<VertexType>::HasUV1;
case 2:
return FRuntimeMeshVertexTraits<VertexType>::HasUV2;
case 3:
return FRuntimeMeshVertexTraits<VertexType>::HasUV3;
case 4:
return FRuntimeMeshVertexTraits<VertexType>::HasUV4;
case 5:
return FRuntimeMeshVertexTraits<VertexType>::HasUV5;
case 6:
return FRuntimeMeshVertexTraits<VertexType>::HasUV6;
case 7:
return FRuntimeMeshVertexTraits<VertexType>::HasUV7;
}
return false;
}
virtual bool HasHighPrecisionNormals() const override { return FRuntimeMeshVertexTraits<VertexType>::HasHighPrecisionNormals; }
virtual bool HasHighPrecisionUVs() const override { return FRuntimeMeshVertexTraits<VertexType>::HasHighPrecisionUVs; }
virtual void SetPosition(const FVector& InPosition) override
{
if (Positions)
{
(*Positions)[CurrentPosition] = InPosition;
}
else
{
SetPositionInternal<VertexType>((*Vertices)[CurrentPosition], InPosition);
}
}
virtual void SetNormal(const FVector4& InNormal) override { SetNormalInternal<VertexType>((*Vertices)[CurrentPosition], InNormal); }
virtual void SetTangent(const FVector& InTangent) override { SetTangentInternal<VertexType>((*Vertices)[CurrentPosition], InTangent); }
virtual void SetColor(const FColor& InColor) override { SetColorInternal<VertexType>((*Vertices)[CurrentPosition], InColor); }
virtual void SetUV(int32 Index, const FVector2D& InUV)
{
switch (Index)
{
case 0:
SetUV0Internal<VertexType>((*Vertices)[CurrentPosition], InUV);
return;
case 1:
SetUV1Internal<VertexType>((*Vertices)[CurrentPosition], InUV);
return;
case 2:
SetUV2Internal<VertexType>((*Vertices)[CurrentPosition], InUV);
return;
case 3:
SetUV3Internal<VertexType>((*Vertices)[CurrentPosition], InUV);
return;
case 4:
SetUV4Internal<VertexType>((*Vertices)[CurrentPosition], InUV);
return;
case 5:
SetUV5Internal<VertexType>((*Vertices)[CurrentPosition], InUV);
return;
case 6:
SetUV6Internal<VertexType>((*Vertices)[CurrentPosition], InUV);
return;
case 7:
SetUV7Internal<VertexType>((*Vertices)[CurrentPosition], InUV);
return;
}
}
virtual void SetPosition(int32 VertexIndex, const FVector& InPosition) override
{
Seek(VertexIndex);
if (Positions)
{
(*Positions)[CurrentPosition] = InPosition;
}
else
{
SetPositionInternal<VertexType>((*Vertices)[CurrentPosition], InPosition);
}
}
virtual void SetNormal(int32 VertexIndex, const FVector4& InNormal) override { Seek(VertexIndex); SetNormalInternal<VertexType>((*Vertices)[CurrentPosition], InNormal); }
virtual void SetTangent(int32 VertexIndex, const FVector& InTangent) override { Seek(VertexIndex); SetTangentInternal<VertexType>((*Vertices)[CurrentPosition], InTangent); }
virtual void SetColor(int32 VertexIndex, const FColor& InColor) override { Seek(VertexIndex); SetColorInternal<VertexType>((*Vertices)[CurrentPosition], InColor); }
virtual void SetUV(int32 VertexIndex, int32 Index, const FVector2D& InUV)
{
Seek(VertexIndex);
switch (Index)
{
case 0:
SetUV0Internal<VertexType>((*Vertices)[CurrentPosition], InUV);
return;
case 1:
SetUV1Internal<VertexType>((*Vertices)[CurrentPosition], InUV);
return;
case 2:
SetUV2Internal<VertexType>((*Vertices)[CurrentPosition], InUV);
return;
case 3:
SetUV3Internal<VertexType>((*Vertices)[CurrentPosition], InUV);
return;
case 4:
SetUV4Internal<VertexType>((*Vertices)[CurrentPosition], InUV);
return;
case 5:
SetUV5Internal<VertexType>((*Vertices)[CurrentPosition], InUV);
return;
case 6:
SetUV6Internal<VertexType>((*Vertices)[CurrentPosition], InUV);
return;
case 7:
SetUV7Internal<VertexType>((*Vertices)[CurrentPosition], InUV);
return;
}
}
virtual FVector GetPosition() const override
{
if (Positions)
{
return (*Positions)[CurrentPosition];
}
else
{
return GetPositionInternal<VertexType>((*Vertices)[CurrentPosition]);
}
}
virtual FVector4 GetNormal() const override { return GetNormalInternal<VertexType>((*Vertices)[CurrentPosition]); }
virtual FVector GetTangent() const override { return GetTangentInternal<VertexType>((*Vertices)[CurrentPosition]); }
virtual FColor GetColor() const override { return GetColorInternal<VertexType>((*Vertices)[CurrentPosition]); }
virtual FVector2D GetUV(int32 Index) const override
{
switch (Index)
{
case 0:
return GetUV0Internal<VertexType>((*Vertices)[CurrentPosition]);
case 1:
return GetUV1Internal<VertexType>((*Vertices)[CurrentPosition]);
case 2:
return GetUV2Internal<VertexType>((*Vertices)[CurrentPosition]);
case 3:
return GetUV3Internal<VertexType>((*Vertices)[CurrentPosition]);
case 4:
return GetUV4Internal<VertexType>((*Vertices)[CurrentPosition]);
case 5:
return GetUV5Internal<VertexType>((*Vertices)[CurrentPosition]);
case 6:
return GetUV6Internal<VertexType>((*Vertices)[CurrentPosition]);
case 7:
return GetUV7Internal<VertexType>((*Vertices)[CurrentPosition]);
}
return FVector2D::ZeroVector;
}
virtual FVector GetPosition(int32 VertexIndex) const override
{
Seek(VertexIndex);
if (Positions)
{
return (*Positions)[CurrentPosition];
}
else
{
return GetPositionInternal<VertexType>((*Vertices)[CurrentPosition]);
}
}
virtual FVector4 GetNormal(int32 VertexIndex) const override { Seek(VertexIndex); return GetNormalInternal<VertexType>((*Vertices)[CurrentPosition]); }
virtual FVector GetTangent(int32 VertexIndex) const override { Seek(VertexIndex); return GetTangentInternal<VertexType>((*Vertices)[CurrentPosition]); }
virtual FColor GetColor(int32 VertexIndex) const override { Seek(VertexIndex); return GetColorInternal<VertexType>((*Vertices)[CurrentPosition]); }
virtual FVector2D GetUV(int32 VertexIndex, int32 Index) const override
{
Seek(VertexIndex);
switch (Index)
{
case 0:
return GetUV0Internal<VertexType>((*Vertices)[CurrentPosition]);
case 1:
return GetUV1Internal<VertexType>((*Vertices)[CurrentPosition]);
case 2:
return GetUV2Internal<VertexType>((*Vertices)[CurrentPosition]);
case 3:
return GetUV3Internal<VertexType>((*Vertices)[CurrentPosition]);
case 4:
return GetUV4Internal<VertexType>((*Vertices)[CurrentPosition]);
case 5:
return GetUV5Internal<VertexType>((*Vertices)[CurrentPosition]);
case 6:
return GetUV6Internal<VertexType>((*Vertices)[CurrentPosition]);
case 7:
return GetUV7Internal<VertexType>((*Vertices)[CurrentPosition]);
}
return FVector2D::ZeroVector;
}
virtual int32 Length() const override { return Vertices->Num(); }
virtual void Seek(int32 Position) const override
{
const_cast<FRuntimeMeshPackedVerticesBuilder<VertexType>*>(this)->CurrentPosition = Position;
}
virtual int32 MoveNext() const override
{
return ++const_cast<FRuntimeMeshPackedVerticesBuilder<VertexType>*>(this)->CurrentPosition;
}
virtual int32 MoveNextOrAdd() override
{
CurrentPosition++;
if (CurrentPosition >= Vertices->Num())
{
Vertices->SetNumZeroed(CurrentPosition + 1, false);
if (Positions)
{
Positions->SetNumZeroed(CurrentPosition + 1, false);
}
}
return CurrentPosition;
}
virtual void Reset() override
{
Vertices->Reset();
if (Positions)
{
Positions->Reset();
}
CurrentPosition = -1;
}
virtual IRuntimeMeshVerticesBuilder* Clone(bool bIncludeData = true) const override
{
FRuntimeMeshPackedVerticesBuilder<VertexType>* NewBuilder = new FRuntimeMeshPackedVerticesBuilder(Positions != nullptr);
if (bIncludeData)
{
*NewBuilder->Vertices = *Vertices;
*NewBuilder->Positions = *Positions;
NewBuilder->Seek(0);
}
return NewBuilder;
}
TArray<VertexType>* GetVertices()
{
return Vertices;
}
TArray<FVector>* GetPositions()
{
return Positions;
}
virtual bool WantsSeparatePositionBuffer() const
{
return Positions != nullptr;
}
private:
template<typename Type>
FORCEINLINE static typename TEnableIf<FRuntimeMeshVertexTraits<Type>::HasPosition>::Type SetPositionInternal(Type& Vertex, const FVector& Position)
{
Vertex.Position = Position;
}
template<typename Type>
FORCEINLINE static typename TEnableIf<!FRuntimeMeshVertexTraits<Type>::HasPosition>::Type SetPositionInternal(Type& Vertex, const FVector& Position)
{
}
template<typename Type>
FORCEINLINE static typename TEnableIf<FRuntimeMeshVertexTraits<Type>::HasPosition, FVector>::Type GetPositionInternal(const Type& Vertex)
{
return Vertex.Position;
}
template<typename Type>
FORCEINLINE static typename TEnableIf<!FRuntimeMeshVertexTraits<Type>::HasPosition, FVector>::Type GetPositionInternal(const Type& Vertex)
{
return FVector::ZeroVector;
}
template<typename Type>
FORCEINLINE static typename TEnableIf<FRuntimeMeshVertexTraits<Type>::HasNormal>::Type SetNormalInternal(Type& Vertex, const FVector4& Normal)
{
Vertex.Normal = Normal;
}
template<typename Type>
FORCEINLINE static typename TEnableIf<!FRuntimeMeshVertexTraits<Type>::HasNormal>::Type SetNormalInternal(Type& Vertex, const FVector4& Normal)
{
}
template<typename Type>
FORCEINLINE static typename TEnableIf<FRuntimeMeshVertexTraits<Type>::HasNormal, FVector4>::Type GetNormalInternal(const Type& Vertex)
{
return Vertex.Normal;
}
template<typename Type>
FORCEINLINE static typename TEnableIf<!FRuntimeMeshVertexTraits<Type>::HasNormal, FVector4>::Type GetNormalInternal(const Type& Vertex)
{
return FVector::ZeroVector;
}
template<typename Type>
FORCEINLINE static typename TEnableIf<FRuntimeMeshVertexTraits<Type>::HasTangent>::Type SetTangentInternal(Type& Vertex, const FVector4& Tangent)
{
Vertex.Tangent = Tangent;
}
template<typename Type>
FORCEINLINE static typename TEnableIf<!FRuntimeMeshVertexTraits<Type>::HasTangent>::Type SetTangentInternal(Type& Vertex, const FVector4& Tangent)
{
}
template<typename Type>
FORCEINLINE static typename TEnableIf<FRuntimeMeshVertexTraits<Type>::HasTangent, FVector4>::Type GetTangentInternal(const Type& Vertex)
{
return Vertex.Tangent;
}
template<typename Type>
FORCEINLINE static typename TEnableIf<!FRuntimeMeshVertexTraits<Type>::HasTangent, FVector4>::Type GetTangentInternal(const Type& Vertex)
{
return FVector::ZeroVector;
}
template<typename Type>
FORCEINLINE static typename TEnableIf<FRuntimeMeshVertexTraits<Type>::HasColor>::Type SetColorInternal(Type& Vertex, const FColor& Color)
{
Vertex.Color = Color;
}
template<typename Type>
FORCEINLINE static typename TEnableIf<!FRuntimeMeshVertexTraits<Type>::HasColor>::Type SetColorInternal(Type& Vertex, const FColor& Color)
{
}
template<typename Type>
FORCEINLINE static typename TEnableIf<FRuntimeMeshVertexTraits<Type>::HasColor, FColor>::Type GetColorInternal(const Type& Vertex)
{
return Vertex.Color;
}
template<typename Type>
FORCEINLINE static typename TEnableIf<!FRuntimeMeshVertexTraits<Type>::HasColor, FColor>::Type GetColorInternal(const Type& Vertex)
{
return FColor::Transparent;
}
#define CreateUVChannelGetSetPair(Index) \
template<typename Type> \
FORCEINLINE static typename TEnableIf<FRuntimeMeshVertexTraits<Type>::HasUV##Index>::Type SetUV##Index##Internal(Type& Vertex, const FVector2D& UV##Index) \
{ \
Vertex.UV##Index = UV##Index; \
} \
template<typename Type> \
FORCEINLINE static typename TEnableIf<!FRuntimeMeshVertexTraits<Type>::HasUV##Index>::Type SetUV##Index##Internal(Type& Vertex, const FVector2D& UV##Index) \
{ \
} \
template<typename Type> \
FORCEINLINE static typename TEnableIf<FRuntimeMeshVertexTraits<Type>::HasUV##Index, FVector2D>::Type GetUV##Index##Internal(const Type& Vertex) \
{ \
return Vertex.UV##Index; \
} \
template<typename Type> \
FORCEINLINE static typename TEnableIf<!FRuntimeMeshVertexTraits<Type>::HasUV##Index, FVector2D>::Type GetUV##Index##Internal(const Type& Vertex) \
{ \
return FVector2D::ZeroVector; \
}
CreateUVChannelGetSetPair(0);
CreateUVChannelGetSetPair(1);
CreateUVChannelGetSetPair(2);
CreateUVChannelGetSetPair(3);
CreateUVChannelGetSetPair(4);
CreateUVChannelGetSetPair(5);
CreateUVChannelGetSetPair(6);
CreateUVChannelGetSetPair(7);
#undef CreateUVChannelGetSetPair
};
class RUNTIMEMESHCOMPONENT_API FRuntimeMeshComponentVerticesBuilder : public IRuntimeMeshVerticesBuilder
{
private:
TArray<FVector>* Positions;
TArray<FVector>* Normals;
TArray<FRuntimeMeshTangent>* Tangents;
TArray<FColor>* Colors;
TArray<FVector2D>* UV0s;
TArray<FVector2D>* UV1s;
int32 CurrentPosition;
bool bOwnsBuffers;
public:
FRuntimeMeshComponentVerticesBuilder(bool bInWantsNormal, bool bInWantsTangent, bool bInWantsColor, bool bInWantsUV0, bool bInWantsUV1)
: Positions(new TArray<FVector>())
, Normals(bInWantsNormal ? new TArray<FVector>() : nullptr)
, Tangents(bInWantsTangent ? new TArray<FRuntimeMeshTangent>() : nullptr)
, Colors(bInWantsColor ? new TArray<FColor>() : nullptr)
, UV0s(bInWantsUV0 ? new TArray<FVector2D>() : nullptr)
, UV1s(bInWantsUV1 ? new TArray<FVector2D>() : nullptr)
, CurrentPosition(-1)
, bOwnsBuffers(true)
{ }
FRuntimeMeshComponentVerticesBuilder(TArray<FVector>* InPositions, TArray<FVector>* InNormals, TArray<FRuntimeMeshTangent>* InTangents,
TArray<FColor>* InColors, TArray<FVector2D>* InUV0s, TArray<FVector2D>* InUV1s = nullptr)
: Positions(InPositions)
, Normals(InNormals)
, Tangents(InTangents)
, Colors(InColors)
, UV0s(InUV0s)
, UV1s(InUV1s)
, CurrentPosition(-1)
, bOwnsBuffers(false)
{ }
FRuntimeMeshComponentVerticesBuilder(const FRuntimeMeshComponentVerticesBuilder& Other) = delete;
FRuntimeMeshComponentVerticesBuilder& operator=(const FRuntimeMeshComponentVerticesBuilder& Other) = delete;
virtual ~FRuntimeMeshComponentVerticesBuilder() override
{
if (bOwnsBuffers)
{
if (Positions) delete Positions;
if (Normals) delete Normals;
if (Tangents) delete Tangents;
if (Colors) delete Colors;
if (UV0s) delete UV0s;
if (UV1s) delete UV1s;
}
}
virtual ERuntimeMeshVerticesBuilderType GetBuilderType() const { return ERuntimeMeshVerticesBuilderType::Packed; }
virtual const FRuntimeMeshVertexTypeInfo* GetVertexType() const;
virtual bool HasPositionComponent() const override { return Positions != nullptr; }
virtual bool HasNormalComponent() const override { return Normals != nullptr; }
virtual bool HasTangentComponent() const override { return Tangents != nullptr; }
virtual bool HasColorComponent() const override { return Colors != nullptr; }
virtual bool HasUVComponent(int32 Index) const override
{
switch (Index)
{
case 0:
return UV0s != nullptr;
case 1:
return UV1s != nullptr;
default:
return false;
}
}
virtual bool HasHighPrecisionNormals() const override { return false; }
virtual bool HasHighPrecisionUVs() const override { return true; }
virtual void SetPosition(const FVector& InPosition) override
{
if (CurrentPosition >= Positions->Num())
{
Positions->SetNumZeroed(CurrentPosition + 1, false);
}
(*Positions)[CurrentPosition] = InPosition;
}
virtual void SetNormal(const FVector4& InNormal) override
{
if (Normals)
{
if (CurrentPosition >= Normals->Num())
{
Normals->SetNumZeroed(CurrentPosition + 1, false);
}
(*Normals)[CurrentPosition] = InNormal;
(*Tangents)[CurrentPosition].bFlipTangentY = InNormal.W < 0.0f;
}
}
virtual void SetTangent(const FVector& InTangent) override
{
if (Tangents)
{
if (CurrentPosition >= Tangents->Num())
{
Tangents->SetNumZeroed(CurrentPosition + 1, false);
}
(*Tangents)[CurrentPosition].TangentX = InTangent;
}
}
virtual void SetColor(const FColor& InColor) override
{
if (Colors)
{
if (CurrentPosition >= Colors->Num())
{
Colors->SetNumZeroed(CurrentPosition + 1, false);
}
(*Colors)[CurrentPosition] = InColor;
}
}
virtual void SetUV(int32 Index, const FVector2D& InUV)
{
switch (Index)
{
case 0:
{
if (UV0s)
{
if (CurrentPosition >= UV0s->Num())
{
UV0s->SetNumZeroed(CurrentPosition + 1, false);
}
(*UV0s)[CurrentPosition] = InUV;
}
}
case 1:
{
if (UV1s)
{
if (CurrentPosition >= UV1s->Num())
{
UV1s->SetNumZeroed(CurrentPosition + 1, false);
}
(*UV1s)[CurrentPosition] = InUV;
}
}
default:
return;
}
}
virtual void SetPosition(int32 VertexIndex, const FVector& InPosition) override
{
Seek(VertexIndex);
if (CurrentPosition >= Positions->Num())
{
Positions->SetNumZeroed(CurrentPosition + 1, false);
}
(*Positions)[CurrentPosition] = InPosition;
}
virtual void SetNormal(int32 VertexIndex, const FVector4& InNormal) override
{
Seek(VertexIndex);
if (Normals)
{
if (CurrentPosition >= Normals->Num())
{
Normals->SetNumZeroed(CurrentPosition + 1, false);
}
(*Normals)[CurrentPosition] = InNormal;
(*Tangents)[CurrentPosition].bFlipTangentY = InNormal.W < 0.0f;
}
}
virtual void SetTangent(int32 VertexIndex, const FVector& InTangent) override
{
Seek(VertexIndex);
if (Tangents)
{
if (CurrentPosition >= Tangents->Num())
{
Tangents->SetNumZeroed(CurrentPosition + 1, false);
}
(*Tangents)[CurrentPosition].TangentX = InTangent;
}
}
virtual void SetColor(int32 VertexIndex, const FColor& InColor) override
{
Seek(VertexIndex);
if (Colors)
{
if (CurrentPosition >= Colors->Num())
{
Colors->SetNumZeroed(CurrentPosition + 1, false);
}
(*Colors)[CurrentPosition] = InColor;
}
}
virtual void SetUV(int32 VertexIndex, int32 Index, const FVector2D& InUV)
{
Seek(VertexIndex);
switch (Index)
{
case 0:
{
if (UV0s)
{
if (CurrentPosition >= UV0s->Num())
{
UV0s->SetNumZeroed(CurrentPosition + 1, false);
}
(*UV0s)[CurrentPosition] = InUV;
}
}
case 1:
{
if (UV1s)
{
if (CurrentPosition >= UV1s->Num())
{
UV1s->SetNumZeroed(CurrentPosition + 1, false);
}
(*UV1s)[CurrentPosition] = InUV;
}
}
default:
return;
}
}
virtual FVector GetPosition() const override
{
check(Positions && Positions->Num() > CurrentPosition);
return (*Positions)[CurrentPosition];
}
virtual FVector4 GetNormal() const override
{
check(Normals && Normals->Num() > CurrentPosition);
float W = (Tangents && Tangents->Num() > CurrentPosition) ? ((*Tangents)[CurrentPosition].bFlipTangentY ? -1.0f : 1.0f) : 1.0f;
return FVector4((*Normals)[CurrentPosition], W);
}
virtual FVector GetTangent() const override
{
check(Tangents && Tangents->Num() > CurrentPosition);
return (*Tangents)[CurrentPosition].TangentX;
}
virtual FColor GetColor() const override
{
check(Colors && Colors->Num() > CurrentPosition);
return (*Colors)[CurrentPosition];
}
virtual FVector2D GetUV(int32 Index) const override
{
switch (Index)
{
case 0:
check(UV0s && UV0s->Num() > CurrentPosition);
return (*UV0s)[CurrentPosition];
case 1:
check(UV1s && UV1s->Num() > CurrentPosition);
return (*UV1s)[CurrentPosition];
}
return FVector2D::ZeroVector;
}
virtual FVector GetPosition(int32 VertexIndex) const override
{
Seek(VertexIndex);
check(Positions && Positions->Num() > CurrentPosition);
return (*Positions)[CurrentPosition];
}
virtual FVector4 GetNormal(int32 VertexIndex) const override
{
Seek(VertexIndex);
check(Normals && Normals->Num() > CurrentPosition);
float W = (Tangents && Tangents->Num() > CurrentPosition) ? ((*Tangents)[CurrentPosition].bFlipTangentY ? -1.0f : 1.0f) : 1.0f;
return FVector4((*Normals)[CurrentPosition], W);
}
virtual FVector GetTangent(int32 VertexIndex) const override
{
Seek(VertexIndex);
check(Tangents && Tangents->Num() > CurrentPosition);
return (*Tangents)[CurrentPosition].TangentX;
}
virtual FColor GetColor(int32 VertexIndex) const override
{
Seek(VertexIndex);
check(Colors && Colors->Num() > CurrentPosition);
return (*Colors)[CurrentPosition];
}
virtual FVector2D GetUV(int32 VertexIndex, int32 Index) const override
{
Seek(VertexIndex);
switch (Index)
{
case 0:
check(UV0s && UV0s->Num() > CurrentPosition);
return (*UV0s)[CurrentPosition];
case 1:
check(UV1s && UV1s->Num() > CurrentPosition);
return (*UV1s)[CurrentPosition];
}
return FVector2D::ZeroVector;
}
virtual int32 Length() const override { return Positions->Num(); }
virtual void Seek(int32 Position) const override
{
const_cast<FRuntimeMeshComponentVerticesBuilder*>(this)->CurrentPosition = Position;
}
virtual int32 MoveNext() const override
{
return ++const_cast<FRuntimeMeshComponentVerticesBuilder*>(this)->CurrentPosition;
}
virtual int32 MoveNextOrAdd() override
{
return ++CurrentPosition;
}
TArray<FVector>* GetPositions() const { return Positions; }
TArray<FVector>* GetNormals() const { return Normals; }
TArray<FRuntimeMeshTangent>* GetTangents() const { return Tangents; }
TArray<FColor>* GetColors() const { return Colors; }
TArray<FVector2D>* GetUV0s() const { return UV0s; }
TArray<FVector2D>* GetUV1s() const { return UV1s; }
virtual void Reset() override
{
Positions->Reset();
Normals->Reset();
Tangents->Reset();
Colors->Reset();
UV0s->Reset();
UV1s->Reset();
CurrentPosition = -1;
}
virtual IRuntimeMeshVerticesBuilder* Clone(bool bIncludeData = true) const override
{
FRuntimeMeshComponentVerticesBuilder* NewBuilder = new FRuntimeMeshComponentVerticesBuilder(Normals != nullptr, Tangents != nullptr, Colors != nullptr, UV0s != nullptr, UV1s != nullptr);
if (bIncludeData)
{
*NewBuilder->Positions = *Positions;
*NewBuilder->Normals = *Normals;
*NewBuilder->Tangents = *Tangents;
*NewBuilder->Colors = *Colors;
*NewBuilder->UV0s = *UV0s;
*NewBuilder->UV1s = *UV1s;
NewBuilder->Seek(0);
}
return NewBuilder;
}
};
class FRuntimeMeshIndicesBuilder
{
bool bOwnsIndexArray;
TArray<int32>* Indices;
int32 CurrentPosition;
public:
FRuntimeMeshIndicesBuilder()
: bOwnsIndexArray(true), Indices(new TArray<int32>()), CurrentPosition(0)
{ }
FRuntimeMeshIndicesBuilder(TArray<int32>* InVertices)
: bOwnsIndexArray(false), Indices(InVertices), CurrentPosition(0)
{ }
FRuntimeMeshIndicesBuilder(const FRuntimeMeshIndicesBuilder& Other) = delete;
FRuntimeMeshIndicesBuilder& operator=(const FRuntimeMeshIndicesBuilder& Other) = delete;
virtual ~FRuntimeMeshIndicesBuilder()
{
if (bOwnsIndexArray)
{
delete Indices;
}
}
virtual ERuntimeMeshVerticesBuilderType GetBuilderType() const { return ERuntimeMeshVerticesBuilderType::Component; }
void AddTriangle(int32 Index0, int32 Index1, int32 Index2)
{
if ((CurrentPosition + 3) >= Indices->Num())
{
Indices->SetNum(CurrentPosition + 3);
}
(*Indices)[CurrentPosition++] = Index0;
(*Indices)[CurrentPosition++] = Index1;
(*Indices)[CurrentPosition++] = Index2;
}
void AddIndex(int32 Index)
{
if ((CurrentPosition + 1) >= Indices->Num())
{
Indices->SetNum(CurrentPosition + 1);
}
(*Indices)[CurrentPosition++] = Index;
}
int32 ReadOne() const
{
return (*Indices)[const_cast<FRuntimeMeshIndicesBuilder*>(this)->CurrentPosition++];
}
int32 GetIndex(int32 Position) const
{
const_cast<FRuntimeMeshIndicesBuilder*>(this)->CurrentPosition = Position;
return (*Indices)[CurrentPosition];
}
int32 TriangleLength() const { return Length() / 3; }
int32 Length() const { return Indices->Num(); }
bool HasRemaining() const { return CurrentPosition < Indices->Num(); }
void Seek(int32 Position) const
{
const_cast<FRuntimeMeshIndicesBuilder*>(this)->CurrentPosition = Position;
}
void SeekEnd() const
{
Seek(Length());
}
void Reset(int32 NewSize = 0)
{
Indices->Reset(NewSize);
CurrentPosition = 0;
}
void SetNum(int32 NewSize)
{
Indices->SetNum(NewSize);
}
FRuntimeMeshIndicesBuilder* Clone(bool bIncludeData = true) const
{
FRuntimeMeshIndicesBuilder* NewBuilder = new FRuntimeMeshIndicesBuilder();
if (bIncludeData)
{
*NewBuilder->Indices = *Indices;
}
return NewBuilder;
}
TArray<int32>* GetIndices()
{
return Indices;
}
};

View File

@ -1,38 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#pragma once
#include "ModuleManager.h"
/**
* The public interface to this module
*/
class IRuntimeMeshComponentPlugin : public IModuleInterface
{
public:
/**
* Singleton-like access to this module's interface. This is just for convenience!
* Beware of calling this during the shutdown phase, though. Your module might have been unloaded already.
*
* @return Returns singleton instance, loading the module on demand if needed
*/
static inline IRuntimeMeshComponentPlugin& Get()
{
return FModuleManager::LoadModuleChecked< IRuntimeMeshComponentPlugin >("RuntimeMeshComponentPlugin");
}
/**
* Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true.
*
* @return True if the module is loaded and ready to use
*/
static inline bool IsAvailable()
{
return FModuleManager::Get().IsModuleLoaded("RuntimeMeshComponentPlugin");
}
};
DECLARE_LOG_CATEGORY_EXTERN(RuntimeMeshLog, Log, All);

View File

@ -1,422 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#pragma once
#include "Engine.h"
#include "Components/MeshComponent.h"
#include "RuntimeMeshProfiling.h"
#include "RuntimeMeshVersion.h"
#include "Runtime/Launch/Resources/Version.h"
#include "RuntimeMeshCore.generated.h"
class FRuntimeMeshVertexFactory;
template<typename T>
struct FRuntimeMeshVertexTraits
{
private:
template<typename C, C> struct ChT;
struct FallbackPosition { FVector Position; };
struct DerivedPosition : T, FallbackPosition { };
template<typename C> static char(&PositionCheck(ChT<FVector FallbackPosition::*, &C::Position>*))[1];
template<typename C> static char(&PositionCheck(...))[2];
struct FallbackNormal { FPackedRGBA16N Normal; };
struct DerivedNormal : T, FallbackNormal { };
template<typename C> static char(&NormalCheck(ChT<FPackedRGBA16N FallbackNormal::*, &C::Normal>*))[1];
template<typename C> static char(&NormalCheck(...))[2];
struct FallbackTangent { FPackedRGBA16N Tangent; };
struct DerivedTangent : T, FallbackTangent { };
template<typename C> static char(&TangentCheck(ChT<FPackedRGBA16N FallbackTangent::*, &C::Tangent>*))[1];
template<typename C> static char(&TangentCheck(...))[2];
struct FallbackColor { FColor Color; };
struct DerivedColor : T, FallbackColor { };
template<typename C> static char(&ColorCheck(ChT<FColor FallbackColor::*, &C::Color>*))[1];
template<typename C> static char(&ColorCheck(...))[2];
struct FallbackUV0 { FVector2D UV0; };
struct DerivedUV0 : T, FallbackUV0 { };
template<typename C> static char(&UV0Check(ChT<FVector2D FallbackUV0::*, &C::UV0>*))[1];
template<typename C> static char(&UV0Check(...))[2];
struct FallbackUV1 { FVector2D UV1; };
struct DerivedUV1 : T, FallbackUV1 { };
template<typename C> static char(&UV1Check(ChT<FVector2D FallbackUV1::*, &C::UV1>*))[1];
template<typename C> static char(&UV1Check(...))[2];
struct FallbackUV2 { FVector2D UV2; };
struct DerivedUV2 : T, FallbackUV2 { };
template<typename C> static char(&UV2Check(ChT<FVector2D FallbackUV2::*, &C::UV2>*))[1];
template<typename C> static char(&UV2Check(...))[2];
struct FallbackUV3 { FVector2D UV3; };
struct DerivedUV3 : T, FallbackUV3 { };
template<typename C> static char(&UV3Check(ChT<FVector2D FallbackUV3::*, &C::UV3>*))[1];
template<typename C> static char(&UV3Check(...))[2];
struct FallbackUV4 { FVector2D UV4; };
struct DerivedUV4 : T, FallbackUV4 { };
template<typename C> static char(&UV4Check(ChT<FVector2D FallbackUV4::*, &C::UV4>*))[1];
template<typename C> static char(&UV4Check(...))[2];
struct FallbackUV5 { FVector2D UV5; };
struct DerivedUV5 : T, FallbackUV5 { };
template<typename C> static char(&UV5Check(ChT<FVector2D FallbackUV5::*, &C::UV5>*))[1];
template<typename C> static char(&UV5Check(...))[2];
struct FallbackUV6 { FVector2D UV6; };
struct DerivedUV6 : T, FallbackUV6 { };
template<typename C> static char(&UV6Check(ChT<FVector2D FallbackUV6::*, &C::UV6>*))[1];
template<typename C> static char(&UV6Check(...))[2];
struct FallbackUV7 { FVector2D UV7; };
struct DerivedUV7 : T, FallbackUV7 { };
template<typename C> static char(&UV7Check(ChT<FVector2D FallbackUV7::*, &C::UV7>*))[1];
template<typename C> static char(&UV7Check(...))[2];
template<typename A, typename B>
struct IsSameType
{
static const bool Value = false;
};
template<typename A>
struct IsSameType<A, A>
{
static const bool Value = true;
};
template<bool HasNormal, bool HasTangent, typename Type>
struct TangentBasisHighPrecisionDetector
{
static const bool Value = false;
};
template<typename Type>
struct TangentBasisHighPrecisionDetector<true, false, Type>
{
static const bool Value = IsSameType<decltype(DeclVal<T>().Normal), FPackedRGBA16N>::Value;
};
template<bool HasNormal, typename Type>
struct TangentBasisHighPrecisionDetector<HasNormal, true, Type>
{
static const bool Value = IsSameType<decltype(DeclVal<T>().Tangent), FPackedRGBA16N>::Value;
};
template<bool HasUV0, typename Type>
struct UVChannelHighPrecisionDetector
{
static const bool Value = false;
};
template<typename Type>
struct UVChannelHighPrecisionDetector<true, Type>
{
static const bool Value = IsSameType<decltype(DeclVal<T>().UV0), FVector2D>::Value;
};
public:
static const bool HasPosition = sizeof(PositionCheck<DerivedPosition>(0)) == 2;
static const bool HasNormal = sizeof(NormalCheck<DerivedNormal>(0)) == 2;
static const bool HasTangent = sizeof(TangentCheck<DerivedTangent>(0)) == 2;
static const bool HasColor = sizeof(ColorCheck<DerivedColor>(0)) == 2;
static const bool HasUV0 = sizeof(UV0Check<DerivedUV0>(0)) == 2;
static const bool HasUV1 = sizeof(UV1Check<DerivedUV1>(0)) == 2;
static const bool HasUV2 = sizeof(UV2Check<DerivedUV2>(0)) == 2;
static const bool HasUV3 = sizeof(UV3Check<DerivedUV3>(0)) == 2;
static const bool HasUV4 = sizeof(UV4Check<DerivedUV4>(0)) == 2;
static const bool HasUV5 = sizeof(UV5Check<DerivedUV5>(0)) == 2;
static const bool HasUV6 = sizeof(UV6Check<DerivedUV6>(0)) == 2;
static const bool HasUV7 = sizeof(UV7Check<DerivedUV7>(0)) == 2;
static const int32 NumUVChannels =
(HasUV0 ? 1 : 0) +
(HasUV1 ? 1 : 0) +
(HasUV2 ? 1 : 0) +
(HasUV3 ? 1 : 0) +
(HasUV4 ? 1 : 0) +
(HasUV5 ? 1 : 0) +
(HasUV6 ? 1 : 0) +
(HasUV7 ? 1 : 0);
static const bool HasHighPrecisionNormals = TangentBasisHighPrecisionDetector<HasNormal, HasTangent, T>::Value;
static const bool HasHighPrecisionUVs = UVChannelHighPrecisionDetector<HasUV0, T>::Value;
};
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 12
/** Structure definition of a vertex */
using RuntimeMeshVertexStructure = FLocalVertexFactory::FDataType;
#else
/** Structure definition of a vertex */
using RuntimeMeshVertexStructure = FLocalVertexFactory::DataType;
#endif
#define RUNTIMEMESH_VERTEXCOMPONENT(VertexBuffer, VertexType, Member, MemberType) \
STRUCTMEMBER_VERTEXSTREAMCOMPONENT(&VertexBuffer, VertexType, Member, MemberType)
/* Update frequency for a section. Used to optimize for update or render speed*/
UENUM(BlueprintType)
enum class EUpdateFrequency : uint8
{
/* Tries to skip recreating the scene proxy if possible. */
Average UMETA(DisplayName = "Average"),
/* Tries to skip recreating the scene proxy if possible and optimizes the buffers for frequent updates. */
Frequent UMETA(DisplayName = "Frequent"),
/* If the component is static it will try to use the static rendering path (this will force a recreate of the scene proxy) */
Infrequent UMETA(DisplayName = "Infrequent")
};
/* Control flags for update actions */
enum class ESectionUpdateFlags
{
None = 0x0,
/**
* This will use move-assignment when copying the supplied vertices/triangles into the section.
* This is faster as it doesn't require copying the data.
*
* CAUTION: This means that your copy of the arrays will be cleared!
*/
MoveArrays = 0x1,
/**
* Should the normals and tangents be calculated automatically?
* To do this manually see RuntimeMeshLibrary::CalculateTangentsForMesh()
*/
CalculateNormalTangent = 0x2,
/**
* Should the tessellation indices be calculated to support tessellation?
* To do this manually see RuntimeMeshLibrary::GenerateTessellationIndexBuffer()
*/
CalculateTessellationIndices = 0x4,
};
ENUM_CLASS_FLAGS(ESectionUpdateFlags)
/**
* Struct used to specify a tangent vector for a vertex
* The Y tangent is computed from the cross product of the vertex normal (Tangent Z) and the TangentX member.
*/
USTRUCT(BlueprintType)
struct FRuntimeMeshTangent
{
GENERATED_USTRUCT_BODY()
/** Direction of X tangent for this vertex */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Tangent)
FVector TangentX;
/** Bool that indicates whether we should flip the Y tangent when we compute it using cross product */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Tangent)
bool bFlipTangentY;
FRuntimeMeshTangent()
: TangentX(1.f, 0.f, 0.f)
, bFlipTangentY(false)
{}
FRuntimeMeshTangent(float X, float Y, float Z, bool bInFlipTangentY = false)
: TangentX(X, Y, Z)
, bFlipTangentY(bInFlipTangentY)
{}
FRuntimeMeshTangent(FVector InTangentX, bool bInFlipTangentY = false)
: TangentX(InTangentX)
, bFlipTangentY(bInFlipTangentY)
{}
void AdjustNormal(FPackedNormal& Normal) const
{
Normal.Vector.W = bFlipTangentY ? 0 : 255;
}
void AdjustNormal(FPackedRGBA16N& Normal) const
{
Normal.W = bFlipTangentY ? 0 : 65535;
}
};
/*
* Configuration flag for the collision cooking to prioritize cooking speed or collision performance.
*/
UENUM(BlueprintType)
enum class ERuntimeMeshCollisionCookingMode : uint8
{
/*
* Favors runtime collision performance of cooking speed.
* This means that cooking a new mesh will be slower, but collision will be faster.
*/
CollisionPerformance UMETA(DisplayName = "Collision Performance"),
/*
* Favors cooking speed over collision performance.
* This means that cooking a new mesh will be faster, but collision will be slower.
*/
CookingPerformance UMETA(DisplayName = "Cooking Performance"),
};
/* The different buffers within the Runtime Mesh Component */
enum class ERuntimeMeshBuffer
{
None = 0x0,
Vertices = 0x1,
Triangles = 0x2,
Positions = 0x4
};
ENUM_CLASS_FLAGS(ERuntimeMeshBuffer)
USTRUCT()
struct FRuntimeMeshCollisionSection
{
GENERATED_BODY()
UPROPERTY()
TArray<FVector> VertexBuffer;
UPROPERTY()
TArray<int32> IndexBuffer;
void Reset()
{
VertexBuffer.Empty();
IndexBuffer.Empty();
}
friend FArchive& operator <<(FArchive& Ar, FRuntimeMeshCollisionSection& Section)
{
Ar << Section.VertexBuffer;
Ar << Section.IndexBuffer;
return Ar;
}
};
USTRUCT()
struct FRuntimeConvexCollisionSection
{
GENERATED_BODY()
UPROPERTY()
TArray<FVector> VertexBuffer;
UPROPERTY()
FBox BoundingBox;
void Reset()
{
VertexBuffer.Empty();
BoundingBox.Init();
}
friend FArchive& operator <<(FArchive& Ar, FRuntimeConvexCollisionSection& Section)
{
Ar << Section.VertexBuffer;
Ar << Section.BoundingBox;
return Ar;
}
};
struct RUNTIMEMESHCOMPONENT_API FRuntimeMeshVertexTypeInfo
{
const FString TypeName;
const FGuid TypeGuid;
FRuntimeMeshVertexTypeInfo(FString Name, FGuid Guid) : TypeName(Name), TypeGuid(Guid) { }
virtual bool Equals(const FRuntimeMeshVertexTypeInfo* Other) const
{
return TypeGuid == Other->TypeGuid;
}
template<typename Type>
void EnsureEquals() const
{
if (!Equals(&Type::TypeInfo))
{
ThrowMismatchException(Type::TypeInfo.TypeName);
}
}
virtual class FRuntimeMeshSectionInterface* CreateSection(bool bInNeedsPositionOnlyBuffer) const = 0;
protected:
void ThrowMismatchException(const FString& OtherName) const
{
UE_LOG(RuntimeMeshLog, Fatal, TEXT("Vertex Type Mismatch: %s and %s"), *TypeName, *OtherName);
}
};
/*
* Internal container used to track known vertex types, for serialization and other purposes.
*/
class RUNTIMEMESHCOMPONENT_API FRuntimeMeshVertexTypeRegistrationContainer
{
struct VertexRegistration
{
const FRuntimeMeshVertexTypeInfo* const TypeInfo;
uint32 ReferenceCount;
VertexRegistration(const FRuntimeMeshVertexTypeInfo* const InTypeInfo)
: TypeInfo(InTypeInfo), ReferenceCount(1) { }
};
TMap<FGuid, VertexRegistration> Registrations;
public:
static FRuntimeMeshVertexTypeRegistrationContainer& GetInstance();
void Register(const FRuntimeMeshVertexTypeInfo* InType);
void UnRegister(const FRuntimeMeshVertexTypeInfo* InType);
const FRuntimeMeshVertexTypeInfo* GetVertexType(FGuid Key) const;
};
template<typename VertexType>
class FRuntimeMeshVertexTypeRegistration : FNoncopyable
{
public:
FRuntimeMeshVertexTypeRegistration()
{
FRuntimeMeshVertexTypeRegistrationContainer::GetInstance().Register(&VertexType::TypeInfo);
}
~FRuntimeMeshVertexTypeRegistration()
{
FRuntimeMeshVertexTypeRegistrationContainer::GetInstance().UnRegister(&VertexType::TypeInfo);
}
};
#define DECLARE_RUNTIMEMESH_CUSTOMVERTEX_TYPEINFO(TypeName, Guid) \
struct FRuntimeMeshVertexTypeInfo_##TypeName : public FRuntimeMeshVertexTypeInfo \
{ \
FRuntimeMeshVertexTypeInfo_##TypeName() : FRuntimeMeshVertexTypeInfo(TEXT(#TypeName), Guid) { } \
}; \
static const FRuntimeMeshVertexTypeInfo_##TypeName TypeInfo;
#define DEFINE_RUNTIMEMESH_CUSTOMVERTEX_TYPEINFO(TypeName) \
const TypeName::FRuntimeMeshVertexTypeInfo_##TypeName TypeName::TypeInfo = TypeName::FRuntimeMeshVertexTypeInfo_##TypeName(); \
FRuntimeMeshVertexTypeRegistration<##TypeName> FRuntimeMeshVertexTypeInfoRegistration_##TypeName(&##TypeName::TypeInfo);

View File

@ -1,129 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#pragma once
#include "Kismet/BlueprintFunctionLibrary.h"
#include "RuntimeMeshComponent.h"
#include "RuntimeMeshLibrary.generated.h"
class RuntimeMeshComponent;
UCLASS()
class RUNTIMEMESHCOMPONENT_API URuntimeMeshLibrary : public UBlueprintFunctionLibrary
{
GENERATED_UCLASS_BODY()
/** Add a quad, specified by four indices, to a triangle index buffer as two triangles. */
UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh")
static void ConvertQuadToTriangles(UPARAM(ref) TArray<int32>& Triangles, int32 Vert0, int32 Vert1, int32 Vert2, int32 Vert3);
/**
* Generate an index buffer for a grid of quads.
* @param NumX Number of vertices in X direction (must be >= 2)
* @param NumY Number of vertices in y direction (must be >= 2)
* @param bWinding Reverses winding of indices generated for each quad
* @out Triangles Output index buffer
*/
UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh")
static void CreateGridMeshTriangles(int32 NumX, int32 NumY, bool bWinding, TArray<int32>& Triangles);
/** Generate vertex and index buffer for a simple box, given the supplied dimensions. Normals, UVs and tangents are also generated for each vertex. */
UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh")
static void CreateBoxMesh(FVector BoxRadius, TArray<FVector>& Vertices, TArray<int32>& Triangles, TArray<FVector>& Normals, TArray<FVector2D>& UVs, TArray<FRuntimeMeshTangent>& Tangents);
/**
* Automatically generate normals and tangent vectors for a mesh
* UVs are required for correct tangent generation.
*/
static void CalculateTangentsForMesh(IRuntimeMeshVerticesBuilder* Vertices, const FRuntimeMeshIndicesBuilder* Triangles);
/**
* Automatically generate normals and tangent vectors for a mesh
* UVs are required for correct tangent generation.
*/
template <typename VertexType>
static void CalculateTangentsForMesh(TArray<VertexType>& Vertices, const TArray<int32>& Triangles)
{
FRuntimeMeshPackedVerticesBuilder<VertexType> VerticesBuilder(&Vertices);
FRuntimeMeshIndicesBuilder IndicesBuilder(const_cast<TArray<int32>*>(&Triangles));
CalculateTangentsForMesh(&VerticesBuilder, &IndicesBuilder);
}
/**
* Automatically generate normals and tangent vectors for a mesh
* UVs are required for correct tangent generation.
*/
template <typename VertexType>
static void CalculateTangentsForMesh(TArray<FVector>& Positions, TArray<VertexType>& Vertices, const TArray<int32>& Triangles)
{
FRuntimeMeshPackedVerticesBuilder<VertexType> VerticesBuilder(&Vertices, &Positions);
FRuntimeMeshIndicesBuilder IndicesBuilder(const_cast<TArray<int32>*>(&Triangles));
CalculateTangentsForMesh(&VerticesBuilder, &IndicesBuilder);
}
/**
* Automatically generate normals and tangent vectors for a mesh
* UVs are required for correct tangent generation.
*/
UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh", meta = (AutoCreateRefTerm = "UVs"))
static void CalculateTangentsForMesh(const TArray<FVector>& Vertices, const TArray<int32>& Triangles, const TArray<FVector2D>& UVs, TArray<FVector>& Normals, TArray<FRuntimeMeshTangent>& Tangents);
/**
* Generates the tessellation indices needed to support tessellation in materials
*/
static void GenerateTessellationIndexBuffer(const IRuntimeMeshVerticesBuilder* Vertices, const FRuntimeMeshIndicesBuilder* Indices, FRuntimeMeshIndicesBuilder* OutTessellationIndices);
/**
* Generates the tessellation indices needed to support tessellation in materials
*/
template <typename VertexType>
static void GenerateTessellationIndexBuffer(TArray<VertexType>& Vertices, const TArray<int32>& Triangles, TArray<int32>& OutTessTriangles)
{
FRuntimeMeshPackedVerticesBuilder<VertexType> VerticesBuilder(&Vertices);
FRuntimeMeshIndicesBuilder IndicesBuilder(const_cast<TArray<int32>*>(&Triangles));
FRuntimeMeshIndicesBuilder OutIndicesBuilder(&OutTessTriangles);
GenerateTessellationIndexBuffer(&VerticesBuilder, &IndicesBuilder, &OutIndicesBuilder);
}
/**
* Generates the tessellation indices needed to support tessellation in materials
*/
template <typename VertexType>
static void GenerateTessellationIndexBuffer(TArray<FVector>& Positions, TArray<VertexType>& Vertices, const TArray<int32>& Triangles, TArray<int32>& OutTessTriangles)
{
FRuntimeMeshPackedVerticesBuilder<VertexType> VerticesBuilder(&Vertices, &Positions);
FRuntimeMeshIndicesBuilder IndicesBuilder(const_cast<TArray<int32>*>(&Triangles));
FRuntimeMeshIndicesBuilder OutIndicesBuilder(&OutTessTriangles);
GenerateTessellationIndexBuffer(&VerticesBuilder, &IndicesBuilder, &OutIndicesBuilder);
}
/**
* Generates the tessellation indices needed to support tessellation in materials
*/
UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh", meta = (AutoCreateRefTerm = "UVs"))
static void GenerateTessellationIndexBuffer(const TArray<FVector>& Vertices, const TArray<int32>& Triangles, const TArray<FVector2D>& UVs, TArray<FVector>& Normals, TArray<FRuntimeMeshTangent>& Tangents, TArray<int32>& OutTessTriangles);
/** Grab geometry data from a StaticMesh asset. */
static void GetSectionFromStaticMesh(UStaticMesh* InMesh, int32 LODIndex, int32 SectionIndex,
IRuntimeMeshVerticesBuilder* Vertices, FRuntimeMeshIndicesBuilder* Triangles, FRuntimeMeshIndicesBuilder* AdjacencyTriangles);
/** Grab geometry data from a StaticMesh asset. */
UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh")
static void GetSectionFromStaticMesh(UStaticMesh* InMesh, int32 LODIndex, int32 SectionIndex, TArray<FVector>& Vertices, TArray<int32>& Triangles, TArray<FVector>& Normals, TArray<FVector2D>& UVs, TArray<FRuntimeMeshTangent>& Tangents);
/* Copies an entire Static Mesh to a Runtime Mesh. Includes all materials, and sections.*/
UFUNCTION(BlueprintCallable, Category = "Components|RuntimeMesh")
static void CopyRuntimeMeshFromStaticMeshComponent(UStaticMeshComponent* StaticMeshComp, int32 LODIndex, URuntimeMeshComponent* RuntimeMeshComp, bool bShouldCreateCollision);
};

View File

@ -1,74 +0,0 @@
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "RuntimeMeshComponent.h"
DECLARE_STATS_GROUP(TEXT("RuntimeMesh"), STATGROUP_RuntimeMesh, STATCAT_Advanced);
// Scene Proxy Profiling
DECLARE_CYCLE_STAT(TEXT("Create Section (RT)"), STAT_RuntimeMesh_CreateSection_RenderThread, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Update Section (RT)"), STAT_RuntimeMesh_UpdateSection_RenderThread, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Update Section - Position Only (RT)"), STAT_RuntimeMesh_UpdateSectionPositionOnly_RenderThread, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Update Section Properties (RT)"), STAT_RuntimeMesh_UpdateSectionProperties_RenderThread, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Apply Batch Update (RT)"), STAT_RuntimeMesh_ApplyBatchUpdate_RenderThread, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("On Transform Changed (RT)"), STAT_RuntimeMesh_OnTransformChanged, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Draw Static Elements (RT)"), STAT_RuntimeMesh_DrawStaticElements, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Get Dynamic Mesh Elements (RT)"), STAT_RuntimeMesh_GetDynamicMeshElements, STATGROUP_RuntimeMesh);
// RuntimeMeshComponent Profiling
DECLARE_CYCLE_STAT(TEXT("CreateMeshSection<VertexType> (GT)"), STAT_RuntimeMesh_CreateMeshSection_VertexType, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("CreateMeshSection<VertexType> (With Bounding Box) (GT)"), STAT_RuntimeMesh_CreateMeshSection_VertexType_WithBoundingBox, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("CreateMeshSectionDualBuffer<VertexType> (GT)"), STAT_RuntimeMesh_CreateMeshSectionDualBuffer_VertexType, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("CreateMeshSectionDualBuffer<VertexType> (With Bounding Box) (GT)"), STAT_RuntimeMesh_CreateMeshSectionDualBuffer_VertexType_WithBoundingBox, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("CreateMeshSection<VertexType> (From Mesh Builder) (GT)"), STAT_RuntimeMesh_CreateMeshSection_VertexType_FromMeshBuilder, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("CreateMeshSection (GT)"), STAT_RuntimeMesh_CreateMeshSection, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("CreateMeshSection (GT)"), STAT_RuntimeMesh_CreateMeshSection_DualUV, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("UpdateMeshSection<VertexType> (GT)"), STAT_RuntimeMesh_UpdateMeshSection_VertexType, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("UpdateMeshSection<VertexType> (With Bounding Box) (GT)"), STAT_RuntimeMesh_UpdateMeshSection_VertexType_WithBoundingBox, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("UpdateMeshSection<VertexType> (With Triangles) (GT)"), STAT_RuntimeMesh_UpdateMeshSection_VertexType_WithTriangles, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("UpdateMeshSection<VertexType> (With Triangles and Bounding Box) (GT)"), STAT_RuntimeMesh_UpdateMeshSection_VertexType_WithTrianglesAndBoundinBox, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("UpdateMeshSection<VertexType> (Dual) (GT)"), STAT_RuntimeMesh_UpdateMeshSection_Dual_VertexType, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("UpdateMeshSection<VertexType> (Dual) (With Bounding Box) (GT)"), STAT_RuntimeMesh_UpdateMeshSection_Dual_VertexType_WithBoundingBox, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("UpdateMeshSection<VertexType> (Dual) (With Triangles) (GT)"), STAT_RuntimeMesh_UpdateMeshSection_Dual_VertexType_WithTriangles, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("UpdateMeshSection<VertexType> (Dual) (With Triangles and Bounding Box) (GT)"), STAT_RuntimeMesh_UpdateMeshSection_Dual_VertexType_WithTrianglesAndBoundinBox, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("UpdateMeshSection (GT)"), STAT_RuntimeMesh_UpdateMeshSection, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("UpdateMeshSection (GT)"), STAT_RuntimeMesh_UpdateMeshSection_DualUV, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("UpdateMeshSectionPositionsImmediate (GT)"), STAT_RuntimeMesh_UpdateMeshSectionPositionsImmediate, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("UpdateMeshSectionPositionsImmediate (With Bounding Box) (GT)"), STAT_RuntimeMesh_UpdateMeshSectionPositionsImmediate_WithBoundinBox, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Finish Create Section (GT)"), STAT_RuntimeMesh_FinishCreateSectionInternal, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Finish Update Section (GT)"), STAT_RuntimeMesh_FinishUpdateSectionInternal, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Clear Mesh Section (GT)"), STAT_RuntimeMesh_ClearMeshSection, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Set Mesh Collision Section (GT)"), STAT_RuntimeMesh_SetMeshCollisionSection, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Clear Mesh Collision Section (GT)"), STAT_RuntimeMesh_ClearMeshCollisionSection, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Clear All Mesh Collision Sections (GT)"), STAT_RuntimeMesh_ClearAllMeshCollisionSections, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Add Collision Convex Mesh (GT)"), STAT_RuntimeMesh_AddCollisionConvexMesh, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Clear Collision Convex Mesh (GT)"), STAT_RuntimeMesh_ClearCollisionConvexMeshes, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Set Collision Convex Meshes (GT)"), STAT_RuntimeMesh_SetCollisionConvexMeshes, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Create Scene Proxy (GT)"), STAT_RuntimeMesh_CreateSceneProxy, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Get Physics TriMesh Data (GT)"), STAT_RuntimeMesh_GetPhysicsTriMeshData, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Update Collision (GT)"), STAT_RuntimeMesh_UpdateCollision, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Update Local Bounds (GT)"), STAT_RuntimeMesh_UpdateLocalBounds, STATGROUP_RuntimeMesh);
DECLARE_CYCLE_STAT(TEXT("Serialize"), STAT_RuntimeMesh_Serialize, STATGROUP_RuntimeMesh);

View File

@ -1,187 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#pragma once
#include "Engine.h"
#include "RuntimeMeshCore.h"
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 12
/** Structure definition of a vertex */
using RuntimeMeshVertexStructure = FLocalVertexFactory::FDataType;
#else
/** Structure definition of a vertex */
using RuntimeMeshVertexStructure = FLocalVertexFactory::DataType;
#endif
#define RUNTIMEMESH_VERTEXCOMPONENT(VertexBuffer, VertexType, Member, MemberType) \
STRUCTMEMBER_VERTEXSTREAMCOMPONENT(&VertexBuffer, VertexType, Member, MemberType)
/*
* Defines an interface for the proxy sections so that
* the vertex factory can query for section visibility
*/
class FRuntimeMeshVisibilityInterface
{
public:
virtual bool ShouldRender() = 0;
};
/** Vertex Buffer for one section. Templated to support different vertex types */
template<typename VertexType>
class FRuntimeMeshVertexBuffer : public FVertexBuffer
{
public:
FRuntimeMeshVertexBuffer(EUpdateFrequency SectionUpdateFrequency) : VertexCount(0)
{
UsageFlags = SectionUpdateFrequency == EUpdateFrequency::Frequent ? BUF_Dynamic : BUF_Static;
}
virtual void InitRHI() override
{
// Create the vertex buffer
FRHIResourceCreateInfo CreateInfo;
VertexBufferRHI = RHICreateVertexBuffer(sizeof(VertexType) * VertexCount, UsageFlags, CreateInfo);
}
/* Get the size of the vertex buffer */
int32 Num() { return VertexCount; }
/* Set the size of the vertex buffer */
void SetNum(int32 NewVertexCount)
{
check(NewVertexCount != 0);
// Make sure we're not already the right size
if (NewVertexCount != VertexCount)
{
VertexCount = NewVertexCount;
// Rebuild resource
ReleaseResource();
InitResource();
}
}
/* Set the data for the vertex buffer */
void SetData(const TArray<VertexType>& Data)
{
check(Data.Num() == VertexCount);
// Lock the vertex buffer
void* Buffer = RHILockVertexBuffer(VertexBufferRHI, 0, Data.Num() * sizeof(VertexType), RLM_WriteOnly);
// Write the vertices to the vertex buffer
FMemory::Memcpy(Buffer, Data.GetData(), Data.Num() * sizeof(VertexType));
// Unlock the vertex buffer
RHIUnlockVertexBuffer(VertexBufferRHI);
}
private:
/* The number of vertices this buffer is currently allocated to hold */
int32 VertexCount;
/* The buffer configuration to use */
EBufferUsageFlags UsageFlags;
};
/** Index Buffer */
class FRuntimeMeshIndexBuffer : public FIndexBuffer
{
public:
FRuntimeMeshIndexBuffer(EUpdateFrequency SectionUpdateFrequency) : IndexCount(0)
{
UsageFlags = SectionUpdateFrequency == EUpdateFrequency::Frequent ? BUF_Dynamic : BUF_Static;
}
virtual void InitRHI() override
{
// Create the index buffer
FRHIResourceCreateInfo CreateInfo;
IndexBufferRHI = RHICreateIndexBuffer(sizeof(int32), IndexCount * sizeof(int32), BUF_Dynamic, CreateInfo);
}
/* Get the size of the index buffer */
int32 Num() { return IndexCount; }
/* Set the size of the index buffer */
void SetNum(int32 NewIndexCount)
{
check(NewIndexCount != 0);
// Make sure we're not already the right size
if (NewIndexCount != IndexCount)
{
IndexCount = NewIndexCount;
// Rebuild resource
ReleaseResource();
InitResource();
}
}
/* Set the data for the index buffer */
void SetData(const TArray<int32>& Data)
{
check(Data.Num() == IndexCount);
// Lock the index buffer
void* Buffer = RHILockIndexBuffer(IndexBufferRHI, 0, IndexCount * sizeof(int32), RLM_WriteOnly);
// Write the indices to the vertex buffer
FMemory::Memcpy(Buffer, Data.GetData(), Data.Num() * sizeof(int32));
// Unlock the index buffer
RHIUnlockIndexBuffer(IndexBufferRHI);
}
private:
/* The number of indices this buffer is currently allocated to hold */
int32 IndexCount;
/* The buffer configuration to use */
EBufferUsageFlags UsageFlags;
};
/** Vertex Factory */
class FRuntimeMeshVertexFactory : public FLocalVertexFactory
{
public:
FRuntimeMeshVertexFactory(FRuntimeMeshVisibilityInterface* InSectionParent) : SectionParent(InSectionParent) { }
/** Init function that can be called on any thread, and will do the right thing (enqueue command if called on main thread) */
void Init(const RuntimeMeshVertexStructure VertexStructure)
{
if (IsInRenderingThread())
{
SetData(VertexStructure);
}
else
{
// Send the command to the render thread
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
InitRuntimeMeshVertexFactory,
FRuntimeMeshVertexFactory*, VertexFactory, this,
const RuntimeMeshVertexStructure, VertexStructure, VertexStructure,
{
VertexFactory->Init(VertexStructure);
});
}
}
/* Gets the section visibility for static sections */
virtual uint64 GetStaticBatchElementVisibility(const class FSceneView& View, const struct FMeshBatch* Batch) const override
{
return SectionParent->ShouldRender();
}
private:
/* Interface to the parent section for checking visibility.*/
FRuntimeMeshVisibilityInterface* SectionParent;
};

View File

@ -1,812 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#pragma once
#include "Engine.h"
#include "Components/MeshComponent.h"
#include "RuntimeMeshProfiling.h"
#include "RuntimeMeshVersion.h"
#include "RuntimeMeshSectionProxy.h"
#include "RuntimeMeshBuilder.h"
#include "RuntimeMeshLibrary.h"
/** Interface class for a single mesh section */
class FRuntimeMeshSectionInterface
{
protected:
const bool bNeedsPositionOnlyBuffer;
public:
/** Position only vertex buffer for this section */
TArray<FVector> PositionVertexBuffer;
/** Index buffer for this section */
TArray<int32> IndexBuffer;
/** Index buffer used for tessellation containing the needed adjacency info */
TArray<int32> TessellationIndexBuffer;
/** Local bounding box of section */
FBox LocalBoundingBox;
/** Should we build collision data for triangles in this section */
bool CollisionEnabled;
/** Should we display this section */
bool bIsVisible;
/** Should this section cast a shadow */
bool bCastsShadow;
/** If this section is currently using an adjacency index buffer */
bool bShouldUseAdjacencyIndexBuffer;
/** Update frequency of this section */
EUpdateFrequency UpdateFrequency;
FRuntimeMeshSectionInterface(bool bInNeedsPositionOnlyBuffer) :
bNeedsPositionOnlyBuffer(bInNeedsPositionOnlyBuffer),
LocalBoundingBox(0),
CollisionEnabled(false),
bIsVisible(true),
bCastsShadow(true),
bIsLegacySectionType(false)
{}
virtual ~FRuntimeMeshSectionInterface() { }
protected:
/** Is this an internal section type. */
bool bIsLegacySectionType;
bool IsDualBufferSection() const { return bNeedsPositionOnlyBuffer; }
/* Updates the vertex position buffer, returns whether we have a new bounding box */
bool UpdateVertexPositionBuffer(TArray<FVector>& Positions, const FBox* BoundingBox, bool bShouldMoveArray)
{
// Holds the new bounding box after this update.
FBox NewBoundingBox(0);
if (bShouldMoveArray)
{
// Move buffer data
PositionVertexBuffer = MoveTemp(Positions);
// Calculate the bounding box if one doesn't exist.
if (BoundingBox == nullptr)
{
for (int32 VertexIdx = 0; VertexIdx < PositionVertexBuffer.Num(); VertexIdx++)
{
NewBoundingBox += PositionVertexBuffer[VertexIdx];
}
}
else
{
// Copy the supplied bounding box instead of calculating it.
NewBoundingBox = *BoundingBox;
}
}
else
{
if (BoundingBox == nullptr)
{
// Copy the buffer and calculate the bounding box at the same time
int32 NumVertices = Positions.Num();
PositionVertexBuffer.SetNumUninitialized(NumVertices);
for (int32 VertexIdx = 0; VertexIdx < NumVertices; VertexIdx++)
{
NewBoundingBox += Positions[VertexIdx];
PositionVertexBuffer[VertexIdx] = Positions[VertexIdx];
}
}
else
{
// Copy the buffer
PositionVertexBuffer = Positions;
// Copy the supplied bounding box instead of calculating it.
NewBoundingBox = *BoundingBox;
}
}
// Update the bounding box if necessary and alert our caller if we did
if (!(LocalBoundingBox == NewBoundingBox))
{
LocalBoundingBox = NewBoundingBox;
return true;
}
return false;
}
virtual void UpdateVertexBuffer(IRuntimeMeshVerticesBuilder& Vertices, const FBox* BoundingBox, bool bShouldMoveArray) = 0;
void UpdateIndexBuffer(TArray<int32>& Triangles, bool bShouldMoveArray)
{
if (bShouldMoveArray)
{
IndexBuffer = MoveTemp(Triangles);
}
else
{
IndexBuffer = Triangles;
}
}
void UpdateIndexBuffer(FRuntimeMeshIndicesBuilder& Triangles, bool bShouldMoveArray)
{
if (bShouldMoveArray)
{
IndexBuffer = MoveTemp(*Triangles.GetIndices());
Triangles.Reset();
}
else
{
IndexBuffer = *Triangles.GetIndices();
}
}
void UpdateTessellationIndexBuffer(TArray<int32>& Triangles, bool bShouldMoveArray)
{
if (bShouldMoveArray)
{
TessellationIndexBuffer = MoveTemp(Triangles);
}
else
{
TessellationIndexBuffer = Triangles;
}
}
virtual FRuntimeMeshSectionCreateDataInterface* GetSectionCreationData(FSceneInterface* InScene, UMaterialInterface* InMaterial) const = 0;
virtual FRuntimeMeshRenderThreadCommandInterface* GetSectionUpdateData(bool bIncludePositionVertices, bool bIncludeVertices, bool bIncludeIndices) const = 0;
virtual FRuntimeMeshRenderThreadCommandInterface* GetSectionPositionUpdateData() const = 0;
virtual void RecalculateBoundingBox() = 0;
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 13
virtual int32 GetCollisionInformation(TArray<FVector>& Positions, TArray<TArray<FVector2D>>& UVs, bool bIncludeUVs) = 0;
#else
virtual int32 GetCollisionInformation(TArray<FVector>& Positions) = 0;
#endif
virtual void GetInternalVertexComponents(int32& NumUVChannels, bool& WantsHalfPrecisionUVs) { }
// This is only meant for internal use for supporting the old style create/update sections
virtual bool UpdateVertexBufferInternal(const TArray<FVector>& Positions, const TArray<FVector>& Normals, const TArray<FRuntimeMeshTangent>& Tangents, const TArray<FVector2D>& UV0, const TArray<FVector2D>& UV1, const TArray<FColor>& Colors) { return false; }
virtual void GetSectionMesh(IRuntimeMeshVerticesBuilder*& Vertices, FRuntimeMeshIndicesBuilder*& Indices) = 0;
virtual const FRuntimeMeshVertexTypeInfo* GetVertexType() const = 0;
virtual void GenerateNormalTangent() = 0;
virtual void GenerateTessellationIndices() = 0;
virtual void Serialize(FArchive& Ar)
{
if (Ar.CustomVer(FRuntimeMeshVersion::GUID) >= FRuntimeMeshVersion::SerializationV2)
{
if (bNeedsPositionOnlyBuffer)
{
Ar << PositionVertexBuffer;
}
Ar << IndexBuffer;
Ar << TessellationIndexBuffer;
Ar << LocalBoundingBox;
Ar << CollisionEnabled;
Ar << bIsVisible;
Ar << bCastsShadow;
Ar << bShouldUseAdjacencyIndexBuffer;
// Serialize the update frequency as an int32
int32 UpdateFreq = (int32)UpdateFrequency;
Ar << UpdateFreq;
UpdateFrequency = (EUpdateFrequency)UpdateFreq;
Ar << bIsLegacySectionType;
}
else
{
if (Ar.CustomVer(FRuntimeMeshVersion::GUID) >= FRuntimeMeshVersion::DualVertexBuffer)
{
Ar << PositionVertexBuffer;
}
Ar << IndexBuffer;
Ar << LocalBoundingBox;
Ar << CollisionEnabled;
Ar << bIsVisible;
int32 UpdateFreq = (int32)UpdateFrequency;
Ar << UpdateFreq;
UpdateFrequency = (EUpdateFrequency)UpdateFreq;
}
}
friend class FRuntimeMeshSceneProxy;
friend class URuntimeMeshComponent;
};
namespace RuntimeMeshSectionInternal
{
template<typename Type>
static typename TEnableIf<FRuntimeMeshVertexTraits<Type>::HasPosition, int32>::Type
GetAllVertexPositions(const TArray<Type>& VertexBuffer, const TArray<FVector>& PositionVertexBuffer, TArray<FVector>& Positions)
{
int32 VertexCount = VertexBuffer.Num();
for (int32 VertIdx = 0; VertIdx < VertexCount; VertIdx++)
{
Positions.Add(VertexBuffer[VertIdx].Position);
}
return VertexCount;
}
template<typename Type>
static typename TEnableIf<!FRuntimeMeshVertexTraits<Type>::HasPosition, int32>::Type
GetAllVertexPositions(const TArray<Type>& VertexBuffer, const TArray<FVector>& PositionVertexBuffer, TArray<FVector>& Positions)
{
Positions.Append(PositionVertexBuffer);
return PositionVertexBuffer.Num();
}
template<typename Type>
static typename TEnableIf<FRuntimeMeshVertexTraits<Type>::HasPosition, bool>::Type
UpdateVertexBufferInternal(TArray<Type>& VertexBuffer, FBox& LocalBoundingBox, TArray<Type>& Vertices, const FBox* BoundingBox, bool bShouldMoveArray)
{
// Holds the new bounding box after this update.
FBox NewBoundingBox(0);
if (bShouldMoveArray)
{
// Move buffer data
VertexBuffer = MoveTemp(Vertices);
// Calculate the bounding box if one doesn't exist.
if (BoundingBox == nullptr)
{
for (int32 VertexIdx = 0; VertexIdx < VertexBuffer.Num(); VertexIdx++)
{
NewBoundingBox += VertexBuffer[VertexIdx].Position;
}
}
else
{
// Copy the supplied bounding box instead of calculating it.
NewBoundingBox = *BoundingBox;
}
}
else
{
if (BoundingBox == nullptr)
{
// Copy the buffer and calculate the bounding box at the same time
int32 NumVertices = Vertices.Num();
VertexBuffer.SetNumUninitialized(NumVertices);
for (int32 VertexIdx = 0; VertexIdx < NumVertices; VertexIdx++)
{
NewBoundingBox += Vertices[VertexIdx].Position;
VertexBuffer[VertexIdx] = Vertices[VertexIdx];
}
}
else
{
// Copy the buffer
VertexBuffer = Vertices;
// Copy the supplied bounding box instead of calculating it.
NewBoundingBox = *BoundingBox;
}
}
// Update the bounding box if necessary and alert our caller if we did
if (!(LocalBoundingBox == NewBoundingBox))
{
LocalBoundingBox = NewBoundingBox;
return true;
}
return false;
}
template<typename Type>
static typename TEnableIf<!FRuntimeMeshVertexTraits<Type>::HasPosition, bool>::Type
UpdateVertexBufferInternal(TArray<Type>& VertexBuffer, FBox& LocalBoundingBox, TArray<Type>& Vertices, const FBox* BoundingBox, bool bShouldMoveArray)
{
if (bShouldMoveArray)
{
VertexBuffer = MoveTemp(Vertices);
}
else
{
VertexBuffer = Vertices;
}
return false;
}
template<typename Type>
static typename TEnableIf<FRuntimeMeshVertexTraits<Type>::HasPosition>::Type RecalculateBoundingBox(TArray<Type>& VertexBuffer, FBox& BoundingBox)
{
for (int32 Index = 0; Index < VertexBuffer.Num(); Index++)
{
BoundingBox += VertexBuffer[Index].Position;
}
}
template<typename Type>
static typename TEnableIf<!FRuntimeMeshVertexTraits<Type>::HasPosition>::Type RecalculateBoundingBox(TArray<Type>& VertexBuffer, FBox& BoundingBox)
{
}
}
/** Templated class for a single mesh section */
template<typename VertexType>
class FRuntimeMeshSection : public FRuntimeMeshSectionInterface
{
public:
/** Vertex buffer for this section */
TArray<VertexType> VertexBuffer;
FRuntimeMeshSection(bool bInNeedsPositionOnlyBuffer) : FRuntimeMeshSectionInterface(bInNeedsPositionOnlyBuffer) { }
virtual ~FRuntimeMeshSection() override { }
protected:
bool UpdateVertexBuffer(TArray<VertexType>& Vertices, const FBox* BoundingBox, bool bShouldMoveArray)
{
return RuntimeMeshSectionInternal::UpdateVertexBufferInternal<VertexType>(VertexBuffer, LocalBoundingBox, Vertices, BoundingBox, bShouldMoveArray);
}
virtual void UpdateVertexBuffer(IRuntimeMeshVerticesBuilder& Vertices, const FBox* BoundingBox, bool bShouldMoveArray) override
{
if (Vertices.GetBuilderType() == ERuntimeMeshVerticesBuilderType::Component)
{
FRuntimeMeshComponentVerticesBuilder* VerticesBuilder = static_cast<FRuntimeMeshComponentVerticesBuilder*>(&Vertices);
TArray<FVector>* Positions = VerticesBuilder->GetPositions();
TArray<FVector>* Normals = VerticesBuilder->GetNormals();
TArray<FRuntimeMeshTangent>* Tangents = VerticesBuilder->GetTangents();
TArray<FColor>* Colors = VerticesBuilder->GetColors();
TArray<FVector2D>* UV0s = VerticesBuilder->GetUV0s();
TArray<FVector2D>* UV1s = VerticesBuilder->GetUV1s();
UpdateVertexBufferInternal(
Positions ? *Positions : TArray<FVector>(),
Normals ? *Normals : TArray<FVector>(),
Tangents ? *Tangents : TArray<FRuntimeMeshTangent>(),
UV0s ? *UV0s : TArray<FVector2D>(),
UV1s ? *UV1s : TArray<FVector2D>(),
Colors ? *Colors : TArray<FColor>());
if (BoundingBox)
{
LocalBoundingBox = *BoundingBox;
}
else
{
LocalBoundingBox = FBox(*Positions);
}
if (bShouldMoveArray)
{
// This is just to keep similar behavior to the packed vertices builder.
Vertices.Reset();
}
}
else
{
// Make sure section type is the same
Vertices.GetVertexType()->EnsureEquals<VertexType>();
FRuntimeMeshPackedVerticesBuilder<VertexType>* VerticesBuilder = static_cast<FRuntimeMeshPackedVerticesBuilder<VertexType>*>(&Vertices);
RuntimeMeshSectionInternal::UpdateVertexBufferInternal<VertexType>(VertexBuffer, LocalBoundingBox, *VerticesBuilder->GetVertices(), BoundingBox, bShouldMoveArray);
if (BoundingBox == nullptr && VerticesBuilder->WantsSeparatePositionBuffer())
{
LocalBoundingBox = FBox(*VerticesBuilder->GetPositions());
}
}
}
virtual FRuntimeMeshSectionCreateDataInterface* GetSectionCreationData(FSceneInterface* InScene, UMaterialInterface* InMaterial) const override
{
auto UpdateData = new FRuntimeMeshSectionCreateData<VertexType>();
FMaterialRelevance MaterialRelevance = (InMaterial != nullptr)
? InMaterial->GetRelevance(InScene->GetFeatureLevel())
: UMaterial::GetDefaultMaterial(MD_Surface)->GetRelevance(InScene->GetFeatureLevel());
// Create new section proxy based on whether we need separate position buffer
if (IsDualBufferSection())
{
UpdateData->NewProxy = new FRuntimeMeshSectionProxy<VertexType, true>(InScene, UpdateFrequency, bIsVisible, bCastsShadow, InMaterial, MaterialRelevance);
UpdateData->PositionVertexBuffer = PositionVertexBuffer;
}
else
{
UpdateData->NewProxy = new FRuntimeMeshSectionProxy<VertexType, false>(InScene, UpdateFrequency, bIsVisible, bCastsShadow, InMaterial, MaterialRelevance);
}
const_cast<FRuntimeMeshSection*>(this)->bShouldUseAdjacencyIndexBuffer = UpdateData->NewProxy->ShouldUseAdjacencyIndexBuffer();
UpdateData->VertexBuffer = VertexBuffer;
// Switch between normal/tessellation indices
if (bShouldUseAdjacencyIndexBuffer && TessellationIndexBuffer.Num() > 0)
{
UpdateData->IndexBuffer = TessellationIndexBuffer;
UpdateData->bIsAdjacencyIndexBuffer = true;
}
else
{
UpdateData->IndexBuffer = IndexBuffer;
UpdateData->bIsAdjacencyIndexBuffer = false;
}
return UpdateData;
}
virtual FRuntimeMeshRenderThreadCommandInterface* GetSectionUpdateData(bool bIncludePositionVertices, bool bIncludeVertices, bool bIncludeIndices) const override
{
auto UpdateData = new FRuntimeMeshSectionUpdateData<VertexType>();
UpdateData->bIncludeVertexBuffer = bIncludeVertices;
UpdateData->bIncludePositionBuffer = bIncludePositionVertices;
UpdateData->bIncludeIndices = bIncludeIndices;
if (bIncludePositionVertices)
{
UpdateData->PositionVertexBuffer = PositionVertexBuffer;
}
if (bIncludeVertices)
{
UpdateData->VertexBuffer = VertexBuffer;
}
if (bIncludeIndices)
{
if (bShouldUseAdjacencyIndexBuffer && TessellationIndexBuffer.Num() > 0)
{
UpdateData->IndexBuffer = TessellationIndexBuffer;
UpdateData->bIsAdjacencyIndexBuffer = true;
}
else
{
UpdateData->IndexBuffer = IndexBuffer;
UpdateData->bIsAdjacencyIndexBuffer = false;
}
}
return UpdateData;
}
virtual FRuntimeMeshRenderThreadCommandInterface* GetSectionPositionUpdateData() const override
{
auto UpdateData = new FRuntimeMeshSectionPositionOnlyUpdateData<VertexType>();
UpdateData->PositionVertexBuffer = PositionVertexBuffer;
return UpdateData;
}
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 13
virtual int32 GetCollisionInformation(TArray<FVector>& Positions, TArray<TArray<FVector2D>>& UVs, bool bIncludeUVs) override
#else
virtual int32 GetCollisionInformation(TArray<FVector>& Positions) override
#endif
{
FRuntimeMeshPackedVerticesBuilder<VertexType> VerticesBuilder(&VertexBuffer, bNeedsPositionOnlyBuffer ? &PositionVertexBuffer : nullptr);
int32 PositionStart = Positions.Num();
Positions.SetNum(PositionStart + VerticesBuilder.Length());
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 13
if (bIncludeUVs)
{
UVs[0].SetNumZeroed(PositionStart + VerticesBuilder.Length());
}
#endif
for (int VertexIdx = 0; VertexIdx < VerticesBuilder.Length(); VertexIdx++)
{
Positions[PositionStart + VertexIdx] = VerticesBuilder.GetPosition(VertexIdx);
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 13
if (bIncludeUVs && VerticesBuilder.HasUVComponent(0))
{
UVs[0][PositionStart + VertexIdx] = VerticesBuilder.GetUV(0);
}
#endif
}
return VerticesBuilder.Length();
}
virtual void GetSectionMesh(IRuntimeMeshVerticesBuilder*& Vertices, FRuntimeMeshIndicesBuilder*& Indices) override
{
Vertices = new FRuntimeMeshPackedVerticesBuilder<VertexType>(&VertexBuffer);
Indices = new FRuntimeMeshIndicesBuilder(&IndexBuffer);
}
virtual const FRuntimeMeshVertexTypeInfo* GetVertexType() const { return &VertexType::TypeInfo; }
virtual void GenerateNormalTangent()
{
if (IsDualBufferSection())
{
URuntimeMeshLibrary::CalculateTangentsForMesh<VertexType>(PositionVertexBuffer, VertexBuffer, IndexBuffer);
}
else
{
URuntimeMeshLibrary::CalculateTangentsForMesh<VertexType>(VertexBuffer, IndexBuffer);
}
}
virtual void GenerateTessellationIndices()
{
TArray<int32> TessellationIndices;
if (IsDualBufferSection())
{
URuntimeMeshLibrary::GenerateTessellationIndexBuffer<VertexType>(PositionVertexBuffer, VertexBuffer, IndexBuffer, TessellationIndices);
}
else
{
URuntimeMeshLibrary::GenerateTessellationIndexBuffer<VertexType>(VertexBuffer, IndexBuffer, TessellationIndices);
}
UpdateTessellationIndexBuffer(TessellationIndices, true);
}
virtual void RecalculateBoundingBox() override
{
LocalBoundingBox.Init();
if (IsDualBufferSection())
{
for (int32 Index = 0; Index < PositionVertexBuffer.Num(); Index++)
{
LocalBoundingBox += PositionVertexBuffer[Index];
}
}
else
{
RuntimeMeshSectionInternal::RecalculateBoundingBox<VertexType>(VertexBuffer, LocalBoundingBox);
}
}
virtual void GetInternalVertexComponents(int32& NumUVChannels, bool& WantsHalfPrecisionUVs) override
{
NumUVChannels = FRuntimeMeshVertexTraits<VertexType>::NumUVChannels;
WantsHalfPrecisionUVs = !FRuntimeMeshVertexTraits<VertexType>::HasHighPrecisionUVs;
}
virtual bool UpdateVertexBufferInternal(const TArray<FVector>& Positions, const TArray<FVector>& Normals, const TArray<FRuntimeMeshTangent>& Tangents, const TArray<FVector2D>& UV0, const TArray<FVector2D>& UV1, const TArray<FColor>& Colors) override
{
// Check existence of data components
const bool HasPositions = Positions.Num() > 0;
int32 NewVertexCount = HasPositions ? Positions.Num() : VertexBuffer.Num();
int32 OldVertexCount = FMath::Min(VertexBuffer.Num(), NewVertexCount);
// Size the vertex buffer correctly
if (NewVertexCount != VertexBuffer.Num())
{
VertexBuffer.SetNumZeroed(NewVertexCount);
}
// Clear the bounding box if we have new positions
if (HasPositions)
{
LocalBoundingBox.Init();
}
FRuntimeMeshPackedVerticesBuilder<VertexType> VerticesBuilder(&VertexBuffer);
// Loop through existing range to update data
for (int32 VertexIdx = 0; VertexIdx < OldVertexCount; VertexIdx++)
{
VerticesBuilder.Seek(VertexIdx);
// Update position and bounding box
if (HasPositions)
{
VerticesBuilder.SetPosition(Positions[VertexIdx]);
LocalBoundingBox += Positions[VertexIdx];
}
// see if we have a new normal and/or tangent
bool HasNormal = Normals.Num() > VertexIdx;
bool HasTangent = Tangents.Num() > VertexIdx;
// Update normal and tangent together
if (HasNormal && HasTangent)
{
FVector4 NewNormal(Normals[VertexIdx], Tangents[VertexIdx].bFlipTangentY ? -1.0f : 1.0f);
VerticesBuilder.SetNormal(NewNormal);
VerticesBuilder.SetTangent(Tangents[VertexIdx].TangentX);
}
// Else update only normal keeping the W component
else if (HasNormal)
{
float W = VerticesBuilder.GetNormal().W;
VerticesBuilder.SetNormal(FVector4(Normals[VertexIdx], W));
}
// Else update tangent updating the normals W component
else if (HasTangent)
{
FVector4 Normal = VerticesBuilder.GetNormal();
Normal.W = Tangents[VertexIdx].bFlipTangentY ? -1.0f : 1.0f;
VerticesBuilder.SetNormal(Normal);
VerticesBuilder.SetTangent(Tangents[VertexIdx].TangentX);
}
// Update color
if (Colors.Num() > VertexIdx)
{
VerticesBuilder.SetColor(Colors[VertexIdx]);
}
// Update UV0
if (UV0.Num() > VertexIdx)
{
VerticesBuilder.SetUV(0, UV0[VertexIdx]);
}
// Update UV1 if needed
if (UV1.Num() > VertexIdx && VerticesBuilder.HasUVComponent(1))
{
VerticesBuilder.SetUV(1, UV1[VertexIdx]);
}
}
// Loop through additional range to add new data
for (int32 VertexIdx = OldVertexCount; VertexIdx < NewVertexCount; VertexIdx++)
{
VerticesBuilder.Seek(VertexIdx);
// Set position
VerticesBuilder.SetPosition(Positions[VertexIdx]);
// Update bounding box
LocalBoundingBox += Positions[VertexIdx];
// see if we have a new normal and/or tangent
bool HasNormal = Normals.Num() > VertexIdx;
bool HasTangent = Tangents.Num() > VertexIdx;
// Set normal and tangent both
if (HasNormal && HasTangent)
{
FVector4 NewNormal(Normals[VertexIdx], Tangents[VertexIdx].bFlipTangentY ? -1.0f : 1.0f);
VerticesBuilder.SetNormal(NewNormal);
VerticesBuilder.SetTangent(Tangents[VertexIdx].TangentX);
}
// Set normal and default tangent
else if (HasNormal)
{
VerticesBuilder.SetNormal(FVector4(Normals[VertexIdx], 1.0f));
VerticesBuilder.SetTangent(FVector(1.0f, 0.0f, 0.0f));
}
// Default normal and set tangent
else if (HasTangent)
{
VerticesBuilder.SetNormal(FVector4(0.0f, 0.0f, 1.0f, Tangents[VertexIdx].bFlipTangentY ? -1.0f : 1.0f));
VerticesBuilder.SetTangent(Tangents[VertexIdx].TangentX);
}
// Default normal and tangent
else
{
VerticesBuilder.SetNormal(FVector4(0.0f, 0.0f, 1.0f, 1.0f));
VerticesBuilder.SetTangent(FVector(1.0f, 0.0f, 0.0f));
}
// Set color or default
VerticesBuilder.SetColor(Colors.Num() > VertexIdx ? Colors[VertexIdx] : FColor::White);
// Update UV0
VerticesBuilder.SetUV(0, UV0.Num() > VertexIdx ? UV0[VertexIdx] : FVector2D::ZeroVector);
// Update UV1 if needed
if (VerticesBuilder.HasUVComponent(1))
{
VerticesBuilder.SetUV(1, UV1.Num() > VertexIdx ? UV1[VertexIdx] : FVector2D::ZeroVector);
}
}
return true;
}
private:
void SerializeLegacy(FArchive& Ar)
{
int32 VertexBufferLength = VertexBuffer.Num();
Ar << VertexBufferLength;
if (Ar.IsLoading())
{
VertexBuffer.SetNum(VertexBufferLength);
FRuntimeMeshPackedVerticesBuilder<VertexType> VerticesBuilder(&VertexBuffer);
for (int32 Index = 0; Index < VertexBufferLength; Index++)
{
VerticesBuilder.Seek(Index);
FVector TempPosition;
Ar << TempPosition;
VerticesBuilder.SetPosition(TempPosition);
FPackedNormal TempNormal;
Ar << TempNormal;
VerticesBuilder.SetNormal(TempNormal);
Ar << TempNormal;
VerticesBuilder.SetTangent(TempNormal);
FColor TempColor;
Ar << TempColor;
VerticesBuilder.SetColor(TempColor);
if (FRuntimeMeshVertexTraits<VertexType>::HasHighPrecisionUVs)
{
FVector2D TempUV;
Ar << TempUV;
VerticesBuilder.SetUV(0, TempUV);
if (FRuntimeMeshVertexTraits<VertexType>::NumUVChannels > 1)
{
Ar << TempUV;
VerticesBuilder.SetUV(1, TempUV);
}
}
else
{
FVector2DHalf TempUV;
Ar << TempUV;
VerticesBuilder.SetUV(0, TempUV);
if (FRuntimeMeshVertexTraits<VertexType>::NumUVChannels > 1)
{
Ar << TempUV;
VerticesBuilder.SetUV(1, TempUV);
}
}
}
}
else
{
check(false && "Cannot use legacy save.");
}
}
public:
virtual void Serialize(FArchive& Ar) override
{
if (Ar.CustomVer(FRuntimeMeshVersion::GUID) >= FRuntimeMeshVersion::SerializationV2)
{
Ar << VertexBuffer;
FRuntimeMeshSectionInterface::Serialize(Ar);
}
else
{
FRuntimeMeshSectionInterface::Serialize(Ar);
SerializeLegacy(Ar);
}
}
friend class URuntimeMeshComponent;
};
/** Smart pointer to a Runtime Mesh Section */
using RuntimeMeshSectionPtr = TSharedPtr<FRuntimeMeshSectionInterface>;

View File

@ -1,228 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#pragma once
#include "Engine.h"
#include "Components/MeshComponent.h"
#include "RuntimeMeshProfiling.h"
#include "RuntimeMeshVersion.h"
#include "RuntimeMeshCore.h"
#include "RuntimeMeshRendering.h"
#include "RuntimeMeshUpdateCommands.h"
/** Interface class for the RT proxy of a single mesh section */
class FRuntimeMeshSectionProxyInterface : public FRuntimeMeshVisibilityInterface
{
public:
FRuntimeMeshSectionProxyInterface() {}
virtual ~FRuntimeMeshSectionProxyInterface() {}
virtual bool ShouldRender() = 0;
virtual bool WantsToRenderInStaticPath() const = 0;
virtual bool ShouldUseAdjacencyIndexBuffer() const = 0;
virtual FMaterialRelevance GetMaterialRelevance() const = 0;
virtual void CreateMeshBatch(FMeshBatch& MeshBatch, FMaterialRenderProxy* WireframeMaterial, bool bIsSelected) = 0;
virtual void FinishCreate_RenderThread(FRuntimeMeshSectionCreateDataInterface* UpdateData) = 0;
virtual void FinishUpdate_RenderThread(FRuntimeMeshRenderThreadCommandInterface* UpdateData) = 0;
virtual void FinishPositionUpdate_RenderThread(FRuntimeMeshRenderThreadCommandInterface* UpdateData) = 0;
virtual void FinishPropertyUpdate_RenderThread(FRuntimeMeshRenderThreadCommandInterface* UpdateData) = 0;
};
/** Templated class for the RT proxy of a single mesh section */
template <typename VertexType, bool NeedsPositionOnlyBuffer>
class FRuntimeMeshSectionProxy : public FRuntimeMeshSectionProxyInterface
{
protected:
/** Whether this section is currently visible */
bool bIsVisible;
/** Should this section cast a shadow */
bool bCastsShadow;
/** Whether this section should be using an adjacency index buffer */
bool bShouldUseAdjacency;
/** Whether this section is using a tessellation adjacency index buffer */
bool bIsUsingAdjacency;
/** Update frequency of this section */
const EUpdateFrequency UpdateFrequency;
/** Material applied to this section */
UMaterialInterface* Material;
FMaterialRelevance MaterialRelevance;
FRuntimeMeshVertexBuffer<FVector>* PositionVertexBuffer;
/** Vertex buffer for this section */
FRuntimeMeshVertexBuffer<VertexType> VertexBuffer;
/** Index buffer for this section */
FRuntimeMeshIndexBuffer IndexBuffer;
/** Vertex factory for this section */
FRuntimeMeshVertexFactory VertexFactory;
public:
FRuntimeMeshSectionProxy(FSceneInterface* InScene, EUpdateFrequency InUpdateFrequency, bool bInIsVisible, bool bInCastsShadow, UMaterialInterface* InMaterial, FMaterialRelevance InMaterialRelevance) :
bIsVisible(bInIsVisible), bCastsShadow(bInCastsShadow), UpdateFrequency(InUpdateFrequency), Material(InMaterial), MaterialRelevance(InMaterialRelevance),
PositionVertexBuffer(nullptr), VertexBuffer(InUpdateFrequency), IndexBuffer(InUpdateFrequency), VertexFactory(this)
{
bShouldUseAdjacency = RequiresAdjacencyInformation(InMaterial, VertexFactory.GetType(), InScene->GetFeatureLevel());
}
virtual ~FRuntimeMeshSectionProxy() override
{
VertexBuffer.ReleaseResource();
IndexBuffer.ReleaseResource();
VertexFactory.ReleaseResource();
if (PositionVertexBuffer)
{
PositionVertexBuffer->ReleaseResource();
delete PositionVertexBuffer;
}
}
virtual bool ShouldRender() override { return bIsVisible && VertexBuffer.Num() > 0 && IndexBuffer.Num() > 0; }
virtual bool WantsToRenderInStaticPath() const override { return UpdateFrequency == EUpdateFrequency::Infrequent; }
virtual bool ShouldUseAdjacencyIndexBuffer() const override { return bShouldUseAdjacency; }
virtual FMaterialRelevance GetMaterialRelevance() const { return MaterialRelevance; }
virtual void CreateMeshBatch(FMeshBatch& MeshBatch, FMaterialRenderProxy* WireframeMaterial, bool bIsSelected) override
{
MeshBatch.VertexFactory = &VertexFactory;
MeshBatch.bWireframe = WireframeMaterial != nullptr;
MeshBatch.MaterialRenderProxy = MeshBatch.bWireframe ? WireframeMaterial : Material->GetRenderProxy(bIsSelected);
if (bIsUsingAdjacency && WireframeMaterial == nullptr)
{
MeshBatch.Type = PT_12_ControlPointPatchList;
}
else
{
MeshBatch.Type = PT_TriangleList;
}
MeshBatch.DepthPriorityGroup = SDPG_World;
MeshBatch.CastShadow = bCastsShadow;
FMeshBatchElement& BatchElement = MeshBatch.Elements[0];
BatchElement.IndexBuffer = &IndexBuffer;
BatchElement.FirstIndex = 0;
BatchElement.NumPrimitives = bIsUsingAdjacency? IndexBuffer.Num() / 12 : IndexBuffer.Num() / 3;
BatchElement.MinVertexIndex = 0;
BatchElement.MaxVertexIndex = VertexBuffer.Num() - 1;
}
virtual void FinishCreate_RenderThread(FRuntimeMeshSectionCreateDataInterface* UpdateData) override
{
check(IsInRenderingThread());
auto* SectionUpdateData = UpdateData->As<FRuntimeMeshSectionCreateData<VertexType>>();
check(SectionUpdateData);
if (NeedsPositionOnlyBuffer)
{
// Initialize the position buffer
PositionVertexBuffer = new FRuntimeMeshVertexBuffer<FVector>(UpdateFrequency);
// Get and adjust the vertex structure
auto VertexStructure = VertexType::GetVertexStructure(VertexBuffer);
VertexStructure.PositionComponent = FVertexStreamComponent(PositionVertexBuffer, 0, sizeof(FVector), VET_Float3);
VertexFactory.Init(VertexStructure);
}
else
{
// Get and submit the vertex structure
auto VertexStructure = VertexType::GetVertexStructure(VertexBuffer);
VertexFactory.Init(VertexStructure);
}
// Initialize the vertex factory
VertexFactory.InitResource();
auto& Vertices = SectionUpdateData->VertexBuffer;
VertexBuffer.SetNum(Vertices.Num());
VertexBuffer.SetData(Vertices);
if (NeedsPositionOnlyBuffer)
{
auto& PositionVertices = SectionUpdateData->PositionVertexBuffer;
PositionVertexBuffer->SetNum(PositionVertices.Num());
PositionVertexBuffer->SetData(PositionVertices);
}
auto& Indices = SectionUpdateData->IndexBuffer;
IndexBuffer.SetNum(Indices.Num());
IndexBuffer.SetData(Indices);
bIsUsingAdjacency = SectionUpdateData->bIsAdjacencyIndexBuffer;
}
virtual void FinishUpdate_RenderThread(FRuntimeMeshRenderThreadCommandInterface* UpdateData) override
{
check(IsInRenderingThread());
auto* SectionUpdateData = UpdateData->As<FRuntimeMeshSectionUpdateData<VertexType>>();
check(SectionUpdateData);
if (SectionUpdateData->bIncludeVertexBuffer)
{
auto& VertexBufferData = SectionUpdateData->VertexBuffer;
VertexBuffer.SetNum(VertexBufferData.Num());
VertexBuffer.SetData(VertexBufferData);
}
if (NeedsPositionOnlyBuffer && SectionUpdateData->bIncludePositionBuffer)
{
auto& PositionVertices = SectionUpdateData->PositionVertexBuffer;
PositionVertexBuffer->SetNum(PositionVertices.Num());
PositionVertexBuffer->SetData(PositionVertices);
}
if (SectionUpdateData->bIncludeIndices)
{
auto& IndexBufferData = SectionUpdateData->IndexBuffer;
IndexBuffer.SetNum(IndexBufferData.Num());
IndexBuffer.SetData(IndexBufferData);
bIsUsingAdjacency = SectionUpdateData->bIsAdjacencyIndexBuffer;
}
}
virtual void FinishPositionUpdate_RenderThread(FRuntimeMeshRenderThreadCommandInterface* UpdateData) override
{
check(IsInRenderingThread());
// Get the Position Only update data
auto* SectionUpdateData = UpdateData->As<FRuntimeMeshSectionPositionOnlyUpdateData<VertexType>>();
check(SectionUpdateData);
// Copy the new data to the gpu
PositionVertexBuffer->SetData(SectionUpdateData->PositionVertexBuffer);
}
virtual void FinishPropertyUpdate_RenderThread(FRuntimeMeshRenderThreadCommandInterface* UpdateData) override
{
auto* SectionUpdateData = UpdateData->As<FRuntimeMeshSectionPropertyUpdateData>();
check(SectionUpdateData);
// Copy visibility/shadow
bIsVisible = SectionUpdateData->bIsVisible;
bCastsShadow = SectionUpdateData->bCastsShadow;
}
};

View File

@ -1,257 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#pragma once
#include "Engine.h"
#include "Components/MeshComponent.h"
#include "RuntimeMeshProfiling.h"
#include "RuntimeMeshVersion.h"
/* Base class for all render thread command information */
class FRuntimeMeshRenderThreadCommandInterface
{
public:
FRuntimeMeshRenderThreadCommandInterface() { }
virtual ~FRuntimeMeshRenderThreadCommandInterface() { }
virtual void SetTargetSection(int32 InTargetSection) { TargetSection = InTargetSection; }
virtual int32 GetTargetSection() { return TargetSection; }
/* Cast the update data to the specific type of update data */
template <typename Type>
Type* As()
{
return static_cast<Type*>(this);
}
private:
/* Section index that this creation data applies to */
int32 TargetSection;
};
/* Base class for section creation data. Allows the non templated SceneProxy to get the section proxy;*/
class FRuntimeMeshSectionCreateDataInterface : public FRuntimeMeshRenderThreadCommandInterface
{
public:
/* The new proxy to be used for section creation */
class FRuntimeMeshSectionProxyInterface* NewProxy;
FRuntimeMeshSectionCreateDataInterface() { }
virtual ~FRuntimeMeshSectionCreateDataInterface() override { }
};
/** Templated class for update data sent to the RT for updating a single mesh section */
template<typename VertexType>
class FRuntimeMeshSectionCreateData : public FRuntimeMeshSectionCreateDataInterface
{
public:
/* Updated position vertex buffer for the section */
TArray<FVector> PositionVertexBuffer;
/* Updated vertex buffer for the section */
TArray<VertexType> VertexBuffer;
/* Whether the supplied index buffer contains adjacency info */
bool bIsAdjacencyIndexBuffer;
/* Updated index buffer for the section */
TArray<int32> IndexBuffer;
FRuntimeMeshSectionCreateData() {}
virtual ~FRuntimeMeshSectionCreateData() override { }
};
/** Templated class for update data sent to the RT for updating a single mesh section */
template<typename VertexType>
class FRuntimeMeshSectionUpdateData : public FRuntimeMeshRenderThreadCommandInterface
{
public:
/* Updated position vertex buffer for the section */
TArray<FVector> PositionVertexBuffer;
/* Updated vertex buffer for the section */
TArray<VertexType> VertexBuffer;
/* Updated index buffer for the section */
TArray<int32> IndexBuffer;
/* Should we apply the position buffer */
bool bIncludePositionBuffer;
/* Should we apply the vertex buffer */
bool bIncludeVertexBuffer;
/* Should we apply the indices as an update */
bool bIncludeIndices;
/* Whether the supplied index buffer contains adjacency info */
bool bIsAdjacencyIndexBuffer;
FRuntimeMeshSectionUpdateData() {}
virtual ~FRuntimeMeshSectionUpdateData() override { }
};
/** Templated class for update data sent to the RT for updating a single mesh section */
template<typename VertexType>
class FRuntimeMeshSectionPositionOnlyUpdateData : public FRuntimeMeshRenderThreadCommandInterface
{
public:
/* Updated position vertex buffer for the section */
TArray<FVector> PositionVertexBuffer;
FRuntimeMeshSectionPositionOnlyUpdateData() {}
virtual ~FRuntimeMeshSectionPositionOnlyUpdateData() override { }
};
/** Property update for a single section */
class FRuntimeMeshSectionPropertyUpdateData : public FRuntimeMeshRenderThreadCommandInterface
{
public:
/* Is this section visible */
bool bIsVisible;
/* Is this section casting shadows */
bool bCastsShadow;
FRuntimeMeshSectionPropertyUpdateData() {}
virtual ~FRuntimeMeshSectionPropertyUpdateData() override { }
};
enum class ERuntimeMeshSectionBatchUpdateType
{
None = 0x0,
Create = 0x1,
Destroy = 0x2,
PositionsUpdate = 0x4,
VerticesUpdate = 0x8,
IndicesUpdate = 0x10,
PropertyUpdate = 0x20,
};
ENUM_CLASS_FLAGS(ERuntimeMeshSectionBatchUpdateType)
/* Struct carrying all update data for a batch update sent to the render thread */
struct FRuntimeMeshBatchUpdateData
{
TArray<FRuntimeMeshSectionCreateDataInterface*> CreateSections;
TArray<int32> DestroySections;
TArray<FRuntimeMeshRenderThreadCommandInterface*> UpdateSections;
TArray<FRuntimeMeshSectionPropertyUpdateData*> PropertyUpdateSections;
};
struct FRuntimeMeshBatchUpdateState
{
void StartBatch() { bIsPending = true; }
void ResetBatch()
{
bIsPending = false;
bRequiresSceneProxyReCreate = false;
bRequiresBoundsUpdate = false;
bRequiresCollisionUpdate = false;
SectionUpdates.Empty();
}
bool IsBatchPending() { return bIsPending; }
void MarkSectionCreated(int32 SectionIndex, bool bPromoteToProxyRecreate)
{
// Flag recreate instead of individual section
if (bPromoteToProxyRecreate)
{
bRequiresSceneProxyReCreate = true;
return;
}
EnsureUpdateLength(SectionIndex);
// Clear destroyed flag and set created
SectionUpdates[SectionIndex] &= ~ERuntimeMeshSectionBatchUpdateType::Destroy;
SectionUpdates[SectionIndex] |= ERuntimeMeshSectionBatchUpdateType::Create;
}
void MarkUpdateForSection(int32 SectionIndex, ERuntimeMeshSectionBatchUpdateType UpdateType)
{
EnsureUpdateLength(SectionIndex);
// Add update type
SectionUpdates[SectionIndex] |= UpdateType;
}
void MarkSectionDestroyed(int32 SectionIndex, bool bPromoteToProxyRecreate)
{
// Flag recreate instead of individual section
if (bPromoteToProxyRecreate)
{
bRequiresSceneProxyReCreate = true;
return;
}
EnsureUpdateLength(SectionIndex);
// Clear destroyed flag and set created
SectionUpdates[SectionIndex] &= ~ERuntimeMeshSectionBatchUpdateType::Create;
SectionUpdates[SectionIndex] |= ERuntimeMeshSectionBatchUpdateType::Destroy;
}
void MarkRenderStateDirty() { bRequiresSceneProxyReCreate = true; }
void MarkCollisionDirty() { bRequiresCollisionUpdate = true; }
void MarkBoundsDirty() { bRequiresBoundsUpdate = true; }
bool HasAnyFlagSet(int32 SectionIndex) { return SectionUpdates[SectionIndex] != ERuntimeMeshSectionBatchUpdateType::None; }
bool HasFlagSet(int32 SectionIndex, ERuntimeMeshSectionBatchUpdateType UpdateType)
{
return (SectionUpdates[SectionIndex] & UpdateType) == UpdateType;
}
bool RequiresSceneProxyRecreate() { return bRequiresSceneProxyReCreate; }
bool RequiresBoundsUpdate() { return bRequiresBoundsUpdate; }
bool RequiresCollisionUpdate() { return bRequiresCollisionUpdate; }
int32 GetMaxSection() { return SectionUpdates.Num() - 1; }
private:
void EnsureUpdateLength(int32 SectionIndex)
{
if (SectionIndex < SectionUpdates.Num())
{
return;
}
SectionUpdates.AddZeroed((SectionIndex + 1) - SectionUpdates.Num());
}
bool bIsPending;
bool bRequiresSceneProxyReCreate;
bool bRequiresBoundsUpdate;
bool bRequiresCollisionUpdate;
TArray<ERuntimeMeshSectionBatchUpdateType> SectionUpdates;
};

View File

@ -1,27 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#pragma once
#include "RuntimeMeshComponentPlugin.h"
// Custom version for runtime mesh serialization
namespace FRuntimeMeshVersion
{
enum Type
{
Initial = 0,
TemplatedVertexFix = 1,
SerializationOptional = 2,
DualVertexBuffer = 3,
SerializationV2 = 4,
// -----<new versions can be added above this line>-------------------------------------------------
VersionPlusOne,
LatestVersion = VersionPlusOne - 1
};
// The GUID for this custom version
const static FGuid GUID = FGuid(0xEE48714B, 0x8A2C4652, 0x98BE40E6, 0xCB7EF0E6);
};

View File

@ -1,24 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
using UnrealBuildTool;
public class RuntimeMeshComponent : ModuleRules
{
public RuntimeMeshComponent(TargetInfo Target)
{
PrivateIncludePaths.Add("RuntimeMeshComponent/Private");
PublicIncludePaths.Add("RuntimeMeshComponent/Public");
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"CoreUObject",
"Engine",
"RenderCore",
"ShaderCore",
"RHI"
}
);
}
}

View File

@ -1,265 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#include "RuntimeMeshComponentEditorPrivatePCH.h"
#include "RuntimeMeshComponentDetails.h"
#include "RuntimeMeshComponent.h"
#include "DlgPickAssetPath.h"
#include "IAssetTools.h"
#include "AssetToolsModule.h"
#include "AssetRegistryModule.h"
#include "PhysicsEngine/PhysicsSettings.h"
#include "PhysicsEngine/BodySetup.h"
#define LOCTEXT_NAMESPACE "RuntimeMeshComponentDetails"
TSharedRef<IDetailCustomization> FRuntimeMeshComponentDetails::MakeInstance()
{
return MakeShareable(new FRuntimeMeshComponentDetails);
}
void FRuntimeMeshComponentDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder )
{
IDetailCategoryBuilder& RuntimeMeshCategory = DetailBuilder.EditCategory("RuntimeMesh");
const FText ConvertToStaticMeshText = LOCTEXT("ConvertToStaticMesh", "Create StaticMesh");
// Cache set of selected things
SelectedObjectsList = DetailBuilder.GetDetailsView().GetSelectedObjects();
RuntimeMeshCategory.AddCustomRow(ConvertToStaticMeshText, false)
.NameContent()
[
SNullWidget::NullWidget
]
.ValueContent()
.VAlign(VAlign_Center)
.MaxDesiredWidth(250)
[
SNew(SButton)
.VAlign(VAlign_Center)
.ToolTipText(LOCTEXT("ConvertToStaticMeshTooltip", "Create a new StaticMesh asset using current geometry from this RuntimeMeshComponent. Does not modify instance."))
.OnClicked(this, &FRuntimeMeshComponentDetails::ClickedOnConvertToStaticMesh)
.IsEnabled(this, &FRuntimeMeshComponentDetails::ConvertToStaticMeshEnabled)
.Content()
[
SNew(STextBlock)
.Text(ConvertToStaticMeshText)
]
];
}
URuntimeMeshComponent* FRuntimeMeshComponentDetails::GetFirstSelectedRuntimeMeshComp() const
{
// Find first selected valid RuntimeMeshComp
URuntimeMeshComponent* RuntimeMeshComp = nullptr;
for (const TWeakObjectPtr<UObject>& Object : SelectedObjectsList)
{
URuntimeMeshComponent* TestRuntimeComp = Cast<URuntimeMeshComponent>(Object.Get());
// See if this one is good
if (TestRuntimeComp != nullptr && !TestRuntimeComp->IsTemplate())
{
RuntimeMeshComp = TestRuntimeComp;
break;
}
}
return RuntimeMeshComp;
}
bool FRuntimeMeshComponentDetails::ConvertToStaticMeshEnabled() const
{
return GetFirstSelectedRuntimeMeshComp() != nullptr;
}
FReply FRuntimeMeshComponentDetails::ClickedOnConvertToStaticMesh()
{
// Find first selected RuntimeMeshComp
URuntimeMeshComponent* RuntimeMeshComp = GetFirstSelectedRuntimeMeshComp();
if (RuntimeMeshComp != nullptr)
{
FString NewNameSuggestion = FString(TEXT("RuntimeMeshComp"));
FString PackageName = FString(TEXT("/Game/Meshes/")) + NewNameSuggestion;
FString Name;
FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
AssetToolsModule.Get().CreateUniqueAssetName(PackageName, TEXT(""), PackageName, Name);
TSharedPtr<SDlgPickAssetPath> PickAssetPathWidget =
SNew(SDlgPickAssetPath)
.Title(LOCTEXT("ConvertToStaticMeshPickName", "Choose New StaticMesh Location"))
.DefaultAssetPath(FText::FromString(PackageName));
if (PickAssetPathWidget->ShowModal() == EAppReturnType::Ok)
{
// Get the full name of where we want to create the physics asset.
FString UserPackageName = PickAssetPathWidget->GetFullAssetPath().ToString();
FName MeshName(*FPackageName::GetLongPackageAssetName(UserPackageName));
// Check if the user inputed a valid asset name, if they did not, give it the generated default name
if (MeshName == NAME_None)
{
// Use the defaults that were already generated.
UserPackageName = PackageName;
MeshName = *Name;
}
// Raw mesh data we are filling in
FRawMesh RawMesh;
// Unique Materials to apply to new mesh
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
TArray<FStaticMaterial> Materials;
#else
TArray<UMaterialInterface*> Materials;
#endif
bool bUseHighPrecisionTangents = false;
bool bUseFullPrecisionUVs = false;
const int32 NumSections = RuntimeMeshComp->GetNumSections();
int32 VertexBase = 0;
for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
{
const IRuntimeMeshVerticesBuilder* Vertices;
const FRuntimeMeshIndicesBuilder* Indices;
RuntimeMeshComp->GetSectionMesh(SectionIdx, Vertices, Indices);
if (Vertices->HasHighPrecisionNormals())
{
bUseHighPrecisionTangents = true;
}
if (Vertices->HasHighPrecisionUVs())
{
bUseFullPrecisionUVs = true;
}
// Copy verts
Vertices->Seek(-1);
while (Vertices->MoveNext() < Vertices->Length())
{
RawMesh.VertexPositions.Add(Vertices->GetPosition());
}
// Copy 'wedge' info
Indices->Seek(0);
while (Indices->HasRemaining())
{
int32 Index = Indices->ReadOne();
RawMesh.WedgeIndices.Add(Index + VertexBase);
Vertices->Seek(Index);
FVector TangentX = Vertices->GetTangent();
FVector TangentZ = Vertices->GetNormal();
FVector TangentY = (TangentX ^ TangentZ).GetSafeNormal() * Vertices->GetNormal().W;
RawMesh.WedgeTangentX.Add(TangentX);
RawMesh.WedgeTangentY.Add(TangentY);
RawMesh.WedgeTangentZ.Add(TangentZ);
for (int UVIndex = 0; UVIndex < 8; UVIndex++)
{
if (!Vertices->HasUVComponent(UVIndex))
{
break;
}
RawMesh.WedgeTexCoords[UVIndex].Add(Vertices->GetUV(UVIndex));
}
RawMesh.WedgeColors.Add(Vertices->GetColor());
}
// Find a material index for this section.
UMaterialInterface* Material = RuntimeMeshComp->GetMaterial(SectionIdx);
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
int32 MaterialIndex = Materials.AddUnique(FStaticMaterial(Material));
#else
int32 MaterialIndex = Materials.AddUnique(Material);
#endif
// copy face info
int32 NumTris = Indices->Length() / 3;
for (int32 TriIdx=0; TriIdx < NumTris; TriIdx++)
{
// Set the face material
RawMesh.FaceMaterialIndices.Add(MaterialIndex);
RawMesh.FaceSmoothingMasks.Add(0); // Assume this is ignored as bRecomputeNormals is false
}
// Update offset for creating one big index/vertex buffer
VertexBase += Vertices->Length();
}
// If we got some valid data.
if (RawMesh.VertexPositions.Num() >= 3 && RawMesh.WedgeIndices.Num() >= 3)
{
// Then find/create it.
UPackage* Package = CreatePackage(NULL, *UserPackageName);
check(Package);
// Create StaticMesh object
UStaticMesh* StaticMesh = NewObject<UStaticMesh>(Package, MeshName, RF_Public | RF_Standalone);
StaticMesh->InitResources();
StaticMesh->LightingGuid = FGuid::NewGuid();
// Add source to new StaticMesh
FStaticMeshSourceModel* SrcModel = new (StaticMesh->SourceModels) FStaticMeshSourceModel();
SrcModel->BuildSettings.bRecomputeNormals = false;
SrcModel->BuildSettings.bRecomputeTangents = false;
SrcModel->BuildSettings.bRemoveDegenerates = false;
SrcModel->BuildSettings.bUseHighPrecisionTangentBasis = bUseHighPrecisionTangents;
SrcModel->BuildSettings.bUseFullPrecisionUVs = bUseFullPrecisionUVs;
SrcModel->BuildSettings.bGenerateLightmapUVs = true;
SrcModel->BuildSettings.SrcLightmapIndex = 0;
SrcModel->BuildSettings.DstLightmapIndex = 1;
SrcModel->RawMeshBulkData->SaveRawMesh(RawMesh);
// Set the materials used for this static mesh
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
StaticMesh->StaticMaterials = Materials;
int32 NumMaterials = StaticMesh->StaticMaterials.Num();
#else
StaticMesh->Materials = Materials;
int32 NumMaterials = StaticMesh->Materials.Num();
#endif
// Set up the SectionInfoMap to enable collision
for (int32 SectionIdx = 0; SectionIdx < NumMaterials; SectionIdx++)
{
FMeshSectionInfo Info = StaticMesh->SectionInfoMap.Get(0, SectionIdx);
Info.MaterialIndex = SectionIdx;
Info.bEnableCollision = true;
StaticMesh->SectionInfoMap.Set(0, SectionIdx, Info);
}
// Configure body setup for working collision.
StaticMesh->CreateBodySetup();
StaticMesh->BodySetup->CollisionTraceFlag = CTF_UseComplexAsSimple;
// Build mesh from source
StaticMesh->Build(false);
// Make package dirty.
StaticMesh->MarkPackageDirty();
StaticMesh->PostEditChange();
// Notify asset registry of new asset
FAssetRegistryModule::AssetCreated(StaticMesh);
}
}
}
return FReply::Handled();
}
#undef LOCTEXT_NAMESPACE

View File

@ -1,27 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#pragma once
#include "IDetailCustomization.h"
#include "DetailLayoutBuilder.h"
class FRuntimeMeshComponentDetails : public IDetailCustomization
{
public:
/** Makes a new instance of this detail layout class for a specific detail view requesting it */
static TSharedRef<IDetailCustomization> MakeInstance();
/** IDetailCustomization interface */
virtual void CustomizeDetails( IDetailLayoutBuilder& DetailBuilder ) override;
/** Handle clicking the convert button */
FReply ClickedOnConvertToStaticMesh();
/** Is the convert button enabled */
bool ConvertToStaticMeshEnabled() const;
/** Util to get the RuntimeMeshComponent we want to convert */
class URuntimeMeshComponent* GetFirstSelectedRuntimeMeshComp() const;
/** Cached array of selected objects */
TArray< TWeakObjectPtr<UObject> > SelectedObjectsList;
};

View File

@ -1,31 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#include "RuntimeMeshComponentEditorPrivatePCH.h"
class FRuntimeMeshComponentEditorPlugin : public IRuntimeMeshComponentEditorPlugin
{
/** IModuleInterface implementation */
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};
IMPLEMENT_MODULE( FRuntimeMeshComponentEditorPlugin, RuntimeMeshComponentEditor )
void FRuntimeMeshComponentEditorPlugin::StartupModule()
{
{
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
PropertyModule.RegisterCustomClassLayout(URuntimeMeshComponent::StaticClass()->GetFName(), FOnGetDetailCustomizationInstance::CreateStatic(&FRuntimeMeshComponentDetails::MakeInstance));
}
}
void FRuntimeMeshComponentEditorPlugin::ShutdownModule()
{
}

View File

@ -1,15 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#include "UnrealEd.h"
#include "PropertyEditorModule.h"
#include "DetailLayoutBuilder.h"
#include "DetailCategoryBuilder.h"
#include "DetailWidgetRow.h"
#include "IDetailsView.h"
#include "PhysicsEngine/ConvexElem.h"
#include "RuntimeMeshComponent.h"
#include "Engine/StaticMesh.h"
#include "RawMesh.h"
#include "IRuntimeMeshComponentEditorPlugin.h"
#include "RuntimeMeshComponentDetails.h"

View File

@ -1,36 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
#pragma once
#include "ModuleManager.h"
/**
* The public interface to this module
*/
class IRuntimeMeshComponentEditorPlugin : public IModuleInterface
{
public:
/**
* Singleton-like access to this module's interface. This is just for convenience!
* Beware of calling this during the shutdown phase, though. Your module might have been unloaded already.
*
* @return Returns singleton instance, loading the module on demand if needed
*/
static inline IRuntimeMeshComponentEditorPlugin& Get()
{
return FModuleManager::LoadModuleChecked< IRuntimeMeshComponentEditorPlugin >("RuntimeMeshComponentEditorPlugin");
}
/**
* Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true.
*
* @return True if the module is loaded and ready to use
*/
static inline bool IsAvailable()
{
return FModuleManager::Get().IsModuleLoaded("RuntimeMeshComponentEditorPlugin");
}
};

View File

@ -1,33 +0,0 @@
// Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
namespace UnrealBuildTool.Rules
{
public class RuntimeMeshComponentEditor : ModuleRules
{
public RuntimeMeshComponentEditor(TargetInfo Target)
{
PrivateIncludePaths.Add("RuntimeMeshComponentEditor/Private");
PublicIncludePaths.Add("RuntimeMeshComponentEditor/Public");
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
"CoreUObject",
"Slate",
"SlateCore",
"Engine",
"UnrealEd",
"PropertyEditor",
"RenderCore",
"ShaderCore",
"RHI",
"RuntimeMeshComponent",
"RawMesh",
"AssetTools",
"AssetRegistry"
}
);
}
}
}

View File

@ -7,10 +7,6 @@ This Spine Runtime may only be used for personal or internal use, typically to e
The Spine Runtimes are developed with the intent to be used with data exported from Spine. By purchasing Spine, `Section 2` of the [Spine Software License](https://esotericsoftware.com/files/license.txt) grants the right to create and distribute derivative works of the Spine Runtimes.
## 3rd Party Licenses
spine-ue4 includes a copy of [UE4 Runtime Mesh Component](https://github.com/Koderz/UE4RuntimeMeshComponent) by Chris Conway (Koderz), licensed under the [MIT license](https://github.com/Koderz/UE4RuntimeMeshComponent/blob/master/LICENSE).
## Spine version
spine-ue4 works with data exported from Spine 3.4.02.