OBJ Loader shows OBJ with Tearing

I have an OBJ file that loads into my threeJS app it looks like this in “cloud compare”.


But when I load it into my application it looks like this.

My code is a simple OBJLoader. However, I have also loaded it into
https://adjam93.github.io/threejs-model-viewer/# and it appears like this image

Some other OBJs have loaded fine, although they were small.

This is the code to my fileOBJLoader.js

import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader.js";
import { DoubleSide, Color, Vector3, Box3 } from "three";
import { params } from "../../drawing/createScene.js";

export function handleOBJNoEvent(file, canvas) {
    if (!file) {
        return;
    }

    const reader = new FileReader();

    reader.onload = function(event) {
        const contents = event.target.result;
        const objLoader = new OBJLoader();

        const object = objLoader.parse(contents);

        // Compute the bounding box of the object
        const boundingBox = new Box3().setFromObject(object);
        const center = boundingBox.getCenter(new Vector3());

        // Determine the offsets based on world center parameters
        const offsetX = params.worldXCenter !== 0 ? params.worldXCenter : center.x;
        const offsetY = params.worldYCenter !== 0 ? params.worldYCenter : center.y;
        const offsetZ = params.worldZCenter !== 0 ? params.worldZCenter : center.z;

        // Reposition vertices to center the object at the world center
        object.traverse(function(child) {
            if (child.isMesh) {
                const position = child.geometry.attributes.position;
                for (let i = 0; i < position.count; i++) {
                    position.setXYZ(
                        i,
                        position.getX(i) - offsetX,
                        position.getY(i) - offsetY,
                        position.getZ(i) - offsetZ
                    );
                }
                position.needsUpdate = true;
                child.geometry.computeBoundingBox();
                child.geometry.computeBoundingSphere();
            }
        });

        // Set material to a Grey and DoubleSide for visibility
        object.traverse(function(child) {
            if (child.isMesh) {
                child.material.side = DoubleSide;
                child.material.color = new Color(0xAAAAAA); 
                child.material.needsUpdate = true;
                // Print the X, Y, and Z of each Vertex
                console.log(child.geometry.attributes.position);
            }
        });

        object.position.set(0, 0, 0);
        object.scale.set(1, 1, 1);
        canvas.scene.add(object);
        object.name = file.name;
        console.log("Object position after load:", object.position);
        console.log("Object rotation after load:", object.rotation);
        console.log("Object scale after load:", object.scale);

        // Adjust the camera to ensure the object is visible
        const size = boundingBox.getSize(new Vector3());
        if (params.debugComments) {
            console.log("Loaded OBJ position:", object.position);
            console.log("Loaded OBJ rotation:", object.rotation);
            console.log("Loaded OBJ scale:", object.scale);
        }
    };
    reader.onerror = function(error) {
        console.log("Error reading the OBJ file:", error);
    };
    reader.readAsText(file);
}

Thanks in advance… my application is in the early days but you can view it here…
Kirra3D

If you have an OBJ you’d like to try in the app then before loading it up be sure to set the world Centre Coordinates.

Why not just:

