atteneder / glTFast

Efficient glTF 3D import / export package for Unity
Other
1.25k stars 251 forks source link

ACCESSOR_MIN_MISMATCH #652

Open IrfanSamuel-aai opened 1 year ago

IrfanSamuel-aai commented 1 year ago

Describe the bug I'm exporting a mesh from Unity3d as GLB to bring it into UE but due to ACCESSOR_MIN_MISMATCH, UE is not able to load the mesh and it crashes. image

The house is built via an editor tool so house parts can be flipped (X,Z) and different modules are combined to form one house.

Now I can see the value -0.553424656391144 for SubMesh(1) via manual vertices check but when it's been fetched to meshBound.SetMinMax( ), this is changed to -0.553424835205078 because the function accepts only Vector3(float xyz)

Files zip file has fbx and glb files. SingleHouse.zip

To Reproduce Steps to reproduce the behavior:

  1. Exported the glb file via editor
  2. Load the model on https://gltf-viewer.donmccurdy.com/
  3. See errors

Expected behavior Is there any way to have the correct min/max value with double precision stored for each model submesh?

Screenshot loads fine in Windows 3d Viewer image

Desktop

Additional context How can this issue be resolved to correctly export glb with no mismatch issue. System.Convert.ToSingle( ) also doesn't work.

atteneder commented 1 year ago

Hi @IrfanSamuel-aai,

Thanks for reporting.

The cause of the problem is that the glTF export does not re-calculate the min/max values based on the actual vertex positions (as this requires iterating all vertices), but calculates them from the Mesh's bounds. Those are stored as center/extends vectors and this floating point operation introduces a tiny bit of error, enough to trigger an error by the glTF validator (see issue) and apparently UE's import.

So solutions to solve this:

  1. We should calculate correct min/max in export jobs
  2. UE devs should make their importer less defensive, throw a warning instead of an error, calculate correct values and don't block the import.
IrfanSamuel-aai commented 1 year ago

Hi, Thank you for the reply. Will it be possible to calculate min/max of each mesh in export jobs? We can make this an optional operation to be performed while exporting. We can't be sure, when UE devs will work on their importer. Best regards.

atteneder commented 1 year ago

Hi, Thank you for the reply. Will it be possible to calculate min/max of each mesh in export jobs?

Yes, that's the best approach I think.

RAsoftware916 commented 11 months ago

Hi, do you have idea how to calculate min/max of each mesh in export jobs?

IrfanSamuel-aai commented 11 months ago

Hi, do you have idea how to calculate min/max of each mesh in export jobs?

Hi, I'm able to correctly calculate the the bounds (double) but only limited by meshBound.SetMinMax( ) as it only accepts Vector3. @atteneder can provide us an option to pass the min/max for each mesh along with the Export function then the issue can be easily rectified.

RAsoftware916 commented 11 months ago

@IrfanSamuel-aai Can you give example of your code with correct bound calculation?

IrfanSamuel-aai commented 11 months ago

@RAsoftware916, yes sure :)

public static void GetMeshBounds(Mesh mesh, out double3 min, out double3 max) { int count = mesh.vertexCount; List vertices = new (); mesh.GetVertices(vertices);

    min = new double3(); 
    max = new double3();

    for (int i = 0; i < 3; i++)
    {
        min[i] = float.MaxValue;
        max[i] = float.MinValue;
    }

    for (int i = 0; i < count; i++)
    {
        var v = vertices[i];
        if ((double)v.x < min[0]) min[0] = (double)v.x;
        if ((double)v.y < min[1]) min[1] = (double)v.y;
        if ((double)v.z < min[2]) min[2] = (double)v.z;
        if ((double)v.x > max[0]) max[0] = (double)v.x;
        if ((double)v.y > max[1]) max[1] = (double)v.y;
        if ((double)v.z > max[2]) max[2] = (double)v.z;
    }
}
RAsoftware916 commented 11 months ago

@IrfanSamuel-aai Thanks for your code. I applied your code to GltfWriter.cs and changed the code in lines from 963 first part of switch. I tested this change on my models which makes errors of min/max mismatch in the validator and after this fix errors didn't appear. `switch (attribute.attribute) { case VertexAttribute.Position: Assert.AreEqual(VertexAttributeFormat.Float32,attribute.format); Assert.AreEqual(3,attribute.dimension);

    int count = uMesh.vertexCount;
    List<Vector3> vertices = new List<Vector3>();
    uMesh.GetVertices(vertices);
    double3 minD = new double3(); 
    double3 maxD = new double3();

    for (int i = 0; i < 3; i++)
    {
        minD[i] = float.MaxValue;
        maxD[i] = float.MinValue;
    }

    for (int i = 0; i < count; i++)
    {
        var v = vertices[i];
        if (v.x < minD[0]) minD[0] = v.x;
        if (v.y < minD[1]) minD[1] = v.y;
        if (v.z < minD[2]) minD[2] = v.z;
        if (v.x > maxD[0]) maxD[0] = v.x;
        if (v.y > maxD[1]) maxD[1] = v.y;
        if (v.z > maxD[2]) maxD[2] = v.z;
    }

    accessor.min = new[] {(float)-maxD.x, (float)minD.y, (float)minD.z };
    accessor.max = new[] { (float)-minD.x, (float)maxD.y, (float)maxD.z };
    attributes.POSITION = accessorId;
    break;`
IrfanSamuel-aai commented 11 months ago

@RAsoftware916 great, thanks for sharing your code. @atteneder, can this be merged?

atteneder commented 4 months ago

@RAsoftware916 Thanks for sharing your solution.

@IrfanSamuel-aai Unfortunately I won't merge this particular solution due to performance concerns. It essentially creates another copy of the vertex positions. Granted, this is not everyone's concern, but I certainly care about that stuff.