瀏覽代碼

WIP-stuff

Leonardo Jeanteur 4 年之前
父節點
當前提交
46f25703fa
共有 3 個文件被更改,包括 727 次插入874 次删除
  1. 17 15
      hide/comp/Toolbar.hx
  2. 710 76
      hide/view/Prefab.hx
  3. 0 783
      hide/view/l3d/Level3D.hx

+ 17 - 15
hide/comp/Toolbar.hx

@@ -1,21 +1,23 @@
 package hide.comp;
 package hide.comp;
+
 enum ToolType {
 enum ToolType {
 	Button;
 	Button;
 	Toggle;
 	Toggle;
 	Range;
 	Range;
 	Color;
 	Color;
 }
 }
+
 class ToolsObject {
 class ToolsObject {
-	static public var level3D : hide.view.l3d.Level3D;
+	static public var prefabView : hide.view.Prefab;
 	static var texContent : Element = null;
 	static var texContent : Element = null;
 	static public var tools : Map<String, {title : String, ?icon : String, type : ToolType, ?iconTransform : String, ?rightClick : Void -> Void, ?buttonFunction : Void -> Void, ?toggleFunction : Bool -> Void, ?rangeFunction : Float -> Void, ?colorFunction : Int -> Void}> = [
 	static public var tools : Map<String, {title : String, ?icon : String, type : ToolType, ?iconTransform : String, ?rightClick : Void -> Void, ?buttonFunction : Void -> Void, ?toggleFunction : Bool -> Void, ?rangeFunction : Float -> Void, ?colorFunction : Int -> Void}> = [
-		"perspectiveCamera" => {title : "Perspective camera", icon : "video-camera", type : Button, buttonFunction : () -> @:privateAccess level3D.resetCamera(false)},
-		"topCamera" => {title : "Top camera", icon : "video-camera", type : Button, iconTransform : "rotateZ(90deg)", buttonFunction : () -> @:privateAccess level3D.resetCamera(true)},
-		"snapToGroundToggle" => {title : "Snap to ground", icon : "anchor", type : Toggle, toggleFunction : (v) -> level3D.sceneEditor.snapToGround = v},
-		"localTransformsToggle"=> {title : "Local transforms", icon : "compass", type : Toggle, toggleFunction : (v) -> level3D.sceneEditor.localTransform = v},
-		"gridToggle" => {title : "Toggle grid", icon : "th", type : Toggle, toggleFunction : (v) -> { @:privateAccess level3D.showGrid = v; @:privateAccess level3D.updateGrid(); }},
-		"bakeLights" => {title : "Bake lights", icon : "lightbulb-o", type : Button, buttonFunction : () -> @:privateAccess level3D.bakeLights()},
-		"sceneeditor.sceneInformationToggle" => {title : "Scene information", icon : "info-circle", type : Toggle, toggleFunction : (b) -> @:privateAccess level3D.statusText.visible = b, rightClick : () -> {
+		"perspectiveCamera" => {title : "Perspective camera", icon : "video-camera", type : Button, buttonFunction : () -> @:privateAccess prefabView.resetCamera(false)},
+		"topCamera" => {title : "Top camera", icon : "video-camera", type : Button, iconTransform : "rotateZ(90deg)", buttonFunction : () -> @:privateAccess prefabView.resetCamera(true)},
+		"snapToGroundToggle" => {title : "Snap to ground", icon : "anchor", type : Toggle, toggleFunction : (v) -> prefabView.sceneEditor.snapToGround = v},
+		"localTransformsToggle"=> {title : "Local transforms", icon : "compass", type : Toggle, toggleFunction : (v) -> prefabView.sceneEditor.localTransform = v},
+		"gridToggle" => {title : "Toggle grid", icon : "th", type : Toggle, toggleFunction : (v) -> { @:privateAccess prefabView.showGrid = v; @:privateAccess prefabView.updateGrid(); }},
+		"bakeLights" => {title : "Bake lights", icon : "lightbulb-o", type : Button, buttonFunction : () -> @:privateAccess prefabView.bakeLights()},
+		"sceneeditor.sceneInformationToggle" => {title : "Scene information", icon : "info-circle", type : Toggle, toggleFunction : (b) -> @:privateAccess prefabView.statusText.visible = b, rightClick : () -> {
 			if( texContent != null ) {
 			if( texContent != null ) {
 				texContent.remove();
 				texContent.remove();
 				texContent = null;
 				texContent = null;
@@ -24,15 +26,15 @@ class ToolsObject {
 				{
 				{
 					label : "Show Texture Details",
 					label : "Show Texture Details",
 					click : function() {
 					click : function() {
-						var memStats = @:privateAccess level3D.scene.engine.mem.stats();
-						var texs = @:privateAccess level3D.scene.engine.mem.textures;
+						var memStats = @:privateAccess prefabView.scene.engine.mem.stats();
+						var texs = @:privateAccess prefabView.scene.engine.mem.textures;
 						var list = [for(t in texs) {
 						var list = [for(t in texs) {
 							n: '${t.width}x${t.height}  ${t.format}  ${t.name}',
 							n: '${t.width}x${t.height}  ${t.format}  ${t.name}',
 							size: t.width * t.height
 							size: t.width * t.height
 						}];
 						}];
 						list.sort((a, b) -> Reflect.compare(b.size, a.size));
 						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>');
 						var content = new Element('<div tabindex="1" class="overlay-info"><h2>Scene info</h2><pre></pre></div>');
-						new Element(@:privateAccess level3D.element[0].ownerDocument.body).append(content);
+						new Element(@:privateAccess prefabView.element[0].ownerDocument.body).append(content);
 						var pre = content.find("pre");
 						var pre = content.find("pre");
 						pre.text([for(l in list) l.n].join("\n"));
 						pre.text([for(l in list) l.n].join("\n"));
 						texContent = content;
 						texContent = content;
@@ -44,11 +46,11 @@ class ToolsObject {
 				}
 				}
 			]);
 			]);
 		}},
 		}},
-		"sceneeditor.autoSyncToggle" => {title : "Auto synchronize", icon : "refresh", type : Toggle, toggleFunction : (b) -> @:privateAccess level3D.autoSync = b},
+		"sceneeditor.autoSyncToggle" => {title : "Auto synchronize", icon : "refresh", type : Toggle, toggleFunction : (b) -> @:privateAccess prefabView.autoSync = b},
 		"sceneeditor.backgroundColor" => {title : "Background Color", type : Color, colorFunction :  function(v) {
 		"sceneeditor.backgroundColor" => {title : "Background Color", type : Color, colorFunction :  function(v) {
-			@:privateAccess level3D.scene.engine.backgroundColor = v;
-			@:privateAccess level3D.updateGrid();}},
-		"sceneeditor.sceneSpeed" => {title : "Speed", type : Range, rangeFunction : function(v) @:privateAccess level3D.scene.speed = v}
+			@:privateAccess prefabView.scene.engine.backgroundColor = v;
+			@:privateAccess prefabView.updateGrid();}},
+		"sceneeditor.sceneSpeed" => {title : "Speed", type : Range, rangeFunction : function(v) @:privateAccess prefabView.scene.speed = v}
 	];
 	];
 }
 }
 typedef ToolToggle = {
 typedef ToolToggle = {

+ 710 - 76
hide/view/Prefab.hx

@@ -1,58 +1,290 @@
 package hide.view;
 package hide.view;
+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 hide.comp.cdb.DataFiles;
 
 
-import hrt.prefab.Prefab in PrefabElement;
+
+@: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 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 layerToolbar : hide.comp.Toolbar;
+	var layerButtons : Map<PrefabElement, hide.comp.Toolbar.ToolToggle>;
+
+	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();
@@ -63,73 +295,335 @@ 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>
+					<span class="layer-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="Move to bottom of tree">
+									<div class="icon fa fa-compress"></div>
+								</div>
+								<div class="button separate-btn layout-btn" title="Move to own column">
+									<div class="icon fa fa-expand"></div>
+								</div>
+
+								<div class="button hide-cols-btn close-btn" title="Hide Tree & Props">
+									<div class="icon fa fa-chevron-right"></div>
 								</div>
 								</div>
 							</div>
 							</div>
+
+							<div class="hide-scenetree"></div>
 						</div>
 						</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 class="hide-scroll"></div>
+					</div>
+
+					<div class="button show-cols-btn close-btn" title="Show Tree & Props">
+						<div class="icon fa fa-chevron-left"></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();
+		});
+
+		refreshSceneFilters();
+		refreshGraphicsFilters();
 	}
 	}
 
 
