#load "../renderer/directional_light.jai"; #load "../renderer/point_light.jai"; #load "particles.jai"; MAX_CACHED_PILES :: 8; last_unnamed_scene_id := 0; Scene :: struct { name: string; entities : [..] *Entity; particle_systems : Bucket_Array(Particle_System, 128); by_type : Entity_Storage; pool : Flat_Pool; allocator : Allocator; camera : Camera; directional_light : Directional_Light; mode: Engine_Mode; using custom_fields: _Custom_Scene_Fields; } Entity_File_Info :: struct { id: Entity_Id; full_path: string; } visitor :: (info : *File_Visit_Info, files: *[..] Entity_File_Info) { if info.is_directory return; path, basename, ext := path_decomp (info.full_name); // Entity text files if ext == "ent" && basename != "cam" { file_info : Entity_File_Info; file_info.id = cast(Entity_Id)string_to_int(basename); file_info.full_path = copy_temporary_string(info.full_name); array_add(files, file_info); } } load_scene :: (name: string) -> *Scene { scene := create_scene(name, 1024); scene.mode = engine.mode; if engine.procs.on_scene_loaded != null { engine.procs.on_pre_scene_loaded(scene, engine.mode); } path := tprint("../assets/scenes/%", name); files : [..] Entity_File_Info; files.allocator = temp; visit_files(path, true, *files, visitor); highest : Entity_Id = 0; for file: files { deserialize_entity(scene, file.id, file.full_path); if file.id > highest { highest = file.id; } } next_entity_id = cast(Entity_Id)(highest + 1); update_transforms(scene); calculate_aabbs(scene); make_sure_nothing_collides(scene); if engine.procs.on_scene_loaded != null { engine.procs.on_scene_loaded(scene, engine.mode); } if engine.mode == .EDITING { save_last_opened_scene_file(name); } return scene; } get_last_opened_scene_file :: () -> string { if file_exists("../.config") { return read_entire_file("../.config"); } return .{}; } save_last_opened_scene_file :: (name: string) { write_entire_file("../.config", name); } save_scene :: (scene: *Scene) { path := "../assets/scenes/"; save_scene(scene, path); } save_scene :: (scene: *Scene, path: string) { builder : String_Builder; builder.allocator = temp; full_path := tprint("%/%", path, scene.name); make_directory_if_it_does_not_exist(full_path); for scene.entities { if it.flags & .DONT_SAVE continue; if it.flags & .DELETED continue; serialize_entity(it, full_path); } for scene.entities { if it.flags & .DELETED { delete_entity(it); remove it; } } // Save camera //print_to_builder(*builder, "Camera: % % % % %\n", scene.camera.position.x, scene.camera.position.y, scene.camera.position.z, scene.camera.rotation.yaw, scene.camera.rotation.pitch); //write_entire_file(path, builder_to_string(*builder)); } unload_scene :: (scene: *Scene) { if engine.procs.on_pre_scene_unloaded != null { engine.procs.on_pre_scene_unloaded(scene); } for e: scene.entities { destroy_entity(e); } free(scene.name); fini(*scene.pool); free(scene); } switch_to_scene :: (scene_name: string) { new_scene := load_scene(scene_name); unload_scene(engine.current_scene); engine.current_scene = new_scene; } reload_scene :: (scene: *Scene) { name := copy_temporary_string(scene.name); unload_scene(engine.current_scene); engine.current_scene = load_scene(name); } create_scene :: (name: string = "", max_entities: s64 = 256) -> *Scene { scene := New(Scene); new_name := name; // If no name was specified, we're going to pick an unused "unnamed_scene"-variant if new_name.count == 0 { while true { defer last_unnamed_scene_id += 1; temp_name := tprint("unnamed_scene_%", last_unnamed_scene_id); if !file_exists(tprint("../assets/scenes/%", temp_name)) { new_name = temp_name; break; } } } scene.name = copy_string(new_name); scene.mode = engine.mode; // Setup allocator scene.pool = .{}; scene.allocator.data = *scene.pool; scene.allocator.proc = flat_pool_allocator_proc; init_scene(scene); // Assign allocator to everything that needs allocations scene.entities.allocator = scene.allocator; scene.particle_systems.allocator = scene.allocator; array_reserve(*scene.entities, max_entities); scene.directional_light.color_and_intensity = .{1,1,1,2}; scene.directional_light.direction = to_v4(normalize(Vector3.{0.4, -0.7, 0.4})); dir_light_data : Directional_Light_Buffer_Data; dir_light_data.color_and_intensity = scene.directional_light.color_and_intensity; dir_light_data.direction = scene.directional_light.direction; upload_data_to_buffer(engine.renderer, engine.directional_light_buffer, *dir_light_data, size_of(Directional_Light_Buffer_Data)); return scene; } register_entity :: (scene: *Scene, entity: *Entity, id: Entity_Id = -1) { entity.scene = scene; if id != -1 { entity.id = id; } else { entity.id = next_entity_id; next_entity_id += 1; } array_add(*scene.entities, entity); #if NETWORKING { if net_data.net_mode == { case .LISTEN_SERVER; #through; case .DEDICATED_SERVER; { //net_spawn_entity(entity); } } } } unregister_entity :: (scene: *Scene, entity: *Entity) { array_unordered_remove_by_value(*scene.entities, entity); } update_animators :: (dt: float) { if engine.current_scene == null return; for e: engine.current_scene.entities { if e.flags & .ANIMATED { update_animator(e, *e.animator, dt); } } } update_transforms :: (scene: *Scene) { if scene == null return; for e: scene.entities { if e.parent == null { update_entity_transform(e, Matrix4_Identity); } } } update_entity_node :: (e: *Entity, model_node: Node, index: s64, parent_matrix: Matrix4) { n := *e.renderable.nodes[index]; update_matrix(*n.transform); n.transform.world_matrix = parent_matrix * n.transform.model_matrix; model := get_model_by_handle(e.renderable.model); for children_index: 0..model_node.children.count-1 { index := model_node.children[children_index]-1; mn := model.nodes[index]; update_entity_node(e, mn, xx index, n.transform.world_matrix); } } update_entity_transform :: (e: *Entity, parent_matrix: Matrix4 = Matrix4_Identity) { if e.transform.dirty { update_matrix(*e.transform); e.transform.dirty = false; } if length(e.rendering_offset) > 0.0 { offset := create_identity_transform(); set_position(*offset, e.rendering_offset); e.transform.world_matrix = parent_matrix * e.transform.model_matrix * offset.model_matrix; } else { e.transform.world_matrix = parent_matrix * e.transform.model_matrix; } if e.flags & .RENDERABLE { if e.renderable.model != 0 { model := get_model_by_handle(e.renderable.model); for model_node, i: model.nodes { if model_node.parent == 0 { update_entity_node(e, model_node, i, e.transform.world_matrix); } } } } for i: 0..e.num_children-1 { child := e.children[i]; matrix := e.transform.world_matrix; if child.attach_node_index != -1 { matrix = e.renderable.nodes[child.attach_node_index].transform.world_matrix; } update_entity_transform(child, matrix); } } get_entity_by_id :: (scene: *Scene, id: s64) -> *Entity { for scene.entities { if it.id == id return it; } return null; } #scope_file next_entity_id: Entity_Id;