level_editor.cpp 42 KB

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