kubakub align 3d model
Putting a generated mesh where the projector actually expects it
- model
- template
- model
- file
- matrix
- report
You ran your facade photo through Trellis or Hunyuan3D, imported the mesh into Blender - and it's floating a hundred units from anything, rotated 40 degrees, wrong by a factor of thirty. That's not a bug, it's the deal with image-to-3D: as the 3d-generation doc puts it, the silhouette is genuinely impressive and the geometry underneath is triangle soup, and nobody guarantees where in world space it lands.
kubakub align 3d model fixes the placement. Give it the generated model and the real model of the building (the one your projector and camera were placed against), and it returns the generated one moved, turned and scaled to lie on the real one. You get the aligned mesh in the graph, and it is written to disk as .glb or .obj in the template's own coordinates and size - so importing it into Blender or Houdini puts it simply in place. No transform to dial in by hand.
How the fit actually works
It's a point-cloud registration, and the author is upfront about the method in the source. Points are sampled from both surfaces, both are brought to their own principal axes, and the 24 ways of matching those axes (48 with mirrored ones) get tried with a short fit each - that's how a model lying on its side or facing backwards still lands correctly. Then it refines: nearest points, best turn + scale + move, over and over, dropping the worst fifth of the point pairs each round so a part one model has and the other hasn't doesn't drag the fit sideways.
Two details matter in practice. fit defaults to auto, and the tooltips are worth reading in order: the facade front is for a model made from a picture of a facade, where the generator invented a whole building - back wall, sides, roof - around your flat picture. Laid surface-to-surface that invented building gets squashed flat into your relief, so the node fits only what you'd see from the front (outline first, then wall) and keeps depth proportional. the whole model is the normal surface-to-surface fit; auto picks the facade front when the template is a thin relief and the model is much deeper, and says so in the report. And scale: per axis (the default) allows a different scale per template axis, which is what you want when the generator misread one dimension - mostly depth, from a single photo. The report will say so plainly, e.g. that z came back a quarter too long. one scale keeps proportions, keep the size only moves and turns.
Inputs and outputs you'll actually touch
Set fit, scale, mirror (flip that on when the result sits right but left and right are swapped) and cut_the_back (with the facade front: drops the invented back wall, sides and roof; off keeps everything). Then feed either a model mesh from an image-to-3D node plus a template mesh, or skip the wiring and put file paths in model_file and template_file - files keep their materials and textures. points (default 40000) trades time for exactness; file_format picks the written file.
Outputs: model (the aligned mesh), file (the path it wrote - output/3d/<save_name>_00001.glb), matrix (the 4x4, row by row, if you'd rather apply the transform yourself), and report. Read the report. It tells you how far off the fit was, what was turned and scaled, how close it is now, and - the useful bit - when more than a couple of percent still doesn't sit.
Install
It ships in kubakubkub/ComfyUI_KubaNodes: ComfyUI Manager → search kubakub nodes → restart. Or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/kubakubkub/ComfyUI_KubaNodes
python -m pip install -r ComfyUI_KubaNodes/requirements.txt
That requirements file adds OpenCV and nothing else; the pack wants ComfyUI 0.37.0 or newer, because it's written against the V3 node API. No AI models needed - an empty file field means the built-in sample, so align_3d_model.json runs before you download anything.
Where people get burned
trimesh. The node reads and writes meshes with trimesh, and it is not in requirements.txt. The startup self-check only attributes trimesh to the "mesh to field" node, so you may never see a warning for this one - you just get the error on queue. The fix:
python -m pip install trimesh
It reads .glb, .gltf, .obj, .ply, .stl and .off, so export from Blender or Houdini as .glb and stop worrying.
Depth is the axis that lies. A model built from one photo is often the right width and height and the wrong depth. Keep scale on per axis, then check the report's corrected numbers: on a floor read a third too high, the docs measure the fit landing within 0.2–1% depending on rotation - 6 to 30 cm on a 24 m facade. Cheap to verify, expensive to discover on site.
Nothing is on the network. From 0.4.0 the pack refuses \\server\share\... paths outright, because a shared workflow could point one at any machine. Map the drive and use the letter.
A generated model isn't the template. It sits less tightly than two copies of the same building. On a real Trellis 2 model of a 30 m station facade, the front fit put the outline within about 10 cm and the wall 0.2 m off (median), with some windows visibly beside the template's - because the generator draws windows wherever it likes. That's fine for growing forms out of a wall, less fine for measuring. Hand it to kubakub form height next.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| fit | COMBO | auto | the facade front: for a model made from a picture of a facade. Only what is seen from the front is laid onto the template (the outline, then the wall), so an invented back wall and roof do not pull, and what stands in front of the wall keeps its depth. The model must be upright and seen from the front, as image to 3D writes it. the whole model: surface onto surface, for a model that shows the same as the template, turned any way. auto: the facade front when the template is a thin relief and the model is far deeper. |
| scale | COMBO | per axis | per axis: a scale for each of the template's axes, so a misread depth or height is corrected. one scale: the model keeps its proportions. keep the size: only moved and turned. |
| mirror | BOOLEAN | false | Also try mirrored fits. Turn it on when the result sits right but left and right are swapped. Not used with the facade front. |
| cut_the_back | BOOLEAN | true | With the facade front: everything behind the template's back is cut away (the invented back wall, sides and roof). The facade and what stands in front of it stay. Off = the whole generated model is kept. |
| modelopt | MESH | The generated model, from an image-to-3D node, with its texture or colours. Or give a file in model_file. | |
| templateopt | MESH | The real model it should lie on. Or give a file in template_file. | |
| model_fileopt | STRING | The generated model as a file (.glb, .gltf, .obj, .ply, .stl). Its materials and textures are kept in the aligned file. | |
| template_fileopt | STRING | The real model as a file (.glb, .gltf, .obj, .ply, .stl), in the place and at the size your scene uses. From Blender or Houdini: export as .glb. | |
| save_nameopt | STRING | aligned_model | Name of the file written to the output folder (3d/<name>_00001.glb or .obj). |
| pointsopt | INT | 400002000–200000 | Points taken from the generated model for the fit (five times as many from the template). More = slower and more exact; 40000 takes a few seconds. |
| seedopt | INT | 00–2147483647 | Which points are taken. |
| file_formatopt | COMBO | .glb | .glb: one file with the textures inside, for Blender (File > Import > glTF) and Houdini (File > Import > glTF Scene, or a glTF Hierarchy node). .obj: the .obj, its .mtl and the colour texture as a PNG beside it, for any 3D software; the roughness / metal and normal maps are written beside it too. Both hold the model in the template's coordinates and size: import it without a transform. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| model | MESH | The generated model, lying on the template. |
| file | STRING | The aligned file (.glb or .obj): import it in Blender or Houdini, it is in place. |
| matrix | STRING | The 4x4 matrix that takes the generated model to the template, row by row, for a transform node of your own. |
| report | STRING | How far off it was, what was done, how close it is now, and which length had been misread. |