new OBJLoader().load(url,(object)=>{
  object.position.add(-offX,-offY,-offZ);
  object.traverse(e=>e.isMesh&&(e.material.side=THREE.DoubleSide));
  scene.add(object);
})
``` ?

Thanks for your response…

I ran the app in Edge on Windows and the result


Chrome and Edge on Windows Parallels Win10

But if I run the app on Mac Edge


The code appears fine in these scenarios on win10 OS’s

Maybe Mac is just not dealing with it… :confused:

@BrentBuffham maybe try loading your model in my OBJ Viewer just to see how it will show there. This viewer is web based so you should be able to use it in browsers on Windows and Mac.

I tried your viewer in Firefox browser on Windows and it showed several OBJ files properly, including their vertex colors. That other viewer you mentioned does not appear to have a support for vertex colors.

@manthrax might be correct in suggesting that you should only set the material side and not the color. If you are using the same process for both CSV and OBJ file loading then you might need to stick with your current code for loading just eliminate setting the material color.

1 Like

Thanks… I’ve used your app in the past and its great.
I exported the OBJ with a global shift to near zero. Then I can load it in your application.

Unfortunately, I got the same issue.

You can get the OBJ here there is also a “.stl” and “.ply” (ascii) if you’d like to check.
https://blastingapps.xyz/downloads/Stg5OBJ_nearzero.obj

I’ve since done some reading and with WebGL on Mac there was a suggestion that it is a WebGL depth issue, a suggestion was to use 24-bit or 32-bit depth buffer. Apparently you set this when you make the renderer.

//example suggestion - not attempted yet. - might overcome "Z fighting"
const renderer = new THREE.WebGLRenderer({
    antialias: true,
    depth: true,
    precision: 'highp',
    stencil: false
});

Any thoughts appreciated and thanks for the interaction and interest.

Out of interest, what does the uv map look like for the model? It looks like the same thing that happens when udim texture sets are loaded incorrectly onto a photo scanned model with a multi material setup…

EDIT: looking closer at the following screen shot it looks like the problem is deeper than this…

There is no UV map for this OBJ.
3 View in 2 apps.

It is the same OBJ in a Mac Edge (Left), adjam93’s model viewer in Mac Edge (centre) and Win10 Edge (Right), with the same settings and loading in the two Edge apps. The centre model viewer is displaying the OBJ fine.

The OBJ just isn’t being built in the Mac WebGL the same way that Win WebGL is working. However, you can see that it is possible to get it displaying normally as it is working in the Three.js Model Viewer

You can visibly see that the faces are connected out of order in the Left app.
I guess I’ll troll through Adjam93’s viewer code on Git Hub and see what’s different.

Thanks again for the suggestions. BTW the WebGLRender settings code didn’t resolve the issue.

EDIT

// Didn't resolve the Z-Fighting.
	renderer = new WebGLRenderer({
		antialias: true,
		depth: true,
		precision: "highp",
		powerPreference: "high-performance",
		stencil: false
	});

What the!!! It is a material issue with the normals. If I set the material to wireframe it correctly displays the wireframe.

// Reposition vertices to center the object at the world center
		object.traverse(function (child) {
			if (child.isMesh) {
				const position = child.geometry.attributes.position;
				for (let i = 0; i < position.count; i++) {
					position.setXYZ(i, position.getX(i) - offsetX, position.getY(i) - offsetY, position.getZ(i) - offsetZ);
				}
				position.needsUpdate = true;
				child.material.wireframe = true; // Set wireframe mode
				//child.material.color = new Color(0xaaaaaa);
				child.material.needsUpdate = true;
				child.geometry.computeBoundingBox();
				child.geometry.computeBoundingSphere();
			}
		});

Even more Confusing. :confused:

I put it into @mrdoob 's Editor ThreeJS editor and the Normals and the Solid and Normals are not rendering correctly.

Is it possible that the mesh’s geometry is indexed and the indices are more than 65535? WebGL vertex indices are bound to 16-bits.

1 Like

I think 32 bit indices are supported as well, no?.
But yeah… this looks like an odd problem. OPs download link isn’t working for me or I would check…
Sounds like its browser specific. My guess is this is some apple GPU driver problem.

Up to OpenGL ES 2.0 glDrawElements worked with GL_UNSIGNED_BYTE and GL_UNSIGNED_SHORT indices only. GL_UNSIGNED_INT is supported since OpenGL ES 3.0.

@BrentBuffham: could you try with a much smaller OBJ, just to check whether the amount of vertices hits some hardwall constraint.

1 Like

Sorry about the link it’s fixed now so you can get the OBJ.

OBJ
https://blastingapps.xyz/downloads/Stg5OBJ_nearzero.obj

STL
https://blastingapps.xyz/downloads/Stg5OBJ_nearzero.stl

So the OBJ links are corrected. Apologies.
The file is only 36000 lines

I was able to get it to work if I first loaded the OBJ as a wireframe.
Then load as a Solid directly after.

Adjams viewer also has similar logic

//From https://github.com/Adjam93/threejs-model-viewer/blob/0230380ceb21815d23a706c669376a4ad05cf059/js/userModel.js
            model.traverse(function (child) {
                if (child instanceof THREE.Mesh) {

                    numOfMeshes++;
                    var geometry = child.geometry;
                    stats(filename, geometry, numOfMeshes);

                    child.material = materials.default_material;

                    var wireframe2 = new THREE.WireframeGeometry(child.geometry);
                    var edges = new THREE.LineSegments(wireframe2, materials.wireframeAndModel);
                    materials.wireframeAndModel.visible = false;
                    model.add(edges);

                    setWireFrame(child);
                    setWireframeAndModel(child);

                    setPhong(child);
                    setXray(child);

                }
            });

My app code for testing is below.

const default_material = new MeshLambertMaterial({ color: 0x22ffaa, side: DoubleSide, blending: AdditiveBlending, depthWrite: false });
const solid_material = new MeshPhongMaterial({ color: 0xcccccc, side: DoubleSide, flatShading: true });

// Reposition vertices to center the object at the world center
		object.traverse(function (child) {
			if (child.isMesh) {
				const position = child.geometry.attributes.position;
				for (let i = 0; i < position.count; i++) {
					position.setXYZ(i, position.getX(i) - offsetX, position.getY(i) - offsetY, position.getZ(i) - offsetZ);
				}
				position.needsUpdate = true;
				if (params.wireframeOn) {
					child.material = default_material; // Find out which material works...
					child.material.wireframe = true; // Set wireframe mode
					child.material.needsUpdate = true;
					child.geometry.computeBoundingBox();
					child.geometry.computeBoundingSphere();
				} else {
					child.material = solid_material;
					child.material.needsUpdate = true;
					child.geometry.computeBoundingBox();
					child.geometry.computeBoundingSphere();
				}
			}
		});

20240708_Kirra3D

This is strange. It could be nice if you find out why.

1 Like

@BrentBuffham since I don’t have a Mac to do any testing on then I will have to bow out (otherwise it would be a lot of wild guessing on my part).

In your last tests you have used 2 different materials and with different settings. Maybe try to keep the same settings on both (whatever is applicable to both materials). This has caused the vertex colors to disappear.

Also, if you end up using a specific material then try adding a conditional setting of the material’s vertexColors similar to this:

    child.material.vertexColors = child.geometry.hasAttribute( 'color' ) === true;
1 Like

@BrentBuffham here is another suggestion for your app.

Currently, if I attempt loading the same model 3 times in a row and with the wireframe option alternating from ON to OFF to ON, then you can see what the attached picture shows in Firefox on Windows (the 3rd attempt combines and shows both materials).

It appears that your app is not disposing of the previous model. Maybe take a look at the code of my OBJ Viewer to see how I dispose of the previous model in the init function.

1 Like

Correct… currently it is not disposing of the model. Each time it loads it as another instance.

It’s something to work on it’s really just a beta app… I was just trying to get the obj to show without material issues. The worst bit is iOS is working with a single model load… it’s got to be this AMD GPU in the 2020 Mac I have. I don’t get it…

Scenario 1
- load only as a wireframe obj shows as expected.

Scenario 2
- load only as a solid obj shows material and triangle connection issues.

Scenario 3
- load wireframe first then load solid material both instances of the obj display correctly.

I haven’t had a chance to implement your suggestion on the colouring the normals.

child.material.vertexColors = child.geometry.hasAttribute( ‘color’ ) === true;

I have also broken the perspective camera and the rotation trying to “quick fix” the obj issues.

Now I get it, like a multi model viewer (see the screenshot below - even though it shows the same model I did try different models as well).

I also tried it on 2 different Android phones and it works there as well. Samsung phone has an odd issue, which is that the number keypad that pops up for changing World coordinates only allows positive numbers (Huawei phone keypad on the other hand had a negative sign button). Maybe try your app on whatever phone you might have available.

You might eventually figure out all the issues and make this work.

1 Like

Thanks for the help but to no avail.
However, each time I take a step forward I feel like I take 1.1 steps back. :frowning:

Although I never really use Safari. But I tried the App in Safari and the OBJ displayed correctly.
Only Chrome, Edge and Brave do not display the OBJ on MacOS. Safari displays the OBJ as intended.
GIF BELOW:
20240712_Kirra3D

The downside is that I have to click on the load buttons is Safari 1-3 times to get it to load, whereas Edge and alike load after the first click.

Thanks everyone for your help. But there appears to be no solution.
I have put in Feedback to MS Edge and Chromium… hopefully, they fix it on MacOS.

It is an existing issue
WEBGL CHROMIUM ISSUE

Of Note this is the same issue:
[paid] help resolve bug either in threejs or chromium

On MacOS don’t use Chromium Browsers use WebKit Browsers (Firefox, Safari, DuckDuckGO)