Преглед изворни кода

Add `showDebug`property to 3D Interactives (#972)

This displays an object that shows the collider (wireframe by default)
Also updated samples/Interactive to use this feature
Leonardo Jeanteur пре 4 година
родитељ
комит
2692cedd22

+ 9 - 1
h3d/col/Bounds.hx

@@ -401,7 +401,14 @@ class Bounds implements Collider {
 		return b;
 		return b;
 	}
 	}
 
 
-	#if (hxbit && !macro)
+	#if !macro
+	public function makeDebugObj() : h3d.scene.Object {
+		var prim = new h3d.prim.Cube(xMax - xMin, yMax - yMin, zMax - zMin);
+		prim.translate(xMin, yMin, zMin);
+		prim.addNormals();
+		return new h3d.scene.Mesh(prim);
+	}
+	#if hxbit
 	function customSerialize( ctx : hxbit.Serializer ) {
 	function customSerialize( ctx : hxbit.Serializer ) {
 		ctx.addFloat(xMin);
 		ctx.addFloat(xMin);
 		ctx.addFloat(xMax);
 		ctx.addFloat(xMax);
@@ -419,5 +426,6 @@ class Bounds implements Collider {
 		zMax = ctx.getFloat();
 		zMax = ctx.getFloat();
 	}
 	}
 	#end
 	#end
+	#end
 
 
 }
 }

+ 34 - 1
h3d/col/Capsule.hx

@@ -105,7 +105,39 @@ class Capsule implements Collider {
 		return "Capsule{" + a + "," + b + "," + hxd.Math.fmt(r) + "}";
 		return "Capsule{" + a + "," + b + "," + hxd.Math.fmt(r) + "}";
 	}
 	}
 
 
-	#if (hxbit && !macro)
+	#if !macro
+	public function makeDebugObj() : h3d.scene.Object {
+		var obj = new h3d.scene.Object();
+
+		var segW = 12;
+		var segH = 6;
+
+		var dir = a.sub(b);
+		var full = a.add(b);
+		var dist = a.distance(b);
+		var midPoint = new Point(full.x / 2, full.y / 2, full.z / 2);
+
+		var prim = new h3d.prim.Sphere(r, segW, segH, 0.5);
+		prim.translate(0, 0, dist / 2);
+		prim.addNormals();
+		var spherea = new h3d.scene.Mesh(prim);
+		var sphereb = spherea.clone();
+		spherea.rotate(0, Math.PI / 2, 0);
+		obj.addChild(spherea);
+		sphereb.rotate(0, -1 * Math.PI / 2, 0);
+		obj.addChild(sphereb);
+
+		var cyl = new h3d.prim.Cylinder(segW, r, dist, true);
+		cyl.addNormals();
+		var cylMesh = new h3d.scene.Mesh(cyl);
+		cylMesh.rotate(0, Math.PI / 2, 0);
+		obj.addChild(cylMesh);
+
+		obj.setDirection(dir.toVector());
+		obj.setPosition(midPoint.x, midPoint.y, midPoint.z);
+		return obj;
+	}
+	#if hxbit
 	function customSerialize( ctx : hxbit.Serializer ) {
 	function customSerialize( ctx : hxbit.Serializer ) {
 		ctx.addFloat(a.x);
 		ctx.addFloat(a.x);
 		ctx.addFloat(a.y);
 		ctx.addFloat(a.y);
@@ -121,5 +153,6 @@ class Capsule implements Collider {
 		r = ctx.getFloat();
 		r = ctx.getFloat();
 	}
 	}
 	#end
 	#end
+	#end
 
 
 }
 }

+ 34 - 2
h3d/col/Collider.hx

@@ -10,6 +10,10 @@ interface Collider extends hxd.impl.Serializable.StructSerializable {
 	public function contains( p : Point ) : Bool;
 	public function contains( p : Point ) : Bool;
 	public function inFrustum( f : Frustum, ?localMatrix : h3d.Matrix ) : Bool;
 	public function inFrustum( f : Frustum, ?localMatrix : h3d.Matrix ) : Bool;
 	public function inSphere( s : Sphere ) : Bool;
 	public function inSphere( s : Sphere ) : Bool;
+
+	#if !macro
+	public function makeDebugObj() : h3d.scene.Object;
+	#end
 }
 }
 
 
 
 
