Process hull mesh and find submerged triangles

Process hull mesh and find submerged triangles
This commit is contained in:
Kevin Poretti 2021-04-19 10:01:56 -04:00
parent 62e8c45c91
commit 607a29aa16
51 changed files with 701 additions and 26 deletions

1
.gitignore vendored
View File

@ -12,3 +12,4 @@ Saved
*.suo
*.xcodeproj
*.xcworkspace
.idea

View File

@ -0,0 +1 @@

View File

@ -2,7 +2,7 @@
[/Script/EngineSettings.GameMapsSettings]
GameDefaultMap=/Engine/Maps/Templates/Template_Default.Template_Default
EditorStartupMap=/Game/Maps/ShipTest.ShipTest
[/Script/HardwareTargeting.HardwareTargetingSettings]
TargetedHardwareClass=Desktop
@ -15,3 +15,6 @@ AppliedDefaultGraphicsPerformance=Maximum
+ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/SeaOfCrooks")
+ActiveClassRedirects=(OldClassName="TP_BlankGameModeBase",NewClassName="SeaOfCrooksGameModeBase")
[/Script/PythonScriptPlugin.PythonScriptPluginSettings]
bRemoteExecution=True

BIN
Content/Core/BP_TestGameMode.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Core/BP_TestGameState.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Environment/BP_TestOcean.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Environment/Materials/M_Ocean.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Environment/Materials/M_Sand.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Maps/ShipTest.umap (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Maps/ShipTest_BuiltData.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/BP_Ship.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/BP_TestShip.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/_defaultMat.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/iron.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_cannon_front.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_cannon_front1.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_cannon_left.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_cannon_left2.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_cannon_left3.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_cannon_left4.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_cannon_left5.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_cannon_left6.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_cannon_right.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_cannon_right10.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_cannon_right11.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_cannon_right7.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_cannon_right8.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_cannon_right9.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_sail_back.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_sail_front.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_sail_middle.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_ship_dark_8angles.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/ship_dark_steering.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/window.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/wood.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Ships/Ship_Dark/woodDark.uasset (Stored with Git LFS) Normal file

Binary file not shown.

BIN
Content/Test/Plane.uasset (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -7,7 +7,20 @@
{
"Name": "SeaOfCrooks",
"Type": "Runtime",
"LoadingPhase": "Default"
"LoadingPhase": "Default",
"AdditionalDependencies": [
"Engine"
]
}
],
"Plugins": [
{
"Name": "PythonScriptPlugin",
"Enabled": true
},
{
"Name": "EditorScriptingUtilities",
"Enabled": true
}
]
}

View File

