begin new sprite layer

This commit is contained in:
jacob 2026-01-09 12:45:33 -06:00
parent 2bfc0d2ab2
commit d332315c16
11 changed files with 97 additions and 585 deletions

View File

@ -211,7 +211,7 @@ String SwappedStateFromName(Arena *arena, String name)
{
TempArena scratch = BeginScratch(arena);
String result = Zi;
String path = StringF(scratch.arena, "%F/swap/%F.swp", FmtString(GetAppDirectory()), FmtString(name));
String path = StringF(scratch.arena, "%F/swap/%F", FmtString(GetAppDirectory()), FmtString(name));
wchar_t *path_wstr = WstrFromString(scratch.arena, path);
HANDLE handle = CreateFileW(path_wstr, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (handle != INVALID_HANDLE_VALUE)
@ -240,7 +240,7 @@ void WriteSwappedState(String name, String data)
{
TempArena scratch = BeginScratchNoConflict();
String dir_path = PathFromString(scratch.arena, StringF(scratch.arena, "%F/swap", FmtString(GetAppDirectory())), '\\');
String path = PathFromString(scratch.arena, StringF(scratch.arena, "%F/%F.swp", FmtString(dir_path), FmtString(name)), '\\');
String path = PathFromString(scratch.arena, StringF(scratch.arena, "%F/%F", FmtString(dir_path), FmtString(name)), '\\');
wchar_t *dir_path_wstr = WstrFromString(scratch.arena, dir_path);
wchar_t *path_wstr = WstrFromString(scratch.arena, path);
SHCreateDirectoryExW(0, dir_path_wstr, 0);

View File

@ -223,7 +223,7 @@ S_Shape S_LocalShapeFromEnt(S_Ent *ent)
{
S_Shape result = Zi;
// TODO: This is a temporary hack. Use prefab-lookup table.
// TODO: This is a temporary hack. We should eventually switch to using a prefab lookup table.
if (ent->is_player)
{
result = S_ShapeFromDesc(
@ -1277,7 +1277,7 @@ void S_TickForever(WaveLaneCtx *lane)
String packed = Zi;
if (swapin)
{
packed = SwappedStateFromName(frame_arena, Lit("pp_sim"));
packed = SwappedStateFromName(frame_arena, Lit("pp_sim.swp"));
}
S_UnpackedWorld unpacked = S_UnpackWorld(frame_arena, packed);
@ -1306,7 +1306,7 @@ void S_TickForever(WaveLaneCtx *lane)
if (swapout)
{
String packed = S_PackWorld(frame_arena, world);
WriteSwappedState(Lit("pp_sim"), packed);
WriteSwappedState(Lit("pp_sim.swp"), packed);
}
}

View File

@ -14,3 +14,20 @@ i32 S_TileIdxFromTilePos(Vec2I32 p)
i32 result = ClampI32(p.x + (p.y * S_TilesPitch), 0, S_TilesCount);
return result;
}
#if IsLanguageC
String S_TileNameFromKind(S_TileKind kind)
{
PERSIST Readonly String tile_names[S_TileKind_COUNT] = {
#define X(name, ...) [S_TileKind_##name] = CompLit(#name),
S_TilesXMacro(X)
#undef X
};
String result = Zi;
if (kind >= 0 && kind < countof(tile_names))
{
result = tile_names[kind];
}
return result;
}
#endif

View File

@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////
//~ Tile types
#define S_WorldPitch 60.0
#define S_WorldPitch 64.0
#define S_TilesPitch (S_WorldPitch * 2)
#define S_TilesCount (S_TilesPitch * S_TilesPitch)
@ -11,7 +11,7 @@
X(Wall) \
/* -------------------- */
// Tiles enum
//- Tiles kinds enum
Enum(S_TileKind)
{
#define X(name, ...) S_TileKind_##name,
@ -25,3 +25,7 @@ Enum(S_TileKind)
Vec2I32 S_TilePosFromWorldPos(Vec2 p);
i32 S_TileIdxFromTilePos(Vec2I32 p);
#if IsLanguageC
String S_TileNameFromKind(S_TileKind kind);
#endif

View File

@ -4,12 +4,13 @@
//- Dependencies
@Dep pp
@Dep pp_sim
@Dep sprite
@Dep gpu
@Dep glyph_cache
@Dep platform
@Dep window
@Dep ui
@Dep pp_sim
//////////////////////////////
//- Resources

View File

@ -558,7 +558,7 @@ void V_TickForever(WaveLaneCtx *lane)
}
else
{
String swap_encoded = SwappedStateFromName(frame->arena, Lit("pp_vis"));
String swap_encoded = SwappedStateFromName(frame->arena, Lit("pp_vis.swp"));
bb = BB_BuffFromString(swap_encoded);
br = BB_ReaderFromBuff(&bb);
}
@ -586,7 +586,7 @@ void V_TickForever(WaveLaneCtx *lane)
// Write swapout
if (swapout)
{
WriteSwappedState(Lit("pp_vis"), STRING(BB_GetNumBytesWritten(&bw), BB_GetWrittenRaw(&bw)));
WriteSwappedState(Lit("pp_vis.swp"), STRING(BB_GetNumBytesWritten(&bw), BB_GetWrittenRaw(&bw)));
}
}
}
@ -3194,6 +3194,18 @@ void V_TickForever(WaveLaneCtx *lane)
G_ResourceHandle gpu_params = G_PushBufferFromCpuCopy(frame->gpu_arena, frame->cl, StringFromStruct(&params));
G_StructuredBufferRef gpu_params_ref = G_PushStructuredBufferRef(frame->gpu_arena, gpu_params, V_GpuParams);
// Fill tile textures
{
ResourceKey tiles_resource = ResourceKeyFromStore(&P_Resources, Lit("sprite/tiles.ase"));
for (S_TileKind tile_kind = 0; tile_kind < S_TileKind_COUNT; ++tile_kind)
{
String tile_name = S_TileNameFromKind(tile_kind);
SPR_Key sprite_key = SPR_KeyFromResource(tiles_resource);
SPR_Slice tile_slice = SPR_SliceFromKey(sprite_key, tile_name);
params.tile_slices[tile_kind] = tile_slice;
}
}
// Upload tiles
if (tiles_dirty)
{

View File

@ -84,6 +84,8 @@ Struct(V_GpuParams)
G_RWTexture2DRef cells;
G_RWTexture2DRef stains;
G_RWTexture2DRef drynesses;
SPR_Slice tile_slices[S_TileKind_COUNT];
};
////////////////////////////////////////////////////////////

View File

@ -1,433 +1,40 @@
Readonly SPR_Texture SPR_NilTexture = Zi;
Readonly SPR_Sheet SPR_NilSheet = Zi;
SPR_Ctx SPR = Zi;
////////////////////////////////////////////////////////////
//~ Load jobs
//~ Bootstrap
JobImpl(SPR_LoadTexture, sig, _)
void SPR_Bootstrap(void)
{
TempArena scratch = BeginScratchNoConflict();
SPR_Entry *entry = sig->entry;
ResourceKey resource = entry->resource;
b32 ok = 1;
SPR_Texture *texture = &entry->texture;
texture->valid = 1;
String name = NameFromResource(resource);
String data = DataFromResource(resource);
ASE_DecodedImage decoded = ASE_DecodeImage(scratch.arena, data);
ok = decoded.ok;
// Upload texture to gpu
if (ok)
{
GPU_ArenaHandle gpu_perm = GPU_PermArena();
GPU_ResourceHandle gpu_resource = GPU_PushTexture2D(
gpu_perm,
GPU_Format_R8G8B8A8_Unorm_Srgb,
VEC2I32(decoded.width, decoded.height),
GPU_Layout_AnyQueue_ShaderRead_CopyRead_CopyWrite_Present
);
// texture->texture = gpu_tex;
texture->width = decoded.width;
texture->height = decoded.height;
GPU_CommandListHandle cl = GPU_PrepareCommandList();
{
GPU_ReadTexelsFromCpu(
cl,
gpu_resource, VEC3I32(0,0,0),
decoded.pixels, VEC3I32(decoded.width, decoded.height, 1),
RNG3I32(VEC3I32(0,0,0), VEC3I32(decoded.width, decoded.height, 1))
);
}
GPU_CommitCommandList(cl, GPU_QueueKind_AsyncCopy);
}
EndScratch(scratch);
}
JobImpl(SPR_LoadSheet, sig, _)
{
TempArena scratch = BeginScratchNoConflict();
Arena *perm = PermArena();
SPR_Entry *entry = sig->entry;
ResourceKey resource = entry->resource;
b32 ok = 1;
SPR_Sheet *sheet = &entry->sheet;
sheet->valid = 1;
String name = NameFromResource(resource);
String data = DataFromResource(resource);
ASE_DecodedSheet decoded = ASE_DecodeSheet(scratch.arena, data);
ok = decoded.ok;
if (ok)
{
Vec2 image_size = decoded.image_size;
Vec2 frame_size = decoded.frame_size;
Vec2 frame_center = MulVec2(decoded.frame_size, 0.5f);
sheet->image_size = image_size;
sheet->frame_size = frame_size;
// Init frames
sheet->frames_count = decoded.num_frames;
sheet->frames = PushStructs(perm, SPR_Frame, sheet->frames_count);
for (ASE_Frame *src = decoded.first_frame; src; src = src->next)
{
SPR_Frame *dst = &sheet->frames[src->index];
dst->index = src->index;
dst->duration = src->duration;
dst->clip.p0 = VEC2((f32)src->rect.p0.x / (f32)image_size.x, (f32)src->rect.p0.y / (f32)image_size.y);
dst->clip.p1 = VEC2((f32)src->rect.p1.x / (f32)image_size.x, (f32)src->rect.p1.y / (f32)image_size.y);
}
// Init spans
sheet->spans_count = decoded.num_spans;
sheet->span_bins_count = MaxU32(AlignU64ToNextPow2(sheet->spans_count * 2), 1);
sheet->spans = PushStructs(perm, SPR_Span, sheet->spans_count);
sheet->span_bins = PushStructs(perm, SPR_SpanBin, sheet->span_bins_count);
{
i32 span_index = 0;
for (ASE_Span *src = decoded.first_span; src; src = src->next)
{
SPR_Span *dst = &sheet->spans[span_index];
dst->hash = HashFnv64(Fnv64Basis, src->name);
dst->name = PushString(perm, src->name);
dst->start = src->start;
dst->end = src->end;
// Insert span into bin
{
SPR_SpanBin *bin = &sheet->span_bins[dst->hash % sheet->span_bins_count];
SllQueuePushN(bin->first, bin->last, dst, next_in_bin);
}
++span_index;
}
}
// Init slice groups
sheet->slice_groups_count = decoded.num_slice_keys;
sheet->slice_group_bins_count = MaxU32(AlignU64ToNextPow2(sheet->slice_groups_count * 2), 1);
sheet->slice_groups = PushStructs(perm, SPR_SliceGroup, sheet->slice_groups_count);
sheet->slice_group_bins = PushStructs(perm, SPR_SliceGroupBin, sheet->slice_group_bins_count);
{
i32 group_index = 0;
for (ASE_SliceKey *src_group = decoded.first_slice_key; src_group; src_group = src_group->next)
{
SPR_SliceGroup *dst_group = &sheet->slice_groups[group_index];
dst_group->hash = HashFnv64(Fnv64Basis, src_group->name);
dst_group->name = PushString(perm, src_group->name);
// Init slices
dst_group->slices = PushStructs(perm, SPR_Slice, sheet->frames_count);
{
// Fill is_original slices
for (ASE_Slice *src_slice = src_group->first_slice; src_slice; src_slice = src_slice->next)
{
f32 x0_px = src_slice->rect.p0.x;
f32 y0_px = src_slice->rect.p0.y;
f32 x1_px = src_slice->rect.p1.x;
f32 y1_px = src_slice->rect.p1.y;
f32 width_px = x1_px - x0_px;
f32 height_px = y1_px - y0_px;
f32 x0 = (x0_px - frame_center.x) / frame_size.x;
f32 y0 = (y0_px - frame_center.y) / frame_size.y;
f32 x1 = (x1_px - frame_center.x) / frame_size.x;
f32 y1 = (y1_px - frame_center.y) / frame_size.y;
f32 width = x1 - x0;
f32 height = y1 - y0;
// Rect
Rng2 rect_px = RNG2(VEC2(x0_px, y0_px), VEC2(x1_px, y1_px));
Rng2 rect = RNG2(VEC2(x0, y0), VEC2(x1, y1));
// Center
Vec2 center_px = VEC2(x0_px + (width_px * 0.5f), y0_px + (height_px * 0.5f));
Vec2 center = VEC2(x0 + (width * 0.5f), y0 + (height * 0.5f));
// Dir
Vec2 dir_px = VEC2(center_px.x, -1);
Vec2 dir = VEC2(0, -1);
SPR_Slice *dst_slice = &dst_group->slices[src_slice->start];
dst_slice->is_original = 1;
dst_slice->rect_px = rect_px;
dst_slice->center_px = center_px;
dst_slice->dir_px = dir_px;
dst_slice->rect = rect;
dst_slice->center = center;
dst_slice->dir = dir;
}
// Copy slices forward into frames without a slice
{
SPR_Slice *origin = 0;
for (u32 frame_index = 0; frame_index < sheet->frames_count; ++frame_index)
{
SPR_Slice *slice = &dst_group->slices[frame_index];
if (slice->is_original)
{
origin = slice;
}
else
{
*slice = *origin;
slice->is_original = 0;
}
}
}
}
// Insert group into bin
{
SPR_SliceGroupBin *bin = &sheet->slice_group_bins[dst_group->hash % sheet->slice_group_bins_count];
SllQueuePushN(bin->first, bin->last, dst_group, next_in_bin);
}
++group_index;
}
}
// Init slice ray directions
{
String ray_suffix = Lit(".ray");
for (u32 slice_group_index = 0; slice_group_index < sheet->slice_groups_count; ++slice_group_index)
{
SPR_SliceGroup *group = &sheet->slice_groups[slice_group_index];
if (StringEndsWith(group->name, ray_suffix))
{
String point_slice_group_name = group->name;
point_slice_group_name.len -= ray_suffix.len;
u64 point_slice_group_hash = HashFnv64(Fnv64Basis, point_slice_group_name);
SPR_SliceGroupBin *bin = &sheet->slice_group_bins[point_slice_group_hash % sheet->slice_group_bins_count];
SPR_SliceGroup *point_slice_group = bin->first;
for (; point_slice_group; point_slice_group = point_slice_group->next_in_bin)
{
if (point_slice_group->hash == point_slice_group_hash)
{
break;
}
}
if (point_slice_group)
{
for (u32 frame_index = 0; frame_index < sheet->frames_count; ++frame_index)
{
SPR_Slice *point_slice = &point_slice_group->slices[frame_index];
SPR_Slice *ray_slice = &group->slices[frame_index];
Vec2 ray_end = ray_slice->center_px;
Vec2 ray_end_norm = ray_slice->center;
point_slice->dir_px = SubVec2(ray_end, point_slice->center_px);
point_slice->dir = SubVec2(ray_end_norm, point_slice->center);
point_slice->has_dir = 1;
}
}
}
}
}
}
SetFence(&entry->sheet_ready_fence, 1);
EndScratch(scratch);
}
////////////////////////////////////////////////////////////
//~ Cache
// TODO: Per-thread L1 cache
SPR_Entry *SPR_FetchEntry(ResourceKey resource, JobPoolId pool, SPR_FetchFlag flags)
{
SPR_Entry *entry = 0;
{
SPR_EntryBin *bin = &SPR.entry_bins[resource.hash % countof(SPR.entry_bins)];
// Search for entry
entry = bin->first;
{
Lock lock = LockS(&bin->mutex);
{
for (; entry; entry = entry->next_in_bin)
{
if (entry->resource.hash == resource.hash)
{
break;
}
}
}
Unlock(&lock);
}
// Entry not found: lock, re-search, & create
if (!entry)
{
Lock lock = LockE(&bin->mutex);
{
// Re-search
entry = bin->first;
for (; entry; entry = entry->next_in_bin)
{
if (entry->resource.hash == resource.hash)
{
break;
}
}
// Create
if (!entry)
{
Arena *perm = PermArena();
entry = PushStruct(perm, SPR_Entry);
entry->resource = resource;
SllQueuePushN(bin->first, bin->last, entry, next_in_bin);
}
}
Unlock(&lock);
}
}
// Launch load jobs
if ((flags & SPR_FetchFlag_Texture)
&& !Atomic32Fetch(&entry->texture_touched)
&& !Atomic32FetchTestSet(&entry->texture_touched, 0, 1))
{
RunJob(SPR_LoadTexture, .pool = pool, .sig.entry = entry);
}
if ((flags & SPR_FetchFlag_Sheet)
&& !Atomic32Fetch(&entry->sheet_touched)
&& !Atomic32FetchTestSet(&entry->sheet_touched, 0, 1))
{
RunJob(SPR_LoadSheet, .pool = pool, .sig.entry = entry);
}
return entry;
}
////////////////////////////////////////////////////////////
//~ Sprite data retrieval
SPR_Texture *SPR_TextureFromResource(ResourceKey resource)
{
SPR_Entry *entry = SPR_FetchEntry(resource, CurrentPool(), SPR_FetchFlag_Texture);
YieldOnFence(&entry->texture_ready_fence, 1);
return &entry->texture;
}
SPR_Texture *SPR_TextureFromResourceAsync(ResourceKey resource)
{
SPR_Texture *result = &SPR_NilTexture;
SPR_Entry *entry = SPR_FetchEntry(resource, AsyncPool(), SPR_FetchFlag_Texture);
if (FetchFence(&entry->texture_ready_fence) >= 1)
{
result = &entry->texture;
}
return result;
}
SPR_Sheet *SPR_SheetFromResource(ResourceKey resource)
{
SPR_Entry *entry = SPR_FetchEntry(resource, CurrentPool(), SPR_FetchFlag_Sheet);
YieldOnFence(&entry->sheet_ready_fence, 1);
return &entry->sheet;
}
SPR_Sheet *SPR_SheetFromResourceAsync(ResourceKey resource)
{
SPR_Sheet *result = &SPR_NilSheet;
SPR_Entry *entry = SPR_FetchEntry(resource, AsyncPool(), SPR_FetchFlag_Sheet);
if (FetchFence(&entry->sheet_ready_fence) >= 1)
{
result = &entry->sheet;
}
return result;
OnAsyncTick(SPR_TickAsync);
}
////////////////////////////////////////////////////////////
//~ Key helpers
SPR_SpanKey SPR_SpanKeyFromName(String name)
SPR_Key SPR_KeyFromResource(ResourceKey resource)
{
SPR_SpanKey result = Zi;
result.hash = HashFnv64(Fnv64Basis, name);
return result;
}
SPR_SliceKey SPR_SliceKeyFromName(String name)
{
SPR_SliceKey result = Zi;
result.hash = HashFnv64(Fnv64Basis, name);
SPR_Key result = Zi;
return result;
}
////////////////////////////////////////////////////////////
//~ Sheet access
//~ Lookup
SPR_Span SPR_SpanFromKey(SPR_Sheet *sheet, SPR_SpanKey key)
{
SPR_Span result = Zi;
u32 bins_count = sheet->span_bins_count;
if (bins_count > 0)
{
SPR_SpanBin *bin = &sheet->span_bins[key.hash % bins_count];
SPR_Span *span = bin->first;
for (; span; span = span->next_in_bin)
{
if (span->hash == key.hash)
{
result = *span;
break;
}
}
}
return result;
}
SPR_Frame SPR_FrameFromIndex(SPR_Sheet *sheet, u64 index)
{
SPR_Frame result = Zi;
if (sheet->frames_count > 0)
{
result = sheet->frames[index % sheet->frames_count];
}
return result;
}
SPR_Slice SPR_SliceFromKey(SPR_Sheet *sheet, SPR_SliceKey key, u64 frame_index)
SPR_Slice SPR_SliceFromKey(SPR_Key key, String slice_name)
{
SPR_Slice result = Zi;
b32 match = 0;
u32 bins_count = sheet->slice_group_bins_count;
if (bins_count > 0 && sheet->frames_count > 0)
{
SPR_SliceGroupBin *bin = &sheet->slice_group_bins[key.hash % bins_count];
SPR_SliceGroup *group = bin->first;
for (; group; group = group->next_in_bin)
{
if (group->hash == key.hash)
{
result = group->slices[frame_index % sheet->frames_count];
match = 1;
break;
}
}
}
// u64 hash = key.hash;
// hash = Mi
// Return 'pivot' by default
if (!match)
{
SPR_SliceKey pivot_key = SPR_SliceKeyFromName(Lit("pivot"));
if (key.hash == pivot_key.hash)
{
// 'pivot' slice does not exist, return center
result.center = VEC2(0, 0);
result.center_px = MulVec2(sheet->frame_size, 0.5f);
result.dir_px = VEC2(result.center_px.x, 0);
result.dir = VEC2(0, -0.5);
}
else
{
result = SPR_SliceFromKey(sheet, pivot_key, frame_index);
}
}
// SPR_SliceBin *bin = &SPR.slice_bins[
return result;
}
////////////////////////////////////////////////////////////
//~ Async
void SPR_TickAsync(WaveLaneCtx *lane, AsyncFrameLaneCtx *base_async_lane_frame)
{
}

View File

@ -1,180 +1,29 @@
////////////////////////////////////////////////////////////
//~ Key types
Struct(SPR_SpanKey)
{
u64 hash;
};
Struct(SPR_SliceKey)
{
u64 hash;
};
////////////////////////////////////////////////////////////
//~ Texture types
Struct(SPR_Texture)
{
b32 valid;
Texture2DGpuPtr texture;
u32 width;
u32 height;
};
////////////////////////////////////////////////////////////
//~ Sheet types
Struct(SPR_Frame)
{
u32 index;
f64 duration;
Rng2 clip;
};
Struct(SPR_Span)
{
SPR_Span *next_in_bin;
u64 hash;
String name;
u32 start;
u32 end;
};
Struct(SPR_SpanBin)
{
SPR_Span *first;
SPR_Span *last;
};
Struct(SPR_Slice)
{
// If 1, this slice was not copied over from another frame in the sprite sheet
b32 is_original;
// If 1, the slice has a corresponding '.ray' slice affecting the 'dir' fields
b32 has_dir;
// Values are in the range -0.5 (top / left edge) -> +0.5 (bottom / right edge)
Rng2 rect;
Vec2 center;
Vec2 dir;
// '_px' values retain the original sprite pixel dimensions
Rng2 rect_px;
Vec2 center_px;
Vec2 dir_px;
};
Struct(SPR_SliceGroup)
{
SPR_SliceGroup *next_in_bin;
u64 hash;
String name;
SPR_Slice *slices;
};
Struct(SPR_SliceGroupBin)
{
SPR_SliceGroup *first;
SPR_SliceGroup *last;
};
Struct(SPR_Sheet)
{
b32 valid;
Vec2 image_size;
Vec2 frame_size;
u32 frames_count;
SPR_Frame *frames;
u32 spans_count;
SPR_Span *spans;
u32 slice_groups_count;
SPR_SliceGroup *slice_groups;
u32 span_bins_count;
SPR_SpanBin *span_bins;
u32 slice_group_bins_count;
SPR_SliceGroupBin *slice_group_bins;
};
////////////////////////////////////////////////////////////
//~ Cache types
Enum(SPR_FetchFlag)
{
SPR_FetchFlag_None = 0,
SPR_FetchFlag_Texture = (1 << 0),
SPR_FetchFlag_Sheet = (1 << 1),
};
Struct(SPR_Entry)
{
SPR_Entry *next_in_bin;
SPR_Texture texture;
SPR_Sheet sheet;
Atomic32 texture_touched;
Atomic32 sheet_touched;
ResourceKey resource;
Fence texture_ready_fence;
Fence sheet_ready_fence;
};
Struct(SPR_EntryBin)
{
SPR_Entry *first;
SPR_Entry *last;
Mutex mutex;
};
////////////////////////////////////////////////////////////
//~ State types
Struct(SPR_Ctx)
{
SPR_EntryBin entry_bins[1024];
i32 _;
};
extern SPR_Ctx SPR;
extern Readonly SPR_Sheet SPR_NilSheet;
extern Readonly SPR_Texture SPR_NilTexture;
////////////////////////////////////////////////////////////
//~ Load jobs
//~ Bootstrap
JobDecl(SPR_LoadTexture, { SPR_Entry *entry; });
JobDecl(SPR_LoadSheet, { SPR_Entry *entry; });
////////////////////////////////////////////////////////////
//~ Cache
SPR_Entry *SPR_FetchEntry(ResourceKey resource, JobPoolId pool, SPR_FetchFlag flags);
////////////////////////////////////////////////////////////
//~ Sprite data retrieval
SPR_Texture *SPR_TextureFromResource(ResourceKey resource);
SPR_Texture *SPR_TextureFromResourceAsync(ResourceKey resource);
SPR_Sheet *SPR_SheetFromResource(ResourceKey resource);
SPR_Sheet *SPR_SheetFromResourceAsync(ResourceKey resource);
void SPR_Bootstrap(void);
////////////////////////////////////////////////////////////
//~ Key helpers
SPR_SpanKey SPR_SpanKeyFromName(String name);
SPR_SliceKey SPR_SliceKeyFromName(String name);
SPR_Key SPR_KeyFromResource(ResourceKey resource);
////////////////////////////////////////////////////////////
//~ Sheet access
//~ Lookup
SPR_Span SPR_SpanFromKey(SPR_Sheet *sheet, SPR_SpanKey key);
SPR_Frame SPR_FrameFromIndex(SPR_Sheet *sheet, u64 index);
SPR_Slice SPR_SliceFromKey(SPR_Sheet *sheet, SPR_SliceKey key, u64 frame_index);
SPR_Slice SPR_SliceFromKey(SPR_Key key, String slice_name);
////////////////////////////////////////////////////////////
//~ Async
void SPR_TickAsync(WaveLaneCtx *lane, AsyncFrameLaneCtx *base_async_lane_frame);

View File

@ -9,8 +9,12 @@
//////////////////////////////
//- Api
@IncludeC sprite_shared.cgh
@IncludeG sprite_shared.cgh
@IncludeC sprite.h
@Bootstrap SPR_Bootstrap
//////////////////////////////
//- Impl

View File

@ -0,0 +1,16 @@
////////////////////////////////////////////////////////////
//~ Key types
Struct(SPR_Key)
{
u64 v;
};
////////////////////////////////////////////////////////////
//~ Slice types
Struct(SPR_Slice)
{
G_Texture2DRef tex;
Rng2 uv;
};