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

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