137 lines
3.7 KiB
Plaintext
137 lines
3.7 KiB
Plaintext
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 camera’s 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
|