Przeglądaj źródła

added SCN serialization support (requires -lib hxbit)

Nicolas Cannasse 8 lat temu
rodzic
commit
32ac3cfdd5

+ 6 - 0
h3d/impl/MacroHelper.hx

@@ -28,4 +28,10 @@ class MacroHelper {
 
 #end
 
+	public static macro function getResourcesPath() {
+		var dir = haxe.macro.Context.definedValue("resourcesPath");
+		if( dir == null ) dir = "res";
+		return macro $v{try Context.resolvePath(dir) catch( e : Dynamic ) null};
+	}
+
 }

+ 204 - 0
h3d/impl/Serializable.hx

@@ -0,0 +1,204 @@
+package h3d.impl;
+
+#if !hxbit
+private interface EmptyInterface {}
+#end
+
+typedef Serializable = #if hxbit hxbit.Serializable #else EmptyInterface #end;
+
+#if hxbit
+class SceneSerializer extends hxbit.Serializer {
+
+	public var materialRef = new Map<String,h3d.mat.Material>();
+
+	var version = 0;
+
+	var resPath : String = MacroHelper.getResourcesPath();
+	var shaderVarIndex : Int;
+	var shaderUID = 0;
+	var shaderIndexes = new Map<hxsl.Shader,Int>();
+	var cachedShaders = new Array<hxsl.Shader>();
+
+	function addTexture( t : h3d.mat.Texture ) {
+		if( t == null ) {
+			addInt(0);
+			return true;
+		}
+		if( t.name != null ) {
+			addInt(1);
+			addString(t.name);
+			return true;
+		}
+		return false;
+	}
+
+	function getTexture() {
+		switch( getInt() ) {
+		case 0:
+			return null;
+		case 1:
+			return resolveTexture(getString());
+		default:
+			throw "assert";
+		}
+	}
+
+	function resolveTexture( path : String ) {
+		return hxd.res.Loader.currentInstance.load(path).toTexture();
+	}
+
+	public function loadHMD( path : String ) {
+		return hxd.res.Loader.currentInstance.load(path).toHmd();
+	}
+
+	public function addShader( s : hxsl.Shader ) {
+		if( s == null ) {
+			addInt(0);
+			return;
+		}
+		var id = shaderIndexes.get(s);
+		if( id != null ) {
+			addInt(id);
+			return;
+		}
+		id = ++shaderUID;
+		shaderIndexes.set(s, id);
+		addInt(id);
+		addString(Type.getClassName(Type.getClass(s)));
+		shaderVarIndex = 0;
+		for( v in @:privateAccess s.shader.data.vars )
+			addShaderVar(v, s);
+	}
+
+	public function getShader() {
+		var id = getInt();
+		if( id == 0 )
+			return null;
+		var s = cachedShaders[id];
+		if( s != null )
+			return s;
+		var sname = getString();
+		var cl : Class<hxsl.Shader> = cast Type.resolveClass(sname);
+		if( cl == null ) throw "Missing shader " + sname;
+		s = Type.createEmptyInstance(cl);
+		@:privateAccess s.initialize();
+		for( v in @:privateAccess s.shader.data.vars ) {
+			if( !canSerializeVar(v) ) continue;
+			var val : Dynamic = getShaderVar(v, s);
+			Reflect.setField(s, v.name+"__", val);
+		}
+		cachedShaders[id] = s;
+		return s;
+	}
+
+	function canSerializeVar( v : hxsl.Ast.TVar ) {
+		return v.kind == Param && (v.qualifiers == null || v.qualifiers.indexOf(Ignore) < 0);
+	}
+
+	function addShaderVar( v : hxsl.Ast.TVar, s : hxsl.Shader ) {
+		if( !canSerializeVar(v) )
+			return;
+		switch( v.type ) {
+		case TStruct(vl):
+			for( v in vl )
+				addShaderVar(v, s);
+			return;
+		default:
+		}
+		var val : Dynamic = s.getParamValue(shaderVarIndex++);
+		switch( v.type ) {
+		case TBool:
+			addBool(val);
+		case TInt:
+			addInt(val);
+		case TFloat:
+			addFloat(val);
+		case TVec(n, VFloat):
+			var v : h3d.Vector = val;
+			addFloat(v.x);
+			addFloat(v.y);
+			if( n >= 3 ) addFloat(v.z);
+			if( n >= 4 ) addFloat(v.w);
+		case TSampler2D:
+			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);
+		}
+	}
+
+	function getShaderVar( v : hxsl.Ast.TVar, s : hxsl.Shader ) : Dynamic {
+		switch( v.type ) {
+		case TStruct(vl):
+			var obj = {};
+			for( v in vl ) {
+				if( !canSerializeVar(v) ) continue;
+				Reflect.setField(obj, v.name, getShaderVar(v, s));
+			}
+			return obj;
+		default:
+		}
+		switch( v.type ) {
+		case TBool:
+			return getBool();
+		case TFloat:
+			return getFloat();
+		case TInt:
+			return getInt();
+		case TVec(n, VFloat):
+			var v = new h3d.Vector(getFloat(), getFloat());
+			if( n >= 3 ) v.z = getFloat();
+			if( n >= 4 ) v.w = getFloat();
+			return v;
+		case TSampler2D:
+			return getTexture();
+		default:
+			throw "Cannot unserialize macro var " + v.name+":"+hxsl.Ast.Tools.toString(v.type);
+		}
+	}
+
+	function initSCNPaths( resPath : String, projectPath : String ) {
+		this.resPath = resPath;
+	}
+
+	public function loadSCN( bytes ) {
+		setInput(bytes, 0);
+		if( getString() != "SCN" )
+			throw "Invalid SCN file";
+		version = getInt();
+		beginLoad(bytes, inPos);
+		initSCNPaths(getString(), getString());
+		var objs = [];
+		for( i in 0...getInt() ) {
+			var obj : h3d.scene.Object = cast getAnyRef();
+			objs.push(obj);
+		}
+		endLoad();
+		return { content : objs };
+	}
+
+	public function saveSCN( obj : h3d.scene.Object, includeRoot : Bool ) {
+		begin();
+		addString("SCN");
+		addInt(version); // version
+
+		var pos = out.length;
+		usedClasses = [];
+		addString(resPath);
+		#if sys
+		addString(Sys.getCwd());
+		#else
+		addString(null);
+		#end
+
+		var objs = includeRoot ? [obj] : [for( o in obj ) o];
+		objs = [for( o in obj ) if( @:privateAccess !o.flags.has(FNoSerialize) ) o];
+		addInt(objs.length);
+		for( o in objs )
+			addAnyRef(o);
+
+		return endSave(pos);
+	}
+
+}
+#end

