1 /*
2 PLIB - A Suite of Portable Game Libraries
3 Copyright (C) 1998,2002 Steve Baker
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 For further information visit http://plib.sourceforge.net
20
21 $Id: ssgSaveVRML1.cxx 1568 2002-09-02 06:05:49Z sjbaker $
22
23 --------------------------------------------------------------------
24
25 This save routine was written by Warren Wilbur to support a sub-set
26 of the standard Virtual Reality Modelling Language v1.0 (i.e. VRML1.0).
27 The Version 1.0 Specification, 9-Nov-95 was used and should be available
28 at www.vrml.org
29
30 When reporting bugs/difficulties please mention 'VRML' in the subject
31 of your email and post to the plib developers mailing list.
32 */
33
34 #include <stdio.h>
35 #include "ssgLocal.h"
36 #include "ssgLoaderWriterStuff.h"
37
38 /* Function name: SaveVRML1MaterialNode
39 *
40 * Limitations: Before calling this function you must verify that
41 * at least one material exists whose textureName
42 * matches the one passed in to this function,
43 * otherwise you might save a empty Material node!
44 *
45 * Notes: Saving empty Material notes will create an
46 * unnecessary choking hazard for VRML loaders :(
47 */
48
SaveVRML1MaterialNode(FILE * fd,ssgIndexArray * materials_ptr,ssgSimpleStateArray * ssa_ptr,char * textureName,bool saveDiffuse,bool saveAmbient,bool saveEmission,bool saveSpecular)49 static void SaveVRML1MaterialNode(FILE *fd, ssgIndexArray *materials_ptr,
50 ssgSimpleStateArray *ssa_ptr,
51 char *textureName, bool saveDiffuse,
52 bool saveAmbient, bool saveEmission,
53 bool saveSpecular)
54 {
55 ssgSimpleState *ss_ptr;
56 int i;
57
58 /* Tell VRML loaders that we are providing a single color for each
59 * face in the array of indices written below. */
60
61 fprintf(fd, " MaterialBinding { value PER_FACE }\n");
62 fprintf(fd, " Material {\n");
63
64 if (saveDiffuse)
65 {
66 fprintf(fd, " diffuseColor [\n");
67 for (i = 0; i < materials_ptr->getNum(); i++)
68 {
69 ss_ptr = ssa_ptr->get(*(materials_ptr->get(i)));
70
71 /* If we are trying to save all untextured materials then check
72 * if either ptr is NULL */
73
74 if ( ( (textureName == NULL)&&
75 ((ss_ptr == NULL)||(ss_ptr->getTextureFilename() == NULL)) )||
76
77 /* If we are trying to save all materials that are textured by
78 * a specific texture then check if the texture filename matches */
79
80 ( (textureName != NULL)&&(ss_ptr != NULL)&&
81 (ss_ptr->getTextureFilename() != NULL)&&
82 (!strcmp(textureName, ss_ptr->getTextureFilename())) ) )
83 {
84 float diffuse0, diffuse1, diffuse2;
85
86 diffuse0 = ss_ptr->diffuse_colour[0];
87 diffuse1 = ss_ptr->diffuse_colour[1];
88 diffuse2 = ss_ptr->diffuse_colour[2];
89
90 #ifdef EXPERIMENTAL_ADD_AMBIENT_TO_DIFFUSE
91 diffuse0 += ss_ptr->ambient_colour[0];
92 diffuse1 += ss_ptr->ambient_colour[1];
93 diffuse2 += ss_ptr->ambient_colour[2];
94 #endif //EXPERIMENTAL_ADD_AMBIENT_TO_DIFFUSE
95
96 #ifdef EXPERIMENTAL_ADD_EMISSION_TO_DIFFUSE
97 diffuse0 += ss_ptr->emission_colour[0];
98 diffuse1 += ss_ptr->emission_colour[1];
99 diffuse2 += ss_ptr->emission_colour[2];
100 #endif //EXPERIMENTAL_ADD_EMISSION_TO_DIFFUSE
101
102 #ifdef EXPERIMENTAL_ADD_SPECULAR_TO_DIFFUSE
103 diffuse0 += ss_ptr->specular_colour[0];
104 diffuse1 += ss_ptr->specular_colour[1];
105 diffuse2 += ss_ptr->specular_colour[2];
106 #endif //EXPERIMENTAL_ADD_SPECULAR_TO_DIFFUSE
107
108 /* OpenGL caps the maximum RGB value for a colour to 1.0 when it
109 * calculates colours in a scene. If we don't cap the value OpenGL
110 * will do it for us. */
111
112 fprintf(fd, " %f %f %f,\n", diffuse0 > 1.0 ? 1.0:diffuse0,
113 diffuse1 > 1.0 ? 1.0:diffuse1, diffuse2 > 1.0 ? 1.0:diffuse2);
114 }
115 }
116 fprintf(fd, " ]\n"); //close diffuseColor array
117 }
118
119 if (saveAmbient)
120 {
121 fprintf(fd, " ambientColor [\n");
122 for (i = 0; i < materials_ptr->getNum(); i++)
123 {
124 ss_ptr = ssa_ptr->get(*(materials_ptr->get(i)));
125
126 /* If we are trying to save all untextured materials then check
127 * if either ptr is NULL */
128
129 if ( ( (textureName == NULL)&&
130 ((ss_ptr == NULL)||(ss_ptr->getTextureFilename() == NULL)) )||
131
132 /* If we are trying to save all materials that are textured by
133 * a specific texture then check if the texture filename matches */
134
135 ( (textureName != NULL)&&(ss_ptr != NULL)&&
136 (ss_ptr->getTextureFilename() != NULL)&&
137 (!strcmp(textureName, ss_ptr->getTextureFilename())) ) )
138 {
139 fprintf(fd, " %f %f %f,\n", ss_ptr->ambient_colour[0],
140 ss_ptr->ambient_colour[1], ss_ptr->ambient_colour[2]);
141 }
142 fprintf(fd, " ]\n"); //close ambientColor array
143 }
144 }
145
146 if(saveEmission)
147 {
148 fprintf(fd, " emissiveColor [\n");
149 for (i = 0; i < materials_ptr->getNum(); i++)
150 {
151 ss_ptr = ssa_ptr->get(*(materials_ptr->get(i)));
152
153 /* If we are trying to save all untextured materials then check
154 * if either ptr is NULL */
155
156 if ( ( (textureName == NULL)&&
157 ((ss_ptr == NULL)||(ss_ptr->getTextureFilename() == NULL)) )||
158
159 /* If we are trying to save all materials that are textured by
160 * a specific texture then check if the texture filename matches */
161
162 ( (textureName != NULL)&&(ss_ptr != NULL)&&
163 (ss_ptr->getTextureFilename() != NULL)&&
164 (!strcmp(textureName, ss_ptr->getTextureFilename())) ) )
165 {
166 fprintf(fd, " %f %f %f,\n", ss_ptr->emission_colour[0],
167 ss_ptr->emission_colour[1], ss_ptr->emission_colour[2]);
168 }
169 fprintf(fd, " ]\n"); //close emissionColor array
170 }
171 }
172
173 if(saveSpecular)
174 {
175 fprintf(fd, " specularColor [\n");
176 for (i = 0; i < materials_ptr->getNum(); i++)
177 {
178 ss_ptr = ssa_ptr->get(*(materials_ptr->get(i)));
179
180 /* If we are trying to save all untextured materials then check
181 * if either ptr is NULL */
182
183 if ( ( (textureName == NULL)&&
184 ((ss_ptr == NULL)||(ss_ptr->getTextureFilename() == NULL)) )||
185
186 /* If we are trying to save all materials that are textured by
187 * a specific texture then check if the texture filename matches */
188
189 ( (textureName != NULL)&&(ss_ptr != NULL)&&
190 (ss_ptr->getTextureFilename() != NULL)&&
191 (!strcmp(textureName, ss_ptr->getTextureFilename())) ) )
192 {
193 fprintf(fd, " %f %f %f,\n", ss_ptr->specular_colour[0],
194 ss_ptr->specular_colour[1], ss_ptr->specular_colour[2]);
195 }
196 fprintf(fd, " ]\n"); //close specularColor array
197 }
198 }
199
200 fprintf(fd, " }\n"); //close Material node
201 return;
202 }
203
204 /* The 'main' entry point for saving a model in VRML1.0 */
205
ssgSaveVRML1(const char * fname,ssgEntity * ent)206 int ssgSaveVRML1( const char* fname, ssgEntity *ent ) {
207 ssgVertexArray *vertices_ptr;
208 ssgIndexArray *indices_ptr;
209 FILE *fd;
210 ssgSimpleStateArray ssa;
211 ssgTexCoordArray *texcoord_ptr;
212 ssgIndexArray *materials_ptr;
213 bool textured_faces_found, untextured_faces_found,
214 textureFacesAlreadySaved;
215 int i, j, index1, index2, index3;
216 ssgSimpleState *ss_ptr, *ss_ptr2;
217
218 fd = fopen ( fname, "w" ) ;
219 if ( fd == NULL )
220 {
221 ulSetError ( UL_WARNING, "ssgSaveVRML1: Failed to open '%s' for writing",
222 fname );
223 return FALSE ;
224 }
225
226 vertices_ptr = new ssgVertexArray();
227 indices_ptr = new ssgIndexArray();
228 materials_ptr = new ssgIndexArray();
229 texcoord_ptr = new ssgTexCoordArray();
230
231 sgMat4 ident;
232 sgMakeIdentMat4( ident );
233 ssgAccumVerticesAndFaces( ent, ident, vertices_ptr, indices_ptr, -1.0f,
234 &ssa, materials_ptr, texcoord_ptr);
235
236 /* The spec requires every file to begin with these characters */
237
238 fprintf(fd, "#VRML V1.0 ascii\n\n");
239
240 /* Since a VRML file contains only one parent node we must use a
241 * node type that can have several child 'nodes' so we can save
242 * the materials, texture coordinates, vertices, and indices each
243 * as (seperate) child nodes. */
244
245 fprintf(fd, "Separator {\n");
246
247 /* Save all the individual vertices used in the model. It doesn't
248 * matter if there are duplicates... */
249
250 fprintf(fd, " Coordinate3 {\n point [\n");
251
252 for (i = 0; i < vertices_ptr->getNum(); i++)
253 {
254 fprintf(fd, " %f %f %f,\n", vertices_ptr->get(i)[0],
255 vertices_ptr->get(i)[1], vertices_ptr->get(i)[2]);
256 }
257
258 fprintf(fd, " ]\n }\n"); //close point array and
259 //Coordinate3
260
261 /* Chcck if the model is textured at all. This test will help us parse
262 * out how to save the model since it may be totally textured, partially
263 * textured, or not textured at all. */
264
265 textured_faces_found = false;
266 untextured_faces_found = false;
267 for (i = 0; i < materials_ptr->getNum(); i++)
268 {
269 ss_ptr = ssa.get(*(materials_ptr->get(i)));
270 if ( (ss_ptr != NULL)&&(ss_ptr->getTextureFilename() != NULL) )
271 {
272 textured_faces_found = true;
273 }
274 else
275 {
276 untextured_faces_found = true;
277 }
278 }
279
280 if (untextured_faces_found)
281 {
282 /* Save all the material node fields which VRML supports. Note that the
283 * VRML spec discourages complicated uses of the Material Node. We
284 * cannot expect VRML implementations to support the full syntax
285 * of the Material Node including ambient, diffuse, specular, emissive,
286 * shininess, and transparency. We should be always be okay if we just
287 * use diffuse. */
288
289 SaveVRML1MaterialNode(fd, materials_ptr, &ssa, NULL,
290 true, false, false, false);
291
292 /* Save all faces that are not textured in a single IndexedFaceSet node */
293
294 fprintf(fd, " IndexedFaceSet {\n coordIndex [\n");
295 for (i = 0; i < indices_ptr->getNum(); i+=3)
296 {
297 ss_ptr = ssa.get(*(materials_ptr->get(i/3)));
298
299 /* Make sure this face doesn't have a texture associated with it */
300
301 if ( (ss_ptr == NULL)||(ss_ptr->getTextureFilename() == NULL) )
302 {
303 index1 = *indices_ptr->get(i);
304 index2 = *indices_ptr->get(i+1);
305 index3 = *indices_ptr->get(i+2);
306
307 /* Check for index overflow since PLIB stores it as a short */
308
309 if ( (index1 < 0)||(index2 < 0)||(index3 < 0) )
310 {
311 ulSetError(UL_WARNING, "ssgSaveVRML1: Save error: index overflow, "
312 "value won't fit in 16bits.");
313 }
314 else
315 {
316 fprintf(fd, " %d, %d, %d, -1,\n", index1, index2,
317 index3);
318 }
319 }
320 }
321 fprintf(fd, " ]\n }\n"); //close coordIndex array and
322 //IndexedFaceSet
323 }
324
325 if (textured_faces_found)
326 {
327 /* Save all texture coordinates (per-vertex) for all the textures in
328 * the model. It doesn't matter if there is one texture or more than
329 * one since we will specify which texture to use with the coordinates
330 * before saving the portion of the indexed face set which uses that
331 * texture. */
332
333 fprintf(fd, " TextureCoordinate2 {\n point [\n");
334 for (i = 0; i < texcoord_ptr->getNum(); i++)
335 {
336
337 /* In VrmlView Pro 3.0 (Linux) textured models appear correct Left-Right
338 * but the texture is reversed Top-Bottom. Enabling the INVERSE_REPEAT
339 * macro fixes the problem for VrmlView. I don't want to enable this until
340 * I figure out where the problem really is!? */
341
342 //#define INVERSE_REPEAT(a) (a > 0.0 ? 1.0 - a:a + 1.0)
343 #define INVERSE_REPEAT(a) a
344
345 fprintf(fd, " %f %f,\n", texcoord_ptr->get(i)[0],
346 INVERSE_REPEAT(texcoord_ptr->get(i)[1]));
347 }
348 fprintf(fd, " ]\n }\n");
349
350 /* Now save separate Texture2 and IndexedFaceSet node pairs for each
351 * texture used in the model. Each of the IndexedFaceSet(s) will
352 * reference back to the initial vertices, materials, and texture
353 * coordinates (due to the lack of Seperator nodes in between).
354 * Find the first textured face starting at the i'th face. In this
355 * manner we will find the next texture used in the model and save
356 * all faces that are textured with it. */
357
358 for (i = 0; i < indices_ptr->getNum(); i+=3)
359 {
360 ss_ptr = ssa.get(*(materials_ptr->get(i/3)));
361 if ( (ss_ptr != NULL)&&(ss_ptr->getTextureFilename() != NULL) )
362 {
363 /* We've found the next textured face. Since we save all
364 * faces using a texture when we find the first face using
365 * that texture we must check if the faces for this texture
366 * have already been saved. If we can find a face using
367 * this texture earlier in the list of faces then we know
368 * that it has already been saved. */
369
370 textureFacesAlreadySaved = false;
371 for (j = 0; j < i; j+=3)
372 {
373 ss_ptr2 = ssa.get(*(materials_ptr->get(j/3)));
374 if ( (ss_ptr2 != NULL)&&
375 (ss_ptr2->getTextureFilename() != NULL)&&
376 (!strcmp(ss_ptr->getTextureFilename(),
377 ss_ptr2->getTextureFilename())) )
378 {
379 textureFacesAlreadySaved = true;
380 break;
381 }
382 }
383
384 if (!textureFacesAlreadySaved)
385 {
386 fprintf(fd, " Texture2 {\n");
387 fprintf(fd, " filename %s\n", ss_ptr->getTextureFilename());
388 //TODO: support CLAMP mode as well.
389 fprintf(fd, " wrapS REPEAT\n");
390 fprintf(fd, " wrapT REPEAT\n");
391 fprintf(fd, " }\n");
392
393 /* Save all the materials needed by this following indexed face
394 * set. This will save all materials that have the same texture
395 * filename specified below. */
396
397 SaveVRML1MaterialNode(fd, materials_ptr, &ssa,
398 ss_ptr->getTextureFilename(),
399 true, false, false, false);
400
401 fprintf(fd, " IndexedFaceSet {\n coordIndex [\n");
402 for (j = i; j < indices_ptr->getNum(); j+=3)
403 {
404 /* Save each face which is textured by the Texture2 node defined
405 * above. */
406
407 ss_ptr2 = ssa.get(*(materials_ptr->get(j/3)));
408 if ( (ss_ptr2 != NULL)&&
409 (ss_ptr2->getTextureFilename() != NULL)&&
410 (!strcmp(ss_ptr->getTextureFilename(),
411 ss_ptr2->getTextureFilename())) )
412 {
413 index1 = *indices_ptr->get(j);
414 index2 = *indices_ptr->get(j+1);
415 index3 = *indices_ptr->get(j+2);
416
417 /* Check for index overflow since PLIB stores it as a
418 * short */
419
420 if ( (index1 < 0)||(index2 < 0)||(index3 < 0) )
421 {
422 ulSetError(UL_WARNING, "ssgSaveVRML1: Save error: index "
423 "overflow, value won't fit in 16bits.");
424 }
425 else
426 {
427 fprintf(fd, " %d, %d, %d, -1,\n",
428 index1, index2, index3);
429 }
430 }
431 }
432 fprintf(fd, " ]\n }\n"); //close coordIndex array and
433 //IndexedFaceSet
434 }
435 }
436 }
437 }
438
439 fprintf(fd, "}\n"); //close Seperator
440 fclose( fd ) ;
441
442 delete vertices_ptr;
443 delete indices_ptr;
444 delete materials_ptr;
445 delete texcoord_ptr;
446 return TRUE;
447 }
448
449
450