API Docs for:
Show:

File: ../src/resources/prefab.js


/**
* Prefab work in two ways: 
* - It can contain a node structure and all the associated resources (textures, meshes, animations, etc)
* - When a node in the scene was created from a Prefab, the prefab is loaded so the associated resources are recovered, but the node structure is not modified.
* 
* @class Prefab
* @constructor
*/

function Prefab(o)
{
	this.resource_names = []; 
	this.prefab_json = null;
	this.prefab_data = null; //json object
	this._resources_data = {}; //data as it was loaded from the WBin

	if(o)
		this.configure(o);
}

Prefab.version = "0.2"; //used to know where the file comes from 

/**
* configure the prefab
* @method configure
* @param {*} data
**/

Prefab.prototype.configure = function(data)
{
	if(!data)
		throw("No prefab data found");

	if(data.hasOwnProperty("prefab_data"))
	{
		this.prefab_data = data.prefab_data;
		this.prefab_json = data.prefab_json || JSON.stringify( this.prefab_data );
	}
	else
	{
		//read from WBin info
		var prefab_json = data["@json"];
		if(!prefab_json)
		{
			console.warn("No JSON found in prefab");
			return;
		}

		var version = data["@version"]; //not used yet
		this.prefab_json = prefab_json;
		this.prefab_data = JSON.parse( prefab_json );
	}

	this.resource_names = data["@resources_name"] || data.resource_names;

	//extract resource names
	if(this.resource_names)
	{
		var resources = {};
		for(var i in this.resource_names)
		{
			if(!version) //legacy
				resources[ this.resource_names[i] ] = data[ this.resource_names[i] ];
			else
				resources[ this.resource_names[i] ] = data[ "@RES_" + i ];
		}
		this._resources_data = resources;
	}

	//store resources in ResourcesManager
	this.processResources();
}

Prefab.fromBinary = function(data)
{
	if(data.constructor == ArrayBuffer)
		data = WBin.load(data, true);

	return new LS.Prefab(data);
}

//given a list of resources that come from a Prefab (usually a wbin) it extracts, process and register them 
Prefab.prototype.processResources = function()
{
	if(!this._resources_data)
		return;

	var resources = this._resources_data;

	//block this resources of being loaded, this is to avoid chain reactions when a resource uses 
	//another one contained in this Prefab
	for(var resname in resources)
	{
		if( LS.ResourcesManager.resources[ resname ] )
			continue; //already loaded
		LS.ResourcesManager.resources_being_processed[ resname ] = true;
	}

	//process and store in ResourcesManager
	for(var resname in resources)
	{
		if( LS.ResourcesManager.resources[resname] )
			continue; //already loaded

		var resdata = resources[resname];
		if(!resdata)
		{
			console.warn("resource data in prefab is undefined, skipping it:" + resname);
			continue;
		}
		var resource = LS.ResourcesManager.processResource( resname, resdata, { is_local: true, from_prefab: true } );
	}
}

/**
* Creates an instance of the object inside the prefab
* @method createObject
* @return object contained 
**/

Prefab.prototype.createObject = function()
{
	if(!this.prefab_json)
		throw("No prefab_json data found");

	var conf_data = JSON.parse(this.prefab_json);

	if(!conf_data)
	{
		console.error("Prefab data is null");
		return null;
	}

	var node = new LS.SceneNode();
	node.configure(conf_data);
	LS.ResourcesManager.loadResources( node.getResources({},true) );

	if(this.fullpath)
		node.prefab = this.fullpath;

	return node;
}

