|
@@ -1,25 +1,54 @@
|
|
|
package hrt.prefab;
|
|
|
|
|
|
-/**
|
|
|
- Prefab is an data-oriented tree container capable of creating instances of Heaps objects.
|
|
|
-**/
|
|
|
-@:keepSub @:build(hrt.impl.Macros.buildPrefab()) @:autoBuild(hrt.impl.Macros.buildPrefab())
|
|
|
+typedef PrefabField = {
|
|
|
+ var name : String;
|
|
|
+ var hasSetter : Bool;
|
|
|
+ var meta : PrefabMeta;
|
|
|
+ var defaultValue : Dynamic;
|
|
|
+}
|
|
|
+
|
|
|
+typedef PrefabMeta = {
|
|
|
+ var ?doc : String;
|
|
|
+ var ?range_min : Float;
|
|
|
+ var ?range_max : Float;
|
|
|
+ var ?range_step : Float;
|
|
|
+}
|
|
|
+
|
|
|
+typedef PrefabInfo = {
|
|
|
+ var prefabClass : Class<Prefab>;
|
|
|
+ var ?extension: String;
|
|
|
+ #if editor
|
|
|
+ var inf : hide.prefab.HideProps;
|
|
|
+ #end
|
|
|
+};
|
|
|
+
|
|
|
+@:access(Prefab)
|
|
|
+abstract ContextMake(ContextShared) from ContextShared to ContextShared {
|
|
|
+
|
|
|
+ @:from static function fromObject3D( parent : h3d.scene.Object ) : ContextMake {
|
|
|
+ return new ContextShared(parent, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ @:from static function fromObject2D( parent : h2d.Object ) : ContextMake {
|
|
|
+ return new ContextShared(parent, true);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@:allow(hide)
|
|
|
+@:keepSub
|
|
|
+@:autoBuild(hrt.prefab.Macros.buildPrefab())
|
|
|
+@:build(hrt.prefab.Macros.buildPrefab())
|
|
|
class Prefab {
|
|
|
|
|
|
/**
|
|
|
- The type of prefab, allows to identify which class it should be loaded with.
|
|
|
+ The registered type name for this prefab, used to identify a prefab when serializing
|
|
|
**/
|
|
|
- @:s public var type(default, null) : String;
|
|
|
+ public var type(get, never) : String;
|
|
|
|
|
|
/**
|
|
|
The name of the prefab in the tree view
|
|
|
**/
|
|
|
- @:s public var name : String;
|
|
|
-
|
|
|
- /**
|
|
|
- The parent of the prefab in the tree view
|
|
|
- **/
|
|
|
- public var parent(default, set) : Prefab;
|
|
|
+ @:s public var name : String = "";
|
|
|
|
|
|
/**
|
|
|
The associated source file (an image, a 3D model, etc.) if the prefab type needs it.
|
|
@@ -27,9 +56,9 @@ class Prefab {
|
|
|
@:s public var source : String;
|
|
|
|
|
|
/**
|
|
|
- The list of children prefab in the tree view
|
|
|
+ The parent of the prefab in the tree view
|
|
|
**/
|
|
|
- public var children(default, null) : Array<Prefab>;
|
|
|
+ public var children : Array<Prefab> = [];
|
|
|
|
|
|
/**
|
|
|
Tells if the prefab will create an instance when calling make() or be ignored. Also apply to this prefab children.
|
|
@@ -60,140 +89,157 @@ class Prefab {
|
|
|
/**
|
|
|
A storage for some extra properties
|
|
|
**/
|
|
|
- @:s public var props : Any;
|
|
|
+ @:s public var props : Any = null;
|
|
|
|
|
|
/**
|
|
|
- Creates a new prefab with the given parent.
|
|
|
+ The parent of the prefab in the tree view
|
|
|
**/
|
|
|
- public function new(?parent : Prefab) {
|
|
|
- this.parent = parent;
|
|
|
- children = [];
|
|
|
+ public var parent(default, set) : Prefab;
|
|
|
+
|
|
|
+ /**
|
|
|
+ Infos shared by all the prefabs in a given prefab hierarchy (but not by references)
|
|
|
+ **/
|
|
|
+ public var shared(default, null) : ContextShared;
|
|
|
+
|
|
|
+ // Public API
|
|
|
+
|
|
|
+ public function new(parent:Prefab, contextShared: ContextShared) {
|
|
|
+ initParentShared(parent, contextShared);
|
|
|
+ }
|
|
|
+
|
|
|
+ function initParentShared(parent:Prefab, contextShared: ContextShared) {
|
|
|
+ if (parent == null) {
|
|
|
+ shared = contextShared;
|
|
|
+ if (shared == null) {
|
|
|
+ shared = new ContextShared(false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ this.parent = parent;
|
|
|
+ }
|
|
|
+
|
|
|
+ function get_type() {
|
|
|
+ var thisClass = Type.getClass(this);
|
|
|
+ return getClassTypeName(thisClass);
|
|
|
}
|
|
|
|
|
|
function set_parent(p) {
|
|
|
- if( parent != null )
|
|
|
+ if( parent != null ) {
|
|
|
parent.children.remove(this);
|
|
|
+ }
|
|
|
parent = p;
|
|
|
- if( parent != null )
|
|
|
+ if( parent != null ) {
|
|
|
+ this.shared = parent.shared;
|
|
|
parent.children.push(this);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ setSharedRec(new ContextShared(false));
|
|
|
+ }
|
|
|
return p;
|
|
|
}
|
|
|
|
|
|
- #if editor
|
|
|
-
|
|
|
/**
|
|
|
- Allows to customize how the prefab object is edited within Hide
|
|
|
+ Instanciate this prefab. If `newContextShared` is given or if `this.shared.isInstance` is false,
|
|
|
+ this prefab is cloned and then the clone is instanciated and returned.
|
|
|
+ If `this.shared.isInstance` is true, this prefab is instanciated instead.
|
|
|
**/
|
|
|
- public function edit( ctx : hide.prefab.EditContext ) {
|
|
|
- }
|
|
|
+ public function make( ?sh:ContextMake ) : Prefab {
|
|
|
+ // -- There is generated code here to properly clone the prefab if sh is set. See hrt.prefab.Macros
|
|
|
+ makeInstance();
|
|
|
+ for (c in children)
|
|
|
+ makeChild(c);
|
|
|
+ postMakeInstance();
|
|
|
|
|
|
- /**
|
|
|
- Allows to customize how the prefab object is displayed / handled within Hide
|
|
|
- **/
|
|
|
- public function getHideProps() : hide.prefab.HideProps {
|
|
|
- return { icon : "question-circle", name : "Unknown" };
|
|
|
+ return this;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- Allows to customize how the prefab instance changes when selected/unselected within Hide.
|
|
|
- Selection of descendants is skipped if false is returned.
|
|
|
- **/
|
|
|
- public function setSelected( ctx : Context, b : Bool ) {
|
|
|
- return true;
|
|
|
+ function makeClone(?sh:ContextShared) : Prefab {
|
|
|
+ if( sh == null ) {
|
|
|
+ sh = new ContextShared(shared.currentPath);
|
|
|
+ }
|
|
|
+ if( !sh.isInstance ) throw "assert";
|
|
|
+ if( sh.currentPath == null ) sh.currentPath = shared.currentPath;
|
|
|
+ #if editor
|
|
|
+ sh.editor = this.shared.editor;
|
|
|
+ #end
|
|
|
+ return this.clone(sh).make(sh);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Allows the prefab to create an interactive so it can be selected in the scene.
|
|
|
+ Create a copy of the data this prefab and all of it's children (unless `withChildren` is `false`), without calling `make()` on them.
|
|
|
+ If `parent` is given, then `sh` will be set to `parent.shared`. If `parent` and `sh` is null, `sh` will be set to a new context shared will be created.
|
|
|
+ The `parent` and `sh` are then given to the clone constructor.
|
|
|
**/
|
|
|
- public function makeInteractive( ctx : Context ) : hxd.SceneEvents.Interactive {
|
|
|
- return null;
|
|
|
- }
|
|
|
+ public function clone(?parent:Prefab = null, ?sh: ContextShared = null, withChildren : Bool = true) : Prefab {
|
|
|
+ if (parent != null && sh != null && parent.shared != sh)
|
|
|
+ throw "Both parent and sh are set but shared don't match";
|
|
|
|
|
|
- #end
|
|
|
+ if (sh == null) {
|
|
|
+ if (parent != null) {
|
|
|
+ sh = parent.shared;
|
|
|
+ } else {
|
|
|
+ sh = new hrt.prefab.ContextShared(this.shared.currentPath, true);
|
|
|
+ #if editor
|
|
|
+ sh.editor = shared.editor;
|
|
|
+ #end
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- Iterate over children prefab
|
|
|
- **/
|
|
|
- public inline function iterator() : Iterator<Prefab> {
|
|
|
- return children.iterator();
|
|
|
- }
|
|
|
+ var thisClass = Type.getClass(this);
|
|
|
|
|
|
- /**
|
|
|
- Override to implement your custom prefab data loading
|
|
|
- **/
|
|
|
- function load( obj : Dynamic ) {
|
|
|
- loadSerializedFields(obj);
|
|
|
- }
|
|
|
+ // We bypass the normal new function to avoid initializing the
|
|
|
+ // serializable fields, because they will be initialized by the copy function
|
|
|
+ var inst = Type.createEmptyInstance(thisClass);
|
|
|
+ inst.postCloneInit(); // Macro function that init all the non serializable fields of a prefab
|
|
|
+ inst.children = [];
|
|
|
+ inst.__newInit(parent, sh);// Macro function that contains the code of the new function
|
|
|
|
|
|
- /**
|
|
|
- Override to implement your custom prefab data saving
|
|
|
- **/
|
|
|
- function save() : {} {
|
|
|
- var obj = {};
|
|
|
- saveSerializedFields(obj);
|
|
|
- return obj;
|
|
|
- }
|
|
|
+ inst.copy(this);
|
|
|
+ if (withChildren) {
|
|
|
+ for (child in children) {
|
|
|
+ child.clone(inst, sh);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
- Override to implement your custom prefab data copying.
|
|
|
- You should copy the field from `p` to `this`, p being an instance of your class
|
|
|
- **/
|
|
|
- function copy( p : Prefab ) {
|
|
|
- copySerializedFields(p);
|
|
|
+ return inst;
|
|
|
}
|
|
|
|
|
|
- function copyValue<T>( v : T ) : T {
|
|
|
- // copy in-depth - might be optimized by macros when called in copy()
|
|
|
- return haxe.Unserializer.run(haxe.Serializer.run(v));
|
|
|
- }
|
|
|
+ // Make related functions
|
|
|
|
|
|
/**
|
|
|
- Creates an instance for this prefab only (and not its children).
|
|
|
- Use make(ctx) to creates the whole instances tree;
|
|
|
+ Create a child from this prefab. Override to filter which child should
|
|
|
+ be created
|
|
|
**/
|
|
|
- public function makeInstance( ctx : Context ) : Context {
|
|
|
- return ctx;
|
|
|
+ function makeChild(c:Prefab) : Void {
|
|
|
+ if (!c.shouldBeInstanciated()) return;
|
|
|
+ if (shared.customMake == null) {
|
|
|
+ c.make(shared);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ shared.customMake(c);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Allows to customize how an instance gets updated when a property name changes.
|
|
|
- You can also call updateInstance(ctx) in order to force whole instance synchronization against current prefab data.
|
|
|
+ Override this function to create runtime objects from this prefab
|
|
|
**/
|
|
|
- public function updateInstance( ctx : Context, ?propName : String ) {
|
|
|
+ function makeInstance() : Void {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Removes the created instance for this prefab only (not is children).
|
|
|
- If false is returned, the instance could not be removed and the whole context scene needs to be rebuilt
|
|
|
+ Called after makeInstance (and by extension postMakeInstance) has been called on all the children
|
|
|
**/
|
|
|
- public function removeInstance( ctx : Context ) : Bool {
|
|
|
- return false;
|
|
|
+ function postMakeInstance() : Void {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Save the whole prefab data and its children.
|
|
|
+ Allows to customize how an instance gets updated when a property name changes.
|
|
|
+ You can also call updateInstance() in order to force whole instance synchronization against current prefab data.
|
|
|
**/
|
|
|
- public function saveData() : {} {
|
|
|
- var obj : Dynamic = save();
|
|
|
-
|
|
|
- if( children.length > 0 )
|
|
|
- obj.children = [for( s in children ) s.saveData()];
|
|
|
-
|
|
|
- return obj;
|
|
|
+ function updateInstance(?propName : String) {
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- Load the whole prefab data and creates its children.
|
|
|
- **/
|
|
|
- public function loadData( v : Dynamic ) {
|
|
|
- load(v);
|
|
|
- if( children.length > 0 )
|
|
|
- children = [];
|
|
|
- var children : Array<Dynamic> = v.children;
|
|
|
- if( children != null )
|
|
|
- for( v in children )
|
|
|
- loadPrefab(v, this);
|
|
|
- }
|
|
|
+ // End of make related functions
|
|
|
|
|
|
/**
|
|
|
Updates in-place the whole prefab data and its children.
|
|
@@ -249,68 +295,63 @@ class Prefab {
|
|
|
prev.reload(v);
|
|
|
newchild.push(prev);
|
|
|
} else {
|
|
|
- newchild.push(loadPrefab(v,this));
|
|
|
+ newchild.push(createFromDynamic(v,this));
|
|
|
}
|
|
|
}
|
|
|
children = newchild;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Creates the correct prefab based on v.type and load its data and children.
|
|
|
- If one the prefab in the tree is not registered, a hxd.prefab.Unkown is created instead.
|
|
|
+ Cleanup prefab specific ressources, and call dispose on it's children.
|
|
|
**/
|
|
|
- public static function loadPrefab( v : Dynamic, ?parent : Prefab ) {
|
|
|
- var pcl = @:privateAccess Library.registeredElements.get(v.type);
|
|
|
- var pcl = pcl == null ? null : pcl.cl;
|
|
|
- if( pcl == null ) pcl = Unknown;
|
|
|
- var p = Type.createInstance(pcl, [parent]);
|
|
|
- p.loadData(v);
|
|
|
- return p;
|
|
|
+ public function dispose() {
|
|
|
+ for (child in children) {
|
|
|
+ child.dispose();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Creates an instance for this prefab and its children.
|
|
|
+ Find the first h2d.Object in this hierarchy, in either this or it's parents
|
|
|
**/
|
|
|
- public function make( ctx : Context ) : Context {
|
|
|
- if( !enabled )
|
|
|
- return ctx;
|
|
|
- if( ctx == null ) {
|
|
|
- ctx = new Context();
|
|
|
- ctx.init();
|
|
|
- }
|
|
|
- var fromRef = #if editor ctx.shared.parent != null #else true #end;
|
|
|
- if (fromRef && editorOnly #if editor || inGameOnly #end)
|
|
|
- return ctx;
|
|
|
- ctx = makeInstance(ctx);
|
|
|
- for( c in children )
|
|
|
- makeChild(ctx, c);
|
|
|
- return ctx;
|
|
|
+ public function findFirstLocal2d() : h2d.Object {
|
|
|
+ var o2d = findParent(Object2D, (p) -> p.local2d != null, true);
|
|
|
+ return o2d != null ? o2d.local2d : shared.root2d;
|
|
|
}
|
|
|
|
|
|
- function makeChild( ctx : Context, p : Prefab ) {
|
|
|
- if( ctx.shared.customMake == null )
|
|
|
- p.make(ctx);
|
|
|
- else if( p.enabled )
|
|
|
- ctx.shared.customMake(ctx, p);
|
|
|
+ /**
|
|
|
+ Find the first h3d.scene.Object in this hierarchy, in either this or it's parents
|
|
|
+ **/
|
|
|
+ public function findFirstLocal3d() : h3d.scene.Object {
|
|
|
+ var o3d = findParent(Object3D, (p) -> p.local3d != null, true);
|
|
|
+ return o3d != null ? o3d.local3d : shared.root3d;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- If the prefab `props` represent CDB data, returns the sheet name of it, or null.
|
|
|
- **/
|
|
|
- public function getCdbType() : String {
|
|
|
- if( props == null )
|
|
|
- return null;
|
|
|
- return Reflect.field(props, "$cdbtype");
|
|
|
+ Search the prefab tree for the prefab matching the given prefab class (and name, if specified).
|
|
|
+ Throw an exception if not found. Uses getOpt() to return null instead.
|
|
|
+ **/
|
|
|
+ public function get<T:Prefab>( ?cl : Class<T>, ?name : String ) : T {
|
|
|
+ var v = getOpt(cl, name);
|
|
|
+ if( v == null )
|
|
|
+ throw "Missing prefab " + (name == null ? Type.getClassName(cl) : (cl == null ? name : name+"(" + Type.getClassName(cl) + ")"));
|
|
|
+ return v;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Search the prefab tree for the prefab matching the given name, returns null if not found
|
|
|
+ Simlar to get() but returns null if not found.
|
|
|
**/
|
|
|
- public function getPrefabByName( name : String ) {
|
|
|
- if( this.name == name )
|
|
|
- return this;
|
|
|
+ public function getOpt<T:Prefab>( ?cl : Class<T>, ?name : String, ?followRefs : Bool ) : Null<T> {
|
|
|
+ if( name == null || this.name == name ) {
|
|
|
+ if (cl != null) {
|
|
|
+ var cval = to(cl);
|
|
|
+ if( cval != null ) return cval;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return cast this;
|
|
|
+ }
|
|
|
+ }
|
|
|
for( c in children ) {
|
|
|
- var p = c.getPrefabByName(name);
|
|
|
+ var p = c.getOpt(cl, name, followRefs);
|
|
|
if( p != null )
|
|
|
return p;
|
|
|
}
|
|
@@ -318,131 +359,116 @@ class Prefab {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Search the prefab tree for the prefabs matching the given path.
|
|
|
- Can use wildcards, such as `*`/level`*`/collision
|
|
|
+ Returns the root prefab, i.e. the first prefab that doesn't have any parent.
|
|
|
**/
|
|
|
- public function getPrefabsByPath( path : String ) {
|
|
|
- var out = [];
|
|
|
- if( path == "" )
|
|
|
- out.push(this);
|
|
|
- else
|
|
|
- getPrefabsByPathRec(path.split("."), 0, out);
|
|
|
- return out;
|
|
|
- }
|
|
|
+ public function getRoot( followRefs : Bool = false ) : Prefab {
|
|
|
+ var root = this;
|
|
|
|
|
|
- function getPrefabsByPathRec( parts : Array<String>, index : Int, out : Array<Prefab> ) {
|
|
|
- var name = parts[index++];
|
|
|
- if( name == null ) {
|
|
|
- out.push(this);
|
|
|
- return;
|
|
|
- }
|
|
|
- var r = name.indexOf('*') < 0 ? null : new EReg("^"+name.split("*").join(".*")+"$","");
|
|
|
- for( c in children ) {
|
|
|
- var cname = c.name;
|
|
|
- if( cname == null ) cname = c.getDefaultName();
|
|
|
- if( r == null ? c.name == name : r.match(cname) )
|
|
|
- c.getPrefabsByPathRec(parts, index, out);
|
|
|
+ while( root.parent != null || (followRefs && root.shared.parentPrefab != null) ) {
|
|
|
+ if( root.parent != null )
|
|
|
+ root = root.parent;
|
|
|
+ else if( followRefs )
|
|
|
+ root = root.shared.parentPrefab;
|
|
|
}
|
|
|
+ return root;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Simlar to get() but returns null if not found.
|
|
|
+ Returns all prefabs in the tree matching the specified class.
|
|
|
**/
|
|
|
- public function getOpt<T:Prefab>( cl : Class<T>, ?name : String, ?followRefs : Bool ) : T {
|
|
|
- if( name == null || this.name == name ) {
|
|
|
- var cval = to(cl);
|
|
|
- if( cval != null ) return cval;
|
|
|
- }
|
|
|
- for( c in children ) {
|
|
|
- var p = c.getOpt(cl, name, followRefs);
|
|
|
- if( p != null )
|
|
|
- return p;
|
|
|
+ public function flatten<T:Prefab>( ?cl : Class<T>, ?arr: Array<T> ) : Array<T> {
|
|
|
+ if(arr == null)
|
|
|
+ arr = [];
|
|
|
+ if( cl == null )
|
|
|
+ arr.push(cast this);
|
|
|
+ else {
|
|
|
+ var i = to(cl);
|
|
|
+ if(i != null)
|
|
|
+ arr.push(i);
|
|
|
}
|
|
|
- return null;
|
|
|
+ for(c in children)
|
|
|
+ c.flatten(cl, arr);
|
|
|
+ return arr;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Search the prefab tree for the prefab matching the given prefab class (and name, if specified).
|
|
|
- Throw an exception if not found. Uses getOpt() to return null instead.
|
|
|
+ Returns all the prefab in the tree
|
|
|
**/
|
|
|
- public function get<T:Prefab>( cl : Class<T>, ?name : String ) : T {
|
|
|
- var v = getOpt(cl, name);
|
|
|
- if( v == null )
|
|
|
- throw "Missing prefab " + (name == null ? Type.getClassName(cl) : (cl == null ? name : name+"(" + Type.getClassName(cl) + ")"));
|
|
|
- return v;
|
|
|
+ public function all(?arr: Array<Prefab>) : Array<Prefab> {
|
|
|
+ return flatten(Prefab, arr != null ? arr : []);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Return all prefabs in the tree matching the given prefab class.
|
|
|
+ Converts the prefab to another prefab class.
|
|
|
+ Returns null if not of this type.
|
|
|
**/
|
|
|
- public function getAll<T:Prefab>( cl : Class<T>, ?followRefs : Bool, ?arr: Array<T> ) : Array<T> {
|
|
|
- return findAll(function(p) return p.to(cl), followRefs, arr);
|
|
|
+ public function to<T:Prefab>( c : Class<T> ) : Null<T> {
|
|
|
+ return Std.downcast(this, c);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Find a single prefab in the tree by calling `f` on each and returning the first not-null value returned, or null if not found.
|
|
|
+ Find a the first prefab in the tree with the given class that matches the optionnal `filter`.
|
|
|
+ Returns null if no matching prefab was found
|
|
|
**/
|
|
|
- public function find<T>( f : Prefab -> Null<T>, ?followRefs : Bool ) : Null<T> {
|
|
|
- var v = f(this);
|
|
|
- if( v != null )
|
|
|
- return v;
|
|
|
+ public function find<T:Prefab>(?cl: Class<T>, ?filter : T -> Bool, followRefs : Bool = false ) : Null<T> {
|
|
|
+ var asCl = cl != null ? Std.downcast(this, cl) : cast this;
|
|
|
+ if (asCl != null)
|
|
|
+ if (filter == null || filter(asCl))
|
|
|
+ return asCl;
|
|
|
for( p in children ) {
|
|
|
- var v = p.find(f, followRefs);
|
|
|
+ var v = p.find(cl, filter, followRefs);
|
|
|
if( v != null ) return v;
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Find several prefabs in the tree by calling `f` on each and returning all the not-null values returned.
|
|
|
+ Find all the prefabs of the given class `cl` in the tree, that matches `filter` if it is is defined.
|
|
|
+ The result is stored in the given array `arr` if it's defined, otherwise an array is created. The final array
|
|
|
+ is then returned.
|
|
|
**/
|
|
|
- public function findAll<T>( f : Prefab -> Null<T>, ?followRefs : Bool, ?arr : Array<T> ) : Array<T> {
|
|
|
+ public function findAll<T:Prefab>(?cl: Class<T>, ?filter : Prefab -> Bool, followRefs : Bool = false, ?arr : Array<T> ) : Array<T> {
|
|
|
if( arr == null ) arr = [];
|
|
|
- var v = f(this);
|
|
|
- if( v != null )
|
|
|
- arr.push(v);
|
|
|
- for( o in children )
|
|
|
- o.findAll(f,followRefs,arr);
|
|
|
- return arr;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- Returns all prefabs in the tree matching the specified class.
|
|
|
- **/
|
|
|
- public function flatten<T:Prefab>( ?cl : Class<T>, ?arr: Array<T> ) : Array<T> {
|
|
|
- if(arr == null)
|
|
|
- arr = [];
|
|
|
- if( cl == null )
|
|
|
- arr.push(cast this);
|
|
|
- else {
|
|
|
- var i = to(cl);
|
|
|
- if(i != null)
|
|
|
- arr.push(i);
|
|
|
+ var asCl = cl != null ? Std.downcast(this, cl) : cast this;
|
|
|
+ if (asCl != null) {
|
|
|
+ if (filter == null || filter(asCl))
|
|
|
+ arr.push(asCl);
|
|
|
}
|
|
|
- for(c in children)
|
|
|
- c.flatten(cl, arr);
|
|
|
+ if (followRefs) {
|
|
|
+ var ref = to(Reference);
|
|
|
+ if (ref != null && ref.refInstance != null) {
|
|
|
+ ref.refInstance.findAll(cl, filter, followRefs, arr);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for( o in children )
|
|
|
+ o.findAll(cl, filter,followRefs,arr);
|
|
|
return arr;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Returns the first parent in the tree matching the specified class or null if not found.
|
|
|
+ Find the first prefab in this prefab parent chain that matches the given class `cl`, and optionally the given `filter`.
|
|
|
+ If `includeSelf` is true, then this prefab is checked as well.
|
|
|
**/
|
|
|
- public function getParent<T:Prefab>( c : Class<T> ) : Null<T> {
|
|
|
- var p = parent;
|
|
|
- while(p != null) {
|
|
|
- var inst = p.to(c);
|
|
|
- if(inst != null) return inst;
|
|
|
- p = p.parent;
|
|
|
+ public function findParent<T:Prefab>(?cl:Class<T> ,?filter : (p:T) -> Bool, includeSelf:Bool = false) : Null<T> {
|
|
|
+ var current = includeSelf ? this : this.parent;
|
|
|
+ while(current != null) {
|
|
|
+ var asCl = cl != null ? Std.downcast(current, cl) : cast current;
|
|
|
+ if (asCl != null) {
|
|
|
+ if (filter == null || filter(asCl))
|
|
|
+ return asCl;
|
|
|
+ }
|
|
|
+ current = current.parent;
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Converts the prefab to another prefab class.
|
|
|
- Returns null if not of this type.
|
|
|
+ Iterate over this children prefab
|
|
|
**/
|
|
|
- public function to<T:Prefab>( c : Class<T> ) : Null<T> {
|
|
|
- return Std.downcast(this, c);
|
|
|
+ public inline function iterator() : Iterator<Prefab> {
|
|
|
+ if (children != null)
|
|
|
+ return children.iterator();
|
|
|
+ return [].iterator();
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -451,7 +477,7 @@ class Prefab {
|
|
|
public function getAbsPath(unique=false) {
|
|
|
if(parent == null)
|
|
|
return "";
|
|
|
- var path = name != null ? name : getDefaultName();
|
|
|
+ var path = name ?? "";
|
|
|
if(unique) {
|
|
|
var suffix = 0;
|
|
|
for(i in 0...parent.children.length) {
|
|
@@ -459,7 +485,7 @@ class Prefab {
|
|
|
if(c == this)
|
|
|
break;
|
|
|
else {
|
|
|
- var cname = c.name != null ? c.name : c.getDefaultName();
|
|
|
+ var cname = c.name ?? "";
|
|
|
if(cname == path)
|
|
|
++suffix;
|
|
|
}
|
|
@@ -473,9 +499,90 @@ class Prefab {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- Returns the default name for this prefab
|
|
|
+ If the prefab `props` represent CDB data, returns the sheet name of it, or null.
|
|
|
+ **/
|
|
|
+ public function getCdbType() : String {
|
|
|
+ if( props == null )
|
|
|
+ return null;
|
|
|
+ return Reflect.field(props, "$cdbtype");
|
|
|
+ }
|
|
|
+
|
|
|
+ public final function toString() : String{
|
|
|
+ var str = type;
|
|
|
+ if ( name != "" ) str += '($name)';
|
|
|
+ return str;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Check if `original` prefab class is or inherits for the class `parent`.
|
|
|
+ **/
|
|
|
+ public static function isOfType( original : Class<Prefab>, parent : Class<Prefab> ) {
|
|
|
+ var c : Class<Dynamic> = original;
|
|
|
+ while( c != null ) {
|
|
|
+ if( c == parent ) return true;
|
|
|
+ c = Type.getSuperClass(c);
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Editor API
|
|
|
+
|
|
|
+ #if editor
|
|
|
+ /**
|
|
|
+ Allows to customize how the prefab object is displayed / handled within Hide
|
|
|
+ **/
|
|
|
+ public function getHideProps() : hide.prefab.HideProps {
|
|
|
+ return { icon : "question-circle", name : "Unknown" };
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Create an interactive object to the scene objects of this prefab
|
|
|
+ **/
|
|
|
+ public function makeInteractive() : hxd.SceneEvents.Interactive {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Allows to customize how the prefab instance changes when selected/unselected within Hide.
|
|
|
+ Selection of descendants is skipped if false is returned.
|
|
|
+ **/
|
|
|
+ public function setSelected(b : Bool ) : Bool {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Called when the hide editor wants to edit this Prefab.
|
|
|
+ Used to create the various editor interfaces
|
|
|
+ **/
|
|
|
+ public function edit(editContext : hide.prefab.EditContext) {
|
|
|
+ }
|
|
|
+
|
|
|
+ public function setEditor(sceneEditor: hide.comp.SceneEditor) {
|
|
|
+ if (sceneEditor == null)
|
|
|
+ throw "No editor for setEditor";
|
|
|
+
|
|
|
+ shared.editor = sceneEditor;
|
|
|
+
|
|
|
+ setEditorChildren(sceneEditor);
|
|
|
+ }
|
|
|
+
|
|
|
+ function setEditorChildren(sceneEditor: hide.comp.SceneEditor) {
|
|
|
+ for (c in children) {
|
|
|
+ c.setEditorChildren(sceneEditor);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Returns a list of all the serializable fieds of the prefab.
|
|
|
+ **/
|
|
|
+ public final function getSerializableProps() : Array<PrefabField> {
|
|
|
+ return getSerializablePropsForClass(Type.getClass(this));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Returns the default display name for this prefab
|
|
|
**/
|
|
|
- public function getDefaultName() : String {
|
|
|
+ public function getDefaultEditorName() : String {
|
|
|
if(source != null) {
|
|
|
var f = new haxe.io.Path(source).file;
|
|
|
f = f.split(" ")[0].split("-")[0];
|
|
@@ -483,26 +590,188 @@ class Prefab {
|
|
|
}
|
|
|
return type.split(".").pop();
|
|
|
}
|
|
|
+ #end
|
|
|
+
|
|
|
+ // Internal
|
|
|
+
|
|
|
+ function setSharedRec(newShared : ContextShared) {
|
|
|
+ this.shared = newShared;
|
|
|
+ for (c in children)
|
|
|
+ c.setSharedRec(newShared);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Recursively copy this prefab and it's children into a dynamic object, containing
|
|
|
+ all the serializable properties and the type of the object
|
|
|
+ **/
|
|
|
+ function serialize() : Dynamic {
|
|
|
+ var ser = save();
|
|
|
+ ser.type = type;
|
|
|
+
|
|
|
+ if (children.length > 0) {
|
|
|
+ var serChildren = [];
|
|
|
+ for (child in children) {
|
|
|
+ serChildren.push(child.serialize());
|
|
|
+ }
|
|
|
+ ser.children = serChildren;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ser;
|
|
|
+ }
|
|
|
+
|
|
|
+ function locateObject( path : String ) {
|
|
|
+ if( path == null )
|
|
|
+ return null;
|
|
|
+ var parts = path.split(".");
|
|
|
+ var root = shared.root3d;
|
|
|
+ if (root == null)
|
|
|
+ return null;
|
|
|
+ while( parts.length > 0 ) {
|
|
|
+ var v = null;
|
|
|
+ var pname = parts.shift();
|
|
|
+ for( o in root )
|
|
|
+ if( o.name == pname ) {
|
|
|
+ v = o;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if( v == null ) {
|
|
|
+ v = root.getObjectByName(pname);
|
|
|
+ //if( v != null && v.parent != root ) v = null; ??
|
|
|
+ }
|
|
|
+ if( v == null ) {
|
|
|
+ var parts2 = path.split(".");
|
|
|
+ for( i in 0...parts.length ) parts2.pop();
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ root = v;
|
|
|
+ }
|
|
|
+ return root;
|
|
|
+ }
|
|
|
+
|
|
|
+ function shouldBeInstanciated() : Bool {
|
|
|
+ if (!enabled) return false;
|
|
|
+
|
|
|
+ #if editor
|
|
|
+ if (shared.parentPrefab != null && inGameOnly)
|
|
|
+ return false;
|
|
|
+ #else
|
|
|
+ if (editorOnly)
|
|
|
+ return false;
|
|
|
+ #end
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Copy all the properties in data to this prefab object. This is not recursive. Done when loading the json data of the prefab.
|
|
|
+ **/
|
|
|
+ function load(data : Dynamic) : Void {
|
|
|
+ this.copyFromDynamic(data);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Copy all the properties in Prefab to this prefab object. Done when cloning an existing prefab.
|
|
|
+ **/
|
|
|
+ function copy(data: Prefab) : Void {
|
|
|
+ this.copyFromOther(data);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Save all the properties to the given dynamic object. This is not recursive. Returns the updated dynamic object.
|
|
|
+ **/
|
|
|
+ function save() : Dynamic {
|
|
|
+ return this.copyToDynamic({});
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ Create a new prefab from the given `data`.
|
|
|
+ **/
|
|
|
+ static function createFromDynamic(data:Dynamic, parent:Prefab = null, contextShared:ContextShared = null) : Prefab {
|
|
|
+ var type : String = data.type;
|
|
|
+
|
|
|
+ var cl : Class<Prefab> = Unknown;
|
|
|
+
|
|
|
+ if (type != null) {
|
|
|
+ var classEntry = registry.get(type);
|
|
|
+ if (classEntry != null)
|
|
|
+ cl = classEntry.prefabClass;
|
|
|
+ }
|
|
|
+
|
|
|
+ var prefabInstance = Type.createInstance(cl, [parent, contextShared]);
|
|
|
+
|
|
|
+ prefabInstance.load(data);
|
|
|
+
|
|
|
+ var children = Std.downcast(Reflect.field(data, "children"), Array);
|
|
|
+ if (children != null) {
|
|
|
+ for (child in children) {
|
|
|
+ createFromDynamic(child, prefabInstance);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return prefabInstance;
|
|
|
+ }
|
|
|
|
|
|
/**
|
|
|
- Clone this prefab, and all its children if recursive=true.
|
|
|
+ Return the first h3d.scene.Objects found in each of this prefab children.
|
|
|
+ If a children has no h3d.scene.Objects, it then search in it's children and so on.
|
|
|
**/
|
|
|
- public final function clone( ?parent : Prefab, recursive = true ) : Prefab {
|
|
|
- var obj = Type.createInstance(Type.getClass(this),[parent]);
|
|
|
- obj.copy(this);
|
|
|
- if( recursive ) {
|
|
|
- for( c in children )
|
|
|
- c.clone(obj);
|
|
|
+ static function getChildrenRoots( base : h3d.scene.Object, p : Prefab, out : Array<h3d.scene.Object> ) {
|
|
|
+ for( c in p.children ) {
|
|
|
+ var local3d = Object3D.getLocal3d(c);
|
|
|
+ if( local3d == base )
|
|
|
+ getChildrenRoots(base, c, out);
|
|
|
+ else
|
|
|
+ out.push(local3d);
|
|
|
}
|
|
|
- return obj;
|
|
|
+ return out;
|
|
|
+ }
|
|
|
+
|
|
|
+ inline static function getSerializablePropsForClass(cl : Class<Prefab>) {
|
|
|
+ return (cl:Dynamic).getSerializablePropsStatic();
|
|
|
+ }
|
|
|
+
|
|
|
+ static function getClassTypeName(cl : Class<Prefab>) : String {
|
|
|
+ return reverseRegistry.get(Type.getClassName(cl));
|
|
|
+ }
|
|
|
+
|
|
|
+ static function getPrefabInfoByName(name:String) : PrefabInfo {
|
|
|
+ return registry[name];
|
|
|
}
|
|
|
|
|
|
+ static function getPrefabType(path: String) {
|
|
|
+ var extension = path.split(".").pop().toLowerCase();
|
|
|
+ return extensionRegistry.get(extension);
|
|
|
+ }
|
|
|
+
|
|
|
+ static var registry : Map<String, PrefabInfo> = new Map();
|
|
|
+
|
|
|
+ // Map prefab class name to the serialized name of the prefab
|
|
|
+ static var reverseRegistry : Map<String, String> = new Map();
|
|
|
+ static var extensionRegistry : Map<String, String> = new Map();
|
|
|
+
|
|
|
/**
|
|
|
- Similar to clone() but uses full data copying to guarantee a whole copy with no data reference (but is slower).
|
|
|
+ Register the given prefab class with the given typeName in the prefab regsitry.
|
|
|
+ This is necessary for the serialisation system.
|
|
|
+ Call it by placing this in you prefab class :
|
|
|
+ ```
|
|
|
+ public static var _ = Prefab.register("myPrefabName", myPrefabClassName);
|
|
|
+ ```
|
|
|
**/
|
|
|
- public final function cloneData( recursive = true ) {
|
|
|
- var data = recursive ? saveData() : save();
|
|
|
- data = haxe.Json.parse(haxe.Json.stringify(data));
|
|
|
- return loadPrefab(data);
|
|
|
+ static function register(typeName : String, prefabClass: Class<hrt.prefab.Prefab>, ?extension: String) {
|
|
|
+ #if editor
|
|
|
+ var info : hide.prefab.HideProps = cast Type.createEmptyInstance(prefabClass).getHideProps();
|
|
|
+ #end
|
|
|
+
|
|
|
+ reverseRegistry.set(Type.getClassName(prefabClass), typeName);
|
|
|
+ registry.set(typeName, {prefabClass: prefabClass #if editor, inf : info #end, extension: extension});
|
|
|
+ if (extension != null) {
|
|
|
+ extensionRegistry.set(extension, typeName);
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
}
|
|
|
+
|
|
|
+ // Static initialization trick to register this class with the given name
|
|
|
+ // in the prefab registry. Call this in your own classes
|
|
|
+ static var _ = Prefab.register("prefab", Prefab, "prefab");
|
|
|
}
|