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;
|
||||
}
|
||||
271
core/console.jai
Normal file
271
core/console.jai
Normal file
@@ -0,0 +1,271 @@
|
||||
#import "File_Utilities";
|
||||
|
||||
console : *Console;
|
||||
|
||||
Command_Proc :: struct {
|
||||
name: string;
|
||||
proc: (arguments: [] string, buffer: *[..] Buffer_Entry);
|
||||
}
|
||||
|
||||
Buffer_Entry :: struct {
|
||||
text: string;
|
||||
color: Color;
|
||||
}
|
||||
|
||||
Console :: struct {
|
||||
buffer: [..] Buffer_Entry;
|
||||
current_string: string;
|
||||
commands: [..] Command_Proc;
|
||||
|
||||
active: bool;
|
||||
visible: bool;
|
||||
open_amount: float;
|
||||
animation_position: float; // 1.0 means fully visible
|
||||
|
||||
font: Font_Handle;
|
||||
rect_pipeline: Pipeline_State_Handle;
|
||||
text_pipeline: Pipeline_State_Handle;
|
||||
vb: Buffer_Handle;
|
||||
cursor_vb: Buffer_Handle;
|
||||
verts: [..] Colored_Vert;
|
||||
}
|
||||
|
||||
add_command :: (console: *Console, cmd: string, proc: (arguments: [] string, buffer: *[..] Buffer_Entry)) {
|
||||
command : Command_Proc;
|
||||
command.name = copy_string(cmd);
|
||||
command.proc = proc;
|
||||
|
||||
array_add(*console.commands, command);
|
||||
}
|
||||
|
||||
init_console :: () {
|
||||
console = New(Console);
|
||||
console.verts.allocator = temp;
|
||||
buffer_size := size_of(Colored_Vert) * 12;
|
||||
console.vb = create_vertex_buffer(renderer, null, xx buffer_size, stride=size_of(Colored_Vert), mappable=true);
|
||||
console.cursor_vb = create_vertex_buffer(renderer, null, xx buffer_size, stride=size_of(Colored_Vert), mappable=true);
|
||||
|
||||
{
|
||||
vs := create_vertex_shader(renderer, "../assets/shaders/ui_rect.hlsl", "VS");
|
||||
ps := create_pixel_shader(renderer, "../assets/shaders/ui_rect.hlsl", "PS");
|
||||
|
||||
layout : [2] Vertex_Data_Info;
|
||||
layout[0] = .{0, .POSITION2D, 0};
|
||||
layout[1] = .{0, .COLOR_WITH_ALPHA, 0};
|
||||
params : [0] Shader_Parameter;
|
||||
|
||||
console.rect_pipeline = create_pipeline_state(renderer, vs, ps, layout, params, blend_type=.TRANSPARENT);
|
||||
}
|
||||
|
||||
{
|
||||
vs := create_vertex_shader(renderer, "../assets/shaders/font.hlsl", "VS");
|
||||
ps := create_pixel_shader(renderer, "../assets/shaders/font.hlsl", "PS");
|
||||
|
||||
layout : [3] Vertex_Data_Info;
|
||||
layout[0] = .{0,.POSITION2D, 0};
|
||||
layout[1] = .{0,.TEXCOORD0, 0};
|
||||
layout[2] = .{0,.COLOR_WITH_ALPHA, 0};
|
||||
params : [0] Shader_Parameter;
|
||||
|
||||
console.text_pipeline = create_pipeline_state(renderer, vs, ps, layout, params, blend_type=.TRANSPARENT);
|
||||
}
|
||||
|
||||
console.font = create_font(renderer, "../assets/fonts/Inconsolata-Regular.ttf", 18);
|
||||
console.current_string = alloc_string(256);
|
||||
console.current_string.count = 0;
|
||||
|
||||
//add_command(console, "stats", toggle_stats);
|
||||
//add_command(console, "camset", save_camera);
|
||||
//add_command(console, "load", load_scene);
|
||||
//add_command(console, "copy", copy_scene);
|
||||
//add_command(console, "vsync", set_vsync);
|
||||
|
||||
//engine.console = console;
|
||||
}
|
||||
|
||||
find_command :: (console: *Console, cmd_str: string) -> Command_Proc, bool {
|
||||
for console.commands {
|
||||
if equal(it.name, cmd_str) {
|
||||
return it, true;
|
||||
}
|
||||
}
|
||||
|
||||
return .{}, false;
|
||||
}
|
||||
|
||||
update_console :: () {
|
||||
if key_down(.TILDE) {
|
||||
console.active = !console.active;
|
||||
input.has_char = false; // Make sure that the tilde is not used as the first input character in the console
|
||||
}
|
||||
|
||||
if console.active {
|
||||
if key_down(.BACKSPACE) {
|
||||
if console.current_string.count > 0 {
|
||||
console.current_string.count -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if key_down(.RETURN) {
|
||||
if console.current_string.count > 0 {
|
||||
// @Incomplete(niels): Using split with space will take spaces as part of the args.
|
||||
// So each subsequent space will be an argument itself.
|
||||
// This is a bit of a hacky fix for now
|
||||
arguments := split(console.current_string, " ");
|
||||
index := 0;
|
||||
while index < arguments.count - 1 {
|
||||
if arguments[index].count == 0 {
|
||||
arguments[index] = arguments[arguments.count - 1];
|
||||
arguments.count -= 1;
|
||||
} else {
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if arguments[arguments.count - 1].count == 0 {
|
||||
arguments.count -= 1;
|
||||
}
|
||||
|
||||
cmd, success := find_command(console, arguments[0]);
|
||||
|
||||
push_entry(*console.buffer, console.current_string, .{1,1,1,1});
|
||||
|
||||
if success {
|
||||
cmd.proc(arguments, *console.buffer);
|
||||
} else {
|
||||
push_entry(*console.buffer, console.current_string, .{1,0,0,1});
|
||||
}
|
||||
|
||||
console.current_string.count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if input.has_char {
|
||||
if console.current_string.count < 256 {
|
||||
console.current_string.data[console.current_string.count] = xx input.current_char;
|
||||
console.current_string.count += 1;
|
||||
input.has_char = false;
|
||||
}
|
||||
}
|
||||
|
||||
eat_all_input(*input);
|
||||
}
|
||||
}
|
||||
|
||||
make_vert :: (x: float, y: float, color: Color) -> Colored_Vert {
|
||||
vert : Colored_Vert;
|
||||
vert.position.x = x;
|
||||
vert.position.y = y;
|
||||
vert.color = color;
|
||||
return vert;
|
||||
}
|
||||
|
||||
add_rect :: (renderer: *Renderer, x: float, y: float, width: float, height: float, color: Color, verts: *[..] Colored_Vert) {
|
||||
inv_w := 1.0 / cast(float)renderer.render_target_width;
|
||||
inv_h := 1.0 / cast(float)renderer.render_target_height;
|
||||
|
||||
x = x * inv_w * 2.0 - 1.0;
|
||||
y = (cast(float)renderer.render_target_height - y) * inv_h * 2.0 - 1.0;
|
||||
w : float = width * inv_w * 2.0;
|
||||
h : float = height * inv_h * 2.0;
|
||||
|
||||
array_add(verts, make_vert(x + w, y - h, color));
|
||||
array_add(verts, make_vert(x, y - h, color));
|
||||
array_add(verts, make_vert(x, y, color));
|
||||
|
||||
array_add(verts, make_vert(x + w, y - h, color));
|
||||
array_add(verts, make_vert(x, y, color));
|
||||
array_add(verts, make_vert(x + w, y, color));
|
||||
}
|
||||
|
||||
render_console :: () {
|
||||
if console.active {
|
||||
console.visible = true;
|
||||
console.open_amount += dt * 3.0;
|
||||
} else {
|
||||
console.open_amount -= dt * 3.0;
|
||||
if console.open_amount <= 0.0 {
|
||||
console.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
console.open_amount = clamp(console.open_amount, 0.0, 1.0);
|
||||
|
||||
if !console.visible return;
|
||||
|
||||
offset_y := (1.0 - console.open_amount) * 200.0 - 75;
|
||||
|
||||
console.verts.count = 0;
|
||||
console_color : Color = .{48.0/255.0, 64.0/255.0, 36.0/255.0, 0.7};
|
||||
console_line_color : Color = .{18.0/255.0, 34.0/255.0, 6.0/255.0, 0.7};
|
||||
|
||||
add_rect(renderer, 0.0, cast(float)renderer.render_target_height - 200 + offset_y, cast(float)renderer.render_target_width, 200.0, console_color, *console.verts);
|
||||
add_rect(renderer, 0.0, cast(float)renderer.render_target_height - 30 + offset_y, cast(float)renderer.render_target_width, 30.0, console_line_color , *console.verts);
|
||||
|
||||
upload_data_to_buffer(renderer, console.vb, console.verts.data, cast(s32)console.verts.count * size_of(Colored_Vert));
|
||||
|
||||
push_cmd_set_draw_mode(renderer, .FILL);
|
||||
push_cmd_set_depth_write(renderer, false);
|
||||
push_cmd_set_pipeline_state(renderer, console.rect_pipeline);
|
||||
|
||||
push_cmd_set_vertex_buffer(renderer, console.vb);
|
||||
push_cmd_draw(renderer, console.verts.count);
|
||||
|
||||
push_cmd_set_pipeline_state(renderer, console.text_pipeline);
|
||||
font := *renderer.fonts[console.font - 1];
|
||||
push_cmd_set_texture(renderer, 0, font.texture);
|
||||
|
||||
size := get_text_size(renderer, ">", console.font);
|
||||
|
||||
render_data := bake_text(renderer, 5.0, size.y - offset_y, ">", console.font, .{1,1,1,1});
|
||||
push_cmd_set_vertex_buffer(renderer, render_data.vb);
|
||||
push_cmd_draw(renderer, render_data.vert_count);
|
||||
|
||||
x := 15.0;
|
||||
|
||||
if console.current_string.count > 0 {
|
||||
render_data := bake_text(renderer, x, size.y - offset_y, console.current_string, console.font, .{1,1,1,1});
|
||||
push_cmd_set_vertex_buffer(renderer, render_data.vb);
|
||||
push_cmd_draw(renderer, render_data.vert_count);
|
||||
|
||||
x = 10 + render_data.size.x + 2.0;
|
||||
}
|
||||
|
||||
y := 37.0;
|
||||
i := console.buffer.count - 1;
|
||||
count := 0;
|
||||
|
||||
while i >= 0 && count < 8 {
|
||||
defer i -= 1;
|
||||
defer count += 1;
|
||||
|
||||
entry := console.buffer[i];
|
||||
|
||||
render_data := bake_text(renderer, 15.0, y - offset_y, entry.text, console.font, entry.color);
|
||||
push_cmd_set_vertex_buffer(renderer, render_data.vb);
|
||||
push_cmd_draw(renderer, render_data.vert_count);
|
||||
|
||||
y += 20.0;
|
||||
}
|
||||
|
||||
console.verts.count = 0;
|
||||
|
||||
//#if !NEW_UI {
|
||||
add_rect(renderer, x, cast(float)renderer.render_target_height - 25 + offset_y, 10, 20.0, .{1,1,1,1}, *console.verts);
|
||||
//}
|
||||
|
||||
upload_data_to_buffer(renderer, console.cursor_vb, console.verts.data, cast(s32)console.verts.count * size_of(Colored_Vert));
|
||||
|
||||
push_cmd_set_draw_mode(renderer, .FILL);
|
||||
push_cmd_set_depth_write(renderer, false);
|
||||
push_cmd_set_pipeline_state(renderer, console.rect_pipeline);
|
||||
push_cmd_set_vertex_buffer(renderer, console.cursor_vb);
|
||||
push_cmd_draw(renderer, console.verts.count);
|
||||
}
|
||||
|
||||
push_entry :: (buffer: *[..] Buffer_Entry, text: string, color: Color) {
|
||||
entry : Buffer_Entry;
|
||||
entry.text = copy_string(text);
|
||||
entry.color = color;
|
||||
array_add(buffer, entry);
|
||||
}
|
||||
189
core/entity.jai
Normal file
189
core/entity.jai
Normal file
@@ -0,0 +1,189 @@
|
||||
Entity_Id :: #type, isa s64;
|
||||
|
||||
Entity_Flags :: enum_flags u8 {
|
||||
NONE;
|
||||
RENDERABLE;
|
||||
COLLISION;
|
||||
PHYSICS;
|
||||
STATIC;
|
||||
TRIGGER;
|
||||
|
||||
ANIMATED;
|
||||
|
||||
DONT_SAVE;
|
||||
}
|
||||
|
||||
Renderable_Type :: enum {
|
||||
MODEL;
|
||||
}
|
||||
|
||||
Entity_Material :: struct {
|
||||
base_color : Vector4;
|
||||
}
|
||||
|
||||
MAX_NODES :: 256;
|
||||
|
||||
Node_Render_Data :: struct {
|
||||
enabled : bool = true;
|
||||
transform: Transform;
|
||||
material : Entity_Material;
|
||||
has_sampled_animation: bool;
|
||||
|
||||
// Buffers
|
||||
transform_buffer: Buffer_Handle;
|
||||
material_buffer: Buffer_Handle;
|
||||
|
||||
bone_buffers : [MAX_BONES] Buffer_Handle;
|
||||
num_bones: s64;
|
||||
}
|
||||
|
||||
Renderable :: struct {
|
||||
visible: bool = true;
|
||||
type : Renderable_Type;
|
||||
|
||||
model: *Model;
|
||||
nodes: [MAX_NODES] Node_Render_Data;
|
||||
num_nodes: s64;
|
||||
}
|
||||
|
||||
MAX_CHILDREN :: 16;
|
||||
|
||||
Entity :: struct {
|
||||
id: Entity_Id;
|
||||
type : Type;
|
||||
|
||||
enabled: bool = true;
|
||||
|
||||
name: string;
|
||||
|
||||
parent: *Entity; @DontSerialize
|
||||
children: [MAX_CHILDREN] *Entity; @DontSerialize
|
||||
num_children: s64; @DontSerialize
|
||||
attach_node_index: s64 = -1; @DontSerialize
|
||||
|
||||
flags : Entity_Flags;
|
||||
|
||||
transform: Transform;
|
||||
|
||||
grid_position: Vector3i;
|
||||
snap_offset: Vector3;
|
||||
|
||||
renderable: Renderable; @DontSerialize
|
||||
animator: Animator; @DontSerialize
|
||||
|
||||
// Physics
|
||||
body : Physics_Body; @DontSerialize
|
||||
collider : Collider; @DontSerialize
|
||||
// End physics
|
||||
|
||||
remote_id: Entity_Id; @DontSerialize
|
||||
is_proxy: bool; @DontSerialize
|
||||
last_replication_time: float; @DontSerialize
|
||||
|
||||
_locator: Bucket_Locator; @DontSerialize
|
||||
scene: *Scene; @DontSerialize
|
||||
}
|
||||
|
||||
add_child :: (e: *Entity, child: *Entity, node_name: string = "") {
|
||||
set_parent(child, e, node_name);
|
||||
}
|
||||
|
||||
set_parent :: (e: *Entity, parent: *Entity, node_name: string = "") {
|
||||
parent.children[parent.num_children] = e;
|
||||
e.parent = parent;
|
||||
parent.num_children += 1;
|
||||
|
||||
for node, index: parent.renderable.model.nodes {
|
||||
if node.name == node_name {
|
||||
e.attach_node_index = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_base_color :: (e: *Entity, color: Vector4, node_name: string = "") {
|
||||
if e.renderable.type == .MODEL {
|
||||
for i: 0..e.renderable.num_nodes-1 {
|
||||
actual_node := e.renderable.model.nodes[i];
|
||||
if node_name.count == 0 || node_name == actual_node.name {
|
||||
data := *e.renderable.nodes[i];
|
||||
if data.material_buffer > 0 {
|
||||
material : Entity_Material = ---;
|
||||
material.base_color = color;
|
||||
upload_data_to_buffer(renderer, data.material_buffer, *material, size_of(Entity_Material));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_node_enabled :: (e: *Entity, node_name: string, enabled : bool) {
|
||||
if e.renderable.type == .MODEL {
|
||||
for i: 0..e.renderable.num_nodes-1 {
|
||||
actual_node := e.renderable.model.nodes[i];
|
||||
if node_name.count == 0 || node_name == actual_node.name {
|
||||
data := *e.renderable.nodes[i];
|
||||
data.enabled = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
load_model_into_entity :: (e: *Entity, model: *Model) {
|
||||
e.renderable.type = .MODEL;
|
||||
|
||||
assert(model.nodes.count <= MAX_NODES);
|
||||
|
||||
e.renderable.num_nodes = model.nodes.count;
|
||||
e.renderable.model = model;
|
||||
|
||||
for *model.nodes {
|
||||
data : Node_Render_Data;
|
||||
data.transform = it.transform;
|
||||
update_matrix(*it.transform);
|
||||
|
||||
if it.meshes.count > 0 {
|
||||
material : Entity_Material = ---;
|
||||
material.base_color = it.material_defaults[0].base_color; // @Incomplete: What if there are multiple meshes?
|
||||
|
||||
data.material = material;
|
||||
data.transform_buffer = create_constant_buffer(renderer, null, size_of(Matrix4), mappable=true);
|
||||
data.material_buffer = create_constant_buffer(renderer, *material, size_of(Entity_Material), mappable=true);
|
||||
data.num_bones = it.num_bones;
|
||||
|
||||
if it.num_bones > 0 {
|
||||
for bone_index: 0..it.num_bones-1 {
|
||||
data.bone_buffers[bone_index] = create_constant_buffer(renderer, null, size_of(Matrix4) * MAX_BONES, mappable=true);
|
||||
}
|
||||
}
|
||||
}
|
||||
e.renderable.nodes[it_index] = data;
|
||||
}
|
||||
}
|
||||
|
||||
destroy_entity :: (e: *Entity, remove_from_scene: bool = true) {
|
||||
free(e.name);
|
||||
|
||||
for 0..e.renderable.num_nodes-1 {
|
||||
node_data := e.renderable.nodes[it];
|
||||
|
||||
if node_data.transform_buffer > 0 {
|
||||
destroy_buffer(renderer, node_data.transform_buffer);
|
||||
}
|
||||
|
||||
if node_data.material_buffer > 0 {
|
||||
destroy_buffer(renderer, node_data.material_buffer);
|
||||
}
|
||||
|
||||
for bi: 0..node_data.num_bones-1 {
|
||||
destroy_buffer(renderer, node_data.bone_buffers[bi]);
|
||||
}
|
||||
}
|
||||
|
||||
if remove_from_scene {
|
||||
array_unordered_remove_by_value(*game_state.current_scene.entities, e);
|
||||
if e.type == {
|
||||
case Block; bucket_array_remove(*game_state.current_scene.by_type._Block, e._locator);
|
||||
}
|
||||
}
|
||||
}
|
||||
19
core/fps.jai
Normal file
19
core/fps.jai
Normal file
@@ -0,0 +1,19 @@
|
||||
FPS_COUNT_AMOUNT :: 8;
|
||||
average_fps: int;
|
||||
fps_counts: [FPS_COUNT_AMOUNT] int;
|
||||
fps_count_cursor: int;
|
||||
|
||||
update_fps_counter :: (dt: float) {
|
||||
fps_counts[fps_count_cursor] = cast(int)(1.0/dt);
|
||||
fps_count_cursor += 1;
|
||||
if fps_count_cursor == FPS_COUNT_AMOUNT {
|
||||
fps_count_cursor = 0;
|
||||
}
|
||||
|
||||
total_fps : int;
|
||||
for 0..FPS_COUNT_AMOUNT-1 {
|
||||
total_fps += fps_counts[it];
|
||||
}
|
||||
|
||||
average_fps = total_fps / FPS_COUNT_AMOUNT;
|
||||
}
|
||||
366
core/math.jai
Normal file
366
core/math.jai
Normal file
@@ -0,0 +1,366 @@
|
||||
DEGREES_TO_RADIANS : float : 0.017453292;
|
||||
RADIANS_TO_DEGREES : float : 57.295779;
|
||||
|
||||
hfov_to_vfov :: (hfov: float, aspect_ratio: float) -> float {
|
||||
return 2.0 * atan(tan(hfov * DEGREES_TO_RADIANS * 0.5) / aspect_ratio) * RADIANS_TO_DEGREES;
|
||||
}
|
||||
|
||||
lerp_angle :: (from: float, to: float, weight: float) -> float {
|
||||
start_angle := fmod_cycling(from, 2*PI);
|
||||
end_angle := fmod_cycling(to, 2*PI);
|
||||
|
||||
angle_distance := fmod_cycling(end_angle - start_angle + PI, 2 * PI) - PI;
|
||||
|
||||
interpolated_angle := start_angle + angle_distance * weight;
|
||||
return fmod_cycling(interpolated_angle, 2*PI);
|
||||
}
|
||||
|
||||
short_angle_dist :: (from: float, to: float) -> float {
|
||||
max_angle :: PI * 2.0;
|
||||
difference := fmod_cycling(to - from, max_angle);
|
||||
return fmod_cycling(2.0 * difference, max_angle) - difference;
|
||||
}
|
||||
|
||||
degrees_to_radians :: (degrees: float) -> float#expand {
|
||||
constant : float : 0.01745329;
|
||||
return degrees * constant;
|
||||
}
|
||||
|
||||
radians_to_degrees :: (radians: float) -> float#expand {
|
||||
constant : float : 57.295779;
|
||||
return radians * constant;
|
||||
}
|
||||
|
||||
Vector2i :: struct {
|
||||
x: s32;
|
||||
y: s32;
|
||||
}
|
||||
|
||||
Vector3i :: struct {
|
||||
x: s32;
|
||||
y: s32;
|
||||
z: s32;
|
||||
}
|
||||
|
||||
Color :: #type,isa Vector4;
|
||||
|
||||
Colored_Vert :: struct {
|
||||
position: Vector2;
|
||||
color : Color;
|
||||
}
|
||||
|
||||
AABB :: struct {
|
||||
min: Vector3 = .{FLOAT32_INFINITY, FLOAT32_INFINITY, FLOAT32_INFINITY};
|
||||
max: Vector3 = .{-FLOAT32_INFINITY, -FLOAT32_INFINITY, -FLOAT32_INFINITY};
|
||||
}
|
||||
|
||||
apply_min_max :: (min: *Vector3, max: *Vector3, p: Vector3) {
|
||||
if p.x < min.x min.x = p.x;
|
||||
if p.y < min.y min.y = p.y;
|
||||
if p.z < min.z min.z = p.z;
|
||||
if p.x > max.x max.x = p.x;
|
||||
if p.y > max.y max.y = p.y;
|
||||
if p.z > max.z max.z = p.z;
|
||||
}
|
||||
|
||||
point_inside_aabb :: (aabb: AABB, point: Vector3) -> bool {
|
||||
return point.x >= aabb.min.x && point.x <= aabb.max.x
|
||||
&& point.y >= aabb.min.y && point.y <= aabb.max.y
|
||||
&& point.z >= aabb.min.z && point.z <= aabb.max.z;
|
||||
}
|
||||
|
||||
operator == :: inline (a: Vector3i, b: Vector3i) -> bool {
|
||||
return a.x == b.x && a.y == b.y && a.z == b.z;
|
||||
}
|
||||
|
||||
operator + :: inline (a: Vector3i, b: Vector3i) -> Vector3i {
|
||||
return .{a.x + b.x, a.y + b.y, a.z + b.z};
|
||||
}
|
||||
|
||||
operator - :: inline (a: Vector3i, b: Vector3i) -> Vector3i {
|
||||
return .{a.x - b.x, a.y - b.y, a.z - b.z};
|
||||
}
|
||||
|
||||
to_v4 :: (v3: Vector3) -> Vector4 {
|
||||
v4 : Vector4;
|
||||
v4.x = v3.x;
|
||||
v4.y = v3.y;
|
||||
v4.z = v3.z;
|
||||
v4.w = 1.0;
|
||||
return v4;
|
||||
}
|
||||
|
||||
to_v3 :: (v4: Vector4) -> Vector3 {
|
||||
v3 : Vector3;
|
||||
v3.x = v4.x;
|
||||
v3.y = v4.y;
|
||||
v3.z = v4.z;
|
||||
return v3;
|
||||
}
|
||||
|
||||
to_v3 :: (v2: Vector2) -> Vector3 {
|
||||
v : Vector3;
|
||||
v.x = v2.x;
|
||||
v.y = v2.y;
|
||||
return v;
|
||||
}
|
||||
|
||||
to_v2 :: (v3: Vector3) -> Vector2 {
|
||||
v : Vector2;
|
||||
v.x = v3.x;
|
||||
v.y = v3.y;
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
round :: (val: float) -> float {
|
||||
return floor(val + 0.5);
|
||||
}
|
||||
|
||||
move_towards :: (origin: float, target: float, amount: float) -> float {
|
||||
if origin < target {
|
||||
return min(origin + amount, target);
|
||||
} else if origin > target {
|
||||
return max(origin - amount, target);
|
||||
} else {
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
move_towards :: (origin: Vector3, target: Vector3, amount: float) -> Vector3 {
|
||||
result : Vector3;
|
||||
dir := normalize(target - origin);
|
||||
result.x = move_towards(origin.x, target.x, abs(dir.x) * amount);
|
||||
result.y = move_towards(origin.y, target.y, abs(dir.y) * amount);
|
||||
result.z = move_towards(origin.z, target.z, abs(dir.z) * amount);
|
||||
return result;
|
||||
}
|
||||
|
||||
smooth_damp :: (current: Vector3, target: Vector3, current_velocity: *Vector3, smooth_time: float, max_speed: float, delta_time: float) -> Vector3 {
|
||||
output_x := 0.0;
|
||||
output_y := 0.0;
|
||||
output_z := 0.0;
|
||||
|
||||
// Based on Game Programming Gems 4 Chapter 1.10
|
||||
smooth_time = max(0.0001, smooth_time);
|
||||
omega := 2.0 / smooth_time;
|
||||
|
||||
x := omega * delta_time;
|
||||
exp := 1.0 / (1.0 + x + 0.48 * x * x + 0.235 * x * x * x);
|
||||
|
||||
change_x := current.x - target.x;
|
||||
change_y := current.y - target.y;
|
||||
change_z := current.z - target.z;
|
||||
original_to := target;
|
||||
|
||||
// Clamp maximum speed
|
||||
max_change := max_speed * smooth_time;
|
||||
|
||||
max_change_sq := max_change * max_change;
|
||||
sqrmag := change_x * change_x + change_y * change_y + change_z * change_z;
|
||||
if sqrmag > max_change_sq {
|
||||
mag := cast(float)sqrt(sqrmag);
|
||||
change_x = change_x / mag * max_change;
|
||||
change_y = change_y / mag * max_change;
|
||||
change_z = change_z / mag * max_change;
|
||||
}
|
||||
|
||||
final_target := target;
|
||||
final_target.x = current.x - change_x;
|
||||
final_target.y = current.y - change_y;
|
||||
final_target.z = current.z - change_z;
|
||||
|
||||
temp_x := (current_velocity.x + omega * change_x) * delta_time;
|
||||
temp_y := (current_velocity.y + omega * change_y) * delta_time;
|
||||
temp_z := (current_velocity.z + omega * change_z) * delta_time;
|
||||
|
||||
current_velocity.x = (current_velocity.x - omega * temp_x) * exp;
|
||||
current_velocity.y = (current_velocity.y - omega * temp_y) * exp;
|
||||
current_velocity.z = (current_velocity.z - omega * temp_z) * exp;
|
||||
|
||||
output_x = final_target.x + (change_x + temp_x) * exp;
|
||||
output_y = final_target.y + (change_y + temp_y) * exp;
|
||||
output_z = final_target.z + (change_z + temp_z) * exp;
|
||||
|
||||
// Prevent overshooting
|
||||
orig_minus_current_x := original_to.x - current.x;
|
||||
orig_minus_current_y := original_to.y - current.y;
|
||||
orig_minus_current_z := original_to.z - current.z;
|
||||
out_minus_orig_x := output_x - original_to.x;
|
||||
out_minus_orig_y := output_y - original_to.y;
|
||||
out_minus_orig_z := output_z - original_to.z;
|
||||
|
||||
if orig_minus_current_x * out_minus_orig_x + orig_minus_current_y * out_minus_orig_y + orig_minus_current_z * out_minus_orig_z > 0 {
|
||||
output_x = original_to.x;
|
||||
output_y = original_to.y;
|
||||
output_z = original_to.z;
|
||||
|
||||
current_velocity.x = (output_x - original_to.x) / delta_time;
|
||||
current_velocity.y = (output_y - original_to.y) / delta_time;
|
||||
current_velocity.z = (output_z - original_to.z) / delta_time;
|
||||
}
|
||||
|
||||
return .{output_x, output_y, output_z};
|
||||
}
|
||||
|
||||
smooth_damp :: (current: float, target: float, current_velocity: *float, smooth_time: float, max_speed: float, delta_time: float) -> float {
|
||||
smooth_time = max(0.0001, smooth_time);
|
||||
omega := 2.0 / smooth_time;
|
||||
|
||||
x := omega * delta_time;
|
||||
exp := 1.0 / (1.0 + x + 0.48 * x * x + 0.235 * x * x * x);
|
||||
change := current - target;
|
||||
original_to := target;
|
||||
|
||||
// Clamp maximum speed
|
||||
max_change := max_speed * smooth_time;
|
||||
change = clamp(change, -max_change, max_change);
|
||||
target = current - change;
|
||||
|
||||
temp := (<<current_velocity + omega * change) * delta_time;
|
||||
<<current_velocity = (<<current_velocity - omega * temp) * exp;
|
||||
output := target + (change + temp) * exp;
|
||||
|
||||
// Prevent overshooting
|
||||
if (original_to - current > 0.0) == (output > original_to) {
|
||||
output = original_to;
|
||||
<<current_velocity = (output - original_to) / delta_time;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
random_in_unit_circle :: () -> Vector2 {
|
||||
result : Vector2;
|
||||
angle := random_get_within_range(0.0, PI * 2.0);
|
||||
radius := random_get_within_range(0.0, 1.0);
|
||||
result.x = radius * cos(angle);
|
||||
result.y = radius * sin(angle);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
random_in_unit_sphere :: () -> Vector3 {
|
||||
result : Vector3;
|
||||
result.x = random_get_within_range(-1.0, 1.0);
|
||||
result.y = random_get_within_range(-1.0, 1.0);
|
||||
result.z = random_get_within_range(-1.0, 1.0);
|
||||
|
||||
return normalize(result);
|
||||
}
|
||||
|
||||
look_at_lh :: (position: Vector3, target: Vector3, up: Vector3) -> Matrix4 {
|
||||
z_axis := normalize(target - position);
|
||||
x_axis := normalize(cross(up, z_axis));
|
||||
y_axis := cross(z_axis, x_axis);
|
||||
|
||||
m : Matrix4;
|
||||
|
||||
m._11 = x_axis.x;
|
||||
m._12 = x_axis.y;
|
||||
m._13 = x_axis.z;
|
||||
m._14 = -dot(x_axis, position);
|
||||
|
||||
m._21 = y_axis.x;
|
||||
m._22 = y_axis.y;
|
||||
m._23 = y_axis.z;
|
||||
m._24 = -dot(y_axis, position);
|
||||
|
||||
m._31 = z_axis.x;
|
||||
m._32 = z_axis.y;
|
||||
m._33 = z_axis.z;
|
||||
m._34 = -dot(z_axis, position);
|
||||
|
||||
m._41 = 0.0;
|
||||
m._42 = 0.0;
|
||||
m._43 = 0.0;
|
||||
m._44 = 1.0;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
project :: (v1: Vector3, v2: Vector3) -> Vector3 {
|
||||
return (v2*v1)/(v2*v2)*v2;
|
||||
}
|
||||
|
||||
reject :: (v1: Vector3, v2: Vector3) -> Vector3 {
|
||||
return v1 - project(v1, v2);
|
||||
}
|
||||
|
||||
horizontal_distance :: inline (v1: Vector3, v2: Vector3) -> float {
|
||||
return distance(Vector2.{v1.x,v1.z}, Vector2.{v2.x,v2.z});
|
||||
}
|
||||
|
||||
horizontal_direction :: inline (from: Vector3, to: Vector3) -> Vector3 {
|
||||
adjusted_from := from;
|
||||
adjusted_from.y = to.y;
|
||||
return normalize(to - adjusted_from);
|
||||
}
|
||||
|
||||
closest_point_on_line_segment :: (a: Vector3, b: Vector3, point: Vector3, ignore_y: bool = false) -> Vector3, float {
|
||||
actual_a := a;
|
||||
actual_b := b;
|
||||
|
||||
if ignore_y {
|
||||
actual_a.y = point.y;
|
||||
actual_b.y = point.y;
|
||||
}
|
||||
|
||||
ab := actual_b - actual_a;
|
||||
ap := point - actual_a;
|
||||
|
||||
proj := dot(ap, ab);
|
||||
ab_len_sq := length_squared(ab);
|
||||
d := proj / ab_len_sq;
|
||||
|
||||
cp : Vector3 = ---;
|
||||
if d <= 0.0 {
|
||||
cp = actual_a;
|
||||
} else if d >= 1.0 {
|
||||
cp = actual_b;
|
||||
} else {
|
||||
cp = actual_a + ab * d;
|
||||
}
|
||||
|
||||
return cp, distance(point, cp);
|
||||
}
|
||||
|
||||
reflect :: (incident: Vector3, normal: Vector3) -> Vector3 {
|
||||
dot_product := dot(incident, normal);
|
||||
reflected : Vector3 = ---;
|
||||
reflected.x = incident.x - 2.0 * dot_product * normal.x;
|
||||
reflected.y = incident.y - 2.0 * dot_product * normal.y;
|
||||
reflected.z = incident.z - 2.0 * dot_product * normal.z;
|
||||
return reflected;
|
||||
}
|
||||
|
||||
quat_to_pitch_yaw_roll :: (using q: Quaternion) -> pitch: float, yaw: float, roll: float {
|
||||
roll := atan2(2*y*w - 2*x*z, 1 - 2*y*y - 2*z*z);
|
||||
pitch := atan2(2*x*w - 2*y*z, 1 - 2*x*x - 2*z*z);
|
||||
yaw := asin(2*x*y + 2*z*w);
|
||||
return pitch, yaw, roll;
|
||||
}
|
||||
|
||||
ease_in :: (x : float, exp : int = 2) -> float {
|
||||
return pow(x, xx exp);
|
||||
}
|
||||
|
||||
ease_out :: (x : float, exp : int = 2) -> float {
|
||||
return 1.0 - pow(1 - x, xx exp);
|
||||
}
|
||||
|
||||
ease_in_sine :: (x: float) -> float {
|
||||
return 1.0 - cos((x * PI) * 0.5);
|
||||
}
|
||||
|
||||
ease_out_sine :: (x: float) -> float {
|
||||
return 1.0 - sin((x * PI) * 0.5);
|
||||
}
|
||||
|
||||
ease_in_out_sine :: (x: float) -> float {
|
||||
return -(cos(PI * x) - 1.0) * 0.5;
|
||||
}
|
||||
|
||||
#import "PCG";
|
||||
#import "Math";
|
||||
|
||||
87
core/parray.jai
Normal file
87
core/parray.jai
Normal file
@@ -0,0 +1,87 @@
|
||||
PArray :: struct (Data_Type : Type, Handle_Type : Type) {
|
||||
data: [..] Data_Type;
|
||||
indices: [..] u32;
|
||||
}
|
||||
|
||||
parray_reset :: (using array: *PArray) {
|
||||
array_reset(*array.data);
|
||||
array_reset(*array.indices);
|
||||
}
|
||||
|
||||
parray_free :: (using array: PArray) {
|
||||
array_free(array.data);
|
||||
array_free(array.indices);
|
||||
}
|
||||
|
||||
parray_reserve :: (using parray: *PArray, capacity: s32) {
|
||||
if capacity > 0 {
|
||||
array_reserve(*data, capacity);
|
||||
array_reserve(*indices, capacity);
|
||||
}
|
||||
}
|
||||
|
||||
parray_get :: (using parray: PArray, handle: parray.Handle_Type) -> *parray.Data_Type {
|
||||
index := indices[handle - 1] - 1;
|
||||
return *data[index];
|
||||
}
|
||||
|
||||
parray_get_val :: (using parray: PArray, handle: parray.Handle_Type) -> parray.Data_Type {
|
||||
assert(xx handle > 0 && xx handle <= indices.count);
|
||||
index := indices[handle - 1] - 1;
|
||||
return data[index];
|
||||
}
|
||||
|
||||
parray_add :: (using parray: *PArray, value: parray.Data_Type) -> parray.Handle_Type {
|
||||
array_add(*data, value);
|
||||
index := cast(u32)data.count;
|
||||
|
||||
handle : Handle_Type = 0;
|
||||
|
||||
// find the next empty index
|
||||
for *val, i: indices {
|
||||
if <<val == 0 {
|
||||
<<val = index;
|
||||
handle = cast(Handle_Type)i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if handle == 0 {
|
||||
array_add(*indices, index);
|
||||
handle = cast(Handle_Type)indices.count;
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
parray_remove :: (using parray: *PArray, handle: parray.Handle_Type) {
|
||||
index := indices[handle - 1] - 1;
|
||||
indices[handle - 1] = 0;
|
||||
|
||||
if data.count == 1 {
|
||||
data.count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
data[index] = data[data.count-1];
|
||||
|
||||
for * indices {
|
||||
if <<it == data.count {
|
||||
<<it = index + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
data.count -= 1;
|
||||
}
|
||||
|
||||
for_expansion :: (parray: *PArray, body: Code, flags: For_Flags) #expand {
|
||||
for *value, `it_index: parray.data {
|
||||
#if flags & .POINTER {
|
||||
`it := value;
|
||||
} else {
|
||||
`it := <<value;
|
||||
}
|
||||
#insert body;
|
||||
}
|
||||
}
|
||||
239
core/ray.jai
Normal file
239
core/ray.jai
Normal file
@@ -0,0 +1,239 @@
|
||||
Ray :: struct {
|
||||
origin : Vector3;
|
||||
direction : Vector3;
|
||||
}
|
||||
|
||||
ray_aabb_intersection :: (origin: Vector3, direction: Vector3, aabb: AABB) -> float {
|
||||
t1 := (aabb.min.x - origin.x) / direction.x;
|
||||
t2 := (aabb.max.x - origin.x) / direction.x;
|
||||
t3 := (aabb.min.y - origin.y) / direction.y;
|
||||
t4 := (aabb.max.y - origin.y) / direction.y;
|
||||
t5 := (aabb.min.z - origin.z) / direction.z;
|
||||
t6 := (aabb.max.z - origin.z) / direction.z;
|
||||
|
||||
tmin := max(max(min(t1, t2), min(t3, t4)), min(t5, t6));
|
||||
tmax := min(min(max(t1, t2), max(t3, t4)), max(t5, t6));
|
||||
|
||||
// if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behing us
|
||||
if tmax < 0 {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// if tmin > tmax, ray doesn't intersect AABB
|
||||
if tmin > tmax {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if tmin < 0 {
|
||||
return tmax;
|
||||
}
|
||||
return tmin;
|
||||
}
|
||||
|
||||
ray_sphere_intersect :: (ray: Ray, center: Vector3, radius: float) -> bool {
|
||||
t : float;
|
||||
oc := ray.origin - center;
|
||||
a := dot(ray.direction, ray.direction);
|
||||
b := 2.0 * dot(oc, ray.direction);
|
||||
c := dot(oc, oc) - radius * radius;
|
||||
discriminant := b * b - 4 * a * c;
|
||||
|
||||
if discriminant > 0 {
|
||||
// Two intersection points, choose the closest one (smallest positive t)
|
||||
t1 := (-b - sqrt(discriminant)) / (2.0 * a);
|
||||
t2 := (-b + sqrt(discriminant)) / (2.0 * a);
|
||||
|
||||
if t1 > 0 || t2 > 0 {
|
||||
t = ifx (t1 < t2) then t1 else t2;
|
||||
return true;
|
||||
}
|
||||
} else if discriminant == 0 {
|
||||
// One intersection point
|
||||
t = -b / (2.0 * a);
|
||||
return t > 0;
|
||||
}
|
||||
|
||||
// No intersection
|
||||
return false;
|
||||
}
|
||||
|
||||
ray_plane_intersection :: (ray: Ray, p: Vector3, n: Vector3) -> bool, float {
|
||||
denom := dot(n, ray.direction);
|
||||
if abs(denom) > 0.0001 {
|
||||
offset := p - ray.origin;
|
||||
t := dot(offset, n) / denom;
|
||||
return t >= 0.0, t;
|
||||
}
|
||||
|
||||
return false, 0.0;
|
||||
}
|
||||
|
||||
ray_triangle_intersect :: (origin: Vector3, dir: Vector3, v0: Vector3, v1: Vector3, v2: Vector3) -> bool, float, Vector3 {
|
||||
EPSILON := 0.0000001;
|
||||
edge1 := v1 - v0;
|
||||
edge2 := v2 - v0;
|
||||
h := cross(dir, edge2);
|
||||
a := dot(edge1, h);
|
||||
|
||||
if a > -EPSILON && a < EPSILON return false, 0.0, .{0,0,0}; // This ray is parallel to this triangle.
|
||||
|
||||
f := 1.0/a;
|
||||
s := origin - v0;
|
||||
u := f * dot(s, h);
|
||||
|
||||
if u < 0.0 || u > 1.0 return false, 0.0, .{0,0,0};
|
||||
|
||||
q := cross(s, edge1);
|
||||
v := f * dot(dir, q);
|
||||
|
||||
if v < 0.0 || u + v > 1.0 return false, 0.0, .{0,0,0};
|
||||
|
||||
// At this stage we can compute t to find out where the intersection point is on the line.
|
||||
t := f * dot(edge2, q);
|
||||
if t > EPSILON { // ray intersection
|
||||
//outIntersectionPoint = rayOrigin + rayVector * t;
|
||||
normal := normalize(cross(edge1, edge2));
|
||||
return true, t, normal;
|
||||
}
|
||||
else // This means that there is a line intersection but not a ray intersection.
|
||||
return false, 0.0, .{0,0,0};
|
||||
//e0 := v1 - v0;
|
||||
//e1 := v2 - v0;
|
||||
//pvec := cross(dir, e1);
|
||||
|
||||
//det := dot(pvec, e0);
|
||||
|
||||
//if det > 0 {
|
||||
// inv_det := 1.0 / det;
|
||||
// tvec := origin - v0;
|
||||
// u := inv_det * dot(tvec, pvec);
|
||||
|
||||
// if u < 0.0 || u > 1.0 {
|
||||
// return false, 0.0;
|
||||
// }
|
||||
|
||||
// qvec := cross(tvec, e0);
|
||||
// v := inv_det * dot(qvec, dir);
|
||||
|
||||
// if v < 0.0 || u + v > 1.0 {
|
||||
// return false, 0.0;
|
||||
// }
|
||||
|
||||
// return true, dot(e1, qvec) * inv_det;
|
||||
//}
|
||||
|
||||
//return false, 0.0;
|
||||
}
|
||||
|
||||
distance_from_ray_to_point :: (ray: Ray, p: Vector3) -> float {
|
||||
pb := p - ray.origin;
|
||||
t0 := dot(ray.direction, pb) / dot(ray.direction, ray.direction);
|
||||
if t0 <= 0.0 {
|
||||
return length(pb);
|
||||
} else if t0 > 0.0 && t0 < 1.0 {
|
||||
return length(p - (ray.origin + t0 * ray.direction));
|
||||
} else {
|
||||
return length(p - (ray.origin + ray.direction));
|
||||
}
|
||||
}
|
||||
|
||||
closest_point_on_ray :: (from: Vector3, dir: Vector3, p: Vector3) -> Vector3 {
|
||||
lhs := p - from;
|
||||
dot_p := dot(lhs, dir);
|
||||
return from + dir * dot_p;
|
||||
}
|
||||
|
||||
closest_distance_between_rays :: (l1: Ray, l2: Ray) -> distance: float, t1: float, t2: float {
|
||||
u := l1.direction;
|
||||
v := l2.direction;
|
||||
w := l1.origin - l2.origin;
|
||||
a := dot(u, u);
|
||||
b := dot(u, v);
|
||||
c := dot(v, v);
|
||||
d := dot(u, w);
|
||||
e := dot(v, w);
|
||||
D := a * c - b * b;
|
||||
|
||||
sc: float;
|
||||
tc: float;
|
||||
|
||||
if D < 0.00001 {
|
||||
sc = 0.0;
|
||||
|
||||
if b > c {
|
||||
tc = d / b;
|
||||
} else {
|
||||
tc = e / c;
|
||||
}
|
||||
} else {
|
||||
sc = ((b * e) - (c * d)) / D;
|
||||
tc = ((a * e) - (b * d)) / D;
|
||||
}
|
||||
|
||||
P1 := u * sc;
|
||||
P2 := v * tc;
|
||||
dP := w + P1 - P2;
|
||||
|
||||
return length(dP), sc, tc;
|
||||
}
|
||||
|
||||
get_positions :: (e : *Entity, n : Node_Render_Data, m : Mesh, i0 : u32, i1 : u32, i2 : u32) -> Vector3, Vector3, Vector3 {
|
||||
p0 : Vector3;
|
||||
p1 : Vector3;
|
||||
p2 : Vector3;
|
||||
|
||||
p0 = transform_point(n.transform.world_matrix, m.positions[i0]);
|
||||
p1 = transform_point(n.transform.world_matrix, m.positions[i1]);
|
||||
p2 = transform_point(n.transform.world_matrix, m.positions[i2]);
|
||||
|
||||
return p0, p1, p2;
|
||||
}
|
||||
|
||||
ray_entity_intersect :: (ray: Ray, e: *Entity) -> bool, float, Vector3 {
|
||||
has_hit := false;
|
||||
closest : float = 10000000;
|
||||
closest_normal : Vector3;
|
||||
|
||||
for n: e.renderable.model.nodes {
|
||||
render_node := e.renderable.nodes[it_index];
|
||||
for handle: n.meshes {
|
||||
m := parray_get(*renderer.meshes, handle);
|
||||
index := 0;
|
||||
|
||||
|
||||
if m.indices.count > 0 {
|
||||
while index < m.indices.count - 1 {
|
||||
i0 := m.indices[index];
|
||||
i1 := m.indices[index + 1];
|
||||
i2 := m.indices[index + 2];
|
||||
|
||||
p2, p1, p0 := get_positions(e, render_node, m, i0, i1, i2);
|
||||
|
||||
result, t, normal := ray_triangle_intersect(ray.origin, ray.direction, p0, p1, p2);
|
||||
if result && t < closest {
|
||||
has_hit = true;
|
||||
closest = t;
|
||||
closest_normal = normal;
|
||||
}
|
||||
|
||||
index += 3;
|
||||
}
|
||||
} else {
|
||||
while index < m.positions.count - 1 {
|
||||
p2, p1, p0 := get_positions(e, render_node, m, xx index, xx (index + 1), xx (index + 2));
|
||||
|
||||
result, t, normal := ray_triangle_intersect(ray.origin, ray.direction, p0, p1, p2);
|
||||
if result && t < closest {
|
||||
has_hit = true;
|
||||
closest = t;
|
||||
closest_normal = normal;
|
||||
}
|
||||
|
||||
index += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return has_hit, closest, closest_normal;
|
||||
}
|
||||
142
core/scene.jai
Normal file
142
core/scene.jai
Normal file
@@ -0,0 +1,142 @@
|
||||
#load "../renderer/directional_light.jai";
|
||||
#load "particles.jai";
|
||||
|
||||
#placeholder Entity_Storage;
|
||||
|
||||
MAX_CACHED_PILES :: 8;
|
||||
|
||||
Scene :: struct {
|
||||
name: string;
|
||||
|
||||
entities : [..] *Entity;
|
||||
|
||||
particle_systems : Bucket_Array(Particle_System, 64);
|
||||
|
||||
bullet_impact_particle_systems : [..] *Particle_System;
|
||||
|
||||
by_type : Entity_Storage;
|
||||
|
||||
pool : Flat_Pool;
|
||||
allocator : Allocator;
|
||||
|
||||
camera : Camera;
|
||||
directional_light : Directional_Light;
|
||||
|
||||
}
|
||||
|
||||
Entity_File_Info :: struct {
|
||||
id: s64;
|
||||
full_path: string;
|
||||
}
|
||||
|
||||
visitor :: (info : *File_Visit_Info, files: *[..] Entity_File_Info) {
|
||||
if info.is_directory
|
||||
return;
|
||||
path, basename, ext := path_decomp (info.full_name);
|
||||
|
||||
// Entity text files
|
||||
if ext == "ent" && basename != "cam" {
|
||||
file_info : Entity_File_Info;
|
||||
file_info.id = cast(s32)string_to_int(basename);
|
||||
file_info.full_path = copy_temporary_string(info.full_name);
|
||||
array_add(files, file_info);
|
||||
}
|
||||
}
|
||||
|
||||
load_scene :: (path: string) -> *Scene {
|
||||
scene := create_scene("", 1024);
|
||||
|
||||
files : [..] Entity_File_Info;
|
||||
files.allocator = temp;
|
||||
visit_files(path, true, *files, visitor);
|
||||
|
||||
for file: files {
|
||||
deserialize_entity(scene, file.full_path);
|
||||
}
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
save_scene :: (scene: *Scene, path: string) {
|
||||
scene.camera = game_state.camera;
|
||||
builder : String_Builder;
|
||||
builder.allocator = temp;
|
||||
|
||||
full_path := tprint("%/%", path, scene.name);
|
||||
|
||||
make_directory_if_it_does_not_exist(full_path);
|
||||
|
||||
for scene.entities {
|
||||
if it.flags & .DONT_SAVE continue;
|
||||
|
||||
serialize_entity(it, full_path);
|
||||
}
|
||||
|
||||
// Save camera
|
||||
//print_to_builder(*builder, "Camera: % % % % %\n", scene.camera.position.x, scene.camera.position.y, scene.camera.position.z, scene.camera.rotation.yaw, scene.camera.rotation.pitch);
|
||||
|
||||
//write_entire_file(path, builder_to_string(*builder));
|
||||
}
|
||||
|
||||
unload_scene :: (scene: *Scene) {
|
||||
for e: scene.entities {
|
||||
destroy_entity(e, false);
|
||||
}
|
||||
|
||||
fini(*scene.pool);
|
||||
free(scene);
|
||||
}
|
||||
|
||||
create_scene :: (name: string, max_entities: s64 = 256) -> *Scene {
|
||||
scene := New(Scene);
|
||||
scene.name = copy_string(name);
|
||||
|
||||
// Setup allocator
|
||||
scene.pool = .{};
|
||||
scene.allocator.data = *scene.pool;
|
||||
scene.allocator.proc = flat_pool_allocator_proc;
|
||||
|
||||
// Assign allocator to everything that needs allocations
|
||||
scene.entities.allocator = scene.allocator;
|
||||
scene.particle_systems.allocator = scene.allocator;
|
||||
scene.bullet_impact_particle_systems.allocator = scene.allocator;
|
||||
|
||||
array_reserve(*scene.entities, max_entities);
|
||||
|
||||
scene.directional_light.color_and_intensity = .{1,1,1,1};
|
||||
scene.directional_light.direction = to_v4(normalize(Vector3.{0.3, -0.3, 0.5}));
|
||||
|
||||
dir_light_data : Directional_Light_Buffer_Data;
|
||||
dir_light_data.color_and_intensity = scene.directional_light.color_and_intensity;
|
||||
dir_light_data.direction = scene.directional_light.direction;
|
||||
upload_data_to_buffer(renderer, directional_light_buffer, *dir_light_data, size_of(Directional_Light_Buffer_Data));
|
||||
|
||||
array_resize(*scene.bullet_impact_particle_systems, 32);
|
||||
|
||||
for 0..31 {
|
||||
scene.bullet_impact_particle_systems[it] = create_particle_system(particle_pipeline, on_update_bullet_hit_particles, null, scene);
|
||||
}
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
register_entity :: (scene: *Scene, entity: *Entity) {
|
||||
entity.scene = scene;
|
||||
entity.id = next_entity_id;
|
||||
array_add(*scene.entities, entity);
|
||||
next_entity_id += 1;
|
||||
|
||||
if net_data.net_mode == {
|
||||
case .LISTEN_SERVER; #through;
|
||||
case .DEDICATED_SERVER; {
|
||||
//net_spawn_entity(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unregister_entity :: (scene: *Scene, entity: *Entity) {
|
||||
array_unordered_remove_by_value(*scene.entities, entity);
|
||||
}
|
||||
|
||||
#scope_file
|
||||
next_entity_id: Entity_Id;
|
||||
29
core/stack.jai
Normal file
29
core/stack.jai
Normal file
@@ -0,0 +1,29 @@
|
||||
Stack :: struct(Value_Type : Type) {
|
||||
values: [..] Value_Type;
|
||||
}
|
||||
|
||||
stack_push :: (stack: *Stack, value: stack.Value_Type) {
|
||||
array_add(*stack.values, value);
|
||||
}
|
||||
|
||||
stack_pop :: (stack: *Stack) -> stack.Value_Type {
|
||||
if stack.values.count > 0 {
|
||||
index := stack.values.count - 1;
|
||||
stack.values.count -= 1;
|
||||
return stack.values.data[index];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
stack_peek :: (stack: *Stack) -> stack.Value_Type {
|
||||
if stack.values.count > 0 {
|
||||
return stack.values[stack.values.count-1];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
is_stack_empty :: (stack: *Stack) -> bool {
|
||||
return stack.values.count == 0;
|
||||
}
|
||||
21
core/static_array.jai
Normal file
21
core/static_array.jai
Normal file
@@ -0,0 +1,21 @@
|
||||
Static_Array :: struct (Data_Type : Type, Count: s64) {
|
||||
data: [Count] Data_Type;
|
||||
count: s64;
|
||||
}
|
||||
|
||||
array_add :: (static_array: *Static_Array, value: static_array.Data_Type) {
|
||||
assert(static_array.count <= static_array.Count);
|
||||
static_array.data[static_array.count] = value;
|
||||
static_array.count += 1;
|
||||
}
|
||||
|
||||
for_expansion :: (static_array: *Static_Array, body: Code, flags: For_Flags) #expand {
|
||||
for `it_index: 0..static_array.count-1 {
|
||||
#if flags & .POINTER {
|
||||
`it := *static_array.data[it_index];
|
||||
} else {
|
||||
`it := static_array.data[it_index];
|
||||
}
|
||||
#insert body;
|
||||
}
|
||||
}
|
||||
6
core/string_helpers.jai
Normal file
6
core/string_helpers.jai
Normal file
@@ -0,0 +1,6 @@
|
||||
to_temp_c_string :: (s: string) -> *u8 {
|
||||
result : *u8 = alloc(s.count + 1,, allocator=temp);
|
||||
memcpy(result, s.data, s.count);
|
||||
result[s.count] = 0;
|
||||
return result;
|
||||
}
|
||||
172
core/transform.jai
Normal file
172
core/transform.jai
Normal file
@@ -0,0 +1,172 @@
|
||||
#import "Math";
|
||||
|
||||
Transform_Identity : Transform : .{ .{0,0,0}, .{0,0,0,1}, .{1,1,1}, Matrix4_Identity, Matrix4_Identity, false };
|
||||
|
||||
Transform :: struct {
|
||||
position: Vector3;
|
||||
orientation: Quaternion;
|
||||
scale : Vector3;
|
||||
|
||||
model_matrix: Matrix4 = Matrix4_Identity; @DontSerialize
|
||||
|
||||
world_matrix: Matrix4; @DontSerialize
|
||||
|
||||
dirty: bool; @DontSerialize
|
||||
}
|
||||
|
||||
create_identity_transform :: () -> Transform {
|
||||
transform : Transform;
|
||||
|
||||
transform.position = .{};
|
||||
transform.orientation = .{0,0,0,1};
|
||||
transform.scale = .{1,1,1};
|
||||
transform.model_matrix = Matrix4_Identity;
|
||||
|
||||
update_matrix(*transform);
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
make_matrix :: (position: Vector3, orientation: Quaternion, scale: Vector3) -> Matrix4 {
|
||||
trans_mat := make_translation_matrix4(position);
|
||||
scale_mat := make_scale_matrix4(scale);
|
||||
rot_mat := rotation_matrix(Matrix4, orientation);
|
||||
return trans_mat * rot_mat * scale_mat;
|
||||
}
|
||||
|
||||
create_transform :: (position: Vector3, orientation: Quaternion, scale: Vector3) -> Transform {
|
||||
transform : Transform;
|
||||
|
||||
transform.position = position;
|
||||
transform.orientation = orientation;
|
||||
transform.scale = scale;
|
||||
transform.model_matrix = make_matrix(position, orientation, scale);
|
||||
|
||||
return transform;
|
||||
}
|
||||
|
||||
update_matrix :: (transform: *Transform) {
|
||||
transform.model_matrix = make_matrix(transform.position, transform.orientation, transform.scale);
|
||||
transform.dirty = true;
|
||||
}
|
||||
|
||||
set_position :: (transform: *Transform, position: Vector3, calculate_matrix: bool = true) {
|
||||
transform.position = position;
|
||||
if calculate_matrix update_matrix(transform);
|
||||
}
|
||||
|
||||
set_position :: (transform: *Transform, x: float, y: float, z: float, calculate_matrix: bool = true) {
|
||||
transform.position.x = x;
|
||||
transform.position.y = y;
|
||||
transform.position.z = z;
|
||||
if calculate_matrix update_matrix(transform);
|
||||
}
|
||||
|
||||
translate :: (transform: *Transform, translation: Vector3, calculate_matrix: bool = true) {
|
||||
transform.position += translation;
|
||||
if calculate_matrix update_matrix(transform);
|
||||
}
|
||||
|
||||
euler_to_quaternion :: (yaw: float, pitch: float, roll: float) -> Quaternion {
|
||||
cy := cos(yaw * 0.5);
|
||||
sy := sin(yaw * 0.5);
|
||||
cp := cos(pitch * 0.5);
|
||||
sp := sin(pitch * 0.5);
|
||||
cr := cos(roll * 0.5);
|
||||
sr := sin(roll * 0.5);
|
||||
|
||||
q: Quaternion;
|
||||
q.w = cr * cp * cy + sr * sp * sy;
|
||||
q.x = sr * cp * cy - cr * sp * sy;
|
||||
q.y = cr * sp * cy + sr * cp * sy;
|
||||
q.z = cr * cp * sy - sr * sp * cy;
|
||||
|
||||
return q;
|
||||
}
|
||||
|
||||
sign :: (val: $T) -> T {
|
||||
if val < 0 return -1.0;
|
||||
if val > 0 return 1.0;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
quaternion_to_euler_v3 :: (q: Quaternion) -> Vector3 {
|
||||
yaw, pitch, roll := quaternion_to_euler(q);
|
||||
|
||||
v : Vector3 = ---;
|
||||
v.x = yaw;
|
||||
v.y = pitch;
|
||||
v.z = roll;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
quaternion_to_euler :: (q: Quaternion) -> yaw: float, pitch: float, roll: float {
|
||||
yaw: float;
|
||||
pitch: float;
|
||||
roll: float;
|
||||
|
||||
sinr_cosp := 2.0 * (q.w * q.x + q.y * q.z);
|
||||
cosr_cosp := 1.0 - 2.0 * (q.x * q.x + q.y * q.y);
|
||||
roll = atan2(sinr_cosp, cosr_cosp);
|
||||
|
||||
// pitch (y-axis rotation)
|
||||
sinp := 2.0 * (q.w * q.y - q.z * q.x);
|
||||
if abs(sinp) >= 1.0
|
||||
pitch = sign(sinp) * (PI / 2); // use 90 degrees if out of range
|
||||
else
|
||||
pitch = asin(sinp);
|
||||
|
||||
// yaw (z-axis rotation)
|
||||
siny_cosp := 2.0 * (q.w * q.z + q.x * q.y);
|
||||
cosy_cosp := 1.0 - 2.0 * (q.y * q.y + q.z * q.z);
|
||||
yaw = atan2(siny_cosp, cosy_cosp);
|
||||
|
||||
return yaw, pitch, roll;
|
||||
}
|
||||
|
||||
set_rotation :: (transform: *Transform, orientation: Quaternion, calculate_matrix: bool = true) {
|
||||
transform.orientation = orientation;
|
||||
if calculate_matrix update_matrix(transform);
|
||||
}
|
||||
|
||||
set_rotation :: (transform: *Transform, euler_angles: Vector3, calculate_matrix: bool = true) {
|
||||
orientation := euler_to_quaternion(degrees_to_radians(euler_angles.y), degrees_to_radians(euler_angles.x), degrees_to_radians(euler_angles.z));
|
||||
transform.orientation = orientation;
|
||||
if calculate_matrix update_matrix(transform);
|
||||
}
|
||||
|
||||
set_scale :: (transform: *Transform, scale: Vector3, calculate_matrix: bool = true) {
|
||||
transform.scale = scale;
|
||||
if calculate_matrix update_matrix(transform);
|
||||
}
|
||||
|
||||
set_scale :: (transform: *Transform, scale: float, calculate_matrix: bool = true) {
|
||||
transform.scale = .{scale, scale, scale};
|
||||
if calculate_matrix update_matrix(transform);
|
||||
}
|
||||
|
||||
get_forward :: (transform: Transform) -> Vector3 {
|
||||
v := rotation_matrix(Matrix4, transform.orientation) * Vector4.{0,0,1,0};
|
||||
return .{v.x, v.y, v.z};
|
||||
}
|
||||
|
||||
get_right :: (transform: Transform) -> Vector3 {
|
||||
v := rotation_matrix(Matrix4, transform.orientation) * Vector4.{1,0,0,0};
|
||||
return .{v.x, v.y, v.z};
|
||||
}
|
||||
|
||||
get_up :: (transform: Transform) -> Vector3 {
|
||||
v := rotation_matrix(Matrix4, transform.orientation) * Vector4.{0,1,0,0};
|
||||
return .{v.x, v.y, v.z};
|
||||
}
|
||||
|
||||
get_position :: (transform: Transform) -> Vector3 {
|
||||
return .{transform.model_matrix._14, transform.model_matrix._24, transform.model_matrix._34};
|
||||
}
|
||||
|
||||
get_rotation :: (transform: Transform) -> Vector3 {
|
||||
|
||||
return .{transform.model_matrix._14, transform.model_matrix._24, transform.model_matrix._34};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user