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