level_editor.cpp 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648
  1. /*
  2. * Copyright (c) 2012-2022 Daniele Bartolini et al.
  3. * License: https://github.com/dbartolini/crown/blob/master/LICENSE
  4. */
  5. #if CROWN_TOOLS
  6. #include "core/containers/vector.inl"
  7. #include "core/filesystem/file.h"
  8. #include "core/filesystem/filesystem_disk.h"
  9. #include "core/filesystem/path.h"
  10. #include "core/json/json.h"
  11. #include "core/json/json_object.inl"
  12. #include "core/json/sjson.h"
  13. #include "core/math/vector2.inl"
  14. #include "core/memory/temp_allocator.inl"
  15. #include "core/network/ip_address.h"
  16. #include "core/network/socket.h"
  17. #include "core/process.h"
  18. #include "core/strings/dynamic_string.inl"
  19. #include "core/strings/string_id.inl"
  20. #include "core/strings/string_stream.inl"
  21. #include "device/device.h"
  22. #include "device/device_options.h"
  23. #include "device/input_device.h"
  24. #include "device/input_manager.h"
  25. #include "device/input_types.h"
  26. #include "device/log.h"
  27. #include "device/pipeline.h"
  28. #include "device/types.h"
  29. #include "imgui_context.h"
  30. #include "resource/resource_manager.h"
  31. #include "resource/texture_resource.h"
  32. #include "tool_api.h"
  33. #include <iconfontheaders/icons_material_design.h>
  34. #include <imgui.h>
  35. #include <nfd.h>
  36. #include <time.h>
  37. #if CROWN_PLATFORM_POSIX
  38. #include <sys/time.h>
  39. #endif
  40. LOG_SYSTEM(LEVEL_EDITOR, "level_editor")
  41. namespace crown
  42. {
  43. struct Project
  44. {
  45. DynamicString _data_dir;
  46. Project(Allocator& a)
  47. : _data_dir(a)
  48. {
  49. }
  50. };
  51. static u16 _width = 1280;
  52. static u16 _height = 720;
  53. static struct LevelEditor* _editor;
  54. static struct Project* _project;
  55. static TCPSocket _client;
  56. static Process _game_process;
  57. struct StartGame
  58. {
  59. enum Enum
  60. {
  61. NORMAL,
  62. TEST
  63. };
  64. };
  65. static void stop_game()
  66. {
  67. if (_game_process.spawned())
  68. {
  69. _game_process.force_exit();
  70. _game_process.wait();
  71. }
  72. }
  73. // FIXME: duplicated from compile_options.h
  74. #if CROWN_PLATFORM_LINUX
  75. #define EXE_PREFIX "./"
  76. #define EXE_SUFFIX ""
  77. #elif CROWN_PLATFORM_WINDOWS
  78. #define EXE_PREFIX ""
  79. #define EXE_SUFFIX ".exe"
  80. #else
  81. #error "Unknown platform"
  82. #endif // CROWN_PLATFORM_LINUX
  83. #define EXE_PATH(exe) EXE_PREFIX exe EXE_SUFFIX
  84. static void start_game(StartGame::Enum sg, const char* data_dir)
  85. {
  86. // Stop any previously launched game
  87. stop_game();
  88. const char* argv[] =
  89. {
  90. #if CROWN_DEBUG && !CROWN_DEVELOPMENT
  91. EXE_PATH("crown-debug"),
  92. #else
  93. EXE_PATH("crown-development"),
  94. #endif
  95. "--data-dir", data_dir,
  96. "--console-port", "12345",
  97. // "--wait-console",
  98. "--lua-string", sg == StartGame::TEST ? "TEST=true" : "",
  99. NULL
  100. };
  101. _game_process.spawn(argv);
  102. }
  103. struct Pivot
  104. {
  105. enum Enum
  106. {
  107. TOP_LEFT,
  108. TOP_CENTER,
  109. TOP_RIGHT,
  110. LEFT,
  111. CENTER,
  112. RIGHT,
  113. BOTTOM_LEFT,
  114. BOTTOM_CENTER,
  115. BOTTOM_RIGHT,
  116. COUNT
  117. };
  118. };
  119. static const char* pivot_names[] =
  120. {
  121. "Top Left", // Pivot::TOP_LEFT
  122. "Top Right", // Pivot::TOP_CENTER
  123. "Top Center", // Pivot::TOP_RIGHT
  124. "Left", // Pivot::LEFT
  125. "Center", // Pivot::CENTER
  126. "Right", // Pivot::RIGHT
  127. "Bottom Left", // Pivot::BOTTOM_LEFT
  128. "Bottom Center", // Pivot::BOTTOM_CENTER
  129. "Bottom Right" // Pivot::BOTTOM_RIGHT
  130. };
  131. CE_STATIC_ASSERT(countof(pivot_names) == Pivot::COUNT);
  132. Vector2 sprite_cell_xy(int r, int c, int offset_x, int offset_y, int cell_w, int cell_h, int spacing_x, int spacing_y)
  133. {
  134. int x0 = offset_x + c*cell_w + c*spacing_x;
  135. int y0 = offset_y + r*cell_h + r*spacing_y;
  136. return vector2((f32)x0, (f32)y0);
  137. }
  138. Vector2 sprite_cell_pivot_xy(int cell_w, int cell_h, int pivot)
  139. {
  140. int pivot_x = 0;
  141. int pivot_y = 0;
  142. switch (pivot)
  143. {
  144. case Pivot::TOP_LEFT:
  145. pivot_x = 0;
  146. pivot_y = 0;
  147. break;
  148. case Pivot::TOP_CENTER:
  149. pivot_x = cell_w / 2;
  150. pivot_y = 0;
  151. break;
  152. case Pivot::TOP_RIGHT:
  153. pivot_x = cell_w;
  154. pivot_y = 0;
  155. break;
  156. case Pivot::BOTTOM_LEFT:
  157. pivot_x = 0;
  158. pivot_y = cell_h;
  159. break;
  160. case Pivot::BOTTOM_CENTER:
  161. pivot_x = cell_w / 2;
  162. pivot_y = cell_h;
  163. break;
  164. case Pivot::BOTTOM_RIGHT:
  165. pivot_x = cell_w;
  166. pivot_y = cell_h;
  167. break;
  168. case Pivot::LEFT:
  169. pivot_x = 0;
  170. pivot_y = cell_h / 2;
  171. break;
  172. case Pivot::CENTER:
  173. pivot_x = cell_w / 2;
  174. pivot_y = cell_h / 2;
  175. break;
  176. case Pivot::RIGHT:
  177. pivot_x = cell_w;
  178. pivot_y = cell_h / 2;
  179. break;
  180. default:
  181. CE_FATAL("Unknown pivot");
  182. break;
  183. }
  184. return vector2((f32)pivot_x, (f32)pivot_y);
  185. }
  186. struct SpriteImporter
  187. {
  188. int width;
  189. int height;
  190. int cells_h;
  191. int cells_v;
  192. bool cell_wh_auto;
  193. int cell_w;
  194. int cell_h;
  195. int offset_x;
  196. int offset_y;
  197. int spacing_x;
  198. int spacing_y;
  199. int pivot;
  200. int layer;
  201. int depth;
  202. SpriteImporter()
  203. : width(128)
  204. , height(128)
  205. , cells_h(4)
  206. , cells_v(4)
  207. , cell_wh_auto(false)
  208. , cell_w(16)
  209. , cell_h(16)
  210. , offset_x(0)
  211. , offset_y(0)
  212. , spacing_x(0)
  213. , spacing_y(0)
  214. , pivot(Pivot::CENTER)
  215. , layer(0)
  216. , depth(0)
  217. {
  218. }
  219. void draw()
  220. {
  221. ImGui::Columns(2);
  222. #if 1
  223. ImDrawList* draw_list = ImGui::GetWindowDrawList();
  224. {
  225. // Here we are using InvisibleButton() as a convenience to 1) advance the cursor and 2) allows us to use IsItemHovered()
  226. // However you can draw directly and poll mouse/keyboard by yourself. You can manipulate the cursor using GetCursorPos() and SetCursorPos().
  227. // If you only use the ImDrawList API, you can notify the owner window of its extends by using SetCursorPos(max).
  228. ImVec2 canvas_pos = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates!
  229. ImVec2 canvas_size = ImGui::GetContentRegionAvail(); // Resize canvas to what's available
  230. if (canvas_size.x < 50.0f) canvas_size.x = 10.0f;
  231. if (canvas_size.y < 50.0f) canvas_size.y = 50.0f;
  232. draw_list->AddRectFilledMultiColor(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), ImColor(50,50,50), ImColor(50,50,60), ImColor(60,60,70), ImColor(50,50,60));
  233. // Pivot is relative to the top-left corner of the cell
  234. Vector2 pivot_xy = sprite_cell_pivot_xy(cell_w
  235. , cell_h
  236. , pivot
  237. );
  238. int num_v = cells_v;
  239. int num_h = cells_h;
  240. for (int h = 0; h < num_v; ++h)
  241. {
  242. for (int w = 0; w < num_h; ++w)
  243. {
  244. Vector2 cell = sprite_cell_xy(h
  245. , w
  246. , offset_x
  247. , offset_y
  248. , cell_w
  249. , cell_h
  250. , spacing_x
  251. , spacing_y
  252. );
  253. const int x0 = (int)cell.x;
  254. const int y0 = (int)cell.y;
  255. const int x1 = x0+(int)cell_w;
  256. const int y1 = y0+(int)cell_h;
  257. draw_list->AddRect(ImVec2(canvas_pos.x + x0, canvas_pos.y + y0)
  258. , ImVec2(canvas_pos.x + x1, canvas_pos.y + y1)
  259. , ImColor(230, 26, 26, 153)
  260. );
  261. draw_list->AddCircleFilled(ImVec2(x0 + canvas_pos.x + pivot_xy.x, y0 + canvas_pos.y + pivot_xy.y)
  262. , 5.0f
  263. , ImColor(26, 26, 230, 153)
  264. );
  265. }
  266. }
  267. ImGui::InvisibleButton("canvas", canvas_size);
  268. draw_list->PushClipRect(canvas_pos, ImVec2(canvas_pos.x+canvas_size.x, canvas_pos.y+canvas_size.y)); // clip lines within the canvas (if we resize it, etc.)
  269. }
  270. #endif
  271. ImGui::NextColumn();
  272. ImGui::BeginGroup();
  273. ImGui::LabelText("Resolution", "%d x %d", width, height);
  274. ImGui::InputInt("Cells H", &cells_h);
  275. cells_h = clamp(cells_h, 1, 256);
  276. ImGui::InputInt("Cells V", &cells_v);
  277. cells_v = clamp(cells_v, 1, 256);
  278. ImGui::Checkbox("Cell WH auto", &cell_wh_auto);
  279. ImGui::InputInt("Cell W", &cell_w);
  280. cell_w = clamp(cell_w, 1, 4096);
  281. ImGui::InputInt("Cell H", &cell_h);
  282. cell_h = clamp(cell_h, 1, 4096);
  283. ImGui::InputInt("Offset X", &offset_x);
  284. offset_x = clamp(offset_x, 0, 128);
  285. ImGui::InputInt("Offset Y", &offset_y);
  286. offset_y = clamp(offset_y, 0, 128);
  287. ImGui::InputInt("Spacing X", &spacing_x);
  288. spacing_x = clamp(spacing_x, 0, 128);
  289. ImGui::InputInt("Spacing Y", &spacing_y);
  290. spacing_y = clamp(spacing_y, 0, 128);
  291. ImGui::Combo("Pivot", &pivot, pivot_names, Pivot::COUNT);
  292. ImGui::InputInt("Layer", &layer);
  293. layer = clamp(layer, 0, 7);
  294. ImGui::InputInt("Depth", &depth);
  295. depth = clamp(depth, 0, 9999);
  296. ImGui::EndGroup();
  297. }
  298. };
  299. //-----------------------------------------------------------------------------
  300. struct Inspector
  301. {
  302. // Inspector
  303. char _name[1024];
  304. f32 _position[3];
  305. f32 _rotation[3];
  306. f32 _scale[3];
  307. char _sprite[1024];
  308. char _material[1024];
  309. bool _visible;
  310. char _state_machine[1024];
  311. bool _open;
  312. Inspector()
  313. : _visible(true)
  314. , _open(true)
  315. {
  316. memset(_name, 0, sizeof(_name));
  317. memset(_sprite, 0, sizeof(_sprite));
  318. memset(_material, 0, sizeof(_material));
  319. memset(_position, 0, sizeof(_position));
  320. memset(_rotation, 0, sizeof(_rotation));
  321. memset(_scale, 0, sizeof(_scale));
  322. memset(_state_machine, 0, sizeof(_state_machine));
  323. }
  324. void draw()
  325. {
  326. ImGui::Begin("Inspector", &_open);
  327. if (ImGui::TreeNodeEx("Unit", ImGuiTreeNodeFlags_DefaultOpen))
  328. {
  329. ImGui::InputText("Name", _name, sizeof(_name));
  330. ImGui::TreePop();
  331. }
  332. if (ImGui::TreeNodeEx("Transform", ImGuiTreeNodeFlags_DefaultOpen))
  333. {
  334. ImGui::InputFloat3("Position", _position);
  335. ImGui::InputFloat3("Rotation", _rotation);
  336. ImGui::InputFloat3("Scale", _scale);
  337. ImGui::TreePop();
  338. }
  339. if (ImGui::TreeNodeEx("Renderer", ImGuiTreeNodeFlags_DefaultOpen))
  340. {
  341. ImGui::InputText("Sprite", _sprite, sizeof(_sprite));
  342. ImGui::InputText("Material", _material, sizeof(_material));
  343. ImGui::Checkbox("Visible", &_visible);
  344. ImGui::TreePop();
  345. }
  346. if (ImGui::TreeNodeEx("Animation", ImGuiTreeNodeFlags_DefaultOpen))
  347. {
  348. ImGui::InputText("State Machine", _state_machine, sizeof(_state_machine));
  349. ImGui::TreePop();
  350. }
  351. ImGui::End();
  352. }
  353. };
  354. //-----------------------------------------------------------------------------
  355. struct SceneView
  356. {
  357. ImVec2 _origin;
  358. ImVec2 _size;
  359. ImVec2 _cursor;
  360. bool _open;
  361. SceneView()
  362. : _open(true)
  363. {
  364. }
  365. void draw()
  366. {
  367. ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
  368. ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
  369. ImGui::Begin("Scene View"
  370. , &_open
  371. , ImGuiWindowFlags_NoScrollbar
  372. | ImGuiWindowFlags_NoScrollWithMouse
  373. );
  374. _origin = ImGui::GetCursorScreenPos();
  375. uint16_t w, h;
  376. device()->resolution(w, h);
  377. bgfx::TextureHandle txh = device()->_pipeline->_buffers[0];
  378. CE_ENSURE(bgfx::isValid(txh));
  379. ImGui::Image((void*)(uintptr_t)txh.idx
  380. , ImVec2(w, h)
  381. #if CROWN_PLATFORM_WINDOWS
  382. , ImVec2(0, 0)
  383. , ImVec2(1, 1)
  384. #else
  385. , ImVec2(0, 1)
  386. , ImVec2(1, 0)
  387. #endif // CROWN_PLATFORM_WINDOWS
  388. );
  389. ImVec2 mouse_pos_in_view = ImVec2(ImGui::GetIO().MousePos.x - _origin.x
  390. , ImGui::GetIO().MousePos.y - _origin.y
  391. );
  392. if (ImGui::IsWindowHovered()
  393. && mouse_pos_in_view.x > 0
  394. && mouse_pos_in_view.x < w
  395. && mouse_pos_in_view.y > 0
  396. && mouse_pos_in_view.y < h
  397. )
  398. {
  399. // Send all input to engine
  400. ImGui::CaptureMouseFromApp(false);
  401. ImGui::CaptureKeyboardFromApp(false);
  402. }
  403. else
  404. {
  405. // Send all input to imgui
  406. ImGui::CaptureMouseFromApp(true);
  407. ImGui::CaptureKeyboardFromApp(true);
  408. }
  409. _size = ImGui::GetWindowSize();
  410. _size.y -= ImGui::GetFrameHeight();
  411. ImGui::End();
  412. ImGui::PopStyleVar(2);
  413. }
  414. };
  415. //-----------------------------------------------------------------------------
  416. struct SceneTree
  417. {
  418. bool _open;
  419. SceneTree()
  420. : _open(true)
  421. {
  422. }
  423. void draw()
  424. {
  425. ImGui::Begin("Scene Tree", &_open);
  426. if (ImGui::TreeNodeEx("Units", ImGuiTreeNodeFlags_DefaultOpen))
  427. {
  428. if (ImGui::TreeNodeEx("Objects", ImGuiTreeNodeFlags_DefaultOpen))
  429. {
  430. for (int i = 0; i < 5; i++)
  431. if (ImGui::TreeNode((void*)(intptr_t)i, "Child %d", i))
  432. {
  433. ImGui::Text("blah blah");
  434. ImGui::SameLine();
  435. if (ImGui::SmallButton("print")) printf("Child %d pressed", i);
  436. ImGui::TreePop();
  437. }
  438. ImGui::TreePop();
  439. }
  440. if (ImGui::TreeNodeEx("Lights", ImGuiTreeNodeFlags_DefaultOpen))
  441. {
  442. // ShowHelpMarker("This is a more standard looking tree with selectable nodes.\nClick to select, Ctrl+Click to toggle, click on arrows or double-click to open.");
  443. static bool align_label_with_current_x_position = false;
  444. ImGui::Checkbox("Align label with current X position)", &align_label_with_current_x_position);
  445. ImGui::Text("Hello!");
  446. if (align_label_with_current_x_position)
  447. ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());
  448. static int selection_mask = (1 << 2); // Dumb representation of what may be user-side selection state. You may carry selection state inside or outside your objects in whatever format you see fit.
  449. int node_clicked = -1; // Temporary storage of what node we have clicked to process selection at the end of the loop. May be a pointer to your own node type, etc.
  450. ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, ImGui::GetFontSize()*3); // Increase spacing to differentiate leaves from expanded contents.
  451. for (int i = 0; i < 6; i++)
  452. {
  453. // Disable the default open on single-click behavior and pass in Selected flag according to our selection state.
  454. ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ((selection_mask & (1 << i)) ? ImGuiTreeNodeFlags_Selected : 0);
  455. if (i < 3)
  456. {
  457. // Node
  458. bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i);
  459. if (ImGui::IsItemClicked())
  460. node_clicked = i;
  461. if (node_open)
  462. {
  463. ImGui::Text("Blah blah\nBlah Blah");
  464. ImGui::TreePop();
  465. }
  466. }
  467. else
  468. {
  469. // Leaf: The only reason we have a TreeNode at all is to allow selection of the leaf. Otherwise we can use BulletText() or TreeAdvanceToLabelPos()+Text().
  470. ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen, "Selectable Leaf %d", i);
  471. if (ImGui::IsItemClicked())
  472. node_clicked = i;
  473. }
  474. }
  475. if (node_clicked != -1)
  476. {
  477. // Update selection state. Process outside of tree loop to avoid visual inconsistencies during the clicking-frame.
  478. if (ImGui::GetIO().KeyCtrl)
  479. selection_mask ^= (1 << node_clicked); // Ctrl+click to toggle
  480. else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, this commented bit preserve selection when clicking on item that is part of the selection
  481. selection_mask = (1 << node_clicked); // Click to single-select
  482. }
  483. ImGui::PopStyleVar();
  484. if (align_label_with_current_x_position)
  485. ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing());
  486. ImGui::TreePop();
  487. }
  488. ImGui::TreePop();
  489. }
  490. ImGui::End();
  491. }
  492. };
  493. //-----------------------------------------------------------------------------
  494. struct SpriteAnimator
  495. {
  496. bool _open;
  497. bool _add_animation_popup_open;
  498. Array<const char*> _entities;
  499. s32 _cur_entity;
  500. TextureResource* _texture;
  501. u32 _texture_width;
  502. u32 _texture_height;
  503. struct Frame
  504. {
  505. char name[512];
  506. ImVec2 pivot;
  507. ImVec4 region;
  508. };
  509. Array<Frame> _frames;
  510. char _anim_name[512];
  511. f32 _anim_time;
  512. Array<const char*> _listbox_items;
  513. s32 _listbox_item_current;
  514. Array<Frame> _anim_preview_frames;
  515. f32 _delta;
  516. u32 current;
  517. Vector<DynamicString> file_list_sprites;
  518. FilesystemDisk* _fs;
  519. SpriteAnimator(const DynamicString& src_dir)
  520. : _open(false)
  521. , _add_animation_popup_open(false)
  522. , _entities(default_allocator())
  523. , _cur_entity(0)
  524. , _texture(nullptr)
  525. , _texture_width(0)
  526. , _texture_height(0)
  527. , _frames(default_allocator())
  528. , _anim_time(0.1f)
  529. , _listbox_items(default_allocator())
  530. , _listbox_item_current(0)
  531. , _anim_preview_frames(default_allocator())
  532. , _delta(0.0f)
  533. , current(0)
  534. , file_list_sprites(default_allocator())
  535. {
  536. memset(_anim_name, 0, sizeof(_anim_name));
  537. _fs = CE_NEW(default_allocator(), FilesystemDisk)(default_allocator());
  538. _fs->set_prefix(src_dir.c_str());
  539. get_sprites_list();
  540. }
  541. ~SpriteAnimator()
  542. {
  543. CE_DELETE(default_allocator(), _fs);
  544. }
  545. ImVec2 pixel_to_uv(u32 tex_w, u32 tex_h, f32 x, f32 y)
  546. {
  547. ImVec2 uv;
  548. uv.x = (f32)x / (f32)tex_w;
  549. uv.y = (f32)y / (f32)tex_h;
  550. return uv;
  551. }
  552. void get_directory(DynamicString& dir, const char* path)
  553. {
  554. CE_ENSURE(NULL != path);
  555. const char* ls = strrchr(path, '/');
  556. u32 file_len = strlen32(ls+1);
  557. u32 dir_len = strlen32(path) - file_len;
  558. char buff[1024];
  559. memcpy(buff, path, dir_len);
  560. buff[dir_len] = '\0';
  561. path::reduce(dir, buff);
  562. }
  563. void save_sprite_animation()
  564. {
  565. TempAllocator4096 ta;
  566. StringStream ss(ta);
  567. ss << "frames = [ ";
  568. for (u32 i = 0; i < array::size(_listbox_items); i++)
  569. {
  570. ss << _listbox_items[i] + 7 << " ";
  571. }
  572. ss << "]\n";
  573. ss << "total_time = ";
  574. ss << _anim_time;
  575. DynamicString dir(ta);
  576. get_directory(dir, _entities[_cur_entity]);
  577. DynamicString file_name(ta);
  578. path::join(file_name, dir.c_str(), _anim_name);
  579. file_name += ".sprite_animation";
  580. File* f = _fs->open(file_name.c_str(), FileOpenMode::WRITE);
  581. f->write(string_stream::c_str(ss), strlen32(string_stream::c_str(ss)));
  582. f->close();
  583. }
  584. void get_files_list(Vector<DynamicString>& out, const char* path="")
  585. {
  586. TempAllocator4096 ta;
  587. Vector<DynamicString> files(ta);
  588. _fs->list_files(path, files);
  589. for (u32 i = 0; i < vector::size(files); i++)
  590. {
  591. DynamicString join(ta);
  592. path::join(join, path, files[i].c_str());
  593. join = join.c_str()[0] == '/' ? join.c_str()+1 : join.c_str();
  594. if (_fs->is_directory(join.c_str()))
  595. {
  596. get_files_list(out, join.c_str());
  597. }
  598. else
  599. {
  600. vector::push_back(out, join);
  601. }
  602. }
  603. }
  604. void get_sprites_list()
  605. {
  606. TempAllocator4096 ta;
  607. Vector<DynamicString> files(ta);
  608. get_files_list(files);
  609. for (DynamicString* f = vector::begin(files); f != vector::end(files); f++)
  610. if (f->has_suffix(".sprite"))
  611. array::push_back(_entities, (const char*) strdup(f->c_str()));
  612. }
  613. void draw()
  614. {
  615. ImGui::Begin("Animator", &_open);
  616. if (_texture)
  617. {
  618. Frame f = _frames[0];
  619. ImVec2 start = pixel_to_uv(_texture_width, _texture_height, f.region.x, f.region.y);
  620. ImVec2 end = pixel_to_uv(_texture_width, _texture_height, f.region.x+f.region.z, f.region.y+f.region.w);
  621. ImGui::Image((void*)(uintptr_t)_texture->handle.idx
  622. , ImVec2(f.region.z, f.region.w)
  623. , start
  624. , end
  625. , ImColor(255, 255, 255, 55)
  626. );
  627. }
  628. if (ImGui::Combo("Entities", &_cur_entity, (const char* const*) array::begin(_entities), array::size(_entities)))
  629. {
  630. array::clear(_frames);
  631. const char* sprite = _entities[_cur_entity];
  632. u32 sprite_len = strlen32(sprite);
  633. char entity[1024];
  634. strncpy(entity, sprite, sizeof(entity)-1);
  635. entity[sprite_len-7] = '\0'; // remove ".sprite"
  636. ResourceManager* resman = device()->_resource_manager;
  637. _texture = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, StringId64(entity));
  638. File* file = _fs->open(sprite, FileOpenMode::READ);
  639. const u32 size = file->size();
  640. Buffer buf(default_allocator());
  641. array::resize(buf, size);
  642. file->read(array::begin(buf), size);
  643. _fs->close(*file);
  644. JsonObject obj(default_allocator());
  645. JsonArray list(default_allocator());
  646. sjson::parse(obj, buf);
  647. sjson::parse_array(list, obj["frames"]);
  648. _texture_width = sjson::parse_int(obj["width"]);
  649. _texture_height = sjson::parse_int(obj["height"]);
  650. for (u32 i = 0; i < array::size(list); i++)
  651. {
  652. JsonObject frame(default_allocator());
  653. DynamicString name(default_allocator());
  654. JsonArray pivot(default_allocator());
  655. JsonArray region(default_allocator());
  656. sjson::parse_object(frame, list[i]);
  657. sjson::parse_array(pivot, frame["pivot"]);
  658. sjson::parse_array(region, frame["region"]);
  659. Frame f;
  660. sjson::parse_string(name, frame["name"]);
  661. strncpy(f.name, name.c_str(), name.length());
  662. f.name[name.length()] = '\0';
  663. f.pivot.x = sjson::parse_float(pivot[0]);
  664. f.pivot.y = sjson::parse_float(pivot[1]);
  665. f.region.x = sjson::parse_float(region[0]);
  666. f.region.y = sjson::parse_float(region[1]);
  667. f.region.z = sjson::parse_float(region[2]);
  668. f.region.w = sjson::parse_float(region[3]);
  669. array::push_back(_frames, f);
  670. }
  671. }
  672. if (ImGui::Button("Add animation", ImVec2(100, 25)))
  673. {
  674. _add_animation_popup_open = true;
  675. }
  676. if (_add_animation_popup_open)
  677. {
  678. ImGui::OpenPopup("Add animation");
  679. }
  680. if (ImGui::BeginPopup("Add animation"))
  681. {
  682. ImGui::InputText("Name", _anim_name, sizeof(_anim_name));
  683. ImGui::InputFloat("Time", &_anim_time, 0.1f, 0.1f);
  684. ImGui::ListBox("Animation Frames", &_listbox_item_current, (const char* const*)array::begin(_listbox_items), array::size(_listbox_items));
  685. if (ImGui::Button("Clear Frames", ImVec2(100.0f, 25.0f)))
  686. {
  687. array::clear(_listbox_items);
  688. array::clear(_anim_preview_frames);
  689. _delta = 0.0f;
  690. current = 0;
  691. }
  692. if (array::size(_anim_preview_frames) > 0)
  693. {
  694. _delta += 1.0f/60.0f;
  695. if (_delta >= _anim_time/array::size(_anim_preview_frames))
  696. {
  697. _delta = 0;
  698. current++;
  699. if (current >= array::size(_anim_preview_frames))
  700. current = 0;
  701. }
  702. Frame f = _anim_preview_frames[current];
  703. ImVec2 start = pixel_to_uv(_texture_width, _texture_height, f.region.x, f.region.y);
  704. ImVec2 end = pixel_to_uv(_texture_width, _texture_height, f.region.x+f.region.z, f.region.y+f.region.w);
  705. ImGui::Image(
  706. (void*)(uintptr_t)_texture->handle.idx
  707. , ImVec2(f.region.z, f.region.w)
  708. , start
  709. , end
  710. , ImColor(255, 255, 255, 55)
  711. );
  712. }
  713. ImGui::Separator();
  714. for (u32 i = 0; i < array::size(_frames); i++)
  715. {
  716. Frame f = _frames[i];
  717. ImVec2 start = pixel_to_uv(_texture_width, _texture_height, f.region.x, f.region.y);
  718. ImVec2 end = pixel_to_uv(_texture_width, _texture_height, f.region.x+f.region.z, f.region.y+f.region.w);
  719. ImGui::SameLine();
  720. if (i % 9 == 0) ImGui::NewLine();
  721. ImGui::BeginGroup();
  722. ImGui::Image(
  723. (void*)(uintptr_t)_texture->handle.idx
  724. , ImVec2(f.region.z, f.region.w)
  725. , start
  726. , end
  727. , ImColor(255, 255, 255, 55)
  728. );
  729. ImGui::NewLine();
  730. if (ImGui::Button(_frames[i].name, ImVec2(100.0f, 25.0f)))
  731. {
  732. array::push_back(_listbox_items, (const char*) strdup(_frames[i].name));
  733. array::push_back(_anim_preview_frames, _frames[i]);
  734. }
  735. ImGui::EndGroup();
  736. }
  737. if (ImGui::Button("Save", ImVec2(100, 25)))
  738. {
  739. save_sprite_animation();
  740. ImGui::CloseCurrentPopup();
  741. _add_animation_popup_open = false;
  742. }
  743. ImGui::SameLine();
  744. if (ImGui::Button("Cancel", ImVec2(100, 25)))
  745. {
  746. ImGui::CloseCurrentPopup();
  747. _add_animation_popup_open = false;
  748. }
  749. ImGui::EndPopup();
  750. }
  751. ImGui::End();
  752. }
  753. };
  754. //-----------------------------------------------------------------------------
  755. struct LevelEditor
  756. {
  757. DynamicString _source_dir;
  758. // FX
  759. TextureResource* tool_move_texture;
  760. TextureResource* tool_place_texture;
  761. TextureResource* tool_rotate_texture;
  762. TextureResource* tool_scale_texture;
  763. TextureResource* reference_world_texture;
  764. TextureResource* reference_local_texture;
  765. TextureResource* axis_local_texture;
  766. TextureResource* axis_world_texture;
  767. TextureResource* snap_to_grid_texture;
  768. // State
  769. f32 _grid_size;
  770. f32 _rotation_snap;
  771. bool _show_grid;
  772. bool _snap_to_grid;
  773. bool _debug_render_world;
  774. bool _debug_physics_world;
  775. tool::ToolType::Enum _tool_type;
  776. tool::SnapMode::Enum _snap_mode;
  777. tool::ReferenceSystem::Enum _reference_system;
  778. ImVec2 _main_menu_pos;
  779. ImVec2 _main_menu_size;
  780. Console _console;
  781. Inspector _inspector;
  782. SceneView _scene_view;
  783. SceneTree _scene_tree;
  784. SpriteAnimator _animator;
  785. SpriteImporter _sprite_importer;
  786. bool _fullscreen;
  787. LevelEditor(const DynamicString& source_dir)
  788. : _source_dir(default_allocator())
  789. , _grid_size(1.0f)
  790. , _rotation_snap(15)
  791. , _show_grid(true)
  792. , _snap_to_grid(true)
  793. , _debug_render_world(false)
  794. , _debug_physics_world(false)
  795. , _tool_type(tool::ToolType::MOVE)
  796. , _snap_mode(tool::SnapMode::RELATIVE)
  797. , _reference_system(tool::ReferenceSystem::LOCAL)
  798. , _main_menu_pos(0, 0)
  799. , _main_menu_size(0, 0)
  800. , _animator(source_dir)
  801. , _fullscreen(false)
  802. {
  803. ResourceManager* resman = device()->_resource_manager;
  804. tool_move_texture = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, STRING_ID_64("core/editors/gui/tool-move", 0x57f16b5c703944e8));
  805. tool_place_texture = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, STRING_ID_64("core/editors/gui/tool-place", 0xf33a70241fe20ffd));
  806. tool_rotate_texture = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, STRING_ID_64("core/editors/gui/tool-rotate", 0x59bfc47bde2b2072));
  807. tool_scale_texture = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, STRING_ID_64("core/editors/gui/tool-scale", 0x73d5bcd35a65d39c));
  808. reference_world_texture = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, STRING_ID_64("core/editors/gui/reference-world", 0xe072b9ad48895d31));
  809. reference_local_texture = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, STRING_ID_64("core/editors/gui/reference-local", 0x22153239997ae30a));
  810. axis_local_texture = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, STRING_ID_64("core/editors/gui/axis-local", 0xb3b5b50a9cecce48));
  811. axis_world_texture = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, STRING_ID_64("core/editors/gui/axis-world", 0xd10f6f1a9a680032));
  812. snap_to_grid_texture = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, STRING_ID_64("core/editors/gui/snap-to-grid", 0xf8831d3758d30394));
  813. imgui_create();
  814. }
  815. ~LevelEditor()
  816. {
  817. imgui_destroy();
  818. }
  819. void update(f32 dt)
  820. {
  821. CE_UNUSED(dt);
  822. static f32 last_w = 0.0f;
  823. static f32 last_h = 0.0f;
  824. if (last_w != _scene_view._size.x || last_h != _scene_view._size.y)
  825. {
  826. last_w = _scene_view._size.x;
  827. last_h = _scene_view._size.y;
  828. device()->_width = u16(_scene_view._size.x != 0.0f ? _scene_view._size.x : 128.0f);
  829. device()->_height = u16(_scene_view._size.y != 0.0f ? _scene_view._size.y : 128.0f);
  830. }
  831. u32 message_count = 0;
  832. // Receive response from engine
  833. for (;;)
  834. {
  835. u32 msg_len = 0;
  836. ReadResult rr = _client.read_nonblock(&msg_len, sizeof(msg_len));
  837. if (rr.error == ReadResult::WOULDBLOCK)
  838. break;
  839. if (ReadResult::SUCCESS == rr.error)
  840. {
  841. char msg[8192];
  842. rr = _client.read(msg, msg_len);
  843. msg[msg_len] = '\0';
  844. message_count++;
  845. // logi(LEVEL_EDITOR, "count: %d", message_count);
  846. if (ReadResult::SUCCESS == rr.error)
  847. {
  848. TempAllocator4096 ta;
  849. JsonObject obj(ta);
  850. DynamicString type(ta);
  851. json::parse(obj, msg);
  852. json::parse_string(type, obj["type"]);
  853. if (type == "message")
  854. {
  855. DynamicString severity(ta);
  856. DynamicString message(ta);
  857. json::parse_string(severity, obj["severity"]);
  858. json::parse_string(message, obj["message"]);
  859. LogSeverity::Enum ls = LogSeverity::COUNT;
  860. if (strcmp("info", severity.c_str()) == 0)
  861. ls = LogSeverity::LOG_INFO;
  862. else if (strcmp("warning", severity.c_str()) == 0)
  863. ls = LogSeverity::LOG_WARN;
  864. else if (strcmp("error", severity.c_str()) == 0)
  865. ls = LogSeverity::LOG_ERROR;
  866. else
  867. CE_FATAL("Unknown severity");
  868. _console.add_log(ls, message.c_str());
  869. }
  870. else if (type == "error")
  871. {
  872. DynamicString message(ta);
  873. json::parse_string(message, obj["message"]);
  874. _console.add_log(LogSeverity::LOG_ERROR, message.c_str());
  875. }
  876. else
  877. {
  878. _console.add_log(LogSeverity::LOG_ERROR, "Unknown message type");
  879. }
  880. console_scroll_to_bottom(_console);
  881. }
  882. }
  883. }
  884. imgui_begin_frame(VIEW_IMGUI, _width, _height);
  885. ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None;
  886. // We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
  887. // because it would be confusing to have two docking targets within each others.
  888. ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
  889. ImGuiViewport* viewport = ImGui::GetMainViewport();
  890. ImGui::SetNextWindowPos(viewport->Pos);
  891. ImGui::SetNextWindowSize(viewport->Size);
  892. ImGui::SetNextWindowViewport(viewport->ID);
  893. ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
  894. ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
  895. window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
  896. window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
  897. // When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background and handle the pass-through hole, so we ask Begin() to not render a background.
  898. if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
  899. window_flags |= ImGuiWindowFlags_NoBackground;
  900. ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
  901. ImGui::Begin("DockSpace Demo", NULL, window_flags);
  902. ImGui::PopStyleVar(3);
  903. // DockSpace
  904. ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
  905. ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
  906. main_menu_bar();
  907. ImGui::End();
  908. if (_scene_view._open)
  909. {
  910. _scene_view.draw();
  911. // Draw toolbar overlay
  912. ImVec2 window_pos;
  913. window_pos.x = _scene_view._origin.x + 6.0f;
  914. window_pos.y = _scene_view._origin.y + 6.0f;
  915. ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always);
  916. ImGui::SetNextWindowBgAlpha(0.35f);
  917. if (ImGui::Begin("Toobar Overlay"
  918. , NULL
  919. , ImGuiWindowFlags_NoMove
  920. | ImGuiWindowFlags_NoDecoration
  921. | ImGuiWindowFlags_AlwaysAutoResize
  922. | ImGuiWindowFlags_NoSavedSettings
  923. | ImGuiWindowFlags_NoFocusOnAppearing
  924. | ImGuiWindowFlags_NoNav
  925. ))
  926. {
  927. ImGui::BeginGroup();
  928. if (ImGui::ImageButton(tool_place_texture->handle, ImVec2(16, 16)))
  929. {
  930. _tool_type = tool::ToolType::PLACE;
  931. tool_send_state();
  932. }
  933. if (ImGui::ImageButton(tool_move_texture->handle, ImVec2(16, 16)))
  934. {
  935. _tool_type = tool::ToolType::MOVE;
  936. tool_send_state();
  937. }
  938. if (ImGui::ImageButton(tool_rotate_texture->handle, ImVec2(16, 16)))
  939. {
  940. _tool_type = tool::ToolType::ROTATE;
  941. tool_send_state();
  942. }
  943. if (ImGui::ImageButton(tool_scale_texture->handle, ImVec2(16, 16)))
  944. {
  945. _tool_type = tool::ToolType::SCALE;
  946. tool_send_state();
  947. }
  948. ImGui::Separator();
  949. if (ImGui::ImageButton(axis_local_texture->handle, ImVec2(16, 16)))
  950. {
  951. _reference_system = tool::ReferenceSystem::LOCAL;
  952. tool_send_state();
  953. }
  954. if (ImGui::ImageButton(axis_world_texture->handle, ImVec2(16, 16)))
  955. {
  956. _reference_system = tool::ReferenceSystem::WORLD;
  957. tool_send_state();
  958. }
  959. ImGui::Separator();
  960. if (ImGui::ImageButton(reference_world_texture->handle, ImVec2(16, 16)))
  961. {
  962. _snap_mode = tool::SnapMode::RELATIVE;
  963. tool_send_state();
  964. }
  965. if (ImGui::ImageButton(reference_local_texture->handle, ImVec2(16, 16)))
  966. {
  967. _snap_mode = tool::SnapMode::ABSOLUTE;
  968. tool_send_state();
  969. }
  970. ImGui::Separator();
  971. if (ImGui::ImageButton(snap_to_grid_texture->handle, ImVec2(16, 16)))
  972. {
  973. _snap_to_grid = !_snap_to_grid;
  974. tool_send_state();
  975. }
  976. ImGui::EndGroup();
  977. }
  978. ImGui::End();
  979. }
  980. if (_scene_tree._open)
  981. _scene_tree.draw();
  982. if (_inspector._open)
  983. _inspector.draw();
  984. if (_console._open)
  985. console_draw(_console, _client);
  986. if (_animator._open)
  987. _animator.draw();
  988. // _sprite_importer.draw();
  989. imgui_end_frame();
  990. }
  991. void send_command(StringStream& ss)
  992. {
  993. console_send_script(_client, string_stream::c_str(ss));
  994. }
  995. void tool_send_state()
  996. {
  997. TempAllocator256 ta;
  998. StringStream ss(ta);
  999. tool::set_grid_size(ss, _grid_size);
  1000. tool::set_rotation_snap(ss, _rotation_snap);
  1001. tool::enable_show_grid(ss, _show_grid);
  1002. tool::enable_snap_to_grid(ss, _snap_to_grid);
  1003. tool::enable_debug_render_world(ss, _debug_render_world);
  1004. tool::enable_debug_physics_world(ss, _debug_physics_world);
  1005. tool::set_tool_type(ss, _tool_type);
  1006. tool::set_snap_mode(ss, _snap_mode);
  1007. tool::set_reference_system(ss, _reference_system);
  1008. send_command(ss);
  1009. }
  1010. void format_time(uint64_t time, char* str, size_t size)
  1011. {
  1012. #if CROWN_PLATFORM_WINDOWS
  1013. // SYSTEMTIME st;
  1014. // FileTimeToSystemTime(&time, &st);
  1015. // sprintf(str, "%d/%d/%d %d:%d"
  1016. // , st.wDay
  1017. // , st.wMonth
  1018. // , st.wYear
  1019. // , st.wHour
  1020. // , st.wMinute);
  1021. #else
  1022. struct tm lt;
  1023. localtime_r((time_t*)&time, &lt);
  1024. strftime(str, size, "%d/%m/%Y %H:%M", &lt);
  1025. #endif
  1026. }
  1027. void main_menu_bar()
  1028. {
  1029. // Main Menu
  1030. if (ImGui::BeginMainMenuBar())
  1031. {
  1032. if (ImGui::BeginMenu("File"))
  1033. {
  1034. if (ImGui::MenuItem("New"))
  1035. {
  1036. }
  1037. if (ImGui::MenuItem("Open", "Ctrl+O"))
  1038. {
  1039. nfdchar_t *out_path = NULL;
  1040. nfdresult_t result = NFD_OpenDialog(NULL, NULL, &out_path);
  1041. if ( result == NFD_OKAY )
  1042. {
  1043. logi(LEVEL_EDITOR, "Success!");
  1044. logi(LEVEL_EDITOR, out_path);
  1045. free(out_path);
  1046. }
  1047. else if ( result == NFD_CANCEL )
  1048. {
  1049. logi(LEVEL_EDITOR, "User pressed cancel.");
  1050. }
  1051. else
  1052. {
  1053. loge(LEVEL_EDITOR, "Error: %s\n", NFD_GetError());
  1054. }
  1055. }
  1056. if (ImGui::MenuItem("Save", "Ctrl+S"))
  1057. {
  1058. }
  1059. if (ImGui::MenuItem("Save As..."))
  1060. {
  1061. }
  1062. ImGui::Separator();
  1063. if (ImGui::MenuItem("Quit", "Ctrl+Q", false, true))
  1064. {
  1065. TempAllocator64 ta;
  1066. StringStream ss(ta);
  1067. tool::device_quit(ss);
  1068. send_command(ss);
  1069. }
  1070. ImGui::EndMenu();
  1071. }
  1072. if (ImGui::BeginMenu("Edit"))
  1073. {
  1074. if (ImGui::MenuItem("Undo", "Ctrl+Z")) {}
  1075. if (ImGui::MenuItem("Redo", "Ctrl+Y")) {}
  1076. ImGui::EndMenu();
  1077. }
  1078. if (ImGui::BeginMenu("Create"))
  1079. {
  1080. TempAllocator256 ta;
  1081. StringStream ss(ta);
  1082. if (ImGui::BeginMenu("Primitives"))
  1083. {
  1084. if (ImGui::MenuItem("Cube", NULL, false, true))
  1085. {
  1086. _tool_type = tool::ToolType::PLACE;
  1087. tool_send_state();
  1088. tool::set_placeable(ss, "unit", "core/units/primitives/cube");
  1089. send_command(ss);
  1090. }
  1091. if (ImGui::MenuItem("Sphere"))
  1092. {
  1093. _tool_type = tool::ToolType::PLACE;
  1094. tool_send_state();
  1095. tool::set_placeable(ss, "unit", "core/units/primitives/sphere");
  1096. send_command(ss);
  1097. }
  1098. if (ImGui::MenuItem("Cone"))
  1099. {
  1100. _tool_type = tool::ToolType::PLACE;
  1101. tool_send_state();
  1102. tool::set_placeable(ss, "unit", "core/units/primitives/cone");
  1103. send_command(ss);
  1104. }
  1105. if (ImGui::MenuItem("Cylinder"))
  1106. {
  1107. _tool_type = tool::ToolType::PLACE;
  1108. tool_send_state();
  1109. tool::set_placeable(ss, "unit", "core/units/primitives/cylinder");
  1110. send_command(ss);
  1111. }
  1112. if (ImGui::MenuItem("Plane"))
  1113. {
  1114. _tool_type = tool::ToolType::PLACE;
  1115. tool_send_state();
  1116. tool::set_placeable(ss, "unit", "core/units/primitives/plane");
  1117. send_command(ss);
  1118. }
  1119. ImGui::EndMenu();
  1120. }
  1121. if (ImGui::MenuItem("Camera"))
  1122. {
  1123. _tool_type = tool::ToolType::PLACE;
  1124. tool_send_state();
  1125. tool::set_placeable(ss, "unit", "core/units/camera");
  1126. send_command(ss);
  1127. }
  1128. if (ImGui::MenuItem("Light"))
  1129. {
  1130. _tool_type = tool::ToolType::PLACE;
  1131. tool_send_state();
  1132. tool::set_placeable(ss, "unit", "core/units/light");
  1133. send_command(ss);
  1134. }
  1135. if (ImGui::MenuItem("Sound"))
  1136. {
  1137. _tool_type = tool::ToolType::PLACE;
  1138. tool_send_state();
  1139. tool::set_placeable(ss, "sound", "core/units/camera");
  1140. send_command(ss);
  1141. }
  1142. ImGui::EndMenu();
  1143. }
  1144. if (ImGui::BeginMenu("Scene View"))
  1145. {
  1146. TempAllocator256 ta;
  1147. StringStream ss(ta);
  1148. if (ImGui::BeginMenu("Camera View"))
  1149. {
  1150. if (ImGui::MenuItem("Perspective"))
  1151. {
  1152. tool::camera_view_perspective(ss);
  1153. send_command(ss);
  1154. }
  1155. if (ImGui::MenuItem("Front"))
  1156. {
  1157. tool::camera_view_front(ss);
  1158. send_command(ss);
  1159. }
  1160. if (ImGui::MenuItem("Back"))
  1161. {
  1162. tool::camera_view_back(ss);
  1163. send_command(ss);
  1164. }
  1165. if (ImGui::MenuItem("Right"))
  1166. {
  1167. tool::camera_view_right(ss);
  1168. send_command(ss);
  1169. }
  1170. if (ImGui::MenuItem("Left"))
  1171. {
  1172. tool::camera_view_left(ss);
  1173. send_command(ss);
  1174. }
  1175. if (ImGui::MenuItem("Top"))
  1176. {
  1177. tool::camera_view_top(ss);
  1178. send_command(ss);
  1179. }
  1180. if (ImGui::MenuItem("Bottom"))
  1181. {
  1182. tool::camera_view_bottom(ss);
  1183. send_command(ss);
  1184. }
  1185. ImGui::EndMenu();
  1186. }
  1187. ImGui::Separator();
  1188. if (ImGui::MenuItem("Show Grid", NULL, _show_grid))
  1189. {
  1190. _show_grid = !_show_grid;
  1191. tool_send_state();
  1192. }
  1193. if (ImGui::SliderFloat("Grid Size", &_grid_size, 0.1f, 5.0f))
  1194. {
  1195. tool_send_state();
  1196. }
  1197. if (ImGui::SliderFloat("Snap Rot", &_rotation_snap, 1, 180))
  1198. {
  1199. tool_send_state();
  1200. }
  1201. ImGui::Separator();
  1202. if (ImGui::MenuItem("Toggle Full Screen", "F11"))
  1203. {
  1204. _fullscreen = !_fullscreen;
  1205. device()->_window->set_fullscreen(_fullscreen);
  1206. }
  1207. ImGui::EndMenu();
  1208. }
  1209. if (ImGui::BeginMenu("Windows"))
  1210. {
  1211. if (ImGui::MenuItem("Scene"))
  1212. {
  1213. _scene_view._open = true;
  1214. }
  1215. if (ImGui::MenuItem("Scene Tree"))
  1216. {
  1217. _scene_tree._open = true;
  1218. }
  1219. if (ImGui::MenuItem("Inspector"))
  1220. {
  1221. _inspector._open = true;
  1222. }
  1223. if (ImGui::MenuItem("Console"))
  1224. {
  1225. _console._open = true;
  1226. }
  1227. if (ImGui::MenuItem("Animator"))
  1228. {
  1229. _animator._open = true;
  1230. }
  1231. ImGui::EndMenu();
  1232. }
  1233. if (ImGui::BeginMenu("Test"))
  1234. {
  1235. if (ImGui::MenuItem("Test Level"))
  1236. start_game(StartGame::TEST, _project->_data_dir.c_str());
  1237. if (ImGui::MenuItem("Test Game"))
  1238. start_game(StartGame::NORMAL, _project->_data_dir.c_str());
  1239. if (ImGui::MenuItem("Stop"))
  1240. stop_game();
  1241. ImGui::EndMenu();
  1242. }
  1243. if (ImGui::BeginMenu("Help"))
  1244. {
  1245. ImGui::MenuItem("About", "");
  1246. ImGui::EndMenu();
  1247. }
  1248. _main_menu_pos = ImGui::GetWindowPos();
  1249. _main_menu_size = ImGui::GetWindowSize();
  1250. ImGui::EndMainMenuBar();
  1251. }
  1252. }
  1253. };
  1254. void tool_init()
  1255. {
  1256. const DeviceOptions& opt = device()->_options;
  1257. _project = CE_NEW(default_allocator(), Project)(default_allocator());
  1258. _project->_data_dir = opt._data_dir;
  1259. _editor = CE_NEW(default_allocator(), LevelEditor)(opt._source_dir);
  1260. _client.connect(IP_ADDRESS_LOOPBACK, CROWN_DEFAULT_CONSOLE_PORT);
  1261. }
  1262. void tool_update(f32 dt)
  1263. {
  1264. _editor->update(dt);
  1265. }
  1266. void tool_shutdown()
  1267. {
  1268. _client.close();
  1269. CE_DELETE(default_allocator(), _editor);
  1270. CE_DELETE(default_allocator(), _project);
  1271. stop_game();
  1272. }
  1273. extern bool next_event(OsEvent& ev);
  1274. bool tool_process_events()
  1275. {
  1276. ImGuiIO& io = ImGui::GetIO();
  1277. bool exit = false;
  1278. bool reset = false;
  1279. TempAllocator4096 ta;
  1280. StringStream ss(ta);
  1281. OsEvent event;
  1282. while (next_event(event))
  1283. {
  1284. switch (event.type)
  1285. {
  1286. case OsEventType::BUTTON:
  1287. switch (event.button.device_id)
  1288. {
  1289. case crown::InputDeviceType::KEYBOARD:
  1290. io.KeysDown[event.button.button_num] = event.button.pressed;
  1291. io.KeyCtrl = io.KeysDown[crown::KeyboardButton::CTRL_LEFT] || io.KeysDown[crown::KeyboardButton::CTRL_RIGHT];
  1292. io.KeyShift = io.KeysDown[crown::KeyboardButton::SHIFT_LEFT] || io.KeysDown[crown::KeyboardButton::SHIFT_RIGHT];
  1293. io.KeyAlt = io.KeysDown[crown::KeyboardButton::ALT_LEFT] || io.KeysDown[crown::KeyboardButton::ALT_RIGHT];
  1294. io.KeySuper = io.KeysDown[crown::KeyboardButton::SUPER_LEFT] || io.KeysDown[crown::KeyboardButton::SUPER_RIGHT];
  1295. if (!io.WantCaptureKeyboard)
  1296. {
  1297. if (event.button.pressed)
  1298. {
  1299. if (event.button.button_num == crown::KeyboardButton::W)
  1300. tool::key_down(ss, "w");
  1301. if (event.button.button_num == crown::KeyboardButton::A)
  1302. tool::key_down(ss, "a");
  1303. if (event.button.button_num == crown::KeyboardButton::S)
  1304. tool::key_down(ss, "s");
  1305. if (event.button.button_num == crown::KeyboardButton::D)
  1306. tool::key_down(ss, "d");
  1307. if (event.button.button_num == crown::KeyboardButton::CTRL_LEFT)
  1308. tool::key_down(ss, "ctrl_left");
  1309. if (event.button.button_num == crown::KeyboardButton::SHIFT_LEFT)
  1310. tool::key_down(ss, "shift_left");
  1311. if (event.button.button_num == crown::KeyboardButton::ALT_LEFT)
  1312. tool::key_down(ss, "alt_left");
  1313. }
  1314. else
  1315. {
  1316. if (event.button.button_num == crown::KeyboardButton::W)
  1317. tool::key_up(ss, "w");
  1318. if (event.button.button_num == crown::KeyboardButton::A)
  1319. tool::key_up(ss, "a");
  1320. if (event.button.button_num == crown::KeyboardButton::S)
  1321. tool::key_up(ss, "s");
  1322. if (event.button.button_num == crown::KeyboardButton::D)
  1323. tool::key_up(ss, "d");
  1324. if (event.button.button_num == crown::KeyboardButton::CTRL_LEFT)
  1325. tool::key_up(ss, "ctrl_left");
  1326. if (event.button.button_num == crown::KeyboardButton::SHIFT_LEFT)
  1327. tool::key_up(ss, "shift_left");
  1328. if (event.button.button_num == crown::KeyboardButton::ALT_LEFT)
  1329. tool::key_up(ss, "alt_left");
  1330. }
  1331. }
  1332. break;
  1333. case crown::InputDeviceType::MOUSE:
  1334. io.MouseDown[event.button.button_num] = event.button.pressed;
  1335. if (!io.WantCaptureMouse)
  1336. {
  1337. ImVec2& cursor = _editor->_scene_view._cursor;
  1338. cursor.x = io.MousePos.x - _editor->_scene_view._origin.x;
  1339. cursor.y = io.MousePos.y - _editor->_scene_view._origin.y;
  1340. tool::set_mouse_state(ss
  1341. , cursor.x
  1342. , cursor.y
  1343. , io.MouseDown[MouseButton::LEFT]
  1344. , io.MouseDown[MouseButton::MIDDLE]
  1345. , io.MouseDown[MouseButton::RIGHT]
  1346. );
  1347. if (event.button.pressed)
  1348. {
  1349. if (io.KeysDown[crown::KeyboardButton::ALT_LEFT])
  1350. {
  1351. if (event.button.button_num == crown::MouseButton::LEFT)
  1352. ss << "LevelEditor:camera_drag_start('tumble')";
  1353. if (event.button.button_num == crown::MouseButton::MIDDLE)
  1354. ss << "LevelEditor:camera_drag_start('track')";
  1355. if (event.button.button_num == crown::MouseButton::RIGHT)
  1356. ss << "LevelEditor:camera_drag_start('dolly')";
  1357. }
  1358. else
  1359. {
  1360. tool::mouse_down(ss, cursor.x, cursor.y);
  1361. }
  1362. }
  1363. else
  1364. {
  1365. if (io.KeysDown[crown::KeyboardButton::ALT_LEFT])
  1366. {
  1367. if (event.button.button_num != crown::MouseButton::LEFT
  1368. || event.button.button_num != crown::MouseButton::MIDDLE
  1369. || event.button.button_num != crown::MouseButton::RIGHT
  1370. )
  1371. {
  1372. ss << "LevelEditor:camera_drag_start('idle')";
  1373. }
  1374. }
  1375. else
  1376. {
  1377. tool::mouse_up(ss, cursor.x, cursor.y);
  1378. }
  1379. }
  1380. }
  1381. break;
  1382. }
  1383. break;
  1384. case OsEventType::AXIS:
  1385. switch(event.axis.device_id)
  1386. {
  1387. case InputDeviceType::MOUSE:
  1388. switch(event.axis.axis_num)
  1389. {
  1390. case crown::MouseAxis::CURSOR:
  1391. io.MousePos = ImVec2(event.axis.axis_x, event.axis.axis_y);
  1392. if (!io.WantCaptureMouse)
  1393. {
  1394. ImVec2& cursor = _editor->_scene_view._cursor;
  1395. cursor.x = io.MousePos.x - _editor->_scene_view._origin.x;
  1396. cursor.y = io.MousePos.y - _editor->_scene_view._origin.y;
  1397. tool::set_mouse_state(ss
  1398. , cursor.x
  1399. , cursor.y
  1400. , io.MouseDown[MouseButton::LEFT]
  1401. , io.MouseDown[MouseButton::MIDDLE]
  1402. , io.MouseDown[MouseButton::RIGHT]
  1403. );
  1404. }
  1405. break;
  1406. case crown::MouseAxis::WHEEL:
  1407. io.MouseWheel += event.axis.axis_y;
  1408. if (!io.WantCaptureMouse)
  1409. tool::mouse_wheel(ss, io.MouseWheel);
  1410. break;
  1411. }
  1412. }
  1413. break;
  1414. case OsEventType::TEXT:
  1415. if (event.text.utf8[0] < 0x7f)
  1416. io.AddInputCharactersUTF8((const char*)event.text.utf8);
  1417. break;
  1418. case OsEventType::RESOLUTION:
  1419. _width = event.resolution.width;
  1420. _height = event.resolution.height;
  1421. reset = true;
  1422. break;
  1423. case OsEventType::EXIT:
  1424. exit = true;
  1425. break;
  1426. default:
  1427. break;
  1428. }
  1429. }
  1430. if (array::size(ss) > 0)
  1431. _editor->send_command(ss);
  1432. bool vsync = true;
  1433. if (reset)
  1434. bgfx::reset(_width, _height, (vsync ? BGFX_RESET_VSYNC : BGFX_RESET_NONE));
  1435. return exit;
  1436. }
  1437. } // namespace crown
  1438. #endif // CROWN_TOOLS