-	public function onSceneReady() {
-		tabs.allowMask(scene);
+	function refreshColLayout() {
+		var config = ide.ideConfig;
+		if( config.sceneEditorLayout == null ) {
+			config.sceneEditorLayout = {
+				colsVisible: true,
+				colsCombined: false,
+			};
+		}
+		setCombine(config.sceneEditorLayout.colsCombined);
 
 
-		// TOMORROW this isn't in the Level3D view
-		{
-			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;
+		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();
+	}
+
+	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.saveDisplayKey = "Prefab/tools";
+	public function onSceneReady() {
+		tools.saveDisplayKey = "Prefab/toolbar";
+		statusText = new h2d.Text(hxd.res.DefaultFont.get(), scene.s2d);
+		statusText.setPosition(5, 5);
+		statusText.visible = false;
+		hide.comp.Toolbar.ToolsObject.prefabView = this;
+		var toolsNames : Iterator<String> = hide.comp.Toolbar.ToolsObject.tools.keys();
+		for (toolName in toolsNames) {
+			var tool = hide.comp.Toolbar.ToolsObject.tools.get(toolName);
+			if (tool != null) {
+				var shortcut = (config.get("key.sceneeditor." + toolName) != null)? " (" + config.get("key.sceneeditor." + toolName) + ")" : "";
+				switch(tool.type) {
+					case Button:
+						var button = tools.addButton(tool.icon, tool.title + shortcut, tool.buttonFunction);
+						if (tool.iconTransform != null) {
+							button.find(".icon").css({transform: tool.iconTransform});
+						}
+					case Toggle:
+						var toggle = tools.addToggle(tool.icon, tool.title + shortcut, tool.toggleFunction);
+						if (tool.iconTransform != null) {
+							toggle.element.find(".icon").css({transform: tool.iconTransform});
+						}
+						keys.register("sceneeditor." + toolName, () -> toggle.toggle(!toggle.isDown()));
+						if (tool.rightClick != null) {
+							toggle.rightClick(tool.rightClick);
+						}
+					case Color:
+						tools.addColor(tool.title, tool.colorFunction);
+					case Range:
+						tools.addRange(tool.title, tool.rangeFunction, 1.);
 
 
-		tools.addToggle("arrows", "2D Camera", (b) -> sceneEditor.camera2D = b);
-		tools.addButton("video-camera", "Default camera", () -> sceneEditor.resetCamera());
+				}
+			}
+		}
+		posToolTip = new h2d.Text(hxd.res.DefaultFont.get(), scene.s2d);
+		posToolTip.dropShadow = { dx : 1, dy : 1, color : 0, alpha : 0.5 };
 
 
-		tools.addColor("Background color", function(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);
+		updateStats();
+		updateGrid();
+		initGraphicsFilters();
 	}
 	}
 
 
-	function onUpdate(dt:Float) {
-		var cam = scene.s3d.camera;
+	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);
+	}
 
 
-		// TOMORROW this isn't in the Level3D view
-		{
-			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
-				));
+	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) {
+		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) ) {
@@ -137,12 +631,152 @@ class Prefab extends FileView {
 			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);
+		}
+
+		var sceneFiltersMenu = layerToolbar.addMenu("", "Scene filters");
+		var content : Array<hide.comp.ContextMenu.ContextMenuItem> = [];
+		var initDone = false;
+		for(typeid in sceneFilters.keys()) {
+			content.push({label : typeid, checked : sceneFilters[typeid], click : function() {
+				var on = !sceneFilters[typeid];
+				sceneFilters.set(typeid, on);
+				if(initDone)
+					applySceneFilter(typeid, on);
+				content.find(function(item) return item.label == typeid).checked = on;
+			}});
+		}
+		sceneFiltersMenu.setContent(content);
+		initDone = true;
+	}
+
+	function initGraphicsFilters() {
+		for (typeid in graphicsFilters.keys())
+		{
+			applyGraphicsFilters(typeid, graphicsFilters.get(typeid));
+		}
 	}
 	}
 
 
