24
24
import georegression .struct .point .Point2D_F32 ;
25
25
import georegression .struct .point .Point3D_F32 ;
26
26
import georegression .struct .point .Point3D_F64 ;
27
+ import org .apache .commons .io .FilenameUtils ;
27
28
import org .ddogleg .struct .DogArray_I32 ;
28
29
29
30
import java .io .*;
@@ -44,30 +45,71 @@ public static void save( PointCloudReader cloud, Writer writer ) throws IOExcept
44
45
var obj = new ObjFileWriter (writer );
45
46
obj .addComment ("Created by BoofCV" );
46
47
48
+ boolean hasColor = cloud .colors ();
49
+
47
50
var point = new Point3D_F64 ();
48
51
int N = cloud .size ();
49
52
for (int i = 0 ; i < N ; i ++) {
50
53
cloud .get (i , point );
51
- obj .addVertex (point .x , point .y , point .z );
54
+ if (hasColor ) {
55
+ addRgbVertex (obj , point , cloud .getRGB (i ));
56
+ } else {
57
+ obj .addVertex (point .x , point .y , point .z );
58
+ }
52
59
obj .addPoint (-1 );
53
60
}
54
61
}
55
62
63
+ /**
64
+ * Creates a MTL file. This is required if the mesh is textured mapped
65
+ *
66
+ * @param textureFile Path to texture mapped file
67
+ * @param writer Where the MTL will be written to
68
+ */
69
+ public static void saveMtl ( String textureFile , Writer writer ) throws IOException {
70
+ // Use hard coded values
71
+ String text = """
72
+ newmtl %s
73
+ Ka 1.0 1.0 1.0
74
+ Kd 1.0 1.0 1.0
75
+ Ks 0.0 0.0 0.0
76
+ d 1.0
77
+ Ns 0.0
78
+ illum 0
79
+ map_Kd %s""" ;
80
+
81
+ String baseName = FilenameUtils .getBaseName (textureFile );
82
+ writer .write (String .format (text , baseName , textureFile ));
83
+ }
84
+
56
85
public static void save ( VertexMesh mesh , Writer writer ) throws IOException {
57
86
var obj = new ObjFileWriter (writer );
58
87
obj .addComment ("Created by BoofCV" );
59
88
89
+ // If there's a texture file, add a material
90
+ if (!mesh .textureName .isEmpty ()) {
91
+ String baseName = FilenameUtils .getBaseName (mesh .textureName );
92
+ obj .addLibrary (baseName + ".mtl" );
93
+ obj .addMaterial (baseName );
94
+ }
95
+
96
+ boolean hasVertexColors = mesh .rgb .size > 0 ;
97
+
60
98
// First save the vertexes
61
99
int N = mesh .vertexes .size ();
62
100
for (int i = 0 ; i < N ; i ++) {
63
101
Point3D_F64 p = mesh .vertexes .getTemp (i );
64
- obj .addVertex (p .x , p .y , p .z );
102
+ if (hasVertexColors ) {
103
+ addRgbVertex (obj , p , mesh .rgb .get (i ));
104
+ } else {
105
+ obj .addVertex (p .x , p .y , p .z );
106
+ }
65
107
}
66
108
67
109
// Save vertex normals
68
110
for (int i = 0 ; i < mesh .normals .size (); i ++) {
69
111
Point3D_F32 p = mesh .normals .getTemp (i );
70
- obj .addVertex (p .x , p .y , p .z );
112
+ obj .addVertexNormal (p .x , p .y , p .z );
71
113
}
72
114
73
115
// Save vertex textures
@@ -97,8 +139,20 @@ public static void save( VertexMesh mesh, Writer writer ) throws IOException {
97
139
}
98
140
}
99
141
142
+ private static void addRgbVertex ( ObjFileWriter obj , Point3D_F64 p , int rgb ) throws IOException {
143
+ // Convert to float format
144
+ double red = ((rgb >> 16 ) & 0xFF )/255.0 ;
145
+ double green = ((rgb >> 8 ) & 0xFF )/255.0 ;
146
+ double blue = (rgb & 0xFF )/255.0 ;
147
+ obj .addVertex (p .x , p .y , p .z , red , green , blue );
148
+ }
149
+
100
150
public static void load ( InputStream input , PointCloudWriter output ) throws IOException {
101
151
var obj = new ObjFileReader () {
152
+ @ Override protected void addLibrary ( String name ) {}
153
+
154
+ @ Override protected void addMaterial ( String name ) {}
155
+
102
156
@ Override protected void addVertex ( double x , double y , double z ) {
103
157
output .startPoint ();
104
158
output .location (x , y , z );
@@ -107,10 +161,9 @@ public static void load( InputStream input, PointCloudWriter output ) throws IOE
107
161
108
162
@ Override
109
163
protected void addVertexWithColor ( double x , double y , double z , double red , double green , double blue ) {
110
- int rgb = ((int )(255 *red )) << 16 | ((int )(255 *green )) << 8 | ((int )(255 *blue ));
111
164
output .startPoint ();
112
165
output .location (x , y , z );
113
- output .color (rgb );
166
+ output .color (convertToInt ( red , green , blue ) );
114
167
output .stopPoint ();
115
168
}
116
169
@@ -130,15 +183,20 @@ protected void addVertexWithColor( double x, double y, double z, double red, dou
130
183
public static void load ( InputStream input , VertexMesh output ) throws IOException {
131
184
output .reset ();
132
185
var obj = new ObjFileReader () {
186
+ // Library and material doesn't translate well a stream input and mesh output
187
+ // A single obj file can define multiple objects with different texture maps
188
+ @ Override protected void addLibrary ( String name ) {}
189
+
190
+ @ Override protected void addMaterial ( String name ) {}
191
+
133
192
@ Override protected void addVertex ( double x , double y , double z ) {
134
193
output .vertexes .append (x , y , z );
135
194
}
136
195
137
196
@ Override
138
197
protected void addVertexWithColor ( double x , double y , double z , double red , double green , double blue ) {
139
- int rgb = ((int )(255 *red )) << 16 | ((int )(255 *green )) << 8 | ((int )(255 *blue ));
140
198
output .vertexes .append (x , y , z );
141
- output .rgb .add (rgb );
199
+ output .rgb .add (convertToInt ( red , green , blue ) );
142
200
}
143
201
144
202
@ Override protected void addVertexNormal ( double x , double y , double z ) {
@@ -154,18 +212,36 @@ protected void addVertexWithColor( double x, double y, double z, double red, dou
154
212
@ Override protected void addLine ( DogArray_I32 vertexes ) {}
155
213
156
214
@ Override protected void addFace ( DogArray_I32 indexes , int vertexCount ) {
157
- int types = indexes .size /vertexCount ;
158
- for (int idxVert = 0 ; idxVert < vertexCount ; idxVert ++) {
159
- output .faceVertexes .add (indexes .get (idxVert )/types );
160
- for (int i = 1 ; i < types ; i ++) {
161
- if (indexes .get (i - 1 ) != indexes .get (i ))
162
- throw new RuntimeException ("Only vertexes types with the same index supported" );
163
- }
164
- }
165
-
166
- output .faceOffsets .add (output .faceVertexes .size );
215
+ addFactToMesh (indexes , vertexCount , output );
167
216
}
168
217
};
169
218
obj .parse (new BufferedReader (new InputStreamReader (input , StandardCharsets .UTF_8 )));
170
219
}
220
+
221
+ static void addFactToMesh ( DogArray_I32 indexes , int vertexCount , VertexMesh output ) {
222
+ // order of that indexes are specifies is always vertex/texture/normal
223
+
224
+ boolean hasTexture = output .texture .size () > 0 ;
225
+ boolean hasNormals = output .normals .size () > 0 ;
226
+
227
+ int typeCount = indexes .size /vertexCount ;
228
+ for (int idxVert = 0 ; idxVert < vertexCount ; idxVert ++) {
229
+ int index = idxVert *typeCount ;;
230
+ output .faceVertexes .add (indexes .get (index ++));
231
+
232
+ if (hasTexture ) {
233
+ output .faceVertexTextures .add (indexes .get (index ++));
234
+ }
235
+
236
+ if (hasNormals ) {
237
+ output .faceVertexNormals .add (indexes .get (index ));
238
+ }
239
+ }
240
+
241
+ output .faceOffsets .add (output .faceVertexes .size );
242
+ }
243
+
244
+ static int convertToInt ( double red , double green , double blue ) {
245
+ return ((int )(255 *red + 0.5 )) << 16 | ((int )(255 *green + 0.5 )) << 8 | ((int )(255 *blue + 0.5 ));
246
+ }
171
247
}
0 commit comments