Skip to content

Instantly share code, notes, and snippets.

@deniska
Last active January 18, 2017 00:23
Show Gist options
  • Save deniska/ebf2f1e1194eeaeb9f63bef8f8d0c7c6 to your computer and use it in GitHub Desktop.
Save deniska/ebf2f1e1194eeaeb9f63bef8f8d0c7c6 to your computer and use it in GitHub Desktop.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Hello world</title>
</head>
<body>
<canvas id="c" width="640" height="480"></canvas>
<script src="lib/twgl-full.js"></script>
<script src="texturebatch.js"></script>
<script>
var gl = twgl.getWebGLContext(document.getElementById("c"));
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
var batch = new TextureBatch(gl);
var width = 10;
var aspect = gl.canvas.height / gl.canvas.width;
var height = width*aspect;
var cam = twgl.m4.ortho(-0, width, 0, width*aspect, -10, 10);
batch.setCamera(cam);
var ta = TextureAtlas.fromPackFileURL(
gl, 'output/pack.atlas', done
);
function done() {
console.log(ta);
requestAnimationFrame(render);
}
function render(time) {
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0.7, 0.7, 0.7, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
batch.begin();
batch.draw(ta.get('star'),
{x: 1, y: 1, w: 2, h: 2, r: 0.3});
batch.draw(ta.get('circle'),
{x: 7, y: 5, w: 2, h: 2, r: 0.3});
var aspect = ta.get('hello').aspect;
batch.draw(ta.get('hello'),
{x: width/2, y: height/2,
w: 6, h: 6 * aspect,
ox: 3, oy: 3 * aspect,
r: -0.3 + 0.1 * Math.sin(time/300)
});
batch.end();
requestAnimationFrame(render);
}
</script>
</body>
</html>
'use strict';
var vertshader = [
'precision lowp float;',
'uniform mat4 u_camera;',
'attribute vec2 vertcoord;',
'attribute vec2 texcoord;',
'attribute vec4 tintcolor;',
'varying vec2 v_texcoord;',
'varying vec4 v_tintcolor;',
'void main(void) {',
' v_texcoord = texcoord;',
' v_tintcolor = tintcolor;',
' gl_Position = u_camera * vec4(vertcoord, 0.0, 1.0);',
'}'
].join('\n');
var fragshader = [
'precision lowp float;',
'varying vec2 v_texcoord;',
'varying vec4 v_tintcolor;',
'uniform sampler2D u_texture;',
'void main(void) {',
' gl_FragColor = texture2D(u_texture, v_texcoord) * v_tintcolor;',
'}'
].join('\n');
// TextureRegion
// -------------
function TextureRegion(texture) {
this.texture = texture;
this.u1 = 0;
this.v1 = 0;
this.u2 = 1;
this.v2 = 1;
this.aspect = 1;
}
// TextureAtlas
// ------------
function TextureAtlas(gl) {
this.regions = {};
this.gl = gl;
}
TextureAtlas.prototype.get = function (name) {
return this.regions[name];
};
TextureAtlas.fromPackFileURL = function (gl, url, done) {
var atlas = new TextureAtlas(gl);
var req = new XMLHttpRequest();
var urlPath = url.split('/');
urlPath.pop();
var baseUrl = urlPath.join('/');
var loadedTextures;
var textures = {};
var data;
req.onreadystatechange = function () {
if (req.readyState === 4 && req.status === 200) {
data = packFileToObj(req.responseText);
for (var k in data) {
var v = data[k];
textures[k] = {
src: baseUrl + '/' + k
};
}
loadedTextures = twgl.createTextures(gl, textures, onload);
console.log(textures);
}
};
var onload = function () {
for (var k in data) {
var v = data[k];
for (var regname in v.regions) {
var reg = v.regions[regname];
var region = new TextureRegion(loadedTextures[k]);
region.u1 = reg.xy[0] / v.size[0];
region.v1 = reg.xy[1] / v.size[1];
region.u2 = (reg.xy[0] + reg.size[0]) / v.size[0];
region.v2 = (reg.xy[1] + reg.size[1]) / v.size[1];
region.aspect = reg.size[1] / reg.size[0];
atlas.regions[regname] = region;
}
}
done();
};
req.open('GET', url, true);
req.send();
return atlas;
};
function packFileToObj(text) {
var lines = text.split('\n');
var newFile = false;
var line;
var data = {};
var currentFile;
var currentProp;
var parts;
var prop;
var parsedProp;
for (var i = 0; i < lines.length; i++) {
line = lines[i].trim();
if (line === '') {
newFile = true;
} else if (newFile) {
newFile = false;
currentFile = {
regions: {}
};
currentProp = currentFile;
data[line] = currentFile;
} else if (line.search(':') !== -1) {
parts = line.split(':');
name = parts[0].trim();
prop = parts[1].trim();
parsedProp = parseProp(prop);
currentProp[name] = parsedProp;
} else {
var region = {};
currentFile.regions[line] = region;
currentProp = region;
}
}
return data;
};
function parseProp(prop) {
if (prop === 'true') {
return true;
} else if (prop === 'false') {
return false;
} else if (prop.search(',') !== -1) {
var parts = prop.split(',');
parts[0] = parts[0].trim();
parts[1] = parts[1].trim();
if (isNaN(parseFloat(parts[0]))) {
return parts;
} else {
return [parseFloat(parts[0]), parseFloat(parts[1])];
}
}
return prop;
}
// TextureBatch
// ------------
function TextureBatch(gl) {
this.gl = gl;
this.uniforms = {
u_texture: null,
u_camera: null
};
this.arrays = {
vertcoord: {numComponents: 2, data: []},
texcoord: {numComponents: 2, data: []},
tintcolor: {numComponents: 4, data: []}
};
this.initialized = false;
this.active = false;
this.programInfo = twgl.createProgramInfo(gl, [vertshader, fragshader]);
this.maxvertex = 10000;
}
TextureBatch.prototype.setCamera = function (cam) {
this.uniforms.u_camera = cam;
};
TextureBatch.prototype.draw = function (drawable, obj) {
var o = Object.assign({
x: 0, y: 0,
w: 1, h: 1,
ox: 0, oy: 0,
r: 0
}, obj);
if (drawable.texture !== this.uniforms.u_texture) {
this.uniforms.u_texture = drawable.texture;
this.flush();
}
var tr = drawable;
var sinr = Math.sin(o.r);
var cosr = Math.cos(o.r);
var x1, y1, x2, y2, x3, y3, x4, y4;
x1 = cosr * (-o.ox) - sinr * (-o.oy) + o.x
y1 = sinr * (-o.ox) + cosr * (-o.oy) + o.y
x2 = cosr * (-o.ox + o.w) - sinr * (-o.oy) + o.x
y2 = sinr * (-o.ox + o.w) + cosr * (-o.oy) + o.y
x3 = cosr * (-o.ox) - sinr * (-o.oy + o.h) + o.x
y3 = sinr * (-o.ox) + cosr * (-o.oy + o.h) + o.y
x4 = cosr * (-o.ox + o.w) - sinr * (-o.oy + o.h) + o.x
y4 = sinr * (-o.ox + o.w) + cosr * (-o.oy + o.h) + o.y
this.arrays.vertcoord.data.push(x1, y1, x2, y2, x3, y3);
this.arrays.vertcoord.data.push(x2, y2, x4, y4, x3, y3);
var u1 = tr.u1;
var v1 = tr.v1;
var u2 = tr.u2;
var v2 = tr.v2;
this.arrays.texcoord.data.push(u1, v2, u2, v2, u1, v1);
this.arrays.texcoord.data.push(u2, v2, u2, v1, u1, v1);
//todo: tinting
this.arrays.tintcolor.data.push(1, 1, 1, 1);
this.arrays.tintcolor.data.push(1, 1, 1, 1);
this.arrays.tintcolor.data.push(1, 1, 1, 1);
this.arrays.tintcolor.data.push(1, 1, 1, 1);
this.arrays.tintcolor.data.push(1, 1, 1, 1);
this.arrays.tintcolor.data.push(1, 1, 1, 1);
if (this.arrays.vertcoord.data.length > this.maxvertex) {
this.flush();
}
};
TextureBatch.prototype.flush = function () {
var gl = this.gl;
var attribs;
var arrays = this.arrays;
if (arrays.vertcoord.data.length === 0) {
return;
}
if (!this.initialized) {
this.bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
this.initialized = true;
} else {
attribs = this.bufferInfo.attribs;
twgl.setAttribInfoBufferFromArray(
gl, attribs.vertcoord, arrays.vertcoord
);
twgl.setAttribInfoBufferFromArray(
gl, attribs.texcoord, arrays.texcoord
);
twgl.setAttribInfoBufferFromArray(
gl, attribs.tintcolor, arrays.tintcolor
);
}
gl.useProgram(this.programInfo.program);
twgl.setBuffersAndAttributes(gl, this.programInfo, this.bufferInfo);
twgl.setUniforms(this.programInfo, this.uniforms);
twgl.drawBufferInfo(gl, gl.TRIANGLES, this.bufferInfo);
arrays.vertcoord.data.length = 0;
arrays.texcoord.data.length = 0;
arrays.tintcolor.data.length = 0;
};
TextureBatch.prototype.begin = function () {
this.active = true;
};
TextureBatch.prototype.end = function () {
this.flush()
this.active = false;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment