diff --git a/src/cache.h b/src/cache.h index 9354b58..71d8aa0 100644 --- a/src/cache.h +++ b/src/cache.h @@ -775,13 +775,10 @@ struct WaterCache { // render mirror reflection Core::setTarget(reflect, CLEAR_ALL); + Core::validateRenderState(); Camera *camera = (Camera*)game->getCamera(); game->setupBinding(); - mat4 mProj = Core::mProj; - mat4 mView = Core::mView; - mat4 mViewInv = Core::mViewInv; - // merge visible rooms for all items int roomsList[256]; int roomsCount = 0; @@ -835,12 +832,8 @@ struct WaterCache { Core::invalidateTarget(false, true); game->setClipParams(1.0f, NO_CLIP_PLANE); - Core::mProj = mProj; - Core::mView = mView; - Core::mViewInv = mViewInv; - camera->reflectPlane = NULL; - camera->setup(false); + camera->setup(true); } void render() { diff --git a/src/camera.h b/src/camera.h index 5168703..06e8373 100644 --- a/src/camera.h +++ b/src/camera.h @@ -68,9 +68,9 @@ struct Camera : ICamera { return eye.room; } - void updateListener() { + void updateListener(const mat4 &matrix) { Sound::flipped = level->state.flags.flipped; - Sound::listener[cameraIndex].matrix = mViewInv; + Sound::listener[cameraIndex].matrix = matrix; if (cameraIndex == 0) { // reverb effect only for main player TR::Room &r = level->rooms[getRoomIndex()]; int h = (r.info.yBottom - r.info.yTop) / 1024; @@ -148,6 +148,9 @@ struct Camera : ICamera { fpHead.pos -= joint.rot * vec3(0, 48, -24); } + if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) + fpHead.rot = quat(vec3(1, 0, 0), PI); + mViewInv.identity(); mViewInv.setRot(fpHead.rot); mViewInv.setPos(fpHead.pos); @@ -155,7 +158,6 @@ struct Camera : ICamera { eye.pos = mViewInv.getPos(); eye.room = owner->getRoomIndex(); - level->getSector(eye.room, fpHead.pos); return true; } @@ -302,8 +304,6 @@ struct Camera : ICamera { if (eye.pos.y > floor) eye.pos.y = floor; if (eye.pos.y < ceiling) eye.pos.y = ceiling; - - level->getSector(eye.room, eye.pos); } virtual void update() { @@ -350,8 +350,6 @@ struct Camera : ICamera { mViewInv = mat4(eye.pos, target.pos, vec3(0, -1, 0)); } else updateFirstPerson(); - - level->getSector(eye.room, eye.pos); } else { Controller *lookAt = NULL; @@ -371,43 +369,47 @@ struct Camera : ICamera { owner->lookAt(NULL); } - vec3 advAngleOld = advAngle; + if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) { + advAngle = vec3(0.0f); + } else { + vec3 advAngleOld = advAngle; - if (Input::down[ikMouseL]) { - vec2 delta = Input::mouse.pos - Input::mouse.start.L; - advAngle.x -= delta.y * 0.01f; - advAngle.y += delta.x * 0.01f; - advAngle.y = clamp(advAngle.y, -PI, PI); - Input::mouse.start.L = Input::mouse.pos; - } - - // TODO: use player index - if (Input::state[cameraIndex][cLook]) { - float d = 2.0f * Core::deltaTime; - - advAngle.x += Input::joy[cameraIndex].L.y * d; - advAngle.y += Input::joy[cameraIndex].L.x * d; - - if (Input::state[cameraIndex][cUp]) advAngle.x -= d; - if (Input::state[cameraIndex][cDown]) advAngle.x += d; - if (Input::state[cameraIndex][cLeft]) advAngle.y += d; - if (Input::state[cameraIndex][cRight]) advAngle.y -= d; - } - - if (advAngleOld == advAngle) { - if (advTimer > 0.0f) { - advTimer = max(0.0f, advTimer - Core::deltaTime); + if (Input::down[ikMouseL]) { + vec2 delta = Input::mouse.pos - Input::mouse.start.L; + advAngle.x -= delta.y * 0.01f; + advAngle.y += delta.x * 0.01f; + advAngle.y = clamp(advAngle.y, -PI, PI); + Input::mouse.start.L = Input::mouse.pos; } - } else - advTimer = -1.0f; - if (owner->velocity != 0.0f && advTimer < 0.0f && !Input::down[ikMouseL]) - advTimer = -advTimer; + // TODO: use player index + if (Input::state[cameraIndex][cLook]) { + float d = 2.0f * Core::deltaTime; - if (advTimer == 0.0f && advAngle != 0.0f) { - float t = 10.0f * Core::deltaTime; - advAngle.x = lerp(clampAngle(advAngle.x), 0.0f, t); - advAngle.y = lerp(clampAngle(advAngle.y), 0.0f, t); + advAngle.x += Input::joy[cameraIndex].L.y * d; + advAngle.y += Input::joy[cameraIndex].L.x * d; + + if (Input::state[cameraIndex][cUp]) advAngle.x -= d; + if (Input::state[cameraIndex][cDown]) advAngle.x += d; + if (Input::state[cameraIndex][cLeft]) advAngle.y += d; + if (Input::state[cameraIndex][cRight]) advAngle.y -= d; + } + + if (advAngleOld == advAngle) { + if (advTimer > 0.0f) { + advTimer = max(0.0f, advTimer - Core::deltaTime); + } + } else + advTimer = -1.0f; + + if (owner->velocity != 0.0f && advTimer < 0.0f && !Input::down[ikMouseL]) + advTimer = -advTimer; + + if (advTimer == 0.0f && advAngle != 0.0f) { + float t = 10.0f * Core::deltaTime; + advAngle.x = lerp(clampAngle(advAngle.x), 0.0f, t); + advAngle.y = lerp(clampAngle(advAngle.y), 0.0f, t); + } } targetAngle = owner->angle + advAngle; @@ -466,13 +468,15 @@ struct Camera : ICamera { mViewInv = mat4(eye.pos, target.pos, vec3(0, -1, 0)); } else updateFirstPerson(); - - if (Core::settings.detail.vr) { - mViewInv = mViewInv * Input::hmd.eye[0]; - } } - updateListener(); - + + level->getSector(eye.room, eye.pos); + + if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) + updateListener(mViewInv * Input::hmd.head); + else + updateListener(mViewInv); + smooth = true; } @@ -480,30 +484,34 @@ struct Camera : ICamera { if (calcMatrices) { Core::mViewInv = mViewInv; - if (Core::settings.detail.vr) + if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) Core::mViewInv = Core::mViewInv * Input::hmd.eye[Core::eye == -1.0f ? 0 : 1]; + if (shake > 0.0f) + 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_ON) + Core::mViewInv.setPos(Core::mViewInv.getPos() + Core::mViewInv.right().xyz() * (Core::eye * (firstPerson ? 8.0f : 32.0f) )); + if (reflectPlane) { Core::mViewInv = mat4(*reflectPlane) * Core::mViewInv; Core::mViewInv.scale(vec3(1.0f, -1.0f, 1.0f)); } Core::mView = Core::mViewInv.inverseOrtho(); - if (shake > 0.0f) - Core::mView.translate(vec3(0.0f, sinf(shake * PI * 7) * shake * 48.0f, 0.0f)); - if (Core::settings.detail.stereo == Core::Settings::STEREO_ON) - Core::mView.translate(Core::mViewInv.right().xyz() * (-Core::eye * (firstPerson ? 8.0f : 32.0f) )); - - if (Core::settings.detail.vr) + if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) Core::mProj = Input::hmd.proj[Core::eye == -1.0f ? 0 : 1]; else Core::mProj = mat4(fov, aspect, znear, zfar); } Core::setViewProj(Core::mView, Core::mProj); + Core::viewPos = Core::mViewInv.getPos(); - Core::viewPos = Core::mViewInv.offset().xyz(); + // update room for eye (with HMD offset) + if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) + level->getSector(eye.room, Core::viewPos); frustum->pos = Core::viewPos; frustum->calcPlanes(Core::mViewProj); diff --git a/src/core.h b/src/core.h index cbbe6aa..df935bb 100644 --- a/src/core.h +++ b/src/core.h @@ -49,6 +49,8 @@ #define glProgramBinary glProgramBinaryOES #define GL_PROGRAM_BINARY_LENGTH GL_PROGRAM_BINARY_LENGTH_OES + + extern void osToggleVR(bool enable); #elif __RPI__ #define MOBILE #include @@ -277,7 +279,7 @@ struct KeySet { namespace Core { float deltaTime; int lastTime; - int width, height; + int x, y, width, height; struct Support { int maxVectors; @@ -298,12 +300,12 @@ namespace Core { #endif } support; -#define SETTINGS_VERSION 1 +#define SETTINGS_VERSION 2 #define SETTINGS_READING 0xFF struct Settings { enum Quality { LOW, MEDIUM, HIGH }; - enum Stereo { STEREO_OFF, STEREO_ON, STEREO_SPLIT }; + enum Stereo { STEREO_OFF, STEREO_ON, STEREO_SPLIT, STEREO_VR }; uint8 version; @@ -319,7 +321,6 @@ namespace Core { }; uint8 vsync; uint8 stereo; - uint8 vr; void setFilter(Quality value) { if (value > MEDIUM && !(support.maxAniso > 1)) value = MEDIUM; @@ -674,7 +675,7 @@ namespace Core { } reqTarget; struct Stats { - int dips, tris, frame, fps, fpsTime; + int dips, tris, rt, frame, fps, fpsTime; #ifdef PROFILE int tFrame; #endif @@ -682,12 +683,12 @@ namespace Core { Stats() : frame(0), fps(0), fpsTime(0) {} void start() { - dips = tris = 0; + dips = tris = rt = 0; } void stop() { if (fpsTime < Core::getTime()) { - LOG("FPS: %d DIP: %d TRI: %d\n", fps, dips, tris); + LOG("FPS: %d DIP: %d TRI: %d RT: %d\n", fps, dips, tris, rt); #ifdef PROFILE LOG("frame time: %d mcs\n", tFrame / 1000); #endif @@ -751,6 +752,7 @@ namespace Core { } void init() { + x = y = 0; #ifdef USE_INFLATE tinf_init(); #endif @@ -941,6 +943,10 @@ namespace Core { support.colorHalf ? "full" : (support.texHalf ? (support.texHalfLinear ? "linear" : "nearest") : "false")); LOG("\n"); + #ifndef _PSP + glEnable(GL_SCISSOR_TEST); + #endif + #ifdef FFP #ifdef _PSP Core::width = 480; @@ -1116,6 +1122,9 @@ namespace Core { eyeTex[0] = eyeTex[1] = NULL; + memset(&active, 0, sizeof(active)); + renderState = 0; + resetTime(); } @@ -1194,6 +1203,7 @@ namespace Core { sceGuDrawBufferList(GU_PSM_5650, target->offset, target->width); */ #else + Core::stats.rt++; if (!target) { // may be a null glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); } else { @@ -1223,6 +1233,7 @@ namespace Core { sceGuViewport(2048 + int(viewport.x), 2048 + int(viewport.y), int(viewport.z), int(viewport.w)); #else glViewport(int(viewport.x), int(viewport.y), int(viewport.z), int(viewport.w)); + glScissor(int(viewport.x), int(viewport.y), int(viewport.z), int(viewport.w)); #endif } renderState &= ~RS_VIEWPORT; @@ -1468,16 +1479,30 @@ namespace Core { #endif } - void beginFrame() { - //memset(&active, 0, sizeof(active)); - setViewport(0, 0, Core::width, Core::height); + void reset() { + #ifndef _PSP + if (Core::support.VAO) + glBindVertexArray(0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glUseProgram(0); + #endif + + memset(&active, 0, sizeof(active)); + renderState = 0; + + setViewport(Core::x, Core::y, Core::width, Core::height); viewportDef = viewport; + setCulling(cfFront); setBlending(bmAlpha); setDepthTest(true); setDepthWrite(true); setColorWrite(true, true, true, true); + validateRenderState(); + } + void beginFrame() { Core::stats.start(); } diff --git a/src/format.h b/src/format.h index 7cffaff..9cae927 100644 --- a/src/format.h +++ b/src/format.h @@ -10,6 +10,9 @@ #define MAX_TRIGGER_COMMANDS 32 #define MAX_MESHES 512 +// Lara's height in units / height in meters +#define ONE_METER (768.0f / 1.8f) + #define TR1_TYPES_START 0 #define TR2_TYPES_START 1000 #define TR3_TYPES_START 2000 diff --git a/src/game.h b/src/game.h index fc9d17a..9b4ee23 100644 --- a/src/game.h +++ b/src/game.h @@ -39,6 +39,11 @@ void loadSettings(Stream *stream, void *userData) { delete stream; } + #ifdef ANDROID + if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) + osToggleVR(true); + #endif + Core::settings.version = SETTINGS_VERSION; shaderCache = new ShaderCache(); Game::startLevel((Stream*)userData); @@ -162,22 +167,37 @@ namespace Game { return true; } - void render() { - if (Core::settings.version == SETTINGS_READING) - return; + void frameBegin() { + if (Core::settings.version == SETTINGS_READING) return; + Core::reset(); + Core::beginFrame(); + level->renderPrepare(); + } + + void frameRender() { + if (Core::settings.version == SETTINGS_READING) return; PROFILE_MARKER("RENDER"); PROFILE_TIMING(Core::stats.tFrame); - Core::beginFrame(); - level->render(); - UI::renderTouch(); + level->render(); #ifdef _DEBUG level->renderDebug(); - #endif + #endif + } + void frameEnd() { + if (Core::settings.version == SETTINGS_READING) return; + + UI::renderTouch(); Core::endFrame(); } + + void render() { + frameBegin(); + frameRender(); + frameEnd(); + } } #endif diff --git a/src/input.h b/src/input.h index 7671b38..dbbfb1d 100644 --- a/src/input.h +++ b/src/input.h @@ -32,6 +32,7 @@ namespace Input { } touch[6]; struct HMD { + mat4 head; mat4 eye[2]; mat4 proj[2]; mat4 controllers[2]; @@ -46,7 +47,10 @@ namespace Input { } void reset() { - // + eye[0].identity(); + eye[1].identity(); + proj[0].identity(); + proj[1].identity(); } } hmd; diff --git a/src/inventory.h b/src/inventory.h index d293c31..69c031b 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -129,7 +129,13 @@ static const OptionItem optDetail[] = { OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_VSYNC, SETTINGS( detail.vsync ), STR_OFF, 0, 1 ), #endif #ifndef _PSP - OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_STEREO, SETTINGS( detail.stereo ), STR_OFF, 0, 2 ), + OptionItem( OptionItem::TYPE_PARAM, STR_OPT_DETAIL_STEREO, SETTINGS( detail.stereo ), STR_OFF, 0, +#if /*defined(_WIN32) ||*/ defined(ANDROID) + 3 /* with VR option */ +#else + 2 /* without VR support */ +#endif + ), #endif OptionItem( ), OptionItem( OptionItem::TYPE_BUTTON, STR_APPLY ), @@ -218,6 +224,8 @@ struct Inventory { TR::LevelID nextLevel; // toggle result ControlKey lastKey; + mat4 head; + int slot; Core::Settings settings; @@ -423,14 +431,11 @@ struct Inventory { Core::setBasis(joints, m.mCount); - Core::setBlending(bmNone); + Core::setBlending(bmAlpha); mesh->transparent = 0; mesh->renderModel(desc.model); - - Core::setBlending(bmAlpha); mesh->transparent = 1; mesh->renderModel(desc.model); - Core::setBlending(bmAdd); Core::setDepthWrite(false); mesh->transparent = 2; @@ -675,6 +680,8 @@ struct Inventory { index = targetIndex = pageItemIndex[page]; + head = Input::hmd.head.inverseOrtho(); + //if (type == TR::Entity::INV_PASSPORT) // toggle after death // chooseItem(); } @@ -1090,6 +1097,9 @@ struct Inventory { } void prepareBackground() { + if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) + return; + #ifdef _PSP return; #endif @@ -1206,9 +1216,7 @@ struct Inventory { y = options[i].render(x, y, width, slot == i, &stg); } - void renderItemText(Item *item) { - float eye = UI::width * Core::eye * 0.01f; - + void renderItemText(float eye, Item *item) { if (item->type == TR::Entity::INV_PASSPORT && phaseChoose == 1.0f) { // } else @@ -1257,6 +1265,8 @@ struct Inventory { if (phaseSelect < 1.0f) angle = lerpAngle(angle, getAngle(targetIndex, count), hermite(phaseSelect)); + Basis basis = Basis(quat(vec3(1, 0, 0), ringTilt), vec3(0)); + int itemIndex = 0; for (int i = 0; i < itemsCount; i++) { Item *item = items[i]; @@ -1266,7 +1276,6 @@ struct Inventory { float a = getAngle(itemIndex, count) - angle - collapseAngle; float ia = item->angle; - float ra = ringTilt; float rd = radius; float rh = ringHeight; @@ -1276,10 +1285,9 @@ struct Inventory { rd += 296 * phaseChoose; } - Basis basis = Basis(quat(vec3(1, 0, 0), ra), vec3(0.0f)); - basis = 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 * INVENTORY_HEIGHT - rh, 0)); - item->render(game, basis); + item->render(game, b); itemIndex++; } @@ -1387,23 +1395,32 @@ struct Inventory { #endif Index indices[6] = { 0, 1, 2, 0, 2, 3 }; Vertex vertices[4]; - vertices[ 0].coord = short4(-32767, 32767, 0, 0); - vertices[ 1].coord = short4( 32767, 32767, 0, 0); - vertices[ 2].coord = short4( 32767, -32767, 0, 0); - vertices[ 3].coord = short4(-32767, -32767, 0, 0); - vertices[ 0].light = - vertices[ 1].light = - vertices[ 2].light = - vertices[ 3].light = ubyte4(255, 255, 255, uint8(phaseRing * 255)); - vertices[ 0].texCoord = short4( 0, 32767, 0, 0); - vertices[ 1].texCoord = short4(32767, 32767, 0, 0); - vertices[ 2].texCoord = short4(32767, 0, 0, 0); - vertices[ 3].texCoord = short4( 0, 0, 0, 0); + vertices[0].coord = short4(-32767, 32767, 0, 0); + vertices[1].coord = short4( 32767, 32767, 0, 0); + vertices[2].coord = short4( 32767, -32767, 0, 0); + vertices[3].coord = short4(-32767, -32767, 0, 0); + if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) { + vertices[0].light = + vertices[1].light = + vertices[2].light = + vertices[3].light = ubyte4(0, 0, 0, uint8(phaseRing * 255)); + } else { + vertices[0].light = + vertices[1].light = + vertices[2].light = + vertices[3].light = ubyte4(255, 255, 255, uint8(phaseRing * 255)); + } + vertices[0].texCoord = short4( 0, 32767, 0, 0); + vertices[1].texCoord = short4(32767, 32767, 0, 0); + vertices[2].texCoord = short4(32767, 0, 0, 0); + vertices[3].texCoord = short4( 0, 0, 0, 0); game->setShader(Core::passFilter, Shader::DEFAULT, false, false); + if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) + Core::whiteTex->bind(sDiffuse); // black background + else + background[0]->bind(sDiffuse); // blured grayscale image - // blured grayscale image - background[0]->bind(sDiffuse); Core::setBlending(phaseRing < 1.0f ? bmAlpha : bmNone); game->getMesh()->renderBuffer(indices, COUNT(indices), vertices, COUNT(vertices)); } @@ -1414,17 +1431,57 @@ struct Inventory { Core::setDepthTest(false); - if (background[0]) { - if (background[1]) + if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) { + if (game->getLevel()->isTitle()) { + if (background[0]) { + renderTitleBG(); + } + } else renderGameBG(); - else - renderTitleBG(); + } else { + if (background[0]) { + if (background[1]) + renderGameBG(); + else + renderTitleBG(); + } } Core::setBlending(bmAlpha); Core::setDepthTest(true); } + void setupCamera(float aspect, bool ui = false) { + vec3 pos = vec3(0, 0, -1286); + + if (ui) { + pos.x += UI::width * 0.5f; + pos.y += UI::height * 0.5f; + pos.z += 1024.0f; + } + + if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) + pos.z -= 256.0f; + + if (Core::settings.detail.stereo == Core::Settings::STEREO_ON) + pos.x += Core::eye * 8.0f; + + Core::mViewInv = mat4(pos, pos + vec3(0, 0, 1), vec3(0, -1, 0)); + + if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) + Core::mViewInv = Core::mViewInv * head * Input::hmd.eye[Core::eye == -1.0f ? 0 : 1]; + + if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) + Core::mProj = Input::hmd.proj[Core::eye == -1.0f ? 0 : 1]; + else + Core::mProj = mat4(70.0f, aspect, 32.0f, 2048.0f); + + Core::mView = Core::mViewInv.inverseOrtho(); + Core::viewPos = Core::mViewInv.getPos(); + + Core::setViewProj(Core::mView, Core::mProj); + } + void render(float aspect) { if (!isActive() && titleTimer == 0.0f) return; @@ -1437,17 +1494,7 @@ struct Inventory { Core::mLightProj.identity(); - Core::mView.identity(); - Core::mView.translate(vec3(-Core::eye * 8.0f, 0, -1286)); // y = -96 in title - - Core::mView.up() *= -1.0f; - Core::mView.dir() *= -1.0f; - Core::mViewInv = Core::mView.inverseOrtho(); - - Core::mProj = mat4(70.0f, aspect, 32.0f, 2048.0f); - Core::setViewProj(Core::mView, Core::mProj); - - Core::viewPos = Core::mViewInv.getPos(); + setupCamera(aspect); Core::whiteTex->bind(sShadow); game->setShader(Core::passCompose, Shader::ENTITY, false, false); @@ -1477,6 +1524,12 @@ struct Inventory { 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 (!game->getLevel()->isTitle()) UI::textOut(vec2(-eye, 32), pageTitle[page], UI::aCenter, UI::width); @@ -1490,17 +1543,19 @@ struct Inventory { UI::textOut(vec2(-eye, 480 - 16), "]", UI::aRight, UI::width - 20); } - if (index == targetIndex) - renderItemText(items[getGlobalIndex(page, index)]); + if (index == targetIndex && page == targetPage) + renderItemText(eye, items[getGlobalIndex(page, index)]); // inventory controls help - float dx = 32.0f - eye; - char buf[64]; - sprintf(buf, STR[STR_HELP_SELECT], STR[STR_KEY_FIRST + ikEnter] ); - UI::textOut(vec2(dx, 480 - 64), buf, UI::aLeft, UI::width); - if (chosen) { - sprintf(buf, STR[STR_HELP_BACK], STR[STR_KEY_FIRST + Core::settings.controls[playerIndex].keys[ cInventory ].key] ); - UI::textOut(vec2(0, 480 - 64), buf, UI::aRight, UI::width - dx); + if (page == targetPage) { + float dx = 32.0f - eye; + char buf[64]; + sprintf(buf, STR[STR_HELP_SELECT], STR[STR_KEY_FIRST + ikEnter] ); + UI::textOut(vec2(dx, 480 - 64), buf, UI::aLeft, UI::width); + if (chosen) { + sprintf(buf, STR[STR_HELP_BACK], STR[STR_KEY_FIRST + Core::settings.controls[playerIndex].keys[ cInventory ].key] ); + UI::textOut(vec2(0, 480 - 64), buf, UI::aRight, UI::width - dx); + } } } }; diff --git a/src/lara.h b/src/lara.h index 74cc1dd..63a0b44 100644 --- a/src/lara.h +++ b/src/lara.h @@ -183,8 +183,8 @@ struct Lara : Character { #define LARA_RGUN_JOINT 10 #define LARA_LGUN_JOINT 13 - #define LARA_RGUN_OFFSET vec3( 10, -50, 0) - #define LARA_LGUN_OFFSET vec3(-10, -50, 0) + #define LARA_RGUN_OFFSET vec3(-10, -50, 0) + #define LARA_LGUN_OFFSET vec3( 10, -50, 0) enum { BODY_HIP = 0x0001, @@ -474,6 +474,7 @@ struct Lara : Character { if (level->extra.braid > -1) braid = new Braid(this, (level->version & (TR::VER_TR2 | TR::VER_TR3)) ? vec3(-2.0f, -16.0f, -48.0f) : vec3(-4.0f, 24.0f, -48.0f)); + reset(15, vec3(70067, -256, 29104), -0.68f); // level 2 (pool) #ifdef _DEBUG //reset(14, vec3(40448, 3584, 60928), PI * 0.5f, STAND_ONWATER); // gym (pool) //reset(0, vec3(74858, 3072, 20795), 0); // level 1 (dart) @@ -941,7 +942,8 @@ struct Lara : Character { shots++; - game->addMuzzleFlash(this, i ? LARA_LGUN_JOINT : LARA_RGUN_JOINT, i ? LARA_LGUN_OFFSET : LARA_RGUN_OFFSET, 1 + camera->cameraIndex); + if (wpnCurrent != Weapon::SHOTGUN) + game->addMuzzleFlash(this, i ? LARA_LGUN_JOINT : LARA_RGUN_JOINT, i ? LARA_LGUN_OFFSET : LARA_RGUN_OFFSET, 1 + camera->cameraIndex); // TODO: use new trace code int joint = wpnCurrent == Weapon::SHOTGUN ? 8 : (i ? 11 : 8); @@ -1151,6 +1153,10 @@ struct Lara : Character { return vec3(atan2f(dir.y, sqrtf(dir.x * dir.x + dir.z * dir.z)) - angle.x, atan2f(dir.x, dir.z) - angle.y + PI, 0.0f); } + vec3 getAngleAbs(const vec3 &dir) { + return vec3(-atan2f(dir.y, sqrtf(dir.x * dir.x + dir.z * dir.z)), -atan2f(dir.x, dir.z), 0.0f); + } + virtual void lookAt(Controller *target) { if (health <= 0.0f) return; @@ -1439,10 +1445,8 @@ struct Lara : Character { flags.invisible = true; if (!environment) environment = new Texture(256, 256, Texture::RGBA, Texture::CUBEMAP | Texture::MIPMAPS); - Core::beginFrame(); game->renderEnvironment(getRoomIndex(), pos - vec3(0.0f, 384.0f, 0.0f), &environment, 0, Core::passCompose); environment->generateMipMap(); - Core::endFrame(); flags.invisible = false; } @@ -2637,28 +2641,51 @@ struct Lara : Character { Input::Joystick &joy = Input::joy[Core::settings.controls[pid].joyIndex]; - if ((state == STATE_STOP || state == STATE_SURF_TREAD || state == STATE_HANG) && fabsf(joy.L.x) < 0.5f && fabsf(joy.L.y) < 0.5f) - return input; + if (!((state == STATE_STOP || state == STATE_SURF_TREAD || state == STATE_HANG) && fabsf(joy.L.x) < 0.5f && fabsf(joy.L.y) < 0.5f)) { + bool moving = state == STATE_RUN || state == STATE_WALK || state == STATE_BACK || state == STATE_FAST_BACK || state == STATE_SURF_SWIM || state == STATE_SURF_BACK || state == STATE_FORWARD_JUMP; - bool moving = state == STATE_RUN || state == STATE_WALK || state == STATE_BACK || state == STATE_FAST_BACK || state == STATE_SURF_SWIM || state == STATE_SURF_BACK || state == STATE_FORWARD_JUMP; + if (!moving) { + if (fabsf(joy.L.x) < fabsf(joy.L.y)) + joy.L.x = 0.0f; + else + joy.L.y = 0.0f; + } - if (!moving) { - if (fabsf(joy.L.x) < fabsf(joy.L.y)) - joy.L.x = 0.0f; - else - joy.L.y = 0.0f; + if (joy.L.x != 0.0f) { + input |= (joy.L.x < 0.0f) ? LEFT : RIGHT; + if (moving || stand == STAND_UNDERWATER || stand == STAND_ONWATER) + rotFactor.y = min(fabsf(joy.L.x) / 0.9f, 1.0f); + } + + if (joy.L.y != 0.0f) { + input |= (joy.L.y < 0.0f) ? FORTH : BACK; + if (stand == STAND_UNDERWATER) + rotFactor.x = min(fabsf(joy.L.y) / 0.9f, 1.0f); + } } - if (joy.L.x != 0.0f) { - input |= (joy.L.x < 0.0f) ? LEFT : RIGHT; - if (moving || stand == STAND_UNDERWATER || stand == STAND_ONWATER) - rotFactor.y = min(fabsf(joy.L.x) / 0.9f, 1.0f); - } + // VR control + if (Core::settings.detail.stereo == Core::Settings::STEREO_VR && camera->firstPerson && canFreeRotate()) { + if (!(input & WALK)) { + input &= ~(LEFT | RIGHT); + } - if (joy.L.y != 0.0f) { - input |= (joy.L.y < 0.0f) ? FORTH : BACK; - if (stand == STAND_UNDERWATER) - rotFactor.x = min(fabsf(joy.L.y) / 0.9f, 1.0f); + vec3 ang = getAngleAbs(Input::hmd.head.dir().xyz()); + + angle.y = ang.y; +// rotFactor = vec2(1.0f); +// ang.y = shortAngle(angle.y, ang.y); +// if (fabsf(ang.y) > 5 * DEG2RAD) +// input |= ang.y < 0.0f ? LEFT : RIGHT; + + if (stand == STAND_UNDERWATER) { + input &= ~(FORTH | BACK); + + angle.x = ang.x; +// ang.x = shortAngle(angle.x, ang.x); +// if (fabsf(ang.x) > 5 * DEG2RAD) +// input |= ang.x < 0.0f ? FORTH : BACK; + } } return input; @@ -2669,6 +2696,7 @@ struct Lara : Character { || state == STATE_DEATH || state == STATE_UNDERWATER_DEATH || state == STATE_HANG + || state == STATE_HANG_UP || state == STATE_HANG_LEFT || state == STATE_HANG_RIGHT || state == STATE_PUSH_BLOCK @@ -2682,9 +2710,12 @@ struct Lara : Character { || state == STATE_SPECIAL || state == STATE_REACH || state == STATE_SWAN_DIVE + || state == STATE_FAST_DIVE || state == STATE_HANDSTAND || state == STATE_ROLL_1 || state == STATE_ROLL_2 + || state == STATE_MIDAS_USE + || state == STATE_MIDAS_DEATH // make me sick! // || state == STATE_BACK_JUMP // || state == STATE_LEFT_JUMP @@ -2694,6 +2725,10 @@ struct Lara : Character { || animation.index == ANIM_CLIMB_JUMP; } + bool canFreeRotate() { + return !(useHeadAnimation() || state == STATE_SLIDE || state == STATE_SLIDE_BACK); + } + virtual void doCustomCommand(int curFrame, int prevFrame) { switch (state) { case STATE_PICK_UP : { @@ -2765,7 +2800,7 @@ struct Lara : Character { else hit(Core::deltaTime * 150.0f); } else - if (oxygen < LARA_MAX_OXYGEN) + if (oxygen < LARA_MAX_OXYGEN && health > 0.0f) oxygen = min(LARA_MAX_OXYGEN, oxygen += Core::deltaTime * 10.0f); usedKey = TR::Entity::LARA; @@ -2811,9 +2846,12 @@ struct Lara : Character { w *= TURN_WATER_FAST; else if (state == STATE_TREAD || state == STATE_SURF_TREAD || state == STATE_SURF_SWIM || state == STATE_SURF_BACK) w *= TURN_WATER_FAST; - else if (state == STATE_RUN) - w *= sign(w) != sign(tilt) ? 0.0f : w * TURN_FAST * tilt / LARA_TILT_MAX; - else if (state == STATE_FAST_TURN) + else if (state == STATE_RUN) { + if (Core::settings.detail.stereo == Core::Settings::STEREO_VR) + w *= TURN_FAST; + else + w *= sign(w) != sign(tilt) ? 0.0f : w * TURN_FAST * tilt / LARA_TILT_MAX; + } else if (state == STATE_FAST_TURN) w *= TURN_FAST; else if (state == STATE_FAST_BACK) w *= TURN_FAST_BACK; @@ -2928,7 +2966,8 @@ struct Lara : Character { if (stand == STAND_UNDERWATER) vTilt *= 2.0f; vTilt *= rotFactor.y; - updateTilt(state == STATE_RUN || stand == STAND_UNDERWATER, vTilt.x, vTilt.y); + bool VR = (Core::settings.detail.stereo == Core::Settings::STEREO_VR) && camera->firstPerson; + updateTilt((state == STATE_RUN || stand == STAND_UNDERWATER) && !VR, vTilt.x, vTilt.y); collisionOffset = vec3(0.0f); diff --git a/src/level.h b/src/level.h index b4f0ac9..64b7a83 100644 --- a/src/level.h +++ b/src/level.h @@ -235,6 +235,11 @@ struct Level : IGame { bool redraw = settings.detail.stereo != Core::settings.detail.stereo; + #ifdef ANDROID + if ((settings.detail.stereo == Core::Settings::STEREO_VR) ^ (Core::settings.detail.stereo == Core::Settings::STEREO_VR)) + osToggleVR(settings.detail.stereo == Core::Settings::STEREO_VR); + #endif + Core::settings = settings; Stream::cacheWrite("settings", (char*)&settings, sizeof(settings)); @@ -611,10 +616,10 @@ struct Level : IGame { track = TR::LEVEL_INFO[level.id].ambientTrack; if (level.state.flags.track == track) { - if (sndTrack) { - sndTrack->replay(); - sndTrack->setVolume(1.0f, 0.2f); - } + // if (sndTrack) { + // sndTrack->replay(); + // sndTrack->setVolume(1.0f, 0.2f); + // } return; } @@ -708,6 +713,10 @@ struct Level : IGame { camera->doCutscene(lara->pos, lara->angle.y); } */ + + Core::setTarget(NULL); + Core::validateRenderState(); + Core::resetTime(); } @@ -1206,7 +1215,6 @@ struct Level : IGame { } void initReflections() { - Core::beginFrame(); for (int i = 0; i < level.entitiesBaseCount; i++) { TR::Entity &e = level.entities[i]; if (e.type == TR::Entity::CRYSTAL) { @@ -1215,7 +1223,6 @@ struct Level : IGame { c->environment->generateMipMap(); } } - Core::endFrame(); } void setMainLight(Controller *controller) { @@ -1788,6 +1795,24 @@ struct Level : IGame { } } + void renderOpaque(int *roomsList, int roomsCount) { + renderRooms(roomsList, roomsCount, 0); + renderEntities(0); + } + + void renderTransparent(int *roomsList, int roomsCount) { + renderRooms(roomsList, roomsCount, 1); + renderEntities(1); + } + + void renderAdditive(int *roomsList, int roomsCount) { + vec4 oldFog = Core::fogParams; + Core::fogParams = FOG_BLACK; // don't apply fog for additive + renderRooms(roomsList, roomsCount, 2); + renderEntities(2); + Core::fogParams = oldFog; + } + virtual void renderView(int roomIndex, bool water, bool showUI, int roomsCount = 0, int *roomsList = NULL) { PROFILE_MARKER("VIEW"); @@ -1849,7 +1874,10 @@ struct Level : IGame { } if (water) { - Core::setTarget(NULL, (Core::settings.detail.stereo == Core::Settings::STEREO_OFF && players[1] == NULL) ? CLEAR_ALL : 0); // render to back buffer + //bool clear = Core::settings.detail.stereo == Core::Settings::STEREO_VR; + //clear |= Core::settings.detail.stereo == Core::Settings::STEREO_OFF && players[1] == NULL; + bool clear = true; + Core::setTarget(NULL, clear ? CLEAR_ALL : 0); // render to back buffer setupBinding(); } @@ -1857,12 +1885,11 @@ struct Level : IGame { updateLighting(); - // opaque pass - renderRooms(roomsList, roomsCount, 0); - renderEntities(0); - // alpha blending pass - renderRooms(roomsList, roomsCount, 1); - renderEntities(1); + renderOpaque(roomsList, roomsCount); + renderTransparent(roomsList, roomsCount); + + if (camera->isUnderwater()) + renderAdditive(roomsList, roomsCount); Core::setBlending(bmNone); if (water && waterCache && waterCache->visible) { @@ -1875,13 +1902,9 @@ struct Level : IGame { setupBinding(); } - // additive blending pass - vec4 oldFog = Core::fogParams; - Core::fogParams = FOG_BLACK; // don't apply fog for additive - renderRooms(roomsList, roomsCount, 2); - renderEntities(2); - Core::fogParams = oldFog; - + if (!camera->isUnderwater()) + renderAdditive(roomsList, roomsCount); + Core::setBlending(bmNone); if (showUI) { @@ -1931,7 +1954,9 @@ struct Level : IGame { void renderShadows(int roomIndex) { PROFILE_MARKER("PASS_SHADOW"); + float oldEye = Core::eye; Core::eye = 0.0f; + Core::pass = Core::passShadow; shadow->unbind(sShadow); bool colorShadow = shadow->format == Texture::RGBA ? true : false; @@ -1948,13 +1973,15 @@ struct Level : IGame { Core::setCulling(cfFront); if (colorShadow) Core::setClearColor(vec4(0.0f)); + + Core::eye = oldEye; } #ifdef _DEBUG void renderDebug() { if (level.isTitle() || inventory.titleTimer > 1.0f) return; - Core::setViewport(0, 0, Core::width, Core::height); + Core::setViewport(Core::x, Core::y, Core::width, Core::height); camera->setup(true); if (Input::down[ikF]) { @@ -2156,28 +2183,34 @@ struct Level : IGame { #endif void setViewport(int view, int eye, bool isUI) { + float vX = float(Core::x); + float vY = float(Core::y); float vW = float(Core::width); float vH = float(Core::height); float aspect = vW / vH; if (Core::defaultTarget) { + vX = 0.0f; + vY = 0.0f; vW = float(Core::defaultTarget->width); vH = float(Core::defaultTarget->height); } vec4 &vp = Core::viewportDef; - + if (players[1] != NULL) { - vp = vec4(vW * 0.5f * view, 0.0f, vW * 0.5f, vH); + vp = vec4(vX + vW * 0.5f * view, vY, vW * 0.5f, vH); if (Core::settings.detail.stereo != Core::Settings::STEREO_SPLIT) aspect *= 0.5f; } else - vp = vec4(0.0f, 0.0f, vW, vH); - - switch (eye) { - case -1 : vp = vec4(vp.x - vp.x * 0.5f, vp.y, vp.z * 0.5f, vp.w); break; - case +1 : vp = vec4(vW * 0.5f + vp.x / 2.0f, vp.y, vp.z * 0.5f, vp.w); break; + vp = vec4(vX, vY, vW, vH); + + if (Core::settings.detail.stereo != Core::Settings::STEREO_VR) { + switch (eye) { + case -1 : vp = vec4(vX + vp.x - vp.x * 0.5f, vY + vp.y, vp.z * 0.5f, vp.w); break; + case +1 : vp = vec4(vX + vW * 0.5f + vp.x / 2.0f, vY + vp.y, vp.z * 0.5f, vp.w); break; + } } Core::eye = float(eye); @@ -2188,15 +2221,23 @@ struct Level : IGame { camera->aspect = aspect; } - void renderGame(bool showUI) { + void renderPrepare() { Core::invalidateTarget(true, true); if (ambientCache) ambientCache->processQueue(); + if (shadow) + renderShadows(player->getRoomIndex()); + + Core::setTarget(NULL, CLEAR_ALL); + Core::validateRenderState(); + } + + void renderGame(bool showUI) { //if (Core::settings.detail.stereo || Core::settings.detail.splitscreen) { - Core::setTarget(NULL, CLEAR_ALL); - Core::validateRenderState(); + // Core::setTarget(NULL, CLEAR_ALL); + // Core::validateRenderState(); //} /* // catsuit test @@ -2215,6 +2256,8 @@ struct Level : IGame { mesh->renderQuad(); return; */ + vec4 vp = Core::viewportDef; + int viewsCount = players[1] ? 2 : 1; for (int view = 0; view < viewsCount; view++) { player = players[view]; @@ -2224,12 +2267,12 @@ struct Level : IGame { params->clipSign = 1.0f; params->waterHeight = params->clipHeight; - if (shadow) + if (shadow && view == 1) renderShadows(player->getRoomIndex()); if (shadow) shadow->bind(sShadow); Core::pass = Core::passCompose; - + /* if (view == 0 && Input::hmd.ready) { Core::settings.detail.vr = true; @@ -2255,9 +2298,11 @@ struct Level : IGame { Core::defaultTarget = oldTarget; Core::setTarget(NULL, CLEAR_ALL); Core::viewportDef = vp; - } - + } + */ if (Core::settings.detail.stereo == Core::Settings::STEREO_ON) { // left/right SBS stereo + float oldEye = Core::eye; + setViewport(view, -1, false); setup(); renderView(camera->getRoomIndex(), true, showUI); @@ -2265,15 +2310,16 @@ struct Level : IGame { setViewport(view, 1, false); setup(); renderView(camera->getRoomIndex(), true, showUI); + + Core::eye = oldEye; } else { - setViewport(view, 0, false); + setViewport(view, int(Core::eye), false); setup(); renderView(camera->getRoomIndex(), true, showUI); } } - Core::eye = 0; - Core::viewportDef = vec4(0.0f, 0.0f, float(Core::width), float(Core::height)); + Core::viewportDef = vp; player = players[0]; camera = player->camera; @@ -2301,18 +2347,27 @@ struct Level : IGame { 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); + else + pos = vec2(UI::width - 32 - size.x - eye, 32); + + if (!player->dozy && (player->stand == Lara::STAND_ONWATER || player->stand == Character::STAND_UNDERWATER)) { + UI::renderBar(UI::BAR_OXYGEN, pos, size, oxygen); + pos.y += 16.0f; + } + if ((!inventory.active && (!player->emptyHands() || player->damageTime > 0.0f || health <= 0.2f))) { - UI::renderBar(UI::BAR_HEALTH, vec2(UI::width - 32 - size.x - eye, 32), size, health); + 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 (index > -1) - inventory.renderItemCount(inventory.items[index], vec2(UI::width - 32 - size.x - eye, 64), size.x); + inventory.renderItemCount(inventory.items[index], pos, size.x); } } - - if (!player->dozy && (player->stand == Lara::STAND_ONWATER || player->stand == Character::STAND_UNDERWATER)) - UI::renderBar(UI::BAR_OXYGEN, vec2(32 - eye, 32), size, oxygen); } if (!level.isTitle()) @@ -2324,11 +2379,12 @@ struct Level : IGame { void renderInventoryEye(int eye) { float aspect = float(Core::width) / float(Core::height); - switch (eye) { - case -1 : Core::setViewport(0, 0, Core::width / 2, Core::height); break; - case 0 : Core::setViewport(0, 0, Core::width, Core::height); break; - case +1 : Core::setViewport(Core::width / 2, 0, Core::width / 2, Core::height); break; - } + if (Core::settings.detail.stereo != Core::Settings::STEREO_VR) + 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; @@ -2351,14 +2407,16 @@ struct Level : IGame { if (!(level.isTitle() || inventory.titleTimer > 0.0f)) inventory.renderBackground(); + float oldEye = Core::eye; + if ((Core::settings.detail.stereo == Core::Settings::STEREO_ON) || (Core::settings.detail.stereo == Core::Settings::STEREO_SPLIT && players[1])) { renderInventoryEye(-1); renderInventoryEye(+1); } else - renderInventoryEye(0); + renderInventoryEye(int(Core::eye)); - Core::setViewport(0, 0, Core::width, Core::height); - Core::eye = 0.0f; + Core::setViewport(Core::x, Core::y, Core::width, Core::height); + Core::eye = oldEye; } void render() { diff --git a/src/mesh.h b/src/mesh.h index 453e35c..6e4ae0f 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -200,6 +200,7 @@ struct Mesh { range.bind(VAO); bind(true); range.setup(vBuffer); + unbind(); } else #endif range.aIndex = -1; @@ -217,12 +218,21 @@ struct Mesh { #endif } + static void unbind() { + if (Core::support.VAO) + glBindVertexArray(Core::active.VAO = 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Core::active.iBuffer = 0); + glBindBuffer(GL_ARRAY_BUFFER, Core::active.vBuffer = 0); + } + void render(const MeshRange &range) { + if (range.aIndex == -1) + bind(); + #ifndef _PSP range.bind(VAO); #endif - bind(); if (range.aIndex == -1) range.setup(vBuffer); @@ -707,7 +717,7 @@ struct MeshBuilder { #ifdef GENERATE_WATER_PLANE plane.vStart = vStartCommon; plane.iStart = iCount; - plane.iCount = PLANE_DETAIL * 2 * PLANE_DETAIL * 2 * (2 * 3); + plane.iCount = SQR(PLANE_DETAIL * 2) * 6; baseIdx = vCount - vStartCommon; for (int16 j = -PLANE_DETAIL; j <= PLANE_DETAIL; j++) diff --git a/src/platform/android/app/build.gradle b/src/platform/android/app/build.gradle index 95dabc5..cfe0ce6 100644 --- a/src/platform/android/app/build.gradle +++ b/src/platform/android/app/build.gradle @@ -1,11 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" + compileSdkVersion 26 + buildToolsVersion '26.0.2' + defaultConfig { applicationId "com.xproger.openlara" - minSdkVersion 18 + minSdkVersion 19 targetSdkVersion 18 versionCode 1 versionName "0.1" @@ -36,4 +37,6 @@ android { dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') + + compile 'com.google.vr:sdk-base:1.80.0' } diff --git a/src/platform/android/app/src/main/AndroidManifest.xml b/src/platform/android/app/src/main/AndroidManifest.xml index 6900fb7..e4f9c35 100644 --- a/src/platform/android/app/src/main/AndroidManifest.xml +++ b/src/platform/android/app/src/main/AndroidManifest.xml @@ -3,7 +3,7 @@ android:versionCode="1" android:versionName="0.1" > - + diff --git a/src/platform/android/app/src/main/cpp/main.cpp b/src/platform/android/app/src/main/cpp/main.cpp index 6ce1b75..7d33b3b 100644 --- a/src/platform/android/app/src/main/cpp/main.cpp +++ b/src/platform/android/app/src/main/cpp/main.cpp @@ -7,6 +7,8 @@ #include "game.h" +JavaVM *jvm; + #define JNI_METHOD(return_type, method_name) \ JNIEXPORT return_type JNICALL \ Java_org_xproger_openlara_Wrapper_##method_name @@ -29,12 +31,24 @@ void osJoyVibrate(int index, float L, float R) { // } +void osToggleVR(bool enable) { + JNIEnv *jniEnv; + jvm->AttachCurrentThread(&jniEnv, NULL); + + jboolean v = enable; + jclass c = jniEnv->FindClass("org/xproger/openlara/MainActivity"); + jmethodID m = jniEnv->GetStaticMethodID(c, "toggleVR", "(Z)V"); + jniEnv->CallStaticVoidMethod(c, m, v); +} + extern "C" { char Stream::cacheDir[255]; char Stream::contentDir[255]; JNI_METHOD(void, nativeInit)(JNIEnv* env, jobject obj, jstring contentDir, jstring cacheDir) { + env->GetJavaVM(&jvm); + timeval t; gettimeofday(&t, NULL); startTime = t.tv_sec; @@ -51,7 +65,8 @@ JNI_METHOD(void, nativeInit)(JNIEnv* env, jobject obj, jstring contentDir, jstri strcat(Stream::cacheDir, str); env->ReleaseStringUTFChars(cacheDir, str); - Game::init(); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&Core::defaultFBO); + Game::init("level/1/LEVEL2.PSX"); } JNI_METHOD(void, nativeFree)(JNIEnv* env) { @@ -63,14 +78,29 @@ JNI_METHOD(void, nativeReset)(JNIEnv* env) { } JNI_METHOD(void, nativeUpdate)(JNIEnv* env) { + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&Core::defaultFBO); + Game::update(); } -JNI_METHOD(void, nativeRender)(JNIEnv* env) { - Game::render(); +JNI_METHOD(void, nativeFrameBegin)(JNIEnv* env) { + Game::frameBegin(); } -JNI_METHOD(void, nativeResize)(JNIEnv* env, jobject obj, jint w, jint h) { +JNI_METHOD(void, nativeFrameEnd)(JNIEnv* env) { + Game::frameEnd(); + Core::reset(); +} + +JNI_METHOD(void, nativeFrameRender)(JNIEnv* env) { + Game::frameRender(); + Core::reset(); +} + +JNI_METHOD(void, nativeResize)(JNIEnv* env, jobject obj, jint x, jint y, jint w, jint h) { + Core::viewportDef = vec4(float(x), float(y), float(w), float(h)); + Core::x = x; + Core::y = y; Core::width = w; Core::height = h; } @@ -101,7 +131,6 @@ JNI_METHOD(void, nativeTouch)(JNIEnv* env, jobject obj, jint id, jint state, jfl case -4 : Input::setJoyPos(id, jkR, vec2(DeadZone(x), DeadZone(y))); break; default : { int btn = int(x); - LOG("key %d = %d\n", btn, state); if (btn < 0) Input::setJoyDown(id, JoyKey(jkNone - btn), state != -1); else @@ -111,16 +140,6 @@ JNI_METHOD(void, nativeTouch)(JNIEnv* env, jobject obj, jint id, jint state, jfl return; } - if (id == -100) { - /* - switch (state) { - case 0 : Input::head.basis.rot.x = x; Input::head.basis.rot.y = y; break; - case 1 : Input::head.basis.rot.z = x; Input::head.basis.rot.w = y; Input::head.set(); break; - } - */ - return; - } - // touch InputKey key = Input::getTouch(id); if (key == ikNone) return; @@ -129,6 +148,37 @@ JNI_METHOD(void, nativeTouch)(JNIEnv* env, jobject obj, jint id, jint state, jfl Input::setDown(key, state == 2); } +JNI_METHOD(void, nativeSetVR)(JNIEnv* env, jobject obj, jboolean enabled) { + Core::Settings settings = Core::settings; + settings.detail.stereo = enabled ? Core::Settings::STEREO_VR : Core::Settings::STEREO_OFF; + Game::level->applySettings(settings); +} + +JNI_METHOD(void, nativeSetHead)(JNIEnv* env, jobject obj, jfloatArray head) { + jfloat *mHead = env->GetFloatArrayElements(head, NULL); + memcpy(&Input::hmd.head, mHead, sizeof(float) * 16); + Input::hmd.head = Input::hmd.head.inverseOrtho(); + env->ReleaseFloatArrayElements(head, mHead, 0); +} + +JNI_METHOD(void, nativeSetEye)(JNIEnv* env, jobject obj, jint eye, jfloatArray proj, jfloatArray view) { + Core::eye = float(eye); + if (eye == 0) return; + eye = eye == -1 ? 0 : 1; + + jfloat *mProj = env->GetFloatArrayElements(proj, NULL); + jfloat *mView = env->GetFloatArrayElements(view, NULL); + + memcpy(&Input::hmd.proj[eye], mProj, sizeof(float) * 16); + memcpy(&Input::hmd.eye[eye], mView, sizeof(float) * 16); + + Input::hmd.eye[eye].setPos(Input::hmd.eye[eye].getPos() * ONE_METER); + Input::hmd.eye[eye] = Input::hmd.eye[eye].inverseOrtho(); + + env->ReleaseFloatArrayElements(proj, mProj, 0); + env->ReleaseFloatArrayElements(view, mView, 0); +} + JNI_METHOD(void, nativeSoundFill)(JNIEnv* env, jobject obj, jshortArray buffer) { jshort *frames = env->GetShortArrayElements(buffer, NULL); jsize count = env->GetArrayLength(buffer) / 2; diff --git a/src/platform/android/app/src/main/java/org/xproger/openlara/MainActivity.java b/src/platform/android/app/src/main/java/org/xproger/openlara/MainActivity.java index 2affad9..d071140 100644 --- a/src/platform/android/app/src/main/java/org/xproger/openlara/MainActivity.java +++ b/src/platform/android/app/src/main/java/org/xproger/openlara/MainActivity.java @@ -2,20 +2,10 @@ package org.xproger.openlara; import java.util.ArrayList; import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; -import android.content.pm.PackageManager; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; -import android.opengl.GLSurfaceView; -import android.opengl.GLSurfaceView.Renderer; import android.os.Bundle; -import android.app.Activity; -import android.content.res.AssetFileDescriptor; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; import android.os.Environment; import android.view.InputDevice; import android.view.KeyEvent; @@ -26,12 +16,24 @@ import android.view.View.OnKeyListener; import android.view.View.OnTouchListener; import android.view.Window; import android.view.WindowManager; -//import android.util.Log; -public class MainActivity extends Activity implements OnTouchListener, OnKeyListener, OnGenericMotionListener, SensorEventListener { +import com.google.vr.sdk.base.AndroidCompat; +import com.google.vr.sdk.base.Eye; +import com.google.vr.sdk.base.GvrActivity; +import com.google.vr.sdk.base.GvrView; +import com.google.vr.sdk.base.HeadTransform; +import com.google.vr.sdk.base.Viewport; + +public class MainActivity extends GvrActivity implements OnTouchListener, OnKeyListener, OnGenericMotionListener { + static GvrView gvrView; + private Wrapper wrapper; private ArrayList joyList = new ArrayList(); + public static void toggleVR(boolean enable) { + gvrView.setStereoModeEnabled(enable); + } + @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); @@ -45,10 +47,11 @@ public class MainActivity extends Activity implements OnTouchListener, OnKeyList super.onCreate(savedInstanceState); - GLSurfaceView view = new GLSurfaceView(this); + //GLSurfaceView view = new GLSurfaceView(this); + final GvrView view = new GvrView(this); view.setEGLContextClientVersion(2); view.setEGLConfigChooser(8, 8, 8, 8, 16, 8); - view.setPreserveEGLContextOnPause(true); + //view.setPreserveEGLContextOnPause(true); view.setRenderer(wrapper = new Wrapper()); view.setFocusable(true); @@ -57,13 +60,30 @@ public class MainActivity extends Activity implements OnTouchListener, OnKeyList view.setOnTouchListener(this); view.setOnGenericMotionListener(this); view.setOnKeyListener(this); - //setAsyncReprojectionEnabled(true); - //setSustainedPerformanceMode(this, true); + view.setTransitionViewEnabled(false); + + if (view.setAsyncReprojectionEnabled(true)) { + AndroidCompat.setSustainedPerformanceMode(this, true); + } + + //AndroidCompat.setVrModeEnabled(this, false); + view.setStereoModeEnabled(false); + view.setDistortionCorrectionEnabled(true); + + view.setOnCloseButtonListener(new Runnable() { + @Override + public void run() { + view.setStereoModeEnabled(false); + wrapper.toggleVR = true; + } + }); + + setGvrView(view); + setContentView(view); -/* - SensorManager sm = (SensorManager)getSystemService(SENSOR_SERVICE); - sm.registerListener(this, sm.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR), SensorManager.SENSOR_DELAY_FASTEST); -*/ + + gvrView = view; + try { String content = Environment.getExternalStorageDirectory().getAbsolutePath(); wrapper.onCreate(content + "/OpenLara/", getCacheDir().getAbsolutePath() + "/"); @@ -115,16 +135,6 @@ public class MainActivity extends Activity implements OnTouchListener, OnKeyList return true; } - @Override - public void onAccuracyChanged(Sensor arg0, int arg1) { - } - - @Override - public void onSensorChanged(SensorEvent event) { - wrapper.onTouch(-100, 0, -event.values[1], event.values[0]); - wrapper.onTouch(-100, 1, event.values[2], event.values[3]); - } - boolean isGamepad(int src) { return (src & (InputDevice.SOURCE_GAMEPAD | InputDevice.SOURCE_JOYSTICK)) != 0; } @@ -285,17 +295,23 @@ class Touch { } } -class Wrapper implements Renderer { +class Wrapper implements GvrView.StereoRenderer { public static native void nativeInit(String contentDir, String cacheDir); public static native void nativeFree(); public static native void nativeReset(); - public static native void nativeResize(int w, int h); + public static native void nativeResize(int x, int y, int w, int h); public static native void nativeUpdate(); - public static native void nativeRender(); + public static native void nativeSetVR(boolean enabled); + public static native void nativeSetHead(float head[]); + public static native void nativeSetEye(int eye, float proj[], float view[]); + public static native void nativeFrameBegin(); + public static native void nativeFrameEnd(); + public static native void nativeFrameRender(); public static native void nativeTouch(int id, int state, float x, float y); public static native void nativeSoundFill(short buffer[]); Boolean ready = false; + Boolean toggleVR = false; private String contentDir; private String cacheDir; private ArrayList touch = new ArrayList<>(); @@ -330,7 +346,26 @@ class Wrapper implements Renderer { } @Override - public void onDrawFrame(GL10 gl) { + public void onSurfaceChanged(int width, int height) { + nativeResize(0, 0, width, height); + } + + @Override + public void onSurfaceCreated(EGLConfig config) { + if (!ready) { + nativeInit(contentDir, cacheDir); + sound.play(); + ready = true; + } + } + + @Override + public void onRendererShutdown() { + // + } + + @Override + public void onNewFrame(HeadTransform headTransform) { synchronized (this) { for (int i = 0; i < touch.size(); i++) { Touch t = touch.get(i); @@ -338,21 +373,37 @@ class Wrapper implements Renderer { } touch.clear(); } - nativeUpdate(); - nativeRender(); - } - @Override - public void onSurfaceChanged(GL10 gl, int width, int height) { - nativeResize(width, height); - } - - @Override - public void onSurfaceCreated(GL10 gl, EGLConfig config) { - if (!ready) { - nativeInit(contentDir, cacheDir); - sound.play(); - ready = true; + if (toggleVR) { + nativeSetVR(false); + toggleVR = false; } + + float view[] = headTransform.getHeadView(); + nativeSetHead(view); + + nativeUpdate(); + nativeFrameBegin(); + } + + @Override + public void onDrawEye(Eye eye) { + float proj[] = eye.getPerspective(8.0f, 32.0f * 1024.0f); + float view[] = eye.getEyeView(); + + int index = 0; + if (eye.getType() == Eye.Type.LEFT) index = -1; + if (eye.getType() == Eye.Type.RIGHT) index = +1; + + nativeSetEye(index, proj, view); + + nativeResize(eye.getViewport().x, eye.getViewport().y, eye.getViewport().width, eye.getViewport().height); + nativeFrameRender(); + } + + @Override + public void onFinishFrame(Viewport viewport) { + nativeResize(viewport.x, viewport.y, viewport.width, viewport.height); + nativeFrameEnd(); } } diff --git a/src/platform/android/build.gradle b/src/platform/android/build.gradle index 1ea4bd0..a47fa4b 100644 --- a/src/platform/android/build.gradle +++ b/src/platform/android/build.gradle @@ -5,7 +5,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.0' + classpath 'com.android.tools.build:gradle:3.0.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files diff --git a/src/platform/android/gradle/wrapper/gradle-wrapper.properties b/src/platform/android/gradle/wrapper/gradle-wrapper.properties index 3710617..fb20fab 100644 --- a/src/platform/android/gradle/wrapper/gradle-wrapper.properties +++ b/src/platform/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Tue Mar 20 22:10:10 MSK 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip diff --git a/src/platform/win/main.cpp b/src/platform/win/main.cpp index 0a3be57..dd31dd5 100644 --- a/src/platform/win/main.cpp +++ b/src/platform/win/main.cpp @@ -605,7 +605,6 @@ void vrUpdateView() { mat4 vL = head * convToMat4(hmd->GetEyeToHeadTransform(vr::Eye_Left)); mat4 vR = head * convToMat4(hmd->GetEyeToHeadTransform(vr::Eye_Right)); - const float ONE_METER = 768.0f / 1.8f; // Lara's height in units / height in meters vL.setPos(vL.getPos() * ONE_METER); vR.setPos(vR.getPos() * ONE_METER); diff --git a/src/trigger.h b/src/trigger.h index 131e14f..9964177 100644 --- a/src/trigger.h +++ b/src/trigger.h @@ -215,6 +215,7 @@ struct MuzzleFlash : Controller { MuzzleFlash(IGame *game, int entity) : Controller(game, entity), owner(NULL), joint(0), lightIndex(-1) { pos.z += (level->version & (TR::VER_TR2 | TR::VER_TR3)) ? 180.0f : 150.0f; activate(); + timer = 0.0f; } virtual void update() { diff --git a/src/ui.h b/src/ui.h index 6c2af1b..e479c7d 100644 --- a/src/ui.h +++ b/src/ui.h @@ -2,6 +2,7 @@ #define H_UI #include "core.h" +#include "mesh.h" #include "controller.h" enum StringID { @@ -13,6 +14,7 @@ enum StringID { , STR_OFF , STR_ON , STR_SPLIT + , STR_VR , STR_QUALITY_LOW , STR_QUALITY_MEDIUM , STR_QUALITY_HIGH @@ -122,6 +124,7 @@ const char *STR[STR_MAX] = { , "Off" , "On" , "Split Screen" + , "VR" , "Low" , "Medium" , "High"