Ver Fonte

Add Spline Mesh ( not definitive )

ShiroSmith há 6 anos atrás
pai
commit
baa43c366e
3 ficheiros alterados com 354 adições e 25 exclusões
  1. 1 1
      hide/prefab/SplineEditor.hx
  2. 144 24
      hrt/prefab/l3d/Spline.hx
  3. 209 0
      hrt/prefab/l3d/SplineMesh.hx

+ 1 - 1
hide/prefab/SplineEditor.hx

@@ -195,7 +195,7 @@ class SplineEditor {
 
 
 		var result : SplinePointData = null;
 		var result : SplinePointData = null;
 		var mousePos = new h3d.Vector( mouseX / h3d.Engine.getCurrent().width, 1.0 - mouseY / h3d.Engine.getCurrent().height, 0);
 		var mousePos = new h3d.Vector( mouseX / h3d.Engine.getCurrent().width, 1.0 - mouseY / h3d.Engine.getCurrent().height, 0);
-		var length = prefab.getLength();
+		var length = prefab.getLength(precision);
 		var stepCount = hxd.Math.ceil(length * precision);
 		var stepCount = hxd.Math.ceil(length * precision);
 		var minDist = -1.0;
 		var minDist = -1.0;
 		for( i in 0 ... stepCount ) {
 		for( i in 0 ... stepCount ) {

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

@@ -1,5 +1,7 @@
 package hrt.prefab.l3d;
 package hrt.prefab.l3d;
 
 
+import hxd.Math;
+
 enum CurveShape {
 enum CurveShape {
 	Linear;
 	Linear;
 	Quadratic;
 	Quadratic;
@@ -10,7 +12,21 @@ typedef SplinePointData = {
 	pos : h3d.col.Point,
 	pos : h3d.col.Point,
 	tangent : h3d.col.Point,
 	tangent : h3d.col.Point,
 	prev : SplinePoint,
 	prev : SplinePoint,
-	next : SplinePoint
+	next : SplinePoint,
+	?lengthPos : Float
+}
+
+class CurveComputedData {
+	public var length : Float;
+	public var points : Array<h3d.col.Point> = [];
+	public function new() {}
+}
+
+class SplineComputedData {
+	public var length : Float;
+	public var precision : Float;
+	public var curves : Array<CurveComputedData> = [];
+	public function new() {}
 }
 }
 
 
 class SplinePoint extends h3d.scene.Object {
 class SplinePoint extends h3d.scene.Object {
@@ -47,10 +63,14 @@ class SplinePoint extends h3d.scene.Object {
 
 
 class Spline extends Object3D {
 class Spline extends Object3D {
 
 
-	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;
 
 
+	// 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 lineGraphics : h3d.scene.Graphics;
 	public var linePrecision : Int = 15;
 	public var linePrecision : Int = 15;
 	public var lineThickness : Int = 4;
 	public var lineThickness : Int = 4;
@@ -80,6 +100,7 @@ class Spline extends Object3D {
 		obj.linePrecision = linePrecision;
 		obj.linePrecision = linePrecision;
 		obj.lineThickness = lineThickness;
 		obj.lineThickness = lineThickness;
 		obj.loop = loop;
 		obj.loop = loop;
+		obj.showSpline = showSpline;
 		return obj;
 		return obj;
 	}
 	}
 
 
@@ -91,6 +112,7 @@ class Spline extends Object3D {
 		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;
 		loop = obj.loop == null ? false : obj.loop;
 		loop = obj.loop == null ? false : obj.loop;
+		showSpline = obj.showSpline == null ? true : obj.showSpline;
 	}
 	}
 
 
 	override function makeInstance( ctx : hrt.prefab.Context ) : hrt.prefab.Context {
 	override function makeInstance( ctx : hrt.prefab.Context ) : hrt.prefab.Context {
@@ -150,35 +172,47 @@ class Spline extends Object3D {
 	}
 	}
 
 
 	// Approximative length calculation : compute the sum of the euclidean distance between a variable amount of points on the curve
 	// 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, ?precision : Float = 1.0 ) : Float {
+	function getLengthBetween( p1 : SplinePoint, p2 : SplinePoint,  ?stepSize : Float = 1.0, ?threshold : Float = 0.01 ) : Float {
 
 
-		if( shape == Linear ) {
+		if( shape == Linear ) 
 			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 maxLength = getMaxLengthBetween(p1, p2);
-		var minLength = getMinLengthBetween(p1, p2);
-		var stepCount = hxd.Math.ceil(precision * maxLength);
+		var pointList : Array<h3d.col.Point> = [];
+		pointList.push( points[0].getPoint() );
 
 
-		var curPt : h3d.col.Point = p1.getPoint();
-		var step = 1.0 / stepCount;
-		var t = step;
-		for( i in 0 ... stepCount ) {
-			var nextPt = getPointBetween(t, p1, p2);
-			sum += curPt.distance(nextPt);
-			curPt = nextPt;
-			t += step;
+		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 );
+				sumT = t;
+				t = 1.0;
+			}
+			else if( distance > stepSize ) 
+				t -= (t - sumT) * 0.5;
+			else if( distance < stepSize ) 
+				t += (t - sumT) * 0.5;
 		}
 		}
 
 
+		var sum = 0.0;
+		for( i in 0 ... pointList.length - 1 ) 
+			sum += pointList[i].distance(pointList[i + 1]);
+
 		p1.distanceToNextPoint = sum;
 		p1.distanceToNextPoint = sum;
 
 
 		return sum;
 		return sum;
 	}
 	}
 
 
+	function computeSplineData( ?precision : Float = 1.0 ) {
+		var d = new SplineComputedData();
+	}
+
 	// Return the closest spline point on the spline from p
 	// Return the closest spline point on the spline from p
 	function getClosestSplinePoint( p : h3d.col.Point ) : SplinePoint {
 	function getClosestSplinePoint( p : h3d.col.Point ) : SplinePoint {
 		var minDist = -1.0;
 		var minDist = -1.0;
@@ -195,7 +229,7 @@ class Spline extends Object3D {
 
 
 	// Return the closest point on the spline from p
 	// Return the closest point on the spline from p
 	function getClosestPoint( p : h3d.col.Point, ?precision : Float = 1.0 ) : SplinePointData {
 	function getClosestPoint( p : h3d.col.Point, ?precision : Float = 1.0 ) : SplinePointData {
-		var length = getLength();
+		var length = getLength(precision);
 		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, tangent : null, prev : null, next : null };
 		var result : SplinePointData = { pos : null, tangent : null, prev : null, next : null };
@@ -210,8 +244,83 @@ class Spline extends Object3D {
 		return result;
 		return result;
 	}
 	}
 
 
-	// Return the point on the spline between p1 and p2 at t, 0 <= t <= 1
-	function getPoint( t : Float, ?precision : Float = 1.0 ) : SplinePointData {
+	// 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);
 		t = hxd.Math.clamp(t, 0, 1);
 
 
 		if( points.length == 1 ) return { 	pos : points[0].getPoint(),
 		if( points.length == 1 ) return { 	pos : points[0].getPoint(),
@@ -229,7 +338,7 @@ class Spline extends Object3D {
 								prev : points[points.length - 1], 
 								prev : points[points.length - 1], 
 								next : points[points.length - 1] };
 								next : points[points.length - 1] };
 
 
-		var totalLength = getLength();
+		var totalLength = getLength(precision);
 		var length = totalLength * t;
 		var length = totalLength * t;
 		var curlength = 0.0;
 		var curlength = 0.0;
 		for( i in 0 ... points.length - 1 ) {
 		for( i in 0 ... points.length - 1 ) {
@@ -241,7 +350,7 @@ class Spline extends Object3D {
 			}
 			}
 		}
 		}
 		return { pos : null, tangent : null, prev : null, next : null };
 		return { pos : null, tangent : null, prev : null, next : null };
-	}
+	}*/
 
 
 	// Return the point on the curve between p1 and p2 at t, 0 <= t <= 1
 	// 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 {
 	inline function getPointBetween( t : Float, p1 : SplinePoint, p2 : SplinePoint ) : h3d.col.Point {
@@ -294,6 +403,14 @@ class Spline extends Object3D {
 	#if editor
 	#if editor
 
 
 	function generateBezierCurve( ctx : hrt.prefab.Context ) {
 	function generateBezierCurve( ctx : hrt.prefab.Context ) {
+		
+		if( !showSpline ) {
+			if( lineGraphics != null ) {
+				lineGraphics.remove();
+				lineGraphics = null;
+			}
+			return;
+		}
 
 
 		if( points == null )
 		if( points == null )
 			return;
 			return;
@@ -307,6 +424,9 @@ class Spline extends Object3D {
 		}
 		}
 
 
 		computedLength = -1;
 		computedLength = -1;
+		for( sp in points ) {
+			sp.distanceToNextPoint = -1;
+		}
 
 
 		var curve : Array<h3d.col.Point> = [];
 		var curve : Array<h3d.col.Point> = [];
 		switch (shape) {
 		switch (shape) {
@@ -369,7 +489,7 @@ class Spline extends Object3D {
 					<dt>Color</dt><dd><input type="color" alpha="true" field="color"/></dd>
 					<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>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>Precision</dt><dd><input type="range" step="1" min="1" max="100" field="linePrecision"/></dd>
-					<dt>Loop</dt><dd><input type="checkbox" field="loop"/></dd>
+					<dt>Show Spline</dt><dd><input type="checkbox" field="showSpline"/></dd>
 					<dt>Type</dt>
 					<dt>Type</dt>
 						<dd>
 						<dd>
 							<select field="shape" >
 							<select field="shape" >

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

@@ -0,0 +1,209 @@
+package hrt.prefab.l3d;
+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;
+		@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();
+		}
+
+		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;
+			}
+		}
+
+		function fragment() {
+			//pixelColor = vec4(1,0,0,1);
+		}
+	}
+}
+
+
+class SplineMesh extends Spline {
+
+	var meshPath : String;
+	var meshes : Array<h3d.scene.Mesh> = [];
+	var spacing: Float = 0.0;
+
+	override function save() {
+		var obj : Dynamic = super.save();
+		obj.meshPath = meshPath;
+		obj.spacing = spacing;
+		return obj;
+	}
+
+	override function load( obj : Dynamic ) {
+		super.load(obj);
+		meshPath = obj.meshPath;
+		spacing = obj.spacing == null ? 0.0 : obj.spacing;
+	}
+
+	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;
+
+		for( pd in pointsData ) {
+			var sp = new SplinePoint(0, 0, 0, ctx.local3d);
+			sp.setTransform(pd);
+			points.push(sp);
+		}
+		pointsData = null;
+
+		if( points == null || points.length == 0 ) {
+			points.push(new SplinePoint(0,0,0, ctx.local3d));
+		}
+
+		updateInstance(ctx);
+		return ctx;
+	}
+
+	override function updateInstance( ctx : hrt.prefab.Context , ?propName : String ) {
+		super.updateInstance(ctx, propName);
+
+		for( m in meshes ) {
+			m.remove();
+		}
+		meshes = [];
+		if( meshPath != null ) {
+			var meshTemplate = 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);
+				}
+			}
+		}
+	}
+
+	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());
+				}
+		}
+		return s;
+	}
+
+	#if editor
+
+	override function setSelected( ctx : hrt.prefab.Context , b : Bool ) {
+		super.setSelected(ctx, b);
+	}
+
+	override function edit( ctx : EditContext ) {
+		super.edit(ctx);
+
+		ctx.properties.add( new hide.Element('
+			<div class="group" name="Mesh">
+				<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>
+				</dl>
+			</div>
+			'), this, function(pname) { ctx.onChange(this, pname); });
+	}
+
+	override function getHideProps() : HideProps {
+		return { icon : "arrows-v", name : "SplineMesh" };
+	}
+
+	static var _ = hrt.prefab.Library.register("splineMesh", SplineMesh);
+	#end
+}