@@ -41,12 +45,26 @@ class OptimizedCollider implements hxd.impl.Serializable implements Collider {
 		return a.inSphere(s) && b.inSphere(s);
 		return a.inSphere(s) && b.inSphere(s);
 	}
 	}
 
 
-	#if (hxbit && !macro)
+	#if !macro
+	public function makeDebugObj() : h3d.scene.Object {
+		var bobj = b.makeDebugObj();
+		var aobj = a.makeDebugObj();
+		if( aobj == null && bobj == null )
+			return null;
+		var ret = new h3d.scene.Object();
+		if( aobj != null )
+			ret.addChild(aobj);
+		if( bobj != null )
+			ret.addChild(bobj);
+		return ret;
+	}
+	#if hxbit
 	function customSerialize(ctx:hxbit.Serializer) {
 	function customSerialize(ctx:hxbit.Serializer) {
 	}
 	}
 	function customUnserialize(ctx:hxbit.Serializer) {
 	function customUnserialize(ctx:hxbit.Serializer) {
 	}
 	}
 	#end
 	#end
+	#end
 
 
 }
 }
 
 
@@ -91,7 +109,20 @@ class GroupCollider implements Collider {
 		return false;
 		return false;
 	}
 	}
 
 
-	#if (hxbit && !macro && heaps_enable_serialize)
+	#if !macro
+	public function makeDebugObj() : h3d.scene.Object {
+		var ret : h3d.scene.Object = null;
+		for( c in colliders ) {
+			var toAdd = c.makeDebugObj();
+			if( toAdd == null )
+				continue;
+			if( ret == null )
+				ret = new h3d.scene.Object();
+			ret.addChild(toAdd);
+		}
+		return ret;
+	}
+	#if (hxbit && heaps_enable_serialize)
 
 
 	function customSerialize(ctx:hxbit.Serializer) {
 	function customSerialize(ctx:hxbit.Serializer) {
 		ctx.addInt(colliders.length);
 		ctx.addInt(colliders.length);
@@ -104,6 +135,7 @@ class GroupCollider implements Collider {
 	}
 	}
 
 
 	#end
 	#end
+	#end
 
 
 
 
 }
 }

+ 5 - 0
h3d/col/HeightMap.hx

@@ -85,4 +85,9 @@ class HeightMap implements Collider {
 		}
 		}
 	}
 	}
 
 
+	#if !macro
+	public function makeDebugObj() : h3d.scene.Object {
+		return null;
+	}
+	#end
 }
 }

+ 11 - 0
h3d/col/ObjectCollider.hx

@@ -64,6 +64,17 @@ class ObjectCollider implements Collider implements hxd.impl.Serializable {
 		return res;
 		return res;
 	}
 	}
 
 
+	#if !macro
+	public function makeDebugObj() : h3d.scene.Object {
+		var ret = collider.makeDebugObj();
+		if( ret != null ) {
+			ret.ignoreParentTransform = true;
+			ret.follow = obj;
+		}
+		return ret;
+	}
+	#end
+
 	#if hxbit
 	#if hxbit
 	function customSerialize( ctx : hxbit.Serializer ) {
 	function customSerialize( ctx : hxbit.Serializer ) {
 	}
 	}

+ 33 - 3
h3d/col/Polygon.hx

@@ -144,13 +144,24 @@ class TriPlane implements Collider {
 		return [new Point(p0x, p0y, p0z), new Point(d1x + p0x, d1y + p0y, d1z + p0z), new Point(d2x + p0x, d2y + p0y, d2z + p0z)];
 		return [new Point(p0x, p0y, p0z), new Point(d1x + p0x, d1y + p0y, d1z + p0z), new Point(d2x + p0x, d2y + p0y, d2z + p0z)];
 	}
 	}
 
 
-	#if (hxbit && !macro)
+	#if !macro
+	public function makeDebugObj() : h3d.scene.Object {
+		var p0 = new Point(p0x, p0y, p0z);
+		var d1 = new Point(d1x, d1y, d1z);
+		var d2 = new Point(d2x, d2y, d2z);
+		var points : Array<Point> = [ p0, d1.add(p0), d2.add(p0) ];
+		var prim = new h3d.prim.Polygon(points);
+		prim.addNormals();
+		return new h3d.scene.Mesh(prim);
+	}
+	#if hxbit
 	function customSerialize( ctx : hxbit.Serializer ) {
 	function customSerialize( ctx : hxbit.Serializer ) {
 		throw "Cannot serialize "+this;
 		throw "Cannot serialize "+this;
 	}
 	}
 	function customUnserialize( ctx : hxbit.Serializer ) {
 	function customUnserialize( ctx : hxbit.Serializer ) {
 	}
 	}
 	#end
 	#end
