1 // 30 Oct 2002
2 // AC3D loader for models generated by the AC3D modeller (www.ac3d.org)
3 // part of this source code were supplied by the AC3D project (Andy Colebourne)
4 // eg the basic parsing of an AC3D file.
5 // Conversion from AC3D scenegraph to OSG by GW Michel.
6 
7 #include <vector>
8 #include <iostream>
9 #include <limits>
10 #include <stdlib.h>
11 
12 #include <osg/GL>
13 #include <osg/GLU>
14 
15 #include <osg/Math>
16 #include <osg/BlendFunc>
17 #include <osg/CullFace>
18 #include <osg/Geode>
19 #include <osg/Group>
20 #include <osg/Geometry>
21 #include <osg/Light>
22 #include <osg/LightSource>
23 #include <osg/Material>
24 #include <osg/Math>
25 #include <osg/Texture2D>
26 #include <osg/TexEnv>
27 #include <osg/StateSet>
28 #include <osg/ShadeModel>
29 #include <osg/Math>
30 #include <osg/Notify>
31 
32 #include <osgUtil/Tessellator>
33 
34 #include <osgDB/FileNameUtils>
35 #include <osgDB/Registry>
36 #include <osgDB/ReadFile>
37 #include <osgDB/FileUtils>
38 #include <osgDB/fstream>
39 
40 #include "Exception.h"
41 #include "Geode.h"
42 
43 namespace ac3d {
44 
45 osg::Node*
46 readFile(std::istream& stream, const osgDB::ReaderWriter::Options* options);
47 
48 }
49 
50 class geodeVisitor : public osg::NodeVisitor { // collects geodes from scene sub-graph attached to 'this'
51         public:
geodeVisitor()52             geodeVisitor():
53                 osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN) {}
54 
~geodeVisitor()55             ~geodeVisitor() { _geodelist.clear();}
56 
57             // one apply for each type of Node that might be a user transform
apply(osg::Geode & geode)58             virtual void apply(osg::Geode& geode) {
59                 _geodelist.push_back(&geode);
60             }
apply(osg::Group & gp)61             virtual void apply(osg::Group& gp){
62                 traverse(gp);    // must continue subgraph traversal.
63             }
getGeodes()64             std::vector<const osg::Geode *> getGeodes() {return _geodelist;}
65         protected:
66 
67             typedef std::vector<const osg::Geode *>    Geodelist;
68             Geodelist  _geodelist;
69 };
70 
71 class ReaderWriterAC : public osgDB::ReaderWriter
72 {
73     public:
74 
ReaderWriterAC()75         ReaderWriterAC()
76         {
77             supportsExtension("ac","AC3D Database format");
78         }
79 
className() const80         virtual const char* className() const { return "AC3D Database Reader"; }
readObject(const std::string & fileName,const osgDB::ReaderWriter::Options * options) const81         virtual ReadResult readObject(const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
82         {
83             return readNode(fileName, options);
84         }
readNode(const std::string & file,const Options * options) const85         virtual ReadResult readNode(const std::string& file,const Options* options) const
86         {
87             std::string ext = osgDB::getFileExtension(file);
88             if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
89 
90             // GWM added Dec 2003 - get full path name (change in osgDB handling of files).
91             std::string fileName = osgDB::findDataFile( file, options );
92             OSG_INFO << "osgDB ac3d reader: starting reading \"" << fileName << "\"" << std::endl;
93 
94             // Anders Backmann - correct return if path not found
95             if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
96 
97             // allocate per file data and start reading
98             osgDB::ifstream fin;
99             fin.open(fileName.c_str(), std::ios::in);
100             if (!fin.is_open()) return ReadResult::FILE_NOT_FOUND;
101 
102             // code for setting up the database path so that internally referenced file are
103             // searched for on relative paths.
104             osg::ref_ptr<Options> local_opt;
105             if (options)
106                 local_opt = static_cast<Options*>(options->clone(osg::CopyOp::DEEP_COPY_ALL));
107             else
108                 local_opt = new Options;
109             local_opt->getDatabasePathList().push_back(osgDB::getFilePath(fileName));
110 
111             ReadResult result = readNode(fin, local_opt.get());
112             if (result.validNode())
113                 result.getNode()->setName(fileName);
114             return result;
115         }
readObject(std::istream & fin,const Options * options) const116         virtual ReadResult readObject(std::istream& fin, const Options* options) const
117         {
118             return readNode(fin, options);
119         }
readNode(std::istream & fin,const Options * options) const120         virtual ReadResult readNode(std::istream& fin, const Options* options) const
121         {
122             std::string header;
123             fin >> header;
124             if (header.substr(0, 4) != "AC3D")
125                 return osgDB::ReaderWriter::ReadResult::FILE_NOT_HANDLED;
126 
127             return ac3d::readFile(fin, options);
128         }
writeNode(const osg::Node & node,const std::string & fileName,const Options *) const129         virtual WriteResult writeNode(const osg::Node& node,const std::string& fileName, const Options* /*options*/) const
130         {
131             std::string ext = osgDB::getFileExtension(fileName);
132             if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
133             geodeVisitor vs; // this collects geodes.
134             std::vector<unsigned int>iNumMaterials;
135             const_cast<osg::Node&>(node).accept(vs); // this parses the tree to streamd Geodes
136             std::vector<const osg::Geode *> glist=vs.getGeodes();
137             osgDB::ofstream fout(fileName.c_str(), std::ios::out | std::ios::binary);
138             // Write out the file header
139             std::vector<const osg::Geode *>::iterator itr;
140             fout << "AC3Db" << std::endl;
141             // output the Materials
142             int iNumGeodesWithGeometry = 0;
143             for (itr=glist.begin();itr!= glist.end();itr++)
144             {
145                 iNumMaterials.push_back(const_cast<ac3d::Geode*>(static_cast<const ac3d::Geode*>(*itr))->ProcessMaterial(fout,itr-glist.begin()));
146                 unsigned int iNumDrawables = (*itr)->getNumDrawables();
147                 int iNumGeometries = 0;
148                 for (unsigned int i = 0; i < iNumDrawables; i++)
149                 {
150                     const osg::Drawable* pDrawable = (*itr)->getDrawable(i);
151                     if (NULL != pDrawable)
152                     {
153                         const osg::Geometry *pGeometry = pDrawable->asGeometry();
154                         if (NULL != pGeometry)
155                             iNumGeometries++;
156                     }
157                 }
158                 if (iNumGeometries > 0)
159                     iNumGeodesWithGeometry++;
160             }
161             // output the Geometry
162             unsigned int nfirstmat=0;
163             fout << "OBJECT world" << std::endl;
164 
165             fout << "kids " << iNumGeodesWithGeometry << std::endl;
166             for (itr=glist.begin();itr!= glist.end();itr++) {
167                 const_cast<ac3d::Geode*>(static_cast<const ac3d::Geode*>(*itr))->ProcessGeometry(fout,nfirstmat);
168                 nfirstmat+=iNumMaterials[itr-glist.begin()];
169             }
170             fout.close();
171             return WriteResult::FILE_SAVED;
172         }
173 
writeNode(const osg::Node & node,std::ostream & fout,const Options * opts) const174         virtual WriteResult writeNode(const osg::Node& node,std::ostream& fout, const Options* opts) const
175         {
176             // write ac file.
177 
178             const osg::Group *gp=node.asGroup();
179             if(gp)
180             {
181                 const unsigned int nch=gp->getNumChildren();
182                 for (unsigned int i=0; i<nch; i++)
183                 {
184                     writeNode(*(gp->getChild(i)), fout, opts);
185                 }
186             }
187             else
188                 OSG_WARN<<"File must start with a geode "<<std::endl;
189 
190             fout.flush();
191             return WriteResult::FILE_SAVED;
192         }
193 private:
194 };
195 
196 // now register with osg::Registry to instantiate the above
197 // reader/writer.
198 REGISTER_OSGPLUGIN(ac, ReaderWriterAC)
199 
200 
201 namespace ac3d {
202 
203 enum {
204   ObjectTypeNormal = 0,
205   ObjectTypeGroup = 1,
206   ObjectTypeLight = 2,
207 
208   SurfaceTypePolygon = 0,
209   SurfaceTypeLineLoop = 1,
210   SurfaceTypeLineStrip = 2,
211 
212   SurfaceShaded = 1<<4,
213   SurfaceTwoSided = 1<<5
214 };
215 
216 /// Returns a possibly quoted string given in the end of the current line in the stream
217 static
218 std::string
readString(std::istream & stream)219 readString(std::istream& stream)
220 {
221     std::string s;
222     stream >> std::ws;
223 
224     if (stream.peek() != '\"')
225     {
226         // Not quoted, just read the string
227         stream >> s;
228     }
229     else
230     {
231         // look for quoted strings
232 
233         // throw away the quote
234         stream.get();
235 
236         // extract characters until either an error happens or a quote is found
237         while (stream.good())
238         {
239             std::istream::char_type c;
240             stream.get(c);
241             if (c == '\"')
242                 break;
243             s += c;
244         }
245     }
246 
247     return s;
248 }
249 
250 static void
setTranslucent(osg::StateSet * stateSet)251 setTranslucent(osg::StateSet* stateSet)
252 {
253     osg::BlendFunc* blendFunc = new osg::BlendFunc;
254     blendFunc->setDataVariance(osg::Object::STATIC);
255     blendFunc->setSource(osg::BlendFunc::SRC_ALPHA);
256     blendFunc->setDestination(osg::BlendFunc::ONE_MINUS_SRC_ALPHA);
257     stateSet->setAttribute(blendFunc);
258     stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
259     stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
260 }
261 
262 // Just a container to store an ac3d material
263 class MaterialData
264 {
265   public:
MaterialData()266     MaterialData() :
267         mMaterial(new osg::Material),
268         mColorArray(new osg::Vec4Array(1)),
269         mTranslucent(false)
270     {
271         mMaterial->setDataVariance(osg::Object::STATIC);
272         mColorArray->setDataVariance(osg::Object::STATIC);
273     }
274 
readMaterial(std::istream & stream)275     void readMaterial(std::istream& stream)
276     {
277         // note that this might be quoted
278         std::string name = readString(stream);
279         mMaterial->setName(name);
280         std::string tmp;
281         stream >> tmp;
282         osg::Vec4 diffuse;
283         stream >> diffuse[0] >> diffuse[1] >> diffuse[2];
284         mMaterial->setDiffuse(osg::Material::FRONT_AND_BACK, diffuse);
285         stream >> tmp;
286         osg::Vec4 ambient;
287         stream >> ambient[0] >> ambient[1] >> ambient[2];
288         mMaterial->setAmbient(osg::Material::FRONT_AND_BACK, ambient);
289         stream >> tmp;
290         osg::Vec4 emmissive;
291         stream >> emmissive[0] >> emmissive[1] >> emmissive[2];
292         mMaterial->setEmission(osg::Material::FRONT_AND_BACK, emmissive);
293         stream >> tmp;
294         osg::Vec4 specular;
295         stream >> specular[0] >> specular[1] >> specular[2];
296         mMaterial->setSpecular(osg::Material::FRONT_AND_BACK, specular);
297         stream >> tmp;
298         float shininess;
299         stream >> shininess;
300         mMaterial->setShininess(osg::Material::FRONT_AND_BACK, shininess);
301         stream >> tmp;
302         float transparency;
303         stream >> transparency;
304         mMaterial->setTransparency(osg::Material::FRONT_AND_BACK, transparency);
305         mTranslucent = 0 < transparency;
306 
307         // must correspond to the material we use for the color array below
308         mMaterial->setColorMode(osg::Material::DIFFUSE);
309         // this must be done past the transparency setting ...
310         (*mColorArray)[0] = mMaterial->getDiffuse(osg::Material::FRONT_AND_BACK);
311     }
312 
toStateSet(osg::StateSet * stateSet) const313     void toStateSet(osg::StateSet* stateSet) const
314     {
315         stateSet->setAttribute(mMaterial.get());
316         if (mTranslucent)
317             setTranslucent(stateSet);
318     }
319 
getColorArray() const320     osg::Vec4Array* getColorArray() const
321     {
322         return mColorArray.get();
323     }
324 
325 private:
326     osg::ref_ptr<osg::Material> mMaterial;
327     osg::ref_ptr<osg::Vec4Array> mColorArray;
328     bool mTranslucent;
329 };
330 
331 class TextureData
332 {
333   public:
TextureData()334     TextureData() :
335         mTranslucent(false),
336         mRepeat(true)
337     {
338     }
339 
setTexture(const std::string & name,const osgDB::ReaderWriter::Options * options,osg::TexEnv * modulateTexEnv)340     bool setTexture(const std::string& name, const osgDB::ReaderWriter::Options* options, osg::TexEnv* modulateTexEnv)
341     {
342         mTexture2DRepeat = new osg::Texture2D;
343         mTexture2DRepeat->setDataVariance(osg::Object::STATIC);
344         mTexture2DRepeat->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::REPEAT);
345         mTexture2DRepeat->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::REPEAT);
346 
347         mTexture2DClamp = new osg::Texture2D;
348         mTexture2DClamp->setDataVariance(osg::Object::STATIC);
349         mTexture2DClamp->setWrap(osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP_TO_EDGE);
350         mTexture2DClamp->setWrap(osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP_TO_EDGE);
351 
352         std::string absFileName = osgDB::findDataFile(name, options);
353         if (absFileName.empty())
354         {
355             OSG_FATAL << "osgDB ac3d reader: could not find texture \"" << name << "\"" << std::endl;
356             return false;
357         }
358         mImage = osgDB::readRefImageFile(absFileName, options);
359         if (!mImage.valid())
360         {
361             OSG_FATAL << "osgDB ac3d reader: could not read texture \"" << name << "\"" << std::endl;
362             return false;
363         }
364         mTexture2DRepeat->setImage(mImage.get());
365         mTexture2DClamp->setImage(mImage.get());
366         mTranslucent = mImage->isImageTranslucent();
367 
368         // Use a shared modulate TexEnv
369         mModulateTexEnv = modulateTexEnv;
370 
371         return true;
372     }
setRepeat(bool repeat)373     void setRepeat(bool repeat)
374     {
375         mRepeat = repeat;
376     }
valid() const377     bool valid() const
378     {
379         return mImage.valid();
380     }
getFileName() const381     std::string getFileName() const
382     {
383         if (!mImage.valid())
384             return std::string();
385         return mImage->getFileName();
386     }
toTextureStateSet(osg::StateSet * stateSet) const387     void toTextureStateSet(osg::StateSet* stateSet) const
388     {
389         if (!valid())
390             return;
391         stateSet->setTextureAttribute(0, mModulateTexEnv.get());
392         if (mRepeat)
393            stateSet->setTextureAttribute(0, mTexture2DRepeat.get());
394         else
395            stateSet->setTextureAttribute(0, mTexture2DClamp.get());
396         stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
397         if (mTranslucent)
398             setTranslucent(stateSet);
399     }
400 private:
401     osg::ref_ptr<osg::TexEnv> mModulateTexEnv;
402     osg::ref_ptr<osg::Texture2D> mTexture2DClamp;
403     osg::ref_ptr<osg::Texture2D> mTexture2DRepeat;
404     osg::ref_ptr<osg::Image> mImage;
405     bool mTranslucent;
406     bool mRepeat;
407 };
408 
409 class FileData
410 {
411   public:
FileData(const osgDB::ReaderWriter::Options * options)412     FileData(const osgDB::ReaderWriter::Options* options) :
413         mOptions(options),
414         mLightIndex(1)
415     {
416         mModulateTexEnv = new osg::TexEnv;
417         mModulateTexEnv->setDataVariance(osg::Object::STATIC);
418         mModulateTexEnv->setMode(osg::TexEnv::MODULATE);
419     }
420 
toTextureData(const std::string & texName)421     TextureData toTextureData(const std::string& texName)
422     {
423         // If it is already there, use this
424         TextureDataMap::iterator i = mTextureStates.find(texName);
425         if (i != mTextureStates.end())
426             return i->second;
427         // Try to load that texture.
428         TextureData textureData;
429         textureData.setTexture(texName, mOptions.get(), mModulateTexEnv.get());
430         if (textureData.valid()) {
431             mTextureStates[texName] = textureData;
432             return textureData;
433         }
434         // still no joy?, try with the stripped filename if this is different
435         // Try the pure file name if it is different
436         std::string simpleTexName = osgDB::getSimpleFileName(texName);
437         if (simpleTexName != texName)
438             return toTextureData(simpleTexName);
439 
440         // Nothing that worked, return invalid data
441         return TextureData();
442     }
443 
getNextLight()444     osg::Light* getNextLight()
445     {
446         osg::Light* light = new osg::Light;
447         light->setDataVariance(osg::Object::STATIC);
448         light->setLightNum(mLightIndex++);
449         return light;
450     }
451 
addMaterial(const MaterialData & material)452     void addMaterial(const MaterialData& material)
453     {
454         mMaterials.push_back(material);
455     }
getNumMaterials() const456     unsigned getNumMaterials() const
457     {
458         return mMaterials.size();
459     }
getMaterial(unsigned idx) const460     const MaterialData& getMaterial(unsigned idx) const
461     {
462         return mMaterials[idx];
463     }
464 
465 private:
466     /// Stores the ac3d file reader options, only used for reading texture files
467     osg::ref_ptr<osgDB::ReaderWriter::Options const> mOptions;
468 
469     /// The list of ac3d MATERIALS
470     std::vector<MaterialData> mMaterials;
471 
472     /// Local per model texture attribute cache.
473     /// ... images are usually cached in the registries object cache
474     typedef std::map<std::string, TextureData> TextureDataMap;
475     TextureDataMap mTextureStates;
476     /// A common shared TexEnv set to modulate
477     osg::ref_ptr<osg::TexEnv> mModulateTexEnv;
478 
479     /// Hack to include light nodes from ac3d into the scenegraph
480     unsigned mLightIndex;
481 };
482 
483 struct RefData {
RefDataac3d::RefData484     RefData(const osg::Vec3& _weightedNormal, const osg::Vec2& _texCoord, bool _smooth) :
485         weightedFlatNormal(_weightedNormal),
486         weightedFlatNormalLength(_weightedNormal.length()),
487         texCoord(_texCoord),
488         smooth(_smooth)
489     { }
490     // weighted flat surface normal
491     osg::Vec3 weightedFlatNormal;
492     float weightedFlatNormalLength;
493     osg::Vec2 texCoord;
494     // resulting vertex normal
495     osg::Vec3 finalNormal;
496     // if zero no need to smooth
497     unsigned smooth;
498 };
499 
500 struct VertexData {
VertexDataac3d::VertexData501     VertexData(const osg::Vec3& vertex) : _vertex(vertex) {}
addRefDataac3d::VertexData502     unsigned addRefData(const RefData& refData)
503     {
504         unsigned index = _refs.size();
505         _refs.push_back(refData);
506         return index;
507     }
508 
collectac3d::VertexData509     void collect(float cosCreaseAngle, const RefData& ref)
510     {
511         unsigned size = _refs.size();
512         for (unsigned i = 0; i < size; ++i)
513         {
514             if (_refs[i].smooth == ~0u)
515             {
516                 float dot = _refs[i].weightedFlatNormal*ref.weightedFlatNormal;
517                 float lengths = _refs[i].weightedFlatNormalLength*ref.weightedFlatNormalLength;
518                 if (cosCreaseAngle*lengths <= dot)
519                 {
520                     // Ok put that into the current set
521                     _refs[i].smooth = ref.smooth;
522                     collect(cosCreaseAngle, _refs[i]);
523                 }
524             }
525         }
526     }
527 
smoothNormalsac3d::VertexData528     void smoothNormals(float cosCreaseAngle)
529     {
530         // compute sets of vertices smoothed to the same normal
531         // if smooth is zero we do not need to smooth
532         // in a first pass mark all refs not yet in a set to ~0u
533         unsigned size = _refs.size();
534         for (unsigned i = 0; i < size; ++i)
535         {
536             if (_refs[i].smooth)
537             {
538                 _refs[i].smooth = ~0u;
539             }
540         }
541         // Now collect the sets
542         unsigned currentSet = 1;
543         for (unsigned i = 0; i < size; ++i)
544         {
545             if (_refs[i].smooth == ~0u)
546             {
547                 _refs[i].smooth = currentSet++;
548                 collect(cosCreaseAngle, _refs[i]);
549             }
550         }
551         // smooth and normalize the sets
552         for (--currentSet; 0 < currentSet; --currentSet)
553         {
554             osg::Vec3 normal(0, 0, 0);
555             for (unsigned i = 0; i < size; ++i)
556             {
557                 if (_refs[i].smooth == currentSet)
558                 {
559                     normal += _refs[i].weightedFlatNormal;
560                 }
561             }
562             normal.normalize();
563             for (unsigned i = 0; i < size; ++i)
564             {
565                 if (_refs[i].smooth == currentSet)
566                 {
567                   _refs[i].finalNormal = normal;
568                 }
569             }
570         }
571 
572         // normalize the ones which do not need smoothing
573         for (unsigned i = 0; i < size; ++i)
574         {
575             if (_refs[i].smooth == 0)
576             {
577                 _refs[i].finalNormal = _refs[i].weightedFlatNormal;
578                 _refs[i].finalNormal.normalize();
579             }
580         }
581     }
582     osg::Vec3 _vertex;
583     std::vector<RefData> _refs;
584 };
585 struct VertexIndex {
VertexIndexac3d::VertexIndex586     VertexIndex(unsigned _vertexIndex = 0, unsigned _refIndex = 0) :
587         vertexIndex(_vertexIndex), refIndex(_refIndex)
588     { }
589     unsigned vertexIndex;
590     unsigned refIndex;
591 };
592 
593 class VertexSet : public osg::Referenced {
594 public:
VertexSet()595     VertexSet() :
596         _cosCreaseAngle(1.0f),
597         _dirty(true)
598     { }
reserve(unsigned n)599     void reserve(unsigned n)
600     {
601         _vertices.reserve(n);
602     }
size() const603     unsigned size() const
604     {
605         return _vertices.size();
606     }
setCreaseAngle(float crease)607     void setCreaseAngle(float crease)
608     {
609         _dirty = true;
610         if (crease <= 0)
611             _cosCreaseAngle = 1.0f;
612         else if (180 <= crease)
613             _cosCreaseAngle = -1.0f;
614         else
615             _cosCreaseAngle = cosf(osg::DegreesToRadians(crease));
616     }
addVertex(const osg::Vec3 & vertex)617     void addVertex(const osg::Vec3& vertex)
618     {
619         _dirty = true;
620         _vertices.push_back(vertex);
621     }
getVertex(unsigned index)622     const osg::Vec3& getVertex(unsigned index)
623     {
624         return _vertices[index]._vertex;
625     }
getVertex(const VertexIndex & vertexIndex)626     const osg::Vec3& getVertex(const VertexIndex& vertexIndex)
627     {
628         return _vertices[vertexIndex.vertexIndex]._vertex;
629     }
getNormal(const VertexIndex & vertexIndex)630     const osg::Vec3& getNormal(const VertexIndex& vertexIndex)
631     {
632         if (_dirty)
633             smoothNormals();
634         return _vertices[vertexIndex.vertexIndex]._refs[vertexIndex.refIndex].finalNormal;
635     }
getTexCoord(const VertexIndex & vertexIndex)636     const osg::Vec2& getTexCoord(const VertexIndex& vertexIndex)
637     {
638         return _vertices[vertexIndex.vertexIndex]._refs[vertexIndex.refIndex].texCoord;
639     }
640 
addRefData(unsigned i,const RefData & refData)641     VertexIndex addRefData(unsigned i, const RefData& refData)
642     {
643          if (_vertices.size() <= i)
644          {
645              OSG_FATAL << "osgDB ac3d reader: internal error, got invalid vertex index!" << std::endl;
646              return VertexIndex(0, 0);
647          }
648         _dirty = true;
649         return VertexIndex(i, _vertices[i].addRefData(refData));
650     }
651 
652 private:
smoothNormals()653     void smoothNormals()
654     {
655         std::vector<VertexData>::iterator i;
656         for (i = _vertices.begin(); i != _vertices.end(); ++i)
657         {
658             i->smoothNormals(_cosCreaseAngle);
659         }
660         _dirty = false;
661     }
662 
663     std::vector<VertexData> _vertices;
664     float _cosCreaseAngle;
665     bool _dirty;
666 };
667 
668 
669 class PrimitiveBin : public osg::Referenced
670 {
671   public:
PrimitiveBin(unsigned flags,VertexSet * vertexSet)672     PrimitiveBin(unsigned flags, VertexSet* vertexSet) :
673         _geode(new osg::Geode),
674         _vertexSet(vertexSet),
675         _flags(flags)
676     {
677         _geode->setDataVariance(osg::Object::STATIC);
678     }
679 
680     virtual bool beginPrimitive(unsigned nRefs) = 0;
681     virtual bool vertex(unsigned vertexIndex, const osg::Vec2& texCoord) = 0;
682     virtual bool endPrimitive() = 0;
683 
684     virtual osg::Geode* finalize(const MaterialData& material, const TextureData& textureData) = 0;
685 
686   protected:
isLineLoop() const687     bool isLineLoop() const
688     {
689         return (_flags & SurfaceTypeLineLoop)!=0;
690     }
isLineStrip() const691     bool isLineStrip() const
692     {
693         return (_flags & SurfaceTypeLineStrip)!=0;
694     }
isTwoSided() const695     bool isTwoSided() const
696     {
697         return (_flags & SurfaceTwoSided)!=0;
698     }
isSmooth() const699     bool isSmooth() const
700     {
701         return (_flags & SurfaceShaded)!=0;
702     }
703 
704     osg::ref_ptr<osg::Geode> _geode;
705     osg::ref_ptr<VertexSet> _vertexSet;
706 
707   private:
708     unsigned _flags;
709 };
710 
711 class LineBin : public PrimitiveBin
712 {
713   private:
714     osg::ref_ptr<osg::Geometry> _geometry;
715     osg::ref_ptr<osg::Vec3Array> _vertices;
716     osg::ref_ptr<osg::Vec2Array> _texCoords;
717     struct Ref {
718       osg::Vec2 texCoord;
719       unsigned index;
720     };
721     std::vector<Ref> _refs;
722 
723   public:
LineBin(unsigned flags,VertexSet * vertexSet)724     LineBin(unsigned flags, VertexSet* vertexSet) :
725         PrimitiveBin(flags, vertexSet),
726         _geometry(new osg::Geometry),
727         _vertices(new osg::Vec3Array),
728         _texCoords(new osg::Vec2Array)
729     {
730         _geometry->setDataVariance(osg::Object::STATIC);
731         _vertices->setDataVariance(osg::Object::STATIC);
732         _texCoords->setDataVariance(osg::Object::STATIC);
733         _geometry->setVertexArray(_vertices.get());
734         _geometry->setTexCoordArray(0, _texCoords.get());
735         osg::StateSet* stateSet = _geode->getOrCreateStateSet();
736         stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
737     }
738 
beginPrimitive(unsigned nRefs)739     virtual bool beginPrimitive(unsigned nRefs)
740     {
741         // Check if we have enough for a line or something broken ...
742         if (nRefs < 2) {
743             OSG_WARN << "osgDB ac3d reader: detected line with less than 2 vertices!" << std::endl;
744             return false;
745         }
746 
747         _refs.reserve(nRefs);
748         _refs.resize(0);
749         return true;
750     }
vertex(unsigned vertexIndex,const osg::Vec2 & texCoord)751     virtual bool vertex(unsigned vertexIndex, const osg::Vec2& texCoord)
752     {
753         Ref ref;
754         ref.index = vertexIndex;
755         ref.texCoord = texCoord;
756         _refs.push_back(ref);
757         return true;
758     }
endPrimitive()759     virtual bool endPrimitive()
760     {
761         GLint type;
762         if (isLineLoop())
763             type = osg::PrimitiveSet::LINE_LOOP;
764         else if (isLineStrip())
765             type = osg::PrimitiveSet::LINE_STRIP;
766         else {
767             OSG_FATAL << "osgDB ac3d reader: non surface flags in surface bin!" << std::endl;
768             return false;
769         }
770         unsigned nRefs = _refs.size();
771         unsigned start = _vertices->size();
772         for (unsigned i = 0; i < nRefs; ++i) {
773             osg::Vec3 vertex = _vertexSet->getVertex(_refs[i].index);
774             _vertices->push_back(vertex);
775             _texCoords->push_back(_refs[i].texCoord);
776         }
777         _geometry->addPrimitiveSet(new osg::DrawArrays(type, start, nRefs));
778 
779         return true;
780     }
781 
finalize(const MaterialData & material,const TextureData &)782     virtual osg::Geode* finalize(const MaterialData& material, const TextureData& /*textureData*/)
783     {
784         _geode->addDrawable(_geometry.get());
785         material.toStateSet(_geode->getOrCreateStateSet());
786         _geometry->setColorArray(material.getColorArray(), osg::Array::BIND_OVERALL);
787         _geometry->setNormalArray(0);
788         return _geode.get();
789     }
790 };
791 
792 
793 
794 class SurfaceBin : public PrimitiveBin {
795   private:
796     struct Ref {
797         osg::Vec2 texCoord;
798         unsigned index;
799     };
800     std::vector<Ref> _refs;
801 
802     struct TriangleData {
803         VertexIndex index[3];
804     };
805     std::vector<TriangleData> _triangles;
806 
807     struct QuadData {
808         VertexIndex index[4];
809     };
810     std::vector<QuadData> _quads;
811 
812     struct PolygonData {
813         std::vector<VertexIndex> index;
814     };
815     std::vector<PolygonData> _polygons;
816     std::vector<PolygonData> _toTessellatePolygons;
817 
818     typedef std::pair<osg::Vec3, osg::Vec3> VertexNormalPair;
819     typedef std::pair<VertexNormalPair, osg::Vec2> VertexNormalTexTuple;
820     typedef std::map<VertexNormalTexTuple, unsigned> VertexIndexMap;
821     VertexIndexMap _vertexIndexMap;
822 
823   public:
SurfaceBin(unsigned flags,VertexSet * vertexSet)824     SurfaceBin(unsigned flags, VertexSet *vertexSet) :
825         PrimitiveBin(flags, vertexSet)
826     { }
827 
beginPrimitive(unsigned nRefs)828     virtual bool beginPrimitive(unsigned nRefs)
829     {
830         _refs.reserve(nRefs);
831         _refs.clear();
832 
833         // Check if we have enough for a line or something broken ...
834         if (nRefs < 3) {
835             OSG_WARN << "osgDB ac3d reader: detected surface with less than 3 vertices!" << std::endl;
836             return false;
837         }
838         return true;
839     }
vertex(unsigned vertexIndex,const osg::Vec2 & texCoord)840     virtual bool vertex(unsigned vertexIndex, const osg::Vec2& texCoord)
841     {
842         Ref ref;
843         ref.index = vertexIndex;
844         ref.texCoord = texCoord;
845         _refs.push_back(ref);
846         return true;
847     }
endPrimitive()848     virtual bool endPrimitive()
849     {
850         unsigned nRefs = _refs.size();
851 
852         // Compute the normal times the enclosed area.
853         // During that check if the surface is convex. If so, put in the surface as such.
854         bool needTessellation = false;
855         osg::Vec3 prevEdgeNormal;
856         osg::Vec3 weightedNormal(0, 0, 0);
857         osg::Vec3 v0 = _vertexSet->getVertex(_refs[0].index);
858         for (unsigned i = 2; i < nRefs; ++i) {
859             osg::Vec3 side1 = _vertexSet->getVertex(_refs[i-1].index) - v0;
860             osg::Vec3 side2 = _vertexSet->getVertex(_refs[i].index) - v0;
861             osg::Vec3 newNormal = side1^side2;
862             if (!needTessellation)
863             {
864                 if (3 < nRefs && newNormal*weightedNormal < 0)
865                 {
866                     needTessellation = true;
867                 }
868                 if (i < 3)
869                 {
870                     prevEdgeNormal = newNormal;
871                 }
872                 else // if (3 <= i) // due to the for loop
873                 {
874                     osg::Vec3 sideim1 = _vertexSet->getVertex(_refs[i-1].index) - _vertexSet->getVertex(_refs[i-2].index);
875                     osg::Vec3 sidei = _vertexSet->getVertex(_refs[i].index) - _vertexSet->getVertex(_refs[i-2].index);
876                     osg::Vec3 edgeNormal = sideim1^sidei;
877                     if (edgeNormal*prevEdgeNormal < 0)
878                     {
879                         needTessellation = true;
880                     }
881                     prevEdgeNormal = edgeNormal;
882                 }
883             }
884 
885             weightedNormal += newNormal;
886         }
887 
888         if (needTessellation)
889         {
890             unsigned polygonIndex = _toTessellatePolygons.size();
891             _toTessellatePolygons.resize(polygonIndex + 1);
892             for (unsigned i = 0; i < nRefs; ++i) {
893                 RefData refData(weightedNormal, _refs[i].texCoord, isSmooth());
894                 VertexIndex vertexIndex = _vertexSet->addRefData(_refs[i].index, refData);
895                 _toTessellatePolygons[polygonIndex].index.push_back(vertexIndex);
896             }
897         }
898         else if (nRefs == 3)
899         {
900             unsigned triangleIndex = _triangles.size();
901             _triangles.resize(triangleIndex + 1);
902             for (unsigned i = 0; i < 3; ++i) {
903                 RefData refData(weightedNormal, _refs[i].texCoord, isSmooth());
904                 VertexIndex vertexIndex = _vertexSet->addRefData(_refs[i].index, refData);
905                 _triangles[triangleIndex].index[i] = vertexIndex;
906             }
907         }
908         else if (nRefs == 4)
909         {
910             unsigned quadIndex = _quads.size();
911             _quads.resize(quadIndex + 1);
912             for (unsigned i = 0; i < 4; ++i) {
913                 RefData refData(weightedNormal, _refs[i].texCoord, isSmooth());
914                 VertexIndex vertexIndex = _vertexSet->addRefData(_refs[i].index, refData);
915                 _quads[quadIndex].index[i] = vertexIndex;
916             }
917         }
918         else
919         {
920             unsigned polygonIndex = _polygons.size();
921             _polygons.resize(polygonIndex + 1);
922             for (unsigned i = 0; i < nRefs; ++i) {
923                 RefData refData(weightedNormal, _refs[i].texCoord, isSmooth());
924                 VertexIndex vertexIndex = _vertexSet->addRefData(_refs[i].index, refData);
925                 _polygons[polygonIndex].index.push_back(vertexIndex);
926             }
927         }
928         return true;
929     }
930 
pushVertex(const VertexIndex & vertexIndex,osg::Vec3Array * vertexArray,osg::Vec3Array * normalArray,osg::Vec2Array * texcoordArray)931     unsigned pushVertex(const VertexIndex& vertexIndex, osg::Vec3Array* vertexArray,
932                         osg::Vec3Array* normalArray, osg::Vec2Array* texcoordArray)
933     {
934         VertexNormalTexTuple vertexNormalTexTuple;
935         vertexNormalTexTuple.first.first = _vertexSet->getVertex(vertexIndex);
936         vertexNormalTexTuple.first.second = _vertexSet->getNormal(vertexIndex);
937         if (texcoordArray)
938             vertexNormalTexTuple.second = _vertexSet->getTexCoord(vertexIndex);
939         else
940             vertexNormalTexTuple.second = osg::Vec2(0, 0);
941 
942         VertexIndexMap::iterator i = _vertexIndexMap.find(vertexNormalTexTuple);
943         if (i != _vertexIndexMap.end())
944             return i->second;
945 
946         unsigned index = vertexArray->size();
947         vertexArray->push_back(vertexNormalTexTuple.first.first);
948         normalArray->push_back(vertexNormalTexTuple.first.second);
949         if (texcoordArray)
950             texcoordArray->push_back(vertexNormalTexTuple.second);
951 
952         _vertexIndexMap.insert(VertexIndexMap::value_type(vertexNormalTexTuple, index));
953 
954         return index;
955     }
956 
createOptimalDrawElements(osg::DrawElementsUInt * drawElements)957     osg::DrawElements* createOptimalDrawElements(osg::DrawElementsUInt* drawElements)
958     {
959         unsigned num = drawElements->getNumIndices();
960         unsigned maxIndex = 0;
961         for (unsigned i = 0; i < num; ++i)
962         {
963             maxIndex = osg::maximum(maxIndex, drawElements->getElement(i));
964         }
965 
966         if (maxIndex <= std::numeric_limits<unsigned char>::max())
967         {
968             osg::DrawElementsUByte* drawElementsUByte = new osg::DrawElementsUByte(drawElements->getMode());
969             drawElementsUByte->reserveElements(num);
970             for (unsigned i = 0; i < num; ++i)
971             {
972                 drawElementsUByte->addElement(drawElements->getElement(i));
973             }
974             return drawElementsUByte;
975         }
976         else if (maxIndex <= std::numeric_limits<unsigned short>::max())
977         {
978             osg::DrawElementsUShort* drawElementsUShort = new osg::DrawElementsUShort(drawElements->getMode());
979             drawElementsUShort->reserveElements(num);
980             for (unsigned i = 0; i < num; ++i)
981             {
982                 drawElementsUShort->addElement(drawElements->getElement(i));
983             }
984             return drawElementsUShort;
985         }
986         else
987         {
988             return drawElements;
989         }
990     }
991 
finalize(const MaterialData & material,const TextureData & textureData)992     virtual osg::Geode* finalize(const MaterialData& material, const TextureData& textureData)
993     {
994         osg::StateSet* stateSet = _geode->getOrCreateStateSet();
995         material.toStateSet(stateSet);
996         textureData.toTextureStateSet(stateSet);
997         stateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON);
998 
999         // Single- or doublesided culling
1000         if (isTwoSided()) {
1001             stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
1002         } else {
1003             osg::CullFace* cullFace = new osg::CullFace;
1004             cullFace->setDataVariance(osg::Object::STATIC);
1005             cullFace->setMode(osg::CullFace::BACK);
1006             stateSet->setAttribute(cullFace);
1007             stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::ON);
1008         }
1009 
1010         // Flat or smooth shading
1011         osg::ShadeModel* shadeModel = new osg::ShadeModel;
1012         shadeModel->setDataVariance(osg::Object::STATIC);
1013         if (isSmooth())
1014             shadeModel->setMode(osg::ShadeModel::SMOOTH);
1015         else
1016             shadeModel->setMode(osg::ShadeModel::FLAT);
1017         stateSet->setAttribute(shadeModel);
1018 
1019         // Set up the arrays, always store texture coords, may be we need them later ...
1020         osg::Geometry* geometry = new osg::Geometry;
1021         _geode->addDrawable(geometry);
1022         geometry->setDataVariance(osg::Object::STATIC);
1023         geometry->setColorArray(material.getColorArray(), osg::Array::BIND_OVERALL);
1024         osg::Vec3Array* normalArray = new osg::Vec3Array;
1025         geometry->setNormalArray(normalArray, osg::Array::BIND_PER_VERTEX);
1026         osg::Vec3Array* vertexArray = new osg::Vec3Array;
1027         geometry->setVertexArray(vertexArray);
1028         osg::Vec2Array* texcoordArray = 0;
1029         if (textureData.valid())
1030         {
1031             texcoordArray = new osg::Vec2Array;
1032             geometry->setTexCoordArray(0, texcoordArray);
1033         }
1034 
1035         // At first handle the polygons to tessellate, fix them and append the other polygons later
1036         if (!_toTessellatePolygons.empty())
1037         {
1038             for (unsigned i = 0; i < _toTessellatePolygons.size(); ++i)
1039             {
1040                 osg::ref_ptr<osg::DrawElementsUInt> drawElements = new osg::DrawElementsUInt(osg::PrimitiveSet::POLYGON);
1041                 for (unsigned j = 0; j < _toTessellatePolygons[i].index.size(); ++j)
1042                 {
1043                     unsigned index = pushVertex(_toTessellatePolygons[i].index[j], vertexArray, normalArray, texcoordArray);
1044                     drawElements->addElement(index);
1045                 }
1046                 geometry->addPrimitiveSet(createOptimalDrawElements(drawElements.get()));
1047             }
1048 
1049             osgUtil::Tessellator Tessellator;
1050             Tessellator.retessellatePolygons(*geometry);
1051         }
1052 
1053         // handle triangles
1054         if (!_triangles.empty())
1055         {
1056             osg::ref_ptr<osg::DrawElementsUInt> drawElements = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES);
1057             for (unsigned i = 0; i < _triangles.size(); ++i)
1058             {
1059                 for (unsigned j = 0; j < 3; ++j)
1060                 {
1061                     unsigned index = pushVertex(_triangles[i].index[j], vertexArray, normalArray, texcoordArray);
1062                     drawElements->addElement(index);
1063                 }
1064             }
1065             geometry->addPrimitiveSet(createOptimalDrawElements(drawElements.get()));
1066         }
1067 
1068         // handle quads
1069         if (!_quads.empty())
1070         {
1071             osg::ref_ptr<osg::DrawElementsUInt> drawElements = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS);
1072             for (unsigned i = 0; i < _quads.size(); ++i)
1073             {
1074                 for (unsigned j = 0; j < 4; ++j)
1075                 {
1076                     unsigned index = pushVertex(_quads[i].index[j], vertexArray, normalArray, texcoordArray);
1077                     drawElements->addElement(index);
1078                 }
1079             }
1080             geometry->addPrimitiveSet(createOptimalDrawElements(drawElements.get()));
1081         }
1082 
1083         // handle polygons
1084         if (!_polygons.empty())
1085         {
1086             for (unsigned i = 0; i < _polygons.size(); ++i)
1087             {
1088                 osg::ref_ptr<osg::DrawElementsUInt> drawElements = new osg::DrawElementsUInt(osg::PrimitiveSet::POLYGON);
1089                 for (unsigned j = 0; j < _polygons[i].index.size(); ++j)
1090                 {
1091                     unsigned index = pushVertex(_polygons[i].index[j], vertexArray, normalArray, texcoordArray);
1092                     drawElements->addElement(index);
1093                 }
1094                 geometry->addPrimitiveSet(createOptimalDrawElements(drawElements.get()));
1095             }
1096         }
1097 
1098         return _geode.get();
1099     }
1100 };
1101 
1102 struct Bins
1103 {
getOrCreatePrimitiveBinac3d::Bins1104     PrimitiveBin* getOrCreatePrimitiveBin(unsigned flags, VertexSet* vertexSet)
1105     {
1106         if ((flags & SurfaceTypeLineLoop) || (flags & SurfaceTypeLineStrip))
1107         {
1108             if (!lineBin.valid())
1109             {
1110                 lineBin = new LineBin(flags, vertexSet);
1111             }
1112             return lineBin.get();
1113         }
1114         else if (flags & SurfaceShaded)
1115         {
1116             if (flags & SurfaceTwoSided)
1117             {
1118                 if (!smoothDoubleSurfaceBin.valid())
1119                 {
1120                     smoothDoubleSurfaceBin = new SurfaceBin(flags, vertexSet);
1121                 }
1122                 return smoothDoubleSurfaceBin.get();
1123             }
1124             else
1125             {
1126                 if (!smoothSingleSurfaceBin.valid())
1127                 {
1128                     smoothSingleSurfaceBin = new SurfaceBin(flags, vertexSet);
1129                 }
1130                 return smoothSingleSurfaceBin.get();
1131             }
1132         }
1133         else
1134         {
1135             if (flags & SurfaceTwoSided)
1136             {
1137                 if (!flatDoubleSurfaceBin.valid())
1138                 {
1139                     flatDoubleSurfaceBin = new SurfaceBin(flags, vertexSet);
1140                 }
1141                 return flatDoubleSurfaceBin.get();
1142             }
1143             else
1144             {
1145                 if (!flatSingleSurfaceBin.valid())
1146                 {
1147                     flatSingleSurfaceBin = new SurfaceBin(flags, vertexSet);
1148                 }
1149                 return flatSingleSurfaceBin.get();
1150             }
1151         }
1152     }
finalizeac3d::Bins1153     void finalize(osg::Group* group, const MaterialData& material, const TextureData& textureData)
1154     {
1155         if (lineBin.valid())
1156         {
1157             group->addChild(lineBin->finalize(material, textureData));
1158         }
1159         if (smoothDoubleSurfaceBin.valid())
1160         {
1161             group->addChild(smoothDoubleSurfaceBin->finalize(material, textureData));
1162         }
1163         if (smoothSingleSurfaceBin.valid())
1164         {
1165             group->addChild(smoothSingleSurfaceBin->finalize(material, textureData));
1166         }
1167         if (flatDoubleSurfaceBin.valid())
1168         {
1169             group->addChild(flatDoubleSurfaceBin->finalize(material, textureData));
1170         }
1171         if (flatSingleSurfaceBin.valid())
1172         {
1173             group->addChild(flatSingleSurfaceBin->finalize(material, textureData));
1174         }
1175     }
1176 
1177 private:
1178     osg::ref_ptr<LineBin> lineBin;
1179     osg::ref_ptr<SurfaceBin> flatDoubleSurfaceBin;
1180     osg::ref_ptr<SurfaceBin> flatSingleSurfaceBin;
1181     osg::ref_ptr<SurfaceBin> smoothDoubleSurfaceBin;
1182     osg::ref_ptr<SurfaceBin> smoothSingleSurfaceBin;
1183 };
1184 
1185 osg::Node*
readObject(std::istream & stream,FileData & fileData,const osg::Matrix & parentTransform,TextureData textureData)1186 readObject(std::istream& stream, FileData& fileData, const osg::Matrix& parentTransform, TextureData textureData)
1187 {
1188     // most of this logic came from Andy Colebourne (developer of the AC3D editor) so it had better be right!
1189 
1190     // The transform configured in this current object level
1191     osg::Matrix transform;
1192     // The vertex pool in this object
1193     osg::ref_ptr<VertexSet> vertexSet = new VertexSet;
1194     osg::ref_ptr<osg::Group> group = new osg::Group;
1195     group->setDataVariance(osg::Object::STATIC);
1196     osg::Vec2 textureOffset(0, 0);
1197     osg::Vec2 textureRepeat(1, 1);
1198     float creaseAngle = 61;
1199     unsigned objectType = ObjectTypeGroup;
1200 
1201     while (!stream.eof() && stream.good()) {
1202         std::string token;
1203         stream >> token;
1204 
1205         if (token == "MATERIAL") {
1206             MaterialData mat;
1207             mat.readMaterial(stream);
1208             fileData.addMaterial(mat);
1209         }
1210         else if (token == "OBJECT") {
1211             std::string type;
1212             stream >> type;
1213 
1214             if (type == "group")
1215                 objectType = ObjectTypeGroup;
1216             else if (type == "light")
1217                 objectType = ObjectTypeLight;
1218             else if (type == "world")
1219                 objectType = ObjectTypeGroup;
1220             else
1221                 objectType = ObjectTypeNormal;
1222         }
1223         else if (token == "crease") {
1224             stream >> creaseAngle;
1225         }
1226         else if (token == "data") {
1227             int len;
1228             stream >> len;
1229             std::vector<char> tmp(len);
1230             stream.read(&(tmp[0]), len);
1231         }
1232         else if (token == "name") {
1233             group->setName(readString(stream));
1234         }
1235         else if (token == "texture") {
1236             // read the texture name
1237             textureData = fileData.toTextureData(readString(stream));
1238         }
1239         else if (token == "texrep") {
1240             stream >> textureRepeat[0] >> textureRepeat[1];
1241 //             if (textureRepeat[0] == 0.0f && textureRepeat[1] == 0.0f)
1242 //                 textureData.setRepeat(false);
1243 //             else
1244 //                 textureData.setRepeat(true);
1245         }
1246         else if (token == "texoff") {
1247             stream >> textureOffset[0] >> textureOffset[1];
1248         }
1249         else if (token == "rot") {
1250             for (unsigned n = 0; n < 3; ++n)
1251                 for (unsigned m = 0; m < 3; ++m)
1252 #if 1
1253                     stream >> transform(n, m);
1254 #else
1255                     stream >> transform(m, n);
1256 #endif
1257         }
1258         else if (token == "loc") {
1259             for (unsigned n = 0; n < 3; ++n)
1260                 stream >> transform(3, n);
1261         }
1262         else if (token == "url") {
1263             std::string url;
1264             stream >> url;
1265             group->addDescription(url);
1266         }
1267         else if (token == "numvert") {
1268             osg::Matrix currentTransform = transform*parentTransform;
1269 
1270             unsigned num;
1271             stream >> num;
1272             if (num != 0) {
1273                 vertexSet->reserve(num);
1274 
1275                 for (unsigned n = 0; n < num; ++n) {
1276                     osg::Vec3 p;
1277                     stream >> p[0] >> p[1] >> p[2];
1278                     vertexSet->addVertex(currentTransform.preMult(p));
1279                 }
1280             }
1281         }
1282         else if (token == "numsurf") {
1283             unsigned num;
1284             stream >> num;
1285             if (0 < num) {
1286                 // list of materials required- generate one geode per material
1287                 std::vector<Bins> primitiveBins(fileData.getNumMaterials());
1288                 vertexSet->setCreaseAngle(creaseAngle);
1289 
1290                 for (unsigned n = 0; n < num; ++n) {
1291                     std::string inner_token;
1292                     stream >> inner_token;
1293 
1294                     if (inner_token != "SURF") {
1295                         OSG_FATAL << "osgDB ac3d reader: expected SURF line while reading object \""
1296                                                 << group->getName() << "\"!" << std::endl;
1297                         return group.release();
1298                     }
1299 
1300                     stream >> inner_token;
1301                     unsigned flags = strtol(inner_token.c_str(), NULL, 0);
1302 
1303                     stream >> inner_token;
1304                     if (inner_token != "mat") {
1305                         OSG_FATAL << "osgDB ac3d reader: expected mat line while reading object \""
1306                                                 << group->getName() << "\"!" << std::endl;
1307                         return group.release();
1308                     }
1309 
1310                     // read the material index
1311                     unsigned matIdx;
1312                     stream >> matIdx;
1313                     if (primitiveBins.size() <= matIdx) {
1314                         OSG_FATAL << "osgDB ac3d reader: invalid material number while reading object \""
1315                                                 << group->getName() << "\"" << std::endl;
1316                         return group.release();
1317                     }
1318 
1319                     // now get the correct PrimitiveBin
1320                     PrimitiveBin* primitiveBin = 0;
1321                     primitiveBin = primitiveBins[matIdx].getOrCreatePrimitiveBin(flags, vertexSet.get());
1322                     if (!primitiveBin) {
1323                         OSG_FATAL << "osgDB ac3d reader: unexpected primitive flags while reading object \""
1324                                                 << group->getName() << "\"" << std::endl;
1325                         return group.release();
1326                     }
1327 
1328                     // read the refs
1329                     stream >> inner_token;
1330                     if (inner_token != "refs") {
1331                         OSG_FATAL << "osgDB ac3d reader: expected refs line while reading object \""
1332                                                 << group->getName() << "\"" << std::endl;
1333                         return group.release();
1334                     }
1335 
1336                     unsigned nRefs = 0;
1337                     stream >> nRefs;
1338                     if (!stream) {
1339                         OSG_FATAL << "osgDB ac3d reader: could not read number of refs while reading object \""
1340                                                 << group->getName() << "\"" << std::endl;
1341                         return group.release();
1342                     }
1343 
1344                     // in case this is an invalid refs count for this primitive
1345                     // read further, but do not store that primitive
1346                     bool acceptPrimitive = primitiveBin->beginPrimitive(nRefs);
1347                     for (unsigned i = 0; i < nRefs; ++i) {
1348                         // Read the vertex index
1349                         unsigned index;
1350                         stream >> index;
1351                         if (vertexSet->size() <= index)
1352                         {
1353                             OSG_FATAL << "osgDB ac3d reader: invalid ref vertex index while reading object \""
1354                                                     << group->getName() << "\"" << std::endl;
1355                             return group.release();
1356                         }
1357 
1358                         // Read the texture corrdinates
1359                         osg::Vec2 texCoord;
1360                         stream >> texCoord[0] >> texCoord[1];
1361                         if (!stream) {
1362                             OSG_WARN << "osgDB ac3d reader: could not parse texture coords while reading object \""
1363                                                    << group->getName() << "\" setting to (0,0)" << std::endl;
1364                             stream.clear();
1365                             std::string dummy;
1366                             std::getline(stream, dummy);
1367                         }
1368 
1369                         if (acceptPrimitive)
1370                         {
1371                             texCoord[0] = textureOffset[0] + texCoord[0]*textureRepeat[0];
1372                             texCoord[1] = textureOffset[1] + texCoord[1]*textureRepeat[1];
1373 
1374                             if (!primitiveBin->vertex(index, texCoord))
1375                             {
1376                                 return group.release();
1377                             }
1378                         }
1379                     }
1380                     if (acceptPrimitive)
1381                     {
1382                         if (!primitiveBin->endPrimitive())
1383                         {
1384                             return group.release();
1385                         }
1386                     }
1387                 }
1388 
1389                 for (unsigned i = 0; i < primitiveBins.size(); ++i)
1390                     primitiveBins[i].finalize(group.get(), fileData.getMaterial(i), textureData);
1391             }
1392         }
1393         else if (token == "kids") {
1394             unsigned num;
1395             stream >> num;
1396             if (num != 0) {
1397                 for (unsigned n = 0; n < num; n++) {
1398                     osg::Node *k = readObject(stream, fileData, transform*parentTransform, textureData);
1399                     if (k == 0) {
1400                         OSG_FATAL << "osgDB ac3d reader: error reading child object" << std::endl;
1401                         return group.release();
1402                     }
1403                     else {
1404                         osg::LightSource *ls = dynamic_cast<osg::LightSource*>(k);
1405                         if (ls) {
1406                             osg::StateSet* lightStateSet = group->getOrCreateStateSet();
1407                             group->setStateSet(lightStateSet);
1408                             group->setCullingActive(false);
1409                             ls->setStateSetModes(*lightStateSet, osg::StateAttribute::ON);
1410                         }
1411 
1412                         group->addChild(k);
1413                     }
1414                 }
1415             }
1416             else if (objectType == ObjectTypeLight) { // add a light source to the scene 1 Nov 2003
1417                 osg::Light* ac3dLight = fileData.getNextLight();
1418                 osg::Matrix tt = transform*parentTransform;
1419                 ac3dLight->setPosition(osg::Vec4(tt(3, 0), tt(3, 1), tt(3, 2), 1));
1420                 ac3dLight->setDirection(osg::Matrix::transform3x3(osg::Vec3(0.0f, 0.0f, -1.0f), tt));
1421                 ac3dLight->setAmbient(osg::Vec4(0.5f,0.5f,0.5f,1.0f));
1422                 ac3dLight->setDiffuse(osg::Vec4(0.5f,0.5f,0.5f,1.0f));
1423                 ac3dLight->setSpecular(osg::Vec4(1.0f,1.0f,0.5f,1.0f));
1424 
1425                 osg::LightSource* ac3dLightSource = new osg::LightSource;
1426                 ac3dLightSource->setDataVariance(osg::Object::STATIC);
1427                 ac3dLightSource->setLight(ac3dLight);
1428                 ac3dLightSource->setLocalStateSetModes(osg::StateAttribute::ON);
1429 
1430                 // for some mad reason, you need to set this so that the light works.  WHY?
1431                 return ac3dLightSource;
1432             }
1433             return group.release();
1434         }
1435     }
1436 
1437     return group.release();
1438 }
1439 
1440 osg::Node*
readFile(std::istream & stream,const osgDB::ReaderWriter::Options * options)1441 readFile(std::istream& stream, const osgDB::ReaderWriter::Options* options)
1442 {
1443     FileData fileData(options);
1444     osg::Matrix identityTransform;
1445     osg::Node* node = readObject(stream, fileData, identityTransform, TextureData());
1446     if (node)
1447       node->setName("World");
1448     return node;
1449 }
1450 
1451 } // namespace ac3d
1452