animation wip

This commit is contained in:
jacob 2026-01-30 05:27:47 -06:00
parent d44a845a8d
commit b7e2fafc85
11 changed files with 615 additions and 1515 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,132 @@
// DEFLATE decoder based on Handmade Hero's png parser
////////////////////////////////////////////////////////////
//~ Aseprite decode types
// https://github.com/aseprite/aseprite/blob/main/docs/ase-file-specs.md
Packed(Struct(ASE_Header)
{
u32 file_size;
u16 magic;
u16 frames_count;
u16 width;
u16 height;
u16 color_depth;
u32 flags;
u16 speed;
u8 _0[8];
u8 palette_entry;
u8 _1[3];
u16 colors_count;
u8 pixel_width;
u8 pixel_height;
i16 grid_x;
i16 grid_y;
u16 grid_width;
u16 grid_height;
u8 _2[84];
});
Packed(Struct(ASE_FrameHeader)
{
u32 bytes_count;
u16 magic;
u16 chunks_count0;
u16 frame_duration_ms;
u8 _[2];
u32 chunks_count1;
});
Enum(ASE_EncodedChunkKind)
{
ASE_EncodedChunkKind_OldPalette1 = 0x0004,
ASE_EncodedChunkKind_OldPalette2 = 0x0011,
ASE_EncodedChunkKind_Layer = 0x2004,
ASE_EncodedChunkKind_Cel = 0x2005,
ASE_EncodedChunkKind_CelExtra = 0x2006,
ASE_EncodedChunkKind_ColorProfile = 0x2007,
ASE_EncodedChunkKind_ExternalFiles = 0x2008,
ASE_EncodedChunkKind_Mask = 0x2016,
ASE_EncodedChunkKind_Path = 0x2017,
ASE_EncodedChunkKind_Tags = 0x2018,
ASE_EncodedChunkKind_Palette = 0x2019,
ASE_EncodedChunkKind_Userdata = 0x2020,
ASE_EncodedChunkKind_Slice = 0x2022,
ASE_EncodedChunkKind_Tileset = 0x2023
};
Enum(ASE_ChunkKind)
{
ASE_ChunkKind_Layer,
ASE_ChunkKind_Cel,
ASE_ChunkKind_Tag,
};
Enum(ASE_LayerKind)
{
ASE_LayerKind_Normal,
ASE_LayerKind_Group,
ASE_LayerKind_Tilemap,
};
Enum(ASE_LayerFlag)
{
ASE_LayerFlag_None = 0,
ASE_LayerFlag_Visible = (1 << 0),
ASE_LayerFlag_Editable = (1 << 1),
ASE_LayerFlag_LockMovement = (1 << 2),
ASE_LayerFlag_Background = (1 << 3),
ASE_LayerFlag_PreferLinkedCels = (1 << 4),
ASE_LayerFlag_Collapsed = (1 << 5),
ASE_LayerFlag_ReferenceLayer = (1 << 6),
};
Enum(ASE_CelKind)
{
ASE_CelKind_Raw = 0,
ASE_CelKind_Linked = 1,
ASE_CelKind_CompressedImage = 2,
ASE_CelKind_CompressedTilemap = 3,
};
Struct(ASE_Chunk)
{
ASE_Chunk *next;
ASE_ChunkKind kind;
//- Common
f32 opacity;
String name;
i64 chunk_layer_idx;
//- Layer
struct
{
ASE_LayerKind kind;
ASE_LayerFlag flags;
ASE_Chunk **cel_chunks;
struct ASE_Layer *final;
} layer;
//- Cel
struct
{
ASE_CelKind kind;
i64 frame_idx;
i64 linked_frame_idx;
Rng2 bounds;
String encoded;
} cel;
//- Tag
struct
{
i64 from;
i64 to;
} tag;
};
////////////////////////////////////////////////////////////
//~ Inflate types
@ -138,21 +266,23 @@ Struct(ASE_Span)
ASE_Span *prev;
String name;
i64 start;
i64 end;
i64 from;
i64 to;
};
Struct(ASE_Meta)
{
i64 frames_count;
i64 layers_count;
ASE_Layer *first_layer;
ASE_Layer *last_layer;
i64 spans_count;
ASE_Span *first_span;
ASE_Span *last_span;
i64 layers_count;
ASE_Layer *first_layer;
ASE_Layer *last_layer;
b32 ok;
};
////////////////////////////////////////////////////////////
@ -184,7 +314,7 @@ u16 ASE_DecodeHuffDict(ASE_HuffDict *huffman, ASE_Bitbuff *bb);
void ASE_Inflate(u8 *dst, u8 *encoded);
////////////////////////////////////////////////////////////
//~ Meta
//~ Decode
ASE_Meta ASE_DecodeMeta(Arena *arena, String encoded);
@ -199,286 +329,3 @@ ASE_Image ASE_DecompressImageFromCel(Arena *arena, ASE_Cel *cel);
u32 ASE_BlendPixel(u32 src, u32 dst, u8 opacity);
void ASE_BlendImage(ASE_Image src_img, ASE_Image dst_img);
// ////////////////////////////////////////////////////////////
// //~ Query
// u32 ASE_FirstPixelFromCel(ASE_Cel *cel);
// ////////////////////////////////////////////////////////////
// //~ Rasterize
// void ASE_RasterizeCel(ASE_Cel *cel, u32 *dst_pixels, Vec2 dst_dims);
// ////////////////////////////////////////////////////////////
// //~ Sheet types
// Struct(ASE_Slice)
// {
// u32 start;
// Rng2I32 rect;
// ASE_Slice *next;
// };
// Struct(ASE_Span)
// {
// String name;
// u32 start;
// u32 end;
// ASE_Span *next;
// };
// Struct(ASE_Frame)
// {
// u32 index;
// Rng2I32 rect;
// f64 duration;
// ASE_Frame *next;
// };
// Struct(ASE_SliceKey)
// {
// String name;
// u32 slices_count;
// ASE_Slice *first_slice;
// ASE_SliceKey *next;
// };
// ////////////////////////////////////////////////////////////
// //~ Decoder result types
// Struct(ASE_Error)
// {
// String msg;
// ASE_Error *next;
// };
// Struct(ASE_ErrorList)
// {
// u64 count;
// ASE_Error *first;
// ASE_Error *last;
// };
// Struct(ASE_DecodedImage)
// {
// u32 width;
// u32 height;
// u32 *pixels; // Array of [width * height] pixels
// ASE_ErrorList errors;
// b32 ok;
// };
// Struct(ASE_DecodedSheet)
// {
// Vec2 image_size;
// Vec2 frame_size;
// u32 frames_count;
// u32 spans_count;
// u32 slice_keys_count;
// ASE_Frame *first_frame;
// ASE_Span *first_span;
// ASE_SliceKey *first_slice_key;
// ASE_ErrorList errors;
// b32 ok;
// };
// ////////////////////////////////////////////////////////////
// //~ Inflator types
// #define ASE_HuffBitCount 16
// Struct(ASE_Bitbuff)
// {
// u8 *data;
// u64 cur_bit;
// };
// Enum(ASE_BlockType)
// {
// ASE_BlockType_Uncompressed = 0,
// ASE_BlockType_CompressedFixed = 1,
// ASE_BlockType_CompressedDynamic = 2,
// ASE_BlockType_Reserved = 3
// };
// Struct(ASE_HuffEntry)
// {
// u16 symbol;
// u16 bits_used;
// };
// Struct(ASE_HuffDict)
// {
// u32 max_code_bits;
// u32 entries_count;
// ASE_HuffEntry *entries;
// };
// ////////////////////////////////////////////////////////////
// //~ Header types
// Packed(Struct(ASE_Header)
// {
// u32 file_size;
// u16 magic;
// u16 frames;
// u16 width;
// u16 height;
// u16 color_depth;
// u32 flags;
// u16 speed;
// u32 _1;
// u32 _2;
// u8 palette_entry;
// u8 _3[3];
// u16 colors_count;
// u8 pixel_width;
// u8 pixel_height;
// i16 grid_x;
// i16 grid_y;
// u16 grid_width;
// u16 grid_height;
// u8 _4[84];
// });
// Packed(Struct(ASE_FrameHeader)
// {
// u32 bytes;
// u16 magic;
// u16 chunks_old;
// u16 frame_duration_ms;
// u8 _[2];
// u32 chunks_new;
// });
// ////////////////////////////////////////////////////////////
// //~ Image decoder types
// Enum(ASE_ChunkKind)
// {
// ASE_ChunkKind_OldPalette1 = 0x0004,
// ASE_ChunkKind_OldPalette2 = 0x0011,
// ASE_ChunkKind_Layer = 0x2004,
// ASE_ChunkKind_Cel = 0x2005,
// ASE_ChunkKind_CelExtra = 0x2006,
// ASE_ChunkKind_ColorProfile = 0x2007,
// ASE_ChunkKind_ExternalFiles = 0x2008,
// ASE_ChunkKind_Mask = 0x2016,
// ASE_ChunkKind_Path = 0x2017,
// ASE_ChunkKind_Tags = 0x2018,
// ASE_ChunkKind_Palette = 0x2019,
// ASE_ChunkKind_Userdata = 0x2020,
// ASE_ChunkKind_Slice = 0x2022,
// ASE_ChunkKind_Tileset = 0x2023
// };
// Enum(ASE_CelKind)
// {
// ASE_CelKind_RawImage = 0,
// ASE_CelKind_Linked = 1,
// ASE_CelKind_CompressedImage = 2,
// ASE_CelKind_CompressedTilemap = 3
// };
// Struct(ASE_Layer)
// {
// u16 flags;
// u16 type;
// u16 child_level;
// u16 blend_mode;
// u8 opacity;
// String name;
// u32 tileset_index;
// u32 index;
// ASE_Layer *next;
// };
// Struct(ASE_Cel)
// {
// u16 layer_index;
// i16 x_pos;
// i16 y_pos;
// u8 opacity;
// ASE_CelKind type;
// i16 z_index;
// // Linked cel
// u16 frame_pos;
// // Compressed image
// u32 width;
// u32 height;
// u32 *pixels;
// u16 frame_index;
// ASE_Cel *next;
// };
// ////////////////////////////////////////////////////////////
// //~ Ase bitbuff helpers
// u32 ASE_PeekBits(ASE_Bitbuff *bb, u32 nbits);
// u32 ASE_ConsumeBits(ASE_Bitbuff *bb, u32 nbits);
// void ASE_SkipBits(ASE_Bitbuff *bb, u32 nbits);
// ////////////////////////////////////////////////////////////
// //~ Inflate
// u32 ASE_ReverseBits(u32 v, u32 bit_count);
// ASE_HuffDict ASE_InitHuffDict(Arena *arena, u32 max_code_bits, u32 *bl_counts, u32 bl_counts_count);
// u16 ASE_DecodeHuffDict(ASE_HuffDict *huffman, ASE_Bitbuff *bb);
// void ASE_Inflate(u8 *dst, u8 *encoded);
// ////////////////////////////////////////////////////////////
// //~ Error helpers
// void ASE_PushError(Arena *arena, ASE_ErrorList *list, String msg_src);
// ////////////////////////////////////////////////////////////
// //~ Decode helpers
// u32 ASE_Blend(u32 src, u32 dst, u8 opacity);
// void ASE_MakeDimensionsSquareish(ASE_Header *header, u32 *frames_x, u32 *frames_y, u64 *image_width, u64 *image_height);
// ////////////////////////////////////////////////////////////
// //~ Decode image
// ASE_DecodedImage ASE_DecodeImage(Arena *arena, String encoded);
// ////////////////////////////////////////////////////////////
// //~ Decode sheet
// ASE_DecodedSheet ASE_DecodeSheet(Arena *arena, String encoded);

