Creating the TangentX, TangentY, TangentZ from Vertex and normal.

I am doing some procedural terrain. The output of the algorithm (Dual Contouring) is a vertex list(position, normal), and a list of indices.

The output is also in Quads not triangles. I know I will have to triangulate these quads, but how do I create the tangent vectors? This terrain is smooth and the quads share verts (so I cannot create the tangents like the wiki does) it will just end up with a normal that favors the last triangle that uses that vert).

Do I create the tangents for every triangle that uses that vert then average them? It just feels like if I already have the normal I shouldn’t have to go through all that (I could be wrong, it would not be the first time).

Not sure if still relevant, but look at how UStaticMesh is built from FRawMesh. There’s an option to generate tangents.

When I left out tangent generation, very funny things happened to my meshes, especially when assigning textures (even though normals were defined). SiggiG has some example code in the forum for calculating the tangents of procedural meshes: Generate Procedural Mesh - C++ - Epic Developer Community Forums

TArray<FProcMeshTangent> Tangents;
FVector surfaceTangent = tri.p1 - tri.p2; //p1 to p3 being FVectors
surfaceTangent = surfaceTangent.GetSafeNormal();
Tangents[VertIdx1] = FProcMeshTangent(surfaceTangent, true);
Tangents[VertIdx2] = FProcMeshTangent(surfaceTangent, true);
Tangents[VertIdx3] = FProcMeshTangent(surfaceTangent, true);

If using the new UProceduralMeshComponent from >= 4.8p3, you then only have to supply these tangents to the UProceduralMeshComponent::CreateMeshSection(…) function.

Otherwise you may check the implementation of this and how the tangents are directly assigned there:
Plugins\Runtime\Source\ProceduralMeshComponent

I just spent a day forum crawling trying to figure out why the tangents I was generating were causing weirdness with bump maps on materials. What I finally discovered is that the tangents need to be pointing in the same direction as the x direction on your UV coordinates.

Hopefully, the following code snippet is enough to help out anyone who is running into the same problem later on.

	vertices->Add(a);
	vertices->Add(b);
	vertices->Add(c);

	triangles->Add(vertexIndex);
	triangles->Add(vertexIndex + 1);
	triangles->Add(vertexIndex + 2);

	normal = FVector::CrossProduct(c - a, b - a).GetSafeNormal();

	normals->Add(normal);
	normals->Add(normal);
	normals->Add(normal);

	uV0->Add(aUV);
	uV0->Add(bUV);
	uV0->Add(cUV);
   
	surfaceTangent = ((((bUV.X - aUV.X) / FVector2D::Distance(bUV, aUV))*(b - a)) +
		(((bUV.Y - aUV.Y) / FVector2D::Distance(bUV, aUV))*FVector::CrossProduct(normal, (b - a)))).GetSafeNormal();

	tangents->Add(FProcMeshTangent(surfaceTangent, true));
	tangents->Add(FProcMeshTangent(surfaceTangent, true));
	tangents->Add(FProcMeshTangent(surfaceTangent, true));