mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-10-14 19:27:09 -04:00
Glasses: SteamVR starts and stops with them, closer view with a field of view setting, Recenter on the pad
This commit is contained in:
1 parent
f0b329768e
commit
468262ab9d
17 files changed
+142
-16
No files matched your search
@@ -319,6 +319,9 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
private var glassesScreen: GlassesScreen? = null
|
||||
private var glassesOutputActive by mutableStateOf(false)
|
||||
private var glasses3dForVr = false
|
||||
private var glassesPresent = false
|
||||
private var steamVrAutoTried = false
|
||||
private var headTracking by mutableStateOf(false)
|
||||
private var glassesSlot by mutableStateOf(0)
|
||||
private var glassesKey: List<Any?> = emptyList()
|
||||
private val glassesScope = kotlinx.coroutines.MainScope()
|
||||
@@ -582,6 +585,8 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
cursorImage, cursorHotX, cursorHotY, cursorImageScale)
|
||||
if (hud.text.isNotEmpty()) HudText(hud.text)
|
||||
if (glassesOutputActive && !loading.visible) com.droiddeck.launcher.ui.GlassesPlayingHint(glasses.model)
|
||||
if (glassesOutputActive && headTracking && !drawerOpen && !pcKeyboardOpen && !loading.visible) com.droiddeck.launcher.ui.GlassesRecenterButton(
|
||||
controllerSettings?.tint ?: android.graphics.Color.rgb(90, 160, 255), controllerSettings?.opacity ?: 100) { headTracker?.recenter() }
|
||||
if (glasses.present && !drawerOpen && !pcKeyboardOpen && !loading.visible) com.droiddeck.launcher.ui.GlassesCornerButton(glassesSlot,
|
||||
controllerSettings?.tint ?: android.graphics.Color.rgb(90, 160, 255), controllerSettings?.opacity ?: 100) {
|
||||
drawerPage = com.droiddeck.launcher.ui.SessionDrawerPage.GLASSES
|
||||
@@ -2643,14 +2648,14 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
Glasses.set3d(true)
|
||||
}
|
||||
if (action == "install") steamVrStatus = SteamVr.Status.INSTALLING
|
||||
Thread({
|
||||
steamVrWorker.execute {
|
||||
val ok = SteamVr.run(this, action)
|
||||
val now = SteamVr.status(this)
|
||||
uiHandler.post {
|
||||
onSteamVrStatus(now)
|
||||
if (!ok) Toast.makeText(this, getString(R.string.cardboard_steamvr_failed), Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}, "steamvr-$action").start()
|
||||
}
|
||||
return
|
||||
}
|
||||
if (action == "start") {
|
||||
@@ -2659,7 +2664,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
}
|
||||
if (action == "stop") restoreSteamVrPicture()
|
||||
if (action == "install") steamVrStatus = SteamVr.Status.INSTALLING
|
||||
Thread({
|
||||
steamVrWorker.execute {
|
||||
val ok = SteamVr.run(this, action)
|
||||
val now = SteamVr.status(this)
|
||||
uiHandler.post {
|
||||
@@ -2668,7 +2673,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
onSteamVrStatus(now)
|
||||
if (!ok) Toast.makeText(this, getString(R.string.cardboard_steamvr_failed), Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}, "steamvr-$action").start()
|
||||
}
|
||||
}
|
||||
|
||||
/** SteamVR draws a side-by-side pair: the picture switches to that, remembering what it was. */
|
||||
@@ -2688,6 +2693,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
if (glassesOutputActive) {
|
||||
onGlassesSteamVr(now)
|
||||
if ((was == SteamVr.Status.RUNNING) != (now == SteamVr.Status.RUNNING)) applyCardboard(cardboard)
|
||||
autoStartSteamVr(now)
|
||||
return
|
||||
}
|
||||
// Started some other way than the drawer (a script, a VR game): the picture follows it all the same.
|
||||
@@ -2727,7 +2733,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
private fun pushCardboard(c: Cardboard) {
|
||||
val fov = when {
|
||||
c.viewer != Cardboard.Viewer.VITURE.id -> 0f
|
||||
c.source == Cardboard.SOURCE_SBS -> GLASSES_VR_FOV_DEG
|
||||
c.source == Cardboard.SOURCE_SBS -> glassesSettings.vrFovDeg.toFloat()
|
||||
else -> glasses.verticalFovDeg
|
||||
}
|
||||
panelSizeMm().let { (w, h) -> c.push(w, h, fov, refreshHz()) }
|
||||
@@ -2772,6 +2778,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
}.also { headTracker = it }
|
||||
t.start()
|
||||
}
|
||||
headTracking = headTracker != null
|
||||
}
|
||||
|
||||
private fun outputHolder(): SurfaceHolder = glassesScreen?.holder ?: surfaceView.holder
|
||||
@@ -2790,11 +2797,29 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
private fun onGlassesChanged() {
|
||||
val g = glasses
|
||||
val s = glassesSettings
|
||||
val key = listOf(g.usable, g.displayId, g.sbs, g.canControl, g.width, g.height, g.hz, s)
|
||||
val key = listOf(g.present, g.usable, g.displayId, g.sbs, g.canControl, g.width, g.height, g.hz, s)
|
||||
if (key == glassesKey) return
|
||||
glassesKey = key
|
||||
updateGlassesScreen()
|
||||
applyCardboard(cardboard)
|
||||
if (glassesPresent && !g.present) onGlassesUnplugged()
|
||||
glassesPresent = g.present
|
||||
}
|
||||
|
||||
private fun onGlassesUnplugged() {
|
||||
val hadVr = steamVrAutoTried
|
||||
steamVrAutoTried = false
|
||||
glasses3dForVr = false
|
||||
if (hadVr && !cardboard.enabled) steamVrAction("stop")
|
||||
}
|
||||
|
||||
private fun autoStartSteamVr(now: SteamVr.Status) {
|
||||
if (now == SteamVr.Status.RUNNING) steamVrAutoTried = true
|
||||
if (now != SteamVr.Status.READY || steamVrAutoTried || !glassesSettings.autoSteamVr || !glasses.canControl ||
|
||||
loading.visible || SessionState.mode != SessionService.MODE_STEAM) return
|
||||
steamVrAutoTried = true
|
||||
Log.i(TAG, "starting SteamVR for ${glasses.model}")
|
||||
steamVrAction("start")
|
||||
}
|
||||
|
||||
private fun updateGlassesScreen() {
|
||||
@@ -3001,7 +3026,8 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
private const val DRAWER_SLOW_REPEAT_FLOOR_MS = 240L
|
||||
private const val DRAWER_FAST_REPEAT_INTERVAL_MS = 90L
|
||||
private const val DRAWER_REPEAT_ACCELERATION_MS = 1_400L
|
||||
private const val GLASSES_VR_FOV_DEG = 70f
|
||||
private val steamVrWorker = java.util.concurrent.ThreadPoolExecutor(0, 1, 30, java.util.concurrent.TimeUnit.SECONDS,
|
||||
java.util.concurrent.LinkedBlockingQueue()) { Thread(it, "steamvr") }
|
||||
/** Compositor scale modes (Container.FULLSCREEN_* values): 1 = fit with bars, centred. */
|
||||
private const val SCALE_FIT = 1
|
||||
private const val ALIGN_CENTER = 0
|
||||
|
||||
@@ -117,7 +117,7 @@ class AgentBridgeProvider : ContentProvider() {
|
||||
val value = request.optString("value")
|
||||
fun on() = value == "1" || value.equals("true", true) || value.equals("on", true)
|
||||
val number = value.toIntOrNull()
|
||||
val numeric = setOf("refreshHz", "sbsHz", "brightness", "volume", "picture3d", "screenWidthDm", "screenDistanceDm", "sbsSupport")
|
||||
val numeric = setOf("refreshHz", "sbsHz", "brightness", "volume", "picture3d", "screenWidthDm", "screenDistanceDm", "vrFovDeg", "sbsSupport")
|
||||
if (key in numeric && number == null) return error("INVALID_VALUE", "'$key' takes a number")
|
||||
when (key) {
|
||||
"refreshHz" -> g.setRefreshHz(number!!)
|
||||
@@ -132,6 +132,8 @@ class AgentBridgeProvider : ContentProvider() {
|
||||
"picture3d" -> g.setPicture3d(number!!)
|
||||
"screenWidthDm" -> g.setScreenWidthDm(number!!)
|
||||
"screenDistanceDm" -> g.setScreenDistanceDm(number!!)
|
||||
"vrFovDeg" -> g.setVrFovDeg(number!!)
|
||||
"autoSteamVr" -> g.setAutoSteamVr(on())
|
||||
"sbsSupport" -> g.setSbsSupport(number!!)
|
||||
else -> return error("INVALID_KEY", "Unknown glasses setting '$key'")
|
||||
}
|
||||
|
||||
@@ -73,8 +73,13 @@ data class GlassesSettings(
|
||||
val picture3d: Int = PICTURE_AUTO,
|
||||
val screenWidthDm: Int = 20,
|
||||
val screenDistanceDm: Int = 30,
|
||||
val vrFovDeg: Int = VR_FOV_DEFAULT,
|
||||
val autoSteamVr: Boolean = true,
|
||||
) {
|
||||
companion object {
|
||||
const val VR_FOV_MIN = 24
|
||||
const val VR_FOV_MAX = 90
|
||||
const val VR_FOV_DEFAULT = 36
|
||||
const val PICTURE_AUTO = 0
|
||||
const val PICTURE_SCREEN = 1
|
||||
const val PICTURE_SBS = 2
|
||||
@@ -198,6 +203,8 @@ object Glasses {
|
||||
fun setPicture3d(picture: Int) = post { update { it.copy(picture3d = picture.coerceIn(0, 2)) } }
|
||||
fun setScreenWidthDm(dm: Int) = post { update { it.copy(screenWidthDm = dm.coerceIn(5, 200)) } }
|
||||
fun setScreenDistanceDm(dm: Int) = post { update { it.copy(screenDistanceDm = dm.coerceIn(5, 200)) } }
|
||||
fun setVrFovDeg(deg: Int) = post { update { it.copy(vrFovDeg = deg.coerceIn(GlassesSettings.VR_FOV_MIN, GlassesSettings.VR_FOV_MAX)) } }
|
||||
fun setAutoSteamVr(on: Boolean) = post { update { it.copy(autoSteamVr = on) } }
|
||||
|
||||
fun glassesDisplay(): Display? = _state.value.displayId.takeIf { it != Display.INVALID_DISPLAY }?.let { displays.getDisplay(it) }
|
||||
|
||||
@@ -252,7 +259,8 @@ object Glasses {
|
||||
.put("fovDeg", s.diagonalFovDeg.toDouble()).put("noSignal", s.noSignal).put("sbsSupport", s.sbsSupport)
|
||||
.put("settings", JSONObject().put("refreshHz", g.refreshHz).put("sbsHz", g.sbsHz).put("sunblock", g.sunblock ?: JSONObject.NULL)
|
||||
.put("gameOnGlasses", g.gameOnGlasses).put("headTracking", g.headTracking).put("auto3d", g.auto3d)
|
||||
.put("picture3d", g.picture3d).put("screenWidthDm", g.screenWidthDm).put("screenDistanceDm", g.screenDistanceDm))
|
||||
.put("picture3d", g.picture3d).put("screenWidthDm", g.screenWidthDm).put("screenDistanceDm", g.screenDistanceDm)
|
||||
.put("vrFovDeg", g.vrFovDeg).put("autoSteamVr", g.autoSteamVr))
|
||||
}
|
||||
|
||||
fun recentFrames(): JSONArray = synchronized(frames) { JSONArray(frames.toList()) }
|
||||
@@ -544,6 +552,8 @@ object Glasses {
|
||||
picture3d = p.getInt("picture3d", GlassesSettings.PICTURE_AUTO),
|
||||
screenWidthDm = p.getInt("screenWidthDm", 20),
|
||||
screenDistanceDm = p.getInt("screenDistanceDm", 30),
|
||||
vrFovDeg = p.getInt("vrFovDeg", GlassesSettings.VR_FOV_DEFAULT).coerceIn(GlassesSettings.VR_FOV_MIN, GlassesSettings.VR_FOV_MAX),
|
||||
autoSteamVr = p.getBoolean("autoSteamVr", true),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -558,6 +568,8 @@ object Glasses {
|
||||
putInt("picture3d", s.picture3d)
|
||||
putInt("screenWidthDm", s.screenWidthDm)
|
||||
putInt("screenDistanceDm", s.screenDistanceDm)
|
||||
putInt("vrFovDeg", s.vrFovDeg)
|
||||
putBoolean("autoSteamVr", s.autoSteamVr)
|
||||
}.apply()
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,11 @@ import androidx.compose.foundation.layout.displayCutout
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -128,6 +130,11 @@ internal fun GlassesGroups(
|
||||
format = { String.format(Locale.US, "%.1f m", it / 10f) }, modifier = track("glasses-distance")) { Glasses.setScreenDistanceDm(it) }
|
||||
}
|
||||
steamVr()
|
||||
ToggleRow(host, "glasses-auto-steamvr", stringResource(R.string.glasses_auto_steamvr), stringResource(R.string.glasses_auto_steamvr_hint),
|
||||
s.autoSteamVr, chipModifier = track("glasses-auto-steamvr")) { Glasses.setAutoSteamVr(it) }
|
||||
SliderRow(stringResource(R.string.glasses_vr_fov), stringResource(R.string.glasses_vr_fov_hint), s.vrFovDeg,
|
||||
GlassesSettings.VR_FOV_MIN..GlassesSettings.VR_FOV_MAX, step = 1,
|
||||
format = { "$it°" }, modifier = track("glasses-vr-fov")) { Glasses.setVrFovDeg(it) }
|
||||
}
|
||||
if ((tab == null || tab == GlassesTab.HARDWARE) && g.canControl) SettingsGroup(stringResource(R.string.glasses_hardware)) {
|
||||
SliderRow(stringResource(R.string.glasses_brightness), null, g.brightness.coerceAtLeast(0), 0..g.brightnessMax, step = 1,
|
||||
@@ -145,6 +152,13 @@ internal fun GlassesGroups(
|
||||
}
|
||||
}
|
||||
|
||||
private fun padButtonColors(tint: Int, opacity: Int, pressed: Boolean): Pair<Color, Color> {
|
||||
val base = Color(tint)
|
||||
val alpha = opacity.coerceIn(0, 100) / 100f
|
||||
fun light(a: Int, f: Float) = Color(base.red + (1 - base.red) * f, base.green + (1 - base.green) * f, base.blue + (1 - base.blue) * f, a * alpha / 255f)
|
||||
return Color(base.red * 0.12f, base.green * 0.12f, base.blue * 0.12f, 190 / 255f) to (if (pressed) light(230, 0.7f) else light(150, 0.25f))
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun GlassesCornerButton(slot: Int, tint: Int, opacity: Int, onClick: () -> Unit) {
|
||||
val density = androidx.compose.ui.platform.LocalDensity.current
|
||||
@@ -155,11 +169,7 @@ internal fun GlassesCornerButton(slot: Int, tint: Int, opacity: Int, onClick: ()
|
||||
val bottom = if (slot < 0) 44.dp else 34.dp
|
||||
val source = remember { MutableInteractionSource() }
|
||||
val pressed by source.collectIsPressedAsState()
|
||||
val base = Color(tint)
|
||||
val alpha = opacity.coerceIn(0, 100) / 100f
|
||||
val fill = Color(base.red * 0.12f, base.green * 0.12f, base.blue * 0.12f, 190 / 255f)
|
||||
fun light(a: Int, f: Float) = Color(base.red + (1 - base.red) * f, base.green + (1 - base.green) * f, base.blue + (1 - base.blue) * f, a * alpha / 255f)
|
||||
val line = if (pressed) light(230, 0.7f) else light(150, 0.25f)
|
||||
val (fill, line) = padButtonColors(tint, opacity, pressed)
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
@@ -176,6 +186,32 @@ internal fun GlassesCornerButton(slot: Int, tint: Int, opacity: Int, onClick: ()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun GlassesRecenterButton(tint: Int, opacity: Int, onClick: () -> Unit) {
|
||||
val source = remember { MutableInteractionSource() }
|
||||
val pressed by source.collectIsPressedAsState()
|
||||
val (fill, line) = padButtonColors(tint, opacity, pressed)
|
||||
val shape = RoundedCornerShape(50)
|
||||
Box(
|
||||
Modifier.fillMaxSize().windowInsetsPadding(WindowInsets.displayCutout).padding(top = 12.dp),
|
||||
contentAlignment = Alignment.TopCenter,
|
||||
) {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.height(36.dp)
|
||||
.clip(shape)
|
||||
.background(fill)
|
||||
.border(1.5.dp, line, shape)
|
||||
.clickable(interactionSource = source, indication = null, role = Role.Button, onClick = onClick)
|
||||
.padding(horizontal = 18.dp)
|
||||
.testTag("glasses-recenter"),
|
||||
) {
|
||||
Text(stringResource(R.string.glasses_recenter_button), color = line, fontSize = 14.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun GlassesPlayingHint(model: String) {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
|
||||
@@ -2183,6 +2183,11 @@
|
||||
<string name="glasses_head_tracking">Seguimiento de cabeza</string>
|
||||
<string name="glasses_head_tracking_hint">Los sensores de las gafas giran la pantalla virtual y SteamVR con tu cabeza.</string>
|
||||
<string name="glasses_recenter">Recentrar la vista</string>
|
||||
<string name="glasses_recenter_button">Recentrar</string>
|
||||
<string name="glasses_auto_steamvr">Iniciar SteamVR con las gafas</string>
|
||||
<string name="glasses_auto_steamvr_hint">En Steam, SteamVR se inicia al conectar las gafas y se detiene al desconectarlas.</string>
|
||||
<string name="glasses_vr_fov">Campo de visión de SteamVR</string>
|
||||
<string name="glasses_vr_fov_hint">Más bajo acerca SteamVR y llena las gafas; más alto muestra más a tu alrededor.</string>
|
||||
<string name="glasses_playing">Jugando en %1$s</string>
|
||||
<string name="stores_fact_developer">Desarrollador</string>
|
||||
<string name="stores_fact_publisher">Distribuidor</string>
|
||||
|
||||
@@ -1013,6 +1013,11 @@
|
||||
<string name="glasses_head_tracking">Suivi de la tête</string>
|
||||
<string name="glasses_head_tracking_hint">Les capteurs des lunettes font tourner l\'écran virtuel et SteamVR avec votre tête.</string>
|
||||
<string name="glasses_recenter">Recentrer la vue</string>
|
||||
<string name="glasses_recenter_button">Recentrer</string>
|
||||
<string name="glasses_auto_steamvr">Lancer SteamVR avec les lunettes</string>
|
||||
<string name="glasses_auto_steamvr_hint">Dans Steam, SteamVR démarre quand les lunettes sont branchées et s\'arrête quand elles sont débranchées.</string>
|
||||
<string name="glasses_vr_fov">Champ de vision SteamVR</string>
|
||||
<string name="glasses_vr_fov_hint">Plus bas rapproche SteamVR et remplit les lunettes ; plus haut montre davantage autour de vous.</string>
|
||||
<string name="glasses_playing">En jeu sur %1$s</string>
|
||||
<string name="stores_fact_developer">Développeur</string>
|
||||
<string name="stores_fact_publisher">Éditeur</string>
|
||||
|
||||
@@ -2139,6 +2139,11 @@
|
||||
<string name="glasses_head_tracking">ヘッドトラッキング</string>
|
||||
<string name="glasses_head_tracking_hint">グラスのセンサーで、頭の動きに合わせて仮想スクリーンと SteamVR を動かします。</string>
|
||||
<string name="glasses_recenter">視点をリセット</string>
|
||||
<string name="glasses_recenter_button">中央に戻す</string>
|
||||
<string name="glasses_auto_steamvr">グラス接続時に SteamVR を起動</string>
|
||||
<string name="glasses_auto_steamvr_hint">Steam では、グラスを接続すると SteamVR を起動し、外すと停止します。</string>
|
||||
<string name="glasses_vr_fov">SteamVR の視野角</string>
|
||||
<string name="glasses_vr_fov_hint">小さくすると SteamVR が近くなりグラスいっぱいに広がり、大きくすると周囲が広く見えます。</string>
|
||||
<string name="glasses_playing">%1$s でプレイ中</string>
|
||||
<string name="stores_fact_developer">開発元</string>
|
||||
<string name="stores_fact_publisher">パブリッシャー</string>
|
||||
|
||||
@@ -2139,6 +2139,11 @@
|
||||
<string name="glasses_head_tracking">헤드 트래킹</string>
|
||||
<string name="glasses_head_tracking_hint">안경의 센서가 머리 움직임에 맞춰 가상 화면과 SteamVR을 돌립니다.</string>
|
||||
<string name="glasses_recenter">시점 재설정</string>
|
||||
<string name="glasses_recenter_button">중앙 맞추기</string>
|
||||
<string name="glasses_auto_steamvr">안경 연결 시 SteamVR 시작</string>
|
||||
<string name="glasses_auto_steamvr_hint">Steam에서 안경을 연결하면 SteamVR을 시작하고, 분리하면 중지합니다.</string>
|
||||
<string name="glasses_vr_fov">SteamVR 시야각</string>
|
||||
<string name="glasses_vr_fov_hint">낮추면 SteamVR이 가까워져 안경을 가득 채우고, 높이면 주변이 더 넓게 보입니다.</string>
|
||||
<string name="glasses_playing">%1$s에서 플레이 중</string>
|
||||
<string name="stores_fact_developer">개발사</string>
|
||||
<string name="stores_fact_publisher">배급사</string>
|
||||
|
||||
@@ -1124,6 +1124,11 @@
|
||||
<string name="glasses_head_tracking">Отслеживание головы</string>
|
||||
<string name="glasses_head_tracking_hint">Датчики очков поворачивают виртуальный экран и SteamVR вслед за головой.</string>
|
||||
<string name="glasses_recenter">Сбросить вид</string>
|
||||
<string name="glasses_recenter_button">Центрировать</string>
|
||||
<string name="glasses_auto_steamvr">Запускать SteamVR с очками</string>
|
||||
<string name="glasses_auto_steamvr_hint">В Steam SteamVR запускается при подключении очков и останавливается при отключении.</string>
|
||||
<string name="glasses_vr_fov">Поле зрения SteamVR</string>
|
||||
<string name="glasses_vr_fov_hint">Меньше — SteamVR ближе и заполняет очки; больше — шире обзор вокруг.</string>
|
||||
<string name="glasses_playing">Игра на %1$s</string>
|
||||
<string name="stores_fact_developer">Разработчик</string>
|
||||
<string name="stores_fact_publisher">Издатель</string>
|
||||
|
||||
@@ -2139,6 +2139,11 @@
|
||||
<string name="glasses_head_tracking">头部追踪</string>
|
||||
<string name="glasses_head_tracking_hint">眼镜自带的传感器会随头部转动虚拟屏幕和 SteamVR。</string>
|
||||
<string name="glasses_recenter">重新居中视角</string>
|
||||
<string name="glasses_recenter_button">重新居中</string>
|
||||
<string name="glasses_auto_steamvr">连接眼镜时启动 SteamVR</string>
|
||||
<string name="glasses_auto_steamvr_hint">在 Steam 中,插入眼镜时启动 SteamVR,拔出时停止。</string>
|
||||
<string name="glasses_vr_fov">SteamVR 视场角</string>
|
||||
<string name="glasses_vr_fov_hint">调低让 SteamVR 更近并填满眼镜;调高可看到更多周围内容。</string>
|
||||
<string name="glasses_playing">正在 %1$s 上游玩</string>
|
||||
<string name="stores_fact_developer">开发商</string>
|
||||
<string name="stores_fact_publisher">发行商</string>
|
||||
|
||||
@@ -2139,6 +2139,11 @@
|
||||
<string name="glasses_head_tracking">頭部追蹤</string>
|
||||
<string name="glasses_head_tracking_hint">眼鏡自帶的感應器會隨頭部轉動虛擬螢幕和 SteamVR。</string>
|
||||
<string name="glasses_recenter">重新置中視角</string>
|
||||
<string name="glasses_recenter_button">重新置中</string>
|
||||
<string name="glasses_auto_steamvr">連接眼鏡時啟動 SteamVR</string>
|
||||
<string name="glasses_auto_steamvr_hint">在 Steam 中,插入眼鏡時啟動 SteamVR,拔出時停止。</string>
|
||||
<string name="glasses_vr_fov">SteamVR 視場角</string>
|
||||
<string name="glasses_vr_fov_hint">調低令 SteamVR 更近並填滿眼鏡;調高可看到更多周圍內容。</string>
|
||||
<string name="glasses_playing">正在 %1$s 上遊玩</string>
|
||||
<string name="stores_fact_developer">開發商</string>
|
||||
<string name="stores_fact_publisher">發行商</string>
|
||||
|
||||
@@ -2139,6 +2139,11 @@
|
||||
<string name="glasses_head_tracking">頭部追蹤</string>
|
||||
<string name="glasses_head_tracking_hint">眼鏡內建的感測器會隨頭部轉動虛擬螢幕和 SteamVR。</string>
|
||||
<string name="glasses_recenter">重新置中視角</string>
|
||||
<string name="glasses_recenter_button">重新置中</string>
|
||||
<string name="glasses_auto_steamvr">連接眼鏡時啟動 SteamVR</string>
|
||||
<string name="glasses_auto_steamvr_hint">在 Steam 中,插上眼鏡時啟動 SteamVR,拔除時停止。</string>
|
||||
<string name="glasses_vr_fov">SteamVR 視野角度</string>
|
||||
<string name="glasses_vr_fov_hint">調低讓 SteamVR 更近並填滿眼鏡;調高可看到更多周圍內容。</string>
|
||||
<string name="glasses_playing">正在 %1$s 上遊玩</string>
|
||||
<string name="stores_fact_developer">開發商</string>
|
||||
<string name="stores_fact_publisher">發行商</string>
|
||||
|
||||
@@ -2396,5 +2396,10 @@
|
||||
<string name="glasses_head_tracking">Head tracking</string>
|
||||
<string name="glasses_head_tracking_hint">The glasses\' own sensors turn the virtual screen and SteamVR with your head.</string>
|
||||
<string name="glasses_recenter">Recenter view</string>
|
||||
<string name="glasses_recenter_button">Recenter</string>
|
||||
<string name="glasses_auto_steamvr">Start SteamVR with the glasses</string>
|
||||
<string name="glasses_auto_steamvr_hint">In Steam, SteamVR starts when the glasses are plugged in and stops when they are unplugged.</string>
|
||||
<string name="glasses_vr_fov">SteamVR field of view</string>
|
||||
<string name="glasses_vr_fov_hint">Lower brings SteamVR closer and fills the glasses; higher shows more around you.</string>
|
||||
<string name="glasses_playing">Playing on %1$s</string>
|
||||
</resources>
|
||||
@@ -61,7 +61,8 @@ switches the picture to side-by-side and runs SteamVR with three DroidDeck piece
|
||||
1. **`driver_droiddeck.so`**, an OpenVR driver with one HMD:
|
||||
- pose: the shared head pose file (3DoF, standing height);
|
||||
- projection: exactly the frustum the Cardboard pass samples, with identity distortion (the app does
|
||||
the lens);
|
||||
the lens). A new frustum mid-run (the glasses' SteamVR field of view) goes out with
|
||||
`SetDisplayProjectionRaw` and `VREvent_LensDistortionChanged`;
|
||||
- a proximity sensor that reads "worn" while Cardboard mode is on. Without it SteamVR takes a still
|
||||
headset (a phone on a table) for an unused one, enters standby after a few seconds, and the
|
||||
compositor stops submitting frames;
|
||||
|
||||
@@ -95,6 +95,9 @@ OpenVR to SteamVR's ARM64 `vrclient.so`, so no x86 SteamVR is involved.
|
||||
and KGSL does not order the copy after the client's GPU work. The presenter's blit sat behind the
|
||||
game's frame, so the compositor showed the swapchain image's older picture. The presenter now
|
||||
waits for its blit before it presents.
|
||||
- **A new projection needs an event.** `SetDisplayProjectionRaw` changed what `vrserver` reports, but
|
||||
`vrcompositor` kept the frustum it started with, so the glasses' field of view did nothing until
|
||||
SteamVR restarted. The driver now follows it with `VREvent_LensDistortionChanged`.
|
||||
- **Head-tracking axes.** With the display at `ROTATION_90`, screen x is minus device y and screen y is
|
||||
device x. Getting this wrong made a turn at the hips tilt the view. Recentring now keeps the world
|
||||
vertical and resets the heading only.
|
||||
|
||||
+1
-1
@@ -674,7 +674,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
glasses_ops = glasses.add_subparsers(dest="glasses_op")
|
||||
glasses_ops.add_parser("status")
|
||||
glasses_ops.add_parser("allow", help="ask for USB control of the glasses again")
|
||||
glasses_set = glasses_ops.add_parser("set", help="refreshHz, sbsHz, brightness, volume, sunblock, 3d, gameOnGlasses, headTracking, auto3d")
|
||||
glasses_set = glasses_ops.add_parser("set", help="refreshHz, sbsHz, brightness, volume, sunblock, 3d, gameOnGlasses, headTracking, auto3d, picture3d, screenWidthDm, screenDistanceDm, vrFovDeg, autoSteamVr")
|
||||
glasses_set.add_argument("key")
|
||||
glasses_set.add_argument("value")
|
||||
glasses_sim = glasses_ops.add_parser("simulate", help="feed a moving head pose (yaw, pitch and roll sweeps), or hold one")
|
||||
|
||||
@@ -612,6 +612,7 @@ private:
|
||||
uint32_t w = 0, h = 0;
|
||||
GetRecommendedRenderTargetSize(&w, &h);
|
||||
VRServerDriverHost()->SetRecommendedRenderTargetSize(id_, w, h);
|
||||
VRServerDriverHost()->VendorSpecificEvent(id_, VREvent_LensDistortionChanged, VREvent_Data_t{}, 0.0);
|
||||
Log("eye now tan(half fov) %.3f, aspect %.3f (%ux%u eye%s), render %ux%u", tanHalf_.load(), eyeAspect_.load(), hp.eye_w, hp.eye_h,
|
||||
(hp.flags & DROIDDECK_HEADPOSE_MONO) ? ", one eye shown" : "", w, h);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user