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