@ -0,0 +1,223 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Components/BuoyancyComponent.h"
#include "DrawDebugHelpers.h"
#include "StaticMeshResources.h"
#include "Game/SOCGameState.h"
static int32 DebugBuoyancy = 0;
FAutoConsoleVariableRef CVARDebugProtagonistDrawing(TEXT("SOC.DebugBuoyancy"),
DebugBuoyancy,
TEXT("Print debug information about buoyancy component"),
ECVF_Cheat);
// Sets default values for this component's properties
UBuoyancyComponent::UBuoyancyComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
// ...
}
// Called when the game starts
void UBuoyancyComponent::BeginPlay()
{
Super::BeginPlay();
// ...
UStaticMeshComponent* MeshComp = Cast<UStaticMeshComponent>(GetOwner()->GetRootComponent());
VolumeMesh = MeshComp->GetStaticMesh();
SubmergedTriangles = TArray<FTriangleData>();
SubmergedTriangles.SetNum(GetNumTriangles(), false);
}
// Called every frame
void UBuoyancyComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
SubmergedTriangles.Empty();
// determine height of vertices above or below surface of water
ASOCGameState* State = Cast<ASOCGameState>(GetWorld()->GetGameState());
AOcean* Ocean = nullptr;
if(State)
{
Ocean = State->GetOcean();
}
if(Ocean)
{
TArray<FVertexData> Vertices = GetVertexPositions();
for(int idx = 0; idx < Vertices.Num(); idx++)
{
Vertices[idx].CalculateDepth(Ocean->GetOceanHeight());
}
TArray<int32> Indices = GetTriangleIndices();
TArray<FTriangleData> Triangles;
for(int idx = 0; idx < Indices.Num(); idx += 3)
{
FVertexData V1 = Vertices[Indices[idx]];
FVertexData V2 = Vertices[Indices[idx + 1]];
FVertexData V3 = Vertices[Indices[idx + 2]];
if(V1.Depth < 0.0f || V2.Depth < 0.0f || V3.Depth < 0.0f)
{
FTriangleData temp(V1, V2, V3);
Triangles.Add(temp);
FVector Center = (V1.Position + V2.Position + V3.Position) / 3.0f;
if(DebugBuoyancy)
DrawDebugSphere(GetWorld(), Center, 5.0f, 12, FColor::Yellow, false, 0.0f, 0, 1.0f);
}
}
ProcessTriangles(Triangles);
if(DebugBuoyancy)
{
FString DebugMsg = FString::Printf(TEXT("Num Submerged Triangles: %d\nNum Generated Triangles: %d"),
Triangles.Num(), SubmergedTriangles.Num());
GEngine->AddOnScreenDebugMessage(-1, 0.0f, FColor::Cyan, DebugMsg);
for (auto Triangle : SubmergedTriangles)
{
DrawDebugSphere(GetWorld(), Triangle.Highest.Position, 2.0f, 12, FColor::Red,
false, 0.0f, 0, 1.0f);
DrawDebugSphere(GetWorld(), Triangle.Middle.Position, 2.0f, 12, FColor::Red,
false, 0.0f, 0, 1.0f);
DrawDebugSphere(GetWorld(), Triangle.Lowest.Position, 2.0f, 12, FColor::Red,
false, 0.0f, 0, 1.0f);
}
}
}
}
int32 UBuoyancyComponent::GetNumTriangles()
{
int32 result = 0;
if(!VolumeMesh || !VolumeMesh->RenderData)
{
return result;
}
if(VolumeMesh->RenderData->LODResources.Num() > 0)
{
result = VolumeMesh->RenderData->LODResources[0].GetNumTriangles();
}
return result;
}
TArray<FVertexData> UBuoyancyComponent::GetVertexPositions()
{
TArray<FVertexData> VertexPositions = TArray<FVertexData>();
if(!VolumeMesh || !VolumeMesh->RenderData)
{
return VertexPositions;
}
if(VolumeMesh->RenderData->LODResources.Num() > 0)
{
FPositionVertexBuffer* VertexBuffer = &VolumeMesh->RenderData->LODResources[0].VertexBuffers.PositionVertexBuffer;
if(VertexBuffer)
{
for(uint32 idx = 0; idx < VertexBuffer->GetNumVertices(); idx++)
{
// convert asset space vertex position to world space
FVector WorldSpaceVertexPos = GetOwner()->GetActorLocation() + GetOwner()->GetTransform().TransformVector(VertexBuffer->VertexPosition(idx));
FVertexData temp;
temp.Position = WorldSpaceVertexPos;
VertexPositions.Add(temp);
}
}
}
return VertexPositions;
}
TArray<int32> UBuoyancyComponent::GetTriangleIndices()
{
TArray<int32> Indices = TArray<int32>();
if(!VolumeMesh || !VolumeMesh->RenderData) // return empty array
{
return Indices;
}
if(VolumeMesh->RenderData->LODResources.Num() > 0)
{
FRawStaticIndexBuffer* IndexBuffer = &VolumeMesh->RenderData->LODResources[0].IndexBuffer;
if(IndexBuffer)
{
for(int idx = 0; idx < IndexBuffer->GetNumIndices(); idx++)
{
Indices.Add(IndexBuffer->GetIndex(idx));
}
}
}
return Indices;
}
void UBuoyancyComponent::ProcessTriangles(TArray<FTriangleData>& Triangles)
{
for (auto Triangle : Triangles)
{
int32 NumSubmergedVertices = Triangle.GetNumSubmergedVertices();
if(NumSubmergedVertices == 3) // completely submerged so add as is
{
SubmergedTriangles.Add(Triangle);
}
else if (NumSubmergedVertices == 2)
{
FVector H = Triangle.Highest.Position;
FVector M = Triangle.Middle.Position;
FVector L = Triangle.Lowest.Position;
float hH = Triangle.Highest.Depth;
float hM = Triangle.Middle.Depth;
float hL = Triangle.Lowest.Depth;
FVector MH = H - M;
float tM = -hL/(hH - hL);
FVector MIM = tM * MH;
FVector IM = MIM + M;
FVector LH = H - L;
float tL = -hL/(hH - hL);
FVector LIL = tL * LH;
FVector IL = LIL + L;
SubmergedTriangles.Add(FTriangleData(IM, IL, L));
SubmergedTriangles.Add(FTriangleData(M, IM, L));
}
else if (NumSubmergedVertices == 1)
{
FVector H = Triangle.Highest.Position;
FVector M = Triangle.Middle.Position;
FVector L = Triangle.Lowest.Position;
float hH = Triangle.Highest.Depth;
float hM = Triangle.Middle.Depth;
float hL = Triangle.Lowest.Depth;
FVector LM = M - L;
float tM = -hL/(hM - hL);
FVector LJM = tM * LM;
FVector JM = LJM + L;
FVector LH = H - L;
float tH = -hL/(hH - hL);
FVector LJH = tH * LH;
FVector JH = LJH + L;
SubmergedTriangles.Add(FTriangleData(JH, JM, L));
}
}
}

