Explorar el Código

more prefab, added array props, prefab auto-sync

ncannasse hace 7 años
padre
commit
d5e36c9249

+ 3 - 0
bin/style.css

@@ -173,6 +173,9 @@ input[type=checkbox]:checked:after {
   display: inline-block;
   width: 155px;
 }
+.hide-range input[type=range].small {
+  width: 143px;
+}
 .hide-range input[type=text] {
   display: inline-block;
   width: 34px;

+ 5 - 0
bin/style.less

@@ -190,6 +190,11 @@ input[type=checkbox] {
 		display : inline-block;
 		width: 155px;
 	}
+
+	input[type=range].small {
+		width: 143px;
+	}
+
 	input[type=text] {
 		display : inline-block;
 		width: 34px;

+ 73 - 18
hide/comp/PropsEditor.hx

@@ -3,6 +3,7 @@ package hide.comp;
 enum PropType {
 	PInt( ?min : Int, ?max : Int );
 	PFloat( ?min : Float, ?max : Float );
+	PVec( n : Int );
 	PBool;
 	PTexture;
 	PUnsupported( debug : String );
@@ -11,6 +12,7 @@ enum PropType {
 class PropsEditor extends Component {
 
 	public var undo : hide.ui.UndoHistory;
+	public var lastChange : Float = 0.;
 	var fields : Array<PropsField>;
 
 	public function new(root,?undo) {
@@ -59,7 +61,17 @@ class PropsEditor extends Component {
 			case PTexture:
 				new Element('<input type="texturepath" field="${p.name}">').appendTo(def);
 			case PUnsupported(text):
-				new Element('<font color="red">'+StringTools.htmlEscape(text)+'</font>').appendTo(def);
+				new Element('<font color="red">' + StringTools.htmlEscape(text) + '</font>').appendTo(def);
+			case PVec(n):
+				var isColor = p.name.toLowerCase().indexOf("color") >= 0;
+				var names = isColor ? ["r", "g", "b", "a"] : ["x", "y", "z", "w"];
+				for( i in 0...n ) {
+					var div = new Element('<div>').appendTo(def);
+					new Element('<span>${names[i]} </span>').appendTo(div);
+					var e = new Element('<input type="range" class="small" field="${p.name}.$i">').appendTo(div);
+					e.attr("min", isColor ? "0" : "-1");
+					e.attr("max", "1");
+				}
 			}
 		}
 		return add(e, context);
@@ -117,7 +129,10 @@ class PropsEditor extends Component {
 		// init input reflection
 		for( f in e.find("[field]").elements() ) {
 			var f = new PropsField(this, f, context);
-			if( onChange != null ) f.onChange = function(undo) onChange(@:privateAccess f.fname);
+			f.onChange = function(undo) {
+				lastChange = haxe.Timer.stamp();
+				if( onChange != null ) onChange(@:privateAccess f.fname);
+			};
 			fields.push(f);
 		}
 
@@ -148,19 +163,19 @@ class PropsField extends Component {
 		this.context = context;
 		Reflect.setField(f[0],"propsField", this);
 		fname = f.attr("field");
-		current = Reflect.getProperty(context, fname);
+		current = getFieldValue();
 		switch( f.attr("type") ) {
 		case "checkbox":
 			f.prop("checked", current);
 			f.change(function(_) {
-				props.undo.change(Field(context, fname, current), function() {
+				undo(function() {
 					var f = resolveField();
-					f.current = Reflect.getProperty(f.context, fname);
+					f.current = getFieldValue();
 					f.root.prop("checked", f.current);
 					f.onChange(true);
 				});
 				current = f.prop("checked");
-				Reflect.setProperty(context, fname, current);
+				setFieldValue(current);
 				onChange(false);
 			});
 			return;
@@ -169,14 +184,14 @@ class PropsField extends Component {
 			tselect = new hide.comp.TextureSelect(f);
 			tselect.value = current;
 			tselect.onChange = function() {
-				props.undo.change(Field(context, fname, current), function() {
+				undo(function() {
 					var f = resolveField();
-					f.current = Reflect.getProperty(f.context, fname);
+					f.current = getFieldValue();
 					f.tselect.value = f.current;
 					f.onChange(true);
 				});
 				current = tselect.value;
-				Reflect.setProperty(context, fname, current);
+				setFieldValue(current);
 				onChange(false);
 			}
 			return;
@@ -185,14 +200,14 @@ class PropsField extends Component {
 			tselect = new hide.comp.TextureSelect(f);
 			tselect.path = current;
 			tselect.onChange = function() {
-				props.undo.change(Field(context, fname, current), function() {
+				undo(function() {
 					var f = resolveField();
-					f.current = Reflect.getProperty(f.context, fname);
+					f.current = getFieldValue();
 					f.tselect.path = f.current;
 					f.onChange(true);
 				});
 				current = tselect.path;
-				Reflect.setProperty(context, fname, current);
+				setFieldValue(current);
 				onChange(false);
 			}
 			return;
@@ -201,14 +216,14 @@ class PropsField extends Component {
 			fselect = new hide.comp.FileSelect(f, ["hmd", "fbx"]);
 			fselect.path = current;
 			fselect.onChange = function() {
-				props.undo.change(Field(context, fname, current), function() {
+				undo(function() {
 					var f = resolveField();
-					f.current = Reflect.getProperty(f.context, fname);
+					f.current = getFieldValue();
 					f.fselect.path = f.current;
 					f.onChange(true);
 				});
 				current = fselect.path;
-				Reflect.setProperty(context, fname, current);
+				setFieldValue(current);
 				onChange(false);
 			};
 			return;
@@ -258,7 +273,47 @@ class PropsField extends Component {
 				setVal(newVal);
 			});
 		}
+	}
+
+	function getAccess() : { obj : Dynamic, index : Int, name : String } {
+		var obj : Dynamic = context;
+		var path = fname.split(".");
+		var field = path.pop();
+		for( p in path ) {
+			var index = Std.parseInt(p);
+			if( index != null )
+				obj = obj[index];
+			else
+				obj = Reflect.getProperty(obj, p);
+		}
+		var index = Std.parseInt(field);
+		if( index != null )
+			return { obj : obj, index : index, name : null };
+		return { obj : obj, index : -1, name : field };
+	}
+
+
+	function getFieldValue() {
+		var a = getAccess();
+		if( a.name != null )
+			return Reflect.getProperty(a.obj, a.name);
+		return a.obj[a.index];
+	}
+
+	function setFieldValue( value : Dynamic ) {
+		var a = getAccess();
+		if( a.name != null )
+			Reflect.setProperty(a.obj, a.name, value);
+		else
+			a.obj[a.index] = value;
+	}
 
+	function undo( f : Void -> Void ) {
+		var a = getAccess();
+		if( a.name != null )
+			props.undo.change(Field(a.obj, a.name, current), f);
+		else
+			props.undo.change(Array(a.obj, a.index, current), f);
 	}
 
 	function setVal(v) {
@@ -273,9 +328,9 @@ class PropsField extends Component {
 			tempChange = false;
 			if( beforeTempChange == null ) beforeTempChange = { value : current };
 		} else {
-			props.undo.change(Field(context, fname, current), function() {
+			undo(function() {
 				var f = resolveField();
-				var v = Reflect.getProperty(f.context, fname);
+				var v = getFieldValue();
 				f.current = v;
 				f.root.val(v);
 				f.root.parent().find("input[type=text]").val(v);
@@ -283,7 +338,7 @@ class PropsField extends Component {
 			});
 		}
 		current = v;
-		Reflect.setProperty(context, fname, v);
+		setFieldValue(v);
 		onChange(false);
 	}
 

+ 6 - 1
hide/prefab/Prefab.hx

@@ -100,7 +100,7 @@ class Prefab {
 		if( v.source != null )
 			p.source = v.source;
 		p.load(v);
-		var children : Array<Dynamic> = p.children;
+		var children : Array<Dynamic> = v.children;
 		if( children != null )
 			for( v in children )
 				loadRec(v, p);
@@ -129,9 +129,14 @@ class Prefab {
 	}
 
 	public function getOpt<T:Prefab>( cl : Class<T>, ?name : String ) : T {
+		var parts = name == null ? null : name.split(".");
 		for( c in children ) {
 			if( (name == null || c.name == name) && Std.is(c, cl) )
 				return cast c;
+			if( parts != null && parts.length > 1 && c.name == parts[0] ) {
+				parts.shift();
+				return c.getOpt(cl, parts.join("."));
+			}
 			var p = c.getOpt(cl, name);
 			if( p != null )
 				return p;

+ 18 - 0
hide/prefab/Settings.hx

@@ -23,6 +23,22 @@ class Settings extends Prefab {
 		Reflect.deleteField(o, "ignoredFields");
 	}
 
+	public function apply( to : {} ) {
+		var fields = Reflect.fields(data);
+		fields.remove("type");
+		fields.remove("children");
+		fields.remove("name");
+		for( f in fields ) {
+			var prev : Dynamic = Reflect.getProperty(to, f);
+			var value : Dynamic = Reflect.field(data, f);
+			if( prev != null && Std.is(prev, h3d.Vector) && Std.is(value, Array) ) {
+				var val : Array<Float> = value;
+				value = new h3d.Vector(value[0], value[1], value[2], value[3]);
+			}
+			Reflect.setProperty(to, f, value);
+		}
+	}
+
 	override function getHideProps() : HideProps {
 		return { icon : "cogs", name : "Settings" };
 	}
@@ -67,6 +83,8 @@ class Settings extends Prefab {
 
 		var fields = Reflect.fields(data);
 		fields.remove("type");
+		fields.remove("name");
+		fields.remove("children");
 		fields.remove("modelType");
 		fields.remove("ignoredFields");
 		var changed = false;

+ 35 - 3
hide/tools/TypesCache.hx

@@ -128,11 +128,12 @@ class TypesCache {
 				case DPackage(p):
 					pack = p.length == 0 ? "" : p.join(".") + ".";
 				case DClass(c) if( c.extend != null && (c.extend.match(CTPath(["hxsl", "Shader"])) || c.extend.match(CTPath(["h3d", "shader", "ScreenShader"]))) ):
-					var shader = try ide.shaderLoader.loadSharedShader(pack + c.name) catch( e : hxsl.Ast.Error ) null;
+					var error = null;
+					var shader = try ide.shaderLoader.loadSharedShader(pack + c.name, path) catch( e : hxsl.Ast.Error ) { error = e.toString(); null; };
 					var fields = [];
 					var fmap = new Map();
 					if( shader == null )
-						fields.push({ name : "", t : PUnsupported("Failed to load this shader"), def : null });
+						fields.push({ name : "", t : PUnsupported("Failed to load this shader"+(error == null ? "" : " ("+error+")")), def : null });
 					else {
 						for( v in shader.shader.data.vars ) {
 							if( v.kind != Param ) continue;
@@ -144,7 +145,8 @@ class TypesCache {
 						}
 						for( i in shader.inits ) {
 							var fl = fmap.get(i.v.name);
-							fl.def = hxsl.Ast.Tools.evalConst(i.e);
+							if( !fl.t.match(PUnsupported(_)) )
+								fl.def = evalConst(i.e);
 						}
 					}
 					file.models.push({ id : pack + c.name, kind : Shader, file : file, fields : fields });
@@ -167,6 +169,32 @@ class TypesCache {
 		return file;
 	}
 
+	function evalConst( e : hxsl.Ast.TExpr ) : Dynamic {
+		return switch( e.e ) {
+		case TConst(c):
+			switch( c ) {
+			case CNull: null;
+			case CBool(b): b;
+			case CInt(i): i;
+			case CFloat(f): f;
+			case CString(s): s;
+			}
+		case TCall({ e : TGlobal(Vec2 | Vec3 | Vec4) }, args):
+			var vals = [for( a in args ) evalConst(a)];
+			if( vals.length == 1 )
+				switch( e.t ) {
+				case TVec(n, _):
+					for( i in 0...n - 1 ) vals.push(vals[0]);
+					return vals;
+				default:
+					throw "assert";
+				}
+			return vals;
+		default:
+			throw "Unhandled constant init " + hxsl.Printer.toString(e);
+		}
+	}
+
 	function addFile( t : TypeFile ) {
 		hfiles.set(t.path, t);
 		if( t.watch == null ) {
@@ -225,6 +253,8 @@ class TypesCache {
 			return false;
 		case PTexture:
 			return null;
+		case PVec(n):
+			return [for( i in 0...n ) 0.];
 		case PUnsupported(_):
 			return null;
 		}
@@ -260,6 +290,8 @@ class TypesCache {
 			PBool;
 		case TSampler2D:
 			PTexture;
+		case TVec(n, VFloat):
+			PVec(n);
 		default:
 			PUnsupported(hxsl.Ast.Tools.toString(v.type));
 		}

+ 5 - 0
hide/ui/UndoHistory.hx

@@ -2,6 +2,7 @@ package hide.ui;
 
 enum HistoryElement {
 	Field( obj : Dynamic, field : String, oldValue : Dynamic );
+	Array( obj : Array<Dynamic>, field : Int, oldValue : Dynamic );
 	Custom( undoRedo : Bool -> Void );
 }
 
@@ -44,6 +45,10 @@ class UndoHistory {
 			var curValue = Reflect.field(obj, field);
 			other.push({ h : Field(obj, field, curValue), id : e.id, callb : e.callb });
 			Reflect.setProperty(obj, field, value);
+		case Array(arr, index, value):
+			var curValue = arr[index];
+			other.push({ h : Array(arr, index, curValue), id : e.id, callb : e.callb });
+			arr[index] = value;
 		case Custom(f):
 			other.push(e);
 			f(other == redoElts);

+ 63 - 4
hide/view/Prefab.hx

@@ -41,12 +41,31 @@ class Prefab extends FileView {
 
 	var curEdit : EditContext;
 
+	// autoSync
+	var autoSync : Bool;
+	var currentVersion : Int = 0;
+	var lastSyncChange : Float = 0.;
+	var currentSign : String;
+
 	override function getDefaultContent() {
 		return haxe.io.Bytes.ofString(ide.toJSON(new hide.prefab.Library().save()));
 	}
 
+	override function onFileChanged(wasDeleted:Bool) {
+		if( !wasDeleted ) {
+			// double check if content has changed
+			var content = sys.io.File.getContent(getPath());
+			var sign = haxe.crypto.Md5.encode(content);
+			if( sign == currentSign )
+				return;
+		}
+		super.onFileChanged(wasDeleted);
+	}
+
 	override function save() {
-		sys.io.File.saveContent(getPath(), ide.toJSON(data.save()));
+		var content = ide.toJSON(data.save());
+		currentSign = haxe.crypto.Md5.encode(content);
+		sys.io.File.saveContent(getPath(), content);
 	}
 
 	override function onDisplay() {
@@ -75,6 +94,7 @@ class Prefab extends FileView {
 		scene = new hide.comp.Scene(root.find(".scene"));
 		scene.onReady = init;
 		tree = new hide.comp.IconTree(root.find(".tree"));
+		currentVersion = undo.currentID;
 	}
 
 	function refresh( ?callb ) {
@@ -141,7 +161,9 @@ class Prefab extends FileView {
 
 	function init() {
 		data = new hide.prefab.Library();
-		data.load(haxe.Json.parse(sys.io.File.getContent(getPath())));
+		var content = sys.io.File.getContent(getPath());
+		data.load(haxe.Json.parse(content));
+		currentSign = haxe.crypto.Md5.encode(content);
 
 		context = new hide.prefab.Context();
 		context.onError = function(e) {
@@ -197,6 +219,10 @@ class Prefab extends FileView {
 			scene.speed = v;
 		}, scene.speed);
 
+		tools.addToggle("refresh", "Auto synchronize", function(b) {
+			autoSync = b;
+		});
+
 		// BUILD scene tree
 
 		function makeItem(o:PrefabElement) : hide.comp.IconTree.IconTreeItem<PrefabElement> {
@@ -205,13 +231,14 @@ class Prefab extends FileView {
 				data : o,
 				text : o.name,
 				icon : "fa fa-"+p.icon,
-				children : o.iterator().hasNext(),
+				children : o.children.length > 0,
 				state : { opened : true },
 			};
 		}
 		tree.get = function(o:PrefabElement) {
 			var objs = o == null ? data.children : Lambda.array(o);
-			return [for( o in objs ) makeItem(o)];
+			var out = [for( o in objs ) makeItem(o)];
+			return out;
 		};
 		tree.root.parent().contextmenu(function(e) {
 			e.preventDefault();
@@ -251,6 +278,7 @@ class Prefab extends FileView {
 
 			new hide.comp.ContextMenu([
 				{ label : "New...", menu : registered },
+				{ label : "Rename", enabled : current != null, click : function() tree.editNode(current) },
 				{ label : "Delete", enabled : current != null, click : function() {
 					function deleteRec(roots:Array<PrefabElement>) {
 						for( o in roots ) {
@@ -281,6 +309,32 @@ class Prefab extends FileView {
 			undo.change(Field(e, "name", oldName), function() tree.refresh());
 			return true;
 		};
+		tree.onAllowMove = function(_, _) {
+			return true;
+		};
+		tree.onMove = function(e, to, index) {
+			if( to == null ) to = data;
+			var prev = e.parent;
+			var prevIndex = prev.children.indexOf(e);
+			e.parent = to;
+			to.children.remove(e);
+			to.children.insert(index, e);
+			undo.change(Custom(function(undo) {
+				if( undo ) {
+					e.parent = prev;
+					prev.children.remove(e);
+					prev.children.insert(prevIndex, e);
+				} else {
+					e.parent = to;
+					to.children.remove(e);
+					to.children.insert(index, e);
+				}
+				refresh();
+			}));
+			refresh();
+			return true;
+		};
+
 		scene.onResize = function() {
 			scene.s2d.x = scene.s2d.width >> 1;
 			scene.s2d.y = scene.s2d.height >> 1;
@@ -306,6 +360,11 @@ class Prefab extends FileView {
 				lightDirection.z
 			);
 		}
+		if( autoSync && (currentVersion != undo.currentID || lastSyncChange != properties.lastChange) ) {
+			save();
+			lastSyncChange = properties.lastChange;
+			currentVersion = undo.currentID;
+		}
 	}
 
 	static var _ = FileTree.registerExtension(Prefab,["prefab"],{ icon : "sitemap", createNew : "Prefab" });