API Docs for:
Show:

File: ../src/components/meshRenderer.js


/**
* Renders one mesh, it allows to configure the rendering primitive, the submesh (range of mesh) and a level of detail mesh
* @class MeshRenderer
* @namespace LS.Components
* @constructor
* @param {Object} object to configure from
*/
function MeshRenderer(o)
{
	this._enabled = true;

	this._mesh = null;

	this._lod_mesh = null;

	this._submesh_id = -1;

	this._material = null;

	this._primitive = -1;

	this._must_update_static = true; //used in static meshes
	this._transform_version = -1;

	//used to render with several materials (WIP, not finished yet)
	this.use_submaterials = false;
	this.submaterials = [];

	if(o)
		this.configure(o);

	this._RI = new LS.RenderInstance( null, this );
	//this._RIs = [];
	this._is_attached = false;
}

Object.defineProperty( MeshRenderer.prototype, 'enabled', {
	get: function() { return this._enabled; },
	set: function(v) { 
		v = !!v;
		this._enabled = v;
		this.checkRenderInstances();
	},
	enumerable: true
});

/**
* The GL primitive to use when rendering this mesh (gl.POINTS, gl.TRIANGLES, etc), -1 is default, it also supports the option 10 which means Wireframe
* @property primitive {number}
* @default -1;
*/
Object.defineProperty( MeshRenderer.prototype, 'primitive', {
	get: function() { return this._primitive; },
	set: function(v) { 
		v = (v === undefined || v === null ? -1 : v|0);
		if( v < -1 || v > 10 )
			return;
		this._primitive = v;
		this.updateRIs();
	},
	enumerable: true
});

/**
* The material to apply to this render, if not provided the one in the node will be used
* @property material {string}
* @default -1;
*/
Object.defineProperty( MeshRenderer.prototype, 'material', {
	get: function() { return this._material; },
	set: function(v) { 
		this._material = v;
		this.updateRIs();
	},
	enumerable: true
});

/**
* The name of the mesh to render
* @property mesh {string}
* @default null;
*/
Object.defineProperty( MeshRenderer.prototype, 'mesh', {
	get: function() { return this._mesh; },
	set: function(v) { 
		this._mesh = v;
		this.updateRIs();
	},
	enumerable: true
});

/**
* The name of the mesh to render in case the mesh is far away, this mesh is also used for collision testing if using raycast to RenderInstances
* @property lod_mesh {string}
* @default null;
*/
Object.defineProperty( MeshRenderer.prototype, 'lod_mesh', {
	get: function() { return this._lod_mesh; },
	set: function(v) { 
		this._lod_mesh = v;
		this.updateRIs();
	},
	enumerable: true
});

/**
* The id of the submesh group to render, if the id is -1 then all the mesh is rendered.
* @property submesh_id {number}
* @default -1;
*/
Object.defineProperty( MeshRenderer.prototype, 'submesh_id', {
	get: function() { return this._submesh_id; },
	set: function(v) { 
		//what about if v is a string, search for the index?
		this._submesh_id = v;
	},
	enumerable: true
});

Object.defineProperty( MeshRenderer.prototype, 'render_instance', {
	get: function() { return this._RI; },
	set: function(v) { throw("cannot set a render_instance, must use the collectRenderInstances process."); },
	enumerable: false
});

MeshRenderer.icon = "mini-icon-teapot.png";

//vars
MeshRenderer["@mesh"] = { type: "mesh" };
MeshRenderer["@lod_mesh"] = { type: "mesh" };
MeshRenderer["@material"] = { type: "material" };
MeshRenderer["@primitive"] = { type:"enum", values: {"Default":-1, "Points": 0, "Lines":1, "LineLoop":2, "LineStrip":3, "Triangles":4, "TriangleStrip":5, "TriangleFan":6, "Wireframe":10 }};
MeshRenderer["@submesh_id"] = { type:"enum", values: function() {
	var component = this.instance;
	var mesh = component.getMesh();
	if(!mesh) return null;
	if(!mesh || !mesh.info || !mesh.info.groups || mesh.info.groups.length < 2)
		return null;

	var t = {"all":null};
	for(var i = 0; i < mesh.info.groups.length; ++i)
		t[mesh.info.groups[i].name] = i;
	return t;
}};

