Open Source Tomb Raider Engine
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

Render.cpp 36KB

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