Announcement

Collapse
No announcement yet.

Generate Procedural Mesh

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Here's my current working classes for procedural meshes that calculate tangents correctly. It is currently highly inefficient since it stores an extra copy of all of the triangles in the mesh. If I get some free time I'll make a tutorial on how I make the procedural doors.

    Code:
    // UE4 Procedural Mesh Generation from the Epic Wiki (https://wiki.unrealengine.com/Procedural_Mesh_Generation)
    //
    // forked from "Engine/Plugins/Runtime/CustomMeshComponent/Source/CustomMeshComponent/Classes/CustomMeshComponent.h"
    
    #pragma once
    
    #include "ProceduralMeshComponent.generated.h"
    
    USTRUCT(BlueprintType)
    struct FProceduralMeshVertex
    {
    	GENERATED_USTRUCT_BODY()
    
    	UPROPERTY(EditAnywhere, Category = Vertex)
    	FVector Position;
    
    	UPROPERTY(EditAnywhere, Category = Vertex)
    		FColor Color;
    
    	UPROPERTY(EditAnywhere, Category = Vertex)
    		float U;
    
    	UPROPERTY(EditAnywhere, Category = Vertex)
    		float V;
    
    	UPROPERTY(EditAnywhere, Category = Vertex)
    		FVector vNormal = FVector(0.f, 1.f, 0.f);
    
    	UPROPERTY(EditAnywhere, Category = Vertex)
    		FVector vTangent;
    
    	UPROPERTY(EditAnywhere, Category = Vertex)
    		FVector vBitangent;
    
    	UPROPERTY(EditAnywhere, Category = Vertex)
    		bool manualNormals = false;
    };
    
    USTRUCT(BlueprintType)
    struct FProceduralMeshTriangle
    {
    	GENERATED_USTRUCT_BODY()
    
    	UPROPERTY(EditAnywhere, Category = Triangle)
    		FProceduralMeshVertex Vertex0;
    
    	UPROPERTY(EditAnywhere, Category = Triangle)
    		FProceduralMeshVertex Vertex1;
    
    	UPROPERTY(EditAnywhere, Category = Triangle)
    		FProceduralMeshVertex Vertex2;
    
    	UPROPERTY(EditAnywhere, Category = Triangle)
    		FVector faceNormal = FVector(0.f, 1.f, 0.f);
    
    	UPROPERTY(EditAnywhere, Category = Triangle)
    		int32 SmoothingGroup = 0;
    
    	//used to keep track of the triangles intended position in the array
    	UPROPERTY(EditAnywhere, Category = Triangle)
    		int32 triangleIndexNumber;
    
    	UPROPERTY(EditAnywhere, Category = Triangle)
    		TArray<int32> attachedTriangles;
    
    	UPROPERTY(EditAnywhere, Category = Triangle)
    		bool normalsHaveBeenCalculated = false;
    };
    
    USTRUCT(BlueprintType)
    struct FProceduralMeshTriangleSmoothingGroup
    {
    	GENERATED_USTRUCT_BODY()
    
    	UPROPERTY(EditAnywhere, Category = SmoothingGroup)
    	TArray<FProceduralMeshTriangle> SGTriangle;
    
    	UPROPERTY(EditAnywhere, Category = SmoothingGroup)
    		int32 SGIndexNumber;
    
    	UPROPERTY(EditAnywhere, Category = SmoothingGroup)
    		float SmoothingTolernace;
    };
    
    /** Component that allows you to specify custom triangle mesh geometry */
    UCLASS(editinlinenew, meta = (BlueprintSpawnableComponent), ClassGroup = Rendering)
    class UProceduralMeshComponent : public UMeshComponent, public IInterface_CollisionDataProvider
    {
    	GENERATED_UCLASS_BODY()
    
    public:
    	/** Set the geometry to use on this triangle mesh */
    	UFUNCTION(BlueprintCallable, Category = "Components|ProceduralMesh")
    		bool SetProceduralMeshTriangles(const TArray<FProceduralMeshTriangle>& Triangles);
    
    	/** Description of collision */
    	UPROPERTY(BlueprintReadOnly, Category = "Collision")
    	class UBodySetup* ModelBodySetup;
    
    	// 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.
    
    	// Begin UMeshComponent interface.
    	virtual int32 GetNumMaterials() const override;
    	// End UMeshComponent interface.
    
    	void UpdateBodySetup();
    	void UpdateCollision();
    
    	//calcualte our normals, tangents, and bitangents
    	FProceduralMeshTriangle GetNormals(TArray<FProceduralMeshTriangle>& Triangles);
    
    private:
    	// Begin USceneComponent interface.
    	virtual FBoxSphereBounds CalcBounds(const FTransform & LocalToWorld) const override;
    	// Begin USceneComponent interface.
    
    	/** */
    	TArray<FProceduralMeshTriangle> ProceduralMeshTris;
    
    	friend class FProceduralMeshSceneProxy;
    };

    Code:
    /** Scene proxy */
    class FProceduralMeshSceneProxy : public FPrimitiveSceneProxy
    {
    public:
    
    	FProceduralMeshSceneProxy(UProceduralMeshComponent* Component)
    		: FPrimitiveSceneProxy(Component)
    #if ENGINE_MAJOR_VERSION >= 4 && ENGINE_MINOR_VERSION >= 5
    		, MaterialRelevance(Component->GetMaterialRelevance(GetScene().GetFeatureLevel()))
    #else
    		, MaterialRelevance(Component->GetMaterialRelevance())
    #endif
    	{
    		//Store an array of identical triangles
    		TArray<FProceduralMeshTriangle> storedTriangles;
    		for (int TriIdx = 0; TriIdx < Component->ProceduralMeshTris.Num(); TriIdx++)
    		{
    			storedTriangles.Add(Component->ProceduralMeshTris[TriIdx]);
    			storedTriangles[TriIdx].triangleIndexNumber = TriIdx;
    
    			//http://www.lighthouse3d.com/opengl/terrain/index.php3?normals
    			const FVector Edge01 = (storedTriangles[TriIdx].Vertex1.Position - storedTriangles[TriIdx].Vertex0.Position);
    			const FVector Edge02 = (storedTriangles[TriIdx].Vertex2.Position - storedTriangles[TriIdx].Vertex0.Position);
    			FVector faceNormal = -(Edge01 ^ Edge02).GetSafeNormal();
    
    			//If we don't have manually input vertex normals then calculate our own
    			if (!storedTriangles[TriIdx].Vertex0.manualNormals || !storedTriangles[TriIdx].Vertex1.manualNormals || !storedTriangles[TriIdx].Vertex2.manualNormals)
    			{
    				storedTriangles[TriIdx].Vertex0.vNormal = faceNormal;
    				storedTriangles[TriIdx].Vertex1.vNormal = faceNormal;
    				storedTriangles[TriIdx].Vertex2.vNormal = faceNormal;
    			}
    	}
    
    		
    		//search predefined attached triangles for matching vertices and average tangents
    		for (int i = 0; i < storedTriangles.Num() - 1; i++)
    		{
    			//store an array of all attached triangles so that they can have thier normals calculated
    			TArray<FProceduralMeshTriangle> attachedTriangles;
    
    			
    			if (!storedTriangles[i].normalsHaveBeenCalculated)
    			{
    				attachedTriangles.Add(storedTriangles[i]);
    				storedTriangles[i].normalsHaveBeenCalculated = true;
    
    				//add all attached triangles to the array that gets bulk normals calculation
    				//set normalsHaveBeenCalculated to true to prevent the values from getting overwritten
    				for (int j = 0; j < storedTriangles[i].attachedTriangles.Num(); j++)
    				{
    					attachedTriangles.Add(storedTriangles[storedTriangles[i].attachedTriangles[j]]);
    					storedTriangles[storedTriangles[i].attachedTriangles[j]].normalsHaveBeenCalculated = true;
    				}
    				
    
    				//Lengyel, Eric. “Computing Tangent Space Basis Vectors for an Arbitrary Mesh”. Terathon Software 3D Graphics Library, 2001. http://www.terathon.com/code/tangent.html
    
    				for (long a = 0; a < attachedTriangles.Num(); a++)
    				{
    					//determine which way the normals are facing
    					int8 handedness;
    
    
    					FVector v1 = attachedTriangles[a].Vertex0.Position;
    					FVector v2 = attachedTriangles[a].Vertex1.Position;
    					FVector v3 = attachedTriangles[a].Vertex2.Position;
    
    					FVector2D w1 = FVector2D(attachedTriangles[a].Vertex0.U, attachedTriangles[a].Vertex0.V);
    					FVector2D w2 = FVector2D(attachedTriangles[a].Vertex1.U, attachedTriangles[a].Vertex1.V);
    					FVector2D w3 = FVector2D(attachedTriangles[a].Vertex2.U, attachedTriangles[a].Vertex2.V);
    
    					float x1 = v2.X - v1.X;
    					float x2 = v3.X - v1.X;
    					float y1 = v2.Y - v1.Y;
    					float y2 = v3.Y - v1.Y;
    					float z1 = v2.Z - v1.Z;
    					float z2 = v3.Z - v1.Z;
    
    					float s1 = w2.X - w1.X;
    					float s2 = w3.X - w1.X;
    					float t1 = w2.Y - w1.Y;
    					float t2 = w3.Y - w1.Y;
    
    					float r = 1.0F / (s1 * t2 - s2 * t1);
    					FVector sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r,
    						(t2 * z1 - t1 * z2) * r);
    					FVector tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r,
    						(s1 * z2 - s2 * z1) * r);
    
    					attachedTriangles[a].Vertex0.vTangent += sdir;
    					attachedTriangles[a].Vertex1.vTangent += sdir;
    					attachedTriangles[a].Vertex2.vTangent += sdir;
    
    					attachedTriangles[a].Vertex0.vBitangent += tdir;
    					attachedTriangles[a].Vertex1.vBitangent += tdir;
    					attachedTriangles[a].Vertex2.vBitangent += tdir;
    
    
    					//attachedTriangles[a].
    					storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex0.vTangent = attachedTriangles[a].Vertex0.vTangent;
    					storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex0.vBitangent = attachedTriangles[a].Vertex0.vBitangent;
    
    					storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex1.vTangent = attachedTriangles[a].Vertex1.vTangent;
    					storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex1.vBitangent = attachedTriangles[a].Vertex1.vBitangent;
    
    					storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex2.vTangent = attachedTriangles[a].Vertex2.vTangent;
    					storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex2.vBitangent = attachedTriangles[a].Vertex2.vBitangent;
    
    
    					const FVector n = attachedTriangles[a].Vertex0.vNormal;
    					const FVector t = attachedTriangles[a].Vertex0.vTangent;
    
    					storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex0.vNormal = storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex0.vNormal;
    
    					//calculateHandedness
    					handedness = (((n ^ t) | storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex0.vBitangent) < 0.0F) ? -1.0F : 1.0F;
    
    					// Gram-Schmidt orthogonalize
    					storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex0.vTangent = (t - n * (n | t)).GetSafeNormal();
    
    					//calculate Bitangent
    					storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex0.vBitangent = (n ^ t) * handedness;
    
    
    
    					const FVector nn = attachedTriangles[a].Vertex1.vNormal;
    					const FVector tt = attachedTriangles[a].Vertex1.vTangent;
    
    					storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex1.vNormal = storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex1.vNormal;
    
    					//calculateHandedness
    					handedness = (((nn ^ tt) | storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex1.vBitangent) < 0.0F) ? -1.0F : 1.0F;
    
    					// Gram-Schmidt orthogonalize
    					storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex1.vTangent = (tt - nn * (nn | tt)).GetSafeNormal();
    
    					//calculate Bitangent
    					storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex1.vBitangent = (nn ^ tt) * handedness;
    
    
    
    					const FVector nnn = attachedTriangles[a].Vertex2.vNormal;
    					const FVector ttt = attachedTriangles[a].Vertex2.vTangent;
    
    					storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex2.vNormal = storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex2.vNormal;
    
    					//calculateHandedness
    					handedness = (((nnn ^ ttt) | storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex2.vBitangent) < 0.0F) ? -1.0F : 1.0F;
    
    					// Gram-Schmidt orthogonalize
    					storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex2.vTangent = (ttt - nnn * (nnn | ttt)).GetSafeNormal();
    
    					//calculate Bitangent
    					storedTriangles[attachedTriangles[a].triangleIndexNumber].Vertex2.vBitangent = (nnn ^ ttt) * handedness;
    				}
    
    			}
    		}
    		
    		// Add each triangle to the vertex/index buffer
    		for (int TriIdx = 0; TriIdx<Component->ProceduralMeshTris.Num(); TriIdx++)
    		{
    			FProceduralMeshTriangle& Tri = Component->ProceduralMeshTris[TriIdx];
    
    			FDynamicMeshVertex Vert0;
    			Vert0.Position = Tri.Vertex0.Position;
    			Vert0.Color = Tri.Vertex0.Color;
    			Vert0.SetTangents(storedTriangles[TriIdx].Vertex0.vTangent,
    				storedTriangles[TriIdx].Vertex0.vBitangent,
    				storedTriangles[TriIdx].Vertex0.vNormal);
    
    			Vert0.TextureCoordinate.Set(Tri.Vertex0.U, Tri.Vertex0.V);
    			int32 VIndex = VertexBuffer.Vertices.Add(Vert0);
    			IndexBuffer.Indices.Add(VIndex);
    
    			FDynamicMeshVertex Vert1;
    			Vert1.Position = Tri.Vertex1.Position;
    			Vert1.Color = Tri.Vertex1.Color;
    			Vert1.SetTangents(storedTriangles[TriIdx].Vertex1.vTangent,
    				storedTriangles[TriIdx].Vertex1.vBitangent,
    				storedTriangles[TriIdx].Vertex1.vNormal);
    
    			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(storedTriangles[TriIdx].Vertex2.vTangent,
    				storedTriangles[TriIdx].Vertex2.vBitangent,
    				storedTriangles[TriIdx].Vertex2.vNormal);
    
    			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 = Component->GetMaterial(0);
    		if (Material == NULL)
    		{
    			Material = UMaterial::GetDefaultMaterial(MD_Surface);
    		}
    	}

    Comment


      After days and days, I (and with electricsauce's help) fixed the tangents!
      this is my code if someone wants to check

      ProceduralMeshComponent.h
      Code:
      USTRUCT(BlueprintType)
      struct FProceduralMeshVertex
      {
      	GENERATED_USTRUCT_BODY()
      
      	UPROPERTY(EditAnywhere, Category = Triangle)
      	FVector Position;
      
      	UPROPERTY(EditAnywhere, Category = Triangle)
      		FColor Color;
      
      	UPROPERTY(EditAnywhere, Category = Triangle)
      		float U;
      
      	UPROPERTY(EditAnywhere, Category = Triangle)
      		float V; 
      };
      
      USTRUCT(BlueprintType)
      struct FProceduralMeshTriangle
      {
      	GENERATED_USTRUCT_BODY()
      
      	UPROPERTY(EditAnywhere, Category = Triangle)
      	FProceduralMeshVertex Vertex0;
      
      	UPROPERTY(EditAnywhere, Category = Triangle)
      		FProceduralMeshVertex Vertex1;
      
      	UPROPERTY(EditAnywhere, Category = Triangle)
      		FProceduralMeshVertex Vertex2;
      };
      
      /** Component that allows you to specify custom triangle mesh geometry */
      UCLASS(editinlinenew, meta = (BlueprintSpawnableComponent), ClassGroup = Rendering)
      class UProceduralMeshComponent : public UMeshComponent, public IInterface_CollisionDataProvider
      {
      	GENERATED_BODY()
      
      public:
      	UProceduralMeshComponent(const FObjectInitializer& ObjectInitializer);
      
      	/** Set the geometry to use on this triangle mesh */
      	UFUNCTION(BlueprintCallable, Category = "Components|ProceduralMesh")
      		bool SetProceduralMeshTriangles(const TArray<FProceduralMeshTriangle>& Triangles);
      
      	/** Set the shadow smoothness */
      	UPROPERTY(EditAnywhere, Category = Triangle)
      		float SmoothingsGroups = 2;
      
      	/** Add to the geometry to use on this triangle mesh.  This may cause an allocation.  Use SetCustomMeshTriangles() instead when possible to reduce allocations. */
      	UFUNCTION(BlueprintCallable, Category = "Components|ProceduralMesh")
      		void AddProceduralMeshTriangles(const TArray<FProceduralMeshTriangle>& Triangles);
      
      	/** Removes all geometry from this triangle mesh.  Does not deallocate memory, allowing new geometry to reuse the existing allocation. */
      	UFUNCTION(BlueprintCallable, Category = "Components|ProceduralMesh")
      		void ClearProceduralMeshTriangles();
      
      	/** Description of collision */
      	UPROPERTY(BlueprintReadOnly, Category = "Collision")
      	class UBodySetup* ModelBodySetup;
      
      	// 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.
      
      	// Begin UMeshComponent interface.
      	virtual int32 GetNumMaterials() const override;
      	// End UMeshComponent interface.
      
      	void UpdateBodySetup();
      	void UpdateCollision();
      
      private:
      	// Begin USceneComponent interface.
      	virtual FBoxSphereBounds CalcBounds(const FTransform & LocalToWorld) const override;
      	// Begin USceneComponent interface.
      
      	/** */
      	TArray<FProceduralMeshTriangle> ProceduralMeshTris;
      
      	friend class FProceduralMeshSceneProxy;
      };
      FProceduralMeshSceneProxy
      Code:
      	FProceduralMeshSceneProxy(UProceduralMeshComponent* Component)
      		: FPrimitiveSceneProxy(Component)
      		, MaterialRelevance(Component->GetMaterialRelevance(GetScene().GetFeatureLevel()))
      	{
      		TArray<FVector> Positions;
      		TArray<FVector> tan1;
      		TArray<FVector> tan2;
      		TArray<FVector> arr_Normals;
      		TArray<FVector> arr_SmthNormals;
      		//TArray<FVector> arr_TanY;
      		//TArray<FVector> arr_TanZ;
      		// Add each triangle to the vertex/index buffer
      		for (int TriIdx = 0; TriIdx<Component->ProceduralMeshTris.Num(); TriIdx++)
      		{
      			int32 VIndex;
      			FProceduralMeshTriangle& Tri = Component->ProceduralMeshTris[TriIdx];
      
      			const FVector Edge01 = (Tri.Vertex1.Position - Tri.Vertex0.Position);
      			const FVector Edge02 = (Tri.Vertex2.Position - Tri.Vertex0.Position);
      
      			const FVector Normals = -FVector::CrossProduct(Edge01, Edge02).GetSafeNormal();
      
      			FVector v1 = Tri.Vertex0.Position;
      			FVector v2 = Tri.Vertex1.Position;
      			FVector v3 = Tri.Vertex2.Position;
      
      			FVector2D w1 = FVector2D(Tri.Vertex0.U, Tri.Vertex0.V);
      			FVector2D w2 = FVector2D(Tri.Vertex1.U, Tri.Vertex1.V);
      			FVector2D w3 = FVector2D(Tri.Vertex2.U, Tri.Vertex2.V);
      
      			float x1 = v2.X - v1.X;
      			float x2 = v3.X - v1.X;
      			float y1 = v2.Y - v1.Y;
      			float y2 = v3.Y - v1.Y;
      			float z1 = v2.Z - v1.Z;
      			float z2 = v3.Z - v1.Z;
      
      			float s1 = w2.X - w1.X;
      			float s2 = w3.X - w1.X;
      			float t1 = w2.Y - w1.Y;
      			float t2 = w3.Y - w1.Y;
      
      			float r = 1.0F / (s1 * t2 - s2 * t1);
      			FVector sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r);
      			FVector tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r);
      
      			FDynamicMeshVertex Vert0;
      			Vert0.Position = Tri.Vertex0.Position;
      			Vert0.Color = Tri.Vertex0.Color;
      			Vert0.TextureCoordinate.Set(Tri.Vertex0.U, Tri.Vertex0.V);
      
      			if (!Positions.Contains(Vert0.Position))
      			{
      				Positions.Add(Vert0.Position);
      				arr_Normals.Add(Normals);
      				arr_SmthNormals.Add(Normals);
      				tan1.Add(sdir);
      				tan2.Add(tdir);
      				VIndex = VertexBuffer.Vertices.Add(Vert0);
      				IndexBuffer.Indices.Add(VIndex);
      			}
      			else
      			{
      				VIndex = VertexBuffer.Vertices.Add(Vert0);
      				IndexBuffer.Indices.Add(VIndex);
      				arr_Normals.Add(Normals);
      				arr_SmthNormals[Positions.Find(Vert0.Position)] += Normals;
      				tan1[Positions.Find(Vert0.Position)] += sdir;
      				tan2[Positions.Find(Vert0.Position)] += tdir;
      			}
      
      			FDynamicMeshVertex Vert1;
      			Vert1.Position = Tri.Vertex1.Position;
      			Vert1.Color = Tri.Vertex1.Color;
      			Vert1.TextureCoordinate.Set(Tri.Vertex1.U, Tri.Vertex1.V);
      
      			if (!Positions.Contains(Vert1.Position))
      			{
      				Positions.Add(Vert1.Position);
      				arr_Normals.Add(Normals);
      				arr_SmthNormals.Add(Normals);
      				tan1.Add(sdir);
      				tan2.Add(tdir);
      				VIndex = VertexBuffer.Vertices.Add(Vert1);
      				IndexBuffer.Indices.Add(VIndex);
      			}
      			else
      			{
      				VIndex = VertexBuffer.Vertices.Add(Vert1);
      				IndexBuffer.Indices.Add(VIndex);
      				arr_Normals.Add(Normals);
      				arr_SmthNormals[Positions.Find(Vert1.Position)] += Normals;
      				tan1[Positions.Find(Vert1.Position)] += sdir;
      				tan2[Positions.Find(Vert1.Position)] += tdir;
      			}
      
      			FDynamicMeshVertex Vert2;
      			Vert2.Position = Tri.Vertex2.Position;
      			Vert2.Color = Tri.Vertex2.Color;
      			Vert2.TextureCoordinate.Set(Tri.Vertex2.U, Tri.Vertex2.V);
      
      			if (!Positions.Contains(Vert2.Position))
      			{
      				Positions.Add(Vert2.Position);
      				arr_Normals.Add(Normals);
      				arr_SmthNormals.Add(Normals);
      				tan1.Add(sdir);
      				tan2.Add(tdir);
      				VIndex = VertexBuffer.Vertices.Add(Vert2);
      				IndexBuffer.Indices.Add(VIndex);
      			}
      			else
      			{
      				VIndex = VertexBuffer.Vertices.Add(Vert2);
      				IndexBuffer.Indices.Add(VIndex);
      				arr_Normals.Add(Normals);
      				arr_SmthNormals[Positions.Find(Vert2.Position)] += Normals;
      				tan1[Positions.Find(Vert2.Position)] += sdir;
      				tan2[Positions.Find(Vert2.Position)] += tdir;
      			}
      		}
      
      		if (Component->SmoothingsGroups > 0)
      		{
      			for (int i = 0; i < VertexBuffer.Vertices.Num(); i++)
      			{
      				int8 handedness;
      				FVector t1, t2;
      				FDynamicMeshVertex vert = VertexBuffer.Vertices[i];
      				const FVector n = (arr_Normals[i] + (arr_SmthNormals[Positions.Find(vert.Position)] / Component->SmoothingsGroups)).GetSafeNormal();
      				t1 = tan1[Positions.Find(vert.Position)].GetSafeNormal();
      				t2 = tan2[Positions.Find(vert.Position)].GetSafeNormal();
      				handedness = (((n ^ t1) | t2) < 0.0F) ? -1.0F : 1.0F;
      
      				VertexBuffer.Vertices[i].SetTangents((t1 - n * (n | t1)).GetSafeNormal(),
      					(n ^ t1) * handedness,
      					n);
      			}
      		}
      		else
      		{
      			for (int i = 0; i < VertexBuffer.Vertices.Num(); i++)
      			{
      				int8 handedness;
      				FVector t1, t2;
      				FDynamicMeshVertex vert = VertexBuffer.Vertices[i];
      				const FVector n = (arr_Normals[i]).GetSafeNormal();
      				t1 = tan1[Positions.Find(vert.Position)].GetSafeNormal();
      				t2 = tan2[Positions.Find(vert.Position)].GetSafeNormal();
      				handedness = (((n ^ t1) | t2) < 0.0F) ? 1.0F : -1.0F;;
      
      				VertexBuffer.Vertices[i].SetTangents((t1 - n * (n | t1)).GetSafeNormal(),
      					(n ^ t1) * handedness,
      					n);
      			}
      		}
      
      		// Init vertex factory
      		VertexFactory.Init(&VertexBuffer);
      
      		// Enqueue initialization of render resource
      		BeginInitResource(&VertexBuffer);
      		BeginInitResource(&IndexBuffer);
      		BeginInitResource(&VertexFactory);
      
      		// Grab material
      		Material = Component->GetMaterial(0);
      		if (Material == NULL)
      		{
      			Material = UMaterial::GetDefaultMaterial(MD_Surface);
      		}
      	}
      I made some normal average system in my code, to get the default normal just change SmoothingsGroups to zero.

      Comment


        Hi,

        I may have missed something but when placing my DynamicMesh component is my level, if I light build it, I have an issue:

        check(!IsInRenderingThread()); in FDynamicMeshVertexFactory::Init() will stop the process.

        I'm sure I miss some settings, but I have no clue of which one.

        thanks,

        Comment


          Hi Elvince, you must not try to build static lighting with dynamic components, they are incompatible by nature. This had been stated at the beginning of this thread by an UE engineer.

          But we have made a modification to the code (see the wiki or my GitHub copy of it) so that it does not crash anymore (it still does not participate in static lighting)
          [Gamedev programmer at Darewise (Paris) - We are hiring]
          UE4 Git LFS 2.x Source Control Plugin 2.14-beta for UE4.23 - (v1 integrated by default since UE4.7)
          UE4 Plastic SCM Source Control Plugin 1.4.6 for UE4.23 - (integrated by default in Beta status since UE4.24)

          Comment


            I clearly don't want to have static ligth on it.
            It just that I don't know how it works and how elements are put into the static light process or not. I was thinking of a parameter to set to avoid the process to take it into account.

            I will check the updated code for that.

            Thanks,

            Comment


              Hi again guys,

              Has anyone tried to assign multiple UVs to the generated mesh? In the code I've found a possibility to assign only one TextureCoordinate. Can we somehow access more than one texture coordinate and therefore generate multiple UVs?

              thanks

              Comment


                Hi
                I try this tutorial from this link https://wiki.unrealengine.com/Proced...ation#Update_6
                and after fixing warnings and syntax error I try to build and run but there is nothing show at screen.
                I add actor manually and with level blueprint but there is no mesh at screen

                Comment


                  Hi, I don't know why the wiki tutorial is not working anymore, but you could look at the Github project with full source code and Blueprint to get something working:https://github.com/SRombauts/UE4Proc...ProceduralMesh
                  Cheers!
                  [Gamedev programmer at Darewise (Paris) - We are hiring]
                  UE4 Git LFS 2.x Source Control Plugin 2.14-beta for UE4.23 - (v1 integrated by default since UE4.7)
                  UE4 Plastic SCM Source Control Plugin 1.4.6 for UE4.23 - (integrated by default in Beta status since UE4.24)

                  Comment


                    Thanks. I will.

                    Comment


                      Originally posted by SRombauts View Post
                      Hi Elvince, you must not try to build static lighting with dynamic components, they are incompatible by nature. This had been stated at the beginning of this thread by an UE engineer.

                      But we have made a modification to the code (see the wiki or my GitHub copy of it) so that it does not crash anymore (it still does not participate in static lighting)
                      Hi,

                      I look into the Wiki and the code that fails on my side is there:
                      check(!IsInRenderingThread()); inside void Init(const FGeneratedMeshVertexBuffer* VertexBuffer)

                      Did I miss something?

                      Could you tell me how to "remove" the dynamic mesh from staticlight?

                      Comment


                        Thanks for the awesome work, you've really enabled a lot of people to get started with procedural mesh generation.

                        I've kind of hit a wall when it comes to tessellation though, the material DX11 tessellation won't tessellate the generated mesh at all, and "Crack Free Displacement" completely breaks it.
                        The world displacement works fine though.

                        Anyone have an idea as to what information is missing from the generated mesh that prevents it from being tessellated?

                        Comment


                          Originally posted by Grimeh View Post
                          Thanks for the awesome work, you've really enabled a lot of people to get started with procedural mesh generation.

                          I've kind of hit a wall when it comes to tessellation though, the material DX11 tessellation won't tessellate the generated mesh at all, and "Crack Free Displacement" completely breaks it.
                          The world displacement works fine though.

                          Anyone have an idea as to what information is missing from the generated mesh that prevents it from being tessellated?
                          You need to generate adjacency data and then check the material if it has tessellation and set the primitive type to the correct PT type. There is a function in MeshUtilities that can be used for generating the adjacency index buffer from positional data: BuildStaticAdjacencyIndexBuffer.
                          NVIDIA GameWorks merged branch (v4.9.2) (v4.12.5) (v4.13 p2)
                          Feel free to Donate if you wish to support me

                          Comment


                            Hi All, thanks to everyone whos worked on this. It's been a real help in my project.

                            I'm currently working on a procedural spline mesh which is generated from a spline component. I've got it working pretty well:

                            Click image for larger version