/**
* to create a new prefab, it packs all the data an instantiates the resource
* @method Prefab.createPrefab
* @param {String} filename a name for this prefab (if wbin is not appended, it will)
* @param {Object} node_data an object containing all the node data to store
* @param {Array} resource_names_list an array with the name of the resources to store
* @return object containing the prefab data ready to be converted to WBin (it also stores _original_data with the WBin)
**/
Prefab.createPrefab = function( filename, node_data, resource_names_list )
{
	if(!filename)
		return;

	if(!node_data)
		throw("No node_data in prefab");

	filename = filename.replace(/ /gi,"_");
	resource_names_list = resource_names_list || [];

	//LS.clearUIds( node_data ); //remove uids of nodes and components
	node_data.object_type = "SceneNode";

	var prefab = new LS.Prefab();
	var ext = LS.ResourcesManager.getExtension(filename);
	if(ext != "wbin")
		filename += ".wbin";

	//checkfilenames and rename them to short names
	prefab.filename = filename;
	prefab.resource_names = resource_names_list;
	prefab.prefab_data = node_data;
	prefab.prefab_json = JSON.stringify( prefab.prefab_data );

	//get all the resources and store them in a WBin
	var bindata = LS.Prefab.packResources( resource_names_list, { "@json": prefab.prefab_json, "@version": Prefab.version } );
	prefab._original_data = bindata;

	return prefab;
}

//Given a list of resources and some base data, it creates a WBin with all the data
Prefab.packResources = function( resource_names_list, base_data )
{
	var to_binary = base_data || {};
	var resource_names = [];

	if(resource_names_list && resource_names_list.length)
	{
		for(var i = 0; i < resource_names_list.length; ++i)
		{
			var res_name = resource_names_list[i];
			var resource = LS.ResourcesManager.resources[ res_name ];
			if(!resource)
				continue;

			var data = null;
			if(resource._original_data) //must be string or bytes
				data = resource._original_data;
			else
			{
				var data_info = LS.Resource.getDataToStore( resource );
				data = data_info.data;
			}

			if(!data)
			{
				console.warning("Wrong data in resource");
				continue;
			}

			if(data.constructor === Blob || data.constructor === File)
			{
				console.warning("WBin does not support to store File or Blob, please convert to ArrayBuffer using FileReader");
				continue;
			}

			to_binary["@RES_" + resource_names.length ] = data;
			resource_names.push( res_name );
		}
	}

	to_binary["@resources_name"] = resource_names;
	return WBin.create( to_binary, "Prefab" );
}

Prefab.prototype.containsResources = function()
{
	return this.resource_names.length > 0;
}

Prefab.prototype.updateFromNode = function( node, clear_uids )
{
	var data = node.serialize(true);
	if(clear_uids)
		LS.clearUIds(data); //remove UIDs
	this.prefab_data = data;
	this.prefab_json = JSON.stringify( data );
}

Prefab.prototype.flagResources = function()
{
	if(!this.resource_names)
		return;

	for(var i = 0; i < this.resource_names.length; ++i)
	{
		var res_name = this.resource_names[i];
		var resource = LS.ResourcesManager.resources[ res_name ];
		if(!resource)
			continue;

		resource.from_prefab = this.fullpath || this.filename || true;
	}
}

Prefab.prototype.setResourcesLink = function( value )
{
	for(var i = 0; i < this.resource_names.length; ++i)
	{
		var res_name = this.resource_names[i];
		var resource = LS.ResourcesManager.resources[ res_name ];
		if(!resource)
			continue;
		if(value)
			resource.from_prefab = value;
		else
			delete resource.from_prefab;
	}
}

//search for nodes using this prefab and creates the nodes
Prefab.prototype.applyToNodes = function( scene )
{
	scene = scene || LS.GlobalScene;	
	var name = this.fullpath || this.filename;

	for(var i = 0; i < scene._nodes.length; ++i)
	{
		var node = scene._nodes[i];
		if(node.prefab != name)
			continue;
		node.reloadFromPrefab();
	}
}

Prefab.prototype.getDataToStore = function()
{
	this.prefab_json = JSON.stringify( this.prefab_data );
	return LS.Prefab.packResources( this.resource_names, { "@json": this.prefab_json, "@version": LS.Prefab.version } );
}

LS.Prefab = Prefab;
LS.registerResourceClass( Prefab );