level_editor.cpp 40 KB

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