876 lines
29 KiB
Plaintext
876 lines
29 KiB
Plaintext
#import "Compiler";
|
|
#import "Basic";
|
|
#import "String";
|
|
#import "Sort";
|
|
#import "File";
|
|
|
|
build :: (build_release: bool, main_path: string, game_name: string, working_dir: string = #filepath) {
|
|
set_working_directory(working_dir);
|
|
make_directory_if_it_does_not_exist("../../bin");
|
|
|
|
w := compiler_create_workspace("Game");
|
|
opts := get_build_options(w);
|
|
|
|
opts.output_type = .EXECUTABLE;
|
|
opts.output_executable_name = game_name;
|
|
opts.output_path = "../../bin/";
|
|
|
|
new_path: [..] string;
|
|
array_add(*new_path, ..opts.import_path);
|
|
array_add(*new_path, "../../modules");
|
|
array_add(*new_path, "modules/ufbx");
|
|
array_add(*new_path, "modules");
|
|
opts.import_path = new_path;
|
|
|
|
if build_release {
|
|
set_optimization(*opts, .VERY_OPTIMIZED);
|
|
}
|
|
|
|
compiler_begin_intercept(w);
|
|
|
|
set_build_options(opts, w);
|
|
add_build_string(tprint("GAME_NAME :: \"%\";", game_name), w);
|
|
add_build_string("#import \"Bucket_Array\";", w);
|
|
add_build_file(main_path, w);
|
|
|
|
message_loop();
|
|
|
|
compiler_end_intercept(w);
|
|
|
|
set_build_options_dc(.{do_output=false});
|
|
}
|
|
|
|
// Contains all the auto-generated serialization procedures
|
|
entity_serialize_proc_string: [..] string;
|
|
|
|
should_serialize :: (type: *Type_Info_Struct, member: Type_Info_Struct_Member) -> bool {
|
|
if type.name == "Color" {
|
|
return true;
|
|
}
|
|
|
|
if type != null {
|
|
if type.name == {
|
|
case "Vector2"; {
|
|
if member.name == {
|
|
case "x"; #through;
|
|
case "y"; #through;
|
|
case; return false;
|
|
}
|
|
}
|
|
case "Vector3"; {
|
|
if member.name == {
|
|
case "x"; #through;
|
|
case "y"; #through;
|
|
case "z"; return true;
|
|
case; return false;
|
|
}
|
|
}
|
|
case "Vector4"; #through;
|
|
case "Color"; #through;
|
|
case "Quaternion"; {
|
|
if member.name == {
|
|
case "x"; #through;
|
|
case "y"; #through;
|
|
case "z"; #through;
|
|
case "w"; return true;
|
|
case; return false;
|
|
}
|
|
}
|
|
case "Static_Array"; #through;
|
|
case "PArray"; #through;
|
|
case "Stack"; #through;
|
|
case "Queue";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for member.notes {
|
|
if to_lower_copy(it,, allocator = temp) == "dontserialize" return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
should_make_ui :: (type: *Type_Info_Struct, member: Type_Info_Struct_Member) -> bool {
|
|
for member.notes {
|
|
if to_lower_copy(it,, allocator = temp) == "hide" return false;
|
|
}
|
|
|
|
if type != null {
|
|
if type.name == {
|
|
case "Vector2"; {
|
|
if member.name == {
|
|
case "x"; #through;
|
|
case "y"; #through;
|
|
case; return false;
|
|
}
|
|
}
|
|
case "Vector3"; {
|
|
if member.name == {
|
|
case "x"; #through;
|
|
case "y"; #through;
|
|
case "z"; return true;
|
|
case; return false;
|
|
}
|
|
}
|
|
case "Vector4"; #through;
|
|
case "Quaternion"; {
|
|
if member.name == {
|
|
case "x"; #through;
|
|
case "y"; #through;
|
|
case "z"; #through;
|
|
case "w"; return true;
|
|
case; return false;
|
|
}
|
|
}
|
|
case "Color"; return true;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
is_readonly :: (type: *Type_Info_Struct, member: Type_Info_Struct_Member) -> bool {
|
|
for member.notes {
|
|
if to_lower_copy(it,, allocator = temp) == "readonly" return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
generate_member_ui :: (type: *Type_Info_Struct, builder: *String_Builder, path: string = "") {
|
|
for type.members {
|
|
readonly := is_readonly(type, it);
|
|
if should_make_ui(type, it) && (should_serialize(type, it) || readonly) {
|
|
new_path : string;
|
|
if it.name != "entity" {
|
|
if path.count == 0 {
|
|
new_path = it.name;
|
|
} else {
|
|
new_path = tprint("%1.%2", path, it.name);
|
|
}
|
|
}
|
|
|
|
tag : Type_Info_Tag;
|
|
if it.type.type == .VARIANT {
|
|
info_variant := cast(*Type_Info_Variant)it.type;
|
|
tag = info_variant.variant_of.type;
|
|
} else {
|
|
tag = it.type.type;
|
|
}
|
|
|
|
if readonly {
|
|
print_to_builder(builder, "\tui_field_label(\"%\", e.%);\n", new_path, new_path);
|
|
} else {
|
|
if tag == {
|
|
case .STRUCT; {
|
|
info_struct := cast(*Type_Info_Struct) it.type;
|
|
if info_struct.name == "Transform" {
|
|
print_to_builder(builder, TRANSFORM_UI);
|
|
}
|
|
else if info_struct.name == "Vector3" {
|
|
print_to_builder(builder, "\tui_vector_field(tprint(\"%\"), *e.%);\n", new_path, new_path);
|
|
} else {
|
|
generate_member_ui(info_struct, builder, new_path);
|
|
}
|
|
}
|
|
case .BOOL; {
|
|
print_to_builder(builder, "\tui_checkbox_field(tprint(\"%\"), *e.%);\n", new_path, new_path);
|
|
}
|
|
case .STRING; {
|
|
print_to_builder(builder, "\tui_textfield(tprint(\"%\"), *e.%);\n", new_path, new_path);
|
|
//ui_textfield :: (label: string, text: *string, identifier: s64 = 0, loc := #caller_location) {
|
|
}
|
|
case .FLOAT; {
|
|
print_to_builder(builder, "\tui_float_field(tprint(\"%\"), *e.%);\n", new_path, new_path);
|
|
}
|
|
//case .ENUM; #through;
|
|
case .INTEGER; {
|
|
print_to_builder(builder, "\t{val := cast(int)e.%;\n", new_path);
|
|
print_to_builder(builder, "\tui_int_field(tprint(\"%\"), *val);\n", new_path);
|
|
print_to_builder(builder, "\te.% = xx val;}\n", new_path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
generate_member_ui_imgui :: (type: *Type_Info_Struct, builder: *String_Builder, path: string = "") {
|
|
for type.members {
|
|
readonly := is_readonly(type, it);
|
|
if should_make_ui(type, it) && (should_serialize(type, it) || readonly) {
|
|
new_path : string;
|
|
if it.name != "entity" {
|
|
if path.count == 0 {
|
|
new_path = it.name;
|
|
} else {
|
|
new_path = tprint("%1.%2", path, it.name);
|
|
}
|
|
}
|
|
|
|
tag : Type_Info_Tag;
|
|
if it.type.type == .VARIANT {
|
|
info_variant := cast(*Type_Info_Variant)it.type;
|
|
tag = info_variant.variant_of.type;
|
|
} else {
|
|
tag = it.type.type;
|
|
}
|
|
|
|
if readonly {
|
|
print_to_builder(builder, "\tImGui.Text(\"% \%\", e.%);\n", new_path, new_path);
|
|
} else {
|
|
if tag == {
|
|
case .STRUCT; {
|
|
info_struct := cast(*Type_Info_Struct) it.type;
|
|
if info_struct.name == "Transform" {
|
|
print_to_builder(builder, TRANSFORM_UI_IMGUI);
|
|
} else if info_struct.name == "Vector3" {
|
|
print_to_builder(builder, "\tImGui.DragFloat3(tprint_c(\"%\"), *e.%.component);\n", new_path, new_path);
|
|
} else if info_struct.name == "Color" || info_struct.name == "Vector4" {
|
|
print_to_builder(builder, "\tImGui.ColorEdit4(tprint_c(\"%\"), *e.%.component);\n", new_path, new_path);
|
|
} else {
|
|
generate_member_ui_imgui(info_struct, builder, new_path);
|
|
}
|
|
}
|
|
case .BOOL; {
|
|
print_to_builder(builder, "\tImGui.Checkbox(tprint_c(\"%\"), *e.%);\n", new_path, new_path);
|
|
}
|
|
case .STRING; {
|
|
print_to_builder(builder, "\timgui_input_text(tprint(\"%\"), *e.%);\n", new_path, new_path);
|
|
//ui_textfield :: (label: string, text: *string, identifier: s64 = 0, loc := #caller_location) {
|
|
}
|
|
case .FLOAT; {
|
|
print_to_builder(builder, "\tImGui.DragFloat(tprint_c(\"%\"), *e.%);\n", new_path, new_path);
|
|
}
|
|
//case .ENUM; #through;
|
|
case .INTEGER; {
|
|
print_to_builder(builder, "\t{val := cast(int)e.%;\n", new_path);
|
|
print_to_builder(builder, "\timgui_input_int(tprint(\"%\"), *val);\n", new_path);
|
|
print_to_builder(builder, "\te.% = xx val;}\n", new_path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TRANSFORM_UI :: #string DONE
|
|
updated := ui_vector_field("Position", *e.transform.position);
|
|
euler_rotation := quaternion_to_euler_v3(e.transform.orientation);
|
|
euler_rotation *= RADIANS_TO_DEGREES;
|
|
updated |= ui_vector_field("Rotation", *euler_rotation);
|
|
euler_rotation *= DEGREES_TO_RADIANS;
|
|
e.transform.orientation = euler_to_quaternion(euler_rotation);
|
|
updated |= ui_vector_field("Scale", *e.transform.scale);
|
|
|
|
if updated {
|
|
update_matrix(*e.transform);
|
|
}
|
|
DONE
|
|
|
|
TRANSFORM_UI_IMGUI :: #string DONE
|
|
updated := ImGui.DragFloat3("Position", *e.transform.position.component);
|
|
euler_rotation := quaternion_to_euler_v3(e.transform.orientation);
|
|
euler_rotation *= RADIANS_TO_DEGREES;
|
|
updated |= ImGui.DragFloat3("Rotation", *euler_rotation.component);
|
|
euler_rotation *= DEGREES_TO_RADIANS;
|
|
e.transform.orientation = euler_to_quaternion(euler_rotation);
|
|
updated |= ImGui.DragFloat3("Scale", *e.transform.scale.component);
|
|
|
|
if updated {
|
|
update_matrix(*e.transform);
|
|
}
|
|
DONE
|
|
|
|
generate_member_serialization :: (struct_type: *Type_Info_Struct, builder: *String_Builder, path: string = "") {
|
|
for struct_type.members {
|
|
if should_serialize(struct_type, it) {
|
|
new_path : string;
|
|
if path.count == 0 {
|
|
new_path = it.name;
|
|
} else {
|
|
new_path = tprint("%1.%2", path, it.name);
|
|
}
|
|
|
|
type : Type_Info_Tag;
|
|
if it.type.type == .VARIANT {
|
|
info_variant := cast(*Type_Info_Variant)it.type;
|
|
type = info_variant.variant_of.type;
|
|
} else {
|
|
type = it.type.type;
|
|
}
|
|
|
|
if type == {
|
|
case .STRUCT; {
|
|
info_struct := cast(*Type_Info_Struct) it.type;
|
|
generate_member_serialization(info_struct, builder, new_path);
|
|
}
|
|
case .STRING; {
|
|
print_to_builder(builder, "\tprint_to_builder(builder, \"%: \%\\n\", e.%);\n", new_path, new_path);
|
|
}
|
|
case .BOOL; #through;
|
|
case .FLOAT; #through;
|
|
case .ENUM; #through;
|
|
case .INTEGER; {
|
|
print_to_builder(builder, "\tprint_to_builder(builder, \"%: \%\\n\", e.%);\n", new_path, new_path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
generate_member_deserialization :: (struct_type: *Type_Info_Struct, builder: *String_Builder, path: string = "") {
|
|
for struct_type.members {
|
|
if should_serialize(struct_type, it) {
|
|
new_path : string;
|
|
if path.count == 0 {
|
|
new_path = it.name;
|
|
} else {
|
|
new_path = tprint("%1.%2", path, it.name);
|
|
}
|
|
|
|
type : Type_Info_Tag;
|
|
if it.type.type == .VARIANT {
|
|
info_variant := cast(*Type_Info_Variant)it.type;
|
|
type = info_variant.variant_of.type;
|
|
} else {
|
|
type = it.type.type;
|
|
}
|
|
|
|
if type == {
|
|
case .STRUCT; {
|
|
info_struct := cast(*Type_Info_Struct) it.type;
|
|
generate_member_deserialization(info_struct, builder, new_path);
|
|
}
|
|
case .STRING; {
|
|
print_to_builder(builder, "\t\t\t\tcase \"%\";\n", new_path);
|
|
print_to_builder(builder, "\t\t\t\tif values[1].count > 0 { e.%1 = copy_string(trim(values[1])); }\n", new_path);
|
|
}
|
|
case .BOOL; {
|
|
print_to_builder(builder, "\t\t\t\tcase \"%\";\n", new_path);
|
|
print_to_builder(builder, "\t\t\t\t\tscan2(trim(values[1]), \"\%\", *e.%);\n", new_path);
|
|
}
|
|
case .FLOAT; #through;
|
|
case .ENUM; {
|
|
print_to_builder(builder, "\t\t\t\tcase \"%\";\n", new_path);
|
|
print_to_builder(builder, "\t\t\t\t\tscan2(values[1], \"\%\", *e.%);\n", new_path);
|
|
}
|
|
case .INTEGER; {
|
|
print_to_builder(builder, "\t\t\t\tcase \"%\";\n", new_path);
|
|
print_to_builder(builder, "\t\t\t\t\tscan2(values[1], \"\%\", cast(*int)*e.%);\n", new_path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
generate_member_copy :: (type: *Type_Info_Struct, builder: *String_Builder, path: string = "") {
|
|
for type.members {
|
|
if should_serialize(type, it) {
|
|
new_path : string;
|
|
if path.count == 0 {
|
|
new_path = it.name;
|
|
} else {
|
|
new_path = tprint("%1.%2", path, it.name);
|
|
}
|
|
|
|
type : Type_Info_Tag;
|
|
if it.type.type == .VARIANT {
|
|
info_variant := cast(*Type_Info_Variant)it.type;
|
|
type = info_variant.variant_of.type;
|
|
} else {
|
|
type = it.type.type;
|
|
}
|
|
|
|
if type == {
|
|
case .STRUCT; {
|
|
info_struct := cast(*Type_Info_Struct) it.type;
|
|
generate_member_copy(info_struct, builder, new_path);
|
|
}
|
|
case .BOOL; #through;
|
|
case .FLOAT; #through;
|
|
case .ENUM; #through;
|
|
case .INTEGER; {
|
|
print_to_builder(builder, "\tcopy.% = e.%;\n", new_path, new_path);
|
|
}
|
|
case .STRING; {
|
|
print_to_builder(builder, "\tcopy.% = copy_string(e.%,,allocator=e.scene.allocator);\n", new_path, new_path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
generate_ui_procedure_for_entity :: (code_struct: *Code_Struct) {
|
|
name := code_struct.defined_type.name;
|
|
|
|
// Serialize
|
|
ui : String_Builder;
|
|
print_to_builder(*ui, "#if EDITOR {");
|
|
print_to_builder(*ui, "entity_ui_proc :: (e: *%) {\n", name);
|
|
|
|
generate_member_ui(code_struct.defined_type, *ui);
|
|
|
|
print_to_builder(*ui, "}\n");
|
|
print_to_builder(*ui, "}");
|
|
|
|
array_add(*entity_serialize_proc_string, builder_to_string(*ui));
|
|
}
|
|
|
|
generate_ui_procedure_for_entity_imgui :: (code_struct: *Code_Struct) {
|
|
name := code_struct.defined_type.name;
|
|
|
|
// Serialize
|
|
ui : String_Builder;
|
|
print_to_builder(*ui, "#if EDITOR {");
|
|
print_to_builder(*ui, "entity_ui_proc_imgui :: (e: *%) {\n", name);
|
|
|
|
generate_member_ui_imgui(code_struct.defined_type, *ui);
|
|
|
|
if wants_custom_ui(code_struct) {
|
|
print_to_builder(*ui, "custom_ui(e);\n");
|
|
}
|
|
|
|
print_to_builder(*ui, "}\n");
|
|
print_to_builder(*ui, "}");
|
|
|
|
array_add(*entity_serialize_proc_string, builder_to_string(*ui));
|
|
}
|
|
|
|
generate_serialize_procedure_for_entity :: (code_struct: *Code_Struct) {
|
|
name := code_struct.defined_type.name;
|
|
|
|
// Serialize
|
|
serialize : String_Builder;
|
|
print_to_builder(*serialize, "serialize_entity :: (e: *%, builder: *String_Builder) {\n", name);
|
|
print_to_builder(*serialize, "\tprint_to_builder(builder, \"type: %\\n\");\n", name);
|
|
|
|
generate_member_serialization(code_struct.defined_type, *serialize);
|
|
|
|
print_to_builder(*serialize, "}\n");
|
|
|
|
array_add(*entity_serialize_proc_string, builder_to_string(*serialize));
|
|
|
|
// Deserialize
|
|
deserialize : String_Builder;
|
|
print_to_builder(*deserialize, "deserialize_entity :: (scene: *Scene, lines: [] string, e: *%) {\n", name);
|
|
print_to_builder(*deserialize, "\tfor line: lines {\n");
|
|
print_to_builder(*deserialize, "\t\tvalues := split(line, \":\",, temp);\n");
|
|
print_to_builder(*deserialize, "\t\tif values.count == 2 {\n");
|
|
print_to_builder(*deserialize, "\t\t\tif trim(values[0], \" \",, temp) == {\n");
|
|
|
|
generate_member_deserialization(code_struct.defined_type, *deserialize);
|
|
|
|
print_to_builder(*deserialize, "\t\t\t}\n");
|
|
print_to_builder(*deserialize, "\t\t}\n");
|
|
print_to_builder(*deserialize, "\t}\n");
|
|
|
|
print_to_builder(*deserialize, "}\n");
|
|
array_add(*entity_serialize_proc_string, builder_to_string(*deserialize));
|
|
|
|
// Duplicate
|
|
duplicate : String_Builder;
|
|
print_to_builder(*duplicate, "duplicate_entity :: (e: *%) -> *% {\n", name, name);
|
|
print_to_builder(*duplicate, "\tcopy := new_%(init=false);\n", to_lower_copy(name,,allocator=temp));
|
|
|
|
generate_member_copy(code_struct.defined_type, *duplicate);
|
|
print_to_builder(*duplicate, "\tinit_entity(copy);\n");
|
|
|
|
print_to_builder(*duplicate, "\treturn copy;\n");
|
|
print_to_builder(*duplicate, "}\n");
|
|
|
|
array_add(*entity_serialize_proc_string, builder_to_string(*duplicate));
|
|
}
|
|
|
|
note_struct :: (code_struct: *Code_Struct) {
|
|
if is_subclass_of(code_struct.defined_type, "Entity") {
|
|
name := code_struct.defined_type.name;
|
|
|
|
added := array_add_if_unique(*entity_type_names, name);
|
|
if added {
|
|
print("Detected entity '%'.\n", name);
|
|
|
|
generate_serialize_procedure_for_entity(code_struct);
|
|
generate_ui_procedure_for_entity_imgui(code_struct);
|
|
}
|
|
}
|
|
}
|
|
|
|
wants_custom_ui :: (code_struct: *Code_Struct) -> bool {
|
|
for note: code_struct.defined_type.notes {
|
|
if to_lower_copy(note,, allocator = temp) == "customui" {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
message: *Message_Import;
|
|
|
|
message_loop :: () {
|
|
while true {
|
|
message := compiler_wait_for_message();
|
|
// We ignore most message types. We mainly want to know about code that has been typechecked,
|
|
// so that we can look for the data structures we care about; or, when all the code is done
|
|
// being typechecked, we want to generate our new code.
|
|
if message.kind == {
|
|
case .IMPORT; {
|
|
import := cast(*Message_Import)message;
|
|
if import.module_name == "Coven" message = import;
|
|
}
|
|
case .TYPECHECKED;
|
|
typechecked := cast(*Message_Typechecked) message;
|
|
for typechecked.structs {
|
|
note_struct(it.expression);
|
|
}
|
|
|
|
for typechecked.procedure_headers {
|
|
if it.expression.name == "deinit_entity" {
|
|
if it.expression.arguments.count > 0 {
|
|
//struct_type := cast(*Type_Info_Struct)it.expression.arguments[0].type_inst.pointer_to;
|
|
struct_type := cast(*Type_Info_Struct)it.expression.arguments[0].type_inst.pointer_to.result;
|
|
array_add(*deinit_entity_procs, struct_type.name);
|
|
}
|
|
} else {
|
|
for note: it.expression.notes {
|
|
if to_lower_copy(note.text,, temp) == {
|
|
case "newentity"; {
|
|
array_add(*new_entity_procs, it.expression.name);
|
|
}
|
|
case "engineinit"; {
|
|
array_add(*engine_init_procs, it.expression.name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
case .PHASE;
|
|
phase := cast(*Message_Phase) message;
|
|
if phase.phase == .TYPECHECKED_ALL_WE_CAN {
|
|
if !generated_code {
|
|
generate_code(message.workspace);
|
|
generated_code = true;
|
|
}
|
|
}
|
|
|
|
case .COMPLETE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
generate_code :: (w: Workspace) {
|
|
// Let's sort the entity_type_names alphabetically, which is nice, but also
|
|
// gives us a reproducible build order, since our metaprogram may see these
|
|
// entities in any order:
|
|
|
|
quick_sort(entity_type_names, compare_strings);
|
|
|
|
// A piece of the code we want to generate is a comma-separated string of entity type
|
|
// names, so, let's make that using join():
|
|
type_names := join(..entity_type_names, ", ");
|
|
|
|
// We will also want to generate entity_storage_string, which contains a bunch of Bucket_Arrays
|
|
// for allocating each entity type. We might be able to do this with join() again, but
|
|
// we'll use String_Builder here, since that is a good example of how to do arbitrarily complex things:
|
|
|
|
entity_storage_string: string;
|
|
{
|
|
builder: String_Builder;
|
|
|
|
for entity_type_names {
|
|
// Let's say it's always 20 items per bucket,
|
|
// unless it's Player, in which case we want 64:
|
|
|
|
items_per_bucket := 20;
|
|
if it == "Player" items_per_bucket = 64;
|
|
|
|
print_to_builder(*builder, " _%1: Bucket_Array(%1, %2, true);\n", it, items_per_bucket);
|
|
}
|
|
|
|
entity_storage_string = builder_to_string(*builder);
|
|
}
|
|
|
|
{
|
|
builder: String_Builder;
|
|
|
|
for entity_type_names {
|
|
print_to_builder(*builder, "scene.by_type._%1.allocator = scene.allocator;", it);
|
|
}
|
|
|
|
build_string := sprint(INIT_SCENE, builder_to_string(*builder));
|
|
add_build_string(build_string, w, message);
|
|
}
|
|
|
|
{
|
|
builder: String_Builder;
|
|
|
|
for engine_init_procs {
|
|
print_to_builder(*builder, "%();", it);
|
|
}
|
|
|
|
build_string := sprint(RUN_ENGINE_INIT, builder_to_string(*builder));
|
|
add_build_string(build_string, w, message);
|
|
}
|
|
|
|
{
|
|
builder: String_Builder;
|
|
|
|
for entity_type_names {
|
|
lower := to_lower_copy (it,, allocator=temp);
|
|
print_to_builder(*builder, "if ImGui.Button(\"New %1\") return new_%2();", it, lower);
|
|
}
|
|
|
|
for new_entity_procs {
|
|
print_to_builder(*builder, "if ImGui.Button(\"%1\") return %1();", it);
|
|
}
|
|
|
|
build_string := sprint(EDITOR_UI_ENTITY_CREATION, builder_to_string(*builder));
|
|
add_build_string(build_string, w);
|
|
}
|
|
|
|
{
|
|
builder: String_Builder;
|
|
|
|
for entity_type_names {
|
|
print_to_builder(*builder, "\tcase %1; bucket_array_remove(*e.scene.by_type._%1, e._locator);", it);
|
|
}
|
|
|
|
build_string := sprint(DELETE_ENTITY, builder_to_string(*builder));
|
|
add_build_string(build_string, w);
|
|
}
|
|
|
|
{
|
|
builder: String_Builder;
|
|
|
|
for entity_type_names {
|
|
print_to_builder(*builder, "\tcase %1; serialize_entity(cast(*%1)e, *builder);\n", it);
|
|
}
|
|
|
|
build_string := sprint(SERIALIZE_ENTITY, builder_to_string(*builder));
|
|
add_build_string(build_string, w);
|
|
}
|
|
|
|
{
|
|
builder: String_Builder;
|
|
|
|
for entity_type_names {
|
|
print_to_builder(*builder, "\tcase %1; return duplicate_entity(cast(*%1)e);\n", it);
|
|
}
|
|
|
|
build_string := sprint(DUPLICATE_ENTITY, builder_to_string(*builder));
|
|
add_build_string(build_string, w);
|
|
}
|
|
|
|
{
|
|
builder: String_Builder;
|
|
|
|
for entity_type_names {
|
|
print_to_builder(*builder, "\tcase \"%1\"; p, locator := find_and_occupy_empty_slot(*scene.by_type._%1); p._locator = locator; e = p; register_entity(scene, p, id); deserialize_entity(scene, lines, cast(*%1)e); init_entity(p); update_matrix(*e.transform);\n", it);
|
|
}
|
|
|
|
build_string := sprint(DESERIALIZE_ENTITY, builder_to_string(*builder));
|
|
add_build_string(build_string, w);
|
|
}
|
|
|
|
{
|
|
builder: String_Builder;
|
|
|
|
for entity_type_names {
|
|
print_to_builder(*builder, "\tcase %1; entity_ui_proc_imgui(cast(*%1)e);\n", it);
|
|
}
|
|
|
|
build_string := sprint(ENTITY_UI, builder_to_string(*builder));
|
|
add_build_string(build_string, w);
|
|
}
|
|
|
|
{
|
|
if deinit_entity_procs.count > 0 {
|
|
builder: String_Builder;
|
|
|
|
for deinit_entity_procs {
|
|
print_to_builder(*builder, "\tcase %1; deinit_entity(cast(*%1)e);\n", it);
|
|
}
|
|
|
|
build_string := sprint(DEINIT_ENTITY, builder_to_string(*builder));
|
|
add_build_string(build_string, w);
|
|
} else {
|
|
add_build_string("call_correct_deinit_entity :: (e: *Entity) {}", w);
|
|
add_build_string("deinit_entity :: () {}", w);
|
|
}
|
|
}
|
|
|
|
{
|
|
builder: String_Builder;
|
|
|
|
for entity_type_names {
|
|
print_to_builder(*builder, "new_%1 :: (scene: *Scene = null, init: bool = true) -> *%2 { _scene := scene;\nif _scene == null { \n_scene = engine.current_scene; }\np, locator := find_and_occupy_empty_slot(*_scene.by_type._%2); p._locator = locator; register_entity(_scene, p); p.transform = create_identity_transform(); if init { init_entity(p); } return p; }\n", to_lower_copy(it,, allocator=temp), it);
|
|
}
|
|
|
|
add_build_string(builder_to_string(*builder), w);
|
|
}
|
|
|
|
for entity_serialize_proc_string {
|
|
add_build_string(it, w);
|
|
}
|
|
|
|
|
|
// We fill out INSERTION_STRING to create the code we want to insert:
|
|
build_string := sprint(INSERTION_STRING, entity_type_names.count, type_names, entity_storage_string);
|
|
|
|
// Add this string to the target program:
|
|
add_build_string(build_string, w);
|
|
add_build_string(PLACEHOLDER, w);
|
|
|
|
// We'll print out the added code just to show at compile-time what we are doing:
|
|
//print("Adding build string:\n%\n", build_string);
|
|
}
|
|
|
|
generated_code := false;
|
|
entity_type_names: [..] string;
|
|
new_entity_procs: [..] string;
|
|
engine_init_procs: [..] string;
|
|
deinit_entity_procs: [..] string;
|
|
|
|
// INSERTION_STRING represents the code we want to add to the target program.
|
|
// We'll use print to insert useful things where the % markers are.
|
|
INSERTION_STRING :: #string DONE
|
|
|
|
// NUM_ENTITY_TYPES tells the target program how many entity types there are.
|
|
NUM_ENTITY_TYPES :: %1;
|
|
|
|
// entity_types is an array containing all the entity types.
|
|
entity_types : [%1] Type : .[ %2 ];
|
|
|
|
Entity_Storage :: struct {
|
|
%3
|
|
}
|
|
DONE
|
|
|
|
DELETE_ENTITY :: #string,\% DONE
|
|
delete_entity :: (e: *Entity) {
|
|
// Delete the file too
|
|
path := tprint("../assets/scenes/\%/\%.ent", e.scene.name, e.id);
|
|
File.file_delete(path);
|
|
|
|
destroy_entity(e);
|
|
|
|
if e.type == {
|
|
%1
|
|
}
|
|
}
|
|
|
|
//#import "File";
|
|
|
|
DONE
|
|
|
|
SERIALIZE_ENTITY :: #string,\% DONE
|
|
serialize_entity :: (e: *Entity, path: string) {
|
|
builder: String_Builder;
|
|
builder.allocator = temp;
|
|
|
|
if e.type == {
|
|
%1
|
|
}
|
|
|
|
File.write_entire_file(tprint("\%/\%.ent", path, e.id), builder_to_string(*builder));
|
|
}
|
|
DONE
|
|
|
|
DESERIALIZE_ENTITY :: #string DONE
|
|
deserialize_entity :: (scene: *Scene, id: Entity_Id, path: string) -> *Entity {
|
|
content := File.read_entire_file(path);
|
|
if content.count == 0 return null;
|
|
|
|
lines := split(content, "\n",, temp);
|
|
first_line := split(lines[0], ":",, temp);
|
|
|
|
if first_line.count != 2 return null;
|
|
|
|
e: *Entity;
|
|
|
|
type := trim(first_line[1], " \n\r");
|
|
|
|
if type == {
|
|
%1
|
|
}
|
|
|
|
free(content);
|
|
|
|
return e;
|
|
}
|
|
|
|
DONE
|
|
DUPLICATE_ENTITY :: #string DONE
|
|
duplicate_entity :: (e: *Entity) -> *Entity {
|
|
if e.type == {
|
|
%1
|
|
}
|
|
|
|
return e;
|
|
}
|
|
DONE
|
|
|
|
ENTITY_UI :: #string DONE
|
|
#if EDITOR {
|
|
entity_ui :: (e: *Entity) {
|
|
if e.type == {
|
|
%1
|
|
}
|
|
}
|
|
}
|
|
DONE
|
|
|
|
DEINIT_ENTITY :: #string DONE
|
|
#if EDITOR {
|
|
call_correct_deinit_entity :: (e: *Entity) {
|
|
if e.type == {
|
|
%1
|
|
}
|
|
}
|
|
}
|
|
DONE
|
|
|
|
INIT_SCENE :: #string DONE
|
|
init_scene :: (scene: *Scene) {
|
|
%1
|
|
}
|
|
DONE
|
|
|
|
RUN_ENGINE_INIT :: #string DONE
|
|
run_engine_inits :: () {
|
|
%1
|
|
}
|
|
DONE
|
|
|
|
|
|
EDITOR_UI_ENTITY_CREATION :: #string DONE
|
|
#if EDITOR {
|
|
editor_ui_entity_creation :: () -> *Entity {
|
|
%1
|
|
|
|
return null;
|
|
}
|
|
}
|
|
DONE
|
|
|
|
PLACEHOLDER :: #string DONE
|
|
#poke_name Coven init_scene;
|
|
#poke_name Coven Entity_Storage;
|
|
#poke_name Coven delete_entity;
|
|
#poke_name Coven deserialize_entity;
|
|
#poke_name Coven deinit_entity;
|
|
#poke_name Coven serialize_entity;
|
|
#poke_name Coven new_mesh_entity;
|
|
#poke_name Coven call_correct_deinit_entity;
|
|
#poke_name Coven run_engine_inits;
|
|
|
|
#if EDITOR {
|
|
#poke_name Coven editor_ui_entity_creation;
|
|
#poke_name Coven entity_ui;
|
|
#poke_name Coven duplicate_entity;
|
|
}
|
|
DONE
|