Skip to content

Instantly share code, notes, and snippets.

@versusvoid
Created April 17, 2015 10:28
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save versusvoid/4d30c3b965ade9686778 to your computer and use it in GitHub Desktop.
Save versusvoid/4d30c3b965ade9686778 to your computer and use it in GitHub Desktop.
InstancedStaticMeshComponent with physics support
#include "Test1.h"
#include "PhysInstancedStaticMeshComponent.h"
#include "PhysXIncludes.h"
#include "PhysicsPublic.h"
#include "Private/PhysicsEngine/PhysXSupport.h"
UPhysInstancedStaticMeshComponent::UPhysInstancedStaticMeshComponent(const FObjectInitializer& initializer)
: Super(initializer)
{
this->OnComponentHit.AddDynamic(this, &UPhysInstancedStaticMeshComponent::OnHit);
// Tick needed for updating displayed meshes when they are moved
PrimaryComponentTick.bCanEverTick = true;
PrimaryComponentTick.bStartWithTickEnabled = true;
}
// The only change needed here is in InitInstanceBody(), but it and CreateAllInstanceBodies()
// are private in UInstancedStaticMeshComponent, so we end up copypasting them wih copypaste-override
// of this one
void UPhysInstancedStaticMeshComponent::CreatePhysicsState()
{
check(InstanceBodies.Num() == 0);
FPhysScene* PhysScene = GetWorld()->GetPhysicsScene();
if (!PhysScene) { return; }
#if WITH_PHYSX
check(Aggregates.Num() == 0);
const int32 NumBodies = PerInstanceSMData.Num();
// Aggregates aren't used for static objects
const int32 NumAggregates = (Mobility == EComponentMobility::Movable) ? FMath::DivideAndRoundUp<int32>(NumBodies, AggregateMaxSize) : 0;
// Get the scene type from the main BodyInstance
const uint32 SceneType = BodyInstance.UseAsyncScene() ? PST_Async : PST_Sync;
for (int32 i = 0; i < NumAggregates; i++)
{
auto* Aggregate = GPhysXSDK->createAggregate(AggregateMaxSize, false);
Aggregates.Add(Aggregate);
PhysScene->GetPhysXScene(SceneType)->addAggregate(*Aggregate);
}
#endif
// Create all the bodies.
CreateAllInstanceBodies();
USceneComponent::CreatePhysicsState();
}
void UPhysInstancedStaticMeshComponent::CreateAllInstanceBodies()
{
int32 NumBodies = PerInstanceSMData.Num();
InstanceBodies.Init(NumBodies);
for (int32 i = 0; i < NumBodies; ++i)
{
InstanceBodies[i] = new FBodyInstance;
InitInstanceBody(i, InstanceBodies[i]);
}
}
void UPhysInstancedStaticMeshComponent::InitInstanceBody(int32 InstanceIdx, FBodyInstance* InstanceBodyInstance)
{
if (!StaticMesh)
{
UE_LOG(LogStaticMesh, Warning, TEXT("Unabled to create a body instance for %s in Actor %s. No StaticMesh set."), *GetName(), GetOwner() ? *GetOwner()->GetName() : TEXT("?"));
return;
}
check(InstanceIdx < PerInstanceSMData.Num());
check(InstanceIdx < InstanceBodies.Num());
check(InstanceBodyInstance);
UBodySetup* BodySetup = GetBodySetup();
check(BodySetup);
// Get transform of the instance
FTransform InstanceTransform = FTransform(PerInstanceSMData[InstanceIdx].Transform) * ComponentToWorld;
InstanceBodyInstance->CopyBodyInstancePropertiesFrom(&BodyInstance);
InstanceBodyInstance->InstanceBodyIndex = InstanceIdx; // Set body index
// original:
// make sure we never enable bSimulatePhysics for ISMComps
//InstanceBodyInstance->bSimulatePhysics = false;
// new:
InstanceBodyInstance->SetInstanceSimulatePhysics(IsSimulatingPhysics());
#if WITH_PHYSX
// original:
// Create physics body instance.
//auto* Aggregate = (Mobility == EComponentMobility::Movable) ? Aggregates[FMath::DivideAndRoundDown<int32>(InstanceIdx, AggregateMaxSize)] : nullptr;
//check(Mobility != EComponentMobility::Movable || Aggregate->getNbActors() < Aggregate->getMaxNbActors());
//InstanceBodyInstance->bAutoWeld = false; //We don't support this for instanced meshes.
//InstanceBodyInstance->InitBody(BodySetup, InstanceTransform, this, GetWorld()->GetPhysicsScene(), Aggregate);
// new:
auto* Aggregate = (Mobility == EComponentMobility::Movable && !BodyInstance.bSimulatePhysics) ? Aggregates[FMath::DivideAndRoundDown<int32>(InstanceIdx, AggregateMaxSize)] : nullptr;
check(Mobility != EComponentMobility::Movable || Aggregate->getNbActors() < Aggregate->getMaxNbActors());
InstanceBodyInstance->bAutoWeld = false; //We don't support this for instanced meshes.
InstanceBodyInstance->InitBody(BodySetup, InstanceTransform, this, GetWorld()->GetPhysicsScene(), Aggregate);
#endif //WITH_PHYSX
}
// Hackish way to handle impacts. Remember parameters and apply them when when Hit event will
// be received and we will know exact Instance.
void UPhysInstancedStaticMeshComponent::AddImpulse(FVector Impulse, FName BoneName, bool bVelChange) {
LastImpulse = Impulse;
}
void UPhysInstancedStaticMeshComponent::AddImpulseAtLocation(FVector Impulse, FVector Location, FName BoneName)
{
LastImpulse = Impulse;
LastLocation = Location;
}
void UPhysInstancedStaticMeshComponent::AddForceAtLocation(FVector Force, FVector Location, FName BoneName)
{
LastForce = Force;
LastLocation = Location;
}
bool UPhysInstancedStaticMeshComponent::CanEditSimulatePhysics() {
return true;
}
// Reusing flag from main BodyInstance
void UPhysInstancedStaticMeshComponent::SetSimulatePhysics(bool bSimulate) {
BodyInstance.bSimulatePhysics = bSimulate;
}
bool UPhysInstancedStaticMeshComponent::IsSimulatingPhysics(FName BoneName) const {
return BodyInstance.bSimulatePhysics;
}
// Can be optimized to update transform on moving instances only
void UPhysInstancedStaticMeshComponent::ReceiveTick(float DeltaSeconds) {
for (int i = 0; i < InstanceBodies.Num(); ++i) {
UpdateInstanceTransform(i, InstanceBodies[i]->GetUnrealWorldTransform());
}
}
// Physics handling
void UPhysInstancedStaticMeshComponent::OnHit(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit) {
FBodyInstance* BI = InstanceBodies[Hit.Item];
BI->AddImpulse(LastImpulse, false);
BI->AddImpulseAtPosition(LastImpulse, LastLocation);
BI->AddForceAtPosition(LastForce, LastLocation);
LastImpulse = FVector::ZeroVector;
LastForce = FVector::ZeroVector;
}
#pragma once
#include "InstancedStaticMeshComponent.h"
#include "PhysInstancedStaticMeshComponent.generated.h"
UCLASS()
class TEST1_API UPhysInstancedStaticMeshComponent : public UInstancedStaticMeshComponent
{
GENERATED_UCLASS_BODY()
UPROPERTY()
FVector LastImpulse;
UPROPERTY()
FVector LastForce;
UPROPERTY()
FVector LastLocation;
public:
virtual void CreatePhysicsState() override;
virtual void AddImpulse(FVector Impulse, FName BoneName = NAME_None, bool bVelChange = false) override;
virtual void AddImpulseAtLocation(FVector Impulse, FVector Location, FName BoneName = NAME_None) override;
virtual void AddForceAtLocation(FVector Force, FVector Location, FName BoneName = NAME_None) override;
virtual bool CanEditSimulatePhysics() override;
virtual void SetSimulatePhysics(bool bSimulate) override;
virtual bool IsSimulatingPhysics(FName BoneName = NAME_None) const override;
virtual void ReceiveTick(float DeltaSeconds) override;
private:
void InitInstanceBody(int32 InstanceIdx, FBodyInstance* BodyInstance);
void CreateAllInstanceBodies();
UFUNCTION()
virtual void OnHit(class AActor* OtherActor, class UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment