///@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;