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