diff --git a/animation/animator.jai b/animation/animator.jai index 9643ae5..989a037 100644 --- a/animation/animator.jai +++ b/animation/animator.jai @@ -158,11 +158,12 @@ animation_time_normalized :: (e: *Entity) -> float { sample_animation_state :: (e: *Entity, index: s32, weight: float) { state := *e.animator.states[index]; + model := get_model_by_handle(e.renderable.model); if state.type == { case .SINGLE; { - animation := *e.renderable.model.animations[state.single.index]; + animation := *model.animations[state.single.index]; sample_animation(e, animation, min(state.single.time, cast(float)animation.duration - 0.0001), weight); } case .BLEND_TREE_1D; @@ -187,8 +188,8 @@ sample_animation_state :: (e: *Entity, index: s32, weight: float) { range := p1_position - p0_position; alpha := position / range; - anim0 := e.renderable.model.animations[state.blend_tree.points[p0].index]; - anim1 := e.renderable.model.animations[state.blend_tree.points[p1].index]; + anim0 := model.animations[state.blend_tree.points[p0].index]; + anim1 := model.animations[state.blend_tree.points[p1].index]; time0 := state.blend_tree.points[p0].time; time1 := state.blend_tree.points[p1].time; @@ -213,13 +214,14 @@ transition_to_animation_state :: (e: *Entity, index: s32, transition_duration: f update_animation_state :: (e: *Entity, index: s32, dt: float) { state := *e.animator.states[index]; + model := get_model_by_handle(e.renderable.model); if state.type == { case .SINGLE; { state.single.time += dt * state.single.multiplier; - animation := *e.renderable.model.animations[state.single.index]; + animation := *model.animations[state.single.index]; if state.single.time > animation.duration { if state.single.looping { @@ -238,7 +240,7 @@ update_animation_state :: (e: *Entity, index: s32, dt: float) { for *p: state.blend_tree.points { p.time += dt * p.multiplier; - animation := *e.renderable.model.animations[p.index]; + animation := *model.animations[p.index]; if p.time > animation.duration { if p.looping { @@ -259,7 +261,6 @@ update_animator :: (e: *Entity, animator: *Animator, dt: float) { it.transform.position = .{0,0,0}; it.transform.scale = .{0,0,0}; it.transform.orientation = .{0,0,0,0}; - it.has_sampled_animation = false; } if animator.playing_transition { diff --git a/core/entity.jai b/core/entity.jai index ee3e666..a559687 100644 --- a/core/entity.jai +++ b/core/entity.jai @@ -31,7 +31,6 @@ Node_Render_Data :: struct { enabled : bool = true; transform: Transform; material : Entity_Material; - has_sampled_animation: bool; // Buffers transform_buffer: Buffer_Handle; @@ -45,9 +44,11 @@ Renderable :: struct { visible: bool = true; type : Renderable_Type; @DontSerialize - model: *Model; @DontSerialize + model: Model_Handle; @DontSerialize nodes: [MAX_NODES] Node_Render_Data; @DontSerialize num_nodes: s64; @DontSerialize + + //node_buffer : Buffer_Handle; // Structure buffer with all transform + material // TODO } MAX_CHILDREN :: 16; @@ -114,8 +115,10 @@ set_parent :: (e: *Entity, parent: *Entity, node_name: string = "") { set_base_color :: (e: *Entity, color: Vector4, node_name: string = "") { if e.renderable.type == .MODEL { + model := get_model_by_handle(e.renderable.model); + for i: 0..e.renderable.num_nodes-1 { - actual_node := e.renderable.model.nodes[i]; + actual_node := model.nodes[i]; if node_name.count == 0 || node_name == actual_node.name { data := *e.renderable.nodes[i]; if data.material_buffer > 0 { @@ -140,13 +143,15 @@ set_node_enabled :: (e: *Entity, node_name: string, enabled : bool) { } } -load_model_into_entity :: (e: *Entity, model: *Model) { +load_model_into_entity :: (e: *Entity, handle: Model_Handle) { + model := get_model_by_handle(handle); + e.renderable.type = .MODEL; assert(model.nodes.count <= MAX_NODES); e.renderable.num_nodes = model.nodes.count; - e.renderable.model = model; + e.renderable.model = handle; for *model.nodes { data : Node_Render_Data; diff --git a/core/mesh_entity.jai b/core/mesh_entity.jai new file mode 100644 index 0000000..afb6866 --- /dev/null +++ b/core/mesh_entity.jai @@ -0,0 +1,42 @@ +Mesh_Entity :: struct { + using #as entity: Entity; + entity.flags = .RENDERABLE; + entity.type = Mesh_Entity; + + model_path: string; +} + +init_entity :: (e: *Mesh_Entity) { + if e.model_path.count > 0 { + load_model_into_entity(e, get_or_load_model(e.model_path)); + } +} + +mesh_entity_files : [..] Mesh_Entity_Info; + +Mesh_Entity_Info :: struct { + full_path: string; +} + +mesh_entity_visitor :: (info : *File_Visit_Info, files: *[..] Mesh_Entity_Info) { + if info.is_directory + return; + path, basename, ext := path_decomp (info.full_name); + + // Entity text files + if ext == "fbx" { + file_info : Mesh_Entity_Info; + file_info.full_path = copy_string(info.full_name); + array_add(files, file_info); + } +} + +find_all_mesh_entities :: () { + path := "../assets/models/level_design/"; + + visit_files(path, true, *mesh_entity_files, mesh_entity_visitor); + + for mesh_entity_files { + print(it.full_path); + } +} \ No newline at end of file diff --git a/core/ray.jai b/core/ray.jai index 29fec28..816c1ad 100644 --- a/core/ray.jai +++ b/core/ray.jai @@ -194,7 +194,11 @@ ray_entity_intersect :: (ray: Ray, e: *Entity) -> bool, float, Vector3 { closest : float = 10000000; closest_normal : Vector3; - for n: e.renderable.model.nodes { + if e.renderable.model == 0 return false, 0.0, .{}; + + model := get_model_by_handle(e.renderable.model); + + for n: model.nodes { render_node := e.renderable.nodes[it_index]; for handle: n.meshes { m := parray_get(*engine.renderer.meshes, handle); diff --git a/core/scene.jai b/core/scene.jai index d29247b..6ea4e83 100644 --- a/core/scene.jai +++ b/core/scene.jai @@ -248,9 +248,11 @@ update_entity_node :: (e: *Entity, model_node: Node, index: s64, parent_matrix: 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 := e.renderable.model.nodes[index]; + mn := model.nodes[index]; update_entity_node(e, mn, xx index, n.transform.world_matrix); } } @@ -270,9 +272,12 @@ update_entity_transform :: (e: *Entity, parent_matrix: Matrix4 = Matrix4_Identit } if e.flags & .RENDERABLE { - for model_node, i: e.renderable.model.nodes { - if model_node.parent == 0 { - update_entity_node(e, model_node, i, e.transform.world_matrix); + 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); + } } } } diff --git a/editor/editor_ui.jai b/editor/editor_ui.jai index b70b747..95534df 100644 --- a/editor/editor_ui.jai +++ b/editor/editor_ui.jai @@ -54,6 +54,33 @@ editor_ui :: () { } ImGui.End(); + + ImGui.Begin("Create Mesh Entity", flags=ImGui.WindowFlags.NoResize); + + for mesh_entity_files { + ImGui.PushID(to_temp_c_string(it.full_path)) ; + defer ImGui.PopID(); + + if ImGui.Button(to_temp_c_string(it.full_path)) { + mesh_entity := new_mesh_entity(init=false); + mesh_entity.model_path = it.full_path; + init_entity(mesh_entity); + new_entity = mesh_entity; + break; + } + } + + //set_position(*new_entity.transform, engine.editor.camera.position + engine.editor.camera.forward * 20.0); + //engine.editor.selected_entities.count = 0; + //array_add(*engine.editor.selected_entities, new_entity); + + ImGui.End(); + + if new_entity != null { + set_position(*new_entity.transform, engine.editor.camera.position + engine.editor.camera.forward * 20.0); + engine.editor.selected_entities.count = 0; + array_add(*engine.editor.selected_entities, new_entity); + } } ImGui.Begin("Entities"); diff --git a/metaprogram.jai b/metaprogram.jai index 5c66c7d..c3e1381 100644 --- a/metaprogram.jai +++ b/metaprogram.jai @@ -784,6 +784,7 @@ PLACEHOLDER :: #string DONE #poke_name Coven delete_entity; #poke_name Coven deserialize_entity; #poke_name Coven serialize_entity; +#poke_name Coven new_mesh_entity; #if EDITOR { #poke_name Coven editor_ui_entity_creation; diff --git a/module.jai b/module.jai index 10b60ec..56c6bab 100644 --- a/module.jai +++ b/module.jai @@ -82,6 +82,8 @@ coven_init :: (window_title: string, window_width: u32, window_height: u32, full init_editor(); ui_init(); } + + find_all_mesh_entities(); } coven_run :: (game_update_proc: (float), game_editor_update_proc: (float), game_update_post_physics_proc: (float)) { @@ -206,6 +208,7 @@ switch_engine_mode :: (to_mode: Engine_Mode) { #load "windowing/window.jai"; #load "physics/physics.jai"; +#load "core/mesh_entity.jai"; #load "core/string_helpers.jai"; #load "core/math.jai"; #load "core/ray.jai"; diff --git a/physics/physics.jai b/physics/physics.jai index 5b4ac47..8358b1d 100644 --- a/physics/physics.jai +++ b/physics/physics.jai @@ -408,10 +408,12 @@ calculate_aabbs :: (scene: *Scene) { for e: scene.entities { if e.flags & .COLLISION && e.flags & .RENDERABLE { if e.collider.override_aabb continue; + if e.renderable.model == 0 continue; aabb : AABB; + model := get_model_by_handle(e.renderable.model); - for n : e.renderable.model.nodes { + for n : model.nodes { if n.parent == 0 { bake_aabb(*aabb, Matrix4_Identity, e, n); } @@ -459,9 +461,11 @@ bake_aabb :: (aabb: *AABB, parent_matrix: Matrix4, e: *Entity, n: Node) { } } } + + model := get_model_by_handle(e.renderable.model); for n.children { - child := *e.renderable.model.nodes[it - 1]; + child := *model.nodes[it - 1]; bake_aabb(aabb, node_matrix, e, child); } diff --git a/renderer/engine_buffers.jai b/renderer/engine_buffers.jai index 0406ab4..ceb6321 100644 --- a/renderer/engine_buffers.jai +++ b/renderer/engine_buffers.jai @@ -97,7 +97,10 @@ sync_engine_buffers :: () { if it.flags & .RENDERABLE { if it.renderable.type == { case .MODEL; { - for n, i: it.renderable.model.nodes { + if it.renderable.model == 0 continue; + + model := get_model_by_handle(it.renderable.model); + for n, i: model.nodes { if n.meshes.count > 0 { node_data := *it.renderable.nodes[i]; upload_data_to_buffer(engine.renderer, node_data.transform_buffer, *node_data.transform.world_matrix, size_of(Matrix4)); diff --git a/renderer/model.jai b/renderer/model.jai index b914919..3ecafff 100644 --- a/renderer/model.jai +++ b/renderer/model.jai @@ -1,5 +1,10 @@ Node_Handle :: #type, distinct u32; Material_Handle :: #type, distinct u32; +Model_Handle :: #type, isa u32; + +Model_Asset :: struct { + path: string; +} MAX_BONES :: 128; MAX_WEIGHTS :: 4; @@ -66,7 +71,9 @@ Model :: struct { materials : [..] Model_Material; } -get_first_mesh_from_model :: (model: Model) -> Mesh_Handle, bool { +get_first_mesh_from_model :: (handle: Model_Handle) -> Mesh_Handle, bool { + model := get_model_by_handle(handle); + for model.nodes { for m: it.meshes { return m, true; @@ -377,7 +384,9 @@ parse_fbx_node :: (model: *Model, fbx_node: *ufbx_node) { // } //} - array_add(*node.meshes, parray_add(*engine.renderer.meshes, mesh)); + array_add(*node.meshes, parray_add(*engine.renderer.meshes, mesh));struct { + path: string; + } array_add(*node.material_defaults, model.materials[mesh_mat.material.typed_id]); // @Incomplete } } @@ -580,7 +589,7 @@ load_fbx_texture :: (map: ufbx_material_map, format: Format) -> Texture_Handle { return 0; } -load_fbx :: (path: string) -> *Model, bool { +load_fbx :: (path: string) -> Model_Handle, bool { opts : ufbx_load_opts = .{}; opts.load_external_files = true; opts.generate_missing_normals = true; @@ -597,10 +606,11 @@ load_fbx :: (path: string) -> *Model, bool { if scene == null { log_error("FBX '%' could not be loaded", path); - return null, false; + return 0, false; } - model, locator := find_and_occupy_empty_slot(*engine.renderer.model_lib); + model : Model; + model.path = copy_string(path); model.name = copy_string(path); @@ -643,24 +653,27 @@ load_fbx :: (path: string) -> *Model, bool { for i: 0..scene.nodes.count-1 { node := scene.nodes.data[i]; - parse_fbx_node(model, node); + parse_fbx_node(*model, node); } // Load animations array_resize(*model.animations, xx scene.anim_stacks.count); for 0..model.animations.count-1 { - parse_anim_stack(*model.animations[it], scene.anim_stacks.data[it], scene, model); + parse_anim_stack(*model.animations[it], scene.anim_stacks.data[it], scene, *model); } ufbx_free_scene(scene); - return model, false; + array_add(*engine.renderer.model_lib, model); + handle := cast(Model_Handle)engine.renderer.model_lib.count; + + return handle, false; } -get_or_load_model :: (path: string) -> *Model { +get_or_load_model :: (path: string) -> Model_Handle { for * engine.renderer.model_lib { if it.path == path { - return it; + return xx (it_index + 1); } } diff --git a/renderer/renderer.jai b/renderer/renderer.jai index daa3f86..b4361df 100644 --- a/renderer/renderer.jai +++ b/renderer/renderer.jai @@ -586,7 +586,7 @@ Renderer :: struct { fonts : [..] Font; render_graph : *Render_Graph; - model_lib : Bucket_Array(Model, 128); + model_lib : Static_Array(Model, 128); callbacks : struct { get_custom_material_parameter_mapping: (mapping_str: string) -> bool, Material_Mapping_Info; @@ -605,7 +605,7 @@ Renderer :: struct { render_target_height: u32; default_models : struct { - sphere: Model; + sphere: Model_Handle; } default_meshes : struct { @@ -1627,6 +1627,12 @@ create_material_from_pipeline :: (pipeline: Pipeline_State_Handle) -> Material_O return material; } +get_model_by_handle :: (handle: Model_Handle) -> *Model { + if handle == 0 return null; + + return *engine.renderer.model_lib[handle-1]; +} + check_for_shader_modifications :: () { changed, needs_wait, wait_seconds := process_changes(*engine.renderer.watcher); }