PageRenderTime 34ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 0ms

/Semester 7/GameEngine/OpenSceneGraph-3.4.0/src/osgPlugins/mdl/VTXReader.cpp

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