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.

Render.cpp 31KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232
  1. /*!
  2. * \file src/Render.cpp
  3. * \brief OpenRaider Renderer class
  4. *
  5. * \author Mongoose
  6. * \author xythobuz
  7. */
  8. #include <algorithm>
  9. #ifdef __APPLE__
  10. #include <OpenGL/gl.h>
  11. #elif defined WIN32
  12. #include <gl/glew.h>
  13. #include <gl/wglew.h>
  14. #else
  15. #include <GL/gl.h>
  16. #endif
  17. #include <stdlib.h>
  18. #include <math.h>
  19. #include <string.h>
  20. #include "global.h"
  21. #include "main.h"
  22. #include "Game.h"
  23. #include "OpenRaider.h"
  24. #include "Render.h"
  25. #include "utils/strings.h"
  26. bool compareEntites(const void *voidA, const void *voidB)
  27. {
  28. entity_t *a = (entity_t *)voidA, *b = (entity_t *)voidB;
  29. vec_t distA, distB;
  30. if (!a || !b)
  31. return false; // error really
  32. distA = getRender().mViewVolume.getDistToSphereFromNear(a->pos[0],
  33. a->pos[1],
  34. a->pos[2],
  35. 1.0f);
  36. distB = getRender().mViewVolume.getDistToSphereFromNear(b->pos[0],
  37. b->pos[1],
  38. b->pos[2],
  39. 1.0f);
  40. return (distA < distB);
  41. }
  42. ////////////////////////////////////////////////////////////
  43. // Constructors
  44. ////////////////////////////////////////////////////////////
  45. Render::Render()
  46. {
  47. mSkyMesh = -1;
  48. mSkyMeshRotation = false;
  49. mMode = Render::modeDisabled;
  50. mLock = 0;
  51. mFlags = (fRoomAlpha | fViewModel |
  52. fEntityModels | fRenderPonytail |
  53. fUsePortals | fUpdateRoomListPerFrame);
  54. }
  55. Render::~Render()
  56. {
  57. ClearWorld();
  58. }
  59. ////////////////////////////////////////////////////////////
  60. // Public Accessors
  61. ////////////////////////////////////////////////////////////
  62. void Render::screenShot(char *filenameBase)
  63. {
  64. mTexture.glScreenShot(filenameBase, getWindow().mWidth, getWindow().mHeight);
  65. }
  66. unsigned int Render::getFlags() {
  67. return mFlags;
  68. }
  69. ////////////////////////////////////////////////////////////
  70. // Public Mutators
  71. ////////////////////////////////////////////////////////////
  72. void Render::loadTexture(unsigned char *image,
  73. unsigned int width, unsigned int height,
  74. unsigned int id)
  75. {
  76. glColor3fv(WHITE);
  77. mTexture.loadBufferSlot(image, width, height, Texture::RGBA, 32, id);
  78. }
  79. int Render::initTextures(char *textureDir) {
  80. char *filename;
  81. unsigned int numTextures = 0;
  82. unsigned char color[4];
  83. mTexture.reset();
  84. mTexture.setMaxTextureCount(128); /* TR never needs more than 32 iirc
  85. However, color texturegen is a lot */
  86. mTexture.setFlag(Texture::fUseMipmaps);
  87. color[0] = 0xff;
  88. color[1] = 0xff;
  89. color[2] = 0xff;
  90. color[3] = 0xff;
  91. if (mTexture.loadColorTexture(color, 32, 32) > -1)
  92. numTextures++;
  93. //! \fixme Error Checking. Return negative error code, check in calling place too
  94. filename = bufferString("%s/%s", textureDir, "splash.tga");
  95. if (mTexture.loadTGA(filename) > -1)
  96. numTextures++;
  97. delete [] filename;
  98. return numTextures;
  99. }
  100. void Render::ClearWorld() {
  101. mRoomRenderList.clear();
  102. for (unsigned int i = 0; i < mModels.size(); i++) {
  103. if (mModels[i])
  104. delete mModels[i];
  105. }
  106. mModels.clear();
  107. }
  108. // Texture must be set to WHITE solid color texture
  109. void renderTrace(int color, vec3_t start, vec3_t end)
  110. {
  111. const float widthStart = 10.0f; //5.0f;
  112. const float widthEnd = 10.0f;
  113. float delta = randomNum(0.01f, 0.16f); // for flicker fx
  114. // Draw two long quads that skrink and fade the they go further out
  115. glBegin(GL_QUADS);
  116. switch (color)
  117. {
  118. case 0:
  119. glColor3f(0.9f - delta, 0.2f, 0.2f);
  120. break;
  121. case 1:
  122. glColor3f(0.2f, 0.9f - delta, 0.2f);
  123. break;
  124. case 2:
  125. default:
  126. glColor3f(0.2f, 0.2f, 0.9f - delta);
  127. }
  128. glVertex3f(start[0], start[1], start[2]);
  129. glVertex3f(start[0], start[1] + widthStart, start[2] + widthStart);
  130. glVertex3f(end[0], end[1] + widthEnd, end[2] + widthEnd);
  131. glVertex3f(end[0], end[1], end[2]);
  132. glVertex3f(start[0], start[1], start[2]);
  133. glVertex3f(start[0], start[1] + widthStart, start[2] - widthStart);
  134. glVertex3f(end[0], end[1] + widthEnd, end[2] - widthEnd);
  135. glVertex3f(end[0], end[1], end[2]);
  136. glVertex3f(start[0], start[1] + widthStart, start[2] + widthStart);
  137. glVertex3f(start[0], start[1] + widthStart, start[2] - widthStart);
  138. glVertex3f(end[0], end[1] + widthEnd, end[2] - widthEnd);
  139. glVertex3f(end[0], end[1] + widthEnd, end[2] + widthEnd);
  140. glEnd();
  141. }
  142. void setLighting(bool on)
  143. {
  144. if (on)
  145. {
  146. glEnable(GL_LIGHTING);
  147. glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 0);
  148. glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, WHITE);
  149. glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, WHITE);
  150. glLightModelfv(GL_LIGHT_MODEL_AMBIENT, DIM_WHITE);
  151. }
  152. else
  153. {
  154. glDisable(GL_LIGHTING);
  155. }
  156. }
  157. void lightRoom(Room &room)
  158. {
  159. for (unsigned int i = 0; i < room.sizeLights(); ++i)
  160. {
  161. Light &light = room.getLight(i);
  162. vec4_t pos, color;
  163. vec3_t dir;
  164. light.getPos(pos);
  165. light.getColor(color);
  166. light.getDir(dir);
  167. glEnable(GL_LIGHT0 + i);
  168. switch (light.getType())
  169. {
  170. case Light::typeSpot:
  171. glLightf(GL_LIGHT0 + i, GL_SPOT_CUTOFF, light.getCutoff());
  172. glLightfv(GL_LIGHT0 + i, GL_POSITION, pos);
  173. glLightfv(GL_LIGHT0 + i, GL_SPOT_DIRECTION, dir);
  174. glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, color);
  175. break;
  176. case Light::typePoint:
  177. case Light::typeDirectional:
  178. glLightf(GL_LIGHT0 + i, GL_CONSTANT_ATTENUATION, 1.0); // 1.0
  179. // GL_QUADRATIC_ATTENUATION
  180. // GL_LINEAR_ATTENUATION
  181. glLightf(GL_LIGHT0 + i, GL_LINEAR_ATTENUATION, light.getAtt());
  182. glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, color); // GL_DIFFUSE
  183. glLightfv(GL_LIGHT0 + i, GL_POSITION, pos);
  184. break;
  185. }
  186. }
  187. }
  188. void Render::clearFlags(unsigned int flags)
  189. {
  190. // _defaults |= flags; // Force removal if it wasn't set
  191. mFlags ^= flags;
  192. if (flags & Render::fFog)
  193. {
  194. if (glIsEnabled(GL_FOG))
  195. {
  196. glDisable(GL_FOG);
  197. }
  198. }
  199. if (flags & Render::fGL_Lights)
  200. {
  201. setLighting(false);
  202. }
  203. }
  204. void Render::setFlags(unsigned int flags)
  205. {
  206. mFlags |= flags;
  207. if (flags & Render::fFog)
  208. {
  209. glEnable(GL_FOG);
  210. glFogf(GL_FOG_MODE, GL_EXP2);
  211. glFogf(GL_FOG_DENSITY, 0.00008f);
  212. glFogf(GL_FOG_START, 30000.0f);
  213. glFogf(GL_FOG_END, 50000.0f);
  214. glFogfv(GL_FOG_COLOR, BLACK);
  215. }
  216. if (flags & Render::fGL_Lights)
  217. {
  218. setLighting(true);
  219. }
  220. }
  221. int Render::getMode()
  222. {
  223. return mMode;
  224. }
  225. void Render::setMode(int n)
  226. {
  227. mMode = n;
  228. switch (mMode)
  229. {
  230. case Render::modeDisabled:
  231. break;
  232. case Render::modeSolid:
  233. case Render::modeWireframe:
  234. glClearColor(NEXT_PURPLE[0], NEXT_PURPLE[1],
  235. NEXT_PURPLE[2], NEXT_PURPLE[3]);
  236. glDisable(GL_TEXTURE_2D);
  237. break;
  238. default:
  239. if (mMode == Render::modeLoadScreen)
  240. {
  241. glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  242. }
  243. else
  244. {
  245. glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  246. }
  247. glClearColor(BLACK[0], BLACK[1], BLACK[2], BLACK[3]);
  248. glEnable(GL_TEXTURE_2D);
  249. }
  250. }
  251. // Replaced the deprecated gluLookAt with slightly modified code from here:
  252. // http://www.khronos.org/message_boards/showthread.php/4991
  253. void gluLookAt(float eyeX, float eyeY, float eyeZ,
  254. float lookAtX, float lookAtY, float lookAtZ,
  255. float upX, float upY, float upZ) {
  256. float f[3];
  257. // calculating the viewing vector
  258. f[0] = lookAtX - eyeX;
  259. f[1] = lookAtY - eyeY;
  260. f[2] = lookAtZ - eyeZ;
  261. float fMag, upMag;
  262. fMag = sqrtf(f[0] * f[0] + f[1] * f[1] + f[2] * f[2]);
  263. upMag = sqrtf(upX * upX + upY * upY + upZ * upZ);
  264. // normalizing the viewing vector
  265. f[0] = f[0] / fMag;
  266. f[1] = f[1] / fMag;
  267. f[2] = f[2] / fMag;
  268. float s[3] = {
  269. f[1] * upZ - upY * f[2],
  270. upX * f[2] - f[0] * upZ,
  271. f[0] * upY - upX * f[1]
  272. };
  273. float u[3] = {
  274. s[1] * f[2] - f[1] * s[2],
  275. f[0] * s[2] - s[0] * f[2],
  276. s[0] * f[1] - f[0] * s[1]
  277. };
  278. float m[16] = {
  279. s[0], u[0], -f[0], 0,
  280. s[1], u[1], -f[1], 0,
  281. s[2], u[2], -f[2], 0,
  282. 0, 0, 0, 1
  283. };
  284. glMultMatrixf(m);
  285. glTranslatef(-eyeX, -eyeY, -eyeZ);
  286. }
  287. void Render::display()
  288. {
  289. vec3_t curPos;
  290. vec3_t camPos;
  291. vec3_t atPos;
  292. int index;
  293. switch (mMode)
  294. {
  295. case Render::modeDisabled:
  296. return;
  297. case Render::modeLoadScreen:
  298. //! \fixme entry for seperate main drawing method -- Mongoose 2002.01.01
  299. drawLoadScreen();
  300. return;
  301. default:
  302. ;
  303. }
  304. if (mMode == Render::modeWireframe)
  305. glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
  306. else
  307. glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  308. index = -1;
  309. if (getGame().mLara)
  310. {
  311. float yaw;
  312. int sector;
  313. float camOffsetH = 0.0f;
  314. switch (getGame().mLara->moveType)
  315. {
  316. case worldMoveType_fly:
  317. case worldMoveType_noClipping:
  318. case worldMoveType_swim:
  319. camOffsetH = 64.0f;
  320. break;
  321. case worldMoveType_walk:
  322. case worldMoveType_walkNoSwim:
  323. camOffsetH = 512.0f;
  324. break;
  325. }
  326. curPos[0] = getGame().mLara->pos[0];
  327. curPos[1] = getGame().mLara->pos[1];
  328. curPos[2] = getGame().mLara->pos[2];
  329. yaw = getGame().mLara->angles[1];
  330. index = getGame().mLara->room;
  331. // Mongoose 2002.08.24, New 3rd person camera hack
  332. camPos[0] = curPos[0];
  333. camPos[1] = curPos[1] - camOffsetH; // UP is lower val
  334. camPos[2] = curPos[2];
  335. camPos[0] -= (1024.0f * sinf(yaw));
  336. camPos[2] -= (1024.0f * cosf(yaw));
  337. sector = getWorld().getSector(index, camPos[0], camPos[2]);
  338. // Handle camera out of world
  339. if (sector < 0 || getWorld().isWall(index, sector))
  340. {
  341. camPos[0] = curPos[0] + (64.0f * sinf(yaw));
  342. camPos[1] -= 64.0f;
  343. camPos[2] = curPos[2] + (64.0f * cosf(yaw));
  344. }
  345. getCamera().setPosition(camPos);
  346. }
  347. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  348. glLoadIdentity();
  349. // Setup view in OpenGL with camera
  350. getCamera().update();
  351. getCamera().getPosition(camPos);
  352. getCamera().getTarget(atPos);
  353. // Mongoose 2002.08.13, Quick fix to render OpenRaider upside down
  354. gluLookAt(camPos[0], camPos[1], camPos[2], atPos[0], atPos[1], atPos[2], 0.0f, -1.0f, 0.0f);
  355. // Update view volume for vising
  356. updateViewVolume();
  357. // Let's see the LoS -- should be event controled
  358. if (getGame().mLara)
  359. {
  360. // SkeletalModel *mdl = getGame().mLara->tmpHook;
  361. // Draw in solid colors
  362. glDisable(GL_TEXTURE_2D);
  363. glDisable(GL_CULL_FACE);
  364. if (getGame().mLara->state == 64) // eWeaponFire
  365. {
  366. vec3_t u, v; //, s, t;
  367. // Center of getGame().mLara
  368. u[0] = curPos[0];
  369. u[1] = curPos[1] - 512.0f;
  370. u[2] = curPos[2];
  371. // Location getGame().mLara is aiming at? ( Not finished yet )
  372. v[0] = u[0] + (9000.0f * sinf(getGame().mLara->angles[1]));
  373. v[1] = u[1] + (9000.0f * sinf(getGame().mLara->angles[2]));
  374. v[2] = u[2] + (9000.0f * cosf(getGame().mLara->angles[1]));
  375. // Test tracing of aim
  376. renderTrace(0, u, v); // v = target
  377. }
  378. entity_t *route = getGame().mLara->master;
  379. while (route)
  380. {
  381. if (route->master)
  382. {
  383. renderTrace(1, route->pos, route->master->pos);
  384. }
  385. route = route->master;
  386. }
  387. glEnable(GL_CULL_FACE);
  388. glEnable(GL_TEXTURE_2D);
  389. }
  390. // Render world
  391. glColor3fv(DIM_WHITE); // was WHITE
  392. drawSkyMesh(96.0);
  393. // Figure out how much of the map to render
  394. newRoomRenderList(index);
  395. // Room solid pass, need to do depth sorting to avoid 2 pass render
  396. for (unsigned int i = 0; i < mRoomRenderList.size(); i++)
  397. {
  398. Room &room = *mRoomRenderList[i];
  399. if (mFlags & Render::fGL_Lights)
  400. {
  401. lightRoom(room);
  402. }
  403. room.display(false);
  404. }
  405. // Draw all visible enities
  406. if (mFlags & Render::fEntityModels)
  407. {
  408. entity_t *e;
  409. std::vector<entity_t *> entityRenderList;
  410. std::vector<entity_t *> *entities = getWorld().getEntities();
  411. for (unsigned int i = 0; i < entities->size(); i++)
  412. {
  413. e = entities->at(i);
  414. // Mongoose 2002.03.26, Don't show lara to it's own player
  415. if (!e || e == getGame().mLara)
  416. {
  417. continue;
  418. }
  419. // Mongoose 2002.08.15, Nothing to draw, skip
  420. // Mongoose 2002.12.24, Some entities have no animation =p
  421. if (e->tmpHook)
  422. if (e->tmpHook->model->animation.empty())
  423. continue;
  424. // Is it in view volume? ( Hack to use sphere )
  425. if (!isVisible(e->pos[0], e->pos[1], e->pos[2], 512.0f))
  426. continue;
  427. //! \fixme Is it in a room we're rendering?
  428. //if (mRoomRenderList[e->room] == 0x0)
  429. //{
  430. // continue;
  431. //}
  432. entityRenderList.push_back(e);
  433. }
  434. // Draw objects not tied to rooms
  435. glPushMatrix();
  436. drawObjects();
  437. glPopMatrix();
  438. // Depth sort entityRenderList with qsort
  439. std::sort(entityRenderList.begin(), entityRenderList.end(), compareEntites);
  440. for (unsigned int i = 0; i < entityRenderList.size(); i++)
  441. {
  442. e = entityRenderList[i];
  443. glPushMatrix();
  444. glTranslatef(e->pos[0], e->pos[1], e->pos[2]);
  445. glRotatef(e->angles[1], 0, 1, 0);
  446. drawModel(e->tmpHook);
  447. glPopMatrix();
  448. }
  449. }
  450. // Room alpha pass
  451. // Skip room alpha pass for modes w/o texture
  452. if (!(mMode == Render::modeSolid || mMode == Render::modeWireframe))
  453. {
  454. for (unsigned int i = 0; i < mRoomRenderList.size(); i++)
  455. {
  456. mRoomRenderList[i]->display(true);
  457. }
  458. }
  459. if (mMode == Render::modeWireframe)
  460. glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  461. glFlush();
  462. }
  463. void Render::drawLoadScreen()
  464. {
  465. float x = 0.0f, y = 0.0f, z = -160.0f;
  466. float w, h;
  467. if (getWindow().mWidth < getWindow().mHeight)
  468. w = h = (float)getWindow().mWidth;
  469. else
  470. w = h = (float)getWindow().mHeight;
  471. if (mTexture.getTextureCount() <= 0)
  472. return;
  473. // Mongoose 2002.01.01, Rendered while game is loading...
  474. //! \fixme seperate logo/particle coor later
  475. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  476. glLoadIdentity();
  477. glColor3fv(WHITE);
  478. if (mFlags & Render::fGL_Lights)
  479. glDisable(GL_LIGHTING);
  480. // Mongoose 2002.01.01, Draw logo/load screen
  481. glTranslatef(0.0f, 0.0f, -2000.0f);
  482. glRotatef(180.0f, 1.0f, 0.0f, 0.0f);
  483. mTexture.bindTextureId(1); //! \fixme store texture id somewhere
  484. glBegin(GL_TRIANGLE_STRIP);
  485. glTexCoord2f(1.0, 1.0);
  486. glVertex3f(x + w, y + h, z);
  487. glTexCoord2f(0.0, 1.0);
  488. glVertex3f(x - w, y + h, z);
  489. glTexCoord2f(1.0, 0.0);
  490. glVertex3f(x + w, y - h, z);
  491. glTexCoord2f(0.0, 0.0);
  492. glVertex3f(x - w, y - h, z);
  493. glEnd();
  494. if (mFlags & Render::fGL_Lights)
  495. glEnable(GL_LIGHTING);
  496. glFlush();
  497. }
  498. void Render::newRoomRenderList(int index)
  499. {
  500. static int currentRoomId = -1;
  501. if (mFlags & Render::fUsePortals)
  502. {
  503. if (index == -1) // -1 is error, so draw room 0, for the hell of it
  504. {
  505. mRoomRenderList.clear();
  506. mRoomRenderList.push_back(&getWorld().getRoom(0));
  507. }
  508. else
  509. {
  510. // Update room render list if needed
  511. if (mFlags & Render::fUpdateRoomListPerFrame ||
  512. currentRoomId != index)
  513. {
  514. buildRoomRenderList(getWorld().getRoom(index));
  515. }
  516. }
  517. }
  518. else // Render all rooms pretty much
  519. {
  520. if (currentRoomId != index || index == -1)
  521. {
  522. printf("*** Room render list -> %i\n", index);
  523. mRoomRenderList.clear();
  524. for (unsigned int i = 0; i < getWorld().sizeRoom(); i++)
  525. {
  526. if (!isVisible(getWorld().getRoom(i).getBoundingBox()))
  527. continue;
  528. mRoomRenderList.push_back(&getWorld().getRoom(i));
  529. }
  530. }
  531. }
  532. currentRoomId = index;
  533. }
  534. void Render::buildRoomRenderList(Room &room)
  535. {
  536. // Must be visible
  537. //! \fixme Add depth sorting here - remove multipass
  538. if (!isVisible(room.getBoundingBox()))
  539. return;
  540. // Must not already be cached
  541. for (unsigned int i = 0; i < mRoomRenderList.size(); i++)
  542. {
  543. Room &room2 = *mRoomRenderList[i];
  544. if (&room2 == &room)
  545. return;
  546. }
  547. /* Add current room to list */
  548. mRoomRenderList.push_back(&room);
  549. if (mFlags & Render::fOneRoom)
  550. {
  551. return;
  552. }
  553. else if (mFlags & Render::fAllRooms) /* Are you serious? */
  554. {
  555. for (unsigned int i = 0; i < getWorld().sizeRoom(); i++)
  556. {
  557. Room &room2 = getWorld().getRoom(i);
  558. if (&room2 != &room)
  559. {
  560. buildRoomRenderList(room2);
  561. }
  562. }
  563. return;
  564. }
  565. // Try to add adj rooms and their adj rooms, skip this room
  566. for (unsigned int i = 1; i < room.sizeAdjacentRooms(); i++)
  567. {
  568. if (room.getAdjacentRoom(i) < 0)
  569. continue;
  570. Room &room2 = getWorld().getRoom(room.getAdjacentRoom(i));
  571. // Mongoose 2002.03.22, Add portal visibility check here
  572. if (&room2 != &room)
  573. {
  574. buildRoomRenderList(room2);
  575. }
  576. }
  577. }
  578. void Render::drawSkyMesh(float scale)
  579. {
  580. skeletal_model_t *model = getWorld().getModel(mSkyMesh);
  581. if (!model)
  582. return;
  583. glDisable(GL_DEPTH_TEST);
  584. glPushMatrix();
  585. if (mSkyMeshRotation)
  586. {
  587. glRotated(90.0, 1, 0, 0);
  588. }
  589. glTranslated(0.0, 1000.0, 0.0);
  590. glScaled(scale, scale, scale);
  591. //drawModel(model);
  592. //drawModelMesh(getWorld().getMesh(mSkyMesh), );
  593. glPopMatrix();
  594. glEnable(GL_DEPTH_TEST);
  595. }
  596. void Render::drawObjects()
  597. {
  598. // Draw lara or other player model ( move to entity rendering method )
  599. if (mFlags & Render::fViewModel && getGame().mLara && getGame().mLara->tmpHook)
  600. {
  601. SkeletalModel *mdl = getGame().mLara->tmpHook;
  602. if (mdl)
  603. {
  604. int frame;
  605. // Mongoose 2002.03.22, Test 'idle' aniamtions
  606. if (!getGame().mLara->moving)
  607. {
  608. frame = mdl->getIdleAnimation();
  609. // Mongoose 2002.08.15, Stop flickering of idle lara here
  610. if (frame == 11)
  611. {
  612. mdl->setFrame(0);
  613. }
  614. }
  615. else
  616. {
  617. frame = mdl->getAnimation();
  618. }
  619. animation_frame_t *animation = mdl->model->animation[frame];
  620. if (animation && mdl->getFrame() > (int)animation->frame.size()-1)
  621. {
  622. mdl->setFrame(0);
  623. }
  624. }
  625. glPushMatrix();
  626. glTranslated(getGame().mLara->pos[0], getGame().mLara->pos[1], getGame().mLara->pos[2]);
  627. glRotated(OR_RAD_TO_DEG(getCamera().getRadianYaw()), 0, 1, 0);
  628. drawModel(getGame().mLara->tmpHook);
  629. glPopMatrix();
  630. }
  631. // Mongoose 2002.03.22, Draw sprites after player to handle alpha
  632. for (unsigned int i = 0; i < getWorld().sizeSprite(); i++) {
  633. SpriteSequence &sprite = getWorld().getSprite(i);
  634. for (unsigned int j = 0; j < sprite.size(); j++)
  635. sprite.get(j).display();
  636. }
  637. }
  638. void Render::drawModel(SkeletalModel *model)
  639. {
  640. animation_frame_t *animation;
  641. bone_frame_t *boneframe;
  642. bone_frame_t *boneframe2 = 0x0;
  643. bone_tag_t *tag;
  644. bone_tag_t *tag2;
  645. int bframe, aframe;
  646. skeletal_model_t *mdl;
  647. if (!model || !model->model)
  648. return;
  649. mdl = model->model;
  650. aframe = model->getAnimation();
  651. bframe = model->getFrame();
  652. animation = mdl->animation[aframe];
  653. if (!animation)
  654. {
  655. #ifdef DEBUG
  656. printf("ERROR: No animation for model[%i].aframe[%i] %lu\n",
  657. mdl->id, aframe, mdl->animation.size());
  658. #endif
  659. return;
  660. }
  661. if (animation->frame.empty())
  662. {
  663. #ifdef DEBUG
  664. printf("ERROR: No boneframes?!?! *** %i:%i ***\n",
  665. mdl->id, bframe);
  666. #endif
  667. return;
  668. }
  669. boneframe = animation->frame[bframe];
  670. if (!boneframe)
  671. return;
  672. if (boneframe->tag.empty())
  673. {
  674. printf("Empty bone frame?!?!\n");
  675. return;
  676. }
  677. glTranslatef(boneframe->pos[0], boneframe->pos[1], boneframe->pos[2]);
  678. for (unsigned int a = 0; a < boneframe->tag.size(); a++)
  679. {
  680. tag = boneframe->tag[a];
  681. if (!tag)
  682. continue;
  683. if (a == 0)
  684. {
  685. if (!equalEpsilon(tag->rot[1], 0.0f)) // was just if (tag->rot[1])
  686. glRotatef(tag->rot[1], 0, 1, 0);
  687. if (!equalEpsilon(tag->rot[0], 0.0f))
  688. glRotatef(tag->rot[0], 1, 0, 0);
  689. if (!equalEpsilon(tag->rot[2], 0.0f))
  690. glRotatef(tag->rot[2], 0, 0, 1);
  691. }
  692. else
  693. {
  694. if (tag->flag & 0x01)
  695. glPopMatrix();
  696. if (tag->flag & 0x02)
  697. glPushMatrix();
  698. glTranslatef(tag->off[0], tag->off[1], tag->off[2]);
  699. if (!equalEpsilon(tag->rot[1], 0.0f))
  700. glRotatef(tag->rot[1], 0, 1, 0);
  701. if (!equalEpsilon(tag->rot[0], 0.0f))
  702. glRotatef(tag->rot[0], 1, 0, 0);
  703. if (!equalEpsilon(tag->rot[2], 0.0f))
  704. glRotatef(tag->rot[2], 0, 0, 1);
  705. }
  706. // Draw layered lara in TR4 ( 2 meshes per tag )
  707. if (mdl->tr4Overlay == 1)
  708. {
  709. boneframe2 = (mdl->animation[0])->frame[0];
  710. if (boneframe2)
  711. {
  712. tag2 = boneframe2->tag[a];
  713. if (tag2)
  714. {
  715. drawModelMesh(getWorld().getMesh(tag2->mesh));
  716. }
  717. }
  718. }
  719. if (mFlags & Render::fRenderPonytail)
  720. {
  721. if (mdl->ponytailId > 0 &&
  722. a == 14)
  723. {
  724. glPushMatrix();
  725. // Mongoose 2002.08.30, TEST to align offset
  726. glTranslatef(mdl->ponytail[0], mdl->ponytail[1], mdl->ponytail[2]);
  727. glRotatef(mdl->ponytailAngle, 1, 0, 0);
  728. // HACK: To fill TR4 void between ponytail/head
  729. // since no vertex welds are implemented yet
  730. if (mdl->tr4Overlay == 1)
  731. {
  732. glScalef(1.20f, 1.20f, 1.20f);
  733. }
  734. #ifdef EXPERIMENTAL_NON_ITEM_RENDER
  735. drawModel(mModels[mdl->ponytail], 0, 0);
  736. #else
  737. for (unsigned int i = 0; i < mdl->ponytailNumMeshes; ++i)
  738. {
  739. glPushMatrix();
  740. if (i > 0)
  741. {
  742. glRotatef(randomNum(-8.0f, -10.0f), 1, 0, 0);
  743. glRotatef(randomNum(-5.0f, 5.0f), 0, 1, 0);
  744. glRotatef(randomNum(-5.0f, 5.0f), 0, 0, 1);
  745. glTranslatef(0.0, 0.0, mdl->ponyOff);
  746. }
  747. if (mdl->pigtails)
  748. {
  749. glPushMatrix();
  750. glTranslatef(mdl->ponyOff2, 0.0, 0.0);
  751. drawModelMesh(getWorld().getMesh(mdl->ponytailMeshId + i));
  752. glPopMatrix();
  753. glPushMatrix();
  754. glTranslatef(-mdl->ponyOff2, 0.0, 0.0);
  755. drawModelMesh(getWorld().getMesh(mdl->ponytailMeshId + i));
  756. glPopMatrix();
  757. }
  758. else
  759. {
  760. drawModelMesh(getWorld().getMesh(mdl->ponytailMeshId + i));
  761. }
  762. }
  763. for (unsigned int i = 0; i < mdl->ponytailNumMeshes; ++i)
  764. {
  765. glPopMatrix();
  766. }
  767. #endif
  768. glPopMatrix();
  769. }
  770. }
  771. drawModelMesh(getWorld().getMesh(tag->mesh));
  772. }
  773. // Cycle frames ( cheap hack from old ent state based system )
  774. if (mFlags & fAnimateAllModels)
  775. {
  776. if (model->getFrame() + 1 > (int)animation->frame.size()-1)
  777. {
  778. model->setFrame(0);
  779. }
  780. else
  781. {
  782. model->setFrame(model->getFrame()+1);
  783. }
  784. }
  785. }
  786. void Render::tmpRenderModelMesh(model_mesh_t *r_mesh, texture_tri_t *ttri)
  787. {
  788. glBegin(GL_TRIANGLES);
  789. switch (mMode)
  790. {
  791. case modeSolid:
  792. case modeVertexLight:
  793. if (r_mesh->colors)
  794. {
  795. glColor3fv(r_mesh->colors+ttri->index[0]);
  796. glTexCoord2fv(ttri->st);
  797. glVertex3fv(r_mesh->vertices+ttri->index[0]*3);
  798. glColor3fv(r_mesh->colors+ttri->index[1]);
  799. glTexCoord2fv(ttri->st+2);
  800. glVertex3fv(r_mesh->vertices+ttri->index[1]*3);
  801. glColor3fv(r_mesh->colors+ttri->index[2]);
  802. glTexCoord2fv(ttri->st+4);
  803. glVertex3fv(r_mesh->vertices+ttri->index[2]*3);
  804. }
  805. else if (r_mesh->normals)
  806. {
  807. glNormal3fv(r_mesh->normals+ttri->index[0]*3);
  808. glTexCoord2fv(ttri->st);
  809. glVertex3fv(r_mesh->vertices+ttri->index[0]*3);
  810. glNormal3fv(r_mesh->normals+ttri->index[1]*3);
  811. glTexCoord2fv(ttri->st+2);
  812. glVertex3fv(r_mesh->vertices+ttri->index[1]*3);
  813. glNormal3fv(r_mesh->normals+ttri->index[2]*3);
  814. glTexCoord2fv(ttri->st+4);
  815. glVertex3fv(r_mesh->vertices+ttri->index[2]*3);
  816. }
  817. else
  818. {
  819. glTexCoord2fv(ttri->st);
  820. glVertex3fv(r_mesh->vertices+ttri->index[0]*3);
  821. glTexCoord2fv(ttri->st+2);
  822. glVertex3fv(r_mesh->vertices+ttri->index[1]*3);
  823. glTexCoord2fv(ttri->st+4);
  824. glVertex3fv(r_mesh->vertices+ttri->index[2]*3);
  825. }
  826. break;
  827. case modeWireframe:
  828. glVertex3fv(r_mesh->vertices+ttri->index[0]*3);
  829. glVertex3fv(r_mesh->vertices+ttri->index[1]*3);
  830. glVertex3fv(r_mesh->vertices+ttri->index[2]*3);
  831. break;
  832. default:
  833. glTexCoord2fv(ttri->st);
  834. glVertex3fv(r_mesh->vertices+ttri->index[0]*3);
  835. glTexCoord2fv(ttri->st+2);
  836. glVertex3fv(r_mesh->vertices+ttri->index[1]*3);
  837. glTexCoord2fv(ttri->st+4);
  838. glVertex3fv(r_mesh->vertices+ttri->index[2]*3);
  839. }
  840. glEnd();
  841. }
  842. void Render::drawModelMesh(model_mesh_t *r_mesh)
  843. {
  844. texture_tri_t *ttri;
  845. int lastTexture = -1;
  846. // If they pass NULL structs let it hang up - this is tmp
  847. //! \fixme Duh, vis tests need to be put back
  848. //if (!isVisible(r_mesh->center, r_mesh->radius, r_mesh->bbox))
  849. //{
  850. // return;
  851. //}
  852. //! \fixme 'AMBIENT' -- Mongoose 2002.01.08
  853. glColor3fv(WHITE);
  854. if (mMode == modeWireframe)
  855. glColor3fv(WHITE);
  856. glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  857. glBindTexture(GL_TEXTURE_2D, 1); // White texture for colors
  858. // Colored Triagles
  859. for (unsigned int i = 0; i < r_mesh->coloredTriangles.size(); i++)
  860. {
  861. ttri = r_mesh->coloredTriangles[i];
  862. if (!ttri)
  863. continue;
  864. if (mMode != modeWireframe && mMode != modeSolid &&
  865. ttri->texture != lastTexture)
  866. {
  867. glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  868. glBindTexture(GL_TEXTURE_2D, ttri->texture+1);
  869. lastTexture = ttri->texture;
  870. }
  871. tmpRenderModelMesh(r_mesh, ttri);
  872. }
  873. // Colored Rectagles
  874. for (unsigned int i = 0; i < r_mesh->coloredRectangles.size(); i++)
  875. {
  876. ttri = r_mesh->coloredRectangles[i];
  877. if (!ttri)
  878. continue;
  879. if (mMode != modeWireframe && mMode != modeSolid &&
  880. ttri->texture != lastTexture)
  881. {
  882. glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  883. glBindTexture(GL_TEXTURE_2D, ttri->texture+1);
  884. lastTexture = ttri->texture;
  885. }
  886. tmpRenderModelMesh(r_mesh, ttri);
  887. }
  888. // Textured Tris
  889. for (unsigned int i = 0; i < r_mesh->texturedTriangles.size(); i++)
  890. {
  891. ttri = r_mesh->texturedTriangles[i];
  892. if (!ttri)
  893. continue;
  894. if (mMode != modeWireframe && mMode != modeSolid &&
  895. ttri->texture != lastTexture)
  896. {
  897. glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  898. glBindTexture(GL_TEXTURE_2D, ttri->texture+1);
  899. lastTexture = ttri->texture;
  900. }
  901. tmpRenderModelMesh(r_mesh, ttri);
  902. }
  903. // Textured Quads
  904. for (unsigned int i = 0; i < r_mesh->texturedRectangles.size(); i++)
  905. {
  906. ttri = r_mesh->texturedRectangles[i];
  907. if (!ttri)
  908. continue;
  909. if (mMode != modeWireframe && mMode != modeSolid &&
  910. ttri->texture != lastTexture)
  911. {
  912. glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  913. glBindTexture(GL_TEXTURE_2D, ttri->texture+1);
  914. lastTexture = ttri->texture;
  915. }
  916. tmpRenderModelMesh(r_mesh, ttri);
  917. }
  918. }
  919. void Render::setSkyMesh(int index, bool rot)
  920. {
  921. mSkyMesh = index;
  922. mSkyMeshRotation = rot;
  923. }
  924. void Render::ViewModel(entity_t *ent, int index)
  925. {
  926. skeletal_model_t *model;
  927. if (!ent)
  928. {
  929. return;
  930. }
  931. model = getWorld().getModel(index);
  932. if (model)
  933. {
  934. ent->modelId = index;
  935. printf("Viewmodel skeletal model %i\n", model->id);
  936. }
  937. }
  938. void Render::addSkeletalModel(SkeletalModel *mdl)
  939. {
  940. mModels.push_back(mdl);
  941. }
  942. void Render::updateViewVolume()
  943. {
  944. matrix_t proj;
  945. matrix_t mdl;
  946. glGetFloatv(GL_PROJECTION_MATRIX, proj);
  947. glGetFloatv(GL_MODELVIEW_MATRIX, mdl);
  948. mViewVolume.updateFrame(proj, mdl);
  949. }
  950. bool Render::isVisible(Box &box) {
  951. vec3_t bbox[2];
  952. box.getBoundingBox(bbox);
  953. // For debugging purposes
  954. if (mMode == Render::modeWireframe)
  955. box.display(true, PINK, RED);
  956. return mViewVolume.isBboxInFrustum(bbox[0], bbox[1]);
  957. }
  958. bool Render::isVisible(float x, float y, float z)
  959. {
  960. // For debugging purposes
  961. if (mMode == Render::modeWireframe)
  962. {
  963. glPointSize(5.0);
  964. glColor3fv(PINK);
  965. glBegin(GL_POINTS);
  966. glVertex3f(x, y, z);
  967. glEnd();
  968. }
  969. return (mViewVolume.isPointInFrustum(x, y, z));
  970. }
  971. bool Render::isVisible(float x, float y, float z, float radius)
  972. {
  973. // For debugging purposes
  974. if (mMode == Render::modeWireframe)
  975. {
  976. glPointSize(5.0);
  977. glColor3fv(PINK);
  978. glBegin(GL_POINTS);
  979. glVertex3f(x, y, z);
  980. glEnd();
  981. }
  982. return (mViewVolume.isSphereInFrustum(x, y, z, radius));
  983. }