SceneEditor.hx 73 KB

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