浏览代码

added objects support

Nicolas Cannasse 5 年之前
父节点
当前提交
e66ed47f04
共有 1 个文件被更改,包括 148 次插入2 次删除
  1. 148 2
      hrt/prefab/l3d/HeightMap.hx

+ 148 - 2
hrt/prefab/l3d/HeightMap.hx

@@ -7,6 +7,11 @@ enum abstract HeightMapTextureKind(String) {
 	var SplatMap = "splatmap";
 }
 
+private class WorldNoSoil extends h3d.scene.World {
+	override function initChunkSoil(c:h3d.scene.World.WorldChunk) {
+	}
+}
+
 class HeightMapShader extends hxsl.Shader {
 	static var SRC = {
 		@:import h3d.shader.BaseMesh;
@@ -83,7 +88,13 @@ class HeightMap extends Object3D {
 	var normalScale = 1.;
 	var tileX = 1;
 	var tileY = 1;
+	var objects : {
+		var file : String;
+		var assetsPath : String;
+	};
+
 	var heightTexturesCache : Array<hxd.Pixels>;
+	var objectsCache : Array<{ obj : String, x : Float, y : Float, scale : Float, rot : Float, tint : Int }>;
 
 	override function save():{} {
 		var o : Dynamic = super.save();
@@ -95,6 +106,8 @@ class HeightMap extends Object3D {
 			o.tileX = tileX;
 			o.tileY = tileY;
 		}
+		if( objects != null )
+			o.objects = objects;
 		return o;
 	}
 
@@ -111,11 +124,33 @@ class HeightMap extends Object3D {
 			tileX = 1;
 			tileY = 1;
 		}
+		objects = obj.objects;
+	}
+
+	public function getZ( x : Float, y : Float ) : Null<Float> {
+		var rx = x / size;
+		var ry = y / size;
+		var tx = Math.floor(rx);
+		var ty = Math.floor(ry);
+		if( tx < 0 || ty < 0 || tx >= tileX || ty >= tileY )
+			return null;
+		var curMap = getHeightMap(tx, ty);
+		var w = curMap.width;
+		var ix = Std.int( (rx - tx) * w );
+		var iy = Std.int( (ry - ty) * w );
+		var h = curMap.bytes.getFloat((ix+iy*w) << 2);
+		h *= getHScale();
+		return h;
 	}
 
 	override function localRayIntersection(ctx:Context, ray:h3d.col.Ray):Float {
 		if( ray.lz > 0 )
 			return -1; // only from top
+		if( ray.lx == 0 && ray.ly == 0 ) {
+			var z = getZ(ray.px, ray.py);
+			if( z == null || z > ray.pz ) return -1;
+			return ray.pz - z;
+		}
 		var maxZ = getHScale() * 100;
 		var b = h3d.col.Bounds.fromValues(0,0,0,size * tileX, size * tileY, maxZ);
 		var dist = b.rayIntersection(ray, false);
@@ -245,15 +280,16 @@ class HeightMap extends Object3D {
 			grid.addNormals();
 			mesh.primitive = grid;
 		}
+
 		updateMesh(ctx, mesh, 0, 0);
 		var prev = new Map();
 		for( c in mesh )
 			if( c.name != null && c.name.charCodeAt(0) == '$'.code )
-				prev.set(c.name, c.toMesh());
+				prev.set(c.name, c);
 		for( x in 0...tileX ) {
 			for( y in 0...tileY ) {
 				var name = "$h_"+x+"_"+y;
-				var sub = prev.get(name);
+				var sub = Std.downcast(prev.get(name), h3d.scene.Mesh);
 				if( sub == null ) {
 					sub = new h3d.scene.Mesh(mesh.primitive, mesh);
 					sub.name = name;
@@ -265,9 +301,85 @@ class HeightMap extends Object3D {
 				updateMesh(ctx, sub, x, y);
 			}
 		}
+
+		if( objects != null && objects.file != "" )
+			loadObjectCache(ctx);
+
+		if( objectsCache != null ) {
+			var world : h3d.scene.World = cast prev.get("$world");
+			var csize = Std.int(size/2);
+			var wsize = Std.int(size * hxd.Math.max(tileX, tileY));
+			var models = new Map();
+			var nullModel = new h3d.scene.World.WorldModel(null);
+			if( world == null || world.worldSize != wsize || world.chunkSize != csize ) {
+				world = new WorldNoSoil(csize, wsize, mesh);
+				world.name = "$world";
+			} else @:privateAccess {
+				prev.remove(world.name);
+				for( c in world.allChunks )
+					world.cleanChunk(c);
+			}
+			for( o in objectsCache ) {
+				var m = models.get(o.obj);
+				if( m == null ) {
+					var path = objects.assetsPath + "/" + o.obj;
+					var r = try hxd.res.Loader.currentInstance.load(path + ".FBX").toModel() catch( e : hxd.res.NotFound )
+						try hxd.res.Loader.currentInstance.load(path + ".fbx").toModel() catch( e : hxd.res.NotFound ) null;
+					m = r == null ? nullModel : world.loadModel(r);
+					models.set(o.obj, m);
+				}
+				if( m == nullModel ) continue;
+				world.add(m, o.x, o.y, getZ(o.x, o.y), o.scale, o.rot);
+				// TODO : add o.tint
+			}
+			world.done();
+		}
+
 		for( p in prev ) p.remove();
 	}
 
+	function loadObjectCache( ctx : Context ) {
+		var data = ctx.shared.loadBytes(objects.file);
+		if( data == null ) return;
+		objectsCache = [];
+		var xml = new haxe.xml.Access(Xml.parse(data.toString()).firstElement());
+		var resolution = Std.parseFloat(xml.node.Surface.att.ResolutionX);
+		var scale = size * tileX / resolution;
+		for( layer in xml.node.Objects.node.Layers.nodes.Layer ) {
+			var obj = layer.node.Object;
+			var name = obj.att.MeshAssetFileName;
+			var data = haxe.crypto.Base64.decode(obj.node.Data.innerData);
+			for( i in 0...Std.int(data.length/40) ) {
+				var p = i * 40;
+				var x = data.getFloat(p); p += 4;
+				p += 4; // skip
+				var y = resolution - data.getFloat(p); p += 4;
+
+				x *= scale;
+				y *= scale;
+				if( x < 0 || y < 0 || x >= size * tileX || y >= size * tileY ) continue;
+
+				var scW = data.getFloat(p); p += 4;
+				var scH = data.getFloat(p); p += 4;
+				var rotX = data.getFloat(p); p += 4;
+				var rotY = data.getFloat(p); p += 4;
+				var rotZ = data.getFloat(p); p += 4;
+				p += 4; // ???
+				var tint = data.getInt32(p);
+				tint = tint & 0xFFFFFF;
+				tint = ((tint & 0xFF) << 16) | (tint & 0xFF00) | (tint >> 16);
+				objectsCache.push({
+					obj : name,
+					x : x,
+					y : y,
+					scale : scW * scale,
+					rot : rotY,
+					tint : tint,
+				});
+			}
+		}
+	}
+
 	function getHScale() {
 		return heightScale * size * 0.1;
 	}
@@ -317,8 +429,10 @@ class HeightMap extends Object3D {
 				<dt>X</dt><dd><input type="range" min="1" max="16" step="1" field="tileX"/></dd>
 				<dt>Y</dt><dd><input type="range" min="1" max="16" step="1" field="tileY"/></dd>
 			</dl>
+			<div class="group" name="Objects">
 			</div>
 		');
+
 		var list = props.find("ul");
 		ectx.properties.add(props,this, (_) -> updateInstance(ctx));
 		for( tex in textures ) {
@@ -371,6 +485,38 @@ class HeightMap extends Object3D {
 			textures.push({ path : null, kind : Albedo, enable: true });
 			ectx.rebuildProperties();
 		});
+
+		var objs = props.find("[name=Objects]");
+		if( objects == null ) {
+			var e = new hide.Element('
+			<dl>
+				<dt></dt><dd><a class="button" href="#">Add</a></dd>
+			</dl>
+			');
+			e.appendTo(objs).find("a.button").click(function(_) {
+				objects = {
+					file : "",
+					assetsPath : "",
+				};
+				ectx.rebuildProperties();
+			});
+		} else {
+			var e = new hide.Element('
+			<dl>
+				<dt>File</dt><dd><input type="fileselect" field="file"/></dd>
+				<dt>Assets Path</dt><dd><input field="assetsPath"/></dd>
+				<dt></dt><dd><a class="button" href="#">Remove</a></dd>
+			</dl>
+			');
+			ectx.properties.build(e, objects, function(_) {
+				objectsCache = null;
+				updateInstance(ctx);
+			});
+			e.appendTo(objs).find("a.button").click(function(_) {
+				objects = null;
+				ectx.rebuildProperties();
+			});
+		}
 	}
 	#end