Fix the UV baker's frame: o_voxel exports rotated
The everything-on job failed the gate at IoU 0.660 on geometry that measures 0.956. Cause: o_voxel's to_glb applies _BLENDER_ROT on export, (x,y,z) -> (x, z, -y), which is EXACTLY the inverse of to_camera_frame. So the vertex baker and the UV baker were returning meshes in different frames and the gate double-rotated the UV one. Verified numerically: OV == _BLENDER_ROT, and OV @ _BLENDER_ROT.T == I. to_glb now un-rotates back into the voxel-grid frame and returns a Trimesh, so every path in mesh.py speaks one frame. After the fix the baked mesh measures IoU 0.883 -- identical to its input -- with bounds matching to 3dp. That is the THIRD frame bug this session (mesh vertices vs ProjGrid's rotated lattice; marching_cubes' voxel-index space; now o_voxel's export rotation). None of them throw: each produces a plausible object that renders fine and silhouettes wrong. The gate caught all three, which is the argument for having it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
bf8b1a35d5
commit
7624051dbf
@ -24,6 +24,7 @@ from typing import Tuple
|
|||||||
|
|
||||||
import mlx.core as mx
|
import mlx.core as mx
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import trimesh
|
||||||
|
|
||||||
# Upstream fixes both: the model always works in a unit cube centred on the origin.
|
# Upstream fixes both: the model always works in a unit cube centred on the origin.
|
||||||
AABB = [[-0.5, -0.5, -0.5], [0.5, 0.5, 0.5]]
|
AABB = [[-0.5, -0.5, -0.5], [0.5, 0.5, 0.5]]
|
||||||
@ -151,7 +152,7 @@ def to_glb(vertices, faces, tex_voxels, attr_layout: dict, resolution: int,
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
from o_voxel import postprocess_cpu as pp
|
from o_voxel import postprocess_cpu as pp
|
||||||
|
|
||||||
return pp.to_glb(
|
scene = pp.to_glb(
|
||||||
vertices=vertices,
|
vertices=vertices,
|
||||||
faces=faces,
|
faces=faces,
|
||||||
attr_volume=_torch(tex_voxels.feats).float(),
|
attr_volume=_torch(tex_voxels.feats).float(),
|
||||||
@ -168,6 +169,18 @@ def to_glb(vertices, faces, tex_voxels, attr_layout: dict, resolution: int,
|
|||||||
remesh=remesh, remesh_band=1, remesh_project=0,
|
remesh=remesh, remesh_band=1, remesh_project=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# o_voxel EXPORTS ROTATED: it applies _BLENDER_ROT, i.e. (x,y,z) -> (x, z, -y),
|
||||||
|
# which is exactly the inverse of `to_camera_frame`. Left alone, the vertex baker
|
||||||
|
# and the UV baker return meshes in DIFFERENT frames, and the silhouette gate
|
||||||
|
# double-rotates the UV one — it scored 0.660 on geometry that measures 0.956.
|
||||||
|
# Rotate back so every path in this module speaks the voxel-grid frame; the export
|
||||||
|
# rotation is then applied once, deliberately, at the very end if wanted.
|
||||||
|
from .proj import _BLENDER_ROT
|
||||||
|
|
||||||
|
mesh = scene if isinstance(scene, trimesh.Trimesh) else scene.dump(concatenate=True)
|
||||||
|
mesh.vertices = np.asarray(mesh.vertices) @ _BLENDER_ROT.T
|
||||||
|
return mesh
|
||||||
|
|
||||||
|
|
||||||
# Upstream rotates the asset out of its internal frame on the way out (inference.py).
|
# Upstream rotates the asset out of its internal frame on the way out (inference.py).
|
||||||
EXPORT_ROTATION = np.array([[-1, 0, 0, 0],
|
EXPORT_ROTATION = np.array([[-1, 0, 0, 0],
|
||||||
|
|||||||
@ -171,12 +171,13 @@ def main():
|
|||||||
f" {time.time() - t:6.1f}s")
|
f" {time.time() - t:6.1f}s")
|
||||||
else:
|
else:
|
||||||
import torch
|
import torch
|
||||||
scene = to_glb(torch.from_numpy(np.asarray(pre.vertices, np.float32)),
|
# to_glb now returns a Trimesh already un-rotated into the voxel-grid
|
||||||
torch.from_numpy(np.asarray(pre.faces, np.int32)),
|
# frame, so it is directly comparable with the vertex-baked path
|
||||||
tex_voxels, PBR_ATTR_LAYOUT, info["output_resolution"],
|
mesh = to_glb(torch.from_numpy(np.asarray(pre.vertices, np.float32)),
|
||||||
texture_size=a.texture_size,
|
torch.from_numpy(np.asarray(pre.faces, np.int32)),
|
||||||
decimation_target=a.target_faces or 500_000)
|
tex_voxels, PBR_ATTR_LAYOUT, info["output_resolution"],
|
||||||
mesh = scene if isinstance(scene, trimesh.Trimesh) else scene.dump(concatenate=True)
|
texture_size=a.texture_size,
|
||||||
|
decimation_target=a.target_faces or 500_000)
|
||||||
print(f" bake UV {len(mesh.faces):,} faces, {a.texture_size}px "
|
print(f" bake UV {len(mesh.faces):,} faces, {a.texture_size}px "
|
||||||
f"{time.time() - t:6.1f}s")
|
f"{time.time() - t:6.1f}s")
|
||||||
info["baker"] = a.baker
|
info["baker"] = a.baker
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user