Open Source Tomb Raider Engine
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SkeletalModel.cpp 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. /*!
  2. * \file src/SkeletalModel.cpp
  3. * \brief This is the factored out skeletal model class
  4. *
  5. * \author Mongoose
  6. * \author xythobuz
  7. */
  8. #include "global.h"
  9. #include "Log.h"
  10. #include "Render.h"
  11. #include "SkeletalModel.h"
  12. #include "World.h"
  13. BoneTag::BoneTag(int m, float o[3], float r[3], char f) {
  14. mesh = m;
  15. flag = f;
  16. for (int i = 0; i < 3; i++) {
  17. off[i] = o[i];
  18. rot[i] = r[i];
  19. }
  20. }
  21. BoneTag::BoneTag(TombRaider& tr, unsigned int index, unsigned int i, unsigned int* l,
  22. unsigned int frame_offset) {
  23. tr2_moveable_t* moveable = tr.Moveable();
  24. tr2_meshtree_t* meshtree = tr.MeshTree();
  25. unsigned short* frame = tr.Frame();
  26. off[0] = 0.0f;
  27. off[1] = 0.0f;
  28. off[2] = 0.0f;
  29. flag = 0x00;
  30. rot[0] = 0.0f;
  31. rot[1] = 0.0f;
  32. rot[2] = 0.0f;
  33. mesh = moveable[index].starting_mesh + i;
  34. // Setup offsets to produce skeleton
  35. if (i == 0) {
  36. // Always push tag[0], this isn't really used either
  37. flag = 0x02;
  38. } else { // Nonprimary tag - position relative to first tag
  39. // Hack: moveable[index].mesh_tree is a byte offset
  40. // into mesh_tree[], so we have to convert to index
  41. int* tree = (int*)(void*)meshtree;
  42. tr2_meshtree_t* mesh_tree = (tr2_meshtree_t*)(tree
  43. + moveable[index].mesh_tree + ((i - 1) * 4));
  44. off[0] = mesh_tree->x;
  45. off[1] = mesh_tree->y;
  46. off[2] = mesh_tree->z;
  47. flag = (char)mesh_tree->flags;
  48. }
  49. // Setup tag rotations
  50. tr.computeRotationAngles(&frame, &frame_offset, l, rot, rot + 1, rot + 2);
  51. }
  52. void BoneTag::display() {
  53. getWorld().getStaticMesh(mesh).display();
  54. }
  55. void BoneTag::getOffset(float o[3]) {
  56. o[0] = off[0];
  57. o[1] = off[1];
  58. o[2] = off[2];
  59. }
  60. void BoneTag::getRotation(float r[3]) {
  61. r[0] = rot[0];
  62. r[1] = rot[1];
  63. r[2] = rot[2];
  64. }
  65. char BoneTag::getFlag() {
  66. return flag;
  67. }
  68. // ----------------------------------------------------------------------------
  69. BoneFrame::BoneFrame(float p[3]) {
  70. for (int i = 0; i < 3; i++)
  71. pos[i] = p[i];
  72. }
  73. BoneFrame::BoneFrame(TombRaider& tr, unsigned int index, unsigned int frame_offset) {
  74. tr2_moveable_t* moveable = tr.Moveable();
  75. unsigned short* frame = tr.Frame();
  76. pos[0] = (short)frame[frame_offset + 6];
  77. pos[1] = (short)frame[frame_offset + 7];
  78. pos[2] = (short)frame[frame_offset + 8];
  79. unsigned int l = 9; // First angle offset in this Frame
  80. for (unsigned int i = 0; i < moveable[index].num_meshes; i++)
  81. tag.push_back(new BoneTag(tr, index, i, &l, frame_offset));
  82. }
  83. BoneFrame::~BoneFrame() {
  84. for (unsigned long i = 0; i < tag.size(); i++)
  85. delete tag[i];
  86. }
  87. unsigned long BoneFrame::size() {
  88. return tag.size();
  89. }
  90. BoneTag& BoneFrame::get(unsigned long i) {
  91. assert(i < tag.size());
  92. return *tag.at(i);
  93. }
  94. void BoneFrame::add(BoneTag* t) {
  95. tag.push_back(t);
  96. }
  97. void BoneFrame::getPosition(float p[3]) {
  98. p[0] = pos[0];
  99. p[1] = pos[1];
  100. p[2] = pos[2];
  101. }
  102. // ----------------------------------------------------------------------------
  103. AnimationFrame::AnimationFrame(char r) {
  104. rate = r;
  105. }
  106. AnimationFrame::AnimationFrame(TombRaider& tr, unsigned int index, int a,
  107. unsigned int* frame_offset, int frame_step) {
  108. tr2_moveable_t* moveable = tr.Moveable();
  109. tr2_animation_t* animation = tr.Animation();
  110. unsigned int frame_count = (animation[a].frame_end - animation[a].frame_start) + 1;
  111. rate = animation[a].frame_rate;
  112. for (unsigned int f = 0; f < frame_count; f++, *frame_offset += frame_step) {
  113. // HACK: Lara's ObjectID is 315, but her meshes start at 0, so make a
  114. // quick substitution (so she doesn't appear as a bunch of thighs)
  115. if ((index == 0) && (tr.Engine() == TR_VERSION_3)) {
  116. for (int j = 0; (j < (int)tr.NumMoveables()) && (index == 0); j++) {
  117. if (moveable[j].object_id == 315)
  118. index = j;
  119. }
  120. }
  121. // Fix Lara in TR4
  122. // Body is ItemID 8, joints are ItemID 9
  123. // (TR4 demo: body is ItemID 10, joints are ItemID 11)
  124. if ((index == 0) && (tr.Engine() == TR_VERSION_4)) {
  125. for (int j = 0; (j < (int)tr.NumMoveables()) && (index == 0); j++) {
  126. if (moveable[j].object_id == 8)
  127. index = j;
  128. }
  129. } else if ((moveable[index].object_id == 8) && (tr.Engine() == TR_VERSION_4)) {
  130. // KLUDGE to do "skinning"
  131. index = 0;
  132. for (int j = 0; (j < (int)tr.NumMoveables()) && (index == 0); j++) {
  133. if (moveable[j].object_id == 9)
  134. index = j;
  135. }
  136. }
  137. if (*frame_offset > tr.NumFrames()) {
  138. getLog() << "WARNING: Bad animation frame " << *frame_offset
  139. << " > " << tr.NumFrames() << " (" << index << "." << a << ")"
  140. << Log::endl;
  141. return;
  142. }
  143. frame.push_back(new BoneFrame(tr, index, *frame_offset));
  144. }
  145. }
  146. AnimationFrame::~AnimationFrame() {
  147. for (unsigned long i = 0; i < frame.size(); i++)
  148. delete frame[i];
  149. }
  150. unsigned long AnimationFrame::size() {
  151. return frame.size();
  152. }
  153. BoneFrame& AnimationFrame::get(unsigned long i) {
  154. assert(i < frame.size());
  155. return *frame.at(i);
  156. }
  157. void AnimationFrame::add(BoneFrame* f) {
  158. frame.push_back(f);
  159. }
  160. // ----------------------------------------------------------------------------
  161. SkeletalModel::SkeletalModel(int i) {
  162. id = i;
  163. }
  164. SkeletalModel::SkeletalModel(TombRaider& tr, unsigned int index, int objectId) {
  165. tr2_moveable_t* moveable = tr.Moveable();
  166. tr2_animation_t* anim = tr.Animation();
  167. tr2_mesh_t* mesh = tr.Mesh();
  168. id = objectId;
  169. // Gather more info if this is lara
  170. if (id == 0) {
  171. // Only TR4 lara has 2 layer bone tags/meshes per bone frame
  172. tr4Overlay = (tr.Engine() == TR_VERSION_4);
  173. ponytailId = 0;
  174. } else {
  175. tr4Overlay = false;
  176. ponytailId = -1;
  177. }
  178. switch (tr.Engine()) {
  179. case TR_VERSION_4:
  180. if (moveable[index].object_id == 30) {
  181. ponytailId = getWorld().sizeSkeletalModel(); //! \fixme Why is this even needed?
  182. ponytailMeshId = moveable[index].starting_mesh;
  183. ponytailNumMeshes = ((moveable[index].num_meshes > 0) ?
  184. moveable[index].num_meshes : 0);
  185. ponytailAngle = -90.0f;
  186. ponytail[0] = -3;
  187. ponytail[1] = -22;
  188. ponytail[2] = -20;
  189. ponyOff = 40;
  190. ponyOff2 = 32;
  191. pigtails = false;
  192. // Try to guess pigtails by looking for certian num verts in head
  193. if (mesh[moveable[0].starting_mesh].num_vertices > 80) {
  194. pigtails = true;
  195. ponyOff -= 20;
  196. ponytail[1] -= 32;
  197. }
  198. getRender().setFlags(Render::fRenderPonytail);
  199. getLog() << "Found known ponytail" << Log::endl;
  200. }
  201. break;
  202. case TR_VERSION_1:
  203. case TR_VERSION_2:
  204. case TR_VERSION_3:
  205. case TR_VERSION_5:
  206. case TR_VERSION_UNKNOWN:
  207. if (moveable[index].object_id == 2) {
  208. ponytailId = getWorld().sizeSkeletalModel(); //! \fixme Why is this even needed?
  209. ponytailMeshId = moveable[index].starting_mesh;
  210. ponytailNumMeshes = ((moveable[index].num_meshes > 0) ?
  211. moveable[index].num_meshes : 0);
  212. ponytailAngle = -90.0f;
  213. ponytail[0] = 0;
  214. ponytail[1] = -20;
  215. ponytail[2] = -20;
  216. ponyOff = 40;
  217. ponyOff2 = 0;
  218. getRender().setFlags(Render::fRenderPonytail);
  219. getLog() << "Found ponytail?" << Log::endl;
  220. }
  221. break;
  222. }
  223. // Animations
  224. int a = moveable[index].animation;
  225. unsigned int frame_offset = anim[a].frame_offset / 2;
  226. int frame_step = anim[a].frame_size;
  227. int frame_cycle = 0;
  228. if (a >= (int)tr.NumAnimations())
  229. a = tr.NumFrames() - frame_offset; //! \fixme Couldn't a be already used out of range?!
  230. else
  231. a = (anim[a].frame_offset / 2) - frame_offset; //! \fixme Same as a = 0; ??
  232. if (frame_step != 0) // prevent divide-by-zero errors
  233. a /= frame_step;
  234. if (a != 0)
  235. frame_offset += frame_step * (frame_cycle % a);
  236. if (a < 0) {
  237. getLog() << "Invalid animation data for model " << index << ". Skip!" << Log::endl;
  238. return;
  239. } else {
  240. for (; a < tr.getNumAnimsForMoveable(index); a++) {
  241. animation.push_back(new AnimationFrame(tr, index, a, &frame_offset, frame_step));
  242. if (frame_offset > tr.NumFrames())
  243. return;
  244. frame_offset = anim[a].frame_offset / 2;
  245. frame_step = anim[a].frame_size;
  246. }
  247. }
  248. }
  249. SkeletalModel::~SkeletalModel() {
  250. for (unsigned long i = 0; i < animation.size(); i++)
  251. delete animation[i];
  252. }
  253. void SkeletalModel::display(unsigned long aframe, unsigned long bframe) {
  254. assert(aframe < size());
  255. assert(bframe < get(aframe).size());
  256. AnimationFrame& anim = get(aframe);
  257. BoneFrame& boneframe = anim.get(bframe);
  258. float pos[3];
  259. boneframe.getPosition(pos);
  260. glTranslatef(pos[0], pos[1], pos[2]);
  261. for (unsigned int a = 0; a < boneframe.size(); a++) {
  262. BoneTag& tag = boneframe.get(a);
  263. float rot[3], off[3];
  264. tag.getRotation(rot);
  265. tag.getOffset(off);
  266. if (a == 0) {
  267. glRotatef(rot[1], 0, 1, 0);
  268. glRotatef(rot[0], 1, 0, 0);
  269. glRotatef(rot[2], 0, 0, 1);
  270. } else {
  271. if (tag.getFlag() & 0x01)
  272. glPopMatrix();
  273. if (tag.getFlag() & 0x02)
  274. glPushMatrix();
  275. glTranslatef(off[0], off[1], off[2]);
  276. glRotatef(rot[1], 0, 1, 0);
  277. glRotatef(rot[0], 1, 0, 0);
  278. glRotatef(rot[2], 0, 0, 1);
  279. }
  280. // Draw layered lara in TR4 (2 meshes per tag)
  281. if (tr4Overlay) {
  282. BoneFrame& boneframe2 = get(0).get(0); //! \fixme Woot?
  283. if (a < boneframe2.size())
  284. boneframe2.get(a).display();
  285. }
  286. if (getRender().getFlags() & Render::fRenderPonytail) {
  287. if ((ponytailId > 0) && (a == 14)) {
  288. glPushMatrix();
  289. // Mongoose 2002.08.30, TEST to align offset
  290. glTranslatef(ponytail[0], ponytail[1], ponytail[2]);
  291. glRotatef(ponytailAngle, 1, 0, 0);
  292. // HACK: To fill TR4 void between ponytail/head
  293. // since no vertex welds are implemented yet
  294. if (tr4Overlay)
  295. glScalef(1.20f, 1.20f, 1.20f);
  296. #ifdef EXPERIMENTAL_NON_ITEM_RENDER
  297. getWorld().getSkeletalModel(ponytail).display(0, 0);
  298. #else
  299. for (unsigned int i = 0; i < ponytailNumMeshes; i++) {
  300. glPushMatrix();
  301. if (i > 0) {
  302. glRotatef(randomNum(-8.0f, -10.0f), 1, 0, 0);
  303. glRotatef(randomNum(-5.0f, 5.0f), 0, 1, 0);
  304. glRotatef(randomNum(-5.0f, 5.0f), 0, 0, 1);
  305. glTranslatef(0.0, 0.0, ponyOff);
  306. }
  307. if (pigtails) {
  308. glPushMatrix();
  309. glTranslatef(ponyOff2, 0.0, 0.0);
  310. getWorld().getStaticMesh(ponytailMeshId + i).display();
  311. glPopMatrix();
  312. glPushMatrix();
  313. glTranslatef(-ponyOff2, 0.0, 0.0);
  314. getWorld().getStaticMesh(ponytailMeshId + i).display();
  315. glPopMatrix();
  316. } else {
  317. getWorld().getStaticMesh(ponytailMeshId + i).display();
  318. }
  319. }
  320. for (unsigned int i = 0; i < ponytailNumMeshes; i++)
  321. glPopMatrix();
  322. #endif
  323. glPopMatrix();
  324. }
  325. }
  326. tag.display();
  327. }
  328. }
  329. int SkeletalModel::getId() {
  330. return id;
  331. }
  332. void SkeletalModel::setPigTail(bool b) {
  333. pigtails = b;
  334. if (b) {
  335. ponyOff -= 20;
  336. ponytail[1] -= 32;
  337. } else {
  338. ponyOff += 20;
  339. ponytail[1] += 32;
  340. }
  341. }
  342. void SkeletalModel::setPonyPos(float x, float y, float z, float angle) {
  343. ponytail[0] = x;
  344. ponytail[1] = y;
  345. ponytail[2] = z;
  346. ponytailAngle = angle;
  347. }
  348. unsigned long SkeletalModel::size() {
  349. return animation.size();
  350. }
  351. AnimationFrame& SkeletalModel::get(unsigned long i) {
  352. assert(i < animation.size());
  353. return *animation.at(i);
  354. }
  355. void SkeletalModel::add(AnimationFrame* f) {
  356. animation.push_back(f);
  357. }