Parcourir la source

Adding HierarchicalWorld to heaps. It creates a quad tree useful for gameplay and asset management on large worlds. onCreate dynamic function can be used to streaming. It is automatically created with hrt.prefab.World if working with hide.

clementlandrin il y a 1 an
Parent
commit
c19126e621
3 fichiers modifiés avec 207 ajouts et 1 suppressions
  1. 198 0
      h3d/scene/HierarchicalWorld.hx
  2. 2 1
      hxd/res/Config.hx
  3. 7 0
      hxd/res/World.hx

+ 198 - 0
h3d/scene/HierarchicalWorld.hx

@@ -0,0 +1,198 @@
+package h3d.scene;
+
+typedef WorldData = {
+	var x : Int;
+	var y : Int;
+	var size : Int;
+	var depth : Int;
+	var maxDepth : Int;
+	var onCreate : HierarchicalWorld -> Void;
+}
+
+class HierarchicalWorld extends Object {
+
+	static public var FULL = false;
+	static public var DEBUG = false;
+
+	static inline final UNLOCK_COLOR = 0xFFFFFF;
+	static inline final LOCK_COLOR = 0xFF0000;
+
+	public var data : WorldData;
+	var bounds : h3d.col.Bounds;
+	var subdivided(default, set) = false;
+	function set_subdivided(v : Bool) {
+		subdivided = v;
+		updateGraphics();
+		return subdivided;
+	}
+	var debugGraphics : h3d.scene.Graphics;
+	// during edition, it's necessary to lock chunks that are being modified.
+	var locked(default, set) : Bool = false;
+	function set_locked(v : Bool) {
+		locked = v;
+		updateGraphics();
+		return locked;
+	}
+
+	function updateGraphics() {
+		if ( debugGraphics == null )
+			return;
+		var hasLockedColor = locked && data.depth == data.maxDepth;
+		var color = hasLockedColor ? LOCK_COLOR : UNLOCK_COLOR;
+		var s = debugGraphics.material.mainPass.getShader(h3d.shader.FixedColor);
+		s.color.setColor(color);
+		debugGraphics.lineStyle(hasLockedColor ? 10.0 : 1.0, 0xFFFFFF, 1.0);
+	}
+
+	function createGraphics() {
+		if ( debugGraphics != null )
+			throw "??";
+		var b = bounds.clone();
+		b.transform(getAbsPos().getInverse());
+		b.zMin = 0.0;
+		b.zMax = 0.1;
+		debugGraphics = new h3d.scene.Box(0xFFFFFF, b, false, this);
+		debugGraphics.material.mainPass.setPassName("afterTonemapping");
+		debugGraphics.material.shadows = false;
+		debugGraphics.material.mainPass.addShader(new h3d.shader.FixedColor(UNLOCK_COLOR));
+		updateGraphics();
+	}
+
+	public function new(parent, data : WorldData) {
+		super(parent);
+		this.data = data;
+		this.x = data.x;
+		this.y = data.y;
+		calcAbsPos();
+		bounds = new h3d.col.Bounds();
+		var halfSize = data.size >> 1;
+		// TBD : z bounds? Negative & positive infinity causes bounds to break.
+		var pseudoInfinity = 1e10;
+		bounds.addPoint(new h3d.col.Point(-halfSize, -halfSize, -pseudoInfinity));
+		bounds.addPoint(new h3d.col.Point(halfSize,halfSize, pseudoInfinity));
+		bounds.transform(absPos);
+
+		if ( data.depth != 0 && data.onCreate != null ) {
+			data.onCreate(this);
+		}
+	}
+
+	function init() {
+		if ( data.depth == 0 && data.onCreate != null )
+			data.onCreate(this);
+	}
+
+	function canSubdivide() {
+		return data.depth < data.maxDepth;
+	}
+
+	function createNode(parent, data) {
+		return new HierarchicalWorld(parent, data);
+	}
+
+	function subdivide() {
+		if ( subdivided )
+			return;
+		subdivided = true;
+		var childSize = data.size >> 1;
+		for ( i in 0...2 ) {
+			for ( j in 0...2 ) {
+				var halfChildSize = childSize >> 1;
+				var childData : WorldData = {
+					size : childSize,
+					x : i * childSize - halfChildSize,
+					y : j * childSize - halfChildSize,
+					depth : data.depth + 1,
+					maxDepth : data.maxDepth,
+					onCreate : data.onCreate
+				};
+				var node = createNode(this, childData);
+			}
+		}
+	}
+
+	function removeSubdivisions() {
+		if ( !subdivided )
+			return;
+		subdivided = false;
+		var i = children.length;
+		while ( i-- > 0 ) {
+			if ( Std.isOfType(children[i], HierarchicalWorld) )
+				children[i].remove();
+		}
+	}
+
+	function calcDist(ctx : h3d.scene.RenderContext) {
+		return ctx.camera.pos.distance(getAbsPos().getPosition());
+	}
+
+	override function syncRec(ctx : h3d.scene.RenderContext) {
+
+		if ( debugGraphics == null && DEBUG ) {
+			createGraphics();
+		} else if ( debugGraphics != null && !DEBUG ) {
+			debugGraphics.remove();
+			debugGraphics = null;
+		}
+
+		culled = !bounds.inFrustum(ctx.camera.frustum);
+		if ( canSubdivide() ) {
+			if ( FULL || calcDist(ctx) < data.size * 2.0 ) {
+				subdivide();
+			} else if ( !locked ) {
+				removeSubdivisions();
+			}
+		}
+		super.syncRec(ctx);
+	}
+
+	override function emitRec(ctx : h3d.scene.RenderContext) {
+		if ( culled )
+			return;
+		super.emitRec(ctx);
+	}
+
+	public function getChunkPos(x : Float, y : Float, depth = -1) {
+		var root = getRoot();
+		var depth = depth;
+		if ( depth < 0 )
+			depth = data.maxDepth;
+		var chunkSize = root.data.size >> depth;
+		return new h2d.col.Point((Math.floor(x / chunkSize) + 0.5) * chunkSize,
+			(Math.floor(y / chunkSize) + 0.5) * chunkSize);
+	}
+
+	public function requestCreateAt(x : Float, y : Float, lock : Bool) {
+		if ( !bounds.contains(new h3d.col.Point(x, y, 0.0)) )
+			return;
+		if ( lock )
+			locked = true;
+		if ( canSubdivide() )
+			subdivide();
+		for ( c in children ) {
+			var node = Std.downcast(c, HierarchicalWorld);
+			if ( node == null )
+				continue;
+			node.requestCreateAt(x, y, lock);
+		}
+	}
+
+	public function unlockAt(x : Float, y : Float) {
+		if ( !bounds.contains(new h3d.col.Point(x, y, 0.0)) )
+			return;
+		locked = false;
+		for ( c in children ) {
+			var node = Std.downcast(c, HierarchicalWorld);
+			if ( node == null )
+				continue;
+			node.unlockAt(x, y);
+		}
+	}
+
+	public function getRoot() : h3d.scene.HierarchicalWorld {
+		var root : h3d.scene.Object = this;
+		while ( Std.isOfType(root.parent, HierarchicalWorld) )
+			root = root.parent;
+		return cast root;
+	}
+}

+ 2 - 1
hxd/res/Config.hx

@@ -26,7 +26,8 @@ class Config {
 		"atlas" => "hxd.res.Atlas",
 		"atlas" => "hxd.res.Atlas",
 		"grd" => "hxd.res.Gradients",
 		"grd" => "hxd.res.Gradients",
 		#if hide
 		#if hide
-		"prefab,fx,fx2d,l3d" => "hxd.res.Prefab"
+		"prefab,fx,fx2d,l3d" => "hxd.res.Prefab",
+		"world" => "hxd.res.World"
 		#end
 		#end
 	];
 	];
 
 

+ 7 - 0
hxd/res/World.hx

@@ -0,0 +1,7 @@
+package hxd.res;
+
+#if hide
+typedef World = hrt.prefab.Resource;
+#else
+typedef World = hxd.res.Resource;
+#end