MeshRenderer["@use_submaterials"] = { type: LS.TYPES.BOOLEAN, widget: null }; //avoid widget
MeshRenderer["@submaterials"] = { widget: null }; //avoid 

//we bind to onAddedToNode because the event is triggered per node so we know which RIs belong to which node
MeshRenderer.prototype.onAddedToScene = function( scene )
{
	this.checkRenderInstances();
}

MeshRenderer.prototype.onRemovedFromScene = function( scene )
{
	this.checkRenderInstances();
}

MeshRenderer.prototype.onAddedToNode = function( node )
{
	LEvent.bind( node, "materialChanged", this.updateRIs, this );
	LEvent.bind( node, "collectRenderInstances", this.onCollectInstances, this );
	this._RI.node = node;
}

MeshRenderer.prototype.onRemovedFromNode = function( node )
{
	LEvent.unbind( node, "materialChanged", this.updateRIs, this );
	LEvent.unbind( node, "collectRenderInstances", this.onCollectInstances, this );
}


/**
* Configure from a serialized object
* @method configure
* @param {Object} object with the serialized info
*/
MeshRenderer.prototype.configure = function(o)
{
	if(o.uid)
		this.uid = o.uid;
	if(o.enabled !== undefined)
		this.enabled = o.enabled;
	this.mesh = o.mesh;
	this.lod_mesh = o.lod_mesh;
	if(o.submesh_id !== undefined)
		this.submesh_id = o.submesh_id;
	this.primitive = o.primitive; //gl.TRIANGLES
	this.material = o.material;
	this.use_submaterials = !!o.use_submaterials;
	if(o.submaterials)
		this.submaterials = o.submaterials;
	if(o.material && o.material.constructor === String)
		this.material = o.material;
}

/**
* Serialize the object 
* @method serialize
* @return {Object} object with the serialized info
*/
MeshRenderer.prototype.serialize = function()
{
	var o = { 
		object_class: "MeshRenderer",
		enabled: this.enabled,
		uid: this.uid,
		mesh: this.mesh,
		lod_mesh: this.lod_mesh
	};

	if(this.material && this.material.constructor === String )
		o.material = this.material;

	if(this.primitive != -1)
		o.primitive = this.primitive;
	if(this.submesh_id != -1)
		o.submesh_id = this.submesh_id;
	o.material = this.material;

	if(this.use_submaterials)
		o.use_submaterials = this.use_submaterials;
	o.submaterials = this.submaterials;

	return o;
}

MeshRenderer.prototype.getMesh = function() {
	if(!this.mesh)
		return null;

	if( this.mesh.constructor === String )
		return LS.ResourcesManager.meshes[ this.mesh ];
	return this.mesh;
}

MeshRenderer.prototype.getLODMesh = function() {
	if(!this.lod_mesh)
		return null;

	if( this.lod_mesh.constructor === String )
		return LS.ResourcesManager.meshes[ this.lod_mesh ];

	return null;
}

MeshRenderer.prototype.getAnyMesh = function() {
	return (this.getMesh() || this.getLODMesh());
}

MeshRenderer.prototype.getResources = function(res)
{
	if( this.mesh && this.mesh.constructor === String )
		res[ this.mesh ] = GL.Mesh;
	if( this.lod_mesh && this.lod_mesh.constructor === String )
		res[this.lod_mesh] = GL.Mesh;
	if( this.material && this.material.constructor === String )
		res[this.material] = LS.Material;

	if(this.use_submaterials)
	{
		for(var i  = 0; i < this.submaterials.length; ++i)
			if(this.submaterials[i])
				res[this.submaterials[i]] = LS.Material;
	}
	return res;
}

