gpu particle testing
This commit is contained in:
parent
0e269f0e83
commit
b8e08c72ad
Binary file not shown.
Binary file not shown.
@ -486,9 +486,11 @@
|
||||
typedef u32 b32;
|
||||
#elif IsLanguageG
|
||||
typedef int i32;
|
||||
typedef int64_t i64;
|
||||
typedef uint u32;
|
||||
typedef uint64_t u64;
|
||||
typedef float f32;
|
||||
typedef uint b32;
|
||||
typedef bool b32; // bool has 32-bit size & alignment in HLSL
|
||||
#endif
|
||||
|
||||
//- Min / max constants
|
||||
@ -609,6 +611,7 @@
|
||||
#define CompLit(cstr_lit) { .len = (sizeof((cstr_lit)) - 1), .text = (u8 *)(cstr_lit) }
|
||||
#define StringFromPointers(p0, p1) ((String) { (u8 *)(p1) - (u8 *)(p0), (u8 *)p0 })
|
||||
#define StringFromStruct(ptr) ((String) { sizeof(*(ptr)), (u8 *)(ptr) })
|
||||
#define StringFromStructs(ptr, count) ((String) { sizeof(*(ptr)) * (count), (u8 *)(ptr) })
|
||||
#define StringFromArena(arena) (STRING((arena)->pos, ArenaFirst(arena, u8)))
|
||||
|
||||
#define StringFromFixedArray(a) \
|
||||
@ -742,23 +745,23 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Basic mixing/hashing
|
||||
|
||||
#if IsLanguageC
|
||||
// Based on Jon Maiga's "mx3"
|
||||
// https://jonkagstrom.com/mx3/mx3_rev2.html
|
||||
u64 MixU64(u64 seed)
|
||||
{
|
||||
// Based on Jon Maiga's "mx3"
|
||||
// https://jonkagstrom.com/mx3/mx3_rev2.html
|
||||
u64 MixU64(u64 seed)
|
||||
{
|
||||
seed = (seed ^ (seed >> 32)) * 0xbea225f9eb34556d;
|
||||
seed = (seed ^ (seed >> 29)) * 0xbea225f9eb34556d;
|
||||
seed = (seed ^ (seed >> 32)) * 0xbea225f9eb34556d;
|
||||
seed = (seed ^ (seed >> 29));
|
||||
return seed;
|
||||
}
|
||||
}
|
||||
|
||||
u64 MixU64s(u64 seed_a, u64 seed_b)
|
||||
{
|
||||
u64 MixU64s(u64 seed_a, u64 seed_b)
|
||||
{
|
||||
return MixU64((seed_a * 3) + seed_b);
|
||||
}
|
||||
}
|
||||
|
||||
#if IsLanguageC
|
||||
// FNV-1a parameters for different hash sizes:
|
||||
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV_hash_parameters
|
||||
Inline u64 HashFnv64(u64 seed, String s)
|
||||
|
||||
@ -103,11 +103,11 @@ G_ForceDeclConstant(f32, G_ShaderConst_TweakF32, 10
|
||||
template<typename T> u32 countof(RWStructuredBuffer<T> buff) { u32 result; buff.GetDimensions(result); return result; }
|
||||
u32 countof(ByteAddressBuffer buff) { u32 result; buff.GetDimensions(result); return result; }
|
||||
u32 countof(RWByteAddressBuffer buff) { u32 result; buff.GetDimensions(result); return result; }
|
||||
u32 countof(Texture1D tex) { u32 result; tex.GetDimensions(result); return result; }
|
||||
template<typename T> u32 countof(Texture1D<T> tex) { u32 result; tex.GetDimensions(result); return result; }
|
||||
template<typename T> u32 countof(RWTexture1D<T> tex) { u32 result; tex.GetDimensions(result); return result; }
|
||||
Vec2U32 countof(Texture2D tex) { Vec2U32 result; tex.GetDimensions(result.x, result.y); return result; }
|
||||
template<typename T> Vec2U32 countof(Texture2D<T> tex) { Vec2U32 result; tex.GetDimensions(result.x, result.y); return result; }
|
||||
template<typename T> Vec2U32 countof(RWTexture2D<T> tex) { Vec2U32 result; tex.GetDimensions(result.x, result.y); return result; }
|
||||
Vec3U32 countof(Texture3D tex) { Vec3U32 result; tex.GetDimensions(result.x, result.y, result.z); return result; }
|
||||
template<typename T> Vec3U32 countof(Texture3D<T> tex) { Vec3U32 result; tex.GetDimensions(result.x, result.y, result.z); return result; }
|
||||
template<typename T> Vec3U32 countof(RWTexture3D<T> tex) { Vec3U32 result; tex.GetDimensions(result.x, result.y, result.z); return result; }
|
||||
#endif
|
||||
|
||||
|
||||
@ -8,8 +8,9 @@
|
||||
//////////////////////////////
|
||||
//- Api
|
||||
|
||||
@IncludeC pp_sim_tiles.cgh
|
||||
@IncludeG pp_sim_tiles.cgh
|
||||
@IncludeC pp_sim_shared.cgh
|
||||
@IncludeG pp_sim_shared.cgh
|
||||
|
||||
@IncludeC pp_sim_core.h
|
||||
@IncludeC pp_sim_transcode.h
|
||||
|
||||
@ -20,5 +21,5 @@
|
||||
|
||||
@IncludeC pp_sim_core.c
|
||||
@IncludeC pp_sim_transcode.c
|
||||
@IncludeC pp_sim_tiles.cg
|
||||
@IncludeG pp_sim_tiles.cg
|
||||
@IncludeC pp_sim_shared.cg
|
||||
@IncludeG pp_sim_shared.cg
|
||||
|
||||
@ -1469,8 +1469,6 @@ void S_TickForever(WaveLaneCtx *lane)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//////////////////////////////
|
||||
//- Debug draw entities
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Tile types
|
||||
|
||||
#define S_WorldPitch 96
|
||||
#define S_WorldPitch 96.0
|
||||
#define S_TilesPitch (S_WorldPitch * 2)
|
||||
#define S_TilesCount (S_TilesPitch * S_TilesPitch)
|
||||
|
||||
@ -22,15 +22,18 @@
|
||||
@PixelShader V_DVertPS
|
||||
@VertexShader V_OverlayVS
|
||||
@PixelShader V_OverlayPS
|
||||
@ComputeShader V_EmitParticlesCS
|
||||
@ComputeShader V_SimParticlesCS
|
||||
|
||||
//////////////////////////////
|
||||
//- Api
|
||||
|
||||
@IncludeC pp_vis_shaders.cgh
|
||||
@IncludeC pp_vis_shared.cgh
|
||||
@IncludeG pp_vis_shared.cgh
|
||||
|
||||
@IncludeC pp_vis_core.h
|
||||
|
||||
@IncludeG pp_vis_shaders.cgh
|
||||
@IncludeG pp_vis_shaders.gh
|
||||
@IncludeG pp_vis_gpu.gh
|
||||
|
||||
@Bootstrap V_Bootstrap
|
||||
|
||||
@ -39,4 +42,4 @@
|
||||
|
||||
@IncludeC pp_vis_core.c
|
||||
|
||||
@IncludeG pp_vis_shaders.g
|
||||
@IncludeG pp_vis_gpu.g
|
||||
|
||||
@ -307,12 +307,37 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
world->ent_bins_count = Kibi(16);
|
||||
world->ent_bins = PushStructs(world_arena, S_EntBin, world->ent_bins_count);
|
||||
world->tiles = PushStructs(world_arena, u8, S_TilesCount);
|
||||
Vec2I32 tiles_dims = VEC2I32(S_TilesPitch, S_TilesPitch);
|
||||
|
||||
Vec2I32 tiles_dims = VEC2I32(S_TilesPitch, S_TilesPitch);
|
||||
Vec2I32 decals_dims = VEC2I32(V_PixelsPerMeter * S_WorldPitch, V_PixelsPerMeter * S_WorldPitch);
|
||||
|
||||
u32 max_particles = Kibi(128);
|
||||
|
||||
// Init gpu state
|
||||
G_ResourceHandle gpu_state = Zi;
|
||||
G_ResourceHandle gpu_tiles = Zi;
|
||||
G_ResourceHandle gpu_particles = Zi;
|
||||
G_ResourceHandle gpu_decals = Zi;
|
||||
G_RWStructuredBufferRef gpu_state_ref = Zi;
|
||||
G_Texture2DRef gpu_tiles_ref = Zi;
|
||||
G_RWStructuredBufferRef gpu_particles_ref = Zi;
|
||||
G_RWTexture2DRef gpu_decals_ref = Zi;
|
||||
{
|
||||
// FIXME: Clear initial resource memory
|
||||
G_CommandListHandle cl = G_PrepareCommandList(G_QueueKind_Direct);
|
||||
{
|
||||
// Init state buffer
|
||||
{
|
||||
gpu_state = G_PushBuffer(
|
||||
gpu_perm, cl,
|
||||
V_GpuState,
|
||||
1,
|
||||
.flags = G_ResourceFlag_AllowShaderReadWrite
|
||||
);
|
||||
gpu_state_ref = G_PushRWStructuredBufferRef(gpu_perm, gpu_state, V_GpuState);
|
||||
}
|
||||
|
||||
// Init tile map texture
|
||||
{
|
||||
gpu_tiles = G_PushTexture2D(
|
||||
gpu_perm, cl,
|
||||
@ -321,13 +346,28 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
G_Layout_DirectQueue_ShaderRead_ShaderReadWrite_CopyRead_CopyWrite
|
||||
);
|
||||
gpu_tiles_ref = G_PushTexture2DRef(gpu_perm, gpu_tiles);
|
||||
// TODO: Clear tiles from GPU (instead of copy from cpu)
|
||||
G_CopyCpuToTexture(
|
||||
cl,
|
||||
gpu_tiles, VEC3I32(0, 0, 0),
|
||||
world->tiles, VEC3I32(tiles_dims.x, tiles_dims.y, 1),
|
||||
RNG3I32(VEC3I32(0, 0, 0), VEC3I32(tiles_dims.x, tiles_dims.y, 1))
|
||||
}
|
||||
// Init particle buffer
|
||||
{
|
||||
gpu_particles = G_PushBuffer(
|
||||
gpu_perm, cl,
|
||||
V_Particle,
|
||||
max_particles,
|
||||
.flags = G_ResourceFlag_AllowShaderReadWrite
|
||||
);
|
||||
gpu_particles_ref = G_PushRWStructuredBufferRef(gpu_perm, gpu_particles, V_Particle);
|
||||
}
|
||||
// Init decal texture
|
||||
{
|
||||
gpu_decals = G_PushTexture2D(
|
||||
gpu_perm, cl,
|
||||
G_Format_R8_Uint,
|
||||
decals_dims,
|
||||
G_Layout_DirectQueue_ShaderReadWrite,
|
||||
.flags = G_ResourceFlag_AllowShaderReadWrite
|
||||
);
|
||||
gpu_decals_ref = G_PushRWTexture2DRef(gpu_perm, gpu_decals);
|
||||
}
|
||||
}
|
||||
G_CommitCommandList(cl);
|
||||
}
|
||||
@ -342,7 +382,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- State
|
||||
//- Init shortcuts
|
||||
|
||||
// Init shortcuts
|
||||
u64 shortcut_bins_count = 1024;
|
||||
@ -420,6 +460,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
frame->time_ns = TimeNs();
|
||||
frame->dt_ns = ClampI64(frame->time_ns - last_frame->time_ns, 1, NsFromSeconds(1.0 / 50));
|
||||
frame->dt = SecondsFromNs(frame->dt_ns);
|
||||
frame->rand = last_frame->rand;
|
||||
|
||||
if (S_IsKeyNil(V.player_key))
|
||||
{
|
||||
@ -727,11 +768,11 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Initialize world <-> draw <-> ui transforms
|
||||
//- Compute frame xforms
|
||||
|
||||
// World <-> ui
|
||||
frame->world_to_ui_xf = XformIdentity;
|
||||
frame->ui_to_world_xf = XformIdentity;
|
||||
frame->xf.world_to_ui = XformIdentity;
|
||||
frame->xf.ui_to_world = XformIdentity;
|
||||
{
|
||||
// Determine target camera pos
|
||||
Vec2 target_camera_pos = Zi;
|
||||
@ -812,35 +853,45 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
frame->camera_zoom = LerpF32(last_frame->camera_zoom, target_camera_zoom, frame->camera_lerp_rate);
|
||||
{
|
||||
f32 camera_scale = (f32)frame->draw_dims.x / (meters_per_draw_width * frame->camera_zoom);
|
||||
frame->world_to_ui_xf = XformFromScale(VEC2(camera_scale, camera_scale));
|
||||
frame->world_to_ui_xf = TranslateXform(frame->world_to_ui_xf, NegVec2(frame->camera_pos));
|
||||
frame->world_to_ui_xf = WorldTranslateXform(frame->world_to_ui_xf, MulVec2(Vec2FromVec(frame->draw_dims), 0.5));
|
||||
frame->ui_to_world_xf = InvertXform(frame->world_to_ui_xf);
|
||||
frame->xf.world_to_ui = XformFromScale(VEC2(camera_scale, camera_scale));
|
||||
frame->xf.world_to_ui = TranslateXform(frame->xf.world_to_ui, NegVec2(frame->camera_pos));
|
||||
frame->xf.world_to_ui = WorldTranslateXform(frame->xf.world_to_ui, MulVec2(Vec2FromVec(frame->draw_dims), 0.5));
|
||||
frame->xf.ui_to_world = InvertXform(frame->xf.world_to_ui);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw <-> ui
|
||||
frame->draw_to_ui_xf = XformIdentity;
|
||||
frame->ui_to_draw_xf = XformIdentity;
|
||||
frame->xf.draw_to_ui = XformIdentity;
|
||||
frame->xf.ui_to_draw = XformIdentity;
|
||||
{
|
||||
frame->ui_to_draw_xf = InvertXform(frame->draw_to_ui_xf);
|
||||
frame->xf.ui_to_draw = InvertXform(frame->xf.draw_to_ui);
|
||||
}
|
||||
|
||||
// World <-> draw
|
||||
frame->world_to_draw_xf = XformIdentity;
|
||||
frame->draw_to_world_xf = XformIdentity;
|
||||
frame->xf.world_to_draw = XformIdentity;
|
||||
frame->xf.draw_to_world = XformIdentity;
|
||||
{
|
||||
frame->world_to_draw_xf = MulXform(frame->world_to_ui_xf, frame->ui_to_draw_xf);
|
||||
frame->draw_to_world_xf = InvertXform(frame->world_to_draw_xf);
|
||||
frame->xf.world_to_draw = MulXform(frame->xf.world_to_ui, frame->xf.ui_to_draw);
|
||||
frame->xf.draw_to_world = InvertXform(frame->xf.world_to_draw);
|
||||
}
|
||||
|
||||
// World <-> decal
|
||||
// TODO: This can be constant (so shaders don't need to read it every frame)
|
||||
frame->xf.world_to_decal = XformIdentity;
|
||||
frame->xf.decal_to_world = XformIdentity;
|
||||
{
|
||||
frame->xf.world_to_decal = ScaleXform(frame->xf.world_to_decal, VEC2(V_PixelsPerMeter, V_PixelsPerMeter));
|
||||
frame->xf.world_to_decal = TranslateXform(frame->xf.world_to_decal, VEC2((S_WorldPitch / 2.0), (S_WorldPitch / 2.0)));
|
||||
frame->xf.decal_to_world = InvertXform(frame->xf.world_to_decal);
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Update cursors / selection
|
||||
|
||||
frame->ui_cursor = ui_frame->cursor_pos;
|
||||
frame->draw_cursor = MulXformV2(frame->ui_to_draw_xf, frame->ui_cursor);
|
||||
frame->world_cursor = MulXformV2(frame->ui_to_world_xf, frame->ui_cursor);
|
||||
frame->draw_cursor = MulXformV2(frame->xf.ui_to_draw, frame->ui_cursor);
|
||||
frame->world_cursor = MulXformV2(frame->xf.ui_to_world, frame->ui_cursor);
|
||||
|
||||
frame->world_selection_start = frame->world_cursor;
|
||||
if (frame->is_editing)
|
||||
@ -870,11 +921,11 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
frame->world_selection.p1.x = MaxF32(frame->world_cursor.x, frame->world_selection_start.x);
|
||||
frame->world_selection.p1.y = MaxF32(frame->world_cursor.y, frame->world_selection_start.y);
|
||||
|
||||
frame->ui_selection.p0 = MulXformV2(frame->world_to_ui_xf, frame->world_selection.p0);
|
||||
frame->ui_selection.p1 = MulXformV2(frame->world_to_ui_xf, frame->world_selection.p1);
|
||||
frame->ui_selection.p0 = MulXformV2(frame->xf.world_to_ui, frame->world_selection.p0);
|
||||
frame->ui_selection.p1 = MulXformV2(frame->xf.world_to_ui, frame->world_selection.p1);
|
||||
|
||||
frame->draw_selection.p0 = MulXformV2(frame->world_to_draw_xf, frame->world_selection.p0);
|
||||
frame->draw_selection.p1 = MulXformV2(frame->world_to_draw_xf, frame->world_selection.p1);
|
||||
frame->draw_selection.p0 = MulXformV2(frame->xf.world_to_draw, frame->world_selection.p0);
|
||||
frame->draw_selection.p1 = MulXformV2(frame->xf.world_to_draw, frame->world_selection.p1);
|
||||
|
||||
//////////////////////////////
|
||||
//- Place tiles
|
||||
@ -2111,12 +2162,14 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
{
|
||||
UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
|
||||
Vec2I32 tile_pos = S_TilePosFromWorldPos(frame->world_cursor);
|
||||
Vec2 decal_pos = MulXformV2(frame->xf.world_to_decal, frame->world_cursor);
|
||||
i32 tile_idx = S_TileIdxFromTilePos(tile_pos);
|
||||
{
|
||||
UI_BuildLabelF("Camera pos: %F", FmtFloat2(frame->camera_pos));
|
||||
UI_BuildLabelF("Cursor world pos: %F", FmtFloat2(frame->world_cursor));
|
||||
UI_BuildLabelF("Cursor tile pos: %F", FmtSint2(tile_pos));
|
||||
UI_BuildLabelF("Cursor tile idx: %F", FmtSint(tile_idx));
|
||||
UI_BuildLabelF("Cursor decal pos: %F", FmtFloat2(decal_pos));
|
||||
UI_BuildLabelF("Hovered ent: %F", S_FmtKey(hovered_ent->key));
|
||||
}
|
||||
UI_BuildSpacer(UI_PIX(padding, 1), Axis_Y);
|
||||
@ -2498,11 +2551,10 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
//////////////////////////////
|
||||
//- Draw entities
|
||||
|
||||
// Build shape buffers
|
||||
for (S_Ent *ent = S_FirstEnt(world); ent->valid; ent = S_NextEnt(ent))
|
||||
{
|
||||
Xform ent_to_world_xf = ent->xf;
|
||||
Xform ent_to_draw_xf = MulXform(frame->world_to_draw_xf, ent_to_world_xf);
|
||||
Xform ent_to_draw_xf = MulXform(frame->xf.world_to_draw, ent_to_world_xf);
|
||||
S_Shape draw_shape = S_MulXformShape(ent_to_draw_xf, ent->local_shape);
|
||||
|
||||
f32 opacity = 0.5;
|
||||
@ -2561,7 +2613,7 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
{
|
||||
case S_DebugDrawKind_Point:
|
||||
{
|
||||
Vec2 ui_p = MulXformV2(frame->world_to_ui_xf, desc->point.p);
|
||||
Vec2 ui_p = MulXformV2(frame->xf.world_to_ui, desc->point.p);
|
||||
S_Shape ui_shape = S_ShapeFromDesc(
|
||||
.count = 1,
|
||||
.points = { ui_p },
|
||||
@ -2572,27 +2624,68 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
|
||||
case S_DebugDrawKind_Line:
|
||||
{
|
||||
Vec2 ui_p0 = MulXformV2(frame->world_to_ui_xf, desc->line.p0);
|
||||
Vec2 ui_p1 = MulXformV2(frame->world_to_ui_xf, desc->line.p1);
|
||||
Vec2 ui_p0 = MulXformV2(frame->xf.world_to_ui, desc->line.p0);
|
||||
Vec2 ui_p1 = MulXformV2(frame->xf.world_to_ui, desc->line.p1);
|
||||
V_DrawLine(ui_p0, ui_p1, color);
|
||||
} break;
|
||||
|
||||
case S_DebugDrawKind_Rect:
|
||||
{
|
||||
Rng2 ui_rect = Zi;
|
||||
ui_rect.p0 = MulXformV2(frame->world_to_ui_xf, desc->rect.p0);
|
||||
ui_rect.p1 = MulXformV2(frame->world_to_ui_xf, desc->rect.p1);
|
||||
ui_rect.p0 = MulXformV2(frame->xf.world_to_ui, desc->rect.p0);
|
||||
ui_rect.p1 = MulXformV2(frame->xf.world_to_ui, desc->rect.p1);
|
||||
V_DrawRect(ui_rect, color, V_DrawFlag_Line);
|
||||
} break;
|
||||
|
||||
case S_DebugDrawKind_Shape:
|
||||
{
|
||||
S_Shape ui_shape = S_MulXformShape(frame->world_to_ui_xf, desc->shape);
|
||||
S_Shape ui_shape = S_MulXformShape(frame->xf.world_to_ui, desc->shape);
|
||||
V_DrawShape(ui_shape, color, detail, V_DrawFlag_Line);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Build emitters
|
||||
|
||||
i64 emitters_count = 0;
|
||||
V_EmitterNode *first_emitter_node = 0;
|
||||
V_EmitterNode *last_emitter_node = 0;
|
||||
|
||||
// Spawn test emitters
|
||||
{
|
||||
V_Emitter *emitter = 0;
|
||||
{
|
||||
V_EmitterNode *en = PushStruct(frame->arena, V_EmitterNode);
|
||||
SllQueuePush(first_emitter_node, last_emitter_node, en);
|
||||
emitters_count += 1;
|
||||
emitter = &en->emitter;
|
||||
}
|
||||
emitter->particle_kind = V_ParticleKind_Test;
|
||||
|
||||
Vec2 dir = VEC2(0, 1);
|
||||
emitter->pos = frame->world_cursor;
|
||||
emitter->angle = AngleFromVec2(dir);
|
||||
|
||||
emitter->count = 1000;
|
||||
emitter->speed = 4;
|
||||
|
||||
emitter->seed = RandU64FromState(&frame->rand);
|
||||
emitter->speed_spread = 1;
|
||||
emitter->angle_spread = Tau;
|
||||
}
|
||||
|
||||
// Flatten emitters list
|
||||
V_Emitter *flattened_emitters = PushStructsNoZero(frame->arena, V_Emitter, emitters_count);
|
||||
{
|
||||
i64 emitter_idx = 0;
|
||||
for (V_EmitterNode *en = first_emitter_node; en; en = en->next)
|
||||
{
|
||||
flattened_emitters[emitter_idx] = en->emitter;
|
||||
++emitter_idx;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Push data to GPU
|
||||
|
||||
@ -2615,15 +2708,20 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
G_StructuredBufferRef dverts_ro = G_PushStructuredBufferRef(frame->gpu_arena, dverts_buff, V_DVert);
|
||||
G_IndexBufferDesc dvert_idxs_ib = G_IdxBuff32(dvert_idxs_buff);
|
||||
|
||||
// Emitters
|
||||
G_ResourceHandle gpu_emitters = G_PushBufferFromString(frame->gpu_arena, frame->cl, StringFromStructs(flattened_emitters, emitters_count));
|
||||
G_StructuredBufferRef gpu_emitters_ref = G_PushStructuredBufferRef(frame->gpu_arena, gpu_emitters, V_Emitter);
|
||||
|
||||
// Params
|
||||
V_DParams params = Zi;
|
||||
V_GpuParams params = Zi;
|
||||
{
|
||||
params.dt = frame->dt;
|
||||
params.target_size = frame->draw_dims;
|
||||
params.target_ro = draw_target_ro;
|
||||
params.target_rw = draw_target_rw;
|
||||
|
||||
params.world_to_draw_xf = frame->world_to_draw_xf;
|
||||
params.draw_to_world_xf = frame->draw_to_world_xf;
|
||||
params.xf = frame->xf;
|
||||
params.seed = RandU64FromState(&frame->rand);
|
||||
params.noise = G_BasicNoiseTexture();
|
||||
|
||||
params.selection_mode = frame->selection_mode;
|
||||
params.equipped_tile = frame->equipped_tile;
|
||||
@ -2642,12 +2740,21 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
|
||||
params.tiles = gpu_tiles_ref;
|
||||
params.shape_verts = dverts_ro;
|
||||
|
||||
params.emitters_count = emitters_count;
|
||||
params.emitters = gpu_emitters_ref;
|
||||
|
||||
params.max_particles = max_particles;
|
||||
params.particles = gpu_particles_ref;
|
||||
|
||||
params.decals = gpu_decals_ref;
|
||||
}
|
||||
G_ResourceHandle params_buff = G_PushBufferFromString(frame->gpu_arena, frame->cl, StringFromStruct(¶ms));
|
||||
G_StructuredBufferRef params_ro = G_PushStructuredBufferRef(frame->gpu_arena, params_buff, V_DParams);
|
||||
G_ResourceHandle gpu_params = G_PushBufferFromString(frame->gpu_arena, frame->cl, StringFromStruct(¶ms));
|
||||
G_StructuredBufferRef gpu_params_ref = G_PushStructuredBufferRef(frame->gpu_arena, gpu_params, V_GpuParams);
|
||||
|
||||
// Constants
|
||||
G_SetConstant(frame->cl, V_ShaderConst_Params, params_ro);
|
||||
G_SetConstant(frame->cl, V_ShaderConst_State, gpu_state_ref);
|
||||
G_SetConstant(frame->cl, V_ShaderConst_Params, gpu_params_ref);
|
||||
|
||||
// Sync
|
||||
G_DumbGlobalMemorySync(frame->cl);
|
||||
@ -2696,6 +2803,19 @@ void V_TickForever(WaveLaneCtx *lane)
|
||||
);
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Particle simulation pass
|
||||
|
||||
{
|
||||
// Emit particles
|
||||
G_Compute(frame->cl, V_EmitParticlesCS, VEC3I32((emitters_count + 63) / 64, 1, 1));
|
||||
|
||||
G_DumbMemorySync(frame->cl, gpu_particles);
|
||||
|
||||
// Simulate particles
|
||||
G_Compute(frame->cl, V_SimParticlesCS, VEC3I32((max_particles + 63) / 64, 1, 1));
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Finalize draw target
|
||||
|
||||
|
||||
@ -213,6 +213,7 @@ Struct(V_Frame)
|
||||
i64 time_ns;
|
||||
i64 dt_ns;
|
||||
f64 dt;
|
||||
RandState rand;
|
||||
|
||||
Button held_buttons[Button_COUNT];
|
||||
V_Palette palette;
|
||||
@ -245,17 +246,8 @@ Struct(V_Frame)
|
||||
Vec2 camera_pos;
|
||||
f32 camera_zoom;
|
||||
|
||||
// World <-> ui
|
||||
Xform world_to_ui_xf;
|
||||
Xform ui_to_world_xf;
|
||||
|
||||
// Draw <-> ui
|
||||
Xform draw_to_ui_xf;
|
||||
Xform ui_to_draw_xf;
|
||||
|
||||
// World <-> draw
|
||||
Xform world_to_draw_xf;
|
||||
Xform draw_to_world_xf;
|
||||
// Xforms;
|
||||
V_Xforms xf;
|
||||
|
||||
// Cursors
|
||||
Vec2 ui_cursor;
|
||||
|
||||
428
src/pp/pp_vis/pp_vis_gpu.g
Normal file
428
src/pp/pp_vis/pp_vis_gpu.g
Normal file
@ -0,0 +1,428 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Backdrop shader
|
||||
|
||||
ComputeShader2D(V_BackdropCS, 8, 8)
|
||||
{
|
||||
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
||||
RWTexture2D<Vec4> target = G_Dereference<Vec4>(params.target_rw);
|
||||
Texture2D<S_TileKind> tiles = G_Dereference<S_TileKind>(params.tiles);
|
||||
|
||||
const Vec4 background_color_a = LinearFromSrgb(Vec4(0.30, 0.30, 0.30, 1));
|
||||
const Vec4 background_color_b = LinearFromSrgb(Vec4(0.15, 0.15, 0.15, 1));
|
||||
const Vec4 grid_color = LinearFromSrgb(Vec4(0, 0, 0, 1));
|
||||
const Vec4 x_axis_color = LinearFromSrgb(Vec4(0.75, 0, 0, 1));
|
||||
const Vec4 y_axis_color = LinearFromSrgb(Vec4(0, 0.75, 0, 1));
|
||||
const Vec4 bounds_color = LinearFromSrgb(Vec4(0.75, 0.75, 0, 1));
|
||||
|
||||
Vec2 ui_pos = SV_DispatchThreadID + Vec2(0.5, 0.5);
|
||||
if (ui_pos.x < params.target_size.x && ui_pos.y < params.target_size.y)
|
||||
{
|
||||
Vec4 result = Vec4(0.025, 0.025, 0.025, 1);
|
||||
Vec2 world_pos = mul(params.xf.ui_to_world, Vec3(ui_pos, 1));
|
||||
Vec2I32 tile_pos = S_TilePosFromWorldPos(world_pos);
|
||||
|
||||
f32 half_thickness = 1;
|
||||
f32 half_bounds_size = S_WorldPitch * 0.5;
|
||||
Vec2 bounds_screen_p0 = mul(params.xf.world_to_draw, Vec3(-half_bounds_size, -half_bounds_size, 1));
|
||||
Vec2 bounds_screen_p1 = mul(params.xf.world_to_draw, Vec3(half_bounds_size, half_bounds_size, 1));
|
||||
bool is_in_bounds = ui_pos.x > (bounds_screen_p0.x - half_thickness) &&
|
||||
ui_pos.y > (bounds_screen_p0.y - half_thickness) &&
|
||||
ui_pos.x < (bounds_screen_p1.x + half_thickness) &&
|
||||
ui_pos.y < (bounds_screen_p1.y + half_thickness);
|
||||
if (is_in_bounds)
|
||||
{
|
||||
// Grid checker
|
||||
{
|
||||
i32 color_idx = 0;
|
||||
Vec4 colors[2] = {
|
||||
background_color_a,
|
||||
background_color_b
|
||||
};
|
||||
const f32 checker_size = 0.5;
|
||||
Vec2 world_pos_modded = fmod(abs(world_pos), Vec2(checker_size * 2, checker_size * 2));
|
||||
if (world_pos_modded.x < checker_size)
|
||||
{
|
||||
color_idx = !color_idx;
|
||||
}
|
||||
if (world_pos_modded.y < checker_size)
|
||||
{
|
||||
color_idx = !color_idx;
|
||||
}
|
||||
if (world_pos.x < 0)
|
||||
{
|
||||
color_idx = !color_idx;
|
||||
}
|
||||
if (world_pos.y < 0)
|
||||
{
|
||||
color_idx = !color_idx;
|
||||
}
|
||||
result = colors[color_idx];
|
||||
}
|
||||
// Grid outline
|
||||
{
|
||||
Vec2 grid_screen_p0 = mul(params.xf.world_to_draw, Vec3(floor(world_pos), 1));
|
||||
Vec2 grid_screen_p1 = mul(params.xf.world_to_draw, Vec3(ceil(world_pos), 1));
|
||||
f32 grid_dist = 100000;
|
||||
grid_dist = min(grid_dist, abs(ui_pos.x - grid_screen_p0.x));
|
||||
grid_dist = min(grid_dist, abs(ui_pos.x - grid_screen_p1.x));
|
||||
grid_dist = min(grid_dist, abs(ui_pos.y - grid_screen_p0.y));
|
||||
grid_dist = min(grid_dist, abs(ui_pos.y - grid_screen_p1.y));
|
||||
if (grid_dist <= half_thickness)
|
||||
{
|
||||
result = grid_color;
|
||||
}
|
||||
}
|
||||
// Tile
|
||||
{
|
||||
S_TileKind tile = tiles.Load(Vec3I32(tile_pos, 0));
|
||||
switch (tile)
|
||||
{
|
||||
default: break;
|
||||
|
||||
case S_TileKind_Floor:
|
||||
{
|
||||
result = Color_Blue;
|
||||
} break;
|
||||
|
||||
case S_TileKind_Wall:
|
||||
{
|
||||
result = Color_Red;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
// Axis
|
||||
{
|
||||
Vec2 zero_screen = mul(params.xf.world_to_draw, Vec3(0, 0, 1));
|
||||
f32 x_dist = abs(ui_pos.x - zero_screen.x);
|
||||
f32 y_dist = abs(ui_pos.y - zero_screen.y);
|
||||
if (y_dist <= half_thickness)
|
||||
{
|
||||
result = x_axis_color;
|
||||
}
|
||||
else if (x_dist <= half_thickness)
|
||||
{
|
||||
result = y_axis_color;
|
||||
}
|
||||
}
|
||||
// World bounds
|
||||
{
|
||||
f32 bounds_dist = 100000;
|
||||
bounds_dist = min(bounds_dist, abs(ui_pos.x - bounds_screen_p0.x));
|
||||
bounds_dist = min(bounds_dist, abs(ui_pos.x - bounds_screen_p1.x));
|
||||
bounds_dist = min(bounds_dist, abs(ui_pos.y - bounds_screen_p0.y));
|
||||
bounds_dist = min(bounds_dist, abs(ui_pos.y - bounds_screen_p1.y));
|
||||
if (bounds_dist <= half_thickness)
|
||||
{
|
||||
result = bounds_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target[SV_DispatchThreadID] = result;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Quad shader
|
||||
|
||||
//////////////////////////////
|
||||
//- Vertex shader
|
||||
|
||||
VertexShader(V_DQuadVS, V_DQuadPSInput)
|
||||
{
|
||||
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
||||
StructuredBuffer<V_DQuad> quads = G_Dereference<V_DQuad>(params.quads);
|
||||
RWTexture2D<Vec4> target = G_Dereference<Vec4>(params.target_rw);
|
||||
|
||||
V_DQuad quad = quads[SV_InstanceID];
|
||||
|
||||
Vec2 rect_uv = RectUvFromVertexId(SV_VertexID);
|
||||
// Vec2 tex_uv = lerp(quad.tex_uv0, quad.tex_uv1, rect_uv);
|
||||
// Vec2 target_pos = lerp(quad.p0, quad.p1, rect_uv);
|
||||
Vec2 target_pos = 0;
|
||||
|
||||
V_DQuadPSInput result;
|
||||
result.sv_position = Vec4(NdcFromPos(target_pos, countof(target)).xy, 0, 1);
|
||||
result.quad_idx = SV_InstanceID;
|
||||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Pixel shader
|
||||
|
||||
PixelShader(V_DQuadPS, V_DQuadPSOutput, V_DQuadPSInput input)
|
||||
{
|
||||
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
||||
StructuredBuffer<V_DQuad> quads = G_Dereference<V_DQuad>(params.quads);
|
||||
V_DQuad quad = quads[input.quad_idx];
|
||||
|
||||
Vec4 final_color = 0;
|
||||
|
||||
V_DQuadPSOutput output;
|
||||
output.sv_target0 = final_color;
|
||||
return output;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Particle simulation shaders
|
||||
|
||||
//////////////////////////////
|
||||
//- Particle emitter shader
|
||||
|
||||
// TODO: Initialize particles in per-particle-sim-thread rather than sequentially in emitter thread
|
||||
|
||||
ComputeShader(V_EmitParticlesCS, 64)
|
||||
{
|
||||
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
||||
RWStructuredBuffer<V_GpuState> state_buff = G_Dereference<V_GpuState>(V_ShaderConst_State);
|
||||
StructuredBuffer<V_Emitter> emitters = G_Dereference<V_Emitter>(params.emitters);
|
||||
RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(params.particles);
|
||||
|
||||
u32 emitter_idx = SV_DispatchThreadID;
|
||||
if (emitter_idx < params.emitters_count)
|
||||
{
|
||||
V_Emitter emitter = emitters[emitter_idx];
|
||||
|
||||
u32 first_particle_seq;
|
||||
InterlockedAdd(state_buff[0].particle_seq, emitter.count, first_particle_seq);
|
||||
u32 last_particle_seq = first_particle_seq + emitter.count;
|
||||
|
||||
u32 particles_created_count = 0;
|
||||
for (u32 particle_seq = first_particle_seq; particle_seq < last_particle_seq; ++particle_seq)
|
||||
{
|
||||
u32 particle_idx = particle_seq % params.max_particles;
|
||||
particles[particle_idx].kind = emitter.particle_kind;
|
||||
particles[particle_idx].emitter_init_num = emitter_idx + 1;
|
||||
particles[particle_idx].idx_in_emitter = particles_created_count;
|
||||
particles_created_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
||||
// RWStructuredBuffer<V_GpuState> state_buff = G_Dereference<V_GpuState>(V_ShaderConst_State);
|
||||
// StructuredBuffer<V_Emitter> emitters = G_Dereference<V_Emitter>(params.emitters);
|
||||
// RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(params.particles);
|
||||
// Texture3D<u32> noise3d = G_Dereference<u32>(params.noise);
|
||||
|
||||
// u32 emitter_idx = SV_DispatchThreadID;
|
||||
// if (emitter_idx < params.emitters_count)
|
||||
// {
|
||||
// V_Emitter emitter = emitters[emitter_idx];
|
||||
|
||||
// u32 first_particle_seq;
|
||||
// InterlockedAdd(state_buff[0].particle_seq, emitter.count, first_particle_seq);
|
||||
// u32 last_particle_seq = first_particle_seq + emitter.count;
|
||||
|
||||
// Vec2U32 speed_noise_coord;
|
||||
// Vec2U32 angle_noise_coord;
|
||||
// {
|
||||
// speed_noise_coord.x = ((emitter.speed_seed >> 0) & 0xFFFF) % countof(noise3d).x;
|
||||
// speed_noise_coord.y = ((emitter.speed_seed >> 16) & 0xFFFF) % countof(noise3d).y;
|
||||
// angle_noise_coord.x = ((emitter.angle_seed >> 0) & 0xFFFF) % countof(noise3d).x;
|
||||
// angle_noise_coord.y = ((emitter.angle_seed >> 16) & 0xFFFF) % countof(noise3d).y;
|
||||
// }
|
||||
|
||||
// u64 base_seed =
|
||||
// for (u32 particle_seq = first_particle_seq; particle_seq < last_particle_seq; ++particle_seq)
|
||||
// {
|
||||
// V_Particle particle;
|
||||
// {
|
||||
// u32 speed_noise = noise3d.Load(Vec4U32(speed_noise_coord.xy, particle_seq % countof(noise3d).z, 0));
|
||||
// u32 angle_noise = noise3d.Load(Vec4U32(angle_noise_coord.xy, particle_seq % countof(noise3d).z, 0));
|
||||
// f32 speed_rand = speed_noise / (f32)0xFFFF;
|
||||
// f32 angle_rand = angle_noise / (f32)0xFFFF;
|
||||
// f32 speed = emitter.speed + ((speed_rand * 2 - 1) * emitter.speed_spread);
|
||||
// f32 angle = emitter.angle + ((angle_rand * 2 - 1) * emitter.angle_spread);
|
||||
|
||||
// particle.kind = emitter.particle_kind;
|
||||
// particle.pos = emitter.pos;
|
||||
// particle.velocity = Vec2(cos(angle), sin(angle)) * speed;
|
||||
// }
|
||||
// particles[particle_seq % params.max_particles] = particle;
|
||||
// }
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Particle sim shader
|
||||
|
||||
ComputeShader(V_SimParticlesCS, 64)
|
||||
{
|
||||
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
||||
RWStructuredBuffer<V_Particle> particles = G_Dereference<V_Particle>(params.particles);
|
||||
RWTexture2D<V_ParticleKind> decals = G_Dereference<V_ParticleKind>(params.decals);
|
||||
|
||||
u32 particle_idx = SV_DispatchThreadID;
|
||||
if (particle_idx < params.max_particles)
|
||||
{
|
||||
V_Particle particle = particles[particle_idx];
|
||||
if (particle.kind != V_ParticleKind_None)
|
||||
{
|
||||
// Initialize
|
||||
if (particle.emitter_init_num != 0)
|
||||
{
|
||||
V_Emitter emitter = G_Dereference<V_Emitter>(params.emitters)[particle.emitter_init_num - 1];
|
||||
|
||||
// TODO: Faster mix
|
||||
u64 seed = MixU64(emitter.seed + particle.idx_in_emitter);
|
||||
|
||||
// TODO: Blue noise
|
||||
u32 speed_noise = ((u32)seed >> 0) & 0xFFFF;
|
||||
u32 angle_noise = ((u32)seed >> 16) & 0xFFFF;
|
||||
f32 speed_rand = speed_noise / (f32)0xFFFF;
|
||||
f32 angle_rand = angle_noise / (f32)0xFFFF;
|
||||
|
||||
f32 speed = emitter.speed + ((speed_rand * 2 - 1) * emitter.speed_spread);
|
||||
f32 angle = emitter.angle + ((angle_rand * 2 - 1) * emitter.angle_spread);
|
||||
|
||||
particle.pos = emitter.pos;
|
||||
particle.velocity = Vec2(cos(angle), sin(angle)) * speed;
|
||||
|
||||
particle.emitter_init_num = 0;
|
||||
}
|
||||
|
||||
// Simulate
|
||||
{
|
||||
f32 dt = params.dt;
|
||||
particle.pos += particle.velocity * dt;
|
||||
|
||||
Vec2 decal_pos = mul(params.xf.world_to_decal, Vec3(particle.pos, 1));
|
||||
if (decal_pos.x >= 0 && decal_pos.y >= 0 && decal_pos.x < countof(decals).x && decal_pos.y < countof(decals).y)
|
||||
{
|
||||
// FIXME: Atomic write
|
||||
decals[floor(decal_pos)] = particle.kind;
|
||||
}
|
||||
}
|
||||
particles[particle_idx] = particle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Shape shader
|
||||
|
||||
//////////////////////////////
|
||||
//- Vertex shader
|
||||
|
||||
VertexShader(V_DVertVS, V_DVertPSInput)
|
||||
{
|
||||
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
||||
StructuredBuffer<V_DVert> verts = G_Dereference<V_DVert>(params.shape_verts);
|
||||
RWTexture2D<Vec4> target = G_Dereference<Vec4>(params.target_rw);
|
||||
|
||||
V_DVert vert = verts[SV_VertexID];
|
||||
|
||||
Vec2 target_pos = vert.pos;
|
||||
|
||||
V_DVertPSInput result;
|
||||
result.sv_position = Vec4(NdcFromPos(target_pos, countof(target)).xy, 0, 1);
|
||||
result.color_lin = vert.color_lin;
|
||||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Pixel shader
|
||||
|
||||
PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input)
|
||||
{
|
||||
V_DVertPSOutput output;
|
||||
output.sv_target0 = input.color_lin;
|
||||
return output;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Overlay shader
|
||||
|
||||
//////////////////////////////
|
||||
//- Vertex shader
|
||||
|
||||
VertexShader(V_OverlayVS, V_OverlayPSInput)
|
||||
{
|
||||
Vec2 uv = RectUvFromVertexId(SV_VertexID);
|
||||
V_OverlayPSInput result;
|
||||
result.sv_position = Vec4(NdcFromUv(uv).xy, 0, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Pixel shader
|
||||
|
||||
PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input)
|
||||
{
|
||||
V_GpuParams params = G_Dereference<V_GpuParams>(V_ShaderConst_Params)[0];
|
||||
Vec2 ui_pos = input.sv_position.xy;
|
||||
Vec4 result = 0;
|
||||
|
||||
Vec2 world_pos = mul(params.xf.ui_to_world, Vec3(ui_pos, 1));
|
||||
Vec2I32 tile_pos = S_TilePosFromWorldPos(world_pos);
|
||||
S_TileKind equipped_tile = params.equipped_tile;
|
||||
|
||||
f32 half_thickness = 1;
|
||||
Vec4 border_color = LinearFromSrgb(Vec4(1, 1, 1, 1));
|
||||
// Vec4 inner_color = LinearFromSrgb(Vec4(0.4, 0.4, 0.4, 0.25));
|
||||
|
||||
Vec4 inner_color = LinearFromSrgb(Vec4(0.4, 0.8, 0.4, 0.6));
|
||||
|
||||
Rng2 screen_selection = params.draw_selection;
|
||||
Rng2 world_selection = params.world_selection;
|
||||
|
||||
Rng2I32 tile_selection;
|
||||
tile_selection.p0 = S_TilePosFromWorldPos(world_selection.p0);
|
||||
tile_selection.p1 = S_TilePosFromWorldPos(world_selection.p1);
|
||||
|
||||
if (params.has_mouse_focus)
|
||||
{
|
||||
if (params.selection_mode == V_SelectionMode_Tile)
|
||||
{
|
||||
f32 dist = 100000000;
|
||||
dist = min(dist, ui_pos.x - screen_selection.p0.x);
|
||||
dist = min(dist, ui_pos.y - screen_selection.p0.y);
|
||||
dist = min(dist, screen_selection.p1.x - ui_pos.x);
|
||||
dist = min(dist, screen_selection.p1.y - ui_pos.y);
|
||||
dist = -dist;
|
||||
|
||||
// if (dist >= -half_thickness && dist <= half_thickness)
|
||||
// {
|
||||
// result = border_color;
|
||||
// }
|
||||
// else
|
||||
{
|
||||
if (
|
||||
world_pos.x > -(S_WorldPitch / 2) &&
|
||||
world_pos.y > -(S_WorldPitch / 2) &&
|
||||
world_pos.x < (S_WorldPitch / 2) &&
|
||||
world_pos.y < (S_WorldPitch / 2) &&
|
||||
tile_pos.x >= tile_selection.p0.x &&
|
||||
tile_pos.x <= tile_selection.p1.x &&
|
||||
tile_pos.y >= tile_selection.p0.y &&
|
||||
tile_pos.y <= tile_selection.p1.y
|
||||
)
|
||||
{
|
||||
result = inner_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove this
|
||||
// Decals test
|
||||
{
|
||||
RWTexture2D<V_ParticleKind> decals = G_Dereference<V_ParticleKind>(params.decals);
|
||||
Vec2 decal_pos = mul(params.xf.world_to_decal, Vec3(world_pos, 1));
|
||||
// Vec2 decal_uv = decal_pos / countof(decals);
|
||||
V_ParticleKind decal = decals.Load(floor(decal_pos));
|
||||
|
||||
if (decal == V_ParticleKind_Test)
|
||||
{
|
||||
result = Color_Yellow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
V_OverlayPSOutput output;
|
||||
output.sv_target0 = result;
|
||||
return output;
|
||||
}
|
||||
@ -17,7 +17,6 @@ Struct(V_DQuadPSOutput)
|
||||
|
||||
Struct(V_DVertPSInput)
|
||||
{
|
||||
|
||||
Semantic(Vec4, sv_position);
|
||||
Semantic(Vec4, color_lin);
|
||||
};
|
||||
@ -44,17 +43,21 @@ Struct(V_OverlayPSOutput)
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Shaders
|
||||
|
||||
//- Backdrop
|
||||
//- Backdrop shader
|
||||
ComputeShader2D(V_BackdropCS, 8, 8);
|
||||
|
||||
//- Quad
|
||||
//- Quad shader
|
||||
VertexShader(V_DQuadVS, V_DQuadPSInput);
|
||||
PixelShader(V_DQuadPS, V_DQuadPSOutput, V_DQuadPSInput input);
|
||||
|
||||
//- Shape
|
||||
//- Particle simulation shaders
|
||||
ComputeShader(V_EmitParticlesCS, 64);
|
||||
ComputeShader(V_SimParticlesCS, 64);
|
||||
|
||||
//- Shape shader
|
||||
VertexShader(V_DVertVS, V_DVertPSInput);
|
||||
PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input);
|
||||
|
||||
//- Overlay
|
||||
//- Overlay shader
|
||||
VertexShader(V_OverlayVS, V_OverlayPSInput);
|
||||
PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input);
|
||||
@ -1,67 +0,0 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Constant types
|
||||
|
||||
G_DeclConstant(G_StructuredBufferRef, V_ShaderConst_Params, 0);
|
||||
|
||||
Enum(V_SelectionMode)
|
||||
{
|
||||
V_SelectionMode_None,
|
||||
V_SelectionMode_Tile,
|
||||
};
|
||||
|
||||
Struct(V_DParams)
|
||||
{
|
||||
Vec2 target_size;
|
||||
G_Texture2DRef target_ro;
|
||||
G_RWTexture2DRef target_rw;
|
||||
|
||||
Xform world_to_draw_xf;
|
||||
Xform draw_to_world_xf;
|
||||
|
||||
V_SelectionMode selection_mode;
|
||||
S_TileKind equipped_tile;
|
||||
|
||||
b32 has_mouse_focus;
|
||||
b32 has_keyboard_focus;
|
||||
Vec2 ui_cursor;
|
||||
Vec2 draw_cursor;
|
||||
Vec2 world_cursor;
|
||||
Rng2 ui_selection;
|
||||
Rng2 draw_selection;
|
||||
Rng2 world_selection;
|
||||
|
||||
Vec2 camera_pos;
|
||||
f32 camera_zoom;
|
||||
|
||||
G_Texture2DRef tiles;
|
||||
G_StructuredBufferRef quads;
|
||||
G_StructuredBufferRef shape_verts;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Backdrop shader types
|
||||
|
||||
#define V_BackdropCSThreadSizeFromTexSize(tex_size) VEC3I32((tex_size.x + 7) / 8, (tex_size.y + 7) / 8, 1)
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Quad shader types
|
||||
|
||||
Enum(V_DQuadFlag)
|
||||
{
|
||||
V_DQuadFlag_None = 0,
|
||||
V_DQuadFlag_DrawGrid = (1 << 0),
|
||||
};
|
||||
|
||||
Struct(V_DQuad)
|
||||
{
|
||||
V_DQuadFlag flags;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Shape shader types
|
||||
|
||||
Struct(V_DVert)
|
||||
{
|
||||
Vec2 pos;
|
||||
Vec4 color_lin;
|
||||
};
|
||||
@ -1,276 +0,0 @@
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Backdrop shader
|
||||
|
||||
ComputeShader2D(V_BackdropCS, 8, 8)
|
||||
{
|
||||
V_DParams params = G_Dereference<V_DParams>(V_ShaderConst_Params)[0];
|
||||
RWTexture2D<Vec4> target = G_Dereference<Vec4>(params.target_rw);
|
||||
Texture2D<uint> tiles = G_Dereference<uint>(params.tiles);
|
||||
|
||||
const Vec4 background_color_a = LinearFromSrgb(Vec4(0.30, 0.30, 0.30, 1));
|
||||
const Vec4 background_color_b = LinearFromSrgb(Vec4(0.15, 0.15, 0.15, 1));
|
||||
const Vec4 grid_color = LinearFromSrgb(Vec4(0, 0, 0, 1));
|
||||
const Vec4 x_axis_color = LinearFromSrgb(Vec4(0.75, 0, 0, 1));
|
||||
const Vec4 y_axis_color = LinearFromSrgb(Vec4(0, 0.75, 0, 1));
|
||||
const Vec4 bounds_color = LinearFromSrgb(Vec4(0.75, 0.75, 0, 1));
|
||||
|
||||
Vec2 screen_pos = SV_DispatchThreadID + Vec2(0.5, 0.5);
|
||||
if (screen_pos.x < params.target_size.x && screen_pos.y < params.target_size.y)
|
||||
{
|
||||
Vec4 result = Vec4(0.025, 0.025, 0.025, 1);
|
||||
Vec2 world_pos = mul(params.draw_to_world_xf, Vec3(screen_pos, 1));
|
||||
Vec2I32 tile_pos = S_TilePosFromWorldPos(world_pos);
|
||||
|
||||
f32 half_thickness = 1;
|
||||
f32 half_bounds_size = S_WorldPitch * 0.5;
|
||||
Vec2 bounds_screen_p0 = mul(params.world_to_draw_xf, Vec3(-half_bounds_size, -half_bounds_size, 1));
|
||||
Vec2 bounds_screen_p1 = mul(params.world_to_draw_xf, Vec3(half_bounds_size, half_bounds_size, 1));
|
||||
bool is_in_bounds = screen_pos.x > (bounds_screen_p0.x - half_thickness) &&
|
||||
screen_pos.y > (bounds_screen_p0.y - half_thickness) &&
|
||||
screen_pos.x < (bounds_screen_p1.x + half_thickness) &&
|
||||
screen_pos.y < (bounds_screen_p1.y + half_thickness);
|
||||
if (is_in_bounds)
|
||||
{
|
||||
// Grid checker
|
||||
{
|
||||
i32 color_idx = 0;
|
||||
Vec4 colors[2] = {
|
||||
background_color_a,
|
||||
background_color_b
|
||||
};
|
||||
const f32 checker_size = 0.5;
|
||||
Vec2 world_pos_modded = fmod(abs(world_pos), Vec2(checker_size * 2, checker_size * 2));
|
||||
if (world_pos_modded.x < checker_size)
|
||||
{
|
||||
color_idx = !color_idx;
|
||||
}
|
||||
if (world_pos_modded.y < checker_size)
|
||||
{
|
||||
color_idx = !color_idx;
|
||||
}
|
||||
if (world_pos.x < 0)
|
||||
{
|
||||
color_idx = !color_idx;
|
||||
}
|
||||
if (world_pos.y < 0)
|
||||
{
|
||||
color_idx = !color_idx;
|
||||
}
|
||||
result = colors[color_idx];
|
||||
}
|
||||
// Grid outline
|
||||
{
|
||||
Vec2 grid_screen_p0 = mul(params.world_to_draw_xf, Vec3(floor(world_pos), 1));
|
||||
Vec2 grid_screen_p1 = mul(params.world_to_draw_xf, Vec3(ceil(world_pos), 1));
|
||||
f32 grid_dist = 100000;
|
||||
grid_dist = min(grid_dist, abs(screen_pos.x - grid_screen_p0.x));
|
||||
grid_dist = min(grid_dist, abs(screen_pos.x - grid_screen_p1.x));
|
||||
grid_dist = min(grid_dist, abs(screen_pos.y - grid_screen_p0.y));
|
||||
grid_dist = min(grid_dist, abs(screen_pos.y - grid_screen_p1.y));
|
||||
if (grid_dist <= half_thickness)
|
||||
{
|
||||
result = grid_color;
|
||||
}
|
||||
}
|
||||
// Tile
|
||||
{
|
||||
S_TileKind tile = (S_TileKind)tiles.Load(Vec3I32(tile_pos, 0));
|
||||
switch (tile)
|
||||
{
|
||||
default: break;
|
||||
|
||||
case S_TileKind_Floor:
|
||||
{
|
||||
result = Color_Blue;
|
||||
} break;
|
||||
|
||||
case S_TileKind_Wall:
|
||||
{
|
||||
result = Color_Red;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
// Axis
|
||||
{
|
||||
Vec2 zero_screen = mul(params.world_to_draw_xf, Vec3(0, 0, 1));
|
||||
f32 x_dist = abs(screen_pos.x - zero_screen.x);
|
||||
f32 y_dist = abs(screen_pos.y - zero_screen.y);
|
||||
if (y_dist <= half_thickness)
|
||||
{
|
||||
result = x_axis_color;
|
||||
}
|
||||
else if (x_dist <= half_thickness)
|
||||
{
|
||||
result = y_axis_color;
|
||||
}
|
||||
}
|
||||
// World bounds
|
||||
{
|
||||
f32 bounds_dist = 100000;
|
||||
bounds_dist = min(bounds_dist, abs(screen_pos.x - bounds_screen_p0.x));
|
||||
bounds_dist = min(bounds_dist, abs(screen_pos.x - bounds_screen_p1.x));
|
||||
bounds_dist = min(bounds_dist, abs(screen_pos.y - bounds_screen_p0.y));
|
||||
bounds_dist = min(bounds_dist, abs(screen_pos.y - bounds_screen_p1.y));
|
||||
if (bounds_dist <= half_thickness)
|
||||
{
|
||||
result = bounds_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target[SV_DispatchThreadID] = result;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Quad shader
|
||||
|
||||
//////////////////////////////
|
||||
//- Vertex shader
|
||||
|
||||
VertexShader(V_DQuadVS, V_DQuadPSInput)
|
||||
{
|
||||
V_DParams params = G_Dereference<V_DParams>(V_ShaderConst_Params)[0];
|
||||
StructuredBuffer<V_DQuad> quads = G_Dereference<V_DQuad>(params.quads);
|
||||
RWTexture2D<Vec4> target = G_Dereference<Vec4>(params.target_rw);
|
||||
|
||||
V_DQuad quad = quads[SV_InstanceID];
|
||||
|
||||
Vec2 rect_uv = RectUvFromVertexId(SV_VertexID);
|
||||
// Vec2 tex_uv = lerp(quad.tex_uv0, quad.tex_uv1, rect_uv);
|
||||
// Vec2 target_pos = lerp(quad.p0, quad.p1, rect_uv);
|
||||
Vec2 target_pos = 0;
|
||||
|
||||
V_DQuadPSInput result;
|
||||
result.sv_position = Vec4(NdcFromPos(target_pos, countof(target)).xy, 0, 1);
|
||||
result.quad_idx = SV_InstanceID;
|
||||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Pixel shader
|
||||
|
||||
PixelShader(V_DQuadPS, V_DQuadPSOutput, V_DQuadPSInput input)
|
||||
{
|
||||
V_DParams params = G_Dereference<V_DParams>(V_ShaderConst_Params)[0];
|
||||
StructuredBuffer<V_DQuad> quads = G_Dereference<V_DQuad>(params.quads);
|
||||
V_DQuad quad = quads[input.quad_idx];
|
||||
|
||||
Vec4 final_color = 0;
|
||||
|
||||
V_DQuadPSOutput output;
|
||||
output.sv_target0 = final_color;
|
||||
return output;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Shape shader
|
||||
|
||||
//////////////////////////////
|
||||
//- Vertex shader
|
||||
|
||||
VertexShader(V_DVertVS, V_DVertPSInput)
|
||||
{
|
||||
V_DParams params = G_Dereference<V_DParams>(V_ShaderConst_Params)[0];
|
||||
StructuredBuffer<V_DVert> verts = G_Dereference<V_DVert>(params.shape_verts);
|
||||
RWTexture2D<Vec4> target = G_Dereference<Vec4>(params.target_rw);
|
||||
|
||||
V_DVert vert = verts[SV_VertexID];
|
||||
|
||||
Vec2 target_pos = vert.pos;
|
||||
|
||||
V_DVertPSInput result;
|
||||
result.sv_position = Vec4(NdcFromPos(target_pos, countof(target)).xy, 0, 1);
|
||||
result.color_lin = vert.color_lin;
|
||||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Pixel shader
|
||||
|
||||
PixelShader(V_DVertPS, V_DVertPSOutput, V_DVertPSInput input)
|
||||
{
|
||||
V_DVertPSOutput output;
|
||||
output.sv_target0 = input.color_lin;
|
||||
return output;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Overlay shader
|
||||
|
||||
//////////////////////////////
|
||||
//- Vertex shader
|
||||
|
||||
VertexShader(V_OverlayVS, V_OverlayPSInput)
|
||||
{
|
||||
Vec2 uv = RectUvFromVertexId(SV_VertexID);
|
||||
V_OverlayPSInput result;
|
||||
result.sv_position = Vec4(NdcFromUv(uv).xy, 0, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- Pixel shader
|
||||
|
||||
PixelShader(V_OverlayPS, V_OverlayPSOutput, V_OverlayPSInput input)
|
||||
{
|
||||
V_DParams params = G_Dereference<V_DParams>(V_ShaderConst_Params)[0];
|
||||
Texture2D<uint> tiles = G_Dereference<uint>(params.tiles);
|
||||
Vec2 screen_pos = input.sv_position.xy;
|
||||
Vec4 result = 0;
|
||||
|
||||
Vec2 world_pos = mul(params.draw_to_world_xf, Vec3(screen_pos, 1));
|
||||
Vec2I32 tile_pos = S_TilePosFromWorldPos(world_pos);
|
||||
S_TileKind equipped_tile = params.equipped_tile;
|
||||
|
||||
f32 half_thickness = 1;
|
||||
Vec4 border_color = LinearFromSrgb(Vec4(1, 1, 1, 1));
|
||||
// Vec4 inner_color = LinearFromSrgb(Vec4(0.4, 0.4, 0.4, 0.25));
|
||||
|
||||
Vec4 inner_color = LinearFromSrgb(Vec4(0.4, 0.8, 0.4, 0.6));
|
||||
|
||||
Rng2 screen_selection = params.draw_selection;
|
||||
Rng2 world_selection = params.world_selection;
|
||||
|
||||
Rng2I32 tile_selection;
|
||||
tile_selection.p0 = S_TilePosFromWorldPos(world_selection.p0);
|
||||
tile_selection.p1 = S_TilePosFromWorldPos(world_selection.p1);
|
||||
|
||||
if (params.has_mouse_focus)
|
||||
{
|
||||
if (params.selection_mode == V_SelectionMode_Tile)
|
||||
{
|
||||
f32 dist = 100000000;
|
||||
dist = min(dist, screen_pos.x - screen_selection.p0.x);
|
||||
dist = min(dist, screen_pos.y - screen_selection.p0.y);
|
||||
dist = min(dist, screen_selection.p1.x - screen_pos.x);
|
||||
dist = min(dist, screen_selection.p1.y - screen_pos.y);
|
||||
dist = -dist;
|
||||
|
||||
// if (dist >= -half_thickness && dist <= half_thickness)
|
||||
// {
|
||||
// result = border_color;
|
||||
// }
|
||||
// else
|
||||
{
|
||||
if (
|
||||
world_pos.x > -(S_WorldPitch / 2) &&
|
||||
world_pos.y > -(S_WorldPitch / 2) &&
|
||||
world_pos.x < (S_WorldPitch / 2) &&
|
||||
world_pos.y < (S_WorldPitch / 2) &&
|
||||
tile_pos.x >= tile_selection.p0.x &&
|
||||
tile_pos.x <= tile_selection.p1.x &&
|
||||
tile_pos.y >= tile_selection.p0.y &&
|
||||
tile_pos.y <= tile_selection.p1.y
|
||||
)
|
||||
{
|
||||
result = inner_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
V_OverlayPSOutput output;
|
||||
output.sv_target0 = result;
|
||||
return output;
|
||||
}
|
||||
146
src/pp/pp_vis/pp_vis_shared.cgh
Normal file
146
src/pp/pp_vis/pp_vis_shared.cgh
Normal file
@ -0,0 +1,146 @@
|
||||
#define V_PixelsPerMeter 48.0
|
||||
#define V_PixelsPerSqMeter (V_PixelsPerMeter * V_PixelsPerMeter)
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Constant types
|
||||
|
||||
G_DeclConstant(G_RWStructuredBufferRef, V_ShaderConst_State, 0);
|
||||
G_DeclConstant(G_StructuredBufferRef, V_ShaderConst_Params, 1);
|
||||
|
||||
Enum(V_SelectionMode)
|
||||
{
|
||||
V_SelectionMode_None,
|
||||
V_SelectionMode_Tile,
|
||||
};
|
||||
|
||||
Struct(V_Xforms)
|
||||
{
|
||||
// World <-> ui
|
||||
Xform world_to_ui;
|
||||
Xform ui_to_world;
|
||||
|
||||
// Draw <-> ui
|
||||
Xform draw_to_ui;
|
||||
Xform ui_to_draw;
|
||||
|
||||
// World <-> draw
|
||||
Xform world_to_draw;
|
||||
Xform draw_to_world;
|
||||
|
||||
// World <-> decal
|
||||
Xform world_to_decal;
|
||||
Xform decal_to_world;
|
||||
};
|
||||
|
||||
Struct(V_GpuState)
|
||||
{
|
||||
u32 particle_seq; // Atomic monotonically increasing allocation counter sequence for particle ring buffer
|
||||
};
|
||||
|
||||
Struct(V_GpuParams)
|
||||
{
|
||||
f32 dt;
|
||||
Vec2 target_size;
|
||||
G_Texture2DRef target_ro;
|
||||
G_RWTexture2DRef target_rw;
|
||||
V_Xforms xf;
|
||||
|
||||
u64 seed;
|
||||
G_Texture3DRef noise;
|
||||
|
||||
V_SelectionMode selection_mode;
|
||||
S_TileKind equipped_tile;
|
||||
|
||||
b32 has_mouse_focus;
|
||||
b32 has_keyboard_focus;
|
||||
Vec2 ui_cursor;
|
||||
Vec2 draw_cursor;
|
||||
Vec2 world_cursor;
|
||||
Rng2 ui_selection;
|
||||
Rng2 draw_selection;
|
||||
Rng2 world_selection;
|
||||
|
||||
Vec2 camera_pos;
|
||||
f32 camera_zoom;
|
||||
|
||||
G_Texture2DRef tiles;
|
||||
G_StructuredBufferRef quads;
|
||||
G_StructuredBufferRef shape_verts;
|
||||
|
||||
u32 emitters_count;
|
||||
G_StructuredBufferRef emitters;
|
||||
|
||||
u32 max_particles;
|
||||
G_RWStructuredBufferRef particles;
|
||||
G_RWTexture2DRef decals;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Particle types
|
||||
|
||||
Enum(V_ParticleKind)
|
||||
{
|
||||
V_ParticleKind_None,
|
||||
V_ParticleKind_Test
|
||||
};
|
||||
|
||||
Struct(V_Emitter)
|
||||
{
|
||||
V_ParticleKind particle_kind;
|
||||
|
||||
Vec2 pos;
|
||||
f32 angle;
|
||||
u32 count;
|
||||
f32 speed;
|
||||
|
||||
u64 seed;
|
||||
f32 angle_spread;
|
||||
f32 speed_spread;
|
||||
};
|
||||
|
||||
// TODO: Pack this efficiently
|
||||
Struct(V_Particle)
|
||||
{
|
||||
V_ParticleKind kind;
|
||||
u32 emitter_init_num; // if != 0, then initialize using emitter at index (emitter_init_num - 1)
|
||||
u32 idx_in_emitter;
|
||||
|
||||
Vec2 pos;
|
||||
Vec2 velocity;
|
||||
};
|
||||
|
||||
#if IsLanguageC
|
||||
Struct(V_EmitterNode)
|
||||
{
|
||||
V_EmitterNode *next;
|
||||
V_Emitter emitter;
|
||||
};
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Backdrop shader types
|
||||
|
||||
#define V_BackdropCSThreadSizeFromTexSize(tex_size) VEC3I32((tex_size.x + 7) / 8, (tex_size.y + 7) / 8, 1)
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Quad types
|
||||
|
||||
Enum(V_DQuadFlag)
|
||||
{
|
||||
V_DQuadFlag_None = 0,
|
||||
V_DQuadFlag_DrawGrid = (1 << 0),
|
||||
};
|
||||
|
||||
Struct(V_DQuad)
|
||||
{
|
||||
V_DQuadFlag flags;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
//~ Vert types
|
||||
|
||||
Struct(V_DVert)
|
||||
{
|
||||
Vec2 pos;
|
||||
Vec4 color_lin;
|
||||
};
|
||||
@ -20,13 +20,14 @@
|
||||
//////////////////////////////
|
||||
//- Api
|
||||
|
||||
@IncludeC ui_shared.cgh
|
||||
@IncludeG ui_shared.cgh
|
||||
|
||||
@IncludeC ui_icon.h
|
||||
@IncludeC ui_core.h
|
||||
@IncludeC ui_extras.h
|
||||
@IncludeC ui_shaders.cgh
|
||||
|
||||
@IncludeG ui_shaders.cgh
|
||||
@IncludeG ui_shaders.gh
|
||||
@IncludeG ui_gpu.gh
|
||||
|
||||
@Bootstrap UI_Bootstrap
|
||||
|
||||
@ -35,4 +36,5 @@
|
||||
|
||||
@IncludeC ui_core.c
|
||||
@IncludeC ui_extras.c
|
||||
@IncludeG ui_shaders.g
|
||||
|
||||
@IncludeG ui_gpu.g
|
||||
|
||||
Loading…
Reference in New Issue
Block a user