level_editor.cpp 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225
  1. /*
  2. * Copyright (c) 2012-2017 Daniele Bartolini and individual contributors.
  3. * License: https://github.com/dbartolini/crown/blob/master/LICENSE
  4. */
  5. #if CROWN_TOOLS
  6. #include <imgui.h>
  7. #include <iconfontheaders/icons_material_design.h>
  8. #include <time.h>
  9. #include "core/containers/vector.h"
  10. #include "core/filesystem/filesystem_disk.h"
  11. #include "core/filesystem/path.h"
  12. #include "core/filesystem/file.h"
  13. #include "core/json/json.h"
  14. #include "core/json/sjson.h"
  15. #include "core/json/json_object.h"
  16. #include "core/network/socket.h"
  17. #include "core/strings/dynamic_string.h"
  18. #include "device/device.h"
  19. #include "device/device_event_queue.h"
  20. #include "device/input_device.h"
  21. #include "device/input_manager.h"
  22. #include "device/input_types.h"
  23. #include "device/device_options.h"
  24. #include "device/log.h"
  25. #include "resource/resource_manager.h"
  26. #include "resource/texture_resource.h"
  27. #include "device/pipeline.h"
  28. #include "imgui_context.h"
  29. #include "tool_api.h"
  30. #if CROWN_PLATFORM_POSIX
  31. #include <sys/time.h>
  32. #endif
  33. namespace { const crown::log_internal::System LEVEL_EDITOR = { "LevelEditor" }; }
  34. namespace crown
  35. {
  36. static u16 _width = 1280;
  37. static u16 _height = 720;
  38. //-----------------------------------------------------------------------------
  39. struct Inspector
  40. {
  41. // Inspector
  42. char _name[1024];
  43. float _position[3];
  44. float _rotation[3];
  45. float _scale[3];
  46. char _sprite[1024];
  47. char _material[1024];
  48. bool _visible;
  49. char _state_machine[1024];
  50. bool _open;
  51. Inspector()
  52. : _visible(true)
  53. , _open(true)
  54. {
  55. memset(_name, 0, sizeof(_name));
  56. memset(_sprite, 0, sizeof(_sprite));
  57. memset(_material, 0, sizeof(_material));
  58. memset(_position, 0, sizeof(_position));
  59. memset(_rotation, 0, sizeof(_rotation));
  60. memset(_scale, 0, sizeof(_scale));
  61. memset(_state_machine, 0, sizeof(_state_machine));
  62. }
  63. void draw()
  64. {
  65. if (!_open) return;
  66. if (ImGui::BeginDock("Inspector", &_open))
  67. {
  68. if (ImGui::TreeNodeEx("Unit", ImGuiTreeNodeFlags_DefaultOpen))
  69. {
  70. ImGui::InputText("Name", _name, sizeof(_name));
  71. ImGui::TreePop();
  72. }
  73. if (ImGui::TreeNodeEx("Transform", ImGuiTreeNodeFlags_DefaultOpen))
  74. {
  75. ImGui::InputFloat3("Position", _position, ImGuiInputTextFlags_CharsDecimal);
  76. ImGui::InputFloat3("Rotation", _rotation, ImGuiInputTextFlags_CharsDecimal);
  77. ImGui::InputFloat3("Scale", _scale, ImGuiInputTextFlags_CharsDecimal);
  78. ImGui::TreePop();
  79. }
  80. if (ImGui::TreeNodeEx("Renderer", ImGuiTreeNodeFlags_DefaultOpen))
  81. {
  82. ImGui::InputText("Sprite", _sprite, sizeof(_sprite));
  83. ImGui::InputText("Material", _material, sizeof(_material));
  84. ImGui::Checkbox("Visible", &_visible);
  85. ImGui::TreePop();
  86. }
  87. if (ImGui::TreeNodeEx("Animation", ImGuiTreeNodeFlags_DefaultOpen))
  88. {
  89. ImGui::InputText("State Machine", _state_machine, sizeof(_state_machine));
  90. ImGui::TreePop();
  91. }
  92. }
  93. ImGui::EndDock();
  94. }
  95. };
  96. //-----------------------------------------------------------------------------
  97. struct SceneView
  98. {
  99. ImVec2 _pos;
  100. ImVec2 _size;
  101. ImVec2 _mouse_curr;
  102. ImVec2 _mouse_last;
  103. bool _open;
  104. SceneView()
  105. : _open(true)
  106. {
  107. }
  108. void draw()
  109. {
  110. if (!_open) return;
  111. ImGui::SetNextWindowPos(ImVec2(0, 25));
  112. if (ImGui::BeginDock("Scene View"
  113. , &_open
  114. , ImGuiWindowFlags_NoScrollbar
  115. | ImGuiWindowFlags_NoScrollWithMouse
  116. ))
  117. {
  118. uint16_t w, h;
  119. device()->resolution(w, h);
  120. bgfx::TextureHandle txh = device()->_pipeline->_buffers[0];
  121. CE_ENSURE(bgfx::isValid(txh));
  122. ImGui::Image((void*)(uintptr_t)txh.idx
  123. , ImVec2(w, h)
  124. #if CROWN_PLATFORM_WINDOWS
  125. , ImVec2(0, 0)
  126. , ImVec2(1, 1)
  127. #else
  128. , ImVec2(0, 1)
  129. , ImVec2(1, 0)
  130. #endif // CROWN_PLATFORM_WINDOWS
  131. );
  132. if (ImGui::IsWindowHovered())
  133. {
  134. // send all input to engine
  135. ImGui::CaptureMouseFromApp(false);
  136. ImGui::CaptureKeyboardFromApp(false);
  137. }
  138. else
  139. {
  140. // send all input to imgui
  141. ImGui::CaptureMouseFromApp(true);
  142. ImGui::CaptureKeyboardFromApp(true);
  143. }
  144. }
  145. _pos = ImGui::GetWindowPos();
  146. _size = ImGui::GetWindowSize();
  147. ImGui::EndDock();
  148. }
  149. };
  150. //-----------------------------------------------------------------------------
  151. struct SceneTree
  152. {
  153. bool _open;
  154. SceneTree()
  155. : _open(true)
  156. {
  157. }
  158. void draw()
  159. {
  160. if (!_open) return;
  161. if (ImGui::BeginDock("Scene Tree", &_open))
  162. {
  163. if (ImGui::TreeNodeEx("Units", ImGuiTreeNodeFlags_DefaultOpen))
  164. {
  165. if (ImGui::TreeNodeEx("Objects", ImGuiTreeNodeFlags_DefaultOpen))
  166. {
  167. for (int i = 0; i < 5; i++)
  168. if (ImGui::TreeNode((void*)(intptr_t)i, "Child %d", i))
  169. {
  170. ImGui::Text("blah blah");
  171. ImGui::SameLine();
  172. if (ImGui::SmallButton("print")) printf("Child %d pressed", i);
  173. ImGui::TreePop();
  174. }
  175. ImGui::TreePop();
  176. }
  177. if (ImGui::TreeNodeEx("Lights", ImGuiTreeNodeFlags_DefaultOpen))
  178. {
  179. // 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.");
  180. static bool align_label_with_current_x_position = false;
  181. ImGui::Checkbox("Align label with current X position)", &align_label_with_current_x_position);
  182. ImGui::Text("Hello!");
  183. if (align_label_with_current_x_position)
  184. ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing());
  185. 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.
  186. 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.
  187. ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, ImGui::GetFontSize()*3); // Increase spacing to differentiate leaves from expanded contents.
  188. for (int i = 0; i < 6; i++)
  189. {
  190. // Disable the default open on single-click behavior and pass in Selected flag according to our selection state.
  191. ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ((selection_mask & (1 << i)) ? ImGuiTreeNodeFlags_Selected : 0);
  192. if (i < 3)
  193. {
  194. // Node
  195. bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i);
  196. if (ImGui::IsItemClicked())
  197. node_clicked = i;
  198. if (node_open)
  199. {
  200. ImGui::Text("Blah blah\nBlah Blah");
  201. ImGui::TreePop();
  202. }
  203. }
  204. else
  205. {
  206. // 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().
  207. ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen, "Selectable Leaf %d", i);
  208. if (ImGui::IsItemClicked())
  209. node_clicked = i;
  210. }
  211. }
  212. if (node_clicked != -1)
  213. {
  214. // Update selection state. Process outside of tree loop to avoid visual inconsistencies during the clicking-frame.
  215. if (ImGui::GetIO().KeyCtrl)
  216. selection_mask ^= (1 << node_clicked); // Ctrl+click to toggle
  217. 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
  218. selection_mask = (1 << node_clicked); // Click to single-select
  219. }
  220. ImGui::PopStyleVar();
  221. if (align_label_with_current_x_position)
  222. ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing());
  223. ImGui::TreePop();
  224. }
  225. ImGui::TreePop();
  226. }
  227. }
  228. ImGui::EndDock(); // End Object List
  229. }
  230. };
  231. //-----------------------------------------------------------------------------
  232. struct SpriteAnimator
  233. {
  234. bool _open;
  235. bool _add_animation_popup_open;
  236. Array<const char*> _entities;
  237. s32 _cur_entity;
  238. TextureResource* _texture;
  239. u32 _texture_width;
  240. u32 _texture_height;
  241. struct Frame
  242. {
  243. char name[512];
  244. ImVec2 pivot;
  245. ImVec4 region;
  246. };
  247. Array<Frame> _frames;
  248. char _anim_name[512];
  249. f32 _anim_time;
  250. Array<const char*> _listbox_items;
  251. s32 _listbox_item_current;
  252. Array<Frame> _anim_preview_frames;
  253. f32 _delta;
  254. u32 current;
  255. Vector<DynamicString> file_list_sprites;
  256. FilesystemDisk* _fs;
  257. SpriteAnimator(const DynamicString& src_dir)
  258. : _open(true)
  259. , _add_animation_popup_open(false)
  260. , _entities(default_allocator())
  261. , _cur_entity(0)
  262. , _texture(nullptr)
  263. , _texture_width(0)
  264. , _texture_height(0)
  265. , _frames(default_allocator())
  266. , _anim_time(0.1f)
  267. , _listbox_items(default_allocator())
  268. , _listbox_item_current(0)
  269. , _anim_preview_frames(default_allocator())
  270. , _delta(0.0f)
  271. , current(0)
  272. , file_list_sprites(default_allocator())
  273. {
  274. memset(_anim_name, 0, sizeof(_anim_name));
  275. _fs = CE_NEW(default_allocator(), FilesystemDisk)(default_allocator());
  276. _fs->set_prefix(src_dir.c_str());
  277. get_sprites_list();
  278. }
  279. ~SpriteAnimator()
  280. {
  281. CE_DELETE(default_allocator(), _fs);
  282. }
  283. ImVec2 pixel_to_uv(uint32_t tex_w, uint32_t tex_h, float x, float y)
  284. {
  285. ImVec2 uv;
  286. uv.x = (float)x / (float)tex_w;
  287. uv.y = (float)y / (float)tex_h;
  288. return uv;
  289. }
  290. void get_directory(DynamicString& dir, const char* path)
  291. {
  292. CE_ENSURE(NULL != path);
  293. const char* ls = strrchr(path, '/');
  294. u32 file_len = strlen(ls+1);
  295. u32 dir_len = strlen(path) - file_len;
  296. char buff[1024];
  297. memcpy(buff, path, dir_len);
  298. buff[dir_len] = '\0';
  299. path::reduce(dir, buff);
  300. }
  301. void save_sprite_animation()
  302. {
  303. TempAllocator4096 ta;
  304. StringStream ss(ta);
  305. ss << "frames = [ ";
  306. for (u32 i = 0; i < array::size(_listbox_items); i++)
  307. {
  308. ss << _listbox_items[i] + 7 << " ";
  309. }
  310. ss << "]\n";
  311. ss << "total_time = ";
  312. ss << _anim_time;
  313. DynamicString dir(ta);
  314. get_directory(dir, _entities[_cur_entity]);
  315. DynamicString file_name(ta);
  316. path::join(file_name, dir.c_str(), _anim_name);
  317. file_name += ".sprite_animation";
  318. File* f = _fs->open(file_name.c_str(), FileOpenMode::WRITE);
  319. f->write(string_stream::c_str(ss), strlen(string_stream::c_str(ss)));
  320. f->close();
  321. }
  322. void get_files_list(Vector<DynamicString>& out, const char* path="")
  323. {
  324. TempAllocator4096 ta;
  325. Vector<DynamicString> files(ta);
  326. _fs->list_files(path, files);
  327. for (u32 i = 0; i < vector::size(files); i++)
  328. {
  329. DynamicString join(ta);
  330. path::join(join, path, files[i].c_str());
  331. join = join.c_str()[0] == '/' ? join.c_str()+1 : join.c_str();
  332. if (_fs->is_directory(join.c_str()))
  333. {
  334. get_files_list(out, join.c_str());
  335. }
  336. else
  337. {
  338. vector::push_back(out, join);
  339. }
  340. }
  341. }
  342. void get_sprites_list()
  343. {
  344. TempAllocator4096 ta;
  345. Vector<DynamicString> files(ta);
  346. get_files_list(files);
  347. for (DynamicString* f = vector::begin(files); f != vector::end(files); f++)
  348. if (f->has_suffix(".sprite"))
  349. array::push_back(_entities, (const char*) strdup(f->c_str()));
  350. }
  351. void draw()
  352. {
  353. if (!_open) return;
  354. if (ImGui::BeginDock("Animator", &_open))
  355. {
  356. if (_texture)
  357. {
  358. Frame f = _frames[0];
  359. ImVec2 start = pixel_to_uv(_texture_width, _texture_height, f.region.x, f.region.y);
  360. ImVec2 end = pixel_to_uv(_texture_width, _texture_height, f.region.x+f.region.z, f.region.y+f.region.w);
  361. ImGui::Image((void*)(uintptr_t) _texture->handle.idx
  362. , ImVec2(f.region.z, f.region.w)
  363. , start
  364. , end
  365. , ImColor(255, 255, 255, 55)
  366. );
  367. }
  368. if (ImGui::Combo("Entities", &_cur_entity, (const char* const*) array::begin(_entities), array::size(_entities)))
  369. {
  370. array::clear(_frames);
  371. const char* sprite = _entities[_cur_entity];
  372. u32 sprite_len = strlen(sprite);
  373. char entity[1024];
  374. strncpy(entity, sprite, strlen(sprite));
  375. entity[sprite_len-7] = '\0'; // remove ".sprite"
  376. ResourceManager* resman = device()->_resource_manager;
  377. _texture = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, StringId64(entity));
  378. File* file = _fs->open(sprite, FileOpenMode::READ);
  379. const u32 size = file->size();
  380. Buffer buf(default_allocator());
  381. array::resize(buf, size);
  382. file->read(array::begin(buf), size);
  383. _fs->close(*file);
  384. JsonObject obj(default_allocator());
  385. JsonArray list(default_allocator());
  386. sjson::parse(buf, obj);
  387. sjson::parse_array(obj["frames"], list);
  388. _texture_width = sjson::parse_int(obj["width"]);
  389. _texture_height = sjson::parse_int(obj["height"]);
  390. for (uint32_t i = 0; i < array::size(list); i++)
  391. {
  392. JsonObject frame(default_allocator());
  393. DynamicString name(default_allocator());
  394. JsonArray pivot(default_allocator());
  395. JsonArray region(default_allocator());
  396. sjson::parse_object(list[i], frame);
  397. sjson::parse_array(frame["pivot"], pivot);
  398. sjson::parse_array(frame["region"], region);
  399. Frame f;
  400. sjson::parse_string(frame["name"], name);
  401. strncpy(f.name, name.c_str(), name.length());
  402. f.name[name.length()] = '\0';
  403. f.pivot.x = sjson::parse_float(pivot[0]);
  404. f.pivot.y = sjson::parse_float(pivot[1]);
  405. f.region.x = sjson::parse_float(region[0]);
  406. f.region.y = sjson::parse_float(region[1]);
  407. f.region.z = sjson::parse_float(region[2]);
  408. f.region.w = sjson::parse_float(region[3]);
  409. array::push_back(_frames, f);
  410. }
  411. }
  412. if (ImGui::Button("Add animation", ImVec2(100, 25)))
  413. {
  414. _add_animation_popup_open = true;
  415. }
  416. if (_add_animation_popup_open)
  417. {
  418. ImGui::OpenPopup("Add animation");
  419. }
  420. if (ImGui::BeginPopup("Add animation"))
  421. {
  422. ImGui::InputText("Name", _anim_name, sizeof(_anim_name));
  423. ImGui::InputFloat("Time", &_anim_time, 0.1f, 0.1f, 1);
  424. ImGui::ListBox("Animation Frames", &_listbox_item_current, (const char* const*)array::begin(_listbox_items), array::size(_listbox_items));
  425. if (ImGui::Button("Clear Frames", ImVec2(100.0f, 25.0f)))
  426. {
  427. array::clear(_listbox_items);
  428. array::clear(_anim_preview_frames);
  429. _delta = 0.0f;
  430. current = 0;
  431. }
  432. if (array::size(_anim_preview_frames) > 0)
  433. {
  434. _delta += 1.0f/60.0f;
  435. if (_delta >= _anim_time/array::size(_anim_preview_frames))
  436. {
  437. _delta = 0;
  438. current++;
  439. if (current >= array::size(_anim_preview_frames))
  440. current = 0;
  441. }
  442. Frame f = _anim_preview_frames[current];
  443. ImVec2 start = pixel_to_uv(_texture_width, _texture_height, f.region.x, f.region.y);
  444. ImVec2 end = pixel_to_uv(_texture_width, _texture_height, f.region.x+f.region.z, f.region.y+f.region.w);
  445. ImGui::Image(
  446. (void*)(uintptr_t) _texture->handle.idx
  447. , ImVec2(f.region.z, f.region.w)
  448. , start
  449. , end
  450. , ImColor(255, 255, 255, 55)
  451. );
  452. }
  453. ImGui::Separator();
  454. for (uint32_t i = 0; i < array::size(_frames); i++)
  455. {
  456. Frame f = _frames[i];
  457. ImVec2 start = pixel_to_uv(_texture_width, _texture_height, f.region.x, f.region.y);
  458. ImVec2 end = pixel_to_uv(_texture_width, _texture_height, f.region.x+f.region.z, f.region.y+f.region.w);
  459. ImGui::SameLine();
  460. if (i % 9 == 0) ImGui::NewLine();
  461. ImGui::BeginGroup();
  462. ImGui::Image(
  463. (void*)(uintptr_t) _texture->handle.idx
  464. , ImVec2(f.region.z, f.region.w)
  465. , start
  466. , end
  467. , ImColor(255, 255, 255, 55)
  468. );
  469. ImGui::NewLine();
  470. if (ImGui::Button(_frames[i].name, ImVec2(100.0f, 25.0f)))
  471. {
  472. array::push_back(_listbox_items, (const char*) strdup(_frames[i].name));
  473. array::push_back(_anim_preview_frames, _frames[i]);
  474. }
  475. ImGui::EndGroup();
  476. }
  477. if (ImGui::Button("Save", ImVec2(100, 25)))
  478. {
  479. save_sprite_animation();
  480. ImGui::CloseCurrentPopup();
  481. _add_animation_popup_open = false;
  482. }
  483. ImGui::SameLine();
  484. if (ImGui::Button("Cancel", ImVec2(100, 25)))
  485. {
  486. ImGui::CloseCurrentPopup();
  487. _add_animation_popup_open = false;
  488. }
  489. ImGui::EndPopup();
  490. }
  491. }
  492. ImGui::EndDock();
  493. }
  494. };
  495. //-----------------------------------------------------------------------------
  496. struct LevelEditor
  497. {
  498. DynamicString _source_dir;
  499. // FX
  500. TextureResource* tex_move;
  501. TextureResource* tex_place;
  502. TextureResource* tex_rotate;
  503. TextureResource* tex_scale;
  504. TextureResource* tex_ref_world;
  505. TextureResource* tex_ref_local;
  506. TextureResource* tex_axis_local;
  507. TextureResource* tex_axis_world;
  508. TextureResource* tex_snap_grid;
  509. // State
  510. float _grid_size;
  511. int32_t _rotation_snap;
  512. bool _show_grid;
  513. bool _snap_to_grid;
  514. bool _debug_render_world;
  515. bool _debug_physics_world;
  516. tool::ToolType::Enum _tool_type;
  517. tool::SnapMode::Enum _snap_mode;
  518. tool::ReferenceSystem::Enum _reference_system;
  519. ImVec2 _main_menu_pos;
  520. ImVec2 _main_menu_size;
  521. ImVec2 _toolbar_pos;
  522. ImVec2 _toolbar_size;
  523. Console _console;
  524. Inspector _inspector;
  525. SceneView _scene_view;
  526. SceneTree _scene_tree;
  527. SpriteAnimator _animator;
  528. LevelEditor(const DynamicString& source_dir)
  529. : _source_dir(default_allocator())
  530. , _grid_size(1.0f)
  531. , _rotation_snap(15)
  532. , _show_grid(true)
  533. , _snap_to_grid(true)
  534. , _debug_render_world(false)
  535. , _debug_physics_world(false)
  536. , _tool_type(tool::ToolType::MOVE)
  537. , _snap_mode(tool::SnapMode::RELATIVE)
  538. , _reference_system(tool::ReferenceSystem::LOCAL)
  539. , _main_menu_pos(0, 0)
  540. , _main_menu_size(0, 0)
  541. , _toolbar_pos(0, 0)
  542. , _toolbar_size(0, 0)
  543. , _animator(source_dir)
  544. {
  545. ResourceManager* resman = device()->_resource_manager;
  546. tex_move = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, StringId64("core/editors/gui/tool-move"));
  547. tex_place = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, StringId64("core/editors/gui/tool-place"));
  548. tex_rotate = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, StringId64("core/editors/gui/tool-rotate"));
  549. tex_scale = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, StringId64("core/editors/gui/tool-scale"));
  550. tex_ref_world = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, StringId64("core/editors/gui/reference-world"));
  551. tex_ref_local = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, StringId64("core/editors/gui/reference-local"));
  552. tex_axis_local = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, StringId64("core/editors/gui/axis-local"));
  553. tex_axis_world = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, StringId64("core/editors/gui/axis-world"));
  554. tex_snap_grid = (TextureResource*)resman->get(RESOURCE_TYPE_TEXTURE, StringId64("core/editors/gui/snap-to-grid"));
  555. imgui_create();
  556. ImGui::LoadDock();
  557. }
  558. ~LevelEditor()
  559. {
  560. ImGui::SaveDock();
  561. imgui_destroy();
  562. }
  563. void update(float dt)
  564. {
  565. CE_UNUSED(dt);
  566. static f32 last_w = 0.0f;
  567. static f32 last_h = 0.0f;
  568. if (last_w != _scene_view._size.x || last_h != _scene_view._size.y)
  569. {
  570. last_w = _scene_view._size.x;
  571. last_h = _scene_view._size.y;
  572. device()->_width = _scene_view._size.x != 0.0f ? _scene_view._size.x : 128.0f;
  573. device()->_height = _scene_view._size.y != 0.0f ? _scene_view._size.y : 128.0f;
  574. }
  575. u32 message_count = 0;
  576. // Receive response from engine
  577. for (;;)
  578. {
  579. uint32_t msg_len = 0;
  580. ReadResult rr = _console._client.read_nonblock(&msg_len, sizeof(msg_len));
  581. if (rr.error == ReadResult::WOULDBLOCK)
  582. break;
  583. if (ReadResult::SUCCESS == rr.error)
  584. {
  585. char msg[8192];
  586. rr = _console._client.read(msg, msg_len);
  587. msg[msg_len] = '\0';
  588. message_count++;
  589. // logi(LEVEL_EDITOR, "count: %d", message_count);
  590. if (ReadResult::SUCCESS == rr.error)
  591. {
  592. TempAllocator4096 ta;
  593. JsonObject obj(ta);
  594. DynamicString type(ta);
  595. json::parse(msg, obj);
  596. json::parse_string(obj["type"], type);
  597. if (type == "message")
  598. {
  599. DynamicString severity(ta);
  600. DynamicString message(ta);
  601. json::parse_string(obj["severity"], severity);
  602. json::parse_string(obj["message"], message);
  603. LogSeverity::Enum ls = LogSeverity::COUNT;
  604. if (strcmp("info", severity.c_str()) == 0)
  605. ls = LogSeverity::LOG_INFO;
  606. else if (strcmp("warning", severity.c_str()) == 0)
  607. ls = LogSeverity::LOG_WARN;
  608. else if (strcmp("error", severity.c_str()) == 0)
  609. ls = LogSeverity::LOG_ERROR;
  610. else
  611. CE_FATAL("Unknown severity");
  612. ConsoleLog log(ls, message.c_str());
  613. vector::push_back(_console._console_items, log);
  614. }
  615. else
  616. {
  617. ConsoleLog log(LogSeverity::LOG_ERROR, "Unknown message type");
  618. vector::push_back(_console._console_items, log);
  619. }
  620. console_scroll_to_bottom();
  621. }
  622. }
  623. }
  624. imgui_begin_frame(VIEW_IMGUI, _width, _height);
  625. float offset_y = _main_menu_size.y;
  626. ImGui::RootDock(ImVec2(0, offset_y), ImVec2(_width, _height-offset_y));
  627. main_menu_bar();
  628. // toolbar();
  629. _scene_view.draw();
  630. _scene_tree.draw();
  631. _inspector.draw();
  632. console_draw(_console);
  633. _animator.draw();
  634. imgui_end_frame();
  635. }
  636. void send_command(StringStream& ss)
  637. {
  638. TempAllocator4096 ta;
  639. StringStream out(ta);
  640. out << "{\"type\":\"script\",\"script\":\"";
  641. out << string_stream::c_str(ss);
  642. out << "\"}";
  643. const char* cmd = string_stream::c_str(out);
  644. const uint32_t size = strlen32(cmd);
  645. _console._client.write(&size, sizeof(uint32_t));
  646. _console._client.write(cmd, size);
  647. }
  648. void tool_send_state()
  649. {
  650. TempAllocator256 ta;
  651. StringStream ss(ta);
  652. tool::set_grid_size(ss, _grid_size);
  653. tool::set_rotation_snap(ss, _rotation_snap);
  654. tool::enable_show_grid(ss, _show_grid);
  655. tool::enable_snap_to_grid(ss, _snap_to_grid);
  656. tool::enable_debug_render_world(ss, _debug_render_world);
  657. tool::enable_debug_physics_world(ss, _debug_physics_world);
  658. tool::set_tool_type(ss, _tool_type);
  659. tool::set_snap_mode(ss, _snap_mode);
  660. tool::set_reference_system(ss, _reference_system);
  661. send_command(ss);
  662. }
  663. void format_time(uint64_t time, char* str, size_t size)
  664. {
  665. #if CROWN_PLATFORM_WINDOWS
  666. // SYSTEMTIME st;
  667. // FileTimeToSystemTime(&time, &st);
  668. // sprintf(str, "%d/%d/%d %d:%d"
  669. // , st.wDay
  670. // , st.wMonth
  671. // , st.wYear
  672. // , st.wHour
  673. // , st.wMinute);
  674. #else
  675. struct tm lt;
  676. localtime_r((time_t*)&time, &lt);
  677. strftime(str, size, "%d/%m/%Y %H:%M", &lt);
  678. #endif
  679. }
  680. void main_menu_bar()
  681. {
  682. // Main Menu
  683. if (ImGui::BeginMainMenuBar())
  684. {
  685. if (ImGui::BeginMenu("File"))
  686. {
  687. if (ImGui::MenuItem("New"))
  688. {
  689. }
  690. if (ImGui::MenuItem("Open", "Ctrl+O"))
  691. {
  692. }
  693. if (ImGui::MenuItem("Save", "Ctrl+S"))
  694. {
  695. }
  696. if (ImGui::MenuItem("Save As..."))
  697. {
  698. }
  699. ImGui::Separator();
  700. if (ImGui::MenuItem("Quit", "Ctrl+Q", false, true))
  701. {
  702. TempAllocator64 ta;
  703. StringStream ss(ta);
  704. tool::device_quit(ss);
  705. send_command(ss);
  706. }
  707. ImGui::EndMenu();
  708. }
  709. if (ImGui::BeginMenu("Edit"))
  710. {
  711. if (ImGui::MenuItem("Undo", "Ctrl+Z")) {}
  712. if (ImGui::MenuItem("Redo", "Ctrl+Y")) {}
  713. ImGui::EndMenu();
  714. }
  715. if (ImGui::BeginMenu("Create"))
  716. {
  717. TempAllocator256 ta;
  718. StringStream ss(ta);
  719. if (ImGui::BeginMenu("Primitives"))
  720. {
  721. if (ImGui::MenuItem("Cube", NULL, false, true))
  722. {
  723. _tool_type = tool::ToolType::PLACE;
  724. tool_send_state();
  725. tool::set_placeable(ss, "unit", "core/units/primitives/cube");
  726. send_command(ss);
  727. }
  728. if (ImGui::MenuItem("Sphere"))
  729. {
  730. _tool_type = tool::ToolType::PLACE;
  731. tool_send_state();
  732. tool::set_placeable(ss, "unit", "core/units/primitives/sphere");
  733. send_command(ss);
  734. }
  735. if (ImGui::MenuItem("Cone"))
  736. {
  737. _tool_type = tool::ToolType::PLACE;
  738. tool_send_state();
  739. tool::set_placeable(ss, "unit", "core/units/primitives/cone");
  740. send_command(ss);
  741. }
  742. if (ImGui::MenuItem("Cylinder"))
  743. {
  744. _tool_type = tool::ToolType::PLACE;
  745. tool_send_state();
  746. tool::set_placeable(ss, "unit", "core/units/primitives/cylinder");
  747. send_command(ss);
  748. }
  749. if (ImGui::MenuItem("Plane"))
  750. {
  751. _tool_type = tool::ToolType::PLACE;
  752. tool_send_state();
  753. tool::set_placeable(ss, "unit", "core/units/primitives/plane");
  754. send_command(ss);
  755. }
  756. ImGui::EndMenu();
  757. }
  758. if (ImGui::MenuItem("Camera"))
  759. {
  760. _tool_type = tool::ToolType::PLACE;
  761. tool_send_state();
  762. tool::set_placeable(ss, "unit", "core/units/camera");
  763. send_command(ss);
  764. }
  765. if (ImGui::MenuItem("Light"))
  766. {
  767. _tool_type = tool::ToolType::PLACE;
  768. tool_send_state();
  769. tool::set_placeable(ss, "unit", "core/units/light");
  770. send_command(ss);
  771. }
  772. if (ImGui::MenuItem("Sound"))
  773. {
  774. _tool_type = tool::ToolType::PLACE;
  775. tool_send_state();
  776. tool::set_placeable(ss, "sound", "core/units/camera");
  777. send_command(ss);
  778. }
  779. ImGui::EndMenu();
  780. }
  781. if (ImGui::BeginMenu("Scene View"))
  782. {
  783. TempAllocator256 ta;
  784. StringStream ss(ta);
  785. if (ImGui::BeginMenu("Camera View"))
  786. {
  787. if (ImGui::MenuItem("Perspective"))
  788. {
  789. tool::camera_view_perspective(ss);
  790. send_command(ss);
  791. }
  792. if (ImGui::MenuItem("Front"))
  793. {
  794. tool::camera_view_front(ss);
  795. send_command(ss);
  796. }
  797. if (ImGui::MenuItem("Back"))
  798. {
  799. tool::camera_view_back(ss);
  800. send_command(ss);
  801. }
  802. if (ImGui::MenuItem("Right"))
  803. {
  804. tool::camera_view_right(ss);
  805. send_command(ss);
  806. }
  807. if (ImGui::MenuItem("Left"))
  808. {
  809. tool::camera_view_left(ss);
  810. send_command(ss);
  811. }
  812. if (ImGui::MenuItem("Top"))
  813. {
  814. tool::camera_view_top(ss);
  815. send_command(ss);
  816. }
  817. if (ImGui::MenuItem("Bottom"))
  818. {
  819. tool::camera_view_bottom(ss);
  820. send_command(ss);
  821. }
  822. ImGui::EndMenu();
  823. }
  824. ImGui::Separator();
  825. if (ImGui::MenuItem("Show Grid", NULL, _show_grid))
  826. {
  827. _show_grid = !_show_grid;
  828. tool_send_state();
  829. }
  830. if (ImGui::SliderFloat("Grid Size", &_grid_size, 0.1f, 5.0f))
  831. {
  832. tool_send_state();
  833. }
  834. if (ImGui::SliderInt("Snap Rot", &_rotation_snap, 1, 180))
  835. {
  836. tool_send_state();
  837. }
  838. ImGui::EndMenu();
  839. }
  840. if (ImGui::BeginMenu("Windows"))
  841. {
  842. if (ImGui::MenuItem("Scene"))
  843. {
  844. _scene_view._open = true;
  845. }
  846. if (ImGui::MenuItem("Scene Tree"))
  847. {
  848. _scene_tree._open = true;
  849. }
  850. if (ImGui::MenuItem("Inspector"))
  851. {
  852. _inspector._open = true;
  853. }
  854. if (ImGui::MenuItem("Console"))
  855. {
  856. _console._open = true;
  857. }
  858. if (ImGui::MenuItem("Animator"))
  859. {
  860. _animator._open = true;
  861. }
  862. ImGui::EndMenu();
  863. }
  864. if (ImGui::BeginMenu("Help"))
  865. {
  866. ImGui::MenuItem("About", "");
  867. ImGui::EndMenu();
  868. }
  869. _main_menu_pos = ImGui::GetWindowPos();
  870. _main_menu_size = ImGui::GetWindowSize();
  871. ImGui::EndMainMenuBar();
  872. }
  873. }
  874. void toolbar()
  875. {
  876. if (ImGui::BeginToolbar("Toolbar", _toolbar_pos, _toolbar_size))
  877. {
  878. if (ImGui::ToolbarButton((void*)(uintptr_t) tex_place->handle.idx, ImVec4(0, 0, 0, 0), "Place"))
  879. {
  880. _tool_type = tool::ToolType::PLACE;
  881. tool_send_state();
  882. }
  883. if (ImGui::ToolbarButton((void*)(uintptr_t) tex_move->handle.idx, ImVec4(0, 0, 0, 0), "Move"))
  884. {
  885. _tool_type = tool::ToolType::MOVE;
  886. tool_send_state();
  887. }
  888. if (ImGui::ToolbarButton((void*)(uintptr_t) tex_rotate->handle.idx, ImVec4(0, 0, 0, 0), "Rotate"))
  889. {
  890. _tool_type = tool::ToolType::ROTATE;
  891. tool_send_state();
  892. }
  893. if (ImGui::ToolbarButton((void*)(uintptr_t) tex_scale->handle.idx, ImVec4(0, 0, 0, 0), "Scale"))
  894. {
  895. _tool_type = tool::ToolType::SCALE;
  896. tool_send_state();
  897. }
  898. if (ImGui::ToolbarButton((void*)(uintptr_t) tex_axis_local->handle.idx, ImVec4(0, 0, 0, 0), "Reference System: Local"))
  899. {
  900. _reference_system = tool::ReferenceSystem::LOCAL;
  901. tool_send_state();
  902. }
  903. if (ImGui::ToolbarButton((void*)(uintptr_t) tex_axis_world->handle.idx, ImVec4(0, 0, 0, 0), "Reference System: World"))
  904. {
  905. _reference_system = tool::ReferenceSystem::WORLD;
  906. tool_send_state();
  907. }
  908. if (ImGui::ToolbarButton((void*)(uintptr_t) tex_axis_local->handle.idx, ImVec4(0, 0, 0, 0), "Snap Mode: Relative"))
  909. {
  910. _snap_mode = tool::SnapMode::RELATIVE;
  911. tool_send_state();
  912. }
  913. if (ImGui::ToolbarButton((void*)(uintptr_t) tex_ref_world->handle.idx, ImVec4(0, 0, 0, 0), "Snap Mode: Absolute"))
  914. {
  915. _snap_mode = tool::SnapMode::ABSOLUTE;
  916. tool_send_state();
  917. }
  918. _toolbar_pos = ImGui::GetWindowPos();
  919. _toolbar_size = ImGui::GetWindowSize();
  920. ImGui::EndToolbar();
  921. }
  922. }
  923. };
  924. LevelEditor* s_editor;
  925. void tool_init()
  926. {
  927. const DeviceOptions& opt = device()->_device_options;
  928. s_editor = CE_NEW(default_allocator(), LevelEditor)(opt._source_dir);
  929. }
  930. void tool_update(float dt)
  931. {
  932. s_editor->update(dt);
  933. }
  934. void tool_shutdown()
  935. {
  936. CE_DELETE(default_allocator(), s_editor);
  937. }
  938. extern bool next_event(OsEvent& ev);
  939. bool tool_process_events()
  940. {
  941. ImGuiIO& io = ImGui::GetIO();
  942. bool exit = false;
  943. bool reset = false;
  944. TempAllocator4096 ta;
  945. StringStream ss(ta);
  946. OsEvent event;
  947. while (next_event(event))
  948. {
  949. if (event.type == OsEventType::NONE)
  950. continue;
  951. switch (event.type)
  952. {
  953. case OsEventType::BUTTON:
  954. switch (event.button.device_id)
  955. {
  956. case crown::InputDeviceType::KEYBOARD:
  957. io.KeyCtrl = ((event.button.button_num == crown::KeyboardButton::CTRL_LEFT)
  958. || (event.button.button_num == crown::KeyboardButton::CTRL_RIGHT)) && event.button.pressed;
  959. io.KeyShift = ((event.button.button_num == crown::KeyboardButton::SHIFT_LEFT)
  960. || (event.button.button_num == crown::KeyboardButton::SHIFT_RIGHT)) && event.button.pressed;
  961. io.KeyAlt = ((event.button.button_num == crown::KeyboardButton::ALT_LEFT)
  962. || (event.button.button_num == crown::KeyboardButton::ALT_RIGHT)) && event.button.pressed;
  963. io.KeySuper = ((event.button.button_num == crown::KeyboardButton::SUPER_LEFT)
  964. || (event.button.button_num == crown::KeyboardButton::SUPER_RIGHT)) && event.button.pressed;
  965. io.KeysDown[event.button.button_num] = event.button.pressed;
  966. if (!io.WantCaptureKeyboard)
  967. {
  968. if (event.button.pressed)
  969. {
  970. if (event.button.button_num == crown::KeyboardButton::W)
  971. tool::keyboard_pressed(ss, 'w');
  972. if (event.button.button_num == crown::KeyboardButton::A)
  973. tool::keyboard_pressed(ss, 'a');
  974. if (event.button.button_num == crown::KeyboardButton::S)
  975. tool::keyboard_pressed(ss, 's');
  976. if (event.button.button_num == crown::KeyboardButton::D)
  977. tool::keyboard_pressed(ss, 'd');
  978. }
  979. else
  980. {
  981. if (event.button.button_num == crown::KeyboardButton::W)
  982. tool::keyboard_released(ss, 'w');
  983. if (event.button.button_num == crown::KeyboardButton::A)
  984. tool::keyboard_released(ss, 'a');
  985. if (event.button.button_num == crown::KeyboardButton::S)
  986. tool::keyboard_released(ss, 's');
  987. if (event.button.button_num == crown::KeyboardButton::D)
  988. tool::keyboard_released(ss, 'd');
  989. }
  990. }
  991. break;
  992. case crown::InputDeviceType::MOUSE:
  993. io.MouseDown[0] = (event.button.button_num == crown::MouseButton::LEFT) && event.button.pressed;
  994. io.MouseDown[1] = (event.button.button_num == crown::MouseButton::RIGHT) && event.button.pressed;
  995. io.MouseDown[2] = (event.button.button_num == crown::MouseButton::MIDDLE) && event.button.pressed;
  996. if (!io.WantCaptureMouse)
  997. {
  998. ImVec2& mouse_curr = s_editor->_scene_view._mouse_curr;
  999. mouse_curr.x = io.MousePos.x - s_editor->_scene_view._pos.x;
  1000. mouse_curr.y = io.MousePos.y - s_editor->_scene_view._pos.y;
  1001. tool::set_mouse_state(ss
  1002. , mouse_curr.x
  1003. , mouse_curr.y
  1004. , io.MouseDown[0]
  1005. , io.MouseDown[2]
  1006. , io.MouseDown[1]
  1007. );
  1008. if (event.button.button_num == crown::MouseButton::LEFT)
  1009. {
  1010. if (event.button.pressed)
  1011. tool::mouse_down(ss, mouse_curr.x, mouse_curr.y);
  1012. else
  1013. tool::mouse_up(ss, mouse_curr.x, mouse_curr.y);
  1014. }
  1015. }
  1016. break;
  1017. }
  1018. break;
  1019. case OsEventType::AXIS:
  1020. switch(event.axis.device_id)
  1021. {
  1022. case InputDeviceType::MOUSE:
  1023. switch(event.axis.axis_num)
  1024. {
  1025. case crown::MouseAxis::CURSOR:
  1026. io.MousePos = ImVec2(event.axis.axis_x, event.axis.axis_y);
  1027. if (!io.WantCaptureMouse)
  1028. {
  1029. ImVec2& mouse_curr = s_editor->_scene_view._mouse_curr;
  1030. ImVec2& mouse_last = s_editor->_scene_view._mouse_last;
  1031. mouse_curr.x = io.MousePos.x - s_editor->_scene_view._pos.x;
  1032. mouse_curr.y = io.MousePos.y - s_editor->_scene_view._pos.y;
  1033. float delta_x = mouse_curr.x - mouse_last.x;
  1034. float delta_y = mouse_curr.y - mouse_last.y;
  1035. tool::mouse_move(ss, mouse_curr.x, mouse_curr.y, delta_x, delta_y);
  1036. mouse_last = mouse_curr;
  1037. }
  1038. break;
  1039. case crown::MouseAxis::WHEEL:
  1040. io.MouseWheel += event.axis.axis_y;
  1041. if (!io.WantCaptureMouse)
  1042. tool::mouse_wheel(ss, io.MouseWheel);
  1043. break;
  1044. }
  1045. }
  1046. break;
  1047. case OsEventType::TEXT:
  1048. io.AddInputCharactersUTF8((const char*) event.text.utf8);
  1049. break;
  1050. case OsEventType::RESOLUTION:
  1051. _width = event.resolution.width;
  1052. _height = event.resolution.height;
  1053. reset = true;
  1054. break;
  1055. case OsEventType::EXIT:
  1056. exit = true;
  1057. break;
  1058. default:
  1059. break;
  1060. }
  1061. }
  1062. s_editor->send_command(ss);
  1063. bool vsync = true;
  1064. if (reset)
  1065. bgfx::reset(_width, _height, (vsync ? BGFX_RESET_VSYNC : BGFX_RESET_NONE));
  1066. return exit;
  1067. }
  1068. } // namespace crown
  1069. #endif // CROWN_TOOLS