Explorar el Código

added h3d.scene.Interactive (close #57)

ncannasse hace 9 años
padre
commit
606c2b1dc2
Se han modificado 10 ficheros con 397 adiciones y 23 borrados
  1. 1 1
      h2d/Interactive.hx
  2. 28 6
      h2d/Scene.hx
  3. 39 6
      h3d/col/Ray.hx
  4. 7 0
      h3d/col/RayCollider.hx
  5. 149 0
      h3d/scene/Interactive.hx
  6. 54 5
      h3d/scene/Object.hx
  7. 114 1
      h3d/scene/Scene.hx
  8. 2 1
      hxd/App.hx
  9. 1 0
      hxd/Event.hx
  10. 2 3
      hxd/SceneEvents.hx

+ 1 - 1
h2d/Interactive.hx

@@ -64,7 +64,7 @@ class Interactive extends Drawable implements hxd.SceneEvents.Interactive {
 	}
 
 	@:noCompletion public function getInteractiveScene() : hxd.SceneEvents.InteractiveScene {
-		return getScene();
+		return scene;
 	}
 
 	@:noCompletion public function handleEvent( e : hxd.Event ) {

+ 28 - 6
h2d/Scene.hx

@@ -141,7 +141,7 @@ class Scene extends Layers implements h3d.IDrawable implements hxd.SceneEvents.I
 		return null;
 	}
 
-	public function screenToLocal( e : hxd.Event ) {
+	function screenToLocal( e : hxd.Event ) {
 		var x = screenXToLocal(e.relX);
 		var y = screenYToLocal(e.relY);
 		var rx = x * matA + y * matB + absX;
@@ -150,6 +150,32 @@ class Scene extends Layers implements h3d.IDrawable implements hxd.SceneEvents.I
 		e.relY = ry;
 	}
 
+	public function dispatchEvent( event : hxd.Event, to : hxd.SceneEvents.Interactive ) {
+		var i : Interactive = cast to;
+		screenToLocal(event);
+
+		var rx = event.relX;
+		var ry = event.relY;
+
+		var dx = rx - i.absX;
+		var dy = ry - i.absY;
+
+		var w1 = i.width * i.matA;
+		var h1 = i.width * i.matC;
+		var ky = h1 * dx + w1 * dy;
+
+		var w2 = i.height * i.matB;
+		var h2 = i.height * i.matD;
+		var kx = w2 * dy + h2 * dx;
+
+		var max = w1 * h2 - h1 * w2;
+
+		event.relX = (kx / max) * i.width;
+		event.relY = (ky / max) * i.height;
+
+		i.handleEvent(event);
+	}
+
 	public function handleEvent( event : hxd.Event, last : hxd.SceneEvents.Interactive ) : hxd.SceneEvents.Interactive {
 		screenToLocal(event);
 		var rx = event.relX;
@@ -297,11 +323,7 @@ class Scene extends Layers implements h3d.IDrawable implements hxd.SceneEvents.I
 
 	@:allow(h2d)
 	function removeEventTarget(i,notify=false) {
-		for( k in 0...interactive.length )
-			if( interactive[k] == i ) {
-				interactive.splice(k, 1);
-				break;
-			}
+		interactive.remove(i);
 		if( notify && events != null )
 			@:privateAccess events.onRemove(i);
 	}

+ 39 - 6
h3d/col/Ray.hx

@@ -4,16 +4,36 @@ import hxd.Math;
 @:allow(h3d.col)
 class Ray {
 
-	var px : Float;
-	var py : Float;
-	var pz : Float;
-	var lx : Float;
-	var ly : Float;
-	var lz : Float;
+	public var px : Float;
+	public var py : Float;
+	public var pz : Float;
+	public var lx : Float;
+	public var ly : Float;
+	public var lz : Float;
 
 	inline function new() {
 	}
 
+	public inline function clone() {
+		var r = new Ray();
+		r.px = px;
+		r.py = py;
+		r.pz = pz;
+		r.lx = lx;
+		r.ly = ly;
+		r.lz = lz;
+		return r;
+	}
+
+	public inline function load( r : Ray ) {
+		px = r.px;
+		py = r.py;
+		pz = r.pz;
+		lx = r.lx;
+		ly = r.ly;
+		lz = r.lz;
+	}
+
 	public function normalize() {
 		var l = lx * lx + ly * ly + lz * lz;
 		if( l < Math.EPSILON ) l = 0 else l = Math.invSqrt(l);
@@ -22,6 +42,19 @@ class Ray {
 		lz *= l;
 	}
 
+	public inline function transform( m : h3d.Matrix ) {
+		var p = new h3d.Vector(px, py, pz);
+		p.transform3x4(m);
+		px = p.x;
+		py = p.y;
+		pz = p.z;
+		var l = new h3d.Vector(lx, ly, lz);
+		l.transform3x3(m);
+		lx = l.x;
+		ly = l.y;
+		lz = l.z;
+	}
+
 	public inline function getPos() {
 		return new Point(px, py, pz);
 	}

+ 7 - 0
h3d/col/RayCollider.hx

@@ -0,0 +1,7 @@
+package h3d.col;
+
+interface RayCollider {
+
+	public function rayIntersection( r : Ray, ?p : Point ) : Null<Point>;
+
+}

+ 149 - 0
h3d/scene/Interactive.hx

@@ -0,0 +1,149 @@
+package h3d.scene;
+
+class Interactive extends Object implements hxd.SceneEvents.Interactive {
+
+	public var shape : h3d.col.RayCollider;
+	public var cursor(default,set) : hxd.System.Cursor;
+	/**
+		Set the default `cancel` mode (see `hxd.Event`), default to false.
+	**/
+	public var cancelEvents : Bool = false;
+	/**
+		Set the default `propagate` mode (see `hxd.Event`), default to false.
+	**/
+	public var propagateEvents : Bool = false;
+	public var enableRightButton : Bool;
+	var scene : Scene;
+	var isMouseDown : Int;
+
+	@:allow(h3d.scene.Scene)
+	var hitPoint = new h3d.Vector();
+
+	public function new(shape, ?parent) {
+		super(parent);
+		this.shape = shape;
+		cursor = Button;
+	}
+
+	override function onAlloc() {
+		this.scene = getScene();
+		if( scene != null ) scene.addEventTarget(this);
+		super.onAlloc();
+	}
+
+	override function onDelete() {
+		if( scene != null ) {
+			scene.removeEventTarget(this);
+			scene = null;
+		}
+		super.onDelete();
+	}
+
+	@:noCompletion public function getInteractiveScene() : hxd.SceneEvents.InteractiveScene {
+		return scene;
+	}
+
+	@:noCompletion public function handleEvent( e : hxd.Event ) {
+		if( propagateEvents ) e.propagate = true;
+		if( cancelEvents ) e.cancel = true;
+		switch( e.kind ) {
+		case EMove:
+			onMove(e);
+		case EPush:
+			if( enableRightButton || e.button == 0 ) {
+				isMouseDown = e.button;
+				onPush(e);
+			}
+		case ERelease:
+			if( enableRightButton || e.button == 0 ) {
+				onRelease(e);
+				if( isMouseDown == e.button )
+					onClick(e);
+			}
+			isMouseDown = -1;
+		case EReleaseNoClick:
+			if( enableRightButton || e.button == 0 ) {
+				e.kind = ERelease;
+				onRelease(e);
+				e.kind = EReleaseNoClick;
+			}
+			isMouseDown = -1;
+		case EOver:
+			hxd.System.setCursor(cursor);
+			onOver(e);
+		case EOut:
+			isMouseDown = -1;
+			hxd.System.setCursor(Default);
+			onOut(e);
+		case EWheel:
+			onWheel(e);
+		case EFocusLost:
+			onFocusLost(e);
+		case EFocus:
+			onFocus(e);
+		case EKeyUp:
+			onKeyUp(e);
+		case EKeyDown:
+			onKeyDown(e);
+		}
+	}
+
+	function set_cursor(c) {
+		this.cursor = c;
+		if( isOver() )
+			hxd.System.setCursor(cursor);
+		return c;
+	}
+
+	public function focus() {
+		if( scene == null || scene.events == null )
+			return;
+		scene.events.focus(this);
+	}
+
+	public function blur() {
+		if( hasFocus() ) scene.events.blur();
+	}
+
+	public function isOver() {
+		return scene != null && scene.events != null && @:privateAccess scene.events.currentOver == this;
+	}
+
+	public function hasFocus() {
+		return scene != null && scene.events != null && @:privateAccess scene.events.currentFocus == this;
+	}
+
+	public dynamic function onOver( e : hxd.Event ) {
+	}
+
+	public dynamic function onOut( e : hxd.Event ) {
+	}
+
+	public dynamic function onPush( e : hxd.Event ) {
+	}
+
+	public dynamic function onRelease( e : hxd.Event ) {
+	}
+
+	public dynamic function onClick( e : hxd.Event ) {
+	}
+
+	public dynamic function onMove( e : hxd.Event ) {
+	}
+
+	public dynamic function onWheel( e : hxd.Event ) {
+	}
+
+	public dynamic function onFocus( e : hxd.Event ) {
+	}
+
+	public dynamic function onFocusLost( e : hxd.Event ) {
+	}
+
+	public dynamic function onKeyUp( e : hxd.Event ) {
+	}
+
+	public dynamic function onKeyDown( e : hxd.Event ) {
+	}
+
+}

+ 54 - 5
h3d/scene/Object.hx

@@ -6,6 +6,7 @@ package h3d.scene;
 	public var FCulled = 4;
 	public var FFollowPosition = 8;
 	public var FLightCameraCenter = 16;
+	public var FAllocated = 32;
 	public inline function toInt() return this;
 }
 
@@ -25,7 +26,8 @@ class Object {
 	public var scaleX(default,set) : Float;
 	public var scaleY(default, set) : Float;
 	public var scaleZ(default,set) : Float;
-	public var visible(get,set) : Bool;
+	public var visible(get, set) : Bool;
+	var allocated(get,set) : Bool;
 
 	/**
 		Follow a given object or joint as if it was our parent. Ignore defaultTransform when set.
@@ -69,6 +71,7 @@ class Object {
 	}
 
 	inline function get_visible() return (flags & FVisible.toInt()) != 0;
+	inline function get_allocated() return (flags & FAllocated.toInt()) != 0;
 	inline function get_posChanged() return (flags & FPosChanged.toInt()) != 0;
 	inline function get_culled() return (flags & FCulled.toInt()) != 0;
 	inline function get_followPositionOnly() return (flags & FFollowPosition.toInt()) != 0;
@@ -76,6 +79,7 @@ class Object {
 	inline function set_posChanged(b) { if( b ) flags |= FPosChanged.toInt() else flags &= ~FPosChanged.toInt(); return b; }
 	inline function set_culled(b) { if( b ) flags |= FCulled.toInt() else flags &= ~FCulled.toInt(); return b; }
 	inline function set_visible(b) { culled = !b; if( b ) flags |= FVisible.toInt() else flags &= ~FVisible.toInt(); return b; }
+	inline function set_allocated(b) { if( b ) flags |= FAllocated.toInt() else flags &= ~FAllocated.toInt(); return b; }
 	inline function set_followPositionOnly(b) { if( b ) flags |= FFollowPosition.toInt() else flags &= ~FFollowPosition.toInt(); return b; }
 	inline function set_lightCameraCenter(b) { if( b ) flags |= FLightCameraCenter.toInt() else flags &= ~FLightCameraCenter.toInt(); return b; }
 
@@ -235,17 +239,62 @@ class Object {
 			if( p == o ) throw "Recursive addChild";
 			p = p.parent;
 		}
-		if( o.parent != null )
+		if( o.parent != null ) {
+			// prevent calling onDelete
+			var old = o.allocated;
+			o.allocated = false;
 			o.parent.removeChild(o);
-		childs.insert(pos,o);
+			o.allocated = old;
+		}
+		childs.insert(pos, o);
+		if( !allocated && o.allocated )
+			o.onDelete();
 		o.parent = this;
-		o.lastFrame = -1;
 		o.posChanged = true;
+		// ensure that proper alloc/delete is done if we change parent
+		if( allocated ) {
+			if( !o.allocated )
+				o.onAlloc();
+			else
+				o.onParentChangedRec();
+		}
+	}
+
+	function onParentChangedRec() {
+		onParentChanged();
+		for( c in childs )
+			c.onParentChangedRec();
+	}
+
+	// called when we're allocated already but moved in hierarchy
+	function onParentChanged() {
+	}
+
+	// kept for internal init
+	function onAlloc() {
+		allocated = true;
+		for( c in childs )
+			c.onAlloc();
+	}
+
+	// kept for internal cleanup
+	function onDelete() {
+		allocated = false;
+		for( c in childs )
+			c.onDelete();
 	}
 
 	public function removeChild( o : Object ) {
-		if( childs.remove(o) )
+		if( childs.remove(o) ) {
+			if( o.allocated ) o.onDelete();
 			o.parent = null;
+		}
+	}
+
+	function getScene() {
+		var p = this;
+		while( p.parent != null ) p = p.parent;
+		return Std.instance(p, Scene);
 	}
 
 	public inline function isMesh() {

+ 114 - 1
h3d/scene/Scene.hx

@@ -1,6 +1,6 @@
 package h3d.scene;
 
-class Scene extends Object implements h3d.IDrawable {
+class Scene extends Object implements h3d.IDrawable implements hxd.SceneEvents.InteractiveScene {
 
 	public var camera : h3d.Camera;
 	public var lightSystem : h3d.pass.LightSystem;
@@ -8,9 +8,15 @@ class Scene extends Object implements h3d.IDrawable {
 	var prePasses : Array<h3d.IDrawable>;
 	var postPasses : Array<h3d.IDrawable>;
 	var ctx : RenderContext;
+	var interactives : Array<Interactive>;
+	@:allow(h3d.scene.Interactive)
+	var events : hxd.SceneEvents;
+	var hitInteractives : Array<Interactive>;
 
 	public function new() {
 		super(null);
+		hitInteractives = [];
+		interactives = [];
 		camera = new h3d.Camera();
 		ctx = new RenderContext();
 		renderer = new Renderer();
@@ -19,6 +25,98 @@ class Scene extends Object implements h3d.IDrawable {
 		prePasses = [];
 	}
 
+	@:noCompletion public function setEvents(events) {
+		this.events = events;
+	}
+
+	public function dispatchListeners(event:hxd.Event) {
+	}
+
+	function sortHitPointByCameraDistance( i1 : Interactive, i2 : Interactive ) {
+		var z1 = i1.hitPoint.w;
+		var z2 = i2.hitPoint.w;
+		if( z1 > z2 )
+			return 1;
+		return -1;
+	}
+
+	public function dispatchEvent( event : hxd.Event, to : hxd.SceneEvents.Interactive ) {
+		var i : Interactive = cast to;
+		// TODO : compute relX/Y/Z
+		i.handleEvent(event);
+	}
+
+	public function handleEvent( event : hxd.Event, last : hxd.SceneEvents.Interactive ) {
+
+		if( interactives.length == 0 )
+			return null;
+
+		if( hitInteractives.length == 0 ) {
+
+			var stage = hxd.Stage.getInstance();
+			var screenX = (event.relX / stage.width - 0.5) * 2;
+			var screenY = -(event.relY / stage.height - 0.5) * 2;
+			var p0 = camera.unproject(screenX, screenY, 0);
+			var p1 = camera.unproject(screenX, screenY, 1);
+			var r = h3d.col.Ray.fromPoints(p0.toPoint(), p1.toPoint());
+			var saveR = r.clone();
+
+			var hitTmp = new h3d.col.Point();
+			for( i in interactives ) {
+				var minv = i.getInvPos();
+				r.transform(minv);
+				r.normalize();
+				var hit = i.shape.rayIntersection(r, hitTmp);
+				r.load(saveR);
+				if( hit == null ) continue;
+
+				i.hitPoint.x = hit.x;
+				i.hitPoint.y = hit.y;
+				i.hitPoint.z = hit.z;
+				hitInteractives.push(i);
+			}
+
+			if( hitInteractives.length == 0 )
+				return null;
+
+			if( hitInteractives.length > 1 ) {
+				for( i in hitInteractives ) {
+					var m = i.invPos;
+					var p = i.hitPoint.clone();
+					p.transform3x4(i.absPos);
+					p.project(camera.m);
+					i.hitPoint.w = p.z;
+				}
+				hitInteractives.sort(sortHitPointByCameraDistance);
+			}
+
+			hitInteractives.push(null);
+		}
+
+
+		while( hitInteractives.length > 0 ) {
+
+			var i = hitInteractives.pop();
+			if( i == null )
+				return null;
+
+			event.relX = i.hitPoint.x;
+			event.relY = i.hitPoint.y;
+			event.relZ = i.hitPoint.z;
+			i.handleEvent(event);
+
+			if( event.cancel ) {
+				event.cancel = false;
+				event.propagate = true;
+				continue;
+			}
+
+			return i;
+		}
+
+		return null;
+	}
+
 	override function clone( ?o : Object ) {
 		var s = o == null ? new Scene() : cast o;
 		s.camera = camera.clone();
@@ -47,6 +145,17 @@ class Scene extends Object implements h3d.IDrawable {
 		prePasses.remove(p);
 	}
 
+	@:allow(h3d)
+	function addEventTarget(i:Interactive) {
+		interactives.push(i);
+	}
+
+	@:allow(h3d)
+	function removeEventTarget(i:Interactive) {
+		if( interactives.remove(i) && events != null )
+			@:privateAccess events.onRemove(i);
+	}
+
 	public function setElapsedTime( elapsedTime ) {
 		ctx.elapsedTime = elapsedTime;
 	}
@@ -54,6 +163,10 @@ class Scene extends Object implements h3d.IDrawable {
 	@:access(h3d.mat.Pass)
 	@:access(h3d.scene.RenderContext)
 	public function render( engine : h3d.Engine ) {
+
+		if( !allocated )
+			onAlloc();
+
 		camera.screenRatio = engine.width / engine.height;
 		camera.update();
 		ctx.camera = camera;

+ 2 - 1
hxd/App.hx

@@ -41,6 +41,7 @@ class App {
 		s3d.addPass(s2d);
 		sevents = new hxd.SceneEvents();
 		sevents.addScene(s2d);
+		sevents.addScene(s3d);
 		loadAssets(function() {
 			initDone = true;
 			init();
@@ -50,7 +51,7 @@ class App {
 			hxd.Key.initialize();
 		});
 	}
-	
+
 	function dispose() {
 		s2d.dispose();
 		s3d.dispose();

+ 1 - 0
hxd/Event.hx

@@ -19,6 +19,7 @@ class Event {
 	public var kind : EventKind;
 	public var relX : Float;
 	public var relY : Float;
+	public var relZ : Float;
 	/**
 		Will propagate the event to other interactives that are below the current one.
 	**/

+ 2 - 3
hxd/SceneEvents.hx

@@ -2,8 +2,8 @@ package hxd;
 
 interface InteractiveScene {
 	public function setEvents( s : SceneEvents ) : Void;
-	public function screenToLocal( e : Event ) : Void;
 	public function handleEvent( e : Event, last : Interactive ) : Interactive;
+	public function dispatchEvent( e : Event, to : Interactive ) : Void;
 	public function dispatchListeners( e : Event ) : Void;
 }
 
@@ -188,8 +188,7 @@ class SceneEvents {
 					var s = i.getInteractiveScene();
 					if( s == null ) continue;
 					event.kind = EReleaseNoClick;
-					s.screenToLocal(event);
-					i.handleEvent(event);
+					s.dispatchEvent(event,i);
 					event.kind = ERelease;
 					event.relX = oldX;
 					event.relY = oldY;