+ 3 - 3
h3d/mat/BaseMaterial.hx

@@ -2,10 +2,10 @@ package h3d.mat;
 import h3d.mat.Data;
 import h3d.mat.Pass;
 
-class BaseMaterial {
+class BaseMaterial implements h3d.impl.Serializable {
 
-	var passes : Pass;
-	public var name : String;
+	@:s var passes : Pass;
+	@:s public var name : String;
 	public var mainPass(get, never) : Pass;
 
 	public var props(default,set) : MaterialProps;

+ 10 - 0
h3d/mat/Material.hx

@@ -166,4 +166,14 @@ class Material extends BaseMaterial {
 		return t;
 	}
 
+	#if hxbit
+	function customSerialize( ctx : hxbit.Serializer ) {
+	}
+	function customUnserialize( ctx : hxbit.Serializer ) {
+		var last = mainPass.shaders;
+		while( last.next != null ) last = last.next;
+		mshader = cast last.s;
+	}
+	#end
+
 }

+ 38 - 8
h3d/mat/Pass.hx

@@ -3,22 +3,22 @@ import h3d.mat.Data;
 
 @:allow(h3d.mat.BaseMaterial)
 @:build(hxd.impl.BitsBuilder.build())
-class Pass {
+class Pass implements h3d.impl.Serializable {
 
-	public var name(default, null) : String;
+	@:s public var name(default, null) : String;
 	var passId : Int;
-	var bits : Int = 0;
-	var parentPass : Pass;
+	@:s var bits : Int = 0;
+	@:s var parentPass : Pass;
 	var parentShaders : hxsl.ShaderList;
 	var shaders : hxsl.ShaderList;
-	var nextPass : Pass;
+	@:s var nextPass : Pass;
 
-	public var enableLights : Bool;
+	@:s public var enableLights : Bool;
 	/**
 		Inform the pass system that the parameters will be modified in object draw() command,
 		so they will be manually uploaded by calling RenderContext.uploadParams.
 	**/
-	public var dynamicParameters : Bool;
+	@:s public var dynamicParameters : Bool;
 
 	@:bits(bits) public var culling : Face;
 	@:bits(bits) public var depthWrite : Bool;
@@ -31,7 +31,7 @@ class Pass {
 	@:bits(bits) public var blendAlphaOp : Operation;
 	@:bits(bits, 4) public var colorMask : Int;
 
-	public var stencil : Stencil;
+	@:s public var stencil : Stencil;
 
 	public function new(name, ?shaders, ?parent) {
 		this.parentPass = parent;
@@ -188,4 +188,34 @@ class Pass {
 		return "VERTEX=\n" + toString(shader.vertex.data) + "\n\nFRAGMENT=\n" + toString(shader.fragment.data);
 	}
 
+	#if hxbit
+
+	public function customSerialize( ctx : hxbit.Serializer ) {
+		var ctx : h3d.impl.Serializable.SceneSerializer = cast ctx;
+		var s = shaders;
+		while( s != parentShaders ) {
+			ctx.addShader(s.s);
+			s = s.next;
+		}
+		ctx.addShader(null);
+	}
+	public function customUnserialize( ctx : hxbit.Serializer ) {
+		var ctx : h3d.impl.Serializable.SceneSerializer = cast ctx;
+		var head = null;
+		while( true ) {
+			var s = ctx.getShader();
+			if( s == null ) break;
+			var sl = new hxsl.ShaderList(s);
+			if( head == null ) {
+				head = shaders = sl;
+			} else {
+				head.next = sl;
+				head = sl;
+			}
+		}
+		setPassName(name);
+		//loadBits(bits);
+	}
+	#end
+
 }

+ 12 - 2
h3d/mat/Stencil.hx

@@ -3,8 +3,8 @@ import h3d.mat.Data;
 
 @:allow(h3d.mat.Material)
 @:build(hxd.impl.BitsBuilder.build())
-class Stencil
-{
+class Stencil implements h3d.impl.Serializable {
+
 	var frontRefBits : Int = 0;
 	var backRefBits  : Int = 0;
 	var opBits       : Int = 0;
@@ -98,4 +98,14 @@ class Stencil
 		opBits = s.opBits;
 	}
 
+	#if hxbit
+	public function customSerialize( ctx : hxbit.Serializer ) {
+	}
+	public function customUnserialize( ctx : hxbit.Serializer ) {
+		//loadFrontRefBits(frontRefBits);
+		//loadBackRefBits(backRefBits);
+		//loadOpBits(opBits);
+	}
+	#end
+
 }

+ 3 - 3
h3d/prim/Cube.hx

@@ -3,9 +3,9 @@ import h3d.col.Point;
 
 class Cube extends Polygon {
 
-	var sizeX : Float;
-	var sizeY : Float;
-	var sizeZ : Float;
+	@:s var sizeX : Float;
+	@:s var sizeY : Float;
+	@:s var sizeZ : Float;
 
 	public function new( x = 1., y = 1., z = 1. )
 	{

+ 24 - 0
h3d/prim/HMDModel.hx

@@ -159,4 +159,28 @@ class HMDModel extends MeshPrimitive {
 		return collider;
 	}
 
+	#if hxbit
+	override function customSerialize(ctx:hxbit.Serializer) {
+		ctx.addString(@:privateAccess lib.entry.path);
+		for( m in lib.header.models )
+			if( lib.header.geometries[m.geometry] == this.data ) {
+				ctx.addString(m.name);
+				break;
+			}
+	}
+	override function customUnserialize(ctx:hxbit.Serializer) {
+		var libPath = ctx.getString();
+		var modelPath = ctx.getString();
+		var ctx : h3d.impl.Serializable.SceneSerializer = cast ctx;
+		lib = ctx.loadHMD(libPath);
+		for( m in lib.header.models )
+			if( m.name == modelPath ) {
+				this.data = lib.header.geometries[m.geometry];
+				@:privateAccess lib.cachedPrimitives[m.geometry] = this;
+				break;
+			}
+		dataPosition = lib.header.dataPosition;
+	}
+	#end
+
 }

+ 68 - 4
h3d/prim/Polygon.hx

@@ -8,10 +8,10 @@ class Polygon extends Primitive {
 	public var uvs : Array<UV>;
 	public var idx : hxd.IndexBuffer;
 	public var colors : Array<Point>;
-	var scaled = 1.;
-	var translatedX = 0.;
-	var translatedY = 0.;
-	var translatedZ = 0.;
+	@:s var scaled = 1.;
+	@:s var translatedX = 0.;
+	@:s var translatedY = 0.;
+	@:s var translatedZ = 0.;
 
 	public function new( points, ?idx ) {
 		this.points = points;
@@ -194,4 +194,68 @@ class Polygon extends Primitive {
 		return poly;
 	}
 
+	#if hxbit
+	override function customSerialize(ctx:hxbit.Serializer) {
+		ctx.addInt(points.length);
+		for( p in points ) {
+			ctx.addDouble(p.x);
+			ctx.addDouble(p.y);
+			ctx.addDouble(p.z);
+		}
+		if( normals == null )
+			ctx.addInt(0);
+		else {
+			ctx.addInt(normals.length);
+			for( p in normals ) {
+				ctx.addDouble(p.x);
+				ctx.addDouble(p.y);
+				ctx.addDouble(p.z);
+			}
+		}
+		if( uvs == null )
+			ctx.addInt(0);
+		else {
+			ctx.addInt(uvs.length);
+			for( uv in uvs ) {
+				ctx.addDouble(uv.u);
+				ctx.addDouble(uv.v);
+			}
+		}
+		if( idx == null )
+			ctx.addInt(0);
+		else {
+			ctx.addInt(idx.length);
+			for( i in idx )
+				ctx.addInt(i);
+		}
+		if( colors == null )
+			ctx.addInt(0);
+		else {
+			ctx.addInt(colors.length);
+			for( c in colors ) {
+				ctx.addDouble(c.x);
+				ctx.addDouble(c.y);
+				ctx.addDouble(c.z);
+			}
+		}
+	}
+
+	override function customUnserialize(ctx:hxbit.Serializer) {
+		points = [for( i in 0...ctx.getInt() ) new h3d.col.Point(ctx.getDouble(), ctx.getDouble(), ctx.getDouble())];
+		normals = [for( i in 0...ctx.getInt() ) new h3d.col.Point(ctx.getDouble(), ctx.getDouble(), ctx.getDouble())];
+		uvs = [for( i in 0...ctx.getInt() ) new UV(ctx.getDouble(), ctx.getDouble())];
+		if( normals.length == 0 ) normals = null;
+		if( uvs.length == 0 ) uvs = null;
+		var nindex = ctx.getInt();
+		if( nindex > 0 ) {
+			idx = new hxd.IndexBuffer();
+			idx.grow(nindex);
+			for( i in 0...nindex )
+				idx[i] = ctx.getInt();
+		}
+		colors = [for( i in 0...ctx.getInt() ) new h3d.col.Point(ctx.getDouble(), ctx.getDouble(), ctx.getDouble())];
+		if( colors.length == 0 ) colors = null;
+	}
+	#end
+
 }

+ 14 - 1
h3d/prim/Primitive.hx

@@ -1,6 +1,6 @@
 package h3d.prim;
 
-class Primitive {
+class Primitive implements h3d.impl.Serializable {
 
 	public var buffer : Buffer;
 	public var indexes : Indexes;
@@ -57,4 +57,17 @@ class Primitive {
 		}
 	}
 
+	public function toString() {
+		return Type.getClassName(Type.getClass(this)).split(".").pop();
+	}
+
+	#if hxbit
+	function customSerialize( ctx : hxbit.Serializer ) {
+		throw "Cannot serialize " + toString();
+	}
+	function customUnserialize( ctx : hxbit.Serializer ) {
+		throw "customUnserialize not implemented on " + toString();
+	}
+	#end
+
 }

+ 3 - 4
h3d/prim/Sphere.hx

@@ -3,9 +3,9 @@ import h3d.col.Point;
 
 class Sphere extends Polygon {
 
-	var ray : Float;
-	var segsH : Int;
-	var segsW : Int;
+	@:s var ray : Float;
+	@:s var segsH : Int;
+	@:s var segsW : Int;
 
 	public function new( ray = 1., segsW = 8, segsH = 6 ) {
 		this.ray = ray;
@@ -66,4 +66,3 @@ class Sphere extends Polygon {
 	}
 
 }
-

+ 1 - 0
h3d/scene/CameraController.hx

@@ -28,6 +28,7 @@ class CameraController extends h3d.scene.Object {
 	public function new(?distance,?parent) {
 		super(parent);
 		set(distance);
+		flags.set(FNoSerialize,true);
 		toTarget();
 	}
 

+ 14 - 0
h3d/scene/DirLight.hx

@@ -30,4 +30,18 @@ class DirLight extends Light {
 		super.emit(ctx);
 	}
 
+	#if hxbit
+	override function customSerialize(ctx:hxbit.Serializer) {
+		super.customSerialize(ctx);
+		ctx.addDouble(direction.x);
+		ctx.addDouble(direction.y);
+		ctx.addDouble(direction.z);
+	}
+	override function customUnserialize(ctx:hxbit.Serializer) {
+		shader = dshader = new h3d.shader.DirLight();
+		super.customUnserialize(ctx);
+		direction = new h3d.Vector(ctx.getDouble(), ctx.getDouble(), ctx.getDouble());
+	}
+	#end
+
 }

+ 19 - 2
h3d/scene/Light.hx

@@ -4,11 +4,11 @@ class Light extends Object {
 
 	var shader : hxsl.Shader;
 	var objectDistance : Float; // used internaly
-	var cullingDistance : Float = 1e10;
+	@:s var cullingDistance : Float = 1e10;
 	@:noCompletion public var next : Light;
 
+	@:s public var priority : Int = 0;
 	public var color(get, never) : h3d.Vector;
-	public var priority : Int = 0;
 	public var enableSpecular(get, set) : Bool;
 
 	function new(shader,?parent) {
@@ -34,4 +34,21 @@ class Light extends Object {
 		ctx.emitLight(this);
 	}
 
+	#if hxbit
+	override function customSerialize(ctx:hxbit.Serializer) {
+		super.customSerialize(ctx);
+		ctx.addDouble(color.x);
+		ctx.addDouble(color.y);
+		ctx.addDouble(color.z);
+		ctx.addDouble(color.w);
+		ctx.addBool(enableSpecular);
+	}
+	override function customUnserialize(ctx:hxbit.Serializer) {
+		super.customUnserialize(ctx);
+		color.set(ctx.getDouble(), ctx.getDouble(), ctx.getDouble(), ctx.getDouble());
+		enableSpecular = ctx.getBool();
+	}
+	#end
+
+
 }

+ 14 - 0
h3d/scene/Mesh.hx

@@ -58,4 +58,18 @@ class Mesh extends Object {
 		if( primitive != null ) primitive.dispose();
 		super.dispose();
 	}
+
+	#if hxbit
+	override function customSerialize(ctx:hxbit.Serializer) {
+		super.customSerialize(ctx);
+		ctx.addKnownRef(primitive);
+		ctx.addKnownRef(material);
+	}
+	override function customUnserialize(ctx:hxbit.Serializer) {
+		super.customUnserialize(ctx);
+		primitive = ctx.getKnownRef(h3d.prim.Primitive);
+		material = ctx.getKnownRef(h3d.mat.Material);
+	}
+	#end
+
 }

+ 14 - 1
h3d/scene/MultiMaterial.hx

@@ -37,7 +37,7 @@ class MultiMaterial extends Mesh {
 		for( m in materials )
 			if( m != null )
 				a.push(m);
-		for( o in childs )
+		for( o in children )
 			o.getMaterials(a);
 		return a;
 	}
@@ -48,4 +48,17 @@ class MultiMaterial extends Mesh {
 		super.draw(ctx);
 	}
 
+	#if hxbit
+	override function customSerialize(ctx:hxbit.Serializer) {
+		super.customSerialize(ctx);
+		ctx.addInt(materials.length);
+		for( m in materials ) ctx.addKnownRef(m);
+	}
+
+	override function customUnserialize(ctx:hxbit.Serializer) {
+		super.customUnserialize(ctx);
+		materials = [for( i in 0...ctx.getInt() ) ctx.getKnownRef(h3d.mat.Material)];
+	}
+	#end
+
 }

+ 71 - 42
h3d/scene/Object.hx

@@ -9,6 +9,7 @@ package h3d.scene;
 	public var FAllocated = 32;
 	public var FAlwaysSync = 64;
 	public var FInheritCulled = 128;
+	public var FNoSerialize = 256;
 	public inline function new() {
 		this = 0;
 	}
@@ -20,29 +21,29 @@ package h3d.scene;
 	}
 }
 
-class Object {
+class Object implements h3d.impl.Serializable {
 
 	static inline var ROT2RAD = -0.017453292519943295769236907684886;
 
-	var flags : ObjectFlags;
-	var childs : Array<Object>;
+	@:s var flags : ObjectFlags;
+	var children : Array<Object>;
 	public var parent(default, null) : Object;
 	public var numChildren(get, never) : Int;
 
-	public var name : Null<String>;
-	public var x(default,set) : Float;
-	public var y(default, set) : Float;
-	public var z(default, set) : Float;
-	public var scaleX(default,set) : Float;
-	public var scaleY(default, set) : Float;
-	public var scaleZ(default,set) : Float;
+	@:s public var name : Null<String>;
+	@:s public var x(default,set) : Float;
+	@:s public var y(default, set) : Float;
+	@:s public var z(default, set) : Float;
+	@:s public var scaleX(default,set) : Float;
+	@:s public var scaleY(default, set) : Float;
+	@:s public var scaleZ(default,set) : Float;
 	public var visible(get, set) : Bool;
 	var allocated(get,set) : Bool;
 
 	/**
 		Follow a given object or joint as if it was our parent. Ignore defaultTransform when set.
 	**/
-	public var follow(default, set) : Object;
+	@:s public var follow(default, set) : Object;
 	public var followPositionOnly(get, set) : Bool;
 
 	/**
@@ -88,7 +89,7 @@ class Object {
 		qRot = new h3d.Quat();
 		posChanged = false;
 		visible = true;
-		childs = [];
+		children = [];
 		if( parent != null )
 			parent.addChild(this);
 	}
@@ -145,19 +146,19 @@ class Object {
 			defaultTransform = null;
 		}
 		if( recursive )
-			for( c in childs )
+			for( c in children )
 				c.applyAnimationTransform();
 	}
 
 	public function getObjectsCount() {
 		var k = 0;
-		for( c in childs )
+		for( c in children )
 			k += c.getObjectsCount() + 1;
 		return k;
 	}
 
 	public function getMaterialByName( name : String ) : h3d.mat.Material {
-		for( o in childs ) {
+		for( o in children ) {
 			var m = o.getMaterialByName(name);
 			if( m != null ) return m;
 		}
@@ -166,7 +167,7 @@ class Object {
 
 	public function getMaterials( ?a : Array<h3d.mat.Material> ) {
 		if( a == null ) a = [];
-		for( o in childs )
+		for( o in children )
 			o.getMaterials(a);
 		return a;
 	}
@@ -212,12 +213,12 @@ class Object {
 		if( b == null )
 			b = new h3d.col.Bounds();
 		if( posChanged ) {
-			for( c in childs )
+			for( c in children )
 				c.posChanged = true;
 			posChanged = false;
 			calcAbsPos();
 		}
-		for( c in childs )
+		for( c in children )
 			c.getBounds(b, true);
 		return b;
 	}
@@ -226,7 +227,7 @@ class Object {
 		if( out == null ) out = [];
 		var m = Std.instance(this, Mesh);
 		if( m != null ) out.push(m);
-		for( c in childs )
+		for( c in children )
 			c.getMeshes(out);
 		return out;
 	}
@@ -234,7 +235,7 @@ class Object {
 	public function getObjectByName( name : String ) {
 		if( this.name == name )
 			return this;
-		for( c in childs ) {
+		for( c in children ) {
 			var o = c.getObjectByName(name);
 			if( o != null ) return o;
 		}
@@ -258,21 +259,21 @@ class Object {
 		o.visible = visible;
 		if( defaultTransform != null )
 			o.defaultTransform = defaultTransform.clone();
-		for( c in childs ) {
+		for( c in children ) {
 			var c = c.clone();
 			c.parent = o;
-			o.childs.push(c);
+			o.children.push(c);
 		}
 		return o;
 	}
 
 	public function addChild( o : Object ) {
-		addChildAt(o, childs.length);
+		addChildAt(o, children.length);
 	}
 
 	public function addChildAt( o : Object, pos : Int ) {
 		if( pos < 0 ) pos = 0;
-		if( pos > childs.length ) pos = childs.length;
+		if( pos > children.length ) pos = children.length;
 		var p = this;
 		while( p != null ) {
 			if( p == o ) throw "Recursive addChild";
@@ -285,7 +286,7 @@ class Object {
 			o.parent.removeChild(o);
 			o.allocated = old;
 		}
-		childs.insert(pos, o);
+		children.insert(pos, o);
 		if( !allocated && o.allocated )
 			o.onRemove();
 		o.parent = this;
@@ -306,31 +307,31 @@ class Object {
 			var m = Std.instance(this, Mesh);
 			if( m != null ) callb(m);
 		}
-		for( o in childs )
+		for( o in children )
 			o.iterVisibleMeshes(callb);
 	}
 
 	function onParentChanged() {
-		for( c in childs )
+		for( c in children )
 			c.onParentChanged();
 	}
 
 	// kept for internal init
 	function onAdd() {
 		allocated = true;
-		for( c in childs )
+		for( c in children )
 			c.onAdd();
 	}
 
 	// kept for internal cleanup
 	function onRemove() {
 		allocated = false;
-		for( c in childs )
+		for( c in children )
 			c.onRemove();
 	}
 
 	public function removeChild( o : Object ) {
-		if( childs.remove(o) ) {
+		if( children.remove(o) ) {
 			if( o.allocated ) o.onRemove();
 			o.parent = null;
 			o.posChanged = true;
@@ -364,7 +365,7 @@ class Object {
 
 	public function getCollider() : h3d.col.Collider {
 		var colliders = [];
-		for( obj in childs ) {
+		for( obj in children ) {
 			var c = obj.getCollider();
 			var cgrp = Std.instance(c, h3d.col.Collider.GroupCollider);
 			if( cgrp != null ) {
@@ -448,9 +449,9 @@ class Object {
 		sync(ctx);
 		posChanged = false;
 		lastFrame = ctx.frame;
-		var p = 0, len = childs.length;
+		var p = 0, len = children.length;
 		while( p < len ) {
-			var c = childs[p];
+			var c = children[p];
 			if( c == null )
 				break;
 			if( c.lastFrame != ctx.frame ) {
@@ -459,9 +460,9 @@ class Object {
 			}
 			// if the object was removed, let's restart again.
 			// our lastFrame ensure that no object will get synched twice
-			if( childs[p] != c ) {
+			if( children[p] != c ) {
 				p = 0;
-				len = childs.length;
+				len = children.length;
 			} else
 				p++;
 		}
@@ -473,7 +474,7 @@ class Object {
 		if( posChanged ) {
 			posChanged = false;
 			calcAbsPos();
-			for( c in childs )
+			for( c in children )
 				c.posChanged = true;
 		}
 	}
@@ -491,12 +492,12 @@ class Object {
 			if( currentAnimation != null ) currentAnimation.sync();
 			posChanged = false;
 			calcAbsPos();
-			for( c in childs )
+			for( c in children )
 				c.posChanged = true;
 		}
 		if( !culled )
 			emit(ctx);
-		for( c in childs )
+		for( c in children )
 			c.emitRec(ctx);
 	}
 
@@ -597,20 +598,48 @@ class Object {
 	}
 
 	public inline function getChildAt( n ) {
-		return childs[n];
+		return children[n];
 	}
 
 	inline function get_numChildren() {
-		return childs.length;
+		return children.length;
 	}
 
 	public inline function iterator() : hxd.impl.ArrayIterator<Object> {
-		return new hxd.impl.ArrayIterator(childs);
+		return new hxd.impl.ArrayIterator(children);
 	}
 
 	public function dispose() {
-		for( c in childs )
+		for( c in children )
 			c.dispose();
 	}
 
+	#if hxbit
+	function customSerialize( ctx : hxbit.Serializer ) {
+
+		var children = [for( o in children ) if( !o.flags.has(FNoSerialize) ) o];
+		ctx.addInt(children.length);
+		for( o in children )
+			ctx.addKnownRef(o);
+
+		ctx.addDouble(qRot.x);
+		ctx.addDouble(qRot.y);
+		ctx.addDouble(qRot.z);
+		ctx.addDouble(qRot.w);
+		// defaultTransform
+		// currentAnimation
+	}
+
+	function customUnserialize( ctx : hxbit.Serializer ) {
+		children = [for( i in 0...ctx.getInt() ) ctx.getKnownRef(Object)];
+		qRot = new h3d.Quat(ctx.getDouble(),ctx.getDouble(),ctx.getDouble(),ctx.getDouble());
+		for( c in children )
+			c.parent = this;
+		allocated = false;
+		posChanged = true;
+		absPos = new h3d.Matrix();
+		absPos.identity();
+	}
+	#end
+
 }

+ 6 - 0
h3d/scene/Scene.hx

@@ -375,4 +375,10 @@ class Scene extends Object implements h3d.IDrawable implements hxd.SceneEvents.I
 		ctx.engine = null;
 	}
 
+	#if hxbit
+	override function customSerialize(ctx:hxbit.Serializer) {
+		throw this + " should not be serialized";
+	}
+	#end
+
 }

+ 40 - 16
h3d/scene/Skin.hx

@@ -126,25 +126,27 @@ class Skin extends MultiMaterial {
 		return skinData;
 	}
 
-	public function setSkinData( s ) {
+	public function setSkinData( s, shaderInit = true ) {
 		skinData = s;
 		jointsUpdated = true;
 		primitive = s.primitive;
-		skinShader = new h3d.shader.Skin();
-		var maxBones = 0;
-		if( skinData.splitJoints != null ) {
-			for( s in skinData.splitJoints )
-				if( s.joints.length > maxBones )
-					maxBones = s.joints.length;
-		} else
-			maxBones = skinData.boundJoints.length;
-		if( skinShader.MaxBones < maxBones )
-			skinShader.MaxBones = maxBones;
-		for( m in materials )
-			if( m != null ) {
-				m.mainPass.addShader(skinShader);
-				if( skinData.splitJoints != null ) m.mainPass.dynamicParameters = true;
-			}
+		if( shaderInit ) {
+			skinShader = new h3d.shader.Skin();
+			var maxBones = 0;
+			if( skinData.splitJoints != null ) {
+				for( s in skinData.splitJoints )
+					if( s.joints.length > maxBones )
+						maxBones = s.joints.length;
+			} else
+				maxBones = skinData.boundJoints.length;
+			if( skinShader.MaxBones < maxBones )
+				skinShader.MaxBones = maxBones;
+			for( m in materials )
+				if( m != null ) {
+					m.mainPass.addShader(skinShader);
+					if( skinData.splitJoints != null ) m.mainPass.dynamicParameters = true;
+				}
+		}
 		currentRelPose = [];
 		currentAbsPose = [];
 		currentPalette = [];
@@ -236,5 +238,27 @@ class Skin extends MultiMaterial {
 		}
 	}
 
+	#if hxbit
+	override function customUnserialize(ctx:hxbit.Serializer) {
+		super.customUnserialize(ctx);
+		var prim = Std.instance(primitive, h3d.prim.HMDModel);
+		if( prim == null ) throw "Cannot load skind primitive " + prim;
+		jointsUpdated = true;
+		skinShader = material.mainPass.getShader(h3d.shader.Skin);
+		@:privateAccess {
+			var lib = prim.lib;
+			for( m in lib.header.models )
+				if( lib.header.geometries[m.geometry] == prim.data ) {
+					var skinData = lib.makeSkin(m.skin);
+					skinData.primitive = prim;
+					setSkinData(skinData, false);
+					break;
+				}
+		}
+		for( p in material.getPasses() )
+			trace(p.name+":"+[for( s in p.getShaders() ) s.toString()]);
+	}
+	#end
+
 
 }

+ 1 - 1
h3d/shader/Skin.hx

@@ -16,7 +16,7 @@ class Skin extends hxsl.Shader {
 		var transformedNormal : Vec3;
 
 		@const var MaxBones : Int;
-		@param var bonesMatrixes : Array<Mat3x4,MaxBones>;
+		@ignore @param var bonesMatrixes : Array<Mat3x4,MaxBones>;
 
 		function vertex() {
 			#if floatSkinIndexes

+ 0 - 18
hxd/App.hx

@@ -110,24 +110,6 @@ class App implements h3d.IDrawable {
 		if( isDisposed ) return;
 		s2d.setElapsedTime(Timer.tmod/60);
 		s3d.setElapsedTime(Timer.tmod / 60);
-		#if debug
-		if( hxd.Key.isDown(hxd.Key.CTRL) && hxd.Key.isPressed(hxd.Key.F12) ) {
-			var driver = engine.driver;
-			var old = driver.logEnable;
-			var log = new h3d.impl.LogDriver(driver);
-			log.logLines = [];
-			engine.setDriver(log);
-			try {
-				engine.render(this);
-			} catch( e : Dynamic ) {
-				log.logLines.push(Std.string(e));
-			}
-			driver.logEnable = old;
-			engine.setDriver(driver);
-			hxd.File.saveBytes("log.txt", haxe.io.Bytes.ofString(log.logLines.join("\n")));
-			return;
-		}
-		#end
 		engine.render(this);
 	}
 

+ 1 - 1
hxd/inspect/ScenePanel.hx

@@ -38,7 +38,7 @@ private class SceneObject extends TreeNode {
 		var name = objectName(o);
 		if( o.parent == null )
 			return name;
-		var idx = Lambda.indexOf(@:privateAccess o.parent.childs, o);
+		var idx = Lambda.indexOf(@:privateAccess o.parent.children, o);
 		var count = 0;
 		for( i in 0...idx )
 			if( objectName(o.parent.getChildAt(i)) == name )

+ 8 - 0
hxsl/Shader.hx

@@ -11,6 +11,10 @@ class Shader {
 	var constModified : Bool;
 
 	public function new() {
+		initialize();
+	}
+
+	function initialize() {
 		var cl : Dynamic = std.Type.getClass(this);
 		shader = cl._SHADER;
 		constModified = true;
@@ -88,4 +92,8 @@ class Shader {
 		return this;
 	}
 
+	public function toString() {
+		return std.Type.getClassName(std.Type.getClass(this));
+	}
+
 }

+ 1 - 1
samples/Base3D.hx

@@ -1,6 +1,6 @@
 import h3d.scene.*;
 
-class Base3D extends hxd.App {
+class Base3D extends SampleApp {
 
 	var time : Float = 0.;
 	var obj1 : Mesh;

+ 4 - 0
samples/Generator.hx

@@ -43,6 +43,8 @@ class Generator {
 		try sys.FileSystem.createDirectory("build") catch( e : Dynamic ) {};
 		sys.io.File.saveContent("build/README.txt","This directory is automatically generated by samples/Script.hx using samples/templates");
 
+		var hasHxBit = Sys.command("haxelib path hxbit") == 0;
+			
 		for( f in sys.FileSystem.readDirectory(".") ) {
 
 			if( sys.FileSystem.isDirectory(f) || !StringTools.endsWith(f,".hx") )
@@ -56,6 +58,8 @@ class Generator {
 			var params = [];
 			if( sys.FileSystem.exists(name+"_res") )
 				params.push("-D resourcesPath=../../"+name+"_res");
+			if( hasHxBit )
+				params.push("-lib hxbit");
 
 			var content = sys.io.File.getContent(f);
 			~/\/\/PARAM=(.*)/g.map(content,function(r) { params.push(StringTools.trim(r.matched(1))); return ""; });

+ 10 - 0
samples/SampleApp.hx

@@ -9,6 +9,16 @@ class SampleApp extends hxd.App {
 		fui.padding = 10;
 	}
 
+	#if hxbit
+	override function mainLoop() {
+		if( hxd.Key.isDown(hxd.Key.CTRL) && hxd.Key.isPressed("S".code) ) {
+			var bytes = new h3d.impl.Serializable.SceneSerializer().saveSCN(s3d,false);
+			hxd.File.saveBytes("scene.scn", bytes);
+		}
+		super.mainLoop();
+	}
+	#end
+
 	function getFont() {
 		return hxd.res.DefaultFont.get();
 	}

+ 1 - 38
samples/Skin.hx

@@ -1,6 +1,6 @@
 import h3d.scene.*;
 
-class Skin extends hxd.App {
+class Skin extends SampleApp {
 
 	var cache : h3d.prim.ModelCache;
 
@@ -31,43 +31,6 @@ class Skin extends hxd.App {
 		dir.enableSpecular = true;
 
 		new h3d.scene.CameraController(s3d).loadFromCamera();
-
-		#if hxbit
-		// this is an example for connecting to scene inspector
-		// and enable extra properties
-		// this requires to compile with -lib hxbit and run http://castledb.org
-		var i = new hxd.inspect.Inspector(s3d);
-
-		var delta = s3d.camera.pos.sub(s3d.camera.target);
-		delta.z = 0;
-		var angle = Math.atan2(delta.y, delta.x);
-		var dist = delta.length();
-
-		// add node to scene graph
-		var n = i.scenePanel.addNode("Rotation", "repeat", function() {
-			return [
-				PFloat("v", function() return angle, function(v) {
-					angle = v;
-					s3d.camera.pos.x = Math.cos(angle) * dist;
-					s3d.camera.pos.y = Math.sin(angle) * dist;
-				}),
-				PCustom("", function() {
-					var j = i.J("<button>");
-					j.text("Click Me!");
-					j.click(function(_) {
-						var j = new hxd.inspect.Panel(null,"New Panel");
-						j.content.text("Nothing to see there.");
-						j.show();
-					});
-					return j;
-				})
-			];
-		});
-		i.scenePanel.addNode("Test", "", n);
-		i.addTool("Exit", "bomb", function() {
-			hxd.System.exit();
-		});
-		#end
 	}
 
 	static function main() {