|
@@ -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
|
|
|
|