FIX - keep ultrawide camera aspect ratio

This commit is contained in:
Eichenholz
2026-08-06 16:15:05 +02:00
parent aea7872ad8
commit f110b80c32
4 changed files with 103 additions and 30 deletions
+6 -6
View File
@@ -31,20 +31,20 @@ describe('gameViewGeometry', () => {
])
})
it('uses the tablet camera zoom levels around the crop center', () => {
it('keeps 0.5x full-frame while higher zoom levels crop around the center', () => {
const wideGeometry = gameViewGeometry(1920, 1080, 540, 720, 0.5)
expect(Array.from(wideGeometry.textureCoordinates)).toEqual([
expect.closeTo(0.08, 2),
expect.closeTo(0.29, 2),
0,
expect.closeTo(0.92, 2),
expect.closeTo(0.71, 2),
0,
expect.closeTo(0.08, 2),
expect.closeTo(0.29, 2),
1,
expect.closeTo(0.92, 2),
expect.closeTo(0.71, 2),
1,
])
expect(Array.from(wideGeometry.positions)).toEqual([
-1, -0.5, 1, -0.5, -1, 0.5, 1, 0.5,
-1, -1, 1, -1, -1, 1, 1, 1,
])
const zoomedGeometry = gameViewGeometry(1920, 1080, 540, 720, 2)
+2 -22
View File
@@ -58,9 +58,7 @@ export function gameViewGeometry(
bottom = 1 - top
}
const baseWidth = right - left
const baseHeight = bottom - top
const normalizedZoom = Math.min(3, Math.max(0.5, zoom))
const normalizedZoom = Math.min(3, Math.max(1, zoom))
const centerX = (left + right) / 2
const centerY = (top + bottom) / 2
left = Math.max(0, centerX + (left - centerX) / normalizedZoom)
@@ -68,26 +66,8 @@ export function gameViewGeometry(
top = Math.max(0, centerY + (top - centerY) / normalizedZoom)
bottom = Math.min(1, centerY + (bottom - centerY) / normalizedZoom)
const positionScaleX = Math.min(
1,
(normalizedZoom * (right - left)) / baseWidth,
)
const positionScaleY = Math.min(
1,
(normalizedZoom * (bottom - top)) / baseHeight,
)
return {
positions: new Float32Array([
-positionScaleX,
-positionScaleY,
positionScaleX,
-positionScaleY,
-positionScaleX,
positionScaleY,
positionScaleX,
positionScaleY,
]),
positions: new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]),
textureCoordinates: new Float32Array([
left,
top,
+2 -1
View File
@@ -232,6 +232,7 @@ function setZoom(zoom: (typeof zoomLevels)[number]): void {
selectedZoom.value = zoom
resizeGameView()
window.postMessage({ data: { zoom }, type: 'camera:zoom' }, '*')
void nuiCall('camera:setZoom', { zoom })
}
function resizeGameView(entry?: ResizeObserverEntry): void {
@@ -392,7 +393,7 @@ onBeforeUnmount(() => {
<div
v-else
class="camera-dev-view"
:style="{ transform: `scale(${selectedZoom})` }"
:style="{ transform: `scale(${Math.max(1, selectedZoom)})` }"
aria-hidden="true"
>
<span class="camera-dev-sun"></span>
+93 -1
View File
@@ -2,6 +2,7 @@ local first_person_view_mode = 4
local third_person_view_mode = 1
local front_camera_view_mode = 0
local front_camera_fov = 25.0
local ultrawide_fov_multiplier = 2.0
local front_camera_distance = 0.75
local front_camera_height = 0.05
local front_camera_target_height = 0.03
@@ -13,10 +14,12 @@ local camera_state = {
focus_watcher = false,
front_camera = false,
front_camera_handle = nil,
ultrawide_camera_handle = nil,
nui_focused = true,
previous_ped_view = nil,
previous_radar_hidden = nil,
previous_vehicle_view = nil,
zoom = 1.0,
}
local function rotation_to_direction(rotation)
@@ -77,11 +80,50 @@ local function ensure_front_camera(ped)
return
end
camera_state.front_camera_handle = CreateCam("DEFAULT_SCRIPTED_CAMERA", true)
SetCamFov(camera_state.front_camera_handle, front_camera_fov)
SetCamFov(
camera_state.front_camera_handle,
camera_state.zoom == 0.5 and front_camera_fov * ultrawide_fov_multiplier or front_camera_fov
)
SetCamActive(camera_state.front_camera_handle, true)
RenderScriptCams(true, false, 0, true, true)
end
local function ensure_ultrawide_camera()
if camera_state.ultrawide_camera_handle and DoesCamExist(camera_state.ultrawide_camera_handle) then
return
end
local camera_coords = GetGameplayCamCoord()
local camera_rotation = GetGameplayCamRot(2)
camera_state.ultrawide_camera_handle = CreateCam("DEFAULT_SCRIPTED_CAMERA", true)
SetCamCoord(
camera_state.ultrawide_camera_handle,
camera_coords.x,
camera_coords.y,
camera_coords.z
)
SetCamRot(
camera_state.ultrawide_camera_handle,
camera_rotation.x,
camera_rotation.y,
camera_rotation.z,
2
)
SetCamFov(
camera_state.ultrawide_camera_handle,
math.min(GetGameplayCamFov() * ultrawide_fov_multiplier, 120.0)
)
SetCamActive(camera_state.ultrawide_camera_handle, true)
RenderScriptCams(true, false, 0, true, true)
end
local function clear_ultrawide_camera()
if camera_state.ultrawide_camera_handle and DoesCamExist(camera_state.ultrawide_camera_handle) then
RenderScriptCams(false, false, 0, true, true)
DestroyCam(camera_state.ultrawide_camera_handle, false)
end
camera_state.ultrawide_camera_handle = nil
end
local function clear_front_camera()
if camera_state.front_camera_handle and DoesCamExist(camera_state.front_camera_handle) then
RenderScriptCams(false, false, 0, true, true)
@@ -139,7 +181,9 @@ local function set_camera_active(active)
camera_state.active = active
if active then
camera_state.front_camera = false
camera_state.zoom = 1.0
clear_front_camera()
clear_ultrawide_camera()
camera_state.previous_ped_view = GetFollowPedCamViewMode()
camera_state.previous_vehicle_view = GetFollowVehicleCamViewMode()
camera_state.previous_radar_hidden = IsRadarHidden()
@@ -165,6 +209,23 @@ local function set_camera_active(active)
camera_position.z
)
PointCamAtCoord(camera_state.front_camera_handle, target.x, target.y, target.z)
elseif camera_state.zoom == 0.5 then
ensure_ultrawide_camera()
local camera_coords = GetGameplayCamCoord()
local camera_rotation = GetGameplayCamRot(2)
SetCamCoord(
camera_state.ultrawide_camera_handle,
camera_coords.x,
camera_coords.y,
camera_coords.z
)
SetCamRot(
camera_state.ultrawide_camera_handle,
camera_rotation.x,
camera_rotation.y,
camera_rotation.z,
2
)
end
local now = GetGameTimer()
if now >= next_apply then
@@ -180,6 +241,7 @@ local function set_camera_active(active)
camera_state.flash_enabled = false
camera_state.front_camera = false
clear_front_camera()
clear_ultrawide_camera()
restore_camera_view()
if not camera_state.nui_focused then
camera_state.nui_focused = true
@@ -197,13 +259,39 @@ local function set_front_camera(active)
return
end
if active then
clear_ultrawide_camera()
ensure_front_camera(PlayerPedId())
else
clear_front_camera()
if camera_state.zoom == 0.5 then
ensure_ultrawide_camera()
end
end
apply_camera_view()
end
local function set_camera_zoom(zoom)
if zoom ~= 0.5 and zoom ~= 1.0 and zoom ~= 2.0 and zoom ~= 3.0 then
return false
end
camera_state.zoom = zoom
if not camera_state.active then
return true
end
if camera_state.front_camera then
ensure_front_camera(PlayerPedId())
SetCamFov(
camera_state.front_camera_handle,
zoom == 0.5 and front_camera_fov * ultrawide_fov_multiplier or front_camera_fov
)
elseif zoom == 0.5 then
ensure_ultrawide_camera()
else
clear_ultrawide_camera()
end
return true
end
RegisterNUICallback("camera:setActive", function(data, cb)
set_camera_active(data and data.active == true)
cb({ success = true })
@@ -226,6 +314,10 @@ RegisterNUICallback("camera:setFacing", function(data, cb)
cb({ success = true })
end)
RegisterNUICallback("camera:setZoom", function(data, cb)
cb({ success = set_camera_zoom(tonumber(data and data.zoom)) })
end)
RegisterNUICallback("media:requestUpload", function(data, cb)
TriggerServerEvent("sky_phone:media:request-upload", data or {})
cb({ success = true })