+	#end
 
 
 }
 }
 
 
@@ -190,7 +201,6 @@ class Polygon implements Collider {
 
 
 	public function clone() : h3d.col.Polygon {
 	public function clone() : h3d.col.Polygon {
 		var clone = new h3d.col.Polygon();
 		var clone = new h3d.col.Polygon();
-		clone.triPlanes = new TriPlane();
 		clone.triPlanes = triPlanes.clone();
 		clone.triPlanes = triPlanes.clone();
 		return clone;
 		return clone;
 	}
 	}
@@ -262,13 +272,33 @@ class Polygon implements Collider {
 		return false;
 		return false;
 	}
 	}
 
 
-	#if (hxbit && !macro)
+	#if !macro
+	public function makeDebugObj() : h3d.scene.Object {
+		var points : Array<Point> = [];
+		var idx = new hxd.IndexBuffer();
+
+		var t = triPlanes;
+		while( t != null ) {
+			var p0 = new Point(t.p0x, t.p0y, t.p0z);
+			var d1 = new Point(t.d1x, t.d1y, t.d1z);
+			var d2 = new Point(t.d2x, t.d2y, t.d2z);
+			points.push(p0);
+			points.push(d1.add(p0));
+			points.push(d2.add(p0));
+			t = t.next;
+		}
+		var prim = new h3d.prim.Polygon(points);
+		prim.addNormals();
+		return new h3d.scene.Mesh(prim);
+	}
+	#if hxbit
 	function customSerialize( ctx : hxbit.Serializer ) {
 	function customSerialize( ctx : hxbit.Serializer ) {
 		throw "Cannot serialize "+this;
 		throw "Cannot serialize "+this;
 	}
 	}
 	function customUnserialize( ctx : hxbit.Serializer ) {
 	function customUnserialize( ctx : hxbit.Serializer ) {
 	}
 	}
 	#end
 	#end
+	#end
 
 
 	public static function fromPolygon2D( p : h2d.col.Polygon, z = 0. ) {
 	public static function fromPolygon2D( p : h2d.col.Polygon, z = 0. ) {
 		var pout = new Polygon();
 		var pout = new Polygon();

+ 20 - 1
h3d/col/PolygonBuffer.hx

@@ -92,7 +92,25 @@ class PolygonBuffer implements Collider {
 		return best;
 		return best;
 	}
 	}
 
 
-	#if (hxbit && !macro)
+	#if !macro
+	public function makeDebugObj() : h3d.scene.Object {
+		var points = new Array<Point>();
+		var idx = new hxd.IndexBuffer();
+		var i = startIndex;
+		for( t in 0...triCount ) {
+			idx.push(indexes[i++]);
+			idx.push(indexes[i++]);
+			idx.push(indexes[i++]);
+		}
+		i = 0;
+		while( i < buffer.length ) {
+			points.push(new Point(buffer[i++], buffer[i++], buffer[i++]));
+		}
+		var prim = new h3d.prim.Polygon(points, idx);
+		prim.addNormals();
+		return new h3d.scene.Mesh(prim);
+	}
+	#if hxbit
 	function customSerialize( ctx : hxbit.Serializer ) {
 	function customSerialize( ctx : hxbit.Serializer ) {
 		if( source == null )
 		if( source == null )
 			throw "Cannot serialize " + this;
 			throw "Cannot serialize " + this;
@@ -116,5 +134,6 @@ class PolygonBuffer implements Collider {
 		@:privateAccess prim.initCollider(this);
 		@:privateAccess prim.initCollider(this);
 	}
 	}
 	#end
 	#end
+	#end
 
 
 }
 }

+ 6 - 1
h3d/col/SkinCollider.hx

@@ -91,7 +91,11 @@ class SkinCollider implements hxd.impl.Serializable implements Collider {
 		}
 		}
 	}
 	}
 
 
