Initial commit

This commit is contained in:
2024-10-11 22:21:32 +02:00
commit 1bdd01e9b2
38 changed files with 11363 additions and 0 deletions

345
core/camera.jai Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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};
}