work on adding animations and sprites

This commit is contained in:
Tuomas Katajisto 2025-10-25 19:37:15 +03:00
parent c7a4e94522
commit a52cc6a934
15 changed files with 157 additions and 113 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 551 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 KiB

View File

@ -0,0 +1,75 @@
#scope_file
current_sheet : s32 = 0;
current_sheet_last_frame : s32 = -1;
current_sheet_ptr : *Spritesheet;
step_offset_y : s32 = 0;
scale : float = 5;
#scope_export
tick_animation_studio :: () {
if input_button_states[Key_Code.ARROW_UP] & .START {
step_offset_y -= 1;
}
if input_button_states[Key_Code.ARROW_DOWN] & .START {
step_offset_y += 1;
}
scale += mouse_delta_z;
scale = clamp(scale, 1, 10);
}
draw_animation_studio_ui :: (theme: *GR.Overall_Theme) {
if g_spritesheets.count < 1 then return;
r := GR.get_rect(0, ui_h(5,0), ui_w(20, 0), ui_h(95, 0));
ui_add_mouse_occluder(r);
draw_bg_rectangle(r, theme);
sheets : [..]string;
sheets.allocator = temp;
for v : g_spritesheets {
array_add(*sheets, v.name);
}
r2 := r;
r2.h = ui_h(5,0);
r2.w = r.w;
GR.dropdown(r2, sheets, *current_sheet, *theme.dropdown_theme);
if current_sheet != current_sheet_last_frame {
current_sheet_ptr = table_find_pointer(*g_spritesheets, sheets[current_sheet]);
}
number_theme : GR.Number_Input_Theme;
r2.y += r2.h * 1.1;
GR.label(r2, "Step X", *theme.label_theme);
r2.y += r2.h * 1.1;
GR.number_input(r2, tprint("%", current_sheet_ptr.step_x), *current_sheet_ptr.step_x, 0, 128, *number_theme);
r2.y += r2.h * 1.1;
GR.label(r2, "Step Y", *theme.label_theme);
r2.y += r2.h * 1.1;
GR.number_input(r2, tprint("%", current_sheet_ptr.step_y), *current_sheet_ptr.step_y, 0, 128, *number_theme);
uiTex := New(Ui_Texture ,,temp);
uiTex.tex = current_sheet_ptr.sheet;
if uiTex.tex.id != INVALID_ID {
menu_offset := Vector2.{ui_w(20,0), ui_h(5,0)};
zone_r := GR.get_rect(menu_offset.x, menu_offset.y, ui_w(80,0), ui_h(95,0));
draw_rectangle(zone_r, .{1.0, 0.0, 1.0, 1.0});
set_shader_for_images(uiTex);
img_r := zone_r;
img_r.y -= step_offset_y * cast(s32)current_sheet_ptr.step_y * scale;
img_r.w = current_sheet_ptr.w * scale;
img_r.h = current_sheet_ptr.h * scale;
immediate_quad(.{img_r.x, img_r.y}, .{img_r.x + img_r.w, img_r.y}, .{img_r.x + img_r.w, img_r.y + img_r.h}, .{img_r.x, img_r.y + img_r.h});
set_shader_for_color();
}
}

View File

@ -3,6 +3,7 @@
#load "picker.jai"; #load "picker.jai";
#load "trile_editor.jai"; #load "trile_editor.jai";
#load "level_editor.jai"; #load "level_editor.jai";
#load "animation_studio.jai";
} }
#if HAS_TACOMA { #load "tacoma.jai"; } #if HAS_TACOMA { #load "tacoma.jai"; }
#load "console.jai"; #load "console.jai";
@ -65,6 +66,8 @@ draw_editor_ui :: (theme: *GR.Overall_Theme) {
draw_trile_editor_ui(theme); draw_trile_editor_ui(theme);
case .Level_Editor; case .Level_Editor;
draw_level_editor_ui(theme); draw_level_editor_ui(theme);
case .Animation_Editor;
draw_animation_studio_ui(theme);
} }
} }
draw_profiler(); draw_profiler();
@ -103,6 +106,7 @@ tick_editor_ui :: () {
case .Level_Editor; case .Level_Editor;
tick_level_editor(); tick_level_editor();
case .Animation_Editor; case .Animation_Editor;
tick_animation_studio();
} }
} }
} }

View File

