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