Initial commit
This commit is contained in:
345
core/camera.jai
Normal file
345
core/camera.jai
Normal file
@@ -0,0 +1,345 @@
|
||||
Camera_Type :: enum {
|
||||
ORTHOGRAPHIC;
|
||||
PERSPECTIVE;
|
||||
}
|
||||
|
||||
Camera_Buffer_Data :: struct {
|
||||
projection_matrix: Matrix4;
|
||||
view_matrix: Matrix4;
|
||||
position: Vector4;
|
||||
}
|
||||
|
||||
Camera :: struct {
|
||||
type : Camera_Type;
|
||||
|
||||
position : Vector3;
|
||||
rotation : struct {
|
||||
yaw : float;
|
||||
pitch : float;
|
||||
roll : float;
|
||||
}
|
||||
|
||||
aspect_ratio : float;
|
||||
fov : float;
|
||||
z_near : float;
|
||||
z_far : float;
|
||||
|
||||
forward : Vector3;
|
||||
up : Vector3;
|
||||
right : Vector3;
|
||||
world_up : Vector3 = .{0,1,0};
|
||||
|
||||
projection_matrix : Matrix4;
|
||||
view_matrix : Matrix4;
|
||||
|
||||
dirty : bool;
|
||||
}
|
||||
|
||||
create_perspective_camera :: (position: Vector3 = .{}, fov: float, aspect: float, yaw: float = 0.0, pitch: float = 0.0, roll: float = 0.0, z_near: float = 0.1, z_far: float = 1000.0) -> Camera {
|
||||
camera : Camera;
|
||||
camera.type = .PERSPECTIVE;
|
||||
camera.world_up = .{0,1,0};
|
||||
camera.position = position;
|
||||
camera.rotation.yaw = yaw;
|
||||
camera.rotation.pitch = pitch;
|
||||
camera.rotation.roll = roll;
|
||||
camera.aspect_ratio = aspect;
|
||||
camera.z_near = z_near;
|
||||
camera.z_far = z_far;
|
||||
camera.fov = fov;
|
||||
camera.projection_matrix = make_lh_projection_matrix(fov * (TAU / 360.0), aspect, z_near, z_far);
|
||||
update_view_matrix(*camera);
|
||||
|
||||
return camera;
|
||||
}
|
||||
|
||||
create_orthographic_camera :: (position: Vector3 = .{}, yaw: float = 0.0, pitch: float = 0.0, roll: float = 0.0, left: float, right: float, bottom: float, top: float, z_near: float = 0.1, z_far: float = 1000.0) -> Camera {
|
||||
camera : Camera;
|
||||
camera.type = .ORTHOGRAPHIC;
|
||||
camera.world_up = .{0,1,0};
|
||||
camera.position = position;
|
||||
camera.rotation.yaw = yaw;
|
||||
camera.rotation.pitch = pitch;
|
||||
camera.rotation.roll = roll;
|
||||
camera.aspect_ratio = 0.1;//aspect;
|
||||
camera.z_near = z_near;
|
||||
camera.z_far = z_far;
|
||||
camera.fov = 0.0;
|
||||
camera.projection_matrix = orthographic_lh_projection_matrix(left, right, bottom, top, z_near, z_far);
|
||||
update_view_matrix(*camera);
|
||||
|
||||
return camera;
|
||||
}
|
||||
|
||||
orthographic_lh_projection_matrix :: (left: float, right: float, bottom: float, top: float, near: float, far: float) -> Matrix4
|
||||
{
|
||||
m : Matrix4;
|
||||
|
||||
width := right - left;
|
||||
height := top - bottom;
|
||||
|
||||
m._11 = 2.0 / width;
|
||||
m._22 = 2.0 / height;
|
||||
|
||||
m._33 = 1 / (far - near);
|
||||
m._34 = -near / (near-far);
|
||||
m._44 = 1.0;
|
||||
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
make_lh_projection_matrix :: (fov_vertical: float, aspect_ratio_horizontal_over_vertical: float, z_near: float, z_far: float, x_offset:=0.0, y_offset:=0.0, depth_range_01:=false) -> Matrix4 {
|
||||
result := Matrix4_Identity;
|
||||
|
||||
tan_theta := tan(fov_vertical * 0.5);
|
||||
y_scale := 1 / tan_theta;
|
||||
x_scale := y_scale / aspect_ratio_horizontal_over_vertical;
|
||||
|
||||
result._11 = x_scale;
|
||||
result._22 = y_scale;
|
||||
result._33 = z_far / (z_far - z_near);
|
||||
result._34 = -z_near * z_far / (z_far - z_near);
|
||||
result._43 = 1;
|
||||
result._44 = 0;
|
||||
|
||||
result._13 = x_offset; // / w;
|
||||
result._23 = y_offset; // / h;
|
||||
|
||||
if depth_range_01 {
|
||||
// To map -1,1 depth range to 0,1 we transform z as follows: z' = z * 0.5 + 0.5
|
||||
result._33 = result._33 * 0.5 + result._43 * 0.5;
|
||||
result._34 = result._34 * 0.5 + result._44 * 0.5;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
world_to_screen :: (camera: Camera, world_position: Vector3) -> Vector3 {
|
||||
pos : Vector4;
|
||||
pos.x = world_position.x;
|
||||
pos.y = world_position.y;
|
||||
pos.z = world_position.z;
|
||||
pos.w = 1.0;
|
||||
position := camera.projection_matrix * camera.view_matrix * pos;
|
||||
position.x /= position.w;
|
||||
position.y /= position.w;
|
||||
position.z /= position.w;
|
||||
|
||||
screen_position : Vector3;
|
||||
screen_position.x = (position.x + 1.0) * 0.5 * cast(float)renderer.render_target_width;
|
||||
screen_position.y = (position.y + 1.0) * 0.5 * cast(float)renderer.render_target_height;
|
||||
screen_position.z = position.z;
|
||||
return screen_position;
|
||||
}
|
||||
|
||||
screen_to_world :: (camera: Camera, screen_position: Vector2) -> Vector3 {
|
||||
pos : Vector4;
|
||||
pos.x = (screen_position.x / cast(float)renderer.render_target_width) * 2.0 - 1.0;
|
||||
pos.y = (screen_position.y / cast(float)renderer.render_target_height) * 2.0 - 1.0;
|
||||
pos.z = 0.0;
|
||||
pos.w = 1.0;
|
||||
|
||||
result := inverse(camera.projection_matrix * camera.view_matrix) * pos;
|
||||
result.x /= result.w;
|
||||
result.y /= result.w;
|
||||
result.z /= result.w;
|
||||
|
||||
world_position : Vector3;
|
||||
world_position.x = result.x;
|
||||
world_position.y = result.y;
|
||||
world_position.z = result.z;
|
||||
|
||||
return world_position;
|
||||
}
|
||||
|
||||
normalized_screen_to_ray_v2 :: (camera: *Camera, screen_position: Vector2) -> Ray {
|
||||
nds : Vector2;
|
||||
nds.x = (2.0 * screen_position.x) - 1.0;
|
||||
nds.y = (2.0 * screen_position.y) - 1.0;
|
||||
|
||||
origin : Vector4;
|
||||
origin.x = nds.x;
|
||||
origin.y = nds.y;
|
||||
origin.z = 0.0;
|
||||
origin.w = 1.0;
|
||||
|
||||
far : Vector4;
|
||||
far.x = nds.x;
|
||||
far.y = nds.y;
|
||||
far.z = 1.0;
|
||||
far.w = 1.0;
|
||||
|
||||
inverse_view_proj := inverse(camera.projection_matrix * camera.view_matrix);
|
||||
ray_origin := inverse_view_proj * origin;
|
||||
ray_end := inverse_view_proj * far;
|
||||
|
||||
// The hero we didn't know we needed
|
||||
ray_origin /= ray_origin.w;
|
||||
ray_end /= ray_end.w;
|
||||
|
||||
ray : Ray;
|
||||
ray.origin = to_v3(ray_origin);
|
||||
ray.direction = normalize(to_v3(ray_end) - ray.origin);
|
||||
|
||||
return ray;
|
||||
}
|
||||
|
||||
|
||||
screen_to_ray_v2 :: (camera: Camera, screen_position: Vector2, screen_size: Vector2) -> Ray {
|
||||
nds : Vector2;
|
||||
nds.x = (2.0 * screen_position.x) / screen_size.x - 1.0;
|
||||
nds.y = (2.0 * screen_position.y) / screen_size.y - 1.0;
|
||||
|
||||
origin : Vector4;
|
||||
origin.x = nds.x;
|
||||
origin.y = nds.y;
|
||||
origin.z = 0.0;
|
||||
origin.w = 1.0;
|
||||
|
||||
far : Vector4;
|
||||
far.x = nds.x;
|
||||
far.y = nds.y;
|
||||
far.z = 1.0;
|
||||
far.w = 1.0;
|
||||
|
||||
inverse_view_proj := inverse(camera.projection_matrix * camera.view_matrix);
|
||||
ray_origin := inverse_view_proj * origin;
|
||||
ray_end := inverse_view_proj * far;
|
||||
|
||||
// The hero we didn't know we needed
|
||||
ray_origin /= ray_origin.w;
|
||||
ray_end /= ray_end.w;
|
||||
|
||||
ray : Ray;
|
||||
ray.origin = to_v3(ray_origin);
|
||||
ray.direction = normalize(to_v3(ray_end) - ray.origin);
|
||||
|
||||
return ray;
|
||||
}
|
||||
|
||||
screen_to_ray :: (camera: *Camera, screen_position: Vector2, screen_size: Vector2) -> Ray {
|
||||
ray : Ray;
|
||||
ray.origin = camera.position;
|
||||
|
||||
ray_nds : Vector3;
|
||||
ray_nds.x = (2.0 * screen_position.x) / screen_size.x - 1.0;
|
||||
ray_nds.y = (2.0 * screen_position.y) / screen_size.y - 1.0;
|
||||
ray_nds.z = 0.0;
|
||||
|
||||
ray_clip : Vector4;
|
||||
ray_clip.x = ray_nds.x;
|
||||
ray_clip.y = ray_nds.y;
|
||||
ray_clip.z = -1.0;
|
||||
ray_clip.w = 1.0;
|
||||
|
||||
ray_eye := inverse(camera.projection_matrix) * ray_clip;
|
||||
ray_eye.z = 1.0;
|
||||
ray_eye.w = 0.0;
|
||||
|
||||
ray_world := to_v3(inverse(camera.view_matrix) * ray_eye);
|
||||
ray.direction = normalize(ray_world);
|
||||
|
||||
return ray;
|
||||
}
|
||||
|
||||
set_fov :: (camera: *Camera, fov: float) {
|
||||
camera.fov = fov;
|
||||
camera.projection_matrix = make_lh_projection_matrix(fov * (TAU / 360.0), camera.aspect_ratio, camera.z_near, camera.z_far);
|
||||
camera.dirty = true;
|
||||
}
|
||||
|
||||
set_position :: (camera: *Camera, position: Vector3) {
|
||||
camera.position = position;
|
||||
update_view_matrix(camera);
|
||||
}
|
||||
|
||||
set_yaw :: (camera: *Camera, yaw: float) {
|
||||
camera.rotation.yaw = yaw;
|
||||
update_view_matrix(camera);
|
||||
}
|
||||
|
||||
set_pitch :: (camera: *Camera, pitch: float) {
|
||||
camera.rotation.pitch = pitch;
|
||||
update_view_matrix(camera);
|
||||
}
|
||||
|
||||
set_roll :: (camera: *Camera, roll: float) {
|
||||
camera.rotation.roll = roll;
|
||||
update_view_matrix(camera);
|
||||
}
|
||||
|
||||
set_pitch_yaw_roll :: (camera: *Camera, pitch: float, yaw: float, roll: float) {
|
||||
camera.rotation.pitch = pitch;
|
||||
camera.rotation.yaw = yaw;
|
||||
camera.rotation.roll = roll;
|
||||
update_view_matrix(camera);
|
||||
}
|
||||
|
||||
update_view_matrix :: (using camera: *Camera) {
|
||||
camera.rotation.pitch = clamp(camera.rotation.pitch, -89.0, 89.0);
|
||||
|
||||
pitch := rotation.pitch * DEGREES_TO_RADIANS;
|
||||
yaw := rotation.yaw * DEGREES_TO_RADIANS;
|
||||
roll := rotation.roll * DEGREES_TO_RADIANS;
|
||||
|
||||
pitch_mat := Matrix4.{
|
||||
1, 0, 0, 0,
|
||||
0, cos(pitch), sin(pitch), 0,
|
||||
0, -sin(pitch), cos(pitch), 0,
|
||||
0, 0, 0, 1};
|
||||
|
||||
yaw_mat := Matrix4.{
|
||||
cos(yaw), 0, -sin(yaw), 0,
|
||||
0, 1, 0, 0,
|
||||
sin(yaw), 0, cos(yaw), 0,
|
||||
0, 0, 0, 1};
|
||||
|
||||
roll_mat := Matrix4.{
|
||||
cos(roll), sin(roll), 0, 0,
|
||||
-sin(roll), cos(roll), 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1};
|
||||
|
||||
matrix := yaw_mat * pitch_mat * roll_mat;
|
||||
|
||||
camera.forward = .{0,0,1};
|
||||
camera.right = .{1,0,0};
|
||||
camera.up = .{0,1,0};
|
||||
|
||||
camera.forward = normalize(to_v3(matrix * Vector4.{camera.forward.x, camera.forward.y, camera.forward.z, 0.0}));
|
||||
camera.right = normalize(to_v3(matrix * Vector4.{camera.right.x, camera.right.y, camera.right.z, 0.0}));
|
||||
camera.up = normalize(to_v3(matrix * Vector4.{camera.up.x, camera.up.y, camera.up.z, 0.0}));
|
||||
|
||||
eye := camera.position + camera.forward;
|
||||
|
||||
//camera.forward = normalize(direction);
|
||||
//camera.right = normalize(cross_product(Vector3.{0,1,0}, camera.forward));
|
||||
//camera.up = normalize(cross_product(camera.forward, camera.right));
|
||||
|
||||
m := Matrix4_Identity;
|
||||
m._11 = camera.right.x;
|
||||
m._12 = camera.right.y;
|
||||
m._13 = camera.right.z;
|
||||
m._14 = -dot(camera.right, eye);
|
||||
|
||||
m._21 = camera.up.x;
|
||||
m._22 = camera.up.y;
|
||||
m._23 = camera.up.z;
|
||||
m._24 = -dot(camera.up, eye);
|
||||
|
||||
m._31 = camera.forward.x;
|
||||
m._32 = camera.forward.y;
|
||||
m._33 = camera.forward.z;
|
||||
m._34 = -dot(camera.forward, eye);
|
||||
|
||||
m._41 = 0.0;
|
||||
m._42 = 0.0;
|
||||
m._43 = 0.0;
|
||||
m._44 = 1.0;
|
||||
|
||||
camera.view_matrix = m;
|
||||
|
||||
camera.dirty = true;
|
||||
}
|
||||
Reference in New Issue
Block a user