level_editor.cpp 41 KB

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