work on particles
This commit is contained in:
parent
8ab0a1fd6b
commit
20b6b8ae29
@ -45,14 +45,14 @@ g_asset_manager : Asset_Manager;
|
||||
|
||||
#load "rdm_loader.jai";
|
||||
|
||||
MAX_FILE_SIZE :: 200_000_000;
|
||||
buf : [MAX_FILE_SIZE]u8;
|
||||
world_buf : [MAX_FILE_SIZE]u8;
|
||||
|
||||
MAX_FILE_SIZE :: 200_000_000;
|
||||
RDM_ATLAS_MAX_BYTES :: 4096 * 4096 * 4 * 4 + size_of(RDM_File_Header);
|
||||
RDM_LOOKUP_MAX_BYTES :: 512 * 512 * 4 * 4 + size_of(RDM_File_Header);
|
||||
rdm_atlas_buf : [RDM_ATLAS_MAX_BYTES]u8;
|
||||
rdm_lookup_buf : [RDM_LOOKUP_MAX_BYTES]u8;
|
||||
|
||||
buf : []u8;
|
||||
world_buf : []u8;
|
||||
rdm_atlas_buf : []u8;
|
||||
rdm_lookup_buf : []u8;
|
||||
|
||||
buffer_for_fetch :: (type: Fetch_Type) -> (*u8, u64) {
|
||||
if type == .PACK return buf.data, xx buf.count;
|
||||
@ -298,6 +298,13 @@ free_resources_from_pack :: (pack: *Loaded_Pack) {
|
||||
table_reset(*pack.animations);
|
||||
}
|
||||
|
||||
asset_manager_init :: () {
|
||||
buf = NewArray(MAX_FILE_SIZE, u8, false);
|
||||
world_buf = NewArray(MAX_FILE_SIZE, u8, false);
|
||||
rdm_atlas_buf = NewArray(RDM_ATLAS_MAX_BYTES, u8, false);
|
||||
rdm_lookup_buf = NewArray(RDM_LOOKUP_MAX_BYTES, u8, false);
|
||||
}
|
||||
|
||||
mandatory_loads_done :: () -> bool {
|
||||
if g_asset_manager.is_fetching && g_asset_manager.current_fetch.should_block_engine then return false;
|
||||
for g_asset_manager.fetch_queue {
|
||||
|
||||
@ -30,6 +30,7 @@ stbi :: #import "stb_image";
|
||||
#load "load.jai";
|
||||
#load "ray.jai";
|
||||
#load "profiling.jai";
|
||||
// #load "particles/particles.jai";
|
||||
#load "world.jai";
|
||||
#load "utils.jai";
|
||||
#load "audio/audio.jai";
|
||||
@ -92,6 +93,7 @@ init :: () {
|
||||
sfetch_setup(*(sfetch_desc_t.{
|
||||
logger = .{ func = slog_func },
|
||||
}));
|
||||
asset_manager_init();
|
||||
saudio_setup(*(saudio_desc.{
|
||||
logger = .{ func = slog_func },
|
||||
stream_cb = sokol_audio_callback,
|
||||
|
||||
@ -1,18 +1,8 @@
|
||||
#scope_export
|
||||
|
||||
g_pending_emitter_world_name : string;
|
||||
|
||||
particles_process_deferred_loads :: () {
|
||||
if g_pending_emitter_world_name.count == 0 then return;
|
||||
name := g_pending_emitter_world_name;
|
||||
g_pending_emitter_world_name = "";
|
||||
load_emitters_for_world(name);
|
||||
free(name.data);
|
||||
}
|
||||
|
||||
Particle_Emitter_Definition :: struct {
|
||||
Particle_Emitter_Config :: struct {
|
||||
name : string;
|
||||
sprite_sheet : string;
|
||||
animation_name : string;
|
||||
emission_rate : float = 10.0;
|
||||
lifetime_min : float = 0.5;
|
||||
lifetime_max : float = 2.0;
|
||||
@ -22,14 +12,13 @@ Particle_Emitter_Definition :: struct {
|
||||
size_end : float = 0.0;
|
||||
color_start : Vector4 = .{1, 1, 1, 1};
|
||||
color_end : Vector4 = .{1, 1, 1, 0};
|
||||
frame_count : int = 1;
|
||||
gravity : float = 2.0;
|
||||
|
||||
sprite_image : sg_image; // resolved at runtime from sprite_sheet name
|
||||
animation : *Animation; // resolved at runtime from animation_name
|
||||
}
|
||||
|
||||
Particle_Emitter_Instance :: struct {
|
||||
definition : *Particle_Emitter_Definition;
|
||||
definition : *Particle_Emitter_Config;
|
||||
definition_name : string;
|
||||
position : Vector3;
|
||||
active : bool = true;
|
||||
@ -43,16 +32,16 @@ Particle :: struct {
|
||||
lifetime : float;
|
||||
gravity : float;
|
||||
alive : bool;
|
||||
definition : *Particle_Emitter_Definition;
|
||||
definition : *Particle_Emitter_Config;
|
||||
}
|
||||
|
||||
MAX_PARTICLES : s32 : 2048;
|
||||
|
||||
g_particles : [2048] Particle;
|
||||
g_emitter_instances : [..] Particle_Emitter_Instance;
|
||||
g_emitter_defs : [..] Particle_Emitter_Definition;
|
||||
g_particles : [2048] Particle;
|
||||
g_emitter_defs : [..] Particle_Emitter_Config;
|
||||
|
||||
tick_particles :: (dt: float) {
|
||||
return;
|
||||
for *p: g_particles {
|
||||
if !p.alive then continue;
|
||||
p.age += dt;
|
||||
@ -60,11 +49,12 @@ tick_particles :: (dt: float) {
|
||||
p.alive = false;
|
||||
continue;
|
||||
}
|
||||
p.velocity.y -= p.gravity * dt;
|
||||
p.position += p.velocity * dt;
|
||||
p.velocity.y -= p.gravity * dt;
|
||||
p.position += p.velocity * dt;
|
||||
}
|
||||
|
||||
for *inst: g_emitter_instances {
|
||||
for *inst: get_current_world().world.emitter_instances {
|
||||
if inst == null then continue;
|
||||
if !inst.active then continue;
|
||||
if inst.definition == null then continue;
|
||||
def := inst.definition;
|
||||
@ -76,71 +66,19 @@ tick_particles :: (dt: float) {
|
||||
}
|
||||
}
|
||||
|
||||
tick_emitter_instance :: (inst: *Particle_Emitter_Instance, dt: float) {
|
||||
if !inst.active then return;
|
||||
if inst.definition == null then return;
|
||||
def := inst.definition;
|
||||
inst.spawn_accumulator += def.emission_rate * dt;
|
||||
while inst.spawn_accumulator >= 1.0 {
|
||||
inst.spawn_accumulator -= 1.0;
|
||||
spawn_one_particle(inst.position, def);
|
||||
}
|
||||
}
|
||||
|
||||
add_particle_render_tasks :: () {
|
||||
for *def: g_emitter_defs {
|
||||
if def.sprite_image.id == 0 then continue;
|
||||
|
||||
instances : [..] Particle_Instance_Data;
|
||||
instances.allocator = temp;
|
||||
|
||||
for *p: g_particles {
|
||||
if !p.alive then continue;
|
||||
if p.definition != def then continue;
|
||||
|
||||
t := p.age / p.lifetime;
|
||||
t = clamp(t, 0.0, 1.0);
|
||||
|
||||
frame_count := cast(s32) max(def.frame_count, 1);
|
||||
frame_idx := cast(s32)(t * cast(float) frame_count);
|
||||
frame_idx = clamp(frame_idx, 0, frame_count - 1);
|
||||
|
||||
frame_w := 1.0 / cast(float) frame_count;
|
||||
uv_rect := Vector4.{frame_idx * frame_w, 0.0, frame_w, 1.0};
|
||||
|
||||
size := def.size_start + (def.size_end - def.size_start) * t;
|
||||
color := lerp_color(def.color_start, def.color_end, t);
|
||||
|
||||
inst : Particle_Instance_Data;
|
||||
inst.pos_size = .{p.position.x, p.position.y, p.position.z, size};
|
||||
inst.uv_rect = uv_rect;
|
||||
inst.color = color;
|
||||
array_add(*instances, inst);
|
||||
}
|
||||
|
||||
if instances.count == 0 then continue;
|
||||
|
||||
task : Rendering_Task_Particles;
|
||||
task.instances = instances;
|
||||
task.sprite_image = def.sprite_image;
|
||||
add_rendering_task(task);
|
||||
}
|
||||
}
|
||||
|
||||
clear_particle_emitter_instances :: () {
|
||||
array_reset(*g_emitter_instances);
|
||||
for *p: g_particles {
|
||||
p.alive = false;
|
||||
}
|
||||
}
|
||||
|
||||
place_emitter :: (def_name: string, position: Vector3) {
|
||||
inst : Particle_Emitter_Instance;
|
||||
inst.definition_name = sprint("%", def_name);
|
||||
inst.position = position;
|
||||
inst.active = true;
|
||||
resolve_emitter_definition(*inst);
|
||||
array_add(*g_emitter_instances, inst);
|
||||
}
|
||||
|
||||
remove_emitter_at_index :: (idx: int) {
|
||||
if idx < 0 || idx >= g_emitter_instances.count then return;
|
||||
array_unordered_remove_by_index(*g_emitter_instances, idx);
|
||||
}
|
||||
|
||||
resolve_all_emitter_instances :: () {
|
||||
for *inst: g_emitter_instances {
|
||||
resolve_emitter_definition(inst);
|
||||
}
|
||||
// TODO
|
||||
}
|
||||
|
||||
get_emitter_def_names :: () -> []string {
|
||||
@ -152,7 +90,7 @@ get_emitter_def_names :: () -> []string {
|
||||
return names;
|
||||
}
|
||||
|
||||
save_particle_definition :: (def: Particle_Emitter_Definition) {
|
||||
save_particle_definition :: (def: Particle_Emitter_Config) {
|
||||
#if OS != .WASM {
|
||||
file :: #import "File";
|
||||
dir := "./game/resources/particles";
|
||||
@ -164,58 +102,6 @@ save_particle_definition :: (def: Particle_Emitter_Definition) {
|
||||
}
|
||||
}
|
||||
|
||||
load_emitters_for_world :: (world_name: string) {
|
||||
#if OS != .WASM {
|
||||
file :: #import "File";
|
||||
path := tprint("./game/resources/worlds/%/emitters.json", world_name);
|
||||
data, ok := file.read_entire_file(path);
|
||||
if !ok then return;
|
||||
defer free(data.data);
|
||||
|
||||
Emitter_Entry :: struct {
|
||||
definition : string;
|
||||
position : [3] float;
|
||||
}
|
||||
success, entries := Jaison.json_parse_string(cast(string) data, [] Emitter_Entry,, temp);
|
||||
if !success {
|
||||
log_error("Failed to parse emitters.json for world '%'", world_name);
|
||||
return;
|
||||
}
|
||||
for entry: entries {
|
||||
pos := Vector3.{entry.position[0], entry.position[1], entry.position[2]};
|
||||
place_emitter(entry.definition, pos);
|
||||
}
|
||||
log_info("Loaded % emitters for world '%'", entries.count, world_name);
|
||||
}
|
||||
}
|
||||
|
||||
save_emitters_for_world :: (world_name: string) {
|
||||
#if OS != .WASM {
|
||||
file :: #import "File";
|
||||
dir := tprint("./game/resources/worlds/%", world_name);
|
||||
file.make_directory_if_it_does_not_exist(dir, recursive = true);
|
||||
path := tprint("%/emitters.json", dir);
|
||||
|
||||
Emitter_Entry :: struct {
|
||||
definition : string;
|
||||
position : [3] float;
|
||||
}
|
||||
entries : [..] Emitter_Entry;
|
||||
entries.allocator = temp;
|
||||
for inst: g_emitter_instances {
|
||||
e : Emitter_Entry;
|
||||
e.definition = inst.definition_name;
|
||||
e.position[0] = inst.position.x;
|
||||
e.position[1] = inst.position.y;
|
||||
e.position[2] = inst.position.z;
|
||||
array_add(*entries, e);
|
||||
}
|
||||
json := Jaison.json_write_string(entries,, temp);
|
||||
file.write_entire_file(path, json);
|
||||
log_info("Saved % emitters for world '%'", entries.count, world_name);
|
||||
}
|
||||
}
|
||||
|
||||
#scope_file
|
||||
|
||||
Random :: #import "Random";
|
||||
@ -224,7 +110,7 @@ rng :: inline (lo: float, hi: float) -> float {
|
||||
return lo + Random.random_get_zero_to_one() * (hi - lo);
|
||||
}
|
||||
|
||||
spawn_one_particle :: (position: Vector3, def: *Particle_Emitter_Definition) {
|
||||
spawn_one_particle :: (position: Vector3, def: *Particle_Emitter_Config) {
|
||||
for *p: g_particles {
|
||||
if p.alive then continue;
|
||||
p.alive = true;
|
||||
@ -245,13 +131,3 @@ spawn_one_particle :: (position: Vector3, def: *Particle_Emitter_Definition) {
|
||||
lerp_color :: (a: Vector4, b: Vector4, t: float) -> Vector4 {
|
||||
return a * (1.0 - t) + b * t;
|
||||
}
|
||||
|
||||
resolve_emitter_definition :: (inst: *Particle_Emitter_Instance) {
|
||||
for *def: g_emitter_defs {
|
||||
if def.name == inst.definition_name {
|
||||
inst.definition = def;
|
||||
return;
|
||||
}
|
||||
}
|
||||
inst.definition = null;
|
||||
}
|
||||
|
||||
@ -59,9 +59,11 @@ Chunk :: struct {
|
||||
}
|
||||
|
||||
World :: struct {
|
||||
name : string;
|
||||
conf : World_Config;
|
||||
chunks : Table(Chunk_Key, Chunk, chunk_key_hash, chunk_key_compare);
|
||||
name : string;
|
||||
conf : World_Config;
|
||||
chunks : Table(Chunk_Key, Chunk, chunk_key_hash, chunk_key_compare);
|
||||
// emitter_instances : [..]Particle_Emitter_Instance;
|
||||
emitter_instances : [..]string;
|
||||
}
|
||||
|
||||
// Convert a world-space integer position to chunk coordinate.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user