You can use point_cloud_utils to convert non-watertight ShapeNet models into a dataset of Signed Distance Functions (SDF). The process involves:
- Loading the mesh: Use
pcu.load_mesh_vf to get vertices and faces. - Creating a watertight manifold: Use
pcu.make_mesh_watertight with a specified manifold_resolution. Higher resolution improves quality but increases computation time. - Estimating normals: Use
pcu.estimate_mesh_vertex_normals on the new watertight mesh. - Sampling volume points: Generate random points in the bounding volume (ShapeNet models are typically normalized within
[-0.5, 0.5]^3) and compute their signed distances using pcu.signed_distance_to_mesh. - Sampling surface points: Use
pcu.sample_mesh_random to get face IDs and barycentric coordinates, then use pcu.interpolate_barycentric_coords to compute the actual 3D coordinates and normals at those points. - Saving results: Save the points and SDF values as a
.npz file and the watertight mesh as an .obj file using pcu.save_mesh_vfn.
import os
import numpy as np
import point_cloud_utils as pcu
# Configuration
category_path = "./02828884"
manifold_resolution = 20_000
num_vol_pts = 100_000
num_surf_pts = 100_000
for model_path in os.listdir(category_path):
# 1. Load mesh
v, f = pcu.load_mesh_vf(os.path.join(category_path, model_path, "model.obj"))
# 2. Convert to watertight manifold
vm, fm = pcu.make_mesh_watertight(v, f, manifold_resolution)
nm = pcu.estimate_mesh_vertex_normals(vm, fm)
# 3. Generate volume points and compute SDF
# ShapeNet shapes are normalized within [-0.5, 0.5]^3
p_vol = (np.random.rand(num_vol_pts, 3) - 0.5) * 1.1
sdf, _, _ = pcu.signed_distance_to_mesh(p_vol, vm, fm)
# 4. Sample surface points
fid_surf, bc_surf = pcu.sample_mesh_random(vm, fm, num_surf_pts)
p_surf = pcu.interpolate_barycentric_coords(fm, fid_surf, bc_surf, vm)
n_surf = pcu.interpolate_barycentric_coords(fm, fid_surf, bc_surf, nm)
# 5. Save data
npz_path = os.path.join(category_path, model_path, "samples.npz")
np.savez(npz_path, p_vol=p_vol, sdf_vol=sdf, p_surf=p_surf, n_surf=n_surf)
watertight_mesh_path = os.path.join(category_path, model_path, "model_watertight.obj")
pcu.save_mesh_vfn(watertight_mesh_path, vm, fm, nm)