-	// TOMORROW Comment this? but then the transition breaks existing tabs
-	// static var _ = FileTree.registerExtension(Prefab,["prefab"],{ icon : "sitemap" });
+	function refreshGraphicsFilters() {
+		var filters : Array<String> = ["shadows"];
+		filters = filters.copy();
+		graphicsFilters = new Map();
+		for(f in filters) {
+			graphicsFilters.set(f, getDisplayState("graphicsFilters/" + f) != false);
+		}
+		var graphicsFiltersMenu = layerToolbar.addMenu("", "Graphics filters");
+		var content : Array<hide.comp.ContextMenu.ContextMenuItem> = [];
+		var initDone = false;
+		for(typeid in graphicsFilters.keys()) {
+			content.push({label : typeid, checked : graphicsFilters[typeid], click : function() {
+				var on = !graphicsFilters[typeid];
+				graphicsFilters.set(typeid, on);
+				if(initDone)
+					applyGraphicsFilters(typeid, on);
+				content.find(function(item) return item.label == typeid).checked = on;
+			}});
+		}
+		graphicsFiltersMenu.setContent(content);
+		initDone = true;
+	}
+
+	function applyTreeStyle(p: PrefabElement, el: Element, pname: String) {
+	}
+
+	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 - 783
hide/view/l3d/Level3D.hx

