stevee00 commited on
Commit
4921374
·
verified ·
1 Parent(s): 6df8db0

Upload src/interiorfusion/utils/mesh_utils.py

Browse files
src/interiorfusion/utils/mesh_utils.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Mesh utilities for export and manipulation."""
2
+
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Dict, List, Optional, Union
6
+
7
+ import numpy as np
8
+
9
+
10
+ def export_mesh(
11
+ mesh,
12
+ output_path: Union[str, Path],
13
+ format: str = "glb",
14
+ materials: Optional[List[Dict]] = None,
15
+ ) -> str:
16
+ """
17
+ Export mesh to various 3D formats.
18
+
19
+ Supported formats: glb, fbx, obj, usdz, ply
20
+ """
21
+ output_path = Path(output_path)
22
+ output_path.parent.mkdir(parents=True, exist_ok=True)
23
+
24
+ try:
25
+ import trimesh
26
+ except ImportError:
27
+ raise ImportError("trimesh is required for mesh export")
28
+
29
+ format = format.lower()
30
+
31
+ if format == "glb" or format == "gltf":
32
+ # Export as GLB (GL Transmission Format)
33
+ mesh.export(str(output_path))
34
+
35
+ elif format == "fbx":
36
+ # Export as FBX
37
+ # trimesh can export some formats; for FBX we may need assimp
38
+ mesh.export(str(output_path))
39
+
40
+ elif format == "obj":
41
+ # Export as OBJ with MTL
42
+ mesh.export(str(output_path))
43
+
44
+ elif format == "usdz":
45
+ # USDZ requires USD libraries
46
+ # For now, export as GLB and note conversion needed
47
+ glb_path = output_path.with_suffix(".glb")
48
+ mesh.export(str(glb_path))
49
+ print(f"USDZ export: converted to GLB at {glb_path}. Use Apple's usdzconvert.")
50
+
51
+ elif format == "ply":
52
+ # Export as PLY (Stanford Polygon Format)
53
+ mesh.export(str(output_path))
54
+
55
+ else:
56
+ raise ValueError(f"Unsupported format: {format}")
57
+
58
+ return str(output_path)
59
+
60
+
61
+ def export_gaussian_splatting(
62
+ gaussian_cloud: np.ndarray,
63
+ output_path: Union[str, Path],
64
+ ) -> str:
65
+ """
66
+ Export Gaussian Splatting representation to PLY format.
67
+
68
+ Args:
69
+ gaussian_cloud: [N, 14] array with columns:
70
+ [x, y, z, scale_x, scale_y, scale_z,
71
+ rot_qx, rot_qy, rot_qz, rot_qw,
72
+ r, g, b, opacity]
73
+ """
74
+ output_path = Path(output_path)
75
+ output_path.parent.mkdir(parents=True, exist_ok=True)
76
+
77
+ try:
78
+ from plyfile import PlyData, PlyElement
79
+ except ImportError:
80
+ # Fallback: write simple ASCII PLY
81
+ _write_ascii_ply(gaussian_cloud, output_path)
82
+ return str(output_path)
83
+
84
+ # Write binary PLY with Gaussian attributes
85
+ num_points = len(gaussian_cloud)
86
+
87
+ # Define dtype for Gaussian splatting format
88
+ dtype = [
89
+ ('x', 'f4'), ('y', 'f4'), ('z', 'f4'),
90
+ ('scale_0', 'f4'), ('scale_1', 'f4'), ('scale_2', 'f4'),
91
+ ('rot_0', 'f4'), ('rot_1', 'f4'), ('rot_2', 'f4'), ('rot_3', 'f4'),
92
+ ('f_dc_0', 'f4'), ('f_dc_1', 'f4'), ('f_dc_2', 'f4'),
93
+ ('opacity', 'f4'),
94
+ ]
95
+
96
+ # Convert RGB to spherical harmonics DC term (simplified)
97
+ # In production, would also include SH bands 1, 2, 3
98
+
99
+ vertices = np.zeros(num_points, dtype=dtype)
100
+ vertices['x'] = gaussian_cloud[:, 0]
101
+ vertices['y'] = gaussian_cloud[:, 1]
102
+ vertices['z'] = gaussian_cloud[:, 2]
103
+ vertices['scale_0'] = gaussian_cloud[:, 3]
104
+ vertices['scale_1'] = gaussian_cloud[:, 4]
105
+ vertices['scale_2'] = gaussian_cloud[:, 5]
106
+ vertices['rot_0'] = gaussian_cloud[:, 6]
107
+ vertices['rot_1'] = gaussian_cloud[:, 7]
108
+ vertices['rot_2'] = gaussian_cloud[:, 8]
109
+ vertices['rot_3'] = gaussian_cloud[:, 9]
110
+ vertices['f_dc_0'] = gaussian_cloud[:, 10]
111
+ vertices['f_dc_1'] = gaussian_cloud[:, 11]
112
+ vertices['f_dc_2'] = gaussian_cloud[:, 12]
113
+ vertices['opacity'] = gaussian_cloud[:, 13]
114
+
115
+ el = PlyElement.describe(vertices, 'vertex')
116
+ PlyData([el], text=True).write(str(output_path))
117
+
118
+ return str(output_path)
119
+
120
+
121
+ def _write_ascii_ply(gaussian_cloud: np.ndarray, output_path: Path):
122
+ """Write simple ASCII PLY fallback."""
123
+ num_points = len(gaussian_cloud)
124
+
125
+ with open(output_path, 'w') as f:
126
+ f.write("ply\n")
127
+ f.write("format ascii 1.0\n")
128
+ f.write(f"element vertex {num_points}\n")
129
+ f.write("property float x\n")
130
+ f.write("property float y\n")
131
+ f.write("property float z\n")
132
+ f.write("property float scale_0\n")
133
+ f.write("property float scale_1\n")
134
+ f.write("property float scale_2\n")
135
+ f.write("property float rot_0\n")
136
+ f.write("property float rot_1\n")
137
+ f.write("property float rot_2\n")
138
+ f.write("property float rot_3\n")
139
+ f.write("property float f_dc_0\n")
140
+ f.write("property float f_dc_1\n")
141
+ f.write("property float f_dc_2\n")
142
+ f.write("property float opacity\n")
143
+ f.write("end_header\n")
144
+
145
+ for point in gaussian_cloud:
146
+ f.write(" ".join(f"{v:.6f}" for v in point) + "\n")
147
+
148
+
149
+ def decimate_mesh(mesh, target_faces: int = 10000):
150
+ """Reduce mesh complexity for mobile/VR."""
151
+ try:
152
+ import trimesh
153
+ if hasattr(mesh, 'simplify'):
154
+ return mesh.simplify(target_faces)
155
+ except Exception:
156
+ pass
157
+ return mesh
158
+
159
+
160
+ def compute_uv_atlas(mesh):
161
+ """Compute UV atlas for mesh texture mapping."""
162
+ try:
163
+ import xatlas
164
+ # Placeholder: would use xatlas for UV unwrapping
165
+ return mesh
166
+ except ImportError:
167
+ return mesh
168
+
169
+
170
+ def merge_materials(materials: List[Dict]) -> Dict:
171
+ """Merge multiple PBR materials into a single material atlas."""
172
+ # In production: create texture atlas
173
+ # For now, return first material
174
+ if materials:
175
+ return materials[0]
176
+ return {
177
+ "albedo": [0.7, 0.7, 0.7],
178
+ "metallic": 0.0,
179
+ "roughness": 0.5,
180
+ }