SceneEditor.hx 76 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753
  1. package hide.comp;
  2. import hrt.prefab.Reference;
  3. import h3d.scene.Mesh;
  4. import h3d.col.FPoint;
  5. import h3d.col.Ray;
  6. import h3d.col.PolygonBuffer;
  7. import h3d.prim.HMDModel;
  8. import h3d.col.Collider.OptimizedCollider;
  9. import h3d.Vector;
  10. import hxd.Key as K;
  11. import hxd.Math as M;
  12. import hrt.prefab.Prefab as PrefabElement;
  13. import hrt.prefab.Object2D;
  14. import hrt.prefab.Object3D;
  15. import h3d.scene.Object;
  16. import hide.comp.cdb.DataFiles;
  17. import hide.view.CameraController.CamController as CameraController;
  18. enum SelectMode {
  19. /**
  20. Update tree, add undo command
  21. **/
  22. Default;
  23. /**
  24. Update tree only
  25. **/
  26. NoHistory;
  27. /**
  28. Add undo but don't update tree
  29. **/
  30. NoTree;
  31. /**
  32. Don't refresh tree and don't undo command
  33. **/
  34. Nothing;
  35. }
  36. @:access(hide.comp.SceneEditor)
  37. class SceneEditorContext extends hide.prefab.EditContext {
  38. public var editor(default, null) : SceneEditor;
  39. public var elements : Array<PrefabElement>;
  40. public var rootObjects(default, null): Array<Object>;
  41. public var rootObjects2D(default, null): Array<h2d.Object>;
  42. public var rootElements(default, null): Array<PrefabElement>;
  43. public function new(ctx, elts, editor) {
  44. super(ctx);
  45. this.editor = editor;
  46. this.updates = editor.updates;
  47. this.elements = elts;
  48. rootObjects = [];
  49. rootObjects2D = [];
  50. rootElements = [];
  51. cleanups = [];
  52. for(elt in elements) {
  53. // var obj3d = elt.to(Object3D);
  54. // if(obj3d == null) continue;
  55. if(!SceneEditor.hasParent(elt, elements)) {
  56. rootElements.push(elt);
  57. var ctx = getContext(elt);
  58. if(ctx != null) {
  59. var pobj = elt.parent == editor.sceneData ? ctx.shared.root3d : getContextRec(elt.parent).local3d;
  60. var pobj2d = elt.parent == editor.sceneData ? ctx.shared.root2d : getContextRec(elt.parent).local2d;
  61. if( ctx.local3d != pobj )
  62. rootObjects.push(ctx.local3d);
  63. if( ctx.local2d != pobj2d )
  64. rootObjects2D.push(ctx.local2d);
  65. }
  66. }
  67. }
  68. }
  69. override function screenToGround(x:Float, y:Float, ?forPrefab:hrt.prefab.Prefab) {
  70. return editor.screenToGround(x, y, forPrefab);
  71. }
  72. override function positionToGroundZ(x:Float, y:Float, ?forPrefab:hrt.prefab.Prefab):Float {
  73. return editor.getZ(x, y, forPrefab);
  74. }
  75. override function getCurrentProps( p : hrt.prefab.Prefab ) {
  76. var cur = editor.curEdit;
  77. return cur != null && cur.elements[0] == p ? editor.properties.element : new Element();
  78. }
  79. function getContextRec( p : hrt.prefab.Prefab ) {
  80. if( p == null )
  81. return editor.context;
  82. var c = editor.context.shared.contexts.get(p);
  83. if( c == null )
  84. return getContextRec(p.parent);
  85. return c;
  86. }
  87. override function rebuildProperties() {
  88. editor.scene.setCurrent();
  89. editor.selectElements(elements, NoHistory);
  90. }
  91. override function rebuildPrefab( p : hrt.prefab.Prefab ) {
  92. // refresh all for now
  93. editor.refresh();
  94. }
  95. public function cleanup() {
  96. for( c in cleanups.copy() )
  97. c();
  98. cleanups = [];
  99. }
  100. override function onChange(p : PrefabElement, pname: String) {
  101. super.onChange(p, pname);
  102. editor.onPrefabChange(p, pname);
  103. }
  104. }
  105. enum RefreshMode {
  106. Partial;
  107. Full;
  108. }
  109. typedef CustomPivot = { elt : PrefabElement, mesh : Mesh, locPos : Vector };
  110. class SceneEditor {
  111. public var tree : hide.comp.IconTree<PrefabElement>;
  112. public var scene : hide.comp.Scene;
  113. public var properties : hide.comp.PropsEditor;
  114. public var context(default,null) : hrt.prefab.Context;
  115. public var curEdit(default, null) : SceneEditorContext;
  116. public var snapToGround = false;
  117. public var localTransform = true;
  118. public var cameraController : h3d.scene.CameraController;
  119. public var cameraController2D : hide.view.l3d.CameraController2D;
  120. public var editorDisplay(default,set) : Bool;
  121. public var camera2D(default,set) : Bool = false;
  122. public var objectAreSelectable = true;
  123. // Windows default is 0.5
  124. public var dblClickDuration = 0.2;
  125. var updates : Array<Float -> Void> = [];
  126. var showGizmo = true;
  127. var gizmo : hide.view.l3d.Gizmo;
  128. var gizmo2d : hide.view.l3d.Gizmo2D;
  129. static var customPivot : CustomPivot;
  130. var interactives : Map<PrefabElement, hxd.SceneEvents.Interactive>;
  131. var ide : hide.Ide;
  132. public var event(default, null) : hxd.WaitEvent;
  133. var hideList : Map<PrefabElement, Bool> = new Map();
  134. var undo(get, null):hide.ui.UndoHistory;
  135. function get_undo() { return view.undo; }
  136. public var view(default, null) : hide.view.FileView;
  137. var sceneData : PrefabElement;
  138. var lastRenderProps : hrt.prefab.RenderProps;
  139. var focusedSinceSelect = false;
  140. public function new(view, data) {
  141. ide = hide.Ide.inst;
  142. this.view = view;
  143. this.sceneData = data;
  144. event = new hxd.WaitEvent();
  145. var propsEl = new Element('<div class="props"></div>');
  146. properties = new hide.comp.PropsEditor(undo,null,propsEl);
  147. properties.saveDisplayKey = view.saveDisplayKey + "/properties";
  148. tree = new hide.comp.IconTree();
  149. tree.async = false;
  150. tree.autoOpenNodes = false;
  151. var sceneEl = new Element('<div class="heaps-scene"></div>');
  152. scene = new hide.comp.Scene(view.config, null, sceneEl);
  153. scene.editor = this;
  154. scene.onReady = onSceneReady;
  155. scene.onResize = function() {
  156. if( cameraController2D != null ) cameraController2D.toTarget();
  157. onResize();
  158. };
  159. context = new hrt.prefab.Context();
  160. context.shared = new hide.prefab.ContextShared(scene);
  161. context.shared.currentPath = view.state.path;
  162. context.init();
  163. editorDisplay = true;
  164. view.keys.register("copy", onCopy);
  165. view.keys.register("paste", onPaste);
  166. view.keys.register("cancel", deselect);
  167. view.keys.register("selectAll", selectAll);
  168. view.keys.register("duplicate", duplicate.bind(true));
  169. view.keys.register("duplicateInPlace", duplicate.bind(false));
  170. view.keys.register("group", groupSelection);
  171. view.keys.register("delete", () -> deleteElements(curEdit.rootElements));
  172. view.keys.register("search", function() tree.openFilter());
  173. view.keys.register("rename", function () {
  174. if(curEdit.rootElements.length > 0)
  175. tree.editNode(curEdit.rootElements[0]);
  176. });
  177. view.keys.register("sceneeditor.focus", focusSelection);
  178. view.keys.register("sceneeditor.lasso", startLassoSelect);
  179. view.keys.register("sceneeditor.hide", function() {
  180. var isHidden = isHidden(curEdit.rootElements[0]);
  181. setVisible(curEdit.elements, isHidden);
  182. });
  183. view.keys.register("sceneeditor.isolate", function() { isolate(curEdit.elements); });
  184. view.keys.register("sceneeditor.showAll", function() { setVisible(context.shared.elements(), true); });
  185. view.keys.register("sceneeditor.selectParent", function() {
  186. if(curEdit.rootElements.length > 0) {
  187. var p = curEdit.rootElements[0].parent;
  188. if( p != null && p != sceneData ) selectElements([p]);
  189. }
  190. });
  191. view.keys.register("sceneeditor.reparent", function() {
  192. if(curEdit.rootElements.length > 1) {
  193. var children = curEdit.rootElements.copy();
  194. var parent = children.pop();
  195. reparentElement(children, parent, 0);
  196. }
  197. });
  198. view.keys.register("sceneeditor.editPivot", editPivot);
  199. view.keys.register("sceneeditor.gatherToMouse", gatherToMouse);
  200. // Load display state
  201. {
  202. var all = sceneData.flatten(hrt.prefab.Prefab);
  203. var list = @:privateAccess view.getDisplayState("hideList");
  204. if(list != null) {
  205. var m = [for(i in (list:Array<Dynamic>)) i => true];
  206. for(p in all) {
  207. if(m.exists(p.getAbsPath(true)))
  208. hideList.set(p, true);
  209. }
  210. }
  211. }
  212. }
  213. public function dispose() {
  214. scene.dispose();
  215. tree.dispose();
  216. }
  217. function set_camera2D(b) {
  218. if( cameraController != null ) cameraController.visible = !b;
  219. if( cameraController2D != null ) cameraController2D.visible = b;
  220. return camera2D = b;
  221. }
  222. public function onResourceChanged(lib : hxd.fmt.hmd.Library) {
  223. var models = sceneData.findAll(p -> Std.downcast(p, PrefabElement));
  224. var toRebuild : Array<PrefabElement> = [];
  225. for(m in models) {
  226. @:privateAccess if(m.source == lib.resource.entry.path) {
  227. if (toRebuild.indexOf(m) < 0) {
  228. toRebuild.push(m);
  229. }
  230. }
  231. }
  232. for(m in toRebuild) {
  233. removeInstance(m);
  234. makeInstance(m);
  235. }
  236. }
  237. public dynamic function onResize() {
  238. }
  239. function set_editorDisplay(v) {
  240. context.shared.editorDisplay = v;
  241. return editorDisplay = v;
  242. }
  243. public function getSelection() {
  244. return curEdit != null ? curEdit.elements : [];
  245. }
  246. function makeCamController() : h3d.scene.CameraController {
  247. var c = new CameraController(scene.s3d, this);
  248. c.friction = 0.9;
  249. c.panSpeed = 0.6;
  250. c.zoomAmount = 1.05;
  251. c.smooth = 0.7;
  252. c.minDistance = 1;
  253. return c;
  254. }
  255. public function setFullScreen( b : Bool ) {
  256. view.fullScreen = b;
  257. if( b ) {
  258. view.element.find(".tabs").hide();
  259. } else {
  260. view.element.find(".tabs").show();
  261. }
  262. }
  263. function makeCamController2D() {
  264. return new hide.view.l3d.CameraController2D(context.shared.root2d);
  265. }
  266. function focusSelection() {
  267. if(curEdit.rootObjects.length > 0) {
  268. var bnds = new h3d.col.Bounds();
  269. var centroid = new h3d.Vector();
  270. for(obj in curEdit.rootObjects) {
  271. centroid = centroid.add(obj.getAbsPos().getPosition());
  272. bnds.add(obj.getBounds());
  273. }
  274. if(!bnds.isEmpty()) {
  275. var s = bnds.toSphere();
  276. var r = focusedSinceSelect ? s.r * 4.0 : null;
  277. cameraController.set(r, null, null, s.getCenter());
  278. }
  279. else {
  280. centroid.scale3(1.0 / curEdit.rootObjects.length);
  281. cameraController.set(centroid.toPoint());
  282. }
  283. }
  284. for(obj in curEdit.rootElements)
  285. tree.revealNode(obj);
  286. focusedSinceSelect = true;
  287. }
  288. function getAvailableTags(p: PrefabElement) : Array<{id: String, color: String}>{
  289. return null;
  290. }
  291. public function getTag(p: PrefabElement) {
  292. if(p.props != null) {
  293. var tagId = Reflect.field(p.props, "tag");
  294. if(tagId != null) {
  295. var tags = getAvailableTags(p);
  296. if(tags != null)
  297. return Lambda.find(tags, t -> t.id == tagId);
  298. }
  299. }
  300. return null;
  301. }
  302. public function setTag(p: PrefabElement, tag: String) {
  303. if(p.props == null)
  304. p.props = {};
  305. var prevVal = getTag(p);
  306. Reflect.setField(p.props, "tag", tag);
  307. onPrefabChange(p, "tag");
  308. undo.change(Field(p.props, "tag", prevVal), function() {
  309. onPrefabChange(p, "tag");
  310. });
  311. }
  312. function getTagMenu(p: PrefabElement) : Array<hide.comp.ContextMenu.ContextMenuItem> {
  313. var tags = getAvailableTags(p);
  314. if(tags == null) return null;
  315. var ret = [];
  316. for(tag in tags) {
  317. var style = 'background-color: ${tag.color};';
  318. ret.push({
  319. label: '<span class="tag-disp-expand"><span class="tag-disp" style="$style">${tag.id}</span></span>',
  320. click: function () {
  321. if(getTag(p) == tag)
  322. setTag(p, null);
  323. else
  324. setTag(p, tag.id);
  325. },
  326. checked: getTag(p) == tag,
  327. stayOpen: true,
  328. });
  329. }
  330. return ret;
  331. }
  332. function onSceneReady() {
  333. tree.saveDisplayKey = view.saveDisplayKey + '/tree';
  334. scene.s2d.addChild(context.shared.root2d);
  335. scene.s3d.addChild(context.shared.root3d);
  336. gizmo = new hide.view.l3d.Gizmo(scene);
  337. gizmo.moveStep = view.config.get("sceneeditor.gridSnapStep");
  338. gizmo2d = new hide.view.l3d.Gizmo2D();
  339. scene.s2d.add(gizmo2d, 1); // over local3d
  340. cameraController = makeCamController();
  341. cameraController.onClick = function(e) {
  342. switch( e.button ) {
  343. case K.MOUSE_RIGHT:
  344. selectNewObject();
  345. case K.MOUSE_LEFT:
  346. selectElements([]);
  347. }
  348. };
  349. if (!camera2D)
  350. resetCamera();
  351. var cam = @:privateAccess view.getDisplayState("Camera");
  352. if( cam != null ) {
  353. scene.s3d.camera.pos.set(cam.x, cam.y, cam.z);
  354. scene.s3d.camera.target.set(cam.tx, cam.ty, cam.tz);
  355. }
  356. cameraController.loadFromCamera();
  357. scene.s2d.defaultSmooth = true;
  358. context.shared.root2d.x = scene.s2d.width >> 1;
  359. context.shared.root2d.y = scene.s2d.height >> 1;
  360. cameraController2D = makeCamController2D();
  361. cameraController2D.onClick = cameraController.onClick;
  362. var cam2d = @:privateAccess view.getDisplayState("Camera2D");
  363. if( cam2d != null ) {
  364. context.shared.root2d.x = scene.s2d.width*0.5 + cam2d.x;
  365. context.shared.root2d.y = scene.s2d.height*0.5 + cam2d.y;
  366. context.shared.root2d.setScale(cam2d.z);
  367. }
  368. cameraController2D.loadFromScene();
  369. if (camera2D)
  370. resetCamera();
  371. scene.onUpdate = update;
  372. // BUILD scene tree
  373. var icons = new Map();
  374. var iconsConfig = view.config.get("sceneeditor.icons");
  375. for( f in Reflect.fields(iconsConfig) )
  376. icons.set(f, Reflect.field(iconsConfig,f));
  377. function makeItem(o:PrefabElement, ?state) : hide.comp.IconTree.IconTreeItem<PrefabElement> {
  378. var p = o.getHideProps();
  379. var ref = o.to(Reference);
  380. var icon = p.icon;
  381. var ct = o.getCdbType();
  382. if( ct != null && icons.exists(ct) )
  383. icon = icons.get(ct);
  384. var r : hide.comp.IconTree.IconTreeItem<PrefabElement> = {
  385. value : o,
  386. text : o.name,
  387. icon : "ico ico-"+icon,
  388. children : o.children.length > 0 || (ref != null && @:privateAccess ref.editMode),
  389. state: state
  390. };
  391. return r;
  392. }
  393. tree.get = function(o:PrefabElement) {
  394. var objs = o == null ? sceneData.children : Lambda.array(o);
  395. if( o != null && o.getHideProps().hideChildren != null ) {
  396. var hideChildren = o.getHideProps().hideChildren;
  397. var visibleObjs = [];
  398. for( o in objs ) {
  399. if( hideChildren(o) )
  400. continue;
  401. visibleObjs.push(o);
  402. }
  403. objs = visibleObjs;
  404. }
  405. var ref = o == null ? null : o.to(Reference);
  406. @:privateAccess if( ref != null && ref.editMode && ref.ref != null ) {
  407. for( c in ref.ref )
  408. objs.push(c);
  409. }
  410. var out = [for( o in objs ) makeItem(o)];
  411. return out;
  412. };
  413. function ctxMenu(tree, e) {
  414. e.preventDefault();
  415. var current = tree.getCurrentOver();
  416. if(current != null && (curEdit == null || curEdit.elements.indexOf(current) < 0)) {
  417. selectElements([current]);
  418. }
  419. var newItems = getNewContextMenu(current);
  420. var menuItems : Array<hide.comp.ContextMenu.ContextMenuItem> = [
  421. { label : "New...", menu : newItems },
  422. ];
  423. var actionItems : Array<hide.comp.ContextMenu.ContextMenuItem> = [
  424. { label : "Rename", enabled : current != null, click : function() tree.editNode(current), keys : view.config.get("key.rename") },
  425. { label : "Delete", enabled : current != null, click : function() deleteElements(curEdit.rootElements), keys : view.config.get("key.delete") },
  426. { label : "Duplicate", enabled : current != null, click : duplicate.bind(false), keys : view.config.get("key.duplicateInPlace") },
  427. ];
  428. var isObj = current != null && (current.to(Object3D) != null || current.to(Object2D) != null);
  429. var isRef = isReference(current);
  430. if( current != null ) {
  431. menuItems.push({ label : "Enable", checked : current.enabled, stayOpen : true, click : function() setEnabled(curEdit.elements, !current.enabled) });
  432. menuItems.push({ label : "Editor only", checked : current.editorOnly, stayOpen : true, click : function() setEditorOnly(curEdit.elements, !current.editorOnly) });
  433. }
  434. if( isObj ) {
  435. menuItems = menuItems.concat([
  436. { label : "Show in editor" , checked : !isHidden(current), stayOpen : true, click : function() setVisible(curEdit.elements, isHidden(current)), keys : view.config.get("key.sceneeditor.hide") },
  437. { label : "Locked", checked : current.locked, stayOpen : true, click : function() {
  438. current.locked = !current.locked;
  439. setLock(curEdit.elements, current.locked);
  440. } },
  441. { label : "Select all", click : selectAll, keys : view.config.get("key.selectAll") },
  442. { label : "Select children", enabled : current != null, click : function() selectElements(current.flatten()) },
  443. ]);
  444. if( !isRef )
  445. actionItems = actionItems.concat([
  446. { label : "Isolate", click : function() isolate(curEdit.elements), keys : view.config.get("key.sceneeditor.isolate") },
  447. { label : "Group", enabled : curEdit != null && canGroupSelection(), click : groupSelection, keys : view.config.get("key.group") },
  448. ]);
  449. }
  450. if( current != null ) {
  451. var menu = getTagMenu(current);
  452. if(menu != null)
  453. menuItems.push({ label : "Tag", menu: menu });
  454. }
  455. menuItems.push({ isSeparator : true, label : "" });
  456. new hide.comp.ContextMenu(menuItems.concat(actionItems));
  457. };
  458. tree.element.parent().contextmenu(ctxMenu.bind(tree));
  459. tree.allowRename = true;
  460. tree.init();
  461. tree.onClick = function(e, _) {
  462. selectElements(tree.getSelection(), NoTree);
  463. }
  464. tree.onDblClick = function(e) {
  465. focusSelection();
  466. return true;
  467. }
  468. tree.onRename = function(e, name) {
  469. var oldName = e.name;
  470. e.name = name;
  471. undo.change(Field(e, "name", oldName), function() {
  472. tree.refresh();
  473. refreshScene();
  474. });
  475. refreshScene();
  476. return true;
  477. };
  478. tree.onAllowMove = function(e, to) return checkAllowParent({cl : e.type, inf : e.getHideProps()}, to);
  479. // Batch tree.onMove, which is called for every node moved, causing problems with undo and refresh
  480. {
  481. var movetimer : haxe.Timer = null;
  482. var moved = [];
  483. tree.onMove = function(e, to, idx) {
  484. if(movetimer != null) {
  485. movetimer.stop();
  486. }
  487. moved.push(e);
  488. movetimer = haxe.Timer.delay(function() {
  489. reparentElement(moved, to, idx);
  490. movetimer = null;
  491. moved = [];
  492. }, 50);
  493. }
  494. }
  495. tree.applyStyle = function(p, el) applyTreeStyle(p, el);
  496. selectElements([]);
  497. refresh();
  498. this.camera2D = camera2D;
  499. }
  500. function checkAllowParent(prefabInf, prefabParent : PrefabElement) : Bool {
  501. if (prefabInf.inf.allowParent == null)
  502. if (prefabParent == null || prefabParent.getHideProps().allowChildren == null || (prefabParent.getHideProps().allowChildren != null && prefabParent.getHideProps().allowChildren(prefabInf.cl)))
  503. return true;
  504. else return false;
  505. if (prefabParent == null)
  506. if (prefabInf.inf.allowParent(sceneData))
  507. return true;
  508. else return false;
  509. if ((prefabParent.getHideProps().allowChildren == null || prefabParent.getHideProps().allowChildren != null && prefabParent.getHideProps().allowChildren(prefabInf.cl))
  510. && prefabInf.inf.allowParent(prefabParent))
  511. return true;
  512. return false;
  513. };
  514. public function refresh( ?mode: RefreshMode, ?callb: Void->Void) {
  515. if(mode == null || mode == Full) refreshScene();
  516. refreshTree(callb);
  517. }
  518. public function collapseTree() {
  519. tree.collapseAll();
  520. }
  521. function refreshTree( ?callb ) {
  522. tree.refresh(function() {
  523. var all = sceneData.flatten(hrt.prefab.Prefab);
  524. for(elt in all) {
  525. var el = tree.getElement(elt);
  526. if(el == null) continue;
  527. applyTreeStyle(elt, el);
  528. }
  529. if(callb != null) callb();
  530. });
  531. }
  532. function refreshProps() {
  533. selectElements(curEdit.elements, Nothing);
  534. }
  535. public function refreshScene() {
  536. var sh = context.shared;
  537. sh.root3d.remove();
  538. sh.root2d.remove();
  539. for( c in sh.contexts )
  540. if( c != null && c.cleanup != null )
  541. c.cleanup();
  542. context.shared = sh = new hide.prefab.ContextShared(scene);
  543. sh.editorDisplay = editorDisplay;
  544. sh.currentPath = view.state.path;
  545. scene.s3d.addChild(sh.root3d);
  546. scene.s2d.addChild(sh.root2d);
  547. sh.root2d.addChild(cameraController2D);
  548. scene.setCurrent();
  549. scene.onResize();
  550. context.init();
  551. sceneData.make(context);
  552. var bgcol = scene.engine.backgroundColor;
  553. scene.init();
  554. scene.engine.backgroundColor = bgcol;
  555. refreshInteractives();
  556. var all = sceneData.flatten(hrt.prefab.Prefab);
  557. for(elt in all)
  558. applySceneStyle(elt);
  559. if( lastRenderProps == null ) {
  560. var renderProps = getAllWithRefs(sceneData,hrt.prefab.RenderProps);
  561. for( r in renderProps )
  562. if( @:privateAccess r.isDefault ) {
  563. lastRenderProps = r;
  564. break;
  565. }
  566. if( lastRenderProps == null )
  567. lastRenderProps = renderProps[0];
  568. }
  569. if( lastRenderProps != null )
  570. lastRenderProps.applyProps(scene.s3d.renderer);
  571. else {
  572. var refPrefab = new Reference();
  573. refPrefab.source = view.config.getLocal("scene.renderProps");
  574. refPrefab.makeInstance(context);
  575. if( @:privateAccess refPrefab.ref != null ) {
  576. var renderProps = @:privateAccess refPrefab.ref.get(hrt.prefab.RenderProps);
  577. if( renderProps != null )
  578. renderProps.applyProps(scene.s3d.renderer);
  579. }
  580. }
  581. onRefresh();
  582. }
  583. function getAllWithRefs<T:hrt.prefab.Prefab>( p : hrt.prefab.Prefab, cl : Class<T>, ?arr : Array<T> ) : Array<T> {
  584. if( arr == null ) arr = [];
  585. var v = p.to(cl);
  586. if( v != null ) arr.push(v);
  587. for( c in p.children )
  588. getAllWithRefs(c, cl, arr);
  589. var ref = p.to(Reference);
  590. @:privateAccess if( ref != null && ref.ref != null ) getAllWithRefs(ref.ref, cl, arr);
  591. return arr;
  592. }
  593. public dynamic function onRefresh() {
  594. }
  595. function makeInteractive( elt : PrefabElement, ?shared : hrt.prefab.ContextShared ) {
  596. if( shared == null )
  597. shared = context.shared;
  598. var ctx = shared.contexts[elt];
  599. if( ctx == null )
  600. return;
  601. var int = elt.makeInteractive(ctx);
  602. if( int != null ) {
  603. initInteractive(elt,cast int);
  604. if( isLocked(elt) ) toggleInteractive(elt, false);
  605. }
  606. var ref = Std.downcast(elt,Reference);
  607. @:privateAccess if( ref != null && ref.editMode ) {
  608. var ctx = shared.getRef(elt);
  609. for( p in ref.ref.flatten() )
  610. makeInteractive(p, ctx);
  611. }
  612. }
  613. function toggleInteractive( e : PrefabElement, visible : Bool ) {
  614. var int = getInteractive(e);
  615. if( int == null ) return;
  616. var i2d = Std.downcast(int,h2d.Interactive);
  617. var i3d = Std.downcast(int,h3d.scene.Interactive);
  618. if( i2d != null ) i2d.visible = visible;
  619. if( i3d != null ) i3d.visible = visible;
  620. }
  621. function initInteractive( elt : PrefabElement, int : {
  622. dynamic function onPush(e:hxd.Event) : Void;
  623. dynamic function onMove(e:hxd.Event) : Void;
  624. dynamic function onRelease(e:hxd.Event) : Void;
  625. dynamic function onClick(e:hxd.Event) : Void;
  626. function handleEvent(e:hxd.Event) : Void;
  627. function preventClick() : Void;
  628. } ) {
  629. if( int == null ) return;
  630. var startDrag = null;
  631. var curDrag = null;
  632. var dragBtn = -1;
  633. var lastPush : Array<Float> = null;
  634. var i3d = Std.downcast(int, h3d.scene.Interactive);
  635. var i2d = Std.downcast(int, h2d.Interactive);
  636. var prevClickTime : Float = -1e20;
  637. int.onClick = function(e) {
  638. if(e.button == K.MOUSE_RIGHT) {
  639. var dist = hxd.Math.distance(scene.s2d.mouseX - lastPush[0], scene.s2d.mouseY - lastPush[1]);
  640. if( dist > 5 ) return;
  641. selectNewObject();
  642. e.propagate = false;
  643. return;
  644. }
  645. }
  646. int.onPush = function(e) {
  647. if( e.button == K.MOUSE_MIDDLE ) return;
  648. startDrag = [scene.s2d.mouseX, scene.s2d.mouseY];
  649. if( e.button == K.MOUSE_RIGHT )
  650. lastPush = startDrag;
  651. dragBtn = e.button;
  652. if( e.button == K.MOUSE_LEFT ) {
  653. var elts = null;
  654. if(K.isDown(K.SHIFT)) {
  655. if(Type.getClass(elt.parent) == hrt.prefab.Object3D)
  656. elts = [elt.parent];
  657. else
  658. elts = elt.parent.children;
  659. }
  660. else
  661. elts = [elt];
  662. if(K.isDown(K.CTRL)) {
  663. var current = curEdit.elements.copy();
  664. if(current.indexOf(elt) < 0) {
  665. for(e in elts) {
  666. if(current.indexOf(e) < 0)
  667. current.push(e);
  668. }
  669. }
  670. else {
  671. for(e in elts)
  672. current.remove(e);
  673. }
  674. selectElements(current);
  675. }
  676. else
  677. selectElements(elts);
  678. }
  679. // ensure we get onMove even if outside our interactive, allow fast click'n'drag
  680. if( e.button == K.MOUSE_LEFT ) {
  681. scene.sevents.startCapture(int.handleEvent);
  682. e.propagate = false;
  683. }
  684. };
  685. int.onRelease = function(e) {
  686. if( e.button == K.MOUSE_MIDDLE ) return;
  687. startDrag = null;
  688. curDrag = null;
  689. dragBtn = -1;
  690. if( e.button == K.MOUSE_LEFT ) {
  691. scene.sevents.stopCapture();
  692. e.propagate = false;
  693. var curTime = haxe.Timer.stamp();
  694. if( curTime - prevClickTime < dblClickDuration && !(elt.getHideProps().isGround)) {
  695. focusSelection();
  696. prevClickTime = -1e20;
  697. }
  698. else
  699. prevClickTime = curTime;
  700. }
  701. }
  702. int.onMove = function(e) {
  703. if(startDrag != null && hxd.Math.distance(startDrag[0] - scene.s2d.mouseX, startDrag[1] - scene.s2d.mouseY) > 5 ) {
  704. if(dragBtn == K.MOUSE_LEFT ) {
  705. if( i3d != null ) {
  706. moveGizmoToSelection();
  707. gizmo.startMove(MoveXY);
  708. }
  709. if( i2d != null ) {
  710. moveGizmoToSelection();
  711. gizmo2d.startMove(Pan);
  712. }
  713. }
  714. int.preventClick();
  715. startDrag = null;
  716. }
  717. }
  718. interactives.set(elt,cast int);
  719. }
  720. function selectNewObject() {
  721. if( !objectAreSelectable )
  722. return;
  723. var parentEl = sceneData;
  724. // for now always create at scene root, not `curEdit.rootElements[0];`
  725. var group = getParentGroup(parentEl);
  726. if( group != null )
  727. parentEl = group;
  728. var originPt = getPickTransform(parentEl).getPosition();
  729. var newItems = getNewContextMenu(parentEl, function(newElt) {
  730. var newObj3d = Std.downcast(newElt, Object3D);
  731. if(newObj3d != null) {
  732. var newPos = new h3d.Matrix();
  733. newPos.identity();
  734. newPos.setPosition(originPt);
  735. var invParent = getObject(parentEl).getAbsPos().clone();
  736. invParent.invert();
  737. newPos.multiply(newPos, invParent);
  738. newObj3d.setTransform(newPos);
  739. }
  740. var newObj2d = Std.downcast(newElt, Object2D);
  741. if( newObj2d != null ) {
  742. var pt = new h2d.col.Point(scene.s2d.mouseX, scene.s2d.mouseY);
  743. var l2d = getContext(parentEl).local2d;
  744. l2d.globalToLocal(pt);
  745. newObj2d.x = pt.x;
  746. newObj2d.y = pt.y;
  747. }
  748. });
  749. var menuItems : Array<hide.comp.ContextMenu.ContextMenuItem> = [
  750. { label : "New...", menu : newItems },
  751. { isSeparator : true, label : "" },
  752. {
  753. label : "Gather here",
  754. click : gatherToMouse,
  755. enabled : (curEdit.rootElements.length > 0),
  756. keys : view.config.get("key.sceneeditor.gatherToMouse"),
  757. },
  758. ];
  759. new hide.comp.ContextMenu(menuItems);
  760. }
  761. public function refreshInteractive(elt : PrefabElement) {
  762. var int = interactives.get(elt);
  763. if(int != null) {
  764. var i3d = Std.downcast(int, h3d.scene.Interactive);
  765. if( i3d != null ) i3d.remove() else cast(int,h2d.Interactive).remove();
  766. interactives.remove(elt);
  767. }
  768. makeInteractive(elt);
  769. }
  770. function refreshInteractives() {
  771. var contexts = context.shared.contexts;
  772. interactives = new Map();
  773. var all = contexts.keys();
  774. for(elt in all) {
  775. makeInteractive(elt);
  776. }
  777. }
  778. function setupGizmo() {
  779. if(curEdit == null) return;
  780. var posQuant = view.config.get("sceneeditor.xyzPrecision");
  781. var scaleQuant = view.config.get("sceneeditor.scalePrecision");
  782. var rotQuant = view.config.get("sceneeditor.rotatePrecision");
  783. inline function quantize(x: Float, step: Float) {
  784. if(step > 0) {
  785. x = Math.round(x / step) * step;
  786. x = untyped parseFloat(x.toFixed(5)); // Snap to closest nicely displayed float :cold_sweat:
  787. }
  788. return x;
  789. }
  790. gizmo.onStartMove = function(mode) {
  791. var objects3d = [for(o in curEdit.rootElements) {
  792. var obj3d = o.to(hrt.prefab.Object3D);
  793. if(obj3d != null)
  794. obj3d;
  795. }];
  796. var sceneObjs = [for(o in objects3d) getContext(o).local3d];
  797. var pivotPt = getPivot(sceneObjs);
  798. var pivot = new h3d.Matrix();
  799. pivot.initTranslation(pivotPt.x, pivotPt.y, pivotPt.z);
  800. var invPivot = pivot.clone();
  801. invPivot.invert();
  802. var localMats = [for(o in sceneObjs) {
  803. var m = worldMat(o);
  804. m.multiply(m, invPivot);
  805. m;
  806. }];
  807. var prevState = [for(o in objects3d) o.saveTransform()];
  808. gizmo.onMove = function(translate: h3d.Vector, rot: h3d.Quat, scale: h3d.Vector) {
  809. var transf = new h3d.Matrix();
  810. transf.identity();
  811. if(rot != null)
  812. rot.toMatrix(transf);
  813. if(translate != null)
  814. transf.translate(translate.x, translate.y, translate.z);
  815. for(i in 0...sceneObjs.length) {
  816. var newMat = localMats[i].clone();
  817. newMat.multiply(newMat, transf);
  818. newMat.multiply(newMat, pivot);
  819. if(snapToGround && mode == MoveXY) {
  820. newMat.tz = getZ(newMat.tx, newMat.ty);
  821. }
  822. var invParent = sceneObjs[i].parent.getAbsPos().clone();
  823. invParent.invert();
  824. newMat.multiply(newMat, invParent);
  825. if(scale != null) {
  826. newMat.prependScale(scale.x, scale.y, scale.z);
  827. }
  828. var obj3d = objects3d[i];
  829. var rot = newMat.getEulerAngles();
  830. obj3d.x = quantize(newMat.tx, posQuant);
  831. obj3d.y = quantize(newMat.ty, posQuant);
  832. obj3d.z = quantize(newMat.tz, posQuant);
  833. obj3d.rotationX = quantize(M.radToDeg(rot.x), rotQuant);
  834. obj3d.rotationY = quantize(M.radToDeg(rot.y), rotQuant);
  835. obj3d.rotationZ = quantize(M.radToDeg(rot.z), rotQuant);
  836. if(scale != null) {
  837. inline function scaleSnap(x: Float) {
  838. if(K.isDown(K.CTRL)) {
  839. var step = K.isDown(K.SHIFT) ? 0.5 : 1.0;
  840. x = Math.round(x / step) * step;
  841. }
  842. return x;
  843. }
  844. var s = newMat.getScale();
  845. obj3d.scaleX = quantize(scaleSnap(s.x), scaleQuant);
  846. obj3d.scaleY = quantize(scaleSnap(s.y), scaleQuant);
  847. obj3d.scaleZ = quantize(scaleSnap(s.z), scaleQuant);
  848. }
  849. obj3d.applyTransform(sceneObjs[i]);
  850. }
  851. }
  852. gizmo.onFinishMove = function() {
  853. var newState = [for(o in objects3d) o.saveTransform()];
  854. refreshProps();
  855. undo.change(Custom(function(undo) {
  856. if( undo ) {
  857. for(i in 0...objects3d.length) {
  858. objects3d[i].loadTransform(prevState[i]);
  859. objects3d[i].applyTransform(sceneObjs[i]);
  860. }
  861. refreshProps();
  862. }
  863. else {
  864. for(i in 0...objects3d.length) {
  865. objects3d[i].loadTransform(newState[i]);
  866. objects3d[i].applyTransform(sceneObjs[i]);
  867. }
  868. refreshProps();
  869. }
  870. for(o in objects3d)
  871. o.updateInstance(getContext(o));
  872. }));
  873. for(o in objects3d)
  874. o.updateInstance(getContext(o));
  875. }
  876. }
  877. gizmo2d.onStartMove = function(mode) {
  878. var objects2d = [for(o in curEdit.rootElements) {
  879. var obj = o.to(hrt.prefab.Object2D);
  880. if(obj != null) obj;
  881. }];
  882. var sceneObjs = [for(o in objects2d) getContext(o).local2d];
  883. var pivot = getPivot2D(sceneObjs);
  884. var center = pivot.getCenter();
  885. var prevState = [for(o in objects2d) o.saveTransform()];
  886. var startPos = [for(o in sceneObjs) o.getAbsPos()];
  887. gizmo2d.onMove = function(t) {
  888. t.x = Math.round(t.x);
  889. t.y = Math.round(t.y);
  890. for(i in 0...sceneObjs.length) {
  891. var pos = startPos[i].clone();
  892. var obj = objects2d[i];
  893. switch( mode ) {
  894. case Pan:
  895. pos.x += t.x;
  896. pos.y += t.y;
  897. case ScaleX, ScaleY, Scale:
  898. // no inherited rotation
  899. if( pos.b == 0 && pos.c == 0 ) {
  900. pos.x -= center.x;
  901. pos.y -= center.y;
  902. pos.x *= t.scaleX;
  903. pos.y *= t.scaleY;
  904. pos.x += center.x;
  905. pos.y += center.y;
  906. obj.scaleX = quantize(t.scaleX * prevState[i].scaleX, scaleQuant);
  907. obj.scaleY = quantize(t.scaleY * prevState[i].scaleY, scaleQuant);
  908. } else {
  909. var m2 = new h2d.col.Matrix();
  910. m2.initScale(t.scaleX, t.scaleY);
  911. pos.x -= center.x;
  912. pos.y -= center.y;
  913. pos.multiply(pos,m2);
  914. pos.x += center.x;
  915. pos.y += center.y;
  916. var s = pos.getScale();
  917. obj.scaleX = quantize(s.x, scaleQuant);
  918. obj.scaleY = quantize(s.y, scaleQuant);
  919. }
  920. case Rotation:
  921. pos.x -= center.x;
  922. pos.y -= center.y;
  923. var ca = Math.cos(t.rotation);
  924. var sa = Math.sin(t.rotation);
  925. var px = pos.x * ca - pos.y * sa;
  926. var py = pos.x * sa + pos.y * ca;
  927. pos.x = px + center.x;
  928. pos.y = py + center.y;
  929. var r = M.degToRad(prevState[i].rotation) + t.rotation;
  930. r = quantize(M.radToDeg(r), rotQuant);
  931. obj.rotation = r;
  932. }
  933. var pt = pos.getPosition();
  934. sceneObjs[i].parent.globalToLocal(pt);
  935. obj.x = quantize(pt.x, posQuant);
  936. obj.y = quantize(pt.y, posQuant);
  937. obj.applyTransform(sceneObjs[i]);
  938. }
  939. };
  940. gizmo2d.onFinishMove = function() {
  941. var newState = [for(o in objects2d) o.saveTransform()];
  942. refreshProps();
  943. undo.change(Custom(function(undo) {
  944. if( undo ) {
  945. for(i in 0...objects2d.length) {
  946. objects2d[i].loadTransform(prevState[i]);
  947. objects2d[i].applyTransform(sceneObjs[i]);
  948. }
  949. refreshProps();
  950. }
  951. else {
  952. for(i in 0...objects2d.length) {
  953. objects2d[i].loadTransform(newState[i]);
  954. objects2d[i].applyTransform(sceneObjs[i]);
  955. }
  956. refreshProps();
  957. }
  958. for(o in objects2d)
  959. o.updateInstance(getContext(o));
  960. }));
  961. for(o in objects2d)
  962. o.updateInstance(getContext(o));
  963. };
  964. };
  965. }
  966. function moveGizmoToSelection() {
  967. // Snap Gizmo at center of objects
  968. gizmo.getRotationQuat().identity();
  969. if(curEdit != null && curEdit.rootObjects.length > 0) {
  970. var pos = getPivot(curEdit.rootObjects);
  971. gizmo.visible = showGizmo;
  972. gizmo.setPosition(pos.x, pos.y, pos.z);
  973. if(curEdit.rootObjects.length == 1 && (localTransform || K.isDown(K.ALT))) {
  974. var obj = curEdit.rootObjects[0];
  975. var mat = worldMat(obj);
  976. var s = mat.getScale();
  977. if(s.x != 0 && s.y != 0 && s.z != 0) {
  978. mat.prependScale(1.0 / s.x, 1.0 / s.y, 1.0 / s.z);
  979. gizmo.getRotationQuat().initRotateMatrix(mat);
  980. }
  981. }
  982. }
  983. else {
  984. gizmo.visible = false;
  985. }
  986. if( curEdit != null && curEdit.rootObjects2D.length > 0 && !gizmo.visible ) {
  987. var pos = getPivot2D(curEdit.rootObjects2D);
  988. gizmo2d.visible = showGizmo;
  989. gizmo2d.setPosition(pos.getCenter().x, pos.getCenter().y);
  990. gizmo2d.setSize(pos.width, pos.height);
  991. } else {
  992. gizmo2d.visible = false;
  993. }
  994. }
  995. var inLassoMode = false;
  996. function startLassoSelect() {
  997. if(inLassoMode) {
  998. inLassoMode = false;
  999. return;
  1000. }
  1001. scene.setCurrent();
  1002. inLassoMode = true;
  1003. var g = new h2d.Object(scene.s2d);
  1004. var overlay = new h2d.Bitmap(h2d.Tile.fromColor(0xffffff, 10000, 10000, 0.1), g);
  1005. var intOverlay = new h2d.Interactive(10000, 10000, scene.s2d);
  1006. var lastPt = new h2d.col.Point(scene.s2d.mouseX, scene.s2d.mouseY);
  1007. var points : h2d.col.Polygon = [lastPt];
  1008. var polyG = new h2d.Graphics(g);
  1009. event.waitUntil(function(dt) {
  1010. var curPt = new h2d.col.Point(scene.s2d.mouseX, scene.s2d.mouseY);
  1011. if(curPt.distance(lastPt) > 3.0) {
  1012. points.push(curPt);
  1013. polyG.clear();
  1014. polyG.beginFill(0xff0000, 0.5);
  1015. polyG.lineStyle(1.0, 0, 1.0);
  1016. polyG.moveTo(points[0].x, points[0].y);
  1017. for(i in 1...points.length) {
  1018. polyG.lineTo(points[i].x, points[i].y);
  1019. }
  1020. polyG.endFill();
  1021. lastPt = curPt;
  1022. }
  1023. var finish = false;
  1024. if(!inLassoMode || K.isDown(K.ESCAPE) || K.isDown(K.MOUSE_RIGHT)) {
  1025. finish = true;
  1026. }
  1027. if(K.isDown(K.MOUSE_LEFT)) {
  1028. var contexts = context.shared.contexts;
  1029. var all = getAllSelectable3D();
  1030. var inside = [];
  1031. for(elt in all) {
  1032. if(elt.to(Object3D) == null)
  1033. continue;
  1034. var ctx = contexts[elt];
  1035. var o = ctx.local3d;
  1036. if(o == null || !o.visible)
  1037. continue;
  1038. var absPos = o.getAbsPos();
  1039. var screenPos = worldToScreen(absPos.tx, absPos.ty, absPos.tz);
  1040. if(points.contains(screenPos, false)) {
  1041. inside.push(elt);
  1042. }
  1043. }
  1044. selectElements(inside);
  1045. finish = true;
  1046. }
  1047. if(finish) {
  1048. intOverlay.remove();
  1049. g.remove();
  1050. inLassoMode = false;
  1051. return true;
  1052. }
  1053. return false;
  1054. });
  1055. }
  1056. public function onPrefabChange(p: PrefabElement, ?pname: String) {
  1057. var model = p.to(hrt.prefab.Model);
  1058. if(model != null && pname == "source") {
  1059. refreshScene();
  1060. return;
  1061. }
  1062. if(p != sceneData) {
  1063. var el = tree.getElement(p);
  1064. if( el != null && el.toggleClass != null ) applyTreeStyle(p, el, pname);
  1065. }
  1066. applySceneStyle(p);
  1067. }
  1068. public function applyTreeStyle(p: PrefabElement, el: Element, ?pname: String) {
  1069. var obj3d = p.to(Object3D);
  1070. el.toggleClass("disabled", !p.enabled);
  1071. var aEl = el.find("a").first();
  1072. var tag = getTag(p);
  1073. if(tag != null) {
  1074. aEl.css("background", tag.color);
  1075. el.find("ul").first().css("background", tag.color + "80");
  1076. }
  1077. else if(pname == "tag") {
  1078. aEl.css("background", "");
  1079. el.find("ul").first().css("background", "");
  1080. }
  1081. if(obj3d != null) {
  1082. el.toggleClass("disabled", !p.enabled || !obj3d.visible);
  1083. el.toggleClass("hidden", isHidden(obj3d));
  1084. el.toggleClass("locked", p.locked);
  1085. el.toggleClass("editorOnly", p.editorOnly);
  1086. var visTog = el.find(".visibility-toggle").first();
  1087. if(visTog.length == 0) {
  1088. visTog = new Element('<i class="ico ico-eye visibility-toggle" title = "Hide (${view.config.get("key.sceneeditor.hide")})"/>').insertAfter(el.find("a.jstree-anchor").first());
  1089. visTog.click(function(e) {
  1090. if(curEdit.elements.indexOf(obj3d) >= 0)
  1091. setVisible(curEdit.elements, isHidden(obj3d));
  1092. else
  1093. setVisible([obj3d], isHidden(obj3d));
  1094. e.preventDefault();
  1095. e.stopPropagation();
  1096. });
  1097. visTog.dblclick(function(e) {
  1098. e.preventDefault();
  1099. e.stopPropagation();
  1100. });
  1101. }
  1102. var lockTog = el.find(".lock-toggle").first();
  1103. if(lockTog.length == 0) {
  1104. lockTog = new Element('<i class="ico ico-lock lock-toggle"/>').insertAfter(el.find("a.jstree-anchor").first());
  1105. lockTog.click(function(e) {
  1106. if(curEdit.elements.indexOf(obj3d) >= 0)
  1107. setLock(curEdit.elements, !obj3d.locked);
  1108. else
  1109. setLock([obj3d], !obj3d.locked);
  1110. e.preventDefault();
  1111. e.stopPropagation();
  1112. });
  1113. lockTog.dblclick(function(e) {
  1114. e.preventDefault();
  1115. e.stopPropagation();
  1116. });
  1117. }
  1118. lockTog.css({visibility: p.locked ? "visible" : "hidden"});
  1119. }
  1120. }
  1121. public function applySceneStyle(p: PrefabElement) {
  1122. var obj3d = p.to(Object3D);
  1123. if(obj3d != null) {
  1124. var visible = obj3d.visible && !isHidden(obj3d);
  1125. for(ctx in getContexts(obj3d)) {
  1126. ctx.local3d.visible = visible;
  1127. }
  1128. }
  1129. }
  1130. public function getInteractives(elt : PrefabElement) {
  1131. var r = [getInteractive(elt)];
  1132. for(c in elt.children) {
  1133. r = r.concat(getInteractives(c));
  1134. }
  1135. return r;
  1136. }
  1137. public function getInteractive(elt: PrefabElement) {
  1138. return interactives.get(elt);
  1139. }
  1140. public function getContext(elt : PrefabElement, ?shared : hrt.prefab.ContextShared) {
  1141. if(elt == null) return null;
  1142. if( shared == null ) shared = context.shared;
  1143. var ctx = shared.contexts.get(elt);
  1144. if( ctx == null ) {
  1145. for( r in @:privateAccess shared.refsContexts ) {
  1146. ctx = getContext(elt, r);
  1147. if( ctx != null ) break;
  1148. }
  1149. }
  1150. if( ctx == null && elt == sceneData )
  1151. ctx = context;
  1152. return ctx;
  1153. }
  1154. public function getContexts(elt: PrefabElement) {
  1155. if(elt == null)
  1156. return null;
  1157. return context.shared.getContexts(elt);
  1158. }
  1159. public function getObject(elt: PrefabElement) {
  1160. var ctx = getContext(elt);
  1161. if(ctx != null)
  1162. return ctx.local3d;
  1163. return context.shared.root3d;
  1164. }
  1165. public function getSelfObject(elt: PrefabElement) {
  1166. var ctx = getContext(elt);
  1167. var parentCtx = getContext(elt.parent);
  1168. if(ctx == null || parentCtx == null) return null;
  1169. if(ctx.local3d != parentCtx.local3d)
  1170. return ctx.local3d;
  1171. return null;
  1172. }
  1173. function removeInstance(elt : PrefabElement) {
  1174. var result = true;
  1175. var contexts = context.shared.contexts;
  1176. function recRemove(e: PrefabElement) {
  1177. for(c in e.children)
  1178. recRemove(c);
  1179. var int = interactives.get(e);
  1180. if(int != null) {
  1181. var i3d = Std.downcast(int, h3d.scene.Interactive);
  1182. if( i3d != null ) i3d.remove() else cast(int,h2d.Interactive).remove();
  1183. interactives.remove(e);
  1184. }
  1185. for(ctx in getContexts(e)) {
  1186. if(!e.removeInstance(ctx))
  1187. result = false;
  1188. contexts.remove(e);
  1189. }
  1190. }
  1191. recRemove(elt);
  1192. return result;
  1193. }
  1194. function makeInstance(elt: PrefabElement) {
  1195. scene.setCurrent();
  1196. var p = elt.parent;
  1197. var parentCtx = null;
  1198. while( p != null ) {
  1199. parentCtx = getContext(p);
  1200. if( parentCtx != null ) break;
  1201. p = p.parent;
  1202. }
  1203. var ctx = elt.make(parentCtx);
  1204. for( p in elt.flatten() )
  1205. makeInteractive(p);
  1206. scene.init(ctx.local3d);
  1207. }
  1208. function refreshParents( elts : Array<PrefabElement> ) {
  1209. var parents = new Map();
  1210. for( e in elts ) {
  1211. if( e.parent == null ) throw e+" is missing parent";
  1212. parents.set(e.parent, true);
  1213. }
  1214. for( p in parents.keys() ) {
  1215. var h = p.getHideProps();
  1216. if( h.onChildListChanged != null ) h.onChildListChanged();
  1217. }
  1218. if( lastRenderProps != null && parents.exists(lastRenderProps) )
  1219. lastRenderProps.applyProps(scene.s3d.renderer);
  1220. }
  1221. public function addElements(elts : Array<PrefabElement>, selectObj : Bool = true, doRefresh : Bool = true, enableUndo = true) {
  1222. for (e in elts) {
  1223. makeInstance(e);
  1224. }
  1225. if (doRefresh) {
  1226. refresh(Partial, if (selectObj) () -> selectElements(elts, NoHistory) else null);
  1227. refreshParents(elts);
  1228. }
  1229. if( !enableUndo )
  1230. return;
  1231. undo.change(Custom(function(undo) {
  1232. var fullRefresh = false;
  1233. if(undo) {
  1234. selectElements([], NoHistory);
  1235. for (e in elts) {
  1236. if(!removeInstance(e))
  1237. fullRefresh = true;
  1238. e.parent.children.remove(e);
  1239. }
  1240. refresh(fullRefresh ? Full : Partial);
  1241. }
  1242. else {
  1243. for (e in elts) {
  1244. e.parent.children.push(e);
  1245. makeInstance(e);
  1246. }
  1247. refresh(Partial, () -> selectElements(elts,NoHistory));
  1248. refreshParents(elts);
  1249. }
  1250. }));
  1251. }
  1252. function makeCdbProps( e : PrefabElement, type : cdb.Sheet ) {
  1253. var props = type.getDefaults();
  1254. Reflect.setField(props, "$cdbtype", DataFiles.getTypeName(type));
  1255. if( type.idCol != null && !type.idCol.opt ) {
  1256. var id = new haxe.io.Path(view.state.path).file;
  1257. id = id.charAt(0).toUpperCase() + id.substr(1);
  1258. id += "_"+e.name;
  1259. Reflect.setField(props, type.idCol.name, id);
  1260. }
  1261. return props;
  1262. }
  1263. function fillProps( edit, e : PrefabElement ) {
  1264. e.edit(edit);
  1265. var typeName = e.getCdbType();
  1266. if( typeName == null && e.props != null )
  1267. return; // don't allow CDB data with props already used !
  1268. var types = DataFiles.getAvailableTypes();
  1269. if( types.length == 0 )
  1270. return;
  1271. var group = new hide.Element('
  1272. <div class="group" name="CDB">
  1273. <dl>
  1274. <dt>
  1275. <div class="btn-cdb-large ico ico-file-text"></div>
  1276. Type
  1277. </dt>
  1278. <dd><select><option value="">- No props -</option></select></dd>
  1279. </div>
  1280. ');
  1281. var cdbLarge = @:privateAccess view.getDisplayState("cdbLarge");
  1282. var detachable = new DetachablePanel();
  1283. detachable.saveDisplayKey = "detachedCdb";
  1284. group.find(".btn-cdb-large").click((_) -> {
  1285. cdbLarge = !cdbLarge;
  1286. @:privateAccess view.saveDisplayState("cdbLarge", cdbLarge);
  1287. group.toggleClass("cdb-large", cdbLarge);
  1288. detachable.setDetached(cdbLarge);
  1289. });
  1290. group.toggleClass("cdb-large", cdbLarge == true);
  1291. detachable.setDetached(cdbLarge == true);
  1292. var select = group.find("select");
  1293. for(t in types) {
  1294. var id = DataFiles.getTypeName(t);
  1295. new hide.Element("<option>").attr("value", id).text(id).appendTo(select);
  1296. }
  1297. var curType = DataFiles.resolveType(typeName);
  1298. if(curType != null) select.val(DataFiles.getTypeName(curType));
  1299. function changeProps(props: Dynamic) {
  1300. properties.undo.change(Field(e, "props", e.props), ()->edit.rebuildProperties());
  1301. e.props = props;
  1302. edit.onChange(e, "props");
  1303. edit.rebuildProperties();
  1304. }
  1305. select.change(function(v) {
  1306. var typeId = select.val();
  1307. if(typeId == null || typeId == "") {
  1308. changeProps(null);
  1309. return;
  1310. }
  1311. var props = makeCdbProps(e, DataFiles.resolveType(typeId));
  1312. changeProps(props);
  1313. });
  1314. edit.properties.add(group);
  1315. if(curType != null) {
  1316. var props = new hide.Element('<div></div>').appendTo(group.find(".content"));
  1317. var fileRef = view.state.path;
  1318. var ctx = context.shared.getContexts(e)[0];
  1319. if( ctx != null )
  1320. fileRef = ctx.shared.currentPath;
  1321. detachable.element.appendTo(props);
  1322. var editor = new hide.comp.cdb.ObjEditor(curType, view.config, e.props, fileRef, detachable.element);
  1323. editor.undo = properties.undo;
  1324. editor.fileView = view;
  1325. editor.onChange = function(pname) {
  1326. edit.onChange(e, 'props.$pname');
  1327. var e = Std.downcast(e, Object3D);
  1328. if( e != null ) {
  1329. for( ctx in context.shared.getContexts(e) )
  1330. e.addEditorUI(ctx);
  1331. }
  1332. }
  1333. }
  1334. }
  1335. public function showProps(e: PrefabElement) {
  1336. scene.setCurrent();
  1337. var edit = makeEditContext([e]);
  1338. properties.clear();
  1339. fillProps(edit, e);
  1340. }
  1341. function setElementSelected( p : PrefabElement, ctx : hrt.prefab.Context, b : Bool ) {
  1342. return p.setSelected(ctx, b);
  1343. }
  1344. public function selectElements( elts : Array<PrefabElement>, ?mode : SelectMode = Default ) {
  1345. function impl(elts,mode:SelectMode) {
  1346. scene.setCurrent();
  1347. if( curEdit != null )
  1348. curEdit.cleanup();
  1349. var edit = makeEditContext(elts);
  1350. if (elts.length == 0 || (customPivot != null && customPivot.elt != edit.rootElements[0])) {
  1351. customPivot = null;
  1352. }
  1353. properties.clear();
  1354. if( elts.length > 0 ) fillProps(edit, elts[0]);
  1355. switch( mode ) {
  1356. case Default, NoHistory:
  1357. tree.setSelection(elts);
  1358. case Nothing, NoTree:
  1359. }
  1360. function getSelContext( e : PrefabElement ) {
  1361. var ectx = context.shared.contexts.get(e);
  1362. if( ectx == null ) ectx = context.shared.getContexts(e)[0];
  1363. if( ectx == null ) ectx = context;
  1364. return ectx;
  1365. }
  1366. var map = new Map<PrefabElement,Bool>();
  1367. function selectRec(e : PrefabElement, b:Bool) {
  1368. if( map.exists(e) )
  1369. return;
  1370. map.set(e, true);
  1371. if(setElementSelected(e, getSelContext(e), b))
  1372. for( e in e.children )
  1373. selectRec(e,b);
  1374. }
  1375. for( e in elts )
  1376. selectRec(e, true);
  1377. edit.cleanups.push(function() {
  1378. for( e in map.keys() ) {
  1379. if( hasBeenRemoved(e) ) continue;
  1380. setElementSelected(e, getSelContext(e), false);
  1381. }
  1382. });
  1383. curEdit = edit;
  1384. showGizmo = false;
  1385. for( e in elts )
  1386. if( !isLocked(e) ) {
  1387. showGizmo = true;
  1388. break;
  1389. }
  1390. setupGizmo();
  1391. }
  1392. var prev : Array<PrefabElement> = null;
  1393. if( curEdit != null && mode.match(Default|NoTree) ) {
  1394. prev = curEdit.rootElements.copy();
  1395. undo.change(Custom(function(u) {
  1396. if(u) impl(prev,NoHistory);
  1397. else impl(elts,NoHistory);
  1398. }),true);
  1399. }
  1400. impl(elts,mode);
  1401. if( prev == null || curEdit.rootElements.length != prev.length ) {
  1402. focusedSinceSelect = false;
  1403. return;
  1404. }
  1405. for( i in 0...curEdit.rootElements.length ) {
  1406. if( curEdit.rootElements[i] != prev[i] ) {
  1407. focusedSinceSelect = false;
  1408. return;
  1409. }
  1410. }
  1411. }
  1412. function hasBeenRemoved( e : hrt.prefab.Prefab ) {
  1413. var root = sceneData;
  1414. var eltCtx = context.shared.getContexts(e)[0];
  1415. if( eltCtx != null && eltCtx.shared.parent != null ) {
  1416. if( hasBeenRemoved(eltCtx.shared.parent.prefab) )
  1417. return true;
  1418. root = null;
  1419. }
  1420. while( e != null && e != root ) {
  1421. if( e.parent != null && e.parent.children.indexOf(e) < 0 )
  1422. return true;
  1423. e = e.parent;
  1424. }
  1425. return e != root;
  1426. }
  1427. public function resetCamera(distanceFactor = 1.5) {
  1428. if( camera2D ) {
  1429. cameraController2D.initFromScene();
  1430. } else {
  1431. scene.s3d.camera.zNear = scene.s3d.camera.zFar = 0;
  1432. scene.s3d.camera.fovY = 25; // reset to default fov
  1433. scene.resetCamera(distanceFactor);
  1434. cameraController.lockZPlanes = scene.s3d.camera.zNear != 0;
  1435. cameraController.loadFromCamera();
  1436. }
  1437. }
  1438. public function getPickTransform(parent: PrefabElement) {
  1439. var proj = screenToGround(scene.s2d.mouseX, scene.s2d.mouseY);
  1440. if(proj == null) return null;
  1441. var localMat = new h3d.Matrix();
  1442. localMat.initTranslation(proj.x, proj.y, proj.z);
  1443. if(parent == null)
  1444. return localMat;
  1445. var parentMat = worldMat(getObject(parent));
  1446. parentMat.invert();
  1447. localMat.multiply(localMat, parentMat);
  1448. return localMat;
  1449. }
  1450. public function onDragDrop( items : Array<String>, isDrop : Bool ) {
  1451. var pickedEl = js.Browser.document.elementFromPoint(ide.mouseX, ide.mouseY);
  1452. var propEl = properties.element[0];
  1453. while( pickedEl != null ) {
  1454. if( pickedEl == propEl )
  1455. return properties.onDragDrop(items, isDrop);
  1456. pickedEl = pickedEl.parentElement;
  1457. }
  1458. var supported = @:privateAccess hrt.prefab.Library.registeredExtensions;
  1459. var paths = [];
  1460. for(path in items) {
  1461. var ext = haxe.io.Path.extension(path).toLowerCase();
  1462. if( supported.exists(ext) || ext == "fbx" || ext == "hmd" )
  1463. paths.push(path);
  1464. }
  1465. if( paths.length == 0 )
  1466. return false;
  1467. if(isDrop)
  1468. dropElements(paths, sceneData);
  1469. return true;
  1470. }
  1471. function dropElements(paths: Array<String>, parent: PrefabElement) {
  1472. scene.setCurrent();
  1473. var localMat = getPickTransform(parent);
  1474. if(localMat == null) return;
  1475. localMat.tx = hxd.Math.round(localMat.tx * 10) / 10;
  1476. localMat.ty = hxd.Math.round(localMat.ty * 10) / 10;
  1477. localMat.tz = hxd.Math.floor(localMat.tz * 10) / 10;
  1478. var elts: Array<PrefabElement> = [];
  1479. for(path in paths) {
  1480. var obj3d : Object3D;
  1481. var relative = ide.makeRelative(path);
  1482. if(hrt.prefab.Library.getPrefabType(path) != null) {
  1483. var ref = new hrt.prefab.Reference(parent);
  1484. ref.source = relative;
  1485. obj3d = ref;
  1486. obj3d.name = new haxe.io.Path(relative).file;
  1487. }
  1488. else {
  1489. obj3d = new hrt.prefab.Model(parent);
  1490. obj3d.source = relative;
  1491. }
  1492. obj3d.setTransform(localMat);
  1493. autoName(obj3d);
  1494. elts.push(obj3d);
  1495. }
  1496. for(e in elts)
  1497. makeInstance(e);
  1498. refresh(Partial, () -> selectElements(elts));
  1499. undo.change(Custom(function(undo) {
  1500. if( undo ) {
  1501. var fullRefresh = false;
  1502. for(e in elts) {
  1503. if(!removeInstance(e))
  1504. fullRefresh = true;
  1505. parent.children.remove(e);
  1506. }
  1507. refresh(fullRefresh ? Full : Partial);
  1508. }
  1509. else {
  1510. for(e in elts) {
  1511. parent.children.push(e);
  1512. makeInstance(e);
  1513. }
  1514. refresh(Partial);
  1515. }
  1516. }));
  1517. }
  1518. function gatherToMouse() {
  1519. var prevParent = sceneData;
  1520. var localMat = getPickTransform(prevParent);
  1521. if( localMat == null ) return;
  1522. var objects3d = [for(o in curEdit.rootElements) {
  1523. var obj3d = o.to(hrt.prefab.Object3D);
  1524. if( obj3d != null && !obj3d.locked )
  1525. obj3d;
  1526. }];
  1527. if( objects3d.length == 0 ) return;
  1528. var sceneObjs = [for(o in objects3d) getContext(o).local3d];
  1529. var prevState = [for(o in objects3d) o.saveTransform()];
  1530. for( obj3d in objects3d ) {
  1531. if( obj3d.parent != prevParent ) {
  1532. prevParent = obj3d.parent;
  1533. localMat = getPickTransform(prevParent);
  1534. }
  1535. if( localMat == null ) continue;
  1536. obj3d.x = hxd.Math.round(localMat.tx * 10) / 10;
  1537. obj3d.y = hxd.Math.round(localMat.ty * 10) / 10;
  1538. obj3d.z = hxd.Math.floor(localMat.tz * 10) / 10;
  1539. obj3d.updateInstance(getContext(obj3d));
  1540. }
  1541. var newState = [for(o in objects3d) o.saveTransform()];
  1542. refreshProps();
  1543. undo.change(Custom(function(undo) {
  1544. if( undo ) {
  1545. for(i in 0...objects3d.length) {
  1546. objects3d[i].loadTransform(prevState[i]);
  1547. objects3d[i].applyTransform(sceneObjs[i]);
  1548. }
  1549. refreshProps();
  1550. }
  1551. else {
  1552. for(i in 0...objects3d.length) {
  1553. objects3d[i].loadTransform(newState[i]);
  1554. objects3d[i].applyTransform(sceneObjs[i]);
  1555. }
  1556. refreshProps();
  1557. }
  1558. for(o in objects3d)
  1559. o.updateInstance(getContext(o));
  1560. }));
  1561. }
  1562. function canGroupSelection() {
  1563. var elts = curEdit.rootElements;
  1564. if(elts.length == 0)
  1565. return false;
  1566. if(elts.length == 1)
  1567. return true;
  1568. // Only allow grouping of sibling elements
  1569. var parent = elts[0].parent;
  1570. for(e in elts)
  1571. if(e.parent != parent)
  1572. return false;
  1573. return true;
  1574. }
  1575. function groupSelection() {
  1576. if(!canGroupSelection())
  1577. return;
  1578. var elts = curEdit.rootElements;
  1579. var parent = elts[0].parent;
  1580. var parentMat = worldMat(parent);
  1581. var invParentMat = parentMat.clone();
  1582. invParentMat.invert();
  1583. var pivot = new h3d.Vector();
  1584. {
  1585. var count = 0;
  1586. for(elt in curEdit.rootElements) {
  1587. var m = worldMat(elt);
  1588. if(m != null) {
  1589. pivot = pivot.add(m.getPosition());
  1590. ++count;
  1591. }
  1592. }
  1593. pivot.scale3(1.0 / count);
  1594. }
  1595. var local = new h3d.Matrix();
  1596. local.initTranslation(pivot.x, pivot.y, pivot.z);
  1597. local.multiply(local, invParentMat);
  1598. var group = new hrt.prefab.Object3D(parent);
  1599. @:privateAccess group.type = "object";
  1600. autoName(group);
  1601. group.x = local.tx;
  1602. group.y = local.ty;
  1603. group.z = local.tz;
  1604. var parentCtx = getContext(parent);
  1605. if(parentCtx == null)
  1606. parentCtx = context;
  1607. group.make(parentCtx);
  1608. var groupCtx = getContext(group);
  1609. var effectFunc = reparentImpl(elts, group, 0);
  1610. undo.change(Custom(function(undo) {
  1611. if(undo) {
  1612. group.parent = null;
  1613. context.shared.contexts.remove(group);
  1614. effectFunc(true);
  1615. }
  1616. else {
  1617. group.parent = parent;
  1618. context.shared.contexts.set(group, groupCtx);
  1619. effectFunc(false);
  1620. }
  1621. if(undo)
  1622. refresh(()->selectElements([],NoHistory));
  1623. else
  1624. refresh(()->selectElements([group],NoHistory));
  1625. }));
  1626. refresh(effectFunc(false) ? Full : Partial, () -> selectElements([group],NoHistory));
  1627. }
  1628. function onCopy() {
  1629. if(curEdit == null) return;
  1630. if(curEdit.rootElements.length == 1) {
  1631. view.setClipboard(curEdit.rootElements[0].saveData(), "prefab");
  1632. }
  1633. else {
  1634. var lib = new hrt.prefab.Library();
  1635. for(e in curEdit.rootElements) {
  1636. lib.children.push(e);
  1637. }
  1638. view.setClipboard(lib.saveData(), "library");
  1639. }
  1640. }
  1641. function onPaste() {
  1642. var parent : PrefabElement = sceneData;
  1643. if(curEdit != null && curEdit.elements.length > 0) {
  1644. parent = curEdit.elements[0];
  1645. }
  1646. var obj = haxe.Json.parse(haxe.Json.stringify(view.getClipboard("prefab")));
  1647. if(obj != null) {
  1648. var p = hrt.prefab.Prefab.loadPrefab(obj, parent);
  1649. autoName(p);
  1650. addElements([p]);
  1651. }
  1652. else {
  1653. obj = view.getClipboard("library");
  1654. if(obj != null) {
  1655. var lib = hrt.prefab.Prefab.loadPrefab(obj);
  1656. for(c in lib.children) {
  1657. autoName(c);
  1658. parent.children.push(c);
  1659. }
  1660. addElements(lib.children);
  1661. }
  1662. }
  1663. }
  1664. public function isVisible(elt: PrefabElement) {
  1665. if(elt == sceneData)
  1666. return true;
  1667. var o = elt.to(Object3D);
  1668. if(o == null)
  1669. return true;
  1670. return o.visible && !isHidden(o) && (elt.parent != null ? isVisible(elt.parent) : true);
  1671. }
  1672. public function getAllSelectable3D() : Array<PrefabElement> {
  1673. var ret = [];
  1674. for(elt in interactives.keys()) {
  1675. var i = interactives.get(elt);
  1676. var p : h3d.scene.Object = Std.downcast(i, h3d.scene.Interactive);
  1677. if( p == null )
  1678. continue;
  1679. while( p != null && p.visible )
  1680. p = p.parent;
  1681. if( p != null ) continue;
  1682. ret.push(elt);
  1683. }
  1684. return ret;
  1685. }
  1686. public function selectAll() {
  1687. selectElements(getAllSelectable3D());
  1688. }
  1689. public function deselect() {
  1690. selectElements([]);
  1691. }
  1692. public function isSelected( p : PrefabElement ) {
  1693. return curEdit != null && curEdit.elements.indexOf(p) >= 0;
  1694. }
  1695. public function setEnabled(elements : Array<PrefabElement>, enable: Bool) {
  1696. var old = [for(e in elements) e.enabled];
  1697. function apply(on) {
  1698. for(i in 0...elements.length) {
  1699. elements[i].enabled = on ? enable : old[i];
  1700. onPrefabChange(elements[i]);
  1701. }
  1702. refreshScene();
  1703. }
  1704. apply(true);
  1705. undo.change(Custom(function(undo) {
  1706. if(undo)
  1707. apply(false);
  1708. else
  1709. apply(true);
  1710. }));
  1711. }
  1712. public function setEditorOnly(elements : Array<PrefabElement>, enable: Bool) {
  1713. var old = [for(e in elements) e.editorOnly];
  1714. function apply(on) {
  1715. for(i in 0...elements.length) {
  1716. elements[i].editorOnly = on ? enable : old[i];
  1717. onPrefabChange(elements[i]);
  1718. }
  1719. refreshScene();
  1720. }
  1721. apply(true);
  1722. undo.change(Custom(function(undo) {
  1723. if(undo)
  1724. apply(false);
  1725. else
  1726. apply(true);
  1727. }));
  1728. }
  1729. public function isHidden(e: PrefabElement) {
  1730. if(e == null)
  1731. return false;
  1732. return hideList.exists(e);
  1733. }
  1734. public function isLocked(e: PrefabElement) {
  1735. while( e != null ) {
  1736. if( e.locked ) return true;
  1737. e = e.parent;
  1738. }
  1739. return false;
  1740. }
  1741. function saveDisplayState() {
  1742. var state = [for (h in hideList.keys()) h.getAbsPath(true)];
  1743. @:privateAccess view.saveDisplayState("hideList", state);
  1744. }
  1745. public function setVisible(elements : Array<PrefabElement>, visible: Bool) {
  1746. for(o in elements) {
  1747. for(c in o.flatten(Object3D)) {
  1748. if( visible )
  1749. hideList.remove(c);
  1750. else
  1751. hideList.set(o, true);
  1752. var el = tree.getElement(c);
  1753. if( el != null ) applyTreeStyle(c, el);
  1754. applySceneStyle(c);
  1755. }
  1756. }
  1757. saveDisplayState();
  1758. }
  1759. public function setLock(elements : Array<PrefabElement>, locked: Bool, enableUndo : Bool = true) {
  1760. var prev = [for( o in elements ) o.locked];
  1761. for(o in elements) {
  1762. o.locked = locked;
  1763. for( c in o.flatten(hrt.prefab.Prefab) ) {
  1764. var el = tree.getElement(c);
  1765. applyTreeStyle(c, el);
  1766. applySceneStyle(c);
  1767. toggleInteractive(c,!isLocked(c));
  1768. }
  1769. }
  1770. if (enableUndo) {
  1771. undo.change(Custom(function(redo) {
  1772. for( i in 0...elements.length )
  1773. elements[i].locked = redo ? locked : prev[i];
  1774. }), function() {
  1775. tree.refresh();
  1776. refreshScene();
  1777. });
  1778. }
  1779. saveDisplayState();
  1780. showGizmo = !locked;
  1781. moveGizmoToSelection();
  1782. }
  1783. function isolate(elts : Array<PrefabElement>) {
  1784. var toShow = elts.copy();
  1785. var toHide = [];
  1786. function hideSiblings(elt: PrefabElement) {
  1787. var p = elt.parent;
  1788. for(c in p.children) {
  1789. var needsVisible = c == elt || toShow.indexOf(c) >= 0 || hasChild(c, toShow);
  1790. if(!needsVisible) {
  1791. toHide.push(c);
  1792. }
  1793. }
  1794. if(p != sceneData) {
  1795. hideSiblings(p);
  1796. }
  1797. }
  1798. for(e in toShow) {
  1799. hideSiblings(e);
  1800. }
  1801. setVisible(toHide, false);
  1802. }
  1803. var isDuplicating = false;
  1804. function duplicate(thenMove: Bool) {
  1805. if(curEdit == null) return;
  1806. var elements = curEdit.rootElements;
  1807. if(elements == null || elements.length == 0)
  1808. return;
  1809. if( isDuplicating )
  1810. return;
  1811. isDuplicating = true;
  1812. if( gizmo.moving ) {
  1813. @:privateAccess gizmo.finishMove();
  1814. }
  1815. var undoes = [];
  1816. var newElements = [];
  1817. for(elt in elements) {
  1818. var clone = elt.cloneData();
  1819. var index = elt.parent.children.indexOf(elt) + 1;
  1820. clone.parent = elt.parent;
  1821. elt.parent.children.remove(clone);
  1822. elt.parent.children.insert(index, clone);
  1823. autoName(clone);
  1824. makeInstance(clone);
  1825. newElements.push(clone);
  1826. undoes.push(function(undo) {
  1827. if(undo) elt.parent.children.remove(clone);
  1828. else elt.parent.children.insert(index, clone);
  1829. });
  1830. }
  1831. refreshTree(function() {
  1832. selectElements(newElements);
  1833. tree.setSelection(newElements);
  1834. if(thenMove && curEdit.rootObjects.length > 0) {
  1835. gizmo.startMove(MoveXY, true);
  1836. gizmo.onFinishMove = function() {
  1837. refreshProps();
  1838. }
  1839. }
  1840. isDuplicating = false;
  1841. });
  1842. undo.change(Custom(function(undo) {
  1843. selectElements([], NoHistory);
  1844. var fullRefresh = false;
  1845. if(undo) {
  1846. for(elt in newElements) {
  1847. if(!removeInstance(elt)) {
  1848. fullRefresh = true;
  1849. break;
  1850. }
  1851. }
  1852. }
  1853. for(u in undoes) u(undo);
  1854. if(!undo) {
  1855. for(elt in newElements)
  1856. makeInstance(elt);
  1857. }
  1858. refresh(fullRefresh ? Full : Partial);
  1859. }));
  1860. }
  1861. function setTransform(elt: PrefabElement, ?mat: h3d.Matrix, ?position: h3d.Vector) {
  1862. var obj3d = Std.downcast(elt, hrt.prefab.Object3D);
  1863. if(obj3d == null)
  1864. return;
  1865. if(mat != null)
  1866. obj3d.setTransform(mat);
  1867. else {
  1868. obj3d.x = position.x;
  1869. obj3d.y = position.y;
  1870. obj3d.z = position.z;
  1871. }
  1872. var ctx = getContext(obj3d);
  1873. if(ctx != null)
  1874. obj3d.updateInstance(ctx);
  1875. }
  1876. public function deleteElements(elts : Array<PrefabElement>, ?then: Void->Void, doRefresh : Bool = true, enableUndo : Bool = true) {
  1877. var fullRefresh = false;
  1878. var undoes = [];
  1879. for(elt in elts) {
  1880. if(!removeInstance(elt))
  1881. fullRefresh = true;
  1882. var index = elt.parent.children.indexOf(elt);
  1883. elt.parent.children.remove(elt);
  1884. undoes.unshift(function(undo) {
  1885. if(undo) elt.parent.children.insert(index, elt);
  1886. else elt.parent.children.remove(elt);
  1887. });
  1888. }
  1889. function refreshFunc(then) {
  1890. refresh(fullRefresh ? Full : Partial, then);
  1891. if( !fullRefresh ) refreshParents(elts);
  1892. }
  1893. if (doRefresh)
  1894. refreshFunc(then != null ? then : () -> selectElements([],NoHistory));
  1895. if (enableUndo) {
  1896. undo.change(Custom(function(undo) {
  1897. if(!undo && !fullRefresh)
  1898. for(e in elts) removeInstance(e);
  1899. for(u in undoes) u(undo);
  1900. if(undo)
  1901. for(e in elts) makeInstance(e);
  1902. refreshFunc(then != null ? then : selectElements.bind(undo ? elts : [],NoHistory));
  1903. }));
  1904. }
  1905. }
  1906. function reparentElement(e : Array<PrefabElement>, to : PrefabElement, index : Int) {
  1907. if( to == null )
  1908. to = sceneData;
  1909. var ref = Std.downcast(to, Reference);
  1910. @:privateAccess if( ref != null && ref.editMode ) to = ref.ref;
  1911. var effectFunc = reparentImpl(e, to, index);
  1912. undo.change(Custom(function(undo) {
  1913. refresh(effectFunc(undo) ? Full : Partial);
  1914. }));
  1915. refresh(effectFunc(false) ? Full : Partial);
  1916. }
  1917. function makeTransform(mat: h3d.Matrix) {
  1918. var rot = mat.getEulerAngles();
  1919. var x = mat.tx;
  1920. var y = mat.ty;
  1921. var z = mat.tz;
  1922. var s = mat.getScale();
  1923. var scaleX = s.x;
  1924. var scaleY = s.y;
  1925. var scaleZ = s.z;
  1926. var rotationX = hxd.Math.radToDeg(rot.x);
  1927. var rotationY = hxd.Math.radToDeg(rot.y);
  1928. var rotationZ = hxd.Math.radToDeg(rot.z);
  1929. return { x : x, y : y, z : z, scaleX : scaleX, scaleY : scaleY, scaleZ : scaleZ, rotationX : rotationX, rotationY : rotationY, rotationZ : rotationZ };
  1930. }
  1931. function reparentImpl(elts : Array<PrefabElement>, toElt: PrefabElement, index: Int) : Bool -> Bool {
  1932. var effects = [];
  1933. var fullRefresh = false;
  1934. var toRefresh : Array<PrefabElement> = null;
  1935. for(elt in elts) {
  1936. var prev = elt.parent;
  1937. var prevIndex = prev.children.indexOf(elt);
  1938. var obj3d = elt.to(Object3D);
  1939. var preserveTransform = Std.is(toElt, hrt.prefab.fx.Emitter) || Std.is(prev, hrt.prefab.fx.Emitter);
  1940. var toObj = getObject(toElt);
  1941. var obj = getObject(elt);
  1942. var prevState = null, newState = null;
  1943. if(obj3d != null && toObj != null && obj != null && !preserveTransform) {
  1944. var mat = worldMat(obj);
  1945. var parentMat = worldMat(toObj);
  1946. parentMat.invert();
  1947. mat.multiply(mat, parentMat);
  1948. prevState = obj3d.saveTransform();
  1949. newState = makeTransform(mat);
  1950. }
  1951. effects.push(function(undo) {
  1952. var refresh = false;
  1953. if( undo ) {
  1954. refresh = !removeInstance(elt);
  1955. elt.parent = prev;
  1956. prev.children.remove(elt);
  1957. prev.children.insert(prevIndex, elt);
  1958. if(obj3d != null && prevState != null)
  1959. obj3d.loadTransform(prevState);
  1960. } else {
  1961. var refresh = !removeInstance(elt);
  1962. elt.parent = toElt;
  1963. toElt.children.remove(elt);
  1964. toElt.children.insert(index, elt);
  1965. if(obj3d != null && newState != null)
  1966. obj3d.loadTransform(newState);
  1967. };
  1968. if(toRefresh.indexOf(elt) < 0)
  1969. toRefresh.push(elt);
  1970. return refresh;
  1971. });
  1972. }
  1973. return function(undo) {
  1974. var refresh = false;
  1975. toRefresh = [];
  1976. for(f in effects) {
  1977. if(f(undo))
  1978. refresh = true;
  1979. }
  1980. if(!refresh) {
  1981. for(elt in toRefresh) {
  1982. removeInstance(elt);
  1983. makeInstance(elt);
  1984. }
  1985. }
  1986. return refresh;
  1987. }
  1988. }
  1989. function autoName(p : PrefabElement) {
  1990. var uniqueName = false;
  1991. if( p.type == "volumetricLightmap" || p.type == "light" )
  1992. uniqueName = true;
  1993. var prefix = null;
  1994. if(p.name != null && p.name.length > 0) {
  1995. if(uniqueName)
  1996. prefix = ~/_+[0-9]+$/.replace(p.name, "");
  1997. else
  1998. prefix = p.name;
  1999. }
  2000. else
  2001. prefix = p.getDefaultName();
  2002. if(uniqueName) {
  2003. prefix += "_";
  2004. var id = 0;
  2005. while( sceneData.getPrefabByName(prefix + id) != null )
  2006. id++;
  2007. p.name = prefix + id;
  2008. }
  2009. else
  2010. p.name = prefix;
  2011. for(c in p.children) {
  2012. autoName(c);
  2013. }
  2014. }
  2015. function update(dt:Float) {
  2016. var cam = scene.s3d.camera;
  2017. @: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 });
  2018. @:privateAccess view.saveDisplayState("Camera2D", { x : context.shared.root2d.x - scene.s2d.width*0.5, y : context.shared.root2d.y - scene.s2d.height*0.5, z : context.shared.root2d.scaleX });
  2019. if(gizmo != null) {
  2020. if(!gizmo.moving) {
  2021. moveGizmoToSelection();
  2022. }
  2023. gizmo.update(dt);
  2024. }
  2025. event.update(dt);
  2026. for( f in updates )
  2027. f(dt);
  2028. onUpdate(dt);
  2029. }
  2030. public dynamic function onUpdate(dt:Float) {
  2031. }
  2032. // Override
  2033. function makeEditContext(elts : Array<PrefabElement>) : SceneEditorContext {
  2034. var p = elts[0];
  2035. var rootCtx = context;
  2036. while( p != null ) {
  2037. var ctx = context.shared.getContexts(p)[0];
  2038. if( ctx != null ) rootCtx = ctx;
  2039. p = p.parent;
  2040. }
  2041. // rootCtx might not be == context depending on references
  2042. var edit = new SceneEditorContext(rootCtx, elts, this);
  2043. edit.properties = properties;
  2044. edit.scene = scene;
  2045. return edit;
  2046. }
  2047. function getNewRecentContextMenu(current, ?onMake: PrefabElement->Void=null) : Array<hide.comp.ContextMenu.ContextMenuItem> {
  2048. var parent = current == null ? sceneData : current;
  2049. var grecent = [];
  2050. var recents : Array<String> = ide.currentConfig.get("sceneeditor.newrecents", []);
  2051. for( g in recents) {
  2052. var pmodel = hrt.prefab.Library.getRegistered().get(g);
  2053. if (pmodel != null && checkAllowParent({cl : g, inf : pmodel.inf}, parent))
  2054. grecent.push(getNewTypeMenuItem(g, parent, onMake));
  2055. }
  2056. return grecent;
  2057. }
  2058. // Override
  2059. function getNewContextMenu(current: PrefabElement, ?onMake: PrefabElement->Void=null, ?groupByType=true ) : Array<hide.comp.ContextMenu.ContextMenuItem> {
  2060. var newItems = new Array<hide.comp.ContextMenu.ContextMenuItem>();
  2061. var allRegs = hrt.prefab.Library.getRegistered().copy();
  2062. allRegs.remove("reference");
  2063. allRegs.remove("unknown");
  2064. var parent = current == null ? sceneData : current;
  2065. var groups = [];
  2066. var gother = [];
  2067. for( g in (view.config.get("sceneeditor.newgroups") : Array<String>) ) {
  2068. var parts = g.split("|");
  2069. var cl : Dynamic = Type.resolveClass(parts[1]);
  2070. if( cl == null ) continue;
  2071. groups.push({
  2072. label : parts[0],
  2073. cl : cl,
  2074. group : [],
  2075. });
  2076. }
  2077. for( ptype in allRegs.keys() ) {
  2078. var pinf = allRegs.get(ptype);
  2079. if (!checkAllowParent({cl : ptype, inf : pinf.inf}, parent)) continue;
  2080. if(ptype == "shader") {
  2081. newItems.push(getNewShaderMenu(parent, onMake));
  2082. continue;
  2083. }
  2084. var m = getNewTypeMenuItem(ptype, parent, onMake);
  2085. if( !groupByType )
  2086. newItems.push(m);
  2087. else {
  2088. var found = false;
  2089. for( g in groups )
  2090. if( hrt.prefab.Library.isOfType(ptype,g.cl) ) {
  2091. g.group.push(m);
  2092. found = true;
  2093. break;
  2094. }
  2095. if( !found ) gother.push(m);
  2096. }
  2097. }
  2098. function sortByLabel(arr:Array<hide.comp.ContextMenu.ContextMenuItem>) {
  2099. arr.sort(function(l1,l2) return Reflect.compare(l1.label,l2.label));
  2100. }
  2101. for( g in groups )
  2102. if( g.group.length > 0 ) {
  2103. sortByLabel(g.group);
  2104. newItems.push({ label : g.label, menu : g.group });
  2105. }
  2106. sortByLabel(gother);
  2107. sortByLabel(newItems);
  2108. if( gother.length > 0 ) {
  2109. if( newItems.length == 0 )
  2110. return gother;
  2111. newItems.push({ label : "Other", menu : gother });
  2112. }
  2113. return newItems;
  2114. }
  2115. function getNewTypeMenuItem(
  2116. ptype: String,
  2117. parent: PrefabElement,
  2118. onMake: PrefabElement->Void,
  2119. ?label: String,
  2120. ?objectName: String,
  2121. ?path: String
  2122. ) : hide.comp.ContextMenu.ContextMenuItem {
  2123. var pmodel = hrt.prefab.Library.getRegistered().get(ptype);
  2124. return {
  2125. label : label != null ? label : pmodel.inf.name,
  2126. click : function() {
  2127. function make(?sourcePath) {
  2128. var p = Type.createInstance(pmodel.cl, [parent]);
  2129. @:privateAccess p.type = ptype;
  2130. if(sourcePath != null)
  2131. p.source = sourcePath;
  2132. if( objectName != null)
  2133. p.name = objectName;
  2134. else
  2135. autoName(p);
  2136. if(onMake != null)
  2137. onMake(p);
  2138. var recents : Array<String> = ide.currentConfig.get("sceneeditor.newrecents", []);
  2139. recents.remove(p.type);
  2140. recents.unshift(p.type);
  2141. var recentSize : Int = view.config.get("sceneeditor.recentsize");
  2142. if (recents.length > recentSize) recents.splice(recentSize, recents.length - recentSize);
  2143. ide.currentConfig.set("sceneeditor.newrecents", recents);
  2144. return p;
  2145. }
  2146. if( pmodel.inf.fileSource != null ) {
  2147. if( path != null ) {
  2148. var p = make(path);
  2149. addElements([p]);
  2150. var recents : Array<String> = ide.currentConfig.get("sceneeditor.newrecents", []);
  2151. recents.remove(p.type);
  2152. } else {
  2153. ide.chooseFile(pmodel.inf.fileSource, function(path) {
  2154. addElements([make(path)]);
  2155. });
  2156. }
  2157. }
  2158. else
  2159. addElements([make()]);
  2160. },
  2161. icon : pmodel.inf.icon,
  2162. };
  2163. }
  2164. function getNewShaderMenu(parentElt: PrefabElement, ?onMake: PrefabElement->Void) : hide.comp.ContextMenu.ContextMenuItem {
  2165. function isClassShader(path) {
  2166. if(StringTools.endsWith(path, ".hx")) path = path.substr(0, -3);
  2167. var cpath = path.split("/").join(".");
  2168. var cl = Type.resolveClass(cpath);
  2169. return cl != null;
  2170. }
  2171. var shModel = hrt.prefab.Library.getRegistered().get("shader");
  2172. var graphModel = hrt.prefab.Library.getRegistered().get("shgraph");
  2173. var custom = {
  2174. label : "Custom...",
  2175. click : function() {
  2176. ide.chooseFile(shModel.inf.fileSource.concat(graphModel.inf.fileSource), function(path) {
  2177. var cl = isClassShader(path) ? shModel.cl : graphModel.cl;
  2178. var p = Type.createInstance(cl, [parentElt]);
  2179. p.source = path;
  2180. autoName(p);
  2181. if(onMake != null)
  2182. onMake(p);
  2183. addElements([p]);
  2184. });
  2185. },
  2186. icon : shModel.inf.icon,
  2187. };
  2188. function classShaderItem(path) : hide.comp.ContextMenu.ContextMenuItem {
  2189. var name = path;
  2190. if(StringTools.endsWith(name, ".hx")) {
  2191. name = new haxe.io.Path(path).file;
  2192. }
  2193. else {
  2194. name = name.split(".").pop();
  2195. }
  2196. return getNewTypeMenuItem("shader", parentElt, onMake, name, name, path);
  2197. }
  2198. function graphShaderItem(path) : hide.comp.ContextMenu.ContextMenuItem {
  2199. var name = new haxe.io.Path(path).file;
  2200. return getNewTypeMenuItem("shgraph", parentElt, onMake, name, name, path);
  2201. }
  2202. var menu : Array<hide.comp.ContextMenu.ContextMenuItem> = [];
  2203. var shaders : Array<String> = hide.Ide.inst.currentConfig.get("fx.shaders", []);
  2204. for(path in shaders) {
  2205. var strippedSlash = StringTools.endsWith(path, "/") ? path.substr(0, -1) : path;
  2206. var fullPath = ide.getPath(strippedSlash);
  2207. if( isClassShader(path) ) {
  2208. menu.push(classShaderItem(path));
  2209. } else if( StringTools.endsWith(path, ".shgraph")) {
  2210. menu.push(graphShaderItem(path));
  2211. } else if( sys.FileSystem.exists(fullPath) && sys.FileSystem.isDirectory(fullPath) ) {
  2212. var submenu : Array<hide.comp.ContextMenu.ContextMenuItem> = [];
  2213. for( c in sys.FileSystem.readDirectory(fullPath) ) {
  2214. var relPath = ide.makeRelative(fullPath + "/" + c);
  2215. if( isClassShader(relPath) ) {
  2216. submenu.push(classShaderItem(relPath));
  2217. } else if( StringTools.endsWith(relPath, ".shgraph")) {
  2218. submenu.push(graphShaderItem(relPath));
  2219. }
  2220. }
  2221. if( submenu.length > 0 ) {
  2222. menu.push({ label : path, menu : submenu });
  2223. }
  2224. }
  2225. }
  2226. menu.sort(function(l1,l2) return Reflect.compare(l1.label,l2.label));
  2227. menu.unshift(custom);
  2228. return {
  2229. label: "Shader",
  2230. menu: menu
  2231. };
  2232. }
  2233. public function getZ(x: Float, y: Float, ?paintOn : hrt.prefab.Prefab) {
  2234. var offset = 1000000;
  2235. var ray = h3d.col.Ray.fromValues(x, y, offset, 0, 0, -1);
  2236. var dist = projectToGround(ray, paintOn);
  2237. if(dist >= 0) {
  2238. return offset - dist;
  2239. }
  2240. return 0.;
  2241. }
  2242. function getGroundPrefabs() : Array<PrefabElement> {
  2243. function getAll(data:PrefabElement) {
  2244. var all = data.findAll((p) -> p);
  2245. for( a in all.copy() ) {
  2246. var r = Std.downcast(a, hrt.prefab.Reference);
  2247. if( r != null ) {
  2248. var sub = @:privateAccess r.ref;
  2249. if( sub != null ) all = all.concat(getAll(sub));
  2250. }
  2251. }
  2252. return all;
  2253. }
  2254. var all = getAll(sceneData);
  2255. var grounds = [for( p in all ) if( p.getHideProps().isGround ) p];
  2256. var results = [];
  2257. for( g in grounds )
  2258. results = results.concat(getAll(g));
  2259. return results;
  2260. }
  2261. public function projectToGround(ray: h3d.col.Ray, ?paintOn : hrt.prefab.Prefab ) {
  2262. var minDist = -1.;
  2263. for( elt in (paintOn == null ? getGroundPrefabs() : [paintOn]) ) {
  2264. var obj = Std.downcast(elt, Object3D);
  2265. if( obj == null ) continue;
  2266. var ctx = getContext(elt);
  2267. if( ctx == null ) continue;
  2268. var lray = ray.clone();
  2269. lray.transform(ctx.local3d.getInvPos());
  2270. var dist = obj.localRayIntersection(ctx, lray);
  2271. if( dist > 0 ) {
  2272. var pt = lray.getPoint(dist);
  2273. pt.transform(ctx.local3d.getAbsPos());
  2274. var dist = pt.sub(ray.getPos()).length();
  2275. if( minDist < 0 || dist < minDist )
  2276. minDist = dist;
  2277. }
  2278. }
  2279. if( minDist >= 0 )
  2280. return minDist;
  2281. var zPlane = h3d.col.Plane.Z(0);
  2282. var pt = ray.intersect(zPlane);
  2283. if( pt != null ) {
  2284. minDist = pt.sub(ray.getPos()).length();
  2285. var dirToPt = pt.sub(ray.getPos());
  2286. if( dirToPt.dot(ray.getDir()) < 0 )
  2287. return -1;
  2288. }
  2289. return minDist;
  2290. }
  2291. public function screenDistToGround(sx : Float, sy : Float, ?paintOn : hrt.prefab.Prefab) : Null<Float> {
  2292. var camera = scene.s3d.camera;
  2293. var ray = camera.rayFromScreen(sx, sy);
  2294. var dist = projectToGround(ray, paintOn);
  2295. if( dist >= 0 )
  2296. return dist + camera.zNear;
  2297. return null;
  2298. }
  2299. public function screenToGround(sx: Float, sy: Float, ?paintOn : hrt.prefab.Prefab ) {
  2300. var camera = scene.s3d.camera;
  2301. var ray = camera.rayFromScreen(sx, sy);
  2302. var dist = projectToGround(ray, paintOn);
  2303. if(dist >= 0) {
  2304. return ray.getPoint(dist);
  2305. }
  2306. return null;
  2307. }
  2308. public function worldToScreen(wx: Float, wy: Float, wz: Float) {
  2309. var camera = scene.s3d.camera;
  2310. var pt = camera.project(wx, wy, wz, scene.s2d.width, scene.s2d.height);
  2311. return new h2d.col.Point(pt.x, pt.y);
  2312. }
  2313. public function worldMat(?obj: Object, ?elt: PrefabElement) {
  2314. if(obj != null) {
  2315. if(obj.defaultTransform != null) {
  2316. var m = obj.defaultTransform.clone();
  2317. m.invert();
  2318. m.multiply(m, obj.getAbsPos());
  2319. return m;
  2320. }
  2321. else {
  2322. return obj.getAbsPos().clone();
  2323. }
  2324. }
  2325. else {
  2326. var mat = new h3d.Matrix();
  2327. mat.identity();
  2328. var o = Std.downcast(elt, Object3D);
  2329. while(o != null) {
  2330. mat.multiply(mat, o.getTransform());
  2331. o = o.parent.to(hrt.prefab.Object3D);
  2332. }
  2333. return mat;
  2334. }
  2335. }
  2336. function editPivot() {
  2337. if (curEdit.rootObjects.length == 1) {
  2338. var ray = scene.s3d.camera.rayFromScreen(scene.s2d.mouseX, scene.s2d.mouseY);
  2339. var polyColliders = new Array<PolygonBuffer>();
  2340. var meshes = new Array<Mesh>();
  2341. for (m in curEdit.rootObjects[0].getMeshes()) {
  2342. var hmdModel = Std.downcast(m.primitive, HMDModel);
  2343. if (hmdModel != null) {
  2344. var optiCollider = Std.downcast(hmdModel.getCollider(), OptimizedCollider);
  2345. var polyCollider = Std.downcast(optiCollider.b, PolygonBuffer);
  2346. if (polyCollider != null) {
  2347. polyColliders.push(polyCollider);
  2348. meshes.push(m);
  2349. }
  2350. }
  2351. }
  2352. if (polyColliders.length > 0) {
  2353. var pivot = getClosestVertex(polyColliders, meshes, ray);
  2354. if (pivot != null) {
  2355. pivot.elt = curEdit.rootElements[0];
  2356. customPivot = pivot;
  2357. } else {
  2358. // mouse outside
  2359. }
  2360. } else {
  2361. // no collider found
  2362. }
  2363. } else {
  2364. throw "Can't edit when multiple objects are selected";
  2365. }
  2366. }
  2367. function getClosestVertex( colliders : Array<PolygonBuffer>, meshes : Array<Mesh>, ray : Ray ) : CustomPivot {
  2368. var best = -1.;
  2369. var bestVertex : CustomPivot = null;
  2370. for (idx in 0...colliders.length) {
  2371. var c = colliders[idx];
  2372. var m = meshes[idx];
  2373. var r = ray.clone();
  2374. r.transform(m.getInvPos());
  2375. var rdir = new FPoint(r.lx, r.ly, r.lz);
  2376. var r0 = new FPoint(r.px, r.py, r.pz);
  2377. @:privateAccess var i = c.startIndex;
  2378. @:privateAccess for( t in 0...c.triCount ) {
  2379. var i0 = c.indexes[i++] * 3;
  2380. var p0 = new FPoint(c.buffer[i0++], c.buffer[i0++], c.buffer[i0]);
  2381. var i1 = c.indexes[i++] * 3;
  2382. var p1 = new FPoint(c.buffer[i1++], c.buffer[i1++], c.buffer[i1]);
  2383. var i2 = c.indexes[i++] * 3;
  2384. var p2 = new FPoint(c.buffer[i2++], c.buffer[i2++], c.buffer[i2]);
  2385. var e1 = p1.sub(p0);
  2386. var e2 = p2.sub(p0);
  2387. var p = rdir.cross(e2);
  2388. var det = e1.dot(p);
  2389. if( det < hxd.Math.EPSILON ) continue; // backface culling (negative) and near parallel (epsilon)
  2390. var invDet = 1 / det;
  2391. var T = r0.sub(p0);
  2392. var u = T.dot(p) * invDet;
  2393. if( u < 0 || u > 1 ) continue;
  2394. var q = T.cross(e1);
  2395. var v = rdir.dot(q) * invDet;
  2396. if( v < 0 || u + v > 1 ) continue;
  2397. var t = e2.dot(q) * invDet;
  2398. if( t < hxd.Math.EPSILON ) continue;
  2399. if( best < 0 || t < best ) {
  2400. best = t;
  2401. var ptIntersection = r.getPoint(t);
  2402. var pI = new FPoint(ptIntersection.x, ptIntersection.y, ptIntersection.z);
  2403. inline function distanceFPoints(a : FPoint, b : FPoint) : Float {
  2404. var dx = a.x - b.x;
  2405. var dy = a.y - b.y;
  2406. var dz = a.z - b.z;
  2407. return dx * dx + dy * dy + dz * dz;
  2408. }
  2409. var test0 = distanceFPoints(p0, pI);
  2410. var test1 = distanceFPoints(p1, pI);
  2411. var test2 = distanceFPoints(p2, pI);
  2412. var locBestVertex : FPoint;
  2413. if (test0 <= test1 && test0 <= test2) {
  2414. locBestVertex = p0;
  2415. } else if (test1 <= test0 && test1 <= test2) {
  2416. locBestVertex = p1;
  2417. } else {
  2418. locBestVertex = p2;
  2419. }
  2420. bestVertex = { elt : null, mesh: m, locPos: new Vector(locBestVertex.x, locBestVertex.y, locBestVertex.z) };
  2421. }
  2422. }
  2423. }
  2424. return bestVertex;
  2425. }
  2426. static function isReference( what : PrefabElement ) : Bool {
  2427. return what != null && what.to(hrt.prefab.Reference) != null;
  2428. }
  2429. static function getPivot(objects: Array<Object>) {
  2430. if (customPivot != null) {
  2431. return customPivot.mesh.localToGlobal(customPivot.locPos.toPoint());
  2432. }
  2433. var pos = new h3d.col.Point();
  2434. for(o in objects) {
  2435. pos = pos.add(o.getAbsPos().getPosition().toPoint());
  2436. }
  2437. pos.scale(1.0 / objects.length);
  2438. return pos;
  2439. }
  2440. static function getPivot2D( objects : Array<h2d.Object> ) {
  2441. var b = new h2d.col.Bounds();
  2442. for( o in objects )
  2443. b.addBounds(o.getBounds());
  2444. return b;
  2445. }
  2446. public static function hasParent(elt: PrefabElement, list: Array<PrefabElement>) {
  2447. for(p in list) {
  2448. if(isParent(elt, p))
  2449. return true;
  2450. }
  2451. return false;
  2452. }
  2453. public static function hasChild(elt: PrefabElement, list: Array<PrefabElement>) {
  2454. for(p in list) {
  2455. if(isParent(p, elt))
  2456. return true;
  2457. }
  2458. return false;
  2459. }
  2460. public static function isParent(elt: PrefabElement, parent: PrefabElement) {
  2461. var p = elt.parent;
  2462. while(p != null) {
  2463. if(p == parent) return true;
  2464. p = p.parent;
  2465. }
  2466. return false;
  2467. }
  2468. static function getParentGroup(elt: PrefabElement) {
  2469. while(elt != null) {
  2470. if(elt.type == "object")
  2471. return elt;
  2472. elt = elt.parent;
  2473. }
  2474. return null;
  2475. }
  2476. }