mirror of
https://github.com/DramaticShape/DramaticShapeVoxelMod.git
synced 2026-08-12 14:51:03 +02:00
Merge branch 'dev' into 3rd-person-camera
This commit is contained in:
+109
-9
@@ -59,6 +59,25 @@ local VR = {}
|
||||
-- spoken for, and a headset is not something to toggle by accident.
|
||||
VR.setting = ModSetting.new("vr", "VR", { false, true }, { "OFF", "ON" })
|
||||
|
||||
-- How the right stick turns you in first person. OFF is the 45-degree
|
||||
-- SNAP this mod shipped with and the reason for it is comfort, not
|
||||
-- taste: a software turn moves the world past a head that did not move,
|
||||
-- which is vection with no vestibular signal to match it, and it is the
|
||||
-- single most reliable way to make somebody ill in a headset. A snap
|
||||
-- gives the inner ear nothing to disagree with.
|
||||
--
|
||||
-- But snap turning is not free either -- it costs continuity, and the
|
||||
-- players who have their sea legs generally want the stick. So it is a
|
||||
-- row rather than a decision: OFF by default, on for anyone who asks,
|
||||
-- and the row only exists while there is a headset to use it in.
|
||||
VR.smoothTurn = ModSetting.new("smoothturn", "SMOOTH TURN",
|
||||
{ false, true }, { "OFF", "ON" })
|
||||
|
||||
-- radians per second at full deflection, with a squared response so the
|
||||
-- first half of the throw aims and the rest turns -- the same curve
|
||||
-- FirstPerson gives the flat screen's right stick
|
||||
VR.SMOOTH_TURN_RATE = 2.2
|
||||
|
||||
-- Where the diorama's UI panel floats vs first person's. These are the
|
||||
-- FALLBACK screens: wherever the pokedex is up and lit -- first
|
||||
-- person's menus, a battle's 2D scene -- no quad is submitted at all
|
||||
@@ -181,6 +200,11 @@ local function shutdown(reason)
|
||||
BattleCam.still = false
|
||||
VoxelScene.spriteLean = nil
|
||||
Pokedex.clear()
|
||||
-- the horde's gun too: its VR frame is a matrix built from a hand pose,
|
||||
-- and a stale one left behind would pin the model to wherever the
|
||||
-- controller was when the session died -- on the FLAT screen, where the
|
||||
-- view model should have taken over
|
||||
V.require("HordeGun").clear()
|
||||
zoom, heightOff = 1, 0
|
||||
fpYawOff, snapArmed = 0, true
|
||||
camMode, fadeAlpha = "explore", 0
|
||||
@@ -316,11 +340,46 @@ local function renderWorld(views, ctl)
|
||||
if scr then
|
||||
Pokedex.screen(scr[1], scr[2], scr[3], scr[4], scr[5])
|
||||
end
|
||||
elseif V.require("Horde").active then
|
||||
-- HORDE MODE's readout, on the device already in the player's left
|
||||
-- hand. It cannot be a flat overlay: the eye buffers have
|
||||
-- ASYMMETRIC frusta, so the same canvas pixel is a different ANGLE
|
||||
-- in each eye and a 2D HUD drawn into both tears down the middle.
|
||||
-- The Pokedex is real geometry both eyes see from their own
|
||||
-- position, so the stereo is correct by construction -- and it is
|
||||
-- already tracked, already lit, and already the thing this mod
|
||||
-- puts information on. (The gun wore it briefly and that was
|
||||
-- worse: a screen on the slide sits exactly where the iron sights
|
||||
-- need to be looked through.)
|
||||
--
|
||||
-- The UV rect goes over the usual way up: v = 0 at the TOP, which
|
||||
-- is how the device's screen quad reads every other texture it
|
||||
-- wears. An inverted rect was tried first, on the theory that a
|
||||
-- self-drawn canvas samples from the bottom -- it does not here,
|
||||
-- and it stood the readout on its head.
|
||||
local tex = V.require("HordeHud").panelTexture()
|
||||
if tex then Pokedex.screen(tex, 0, 0, 1, 1) end
|
||||
end
|
||||
else
|
||||
Pokedex.clear()
|
||||
end
|
||||
|
||||
-- and the horde's gun on the tracked RIGHT hand, under the same
|
||||
-- mapping. The AIM pose where the runtime offers one -- the barrel
|
||||
-- should point where the player is pointing, not along their wrist --
|
||||
-- and the grip pose as the fallback. Placed here rather than in the
|
||||
-- draw because the shot is traced down the model's own axis, so the
|
||||
-- matrix has to exist before anything can be hit with it.
|
||||
do
|
||||
local HordeGun = V.require("HordeGun")
|
||||
local right = ctl and (ctl.aimr or ctl.handr) or nil
|
||||
if right and fp and not battle and V.require("Horde").active then
|
||||
HordeGun.place(right, pivot, anchor, scale, mountYaw)
|
||||
else
|
||||
HordeGun.clear()
|
||||
end
|
||||
end
|
||||
|
||||
local eyes = {}
|
||||
for i = 1, 2 do
|
||||
local v = views[i]
|
||||
@@ -344,7 +403,10 @@ local function renderWorld(views, ctl)
|
||||
end
|
||||
|
||||
-- the snap's fade, over the finished eyes: plain black at this moment's
|
||||
-- strength, drawn before the blit so the headset never sees the swap
|
||||
-- strength, drawn before the blit so the headset never sees the swap.
|
||||
-- A full-frame fill is the ONE 2D thing that is safe to draw into an
|
||||
-- eye buffer -- it covers everything, so it does not matter that the
|
||||
-- two frusta disagree about where any given pixel points.
|
||||
if fadeAlpha > 0 then
|
||||
pcall(function()
|
||||
for i = 1, 2 do
|
||||
@@ -506,9 +568,25 @@ local function driveControls(ctl, dt, fp)
|
||||
if not (ok and Game.input) then return end
|
||||
local inp = Game.input
|
||||
|
||||
setGB(inp, "a", ctl.a)
|
||||
setGB(inp, "b", ctl.b)
|
||||
setGB(inp, "start", ctl.start)
|
||||
-- HORDE MODE re-reads the right hand as a weapon: the trigger fires
|
||||
-- (its own OpenXR action, suggested alongside START on the same input
|
||||
-- -- see VRXR.setupInput), and B reloads. START is dropped rather than
|
||||
-- forwarded, because the mode does not pause. Everything else -- the
|
||||
-- stick's walk, the snap turn, A -- keeps working, so the player can
|
||||
-- still move and look while they are being chased.
|
||||
local Horde = V.require("Horde")
|
||||
if Horde.playing() then
|
||||
local Gun = V.require("HordeGun")
|
||||
if ctl.fireChanged and ctl.fire then Gun.fire() end
|
||||
if ctl.bChanged and ctl.b then Gun.reload() end
|
||||
setGB(inp, "a", ctl.a)
|
||||
setGB(inp, "b", false)
|
||||
setGB(inp, "start", false)
|
||||
else
|
||||
setGB(inp, "a", ctl.a)
|
||||
setGB(inp, "b", ctl.b)
|
||||
setGB(inp, "start", ctl.start)
|
||||
end
|
||||
|
||||
-- the left stick, through the engine's OWN stick handler: it quantises
|
||||
-- to the grid d-pad for the diorama, and FirstPerson.moveVector reads
|
||||
@@ -517,12 +595,32 @@ local function driveControls(ctl, dt, fp)
|
||||
inp:gamepadaxis(nil, "leftx", ctl.moveX or 0)
|
||||
inp:gamepadaxis(nil, "lefty", -(ctl.moveY or 0))
|
||||
|
||||
if ctl.toggleChanged and ctl.toggle then VR.stepView() end
|
||||
-- the left stick click: the VOXEL ladder ordinarily, and the way out of
|
||||
-- horde mode while it runs (the rung is locked there, so the click has
|
||||
-- nothing else to do, and a headset has no ESCAPE key)
|
||||
if ctl.toggleChanged and ctl.toggle then
|
||||
if Horde.active then Horde.askExit() else VR.stepView() end
|
||||
end
|
||||
|
||||
-- first person's snap turn: a flick of the right stick steps the view
|
||||
-- 45 degrees, once per flick -- it re-arms only when the stick comes
|
||||
-- back toward centre, so holding it turns exactly once
|
||||
if fp and camMode ~= "battle" then
|
||||
-- first person's turn on the right stick. SMOOTH TURN ON makes it a
|
||||
-- rate -- hold and the world rotates under you -- and OFF (the
|
||||
-- default) makes it a 45-degree snap per flick: see the row's own
|
||||
-- reasoning where it is declared. Either way the offset turns the
|
||||
-- MAPPING, so the eyes, the walk direction, the pokedex and the gun
|
||||
-- all agree about which way the world now faces.
|
||||
if fp and camMode ~= "battle" and VR.smoothTurn:get() == true then
|
||||
local sx = ctl.lookX or 0
|
||||
local a = math.abs(sx)
|
||||
if a > 0.2 then
|
||||
a = (a - 0.2) / 0.8
|
||||
-- increasing yaw turns LEFT in this mod's compass, so a stick
|
||||
-- pushed right subtracts -- the same sign the snap below uses
|
||||
fpYawOff = wrapPi(fpYawOff
|
||||
- (sx > 0 and 1 or -1) * a * a
|
||||
* VR.SMOOTH_TURN_RATE * (dt or 0))
|
||||
end
|
||||
snapArmed = true -- so a switch back to snap mid-flick re-arms
|
||||
elseif fp and camMode ~= "battle" then
|
||||
local sx = ctl.lookX or 0
|
||||
if math.abs(sx) > 0.65 then
|
||||
if snapArmed then
|
||||
@@ -680,6 +778,8 @@ function VR.invalidate()
|
||||
if dexCanvas and dexCanvas.release then pcall(dexCanvas.release, dexCanvas) end
|
||||
dexCanvas = nil
|
||||
Pokedex.invalidate()
|
||||
V.require("HordeGun").invalidate()
|
||||
V.require("HordeHud").invalidate()
|
||||
for k in pairs(fboCache) do fboCache[k] = nil end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user