Hello,
I use pythonocc to convert step files to gltf files to render with threejs. I’ve been mostly successful so far but I’m seeing that some feature lines, which render fine off the step file in the CAD software, are not getting rendered by threejs.
As you can see in the image below, the arrows point to where there should’ve been feature lines between the surfaces.
The difference in the color shade in the pocket (where the edge failed to be rendered) is due to my application of lighting. When I apply only ambient lighting, both surfaces have the same shade as seen below:
And here is the same part rendered in a CAD program where feature edges are drawn perfectly:
Here’s how I render that yields my result (the blue):
new GLTFLoader().load(
'/resources/models/box.glb',
function (gltf) {
const model = gltf.scene;
scene.add(model);
model.traverse((node) => {
if (node.isMesh) {
node.castShadow = true;
node.receiveShadow = true;
node.material = new THREE.MeshPhongMaterial({
color: 0x577794,
shininess: 30,
specular: 0x111111
});
const edges = new THREE.EdgesGeometry(node.geometry, 15);
const lineMaterial = new THREE.LineBasicMaterial({color: 0x000000});
const wireframe = new THREE.LineSegments(edges, lineMaterial);
node.add(wireframe);
}
});
},
undefined,
function (err) {
console.error(err);
}
);
I haven’t been able to find what’s causing this despite my many attempts. What am I missing? Thanks
I don’t perceive Three.js to be in error. You expect Three.js to draw a line, where a distinct change of color/shade makes it perfectly clear that there’s a switch in orientation of faces. That’s fine with me.
You share nothing about what are your “many attempts”, so it is hard to guess what you are missing.
First try to find whether lines are draw but are somehow invisible; or they are not drawn at all. To do this, render only the lines.
-
If the lines are still not visible → look at how they are constructed, maybe you use something that skips particular lines
-
If the lines are visible → then most likely some of the faces are draw over the lines, in this case use polygonOffset
, polygonOffsetFactor
, and polygonOffsetUnits
to shift the faces and make the lines stand up; or try playing with renderOrder
Thanks. The shade difference is due to the HemisphereLight that I’m using in addition to AmbientLight. When I disable the HemisphereLight, there is no shade difference and the two surfaces look as if they are the same surface. I have added a screenshot to my first message with HemisphereLight disabled.
First I have tried solving it within threejs by using Line2 and LineSegments2 along with a large zindex in case the problem could be surface being drawn over the line. I’ve also tried different values for the thresholdAngle
in const edges = new THREE.EdgesGeometry(node.geometry, 15);
These didn’t make a difference.
Then I asked the AI and it suggested the issue was because when I convert from step to gltf, the actual feature lines were not being written to gltf and threejs was figuring edges out on its own, causing some lines to not be rendered. I use pythonocc
in the below script to convert step files to gltf files:
#!/usr/bin/env python3
# usage: python step_to_gltf.py <in.step> <out.glb|gltf> <lin_def> <ang_def>
import sys
from pathlib import Path
from OCC.Extend.DataExchange import read_step_file
from OCC.Core.BRepTools import breptools # modern, non-deprecated
from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh
from OCC.Core.TDocStd import TDocStd_Document
from OCC.Core.XCAFDoc import XCAFDoc_DocumentTool
from OCC.Core.TCollection import TCollection_AsciiString
from OCC.Core.TColStd import TColStd_IndexedDataMapOfStringString
from OCC.Core.RWGltf import RWGltf_CafWriter
from OCC.Core.IFSelect import IFSelect_RetDone
from OCC.Core.Message import Message_ProgressRange
if len(sys.argv) != 5:
sys.exit("usage: step_to_gltf.py <in.step> <out.glb|gltf> <lin_def> <ang_def>")
in_file, out_file = sys.argv[1], sys.argv[2]
lin_def, ang_def = float(sys.argv[3]), float(sys.argv[4])
# 1 load STEP
shape = read_step_file(in_file)
# 2 strip any existing mesh, then re-mesh with absolute tolerances
breptools.Clean(shape)
BRepMesh_IncrementalMesh(shape, lin_def, False, ang_def, True).Perform()
# 3 put the shape in an XDE document
doc = TDocStd_Document("doc")
XCAFDoc_DocumentTool.ShapeTool(doc.Main()).AddShape(shape)
# 4 (optional) metadata
info = TColStd_IndexedDataMapOfStringString()
info.Add(TCollection_AsciiString("Generator"), TCollection_AsciiString("step_to_gltf.py"))
# 5 write glTF/GLB
writer = RWGltf_CafWriter(out_file, Path(out_file).suffix.lower() == ".glb")
result = writer.Perform(doc, info, Message_ProgressRange())
if result != IFSelect_RetDone:
sys.exit("glTF export failed")
print("Export OK")
This does a good job converting step to gltf, and I’ve tried lower values for lin_def
and ang_def
to increase detail, but sadly that also didn’t work.
I’ve also tried pythonocc’s STEPControl_Reader
to read the step file and RWGltf_CafWriter
to write to gltf while again meshing with BRepMesh_IncrementalMesh
in-between, but this also didn’t work. While I’m not sure my attempts with pythonocc actually tried to capture the geometry edges, feel like I’m running out of things to try. Thanks again.
1 Like
What I have proposed will help you see whether lines exist but are not visible; or whether they do not exist. At least this would help you rule out possible issues.
You were right! It seems the lines are there but the surfaces are still somehow over them. You can see below how it looks when I make the surfaces transparent:
This is a bit unexpected as I did try giving the lines a very large zindex when I rendered them with Line2 and LineSegments2 (although currently I’m not using them).
Leaving it here as a quick reply for now while I try the options you suggested. Thanks a lot.
Update: polygonOffset did the trick. I added:
polygonOffset: true,
polygonOffsetFactor: 1,
polygonOffsetUnits: 1
to the MeshPhongMaterial I’m using for surfaces, didn’t need to touch anything else and it worked:
I have also tried renderOrder which does make the edges visible by rendering them after the surfaces, however it causes the model to appear transparent since all edges (even those that are actually behind surfaces and should be invisible) are rendered over all surfaces. So polygonOffset was the better solution for this case. Thanks a lot again for your help.
1 Like