View File

@ -0,0 +1,25 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Game/SOCGameModeBase.h"
#include "Game/SOCGameState.h"
ASOCGameModeBase::ASOCGameModeBase()
{
GameStateClass = ASOCGameState::StaticClass();
}
void ASOCGameModeBase::StartPlay()
{
Super::StartPlay();
ASOCGameState* State = Cast<ASOCGameState>(GameState);
if(State)
{
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
SpawnParams.Owner = State;
State->SetOcean(GetWorld()->SpawnActor<AOcean>(OceanClass,
FVector::ZeroVector, FRotator::ZeroRotator, SpawnParams));
}
}

View File

@ -0,0 +1,14 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Game/SOCGameState.h"
AOcean* ASOCGameState::GetOcean()
{
return Ocean;
}
void ASOCGameState::SetOcean(AOcean* NewOcean)
{
Ocean = NewOcean;
}

View File

@ -0,0 +1,41 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "Ocean.h"
// Sets default values
AOcean::AOcean()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
OceanMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("OceanMesh"));
OceanMesh->SetupAttachment(RootComponent);
OceanMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
float AOcean::GetOceanHeight()
{
/**
* For now assume a completely flat static ocean.
* Once we get into procedural ocean and wave generation we will want to be able to pass in a world X and Y position
* and have this function return the height of the ocean at that location
*/
return GetActorLocation().Z;
}
// Called when the game starts or when spawned
void AOcean::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AOcean::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}

View File

@ -0,0 +1,38 @@
// Fill out your copyright notice in the Description page of Project Settings.
#include "ShipBase.h"
// Sets default values
AShipBase::AShipBase()
{
// Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
ShipMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ShipMesh"));
ShipMesh->SetupAttachment(RootComponent);
BuoyancyComp = CreateDefaultSubobject<UBuoyancyComponent>(TEXT("BuoyancyComponent"));
}
// Called when the game starts or when spawned
void AShipBase::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AShipBase::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
// Called to bind functionality to input
void AShipBase::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
}

View File

