Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 46 additions & 47 deletions Source/Barrage/Private/BarrageDispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
//https://github.com/GaijinEntertainment/DagorEngine/blob/71a26585082f16df80011e06e7a4e95302f5bb7f/prog/engine/phys/physJolt/joltPhysics.cpp#L800
//this is how gaijin uses jolt, and war thunder's honestly a pretty strong comp to our use case.


void UBarrageDispatch::GrantFeed()
{
FScopeLock GrantFeedLock(&GrowOnlyAccLock);
Expand All @@ -34,9 +33,9 @@ UBarrageDispatch::~UBarrageDispatch()
void UBarrageDispatch::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
for (auto& x : Tombs)
for (TSharedPtr<TArray<TSharedPtr<FBarragePrimitive>>>& tomb : Tombs)
{
x = MakeShareable(new TArray<FBLet>());
tomb = MakeShareable(new TArray<FBLet>());
}

UE_LOG(LogTemp, Warning, TEXT("Barrage:TransformUpdateQueue: Online"));
Expand All @@ -53,25 +52,25 @@ void UBarrageDispatch::Initialize(FSubsystemCollectionBase& Collection)

void UBarrageDispatch::OnWorldBeginPlay(UWorld& InWorld)
{

Super::OnWorldBeginPlay(InWorld);

}

void UBarrageDispatch::Deinitialize()
{
Super::Deinitialize();
JoltBodyLifecycleMapping = nullptr;
TranslationMapping = nullptr;
for (auto& x : Tombs)

for (TSharedPtr<TArray<TSharedPtr<FBarragePrimitive>>>& tomb : Tombs)
{
x = nullptr;
tomb = nullptr;
}
auto HoldOpen = GameTransformPump;

TSharedPtr<TCircularQueue<TransformUpdate>> HoldOpen = GameTransformPump;
GameTransformPump = nullptr;
if (HoldOpen)
{
auto val = HoldOpen.GetSharedReferenceCount();
int32 val = HoldOpen.GetSharedReferenceCount();
if (val > 1)
{
UE_LOG(LogTemp, Warning,
Expand All @@ -93,9 +92,9 @@ void UBarrageDispatch::SphereCast(
TSharedPtr<FHitResult> OutHit,
uint64_t timestamp)
{
auto HoldOpen = JoltGameSim;
TSharedPtr<JOLT::FWorldSimOwner> HoldOpen = JoltGameSim;
if (HoldOpen) {
auto bodyID = HoldOpen->BarrageToJoltMapping->Find(ShapeSource);
JPH::BodyID* bodyID = HoldOpen->BarrageToJoltMapping->Find(ShapeSource);
HoldOpen->SphereCast(Radius, Distance, CastFrom, Direction, *bodyID, OutHit);
}
}
Expand All @@ -107,10 +106,10 @@ void UBarrageDispatch::SphereCast(
//feature. I'm going to wait to refactor the types until testing is complete.
FBLet UBarrageDispatch::CreatePrimitive(FBBoxParams& Definition, FSkeletonKey OutKey, uint16_t Layer)
{
auto HoldOpen = JoltGameSim;
TSharedPtr<JOLT::FWorldSimOwner> HoldOpen = JoltGameSim;
if (HoldOpen)
{
auto temp = HoldOpen->CreatePrimitive(Definition, Layer);
FBarrageKey temp = HoldOpen->CreatePrimitive(Definition, Layer);
return ManagePointers(OutKey, temp, FBarragePrimitive::Box);
}
return FBLet();
Expand All @@ -119,32 +118,32 @@ FBLet UBarrageDispatch::CreatePrimitive(FBBoxParams& Definition, FSkeletonKey Ou
//TODO: COMPLETE MOCK
FBLet UBarrageDispatch::CreatePrimitive(FBCharParams& Definition, FSkeletonKey OutKey, uint16_t Layer)
{
auto HoldOpen = JoltGameSim;
TSharedPtr<JOLT::FWorldSimOwner> HoldOpen = JoltGameSim;
if (HoldOpen)
{
auto temp = HoldOpen->CreatePrimitive(Definition, Layer);
FBarrageKey temp = HoldOpen->CreatePrimitive(Definition, Layer);
return ManagePointers(OutKey, temp, FBarragePrimitive::Character);
}
return FBLet();
}

FBLet UBarrageDispatch::CreatePrimitive(FBSphereParams& Definition, FSkeletonKey OutKey, uint16_t Layer)
{
auto HoldOpen = JoltGameSim;
TSharedPtr<JOLT::FWorldSimOwner> HoldOpen = JoltGameSim;
if (HoldOpen)
{
auto temp = HoldOpen->CreatePrimitive(Definition, Layer);
FBarrageKey temp = HoldOpen->CreatePrimitive(Definition, Layer);
return ManagePointers(OutKey, temp, FBarragePrimitive::Sphere);
}
return FBLet();
}

FBLet UBarrageDispatch::CreatePrimitive(FBCapParams& Definition, FSkeletonKey OutKey, uint16 Layer)
{
auto HoldOpen = JoltGameSim;
TSharedPtr<JOLT::FWorldSimOwner> HoldOpen = JoltGameSim;
if (HoldOpen)
{
auto temp = HoldOpen->CreatePrimitive(Definition, Layer);
FBarrageKey temp = HoldOpen->CreatePrimitive(Definition, Layer);
return ManagePointers(OutKey, temp, FBarragePrimitive::Capsule);
}
return FBLet();
Expand All @@ -166,12 +165,13 @@ FBLet UBarrageDispatch::ManagePointers(FSkeletonKey OutKey, FBarrageKey temp, FB
//https://github.com/jrouwe/JoltPhysics/blob/master/Samples/Tests/Shapes/MeshShapeTest.cpp
//probably worth reviewing how indexed triangles work, too : https://www.youtube.com/watch?v=dOjZw5VU6aM
FBLet UBarrageDispatch::LoadComplexStaticMesh(FBMeshParams& Definition,
const UStaticMeshComponent* StaticMeshComponent, FSkeletonKey Outkey)
const UStaticMeshComponent* StaticMeshComponent,
FSkeletonKey Outkey) const
{
auto HoldOpen = JoltGameSim;
TSharedPtr<JOLT::FWorldSimOwner> HoldOpen = JoltGameSim;
if (HoldOpen)
{
auto shared = HoldOpen->LoadComplexStaticMesh(Definition, StaticMeshComponent, Outkey);
FBLet shared = HoldOpen->LoadComplexStaticMesh(Definition, StaticMeshComponent, Outkey);
if(shared)
{
JoltBodyLifecycleMapping->Add(shared->KeyIntoBarrage, shared);
Expand Down Expand Up @@ -208,7 +208,7 @@ FBLet UBarrageDispatch::GetShapeRef(FSkeletonKey Existing) const
//that this thoroughfare? it leads into the region of peril.
//3) you get a valid shared pointer which will hold the asset open until you're done, but the markings are being set
//this means your calls will all succeed but none will be applied during the apply shadow phase.
auto ref = TranslationMapping->Find(Existing);
FBarrageKey* ref = TranslationMapping->Find(Existing);
if(ref)
{
FBLet* deref = JoltBodyLifecycleMapping->Find(*ref);
Expand All @@ -220,31 +220,30 @@ FBLet UBarrageDispatch::GetShapeRef(FSkeletonKey Existing) const
return FBLet();
}

void UBarrageDispatch::FinalizeReleasePrimitive(FBarrageKey BarrageKey)
void UBarrageDispatch::FinalizeReleasePrimitive(FBarrageKey BarrageKey) const
{
auto HoldOpen = JoltGameSim;
if (HoldOpen && JoltGameSim)
TSharedPtr<JOLT::FWorldSimOwner> HoldOpen = JoltGameSim;
if (HoldOpen)
{
HoldOpen->FinalizeReleasePrimitive(BarrageKey);
}
}


TStatId UBarrageDispatch::GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(UBarrageDispatch, STATGROUP_Tickables);
}

void UBarrageDispatch::StackUp()
{
auto WorldSimOwner = JoltGameSim;
TSharedPtr<JOLT::FWorldSimOwner> WorldSimOwner = JoltGameSim;
//currently, these are only characters but that could change. This would likely become a TMap then but maybe not.
if (WorldSimOwner)
{
for (auto& x : WorldSimOwner->ThreadAcc)
for (JOLT::FWorldSimOwner::FeedMap& SimOwnerFeedMap : WorldSimOwner->ThreadAcc)
{
TSharedPtr<JOLT::FWorldSimOwner::ThreadFeed> HoldOpen;
if (x.Queue && ((HoldOpen = x.Queue)) && x.That != std::thread::id()) //if there IS a thread.
if (SimOwnerFeedMap.Queue && ((HoldOpen = SimOwnerFeedMap.Queue)) && SimOwnerFeedMap.That != std::thread::id()) //if there IS a thread.
{
while (HoldOpen && !HoldOpen->IsEmpty())
{
Expand Down Expand Up @@ -276,57 +275,56 @@ void UBarrageDispatch::StackUp()
}
HoldOpen->Dequeue();
}

}
}
}
}

bool UBarrageDispatch::UpdateCharacters(TSharedPtr<TArray<FBPhysicsInput>> CharacterInputs)
bool UBarrageDispatch::UpdateCharacters(TSharedPtr<TArray<FBPhysicsInput>> CharacterInputs) const
{
return JoltGameSim->UpdateCharacters(CharacterInputs);
}

bool UBarrageDispatch::UpdateCharacter(FBPhysicsInput& CharacterInput)
bool UBarrageDispatch::UpdateCharacter(FBPhysicsInput& CharacterInput) const
{
return JoltGameSim->UpdateCharacter(CharacterInput);
}

void UBarrageDispatch::StepWorld(uint64 Time)
{
auto HoldOpenWorld = JoltGameSim;
TSharedPtr<JOLT::FWorldSimOwner> HoldOpenWorld = JoltGameSim;
if (HoldOpenWorld)
{
JoltGameSim->StepSimulation();
auto HoldOpenCharacters = JoltGameSim->CharacterToJoltMapping;
TSharedPtr<TMap<FBarrageKey, TSharedPtr<JOLT::FBCharacterBase>>> HoldOpenCharacters = JoltGameSim->CharacterToJoltMapping;
if(HoldOpenCharacters)
{
for(auto x : *HoldOpenCharacters)
for(TTuple<FBarrageKey, TSharedPtr<JOLT::FBCharacterBase>> CharacterTuple : *HoldOpenCharacters)
{
if(x.Value->mCharacter && !x.Value->mCharacter->GetPosition().IsNaN())
if(CharacterTuple.Value->mCharacter && !CharacterTuple.Value->mCharacter->GetPosition().IsNaN())
{
x.Value->StepCharacter();
CharacterTuple.Value->StepCharacter();
}
else if (x.Value->mCharacter)
else if (CharacterTuple.Value->mCharacter)
{
x.Value->mCharacter->SetLinearVelocity(x.Value->World->GetGravity());
x.Value->mCharacter->SetPosition(x.Value->mInitialPosition);
x.Value->mForcesUpdate = x.Value->World->GetGravity();
x.Value->StepCharacter();
CharacterTuple.Value->mCharacter->SetLinearVelocity(CharacterTuple.Value->World->GetGravity());
CharacterTuple.Value->mCharacter->SetPosition(CharacterTuple.Value->mInitialPosition);
CharacterTuple.Value->mForcesUpdate = CharacterTuple.Value->World->GetGravity();
CharacterTuple.Value->StepCharacter();
}
}
}

//maintain tombstones
CleanTombs();
auto HoldOpen = JoltBodyLifecycleMapping;
TSharedPtr<TMap<FBarrageKey, FBLet>> HoldOpen = JoltBodyLifecycleMapping;
if (HoldOpen && HoldOpen.Get() && !HoldOpen.Get()->IsEmpty())
{
for (auto& x : *HoldOpen)
for (TTuple<FBarrageKey, FBLet>& x : *HoldOpen)
{
auto RefHoldOpen = x.Value;
TSharedPtr<FBarragePrimitive> RefHoldOpen = x.Value;
if (RefHoldOpen && RefHoldOpen.IsValid() && FBarragePrimitive::IsNotNull(RefHoldOpen))
{

FBarragePrimitive::TryUpdateTransformFromJolt(x.Value, Time);
//returns a bool that can be used for debug.
}
Expand Down Expand Up @@ -380,6 +378,7 @@ FBCapParams FBarrageBounder::GenerateCapsuleBounds(UE::Geometry::FCapsule3d Caps
blob.point = Capsule.Center();
blob.JoltRadius = CoordinateUtils::RadiusToJolt(Capsule.Radius);
blob.JoltHalfHeightOfCylinder = CoordinateUtils::RadiusToJolt(Capsule.Extent());
blob.taper = 0.f;
return blob;
}

Expand Down
38 changes: 16 additions & 22 deletions Source/Barrage/Public/BarrageDispatch.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once
#include "SkeletonTypes.h"

#include "SkeletonTypes.h"

#include "CoreMinimal.h"
#include "Subsystems/WorldSubsystem.h"
Expand All @@ -22,7 +22,6 @@ enum LayersMap

class BARRAGE_API FBarrageBounder
{

friend class FBBoxParams;
friend class FBSphereParams;
friend class FBCapParams;
Expand Down Expand Up @@ -54,8 +53,6 @@ class BARRAGE_API UBarrageDispatch : public UTickableWorldSubsystem
static inline constexpr float TickRateInDelta = 1.0 / 120.0;

public:


uint8 ThreadAccTicker = 0;
TSharedPtr<TransformUpdatesForGameThread> GameTransformPump;
//this value indicates you have none.
Expand All @@ -70,53 +67,50 @@ class BARRAGE_API UBarrageDispatch : public UTickableWorldSubsystem
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void OnWorldBeginPlay(UWorld& InWorld) override;
virtual void Deinitialize() override;



virtual void SphereCast(FBarrageKey ShapeSource, double Radius, double Distance, FVector3d CastFrom, FVector3d Direction, TSharedPtr<FHitResult> OutHit, uint64_t timestamp = 0);
//and viola [sic] actually pretty elegant even without type polymorphism by using overloading polymorphism.
FBLet CreatePrimitive(FBBoxParams& Definition, FSkeletonKey Outkey, uint16 Layer);
FBLet CreatePrimitive(FBCharParams& Definition, FSkeletonKey Outkey, uint16 Layer);
FBLet CreatePrimitive(FBSphereParams& Definition, FSkeletonKey OutKey, uint16 Layer);
FBLet CreatePrimitive(FBCapParams& Definition, FSkeletonKey OutKey, uint16 Layer);
FBLet LoadComplexStaticMesh(FBMeshParams& Definition, const UStaticMeshComponent* StaticMeshComponent, FSkeletonKey Outkey);
FBLet LoadComplexStaticMesh(FBMeshParams& Definition, const UStaticMeshComponent* StaticMeshComponent, FSkeletonKey Outkey) const;
FBLet GetShapeRef(FBarrageKey Existing) const;
FBLet GetShapeRef(FSkeletonKey Existing) const;
void FinalizeReleasePrimitive(FBarrageKey BarrageKey);
void FinalizeReleasePrimitive(FBarrageKey BarrageKey) const;

//any non-zero value is the same, effectively, as a nullity for the purposes of any new operation.
//because we can't control certain aspects of timing and because we may need to roll back, we use tombstoning
//instead of just reference counting and deleting - this is because cases arise where there MUST be an authoritative
//single source answer to the alive/dead question for a rigid body, but we still want all the advantages of ref counting
//and we want to be able to revert that decision for faster rollbacks or for pooling purposes.
constexpr static uint32 TombstoneInitialMinimum = 9;
constexpr static uint32 TOMBSTONE_INITIAL_MINIMUM = 9;

uint32 SuggestTombstone(const FBLet& Target)
uint32 SuggestTombstone(const FBLet& Target) const
{
if (FBarragePrimitive::IsNotNull(Target))
{
Target->tombstone = TombstoneInitialMinimum + TombOffset;
Target->tombstone = TOMBSTONE_INITIAL_MINIMUM + TombOffset;
return Target->tombstone;
}
return 1;
// one indicates that something has no remaining time to live, and is equivalent to finding a nullptr. we return if for tombstone suggestions against tombstoned or null data.
}

virtual TStatId GetStatId() const override;
TSharedPtr< JOLT::FWorldSimOwner> JoltGameSim;
TSharedPtr<JOLT::FWorldSimOwner> JoltGameSim;

//StackUp should be called before stepworld and from the same thread. anything can be done between them.
//Returns rather than applies the FBPhysicsInputs that affect Primitives of Types: Character
//This list may expand. Failure to handle these will result in catastrophic bugs.
void StackUp();
bool UpdateCharacters(TSharedPtr<TArray<FBPhysicsInput>> CharacterInputs);
bool UpdateCharacter(FBPhysicsInput& CharacterInput);
bool UpdateCharacters(TSharedPtr<TArray<FBPhysicsInput>> CharacterInputs) const;
bool UpdateCharacter(FBPhysicsInput& CharacterInput) const;
//ONLY call this from a thread OTHER than gamethread, or you will experience untold sorrow.
void StepWorld(uint64 Time);

FBarrageKey GenerateBarrageKeyFromBodyId(const JPH::BodyID& Input) const;
FBarrageKey GenerateBarrageKeyFromBodyId(const uint32 RawIndexAndSequenceNumberInput) const;

protected:

private:
TSharedPtr<TMap<FBarrageKey, FBLet>> JoltBodyLifecycleMapping;
Expand All @@ -130,21 +124,21 @@ class BARRAGE_API UBarrageDispatch : public UTickableWorldSubsystem
//this reserves as little memory as possible, but it could be quite a lot (megs) of reserved memory if you expire
//tons and tons of bodies in one frame. if that's a bottleneck for you, you may wish to shorten the tombstone promise
//or optimize this for memory better. In general, Barrage and Artillery trade memory for speed and elegance.
TSharedPtr<TArray<FBLet>> Tombs[TombstoneInitialMinimum + 1];
TSharedPtr<TArray<FBLet>> Tombs[TOMBSTONE_INITIAL_MINIMUM + 1];

void CleanTombs()
{
//free tomb at offset - TombstoneInitialMinimum, fulfilling our promised minimum.
auto HoldOpen = Tombs;
auto& Mausoleum = HoldOpen[(TombOffset - TombstoneInitialMinimum) % (TombstoneInitialMinimum + 1)];
//free tomb at offset - TOMBSTONE_INITIAL_MINIMUM, fulfilling our promised minimum.
TSharedPtr<TArray<TSharedPtr<FBarragePrimitive>>>* HoldOpen = Tombs;
TSharedPtr<TArray<TSharedPtr<FBarragePrimitive>>>& Mausoleum = HoldOpen[(TombOffset - TOMBSTONE_INITIAL_MINIMUM) % (TOMBSTONE_INITIAL_MINIMUM + 1)];
if(Mausoleum)
{
Mausoleum->Empty(); //roast 'em lmao.
}
TombOffset = (TombOffset + 1) % (TombstoneInitialMinimum + 1);
TombOffset = (TombOffset + 1) % (TOMBSTONE_INITIAL_MINIMUM + 1);
}

void Entomb(FBLet NONREFERENCE)
void Entomb(const FBLet& NONREFERENCE) const
{
//request removal here
JoltBodyLifecycleMapping->Remove(NONREFERENCE->KeyIntoBarrage);
Expand Down
2 changes: 1 addition & 1 deletion Source/Barrage/Public/FBarragePrimitive.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class BARRAGE_API FBarragePrimitive
//0 is normal.
//1 or more is dead and indicates that the primitive could be released at any time. These should be considered
// opaque values as implementation is not final. a primitive is guaranteed to be tombstoned for at least
// BarrageDispatch::TombstoneInitialMinimum cycles before it is released, so this can safely be
// BarrageDispatch::TOMBSTONE_INITIAL_MINIMUM cycles before it is released, so this can safely be
// used in conjunction with null checks to ensure that short tasks can finish safely without
// worrying about the exact structure of our lifecycles. it is also used for pool and rollback handling,
// and the implementation will change as those come online in artillery.
Expand Down