-	#if (hxbit && !macro)
+	#if !macro
+	public function makeDebugObj() : h3d.scene.Object {
+		return null;
+	}
+	#if hxbit
 	function customSerialize( ctx : hxbit.Serializer ) {
 	function customSerialize( ctx : hxbit.Serializer ) {
 	}
 	}
 	function customUnserialize( ctx : hxbit.Serializer ) {
 	function customUnserialize( ctx : hxbit.Serializer ) {
@@ -99,5 +103,6 @@ class SkinCollider implements hxd.impl.Serializable implements Collider {
 		this.transform.setData(col.buffer.copy(), col.indexes, col.startIndex, col.triCount);
 		this.transform.setData(col.buffer.copy(), col.indexes, col.startIndex, col.triCount);
 	}
 	}
 	#end
 	#end
+	#end
 
 
 }
 }

+ 9 - 1
h3d/col/Sphere.hx

@@ -86,7 +86,14 @@ class Sphere implements Collider {
 		return "Sphere{" + getCenter()+","+ hxd.Math.fmt(r) + "}";
 		return "Sphere{" + getCenter()+","+ hxd.Math.fmt(r) + "}";
 	}
 	}
 
 
-	#if (hxbit && !macro)
+	#if !macro
+	public function makeDebugObj() : h3d.scene.Object {
+		var prim = new h3d.prim.Sphere(r, 20, 15);
+		prim.translate(x, y, z);
+		prim.addNormals();
+		return new h3d.scene.Mesh(prim);
+	}
+	#if hxbit
 	function customSerialize( ctx : hxbit.Serializer ) {
 	function customSerialize( ctx : hxbit.Serializer ) {
 		ctx.addFloat(x);
 		ctx.addFloat(x);
 		ctx.addFloat(y);
 		ctx.addFloat(y);
@@ -100,5 +107,6 @@ class Sphere implements Collider {
 		r = ctx.getFloat();
 		r = ctx.getFloat();
 	}
 	}
 	#end
 	#end
+	#end
 
 
 }
 }

+ 9 - 0
h3d/col/TransformCollider.hx

@@ -72,6 +72,15 @@ class TransformCollider implements Collider {
 		return res;
 		return res;
 	}
 	}
 
 
