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