瀏覽代碼

Add updateInstance() call after moving a prefab
Add spline and spline editor - not ready

ShiroSmith 6 年之前
父節點
當前提交
6d5a8e52f1
共有 3 個文件被更改,包括 511 次插入1 次删除
  1. 3 1
      hide/comp/SceneEditor.hx
  2. 299 0
      hide/prefab/SplineEditor.hx
  3. 209 0
      hrt/prefab/l3d/Spline.hx

+ 3 - 1
hide/comp/SceneEditor.hx

@@ -741,8 +741,10 @@ class SceneEditor {
 
 					for(o in objects3d)
 						o.updateInstance(getContext(o));
-
 				}));
+
+				for(o in objects3d)
+					o.updateInstance(getContext(o));
 			}
 		}
 	}

+ 299 - 0
hide/prefab/SplineEditor.hx

@@ -0,0 +1,299 @@
+package hide.prefab;
+import hxd.Key as K;
+import hrt.prefab.l3d.Spline;
+
+#if editor
+
+class SplinePointViewer extends h3d.scene.Object {
+
+	var pointViewer : h3d.scene.Mesh;
+	var controlPointsViewer : h3d.scene.Graphics;
+
+	public function new( sp : SplinePoint ) {
+		super(sp);
+		name = "SplinePointViewer";
+		pointViewer = new h3d.scene.Mesh(h3d.prim.Sphere.defaultUnitSphere(), null, this);
+		pointViewer.name = "sphereHandle";
+		pointViewer.material.setDefaultProps("ui");
+
+		controlPointsViewer = new h3d.scene.Graphics(this);
+		controlPointsViewer.lineStyle(4, 0xffffff);
+		controlPointsViewer.material.mainPass.setPassName("overlay");
+		controlPointsViewer.material.mainPass.depth(false, LessEqual);
+		controlPointsViewer.ignoreParentTransform = false;
+		controlPointsViewer.clear();
+		controlPointsViewer.moveTo(1, 0, 0);
+		controlPointsViewer.lineTo(-1, 0, 0);
+	}
+
+	override function sync( ctx : h3d.scene.RenderContext ) {
+		var cam = ctx.camera;
+		var gpos = getAbsPos().getPosition();
+		var distToCam = cam.pos.sub(gpos).length();
+		var engine = h3d.Engine.getCurrent();
+		var ratio = 18 / engine.height;
+		var correctionFromParents =  1.0 / getAbsPos().getScale().x;
+		pointViewer.setScale(correctionFromParents * ratio * distToCam * Math.tan(cam.fovY * 0.5 * Math.PI / 180.0));
+		calcAbsPos();
+		super.sync(ctx);
+	}
+
+	public function interset( ray : h3d.col.Ray ) : Bool {
+		return pointViewer.getCollider().rayIntersection(ray, false) != -1;
+	}
+}
+
+class SplineEditor {
+
+	public var prefab : Spline;
+	public var editContext : EditContext;
+	var editMode = false;
+	var undo : hide.ui.UndoHistory;
+
+	var interactive : h2d.Interactive;
+
+	 // Easy way to keep track of viewers
+	var splinePointViewers : Array<SplinePointViewer> = [];
+	var gizmos : Array<hide.view.l3d.Gizmo> = [];
+
+	public function new( prefab : Spline, undo : hide.ui.UndoHistory ){
+		this.prefab = prefab;
+		this.undo = undo;
+	}
+
+	public function update( ctx : hrt.prefab.Context , ?propName : String ) {
+		if( editMode )
+			showViewers(ctx);
+		else 
+			removeViewers();
+
+	}
+
+	function reset() {
+		removeViewers();
+		removeGizmos();
+		if( interactive != null )
+			interactive.remove();
+	}
+
+	function trySelectPoint( ray: h3d.col.Ray ) : SplinePointViewer {
+		for( spv in splinePointViewers )
+			if( spv.interset(ray) )
+				return spv;
+		return null;
+	}
+
+	inline function getContext() {
+		return editContext.getContext(prefab);
+	}
+
+	function removeViewers() {
+		for( v in splinePointViewers )
+			v.remove();
+		splinePointViewers = [];
+	}
+
+	function showViewers( ctx : hrt.prefab.Context ) {
+		removeViewers(); // Security, avoid duplication
+		for( sp in prefab.points ) {
+			var spv = new SplinePointViewer(sp);
+			splinePointViewers.push(spv);
+		}
+	}
+
+	function removeGizmos() {
+		for( g in gizmos ) {
+			g.remove();
+			@:privateAccess editContext.scene.editor.updates.remove(g.update);
+		}
+		gizmos = [];
+	}
+
+	function createGizmos( ctx : hrt.prefab.Context  ) {
+		removeGizmos(); // Security, avoid duplication
+		var sceneEditor = @:privateAccess editContext.scene.editor;
+		for( sp in prefab.points ) {
+			var gizmo = new hide.view.l3d.Gizmo(editContext.scene);
+			gizmo.getRotationQuat().identity();
+			gizmo.visible = true;
+			var worldPos = ctx.local3d.localToGlobal(new h3d.Vector(sp.x, sp.y, sp.z));
+			gizmo.setPosition(worldPos.x, worldPos.y, worldPos.z);
+			@:privateAccess sceneEditor.updates.push( gizmo.update );
+			gizmos.push(gizmo);
+
+			gizmo.onStartMove = function(mode) {
+				/**/
+				var sceneObj = sp;
+				var pivotPt = sceneObj.getAbsPos().getPosition();
+				var pivot = new h3d.Matrix();
+				pivot.initTranslation(pivotPt.x, pivotPt.y, pivotPt.z);
+				var invPivot = pivot.clone();
+				invPivot.invert();
+				var worldMat : h3d.Matrix = sceneEditor.worldMat(sceneObj);
+				var localMat : h3d.Matrix = worldMat.clone();
+				localMat.multiply(localMat, invPivot);
+
+				var posQuant = @:privateAccess sceneEditor.view.config.get("sceneeditor.xyzPrecision");
+				var scaleQuant = @:privateAccess sceneEditor.view.config.get("sceneeditor.scalePrecision");
+				var rotQuant = @:privateAccess sceneEditor.view.config.get("sceneeditor.rotatePrecision");
+
+				inline function quantize(x: Float, step: Float) {
+					if(step > 0) {
+						x = Math.round(x / step) * step;
+						x = untyped parseFloat(x.toFixed(5)); // Snap to closest nicely displayed float :cold_sweat:
+					}
+					return x;
+				}
+
+				var rot = sceneObj.getRotationQuat().toEuler();
+				var prevState = { 	x : sceneObj.x, y : sceneObj.y, z : sceneObj.z, 
+									scaleX : sceneObj.scaleX, scaleY : sceneObj.scaleY, scaleZ : sceneObj.scaleZ, 
+									rotationX : rot.x, rotationY : rot.y, rotationZ : rot.z };
+
+				gizmo.onMove = function(translate: h3d.Vector, rot: h3d.Quat, scale: h3d.Vector) {
+					var transf = new h3d.Matrix();
+					transf.identity();
+
+					if(rot != null)
+						rot.toMatrix(transf);
+
+					if(translate != null)
+						transf.translate(translate.x, translate.y, translate.z);
+
+					var newMat = localMat.clone();
+					newMat.multiply(newMat, transf);
+					newMat.multiply(newMat, pivot);
+					var invParent = sceneObj.parent.getAbsPos().clone();
+					invParent.invert();
+					newMat.multiply(newMat, invParent);
+					if(scale != null) {
+						newMat.prependScale(scale.x, scale.y, scale.z);
+					}
+
+					var rot = newMat.getEulerAngles();
+					sceneObj.x = quantize(newMat.tx, posQuant);
+					sceneObj.y = quantize(newMat.ty, posQuant);
+					sceneObj.z = quantize(newMat.tz, posQuant);
+					sceneObj.setRotation(hxd.Math.degToRad(quantize(hxd.Math.radToDeg(rot.x), rotQuant)), hxd.Math.degToRad(quantize(hxd.Math.radToDeg(rot.y), rotQuant)), hxd.Math.degToRad(quantize(hxd.Math.radToDeg(rot.z), rotQuant)));
+					if(scale != null) {
+						inline function scaleSnap(x: Float) {
+							if(K.isDown(K.CTRL)) {
+								var step = K.isDown(K.SHIFT) ? 0.5 : 1.0;
+								x = Math.round(x / step) * step;
+							}
+							return x;
+						}
+						var s = newMat.getScale();
+						sceneObj.scaleX = quantize(scaleSnap(s.x), scaleQuant);
+						sceneObj.scaleY = quantize(scaleSnap(s.y), scaleQuant);
+						sceneObj.scaleZ = quantize(scaleSnap(s.z), scaleQuant);
+					}	
+
+					prefab.updateInstance(ctx);	
+				}
+
+				gizmo.onFinishMove = function() {
+					//var newState = [for(o in objects3d) o.saveTransform()];
+					/*undo.change(Custom(function(undo) {
+						if( undo ) {
+							for(i in 0...objects3d.length) {
+								objects3d[i].loadTransform(prevState[i]);
+								objects3d[i].applyPos(sceneObjs[i]);
+							}
+						}
+						else {
+							for(i in 0...objects3d.length) {
+								objects3d[i].loadTransform(newState[i]);
+								objects3d[i].applyPos(sceneObjs[i]);
+							}
+						}
+					}));*/
+				}/**/
+			}
+		}
+	}
+
+	public function setSelected( ctx : hrt.prefab.Context , b : Bool ) {
+		reset();
+
+		if( !editMode )
+			return;
+
+		if( b ) {
+			@:privateAccess editContext.scene.editor.gizmo.visible = false;
+			@:privateAccess editContext.scene.editor.curEdit = null;
+			createGizmos(ctx);
+			/*var s2d = @:privateAccess ctx.local2d.getScene();
+			interactive = new h2d.Interactive(10000, 10000, s2d);
+			interactive.propagateEvents = true;
+			interactive.cancelEvents = false;
+
+			interactive.onKeyDown =
+				function(e) {
+					e.propagate = false;
+				};
+
+			interactive.onKeyUp =
+				function(e) {
+					e.propagate = false;
+				};
+
+			interactive.onPush =
+				function(e) {
+					if( K.isDown( K.MOUSE_LEFT ) ) {
+						e.propagate = false;
+						var ray = @:privateAccess ctx.local3d.getScene().camera.rayFromScreen(s2d.mouseX, s2d.mouseY);
+						var p = trySelectPoint(ray);
+					}
+				};
+
+			interactive.onRelease =
+				function(e) {
+
+				};
+
+			interactive.onMove =
+				function(e) {
+
+				};*/
+		}
+		else {
+			editMode = false;
+		}
+	}
+
+	public function edit( ctx : EditContext ) {
+
+		var props = new hide.Element('
+		<div class="spline-editor">
+			<div class="group" name="Description">
+				<div class="description">
+					<i>Ctrl + Left Click</i> Destroy the world
+				</div>
+			</div>
+			<div class="group" name="Tool">
+				<div align="center">
+					<input type="button" value="Edit Mode : Disabled" class="editModeButton" />
+				</div>
+			</div>
+		</div>');
+
+		var editModeButton = props.find(".editModeButton");
+		editModeButton.click(function(_) {
+			editMode = !editMode;
+			editModeButton.val(editMode ? "Edit Mode : Enabled" : "Edit Mode : Disabled");
+			editModeButton.toggleClass("editModeEnabled", editMode);
+			setSelected(getContext(), true);
+			ctx.onChange(prefab, null);
+		});
+
+		ctx.properties.add(props, this, function(pname) {
+			ctx.onChange(prefab, pname);
+		});
+
+		return props;
+	}
+
+}
+
+#end

+ 209 - 0
hrt/prefab/l3d/Spline.hx

@@ -0,0 +1,209 @@
+package hrt.prefab.l3d;
+
+enum CurveShape {
+	Linear;
+	Quadratic;
+	Cubic;
+}
+
+class SplinePoint extends h3d.scene.Object {
+
+	public function new(x : Float, y : Float, z : Float, parent : h3d.scene.Object) {
+		super(parent);
+		setPosition(x,y,z);
+		scale(1);
+	}
+
+	public function getPoint() : h3d.col.Point {
+		var absPos = getAbsPos();
+		var pos = new h3d.col.Point(absPos.tx, absPos.ty, absPos.tz);
+		return pos;
+	}
+
+	public function getFirstControlPoint() : h3d.col.Point {
+		var absPos = getAbsPos();
+		var right = absPos.front();
+		right.scale3(scaleX);
+		var pos = new h3d.col.Point(absPos.tx, absPos.ty, absPos.tz);
+		pos = pos.add(right.toPoint());
+		return pos;
+	}
+
+	public function getSecondControlPoint() : h3d.col.Point {
+		var absPos = getAbsPos();
+		var left = absPos.front();
+		left.scale3(-scaleX);
+		var pos = new h3d.col.Point(absPos.tx, absPos.ty, absPos.tz);
+		pos = pos.add(left.toPoint());
+		return pos;
+	}
+}
+
+class Spline extends Object3D {
+
+	public var pointsData : Array<h3d.Matrix> = [];
+	public var points : Array<SplinePoint> = [];
+	public var shape : CurveShape = Quadratic;
+
+	#if editor
+	public var editor : hide.prefab.SplineEditor;
+	public var lineGraphics : h3d.scene.Graphics;
+	public var precision : Int = 15;
+	public var color : Int = 0xFFFFFFFF;
+	#end
+
+	override function save() {
+		var obj : Dynamic = super.save();
+		obj.points = [ for(sp in points) { sp.getAbsPos(); } ];
+		obj.shape = shape;
+		return obj;
+	}
+
+	override function load( obj : Dynamic ) {
+		super.load(obj);
+		pointsData = obj.points == null ? [] : obj.points;
+		shape = obj.shape == null ? Linear : obj.shape;
+	}
+
+	override function makeInstance( ctx : hrt.prefab.Context ) : hrt.prefab.Context {
+		var ctx = ctx.clone(this);
+
+		ctx.local3d = new h3d.scene.Object(ctx.local3d);
+		ctx.local3d.name = name;
+
+		#if editor
+		lineGraphics = new h3d.scene.Graphics(ctx.local3d);
+		lineGraphics.lineStyle(2, color);
+		lineGraphics.material.mainPass.setPassName("overlay");
+		lineGraphics.material.mainPass.depth(false, LessEqual);
+		lineGraphics.ignoreParentTransform = false;
+
+		/*points = [];
+		points.push(new SplinePoint(0,0,0, ctx.local3d));
+		points.push(new SplinePoint(5,10,0, ctx.local3d));
+		points.push(new SplinePoint(-5,20,5, ctx.local3d));
+		points.push(new SplinePoint(-10,30,0, ctx.local3d));
+		points.push(new SplinePoint(10,40,-5, ctx.local3d));*/
+		#end
+	
+		for( pd in pointsData ) {
+			var sp = new SplinePoint(0, 0, 0, ctx.local3d);
+			sp.setTransform(pd);
+			points.push(sp);
+		}
+		pointsData = null;
+
+		updateInstance(ctx);
+		return ctx;
+	}
+
+	override function updateInstance( ctx : hrt.prefab.Context , ?propName : String ) {
+		super.updateInstance(ctx, propName);
+		#if editor
+		lineGraphics.lineStyle(2, color);
+		if( editor != null )
+			editor.update(ctx, propName);
+		generateBezierCurve(ctx);
+		#end
+	}
+
+	// Linear Interpolation 
+	// p(t) = p0 + (p1 - p0) * t
+	function getLinearBezierPoint( t : Float, p0 : h3d.col.Point, p1 : h3d.col.Point ) : h3d.col.Point {
+		return p0.add((p1.sub(p0).multiply(t)));
+	}
+
+	// Quadratic Interpolation 
+	// p(t) = p0 * (1 - t)² + p1 * t * 2 * (1 - t) + p2 * t²
+	function getQuadraticBezierPoint( t : Float, p0 : h3d.col.Point, p1 : h3d.col.Point, p2 : h3d.col.Point) : h3d.col.Point {
+		return p0.multiply((1 - t) * (1 - t)).add(p1.multiply(t * 2 * (1 - t))).add(p2.multiply(t * t));
+	}
+
+	// Cubic Interpolation
+	// p(t) = p0 * (1 - t)³ + p1 * t * 3 * (1 - t)² + p2 * t² * 3 *(1 - t) + p3 * t³
+	function getCubicBezierPoint( t : Float, p0 : h3d.col.Point, p1 : h3d.col.Point, p2 : h3d.col.Point, p3 : h3d.col.Point) : h3d.col.Point {
+		return p0.multiply((1 - t) * (1 - t) * (1 - t)).add(p1.multiply(t * 3 * (1 - t) * (1 - t))).add(p2.multiply(t * t * 3 * (1 - t))).add(p3.multiply(t * t * t));
+	}
+
+	#if editor
+
+	function generateBezierCurve( ctx : hrt.prefab.Context ) {
+
+		if( points == null )
+			return;
+
+		var curve : Array<h3d.col.Point> = [];
+		switch (shape) {
+			case Linear:
+				for( sp in points )
+					curve.push(sp.getPoint());
+			case Quadratic:
+				var i = 0;
+				while( i < points.length - 1 ) {
+					for( v in 0 ... precision + 1 ) {
+						curve.push(getQuadraticBezierPoint( v / precision, points[i].getPoint(), points[i].getSecondControlPoint(), points[i+1].getPoint()));
+					}
+					++i;
+				}
+			case Cubic:
+				var i = 0;
+				while( i < points.length - 1 ) {
+					for( v in 0 ... precision + 1 ) {
+						curve.push(getCubicBezierPoint( v / precision, points[i].getPoint(), points[i].getSecondControlPoint(), points[i+1].getFirstControlPoint(), points[i+1].getPoint()));
+					}
+					++i;
+				}
+		}
+
+		lineGraphics.clear();
+		var b = true;
+		for( p in curve ) {
+			var localPos = ctx.local3d.globalToLocal(p.toVector());
+			b ? lineGraphics.moveTo(localPos.x, localPos.y, localPos.z) : lineGraphics.lineTo(localPos.x, localPos.y, localPos.z);
+			b = false;
+		}
+	}
+
+	override function setSelected( ctx : hrt.prefab.Context , b : Bool ) {
+		super.setSelected(ctx, b);
+
+		if( editor != null )
+			editor.setSelected(ctx, b);
+
+		//lineGraphics.visible = b;
+	}
+
+	override function edit( ctx : EditContext ) {
+		super.edit(ctx);
+
+		ctx.properties.add( new hide.Element('
+			<div class="group" name="Spline">
+				<dl>
+					<dt>Color</dt><dd><input type="color" alpha="true" field="color"/></dd>
+					<dt>Type</dt>
+						<dd>
+							<select field="shape" >
+								<option value="Linear">Linear</option>
+								<option value="Quadratic">Quadratic</option>
+								<option value="Cubic">Cubic</option>
+							</select>
+						</dd>
+					<dt>Precision</dt><dd><input type="range"step="1" min="1" max="100" field="precision"/></dd>
+				</dl>
+			</div>'), this, function(pname) { ctx.onChange(this, pname); });
+
+		if( editor == null ) {
+			editor = new hide.prefab.SplineEditor(this, ctx.properties.undo);
+		}
+
+		editor.editContext = ctx;
+		editor.edit(ctx);
+	}
+
+	override function getHideProps() : HideProps {
+		return { icon : "arrows-v", name : "Spline" };
+	}
+	#end
+
+	static var _ = hrt.prefab.Library.register("spline", Spline);
+}