View File

@ -372,7 +372,7 @@ void BuildEntryPoint(WaveLaneCtx *lane)
PushStringToList(perm, &cp.warnings_msvc, Lit("-W4"));
PushStringToList(perm, &cp.warnings_msvc, Lit("-WX"));
// PushStringToList(perm, &cp.warnings_msvc, Lit("-we4013")); // function undefined; assuming extern returning int
PushStringToList(perm, &cp.warnings_msvc, Lit("-we4668")); // 'X' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
PushStringToList(perm, &cp.warnings_msvc, Lit("-we4668")); // X is not defined as a preprocessor macro, replacing with '0' for '#if/#elif'
// Disable warnings
PushStringToList(perm, &cp.warnings_msvc, Lit("-wd4244")); // 'function': conversion from 'int' to 'f32', possible loss of data

View File

@ -4265,6 +4265,7 @@ void V_TickForever(WaveLaneCtx *lane)
}
V_TileDesc tile_desc = Zi;
{
tile_desc.tex_slice_uv = tile_slice.tex_rect_uv;
tile_desc.tex = tile_slice.tex;
}
params.tile_descs[tile_kind] = tile_desc;

View File

@ -493,7 +493,7 @@ ComputeShader2D(V_ShadeCS, 8, 8)
Texture2D<P_TileKind> tiles = G_Dereference<P_TileKind>(params.tiles);
RWTexture2D<Vec4> stains = G_Dereference<Vec4>(params.stains);
RWTexture2D<f32> drynesses = G_Dereference<f32>(params.drynesses);
SamplerState wrap_sampler = G_Dereference(params.pt_wrap_sampler);
SamplerState clamp_sampler = G_Dereference(params.pt_clamp_sampler);
Vec2 shade_pos = SV_DispatchThreadID + Vec2(0.5, 0.5);
Vec2 world_pos = mul(params.af.shade_to_world, Vec3(shade_pos, 1));
@ -548,7 +548,8 @@ ComputeShader2D(V_ShadeCS, 8, 8)
{
V_TileDesc tile_desc = params.tile_descs[tile];
Texture2D<Vec4> tile_tex = G_Dereference<Vec4>(tile_desc.tex);
tile_color = tile_tex.SampleLevel(wrap_sampler, world_pos, 0);
Vec2 tile_samp_uv = lerp(tile_desc.tex_slice_uv.p0, tile_desc.tex_slice_uv.p1, frac(world_pos));
tile_color = tile_tex.SampleLevel(clamp_sampler, tile_samp_uv, 0);
}
// Checkered grid
else if (tile == P_TileKind_Empty)

