SceneEditor.hx 46 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770
  1. package hide.comp;
  2. import hxd.Key as K;
  3. import hxd.Math as M;
  4. import hrt.prefab.Prefab as PrefabElement;
  5. import hrt.prefab.Object3D;
  6. import h3d.scene.Object;
  7. @:access(hide.comp.SceneEditor)
  8. class SceneEditorContext extends hide.prefab.EditContext {
  9. public var editor(default, null) : SceneEditor;
  10. public var elements : Array<PrefabElement>;
  11. public var rootObjects(default, null): Array<Object>;
  12. public var rootElements(default, null): Array<PrefabElement>;
  13. public function new(ctx, elts, editor) {
  14. super(ctx);
  15. this.editor = editor;
  16. this.updates = editor.updates;
  17. this.elements = elts;
  18. rootObjects = [];
  19. rootElements = [];
  20. cleanups = [];
  21. for(elt in elements) {
  22. // var obj3d = elt.to(Object3D);
  23. // if(obj3d == null) continue;
  24. if(!SceneEditor.hasParent(elt, elements)) {
  25. rootElements.push(elt);
  26. var ctx = getContext(elt);
  27. if(ctx != null) {
  28. var pobj = elt.parent == editor.sceneData ? ctx.shared.root3d : getContextRec(elt.parent).local3d;
  29. if( ctx.local3d != pobj )
  30. rootObjects.push(ctx.local3d);
  31. }
  32. }
  33. }
  34. }
  35. override function getCurrentProps( p : hrt.prefab.Prefab ) {
  36. var cur = editor.curEdit;
  37. return cur != null && cur.elements[0] == p ? editor.properties.element : new Element();
  38. }
  39. function getContextRec( p : hrt.prefab.Prefab ) {
  40. if( p == null )
  41. return editor.context;
  42. var c = editor.context.shared.contexts.get(p);
  43. if( c == null )
  44. return getContextRec(p.parent);
  45. return c;
  46. }
  47. override function rebuildProperties() {
  48. editor.scene.setCurrent();
  49. editor.selectObjects(elements);
  50. }
  51. override function rebuildPrefab( p : hrt.prefab.Prefab ) {
  52. // refresh all for now
  53. editor.refresh();
  54. }
  55. public function cleanup() {
  56. for( c in cleanups.copy() )
  57. c();
  58. cleanups = [];
  59. }
  60. override function onChange(p : PrefabElement, pname: String) {
  61. super.onChange(p, pname);
  62. editor.onPrefabChange(p, pname);
  63. }
  64. }
  65. enum RefreshMode {
  66. Partial;
  67. Full;
  68. }
  69. class SceneEditor {
  70. public var tree : hide.comp.IconTree<PrefabElement>;
  71. public var favTree : hide.comp.IconTree<PrefabElement>;
  72. public var scene : hide.comp.Scene;
  73. public var properties : hide.comp.PropsEditor;
  74. public var context(default,null) : hrt.prefab.Context;
  75. public var curEdit(default, null) : SceneEditorContext;
  76. public var snapToGround = false;
  77. public var localTransform = true;
  78. public var cameraController : h3d.scene.CameraController;
  79. public var editorDisplay(default,set) : Bool;
  80. var searchBox : Element;
  81. var updates : Array<Float -> Void> = [];
  82. var gizmo : hide.view.l3d.Gizmo;
  83. var interactives : Map<PrefabElement, h3d.scene.Interactive>;
  84. var ide : hide.Ide;
  85. public var event(default, null) : hxd.WaitEvent;
  86. var hideList : Map<PrefabElement, Bool> = new Map();
  87. var favorites : Array<PrefabElement> = [];
  88. var undo(get, null):hide.ui.UndoHistory;
  89. function get_undo() { return view.undo; }
  90. public var view(default, null) : hide.view.FileView;
  91. var sceneData : PrefabElement;
  92. public function new(view, data) {
  93. ide = hide.Ide.inst;
  94. this.view = view;
  95. this.sceneData = data;
  96. event = new hxd.WaitEvent();
  97. var propsEl = new Element('<div class="props"></div>');
  98. properties = new hide.comp.PropsEditor(undo,null,propsEl);
  99. properties.saveDisplayKey = view.saveDisplayKey + "/properties";
  100. tree = new hide.comp.IconTree();
  101. tree.async = false;
  102. tree.autoOpenNodes = false;
  103. favTree = new hide.comp.IconTree();
  104. favTree.async = false;
  105. favTree.autoOpenNodes = false;
  106. var sceneEl = new Element('<div class="heaps-scene"></div>');
  107. scene = new hide.comp.Scene(view.config, null, sceneEl);
  108. scene.editor = this;
  109. scene.onReady = onSceneReady;
  110. scene.onResize = function() {
  111. context.shared.root2d.x = scene.width >> 1;
  112. context.shared.root2d.y = scene.height >> 1;
  113. onResize();
  114. };
  115. context = new hrt.prefab.Context();
  116. context.shared = new hide.prefab.ContextShared(scene);
  117. context.shared.currentPath = view.state.path;
  118. context.init();
  119. editorDisplay = true;
  120. view.keys.register("copy", onCopy);
  121. view.keys.register("paste", onPaste);
  122. view.keys.register("cancel", deselect);
  123. view.keys.register("selectAll", selectAll);
  124. view.keys.register("duplicate", duplicate.bind(true));
  125. view.keys.register("group", groupSelection);
  126. view.keys.register("delete", () -> deleteElements(curEdit.rootElements));
  127. view.keys.register("search", function() {
  128. if(searchBox != null) {
  129. searchBox.show();
  130. searchBox.find("input").focus().select();
  131. }
  132. });
  133. view.keys.register("rename", function () {
  134. if(curEdit.rootElements.length > 0)
  135. tree.editNode(curEdit.rootElements[0]);
  136. });
  137. view.keys.register("sceneeditor.focus", focusSelection);
  138. view.keys.register("sceneeditor.lasso", startLassoSelect);
  139. view.keys.register("sceneeditor.hide", function() {
  140. var isHidden = isHidden(curEdit.rootElements[0]);
  141. setVisible(curEdit.elements, isHidden);
  142. });
  143. view.keys.register("sceneeditor.isolate", function() { isolate(curEdit.elements); });
  144. view.keys.register("sceneeditor.showAll", function() { setVisible(context.shared.elements(), true); });
  145. view.keys.register("sceneeditor.selectParent", function() {
  146. if(curEdit.rootElements.length > 0)
  147. selectObjects([curEdit.rootElements[0].parent]);
  148. });
  149. view.keys.register("sceneeditor.reparent", function() {
  150. if(curEdit.rootElements.length > 1) {
  151. var children = curEdit.rootElements.copy();
  152. var parent = children.pop();
  153. reparentElement(children, parent, 0);
  154. }
  155. });
  156. // Load display state
  157. {
  158. var all = sceneData.flatten(hrt.prefab.Prefab);
  159. var list = @:privateAccess view.getDisplayState("hideList");
  160. if(list != null) {
  161. var m = [for(i in (list:Array<Dynamic>)) i => true];
  162. for(p in all) {
  163. if(m.exists(p.getAbsPath()))
  164. hideList.set(p, true);
  165. }
  166. }
  167. var favList = @:privateAccess view.getDisplayState("favorites");
  168. if(favList != null) {
  169. for(p in all) {
  170. if(favList.indexOf(p.getAbsPath()) >= 0)
  171. favorites.push(p);
  172. }
  173. }
  174. }
  175. }
  176. public dynamic function onResize() {
  177. }
  178. function set_editorDisplay(v) {
  179. context.shared.editorDisplay = v;
  180. return editorDisplay = v;
  181. }
  182. public function getSelection() {
  183. return curEdit != null ? curEdit.elements : [];
  184. }
  185. public function addSearchBox(parent : Element) {
  186. searchBox = new Element("<div>").addClass("searchBox").appendTo(parent);
  187. new Element("<input type='text'>").appendTo(searchBox).keydown(function(e) {
  188. if( e.keyCode == 27 ) {
  189. searchBox.find("i").click();
  190. return;
  191. }
  192. }).keyup(function(e) {
  193. tree.searchFilter(e.getThis().val());
  194. });
  195. new Element("<i>").addClass("fa fa-times-circle").appendTo(searchBox).click(function(_) {
  196. tree.searchFilter(null);
  197. searchBox.toggle();
  198. });
  199. }
  200. function makeCamController() {
  201. var c = new h3d.scene.CameraController(scene.s3d);
  202. c.friction = 0.9;
  203. c.panSpeed = 0.6;
  204. c.zoomAmount = 1.05;
  205. c.smooth = 0.7;
  206. return c;
  207. }
  208. function focusSelection() {
  209. if(curEdit.rootObjects.length > 0) {
  210. cameraController.set(curEdit.rootObjects[0].getAbsPos().getPosition().toPoint());
  211. }
  212. for(obj in curEdit.rootElements)
  213. tree.revealNode(obj);
  214. }
  215. function onSceneReady() {
  216. tree.saveDisplayKey = view.saveDisplayKey + '/tree';
  217. scene.s2d.addChild(context.shared.root2d);
  218. scene.s3d.addChild(context.shared.root3d);
  219. gizmo = new hide.view.l3d.Gizmo(scene);
  220. gizmo.moveStep = view.config.get("sceneeditor.gridSnapStep");
  221. cameraController = makeCamController();
  222. resetCamera();
  223. var cam = @:privateAccess view.getDisplayState("Camera");
  224. if( cam != null ) {
  225. scene.s3d.camera.pos.set(cam.x, cam.y, cam.z);
  226. scene.s3d.camera.target.set(cam.tx, cam.ty, cam.tz);
  227. }
  228. cameraController.loadFromCamera();
  229. scene.onUpdate = update;
  230. // BUILD scene tree
  231. function makeItem(o:PrefabElement, ?state) : hide.comp.IconTree.IconTreeItem<PrefabElement> {
  232. var p = o.getHideProps();
  233. var r : hide.comp.IconTree.IconTreeItem<PrefabElement> = {
  234. value : o,
  235. text : o.name,
  236. icon : "fa fa-"+p.icon,
  237. children : o.children.length > 0,
  238. state: state
  239. };
  240. return r;
  241. }
  242. favTree.get = function (o:PrefabElement) {
  243. if(o == null) {
  244. return [for(f in favorites) makeItem(f, {
  245. disabled: true
  246. })];
  247. }
  248. return [];
  249. }
  250. favTree.allowRename = false;
  251. favTree.init();
  252. favTree.onAllowMove = function(_, _) {
  253. return false;
  254. };
  255. favTree.onClick = function(e, evt) {
  256. if(evt.ctrlKey) {
  257. var sel = tree.getSelection();
  258. sel.push(e);
  259. selectObjects(sel, true);
  260. tree.revealNode(e);
  261. }
  262. else
  263. selectObjects([e], true);
  264. }
  265. favTree.onDblClick = function(e) {
  266. selectObjects([e], true);
  267. tree.revealNode(e);
  268. return true;
  269. }
  270. tree.get = function(o:PrefabElement) {
  271. var objs = o == null ? sceneData.children : Lambda.array(o);
  272. var out = [for( o in objs ) makeItem(o)];
  273. return out;
  274. };
  275. function ctxMenu(tree, e) {
  276. e.preventDefault();
  277. var current = tree.getCurrentOver();
  278. if(current != null && (curEdit == null || curEdit.elements.indexOf(current) < 0)) {
  279. selectObjects([current]);
  280. }
  281. var newItems = getNewContextMenu(current);
  282. var menuItems : Array<hide.comp.ContextMenu.ContextMenuItem> = [
  283. { label : "New...", menu : newItems },
  284. ];
  285. var actionItems : Array<hide.comp.ContextMenu.ContextMenuItem> = [
  286. { label : "Favorite", checked : current != null && isFavorite(current), click : function() setFavorite(current, !isFavorite(current)) },
  287. { label : "Rename", enabled : current != null, click : function() tree.editNode(current) },
  288. { label : "Delete", enabled : current != null, click : function() deleteElements(curEdit.rootElements) },
  289. { label : "Duplicate", enabled : current != null, click : duplicate.bind(false) },
  290. { label : "Reference", enabled : current != null, click : function() createRef(current, current.parent) },
  291. ];
  292. if(current != null && current.to(Object3D) != null && current.to(hrt.prefab.Reference) == null) {
  293. var visible = !isHidden(current);
  294. menuItems = menuItems.concat([
  295. { label : "Visible", checked : visible, click : function() setVisible(curEdit.elements, !visible) },
  296. { label : "Select all", click : selectAll },
  297. { label : "Select children", enabled : current != null, click : function() selectObjects(current.flatten()) },
  298. ]);
  299. actionItems = actionItems.concat([
  300. { label : "Isolate", click : function() isolate(curEdit.elements) },
  301. { label : "Group", enabled : curEdit != null && canGroupSelection(), click : groupSelection }
  302. ]);
  303. }
  304. else if(current != null) {
  305. var enabled = current.enabled;
  306. menuItems.push({ label : "Enable", checked : enabled, click : function() setEnabled(curEdit.elements, !enabled) });
  307. }
  308. menuItems.push({ isSeparator : true, label : "" });
  309. new hide.comp.ContextMenu(menuItems.concat(actionItems));
  310. };
  311. tree.element.parent().contextmenu(ctxMenu.bind(tree));
  312. favTree.element.parent().contextmenu(ctxMenu.bind(favTree));
  313. tree.allowRename = true;
  314. tree.init();
  315. tree.onClick = function(e, _) {
  316. selectObjects(tree.getSelection(), false);
  317. }
  318. tree.onDblClick = function(e) {
  319. focusSelection();
  320. return true;
  321. }
  322. tree.onRename = function(e, name) {
  323. var oldName = e.name;
  324. e.name = name;
  325. undo.change(Field(e, "name", oldName), function() {
  326. tree.refresh();
  327. refreshScene();
  328. });
  329. return true;
  330. };
  331. tree.onAllowMove = function(_, _) {
  332. return true;
  333. };
  334. // Batch tree.onMove, which is called for every node moved, causing problems with undo and refresh
  335. {
  336. var movetimer : haxe.Timer = null;
  337. var moved = [];
  338. tree.onMove = function(e, to, idx) {
  339. if(movetimer != null) {
  340. movetimer.stop();
  341. }
  342. moved.push(e);
  343. movetimer = haxe.Timer.delay(function() {
  344. reparentElement(moved, to, idx);
  345. movetimer = null;
  346. moved = [];
  347. }, 50);
  348. }
  349. }
  350. tree.applyStyle = applyTreeStyle;
  351. selectObjects([]);
  352. refresh();
  353. }
  354. public function refresh( ?mode: RefreshMode, ?callb: Void->Void) {
  355. if(mode == null || mode == Full) refreshScene();
  356. refreshFavs();
  357. refreshTree(callb);
  358. }
  359. public function collapseTree() {
  360. tree.collapseAll();
  361. for(fav in favorites)
  362. tree.openNode(fav);
  363. }
  364. function refreshTree( ?callb ) {
  365. tree.refresh(function() {
  366. var all = sceneData.flatten(hrt.prefab.Prefab);
  367. for(elt in all) {
  368. var el = tree.getElement(elt);
  369. if(el == null) continue;
  370. applyTreeStyle(elt, el);
  371. }
  372. if(callb != null) callb();
  373. });
  374. }
  375. function refreshFavs() {
  376. favTree.refresh();
  377. }
  378. function refreshProps() {
  379. selectObjects(curEdit.elements, false);
  380. }
  381. public function refreshScene() {
  382. var sh = context.shared;
  383. sh.root3d.remove();
  384. sh.root2d.remove();
  385. for( c in sh.contexts )
  386. if( c != null && c.cleanup != null )
  387. c.cleanup();
  388. context.shared = sh = new hide.prefab.ContextShared(scene);
  389. sh.editorDisplay = editorDisplay;
  390. sh.currentPath = view.state.path;
  391. scene.s3d.addChild(sh.root3d);
  392. scene.s2d.addChild(sh.root2d);
  393. scene.setCurrent();
  394. scene.onResize();
  395. context.init();
  396. sceneData.make(context);
  397. var bgcol = scene.engine.backgroundColor;
  398. scene.init();
  399. scene.engine.backgroundColor = bgcol;
  400. refreshInteractives();
  401. var all = sceneData.flatten(hrt.prefab.Prefab);
  402. for(elt in all)
  403. applySceneStyle(elt);
  404. onRefresh();
  405. }
  406. public dynamic function onRefresh() {
  407. }
  408. function makeInteractive(elt: PrefabElement) {
  409. var obj3d = Std.instance(elt, Object3D);
  410. if(obj3d == null)
  411. return;
  412. var contexts = context.shared.contexts;
  413. var ctx = contexts[elt];
  414. if(ctx == null)
  415. return;
  416. var local3d = ctx.local3d;
  417. if(local3d == null)
  418. return;
  419. var meshes = context.shared.getObjects(elt, h3d.scene.Mesh);
  420. var invRootMat = local3d.getAbsPos().clone();
  421. invRootMat.invert();
  422. var bounds = new h3d.col.Bounds();
  423. for(mesh in meshes) {
  424. if(mesh.ignoreCollide)
  425. continue;
  426. // invisible objects are ignored collision wise
  427. var p : h3d.scene.Object = mesh;
  428. while( p != local3d ) {
  429. if( !p.visible ) break;
  430. p = p.parent;
  431. }
  432. if( p != local3d ) continue;
  433. var localMat = mesh.getAbsPos().clone();
  434. localMat.multiply(localMat, invRootMat);
  435. var lb = mesh.primitive.getBounds().clone();
  436. lb.transform(localMat);
  437. bounds.add(lb);
  438. }
  439. var meshCollider = new h3d.col.Collider.GroupCollider([for(m in meshes) {
  440. var c : h3d.col.Collider = try m.getGlobalCollider() catch(e: Dynamic) null;
  441. if(c != null) c;
  442. }]);
  443. var boundsCollider = new h3d.col.ObjectCollider(local3d, bounds);
  444. var int = new h3d.scene.Interactive(boundsCollider, local3d);
  445. interactives.set(elt, int);
  446. int.ignoreParentTransform = true;
  447. int.preciseShape = meshCollider;
  448. int.propagateEvents = true;
  449. int.enableRightButton = true;
  450. var startDrag = null;
  451. var dragBtn = -1;
  452. int.onClick = function(e) {
  453. if(e.button == K.MOUSE_RIGHT) {
  454. e.propagate = false;
  455. var parentEl = curEdit.rootElements[0];
  456. if(parentEl == null)
  457. parentEl = elt;
  458. var newItems = getNewContextMenu(parentEl, function(newElt) {
  459. var newObj3d = Std.instance(newElt, Object3D);
  460. if(newObj3d != null) {
  461. var newPos = new h3d.Matrix();
  462. newPos.identity();
  463. newPos.setPosition(@:privateAccess int.hitPoint);
  464. var invParent = getObject(parentEl).getAbsPos().clone();
  465. invParent.invert();
  466. newPos.multiply(newPos, invParent);
  467. newObj3d.setTransform(newPos);
  468. }
  469. });
  470. var menuItems : Array<hide.comp.ContextMenu.ContextMenuItem> = [
  471. { label : "New...", menu : newItems },
  472. ];
  473. new hide.comp.ContextMenu(menuItems);
  474. }
  475. }
  476. int.onPush = function(e) {
  477. startDrag = [scene.s2d.mouseX, scene.s2d.mouseY];
  478. dragBtn = e.button;
  479. if(e.button != K.MOUSE_LEFT)
  480. return;
  481. e.propagate = false;
  482. var elts = null;
  483. if(K.isDown(K.SHIFT)) {
  484. if(Type.getClass(elt.parent) == hrt.prefab.Object3D)
  485. elts = [elt.parent];
  486. else
  487. elts = elt.parent.children;
  488. }
  489. else
  490. elts = [elt];
  491. if(K.isDown(K.CTRL)) {
  492. var current = curEdit.elements.copy();
  493. if(current.indexOf(elt) < 0) {
  494. for(e in elts) {
  495. if(current.indexOf(e) < 0)
  496. current.push(e);
  497. }
  498. }
  499. else {
  500. for(e in elts)
  501. current.remove(e);
  502. }
  503. selectObjects(current);
  504. }
  505. else
  506. selectObjects(elts);
  507. }
  508. int.onRelease = function(e) {
  509. startDrag = null;
  510. dragBtn = -1;
  511. if(e.button == K.MOUSE_LEFT) {
  512. e.propagate = false;
  513. }
  514. }
  515. int.onMove = function(e) {
  516. if(startDrag != null) {
  517. if((M.abs(startDrag[0] - scene.s2d.mouseX) + M.abs(startDrag[1] - scene.s2d.mouseY)) > 5) {
  518. int.preventClick();
  519. startDrag = null;
  520. if(dragBtn == K.MOUSE_LEFT) {
  521. moveGizmoToSelection();
  522. gizmo.startMove(MoveXY);
  523. }
  524. }
  525. }
  526. }
  527. }
  528. function refreshInteractives() {
  529. var contexts = context.shared.contexts;
  530. interactives = new Map();
  531. var all = contexts.keys();
  532. for(elt in all) {
  533. makeInteractive(elt);
  534. }
  535. }
  536. function setupGizmo() {
  537. if(curEdit == null) return;
  538. gizmo.onStartMove = function(mode) {
  539. var objects3d = [for(o in curEdit.rootElements) {
  540. var obj3d = o.to(hrt.prefab.Object3D);
  541. if(obj3d != null)
  542. obj3d;
  543. }];
  544. var sceneObjs = [for(o in objects3d) getContext(o).local3d];
  545. var pivotPt = getPivot(sceneObjs);
  546. var pivot = new h3d.Matrix();
  547. pivot.initTranslation(pivotPt.x, pivotPt.y, pivotPt.z);
  548. var invPivot = pivot.clone();
  549. invPivot.invert();
  550. var localMats = [for(o in sceneObjs) {
  551. var m = worldMat(o);
  552. m.multiply(m, invPivot);
  553. m;
  554. }];
  555. var posQuant = view.config.get("sceneeditor.xyzPrecision");
  556. var scaleQuant = view.config.get("sceneeditor.scalePrecision");
  557. var rotQuant = view.config.get("sceneeditor.rotatePrecision");
  558. inline function quantize(x: Float, step: Float) {
  559. if(step > 0) {
  560. x = Math.round(x / step) * step;
  561. x = untyped parseFloat(x.toFixed(5)); // Snap to closest nicely displayed float :cold_sweat:
  562. }
  563. return x;
  564. }
  565. var prevState = [for(o in objects3d) o.saveTransform()];
  566. gizmo.onMove = function(translate: h3d.Vector, rot: h3d.Quat, scale: h3d.Vector) {
  567. var transf = new h3d.Matrix();
  568. transf.identity();
  569. if(rot != null)
  570. rot.toMatrix(transf);
  571. if(translate != null)
  572. transf.translate(translate.x, translate.y, translate.z);
  573. for(i in 0...sceneObjs.length) {
  574. var newMat = localMats[i].clone();
  575. newMat.multiply(newMat, transf);
  576. newMat.multiply(newMat, pivot);
  577. if(snapToGround && mode == MoveXY) {
  578. newMat.tz = getZ(newMat.tx, newMat.ty);
  579. }
  580. var invParent = sceneObjs[i].parent.getAbsPos().clone();
  581. invParent.invert();
  582. newMat.multiply(newMat, invParent);
  583. if(scale != null) {
  584. newMat.prependScale(scale.x, scale.y, scale.z);
  585. }
  586. var obj3d = objects3d[i];
  587. var rot = newMat.getEulerAngles();
  588. obj3d.x = quantize(newMat.tx, posQuant);
  589. obj3d.y = quantize(newMat.ty, posQuant);
  590. obj3d.z = quantize(newMat.tz, posQuant);
  591. obj3d.rotationX = quantize(M.radToDeg(rot.x), rotQuant);
  592. obj3d.rotationY = quantize(M.radToDeg(rot.y), rotQuant);
  593. obj3d.rotationZ = quantize(M.radToDeg(rot.z), rotQuant);
  594. if(scale != null) {
  595. inline function scaleSnap(x: Float) {
  596. if(K.isDown(K.CTRL)) {
  597. var step = K.isDown(K.SHIFT) ? 0.5 : 1.0;
  598. x = Math.round(x / step) * step;
  599. }
  600. return x;
  601. }
  602. var s = newMat.getScale();
  603. obj3d.scaleX = quantize(scaleSnap(s.x), scaleQuant);
  604. obj3d.scaleY = quantize(scaleSnap(s.y), scaleQuant);
  605. obj3d.scaleZ = quantize(scaleSnap(s.z), scaleQuant);
  606. }
  607. obj3d.applyPos(sceneObjs[i]);
  608. }
  609. }
  610. gizmo.onFinishMove = function() {
  611. var newState = [for(o in objects3d) o.saveTransform()];
  612. refreshProps();
  613. undo.change(Custom(function(undo) {
  614. if( undo ) {
  615. for(i in 0...objects3d.length) {
  616. objects3d[i].loadTransform(prevState[i]);
  617. objects3d[i].applyPos(sceneObjs[i]);
  618. }
  619. refreshProps();
  620. }
  621. else {
  622. for(i in 0...objects3d.length) {
  623. objects3d[i].loadTransform(newState[i]);
  624. objects3d[i].applyPos(sceneObjs[i]);
  625. }
  626. refreshProps();
  627. }
  628. for(o in objects3d)
  629. o.updateInstance(getContext(o));
  630. }));
  631. }
  632. }
  633. }
  634. function moveGizmoToSelection() {
  635. // Snap Gizmo at center of objects
  636. gizmo.getRotationQuat().identity();
  637. if(curEdit != null && curEdit.rootObjects.length > 0) {
  638. var pos = getPivot(curEdit.rootObjects);
  639. gizmo.visible = true;
  640. gizmo.setPosition(pos.x, pos.y, pos.z);
  641. if(curEdit.rootObjects.length == 1 && (localTransform || K.isDown(K.ALT))) {
  642. var obj = curEdit.rootObjects[0];
  643. var mat = worldMat(obj);
  644. var s = mat.getScale();
  645. if(s.x != 0 && s.y != 0 && s.z != 0) {
  646. mat.prependScale(1.0 / s.x, 1.0 / s.y, 1.0 / s.z);
  647. gizmo.getRotationQuat().initRotateMatrix(mat);
  648. }
  649. }
  650. }
  651. else {
  652. gizmo.visible = false;
  653. }
  654. }
  655. var inLassoMode = false;
  656. function startLassoSelect() {
  657. if(inLassoMode) {
  658. inLassoMode = false;
  659. return;
  660. }
  661. inLassoMode = true;
  662. var g = new h2d.Object(scene.s2d);
  663. var overlay = new h2d.Bitmap(h2d.Tile.fromColor(0xffffff, 10000, 10000, 0.1), g);
  664. var intOverlay = new h2d.Interactive(10000, 10000, scene.s2d);
  665. var lastPt = new h2d.col.Point(scene.s2d.mouseX, scene.s2d.mouseY);
  666. var points : h2d.col.Polygon = [lastPt];
  667. var polyG = new h2d.Graphics(g);
  668. event.waitUntil(function(dt) {
  669. var curPt = new h2d.col.Point(scene.s2d.mouseX, scene.s2d.mouseY);
  670. if(curPt.distance(lastPt) > 3.0) {
  671. points.push(curPt);
  672. polyG.clear();
  673. polyG.beginFill(0xff0000, 0.5);
  674. polyG.lineStyle(1.0, 0, 1.0);
  675. polyG.moveTo(points[0].x, points[0].y);
  676. for(i in 1...points.length) {
  677. polyG.lineTo(points[i].x, points[i].y);
  678. }
  679. polyG.endFill();
  680. lastPt = curPt;
  681. }
  682. var finish = false;
  683. if(!inLassoMode || K.isDown(K.ESCAPE) || K.isDown(K.MOUSE_RIGHT)) {
  684. finish = true;
  685. }
  686. if(K.isDown(K.MOUSE_LEFT)) {
  687. var contexts = context.shared.contexts;
  688. var all = getAllSelectable();
  689. var inside = [];
  690. for(elt in all) {
  691. if(elt.to(Object3D) == null)
  692. continue;
  693. var ctx = contexts[elt];
  694. var o = ctx.local3d;
  695. if(o == null || !o.visible)
  696. continue;
  697. var absPos = o.getAbsPos();
  698. var screenPos = worldToScreen(absPos.tx, absPos.ty, absPos.tz);
  699. if(points.contains(screenPos, false)) {
  700. inside.push(elt);
  701. }
  702. }
  703. selectObjects(inside);
  704. finish = true;
  705. }
  706. if(finish) {
  707. intOverlay.remove();
  708. g.remove();
  709. inLassoMode = false;
  710. return true;
  711. }
  712. return false;
  713. });
  714. }
  715. public function onPrefabChange(p: PrefabElement, ?pname: String) {
  716. var model = p.to(hrt.prefab.Model);
  717. if(model != null && pname == "source") {
  718. refreshScene();
  719. return;
  720. }
  721. if(p != sceneData) {
  722. var el = tree.getElement(p);
  723. applyTreeStyle(p, el);
  724. }
  725. applySceneStyle(p);
  726. }
  727. public function applyTreeStyle(p: PrefabElement, el: Element) {
  728. var obj3d = p.to(Object3D);
  729. el.toggleClass("disabled", !p.enabled);
  730. el.find("a").first().toggleClass("favorite", isFavorite(p));
  731. if(obj3d != null) {
  732. el.toggleClass("hidden", isHidden(obj3d));
  733. var tog = el.find(".visibility-toggle").first();
  734. if(tog.length == 0) {
  735. tog = new Element('<i class="fa fa-eye visibility-toggle"/>').insertAfter(el.find("a.jstree-anchor").first());
  736. tog.click(function(e) {
  737. if(curEdit.elements.indexOf(obj3d) >= 0)
  738. setVisible(curEdit.elements, isHidden(obj3d));
  739. else
  740. setVisible([obj3d], isHidden(obj3d));
  741. e.preventDefault();
  742. e.stopPropagation();
  743. });
  744. tog.dblclick(function(e) {
  745. e.preventDefault();
  746. e.stopPropagation();
  747. });
  748. }
  749. }
  750. }
  751. public function applySceneStyle(p: PrefabElement) {
  752. var obj3d = p.to(Object3D);
  753. if(obj3d != null) {
  754. var visible = obj3d.visible && !isHidden(obj3d);
  755. for(ctx in getContexts(obj3d)) {
  756. ctx.local3d.visible = visible;
  757. }
  758. }
  759. }
  760. public function getContext(elt : PrefabElement) {
  761. if(elt == null) return null;
  762. var ctx = null;
  763. if(elt == sceneData) {
  764. ctx = context.shared.contexts.get(sceneData);
  765. if(ctx != null) return ctx; // Some libs make their own instances
  766. return context;
  767. }
  768. return context.shared.contexts.get(elt);
  769. }
  770. public function getContexts(elt: PrefabElement) {
  771. if(elt == null)
  772. return null;
  773. return context.shared.getContexts(elt);
  774. }
  775. public function getObject(elt: PrefabElement) {
  776. var ctx = getContext(elt);
  777. if(ctx != null)
  778. return ctx.local3d;
  779. return context.shared.root3d;
  780. }
  781. public function getSelfObject(elt: PrefabElement) {
  782. var ctx = getContext(elt);
  783. var parentCtx = getContext(elt.parent);
  784. if(ctx == null || parentCtx == null) return null;
  785. if(ctx.local3d != parentCtx.local3d)
  786. return ctx.local3d;
  787. return null;
  788. }
  789. function removeInstance(elt : PrefabElement) {
  790. var result = true;
  791. var contexts = context.shared.contexts;
  792. function recRemove(e: PrefabElement) {
  793. for(c in e.children)
  794. recRemove(c);
  795. var int = interactives.get(e);
  796. if(int != null) {
  797. int.remove();
  798. interactives.remove(e);
  799. }
  800. for(ctx in getContexts(e)) {
  801. if(!e.removeInstance(ctx))
  802. result = false;
  803. contexts.remove(e);
  804. }
  805. }
  806. recRemove(elt);
  807. return result;
  808. }
  809. function makeInstance(elt: PrefabElement) {
  810. scene.setCurrent();
  811. var parentCtx = getContext(elt.parent);
  812. var ctx = elt.make(parentCtx);
  813. for( p in elt.flatten() )
  814. makeInteractive(p);
  815. scene.init(ctx.local3d);
  816. }
  817. public function addObject(e : PrefabElement) {
  818. makeInstance(e);
  819. refresh(Partial, () -> selectObjects([e]));
  820. undo.change(Custom(function(undo) {
  821. var fullRefresh = false;
  822. if(undo) {
  823. deselect();
  824. if(!removeInstance(e))
  825. fullRefresh = true;
  826. e.parent.children.remove(e);
  827. refresh(fullRefresh ? Full : Partial);
  828. }
  829. else {
  830. e.parent.children.push(e);
  831. makeInstance(e);
  832. refresh(Partial, () -> selectObjects([e]));
  833. }
  834. }));
  835. }
  836. function fillProps( edit, e : PrefabElement ) {
  837. e.edit(edit);
  838. }
  839. public function showProps(e: PrefabElement) {
  840. scene.setCurrent();
  841. var edit = makeEditContext([e]);
  842. properties.clear();
  843. fillProps(edit, e);
  844. }
  845. function setObjectSelected( p : PrefabElement, ctx : hrt.prefab.Context, b : Bool ) {
  846. p.setSelected(ctx, b);
  847. }
  848. public function selectObjects( elts : Array<PrefabElement>, ?includeTree=true) {
  849. scene.setCurrent();
  850. if( curEdit != null )
  851. curEdit.cleanup();
  852. var edit = makeEditContext(elts);
  853. properties.clear();
  854. if( elts.length > 0 ) fillProps(edit, elts[0]);
  855. if(includeTree) {
  856. tree.setSelection(elts);
  857. }
  858. var map = new Map<PrefabElement,Bool>();
  859. function selectRec(e : PrefabElement, b:Bool) {
  860. if( map.exists(e) )
  861. return;
  862. map.set(e, true);
  863. var ectx = context.shared.contexts.get(e);
  864. setObjectSelected(e, ectx == null ? context : ectx, b);
  865. for( e in e.children )
  866. selectRec(e,b);
  867. }
  868. for( e in elts )
  869. selectRec(e, true);
  870. edit.cleanups.push(function() {
  871. for( e in map.keys() ) {
  872. if( hasBeenRemoved(e) ) continue;
  873. var ectx = context.shared.contexts.get(e);
  874. setObjectSelected(e, ectx == null ? context : ectx, false);
  875. }
  876. });
  877. curEdit = edit;
  878. setupGizmo();
  879. }
  880. function hasBeenRemoved( e : hrt.prefab.Prefab ) {
  881. while( e != null && e != sceneData ) {
  882. if( e.parent != null && e.parent.children.indexOf(e) < 0 )
  883. return true;
  884. e = e.parent;
  885. }
  886. return e == null;
  887. }
  888. public function resetCamera() {
  889. scene.s3d.camera.zNear = scene.s3d.camera.zFar = 0;
  890. scene.resetCamera(1.5);
  891. cameraController.lockZPlanes = scene.s3d.camera.zNear != 0;
  892. cameraController.loadFromCamera();
  893. }
  894. public function getPickTransform(parent: PrefabElement) {
  895. var proj = screenToWorld(scene.s2d.mouseX, scene.s2d.mouseY);
  896. if(proj == null) return null;
  897. var localMat = new h3d.Matrix();
  898. localMat.initTranslation(proj.x, proj.y, proj.z);
  899. if(parent == null)
  900. return localMat;
  901. var parentMat = worldMat(getObject(parent));
  902. parentMat.invert();
  903. localMat.multiply(localMat, parentMat);
  904. return localMat;
  905. }
  906. public function dropObjects(paths: Array<String>, parent: PrefabElement) {
  907. var localMat = getPickTransform(parent);
  908. if(localMat == null) return;
  909. localMat.tx = hxd.Math.round(localMat.tx);
  910. localMat.ty = hxd.Math.round(localMat.ty);
  911. localMat.tz = hxd.Math.round(localMat.tz);
  912. var elts: Array<PrefabElement> = [];
  913. for(path in paths) {
  914. var obj3d : Object3D;
  915. var relative = ide.makeRelative(path);
  916. if(hrt.prefab.Library.getPrefabType(path) != null) {
  917. var ref = new hrt.prefab.Reference(parent);
  918. ref.refpath = "/" + relative;
  919. obj3d = ref;
  920. obj3d.name = new haxe.io.Path(relative).file;
  921. }
  922. else {
  923. obj3d = new hrt.prefab.Model(parent);
  924. obj3d.source = relative;
  925. }
  926. obj3d.setTransform(localMat);
  927. autoName(obj3d);
  928. elts.push(obj3d);
  929. }
  930. for(e in elts)
  931. makeInstance(e);
  932. refresh(Partial, () -> selectObjects(elts));
  933. undo.change(Custom(function(undo) {
  934. if( undo ) {
  935. var fullRefresh = false;
  936. for(e in elts) {
  937. if(!removeInstance(e))
  938. fullRefresh = true;
  939. parent.children.remove(e);
  940. }
  941. refresh(fullRefresh ? Full : Partial);
  942. }
  943. else {
  944. for(e in elts) {
  945. parent.children.push(e);
  946. makeInstance(e);
  947. }
  948. refresh(Partial);
  949. }
  950. }));
  951. }
  952. function canGroupSelection() {
  953. var elts = curEdit.rootElements;
  954. if(elts.length == 0)
  955. return false;
  956. if(elts.length == 1)
  957. return true;
  958. // Only allow grouping of sibling elements
  959. var parent = elts[0].parent;
  960. for(e in elts)
  961. if(e.parent != parent)
  962. return false;
  963. return true;
  964. }
  965. function groupSelection() {
  966. if(!canGroupSelection())
  967. return;
  968. var elts = curEdit.rootElements;
  969. var parent = elts[0].parent;
  970. var parentMat = worldMat(parent);
  971. var invParentMat = parentMat.clone();
  972. invParentMat.invert();
  973. var pivot = new h3d.Vector();
  974. {
  975. var count = 0;
  976. for(elt in curEdit.rootElements) {
  977. var m = worldMat(elt);
  978. if(m != null) {
  979. pivot = pivot.add(m.getPosition());
  980. ++count;
  981. }
  982. }
  983. pivot.scale3(1.0 / count);
  984. }
  985. var local = new h3d.Matrix();
  986. local.initTranslation(pivot.x, pivot.y, pivot.z);
  987. local.multiply(local, invParentMat);
  988. var group = new hrt.prefab.Object3D(parent);
  989. @:privateAccess group.type = "object";
  990. autoName(group);
  991. group.x = local.tx;
  992. group.y = local.ty;
  993. group.z = local.tz;
  994. var parentCtx = getContext(parent);
  995. if(parentCtx == null)
  996. parentCtx = context;
  997. group.make(parentCtx);
  998. var groupCtx = getContext(group);
  999. var effectFunc = reparentImpl(elts, group, 0);
  1000. undo.change(Custom(function(undo) {
  1001. if(undo) {
  1002. group.parent = null;
  1003. context.shared.contexts.remove(group);
  1004. effectFunc(true);
  1005. }
  1006. else {
  1007. group.parent = parent;
  1008. context.shared.contexts.set(group, groupCtx);
  1009. effectFunc(false);
  1010. }
  1011. if(undo)
  1012. refresh(deselect);
  1013. else
  1014. refresh(()->selectObjects([group]));
  1015. }));
  1016. refresh(effectFunc(false) ? Full : Partial, () -> selectObjects([group]));
  1017. }
  1018. function onCopy() {
  1019. if(curEdit == null) return;
  1020. if(curEdit.rootElements.length == 1) {
  1021. view.setClipboard(curEdit.rootElements[0].saveData(), "prefab");
  1022. }
  1023. else {
  1024. var lib = new hrt.prefab.Library();
  1025. for(e in curEdit.rootElements) {
  1026. lib.children.push(e);
  1027. }
  1028. view.setClipboard(lib.saveData(), "library");
  1029. }
  1030. }
  1031. function onPaste() {
  1032. var parent : PrefabElement = sceneData;
  1033. if(curEdit != null && curEdit.elements.length > 0) {
  1034. parent = curEdit.elements[0];
  1035. }
  1036. var obj = haxe.Json.parse(haxe.Json.stringify(view.getClipboard("prefab")));
  1037. if(obj != null) {
  1038. var p = hrt.prefab.Prefab.loadPrefab(obj, parent);
  1039. autoName(p);
  1040. refresh();
  1041. }
  1042. else {
  1043. obj = view.getClipboard("library");
  1044. if(obj != null) {
  1045. var lib = hrt.prefab.Prefab.loadPrefab(obj);
  1046. for(c in lib.children) {
  1047. autoName(c);
  1048. c.parent = parent;
  1049. }
  1050. refresh();
  1051. }
  1052. }
  1053. }
  1054. public function isVisible(elt: PrefabElement) {
  1055. if(elt == sceneData)
  1056. return true;
  1057. var o = elt.to(Object3D);
  1058. if(o == null)
  1059. return true;
  1060. return o.visible && !isHidden(o) && (elt.parent != null ? isVisible(elt.parent) : true);
  1061. }
  1062. public function getAllSelectable() : Array<PrefabElement> {
  1063. var ret = [];
  1064. for(elt in interactives.keys()) {
  1065. var i = interactives.get(elt);
  1066. var p : h3d.scene.Object = i;
  1067. while( p != null && p.visible )
  1068. p = p.parent;
  1069. if( p != null ) continue;
  1070. ret.push(elt);
  1071. }
  1072. return ret;
  1073. }
  1074. public function selectAll() {
  1075. selectObjects(getAllSelectable());
  1076. }
  1077. public function deselect() {
  1078. selectObjects([]);
  1079. }
  1080. public function isSelected( p : PrefabElement ) {
  1081. return curEdit != null && curEdit.elements.indexOf(p) >= 0;
  1082. }
  1083. public function setEnabled(elements : Array<PrefabElement>, enable: Bool) {
  1084. // Don't disable/enable Object3Ds, too confusing with visibility
  1085. elements = [for(e in elements) if(e.to(Object3D) == null || e.to(hrt.prefab.Reference) != null) e];
  1086. var old = [for(e in elements) e.enabled];
  1087. function apply(on) {
  1088. for(i in 0...elements.length) {
  1089. elements[i].enabled = on ? enable : old[i];
  1090. onPrefabChange(elements[i]);
  1091. }
  1092. refreshScene();
  1093. }
  1094. apply(true);
  1095. undo.change(Custom(function(undo) {
  1096. if(undo)
  1097. apply(false);
  1098. else
  1099. apply(true);
  1100. }));
  1101. }
  1102. public function isHidden(e: PrefabElement) {
  1103. if(e == null)
  1104. return false;
  1105. return hideList.exists(e);
  1106. }
  1107. function saveDisplayState() {
  1108. var state = [for (h in hideList.keys()) h.getAbsPath()];
  1109. @:privateAccess view.saveDisplayState("hideList", state);
  1110. var state = [for(f in favorites) f.getAbsPath()];
  1111. @:privateAccess view.saveDisplayState("favorites", state);
  1112. }
  1113. public function isFavorite(e: PrefabElement) {
  1114. return favorites.indexOf(e) >= 0;
  1115. }
  1116. public function setFavorite(e: PrefabElement, fav: Bool) {
  1117. if(fav && !isFavorite(e))
  1118. favorites.push(e);
  1119. else if(!fav && isFavorite(e))
  1120. favorites.remove(e);
  1121. var el = tree.getElement(e);
  1122. if(el != null)
  1123. applyTreeStyle(e, el);
  1124. refreshFavs();
  1125. saveDisplayState();
  1126. }
  1127. public function setVisible(elements : Array<PrefabElement>, visible: Bool) {
  1128. for(o in elements) {
  1129. if(visible) {
  1130. for(c in o.flatten(Object3D)) {
  1131. hideList.remove(c);
  1132. var el = tree.getElement(c);
  1133. applyTreeStyle(c, el);
  1134. applySceneStyle(c);
  1135. }
  1136. }
  1137. else {
  1138. hideList.set(o, true);
  1139. var el = tree.getElement(o);
  1140. applyTreeStyle(o, el);
  1141. applySceneStyle(o);
  1142. }
  1143. }
  1144. saveDisplayState();
  1145. }
  1146. function isolate(elts : Array<PrefabElement>) {
  1147. var toShow = elts.copy();
  1148. var toHide = [];
  1149. function hideSiblings(elt: PrefabElement) {
  1150. var p = elt.parent;
  1151. for(c in p.children) {
  1152. var needsVisible = c == elt
  1153. || toShow.indexOf(c) >= 0
  1154. || hasChild(c, toShow);
  1155. if(!needsVisible) {
  1156. toHide.push(c);
  1157. }
  1158. }
  1159. if(p != sceneData) {
  1160. hideSiblings(p);
  1161. }
  1162. }
  1163. for(e in toShow) {
  1164. hideSiblings(e);
  1165. }
  1166. setVisible(toHide, false);
  1167. }
  1168. function createRef(elt: PrefabElement, toParent: PrefabElement) {
  1169. var ref = new hrt.prefab.Reference(toParent);
  1170. ref.name = elt.name;
  1171. ref.refpath = elt.getAbsPath();
  1172. var obj3d = Std.instance(elt, Object3D);
  1173. if(obj3d != null) {
  1174. ref.x = obj3d.x;
  1175. ref.y = obj3d.y;
  1176. ref.z = obj3d.z;
  1177. ref.scaleX = obj3d.scaleX;
  1178. ref.scaleY = obj3d.scaleY;
  1179. ref.scaleZ = obj3d.scaleZ;
  1180. ref.rotationX = obj3d.rotationX;
  1181. ref.rotationY = obj3d.rotationY;
  1182. ref.rotationZ = obj3d.rotationZ;
  1183. }
  1184. addObject(ref);
  1185. }
  1186. function duplicate(thenMove: Bool) {
  1187. if(curEdit == null) return;
  1188. var elements = curEdit.rootElements;
  1189. if(elements == null || elements.length == 0)
  1190. return;
  1191. var contexts = context.shared.contexts;
  1192. var undoes = [];
  1193. var newElements = [];
  1194. for(elt in elements) {
  1195. var clone = elt.clone();
  1196. var index = elt.parent.children.indexOf(elt) + 1;
  1197. clone.parent = elt.parent;
  1198. elt.parent.children.remove(clone);
  1199. elt.parent.children.insert(index, clone);
  1200. autoName(clone);
  1201. makeInstance(clone);
  1202. newElements.push(clone);
  1203. undoes.push(function(undo) {
  1204. if(undo) elt.parent.children.remove(clone);
  1205. else elt.parent.children.insert(index, clone);
  1206. });
  1207. }
  1208. refreshTree(function() {
  1209. selectObjects(newElements);
  1210. tree.setSelection(newElements);
  1211. if(thenMove && curEdit.rootObjects.length > 0) {
  1212. gizmo.startMove(MoveXY, true);
  1213. gizmo.onFinishMove = function() {
  1214. refreshProps();
  1215. }
  1216. }
  1217. });
  1218. undo.change(Custom(function(undo) {
  1219. deselect();
  1220. var fullRefresh = false;
  1221. if(undo) {
  1222. for(elt in newElements) {
  1223. if(!removeInstance(elt)) {
  1224. fullRefresh = true;
  1225. break;
  1226. }
  1227. }
  1228. }
  1229. for(u in undoes) u(undo);
  1230. if(!undo) {
  1231. for(elt in newElements)
  1232. makeInstance(elt);
  1233. }
  1234. refresh(fullRefresh ? Full : Partial);
  1235. }));
  1236. }
  1237. function setTransform(elt: PrefabElement, ?mat: h3d.Matrix, ?position: h3d.Vector) {
  1238. var obj3d = Std.instance(elt, hrt.prefab.Object3D);
  1239. if(obj3d == null)
  1240. return;
  1241. if(mat != null)
  1242. obj3d.setTransform(mat);
  1243. else {
  1244. obj3d.x = position.x;
  1245. obj3d.y = position.y;
  1246. obj3d.z = position.z;
  1247. }
  1248. var ctx = getContext(obj3d);
  1249. if(ctx != null)
  1250. obj3d.updateInstance(ctx);
  1251. }
  1252. public function deleteElements(elts : Array<PrefabElement>, ?then: Void->Void) {
  1253. var fullRefresh = false;
  1254. var undoes = [];
  1255. for(elt in elts) {
  1256. if(!removeInstance(elt))
  1257. fullRefresh = true;
  1258. var index = elt.parent.children.indexOf(elt);
  1259. elt.parent.children.remove(elt);
  1260. undoes.push(function(undo) {
  1261. if(undo) elt.parent.children.insert(index, elt);
  1262. else elt.parent.children.remove(elt);
  1263. });
  1264. }
  1265. function refreshFunc(then) {
  1266. refresh(fullRefresh ? Full : Partial, then);
  1267. }
  1268. refreshFunc(then != null ? then : deselect);
  1269. undo.change(Custom(function(undo) {
  1270. if(!undo && !fullRefresh)
  1271. for(e in elts) removeInstance(e);
  1272. for(u in undoes) u(undo);
  1273. if(undo)
  1274. for(e in elts) makeInstance(e);
  1275. refreshFunc(then != null ? then : selectObjects.bind(undo ? elts : []));
  1276. }));
  1277. }
  1278. function reparentElement(e : Array<PrefabElement>, to : PrefabElement, index : Int) {
  1279. if( to == null )
  1280. to = sceneData;
  1281. var effectFunc = reparentImpl(e, to, index);
  1282. undo.change(Custom(function(undo) {
  1283. refresh(effectFunc(undo) ? Full : Partial);
  1284. }));
  1285. refresh(effectFunc(false) ? Full : Partial);
  1286. }
  1287. function makeTransform(mat: h3d.Matrix) {
  1288. var rot = mat.getEulerAngles();
  1289. var x = mat.tx;
  1290. var y = mat.ty;
  1291. var z = mat.tz;
  1292. var s = mat.getScale();
  1293. var scaleX = s.x;
  1294. var scaleY = s.y;
  1295. var scaleZ = s.z;
  1296. var rotationX = hxd.Math.radToDeg(rot.x);
  1297. var rotationY = hxd.Math.radToDeg(rot.y);
  1298. var rotationZ = hxd.Math.radToDeg(rot.z);
  1299. return { x : x, y : y, z : z, scaleX : scaleX, scaleY : scaleY, scaleZ : scaleZ, rotationX : rotationX, rotationY : rotationY, rotationZ : rotationZ };
  1300. }
  1301. function reparentImpl(elts : Array<PrefabElement>, toElt: PrefabElement, index: Int) : Bool -> Bool {
  1302. var effects = [];
  1303. var fullRefresh = false;
  1304. var toRefresh : Array<PrefabElement> = null;
  1305. for(elt in elts) {
  1306. var prev = elt.parent;
  1307. var prevIndex = prev.children.indexOf(elt);
  1308. var obj3d = elt.to(Object3D);
  1309. var preserveTransform = Std.is(toElt, hrt.prefab.fx.Emitter) || Std.is(prev, hrt.prefab.fx.Emitter);
  1310. var toObj = getObject(toElt);
  1311. var obj = getObject(elt);
  1312. var prevState = null, newState = null;
  1313. if(obj3d != null && toObj != null && obj != null && !preserveTransform) {
  1314. var mat = worldMat(elt);
  1315. var parentMat = worldMat(toElt);
  1316. parentMat.invert();
  1317. mat.multiply(mat, parentMat);
  1318. prevState = obj3d.saveTransform();
  1319. newState = makeTransform(mat);
  1320. }
  1321. effects.push(function(undo) {
  1322. var refresh = false;
  1323. if( undo ) {
  1324. refresh = !removeInstance(elt);
  1325. elt.parent = prev;
  1326. prev.children.remove(elt);
  1327. prev.children.insert(prevIndex, elt);
  1328. if(obj3d != null && prevState != null)
  1329. obj3d.loadTransform(prevState);
  1330. } else {
  1331. var refresh = !removeInstance(elt);
  1332. elt.parent = toElt;
  1333. toElt.children.remove(elt);
  1334. toElt.children.insert(index, elt);
  1335. if(obj3d != null && newState != null)
  1336. obj3d.loadTransform(newState);
  1337. };
  1338. if(toRefresh.indexOf(elt) < 0)
  1339. toRefresh.push(elt);
  1340. return refresh;
  1341. });
  1342. }
  1343. return function(undo) {
  1344. var refresh = false;
  1345. toRefresh = [];
  1346. for(f in effects) {
  1347. if(f(undo))
  1348. refresh = true;
  1349. }
  1350. if(!refresh) {
  1351. for(elt in toRefresh) {
  1352. removeInstance(elt);
  1353. makeInstance(elt);
  1354. }
  1355. }
  1356. return refresh;
  1357. }
  1358. }
  1359. function autoName(p : PrefabElement) {
  1360. var uniqueName = false;
  1361. if( p.type == "volumetricLightmap" || p.type == "light" )
  1362. uniqueName = true;
  1363. var prefix = null;
  1364. if(p.name != null && p.name.length > 0) {
  1365. if(uniqueName)
  1366. prefix = ~/_+[0-9]+$/.replace(p.name, "");
  1367. else
  1368. prefix = p.name;
  1369. }
  1370. else
  1371. prefix = p.getDefaultName();
  1372. if(uniqueName) {
  1373. prefix += "_";
  1374. var id = 0;
  1375. while( sceneData.getPrefabByName(prefix + id) != null )
  1376. id++;
  1377. p.name = prefix + id;
  1378. }
  1379. else
  1380. p.name = prefix;
  1381. for(c in p.children) {
  1382. autoName(c);
  1383. }
  1384. }
  1385. function update(dt:Float) {
  1386. trace(K.isDown(K.CTRL));
  1387. var cam = scene.s3d.camera;
  1388. @:privateAccess view.saveDisplayState("Camera", { x : cam.pos.x, y : cam.pos.y, z : cam.pos.z, tx : cam.target.x, ty : cam.target.y, tz : cam.target.z });
  1389. if(gizmo != null) {
  1390. if(!gizmo.moving) {
  1391. moveGizmoToSelection();
  1392. }
  1393. gizmo.update(dt);
  1394. }
  1395. event.update(dt);
  1396. for( f in updates )
  1397. f(dt);
  1398. onUpdate(dt);
  1399. }
  1400. public dynamic function onUpdate(dt:Float) {
  1401. }
  1402. // Override
  1403. function makeEditContext(elts : Array<PrefabElement>) : SceneEditorContext {
  1404. var edit = new SceneEditorContext(context, elts, this);
  1405. edit.prefabPath = view.state.path;
  1406. edit.properties = properties;
  1407. edit.scene = scene;
  1408. return edit;
  1409. }
  1410. // Override
  1411. function getNewContextMenu(current: PrefabElement, ?onMake: PrefabElement->Void=null) : Array<hide.comp.ContextMenu.ContextMenuItem> {
  1412. var newItems = new Array<hide.comp.ContextMenu.ContextMenuItem>();
  1413. var allRegs = hrt.prefab.Library.getRegistered().copy();
  1414. allRegs.remove("reference");
  1415. var parent = current == null ? sceneData : current;
  1416. var allowChildren = null;
  1417. {
  1418. var cur = parent;
  1419. while( allowChildren == null && cur != null ) {
  1420. allowChildren = cur.getHideProps().allowChildren;
  1421. cur = cur.parent;
  1422. }
  1423. }
  1424. for( ptype in allRegs.keys() ) {
  1425. var pinf = allRegs.get(ptype);
  1426. if( allowChildren != null && !allowChildren(ptype) ) {
  1427. if( pinf.inf.allowParent == null || !pinf.inf.allowParent(parent) )
  1428. continue;
  1429. } else {
  1430. if( pinf.inf.allowParent != null && !pinf.inf.allowParent(parent) )
  1431. continue;
  1432. }
  1433. if(ptype == "shader")
  1434. newItems.push(getNewShaderMenu(parent, onMake));
  1435. else
  1436. newItems.push(getNewTypeMenuItem(ptype, parent, onMake));
  1437. }
  1438. newItems.sort(function(l1,l2) return Reflect.compare(l1.label,l2.label));
  1439. return newItems;
  1440. }
  1441. function getNewTypeMenuItem(ptype: String, parent: PrefabElement, onMake: PrefabElement->Void, ?label: String) : hide.comp.ContextMenu.ContextMenuItem {
  1442. var pmodel = hrt.prefab.Library.getRegistered().get(ptype);
  1443. return {
  1444. label : label != null ? label : pmodel.inf.name,
  1445. click : function() {
  1446. function make(?path) {
  1447. var p = Type.createInstance(pmodel.cl, [parent]);
  1448. @:privateAccess p.type = ptype;
  1449. if(path != null)
  1450. p.source = path;
  1451. autoName(p);
  1452. if(onMake != null)
  1453. onMake(p);
  1454. return p;
  1455. }
  1456. if( pmodel.inf.fileSource != null )
  1457. ide.chooseFile(pmodel.inf.fileSource, function(path) {
  1458. if( path == null ) return;
  1459. var p = make(path);
  1460. addObject(p);
  1461. });
  1462. else
  1463. addObject(make());
  1464. }
  1465. };
  1466. }
  1467. function getNewShaderMenu(parentElt: PrefabElement, onMake: PrefabElement->Void) : hide.comp.ContextMenu.ContextMenuItem {
  1468. var custom = getNewTypeMenuItem("shader", parentElt, onMake, "Custom...");
  1469. function shaderItem(name, path) : hide.comp.ContextMenu.ContextMenuItem {
  1470. return {
  1471. label : name,
  1472. click : function() {
  1473. var s = new hrt.prefab.Shader(parentElt);
  1474. s.source = path;
  1475. s.name = name;
  1476. addObject(s);
  1477. }
  1478. }
  1479. }
  1480. var menu = [custom];
  1481. var shaders : Array<String> = hide.Ide.inst.currentConfig.get("fx.shaders", []);
  1482. for(path in shaders) {
  1483. var name = path;
  1484. if(StringTools.endsWith(name, ".hx")) {
  1485. name = name.substr(0, -3);
  1486. name = name.split("/").pop();
  1487. }
  1488. else {
  1489. name = name.split(".").pop();
  1490. }
  1491. menu.push(shaderItem(name, path));
  1492. }
  1493. return {
  1494. label: "Shaders",
  1495. menu: menu
  1496. };
  1497. }
  1498. public function getZ(x: Float, y: Float) {
  1499. var offset = 1000;
  1500. var ray = h3d.col.Ray.fromValues(x, y, offset, 0, 0, -1);
  1501. var dist = projectToGround(ray);
  1502. if(dist >= 0) {
  1503. return offset - dist;
  1504. }
  1505. return 0.;
  1506. }
  1507. public function projectToGround(ray: h3d.col.Ray) {
  1508. var minDist = -1.;
  1509. var zPlane = h3d.col.Plane.Z(0);
  1510. var pt = ray.intersect(zPlane);
  1511. if(pt != null) {
  1512. minDist = pt.sub(ray.getPos()).length();
  1513. }
  1514. return minDist;
  1515. }
  1516. public function screenToWorld(sx: Float, sy: Float) {
  1517. var camera = scene.s3d.camera;
  1518. var ray = camera.rayFromScreen(sx, sy);
  1519. var dist = projectToGround(ray);
  1520. if(dist >= 0) {
  1521. return ray.getPoint(dist);
  1522. }
  1523. return null;
  1524. }
  1525. public function worldToScreen(wx: Float, wy: Float, wz: Float) {
  1526. var camera = scene.s3d.camera;
  1527. var pt = camera.project(wx, wy, wz, scene.s2d.width, scene.s2d.height);
  1528. return new h2d.col.Point(pt.x, pt.y);
  1529. }
  1530. public function worldMat(?obj: Object, ?elt: PrefabElement) {
  1531. if(obj != null) {
  1532. if(obj.defaultTransform != null) {
  1533. var m = obj.defaultTransform.clone();
  1534. m.invert();
  1535. m.multiply(m, obj.getAbsPos());
  1536. return m;
  1537. }
  1538. else {
  1539. return obj.getAbsPos().clone();
  1540. }
  1541. }
  1542. else {
  1543. var mat = new h3d.Matrix();
  1544. mat.identity();
  1545. var o = Std.instance(elt, Object3D);
  1546. while(o != null) {
  1547. mat.multiply(mat, o.getTransform());
  1548. o = o.parent.to(hrt.prefab.Object3D);
  1549. }
  1550. return mat;
  1551. }
  1552. }
  1553. static function getPivot(objects: Array<Object>) {
  1554. var pos = new h3d.Vector();
  1555. for(o in objects) {
  1556. pos = pos.add(o.getAbsPos().getPosition());
  1557. }
  1558. pos.scale3(1.0 / objects.length);
  1559. return pos;
  1560. }
  1561. public static function hasParent(elt: PrefabElement, list: Array<PrefabElement>) {
  1562. for(p in list) {
  1563. if(isParent(elt, p))
  1564. return true;
  1565. }
  1566. return false;
  1567. }
  1568. public static function hasChild(elt: PrefabElement, list: Array<PrefabElement>) {
  1569. for(p in list) {
  1570. if(isParent(p, elt))
  1571. return true;
  1572. }
  1573. return false;
  1574. }
  1575. public static function isParent(elt: PrefabElement, parent: PrefabElement) {
  1576. var p = elt.parent;
  1577. while(p != null) {
  1578. if(p == parent) return true;
  1579. p = p.parent;
  1580. }
  1581. return false;
  1582. }
  1583. }