Entity_Id :: #type, isa s64; Entity_Flags :: enum_flags u16 { NONE :: 0; RENDERABLE :: 1; PHYSICS :: 2; ANIMATED :: 4; SNAP_TO_GRID :: 8; UNIFORM_SCALE :: 16; DONT_SAVE :: 32; DELETED :: 64; } 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 use_default_pipeline: bool = true; 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; #if PHYSICS { Collider_Type :: enum { SPHERE; BOX; CAPSULE; TRIANGLE_MESH; CONVEX_MESH; CHARACTER; } Physics_Lock :: enum_flags u8 { LINEAR_X; LINEAR_Y; LINEAR_Z; ANGULAR_X; ANGULAR_Y; ANGULAR_Z; } Physics_Body :: struct { enabled: bool; trigger: bool; dynamic: bool; velocity: Vector3; static_friction: float; dynamic_friction: float; restitution: float; collider_color: Color; lock: Physics_Lock; offset: Vector3; type: Collider_Type; union { sphere : struct { radius: float; } box : struct { half_extent: Vector3; } capsule : struct { radius: float; half_height: float; } character : struct { radius: float; height: float; } } physx_handle: PhysX_Handle; } } 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 #if PHYSICS { physics: Physics_Body; @DontSerialize } #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) { if handle == 0 { log_error("MODEL: Attempted to load model into entity of type %, but the model handle is zero. This probably means that the model has not been loaded properly.\n", e.type); return; } 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); 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); }