+	#if !macro
+	public function makeDebugObj() : h3d.scene.Object {
+		var obj = collider.makeDebugObj();
+		obj.ignoreParentTransform = true;
+		obj.defaultTransform = mat;
+		return obj;
+	}
+	#end
+
 	public static function make( mat : h3d.Matrix, col ) {
 	public static function make( mat : h3d.Matrix, col ) {
 		if( mat.isIdentityEpsilon(1e-10) )
 		if( mat.isIdentityEpsilon(1e-10) )
 			return col;
 			return col;

+ 9 - 6
h3d/prim/Sphere.hx

@@ -7,28 +7,30 @@ class Sphere extends Polygon {
 	@:s var segsH : Int;
 	@:s var segsH : Int;
 	@:s var segsW : Int;
 	@:s var segsW : Int;
 
 
-	public function new( ray = 1., segsW = 8, segsH = 6 ) {
+	// Use 1 for a full sphere, 0.5 for a half sphere
+	@:s var portion : Float;
+
+	public function new( ray = 1., segsW = 8, segsH = 6, portion = 1. ) {
 		this.ray = ray;
 		this.ray = ray;
 		this.segsH = segsH;
 		this.segsH = segsH;
 		this.segsW = segsW;
 		this.segsW = segsW;
+		this.portion = portion;
 
 
 		var dp = Math.PI * 2 / segsW;
 		var dp = Math.PI * 2 / segsW;
 		var pts = [], idx = new hxd.IndexBuffer();
 		var pts = [], idx = new hxd.IndexBuffer();
-		var dx = 1, dy = segsW + 1;
 		for( y in 0...segsH+1 ) {
 		for( y in 0...segsH+1 ) {
-			var t = (y / segsH) * Math.PI;
+			var t = (y / segsH) * Math.PI * portion;
 			var st = Math.sin(t);
 			var st = Math.sin(t);
 			var pz = Math.cos(t);
 			var pz = Math.cos(t);
 			var p = 0.;
 			var p = 0.;
 			for( x in 0...segsW+1 ) {
 			for( x in 0...segsW+1 ) {
 				var px = st * Math.cos(p);
 				var px = st * Math.cos(p);
 				var py = st * Math.sin(p);
 				var py = st * Math.sin(p);
-				var i = pts.length;
 				pts.push(new Point(px * ray, py * ray, pz * ray));
 				pts.push(new Point(px * ray, py * ray, pz * ray));
 				p += dp;
 				p += dp;
 			}
 			}
 		}
 		}
-		for( y in 0...segsH )
+		for( y in 0...segsH ) {
 			for( x in 0...segsW ) {
 			for( x in 0...segsW ) {
 				inline function vertice(x, y) return x + y * (segsW + 1);
 				inline function vertice(x, y) return x + y * (segsW + 1);
 				var v1 = vertice(x + 1, y);
 				var v1 = vertice(x + 1, y);
@@ -40,12 +42,13 @@ class Sphere extends Polygon {
 					idx.push(v2);
 					idx.push(v2);
 					idx.push(v4);
 					idx.push(v4);
 				}
 				}
-				if( y != segsH - 1 ) {
+				if( y != segsH - 1 || portion != 1. ) {
 					idx.push(v2);
 					idx.push(v2);
 					idx.push(v3);
 					idx.push(v3);
 					idx.push(v4);
 					idx.push(v4);
 				}
 				}
 			}
 			}
+		}
 
 
 		super(pts, idx);
 		super(pts, idx);
 	}
 	}

+ 35 - 0
h3d/scene/Interactive.hx

@@ -2,6 +2,9 @@ package h3d.scene;
 
 
 class Interactive extends Object implements hxd.SceneEvents.Interactive {
 class Interactive extends Object implements hxd.SceneEvents.Interactive {
 
 
+	var debugObj : Object;
+	public var showDebug(default, set) : Bool = false;
+
 	@:s public var shape : h3d.col.Collider;
 	@:s public var shape : h3d.col.Collider;
 
 
 	/**
 	/**
@@ -42,6 +45,38 @@ class Interactive extends Object implements hxd.SceneEvents.Interactive {
 		cursor = Button;
 		cursor = Button;
 	}
 	}
 
 
+	public function set_showDebug(val) {
+		if( !val ) {
+			if( debugObj != null )
+				debugObj.remove();
+			debugObj = null;
+			return false;
+		}
+		if( debugObj != null )
+			return true;
+		debugObj = shape.makeDebugObj();
+		if( debugObj != null ) {
+			setupDebugMaterial(debugObj);
+
+			debugObj.ignoreParentTransform = true;
+			this.addChild(debugObj);
+		}
+		return debugObj != null;
+	}
+
+	public static dynamic function setupDebugMaterial(debugObj: Object) {
+		var materials = debugObj.getMaterials();
+		for( m in materials ) {
+			var engine = h3d.Engine.getCurrent();
+			if( engine.driver.hasFeature(Wireframe) )
+				m.mainPass.wireframe = true;
+			m.castShadows = false;
+			m.receiveShadows = false;
+			// m.blendMode = Alpha;
+			// m.mainPass.depth(false, Always);
+		}
+	}
+
 	override function onAdd() {
 	override function onAdd() {
 		this.scene = getScene();
 		this.scene = getScene();
 		if( scene != null ) scene.addEventTarget(this);
 		if( scene != null ) scene.addEventTarget(this);

+ 69 - 2
samples/Interactive.hx

@@ -1,6 +1,9 @@
 //PARAM=-D resourcesPath=../../skin_res
 //PARAM=-D resourcesPath=../../skin_res
 
 
-class Interactive extends hxd.App {
+class Interactive extends SampleApp {
+
+	var interactives : Array<h3d.scene.Interactive> = [];
+	var showDebug = false;
 
 
 	var rnd : hxd.Rand;
 	var rnd : hxd.Rand;
 	var light : h3d.scene.fwd.DirLight;
 	var light : h3d.scene.fwd.DirLight;
@@ -34,9 +37,13 @@ class Interactive extends hxd.App {
 			beacon.remove();
 			beacon.remove();
 			beacon = null;
 			beacon = null;
 		};
 		};
+		interactives.push(i);
+		if( showDebug )
+			i.showDebug = showDebug;
 	}
 	}
 
 
 	override function init() {
 	override function init() {
+		super.init();
 		light = new h3d.scene.fwd.DirLight(new h3d.Vector( 0.3, -0.4, -0.9), s3d);
 		light = new h3d.scene.fwd.DirLight(new h3d.Vector( 0.3, -0.4, -0.9), s3d);
 		light.enableSpecular = true;
 		light.enableSpecular = true;
 		light.color.set(0.28, 0.28, 0.28);
 		light.color.set(0.28, 0.28, 0.28);
@@ -45,7 +52,7 @@ class Interactive extends hxd.App {
 
 
 		rnd = new hxd.Rand(5);
 		rnd = new hxd.Rand(5);
 		for(i in 0...8) {
 		for(i in 0...8) {
-			var c = if( rnd.random(2) == 0 ) new h3d.prim.Cube() else new h3d.prim.Sphere(1,64,32);
+			var c = if( rnd.random(2) == 0 ) new h3d.prim.Cube() else new h3d.prim.Sphere(1, 30, 20);
 			//c.unindex();
 			//c.unindex();
 			c.addNormals();
 			c.addNormals();
 			c.addUVs();
 			c.addUVs();
@@ -53,7 +60,10 @@ class Interactive extends hxd.App {
 			m.x = rnd.srand() * 0.9;
 			m.x = rnd.srand() * 0.9;
 			m.y = rnd.srand() * 0.9;
 			m.y = rnd.srand() * 0.9;
 			m.scale(0.25 + rnd.rand() * 0.3);
 			m.scale(0.25 + rnd.rand() * 0.3);
+			if( i & 1 == 0 )
+				m.rotate(0.25, 0.5, 0.8);
 			m.material.mainPass.enableLights = true;
 			m.material.mainPass.enableLights = true;
+
 			m.material.shadows = true;
 			m.material.shadows = true;
 			var c = 0.3 + rnd.rand() * 0.3;
 			var c = 0.3 + rnd.rand() * 0.3;
 			var color = new h3d.Vector(c, c * 0.6, c * 0.6);
 			var color = new h3d.Vector(c, c * 0.6, c * 0.6);
@@ -63,6 +73,54 @@ class Interactive extends hxd.App {
 			initInteract(interact, m);
 			initInteract(interact, m);
 		}
 		}
 
 
+		// A cylinder with a capsule collider
+		{
+			var cradius = 0.5;
+			var cheight = 1;
+			var c = new h3d.prim.Cylinder(20, cradius, cheight);
+			//c.unindex();
+			c.addNormals();
+			c.addUVs();
+			var m = new h3d.scene.Mesh(c, s3d);
+			m.y = 1.2;
+			m.scale(0.25 + rnd.rand() * 0.3);
+			m.rotate(-0.25, -0.5, -0.8);
+			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 p1 = new h3d.col.Point(0, 0, cheight);
+			var p2 = new h3d.col.Point(0, 0, 0);
+			var col = new h3d.col.Capsule(p1, p2, cradius);
+
+			var interact = new h3d.scene.Interactive(new h3d.col.ObjectCollider(m, col), s3d);
+			initInteract(interact, m);
+		}
+
+		// A sphere with bounds (box/square collider)
+		{
+			var c = new h3d.prim.Sphere(0.5, 15, 10);
+			//c.unindex();
+			c.addNormals();
+			c.addUVs();
+			var m = new h3d.scene.Mesh(c, s3d);
+			m.x = 0.3;
+			m.y = -1.2;
+			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 col = m.getBounds();
+			var interact = new h3d.scene.Interactive(col, s3d);
+			initInteract(interact, m);
+		}
+
 		var cache = new h3d.prim.ModelCache();
 		var cache = new h3d.prim.ModelCache();
 		obj = cache.loadModel(hxd.Res.Model);
 		obj = cache.loadModel(hxd.Res.Model);
 		obj.scale(1 / 20);
 		obj.scale(1 / 20);
@@ -103,9 +161,18 @@ class Interactive extends hxd.App {
 			pix = null;
 			pix = null;
 		};
 		};
 
 
+		addCheck("Show Debug Colliders", function() { return showDebug; }, function(v) { setDebug(v); });
+
+		new h3d.scene.CameraController(s3d).loadFromCamera();
 		onResize();
 		onResize();
 	}
 	}
 
 
+	function setDebug(showDebug) {
+		this.showDebug = showDebug;
+		for( i in interactives )
+			i.showDebug = showDebug;
+	}
+
 	override function onResize() {
 	override function onResize() {
 		b.x = (s2d.width >> 1) - 200;
 		b.x = (s2d.width >> 1) - 200;
 		b.y = 150;
 		b.y = 150;