Browse Source

added Object.getCollider, use PolygonBuffer for HMD models, added SkinTransform collider

ncannasse 8 năm trước cách đây
mục cha
commit
1ef634252f

+ 36 - 0
h3d/col/FPoint.hx

@@ -0,0 +1,36 @@
+package h3d.col;
+using hxd.Math;
+
+class FPoint {
+
+	public var x : hxd.impl.Float32;
+	public var y : hxd.impl.Float32;
+	public var z : hxd.impl.Float32;
+
+	public inline function new(x=0.,y=0.,z=0.) {
+		this.x = x;
+		this.y = y;
+		this.z = z;
+	}
+
+	public inline function sub( p : FPoint ) {
+		return new FPoint(x - p.x, y - p.y, z - p.z);
+	}
+
+	public inline function add( p : FPoint ) {
+		return new FPoint(x + p.x, y + p.y, z + p.z);
+	}
+
+	public inline function cross( p : FPoint ) {
+		return new FPoint(y * p.z - z * p.y, z * p.x - x * p.z,  x * p.y - y * p.x);
+	}
+
+	public inline function dot( p : FPoint ) {
+		return x * p.x + y * p.y + z * p.z;
+	}
+
+	public function toString() {
+		return 'FPoint{${x.fmt()},${y.fmt()},${z.fmt()}}';
+	}
+
+}

+ 69 - 0
h3d/col/PolygonBuffer.hx

@@ -0,0 +1,69 @@
+package h3d.col;
+
+class PolygonBuffer implements Collider {
+
+	var buffer : haxe.ds.Vector<hxd.impl.Float32>;
+	var indexes : haxe.ds.Vector<hxd.impl.UInt16>;
+	var startIndex : Int;
+	var triCount : Int;
+
+	public function new( buffer, indexes, startIndex = 0, triCount = -1 ) {
+		this.buffer = buffer;
+		this.indexes = indexes;
+		this.startIndex = startIndex;
+		this.triCount = triCount >= 0 ? triCount : Std.int((indexes.length - startIndex) / 3);
+	}
+
+	public function contains( p : Point ) {
+		throw "Not implemented";
+		return false;
+	}
+
+	public function inFrustum( m : h3d.Matrix ) {
+		throw "Not implemented";
+		return false;
+	}
+
+	// Möller–Trumbore intersection
+	public function rayIntersection( r : Ray, bestMatch : Bool ) : Float {
+		var i = startIndex;
+		var rdir = new FPoint(r.lx, r.ly, r.lz);
+		var r0 = new FPoint(r.px, r.py, r.pz);
+		var best = -1.;
+		for( t in 0...triCount ) {
+			var i0 = indexes[i++] * 3;
+			var p0 = new FPoint(buffer[i0++], buffer[i0++], buffer[i0]);
+			var i1 = indexes[i++] * 3;
+			var p1 = new FPoint(buffer[i1++], buffer[i1++], buffer[i1]);
+			var i2 = indexes[i++] * 3;
+			var p2 = new FPoint(buffer[i2++], buffer[i2++], buffer[i2]);
+
+			var e1 = p1.sub(p0);
+			var e2 = p2.sub(p0);
+			var p = rdir.cross(e2);
+			var det = e1.dot(p);
+			if( det < hxd.Math.EPSILON ) continue; // backface culling (negative) and near parallel (epsilon)
+
+			var invDet = 1 / det;
+			var T = r0.sub(p0);
+			var u = T.dot(p) * invDet;
+
+			if( u < 0 || u > 1 ) continue;
+
+			var q = T.cross(e1);
+			var v = rdir.dot(q) * invDet;
+
+			if( v < 0 || u + v > 1 ) continue;
+
+			var t = e2.dot(q) * invDet;
+
+			if( t < hxd.Math.EPSILON ) continue;
+
+			if( !bestMatch ) return t;
+			if( best < 0 || t < best ) best = t;
+		}
+		return best;
+	}
+
+}
+

+ 67 - 0
h3d/col/SkinTransform.hx

