After days and days, I (and with 's help) fixed the tangents!
this is my code if someone wants to check
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.