MeshRenderer.prototype.onResourceRenamed = function (old_name, new_name, resource)
{
	if(this.mesh == old_name)
		this.mesh = new_name;
	if(this.lod_mesh == old_name)
		this.lod_mesh = new_name;
	if(this.material == old_name)
		this.material = new_name;
	if(this.morph_targets)
		for(var i in this.morph_targets)
			if( this.morph_targets[i].mesh == old_name )
				this.morph_targets[i].mesh = new_name;
}

MeshRenderer.prototype.checkRenderInstances = function()
{
	return;

	var should_be_attached = this._enabled && this._root.scene;

	if( should_be_attached && !this._is_attached )
	{
		this._root.scene.attachSceneElement( this._RI );
		this._is_attached = true;
	}
	else if( !should_be_attached && this._is_attached )
	{
		this._root.scene.detachSceneElement( this._RI );
		this._is_attached = false;
	}
}

//called everytime something affecting this RIs configuration changes
MeshRenderer.prototype.updateRIs = function()
{
	return;

	var node = this._root;
	if(!node)
		return;

	var RI = this._RI;
	var is_static = this._root.flags && this._root.flags.is_static;
	var transform = this._root.transform;

	//optimize: TODO
	//if( is_static && LS.allow_static && !this._must_update_static && (!transform || (transform && this._transform_version == transform._version)) )
	//	return instances.push( RI );

	//assigns matrix, layers
	RI.fromNode( this._root );

	//material (after flags because it modifies the flags)
	var material = null;
	if(this.material)
		material = LS.ResourcesManager.getResource( this.material );
	else
		material = this._root.getMaterial();
	RI.setMaterial( material );

	//buffers from mesh and bounding
	var mesh = LS.ResourcesManager.getMesh( this._mesh );
	if( mesh )
	{
		RI.setMesh( mesh, this.primitive );
		if(this._submesh_id != -1 && this._submesh_id != null && mesh.info && mesh.info.groups)
		{
			var group = mesh.info.groups[this._submesh_id];
			if(group)
				RI.setRange( group.start, group.length );
		}
		else
			RI.setRange(0,-1);
	}
	else
	{
		RI.setMesh( null );
		RI.setRange(0,-1);
		if(this._once_binding_index != null)
			this._once_binding_index = LS.ResourcesManager.onceLoaded( this._mesh, this.updateRIs.bind(this ) );
	}

	//used for raycasting
	/*
	if(this.lod_mesh)
	{
		if( this.lod_mesh.constructor === String )
			RI.collision_mesh = LS.ResourcesManager.resources[ this.lod_mesh ];
		else
			RI.collision_mesh = this.lod_mesh;
		//RI.setLODMesh( RI.collision_mesh );
	}
	else
	*/
		RI.collision_mesh = mesh;

	//mark it as ready once no more changes should be applied
	if( is_static && LS.allow_static && !this.isLoading() )
	{
		this._must_update_static = false;
		this._transform_version = transform ? transform._version : 0;
	}
}

