root/OpenSceneGraph/trunk/src/osgPlugins/md2/ReaderWriterMD2.cpp @ 13557

Revision 13557, 13.4 kB (checked in by robert, 44 hours ago)

Reordered method implemenations to make it easier to compare similar methods

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * ReaderWriterMD2.cpp
3 *
4 * MD2 Reading code
5 *
6 * Author(s):  Vladimir Vukicevic <vladimir@pobox.com>
7 *
8 */
9
10#include <osg/TexEnv>
11#include <osg/CullFace>
12
13#include <osg/Geode>
14#include <osg/Geometry>
15#include <osg/Material>
16#include <osg/Image>
17#include <osg/Texture2D>
18
19#include <osg/Switch>
20#include <osg/Sequence>
21
22#include <osg/Notify>
23#include <osgDB/Registry>
24#include <osgDB/ReadFile>
25#include <osgDB/FileNameUtils>
26#include <osgDB/FileUtils>
27
28#include <stdlib.h>
29#include <string.h>
30#include <fcntl.h>
31
32#if defined(WIN32) && !defined(__CYGWIN__)
33include <io.h>
34#else
35include <unistd.h>
36#endif
37
38#include <sys/stat.h>
39
40#include <assert.h>
41
42static osg::Node* load_md2 (const char *filename, const osgDB::ReaderWriter::Options* options);
43
44class ReaderWriterMD2 : public osgDB::ReaderWriter
45{
46public:
47    ReaderWriterMD2 ()
48    {
49        supportsExtension("md2","Quak2 MD format");
50    }
51
52    virtual const char* className () const {
53        return "Quake MD2 Reader";
54    }
55
56    virtual ReadResult readNode (const std::string& filename, const osgDB::ReaderWriter::Options* options) const;
57};
58
59REGISTER_OSGPLUGIN(md2, ReaderWriterMD2)
60
61osgDB::ReaderWriter::ReadResult
62ReaderWriterMD2::readNode (const std::string& file, const osgDB::ReaderWriter::Options* options) const
63{
64    std::string ext = osgDB::getLowerCaseFileExtension(file);
65    if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
66
67    std::string fileName = osgDB::findDataFile( file, options );
68    if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
69
70    // code for setting up the database path so that internally referenced file are searched for on relative paths.
71    osg::ref_ptr<Options> local_opt = options ? static_cast<Options*>(options->clone(osg::CopyOp::SHALLOW_COPY)) : new Options;
72    local_opt->setDatabasePath(osgDB::getFilePath(fileName));
73
74    return load_md2 (fileName.c_str(), options);
75}
76
77
78/////////////////// MD2 parsing code //////////////////////
79
80typedef struct {
81    int magic;
82    int version;
83    int skinWidth;
84    int skinHeight;
85    int frameSize;                  // size of each frame in bytes
86    int numSkins;
87    int numVertices;                // number of vertices in each frame
88    int numTexcoords;               // number of texcoords in each frame (usually same as numVertices)
89    int numTriangles;               // number of triangles in each frame
90    int numGlCommands;
91    int numFrames;                  // number of frames
92    int offsetSkins;
93    int offsetTexCoords;
94    int offsetTriangles;
95    int offsetFrames;
96    int offsetGlCommands;           // num dwords in gl commands list
97    int offsetEnd;
98} MD2_HEADER;
99
100#define MD2_HEADER_MAGIC 0x32504449
101
102typedef struct {
103    unsigned char vertex[3];
104    unsigned char lightNormalIndex;
105} MD2_VERTEX;
106
107typedef struct {
108    float scale[3];
109    float translate[3];
110    char name[16];
111    MD2_VERTEX vertices[1];
112} MD2_FRAME;
113
114typedef struct {
115    short vertexIndices[3];
116    short textureIndices[3];
117} MD2_TRIANGLE;
118
119typedef struct {
120    float s;
121    float t;
122    int vertexIndex;
123} MD2_GLCOMMANDVERTEX;
124
125typedef struct {
126    short s;
127    short t;
128} MD2_TEXTURECOORDINATE;
129
130typedef struct {
131    char name[64];
132} MD2_SKIN;
133
134#define NUMVERTEXNORMALS 162
135float g_md2VertexNormals[NUMVERTEXNORMALS][3] = {
136#include "anorms.h"
137};
138
139osg::Vec3Array *g_md2NormalsArray = NULL;
140
141//
142// the result will be an osg::Switch
143// whose children will be osg::Sequences, one for each animation
144// the children of the osg::Sequence will be the osg::Geode that contains the
145// osg::Geometry of the relevant frame.  The osg::Geode will have a osg::StateSet
146// containing the texture information.
147//
148
149// this is also quite non-portable to non-little-endian architectures
150
151static osg::Node*
152load_md2 (const char *filename, const osgDB::ReaderWriter::Options* options)
153{
154    struct stat st;
155    void *mapbase;
156    int file_fd;
157    osg::Node* result = NULL;
158
159    if (stat (filename, &st) < 0) {
160        return NULL;
161    }
162
163    file_fd = open (filename, O_RDONLY);
164    if (file_fd <= 0) {
165        close (file_fd);
166        return NULL;
167    }
168
169    mapbase = malloc (st.st_size);
170    if (!mapbase)
171    {
172        close (file_fd);
173        return NULL;
174    }
175
176    if (read(file_fd, mapbase, st.st_size)==0)
177    {
178        close (file_fd);
179        if (mapbase) free(mapbase);
180        return NULL;
181    }
182
183    if (g_md2NormalsArray == NULL) {
184        g_md2NormalsArray = new osg::Vec3Array;
185        for (int i = 0; i < NUMVERTEXNORMALS; i++)
186            g_md2NormalsArray->push_back (osg::Vec3 (g_md2VertexNormals[i][0], g_md2VertexNormals[i][1], g_md2VertexNormals[i][2]));
187    }
188
189
190    MD2_HEADER *md2_header = (MD2_HEADER *) mapbase;
191    if (md2_header->magic != MD2_HEADER_MAGIC || md2_header->version != 8) {
192#if 0
193        munmap (mapbase);
194#else
195        free (mapbase);
196#endif
197        close (file_fd);
198        return NULL;
199    }
200
201    MD2_SKIN *md2_skins = (MD2_SKIN *) ((unsigned char *) mapbase + md2_header->offsetSkins);
202    MD2_TEXTURECOORDINATE *md2_texcoords = (MD2_TEXTURECOORDINATE *) ((unsigned char *) mapbase + md2_header->offsetTexCoords);
203    MD2_TRIANGLE *md2_triangles = (MD2_TRIANGLE *) ((unsigned char *) mapbase + md2_header->offsetTriangles);
204
205    osg::Switch *base_switch = new osg::Switch ();
206    osg::Sequence *current_sequence = NULL;
207
208    // read in the frame info into a vector
209    const char *last_frame_name = NULL;
210
211    osg::Vec3Array *vertexCoords = NULL;
212    osg::Vec2Array *texCoords = NULL;
213    osg::UIntArray *vertexIndices = NULL;
214    osg::UIntArray *texIndices = NULL;
215    osg::Vec3Array *normalCoords = NULL;
216    osg::UIntArray *normalIndices = NULL;
217
218    // load the texture skins
219
220    // there is code here to support multiple skins, but there's no need for it
221    // since we really just want to support one; we have no way of returning more
222    // than one to the user in any case.
223#if 0
224    std::vector<osg::Texture2D*> skin_textures;
225
226    for (int si = 0; si < md2_header->numSkins; si++)
227    {
228        osg::ref_ptr<osg::Image> img;
229        osg::ref_ptr<osg::Texture2D> tex;
230        std::string imgname (md2_skins[si].name);
231
232        // first try loading the imgname straight
233        img = osgDB::readRefImageFile (imgname, options);
234        if (img.valid()) {
235            tex = new osg::Texture2D;
236            tex->setImage (img);
237            skin_textures.push_back (tex);
238            continue;
239        }
240
241        // we failed, so check if it's a PCX image
242        if (imgname.size() > 4 &&
243            osgDB::equalCaseInsensitive (imgname.substr (imgname.size() - 3, 3), "pcx"))
244        {
245            // it's a pcx, so try bmp and tga, since pcx sucks
246            std::string basename = imgname.substr (0, imgname.size() - 3);
247            img = osgDB::readRefImageFile (basename + "bmp", options);
248            if (img.valid()) {
249                tex = new osg::Texture2D;
250                tex->setImage (img.get());
251                skin_textures.push_back (tex);
252                continue;
253            }
254
255            img = osgDB::readRefImageFile (basename + "tga", options);
256            if (img.valid()) {
257                tex = new osg::Texture2D;
258                tex->setImage (img,get());
259                skin_textures.push_back (tex);
260                continue;
261            }
262        }
263
264        // couldn't find the referenced texture skin for this model
265        skin_textures.push_back (NULL);
266        OSG_WARN << "MD2 Loader: Couldn't load skin " << imgname << " referenced by model " << filename << std::endl;
267    }
268#else
269    // load the single skin
270    osg::ref_ptr<osg::Image> skin_image;
271    osg::ref_ptr<osg::Texture2D> skin_texture = NULL;
272
273    if (md2_header->numSkins > 0) {
274        std::string imgname (md2_skins[0].name);
275
276        do {
277            // first try loading the imgname straight
278            skin_image = osgDB::readRefImageFile(imgname, options);
279            if (skin_image.valid()) break;
280
281            // we failed, so check if it's a PCX image
282            if (imgname.size() > 4 &&
283                osgDB::equalCaseInsensitive (imgname.substr (imgname.size() - 3, 3), "pcx"))
284                {
285                // it's a pcx, so try bmp and tga, since pcx sucks
286                std::string basename = imgname.substr (0, imgname.size() - 3);
287                skin_image = osgDB::readRefImageFile (basename + "bmp", options);
288                if (skin_image.valid()) break;
289
290                skin_image = osgDB::readRefImageFile (basename + "tga", options);
291                if (skin_image.valid()) break;
292            }
293        } while (0);
294
295        if (skin_image.valid()) {
296            skin_texture = new osg::Texture2D;
297            skin_texture->setImage (skin_image.get());
298            skin_texture->setFilter (osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST);
299        } else {
300            // couldn't find the referenced texture skin for this model
301            OSG_WARN << "MD2 Loader: Couldn't load skin " << imgname << " referenced by model " << filename << std::endl;
302        }
303    }
304
305#endif
306
307    int sequence_frame = 0;
308
309    for (int curFrame = 0; curFrame < md2_header->numFrames; curFrame++) {
310        //        std::cerr << "Num vertices " << md2_header->numVertices << std::endl;
311
312        //long *command = (long *) ((unsigned char *) mapbase + md2_header->offsetGlCommands);
313
314        MD2_FRAME *frame = (MD2_FRAME *) ((unsigned char *) mapbase + md2_header->offsetFrames + (md2_header->frameSize * curFrame));
315        MD2_VERTEX *frame_vertices = frame->vertices;
316
317
318        //        std::cerr << "Reading frame " << curFrame << " (gl offset: " << md2_header->offsetGlCommands << ") name: " << frame->name << std::endl;
319
320        int last_len = last_frame_name ? strcspn (last_frame_name, "0123456789") : 0;
321        int cur_len = strcspn (frame->name, "0123456789");
322
323        if (last_len != cur_len || strncmp (last_frame_name, frame->name, last_len) != 0) {
324            if (current_sequence) {
325                current_sequence->setInterval (osg::Sequence::LOOP, 0, -1);
326                base_switch->addChild (current_sequence);
327            }
328
329            current_sequence = new osg::Sequence ();
330            current_sequence->setMode (osg::Sequence::START);
331            current_sequence->setDuration (1.0f, -1);
332            sequence_frame = 0;
333
334            current_sequence->setName (std::string (frame->name, cur_len));
335        }
336
337        vertexCoords = new osg::Vec3Array;
338        normalCoords = new osg::Vec3Array;
339
340        for (int vi = 0; vi < md2_header->numVertices; vi++) {
341            vertexCoords->push_back
342                (osg::Vec3
343                 (frame_vertices[vi].vertex[0] * frame->scale[0] + frame->translate[0],
344                  -1 * (frame_vertices[vi].vertex[2] * frame->scale[2] + frame->translate[2]),
345                  frame_vertices[vi].vertex[1] * frame->scale[1] + frame->translate[1]));
346            osg::Vec3 z = (*g_md2NormalsArray) [frame_vertices[vi].lightNormalIndex];
347            normalCoords->push_back (z);
348        }
349
350        if (curFrame == 0) {
351            vertexIndices = new osg::UIntArray;
352            normalIndices = new osg::UIntArray;
353
354            texCoords = new osg::Vec2Array;
355            texIndices = new osg::UIntArray;
356
357            for (int vi = 0; vi < md2_header->numTexcoords; vi++) {
358                texCoords->push_back
359                    (osg::Vec2 ((float) md2_texcoords[vi].s / md2_header->skinWidth,
360                                1.0f - (float) md2_texcoords[vi].t / md2_header->skinHeight));
361            }
362
363            for (int ti = 0; ti < md2_header->numTriangles; ti++) {
364                vertexIndices->push_back (md2_triangles[ti].vertexIndices[0]);
365                vertexIndices->push_back (md2_triangles[ti].vertexIndices[1]);
366                vertexIndices->push_back (md2_triangles[ti].vertexIndices[2]);
367
368                normalIndices->push_back (md2_triangles[ti].vertexIndices[0]);
369                normalIndices->push_back (md2_triangles[ti].vertexIndices[1]);
370                normalIndices->push_back (md2_triangles[ti].vertexIndices[2]);
371
372                texIndices->push_back (md2_triangles[ti].textureIndices[0]);
373                texIndices->push_back (md2_triangles[ti].textureIndices[1]);
374                texIndices->push_back (md2_triangles[ti].textureIndices[2]);
375            }
376        }
377
378        deprecated_osg::Geometry *geom = new deprecated_osg::Geometry;
379
380        geom->setVertexArray (vertexCoords);
381        geom->setVertexIndices (vertexIndices);
382
383        geom->setTexCoordArray (0, texCoords);
384        geom->setTexCoordIndices (0, texIndices);
385
386        geom->setNormalArray (normalCoords);
387        geom->setNormalIndices (normalIndices);
388        geom->setNormalBinding (deprecated_osg::Geometry::BIND_PER_VERTEX);
389
390        geom->addPrimitiveSet (new osg::DrawArrays (osg::PrimitiveSet::TRIANGLES, 0, vertexIndices->size ()));
391
392        osg::Geode *geode = new osg::Geode;
393        geode->addDrawable (geom);
394
395        current_sequence->addChild (geode);
396        current_sequence->setTime (sequence_frame, 0.2f);
397        sequence_frame++;
398
399        last_frame_name = frame->name;
400    }
401
402    if (current_sequence) {
403        current_sequence->setInterval (osg::Sequence::LOOP, 0, -1);
404        base_switch->addChild (current_sequence);
405    }
406
407    osg::StateSet *state = new osg::StateSet;
408
409    if (skin_texture != NULL) {
410        state->setTextureAttributeAndModes (0, skin_texture.get(), osg::StateAttribute::ON);
411    }
412    base_switch->setStateSet (state);
413
414    //base_switch->setAllChildrenOff ();
415
416    base_switch->setSingleChildOn(0);
417
418    result = base_switch;
419
420#if 0
421    munamp (mapbase);
422#else
423    free (mapbase);
424#endif
425    close (file_fd);
426    return result;
427}
Note: See TracBrowser for help on using the browser.