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

View extends Component, handle SceneTree clicks, save Model camera

Nicolas Cannasse 8 жил өмнө
parent
commit
15b1b18d0f

+ 4 - 10
bin/style.css

@@ -157,8 +157,10 @@ input[type=checkbox]:checked:after {
   height: 100%;
 }
 .hide-scene-layer .jstree {
-  background: transparent !important;
-  user-select: none;
+  padding-right: 5px;
+}
+.hide-scene-layer .jstree-container-ul {
+  padding-right: 5px;
 }
 .flex {
   width: 100%;
@@ -235,19 +237,11 @@ input[type=checkbox]:checked:after {
 }
 /* Properties */
 .hide-properties {
-  width: 100%;
-  height: 100%;
-  display: flex;
-}
-.hide-properties > .panel {
   flex: 0 0 260px;
   padding: 10px;
   overflow: auto;
   padding-bottom: 80px;
 }
-.hide-properties > .content {
-  flex-grow: 100;
-}
 .hide-properties .section {
   margin-bottom: 10px;
 }

+ 10 - 16
bin/style.less

@@ -169,9 +169,13 @@ input[type=checkbox] {
 	height: 100%;
 
 	.jstree {
-		background: transparent !important;
-		user-select: none;
+		padding-right : 5px;
 	}
+
+	.jstree-container-ul {
+		padding-right : 5px;
+	}
+
 }
 
 .flex {
@@ -255,20 +259,10 @@ input[type=checkbox] {
 /* Properties */
 
 .hide-properties {
-	width:100%;
-	height:100%;
-	display: flex;
-
-	>.panel {
-		flex : 0 0 260px;
-		padding:10px;
-		overflow:auto;
-		padding-bottom:80px;
-	}
-
-	>.content {
-		flex-grow:100;
-	}
+	flex : 0 0 260px;
+	padding:10px;
+	overflow:auto;
+	padding-bottom:80px;
 
 	.section {
 		margin-bottom : 10px;

+ 19 - 0
hide/comp/Component.hx

@@ -3,11 +3,30 @@ package hide.comp;
 class Component {
 
 	var ide : hide.ui.Ide;
+	public var name(get, never) : String;
 	public var root : Element;
+	public var saveDisplayKey : String;
 
 	public function new(root) {
 		ide = hide.ui.Ide.inst;
 		this.root = root;
 	}
 
+	@:final function get_name() return Type.getClassName(Type.getClass(this));
+
+	function getDisplayState( key : String ) : Dynamic {
+		if( saveDisplayKey == null )
+			return null;
+		var v = js.Browser.window.localStorage.getItem(saveDisplayKey + "/" + key);
+		if( v == null )
+			return null;
+		return haxe.Json.parse(v);
+	}
+
+	function saveDisplayState( key : String, value : Dynamic ) {
+		if( saveDisplayKey == null )
+			return;
+		js.Browser.window.localStorage.setItem(saveDisplayKey + "/" + key, haxe.Json.stringify(value));
+	}
+
 }

+ 8 - 0
hide/comp/IconTree.hx

@@ -20,6 +20,9 @@ class IconTree extends Component {
 		return [{ id : id+"0", text : "get()", children : true }];
 	}
 
+	public dynamic function onClick( id : String ) : Void {
+	}
+
 	public dynamic function onDblClick( id : String ) : Void {
 	}
 
@@ -40,6 +43,11 @@ class IconTree extends Component {
 			},
 			plugins : [ "wholerow" ],
 		});
+		root.on("click.jstree", function (event) {
+			var node = new Element(event.target).closest("li");
+   			var data = node[0].id;
+			onClick(data);
+		});
 		root.on("dblclick.jstree", function (event) {
 			var node = new Element(event.target).closest("li");
    			var data = node[0].id;

+ 11 - 30
hide/comp/PropsEditor.hx

@@ -2,55 +2,36 @@ package hide.comp;
 
 class PropsEditor extends Component {
 
-	public var panel : Element;
-	public var content : Element;
 	public var undo : hide.ui.UndoHistory;
-	public var saveKey : String;
 	var fields : Array<PropsField>;
 
 	public function new(root,?undo) {
 		super(root);
+		root.addClass("hide-properties");
 		this.undo = undo == null ? new hide.ui.UndoHistory() : undo;
-		var e = new Element("<div class='hide-properties'><div class='content'></div><div class='panel'></div></div>").appendTo(root);
-		content = e.find(".content");
-		panel = e.find(".panel");
 		fields = [];
 	}
 
 	public function clear() {
-		panel.html('');
+		root.html('');
 		fields = [];
 	}
 
-	public function addMaterial( m : h3d.mat.Material, props : Dynamic, ?parent : Element ) {
+	public function addMaterial( m : h3d.mat.Material, ?parent : Element ) {
+		var props = m.props;
 		var def = h3d.mat.MaterialSetup.current.editMaterial(props);
 		def = add(def, props);
 		def.find("input,select").change(function(_) {
-			m.props = props;
+			m.refreshProps();
 			def.remove();
-			addMaterial(m, props, parent);
+			addMaterial(m, parent);
 		});
 		if( parent != null && parent.length != 0 ) def.appendTo(parent);
 	}
 
-	function getState( key : String ) : Dynamic {
-		if( saveKey == null )
-			return null;
-		var v = js.Browser.window.localStorage.getItem("propeditor/" + key);
-		if( v == null )
-			return null;
-		return haxe.Json.parse(v);
-	}
-
-	function saveState( key : String, value : Dynamic ) {
-		if( saveKey == null )
-			return;
-		js.Browser.window.localStorage.setItem("propeditor/" + key, haxe.Json.stringify(value));
-	}
-
 	public function add( e : Element, ?context : Dynamic ) {
 
-		e.appendTo(panel);
+		e.appendTo(root);
 		e = e.wrap("<div></div>").parent(); // necessary to have find working on top level element
 
 		e.find("input[type=checkbox]").wrap("<div class='checkbox-wrapper'></div>");
@@ -59,13 +40,13 @@ class PropsEditor extends Component {
 
 		// -- reload states ---
 		for( h in e.find(".section > h1").elements() )
-			if( getState("section:" + StringTools.trim(h.text())) != false )
+			if( getDisplayState("section:" + StringTools.trim(h.text())) != false )
 				h.parent().addClass("open");
 
 		for( group in e.find(".group").elements() ) {
 			var s = group.closest(".section");
 			var key = (s.length == 0 ? "" : StringTools.trim(s.children("h1").text()) + "/") + group.attr("name");
-			if( getState("group:" + key) != false )
+			if( getDisplayState("group:" + key) != false )
 				group.addClass("open");
 		}
 
@@ -76,7 +57,7 @@ class PropsEditor extends Component {
 			var section = e.getThis().parent();
 			section.toggleClass("open");
 			section.children(".content").slideToggle(100);
-			saveState("section:" + StringTools.trim(e.getThis().text()), section.hasClass("open"));
+			saveDisplayState("section:" + StringTools.trim(e.getThis().text()), section.hasClass("open"));
 		}).find("input").mousedown(function(e) e.stopPropagation());
 
 		for( g in e.find(".group").elements() ) {
@@ -94,7 +75,7 @@ class PropsEditor extends Component {
 
 			var s = group.closest(".section");
 			var key = (s.length == 0 ? "" : StringTools.trim(s.children("h1").text()) + "/") + group.attr("name");
-			saveState("group:" + key, group.hasClass("open"));
+			saveDisplayState("group:" + key, group.hasClass("open"));
 
 		}).find("input").mousedown(function(e) e.stopPropagation());
 

+ 12 - 1
hide/comp/Scene.hx

@@ -146,6 +146,14 @@ class Scene extends Component implements h3d.IDrawable {
 		});
 	}
 
+	function initMaterials( obj : h3d.scene.Object, path : String ) {
+		var res = hxd.res.Any.fromBytes(path, haxe.io.Bytes.alloc(0));
+		for( m in obj.getMaterials() ) {
+			m.model = res;
+			h3d.mat.MaterialSetup.current.initModelMaterial(m);
+		}
+	}
+
 	function loadSCN( path : String ) {
 		var ctx = new SceneLoader(path,this);
 		var fullPath = ide.getPath(path);
@@ -153,6 +161,7 @@ class Scene extends Component implements h3d.IDrawable {
 		var root = new h3d.scene.Object();
 		for( o in ctx.loadSCN(bytes).content )
 			root.addChild(o);
+		initMaterials(root, path);
 		return root;
 	}
 
@@ -160,7 +169,9 @@ class Scene extends Component implements h3d.IDrawable {
 		if( StringTools.endsWith(path.toLowerCase(), ".scn") )
 			return loadSCN(path);
 		var lib = loadHMD(path,false);
-		return lib.makeObject(loadTextureFile.bind(path));
+		var obj = lib.makeObject(loadTextureFile.bind(path));
+		initMaterials(obj, path);
+		return obj;
 	}
 
 	public function loadAnimation( path : String ) {

+ 41 - 11
hide/comp/SceneTree.hx

@@ -12,6 +12,40 @@ class SceneTree extends IconTree {
 		init();
 	}
 
+	function resolvePath(id:String) : Dynamic {
+		var path = id.split("/");
+		var root = showRoot ? obj.parent : obj;
+		while( path.length > 0 ) {
+			var idx = Std.parseInt(path[0]);
+			if( idx == null ) break;
+			path.shift();
+			root = root.getChildAt(idx);
+		}
+		if( path.length == 0 )
+			return root;
+		var prop = path.shift();
+		switch( prop.split(":").shift() ) {
+		case "mat":
+			return root.toMesh().getMaterials()[Std.parseInt(prop.substr(4))];
+		default:
+		}
+		return null;
+	}
+
+	override function onClick(id:String) {
+		var v : Dynamic = resolvePath(id);
+		if( Std.is(v, h3d.scene.Object) )
+			onSelectObject(v);
+		else if( Std.is(v, h3d.mat.Material) )
+			onSelectMaterial(v);
+	}
+
+	public dynamic function onSelectObject( obj : h3d.scene.Object ) {
+	}
+
+	public dynamic function onSelectMaterial( m : h3d.mat.Material ) {
+	}
+
 	function getIcon( c : h3d.scene.Object ) {
 		if( c.isMesh() ) {
 			if( Std.is(c, h3d.scene.Skin) )
@@ -45,19 +79,15 @@ class SceneTree extends IconTree {
 			}
 		];
 		if( root.isMesh() ) {
-			function makeMaterial( m : h3d.mat.Material, index : Int ) : IconTree.IconTreeItem {
-				return {
-					id : path+"mat"+index,
-					text : m.name == null ? "Material@"+index : m.name,
+			var materials = root.toMesh().getMeshMaterials();
+			for( i in 0...materials.length ) {
+				var m = materials[i];
+				elements.push({
+					id : path+"mat:"+i,
+					text : m.name == null ? "Material@"+i : m.name,
 					icon : "fa fa-photo",
-				};
+				});
 			}
-			var multi = Std.instance(root,h3d.scene.MultiMaterial);
-			if( multi != null )
-				for( m in multi.materials )
-					elements.push(makeMaterial(m,multi.materials.indexOf(m)));
-			else
-				elements.push(makeMaterial(root.toMesh().material,0));
 		}
 		return elements;
 	}

+ 1 - 1
hide/ui/Ide.hx

@@ -133,7 +133,7 @@ class Ide {
 			layout.registerComponent(vcl.name,function(cont,state) {
 				var view = Type.createInstance(vcl.cl,[state]);
 				view.setContainer(cont);
-				try view.onDisplay(cont.getElement()) catch( e : Dynamic ) js.Browser.alert(vcl.name+":"+e);
+				try view.onDisplay() catch( e : Dynamic ) js.Browser.alert(vcl.name+":"+e);
 			});
 
 		layout.init();

+ 7 - 7
hide/ui/View.hx

@@ -10,9 +10,8 @@ enum DisplayPosition {
 typedef ViewOptions = { ?position : DisplayPosition, ?width : Int }
 
 @:keepSub @:allow(hide.ui.Ide)
-class View<T> {
+class View<T> extends hide.comp.Component {
 
-	var ide : Ide;
 	var container : golden.Container;
 	var state : T;
 	var keys(get,null) : Keys;
@@ -24,6 +23,7 @@ class View<T> {
 	var contentHeight(get,never) : Int;
 
 	public function new(state:T) {
+		super(null);
 		this.state = state;
 		ide = Ide.inst;
 	}
@@ -71,18 +71,18 @@ class View<T> {
 			keys.processEvent(e);
 		});
 		untyped cont.parent.__view = this;
+		root = cont.getElement();
 	}
 
 	public function rebuild() {
 		if( container == null ) return;
 		syncTitle();
-		var e = container.getElement();
-		e.html('');
-		onDisplay(container.getElement());
+		root.html('');
+		onDisplay();
 	}
 
-	public function onDisplay( e : Element ) {
-		e.text(Type.getClassName(Type.getClass(this))+(state == null ? "" : " "+state));
+	public function onDisplay() {
+		root.text(Type.getClassName(Type.getClass(this))+(state == null ? "" : " "+state));
 	}
 
 	public function onResize() {

+ 2 - 2
hide/view/About.hx

@@ -6,8 +6,8 @@ class About extends hide.ui.View<{}> {
 		super(state);
 	}
 
-	override function onDisplay( e : Element ) {
-		e.html('
+	override function onDisplay() {
+		root.html('
 		<p>
 			Heaps IDE v0.1<br/>
 			(c)2017 Nicolas Cannasse

+ 5 - 5
hide/view/FileTree.hx

@@ -56,13 +56,13 @@ class FileTree extends hide.ui.View<{ root : String, opened : Array<String> }> {
 		return state.root;
 	}
 
-	override function onDisplay( e : Element ) {
+	override function onDisplay() {
 
 		if( state.root == null ) return;
 
 		if( state.opened == null ) state.opened = [];
 
-		var panel = new Element("<div class='hide-scroll'>").appendTo(e);
+		var panel = new Element("<div class='hide-scroll'>").appendTo(root);
 		tree = new hide.comp.IconTree(panel);
 		tree.get = function(path) {
 			if( path == null ) path = "";
@@ -96,10 +96,10 @@ class FileTree extends hide.ui.View<{ root : String, opened : Array<String> }> {
 		// prevent dummy mouseLeft from breaking our quickOpen feature
 		var mouseLeft = false;
 		var leftCount = 0;
-		e.on("mouseenter", function(_) {
+		root.on("mouseenter", function(_) {
 			mouseLeft = false;
 		});
-		e.on("mouseleave", function(_) {
+		root.on("mouseleave", function(_) {
 			mouseLeft = true;
 			leftCount++;
 			var k = leftCount;
@@ -109,7 +109,7 @@ class FileTree extends hide.ui.View<{ root : String, opened : Array<String> }> {
 					lastOpen = null;
 				},1000);
 		});
-		e.contextmenu(function(e) {
+		root.contextmenu(function(e) {
 			var current = tree.getCurrentOver();
 			if( current == null ) return;
 			tree.setSelection([current]);

+ 3 - 3
hide/view/Image.hx

@@ -5,8 +5,8 @@ class Image extends FileView {
 	var bmp : h2d.Bitmap;
 	var scene : hide.comp.Scene;
 
-	override function onDisplay( e : Element ) {
-		scene = new hide.comp.Scene(e);
+	override function onDisplay() {
+		scene = new hide.comp.Scene(root);
 		scene.onReady = function() {
 			scene.loadTexture(state.path, function(t) {
 				bmp = new h2d.Bitmap(h2d.Tile.fromTexture(t), scene.s2d);
@@ -20,7 +20,7 @@ class Image extends FileView {
 		var scale = Math.min(1,Math.min(contentWidth / bmp.tile.width, contentHeight / bmp.tile.height));
 		bmp.setScale(scale * js.Browser.window.devicePixelRatio);
 		bmp.x = (scene.s2d.width - Std.int(bmp.tile.width * bmp.scaleX)) >> 1;
-		bmp.y = (scene.s2d.height - Std.int(bmp.tile.height * bmp.scaleY)) >> 1;		
+		bmp.y = (scene.s2d.height - Std.int(bmp.tile.height * bmp.scaleY)) >> 1;
 	}
 
 	static var _ = FileTree.registerExtension(Image,["png","jpg","jpeg","gif"],{ icon : "picture-o" });

+ 42 - 15
hide/view/Model.hx

@@ -6,21 +6,30 @@ class Model extends FileView {
 	var obj : h3d.scene.Object;
 	var scene : hide.comp.Scene;
 	var control : h3d.scene.CameraController;
-	var tree : hide.comp.IconTree;
+	var tree : hide.comp.SceneTree;
 	var overlay : Element;
+	var properties : hide.comp.PropsEditor;
 
-	override function onDisplay( e : Element ) {
-		e.html('
+	override function onDisplay() {
+		root.html('
 			<div class="flex vertical">
 				<div class="toolbar"></div>
-				<div class="scene">
-					<div class="hide-scene-layer hide-scroll"></div>
+				<div class="flex">
+					<div class="scene">
+						<div class="hide-scroll hide-scene-layer">
+							<div class="tree"></div>
+						</div>
+					</div>
+					<div class="props">
+					</div>
 				</div>
 			</div>
 		');
-		tools = new hide.comp.Toolbar(e.find(".toolbar"));
-		overlay = e.find(".hide-scene-layer");
-		scene = new hide.comp.Scene(e.find(".scene"));
+		tools = new hide.comp.Toolbar(root.find(".toolbar"));
+		overlay = root.find(".hide-scene-layer .tree");
+		properties = new hide.comp.PropsEditor(root.find(".props"));
+		properties.saveDisplayKey = "Model";
+		scene = new hide.comp.Scene(root.find(".scene"));
 		scene.onReady = init;
 	}
 
@@ -29,8 +38,21 @@ class Model extends FileView {
 
 		new h3d.scene.Object(scene.s3d).addChild(obj);
 		control = new h3d.scene.CameraController(scene.s3d);
-		tree = new hide.comp.SceneTree(obj,overlay, obj.name != null);
-		resetCamera();
+		tree = new hide.comp.SceneTree(obj, overlay, obj.name != null);
+		tree.onSelectMaterial = function(m) {
+			properties.clear();
+			properties.addMaterial(m);
+		}
+
+		this.saveDisplayKey = "Model:"+state.path;
+		var cam = getDisplayState("Camera");
+		if( cam == null )
+			scene.resetCamera(obj, 1.5);
+		else {
+			scene.s3d.camera.pos.set(cam.x, cam.y, cam.z);
+			scene.s3d.camera.target.set(cam.tx, cam.ty, cam.tz);
+		}
+		control.loadFromCamera();
 
 		var anims = listAnims();
 		if( anims.length > 0 ) {
@@ -53,10 +75,20 @@ class Model extends FileView {
 		}
 
 		scene.init(props);
+		scene.onUpdate = update;
+		tools.addButton("video-camera", "Reset Camera", function() {
+			scene.resetCamera(obj,1.5);
+			control.loadFromCamera();
+		});
 		//tools.addButton("cube","Test");
 		//tools.addToggle("bank","Test toggle");
 	}
 
+	function update(dt:Float) {
+		var cam = scene.s3d.camera;
+		saveDisplayState("Camera", { x : cam.pos.x, y : cam.pos.y, z : cam.pos.z, tx : cam.target.x, ty : cam.target.y, tz : cam.target.z });
+	}
+
 	function listAnims() {
 		var dirs : Array<String> = props.get("hmd.animPaths");
 		if( dirs == null ) dirs = [];
@@ -74,11 +106,6 @@ class Model extends FileView {
 		return anims;
 	}
 
-	function resetCamera() {
-		scene.resetCamera(obj);
-		control.loadFromCamera();
-	}
-
 	static var _ = FileTree.registerExtension(Model,["hmd","fbx","scn"],{ icon : "cube" });
 
 }

+ 12 - 6
hide/view/Particles3D.hx

@@ -29,10 +29,16 @@ class Particles3D extends FileView {
 		return haxe.io.Bytes.ofString(ide.toJSON(p.save()));
 	}
 
-	override function onDisplay( e : Element ) {
-		properties = new hide.comp.PropsEditor(e, undo);
-		properties.saveKey = "particles3D";
-		scene = new hide.comp.Scene(properties.content);
+	override function onDisplay() {
+		root.html('
+			<div class="flex">
+				<div class="scene"></div>
+				<div class="props"></div>
+			</div>
+		');
+		properties = new hide.comp.PropsEditor(root.find(".props"), undo);
+		properties.saveDisplayKey = "particles3D";
+		scene = new hide.comp.Scene(root.find(".scene"));
 		scene.onReady = init;
 	}
 
@@ -141,7 +147,7 @@ class Particles3D extends FileView {
 		});
 		e.find("[field=emitLoop]").change(function(_) parts.currentTime = 0);
 		properties.add(e, g);
-		properties.addMaterial( parts.materials[Lambda.indexOf({ iterator : parts.getGroups },g)], g.getMaterialProps(), e.find(".material > .content") );
+		properties.addMaterial( parts.materials[Lambda.indexOf({ iterator : parts.getGroups },g)], e.find(".material > .content") );
 	}
 
 	function init() {
@@ -187,7 +193,7 @@ class Particles3D extends FileView {
 			var g = parts.addGroup();
 			g.name = "Group#" + Lambda.count({ iterator : parts.getGroups });
 			addGroup(g);
-			extra.appendTo(properties.panel);
+			extra.appendTo(properties.root);
 		}, null);
 	}
 

+ 4 - 4
hide/view/Script.hx

@@ -5,11 +5,11 @@ class Script extends FileView {
 	var editor : monaco.Editor;
 	var originData : String;
 
-	override function onDisplay( e : Element ) {
+	override function onDisplay() {
 
 		if( monaco.Editor == null ) {
-			e.html("<div class='hide-loading'></div>");
-			haxe.Timer.delay(function() { e.html(''); onDisplay(e); }, 100);
+			root.html("<div class='hide-loading'></div>");
+			haxe.Timer.delay(rebuild, 100);
 			return;
 		}
 		var lang = switch( extension ) {
@@ -20,7 +20,7 @@ class Script extends FileView {
 		default: "text";
 		}
 		originData = sys.io.File.getContent(getPath());
-		editor = monaco.Editor.create(e[0],{
+		editor = monaco.Editor.create(root[0],{
 			value : originData,
 			language : lang,
 			automaticLayout : true,

+ 3 - 3
hide/view/Sound.hx

@@ -2,11 +2,11 @@ package hide.view;
 
 class Sound extends FileView {
 
-	override function onDisplay( e : Element ) {
+	override function onDisplay() {
 		var path = getPath();
-		new Element('<audio ${ide.initializing ? '' : 'autoplay="autoplay"'} controls="controls"><source src="file://${getPath()}"/></audio>').appendTo(e);	
+		root.html('<audio ${ide.initializing ? '' : 'autoplay="autoplay"'} controls="controls"><source src="file://${getPath()}"/></audio>');
 	}
-	
+
 	static var _ = {
 		FileTree.registerExtension(Sound,["wav"],{ icon : "volume-up" });
 		FileTree.registerExtension(Sound,["mp3","ogg"],{ icon : "music" });

+ 2 - 2
hide/view/Unknown.hx

@@ -2,8 +2,8 @@ package hide.view;
 
 class Unknown extends hide.ui.View<{}> {
 
-	override function onDisplay( e : Element ) {
-		e.html('Component is no longer available <br/><pre>' + haxe.Json.stringify(state) + '</pre>');
+	override function onDisplay() {
+		root.html('Component is no longer available <br/><pre>' + haxe.Json.stringify(state) + '</pre>');
 	}
 
 	static var _ = hide.ui.View.register(Unknown);