1 // -*-c++-*-
2 
3 /*
4  * Lightwave Object loader for Open Scene Graph
5  *
6  * Copyright (C) 2001 Ulrich Hertlein <u.hertlein@web.de>
7  * Improved LWO2 reader is (C) 2003-2004 Marco Jez <marco.jez@poste.it>
8  *
9  * The Open Scene Graph (OSG) is a cross platform C++/OpenGL library for
10  * real-time rendering of large 3D photo-realistic models.
11  * The OSG homepage is http://www.openscenegraph.org/
12  */
13 
14 #if defined(_MSC_VER)
15     #pragma warning( disable : 4786 )
16 #endif
17 
18 #include <string>
19 #include <memory>
20 #include <sstream>
21 #include <algorithm>
22 
23 #include <osg/Notify>
24 #include <osg/Node>
25 #include <osg/Group>
26 #include <osg/Geode>
27 #include <osg/Group>
28 #include <osg/Texture2D>
29 #include <osg/Geometry>
30 #include <osg/StateSet>
31 
32 #include <osgDB/Registry>
33 #include <osgDB/ReadFile>
34 #include <osgDB/FileNameUtils>
35 #include <osgDB/FileUtils>
36 
37 #include <osgUtil/SmoothingVisitor>
38 #include <osgUtil/Tessellator>
39 
40 #include <string.h>
41 
42 #include "Converter.h"
43 #include "VertexMap.h"
44 
45 #include "old_lw.h"
46 #include "old_Lwo2.h"
47 
48 class ReaderWriterLWO : public osgDB::ReaderWriter
49 {
50 public:
ReaderWriterLWO()51     ReaderWriterLWO()
52     {
53         supportsExtension("lwo","Lightwave object format");
54         supportsExtension("lw","Lightwave object format");
55         supportsExtension("geo","Lightwave geometry format");
56     }
57 
className() const58     virtual const char* className() const { return "Lightwave Object Reader"; }
59 
readNode(const std::string & file,const osgDB::ReaderWriter::Options * options) const60     virtual ReadResult readNode(const std::string& file, const osgDB::ReaderWriter::Options* options) const
61     {
62         std::string ext = osgDB::getLowerCaseFileExtension(file);
63         if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
64 
65         std::string fileName = osgDB::findDataFile( file, options );
66         if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
67 
68         // code for setting up the database path so that internally referenced file are searched for on relative paths.
69         osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
70         local_opt->setDatabasePath(osgDB::getFilePath(fileName));
71 
72         ReadResult result = readNode_LWO1(fileName,local_opt.get());
73         if (result.success()) return result;
74 
75         if (!options || options->getOptionString() != "USE_OLD_READER") {
76             ReadResult result = readNode_LWO2(fileName, local_opt.get());
77             if (result.success()) return result;
78         }
79 
80         return readNode_old_LWO2(fileName, local_opt.get());
81     }
82 
83     lwosg::Converter::Options parse_options(const Options *options) const;
84 
85     virtual ReadResult readNode_LWO2(const std::string& fileName, const osgDB::ReaderWriter::Options*) const;
86     virtual ReadResult readNode_old_LWO2(const std::string& fileName, const osgDB::ReaderWriter::Options*) const;
87     virtual ReadResult readNode_LWO1(const std::string& fileName, const osgDB::ReaderWriter::Options*) const;
88 
89 protected:
90 
91 
92 
93 };
94 
parse_options(const Options * options) const95 lwosg::Converter::Options ReaderWriterLWO::parse_options(const Options *options) const
96 {
97     lwosg::Converter::Options conv_options;
98 
99     if (options) {
100         std::istringstream iss(options->getOptionString());
101         std::string opt;
102         while (iss >> opt) {
103             if (opt == "COMBINE_GEODES")           conv_options.combine_geodes = true;
104             if (opt == "FORCE_ARB_COMPRESSION")    conv_options.force_arb_compression = true;
105             if (opt == "USE_OSGFX")                conv_options.use_osgfx = true;
106             if (opt == "NO_LIGHTMODEL_ATTRIBUTE")  conv_options.apply_light_model = false;
107             if (opt == "BIND_TEXTURE_MAP")
108             {
109                 std::string mapname;
110                 int unit;
111                 if (iss >> mapname >> unit)
112                 {
113                     conv_options.texturemap_bindings.insert(lwosg::VertexMap_binding_map::value_type(mapname,  unit));
114                 }
115             }
116             if (opt == "MAX_TEXTURE_UNITS") {
117                 int n;
118                 if (iss >> n) {
119                     conv_options.max_tex_units = n;
120                 }
121             }
122         }
123     }
124 
125     return conv_options;
126 }
127 
128 
129 // register with Registry to instantiate the above reader/writer.
REGISTER_OSGPLUGIN(lwo,ReaderWriterLWO)130 REGISTER_OSGPLUGIN(lwo, ReaderWriterLWO)
131 
132 osgDB::ReaderWriter::ReadResult ReaderWriterLWO::readNode_LWO2(const std::string &fileName, const osgDB::ReaderWriter::Options *options) const
133 {
134     lwosg::Converter::Options conv_options = parse_options(options);
135 
136     lwosg::Converter converter(conv_options, options);
137     osg::ref_ptr<osg::Node> node = converter.convert(fileName);
138     if (node.valid()) {
139         return node.release();
140     }
141 
142     return ReadResult::FILE_NOT_HANDLED;
143 }
144 
145 
readNode_old_LWO2(const std::string & fileName,const osgDB::ReaderWriter::Options *) const146 osgDB::ReaderWriter::ReadResult ReaderWriterLWO::readNode_old_LWO2(const std::string& fileName, const osgDB::ReaderWriter::Options*) const
147 {
148     std::auto_ptr<Lwo2> lwo2(new Lwo2());
149     if (lwo2->ReadFile(fileName))
150     {
151         osg::ref_ptr<Group> group = new osg::Group();
152         if (lwo2->GenerateGroup(*group)) return group.release();
153     }
154     return ReadResult::FILE_NOT_HANDLED;
155 }
156 
157 
158 
159 
160 
161 // collect all the data relavent to a particular osg::Geometry being created.
162 struct GeometryCollection
163 {
GeometryCollectionGeometryCollection164     GeometryCollection():
165         _numPrimitives(0),
166         _numPrimitivesWithTexCoords(0),
167         _numPoints(0),
168         _texturesActive(false),
169         _vertices(osg::Vec3Array::iterator()),
170         _texcoords(osg::Vec2Array::iterator()),
171         _coordCount(0),
172         _geom(0) {}
173 
174     int                         _numPrimitives;
175     int                         _numPrimitivesWithTexCoords;
176     int                         _numPoints;
177     bool                        _texturesActive;
178     osg::Vec3Array::iterator    _vertices;
179     osg::Vec2Array::iterator    _texcoords;
180     int                         _coordCount;
181     osg::Geometry*              _geom;
182 };
183 
184 
185 
186 // read file and convert to OSG.
readNode_LWO1(const std::string & fileName,const osgDB::ReaderWriter::Options *) const187 osgDB::ReaderWriter::ReadResult ReaderWriterLWO::readNode_LWO1(const std::string& fileName, const osgDB::ReaderWriter::Options*) const
188 {
189     lwObject* lw = lw_object_read(fileName.c_str(),osg::notify(osg::INFO));
190     if (!lw)
191         return ReadResult::FILE_NOT_HANDLED;
192 
193     OSG_INFO << "faces " << lw->face_cnt << std::endl;
194     OSG_INFO << "materials " << lw->material_cnt << std::endl;
195     OSG_INFO << "vertices " << lw->vertex_cnt << std::endl;
196 
197     typedef std::map<int,GeometryCollection> MaterialToGeometryCollectionMap;
198     MaterialToGeometryCollectionMap mtgcm;
199 
200     // bin the indices for each material into the mtis;
201     int i;
202     for (i = 0; i < lw->face_cnt; ++i)
203     {
204         lwFace& face = lw->face[i];
205         if (face.index_cnt>=3)
206         {
207             GeometryCollection& gc = mtgcm[face.material];
208             gc._numPoints += face.index_cnt;
209             gc._numPrimitives += 1;
210             if (face.texcoord) gc._numPrimitivesWithTexCoords += 1;
211         }
212     }
213 
214     MaterialToGeometryCollectionMap::iterator itr;
215     for(itr=mtgcm.begin(); itr!=mtgcm.end(); ++itr)
216     {
217         GeometryCollection& gc = itr->second;
218 
219         if (gc._numPrimitives)
220         {
221             lwMaterial& lw_material = lw->material[itr->first];
222 
223             gc._geom = new osg::Geometry;
224 
225             osg::Vec3Array* vertArray = new osg::Vec3Array(gc._numPoints);
226             gc._vertices = vertArray->begin();
227             gc._geom->setVertexArray(vertArray);
228 
229             // set up color.
230             osg::Vec4Array* colors = new osg::Vec4Array(1);
231             (*colors)[0].set(lw_material.r,
232                              lw_material.g,
233                              lw_material.b,
234                              1.0f);
235 
236             gc._geom->setColorArray(colors, osg::Array::BIND_OVERALL);
237 
238             // set up texture if needed.
239             if (gc._numPrimitivesWithTexCoords==gc._numPrimitives)
240             {
241                 if (lw_material.ctex.flags && strlen(lw_material.ctex.name)!=0)
242                 {
243                     OSG_INFO << "ctex " << lw_material.ctex.name << std::endl;
244                     osg::ref_ptr<osg::Image> image = osgDB::readRefImageFile(lw_material.ctex.name);
245                     if (image.valid())
246                     {
247                         // create state
248                         osg::StateSet* stateset = new osg::StateSet;
249 
250                         // create texture
251                         osg::Texture2D* texture = new osg::Texture2D;
252                         texture->setImage(image.get());
253 
254                         // texture wrap mode
255                         static osg::Texture::WrapMode mode[] = {
256                             osg::Texture::CLAMP,
257                             osg::Texture::CLAMP,
258                             osg::Texture::REPEAT,
259                             osg::Texture::MIRROR
260                         };
261                         texture->setWrap(osg::Texture::WRAP_S,
262                                          mode[lw_material.ctex.u_wrap]);
263                         texture->setWrap(osg::Texture::WRAP_T,
264                                          mode[lw_material.ctex.v_wrap]);
265 
266                         stateset->setTextureAttributeAndModes(0,texture,osg::StateAttribute::ON);
267                         gc._texturesActive=true;
268 
269                         gc._geom->setStateSet(stateset);
270 
271                         osg::Vec2Array* texcoordArray = new osg::Vec2Array(gc._numPoints);
272                         gc._texcoords = texcoordArray->begin();
273                         gc._geom->setTexCoordArray(0,texcoordArray);
274                     }
275                 }
276             }
277         }
278     }
279 
280 
281     for (i = 0; i < lw->face_cnt; ++i)
282     {
283         lwFace& face = lw->face[i];
284         if (face.index_cnt>=3)
285         {
286             GeometryCollection& gc = mtgcm[face.material];
287 
288             osg::PrimitiveSet::Mode mode;
289             switch(face.index_cnt)
290             {
291                 case(0):
292                     mode = osg::PrimitiveSet::POINTS;
293                     break;
294                 case(1):
295                     mode = osg::PrimitiveSet::POINTS;
296                     break;
297                 case(2):
298                     mode = osg::PrimitiveSet::LINES;
299                     break;
300                 case(3):
301                     mode = osg::PrimitiveSet::TRIANGLES;
302                     break;
303                 case(4):
304                     mode = osg::PrimitiveSet::QUADS;
305                     break;
306                 default:
307                     mode = osg::PrimitiveSet::POLYGON;
308                     break;
309             }
310 
311             gc._geom->addPrimitiveSet(new osg::DrawArrays(mode,gc._coordCount,face.index_cnt));
312             gc._coordCount += face.index_cnt;
313 
314             // From the spec_low.lxt :
315             //   "By convention, the +X direction is to the right or east, the +Y
316             //    direction is upward, and the +Z direction is forward or north"
317             // However, the osg sticks to the more conventional, y to the north,
318             // z upwards, x is the same - rigth/east.  To handle this difference
319             // simple exchange osg_z for lwo_y, and osg_y for lwo_z.
320 
321             // add the corners in reverse order to reverse the windings, to keep the anticlockwise rotation of polys.
322             int j;
323             for(j=face.index_cnt-1;j>=0;--j)
324             {
325                 (*gc._vertices++).set(lw->vertex[face.index[j]*3], lw->vertex[face.index[j]*3+2], lw->vertex[face.index[j]*3+1]);
326             }
327 
328             if (gc._texturesActive && face.texcoord)
329             {
330                 for(j=face.index_cnt-1;j>=0;--j)
331                 {
332                     (*gc._texcoords++).set(face.texcoord[j*2],face.texcoord[j*2+1]);
333                 }
334             }
335         }
336     }
337 
338     osg::Geode* geode = new osg::Geode;
339 
340     osgUtil::Tessellator tessellator;
341 
342     // add everything into the Geode.
343     osgUtil::SmoothingVisitor smoother;
344     for(itr=mtgcm.begin();
345         itr!=mtgcm.end();
346         ++itr)
347     {
348         GeometryCollection& gc = itr->second;
349         if (gc._geom)
350         {
351 
352             tessellator.retessellatePolygons(*gc._geom);
353 
354             smoother.smooth(*gc._geom);
355 
356             geode->addDrawable(gc._geom);
357         }
358 
359     }
360 
361     // free
362     lw_object_free(lw);
363 
364     return geode;
365 }
366