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

Revision 13497, 13.5 kB (checked in by robert, 2 days ago)

Added shaders to support experimental shader based displacement mapping technique osgTerrain::ShaderTerrain?.

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