Aollero/building impostor texture (#6379)

* Impostor texture SceneCapture component

* SceneComponment distance issue solved with ortho camera

* Dynamic  Scene Component Creation

* Procedural material duplication for new building

* Generate Impostor Geometry

* Impostor texture SceneCapture component

* SceneComponment distance issue solved with ortho camera

* Dynamic  Scene Component Creation

* Procedural material duplication for new building

* Generate Impostor Geometry

* Style updated

* Update ProceduralBuildingUtilities.h

* Ubuntu error fixed

---------

Co-authored-by: aollero <aollero@cvc.uab.cat>
This commit is contained in:
adrian-ollero 2023-04-24 11:37:50 +02:00 committed by GitHub
parent a9819fa289
commit 89a4cf29fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 415 additions and 4 deletions

View File

@ -4,16 +4,88 @@
#include "ProceduralBuildingUtilities.h"
#include "AssetRegistryModule.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Components/StaticMeshComponent.h"
#include "Engine/TextureRenderTarget2D.h"
#include "FileHelpers.h"
#include "GameFramework/Actor.h"
#include "IMeshMergeUtilities.h"
#include "Materials/MaterialInstanceConstant.h"
#include "MeshMergeModule.h"
#include "ProceduralMeshComponent.h"
#include "UObject/Class.h"
#include "UObject/UObjectGlobals.h"
void AProceduralBuildingUtilities::GenerateImpostor()
void AProceduralBuildingUtilities::GenerateImpostorTexture(const FVector& BuildingSize)
{
// Not implemented yet.
USceneCaptureComponent2D* Camera = NewObject<USceneCaptureComponent2D>(
this, USceneCaptureComponent2D::StaticClass(), TEXT("ViewProjectionCaptureComponent"));
Camera->AttachToComponent(
GetRootComponent(),
FAttachmentTransformRules::SnapToTargetNotIncludingScale,
FName("ViewProjectionCaptureComponentName"));
AddInstanceComponent(Camera);
Camera->OnComponentCreated();
Camera->RegisterComponent();
check(Camera!=nullptr);
Camera->ProjectionType = ECameraProjectionMode::Orthographic;
Camera->PrimitiveRenderMode = ESceneCapturePrimitiveRenderMode::PRM_UseShowOnlyList;
Camera->CaptureSource = ESceneCaptureSource::SCS_BaseColor;
if(Camera->ShowOnlyActors.Num() == 0)
{
Camera->ShowOnlyActors.Add(this);
}
SetTargetTextureToSceneCaptureComponent(Camera);
check(Camera->TextureTarget != nullptr);
// FRONT View
RenderImpostorView(Camera, BuildingSize, EBuildingCameraView::FRONT);
// LEFT View
RenderImpostorView(Camera, BuildingSize, EBuildingCameraView::LEFT);
// BACK View
RenderImpostorView(Camera, BuildingSize, EBuildingCameraView::BACK);
// RIGHT View
RenderImpostorView(Camera, BuildingSize, EBuildingCameraView::RIGHT);
Camera->DestroyComponent();
}
UProceduralMeshComponent* AProceduralBuildingUtilities::GenerateImpostorGeometry(const FVector& BuildingSize)
{
// Spawn procedural mesh actor / component
UProceduralMeshComponent* Mesh = NewObject<UProceduralMeshComponent>(
this, UProceduralMeshComponent::StaticClass(), TEXT("LOD Impostor Mesh"));
Mesh->AttachToComponent(
GetRootComponent(),
FAttachmentTransformRules::SnapToTargetNotIncludingScale,
FName("LOD Impostor Mesh"));
AddInstanceComponent(Mesh);
Mesh->OnComponentCreated();
Mesh->RegisterComponent();
check(Mesh != nullptr)
// FRONT View
CreateBuildingImpostorGeometryForView(Mesh, BuildingSize, EBuildingCameraView::FRONT);
// LEFT View
CreateBuildingImpostorGeometryForView(Mesh, BuildingSize, EBuildingCameraView::LEFT);
// BACK View
CreateBuildingImpostorGeometryForView(Mesh, BuildingSize, EBuildingCameraView::BACK);
// RIGHT View
CreateBuildingImpostorGeometryForView(Mesh, BuildingSize, EBuildingCameraView::RIGHT);
return Mesh;
// Cook new mesh to a static mesh
// Assign as LOD or store on disk for manual import
}
void AProceduralBuildingUtilities::CookProceduralBuildingToMesh(const FString& DestinationPath, const FString& FileName)
@ -40,6 +112,7 @@ void AProceduralBuildingUtilities::CookProceduralBuildingToMesh(const FString& D
FString PackageName = DestinationPath + FileName;
UPackage* NewPackage = CreatePackage(*PackageName);
check(NewPackage);
const IMeshMergeUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked<IMeshMergeModule>("MeshMergeUtilities").GetUtilities();
MeshUtilities.MergeComponentsToStaticMesh(
@ -64,3 +137,292 @@ void AProceduralBuildingUtilities::CookProceduralBuildingToMesh(const FString& D
true,
SAVE_NoError);
}
void AProceduralBuildingUtilities::CookProceduralMeshToMesh(
UProceduralMeshComponent* Mesh,
const FString& DestinationPath,
const FString& FileName)
{
FMeshDescription MeshDescription = BuildMeshDescription(Mesh);
FString PackageName = DestinationPath + FileName;
UPackage* NewPackage = CreatePackage(*PackageName);
check(NewPackage);
UStaticMesh* StaticMesh = NewObject<UStaticMesh>(NewPackage, *FileName, RF_Public | RF_Standalone);
StaticMesh->InitResources();
StaticMesh->LightingGuid = FGuid::NewGuid();
FStaticMeshSourceModel& SrcModel = StaticMesh->AddSourceModel();
SrcModel.BuildSettings.bRecomputeNormals = false;
SrcModel.BuildSettings.bRecomputeTangents = false;
SrcModel.BuildSettings.bRemoveDegenerates = false;
SrcModel.BuildSettings.bUseHighPrecisionTangentBasis = false;
SrcModel.BuildSettings.bUseFullPrecisionUVs = false;
SrcModel.BuildSettings.bGenerateLightmapUVs = true;
SrcModel.BuildSettings.SrcLightmapIndex = 0;
SrcModel.BuildSettings.DstLightmapIndex = 1;
StaticMesh->CreateMeshDescription(0, MoveTemp(MeshDescription));
StaticMesh->CommitMeshDescription(0);
if (!Mesh->bUseComplexAsSimpleCollision )
{
StaticMesh->CreateBodySetup();
UBodySetup* NewBodySetup = StaticMesh->BodySetup;
NewBodySetup->BodySetupGuid = FGuid::NewGuid();
NewBodySetup->AggGeom.ConvexElems = Mesh->ProcMeshBodySetup->AggGeom.ConvexElems;
NewBodySetup->bGenerateMirroredCollision = false;
NewBodySetup->bDoubleSidedGeometry = true;
NewBodySetup->CollisionTraceFlag = CTF_UseDefault;
NewBodySetup->CreatePhysicsMeshes();
}
TSet<UMaterialInterface*> UniqueMaterials;
const int32 NumSections = Mesh->GetNumSections();
for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
{
FProcMeshSection *ProcSection =
Mesh->GetProcMeshSection(SectionIdx);
UMaterialInterface *Material = Mesh->GetMaterial(SectionIdx);
UniqueMaterials.Add(Material);
}
for (auto* Material : UniqueMaterials)
{
StaticMesh->StaticMaterials.Add(FStaticMaterial(Material));
}
StaticMesh->ImportVersion = EImportStaticMeshVersion::LastVersion;
StaticMesh->Build(false);
StaticMesh->PostEditChange();
FAssetRegistryModule::AssetCreated(StaticMesh);
UPackage::SavePackage(NewPackage,
StaticMesh,
EObjectFlags::RF_Public | EObjectFlags::RF_Standalone,
*FileName,
GError,
nullptr,
true,
true,
SAVE_NoError);
}
UMaterialInstanceConstant* AProceduralBuildingUtilities::GenerateBuildingMaterialAsset(
const FString& DuplicateParentPath,
const FString& DestinationPath,
const FString& FileName)
{
const FString BaseMaterialSearchPath = DuplicateParentPath;
const FString PackageName = DestinationPath + FileName;
UMaterialInstanceConstant* ParentMaterial = LoadObject<UMaterialInstanceConstant>(nullptr, *BaseMaterialSearchPath);
check(ParentMaterial != nullptr);
UPackage* NewPackage = CreatePackage(*PackageName);
FObjectDuplicationParameters Parameters(ParentMaterial, NewPackage);
Parameters.DestName = FName(FileName);
Parameters.DestClass = ParentMaterial->GetClass();
Parameters.DuplicateMode = EDuplicateMode::Normal;
Parameters.PortFlags = PPF_Duplicate;
UMaterialInstanceConstant* DuplicatedMaterial = CastChecked<UMaterialInstanceConstant>(StaticDuplicateObjectEx(Parameters));
const FString PackageFileName = FPackageName::LongPackageNameToFilename(
PackageName,
".uasset");
UPackage::SavePackage(NewPackage, DuplicatedMaterial, EObjectFlags::RF_Public | EObjectFlags::RF_Standalone,
*PackageFileName, GError, nullptr, true, true, SAVE_NoError);
return DuplicatedMaterial;
}
void AProceduralBuildingUtilities::RenderImpostorView(
USceneCaptureComponent2D* Camera,
const FVector BuildingSize,
const EBuildingCameraView View)
{
MoveCameraToViewPosition(Camera, BuildingSize, View);
Camera->CaptureScene();
UTextureRenderTarget2D* CameraRT = Camera->TextureTarget;
UTexture2D* ViewTexture = CameraRT->ConstructTexture2D(GetWorld(), FString("View_").Append(FString::FromInt(View)), EObjectFlags::RF_NoFlags );
BakeSceneCaptureRTToTextureAtlas(ViewTexture, View);
}
void AProceduralBuildingUtilities::MoveCameraToViewPosition(
USceneCaptureComponent2D* Camera,
const FVector BuildingSize,
const EBuildingCameraView View)
{
const float BuildingHeight = BuildingSize.Z;
float ViewAngle = 0.f;
float BuildingWidth = 0.f;
float BuildingDepth = 0.f;
GetWidthDepthFromView(BuildingSize, View, BuildingWidth, BuildingDepth, ViewAngle);
/* ORTHO */
float ViewOrthoWidth = FMath::Max(BuildingWidth, BuildingHeight);
Camera->OrthoWidth = ViewOrthoWidth;
float CameraDistance = 0.5f * BuildingDepth + 1000.f;
FVector NewCameraLocation(
CameraDistance * FMath::Cos(FMath::DegreesToRadians(ViewAngle)),
CameraDistance * FMath::Sin(FMath::DegreesToRadians(ViewAngle)),
BuildingSize.Z / 2.0f);
FRotator NewCameraRotation(0.0f, ViewAngle + 180.f, 0.0f);
Camera->SetRelativeLocationAndRotation(NewCameraLocation, NewCameraRotation, false, nullptr, ETeleportType::None);
}
void AProceduralBuildingUtilities::CreateBuildingImpostorGeometryForView(
UProceduralMeshComponent* Mesh,
const FVector& BuildingSize,
const EBuildingCameraView View)
{
// Create vertices based on Building Size
TArray<FVector> Vertices;
TArray<int32> Triangles;
const float BuildingHeight = BuildingSize.Z;
float BuildingWidth = 0.f;
float BuildingDepth = 0.f;
float ViewAngle = 0.f;
GetWidthDepthFromView(BuildingSize, View, BuildingWidth, BuildingDepth, ViewAngle);
FVector RotationAxis(0.0f, 0.0f, 1.0f);
FVector OriginOffset(- 0.5f * BuildingDepth, - 0.5f * BuildingWidth, 0.0f);
// Vertices are created in local space, then offsetted to compensate origin and finally rotating
// according to the ViewAngle
Vertices.Add((FVector(0.0f, 0.0f, 0.0f) + OriginOffset).RotateAngleAxis(ViewAngle, RotationAxis));
Vertices.Add((FVector(0.0f, 0.0f, BuildingHeight) + OriginOffset).RotateAngleAxis(ViewAngle, RotationAxis));
Vertices.Add((FVector(0.0f, BuildingWidth, 0.0f) + OriginOffset).RotateAngleAxis(ViewAngle, RotationAxis));
Vertices.Add((FVector(0.0f, BuildingWidth, BuildingHeight) + OriginOffset).RotateAngleAxis(ViewAngle, RotationAxis));
Triangles.Add(0); Triangles.Add(2); Triangles.Add(1);
Triangles.Add(2); Triangles.Add(3); Triangles.Add(1);
TArray<FVector2D> UVs;
CalculateViewGeometryUVs(BuildingWidth, BuildingHeight, View, UVs);
TArray<FVector> Normals;
Normals.Init(FVector(-1.0f, 0.0f, 0.0f).RotateAngleAxis(ViewAngle, RotationAxis), Vertices.Num());
TArray<FLinearColor> Colors;
Colors.Init(FLinearColor(0.0f, 0.0f, 0.0f, 1.0f), Vertices.Num());
TArray<FProcMeshTangent> Tangents;
// Tangents.Init(FProcMeshTangent(-1.0f, -1.0f, -1.0f).RotateAngleAxis(ViewAngle, RotationAxis), Vertices.Num());
Tangents.Init(FProcMeshTangent(FVector(0.0f, 1.0f, 0.0f).RotateAngleAxis(ViewAngle, RotationAxis), false), Vertices.Num());
Mesh->CreateMeshSection_LinearColor((int32)View, Vertices, Triangles, Normals, UVs, Colors, Tangents, false);
}
void AProceduralBuildingUtilities::GetWidthDepthFromView(
const FVector& BuildingSize,
const EBuildingCameraView View,
float& OutWidth,
float& OutDepth,
float& OutViewAngle)
{
switch(View)
{
case EBuildingCameraView::FRONT:
OutViewAngle = 0.0f;
OutWidth = FMath::Abs(BuildingSize.Y);
OutDepth = FMath::Abs(BuildingSize.X);
break;
case EBuildingCameraView::LEFT:
OutViewAngle = 90.0f;
OutWidth = FMath::Abs(BuildingSize.X);
OutDepth = FMath::Abs(BuildingSize.Y);
break;
case EBuildingCameraView::BACK:
OutViewAngle = 180.0f;
OutWidth = FMath::Abs(BuildingSize.Y);
OutDepth = FMath::Abs(BuildingSize.X);
break;
case EBuildingCameraView::RIGHT:
OutViewAngle = 270.0f;
OutWidth = FMath::Abs(BuildingSize.X);
OutDepth = FMath::Abs(BuildingSize.Y);
break;
default:
OutViewAngle = 0.0f;
OutWidth = 0.0f;
OutDepth = 0.0f;
}
}
void AProceduralBuildingUtilities::CalculateViewGeometryUVs(
const float BuildingWidth,
const float BuildingHeight,
const EBuildingCameraView View,
TArray<FVector2D>& OutUVs)
{
// Calculate UVs from 0 to 1
// ------------
// | uv1 uv3 |
// | uv0 uv2 |
// ------------
FVector2D OriginOffset;
switch(View)
{
case EBuildingCameraView::FRONT:
OriginOffset = FVector2D(0.3f, 0.3f);
break;
case EBuildingCameraView::LEFT:
OriginOffset = FVector2D(0.3f, 0.4f);
break;
case EBuildingCameraView::BACK:
OriginOffset = FVector2D(0.4f, 0.3f);
break;
case EBuildingCameraView::RIGHT:
OriginOffset = FVector2D(0.4f, 0.4f);
break;
default:
OriginOffset = FVector2D(0.0f, 0.0f);
}
float WidthRatio = BuildingWidth / BuildingHeight;
float HeightRatio = BuildingHeight / BuildingWidth;
FVector2D X0, X1, X2, X3;
if(WidthRatio < 1.0f)
{
// Fit Vertically
X0 = FVector2D(0.05f - 0.05f * WidthRatio, 0.1f) + OriginOffset;
X1 = FVector2D(0.05f - 0.05f * WidthRatio, 0.0f) + OriginOffset;
X2 = FVector2D(0.05f + 0.05f * WidthRatio, 0.1f) + OriginOffset;
X3 = FVector2D(0.05f + 0.05f * WidthRatio, 0.0f) + OriginOffset;
}
else
{
// Fit Horizontally
X0 = FVector2D(0.0f, 0.05f + 0.05f * HeightRatio) + OriginOffset;
X1 = FVector2D(0.0f, 0.05f - 0.05f * HeightRatio) + OriginOffset;
X2 = FVector2D(0.1f, 0.05f + 0.05f * HeightRatio) + OriginOffset;
X3 = FVector2D(0.1f, 0.05f - 0.05f * HeightRatio) + OriginOffset;
}
// Fix offset to fit in view quadrant
OutUVs.Add(X0);
OutUVs.Add(X1);
OutUVs.Add(X2);
OutUVs.Add(X3);
}

View File

@ -6,7 +6,17 @@
#include "EditorUtilityActor.h"
#include "ProceduralBuildingUtilities.generated.h"
struct FIntRect;
class USceneCaptureComponent2D;
class UTexture2D;
UENUM(BlueprintType)
enum EBuildingCameraView
{
FRONT,
LEFT,
BACK,
RIGHT
};
/**
*
@ -17,10 +27,49 @@ class CARLATOOLS_API AProceduralBuildingUtilities : public AEditorUtilityActor
GENERATED_BODY()
public:
UFUNCTION(BlueprintImplementableEvent, Category="Procedural Building Utilities")
void BakeSceneCaptureRTToTextureAtlas(UTexture2D* SceneCaptureRT, EBuildingCameraView TargetView);
UFUNCTION(BlueprintImplementableEvent, Category="Procedural Building Utilities")
void SetTargetTextureToSceneCaptureComponent(USceneCaptureComponent2D* SceneCaptureComponent);
UFUNCTION(BlueprintCallable, Category="Procedural Building Utilities")
void GenerateImpostor();
void GenerateImpostorTexture(const FVector& BuildingSize);
UFUNCTION(BlueprintCallable, Category="Procedural Building Utilities")
class UProceduralMeshComponent* GenerateImpostorGeometry(const FVector& BuildingSize);
UFUNCTION(BlueprintCallable, Category="Procedural Building Utilities")
void CookProceduralBuildingToMesh(const FString& DestinationPath, const FString& FileName);
UFUNCTION(BlueprintCallable, Category="Procedural Building Utilities")
void CookProceduralMeshToMesh(class UProceduralMeshComponent* Mesh, const FString& DestinationPath, const FString& FileName);
UFUNCTION(BlueprintCallable, Category="Procedural Building Utilities")
class UMaterialInstanceConstant* GenerateBuildingMaterialAsset(
const FString& DuplicateParentPath,
const FString& DestinationPath,
const FString& FileName);
private:
void RenderImpostorView(USceneCaptureComponent2D* Camera, const FVector BuildingSize, const EBuildingCameraView View);
void MoveCameraToViewPosition(USceneCaptureComponent2D* Camera, const FVector BuildingSize, const EBuildingCameraView View);
void CreateBuildingImpostorGeometryForView(
class UProceduralMeshComponent* Mesh,
const FVector& BuildingSize,
const EBuildingCameraView View);
void GetWidthDepthFromView(
const FVector& BuildingSize,
const EBuildingCameraView View,
float& OutWidth, float& OutDepth,
float& OutViewAngle);
void CalculateViewGeometryUVs(
const float BuildingWidth,
const float BuildingHeight,
const EBuildingCameraView View,
TArray<FVector2D>& OutUVs);
};