mirror of
https://github.com/XProger/OpenLara.git
synced 2025-08-23 13:23:14 +02:00
#129 TR1 FMVs support (Escape-124)
This commit is contained in:
@@ -587,7 +587,7 @@ namespace Core {
|
||||
|
||||
#include "texture.h"
|
||||
#include "shader.h"
|
||||
//#include "video.h"
|
||||
#include "video.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
|
@@ -17,6 +17,7 @@ namespace Game {
|
||||
Input::stopJoyVibration();
|
||||
delete level;
|
||||
level = new Level(*lvl);
|
||||
level->init();
|
||||
UI::game = level;
|
||||
#if !defined(_OS_PSP) && !defined(_OS_CLOVER)
|
||||
UI::helpTipTime = 5.0f;
|
||||
|
@@ -193,7 +193,7 @@ namespace TR {
|
||||
struct LevelInfo {
|
||||
const char *name;
|
||||
const char *title;
|
||||
int ambientTrack;
|
||||
int track;
|
||||
} LEVEL_INFO[LVL_MAX] = {
|
||||
// TR1
|
||||
{ "" , "Custom Level", TRACK_TR1_CAVES },
|
||||
@@ -243,8 +243,8 @@ namespace TR {
|
||||
{ "CATACOMB" , "Catacombs of the Talion", TRACK_TR2_TIBET_2 },
|
||||
{ "ICECAVE" , "Ice Palace", TRACK_TR2_TIBET_2 },
|
||||
{ "EMPRTOMB" , "Temple of Xian", TRACK_TR2_CHINA_2 },
|
||||
{ "FLOATING" , "Floating Islands", TRACK_TR2_CHINA_2 },
|
||||
{ "CUT4" , "", TRACK_TR2_CUT_4 },
|
||||
{ "FLOATING" , "Floating Islands", TRACK_TR2_CHINA_2 },
|
||||
{ "XIAN" , "The Dragon's Lair", TRACK_TR2_CHINA_2 },
|
||||
{ "HOUSE" , "Home Sweet Home", NO_TRACK },
|
||||
// TR3
|
||||
@@ -341,6 +341,7 @@ namespace TR {
|
||||
case 636660 : version = VER_TR1_PSX;
|
||||
case 512104 : return LVL_TR1_CUT_3;
|
||||
case 1686748 : version = VER_TR1_PSX;
|
||||
case 3094342 :
|
||||
case 3094020 : return LVL_TR1_10B;
|
||||
case 940398 : version = VER_TR1_PSX;
|
||||
case 879582 : return LVL_TR1_CUT_4;
|
||||
@@ -506,7 +507,7 @@ namespace TR {
|
||||
|
||||
LevelID getEndId(Version version) {
|
||||
switch (version & VER_VERSION) {
|
||||
case VER_TR1 : return LVL_TR1_10C;
|
||||
case VER_TR1 : return LVL_TR1_END2;
|
||||
case VER_TR2 : return LVL_TR2_HOUSE;
|
||||
case VER_TR3 : return LVL_TR3_CHAMBER;
|
||||
}
|
||||
@@ -739,7 +740,7 @@ namespace TR {
|
||||
new Stream(title, callback, userData);
|
||||
}
|
||||
|
||||
const char* getGameScreen(Version version, LevelID id) {
|
||||
const char* getGameScreen(LevelID id) {
|
||||
switch (id) {
|
||||
// TR1
|
||||
case LVL_TR1_TITLE :
|
||||
@@ -855,10 +856,65 @@ namespace TR {
|
||||
CHECK_FILE("pix/ANTARC.BMP");
|
||||
return "level/3/ANTARC.PNG";
|
||||
|
||||
default : ;
|
||||
default : return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
const char* getGameVideo(LevelID id) {
|
||||
switch (id) {
|
||||
// TR1
|
||||
case LVL_TR1_TITLE :
|
||||
CHECK_FILE("FMV/CAFE.RPL");
|
||||
return "video/1/CAFE.RPL";
|
||||
case LVL_TR1_GYM :
|
||||
CHECK_FILE("FMV/MANSION.RPL");
|
||||
return "video/1/MANSION.RPL";
|
||||
case LVL_TR1_1 :
|
||||
CHECK_FILE("FMV/SNOW.RPL");
|
||||
return "video/1/SNOW.RPL";
|
||||
case LVL_TR1_4 :
|
||||
CHECK_FILE("FMV/LIFT.RPL");
|
||||
return "video/1/LIFT.RPL";
|
||||
case LVL_TR1_8A :
|
||||
CHECK_FILE("FMV/VISION.RPL");
|
||||
return "video/1/VISION.RPL";
|
||||
case LVL_TR1_10A :
|
||||
CHECK_FILE("FMV/CANYON.RPL");
|
||||
return "video/1/CANYON.RPL";
|
||||
case LVL_TR1_10B :
|
||||
CHECK_FILE("FMV/PIRAMID.RPL");
|
||||
return "video/1/PIRAMID.RPL";
|
||||
case LVL_TR1_CUT_4 :
|
||||
CHECK_FILE("FMV/PRISON.RPL");
|
||||
return "video/1/PRISON.RPL";
|
||||
case LVL_TR1_EGYPT :
|
||||
CHECK_FILE("FMV/END.RPL");
|
||||
return "video/1/END.RPL";
|
||||
// TR2
|
||||
case LVL_TR2_TITLE :
|
||||
CHECK_FILE("fmv/ANCIENT.RPL");
|
||||
return "video/2/ANCIENT.RPL";
|
||||
case LVL_TR2_WALL :
|
||||
CHECK_FILE("fmv/MODERN.RPL");
|
||||
return "video/2/MODERN.RPL";
|
||||
case LVL_TR2_RIG :
|
||||
CHECK_FILE("fmv/LANDING.RPL");
|
||||
return "video/2/LANDING.RPL";
|
||||
case LVL_TR2_UNWATER :
|
||||
CHECK_FILE("fmv/MS.RPL");
|
||||
return "video/2/MS.RPL";
|
||||
case LVL_TR2_SKIDOO :
|
||||
CHECK_FILE("fmv/CRASH.RPL");
|
||||
return "video/2/CRASH.RPL";
|
||||
case LVL_TR2_EMPRTOMB :
|
||||
CHECK_FILE("fmv/JEEP.RPL");
|
||||
return "video/2/JEEP.RPL";
|
||||
case LVL_TR2_HOUSE :
|
||||
CHECK_FILE("fmv/END.RPL");
|
||||
return "video/2/END.RPL";
|
||||
// TR3
|
||||
default : return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#define FOG_DIST (1.0f / (18 * 1024))
|
||||
|
@@ -592,10 +592,18 @@ namespace GAPI {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void *pix = data;
|
||||
if (data && !Core::support.texNPOT && (width != origWidth || height != origHeight))
|
||||
pix = NULL;
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
glTexImage2D(cube ? (GL_TEXTURE_CUBE_MAP_POSITIVE_X + i) : GL_TEXTURE_2D, 0, desc.ifmt, width, height, 0, desc.fmt, desc.type, data);
|
||||
glTexImage2D(cube ? (GL_TEXTURE_CUBE_MAP_POSITIVE_X + i) : GL_TEXTURE_2D, 0, desc.ifmt, width, height, 0, desc.fmt, desc.type, pix);
|
||||
if (!cube) break;
|
||||
}
|
||||
|
||||
if (pix != data)
|
||||
update(data);
|
||||
}
|
||||
|
||||
void deinit() {
|
||||
|
102
src/inventory.h
102
src/inventory.h
@@ -210,6 +210,7 @@ struct Inventory {
|
||||
|
||||
IGame *game;
|
||||
Texture *background[2];
|
||||
Video *video;
|
||||
|
||||
bool active;
|
||||
bool chosen;
|
||||
@@ -454,6 +455,10 @@ struct Inventory {
|
||||
|
||||
static void loadTitleBG(Stream *stream, void *userData) {
|
||||
Inventory *inv = (Inventory*)userData;
|
||||
|
||||
if (!inv->video)
|
||||
inv->skipVideo(); // play background track etc.
|
||||
|
||||
if (!stream) {
|
||||
inv->titleTimer = 0.0f;
|
||||
return;
|
||||
@@ -464,6 +469,13 @@ struct Inventory {
|
||||
delete stream;
|
||||
}
|
||||
|
||||
static void loadVideo(Stream *stream, void *userData) {
|
||||
Inventory *inv = (Inventory*)userData;
|
||||
if (stream)
|
||||
inv->video = new Video(stream);
|
||||
new Stream(TR::getGameScreen(inv->game->getLevel()->id), loadTitleBG, inv);
|
||||
}
|
||||
|
||||
Inventory(IGame *game) : game(game), active(false), chosen(false), index(0), targetIndex(0), page(PAGE_OPTION), targetPage(PAGE_OPTION), itemsCount(0), playerIndex(0), changeTimer(0.0f), nextLevel(TR::LVL_MAX), lastKey(cMAX) {
|
||||
TR::LevelID id = game->getLevel()->id;
|
||||
|
||||
@@ -505,13 +517,6 @@ struct Inventory {
|
||||
|
||||
memset(background, 0, sizeof(background));
|
||||
|
||||
const char *titleBG = TR::getGameScreen(level->version, level->id);
|
||||
if (titleBG) {
|
||||
titleTimer = TITLE_LOADING;
|
||||
new Stream(titleBG, loadTitleBG, this);
|
||||
} else
|
||||
titleTimer = 0.0f;
|
||||
|
||||
if (level->isTitle()) {
|
||||
add(TR::Entity::INV_HOME);
|
||||
} else {
|
||||
@@ -523,9 +528,14 @@ struct Inventory {
|
||||
memset(pageItemIndex, 0, sizeof(pageItemIndex));
|
||||
|
||||
waitForKey = NULL;
|
||||
video = NULL;
|
||||
|
||||
titleTimer = TITLE_LOADING;
|
||||
}
|
||||
|
||||
~Inventory() {
|
||||
delete video;
|
||||
|
||||
for (int i = 0; i < itemsCount; i++)
|
||||
delete items[i];
|
||||
|
||||
@@ -533,6 +543,10 @@ struct Inventory {
|
||||
delete background[i];
|
||||
}
|
||||
|
||||
void init() {
|
||||
new Stream(TR::getGameVideo(game->getLevel()->id), loadVideo, this);
|
||||
}
|
||||
|
||||
bool isActive() {
|
||||
return active || phaseRing > 0.0f;
|
||||
}
|
||||
@@ -651,7 +665,10 @@ struct Inventory {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool toggle(int playerIndex = 0, Page curPage = PAGE_INVENTORY, TR::Entity::Type type = TR::Entity::LARA) {
|
||||
void toggle(int playerIndex = 0, Page curPage = PAGE_INVENTORY, TR::Entity::Type type = TR::Entity::LARA) {
|
||||
if (titleTimer != 0.0f || (isActive() != active))
|
||||
return;
|
||||
|
||||
Input::stopJoyVibration();
|
||||
|
||||
this->playerIndex = playerIndex;
|
||||
@@ -686,7 +703,6 @@ struct Inventory {
|
||||
// chooseItem();
|
||||
}
|
||||
}
|
||||
return active;
|
||||
}
|
||||
|
||||
void doPhase(bool increase, float speed, float &value) {
|
||||
@@ -898,7 +914,30 @@ struct Inventory {
|
||||
}
|
||||
};
|
||||
|
||||
void skipVideo() {
|
||||
delete video;
|
||||
video = NULL;
|
||||
game->playTrack(0);
|
||||
if (game->getLevel()->isTitle()) {
|
||||
titleTimer = 0.0f;
|
||||
toggle(0, Inventory::PAGE_OPTION);
|
||||
}
|
||||
Input::reset();
|
||||
}
|
||||
|
||||
void update() {
|
||||
if (video && (Input::state[0][cInventory] || Input::state[1][cInventory]))
|
||||
skipVideo();
|
||||
|
||||
if (video) {
|
||||
video->update();
|
||||
if (video->isPlaying)
|
||||
return;
|
||||
skipVideo();
|
||||
}
|
||||
|
||||
if (video || titleTimer == TITLE_LOADING) return;
|
||||
|
||||
if (titleTimer != TITLE_LOADING && titleTimer > 0.0f) {
|
||||
titleTimer -= Core::deltaTime;
|
||||
if (titleTimer < 0.0f)
|
||||
@@ -1139,14 +1178,14 @@ struct Inventory {
|
||||
// vertical blur
|
||||
Core::setTarget(background[1], RT_STORE_COLOR);
|
||||
game->setShader(Core::passFilter, Shader::FILTER_BLUR, false, false);
|
||||
Core::active.shader->setParam(uParam, vec4(0, 1, 1.0f / INVENTORY_BG_SIZE, 0));;
|
||||
Core::active.shader->setParam(uParam, vec4(0, 1.0f / INVENTORY_BG_SIZE, 0, 0));
|
||||
background[0]->bind(sDiffuse);
|
||||
game->getMesh()->renderQuad();
|
||||
|
||||
// horizontal blur
|
||||
Core::setTarget(background[0], RT_STORE_COLOR);
|
||||
game->setShader(Core::passFilter, Shader::FILTER_BLUR, false, false);
|
||||
Core::active.shader->setParam(uParam, vec4(1, 0, 1.0f / INVENTORY_BG_SIZE, 0));;
|
||||
Core::active.shader->setParam(uParam, vec4(1.0f / INVENTORY_BG_SIZE, 0, 0, 0));
|
||||
background[1]->bind(sDiffuse);
|
||||
game->getMesh()->renderQuad();
|
||||
|
||||
@@ -1307,7 +1346,7 @@ struct Inventory {
|
||||
}
|
||||
}
|
||||
|
||||
void renderTitleBG(float sx = 1.0f, float sy = 1.0f) {
|
||||
void renderTitleBG(float sx = 1.0f, float sy = 1.0f, uint8 alpha = 255) {
|
||||
float aspectSrc, aspectDst, aspectImg, ax, ay;
|
||||
|
||||
if (background[0]) {
|
||||
@@ -1331,14 +1370,7 @@ struct Inventory {
|
||||
Core::mModel.scale(vec3(1.0f / 32767.0f));
|
||||
#endif
|
||||
|
||||
uint8 alpha;
|
||||
if (!isActive() && titleTimer > 0.0f && titleTimer < 1.0f) {
|
||||
Core::setBlendMode(bmAlpha);
|
||||
alpha = uint8(titleTimer * 255);
|
||||
} else {
|
||||
Core::setBlendMode(bmNone);
|
||||
alpha = 255;
|
||||
}
|
||||
Core::setBlendMode(alpha < 255 ? bmAlpha : bmNone);
|
||||
|
||||
Index indices[6 * 3] = { 0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11 };
|
||||
Vertex vertices[4 * 3];
|
||||
@@ -1414,7 +1446,7 @@ struct Inventory {
|
||||
background[0]->bind(sDiffuse);
|
||||
|
||||
game->setShader(Core::passFilter, Shader::FILTER_UPSCALE, false, false);
|
||||
Core::active.shader->setParam(uParam, vec4(float(Core::active.textures[sDiffuse]->width), float(Core::active.textures[sDiffuse]->height), 0.0f, 0.0f));
|
||||
Core::active.shader->setParam(uParam, vec4(float(Core::active.textures[sDiffuse]->width), float(Core::active.textures[sDiffuse]->height), Core::getTime() * 0.001f, 0.0f));
|
||||
game->getMesh()->renderBuffer(indices, COUNT(indices), vertices, COUNT(vertices));
|
||||
}
|
||||
|
||||
@@ -1452,16 +1484,22 @@ struct Inventory {
|
||||
|
||||
Core::setDepthTest(false);
|
||||
|
||||
uint8 alpha;
|
||||
if (!isActive() && titleTimer > 0.0f && titleTimer < 1.0f)
|
||||
alpha = uint8(titleTimer * 255);
|
||||
else
|
||||
alpha = 255;
|
||||
|
||||
if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) {
|
||||
if (game->getLevel()->isTitle())
|
||||
renderTitleBG();
|
||||
renderTitleBG(1.0f, 1.0f, alpha);
|
||||
else
|
||||
renderGameBG();
|
||||
} else {
|
||||
if (background[1])
|
||||
renderGameBG();
|
||||
else
|
||||
renderTitleBG();
|
||||
renderTitleBG(1.0f, 1.0f, alpha);
|
||||
}
|
||||
|
||||
Core::setBlendMode(bmAlpha);
|
||||
@@ -1504,6 +1542,24 @@ struct Inventory {
|
||||
}
|
||||
|
||||
void render(float aspect) {
|
||||
if (video) {
|
||||
Core::setDepthTest(false);
|
||||
video->render();
|
||||
|
||||
Texture *tmp = background[0];
|
||||
|
||||
background[0] = video->frameTex[0];
|
||||
renderTitleBG(1.0f, 1.2f, 255);
|
||||
|
||||
background[0] = video->frameTex[1];
|
||||
renderTitleBG(1.0f, 1.2f, clamp(int((video->time / video->step) * 255), 0, 255));
|
||||
|
||||
background[0] = tmp;
|
||||
|
||||
Core::setDepthTest(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isActive() && titleTimer == 0.0f)
|
||||
return;
|
||||
|
||||
|
111
src/level.h
111
src/level.h
@@ -26,7 +26,7 @@ extern void loadAsync(Stream *stream, void *userData);
|
||||
|
||||
struct Level : IGame {
|
||||
TR::Level level;
|
||||
Inventory inventory;
|
||||
Inventory *inventory;
|
||||
Texture *atlas;
|
||||
MeshBuilder *mesh;
|
||||
|
||||
@@ -57,8 +57,6 @@ struct Level : IGame {
|
||||
float cutsceneWaitTimer;
|
||||
float animTexTimer;
|
||||
|
||||
Texture *cube360;
|
||||
|
||||
// IGame implementation ========
|
||||
virtual void loadLevel(TR::LevelID id) {
|
||||
if (isEnded) return;
|
||||
@@ -86,7 +84,7 @@ struct Level : IGame {
|
||||
virtual void saveGame(int slot) {
|
||||
LOG("Save Game... ");
|
||||
|
||||
char *data = new char[sizeof(TR::SaveGame) + sizeof(TR::SaveGame::Item) * inventory.itemsCount + sizeof(TR::SaveGame::CurrentState) + sizeof(TR::SaveGame::Entity) * level.entitiesCount]; // oversized
|
||||
char *data = new char[sizeof(TR::SaveGame) + sizeof(TR::SaveGame::Item) * inventory->itemsCount + sizeof(TR::SaveGame::CurrentState) + sizeof(TR::SaveGame::Entity) * level.entitiesCount]; // oversized
|
||||
char *ptr = data;
|
||||
|
||||
TR::SaveGame *save = (TR::SaveGame*)ptr;
|
||||
@@ -107,9 +105,9 @@ struct Level : IGame {
|
||||
|
||||
// inventory items
|
||||
currentState->progress.itemsCount = 0;
|
||||
for (int i = 0; i < inventory.itemsCount; i++) {
|
||||
for (int i = 0; i < inventory->itemsCount; i++) {
|
||||
TR::SaveGame::Item *item = (TR::SaveGame::Item*)ptr;
|
||||
Inventory::Item *invItem = inventory.items[i];
|
||||
Inventory::Item *invItem = inventory->items[i];
|
||||
|
||||
if (!TR::Entity::isPickup(TR::Entity::convFromInv(invItem->type))) continue;
|
||||
|
||||
@@ -167,7 +165,7 @@ struct Level : IGame {
|
||||
|
||||
for (int i = 0; i < currentState->progress.itemsCount; i++) {
|
||||
TR::SaveGame::Item *item = (TR::SaveGame::Item*)ptr;
|
||||
inventory.add(TR::Entity::Type(item->type), item->count, false);
|
||||
inventory->add(TR::Entity::Type(item->type), item->count, false);
|
||||
ptr += sizeof(*item);
|
||||
}
|
||||
|
||||
@@ -203,11 +201,11 @@ struct Level : IGame {
|
||||
}
|
||||
|
||||
void clearInventory() {
|
||||
int i = inventory.itemsCount;
|
||||
int i = inventory->itemsCount;
|
||||
|
||||
while (i--) {
|
||||
if (TR::Entity::isPickup(TR::Entity::convFromInv(inventory.items[i]->type)))
|
||||
inventory.remove(i);
|
||||
if (TR::Entity::isPickup(TR::Entity::convFromInv(inventory->items[i]->type)))
|
||||
inventory->remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,10 +279,10 @@ struct Level : IGame {
|
||||
waterCache = Core::settings.detail.water > Core::Settings::LOW ? new WaterCache(this) : NULL;
|
||||
}
|
||||
|
||||
if (redraw && inventory.active && !level.isTitle()) {
|
||||
if (redraw && inventory->active && !level.isTitle()) {
|
||||
Core::reset();
|
||||
Core::beginFrame();
|
||||
inventory.prepareBackground();
|
||||
inventory->prepareBackground();
|
||||
Core::endFrame();
|
||||
}
|
||||
}
|
||||
@@ -559,20 +557,20 @@ struct Level : IGame {
|
||||
|
||||
virtual bool invUse(int playerIndex, TR::Entity::Type type) {
|
||||
if (!players[playerIndex]->useItem(type))
|
||||
return inventory.use(type);
|
||||
return inventory->use(type);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void invAdd(TR::Entity::Type type, int count) {
|
||||
inventory.add(type, count);
|
||||
inventory->add(type, count);
|
||||
}
|
||||
|
||||
virtual int* invCount(TR::Entity::Type type) {
|
||||
return inventory.getCountPtr(type);
|
||||
return inventory->getCountPtr(type);
|
||||
}
|
||||
|
||||
virtual bool invChooseKey(int playerIndex, TR::Entity::Type hole) {
|
||||
return inventory.chooseKey(playerIndex, hole);
|
||||
return inventory->chooseKey(playerIndex, hole);
|
||||
}
|
||||
|
||||
virtual Sound::Sample* playSound(int id, const vec3 &pos = vec3(0.0f), int flags = 0) const {
|
||||
@@ -609,7 +607,7 @@ struct Level : IGame {
|
||||
void stopChannel(Sound::Sample *channel) {
|
||||
if (channel == sndTrack) {
|
||||
sndTrack = NULL;
|
||||
if (level.state.flags.track == TR::LEVEL_INFO[level.id].ambientTrack) // play ambient track
|
||||
if (level.state.flags.track == TR::LEVEL_INFO[level.id].track) // play ambient track
|
||||
playTrack(0);
|
||||
}
|
||||
}
|
||||
@@ -640,7 +638,7 @@ struct Level : IGame {
|
||||
|
||||
virtual void playTrack(uint8 track) {
|
||||
if (track == 0)
|
||||
track = TR::LEVEL_INFO[level.id].ambientTrack;
|
||||
track = TR::LEVEL_INFO[level.id].track;
|
||||
|
||||
if (level.state.flags.track == track) {
|
||||
// if (sndTrack) {
|
||||
@@ -660,7 +658,7 @@ struct Level : IGame {
|
||||
if (track == 0xFF) return;
|
||||
|
||||
int flags = Sound::MUSIC;
|
||||
if (track == TR::LEVEL_INFO[level.id].ambientTrack)
|
||||
if (track == TR::LEVEL_INFO[level.id].track)
|
||||
flags |= Sound::LOOP;
|
||||
|
||||
waitTrack = true;
|
||||
@@ -672,7 +670,7 @@ struct Level : IGame {
|
||||
}
|
||||
//==============================
|
||||
|
||||
Level(Stream &stream) : level(stream), inventory(this), waitTrack(false), isEnded(false), cutsceneWaitTimer(0.0f), animTexTimer(0.0f) {
|
||||
Level(Stream &stream) : level(stream), waitTrack(false), isEnded(false), cutsceneWaitTimer(0.0f), animTexTimer(0.0f) {
|
||||
#ifdef _OS_PSP
|
||||
GAPI::freeEDRAM();
|
||||
#endif
|
||||
@@ -688,6 +686,8 @@ struct Level : IGame {
|
||||
mesh = new MeshBuilder(level, atlas);
|
||||
initOverrides();
|
||||
|
||||
inventory = new Inventory(this);
|
||||
|
||||
for (int i = 0; i < level.entitiesBaseCount; i++) {
|
||||
TR::Entity &e = level.entities[i];
|
||||
e.controller = initController(i);
|
||||
@@ -724,18 +724,14 @@ struct Level : IGame {
|
||||
playSound(src.id, vec3(float(src.x), float(src.y), float(src.z)), flags);
|
||||
}
|
||||
|
||||
} else {
|
||||
inventory.toggle(0, Inventory::PAGE_OPTION);
|
||||
}
|
||||
|
||||
setClipParams(1.0f, NO_CLIP_PLANE);
|
||||
|
||||
effect = TR::Effect::NONE;
|
||||
cube360 = NULL;
|
||||
|
||||
sndWater = sndTrack = NULL;
|
||||
|
||||
playTrack(0);
|
||||
/*
|
||||
if (level.id == TR::LVL_TR2_RIG) {
|
||||
lara->animation.setAnim(level.models[level.extra.laraSpec].animation);
|
||||
@@ -747,7 +743,7 @@ struct Level : IGame {
|
||||
}
|
||||
|
||||
virtual ~Level() {
|
||||
delete cube360;
|
||||
delete inventory;
|
||||
|
||||
for (int i = 0; i < level.entitiesCount; i++)
|
||||
delete (Controller*)level.entities[i].controller;
|
||||
@@ -763,6 +759,10 @@ struct Level : IGame {
|
||||
Sound::stopAll();
|
||||
}
|
||||
|
||||
void init() {
|
||||
inventory->init();
|
||||
}
|
||||
|
||||
void addPlayer(int index) {
|
||||
if (level.isCutsceneLevel()) return;
|
||||
|
||||
@@ -1477,7 +1477,7 @@ struct Level : IGame {
|
||||
if (isModel) { // model
|
||||
vec3 pos = controller->getPos();
|
||||
if (ambientCache) {
|
||||
if (!controller->getEntity().isDoor()) { // no advanced ambient lighting for secret (all) doors
|
||||
if (!controller->getEntity().isDoor() && !controller->getEntity().isBlock()) { // no advanced ambient lighting for secret (all) doors and blocks
|
||||
AmbientCache::Cube cube;
|
||||
ambientCache->getAmbient(roomIndex, pos, cube);
|
||||
if (cube.status == AmbientCache::Cube::READY)
|
||||
@@ -1501,8 +1501,13 @@ struct Level : IGame {
|
||||
}
|
||||
|
||||
void update() {
|
||||
if (inventory->video) {
|
||||
inventory->update();
|
||||
return;
|
||||
}
|
||||
|
||||
if (level.isCutsceneLevel() && waitTrack) {
|
||||
if (!sndTrack && TR::LEVEL_INFO[level.id].ambientTrack != TR::NO_TRACK) {
|
||||
if (!sndTrack && TR::LEVEL_INFO[level.id].track != TR::NO_TRACK) {
|
||||
if (camera->timer > 0.0f) // for the case that audio stops before animation ends
|
||||
loadNextLevel();
|
||||
return;
|
||||
@@ -1521,7 +1526,7 @@ struct Level : IGame {
|
||||
}
|
||||
}
|
||||
|
||||
if ((Input::state[0][cInventory] || Input::state[1][cInventory]) && !level.isTitle() && inventory.titleTimer < 1.0f && !inventory.active && inventory.lastKey == cMAX) {
|
||||
if ((Input::state[0][cInventory] || Input::state[1][cInventory]) && !level.isTitle() && inventory->titleTimer < 1.0f && !inventory->active && inventory->lastKey == cMAX) {
|
||||
int playerIndex = Input::state[0][cInventory] ? 0 : 1;
|
||||
|
||||
if (level.isCutsceneLevel()) {
|
||||
@@ -1530,21 +1535,21 @@ struct Level : IGame {
|
||||
}
|
||||
|
||||
if (player->health <= 0.0f)
|
||||
inventory.toggle(playerIndex, Inventory::PAGE_OPTION, TR::Entity::INV_PASSPORT);
|
||||
inventory->toggle(playerIndex, Inventory::PAGE_OPTION, TR::Entity::INV_PASSPORT);
|
||||
else
|
||||
inventory.toggle(playerIndex);
|
||||
inventory->toggle(playerIndex);
|
||||
}
|
||||
|
||||
inventory.update();
|
||||
inventory->update();
|
||||
|
||||
if (inventory.titleTimer > 1.0f)
|
||||
if (inventory->titleTimer > 1.0f)
|
||||
return;
|
||||
|
||||
UI::update();
|
||||
|
||||
float volWater, volTrack;
|
||||
|
||||
if (inventory.isActive() || level.isTitle()) {
|
||||
if (inventory->isActive() || level.isTitle()) {
|
||||
Sound::reverb.setRoomSize(vec3(1.0f));
|
||||
volWater = 0.0f;
|
||||
volTrack = level.isTitle() ? 0.9f : 0.0f;
|
||||
@@ -2189,7 +2194,7 @@ struct Level : IGame {
|
||||
|
||||
#ifdef DEBUG_RENDER
|
||||
void renderDebug() {
|
||||
if (level.isTitle() || inventory.titleTimer > 1.0f) return;
|
||||
if (level.isTitle() || inventory->titleTimer > 1.0f) return;
|
||||
|
||||
Core::setViewport(Core::x, Core::y, Core::width, Core::height);
|
||||
camera->setup(true);
|
||||
@@ -2432,6 +2437,11 @@ struct Level : IGame {
|
||||
}
|
||||
|
||||
void renderPrepare() {
|
||||
if (inventory->video) {
|
||||
inventory->render(1.0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ambientCache)
|
||||
ambientCache->processQueue();
|
||||
|
||||
@@ -2534,7 +2544,7 @@ struct Level : IGame {
|
||||
}
|
||||
|
||||
void renderUI() {
|
||||
if (level.isCutsceneLevel() || inventory.titleTimer > 1.0f) return;
|
||||
if (level.isCutsceneLevel() || inventory->titleTimer > 1.0f) return;
|
||||
|
||||
UI::begin();
|
||||
UI::updateAspect(camera->aspect);
|
||||
@@ -2551,7 +2561,7 @@ struct Level : IGame {
|
||||
if (oxygen <= 0.2f) oxygen = 0.0f;
|
||||
}
|
||||
|
||||
float eye = inventory.active ? 0.0f : UI::width * Core::eye * 0.02f;
|
||||
float eye = inventory->active ? 0.0f : UI::width * Core::eye * 0.02f;
|
||||
|
||||
vec2 pos;
|
||||
if (Core::settings.detail.stereo == Core::Settings::STEREO_VR)
|
||||
@@ -2564,14 +2574,14 @@ struct Level : IGame {
|
||||
pos.y += 16.0f;
|
||||
}
|
||||
|
||||
if ((!inventory.active && (!player->emptyHands() || player->damageTime > 0.0f || health <= 0.2f))) {
|
||||
if ((!inventory->active && (!player->emptyHands() || player->damageTime > 0.0f || health <= 0.2f))) {
|
||||
UI::renderBar(UI::BAR_HEALTH, pos, size, health);
|
||||
pos.y += 32.0f;
|
||||
|
||||
if (!inventory.active && !player->emptyHands()) { // ammo
|
||||
int index = inventory.contains(player->getCurrentWeaponInv());
|
||||
if (!inventory->active && !player->emptyHands()) { // ammo
|
||||
int index = inventory->contains(player->getCurrentWeaponInv());
|
||||
if (index > -1)
|
||||
inventory.renderItemCount(inventory.items[index], pos, size.x);
|
||||
inventory->renderItemCount(inventory->items[index], pos, size.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2597,21 +2607,21 @@ struct Level : IGame {
|
||||
|
||||
Core::eye = float(eye);
|
||||
|
||||
if (level.isTitle() || inventory.titleTimer > 0.0f)
|
||||
inventory.renderBackground();
|
||||
inventory.render(aspect);
|
||||
if (level.isTitle() || inventory->titleTimer > 0.0f)
|
||||
inventory->renderBackground();
|
||||
inventory->render(aspect);
|
||||
|
||||
UI::begin();
|
||||
UI::updateAspect(aspect);
|
||||
inventory.renderUI();
|
||||
inventory->renderUI();
|
||||
UI::end();
|
||||
}
|
||||
|
||||
void renderInventory() {
|
||||
Core::setTarget(NULL, RT_CLEAR_DEPTH | RT_STORE_COLOR);
|
||||
|
||||
if (!(level.isTitle() || inventory.titleTimer > 0.0f))
|
||||
inventory.renderBackground();
|
||||
if (!(level.isTitle() || inventory->titleTimer > 0.0f))
|
||||
inventory->renderBackground();
|
||||
|
||||
float oldEye = Core::eye;
|
||||
|
||||
@@ -2626,7 +2636,10 @@ struct Level : IGame {
|
||||
}
|
||||
|
||||
void render() {
|
||||
bool title = inventory.isActive() || level.isTitle();
|
||||
if (inventory->video)
|
||||
return;
|
||||
|
||||
bool title = inventory->isActive() || level.isTitle();
|
||||
bool copyBg = title && lastTitle != title;
|
||||
lastTitle = title;
|
||||
|
||||
@@ -2640,11 +2653,11 @@ struct Level : IGame {
|
||||
}
|
||||
|
||||
if (copyBg) {
|
||||
inventory.prepareBackground();
|
||||
inventory->prepareBackground();
|
||||
}
|
||||
|
||||
if (!level.isTitle()) {
|
||||
if (inventory.phaseRing < 1.0f && inventory.titleTimer <= 1.0f) {
|
||||
if (inventory->phaseRing < 1.0f && inventory->titleTimer <= 1.0f) {
|
||||
renderGame(true);
|
||||
title = false;
|
||||
}
|
||||
|
@@ -225,6 +225,7 @@
|
||||
<ClInclude Include="..\..\format.h" />
|
||||
<ClInclude Include="..\..\trigger.h" />
|
||||
<ClInclude Include="..\..\utils.h" />
|
||||
<ClInclude Include="..\..\video.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\shaders\filter.glsl" />
|
||||
|
@@ -52,6 +52,7 @@
|
||||
<ClInclude Include="..\..\libs\tinf\tinf.h">
|
||||
<Filter>libs\tinf</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\video.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\shaders\filter.glsl">
|
||||
|
@@ -10,7 +10,7 @@ struct Shader : GAPI::Shader {
|
||||
/* shader */ SPRITE = 0, FLASH = 1, ROOM = 2, ENTITY = 3, MIRROR = 4,
|
||||
/* filter */ FILTER_UPSCALE = 0, FILTER_DOWNSAMPLE = 1, FILTER_GRAYSCALE = 2, FILTER_BLUR = 3, FILTER_EQUIRECTANGULAR = 4,
|
||||
/* water */ WATER_DROP = 0, WATER_STEP = 1, WATER_CAUSTICS = 2, WATER_MASK = 3, WATER_COMPOSE = 4,
|
||||
MAX = 6
|
||||
MAX = 5
|
||||
};
|
||||
|
||||
Shader(Core::Pass pass, Type type, int *def, int defCount) : GAPI::Shader() {
|
||||
|
@@ -46,7 +46,7 @@ uniform vec4 uParam;
|
||||
const vec3 offset = vec3(0.0, 1.3846153846, 3.2307692308);
|
||||
const vec3 weight = vec3(0.2270270270, 0.3162162162, 0.0702702703);
|
||||
|
||||
vec2 dir = uParam.xy * uParam.z;
|
||||
vec2 dir = uParam.xy;
|
||||
vec4 color = texture2D(sDiffuse, vTexCoord) * weight[0];
|
||||
color += texture2D(sDiffuse, vTexCoord + dir * offset[1]) * weight[1];
|
||||
color += texture2D(sDiffuse, vTexCoord - dir * offset[1]) * weight[1];
|
||||
@@ -67,7 +67,6 @@ uniform vec4 uParam;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
vec4 upscale() { // https://www.shadertoy.com/view/XsfGDn
|
||||
vec2 uv = vTexCoord * uParam.xy + 0.5;
|
||||
vec2 iuv = floor(uv);
|
||||
|
10
src/sound.h
10
src/sound.h
@@ -481,6 +481,10 @@ namespace Sound {
|
||||
bool isPlaying;
|
||||
bool stopAfterFade;
|
||||
|
||||
Sample(Decoder *decoder, float volume, float pitch, int flags, int id) : uniquePtr(NULL), decoder(decoder), volume(volume), volumeTarget(volume), volumeDelta(0.0f), pitch(pitch), flags(flags), id(id) {
|
||||
isPlaying = decoder != NULL;
|
||||
}
|
||||
|
||||
Sample(Stream *stream, const vec3 *pos, float volume, float pitch, int flags, int id) : uniquePtr(pos), decoder(NULL), volume(volume), volumeTarget(volume), volumeDelta(0.0f), pitch(pitch), flags(flags), id(id) {
|
||||
this->pos = pos ? *pos : vec3(0.0f);
|
||||
|
||||
@@ -811,6 +815,12 @@ namespace Sound {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Sample* play(Decoder *decoder) {
|
||||
if (channelsCount < SND_CHANNELS_MAX)
|
||||
return channels[channelsCount++] = new Sample(decoder, 1.0f, 1.0f, MUSIC, -1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void stop(int id = -1) {
|
||||
OS_LOCK(lock);
|
||||
|
||||
|
54
src/utils.h
54
src/utils.h
@@ -180,6 +180,12 @@ int nextPow2(uint32 x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
inline uint32 log2i(uint32 value) {
|
||||
int res = 0;
|
||||
for(; value; value >>= 1, res++);
|
||||
return res ? res - 1 : res;
|
||||
}
|
||||
|
||||
uint32 fnv32(const char *data, int32 size, uint32 hash = 0x811c9dc5) {
|
||||
for (int i = 0; i < size; i++)
|
||||
hash = (hash ^ data[i]) * 0x01000193;
|
||||
@@ -1200,7 +1206,17 @@ struct Stream {
|
||||
}
|
||||
}
|
||||
|
||||
Stream(const char *name, Callback *callback = NULL, void *userData = NULL) : callback(callback), userData(userData), data(NULL), name(NULL), size(-1), pos(0), endian(eLittle) {
|
||||
Stream(const char *name, Callback *callback = NULL, void *userData = NULL) : callback(callback), userData(userData), f(NULL), data(NULL), name(NULL), size(-1), pos(0), endian(eLittle) {
|
||||
if (!name && callback) {
|
||||
callback(NULL, userData);
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!name) {
|
||||
ASSERT(false);
|
||||
}
|
||||
|
||||
if (contentDir[0] && (!cacheDir[0] || !strstr(name, cacheDir))) {
|
||||
char path[255];
|
||||
path[0] = 0;
|
||||
@@ -1277,8 +1293,10 @@ struct Stream {
|
||||
}
|
||||
|
||||
void setPos(int pos) {
|
||||
this->pos = pos;
|
||||
if (f) fseek(f, pos, SEEK_SET);
|
||||
if (this->pos != pos) {
|
||||
this->pos = pos;
|
||||
if (f) fseek(f, pos, SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
void seek(int offset) {
|
||||
@@ -1287,13 +1305,14 @@ struct Stream {
|
||||
pos += offset;
|
||||
}
|
||||
|
||||
void raw(void *data, int count) {
|
||||
if (!count) return;
|
||||
int raw(void *data, int count) {
|
||||
if (!count) return 0;
|
||||
if (f)
|
||||
fread(data, 1, count, f);
|
||||
count = fread(data, 1, count, f);
|
||||
else
|
||||
memcpy(data, this->data + pos, count);
|
||||
pos += count;
|
||||
return count;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -1427,6 +1446,29 @@ struct BitStream {
|
||||
|
||||
BitStream(uint8 *data, int size) : data(data), end(data + size), index(0), value(0) {}
|
||||
|
||||
uint32 read(int count) {
|
||||
uint32 bits = 0;
|
||||
|
||||
int m = count - 1;
|
||||
|
||||
while (count--) {
|
||||
if (!index) {
|
||||
ASSERT(data < end);
|
||||
value = *data++;
|
||||
index = 8;
|
||||
}
|
||||
|
||||
if (value & 1)
|
||||
bits |= (1 << (m - count));
|
||||
|
||||
value >>= 1;
|
||||
|
||||
index--;
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
uint8 readBits(int count) {
|
||||
uint32 bits = 0;
|
||||
|
||||
|
437
src/video.h
Normal file
437
src/video.h
Normal file
@@ -0,0 +1,437 @@
|
||||
#ifndef H_VIDEO
|
||||
#define H_VIDEO
|
||||
|
||||
#include "utils.h"
|
||||
#include "texture.h"
|
||||
#include "sound.h"
|
||||
|
||||
struct Video {
|
||||
|
||||
struct Decoder : Sound::Decoder {
|
||||
int width, height, fps;
|
||||
|
||||
Decoder(Stream *stream) : Sound::Decoder(stream, 2) {}
|
||||
virtual ~Decoder() { /* delete stream; */ }
|
||||
virtual bool decode(uint8 *frame) { return false; }
|
||||
};
|
||||
|
||||
// based on ffmpeg code https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/escape124.c
|
||||
struct Escape124 : Decoder {
|
||||
int vfmt, bpp;
|
||||
int sfmt, rate, channels, bps;
|
||||
int framesCount, chunksCount, offset;
|
||||
int curVideoPos, curVideoChunk;
|
||||
int curAudioPos, curAudioChunk;
|
||||
|
||||
uint32 *prevFrame, *nextFrame;
|
||||
Sound::Frame prevSample;
|
||||
|
||||
struct Chunk {
|
||||
int offset;
|
||||
int videoSize;
|
||||
int audioSize;
|
||||
} *chunks;
|
||||
|
||||
union MacroBlock {
|
||||
uint32 pixels[4];
|
||||
};
|
||||
|
||||
union SuperBlock {
|
||||
uint32 pixels[64];
|
||||
};
|
||||
|
||||
struct Codebook {
|
||||
uint32 size;
|
||||
uint32 depth;
|
||||
MacroBlock *blocks;
|
||||
} codebook[3];
|
||||
|
||||
Escape124(Stream *stream) : Decoder(stream), chunks(NULL) {
|
||||
for (int i = 0; i < 4; i++)
|
||||
skipLine();
|
||||
|
||||
vfmt = readValue(); // video format
|
||||
width = readValue(); // x size in pixels
|
||||
height = readValue(); // y size in pixels
|
||||
bpp = readValue(); // bits per pixel RGB
|
||||
fps = readValue(); // frames per second
|
||||
sfmt = readValue(); // sound format
|
||||
rate = readValue(); // Hz Samples
|
||||
channels = readValue(); // channel
|
||||
bps = readValue(); // bits per sample (LINEAR UNSIGNED)
|
||||
framesCount = readValue(); // frames per chunk
|
||||
chunksCount = readValue() + 1; // number of chunks
|
||||
skipLine(); // even chunk size
|
||||
skipLine(); // odd chunk size
|
||||
offset = readValue(); // offset to chunk cat
|
||||
skipLine(); // offset to sprite
|
||||
skipLine(); // size of sprite
|
||||
skipLine(); // offset to key frames
|
||||
|
||||
stream->setPos(offset);
|
||||
|
||||
chunks = new Chunk[chunksCount];
|
||||
for (int i = 0; i < chunksCount; i++) {
|
||||
chunks[i].offset = readValue();
|
||||
chunks[i].videoSize = readValue();
|
||||
chunks[i].audioSize = readValue();
|
||||
}
|
||||
|
||||
prevFrame = new uint32[width * height];
|
||||
nextFrame = new uint32[width * height];
|
||||
memset(prevFrame, 0, width * height * sizeof(uint32));
|
||||
memset(nextFrame, 0, width * height * sizeof(uint32));
|
||||
|
||||
codebook[0].blocks =
|
||||
codebook[1].blocks =
|
||||
codebook[2].blocks = NULL;
|
||||
|
||||
curVideoPos = curVideoChunk = 0;
|
||||
curAudioPos = curAudioChunk = 0;
|
||||
|
||||
prevSample.L = prevSample.R = 0;
|
||||
}
|
||||
|
||||
virtual ~Escape124() {
|
||||
delete[] chunks;
|
||||
delete[] codebook[0].blocks;
|
||||
delete[] codebook[1].blocks;
|
||||
delete[] codebook[2].blocks;
|
||||
delete[] prevFrame;
|
||||
delete[] nextFrame;
|
||||
}
|
||||
|
||||
void skipLine() {
|
||||
char c;
|
||||
while (stream->read(c) != '\n');
|
||||
}
|
||||
|
||||
int readValue() {
|
||||
char buf[255];
|
||||
for (int i = 0; i < sizeof(buf); i++) {
|
||||
char &c = buf[i];
|
||||
stream->read(c);
|
||||
if (c == ' ' || c == '.' || c == ',' || c == ';' || c == '\n') {
|
||||
if (c == ' ' || c == '.')
|
||||
skipLine();
|
||||
c = '\0';
|
||||
return atoi(buf);
|
||||
}
|
||||
}
|
||||
ASSERT(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void copySuperBlock(uint32 *dst, int dstWidth, uint32 *src, int srcWidth) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
memcpy(dst, src, 8 * sizeof(uint32));
|
||||
src += srcWidth;
|
||||
dst += dstWidth;
|
||||
}
|
||||
}
|
||||
|
||||
int getSkipCount(BitStream &bs) {
|
||||
int value;
|
||||
|
||||
value = bs.read(1);
|
||||
if (!value)
|
||||
return value;
|
||||
|
||||
value += bs.read(3);
|
||||
if (value != (1 + ((1 << 3) - 1)))
|
||||
return value;
|
||||
|
||||
value += bs.read(7);
|
||||
if (value != (1 + ((1 << 3) - 1)) + ((1 << 7) - 1))
|
||||
return value;
|
||||
|
||||
return value + bs.read(12);
|
||||
}
|
||||
|
||||
void decodeMacroBlock(BitStream &bs, MacroBlock &mb, int &cbIndex, int sbIndex) {
|
||||
int value = bs.read(1);
|
||||
if (value) {
|
||||
static const int8 trans[3][2] = { {2, 1}, {0, 2}, {1, 0} };
|
||||
value = bs.read(1);
|
||||
cbIndex = trans[cbIndex][value];
|
||||
}
|
||||
|
||||
Codebook &cb = codebook[cbIndex];
|
||||
uint32 bIndex = bs.read(cb.depth);
|
||||
|
||||
if (cbIndex == 1)
|
||||
bIndex += sbIndex << cb.depth;
|
||||
|
||||
memcpy(&mb, cb.blocks + bIndex, sizeof(mb));
|
||||
}
|
||||
|
||||
void insertMacroBlock(SuperBlock &sb, const MacroBlock &mb, int index) {
|
||||
uint32 *dst = sb.pixels + (index + (index & -4)) * 2;
|
||||
dst[0] = mb.pixels[0];
|
||||
dst[1] = mb.pixels[1];
|
||||
dst[8] = mb.pixels[2];
|
||||
dst[9] = mb.pixels[3];
|
||||
}
|
||||
|
||||
union Color32 {
|
||||
uint32 value;
|
||||
struct { uint8 r, g, b, a; };
|
||||
|
||||
Color32() {}
|
||||
|
||||
Color32(uint16 v) {
|
||||
r = (v & 0x7C00) >> 7;
|
||||
g = (v & 0x03E0) >> 2;
|
||||
b = (v & 0x001F) << 3;
|
||||
a = 255;
|
||||
}
|
||||
};
|
||||
|
||||
bool decode(uint8 *frame) {
|
||||
if (curVideoChunk >= chunksCount)
|
||||
return false;
|
||||
|
||||
if (curVideoPos >= chunks[curVideoChunk].videoSize) {
|
||||
curVideoChunk++;
|
||||
curVideoPos = 0;
|
||||
if (curVideoChunk >= chunksCount)
|
||||
return false;
|
||||
}
|
||||
stream->setPos(chunks[curVideoChunk].offset + curVideoPos);
|
||||
|
||||
uint32 flags, size;
|
||||
stream->read(flags);
|
||||
stream->read(size);
|
||||
|
||||
curVideoPos += size;
|
||||
|
||||
// skip unchanged frame
|
||||
if (!(flags & 0x114) || !(flags & 0x7800000))
|
||||
return true;
|
||||
|
||||
int sbCount = (width / 8) * (height / 8);
|
||||
|
||||
// read data into bit stream
|
||||
size -= (sizeof(flags) + sizeof(size));
|
||||
|
||||
uint8 *data = new uint8[size];
|
||||
stream->raw(data, size);
|
||||
BitStream bs(data, size);
|
||||
|
||||
// read codebook changes
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (flags & (1 << (17 + i))) {
|
||||
|
||||
Codebook &cb = codebook[i];
|
||||
|
||||
if (i == 2) {
|
||||
cb.size = bs.read(20);
|
||||
cb.depth = log2i(cb.size - 1) + 1;
|
||||
} else {
|
||||
cb.depth = bs.read(4);
|
||||
cb.size = (i == 0 ? 1 : sbCount) << cb.depth;
|
||||
}
|
||||
|
||||
delete[] cb.blocks;
|
||||
cb.blocks = new MacroBlock[cb.size];
|
||||
|
||||
for (uint32 j = 0; j < cb.size; j++) {
|
||||
uint8 mask = bs.read(4);
|
||||
Color32 cA = Color32(uint16(bs.read(15)));
|
||||
Color32 cB = Color32(uint16(bs.read(15)));
|
||||
|
||||
if (cA.value != cB.value && (mask == 6 || mask == 9) && // check for 0101 or 1010 mask
|
||||
abs(int(cA.r) - int(cB.r)) <= 8 &&
|
||||
abs(int(cA.g) - int(cB.g)) <= 8 &&
|
||||
abs(int(cA.b) - int(cB.b)) <= 8) {
|
||||
|
||||
cA.r = (int(cA.r) + int(cB.r)) / 2;
|
||||
cA.g = (int(cA.g) + int(cB.g)) / 2;
|
||||
cA.b = (int(cA.b) + int(cB.b)) / 2;
|
||||
|
||||
cB = cA;
|
||||
}
|
||||
|
||||
for (int k = 0; k < 4; k++)
|
||||
cb.blocks[j].pixels[k] = (mask & (1 << k)) ? cB.value : cA.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const uint16 maskMatrix[] = { 0x1, 0x2, 0x10, 0x20,
|
||||
0x4, 0x8, 0x40, 0x80,
|
||||
0x100, 0x200, 0x1000, 0x2000,
|
||||
0x400, 0x800, 0x4000, 0x8000};
|
||||
|
||||
SuperBlock sb;
|
||||
MacroBlock mb;
|
||||
int cbIndex = 1;
|
||||
|
||||
int skip = -1;
|
||||
for (int sbIndex = 0; sbIndex < sbCount; sbIndex++) {
|
||||
int sbLine = width / 8;
|
||||
int sbOffset = ((sbIndex / sbLine) * width + (sbIndex % sbLine)) * 8;
|
||||
uint32 *src = prevFrame + sbOffset;
|
||||
uint32 *dst = nextFrame + sbOffset;
|
||||
|
||||
uint16 multiMask = 0;
|
||||
|
||||
if (skip == -1)
|
||||
skip = getSkipCount(bs);
|
||||
|
||||
if (skip) {
|
||||
copySuperBlock(dst, width, src, width);
|
||||
} else {
|
||||
copySuperBlock(sb.pixels, 8, src, width);
|
||||
|
||||
while (!bs.read(1)) {
|
||||
decodeMacroBlock(bs, mb, cbIndex, sbIndex);
|
||||
uint16 mask = bs.read(16);
|
||||
multiMask |= mask;
|
||||
for (int i = 0; i < 16; i++)
|
||||
if (mask & maskMatrix[i])
|
||||
insertMacroBlock(sb, mb, i);
|
||||
}
|
||||
|
||||
if (!bs.read(1)) {
|
||||
uint16 invMask = bs.read(4);
|
||||
for (int i = 0; i < 4; i++)
|
||||
multiMask ^= ((invMask & (1 << i)) ? 0x0F : bs.read(4)) << (i * 4);
|
||||
for (int i = 0; i < 16; i++)
|
||||
if (multiMask & maskMatrix[i]) {
|
||||
decodeMacroBlock(bs, mb, cbIndex, sbIndex);
|
||||
insertMacroBlock(sb, mb, i);
|
||||
}
|
||||
} else
|
||||
if (flags & (1 << 16))
|
||||
while (!bs.read(1)) {
|
||||
decodeMacroBlock(bs, mb, cbIndex, sbIndex);
|
||||
insertMacroBlock(sb, mb, bs.read(4));
|
||||
}
|
||||
|
||||
copySuperBlock(dst, width, sb.pixels, 8);
|
||||
}
|
||||
|
||||
skip--;
|
||||
}
|
||||
|
||||
delete[] data;
|
||||
|
||||
memcpy(frame, nextFrame, width * height * 4);
|
||||
swap(prevFrame, nextFrame);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual int decode(Sound::Frame *frames, int count) {
|
||||
if (abs(curAudioChunk - curVideoChunk) > 1) { // sync with video chunk
|
||||
curAudioChunk = curVideoChunk;
|
||||
curAudioPos = 0;
|
||||
}
|
||||
|
||||
if (curAudioChunk >= chunksCount) {
|
||||
for (int i = 0; i < count; i++)
|
||||
frames[i].L = frames[i].R = 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
Chunk *chunk = &chunks[curAudioChunk];
|
||||
stream->setPos(chunk->offset + chunk->videoSize + curAudioPos);
|
||||
|
||||
int sampleSize = channels * (bps / 8);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
|
||||
if (curAudioChunk >= chunksCount) {
|
||||
frames[i].L = frames[i].R = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sfmt == 101) {
|
||||
ubyte2 sample;
|
||||
stream->raw(&sample, sizeof(sample));
|
||||
frames[i].L = uint16(sample.x) << 7;
|
||||
frames[i].R = uint16(sample.y) << 7;
|
||||
} else if (sfmt == 1) {
|
||||
Sound::Frame sample;
|
||||
|
||||
stream->raw(&sample, sizeof(Sound::Frame));
|
||||
|
||||
frames[i].L = (int(prevSample.L) + int(sample.L)) / 2;
|
||||
frames[i].R = (int(prevSample.R) + int(sample.R)) / 2;
|
||||
i++;
|
||||
frames[i] = prevSample = sample;
|
||||
}
|
||||
|
||||
curAudioPos += sampleSize;
|
||||
if (curAudioPos >= chunk->audioSize) {
|
||||
curAudioPos = 0;
|
||||
curAudioChunk++;
|
||||
if (curAudioChunk < chunksCount) {
|
||||
chunk = &chunks[curAudioChunk];
|
||||
stream->setPos(chunk->offset + chunk->videoSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
Decoder *decoder;
|
||||
Texture *frameTex[2];
|
||||
uint8 *frameData;
|
||||
float time, step;
|
||||
float invFPS;
|
||||
bool isPlaying;
|
||||
bool needUpdate;
|
||||
Sound::Sample *sample;
|
||||
|
||||
Video(Stream *stream) : decoder(NULL), time(0.0f), isPlaying(false) {
|
||||
frameTex[0] = frameTex[1] = NULL;
|
||||
|
||||
if (!stream) return;
|
||||
|
||||
decoder = new Escape124(stream);
|
||||
frameData = new uint8[decoder->width * decoder->height * 4];
|
||||
memset(frameData, 0, decoder->width * decoder->height * 4);
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
frameTex[i] = new Texture(decoder->width, decoder->height, FMT_RGBA, 0, frameData);
|
||||
|
||||
sample = Sound::play(decoder);
|
||||
|
||||
step = 1.0f / decoder->fps;
|
||||
time = step;
|
||||
isPlaying = true;
|
||||
}
|
||||
|
||||
~Video() {
|
||||
sample->decoder = NULL;
|
||||
sample->stop();
|
||||
delete decoder;
|
||||
delete frameTex[0];
|
||||
delete frameTex[1];
|
||||
delete[] frameData;
|
||||
}
|
||||
|
||||
void update() {
|
||||
if (!isPlaying) return;
|
||||
|
||||
time += Core::deltaTime;
|
||||
if (time < step)
|
||||
return;
|
||||
time -= step;
|
||||
|
||||
isPlaying = needUpdate = decoder->decode(frameData);
|
||||
}
|
||||
|
||||
void render() { // just update GPU texture if it's necessary
|
||||
if (!needUpdate) return;
|
||||
frameTex[0]->update(frameData);
|
||||
swap(frameTex[0], frameTex[1]);
|
||||
needUpdate = false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user