View File

@ -48,6 +48,7 @@ Struct(V_Affines)
Struct(V_TileDesc)
{
Rng2 tex_slice_uv;
G_Texture2DRef tex;
};

View File

@ -108,6 +108,7 @@ SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, i64 f
{
SPR_SliceEntry *slice = &sheet->slices[slice_idx];
slice->bounds = Rng2Empty;
Atomic64Set(&slice->atlas_copy_completion_target, I64Max);
for (SPR_RayKind ray_kind = 0; ray_kind < SPR_RayKind_COUNT; ++ray_kind)
{
slice->rays[ray_kind] = XformIdentity;
@ -204,7 +205,8 @@ SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, i64 f
}
//- Init spans
sheet->span_bins_count = 256;
{
sheet->span_bins_count = MaxI64(1, NextPow2U64(sheet->meta.spans_count * 4));
sheet->span_bins = PushStructs(perm, SPR_SpanBin, sheet->span_bins_count);
for (ASE_Span *ase_span = sheet->meta.first_span; ase_span; ase_span = ase_span->next)
{
@ -224,13 +226,25 @@ SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, i64 f
SllQueuePush(sheet->first_span, sheet->last_span, span);
SllStackPushN(span_bin->first, span, next_in_bin);
span->key = new_span_key;
span->start = ase_span->start;
span->end = ase_span->end;
span->from = ase_span->from;
span->to = ase_span->to;
}
}
// TODO: Proper validation
sheet->ok = 1;
// Insert nil span
{
SPR_SpanBin *span_bin = &sheet->span_bins[0];
SPR_SpanEntry *span = PushStruct(perm, SPR_SpanEntry);
SllQueuePush(sheet->first_span, sheet->last_span, span);
SllStackPushN(span_bin->first, span, next_in_bin);
span->key = SPR_NilSpanKey;
span->from = 0;
span->to = sheet->slices_count;
}
}
// TODO: More rigorous validation
sheet->ok = sheet->meta.ok;
}
}
Unlock(&sheet_bin_lock);
@ -263,8 +277,8 @@ SPR_Slice SPR_SliceFromSheet(SPR_SheetKey sheet_key, SPR_SpanKey span_key, i64 f
}
if (span)
{
span_start = span->start;
span_end = span->end;
span_start = span->from;
span_end = span->to;
}
}