@ -0,0 +1,116 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "BuoyancyComponent.generated.h"
USTRUCT()
struct FVertexData
{
GENERATED_BODY()
FVector Position;
// depth of the vertex above the surface of the water
// negative value means the vertex is submerged
float Depth;
int32 Index;
FVertexData() {}
FVertexData(FVector Position)
{
this->Position = Position;
Depth = 0.0f;
}
FVertexData(FVector Position, float Depth)
{
this->Position = Position;
this->Depth = Depth;
}
void CalculateDepth(float SurfaceHeight)
{
Depth = Position.Z - SurfaceHeight;
}
bool operator< (const FVertexData& rhs) const
{
return this->Depth < rhs.Depth;
}
};
USTRUCT()
struct FTriangleData
{
GENERATED_BODY()
FVertexData Highest;
FVertexData Middle;
FVertexData Lowest;
FTriangleData() {}
FTriangleData(FVertexData V1, FVertexData V2, FVertexData V3)
{
TArray<FVertexData> Vertices = {V1, V2, V3};
Vertices.Sort();
Lowest = Vertices[0];
Middle = Vertices[1];
Highest = Vertices[2];
}
int32 GetNumSubmergedVertices()
{
int32 result = 0;
if(Highest.Depth < 0.0f)
{
result++;
}
if(Middle.Depth < 0.0f)
{
result++;
}
if(Lowest.Depth < 0.0f)
{
result++;
}
return result;
}
};
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class SEAOFCROOKS_API UBuoyancyComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UBuoyancyComponent();
protected:
UPROPERTY(BlueprintReadOnly)
UStaticMesh* VolumeMesh;
UPROPERTY()
TArray<FTriangleData> SubmergedTriangles;
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
private:
int32 GetNumTriangles();
TArray<FVertexData> GetVertexPositions();
TArray<int32> GetTriangleIndices();
void ProcessTriangles(TArray<FTriangleData>& Triangles);
};

View File

@ -0,0 +1,27 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Ocean.h"
#include "GameFramework/GameMode.h"
#include "SOCGameModeBase.generated.h"
/**
*
*/
UCLASS()
class SEAOFCROOKS_API ASOCGameModeBase : public AGameMode
{
GENERATED_BODY()
public:
ASOCGameModeBase();
protected:
UPROPERTY(EditDefaultsOnly, Category="Ocean")
TSubclassOf<AOcean> OceanClass;
virtual void StartPlay() override;
};

View File

@ -0,0 +1,26 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Ocean.h"
#include "GameFramework/GameState.h"
#include "SOCGameState.generated.h"
/**
*
*/
UCLASS()
class SEAOFCROOKS_API ASOCGameState : public AGameState
{
GENERATED_BODY()
public:
AOcean* GetOcean();
void SetOcean(AOcean* NewOcean);
protected:
UPROPERTY()
AOcean* Ocean;
};

View File

@ -0,0 +1,31 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Ocean.generated.h"
UCLASS()
class SEAOFCROOKS_API AOcean : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AOcean();
float GetOceanHeight();
protected:
UPROPERTY(EditDefaultsOnly, Category="Components")
UStaticMeshComponent* OceanMesh;
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};

View File

@ -0,0 +1,36 @@
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Pawn.h"
#include "Components/BuoyancyComponent.h"
#include "ShipBase.generated.h"
UCLASS()
class SEAOFCROOKS_API AShipBase : public APawn
{
GENERATED_BODY()
public:
// Sets default values for this pawn's properties
AShipBase();
protected:
UPROPERTY(EditDefaultsOnly, Category="Components")
UStaticMeshComponent* ShipMesh;
UPROPERTY(EditDefaultsOnly, Category="Components")
UBuoyancyComponent* BuoyancyComp;
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
};

View File

@ -8,7 +8,7 @@ public class SeaOfCrooks : ModuleRules
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "RenderCore", "RHI" });
PrivateDependencyModuleNames.AddRange(new string[] { });

View File

@ -1,5 +0,0 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "SeaOfCrooksGameModeBase.h"

View File

@ -1,17 +0,0 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "SeaOfCrooksGameModeBase.generated.h"
/**
*
*/
UCLASS()
class SEAOFCROOKS_API ASeaOfCrooksGameModeBase : public AGameModeBase
{
GENERATED_BODY()
};