فهرست منبع

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 1 سال پیش
والد
کامیت
c19126e621
3فایلهای تغییر یافته به همراه207 افزوده شده و 1 حذف شده
  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",
 		"grd" => "hxd.res.Gradients",
 		#if hide
-		"prefab,fx,fx2d,l3d" => "hxd.res.Prefab"
+		"prefab,fx,fx2d,l3d" => "hxd.res.Prefab",
+		"world" => "hxd.res.World"
 		#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