Files
prosperon/camera.cm
2026-01-19 01:06:51 -06:00

137 lines
3.7 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var cam = {}
/*
presentation can be one of
letterbox
overscan
stretch
.. or simply 'null' for no presentation
*/
function rect_contains_pt(rect, x, y) {
return x >= rect.x && x <= rect.x + rect.width &&
y >= rect.y && y <= rect.y + rect.height
}
var basecam = {
pos: {x:0,y:0},
ortho:true,
width: 1,
height: 1,
fov:50,
near_z:0,
far_z:1000,
anchor: {x:0.5,y:0.5},
rotation:[0,0,0,1],
presentation: "letterbox",
background: {r:1,g:1,b:1,a:0},
viewport: {x:0,y:0,width:1,height:1},
aspect_ratio() {
return this.y/this.x;
},
screen_rect() {
var src = { width: this.width, height: this.height }
var dst = { x: 0, y: 0, width: gameres.width, height: gameres.height }
return place_rect(src, dst, this.presentation, this.align_x, this.align_y, false)
},
rect() {
return {x:0, y:0, width: this.width, height: this.height}
},
sensor() {
return {
width: gameres.width,
height: gameres.height,
}
},
// The rectangle (in window pixels) where this cameras target will be drawn
screen_rect() {
var src = { width: this.width, height: this.height }
var dst = { x: 0, y: 0, width: gameres.width, height: gameres.height }
return place_rect(src, dst, this.presentation, this.align_x, this.align_y, false)
},
// --- Space converters ---------------------------------------------------
// World -> View UV [0..1] independent of pixels/letterbox
world_to_view_uv(wx, wy) {
var ax = this.anchor[0], ay = this.anchor[1]
var u = (wx - this.pos[0]) / this.width + ax
var v = (wy - this.pos[1]) / this.height + ay
// apply viewport (maps camera-local UV into sub-rect)
var vp = this.viewport
u = vp.x + u * vp.width
v = vp.y + v * vp.height
return { x: u, y: v }
},
// View UV -> World
view_uv_to_world(u, v) {
var vp = this.viewport
var uu = (u - vp.x) / vp.width
var vv = (v - vp.y) / vp.height
var ax = this.anchor[0], ay = this.anchor[1]
var wx = this.pos[0] + (uu - ax) * this.width
var wy = this.pos[1] + (vv - ay) * this.height
return { x: wx, y: wy }
},
// World -> Window pixels (what you want for hit-tests)
world_to_window(wx, wy) {
var uv = this.world_to_view_uv(wx, wy)
var sr = this.screen_rect()
var sx = sr.x + uv.x * sr.width
var sy = sr.y + uv.y * sr.height
return { x: sx, y: sy }
},
// Window pixels -> World (mouse picking)
window_to_world(sx, sy) {
var sr = this.screen_rect()
if (!rect_contains_pt(sr, sx, sy)) {
// outside letterbox bars; clamp or return null
// return null
}
var u = (sx - sr.x) / sr.width
var v = (sy - sr.y) / sr.height
return this.view_uv_to_world(u, v)
},
// World -> normalized window [0..1]
world_to_screen_norm(wx, wy) {
var p = this.world_to_window(wx, wy)
return { x: p.x / gameres.width, y: p.y / gameres.height }
},
// Normalized window [0..1] -> World
screen_norm_to_world(nx, ny) {
var sx = nx * gameres.width
var sy = ny * gameres.height
return this.window_to_world(sx, sy)
},
screen_to_world: function(sx, sy) {
// sx, sy are normalized screen coordinates (0-1), bottom left [0,0], top right [1,1]
var screen_rect = this.screen_rect()
var pixel_x = sx * gameres.width
var pixel_y = sy * gameres.height
var rel_x = (pixel_x - screen_rect.x) / screen_rect.width
var rel_y = (pixel_y - screen_rect.y) / screen_rect.height
var ax = this.anchor[0]
var ay = this.anchor[1]
var world_x = this.pos[0] + (rel_x - ax) * this.width
var world_y = this.pos[1] + (rel_y - ay) * this.height
return {x: world_x, y: world_y}
},
}
cam.make = function()
{
return meme(basecam)
}
return cam