Generate Procedural Mesh

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

ProceduralMeshComponent.h



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



	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*;
				const FVector n = (arr_Normals* + (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*.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*;
				const FVector n = (arr_Normals*).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*.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.