1 #ifndef H_LEVEL
2 #define H_LEVEL
3 
4 #include "core.h"
5 #include "utils.h"
6 #include "format.h"
7 #include "cache.h"
8 #include "enemy.h"
9 #include "camera.h"
10 #include "lara.h"
11 #include "objects.h"
12 #include "inventory.h"
13 #include "savegame.h"
14 #include "network.h"
15 #include "extension.h"
16 
17 #if defined(_DEBUG) && defined(_GAPI_GL) && !defined(_GAPI_GLES)
18     #define DEBUG_RENDER
19 #endif
20 
21 #ifdef DEBUG_RENDER
22     #include "debug.h"
23 #endif
24 
25 #define ANIM_TEX_TIMESTEP (10.0f / 30.0f)
26 #define SKY_TIME_PERIOD   (1.0f / 0.005f)
27 
28 extern void loadLevelAsync(Stream *stream, void *userData);
29 
30 extern Array<SaveSlot> saveSlots;
31 extern SaveResult saveResult;
32 extern int loadSlot;
33 
34 struct Level : IGame {
35 
36     TR::Level   level;
37     Texture     *atlasRooms;
38     Texture     *atlasObjects;
39     Texture     *atlasSprites;
40     Texture     *atlasGlyphs;
41     MeshBuilder *mesh;
42 
43     Lara        *players[2], *player;
44     Camera      *camera;
45     Texture     *shadow[2];
46     Texture     *scaleTex;
47 
48     struct Params {
49         float   time;
50         float   waterHeight;
51         float   reserved[2];
52     } *params;
53 
54     ZoneCache    *zoneCache;
55     AmbientCache *ambientCache;
56     WaterCache   *waterCache;
57 
58     Sound::Sample *sndTrack, *sndWater;
59     bool waitTrack;
60 
61     bool lastTitle;
62     bool isEnded;
63     bool needRedrawTitleBG;
64     bool needRedrawReflections;
65     bool needRenderGame;
66     bool needRenderInventory;
67     bool showStats;
68     bool skyIsVisible;
69 
70     TR::LevelID nextLevel;
71 
72     TR::Effect::Type effect;
73     float      effectTimer;
74     int        effectIdx;
75     float      cutsceneWaitTimer;
76     float      animTexTimer;
77     float      statsTimeDelta;
78 
79     vec3 underwaterColor;
80     vec4 underwaterFogParams;
81     vec4 levelFogParams;
82 
83     mat4 mLightProj[2];
84 
85 // IGame implementation ========
loadLevelLevel86     virtual void loadLevel(TR::LevelID id) {
87         sndWater = sndTrack = NULL;
88         Sound::stopAll();
89         nextLevel = id;
90     }
91 
loadNextLevelLevel92     virtual void loadNextLevel() {
93         if (nextLevel != TR::LVL_MAX) return;
94 
95         TR::LevelID id = TR::LVL_MAX;
96     //#ifdef _OS_WEB
97     //    if (level.id == TR::LVL_TR1_2 && level.version != TR::VER_TR1_PC)
98     //        id = TR::LVL_TR1_TITLE;
99     //    else
100     //#endif
101         id = (level.isEnd() || level.isHome()) ? level.getTitleId() : TR::LevelID(level.id + 1);
102 
103         TR::isGameEnded = level.isEnd();
104 
105         if (!level.isTitle() && loadSlot == -1) {
106         // update statistics info for current level
107             if (!TR::isCutsceneLevel(level.id) && !level.isHome())
108                 saveGame(level.id, false, true);
109         // save next level
110             if (!TR::isCutsceneLevel(id) && !TR::isTitleLevel(id)) {
111                 saveGame(id, false, false);
112                 loadSlot  = getSaveSlot(id, false);
113                 showStats = true;
114             }
115         }
116         loadLevel(id);
117     }
118 
119     virtual void invShow(int playerIndex, int page, int itemIndex = -1) {
120         if (itemIndex != -1 || page == Inventory::PAGE_SAVEGAME)
121             inventory->pageItemIndex[page] = itemIndex;
122         inventory->toggle(playerIndex, Inventory::Page(page));
123     }
124 
125     SaveSlot createSaveSlot(TR::LevelID id, bool checkpoint, bool dummy = false) {
126         SaveSlot slot;
127 
128         // allocate oversized data for save slot
129         slot.data  = new uint8[sizeof(SaveStats) + sizeof(SaveItem) * inventory->itemsCount + // for every save
130                                sizeof(SaveState) + sizeof(SaveEntity) * level.entitiesCount]; // only for checkpoints
131 
132         uint8 *ptr = slot.data;
133 
134     // level progress stats
135         SaveStats *stats = (SaveStats*)ptr;
136         if (!checkpoint)
137             memset(stats, 0, sizeof(*stats));
138         else
139             *stats = saveStats;
140         stats->level      = id;
141         stats->checkpoint = checkpoint;
142         ptr += sizeof(*stats);
143 
144     // inventory items
145         int32 *itemsCount = (int32*)ptr;
146         ptr += sizeof(*itemsCount);
147 
148         *itemsCount = 0;
149         if (dummy) {
150             SaveItem *item = (SaveItem*)ptr;
151             ptr += sizeof(*item);
152 
153             item->type  = TR::Entity::INV_PISTOLS;
154             item->count = UNLIMITED_AMMO;
155 
156             *itemsCount = 1;
157         } else {
158             for (int i = 0; i < inventory->itemsCount; i++) {
159                 Inventory::Item *invItem = inventory->items[i];
160 
161                 if (!TR::Entity::isPickup(TR::Level::convFromInv(invItem->type)))
162                     continue;
163 
164                 if (!checkpoint) {
165                     if (!TR::Entity::isCrossLevelItem(TR::Level::convFromInv(invItem->type)))
166                         continue;
167                     if (TR::isEmptyLevel(id))
168                         continue;
169                 }
170 
171                 SaveItem *item = (SaveItem*)ptr;
172                 ptr += sizeof(*item);
173 
174                 item->type  = invItem->type;
175                 item->count = invItem->count;
176 
177                 (*itemsCount)++;
178             }
179         }
180 
181         if (checkpoint) {
182         // level state
183             SaveState *state = (SaveState*)ptr;
184             ptr += sizeof(*state);
185             *state = level.state;
186 
187         // level entities
188             int32 *entitiesCount = (int32*)ptr;
189             ptr += sizeof(*entitiesCount);
190 
191             *entitiesCount = 0;
192             for (int i = 0; i < level.entitiesCount; i++) {
193                 Controller *controller = (Controller*)level.entities[i].controller;
194                 SaveEntity *entity = (SaveEntity*)ptr;
195                 if (!controller || !controller->getSaveData(*entity)) continue;
196                 ptr += (sizeof(SaveEntity) - sizeof(SaveEntity::Extra)) + entity->extraSize;
197                 (*entitiesCount)++;
198             }
199         }
200 
201         slot.size = int32(ptr - slot.data);
202 
203         return slot;
204     }
205 
parseSaveSlotLevel206     void parseSaveSlot(const SaveSlot &slot) {
207         clearInventory();
208 
209         uint8 *data = slot.data;
210         uint8 *ptr  = data;
211 
212     // level progress stats
213         if (slot.isCheckpoint())
214             saveStats = *(SaveStats*)ptr; // start level current position
215 
216         ptr += sizeof(saveStats);
217 
218     // inventory items
219         int32 itemsCount = *(int32*)ptr;
220         ptr += sizeof(itemsCount);
221 
222         for (int i = 0; i < itemsCount; i++) {
223             SaveItem *item = (SaveItem*)ptr;
224             inventory->add(TR::Entity::Type(item->type), item->count, false);
225             ptr += sizeof(*item);
226         }
227 
228         if (slot.isCheckpoint()) {
229             clearEntities();
230 
231         // level state
232             level.state = *(SaveState*)ptr;
233             ptr += sizeof(level.state);
234 
235         // level entities
236             int32 entitiesCount = *(int32*)ptr;
237             ptr += sizeof(entitiesCount);
238 
239             for (int i = 0; i < entitiesCount; i++) {
240                 SaveEntity *entity = (SaveEntity*)ptr;
241 
242                 Controller *controller;
243                 if (i >= level.entitiesBaseCount)
244                     controller = addEntity(TR::Entity::Type(entity->type), entity->room, vec3(float(entity->x), float(entity->y), float(entity->z)), TR::angle(entity->rotation));
245                 else
246                     controller = (Controller*)level.entities[i].controller;
247 
248                 controller->setSaveData(*entity);
249                 if (Controller::first != controller && controller->flags.state != TR::Entity::asNone) {
250                     controller->next = Controller::first;
251                     Controller::first = controller;
252                 }
253 
254                 if (controller->getEntity().isLara()) {
255                     Lara *lara = (Lara*)controller;
256                     if (lara->camera)
257                         lara->camera->reset();
258                 }
259 
260                 ptr += (sizeof(SaveEntity) - sizeof(SaveEntity::Extra)) + entity->extraSize;
261             }
262 
263             if (level.state.flags.flipped) {
264                 flipMap();
265                 level.state.flags.flipped = true;
266             }
267 
268             uint8 track = level.state.flags.track;
269             level.state.flags.track = 0;
270             playTrack(track);
271         }
272 
273         statsTimeDelta = 0.0f;
274     }
275 
saveGameWriteAsyncLevel276     static void saveGameWriteAsync(Stream *stream, void *userData) {
277         if (stream != NULL) {
278             delete[] stream->data;
279             delete stream;
280             saveResult = SAVE_RESULT_SUCCESS;
281             UI::showHint(STR_HINT_SAVING_DONE, 1.0f);
282         } else {
283             saveResult = SAVE_RESULT_ERROR;
284             UI::showHint(STR_HINT_SAVING_ERROR, 3.0f);
285         }
286     }
287 
saveGameLevel288     virtual void saveGame(TR::LevelID id, bool checkpoint, bool updateStats) {
289         ASSERT(saveResult != SAVE_RESULT_WAIT);
290 
291         if (saveResult == SAVE_RESULT_WAIT)
292             return;
293 
294         LOG("Save Game...\n");
295 
296         SaveSlot slot;
297         if (updateStats) {
298             removeSaveSlot(id, true);
299             int index = getSaveSlot(id, false);
300             if (index == -1) {
301                 slot = createSaveSlot(id, false, true);
302                 saveSlots.push(slot);
303             } else
304                 slot = saveSlots[index];
305             SaveStats *stats = (SaveStats*)slot.data;
306             stats->level      = level.id;
307             stats->checkpoint = checkpoint;
308         } else {
309             removeSaveSlot(id, checkpoint); // remove checkpoints and level saves
310             saveSlots.push(createSaveSlot(id, checkpoint));
311         }
312 
313         saveSlots.sort();
314 
315         if (!updateStats) {
316             saveResult = SAVE_RESULT_WAIT;
317             UI::showHint(STR_HINT_SAVING, 60.0f);
318 
319             int size;
320             uint8 *data = writeSaveSlots(size);
321             osWriteSlot(new Stream(SAVE_FILENAME, (const char*)data, size, saveGameWriteAsync, this));
322         }
323     }
324 
loadGameLevel325     virtual void loadGame(int slot) {
326         LOG("Load Game...\n");
327         loadSlot = slot;
328     }
329 
clearInventoryLevel330     void clearInventory() {
331         int i = inventory->itemsCount;
332 
333         while (i--) {
334             if (TR::Entity::isPickup(TR::Level::convFromInv(inventory->items[i]->type)))
335                 inventory->remove(i);
336         }
337     }
338 
clearEntitiesLevel339     void clearEntities() {
340         Controller::first = NULL;
341         for (int i = 0; i < level.entitiesCount; i++) {
342             TR::Entity &e = level.entities[i];
343             Controller *controller = (Controller*)e.controller;
344             if (controller) {
345                 controller->next = NULL;
346                 controller->flags.state = TR::Entity::asNone;
347                 if (i >= level.entitiesBaseCount) {
348 
349                     if (e.type == TR::Entity::ENEMY_SKATEBOARD) {
350                         continue;
351                     }
352 
353                     delete controller;
354                     e.controller = NULL;
355                 }
356             }
357         }
358     }
359 
initShadowLevel360     void initShadow() {
361         delete shadow[0];
362         delete shadow[1];
363         shadow[0] = shadow[1] = NULL;
364     #ifndef FFP
365         if (Core::settings.detail.shadows > Core::Settings::LOW) {
366             if (level.isTitle())
367                 shadow[0] = new Texture(32, 32, 1, FMT_SHADOW); // init dummy shadow map
368             else
369                 shadow[0] = new Texture(SHADOW_TEX_SIZE, SHADOW_TEX_SIZE, 1, FMT_SHADOW, OPT_TARGET);
370         }
371     #endif
372     }
373 
applySettingsLevel374     virtual void applySettings(const Core::Settings &settings) {
375         if (settings.detail.filter != Core::settings.detail.filter) {
376             atlasRooms->setFilterQuality(settings.detail.filter);
377             atlasObjects->setFilterQuality(settings.detail.filter);
378             atlasSprites->setFilterQuality(settings.detail.filter);
379         }
380 
381         bool rebuildMesh    = settings.detail.water    != Core::settings.detail.water;
382         bool rebuildAmbient = settings.detail.lighting != Core::settings.detail.lighting;
383         bool rebuildShadows = settings.detail.shadows  != Core::settings.detail.shadows;
384         bool rebuildWater   = settings.detail.water    != Core::settings.detail.water;
385         bool switchModels   = settings.detail.simple   != Core::settings.detail.simple;
386 
387         bool rebuildShaders = rebuildWater || rebuildAmbient || rebuildShadows;
388 
389         bool redraw = memcmp(&settings.detail, &Core::settings.detail, sizeof(settings.detail)) != 0;
390 
391         bool toggleVR = (settings.detail.stereo == Core::Settings::STEREO_VR) ^ (Core::settings.detail.stereo == Core::Settings::STEREO_VR);
392 
393         Core::settings = settings;
394 
395         if (toggleVR) {
396             osToggleVR(Core::settings.detail.stereo == Core::Settings::STEREO_VR);
397         }
398 
399         Core::setVSync(Core::settings.detail.vsync != 0);
400 
401         Stream::cacheWrite("settings", (char*)&settings, sizeof(settings));
402 
403         if (rebuildShaders) {
404         #if !defined(_GAPI_D3D8) && !defined(_GAPI_D3D9) && !defined(_GAPI_D3D11) && !defined(_GAPI_GXM)
405             delete shaderCache;
406             shaderCache = new ShaderCache();
407         #endif
408         }
409 
410         if (rebuildMesh) {
411             delete mesh;
412             mesh = new MeshBuilder(&level, atlasRooms);
413         }
414 
415         if (rebuildAmbient) {
416             delete ambientCache;
417             ambientCache = Core::settings.detail.lighting > Core::Settings::MEDIUM ? new AmbientCache(this) : NULL;
418         }
419 
420         if (rebuildShadows)
421             initShadow();
422 
423         if (rebuildWater) {
424             delete waterCache;
425             waterCache = Core::settings.detail.water > Core::Settings::LOW ? new WaterCache(this) : NULL;
426         }
427 
428         if (redraw && inventory->active && !level.isTitle())
429             needRedrawTitleBG = true;
430 
431         if (switchModels)
432             resetModels();
433     }
434 
getLevelLevel435     virtual TR::Level* getLevel() {
436         return &level;
437     }
438 
getMeshLevel439     virtual MeshBuilder* getMesh() {
440         return mesh;
441     }
442 
443     virtual ICamera* getCamera(int index = -1) {
444         if (index == -1)
445             return camera;
446         if (players[index])
447             return players[index]->camera;
448         return NULL;
449     }
450 
451     virtual Controller* getLara(int index = 0) {
452         return players[index];
453     }
454 
getLaraLevel455     virtual Controller* getLara(const vec3 &pos) {
456         if (!players[1])
457             return players[0];
458         if (players[0]->health <= 0.0f)
459             return players[1];
460         if (players[1]->health <= 0.0f)
461             return players[0];
462         return (players[0]->pos - pos).length2() < (players[1]->pos - pos).length2() ? players[0] : players[1];
463     }
464 
isCutsceneLevel465     virtual bool isCutscene() {
466         if (level.isTitle()) return false;
467         return camera->mode == Camera::MODE_CUTSCENE;
468     }
469 
getRandomBoxLevel470     virtual uint16 getRandomBox(uint16 zone, uint16 *zones) {
471         ZoneCache::Item *item = zoneCache->getBoxes(zone, zones);
472         return item->boxes[rand() % item->count];
473     }
474 
findPathLevel475     virtual uint16 findPath(int ascend, int descend, bool big, int boxStart, int boxEnd, uint16 *zones, uint16 **boxes) {
476         return zoneCache->findPath(ascend, descend, big, boxStart, boxEnd, zones, boxes);
477     }
478 
updateBlocksLevel479     void updateBlocks(bool rise) {
480         for (int i = 0; i < level.entitiesBaseCount; i++) {
481             Controller *controller = (Controller*)level.entities[i].controller;
482             switch (level.entities[i].type) {
483                 case TR::Entity::BLOCK_1 :
484                 case TR::Entity::BLOCK_2 :
485                 case TR::Entity::BLOCK_3 :
486                 case TR::Entity::BLOCK_4 :
487                 case TR::Entity::BLOCK_5 :
488                     ((Block*)controller)->updateFloor(rise);
489                     break;
490                 case TR::Entity::MOVING_BLOCK :
491                     ((MovingBlock*)controller)->updateFloor(rise);
492                     break;
493                 default : ;
494             }
495         }
496     }
497 
498     virtual void flipMap(bool water = true) {
499         updateBlocks(false);
500         if (water && waterCache) waterCache->flipMap();
501         mesh->flipMap();
502         level.flipMap();
503         updateBlocks(true);
504     }
505 
setWaterParamsLevel506     virtual void setWaterParams(float height) {
507         params->waterHeight = height;
508     }
509 
waterDropLevel510     virtual void waterDrop(const vec3 &pos, float radius, float strength) {
511         if (waterCache)
512             waterCache->addDrop(pos, radius, strength);
513     }
514 
515     virtual void setShader(Core::Pass pass, Shader::Type type, bool underwater = false, bool alphaTest = false) {
516         shaderCache->bind(pass, type, (underwater ? ShaderCache::FX_UNDERWATER : 0) | (alphaTest ? ShaderCache::FX_ALPHA_TEST : 0));
517     }
518 
519     virtual void setRoomParams(int roomIndex, Shader::Type type, float diffuse, float ambient, float specular, float alpha, bool alphaTest = false) {
520         if (Core::pass == Core::passShadow) {
521             setShader(Core::pass, type, false, alphaTest);
522             return;
523         }
524 
525         TR::Room &room = level.rooms[roomIndex];
526 
527         if (room.dynLightsCount) {
528             Core::lightPos[3]   = room.dynLights[0].pos;
529             Core::lightColor[3] = room.dynLights[0].color;
530         } else {
531             Core::lightPos[3]   = vec4(0);
532             Core::lightColor[3] = vec4(0, 0, 0, 1);
533         }
534 
535         if (type == Shader::SPRITE) {
536             alphaTest = true;
537         }
538 
539     #ifdef FFP
540         switch (type) {
541             case Shader::SPRITE :
542             case Shader::ROOM   :
543                 ambient = 1.0f;
544                 Core::lightColor[0].w = 1.0f;
545                 break;
546             case Shader::FLASH  :
547             case Shader::MIRROR :
548                 ambient = 1.0f;
549                 Core::lightColor[0].w =
550                 Core::lightColor[1].w =
551                 Core::lightColor[2].w =
552                 Core::lightColor[3].w = 1.0f;
553                 break;
554             default : ;
555         }
556     #endif
557 
558         vec4 material;
559         if (Core::pass == Core::passAmbient) {
560             if (room.flags.water) {
561                 material = vec4(underwaterColor, 1.0f);
562             } else {
563                 material = vec4(1.0f);
564             }
565         } else {
566             material = vec4(diffuse, ambient, specular, alpha);
567         }
568 
569         setShader(Core::pass, type, (Core::pass == Core::passAmbient) ? false : room.flags.water, alphaTest);
570 
571         if (room.flags.water) {
572             Core::setFog(underwaterFogParams);
573         } else {
574             Core::setFog(levelFogParams);
575         }
576 
577         #ifdef _GAPI_SW
578             GAPI::setPalette(room.flags.water ? GAPI::swPaletteWater : GAPI::swPaletteColor);
579             GAPI::setShading(true);
580         #endif
581 
582         Core::setMaterial(material.x, material.y, material.z, material.w);
583 
584         if (room.flags.water) {
585             if (waterCache) {
586                 waterCache->bindCaustics(roomIndex);
587             }
588             setWaterParams(float(room.waterLevel[level.state.flags.flipped]));
589         } else {
590             setWaterParams(NO_WATER_HEIGHT);
591         }
592 
593         Core::active.shader->setParam(uParam, Core::params);
594 
595         Core::updateLights();
596 
597         if (Core::settings.detail.shadows > Core::Settings::MEDIUM) {
598             Core::active.shader->setParam(uContacts, Core::contacts[0], MAX_CONTACTS);
599         }
600     }
601 
setupBindingLevel602     virtual void setupBinding() {
603         Core::whiteTex->bind(sNormal);
604         Core::whiteTex->bind(sMask);
605         Core::whiteTex->bind(sReflect);
606         atlasRooms->bind(sDiffuse);
607 
608         if (Core::pass != Core::passShadow) {
609             Texture *shadowMap = shadow[player ? player->camera->cameraIndex : 0];
610             if (shadowMap) shadowMap->bind(sShadow);
611         }
612 
613         Core::basis.identity();
614     }
615 
616     virtual void renderEnvironment(int roomIndex, const vec3 &pos, Texture **targets, int stride = 0, Core::Pass pass = Core::passAmbient) {
617         #ifdef FFP
618             return;
619         #endif
620 
621         #ifdef _GAPI_SW
622             return;
623         #endif
624 
625         #ifdef _GAPI_C3D
626             GAPI::rotate90 = false;
627         #endif
628 
629         #ifdef _GAPI_D3D8
630             GAPI::setFrontFace(false);
631         #endif
632 
633         PROFILE_MARKER("ENVIRONMENT");
634         setupBinding();
635         float      tmpEye  = Core::eye;
636         Core::Pass tmpPass = Core::pass;
637         Core::eye = 0.0f;
638 
639         int16 rIndex = roomIndex;
640         level.getSector(rIndex, pos); // fix room index for overlapped blocks
641 
642     // render level into cube faces or texture images
643         for (int i = 0; i < 6; i++) {
644             setupCubeCamera(pos, i);
645             Core::pass = pass;
646             if (targets[0]->opt & OPT_CUBEMAP) {
647                 Core::setTarget(targets[0], NULL, RT_CLEAR_COLOR | RT_CLEAR_DEPTH | RT_STORE_COLOR, i);
648             } else {
649                 Core::setTarget(targets[i * stride], NULL, RT_CLEAR_COLOR | RT_CLEAR_DEPTH | RT_STORE_COLOR);
650             }
651             renderView(rIndex, false, false);
652         }
653 
654         #ifdef _GAPI_D3D8
655             GAPI::setFrontFace(true);
656         #endif
657 
658         #ifdef _GAPI_C3D
659             GAPI::rotate90 = true;
660         #endif
661 
662         Core::pass = tmpPass;
663         Core::eye  = tmpEye;
664     }
665 
renderModelFullLevel666     virtual void renderModelFull(int modelIndex, bool underwater, Basis *joints) {
667         vec4 ambient[6] = { vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0) };
668 
669         // opaque
670         Core::setBlendMode(bmPremult); // inventory items has fade-out/in alpha
671         mesh->transparent = 0;
672         setShader(Core::passCompose, Shader::ENTITY, underwater, false);
673         Core::setBasis(joints, level.models[modelIndex].mCount);
674         Core::active.shader->setParam(uMaterial, Core::active.material);
675         Core::active.shader->setParam(uAmbient, ambient[0], 6);
676         Core::setFog(FOG_NONE);
677         Core::updateLights();
678         mesh->renderModel(modelIndex, underwater);
679         // transparent
680         mesh->transparent = 1;
681         setShader(Core::passCompose, Shader::ENTITY, underwater, true);
682         Core::setBasis(joints, level.models[modelIndex].mCount);
683         Core::active.shader->setParam(uMaterial, Core::active.material);
684         Core::active.shader->setParam(uAmbient, ambient[0], 6);
685         Core::setFog(FOG_NONE);
686         Core::updateLights();
687         mesh->renderModel(modelIndex, underwater);
688         // additive
689         Core::setBlendMode(bmAdd);
690         Core::setDepthWrite(false);
691         mesh->transparent = 2;
692         setShader(Core::passCompose, Shader::ENTITY, underwater, false);
693         Core::setBasis(joints, level.models[modelIndex].mCount);
694         Core::active.shader->setParam(uMaterial, Core::active.material);
695         Core::active.shader->setParam(uAmbient, ambient[0], 6);
696         Core::setFog(FOG_NONE);
697         Core::updateLights();
698         mesh->renderModel(modelIndex, underwater);
699         Core::setDepthWrite(true);
700         Core::setBlendMode(bmNone);
701         mesh->transparent = 0;
702     }
703 
setEffectLevel704     virtual void setEffect(Controller *controller, TR::Effect::Type effect) {
705         this->effect      = effect;
706         this->effectTimer = 0.0f;
707         this->effectIdx   = 0;
708 
709         switch (effect) {
710             case TR::Effect::FLOOR_SHAKE :
711                 for (int i = 0; i < 2; i++)
712                     if (players[i] && players[i]->camera)
713                         players[i]->camera->shake = 0.5f * max(0.0f, 1.0f - (controller->pos - players[i]->camera->eye.pos).length2() / (15 * 1024 * 15 * 1024));
714                 return;
715             case TR::Effect::FLOOD : {
716                 Sound::Sample *sample = playSound(TR::SND_FLOOD);
717                 if (sample)
718                     sample->setVolume(0.0f, 4.0f);
719                 break;
720             }
721             case TR::Effect::STAIRS2SLOPE :
722                 playSound(TR::SND_STAIRS2SLOPE);
723                 break;
724             case TR::Effect::EXPLOSION :
725                 playSound(TR::SND_TNT);
726                 shakeCamera(1.0f);
727                 break;
728             default : ;
729         }
730     }
731 
checkTriggerLevel732     virtual void checkTrigger(Controller *controller, bool heavy) {
733         players[0]->checkTrigger(controller, heavy);
734     }
735 
736     virtual void shakeCamera(float value, bool add = false) {
737         for (int i = 0; i < 2; i++)
738             if (players[i] && players[i]->camera) {
739                 if (add)
740                     players[i]->camera->shake += value;
741                 else
742                     players[i]->camera->shake  = value;
743             }
744     }
745 
746 
addEntityLevel747     virtual Controller* addEntity(TR::Entity::Type type, int room, const vec3 &pos, float angle) {
748         int index;
749         for (index = level.entitiesBaseCount; index < level.entitiesCount; index++) {
750             TR::Entity &e = level.entities[index];
751             if (!e.controller) {
752                 e.type          = type;
753                 e.room          = room;
754                 e.x             = int(pos.x);
755                 e.y             = int(pos.y);
756                 e.z             = int(pos.z);
757                 e.rotation      = TR::angle(normalizeAngle(angle));
758                 e.intensity     = -1;
759                 e.flags.value   = 0;
760                 e.flags.smooth  = true;
761                 e.modelIndex    = level.getModelIndex(e.type);
762                 break;
763             }
764         }
765 
766         if (index == level.entitiesCount)
767             return NULL;
768 
769         TR::Entity &e = level.entities[index];
770         if (e.isPickup())
771             e.intensity = 4096;
772         else
773             if (e.isSprite()) {
774                 if (e.type == TR::Entity::LAVA_PARTICLE || e.type == TR::Entity::FLAME)
775                     e.intensity = 0; // emissive
776                 else
777                     e.intensity = 0x1FFF - level.rooms[room].ambient;
778             }
779 
780         Controller *controller = initController(index);
781         e.controller = controller;
782 
783         if (e.isEnemy() || e.isSprite()) {
784             controller->flags.active = TR::ACTIVE;
785             controller->activate();
786         }
787 
788         return controller;
789     }
790 
removeEntityLevel791     virtual void removeEntity(Controller *controller) {
792         level.entities[controller->entity].controller = NULL;
793         delete controller;
794     }
795 
addMuzzleFlashLevel796     virtual void addMuzzleFlash(Controller *owner, int joint, const vec3 &offset, int lightIndex) {
797         MuzzleFlash *mf = (MuzzleFlash*)addEntity(TR::Entity::MUZZLE_FLASH, owner->getRoomIndex(), offset, 0);
798         if (mf) {
799             mf->owner      = owner;
800             mf->joint      = joint;
801             mf->lightIndex = lightIndex;
802         }
803     }
804 
invUseLevel805     virtual bool invUse(int playerIndex, TR::Entity::Type type) {
806         if (!players[playerIndex]->useItem(type))
807             return inventory->use(type);
808         return true;
809     }
810 
invAddLevel811     virtual void invAdd(TR::Entity::Type type, int count) {
812         inventory->add(type, count);
813     }
814 
invCountLevel815     virtual int* invCount(TR::Entity::Type type) {
816         return inventory->getCountPtr(type);
817     }
818 
invChooseKeyLevel819     virtual bool invChooseKey(int playerIndex, TR::Entity::Type hole) {
820         return inventory->chooseKey(playerIndex, hole);
821     }
822 
823     virtual Sound::Sample* playSound(int id, const vec3 &pos = vec3(0.0f), int flags = 0) const {
824         if (level.version == TR::VER_TR1_PSX && id == TR::SND_SECRET)
825             return NULL;
826 
827         if (!level.soundsInfo) return NULL;
828 
829         int16 a = level.soundsMap[id];
830         if (a == -1) return NULL;
831 
832         TR::SoundInfo &b = level.soundsInfo[a];
833         if (b.chance == 0 || randf() <= b.chance) {
834             int   index  = b.index + rand() % b.flags.count;
835             float volume = b.volume;
836             float pitch  = 1.0f + (b.flags.pitch ? ((randf() - 0.5f) * b.pitch) : 0.0f);
837 
838             if (level.version == TR::VER_TR2_PSX) // fix for 8 kHz VAG in PSX TR2
839                 pitch *= 8000.0f / 11025.0f;
840 
841             if (!(flags & Sound::MUSIC)) {
842                 switch (b.flags.mode) {
843                     case 0 : if (level.version & TR::VER_TR1) flags |= Sound::UNIQUE; break;
844                     case 1 : flags |= (level.version & TR::VER_TR1) ? Sound::REPLAY : Sound::UNIQUE; break;
845                     case 2 : if (level.version & TR::VER_TR1)    flags |= Sound::LOOP; break;
846                     case 3 : if (!(level.version & TR::VER_TR1)) flags |= Sound::LOOP | Sound::UNIQUE; break;
847                 }
848             }
849             if (b.flags.gain) volume = max(0.0f, volume - randf() * 0.25f);
850             //if (b.flags.camera) flags &= ~Sound::PAN;
851             return Sound::play(level.getSampleStream(index), &pos, volume, pitch, flags, id);
852         }
853         return NULL;
854     }
855 
stopChannelLevel856     void stopChannel(Sound::Sample *channel) {
857         if (channel == sndTrack) {
858             sndTrack = NULL;
859             if (level.state.flags.track != TR::LEVEL_INFO[level.id].track && TR::LEVEL_INFO[level.id].track != TR::NO_TRACK) // play ambient track
860                 playTrack(0);
861         }
862     }
863 
864     struct TrackRequest {
865         Level *level;
866         int   flags;
867 
TrackRequestLevel::TrackRequest868         TrackRequest(Level *level, int flags) : level(level), flags(flags) {}
869     };
870 
playAsyncLevel871     static void playAsync(Stream *stream, void *userData) {
872         TrackRequest *req = (TrackRequest*)userData;
873         Level *level = req->level;
874         level->waitTrack = false;
875         if (stream) {
876             level->sndTrack = Sound::play(stream, NULL, 0.01f, 1.0f, req->flags);
877             if (level->sndTrack) {
878                 if (level->level.isCutsceneLevel()) {
879                     Core::resetTime();
880                 }
881                 level->sndTrack->volume = level->sndTrack->volumeTarget = 0.0f;
882             }
883         }
884 
885         delete req;
886     }
887 
playAsyncBGLevel888     static void playAsyncBG(Stream *stream, void *userData) {
889         TrackRequest *req = (TrackRequest*)userData;
890         if (stream)
891             Sound::play(stream, NULL, 1.0f, 1.0f, req->flags);
892         delete req;
893     }
894 
895     virtual void playTrack(uint8 track, bool background = false) {
896         if (background) {
897             TR::getGameTrack(level.version, track, playAsyncBG, new TrackRequest(this, Sound::MUSIC));
898             return;
899         }
900 
901         if (track == 0) {
902             if (sndTrack) return;
903             track = TR::LEVEL_INFO[level.id].track;
904         }
905 
906         if (level.state.flags.track == track) {
907         //    if (sndTrack) {
908         //        sndTrack->replay();
909         //        sndTrack->setVolume(1.0f, 0.2f);
910         //    }
911             return;
912         }
913 
914         level.state.flags.track = track;
915 
916         if (sndTrack) {
917             sndTrack->setVolume(-1.0f, 0.2f);
918             sndTrack = NULL;
919         }
920 
921         if (track == 0xFF) return;
922 
923         int flags = Sound::MUSIC;
924         if (track == TR::LEVEL_INFO[level.id].track)
925             flags |= Sound::LOOP;
926 
927         waitTrack = true;
928         TR::getGameTrack(level.version, track, playAsync, new TrackRequest(this, flags));
929 
930         UI::showSubs(TR::getSubs(level.version, track));
931     }
932 
stopTrackLevel933     virtual void stopTrack() {
934         playTrack(0xFF);
935     }
936 //==============================
937 
LevelLevel938     Level(Stream &stream) : level(stream), waitTrack(false), isEnded(false), cutsceneWaitTimer(0.0f), animTexTimer(0.0f), statsTimeDelta(0.0f) {
939         level.simpleItems = Core::settings.detail.simple == 1;
940         level.initModelIndices();
941 
942     #ifdef _GAPI_GU
943         GAPI::freeEDRAM();
944     #endif
945         nextLevel = TR::LVL_MAX;
946         showStats = false;
947 
948         params = (Params*)&Core::params;
949         params->time = 0.0f;
950 
951         memset(players, 0, sizeof(players));
952         player = NULL;
953 
954         underwaterColor     = vec3(0.6f, 0.9f, 0.9f);
955         underwaterFogParams = vec4(underwaterColor * 0.2f, 1.0f / (6 * 1024));
956         levelFogParams      = TR::getFogParams(level.id);
957 
958         inventory->game = this;
959 
960         if (!level.isCutsceneLevel()) {
961             inventory->reset();
962             memset(&saveStats, 0, sizeof(saveStats));
963             saveStats.level = level.id;
964         }
965 
966         initTextures();
967         mesh = new MeshBuilder(&level, atlasRooms);
968         initEntities();
969 
970         shadow[0] = shadow[1] = NULL;
971         scaleTex     = NULL;
972         camera       = NULL;
973         ambientCache = NULL;
974         waterCache   = NULL;
975         zoneCache    = NULL;
976 
977         needRedrawTitleBG = false;
978         needRedrawReflections = true;
979 
980         initShadow();
981 
982         if (!(lastTitle = level.isTitle())) {
983             ASSERT(players[0] != NULL);
984             player = players[0];
985             camera = player->camera;
986 
987             zoneCache    = new ZoneCache(this);
988             ambientCache = Core::settings.detail.lighting > Core::Settings::MEDIUM ? new AmbientCache(this) : NULL;
989             waterCache   = Core::settings.detail.water    > Core::Settings::LOW    ? new WaterCache(this)   : NULL;
990 
991             if (ambientCache) { // at first calculate ambient cube for Lara
992                 AmbientCache::Cube cube;
993                 ambientCache->getAmbient(players[0]->getRoomIndex(), players[0]->pos, cube); // add to queue
994             }
995 
996             for (int i = 0; i < level.soundSourcesCount; i++) {
997                 TR::SoundSource &src = level.soundSources[i];
998                 int flags = Sound::PAN;
999                 if (src.flags & 64)  flags |= Sound::FLIPPED;
1000                 if (src.flags & 128) flags |= Sound::UNFLIPPED;
1001                 playSound(src.id, vec3(float(src.x), float(src.y), float(src.z)), flags);
1002             }
1003 
1004         }
1005 
1006         effect  = TR::Effect::NONE;
1007 
1008         sndWater = sndTrack = NULL;
1009         UI::init(this);
1010 
1011         /*
1012         if (level.id == TR::LVL_TR2_RIG) {
1013             lara->animation.setAnim(level.models[level.extra.laraSpec].animation);
1014             camera->doCutscene(lara->pos, lara->angle.y);
1015         }
1016         */
1017 
1018         saveResult = SAVE_RESULT_SUCCESS;
1019         if (loadSlot != -1 && saveSlots[loadSlot].getLevelID() == level.id) {
1020             parseSaveSlot(saveSlots[loadSlot]);
1021             loadSlot = -1;
1022         }
1023 
1024         Network::start(this);
1025 
1026         Core::resetTime();
1027     }
1028 
~LevelLevel1029     virtual ~Level() {
1030         UI::init(NULL);
1031 
1032         Network::stop();
1033 
1034         for (int i = 0; i < level.entitiesCount; i++)
1035             delete (Controller*)level.entities[i].controller;
1036 
1037         delete shadow[0];
1038         delete shadow[1];
1039         delete scaleTex;
1040         delete ambientCache;
1041         delete waterCache;
1042         delete zoneCache;
1043 
1044         delete atlasRooms;
1045         #ifndef FFP
1046             delete atlasObjects;
1047             delete atlasSprites;
1048             delete atlasGlyphs;
1049         #endif
1050         delete mesh;
1051 
1052         Sound::stopAll();
1053     }
1054 
initLevel1055     void init(bool playLogo, bool playVideo) {
1056         inventory->init(playLogo, playVideo);
1057     }
1058 
initEntitiesLevel1059     void initEntities() {
1060         for (int i = 0; i < level.entitiesBaseCount; i++) {
1061             TR::Entity &e = level.entities[i];
1062             e.controller = initController(i);
1063             if (e.type == TR::Entity::LARA || ((level.version & TR::VER_TR1) && e.type == TR::Entity::CUT_1))
1064                 players[0] = (Lara*)e.controller;
1065         }
1066 
1067         Sound::listenersCount = 1;
1068     }
1069 
resetModelsLevel1070     void resetModels() {
1071         level.simpleItems = Core::settings.detail.simple == 1;
1072         level.initModelIndices();
1073         for (int i = 0; i < level.entitiesCount; i++) {
1074             TR::Entity &e = level.entities[i];
1075             if (!e.controller) continue;
1076             Controller *controller = (Controller*)e.controller;
1077             controller->updateModel();
1078         }
1079     }
1080 
addPlayerLevel1081     void addPlayer(int index) {
1082         if (level.isCutsceneLevel()) return;
1083 
1084         if (!players[index]) {
1085             players[index] = (Lara*)addEntity(TR::Entity::LARA, 0, vec3(0.0f), 0.0f);
1086             players[index]->camera->cameraIndex = index;
1087             Sound::listenersCount = 2;
1088         } else if (index == 1) {
1089             removePlayer(index);
1090             Sound::listenersCount = 1;
1091             return;
1092         }
1093 
1094         Lara *lead = players[index ^ 1];
1095         if (!lead) return;
1096 
1097         players[index]->reset(lead->getRoomIndex(), lead->pos, lead->angle.y, lead->stand);
1098     }
1099 
removePlayerLevel1100     void removePlayer(int index) {
1101         for (int i = 0; i < level.entitiesCount; i++) {
1102             if (level.entities[i].controller && level.entities[i].isEnemy()) {
1103                 Enemy *e = (Enemy*)level.entities[i].controller;
1104                 if (e->target == players[index]) {
1105                     e->target = NULL;
1106                 }
1107             }
1108         }
1109 
1110         Controller *c = Controller::first;
1111         while (c) {
1112             Controller *next = c->next;
1113             if (c->getEntity().type == TR::Entity::FLAME && ((Flame*)c)->owner == players[index])
1114                 removeEntity(c);
1115             c = next;
1116         }
1117 
1118         removeEntity(players[index]);
1119         players[index] = NULL;
1120     }
1121 
initControllerLevel1122     Controller* initController(int index) {
1123         if (level.entities[index].type == TR::Entity::CUT_1 && (level.version & TR::VER_TR1))
1124             return new Lara(this, index);
1125 
1126         switch (level.entities[index].type) {
1127             case TR::Entity::LARA                  : return new Lara(this, index);
1128             case TR::Entity::ENEMY_DOPPELGANGER    : return new Doppelganger(this, index);
1129             case TR::Entity::ENEMY_WOLF            : return new Wolf(this, index);
1130             case TR::Entity::ENEMY_BEAR            : return new Bear(this, index);
1131             case TR::Entity::ENEMY_BAT             : return new Bat(this, index);
1132             case TR::Entity::ENEMY_PUMA            :
1133             case TR::Entity::ENEMY_LION_MALE       :
1134             case TR::Entity::ENEMY_LION_FEMALE     : return new Lion(this, index);
1135             case TR::Entity::ENEMY_RAT_LAND        :
1136             case TR::Entity::ENEMY_RAT_WATER       : return new Rat(this, index);
1137             case TR::Entity::ENEMY_REX             : return new Rex(this, index);
1138             case TR::Entity::ENEMY_RAPTOR          : return new Raptor(this, index);
1139             case TR::Entity::ENEMY_MUTANT_1        :
1140             case TR::Entity::ENEMY_MUTANT_2        :
1141             case TR::Entity::ENEMY_MUTANT_3        : return new Mutant(this, index);
1142             case TR::Entity::ENEMY_CENTAUR         : return new Centaur(this, index);
1143             case TR::Entity::ENEMY_MUMMY           : return new Mummy(this, index);
1144             case TR::Entity::ENEMY_CROCODILE_LAND  :
1145             case TR::Entity::ENEMY_CROCODILE_WATER : return new Crocodile(this, index);
1146             case TR::Entity::ENEMY_GORILLA         : return new Gorilla(this, index);
1147             case TR::Entity::ENEMY_LARSON          : return new Larson(this, index);
1148             case TR::Entity::ENEMY_PIERRE          : return new Pierre(this, index);
1149             case TR::Entity::ENEMY_SKATEBOY        : return new SkaterBoy(this, index);
1150             case TR::Entity::ENEMY_COWBOY          : return new Cowboy(this, index);
1151             case TR::Entity::ENEMY_MR_T            : return new MrT(this, index);
1152             case TR::Entity::ENEMY_NATLA           : return new Natla(this, index);
1153             case TR::Entity::ENEMY_GIANT_MUTANT    : return new GiantMutant(this, index);
1154             case TR::Entity::DOOR_1                :
1155             case TR::Entity::DOOR_2                :
1156             case TR::Entity::DOOR_3                :
1157             case TR::Entity::DOOR_4                :
1158             case TR::Entity::DOOR_5                :
1159             case TR::Entity::DOOR_6                :
1160             case TR::Entity::DOOR_7                :
1161             case TR::Entity::DOOR_8                : return new Door(this, index);
1162             case TR::Entity::TRAP_DOOR_1           :
1163             case TR::Entity::TRAP_DOOR_2           : return new TrapDoor(this, index);
1164             case TR::Entity::BRIDGE_1              :
1165             case TR::Entity::BRIDGE_2              :
1166             case TR::Entity::BRIDGE_3              : return new Bridge(this, index);
1167             case TR::Entity::GEARS_1               :
1168             case TR::Entity::GEARS_2               :
1169             case TR::Entity::GEARS_3               : return new Gear(this, index);
1170             case TR::Entity::INV_KEY_ITEM_1        :
1171             case TR::Entity::INV_KEY_ITEM_2        :
1172             case TR::Entity::INV_KEY_ITEM_3        :
1173             case TR::Entity::INV_KEY_ITEM_4        : return new KeyItemInv(this, index);
1174             case TR::Entity::TRAP_FLOOR            : return new TrapFloor(this, index);
1175             case TR::Entity::CRYSTAL               : return new Crystal(this, index);
1176             case TR::Entity::TRAP_SWING_BLADE      : return new TrapSwingBlade(this, index);
1177             case TR::Entity::TRAP_SPIKES           : return new TrapSpikes(this, index);
1178             case TR::Entity::TRAP_BOULDER          :
1179             case TR::Entity::TRAP_BOULDERS         : return new TrapBoulder(this, index);
1180             case TR::Entity::DART                  : return new Dart(this, index);
1181             case TR::Entity::TRAP_DART_EMITTER     : return new TrapDartEmitter(this, index);
1182             case TR::Entity::DRAWBRIDGE            : return new Drawbridge(this, index);
1183             case TR::Entity::BLOCK_1               :
1184             case TR::Entity::BLOCK_2               :
1185             case TR::Entity::BLOCK_3               :
1186             case TR::Entity::BLOCK_4               :
1187             case TR::Entity::BLOCK_5               : return new Block(this, index);
1188             case TR::Entity::MOVING_BLOCK          : return new MovingBlock(this, index);
1189             case TR::Entity::TRAP_CEILING_1        :
1190             case TR::Entity::TRAP_CEILING_2        : return new TrapCeiling(this, index);
1191             case TR::Entity::TRAP_SLAM             : return new TrapSlam(this, index);
1192             case TR::Entity::TRAP_SWORD            : return new TrapSword(this, index);
1193             case TR::Entity::HAMMER_HANDLE         : return new ThorHammer(this, index);
1194             case TR::Entity::HAMMER_BLOCK          : return new ThorHammerBlock(this, index);
1195             case TR::Entity::LIGHTNING             : return new Lightning(this, index);
1196             case TR::Entity::MOVING_OBJECT         : return new MovingObject(this, index);
1197             case TR::Entity::SWITCH                :
1198             case TR::Entity::SWITCH_WATER          :
1199             case TR::Entity::SWITCH_BUTTON         :
1200             case TR::Entity::SWITCH_BIG            : return new Switch(this, index);
1201             case TR::Entity::PUZZLE_HOLE_1         :
1202             case TR::Entity::PUZZLE_HOLE_2         :
1203             case TR::Entity::PUZZLE_HOLE_3         :
1204             case TR::Entity::PUZZLE_HOLE_4         :
1205             case TR::Entity::KEY_HOLE_1            :
1206             case TR::Entity::KEY_HOLE_2            :
1207             case TR::Entity::KEY_HOLE_3            :
1208             case TR::Entity::KEY_HOLE_4            : return new KeyHole(this, index);
1209             case TR::Entity::MIDAS_HAND            : return new MidasHand(this, index);
1210             case TR::Entity::SCION_TARGET          : return new ScionTarget(this, index);
1211             case TR::Entity::WATERFALL             : return new Waterfall(this, index);
1212             case TR::Entity::NATLA_BULLET          :
1213             case TR::Entity::MUTANT_BULLET         :
1214             case TR::Entity::CENTAUR_BULLET        : return new Bullet(this, index);
1215             case TR::Entity::TRAP_LAVA             : return new TrapLava(this, index);
1216             case TR::Entity::BUBBLE                : return new Bubble(this, index);
1217             case TR::Entity::EXPLOSION             : return new Explosion(this, index);
1218             case TR::Entity::WATER_SPLASH          :
1219             case TR::Entity::BLOOD                 :
1220             case TR::Entity::SMOKE                 :
1221             case TR::Entity::SPARKLES              : return new Sprite(this, index, true, Sprite::FRAME_ANIMATED);
1222             case TR::Entity::RICOCHET              : return new Sprite(this, index, true, Sprite::FRAME_RANDOM);
1223             case TR::Entity::CENTAUR_STATUE        : return new CentaurStatue(this, index);
1224             case TR::Entity::CABIN                 : return new Cabin(this, index);
1225             case TR::Entity::MUZZLE_FLASH          : return new MuzzleFlash(this, index);
1226             case TR::Entity::LAVA_PARTICLE         : ASSERT(false); return NULL;
1227             case TR::Entity::TRAP_LAVA_EMITTER     : return new TrapLavaEmitter(this, index);
1228             case TR::Entity::FLAME                 : return new Flame(this, index);
1229             case TR::Entity::TRAP_FLAME_EMITTER    : return new TrapFlameEmitter(this, index);
1230             case TR::Entity::BOAT                  : return new Boat(this, index);
1231             case TR::Entity::EARTHQUAKE            : return new Earthquake(this, index);
1232             case TR::Entity::MUTANT_EGG_SMALL      :
1233             case TR::Entity::MUTANT_EGG_BIG        : return new MutantEgg(this, index);
1234 
1235             case TR::Entity::ENEMY_DOG              : return new Dog(this, index);
1236             case TR::Entity::ENEMY_TIGER            : return new Tiger(this, index);
1237             case TR::Entity::ENEMY_GOON_MASK_1      :
1238             case TR::Entity::ENEMY_GOON_MASK_2      :
1239             case TR::Entity::ENEMY_GOON_MASK_3      :
1240             case TR::Entity::ENEMY_GOON_KNIFE       :
1241             case TR::Entity::ENEMY_GOON_SHOTGUN     :
1242             case TR::Entity::ENEMY_RAT              :
1243             case TR::Entity::ENEMY_DRAGON_FRONT     :
1244             case TR::Entity::ENEMY_DRAGON_BACK      :
1245             case TR::Entity::ENEMY_SHARK            :
1246             case TR::Entity::ENEMY_MORAY_1          :
1247             case TR::Entity::ENEMY_MORAY_2          :
1248             case TR::Entity::ENEMY_BARACUDA         :
1249             case TR::Entity::ENEMY_DIVER            :
1250             case TR::Entity::ENEMY_GUNMAN_1         :
1251             case TR::Entity::ENEMY_GUNMAN_2         :
1252             case TR::Entity::ENEMY_GOON_STICK_1     :
1253             case TR::Entity::ENEMY_GOON_STICK_2     :
1254             case TR::Entity::ENEMY_GOON_FLAME       :
1255             case TR::Entity::UNUSED_23              :
1256             case TR::Entity::ENEMY_SPIDER           :
1257             case TR::Entity::ENEMY_SPIDER_GIANT     :
1258             case TR::Entity::ENEMY_CROW             :
1259             case TR::Entity::ENEMY_MARCO            :
1260             case TR::Entity::ENEMY_GUARD_SPEAR        :
1261             case TR::Entity::ENEMY_GUARD_SPEAR_STATUE :
1262             case TR::Entity::ENEMY_GUARD_SWORD        :
1263             case TR::Entity::ENEMY_GUARD_SWORD_STATUE :
1264             case TR::Entity::ENEMY_YETI             :
1265             case TR::Entity::ENEMY_BIRD_MONSTER     :
1266             case TR::Entity::ENEMY_EAGLE            :
1267             case TR::Entity::ENEMY_MERCENARY_1      :
1268             case TR::Entity::ENEMY_MERCENARY_2      :
1269             case TR::Entity::ENEMY_MERCENARY_3      :
1270             case TR::Entity::ENEMY_MERCENARY_SNOWMOBILE :
1271             case TR::Entity::ENEMY_MONK_1           :
1272             case TR::Entity::ENEMY_MONK_2           : return new Enemy(this, index, 100, 10, 0.0f, 0.0f);
1273             case TR::Entity::ENEMY_WINSTON          : return new Winston(this, index);
1274 
1275             case TR::Entity::CRYSTAL_PICKUP         : return new CrystalPickup(this, index);
1276             case TR::Entity::STONE_ITEM_1           :
1277             case TR::Entity::STONE_ITEM_2           :
1278             case TR::Entity::STONE_ITEM_3           :
1279             case TR::Entity::STONE_ITEM_4           : return new StoneItem(this, index);
1280 
1281             case TR::Entity::WINDOW_1               :
1282             case TR::Entity::WINDOW_2               : return new BreakableWindow(this, index);
1283 
1284             case TR::Entity::HELICOPTER_FLYING      : return new HelicopterFlying(this, index);
1285 
1286             case TR::Entity::FISH_EMITTER           : return new DummyController(this, index);
1287 
1288             default                                 : return new Controller(this, index);
1289         }
1290     }
1291 
1292     #define ATLAS_PAGE_BARS   4096
1293     #define ATLAS_PAGE_GLYPHS 8192
1294 
1295     AtlasTile *tileData;
1296     uint8 *glyphsRU;
1297     uint8 *glyphsJA;
1298     uint8 *glyphsGR;
1299     uint8 *glyphsCN;
1300 
getAdvGlyphPageLevel1301     static int getAdvGlyphPage(int index) {
1302         index -= UI::advGlyphsStart;
1303         if (index >= RU_GLYPH_COUNT) {
1304             index -= RU_GLYPH_COUNT;
1305             if (index >= JA_GLYPH_COUNT) {
1306                 index -= JA_GLYPH_COUNT;
1307                 if (index >= GR_GLYPH_COUNT) {
1308                     index -= GR_GLYPH_COUNT;
1309                     return 4 + index / 256; // CN
1310                 } else {
1311                     return 3; // GR
1312                 }
1313             } else {
1314                 return 1 + index / 256; // JA
1315             }
1316         }
1317         return 0; // RU
1318     }
1319 
fillCallbackLevel1320     static void fillCallback(Atlas *atlas, int id, int tileX, int tileY, int atlasWidth, int atlasHeight, Atlas::Tile &tile, void *userData, void *data) {
1321         static const uint32 CommonTexData[CTEX_MAX][25] = {
1322             // flash bar
1323                 { 0x00000000, 0xFFA20058, 0xFFFFFFFF, 0xFFA20058, 0x00000000 },
1324             // health bar
1325                 { 0xFF2C5D71, 0xFF5E81AE, 0xFF2C5D71, 0xFF1B4557, 0xFF16304F },
1326             // oxygen bar
1327                 { 0xFF647464, 0xFFA47848, 0xFF647464, 0xFF4C504C, 0xFF303030 },
1328             // option bar
1329                 { 0x00000000, 0x20202020, 0x20202020, 0x20202020, 0x00000000,
1330                   0x00000000, 0x60606060, 0x60606060, 0x60606060, 0x00000000,
1331                   0x00000000, 0x80808080, 0x80808080, 0x80808080, 0x00000000,
1332                   0x00000000, 0x60606060, 0x60606060, 0x60606060, 0x00000000,
1333                   0x00000000, 0x20202020, 0x20202020, 0x20202020, 0x00000000 },
1334             // white room
1335                 { 0xFFFFFFFF },
1336             // white object
1337                 { 0xFFFFFFFF },
1338             // white sprite
1339                 { 0xFFFFFFFF },
1340             };
1341 
1342         int stride = 256, uvCount;
1343         short2 *uv = NULL;
1344 
1345         Level *owner = (Level*)userData;
1346         TR::Level *level = &owner->level;
1347 
1348         AtlasColor *src, *dst = (AtlasColor*)data;
1349         short4 mm;
1350 
1351         bool isSprite = false;
1352 
1353         if (id < level->objectTexturesCount) { // textures
1354             TR::TextureInfo &t = level->objectTextures[id];
1355             mm      = t.getMinMax();
1356             src     = owner->tileData->color;
1357             uv      = t.texCoordAtlas;
1358             uvCount = 4;
1359             if (data) {
1360                 level->fillObjectTexture(owner->tileData, tile.uv, tile.tex);
1361             }
1362         } else {
1363             id -= level->objectTexturesCount;
1364 
1365             if (id < level->spriteTexturesCount) { // sprites
1366                 TR::TextureInfo &t = level->spriteTextures[id];
1367                 mm       = t.getMinMax();
1368                 src      = owner->tileData->color;
1369                 uv       = t.texCoordAtlas;
1370                 uvCount  = 2;
1371                 isSprite = true;
1372                 if (data) {
1373                     if (id < UI::advGlyphsStart) {
1374                         level->fillObjectTexture(owner->tileData, tile.uv, tile.tex);
1375                     } else {
1376                         int page = getAdvGlyphPage(id);
1377                         int offset = ATLAS_PAGE_GLYPHS + page * 256;
1378                         short4 uv = tile.uv;
1379                         uv.y -= offset;
1380                         uv.w -= offset;
1381                         Color32 *glyphsData = NULL;
1382 
1383                         switch (page) {
1384                             case 0  : glyphsData = (Color32*)owner->glyphsRU; break;
1385                             case 1  :
1386                             case 2  : glyphsData = (Color32*)owner->glyphsJA + (page - 1) * 256 * 256; break;
1387                             case 3  : glyphsData = (Color32*)owner->glyphsGR; break;
1388                             case 4  :
1389                             case 5  :
1390                             case 6  :
1391                             case 7  : glyphsData = (Color32*)owner->glyphsCN + (page - 4) * 256 * 256; break;
1392                             default : ASSERT(false);
1393                         }
1394 
1395                         level->fillObjectTexture32(owner->tileData, glyphsData, uv, tile.tex);
1396                     }
1397                 }
1398             } else { // common (generated) textures
1399                 id -= level->spriteTexturesCount;
1400 
1401                 TR::TextureInfo *tex;
1402                 mm.x = mm.y = mm.z = mm.w = 0;
1403                 stride  = 1;
1404                 uvCount = 4;
1405 
1406                 switch (id) {
1407                     case CTEX_FLASH        :
1408                     case CTEX_HEALTH       :
1409                     case CTEX_OXYGEN       :
1410                     case CTEX_OPTION       :
1411                     case CTEX_WHITE_ROOM   :
1412                     case CTEX_WHITE_OBJECT :
1413                     case CTEX_WHITE_SPRITE :
1414                         src = owner->tileData->color;
1415                         tex = &CommonTex[id];
1416                         if (id != CTEX_WHITE_ROOM && id != CTEX_WHITE_OBJECT && id != CTEX_WHITE_SPRITE) {
1417                             mm.w = 4; // height - 1
1418                             if (id == CTEX_OPTION) {
1419                                 stride = 5;
1420                                 mm.z   = 4;
1421                             }
1422                         }
1423 
1424                         for (int i = 0; i < (mm.z + 1) * (mm.w + 1); i++) {
1425                             src[i] = ((Color32*)&CommonTexData[id])[i];
1426                         }
1427                         break;
1428                     default : return;
1429                 }
1430 
1431                 uv = tex->texCoordAtlas;
1432                 uv[2].y += mm.w;
1433                 uv[3].y += mm.w;
1434                 uv[1].x += mm.z;
1435                 uv[2].x += mm.z;
1436             }
1437         }
1438 
1439         int cx = -mm.x, cy = -mm.y;
1440 
1441         if (data) {
1442             int w = mm.z - mm.x + 1;
1443             int h = mm.w - mm.y + 1;
1444             dst += tileY * atlasWidth + tileX;
1445             for (int y = -atlas->border.y; y < h + atlas->border.w; y++) {
1446                 for (int x = -atlas->border.x; x < w + atlas->border.z; x++) {
1447                     AtlasColor *p = &src[mm.y * stride + mm.x];
1448                     ASSERT((x + atlas->border.x + tileX) >= 0 && (x + atlas->border.x + tileX) < atlasWidth);
1449                     ASSERT((y + atlas->border.y + tileY) >= 0 && (y + atlas->border.y + tileY) < atlasHeight);
1450                     p += clamp(x, 0, w - 1);
1451                     p += clamp(y, 0, h - 1) * stride;
1452 
1453                     if (isSprite && (y < 0 || y >= h || x < 0 || x >= w)) {
1454                         dst[x + atlas->border.x].value = 0;
1455                     } else {
1456                         dst[x + atlas->border.x] = *p;
1457                     }
1458                 }
1459                 dst += atlasWidth;
1460             }
1461 
1462             cx += tileX + atlas->border.x;
1463             cy += tileY + atlas->border.y;
1464         }
1465 
1466         for (int i = 0; i < uvCount; i++) {
1467             if (uv[i].x == mm.z) uv[i].x++;
1468             if (uv[i].y == mm.w) uv[i].y++;
1469             uv[i].x += cx;
1470             uv[i].y += cy;
1471 
1472             uv[i].x = int32(uv[i].x) * 32767 / atlasWidth;
1473             uv[i].y = int32(uv[i].y) * 32767 / atlasHeight;
1474         }
1475 
1476     // apply ref for instanced tile
1477         if (data) return;
1478 
1479         int ref = tileX;
1480         if (ref < level->objectTexturesCount) { // textures
1481             mm = level->objectTextures[ref].getMinMaxAtlas();
1482         } else {
1483             ref -= level->objectTexturesCount;
1484             if (ref < level->spriteTexturesCount) { // sprites
1485                 mm = level->spriteTextures[ref].getMinMaxAtlas();
1486             } else {
1487                 ASSERT(!"only object textures and sprites can be instanced");
1488             }
1489         }
1490 
1491         for (int i = 0; i < uvCount; i++) {
1492             uv[i].x += mm.x;
1493             uv[i].y += mm.y;
1494         }
1495     }
1496 
1497 #ifdef _DEBUG
dumpGlyphsLevel1498     void dumpGlyphs() {
1499         ASSERT(level.tiles8);
1500 
1501         TR::SpriteSequence &seq = level.spriteSequences[level.extra.glyphs];
1502         short2 size = short2(0, 0);
1503         for (int i = 0; i < seq.sCount; i++) {
1504             TR::TextureInfo &sprite = level.spriteTextures[seq.sStart + i];
1505             short w = sprite.texCoord[1].x - sprite.texCoord[0].x + 1;
1506             short h = sprite.texCoord[1].y - sprite.texCoord[0].y + 1;
1507             size.y += h + 1;
1508             size.x = max(size.x, w);
1509         }
1510         size.x += 1;
1511         size.y += 1;
1512         size.x = (size.x + 3) / 4 * 4;
1513         Color32 *data = new Color32[int(size.x) * int(size.y)];
1514         memset(data, 128, int(size.x) * int(size.y) * 4);
1515 
1516         short2 pos = short2(1, 1);
1517         for (int i = 0; i < seq.sCount; i++) {
1518             TR::TextureInfo &sprite = level.spriteTextures[seq.sStart + i];
1519             short w = sprite.texCoord[1].x - sprite.texCoord[0].x + 1;
1520             short h = sprite.texCoord[1].y - sprite.texCoord[0].y + 1;
1521 
1522             for (int y = 0; y < h; y++)
1523                 for (int x = 0; x < w; x++) {
1524                     Tile8 &tile = level.tiles8[sprite.tile];
1525 
1526                     data[pos.x + x + (pos.y + y) * size.x] = level.getColor(tile.index[sprite.texCoord[0].x + x + (sprite.texCoord[0].y + y) * 256]);
1527                 }
1528             pos.y += h + 1;
1529         }
1530 
1531         Texture::SaveBMP("pc_glyph.bmp", (char*)data, size.x, size.y);
1532         delete[] data;
1533     }
1534 
dumpKanjiLevel1535     void dumpKanji() {
1536         Stream stream("DATA/KANJI.PSX");
1537         int size = stream.readLE32() / 2;
1538         ColorIndex4 *buffer = new ColorIndex4[size];
1539         stream.raw(buffer, size);
1540         int width = 256;
1541         int height = size / width * 2;
1542 
1543         Color32 *image = new Color32[width * height];
1544 
1545         Tile4 *tile = (Tile4*)buffer;
1546         CLUT  &clut = level.cluts[level.spriteTextures[level.kanjiSprite].clut];
1547 
1548         ColorIndex4 *idx = buffer;
1549         Color32 *ptr = image;
1550 
1551         for (int y = 0; y < height; y++) {
1552             for (int x = 0; x < width; x += 2) {
1553                 *ptr++ = clut.color[idx->a];
1554                 *ptr++ = clut.color[idx->b];
1555                 idx++;
1556             }
1557         }
1558 
1559         Texture::SaveBMP("kanji", (char*)image, width, height);
1560 
1561         delete[] buffer;
1562         delete[] image;
1563     }
1564 #endif
1565 
initTexturesLevel1566     void initTextures() {
1567     #ifndef SPLIT_BY_TILE
1568 
1569         #if defined(_GAPI_SW) || defined(_GAPI_GU)
1570             #error atlas packing is not allowed for this platform
1571         #endif
1572 
1573         #ifdef _DEBUG
1574             //dumpGlyphs();
1575             //dumpKanji();
1576         #endif
1577 
1578         UI::patchGlyphs(level);
1579 
1580         {
1581             uint32 glyphsW, glyphsH;
1582             Stream stream(NULL, GLYPH_RU, size_GLYPH_RU);
1583             glyphsRU = Texture::LoadPNG(stream, glyphsW, glyphsH);
1584         }
1585 
1586         {
1587             uint32 glyphsW, glyphsH;
1588             Stream stream(NULL, GLYPH_JA, size_GLYPH_JA);
1589             glyphsJA = Texture::LoadBMP(stream, glyphsW, glyphsH);
1590         }
1591 
1592         {
1593             uint32 glyphsW, glyphsH;
1594             Stream stream(NULL, GLYPH_GR, size_GLYPH_GR);
1595             glyphsGR = Texture::LoadBMP(stream, glyphsW, glyphsH);
1596         }
1597 
1598         {
1599             uint32 glyphsW, glyphsH;
1600             Stream stream(NULL, GLYPH_CN, size_GLYPH_CN);
1601             glyphsCN = Texture::LoadBMP(stream, glyphsW, glyphsH);
1602         }
1603 
1604     // repack texture tiles
1605         int maxTiles = level.objectTexturesCount + level.spriteTexturesCount + CTEX_MAX;
1606         Atlas *rAtlas = new Atlas(maxTiles, short4(4, 4, 4, 4), this, fillCallback);
1607         Atlas *oAtlas = new Atlas(maxTiles, short4(4, 4, 4, 4), this, fillCallback);
1608         Atlas *sAtlas = new Atlas(maxTiles, short4(4, 4, 4, 4), this, fillCallback);
1609         Atlas *gAtlas = new Atlas(maxTiles, short4(0, 0, 1, 1), this, fillCallback);
1610         // add textures
1611         for (int i = 0; i < level.objectTexturesCount; i++) {
1612             TR::TextureInfo &t = level.objectTextures[i];
1613             if (t.tile == 0xFFFF) continue;
1614 
1615             short4 uv;
1616             uv.x = min(min(t.texCoord[0].x, t.texCoord[1].x), t.texCoord[2].x);
1617             uv.y = min(min(t.texCoord[0].y, t.texCoord[1].y), t.texCoord[2].y);
1618             uv.z = max(max(t.texCoord[0].x, t.texCoord[1].x), t.texCoord[2].x) + 1;
1619             uv.w = max(max(t.texCoord[0].y, t.texCoord[1].y), t.texCoord[2].y) + 1;
1620 
1621             if (t.type == TR::TEX_TYPE_ROOM)
1622                 rAtlas->add(i, uv, &t);
1623             else
1624                 oAtlas->add(i, uv, &t);
1625         }
1626         // add sprites
1627         for (int i = 0; i < level.spriteTexturesCount; i++) {
1628             TR::TextureInfo &t = level.spriteTextures[i];
1629             if (t.tile == 0xFFFF) continue;
1630 
1631             short4 uv;
1632             uv.x = t.texCoord[0].x;
1633             uv.y = t.texCoord[0].y;
1634             uv.z = t.texCoord[1].x + 1;
1635             uv.w = t.texCoord[1].y + 1;
1636 
1637             if (i >= UI::advGlyphsStart) {
1638              // add virtual UV offset for additional glyph sprites
1639                 int offset = ATLAS_PAGE_GLYPHS + getAdvGlyphPage(i) * 256;
1640                 uv.y += offset;
1641                 uv.w += offset;
1642             }
1643 
1644             if (level.extra.glyphs != -1) {
1645                 TR::SpriteSequence &seq = level.spriteSequences[level.extra.glyphs];
1646                 if ((i >= seq.sStart && i < seq.sStart + seq.sCount) || i >= UI::advGlyphsStart) {
1647                     gAtlas->add(level.objectTexturesCount + i, uv, &t);
1648                     continue;
1649                 }
1650             }
1651             sAtlas->add(level.objectTexturesCount + i, uv, &t);
1652         }
1653         // add common textures
1654         const short2 CommonTexOffset[] = { short2(1, 5), short2(1, 5), short2(1, 5), short2(5, 5), short2(1, 1), short2(1, 1), short2(1, 1) };
1655         ASSERT(COUNT(CommonTexOffset) == CTEX_MAX);
1656         memset(CommonTex, 0, sizeof(CommonTex));
1657         for (int i = 0; i < CTEX_MAX; i++) {
1658             CommonTex[i].type = CommonTex[i].dataType = TR::TEX_TYPE_SPRITE;
1659             Atlas *dst = (i == CTEX_FLASH || i == CTEX_WHITE_OBJECT) ? oAtlas : ((i == CTEX_WHITE_ROOM) ? rAtlas : gAtlas);
1660             dst->add(level.objectTexturesCount + level.spriteTexturesCount + i, short4(i * 32, ATLAS_PAGE_BARS, i * 32 + CommonTexOffset[i].x, ATLAS_PAGE_BARS + CommonTexOffset[i].y), &CommonTex[i]);
1661         }
1662 
1663         // get result texture
1664         tileData = new AtlasTile();
1665 
1666         atlasRooms   = rAtlas->pack(OPT_MIPMAPS | OPT_VRAM_3DS);
1667         atlasObjects = oAtlas->pack(OPT_MIPMAPS);
1668         atlasSprites = sAtlas->pack(OPT_MIPMAPS);
1669         atlasGlyphs  = gAtlas->pack(0);
1670 
1671     #ifdef _OS_3DS
1672         ASSERT(atlasRooms->width   <= 1024 && atlasRooms->height   <= 1024);
1673         ASSERT(atlasObjects->width <= 1024 && atlasObjects->height <= 1024);
1674         ASSERT(atlasSprites->width <= 1024 && atlasSprites->height <= 1024);
1675     #endif
1676 
1677         delete[] tileData;
1678         tileData = NULL;
1679 
1680         delete[] glyphsRU;
1681         delete[] glyphsJA;
1682         delete[] glyphsGR;
1683         delete[] glyphsCN;
1684 
1685         glyphsRU = NULL;
1686         glyphsJA = NULL;
1687         glyphsGR = NULL;
1688         glyphsCN = NULL;
1689 
1690         atlasRooms->setFilterQuality(Core::settings.detail.filter);
1691         atlasObjects->setFilterQuality(Core::settings.detail.filter);
1692         atlasSprites->setFilterQuality(Core::settings.detail.filter);
1693         atlasGlyphs->setFilterQuality(Core::Settings::MEDIUM);
1694 
1695         delete rAtlas;
1696         delete oAtlas;
1697         delete sAtlas;
1698         delete gAtlas;
1699 
1700         LOG("rooms   : %d x %d\n", atlasRooms->width, atlasRooms->height);
1701         LOG("objects : %d x %d\n", atlasObjects->width, atlasObjects->height);
1702         LOG("sprites : %d x %d\n", atlasSprites->width, atlasSprites->height);
1703         LOG("glyphs  : %d x %d\n", atlasGlyphs->width, atlasGlyphs->height);
1704         PROFILE_LABEL(TEXTURE, atlasRooms->ID, "atlas_rooms");
1705         PROFILE_LABEL(TEXTURE, atlasObjects->ID, "atlas_objects");
1706         PROFILE_LABEL(TEXTURE, atlasSprites->ID, "atlas_sprites");
1707         PROFILE_LABEL(TEXTURE, atlasGlyphs->ID, "atlas_glyphs");
1708 
1709     #else
1710         ASSERT(level.tilesCount);
1711 
1712         #if defined(_GAPI_SW)
1713             atlasRooms   =
1714             atlasObjects =
1715             atlasSprites =
1716             atlasGlyphs  = new Texture(level.tiles8, level.tilesCount);
1717             GAPI::initPalette(level.palette, level.lightmap);
1718         #elif defined(_GAPI_GU)
1719             atlasRooms   =
1720             atlasObjects =
1721             atlasSprites =
1722             atlasGlyphs  = new Texture(level.tiles4, level.tilesCount, level.cluts, level.clutsCount);
1723         #else
1724             Texture::Tile *tiles = new Texture::Tile[level.tilesCount];
1725             for (int i = 0; i < level.tilesCount; i++) {
1726                 tiles[i].width = tiles[i].height = 256;
1727                 tiles[i].data = new uint32[256 * 256];
1728             }
1729 
1730             for (int i = 0; i < level.objectTexturesCount; i++) {
1731                 TR::TextureInfo &t = level.objectTextures[i];
1732                 short4 uv = t.getMinMax();
1733                 uv.z++;
1734                 uv.w++;
1735                 level.fillObjectTexture((AtlasTile*)tiles[t.tile].data, uv, &t);
1736             }
1737 
1738             for (int i = 0; i < level.spriteTexturesCount; i++) {
1739                 TR::TextureInfo &t = level.spriteTextures[i];
1740                 short4 uv = t.getMinMax();
1741                 uv.z++;
1742                 uv.w++;
1743                 level.fillObjectTexture((AtlasTile*)tiles[t.tile].data, uv, &t);
1744             }
1745 
1746             for (int i = 0; i < level.tilesCount; i++) {
1747                 char buf[256];
1748                 sprintf(buf, "texture/%s_%d.png", TR::LEVEL_INFO[level.id].name, i);
1749                 if (Stream::exists(buf)) {
1750                     Stream stream(buf);
1751                     delete[] tiles[i].data;
1752                     tiles[i].data = (uint32*)Texture::LoadPNG(stream, tiles[i].width, tiles[i].height);
1753                 }
1754             }
1755 
1756             atlasRooms   =
1757             atlasObjects =
1758             atlasSprites =
1759             atlasGlyphs  = new Texture(tiles, level.tilesCount);
1760 
1761             for (int i = 0; i < level.tilesCount; i++)
1762                 delete[] tiles[i].data;
1763             delete[] tiles;
1764         #endif
1765 
1766         #ifndef _GAPI_SW
1767         for (int i = 0; i < level.objectTexturesCount; i++) {
1768             TR::TextureInfo &t = level.objectTextures[i];
1769 
1770             t.texCoordAtlas[0].x <<= 7;
1771             t.texCoordAtlas[0].y <<= 7;
1772             t.texCoordAtlas[1].x <<= 7;
1773             t.texCoordAtlas[1].y <<= 7;
1774             t.texCoordAtlas[2].x <<= 7;
1775             t.texCoordAtlas[2].y <<= 7;
1776             t.texCoordAtlas[3].x <<= 7;
1777             t.texCoordAtlas[3].y <<= 7;
1778 
1779             t.texCoordAtlas[0].x += 64;
1780             t.texCoordAtlas[0].y += 64;
1781             t.texCoordAtlas[1].x += 64;
1782             t.texCoordAtlas[1].y += 64;
1783             t.texCoordAtlas[2].x += 64;
1784             t.texCoordAtlas[2].y += 64;
1785             t.texCoordAtlas[3].x += 64;
1786             t.texCoordAtlas[3].y += 64;
1787         }
1788 
1789         for (int i = 0; i < level.spriteTexturesCount; i++) {
1790             TR::TextureInfo &t = level.spriteTextures[i];
1791 
1792             t.texCoordAtlas[0].x <<= 7;
1793             t.texCoordAtlas[0].y <<= 7;
1794             t.texCoordAtlas[1].x <<= 7;
1795             t.texCoordAtlas[1].y <<= 7;
1796             /*
1797             t.texCoord[0].x += 16;
1798             t.texCoord[0].y += 16;
1799             t.texCoord[1].x += 16;
1800             t.texCoord[1].y += 16;
1801             */
1802         }
1803         #endif
1804     #endif
1805     }
1806 
initReflectionsLevel1807     void initReflections() {
1808         for (int i = 0; i < level.entitiesBaseCount; i++) {
1809             TR::Entity &e = level.entities[i];
1810             if (e.type == TR::Entity::CRYSTAL) {
1811                 Crystal *c = (Crystal*)e.controller;
1812                 if (c->environment) { // already initialized and baked
1813                     continue;
1814                 }
1815                 c->bake();
1816 
1817             #ifdef _GAPI_C3D
1818                 // C3D has a limit of GX commands for buffers clearing (GX_MemoryFill), so we limit render to one cubemap per frame
1819                 return;
1820             #endif
1821             }
1822         }
1823         needRedrawReflections = false;
1824     }
1825 
setMainLightLevel1826     void setMainLight(Controller *controller) {
1827         Core::lightPos[0]   = controller->mainLightPos;
1828         Core::lightColor[0] = vec4(controller->mainLightColor.xyz(), 1.0f / controller->mainLightColor.w);
1829     }
1830 
renderSkyLevel1831     void renderSky() {
1832         #if !defined(_GAPI_GL) && !defined(_GAPI_D3D11)
1833             return;
1834         #endif
1835         ASSERT(mesh->transparent == 0);
1836 
1837         Shader::Type type;
1838         TR::SkyParams skyParams;
1839 
1840         if (level.version & TR::VER_TR1) {
1841             if (Core::settings.detail.lighting < Core::Settings::HIGH || !Core::support.tex3D || !TR::getSkyParams(level.id, skyParams))
1842                 return;
1843             type = Shader::SKY_AZURE;
1844         } else { // TR2, TR3
1845             if (level.extra.sky == -1)
1846                 return;
1847 
1848             if (Core::settings.detail.lighting < Core::Settings::HIGH || !Core::support.tex3D) {
1849                 type = Shader::DEFAULT;
1850             } else {
1851                 type = Shader::SKY_CLOUDS;
1852                 if (!TR::getSkyParams(level.id, skyParams)) {
1853                     type = Shader::DEFAULT;
1854                 }
1855             }
1856         }
1857 
1858         if (type != Shader::DEFAULT && !Core::perlinTex) {
1859             type = Shader::DEFAULT;
1860             if (level.version & TR::VER_TR1) {
1861                 return;
1862             }
1863         }
1864 
1865         Core::Pass pass = Core::pass;
1866         mat4 mView = Core::mView;
1867         mat4 mProj = Core::mProj;
1868 
1869         Core::mView.setPos(vec3(0));
1870         Core::setViewProj(Core::mView, Core::mProj);
1871 
1872         setShader(Core::passSky, type, false, false);
1873 
1874         if (type != Shader::DEFAULT) {
1875             float time = Core::params.x;
1876             if (time > SKY_TIME_PERIOD) {
1877                 time /= SKY_TIME_PERIOD;
1878                 time = (time - int(time)) * SKY_TIME_PERIOD;
1879             }
1880 
1881             Core::active.shader->setParam(uParam,     vec4(skyParams.wind * time * 2.0f, 1.0));
1882             Core::active.shader->setParam(uLightProj, *(mat4*)&skyParams);
1883             Core::active.shader->setParam(uPosScale,  skyParams.cloudDownColor, 2);
1884 
1885             Core::perlinTex->bind(sNormal);
1886             Core::ditherTex->bind(sMask);
1887         }
1888 
1889         if (level.version & TR::VER_TR1) {
1890             Core::setCullMode(cmNone);
1891             mesh->renderBox();
1892             Core::setCullMode(cmFront);
1893         } else {
1894             Basis b;
1895             Core::setBasis(&b, 1); // unused
1896             mesh->renderModel(level.extra.sky);
1897         }
1898 
1899         Core::setViewProj(mView, mProj);
1900         Core::pass = pass;
1901     }
1902 
prepareRoomsLevel1903     void prepareRooms(RoomDesc *roomsList, int roomsCount) {
1904         skyIsVisible = (level.version & TR::VER_TR1);
1905 
1906         for (int i = 0; i < level.roomsCount; i++)
1907             level.rooms[i].flags.visible = false;
1908 
1909         for (int i = 0; i < roomsCount; i++) {
1910             TR::Room &r = level.rooms[roomsList[i].index];
1911             skyIsVisible |= r.flags.sky;
1912             r.flags.visible = true;
1913         }
1914 
1915         if (Core::pass == Core::passShadow)
1916             return;
1917 
1918         if (Core::settings.detail.shadows > Core::Settings::MEDIUM) {
1919             Sphere spheres[MAX_CONTACTS];
1920             int count = player->getSpheres(spheres);
1921 
1922             for (int i = 0; i < MAX_CONTACTS; i++)
1923                 if (i < count)
1924                     Core::contacts[i] = vec4(spheres[i].center, PI * spheres[i].radius * spheres[i].radius * 0.25f);
1925                 else
1926                     Core::contacts[i] = vec4(0.0f);
1927         }
1928 
1929         setMainLight(player);
1930     }
1931 
getPortalRectLevel1932     short4 getPortalRect(const vec4 &v, short4 vp) {
1933         //vec4 s = vec4(v.x, -v.w, v.z, -v.y);
1934         vec4 sp = (v * 0.5 + 0.5) * vec4(float(vp.z), float(vp.w), float(vp.z), float(vp.w));
1935 
1936         short4 s(short(sp.x) + vp.x, short(sp.y) + vp.y, short(sp.z)+ vp.x, short(sp.w) + vp.y);
1937 
1938         // expand
1939         s.x -= 2;
1940         s.y -= 2;
1941         s.z += 2;
1942         s.w += 2;
1943 
1944         // clamp
1945         s.x = max(s.x, vp.x);
1946         s.y = max(s.y, vp.y);
1947         s.z = min(s.z, short(vp.x + vp.z));
1948         s.w = min(s.w, short(vp.y + vp.w));
1949 
1950         // convert from bounds to x,y,w,h
1951         s.z -= s.x;
1952         s.w -= s.y;
1953 
1954         // Use the viewport rect if one of the dimensions is the same size
1955         // as the viewport. This may fix clipping bugs while still allowing
1956         // impossible geometry tricks.
1957         if (s.z - s.x >= vp.z - vp.x || s.w - s.y >= vp.w - vp.y) {
1958            return vp;
1959         }
1960 
1961         return s;
1962     }
1963 
renderRoomsLevel1964     void renderRooms(RoomDesc *roomsList, int roomsCount, int transp) {
1965         PROFILE_MARKER("ROOMS");
1966 
1967         if (Core::pass == Core::passShadow)
1968             return;
1969 
1970         Basis basis;
1971         basis.identity();
1972 
1973         Core::mModel.identity();
1974 
1975         switch (transp) {
1976             case 0 : Core::setBlendMode(bmNone);    break;
1977             case 1 : Core::setBlendMode(bmPremult); break;
1978             case 2 : Core::setBlendMode(bmAdd);   Core::setDepthWrite(false); break;
1979         }
1980 
1981         int i     = 0;
1982         int end   = roomsCount;
1983         int dir   = 1;
1984 
1985         if (transp) {
1986             i   = roomsCount - 1;
1987             end = -1;
1988             dir = -1;
1989         }
1990 
1991         atlasRooms->bind(sDiffuse);
1992 
1993         short4 vp = Core::scissor;
1994 
1995         while (i != end) {
1996             int roomIndex = roomsList[i].index;
1997             MeshBuilder::RoomRange &range = mesh->rooms[roomIndex];
1998 
1999             if (!range.geometry[transp].count && !range.dynamic[transp].count) {
2000                 i += dir;
2001                 continue;
2002             }
2003 
2004             Core::setScissor(getPortalRect(roomsList[i].portal, vp));
2005 
2006             const TR::Room &room = level.rooms[roomIndex];
2007 
2008             vec3 center = room.getCenter();
2009             int ambient = room.getAmbient(int(center.x), int(center.y), int(center.z));
2010 
2011             setRoomParams(roomIndex, Shader::ROOM, 1.0f, intensityf(ambient), 0.0f, 1.0f, transp == 1);
2012 
2013             basis.pos = room.getOffset();
2014             Core::setBasis(&basis, 1);
2015 
2016             Core::mModel.setPos(basis.pos);
2017 
2018             mesh->transparent = transp;
2019             mesh->renderRoomGeometry(roomIndex);
2020 
2021             i += dir;
2022         }
2023 
2024         Core::setDepthWrite(true);
2025 
2026         if (transp == 1) {
2027             atlasSprites->bind(sDiffuse);
2028 
2029             Core::setBlendMode(bmPremult);
2030 
2031             #ifdef MERGE_SPRITES
2032                 basis.rot = Core::mViewInv.getRot();
2033             #else
2034                 basis.rot = quat(0, 0, 0, 1);
2035             #endif
2036 
2037             for (int i = 0; i < roomsCount; i++) {
2038                 int roomIndex = roomsList[i].index;
2039                 level.rooms[roomIndex].flags.visible = true;
2040 
2041                 MeshBuilder::RoomRange &range = mesh->rooms[roomIndex];
2042 
2043                 if (!range.sprites.iCount)
2044                     continue;
2045 
2046                 Core::setScissor(getPortalRect(roomsList[i].portal, vp));
2047 
2048                 setRoomParams(roomIndex, Shader::SPRITE, 1.0f, 1.0f, 0.0f, 1.0f, true);
2049 
2050                 basis.pos = level.rooms[roomIndex].getOffset();
2051                 Core::setBasis(&basis, 1);
2052 
2053                 mesh->renderRoomSprites(roomIndex);
2054             }
2055         }
2056 
2057         Core::setScissor(vp);
2058         Core::setBlendMode(bmNone);
2059     }
2060 
renderEntityLevel2061     void renderEntity(const TR::Entity &entity) {
2062         //if (entity.room != lara->getRoomIndex()) return;
2063         if (Core::pass == Core::passShadow && !entity.castShadow()) return;
2064 
2065         Controller *controller = (Controller*)entity.controller;
2066         int roomIndex = controller->getRoomIndex();
2067         TR::Room &room = level.rooms[roomIndex];
2068 
2069         if (controller->flags.invisible)
2070             return;
2071 
2072         if (!entity.isLara() && !entity.isActor() && !room.flags.visible)
2073             return;
2074 
2075         bool isModel;
2076 
2077         if (entity.type != TR::Entity::TRAP_LAVA_EMITTER) {
2078             isModel = entity.modelIndex > 0;
2079             if (isModel) {
2080                 if (!mesh->models[controller->getModel()->index].geometry[mesh->transparent].count) return;
2081             } else {
2082                 if (level.spriteSequences[-(entity.modelIndex + 1)].transp != mesh->transparent) return;
2083             }
2084         } else {
2085             if (mesh->transparent != 2) {
2086                 return;
2087             }
2088             isModel = false;
2089         }
2090 
2091         Shader::Type type = isModel ? Shader::ENTITY : Shader::SPRITE;
2092         if (entity.type == TR::Entity::CRYSTAL)
2093             type = Shader::MIRROR;
2094 
2095         if (isModel) { // model
2096             ASSERT(controller->intensity >= 0.0f);
2097 
2098             setMainLight(level.isCutsceneLevel() ? player : controller);
2099             setRoomParams(roomIndex, type, 1.0f, controller->intensity, controller->specular, 1.0f, mesh->transparent == 1);
2100 
2101             vec3 pos = controller->getPos();
2102             if (ambientCache) {
2103                 if (!entity.isDoor() && !entity.isBlock() && !entity.isPickup()) { // no advanced ambient lighting for secret (all) doors and blocks
2104                     AmbientCache::Cube cube;
2105                     ambientCache->getAmbient(roomIndex, pos, cube);
2106                     if (cube.status == AmbientCache::Cube::READY) {
2107                         memcpy(controller->ambient, cube.colors, sizeof(cube.colors)); // store last calculated ambient into controller
2108                     }
2109                 } else {
2110                     if (entity.isPickup()) {
2111                         controller->ambient[0] =
2112                         controller->ambient[1] =
2113                         controller->ambient[5] =
2114                         controller->ambient[4] = vec4(Core::active.material.y * 0.8f);
2115                         controller->ambient[2] = vec4(Core::active.material.y * 0.1f);
2116                         controller->ambient[3] = vec4(Core::active.material.y);
2117                     } else {
2118                         controller->ambient[0] =
2119                         controller->ambient[1] =
2120                         controller->ambient[2] =
2121                         controller->ambient[3] =
2122                         controller->ambient[4] =
2123                         controller->ambient[5] = vec4(Core::active.material.y);
2124                     }
2125                 }
2126                 Core::active.shader->setParam(uAmbient, controller->ambient[0], 6);
2127             }
2128         }
2129 
2130         controller->render(camera->frustum, mesh, type, room.flags.water);
2131     }
2132 
loadNextLevelDataLevel2133     void loadNextLevelData() {
2134         isEnded = true;
2135         char buf[64];
2136         TR::getGameLevelFile(buf, level.version, nextLevel);
2137         nextLevel = TR::LVL_MAX;
2138         new Stream(buf, loadLevelAsync);
2139     }
2140 
updateLevel2141     void update() {
2142         if (isEnded) return;
2143 
2144         bool invRing = inventory->phaseRing != 0.0f && inventory->phaseRing != 1.0f;
2145         if (inventory->video || inventory->titleTimer >= 1.0f || level.isCutsceneLevel() || invRing) {
2146             memset(Input::btnEnable, 0, sizeof(Input::btnEnable));
2147             Input::btnEnable[Input::bInventory] = !invRing;
2148         } else
2149             memset(Input::btnEnable, 1, sizeof(Input::btnEnable));
2150 
2151         Input::btnEnable[Input::bWeapon] &= players[0] && players[0]->canDrawWeapon();
2152 
2153         if (inventory->isActive())
2154             Input::btnEnable[Input::bWalk] = Input::btnEnable[Input::bJump] = Input::btnEnable[Input::bWeapon] = false;
2155 
2156         if (inventory->video) {
2157             inventory->update();
2158             UI::update();
2159             return;
2160         }
2161 
2162         if (level.isCutsceneLevel() && waitTrack) {
2163             if (!sndTrack && TR::LEVEL_INFO[level.id].track != TR::NO_TRACK) {
2164                 if (camera->timer > 0.0f) // for the case that audio stops before animation ends
2165                     loadNextLevel();
2166                 return;
2167             }
2168 
2169             if (cutsceneWaitTimer > 0.0f) {
2170                 cutsceneWaitTimer -= Core::deltaTime;
2171                 if (cutsceneWaitTimer > 0.0f)
2172                     return;
2173                 if (sndTrack)
2174                     sndTrack->setVolume(1.0f, 0.0f);
2175                 cutsceneWaitTimer = 0.0f;
2176                 Core::resetTime();
2177                 LOG("reset timer - %d\n", Core::getTime());
2178                 return;
2179             }
2180         }
2181 
2182         if ((Input::lastState[0] == cInventory || Input::lastState[1] == cInventory) && !level.isTitle() && inventory->titleTimer < 1.0f && !inventory->active) {
2183             int playerIndex = (Input::lastState[0] == cInventory) ? 0 : 1;
2184 
2185             if (getLara(playerIndex)) {
2186                 if (level.isCutsceneLevel()) { // skip cutscene level
2187                     loadNextLevel();
2188                     return;
2189                 }
2190 
2191                 if (player->health <= 0.0f)
2192                     inventory->toggle(playerIndex, Inventory::PAGE_OPTION, TR::Entity::INV_PASSPORT);
2193                 else
2194                     inventory->toggle(playerIndex);
2195             }
2196         }
2197 
2198         bool invActive = inventory->isActive();
2199 
2200         inventory->update();
2201 
2202         if (inventory->titleTimer > 1.0f)
2203             return;
2204 
2205         if (nextLevel != TR::LVL_MAX && !inventory->isActive()) {
2206             if (showStats) {
2207                 inventory->toggle(0, Inventory::PAGE_LEVEL_STATS);
2208                 showStats = false;
2209                 return;
2210             }
2211             loadNextLevelData();
2212             return;
2213         }
2214 
2215         if (loadSlot > -1 && nextLevel == TR::LVL_MAX) {
2216             if (inventory->isActive())
2217                 return;
2218 
2219             loadLevel(saveSlots[loadSlot].getLevelID());
2220             return;
2221         }
2222 
2223         if (!inventory->isActive()) {
2224             UI::update();
2225         }
2226 
2227         float volWater, volTrack;
2228 
2229         if (invActive || level.isTitle()) {
2230             Sound::reverb.setRoomSize(vec3(1.0f));
2231             Sound::listener[0].underwater = false;
2232             volWater = 0.0f;
2233             volTrack = level.isTitle() ? 0.9f : 0.0f;
2234         } else {
2235 
2236             if (!level.isCutsceneLevel()) {
2237                 statsTimeDelta += Core::deltaTime;
2238                 while (statsTimeDelta >= 1.0f) {
2239                     statsTimeDelta -= 1.0f;
2240                     saveStats.time++;
2241                 }
2242             }
2243 
2244             params->time += Core::deltaTime;
2245             animTexTimer += Core::deltaTime;
2246 
2247             float timeStep = ANIM_TEX_TIMESTEP;
2248             if (level.version & TR::VER_TR1)
2249                 timeStep *= 0.5f;
2250 
2251             if (animTexTimer > timeStep) {
2252                 level.shiftAnimTex();
2253                 animTexTimer -= timeStep;
2254             }
2255 
2256             updateEffect();
2257 
2258             Controller *c = Controller::first;
2259             while (c) {
2260                 Controller *next = c->next;
2261                 c->update();
2262                 c = next;
2263             }
2264 
2265             if (waterCache)
2266                 waterCache->update();
2267 
2268             Controller::clearInactive();
2269 
2270         // underwater ambient sound volume control
2271             if (camera->isUnderwater()) {
2272                 if (!sndWater && !level.isCutsceneLevel()) {
2273                     sndWater = playSound(TR::SND_UNDERWATER, vec3(0.0f), Sound::LOOP | Sound::MUSIC);
2274                     if (sndWater)
2275                         sndWater->volume = sndWater->volumeTarget = 0.0f;
2276                 }
2277                 volWater = 1.0f;
2278             } else {
2279                 volWater = 0.0f;
2280             }
2281 
2282             Sound::listener[0].underwater = (volWater == 1.0f);
2283 
2284             volTrack = 1.0f;
2285         }
2286 
2287         if (sndWater && sndWater->volumeTarget != volWater)
2288             sndWater->setVolume(volWater, 0.2f);
2289         if (sndTrack && sndTrack->volumeTarget != volTrack)
2290             sndTrack->setVolume(volTrack, 0.2f);
2291 
2292     #ifdef _DEBUG
2293     #ifdef _NAPI_SOCKET
2294         if (Input::down[ikJ]) {
2295             //Network::sayHello();
2296             NAPI::Peer peer;
2297             peer.ip = ((93 << 0) | (92 << 8) | (200 << 16) | (163 << 24));
2298             peer.port = htons(10994);
2299             LOG("join %s:%d\n", inet_ntoa(*(in_addr*)&peer.ip), ntohs(peer.port));
2300             Network::joinGame(peer);
2301             Input::down[ikJ] = false;
2302         }
2303 
2304         if (Input::down[ikY]) {
2305             NAPI::requestAddress();
2306             Input::down[ikY] = false;
2307         }
2308     #endif
2309     #endif
2310 
2311     #ifdef GEOMETRY_EXPORT
2312         if (Input::down[ikF1]) {
2313             Extension::exportGeometry(this, atlasRooms, atlasObjects, atlasSprites);
2314             Input::down[ikF1] = false;
2315         }
2316     #endif
2317     }
2318 
updateEffectLevel2319     void updateEffect() {
2320         if (effect == TR::Effect::NONE)
2321             return;
2322 
2323         effectTimer += Core::deltaTime;
2324 
2325         switch (effect) {
2326             case TR::Effect::TR1_FLICKER : {
2327                 int idx = effectIdx;
2328                 switch (effectIdx) {
2329                     case 0 : if (effectTimer > 3.0f) effectIdx++; break;
2330                     case 1 : if (effectTimer > 3.1f) effectIdx++; break;
2331                     case 2 : if (effectTimer > 3.5f) effectIdx++; break;
2332                     case 3 : if (effectTimer > 3.6f) effectIdx++; break;
2333                     case 4 : if (effectTimer > 4.1f) { effectIdx++; effect = TR::Effect::NONE; } break;
2334                 }
2335                 if (idx != effectIdx)
2336                     flipMap();
2337                 break;
2338             }
2339             case TR::Effect::EARTHQUAKE : {
2340                 switch (effectIdx) {
2341                     case 0 : if (effectTimer > 0.0f) { playSound(TR::SND_ROCK);     effectIdx++; shakeCamera(1.0f); } break;
2342                     case 1 : if (effectTimer > 0.1f) { playSound(TR::SND_STOMP);    effectIdx++; } break;
2343                     case 2 : if (effectTimer > 0.6f) { playSound(TR::SND_BOULDER);  effectIdx++; shakeCamera(0.5f, true); } break;
2344                     case 3 : if (effectTimer > 1.1f) { playSound(TR::SND_ROCK);     effectIdx++; } break;
2345                     case 4 : if (effectTimer > 1.6f) { playSound(TR::SND_BOULDER);  effectIdx++; shakeCamera(0.5f, true); } break;
2346                     case 5 : if (effectTimer > 2.3f) { playSound(TR::SND_BOULDER);  shakeCamera(0.5f, true); effect = TR::Effect::NONE; } break;
2347                 }
2348                 break;
2349             }
2350             default : effect = TR::Effect::NONE; return;
2351         }
2352     }
2353 
setupLevel2354     void setup() {
2355         camera->setup(Core::pass == Core::passCompose);
2356         setupBinding();
2357     }
2358 
renderEntitiesTranspLevel2359     void renderEntitiesTransp(int transp) {
2360         mesh->dynBegin();
2361         mesh->transparent = transp;
2362 
2363         atlasObjects->bind(sDiffuse);
2364         for (int i = 0; i < level.entitiesCount; i++) {
2365             TR::Entity &e = level.entities[i];
2366             if (!e.controller || e.modelIndex == 0) continue;
2367             renderEntity(e);
2368         }
2369 
2370         {
2371             PROFILE_MARKER("ENTITY_SPRITES");
2372 
2373             if (mesh->dynICount) {
2374                 atlasSprites->bind(sDiffuse);
2375                 Core::lightPos[0]   = vec4(0, 0, 0, 0);
2376                 Core::lightColor[0] = vec4(0, 0, 0, 1);
2377                 setRoomParams(getLara()->getRoomIndex(), Shader::SPRITE, 1.0f, 1.0f, 0.0f, 1.0f, mesh->transparent == 1);
2378 
2379                 Basis b;
2380                 b.w   = 1.0f;
2381                 b.pos = Core::viewPos.xyz();
2382                 #ifdef MERGE_SPRITES
2383                     b.rot = Core::mViewInv.getRot();
2384                 #else
2385                     b.rot = quat(0, 0, 0, 1);
2386                 #endif
2387                 Core::setBasis(&b, 1);
2388             }
2389 
2390             mesh->dynEnd();
2391         }
2392     }
2393 
renderEntitiesLevel2394     void renderEntities(int transp) {
2395         if (Core::pass == Core::passAmbient) // TODO allow static entities
2396             return;
2397 
2398         PROFILE_MARKER("ENTITIES");
2399 
2400         if (transp == 0) {
2401             Core::setBlendMode(bmNone);
2402             renderEntitiesTransp(transp);
2403         }
2404 
2405         if (transp == 1) {
2406             Core::setBlendMode(bmPremult);
2407             renderEntitiesTransp(transp);
2408 
2409         #ifndef FFP
2410             Core::setFog(FOG_NONE);
2411             Core::whiteTex->bind(sDiffuse);
2412             Core::setBlendMode(bmMult);
2413             for (int i = 0; i < level.entitiesCount; i++) {
2414                 TR::Entity &entity = level.entities[i];
2415                 Controller *controller = (Controller*)entity.controller;
2416                 if (controller && controller->flags.rendered && controller->getEntity().castShadow())
2417                     controller->renderShadow(mesh);
2418             }
2419             Core::setBlendMode(bmNone);
2420         #endif
2421         }
2422 
2423         if (transp == 2) {
2424             Core::setDepthWrite(false);
2425             Core::setBlendMode(bmAdd);
2426             renderEntitiesTransp(transp);
2427             Core::setDepthWrite(true);
2428         }
2429     }
2430 
projectPointLevel2431     virtual vec4 projectPoint(const vec4 &p) {
2432         vec4 res;
2433         res = Core::mViewProj * p;
2434 
2435         #ifdef _OS_3DS
2436             if (GAPI::rotate90) {
2437                 res.y = -res.y;
2438                 swap(res.x, res.y);
2439             }
2440         #endif
2441 
2442         return res;
2443     }
2444 
checkPortalLevel2445     bool checkPortal(const TR::Room &room, const TR::Room::Portal &portal, const vec4 &viewPort, vec4 &clipPort) {
2446         vec3 n = portal.normal;
2447         vec3 v = Core::viewPos.xyz() - (room.getOffset() + portal.vertices[0]);
2448 
2449         if (n.dot(v) <= 0.0f)
2450             return false;
2451 
2452         int  zClip = 0;
2453         vec4 p[4];
2454 
2455         clipPort = vec4(INF, INF, -INF, -INF);
2456 
2457         for (int i = 0; i < 4; i++) {
2458             p[i] = projectPoint(vec4(vec3(portal.vertices[i]) + room.getOffset(), 1.0f));
2459 
2460             if (p[i].w > 0.0f) {
2461                 p[i].xy() *= (1.0f / p[i].w);
2462 
2463                 clipPort.x = min(clipPort.x, p[i].x);
2464                 clipPort.y = min(clipPort.y, p[i].y);
2465                 clipPort.z = max(clipPort.z, p[i].x);
2466                 clipPort.w = max(clipPort.w, p[i].y);
2467             } else
2468                 zClip++;
2469         }
2470 
2471         if (zClip == 4)
2472             return false;
2473 
2474         if (zClip > 0) {
2475             for (int i = 0; i < 4; i++) {
2476                 vec4 &a = p[i];
2477                 vec4 &b = p[(i + 1) % 4];
2478 
2479                 if ((a.w > 0.0f) ^ (b.w > 0.0f)) {
2480 
2481                     if (a.x < 0.0f && b.x < 0.0f)
2482                         clipPort.x = -1.0f;
2483                     else
2484                         if (a.x > 0.0f && b.x > 0.0f)
2485                             clipPort.z = 1.0f;
2486                         else {
2487                             clipPort.x = -1.0f;
2488                             clipPort.z =  1.0f;
2489                         }
2490 
2491                     if (a.y < 0.0f && b.y < 0.0f)
2492                         clipPort.y = -1.0f;
2493                     else
2494                         if (a.y > 0.0f && b.y > 0.0f)
2495                             clipPort.w = 1.0f;
2496                         else {
2497                             clipPort.y = -1.0f;
2498                             clipPort.w =  1.0f;
2499                         }
2500 
2501                 }
2502             }
2503         }
2504 
2505         if (clipPort.x > viewPort.z || clipPort.y > viewPort.w || clipPort.z < viewPort.x || clipPort.w < viewPort.y)
2506             return false;
2507 
2508         clipPort.x = max(clipPort.x, viewPort.x);
2509         clipPort.y = max(clipPort.y, viewPort.y);
2510         clipPort.z = min(clipPort.z, viewPort.z);
2511         clipPort.w = min(clipPort.w, viewPort.w);
2512 
2513         return true;
2514     }
2515 
2516     virtual void getVisibleRooms(RoomDesc *roomsList, int &roomsCount, int from, int to, const vec4 &viewPort, bool water, int count = 0) {
2517         if (roomsCount >= 255 || count > 16) {
2518             //ASSERT(false);
2519             return;
2520         }
2521 
2522         TR::Room &room = level.rooms[to];
2523 
2524         if (Core::pass == Core::passCompose && water && waterCache && from != TR::NO_ROOM && (level.rooms[from].flags.water ^ level.rooms[to].flags.water))
2525             waterCache->setVisible(from, to);
2526 
2527         room.flags.visible = true;
2528         roomsList[roomsCount++] = RoomDesc(to, viewPort);
2529 
2530         vec4 clipPort;
2531         for (int i = 0; i < room.portalsCount; i++) {
2532             TR::Room::Portal &p = room.portals[i];
2533 
2534             if (Core::pass == Core::passCompose && water && waterCache && (level.rooms[to].flags.water ^ level.rooms[p.roomIndex].flags.water))
2535                 waterCache->setVisible(to, p.roomIndex);
2536 
2537             if (from != room.portals[i].roomIndex && checkPortal(room, p, viewPort, clipPort))
2538                 getVisibleRooms(roomsList, roomsCount, to, p.roomIndex, clipPort, water, count + 1);
2539         }
2540     }
2541 
renderOpaqueLevel2542     void renderOpaque(RoomDesc *roomsList, int roomsCount) {
2543         renderRooms(roomsList, roomsCount, 0);
2544         renderEntities(0);
2545         if (Core::pass != Core::passShadow && skyIsVisible) {
2546             renderSky();
2547         }
2548     }
2549 
renderTransparentLevel2550     void renderTransparent(RoomDesc *roomsList, int roomsCount) {
2551         renderRooms(roomsList, roomsCount, 1);
2552         renderEntities(1);
2553     }
2554 
renderAdditiveLevel2555     void renderAdditive(RoomDesc *roomsList, int roomsCount) {
2556         // don't apply fog for additive geometry
2557         vec4 oldLevelFogParams = levelFogParams;
2558         vec4 oldUnderwaterFogParams = underwaterFogParams;
2559         levelFogParams = FOG_NONE;
2560         underwaterFogParams = FOG_NONE;
2561 
2562         renderRooms(roomsList, roomsCount, 2);
2563         renderEntities(2);
2564 
2565         levelFogParams = oldLevelFogParams;
2566         underwaterFogParams = oldUnderwaterFogParams;
2567     }
2568 
2569     virtual void renderView(int roomIndex, bool water, bool showUI, int roomsCount = 0, RoomDesc *roomsList = NULL) {
2570         PROFILE_MARKER("VIEW");
2571 
2572         if (water && waterCache)
2573             waterCache->reset();
2574 
2575         RoomDesc rList[256];
2576 
2577         if (!roomsList) {
2578             roomsList = rList;
2579 
2580             // mark all rooms as invisible
2581             for (int i = 0; i < level.roomsCount; i++)
2582                 level.rooms[i].flags.visible = false;
2583 
2584             if (level.isCutsceneLevel()) { // render all rooms except flipped
2585             // hide alternative rooms from getVisibleRooms
2586                 for (int i = 0; i < level.roomsCount; i++) {
2587                     int flipIndex = level.rooms[i].alternateRoom;
2588                     if (flipIndex > -1)
2589                         level.rooms[flipIndex].flags.visible = true;
2590                 }
2591 
2592             // get room list through portals (it will change room visible flag)
2593                 getVisibleRooms(roomsList, roomsCount, TR::NO_ROOM, roomIndex, vec4(-1.0f, -1.0f, 1.0f, 1.0f), water);
2594 
2595             // add other non-alternative rooms
2596                 for (int i = 0; i < level.roomsCount; i++)
2597                     if (!level.rooms[i].flags.visible)
2598                         roomsList[roomsCount++] = RoomDesc(i, vec4(-1.0f, -1.0f, 1.0f, 1.0f));
2599 
2600             // refresh visible flag
2601                 for (int i = 0; i < level.roomsCount; i++)
2602                     level.rooms[i].flags.visible = false;
2603 
2604                 for (int i = 0; i < roomsCount; i++)
2605                     level.rooms[roomsList[i].index].flags.visible = true;
2606             } else
2607                 getVisibleRooms(roomsList, roomsCount, TR::NO_ROOM, roomIndex, vec4(-1.0f, -1.0f, 1.0f, 1.0f), water);
2608         }
2609 
2610         if (water && waterCache) {
2611             for (int i = 0; i < roomsCount; i++)
2612                 waterCache->setVisible(roomsList[i].index);
2613 
2614             waterCache->renderReflection();
2615 
2616             Core::Pass pass = Core::pass;
2617             waterCache->simulate();
2618             Core::pass = pass;
2619         }
2620 
2621         // clear entity rendered flag (used for blob shadows)
2622         if (Core::pass != Core::passAmbient)
2623             for (int i = 0; i < level.entitiesCount; i++) {
2624                 Controller *controller = (Controller*)level.entities[i].controller;
2625                 if (controller)
2626                     controller->flags.rendered = false;
2627             }
2628 
2629         Texture *screen = NULL;
2630         if (water) {
2631             screen = (waterCache && waterCache->visible) ? waterCache->getScreenTex() : NULL;
2632 
2633             int clearFlags = RT_STORE_COLOR;
2634 
2635             if (screen) {
2636                 clearFlags |= RT_CLEAR_COLOR | RT_CLEAR_DEPTH | RT_STORE_DEPTH;
2637             }
2638 
2639             #ifndef EARLY_CLEAR
2640                 clearFlags |= RT_CLEAR_COLOR | RT_CLEAR_DEPTH;
2641             #endif
2642 
2643             Core::setTarget(screen, NULL, clearFlags); // render to screen texture or back buffer
2644             Core::validateRenderState();
2645             setupBinding();
2646         }
2647 
2648         prepareRooms(roomsList, roomsCount);
2649 
2650         renderOpaque(roomsList, roomsCount);
2651         renderTransparent(roomsList, roomsCount);
2652 
2653         if (camera->isUnderwater())
2654             renderAdditive(roomsList, roomsCount);
2655 
2656         Core::setFog(FOG_NONE);
2657 
2658         Core::setBlendMode(bmNone);
2659         if (water && waterCache && waterCache->visible) {
2660             Core::Pass pass = Core::pass;
2661             if (!camera->isUnderwater())
2662                 waterCache->renderRays();
2663             waterCache->renderMask();
2664             waterCache->copyScreenToRefraction();
2665             setMainLight(player);
2666             waterCache->compose();
2667             if (camera->isUnderwater())
2668                 waterCache->renderRays();
2669 
2670             Core::pass = pass;
2671             setupBinding();
2672         }
2673 
2674         if (!camera->isUnderwater())
2675             renderAdditive(roomsList, roomsCount);
2676 
2677         Core::setFog(FOG_NONE);
2678 
2679         Core::setBlendMode(bmNone);
2680 
2681         Core::Pass pass = Core::pass;
2682 
2683         if (water && waterCache && waterCache->visible && screen) {
2684             Core::setTarget(NULL, NULL, RT_CLEAR_DEPTH | RT_STORE_COLOR);
2685             Core::validateRenderState();
2686             waterCache->blitTexture(screen);
2687         }
2688 
2689         if (showUI) {
2690             renderUI();
2691         }
2692 
2693         Core::pass = pass;
2694     }
2695 
setupCubeCameraLevel2696     void setupCubeCamera(const vec3 &pos, int face) {
2697         vec3 up  = vec3(0, -1, 0);
2698         vec3 dir;
2699         switch (face) {
2700             case 0 : dir = vec3( 1,  0,  0); break;
2701             case 1 : dir = vec3(-1,  0,  0); break;
2702             case 2 : dir = vec3( 0,  1,  0); up = vec3(0, 0,  1); break;
2703             case 3 : dir = vec3( 0, -1,  0); up = vec3(0, 0, -1); break;
2704             case 4 : dir = vec3( 0,  0,  1); break;
2705             case 5 : dir = vec3( 0,  0, -1); break;
2706         }
2707 
2708         Core::mViewInv  = mat4(pos, pos + dir, up);
2709         Core::mView     = Core::mViewInv.inverseOrtho();
2710         Core::mProj     = GAPI::perspective(90, 1.0f, 32.0f, 45.0f * 1024.0f, 0.0f);
2711 
2712         #ifdef _GAPI_D3D8
2713             Core::mProj.scale(vec3(1.0f, -1.0f, 1.0f));
2714         #endif
2715 
2716         Core::mViewProj = Core::mProj * Core::mView;
2717         Core::viewPos   = Core::mViewInv.offset().xyz();
2718 
2719         camera->setup(false);
2720     }
2721 
renderShadowViewLevel2722     void renderShadowView(int roomIndex) {
2723         vec3 pos = player->getBoundingBox().center();
2724 
2725         float znear = camera->znear;
2726         float zfar  = player->mainLightColor.w * 1.5f;
2727 
2728         Core::mViewInv = mat4(player->mainLightPos, pos, vec3(0, -1, 0));
2729         Core::mView    = Core::mViewInv.inverseOrtho();
2730         Core::mProj    = GAPI::perspective(90.0f, 1.0f, znear, zfar, 0.0f);
2731 
2732         mat4 &m = mLightProj[player->camera->cameraIndex];
2733         m = Core::mProj * Core::mView;
2734 
2735         mat4 bias;
2736 
2737         if (GAPI::getProjRange() == mat4::PROJ_ZERO_POS)
2738             bias = mat4(
2739                 0.5f, 0.0f, 0.0f, 0.0f,
2740                 0.0f, 0.5f, 0.0f, 0.0f,
2741                 0.0f, 0.0f, 1.0f, 0.0f,
2742                 0.5f, 0.5f, 0.0f, 1.0f
2743             );
2744         else {
2745             bias = mat4(
2746                 0.5f, 0.0f, 0.0f, 0.0f,
2747                 0.0f, 0.5f, 0.0f, 0.0f,
2748                 0.0f, 0.0f, 0.5f, 0.0f,
2749                 0.5f, 0.5f, 0.5f, 1.0f
2750             );
2751         }
2752 
2753     #if defined(_GAPI_D3D8) || defined(_GAPI_D3D9) || defined(_GAPI_D3D11) || defined(_GAPI_GXM)
2754         bias.e11 = -bias.e11; // vertical flip for UVs
2755     #endif
2756 
2757         m = bias * m;
2758 
2759         Core::mLightProj = m;
2760 
2761         camera->frustum->pos = Core::viewPos.xyz();
2762         camera->frustum->calcPlanes(Core::mViewProj);
2763 
2764         setup();
2765         renderView(roomIndex, false, false);
2766     }
2767 /*
2768     void renderShadowEntity(int index, Controller *controller, Controller *player) {
2769         Box box = controller->getSpheresBox(true);
2770         mat4 m = controller->getMatrix();
2771 
2772         vec3 pos = m * box.center();
2773         Core::mViewInv = mat4(player->mainLightPos, pos, vec3(0, -1, 0));
2774         Core::mView    = Core::mViewInv.inverseOrtho();
2775         Core::mProj    = mat4(90.0f, 1.0f, 1.0f, 2.0f);
2776 
2777         Box crop = box * (Core::mProj * Core::mView * m);
2778         crop.min.z = max(0.0f, crop.min.z);
2779 
2780         float sx =  2.0f / (crop.max.x - crop.min.x);
2781         float sy =  2.0f / (crop.max.y - crop.min.y);
2782         float sz =  2.0f / (crop.max.z - crop.min.z);
2783         float ox = -0.5f * (crop.max.x + crop.min.x) * sx;
2784         float oy = -0.5f * (crop.max.y + crop.min.y) * sy;
2785         float oz = -0.5f * (crop.max.z + crop.min.z) * sz;
2786 
2787         Core::mProj = mat4(sx,  0,    0,  0,
2788                             0,  sy,   0,  0,
2789                             0,   0,  sz,  0,
2790                             ox,  oy,  oz,  1) * Core::mProj;
2791 
2792         Core::setViewProj(Core::mView, Core::mProj);
2793 
2794         mat4 bias;
2795         bias.identity();
2796         bias.e00 = bias.e11 = bias.e22 = bias.e03 = bias.e13 = bias.e23 = 0.5f;
2797         Core::mLightProj[index] = bias * (Core::mProj * Core::mView);
2798 
2799         Core::setBlendMode(bmNone);
2800 
2801         mesh->transparent = 0;
2802         if (mesh->models[controller->getEntity().modelIndex - 1].geometry[mesh->transparent].count) {
2803             setShader(Core::pass, Shader::ENTITY, false, false);
2804             controller->render(NULL, mesh, Shader::ENTITY, false);
2805         }
2806         mesh->transparent = 1;
2807         if (mesh->models[controller->getEntity().modelIndex - 1].geometry[mesh->transparent].count) {
2808             setShader(Core::pass, Shader::ENTITY, false, true);
2809             controller->render(NULL, mesh, Shader::ENTITY, false);
2810         }
2811         mesh->transparent = 0;
2812     }
2813 
2814     struct NearObj {
2815         int   player;
2816         int   index;
2817         float dist;
2818         NearObj() {}
2819         NearObj(int player, int index, float dist) : player(player), index(index), dist(dist) {}
2820     };
2821 
2822     int getNearObjects(NearObj *nearObj, int maxCount) {
2823         int count = 0;
2824 
2825         nearObj[count++] = NearObj(0, players[0]->entity, 0.0f);
2826         if (players[1])
2827             nearObj[count++] = NearObj(1, players[1]->entity, 0.0f);
2828         int base = count;
2829 
2830         for (int i = 0; i < level.entitiesCount; i++) {
2831             TR::Entity &e = level.entities[i];
2832             Controller *controller = (Controller*)e.controller;
2833             if (controller && e.castShadow() && controller != players[0] && controller != players[1]) {
2834                 int pIndex = 0;
2835                 float dist = (players[0]->getPos() - controller->getPos()).length2();
2836                 if (players[1]) {
2837                     float dist2 = (players[1]->getPos() - controller->getPos()).length2();
2838                     if (dist2 < dist) {
2839                         dist = dist2;
2840                         pIndex = 1;
2841                     }
2842                 }
2843 
2844                 if (dist > 8192 * 8192) continue;
2845 
2846             // get index to insert
2847                 int index = base;
2848                 while (index < count) {
2849                     if (dist < nearObj[index].dist)
2850                         break;
2851                     index++;
2852                 }
2853             // insertion
2854                 if (index < maxCount) {
2855                     if (count < maxCount)
2856                         count++;
2857                     for (int j = count - 1; j > index; j--)
2858                         nearObj[j] = nearObj[j - 1];
2859                     nearObj[index] = NearObj(pIndex, controller->entity, dist);
2860                 }
2861             }
2862         }
2863 
2864         return count;
2865     }
2866 */
renderShadowsLevel2867     void renderShadows(int roomIndex, Texture *shadowMap) {
2868         PROFILE_MARKER("PASS_SHADOW");
2869 
2870         if (Core::settings.detail.shadows == Core::Settings::LOW)
2871             return;
2872         ASSERT(shadow);
2873 
2874     // render to shadow map
2875         float oldEye = Core::eye;
2876         Core::eye = 0.0f;
2877 
2878         Core::pass = Core::passShadow;
2879         shadowMap->unbind(sShadow);
2880         bool colorShadow = shadowMap->fmt == FMT_RGBA ? true : false;
2881         if (colorShadow)
2882             Core::setClearColor(vec4(1.0f));
2883         Core::setTarget(shadowMap, NULL, RT_CLEAR_DEPTH | (colorShadow ? (RT_CLEAR_COLOR | RT_STORE_COLOR) : RT_STORE_DEPTH));
2884         //Core::setCullMode(cmBack);
2885         Core::validateRenderState();
2886 
2887         /*
2888         if (Core::settings.detail.shadows > Core::Settings::MEDIUM) { // per-object shadow map (atlas)
2889             NearObj nearObj[SHADOW_OBJ_MAX];
2890             int nearCount = getNearObjects(nearObj, SHADOW_OBJ_MAX);
2891 
2892             for (int i = 0; i < nearCount; i++) {
2893                 Core::setViewport((i % SHADOW_OBJ_COLS) * SHADOW_TEX_TILE, (i / SHADOW_OBJ_COLS) * SHADOW_TEX_TILE, SHADOW_TEX_TILE, SHADOW_TEX_TILE);
2894                 renderShadowEntity(i, (Controller*)level.entities[nearObj[i].index].controller, players[nearObj[i].player]);
2895             }
2896 
2897             for (int i = nearCount; i < SHADOW_OBJ_MAX; i++)
2898                 Core::mLightProj[i].identity();
2899         } else // all-in-one shadow map
2900         */
2901         renderShadowView(roomIndex);
2902 
2903         //Core::setCullMode(cmFront);
2904         if (colorShadow)
2905             Core::setClearColor(vec4(0.0f));
2906 
2907         #ifdef _GAPI_D3D11 // TODO render pass
2908             Core::setTarget(NULL, NULL, RT_CLEAR_DEPTH | RT_CLEAR_COLOR | RT_STORE_COLOR | RT_STORE_DEPTH);
2909             Core::validateRenderState();
2910         #endif
2911 
2912         Core::eye = oldEye;
2913     }
2914 
2915     #ifdef DEBUG_RENDER
renderDebugLevel2916     void renderDebug() {
2917         if (level.isTitle() || inventory->titleTimer > 1.0f) return;
2918 
2919         Core::setViewport(Core::x, Core::y, Core::width, Core::height);
2920         camera->setup(true);
2921 
2922         Debug::begin();
2923         /*
2924         lara->updateEntity(); // TODO clip angle while rotating
2925 
2926         int q = int(normalizeAngle(lara->angleExt + PI * 0.25f) / (PI * 0.5f));
2927         float radius = 256.0f;
2928 
2929         const vec2 v[] = {
2930             { -radius,  radius },
2931             {  radius,  radius },
2932             {  radius, -radius },
2933             { -radius, -radius },
2934         };
2935 
2936         const vec2 &l = v[q],
2937                    &r = v[(q + 1) % 4],
2938                    &f = (q %= 2) ? vec2(l.x, radius * cosf(lara->angleExt)) : vec2(radius * sinf(lara->angleExt), l.y);
2939 
2940         vec3 F = vec3(f.x, 0.0f, f.y);
2941         vec3 L = vec3(l.x, 0.0f, l.y);
2942         vec3 R = vec3(r.x, 0.0f, r.y);
2943 
2944         vec3 p, n = lara->pos + vec3(0.0f, -512.0f, 0.0f);
2945 
2946         Core::setDepthTest(false);
2947         glBegin(GL_LINES);
2948             glColor3f(0, 0, 1); p = n; glVertex3fv((GLfloat*)&p); p += F; glVertex3fv((GLfloat*)&p);
2949             glColor3f(1, 0, 0); p = n; glVertex3fv((GLfloat*)&p); p += L; glVertex3fv((GLfloat*)&p);
2950             glColor3f(0, 1, 0); p = n; glVertex3fv((GLfloat*)&p); p += R; glVertex3fv((GLfloat*)&p);
2951             glColor3f(1, 1, 0); p = lara->pos; glVertex3fv((GLfloat*)&p); p -= vec3(0.0f, LARA_HANG_OFFSET, 0.0f); glVertex3fv((GLfloat*)&p);
2952         glEnd();
2953         Core::setDepthTest(true);
2954         */
2955 
2956         /*
2957             glMatrixMode(GL_MODELVIEW);
2958             glPushMatrix();
2959             glLoadIdentity();
2960             glMatrixMode(GL_PROJECTION);
2961             glPushMatrix();
2962             glLoadIdentity();
2963             glOrtho(0, Core::width, 0, Core::height, 0, 1);
2964 
2965             if (shadow)
2966                 shadow->bind(sDiffuse);
2967             else
2968                 atlas->bind(sDiffuse);
2969             glEnable(GL_TEXTURE_2D);
2970             Core::setCullMode(cmNone);
2971             Core::setDepthTest(false);
2972             Core::setBlendMode(bmNone);
2973             Core::validateRenderState();
2974 
2975             glColor3f(10, 10, 10);
2976             float w = float(Core::active.textures[sDiffuse]->width);
2977             float h = float(Core::active.textures[sDiffuse]->height);
2978             glBegin(GL_QUADS);
2979                 glTexCoord2f(0, 0); glVertex2f(0, 0);
2980                 glTexCoord2f(1, 0); glVertex2f(w, 0);
2981                 glTexCoord2f(1, 1); glVertex2f(w, h);
2982                 glTexCoord2f(0, 1); glVertex2f(0, h);
2983             glEnd();
2984             glColor3f(1, 1, 1);
2985 
2986             glDisable(GL_TEXTURE_2D);
2987 
2988             glMatrixMode(GL_PROJECTION);
2989             glPopMatrix();
2990             glMatrixMode(GL_MODELVIEW);
2991             glPopMatrix();
2992         */
2993 
2994         /*
2995         Core::setDepthTest(false);
2996         glBegin(GL_LINES);
2997             glColor3f(1, 1, 1);
2998             glVertex3fv((GLfloat*)&lara->pos);
2999             glVertex3fv((GLfloat*)&lara->mainLightPos);
3000         glEnd();
3001         Core::setDepthTest(true);
3002         */
3003         //    Debug::Draw::sphere(lara->mainLightPos, lara->mainLightColor.w, vec4(1, 1, 0, 1));
3004 
3005         //    Box bbox = lara->getBoundingBox();
3006         //    Debug::Draw::box(bbox.min, bbox.max, vec4(1, 0, 1, 1));
3007 
3008             Core::setBlendMode(bmAlpha);
3009             Core::setDepthTest(false);
3010             Core::validateRenderState();
3011         //    Debug::Level::rooms(level, lara->pos, lara->getEntity().room);
3012         //    Debug::Level::lights(level, player->getRoomIndex(), player);
3013         //    Debug::Level::sectors(this, players[0]->getRoomIndex(), (int)players[0]->pos.y);
3014         //    Core::setDepthTest(false);
3015         //    Debug::Level::portals(level);
3016         //    Core::setDepthTest(true);
3017         //    Debug::Level::meshes(level);
3018         //    Debug::Level::entities(level);
3019         //    Debug::Level::zones(level, lara);
3020         //    Debug::Level::blocks(level);
3021         //    Debug::Level::path(level, (Enemy*)level.entities[105].controller);
3022         //    Debug::Level::debugOverlaps(level, lara->box);
3023         //    Debug::Level::debugBoxes(level, lara->dbgBoxes, lara->dbgBoxesCount);
3024             Core::setDepthTest(true);
3025             Core::setBlendMode(bmNone);
3026         /*// render ambient cube
3027             Core::validateRenderState();
3028 
3029             static int dbg_ambient = 0;
3030             dbg_ambient = int(params->time * 2) % 4;
3031 
3032             shadow->unbind(sShadow);
3033             Core::whiteCube->unbind(sEnvironment);
3034 
3035             glActiveTexture(GL_TEXTURE0);
3036             glEnable(GL_TEXTURE_2D);
3037             glDisable(GL_CULL_FACE);
3038             glColor3f(1, 1, 1);
3039             for (int j = 0; j < 6; j++) {
3040                 glPushMatrix();
3041                 glTranslatef(player->pos.x, player->pos.y - 1024, player->pos.z);
3042                 switch (j) {
3043                     case 0 : glRotatef( 90, 0, 1, 0); break;
3044                     case 1 : glRotatef(-90, 0, 1, 0); break;
3045                     case 2 : glRotatef(-90, 1, 0, 0); break;
3046                     case 3 : glRotatef( 90, 1, 0, 0); break;
3047                     case 4 : glRotatef(  0, 0, 1, 0); break;
3048                     case 5 : glRotatef(180, 0, 1, 0); break;
3049                 }
3050                 glTranslatef(0, 0, 256);
3051 
3052                 ambientCache->textures[j * 4 + dbg_ambient]->bind(sDiffuse);
3053                 glBegin(GL_QUADS);
3054                     glTexCoord2f(0, 0); glVertex3f(-256,  256, 0);
3055                     glTexCoord2f(1, 0); glVertex3f( 256,  256, 0);
3056                     glTexCoord2f(1, 1); glVertex3f( 256, -256, 0);
3057                     glTexCoord2f(0, 1); glVertex3f(-256, -256, 0);
3058                 glEnd();
3059                 glPopMatrix();
3060             }
3061             glEnable(GL_CULL_FACE);
3062             glDisable(GL_TEXTURE_2D);
3063 
3064 
3065             glLineWidth(4);
3066             glBegin(GL_LINES);
3067             float S = 64.0f;
3068             for (int i = 0; i < level.roomsCount; i++) {
3069                 if (i != players[0]->getRoomIndex()) continue;
3070                 TR::Room &r = level.rooms[i];
3071                 for (int j = 0; j < r.xSectors * r.zSectors; j++) {
3072                     TR::Room::Sector &s = r.sectors[j];
3073                     vec3 p = vec3(float((j / r.zSectors) * 1024 + 512 + r.info.x),
3074                                   float(max((s.floor - 2) * 256, (s.floor + s.ceiling) * 256 / 2)),
3075                                   float((j % r.zSectors) * 1024 + 512 + r.info.z));
3076 
3077                     AmbientCache::Cube &cube = ambientCache->items[ambientCache->offsets[i] + j];
3078                     if (cube.status == AmbientCache::Cube::READY) {
3079                         glColor3fv((GLfloat*)&cube.colors[0]);
3080                         glVertex3f(p.x + 0, p.y + 0, p.z + 0);
3081                         glVertex3f(p.x + S, p.y + 0, p.z + 0);
3082 
3083                         glColor3fv((GLfloat*)&cube.colors[1]);
3084                         glVertex3f(p.x + 0, p.y + 0, p.z + 0);
3085                         glVertex3f(p.x - S, p.y + 0, p.z + 0);
3086 
3087                         glColor3fv((GLfloat*)&cube.colors[2]);
3088                         glVertex3f(p.x + 0, p.y + 0, p.z + 0);
3089                         glVertex3f(p.x + 0, p.y + S, p.z + 0);
3090 
3091                         glColor3fv((GLfloat*)&cube.colors[3]);
3092                         glVertex3f(p.x + 0, p.y + 0, p.z + 0);
3093                         glVertex3f(p.x + 0, p.y - S, p.z + 0);
3094 
3095                         glColor3fv((GLfloat*)&cube.colors[4]);
3096                         glVertex3f(p.x + 0, p.y + 0, p.z + 0);
3097                         glVertex3f(p.x + 0, p.y + 0, p.z + S);
3098 
3099                         glColor3fv((GLfloat*)&cube.colors[5]);
3100                         glVertex3f(p.x + 0, p.y + 0, p.z + 0);
3101                         glVertex3f(p.x + 0, p.y + 0, p.z - S);
3102                     }
3103                 }
3104             }
3105             glEnd();
3106             glLineWidth(1);
3107         */
3108 
3109             Debug::Level::info(this, player, player->animation);
3110 
3111 
3112         Debug::end();
3113     }
3114     #endif
3115 
setViewportLevel3116     float setViewport(int view, int eye) {
3117         int vX = Core::x;
3118         int vY = Core::y;
3119         int vW = Core::width;
3120         int vH = Core::height;
3121 
3122         float aspect = float(vW) / float(vH) * Core::aspectFix;
3123 
3124         if (Core::defaultTarget) {
3125             vX = 0;
3126             vY = 0;
3127             vW = Core::defaultTarget->width;
3128             vH = Core::defaultTarget->height;
3129         }
3130 
3131         short4 &vp = Core::viewportDef;
3132         vp = short4(vX, vY, vW, vH);
3133 
3134         if (players[1] != NULL && view >= 0) {
3135             vp = short4(vX + vW / 2 * view, vY, vW / 2, vH);
3136 
3137             if (Core::settings.detail.stereo != Core::Settings::STEREO_SPLIT) {
3138                 aspect *= 0.5f;
3139             }
3140         }
3141 
3142         if (Core::settings.detail.stereo == Core::Settings::STEREO_SBS) {
3143             switch (eye) {
3144                 case -1 : vp = short4(vX + vp.x - vp.x / 2, vY + vp.y, vp.z / 2, vp.w);   break;
3145                 case +1 : vp = short4(vX + vW / 2 + vp.x / 2, vY + vp.y, vp.z / 2, vp.w); break;
3146             }
3147         }
3148 
3149         Core::setViewport(vp.x, vp.y, vp.z, vp.w);
3150 
3151         return aspect;
3152     }
3153 
renderPrepareLevel3154     void renderPrepare() {
3155         setupBinding();
3156 
3157         #ifdef _OS_3DS
3158             static float sliderState = -1.0f;
3159 
3160             float slider = osGet3DSliderState();
3161             bool isStereo = slider > 0.0f && !inventory->video;
3162 
3163             if (gfxIs3D() != isStereo) {
3164                 gfxSet3D(isStereo);
3165                 needRedrawTitleBG = inventory->active && !level.isTitle();
3166             }
3167 
3168             Core::settings.detail.stereo = isStereo ? Core::Settings::STEREO_ANAGLYPH : Core::Settings::STEREO_OFF;
3169 
3170             if (slider != sliderState) {
3171                 sliderState = slider;
3172                 needRedrawTitleBG = inventory->active && !level.isTitle();
3173             }
3174         #else
3175             if (Core::settings.detail.stereo == Core::Settings::STEREO_ANAGLYPH) {
3176                 for (int i = 0; i < 2; i++) {
3177                     Texture *&tex = Core::eyeTex[i];
3178                     if (!tex || tex->origWidth != Core::width || tex->origHeight != Core::height) {
3179                         delete tex;
3180                         tex = new Texture(Core::width, Core::height, 1, FMT_RGBA, OPT_TARGET | OPT_NEAREST);
3181                     }
3182                 }
3183             }
3184         #endif
3185 
3186         needRenderGame = !inventory->video && !level.isTitle() && ((inventory->phaseRing < 1.0f && inventory->titleTimer <= 1.0f) || needRedrawTitleBG);
3187         needRenderInventory = inventory->video || level.isTitle() || inventory->phaseRing > 0.0f || inventory->titleTimer > 0.0f;
3188 
3189         bool title  = inventory->isActive() || level.isTitle();
3190         bool copyBg = title && (lastTitle != title || needRedrawTitleBG);
3191         lastTitle = title;
3192         needRedrawTitleBG = false;
3193 
3194         if (!needRenderGame && !copyBg)
3195             return;
3196 
3197         if (needRedrawReflections) {
3198             initReflections();
3199         }
3200 
3201         if (ambientCache) {
3202             ambientCache->processQueue();
3203         }
3204 
3205     #ifndef FFP
3206         if (shadow[0] && players[0]) {
3207             player = players[0];
3208             renderShadows(player->getRoomIndex(), shadow[0]);
3209 
3210             if (players[1]) {
3211                 if (!shadow[1]) {
3212                     shadow[1] = new Texture(shadow[0]->origWidth, shadow[0]->origHeight, 1, shadow[0]->fmt, shadow[0]->opt);
3213                 }
3214 
3215                 player = players[1];
3216                 renderShadows(player->getRoomIndex(), shadow[1]);
3217             }
3218         }
3219     #endif
3220 
3221         if (copyBg) {
3222             inventory->prepareBackground();
3223         }
3224     }
3225 
setDefaultTargetLevel3226     void setDefaultTarget(int eye, int view, bool invBG) {
3227         int texIndex = eye <= 0 ? 0 : 1;
3228 
3229         if (invBG) {
3230             if (Core::settings.detail.stereo == Core::Settings::STEREO_SPLIT) {
3231                 Core::defaultTarget = inventory->getBackgroundTarget(view);
3232             } else {
3233                 Core::defaultTarget = inventory->getBackgroundTarget(texIndex);
3234             }
3235         } else {
3236             if (Core::settings.detail.stereo == Core::Settings::STEREO_ANAGLYPH || Core::settings.detail.stereo == Core::Settings::STEREO_VR) {
3237                 Core::defaultTarget = Core::eyeTex[texIndex];
3238             } else {
3239             #ifdef _OS_3DS
3240                 Core::defaultTarget = Core::eyeTex[0];
3241             #endif
3242             }
3243         }
3244 
3245         if (Core::defaultTarget) {
3246             Core::viewportDef = short4(0, 0, Core::defaultTarget->origWidth, Core::defaultTarget->origHeight);
3247         } else {
3248             Core::viewportDef = short4(0, 0, Core::width, Core::height);
3249         }
3250 
3251         #ifdef EARLY_CLEAR
3252             if (view == 0 && eye <= 0) {
3253                 Core::setTarget(NULL, NULL, RT_CLEAR_COLOR | RT_CLEAR_DEPTH | RT_STORE_COLOR | RT_STORE_DEPTH);
3254                 Core::validateRenderState();
3255             }
3256         #endif
3257     }
3258 
renderEyeLevel3259     void renderEye(int eye, bool showUI, bool invBG) {
3260         float          oldEye      = Core::eye;
3261         GAPI::Texture *oldTarget   = Core::defaultTarget;
3262 
3263         Core::eye = float(eye);
3264 
3265         #ifdef _OS_3DS
3266             Core::eye *= osGet3DSliderState() * 3.25f;
3267         #endif
3268 
3269         if (needRenderGame || invBG) {
3270             int viewsCount = players[1] ? 2 : 1;
3271             for (int view = 0; view < viewsCount; view++) {
3272                 player = players[view];
3273                 camera = player->camera;
3274 
3275                 Core::mLightProj = mLightProj[view];
3276 
3277                 Core::pass = Core::passCompose;
3278 
3279                 short4 oldViewport = Core::viewportDef;
3280 
3281                 setDefaultTarget(eye, view, invBG);
3282 
3283                 if (Core::settings.detail.stereo == Core::Settings::STEREO_SPLIT) {
3284                     camera->aspect = setViewport(invBG ? -1 : view, invBG ? 0 : eye);
3285                 } else {
3286                     camera->aspect = setViewport(view, invBG ? 0 : eye);
3287                 }
3288 
3289                 setup();
3290                 renderView(camera->getRoomIndex(), true, showUI);
3291 
3292                 Core::viewportDef = oldViewport;
3293             }
3294         }
3295 
3296         if (needRenderInventory && !invBG) {
3297             if (players[1] && Core::settings.detail.stereo == Core::Settings::STEREO_SPLIT) {
3298                 renderInventoryEye(eye, 0);
3299                 renderInventoryEye(eye, 1);
3300             } else {
3301                 renderInventoryEye(eye, -1);
3302             }
3303         }
3304 
3305         Core::defaultTarget = oldTarget;
3306         Core::eye           = oldEye;
3307 
3308         Core::setViewport(Core::viewportDef);
3309         Core::setScissor(Core::viewportDef);
3310 
3311         player = players[0];
3312         if (player) {
3313             camera = player->camera;
3314         }
3315     }
3316 
renderGameLevel3317     void renderGame(bool showUI, bool invBG) {
3318         short4         oldViewport = Core::viewportDef;
3319         GAPI::Texture *oldTarget   = Core::defaultTarget;
3320 
3321         bool upscale = !invBG && Core::settings.detail.scale != Core::Settings::SCALE_100;
3322 
3323         if (upscale) {
3324             int scale = (Core::settings.detail.scale + 1) * 25;
3325             int w = Core::width  * scale / 100;
3326             int h = Core::height * scale / 100;
3327             if (!scaleTex || scaleTex->width != w || scaleTex->height != h) {
3328                 delete scaleTex;
3329                 scaleTex = new Texture(w, h, 1, FMT_RGBA, OPT_TARGET);
3330             }
3331             Core::defaultTarget = scaleTex;
3332             Core::viewportDef   = short4(0, 0, w, h);
3333         }
3334 
3335         if (Core::eye == 0.0f && Core::settings.detail.isStereo()) {
3336             renderEye(-1, showUI, invBG);
3337             renderEye(+1, showUI, invBG);
3338         }  else {
3339             renderEye(int(Core::eye), showUI, invBG);
3340         }
3341 
3342     #ifndef _OS_3DS
3343         if (!invBG && Core::settings.detail.stereo == Core::Settings::STEREO_ANAGLYPH) {
3344             mat4 mProj, mView;
3345             mView.identity();
3346             mProj = GAPI::ortho(-1, +1, -1, +1, 0, 1);
3347             mProj.scale(vec3(1.0f / 32767.0f));
3348             Core::setViewProj(mView, mProj);
3349 
3350             Core::setDepthTest(false);
3351             Core::setDepthWrite(false);
3352 
3353             Core::setTarget(NULL, NULL, RT_STORE_COLOR);
3354             Core::validateRenderState();
3355             setShader(Core::passFilter, Shader::FILTER_ANAGLYPH, false, false);
3356             Core::eyeTex[0]->bind(sDiffuse);
3357             Core::eyeTex[1]->bind(sNormal);
3358             Core::setDepthTest(false);
3359             mesh->renderQuad();
3360 
3361             Core::setDepthTest(true);
3362             Core::setDepthWrite(true);
3363         }
3364     #endif
3365 
3366         Core::defaultTarget = oldTarget;
3367         Core::viewportDef   = oldViewport;
3368 
3369         if (upscale) {
3370             mat4 mProj, mView;
3371             mView.identity();
3372             mProj = GAPI::ortho(-1, +1, -1, +1, 0, 1);
3373             mProj.scale(vec3(1.0f / 32767.0f));
3374             Core::setViewProj(mView, mProj);
3375 
3376             Core::setTarget(NULL, NULL, RT_STORE_COLOR);
3377             setShader(Core::passFilter, Shader::FILTER_UPSCALE, false, false);
3378             Core::active.shader->setParam(uParam, vec4(float(scaleTex->width), float(scaleTex->height), 0.0f, 0.0f));
3379             scaleTex->bind(sDiffuse);
3380             Core::setDepthTest(false);
3381             mesh->renderQuad();
3382             Core::setDepthTest(true);
3383             Core::setDepthWrite(true);
3384         }
3385 
3386         // TODO render all UI with native resolution here
3387     }
3388 
renderUILevel3389     void renderUI() {
3390         if (inventory->titleTimer > 1.0f || level.isTitle()) return;
3391 
3392         #ifdef _GAPI_SW
3393             GAPI::setPalette(GAPI::swPaletteColor);
3394             GAPI::setShading(false);
3395         #endif
3396 
3397         Core::pushLights();
3398 
3399         UI::begin(camera->aspect);
3400 
3401         atlasObjects->bind(sDiffuse);
3402         UI::renderPickups();
3403 
3404         atlasGlyphs->bind(sDiffuse);
3405 
3406         Core::resetLights();
3407 
3408         if (!level.isCutsceneLevel()) {
3409         // render health & oxygen bars
3410             vec2 size = vec2(180, 10);
3411 
3412             float health = player->health / float(LARA_MAX_HEALTH);
3413             float oxygen = player->oxygen / float(LARA_MAX_OXYGEN);
3414 
3415             if ((params->time - int(params->time)) < 0.5f) { // blinking
3416                 if (health <= 0.2f) health = 0.0f;
3417                 if (oxygen <= 0.2f) oxygen = 0.0f;
3418             }
3419 
3420             vec2 pos;
3421             if (Core::settings.detail.stereo == Core::Settings::STEREO_VR)
3422                 pos = vec2((UI::width - size.x) * 0.5f, 96);
3423             else
3424                 pos = vec2(UI::width - 32 - size.x, 32);
3425 
3426             if (!player->dozy && (player->stand == Lara::STAND_ONWATER || player->stand == Character::STAND_UNDERWATER)) {
3427                 UI::renderBar(CTEX_OXYGEN, pos, size, oxygen);
3428                 pos.y += 16.0f;
3429             }
3430 
3431             if ((!inventory->active && ((player->wpnReady() && !player->emptyHands()) || player->damageTime > 0.0f || health <= 0.2f))) {
3432                 UI::renderBar(CTEX_HEALTH, pos, size, health);
3433                 pos.y += 32.0f;
3434 
3435                 if (!inventory->active && !player->emptyHands()) { // ammo
3436                     int index = inventory->contains(player->wpnCurrent);
3437                     if (index > -1)
3438                         inventory->renderItemCount(inventory->items[index], pos, size.x);
3439                 }
3440             }
3441 
3442             UI::renderHelp();
3443         }
3444 
3445         UI::renderSubs();
3446 
3447         UI::end();
3448 
3449         Core::popLights();
3450     }
3451 
renderInventoryEyeLevel3452     void renderInventoryEye(int eye, int view) {
3453         short4 oldViewport = Core::viewportDef;
3454 
3455         setDefaultTarget(eye, view, false);
3456 
3457         Core::setTarget(NULL, NULL, RT_CLEAR_DEPTH | RT_STORE_COLOR);
3458 
3459         float aspect = setViewport(view, eye);
3460 
3461         Core::pushLights();
3462         Core::resetLights();
3463 
3464         if (inventory->video) {
3465             inventory->render(1.0);
3466 
3467             if (UI::subsStr != STR_EMPTY) {
3468                 UI::begin(float(Core::width) / float(Core::height));
3469                 atlasGlyphs->bind(sDiffuse);
3470                 UI::renderSubs();
3471                 UI::end();
3472             }
3473         }
3474 
3475         if (!inventory->video) {
3476             inventory->renderBackground(max(0, view));
3477         }
3478 
3479         setupBinding();
3480         atlasObjects->bind(sDiffuse);
3481         inventory->render(aspect);
3482 
3483         UI::begin(aspect);
3484         atlasGlyphs->bind(sDiffuse);
3485         if (!inventory->video) {
3486             inventory->renderUI();
3487         } else {
3488             UI::renderSubs();
3489         }
3490         UI::end();
3491 
3492         Core::popLights();
3493 
3494         Core::viewportDef = oldViewport;
3495     }
3496 
renderLevel3497     void render() {
3498         if (isEnded && !inventory->video) {
3499             Core::setTarget(NULL, NULL, RT_CLEAR_COLOR | RT_STORE_COLOR);
3500             UI::begin(float(Core::width) / float(Core::height));
3501             atlasGlyphs->bind(sDiffuse);
3502             UI::textOut(vec2(0, 480 - 16), STR_LOADING, UI::aCenter, UI::width);
3503             UI::end();
3504             return;
3505         }
3506 
3507         renderGame(true, false);
3508     }
3509 
3510 };
3511 
3512 #endif
3513