@ -225,10 +225,8 @@ draw_tacoma_tab :: (theme: *GR.Overall_Theme, total_r: GR.Rect) {
Edit_Mode :: enum { Edit_Mode :: enum {
TRILES; TRILES;
GROUND;
} }
groundType : Ground_Tile;
editMode : Edit_Mode; editMode : Edit_Mode;
#scope_export #scope_export
@ -236,49 +234,15 @@ editMode : Edit_Mode;
draw_tools_tab :: (theme: *GR.Overall_Theme, total_r: GR.Rect) { draw_tools_tab :: (theme: *GR.Overall_Theme, total_r: GR.Rect) {
r := total_r; r := total_r;
r.h = ui_h(3,0); r.h = ui_h(3,0);
r.w = r.w/2.0;
if GR.button(r, "Edit triles", *t_button_selectable(theme, editMode == .TRILES)) {
editMode = .TRILES;
}
r.x += r.w;
if GR.button(r, "Edit ground", *t_button_selectable(theme, editMode == .GROUND)) {
editMode = .GROUND;
}
r.x -= r.w;
r.w *= 2.0;
r.y += r.h * 1.5;
if editMode == .GROUND {
if GR.button(r, "Grass", *t_button_selectable(theme, groundType == .GRASS)) {
groundType = .GRASS;
}
r.y += r.h;
if GR.button(r, "Water", *t_button_selectable(theme, groundType == .WATER)) {
groundType = .WATER;
}
r.y += r.h;
if GR.button(r, "Sand", *t_button_selectable(theme, groundType == .SAND)) {
groundType = .SAND;
}
}
} }
handle_tool_click :: (x: int, y: int, z: int, delete: bool = false) { handle_tool_click :: (x: int, y: int, z: int, delete: bool = false) {
curworld := get_current_world(); curworld := get_current_world();
if editMode == { if delete {
case .TRILES; remove_trile(cast(float)x, cast(float)y, cast(float)z);
if delete { } else {
remove_trile(cast(float)x, cast(float)y, cast(float)z); if editor_current_trile != null then add_trile(editor_current_trile.name, cast(float)x, cast(float)y, cast(float)z);
} else {
if editor_current_trile != null then add_trile(editor_current_trile.name, cast(float)x, cast(float)y, cast(float)z);
}
case .GROUND;
ray := get_mouse_ray(*get_level_editor_camera());
hit, point := ray_plane_collision_point(ray, 0, 100);
if hit {
curworld.world.ground[floor(point.y).(int) + 500][floor(point.x).(int) + 500] = groundType;
}
} }
} }
add_trile :: (name: string, x: float, y: float, z: float) { add_trile :: (name: string, x: float, y: float, z: float) {

View File

@ -1,9 +1,10 @@
g_asset_pack : Load_Package;
#scope_file #scope_file
MAX_FILE_SIZE :: 2_000_000_000; MAX_FILE_SIZE :: 2_000_000_000;
buf : [MAX_FILE_SIZE]u8; buf : [MAX_FILE_SIZE]u8;
g_asset_pack : Load_Package;
mandatory_loads_left : s32 = 0; mandatory_loads_left : s32 = 0;
@ -48,12 +49,12 @@ add_font_from_pack :: (path: string) {
state.font_default.fons_font = fonsAddFontMem(state.fons, "sans", entry.data.data, xx entry.data.count, 0); state.font_default.fons_font = fonsAddFontMem(state.fons, "sans", entry.data.data, xx entry.data.count, 0);
} }
create_texture_from_pack :: (path: string) -> sg_image { create_texture_from_pack :: (path: string) -> (sg_image, s32, s32) {
ok, entry := table_find_new(*g_asset_pack.lookup, path); ok, entry := table_find_new(*g_asset_pack.lookup, path);
if !ok { if !ok {
print("Failed to find texture % from pack...\n", path); print("Failed to find texture % from pack...\n", path);
img : sg_image; img : sg_image;
return img; return img, 0, 0;
} }
x : s32; x : s32;
@ -79,7 +80,7 @@ create_texture_from_pack :: (path: string) -> sg_image {
stbi.stbi_image_free(data); stbi.stbi_image_free(data);
return img; return img, x, y;
} }
load_string_from_pack :: (path: string) -> string { load_string_from_pack :: (path: string) -> string {
@ -104,7 +105,7 @@ asset_list :: () -> string #expand {
return tprint("> A total of % assets.", count); return tprint("> A total of % assets.", count);
} @Command } @Command
// Returns tprinted pointer, please copy it somewhere. // Returns tprinted string, please copy it somewhere.
format_size :: (bytes: int) -> string { format_size :: (bytes: int) -> string {
bytes_counter := bytes; bytes_counter := bytes;
bytes_float := cast(float) bytes; bytes_float := cast(float) bytes;

View File

@ -119,6 +119,7 @@ init_after_asset_pack :: () {
} }
init_editor(); init_editor();
lworlds(); lworlds();
init_spritesheets();
init_rendering(); init_rendering();
load_post_process_from_pack(); load_post_process_from_pack();

View File

@ -0,0 +1,65 @@
g_spritesheets : Table(string, Spritesheet);
g_animations: Table(string, Animation);
String :: #import "String";
Spritesheet :: struct {
name : string;
step_x : u32 = 32;
step_y : u32 = 32;
w : s32 = 0;
h : s32 = 0;
sheet : sg_image;
}
Animation :: struct {
spritesheet : string;
ids : [..]u32;
fps : float;
}
load_spritesheets :: () {
Jaison :: #import "Jaison";
s := load_string_from_pack("./game/resources/spritesheets.json");
success, sheets := Jaison.json_parse_string(s, [..]Spritesheet,, temp);
if !success then return;
for sheets {
print("Loaded %\n", it.name);
img, w, h := create_texture_from_pack(tprint("./game/resources/sprites/%", it.name));
name_cpy := sprint("%", it.name);
newSheet := Spritesheet.{
name = name_cpy,
step_x = it.step_x,
step_y = it.step_y,
sheet = img,
w = w,
h = h,
};
table_add(*g_spritesheets, name_cpy, newSheet);
}
}
init_spritesheets :: () {
load_spritesheets();
add_new_spritesheets_from_pack();
}
add_new_spritesheets_from_pack :: () {
for v : g_asset_pack.lookup {
isSpritesheet, remainder := String.contains(v.name, "/sprites/");
if isSpritesheet {
ok, sheet := table_find_new(*g_spritesheets, remainder);
if !ok {
print("Adding sheet: %\n", remainder);
img, w, h := create_texture_from_pack(v.name);
newSheet := Spritesheet.{
name = remainder,
sheet = img,
w = w,
h = h,
};
table_add(*g_spritesheets, remainder, newSheet);
}
}
}
}

View File

@ -16,7 +16,6 @@ Render_Command_Type :: enum {
DRAW_SKY; DRAW_SKY;
SET_CAMERA; SET_CAMERA;
DRAW_GROUND; DRAW_GROUND;
GENERATE_GROUND;
ADD_TRILE_POSITIONS; ADD_TRILE_POSITIONS;
DRAW_TRILE_POSITIONS; DRAW_TRILE_POSITIONS;
UPDATE_TRIXELS; UPDATE_TRIXELS;
@ -72,12 +71,6 @@ Render_Command_Draw_Ground :: struct {
worldConfig : *World_Config; worldConfig : *World_Config;
} }
Render_Command_Generate_Ground_Texture :: struct {
#as using c : Render_Command;
c.type = .GENERATE_GROUND;
world : *World;
}
Render_Command_Set_Camera :: struct { Render_Command_Set_Camera :: struct {
#as using c : Render_Command; #as using c : Render_Command;
c.type = .SET_CAMERA; c.type = .SET_CAMERA;

View File

@ -25,9 +25,6 @@ backend_handle_command :: (cmd: *Render_Command) {
case .SET_CAMERA; case .SET_CAMERA;
camera_command := cast(*Render_Command_Set_Camera)cmd; camera_command := cast(*Render_Command_Set_Camera)cmd;
camera = camera_command.camera; camera = camera_command.camera;
case .GENERATE_GROUND;
gen_command := cast(*Render_Command_Generate_Ground_Texture)cmd;
update_image_from_ground(gen_command.world, *gPipelines.plane.bind.images[1]);
case .DRAW_GROUND; case .DRAW_GROUND;
ground_command := cast(*Render_Command_Draw_Ground)cmd; ground_command := cast(*Render_Command_Draw_Ground)cmd;
if in_gbuffer_pass { if in_gbuffer_pass {

View File

@ -943,7 +943,7 @@ create_ssao_pipeline :: () {
} }
init_plane_textures :: () { init_plane_textures :: () {
gPipelines.plane.bind.images[3] = create_texture_from_pack("./resources/utiltex/water.png"); gPipelines.plane.bind.images[3] = create_texture_from_pack("./resources/utiltex/water_small.png");
} }
g_plane_gbuffer_vertex_buffer : sg_buffer; g_plane_gbuffer_vertex_buffer : sg_buffer;

View File

@ -15,6 +15,7 @@
#load "arbtri.jai"; #load "arbtri.jai";
#load "meshgen.jai"; #load "meshgen.jai";
#load "helpers.jai"; #load "helpers.jai";
#load "animation.jai";
#load "pipelines.jai"; #load "pipelines.jai";
#load "post_processing.jai"; #load "post_processing.jai";
#load "backend_sokol_helpers.jai"; #load "backend_sokol_helpers.jai";

View File

@ -113,8 +113,6 @@ tasks_to_commands :: () {
case .GROUND; case .GROUND;
commandDrawGround := New(Render_Command_Draw_Ground,, temp); commandDrawGround := New(Render_Command_Draw_Ground,, temp);
commandDrawGround.worldConfig = *(cast(*Rendering_Task_Ground)it).world.conf; commandDrawGround.worldConfig = *(cast(*Rendering_Task_Ground)it).world.conf;
commandGenGround := New(Render_Command_Generate_Ground_Texture,, temp);
commandGenGround.world = (cast(*Rendering_Task_Ground)it).world;
array_add(*render_command_buckets.main, commandDrawGround); array_add(*render_command_buckets.main, commandDrawGround);
array_add(*render_command_buckets.gbuffer, commandDrawGround); array_add(*render_command_buckets.gbuffer, commandDrawGround);
case .SET_CAMERA; case .SET_CAMERA;

View File

@ -105,17 +105,10 @@ TrilePositions :: struct {
positions: [..]Vector4; positions: [..]Vector4;
} }
Ground_Tile :: enum u8 {
WATER :: 0;
GRASS :: 1;
SAND :: 2;
}
World :: struct { World :: struct {
name : string; name : string;
conf : World_Config; conf : World_Config;
positions : [..]TrilePositions; positions : [..]TrilePositions;
ground : [1000][1000]Ground_Tile;
} }
Trile_Positions_Serialized :: struct { Trile_Positions_Serialized :: struct {
@ -127,7 +120,6 @@ World_Serialized :: struct {
name : string; name : string;
conf : World_Config; conf : World_Config;
positions : [..]Trile_Positions_Serialized; positions : [..]Trile_Positions_Serialized;
ground : string;
} }
// You should call this function with the temp allocator as context allocator. // You should call this function with the temp allocator as context allocator.
@ -147,14 +139,6 @@ serialize_world :: (world: *World) -> World_Serialized {
array_add(*ws.positions, tsp); array_add(*ws.positions, tsp);
} }
groundstring : [..]u8;
for i: 0..999 {
for j: 0..999 {
array_add(*groundstring, (cast(u8) world.ground[i][j]) + #char "a");
}
}
ws.ground.data = groundstring.data;
ws.ground.count = groundstring.count;
return ws; return ws;
} }
@ -175,45 +159,6 @@ deserialize_world :: (ws: *World_Serialized, world: *World) {
} }
array_add(*world.positions, tp); array_add(*world.positions, tp);
} }
for c, ci : ws.ground {
world.ground[ci/1000][ci%1000] = cast(Ground_Tile)((cast(u8)c)-#char "a");
}
}
update_image_from_ground :: (world: *World, img: *sg_image) {
materialdata : [1000*1000*4]u8;
counter : int = 0;
for x: 0..999 {
for y: 0..999 {
if world.ground[x][y] == .GRASS {
materialdata[counter + 0] = 0;
materialdata[counter + 1] = 255;
materialdata[counter + 2] = 0;
materialdata[counter + 3] = 255;
}
if world.ground[x][y] == .WATER {
materialdata[counter + 0] = 0;
materialdata[counter + 1] = 0;
materialdata[counter + 2] = 255;
materialdata[counter + 3] = 255;
}
if world.ground[x][y] == .SAND {
materialdata[counter + 0] = 255;
materialdata[counter + 1] = 0;
materialdata[counter + 2] = 0;
materialdata[counter + 3] = 255;
}
counter += 4;
}
}
imgdata : sg_image_data;
imgdata.subimage[0][0] = .{materialdata.data, materialdata.count};
sg_update_image(img, *imgdata);
} }
draw_world_picker :: (r_in: GR.Rect, theme: *GR.Overall_Theme) { draw_world_picker :: (r_in: GR.Rect, theme: *GR.Overall_Theme) {