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:
Then I created a bunch of Triangle Data in the header:
Then I changed the UpdateBodySetup, from using the BoxElem to the ConvexElem.
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:
Code:
class TESTTERRAIN_API UTestCustomPhysics : public UShapeComponent {
Code:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Shape) TArray<FGenerateMeshTriangle> Triangles;
Code:
// 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 };
Code:
#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[i].Vertex0.Position); convex->VertexData.Add(Triangles[i].Vertex1.Position); convex->VertexData.Add(Triangles[i].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[i]; 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); }
Comment