256 lines
7.0 KiB
C++
256 lines
7.0 KiB
C++
// 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();
|
|
|
|
VolumeAsPrimComp = Cast<UPrimitiveComponent>(MeshComp);
|
|
|
|
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());
|
|
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, Ocean->GetOceanHeight());
|
|
Triangles.Add(temp);
|
|
if(DebugBuoyancy)
|
|
DrawDebugSphere(GetWorld(), temp.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, -1.0f, FColor::Cyan, DebugMsg);
|
|
|
|
for (auto Triangle : SubmergedTriangles)
|
|
{
|
|
DrawDebugSphere(GetWorld(), Triangle.V3.GlobalPosition, 2.0f, 12, FColor::Red,
|
|
false, 0.0f, 0, 1.0f);
|
|
DrawDebugSphere(GetWorld(), Triangle.V2.GlobalPosition, 2.0f, 12, FColor::Red,
|
|
false, 0.0f, 0, 1.0f);
|
|
DrawDebugSphere(GetWorld(), Triangle.V1.GlobalPosition, 2.0f, 12, FColor::Red,
|
|
false, 0.0f, 0, 1.0f);
|
|
}
|
|
}
|
|
|
|
if(VolumeAsPrimComp)
|
|
{
|
|
for (auto Triangle : SubmergedTriangles)
|
|
{
|
|
float rhoInKgCm3 = 0.001025f;
|
|
// F = -p*g*Hcenter*normal
|
|
FVector Force = -rhoInKgCm3 * GetWorld()->GetGravityZ() * Triangle.DistanceToSurface * Triangle.Area * Triangle.Normal;
|
|
FVector VerticalForce(0.0f, 0.0f, Force.Z);
|
|
VolumeAsPrimComp->AddForceAtLocation(VerticalForce, Triangle.Center);
|
|
}
|
|
}
|
|
|
|
FString DebugMsg = FString::Printf(TEXT("Velocity.Z: %s"), *FString::SanitizeFloat(VolumeAsPrimComp->GetPhysicsLinearVelocity().Z));
|
|
GEngine->AddOnScreenDebugMessage(-1, -1.0f, FColor::Magenta, DebugMsg);
|
|
}
|
|
}
|
|
|
|
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.GlobalPosition = 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)
|
|
{
|
|
FVertexData V1 = Triangle.V1;
|
|
FVertexData V2 = Triangle.V2;
|
|
FVertexData V3 = Triangle.V3;
|
|
TArray<FVertexData> Vertices = {V1, V2, V3};
|
|
Vertices.Sort();
|
|
V1 = Vertices[0];
|
|
V2 = Vertices[1];
|
|
V3 = Vertices[2];
|
|
|
|
FVector H = V3.GlobalPosition;
|
|
FVector M = V2.GlobalPosition;
|
|
FVector L = V1.GlobalPosition;
|
|
float hH = V3.Depth;
|
|
float hM = V2.Depth;
|
|
float hL = V1.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(L, IL, IM, Ocean->GetOceanHeight()));
|
|
SubmergedTriangles.Add(FTriangleData(M, L, IM, Ocean->GetOceanHeight()));
|
|
}
|
|
else if (NumSubmergedVertices == 1)
|
|
{
|
|
FVertexData V1 = Triangle.V1;
|
|
FVertexData V2 = Triangle.V2;
|
|
FVertexData V3 = Triangle.V3;
|
|
TArray<FVertexData> Vertices = {V1, V2, V3};
|
|
Vertices.Sort();
|
|
V1 = Vertices[0];
|
|
V2 = Vertices[1];
|
|
V3 = Vertices[2];
|
|
|
|
FVector H = V3.GlobalPosition;
|
|
FVector M = V2.GlobalPosition;
|
|
FVector L = V1.GlobalPosition;
|
|
float hH = V3.Depth;
|
|
float hM = V2.Depth;
|
|
float hL = V1.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(L, JH, JM, Ocean->GetOceanHeight()));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|