@@ -0,0 +1,67 @@
+package h3d.col;
+
+@:access(h3d.col.PolygonBuffer)
+@:access(h3d.scene.Skin)
+class SkinTransform implements Collider {
+
+	var obj : h3d.scene.Skin;
+	var col : PolygonBuffer;
+	var transform : PolygonBuffer;
+	var lastFrame : Int;
+
+	public function new( obj, col ) {
+		this.obj = obj;
+		this.col = col;
+		this.transform = new PolygonBuffer(col.buffer.copy(), col.indexes, col.startIndex, col.triCount);
+	}
+
+	public function contains(p) {
+		applyTransform();
+		return transform.contains(p);
+	}
+
+	public function inFrustum(p) {
+		applyTransform();
+		return transform.inFrustum(p);
+	}
+
+	public function rayIntersection(r, bestMatch) {
+		applyTransform();
+		return transform.rayIntersection(r, bestMatch);
+	}
+
+	function applyTransform() {
+		var invMat = obj.getInvPos();
+		if( !obj.jointsUpdated && lastFrame == obj.lastFrame ) return;
+		lastFrame = obj.lastFrame;
+		obj.syncJoints();
+		var j = 0, v = 0;
+		var nbones = obj.skinData.bonesPerVertex;
+		for( i in 0...obj.skinData.vertexCount ) {
+			var px = 0., py = 0., pz = 0.;
+			var p = new Point(col.buffer[v], col.buffer[v+1], col.buffer[v+2]);
+
+			for( k in 0...nbones ) {
+				var w = obj.skinData.vertexWeights[j];
+				if( w == 0 ) {
+					j++;
+					continue;
+				}
+				var bid = obj.skinData.vertexJoints[j++];
+				var p2 = p.clone();
+				p2.transform(obj.currentPalette[bid]);
+				px += p2.x * w;
+				py += p2.y * w;
+				pz += p2.z * w;
+			}
+
+			var p = new h3d.col.Point(px, py, pz);
+			p.transform(invMat);
+			transform.buffer[v++] = p.x;
+			transform.buffer[v++] = p.y;
+			transform.buffer[v++] = p.z;
+		}
+
+	}
+
+}

+ 5 - 3
h3d/prim/HMDModel.hx

@@ -38,6 +38,10 @@ class HMDModel extends MeshPrimitive {
 		return lib.getBuffers(data, fmt, defaults, material);
 	}
 
+	public function loadSkin(skin) {
+		lib.loadSkin(data, skin);
+	}
+
 	public function addAlias( name : String, realName : String, offset = 0 ) {
 		bufferAliases.set(name, {realName : realName, offset : offset });
 	}
