mirror of
https://github.com/scratchfoundation/scratch-render.git
synced 2025-08-28 22:30:04 -04:00
Refactor ShaderManager's DRAW_MODE and effect info
Draw modes are now enabled/disabled by bitmask and can be combined. Combining silhouette mode and color mask mode will be useful for color-touching-color, for example. Effect converters are now one field in a larger EFFECT_INFO map, which also contains bitmask values for each effect. Drawable no longer makes any assumptions regarding how ShaderManager will unpack the effect bits. The shader cache is now an array of arrays, where the outer index is the mask of active draw modes and the inner index is the mask of active effects (as it was before). Effects which cannot impact the shape of a Drawable are masked out in silhouette mode in order to avoid compiling redundant shaders.
This commit is contained in:
parent
bb357ba8bc
commit
91cc797c3b
2 changed files with 94 additions and 46 deletions
|
@ -47,7 +47,7 @@ function Drawable(gl) {
|
|||
var numEffects = ShaderManager.EFFECTS.length;
|
||||
for (var index = 0; index < numEffects; ++index) {
|
||||
var effectName = ShaderManager.EFFECTS[index];
|
||||
var converter = ShaderManager.EFFECT_VALUE_CONVERTER[effectName];
|
||||
var converter = ShaderManager.EFFECT_INFO[effectName].converter;
|
||||
this._uniforms['u_' + effectName] = converter(0);
|
||||
}
|
||||
|
||||
|
@ -314,18 +314,18 @@ Drawable.prototype.updateProperties = function (properties) {
|
|||
}
|
||||
var numEffects = ShaderManager.EFFECTS.length;
|
||||
for (var index = 0; index < numEffects; ++index) {
|
||||
var propertyName = ShaderManager.EFFECTS[index];
|
||||
if (propertyName in properties) {
|
||||
var rawValue = properties[propertyName];
|
||||
var mask = 1 << index;
|
||||
var effectName = ShaderManager.EFFECTS[index];
|
||||
if (effectName in properties) {
|
||||
var rawValue = properties[effectName];
|
||||
var effectInfo = ShaderManager.EFFECT_INFO[effectName];
|
||||
if (rawValue != 0) {
|
||||
this._effectBits |= mask;
|
||||
this._effectBits |= effectInfo.mask;
|
||||
}
|
||||
else {
|
||||
this._effectBits &= ~mask;
|
||||
this._effectBits &= ~effectInfo.mask;
|
||||
}
|
||||
var converter = ShaderManager.EFFECT_VALUE_CONVERTER[propertyName];
|
||||
this._uniforms['u_' + propertyName] = converter(rawValue);
|
||||
var converter = effectInfo.converter;
|
||||
this._uniforms['u_' + effectName] = converter(rawValue);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -334,7 +334,7 @@ Drawable.prototype.updateProperties = function (properties) {
|
|||
* Set the dimensions of this Drawable's skin.
|
||||
* @param {int} width The width of the new skin.
|
||||
* @param {int} height The height of the new skin.
|
||||
* @param {int} costumeResolution The resolution to use for this skin.
|
||||
* @param {int} [costumeResolution] The resolution to use for this skin.
|
||||
* @private
|
||||
*/
|
||||
Drawable.prototype._setSkinSize = function (width, height, costumeResolution) {
|
||||
|
|
|
@ -5,49 +5,84 @@ function ShaderManager(gl) {
|
|||
|
||||
/**
|
||||
* The cache of all shaders compiled so far. These are generated on demand.
|
||||
* @type {Object.<ShaderManager.DRAW_MODE, Array.<module:twgl.ProgramInfo>>}
|
||||
* The outer index represents the draw mode and the inner index the effects.
|
||||
* @type {Array.<Array.<module:twgl.ProgramInfo>>}
|
||||
* @private
|
||||
*/
|
||||
this._shaderCache = {};
|
||||
for (var modeName in ShaderManager.DRAW_MODE) {
|
||||
if (ShaderManager.DRAW_MODE.hasOwnProperty(modeName)) {
|
||||
this._shaderCache[modeName] = [];
|
||||
this._shaderCache = [];
|
||||
|
||||
var drawModes = Object.keys(ShaderManager.DRAW_MODE);
|
||||
var shaderCache = this._shaderCache;
|
||||
function initCache(index, mask) {
|
||||
if (index >= drawModes.length) {
|
||||
shaderCache[mask] = shaderCache[mask] || [];
|
||||
}
|
||||
else {
|
||||
var drawModeName = drawModes[index];
|
||||
|
||||
// Fill high index first to potentially reduce array resize count
|
||||
initCache(index+1, mask | ShaderManager.DRAW_MODE[drawModeName]);
|
||||
initCache(index+1, mask);
|
||||
}
|
||||
}
|
||||
initCache(0, 0);
|
||||
}
|
||||
|
||||
module.exports = ShaderManager;
|
||||
|
||||
/**
|
||||
* Mapping of each effect to a conversion function. The conversion function
|
||||
* takes a Scratch value (generally in the range 0..100 or -100..100) and maps
|
||||
* it to a value useful to the shader. This may not be reversible.
|
||||
* @type {Object.<string,function>}
|
||||
* @private
|
||||
* Mapping of each effect name to info about that effect.
|
||||
* The info includes:
|
||||
* - The bit in 'effectBits' representing the effect.
|
||||
* - A conversion function which takes a Scratch value (generally in the range
|
||||
* 0..100 or -100..100) and maps it to a value useful to the shader. This
|
||||
* mapping may not be reversible.
|
||||
* @type {Object.<string,Object.<string,*>>}
|
||||
*/
|
||||
ShaderManager.EFFECT_VALUE_CONVERTER = {
|
||||
color: function(x) {
|
||||
return (x / 200) % 1;
|
||||
ShaderManager.EFFECT_INFO = {
|
||||
color: {
|
||||
mask: 1 << 0,
|
||||
converter: function(x) {
|
||||
return (x / 200) % 1;
|
||||
}
|
||||
},
|
||||
fisheye: function(x) {
|
||||
return Math.max(0, (x + 100) / 100);
|
||||
fisheye: {
|
||||
mask: 1 << 1,
|
||||
converter: function(x) {
|
||||
return Math.max(0, (x + 100) / 100);
|
||||
}
|
||||
},
|
||||
whirl: function(x) {
|
||||
return x * Math.PI / 180;
|
||||
whirl: {
|
||||
mask: 1 << 2,
|
||||
converter: function(x) {
|
||||
return x * Math.PI / 180;
|
||||
}
|
||||
},
|
||||
pixelate: function(x) {
|
||||
return Math.abs(x) / 10;
|
||||
pixelate: {
|
||||
mask: 1 << 3,
|
||||
converter: function(x) {
|
||||
return Math.abs(x) / 10;
|
||||
}
|
||||
},
|
||||
mosaic: function(x) {
|
||||
x = Math.round((Math.abs(x) + 10) / 10);
|
||||
// TODO: cap by Math.min(srcWidth, srcHeight)
|
||||
return Math.max(1, Math.min(x, 512));
|
||||
mosaic: {
|
||||
mask: 1 << 4,
|
||||
converter: function(x) {
|
||||
x = Math.round((Math.abs(x) + 10) / 10);
|
||||
// TODO: cap by Math.min(srcWidth, srcHeight)
|
||||
return Math.max(1, Math.min(x, 512));
|
||||
}
|
||||
},
|
||||
brightness: function(x) {
|
||||
return Math.max(-100, Math.min(x, 100)) / 100;
|
||||
brightness: {
|
||||
mask: 1 << 5,
|
||||
converter: function(x) {
|
||||
return Math.max(-100, Math.min(x, 100)) / 100;
|
||||
}
|
||||
},
|
||||
ghost: function(x) {
|
||||
return 1 - Math.max(0, Math.min(x, 100)) / 100;
|
||||
ghost: {
|
||||
mask: 1 << 6,
|
||||
converter: function(x) {
|
||||
return 1 - Math.max(0, Math.min(x, 100)) / 100;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -55,28 +90,28 @@ ShaderManager.EFFECT_VALUE_CONVERTER = {
|
|||
* The name of each supported effect.
|
||||
* @type {Array}
|
||||
*/
|
||||
ShaderManager.EFFECTS = Object.keys(ShaderManager.EFFECT_VALUE_CONVERTER);
|
||||
ShaderManager.EFFECTS = Object.keys(ShaderManager.EFFECT_INFO);
|
||||
|
||||
/**
|
||||
* The available draw modes.
|
||||
* The available draw modes. May be combined with bitwise OR.
|
||||
* @readonly
|
||||
* @enum {string}
|
||||
* @enum {int}
|
||||
*/
|
||||
ShaderManager.DRAW_MODE = {
|
||||
/**
|
||||
* Draw normally.
|
||||
*/
|
||||
default: 'default',
|
||||
default: 0,
|
||||
|
||||
/**
|
||||
* Draw a silhouette using a solid color.
|
||||
*/
|
||||
silhouette: 'silhouette',
|
||||
silhouette: 1 << 0,
|
||||
|
||||
/**
|
||||
* Draw only the parts of the drawable which match a particular color.
|
||||
*/
|
||||
colorMask: 'colorMask'
|
||||
colorMask: 1 << 1
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -88,6 +123,13 @@ ShaderManager.DRAW_MODE = {
|
|||
*/
|
||||
ShaderManager.prototype.getShader = function (drawMode, effectBits) {
|
||||
var cache = this._shaderCache[drawMode];
|
||||
if ((drawMode & ShaderManager.DRAW_MODE.silhouette) != 0) {
|
||||
// Silhouette mode isn't affected by these effects.
|
||||
effectBits &= ~(
|
||||
ShaderManager.EFFECT_INFO.color.mask |
|
||||
ShaderManager.EFFECT_INFO.brightness.mask
|
||||
);
|
||||
}
|
||||
var shader = cache[effectBits];
|
||||
if (!shader) {
|
||||
shader = cache[effectBits] = this._buildShader(drawMode, effectBits);
|
||||
|
@ -105,9 +147,15 @@ ShaderManager.prototype.getShader = function (drawMode, effectBits) {
|
|||
ShaderManager.prototype._buildShader = function (drawMode, effectBits) {
|
||||
var numEffects = ShaderManager.EFFECTS.length;
|
||||
|
||||
var defines = [
|
||||
'#define DRAW_MODE_' + drawMode
|
||||
];
|
||||
var defines = [];
|
||||
for (var drawModeName in ShaderManager.DRAW_MODE) {
|
||||
if (ShaderManager.DRAW_MODE.hasOwnProperty(drawModeName)) {
|
||||
var mask = ShaderManager.DRAW_MODE[drawModeName];
|
||||
if ((drawMode & mask) != 0) {
|
||||
defines.push('#define DRAW_MODE_' + drawModeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var index = 0; index < numEffects; ++index) {
|
||||
if ((effectBits & (1 << index)) != 0) {
|
||||
defines.push('#define ENABLE_' + ShaderManager.EFFECTS[index]);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue