2
0
Эх сурвалжийг харах

Merge pull request #162 from Speedphoenix/ft-kill-l3d

Kill view.l3d.Level3D, revamp Prefab view
trethaller 4 жил өмнө
parent
commit
b5a497b2bb

+ 10 - 11
bin/defaultProps.json

@@ -74,8 +74,9 @@
 	"key.sceneeditor.selectParent": "Backspace",
 	"key.sceneeditor.selectParent": "Backspace",
 	"key.sceneeditor.reparent": "Ctrl-P",
 	"key.sceneeditor.reparent": "Ctrl-P",
 	"key.sceneeditor.editPivot" : "P",
 	"key.sceneeditor.editPivot" : "P",
-	"key.sceneeditor.toggleLocal": "D",
-	"key.sceneeditor.toggleGrid": "G",
+	"key.sceneeditor.localTransformsToggle": "D",
+	"key.sceneeditor.gridToggle": "G",
+	"key.sceneeditor.toggleLayout": "Tab",
 
 
 	// cdb config
 	// cdb config
 	"cdb.databaseFile" : "data.cdb",
 	"cdb.databaseFile" : "data.cdb",
@@ -116,22 +117,20 @@
 	"sceneeditor.rotatePrecision": 0.1,
 	"sceneeditor.rotatePrecision": 0.1,
 
 
 	"sceneeditor.newgroups" : ["2D|hrt.prefab.Object2D","3D|hrt.prefab.Object3D","RFX|hrt.prefab.rfx.RendererFX"],
 	"sceneeditor.newgroups" : ["2D|hrt.prefab.Object2D","3D|hrt.prefab.Object3D","RFX|hrt.prefab.rfx.RendererFX"],
+	"sceneeditor.recentsize" : 6,
 
 
 	"sceneeditor.icons" : {},
 	"sceneeditor.icons" : {},
 	"sceneeditor.ranges" : {},
 	"sceneeditor.ranges" : {},
 	"sceneeditor.huds" : {},
 	"sceneeditor.huds" : {},
 
 
 	// l3d config
 	// l3d config
-	"l3d.groundLayer": "ground",
-	"l3d.cdbLevel": "level",
-	"l3d.colors": {
+	"sceneeditor.colors": {
 	},
 	},
-	"l3d.treeStyles": {
-		"settings": {"color": "#ffffff", "font-weight": "bold"}
-	},
-	"l3d.filterTypes": ["terrain", "model", "polygon", "box", "instance", "light"],
-	"l3d.tags": [ { "id": "tag", "color": "#802000" } ],
-	"l3d.camera.moveSpeed": 1.5,
+	"sceneeditor.filterTypes": ["terrain", "model", "polygon", "box", "instance", "light"],
+	"sceneeditor.tags": [ { "id": "tag", "color": "#802000" } ],
+	"sceneeditor.camera.moveSpeed": 1.5,
+	"sceneeditor.gridSize": 100,
+	"sceneeditor.gridStep": 1,
 
 
 	// FX editor
 	// FX editor
 	"fx.shaders": [
 	"fx.shaders": [

+ 65 - 24
bin/style.css

@@ -2,6 +2,7 @@ body {
   margin: 0;
   margin: 0;
   padding: 0;
   padding: 0;
   background-color: black;
   background-color: black;
+  user-select: none;
 }
 }
 ul,
 ul,
 li {
 li {
@@ -225,18 +226,23 @@ input[type=checkbox]:checked:after {
 .hide-scene-layer .jstree-container-ul {
 .hide-scene-layer .jstree-container-ul {
   padding-right: 5px;
   padding-right: 5px;
 }
 }
-.prefabview .scenetree {
-  max-height: 350px;
+.scene-partition.reduced-columns .hide-scenetree {
+  max-height: 40%;
+}
+.scene-partition.reduced-columns .props-column {
+  max-height: 60%;
+  padding-bottom: 36px;
 }
 }
 .hide-scenetree {
 .hide-scenetree {
   flex: 1;
   flex: 1;
   background-color: #111;
   background-color: #111;
   border: 1px solid #444;
   border: 1px solid #444;
-  width: 300px;
+  min-width: 300px;
   overflow-y: auto;
   overflow-y: auto;
+  min-height: 350px;
 }
 }
 .hide-scenetree .jstree {
 .hide-scenetree .jstree {
-  width: 300px;
+  min-width: 300px;
   overflow-x: hidden;
   overflow-x: hidden;
 }
 }
 .hide-scenetree .jstree-container-ul {
 .hide-scenetree .jstree-container-ul {
@@ -310,6 +316,17 @@ input[type=checkbox]:checked:after {
 .flex.vertical {
 .flex.vertical {
   flex-direction: column;
   flex-direction: column;
 }
 }
+.scene-partition .hide-toolbar .button,
+.scene-partition .show-cols-btn {
+  min-width: 16px !important;
+  height: 16px !important;
+  font-size: 12px !important;
+}
+.scene-partition .show-cols-btn {
+  position: absolute;
+  margin-top: 2px;
+  right: 0px;
+}
 .hide-toolbar {
 .hide-toolbar {
   flex: none;
   flex: none;
   padding-top: 2px;
   padding-top: 2px;
@@ -317,14 +334,42 @@ input[type=checkbox]:checked:after {
   background-color: #222222;
   background-color: #222222;
   display: inline-block;
   display: inline-block;
 }
 }
+.hide-toolbar .layout-btn {
+  position: absolute;
+  right: 31px;
+}
+.hide-toolbar .close-btn {
+  position: absolute;
+  right: 0px;
+}
+.hide-toolbar .toolbar-label,
+.hide-toolbar .menu {
+  margin-right: 5px;
+  display: inline-block;
+  min-width: 20px;
+  padding: 2px;
+  text-align: center;
+  text-shadow: 1px 2px 2px black;
+}
+.hide-toolbar .toolbar-label > .icon,
+.hide-toolbar .menu > .icon {
+  vertical-align: center;
+}
+.hide-toolbar .menu {
+  user-select: none;
+  cursor: pointer;
+  background: #444;
+  padding-left: 5px;
+  padding-right: 5px;
+}
 .hide-toolbar .button,
 .hide-toolbar .button,
-.hide-toolbar .toggle {
+.hide-toolbar .toggle,
+.scene-partition .show-cols-btn {
   margin-right: 5px;
   margin-right: 5px;
   display: inline-block;
   display: inline-block;
   user-select: none;
   user-select: none;
   border: 1px solid #5a5a5a;
   border: 1px solid #5a5a5a;
   min-width: 20px;
   min-width: 20px;
-  height: 20px;
   padding: 2px;
   padding: 2px;
   text-align: center;
   text-align: center;
   border-radius: 1px;
   border-radius: 1px;
@@ -332,14 +377,15 @@ input[type=checkbox]:checked:after {
   background: -webkit-linear-gradient(top, #5a5a5a, #323232);
   background: -webkit-linear-gradient(top, #5a5a5a, #323232);
   cursor: pointer;
   cursor: pointer;
   font-size: 16px;
   font-size: 16px;
-  vertical-align: top;
+  vertical-align: middle;
 }
 }
 .hide-toolbar .button > .icon,
 .hide-toolbar .button > .icon,
 .hide-toolbar .toggle > .icon {
 .hide-toolbar .toggle > .icon {
   vertical-align: center;
   vertical-align: center;
 }
 }
 .hide-toolbar .button:hover,
 .hide-toolbar .button:hover,
-.hide-toolbar .toggle:hover {
+.hide-toolbar .toggle:hover,
+.scene-partition .show-cols-btn:hover {
   color: white;
   color: white;
   border-color: white;
   border-color: white;
   background-color: #666;
   background-color: #666;
@@ -370,12 +416,12 @@ input[type=checkbox]:checked:after {
   padding-top: 3px;
   padding-top: 3px;
   border-top-color: white;
   border-top-color: white;
 }
 }
-.hide-toolbar .button:active {
+.hide-toolbar .button:active,
+.scene-partition .show-cols-btn:active {
   padding-top: 3px;
   padding-top: 3px;
   padding-bottom: 1px;
   padding-bottom: 1px;
 }
 }
 .hide-toolbar .select {
 .hide-toolbar .select {
-  height: 20px;
   padding: 2px;
   padding: 2px;
   padding-top: 4px;
   padding-top: 4px;
   margin-right: 5px;
   margin-right: 5px;
@@ -529,6 +575,15 @@ input[type=checkbox]:checked:after {
   width: 100px;
   width: 100px;
 }
 }
 /* Properties */
 /* Properties */
+.props-column {
+  height: 100%;
+  background-color: #111;
+  width: 320px;
+}
+.props-column .hide-toolbar {
+  width: 100%;
+  vertical-align: middle;
+}
 .hide-properties {
 .hide-properties {
   padding: 10px;
   padding: 10px;
   overflow: auto;
   overflow: auto;
@@ -1581,10 +1636,6 @@ body.hide-subview .lm_controls {
 .jstree .hidden a {
 .jstree .hidden a {
   color: #555;
   color: #555;
 }
 }
-.jstree a.favorite {
-  color: white;
-  font-weight: bold;
-}
 .jstree .disabled a {
 .jstree .disabled a {
   color: #533 !important;
   color: #533 !important;
   display: inline;
   display: inline;
@@ -1610,16 +1661,6 @@ body.hide-subview .lm_controls {
 .jstree-node {
 .jstree-node {
   padding-right: 10px!important;
   padding-right: 10px!important;
 }
 }
-.hide-scene-outliner .favorites {
-  overflow-y: auto;
-  background: #111;
-}
-.hide-scene-outliner .favorites label {
-  font-weight: bold;
-}
-.hide-scene-outliner .favorites-tree {
-  padding: 4px;
-}
 .visibility-toggle,
 .visibility-toggle,
 .visibility-large-toggle {
 .visibility-large-toggle {
   position: absolute;
   position: absolute;

+ 85 - 25
bin/style.less

@@ -2,6 +2,7 @@ body {
 	margin : 0;
 	margin : 0;
 	padding : 0;
 	padding : 0;
 	background-color : black;
 	background-color : black;
+	user-select:none;
 }
 }
 
 
 //:focus { outline: none; }
 //:focus { outline: none; }
@@ -239,19 +240,28 @@ input[type=checkbox] {
 
 
 }
 }
 
 
-.prefabview .scenetree {
-	max-height : 350px;
+
+.scene-partition.reduced-columns {
+	.hide-scenetree {
+		max-height: 40%;
+	}
+
+	.props-column {
+		max-height: 60%;
+		padding-bottom: 36px;
+	}
 }
 }
 
 
 .hide-scenetree {
 .hide-scenetree {
 	flex: 1;
 	flex: 1;
 	background-color : #111;
 	background-color : #111;
 	border : 1px solid #444;
 	border : 1px solid #444;
-	width: 300px;
+	min-width: 300px;
 	overflow-y: auto;
 	overflow-y: auto;
+	min-height : 350px;
 
 
 	.jstree {
 	.jstree {
-		width: 300px;
+		min-width: 300px;
 		overflow-x: hidden;
 		overflow-x: hidden;
 	}
 	}
 
 
@@ -338,6 +348,29 @@ input[type=checkbox] {
 	flex-direction: column;
 	flex-direction: column;
 }
 }
 
 
+.scene-partition .hide-toolbar .button {
+	min-width : 16px !important;
+	height : 16px !important;
+	font-size: 12px !important;
+}
+
+.scene-partition .show-cols-btn {
+	&:extend(.hide-toolbar .button);
+	&:extend(.scene-partition .hide-toolbar .button);
+
+	position: absolute;
+	margin-top: 2px;
+	right: 0px;
+
+	&:hover {
+		&:extend(.hide-toolbar .button:hover);
+	}
+
+	&:active {
+		&:extend(.hide-toolbar .button:active);
+	}
+}
+
 .hide-toolbar {
 .hide-toolbar {
 
 
 	flex: none;
 	flex: none;
@@ -346,13 +379,42 @@ input[type=checkbox] {
 	background-color : rgb(34,34,34);
 	background-color : rgb(34,34,34);
 	display: inline-block;
 	display: inline-block;
 
 
+	.layout-btn {
+		position: absolute;
+		right: 31px;
+	}
+
+	.close-btn {
+		position: absolute;
+		right: 0px;
+	}
+
+	.toolbar-label, .menu {
+		margin-right : 5px;
+		display: inline-block;
+		min-width : 20px;
+		padding : 2px;
+		text-align: center;
+		text-shadow: 1px 2px 2px black;
+		>.icon {
+			vertical-align: center;
+		}
+	}
+
+	.menu {
+		user-select:none;
+		cursor : pointer;
+		background : #444;
+		padding-left: 5px;
+		padding-right: 5px;
+	}
+
 	.button, .toggle {
 	.button, .toggle {
 		margin-right : 5px;
 		margin-right : 5px;
 		display: inline-block;
 		display: inline-block;
 		user-select:none;
 		user-select:none;
 		border: 1px solid rgb(90,90,90);
 		border: 1px solid rgb(90,90,90);
 		min-width : 20px;
 		min-width : 20px;
-		height : 20px;
 		padding : 2px;
 		padding : 2px;
 		text-align: center;
 		text-align: center;
 		border-radius: 1px;
 		border-radius: 1px;
@@ -360,7 +422,7 @@ input[type=checkbox] {
 		background: -webkit-linear-gradient(top, rgb(90,90,90), rgb(50,50,50));
 		background: -webkit-linear-gradient(top, rgb(90,90,90), rgb(50,50,50));
 		cursor : pointer;
 		cursor : pointer;
 		font-size: 16px;
 		font-size: 16px;
-		vertical-align: top;
+		vertical-align: middle;
 		>.icon {
 		>.icon {
 			vertical-align: center;
 			vertical-align: center;
 		}
 		}
@@ -402,7 +464,6 @@ input[type=checkbox] {
 	}
 	}
 
 
 	.select {
 	.select {
-		height : 20px;
 		padding : 2px;
 		padding : 2px;
 		padding-top: 4px;
 		padding-top: 4px;
 		margin-right : 5px;
 		margin-right : 5px;
@@ -418,6 +479,13 @@ input[type=checkbox] {
 	}
 	}
 }
 }
 
 
+.tools-buttons {
+	.select, .button, .menu {
+		
+	}
+
+}
+
 .hide-list {
 .hide-list {
 	width : 100%;
 	width : 100%;
 	min-height : 200px;
 	min-height : 200px;
@@ -570,6 +638,16 @@ input[type=checkbox] {
 }
 }
 
 
 /* Properties */
 /* Properties */
+.props-column {
+	height : 100%;
+	background-color: #111;
+	width: 320px;
+
+	.hide-toolbar {
+		width: 100%;
+		vertical-align: middle;
+	}
+}
 
 
 .hide-properties {
 .hide-properties {
 	//flex : 0 0 300px;
 	//flex : 0 0 300px;
@@ -1781,11 +1859,6 @@ body.hide-subview {
 		}
 		}
 	}
 	}
 
 
-	a.favorite {
-		color: white;
-		font-weight: bold;
-	}
-
 	.disabled {
 	.disabled {
 		a {
 		a {
 			color: #533 !important;
 			color: #533 !important;
@@ -1821,19 +1894,6 @@ body.hide-subview {
 	padding-right: 10px!important;
 	padding-right: 10px!important;
 }
 }
 
 
-.hide-scene-outliner {
-	.favorites {
-		overflow-y: auto;
-		label {
-			font-weight: bold;
-		}
-		background: #111;
-	}
-	.favorites-tree {
-		padding: 4px;
-	}
-}
-
 .visibility-toggle, .visibility-large-toggle {
 .visibility-toggle, .visibility-large-toggle {
 	position: absolute;
 	position: absolute;
 	right: 4px;
 	right: 4px;

+ 2 - 0
hide/Config.hx

@@ -12,6 +12,8 @@ typedef HideGlobalConfig = {
 	var recentProjects : Array<String>;
 	var recentProjects : Array<String>;
 
 
 	var windowPos : { x : Int, y : Int, w : Int, h : Int, max : Bool };
 	var windowPos : { x : Int, y : Int, w : Int, h : Int, max : Bool };
+
+	@:optional var sceneEditorLayout : { colsVisible : Bool, colsCombined : Bool };
 }
 }
 
 
 typedef HideProjectConfig = {
 typedef HideProjectConfig = {

+ 1 - 1
hide/Ide.hx

@@ -35,7 +35,7 @@ class Ide {
 		user : Config,
 		user : Config,
 		current : Config,
 		current : Config,
 	};
 	};
-	var ideConfig(get, never) : hide.Config.HideGlobalConfig;
+	public var ideConfig(get, never) : hide.Config.HideGlobalConfig;
 	public var projectConfig(get, never) : hide.Config.HideProjectConfig;
 	public var projectConfig(get, never) : hide.Config.HideProjectConfig;
 
 
 	var window : nw.Window;
 	var window : nw.Window;

+ 8 - 0
hide/comp/ContextMenu.hx

@@ -7,6 +7,10 @@ typedef ContextMenuItem = {
 	@:optional var enabled : Bool;
 	@:optional var enabled : Bool;
 	@:optional var checked : Bool;
 	@:optional var checked : Bool;
 	@:optional var isSeparator : Bool;
 	@:optional var isSeparator : Bool;
+	@:optional var keys : {
+		@:optional var key : String;
+		@:optional var modifiers : String;
+	};
 }
 }
 
 
 class ContextMenu {
 class ContextMenu {
@@ -34,6 +38,10 @@ class ContextMenu {
 
 
 	function makeMenuItem(i:ContextMenuItem) {
 	function makeMenuItem(i:ContextMenuItem) {
 		var mconf : nw.MenuItem.MenuItemOptions = { label : i.label, type : i.checked != null ? Checkbox : i.isSeparator ? Separator : Normal };
 		var mconf : nw.MenuItem.MenuItemOptions = { label : i.label, type : i.checked != null ? Checkbox : i.isSeparator ? Separator : Normal };
+		if( i.keys != null ) {
+			mconf.key = i.keys.key;
+			mconf.modifiers = i.keys.modifiers;
+		}
 		if( i.menu != null ) mconf.submenu = makeMenu(i.menu);
 		if( i.menu != null ) mconf.submenu = makeMenu(i.menu);
 		var m = new nw.MenuItem(mconf);
 		var m = new nw.MenuItem(mconf);
 		if( i.checked != null ) m.checked = i.checked;
 		if( i.checked != null ) m.checked = i.checked;

+ 30 - 78
hide/comp/SceneEditor.hx

@@ -137,7 +137,6 @@ typedef CustomPivot = { elt : PrefabElement, mesh : Mesh, locPos : Vector };
 class SceneEditor {
 class SceneEditor {
 
 
 	public var tree : hide.comp.IconTree<PrefabElement>;
 	public var tree : hide.comp.IconTree<PrefabElement>;
-	public var favTree : hide.comp.IconTree<PrefabElement>;
 	public var scene : hide.comp.Scene;
 	public var scene : hide.comp.Scene;
 	public var properties : hide.comp.PropsEditor;
 	public var properties : hide.comp.PropsEditor;
 	public var context(default,null) : hrt.prefab.Context;
 	public var context(default,null) : hrt.prefab.Context;
@@ -162,7 +161,6 @@ class SceneEditor {
 	var ide : hide.Ide;
 	var ide : hide.Ide;
 	public var event(default, null) : hxd.WaitEvent;
 	public var event(default, null) : hxd.WaitEvent;
 	var hideList : Map<PrefabElement, Bool> = new Map();
 	var hideList : Map<PrefabElement, Bool> = new Map();
-	var favorites : Array<PrefabElement> = [];
 
 
 	var undo(get, null):hide.ui.UndoHistory;
 	var undo(get, null):hide.ui.UndoHistory;
 	function get_undo() { return view.undo; }
 	function get_undo() { return view.undo; }
@@ -186,10 +184,6 @@ class SceneEditor {
 		tree.async = false;
 		tree.async = false;
 		tree.autoOpenNodes = false;
 		tree.autoOpenNodes = false;
 
 
-		favTree = new hide.comp.IconTree();
-		favTree.async = false;
-		favTree.autoOpenNodes = false;
-
 		var sceneEl = new Element('<div class="heaps-scene"></div>');
 		var sceneEl = new Element('<div class="heaps-scene"></div>');
 		scene = new hide.comp.Scene(view.config, null, sceneEl);
 		scene = new hide.comp.Scene(view.config, null, sceneEl);
 		scene.editor = this;
 		scene.editor = this;
@@ -253,20 +247,12 @@ class SceneEditor {
 						hideList.set(p, true);
 						hideList.set(p, true);
 				}
 				}
 			}
 			}
-			var favList = @:privateAccess view.getDisplayState("favorites");
-			if(favList != null) {
-				for(p in all) {
-					if(favList.indexOf(p.getAbsPath(true)) >= 0)
-						favorites.push(p);
-				}
-			}
 		}
 		}
 	}
 	}
 
 
 	public function dispose() {
 	public function dispose() {
 		scene.dispose();
 		scene.dispose();
 		tree.dispose();
 		tree.dispose();
-		favTree.dispose();
 	}
 	}
 
 
 	function set_camera2D(b) {
 	function set_camera2D(b) {
@@ -465,34 +451,6 @@ class SceneEditor {
 			};
 			};
 			return r;
 			return r;
 		}
 		}
-		favTree.get = function (o:PrefabElement) {
-			if(o == null) {
-				return [for(f in favorites) makeItem(f, {
-					disabled: true
-				})];
-			}
-			return [];
-		}
-		favTree.allowRename = false;
-		favTree.init();
-		favTree.onAllowMove = function(_, _) {
-			return false;
-		};
-		favTree.onClick = function(e, evt) {
-			if(evt.ctrlKey) {
-				var sel = tree.getSelection();
-				sel.push(e);
-				selectElements(sel);
-				tree.revealNode(e);
-			}
-			else
-				selectElements([e]);
-		}
-		favTree.onDblClick = function(e) {
-			selectElements([e]);
-			tree.revealNode(e);
-			return true;
-		}
 		tree.get = function(o:PrefabElement) {
 		tree.get = function(o:PrefabElement) {
 			var objs = o == null ? sceneData.children : Lambda.array(o);
 			var objs = o == null ? sceneData.children : Lambda.array(o);
 			if( o != null && o.getHideProps().hideChildren != null ) {
 			if( o != null && o.getHideProps().hideChildren != null ) {
@@ -525,10 +483,9 @@ class SceneEditor {
 				{ label : "New...", menu : newItems },
 				{ label : "New...", menu : newItems },
 			];
 			];
 			var actionItems : Array<hide.comp.ContextMenu.ContextMenuItem> = [
 			var actionItems : Array<hide.comp.ContextMenu.ContextMenuItem> = [
-				{ label : "Favorite", checked : current != null && isFavorite(current), click : function() setFavorite(current, !isFavorite(current)) },
-				{ label : "Rename", enabled : current != null, click : function() tree.editNode(current) },
-				{ label : "Delete", enabled : current != null, click : function() deleteElements(curEdit.rootElements) },
-				{ label : "Duplicate", enabled : current != null, click : duplicate.bind(false) },
+				{ label : "Rename", enabled : current != null, click : function() tree.editNode(current), keys : hide.ui.Keys.getKeys("rename", view.config) },
+				{ label : "Delete", enabled : current != null, click : function() deleteElements(curEdit.rootElements), keys : hide.ui.Keys.getKeys("delete", view.config) },
+				{ label : "Duplicate", enabled : current != null, click : duplicate.bind(false), keys : hide.ui.Keys.getKeys("duplicate", view.config) },
 			];
 			];
 
 
 			var isObj = current != null && (current.to(Object3D) != null || current.to(Object2D) != null);
 			var isObj = current != null && (current.to(Object3D) != null || current.to(Object2D) != null);
@@ -542,18 +499,18 @@ class SceneEditor {
 			if( isObj ) {
 			if( isObj ) {
 				var visible = !isHidden(current);
 				var visible = !isHidden(current);
 				menuItems = menuItems.concat([
 				menuItems = menuItems.concat([
-					{ label : "Show in editor", checked : visible, click : function() setVisible(curEdit.elements, !visible) },
+					{ label : "Show in editor" , checked : visible, click : function() setVisible(curEdit.elements, !visible), keys : hide.ui.Keys.getKeys("sceneeditor.hide", view.config) },
 					{ label : "Locked", checked : current.locked, click : function() {
 					{ label : "Locked", checked : current.locked, click : function() {
 						current.locked = !current.locked;
 						current.locked = !current.locked;
 						setLock(curEdit.elements, current.locked);
 						setLock(curEdit.elements, current.locked);
 					} },
 					} },
-					{ label : "Select all", click : selectAll },
+					{ label : "Select all", click : selectAll, keys : hide.ui.Keys.getKeys("selectAll", view.config) },
 					{ label : "Select children", enabled : current != null, click : function() selectElements(current.flatten()) },
 					{ label : "Select children", enabled : current != null, click : function() selectElements(current.flatten()) },
 				]);
 				]);
 				if( !isRef )
 				if( !isRef )
 					actionItems = actionItems.concat([
 					actionItems = actionItems.concat([
-						{ label : "Isolate", click : function() isolate(curEdit.elements) },
-						{ label : "Group", enabled : curEdit != null && canGroupSelection(), click : groupSelection }
+						{ label : "Isolate", click : function() isolate(curEdit.elements), keys : hide.ui.Keys.getKeys("sceneeditor.isolate", view.config) },
+						{ label : "Group", enabled : curEdit != null && canGroupSelection(), click : groupSelection, keys : hide.ui.Keys.getKeys("group", view.config) },
 					]);
 					]);
 			}
 			}
 
 
@@ -567,7 +524,6 @@ class SceneEditor {
 			new hide.comp.ContextMenu(menuItems.concat(actionItems));
 			new hide.comp.ContextMenu(menuItems.concat(actionItems));
 		};
 		};
 		tree.element.parent().contextmenu(ctxMenu.bind(tree));
 		tree.element.parent().contextmenu(ctxMenu.bind(tree));
-		favTree.element.parent().contextmenu(ctxMenu.bind(favTree));
 		tree.allowRename = true;
 		tree.allowRename = true;
 		tree.init();
 		tree.init();
 		tree.onClick = function(e, _) {
 		tree.onClick = function(e, _) {
@@ -621,7 +577,6 @@ class SceneEditor {
 
 
 	public function refresh( ?mode: RefreshMode, ?callb: Void->Void) {
 	public function refresh( ?mode: RefreshMode, ?callb: Void->Void) {
 		if(mode == null || mode == Full) refreshScene();
 		if(mode == null || mode == Full) refreshScene();
-		refreshFavs();
 		refreshTree(callb);
 		refreshTree(callb);
 	}
 	}
 
 
@@ -641,10 +596,6 @@ class SceneEditor {
 		});
 		});
 	}
 	}
 
 
-	function refreshFavs() {
-		favTree.refresh();
-	}
-
 	function refreshProps() {
 	function refreshProps() {
 		selectElements(curEdit.elements, Nothing);
 		selectElements(curEdit.elements, Nothing);
 	}
 	}
@@ -1213,7 +1164,6 @@ class SceneEditor {
 		var obj3d  = p.to(Object3D);
 		var obj3d  = p.to(Object3D);
 		el.toggleClass("disabled", !p.enabled);
 		el.toggleClass("disabled", !p.enabled);
 		var aEl = el.find("a").first();
 		var aEl = el.find("a").first();
-		aEl.toggleClass("favorite", isFavorite(p));
 
 
 		var tag = getTag(p);
 		var tag = getTag(p);
 
 
@@ -1233,7 +1183,7 @@ class SceneEditor {
 
 
 			var visTog = el.find(".visibility-toggle").first();
 			var visTog = el.find(".visibility-toggle").first();
 			if(visTog.length == 0) {
 			if(visTog.length == 0) {
-				visTog = new Element('<i class="fa fa-eye visibility-toggle"/>').insertAfter(el.find("a.jstree-anchor").first());
+				visTog = new Element('<i class="fa fa-eye visibility-toggle" title = "Hide (${view.config.get("key.sceneeditor.hide")})"/>').insertAfter(el.find("a.jstree-anchor").first());
 				visTog.click(function(e) {
 				visTog.click(function(e) {
 					if(curEdit.elements.indexOf(obj3d) >= 0)
 					if(curEdit.elements.indexOf(obj3d) >= 0)
 						setVisible(curEdit.elements, isHidden(obj3d));
 						setVisible(curEdit.elements, isHidden(obj3d));
@@ -1886,26 +1836,6 @@ class SceneEditor {
 	function saveDisplayState() {
 	function saveDisplayState() {
 		var state = [for (h in hideList.keys()) h.getAbsPath(true)];
 		var state = [for (h in hideList.keys()) h.getAbsPath(true)];
 		@:privateAccess view.saveDisplayState("hideList", state);
 		@:privateAccess view.saveDisplayState("hideList", state);
-		var state = [for(f in favorites) f.getAbsPath(true)];
-		@:privateAccess view.saveDisplayState("favorites", state);
-	}
-
-	public function isFavorite(e: PrefabElement) {
-		return favorites.indexOf(e) >= 0;
-	}
-
-	public function setFavorite(e: PrefabElement, fav: Bool) {
-		if(fav && !isFavorite(e))
-			favorites.push(e);
-		else if(!fav && isFavorite(e))
-			favorites.remove(e);
-
-		var el = tree.getElement(e);
-		if(el != null)
-			applyTreeStyle(e, el);
-
-		refreshFavs();
-		saveDisplayState();
 	}
 	}
 
 
 	public function setVisible(elements : Array<PrefabElement>, visible: Bool) {
 	public function setVisible(elements : Array<PrefabElement>, visible: Bool) {
@@ -2254,8 +2184,15 @@ class SceneEditor {
 			}
 			}
 		}
 		}
 
 
+		var grecent = [];
 		var groups = [];
 		var groups = [];
 		var gother = [];
 		var gother = [];
+		var recents : Array<String> = ide.currentConfig.get("sceneeditor.newrecents", []);
+		for( g in recents) {
+			grecent.push(getNewTypeMenuItem(g, parent, onMake));
+
+		}
+
 		for( g in (view.config.get("sceneeditor.newgroups") : Array<String>) ) {
 		for( g in (view.config.get("sceneeditor.newgroups") : Array<String>) ) {
 			var parts = g.split("|");
 			var parts = g.split("|");
 			var cl : Dynamic = Type.resolveClass(parts[1]);
 			var cl : Dynamic = Type.resolveClass(parts[1]);
@@ -2308,6 +2245,9 @@ class SceneEditor {
 				return gother;
 				return gother;
 			newItems.push({ label : "Other", menu : gother });
 			newItems.push({ label : "Other", menu : gother });
 		}
 		}
+
+		newItems.unshift({label : "Recents", menu : grecent});
+
 		return newItems;
 		return newItems;
 	}
 	}
 
 
@@ -2324,6 +2264,12 @@ class SceneEditor {
 					autoName(p);
 					autoName(p);
 					if(onMake != null)
 					if(onMake != null)
 						onMake(p);
 						onMake(p);
+					var recents : Array<String> = ide.currentConfig.get("sceneeditor.newrecents", []);
+					recents.remove(p.type);
+					recents.unshift(p.type);
+					var recentSize : Int = view.config.get("sceneeditor.recentsize");
+					if (recents.length > recentSize) recents.splice(recentSize, recents.length - recentSize);
+					ide.currentConfig.set("sceneeditor.newrecents", recents);
 					return p;
 					return p;
 				}
 				}
 
 
@@ -2331,6 +2277,12 @@ class SceneEditor {
 					ide.chooseFile(pmodel.inf.fileSource, function(path) {
 					ide.chooseFile(pmodel.inf.fileSource, function(path) {
 						var p = make(path);
 						var p = make(path);
 						addElements([p]);
 						addElements([p]);
+						var recents : Array<String> = ide.currentConfig.get("sceneeditor.newrecents", []);
+						recents.remove(p.type);
+						recents.unshift(p.type);
+						var recentSize : Int = view.config.get("sceneeditor.recentsize");
+						if (recents.length > recentSize) recents.splice(recentSize, recents.length - recentSize);
+						ide.currentConfig.set("sceneeditor.newrecents", recents);
 					});
 					});
 				else
 				else
 					addElements([make()]);
 					addElements([make()]);

+ 40 - 0
hide/comp/Toolbar.hx

@@ -1,5 +1,22 @@
 package hide.comp;
 package hide.comp;
 
 
+enum ToolType {
+	Button(click: Void->Void);
+	Toggle(toggle: Bool->Void);
+	Range(onChange: Float->Void);
+	Color(onChange: Int -> Void);
+	Menu(items: Array<hide.comp.ContextMenu.ContextMenuItem>);
+}
+
+typedef ToolDef = {
+	id: String,
+	title : String,
+	type : ToolType,
+	?icon : String,
+	?iconStyle: Dynamic,
+	?rightClick : Void -> Void,
+}
+
 typedef ToolToggle = {
 typedef ToolToggle = {
 	var element : Element;
 	var element : Element;
 	function toggle( v : Bool ) : Void;
 	function toggle( v : Bool ) : Void;
@@ -13,6 +30,12 @@ typedef ToolSelect<T> = {
 	dynamic function onSelect( v : T ) : Void;
 	dynamic function onSelect( v : T ) : Void;
 }
 }
 
 
+typedef ToolMenu<T> = {
+	var element : Element;
+	function setContent( elements : Array<hide.comp.ContextMenu.ContextMenuItem> ) : Void;
+	dynamic function onSelect( v : T ) : Void;
+}
+
 class Toolbar extends Component {
 class Toolbar extends Component {
 
 
 	public function new(?parent,?el) {
 	public function new(?parent,?el) {
@@ -89,6 +112,23 @@ class Toolbar extends Component {
 		return tool;
 		return tool;
 	}
 	}
 
 
+	public function addMenu<T>( icon : String, label : String ) : ToolMenu<T> {
+		var e = new Element('<div class="menu"><div class="icon fa fa-$icon"/>${label==null ? "" : label}</div>');
+		var menuItems : Array<hide.comp.ContextMenu.ContextMenuItem> = [];
+		var tool : ToolMenu<T> = {
+			element : e,
+			setContent : function(c) {
+					menuItems = c;
+			},
+			onSelect : function(_) {},
+		};
+		e.click(function(ev) if( ev.button == 0 ){
+			new hide.comp.ContextMenu(menuItems);
+		});
+		e.appendTo(element);
+		return tool;
+	}
+
 	public function addRange( label : String, onChange : Float -> Void, ?defValue = 0., min = 0., max = 1. ) {
 	public function addRange( label : String, onChange : Float -> Void, ?defValue = 0., min = 0., max = 1. ) {
 		var r = new hide.comp.Range(element,new Element('<input title="$label" type="range" min="$min" max="$max" value="$defValue">'));
 		var r = new hide.comp.Range(element,new Element('<input title="$label" type="range" min="$min" max="$max" value="$defValue">'));
 		r.onChange = function(_) onChange(r.value);
 		r.onChange = function(_) onChange(r.value);

+ 0 - 6
hide/comp/cdb/DataFiles.hx

@@ -219,15 +219,9 @@ class DataFiles {
 	public static function getAvailableTypes() {
 	public static function getAvailableTypes() {
 		var sheets = [];
 		var sheets = [];
 		var ide = Ide.inst;
 		var ide = Ide.inst;
-		var levelSheet = ide.database.getSheet(ide.currentConfig.get("l3d.cdbLevel", "level"));
 		for( s in ide.database.sheets )
 		for( s in ide.database.sheets )
 			if( s.props.dataFiles != null )
 			if( s.props.dataFiles != null )
 				sheets.push(s);
 				sheets.push(s);
-		if(levelSheet != null) {
-			for(c in levelSheet.columns)
-				if( c.type == TList )
-					sheets.push(levelSheet.getSub(c));
-		}
 		return sheets;
 		return sheets;
 	}
 	}
 
 

+ 11 - 0
hide/ui/Keys.hx

@@ -77,6 +77,17 @@ class Keys {
 		keys.set(name, callb);
 		keys.set(name, callb);
 	}
 	}
 
 
+	public static function getKeys( eventName : String, config : Config ) {
+		var keyCode : String = config.get("key." + eventName);
+		if( keyCode == null )
+			return null;
+		var splitKeys = keyCode.split('-');
+		return {
+			key: splitKeys[splitKeys.length - 1],
+			modifiers: [for( i in 0...(splitKeys.length - 1)) splitKeys[i]].join("+"),
+		};
+	}
+
 	public static function get( e : Element ) : Keys {
 	public static function get( e : Element ) : Keys {
 		return Reflect.field(e[0], "__keys");
 		return Reflect.field(e[0], "__keys");
 	}
 	}

+ 1 - 7
hide/view/FXEditor.hx

@@ -244,11 +244,7 @@ class FXEditor extends FileView {
 						</div>
 						</div>
 					</div>
 					</div>
 					<div class="hide-scene-outliner">
 					<div class="hide-scene-outliner">
-						<div class="favorites" style="height:20%;">
-							<label>Favorites</label>
-							<div class="favorites-tree"></div>
-						</div>
-						<div style="height:80%;" class="flex vertical">
+						<div class="flex vertical">
 							<div class="hide-toolbar" style="zoom: 80%">
 							<div class="hide-toolbar" style="zoom: 80%">
 								<div class="button collapse-btn" title="Collapse all">
 								<div class="button collapse-btn" title="Collapse all">
 									<div class="icon fa fa-reply-all"></div>
 									<div class="icon fa fa-reply-all"></div>
@@ -275,7 +271,6 @@ class FXEditor extends FileView {
 		tabs = new hide.comp.Tabs(null,element.find(".tabs"));
 		tabs = new hide.comp.Tabs(null,element.find(".tabs"));
 		sceneEditor = new FXSceneEditor(this, data);
 		sceneEditor = new FXSceneEditor(this, data);
 		element.find(".hide-scenetree").first().append(sceneEditor.tree.element);
 		element.find(".hide-scenetree").first().append(sceneEditor.tree.element);
-		element.find(".favorites-tree").first().append(sceneEditor.favTree.element);
 		element.find(".hide-scroll").first().append(sceneEditor.properties.element);
 		element.find(".hide-scroll").first().append(sceneEditor.properties.element);
 		element.find(".heaps-scene").first().append(sceneEditor.scene.element);
 		element.find(".heaps-scene").first().append(sceneEditor.scene.element);
 		element.resize(function(e) {
 		element.resize(function(e) {
@@ -314,7 +309,6 @@ class FXEditor extends FileView {
 
 
 		currentVersion = undo.currentID;
 		currentVersion = undo.currentID;
 		sceneEditor.tree.element.addClass("small");
 		sceneEditor.tree.element.addClass("small");
-		sceneEditor.favTree.element.addClass("small");
 
 
 		var timeline = element.find(".timeline");
 		var timeline = element.find(".timeline");
 		var sMin = 0.0;
 		var sMin = 0.0;

+ 761 - 69
hide/view/Prefab.hx

@@ -1,59 +1,290 @@
 package hide.view;
 package hide.view;
+using Lambda;
 
 
-import hrt.prefab.Prefab in PrefabElement;
+import hxd.Math;
+import hxd.Key as K;
+
+import hrt.prefab.Prefab as PrefabElement;
+import hrt.prefab.Object3D;
+import hrt.prefab.l3d.Instance;
+import hide.comp.cdb.DataFiles;
+
+
+@:access(hide.view.Prefab)
+class CamController extends h3d.scene.CameraController {
+	public var groundSnapAngle = hxd.Math.degToRad(30);
+	var prefab : Prefab;
+	var startPush : h2d.col.Point;
+	var moveCount = 0;
+
+	public function new(parent, prefab) {
+		super(null, parent);
+		this.prefab = prefab;
+	}
+
+	override function onEvent( e : hxd.Event ) {
+		if(curPos == null) return;
+		switch( e.kind ) {
+		case EWheel:
+			zoom(e.wheelDelta);
+		case EPush:
+			pushing = e.button;
+			pushTime = haxe.Timer.stamp();
+			pushStartX = pushX = e.relX;
+			pushStartY = pushY = e.relY;
+			startPush = new h2d.col.Point(pushX, pushY);
+			if( pushing == 2 ) {
+				var se = prefab.sceneEditor;
+				var selection = se.getSelection();
+				var angle = hxd.Math.abs(Math.PI/2 - phi);
+				if( selection.length == 0 && angle > groundSnapAngle ) {
+					var visGround = se.screenToGround(se.scene.s2d.width / 2, se.scene.s2d.height / 2);
+					var dist = se.screenDistToGround(se.scene.s2d.width / 2, se.scene.s2d.height / 2);
+					if( dist != null ) {
+						set(dist, null, null, visGround);
+					}
+				}
+			}
+			moveCount = 0;
+			@:privateAccess scene.window.mouseLock = true;
+		case ERelease, EReleaseOutside:
+			if( pushing == e.button ) {
+				pushing = -1;
+				startPush = null;
+				if( e.kind == ERelease && haxe.Timer.stamp() - pushTime < 0.2 && hxd.Math.distance(e.relX - pushStartX,e.relY - pushStartY) < 5 )
+					onClick(e);
+				@:privateAccess scene.window.mouseLock = false;
+			}
+		case EMove:
+			// Windows bug that jumps movementX/Y on all browsers
+			if( moveCount < 10 && Math.distanceSq(pushX - e.relX, pushY - e.relY) > 100000 ) {
+				pushX = e.relX;
+				pushY = e.relY;
+				return;
+			}
+			moveCount++;
+
+			switch( pushing ) {
+			case 1:
+				if(startPush != null && startPush.distance(new h2d.col.Point(e.relX, e.relY)) > 3) {
+					var angle = hxd.Math.abs(Math.PI/2 - phi);
+					if(K.isDown(K.SHIFT) || angle < groundSnapAngle) {
+						var m = 0.001 * curPos.x * panSpeed / 25;
+						pan(-(e.relX - pushX) * m, (e.relY - pushY) * m);
+					}
+					else {
+						var se = prefab.sceneEditor;
+						var fromPt = se.screenToGround(pushX, pushY);
+						var toPt = se.screenToGround(e.relX, e.relY);
+						if(fromPt == null || toPt == null)
+							return;
+						var delta = toPt.sub(fromPt).toVector();
+						delta.w = 0;
+						targetOffset = targetOffset.sub(delta);
+					}
+				}
+				pushX = e.relX;
+				pushY = e.relY;
+			case 2:
+				rot(e.relX - pushX, e.relY - pushY);
+				pushX = e.relX;
+				pushY = e.relY;
+			default:
+			}
+		case EFocus:
+			@:privateAccess scene.window.mouseLock = false;
+		default:
+		}
+	}
+
+	function moveKeys() {
+		var mov = new h3d.Vector();
+		if( K.isDown(K.UP) || K.isDown(K.Z) || K.isDown(K.W) )
+			mov.x += 1;
+		if( K.isDown(K.DOWN) || K.isDown(K.S) )
+			mov.x -= 1;
+		if( K.isDown(K.LEFT) || K.isDown(K.Q) || K.isDown(K.A) )
+			mov.y -= 1;
+		if( K.isDown(K.RIGHT) || K.isDown(K.D) )
+			mov.y += 1;
+
+		if( mov.x == 0 && mov.y == 0 )
+			return;
+		var dir = new h3d.Vector(
+			mov.x * Math.cos(theta) + mov.y * Math.cos(Math.PI / 2 + theta),
+			mov.x * Math.sin(theta) + mov.y * Math.sin(Math.PI / 2 + theta)
+		);
+		var moveSpeed = Ide.inst.currentConfig.get("sceneeditor.camera.moveSpeed", 1.5);
+
+		var delta = dir.multiply(0.01 * moveSpeed * (distance + scene.camera.zNear));
+		delta.w = 0;
+		targetOffset = targetOffset.sub(delta);
+	}
+
+	override function sync(ctx : h3d.scene.RenderContext) {
+		if( pushing == 2 ) {
+			moveKeys();
+		}
+		super.sync(ctx);
+	}
+}
 
 
 @:access(hide.view.Prefab)
 @:access(hide.view.Prefab)
 private class PrefabSceneEditor extends hide.comp.SceneEditor {
 private class PrefabSceneEditor extends hide.comp.SceneEditor {
 	var parent : Prefab;
 	var parent : Prefab;
+
 	public function new(view, data) {
 	public function new(view, data) {
 		super(view, data);
 		super(view, data);
 		parent = cast view;
 		parent = cast view;
+		this.localTransform = false; // TODO: Expose option
 	}
 	}
-	override function onSceneReady() {
-		super.onSceneReady();
-		parent.onSceneReady();
+
+	override function makeCamController() {
+		var c = new CamController(scene.s3d, parent);
+		c.friction = 0.9;
+		c.panSpeed = 0.6;
+		c.zoomAmount = 1.05;
+		c.smooth = 0.7;
+		c.minDistance = 1;
+		return c;
 	}
 	}
+
+	override function refresh(?mode, ?callback) {
+		parent.onRefresh();
+		super.refresh(mode, callback);
+	}
+
 	override function update(dt) {
 	override function update(dt) {
 		super.update(dt);
 		super.update(dt);
 		parent.onUpdate(dt);
 		parent.onUpdate(dt);
 	}
 	}
-	override function refresh(?mode, ?callb:Void->Void) {
-		refreshScene();
-		refreshTree(callb);
+
+	override function onSceneReady() {
+		super.onSceneReady();
+		parent.onSceneReady();
+	}
+
+	override function applyTreeStyle(p: PrefabElement, el: Element, ?pname: String) {
+		super.applyTreeStyle(p, el, pname);
+		parent.applyTreeStyle(p, el, pname);
+	}
+
+	override function applySceneStyle(p:PrefabElement) {
+		parent.applySceneStyle(p);
+	}
+
+	override function onPrefabChange(p: PrefabElement, ?pname: String) {
+		super.onPrefabChange(p, pname);
+		parent.onPrefabChange(p, pname);
+	}
+
+	override function getNewContextMenu(current: PrefabElement, ?onMake: PrefabElement->Void=null, ?groupByType = true ) {
+		var newItems = super.getNewContextMenu(current, onMake, groupByType);
+
+		function setup(p : PrefabElement) {
+			autoName(p);
+			haxe.Timer.delay(addElements.bind([p]), 0);
+		}
+
+		function addNewInstances() {
+			var items = new Array<hide.comp.ContextMenu.ContextMenuItem>();
+			for(type in DataFiles.getAvailableTypes() ) {
+				var typeId = DataFiles.getTypeName(type);
+				var label = typeId.charAt(0).toUpperCase() + typeId.substr(1);
+
+				var refCols = Instance.findRefColumns(type);
+				var refSheet = refCols == null ? null : type.base.getSheet(refCols.sheet);
+				var idCol = refCols == null ? null : Instance.findIDColumn(refSheet);
+
+				function make(name) {
+					var p = new Instance(current == null ? sceneData : current);
+					p.name = name;
+					p.props = makeCdbProps(p, type);
+					setup(p);
+					if(onMake != null)
+						onMake(p);
+					return p;
+				}
+
+				if(idCol != null && refSheet.props.dataFiles == null ) {
+					var kindItems = new Array<hide.comp.ContextMenu.ContextMenuItem>();
+					for(line in refSheet.lines) {
+						var kind : String = Reflect.getProperty(line, idCol.name);
+						kindItems.push({
+							label : kind,
+							click : function() {
+								var p = make(kind.charAt(0).toLowerCase() + kind.substr(1));
+								var obj : Dynamic = p.props;
+								for( c in refCols.cols ) {
+									if( c == refCols.cols[refCols.cols.length-1] )
+										Reflect.setField(obj, c.name, kind);
+									else {
+										var s = Reflect.field(obj,c.name);
+										if( s == null ) {
+											s = {};
+											Reflect.setField(obj, c.name, s);
+										}
+										obj = s;
+									}
+								}
+							}
+						});
+					}
+					items.unshift({
+						label : label,
+						menu: kindItems
+					});
+				}
+				else {
+					items.push({
+						label : label,
+						click : make.bind(typeId)
+					});
+				}
+			}
+			newItems.unshift({
+				label : "Instance",
+				menu: items
+			});
+		};
+		addNewInstances();
+		return newItems;
+	}
+
+	override function getAvailableTags(p:PrefabElement) {
+		return cast ide.currentConfig.get("sceneeditor.tags");
 	}
 	}
 }
 }
 
 
 class Prefab extends FileView {
 class Prefab extends FileView {
 
 
-	var sceneEditor : PrefabSceneEditor;
+	public var sceneEditor : PrefabSceneEditor;
 	var data : hrt.prefab.Library;
 	var data : hrt.prefab.Library;
-	var tabs : hide.comp.Tabs;
 
 
 	var tools : hide.comp.Toolbar;
 	var tools : hide.comp.Toolbar;
-	var light : h3d.scene.fwd.DirLight;
-	var lightDirection = new h3d.Vector( 1, 2, -4 );
 
 
+	var layerToolbar : hide.comp.Toolbar;
+	var layerButtons : Map<PrefabElement, hide.comp.Toolbar.ToolToggle>;
 
 
-	var scene(get, null):  hide.comp.Scene;
-	function get_scene() return sceneEditor.scene;
-	var properties(get, null):  hide.comp.PropsEditor;
-	function get_properties() return sceneEditor.properties;
+	var grid : h3d.scene.Graphics;
+
+	var gridStep : Int;
+	var gridSize : Int;
+	var showGrid = false;
 
 
 	// autoSync
 	// autoSync
 	var autoSync : Bool;
 	var autoSync : Bool;
 	var currentVersion : Int = 0;
 	var currentVersion : Int = 0;
 	var lastSyncChange : Float = 0.;
 	var lastSyncChange : Float = 0.;
+	var sceneFilters : Map<String, Bool>;
+	var graphicsFilters : Map<String, Bool>;
+	var statusText : h2d.Text;
+	var posToolTip : h2d.Text;
 
 
-	override function getDefaultContent() {
-		return haxe.io.Bytes.ofString(ide.toJSON(new hrt.prefab.Library().saveData()));
-	}
-
-	override function save() {
-		var content = ide.toJSON(data.saveData());
-		currentSign = haxe.crypto.Md5.encode(content);
-		sys.io.File.saveContent(getPath(), content);
-		super.save();
-	}
+	var scene(get, null):  hide.comp.Scene;
+	function get_scene() return sceneEditor.scene;
+	public var properties(get, null):  hide.comp.PropsEditor;
+	function get_properties() return sceneEditor.properties;
 
 
 	override function onDisplay() {
 	override function onDisplay() {
 		if( sceneEditor != null ) sceneEditor.dispose();
 		if( sceneEditor != null ) sceneEditor.dispose();
@@ -64,76 +295,537 @@ class Prefab extends FileView {
 		currentSign = haxe.crypto.Md5.encode(content);
 		currentSign = haxe.crypto.Md5.encode(content);
 
 
 		element.html('
 		element.html('
-			<div class="flex vertical prefabview">
-				<div class="toolbar"></div>
-				<div class="flex-elt">
-					<div class="heaps-scene">
-					</div>
-					<div class="tabs">
-						<div class="tab expand" name="Scene" icon="sitemap">
-							<div class="hide-block">
-								<div class="hide-list scenetree">
+			<div class="flex vertical">
+				<div style="flex: 0 0 30px;">
+					<span class="tools-buttons"></span>
+				</div>
+
+				<div class="scene-partition" style="display: flex; flex-direction: row; flex: 1; overflow: hidden;">
+					<div class="heaps-scene"></div>
+
+					<div class="tree-column">
+						<div class="flex vertical">
+							<div class="hide-toolbar">
+								<div class="toolbar-label">
+									<div class="icon fa fa-sitemap"></div>
+									Scene
+								</div>
+								<div class="button collapse-btn" title="Collapse all">
+									<div class="icon fa fa-reply-all"></div>
+								</div>
+
+								<div class="button combine-btn layout-btn" title="Toggle columns layout">
+									<div class="icon fa fa-compress"></div>
 								</div>
 								</div>
+								<div class="button separate-btn layout-btn" title="Toggle columns layout">
+									<div class="icon fa fa-expand"></div>
+								</div>
+
+								<div
+									class="button hide-cols-btn close-btn"
+									title="Hide Tree & Props (${config.get("key.sceneeditor.toggleLayout")})"
+								>
+									<div class="icon fa fa-chevron-right"></div>
+								</div>
+							</div>
+
+							<div class="hide-scenetree"></div>
+						</div>
+					</div>
+
+					<div class="props-column">
+						<div class="hide-toolbar">
+							<div class="toolbar-label">
+								<div class="icon fa fa-sitemap"></div>
+								Properties
 							</div>
 							</div>
 						</div>
 						</div>
+							<div class="hide-scroll"></div>
+					</div>
+
+					<div
+						class="button show-cols-btn close-btn"
+						title="Show Tree & Props (${config.get("key.sceneeditor.toggleLayout")})"
+					>
+						<div class="icon fa fa-chevron-left"></div>
 					</div>
 					</div>
 				</div>
 				</div>
 			</div>
 			</div>
 		');
 		');
-		tools = new hide.comp.Toolbar(null,element.find(".toolbar"));
-		tabs = new hide.comp.Tabs(null,element.find(".tabs"));
-		sceneEditor = new PrefabSceneEditor(this, data);
-		element.find(".scenetree").first().append(sceneEditor.tree.element);
-		element.find(".tab").first().append(sceneEditor.properties.element);
-		element.find(".heaps-scene").first().append(sceneEditor.scene.element);
+
+		tools = new hide.comp.Toolbar(null,element.find(".tools-buttons"));
+		layerToolbar = new hide.comp.Toolbar(null,element.find(".layer-buttons"));
 		currentVersion = undo.currentID;
 		currentVersion = undo.currentID;
+
+		sceneEditor = new PrefabSceneEditor(this, data);
+		element.find(".hide-scenetree").first().append(sceneEditor.tree.element);
+		element.find(".hide-scroll").first().append(properties.element);
+		element.find(".heaps-scene").first().append(scene.element);
+		sceneEditor.tree.element.addClass("small");
+
+		refreshColLayout();
+		element.find(".combine-btn").first().click((_) -> setCombine(true));
+		element.find(".separate-btn").first().click((_) -> setCombine(false));
+
+		element.find(".show-cols-btn").first().click(showColumns);
+		element.find(".hide-cols-btn").first().click(hideColumns);
+
+		element.find(".collapse-btn").click(function(e) {
+			sceneEditor.collapseTree();
+		});
+
+		keys.register("sceneeditor.toggleLayout", () -> {
+			if( element.find(".tree-column").first().css('display') == 'none' )
+				showColumns();
+			else
+				hideColumns();
+		});
+
+		refreshSceneFilters();
+		refreshGraphicsFilters();
 	}
 	}
 
 
-	public function onSceneReady() {
-		tabs.allowMask(scene);
-		light = sceneEditor.scene.s3d.find(function(o) return Std.downcast(o, h3d.scene.fwd.DirLight));
-		if( light == null ) {
-			light = new h3d.scene.fwd.DirLight(scene.s3d);
-			light.enableSpecular = true;
-		} else
-			light = null;
+	function refreshColLayout() {
+		var config = ide.ideConfig;
+		if( config.sceneEditorLayout == null ) {
+			config.sceneEditorLayout = {
+				colsVisible: true,
+				colsCombined: false,
+			};
+		}
+		setCombine(config.sceneEditorLayout.colsCombined);
 
 
-		tools.saveDisplayKey = "Prefab/tools";
+		if( config.sceneEditorLayout.colsVisible )
+			showColumns();
+		else
+			hideColumns();
+	}
+
+	override function onActivate() {
+		if( sceneEditor != null )
+			refreshColLayout();
+	}
+
+	function hideColumns(?_) {
+		element.find(".tree-column").first().hide();
+		element.find(".props-column").first().hide();
+		element.find(".show-cols-btn").first().show();
+		ide.ideConfig.sceneEditorLayout.colsVisible = false;
+		@:privateAccess ide.config.global.save();
+		@:privateAccess if( scene.window != null) scene.window.checkResize();
+	}
+
+	function showColumns(?_) {
+		element.find(".tree-column").first().show();
+		element.find(".props-column").first().show();
+		element.find(".show-cols-btn").first().hide();
+		ide.ideConfig.sceneEditorLayout.colsVisible = true;
+		@:privateAccess ide.config.global.save();
+		@:privateAccess if( scene.window != null) scene.window.checkResize();
+	}
 
 
-		tools.addToggle("arrows", "2D Camera", (b) -> sceneEditor.camera2D = b);
-		tools.addButton("video-camera", "Default camera", () -> sceneEditor.resetCamera());
+	function setCombine(val) {
+		var fullscene = element.find(".scene-partition").first();
+		var props = element.find(".props-column").first();
+		fullscene.toggleClass("reduced-columns", val);
+		if( val ) {
+			element.find(".hide-scenetree").first().parent().append(props);
+			element.find(".combine-btn").first().hide();
+			element.find(".separate-btn").first().show();
+		} else {
+			fullscene.append(props);
+			element.find(".combine-btn").first().show();
+			element.find(".separate-btn").first().hide();
+		}
+		ide.ideConfig.sceneEditorLayout.colsCombined = val;
+		@:privateAccess ide.config.global.save();
+		@:privateAccess if( scene.window != null) scene.window.checkResize();
+	}
 
 
-		tools.addColor("Background color", function(v) {
+	public function onSceneReady() {
+		tools.saveDisplayKey = "Prefab/toolbar";
+		statusText = new h2d.Text(hxd.res.DefaultFont.get(), scene.s2d);
+		statusText.setPosition(5, 5);
+		statusText.visible = false;
+		var toolsDefs = new Array<hide.comp.Toolbar.ToolDef>();
+		toolsDefs.push({id: "perspectiveCamera", title : "Perspective camera", icon : "video-camera", type : Button(() -> resetCamera(false)) });
+		toolsDefs.push({id: "topCamera", title : "Top camera", icon : "video-camera", iconStyle: { transform: "rotateZ(90deg)" }, type : Button(() -> resetCamera(true))});
+		toolsDefs.push({id: "snapToGroundToggle", title : "Snap to ground", icon : "anchor", type : Toggle((v) -> sceneEditor.snapToGround = v)});
+		toolsDefs.push({id: "localTransformsToggle", title : "Local transforms", icon : "compass", type : Toggle((v) -> sceneEditor.localTransform = v)});
+		toolsDefs.push({id: "gridToggle", title : "Toggle grid", icon : "th", type : Toggle((v) -> { showGrid = v; updateGrid(); }) });
+		toolsDefs.push({id: "bakeLights", title : "Bake lights", icon : "lightbulb-o", type : Button(() -> bakeLights()) });
+		var texContent : Element = null;
+		toolsDefs.push({id: "sceneInformationToggle", title : "Scene information", icon : "info-circle", type : Toggle((b) -> statusText.visible = b), rightClick: () -> {
+			if( texContent != null ) {
+				texContent.remove();
+				texContent = null;
+			}
+			new hide.comp.ContextMenu([
+				{
+					label : "Show Texture Details",
+					click : function() {
+						var memStats = scene.engine.mem.stats();
+						var texs = @:privateAccess scene.engine.mem.textures;
+						var list = [for(t in texs) {
+							n: '${t.width}x${t.height}  ${t.format}  ${t.name}',
+							size: t.width * t.height
+						}];
+						list.sort((a, b) -> Reflect.compare(b.size, a.size));
+						var content = new Element('<div tabindex="1" class="overlay-info"><h2>Scene info</h2><pre></pre></div>');
+						new Element(element[0].ownerDocument.body).append(content);
+						var pre = content.find("pre");
+						pre.text([for(l in list) l.n].join("\n"));
+						texContent = content;
+						content.blur(function(_) {
+							content.remove();
+							texContent = null;
+						});
+					}
+				}
+			]);
+		}});
+		toolsDefs.push({id: "autoSyncToggle", title : "Auto synchronize", icon : "refresh", type : Toggle((b) -> autoSync = b)});
+		toolsDefs.push({id: "backgroundColor", title : "Background Color", type : Color(function(v) {
 			scene.engine.backgroundColor = v;
 			scene.engine.backgroundColor = v;
-		}, scene.engine.backgroundColor);
-		tools.addToggle("refresh", "Auto synchronize", function(b) {
-			autoSync = b;
-		});
-		tools.addRange("Speed", function(v) {
-			scene.speed = v;
-		}, scene.speed);
+			updateGrid();
+		})});
+		toolsDefs.push({id: "graphicsFilters", title : "Graphics filters", type : Menu(filtersToMenuItem(graphicsFilters, "Graphics"))});
+		toolsDefs.push({id: "sceneFilters", title : "Scene filters", type : Menu(filtersToMenuItem(sceneFilters, "Scene"))});
+		toolsDefs.push({id: "sceneSpeed", title : "Speed", type : Range((v) -> scene.speed = v)});
+
+		for (tool in toolsDefs) {
+			var key = config.get("key.sceneeditor." + tool.id);
+			var shortcut = key != null ? " (" + key + ")" : "";
+			var el : Element = null;
+			switch(tool.type) {
+				case Button(f):
+					el = tools.addButton(tool.icon, tool.title + shortcut, f);
+				case Toggle(f):
+					var toggle = tools.addToggle(tool.icon, tool.title + shortcut, f);
+					el = toggle.element;
+					keys.register("sceneeditor." + tool.id, () -> toggle.toggle(!toggle.isDown()));
+					if (tool.rightClick != null)
+						toggle.rightClick(tool.rightClick);
+				case Color(f):
+					el = tools.addColor(tool.title, f).element;
+				case Range(f):
+					el = tools.addRange(tool.title, f, 1.).element;
+				case Menu(items):
+					var menu = tools.addMenu(tool.icon, tool.title);
+					menu.setContent(items);
+					el = menu.element;
+			}
+
+			el.addClass(tool.id);
+			if(tool.iconStyle != null)
+				el.find(".icon").css(tool.iconStyle);
+		}
+		posToolTip = new h2d.Text(hxd.res.DefaultFont.get(), scene.s2d);
+		posToolTip.dropShadow = { dx : 1, dy : 1, color : 0, alpha : 0.5 };
+
+		updateStats();
+		updateGrid();
+		initGraphicsFilters();
+	}
+
+	function updateStats() {
+		var memStats = scene.engine.mem.stats();
+		@:privateAccess
+		var lines : Array<String> = [
+			'Scene objects: ${scene.s3d.getObjectsCount()}',
+			'Interactives: ' + sceneEditor.interactives.count(),
+			'Contexts: ' + sceneEditor.context.shared.contexts.count(),
+			'Triangles: ${scene.engine.drawTriangles}',
+			'Buffers: ${memStats.bufferCount}',
+			'Textures: ${memStats.textureCount}',
+			'FPS: ${Math.round(scene.engine.realFps)}',
+			'Draw Calls: ${scene.engine.drawCalls}',
+		];
+		statusText.text = lines.join("\n");
+		haxe.Timer.delay(function() sceneEditor.event.wait(0.5, updateStats), 0);
+	}
+
+	function bakeLights() {
+		var curSel = sceneEditor.curEdit.elements;
+		sceneEditor.selectElements([]);
+		var passes = [];
+		for( m in scene.s3d.getMaterials() ) {
+			var s = m.getPass("shadow");
+			if( s != null && !s.isStatic ) passes.push(s);
+		}
+		for( p in passes )
+			p.isStatic = true;
+
+		function isDynamic(elt: hrt.prefab.Prefab) {
+			var p = elt;
+			while(p != null) {
+				if(p.name == "dynamic")
+					return true;
+				p = p.parent;
+			}
+			return false;
+		}
+
+		for(elt in data.flatten()) {
+			if(Std.is(elt, Instance) || isDynamic(elt)) {
+				var mats = sceneEditor.context.shared.getMaterials(elt);
+				for(mat in mats) {
+					var p = mat.getPass("shadow");
+					if(p != null)
+						p.isStatic = false;
+				}
+			}
+		}
+
+		scene.s3d.computeStatic();
+		for( p in passes )
+			p.isStatic = false;
+		var lights = data.getAll(hrt.prefab.Light);
+		for( l in lights ) {
+			if(!l.visible)
+				continue;
+			l.saveBaked(sceneEditor.context);
+		}
+		sceneEditor.selectElements(curSel);
+	}
+
+	function bakeVolumetricLightmaps(){
+		var volumetricLightmaps = data.getAll(hrt.prefab.vlm.VolumetricLightmap);
+		var total = 0;
+		for( v in volumetricLightmaps )
+			total += v.volumetricLightmap.getProbeCount();
+		if( total == 0 )
+			return;
+		if( !ide.confirm("Bake "+total+" probes?") )
+			return;
+		function bakeNext() {
+			var v = volumetricLightmaps.shift();
+			if( v == null ) {
+				ide.message("Done");
+				return;
+			}
+			v.startBake(sceneEditor.curEdit, bakeNext);
+			sceneEditor.selectElements([v]);
+		}
+		bakeNext();
+	}
+
+	function resetCamera( top : Bool ) {
+		var targetPt = new h3d.col.Point(0, 0, 0);
+		var curEdit = sceneEditor.curEdit;
+		if(curEdit != null && curEdit.rootObjects.length > 0) {
+			targetPt = curEdit.rootObjects[0].getAbsPos().getPosition().toPoint();
+		}
+		if(top)
+			sceneEditor.cameraController.set(200, Math.PI/2, 0.001, targetPt);
+		else
+			sceneEditor.cameraController.set(200, -4.7, 0.8, targetPt);
+		sceneEditor.cameraController.toTarget();
+	}
+
+	override function getDefaultContent() {
+		return haxe.io.Bytes.ofString(ide.toJSON(new hrt.prefab.Library().saveData()));
+	}
+
+	override function save() {
+		var content = ide.toJSON(data.saveData());
+		var newSign = haxe.crypto.Md5.encode(content);
+		if(newSign != currentSign)
+			haxe.Timer.delay(saveBackup.bind(content), 0);
+		currentSign = newSign;
+		sys.io.File.saveContent(getPath(), content);
+		super.save();
+	}
+
+	function updateGrid() {
+		if(grid != null) {
+			grid.remove();
+			grid = null;
+		}
+
+		if(!showGrid)
+			return;
+
+		grid = new h3d.scene.Graphics(scene.s3d);
+		grid.scale(1);
+		grid.material.mainPass.setPassName("debuggeom");
+		gridStep = ide.currentConfig.get("sceneeditor.gridStep");
+		gridSize = ide.currentConfig.get("sceneeditor.gridSize");
+
+		var col = h3d.Vector.fromColor(scene.engine.backgroundColor);
+		var hsl = col.toColorHSL();
+		if(hsl.z > 0.5) hsl.z -= 0.1;
+		else hsl.z += 0.1;
+		col.makeColor(hsl.x, hsl.y, hsl.z);
+
+		grid.lineStyle(1.0, col.toColor(), 1.0);
+		//width
+		for(i in 0...(hxd.Math.floor(gridSize / gridStep) + 1)) {
+			grid.moveTo(i * gridStep, 0, 0);
+			grid.lineTo(i * gridStep, gridSize, 0);
+
+			grid.moveTo(0, i * gridStep, 0);
+			grid.lineTo(gridSize, i * gridStep, 0);
+		}
+		grid.lineStyle(0);
+		grid.setPosition(-1 * gridSize / 2, -1 * gridSize / 2, 0);
 	}
 	}
 
 
 	function onUpdate(dt:Float) {
 	function onUpdate(dt:Float) {
-		var cam = scene.s3d.camera;
-		if( light != null ) {
-			var angle = Math.atan2(cam.target.y - cam.pos.y, cam.target.x - cam.pos.x);
-			light.setDirection(new h3d.Vector(
-				Math.cos(angle) * lightDirection.x - Math.sin(angle) * lightDirection.y,
-				Math.sin(angle) * lightDirection.x + Math.cos(angle) * lightDirection.y,
-				lightDirection.z
-			));
+		if(K.isDown(K.ALT)) {
+			posToolTip.visible = true;
+			var proj = sceneEditor.screenToGround(scene.s2d.mouseX, scene.s2d.mouseY);
+			posToolTip.text = proj != null ? '${Math.fmt(proj.x)}, ${Math.fmt(proj.y)}, ${Math.fmt(proj.z)}' : '???';
+			posToolTip.setPosition(scene.s2d.mouseX, scene.s2d.mouseY - 12);
+		}
+		else {
+			posToolTip.visible = false;
 		}
 		}
+
 		if( autoSync && (currentVersion != undo.currentID || lastSyncChange != properties.lastChange) ) {
 		if( autoSync && (currentVersion != undo.currentID || lastSyncChange != properties.lastChange) ) {
 			save();
 			save();
 			lastSyncChange = properties.lastChange;
 			lastSyncChange = properties.lastChange;
 			currentVersion = undo.currentID;
 			currentVersion = undo.currentID;
 		}
 		}
+
+	}
+
+	function onRefresh() {
 	}
 	}
 
 
 	override function onDragDrop(items : Array<String>, isDrop : Bool) {
 	override function onDragDrop(items : Array<String>, isDrop : Bool) {
-		return sceneEditor.onDragDrop(items,isDrop);
+		return sceneEditor.onDragDrop(items, isDrop);
+	}
+
+	function applyGraphicsFilters(typeid: String, enable: Bool)
+	{
+		saveDisplayState("graphicsFilters/" + typeid, enable);
+
+		var r : h3d.scene.Renderer = scene.s3d.renderer;
+
+		switch (typeid)
+		{
+		case "shadows":
+			r.shadows = enable;
+		default:
+		}
+	}
+
+	function applySceneFilter(typeid: String, visible: Bool) {
+		saveDisplayState("sceneFilters/" + typeid, visible);
+		var all = data.flatten(hrt.prefab.Prefab);
+		for(p in all) {
+			if(p.type == typeid || p.getCdbType() == typeid) {
+				sceneEditor.applySceneStyle(p);
+			}
+		}
+	}
+
+	function refreshSceneFilters() {
+		var filters : Array<String> = ide.currentConfig.get("sceneeditor.filterTypes");
+		filters = filters.copy();
+		for(sheet in DataFiles.getAvailableTypes()) {
+			filters.push(DataFiles.getTypeName(sheet));
+		}
+		sceneFilters = new Map();
+		for(f in filters) {
+			sceneFilters.set(f, getDisplayState("sceneFilters/" + f) != false);
+		}
+	}
+
+	function initGraphicsFilters() {
+		for (typeid in graphicsFilters.keys())
+		{
+			applyGraphicsFilters(typeid, graphicsFilters.get(typeid));
+		}
+	}
+
+	function refreshGraphicsFilters() {
+		var filters : Array<String> = ["shadows"];
+		filters = filters.copy();
+		graphicsFilters = new Map();
+		for(f in filters) {
+			graphicsFilters.set(f, getDisplayState("graphicsFilters/" + f) != false);
+		}
+	}
+
+	function filtersToMenuItem(filters : Map<String, Bool>, type : String) : Array<hide.comp.ContextMenu.ContextMenuItem> {
+		var content : Array<hide.comp.ContextMenu.ContextMenuItem> = [];
+		var initDone = false;
+		for(typeid in filters.keys()) {
+			content.push({label : typeid, checked : filters[typeid], click : function() {
+				var on = !filters[typeid];
+				filters.set(typeid, on);
+				if(initDone)
+					switch (type){
+						case "Graphics":
+							applyGraphicsFilters(typeid, on);
+						case "Scene":
+							applySceneFilter(typeid, on);
+					}
+					
+				content.find(function(item) return item.label == typeid).checked = on;
+			}});
+		}
+		initDone = true;
+		return content;
+	}
+
+	function applyTreeStyle(p: PrefabElement, el: Element, pname: String) {
 	}
 	}
 
 
-	static var _ = FileTree.registerExtension(Prefab,["prefab"],{ icon : "sitemap", createNew : "Prefab" });
+	function onPrefabChange(p: PrefabElement, ?pname: String) {
+
+	}
+
+	function applySceneStyle(p: PrefabElement) {
+		var prefabView = Std.downcast(p, hrt.prefab.Library); // don't use "to" (Reference)
+		if( prefabView != null && prefabView.parent == null ) {
+			updateGrid();
+			return;
+		}
+
+		var obj3d = p.to(Object3D);
+		if(obj3d != null) {
+			var visible = obj3d.visible && !sceneEditor.isHidden(obj3d) && sceneFilters.get(p.type) != false;
+			if(visible) {
+				var cdbType = p.getCdbType();
+				if(cdbType != null && sceneFilters.get(cdbType) == false)
+					visible = false;
+			}
+			for(ctx in sceneEditor.getContexts(obj3d)) {
+				ctx.local3d.visible = visible;
+			}
+		}
+		var color = getDisplayColor(p);
+		if(color != null){
+			color = (color & 0xffffff) | 0xa0000000;
+			var box = p.to(hrt.prefab.l3d.Box);
+			if(box != null) {
+				var ctx = sceneEditor.getContext(box);
+				box.setColor(ctx, color);
+			}
+			var poly = p.to(hrt.prefab.l3d.Polygon);
+			if(poly != null) {
+				var ctx = sceneEditor.getContext(poly);
+				poly.setColor(ctx, color);
+			}
+		}
+	}
+
+	function getDisplayColor(p: PrefabElement) : Null<Int> {
+		var typeId = p.getCdbType();
+		if(typeId != null) {
+			var colors = ide.currentConfig.get("sceneeditor.colors");
+			var color = Reflect.field(colors, typeId);
+			if(color != null) {
+				return Std.parseInt("0x"+color.substr(1)) | 0xff000000;
+			}
+		}
+		return null;
+	}
+
+	static var _ = FileTree.registerExtension(Prefab, ["prefab"], { icon : "sitemap", createNew : "Prefab" });
+	static var _1 = FileTree.registerExtension(Prefab, ["l3d"], { icon : "sitemap" });
+
 }
 }

+ 0 - 754
hide/view/l3d/Level3D.hx

@@ -1,754 +0,0 @@
-package hide.view.l3d;
-using Lambda;
-
-import hxd.Math;
-import hxd.Key as K;
-
-import hrt.prefab.Prefab as PrefabElement;
-import hrt.prefab.Object3D;
-import hrt.prefab.l3d.Instance;
-import h3d.scene.Object;
-import hide.comp.cdb.DataFiles;
-
-
-class LevelEditContext extends hide.prefab.EditContext {
-	public var parent : Level3D;
-	public function new(parent, context) {
-		super(context);
-		this.parent = parent;
-	}
-}
-
-@:access(hide.view.l3d.Level3D)
-class CamController extends h3d.scene.CameraController {
-	public var groundSnapAngle = hxd.Math.degToRad(30);
-	var level3d : Level3D;
-	var startPush : h2d.col.Point;
-	var moveCount = 0;
-
-	public function new(parent, level3d) {
-		super(null, parent);
-		this.level3d = level3d;
-	}
-
-	override function onEvent( e : hxd.Event ) {
-		if(curPos == null) return;
-		switch( e.kind ) {
-		case EWheel:
-			zoom(e.wheelDelta);
-		case EPush:
-			pushing = e.button;
-			pushTime = haxe.Timer.stamp();
-			pushStartX = pushX = e.relX;
-			pushStartY = pushY = e.relY;
-			startPush = new h2d.col.Point(pushX, pushY);
-			if( pushing == 2 ) {
-				var se = level3d.sceneEditor;
-				var selection = se.getSelection();
-				var angle = hxd.Math.abs(Math.PI/2 - phi);
-				if( selection.length == 0 && angle > groundSnapAngle ) {
-					var visGround = se.screenToGround(se.scene.s2d.width / 2, se.scene.s2d.height / 2);
-					var dist = se.screenDistToGround(se.scene.s2d.width / 2, se.scene.s2d.height / 2);
-					if( dist != null ) {
-						set(dist, null, null, visGround);
-					}
-				}
-			}
-			moveCount = 0;
-			@:privateAccess scene.window.mouseLock = true;
-		case ERelease, EReleaseOutside:
-			if( pushing == e.button ) {
-				pushing = -1;
-				startPush = null;
-				if( e.kind == ERelease && haxe.Timer.stamp() - pushTime < 0.2 && hxd.Math.distance(e.relX - pushStartX,e.relY - pushStartY) < 5 )
-					onClick(e);
-				@:privateAccess scene.window.mouseLock = false;
-			}
-		case EMove:
-			// Windows bug that jumps movementX/Y on all browsers
-			if( moveCount < 10 && Math.distanceSq(pushX - e.relX, pushY - e.relY) > 100000 ) {
-				pushX = e.relX;
-				pushY = e.relY;
-				return;
-			}
-			moveCount++;
-
-			switch( pushing ) {
-			case 1:
-				if(startPush != null && startPush.distance(new h2d.col.Point(e.relX, e.relY)) > 3) {
-					var angle = hxd.Math.abs(Math.PI/2 - phi);
-					if(K.isDown(K.SHIFT) || angle < groundSnapAngle) {
-						var m = 0.001 * curPos.x * panSpeed / 25;
-						pan(-(e.relX - pushX) * m, (e.relY - pushY) * m);
-					}
-					else {
-						var se = level3d.sceneEditor;
-						var fromPt = se.screenToGround(pushX, pushY);
-						var toPt = se.screenToGround(e.relX, e.relY);
-						if(fromPt == null || toPt == null)
-							return;
-						var delta = toPt.sub(fromPt).toVector();
-						delta.w = 0;
-						targetOffset = targetOffset.sub(delta);
-					}
-				}
-				pushX = e.relX;
-				pushY = e.relY;
-			case 2:
-				rot(e.relX - pushX, e.relY - pushY);
-				pushX = e.relX;
-				pushY = e.relY;
-			default:
-			}
-		case EFocus:
-			@:privateAccess scene.window.mouseLock = false;
-		default:
-		}
-	}
-
-	function moveKeys() {
-		var mov = new h3d.Vector();
-		if( K.isDown(K.UP) || K.isDown(K.Z) || K.isDown(K.W) )
-			mov.x += 1;
-		if( K.isDown(K.DOWN) || K.isDown(K.S) )
-			mov.x -= 1;
-		if( K.isDown(K.LEFT) || K.isDown(K.Q) || K.isDown(K.A) )
-			mov.y -= 1;
-		if( K.isDown(K.RIGHT) || K.isDown(K.D) )
-			mov.y += 1;
-
-		if( mov.x == 0 && mov.y == 0 )
-			return;
-		var dir = new h3d.Vector(
-			mov.x * Math.cos(theta) + mov.y * Math.cos(Math.PI / 2 + theta),
-			mov.x * Math.sin(theta) + mov.y * Math.sin(Math.PI / 2 + theta)
-		);
-		var moveSpeed = Ide.inst.currentConfig.get("l3d.camera.moveSpeed", 1.5);
-
-		var delta = dir.multiply(0.01 * moveSpeed * (distance + scene.camera.zNear));
-		delta.w = 0;
-		targetOffset = targetOffset.sub(delta);
-	}
-
-	override function sync(ctx : h3d.scene.RenderContext) {
-		if( pushing == 2 ) {
-			moveKeys();
-		}
-		super.sync(ctx);
-	}
-}
-
-@:access(hide.view.l3d.Level3D)
-private class Level3DSceneEditor extends hide.comp.SceneEditor {
-	var parent : Level3D;
-
-	public function new(view, data) {
-		super(view, data);
-		parent = cast view;
-		this.localTransform = false; // TODO: Expose option
-	}
-
-	override function makeCamController() {
-		var c = new CamController(scene.s3d, parent);
-		c.friction = 0.9;
-		c.panSpeed = 0.6;
-		c.zoomAmount = 1.05;
-		c.smooth = 0.7;
-		c.minDistance = 1;
-		return c;
-	}
-
-	override function refresh(?mode, ?callback) {
-		parent.onRefresh();
-		super.refresh(mode, callback);
-	}
-
-	override function update(dt) {
-		super.update(dt);
-		parent.onUpdate(dt);
-	}
-
-	override function onSceneReady() {
-		super.onSceneReady();
-		parent.onSceneReady();
-	}
-
-	override function applyTreeStyle(p: PrefabElement, el: Element, ?pname: String) {
-		super.applyTreeStyle(p, el, pname);
-		parent.applyTreeStyle(p, el, pname);
-	}
-
-	override function applySceneStyle(p:PrefabElement) {
-		parent.applySceneStyle(p);
-	}
-
-	override function onPrefabChange(p: PrefabElement, ?pname: String) {
-		super.onPrefabChange(p, pname);
-		parent.onPrefabChange(p, pname);
-	}
-
-	override function getNewContextMenu(current: PrefabElement, ?onMake: PrefabElement->Void=null, ?groupByType = true ) {
-		var newItems = super.getNewContextMenu(current, onMake, groupByType);
-
-		function setup(p : PrefabElement) {
-			autoName(p);
-			haxe.Timer.delay(addElements.bind([p]), 0);
-		}
-
-		function addNewInstances() {
-			var items = new Array<hide.comp.ContextMenu.ContextMenuItem>();
-			for(type in DataFiles.getAvailableTypes() ) {
-				var typeId = DataFiles.getTypeName(type);
-				var label = typeId.charAt(0).toUpperCase() + typeId.substr(1);
-
-				var refCols = Instance.findRefColumns(type);
-				var refSheet = refCols == null ? null : type.base.getSheet(refCols.sheet);
-				var idCol = refCols == null ? null : Instance.findIDColumn(refSheet);
-
-				function make(name) {
-					var p = new hrt.prefab.l3d.Instance(current == null ? sceneData : current);
-					p.name = name;
-					p.props = makeCdbProps(p, type);
-					setup(p);
-					if(onMake != null)
-						onMake(p);
-					return p;
-				}
-
-				if(idCol != null && refSheet.props.dataFiles == null ) {
-					var kindItems = new Array<hide.comp.ContextMenu.ContextMenuItem>();
-					for(line in refSheet.lines) {
-						var kind : String = Reflect.getProperty(line, idCol.name);
-						kindItems.push({
-							label : kind,
-							click : function() {
-								var p = make(kind.charAt(0).toLowerCase() + kind.substr(1));
-								var obj : Dynamic = p.props;
-								for( c in refCols.cols ) {
-									if( c == refCols.cols[refCols.cols.length-1] )
-										Reflect.setField(obj, c.name, kind);
-									else {
-										var s = Reflect.field(obj,c.name);
-										if( s == null ) {
-											s = {};
-											Reflect.setField(obj, c.name, s);
-										}
-										obj = s;
-									}
-								}
-							}
-						});
-					}
-					items.unshift({
-						label : label,
-						menu: kindItems
-					});
-				}
-				else {
-					items.push({
-						label : label,
-						click : make.bind(typeId)
-					});
-				}
-			}
-			newItems.unshift({
-				label : "Instance",
-				menu: items
-			});
-		};
-		addNewInstances();
-		return newItems;
-	}
-
-	override function getAvailableTags(p:PrefabElement) {
-		return cast ide.currentConfig.get("l3d.tags");
-	}
-}
-
-class Level3D extends FileView {
-
-	public var sceneEditor : Level3DSceneEditor;
-	var data : hrt.prefab.l3d.Level3D;
-	var tabs : hide.comp.Tabs;
-
-	var tools : hide.comp.Toolbar;
-
-	var levelProps : hide.comp.PropsEditor;
-
-	var layerToolbar : hide.comp.Toolbar;
-	var layerButtons : Map<PrefabElement, hide.comp.Toolbar.ToolToggle>;
-
-	var grid : h3d.scene.Graphics;
-	var curGridSize : Int;
-	var curGridWidth : Int;
-	var curGridHeight : Int;
-
-	var showGrid = false;
-	var currentVersion : Int = 0;
-	var lastSyncChange : Float = 0.;
-	var sceneFilters : Map<String, Bool>;
-	var graphicsFilters : Map<String, Bool>;
-	var statusText : h2d.Text;
-	var posToolTip : h2d.Text;
-
-	var scene(get, null):  hide.comp.Scene;
-	function get_scene() return sceneEditor.scene;
-	public var properties(get, null):  hide.comp.PropsEditor;
-	function get_properties() return sceneEditor.properties;
-
-	override function onDisplay() {
-		if( sceneEditor != null ) sceneEditor.dispose();
-
-		data = cast(hrt.prefab.Library.create("l3d"), hrt.prefab.l3d.Level3D);
-		var content = sys.io.File.getContent(getPath());
-		data.loadData(haxe.Json.parse(content));
-		currentSign = haxe.crypto.Md5.encode(content);
-
-		element.html('
-			<div class="flex vertical">
-				<div style="flex: 0 0 30px;">
-					<span class="tools-buttons"></span>
-					<span class="layer-buttons"></span>
-				</div>
-				<div style="display: flex; flex-direction: row; flex: 1; overflow: hidden;">
-					<div class="heaps-scene"></div>
-					<div class="hide-scene-outliner">
-						<div class="favorites" style="height:20%;">
-							<label>Favorites</label>
-							<div class="favorites-tree"></div>
-						</div>
-						<div style="height:80%;" class="flex vertical">
-							<div class="hide-toolbar" style="zoom: 80%">
-								<div class="button collapse-btn" title="Collapse all">
-									<div class="icon fa fa-reply-all"></div>
-								</div>
-							</div>
-							<div class="hide-scenetree"></div>
-						</div>
-					</div>
-					<div class="tabs">
-						<div class="tab expand" name="Scene" icon="sitemap">
-							<div class="hide-scroll"></div>
-						</div>
-						<div class="tab expand" name="Properties" icon="cog">
-							<div class="level-props"></div>
-						</div>
-					</div>
-				</div>
-			</div>
-		');
-		tools = new hide.comp.Toolbar(null,element.find(".tools-buttons"));
-		layerToolbar = new hide.comp.Toolbar(null,element.find(".layer-buttons"));
-		tabs = new hide.comp.Tabs(null,element.find(".tabs"));
-		currentVersion = undo.currentID;
-
-		levelProps = new hide.comp.PropsEditor(undo,null,element.find(".level-props"));
-		sceneEditor = new Level3DSceneEditor(this, data);
-		element.find(".hide-scenetree").first().append(sceneEditor.tree.element);
-		element.find(".favorites-tree").first().append(sceneEditor.favTree.element);
-		element.find(".hide-scroll").first().append(sceneEditor.properties.element);
-		element.find(".heaps-scene").first().append(sceneEditor.scene.element);
-		sceneEditor.tree.element.addClass("small");
-		sceneEditor.favTree.element.addClass("small");
-		element.find(".collapse-btn").click(function(e) {
-			sceneEditor.collapseTree();
-		});
-
-		// Level edit
-		{
-			var edit = new LevelEditContext(this, sceneEditor.context);
-			edit.properties = levelProps;
-			edit.scene = sceneEditor.scene;
-			edit.cleanups = [];
-			data.edit(edit);
-		}
-
-		refreshSceneFilters();
-		refreshGraphicsFilters();
-	}
-
-	public function onSceneReady() {
-		tabs.allowMask(scene);
-		tools.saveDisplayKey = "Level3D/toolbar";
-		tools.addButton("video-camera", "Perspective camera", () -> resetCamera(false));
-		tools.addButton("video-camera", "Top camera", () -> resetCamera(true)).find(".icon").css({transform: "rotateZ(90deg)"});
-		tools.addToggle("anchor", "Snap to ground", (v) -> sceneEditor.snapToGround = v, sceneEditor.snapToGround);
-		var localToggle = tools.addToggle("compass", "Local transforms", (v) -> sceneEditor.localTransform = v, sceneEditor.localTransform);
-		keys.register("sceneeditor.toggleLocal", () -> localToggle.toggle(!localToggle.isDown()));
-		var gridToggle = tools.addToggle("th", "Show grid", function(v) { showGrid = v; updateGrid(); }, showGrid);
-		keys.register("sceneeditor.toggleGrid", () -> gridToggle.toggle(!gridToggle.isDown()));
-		tools.addButton("sun-o", "Bake Lights", () -> bakeLights());
-		tools.addButton("map", "Bake Volumetric Lightmaps", () -> { bakeLights(); bakeVolumetricLightmaps(); });
-
-
-		statusText = new h2d.Text(hxd.res.DefaultFont.get(), scene.s2d);
-		statusText.setPosition(5, 5);
-		statusText.visible = false;
-		var texContent : Element = null;
-		tools.addToggle("info-circle", "Scene information", function(b) statusText.visible = b).rightClick(function() {
-			if( texContent != null ) {
-				texContent.remove();
-				texContent = null;
-			}
-			new hide.comp.ContextMenu([
-				{
-					label : "Show Texture Details",
-					click : function() {
-						var memStats = scene.engine.mem.stats();
-						var texs = @:privateAccess scene.engine.mem.textures;
-						var list = [for(t in texs) {
-							n: '${t.width}x${t.height}  ${t.format}  ${t.name}',
-							size: t.width * t.height
-						}];
-						list.sort((a, b) -> Reflect.compare(b.size, a.size));
-						var content = new Element('<div tabindex="1" class="overlay-info"><h2>Scene info</h2><pre></pre></div>');
-						new Element(element[0].ownerDocument.body).append(content);
-						var pre = content.find("pre");
-						pre.text([for(l in list) l.n].join("\n"));
-						texContent = content;
-						content.blur(function(_) {
-							content.remove();
-							texContent = null;
-						});
-					}
-				}
-			]);
-		});
-
-		tools.addColor("Background color", function(v) {
-			scene.engine.backgroundColor = v;
-			updateGrid();
-		}, scene.engine.backgroundColor);
-
-		posToolTip = new h2d.Text(hxd.res.DefaultFont.get(), scene.s2d);
-		posToolTip.dropShadow = { dx : 1, dy : 1, color : 0, alpha : 0.5 };
-
-		updateStats();
-		updateGrid();
-		initGraphicsFilters();
-	}
-
-	function updateStats() {
-		var memStats = scene.engine.mem.stats();
-		@:privateAccess
-		var lines : Array<String> = [
-			'Scene objects: ${scene.s3d.getObjectsCount()}',
-			'Interactives: ' + sceneEditor.interactives.count(),
-			'Contexts: ' + sceneEditor.context.shared.contexts.count(),
-			'Triangles: ${scene.engine.drawTriangles}',
-			'Buffers: ${memStats.bufferCount}',
-			'Textures: ${memStats.textureCount}',
-			'FPS: ${Math.round(scene.engine.realFps)}',
-			'Draw Calls: ${scene.engine.drawCalls}',
-		];
-		statusText.text = lines.join("\n");
-		haxe.Timer.delay(function() sceneEditor.event.wait(0.5, updateStats), 0);
-	}
-
-	function bakeLights() {
-		var curSel = sceneEditor.curEdit.elements;
-		sceneEditor.selectElements([]);
-		var passes = [];
-		for( m in scene.s3d.getMaterials() ) {
-			var s = m.getPass("shadow");
-			if( s != null && !s.isStatic ) passes.push(s);
-		}
-		for( p in passes )
-			p.isStatic = true;
-
-		function isDynamic(elt: hrt.prefab.Prefab) {
-			var p = elt;
-			while(p != null) {
-				if(p.name == "dynamic")
-					return true;
-				p = p.parent;
-			}
-			return false;
-		}
-
-		for(elt in data.flatten()) {
-			if(Std.is(elt, Instance) || isDynamic(elt)) {
-				var mats = sceneEditor.context.shared.getMaterials(elt);
-				for(mat in mats) {
-					var p = mat.getPass("shadow");
-					if(p != null)
-						p.isStatic = false;
-				}
-			}
-		}
-
-		scene.s3d.computeStatic();
-		for( p in passes )
-			p.isStatic = false;
-		var lights = data.getAll(hrt.prefab.Light);
-		for( l in lights ) {
-			if(!l.visible)
-				continue;
-			l.saveBaked(sceneEditor.context);
-		}
-		sceneEditor.selectElements(curSel);
-	}
-
-	function bakeVolumetricLightmaps(){
-		var volumetricLightmaps = data.getAll(hrt.prefab.vlm.VolumetricLightmap);
-		var total = 0;
-		for( v in volumetricLightmaps )
-			total += v.volumetricLightmap.getProbeCount();
-		if( total == 0 )
-			return;
-		if( !ide.confirm("Bake "+total+" probes?") )
-			return;
-		function bakeNext() {
-			var v = volumetricLightmaps.shift();
-			if( v == null ) {
-				ide.message("Done");
-				return;
-			}
-			v.startBake(sceneEditor.curEdit, bakeNext);
-			sceneEditor.selectElements([v]);
-		}
-		bakeNext();
-	}
-
-	function resetCamera( top : Bool ) {
-		var targetPt = new h3d.col.Point(0, 0, 0);
-		var curEdit = sceneEditor.curEdit;
-		if(curEdit != null && curEdit.rootObjects.length > 0) {
-			targetPt = curEdit.rootObjects[0].getAbsPos().getPosition().toPoint();
-		}
-		if(top)
-			sceneEditor.cameraController.set(200, Math.PI/2, 0.001, targetPt);
-		else
-			sceneEditor.cameraController.set(200, -4.7, 0.8, targetPt);
-		sceneEditor.cameraController.toTarget();
-	}
-
-	override function getDefaultContent() {
-		return haxe.io.Bytes.ofString(ide.toJSON(new hrt.prefab.l3d.Level3D().saveData()));
-	}
-
-	override function save() {
-		var content = ide.toJSON(data.saveData());
-		var newSign = haxe.crypto.Md5.encode(content);
-		if(newSign != currentSign)
-			haxe.Timer.delay(saveBackup.bind(content), 0);
-		currentSign = newSign;
-		sys.io.File.saveContent(getPath(), content);
-		super.save();
-	}
-
-	function updateGrid() {
-		if(grid != null) {
-			grid.remove();
-			grid = null;
-		}
-
-		if(!showGrid)
-			return;
-
-		grid = new h3d.scene.Graphics(scene.s3d);
-		grid.scale(1);
-		grid.material.mainPass.setPassName("debuggeom");
-		curGridSize = data.gridSize;
-		curGridWidth = data.width;
-		curGridHeight = data.height;
-
-		var col = h3d.Vector.fromColor(scene.engine.backgroundColor);
-		var hsl = col.toColorHSL();
-		if(hsl.z > 0.5) hsl.z -= 0.1;
-		else hsl.z += 0.1;
-		col.makeColor(hsl.x, hsl.y, hsl.z);
-
-		grid.lineStyle(1.0, col.toColor(), 1.0);
-		for(ix in 0... hxd.Math.floor(data.width / data.gridSize )+1) {
-			grid.moveTo(ix * data.gridSize, 0, 0);
-			grid.lineTo(ix * data.gridSize, data.height, 0);
-		}
-		for(iy in 0...  hxd.Math.floor(data.height / data.gridSize )+1) {
-			grid.moveTo(0, iy * data.gridSize, 0);
-			grid.lineTo(data.width, iy * data.gridSize, 0);
-		}
-		grid.lineStyle(0);
-	}
-
-	function onUpdate(dt:Float) {
-		if(K.isDown(K.ALT)) {
-			posToolTip.visible = true;
-			var proj = sceneEditor.screenToGround(scene.s2d.mouseX, scene.s2d.mouseY);
-			posToolTip.text = proj != null ? '${Math.fmt(proj.x)}, ${Math.fmt(proj.y)}, ${Math.fmt(proj.z)}' : '???';
-			posToolTip.setPosition(scene.s2d.mouseX, scene.s2d.mouseY - 12);
-		}
-		else {
-			posToolTip.visible = false;
-		}
-
-		if( curGridSize != data.gridSize || curGridWidth != data.width || curGridHeight != data.height ) {
-			updateGrid();
-		}
-
-	}
-
-	function onRefresh() {
-	}
-
-	override function onDragDrop(items : Array<String>, isDrop : Bool) {
-		return sceneEditor.onDragDrop(items, isDrop);
-	}
-
-	function applyGraphicsFilters(typeid: String, enable: Bool)
-	{
-		saveDisplayState("graphicsFilters/" + typeid, enable);
-
-		var r : h3d.scene.Renderer = scene.s3d.renderer;
-
-		switch (typeid)
-		{
-		case "shadows":
-			r.shadows = enable;
-		default:
-		}
-	}
-
-	function applySceneFilter(typeid: String, visible: Bool) {
-		saveDisplayState("sceneFilters/" + typeid, visible);
-		var all = data.flatten(hrt.prefab.Prefab);
-		for(p in all) {
-			if(p.type == typeid || p.getCdbType() == typeid) {
-				sceneEditor.applySceneStyle(p);
-			}
-		}
-	}
-
-	function refreshSceneFilters() {
-		var filters : Array<String> = ide.currentConfig.get("l3d.filterTypes");
-		filters = filters.copy();
-		for(sheet in DataFiles.getAvailableTypes()) {
-			filters.push(DataFiles.getTypeName(sheet));
-		}
-		sceneFilters = new Map();
-		for(f in filters) {
-			sceneFilters.set(f, getDisplayState("sceneFilters/" + f) != false);
-		}
-
-		if(layerButtons != null) {
-			for(b in layerButtons)
-				b.element.remove();
-		}
-		layerButtons = new Map<PrefabElement, hide.comp.Toolbar.ToolToggle>();
-		var initDone = false;
-		for(typeid in sceneFilters.keys()) {
-			var btn = layerToolbar.addToggle("", typeid, typeid.charAt(0).toLowerCase() + typeid.substr(1), function(on) {
-				sceneFilters.set(typeid, on);
-				if(initDone)
-					applySceneFilter(typeid, on);
-			});
-			if(sceneFilters.get(typeid) != false)
-				btn.toggle(true);
-		}
-		initDone = true;
-	}
-
-	function initGraphicsFilters() {
-		for (typeid in graphicsFilters.keys())
-		{
-			applyGraphicsFilters(typeid, graphicsFilters.get(typeid));
-		}
-	}
-
-	function refreshGraphicsFilters() {
-		var filters : Array<String> = ["shadows"];
-		filters = filters.copy();
-		graphicsFilters = new Map();
-		for(f in filters) {
-			graphicsFilters.set(f, getDisplayState("graphicsFilters/" + f) != false);
-		}
-		if(layerButtons != null) {
-			for(b in layerButtons)
-				b.element.remove();
-		}
-		layerButtons = new Map<PrefabElement, hide.comp.Toolbar.ToolToggle>();
-		var initDone = false;
-		for(typeid in graphicsFilters.keys()) {
-			var btn = layerToolbar.addToggle("", typeid, typeid.charAt(0).toLowerCase() + typeid.substr(1), function(on) {
-				graphicsFilters.set(typeid, on);
-				if (initDone)
-					applyGraphicsFilters(typeid, on);
-			});
-			if(graphicsFilters.get(typeid) != false)
-				btn.toggle(true);
-		}
-		initDone = true;
-	}
-
-	function applyTreeStyle(p: PrefabElement, el: Element, pname: String) {
-		/*
-		var styles = ide.currentConfig.get("l3d.treeStyles");
-		var style: Dynamic = null;
-		var typeId = getCdbTypeId(p);
-		if(typeId != null) {
-			style = Reflect.field(styles, typeId);
-		}
-		if(style == null) {
-			style = Reflect.field(styles, p.name);
-		}
-		var a = el.find("a").first();
-		a.addClass("crop");
-		if(style == null)
-			a.removeAttr("style");
-		else
-			a.css(style);
-			*/
-	}
-
-	function onPrefabChange(p: PrefabElement, ?pname: String) {
-
-	}
-
-	function applySceneStyle(p: PrefabElement) {
-		var level3d = Std.downcast(p, hrt.prefab.l3d.Level3D); // don't use "to" (Reference)
-		if(level3d != null) {
-			updateGrid();
-			return;
-		}
-		var obj3d = p.to(Object3D);
-		if(obj3d != null) {
-			var visible = obj3d.visible && !sceneEditor.isHidden(obj3d) && sceneFilters.get(p.type) != false;
-			if(visible) {
-				var cdbType = p.getCdbType();
-				if(cdbType != null && sceneFilters.get(cdbType) == false)
-					visible = false;
-			}
-			for(ctx in sceneEditor.getContexts(obj3d)) {
-				ctx.local3d.visible = visible;
-			}
-		}
-		var color = getDisplayColor(p);
-		if(color != null){
-			color = (color & 0xffffff) | 0xa0000000;
-			var box = p.to(hrt.prefab.l3d.Box);
-			if(box != null) {
-				var ctx = sceneEditor.getContext(box);
-				box.setColor(ctx, color);
-			}
-			var poly = p.to(hrt.prefab.l3d.Polygon);
-			if(poly != null) {
-				var ctx = sceneEditor.getContext(poly);
-				poly.setColor(ctx, color);
-			}
-		}
-	}
-
-	function getDisplayColor(p: PrefabElement) : Null<Int> {
-		var typeId = p.getCdbType();
-		if(typeId != null) {
-			var colors = ide.currentConfig.get("l3d.colors");
-			var color = Reflect.field(colors, typeId);
-			if(color != null) {
-				return Std.parseInt("0x"+color.substr(1)) | 0xff000000;
-			}
-		}
-		return null;
-	}
-
-	static var _ = FileTree.registerExtension(Level3D,["l3d"],{ icon : "sitemap", createNew : "Level3D" });
-}

+ 1 - 28
hrt/prefab/l3d/Level3D.hx

@@ -1,39 +1,12 @@
 package hrt.prefab.l3d;
 package hrt.prefab.l3d;
 
 
+@:deprecated("Use hrt.prefab.Library instead")
 class Level3D extends hrt.prefab.Library {
 class Level3D extends hrt.prefab.Library {
 
 
-	@:s public var width : Int;
-	@:s public var height : Int;
-	@:s public var gridSize : Int = 1;
-
 	public function new() {
 	public function new() {
 		super();
 		super();
 		type = "level3d";
 		type = "level3d";
-		width = height = 100;
-	}
-
-	#if editor
-
-	override function edit( ctx : EditContext ) {
-		var props = new hide.Element('
-			<div class="group" name="Level">
-					<dl>
-					<dt>Width</dt><dd><input type="number" value="0" field="width"/></dd>
-					<dt>Height</dt><dd><input type="number" value="0" field="height"/></dd>
-					<dt>Grid Size</dt><dd><input type="number" value="0" field="gridSize"/></dd>
-				</dl>
-			</div>
-		');
-		ctx.properties.add(props, this, function(pname) {
-			ctx.onChange(this, pname);
-		});
 	}
 	}
 
 
-	override function getHideProps() : HideProps {
-		return { icon : "cube", name : "Level3D", allowChildren : function(t) return Library.isOfType(t,Object3D) || t == "renderProps", allowParent: _ -> false};
-	}
-
-	#end
-
 	static var _ = Library.register("level3d", Level3D, "l3d");
 	static var _ = Library.register("level3d", Level3D, "l3d");
 }
 }

+ 1 - 1
libs/nw/MenuItem.hx

@@ -6,7 +6,7 @@ package nw;
 	var Separator = "separator";
 	var Separator = "separator";
 }
 }
 
 
-typedef MenuItemOptions = { label : String, ?icon : String, ?type : MenuItemType, ?submenu : Menu };
+typedef MenuItemOptions = { label : String, ?icon : String, ?type : MenuItemType, ?submenu : Menu, ?key : String, ?modifiers : String };
 
 
 extern class MenuItem {
 extern class MenuItem {