Jelajahi Sumber

Merge branch 'l3d-refact'

trethaller 7 tahun lalu
induk
melakukan
7a442def21

+ 5 - 0
bin/defaultProps.json

@@ -70,6 +70,11 @@
 	// l3d config
 	"l3d.groundLayer": "ground",
 	"l3d.cdbLevel": "level",
+	"l3d.colors": {
+	},
+	"l3d.treeStyles": {
+		"settings": {"color": "#ffffff", "font-weight": "bold"}
+	},
 
 	// FX editor
 	"fx.shaders": [

+ 13 - 3
bin/style.css

@@ -823,11 +823,11 @@ body.hide-subview .lm_controls {
 .jstree .filtered {
   display: none;
 }
-.jstree .invisible a {
+.jstree .hidden {
   color: #555 !important;
-  display: inline;
 }
-.jstree .invisible i {
+.jstree .hidden i,
+.jstree .hidden a {
   color: #555;
 }
 .jstree .disabled a {
@@ -852,6 +852,16 @@ body.hide-subview .lm_controls {
 .jstree-icon i {
   color: #aaa;
 }
+.visibility-toggle {
+  float: right;
+  padding: 4px;
+}
+.visibility-toggle:hover {
+  color: white;
+}
+.visibility-toggle .hidden i {
+  color: #555;
+}
 .jstree.small a {
   font-size: 12px;
 }

+ 20 - 7
bin/style.less

@@ -903,16 +903,14 @@ body.hide-subview {
 		display: none;
 	}
 
-	.invisible {
-		a {
-			color: #555 !important;
-			display: inline;
-		}
-		i {
+	.hidden {
+		color: #555 !important;
+
+		i, a {
 			color: #555;
 		}
 	}
-
+	
 	.disabled {
 		a {
 			color: #533 !important;
@@ -944,6 +942,21 @@ body.hide-subview {
 	}
 }
 
+.visibility-toggle {
+	float: right;
+	padding: 4px;
+
+	&:hover {
+		color : white;
+	}
+}
+
+.visibility-toggle .hidden {
+	i {
+		color: #555;
+	}
+}
+
 .jstree.small {
 	@size: 18px;
 

+ 5 - 0
hide.hxml

@@ -1,5 +1,10 @@
 common.hxml
 -js bin/hide.js
 -main hide.Ide
+--macro include("h3d.shader")
+--macro include("h2d.col")
+--macro include("hxd.poly2tri")
+--macro include("hxd.clipper")
+--macro include("hxd.earcut")
 -dce no
 -debug

+ 79 - 3
hide/Renderer.hx

@@ -2,6 +2,20 @@ package hide;
 
 // ----- Default Rendering --------------------------------
 
+class DefaultForwardComposite extends h3d.shader.ScreenShader {
+	static var SRC = {
+		@param var texture : Sampler2D;
+		@param var outline : Sampler2D;
+
+		function fragment() {
+			pixelColor = texture.get(calculatedUV);
+			var outval = outline.get(calculatedUV).rgb;
+			if(outval.r > 0.1 && outval.r < 0.5)
+				pixelColor.rgb += outval.rgb * 3.0 + 0.1;
+		}
+	}
+}
+
 class MaterialSetup extends h3d.mat.MaterialSetup {
     override public function createRenderer() {
 	    return new Renderer();
@@ -20,8 +34,21 @@ class MaterialSetup extends h3d.mat.MaterialSetup {
 
 class Renderer extends h3d.scene.DefaultRenderer {
 
+	var composite: h3d.pass.ScreenFx<DefaultForwardComposite>;
+	var outline = new ScreenOutline();
+	var outlineBlur = new h3d.pass.Blur(4);
+
+	public function new() {
+		super();
+		composite = new h3d.pass.ScreenFx(new DefaultForwardComposite());
+	}
+
 	override function render() {
 
+		var output = allocTarget("output");
+		setTarget(output);
+		clear(h3d.Engine.getCurrent().backgroundColor, 1, 0);
+
 		if( has("shadow") )
 			renderPass(shadow,get("shadow"));
 
@@ -36,8 +63,24 @@ class Renderer extends h3d.scene.DefaultRenderer {
 		renderPass(defaultPass, get("additive") );
 		renderPass(defaultPass, getSort("debuggeom") );
 		renderPass(defaultPass, getSort("debuggeom_alpha"));
-        renderPass(defaultPass, get("outline"));
+		renderPass(defaultPass, getSort("overlay") );
 		renderPass(defaultPass, getSort("ui"));
+
+
+		var outlineTex = allocTarget("outlineBlur", false);
+		{
+			var outlineSrcTex = allocTarget("outline", false);
+			setTarget(outlineSrcTex);
+			clear(0);
+			draw("highlight");
+			resetTarget();
+			outlineBlur.apply(ctx, outlineSrcTex, outlineTex);
+		}
+
+		resetTarget();
+		composite.shader.texture = output;
+		composite.shader.outline = outlineTex;
+		composite.render();
 	}
 }
 
@@ -74,8 +117,33 @@ class PbrSetup extends h3d.mat.PbrMaterialSetup {
 	}
 }
 
+class ScreenOutline extends h3d.shader.ScreenShader {
+	static var SRC = {
+
+		@param var texture: Sampler2D;
+
+		function vertex() {
+		}
+
+		function fragment() {
+			var uv = input.uv;
+			var outval = texture.get(uv).rgb;
+			if(outval.r > 0.1 && outval.r < 0.5)
+				pixelColor.rgb += outval.rgb*3.0 + 0.1;
+		}
+	};
+}
+
 class PbrRenderer extends h3d.scene.pbr.Renderer {
 
+	var outline = new ScreenOutline();
+	var outlineBlur = new h3d.pass.Blur(4);
+
+	public function new(env) {
+		super(env);
+		tonemap.addShader(outline);
+	} 
+
 	override function getDefaultProps( ?kind : String ) : Any {
 		var props : h3d.scene.pbr.Renderer.RenderProps = super.getDefaultProps(kind);
 		props.sky = Background;
@@ -84,16 +152,24 @@ class PbrRenderer extends h3d.scene.pbr.Renderer {
 
 	override function mainDraw() {
 		output.draw(getSort("default", true));
-		output.draw(get("outlined"));
 		output.draw(getSort("alpha"));
 		output.draw(get("additive"));
+
+		
+		var outlineTex = allocTarget("outline", false);
+		setTarget(outlineTex);
+		clear(0);
+		draw("highlight");
+
+		var outlineBlurTex = allocTarget("outlineBlur", false);
+		outlineBlur.apply(ctx, outlineTex, outlineBlurTex);
+		outline.texture = outlineBlurTex;
 	}
 
 	override function postDraw() {
 		defaultPass.draw(getSort("debuggeom"));
 		defaultPass.draw(getSort("debuggeom_alpha"));
 		defaultPass.draw(getSort("overlay"));
-		draw("outline");
 		defaultPass.draw(getSort("ui"));
 	}
 }

+ 14 - 0
hide/comp/IconTree.hx

@@ -12,6 +12,7 @@ typedef IconTreeItem<T> = {
 		@:optional var loaded : Bool;
 	};
 	@:optional var li_attr : Dynamic;
+	@:optional var a_attr : Dynamic;
 	@:optional @:noCompletion var id : String; // internal usage
 	@:optional @:noCompletion var absKey : String; // internal usage
 }
@@ -87,6 +88,8 @@ class IconTree<T:{}> extends Component {
 	public function init() {
 		(element:Dynamic).jstree({
 			core : {
+				dblclick_toggle: false,
+				animation: 50,
 				themes: {
 					name: "default-dark",
 					dots: true,
@@ -153,6 +156,17 @@ class IconTree<T:{}> extends Component {
 				applyStyle(item, el);
 			}
 		});
+		element.on("rename_node.jstree", function(e, data) {
+			var item = map.get(data.node.id).value;
+			var el = getElement(item);
+			applyStyle(item, el);
+		});
+		element.on("before_open.jstree", function(event, data) {
+			var lis = new Element(event.target).find("li");
+			for(li in lis) {
+				applyStyle(map.get(li.id).value, new Element(li));
+			}
+		});
 	}
 
 	function getRev( o : T ) {

+ 77 - 57
hide/comp/SceneEditor.hx

@@ -93,6 +93,7 @@ class SceneEditor {
 	var interactives : Map<PrefabElement, h3d.scene.Interactive>;
 	var ide : hide.Ide;
 	var event : hxd.WaitEvent;
+	var hideList : Map<PrefabElement, Bool> = new Map();
 
 	var undo(get, null):hide.ui.UndoHistory;
 	function get_undo() { return view.undo; }
@@ -159,6 +160,16 @@ class SceneEditor {
 				reparentElement(children, parent, 0);
 			}
 		});
+
+		var list = @:privateAccess view.getDisplayState("hideList");
+		if(list != null) {
+			var m = [for(i in (list:Array<Dynamic>)) i => true];
+			var all = sceneData.flatten(hxd.prefab.Prefab);
+			for(p in all) {
+				if(m.exists(p.getAbsPath()))
+					hideList.set(p, true);
+			}
+		}
 	}
 
 	public dynamic function onResize() {
@@ -264,7 +275,7 @@ class SceneEditor {
 			];
 
 			if(current != null && current.to(Object3D) != null && current.to(hide.prefab.Reference) == null) {
-				var visible = current.to(Object3D).visible;
+				var visible = !isHidden(current);
 				menuItems = menuItems.concat([
 					{ label : "Visible", checked : visible, click : function() setVisible(curEdit.elements, !visible) },
 					{ label : "Select all", click : selectAll },
@@ -321,7 +332,7 @@ class SceneEditor {
 				}, 50);
 			}
 		}
-		tree.applyStyle = updateTreeStyle;
+		tree.applyStyle = applyTreeStyle;
 		selectObjects([]);
 		refresh();
 	}
@@ -333,7 +344,7 @@ class SceneEditor {
 			for(elt in all) {
 				var el = tree.getElement(elt);
 				if(el == null) continue;
-				updateTreeStyle(elt, el);
+				applyTreeStyle(elt, el);
 			}
 			if(callb != null) callb();
 		});
@@ -363,6 +374,10 @@ class SceneEditor {
 		scene.init();
 		scene.engine.backgroundColor = bgcol;
 		refreshInteractives();
+
+		var all = sceneData.flatten(hxd.prefab.Prefab);
+		for(elt in all)
+			applySceneStyle(elt);
 		onRefresh();
 	}
 
@@ -649,22 +664,47 @@ class SceneEditor {
 
 		if(p != sceneData) {
 			var el = tree.getElement(p);
-			updateTreeStyle(p, el);
+			applyTreeStyle(p, el);
 		}
+
+		applySceneStyle(p);
 	}
 
-	function updateTreeStyle(p: PrefabElement, el: Element) {
+	public function applyTreeStyle(p: PrefabElement, el: Element) {
 		var obj3d  = p.to(Object3D);
-		if(obj3d != null)
-			el.toggleClass("invisible", !obj3d.visible);
 		el.toggleClass("disabled", !p.enabled);
+
+		if(obj3d != null) {
+			el.toggleClass("hidden", isHidden(obj3d));
+			var tog = el.find(".visibility-toggle").first();
+			if(tog.length == 0) {
+				tog = new Element('<i class="fa fa-eye visibility-toggle"/>').appendTo(el.find(".jstree-wholerow").first());
+				tog.click(function(e) {
+					if(curEdit.elements.indexOf(obj3d) >= 0)
+						setVisible(curEdit.elements, isHidden(obj3d));
+					else
+						setVisible([obj3d], isHidden(obj3d));
+					
+					e.preventDefault();
+					e.stopPropagation();
+				});
+			}
+		}
+	}
+
+	public function applySceneStyle(p: PrefabElement) {
+	
 	}
 
 	public function getContext(elt : PrefabElement) {
-		if(elt != null) {
-			return context.shared.contexts.get(elt);
-		}
-		return null;
+		if(elt == null) return null;
+		return context.shared.contexts.get(elt);
+	}
+
+	public function getContexts(elt: PrefabElement) {
+		if(elt == null)
+			return null;
+		return context.shared.getContexts(elt);
 	}
 
 	public function getObject(elt: PrefabElement) {
@@ -685,29 +725,11 @@ class SceneEditor {
 		}));
 		refresh(function() {
 			selectObjects([e]);
-			if( e.parent == sceneData && sceneData.children.length == 1 )
-				resetCamera();
 		});
 	}
 
 	function fillProps( edit, e : PrefabElement ) {
 		e.edit(edit);
-		var sheet = e.getCdbModel();
-		if( sheet == null ) return;
-
-		if( e.props == null ) {
-			trace("TODO : add button to init properties");
-			return;
-		}
-		var props = properties.add(new hide.Element('
-			<div class="group" name="Properties ${sheet.name.split('@').pop()}">
-			</div>
-		'),this);
-		var editor = new hide.comp.cdb.ObjEditor(sheet, e.props, props.find(".group .content"));
-		editor.undo = properties.undo;
-		editor.onChange = function(pname) {
-			edit.onChange(e, 'props.$pname');
-		}
 	}
 
 	public function selectObjects( elts : Array<PrefabElement>, ?includeTree=true) {
@@ -918,7 +940,7 @@ class SceneEditor {
 		var o = elt.to(Object3D);
 		if(o == null)
 			return true;
-		return o.visible && (elt.parent != null ? isVisible(elt.parent) : true);
+		return o.visible && !isHidden(o) && (elt.parent != null ? isVisible(elt.parent) : true);
 	}
 
 	public function getAllSelectable() : Array<PrefabElement> {
@@ -965,33 +987,33 @@ class SceneEditor {
 		}));
 	}
 
-	public function setVisible(elements : Array<PrefabElement>, visible: Bool) {
-		var obj3ds = [];
-		for(e in elements) {
-			var o = e.to(Object3D);
-			if(o != null) {
-				obj3ds.push({o: o, vis: o.visible});
-			}
-		}
+	public function isHidden(e: PrefabElement) {
+		return hideList.exists(e);
+	}
 
-		function apply(b) {
-			for(c in obj3ds) {
-				c.o.visible = b ? visible : c.vis;
-				var ctx = getContext(c.o);
-				if(ctx != null) {
-					c.o.updateInstance(ctx, "visible");
+	function saveHideState() {
+		var state = [for (h in hideList.keys()) h.getAbsPath()];
+		@:privateAccess view.saveDisplayState("hideList", state);
+	}
+
+	public function setVisible(elements : Array<PrefabElement>, visible: Bool) {
+		for(o in elements) {
+			if(visible) {
+				for(c in o.flatten(Object3D)) {
+					hideList.remove(c);
+					var el = tree.getElement(c);
+					applyTreeStyle(c, el);
+					applySceneStyle(c);
 				}
-				onPrefabChange(c.o);
+			}
+			else {
+				hideList.set(o, true);
+				var el = tree.getElement(o);
+				applyTreeStyle(o, el);
+				applySceneStyle(o);
 			}
 		}
-
-		apply(true);
-		undo.change(Custom(function(undo) {
-			if(undo)
-				apply(false);
-			else
-				apply(true);
-		}));
+		saveHideState();
 	}
 
 	function isolate(elts : Array<PrefabElement>) {
@@ -1115,9 +1137,11 @@ class SceneEditor {
 		}];
 		var oldContexts = contexts.copy();
 		for(e in elts) {
+			hideList.remove(e);
 			for(c in e.flatten())
 				contexts.remove(c);
 		}
+		saveHideState();
 		var newContexts = contexts.copy();
 		function action(undo) {
 			if( undo ) {
@@ -1199,10 +1223,6 @@ class SceneEditor {
 	function autoName(p : PrefabElement) {
 
 		var uniqueName = false;
-		var layer = p.getParent(hide.prefab.l3d.Layer);
-		if(layer != null) {
-			uniqueName = layer.uniqueNames;
-		}
 		if( p.type == "volumetricLightmap" || p.type == "light" )
 			uniqueName = true;
 

+ 1 - 1
hide/prefab/Object3D.hx

@@ -130,7 +130,7 @@ class Object3D extends Prefab {
 		return {
 			icon : children == null || children.length > 0 ? "folder-open" : "genderless",
 			name : "Group",
-			allowChildren : function(t) return hxd.prefab.Library.isOfType(t,Object3D) || ["settings", "renderProps", "material", "shader"].indexOf(t) >= 0
+			allowChildren : function(t) return hxd.prefab.Library.isOfType(t,Object3D) || ["settings", "material", "shader"].indexOf(t) >= 0 || name == "settings"
 		};
 	}
 	#end

+ 1 - 0
hide/prefab/fx/Emitter.hx

@@ -490,6 +490,7 @@ class Emitter extends Object3D {
 
 	override function makeInstanceRec(ctx: Context) {
 		ctx = makeInstance(ctx);
+		return ctx;
 		// Don't make children, which are used to setup particles
 	}
 

+ 4 - 1
hide/prefab/l3d/Instance.hx

@@ -13,6 +13,9 @@ class Instance extends Object3D {
 		#if editor
 		var ctx = super.makeInstance(ctx);
 		var kind = getCdbKind(this);
+		if(kind == null)
+			return ctx;
+	
 		var modelPath = findModelPath(kind.sheet, kind.idx.obj);
 		if(modelPath != null) {
 			try {
@@ -57,7 +60,7 @@ class Instance extends Object3D {
 	}
 
 	override function getHideProps() : HideProps {
-		return { icon : "circle", name : "Instance", allowParent : function(p) return p.type == "layer" };
+		return { icon : "circle", name : "Instance" };
 	}
 
 	// Move to Prefab?

+ 0 - 95
hide/prefab/l3d/Layer.hx

@@ -1,95 +0,0 @@
-package hide.prefab.l3d;
-using Lambda;
-
-class Layer extends Object3D {
-
-	public var locked = false;
-	public var uniqueNames = false;
-	public var color = 0xffffffff;
-
-	public function new(?parent) {
-		super(parent);
-		type = "layer";
-	}
-
-	#if editor
-	override function getCdbModel( ?p : Prefab ) {
-		if( p == null )
-			return null;
-		var levelSheet = getLevelSheet();
-		if(levelSheet == null) return null;
-		var lname = name.split("_")[0];
-		var col = levelSheet.columns.find(c -> c.name == lname);
-		if(col == null || col.type != TList) return null;
-		return levelSheet.getSub(col);
-	}
-
-	public function getLevelSheet() {
-		var ide = hide.Ide.inst;
-		return ide.database.getSheet(ide.currentProps.get("l3d.cdbLevel", "level"));
-	}
-	#end
-
-	override function save() {
-		var obj : Dynamic = super.save();
-		if( locked ) obj.locked = locked;
-		if( uniqueNames ) obj.uniqueNames = uniqueNames;
-		if( color != -1 ) obj.color = color;
-		return obj;
-	}
-
-	override function load( obj : Dynamic ) {
-		super.load(obj);
-		if(obj.locked != null)
-			locked = obj.locked;
-		if(obj.color != null)
-			color = obj.color;
-		if(obj.uniqueNames != null)
-			uniqueNames = obj.uniqueNames;
-	}
-
-	#if editor
-
-	override function edit( ctx : EditContext ) {
-		super.edit(ctx);
-		var props = ctx.properties.add(new hide.Element('
-			<div class="group" name="Layer">
-				<dl>
-					<dt>Locked</dt><dd><input type="checkbox" field="locked"/></dd>
-					<dt>Unique names</dt><dd><input type="checkbox" field="uniqueNames"/></dd>
-					<dt>Color</dt><dd><input name="colorVal"/></dd>
-				</dl>
-			</div>
-		'),this, function(pname) {
-			ctx.onChange(this, pname);
-		});
-		var colorInput = props.find('input[name="colorVal"]');
-		var picker = new hide.comp.ColorPicker(true,null,colorInput);
-		picker.value = color;
-		picker.onChange = function(move) {
-			if(!move) {
-				var prevVal = color;
-				var newVal = picker.value;
-				color = picker.value;
-				ctx.properties.undo.change(Custom(function(undo) {
-					if(undo) {
-						color = prevVal;
-					}
-					else {
-						color = newVal;
-					}
-					picker.value = color;
-					ctx.onChange(this, "color");
-				}));
-				ctx.onChange(this, "color");
-			}
-		}
-	}
-
-	override function getHideProps() : HideProps {
-		return { icon : "file", name : "Layer" };
-	}
-	#end
-
-	static var _ = hxd.prefab.Library.register("layer", Layer);
-}

+ 7 - 0
hide/prefab/l3d/Level3D.hx

@@ -23,6 +23,13 @@ class Level3D extends hxd.prefab.Library {
 		height = obj.height == null ? 100 : obj.height;
 	}
 
+	override function getCdbModel(?p:hxd.prefab.Prefab):cdb.Sheet {
+		#if (editor && castle)
+		return hide.view.l3d.Level3D.getCdbModel(p);
+		#end 
+		return null;
+	}
+
 	#if editor
 
 	override function edit( ctx : EditContext ) {

+ 9 - 10
hide/prefab/l3d/Polygon.hx

@@ -42,15 +42,14 @@ class Polygon extends Object3D {
 
 		var mat = mesh.material;
 		mat.mainPass.culling = None;
-
-		var layer = getParent(Layer);
-		if(layer != null) {
-			setColor(ctx, layer != null ? (layer.color | 0x40000000) : 0x40ff00ff);
-		}
-		else {
-			mat.props = h3d.mat.MaterialSetup.current.getDefaults("opaque");
-			mat.color.setColor(0xffffffff);
-		}
+		// var layer = getParent(Layer);
+		// if(layer != null) {
+		// 	setColor(ctx, layer != null ? (layer.color | 0x40000000) : 0x40ff00ff);
+		// }
+		// else {
+		// 	mat.props = h3d.mat.MaterialSetup.current.getDefaults("opaque");
+		// 	mat.color.setColor(0xffffffff);
+		// }
 
 		mat.castShadows = false;
 	}
@@ -146,7 +145,7 @@ class Polygon extends Object3D {
 		return ctx;
 	}
 
-	function setColor(ctx: Context, color: Int) {
+	public function setColor(ctx: Context, color: Int) {
 		#if editor
 		if(hide.prefab.Material.hasOverride(this))
 			return;

+ 224 - 139
hide/view/l3d/Level3D.hx

@@ -7,7 +7,6 @@ import hxd.Key as K;
 import hide.prefab.Prefab as PrefabElement;
 import hide.prefab.Object3D;
 import hide.prefab.l3d.Instance;
-import hide.prefab.l3d.Layer;
 import h3d.scene.Object;
 
 
@@ -109,14 +108,14 @@ private class Level3DSceneEditor extends hide.comp.SceneEditor {
 		parent.onSceneReady();
 	}
 
-	override function selectAll() {
-		var all = [for(e in getAllSelectable()) if(e.to(Layer) == null) e];
-		selectObjects(all);
+	override function applyTreeStyle(p: PrefabElement, el: Element) {
+		super.applyTreeStyle(p, el);
+		parent.applyTreeStyle(p, el);
 	}
 
-	override function updateTreeStyle(p: PrefabElement, el: Element) {
-		super.updateTreeStyle(p, el);
-		parent.updateTreeStyle(p, el);
+	override function applySceneStyle(p:PrefabElement) {
+		super.applySceneStyle(p);
+		parent.applySceneStyle(p);
 	}
 
 	override function onPrefabChange(p: PrefabElement, ?pname: String) {
@@ -124,16 +123,6 @@ private class Level3DSceneEditor extends hide.comp.SceneEditor {
 		parent.onPrefabChange(p, pname);
 	}
 
-	override function addObject(p: PrefabElement) {
-		var layer = p.getParent(Layer);
-		if(layer != null) {
-			var cdbSheet = layer.getCdbModel(layer);
-			if(cdbSheet != null)
-				p.props = cdbSheet.getDefaults();
-		}
-		super.addObject(p);
-	}
-
 	override function projectToGround(ray: h3d.col.Ray) {
 		var polygons = parent.getGroundPolys();
 		var minDist = -1.;
@@ -159,13 +148,11 @@ private class Level3DSceneEditor extends hide.comp.SceneEditor {
 		if(current != null && current.type == "object" && current.name == "settings" && current.parent == sceneData)
 			newItems.push(getNewTypeMenuItem("renderProps",current)); // hack : todo
 
-		var curLayer = current != null ? current.to(hide.prefab.l3d.Layer) : null;
-		var cdbSheet = curLayer != null ? curLayer.getCdbModel(curLayer) : null;
-
 		function setup(p : PrefabElement) {
 			var proj = screenToWorld(scene.s2d.width/2, scene.s2d.height/2);
 			var obj3d = p.to(hide.prefab.Object3D);
-			if(proj != null && obj3d != null && p.to(hide.prefab.l3d.Layer) == null) {
+			var autoCenter = proj != null && obj3d != null && (Type.getClass(p) != Object3D || p.parent != sceneData);
+			if(autoCenter) {
 				var parentMat = worldMat(getObject(p.parent));
 				parentMat.invert();
 				var localMat = new h3d.Matrix();
@@ -174,9 +161,6 @@ private class Level3DSceneEditor extends hide.comp.SceneEditor {
 				obj3d.setTransform(localMat);
 			}
 
-			if(cdbSheet != null)
-				p.props = cdbSheet.getDefaults();
-
 			autoName(p);
 			haxe.Timer.delay(addObject.bind(p), 0);
 		}
@@ -184,48 +168,111 @@ private class Level3DSceneEditor extends hide.comp.SceneEditor {
 		newItems = newItems.concat(super.getNewContextMenu(current));
 
 		function addNewInstances() {
-			if(curLayer == null)
-				return;
-			if(cdbSheet == null)
-				return;
-			var refCol = Instance.findRefColumn(cdbSheet);
-			if(refCol == null)
-				return;
-			var refSheet = cdbSheet.base.getSheet(refCol.sheet);
-			var idCol = Instance.findIDColumn(refSheet);
-			if(idCol != 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 = new hide.prefab.l3d.Instance(current);
-							p.name = kind.charAt(0).toLowerCase() + kind.substr(1);
-							setup(p);
-							Reflect.setField(p.props, refCol.col.name, kind);
-						}
-					});
+			var types = Level3D.getCdbTypes();
+			var items = new Array<hide.comp.ContextMenu.ContextMenuItem>();
+			for(type in types) {
+				var typeId = Level3D.getCdbTypeId(type);
+				var label = typeId.charAt(0).toUpperCase() + typeId.substr(1);
+
+				var refCol = Instance.findRefColumn(type);
+				if(refCol == null)
+					continue;
+				var refSheet = type.base.getSheet(refCol.sheet);
+				var idCol = Instance.findIDColumn(refSheet);
+
+				function make(name) {
+					var p = new hide.prefab.l3d.Instance(current);
+					p.props = type.getDefaults();
+					Reflect.setField(p.props, "$cdbtype", typeId);
+					p.name = name;
+					setup(p);
+					return p;
 				}
-				newItems.unshift({
-					label : "Instance",
-					menu: kindItems
-				});
-			}
-			else {
-				newItems.unshift({
-					label : "Instance",
-					click : function() {
-						var p = new hide.prefab.l3d.Instance(current);
-						p.name = "object";
-						setup(p);
+
+				if(idCol != 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));
+								Reflect.setField(p.props, refCol.col.name, kind);
+							}
+						});
 					}
-				});
+					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 fillProps(edit:hide.prefab.EditContext, e:PrefabElement) {
+		super.fillProps(edit, e);
+
+		var sheet = Level3D.getCdbModel(e);
+		var group = new hide.Element('
+			<div class="group" name="CDB">
+				<dl><dt>Type</dt><dd><select><option value="">- No props -</option></select></dd>
+			</div>
+		');
+
+		var select = group.find("select");
+		var cdbTypes = Level3D.getCdbTypes();
+		for(t in cdbTypes) {
+			var current = sheet != null && sheet.name == t.name;
+			var id = Level3D.getCdbTypeId(t);
+			var opt = new hide.Element("<option>").attr("value", id).text(id).appendTo(select);
+		}
+		if(sheet != null) {
+			select.val(Level3D.getCdbTypeId(sheet));
+		}
+
+		function changeProps(props: Dynamic) {
+			properties.undo.change(Field(e, "props", e.props), ()->edit.rebuildProperties());
+			e.props = props;
+			edit.onChange(e, "props");
+			edit.rebuildProperties();
+		}
+
+		select.change(function(v) {
+			var typeId = select.val();
+			if(typeId == null || typeId == "") {
+				changeProps(null);
+				return;
+			}
+			var cdbSheet = Level3D.resolveCdbType(typeId);
+			var props = cdbSheet.getDefaults();
+			Reflect.setField(props, "$cdbtype", typeId);
+			changeProps(props);
+		});
+
+		edit.properties.add(group);
+
+		if(sheet != null) {
+			var props = new hide.Element('<div></div>').appendTo(group.find(".content"));
+			var editor = new hide.comp.cdb.ObjEditor(sheet, e.props, props);
+			editor.undo = properties.undo;
+			editor.onChange = function(pname) {
+				edit.onChange(e, 'props.$pname');
+			}
+		}
+	}
 }
 
 class Level3D extends FileView {
@@ -247,6 +294,7 @@ class Level3D extends FileView {
 	var currentVersion : Int = 0;
 	var lastSyncChange : Float = 0.;
 	var currentSign : String;
+	var sceneFilters : Map<String, Bool>;
 
 	var scene(get, null):  hide.comp.Scene;
 	function get_scene() return sceneEditor.scene;
@@ -306,6 +354,8 @@ class Level3D extends FileView {
 			edit.cleanups = [];
 			data.edit(edit);
 		}
+
+		refreshSceneFilters();
 	}
 
 	public function onSceneReady() {
@@ -446,14 +496,9 @@ class Level3D extends FileView {
 	}
 
 	function onRefresh() {
-		refreshLayerIcons();
 	}
 
 	function onRefreshScene() {
-		var all = data.flatten(hxd.prefab.Prefab);
-		for(elt in all)
-			refreshSceneStyle(elt);
-
 		// Apply first render props
 		var settings = data.children.find(c -> c.name == "settings");
 		if(settings != null) {
@@ -486,13 +531,6 @@ class Level3D extends FileView {
 						parent = sel;
 					else if(sel.parent != null && Type.getClass(sel.parent) == Object3D)
 						parent = sel.parent;
-					else {
-						var curLayer = sel.to(Layer);
-						if(curLayer == null)
-							curLayer = sel.getParent(Layer);
-						if(curLayer != null)
-							parent = curLayer;
-					}
 				}
 				sceneEditor.dropObjects(paths, parent);
 			}
@@ -501,116 +539,163 @@ class Level3D extends FileView {
 		return false;
 	}
 
-	function refreshLayerIcons() {
+	function applySceneFilter(typeid: String, visible: Bool) {
+		saveDisplayState("sceneFilters/" + typeid, visible);
+		var all = data.flatten(hxd.prefab.Prefab);
+		for(p in all) {
+			if(p.type == typeid || getCdbTypeId(p) == typeid) {
+				sceneEditor.applySceneStyle(p);
+			}
+		}
+	}
+
+	function refreshSceneFilters() {
+		var filters = ["model", "polygon", "box", "instance", "light"];
+		for(sheet in getCdbTypes()) {
+			filters.push(getCdbTypeId(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 all = data.flatten(hxd.prefab.Prefab);
 		var initDone = false;
-		for(elt in all) {
-			var layer = elt.to(hide.prefab.l3d.Layer);
-			if(layer == null) continue;
-			layerButtons[elt] = layerToolbar.addToggle("file", layer.name, layer.name, function(on) {
+		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)
-					sceneEditor.setVisible([layer], on);
-			}, layer.visible);
-
-			refreshLayerIcon(layer);
+					applySceneFilter(typeid, on);
+			});
+			if(sceneFilters.get(typeid) != false)
+				btn.toggle(true);
 		}
 		initDone = true;
 	}
 
-	function refreshLayerIcon(layer: hide.prefab.l3d.Layer) {
-		var lb = layerButtons[layer];
-		if(lb != null) {
-			var color = "#" + StringTools.hex(layer.color & 0xffffff, 6);
-			if(layer.visible != lb.isDown())
-				lb.toggle(layer.visible);
-			lb.element.find(".icon").css("color", color);
-			var label = lb.element.find("label");
-			if(layer.locked)
-				label.addClass("locked");
-			else
-				label.removeClass("locked");
+	function applyTreeStyle(p: PrefabElement, el: Element) {
+		var styles = ide.currentProps.get("l3d.treeStyles");
+		var style: Dynamic = null;
+		var typeId = getCdbTypeId(p);
+		if(typeId != null) {
+			style = Reflect.field(styles, typeId);
 		}
-	}
-
-	function updateTreeStyle(p: PrefabElement, el: Element) {
-		var layer = p.to(hide.prefab.l3d.Layer);
-		if(layer != null) {
-			var color = "#" + StringTools.hex(layer.color & 0xffffff, 6);
-			el.find("i.jstree-themeicon").first().css("color", color);
-			el.find("a").first().toggleClass("locked", layer.locked);
-			refreshLayerIcon(layer);
+		if(style == null) {
+			style = Reflect.field(styles, p.name);
 		}
+		var a = el.find("a").first();
+		if(style == null)
+			a.removeAttr("style");
+		else
+			a.css(style);
 	}
 
 	function onPrefabChange(p: PrefabElement, ?pname: String) {
-		refreshSceneStyle(p);
+		
 	}
 
-	function refreshSceneStyle(p: PrefabElement) {
+	function applySceneStyle(p: PrefabElement) {
 		var level3d = p.to(hide.prefab.l3d.Level3D);
 		if(level3d != null) {
 			updateGrid();
 			return;
 		}
-		var layer = p.to(hide.prefab.l3d.Layer);
-		if(layer != null)
-			applyLayerProps(layer);
+
+		var obj3d = p.to(Object3D);
+		if(obj3d != null) {
+			var visible = obj3d.visible && !sceneEditor.isHidden(obj3d) && sceneFilters.get(p.type) != false;
+			if(visible) {
+				var cdbType = getCdbTypeId(p);
+				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 = 0x80ffffff;
+		else
+			color = (color & 0xffffff) | 0xa0000000;
 
 		var box = p.to(hide.prefab.Box);
 		if(box != null) {
 			var ctx = sceneEditor.getContext(box);
-			box.setColor(ctx, getDisplayColor(p));
+			box.setColor(ctx, color);
 		}
 		var poly = p.to(hide.prefab.l3d.Polygon);
 		if(poly != null) {
 			var ctx = sceneEditor.getContext(poly);
-			poly.updateInstance(ctx);
+			poly.setColor(ctx, color);
 		}
 	}
 
-	function applyLayerProps(layer: hide.prefab.l3d.Layer) {
-		var obj3ds = layer.getAll(hide.prefab.Object3D);
-		for(obj in obj3ds) {
-			var i = @:privateAccess sceneEditor.interactives.get(obj);
-			if(i != null) i.visible = !layer.locked;
-		}
-		for(box in layer.getAll(hide.prefab.Box)) {
-			var ctx = sceneEditor.getContext(box);
-			box.setColor(ctx, getDisplayColor(box));
-		}
-		for(poly in layer.getAll(hide.prefab.l3d.Polygon)) {
-			var ctx = sceneEditor.getContext(poly);
-			poly.updateInstance(ctx);
+	function getDisplayColor(p: PrefabElement) : Null<Int> {
+		var typeId = getCdbTypeId(p);
+		if(typeId != null) {
+			var colors = ide.currentProps.get("l3d.colors");
+			var color = Reflect.field(colors, typeId);
+			if(color != null) {
+				return Std.parseInt("0x"+color.substr(1)) | 0xff000000;
+			}
 		}
+		return null;
+	}
+
+	public static function getLevelSheet() {
+		return Ide.inst.database.getSheet(Ide.inst.currentProps.get("l3d.cdbLevel", "level"));
+	}
+
+	static function resolveCdbType(id: String) {
+		var types = Level3D.getCdbTypes();
+		return types.find(t -> getCdbTypeId(t) == id);
+	}
+
+	public static function getCdbTypes() {
+		var levelSheet = getLevelSheet();
+		if(levelSheet == null) return [];
+		return [for(c in levelSheet.columns) if(c.type == TList) levelSheet.getSub(c)];
 	}
 
-	static function getDisplayColor(p: PrefabElement) {
-		var color = 0x80ffffff;
-		var layer = p.getParent(Layer);
-		if(layer != null) {
-			color = layer.color;
+	public static function getCdbTypeId(?p: PrefabElement, ?sheet: cdb.Sheet) : String {
+		if(p != null) {
+			if(p.props == null)
+				return null;
+			return Reflect.getProperty(p.props, "$cdbtype");
 		}
-		var kind = Instance.getCdbKind(p);
-		if(kind != null) {
-			var colorCol = kind.sheet.columns.find(c -> c.type == cdb.Data.ColumnType.TColor);
-			if(colorCol != null) {
-				color = cast Reflect.getProperty(kind.idx.obj, colorCol.name);
-				color |= 0x80000000;
-			}
+		else {
+			return sheet.name.split("@").pop();
 		}
-		return color;
+	}
+
+	public static function getCdbModel(e:hxd.prefab.Prefab):cdb.Sheet {
+		var typeName : String = getCdbTypeId(e);
+		if(typeName == null)
+			return null;
+		return resolveCdbType(typeName);
 	}
 
 	function getGroundPolys() {
-		var gname = props.get("l3d.groundLayer");
-		var groundLayer = data.get(Layer, gname);
-		var polygons = groundLayer.getAll(hide.prefab.l3d.Polygon);
-		return polygons;
+		var groundGroups = data.findAll(p -> if(p.name == "ground") p else null);
+		var ret = [];
+		for(group in groundGroups) {
+			group.visitChildren(function(p) {
+				if(p.name == "nocollide")
+					return false;
+				var pp = p.to(hide.prefab.l3d.Polygon);
+				if(pp == null)
+					return false;
+				ret.push(pp);
+				return true;
+			});
+		}
+		return ret;
 	}
 
 	static var _ = FileTree.registerExtension(Level3D,["l3d"],{ icon : "sitemap", createNew : "Level3D" });