From a927da8d73b402bc709fcdbadea1fc0981f30112 Mon Sep 17 00:00:00 2001 From: XProger Date: Sat, 25 Nov 2017 18:45:43 +0300 Subject: [PATCH] #23 colored lights; fix water plane level; #3 temporary fix shallow water; #15 fix floor data parser for TR3; #11 fix NPOT textures loader for bmp; font shading; --- src/cache.h | 8 +- src/character.h | 11 +++ src/controller.h | 20 ++--- src/core.h | 5 +- src/debug.h | 13 ++-- src/format.h | 167 +++++++++++++++++++++++++++++++--------- src/inventory.h | 10 +-- src/lara.h | 54 +++++++++---- src/level.h | 3 +- src/mesh.h | 36 ++++++--- src/shader.h | 3 +- src/shaders/shader.glsl | 26 ++++--- src/texture.h | 24 ++++-- src/ui.h | 44 +++++++++-- 14 files changed, 306 insertions(+), 118 deletions(-) diff --git a/src/cache.h b/src/cache.h index 0f79221..47c58bf 100644 --- a/src/cache.h +++ b/src/cache.h @@ -396,7 +396,7 @@ struct WaterCache { minZ = min(minZ, z); maxX = max(maxX, x); maxZ = max(maxZ, z); - posY = s.ceiling * 256; + posY = level->rooms[s.roomAbove].waterLevel; if (s.roomBelow != TR::NO_ROOM) caust = s.roomBelow; } @@ -417,8 +417,10 @@ struct WaterCache { bool hasWater = s.roomAbove != TR::NO_ROOM && !level->rooms[s.roomAbove].flags.water; if (hasWater) { TR::Room &rt = level->rooms[s.roomAbove]; - TR::Room::Sector &st = rt.sectors[x * rt.zSectors + z]; - hasWater = s.ceiling > st.ceiling; + int xt = int(r.info.x + x * 1024 - rt.info.x) / 1024; + int zt = int(r.info.z + z * 1024 - rt.info.z) / 1024; + TR::Room::Sector &st = rt.sectors[xt * rt.zSectors + zt]; + hasWater = s.ceiling > st.ceiling; // TODO fix for LEVEL10A, use slant } m[(x - minX) + w * (z - minZ)] = hasWater ? 0xF800 : 0; diff --git a/src/character.h b/src/character.h index 4704f5f..eb70d55 100644 --- a/src/character.h +++ b/src/character.h @@ -59,6 +59,17 @@ struct Character : Controller { updateZone(); } + virtual int getRoomIndex() const { + int index = Controller::getRoomIndex(); + + TR::Level::FloorInfo info; + getFloorInfo(index, pos, info); + + if (level->rooms[index].flags.water && info.roomAbove != TR::NO_ROOM && (info.floor - level->rooms[index].info.yTop) <= 512) + return info.roomAbove; + return index; + } + bool updateZone() { int dx, dz; TR::Room::Sector &s = level->getSector(getRoomIndex(), int(pos.x), int(pos.z), dx, dz); diff --git a/src/controller.h b/src/controller.h index b5c82c4..df4c21b 100644 --- a/src/controller.h +++ b/src/controller.h @@ -372,7 +372,7 @@ struct Controller { break; case TR::FloorData::CLIMB : - info.climb = (*fd++).data; // climb mask + info.climb = cmd.sub; // climb mask break; case 0x07 : @@ -386,7 +386,7 @@ struct Controller { case 0x0F : case 0x10 : case 0x11 : - case 0x12 : break; // TODO TR3 triangulation + case 0x12 : fd++; break; // TODO TR3 triangulation case 0x13 : break; // TODO TR3 monkeyswing @@ -934,17 +934,19 @@ struct Controller { float maxAtt = 0.0f; if (room.flags.sky) { // TODO trace rooms up for sun light, add direct light projection - sunLight.x = int32(center.x); - sunLight.y = int32(center.y) - 8192; - sunLight.z = int32(center.z); - targetLight = &sunLight; + sunLight.x = int32(center.x); + sunLight.y = int32(center.y) - 8192; + sunLight.z = int32(center.z); + sunLight.color = TR::Color32(255, 255, 255, 255); + sunLight.radius = 1000 * 1024; + targetLight = &sunLight; } else { for (int i = 0; i < room.lightsCount; i++) { TR::Room::Light &light = room.lights[i]; - if (light.intensity > 0x1FFF) continue; + if ((light.color.r | light.color.g | light.color.b) == 0) continue; vec3 dir = vec3(float(light.x), float(light.y), float(light.z)) - center; - float att = max(0.0f, 1.0f - dir.length2() / float(light.radius) / float(light.radius)) * (1.0f - intensityf(light.intensity)); + float att = max(0.0f, 1.0f - dir.length2() / float(light.radius) / float(light.radius)); if (att > maxAtt) { maxAtt = att; @@ -962,7 +964,7 @@ struct Controller { } vec3 tpos = vec3(float(targetLight->x), float(targetLight->y), float(targetLight->z)); - vec4 tcolor = vec4(vec3(1.0f - intensityf(targetLight->intensity)), float(targetLight->radius)); + vec4 tcolor = vec4(vec3(targetLight->color.r, targetLight->color.g, targetLight->color.b) * (1.0f / 255.0f), float(targetLight->radius)); if (lerp) { float t = Core::deltaTime * 2.0f; diff --git a/src/core.h b/src/core.h index daaffb7..6c57d29 100644 --- a/src/core.h +++ b/src/core.h @@ -335,7 +335,7 @@ namespace Core { #define MAX_RENDER_BUFFERS 32 #define MAX_CONTACTS 15 #define MAX_ANIM_TEX_RANGES 32 -#define MAX_ANIM_TEX_OFFSETS 130 +#define MAX_ANIM_TEX_OFFSETS 170 struct Shader; struct Texture; @@ -367,7 +367,8 @@ struct Vertex { short4 normal; // xyz - vertex normal, w - unused short4 texCoord; // xy - texture coordinates, zw - trapezoid warping ubyte4 param; // xy - anim tex range and frame index, zw - unused - ubyte4 color; // xyz - color, w - intensity + ubyte4 color; // for non-textured geometry + ubyte4 light; // xyz - color, w - use premultiplied alpha }; #ifdef PROFILE diff --git a/src/debug.h b/src/debug.h index aa3022b..d1236c2 100644 --- a/src/debug.h +++ b/src/debug.h @@ -474,9 +474,8 @@ namespace Debug { for (int i = 0; i < level.roomsCount; i++) for (int j = 0; j < level.rooms[i].lightsCount; j++) { TR::Room::Light &l = level.rooms[i].lights[j]; - float a = 1.0f - intensityf(l.intensity); vec3 p = vec3(float(l.x), float(l.y), float(l.z)); - vec4 color = vec4(a, a, a, 1); + vec4 color = vec4(l.color.r, l.color.g, l.color.b, 255) * (1.0f / 255.0f); // if (i == room) color.x = color.z = 0; Debug::Draw::point(p, color); @@ -485,7 +484,7 @@ namespace Debug { Debug::Draw::sphere(p, float(l.radius), color); } - vec4 color = vec4(lara->mainLightColor.x, 0.0f, 0.0f, 1.0f); + vec4 color = vec4(lara->mainLightColor.xyz, 1.0f); Debug::Draw::point(lara->mainLightPos, color); Debug::Draw::sphere(lara->mainLightPos, lara->mainLightColor.w, color); } @@ -658,9 +657,11 @@ namespace Debug { const char *getEntityName(const TR::Level &level, const TR::Entity &entity) { if (entity.type < TR::Entity::TR1_TYPE_MAX) return TR_TYPE_NAMES[entity.type - TR1_TYPES_START]; - else - if (entity.type < TR::Entity::TR2_TYPE_MAX) - return TR_TYPE_NAMES[entity.type - TR2_TYPES_START + TR::Entity::TR1_TYPE_MAX + 1]; + if (entity.type < TR::Entity::TR2_TYPE_MAX) + return TR_TYPE_NAMES[entity.type - TR2_TYPES_START + (TR::Entity::TR1_TYPE_MAX - TR1_TYPES_START) + 1]; + if (entity.type < TR::Entity::TR3_TYPE_MAX) + return TR_TYPE_NAMES[entity.type - TR3_TYPES_START + (TR::Entity::TR1_TYPE_MAX - TR1_TYPES_START) + (TR::Entity::TR2_TYPE_MAX - TR2_TYPES_START) + 2]; + return "UNKNOWN"; } diff --git a/src/format.h b/src/format.h index 88c5fbc..07ed096 100644 --- a/src/format.h +++ b/src/format.h @@ -485,11 +485,13 @@ E( __LARA_FLARE ) \ E( LARA_UPV ) \ E( VEHICLE_UPV ) \ + E( UNUSED_TR3_13 ) \ E( VEHICLE_KAYAK ) \ E( __VEHICLE_BOAT ) \ E( VEHICLE_QUADBIKE ) \ E( VEHICLE_MINECART ) \ E( BIG_GUN ) \ + E( HYDRO_PROPELLER ) \ E( ENEMY_TRIBESMAN_AXE ) \ E( ENEMY_TRIBESMAN_DART ) \ E( __ENEMY_DOG ) \ @@ -500,8 +502,60 @@ E( __ENEMY_CROW ) \ E( __ENEMY_TIGER ) \ E( ENEMY_VULTURE ) \ -\ - E( __TRAP_FLOOR = TR3_TYPES_START + 83 ) \ + E( ASSAULT_TARGET ) \ + E( ENEMY_CRAWLER_MUTANT_1 ) \ + E( ENEMY_ALLIGATOR ) \ + E( UNUSED_TR3_33 ) \ + E( ENEMY_COMPSOGNATHUS ) \ + E( ENEMY_LIZARD_MAN ) \ + E( ENEMY_PUNA ) \ + E( ENEMY_MERCENARY ) \ + E( ENEMY_RAPTOR_HUNG ) \ + E( ENEMY_RX_TECH_GUY_1 ) \ + E( ENEMY_RX_TECH_GUY_2 ) \ + E( ENEMY_ANTARC_DOG ) \ + E( ENEMY_CRAWLER_MUTANT_2 ) \ + E( UNUSED_TR3_43 ) \ + E( ENEMY_TINNOS_WASP ) \ + E( ENEMY_TINNOS_MONSTER ) \ + E( ENEMY_BRUTE_MUTANT ) \ + E( RESPAWN_TINNOS_WASP ) \ + E( RESPAWN_RAPTOR ) \ + E( ENEMY_WILLARD_SPIDER ) \ + E( ENEMY_RX_TECH_FLAME_GUY ) \ + E( ENEMY_LONDON_MERCENARY ) \ + E( UNUSED_TR3_52 ) \ + E( ENEMY_PUNK ) \ + E( UNUSED_TR3_54 ) \ + E( UNUSED_TR3_55 ) \ + E( ENEMY_LONDON_GUARD ) \ + E( ENEMY_SOPHIA ) \ + E( CLEANER_ROBOT ) \ + E( UNUSED_TR3_59 ) \ + E( ENEMY_MILITARY_1 ) \ + E( ENEMY_MILITARY_2 ) \ + E( PRISONER ) \ + E( ENEMY_MILITARY_3 ) \ + E( GUN_TURRET ) \ + E( ENEMY_DAM_GUARD ) \ + E( TRIPWIRE ) \ + E( ELECTRIC_WIRE ) \ + E( KILLER_TRIPWIRE ) \ + E( ENEMY_COBRA ) \ + E( ENEMY_SHIVA ) \ + E( ENEMY_MONKEY ) \ + E( UNUSED_TR3_72 ) \ + E( ENEMY_TONY ) \ + E( AI_GUARD ) \ + E( AI_AMBUSH ) \ + E( AI_PATROL_1 ) \ + E( AI_MODIFY ) \ + E( AI_FOLLOW ) \ + E( AI_PATROL_2 ) \ + E( AI_PATH ) \ + E( AI_CHECK ) \ + E( UNUSED_TR3_82 ) \ + E( __TRAP_FLOOR ) \ E( UNUSED_TR3_84 ) \ E( UNUSED_TR3_85 ) \ E( TRAP_SWING_THING ) \ @@ -564,7 +618,7 @@ E( __BRIDGE_2 ) \ E( __BRIDGE_3 ) \ E( __INV_PASSPORT ) \ - E( __INV_COMPASS ) \ + E( __INV_STOPWATCH ) \ E( __INV_HOME ) \ E( __CUT_4 ) \ E( __CUT_5 ) \ @@ -598,7 +652,7 @@ E( __MEDIKIT_BIG ) \ E( __FLARES ) \ E( __FLARE ) \ - E( __CRYSTAL ) \ + E( CRYSTAL_PICKUP ) \ E( __INV_DETAIL ) \ E( __INV_SOUND ) \ E( __INV_CONTROLS ) \ @@ -786,7 +840,11 @@ E( __EARTHQUAKE ) \ E( GUN_SHELL_1 ) \ E( GUN_SHELL_2 ) \ + E( UNUSED_TR3_368 ) \ + E( UNUSED_TR3_369 ) \ E( TINNOS_LIGHT_SHAFT ) \ + E( UNUSED_TR3_371 ) \ + E( UNUSED_TR3_372 ) \ E( ELECTRIC_SWITCH ) \ E( TR3_TYPE_MAX ) @@ -854,7 +912,9 @@ namespace TR { FLIP_MAP , DRAW_RIGHTGUN , DRAW_LEFTGUN , - FLICKER , + SHOT_RIGHTGUN , + SHOT_LEFTGUN , + FLICKER = 16, UNKNOWN , MESH_SWAP_1 , MESH_SWAP_2 , @@ -1112,6 +1172,7 @@ namespace TR { struct { uint16 r:5, g:5, b:5, a:1; }; uint16 value; + Color32 getBGR() const { return Color32((b << 3) | (b >> 2), (g << 3) | (g >> 2), (r << 3) | (r >> 2), 255); } operator Color24() const { return Color24((r << 3) | (r >> 2), (g << 3) | (g >> 2), (b << 3) | (b >> 2)); } operator Color32() const { return Color32((r << 3) | (r >> 2), (g << 3) | (g >> 2), (b << 3) | (b >> 2), -a); } }; @@ -1178,9 +1239,9 @@ namespace TR { struct Vertex { TR::Vertex vertex; - int16 lighting; // 0 (bright) .. 0x1FFF (dark) + int16 unused_lighting; // 0 (bright) .. 0x1FFF (dark) uint16 attributes; - Color16 color; + Color32 color; } *vertices; Rectangle *rectangles; @@ -1208,6 +1269,7 @@ namespace TR { uint8 reverbType; uint8 filter; uint8 align; + uint32 waterLevel; struct Portal { uint16 roomIndex; @@ -1239,19 +1301,15 @@ namespace TR { struct Light { int32 x, y, z; - uint16 intensity; - uint16 intensity2; uint32 radius; - uint32 radius2; Color32 color; } *lights; struct Mesh { int32 x, y, z; angle rotation; - Color16 color; - int16 intensity; uint16 meshID; + Color32 color; uint32 meshIndex; // index into static meshes array } *meshes; @@ -1263,7 +1321,7 @@ namespace TR { union FloorData { uint16 data; struct Command { - uint16 func:8, sub:7, end:1; + uint16 func:5, tri:3, sub:7, end:1; } cmd; struct Slant { int8 x:8, z:8; @@ -1529,7 +1587,7 @@ namespace TR { REMAP_3( BRIDGE_2 ); REMAP_3( BRIDGE_3 ); REMAP_3( INV_PASSPORT ); - REMAP_3( INV_COMPASS ); + REMAP_3( INV_STOPWATCH ); REMAP_3( INV_HOME ); REMAP_3( CUT_4 ); REMAP_3( CUT_5 ); @@ -1555,7 +1613,6 @@ namespace TR { REMAP_3( MEDIKIT_BIG ); REMAP_3( FLARES ); REMAP_3( FLARE ); - REMAP_3( CRYSTAL ); REMAP_3( INV_DETAIL ); REMAP_3( INV_SOUND ); REMAP_3( INV_CONTROLS ); @@ -1660,7 +1717,16 @@ namespace TR { (type >= KEY_ITEM_1 && type <= KEY_ITEM_4) || (type == MEDIKIT_SMALL || type == MEDIKIT_BIG) || (type == SCION_PICKUP_QUALOPEC || type == SCION_PICKUP_DROP || type == SCION_PICKUP_HOLDER || type == LEADBAR) || - (type >= SECRET_1 && type <= SECRET_3); // TODO: recheck all items + (type >= SECRET_1 && type <= SECRET_3) || + (type == M16 || type == AMMO_M16) || + (type == MP5 || type == AMMO_MP5) || + (type == AUTOPISTOLS || type == AMMO_AUTOPISTOLS) || + (type == DESERT_EAGLE || type == AMMO_DESERT_EAGLE) || + (type == GRENADE || type == AMMO_GRENADE) || + (type == ROCKET || type == AMMO_ROCKET) || + (type == HARPOON || type == AMMO_HARPOON) || + (type == FLARES || type == FLARE) || + (type >= STONE_ITEM_1 || type <= STONE_ITEM_4); } bool isPickup() const { @@ -3207,6 +3273,8 @@ namespace TR { for (int i = 0; i < d.vCount; i++) { Room::Data::Vertex &v = d.vertices[i]; + uint16 lighting; + if (version == VER_TR2_PSX) { union Compressed { struct { uint32 lighting:8, attributes:8, z:5, y:5, x:5, w:1; }; @@ -3216,30 +3284,36 @@ namespace TR { v.vertex.x = (comp.x << 10); v.vertex.y = (comp.y << 8) + r.info.yTop; v.vertex.z = (comp.z << 10); - v.lighting = comp.lighting; + lighting = comp.lighting; v.attributes = comp.attributes; - v.color.value = 0xFFFF; ASSERT(comp.w == 0); } else { stream.read(v.vertex.x); stream.read(v.vertex.y); stream.read(v.vertex.z); - stream.read(v.lighting); + stream.read(lighting); if (version == VER_TR2_PC || version == VER_TR3_PC) stream.read(v.attributes); if (version == VER_TR2_PC) - stream.read(v.lighting); // real lighting value + stream.read(lighting); // real lighting value - if (version == VER_TR3_PC) - stream.read(v.color.value); - else - v.color.value = 0xFFFF; + if (version == VER_TR3_PC) { + Color16 color; + stream.read(color.value); + v.color = color.getBGR(); + } } if (version & VER_PSX) - v.lighting = 0x1FFF - (v.lighting << 5); // convert vertex luminance from PSX to PC format + lighting = 0x1FFF - (lighting << 5); // convert vertex luminance from PSX to PC format + + if ((version & VER_VERSION) < VER_TR3) { // lighting to color conversion + int value = clamp((lighting > 0x1FFF) ? 255 : (255 - (lighting >> 5)), 0, 255); + v.color.r = v.color.g = v.color.b = value; + v.color.a = 255; + } } if (version == VER_TR2_PSX) @@ -3337,22 +3411,29 @@ namespace TR { stream.read(light.y); stream.read(light.z); + uint16 intensity; + if (version == VER_TR3_PC) stream.read(light.color); - if (version == VER_TR1_PSX) { - uint32 intensity; - light.intensity = stream.read(intensity); - } else - stream.read(light.intensity); + stream.read(intensity); + + if (version == VER_TR1_PSX) + stream.seek(2); if (version & (VER_TR2 | VER_TR3)) - stream.read(light.intensity2); + stream.seek(2); // intensity2 stream.read(light.radius); if (version & VER_TR2) - stream.read(light.radius2); + stream.seek(4); // radius2 + + if ((version & VER_VERSION) < VER_TR3) { + int value = 2555 - clamp((intensity > 0x1FFF) ? 0 : (intensity >> 5), 0, 255); + light.color.r = light.color.g = light.color.b = value; + light.color.a = 0; + } light.radius *= 2; } @@ -3365,11 +3446,23 @@ namespace TR { stream.read(m.y); stream.read(m.z); stream.read(m.rotation); - if (version & (VER_TR2 | VER_TR3)) - stream.read(m.color.value); - if (!(version & VER_TR3)) - m.color.value = 0xFFFF; - stream.read(m.intensity); + if (version & VER_TR3) { + Color16 color; + stream.read(color.value); + m.color = color.getBGR(); + } + + if (version & VER_TR2) + stream.seek(2); + + uint16 intensity; + stream.read(intensity); + if ((version & VER_VERSION) < VER_TR3) { + int value = clamp((intensity > 0x1FFF) ? 255 : (255 - (intensity >> 5)), 0, 255); + m.color.r = m.color.g = m.color.b = value; + m.color.a = 0; + } + stream.read(m.meshID); if (version == VER_TR1_PSX) stream.seek(2); // just an align for PSX version diff --git a/src/inventory.h b/src/inventory.h index 7a6cabf..358767f 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -766,7 +766,7 @@ struct Inventory { sprintf(buf, "%d %c", item->count, spec); for (int i = 0; buf[i] != ' '; i++) buf[i] -= 47; - UI::textOut(pos, buf, UI::aRight, width); + UI::textOut(pos, buf, UI::aRight, width, UI::SHADE_NONE); } } @@ -1071,13 +1071,13 @@ struct Inventory { UI::textOut(vec2( 0, 32), pageTitle[page], UI::aCenter, UI::width); if (canFlipPage(-1)) { - UI::textOut(vec2(16, 32), "[", UI::aLeft, UI::width); - UI::textOut(vec2( 0, 32), "[", UI::aRight, UI::width - 20); + UI::textOut(vec2(16, 32), "[", UI::aLeft, UI::width, UI::SHADE_NONE); + UI::textOut(vec2( 0, 32), "[", UI::aRight, UI::width - 20, UI::SHADE_NONE); } if (canFlipPage(1)) { - UI::textOut(vec2(16, 480 - 16), "]", UI::aLeft, UI::width); - UI::textOut(vec2(0, 480 - 16), "]", UI::aRight, UI::width - 20); + UI::textOut(vec2(16, 480 - 16), "]", UI::aLeft, UI::width, UI::SHADE_NONE); + UI::textOut(vec2(0, 480 - 16), "]", UI::aRight, UI::width - 20, UI::SHADE_NONE); } if (index == targetIndex) diff --git a/src/lara.h b/src/lara.h index 557e966..6c0677e 100644 --- a/src/lara.h +++ b/src/lara.h @@ -949,7 +949,7 @@ struct Lara : Character { } void updateWeapon() { - if (level->cutEntity > -1) return; + if (level->isCutsceneLevel()) return; if (wpnNext != Weapon::EMPTY && emptyHands()) { wpnSet(wpnNext); @@ -981,13 +981,8 @@ struct Lara : Character { if (isRifle) break; } - for (int i = 0; i < 2; i++) { + for (int i = 0; i < 2; i++) arms[i].animation.update(); - arms[i].shotTimer += Core::deltaTime; - - float intensity = clamp((0.1f - arms[i].shotTimer) * 20.0f, EPS, 1.0f); - Core::lightColor[1 + i] = FLASH_LIGHT_COLOR * vec4(intensity, intensity, intensity, 1.0f / sqrtf(intensity)); - } if (isRifle) animateShotgun(); @@ -1382,6 +1377,8 @@ struct Lara : Character { case TR::Effect::LARA_HANDSFREE : break;//meshSwap(1, level->extra.weapons[wpnCurrent], BODY_LEG_L1 | BODY_LEG_R1); break; case TR::Effect::DRAW_RIGHTGUN : drawGun(true); break; case TR::Effect::DRAW_LEFTGUN : drawGun(false); break; + case TR::Effect::SHOT_RIGHTGUN : arms[0].shotTimer = 0; break; + case TR::Effect::SHOT_LEFTGUN : arms[1].shotTimer = 0; break; case TR::Effect::MESH_SWAP_1 : case TR::Effect::MESH_SWAP_2 : case TR::Effect::MESH_SWAP_3 : Character::cmdEffect(fx); @@ -1616,6 +1613,7 @@ struct Lara : Character { int goUnderwater() { angle.x = -PI * 0.25f; game->waterDrop(pos, 256.0f, 0.2f); + stand = STAND_UNDERWATER; return animation.setAnim(ANIM_TO_UNDERWATER); } @@ -2039,17 +2037,24 @@ struct Lara : Character { return stand; } - if (getRoom().flags.water) { - wpnHide(); - if (stand != STAND_UNDERWATER && stand != STAND_ONWATER && (state != STATE_FALL && state != STATE_REACH && state != STATE_SWAN_DIVE && state != STATE_FAST_DIVE)) - animation.setAnim(ANIM_FALL_FORTH); - stopScreaming(); - return STAND_UNDERWATER; - } - TR::Level::FloorInfo info; getFloorInfo(getRoomIndex(), pos, info); + if (getRoom().flags.water) { + if (stand == STAND_UNDERWATER || stand == STAND_ONWATER) + return stand; + wpnHide(); + if (stand == STAND_AIR) { + //if (stand != STAND_UNDERWATER && stand != STAND_ONWATER && (state != STATE_FALL && state != STATE_REACH && state != STATE_SWAN_DIVE && state != STATE_FAST_DIVE)) + // animation.setAnim(ANIM_FALL_FORTH); + stopScreaming(); + return STAND_UNDERWATER; + } else { + pos.y = info.roomCeiling; + return STAND_ONWATER; + } + } + if ((stand == STAND_SLIDE || stand == STAND_GROUND) && (state != STATE_FORWARD_JUMP && state != STATE_BACK_JUMP)) { if (pos.y + 8 >= info.floor && (abs(info.slantX) > 2 || abs(info.slantZ) > 2)) { pos.y = info.floor; @@ -2081,6 +2086,8 @@ struct Lara : Character { } } } + if (stand == STAND_UNDERWATER || stand == STAND_ONWATER) + animation.setAnim(ANIM_STAND); return STAND_GROUND; } @@ -2516,6 +2523,9 @@ struct Lara : Character { //reset(44, vec3(62976, 1536, 23040), 0); reset(44, vec3(62976, 1536, 23040), 0); break; + case TR::LVL_TR3_TEMPLE : + reset(204, vec3(40562, 3584, 58694), 0); + break; default : game->playSound(TR::SND_NO, pos, Sound::PAN); } } @@ -2597,9 +2607,23 @@ struct Lara : Character { usedKey = TR::Entity::LARA; } + void updateFlash() { + for (int i = 0; i < 2; i++) { + if (arms[i].shotTimer < MUZZLE_FLASH_TIME) { + arms[i].shotTimer += Core::deltaTime; + float intensity = clamp((0.1f - arms[i].shotTimer) * 20.0f, EPS, 1.0f); + Core::lightColor[1 + i] = FLASH_LIGHT_COLOR * vec4(intensity, intensity, intensity, 1.0f / sqrtf(intensity)); + Core::lightPos[1 + i] = animation.getJoints(getMatrix(), i == 0 ? 10 : 13, false).pos; + } else { + Core::lightColor[1 + i] = vec4(0, 0, 0, 1); + } + } + } + virtual void updateAnimation(bool commands) { Controller::updateAnimation(commands); updateWeapon(); + updateFlash(); if (stand == STAND_UNDERWATER) specular = 0.0f; else diff --git a/src/level.h b/src/level.h index e1290dd..1ea9a57 100644 --- a/src/level.h +++ b/src/level.h @@ -1668,9 +1668,10 @@ struct Level : IGame { // Debug::Draw::box(bbox.min, bbox.max, vec4(1, 0, 1, 1)); // Core::setBlending(bmAlpha); + // Core::validateRenderState(); // Debug::Level::rooms(level, lara->pos, lara->getEntity().room); // Debug::Level::lights(level, lara->getRoomIndex(), lara); - // Debug::Level::sectors(level, lara->getRoomIndex(), (int)lara->pos.y); + // Debug::Level::sectors(this, lara->getRoomIndex(), (int)lara->pos.y); // Core::setDepthTest(false); // Debug::Level::portals(level); // Core::setDepthTest(true); diff --git a/src/mesh.h b/src/mesh.h index ce063aa..29163d7 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -22,6 +22,7 @@ struct MeshRange { glEnableVertexAttribArray(aTexCoord); glEnableVertexAttribArray(aParam); glEnableVertexAttribArray(aColor); + glEnableVertexAttribArray(aLight); Vertex *v = (Vertex*)NULL + vStart; glVertexAttribPointer(aCoord, 4, GL_SHORT, false, sizeof(Vertex), &v->coord); @@ -29,6 +30,7 @@ struct MeshRange { glVertexAttribPointer(aTexCoord, 4, GL_SHORT, true, sizeof(Vertex), &v->texCoord); glVertexAttribPointer(aParam, 4, GL_UNSIGNED_BYTE, false, sizeof(Vertex), &v->param); glVertexAttribPointer(aColor, 4, GL_UNSIGNED_BYTE, true, sizeof(Vertex), &v->color); + glVertexAttribPointer(aLight, 4, GL_UNSIGNED_BYTE, true, sizeof(Vertex), &v->light); } void bind(GLuint *VAO) const { @@ -316,7 +318,7 @@ struct MeshBuilder { TR::Room::Data::Vertex &v = d.vertices[f.vertex]; TR::SpriteTexture &sprite = level.spriteTextures[f.texture]; - addSprite(indices, vertices, iCount, vCount, vStartRoom, v.vertex.x, v.vertex.y, v.vertex.z, sprite, intensity(v.lighting)); + addSprite(indices, vertices, iCount, vCount, vStartRoom, v.vertex.x, v.vertex.y, v.vertex.z, sprite, v.color, v.color); } range.sprites.iCount = iCount - range.sprites.iStart; } @@ -361,7 +363,7 @@ struct MeshBuilder { range.iStart = iCount; for (int j = 0; j < level.spriteSequences[i].sCount; j++) { TR::SpriteTexture &sprite = level.spriteTextures[level.spriteSequences[i].sStart + j]; - addSprite(indices, vertices, iCount, vCount, vStartSprite, 0, 0, 0, sprite, 255); + addSprite(indices, vertices, iCount, vCount, vStartSprite, 0, 0, 0, sprite, TR::Color32(255, 255, 255, 255), TR::Color32(255, 255, 255, 255)); } range.iCount = iCount - range.iStart; } @@ -380,6 +382,7 @@ struct MeshBuilder { v0.texCoord = { whiteTile.texCoord[0].x, whiteTile.texCoord[0].y, 32767, 32767 }; v0.param = { 0, 0, 0, 0 }; v0.color = { 0, 0, 0, 0 }; + v0.light = { 255, 255, 255, 255 }; if (i == 8) { v0.coord = { 0, 0, 0, 0 }; @@ -399,6 +402,7 @@ struct MeshBuilder { v1 = v0; v1.coord = { c1, 0, s1, 0 }; v1.color = { 255, 255, 255, 0 }; + v1.light = { 255, 255, 255, 255 }; int idx = iCount + i * 3 * 3; int j = ((i + 1) % 8) * 2; @@ -432,6 +436,7 @@ struct MeshBuilder { Vertex &v = vertices[vCount + i]; v.normal = { 0, 0, 0, 0 }; v.color = { 255, 255, 255, 255 }; + v.light = { 255, 255, 255, 255 }; v.texCoord = { whiteTile.texCoord[0].x, whiteTile.texCoord[0].y, 32767, 32767 }; v.param = { 0, 0, 0, 0 }; } @@ -452,6 +457,7 @@ struct MeshBuilder { v.coord = { short(pos.x), short(pos.y), 0, 0 }; v.normal = { 0, 0, 0, 0 }; v.color = { 255, 255, 255, 255 }; + v.light = { 255, 255, 255, 255 }; v.texCoord = { whiteTile.texCoord[0].x, whiteTile.texCoord[0].y, 32767, 32767 }; v.param = { 0, 0, 0, 0 }; @@ -575,6 +581,7 @@ struct MeshBuilder { } void roomRemoveWaterSurfaces(TR::Room &room, int &iCount, int &vCount) { + room.waterLevel = -1; // remove animated water polygons from room geometry for (int i = 0; i < room.data.rCount; i++) { TR::Rectangle &f = room.data.rectangles[i]; @@ -601,6 +608,7 @@ struct MeshBuilder { if (isWaterSurface(yt, s.roomAbove, room.flags.water) || isWaterSurface(yb, s.roomBelow, room.flags.water)) { f.vertices[0] = 0xFFFF; // mark as unused + room.waterLevel = a.y; iCount -= 6; vCount -= 4; } @@ -630,6 +638,7 @@ struct MeshBuilder { if (isWaterSurface(yt, s.roomAbove, room.flags.water) || isWaterSurface(yb, s.roomBelow, room.flags.water)) { f.vertices[0] = 0xFFFF; // mark as unused + room.waterLevel = a.y; iCount -= 3; vCount -= 3; } @@ -664,8 +673,9 @@ struct MeshBuilder { TR::Room::Data::Vertex &v = d.vertices[f.vertices[k]]; vertices[vCount].coord = { v.vertex.x, v.vertex.y, v.vertex.z, 0 }; vertices[vCount].normal = { n.x, n.y, n.z, 0 }; + vertices[vCount].color = { 255, 255, 255, 255 }; TR::Color32 c = v.color; - vertices[vCount].color = { c.b, c.g, c.r, intensity(v.lighting) }; + vertices[vCount].light = { c.r, c.g, c.b, 255 }; vCount++; } } @@ -691,8 +701,9 @@ struct MeshBuilder { auto &v = d.vertices[f.vertices[k]]; vertices[vCount].coord = { v.vertex.x, v.vertex.y, v.vertex.z, 0 }; vertices[vCount].normal = { n.x, n.y, n.z, 0 }; + vertices[vCount].color = { 255, 255, 255, 255 }; TR::Color32 c = v.color; - vertices[vCount].color = { c.b, c.g, c.r, intensity(v.lighting) }; + vertices[vCount].light = { c.r, c.g, c.b, 255 }; vCount++; } } @@ -700,7 +711,7 @@ struct MeshBuilder { return isOpaque; } - bool buildMesh(bool opaque, const TR::Mesh &mesh, const TR::Level &level, Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart, int16 joint, int x, int y, int z, int dir, const TR::Color32 &color) { + bool buildMesh(bool opaque, const TR::Mesh &mesh, const TR::Level &level, Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart, int16 joint, int x, int y, int z, int dir, const TR::Color32 &light) { TR::Color24 COLOR_WHITE = { 255, 255, 255 }; bool isOpaque = true; @@ -715,9 +726,6 @@ struct MeshBuilder { continue; TR::Color32 c = f.flags.color ? level.getColor(f.flags.texture) : COLOR_WHITE; - c.r = int(( (c.r / 255.0f) * (color.b / 255.0f) ) * 255.0f); - c.g = int(( (c.g / 255.0f) * (color.g / 255.0f) ) * 255.0f); - c.b = int(( (c.b / 255.0f) * (color.r / 255.0f) ) * 255.0f); addQuad(indices, iCount, vCount, vStart, vertices, &t, mesh.vertices[f.vertices[0]].coord, @@ -730,7 +738,8 @@ struct MeshBuilder { vertices[vCount].coord = transform(v.coord, joint, x, y, z, dir); vertices[vCount].normal = rotate(v.normal, dir); - vertices[vCount].color = { c.r, c.g, c.b, intensity(v.coord.w) }; + vertices[vCount].color = { c.r, c.g, c.b, 255 }; + vertices[vCount].light = { light.r, light.g, light.b, 255 }; vCount++; } @@ -755,7 +764,8 @@ struct MeshBuilder { vertices[vCount].coord = transform(v.coord, joint, x, y, z, dir); vertices[vCount].normal = rotate(v.normal, dir); - vertices[vCount].color = { c.r, c.g, c.b, intensity(v.coord.w) }; + vertices[vCount].color = { c.r, c.g, c.b, 255 }; + vertices[vCount].light = { light.r, light.g, light.b, 255 }; vCount++; } @@ -905,7 +915,7 @@ struct MeshBuilder { } } - void addSprite(Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart, int16 x, int16 y, int16 z, const TR::SpriteTexture &sprite, uint8 intensity, bool expand = false) { + void addSprite(Index *indices, Vertex *vertices, int &iCount, int &vCount, int vStart, int16 x, int16 y, int16 z, const TR::SpriteTexture &sprite, const TR::Color32 &tColor, const TR::Color32 &bColor, bool expand = false) { addQuad(indices, iCount, vCount, vStart, NULL, NULL); Vertex *quad = &vertices[vCount]; @@ -928,7 +938,9 @@ struct MeshBuilder { quad[3].coord = { x0, y1, z, 0 }; quad[0].normal = quad[1].normal = quad[2].normal = quad[3].normal = { 0, 0, 0, 0 }; - quad[0].color = quad[1].color = quad[2].color = quad[3].color = { 255, 255, 255, intensity }; + quad[0].color = quad[1].color = { tColor.r, tColor.g, tColor.b, tColor.a }; + quad[2].color = quad[3].color = { bColor.r, bColor.g, bColor.b, bColor.a }; + quad[0].light = quad[1].light = quad[2].light = quad[3].light = { 255, 255, 255, 255 }; quad[0].param = quad[1].param = quad[2].param = quad[3].param = { 0, 0, 0, 0 }; quad[0].texCoord = { sprite.texCoord[0].x, sprite.texCoord[0].y, sprite.l, sprite.t }; diff --git a/src/shader.h b/src/shader.h index c49d05a..28f324f 100644 --- a/src/shader.h +++ b/src/shader.h @@ -8,7 +8,8 @@ E( aNormal ) \ E( aTexCoord ) \ E( aParam ) \ - E( aColor ) + E( aColor ) \ + E( aLight ) #define SHADER_SAMPLERS(E) \ E( sDiffuse ) \ diff --git a/src/shaders/shader.glsl b/src/shaders/shader.glsl index a1a296d..438cd9d 100644 --- a/src/shaders/shader.glsl +++ b/src/shaders/shader.glsl @@ -36,6 +36,7 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha #ifdef OPT_SHADOW varying vec3 vAmbient; + varying vec4 vLightMap; #endif #endif @@ -78,6 +79,7 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha #ifndef PASS_SHADOW attribute vec4 aColor; + attribute vec4 aLight; #endif vec3 mulQuat(vec4 q, vec3 v) { @@ -140,7 +142,7 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha void _diffuse() { #ifndef PASS_SHADOW - vDiffuse = vec4(aColor.xyz * (uMaterial.x * 2.0), uMaterial.w); + vDiffuse = vec4(aColor.xyz * (uMaterial.x * 1.8), uMaterial.w); #ifdef UNDERWATER vDiffuse.xyz *= UNDERWATER_COLOR; @@ -171,7 +173,7 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha lum.x = dot(vNormal.xyz, normalize(lv0)); att.x = dot(lv0, lv0); #else - lum.x = aColor.w; + lum.x = 1.0; att.x = 0.0; #ifdef TYPE_SPRITE @@ -197,27 +199,28 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha ambient = vec3(uMaterial.y); #endif #else - ambient = vec3(min(uMaterial.y, light.x)); + ambient = min(uMaterial.yyy, aLight.xyz); #endif #ifdef OPT_SHADOW - vAmbient = ambient; - vLight = light; + vAmbient = ambient; + vLight = light; + vLightMap = aLight * light.x; #else - vLight.w = 0.0; vLight.xyz = uLightColor[1].xyz * light.y + uLightColor[2].xyz * light.z + uLightColor[3].xyz * light.w; + vLight.w = 0.0; #ifdef TYPE_ENTITY vLight.xyz += ambient + uLightColor[0].xyz * light.x; #else - vLight.xyz += light.x; + vLight.xyz += aLight.xyz * light.x; #endif #endif #endif #ifdef PASS_AMBIENT - vLight = aColor.wwww; + vLight = vec4(aLight.xyz, 1.0); #endif #endif } @@ -426,7 +429,7 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha #if !defined(TYPE_FLASH) && !defined(TYPE_MIRROR) #ifdef PASS_AMBIENT - color.xyz *= vLight.x; + color.xyz *= vLight.xyz; #endif #ifdef PASS_COMPOSE @@ -450,12 +453,13 @@ uniform vec4 uMaterial; // x - diffuse, y - ambient, z - specular, w - alpha #endif #ifdef TYPE_ROOM - light += mix(vAmbient.x, vLight.x, rShadow); + light += mix(vAmbient.xyz, vLightMap.xyz, rShadow); #endif #ifdef TYPE_SPRITE - light += vLight.x; + light += vLightMap.xyz; #endif + #else vec3 light = vLight.xyz; #endif diff --git a/src/texture.h b/src/texture.h index 6b1bfdd..5792213 100644 --- a/src/texture.h +++ b/src/texture.h @@ -249,24 +249,32 @@ struct Texture { stream.read(width); stream.read(height); stream.seek(offset - stream.pos); + + int dw = Core::support.texNPOT ? width : nextPow2(width); + int dh = Core::support.texNPOT ? height : nextPow2(height); Color24 *data24 = new Color24[width * height]; - Color32 *data32 = new Color32[width * height]; + Color32 *data32 = new Color32[dw * dh]; stream.raw(data24, width * height * sizeof(Color24)); Color32 *dst = data32; - for (int y = 0; y < height; y++) { + for (int y = 0; y < dh; y++) { Color24 *src = data24 + (height - y - 1) * width; - for (int x = 0; x < width; x++) { - dst->r = src->b; - dst->g = src->g; - dst->b = src->r; + for (int x = 0; x < dw; x++) { + + if (x < width && y < height) { + dst->r = src->b; + dst->g = src->g; + dst->b = src->r; + src++; + } else + dst->r = dst->g = dst->b = 0; + dst->a = 255; dst++; - src++; } } - Texture *tex = new Texture(width, height, Texture::RGBA, false, data32); + Texture *tex = new Texture(dw, dh, Texture::RGBA, false, data32); delete[] data24; delete[] data32; diff --git a/src/ui.h b/src/ui.h index de845a5..cdab125 100644 --- a/src/ui.h +++ b/src/ui.h @@ -254,12 +254,21 @@ namespace UI { Core::setDepthTest(true); } - void textOut(const vec2 &pos, const char *text, Align align = aLeft, float width = 0) { + enum ShadeType { + SHADE_NONE = 0, + SHADE_ORANGE = 1, + SHADE_GRAY = 2, + }; + + void textOut(const vec2 &pos, const char *text, Align align = aLeft, float width = 0, ShadeType shade = SHADE_ORANGE, bool isShadow = false) { if (!text) return; TR::Level *level = game->getLevel(); - MeshBuilder *mesh = game->getMesh(); + if (shade && !isShadow && ((level->version & TR::VER_TR3))) + textOut(pos + vec2(1, 1), text, align, width, shade, true); + + MeshBuilder *mesh = game->getMesh(); int seq = level->extra.glyphs; int x = int(pos.x); @@ -291,14 +300,33 @@ namespace UI { flush(); TR::SpriteTexture &sprite = level->spriteTextures[level->spriteSequences[seq].sStart + frame]; - mesh->addSprite(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, 0, x, y, 0, sprite, 255, true); + + TR::Color32 tColor, bColor; + if (isShadow) { + tColor = bColor = TR::Color32(0, 0, 0, 255); + } else { + tColor = bColor = TR::Color32(255, 255, 255, 255); + + if (shade && ((level->version & TR::VER_TR3))) { + if (shade == SHADE_ORANGE) { + tColor = TR::Color32(255, 190, 90, 255); + bColor = TR::Color32(140, 50, 10, 255); + } + if (shade == SHADE_GRAY) { + tColor = TR::Color32(255, 255, 255, 255); + bColor = TR::Color32(128, 128, 128, 255); + } + } + } + + mesh->addSprite(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, 0, x, y, 0, sprite, tColor, bColor, true); x += char_width[frame] + 1; } } - void textOut(const vec2 &pos, StringID str, Align align = aLeft, float width = 0) { - textOut(pos, STR[str], align, width); + void textOut(const vec2 &pos, StringID str, Align align = aLeft, float width = 0, ShadeType shade = SHADE_ORANGE) { + textOut(pos, STR[str], align, width, shade); } void specOut(const vec2 &pos, char specChar) { @@ -311,7 +339,7 @@ namespace UI { flush(); TR::SpriteTexture &sprite = level->spriteTextures[level->spriteSequences[seq].sStart + specChar]; - mesh->addSprite(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, 0, int(pos.x), int(pos.y), 0, sprite, 255, true); + mesh->addSprite(buffer.indices, buffer.vertices, buffer.iCount, buffer.vCount, 0, int(pos.x), int(pos.y), 0, sprite, TR::Color32(255, 255, 255, 255), TR::Color32(255, 255, 255, 255), true); } #undef MAX_CHARS @@ -402,10 +430,10 @@ namespace UI { void renderHelp() { if (showHelp) - textOut(vec2(0, 64), STR_HELP_TEXT, aRight, width - 32); + textOut(vec2(0, 64), STR_HELP_TEXT, aRight, width - 32, UI::SHADE_GRAY); else if (helpTipTime > 0.0f) - textOut(vec2(0, 480 - 32), STR_HELP_PRESS, aCenter, width); + textOut(vec2(0, 480 - 32), STR_HELP_PRESS, aCenter, width, UI::SHADE_ORANGE); } };