I want to create custom mesh’s with their own Physics. I’ve been trying to do this for a long time now.
My main problem here is that if I attached this object to the custom mesh, the mesh just dissapears when the physics starts…
You can recreate this problem if you use this line in your custom mesh code:
CustomMesh->SetSimulatePhysics(true);
It’d be nice for the custom mesh tutorial to include physics, if anyone wants to do this.
Has anyone solved this problem? I don’t fully understand how unreal manages these things. I’m only a one man team, and only have the forums to consult. Please help me senpai’s.
Success in Voxel Smooth Terrain:
As you can tell, the custom physics works well with static mesh’s attached. So I may research into them instead of the custom mesh’ code on the forum that extends UMeshComponent. But I heard that the latter, had alot of performance increases over static meshes.
Succeess in custom physics:
First I copied the UBoxComponent code and made my own class for custom physics:
class TESTTERRAIN_API UTestCustomPhysics : public UShapeComponent {
Then I created a bunch of Triangle Data in the header:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Shape)
TArray<FGenerateMeshTriangle> Triangles;
Then I changed the UpdateBodySetup, from using the BoxElem to the ConvexElem.
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "GameFramework/Actor.h"
#include "Components/ShapeComponent.h"
#include "GenerateMesh.h"
#include "TestCustomPhysics.generated.h"
UCLASS(ClassGroup = Shapes, editinlinenew, hidecategories = (Object, LOD, Lighting, TextureStreaming), meta = (BlueprintSpawnableComponent))
class TESTTERRAIN_API UTestCustomPhysics : public UShapeComponent {
GENERATED_UCLASS_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Shape)
TArray<FGenerateMeshTriangle> Triangles;
//UPROPERTY(BlueprintCallable, Category = Shape)
void AddCustomBox(FVector Size, FVector Position);
//UPROPERTY(BlueprintCallable, Category = Shape)
void AddCustomBox();
virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) OVERRIDE;
UPROPERTY(EditAnywhere, BlueprintReadOnly, export, Category = Shape)
FVector Size;
UPROPERTY(EditAnywhere, BlueprintReadOnly, export, Category = Shape)
FVector Offset;
// Begin UPrimitiveComponent interface.
virtual FPrimitiveSceneProxy* CreateSceneProxy() override;
virtual bool IsZeroExtent() const override;
virtual struct FCollisionShape GetCollisionShape(float Inflation = 0.0f) const override;
//virtual bool AreSymmetricRotations(const FQuat& A, const FQuat& B, const FVector& Scale3D) const override;
// End UPrimitiveComponent interface.
// Begin USceneComponent interface
virtual FBoxSphereBounds CalcBounds(const FTransform & LocalToWorld) const override;
//virtual void CalcBoundingCylinder(float& CylinderRadius, float& CylinderHalfHeight) const override;
// End USceneComponent interface
// Begin UShapeComponent interface
virtual void UpdateBodySetup() override;
// End UShapeComponent interface
};
#include "TestTerrain.h"
#include "Engine.h"
#include "DynamicMeshBuilder.h"
#include "TestCustomPhysics.h"
// Vertex Buffer
class FGeneratedMeshVertexBuffer : public FVertexBuffer {
public:
TArray<FDynamicMeshVertex> Vertices;
virtual void InitRHI() {
//#if ENGINE_MAJOR_VERSION >= 4 && ENGINE_MINOR_VERSION >= 3
FRHIResourceCreateInfo CreateInfo;
VertexBufferRHI = RHICreateVertexBuffer(Vertices.Num() * sizeof(FDynamicMeshVertex), BUF_Static, CreateInfo);
//#else
// VertexBufferRHI = RHICreateVertexBuffer(Vertices.Num() * sizeof(FDynamicMeshVertex), NULL, BUF_Static);
//#endif
// Copy the vertex data into the vertex buffer.
void* VertexBufferData = RHILockVertexBuffer(VertexBufferRHI, 0, Vertices.Num() * sizeof(FDynamicMeshVertex), RLM_WriteOnly);
FMemory::Memcpy(VertexBufferData, Vertices.GetTypedData(), Vertices.Num() * sizeof(FDynamicMeshVertex));
RHIUnlockVertexBuffer(VertexBufferRHI);
}
};
// Index Buffer
class FGeneratedMeshIndexBuffer : public FIndexBuffer
{
public:
TArray<int32> Indices;
virtual void InitRHI() {
//#if ENGINE_MAJOR_VERSION >= 4 && ENGINE_MINOR_VERSION >= 3
FRHIResourceCreateInfo CreateInfo;
IndexBufferRHI = RHICreateIndexBuffer(sizeof(int32), Indices.Num() * sizeof(int32), BUF_Static, CreateInfo);
//#else
// IndexBufferRHI = RHICreateIndexBuffer(sizeof(int32), Indices.Num() * sizeof(int32), NULL, BUF_Static);
//#endif
// Write the indices to the index buffer.
void* Buffer = RHILockIndexBuffer(IndexBufferRHI, 0, Indices.Num() * sizeof(int32), RLM_WriteOnly);
FMemory::Memcpy(Buffer, Indices.GetTypedData(), Indices.Num() * sizeof(int32));
RHIUnlockIndexBuffer(IndexBufferRHI);
}
};
// Vertex Factory
class FGeneratedMeshVertexFactory : public FLocalVertexFactory {
public:
FGeneratedMeshVertexFactory()
{}
// Initialization
void Init(const FGeneratedMeshVertexBuffer* VertexBuffer) {
check(!IsInRenderingThread());
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
InitGeneratedMeshVertexFactory,
FGeneratedMeshVertexFactory*, VertexFactory, this,
const FGeneratedMeshVertexBuffer*, VertexBuffer, VertexBuffer,
{
// Initialize the vertex factory's stream components.
DataType NewData;
NewData.PositionComponent = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(VertexBuffer, FDynamicMeshVertex, Position, VET_Float3);
NewData.TextureCoordinates.Add(
FVertexStreamComponent(VertexBuffer, STRUCT_OFFSET(FDynamicMeshVertex, TextureCoordinate), sizeof(FDynamicMeshVertex), VET_Float2)
);
NewData.TangentBasisComponents[0] = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(VertexBuffer, FDynamicMeshVertex, TangentX, VET_PackedNormal);
NewData.TangentBasisComponents[1] = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(VertexBuffer, FDynamicMeshVertex, TangentZ, VET_PackedNormal);
NewData.ColorComponent = STRUCTMEMBER_VERTEXSTREAMCOMPONENT(VertexBuffer, FDynamicMeshVertex, Color, VET_Color);
VertexFactory->SetData(NewData);
});
}
};
void UTestCustomPhysics::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) {
AddCustomBox();
Super::PostEditChangeProperty(PropertyChangedEvent);
}
UTestCustomPhysics::UTestCustomPhysics(const class FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
{
//SphereRadius = 130;
Size = FVector(500, 500, 500);
ShapeColor = FColor(255, 0, 0, 255);
AddCustomBox();
//bUseEditorCompositing = true;
}
void UTestCustomPhysics::AddCustomBox() {
AddCustomBox(Size, Offset);
}
void UTestCustomPhysics::AddCustomBox(FVector Size, FVector Position) {
// make vertex positions
FVector p0 = FVector(Position.X, Position.Y, Position.Z);
FVector p1 = FVector(Position.X, Position.Y, Position.Z + Size.Z);
FVector p2 = FVector(Position.X + Size.X, Position.Y, Position.Z + Size.Z);
FVector p3 = FVector(Position.X + Size.X, Position.Y, Position.Z);
FVector p4 = FVector(Position.X + Size.X, Position.Y + Size.Y, Position.Z);
FVector p5 = FVector(Position.X + Size.X, Position.Y + Size.Y, Position.Z + Size.Z);
FVector p6 = FVector(Position.X, Position.Y + Size.Y, Position.Z + Size.Z);
FVector p7 = FVector(Position.X, Position.Y + Size.Y, Position.Z);
// Verticies used in the triangles
FGenerateMeshTriangleVertex v0;
FGenerateMeshTriangleVertex v1;
FGenerateMeshTriangleVertex v2;
FGenerateMeshTriangleVertex v3;
v0.U = 0; v0.V = 0;
v1.U = 0; v1.V = .5;
v2.U = .5; v2.V = .5;
v3.U = .5; v3.V = 0;
float LightRed = 1;
float LightGreen = 1;
float LightBlue = 1;
v0.Color = FColor(LightRed, LightGreen, LightBlue, 255);
v1.Color = FColor(LightRed, LightGreen, LightBlue, 255);
v2.Color = FColor(LightRed, LightGreen, LightBlue, 255);
v3.Color = FColor(LightRed, LightGreen, LightBlue, 255);
v0.U = 0; v0.V = 0;
v1.U = 0; v1.V = 0 + 1;
v2.U = 0 + 1; v2.V = 0 + 1;
v3.U = 0 + 1; v3.V = 0;
FGenerateMeshTriangle t1;
FGenerateMeshTriangle t2;
// front face
v0.Position = p0;
v1.Position = p1;
v2.Position = p2;
v3.Position = p3;
t1.Vertex0 = v0;
t1.Vertex1 = v1;
t1.Vertex2 = v2;
t2.Vertex0 = v0;
t2.Vertex1 = v2;
t2.Vertex2 = v3;
Triangles.Add(t1);
Triangles.Add(t2);
//back face
v0.Position = p4;
v1.Position = p5;
v2.Position = p6;
v3.Position = p7;
t1.Vertex0 = v0;
t1.Vertex1 = v1;
t1.Vertex2 = v2;
t2.Vertex0 = v0;
t2.Vertex1 = v2;
t2.Vertex2 = v3;
Triangles.Add(t1);
Triangles.Add(t2);
// left face
v0.Position = p7;
v1.Position = p6;
v2.Position = p1;
v3.Position = p0;
t1.Vertex0 = v0;
t1.Vertex1 = v1;
t1.Vertex2 = v2;
t2.Vertex0 = v0;
t2.Vertex1 = v2;
t2.Vertex2 = v3;
Triangles.Add(t1);
Triangles.Add(t2);
// right face
v0.Position = p3;
v1.Position = p2;
v2.Position = p5;
v3.Position = p4;
t1.Vertex0 = v0;
t1.Vertex1 = v1;
t1.Vertex2 = v2;
t2.Vertex0 = v0;
t2.Vertex1 = v2;
t2.Vertex2 = v3;
Triangles.Add(t1);
Triangles.Add(t2);
// top face
v0.Position = p1;
v1.Position = p6;
v2.Position = p5;
v3.Position = p2;
t1.Vertex0 = v0;
t1.Vertex1 = v1;
t1.Vertex2 = v2;
t2.Vertex0 = v0;
t2.Vertex1 = v2;
t2.Vertex2 = v3;
Triangles.Add(t1);
Triangles.Add(t2);
// bottom face
v0.Position = p3;
v1.Position = p4;
v2.Position = p7;
v3.Position = p0;
t1.Vertex0 = v0;
t1.Vertex1 = v1;
t1.Vertex2 = v2;
t2.Vertex0 = v0;
t2.Vertex1 = v2;
t2.Vertex2 = v3;
Triangles.Add(t1);
Triangles.Add(t2);
}
FBoxSphereBounds UTestCustomPhysics::CalcBounds(const FTransform & LocalToWorld) const {
return FBoxSphereBounds(FBox(-Size, Size).TransformBy(LocalToWorld));
}
void UTestCustomPhysics::UpdateBodySetup() {
if (ShapeBodySetup == NULL || ShapeBodySetup->IsPendingKill())
{
ShapeBodySetup = ConstructObject<UBodySetup>(UBodySetup::StaticClass(), this);
ShapeBodySetup->CollisionTraceFlag = CTF_UseSimpleAsComplex;
ShapeBodySetup->AggGeom.ConvexElems.Add(FKConvexElem());
}
FKConvexElem* convex = ShapeBodySetup->AggGeom.ConvexElems.GetTypedData();
for (int32 i = convex->VertexData.Num() - 1; i > -1; i--) {
convex->VertexData.RemoveAt(i);
}
for (int32 i = 0; i < Triangles.Num(); i++) {
convex->VertexData.Add(Triangles*.Vertex0.Position);
convex->VertexData.Add(Triangles*.Vertex1.Position);
convex->VertexData.Add(Triangles*.Vertex2.Position);
}
}
/*void UTestCustomPhysics::SetSphereRadius(float InSphereRadius, bool bUpdateOverlaps)
{
//SphereRadius = InSphereRadius;
MarkRenderStateDirty();
if (bPhysicsStateCreated)
{
DestroyPhysicsState();
UpdateBodySetup();
CreatePhysicsState();
if (bUpdateOverlaps && IsCollisionEnabled() && GetOwner())
{
UpdateOverlaps();
}
}
}*/
bool UTestCustomPhysics::IsZeroExtent() const
{
return (Size.X == 0 && Size.Y == 0 && Size.Z == 0);
}
FPrimitiveSceneProxy* UTestCustomPhysics::CreateSceneProxy() {
/** Represents a DrawLightRadiusComponent to the scene manager. */
class FSphereSceneProxy : public FPrimitiveSceneProxy {
private:
const uint32 bDrawOnlyIfSelected : 1;
const FColor SphereColor;
const UMaterialInterface* ShapeMaterial;
const FVector Size;
UMaterialInterface* Material;
FGeneratedMeshVertexBuffer VertexBuffer;
FGeneratedMeshIndexBuffer IndexBuffer;
FGeneratedMeshVertexFactory VertexFactory;
FMaterialRelevance MaterialRelevance;
public:
/** Initialization constructor. */
FSphereSceneProxy(const UTestCustomPhysics* InComponent)
: FPrimitiveSceneProxy(InComponent)
, bDrawOnlyIfSelected(InComponent->bDrawOnlyIfSelected)
, SphereColor(InComponent->ShapeColor)
, ShapeMaterial(InComponent->ShapeMaterial)
, Size(InComponent->Size)
{
for (int32 i = 0; i < InComponent->Triangles.Num(); i++) {
const FGenerateMeshTriangle& Tri = InComponent->Triangles*;
FDynamicMeshVertex Vert0;
Vert0.Position = Tri.Vertex0.Position;
Vert0.Color = Tri.Vertex0.Color;
//Vert0.SetTangents(TangentX, TangentY, TangentZ);
Vert0.SetTangents(Tri.Vertex0.Normal, Tri.Vertex1.Normal, Tri.Vertex2.Normal);
Vert0.TextureCoordinate.Set(Tri.Vertex0.U, Tri.Vertex0.V);
int32 VIndex = VertexBuffer.Vertices.Add(Vert0);
IndexBuffer.Indices.Add(VIndex);
// new stuff
FDynamicMeshVertex Vert1;
Vert1.Position = Tri.Vertex1.Position;
Vert1.Color = Tri.Vertex1.Color;
//Vert1.SetTangents(TangentX, TangentY, TangentZ);
Vert1.SetTangents(Tri.Vertex0.Normal, Tri.Vertex1.Normal, Tri.Vertex2.Normal);
Vert1.TextureCoordinate.Set(Tri.Vertex1.U, Tri.Vertex1.V);
VIndex = VertexBuffer.Vertices.Add(Vert1);
IndexBuffer.Indices.Add(VIndex);
FDynamicMeshVertex Vert2;
Vert2.Position = Tri.Vertex2.Position;
Vert2.Color = Tri.Vertex2.Color;
//Vert2.SetTangents(TangentX, TangentY, TangentZ);
Vert2.SetTangents(Tri.Vertex0.Normal, Tri.Vertex1.Normal, Tri.Vertex2.Normal);
Vert2.TextureCoordinate.Set(Tri.Vertex2.U, Tri.Vertex2.V);
VIndex = VertexBuffer.Vertices.Add(Vert2);
IndexBuffer.Indices.Add(VIndex);
}
// Init vertex factory
VertexFactory.Init(&VertexBuffer);
// Enqueue initialization of render resource
BeginInitResource(&VertexBuffer);
BeginInitResource(&IndexBuffer);
BeginInitResource(&VertexFactory);
// Grab material
Material = InComponent->GetMaterial(0);
if (Material == NULL) {
Material = UMaterial::GetDefaultMaterial(MD_Surface);
}
bWillEverBeLit = false;
}
virtual ~FSphereSceneProxy() {
VertexBuffer.ReleaseResource();
IndexBuffer.ReleaseResource();
VertexFactory.ReleaseResource();
}
// this is where the actual rendering is done
/*virtual void GetDynamicMeshElements(const TArray<const FSceneView*>& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_BoxSceneProxy_GetDynamicMeshElements);
const FMatrix& LocalToWorld = GetLocalToWorld();
const FColor DrawColor = GetSelectionColor(SphereColor, IsSelected(), IsHovered(), false);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
if (VisibilityMap & (1 << ViewIndex))
{
FPrimitiveDrawInterface* PDI = Collector.GetPDI(ViewIndex);
DrawOrientedWireBox(PDI, LocalToWorld.GetOrigin(), LocalToWorld.GetScaledAxis(EAxis::X), LocalToWorld.GetScaledAxis(EAxis::Y), LocalToWorld.GetScaledAxis(EAxis::Z), Size, DrawColor, SDPG_World);
}
}
}*/
virtual void DrawDynamicElements(FPrimitiveDrawInterface* PDI, const FSceneView* View) override
{
/*QUICK_SCOPE_CYCLE_COUNTER(STAT_BoxSceneProxy_DrawDynamicElements);
const FMatrix& LocalToWorld = GetLocalToWorld();
const FColor DrawColor = GetSelectionColor(SphereColor, IsSelected(), IsHovered(), false);
DrawOrientedWireBox(PDI, LocalToWorld.GetOrigin(), LocalToWorld.GetScaledAxis(EAxis::X), LocalToWorld.GetScaledAxis(EAxis::Y), LocalToWorld.GetScaledAxis(EAxis::Z), Size, DrawColor, SDPG_World);*/
QUICK_SCOPE_CYCLE_COUNTER(STAT_GeneratedMeshSceneProxy_DrawDynamicElements);
const bool bWireframe = View->Family->EngineShowFlags.Wireframe;
FColoredMaterialRenderProxy WireframeMaterialInstance(
WITH_EDITOR ? GEngine->WireframeMaterial->GetRenderProxy(IsSelected()) : NULL,
//GEngine->WireframeMaterial->GetRenderProxy(IsSelected()),
//FLinearColor(0, 0.5f, 1.f)
FLinearColor(1.0f, 0.0f, 0.f)
);
FMaterialRenderProxy* MaterialProxy = NULL;
if (bWireframe)
{
MaterialProxy = &WireframeMaterialInstance;
}
else
{
MaterialProxy = Material->GetRenderProxy(IsSelected());
}
// Draw the mesh.
FMeshBatch Mesh;
FMeshBatchElement& BatchElement = Mesh.Elements[0];
Mesh.bWireframe = bWireframe;
Mesh.VertexFactory = &VertexFactory;
Mesh.MaterialRenderProxy = MaterialProxy;
Mesh.ReverseCulling = IsLocalToWorldDeterminantNegative();
Mesh.Type = PT_TriangleList;
Mesh.DepthPriorityGroup = SDPG_World;
Mesh.CastShadow = true;
BatchElement.IndexBuffer = &IndexBuffer;
BatchElement.FirstIndex = 0;
BatchElement.NumPrimitives = IndexBuffer.Indices.Num() / 3;
BatchElement.MinVertexIndex = 0;
//BatchElement.MaxVertexIndex = VertexBuffer.Vertices.Num() - 1;
//BatchElement.PrimitiveUniformBuffer = CreatePrimitiveUniformBufferImmediate(GetLocalToWorld(), GetBounds(), GetLocalBounds(), true);
BatchElement.PrimitiveUniformBuffer = CreatePrimitiveUniformBufferImmediate(GetLocalToWorld(), GetBounds(), GetLocalBounds(), true, true);
PDI->DrawMesh(Mesh);
}
virtual FPrimitiveViewRelevance GetViewRelevance(const FSceneView* View) override {
FPrimitiveViewRelevance Result;
Result.bDrawRelevance = IsShown(View);
Result.bShadowRelevance = IsShadowCast(View);
Result.bDynamicRelevance = true;
MaterialRelevance.SetPrimitiveViewRelevance(Result);
return Result;
}
virtual bool CanBeOccluded() const OVERRIDE
{
return !MaterialRelevance.bDisableDepthTest;
}
virtual uint32 GetMemoryFootprint(void) const override { return(sizeof(*this) + GetAllocatedSize()); }
uint32 GetAllocatedSize(void) const { return(FPrimitiveSceneProxy::GetAllocatedSize()); }
};
return new FSphereSceneProxy(this);
}
FCollisionShape UTestCustomPhysics::GetCollisionShape(float Inflation) const {
//if (IsBox) {
FVector Extent = Size + Inflation;
if (Inflation < 0.f)
{
// Don't shrink below zero size.
Extent = Extent.ComponentMax(FVector::ZeroVector);
}
return FCollisionShape::MakeBox(Extent);
}