Last active
January 18, 2017 00:23
-
-
Save deniska/ebf2f1e1194eeaeb9f63bef8f8d0c7c6 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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