208 lines
6.7 KiB
TypeScript
208 lines
6.7 KiB
TypeScript
|
|
type camera_object_t = {
|
|
base?: object_t;
|
|
data?: camera_data_t;
|
|
p?: mat4_t;
|
|
no_jitter_p?: mat4_t;
|
|
frame?: i32;
|
|
v?: mat4_t;
|
|
prev_v?: mat4_t;
|
|
vp?: mat4_t;
|
|
frustum_planes?: frustum_plane_t[];
|
|
};
|
|
|
|
let _camera_object_v: vec4_t = vec4_create();
|
|
let _camera_object_sphere_center: vec4_t = vec4_create();
|
|
|
|
function camera_object_create(data: camera_data_t): camera_object_t {
|
|
let raw: camera_object_t = {};
|
|
raw.no_jitter_p = mat4_identity();
|
|
raw.frame = 0;
|
|
raw.base = object_create(false);
|
|
raw.base.ext = raw;
|
|
raw.base.ext_type = "camera_object_t";
|
|
raw.data = data;
|
|
raw.prev_v = mat4_nan();
|
|
|
|
camera_object_build_proj(raw);
|
|
|
|
raw.v = mat4_identity();
|
|
raw.vp = mat4_identity();
|
|
|
|
if (data.frustum_culling) {
|
|
raw.frustum_planes = [];
|
|
for (let i: i32 = 0; i < 6; ++i) {
|
|
array_push(raw.frustum_planes, frustum_plane_create());
|
|
}
|
|
}
|
|
|
|
array_push(scene_cameras, raw);
|
|
return raw;
|
|
}
|
|
|
|
function camera_object_build_proj(raw: camera_object_t, screen_aspect: f32 = -1.0) {
|
|
if (raw.data.ortho != null) {
|
|
raw.p = mat4_ortho(raw.data.ortho[0], raw.data.ortho[1], raw.data.ortho[2], raw.data.ortho[3], raw.data.near_plane, raw.data.far_plane);
|
|
}
|
|
else {
|
|
if (screen_aspect < 0) {
|
|
screen_aspect = sys_w() / sys_h();
|
|
}
|
|
let aspect: f32 = raw.data.aspect ? raw.data.aspect : screen_aspect;
|
|
raw.p = mat4_persp(raw.data.fov, aspect, raw.data.near_plane, raw.data.far_plane);
|
|
}
|
|
raw.no_jitter_p = mat4_clone(raw.p);
|
|
}
|
|
|
|
function camera_object_remove(raw: camera_object_t) {
|
|
array_remove(scene_cameras, raw);
|
|
object_remove_super(raw.base);
|
|
}
|
|
|
|
function camera_object_render_frame(raw: camera_object_t) {
|
|
camera_object_proj_jitter(raw);
|
|
camera_object_build_mat(raw);
|
|
render_path_render_frame();
|
|
raw.prev_v = mat4_clone(raw.v);
|
|
}
|
|
|
|
function camera_object_proj_jitter(raw: camera_object_t) {
|
|
let w: i32 = render_path_current_w;
|
|
let h: i32 = render_path_current_h;
|
|
raw.p = mat4_clone(raw.no_jitter_p);
|
|
|
|
let i: i32 = raw.frame % 6;
|
|
|
|
if (context_raw.ddirty > 1 || context_raw.pdirty > 0) {
|
|
i = raw.frame % 2;
|
|
}
|
|
|
|
let x: f32 = 0.0;
|
|
let y: f32 = 0.0;
|
|
|
|
if (i == 0) { x = 0.5; y = 0.333; }
|
|
else if (i == 1) { x = -0.5; y = -0.333; }
|
|
else if (i == 2) { x = 0.25; y = 0.111; }
|
|
else if (i == 3) { x = -0.25; y = -0.111; }
|
|
else if (i == 4) { x = 0.375; y = 0.444; }
|
|
else { x = -0.375; y = -0.444; }
|
|
x *= 2;
|
|
y *= 2;
|
|
|
|
raw.p.m20 += x / w;
|
|
raw.p.m21 += y / h;
|
|
raw.frame++;
|
|
}
|
|
|
|
function camera_object_build_mat(raw: camera_object_t) {
|
|
transform_build_matrix(raw.base.transform);
|
|
|
|
// Prevent camera matrix scaling
|
|
// TODO: discards position affected by scaled camera parent
|
|
let sc: vec4_t = mat4_get_scale(raw.base.transform.world);
|
|
if (sc.x != 1.0 || sc.y != 1.0 || sc.z != 1.0) {
|
|
_camera_object_v = vec4_create(1.0 / sc.x, 1.0 / sc.y, 1.0 / sc.z);
|
|
raw.base.transform.world = mat4_scale(raw.base.transform.world, _camera_object_v);
|
|
}
|
|
|
|
raw.v = mat4_inv(raw.base.transform.world);
|
|
raw.vp = mat4_mult_mat(raw.v, raw.p);
|
|
|
|
if (raw.data.frustum_culling) {
|
|
camera_object_build_view_frustum(raw.vp, raw.frustum_planes);
|
|
}
|
|
|
|
// First time setting up previous V, prevents first frame flicker
|
|
if (mat4_isnan(raw.prev_v)) {
|
|
raw.prev_v = mat4_clone(raw.v);
|
|
}
|
|
}
|
|
|
|
function camera_object_right(raw: camera_object_t): vec4_t {
|
|
return vec4_norm(vec4_create(raw.base.transform.local.m00, raw.base.transform.local.m01, raw.base.transform.local.m02));
|
|
}
|
|
|
|
function camera_object_up(raw: camera_object_t): vec4_t {
|
|
return vec4_norm(vec4_create(raw.base.transform.local.m10, raw.base.transform.local.m11, raw.base.transform.local.m12));
|
|
}
|
|
|
|
function camera_object_look(raw: camera_object_t): vec4_t {
|
|
return vec4_norm(vec4_create(-raw.base.transform.local.m20, -raw.base.transform.local.m21, -raw.base.transform.local.m22));
|
|
}
|
|
|
|
function camera_object_right_world(raw: camera_object_t): vec4_t {
|
|
return vec4_norm(vec4_create(raw.base.transform.world.m00, raw.base.transform.world.m01, raw.base.transform.world.m02));
|
|
}
|
|
|
|
function camera_object_up_world(raw: camera_object_t): vec4_t {
|
|
return vec4_norm(vec4_create(raw.base.transform.world.m10, raw.base.transform.world.m11, raw.base.transform.world.m12));
|
|
}
|
|
|
|
function camera_object_look_world(raw: camera_object_t): vec4_t {
|
|
return vec4_norm(vec4_create(-raw.base.transform.world.m20, -raw.base.transform.world.m21, -raw.base.transform.world.m22));
|
|
}
|
|
|
|
function camera_object_build_view_frustum(vp: mat4_t, frustum_planes: frustum_plane_t[]) {
|
|
// Left plane
|
|
frustum_plane_set_components(frustum_planes[0], vp.m03 + vp.m00, vp.m13 + vp.m10, vp.m23 + vp.m20, vp.m33 + vp.m30);
|
|
// Right plane
|
|
frustum_plane_set_components(frustum_planes[1], vp.m03 - vp.m00, vp.m13 - vp.m10, vp.m23 - vp.m20, vp.m33 - vp.m30);
|
|
// Top plane
|
|
frustum_plane_set_components(frustum_planes[2], vp.m03 - vp.m01, vp.m13 - vp.m11, vp.m23 - vp.m21, vp.m33 - vp.m31);
|
|
// Bottom plane
|
|
frustum_plane_set_components(frustum_planes[3], vp.m03 + vp.m01, vp.m13 + vp.m11, vp.m23 + vp.m21, vp.m33 + vp.m31);
|
|
// Near plane
|
|
frustum_plane_set_components(frustum_planes[4], vp.m02, vp.m12, vp.m22, vp.m32);
|
|
// Far plane
|
|
frustum_plane_set_components(frustum_planes[5], vp.m03 - vp.m02, vp.m13 - vp.m12, vp.m23 - vp.m22, vp.m33 - vp.m32);
|
|
// Normalize planes
|
|
for (let i: i32 = 0; i < frustum_planes.length; ++i) {
|
|
let plane: frustum_plane_t = frustum_planes[i];
|
|
frustum_plane_normalize(plane);
|
|
}
|
|
}
|
|
|
|
function camera_object_sphere_in_frustum(frustum_planes: frustum_plane_t[], t: transform_t, radius_scale: f32 = 1.0, offset_x: f32 = 0.0, offset_y: f32 = 0.0, offset_z: f32 = 0.0): bool {
|
|
// Use scale when radius is changing
|
|
let radius: f32 = t.radius * radius_scale;
|
|
for (let i: i32 = 0; i < frustum_planes.length; ++i) {
|
|
let plane: frustum_plane_t = frustum_planes[i];
|
|
_camera_object_sphere_center = vec4_create(
|
|
transform_world_x(t) + offset_x,
|
|
transform_world_y(t) + offset_y,
|
|
transform_world_z(t) + offset_z);
|
|
// Outside the frustum
|
|
if (frustum_plane_dist_to_sphere(plane, _camera_object_sphere_center, radius) + radius * 2 < 0) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
type frustum_plane_t = {
|
|
normal?: vec4_t;
|
|
constant?: f32;
|
|
};
|
|
|
|
function frustum_plane_create(): frustum_plane_t {
|
|
let raw: frustum_plane_t = {};
|
|
raw.normal = vec4_create(1.0, 0.0, 0.0);
|
|
raw.constant = 0.0;
|
|
return raw;
|
|
}
|
|
|
|
function frustum_plane_normalize(raw: frustum_plane_t) {
|
|
let inv_normal_length: f32 = 1.0 / vec4_len(raw.normal);
|
|
raw.normal = vec4_mult(raw.normal, inv_normal_length);
|
|
raw.constant *= inv_normal_length;
|
|
}
|
|
|
|
function frustum_plane_dist_to_sphere(raw: frustum_plane_t, sphere_center: vec4_t, sphere_radius: f32): f32 {
|
|
return (vec4_dot(raw.normal, sphere_center) + raw.constant) - sphere_radius;
|
|
}
|
|
|
|
function frustum_plane_set_components(raw: frustum_plane_t, x: f32, y: f32, z: f32, w: f32) {
|
|
raw.normal = vec4_create(x, y, z);
|
|
raw.constant = w;
|
|
}
|