Open Source Tomb Raider Engine
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

Render.cpp 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. /*!
  2. * \file src/Render.cpp
  3. * \brief OpenRaider Renderer class
  4. *
  5. * \author Mongoose
  6. * \author xythobuz
  7. */
  8. #include <algorithm>
  9. #include <stdlib.h>
  10. #include <math.h>
  11. #include <string.h>
  12. #include "global.h"
  13. #include "Camera.h"
  14. #include "Game.h"
  15. #include "OpenRaider.h"
  16. #include "Render.h"
  17. #include "TextureManager.h"
  18. #include "utils/strings.h"
  19. #include "utils/tga.h"
  20. #include "Window.h"
  21. #include "World.h"
  22. Render::Render() {
  23. mSkyMesh = -1;
  24. mSkyMeshRotation = false;
  25. mMode = Render::modeDisabled;
  26. mLock = 0;
  27. mFlags = (fRoomAlpha | fEntityModels | fRenderPonytail);
  28. }
  29. Render::~Render() {
  30. ClearWorld();
  31. }
  32. void Render::ClearWorld() {
  33. mRoomRenderList.clear();
  34. }
  35. void Render::screenShot(char *filenameBase)
  36. {
  37. int sz = getWindow().getWidth() * getWindow().getHeight();
  38. unsigned char *image = new unsigned char[sz * 3];
  39. char *filename = NULL;
  40. static int count = 0;
  41. bool done = false;
  42. assert(filenameBase != NULL);
  43. // Don't overwrite files
  44. while (!done) {
  45. filename = bufferString("%s-%04i.tga", filenameBase, count++);
  46. FILE *f = fopen(filename, "rb");
  47. if (f)
  48. fclose(f);
  49. else
  50. done = true;
  51. }
  52. // Capture frame buffer
  53. glReadPixels(0, 0, getWindow().getWidth(), getWindow().getHeight(), GL_BGR_EXT, GL_UNSIGNED_BYTE, image);
  54. tgaSave(filename, image, getWindow().getWidth(), getWindow().getHeight(), 0);
  55. printf("Took screenshot '%s'.\n", filename);
  56. delete [] filename;
  57. delete [] image;
  58. }
  59. unsigned int Render::getFlags() {
  60. return mFlags;
  61. }
  62. void Render::lightRoom(Room &room)
  63. {
  64. for (unsigned int i = 0; i < room.sizeLights(); ++i)
  65. {
  66. Light &light = room.getLight(i);
  67. float pos[4], color[4];
  68. float dir[3];
  69. light.getPos(pos);
  70. light.getColor(color);
  71. light.getDir(dir);
  72. glEnable(GL_LIGHT0 + i);
  73. switch (light.getType())
  74. {
  75. case Light::typeSpot:
  76. glLightf(GL_LIGHT0 + i, GL_SPOT_CUTOFF, light.getCutoff());
  77. glLightfv(GL_LIGHT0 + i, GL_POSITION, pos);
  78. glLightfv(GL_LIGHT0 + i, GL_SPOT_DIRECTION, dir);
  79. glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, color);
  80. break;
  81. case Light::typePoint:
  82. case Light::typeDirectional:
  83. glLightf(GL_LIGHT0 + i, GL_CONSTANT_ATTENUATION, 1.0); // 1.0
  84. // GL_QUADRATIC_ATTENUATION
  85. // GL_LINEAR_ATTENUATION
  86. glLightf(GL_LIGHT0 + i, GL_LINEAR_ATTENUATION, light.getAtt());
  87. glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, color); // GL_DIFFUSE
  88. glLightfv(GL_LIGHT0 + i, GL_POSITION, pos);
  89. break;
  90. }
  91. }
  92. }
  93. void Render::clearFlags(unsigned int flags)
  94. {
  95. mFlags &= ~flags;
  96. if (flags & Render::fFog)
  97. {
  98. if (glIsEnabled(GL_FOG))
  99. {
  100. glDisable(GL_FOG);
  101. }
  102. }
  103. if (flags & Render::fGL_Lights)
  104. {
  105. glDisable(GL_LIGHTING);
  106. }
  107. }
  108. void Render::setFlags(unsigned int flags)
  109. {
  110. mFlags |= flags;
  111. if (flags & Render::fFog)
  112. {
  113. glEnable(GL_FOG);
  114. glFogf(GL_FOG_MODE, GL_EXP2);
  115. glFogf(GL_FOG_DENSITY, 0.00008f);
  116. glFogf(GL_FOG_START, 30000.0f);
  117. glFogf(GL_FOG_END, 50000.0f);
  118. float color[4];
  119. color[0] = BLACK[0] * 256.0f;
  120. color[1] = BLACK[1] * 256.0f;
  121. color[2] = BLACK[2] * 256.0f;
  122. color[3] = BLACK[3] * 256.0f;
  123. glFogfv(GL_FOG_COLOR, color);
  124. }
  125. if (flags & Render::fGL_Lights)
  126. {
  127. float color[4];
  128. color[0] = WHITE[0] * 256.0f;
  129. color[1] = WHITE[1] * 256.0f;
  130. color[2] = WHITE[2] * 256.0f;
  131. color[3] = WHITE[3] * 256.0f;
  132. glEnable(GL_LIGHTING);
  133. glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 0);
  134. glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color);
  135. glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color);
  136. color[0] = GREY[0] * 256.0f;
  137. color[1] = GREY[1] * 256.0f;
  138. color[2] = GREY[2] * 256.0f;
  139. color[3] = GREY[3] * 256.0f;
  140. glLightModelfv(GL_LIGHT_MODEL_AMBIENT, color);
  141. }
  142. }
  143. int Render::getMode()
  144. {
  145. return mMode;
  146. }
  147. void Render::setMode(int n)
  148. {
  149. mMode = n;
  150. switch (mMode)
  151. {
  152. case Render::modeDisabled:
  153. break;
  154. case Render::modeSolid:
  155. case Render::modeWireframe:
  156. glClearColor(PURPLE[0] * 256.0f, PURPLE[1] * 256.0f,
  157. PURPLE[2] * 256.0f, PURPLE[3] * 256.0f);
  158. glDisable(GL_TEXTURE_2D);
  159. break;
  160. default:
  161. if (mMode == Render::modeLoadScreen)
  162. {
  163. glBlendFunc(GL_SRC_ALPHA, GL_ONE);
  164. }
  165. else
  166. {
  167. glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
  168. }
  169. glClearColor(BLACK[0], BLACK[1], BLACK[2], BLACK[3]);
  170. glEnable(GL_TEXTURE_2D);
  171. }
  172. }
  173. void Render::display()
  174. {
  175. switch (mMode)
  176. {
  177. case Render::modeDisabled:
  178. return;
  179. case Render::modeLoadScreen:
  180. //! \fixme entry for seperate main drawing method -- Mongoose 2002.01.01
  181. drawLoadScreen();
  182. return;
  183. default:
  184. break;
  185. }
  186. if (mMode == Render::modeWireframe)
  187. glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
  188. else
  189. glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  190. float camOffsetH = 0.0f;
  191. switch (getGame().getLara().getMoveType())
  192. {
  193. case Entity::MoveTypeFly:
  194. case Entity::MoveTypeNoClipping:
  195. case Entity::MoveTypeSwim:
  196. //camOffsetH = 64.0f;
  197. camOffsetH = 512.0f;
  198. break;
  199. case Entity::MoveTypeWalk:
  200. case Entity::MoveTypeWalkNoSwim:
  201. camOffsetH = 512.0f;
  202. break;
  203. }
  204. float curPos[3], camPos[3], atPos[3];
  205. curPos[0] = getGame().getLara().getPos(0);
  206. curPos[1] = getGame().getLara().getPos(1);
  207. curPos[2] = getGame().getLara().getPos(2);
  208. float yaw = getGame().getLara().getAngle(1);
  209. // Mongoose 2002.08.24, New 3rd person camera hack
  210. camPos[0] = curPos[0] - (1024.0f * sinf(yaw));
  211. camPos[1] = curPos[1] - camOffsetH; // UP is lower val
  212. camPos[2] = curPos[2] - (1024.0f * cosf(yaw));
  213. long index = getGame().getLara().getRoom();
  214. long sector = getWorld().getRoom(index).getSector(camPos[0], camPos[2]);
  215. // Handle camera out of world
  216. if ((sector < 0) ||
  217. ((unsigned int)sector >= getWorld().getRoom(index).sizeSectors()) ||
  218. getWorld().getRoom(index).isWall(sector)) {
  219. camPos[0] = curPos[0] + (64.0f * sinf(yaw));
  220. camPos[1] -= 64.0f;
  221. camPos[2] = curPos[2] + (64.0f * cosf(yaw));
  222. }
  223. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  224. glLoadIdentity();
  225. // Setup view in OpenGL with camera
  226. getCamera().setPosition(camPos);
  227. getCamera().update();
  228. getCamera().getTarget(atPos);
  229. // Mongoose 2002.08.13, Quick fix to render OpenRaider upside down
  230. getWindow().lookAt(camPos[0], camPos[1], camPos[2], atPos[0], atPos[1], atPos[2], 0.0f, -1.0f, 0.0f);
  231. // Update view volume for vising
  232. updateViewVolume();
  233. // Render world
  234. glColor3ubv(GREY); // was WHITE
  235. drawSkyMesh(96.0);
  236. // Figure out how much of the map to render
  237. newRoomRenderList(index);
  238. // Room solid pass, need to do depth sorting to avoid 2 pass render
  239. for (unsigned int i = 0; i < mRoomRenderList.size(); i++) {
  240. Room &room = *mRoomRenderList[i];
  241. if (mFlags & Render::fGL_Lights)
  242. lightRoom(room);
  243. room.display(false);
  244. }
  245. // Draw all visible enities
  246. if (mFlags & Render::fEntityModels) {
  247. std::vector<Entity *> entityRenderList;
  248. for (unsigned int i = 0; i < getWorld().sizeEntity(); i++) {
  249. Entity &e = getWorld().getEntity(i);
  250. // Don't show Lara to the player
  251. if (&e == &getGame().getLara())
  252. continue;
  253. // Mongoose 2002.08.15, Nothing to draw, skip
  254. // Mongoose 2002.12.24, Some entities have no animation =p
  255. if (e.getModel().size() == 0)
  256. continue;
  257. // Is it in view volume? ( Hack to use sphere )
  258. if (!isVisible(e.getPos(0), e.getPos(1), e.getPos(2), 512.0f))
  259. continue;
  260. //! \fixme Is it in a room we're rendering?
  261. //if (mRoomRenderList[e->room] == 0x0)
  262. //{
  263. // continue;
  264. //}
  265. entityRenderList.push_back(&e);
  266. }
  267. // Draw objects not tied to rooms
  268. glPushMatrix();
  269. // Draw lara or other player model ( move to entity rendering method )
  270. getGame().getLara().display();
  271. // Draw sprites after player to handle alpha
  272. for (unsigned int i = 0; i < getWorld().sizeSprite(); i++) {
  273. SpriteSequence &sprite = getWorld().getSprite(i);
  274. for (unsigned int j = 0; j < sprite.size(); j++)
  275. sprite.get(j).display();
  276. }
  277. glPopMatrix();
  278. // Depth sort entityRenderList and display each entity
  279. std::sort(entityRenderList.begin(), entityRenderList.end(), Entity::compare);
  280. for (unsigned int i = 0; i < entityRenderList.size(); i++) {
  281. entityRenderList[i]->display();
  282. }
  283. }
  284. // Room alpha pass
  285. // Skip room alpha pass for modes w/o texture
  286. if (!(mMode == Render::modeSolid || mMode == Render::modeWireframe)) {
  287. for (unsigned int i = 0; i < mRoomRenderList.size(); i++)
  288. mRoomRenderList[i]->display(true);
  289. }
  290. if (mMode == Render::modeWireframe)
  291. glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
  292. glFlush();
  293. }
  294. void Render::drawLoadScreen()
  295. {
  296. float x = 0.0f, y = 0.0f, z = 0.0f;
  297. float w = getWindow().getWidth(), h = getWindow().getHeight();
  298. if (getTextureManager().getTextureCount() <= 0)
  299. return;
  300. // Mongoose 2002.01.01, Rendered while game is loading...
  301. //! \fixme seperate logo/particle coor later
  302. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  303. glLoadIdentity();
  304. glColor3ubv(WHITE);
  305. if (mFlags & Render::fGL_Lights)
  306. glDisable(GL_LIGHTING);
  307. // Mongoose 2002.01.01, Draw logo/load screen
  308. glTranslatef(0.0f, 0.0f, -2000.0f);
  309. glRotatef(180.0f, 1.0f, 0.0f, 0.0f);
  310. if (getTextureManager().getTextureCount() < 2)
  311. getTextureManager().bindTextureId(0); //! \fixme store texture id somewhere
  312. else
  313. getTextureManager().bindTextureId(1);
  314. glBegin(GL_TRIANGLE_STRIP);
  315. glTexCoord2f(1.0, 1.0);
  316. glVertex3f(x + w, y + h, z);
  317. glTexCoord2f(0.0, 1.0);
  318. glVertex3f(x - w, y + h, z);
  319. glTexCoord2f(1.0, 0.0);
  320. glVertex3f(x + w, y - h, z);
  321. glTexCoord2f(0.0, 0.0);
  322. glVertex3f(x - w, y - h, z);
  323. glEnd();
  324. if (mFlags & Render::fGL_Lights)
  325. glEnable(GL_LIGHTING);
  326. glFlush();
  327. }
  328. void Render::newRoomRenderList(int index)
  329. {
  330. static int currentRoomId = -1;
  331. if (index == -1) // -1 is error, so draw room 0, for the hell of it
  332. {
  333. mRoomRenderList.clear();
  334. mRoomRenderList.push_back(&getWorld().getRoom(0));
  335. }
  336. else
  337. {
  338. // Update room render list if needed
  339. if (currentRoomId != index)
  340. {
  341. buildRoomRenderList(getWorld().getRoom(index));
  342. }
  343. }
  344. currentRoomId = index;
  345. }
  346. void Render::buildRoomRenderList(Room &room)
  347. {
  348. // Must be visible
  349. //! \fixme Add depth sorting here - remove multipass
  350. if (!isVisible(room.getBoundingBox()))
  351. return;
  352. // Must not already be cached
  353. for (unsigned int i = 0; i < mRoomRenderList.size(); i++)
  354. {
  355. Room *room2 = mRoomRenderList[i];
  356. if (room2 == &room)
  357. return;
  358. }
  359. /* Add current room to list */
  360. mRoomRenderList.push_back(&room);
  361. // Try to add adj rooms and their adj rooms, skip this room
  362. for (unsigned int i = 1; i < room.sizeAdjacentRooms(); i++)
  363. {
  364. if (room.getAdjacentRoom(i) < 0)
  365. continue;
  366. Room &room2 = getWorld().getRoom(room.getAdjacentRoom(i));
  367. //! \fixme Add portal visibility check here
  368. if (&room2 != &room)
  369. buildRoomRenderList(room2);
  370. }
  371. }
  372. void Render::drawSkyMesh(float scale)
  373. {
  374. //skeletal_model_t *model = getWorld().getModel(mSkyMesh);
  375. //if (!model)
  376. // return;
  377. glDisable(GL_DEPTH_TEST);
  378. glPushMatrix();
  379. if (mSkyMeshRotation)
  380. glRotated(90.0, 1, 0, 0);
  381. glTranslated(0.0, 1000.0, 0.0);
  382. glScaled(scale, scale, scale);
  383. //drawModel(model);
  384. //drawModelMesh(getWorld().getMesh(mSkyMesh), );
  385. glPopMatrix();
  386. glEnable(GL_DEPTH_TEST);
  387. }
  388. void Render::setSkyMesh(int index, bool rot)
  389. {
  390. mSkyMesh = index;
  391. mSkyMeshRotation = rot;
  392. }
  393. void Render::updateViewVolume()
  394. {
  395. float proj[16], mdl[16];
  396. glGetFloatv(GL_PROJECTION_MATRIX, proj);
  397. glGetFloatv(GL_MODELVIEW_MATRIX, mdl);
  398. mViewVolume.updateFrame(proj, mdl);
  399. }
  400. bool Render::isVisible(BoundingBox &box) {
  401. float bbox[2][3];
  402. box.getBoundingBox(bbox);
  403. // For debugging purposes
  404. if (mMode == Render::modeWireframe)
  405. box.display(true, PINK, RED);
  406. return mViewVolume.isBboxInFrustum(bbox[0], bbox[1]);
  407. }
  408. bool Render::isVisible(float x, float y, float z)
  409. {
  410. // For debugging purposes
  411. if (mMode == Render::modeWireframe)
  412. {
  413. glPointSize(5.0);
  414. glColor3ubv(PINK);
  415. glBegin(GL_POINTS);
  416. glVertex3f(x, y, z);
  417. glEnd();
  418. }
  419. return (mViewVolume.isPointInFrustum(x, y, z));
  420. }
  421. bool Render::isVisible(float x, float y, float z, float radius)
  422. {
  423. // For debugging purposes
  424. if (mMode == Render::modeWireframe)
  425. {
  426. glPointSize(5.0);
  427. glColor3ubv(PINK);
  428. glBegin(GL_POINTS);
  429. glVertex3f(x, y, z);
  430. glEnd();
  431. }
  432. return (mViewVolume.isSphereInFrustum(x, y, z, radius));
  433. }