UE4 在C++ 动态生成几何、BSP体、BRUSH ---- MESH_GENERATION

1649 篇文章 12 订阅
1623 篇文章 23 订阅

http://www.cnblogs.com/linqing/p/5015549.html



Posted on  2015-12-03 11:20  林清 阅读( 668) 评论( 0编辑  收藏

截至UE4  4.10 runtime 无法生成BSP类 ,只能通过自定义的Mesh的Vertex 进行绘制 ( Google 考证,能改UE4源码的请忽略 )

 

可用到的 UE4 集成的Render Plugins :

CustomMeshComponent

ProceduralMeshComponent

 

以下是参考

参考文献:

https://wiki.unrealengine.com/Procedural_Mesh_Generation

 https://answers.unrealengine.com/questions/100323/how-to-create-spawn-boxes-with-box-brush-in-c.html

https://forums.unrealengine.com/showthread.php?1552-Generate-Procedural-Mesh

https://answers.unrealengine.com/questions/22696/undefined.html

https://answers.unrealengine.com/questions/3096/procedural-geometry-in-c.html

复制粘贴备份:

 

Realtime subtractive geometry

1
 

Basically what I am looking to do is move one piece of geometry through another, and have the volume 'carved' out of it, similar to the effect seen here using a subtractive cylinder brush:

What I was going to attempt was to have a subtractive brush move along while creating duplicates of itself, however I can't seem to find a means to actually move these 'brush' objects at runtime or instantiate them at all.

If this is possible to do, please advise me of how I can achieve it. If it cannot be done like this, I would love to hear an alternate solution to 'carve' geometry in real time.

Thank you :)

Product Version: Not Selected
Tags:movementspawnbspbrushsubtractive
game mode.png (76.8 kB)
more ▼

asked Apr 01 '14 at 10:51 AM

miracle7 
138 ● ● ● 17

 
 

1 answer:sort voted first ▼
1

I don't think BSP was meant to be used at runtime.

If you plan on making only slight modifications to the mesh you could use dynamically created geometry like CustomMeshComponent and recalculate/update the vertices yourself. But if you thing about some serious changes to the shape my suggestion would be to look into voxel-based geometry instead.

more ▼

answered Apr 01 '14 at 11:16 AM

phoboz 
171 ● ● ● 12

  miracle7  Apr 02 '14 at 8:47 AM
 

Thanks for your response, I was originally thinking that some kind of high density voxel-based geometry would be ideal actually, however I was unaware that you could do that kind of thing in Unreal Engine.

I saw someone mention something similar in this question, however the link in the response doesn't work for me:https://answers.unrealengine.com/questions/3096/procedural-geometry-in-c.html

Also when I search for "voxel based geometry unreal engine" or similar search terms all I get is stuff about Voxel Cone Tracing.

If you have any idea of how to get voxel based geometry in Unreal Engine 4 please let me know, or if someone could at least point me in the right direction it would really help me out :)

 
  phoboz  Apr 02 '14 at 9:01 AM Newest
 

Those are old forum links, not sure if there is still some way to access them. AFAIK there is no built-in voxel in UE4, you will have to do some implementation ;) Unfortunately I never worked with voxels so can't help you any further, but might be worth looking around the forums. Good luck! :)

 

 

 

 

 

 

 

Procedural Mesh Generation

 

This is a very simple demonstration on how to generate procedural meshes and spawn them in game. It is not to be taken as an example of proper programming technique, but only as an indication to help you generate your own meshes.

You can get this up-to-date demo code in a working UE 4.7 project on GitHub. Please note that this no longer works in 4.8 because Epic added an official component with the same name (UProceduralMeshComponent) and similar functionality.

The following assumes you already have a project created. However, you will need to add RHI, RenderCore and ShaderCore modules in your build file.

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "RHI", "RenderCore", "ShaderCore" });

Creating a Generated Mesh class

The Unreal Engine 4 sources come with a CustomMeshComponent under Engine\Plugins\Runtime\CustomMeshComponent. I had trouble with using it as a plugin (link error), so I have reimplemented it. Essentially, copy the files to your project and rename them GeneratedMeshComponent (.h/.cpp). Replace all occurences of "Custom" to "Generated". You will need to add your project header to the cpp file. Otherwise, the code as I have use it is untouched.

Here they are as they appear in my test project:

GeneratedMeshComponent.h

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
 
#pragma once
 