//*
//MeshRenderer.prototype.getRenderInstance = function(options)
MeshRenderer.prototype.onCollectInstances = function(e, instances)
{
	if(!this._enabled)
		return;

	if(this.use_submaterials)
	{
		this.onCollectInstancesSubmaterials(instances);
		return;
	}

	var mesh = this.getAnyMesh();
	if(!mesh)
		return null;

	var node = this._root;
	if(!this._root)
		return;

	var RI = this._RI;
	var is_static = this._root.flags && this._root.flags.is_static;
	var transform = this._root.transform;
	RI.layers = this._root.layers;

	//optimize
	//if( is_static && LS.allow_static && !this._must_update_static && (!transform || (transform && this._transform_version == transform._version)) )
	//	return instances.push( RI );

	//assigns matrix, layers
	RI.fromNode( this._root );

	//material (after flags because it modifies the flags)
	var material = null;
	if(this.material)
		material = LS.ResourcesManager.getResource( this.material );
	else
		material = this._root.getMaterial();
	RI.setMaterial( material );

	//buffers from mesh and bounding
	RI.setMesh( mesh, this.primitive );

	if(this.submesh_id != -1 && this.submesh_id != null && mesh.info && mesh.info.groups)
	{
		var group = mesh.info.groups[this.submesh_id];
		if(group)
		{
			RI.setRange( group.start, group.length );
			if( group.bounding )
				RI.setBoundingBox( group.bounding );
		}
	}
	else
		RI.setRange(0,-1);

	//used for raycasting
	/*
	if(this.lod_mesh)
	{
		if( this.lod_mesh.constructor === String )
			RI.collision_mesh = LS.ResourcesManager.resources[ this.lod_mesh ];
		else
			RI.collision_mesh = this.lod_mesh;
		//RI.setLODMesh( RI.collision_mesh );
	}
	else
	*/
		RI.collision_mesh = mesh;

	//mark it as ready once no more changes should be applied
	if( is_static && LS.allow_static && !this.isLoading() )
	{
		this._must_update_static = false;
		this._transform_version = transform ? transform._version : 0;
	}

	instances.push( RI );
}

//not fully tested
MeshRenderer.prototype.onCollectInstancesSubmaterials = function(instances)
{
	if(!this._RIs)
		this._RIs = [];

	var mesh = this.getMesh();
	if(!mesh)
		return;

	var groups = mesh.info.groups;
	if(!groups)
		return;

	var global = this._root.transform._global_matrix;
	var center = vec3.create();
	mat4.multiplyVec3( center, global, LS.ZEROS );
	var first_RI = null;

	for(var i = 0; i < this.submaterials.length; ++i)
	{
		var submaterial_name = this.submaterials[i];
		if(!submaterial_name)
			continue;
		var group = groups[i];
		if(!group)
			continue;
		var material = LS.ResourcesManager.getResource( submaterial_name );
		if(!material)
			continue;

		var RI = this._RIs[i];
		if(!RI)
			RI = this._RIs[i] = new LS.RenderInstance(this._root,this);

		if(!first_RI)
			RI.setMatrix( this._root.transform._global_matrix );
		else
			RI.setMatrix( first_RI.matrix, first_RI.normal_matrix );
		RI.center.set(center);

		//flags
		RI.setMaterial( material );
		RI.setMesh( mesh, this.primitive );
		RI.setRange( group.start, group.length );
		instances.push(RI);

		if(!first_RI)
			first_RI = RI;
	}
}
//*/

//test if any of the assets is being loaded
MeshRenderer.prototype.isLoading = function()
{
	if( this.mesh && LS.ResourcesManager.isLoading( this.mesh ))
		return true;
	if( this.lod_mesh && LS.ResourcesManager.isLoading( this.lod_mesh ))
		return true;
	if( this.material && LS.ResourcesManager.isLoading( this.material ))
		return true;
	if(this._root && this._root.material && this._root.material.constructor === String && LS.ResourcesManager.isLoading( this._root.material ))
		return true;
	return false;
}

//used when a node has too many submeshes with materials
MeshRenderer.prototype.explodeSubmeshesToChildNodes = function() { 
	var node = this._root;
	if(!node)
		return;

	var mesh = this.getMesh();
	if(!mesh || !mesh.info || !mesh.info.groups )
		return;

	node.removeComponent( this );

	for(var i = 0; i < mesh.info.groups.length; ++i)
	{
		var group = mesh.info.groups[i];
		var child_node = new LS.SceneNode();
		node.addChild( child_node );
		var comp = new LS.Components.MeshRenderer({ mesh: this.mesh, submesh_id: i, material: group.material });
		child_node.addComponent( comp );	
	}

	LS.GlobalScene.refresh();
}

LS.registerComponent( MeshRenderer );
LS.MeshRenderer = MeshRenderer;