module app; import bindbc.sdl; import cimgui.cimgui; import game_core.basic; import game_core.job_updater; import bubel.ecs.core; import bubel.ecs.manager; import bubel.ecs.std; import ecs_utils.gfx.renderer; import ecs_utils.imgui_bind; import ecs_utils.imgui_styles; import ecs_utils.math.vector; import ecs_utils.utils; import glad.gl.gl; import glad.gl.gles2; import glad.gl.loader; import gui.manager; import gui.tool_circle; extern (C) : enum Tool { entity_spawner, component_manipulator, selector } __gshared const (char)*[3] tool_strings = ["Entity spawner", "Component manipulator", "Selector"]; struct Mouse { vec2 position; bool left, right, middle; } struct DemoCallbacks { void function() register; void function() initialize; void function() deinitialize; bool function() loop; void function(SDL_Event*) event; const (char)* tips; } struct Launcher { ECSJobUpdater* job_updater; GUIManager* gui_manager; ImGuiContext* context; SDL_Window* window; SDL_GLContext gl_context; EntityManager* manager; /*bool function() loop; void function() end; void function(SDL_Event*) event;*/ //void function(vec2, Tool, int, bool) tool; float scalling; ivec2 window_size = ivec2(1024,768); Renderer renderer; ubyte[] keys; uint style = 3; uint entities_count; bool multithreading; int threads = 1; ulong timer_freq; double delta_time; uint fps; vec2 render_position; bool play = true; Tool used_tool; int tool_size = 100; float tool_repeat = 0; float repeat_time = 0; bool tool_show = true; bool override_ = true; bool tool_mode = true; ToolCircle* tool_circle; bool show_filtered; EntityID selected_entity; bool swap_interval = true; float windows_alpha = 0.75; //const (char)* tips; bool show_stat_wnd = true; bool show_tips = true; bool show_demo_wnd = true; bool show_tools_wnd = true; bool show_virtual_keys_wnd = false; bool show_profile_wnd = true; int plot_index; PlotStruct[] plot_values; Mouse mouse; struct PlotStruct { float delta_time = 0; float loop_time = 0; float draw_time = 0; } double deltaTime() { return delta_time * play; } DemoCallbacks demo; void switchDemo(DemoCallbacks callbacks)//void function() start, bool function() loop, void function() end, void function(SDL_Event*) event, const (char)* tips) { show_tips = true; gui_manager.clear(); //launcher.ent manager.begin(); manager.update("clean"); manager.end(); if(this.demo.deinitialize)this.demo.deinitialize(); foreach(ref system; manager.systems) { if(system.id != becsID!CountSystem && system.id != becsID!CleanSystem)system.disable(); } /*launcher.manager.getSystem(becsID!CountSystem).enable(); launcher.manager.getSystem(becsID!CleanSystem).enable();//*/ if(callbacks.register)callbacks.register(); if(callbacks.initialize)callbacks.initialize(); demo = callbacks; /*this.loop = loop; this.end = end; this.event = event; this.tips = tips;*/ //this.tool = tool; } bool filterEntity(ref const Entity entity) { EntityMeta meta = entity.getMeta(); foreach(id;gui_manager.filter_list) { if(!meta.hasComponent(id))return false; } if(used_tool == Tool.component_manipulator) { if(!meta.hasComponent(gui_manager.getSelectedComponent().component_id))return false; } return true; } void processTool(vec2 position, bool mode) { static struct Iterator { float size2; vec2 position; ComponentRef[] add_comps; ushort[] rem_comps; ushort[] filter; float distance = float.max; bool filterEntity(ref const Entity entity) { EntityMeta meta = entity.getMeta(); foreach(id;filter) { if(!meta.hasComponent(id))return false; } return true; } void removeEntity(IteratorSystem.EntitiesData data) { if(!filterEntity(data.entity[0]))return; foreach(i;0..data.length) { vec2 rel_vec = data.location[i] - position; float length = rel_vec.x * rel_vec.x + rel_vec.y * rel_vec.y; if(length < size2)gEntityManager.removeEntity(data.entity[i].id); } } void addComponent(IteratorSystem.EntitiesData data) { if(!filterEntity(data.entity[0]))return; foreach(i;0..data.length) { vec2 rel_vec = data.location[i] - position; float length = rel_vec.x * rel_vec.x + rel_vec.y * rel_vec.y; if(length < size2)gEntityManager.addComponents(data.entity[i].id, add_comps); } } void overrideComponent(IteratorSystem.EntitiesData data) { if(!filterEntity(data.entity[0]))return; foreach(i;0..data.length) { vec2 rel_vec = data.location[i] - position; float length = rel_vec.x * rel_vec.x + rel_vec.y * rel_vec.y; if(length < size2) { gEntityManager.removeComponents(data.entity[i].id, rem_comps); gEntityManager.addComponents(data.entity[i].id, add_comps); } } } void removeComponent(IteratorSystem.EntitiesData data) { if(!filterEntity(data.entity[0]))return; foreach(i;0..data.length) { vec2 rel_vec = data.location[i] - position; float length = rel_vec.x * rel_vec.x + rel_vec.y * rel_vec.y; if(length < size2)gEntityManager.removeComponents(data.entity[i].id, rem_comps); } } void selectEntity(IteratorSystem.EntitiesData data) { if(!filterEntity(data.entity[0]))return; foreach(i;0..data.length) { vec2 rel_vec = data.location[i] - position; float length = rel_vec.x * rel_vec.x + rel_vec.y * rel_vec.y; if(length < distance) { distance = length; launcher.selected_entity = data.entity[i].id; } } } } float half_size = tool_size * 0.5; float size2 = half_size * half_size; Iterator iterator; iterator.size2 = size2; iterator.position = position; iterator.filter = gui_manager.filter_list[]; switch(used_tool) { case Tool.entity_spawner: if(mode) { if(gui_manager.templates.length == 0)return; EntityTemplate* tmpl = gui_manager.getSelectedTemplate(); CLocation* location = tmpl.getComponent!CLocation; if(location) { position += randomCircularSample() * half_size; //if(position.y < 16)position.y = 16; //else if(position.y > 299)position.y = 299; *location = position; } manager.addEntity(tmpl); } else { manager.callEntitiesFunction!IteratorSystem(&iterator.removeEntity); } break; case Tool.component_manipulator: { if(gui_manager.components.length == 0)return; if(mode) { ComponentRef[1] comps = [gui_manager.getSelectedComponent()]; iterator.add_comps = comps; if(launcher.override_) { ushort[1] rcomps = [gui_manager.getSelectedComponent().component_id]; iterator.rem_comps = rcomps; manager.callEntitiesFunction!IteratorSystem(&iterator.overrideComponent); } else manager.callEntitiesFunction!IteratorSystem(&iterator.addComponent); } else { ushort[1] comps = [gui_manager.getSelectedComponent().component_id]; iterator.rem_comps = comps; manager.callEntitiesFunction!IteratorSystem(&iterator.removeComponent); } } break; case Tool.selector: iterator.distance = size2; manager.callEntitiesFunction!IteratorSystem(&iterator.selectEntity); break; default: break; } } bool getKeyState(SDL_Scancode key) { if(keys[key])return true; else return false; } void setStyle(uint index) { style = index; .setStyle(index); } double getTime() { ulong local_freq = timer_freq / 1000_000; //return cast(double)(SDL_GetPerformanceCounter() & 0x00FFFFFF) / timer_freq; if(local_freq == 0)return cast(double)(SDL_GetPerformanceCounter() / timer_freq * 1000); else return cast(double)(SDL_GetPerformanceCounter() / local_freq) / 1000; } } __gshared Launcher launcher; struct CountSystem { mixin ECS.System!1; struct EntitiesData { uint length; const (Entity)[] entity; } bool onBegin() { launcher.entities_count = 0; return true; } void onUpdate(EntitiesData data) { launcher.entities_count += data.length; } } struct CleanSystem { mixin ECS.System!64; struct EntitiesData { uint length; Entity[] entities; } void onUpdate(EntitiesData data) { foreach(i; 0..data.length) { launcher.manager.removeEntity(data.entities[i].id); } } } struct IteratorSystem { mixin ECS.System!1; struct EntitiesData { uint length; const (Entity)[] entity; CLocation[] location; } bool onBegin() { return false; } void onUpdate(EntitiesData) { } } void mainLoop(void* arg) { __gshared double time = 0; launcher.delta_time = launcher.getTime() - time; time = launcher.getTime(); if(launcher.delta_time > 1000)launcher.delta_time = 1000; __gshared uint temp_fps = 0; __gshared double fps_time = 0; temp_fps++; fps_time += launcher.delta_time; while(fps_time > 1000) { fps_time -= 1000; launcher.fps = temp_fps; temp_fps = 0; } SDL_Event event; while (SDL_PollEvent(&event)) { ImGui_ImplSDL2_ProcessEvent(&event); if(launcher.demo.event)launcher.demo.event(&event); if (event.type == SDL_QUIT || (event.type == SDL_KEYDOWN && event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)) { quit(); *cast(bool*)arg = false; return; } else if(event.type == SDL_KEYDOWN) { if(event.key.state) { if(SDL_GetModState() & KMOD_CTRL) { switch(event.key.keysym.scancode) { case SDL_SCANCODE_1:launcher.used_tool=Tool.entity_spawner;break; case SDL_SCANCODE_2:launcher.used_tool=Tool.component_manipulator;break; case SDL_SCANCODE_3:launcher.used_tool=Tool.selector;break; default:break; } } else { switch(event.key.keysym.scancode) { case SDL_SCANCODE_1:break; case SDL_SCANCODE_2:break; case SDL_SCANCODE_3:break; case SDL_SCANCODE_4:break; default:break; } } } } else if(event.type == SDL_WINDOWEVENT) { switch(event.window.event) { case SDL_WINDOWEVENT_RESIZED: //launcher.renderer.resize(ivec2(event.window.data1,event.window.data2)); launcher.window_size = ivec2(event.window.data1,event.window.data2); break; case SDL_WINDOWEVENT_SIZE_CHANGED: //launcher.renderer.resize(ivec2(event.window.data1,event.window.data2)); launcher.window_size = ivec2(event.window.data1,event.window.data2); break; default:break; } } else if(event.type == SDL_MOUSEBUTTONDOWN) { switch(event.button.button) { case SDL_BUTTON_LEFT:launcher.mouse.left = true;break; case SDL_BUTTON_RIGHT:launcher.mouse.right = true;break; case SDL_BUTTON_MIDDLE:launcher.mouse.middle = true;break; default:break; } if(!igIsAnyItemHovered())igSetWindowFocus(); if(!igIsWindowHovered(ImGuiHoveredFlags_AnyWindow) && !igIsWindowFocused(ImGuiFocusedFlags_AnyWindow)) { launcher.repeat_time = 0; if(event.button.button == SDL_BUTTON_LEFT)launcher.processTool(vec2(event.button.x, launcher.window_size.y - event.button.y) * launcher.scalling - launcher.render_position,launcher.tool_mode); else if(event.button.button == SDL_BUTTON_RIGHT)launcher.processTool(vec2(event.button.x, launcher.window_size.y - event.button.y) * launcher.scalling - launcher.render_position,!launcher.tool_mode); } } else if(event.type == SDL_MOUSEBUTTONUP) { switch(event.button.button) { case SDL_BUTTON_LEFT:launcher.mouse.left = false;break; case SDL_BUTTON_RIGHT:launcher.mouse.right = false;break; case SDL_BUTTON_MIDDLE:launcher.mouse.middle = false;break; default:break; } } else if(event.type == SDL_MOUSEMOTION) { launcher.mouse.position = vec2(event.motion.x, launcher.window_size.y - event.motion.y); }else if(event.type == SDL_MOUSEWHEEL) { if(!igIsAnyItemHovered() && !igIsWindowHovered(ImGuiHoveredFlags_AnyWindow)) { if(SDL_GetModState() & KMOD_CTRL) { float sign = 1; if(event.wheel.y < 0)sign = -1; float val = /*sign * event.wheel.y */ launcher.tool_repeat * 0.25; if(val < 0.1)val = 0.1; launcher.tool_repeat -= sign * val; if(launcher.tool_repeat < 0)launcher.tool_repeat = 0; else if(launcher.tool_repeat > 1000)launcher.tool_repeat = 1000; } else if(SDL_GetModState() & KMOD_SHIFT) { int sign = 1; if(event.wheel.y < 0)sign = -1; switch(launcher.used_tool) { case Tool.entity_spawner: launcher.gui_manager.selectTemplate(launcher.gui_manager.selected_template-sign); break; case Tool.component_manipulator: launcher.gui_manager.selectComponent(launcher.gui_manager.selected_component-sign); break; default:break; } } else { int sign = 1; if(event.wheel.y < 0)sign = -1; int val = /*sign * event.wheel.y */ launcher.tool_size / 4; if(val < 1)val = 1; launcher.tool_size -= sign * val; if(launcher.tool_size < 1)launcher.tool_size = 1; else if(launcher.tool_size > 256)launcher.tool_size = 256; } } } } if(launcher.tool_repeat != 0 && (launcher.mouse.left || launcher.mouse.right) && !igIsWindowHovered(ImGuiHoveredFlags_AnyWindow) && !igIsWindowFocused(ImGuiFocusedFlags_AnyWindow)) { bool mode = launcher.tool_mode; if(launcher.mouse.right)mode = !mode; float range = 500.0 / cast(float)launcher.tool_repeat; launcher.repeat_time += launcher.delta_time; if(launcher.used_tool != Tool.entity_spawner || !mode) { if(launcher.repeat_time > range)launcher.processTool((launcher.mouse.position*launcher.scalling)-launcher.render_position, mode); while(launcher.repeat_time > range)launcher.repeat_time -= range; } else { while(launcher.repeat_time > range) { launcher.repeat_time -= range; launcher.processTool((launcher.mouse.position*launcher.scalling)-launcher.render_position, mode); } } } version(WebAssembly) { ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplSDL2_NewFrame(launcher.window); } else { //ImGuiImplOpenGL2NewFrame(); ImGui_ImplOpenGL3_NewFrame(); ImGuiImplSDL2NewFrame(launcher.window); } igNewFrame(); if(igBeginMainMenuBar()) { if(igBeginMenu("File",true)) { if(igMenuItemBool("Close","esc",false,true)) { quit(); *cast(bool*)arg = false; return; } igEndMenu(); } if(igBeginMenu("Demos",true)) { if(igMenuItemBool("Simpe",null,false,true)) { import demos.simple; launcher.switchDemo(getSimpleDemo());//&simpleStart,&simpleLoop,&simpleEnd,&simpleEvent,Simple.tips); } if(igMenuItemBool("Snake",null,false,true)) { import demos.snake; launcher.switchDemo(getSnakeDemo());//&snakeStart,&snakeLoop,&snakeEnd,&snakeEvent,Snake.tips); } if(igMenuItemBool("Space Invaders",null,false,true)) { import demos.space_invaders; launcher.switchDemo(getSpaceInvadersDemo());//&spaceInvadersStart,&spaceInvadersLoop,&spaceInvadersEnd,&spaceInvadersEvent,SpaceInvaders.tips); } if(igMenuItemBool("Particles",null,false,true)) { import demos.particles; launcher.switchDemo(getParticlesDemo());//&particlesStart,&particlesLoop,&particlesEnd,&particlesEvent,ParticlesDemo.tips); } if(igMenuItemBool("Brick Breaker",null,false,true)) { import demos.brick_breaker; launcher.switchDemo(getBrickBreakerDemo());//&particlesStart,&particlesLoop,&particlesEnd,&particlesEvent,ParticlesDemo.tips); } if(igMenuItemBool("Sandbox",null,false,true)) { import demos.sandbox; launcher.switchDemo(getSanboxDemo());//&particlesStart,&particlesLoop,&particlesEnd,&particlesEvent,ParticlesDemo.tips); } igEndMenu(); } if(igBeginMenu("Options",true)) { if(igMenuItemBool("VSync", null, launcher.swap_interval, true)) { launcher.swap_interval = !launcher.swap_interval; SDL_GL_SetSwapInterval(launcher.swap_interval); } if(igMenuItemBool("Multithreading", null, launcher.multithreading, true)) { launcher.multithreading = !launcher.multithreading; if(launcher.multithreading) { launcher.job_updater.pool.setThreadsNum(launcher.threads); } else { launcher.job_updater.pool.setThreadsNum(1); } } igSetNextItemWidth(0); igLabelText("Threads:",null); igSameLine(0,4); if(igSliderInt("##Threads",&launcher.threads, 1, 32, null))//"Multithreading", null, launcher.multithreading, true)) { launcher.job_updater.pool.setThreadsNum(launcher.threads); //launcher.threads = !launcher.multithreading; } if(igBeginMenu("Show",true)) { igMenuItemBoolPtr("Statistics",null,&launcher.show_stat_wnd,true); igMenuItemBoolPtr("Demo",null,&launcher.show_demo_wnd,true); igMenuItemBoolPtr("Tips",null,&launcher.show_tips,true); igMenuItemBoolPtr("Virual keys",null,&launcher.show_virtual_keys_wnd,true); igMenuItemBoolPtr("Profile",null,&launcher.show_profile_wnd,true); igEndMenu(); } if(igBeginMenu("Style",true)) { if(igMenuItemBool("Classic",null,launcher.style == 0,true)) { launcher.setStyle(0); } else if(igMenuItemBool("Dark",null,launcher.style == 1,true)) { launcher.setStyle(1); } else if(igMenuItemBool("Light",null,launcher.style == 2,true)) { launcher.setStyle(2); } else if(igMenuItemBool("Default",null,launcher.style == 3,true)) { launcher.setStyle(3); } igEndMenu(); } igEndMenu(); } igEndMainMenuBar(); } if(launcher.show_virtual_keys_wnd) { igSetNextWindowPos(ImVec2(launcher.window_size.x - 400, launcher.window_size.y - 200), ImGuiCond_Once, ImVec2(0,0)); igSetNextWindowSize(ImVec2(64*4+128, 168), ImGuiCond_Once); igSetNextWindowBgAlpha(launcher.windows_alpha); ImGuiStyle * style = igGetStyle(); style.Colors[ImGuiCol_Button].w = 0.8; if(igBegin("Virtual keys",&launcher.show_virtual_keys_wnd, 0)) { igColumns(2,null,0); igSetColumnWidth(0,64*3+16); igIndent(64+4); if(igButton("W",ImVec2(64,64)) || igIsItemDeactivated()) { launcher.keys[SDL_SCANCODE_W] = false; } else if(igIsItemActive()) { launcher.keys[SDL_SCANCODE_W] = true; } igUnindent(64+4); if(igButton("A",ImVec2(64,64)) || igIsItemDeactivated()) { launcher.keys[SDL_SCANCODE_A] = false; } else if(igIsItemActive()) { launcher.keys[SDL_SCANCODE_A] = true; } igSameLine(0,4); if(igButton("S",ImVec2(64,64)) || igIsItemDeactivated()) { launcher.keys[SDL_SCANCODE_S] = false; } else if(igIsItemActive()) { launcher.keys[SDL_SCANCODE_S] = true; } igSameLine(0,4); if(igButton("D",ImVec2(64,64)) || igIsItemDeactivated()) { launcher.keys[SDL_SCANCODE_D] = false; } else if(igIsItemActive()) { launcher.keys[SDL_SCANCODE_D] = true; } igNextColumn(); if(igButton("Space",ImVec2(-1,128)) || igIsItemDeactivated()) { launcher.keys[SDL_SCANCODE_SPACE] = false; } else if(igIsItemActive()) { launcher.keys[SDL_SCANCODE_SPACE] = true; } igColumns(1,null,0); } igEnd(); style.Colors[ImGuiCol_Button].w = 1.0; } if(launcher.show_tips) { igSetNextWindowPos(ImVec2(launcher.window_size.x /2 -250, 100), ImGuiCond_Once, ImVec2(0,0)); igSetNextWindowSize(ImVec2(500, -1), ImGuiCond_Once); igSetNextWindowBgAlpha(0.95); if(igBegin("Tips",&launcher.show_tips,ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings)) { // igBeginChild("",ImVec2(0,0),0,0); igTextWrapped(launcher.demo.tips); // igEndChild(); } igEnd(); } if(launcher.show_demo_wnd) { igSetNextWindowPos(ImVec2(launcher.window_size.x - 260, 30), ImGuiCond_Once, ImVec2(0,0)); igSetNextWindowSize(ImVec2(250, 300), ImGuiCond_Once); if(igBegin("Demo",&launcher.show_demo_wnd,0)) { igCheckbox("Play",&launcher.play); ImDrawList* draw_list = igGetWindowDrawList(); igBeginGroup(); launcher.gui_manager.gui(); igEndGroup(); ImDrawList_AddRect(draw_list, igGetItemRectMin(), ImVec2(igGetWindowPos().x+igGetWindowWidth()-2,igGetItemRectMax().y), igColorConvertFloat4ToU32(ImVec4(0.4,0.4,0.4,0.4)), 4, ImDrawCornerFlags_All, 1); //ImDrawList_AddRect(draw_list, igGetItemRectMin(), igGetItemRectMax(), igColorConvertFloat4ToU32(ImVec4(0.4,0.4,0.4,0.4)), -1, 0, 1); //igBeginChildFrame(1,ImVec2(0,-1),ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_ChildWindow); //igBeginChild("Tool frame",ImVec2(-1,-1),true,0); // igBeginGroup(); // if(igCollapsingHeader("Tool##ToolHeader", ImGuiTreeNodeFlags_SpanAvailWidth | ImGuiTreeNodeFlags_DefaultOpen)) // { // igIndent(8); /*if(igBeginCombo("Tool",tool_strings[launcher.used_tool],0)) { if(igSelectable("Entity spawner",false,0,ImVec2(0,0))) { launcher.used_tool = Tool.entity_spawner; } if(igSelectable("Component manipulator",false,0,ImVec2(0,0))) { launcher.used_tool = Tool.component_manipulator; } if(igSelectable("Selector",false,0,ImVec2(0,0))) { launcher.used_tool = Tool.selector; } igEndCombo(); }*/ /*if(igSelectable("Entity spawner",false,0,ImVec2(0,0))) { launcher.used_tool = Tool.entity_spawner; } if(igSelectable("Component manipulator",false,0,ImVec2(0,0))) { launcher.used_tool = Tool.component_manipulator; } if(igSelectable("Selector",false,0,ImVec2(0,0))) { launcher.used_tool = Tool.selector; }*/ /*if(igBeginTabBar("Tool",ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { if(igIsItemHovered(0))igSetTooltip("Select tool (CTRL + 1,2,3)"); bool a = 1; //this is used as hack to make CTRL+1/2/3 tab selection possible static Tool prev_tool = Tool.entity_spawner; bool different = prev_tool != launcher.used_tool; Tool tool; if(igBeginTabItem("Entity spawner", &a, launcher.used_tool == Tool.entity_spawner && different ? ImGuiTabItemFlags_SetSelected : 0)) { tool = Tool.entity_spawner; igEndTabItem(); } if(igBeginTabItem("Component manipulator", &a, launcher.used_tool == Tool.component_manipulator && different ? ImGuiTabItemFlags_SetSelected : 0)) { tool = Tool.component_manipulator; igEndTabItem(); } if(igBeginTabItem("Selector", &a, launcher.used_tool == Tool.selector && different ? ImGuiTabItemFlags_SetSelected : 0)) { tool = Tool.selector; igEndTabItem(); } launcher.used_tool = tool; prev_tool = launcher.used_tool; } igEndTabBar(); igCheckbox("Show Tool", &launcher.tool_show); if(igIsItemHovered(0))igSetTooltip("Show/hide graphical tool representation"); igSameLine(0,4); igCheckbox("Show Filtered", &launcher.show_filtered); if(igIsItemHovered(0))igSetTooltip("Show/hide filtered entities"); if(launcher.used_tool == Tool.component_manipulator) { igCheckbox("Override", &launcher.override_); } //igSelectable("Selectabe",false,ImGuiSelectableFlags_None,ImVec2(0,0)); if(launcher.used_tool != Tool.selector) { if(igRadioButtonBool("Add", launcher.tool_mode))launcher.tool_mode = true; if(igIsItemHovered(0))igSetTooltip("Tool should adding (Entities or components)"); igSameLine(0,4); if(igRadioButtonBool("Remove", !launcher.tool_mode))launcher.tool_mode = false; if(igIsItemHovered(0))igSetTooltip("Tool should removing (Entities or components)"); } igSliderInt("Tool size", &launcher.tool_size, 0, 256, null); igSliderFloat("Tool repeat", &launcher.tool_repeat, 0, 1024, null, 4); launcher.gui_manager.toolGui();*/ // igUnindent(8); // } // igEndGroup(); ImDrawList_AddRect(draw_list, igGetItemRectMin(), ImVec2(igGetWindowPos().x+igGetWindowWidth()-2,igGetItemRectMax().y), igColorConvertFloat4ToU32(ImVec4(0.4,0.4,0.4,0.4)), 2, ImDrawCornerFlags_All, 1); //igBeginGroup(); if(igCollapsingHeader("Filter", ImGuiTreeNodeFlags_SpanAvailWidth)) { igIndent(8); launcher.gui_manager.filterGUI(); igUnindent(8); } //igEndGroup(); //ImDrawList_AddRect(draw_list, igGetItemRectMin(), ImVec2(igGetWindowPos().x+igGetWindowWidth()-2,igGetItemRectMax().y), igColorConvertFloat4ToU32(ImVec4(0.4,0.4,0.4,0.4)), 2, ImDrawCornerFlags_All, 1); //igEndChild(); //igEndChildFrame(); if(igButton("Clear",ImVec2(-1,0))) { launcher.manager.begin(); launcher.manager.update("clean"); launcher.manager.end(); } } igEnd(); } if(launcher.show_tools_wnd) { igSetNextWindowPos(ImVec2(launcher.window_size.x - 300, 340), ImGuiCond_Once, ImVec2(0,0)); igSetNextWindowSize(ImVec2(300, launcher.window_size.y - 370), ImGuiCond_Once); if(igBegin("Tools", &launcher.show_tools_wnd, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) { if(igIsItemHovered(0))igSetTooltip("Select tool (CTRL + 1,2,3)"); if(igBeginTabBar("Tool",ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { bool a = 1; //this is used as hack to make CTRL+1/2/3 tab selection possible static Tool prev_tool = Tool.entity_spawner; bool different = prev_tool != launcher.used_tool; Tool tool; if(igBeginTabItem("Entity spawner", &a, launcher.used_tool == Tool.entity_spawner && different ? ImGuiTabItemFlags_SetSelected : 0)) { tool = Tool.entity_spawner; igEndTabItem(); } if(igBeginTabItem("Component manipulator", &a, launcher.used_tool == Tool.component_manipulator && different ? ImGuiTabItemFlags_SetSelected : 0)) { tool = Tool.component_manipulator; igEndTabItem(); } if(igBeginTabItem("Selector", &a, launcher.used_tool == Tool.selector && different ? ImGuiTabItemFlags_SetSelected : 0)) { tool = Tool.selector; igEndTabItem(); } launcher.used_tool = tool; prev_tool = launcher.used_tool; } igEndTabBar(); igCheckbox("Show Tool", &launcher.tool_show); if(igIsItemHovered(0))igSetTooltip("Show/hide graphical tool representation"); igSameLine(0,4); igCheckbox("Show Filtered", &launcher.show_filtered); if(igIsItemHovered(0))igSetTooltip("Show/hide filtered entities"); if(launcher.used_tool == Tool.component_manipulator) { igCheckbox("Override", &launcher.override_); } //igSelectable("Selectabe",false,ImGuiSelectableFlags_None,ImVec2(0,0)); if(launcher.used_tool != Tool.selector) { if(igRadioButtonBool("Add", launcher.tool_mode))launcher.tool_mode = true; if(igIsItemHovered(0))igSetTooltip("Tool should adding (Entities or components)"); igSameLine(0,4); if(igRadioButtonBool("Remove", !launcher.tool_mode))launcher.tool_mode = false; if(igIsItemHovered(0))igSetTooltip("Tool should removing (Entities or components)"); } igSliderInt("Tool size", &launcher.tool_size, 0, 256, null); igSliderFloat("Tool repeat", &launcher.tool_repeat, 0, 1024, null, 4); if(igBeginChild("",ImVec2(0,0),1,0)) { launcher.gui_manager.toolGui(); } igEndChild(); } igEnd(); } if(launcher.show_profile_wnd) { //igSetNextWindowPos(ImVec2(launcher.window_size.x - 260, launcher.window_size.y - 280), ImGuiCond_Once, ImVec2(0,0)); igSetNextWindowPos(ImVec2(8, launcher.window_size.y - 258), ImGuiCond_Once, ImVec2(0,0)); igSetNextWindowSize(ImVec2(250, 250), ImGuiCond_Once); if(igBegin("Profile",&launcher.show_profile_wnd,0)) { if(igBeginTabBar("ProfileTabBar",ImGuiTabBarFlags_FittingPolicyScroll)) { if(igBeginTabItem("Plots", null, 0)) { if(igBeginChild("ProfileChild",ImVec2(-1,-1),0,0)) { igPlotLines("Delta time",&launcher.plot_values[0].delta_time,100,launcher.plot_index,"Delta time",0,float.max,ImVec2(0,64),Launcher.PlotStruct.sizeof); igPlotLines("Loop time",&launcher.plot_values[0].loop_time,100,launcher.plot_index,"Loop time",0,float.max,ImVec2(0,64),Launcher.PlotStruct.sizeof); igPlotLines("Draw time",&launcher.plot_values[0].draw_time,100,launcher.plot_index,"Draw time",0,float.max,ImVec2(0,64),Launcher.PlotStruct.sizeof); } igEndChild(); igEndTabItem(); } if(igBeginTabItem("Multithreading graph", null, 0)) { if(igBeginChild("ProfileChild",ImVec2(-1,-1),0,0)) { igText("Work in proggress"); } igEndChild(); igEndTabItem(); } igEndTabBar(); } } igEnd(); } launcher.renderer.resize(launcher.window_size); //launcher.renderer.view(vec2(0,0),vec2(launcher.window_size.x,launcher.window_size.y)); //if(384, 768, 1152, 1536) //576 960 1344 1728 //float scalling; if(launcher.window_size.y < 360)launcher.scalling = 1; else launcher.scalling = 1.0 / ((launcher.window_size.y+120)/360); launcher.renderer.view(launcher.render_position,vec2(launcher.window_size.x,launcher.window_size.y)*launcher.scalling); //launcher.renderer.view(vec2(0,0),vec2(1024*launcher.window_size.x/launcher.window_size.y,768)); //glClear(GL_COLOR_BUFFER_BIT); launcher.renderer.clear(); double loop_time = launcher.getTime(); launcher.job_updater.pool.tryWaitCount = 10000; if(launcher.play) { if(launcher.demo.loop && !launcher.demo.loop()) { quit(); *cast(bool*)arg = false; } } else { launcher.manager.begin(); import game_core.rendering; launcher.manager.callEntitiesFunction!DrawSystem(&(launcher.manager.getSystem!DrawSystem).onUpdate); launcher.manager.end(); } launcher.job_updater.pool.tryWaitCount = 0; loop_time = launcher.getTime() - loop_time; double draw_time = launcher.getTime(); launcher.renderer.present(); draw_time = launcher.getTime() - draw_time; if(launcher.tool_show)launcher.tool_circle.draw(&launcher.renderer, (launcher.mouse.position*launcher.scalling)-launcher.render_position, cast(float)launcher.tool_size, launcher.renderer.view_size.y*6*launcher.scalling); __gshared float plot_time = 0; __gshared uint plot_samples = 0; plot_time += launcher.delta_time; plot_samples++; launcher.plot_values[100].delta_time += launcher.delta_time; launcher.plot_values[100].loop_time += loop_time; launcher.plot_values[100].draw_time += draw_time; if(plot_time > 100) { launcher.plot_values[launcher.plot_index].delta_time = launcher.plot_values[100].delta_time / cast(float)plot_samples; launcher.plot_values[launcher.plot_index].loop_time = launcher.plot_values[100].loop_time / cast(float)plot_samples; launcher.plot_values[launcher.plot_index].draw_time = launcher.plot_values[100].draw_time / cast(float)plot_samples; launcher.plot_values[100].delta_time = 0; launcher.plot_values[100].loop_time = 0; launcher.plot_values[100].draw_time = 0; plot_samples = 0; plot_time -= 100; launcher.plot_index++; if(launcher.plot_index >= 100)launcher.plot_index = 0; } if(launcher.show_stat_wnd) { igSetNextWindowPos(ImVec2(10, 30), ImGuiCond_Appearing, ImVec2(0,0)); igSetNextWindowContentSize(ImVec2(150, 0.0f)); igSetNextWindowBgAlpha(launcher.windows_alpha); if(igBegin("Statistics", &launcher.show_stat_wnd, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize)) { igColumns(2,null,0); igSetColumnWidth(0,100); igText("FPS: "); igText("Entities: "); igText("Loop time: "); igText("Draw time: "); igText("Delta time: "); igNextColumn(); igText("%u",launcher.fps); igText("%u",launcher.entities_count); igText("%2.2fms",loop_time); igText("%2.2fms",draw_time); igText("%2.2fms",launcher.delta_time); igColumns(1,null,0); } igEnd(); } glUseProgram(0); import ecs_utils.gfx.buffer; Buffer.unbind(Buffer.BindTarget.array); Buffer.unbind(Buffer.BindTarget.element_array); igRender(); version(WebAssembly)ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData()); else version(Android)ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData()); else ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData()); //ImGuiImplOpenGL2RenderDrawData(igGetDrawData()); //launcher.renderer.clear(); //launcher.renderer.present(); SDL_GL_SwapWindow(launcher.window); } void quit() { import game_core.rendering : TexCoordsManager; launcher.gui_manager.clear(); Mallocator.dispose(launcher.gui_manager); if(launcher.demo.deinitialize)launcher.demo.deinitialize(); launcher.manager.destroy(); launcher.manager = null; TexCoordsManager.destroy(); SDL_Quit(); version(WebAssembly)emscripten_cancel_main_loop(); } version(Android) { export extern (C) int SDL_main(int argc, char** args) { return app_main(argc,args); } import ldc.attributes; extern (C) __gshared { @section(".tdata") int _tlsstart = 0; @section(".tcommon") int _tlsend = 0; } } else { extern (C) int main(int argc, char** argv) { return app_main(argc,argv); } } int app_main(int argc, char** argv) //int main(int argc, char** argv) { version(BindSDL_Static){} else { loadSDL(); loadSDLImage(); } if (SDL_Init(SDL_INIT_VIDEO) < 0) { printf("SDL could not initialize! SDL_Error: %s", SDL_GetError()); return -1; } SDL_version sdl_version; SDL_GetVersion(&sdl_version); printf("SDL version: %u.%u.%u\n",cast(uint)sdl_version.major,cast(uint)sdl_version.minor,cast(uint)sdl_version.patch); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); launcher.window = SDL_CreateWindow("Simple", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, launcher.window_size.x, launcher.window_size.y, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); SDL_MaximizeWindow(launcher.window); launcher.gl_context = SDL_GL_CreateContext(launcher.window); launcher.context = igCreateContext(null); launcher.timer_freq = SDL_GetPerformanceFrequency(); launcher.plot_values = Mallocator.makeArray!(Launcher.PlotStruct)(101); SDL_GetWindowSize(launcher.window, &launcher.window_size.x, &launcher.window_size.y); version(WebAssembly) { gladLoadGLES2(x => SDL_GL_GetProcAddress(x)); if(!ImGui_ImplSDL2_InitForOpenGL(launcher.window,launcher.gl_context)) { printf("ImGui initialization failed!"); return -2; } if(!ImGui_ImplOpenGL3_Init()) { printf("ImGui OpenGL initialization failed!"); return -3; } } else version(Android) { //gladLoadGL(); gladLoadGLES2(x => SDL_GL_GetProcAddress(x)); if(!ImGuiImplSDL2InitForOpenGL(launcher.window,launcher.gl_context)) { printf("ImGui initialization failed!"); return -2; } if(!ImGui_ImplOpenGL3_Init("#version 100")) { printf("ImGui OpenGL initialization failed!"); return -3; } } else { gladLoadGL(); if(!ImGuiImplSDL2InitForOpenGL(launcher.window,launcher.gl_context)) { printf("ImGui initialization failed!"); return -2; } //if(!ImGuiImplOpenGL2Init()) if(!ImGui_ImplOpenGL3_Init("#version 120")) { printf("ImGui OpenGL initialization failed!"); return -3; } } //ImFontConfig* config = ImFontConfig_ImFontConfig(); ImGuiIO* io = igGetIO(); const ushort* font_ranges = ImFontAtlas_GetGlyphRangesDefault(io.Fonts); ImFontAtlas_AddFontFromFileTTF(io.Fonts,"assets/fonts/Ruda-Bold.ttf", 15.0f, null, font_ranges); //ImFontConfig_destroy(config); setStyle(3); launcher.job_updater = Mallocator.make!ECSJobUpdater(1); //launcher.job_updater.onCreate(); EntityManager.initialize(32, 1<<16); launcher.manager = gEntityManager; //launcher.manager.m_thread_id_func = &launcher.job_updater.getThreadID; //launcher.manager.setJobDispachFunc(&launcher.job_updater.dispatch); launcher.manager.setMultithreadingCallbacks(&launcher.job_updater.dispatch, &launcher.job_updater.getThreadID); launcher.manager.beginRegister(); launcher.manager.registerPass("clean"); launcher.manager.registerComponent!CLocation; launcher.manager.registerSystem!CountSystem(10000); launcher.manager.registerSystem!CleanSystem(0,"clean"); launcher.manager.registerSystem!IteratorSystem(0,"clean"); launcher.manager.endRegister(); loadGFX(); launcher.renderer.initialize(); import game_core.rendering : TexCoordsManager; TexCoordsManager.initialize(); import mmutils.thread_pool : ThreadPool; launcher.threads = ThreadPool.getCPUCoresCount(); if(launcher.threads < 2)launcher.threads = 2; launcher.gui_manager = Mallocator.make!GUIManager(); { import demos.simple; import demos.space_invaders; import demos.particles; import demos.brick_breaker; // launcher.switchDemo(&spaceInvadersStart,&spaceInvadersLoop,&spaceInvadersEnd,&spaceInvadersEvent,SpaceInvaders.tips); // launcher.switchDemo(&particlesStart,&particlesLoop,&particlesEnd,&particlesEvent,ParticlesDemo.tips); // launcher.switchDemo(&simpleStart,&simpleLoop,&simpleEnd,&simpleEvent,Simple.tips); // launcher.switchDemo(getParticlesDemo()); // launcher.switchDemo(getSimpleDemo()); launcher.switchDemo(getSimpleDemo()); } int key_num; ubyte* keys = SDL_GetKeyboardState(&key_num); launcher.keys = keys[0..key_num]; version(WebAssembly) { /*EmscriptenFullscreenStrategy strategy; strategy.scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH; strategy.canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF; strategy.filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_NEAREST;*/ //int result = emscripten_request_fullscreen_strategy(null, true, &strategy); //emscripten_enter_soft_fullscreen(null, &strategy); //printf("Fullscreen request: %s.\n", emscripten_result_to_string(result)); emscripten_set_main_loop_arg(&mainLoop, null, 0, 1); } else { bool arg = true; while(arg == true) { mainLoop(&arg); } } return 0; } void loadGFX() { import ecs_utils.gfx.texture; import ecs_utils.gfx.vertex; import ecs_utils.gfx.shader; import ecs_utils.gfx.material; import ecs_utils.gfx.config; import ecs_utils.gfx.mesh; Texture.__loadBackend(); Renderer.__loadBackend(); GfxConfig.materials = Mallocator.makeArray!Material(3); GfxConfig.meshes = Mallocator.makeArray!Mesh(1); float[16] vertices = [-0.5,-0.5, 0,1, -0.5,0.5, 0,0, 0.5,-0.5, 1,1, 0.5,0.5, 1,0]; GfxConfig.meshes[0].vertices = Mallocator.makeArray(vertices); ushort[6] indices = [0,1,2,1,2,3]; GfxConfig.meshes[0].indices = Mallocator.makeArray(indices); GfxConfig.meshes[0].uploadData(); Shader vsh; vsh.create(); vsh.load("assets/shaders/base.vp"); vsh.compile(); Shader fsh; fsh.create(); fsh.load("assets/shaders/base.fp"); fsh.compile(); GfxConfig.materials[0].create(); GfxConfig.materials[0].data.blend_mode = Material.BlendMode.opaque; GfxConfig.materials[0].data.mode = Material.TransformMode.position; Material.ShaderModule[1] modules = [Material.ShaderModule(vsh,fsh)]; GfxConfig.materials[0].attachModules(modules); //GfxConfig.materials[0]. //GfxConfig.materials[0].load(load_data.materials[i].str); GfxConfig.materials[0].compile(); GfxConfig.materials[0].bindAttribLocation("positions",0); GfxConfig.materials[0].bindAttribLocation("tex_coords",1); GfxConfig.materials[0].bindAttribLocation("depth",2); GfxConfig.materials[0].bindAttribLocation("vcolor",3); GfxConfig.materials[0].link(); /* import std.stdio; writeln("positions ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"positions")); writeln("tex_coords ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"tex_coords")); writeln("depth ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"depth")); writeln("vcolor ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"vcolor"));*/ GfxConfig.materials[0].data.uniforms = Mallocator.makeArray!(Material.Uniform)(3); GfxConfig.materials[0].data.uniforms[0] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("matrix_1"), 0); GfxConfig.materials[0].data.uniforms[1] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("matrix_2"), 16); GfxConfig.materials[0].data.uniforms[2] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("uv_transform"), 32); GfxConfig.materials[0].data.bindings = Mallocator.makeArray!(int)(1); GfxConfig.materials[0].data.bindings[0] = GfxConfig.materials[0].getLocation("tex"); Shader vsh2; vsh2.create(); vsh2.load("assets/shaders/circle.vp"); vsh2.compile(); Shader fsh2; fsh2.create(); fsh2.load("assets/shaders/circle.fp"); fsh2.compile(); GfxConfig.materials[1].create(); GfxConfig.materials[1].data.blend_mode = Material.BlendMode.mixed; GfxConfig.materials[1].data.mode = Material.TransformMode.position; Material.ShaderModule[1] modules2 = [Material.ShaderModule(vsh2,fsh2)]; GfxConfig.materials[1].attachModules(modules2); //GfxConfig.materials[0]. //GfxConfig.materials[0].load(load_data.materials[i].str); GfxConfig.materials[1].compile(); GfxConfig.materials[1].bindAttribLocation("positions",0); //GfxConfig.materials[1].bindAttribLocation("tex_coords",1); //GfxConfig.materials[1].bindAttribLocation("depth",2); //GfxConfig.materials[1].bindAttribLocation("vcolor",3); GfxConfig.materials[1].link(); /* import std.stdio; writeln("positions ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"positions")); writeln("tex_coords ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"tex_coords")); writeln("depth ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"depth")); writeln("vcolor ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"vcolor"));*/ GfxConfig.materials[1].data.uniforms = Mallocator.makeArray!(Material.Uniform)(2); GfxConfig.materials[1].data.uniforms[0] = Material.Uniform(Material.Type.float4, GfxConfig.materials[1].getLocation("matrix_1"), 0); GfxConfig.materials[1].data.uniforms[1] = Material.Uniform(Material.Type.float4, GfxConfig.materials[1].getLocation("matrix_2"), 16); //GfxConfig.materials[1].data.uniforms[2] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("uv_transform"), 32); //GfxConfig.materials[1].data.bindings = Mallocator.makeArray!(int)(1); //GfxConfig.materials[1].data.bindings[0] = GfxConfig.materials[0].getLocation("tex"); Shader vsh3; vsh3.create(); vsh3.load("assets/shaders/additive_particles.vp"); vsh3.compile(); Shader fsh3; fsh3.create(); fsh3.load("assets/shaders/additive_particles.fp"); fsh3.compile(); GfxConfig.materials[2].create(); GfxConfig.materials[2].data.blend_mode = Material.BlendMode.opaque; GfxConfig.materials[2].data.mode = Material.TransformMode.position; Material.ShaderModule[1] modules3 = [Material.ShaderModule(vsh3,fsh3)]; GfxConfig.materials[2].attachModules(modules3); //GfxConfig.materials[0]. //GfxConfig.materials[0].load(load_data.materials[i].str); GfxConfig.materials[2].compile(); GfxConfig.materials[2].bindAttribLocation("positions",0); GfxConfig.materials[2].bindAttribLocation("tex_coords",1); GfxConfig.materials[2].bindAttribLocation("depth",2); GfxConfig.materials[2].bindAttribLocation("vcolor",3); GfxConfig.materials[2].link(); /* import std.stdio; writeln("positions ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"positions")); writeln("tex_coords ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"tex_coords")); writeln("depth ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"depth")); writeln("vcolor ",glGetAttribLocation(GfxConfig.materials[0].data.modules[0].gl_handle,"vcolor"));*/ GfxConfig.materials[2].data.uniforms = Mallocator.makeArray!(Material.Uniform)(3); GfxConfig.materials[2].data.uniforms[0] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("matrix_1"), 0); GfxConfig.materials[2].data.uniforms[1] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("matrix_2"), 16); GfxConfig.materials[2].data.uniforms[2] = Material.Uniform(Material.Type.float4, GfxConfig.materials[0].getLocation("uv_transform"), 32); GfxConfig.materials[2].data.bindings = Mallocator.makeArray!(int)(1); GfxConfig.materials[2].data.bindings[0] = GfxConfig.materials[0].getLocation("tex"); GfxConfig.materials[2].data.blend_mode = Material.BlendMode.additive; /*glUseProgram(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);*/ launcher.tool_circle = Mallocator.make!ToolCircle; }