#include "GeneratedMeshComponent.generated.h"
 
USTRUCT(BlueprintType)
struct FGeneratedMeshTriangle { GENERATED_USTRUCT_BODY()   UPROPERTY(EditAnywhere, Category=Triangle) FVector Vertex0;   UPROPERTY(EditAnywhere, Category=Triangle) FVector Vertex1;   UPROPERTY(EditAnywhere, Category=Triangle) FVector Vertex2; };   /** Component that allows you to specify custom triangle mesh geometry */ UCLASS(editinlinenew, meta=(BlueprintSpawnableComponent), ClassGroup=Rendering) class UGeneratedMeshComponent : public UMeshComponent, public IInterface_CollisionDataProvider { GENERATED_UCLASS_BODY()   public: /** Set the geometry to use on this triangle mesh */ UFUNCTION(BlueprintCallable, Category="Components|GeneratedMesh") bool SetGeneratedMeshTriangles(const TArray<FGeneratedMeshTriangle>& Triangles);   /** Description of collision */ UPROPERTY(BlueprintReadOnly, Category="Collision") class UBodySetup* ModelBodySetup;   // Begin UMeshComponent interface. virtual int32 GetNumMaterials() const OVERRIDE; // End UMeshComponent interface.   // Begin Interface_CollisionDataProvider Interface virtual bool GetPhysicsTriMeshData(struct FTriMeshCollisionData* CollisionData, bool InUseAllTriData) OVERRIDE; virtual bool ContainsPhysicsTriMeshData(bool InUseAllTriData) const OVERRIDE; virtual bool WantsNegXTriMesh() OVERRIDE { return false; } // End Interface_CollisionDataProvider Interface   // Begin UPrimitiveComponent interface. virtual FPrimitiveSceneProxy* CreateSceneProxy() OVERRIDE; virtual class UBodySetup* GetBodySetup() OVERRIDE; // End UPrimitiveComponent interface.   void UpdateBodySetup(); void UpdateCollision(); private:       // Begin USceneComponent interface. virtual FBoxSphereBounds CalcBounds(const FTransform & LocalToWorld) const OVERRIDE; // Begin USceneComponent interface.   /** */ TArray<FGeneratedMeshTriangle> GeneratedMeshTris;   friend class FGeneratedMeshSceneProxy; };

GeneratedMeshComponent.cpp

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. 
#include "MyProject5.h"
#include "DynamicMeshBuilder.h"
#include "GeneratedMeshComponent.h"
#include "Runtime/Launch/Resources/Version.h" // for ENGINE_xxx_VERSION
 
/** 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)

Creating a Generated Actor Class

Next you need to create an Actor-derived class so you can attach the UGeneratedMeshComponent to it and spawn it. Also, to make it accessible to blueprints. Note that this example class should have a TArray<FVector>& input parameter to describe the polyline we are going to rotate, but for our purposes the polyline has been hardcoded.

Create a new class called GameGeneratedActor (i.e. AGameGeneratedActor). There is also a Lathe function that I am including which is very simple, just for the purposes of this exercise. Here's the source for it:

GameGeneratedActor.h

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
 
#pragma once
 
#include "GameFramework/Actor.h"
#include "GeneratedMeshComponent.h"
#include "GameGeneratedActor.generated.h"
 
/** * */ UCLASS() class AGameGeneratedActor : public AActor { GENERATED_UCLASS_BODY()   void Lathe(const TArray<FVector>& points, TArray<FGeneratedMeshTriangle>& triangles, int segments = 64);   };

GameGeneratedActor.cpp

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
 
#include "MyProject5.h"
#include "GameGeneratedActor.h"
 
AGameGeneratedActor::AGameGeneratedActor(const class FPostConstructInitializeProperties& PCIP) : Super(PCIP) {   UGeneratedMeshComponent* mesh = PCIP.CreateDefaultSubobject<UGeneratedMeshComponent>(this, TEXT("GeneratedMesh"));   //Contains the points describing the polyline we are going to rotate TArray<FVector> points;   points.Add(FVector(20, 5, 0)); points.Add(FVector(15, 6, 0)); points.Add(FVector(12, 7, 0)); points.Add(FVector(11, 8, 0)); points.Add(FVector(8, 7, 0)); points.Add(FVector(7, 6, 0)); points.Add(FVector(4, 5, 0)); points.Add(FVector(3, 4, 0)); points.Add(FVector(2, 3, 0)); points.Add(FVector(1, 4, 0));   TArray<FGeneratedMeshTriangle> triangles; Lathe(points, triangles,128); mesh->SetGeneratedMeshTriangles(triangles);   RootComponent = mesh; }   void AGameGeneratedActor::Lathe(const TArray<FVector>& points, TArray<FGeneratedMeshTriangle>& triangles, int segments) { UE_LOG(LogClass, Log, TEXT("AGameGeneratedActor::Lathe POINTS %d"), points.Num());   TArray<FVector> verts;   // precompute some trig float angle = FMath::DegreesToRadians(360.0f / segments); float sinA = FMath::Sin(angle); float cosA = FMath::Cos(angle);   /* This implementation is rotation around the X Axis, other formulas below   Z Axis Rotation x' = x*cos q - y*sin q y' = x*sin q + y*cos q z' = z   X Axis Rotation y' = y*cos q - z*sin q z' = y*sin q + z*cos q x' = x   Y Axis Rotation z' = z*cos q - x*sin q x' = z*sin q + x*cos q y' = y */   //Working point array, in which we keep the rotated line we draw with TArray<FVector> wp; for (int i = 0; i < points.Num(); i++) { wp.Add(points[i]); }   // Add a first and last point on the axis to complete the triangles FVector p0(wp[0].X, 0, 0); FVector pLast(wp[wp.Num() - 1].X, 0, 0);   FGeneratedMeshTriangle tri; //for each segment draw the triangles clockwise for normals pointing out or counterclockwise for the opposite (this here does CW) for (int segment = 0; segment<segments; segment++) {   for 

Spawning the Actor

Once the project has been compiled, the new classes are accessible by Blueprints. So you will need to hook up a SpawnActor in some blueprint and set its class to GameGeneratedActor(I used a PlayerController, and its hooked to a HUD event), and it should look like this (note: I use the transform before and after as it seems to not consider scale on the spawn. In this example I suggest a scale of 10, otherwise the object will be too small)

Once you run the game and trigger the spawn, it should look like this:

For more info, see this thread: Generate Procedural Mesh

Enjoy!

Connecting standard UCustomMeshComponent

If you want to avoid code duplication and standard functionality of UCustomMeshComponent is more than enough for you, you can try to connect CustomMeshPlugin. To avoid link error you should use import/export macro for class:

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
 
#pragma once
 
...
 
/** Component that allows you to specify custom triangle mesh geometry */
UCLASS(hidecategories=(Object,LOD, Physics, Collision), editinlinenew, meta=(BlueprintSpawnableComponent), ClassGroup=Rendering) class CUSTOMMESHCOMPONENT_API UCustomMeshComponent : public UMeshComponent

Blueprint implementation

If you want to be able to generate a mesh from blueprints you can do this:

#include "GameFramework/Actor.h"
#include "ProceduralMeshComponent.h"
#include "CustomMeshBridge.generated.h"
 
 
/**
 *  code by Frederic Artus Nieto
 */
UCLASS() class SSS2_API ACustomMeshBridge : public AActor { GENERATED_UCLASS_BODY() public:   UFUNCTION(BlueprintPure, meta = (FriendlyName = "Make Triangle", Keywords = "New Triangle"), Category = ProceduralMesh) static FGeneratedMeshTriangle CreateTriangle(FVector vertex0, FVector vertex1, FVector vertex2);   UFUNCTION(BlueprintCallable, meta = (FriendlyName = "Set Mesh Material", Keywords = "Set Mesh Material"), Category = ProceduralMesh) static void SetMeshMaterial(UProceduralMeshComponent* component, UMaterialInterface* Material);   UFUNCTION(BlueprintCallable, meta = (FriendlyName = "Set Mesh Triangles", Keywords = "Set Mesh Triangles"), Category = ProceduralMesh) static void SetMeshTriangles(UProceduralMeshComponent* component, TArray<FGeneratedMeshTriangle> Triangles);   };
// code by Frederic Artus Nieto
#include "SSS2.h"
#include "DynamicMeshBuilder.h"
#include "CustomMeshBridge.h"
#include "ConstructorHelpers.h"
 
ACustomMeshBridge::ACustomMeshBridge(const class FPostConstructInitializeProperties& PCIP) : Super(PCIP) { }   FGeneratedMeshTriangle ACustomMeshBridge::CreateTriangle(FVector vertex0, FVector vertex1, FVector vertex2) { FGeneratedMeshTriangle tri; tri.Vertex0 = vertex0; tri.Vertex1 = vertex1; tri.Vertex2 = vertex2; return tri; }   void ACustomMeshBridge::SetMeshMaterial(GameGeneratedActor * component, UMaterialInterface* Material) { component->SetMaterial(0, Material); }   void ACustomMeshBridge::SetMeshTriangles(GameGeneratedActor * component, TArray<FGeneratedMeshTriangle> Triangles) { component->SetProceduralMeshTriangles(Triangles); }

Once you made this class, you will see some new nodes in the ProceduralMesh tab when you select a new node in the blueprint editor (you have to untick "Context Sensitive"). Note that you do need to have a GameGeneratedActor component in your actor to do that.

You then have to use the SetMeshTriangles node to apply the new triangles to the GameGeneratedActor. You can do it during runtime to make animated meshes (I had some laggy experience when updating too many vertices). You can apply a material to the mesh with the SetMeshMaterial node if you want. Note that there are no UVs.

Changelog

Update 1

The UGeneratedMeshComponent class has been updated to include collisions. However, this is currently only available within the Editor, as it is related to cooking physics at runtime (which is not supported outside the Editor at this time). Epic will include the capability for version 4.2 or 4.3. See this thread and this thread. Thanks to all who participated in those threads for pointers, credit goes to them. Remember to set SetActorCollisionsEnabled(true) in your Actor! Dmacesic (talk) 18:04, 11 April 2014 (UTC)

Update 2

Added vertex color fix from Ryvar as per this post.

Update 3

RHICreateXXXBuffer function signature changed for 4.3. Put in macro code to make code work both for 4.2 and 4.3

Update 4

Code updated for UE4.5 : #include Version.h for ENGINE_MINOR_VERSION macro, GetMaterialRelevance(::SM5) , CreatePrimitiveUniformBufferImmediate() with UseEditorDepthTest() and comments on mesh collision in cooked game.

Update 5

Add information about solving link problem.

Update 6

Added an example of blueprint implementation. If you made a little build please post a screen of the bluprint if none is there.

 

External link

本文原创 可以随意转载到任何网站 ~但是~ 转载也要按“基本法” 请注明原文出处和作者

Posted on  2015-12-03 11:20  林清 阅读( 668) 评论( 0编辑  收藏

截至UE4  4.10 runtime 无法生成BSP类 ,只能通过自定义的Mesh的Vertex 进行绘制 ( Google 考证,能改UE4源码的请忽略 )

 

可用到的 UE4 集成的Render Plugins :

CustomMeshComponent

ProceduralMeshComponent

 

以下是参考

参考文献:

https://wiki.unrealengine.com/Procedural_Mesh_Generation

 https://answers.unrealengine.com/questions/100323/how-to-create-spawn-boxes-with-box-brush-in-c.html

https://forums.unrealengine.com/showthread.php?1552-Generate-Procedural-Mesh

https://answers.unrealengine.com/questions/22696/undefined.html

https://answers.unrealengine.com/questions/3096/procedural-geometry-in-c.html

复制粘贴备份:

 

Realtime subtractive geometry

1
 

Basically what I am looking to do is move one piece of geometry through another, and have the volume 'carved' out of it, similar to the effect seen here using a subtractive cylinder brush:

What I was going to attempt was to have a subtractive brush move along while creating duplicates of itself, however I can't seem to find a means to actually move these 'brush' objects at runtime or instantiate them at all.

If this is possible to do, please advise me of how I can achieve it. If it cannot be done like this, I would love to hear an alternate solution to 'carve' geometry in real time.

Thank you :)

Product Version: Not Selected
Tags:movementspawnbspbrushsubtractive
game mode.png (76.8 kB)
more ▼

asked Apr 01 '14 at 10:51 AM

miracle7 
138 ● ● ● 17

 
 

1 answer:sort voted first ▼
1

I don't think BSP was meant to be used at runtime.

If you plan on making only slight modifications to the mesh you could use dynamically created geometry like CustomMeshComponent and recalculate/update the vertices yourself. But if you thing about some serious changes to the shape my suggestion would be to look into voxel-based geometry instead.

more ▼

answered Apr 01 '14 at 11:16 AM

phoboz 
171 ● ● ● 12

  miracle7  Apr 02 '14 at 8:47 AM
 

Thanks for your response, I was originally thinking that some kind of high density voxel-based geometry would be ideal actually, however I was unaware that you could do that kind of thing in Unreal Engine.

I saw someone mention something similar in this question, however the link in the response doesn't work for me:https://answers.unrealengine.com/questions/3096/procedural-geometry-in-c.html

Also when I search for "voxel based geometry unreal engine" or similar search terms all I get is stuff about Voxel Cone Tracing.

If you have any idea of how to get voxel based geometry in Unreal Engine 4 please let me know, or if someone could at least point me in the right direction it would really help me out :)

 
  phoboz  Apr 02 '14 at 9:01 AM Newest
 

Those are old forum links, not sure if there is still some way to access them. AFAIK there is no built-in voxel in UE4, you will have to do some implementation ;) Unfortunately I never worked with voxels so can't help you any further, but might be worth looking around the forums. Good luck! :)

 

 

 

 

 

 

 

