/Semester 7/GameEngine/OpenSceneGraph-3.4.0/src/osgPlugins/mdl/VTXReader.cpp
C++ | 425 lines | 251 code | 89 blank | 85 comment | 23 complexity | 6cbb598577bae5d4e917a2235eeb3bfa MD5 | raw file
- #include <osg/Geometry>
- #include <osg/Group>
- #include <osg/Node>
- #include <osg/Notify>
- #include <osg/StateSet>
- #include <osg/Switch>
- #include <osg/LOD>
- #include <iostream>
- #include "VTXReader.h"
- using namespace mdl;
- using namespace osg;
- using namespace osgDB;
- VTXReader::VTXReader(VVDReader * vvd, MDLRoot * mdlRoot)
- {
- // Save the VVD reader, as we'll need it to read vertex data
- vvd_reader = vvd;
- // Save the root of the MDL tree, as we'll need it to make sure we
- // index the vertex data properly
- mdl_root = mdlRoot;
- // Initialize the root group
- model_root = NULL;
- }
- VTXReader::~VTXReader()
- {
- }
- ref_ptr<Group> VTXReader::processBodyPart(std::istream * str, int offset,
- BodyPart * currentPart)
- {
- int i;
- VTXBodyPart part;
- Model * currentModel;
- ref_ptr<Group> partSwitch;
- ref_ptr<Group> modelGroup;
- // Seek to the body part
- str->seekg(offset);
- // Read it
- str->read((char *) &part, sizeof(VTXBodyPart));
- // If there is more than one model, create a switch to select between them
- // (it seems that only one model is supposed to be seen at a given time,
- // but I don't know the mechanism in the engine that selects a desired
- // model)
- if (part.num_models > 1)
- {
- partSwitch = new Switch();
- }
- // Process the models
- for (i = 0; i < part.num_models; i++)
- {
- // Get the corresponding MDL model from the current body part
- currentModel = currentPart->getModel(i);
- // Process the model
- modelGroup = processModel(str,
- offset + part.model_offset +
- (i * sizeof(VTXModel)),
- currentModel);
- // If there is more than one model, add this model to the part group
- if (part.num_models > 1)
- {
- // Add the model to the switch
- partSwitch->addChild(modelGroup.get());
- // If this is the first child, turn it on, otherwise turn it off
- if (i == 0)
- ((osg::Switch *)partSwitch.get())->setValue(i, true);
- else
- ((osg::Switch *)partSwitch.get())->setValue(i, false);
- }
- }
- // If there is only one model, just return it
- if (part.num_models == 1)
- return modelGroup;
- else
- return partSwitch;
- }
- ref_ptr<Group> VTXReader::processModel(std::istream * str, int offset,
- Model * currentModel)
- {
- int i;
- VTXModel model;
- float lastDistance;
- float distance;
- LOD * lodNode = 0;
- ref_ptr<Group> group;
- ref_ptr<Group> result;
- // Seek to the model
- str->seekg(offset);
- // Read it
- str->read((char *) &model, sizeof(VTXModel));
- // If we have multiple LODs, create an LOD node for them
- if (model.num_lods > 1)
- lodNode = new LOD();
- // Initialize the distances
- lastDistance = 0.0;
- distance = 0.0;
- // Process the LODs
- for (i = 0; i < model.num_lods; i++)
- {
- // Process the LOD group, passing the current MDL model through
- group = processLOD(i, &distance, str,
- offset + model.lod_offset +
- (i * sizeof(VTXModelLOD)),
- currentModel);
- // If this isn't the only LOD, add it to the LOD node
- if (model.num_lods > 1)
- {
- lodNode->addChild(group.get());
- // Fix the LOD distances
- if (distance < 0)
- {
- // Fix negative distance (my best guess is that these mean
- // for the LOD to never switch out)
- distance = 100000.0;
- }
- // Set the ranges on the previous LOD (now that we know the
- // switch point for this one)
- if (i > 0)
- lodNode->setRange(i-1, lastDistance, distance);
- lastDistance = distance;
- }
- }
- if (i > 1)
- lodNode->setRange(i-1, lastDistance, 100000.0);
- // Return either the LOD node or the single LOD group
- if (model.num_lods > 1)
- result = lodNode;
- else
- result = group;
- return result;
- }
- ref_ptr<Group> VTXReader::processLOD(int lodNum, float * distance,
- std::istream * str, int offset,
- Model * currentModel)
- {
- int i;
- VTXModelLOD lod;
- Mesh * currentMesh;
- int vertexOffset;
- ref_ptr<Group> lodGroup;
- ref_ptr<Geode> geode;
- // Seek to the LOD
- str->seekg(offset);
- // Read it
- str->read((char *) &lod, sizeof(VTXModelLOD));
- // Create a group to hold this LOD
- lodGroup = new Group();
- // Process the meshes
- vertexOffset = currentModel->getVertexBase();
- for (i = 0; i < lod.num_meshes; i++)
- {
- // Get the corresponding MDL mesh from the current model
- currentMesh = currentModel->getMesh(i);
- // Process the mesh to get a geode
- geode = processMesh(lodNum, str,
- offset + lod.mesh_offset + (i * VTX_MESH_SIZE),
- vertexOffset);
- // Set the geode's state set based on the current mesh node's state
- // set
- geode->setStateSet(currentMesh->getStateSet());
- // Add the geode to the group
- lodGroup->addChild(geode.get());
- // Add the number of vertices for this mesh at this LOD to the offset,
- // so we can start the next mesh at the proper vertex ID
- vertexOffset += currentMesh->getNumLODVertices(lodNum);
- }
- // Set the distance for this LOD
- *distance = lod.switch_point;
- // Return the LOD group that we created
- return lodGroup;
- }
- ref_ptr<Geode> VTXReader::processMesh(int lodNum, std::istream * str,
- int offset, int vertexOffset)
- {
- int i;
- VTXMesh mesh;
- ref_ptr<Geode> geode;
- ref_ptr<Geometry> geom;
- // Seek to the mesh
- str->seekg(offset);
- // Read it
- str->read((char *) &mesh, VTX_MESH_SIZE);
- // Create a geode to hold the geometry
- geode = new Geode();
- // Process the strip groups
- for (i = 0; i < mesh.num_strip_groups; i++)
- {
- // Process the strip group to get a Geometry
- geom = processStripGroup(lodNum, str,
- offset + mesh.strip_group_offset + (i * VTX_STRIP_GROUP_SIZE),
- vertexOffset);
- // Add the geometry to the geode
- geode->addDrawable(geom.get());
- }
- // Return the geode
- return geode;
- }
- ref_ptr<Geometry> VTXReader::processStripGroup(int lodNum, std::istream * str,
- int offset, int vertexOffset)
- {
- int i;
- VTXStripGroup stripGroup;
- VTXVertex vtxVertex;
- int vertexID;
- ref_ptr<Vec3Array> vertexArray;
- ref_ptr<Vec3Array> normalArray;
- ref_ptr<Vec2Array> texcoordArray;
- unsigned short index;
- unsigned short * indexArray;
- ref_ptr<Geometry> geom;
- ref_ptr<PrimitiveSet> primSet;
- // Seek to the strip group
- str->seekg(offset);
- // Read it
- str->read((char *) &stripGroup, VTX_STRIP_GROUP_SIZE);
- // Create and fill the vertex arrays
- vertexArray = new Vec3Array();
- normalArray = new Vec3Array();
- texcoordArray = new Vec2Array();
- str->seekg(offset + stripGroup.vertex_offset);
- for (i = 0; i < stripGroup.num_vertices; i++)
- {
- // Get the vertex ID from the strip group
- str->read((char *) &vtxVertex, VTX_VERTEX_SIZE);
- vertexID = vtxVertex.orig_mesh_vertex_id + vertexOffset;
- // Get the corresponding vertex, normal, texture coordinates from the
- // VVD file
- vertexArray->push_back(vvd_reader->getVertex(lodNum, vertexID));
- normalArray->push_back(vvd_reader->getNormal(lodNum, vertexID));
- texcoordArray->push_back(vvd_reader->getTexCoords(lodNum, vertexID));
- }
- // Create the geometry and add the vertex data to it
- geom = new Geometry();
- geom->setVertexArray(vertexArray.get());
- geom->setNormalArray(normalArray.get(), Array::BIND_PER_VERTEX);
- geom->setTexCoordArray(0, texcoordArray.get());
- // Create and fill the index array
- indexArray = new unsigned short[stripGroup.num_indices];
- str->seekg(offset + stripGroup.index_offset);
- for (i = 0; i < stripGroup.num_indices; i++)
- {
- // Get the index from the file
- str->read((char *) &index, sizeof(unsigned short));
- // Add to the array
- indexArray[i] = index;
- }
- // Process the strips
- for (i = 0; i < stripGroup.num_strips; i++)
- {
- // Process the strip to create a triangle list or strip
- primSet = processStrip(indexArray, str,
- offset + stripGroup.strip_offset + (i * VTX_STRIP_SIZE));
- // Add the primitive set to the geometry
- geom->addPrimitiveSet(primSet.get());
- }
- // Clean up
- delete [] indexArray;
- // Return the geometry
- return geom;
- }
- ref_ptr<PrimitiveSet> VTXReader::processStrip(unsigned short * indexArray,
- std::istream * str,
- int offset)
- {
- VTXStrip strip;
- DrawElementsUShort * drawElements;
- ref_ptr<PrimitiveSet> primSet;
- unsigned short * start;
- unsigned short * end;
- // Seek to the strip
- str->seekg(offset);
- // Read it. We have to do this in a kind of screwy way because of the
- // weird byte packing. Valve uses pragma pack, but we can't do that
- // because it's non-portable. Of course, I'm assuming a 4-byte alignment
- // here, which might also be non-portable...
- str->read((char *) &strip, VTX_STRIP_SIZE - 8);
- str->read((char *) &strip.num_bone_state_changes, 8);
- // Get the range of indices in question from the strip
- start = &indexArray[strip.index_offset];
- end = &indexArray[strip.index_offset + strip.num_indices];
- // Create the primitive set (based on the flag)
- if (strip.strip_flags & STRIP_IS_TRI_LIST)
- drawElements =
- new DrawElementsUShort(PrimitiveSet::TRIANGLES, start, end);
- else
- drawElements =
- new DrawElementsUShort(PrimitiveSet::TRIANGLE_STRIP, start, end);
- // Flip the indices to get the front faces correct
- std::reverse(drawElements->begin(), drawElements->end());
- // Return the primitive set
- primSet = drawElements;
- return primSet;
- }
- bool VTXReader::readFile(const std::string & file)
- {
- osgDB::ifstream * vtxFile;
- VTXHeader header;
- int i;
- BodyPart * currentPart;
- ref_ptr<Group> partGroup;
- Group * rootGroup;
- // Remember the map name
- vtx_name = getStrippedName(file);
- vtxFile = new osgDB::ifstream(file.c_str(), std::ios::binary);
- if (!vtxFile || vtxFile->fail())
- {
- OSG_NOTICE << "Vertex index file not found" << std::endl;
- return false;
- }
- // Read the header
- vtxFile->read((char *) &header, sizeof(VTXHeader));
- // Make sure the version is one that we handle
- // TODO: Not sure which versions are valid yet
- // Create the root group
- rootGroup = new Group();
- // Process the body parts
- for (i = 0; i < header.num_body_parts; i++)
- {
- // Get the corresponding body part from the MDL tree
- currentPart = mdl_root->getBodyPart(i);
- // Process the body part
- partGroup = processBodyPart(vtxFile,
- header.body_part_offset +
- (i * sizeof(VTXBodyPart)),
- currentPart);
- // Add the result to the root group
- rootGroup->addChild(partGroup.get());
- }
- // Set the model's root node
- model_root = rootGroup;
- // Close the file
- vtxFile->close();
- delete vtxFile;
- return true;
- }
- ref_ptr<Node> VTXReader::getModel()
- {
- return model_root;
- }