Browse Source

separate range component, added animation timeline with events in model viewer

Nicolas Cannasse 8 years ago
parent
commit
8e3ee5c1ad
6 changed files with 289 additions and 121 deletions
  1. 34 23
      bin/style.css
  2. 39 25
      bin/style.less
  3. 7 65
      hide/comp/PropsEditor.hx
  4. 109 0
      hide/comp/Range.hx
  5. 7 0
      hide/comp/Toolbar.hx
  6. 93 8
      hide/view/Model.hx

+ 34 - 23
bin/style.css

@@ -122,8 +122,8 @@ input[type=checkbox]:checked:after {
   display: none;
 }
 .hide-scroll {
-  width: 100%;
-  height: 100%;
+  max-width: 100%;
+  max-height: 100%;
   overflow: auto;
 }
 ::-webkit-scrollbar {
@@ -158,7 +158,7 @@ input[type=checkbox]:checked:after {
   width: auto;
   position: absolute;
   z-index: 1;
-  height: 100%;
+  max-height: 100%;
 }
 .hide-scene-layer .jstree {
   padding-right: 5px;
@@ -166,6 +166,23 @@ input[type=checkbox]:checked:after {
 .hide-scene-layer .jstree-container-ul {
   padding-right: 5px;
 }
+.hide-range {
+  display: inline-block;
+}
+.hide-range input[type=range] {
+  display: inline-block;
+  width: 155px;
+}
+.hide-range input[type=text] {
+  display: inline-block;
+  width: 30px;
+  margin-left: 3px;
+  border-color: transparent;
+}
+.hide-range input[type=text]:hover,
+.hide-range input[type=text]:active {
+  border-color: #444;
+}
 .flex {
   width: 100%;
   height: 100%;
@@ -220,6 +237,11 @@ input[type=checkbox]:checked:after {
   border-top-width: 2px;
   border-top-color: #444;
 }
+.hide-toolbar .toggle.toggled:hover {
+  border-top-width: 1px;
+  padding-top: 3px;
+  border-top-color: white;
+}
 .hide-toolbar .button:active {
   padding-top: 3px;
   padding-bottom: 1px;
@@ -288,30 +310,19 @@ input[type=checkbox]:checked:after {
   user-select: none;
   cursor: default;
 }
-.hide-properties dd {
-  position: relative;
-  width: 200px;
-  margin-left: 10px;
-}
-.hide-properties dd input:not([type=checkbox]):not([type=number]) {
+.hide-properties input {
   width: 190px;
 }
-.hide-properties dd select {
-  width: 197px;
-}
-.hide-properties dd .range-group input[type=range] {
-  display: inline-block;
-  width: 155px;
+.hide-properties input[type=checkbox] {
+  width: 13px;
 }
-.hide-properties dd .range-group input[type=text] {
-  display: inline-block;
-  width: 30px;
-  margin-left: 3px;
-  border-color: transparent;
+.hide-properties select {
+  width: 197px;
 }
-.hide-properties dd .range-group input[type=text]:hover,
-.hide-properties dd .range-group input[type=text]:active {
-  border-color: #444;
+.hide-properties dd {
+  position: relative;
+  width: 200px;
+  margin-left: 10px;
 }
 .hide-properties dd .texture-preview {
   pointer-events: none;

+ 39 - 25
bin/style.less

@@ -126,8 +126,8 @@ input[type=checkbox] {
 #mainmenu { display : none }
 
 .hide-scroll {
-	width : 100%;
-	height : 100%;
+	max-width : 100%;
+	max-height : 100%;
 	overflow : auto;
 }
 
@@ -170,7 +170,7 @@ input[type=checkbox] {
 	width:auto;
 	position: absolute;
 	z-index: 1;
-	height: 100%;
+	max-height: 100%;
 
 	.jstree {
 		padding-right : 5px;
@@ -182,6 +182,25 @@ input[type=checkbox] {
 
 }
 
+.hide-range {
+
+	display : inline-block;
+
+	input[type=range] {
+		display : inline-block;
+		width: 155px;
+	}
+	input[type=text] {
+		display : inline-block;
+		width: 30px;
+		margin-left:3px;
+		border-color: transparent;
+	}
+	input[type=text]:hover, input[type=text]:active {
+		border-color: #444;
+	}
+}
+
 .flex {
 	width:100%;
 	height:100%;
@@ -236,6 +255,11 @@ input[type=checkbox] {
 		text-shadow: none;
 		border-top-width: 2px;
 		border-top-color: #444;
+		&:hover {
+			border-top-width: 1px;
+			padding-top: 3px;
+			border-top-color : white;
+		}
 	}
 
 	.button:active {
@@ -314,32 +338,22 @@ input[type=checkbox] {
 		cursor:default;
 	}
 
+	input {
+		width:190px;
+	}
+
+	input[type=checkbox] {
+		width:13px;
+	}
+
+	select {
+		width:197px;
+	}
+
 	dd {
 		position:relative;
 		width:200px;
 		margin-left:10px;
-		input:not([type=checkbox]):not([type=number]) {
-			width:190px;
-		}
-		select {
-			width:197px;
-		}
-
-		.range-group {
-			input[type=range] {
-				display : inline-block;
-				width: 155px;
-			}
-			input[type=text] {
-				display : inline-block;
-				width: 30px;
-				margin-left:3px;
-				border-color: transparent;
-			}
-			input[type=text]:hover, input[type=text]:active {
-				border-color: #444;
-			}
-		}
 
 		.texture-preview {
 			pointer-events: none;

+ 7 - 65
hide/comp/PropsEditor.hx

@@ -105,6 +105,7 @@ class PropsField extends Component {
 	var tselect : hide.comp.TextureSelect;
 	var fselect : hide.comp.FileSelect;
 	var viewRoot : Element;
+	var range : hide.comp.Range;
 
 	public function new(props, f, context) {
 		super(f);
@@ -178,71 +179,12 @@ class PropsField extends Component {
 			};
 			return;
 		case "range":
-
-			f.wrap('<div class="range-group"/>');
-			var p = f.parent();
-			var inputView = new Element('<input type="text">').appendTo(p);
-			var originMin = Std.parseFloat(f.attr("min"));
-			var originMax = Std.parseFloat(f.attr("max"));
-			var curMin = originMin, curMax = originMax;
-
-			function setVal( v : Float ) {
-				var tempChange = tempChange;
-				this.setVal(v);
-
-				if( tempChange )
-					return;
-
-				if( v < curMin ) {
-					curMin = Math.floor(v);
-					f.attr("min", curMin);
-				}
-				if( v > curMax ) {
-					curMax = Math.ceil(v);
-					f.attr("max", curMax);
-				}
-				if( v >= originMin && v <= originMax ) {
-					f.attr("min", originMin);
-					f.attr("max", originMax);
-					curMin = originMin;
-					curMax = originMax;
-				}
-			}
-
-			var original = current;
-			p.parent().prev("dt").contextmenu(function(e) {
-				e.preventDefault();
-				new ContextMenu([
-					{ label : "Reset", click : function() { inputView.val(""+original); inputView.change(); } },
-					{ label : "Cancel", click : function() {} },
-				]);
-				return false;
-			});
-
-			f.on("input", function(_) { tempChange = true; f.change(); });
-			inputView.keyup(function(e) {
-				if( e.keyCode == 13 || e.keyCode == 27 ) {
-					inputView.blur();
-					inputView.val(current);
-					return;
-				}
-				var v = Std.parseFloat(inputView.val());
-				if( Math.isNaN(v) ) return;
-				setVal(v);
-				f.val(v);
-			});
-
-			f.val(current);
-			inputView.val(current);
-
-			f.change(function(e) {
-
-				var v = Math.round(Std.parseFloat(f.val()) * 100) / 100;
-				setVal(v);
-				inputView.val(v);
-
-			});
-
+			range = new hide.comp.Range(f);
+			range.onChange = function(temp) {
+				tempChange = temp;
+				setVal(range.value);
+			};
+			return;
 		default:
 			if( f.is("select") ) {
 				enumValue = Type.getEnum(current);

+ 109 - 0
hide/comp/Range.hx

@@ -0,0 +1,109 @@
+package hide.comp;
+
+class Range extends Component {
+
+	var original : Null<Float>;
+
+	public var value(get, set) : Float;
+
+	var current : Float;
+	var originMin : Float;
+	var originMax : Float;
+	var curMin : Float;
+	var curMax : Float;
+
+	var f : Element;
+	var inputView : Element;
+
+	public function new(f:Element) {
+		f.wrap('<div class="hide-range"/>');
+		var p = f.parent();
+		super(p);
+
+		this.f = f;
+		if( f.attr("step") == null )
+			f.attr("step", "any");
+		inputView = new Element('<input type="text">').appendTo(p);
+		originMin = Std.parseFloat(f.attr("min"));
+		originMax = Std.parseFloat(f.attr("max"));
+		if( originMin == null || Math.isNaN(originMin) ) originMin = 0;
+		if( originMax == null || Math.isNaN(originMax) ) originMax = 1;
+		curMin = originMin;
+		curMax = originMax;
+		current = Std.parseFloat(f.attr("value"));
+		if( current == null || Math.isNaN(current) ) current = 0;
+
+		p.parent().prev("dt").contextmenu(function(e) {
+			e.preventDefault();
+			new ContextMenu([
+				{ label : "Reset", click : function() { inputView.val(""+original); inputView.change(); } },
+				{ label : "Cancel", click : function() {} },
+			]);
+			return false;
+		});
+
+		f.on("input", function(_) {
+			var v = Math.round(Std.parseFloat(f.val()) * 100) / 100;
+			inputView.val(v);
+			current = v;
+			onChange(true);
+		});
+		inputView.keyup(function(e) {
+			if( e.keyCode == 13 || e.keyCode == 27 ) {
+				inputView.blur();
+				inputView.val(current);
+				return;
+			}
+			var v = Std.parseFloat(inputView.val());
+			if( Math.isNaN(v) ) return;
+			setInner(v);
+			f.val(v);
+			onChange(false);
+		});
+
+		f.val(current);
+		inputView.val(current);
+
+		f.change(function(e) {
+			var v = Math.round(Std.parseFloat(f.val()) * 100) / 100;
+			setInner(v);
+			inputView.val(v);
+			onChange(false);
+		});
+	}
+
+	function set_value(v) {
+		if( original == null ) original = v;
+		setInner(v);
+		current = v;
+		inputView.val(current);
+		f.val(current);
+		return v;
+	}
+
+	function get_value() {
+		return current;
+	}
+
+	function setInner(v:Float) {
+		current = v;
+		if( v < curMin ) {
+			curMin = Math.floor(v);
+			f.attr("min", curMin);
+		}
+		if( v > curMax ) {
+			curMax = Math.ceil(v);
+			f.attr("max", curMax);
+		}
+		if( v >= originMin && v <= originMax ) {
+			f.attr("min", originMin);
+			f.attr("max", originMax);
+			curMin = originMin;
+			curMax = originMax;
+		}
+	}
+
+	public dynamic function onChange( tempChange : Bool ) {
+	}
+
+}

+ 7 - 0
hide/comp/Toolbar.hx

@@ -74,4 +74,11 @@ class Toolbar extends Component {
 		return tool;
 	}
 
+	public function addRange( label : String, onChange : Float -> Void, ?defValue = 0., min = 0., max = 1. ) {
+		var r = new hide.comp.Range(new Element('<input title="$label" type="range" min="$min" max="$max" value="$defValue">'));
+		r.onChange = function(_) onChange(r.value);
+		r.root.appendTo(root);
+		return r;
+	}
+
 }

+ 93 - 8
hide/view/Model.hx

@@ -12,6 +12,11 @@ class Model extends FileView {
 	var light : h3d.scene.DirLight;
 	var lightDirection = new h3d.Vector( 1, 2, -4 );
 
+	var aspeed : hide.comp.Range;
+	var apause : { function toggle( v : Bool ) : Void; var element : Element; };
+	var timeline : h2d.Graphics;
+	var timecursor : h2d.Bitmap;
+
 	override function onDisplay() {
 		root.html('
 			<div class="flex vertical">
@@ -108,14 +113,7 @@ class Model extends FileView {
 			}];
 			content.unshift({ label : "-- no anim --", value : null });
 			sel.setContent(content);
-			sel.onSelect = function(a) {
-				if( a == null ) {
-					obj.stopAnimation();
-					return;
-				}
-				var anim = scene.loadAnimation(a);
-				obj.playAnimation(anim);
-			};
+			sel.onSelect = setAnimation;
 		}
 
 		scene.init(props);
@@ -143,6 +141,90 @@ class Model extends FileView {
 		tools.addColor("Background color", function(v) {
 			scene.engine.backgroundColor = v;
 		}, scene.engine.backgroundColor);
+
+		apause = tools.addToggle("pause", "Pause animation", function(v) {
+			if( obj.currentAnimation != null ) obj.currentAnimation.pause = v;
+		});
+
+		aspeed = tools.addRange("Animation speed", function(v) {
+			if( obj.currentAnimation != null ) obj.currentAnimation.speed = v;
+		}, 1, 0, 2);
+
+		scene.onResize = buildTimeline;
+		setAnimation(null);
+	}
+
+	function setAnimation( file : String ) {
+		if( timeline != null ) {
+			timeline.remove();
+			timeline = null;
+		}
+		apause.toggle(false);
+		aspeed.value = 1;
+		if( file == null ) {
+			obj.stopAnimation();
+			aspeed.root.toggle(false);
+			apause.element.toggle(false);
+			return;
+		}
+		var anim = scene.loadAnimation(file);
+		obj.playAnimation(anim);
+		buildTimeline();
+		aspeed.root.toggle(true);
+		apause.element.toggle(true);
+	}
+
+	function buildTimeline() {
+		if( timeline != null ) {
+			timeline.remove();
+			timeline = null;
+		}
+		if( obj.currentAnimation == null )
+			return;
+
+		var H = 15;
+		var W = scene.s2d.width;
+		timeline = new h2d.Graphics(scene.s2d);
+		timeline.y = scene.s2d.height - H;
+		timeline.beginFill(0, 0.8);
+		timeline.drawRect(0, 0, W, H);
+		var int = new h2d.Interactive(W, H, timeline);
+		timecursor = new h2d.Bitmap(h2d.Tile.fromColor(0x808080, 8, H), timeline);
+		timecursor.x = -100;
+		int.onPush = function(e) {
+			var prevPause = obj.currentAnimation.pause;
+			obj.currentAnimation.pause = true;
+			obj.currentAnimation.setFrame( (e.relX / W) * obj.currentAnimation.frameCount );
+			int.startDrag(function(e) {
+				switch(e.kind ) {
+				case ERelease:
+					obj.currentAnimation.pause = prevPause;
+					int.stopDrag();
+				case EMove:
+					obj.currentAnimation.setFrame( (e.relX / W) * obj.currentAnimation.frameCount );
+				default:
+				}
+			});
+		};
+
+		var events = @:privateAccess obj.currentAnimation.events;
+		for( i in 0...events.length ) {
+			var el = events[i];
+			if( el == null || el.length == 0 ) continue;
+			var px = Std.int((i / obj.currentAnimation.frameCount) * W);
+			timeline.beginFill(0xC0C0C0);
+			timeline.drawRect(px, 0, 1, H);
+			var py = -14;
+			for( e in el ) {
+				var tf = new h2d.Text(hxd.res.DefaultFont.get(), timeline);
+				tf.text = e;
+				tf.x = px - Std.int(tf.textWidth * 0.5);
+				tf.y = py;
+				tf.alpha = 0.5;
+				py -= 15;
+			}
+		}
+
 	}
 
 	function update(dt:Float) {
@@ -156,6 +238,9 @@ class Model extends FileView {
 				lightDirection.z
 			);
 		}
+		if( timeline != null ) {
+			timecursor.x = Std.int((obj.currentAnimation.frame / obj.currentAnimation.frameCount) * (scene.s2d.width - timecursor.tile.width));
+		}
 	}
 
 	static var _ = FileTree.registerExtension(Model,["hmd","fbx","scn"],{ icon : "cube" });