View File

@ -86,8 +86,8 @@ Struct(SPR_SpanEntry)
SPR_SpanEntry *next_in_bin;
SPR_SpanKey key;
i64 start;
i64 end;
i64 from;
i64 to;
};
Struct(SPR_SpanBin)

View File

@ -29,8 +29,6 @@
@IncludeG ui_gpu.gh
@Bootstrap UI_Bootstrap
//////////////////////////////
//- Impl

View File

@ -1,12 +1,5 @@
UI_Ctx UI = Zi;
////////////////////////////////////////////////////////////
//~ Bootstrap
void UI_Bootstrap(void)
{
}
////////////////////////////////////////////////////////////
//~ Key helpers
@ -537,13 +530,11 @@ void UI_SetRawTexture(UI_Key key, G_Texture2DRef tex, Rng2 uv)
UI_BoxReports UI_ReportsFromKey(UI_Key key)
{
UI_BoxReports result = Zi;
UI_Box *box = UI_BoxFromKey(key);
if (box)
{
result = box->reports;
}
return result;
}
@ -629,11 +620,11 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags)
{
ControllerEventsArray controller_events = frame->window_frame.controller_events;
// Locate boxes
//- Locate boxes
UI_Box *top_hovered_box = 0;
UI_Box *active_box = UI_BoxFromKey(prev_frame->active_box);
// Update cursor pos
//- Update cursor pos
for (u64 cev_index = 0; cev_index < controller_events.count; ++cev_index)
{
ControllerEvent cev = controller_events.events[cev_index];
@ -643,7 +634,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags)
}
}
// Init box reports
//- Init box reports
for (u64 pre_index = UI.boxes_count; pre_index-- > 0;)
{
UI_Box *box = prev_frame->boxes_pre[pre_index];
@ -690,7 +681,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags)
report->m3.presses = 0;
}
// Update state from controller events
//- Update state from controller events
i32 mouse_downs = 0;
for (u64 cev_index = 0; cev_index < controller_events.count; ++cev_index)
{
@ -788,7 +779,7 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags)
hot_box = top_hovered_box;
}
// Update box reports
//- Update box reports
{
f32 lower_target = TweakFloat("UI lower blend target", -0.05, -1, 0);
f32 upper_target = TweakFloat("UI upper blend target", 1.05, 1, 10);
@ -804,9 +795,6 @@ UI_Frame *UI_BeginFrame(UI_FrameFlag frame_flags)
f32 target_active = box == active_box ? Inf : lower_target;
f64 target_misc = box->desc.misc;
// TODO: Configurable per-box blend rates
f32 exists_blend_rate = (30 * frame->dt);
f32 hot_blend_rate = (15 * frame->dt);
@ -896,7 +884,6 @@ GC_Run UI_ScaleRun(Arena *arena, GC_Run unscaled_run, Vec2 scale)
{
GC_RunRect *src = &unscaled_run.rects[rect_idx];
GC_RunRect *dst = &result.rects[rect_idx];
*dst = *src;
dst->bounds = MulRng2Vec2(dst->bounds, scale);
dst->advance *= scale.x;
@ -922,10 +909,6 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync)
Rng3 draw_viewport = RNG3(VEC3(0, 0, 0), VEC3(draw_size.x, draw_size.y, 1));
Rng2 draw_scissor = RNG2(VEC2(0, 0), VEC2(draw_size.x, draw_size.y));
//////////////////////////////
//- Process commands
{
//////////////////////////////
//- Create boxes from build cmds
@ -1039,7 +1022,6 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync)
} break;
}
}
}
//////////////////////////////
//- Prune cached boxes

View File

@ -420,11 +420,6 @@ Struct(UI_Ctx)
extern UI_Ctx UI;
////////////////////////////////////////////////////////////
//~ Bootstrap
void UI_Bootstrap(void);
////////////////////////////////////////////////////////////
//~ Key helpers