@@ -148,10 +152,8 @@ class HMDModel extends MeshPrimitive {
 	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 poly = new h3d.col.PolygonBuffer(pos.vertexes, pos.indexes);
 		var sphere = data.bounds.toSphere();
 		collider = new h3d.col.Collider.OptimizedCollider(sphere, poly);
 		return collider;

+ 4 - 0
h3d/scene/Mesh.hx

@@ -30,6 +30,10 @@ class Mesh extends Object {
 		return m;
 	}
 
+	override function getCollider() {
+		return primitive.getCollider();
+	}
+
 	override function draw( ctx : RenderContext ) {
 		primitive.render(ctx.engine);
 	}

+ 17 - 2
h3d/scene/Object.hx

@@ -101,7 +101,7 @@ class Object {
 	inline function get_lightCameraCenter() return flags.has(FLightCameraCenter);
 	inline function get_alwaysSync() return flags.has(FAlwaysSync);
 	inline function get_inheritCulled() return flags.has(FInheritCulled);
-	inline function set_posChanged(b) return flags.set(FPosChanged, b);
+	inline function set_posChanged(b) return flags.set(FPosChanged, b || follow != null);
 	inline function set_culled(b) return flags.set(FCulled, b);
 	inline function set_visible(b) return flags.set(FVisible,b);
 	inline function set_allocated(b) return flags.set(FAllocated, b);
@@ -362,6 +362,21 @@ class Object {
 		throw this + " is not a Mesh";
 	}
 
+	public function getCollider() : h3d.col.Collider {
+		var colliders = [];
+		for( obj in childs ) {
+			var c = obj.getCollider();
+			var cgrp = Std.instance(c, h3d.col.Collider.GroupCollider);
+			if( cgrp != null ) {
+				for( c in cgrp.colliders )
+					colliders.push(c);
+			} else
+				colliders.push(c);
+		}
+		// TODO : handle child position/transform
+		return new h3d.col.Collider.GroupCollider(colliders);
+	}
+
 	/**
 		Same as parent.removeChild(this), but does nothing if parent is null.
 		In order to capture add/removal from scene, you can override onAdd/onRemove/onParentChanged
@@ -432,7 +447,7 @@ class Object {
 		var changed = posChanged;
 		if( changed ) calcAbsPos();
 		sync(ctx);
-		posChanged = follow != null;
+		posChanged = false;
 		lastFrame = ctx.frame;
 		var p = 0, len = childs.length;
 		while( p < len ) {

+ 27 - 18
h3d/scene/Skin.hx

@@ -110,6 +110,12 @@ class Skin extends MultiMaterial {
 		return null;
 	}
 
+	override function getCollider() {
+		var col = cast(super.getCollider(), h3d.col.Collider.OptimizedCollider);
+		cast(primitive, h3d.prim.HMDModel).loadSkin(skinData);
+		return new h3d.col.SkinTransform(this, cast(col.b, h3d.col.PolygonBuffer));
+	}
+
 	override function calcAbsPos() {
 		super.calcAbsPos();
 		// if we update our absolute position, rebuild the matrixes
@@ -155,28 +161,31 @@ class Skin extends MultiMaterial {
 			splitPalette = null;
 	}
 
-	@:noDebug
 	override function sync( ctx : RenderContext ) {
 		if( !ctx.visibleFlag )
 			return;
-		if( jointsUpdated || posChanged ) {
-			for( j in skinData.allJoints ) {
-				var id = j.index;
-				var m = currentAbsPose[id];
-				var r = currentRelPose[id];
-				var bid = j.bindIndex;
-				if( r == null ) r = j.defMat else if( j.retargetAnim ) { r._41 = j.defMat._41; r._42 = j.defMat._42; r._43 = j.defMat._43; }
-				if( j.parent == null )
-					m.multiply3x4inline(r, absPos);
-				else
-					m.multiply3x4inline(r, currentAbsPose[j.parent.index]);
-				if( bid >= 0 )
-					currentPalette[bid].multiply3x4inline(j.transPos, m);
-			}
-			skinShader.bonesMatrixes = currentPalette;
-			if( jointsAbsPosInv != null ) jointsAbsPosInv._44 = 0; // mark as invalid
-			jointsUpdated = false;
+		syncJoints();
+	}
+
+	@:noDebug
+	function syncJoints() {
+		if( !jointsUpdated ) return;
+		for( j in skinData.allJoints ) {
+			var id = j.index;
+			var m = currentAbsPose[id];
+			var r = currentRelPose[id];
+			var bid = j.bindIndex;
+			if( r == null ) r = j.defMat else if( j.retargetAnim ) { r._41 = j.defMat._41; r._42 = j.defMat._42; r._43 = j.defMat._43; }
+			if( j.parent == null )
+				m.multiply3x4inline(r, absPos);
+			else
+				m.multiply3x4inline(r, currentAbsPose[j.parent.index]);
+			if( bid >= 0 )
+				currentPalette[bid].multiply3x4inline(j.transPos, m);
 		}
+		skinShader.bonesMatrixes = currentPalette;
+		if( jointsAbsPosInv != null ) jointsAbsPosInv._44 = 0; // mark as invalid
+		jointsUpdated = false;
 	}
 
 	override function emit( ctx : RenderContext ) {

+ 28 - 0
hxd/fmt/hmd/Library.hx

@@ -28,6 +28,7 @@ class Library {
 	var cachedPrimitives : Array<h3d.prim.Primitive>;
 	var cachedAnimations : Map<String, h3d.anim.Animation>;
 	var cachedSkin : Map<String, h3d.anim.Skin>;
+	var tmp = haxe.io.Bytes.alloc(4);
 
 	public function new(entry, header) {
 		this.entry = entry;
@@ -488,4 +489,31 @@ class Library {
 		return l;
 	}
 
+	@:allow(h3d.anim.Skin)
+	public function loadSkin( geom : Geometry, skin : h3d.anim.Skin ) {
+		if( skin.vertexWeights != null )
+			return;
+		@:privateAccess skin.vertexCount = geom.vertexCount;
+		var w = getBuffers(geom, [new hxd.fmt.hmd.Data.GeometryFormat("weights", DVec3)]).vertexes;
+		skin.vertexWeights = new haxe.ds.Vector(skin.vertexCount * skin.bonesPerVertex);
+		skin.vertexJoints = new haxe.ds.Vector(skin.vertexCount * skin.bonesPerVertex);
+		for( i in 0...skin.vertexWeights.length )
+			skin.vertexWeights[i] = w[i];
+		var vidx = getBuffers(geom, [new hxd.fmt.hmd.Data.GeometryFormat("indexes", DBytes4)]).vertexes;
+		var j = 0;
+		for( i in 0...skin.vertexCount ) {
+			var v = ftoint32(vidx[i]);
+			skin.vertexJoints[j++] = v & 0xFF;
+			skin.vertexJoints[j++] = (v >> 8) & 0xFF;
+			skin.vertexJoints[j++] = (v >> 16) & 0xFF;
+		}
+	}
+
+	function ftoint32( v : hxd.impl.Float32 ) : Int {
+		tmp.setFloat(0, v);
+		return tmp.getInt32(0);
+	}
+
+
+
 }

+ 13 - 14
samples/Interactive.hx

@@ -7,9 +7,10 @@ class Interactive extends hxd.App {
 	var obj : h3d.scene.Object;
 	var b : h2d.Interactive;
 
-	function initInteract( i : h3d.scene.Interactive, m : h3d.scene.Mesh ) {
+	function initInteract( i : h3d.scene.Interactive, m : h3d.scene.Mesh, scale = 1. ) {
 		var beacon = null;
 		var color = m.material.color.clone();
+		i.bestMatch = true;
 		i.onOver = function(e : hxd.Event) {
 			m.material.color.set(0, 1, 0);
 			var s = new h3d.prim.Sphere(1, 32, 32);
@@ -17,12 +18,12 @@ class Interactive extends hxd.App {
 			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.scale(0.05 * scale / m.parent.scaleX);
 			beacon.x = e.relX;
 			beacon.y = e.relY;
 			beacon.z = e.relZ;
 		};
-		i.onMove = function(e:hxd.Event) {
+		i.onMove = i.onCheck = function(e:hxd.Event) {
 			if( beacon == null ) return;
 			beacon.x = e.relX;
 			beacon.y = e.relY;
@@ -57,7 +58,7 @@ class Interactive extends hxd.App {
 			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);
+			var interact = new h3d.scene.Interactive(m.getCollider(), m);
 			initInteract(interact, m);
 		}
 
@@ -66,17 +67,15 @@ class Interactive extends hxd.App {
 		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));
+		obj.z = 0.2;
 		s3d.addChild(obj);
 
+		obj.playAnimation(cache.loadAnimation(hxd.Res.Model)).speed = 0.1;
+
 		for( o in obj ) {
 			var m = o.toMesh();
-			var i = new h3d.scene.Interactive(m.primitive.getCollider(), o);
-			initInteract(i, m);
+			var i = new h3d.scene.Interactive(m.getCollider(), m);
+			initInteract(i, m, 0.2);
 		}
 
 		b = new h2d.Interactive(150, 100, s2d);
@@ -102,17 +101,17 @@ class Interactive extends hxd.App {
 			pix.remove();
 			pix = null;
 		};
-		
+
 		onResize();
 	}
-	
+
 	override function onResize() {
 		b.x = (s2d.width >> 1) - 200;
 		b.y = 150;
 	}
 
 	override function update(dt:Float) {
-		obj.rotate(0, 0, 0.01 * dt);
+		obj.rotate(0, 0, 0.002 * dt);
 	}