2
0
ShiroSmith 6 жил өмнө
parent
commit
cc901c9f4b

+ 49 - 21
hide/prefab/SplineEditor.hx

@@ -8,6 +8,7 @@ class NewSplinePointViewer extends h3d.scene.Object {
 
 
 	var pointViewer : h3d.scene.Mesh;
 	var pointViewer : h3d.scene.Mesh;
 	var connectionViewer : h3d.scene.Graphics;
 	var connectionViewer : h3d.scene.Graphics;
+	var tangentViewer : h3d.scene.Graphics;
 
 
 	public function new( parent : h3d.scene.Object ) {
 	public function new( parent : h3d.scene.Object ) {
 		super(parent);
 		super(parent);
@@ -23,15 +24,30 @@ class NewSplinePointViewer extends h3d.scene.Object {
 		connectionViewer.lineStyle(3, 0xFFFF00);
 		connectionViewer.lineStyle(3, 0xFFFF00);
 		connectionViewer.material.mainPass.setPassName("overlay");
 		connectionViewer.material.mainPass.setPassName("overlay");
 		connectionViewer.material.mainPass.depth(false, LessEqual);
 		connectionViewer.material.mainPass.depth(false, LessEqual);
-		connectionViewer.ignoreParentTransform = false;
 		connectionViewer.clear();
 		connectionViewer.clear();
+
+		tangentViewer = new h3d.scene.Graphics(this);
+		tangentViewer.name = "tangentViewerViewer";
+		tangentViewer.lineStyle(3, 0xFFFF00);
+		tangentViewer.material.mainPass.setPassName("overlay");
+		tangentViewer.material.mainPass.depth(false, LessEqual);
+		tangentViewer.clear();
 	}
 	}
 
 
 	public function update( spd : SplinePointData ) {
 	public function update( spd : SplinePointData ) {
-		connectionViewer.clear();
+
 		pointViewer.setPosition(spd.pos.x, spd.pos.y, spd.pos.z);
 		pointViewer.setPosition(spd.pos.x, spd.pos.y, spd.pos.z);
+		
+		tangentViewer.clear();
+		tangentViewer.visible = spd.tangent != null;
+		if( spd.tangent != null ) {
+			var scale = (spd.prev.scaleX + spd.next.scaleX) * 0.5;
+			tangentViewer.moveTo(spd.pos.x - spd.tangent.x * scale, spd.pos.y - spd.tangent.y * scale, spd.pos.z - spd.tangent.z * scale);
+			tangentViewer.lineTo(spd.pos.x + spd.tangent.x * scale, spd.pos.y + spd.tangent.y * scale, spd.pos.z + spd.tangent.z * scale);
+		}
 
 
 		// Only display the connection if we are adding the new point at the end or the beggining fo the spline
 		// Only display the connection if we are adding the new point at the end or the beggining fo the spline
+		connectionViewer.clear();
 		connectionViewer.visible = spd.prev == spd.next;
 		connectionViewer.visible = spd.prev == spd.next;
 		if( connectionViewer.visible ) {
 		if( connectionViewer.visible ) {
 			var startPos = spd.prev == null ? spd.next.getPoint() : spd.prev.getPoint();
 			var startPos = spd.prev == null ? spd.next.getPoint() : spd.prev.getPoint();
@@ -126,13 +142,6 @@ class SplineEditor {
 		}
 		}
 	}
 	}
 
 
-	function trySelectPoint( ray: h3d.col.Ray ) : SplinePointViewer {
-		for( spv in splinePointViewers )
-			if( spv.interset(ray) )
-				return spv;
-		return null;
-	}
-
 	inline function getContext() {
 	inline function getContext() {
 		return editContext.getContext(prefab);
 		return editContext.getContext(prefab);
 	}
 	}
@@ -161,25 +170,26 @@ class SplineEditor {
 
 
 	function getNewPointPosition( mouseX : Float, mouseY : Float, ctx : hrt.prefab.Context, ?precision = 1.0 ) : SplinePointData {
 	function getNewPointPosition( mouseX : Float, mouseY : Float, ctx : hrt.prefab.Context, ?precision = 1.0 ) : SplinePointData {
 		if( prefab.points.length == 0 ) {
 		if( prefab.points.length == 0 ) {
-			return { pos : ctx.local3d.getAbsPos().getPosition().toPoint(), prev : null, next : null };
+			return { pos : ctx.local3d.getAbsPos().getPosition().toPoint(), tangent : ctx.local3d.getAbsPos().up().toPoint() , prev : null, next : null };
 		} 
 		} 
 
 
 		var closestPt = getClosestPointFromMouse(mouseX, mouseY, ctx, precision);
 		var closestPt = getClosestPointFromMouse(mouseX, mouseY, ctx, precision);
 		
 		
-		// If we are are adding a new point at the end/beginning, just make a raycast cursor -> plane with the transform of the frit/last SplinePoint
+		// If we are are adding a new point at the beginning/end, just make a raycast 'cursor -> plane' with the transform of the first/last SplinePoint
 		if( !prefab.loop && closestPt.next == closestPt.prev ) {
 		if( !prefab.loop && closestPt.next == closestPt.prev ) {
 			var camera = @:privateAccess ctx.local3d.getScene().camera;
 			var camera = @:privateAccess ctx.local3d.getScene().camera;
 			var ray = camera.rayFromScreen(mouseX, mouseY);
 			var ray = camera.rayFromScreen(mouseX, mouseY);
-			var normal = closestPt.prev.getAbsPos().up();
-			var plane = h3d.col.Plane.fromNormalPoint(normal.toPoint(), new h3d.col.Point(closestPt.prev.getAbsPos().tx, closestPt.prev.getAbsPos().ty, closestPt.prev.getAbsPos().tz));
+			var normal = closestPt.prev.getAbsPos().up().toPoint();
+			var plane = h3d.col.Plane.fromNormalPoint(normal, new h3d.col.Point(closestPt.prev.getAbsPos().tx, closestPt.prev.getAbsPos().ty, closestPt.prev.getAbsPos().tz));
 			var pt = ray.intersect(plane);
 			var pt = ray.intersect(plane);
-			return { pos : pt, prev : closestPt.prev, next : closestPt.next };
+			return { pos : pt, tangent : closestPt.tangent, prev : closestPt.prev, next : closestPt.next };
 		}
 		}
 		else 
 		else 
 			return closestPt;
 			return closestPt;
 	}
 	}
 
 
 	function getClosestPointFromMouse( mouseX : Float, mouseY : Float, ctx : hrt.prefab.Context, ?precision = 1.0 ) : SplinePointData {
 	function getClosestPointFromMouse( mouseX : Float, mouseY : Float, ctx : hrt.prefab.Context, ?precision = 1.0 ) : SplinePointData {
+
 		if( ctx == null || ctx.local3d == null || ctx.local3d.getScene() == null ) 
 		if( ctx == null || ctx.local3d == null || ctx.local3d.getScene() == null ) 
 			return null;
 			return null;
 
 
@@ -203,7 +213,7 @@ class SplineEditor {
 		}
 		}
 
 
 		if( result == null ) {
 		if( result == null ) {
-			result = { pos : null, prev : null, next : null};
+			result = { pos : null, tangent : null, prev : null, next : null};
 
 
 			var firstPt = prefab.points[0].getPoint();
 			var firstPt = prefab.points[0].getPoint();
 			var firstPtScreenPos = firstPt.toVector();
 			var firstPtScreenPos = firstPt.toVector();
@@ -244,16 +254,34 @@ class SplineEditor {
 		var pos = spd.pos.toVector();
 		var pos = spd.pos.toVector();
 		pos.project(invMatrix);
 		pos.project(invMatrix);
 
 
+		var scale = 1.0;
 		var index = -1;
 		var index = -1;
-		if( spd.prev == null && spd.next == null ) index = 0;
-		else if( spd.prev == spd.next && spd.prev != null ) {
-			if( spd.prev ==  prefab.points[0] ) index = 0;
-			else if( spd.prev ==  prefab.points[prefab.points.length - 1] ) index = prefab.points.length;
+		if( spd.prev == null && spd.next == null ) {
+			index = 0;
+			scale = 1.0;
+		}
+		else if( spd.prev == spd.next && spd.prev != null && spd.next != null ) {
+			scale = spd.prev.scaleX;
+			if( spd.prev ==  prefab.points[0] ) 
+				index = 0;
+			else if( spd.prev == prefab.points[prefab.points.length - 1] ) 
+				index = prefab.points.length;
+		}
+		else {
+			index = prefab.points.indexOf(spd.next);
+			scale = (spd.prev.scaleX + spd.next.scaleX) * 0.5;
 		}
 		}
-		else index = prefab.points.indexOf(spd.next);
 
 
 		var sp = new SplinePoint(pos.x, pos.y, pos.z, ctx.local3d);
 		var sp = new SplinePoint(pos.x, pos.y, pos.z, ctx.local3d);
 		prefab.points.insert(index, sp);
 		prefab.points.insert(index, sp);
+		if( spd.tangent != null ) {
+			var dir = spd.tangent.toVector();
+			dir.scale3(-1);
+			dir.project(invMatrix);
+			sp.setDirection(dir);
+		}
+		sp.scale(scale);
+
 		prefab.generateBezierCurve(ctx);
 		prefab.generateBezierCurve(ctx);
 		return sp;
 		return sp;
 	}
 	}
@@ -485,7 +513,7 @@ class SplineEditor {
 		<div class="spline-editor">
 		<div class="spline-editor">
 			<div class="group" name="Description">
 			<div class="group" name="Description">
 				<div class="description">
 				<div class="description">
-					<i>Ctrl + Left Click</i> Add a point on the spline
+					<i>Ctrl + Left Click</i> Add a point on the spline <br>
 					<i>Shift + Left Click</i> Delete a point from the spline
 					<i>Shift + Left Click</i> Delete a point from the spline
 				</div>
 				</div>
 			</div>
 			</div>

+ 58 - 24
hrt/prefab/l3d/Spline.hx

@@ -8,6 +8,7 @@ enum CurveShape {
 
 
 typedef SplinePointData = {
 typedef SplinePointData = {
 	pos : h3d.col.Point,
 	pos : h3d.col.Point,
+	tangent : h3d.col.Point,
 	prev : SplinePoint,
 	prev : SplinePoint,
 	next : SplinePoint
 	next : SplinePoint
 }
 }
@@ -21,11 +22,8 @@ class SplinePoint extends h3d.scene.Object {
 		setPosition(x,y,z);
 		setPosition(x,y,z);
 	}
 	}
 
 
-	var p = new h3d.col.Point();
-	public function getPoint() : h3d.col.Point {
-		var absPos = getAbsPos();
-		p.set(absPos.tx, absPos.ty, absPos.tz);
-		return p;
+	inline public function getPoint() : h3d.col.Point {
+		return getAbsPos().getPosition().toPoint();
 	}
 	}
 
 
 	public function getFirstControlPoint() : h3d.col.Point {
 	public function getFirstControlPoint() : h3d.col.Point {
@@ -52,7 +50,7 @@ class Spline extends Object3D {
 	public var pointsData : Array<h3d.Matrix> = [];
 	public var pointsData : Array<h3d.Matrix> = [];
 	public var points : Array<SplinePoint> = [];
 	public var points : Array<SplinePoint> = [];
 	public var shape : CurveShape = Quadratic;
 	public var shape : CurveShape = Quadratic;
-	
+
 	public var lineGraphics : h3d.scene.Graphics;
 	public var lineGraphics : h3d.scene.Graphics;
 	public var linePrecision : Int = 15;
 	public var linePrecision : Int = 15;
 	public var lineThickness : Int = 4;
 	public var lineThickness : Int = 4;
@@ -67,17 +65,17 @@ class Spline extends Object3D {
 
 
 	override function save() {
 	override function save() {
 		var obj : Dynamic = super.save();
 		var obj : Dynamic = super.save();
-		
+
 		if( points!= null && points.length > 0 ) {
 		if( points!= null && points.length > 0 ) {
 			var parentInv = points[0].parent.getAbsPos().clone();
 			var parentInv = points[0].parent.getAbsPos().clone();
 			parentInv.initInverse(parentInv);
 			parentInv.initInverse(parentInv);
 			obj.points = [ for(sp in points) {
 			obj.points = [ for(sp in points) {
 								var abs = sp.getAbsPos().clone();
 								var abs = sp.getAbsPos().clone();
-								abs.multiply(parentInv, abs);
+								abs.multiply(abs, parentInv);
 								abs;
 								abs;
 							} ];
 							} ];
 		}
 		}
-		obj.shape = shape;
+		obj.shape = shape.getIndex();
 		obj.color = color;
 		obj.color = color;
 		obj.linePrecision = linePrecision;
 		obj.linePrecision = linePrecision;
 		obj.lineThickness = lineThickness;
 		obj.lineThickness = lineThickness;
@@ -88,7 +86,7 @@ class Spline extends Object3D {
 	override function load( obj : Dynamic ) {
 	override function load( obj : Dynamic ) {
 		super.load(obj);
 		super.load(obj);
 		pointsData = obj.points == null ? [] : obj.points;
 		pointsData = obj.points == null ? [] : obj.points;
-		shape = obj.shape == null ? Linear : obj.shape;
+		shape = obj.shape == null ? Linear : CurveShape.createByIndex(obj.shape);
 		color = obj.color != null ? obj.color : 0xFFFFFFFF;
 		color = obj.color != null ? obj.color : 0xFFFFFFFF;
 		linePrecision = obj.linePrecision == null ? 15 : obj.linePrecision;
 		linePrecision = obj.linePrecision == null ? 15 : obj.linePrecision;
 		lineThickness = obj.lineThickness == null ? 4 : obj.lineThickness;
 		lineThickness = obj.lineThickness == null ? 4 : obj.lineThickness;
@@ -100,7 +98,7 @@ class Spline extends Object3D {
 
 
 		ctx.local3d = new h3d.scene.Object(ctx.local3d);
 		ctx.local3d = new h3d.scene.Object(ctx.local3d);
 		ctx.local3d.name = name;
 		ctx.local3d.name = name;
-	
+
 		for( pd in pointsData ) {
 		for( pd in pointsData ) {
 			var sp = new SplinePoint(0, 0, 0, ctx.local3d);
 			var sp = new SplinePoint(0, 0, 0, ctx.local3d);
 			sp.setTransform(pd);
 			sp.setTransform(pd);
@@ -128,7 +126,7 @@ class Spline extends Object3D {
 
 
 	// Return the length of the spline, use the computed data if available
 	// Return the length of the spline, use the computed data if available
 	function getLength( ?precision : Float = 1.0 ) : Float {
 	function getLength( ?precision : Float = 1.0 ) : Float {
-		if( computedLength > 0 ) 
+		if( computedLength > 0 )
 			return computedLength;
 			return computedLength;
 		var sum = 0.0;
 		var sum = 0.0;
 		for( i in 0 ... points.length - 1 ) {
 		for( i in 0 ... points.length - 1 ) {
@@ -158,7 +156,7 @@ class Spline extends Object3D {
 			return getMinLengthBetween(p1, p2);
 			return getMinLengthBetween(p1, p2);
 		}
 		}
 
 
-		if( p1.distanceToNextPoint > 0 ) 
+		if( p1.distanceToNextPoint > 0 )
 			return p1.distanceToNextPoint;
 			return p1.distanceToNextPoint;
 
 
 		var sum = 0.0;
 		var sum = 0.0;
@@ -200,7 +198,7 @@ class Spline extends Object3D {
 		var length = getLength();
 		var length = getLength();
 		var stepCount = hxd.Math.ceil(length * precision);
 		var stepCount = hxd.Math.ceil(length * precision);
 		var minDist = -1.0;
 		var minDist = -1.0;
-		var result : SplinePointData = { pos : null, prev : null, next : null };
+		var result : SplinePointData = { pos : null, tangent : null, prev : null, next : null };
 		for( i in 0 ... stepCount ) {
 		for( i in 0 ... stepCount ) {
 			var pt = getPoint( i / stepCount );
 			var pt = getPoint( i / stepCount );
 			var dist = pt.pos.distance(p);
 			var dist = pt.pos.distance(p);
@@ -212,11 +210,24 @@ class Spline extends Object3D {
 		return result;
 		return result;
 	}
 	}
 
 
-	// Return the point on the spline between p1 and p2 at t ( 0 -> 1 )
+	// Return the point on the spline between p1 and p2 at t, 0 <= t <= 1
 	function getPoint( t : Float, ?precision : Float = 1.0 ) : SplinePointData {
 	function getPoint( t : Float, ?precision : Float = 1.0 ) : SplinePointData {
 		t = hxd.Math.clamp(t, 0, 1);
 		t = hxd.Math.clamp(t, 0, 1);
-		if( t == 0 ) return { pos : points[0].getPoint(), prev : points[0], next : points[0] } ;
-		if( t == 1 ) return { pos : points[points.length - 1].getPoint(), prev : points[points.length - 1], next : points[points.length - 1] };
+
+		if( points.length == 1 ) return { 	pos : points[0].getPoint(),
+											tangent : points[0].getAbsPos().right().toPoint(),
+											prev : null, 
+											next : null } ;
+
+		if( t == 0 ) return { 	pos : points[0].getPoint(),
+								tangent : points[0].getFirstControlPoint().sub(points[0].getPoint()),
+								prev : points[0], 
+								next : points[0] } ;
+
+		if( t == 1 ) return { 	pos : points[points.length - 1].getPoint(),
+								tangent : points[points.length - 1].getFirstControlPoint().sub(points[points.length - 1].getPoint()),
+								prev : points[points.length - 1], 
+								next : points[points.length - 1] };
 
 
 		var totalLength = getLength();
 		var totalLength = getLength();
 		var length = totalLength * t;
 		var length = totalLength * t;
@@ -224,14 +235,16 @@ class Spline extends Object3D {
 		for( i in 0 ... points.length - 1 ) {
 		for( i in 0 ... points.length - 1 ) {
 			var curSegmentLength = getLengthBetween(points[i], points[i+1], precision);
 			var curSegmentLength = getLengthBetween(points[i], points[i+1], precision);
 			curlength += curSegmentLength;
 			curlength += curSegmentLength;
-			if( length <= curlength ) 
-				return { pos : getPointBetween( (length - (curlength - curSegmentLength)) / curSegmentLength, points[i], points[i+1]), prev : points[i], next : points[i+1] } ;
+			if( length <= curlength ) {
+				var t = (length - (curlength - curSegmentLength)) / curSegmentLength;
+				return { pos : getPointBetween(t, points[i], points[i+1]), tangent : getTangentBetween(t, points[i], points[i+1]), prev : points[i], next : points[i+1] };
+			}
 		}
 		}
-		return { pos : points[points.length - 1].getPoint(), prev : points[points.length - 1], next : points[points.length - 1] };
+		return { pos : null, tangent : null, prev : null, next : null };
 	}
 	}
 
 
-	// Return the point on the curve between p1 and p2 at t ( 0 -> 1 )
-	inline function getPointBetween( t : Float, p1 : SplinePoint, p2 : SplinePoint ) {
+	// Return the point on the curve between p1 and p2 at t, 0 <= t <= 1
+	inline function getPointBetween( t : Float, p1 : SplinePoint, p2 : SplinePoint ) : h3d.col.Point {
 		return switch (shape) {
 		return switch (shape) {
 			case Linear: getLinearBezierPoint( t, p1.getPoint(), p2.getPoint() );
 			case Linear: getLinearBezierPoint( t, p1.getPoint(), p2.getPoint() );
 			case Quadratic: getQuadraticBezierPoint( t, p1.getPoint(), p1.getSecondControlPoint(), p2.getPoint() );
 			case Quadratic: getQuadraticBezierPoint( t, p1.getPoint(), p1.getSecondControlPoint(), p2.getPoint() );
@@ -239,23 +252,44 @@ class Spline extends Object3D {
 		}
 		}
 	}
 	}
 
 
-	// Linear Interpolation 
+	// Return the tangen on the curve between p1 and p2 at t, 0 <= t <= 1
+	inline function getTangentBetween( t : Float, p1 : SplinePoint, p2 : SplinePoint ) : h3d.col.Point {
+		return switch (shape) {
+			case Linear: getLinearBezierTangent( t, p1.getPoint(), p2.getPoint() );
+			case Quadratic: getQuadraticBezierTangent( t, p1.getPoint(), p1.getSecondControlPoint(), p2.getPoint() );
+			case Cubic: getCubicBezierTangent( t, p1.getPoint(), p1.getSecondControlPoint(), p2.getFirstControlPoint(), p2.getPoint() );
+		}
+	}
+
+	// Linear Interpolation
 	// p(t) = p0 + (p1 - p0) * t
 	// p(t) = p0 + (p1 - p0) * t
 	inline function getLinearBezierPoint( t : Float, p0 : h3d.col.Point, p1 : h3d.col.Point ) : h3d.col.Point {
 	inline function getLinearBezierPoint( t : Float, p0 : h3d.col.Point, p1 : h3d.col.Point ) : h3d.col.Point {
 		return p0.add((p1.sub(p0).multiply(t)));
 		return p0.add((p1.sub(p0).multiply(t)));
 	}
 	}
+	// p'(t) = (p1 - p0)
+	inline function getLinearBezierTangent( t : Float, p0 : h3d.col.Point, p1 : h3d.col.Point ) : h3d.col.Point {
+		return p1.sub(p0).normalizeFast();
+	}
 
 
-	// Quadratic Interpolation 
+	// Quadratic Interpolation
 	// p(t) = p0 * (1 - t)² + p1 * t * 2 * (1 - t) + p2 * t²
 	// p(t) = p0 * (1 - t)² + p1 * t * 2 * (1 - t) + p2 * t²
 	inline function getQuadraticBezierPoint( t : Float, p0 : h3d.col.Point, p1 : h3d.col.Point, p2 : h3d.col.Point) : h3d.col.Point {
 	inline 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));
 		return p0.multiply((1 - t) * (1 - t)).add(p1.multiply(t * 2 * (1 - t))).add(p2.multiply(t * t));
 	}
 	}
+	// p'(t) = 2 * (1 - t) * (p1 - p2) + 2 * t * (p2 - p1)
+	inline function getQuadraticBezierTangent( t : Float, p0 : h3d.col.Point, p1 : h3d.col.Point, p2 : h3d.col.Point) : h3d.col.Point {
+		return p1.sub(p2).multiply(2 * (1 - t)).add(p2.sub(p1).multiply(2 * t)).normalizeFast();
+	}
 
 
 	// Cubic Interpolation
 	// Cubic Interpolation
 	// p(t) = p0 * (1 - t)³ + p1 * t * 3 * (1 - t)² + p2 * t² * 3 * (1 - t) + p3 * t³
 	// p(t) = p0 * (1 - t)³ + p1 * t * 3 * (1 - t)² + p2 * t² * 3 * (1 - t) + p3 * t³
 	inline function getCubicBezierPoint( t : Float, p0 : h3d.col.Point, p1 : h3d.col.Point, p2 : h3d.col.Point, p3 : h3d.col.Point) : h3d.col.Point {
 	inline 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));
 		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));
 	}
 	}
+	// p'(t) = 3 * (1 - t)² * (p1 - p0) + 6 * (1 - t) * t * (p2 - p1) + 3 * t² * (p3 - p2)
+	inline function getCubicBezierTangent( t : Float, p0 : h3d.col.Point, p1 : h3d.col.Point, p2 : h3d.col.Point, p3 : h3d.col.Point) : h3d.col.Point {
+		return p1.sub(p0).multiply(3 * (1 - t) * (1 - t)).add(p2.sub(p1).multiply(6 * (1 - t) * t)).add(p3.sub(p2).multiply(3 * t * t)).normalizeFast();
+	}
 
 
 	#if editor
 	#if editor