Name:	SplineMesh.JPG
Views:	1
Size:	127.3 KB
ID:	1072125

                            I'm playing around with the vertex colors, I want to be able to use them to drive the emissive output of the mesh with alpha controlling the intensity. The problem I'm facing is that currently vertex colors use FColor instead of FLinearColor. I want to be able to have values of more than 1.f for my vertex colors so I can create bloom. Does anyone know if it's possible to change the procedural mesh so that it will use FLinearColors for the vertex color values instead? Or any other way to pass per vertex data into the material?

                            Edit:
                            I've dug around in the engine code a bit and found this comment in FLocalVertexFactory::SetData()
                            Code:
                            void FLocalVertexFactory::SetData(const DataType& InData)
                            {
                            	check(IsInRenderingThread());
                            
                            	// The shader code makes assumptions that the color component is a FColor, performing swizzles on ES2 and Metal platforms as necessary
                            	// If the color is sent down as anything other than VET_Color then you'll get an undesired swizzle on those platforms
                            	check((InData.ColorComponent.Type == VET_None) || (InData.ColorComponent.Type == VET_Color));
                            
                            	Data = InData;
                            	UpdateRHI();
                            }
                            So it looks like it's not possible to use a float4 for vertex color at the moment with out modifying the engine code significantly.
                            Last edited by karltheawesome; 04-08-2015, 05:11 AM. Reason: Reduce image size
                            - Dev Blog - Twitter - Facebook - Google+

                            Comment


                              @Dzhyrma: How to use more than one TextureCoordinate:

                              Code:
                              struct FTerrainMeshVertex
                              {
                              	...
                              	FVector2D TextureCoordinate[2];
                              	...
                              }
                              VertexFactory:

                              Code:
                              DataType NewData;
                              ...
                              NewData.TextureCoordinates.Add( FVertexStreamComponent(VertexBuffer, STRUCT_OFFSET(FTerrainMeshVertex, TextureCoordinate[0]), sizeof(FTerrainMeshVertex), VET_Float2) );
                              NewData.TextureCoordinates.Add( FVertexStreamComponent(VertexBuffer, STRUCT_OFFSET(FTerrainMeshVertex, TextureCoordinate[1]), sizeof(FTerrainMeshVertex), VET_Float2) );
                              ...

                              But there is one thing you need to know: In the material you have to select "Coordinate Index" 2 for TextureCoordinate[1], 4 for TextureCoordinate[2] etc....
                              Last edited by TheSoeldner; 04-08-2015, 05:20 AM.

                              Comment


                                Originally posted by karltheawesome View Post
                                Hi All, thanks to everyone whos worked on this. It's been a real help in my project.

                                I'm currently working on a procedural spline mesh which is generated from a spline component. I've got it working pretty well:

                                [ATTACH=CONFIG]34071[/ATTACH]

                                I'm playing around with the vertex colors, I want to be able to use them to drive the emissive output of the mesh with alpha controlling the intensity. The problem I'm facing is that currently vertex colors use FColor instead of FLinearColor. I want to be able to have values of more than 1.f for my vertex colors so I can create bloom. Does anyone know if it's possible to change the procedural mesh so that it will use FLinearColors for the vertex color values instead? Or any other way to pass per vertex data into the material?

                                Edit:
                                I've dug around in the engine code a bit and found this comment in FLocalVertexFactory::SetData()
                                Code:
                                void FLocalVertexFactory::SetData(const DataType& InData)
                                {
                                	check(IsInRenderingThread());
                                
                                	// The shader code makes assumptions that the color component is a FColor, performing swizzles on ES2 and Metal platforms as necessary
                                	// If the color is sent down as anything other than VET_Color then you'll get an undesired swizzle on those platforms
                                	check((InData.ColorComponent.Type == VET_None) || (InData.ColorComponent.Type == VET_Color));
                                
                                	Data = InData;
                                	UpdateRHI();
                                }
                                So it looks like it's not possible to use a float4 for vertex color at the moment with out modifying the engine code significantly.
                                Hi
                                Is it possible to share some code or info how to make custom spline component or extend existing one ?

                                Comment

                                Working...
                                X