Entity_Id :: #type, isa s64; Entity_Flags :: enum_flags u16 { NONE; RENDERABLE; COLLISION; PHYSICS; STATIC; TRIGGER; ANIMATED; SNAP_TO_GRID; UNIFORM_SCALE; DONT_SAVE; DELETED; } Renderable_Type :: enum { MODEL; } Entity_Material :: struct { base_color : Vector4; } MAX_NODES :: 256; Node_Render_Data :: struct { enabled : bool = true; transform: Transform; material : Entity_Material; // Buffers transform_buffer: Buffer_Handle; material_buffer: Buffer_Handle; bone_buffers : [MAX_BONES] Buffer_Handle; num_bones: s64; } Renderable :: struct { visible: bool = true; type : Renderable_Type; @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; Entity :: struct { name: string; id: Entity_Id; @ReadOnly @DontSerialize type : Type; enabled: bool = true; parent: *Entity; @DontSerialize children: [MAX_CHILDREN] *Entity; @DontSerialize num_children: s64; @DontSerialize attach_node_index: s64 = -1; @DontSerialize flags : Entity_Flags; transform: Transform; rendering_offset: Vector3; snap_offset: Vector3; snap_intervals: Vector3 = .{1,1,1}; renderable: Renderable; animator: Animator; @DontSerialize // Physics body : Physics_Body; collider : Collider; // End physics #if NETWORKING { remote_id: Entity_Id; @DontSerialize client_id: Client_Id; @DontSerialize is_proxy: bool; @DontSerialize last_replication_time: float; @DontSerialize } _locator: Bucket_Locator; @DontSerialize scene: *Scene; @DontSerialize using custom_fields: _Custom_Entity_Fields; } add_child :: (e: *Entity, child: *Entity, node_name: string = "") { set_parent(child, e, node_name); } set_parent :: (e: *Entity, parent: *Entity, node_name: string = "") { parent.children[parent.num_children] = e; e.parent = parent; parent.num_children += 1; model := get_model_by_handle(parent.renderable.model); for node, index: model.nodes { if node.name == node_name { e.attach_node_index = index; break; } } } 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 := model.nodes[i]; if node_name.count == 0 || node_name == actual_node.name { data := *e.renderable.nodes[i]; if data.material_buffer > 0 { material : Entity_Material = ---; material.base_color = color; upload_data_to_buffer(engine.renderer, data.material_buffer, *material, size_of(Entity_Material)); } } } } } set_node_enabled :: (e: *Entity, node_name: string, enabled : bool) { if e.renderable.type == .MODEL { for i: 0..e.renderable.num_nodes-1 { actual_node := e.renderable.model.nodes[i]; if node_name.count == 0 || node_name == actual_node.name { data := *e.renderable.nodes[i]; data.enabled = enabled; } } } } get_node_world_position :: (e: *Entity, node_name: string) -> Vector3 { if e.renderable.type == .MODEL { model := get_model_by_handle(e.renderable.model); for i: 0..e.renderable.num_nodes-1 { actual_node := model.nodes[i]; if node_name.count == 0 || node_name == actual_node.name { data := *e.renderable.nodes[i]; return Vector3.{data.transform.world_matrix._14, data.transform.world_matrix._24, data.transform.world_matrix._34}; } } } return .{}; } 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 = handle; for *model.nodes { data : Node_Render_Data; data.transform = it.transform; update_matrix(*it.transform); if it.meshes.count > 0 { material : Entity_Material = ---; material.base_color = it.material_defaults[0].base_color; // @Incomplete: What if there are multiple meshes? data.material = material; data.transform_buffer = create_constant_buffer(engine.renderer, null, size_of(Matrix4), mappable=true); data.material_buffer = create_constant_buffer(engine.renderer, *material, size_of(Entity_Material), mappable=true); data.num_bones = it.num_bones; if it.num_bones > 0 { for bone_index: 0..it.num_bones-1 { data.bone_buffers[bone_index] = create_constant_buffer(engine.renderer, null, size_of(Matrix4) * MAX_BONES, mappable=true); } } } e.renderable.nodes[it_index] = data; } } mark_entity_deleted :: (e: *Entity) { e.flags |= .DELETED; } entity_should_be_rendered :: (e: *Entity) -> bool { if !(e.flags & .RENDERABLE) || (e.flags & .DELETED) || !e.enabled return false; return true; } destroy_entity :: (e: *Entity) { call_correct_deinit_entity(e); if e.collider.mesh.vertices.data != null { array_free(e.collider.mesh.vertices); } for 0..e.renderable.num_nodes-1 { node_data := e.renderable.nodes[it]; if node_data.transform_buffer > 0 { destroy_buffer(engine.renderer, node_data.transform_buffer); } if node_data.material_buffer > 0 { destroy_buffer(engine.renderer, node_data.material_buffer); } for bi: 0..node_data.num_bones-1 { destroy_buffer(engine.renderer, node_data.bone_buffers[bi]); } } free(e.name); }