@@ -1,783 +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 hide.comp.cdb.DataFiles;
-
-
-@:access(hide.view.l3d.Level3D)
-class CamController extends h3d.scene.CameraController {
-	public var groundSnapAngle = hxd.Math.degToRad(30);
-	var prefab : Level3D;
-	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.l3d.Level3D)
-private class PrefabSceneEditor 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("sceneeditor.tags");
-	}
-}
-
-// TODO TOMORROW rename to Prefab
-class Level3D extends FileView {
-
-	public var sceneEditor : PrefabSceneEditor;
-	var data : hrt.prefab.Library;
-
-	var tools : hide.comp.Toolbar;
-
-	var layerToolbar : hide.comp.Toolbar;
-	var layerButtons : Map<PrefabElement, hide.comp.Toolbar.ToolToggle>;
-
-	var grid : h3d.scene.Graphics;
-
-	var gridStep : Int;
-	var gridSize : Int;
-	var showGrid = false;
-
-	// autoSync
-	var autoSync : Bool;
-	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 = new hrt.prefab.Library();
-		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 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="Move to bottom of tree">
-									<div class="icon fa fa-compress"></div>
-								</div>
-								<div class="button separate-btn layout-btn" title="Move to own column">
-									<div class="icon fa fa-expand"></div>
-								</div>
-
-								<div class="button hide-cols-btn close-btn" title="Hide Tree & Props">
-									<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 class="hide-scroll"></div>
-					</div>
-
-					<div class="button show-cols-btn close-btn" title="Show Tree & Props">
-						<div class="icon fa fa-chevron-left"></div>
-					</div>
-				</div>
-			</div>
-		');
-
-		tools = new hide.comp.Toolbar(null,element.find(".tools-buttons"));
-		layerToolbar = new hide.comp.Toolbar(null,element.find(".layer-buttons"));
-		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();
-		});
-
-		refreshSceneFilters();
-		refreshGraphicsFilters();
-	}
-
-	function refreshColLayout() {
-		var config = ide.ideConfig;
-		if( config.sceneEditorLayout == null ) {
-			config.sceneEditorLayout = {
-				colsVisible: true,
-				colsCombined: false,
-			};
-		}
-		setCombine(config.sceneEditorLayout.colsCombined);
-
-		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();
-	}
-
-	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();
-	}
-
-	public function onSceneReady() {
-		tools.saveDisplayKey = "Prefab/toolbar";
-		statusText = new h2d.Text(hxd.res.DefaultFont.get(), scene.s2d);
-		statusText.setPosition(5, 5);
-		statusText.visible = false;
-		hide.comp.Toolbar.ToolsObject.level3D = this;
-		var toolsNames : Iterator<String> = hide.comp.Toolbar.ToolsObject.tools.keys();
-		for (toolName in toolsNames) {
-			var tool = hide.comp.Toolbar.ToolsObject.tools.get(toolName);
-			if (tool != null) {
-				var shortcut = (config.get("key.sceneeditor." + toolName) != null)? " (" + config.get("key.sceneeditor." + toolName) + ")" : "";
-				switch(tool.type) {
-					case Button:
-						var button = tools.addButton(tool.icon, tool.title + shortcut, tool.buttonFunction);
-						if (tool.iconTransform != null) {
-							button.find(".icon").css({transform: tool.iconTransform});
-						}
-					case Toggle:
-						var toggle = tools.addToggle(tool.icon, tool.title + shortcut, tool.toggleFunction);
-						if (tool.iconTransform != null) {
-							toggle.element.find(".icon").css({transform: tool.iconTransform});
-						}
-						keys.register("sceneeditor." + toolName, () -> toggle.toggle(!toggle.isDown()));
-						if (tool.rightClick != null) {
-							toggle.rightClick(tool.rightClick);
-						}
-					case Color:
-						tools.addColor(tool.title, tool.colorFunction);
-					case Range:
-						tools.addRange(tool.title, tool.rangeFunction, 1.);
-
-				}
-			}
-		}
-		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) {
-		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) ) {
-			save();
-			lastSyncChange = properties.lastChange;
-			currentVersion = undo.currentID;
-		}
-
-	}
-
-	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("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);
-		}
-
-		var sceneFiltersMenu = layerToolbar.addMenu("", "Scene filters");
-		var content : Array<hide.comp.ContextMenu.ContextMenuItem> = [];
-		var initDone = false;
-		for(typeid in sceneFilters.keys()) {
-			content.push({label : typeid, checked : sceneFilters[typeid], click : function() {
-				var on = !sceneFilters[typeid];
-				sceneFilters.set(typeid, on);
-				if(initDone)
-					applySceneFilter(typeid, on);
-				content.find(function(item) return item.label == typeid).checked = on;
-			}});
-		}
-		sceneFiltersMenu.setContent(content);
-		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);
-		}
-		var graphicsFiltersMenu = layerToolbar.addMenu("", "Graphics filters");
-		var content : Array<hide.comp.ContextMenu.ContextMenuItem> = [];
-		var initDone = false;
-		for(typeid in graphicsFilters.keys()) {
-			content.push({label : typeid, checked : graphicsFilters[typeid], click : function() {
-				var on = !graphicsFilters[typeid];
-				graphicsFilters.set(typeid, on);
-				if(initDone)
-					applyGraphicsFilters(typeid, on);
-				content.find(function(item) return item.label == typeid).checked = on;
-			}});
-		}
-		graphicsFiltersMenu.setContent(content);
-		initDone = true;
-	}
-
-	function applyTreeStyle(p: PrefabElement, el: Element, pname: String) {
-	}
-
-	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(Level3D, ["prefab"], { icon : "sitemap", createNew : "Prefab" });
-	static var _1 = FileTree.registerExtension(Level3D, ["l3d"], { icon : "sitemap" });
-
-}