Bladeren bron

implemented RayCollider for Sphere and HMD

bstouls 9 jaren geleden
bovenliggende
commit
953dd75ccc

+ 131 - 0
h3d/col/Polygon.hx

@@ -0,0 +1,131 @@
+package h3d.col;
+
+class TriPlane implements RayCollider {
+
+	public var next : TriPlane;
+
+	var p0x : Float;
+	var p0y : Float;
+	var p0z : Float;
+
+	var d1x : Float;
+	var d1y : Float;
+	var d1z : Float;
+
+	var d2x : Float;
+	var d2y : Float;
+	var d2z : Float;
+
+	var dot00 : Float;
+	var dot01 : Float;
+	var dot11 : Float;
+	var invDenom : Float;
+	var nx : Float;
+	var ny : Float;
+	var nz : Float;
+	var d : Float;
+
+	public function new() {
+	}
+
+	public inline function init( p0 : Point, p1 : Point, p2 : Point ) {
+		p0x = p0.x;
+		p0y = p0.y;
+		p0z = p0.z;
+		var d1 = p1.sub(p0);
+		var d2 = p2.sub(p0);
+		var n = d1.cross(d2);
+		d = n.dot(p0);
+		nx = n.x;
+		ny = n.y;
+		nz = n.z;
+
+		d1x = d1.x;
+		d1y = d1.y;
+		d1z = d1.z;
+		d2x = d2.x;
+		d2y = d2.y;
+		d2z = d2.z;
+
+		dot00 = d1.dot(d1);
+		dot01 = d1.dot(d2);
+		dot11 = d2.dot(d2);
+		invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
+	}
+
+	inline public function rayIntersection( r : Ray, ?pt : Point ) @:privateAccess {
+		var dr = r.lx * nx + r.ly * ny + r.lz * nz;
+		if( dr > 0 ) // backface culling
+			return null;
+		var nd = d - (r.px * nx + r.py * ny + r.pz * nz);
+		var k = nd / dr;
+		if( k < 0 )
+			return null;
+		var px = r.px + r.lx * k;
+		var py = r.py + r.ly * k;
+		var pz = r.pz + r.lz * k;
+		if( !isPointInTriangle(px, py, pz) )
+			return null;
+		if( pt == null ) pt = new Point();
+		pt.x = px;
+		pt.y = py;
+		pt.z = pz;
+		return pt;
+	}
+
+	inline function isPointInTriangle( x : Float, y : Float, z : Float ) {
+		// Compute vectors
+		var v2 = new h3d.col.Point(x - p0x,  y - p0y, z - p0z);
+		var dot02 = d1x * v2.x + d1y * v2.y + d1z * v2.z;
+		var dot12 = d2x * v2.x + d2y * v2.y + d2z * v2.z;
+
+		// Compute barycentric coordinates
+		var u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+		var v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+
+		// Check if point is in triangle
+		return (u >= 0) && (v >= 0) && (u + v < 1);
+	}
+
+}
+
+
+class Polygon implements RayCollider {
+
+	var triPlanes : TriPlane;
+
+	public function new() {
+	}
+
+	public function addBuffers( vertexes : haxe.ds.Vector<hxd.impl.Float32>, indexes : haxe.ds.Vector<hxd.impl.UInt16>, stride = 3 ) {
+		for(i in 0...Std.int(indexes.length / 3)) {
+			var k = i * 3;
+
+			var t = new TriPlane();
+
+			var i0 = indexes[k] * stride;
+			var i1 = indexes[k + 1] * stride;
+			var i2 = indexes[k + 2] * stride;
+
+			t.init(
+				new Point(vertexes[i0], vertexes[i0 + 1], vertexes[i0 + 2]),
+				new Point(vertexes[i1], vertexes[i1 + 1], vertexes[i1 + 2]),
+				new Point(vertexes[i2], vertexes[i2 + 1], vertexes[i2 + 2])
+			);
+
+			t.next = triPlanes;
+			triPlanes = t;
+		}
+	}
+
+	public function rayIntersection( r : Ray, ?pt : Point ) {
+		var t = triPlanes;
+		while( t != null ) {
+			var p = t.rayIntersection(r, pt);
+			if( p != null ) return p;
+			t = t.next;
+		}
+		return null;
+	}
+
+}

