root/OpenSceneGraph/trunk/src/osgPlugins/mdl/VTXReader.cpp @ 13041

Revision 13041, 12.1 kB (checked in by robert, 2 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
Line 
1#include <osg/Geometry>
2#include <osg/Group>
3#include <osg/Node>
4#include <osg/Notify>
5#include <osg/StateSet>
6#include <osg/Switch>
7#include <osg/LOD>
8#include <iostream>
9
10#include "VTXReader.h"
11
12
13using namespace mdl;
14using namespace osg;
15using namespace osgDB;
16
17
18VTXReader::VTXReader(VVDReader * vvd, MDLRoot * mdlRoot)
19{
20    // Save the VVD reader, as we'll need it to read vertex data
21    vvd_reader = vvd;
22
23    // Save the root of the MDL tree, as we'll need it to make sure we
24    // index the vertex data properly
25    mdl_root = mdlRoot;
26
27    // Initialize the root group
28    model_root = NULL;
29}
30
31
32VTXReader::~VTXReader()
33{
34}
35
36
37ref_ptr<Group> VTXReader::processBodyPart(std::istream * str, int offset,
38                                          BodyPart * currentPart)
39{
40    int               i;
41    VTXBodyPart       part;
42    Model *           currentModel;
43    ref_ptr<Group>    partSwitch;
44    ref_ptr<Group>    modelGroup;
45
46    // Seek to the body part
47    str->seekg(offset);
48
49    // Read it
50    str->read((char *) &part, sizeof(VTXBodyPart));
51
52    // If there is more than one model, create a switch to select between them
53    // (it seems that only one model is supposed to be seen at a given time,
54    // but I don't know the mechanism in the engine that selects a desired
55    // model)
56    if (part.num_models > 1)
57    {
58        partSwitch = new Switch();
59    }
60
61    // Process the models
62    for (i = 0; i < part.num_models; i++)
63    {
64        // Get the corresponding MDL model from the current body part
65        currentModel = currentPart->getModel(i);
66
67        // Process the model
68        modelGroup = processModel(str,
69                                  offset + part.model_offset +
70                                  (i * sizeof(VTXModel)),
71                                  currentModel);
72
73        // If there is more than one model, add this model to the part group
74        if (part.num_models > 1)
75        {
76            // Add the model to the switch
77            partSwitch->addChild(modelGroup.get());
78
79            // If this is the first child, turn it on, otherwise turn it off
80            if (i == 0)
81                ((osg::Switch *)partSwitch.get())->setValue(i, true);
82            else
83                ((osg::Switch *)partSwitch.get())->setValue(i, false);
84        }
85    }
86
87    // If there is only one model, just return it
88    if (part.num_models == 1)
89        return modelGroup;
90    else
91        return partSwitch;
92}
93
94
95ref_ptr<Group> VTXReader::processModel(std::istream * str, int offset,
96                                       Model * currentModel)
97{
98    int              i;
99    VTXModel         model;
100    float            lastDistance;
101    float            distance;
102    LOD *            lodNode = 0;
103    ref_ptr<Group>   group;
104    ref_ptr<Group>   result;
105
106    // Seek to the model
107    str->seekg(offset);
108
109    // Read it
110    str->read((char *) &model, sizeof(VTXModel));
111
112    // If we have multiple LODs, create an LOD node for them
113    if (model.num_lods > 1)
114        lodNode = new LOD();
115
116    // Initialize the distances
117    lastDistance = 0.0;
118    distance = 0.0;
119
120    // Process the LODs
121    for (i = 0; i < model.num_lods; i++)
122    {
123        // Process the LOD group, passing the current MDL model through
124        group = processLOD(i, &distance, str,
125                           offset + model.lod_offset +
126                           (i * sizeof(VTXModelLOD)),
127                           currentModel);
128
129        // If this isn't the only LOD, add it to the LOD node
130        if (model.num_lods > 1)
131        {
132            lodNode->addChild(group.get());
133
134            // Fix the LOD distances
135            if (distance < 0)
136            {
137                // Fix negative distance (my best guess is that these mean
138                // for the LOD to never switch out)
139                distance = 100000.0;
140            }
141
142            // Set the ranges on the previous LOD (now that we know the
143            // switch point for this one)
144            if (i > 0)
145                lodNode->setRange(i-1, lastDistance, distance);
146
147            lastDistance = distance;
148        }
149    }
150
151    if (i > 1)
152       lodNode->setRange(i-1, lastDistance, 100000.0);
153
154    // Return either the LOD node or the single LOD group
155    if (model.num_lods > 1)
156        result = lodNode;
157    else
158        result = group;
159
160    return result;
161}
162
163
164ref_ptr<Group> VTXReader::processLOD(int lodNum, float * distance,
165                                     std::istream * str, int offset,
166                                     Model * currentModel)
167{
168    int              i;
169    VTXModelLOD      lod;
170    Mesh *           currentMesh;
171    int              vertexOffset;
172    ref_ptr<Group>   lodGroup;
173    ref_ptr<Geode>   geode;
174
175    // Seek to the LOD
176    str->seekg(offset);
177
178    // Read it
179    str->read((char *) &lod, sizeof(VTXModelLOD));
180
181    // Create a group to hold this LOD
182    lodGroup = new Group();
183
184    // Process the meshes
185    vertexOffset = currentModel->getVertexBase();
186    for (i = 0; i < lod.num_meshes; i++)
187    {
188        // Get the corresponding MDL mesh from the current model
189        currentMesh = currentModel->getMesh(i);
190
191        // Process the mesh to get a geode
192        geode = processMesh(lodNum, str,
193                            offset + lod.mesh_offset + (i * VTX_MESH_SIZE),
194                            vertexOffset);
195
196        // Set the geode's state set based on the current mesh node's state
197        // set
198        geode->setStateSet(currentMesh->getStateSet());
199
200        // Add the geode to the group
201        lodGroup->addChild(geode.get());
202
203        // Add the number of vertices for this mesh at this LOD to the offset,
204        // so we can start the next mesh at the proper vertex ID
205        vertexOffset += currentMesh->getNumLODVertices(lodNum);
206    }
207
208    // Set the distance for this LOD
209    *distance = lod.switch_point;
210
211    // Return the LOD group that we created
212    return lodGroup;
213}
214
215
216ref_ptr<Geode> VTXReader::processMesh(int lodNum, std::istream * str,
217                                      int offset, int vertexOffset)
218{
219    int                 i;
220    VTXMesh             mesh;
221    ref_ptr<Geode>      geode;
222    ref_ptr<Geometry>   geom;
223
224    // Seek to the mesh
225    str->seekg(offset);
226
227    // Read it
228    str->read((char *) &mesh, VTX_MESH_SIZE);
229
230    // Create a geode to hold the geometry
231    geode = new Geode();
232
233    // Process the strip groups
234    for (i = 0; i < mesh.num_strip_groups; i++)
235    {
236        // Process the strip group to get a Geometry
237        geom = processStripGroup(lodNum, str,
238            offset + mesh.strip_group_offset + (i * VTX_STRIP_GROUP_SIZE),
239            vertexOffset);
240
241        // Add the geometry to the geode
242        geode->addDrawable(geom.get());
243    }
244
245    // Return the geode
246    return geode;
247}
248
249
250ref_ptr<Geometry> VTXReader::processStripGroup(int lodNum, std::istream * str,
251                                               int offset, int vertexOffset)
252{
253    int                       i;
254    VTXStripGroup             stripGroup;
255    VTXVertex                 vtxVertex;
256    int                       vertexID;
257    ref_ptr<Vec3Array>        vertexArray;
258    ref_ptr<Vec3Array>        normalArray;
259    ref_ptr<Vec2Array>        texcoordArray;
260    unsigned short            index;
261    unsigned short *          indexArray;
262    ref_ptr<Geometry>         geom;
263    ref_ptr<PrimitiveSet>     primSet;
264
265    // Seek to the strip group
266    str->seekg(offset);
267
268    // Read it
269    str->read((char *) &stripGroup, VTX_STRIP_GROUP_SIZE);
270
271    // Create and fill the vertex arrays
272    vertexArray = new Vec3Array();
273    normalArray = new Vec3Array();
274    texcoordArray = new Vec2Array();
275    str->seekg(offset + stripGroup.vertex_offset);
276    for (i = 0; i < stripGroup.num_vertices; i++)
277    {
278        // Get the vertex ID from the strip group
279        str->read((char *) &vtxVertex, VTX_VERTEX_SIZE);
280        vertexID = vtxVertex.orig_mesh_vertex_id + vertexOffset;
281
282        // Get the corresponding vertex, normal, texture coordinates from the
283        // VVD file
284        vertexArray->push_back(vvd_reader->getVertex(lodNum, vertexID));
285        normalArray->push_back(vvd_reader->getNormal(lodNum, vertexID));
286        texcoordArray->push_back(vvd_reader->getTexCoords(lodNum, vertexID));
287    }
288
289    // Create the geometry and add the vertex data to it
290    geom = new Geometry();
291    geom->setVertexArray(vertexArray.get());
292    geom->setNormalArray(normalArray.get());
293    geom->setNormalBinding(Geometry::BIND_PER_VERTEX);
294    geom->setTexCoordArray(0, texcoordArray.get());
295
296    // Create and fill the index array
297    indexArray = new unsigned short[stripGroup.num_indices];
298    str->seekg(offset + stripGroup.index_offset);
299    for (i = 0; i < stripGroup.num_indices; i++)
300    {
301        // Get the index from the file
302        str->read((char *) &index, sizeof(unsigned short));
303
304        // Add to the array
305        indexArray[i] = index;
306    }
307
308    // Process the strips
309    for (i = 0; i < stripGroup.num_strips; i++)
310    {
311        // Process the strip to create a triangle list or strip
312        primSet = processStrip(indexArray, str,
313            offset + stripGroup.strip_offset + (i * VTX_STRIP_SIZE));
314
315        // Add the primitive set to the geometry
316        geom->addPrimitiveSet(primSet.get());
317    }
318
319    // Clean up
320    delete [] indexArray;
321
322    // Return the geometry
323    return geom;
324}
325
326
327ref_ptr<PrimitiveSet> VTXReader::processStrip(unsigned short * indexArray,
328                                              std::istream * str,
329                                              int offset)
330{
331    VTXStrip                strip;
332    DrawElementsUShort *    drawElements;
333    ref_ptr<PrimitiveSet>   primSet;
334    unsigned short *        start;
335    unsigned short *        end;
336
337    // Seek to the strip
338    str->seekg(offset);
339
340    // Read it.  We have to do this in a kind of screwy way because of the
341    // weird byte packing.  Valve uses pragma pack, but we can't do that
342    // because it's non-portable.  Of course, I'm assuming a 4-byte alignment
343    // here, which might also be non-portable...
344    str->read((char *) &strip, VTX_STRIP_SIZE - 8);
345    str->read((char *) &strip.num_bone_state_changes, 8);
346
347    // Get the range of indices in question from the strip
348    start = &indexArray[strip.index_offset];
349    end = &indexArray[strip.index_offset + strip.num_indices];
350
351    // Create the primitive set (based on the flag)
352    if (strip.strip_flags & STRIP_IS_TRI_LIST)
353        drawElements =
354            new DrawElementsUShort(PrimitiveSet::TRIANGLES, start, end);
355    else
356        drawElements =
357            new DrawElementsUShort(PrimitiveSet::TRIANGLE_STRIP, start, end);
358
359    // Flip the indices to get the front faces correct
360    std::reverse(drawElements->begin(), drawElements->end());
361
362    // Return the primitive set
363    primSet = drawElements;
364    return primSet;
365}
366
367
368bool VTXReader::readFile(const std::string & file)
369{
370    osgDB::ifstream *   vtxFile;
371    VTXHeader           header;
372    int                 i;
373    BodyPart *          currentPart;
374    ref_ptr<Group>      partGroup;
375    Group *             rootGroup;
376
377    // Remember the map name
378    vtx_name = getStrippedName(file);
379
380    vtxFile = new osgDB::ifstream(file.c_str(), std::ios::binary);
381    if (!vtxFile || vtxFile->fail())
382    {
383        OSG_NOTICE << "Vertex index file not found" << std::endl;
384        return false;
385    }
386
387    // Read the header
388    vtxFile->read((char *) &header, sizeof(VTXHeader));
389
390    // Make sure the version is one that we handle
391    // TODO:  Not sure which versions are valid yet
392
393    // Create the root group
394    rootGroup = new Group();
395
396    // Process the body parts
397    for (i = 0; i < header.num_body_parts; i++)
398    {
399        // Get the corresponding body part from the MDL tree
400        currentPart = mdl_root->getBodyPart(i);
401
402        // Process the body part
403        partGroup = processBodyPart(vtxFile,
404                                    header.body_part_offset +
405                                        (i * sizeof(VTXBodyPart)),
406                                    currentPart);
407
408        // Add the result to the root group
409        rootGroup->addChild(partGroup.get());
410    }
411
412    // Set the model's root node
413    model_root = rootGroup;
414
415    // Close the file
416    vtxFile->close();
417    delete vtxFile;
418
419    return true;
420}
421
422
423ref_ptr<Node> VTXReader::getModel()
424{
425    return model_root;
426}
Note: See TracBrowser for help on using the browser.