Procedural Mesh Generation

 

This is a very simple demonstration on how to generate procedural meshes and spawn them in game. It is not to be taken as an example of proper programming technique, but only as an indication to help you generate your own meshes.

You can get this up-to-date demo code in a working UE 4.7 project on GitHub. Please note that this no longer works in 4.8 because Epic added an official component with the same name (UProceduralMeshComponent) and similar functionality.

The following assumes you already have a project created. However, you will need to add RHI, RenderCore and ShaderCore modules in your build file.

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "RHI", "RenderCore", "ShaderCore" });

Creating a Generated Mesh class

The Unreal Engine 4 sources come with a CustomMeshComponent under Engine\Plugins\Runtime\CustomMeshComponent. I had trouble with using it as a plugin (link error), so I have reimplemented it. Essentially, copy the files to your project and rename them GeneratedMeshComponent (.h/.cpp). Replace all occurences of "Custom" to "Generated". You will need to add your project header to the cpp file. Otherwise, the code as I have use it is untouched.

Here they are as they appear in my test project:

GeneratedMeshComponent.h

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
 
#pragma once
 
#include "GeneratedMeshComponent.generated.h"
 
USTRUCT(BlueprintType)
struct FGeneratedMeshTriangle { GENERATED_USTRUCT_BODY()   UPROPERTY(EditAnywhere, Category=Triangle) FVector Vertex0;   UPROPERTY(EditAnywhere, Category=Triangle) FVector Vertex1;   UPROPERTY(EditAnywhere, Category=Triangle) FVector Vertex2; };   /** Component that allows you to specify custom triangle mesh geometry */ UCLASS(editinlinenew, meta=(BlueprintSpawnableComponent), ClassGroup=Rendering) class UGeneratedMeshComponent : public UMeshComponent, public IInterface_CollisionDataProvider { GENERATED_UCLASS_BODY()   public: /** Set the geometry to use on this triangle mesh */ UFUNCTION(BlueprintCallable, Category="Components|GeneratedMesh") bool SetGeneratedMeshTriangles(const TArray<FGeneratedMeshTriangle>& Triangles);   /** Description of collision */ UPROPERTY(BlueprintReadOnly, Category="Collision") class UBodySetup* ModelBodySetup;   // Begin UMeshComponent interface. virtual int32 GetNumMaterials() const OVERRIDE; // End UMeshComponent interface.   // Begin Interface_CollisionDataProvider Interface virtual bool GetPhysicsTriMeshData(struct FTriMeshCollisionData* CollisionData, bool InUseAllTriData) OVERRIDE; virtual bool ContainsPhysicsTriMeshData(bool InUseAllTriData) const OVERRIDE; virtual bool WantsNegXTriMesh() OVERRIDE { return false; } // End Interface_CollisionDataProvider Interface   // Begin UPrimitiveComponent interface. virtual FPrimitiveSceneProxy* CreateSceneProxy() OVERRIDE; virtual class UBodySetup* GetBodySetup() OVERRIDE; // End UPrimitiveComponent interface.   void UpdateBodySetup(); void UpdateCollision(); private:       // Begin USceneComponent interface. virtual FBoxSphereBounds CalcBounds(const FTransform & LocalToWorld) const OVERRIDE; // Begin USceneComponent interface.   /** */ TArray<FGeneratedMeshTriangle> GeneratedMeshTris;   friend class FGeneratedMeshSceneProxy; };

GeneratedMeshComponent.cpp

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. 
#include "MyProject5.h"
#include "DynamicMeshBuilder.h"
#include "GeneratedMeshComponent.h"
#include "Runtime/Launch/Resources/Version.h" // for ENGINE_xxx_VERSION
 
/** 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

Creating a Generated Actor Class

Next you need to create an Actor-derived class so you can attach the UGeneratedMeshComponent to it and spawn it. Also, to make it accessible to blueprints. Note that this example class should have a TArray<FVector>& input parameter to describe the polyline we are going to rotate, but for our purposes the polyline has been hardcoded.

Create a new class called GameGeneratedActor (i.e. AGameGeneratedActor). There is also a Lathe function that I am including which is very simple, just for the purposes of this exercise. Here's the source for it:

GameGeneratedActor.h

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
 
#pragma once
 
#include "GameFramework/Actor.h"
#include "GeneratedMeshComponent.h"
#include "GameGeneratedActor.generated.h"
 
/** * */ UCLASS() class AGameGeneratedActor : public AActor { GENERATED_UCLASS_BODY()   void Lathe(const TArray<FVector>& points, TArray<FGeneratedMeshTriangle>& triangles, int segments = 64);   };

GameGeneratedActor.cpp

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
 
#include "MyProject5.h"
#include "GameGeneratedActor.h"
 
AGameGeneratedActor::AGameGeneratedActor(const class FPostConstructInitializeProperties& PCIP) : Super(PCIP) {   UGeneratedMeshComponent* mesh = PCIP.CreateDefaultSubobject<UGeneratedMeshComponent>(this, TEXT("GeneratedMesh"));   //Contains the points describing the polyline we are going to rotate TArray<FVector> points;   points.Add(FVector(20, 5, 0)); points.Add(FVector(15, 6, 0)); points.Add(FVector(12, 7, 0)); points.Add(FVector(11, 8, 0)); points.Add(FVector(8, 7, 0)); points.Add(FVector(7, 6, 0)); points.Add(FVector(4, 5, 0)); points.Add(FVector(3, 4, 0)); points.Add(FVector(2, 3, 0)); points.Add(FVector(1, 4, 0));   TArray<FGeneratedMeshTriangle> triangles; Lathe(points, triangles,128); mesh->SetGeneratedMeshTriangles(triangles);   RootComponent = mesh; }   void AGameGeneratedActor::Lathe(const TArray<FVector>& points, TArray<FGeneratedMeshTriangle>& triangles, int segments) { UE_LOG(LogClass, Log, TEXT("AGameGeneratedActor::Lathe POINTS %d"), points.Num());   TArray<FVector> verts;   // precompute some trig float angle = FMath::DegreesToRadians(360.0f / segments); float sinA = FMath::Sin(angle); float cosA = FMath::Cos(angle);   /* This implementation is rotation around the X Axis, other formulas below   Z Axis Rotation x' = x*cos q - y*sin q y' = x*sin q + y*cos q z' = z   X Axis Rotation y' = y*cos q - z*sin q z' = y*sin q + z*cos q x' = x   Y Axis Rotation z' = z*cos q - x*sin q x' = z*sin q + x*cos q y' = y */   //Working point array, in which we keep the rotated line we draw with TArray<FVector> wp; for (int i = 0; i < points.Num(); i++) { wp.Add(points[i]); }   // Add a first and last point on the axis to complete the triangles FVector p0(wp[0].X, 0, 0); FVector pLast(wp[wp.Num() - 1].X, 0, 0);   FGeneratedMeshTriangle tri; //for each segment draw the triangles clockwise for normals pointing out or counterclockwise for the opposite (this here does CW) for (int segment = 0; segment<segments; segment++) {   

Spawning the Actor

Once the project has been compiled, the new classes are accessible by Blueprints. So you will need to hook up a SpawnActor in some blueprint and set its class to GameGeneratedActor(I used a PlayerController, and its hooked to a HUD event), and it should look like this (note: I use the transform before and after as it seems to not consider scale on the spawn. In this example I suggest a scale of 10, otherwise the object will be too small)

Once you run the game and trigger the spawn, it should look like this:

For more info, see this thread: Generate Procedural Mesh

Enjoy!

Connecting standard UCustomMeshComponent

If you want to avoid code duplication and standard functionality of UCustomMeshComponent is more than enough for you, you can try to connect CustomMeshPlugin. To avoid link error you should use import/export macro for class:

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
 
#pragma once
 
...
 
/** Component that allows you to specify custom triangle mesh geometry */
UCLASS(hidecategories=(Object,LOD, Physics, Collision), editinlinenew, meta=(BlueprintSpawnableComponent), ClassGroup=Rendering) class CUSTOMMESHCOMPONENT_API UCustomMeshComponent : public UMeshComponent

Blueprint implementation

If you want to be able to generate a mesh from blueprints you can do this:

#include "GameFramework/Actor.h"
#include "ProceduralMeshComponent.h"
#include "CustomMeshBridge.generated.h"
 
 
/**
 *  code by Frederic Artus Nieto
 */
UCLASS() class SSS2_API ACustomMeshBridge : public AActor { GENERATED_UCLASS_BODY() public:   UFUNCTION(BlueprintPure, meta = (FriendlyName = "Make Triangle", Keywords = "New Triangle"), Category = ProceduralMesh) static FGeneratedMeshTriangle CreateTriangle(FVector vertex0, FVector vertex1, FVector vertex2);   UFUNCTION(BlueprintCallable, meta = (FriendlyName = "Set Mesh Material", Keywords = "Set Mesh Material"), Category = ProceduralMesh) static void SetMeshMaterial(UProceduralMeshComponent* component, UMaterialInterface* Material);   UFUNCTION(BlueprintCallable, meta = (FriendlyName = "Set Mesh Triangles", Keywords = "Set Mesh Triangles"), Category = ProceduralMesh) static void SetMeshTriangles(UProceduralMeshComponent* component, TArray<FGeneratedMeshTriangle> Triangles);   };
// code by Frederic Artus Nieto
#include "SSS2.h"
#include "DynamicMeshBuilder.h"
#include "CustomMeshBridge.h"
#include "ConstructorHelpers.h"
 
ACustomMeshBridge::ACustomMeshBridge(const class FPostConstructInitializeProperties& PCIP) : Super(PCIP) { }   FGeneratedMeshTriangle ACustomMeshBridge::CreateTriangle(FVector vertex0, FVector vertex1, FVector vertex2) { FGeneratedMeshTriangle tri; tri.Vertex0 = vertex0; tri.Vertex1 = vertex1; tri.Vertex2 = vertex2; return tri; }   void ACustomMeshBridge::SetMeshMaterial(GameGeneratedActor * component, UMaterialInterface* Material) { component->SetMaterial(0, Material); }   void ACustomMeshBridge::SetMeshTriangles(GameGeneratedActor * component, TArray<FGeneratedMeshTriangle> Triangles) { component->SetProceduralMeshTriangles(Triangles); }

Once you made this class, you will see some new nodes in the ProceduralMesh tab when you select a new node in the blueprint editor (you have to untick "Context Sensitive"). Note that you do need to have a GameGeneratedActor component in your actor to do that.

You then have to use the SetMeshTriangles node to apply the new triangles to the GameGeneratedActor. You can do it during runtime to make animated meshes (I had some laggy experience when updating too many vertices). You can apply a material to the mesh with the SetMeshMaterial node if you want. Note that there are no UVs.

Changelog

Update 1

The UGeneratedMeshComponent class has been updated to include collisions. However, this is currently only available within the Editor, as it is related to cooking physics at runtime (which is not supported outside the Editor at this time). Epic will include the capability for version 4.2 or 4.3. See this thread and this thread. Thanks to all who participated in those threads for pointers, credit goes to them. Remember to set SetActorCollisionsEnabled(true) in your Actor! Dmacesic (talk) 18:04, 11 April 2014 (UTC)

Update 2

Added vertex color fix from Ryvar as per this post.

Update 3

RHICreateXXXBuffer function signature changed for 4.3. Put in macro code to make code work both for 4.2 and 4.3

Update 4

Code updated for UE4.5 : #include Version.h for ENGINE_MINOR_VERSION macro, GetMaterialRelevance(::SM5) , CreatePrimitiveUniformBufferImmediate() with UseEditorDepthTest() and comments on mesh collision in cooked game.

Update 5

Add information about solving link problem.

Update 6

Added an example of blueprint implementation. If you made a little build please post a screen of the bluprint if none is there.

 

External link

本文原创 可以随意转载到任何网站 ~但是~ 转载也要按“基本法” 请注明原文出处和作者
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值