CV Mesh From 3D Model
Get vertices and triangles out of a GLB so OpenCV can use them
- model_3d
- pts3d
- tris
- center
- diameter
- vertex_count
- triangle_count
- loaded
ComfyUI's 3D nodes hand you a FILE_3D - a loaded model object. OpenCV's 3-D side wants two plain arrays: Nx3 vertices and Mx3 triangle indices. This node is the duct tape between those two worlds, and it's the entry point for every mesh-driven node in the pack: cv2.rapid, CV ICP Register, CV PPF Pose Estimation, CV Point Cloud Normals, CV Rasterize Mesh, CV Transform Points 3D.
Same parser as the pack's own 3-D previewer, deliberately - so what you see in the viewer and what you measure can't drift apart. That's a small design decision with a real payoff: if the render and the tracker disagree, you're debugging your own pipeline rather than a parsing mismatch.
What comes out, and what doesn't
Every triangle primitive in the default scene is merged into one indexed mesh, with each node's transform baked in. Materials, UVs and vertex normals are dropped - positions and connectivity only, which is what the solvers consume. Triangle winding is preserved from the file, and that matters: cv2.rapid's backface culling depends on it, so a model exported with flipped faces silhouettes inside out.
Two required inputs and two optional ones:
model_3d- aFILE_3D_GLB/FILE_3D_GLTFfrom coreLoad 3D(or Load 3D Advanced).axis_convention-OpenCV (Y down, Z into the scene)orglTF / Three.js (Y up). glTF stores Y up and Z toward the viewer; OpenCV's camera looks down +Z with Y down. Leave it on OpenCV whenever the mesh feedssolvePnP,cv2.rapidorprojectPointsalongside a real camera pose - it negates Y and Z, exactly the flipCV Camera Pose To 3D Viewapplies in the other direction. The glTF setting is for comparing against the viewer's own coordinates.recenter-keep model origin,bounding-box centre, orcentroid. The origin is what a solved pose refers to, so keep it unless you specifically want pose = "centre of the object", which is usually what an annotation anchor wants.centroidaverages vertices, so dense regions pull it.scale- multiplies every coordinate, default 1. This is your unit reconciler: a model authored in centimetres against a calibration in metres needs 0.01, and a scale error looks like a pose that's right in direction and wrong in depth.
Outputs: pts3d (Nx3 float32), tris (Mx3 int32), center (1x3 bounding-box centre of the returned points, after recentre and scale - wire it to CV Transform Points 3D to place an anchor), diameter (bounding-box diagonal, the natural scale reference: edge budgets for CV Mesh Split Long Edges and PPF sampling steps are fractions of it), vertex_count, triangle_count, and loaded.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
Manager → ComfyUI CV. Restart, then reload the tab. Python ≥ 3.12, V3-API ComfyUI, opencv-contrib-python-headless~=5.0.0.93. No model downloads. If you want the pack's example workflows to have something to load, run workflows/01_install_example_inputs.json once - it copies the sample media (including MilkTruck.glb) into ComfyUI/input, and 3-D models specifically go to ComfyUI/input/3d because that's the only folder core's Load 3D nodes list. Reload the page afterwards, since those dropdowns are built at definition time.
Common issues
"GLB is the format that works." This is the author's own warning, and it's specific: a .gltf with external buffers won't resolve them from ComfyUI's temp folder, so the geometry comes back empty. Export GLB - most 3-D tools will do it, including Blender.
Empty arrays rather than an error. An unreadable or non-triangle model returns empty arrays with loaded=false. The node is failure-tolerant by design, so gate the rest of the graph on loaded with an if/else instead of letting empty arrays reach a solver.
A model that renders right-side-up but tracks wrong. Check axis_convention against the camera path. It's a passive convention declaration, but the flip is real - the wrong value isn't a cosmetic difference.
Poses ~180° out. That's the winding/normals question rather than this node: run CV Mesh Vertex Normals and flip orientation if the model's faces are inside out.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| model_3d | FILE_3D_GLB,FILE_3D_GLTF,FILE_3D | Model from a 3D loader node ('Load 3D', 'Load 3D (Advanced)'). GLB is the format that actually works: a .gltf with external buffers will not resolve them from the temp folder. | |
| axis_convention | COMBO | OpenCV (Y down, Z into the scene) | glTF stores Y up and Z toward the viewer; OpenCV's camera looks down +Z with Y down. Leave this on the OpenCV setting whenever the mesh feeds solvePnP, cv2.rapid or projectPoints alongside a real camera pose - it negates Y and Z, exactly the flip 'CV Camera Pose To 3D View' applies in the other direction. Pick the glTF setting only to compare against the viewer's own coordinates. |
| recenteropt | COMBO | keep model origin | Where the returned points put the origin. The model origin is what a pose solved against this mesh will refer to, so keep it unless you specifically want the object's own centre (a pose then means 'centre of the object', which is usually what an annotation anchor wants). 'centroid' averages the vertices, so dense regions pull it. |
| scaleopt | FLOAT | 1.000.000001–1000000 | Multiplies every coordinate. Use it to put the mesh in the SAME units as the camera pose - a model authored in centimetres needs 0.01 against a calibration in metres, and a scale mismatch shows up as a pose that is right in direction and wrong in depth. |
Outputs (7)
| Name | Type | Description |
|---|---|---|
| pts3d | NPARRAY | Nx3 float32 vertices. This is the 'pts3d' input of the cv2.rapid nodes and a valid Nx3 point cloud for 'CV Point Cloud Normals' / ICP / PPF. |
| tris | NPARRAY | Mx3 int32 triangle indices into pts3d - the 'tris' input of the cv2.rapid nodes. Winding follows the file; cv2.rapid's backface culling depends on it, so a model exported with flipped normals silhouettes inside out. |
| center | NPARRAY | 1x3 bounding-box centre of the RETURNED points (after recentre and scale). Wire it to 'CV Transform Points 3D' or use it to place an annotation anchor. |
| diameter | FLOAT | Bounding-box diagonal of the returned mesh, in the same units. The natural scale reference: an edge budget for 'CV Mesh Split Long Edges' is a fraction of this, and PPF's sampling steps are fractions of it too. |
| vertex_count | INT | Number of vertices (0 when loaded=false). |
| triangle_count | INT | Number of triangles (0 when loaded=false). |
| loaded | BOOLEAN | False when the file could not be read or holds no triangles. Gate the rest of the graph on it with an 'If/Else Switch' instead of letting empty arrays reach a solver. |