瀏覽代碼

more work on particles 3D

Nicolas Cannasse 8 年之前
父節點
當前提交
73b96ef8fb
共有 10 個文件被更改,包括 292 次插入71 次删除
  1. 1 1
      bin/style.css
  2. 1 1
      bin/style.less
  3. 50 0
      hide/comp/FileSelect.hx
  4. 1 0
      hide/comp/PropsEditor.hx
  5. 40 5
      hide/comp/Scene.hx
  6. 14 36
      hide/comp/TextureSelect.hx
  7. 15 5
      hide/ui/Ide.hx
  8. 1 1
      hide/view/FileTree.hx
  9. 2 19
      hide/view/Model.hx
  10. 167 3
      hide/view/Particles3D.hx

+ 1 - 1
bin/style.css

@@ -46,7 +46,7 @@ select:hover,
 input:hover {
   border-color: #888;
 }
-input[type=texture] {
+input.file {
   cursor: pointer;
 }
 input[type=button] {

+ 1 - 1
bin/style.less

@@ -45,7 +45,7 @@ select, input {
 	}
 }
 
-input[type=texture] {
+input.file {
 	cursor : pointer;
 }
 

+ 50 - 0
hide/comp/FileSelect.hx

@@ -0,0 +1,50 @@
+package hide.comp;
+
+class FileSelect extends Component {
+
+	public var path(default, set) : String;
+
+	public function new(root, extensions) {
+		super(root);
+		path = null;
+		root.mousedown(function(e) {
+			e.preventDefault();
+			if( e.button == 0 ) {
+				ide.chooseFile(extensions, function(path) {
+					if( path == null ) return; // cancel
+					this.path = path;
+					onChange();
+				});
+			}
+		});
+		root.contextmenu(function(e) {
+			e.preventDefault();
+			var fpath = getFullPath();
+			new ContextMenu([
+				{ label : "View", enabled : fpath != null, click : function() ide.openFile(fpath) },
+				{ label : "Clear", enabled : path != null, click : function() { path = null; onChange(); } },
+			]);
+			return false;
+		});
+	}
+
+	public function getFullPath() {
+		if( path == null )
+			return null;
+		var fpath = ide.getPath(path);
+		if( sys.FileSystem.exists(fpath) )
+			return fpath;
+		return null;
+	}
+
+	function set_path(p:String) {
+		var text = p == null ? "-- select --" : (sys.FileSystem.exists(ide.getPath(p)) ? "" : "[NOT FOUND] ") + p;
+		root.val(text);
+		root.attr("title", p == null ? "" : p);
+		return this.path = p;
+	}
+
+	public dynamic function onChange() {
+	}
+
+}

+ 1 - 0
hide/comp/PropsEditor.hx

@@ -133,6 +133,7 @@ class PropsField extends Component {
 			});
 			return;
 		case "texture":
+			f.addClass("file");
 			tselect = new hide.comp.TextureSelect(f);
 			tselect.value = current;
 			tselect.onChange = function() {

+ 40 - 5
hide/comp/Scene.hx

@@ -171,7 +171,7 @@ class Scene extends Component implements h3d.IDrawable {
 		engine.init();
 	}
 
-	public function init( props : hide.ui.Props ) {
+	public function init( props : hide.ui.Props, ?root : h3d.scene.Object ) {
 		var autoHide : Array<String> = props.get("scene.autoHide");
 		function initRec( obj : h3d.scene.Object ) {
 			if( autoHide.indexOf(obj.name) >= 0 )
@@ -179,8 +179,11 @@ class Scene extends Component implements h3d.IDrawable {
 			for( o in obj )
 				initRec(o);
 		}
-		initRec(s3d);
-		engine.backgroundColor = Std.parseInt("0x"+props.get("scene.backgroundColor").substr(1)) | 0xFF000000;
+		if( root == null ) {
+			root = s3d;
+			engine.backgroundColor = Std.parseInt("0x"+props.get("scene.backgroundColor").substr(1)) | 0xFF000000;
+		}
+		initRec(root);
 	}
 
 	function setCurrent() {
@@ -219,6 +222,37 @@ class Scene extends Component implements h3d.IDrawable {
 		});
 	}
 
+	public function listAnims( path : String ) {
+		var props = hide.ui.Props.loadForFile(ide, path);
+
+		var dirs : Array<String> = props.get("hmd.animPaths");
+		if( dirs == null ) dirs = [];
+		dirs = [for( d in dirs ) ide.resourceDir + d];
+
+		var parts = path.split("/");
+		parts.pop();
+		dirs.unshift(ide.getPath(parts.join("/")));
+
+		var anims = [];
+
+		var lib = loadHMD(path, false);
+		if( lib.header.animations.length > 0 )
+			anims.push(ide.getPath(path));
+
+		for( dir in dirs )
+			for( f in try sys.FileSystem.readDirectory(dir) catch( e : Dynamic ) [] )
+				if( StringTools.startsWith(f,"Anim_") )
+					anims.push(dir+"/"+f);
+		return anims;
+	}
+
+	public function animationName( path : String ) {
+		var name = path.split("/").pop();
+		if( StringTools.startsWith(name, "Anim_") )
+			name = name.substr(5);
+		return name.substr(0, -4);
+	}
+
 	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() ) {
@@ -296,7 +330,8 @@ class Scene extends Component implements h3d.IDrawable {
 
 	function loadHMD( path : String, isAnimation : Bool ) {
 		var fullPath = ide.getPath(path);
-		var hmd = hmdCache.get(fullPath);
+		var key = fullPath;
+		var hmd = hmdCache.get(key);
 
 		if( hmd != null )
 			return hmd;
@@ -319,7 +354,7 @@ class Scene extends Component implements h3d.IDrawable {
 			hmd = e.toHmd();
 		}
 
-		hmdCache.set(fullPath, hmd);
+		hmdCache.set(key, hmd);
 		return hmd;
 	}
 

+ 14 - 36
hide/comp/TextureSelect.hx

@@ -1,50 +1,28 @@
 package hide.comp;
 
-class TextureSelect extends Component {
+class TextureSelect extends FileSelect {
 
-	var engine : h3d.Engine;
 	public var value(default, set) : h3d.mat.Texture;
 
 	public function new(root) {
-		super(root);
-		root.mousedown(function(e) {
-			e.preventDefault();
-			if( e.button == 0 ) {
-				ide.chooseFile(["jpg", "jpeg", "gif"], function(path) {
-					value = Scene.getNearest(root).loadTextureFile("", path);
-					onChange();
-				});
-			}
-		});
-		root.contextmenu(function(e) {
-			e.preventDefault();
-			var path = getPath();
-			new ContextMenu([
-				{ label : "View", enabled : path != null, click : function() ide.open("hide.view.Image", {path:path}) },
-				{ label : "Clear", enabled : value != null, click : function() { value = null; onChange(); } },
-			]);
-			return false;
-		});
+		super(root,["jpg", "jpeg", "gif"]);
 	}
 
-	public function getPath() {
-		var t = value;
-		if( t == null || t.name == null )
-			return null;
-		var path = ide.getPath(t.name);
-		if( sys.FileSystem.exists(path) )
-			return path;
-		return null;
+	override function set_path(p:String) {
+		super.set_path(p);
+		if( p == null )
+			value = null;
+		else if( value == null || value.name != p )
+			value = Scene.getNearest(root).loadTextureFile("", p);
+		return p;
 	}
 
 	function set_value(t:h3d.mat.Texture) {
-		var path = t == null ? "-- select --" : t.name == null ? "????" : (sys.FileSystem.exists(ide.getPath(t.name)) ? "" : "[NOT FOUND]") + t.name;
-		root.val(path);
-		root.attr("title", t == null ? "" : t.name);
-		return value = t;
-	}
-
-	public dynamic function onChange() {
+		value = t;
+		var p = value == null ? null : value.name;
+		if( p != path )
+			this.path = p;
+		return t;
 	}
 
 }

+ 15 - 5
hide/ui/Ide.hx

@@ -145,7 +145,7 @@ class Ide {
 			layout.registerComponent(vcl.name,function(cont,state) {
 				var view = Type.createInstance(vcl.cl,[state]);
 				view.setContainer(cont);
-				try view.onDisplay() catch( e : Dynamic ) js.Browser.alert(vcl.name+":"+e);
+				try view.onDisplay() catch( e : Dynamic ) error(vcl.name+":"+e);
 			});
 
 		layout.init();
@@ -198,12 +198,22 @@ class Ide {
 		return false;
 	}
 
+	public function cleanObject( v : Dynamic ) {
+		for( f in Reflect.fields(v) )
+			if( Reflect.field(v, f) == null )
+				Reflect.deleteField(v, f);
+	}
+
 	public function getPath( relPath : String ) {
 		if( haxe.io.Path.isAbsolute(relPath) )
 			return relPath;
 		return resourceDir+"/"+relPath;
 	}
 
+	public function error( e : Dynamic ) {
+		js.Browser.alert(e);
+	}
+
 	function get_projectDir() return ideProps.currentProject.split("\\").join("/");
 	function get_resourceDir() return projectDir+"/res";
 
@@ -230,9 +240,9 @@ class Ide {
 				r.load(sys.io.File.getContent(path));
 				renderers.unshift(r);
 			} catch( e : Dynamic ) {
-				js.Browser.alert(e);
+				error(e);
 			}
-			r.onError = function(msg) js.Browser.alert(msg);
+			r.onError = function(msg) error(msg);
 		}
 
 		var render = renderers[0];
@@ -333,9 +343,9 @@ class Ide {
 		for( c in comps.elements() ) {
 			var cname = c.attr("component");
 			var cl = Type.resolveClass(cname);
-			if( cl == null ) js.Browser.alert("Missing component class "+cname);
+			if( cl == null ) error("Missing component class "+cname);
 			var state = c.attr("state");
-			if( state != null ) try haxe.Json.parse(state) catch( e : Dynamic ) js.Browser.alert("Invalid state "+state+" ("+e+")");
+			if( state != null ) try haxe.Json.parse(state) catch( e : Dynamic ) error("Invalid state "+state+" ("+e+")");
 			c.click(function(_) {
 				open(cname, state == null ? null : haxe.Json.parse(state));
 			});

+ 1 - 1
hide/view/FileTree.hx

@@ -154,7 +154,7 @@ class FileTree extends FileView {
 		if( file.indexOf(".") < 0 ) file += "." + ext.extensions[0].split(".").shift();
 
 		if( sys.FileSystem.exists(fullPath + "/" + file) ) {
-			js.Browser.alert("File '" + file+"' already exists");
+			ide.error("File '" + file+"' already exists");
 			createNew(basePath, ext);
 			return;
 		}

+ 2 - 19
hide/view/Model.hx

@@ -85,11 +85,11 @@ class Model extends FileView {
 		}
 		control.loadFromCamera();
 
-		var anims = listAnims();
+		var anims = scene.listAnims(getPath());
 		if( anims.length > 0 ) {
 			var sel = tools.addSelect("play-circle");
 			var content = [for( a in anims ) {
-				var label = a.split("/").pop().substr(5).substr(0,-4);
+				var label = scene.animationName(a);
 				if( StringTools.endsWith(label,"_loop") ) label = label.substr(0,-5);
 				{ label : label, value : a }
 			}];
@@ -143,23 +143,6 @@ class Model extends FileView {
 		}
 	}
 
-	function listAnims() {
-		var dirs : Array<String> = props.get("hmd.animPaths");
-		if( dirs == null ) dirs = [];
-		dirs = [for( d in dirs ) ide.resourceDir + d];
-
-		var parts = getPath().split("/");
-		parts.pop();
-		dirs.unshift(parts.join("/"));
-
-		var anims = [];
-		for( dir in dirs )
-			for( f in sys.FileSystem.readDirectory(dir) )
-				if( StringTools.startsWith(f,"Anim_") )
-					anims.push(dir+"/"+f);
-		return anims;
-	}
-
 	static var _ = FileTree.registerExtension(Model,["hmd","fbx","scn"],{ icon : "cube" });
 
 }

+ 167 - 3
hide/view/Particles3D.hx

@@ -22,6 +22,7 @@ class Particles3D extends FileView {
 	var parts : GpuParticles;
 	var properties : hide.comp.PropsEditor;
 	var bounds : h3d.scene.Box;
+	var model : h3d.scene.Object;
 
 	override function getDefaultContent() {
 		var p = new h3d.parts.GpuParticles();
@@ -141,13 +142,24 @@ class Particles3D extends FileView {
 				{ label : "Enable", checked : g.enable, click : function() { g.enable = !g.enable; e.find("[field=enable]").prop("checked", g.enable); } },
 				{ label : "MoveUp", enabled : index > 0, click : function() moveIndex(-1) },
 				{ label : "MoveDown", enabled : index < groups.length - 1, click : function() moveIndex(1) },
-				{ label : "Delete", click : function() { parts.removeGroup(g); e.remove(); } },
+				{ label : "Delete", click : function() {
+					parts.removeGroup(g);
+					e.remove();
+					undo.change(Custom(function(undo) {
+						if( undo )
+							parts.addGroup(g, index);
+						else
+							parts.removeGroup(g);
+						initProperties();
+					}));
+				}},
 			]);
 			ev.preventDefault();
 		});
 		e.find("[field=emitLoop]").change(function(_) parts.currentTime = 0);
-		properties.add(e, g);
-		properties.addMaterial( parts.materials[Lambda.indexOf({ iterator : parts.getGroups },g)], e.find(".material > .content") );
+		e = properties.add(e, g);
+		properties.addMaterial( parts.materials[Lambda.indexOf({ iterator : parts.getGroups }, g)], e.find(".material > .content") );
+		return e;
 	}
 
 	function init() {
@@ -173,6 +185,9 @@ class Particles3D extends FileView {
 				<h1>Manage</h1>
 				<div class="content">
 					<dl>
+					<dt>Model</dt><dd><input class="file model"/></dd>
+					<dt class="attach">Attach</dt><dd class="attach"><select/></dd>
+					<dt class="anim">Anim</dt><dd class="anim"><select/></dd>
 					<dt>Show Bounds</dt><dd><input type="checkbox" class="bounds"/></dd>
 					<dt>Enable Lights</dt><dd><input type="checkbox" class="lights" checked="checked"/></dd>
 					<dt></dt><dd><input type="button" class="new" value="New Group"/></dd>
@@ -194,7 +209,156 @@ class Particles3D extends FileView {
 			g.name = "Group#" + Lambda.count({ iterator : parts.getGroups });
 			addGroup(g);
 			extra.appendTo(properties.root);
+			undo.change(Custom(function(undo) {
+				if( undo )
+					parts.removeGroup(g);
+				else
+					parts.addGroup(g);
+				initProperties();
+			}));
 		}, null);
+
+		var pmodel = new hide.comp.FileSelect(extra.find(".model"), ["hmd", "fbx"]);
+		var props : { ?model : String, ?attach : String, ?anim : String } = @:privateAccess parts.hideProps;
+		if( props == null ) {
+			props = {};
+			@:privateAccess parts.hideProps = props;
+		}
+		pmodel.path = props.model;
+
+		var attach = extra.find(".attach select");
+		attach.change(function(_) {
+			var prev = props.attach;
+			var next = attach.val();
+			if( prev == next ) return;
+			props.attach = next;
+			undo.change(Custom(function(undo) {
+				props.attach = undo ? prev : next;
+				attach.val(props.attach == null ? '' : props.attach);
+				pmodel.onChange();
+			}));
+			pmodel.onChange();
+			attach.blur();
+		});
+
+		var anim = extra.find(".anim select");
+		anim.change(function(_) {
+			var prev = props.anim;
+			var next = anim.val();
+			if( next == "" ) next = null;
+			if( prev == next ) return;
+			props.anim = next;
+			undo.change(Custom(function(undo) {
+				props.anim = undo ? prev : next;
+				anim.val(props.anim == null ? '' : props.anim);
+				pmodel.onChange();
+			}));
+			pmodel.onChange();
+			anim.blur();
+		});
+		anim.contextmenu(function(e) {
+			e.preventDefault();
+			new hide.comp.ContextMenu([
+				{ label : "Clear", enabled : props.anim != null, click : function() {
+					var prev = props.anim;
+					props.anim = null;
+					anim.val('');
+					undo.change(Custom(function(undo) {
+						props.anim = undo ? prev : null;
+						anim.val(props.anim == null ? '' : props.anim);
+						pmodel.onChange();
+					}));
+					pmodel.onChange();
+				}},
+			]);
+		});
+
+		pmodel.onChange = function() {
+
+			extra.find(".attach").toggle(pmodel.path != null);
+			extra.find(".anim").toggle(pmodel.path != null);
+
+			if( props.model != pmodel.path ) {
+				var prev = props.model;
+				var next = pmodel.path;
+				props.model = next;
+				undo.change(Custom(function(undo) {
+					props.model = pmodel.path = undo ? prev : next;
+					pmodel.onChange();
+				}));
+			}
+
+			if( model != null ) {
+				model.remove();
+				model = null;
+			}
+
+			if( props.model == null ) {
+				props.anim = null;
+				props.attach = null;
+				anim.val('');
+				attach.val('');
+			} else {
+				try {
+					model = scene.loadModel(props.model);
+				} catch( e : Dynamic ) {
+					ide.error(e);
+					props.model = null;
+				}
+				if( model != null && props.anim != null ) {
+					try {
+						var anim = scene.loadAnimation(props.anim);
+						model.playAnimation(anim);
+					} catch( e : Dynamic ) {
+						ide.error(e);
+						props.anim = e;
+					}
+				}
+				if( model != null ) {
+					scene.s3d.addChild(model);
+					scene.init(this.props, model);
+
+					var prev = attach.val();
+					attach.html('');
+					function addRec(o:h3d.scene.Object) {
+						if( o.name != null )
+							new Element('<option value="${o.name}" ${o.name == prev ? "selected='selected'" : ""}>${o.name}</option>').appendTo(attach);
+						var s = Std.instance(o, h3d.scene.Skin);
+						if( s != null )
+							for( j in s.getSkinData().allJoints )
+								new Element('<option value="${j.name}" ${j.name == prev ? "selected='selected'" : ""}>${j.name}</option>').appendTo(attach);
+						for( s in o )
+							addRec(s);
+					}
+					addRec(model);
+
+					var prev = anim.val();
+					anim.html('');
+					var anims = scene.listAnims(props.model);
+					new Element('<option value="">-- select --</option>').appendTo(anim);
+					for( a in anims ) {
+						var name = scene.animationName(a);
+						new Element('<option value="$a" ${a == prev ? "selected='selected'" : ""}>$name</option>').appendTo(anim);
+					}
+					if( anims.length == 0 )
+						extra.find(".anim").hide();
+				}
+			}
+
+			var parent : h3d.scene.Object = null;
+			if( model != null ) {
+				parent = model;
+				if( props.attach != null )
+					parent = model.getObjectByName(props.attach);
+			}
+			parts.follow = parent;
+			ide.cleanObject(props);
+		};
+		pmodel.onChange();
+		if( props.attach != null )
+			attach.val(props.attach);
+		if( props.anim != null )
+			anim.val(props.anim);
 	}
 
 	static var _ = FileTree.registerExtension(Particles3D, ["json.particles3D"], { icon : "snowflake-o", createNew: "Particle 3D" });