Download

Help with a C++ function I have been trying to update/fix

Not going to lie, I’m a complete novice when it comes to C++ programming. I have been trying to get the vertices of a static mesh triangle in order to calculate an interpolated normal to make movement over low-poly meshes smooth at any angle. I was directed to a function on a blog by someone in order to achieve this, and the actual math to determine the barycentric coordinates at the bottom works perfectly (I tested it in Blueprint) but there are missing functions in the first part that actually grabs the vertex information from the static mesh/spline mesh in the beginning part. I imagine it is outdated references to functions that names/locations have been changed. I’ve been trying to fix it on my own and had absolutely no success. If anyone could help that would be amazing! Also any hints for which header files I need to include with the updated code would be nice as well.

Here’s a link to the blog post: http://wacki.me/blog/2017/04/ue4-smooth-trace-normal/


  
 bool GetSmoothNormalFromTrace(const FHitResult& HitResult, FVector& Normal, bool DebugDraw)
{
    // We don't care if this isn't a blocking hit
    if (!HitResult.bBlockingHit)
        return false;

    // See if a static mesh and/or a spline mesh component were hit
    UStaticMeshComponent* StaticMeshComp = Cast<UStaticMeshComponent>(HitResult.GetComponent());
    USplineMeshComponent* SplineMeshComp = Cast<USplineMeshComponent>(HitResult.GetComponent());

    // If we hit a spline mesh component we should also have hit a static mesh component
    if (!StaticMeshComp) return false;

    // Retrieve static mesh. If we hit a spline mesh component we also hit a static mesh component at the same time.
    // So we can just use it to get to the actual static mesh.
    UStaticMesh* StaticMesh = StaticMeshComp->GetStaticMesh();

    // Return if the static mesh isn't set (shouldn't happen)
    if (!StaticMesh)
        return false;

    // In cooked builds we need to have this flag set or we'll crash when trying to access mesh data
    if (!StaticMesh->bAllowCPUAccess)
        return false;

    // Return if RenderData is invalid
    if (!StaticMesh->RenderData)
        return false;

    // No valid mesh data on lod 0 (shouldn't happen)
    if (!StaticMesh->RenderData->LODResources.IsValidIndex(0))
        return false;

    int FaceIndex = HitResult.FaceIndex;
    FTransform ComponentTransform = StaticMeshComp->GetComponentTransform();
    FStaticMeshVertexBuffer* VertexBuffer = &StaticMesh->RenderData->LODResources[0].VertexBuffer;
    FPositionVertexBuffer* PositionVertexBuffer = &StaticMesh->RenderData->LODResources[0].PositionVertexBuffer;
    FIndexArrayView IndexBuffer = StaticMesh->RenderData->LODResources[0].IndexBuffer.GetArrayView();

    // Storage for the actual triangle verteces
    FVector VertexPositions[3];
    FVector VertexNormals[3];

    for (int i = 0; i < 3; i++) {
        // Get vertex index
        uint32 index = IndexBuffer[FaceIndex * 3 + i];
        // Get vertex position and normal
        VertexPositions* = PositionVertexBuffer->VertexPosition(index);
        VertexNormals* = VertexBuffer->VertexTangentZ(index);

        // Transform position and normal into spline mesh space
        if (SplineMeshComp) {
            // Get transform along spline component
            const FTransform SliceTransform = SplineMeshComp->CalcSliceTransform(USplineMeshComponent::GetAxisValue(VertexPositions*, SplineMeshComp->ForwardAxis));
            // Remove spline forward axis from vertex position, it will be added back by transforming the position into spline mesh space
            USplineMeshComponent::GetAxisValue(VertexPositions*, SplineMeshComp->ForwardAxis) = 0;
            // Transform position and normal into spline mesh space
            VertexPositions* = SliceTransform.TransformPosition(VertexPositions*);
            VertexNormals* = SliceTransform.TransformVector(VertexNormals*);
        }

        // Transform position and normal into world space
        VertexPositions* = ComponentTransform.TransformPosition(VertexPositions*);
        VertexNormals* = ComponentTransform.TransformVector(VertexNormals*);
    }

    // Determine the barycentric coordinates
    FVector U = VertexPositions[1] - VertexPositions[0];
    FVector V = VertexPositions[2] - VertexPositions[0];
    FVector W = HitResult.ImpactPoint - VertexPositions[0];

    FVector vCrossW = FVector::CrossProduct(V, W);
    FVector vCrossU = FVector::CrossProduct(V, U);

    if (FVector::DotProduct(vCrossW, vCrossU) < 0.0f) {
        return false;
    }

    FVector uCrossW = FVector::CrossProduct(U, W);
    FVector uCrossV = FVector::CrossProduct(U, V);

    if (FVector::DotProduct(uCrossW, uCrossV) < 0.0f) {
        return false;
    }

    float Denom = uCrossV.Size();
    float b1 = vCrossW.Size() / Denom;
    float b2 = uCrossW.Size() / Denom;
    float b0 = 1.0f - b1 - b2;

    // Determine the hit normal
    Normal.X = b0 * VertexNormals[0].X + b1 * VertexNormals[1].X + b2 * VertexNormals[2].X;
    Normal.Y = b0 * VertexNormals[0].Y + b1 * VertexNormals[1].Y + b2 * VertexNormals[2].Y;
    Normal.Z = b0 * VertexNormals[0].Z + b1 * VertexNormals[1].Z + b2 * VertexNormals[2].Z;

    // Just to be safe here
    Normal.Normalize();

    // Debug draw
#if !UE_BUILD_SHIPPING
    if (DebugDraw) {
        // Quick and dirty debug draw
        FColor DebugColor = FColor::Red;
        float DebugDrawDuration = 30.0f;

        for (int i = 0; i < 3; i++) {
            // draw triangle points
            ::DrawDebugPoint(StaticMeshComp->GetWorld(), VertexPositions*, 16, DebugColor, false, DebugDrawDuration);
            // draw triangle edges
            ::DrawDebugLine(StaticMeshComp->GetWorld(), VertexPositions*, VertexPositions(i + 1) % 3], DebugColor, false, DebugDrawDuration);
            // triangle normals
            ::DrawDebugLine(StaticMeshComp->GetWorld(), VertexPositions*, VertexPositions* + VertexNormals* * 200.0f, DebugColor, false, DebugDrawDuration);
        }

        // draw actual impact normal
        ::DrawDebugPoint(StaticMeshComp->GetWorld(), HitResult.ImpactPoint, 16, DebugColor, false, DebugDrawDuration);
        ::DrawDebugLine(StaticMeshComp->GetWorld(), HitResult.ImpactPoint, HitResult.ImpactPoint + HitResult.ImpactNormal * 200.0f, DebugColor, false, DebugDrawDuration);
        ::DrawDebugLine(StaticMeshComp->GetWorld(), HitResult.ImpactPoint, HitResult.ImpactPoint + Normal * 200.0f, DebugColor, false, DebugDrawDuration);
    }
#endif //!UE_BUILD_SHIPPING

    return true;
}