text: ///@INFO: BASE
  /**
  * RenderState sets the flags for the GPU associated with a rendering action (blending, masking, depth test, etc)
  * It is stored in the material (although defined usually from ShaderCode) so the material can use it.
  *
  * @class RenderState
  * @namespace LS
  * @constructor
  */
  
  /* gpu flags
  
  0: front_face: GL.CCW
  1: cull_face: 1
  2: cull_face_mode: GL.BACK
  
  //depth buffer
  4: depth_test: 1
  5: depth_mask: 1 //write in depth buffer
  6: depth_func: GL.LESS
  7: depth_range0: 0
  8: depth_range1: 1
  
  //blend function
  9: blend: 0;
  10: blendFunc0: GL.SRC_ALPHA
  11: blendFunc1: GL.ONE_MINUS_SRC_ALPHA
  
  //color mask
  12:	colorMask0: 1
  13:	colorMask1: 1
  14:	colorMask2: 1
  15:	colorMask3: 1
  
  //stencil buffer
  16: stencil_test: 0
  17:	stencil_mask: 0xFF,
  18:	stencil_func_func: GL.ALWAYS,
  19:	stencil_func_ref: 0,
  20:	stencil_func_mask: 0xFF,
  21:	stencil_op_sfail: GL.KEEP,
  22:	stencil_op_dpfail: GL.KEEP,
  23:	stencil_op_dppass: GL.KEEP
  
  24: flags
  */
  
  function RenderState( o )
  {
  	this._data = new Uint32Array(25);
  	this.init();
  
  	if(o)
  		this.configure(o);
  }
  
  Object.defineProperty( RenderState.prototype, "front_face", {
  	set: function(v) { this._data[0] = v; },
  	get: function() { return this._data[0];	},
  	enumerable: true
  });
  
  RenderState.SKIP_BLEND = 1;
  RenderState.SKIP_DEPTH = 2;
  RenderState.SKIP_STENCIL = 4;
  
  RenderState["@front_face"] = { widget: "combo", values: { CW: GL.CW, CCW: GL.CCW } };
  
  Object.defineProperty( RenderState.prototype, "cull_face", {
  	set: function(v) { this._data[1] = v ? 1 : 0; },
  	get: function() { return this._data[1] !== 0;	},
  	enumerable: true
  });
  
  Object.defineProperty( RenderState.prototype, "cull_face_mode", {
  	set: function(v) { this._data[2] = v; },
  	get: function() { return this._data[2];	},
  	enumerable: true
  });
  
  RenderState["@cull_face_mode"] = { widget: "combo", values: { FRONT: GL.FRONT, BACK: GL.BACK, FRONT_AND_BACK: GL.FRONT_AND_BACK } };
  
  Object.defineProperty( RenderState.prototype, "depth_test", {
  	set: function(v) { this._data[4] = v ? 1 : 0; },
  	get: function() { return this._data[4] !== 0;	},
  	enumerable: true
  });
  
  Object.defineProperty( RenderState.prototype, "depth_mask", {
  	set: function(v) { this._data[5] = v ? 1 : 0; },
  	get: function() { return this._data[5] !== 0;	},
  	enumerable: true
  });
  
  Object.defineProperty( RenderState.prototype, "depth_func", {
  	set: function(v) { this._data[6] = v; },
  	get: function() { return this._data[6];	},
  	enumerable: true
  });
  
  RenderState["@depth_func"] = { widget: "combo", values: { LESS: GL.LESS, LEQUAL: GL.LEQUAL, EQUAL: GL.EQUAL, NOTEQUAL: GL.NOTEQUAL, GREATER: GL.GREATER, GEQUAL: GL.GEQUAL, ALWAYS: GL.ALWAYS, NEVER: GL.NEVER } };
  
  Object.defineProperty( RenderState.prototype, "depth_range", {
  	set: function(v) { 
  		if(!v || v.length != 2)
  			return;
  		this._data[7] = v[0];
  		this._data[8] = v[1];
  	},
  	get: function() { return this._data.subarray(7,9);	},
  	enumerable: true
  });
  
  Object.defineProperty( RenderState.prototype, "blend", {
  	set: function(v) { this._data[9] = v ? 1 : 0; },
  	get: function() { return this._data[9] !== 0;	},
  	enumerable: true
  });
  
  Object.defineProperty( RenderState.prototype, "blendFunc0", {
  	set: function(v) { this._data[10] = v; },
  	get: function() { return this._data[10];	},
  	enumerable: true
  });
  
  RenderState["@blendFunc0"] = { widget: "combo", values: { ZERO: GL.ZERO, ONE: GL.ONE, SRC_COLOR: GL.SRC_COLOR, ONE_MINUS_SRC_COLOR: GL.ONE_MINUS_SRC_COLOR, DST_COLOR: GL.DST_COLOR, ONE_MINUS_DST_COLOR: GL.ONE_MINUS_DST_COLOR, SRC_ALPHA: GL.SRC_ALPHA, ONE_MINUS_SRC_ALPHA: GL.ONE_MINUS_SRC_ALPHA, DST_ALPHA: GL.DST_ALPHA, ONE_MINUS_DST_ALPHA: GL.ONE_MINUS_DST_ALPHA, CONSTANT_COLOR: GL.CONSTANT_COLOR, ONE_MINUS_CONSTANT_COLOR: GL.ONE_MINUS_CONSTANT_COLOR, CONSTANT_ALPHA: GL.CONSTANT_ALPHA, ONE_MINUS_CONSTANT_ALPHA: GL.ONE_MINUS_CONSTANT_ALPHA, SRC_ALPHA_SATURATE: GL.SRC_ALPHA_SATURATE } };
  
  Object.defineProperty( RenderState.prototype, "blendFunc1", {
  	set: function(v) { this._data[11] = v; },
  	get: function() { return this._data[11];	},
  	enumerable: true
  });
  
  RenderState["@blendFunc1"] = { widget: "combo", values: { ZERO: GL.ZERO, ONE: GL.ONE, SRC_COLOR: GL.SRC_COLOR, ONE_MINUS_SRC_COLOR: GL.ONE_MINUS_SRC_COLOR, DST_COLOR: GL.DST_COLOR, ONE_MINUS_DST_COLOR: GL.ONE_MINUS_DST_COLOR, SRC_ALPHA: GL.SRC_ALPHA, ONE_MINUS_SRC_ALPHA: GL.ONE_MINUS_SRC_ALPHA, DST_ALPHA: GL.DST_ALPHA, ONE_MINUS_DST_ALPHA: GL.ONE_MINUS_DST_ALPHA, CONSTANT_COLOR: GL.CONSTANT_COLOR, ONE_MINUS_CONSTANT_COLOR: GL.ONE_MINUS_CONSTANT_COLOR, CONSTANT_ALPHA: GL.CONSTANT_ALPHA, ONE_MINUS_CONSTANT_ALPHA: GL.ONE_MINUS_CONSTANT_ALPHA, SRC_ALPHA_SATURATE: GL.SRC_ALPHA_SATURATE } };
  
  Object.defineProperty( RenderState.prototype, "blendFunc", {
  	set: function(v)
  	{
  		if(!v || v.length != 2)
  			return;
  		this._data[10] = v[0];
  		this._data[11] = v[1];
  	},
  	get: function()
  	{
  		return this._data.subarray(10,12);
  	},
  	enumerable: false
  });
  
  Object.defineProperty( RenderState.prototype, "colorMask0", {
  	set: function(v) { this._data[12] = v ? 1 : 0; },
  	get: function() { return this._data[12] !== 0;	},
  	enumerable: true
  });
  
  Object.defineProperty( RenderState.prototype, "colorMask1", {
  	set: function(v) { this._data[13] = v ? 1 : 0; },
  	get: function() { return this._data[13] !== 0;	},
  	enumerable: true
  });
  
  Object.defineProperty( RenderState.prototype, "colorMask2", {
  	set: function(v) { this._data[14] = v ? 1 : 0; },
  	get: function() { return this._data[14] !== 0;	},
  	enumerable: true
  });
  
  Object.defineProperty( RenderState.prototype, "colorMask3", {
  	set: function(v) { this._data[15] = v ? 1 : 0; },
  	get: function() { return this._data[15] !== 0;	},
  	enumerable: true
  });
  
  Object.defineProperty( RenderState.prototype, "colorMask", {
  	set: function(v)
  	{
  		if(!v || v.length != 4)
  			return;
  		this._data[12] = v[0];
  		this._data[13] = v[1];
  		this._data[14] = v[2];
  		this._data[15] = v[3];
  	},
  	get: function()
  	{
  		return this._data.subarray(12,16);
  	},
  	enumerable: false
  });
  
  /*
  16: stencil_test: 0
  17:	stencil_mask: 0xFF,
  18:	stencil_func_func: GL.ALWAYS,
  19:	stencil_func_ref: 0,
  20:	stencil_func_mask: 0xFF,
  21:	stencil_op_sfail: GL.KEEP,
  22:	stencil_op_dpfail: GL.KEEP,
  23:	stencil_op_dppass: GL.KEEP
  */
  
  Object.defineProperty( RenderState.prototype, "stencil_test", {
  	set: function(v) { this._data[16] = v ? 1 : 0; },
  	get: function() { return this._data[16] !== 0;	},
  	enumerable: true
  });
  
  Object.defineProperty( RenderState.prototype, "stencil_mask", {
  	set: function(v) { this._data[17] = v; },
  	get: function() { return this._data[17]; },
  	enumerable: true
  });
  
  Object.defineProperty( RenderState.prototype, "skip_blend", {
  	set: function(v) { this._data[25] = v ? (this._data[25] | RenderState.SKIP_BLEND) : (this._data[25] & ~(RenderState.SKIP_BLEND)); },
  	get: function() { return Boolean(this._data[25] & RenderState.SKIP_BLEND); },
  	enumerable: true
  });
  
  Object.defineProperty( RenderState.prototype, "skip_depth", {
  	set: function(v) { this._data[25] = v ? (this._data[25] | RenderState.SKIP_DEPTH) : (this._data[25] & ~(RenderState.SKIP_DEPTH)); },
  	get: function() { return Boolean(this._data[25] & RenderState.SKIP_DEPTH); },
  	enumerable: true
  });
  
  Object.defineProperty( RenderState.prototype, "skip_stencil", {
  	set: function(v) { this._data[25] = v ? (this._data[25] | RenderState.SKIP_STENCIL) : (this._data[25] & ~(RenderState.SKIP_STENCIL)); },
  	get: function() { return Boolean(this._data[25] & RenderState.SKIP_STENCIL); },
  	enumerable: true
  });
  
  
  RenderState["@stencil_mask"] = { widget: "number", min: 0, max: 256, step: 1, precision: 0 };
  
  Object.defineProperty( RenderState.prototype, "stencil_func", {
  	set: function(v) {
  		if(!v || v.length != 3)
  			return;
  		this._data[18] = v[0];
  		this._data[19] = v[1];
  		this._data[20] = v[2];
  	},
  	get: function() { return this._data.subarray(18,21); },
  	enumerable: false
  });
  
  Object.defineProperty( RenderState.prototype, "stencil_func_func", {
  	set: function(v) { this._data[18] = v; },
  	get: function() { return this._data[18]; },
  	enumerable: true
  });
  
  RenderState["@stencil_func_func"] = { widget: "combo", values: { LESS: GL.LESS, LEQUAL: GL.LEQUAL, EQUAL: GL.EQUAL, NOTEQUAL: GL.NOTEQUAL, GREATER: GL.GREATER, GEQUAL: GL.GEQUAL, ALWAYS: GL.ALWAYS, NEVER: GL.NEVER } };
  
  Object.defineProperty( RenderState.prototype, "stencil_func_ref", {
  	set: function(v) { this._data[19] = v; },
  	get: function() { return this._data[19]; },
  	enumerable: true
  });
  
  RenderState["@stencil_func_ref"] = { widget: "number", min: 0, max: 256, step: 1, precision: 0 };
  
  Object.defineProperty( RenderState.prototype, "stencil_func_mask", {
  	set: function(v) { this._data[20] = v; },
  	get: function() { return this._data[20]; },
  	enumerable: true
  });
  
  RenderState["@stencil_func_mask"] = { widget: "number", min: 0, max: 256, step: 1, precision: 0 };
  
  Object.defineProperty( RenderState.prototype, "stencil_op", {
  	set: function(v) {
  		if(!v || v.length != 3)
  			return;
  		this._data[21] = v[0];
  		this._data[22] = v[1];
  		this._data[23] = v[2];
  	},
  	get: function() { return this._data.subarray(21,24); },
  	enumerable: false
  });
  
  Object.defineProperty( RenderState.prototype, "stencil_op_sfail", {
  	set: function(v) { this._data[21] = v; },
  	get: function() { return this._data[21]; },
  	enumerable: true
  });
  
  RenderState["@stencil_op_sfail"] = { widget: "combo", values: { KEEP: GL.KEEP, ZERO: GL.ZERO, REPLACE: GL.REPLACE, INCR: GL.INCR, INCR_WRAP: GL.INCR_WRAP, DECR: GL.DECR_WRAP, INVERT: GL.INVERT } };
  
  Object.defineProperty( RenderState.prototype, "stencil_op_dpfail", {
  	set: function(v) { this._data[22] = v; },
  	get: function() { return this._data[22]; },
  	enumerable: true
  });
  
  RenderState["@stencil_op_dpfail"] = { widget: "combo", values: { KEEP: GL.KEEP, ZERO: GL.ZERO, REPLACE: GL.REPLACE, INCR: GL.INCR, INCR_WRAP: GL.INCR_WRAP, DECR: GL.DECR_WRAP, INVERT: GL.INVERT } };
  
  Object.defineProperty( RenderState.prototype, "stencil_op_dppass", {
  	set: function(v) { this._data[23] = v; },
  	get: function() { return this._data[23]; },
  	enumerable: true
  });
  
  Object.defineProperty( RenderState.prototype, "flags", {
  	set: function(v) { this._data[24] = v; },
  	get: function() { return this._data[24]; },
  	enumerable: true
  });
  
  RenderState["@stencil_op_dppass"] = { widget: "combo", values: { KEEP: GL.KEEP, ZERO: GL.ZERO, REPLACE: GL.REPLACE, INCR: GL.INCR, INCR_WRAP: GL.INCR_WRAP, DECR: GL.DECR_WRAP, INVERT: GL.INVERT } };
  
  RenderState.default_state = {
  	front_face: GL.CCW,
  	cull_face: true,
  	depth_test: true,
  	depth_func: GL.LESS,
  	depth_mask: true,
  	blend: false,
  	blendFunc0: GL.SRC_ALPHA,
  	blendFunc1: GL.ONE_MINUS_SRC_ALPHA,
  	colorMask0: true,
  	colorMask1: true,
  	colorMask2: true,
  	colorMask3: true,
  	stencil_test: false,
  	stencil_mask: 0xFF,
  	stencil_func_func: GL.ALWAYS,
  	stencil_func_ref: 0,
  	stencil_func_mask: 0xFF,
  	stencil_op_sfail: GL.KEEP,
  	stencil_op_dpfail: GL.KEEP,
  	stencil_op_dppass: GL.KEEP
  };
  
  RenderState.last_state = null;
  
  RenderState.prototype.init = function()
  {
  	//gpu flags
  	this.front_face = GL.CCW;
  	this.cull_face = true;
  	//this.cull_face_mode = GL.BACK;
  
  	//depth buffer
  	this.depth_test = true;
  	this.depth_mask = true; //write in depth buffer
  	this.depth_func = GL.LESS;
  	//depth range: never used
  
  	//blend function
  	this.blend = false;
  	this.blendFunc0 = GL.SRC_ALPHA;
  	this.blendFunc1 = GL.ONE_MINUS_SRC_ALPHA;
  	//blend equation
  
  	//color mask
  	this.colorMask0 = true;
  	this.colorMask1 = true;
  	this.colorMask2 = true;
  	this.colorMask3 = true;
  
  	//stencil buffer
  	this.stencil_test = false;
  	this.stencil_mask = 0xFF;
  	this.stencil_func_func = GL.ALWAYS;
  	this.stencil_func_ref = 0;
  	this.stencil_func_mask = 0xFF;
  	this.stencil_op_sfail = GL.KEEP;
  	this.stencil_op_dpfail = GL.KEEP;
  	this.stencil_op_dppass = GL.KEEP;
  
  	this.flags = 0;
  }
  
  //helper, allows to set the blend mode from a string
  RenderState.prototype.setBlendMode = function( mode )
  {
  	var functions = LS.BlendFunctions[ mode ];
  	if(!mode || mode == LS.Blend.NORMAL )
  	{
  		this.blend = false;
  		return;
  	}
  
  	this.blend = true;
  	this.blendFunc0 = mode[0];
  	this.blendFunc1 = mode[1];
  }
  
  RenderState.prototype.enable = function()
  {
  	RenderState.enable( this );
  }
  
  RenderState.enable = function( state, prev )
  {
  	var flags = state.flags;
  
  	if(!prev)
  	{
  		//faces
  		gl.frontFace( state.front_face );
  		if(state.cull_face)
  			gl.enable( gl.CULL_FACE );
  		else
  			gl.disable( gl.CULL_FACE );
  
  		//depth
  		if( !(flags & RenderState.SKIP_DEPTH) )
  		{
  			if(state.depth_test)
  				gl.enable( gl.DEPTH_TEST );
  			else
  				gl.disable( gl.DEPTH_TEST );
  			gl.depthMask( state.depth_mask );
  			gl.depthFunc( state.depth_func );
  		}
  
  		//blend
  		if( !(flags & RenderState.SKIP_BLEND) )
  		{
  			if(state.blend)
  				gl.enable( gl.BLEND );
  			else
  				gl.disable( gl.BLEND );
  			gl.blendFunc( state.blendFunc0, state.blendFunc1 );
  		}
  
  		//color
  		gl.colorMask( state.colorMask0, state.colorMask1, state.colorMask2, state.colorMask3 );
  
  		//stencil
  		if( !(flags & RenderState.SKIP_STENCIL) )
  		{
  			if(state.stencil_test)
  			{
  				gl.enable( gl.STENCIL_TEST );
  				gl.stencilFunc( state.stencil_func_func, state.stencil_func_ref, state.stencil_func_mask );
  				gl.stencilOp( state.stencil_op_sfail, state.stencil_op_dpfail, state.stencil_op_dppass );
  				gl.stencilMask( state.stencil_mask );
  			}
  			else
  				gl.disable( gl.STENCIL_TEST );
  		}
  
  		this.last_state = state;
  		return;
  	}
  
  	//faces
  	if(prev.front_face !== state.front_face)
  		gl.frontFace( state.front_face );
  	if(prev.cull_face !== state.cull_face)
  	{
  		if(state.cull_face)
  			gl.enable( gl.CULL_FACE );
  		else
  			gl.disable( gl.CULL_FACE );
  	}
  
  	//depth
  	if( !(flags & RenderState.SKIP_DEPTH) )
  	{
  		if(prev.depth_test !== state.depth_test)
  		{
  			if(state.depth_test)
  				gl.enable( gl.DEPTH_TEST );
  			else
  				gl.disable( gl.DEPTH_TEST );
  		}
  		if(prev.depth_mask !== state.depth_mask)
  			gl.depthMask( state.depth_mask );
  		if(prev.depth_func !== state.depth_func)
  			gl.depthFunc( state.depth_func );
  	}
  
  	//blend
  	if( !(flags & RenderState.SKIP_BLEND) )
  	{
  		if(prev.blend !== state.blend)
  		{
  			if(state.blend)
  				gl.enable( gl.BLEND );
  			else
  				gl.disable( gl.BLEND );
  		}
  		if(prev.blendFunc0 !== state.blendFunc0 || prev.blendFunc1 !== state.blendFunc1)
  			gl.blendFunc( state.blendFunc0, state.blendFunc1 );
  	}
  
  	//color
  	if(prev.colorMask0 !== state.colorMask0 || prev.colorMask1 !== state.colorMask1 || prev.colorMask2 !== state.colorMask2 || prev.colorMask3 !== state.colorMask3 )
  		gl.colorMask( state.colorMask0, state.colorMask1, state.colorMask2, state.colorMask3 );
  
  	//stencil
  	if( !(flags & RenderState.SKIP_STENCIL) )
  	{
  		if(prev.stencil_test != state.stencil_test )
  		{
  			if(state.stencil_test)
  				gl.enable( gl.STENCIL_TEST);
  			else
  				gl.disable( gl.STENCIL_TEST );
  		}
  
  		if(state.stencil_test)
  		{
  			if( state.stencil_func_func !== prev.stencil_func_func || state.stencil_func_ref !== prev.stencil_func_ref || state.stencil_func_mask !== prev.stencil_func_mask )
  				gl.stencilFunc( state.stencil_func_func, state.stencil_func_ref, state.stencil_func_mask );
  
  			if(state.stencil_op_sfail !== prev.stencil_op_sfail || state.stencil_op_dpfail !== stencil_op_dpfail || state.stencil_op_dppass !== stencil_op_dppass )
  				gl.stencilOp( state.stencil_op_sfail, state.stencil_op_dpfail, state.stencil_op_dppass );
  
  			if(state.stencil_mask !== prev.stencil_mask)
  				gl.stencilMask( prev.stencil_mask );
  		}
  	}
  
  	//save state
  	this.last_state = state;
  }
  
  RenderState.reset = function()
  {
  	this.enable( this.default_state );
  }
  
  RenderState.prototype.serialize = function()
  {
  	return LS.cloneObject(this);
  }
  
  RenderState.prototype.toJSON = RenderState.prototype.serialize;
  
  RenderState.prototype.configure = function(o)
  {
  	LS.cloneObject(o,this);
  }
  
  RenderState.prototype.copyFrom = function( rs )
  {
  	this._data.set( rs._data );
  }
  
  
  LS.RenderState = RenderState;