ソースを参照

Update SplineMesh

ShiroSmith 6 年 前
コミット
12f577603a
3 ファイル変更329 行追加367 行削除
  1. 36 36
      hide/prefab/SplineEditor.hx
  2. 116 219
      hrt/prefab/l3d/Spline.hx
  3. 177 112
      hrt/prefab/l3d/SplineMesh.hx

+ 36 - 36
hide/prefab/SplineEditor.hx

@@ -41,14 +41,17 @@ class NewSplinePointViewer extends h3d.scene.Object {
 		tangentViewer.clear();
 		tangentViewer.visible = spd.tangent != null;
 		if( spd.tangent != null ) {
-			var scale = (spd.prev.scaleX + spd.next.scaleX) * 0.5;
+			var scale = 1.0;
+			if( spd.prev != null && spd.next != null ) scale = (spd.prev.scaleX + spd.next.scaleX) * 0.5;
+			else if( spd.prev != null ) scale = spd.prev.scaleX;
+			else if( spd.next != null ) scale = spd.next.scaleX;
 			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
 		connectionViewer.clear();
-		connectionViewer.visible = spd.prev == spd.next;
+		connectionViewer.visible = spd.prev == null || spd.next == null;
 		if( connectionViewer.visible ) {
 			var startPos = spd.prev == null ? spd.next.getPoint() : spd.prev.getPoint();
 			connectionViewer.moveTo(startPos.x, startPos.y, startPos.z);
@@ -168,19 +171,20 @@ class SplineEditor {
 		return result;
 	}
 
-	function getNewPointPosition( mouseX : Float, mouseY : Float, ctx : hrt.prefab.Context, ?precision = 1.0 ) : SplinePointData {
+	function getNewPointPosition( mouseX : Float, mouseY : Float, ctx : hrt.prefab.Context ) : SplinePointData {
 		if( prefab.points.length == 0 ) {
-			return { pos : ctx.local3d.getAbsPos().getPosition().toPoint(), tangent : ctx.local3d.getAbsPos().up().toPoint() , prev : null, next : null };
+			return { pos : ctx.local3d.getAbsPos().getPosition().toPoint(), tangent : ctx.local3d.getAbsPos().right().toPoint() , prev : null, next : null };
 		} 
 
-		var closestPt = getClosestPointFromMouse(mouseX, mouseY, ctx, precision);
+		var closestPt = getClosestPointFromMouse(mouseX, mouseY, ctx);
 		
 		// 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 == null || closestPt.prev == null) ) {
 			var camera = @:privateAccess ctx.local3d.getScene().camera;
 			var ray = camera.rayFromScreen(mouseX, mouseY);
-			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 normal = closestPt.next == null ? prefab.points[prefab.points.length - 1].getAbsPos().up().toPoint() : prefab.points[0].getAbsPos().up().toPoint();
+			var point = closestPt.next == null ? closestPt.prev.getAbsPos().getPosition().toPoint() : closestPt.next.getAbsPos().getPosition().toPoint();
+			var plane = h3d.col.Plane.fromNormalPoint(normal, point);
 			var pt = ray.intersect(plane);
 			return { pos : pt, tangent : closestPt.tangent, prev : closestPt.prev, next : closestPt.next };
 		}
@@ -188,19 +192,16 @@ class SplineEditor {
 			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 ) : SplinePointData {
 
 		if( ctx == null || ctx.local3d == null || ctx.local3d.getScene() == null ) 
 			return null;
 
 		var result : SplinePointData = null;
 		var mousePos = new h3d.Vector( mouseX / h3d.Engine.getCurrent().width, 1.0 - mouseY / h3d.Engine.getCurrent().height, 0);
-		var length = prefab.getLength(precision);
-		var stepCount = hxd.Math.ceil(length * precision);
 		var minDist = -1.0;
-		for( i in 0 ... stepCount ) {
-			var pt = prefab.getPoint( i / stepCount, precision);
-			var screenPos = pt.pos.toVector();
+		for( s in prefab.data.samples ) {
+			var screenPos = s.pos.toVector();
 			screenPos.project(ctx.local3d.getScene().camera.m);
 			screenPos.z = 0;
 			screenPos.scale3(0.5);
@@ -208,14 +209,15 @@ class SplineEditor {
 			var dist = screenPos.distance(mousePos);
 			if( (dist < minDist || minDist == -1) && dist < 0.1 ) {
 				minDist = dist;
-				result = pt;
+				result = s;
 			}
 		}
 
 		if( result == null ) {
 			result = { pos : null, tangent : null, prev : null, next : null};
 
-			var firstPt = prefab.points[0].getPoint();
+			var firstSp = prefab.points[0];
+			var firstPt = firstSp.getPoint();
 			var firstPtScreenPos = firstPt.toVector();
 			firstPtScreenPos.project(ctx.local3d.getScene().camera.m);
 			firstPtScreenPos.z = 0;
@@ -223,7 +225,8 @@ class SplineEditor {
 			firstPtScreenPos = firstPtScreenPos.add(new h3d.Vector(0.5,0.5));
 			var distToFirstPoint = firstPtScreenPos.distance(mousePos);
 
-			var lastPt = prefab.points[prefab.points.length - 1].getPoint();
+			var lastSp = prefab.points[prefab.points.length - 1];
+			var lastPt = lastSp.getPoint();
 			var lastPtSreenPos = lastPt.toVector();
 			lastPtSreenPos.project(ctx.local3d.getScene().camera.m);
 			lastPtSreenPos.z = 0;
@@ -233,15 +236,15 @@ class SplineEditor {
 
 			if( distTolastPoint < distToFirstPoint ) {
 				result.pos = lastPt;
+				result.tangent = lastSp.getAbsPos().right().toPoint();
 				result.prev = prefab.points[prefab.points.length - 1];
-				result.next = prefab.points[prefab.points.length - 1];
-				minDist = distTolastPoint;
+				result.next = null;
 			}
 			else {
 				result.pos = firstPt;
-				result.prev = prefab.points[0];
+				result.tangent = firstSp.getAbsPos().right().toPoint();
+				result.prev = null;
 				result.next = prefab.points[0]; 
-				minDist = distToFirstPoint;
 			}
 		}
 
@@ -253,19 +256,16 @@ class SplineEditor {
 		invMatrix.initInverse(invMatrix);
 		var pos = spd.pos.toVector();
 		pos.project(invMatrix);
-
+	
+		var index = 0;
 		var scale = 1.0;
-		var index = -1;
-		if( spd.prev == null && spd.next == null ) {
+		if( spd.prev == null ) {
 			index = 0;
-			scale = 1.0;
+			scale = prefab.points[0].getAbsPos().getScale().x; 
 		}
-		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 if( spd.next == null ) {
+			index = prefab.points.length;
+			scale = prefab.points[prefab.points.length - 1].getAbsPos().getScale().x; 
 		}
 		else {
 			index = prefab.points.indexOf(spd.next);
@@ -276,9 +276,9 @@ class SplineEditor {
 		prefab.points.insert(index, sp);
 		if( spd.tangent != null ) {
 			var dir = spd.tangent.toVector();
-			dir.scale3(-1);
-			dir.project(invMatrix);
-			sp.setDirection(dir);
+			dir.transform3x3(invMatrix); // Don't take the translation
+			var rot = h3d.Matrix.lookAtX(dir);
+			sp.setDirection(rot.front());
 		}
 		sp.scale(scale);
 
@@ -420,7 +420,7 @@ class SplineEditor {
 					// Add a new point
 					if( K.isDown( K.MOUSE_LEFT ) && K.isDown( K.CTRL )  ) {
 						e.propagate = false;
-						var pt = getNewPointPosition(s2d.mouseX, s2d.mouseY, ctx, 1);
+						var pt = getNewPointPosition(s2d.mouseX, s2d.mouseY, ctx);
 						var sp = addSplinePoint(pt, ctx);
 						showViewers(ctx);
 						createGizmos(ctx);
@@ -485,7 +485,7 @@ class SplineEditor {
 							newSplinePointViewer = new NewSplinePointViewer(ctx.local3d.getScene());
 						newSplinePointViewer.visible = true;
 
-						var npt = getNewPointPosition(s2d.mouseX, s2d.mouseY, ctx, 1);
+						var npt = getNewPointPosition(s2d.mouseX, s2d.mouseY, ctx);
 						newSplinePointViewer.update(npt);
 					}
 					else {

+ 116 - 219
hrt/prefab/l3d/Spline.hx

@@ -13,26 +13,20 @@ typedef SplinePointData = {
 	tangent : h3d.col.Point,
 	prev : SplinePoint,
 	next : SplinePoint,
-	?lengthPos : Float
+	?t : Float
 }
 
-class CurveComputedData {
-	public var length : Float;
-	public var points : Array<h3d.col.Point> = [];
-	public function new() {}
-}
 
-class SplineComputedData {
+class SplineData {
 	public var length : Float;
-	public var precision : Float;
-	public var curves : Array<CurveComputedData> = [];
+	public var step : Float;
+	public var threshold : Float;
+	public var samples : Array<SplinePointData> = [];
 	public function new() {}
 }
 
 class SplinePoint extends h3d.scene.Object {
 
-	public var distanceToNextPoint = -1.0;
-
 	public function new(x : Float, y : Float, z : Float, parent : h3d.scene.Object) {
 		super(parent);
 		setPosition(x,y,z);
@@ -42,6 +36,12 @@ class SplinePoint extends h3d.scene.Object {
 		return getAbsPos().getPosition().toPoint();
 	}
 
+	public function getTangent() : h3d.col.Point {
+		var tangent = getAbsPos().front().toPoint();
+		tangent.scale(-1);
+		return tangent;
+	}
+
 	public function getFirstControlPoint() : h3d.col.Point {
 		var absPos = getAbsPos();
 		var right = absPos.front();
@@ -66,13 +66,16 @@ class Spline extends Object3D {
 	public var points : Array<SplinePoint> = [];
 	public var shape : CurveShape = Quadratic;
 
+	var data : SplineData;
+	var step : Float = 1.0;
+	var threshold : Float = 0.01;
+
 	// Save/Load the curve as an array of absPos
 	public var pointsData : Array<h3d.Matrix> = [];
 
 	// Graphic
 	public var showSpline : Bool = true;
 	public var lineGraphics : h3d.scene.Graphics;
-	public var linePrecision : Int = 15;
 	public var lineThickness : Int = 4;
 	public var color : Int = 0xFFFFFFFF;
 	public var loop : Bool = false;
@@ -81,8 +84,6 @@ class Spline extends Object3D {
 	public var editor : hide.prefab.SplineEditor;
 	#end
 
-	var computedLength = -1.0;
-
 	override function save() {
 		var obj : Dynamic = super.save();
 
@@ -97,10 +98,11 @@ class Spline extends Object3D {
 		}
 		obj.shape = shape.getIndex();
 		obj.color = color;
-		obj.linePrecision = linePrecision;
 		obj.lineThickness = lineThickness;
 		obj.loop = loop;
 		obj.showSpline = showSpline;
+		obj.step = step;
+		obj.threshold = threshold;
 		return obj;
 	}
 
@@ -109,10 +111,11 @@ class Spline extends Object3D {
 		pointsData = obj.points == null ? [] : obj.points;
 		shape = obj.shape == null ? Linear : CurveShape.createByIndex(obj.shape);
 		color = obj.color != null ? obj.color : 0xFFFFFFFF;
-		linePrecision = obj.linePrecision == null ? 15 : obj.linePrecision;
 		lineThickness = obj.lineThickness == null ? 4 : obj.lineThickness;
 		loop = obj.loop == null ? false : obj.loop;
 		showSpline = obj.showSpline == null ? true : obj.showSpline;
+		step = obj.step == null ? 1.0 : obj.step;
+		obj.threshold = obj.threshold == null ? 0.01 : obj.threshold;
 	}
 
 	override function makeInstance( ctx : hrt.prefab.Context ) : hrt.prefab.Context {
@@ -126,7 +129,7 @@ class Spline extends Object3D {
 			sp.setTransform(pd);
 			points.push(sp);
 		}
-		pointsData = null;
+		pointsData = [];
 
 		if( points == null || points.length == 0 ) {
 			points.push(new SplinePoint(0,0,0, ctx.local3d));
@@ -146,15 +149,29 @@ class Spline extends Object3D {
 		#end
 	}
 
-	// Return the length of the spline, use the computed data if available
-	function getLength( ?precision : Float = 1.0 ) : Float {
-		if( computedLength > 0 )
-			return computedLength;
-		var sum = 0.0;
-		for( i in 0 ... points.length - 1 ) {
-			sum += getLengthBetween(points[i], points[i+1], precision);
+	// Return an interpolation of two samples at length l, 0 <= l <= splineLength
+	function getPointAtLength( l : Float ) : h3d.col.Point {
+		if( data == null )
+			computeSplineData();
+
+		// The last point is not at the same distance, be aware of that case
+		var s1 : Int = hxd.Math.floor(l / step);
+		var s2 : Int = hxd.Math.ceil(l / step);
+		s1 = cast hxd.Math.clamp(s1, 0, data.samples.length - 1);
+		s2 = cast hxd.Math.clamp(s2, 0, data.samples.length - 1);
+
+		// End/Beginning of the curve, just return the point
+		if( s1 == s2 ) 
+			return data.samples[s1].pos;
+		// Linear interpolation between the two samples
+		else {
+			var segmentLength = data.samples[s1].pos.distance(data.samples[s2].pos);
+			//trace(segmentLength);
+			var t = (l - (s1 * step)) / segmentLength;
+			var result = new h3d.Vector();
+			result.lerp(data.samples[s1].pos.toVector(), data.samples[s2].pos.toVector(), t);
+			return result.toPoint();
 		}
-		return sum;
 	}
 
 	// Return the euclidean distance between the two points
@@ -171,46 +188,73 @@ class Spline extends Object3D {
 		return p1.getPoint().distance(p2.getPoint());
 	}
 
-	// Approximative length calculation : compute the sum of the euclidean distance between a variable amount of points on the curve
-	function getLengthBetween( p1 : SplinePoint, p2 : SplinePoint,  ?stepSize : Float = 1.0, ?threshold : Float = 0.01 ) : Float {
+	// Return the sum of the euclidean distances between each samples
+	public function getLength() {
+		if( data == null )
+			computeSplineData();
+		return data.length;
+	}
 
-		if( shape == Linear ) 
-			return getMinLengthBetween(p1, p2);
-		
-		if( p1.distanceToNextPoint > 0 )
-			return p1.distanceToNextPoint;
+	// Sample the spline with the step and threshold
+	function computeSplineData() {
 
-		var pointList : Array<h3d.col.Point> = [];
-		pointList.push( points[0].getPoint() );
+		if( step <= 0 ) 
+			return;
+		
+		var sd = new SplineData();
 
+		// Sample the spline
+		var samples : Array<SplinePointData> = [{ pos : points[0].getPoint(), tangent : points[0].getTangent(), prev : null, next : points[1] }];
+		var i = 0;
 		var sumT = 0.0;
-		var t = 1.0;
-		while( sumT < 1 ) {
-			var p = getPointBetween(t, points[0], points[1]);
-			var distance = p.distance(pointList[pointList.length - 1]);
-
-			if( hxd.Math.abs(distance - stepSize) < threshold ) {
-				pointList.push( p );
+		var maxT = 1.0;
+		var minT = 0.0;
+		while( i < points.length - 1 ) {
+
+			var t = (maxT + minT) * 0.5;
+			var p = getPointBetween(t, points[i], points[i+1]);
+			var curSegmentLength = p.distance(samples[samples.length - 1].pos);
+			
+			// Point found
+			if( hxd.Math.abs(curSegmentLength - step) <= threshold ) {
+				samples.insert(samples.length, { pos : p, tangent : getTangentBetween(t, points[i], points[i+1]), prev : points[i], next : points[i+1], t : t });
 				sumT = t;
-				t = 1.0;
+				maxT = 1.0;
+				minT = sumT;
+				// Last point of the curve too close from the last sample
+				if( points[i+1].getPoint().distance(samples[samples.length - 1].pos) < step ) {
+					// End of the spline
+					if( i == points.length - 2 ) {
+						samples.insert(samples.length, { pos : points[points.length - 1].getPoint(), tangent : points[points.length - 1].getTangent(), prev : points[points.length - 2], next : null, t : 1.0 });
+						break;
+					}
+					// End of the current curve
+					else {
+						i++;
+						sumT = 0.0;
+						minT = 0.0;
+						maxT = 1.0;
+					}
+				}
+			}
+			// Point not found
+			else if( curSegmentLength > step ) {
+				maxT = maxT - (maxT - minT) * 0.5;
+			}
+			else if( curSegmentLength < step ) {
+				minT = minT + (maxT - minT) * 0.5;
 			}
-			else if( distance > stepSize ) 
-				t -= (t - sumT) * 0.5;
-			else if( distance < stepSize ) 
-				t += (t - sumT) * 0.5;
 		}
+		sd.samples = samples;
 
-		var sum = 0.0;
-		for( i in 0 ... pointList.length - 1 ) 
-			sum += pointList[i].distance(pointList[i + 1]);
-
-		p1.distanceToNextPoint = sum;
-
-		return sum;
-	}
+		// Compute the average length of the spline
+		var lengthSum = 0.0;
+		for( i in 0 ... samples.length - 1 ) {
+			lengthSum += samples[i].pos.distance(samples[i+1].pos);
+		}
+		sd.length = lengthSum;
 
-	function computeSplineData( ?precision : Float = 1.0 ) {
-		var d = new SplineComputedData();
+		data = sd;
 	}
 
 	// Return the closest spline point on the spline from p
@@ -228,130 +272,23 @@ class Spline extends Object3D {
 	}
 
 	// Return the closest point on the spline from p
-	function getClosestPoint( p : h3d.col.Point, ?precision : Float = 1.0 ) : SplinePointData {
-		var length = getLength(precision);
-		var stepCount = hxd.Math.ceil(length * precision);
+	function getClosestPoint( p : h3d.col.Point ) : SplinePointData {
+
+		if( data == null )
+			computeSplineData();
+
 		var minDist = -1.0;
-		var result : SplinePointData = { pos : null, tangent : null, prev : null, next : null };
-		for( i in 0 ... stepCount ) {
-			var pt = getPoint( i / stepCount );
-			var dist = pt.pos.distance(p);
+		var result : SplinePointData = null;
+		for( s in data.samples ) {
+			var dist = s.pos.distance(p);
 			if( dist < minDist || minDist == -1 ) {
 				minDist = dist;
-				result = pt;
+				result = s;
 			}
 		}
 		return result;
 	}
 
-	// Return the point on the spline at t, 0 <= t <= 1
-	function getPoint( l : Float, ?step : Float = 1.0, ?threshold : Float = 0.01 ) : SplinePointData {
-		
-		// Early return, easy case
-		if( points.length == 1 ) return { 	pos : points[0].getPoint(),
-											tangent : points[0].getAbsPos().right().toPoint(),
-											prev : null, 
-											next : null, 
-											lengthPos : 0  } ;
-
-		if( l == 0 ) return { 	pos : points[0].getPoint(),
-								tangent : points[0].getFirstControlPoint().sub(points[0].getPoint()),
-								prev : points[0], 
-								next : points[0], 
-								lengthPos : 0  } ;
-
-		if( l == 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], 
-								lengthPos : 1  };
-
-		var targetLength = l * getLength(100);
-		//trace("Try to find a point at " + targetLength);
-
-		// Find the curve of the spline wich contain the asked length
-		var p0 : SplinePoint = null;
-		var p1 : SplinePoint = null;
-		var curveLengthSum = 0.0;
-		for( i in 0 ... points.length - 1 ) {
-			var curveLength = getLengthBetween(points[i], points[i+1], step, threshold);
-			// Step on the curve and get the segment wich contain the asked length
-			if( targetLength <= curveLengthSum + curveLength ) {
-				p0 = points[i];
-				p1 = points[i + 1];
-				//trace("Try to find a point on the curve " + i + " -> " + (i + 1));
-			}
-			curveLengthSum += curveLength;
-		}
-
-		// Step on the curve and get the segment wich contain the asked length
-		var lastP = p0.getPoint();
-		var lengthSum = 0.0;
-		var minT = 0.0;
-		var t = 1.0;
-		while( minT < 1 ) {
-			var p = getPointBetween(t, p0, p1);
-			var curSegmentLength = p.distance(lastP);
-			if( curSegmentLength - step < threshold ) {
-				//trace("Try to find the point on the segment" + lastP + " - " + p + " of length " + lengthSum + " -> " + (lengthSum + curSegmentLength));	
-				// Lerp on the segment wich contain the asked length
-				if( curSegmentLength + lengthSum > targetLength ) {
-					//trace("Found the segment of the curve " + lastP + " - " + p );
-					var finalT = hxd.Math.lerp(minT, t, (targetLength - lengthSum ) / curSegmentLength );
-					trace("Found the segment of the curve " + finalT );
-					return { 	pos : getPointBetween(finalT, p0, p1),
-								tangent : getTangentBetween(finalT, p0, p1),
-								prev : p0, 
-								next : p1, 
-								lengthPos : finalT  };
-				}
-				lastP = p;
-				minT = t;
-				lengthSum += curSegmentLength;
-				t = 1.0;
-			}
-			else if( curSegmentLength > threshold ) 
-				t -= (t - minT) * 0.5;
-			else if( curSegmentLength < threshold ) 
-				t += (t - minT) * 0.5;
-		}
-
-		return { pos : null, tangent : null, prev : null, next : null };
-	}
-
-	// Return the point on the spline at t, 0 <= t <= 1
-	/*function getPoint( t : Float, ?precision : Float = 1.0 ) : SplinePointData {
-		t = hxd.Math.clamp(t, 0, 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(precision);
-		var length = totalLength * t;
-		var curlength = 0.0;
-		for( i in 0 ... points.length - 1 ) {
-			var curSegmentLength = getLengthBetween(points[i], points[i+1], precision);
-			curlength += curSegmentLength;
-			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 : null, tangent : null, prev : null, next : null };
-	}*/
-
 	// 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) {
@@ -403,7 +340,9 @@ class Spline extends Object3D {
 	#if editor
 
 	function generateBezierCurve( ctx : hrt.prefab.Context ) {
-		
+
+		computeSplineData();
+
 		if( !showSpline ) {
 			if( lineGraphics != null ) {
 				lineGraphics.remove();
@@ -412,9 +351,6 @@ class Spline extends Object3D {
 			return;
 		}
 
-		if( points == null )
-			return;
-
 		if( lineGraphics == null ) {
 			lineGraphics = new h3d.scene.Graphics(ctx.local3d);
 			lineGraphics.lineStyle(lineThickness, color);
@@ -423,51 +359,11 @@ class Spline extends Object3D {
 			lineGraphics.ignoreParentTransform = false;
 		}
 
-		computedLength = -1;
-		for( sp in points ) {
-			sp.distanceToNextPoint = -1;
-		}
-
-		var curve : Array<h3d.col.Point> = [];
-		switch (shape) {
-			case Linear:
-				for( sp in points )
-					curve.push(sp.getPoint());
-				if( loop && points.length > 1 )
-					curve.push(points[0].getPoint());
-			case Quadratic:
-				var i = 0;
-				while( i < points.length - 1 ) {
-					for( v in 0 ... linePrecision + 1 ) {
-						curve.push(getQuadraticBezierPoint( v / linePrecision, points[i].getPoint(), points[i].getSecondControlPoint(), points[i+1].getPoint()));
-					}
-					++i;
-				}
-				if( loop && points.length > 1 ) {
-					for( v in 0 ... linePrecision + 1 ) {
-						curve.push(getQuadraticBezierPoint( v / linePrecision, points[points.length - 1].getPoint(), points[points.length - 1].getSecondControlPoint(), points[0].getPoint()));
-					}
-				}
-			case Cubic:
-				var i = 0;
-				while( i < points.length - 1 ) {
-					for( v in 0 ... linePrecision + 1 ) {
-						curve.push(getCubicBezierPoint( v / linePrecision, points[i].getPoint(), points[i].getSecondControlPoint(), points[i+1].getFirstControlPoint(), points[i+1].getPoint()));
-					}
-					++i;
-				}
-				if( loop && points.length > 1 ) {
-					for( v in 0 ... linePrecision + 1 ) {
-						curve.push(getCubicBezierPoint( v / linePrecision, points[points.length - 1].getPoint(), points[points.length - 1].getSecondControlPoint(), points[0].getFirstControlPoint(), points[0].getPoint()));
-					}
-				}
-		}
-
 		lineGraphics.lineStyle(lineThickness, color);
 		lineGraphics.clear();
 		var b = true;
-		for( p in curve ) {
-			var localPos = ctx.local3d.globalToLocal(p.toVector());
+		for( s in data.samples ) {
+			var localPos = ctx.local3d.globalToLocal(s.pos.toVector());
 			b ? lineGraphics.moveTo(localPos.x, localPos.y, localPos.z) : lineGraphics.lineTo(localPos.x, localPos.y, localPos.z);
 			b = false;
 		}
@@ -488,7 +384,8 @@ class Spline extends Object3D {
 				<dl>
 					<dt>Color</dt><dd><input type="color" alpha="true" field="color"/></dd>
 					<dt>Thickness</dt><dd><input type="range" min="1" max="10" field="lineThickness"/></dd>
-					<dt>Precision</dt><dd><input type="range" step="1" min="1" max="100" field="linePrecision"/></dd>
+					<dt>Step</dt><dd><input type="range" min="0.1" max="10" field="step"/></dd>
+					<dt>Threshold</dt><dd><input type="range" min="0.001" max="1" field="threshold"/></dd>
 					<dt>Show Spline</dt><dd><input type="checkbox" field="showSpline"/></dd>
 					<dt>Type</dt>
 						<dd>

+ 177 - 112
hrt/prefab/l3d/SplineMesh.hx

@@ -1,95 +1,93 @@
 package hrt.prefab.l3d;
+import h3d.scene.MeshBatch;
 import h3d.scene.Mesh;
 import hrt.prefab.l3d.Spline.SplinePoint;
 
-
 class SplineMeshShader extends hxsl.Shader {
 
 	static var SRC = {
 		@:import h3d.shader.BaseMesh;
 
-		@const var POINT_COUNT : Int;
-		@const var CURVE_COUNT : Int;
-		@const var CURVE_TYPE : Int;
-		@param var points : Array<Vec4, POINT_COUNT>;
-		@param var lengths : Array<Vec4, CURVE_COUNT>;
-		@param var totalLength : Float;
+		// Spline Infos
+		@const(4096) var POINT_COUNT : Int;
+		@param var stepSize : Float;
+		@param var points : Buffer<Vec4, POINT_COUNT>;
+
+		// Instance Infos
+		@param var modelMat : Mat4;
 		@param var splinePos : Float;
 
-		function getCubicBezierPoint( t : Float, p0 : Vec4, p1 : Vec4, p2 : Vec4, p3 : Vec4 ) : Vec3 {
-			return (p0 * (1 - t) * (1 - t) * (1 - t) + p1 * t * 3 * (1 - t) * (1 - t) + p2 * t * t * 3 * (1 - t) + p3 * t * t * t).xyz;
-		}
-		function getCubicBezierTangent( t : Float, p0 : Vec4, p1 : Vec4, p2 : Vec4, p3 : Vec4 ) : Vec3 {
-			return (3 * (1 - t) * (1 - t) * (p1 - p0) + 6 * (1 - t) * t * (p2 - p1) + 3 * t * t * (p3 - p2)).xyz.normalize();
-		}
+		var calculatedUV : Vec2;
 
 		function vertex() {
-			var pos = splinePos + relativePosition.y; // TO DO : Add modelView transform
-			pos = clamp(pos, 0, totalLength);
-			var curlength = 0.0;
-			var i = 0;
-			while( i < POINT_COUNT - 3 ) {
-				var curSegmentLength = lengths[i].r;
-				curlength += curSegmentLength;
-				if( pos <= curlength ) {
-					
-					/*var targetL = pos;
-					var curL = 0.0;	
-					var curT = 0.0;
-					var curP = getCubicBezierPoint(curT, points[i * 3 + 1], points[i * 3 + 2], points[i * 3 + 3], points[i * 3 + 4]);
-					var dist = curP.distance()
-					while( abs(targetL - curL) > 0.1 ) {
-						var p = getCubicBezierPoint(curT, points[i * 3 + 1], points[i * 3 + 2], points[i * 3 + 3], points[i * 3 + 4]);
-						var dist = curSegmentLength * p;
-						true ? curT *= 0.5 : curT *= -0.5;
-					}*/
-
-					var t = (pos - (curlength - curSegmentLength)) / curSegmentLength;
-
-					var point = getCubicBezierPoint(t, points[i * 3 + 1], points[i * 3 + 2], points[i * 3 + 3], points[i * 3 + 4]);
-					var worldUp = vec3(0,0,1);
-					var front = -getCubicBezierTangent(t, points[i * 3 + 1], points[i * 3 + 2], points[i * 3 + 3], points[i * 3 + 4]);
-					var right = -front.cross(worldUp).normalize();
-					var up = front.cross(right).normalize();			
-
-					var rotation = mat4(	vec4(right.x, front.x, up.x, 0), 
-											vec4(right.y, front.y, up.y, 0), 
-											vec4(right.z, front.z, up.z, 0), 
-											vec4(0,0,0,1));
-
-					var translation = mat4(	vec4(1,0,0, point.x ), 
-											vec4(0,1,0, point.y ), 
-											vec4(0,0,1, point.z ), 
-											vec4(0,0,0,1));
-
-					var transform = rotation * translation;
-
-					var localPos = relativePosition - vec3(0,relativePosition.y,0);
-
-					transformedPosition = localPos* transform.mat3x4(); 
-					break;
-				}
-				i += 3;
-			}
+
+			var modelPos = relativePosition * modelMat.mat3x4();
+			var pos = splinePos + modelPos.y;
+			pos = clamp(pos, 0.0, POINT_COUNT * stepSize);
+			var offsetY = pos - splinePos;
+
+			// Linear Interpolation between two samples
+			var s1 = floor(pos / stepSize).int();
+			var s2 = ceil(pos / stepSize).int();
+			var t = (pos - (s1 * stepSize)) / stepSize;
+			t.saturate();
+			var point = mix(points[s1 * 2], points[s2 * 2], t).xyz;
+			var tangent = mix(points[s1 * 2 + 1], points[s2 * 2 + 1], t).xyz;
+
+			// Construct the new transform
+			var worldUp = vec3(0,0,1);
+			var front = -tangent;
+			var right = -front.cross(worldUp).normalize();
+			var up = front.cross(right).normalize();			
+
+			var rotation = mat4(	vec4(right.x, front.x, up.x, 0),
+									vec4(right.y, front.y, up.y, 0),
+									vec4(right.z, front.z, up.z, 0),
+									vec4(0,0,0,1));
+
+			var translation = mat4(	vec4(1,0,0, point.x ),
+									vec4(0,1,0, point.y ),
+									vec4(0,0,1, point.z ),
+									vec4(0,0,0,1));
+
+			var transform = rotation * translation;
+
+			var localPos = (modelPos - vec3(0, offsetY, 0));
+			transformedPosition = localPos * transform.mat3x4();
+			transformedNormal = transformedNormal * modelMat.mat3x4() * rotation.mat3x4();
 		}
 
 		function fragment() {
-			//pixelColor = vec4(1,0,0,1);
 		}
 	}
 }
 
+enum SplineMeshMode {
+	MultiMesh;
+	Instanced;
+	BigGeometry;
+}
 
 class SplineMesh extends Spline {
 
 	var meshPath : String;
 	var meshes : Array<h3d.scene.Mesh> = [];
+
 	var spacing: Float = 0.0;
+	var meshScale = new h3d.Vector(1,1,1);
+	var meshRotation = new h3d.Vector(0,0,0);
+	var modelMat = new h3d.Matrix();
+
+	var meshBatch : h3d.scene.MeshBatch = null;
+	var meshPrimitive : h3d.prim.MeshPrimitive = null;
+	var meshMaterial : h3d.mat.Material = null;
 
 	override function save() {
 		var obj : Dynamic = super.save();
 		obj.meshPath = meshPath;
 		obj.spacing = spacing;
+		obj.meshScale = meshScale;
+		obj.meshRotation = meshRotation;
 		return obj;
 	}
 
@@ -97,24 +95,25 @@ class SplineMesh extends Spline {
 		super.load(obj);
 		meshPath = obj.meshPath;
 		spacing = obj.spacing == null ? 0.0 : obj.spacing;
+		meshScale = obj.meshScale == null ? new h3d.Vector(1,1,1) : obj.meshScale;
+		meshRotation = obj.meshRotation == null ? new h3d.Vector(0,0,0) : obj.meshRotation;
 	}
 
 	override function makeInstance( ctx : hrt.prefab.Context ) : hrt.prefab.Context {
-		var ctx = ctx.clone(this);
 
+		var ctx = ctx.clone(this);
 		ctx.local3d = new h3d.scene.Object(ctx.local3d);
 		ctx.local3d.name = name;
-
+		
 		for( pd in pointsData ) {
 			var sp = new SplinePoint(0, 0, 0, ctx.local3d);
 			sp.setTransform(pd);
 			points.push(sp);
 		}
-		pointsData = null;
+		pointsData = [];
 
-		if( points == null || points.length == 0 ) {
+		if( points == null || points.length == 0 ) 
 			points.push(new SplinePoint(0,0,0, ctx.local3d));
-		}
 
 		updateInstance(ctx);
 		return ctx;
@@ -123,61 +122,121 @@ class SplineMesh extends Spline {
 	override function updateInstance( ctx : hrt.prefab.Context , ?propName : String ) {
 		super.updateInstance(ctx, propName);
 
-		for( m in meshes ) {
-			m.remove();
+		var rot = new h3d.Matrix();
+		rot.initRotation(hxd.Math.degToRad(meshRotation.x), hxd.Math.degToRad(meshRotation.y), hxd.Math.degToRad(meshRotation.z));
+		var scale = new h3d.Matrix();
+		scale.initScale(meshScale.x, meshScale.y, meshScale.z);
+		modelMat.multiply(scale, rot);
+		
+		createMeshPrimitive(ctx);
+		createMeshBatch(ctx);
+
+		// Remake the material
+		for( c in @:privateAccess children ) {
+			var mat = Std.downcast(c, Material);
+			if( mat != null && mat.enabled ) 
+				@:privateAccess mat.updateObject(ctx, meshBatch);
+			var shader = Std.downcast(c, Shader);
+			if( shader != null && shader.enabled ) 
+				shader.makeInstance(ctx);
 		}
-		meshes = [];
-		if( meshPath != null ) {
-			var meshTemplate = ctx.loadModel(meshPath).toMesh();
+
+		//createMultiMeshes(ctx);
+	}
+
+	function createMeshPrimitive( ctx : Context  ) {
+		meshPrimitive = null;
+		meshMaterial = null;
+		if( meshPath != null ) {	
+			var meshTemplate : h3d.scene.Mesh = ctx.loadModel(meshPath).toMesh();
 			if( meshTemplate != null ) {
-				var step = hxd.Math.abs(meshTemplate.primitive.getBounds().yMax) + hxd.Math.abs(meshTemplate.primitive.getBounds().yMin) + spacing;
-				var length = getLength(0.1);
-				var stepCount = hxd.Math.ceil(length / step);
-				for( i in 0 ... stepCount ) {
-					var m : h3d.scene.Mesh = cast meshTemplate.clone();
-					//m.material.mainPass.setPassName("beforeTonemapping");
-					m.material.castShadows = false;
-					m.ignoreParentTransform = true;
-					var p = getPoint(i / stepCount, 1.0, 0.001);
-					if( p.pos != null ) m.setPosition(p.pos.x, p.pos.y, p.pos.z);
-					//var s = createShader();
-					//m.material.mainPass.addShader(s);
-					//s.splinePos = (i / stepCount) * length;
-					ctx.local3d.addChild(m);
-					meshes.push(m);
-				}
+				meshPrimitive = cast meshTemplate.primitive;
+				meshMaterial = meshTemplate.material;
 			}
 		}
 	}
 
+	function createMeshBatch( ctx : Context ) {
+
+		if( meshPrimitive == null ) {
+			if( meshBatch != null ) {
+				meshBatch.remove();
+				meshBatch = null;
+			}
+			return;
+		}
+
+		if( meshBatch != null /*&& meshBatch.primitive != meshPrimitive*/ ) {
+			meshBatch.remove();
+			meshBatch = null;
+		}
+	
+		if( meshBatch == null ) {
+			var material : h3d.mat.Material = cast meshMaterial.clone();
+			var splinemeshShader = createShader();
+			material.mainPass.addShader(splinemeshShader);
+			material.castShadows = false;
+			meshBatch = new MeshBatch(meshPrimitive, material, ctx.local3d);
+			meshBatch.ignoreParentTransform = true;
+		}
+
+		var localBounds = meshPrimitive.getBounds().clone();
+		localBounds.transform(modelMat);
+		var minOffset = hxd.Math.abs(localBounds.yMin);
+		var step = (hxd.Math.abs(localBounds.yMax) + hxd.Math.abs(localBounds.yMin)) + spacing;
+		var stepCount = hxd.Math.ceil((getLength() - minOffset) / step);
+
+		var splinemeshShader = meshBatch.material.mainPass.getShader(SplineMeshShader);
+		
+		meshBatch.begin(stepCount);
+		for( i in 0 ... stepCount ) {
+			splinemeshShader.splinePos = i * step + minOffset;
+			meshBatch.emitInstance();
+		}
+	}
+
+	function createMultiMeshes( ctx : Context ) {
+
+		for( m in meshes ) 
+			m.remove();
+
+		meshes = [];
+
+		if( meshPrimitive == null )
+			return;
+
+		var localBounds = meshPrimitive.getBounds().clone();
+		localBounds.transform(modelMat);
+		var minOffset = hxd.Math.abs(localBounds.yMin);
+		var step = (hxd.Math.abs(localBounds.yMax) + hxd.Math.abs(localBounds.yMin)) + spacing;
+
+		var length = getLength();
+		var stepCount = hxd.Math.ceil((length - minOffset) / step);
+		for( i in 0 ... stepCount ) {
+			var m = new h3d.scene.Mesh(meshPrimitive, cast meshMaterial.clone());
+			m.material.castShadows = false;
+			m.ignoreParentTransform = true;
+			m.material.mainPass.culling = None;
+			var s = createShader();
+			m.material.mainPass.addShader(s);
+			s.splinePos = i * step + minOffset;
+			ctx.local3d.addChild(m);
+			meshes.push(m);
+		}
+	}
+
 	function createShader() {
 		var s = new SplineMeshShader();
-		s.CURVE_COUNT = points.length - 1;
-		s.totalLength = getLength();
-		s.lengths = [ for( i in 0 ...  points.length - 1 ) new h3d.Vector(getLengthBetween(points[i] , points[i + 1])) ];
-		switch shape {
-			case Linear: 
-				s.POINT_COUNT = points.length;
-				s.CURVE_TYPE = 0;
-				s.points = [ for(sp in points) {
-					sp.getPoint().toVector();
-				}];
-			case Quadratic:
-				s.POINT_COUNT = points.length * 3;
-				s.CURVE_TYPE = 1;
-				s.points = [ for(sp in points) {
-					sp.getPoint().toVector();
-					sp.getSecondControlPoint().toVector();
-				}];
-			case Cubic: 
-				s.POINT_COUNT = points.length * 3;
-				s.CURVE_TYPE = 2;
-				for(sp in points) {
-					s.points.push(sp.getFirstControlPoint().toVector());
-					s.points.push(sp.getPoint().toVector());
-					s.points.push(sp.getSecondControlPoint().toVector());
-				}
+		s.POINT_COUNT = data.samples.length;
+		s.stepSize = step;
+		s.modelMat = modelMat;
+		var bufferData = new hxd.FloatBuffer(s.POINT_COUNT * 4 * 2);
+		for( s in data.samples ) {
+			bufferData.push(s.pos.x); bufferData.push(s.pos.y); bufferData.push(s.pos.z); bufferData.push(0.0);
+			bufferData.push(s.tangent.x); bufferData.push(s.tangent.y); bufferData.push(s.tangent.z); bufferData.push(0.0);
 		}
+		s.points = new h3d.Buffer(s.POINT_COUNT, 4 * 2, [UniformBuffer]);
+		s.points.uploadVector(bufferData, 0, s.points.vertices, 0);
 		return s;
 	}
 
@@ -195,6 +254,12 @@ class SplineMesh extends Spline {
 				<dl>
 					<dt>Mesh</dt><dd><input type="model" field="meshPath"/></dd>
 					<dt>Spacing</dt><dd><input type="range" min="0" max="10" field="spacing"/></dd>
+					<dt>Scale X</dt><dd><input type="range" min="0" max="5" value="1" field="meshScale.x"/></dd>
+					<dt>Scale Y</dt><dd><input type="range" min="0" max="5" value="1" field="meshScale.y"/></dd>
+					<dt>Scale Z</dt><dd><input type="range" min="0" max="5" value="1" field="meshScale.z"/></dd>
+					<dt>Rotation X</dt><dd><input type="range" min="-180" max="180" value="0" field="meshRotation.x" /></dd>
+					<dt>Rotation Y</dt><dd><input type="range" min="-180" max="180" value="0" field="meshRotation.y" /></dd>
+					<dt>Rotation Z</dt><dd><input type="range" min="-180" max="180" value="0" field="meshRotation.z" /></dd>
 				</dl>
 			</div>
 			'), this, function(pname) { ctx.onChange(this, pname); });