1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-11 23:54:09 +02:00

add off-axis (parallel axis asymmetric frustum perspective projection) for stereo mode

This commit is contained in:
XProger
2019-05-14 10:52:15 +03:00
parent 6dfb86f379
commit 7f7e2a7b09
16 changed files with 192 additions and 163 deletions

View File

@@ -13,6 +13,9 @@
#define CAM_SPEED_FOLLOW 12
#define CAM_SPEED_COMBAT 8
#define CAM_FOCAL_LENGTH 1536.0f
#define CAM_EYE_SEPARATION 16.0f
#define CAM_FOLLOW_ANGLE 0.0f
#define CAM_LOOK_ANGLE_XMAX ( 55.0f * DEG2RAD)
#define CAM_LOOK_ANGLE_XMIN (-75.0f * DEG2RAD)
@@ -512,7 +515,7 @@ struct Camera : ICamera {
Core::mViewInv.setPos(Core::mViewInv.getPos() + vec3(0.0f, sinf(shake * PI * 7) * shake * 48.0f, 0.0f));
if (Core::settings.detail.stereo == Core::Settings::STEREO_SBS || Core::settings.detail.stereo == Core::Settings::STEREO_ANAGLYPH)
Core::mViewInv.setPos(Core::mViewInv.getPos() + Core::mViewInv.right().xyz() * (Core::eye * 16.0f) );
Core::mViewInv.setPos(Core::mViewInv.getPos() + Core::mViewInv.right().xyz() * (Core::eye * CAM_EYE_SEPARATION) );
if (reflectPlane) {
Core::mViewInv = mat4(*reflectPlane) * Core::mViewInv;
@@ -521,17 +524,19 @@ struct Camera : ICamera {
Core::mView = Core::mViewInv.inverseOrtho();
if (Core::settings.detail.stereo == Core::Settings::STEREO_VR)
if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) {
Core::mProj = Input::hmd.proj[Core::eye == -1.0f ? 0 : 1];
else
Core::mProj = GAPI::perspective(fov, aspect, znear, zfar);
} else {
float eyeSep = (Core::eye * CAM_EYE_SEPARATION) * znear / CAM_FOCAL_LENGTH;
Core::mProj = GAPI::perspective(fov, aspect, znear, zfar, eyeSep);
}
}
Core::setViewProj(Core::mView, Core::mProj);
Core::viewPos = Core::mViewInv.getPos();
// update room for eye (with HMD offset)
if (Core::settings.detail.stereo == Core::Settings::STEREO_VR)
if (Core::settings.detail.isStereo())
level->getSector(eye.room, Core::viewPos.xyz());
frustum->pos = Core::viewPos.xyz();

View File

@@ -487,7 +487,7 @@ namespace GAPI {
return res;
}
mat4 perspective(float fov, float aspect, float znear, float zfar) {
mat4 perspective(float fov, float aspect, float znear, float zfar, float eye) {
mat4 m;
Mtx_PerspTilt((C3D_Mtx*)&m, fov * DEG2RAD, aspect, znear, zfar, false);

View File

@@ -717,13 +717,16 @@ namespace GAPI {
}
}
mat4 ortho(float l, float r, float b, float t, float znear, float zfar) {
return mat4(mat4::PROJ_ZERO_POS, l, r, b, t, znear, zfar);
mat4 m;
m.ortho(mat4::PROJ_ZERO_POS, l, r, b, t, znear, zfar);
return m;
}
mat4 perspective(float fov, float aspect, float znear, float zfar) {
return mat4(mat4::PROJ_ZERO_POS, fov, aspect, znear, zfar);
mat4 perspective(float fov, float aspect, float znear, float zfar, float eye) {
mat4 m;
m.perspective(mat4::PROJ_ZERO_POS, fov, aspect, znear, zfar, eye);
return m;
}
bool beginFrame() {

View File

@@ -540,11 +540,15 @@ namespace GAPI {
}
mat4 ortho(float l, float r, float b, float t, float znear, float zfar) {
return mat4(mat4::PROJ_ZERO_POS, l, r, b, t, znear, zfar);
mat4 m;
m.ortho(mat4::PROJ_ZERO_POS, l, r, b, t, znear, zfar);
return m;
}
mat4 perspective(float fov, float aspect, float znear, float zfar) {
return mat4(mat4::PROJ_ZERO_POS, fov, aspect, znear, zfar);
mat4 perspective(float fov, float aspect, float znear, float zfar, float eye) {
mat4 m;
m.perspective(mat4::PROJ_ZERO_POS, fov, aspect, znear, zfar, eye);
return m;
}
bool beginFrame() {

View File

@@ -1288,11 +1288,15 @@ namespace GAPI {
}
mat4 ortho(float l, float r, float b, float t, float znear, float zfar) {
return mat4(mat4::PROJ_NEG_POS, l, r, b, t, znear, zfar);
mat4 m;
m.ortho(mat4::PROJ_NEG_POS, l, r, b, t, znear, zfar);
return m;
}
mat4 perspective(float fov, float aspect, float znear, float zfar) {
return mat4(mat4::PROJ_NEG_POS, fov, aspect, znear, zfar);
mat4 perspective(float fov, float aspect, float znear, float zfar, float eye) {
mat4 m;
m.perspective(mat4::PROJ_NEG_POS, fov, aspect, znear, zfar, eye);
return m;
}
bool beginFrame() {

View File

@@ -296,11 +296,15 @@ namespace GAPI {
}
mat4 ortho(float l, float r, float b, float t, float znear, float zfar) {
return mat4(mat4::PROJ_NEG_POS, l, r, b, t, znear, zfar);
mat4 m;
m.ortho(mat4::PROJ_NEG_POS, l, r, b, t, znear, zfar);
return m;
}
mat4 perspective(float fov, float aspect, float znear, float zfar) {
return mat4(mat4::PROJ_NEG_POS, fov, aspect, znear, zfar);
mat4 perspective(float fov, float aspect, float znear, float zfar, float eye) {
mat4 m;
m.perspective(mat4::PROJ_NEG_POS, fov, aspect, znear, zfar, eye);
return m;
}
bool beginFrame() {

View File

@@ -1206,11 +1206,15 @@ namespace GAPI {
}
mat4 ortho(float l, float r, float b, float t, float znear, float zfar) {
return mat4(mat4::PROJ_ZERO_POS, l, r, b, t, znear, zfar);
mat4 m;
m.ortho(mat4::PROJ_ZERO_POS, l, r, b, t, znear, zfar);
return m;
}
mat4 perspective(float fov, float aspect, float znear, float zfar) {
return mat4(mat4::PROJ_ZERO_POS, fov, aspect, znear, zfar);
mat4 perspective(float fov, float aspect, float znear, float zfar, float eye) {
mat4 m;
m.perspective(mat4::PROJ_ZERO_POS, fov, aspect, znear, zfar, eye);
return m;
}
bool beginFrame() {

View File

@@ -6,18 +6,24 @@
#include "ui.h"
#include "savegame.h"
#define INVENTORY_MAX_ITEMS 32
#define INVENTORY_MAX_RADIUS 688.0f
#define INV_MAX_ITEMS 32
#define INV_MAX_RADIUS 688.0f
#ifdef _OS_PSP
#define INVENTORY_BG_SIZE 256
#define INV_BG_SIZE 256
#else
#define INVENTORY_BG_SIZE 512
#define INV_BG_SIZE 512
#endif
#define INVENTORY_HEIGHT 2048.0f
#define INV_HEIGHT 2048.0f
#define TITLE_LOADING 64.0f
#define LINE_HEIGHT 20.0f
#define INV_EYE_SEPARATION 16.0f
#define INV_EYE_FOCAL_LENGTH 256.0f
#define INV_ZNEAR 32.0f
#define INV_ZFAR 2048.0f
#define INV_FOV 70.0f
static const struct OptionItem *waitForKey = NULL;
struct OptionItem {
@@ -539,7 +545,7 @@ struct Inventory {
anim->setAnim(0, 0, false);
}
} *items[INVENTORY_MAX_ITEMS];
} *items[INV_MAX_ITEMS];
static void loadTitleBG(Stream *stream, void *userData) {
Inventory *inv = (Inventory*)userData;
@@ -759,7 +765,7 @@ struct Inventory {
return;
}
ASSERT(itemsCount < INVENTORY_MAX_ITEMS);
ASSERT(itemsCount < INV_MAX_ITEMS);
count = min(UNLIMITED_AMMO, count);
@@ -1346,14 +1352,14 @@ struct Inventory {
}
Texture* getBackgroundTarget() {
if (background[0] && (background[0]->origWidth != INVENTORY_BG_SIZE || background[0]->origHeight != INVENTORY_BG_SIZE)) {
if (background[0] && (background[0]->origWidth != INV_BG_SIZE || background[0]->origHeight != INV_BG_SIZE)) {
delete background[0];
background[0] = NULL;
}
for (int i = 0; i < COUNT(background); i++)
if (!background[i])
background[i] = new Texture(INVENTORY_BG_SIZE, INVENTORY_BG_SIZE, 1, FMT_RGBA, OPT_TARGET);
background[i] = new Texture(INV_BG_SIZE, INV_BG_SIZE, 1, FMT_RGBA, OPT_TARGET);
return background[0];
}
@@ -1385,14 +1391,14 @@ struct Inventory {
// vertical blur
Core::setTarget(background[1], NULL, RT_STORE_COLOR);
game->setShader(Core::passFilter, Shader::FILTER_BLUR, false, false);
Core::active.shader->setParam(uParam, vec4(0, 1.0f / INVENTORY_BG_SIZE, 0, 0));
Core::active.shader->setParam(uParam, vec4(0, 1.0f / INV_BG_SIZE, 0, 0));
background[0]->bind(sDiffuse);
game->getMesh()->renderQuad();
// horizontal blur
Core::setTarget(background[0], NULL, RT_STORE_COLOR);
game->setShader(Core::passFilter, Shader::FILTER_BLUR, false, false);
Core::active.shader->setParam(uParam, vec4(1.0f / INVENTORY_BG_SIZE, 0, 0, 0));
Core::active.shader->setParam(uParam, vec4(1.0f / INV_BG_SIZE, 0, 0, 0));
background[1]->bind(sDiffuse);
game->getMesh()->renderQuad();
@@ -1467,8 +1473,7 @@ struct Inventory {
if (item->type == TR::Entity::INV_CONTROLS || item->type == TR::Entity::INV_DETAIL)
width += 80;
float eye = UI::width * Core::eye * 0.02f;
float x = ( UI::width - width ) * 0.5f - eye;
float x = ( UI::width - width ) * 0.5f;
float y = ( UI::height - height ) * 0.5f + LINE_HEIGHT;
// background
@@ -1534,15 +1539,15 @@ struct Inventory {
return def;
}
void renderItemText(float eye, Item *item) {
void renderItemText(Item *item) {
if (item->type == TR::Entity::INV_PASSPORT && phaseChoose == 1.0f) {
//
} else {
StringID str = getItemName(item->desc.str, game->getLevel()->id, item->type);
UI::textOut(vec2(-eye, 480 - 32), str, UI::aCenter, UI::width);
UI::textOut(vec2(0, 480 - 32), str, UI::aCenter, UI::width);
}
renderItemCount(item, vec2(UI::width / 2 - 160 - eye, 480 - 96), 320);
renderItemCount(item, vec2(UI::width / 2 - 160, 480 - 96), 320);
// show health bar in inventory when selector is over medikit
if (item->type == TR::Entity::INV_MEDIKIT_BIG || item->type == TR::Entity::INV_MEDIKIT_SMALL) {
@@ -1553,12 +1558,12 @@ struct Inventory {
vec2 size = vec2(180, 10);
vec2 pos;
if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) {
pos = vec2((UI::width - size.x) * 0.5f - eye * 4.0f, 96);
pos = vec2((UI::width - size.x) * 0.5f, 96);
} else {
if (game->getLara(1) && playerIndex == 0) {
pos = vec2(32 - eye, 32);
pos = vec2(32, 32);
} else {
pos = vec2(UI::width - 32 - size.x - eye, 32);
pos = vec2(UI::width - 32 - size.x, 32);
}
}
@@ -1579,7 +1584,7 @@ struct Inventory {
case TR::Entity::INV_GAMMA :
case TR::Entity::INV_STOPWATCH :
case TR::Entity::INV_MAP :
UI::textOut(vec2(-eye, 240), STR_EMPTY, UI::aCenter, UI::width);
UI::textOut(vec2(0, 240), STR_EMPTY, UI::aCenter, UI::width);
break;
default : ;
}
@@ -1598,9 +1603,9 @@ struct Inventory {
vec2 cpos(1286, 256 + 1280 * (1.0f - phaseRing));
float ringTilt = cpos.angle();
float radius = phaseRing * INVENTORY_MAX_RADIUS * phase;
float radius = phaseRing * INV_MAX_RADIUS * phase;
float collapseAngle = phaseRing * phase * PI - PI;
float ringHeight = lerp(float(this->page), float(targetPage), quintic(phasePage)) * INVENTORY_HEIGHT;
float ringHeight = lerp(float(this->page), float(targetPage), quintic(phasePage)) * INV_HEIGHT;
float angle = getAngle(pageItemIndex[page], count);
if (phaseSelect < 1.0f)
@@ -1626,7 +1631,7 @@ struct Inventory {
rd += 296 * phaseChoose;
}
Basis b = basis * Basis(quat(vec3(0, 1, 0), PI + ia - a), vec3(sinf(a), 0, -cosf(a)) * rd - vec3(0, item->desc.page * INVENTORY_HEIGHT - rh, 0));
Basis b = basis * Basis(quat(vec3(0, 1, 0), PI + ia - a), vec3(sinf(a), 0, -cosf(a)) * rd - vec3(0, item->desc.page * INV_HEIGHT - rh, 0));
if (item->type == TR::Entity::INV_COMPASS) {
b.rotate(quat(vec3(1.0f, 0.0f, 0.0f), -phaseChoose * PI * 0.1f));
@@ -1833,7 +1838,7 @@ struct Inventory {
pos.z -= 256.0f;
if (Core::settings.detail.stereo == Core::Settings::STEREO_SBS || Core::settings.detail.stereo == Core::Settings::STEREO_ANAGLYPH)
pos.x += Core::eye * 8.0f;
pos.x += Core::eye * INV_EYE_SEPARATION;
Core::mViewInv = mat4(pos, pos + vec3(0, 0, 1), vec3(0, -1, 0));
@@ -1844,10 +1849,12 @@ struct Inventory {
} else
head.e00 = INF;
if (Core::settings.detail.stereo == Core::Settings::STEREO_VR)
if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) {
Core::mProj = Input::hmd.proj[Core::eye == -1.0f ? 0 : 1];
else
Core::mProj = GAPI::perspective(70.0f, aspect, 32.0f, 2048.0f);
} else {
float eyeSep = Core::eye * INV_EYE_SEPARATION * INV_ZNEAR / INV_EYE_FOCAL_LENGTH;
Core::mProj = GAPI::perspective(INV_FOV, aspect, INV_ZNEAR, INV_ZFAR, eyeSep);
}
Core::mView = Core::mViewInv.inverseOrtho();
Core::viewPos = Core::mViewInv.getPos();
@@ -1935,47 +1942,44 @@ struct Inventory {
static const StringID pageTitle[PAGE_MAX] = { STR_OPTION, STR_INVENTORY, STR_ITEMS, STR_SAVEGAME, STR_LEVEL_STATS };
float eye = UI::width * Core::eye * 0.01f;
if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) {
setupCamera(1.0f, true);
Core::active.shader->setParam(uViewProj, Core::mViewProj);
eye = 0.0f;
}
if (page == PAGE_SAVEGAME) {
UI::renderBar(CTEX_OPTION, vec2(-eye + UI::width / 2 - 120, 240 - 14), vec2(240, LINE_HEIGHT - 6), 1.0f, 0x802288FF, 0, 0, 0);
UI::textOut(vec2(-eye, 240), pageTitle[page], UI::aCenter, UI::width);
UI::renderBar(CTEX_OPTION, vec2(-eye - 48 * slot + UI::width / 2, 240 + 24 - 16), vec2(48, 18), 1.0f, 0xFFD8377C, 0);
UI::textOut(vec2(-eye - 48 + UI::width / 2, 240 + 24), STR_YES, UI::aCenter, 48);
UI::textOut(vec2(-eye + UI::width / 2, 240 + 24), STR_NO, UI::aCenter, 48);
UI::renderBar(CTEX_OPTION, vec2(UI::width / 2 - 120, 240 - 14), vec2(240, LINE_HEIGHT - 6), 1.0f, 0x802288FF, 0, 0, 0);
UI::textOut(vec2(0, 240), pageTitle[page], UI::aCenter, UI::width);
UI::renderBar(CTEX_OPTION, vec2(slot + UI::width / 2 - 48, 240 + 24 - 16), vec2(48, 18), 1.0f, 0xFFD8377C, 0);
UI::textOut(vec2(UI::width / 2 - 48, 240 + 24), STR_YES, UI::aCenter, 48);
UI::textOut(vec2(UI::width / 2, 240 + 24), STR_NO, UI::aCenter, 48);
return;
}
if (page == PAGE_LEVEL_STATS) {
showLevelStats(vec2(-eye, 180));
showLevelStats(vec2(0, 180));
return;
}
if (!game->getLevel()->isTitle())
UI::textOut(vec2(-eye, 32), pageTitle[page], UI::aCenter, UI::width);
UI::textOut(vec2(0, 32), pageTitle[page], UI::aCenter, UI::width);
if (canFlipPage(-1)) {
UI::textOut(vec2(16 - eye, 32), "[", UI::aLeft, UI::width);
UI::textOut(vec2(-eye, 32), "[", UI::aRight, UI::width - 20);
UI::textOut(vec2(16, 32), "[", UI::aLeft, UI::width);
UI::textOut(vec2( 0, 32), "[", UI::aRight, UI::width - 20);
}
if (canFlipPage(1)) {
UI::textOut(vec2(16 - eye, 480 - 16), "]", UI::aLeft, UI::width);
UI::textOut(vec2(-eye, 480 - 16), "]", UI::aRight, UI::width - 20);
UI::textOut(vec2(16, 480 - 16), "]", UI::aLeft, UI::width);
UI::textOut(vec2( 0, 480 - 16), "]", UI::aRight, UI::width - 20);
}
if (index == targetIndex && page == targetPage)
renderItemText(eye, items[getGlobalIndex(page, index)]);
renderItemText(items[getGlobalIndex(page, index)]);
// inventory controls help
if (page == targetPage && Input::touchTimerVis <= 0.0f) {
float dx = 32.0f - eye;
float dx = 32.0f;
char buf[64];
const char *bSelect = STR[STR_KEY_FIRST + ikEnter];
const char *bBack = STR[STR_KEY_FIRST + Core::settings.controls[playerIndex].keys[cInventory].key];

View File

@@ -22,7 +22,7 @@ const char *STR_ES[] = { ""
, "S)i"
, "No"
, "Side-By-Side"
, "Anaglyph"
, "Anaglifo"
, "Pantalla dividida"
, "VR"
, "Bajo"

View File

@@ -22,7 +22,7 @@ const char *STR_FR[] = { ""
, "Marche"
, "Arret"
, "Side-By-Side"
, "Anaglyph"
, "Anaglyphe"
, ")Ecran Divis)e"
, "VR"
, "Bas"

View File

@@ -22,7 +22,7 @@ const char *STR_IT[] = { ""
, "On"
, "Off"
, "Side-By-Side"
, "Anaglyph"
, "Anaglifo"
, "Schermo diviso"
, "VR"
, "Basso"

View File

@@ -22,7 +22,7 @@ const char *STR_PL[] = { ""
, "W}l"
, "Wy}l"
, "Side-By-Side"
, "Anaglyph"
, "Anaglif"
, "Podzielony ekran"
, "VR"
, "Niska"

View File

@@ -22,7 +22,7 @@ const char *STR_PT[] = { ""
, "Ligado"
, "Desligado"
, "Side-By-Side"
, "Anaglyph"
, "An)aglifo"
, "Tela Separada"
, "VR"
, "Baixo"

View File

@@ -590,7 +590,7 @@ struct Level : IGame {
Core::whiteTex->bind(sReflect);
Core::whiteCube->bind(sEnvironment);
Texture *shadowMap = shadow[player->camera->cameraIndex];
Texture *shadowMap = shadow[player ? player->camera->cameraIndex : 0];
if (shadowMap) shadowMap->bind(sShadow);
Core::basis.identity();
@@ -2512,7 +2512,7 @@ struct Level : IGame {
Core::mViewInv = mat4(pos, pos + dir, up);
Core::mView = Core::mViewInv.inverseOrtho();
Core::mProj = GAPI::perspective(90, 1.0f, camera->znear, camera->zfar);
Core::mProj = GAPI::perspective(90, 1.0f, camera->znear, camera->zfar, 0.0f);
Core::mViewProj = Core::mProj * Core::mView;
Core::viewPos = Core::mViewInv.offset().xyz();
@@ -2527,7 +2527,7 @@ struct Level : IGame {
Core::mViewInv = mat4(player->mainLightPos, pos, vec3(0, -1, 0));
Core::mView = Core::mViewInv.inverseOrtho();
Core::mProj = GAPI::perspective(90.0f, 1.0f, znear, zfar);
Core::mProj = GAPI::perspective(90.0f, 1.0f, znear, zfar, 0.0f);
Core::mLightProj = Core::mProj * Core::mView;
@@ -2913,34 +2913,12 @@ struct Level : IGame {
} else
vp = Viewport(vX, vY, vW, vH);
Core::eye = float(eye);
#ifdef _OS_3DS
Core::eye *= osGet3DSliderState() / 3.0f;
if (eye <= 0) {
GAPI::curTarget = GAPI::defTarget[0];
} else {
GAPI::curTarget = GAPI::defTarget[1];
}
C3D_FrameDrawOn(GAPI::curTarget);
#else
if (Core::settings.detail.stereo == Core::Settings::STEREO_ANAGLYPH || Core::settings.detail.stereo == Core::Settings::STEREO_VR) {
if (eye <= 0) {
Core::defaultTarget = Core::eyeTex[0];
} else {
Core::defaultTarget = Core::eyeTex[1];
}
Core::setTarget(Core::defaultTarget, NULL, 0);
} else {
if (Core::settings.detail.stereo == Core::Settings::STEREO_SBS) {
switch (eye) {
case -1 : vp = Viewport(vX + vp.x - vp.x / 2, vY + vp.y, vp.width / 2, vp.height); break;
case +1 : vp = Viewport(vX + vW / 2 + vp.x / 2, vY + vp.y, vp.width / 2, vp.height); break;
}
}
#endif
Core::setViewport(vp.x, vp.y, vp.width, vp.height);
@@ -3015,6 +2993,32 @@ struct Level : IGame {
Viewport oldViewport = Core::viewportDef;
GAPI::Texture *oldTarget = Core::defaultTarget;
Core::eye = float(eye);
#ifdef _OS_3DS
Core::eye *= osGet3DSliderState() / 3.0f;
if (eye <= 0) {
GAPI::curTarget = GAPI::defTarget[0];
} else {
GAPI::curTarget = GAPI::defTarget[1];
}
C3D_FrameDrawOn(GAPI::curTarget);
#else
if (Core::settings.detail.stereo == Core::Settings::STEREO_ANAGLYPH || Core::settings.detail.stereo == Core::Settings::STEREO_VR) {
if (eye <= 0) {
Core::defaultTarget = Core::eyeTex[0];
} else {
Core::defaultTarget = Core::eyeTex[1];
}
}
#endif
if (Core::lightColor[1].x > 0.5f) {
LOG("hello");
}
if (needRenderGame) {
int viewsCount = players[1] ? 2 : 1;
for (int view = 0; view < viewsCount; view++) {
player = players[view];
@@ -3029,22 +3033,28 @@ struct Level : IGame {
setup();
renderView(camera->getRoomIndex(), true, showUI);
}
} else {
Core::setTarget(NULL, NULL, RT_CLEAR_DEPTH | RT_STORE_COLOR);
}
Core::pushLights();
Core::resetLights();
if (!(level.isTitle() || inventory->titleTimer > 0.0f))
inventory->renderBackground();
renderInventoryEye(eye);
Core::popLights();
Core::defaultTarget = oldTarget;
Core::viewportDef = oldViewport;
Core::eye = oldEye;
player = players[0];
if (player) {
camera = player->camera;
}
}
void renderGame(bool showUI) {
if (Core::eye == 0.0f && Core::settings.detail.isStereo()) {
@@ -3067,6 +3077,8 @@ struct Level : IGame {
void renderUI() {
if (inventory->titleTimer > 1.0f || level.isTitle()) return;
Core::pushLights();
UI::begin();
UI::updateAspect(camera->aspect);
@@ -3089,13 +3101,11 @@ struct Level : IGame {
if (oxygen <= 0.2f) oxygen = 0.0f;
}
float eye = inventory->active ? 0.0f : UI::width * Core::eye * 0.02f;
vec2 pos;
if (Core::settings.detail.stereo == Core::Settings::STEREO_VR)
pos = vec2((UI::width - size.x) * 0.5f - eye * 4.0f, 96);
pos = vec2((UI::width - size.x) * 0.5f, 96);
else
pos = vec2(UI::width - 32 - size.x - eye, 32);
pos = vec2(UI::width - 32 - size.x, 32);
if (!player->dozy && (player->stand == Lara::STAND_ONWATER || player->stand == Character::STAND_UNDERWATER)) {
UI::renderBar(CTEX_OXYGEN, pos, size, oxygen);
@@ -3119,22 +3129,24 @@ struct Level : IGame {
UI::renderSubs();
UI::end();
Core::popLights();
}
void renderInventoryEye(int eye) {
float aspect = float(Core::width) / float(Core::height);
if (Core::settings.detail.stereo != Core::Settings::STEREO_VR)
if (Core::settings.detail.stereo == Core::Settings::STEREO_SBS) {
switch (eye) {
case -1 : Core::setViewport(Core::x, Core::y, Core::width / 2, Core::height); break;
case 0 : Core::setViewport(Core::x, Core::y, Core::width, Core::height); break;
case +1 : Core::setViewport(Core::x + Core::width / 2, Core::y, Core::width / 2, Core::height); break;
}
}
if (Core::settings.detail.stereo == Core::Settings::STEREO_SPLIT)
eye = 0;
Core::eye = float(eye);
if (Core::settings.detail.stereo == Core::Settings::STEREO_ANAGLYPH) {
Core::setViewport(Core::x, Core::y, Core::width, Core::height);
}
if (level.isTitle() || inventory->titleTimer > 0.0f)
inventory->renderBackground();
@@ -3150,25 +3162,6 @@ struct Level : IGame {
UI::end();
}
void renderInventory() {
Core::setTarget(NULL, NULL, RT_CLEAR_DEPTH | RT_STORE_COLOR);
Core::resetLights();
if (!(level.isTitle() || inventory->titleTimer > 0.0f))
inventory->renderBackground();
float oldEye = Core::eye;
if ((Core::settings.detail.stereo == Core::Settings::STEREO_SBS) || (Core::settings.detail.stereo == Core::Settings::STEREO_ANAGLYPH) || (Core::settings.detail.stereo == Core::Settings::STEREO_SPLIT && players[1])) {
renderInventoryEye(-1);
renderInventoryEye(+1);
} else
renderInventoryEye(int(Core::eye));
Core::setViewport(Core::x, Core::y, Core::width, Core::height);
Core::eye = oldEye;
}
void render() {
if (inventory->video)
return;
@@ -3192,10 +3185,7 @@ struct Level : IGame {
inventory->prepareBackground();
}
if (needRenderGame)
renderGame(true);
renderInventory();
}
};

View File

@@ -643,7 +643,7 @@ namespace UI {
delete item.animation;
pickups.remove(i);
} else {
vec2 target = vec2(w - 48.0f - Core::eye * 16.0f - (i % 4) * 96.0f, UI::height - 48.0f - (i / 4) * 96.0f);
vec2 target = vec2(w - 48.0f - (i % 4) * 96.0f, UI::height - 48.0f - (i / 4) * 96.0f);
item.pos = item.pos.lerp(target, Core::deltaTime * 5.0f);
i++;
}
@@ -710,18 +710,16 @@ namespace UI {
#ifdef _NAPI_SOCKET
textOut(vec2(16, height - 32), command, aLeft, width - 32, 255, UI::SHADE_GRAY);
#endif
float eye = UI::width * Core::eye * 0.02f;
if (hintTime > 0.0f) {
textOut(vec2(16 - eye, 32), hintStr, aLeft, width - 32, 255, UI::SHADE_GRAY);
textOut(vec2(16, 32), hintStr, aLeft, width - 32, 255, UI::SHADE_GRAY);
}
#if defined(_OS_WEB) || defined(_OS_WIN) || defined(_OS_LINUX) || defined(_OS_MAC) || defined(_OS_RPI)
if (showHelp) {
textOut(vec2(32 - eye, 32), STR_HELP_TEXT, aLeft, width - 32, 255, UI::SHADE_GRAY);
textOut(vec2(32, 32), STR_HELP_TEXT, aLeft, width - 32, 255, UI::SHADE_GRAY);
} else {
if (helpTipTime > 0.0f) {
textOut(vec2(0 - eye, height - 16), STR_HELP_PRESS, aCenter, width, 255, UI::SHADE_ORANGE);
textOut(vec2(0, height - 16), STR_HELP_PRESS, aCenter, width, 255, UI::SHADE_ORANGE);
}
}
#endif
@@ -736,12 +734,10 @@ namespace UI {
void renderSubs() {
if (!Core::settings.audio.subtitles) return;
float eye = UI::width * Core::eye * 0.02f;
if (subsTime > 0.0f) {
const char *subs = STR[subsStr] + subsPos;
textOut(vec2(16 - eye, height - 48) + vec2(1, 1), subs, aCenterV, width - 32, 255, UI::SHADE_GRAY, true);
textOut(vec2(16 - eye, height - 48), subs, aCenterV, width - 32, 255, UI::SHADE_GRAY);
textOut(vec2(16, height - 48) + vec2(1, 1), subs, aCenterV, width - 32, 255, UI::SHADE_GRAY, true);
textOut(vec2(16, height - 48), subs, aCenterV, width - 32, 255, UI::SHADE_GRAY);
}
}
@@ -762,7 +758,7 @@ namespace UI {
void setupInventoryShading(vec3 offset) {
Core::mView.identity();
Core::mProj = GAPI::perspective(1.0f, 1.0f, 1.0f, 2.0f);
Core::mProj = GAPI::perspective(1.0f, 1.0f, 1.0f, 2.0f, 0.0f);
Core::mLightProj = Core::mProj * Core::mView;
game->setShader(Core::passCompose, Shader::ENTITY, false, false);
@@ -797,6 +793,7 @@ namespace UI {
mat4 mView = Core::mView;
Core::mView.scale(vec3(0.5f));
Core::mView.translate(vec3(-Core::eye * CAM_EYE_SEPARATION, 0.0f, 0.0f));
Core::setViewProj(Core::mView, Core::mProj);
vec3 lightOffset = vec3(UI::width - 64.0f, UI::height - 64.0f, 2048.0f);

View File

@@ -646,13 +646,15 @@ struct mat4 {
e33 = 1.0f;
}
mat4(ProjRange range, float l, float r, float b, float t, float znear, float zfar) {
void ortho(ProjRange range, float l, float r, float b, float t, float znear, float zfar) {
identity();
e00 = 2.0f / (r - l);
e11 = 2.0f / (t - b);
e22 = 2.0f / (znear - zfar);
e03 = (l + r) / (l - r);
e13 = (t + b) / (b - t);
switch (range) {
case PROJ_NEG_POS :
e23 = (zfar + znear) / (znear - zfar);
@@ -663,18 +665,16 @@ struct mat4 {
}
}
mat4(ProjRange range, float fov, float aspect, float znear, float zfar) {
float k = 1.0f / tanf(fov * 0.5f * DEG2RAD);
void frustum(ProjRange range, float l, float r, float b, float t, float znear, float zfar) {
identity();
if (aspect >= 1.0f) {
e00 = k / aspect;
e11 = k;
} else {
e00 = k;
e11 = k * aspect;
}
e33 = 0.0f;
e00 = 2.0f * znear / (r - l);
e11 = 2.0f * znear / (t - b);
e02 = (r + l) / (r - l);
e12 = (t + b) / (t - b);
e32 = -1.0f;
e33 = 0.0f;
switch (range) {
case PROJ_NEG_POS :
e22 = (znear + zfar) / (znear - zfar);
@@ -687,6 +687,20 @@ struct mat4 {
}
}
void perspective(ProjRange range, float fov, float aspect, float znear, float zfar, float eye = 0.0f) {
float y = tanf(fov * 0.5f * DEG2RAD) * znear;
float x = y;
if (aspect >= 1.0f) {
x = y * aspect;
} else {
x = y;
y /= aspect;
}
frustum(range, -x - eye, x - eye, -y, y, znear, zfar);
}
mat4(const vec3 &from, const vec3 &at, const vec3 &up) {
vec3 r, u, d;
d = (from - at).normal();