pixelbox
Version:
A framework to fast-prototype pixel-based games
187 lines (147 loc) • 8.83 kB
JavaScript
var createCanvas = require('../domUtils/createCanvas');
var pixelbox = require('..');
var settings = pixelbox.settings;
var SCREEN_WIDTH = settings.screen.width;
var SCREEN_HEIGHT = settings.screen.height;
var PIXEL_WIDTH = settings.screen.pixelSize.width;
var PIXEL_HEIGHT = settings.screen.pixelSize.height;
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
// replace default Pixelbox 2d canvas for a WebGL canvas
var canvas = createCanvas(SCREEN_WIDTH, SCREEN_HEIGHT);
canvas.style.width = SCREEN_WIDTH * PIXEL_WIDTH + 'px';
canvas.style.height = SCREEN_HEIGHT * PIXEL_HEIGHT + 'px';
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
// init webGL
var gl = canvas.getContext('webgl', {
antialias: false, // that indicates whether or not to perform anti-aliasing.
alpha: false, // that indicates if the canvas contains an alpha buffer.
depth: false, // that indicates that the drawing buffer has a depth buffer of at least 16 bits.
failIfMajorPerformanceCaveat: false, // that indicates if a context will be created if the system performance is low.
powerPreference: 'default', // A hint to the user agent indicating what configuration of GPU is suitable for the WebGL context. Possible values are:
premultipliedAlpha: false, // that indicates that the page compositor will assume the drawing buffer contains colors with pre-multiplied alpha.
preserveDrawingBuffer: false, // If the value is true the buffers will not be cleared and will preserve their values until cleared or overwritten by the author.
stencil: false, // that indicates that the drawing buffer has a stencil buffer of at least 8 bits.
});
gl.viewport(0, 0, canvas.width, canvas.height);
gl.enable(gl.BLEND);
gl.disable(gl.DEPTH_TEST); // depth testing
gl.depthMask(false);
// gl.depthFunc(gl.LEQUAL); // Near things obscure far things
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
var DUMMY_PROGRAM = null; // TODO
var programs = {};
var _currentProgram = DUMMY_PROGRAM;
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
function _createShader(str, type) {
var shader = gl.createShader(type);
gl.shaderSource(shader, str);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw new Error(gl.getShaderInfoLog(shader));
}
return shader;
}
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
function createProgram(id, vert, frag) {
if (id && programs[id]) return programs[id];
var vertShader = _createShader(vert, gl.VERTEX_SHADER);
var fragShader = _createShader(frag, gl.FRAGMENT_SHADER);
var program = gl.createProgram();
gl.attachShader(program, vertShader);
gl.attachShader(program, fragShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
gl.deleteProgram(program);
throw new Error(gl.getProgramInfoLog(program));
}
// register program in pool
if (id) programs[id] = program;
// TODO: Use the program and construct Uniforms and Attributes
// (so we don't have to do gl.getAttribLocation each time)
return program;
}
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
function useProgram(program) {
// don't call if it same program as previous draw call
if (_currentProgram === program) return program;
if (_currentProgram !== null) {
// switch attributes
// Gets the number of attributes in the current and new programs
var currentAttributes = gl.getProgramParameter(_currentProgram, gl.ACTIVE_ATTRIBUTES);
var newAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
// Fortunately, in OpenGL, attribute index values are always assigned in the
// range [0, ..., NUMBER_OF_VERTEX_ATTRIBUTES - 1], so we can use that to
// enable or disable attributes
if (newAttributes > currentAttributes) {
// We need to enable the missing attributes
for (var i = currentAttributes; i < newAttributes; i++) {
gl.enableVertexAttribArray(i);
}
} else if (newAttributes < currentAttributes) {
// We need to disable the extra attributes
for (var i = newAttributes; i < currentAttributes; i++) {
gl.disableVertexAttribArray(i);
}
}
}
// switch program
_currentProgram = program;
gl.useProgram(program);
return program;
}
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
// function enableAttribute(program, variableName) {
// var location = gl.getAttribLocation(program, variableName);
// // gl.enableVertexAttribArray(location); // TODO: already done in `context.useProgram`
// return location;
// }
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
var _textureMap = new WeakMap();
function getTextureFromImage(image) {
// var texture = image._glTexture;
var texture = _textureMap.get(image);
if (texture) return texture;
if (DEBUG) console.log('Create texture for image', image)
texture = gl.createTexture();
// same as bindBuffer, once a texture is bind, all textures operations are done on this one.
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
// upload the texture to the GPU
gl.texImage2D(
gl.TEXTURE_2D, // binding point (target) of the active texture
0, // level of detail. Level 0 is the base image level and level n is the nth mipmap reduction level
gl.RGBA, // internalformat specifying the color components in the texture
gl.RGBA, // format of the texel data. In WebGL 1, this must be the same as internalformat
gl.UNSIGNED_BYTE, // data type of the texel data
image // pixels
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); // NEAREST, LINEAR, LINEAR_MIPMAP_LINEAR, NEAREST_MIPMAP_NEAREST, NEAREST_MIPMAP_LINEAR, LINEAR_MIPMAP_NEAREST
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); // CLAMP_TO_EDGE, REPEAT, MIRRORED_REPEAT
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
// image._glTexture = texture;
_textureMap.set(image, texture);
return texture;
}
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
function bindTexture(program, channel, uniformId, index) {
var texture;
if (channel._isGlTexture) {
// texture is a frameBuffer
texture = channel.ctx;
} else {
// We assume normal image
texture = getTextureFromImage(channel);
}
gl.activeTexture(gl.TEXTURE0 + index);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.uniform1i(gl.getUniformLocation(program, uniformId), index);
}
//▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
exports.canvas = canvas;
exports.gl = gl;
exports.programs = programs;
exports.createProgram = createProgram;
exports.useProgram = useProgram;
exports.bindTexture = bindTexture;
exports.getTextureFromImage = getTextureFromImage;