+ 19 - 0
h3d/col/RayCollider.hx

@@ -5,3 +5,22 @@ interface RayCollider {
 	public function rayIntersection( r : Ray, ?p : Point ) : Null<Point>;
 
 }
+
+
+class OptimizedCollider implements RayCollider {
+
+	public var a : RayCollider;
+	public var b : RayCollider;
+
+	public function new(a, b) {
+		this.a = a;
+		this.b = b;
+	}
+
+	public function rayIntersection( r : Ray, ?p : Point ) : Null<Point> {
+		if( a.rayIntersection(r, p) == null )
+			return null;
+		return b.rayIntersection(r, p);
+	}
+
+}

+ 17 - 3
h3d/prim/HMDModel.hx

@@ -6,13 +6,14 @@ class HMDModel extends MeshPrimitive {
 	var dataPosition : Int;
 	var indexCount : Int;
 	var indexesTriPos : Array<Int>;
-	var entry : hxd.fs.FileEntry;
+	var lib : hxd.fmt.hmd.Library;
 	var curMaterial : Int;
+	var collider : h3d.col.RayCollider;
 
-	public function new(data, dataPos, entry) {
+	public function new(data, dataPos, lib) {
 		this.data = data;
 		this.dataPosition = dataPos;
-		this.entry = entry;
+		this.lib = lib;
 	}
 
 	override function triCount() {
@@ -35,6 +36,7 @@ class HMDModel extends MeshPrimitive {
 		dispose();
 		buffer = new h3d.Buffer(data.vertexCount, data.vertexStride);
 
+		var entry = @:privateAccess lib.entry;
 		entry.open();
 
 		entry.skip(dataPosition + data.vertexPosition);
@@ -78,4 +80,16 @@ class HMDModel extends MeshPrimitive {
 		curMaterial = -1;
 	}
 
+	override function getCollider() {
+		if( collider != null )
+			return collider;
+
+		var pos = lib.getBuffers(data, [new hxd.fmt.hmd.Data.GeometryFormat("position", DVec3)]);
+		var poly = new h3d.col.Polygon();
+		poly.addBuffers(pos.vertexes, pos.indexes);
+		var sphere = data.bounds.toSphere();
+		collider = new h3d.col.RayCollider.OptimizedCollider(sphere, poly);
+		return collider;
+	}
+
 }

+ 9 - 1
h3d/prim/Polygon.hx

@@ -8,6 +8,10 @@ class Polygon extends Primitive {
 	public var uvs : Array<UV>;
 	public var idx : hxd.IndexBuffer;
 	public var colors : Array<Point>;
+	var scaled = 1.;
+	var translatedX = 0.;
+	var translatedY = 0.;
+	var translatedZ = 0.;
 
 	public function new( points, ?idx ) {
 		this.points = points;
@@ -96,6 +100,9 @@ class Polygon extends Primitive {
 	}
 
 	public function translate( dx, dy, dz ) {
+		translatedX += dx;
+		translatedY += dy;
+		translatedZ += dz;
 		for( p in points ) {
 			p.x += dx;
 			p.y += dy;
@@ -104,6 +111,7 @@ class Polygon extends Primitive {
 	}
 
 	public function scale( s : Float ) {
+		scaled *= s;
 		for( p in points ) {
 			p.x *= s;
 			p.y *= s;
@@ -169,5 +177,5 @@ class Polygon extends Primitive {
 	override function vertexCount() {
 		return points.length;
 	}
-	
+
 }

+ 5 - 0
h3d/prim/Primitive.hx

@@ -13,6 +13,11 @@ class Primitive {
 		return 0;
 	}
 
+	public function getCollider() : h3d.col.RayCollider {
+		throw "not implemented";
+		return null;
+	}
+
 	public function getBounds() : h3d.col.Bounds {
 		throw "not implemented";
 		return null;

+ 8 - 2
h3d/prim/Sphere.hx

@@ -3,10 +3,12 @@ import h3d.col.Point;
 
 class Sphere extends Polygon {
 
+	var ray : Float;
 	var segsH : Int;
 	var segsW : Int;
 
-	public function new( segsW = 8, segsH = 6 ) {
+	public function new( ray = 1., segsW = 8, segsH = 6 ) {
+		this.ray = ray;
 		this.segsH = segsH;
 		this.segsW = segsW;
 		var t = 0., dt = Math.PI / segsH, dp = Math.PI * 2 / segsW;
@@ -20,7 +22,7 @@ class Sphere extends Polygon {
 				var px = st * Math.cos(p);
 				var py = st * Math.sin(p);
 				var i = pts.length;
-				pts.push(new Point(px, py, pz));
+				pts.push(new Point(px * ray, py * ray, pz * ray));
 				p += dp;
 				if( x != segsW ) {
 					idx.push(i);
@@ -38,6 +40,10 @@ class Sphere extends Polygon {
 		super(pts, idx);
 	}
 
+	override public function getCollider() : h3d.col.RayCollider {
+		return new h3d.col.Sphere(translatedX, translatedY, translatedZ, ray * scaled);
+	}
+
 	override function addNormals() {
 		normals = points;
 	}

+ 1 - 1
hxd/fmt/hmd/Library.hx

@@ -248,7 +248,7 @@ class Library {
 	function makePrimitive( id : Int ) {
 		var p = cachedPrimitives[id];
 		if( p != null ) return p;
-		p = new h3d.prim.HMDModel(header.geometries[id], header.dataPosition, entry);
+		p = new h3d.prim.HMDModel(header.geometries[id], header.dataPosition, this);
 		cachedPrimitives[id] = p;
 		return p;
 	}

+ 127 - 0
samples/interact/Main.hx

@@ -0,0 +1,127 @@
+class OptimizedIntersect implements h3d.col.RayCollider {
+
+	var sphere : h3d.col.Sphere;
+	// triangles
+
+	public function new( mesh : h3d.scene.Mesh ) {
+	}
+
+	public function rayIntersection( r : h3d.col.Ray, ?p : h3d.col.Point ) : Null<h3d.col.Point> {
+		//if( !checkRaySphere(r, sphere) )
+		//	return null;
+		// check triangles
+		// ...
+		return null;
+	}
+}
+
+class Main extends hxd.App {
+
+	var rnd : hxd.Rand;
+	var light : h3d.scene.DirLight;
+
+	function initInteract( i : h3d.scene.Interactive, m : h3d.scene.Mesh ) {
+		var beacon = null;
+		var color = m.material.color.clone();
+		i.onOver = function(e : hxd.Event) {
+			m.material.color.set(0, 1, 0);
+			var s = new h3d.prim.Sphere(1, 32, 32);
+			s.addNormals();
+			beacon = new h3d.scene.Mesh(s, m);
+			beacon.material.mainPass.enableLights = true;
+			beacon.material.color.set(1, 0, 0);
+			beacon.scale(0.05 / m.parent.scaleX);
+			beacon.x = e.relX;
+			beacon.y = e.relY;
+			beacon.z = e.relZ;
+		};
+		i.onMove = function(e:hxd.Event) {
+			if( beacon == null ) return;
+			beacon.x = e.relX;
+			beacon.y = e.relY;
+			beacon.z = e.relZ;
+		};
+		i.onOut = function(e : hxd.Event) {
+			m.material.color.load(color);
+			beacon.remove();
+			beacon = null;
+		};
+	}
+
+	override function init() {
+		light = new h3d.scene.DirLight(new h3d.Vector( 0.3, -0.4, -0.9), s3d);
+		light.enableSpecular = true;
+		light.color.set(0.28, 0.28, 0.28);
+		s3d.lightSystem.ambientLight.set(0.74, 0.74, 0.74);
+
+		rnd = new hxd.Rand(5);
+		for(i in 0...6) {
+			var c = new h3d.prim.Sphere(1,64,32);
+			//c.unindex();
+			c.addNormals();
+			c.addUVs();
+			var m = new h3d.scene.Mesh(c, s3d);
+			m.x = rnd.srand() * 0.8;
+			m.y = rnd.srand() * 0.8;
+			m.scale(0.25 + rnd.rand() * 0.5);
+			m.material.mainPass.enableLights = true;
+			m.material.shadows = true;
+			var c = 0.3 + rnd.rand() * 0.3;
+			var color = new h3d.Vector(c, c * 0.6, c * 0.6);
+			m.material.color.load(color);
+
+			var interact = new h3d.scene.Interactive(m.primitive.getCollider(), m);
+			initInteract(interact, m);
+		}
+
+		var cache = new h3d.prim.ModelCache();
+		var obj = cache.loadModel(hxd.Res.Model);
+		obj.scale(1 / 20);
+		obj.rotate(0,0,Math.PI / 2);
+		obj.y = 0.2;
+		obj.z = -0.2;
+
+		// disable skinning (not supported for picking)
+		var pass = obj.getChildAt(1).toMesh().material.mainPass;
+		pass.removeShader(pass.getShader(h3d.shader.Skin));
+		s3d.addChild(obj);
+
+		for( o in obj ) {
+			var m = o.toMesh();
+			var i = new h3d.scene.Interactive(m.primitive.getCollider(), o);
+			initInteract(i, m);
+		}
+
+		var b = new h2d.Interactive(150, 100, s2d);
+		b.backgroundColor = 0x80204060;
+		b.x = 300;
+		b.y = 150;
+		b.rotation = Math.PI / 3;
+		//b.scaleX = 1.5; // TODO
+
+		var pix = null;
+		b.onOver = function(e) {
+			var t = h2d.Tile.fromColor(0xFF0000, 3, 3);
+			t.dx = -1;
+			t.dy = -1;
+			pix = new h2d.Bitmap(t, b);
+			pix.x = e.relX;
+			pix.y = e.relY;
+		};
+		b.onMove = function(e) {
+			if( pix == null ) return;
+			pix.x = e.relX;
+			pix.y = e.relY;
+		}
+		b.onOut = function(e) {
+			pix.remove();
+			pix = null;
+		};
+	}
+
+
+	static function main() {
+		hxd.Res.initEmbed();
+		new Main();
+	}
+}

+ 7 - 0
samples/interact/interact.hxml

@@ -0,0 +1,7 @@
+-swf interact.swf
+-swf-header 800:600:60:FFFFFF
+-swf-version 11.8
+-main Main
+-lib heaps
+-dce full
+-D resourcesPath=../skin/res

+ 57 - 0
samples/interact/interact.hxproj

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<project version="2">
+  <!-- Output SWF options -->
+  <output>
+    <movie outputType="Application" />
+    <movie input="" />
+    <movie path="interact.swf" />
+    <movie fps="60" />
+    <movie width="800" />
+    <movie height="600" />
+    <movie version="11" />
+    <movie minorVersion="8" />
+    <movie platform="Flash Player" />
+    <movie background="#FFFFFF" />
+  </output>
+  <!-- Other classes to be compiled into your SWF -->
+  <classpaths>
+    <!-- example: <class path="..." /> -->
+  </classpaths>
+  <!-- Build options -->
+  <build>
+    <option directives="" />
+    <option flashStrict="False" />
+    <option noInlineOnDebug="False" />
+    <option mainClass="Main" />
+    <option enabledebug="False" />
+    <option additional="-lib heaps&#xA;-dce full&#xA;-D resourcesPath=../skin/res" />
+  </build>
+  <!-- haxelib libraries -->
+  <haxelib>
+    <!-- example: <library name="..." /> -->
+  </haxelib>
+  <!-- Class files to compile (other referenced classes will automatically be included) -->
+  <compileTargets>
+    <!-- example: <compile path="..." /> -->
+  </compileTargets>
+  <!-- Assets to embed into the output SWF -->
+  <library>
+    <!-- example: <asset path="..." id="..." update="..." glyphs="..." mode="..." place="..." sharepoint="..." /> -->
+  </library>
+  <!-- Paths to exclude from the Project Explorer tree -->
+  <hiddenPaths>
+    <hidden path="obj" />
+  </hiddenPaths>
+  <!-- Executed before build -->
+  <preBuildCommand />
+  <!-- Executed after build -->
+  <postBuildCommand alwaysRun="False" />
+  <!-- Other project options -->
+  <options>
+    <option showHiddenPaths="False" />
+    <option testMovie="OpenDocument" />
+    <option testMovieCommand="interact.swf" />
+  </options>
+  <!-- Plugin storage -->
+  <storage />
+</project>