Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/orxonox/branches/nico/src/importer/model.cc @ 3527

Last change on this file since 3527 was 3399, checked in by bensch, 20 years ago

orxonox/branches/nico: merged trunk into branches/nico
merged with command:
svn merge ../trunk/ nico/ -r 3238:HEAD
resolved conflict in favor of branches/nico.

File size: 19.9 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 "model.h"
17
18using namespace std;
19
20/**
21   \brief Creates a 3D-Model.
22
23   This only initializes a 3D-Model, but does not cleanup the Faces.
24*/
25Model::Model(void)
26{
27  this->initialize();
28}
29
30/**
31   \brief Creates a 3D-Model of Primitive-Type type
32
33   if you want to just display a Cube/Sphere/Cylinder/... without any material.
34   
35   \todo implement Cube/Sphere/Cylinder/...
36*/
37Model::Model(PRIMITIVE type)
38{
39  this->initialize();
40
41  if (type == CUBE)
42    this->BoxModel();
43
44  this->importToGL ();
45
46  this->cleanup();
47}
48
49/**
50   \brief Creates a 3D-Model. and assigns it a Name.
51*/
52Model::Model(char* modelName)
53{
54  this->initialize();
55  this->setName(modelName);
56}
57
58/**
59   \brief deletes an Model.
60
61   Looks if any from model allocated space is still in use, and if so deleted it.
62*/
63Model::~Model(void)
64{
65  PRINTF(3)("Deleting Model ");
66  if (this->name)
67    {
68      PRINT(3)("%s\n", this->name);
69      delete []this->name;
70    }
71  else
72      PRINT(3)("\n");
73
74  PRINTF(3)("Deleting display Lists.\n");
75  Group* walker = this->firstGroup;
76  while (walker != NULL)
77    {
78      glDeleteLists (walker->listNumber, 1);
79      Group* delWalker = walker;
80      walker = walker->next;
81      delete delWalker;
82    }
83
84  PRINTF(3)("Deleting Materials.\n");
85  if (this->material)
86    delete this->material;
87}
88
89/**
90   \brief Finalizes an Object. This can be done outside of the Class.
91*/
92void Model::finalize(void)
93{
94  this->importToGL ();
95 
96  this->cleanup();
97
98  this->finalized = true;
99}
100
101/**
102   \brief Draws the Models of all Groups.
103   It does this by just calling the Lists that must have been created earlier.
104*/
105void Model::draw (void) const
106{
107  PRINTF(2)("drawing the 3D-Models\n"); 
108  Group* walker = this->firstGroup;
109  while (walker != NULL)
110    {
111      PRINTF(3)("Drawing model %s\n", walker->name);
112      glCallList (walker->listNumber);
113      walker = walker->next;
114    }
115}
116
117/**
118   \brief Draws the Model number groupNumber
119   \param groupNumber The number of the group that will be displayed.
120
121   It does this by just calling the List that must have been created earlier.
122*/
123void Model::draw (int groupNumber) const 
124{
125  if (groupNumber >= this->groupCount)
126    {
127      PRINTF(1)("You requested model number %i, but this File only contains of %i Models.\n", groupNumber-1, this->groupCount);
128      return;
129    }
130  PRINTF(2)("drawing the requested 3D-Models if found.\n"); 
131  Group* walker = this->firstGroup;
132  int counter = 0;
133  while (walker != NULL)
134    {
135      if (counter == groupNumber)
136        {
137          PRINTF(2)("Drawing model number %i named %s\n", counter, walker->name);
138          glCallList (walker->listNumber);
139          return;
140        }
141      ++counter;
142      walker = walker->next;
143    }
144  PRINTF(1)("Model number %i in %s not Found.\n", groupNumber, this->name);
145  return;
146
147}
148
149/**
150   \brief Draws the Model with a specific groupName
151   \param groupName The name of the group that will be displayed.
152
153   It does this by just calling the List that must have been created earlier.
154*/
155void Model::draw (char* groupName) const
156{
157  PRINTF(2)("drawing the requested 3D-Models if found.\n"); 
158  Group* walker = this->firstGroup;
159  while (walker != NULL)
160    {
161      if (!strcmp(walker->name, groupName))
162        {
163          PRINTF(2)("Drawing model %s\n", walker->name);
164          glCallList (walker->listNumber);
165          return;
166        }
167      walker = walker->next;
168    }
169  PRINTF(1)("Model Named %s in %s not Found.\n", groupName, this->name);
170  return;
171}
172
173/**
174   \returns Count of the Models in this File
175*/
176int Model::getGroupCount (void) const
177{
178  return this->groupCount;
179}
180
181/**
182    \brief initializes the Model.
183
184    This Function initializes all the needed arrays, Lists and clientStates.
185    It also defines default values.
186*/
187bool Model::initialize (void)
188{
189  PRINTF(2)("new 3D-Model is being created\n"); 
190
191  this->name = NULL;
192  this->finalized = false;
193  // setting the start group;
194  this->firstGroup = new Group;
195  this->currentGroup = this->firstGroup;
196  this->groupCount = 0;
197 
198  this->initGroup (this->currentGroup);
199  this->scaleFactor = 1;
200  this->material = new Material();
201
202  this->vertices = new Array();
203  this->vTexture = new Array();
204  this->normals = new Array();
205
206  return true;
207}
208
209void Model::setName(const char* name)
210{
211  if (this->name) 
212    delete this->name;
213  this->name = new char[strlen(name)+1];
214  strcpy(this->name, name);
215}
216/**
217   \brief initializes a new Group model
218   \param group the group that should be initialized.
219   \todo Maybe Group should be a Class, because it does a lot of stuff
220   
221*/
222bool Model::initGroup(Group* group)
223{
224  PRINTF(3)("Adding new Group\n");
225  group->name = "";
226  group->faceMode = -1;
227  group->faceCount = 0; 
228  group->next = NULL;
229
230  group->firstFace = new Face;
231  this->initFace (group->firstFace);
232  group->currentFace = group->firstFace;
233}
234
235/**
236   \brief initializes a new Face. (sets default Values)
237   \param face The face to initialize
238*/
239bool Model::initFace (Face* face)
240{
241  face->vertexCount = 0;
242
243  face->firstElem = NULL;
244 
245  face->materialString = NULL;
246 
247  face->next = NULL;
248
249  return true;
250}
251
252/**
253   \brief finalizes an Model.
254   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.
255*/
256bool Model::cleanup(void)
257{
258  PRINTF(3)("cleaning up the 3D-Model to save Memory.\n");
259
260  if (this->vertices)
261    delete this->vertices;
262  if (this->vTexture)
263    delete this->vTexture;
264  if (this->normals)
265    delete this->normals;
266
267  this->cleanupGroup(this->firstGroup);
268  return true; 
269}
270
271/**
272   \brief Cleans up all groups starting from group.
273   \param group the first Group to clean
274*/
275bool Model::cleanupGroup (Group* group)
276{
277  PRINTF(3)("Cleaning up group\n");
278  if (group->firstFace != NULL)
279    {
280      cleanupFace (group->firstFace);
281      delete group->firstFace;
282    }
283
284  if (group->next !=NULL)
285    cleanupGroup (group->next);
286  return true;
287}
288
289/**
290   \brief Cleans up all Faces starting from face until NULL is reached.
291   \param face the first face to clean.
292*/
293bool Model::cleanupFace (Face* face)
294{
295  PRINTF(3)("Cleaning up Face\n");
296
297  if (face->materialString != NULL)
298      delete []face->materialString;
299  if (face->firstElem != NULL)
300    {
301      this->cleanupFaceElement(face->firstElem);
302      delete face->firstElem;
303    }
304     
305  if (face->next != NULL)
306    {
307      this->cleanupFace (face->next);
308      delete face->next;
309    }
310     
311}
312
313
314/**
315   \brief Cleans up all FaceElements starting from faceElem.
316   \param faceElem the first FaceElement to clean.
317*/
318bool Model::cleanupFaceElement(FaceElement* faceElem)
319{
320  if (faceElem->next != NULL)
321    {
322      this->cleanupFaceElement (faceElem->next);
323      delete faceElem->next;
324    }
325}
326
327/**
328   \brief parses a group String
329   \param groupString the new Group to create
330
331   This function initializes a new Group.
332   With it you should be able to import .obj-files with more than one Models inside.
333*/
334bool Model::addGroup (char* groupString)
335{
336  PRINTF(3)("Read Group: %s.\n", groupString);
337  if (this->groupCount != 0 && this->currentGroup->faceCount>0)
338    {
339      //      finalizeGroup(currentGroup);
340      this->currentGroup = this->currentGroup->next = new Group;
341      this->initGroup(this->currentGroup);
342    }
343  // setting the group name if not default.
344  if (strcmp(groupString, "default"))
345    {
346      this->currentGroup->name = new char [strlen(groupString)+1];
347      strcpy(this->currentGroup->name, groupString);
348    }
349  ++this->groupCount;
350
351}
352
353/**
354   \brief parses a vertex-String
355   \param vertexString The String that will be parsed.
356
357   If a vertex line is found this function will inject it into the vertex-Array
358*/
359bool Model::addVertex (char* vertexString)
360{
361  float subbuffer1;
362  float subbuffer2;
363  float subbuffer3;
364  sscanf (vertexString, "%f %f %f", &subbuffer1, &subbuffer2, &subbuffer3);
365  PRINTF(3)("reading in a vertex: %f %f %f\n", &subbuffer1, &subbuffer2, &subbuffer3);
366  this->vertices->addEntry(subbuffer1*scaleFactor, subbuffer2*scaleFactor, subbuffer3*scaleFactor);
367  return true;
368}
369
370/**
371   \brief parses a face-string
372   \param faceString The String that will be parsed.
373
374   If a face line is found this function will add it to the glList.
375   The function makes a difference between QUADS and TRIANGLES, and will if changed re-open, set and re-close the gl-processe.
376*/
377bool Model::addFace (char* faceString)
378{
379  if (this->currentGroup->faceCount >0)
380    this->currentGroup->currentFace = this->currentGroup->currentFace->next = new Face;
381  this->initFace (this->currentGroup->currentFace);
382
383  FaceElement* tmpElem = this->currentGroup->currentFace->firstElem = new FaceElement;
384  tmpElem->next = NULL;
385  while(strcmp (faceString, "\0"))
386    {
387      if (this->currentGroup->currentFace->vertexCount>0)
388          tmpElem = tmpElem->next = new FaceElement;
389      tmpElem->next = NULL;
390
391      char tmpValue [50];
392      int tmpLen;
393      char* vertex = NULL;
394      char* texture = NULL;
395      char* normal = NULL;
396
397      sscanf (faceString, "%s", tmpValue);
398      tmpLen = strlen(tmpValue);
399      vertex = tmpValue;
400
401      if ((texture = strstr (vertex, "/")) != NULL)
402        {
403          texture[0] = '\0';
404          texture ++;
405         
406          if ((normal = strstr (texture, "/")) !=NULL)
407            {
408              normal[0] = '\0';
409              normal ++;
410            }     
411        }
412      if (vertex)
413        tmpElem->vertexNumber = atoi(vertex)-1;
414      else
415        tmpElem->vertexNumber = -1;
416      if (texture)
417        tmpElem->texCoordNumber = atoi(texture)-1;
418      else
419        tmpElem->texCoordNumber = -1;
420      if (normal)
421        tmpElem->normalNumber = atoi(normal)-1;
422      else
423        tmpElem->normalNumber = -1;
424
425      faceString += tmpLen;
426      if (strcmp (faceString, "\0"))
427        faceString++;
428      this->currentGroup->currentFace->vertexCount++;
429    }
430
431  this->currentGroup->faceCount += this->currentGroup->currentFace->vertexCount -2;
432}
433
434/**
435   \brief parses a vertexNormal-String
436   \param normalString The String that will be parsed.
437
438   If a vertexNormal line is found this function will inject it into the vertexNormal-Array
439*/
440bool Model::addVertexNormal (char* normalString)
441{
442  float subbuffer1;
443  float subbuffer2;
444  float subbuffer3;
445  sscanf (normalString, "%f %f %f", &subbuffer1, &subbuffer2, &subbuffer3);
446  PRINTF(3)("found vertex-Normal %f, %f, %f\n", &subbuffer1,&subbuffer2,&subbuffer3);
447  this->normals->addEntry(subbuffer1, subbuffer2, subbuffer3);
448  return true;
449}
450
451/**
452   \brief parses a vertexTextureCoordinate-String
453   \param vTextureString The String that will be parsed.
454
455   If a vertexTextureCoordinate line is found,
456   this function will inject it into the vertexTexture-Array
457*/
458bool Model::addVertexTexture (char* vTextureString)
459{
460  float subbuffer1;
461  float subbuffer2;
462  sscanf (vTextureString, "%f %f", &subbuffer1, &subbuffer2);
463  PRINTF(3)("found vertex-Texture %f, %f\n", &subbuffer1, &subbuffer2);
464  this->vTexture->addEntry(subbuffer1);
465  this->vTexture->addEntry(subbuffer2);
466  return true;
467}
468
469/**
470   \brief Function that selects a material, if changed in the obj file.
471   \param matString the Material that will be set.
472*/
473bool Model::addUseMtl (char* matString)
474{
475  /*
476  if (!this->mtlFileName)
477    {
478      PRINTF(4)("Not using new defined material, because no mtlFile found yet\n");
479      return false;
480    }
481  */     
482  if (this->currentGroup->faceCount >0)
483    this->currentGroup->currentFace = this->currentGroup->currentFace->next = new Face;
484  this->initFace (this->currentGroup->currentFace);
485 
486  this->currentGroup->currentFace->materialString = new char[strlen(matString)+1];
487  strcpy (this->currentGroup->currentFace->materialString, matString);
488 
489  if (this->currentGroup->faceCount == 0)
490    this->currentGroup->faceCount ++;
491
492}
493
494/**
495   \brief reads and includes the Faces/Materials into the openGL state Machine
496*/
497bool Model::importToGL (void)
498{
499
500  // finalize the Arrays
501  this->vertices->finalizeArray();
502  this->vTexture->finalizeArray();
503  if (normals->getCount() == 0) // vertices-Array must be uilt for this
504    this->buildVertexNormals();
505  this->normals->finalizeArray();
506
507  this->currentGroup = this->firstGroup;
508
509  while (this->currentGroup != NULL)
510    {
511
512      // creating a glList for the Group
513      if ((this->currentGroup->listNumber = glGenLists(1)) == 0)
514        {
515          PRINTF(1)("list could not be created for this Model\n");
516          return false;
517        }
518      glNewList (this->currentGroup->listNumber, GL_COMPILE);
519
520      // Putting Faces to GL
521      Face* tmpFace = this->currentGroup->firstFace;
522      while (tmpFace != NULL)
523        {
524          if (tmpFace->vertexCount == 0 && tmpFace->materialString != NULL)
525            {
526              if (this->currentGroup->faceMode != -1)
527                glEnd();
528              this->currentGroup->faceMode = 0;
529              Material* tmpMat;
530              if ((tmpMat = material->search(tmpFace->materialString)) != NULL)
531                {
532                  tmpMat->select();
533                  PRINTF(2)("using material %s for coming Faces.\n", tmpFace->materialString);
534                }
535              else 
536                PRINTF(1)("material %s not found.\n", tmpFace->materialString);
537
538
539            }
540
541          else if (tmpFace->vertexCount == 3)
542            {
543              if (this->currentGroup->faceMode != 3)
544                {
545                  if (this->currentGroup->faceMode != -1)
546                    glEnd();
547                  glBegin(GL_TRIANGLES);
548                }
549             
550              this->currentGroup->faceMode = 3;
551              PRINTF(3)("found triag.\n");
552            }
553         
554          else if (tmpFace->vertexCount == 4)
555            {
556              if (this->currentGroup->faceMode != 4)
557                {
558                  if (this->currentGroup->faceMode != -1)
559                    glEnd();
560                  glBegin(GL_QUADS);
561                }
562              this->currentGroup->faceMode = 4;
563              PRINTF(3)("found quad.\n");
564            }
565         
566          else if (tmpFace->vertexCount > 4)
567            {
568              if (this->currentGroup->faceMode != -1)
569                glEnd();
570              glBegin(GL_POLYGON);
571              PRINTF(3)("Polygon with %i faces found.", tmpFace->vertexCount);
572              this->currentGroup->faceMode = tmpFace->vertexCount;
573            }
574         
575          FaceElement* tmpElem = tmpFace->firstElem;
576          while (tmpElem != NULL)
577            {
578              //      PRINTF(2)("%s\n", tmpElem->value);
579              this->addGLElement(tmpElem);
580              tmpElem = tmpElem->next;
581            }
582          tmpFace = tmpFace->next;
583        }
584      glEnd();
585      glEndList();
586
587      this->currentGroup = this->currentGroup->next;
588    } 
589}
590
591/**
592   \brief Adds a Face-element (one vertex of a face) with all its information.
593   \param elem The FaceElement to add to the OpenGL-environment.
594
595   It does this by searching:
596   1. The Vertex itself
597   2. The VertexNormale
598   3. The VertexTextureCoordinate
599   merging this information, the face will be drawn.
600*/
601bool Model::addGLElement (FaceElement* elem)
602{
603  PRINTF(3)("importing grafical Element to openGL.\n");
604
605  if (elem->texCoordNumber != -1)
606    glTexCoord2fv(this->vTexture->getArray() + elem->texCoordNumber * 2);
607  if (elem->normalNumber != -1)
608    glNormal3fv(this->normals->getArray() + elem->normalNumber * 3);
609  if (elem->vertexNumber != -1)
610    glVertex3fv(this->vertices->getArray() + elem->vertexNumber * 3);
611
612}
613
614/**
615   \brief A routine that is able to create normals.
616
617   The algorithm does the following:
618   1. It calculates creates Vectors for each normale, and sets them to zero.
619   2. It then Walks through a) all the Groups b) all the Faces c) all the FaceElements
620   3. It searches for a points two neighbours per Face, takes Vecotrs to them calculates FaceNormals and adds it to the Points Normal.
621   4. It goes through all the normale-Points and calculates the VertexNormale and includes it in the normals-Array.
622*/
623bool Model::buildVertexNormals ()
624{
625 
626  PRINTF(2)("Normals are being calculated.\n");
627
628  Vector* normArray = new Vector [vertices->getCount()/3];
629  for (int i=0; i<vertices->getCount()/3;i++)
630    normArray[i] = Vector(.0,.0,.0);
631 
632  int firstTouch;
633  int secondTouch;
634  Vector prevV;
635  Vector nextV;
636  Vector curV;
637
638  Group* tmpGroup = firstGroup;
639  while (tmpGroup)
640    {
641      Face* tmpFace = tmpGroup->firstFace;
642      while (tmpFace)
643        {
644          if (tmpFace->firstElem)
645            {
646              FaceElement* firstElem = tmpFace->firstElem;
647              FaceElement* prevElem;
648              FaceElement* curElem = firstElem;
649              FaceElement* nextElem;
650              FaceElement* lastElem;
651              // find last Element of the Chain. !! IMPORTANT:the last Element of the Chain must point to NULL, or it will resolv into an infinity-loop.
652              while (curElem)
653                {
654                  prevElem = curElem;
655                  curElem = curElem->next;
656                }
657              lastElem = prevElem;
658             
659              curElem = firstElem;
660              for (int j=0; j<tmpFace->vertexCount; j++)
661                {
662                  if (!(nextElem = curElem->next))
663                    nextElem = firstElem;
664                  curElem->normalNumber = curElem->vertexNumber;
665                 
666                  curV = Vector (vertices->getArray()[curElem->vertexNumber*3], vertices->getArray()[curElem->vertexNumber*3+1], vertices->getArray()[curElem->vertexNumber*3+2]);
667                  prevV = Vector (vertices->getArray()[prevElem->vertexNumber*3], vertices->getArray()[prevElem->vertexNumber*3+1], vertices->getArray()[prevElem->vertexNumber*3+2]) - curV;
668                  nextV = Vector (vertices->getArray()[nextElem->vertexNumber*3], vertices->getArray()[nextElem->vertexNumber*3+1], vertices->getArray()[nextElem->vertexNumber*3+2]) - curV;
669                  normArray[curElem->vertexNumber] = normArray[curElem->vertexNumber] + nextV.cross(prevV);
670
671                  prevElem = curElem;
672                  curElem = curElem->next;
673                }
674            }
675          tmpFace = tmpFace->next;
676        }
677      tmpGroup = tmpGroup->next;
678    }
679
680  for (int i=0; i<vertices->getCount()/3;i++)
681    {
682      normArray[i].normalize();
683      PRINTF(3)("Found Normale number %d: (%f; %f, %f).\n", i, normArray[i].x, normArray[i].y, normArray[i].z);
684
685      this->normals->addEntry(normArray[i].x, normArray[i].y, normArray[i].z);
686
687    }
688  delete []normArray; 
689 
690}
691
692
693/**
694   \brief Includes a default model
695
696   This will inject a Cube, because this is the most basic model.
697*/
698void Model::BoxModel(void)
699{
700  this->addVertex ("-0.5 -0.5 0.5");
701  this->addVertex ("0.5 -0.5 0.5");
702  this->addVertex ("-0.5 0.5 0.5");
703  this->addVertex ("0.5 0.5 0.5");
704  this->addVertex ("-0.5 0.5 -0.5");
705  this->addVertex ("0.5 0.5 -0.5");
706  this->addVertex ("-0.5 -0.5 -0.5");
707  this->addVertex ("0.5 -0.5 -0.5");
708
709  this->addVertexTexture ("0.0 0.0");
710  this->addVertexTexture ("1.0 0.0");
711  this->addVertexTexture ("0.0 1.0");
712  this->addVertexTexture ("1.0 1.0");
713  this->addVertexTexture ("0.0 2.0");
714  this->addVertexTexture ("1.0 2.0");
715  this->addVertexTexture ("0.0 3.0");
716  this->addVertexTexture ("1.0 3.0");
717  this->addVertexTexture ("0.0 4.0");
718  this->addVertexTexture ("1.0 4.0");
719  this->addVertexTexture ("2.0 0.0");
720  this->addVertexTexture ("2.0 1.0");
721  this->addVertexTexture ("-1.0 0.0");
722  this->addVertexTexture ("-1.0 1.0");
723
724  this->addVertexNormal ("0.0 0.0 1.0");
725  this->addVertexNormal ("0.0 0.0 1.0");
726  this->addVertexNormal ("0.0 0.0 1.0");
727  this->addVertexNormal ("0.0 0.0 1.0");
728  this->addVertexNormal ("0.0 1.0 0.0");
729  this->addVertexNormal ("0.0 1.0 0.0");
730  this->addVertexNormal ("0.0 1.0 0.0");
731  this->addVertexNormal ("0.0 1.0 0.0");
732  this->addVertexNormal ("0.0 0.0 -1.0");
733  this->addVertexNormal ("0.0 0.0 -1.0");
734  this->addVertexNormal ("0.0 0.0 -1.0");
735  this->addVertexNormal ("0.0 0.0 -1.0");
736  this->addVertexNormal ("0.0 -1.0 0.0");
737  this->addVertexNormal ("0.0 -1.0 0.0");
738  this->addVertexNormal ("0.0 -1.0 0.0");
739  this->addVertexNormal ("0.0 -1.0 0.0");
740  this->addVertexNormal ("1.0 0.0 0.0");
741  this->addVertexNormal ("1.0 0.0 0.0");
742  this->addVertexNormal ("1.0 0.0 0.0");
743  this->addVertexNormal ("1.0 0.0 0.0");
744  this->addVertexNormal ("-1.0 0.0 0.0");
745  this->addVertexNormal ("-1.0 0.0 0.0");
746  this->addVertexNormal ("-1.0 0.0 0.0");
747  this->addVertexNormal ("-1.0 0.0 0.0");
748
749  /* normaleLess-testingMode
750  this->addFace ("1 2 4 3");
751  this->addFace ("3 4 6 5");
752  this->addFace ("5 6 8 7");
753  this->addFace ("7 8 2 1");
754  this->addFace ("2 8 6 4");
755  this->addFace ("7 1 3 5");
756  */
757
758  this->addFace ("1/1/1 2/2/2 4/4/3 3/3/4");
759  this->addFace ("3/3/5 4/4/6 6/6/7 5/5/8");
760  this->addFace ("5/5/9 6/6/10 8/8/11 7/7/12");
761  this->addFace ("7/7/13 8/8/14 2/10/15 1/9/16");
762  this->addFace ("2/2/17 8/11/18 6/12/19 4/4/20");
763  this->addFace ("7/13/21 1/1/22 3/3/23 5/14/24");
764
765}
Note: See TracBrowser for help on using the repository browser.