Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/orxonox/trunk/importer/object.cc @ 3070

Last change on this file since 3070 was 3070, checked in by bensch, 19 years ago

orxonox/trunk/importer: ability to readIn BMP files for the diffuse-color-Texture

File size: 20.1 KB
Line 
1/*
2   orxonox - the future of 3D-vertical-scrollers
3
4   Copyright (C) 2004 orx
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10
11   ### File Specific:
12   main-programmer: Benjamin Grauer
13   co-programmer: ...
14*/
15
16#include "object.h"
17
18/**
19   \brief Creates a 3D-Object, but does not load any 3D-models
20   pretty useless
21*/
22Object::Object ()
23{
24
25  initialize();
26
27  BoxObject();
28
29  importToGL ();
30
31  cleanup();
32}
33
34/**
35   \brief Crates a 3D-Object and loads in a File
36   \param fileName file to parse and load (must be a .obj file)
37*/
38Object::Object(char* fileName)
39{
40  initialize();
41
42  importFile (fileName);
43
44  importToGL ();
45
46  cleanup();
47}
48
49/**
50   \brief Crates a 3D-Object, loads in a File and scales it.
51   \param fileName file to parse and load (must be a .obj file)
52   \param scaling The factor that the object will be scaled with.
53*/
54Object::Object(char* fileName, float scaling)
55{
56  initialize();
57  scaleFactor = scaling;
58
59  importFile (fileName);
60
61  importToGL ();
62
63  cleanup();
64}
65
66/**
67   \brief deletes an Object
68*/
69Object::~Object()
70{
71  if (verbose >= 2)
72    printf ("Deleting display Lists.\n");
73  Group* walker = firstGroup;
74  while (walker != NULL)
75    {
76      glDeleteLists (walker->listNumber, 1);
77      Group* delWalker = walker;
78      walker = walker->next;
79      delete delWalker;
80    } 
81}
82
83
84/**
85   \brief Draws the Objects of all Groups.
86   It does this by just calling the Lists that must have been created earlier.
87*/
88void Object::draw (void) const
89{
90  if (verbose >=2)
91    printf("drawing the 3D-Objects\n"); 
92  Group* walker = firstGroup;
93  while (walker != NULL)
94    {
95      if (verbose >= 3)
96        printf ("Drawing object %s\n", walker->name);
97      glCallList (walker->listNumber);
98      walker = walker->next;
99    }
100}
101
102/**
103   \brief Draws the Object number groupNumber
104   It does this by just calling the List that must have been created earlier.
105   \param groupNumber The number of the group that will be displayed.
106*/
107void Object::draw (int groupNumber) const 
108{
109  if (groupNumber >= groupCount)
110    {
111      if (verbose>=1)
112        printf ("You requested object number %i, but this File only contains of %i Objects.\n", groupNumber-1, groupCount);
113      return;
114    }
115  if (verbose >=2)
116    printf("drawing the requested 3D-Objects if found.\n"); 
117  Group* walker = firstGroup;
118  int counter = 0;
119  while (walker != NULL)
120    {
121      if (counter == groupNumber)
122        {
123          if (verbose >= 2)
124            printf ("Drawing object number %i named %s\n", counter, walker->name);
125          glCallList (walker->listNumber);
126          return;
127        }
128      ++counter;
129      walker = walker->next;
130    }
131  if (verbose >= 1)
132    printf("Object number %i in %s not Found.\n", groupNumber, objFileName);
133  return;
134
135}
136
137/**
138   \brief Draws the Object with a specific groupName
139   It does this by just calling the List that must have been created earlier.
140   \param groupName The name of the group that will be displayed.
141*/
142void Object::draw (char* groupName) const
143{
144  if (verbose >=2)
145    printf("drawing the requested 3D-Objects if found.\n"); 
146  Group* walker = firstGroup;
147  while (walker != NULL)
148    {
149      if (!strcmp(walker->name, groupName))
150        {
151          if (verbose >= 2)
152            printf ("Drawing object %s\n", walker->name);
153          glCallList (walker->listNumber);
154          return;
155        }
156      walker = walker->next;
157    }
158  if (verbose >= 2)
159    printf("Object Named %s in %s not Found.\n", groupName, objFileName);
160  return;
161}
162
163/**
164   \returns Count of the Objects in this File
165*/
166int Object::getGroupCount (void) const
167{
168  return groupCount;
169}
170
171/**
172    \brief initializes the Object
173    This Function initializes all the needed arrays, Lists and clientStates
174*/
175bool Object::initialize (void)
176{
177  if (verbose >=3)
178    printf("new 3D-Object is being created\n"); 
179
180  // setting the start group;
181  firstGroup = new Group;
182  currentGroup = firstGroup;
183  groupCount = 0;
184 
185  initGroup (firstGroup);
186  mtlFileName = "";
187  scaleFactor = 1;
188  material = new Material();
189
190  vertices = new Array();
191  vTexture = new Array();
192  normals = new Array();
193
194  return true;
195}
196
197/**
198   \brief initializes a new Group object
199*/
200bool Object::initGroup(Group* group)
201{
202  if (verbose >= 2)
203    printf("Adding new Group\n");
204  group->name = "";
205  group->faceMode = -1;
206  group->faceCount = 0; 
207  group->next = NULL;
208
209  group->firstFace = new Face;
210  initFace (group->firstFace);
211  group->currentFace = group->firstFace;
212}
213
214/**
215   \brief initializes a new Face. (sets default Values)
216*/
217bool Object::initFace (Face* face)
218{
219  face->vertexCount = 0;
220
221  face->firstElem = NULL;
222 
223  face->materialString = NULL;
224 
225  face->next = NULL;
226
227  return true;
228}
229
230/**
231   \brief finalizes an Object.
232   This funcion is needed, to delete all the Lists, and arrays that are no more needed because they are already imported into openGL. This will be applied at the end of the importing Process.
233*/
234bool Object::cleanup(void)
235{
236  if (verbose >=2)
237    printf("cleaning up the 3D-Object to save Memory.\n");
238
239  if (vertices != NULL)
240    delete vertices;
241  if (vTexture != NULL)
242    delete vTexture;
243  if (normals != NULL)
244    delete normals;
245
246  if (material != NULL)
247    delete material;
248
249  cleanupGroup(firstGroup);
250  return true; 
251}
252
253/**
254   \brief Cleans up all groups starting from group.
255   \param group the first Group to clean
256*/
257bool Object::cleanupGroup (Group* group)
258{
259  if (verbose>=4)
260    printf ("Cleaning up group\n");
261  if (group->firstFace != NULL)
262    {
263      cleanupFace (group->firstFace);
264      delete group->firstFace;
265    }
266
267  if (group->next !=NULL)
268    cleanupGroup (group->next);
269  return true;
270}
271
272/**
273   \brief Cleans up all Faces starting from face.
274   \param face the first face to clean
275*/
276bool Object::cleanupFace (Face* face)
277{
278  if (verbose>=4)
279    printf ("Cleaning up Face\n");
280
281  if (face->materialString != NULL)
282      delete face->materialString;
283  if (face->firstElem != NULL)
284    {
285      cleanupFaceElement(face->firstElem);
286      delete face->firstElem;
287    }
288     
289  if (face->next != NULL)
290    {
291      cleanupFace (face->next);
292      delete face->next;
293    }
294     
295}
296
297
298/**
299   \brief Cleans up all FaceElements starting from faceElem.
300   \param faceElem the first FaceElement to clean.
301*/
302bool Object::cleanupFaceElement(FaceElement* faceElem)
303{
304  if (faceElem->value != NULL)
305    if (verbose>=4)
306      printf ("Cleaning up faceElement %s\n", faceElem->value);
307    //delete faceElem->value;
308
309  if (faceElem->next != NULL)
310    {
311      cleanupFaceElement (faceElem->next);
312      //      delete faceElem->next;
313    }
314}
315
316/**
317   \brief Imports a obj file and handles the the relative location
318   \param fileName The file to import
319*/
320bool Object::importFile (char* fileName)
321{
322  if (verbose >=3)
323    printf("preparing to read in file: %s\n", fileName);   
324  objFileName = fileName;
325  this->readFromObjFile (objFileName);
326  return true;
327}
328
329/**
330   \brief Reads in the .obj File and sets all the Values.
331   This function does read the file, parses it for the occurence of things like vertices, faces and so on, and executes the specific tasks
332   \param fileName the File that will be parsed (.obj-file)
333*/
334bool Object::readFromObjFile (char* fileName)
335{
336  OBJ_FILE = new ifstream(fileName);
337  if (!OBJ_FILE->is_open())
338    {
339      if (verbose >=1)
340        printf ("unable to open .OBJ file: %s\n Loading Box Object instead.\n", fileName);
341      BoxObject();
342      return false;
343    }
344  objFileName = fileName;
345  char Buffer[10000];
346  while(!OBJ_FILE->eof())
347    {
348      OBJ_FILE->getline(Buffer, 10000);
349      if (verbose >=4)
350        printf ("Read input line: %s\n",Buffer);
351     
352
353      // case vertice
354      if (!strncmp(Buffer, "v ", 2))
355        {
356          readVertex(Buffer+2);
357        }
358
359      // case face
360      else if (!strncmp(Buffer, "f ", 2))
361        {
362          readFace (Buffer+2);
363        }
364     
365      else if (!strncmp(Buffer, "mtllib ", 7))
366        {
367          readMtlLib (Buffer+7);
368        }
369
370      else if (!strncmp(Buffer, "usemtl ", 7))
371        {
372          readUseMtl (Buffer+7);
373        }
374
375      // case VertexNormal
376      else if (!strncmp(Buffer, "vn ", 3))
377      {
378        readVertexNormal(Buffer+3);
379      }
380
381      // case VertexTextureCoordinate
382      else if (!strncmp(Buffer, "vt ", 3))
383      {
384        readVertexTexture(Buffer+3);
385      }
386      // case group
387      else if (!strncmp(Buffer, "g ", 2))
388        {
389          readGroup (Buffer+2);
390        }
391      else if (!strncmp(Buffer, "s ", 2))
392        {
393          if (verbose >= 2)
394            printf("smoothing groups not supportet yet. line: %s\n", Buffer);
395        }
396    }
397  OBJ_FILE->close();
398  return true;
399
400}
401
402/**
403   \brief parses a group String
404   This function initializes a new Group.
405   With it you should be able to import .obj-files with more than one Objects inside.
406   \param groupString the new Group to create
407*/
408bool Object::readGroup (char* groupString)
409{
410  // setting the group name if not default.
411  if (strcmp(currentGroup->name, "default"))
412    {
413      currentGroup->name = new char [strlen(groupString)];
414      strcpy(currentGroup->name, groupString);
415    }
416  if (groupCount != 0 && currentGroup->faceCount>0)
417    {
418      //      finalizeGroup(currentGroup);
419      currentGroup = currentGroup->next = new Group;
420      initGroup(currentGroup);
421    }
422
423  ++groupCount;
424
425}
426
427/**
428   \brief parses a vertex-String
429   If a vertex line is found this function will inject it into the vertex-Array
430   \param vertexString The String that will be parsed.
431*/
432bool Object::readVertex (char* vertexString)
433{
434  char subbuffer1[20];
435  char subbuffer2[20];
436  char subbuffer3[20];
437  sscanf (vertexString, "%s %s %s", subbuffer1, subbuffer2, subbuffer3);
438  if (verbose >= 3)
439    printf ("reading in a vertex: %s %s %s\n", subbuffer1, subbuffer2, subbuffer3);
440  vertices->addEntry(atof(subbuffer1)*scaleFactor, atof(subbuffer2)*scaleFactor, atof(subbuffer3)*scaleFactor);
441  return true;
442}
443
444/**
445   \brief parses a face-string
446   If a face line is found this function will add it to the glList.
447   The function makes a difference between QUADS and TRIANGLES, and will if changed re-open, set and re-close the gl-processe.
448   \param faceString The String that will be parsed.
449*/
450bool Object::readFace (char* faceString)
451{
452  if (currentGroup->faceCount >0)
453    currentGroup->currentFace = currentGroup->currentFace->next = new Face;
454  initFace (currentGroup->currentFace);
455
456  FaceElement* tmpElem = currentGroup->currentFace->firstElem = new FaceElement;
457  tmpElem->next = NULL;
458  tmpElem->value = NULL;
459  while(strcmp (faceString, "\0"))
460    {
461      if (currentGroup->currentFace->vertexCount>0)
462          tmpElem = tmpElem->next = new FaceElement;
463      tmpElem->next = NULL;
464      tmpElem->value = NULL;
465
466      char tmpValue [50];
467      sscanf (faceString, "%s", tmpValue);
468      tmpElem->value = new char [strlen(tmpValue)];
469      strcpy (tmpElem->value, tmpValue);
470
471      faceString += strlen(tmpElem->value);
472      if (strcmp (faceString, "\0"))
473        faceString++;
474      currentGroup->currentFace->vertexCount++;
475    }
476
477  currentGroup->faceCount += currentGroup->currentFace->vertexCount -2;
478}
479
480/**
481   \brief parses a vertexNormal-String
482   If a vertexNormal line is found this function will inject it into the vertexNormal-Array
483   \param normalString The String that will be parsed.
484*/
485bool Object::readVertexNormal (char* normalString)
486{
487  char subbuffer1[20];
488  char subbuffer2[20];
489  char subbuffer3[20];
490  sscanf (normalString, "%s %s %s", subbuffer1, subbuffer2, subbuffer3);
491  if (verbose >=3 )
492    printf("found vertex-Normal %s, %s, %s\n", subbuffer1,subbuffer2,subbuffer3);
493  normals->addEntry(atof(subbuffer1), atof(subbuffer2), atof(subbuffer3));
494  return true;
495}
496
497/**
498   \brief parses a vertexTextureCoordinate-String
499   If a vertexTextureCoordinate line is found this function will inject it into the vertexTexture-Array
500   \param vTextureString The String that will be parsed.
501*/
502bool Object::readVertexTexture (char* vTextureString)
503{
504  char subbuffer1[20];
505  char subbuffer2[20];
506  sscanf (vTextureString, "%s %s", subbuffer1, subbuffer2);
507  if (verbose >=3 )
508    printf("found vertex-Texture %s, %s\n", subbuffer1,subbuffer2);
509  vTexture->addEntry(atof(subbuffer1));
510  vTexture->addEntry(atof(subbuffer2));
511  return true;
512}
513
514/**
515    \brief Function to read in a mtl File.
516    this Function parses all Lines of an mtl File
517    \param mtlFile The .mtl file to read
518*/
519bool Object::readMtlLib (char* mtlFile)
520{
521  MTL_FILE = new ifstream (mtlFile);
522  if (!MTL_FILE->is_open())
523    {
524      if (verbose >= 1)
525        printf ("unable to open file: %s\n", mtlFile);
526      return false;
527    }
528  mtlFileName = mtlFile;
529  if (verbose >=2)
530    printf ("Opening mtlFile: %s\n", mtlFileName);
531  char Buffer[500];
532  Material* tmpMat = material;
533  while(!MTL_FILE->eof())
534    {
535      MTL_FILE->getline(Buffer, 500);
536      if (verbose >= 4)
537        printf("found line in mtlFile: %s\n", Buffer);
538     
539
540      // create new Material
541      if (!strncmp(Buffer, "newmtl ", 7))
542        {
543          tmpMat = tmpMat->addMaterial(Buffer+7);
544          //      printf ("%s, %p\n", tmpMat->getName(), tmpMat);
545        }
546      // setting a illumMode
547      else if (!strncmp(Buffer, "illum ", 6))
548        {
549          tmpMat->setIllum(Buffer+6);
550
551        }
552      // setting Diffuse Color
553      else if (!strncmp(Buffer, "Kd ", 3))
554        {
555          tmpMat->setDiffuse(Buffer+3);
556        }
557      // setting Ambient Color
558      else if (!strncmp(Buffer, "Ka ", 3))
559        {
560          tmpMat->setAmbient(Buffer+3);
561        }
562      // setting Specular Color
563      else if (!strncmp(Buffer, "Ks ", 3))
564        {
565          tmpMat->setSpecular(Buffer+3);
566        }
567      // setting The Specular Shininess
568      else if (!strncmp(Buffer, "Ns ", 3))
569        {
570          tmpMat->setShininess(Buffer+3);
571        }
572      // setting up transparency
573      else if (!strncmp(Buffer, "d ", 2))
574        {
575          tmpMat->setTransparency(Buffer+2);
576        }
577      else if (!strncmp(Buffer, "Tf ", 3))
578        {
579          tmpMat->setTransparency(Buffer+3);
580        }
581     
582      else if (!strncmp(Buffer, "map_Kd ", 7))
583        {
584          tmpMat->setDiffuseMap(Buffer+7);
585        }
586      else if (!strncmp(Buffer, "map_Ka ", 7))
587        {
588          tmpMat->setAmbientMap(Buffer+7);
589        }
590      else if (!strncmp(Buffer, "map_Ks ", 7))
591        {
592          tmpMat->setSpecularMap(Buffer+7);
593        }
594      else if (!strncmp(Buffer, "bump ", 5))
595        {
596          tmpMat->setBump(Buffer+7);
597        }
598     
599
600    }
601  return true;
602}
603
604/**
605   \brief Function that selects a material, if changed in the obj file.
606   \param matString the Material that will be set.
607*/
608bool Object::readUseMtl (char* matString)
609{
610  if (!strcmp (mtlFileName, ""))
611    {
612      if (verbose >= 1)
613        printf ("Not using new defined material, because no mtlFile found yet\n");
614      return false;
615    }
616     
617  if (currentGroup->faceCount >0)
618    currentGroup->currentFace = currentGroup->currentFace->next = new Face;
619  initFace (currentGroup->currentFace);
620 
621  currentGroup->currentFace->materialString = new char[strlen(matString)];
622  strcpy (currentGroup->currentFace->materialString, matString);
623 
624  if (currentGroup->faceCount == 0)
625    currentGroup->faceCount ++;
626
627}
628
629/**
630   \brief reads and includes the Faces/Materials into the openGL state Machine
631*/
632bool Object::importToGL (void)
633{
634
635  // finalize the Arrays
636  vertices->finalizeArray();
637  vTexture->finalizeArray();
638  normals->finalizeArray();
639
640  currentGroup = firstGroup;
641
642  while (currentGroup != NULL)
643    {
644
645      // creating a glList for the Group
646      if ((currentGroup->listNumber = glGenLists(1)) == 0)
647        {
648          printf ("list could not be created for this Object\n");
649          return false;
650        }
651      glNewList (currentGroup->listNumber, GL_COMPILE);
652
653      // Putting Faces to GL
654      Face* tmpFace = currentGroup->firstFace;
655      while (tmpFace != NULL)
656        {
657          if (tmpFace->vertexCount == 0 && tmpFace->materialString != NULL)
658            {
659              if (currentGroup->faceMode != -1)
660                glEnd();
661              currentGroup->faceMode = 0;
662              if (verbose >= 2)
663                printf ("using material %s for coming Faces.\n", tmpFace->materialString);
664              Material* tmpMat;
665              if ((tmpMat = material->search(tmpFace->materialString)) != NULL)
666                tmpMat->select();
667
668            }
669
670          else if (tmpFace->vertexCount == 3)
671            {
672              if (currentGroup->faceMode != 3)
673                {
674                  if (currentGroup->faceMode != -1)
675                    glEnd();
676                  glBegin(GL_TRIANGLES);
677                }
678             
679              currentGroup->faceMode = 3;
680              if (verbose >=3)
681                printf ("found triag.\n");
682            }
683         
684          else if (tmpFace->vertexCount == 4)
685            {
686              if (currentGroup->faceMode != 4)
687                {
688                  if (currentGroup->faceMode != -1)
689                    glEnd();
690                  glBegin(GL_QUADS);
691                }
692              currentGroup->faceMode = 4;
693              if (verbose >=3 )
694                printf ("found quad.\n");
695            }
696         
697          else if (tmpFace->vertexCount > 4)
698            {
699              if (currentGroup->faceMode != -1)
700                glEnd();
701              glBegin(GL_POLYGON);
702              if (verbose >=3)
703                printf ("Polygon with %i faces found.", tmpFace->vertexCount);
704              currentGroup->faceMode = tmpFace->vertexCount;
705            }
706         
707          FaceElement* tmpElem = tmpFace->firstElem;
708          while (tmpElem != NULL)
709            {
710              //      printf ("%s\n", tmpElem->value);
711              addGLElement(tmpElem->value);
712              tmpElem = tmpElem->next;
713            }
714          tmpFace = tmpFace->next;
715        }
716      glEnd();
717      glEndList();
718      currentGroup = currentGroup->next;
719    } 
720}
721
722/**
723   \brief Adds a Face-element (one vertex of a face) with all its information.
724   It does this by searching:
725   1. The Vertex itself
726   2. The VertexNormale
727   3. The VertexTextureCoordinate
728   merging this information, the face will be drawn.
729
730*/
731bool Object::addGLElement (char* elementString)
732{
733  if (verbose >=3)
734    printf ("importing grafical Element to openGL: %s\n", elementString);
735  char vertex [100];
736  strcpy (vertex, elementString);
737
738  char* texture;
739  if ((texture = strstr (vertex, "/")) != NULL)
740    {
741      texture[0] = '\0';
742      texture ++;
743      glTexCoord2fv(vTexture->getArray()+(atoi(texture)-1)*2);
744
745      char* normal;
746      if ((normal = strstr (texture, "/")) !=NULL)
747        {
748          normal[0] = '\0';
749          normal ++;
750          //glArrayElement(atoi(vertex)-1);
751          glNormal3fv(normals->getArray() +(atoi(normal)-1)*3);
752        }
753    }
754  glVertex3fv(vertices->getArray() +(atoi(vertex)-1)*3);
755
756}
757
758
759/**
760   \brief Includes a default object
761   This will inject a Cube, because this is the most basic object.
762*/
763void Object::BoxObject(void)
764{
765  readVertex ("-0.500000 -0.500000 0.500000");
766  readVertex ("0.500000 -0.500000 0.500000");
767  readVertex ("-0.500000 0.500000 0.500000");
768  readVertex ("0.500000 0.500000 0.500000");
769  readVertex ("-0.500000 0.500000 -0.500000");
770  readVertex ("0.500000 0.500000 -0.500000");
771  readVertex ("-0.500000 -0.500000 -0.500000");
772  readVertex ("0.500000 -0.500000 -0.500000");
773
774  readVertexTexture ("0.000000 0.000000");
775  readVertexTexture ("1.000000 0.000000");
776  readVertexTexture ("0.000000 1.000000");
777  readVertexTexture ("1.000000 1.000000");
778  readVertexTexture ("0.000000 2.000000");
779  readVertexTexture ("1.000000 2.000000");
780  readVertexTexture ("0.000000 3.000000");
781  readVertexTexture ("1.000000 3.000000");
782  readVertexTexture ("0.000000 4.000000");
783  readVertexTexture ("1.000000 4.000000");
784  readVertexTexture ("2.000000 0.000000");
785  readVertexTexture ("2.000000 1.000000");
786  readVertexTexture ("-1.000000 0.000000");
787  readVertexTexture ("-1.000000 1.000000");
788 
789  readVertexNormal ("0.000000 0.000000 1.000000");
790  readVertexNormal ("0.000000 0.000000 1.000000");
791  readVertexNormal ("0.000000 0.000000 1.000000");
792  readVertexNormal ("0.000000 0.000000 1.000000");
793  readVertexNormal ("0.000000 1.000000 0.000000");
794  readVertexNormal ("0.000000 1.000000 0.000000");
795  readVertexNormal ("0.000000 1.000000 0.000000");
796  readVertexNormal ("0.000000 1.000000 0.000000");
797  readVertexNormal ("0.000000 0.000000 -1.000000");
798  readVertexNormal ("0.000000 0.000000 -1.000000");
799  readVertexNormal ("0.000000 0.000000 -1.000000");
800  readVertexNormal ("0.000000 0.000000 -1.000000");
801  readVertexNormal ("0.000000 -1.000000 0.000000");
802  readVertexNormal ("0.000000 -1.000000 0.000000");
803  readVertexNormal ("0.000000 -1.000000 0.000000");
804  readVertexNormal ("0.000000 -1.000000 0.000000");
805  readVertexNormal ("1.000000 0.000000 0.000000");
806  readVertexNormal ("1.000000 0.000000 0.000000");
807  readVertexNormal ("1.000000 0.000000 0.000000");
808  readVertexNormal ("1.000000 0.000000 0.000000");
809  readVertexNormal ("-1.000000 0.000000 0.000000");
810  readVertexNormal ("-1.000000 0.000000 0.000000");
811  readVertexNormal ("-1.000000 0.000000 0.000000");
812  readVertexNormal ("-1.000000 0.000000 0.000000");
813
814  readFace ("1/1/1 2/2/2 4/4/3 3/3/4");
815  readFace ("3/3/5 4/4/6 6/6/7 5/5/8");
816  readFace ("5/5/9 6/6/10 8/8/11 7/7/12");
817  readFace ("7/7/13 8/8/14 2/10/15 1/9/16");
818  readFace ("2/2/17 8/11/18 6/12/19 4/4/20");
819  readFace ("7/13/21 1/1/22 3/3/23 5/14/24");
820}
Note: See TracBrowser for help on using the repository browser.