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 13KB

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