2
0
Эх сурвалжийг харах

more serialization support

ncannasse 8 жил өмнө
parent
commit
e8c32cb1a8

+ 66 - 6
h3d/impl/Serializable.hx

@@ -18,29 +18,85 @@ class SceneSerializer extends hxbit.Serializer {
 	var shaderUID = 0;
 	var shaderIndexes = new Map<hxsl.Shader,Int>();
 	var cachedShaders = new Array<hxsl.Shader>();
+	var cachedTextures = new Map<Int,h3d.mat.Texture>();
 
 	function addTexture( t : h3d.mat.Texture ) {
 		if( t == null ) {
 			addInt(0);
 			return true;
 		}
-		if( t.name != null ) {
+		addInt(t.id);
+		if( cachedTextures.exists(t.id) )
+			return true;
+		addInt(t.filter.getIndex());
+		addInt(t.mipMap.getIndex());
+		addInt(t.wrap.getIndex());
+		cachedTextures.set(t.id, t);
+		if( t.name != null && hxd.res.Loader.currentInstance.exists(t.name) ) {
 			addInt(1);
 			addString(t.name);
 			return true;
 		}
+		if( t.flags.has(Serialize) ) {
+			addInt(2);
+			var pix = t.capturePixels();
+			addInt(t.width);
+			addInt(t.height);
+			addInt(t.flags.toInt());
+			addInt(t.format.getIndex());
+			addInt(pix.format.getIndex());
+			addBytesSub(pix.bytes, 0, t.width * t.height * hxd.Pixels.bytesPerPixel(pix.format));
+			return true;
+		}
+		var tch = Std.instance(t, h3d.mat.TextureChannels);
+		if( tch != null ) {
+			addInt(3);
+			var channels = @:privateAccess tch.channels;
+			addInt(channels.length);
+			for( i in 0...channels.length ) {
+				var c = channels[i];
+				if( c == null ) continue;
+				if( c.r == null ) return false;
+				addInt(i);
+				addString(c.r.entry.path);
+				addInt(c.c.toInt());
+			}
+			return true;
+		}
 		return false;
 	}
 
 	function getTexture() {
-		switch( getInt() ) {
-		case 0:
+		var tid = getInt();
+		if( tid == 0 )
 			return null;
+		var t = cachedTextures.get(tid);
+		if( t != null )
+			return t;
+		var filter = h3d.mat.Data.Filter.createByIndex(getInt());
+		var mipmap = h3d.mat.Data.MipMap.createByIndex(getInt());
+		var wrap = h3d.mat.Data.Wrap.createByIndex(getInt());
+		switch( getInt() ) {
 		case 1:
-			return resolveTexture(getString());
+			t = resolveTexture(getString());
+		case 2:
+			var width = getInt(), height = getInt();
+			var flags : haxe.EnumFlags<h3d.mat.Data.TextureFlags> = haxe.EnumFlags.ofInt(getInt());
+			var format = h3d.mat.Data.TextureFormat.createByIndex(getInt());
+			var pixFormat = h3d.mat.Data.TextureFormat.createByIndex(getInt());
+			var flags = [for( f in h3d.mat.Data.TextureFlags.createAll() ) if( flags.has(f) ) f];
+			t = new h3d.mat.Texture(width, height, flags, format);
+			t.uploadPixels(new hxd.Pixels(width, height, getBytes(), pixFormat));
+		case 3:
+			throw "TODO";
 		default:
 			throw "assert";
 		}
+		t.filter = filter;
+		t.mipMap = mipmap;
+		t.wrap = wrap;
+		cachedTextures.set(tid, t);
+		return t;
 	}
 
 	function resolveTexture( path : String ) {
@@ -96,7 +152,7 @@ class SceneSerializer extends hxbit.Serializer {
 	}
 
 	function addShaderVar( v : hxsl.Ast.TVar, s : hxsl.Shader ) {
-		if( !canSerializeVar(v) )
+		if( v.kind != Param )
 			return;
 		switch( v.type ) {
 		case TStruct(vl):
@@ -105,6 +161,10 @@ class SceneSerializer extends hxbit.Serializer {
 			return;
 		default:
 		}
+		if( !canSerializeVar(v) ) {
+			shaderVarIndex++;
+			return;
+		}
 		var val : Dynamic = s.getParamValue(shaderVarIndex++);
 		switch( v.type ) {
 		case TBool:
@@ -123,7 +183,7 @@ class SceneSerializer extends hxbit.Serializer {
 			if( !addTexture(val) )
 				throw "Cannot serialize unnamed texture " + s+"."+v.name+" = "+val;
 		default:
-			throw "Cannot serialize macro var " + v.name+":"+hxsl.Ast.Tools.toString(v.type);
+			throw "Cannot serialize macro var " + v.name+":"+hxsl.Ast.Tools.toString(v.type)+" in "+s;
 		}
 	}
 

+ 4 - 0
h3d/mat/Data.hx

@@ -112,6 +112,10 @@ enum TextureFlags {
 		The texture is being currently loaded. Set onLoaded to get event when loading is complete.
 	**/
 	Loading;
+	/**
+		Allow texture data serialization when found in a scene (for user generated textures)
+	**/
+	Serialize;
 }
 
 typedef TextureFormat = hxd.PixelFormat;

+ 1 - 1
h3d/mat/Pass.hx

@@ -214,7 +214,7 @@ class Pass implements h3d.impl.Serializable {
 			}
 		}
 		setPassName(name);
-		//loadBits(bits);
+		loadBits(bits);
 	}
 	#end
 

+ 6 - 6
h3d/mat/Stencil.hx

@@ -5,9 +5,9 @@ import h3d.mat.Data;
 @:build(hxd.impl.BitsBuilder.build())
 class Stencil implements h3d.impl.Serializable {
 
-	var frontRefBits : Int = 0;
-	var backRefBits  : Int = 0;
-	var opBits       : Int = 0;
+	@:s var frontRefBits : Int = 0;
+	@:s var backRefBits  : Int = 0;
+	@:s var opBits       : Int = 0;
 
 	@:bits(opBits) public var frontTest : Compare;
 	@:bits(opBits) public var frontSTfail : StencilOp;
@@ -102,9 +102,9 @@ class Stencil implements h3d.impl.Serializable {
 	public function customSerialize( ctx : hxbit.Serializer ) {
 	}
 	public function customUnserialize( ctx : hxbit.Serializer ) {
-		//loadFrontRefBits(frontRefBits);
-		//loadBackRefBits(backRefBits);
-		//loadOpBits(opBits);
+		loadFrontRefBits(frontRefBits);
+		loadBackRefBits(backRefBits);
+		loadOpBits(opBits);
 	}
 	#end
 

+ 3 - 3
h3d/mat/Texture.hx

@@ -239,13 +239,13 @@ class Texture {
 	}
 
 	/**
-		Downloads the current texture data from the GPU. On some platforms, color might be premultiplied by Alpha unless withAlpha = true.
+		Downloads the current texture data from the GPU.
 		Beware, this is a very slow operation that shouldn't be done during rendering.
 	**/
-	public function capturePixels( withAlpha = false ) {
+	public function capturePixels() {
 		#if flash
 
-		var twoPassCapture = #if flash withAlpha #else false #end;
+		var twoPassCapture = true;
 
 		var e = h3d.Engine.getCurrent();
 		var oldW = e.width, oldH = e.height;

+ 10 - 3
h3d/mat/TextureChannels.hx

@@ -4,6 +4,7 @@ import h3d.mat.Data;
 class TextureChannels extends Texture {
 
 	var pixels : hxd.Pixels;
+	var channels : Array<{ r : hxd.res.Image, c : hxd.Pixels.Channel }> = [];
 	public var allowAsync : Bool = true;
 
 	public function new(w, h, ?flags : Array<TextureFlags>, ?format : TextureFormat, ?allocPos : h3d.impl.AllocPos ) {
@@ -22,9 +23,14 @@ class TextureChannels extends Texture {
 		@:privateAccess if( t != null ) mem.deleteTexture(this);
 	}
 
-	@:noDebug
 	function setPixels( c : hxd.Pixels.Channel, src : hxd.Pixels, srcChannel : hxd.Pixels.Channel ) {
 		reset();
+		channels[c.toInt()] = { r : null, c : srcChannel };
+		setPixelsInner(c, src, srcChannel);
+	}
+
+	@:noDebug
+	function setPixelsInner( c : hxd.Pixels.Channel, src : hxd.Pixels, srcChannel : hxd.Pixels.Channel ) {
 		if( src.width != width || src.height != height )
 			throw "Size mismatch : " + src.width + "x" + src.height + " should be " + width + "x" + height;
 		var bpp = hxd.Pixels.bytesPerPixel(pixels.format);
@@ -45,15 +51,16 @@ class TextureChannels extends Texture {
 	public function setResource( c : hxd.Pixels.Channel, res : hxd.res.Image, ?srcChannel : hxd.Pixels.Channel ) {
 		if( srcChannel == null ) srcChannel = c;
 		if( !allowAsync || !res.getFormat().useAsyncDecode )
-			setPixels(c, res.getPixels(), srcChannel);
+			setPixelsInner(c, res.getPixels(), srcChannel);
 		else {
 			res.entry.loadBitmap(function(bmp) {
 				var bmp = bmp.toBitmap();
 				var pix = bmp.getPixels();
 				bmp.dispose();
-				setPixels(c, pix, srcChannel);
+				setPixelsInner(c, pix, srcChannel);
 			});
 		}
+		channels[c.toInt()] = { r : res, c : srcChannel };
 		res.watch(function() if( t != null ) setResource(c, res, srcChannel));
 	}
 

+ 32 - 1
h3d/parts/GpuParticles.hx

@@ -512,7 +512,8 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 	}
 
 	function set_seed(s) {
-		for( g in groups ) g.needRebuild = true;
+		if( groups != null )
+			for( g in groups ) g.needRebuild = true;
 		return seed = s;
 	}
 
@@ -889,4 +890,34 @@ class GpuParticles extends h3d.scene.MultiMaterial {
 		}
 	}
 
+	#if hxbit
+	override function serialize( ctx : hxbit.Serializer ) {
+		var old = primitive;
+		var oldMat = materials;
+		primitive = null;
+		materials = [];
+		super.serialize(ctx);
+		primitive = old;
+		materials = oldMat;
+	}
+	override function customSerialize(ctx:hxbit.Serializer) {
+		super.customSerialize(ctx);
+		ctx.addString(resourcePath);
+		ctx.addFloat(amount);
+		if( resourcePath == null )
+			ctx.addDynamic(save());
+
+	}
+	override function customUnserialize(ctx:hxbit.Serializer) {
+		super.customUnserialize(ctx);
+		resourcePath = ctx.getString();
+		amount = ctx.getFloat();
+		groups = [];
+		if( resourcePath != null )
+			load(haxe.Json.parse(hxd.res.Loader.currentInstance.load(resourcePath).toText()), resourcePath);
+		else
+			load(ctx.getDynamic());
+	}
+	#end
+
 }

+ 61 - 0
h3d/prim/BigPrimitive.hx

@@ -283,4 +283,65 @@ class BigPrimitive extends Primitive {
 			tmpIdx[idxPos++] = idx[i+startTri*3] + start;
 	}
 
+	#if hxbit
+	override function customSerialize(ctx:hxbit.Serializer) {
+		flush();
+		ctx.addBool(isRaw);
+		ctx.addInt(stride);
+		ctx.addFloat(bounds.xMin);
+		ctx.addFloat(bounds.yMin);
+		ctx.addFloat(bounds.zMin);
+		ctx.addFloat(bounds.xMax);
+		ctx.addFloat(bounds.yMax);
+		ctx.addFloat(bounds.zMax);
+		ctx.addInt(buffers.length);
+		var reqSize = 0;
+		for( a in allIndexes ) {
+			var sz = a.count << 1;
+			if( reqSize < sz ) reqSize = sz;
+		}
+		for( b in buffers ) {
+			var sz = (b.vertices * stride) << 2;
+			if( reqSize < sz ) reqSize = sz;
+		}
+		var tmpBytes = haxe.io.Bytes.alloc(reqSize);
+		for( i in 0...buffers.length ) {
+			var idx = allIndexes[i];
+			idx.readBytes(tmpBytes, 0, idx.count);
+			ctx.addInt(idx.count);
+			ctx.addBytesSub(tmpBytes, 0, idx.count << 1);
+
+			var b = buffers[i];
+			b.readBytes(tmpBytes, 0, b.vertices);
+			ctx.addInt(b.vertices);
+			ctx.addBytesSub(tmpBytes, 0, b.vertices * stride << 2);
+		}
+	}
+	override function customUnserialize(ctx:hxbit.Serializer) {
+		isRaw = ctx.getBool();
+		stride = ctx.getInt();
+		bounds = new h3d.col.Bounds();
+		bounds.xMin = ctx.getFloat();
+		bounds.yMin = ctx.getFloat();
+		bounds.zMin = ctx.getFloat();
+		bounds.xMin = ctx.getFloat();
+		bounds.yMax = ctx.getFloat();
+		bounds.zMax = ctx.getFloat();
+		var count = ctx.getInt();
+		buffers = [];
+		allIndexes = [];
+		for( i in 0...count ) {
+			var nidx = ctx.getInt();
+			var idx = new h3d.Indexes(nidx);
+			idx.uploadBytes(ctx.getBytes(), 0, nidx);
+			allIndexes.push(idx);
+
+			var nvert = ctx.getInt();
+			var buf = new h3d.Buffer(nvert, stride);
+			buf.uploadBytes(ctx.getBytes(), 0, nvert);
+			buffers.push(buf);
+		}
+	}
+	#end
+
 }

+ 10 - 0
h3d/scene/Scene.hx

@@ -375,6 +375,16 @@ class Scene extends Object implements h3d.IDrawable implements hxd.SceneEvents.I
 		ctx.engine = null;
 	}
 
+
+	public function serializeScene() : haxe.io.Bytes {
+		#if hxbit		
+		var s = new h3d.impl.Serializable.SceneSerializer();
+		return s.saveSCN(this, false);
+		#else
+		throw "You need -lib hxbit to serialize the scene data";
+		#end
+	}
+
 	#if hxbit
 	override function customSerialize(ctx:hxbit.Serializer) {
 		throw this + " should not be serialized";

+ 1 - 0
h3d/scene/World.hx

@@ -364,6 +364,7 @@ class World extends Object {
 				if( b == null ) {
 					b = new h3d.scene.Mesh(new h3d.prim.BigPrimitive(getStride(model), true), c.root);
 					b.name = g.m.name;
+					b.flags.set(FNoSerialize, true);
 					c.buffers.set(g.m.bits, b);
 					initMaterial(b, g.m);
 				}

+ 33 - 8
hxd/impl/BitsBuilder.hx

@@ -7,7 +7,7 @@ class BitsBuilder {
 	public static function build() {
 		var fields = Context.getBuildFields();
 		var pos = Context.currentPos();
-		var offsetMap = new Map<String, Int>();
+		var bitsMap = new Map<String, { field : String, current : Int, fields : Array<haxe.macro.Expr> }>();
 		for( f in fields.copy() ) {
 			var bits = 0;
 			var field : String = null;
@@ -16,7 +16,7 @@ class BitsBuilder {
 					if( m2.params.length < 1 )
 						Context.error("Please specify the bits field", f.pos);
 
-					bits  = -1;
+					bits = -1; // auto
 					field = switch( m2.params[0] ) {
 						case { expr : EConst(CIdent(v)) } : v;
 						default : null;
@@ -34,9 +34,13 @@ class BitsBuilder {
 
 			if( bits == 0 ) continue;
 
-			var offset = offsetMap.get(field);
-			if( offset == null ) offset = 0;
+			var inf = bitsMap.get(field);
+			if( inf == null ) {
+				inf = { field : field, current : 0, fields : [] };
+				bitsMap.set(field, inf);
+			}
 
+			var offset = inf.current;
 			switch( f.kind ) {
 			case FVar(vt = TPath( { pack : pack, name : name }), init):
 				f.kind = FProp("default", "set", vt, init);
@@ -44,15 +48,18 @@ class BitsBuilder {
 				var path = pack.copy();
 				path.push(name);
 				var t = try Context.getType(path.join(".")) catch( e : Dynamic ) continue;
+				var fget = null;
 				var expr = switch( t ) {
 				case TAbstract(a, _):
 					var t = Std.string(a);
 					switch( t ) {
 					case "Bool":
 						if( bits < 0 ) bits = 1;
+						fget = function(v) return macro ($v != 0);
 						macro (v ? 1 : 0);
 					case "Int":
 						if( bits < 0 ) Context.error("Please specify bit count", f.pos);
+						fget = function(v) return v;
 						macro (v & $v{ (1<<bits) - 1 });
 					default:
 						null;
@@ -62,6 +69,7 @@ class BitsBuilder {
 						var count = e.get().names.length;
 						while( count > 1 << bits ) bits++;
 					}
+					fget = function(v) return macro Type.createEnumIndex($p{path},$v);
 					macro Type.enumIndex(v);
 				default:
 					null;
@@ -81,6 +89,9 @@ class BitsBuilder {
 					pos : f.pos,
 				});
 
+				var v = fget(macro (this.$field >> $v{offset}) & $v{mask});
+				inf.fields.push(macro this.$name = $v);
+
 				fields.push({
 					name : "get" + f.name.charAt(0).toUpperCase() + f.name.substr(1),
 					access : [AStatic,APublic,AInline],
@@ -109,12 +120,26 @@ class BitsBuilder {
 					kind : FVar(null, macro $v{ mask << offset } ),
 					pos : pos,
 				});
-				offset += bits;
 			default:
 			}
-			if( offset > 32 )
-				Context.error(offset + " bits were used while maximum was 32", pos);
-			offsetMap.set(field, offset);
+
+			inf.current += bits;
+			if( inf.current > 32 )
+				Context.error(inf.current + " bits were used while maximum was 32", pos);
+		}
+
+		for( f in bitsMap ) {
+			var field = f.field;
+			fields.push({
+				name : "load" + f.field.charAt(0).toUpperCase() + f.field.substr(1),
+				access : [],
+				pos : pos,
+				kind : FFun({
+					args : [{ name : "bits", type : macro : Int }],
+					ret : null,
+					expr : macro { this.$field = bits; {$a{f.fields}} },
+				}),
+			});
 		}
 
 		return fields;

+ 1 - 1
hxd/inspect/Inspector.hx

@@ -160,7 +160,7 @@ class Inspector {
 		function load( mode = "rgba" ) {
 			p.j.html("Loading...");
 			haxe.Timer.delay(function() {
-				var bmp = t.capturePixels(true);
+				var bmp = t.capturePixels();
 				function setChannel(v) {
 					var bits = v * 8;
 					for( x in 0...bmp.width )