Skip to content

Instantly share code, notes, and snippets.

@MLaszczewski
Created August 22, 2016 09:04
Show Gist options
  • Save MLaszczewski/a22687fe3833acbea57bda3af5a8fd04 to your computer and use it in GitHub Desktop.
Save MLaszczewski/a22687fe3833acbea57bda3af5a8fd04 to your computer and use it in GitHub Desktop.
var gfx = require("./gfx.js")
var fastMath = require('./fastMath.js')
var sintab = fastMath.sintab
var costab = fastMath.costab
var STNode=function(x,y,width,height) {
this.left = null
this.right = null
this.image = null
this.x = x
this.y = y
this.width = width
this.height = height
}
STNode.prototype.insert = function(image,spacing) {
var box_width=image.width + spacing
var box_height=image.height + spacing
if(this.left && this.right) {
return this.left.insert(image,spacing) || this.right.insert(image,spacing)
} else {
// if there's already a sprite here, return
if(this.image) return null;
// if we're too small, return
if((box_width>this.width) || ((box_height)>this.height)) return null
if((box_width==this.width) && ((box_height)==this.height)) { //if we're just right, accept
this.image=image
return this
} else { // otherwise, gotta split this node and create some kids
var dw = this.width - (box_width)
var dh = this.height - (box_height)
if(dw>dh) {
this.left = new STNode(this.x, this.y, box_width, this.height)
this.right = new STNode(this.x+box_width, this.y, this.width-(box_width), this.height)
} else {
this.left = new STNode(this.x, this.y, this.width, box_height)
this.right = new STNode(this.x, this.y+box_height, this.width, this.height-(box_height))
}
// insert into first child we created
return this.left.insert(image,spacing)
}
}
}
var AutoSpritesTexture = function(gl,size,spacing) {
this.gl = gl
this.size = size
this.canvas = document.createElement('canvas')
this.canvas.width = this.size
this.canvas.height = this.size
this.ctx = this.canvas.getContext('2d')
this.sprites = []
this.spacing = spacing
this.needUpload = false
var gl = this.gl
this.texture = gl.createTexture()
gl.bindTexture(gl.TEXTURE_2D, this.texture)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR)
this.shaderProgram = createSpritesShaderProgram(gl)
this.shaderProgram3d = create3dSpritesShaderProgram(gl)
this.positionBuffer = gl.createBuffer()
this.colorBuffer = gl.createBuffer()
this.coordBuffer = gl.createBuffer()
}
AutoSpritesTexture.prototype.tryPack = function(sprites) {
var nodes = new Array(sprites.length)
var root = new STNode(0,0,this.size,this.size)
sprites = sprites.sort((function(a,b) {
return Math.max(b.width,b.height)-Math.max(a.width,a.height)
}).bind(this))
for(var i=0; i< sprites.length; i++) {
var sprite = sprites[i]
var node = root.insert(sprite.image,this.spacing*2)
if(!node) return false;
nodes[i] = node
}
var fix = -2
for(var i=0; i<sprites.length; i++) {
var node = nodes[i]
var sprite = sprites[i]
sprite.coords = {
width: node.width,
height: node.height,
x: node.x,
y: node.y,
xmin: (node.x+fix)/this.size,
ymin: (node.y+fix)/this.size,
xmax: (node.x+node.width-this.spacing+fix)/this.size,
ymax: (node.y+node.height-this.spacing+fix)/this.size
}
}
this.sprites = sprites
this.needUpload = true
return true
}
AutoSpritesTexture.prototype.tryAddSprite = function(sprite) {
if(!this.tryPack(this.sprites.concat([sprite]))) {
return false;
} else {
sprite.spritesTexture = this
sprite.loaded = true
return true;
}
}
AutoSpritesTexture.prototype.isBigEnough = function(w,h) {
return (w<this.size && h<this.size)
}
AutoSpritesTexture.prototype.upload = function() {
if(!this.needUpload) return;
var ctx = this.ctx
var canvas = this.canvas
var gl = this.gl
ctx.clearRect(0, 0, canvas.width, canvas.height)
/*ctx.fillStyle="#FF00FF"
ctx.fillRect(0,0,canvas.width, canvas.height)*/
for(var i=0; i<this.sprites.length; i++) {
var sprite = this.sprites[i]
ctx.drawImage(sprite.image,sprite.coords.x,sprite.coords.y)
}
gl.bindTexture(gl.TEXTURE_2D, this.texture)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas)
gl.generateMipmap(gl.TEXTURE_2D)
for(var i=0; i<this.sprites.length; i++) {
var sprite = this.sprites[i]
sprite.texture = this.textureId
console.log("SPRITE QUAD",sprite.name,sprite.coords)
sprite.quad = [
sprite.coords.xmin,sprite.coords.ymin,
sprite.coords.xmax,sprite.coords.ymin,
sprite.coords.xmax,sprite.coords.ymax,
sprite.coords.xmin ,sprite.coords.ymax,
sprite.coords.xmin, sprite.coords.ymin,
sprite.coords.xmax,sprite.coords.ymax
]
sprite.loaded = true
}
this.needUpload = false
}
AutoSpritesTexture.prototype.removeSprite = function(sprite) {
console.log("REMOVE SPRITE",sprite.name)
this.sprites.splice(this.sprites.indexOf(sprite),1)
}
AutoSpritesTexture.prototype.bufferSprite = function(drawBuffer,sprite,x,y,angle,s,r,g,b,a) {
if(!sprite) throw new Error("SPRITE "+id+" NOT FOUND!")
var quad = sprite.quad
if(!quad) return;
var ai=Math.floor(angle*10)%3600
var ulX=x+s*sintab[(ai+3150)%3600]
var ulY=y-s*costab[(ai+3150)%3600]
var urX=x+s*sintab[(ai+450)%3600]
var urY=y-s*costab[(ai+450)%3600]
var lrX=x+s*sintab[(ai+1350)%3600]
var lrY=y-s*costab[(ai+1350)%3600]
var llX=x+s*sintab[(ai+2250)%3600]
var llY=y-s*costab[(ai+2250)%3600]
drawBuffer.addPoint(ulX,ulY,quad[0],quad[1],r,g,b,a)
drawBuffer.addPoint(urX,urY,quad[2],quad[3],r,g,b,a)
drawBuffer.addPoint(lrX,lrY,quad[4],quad[5],r,g,b,a)
drawBuffer.addPoint(llX,llY,quad[6],quad[7],r,g,b,a)
drawBuffer.addPoint(ulX,ulY,quad[8],quad[9],r,g,b,a)
drawBuffer.addPoint(lrX,lrY,quad[10],quad[11],r,g,b,a)
}
module.exports = AutoSpritesTexture
var create3dSpritesShaderProgram=function(gl) {
var vertexShader = gfx.createShader(gl,gl.VERTEX_SHADER,
[ "attribute vec3 aVertexPosition;",
"attribute vec4 aVertexColor;",
"attribute vec2 aTextureCoord;",
"uniform mat4 uCameraMatrix;",
"varying vec4 vColor;",
"varying highp vec2 vTextureCoord;",
"void main() {",
" gl_Position =uCameraMatrix*vec4(aVertexPosition, 1.0);",
" vColor = aVertexColor;",
" vTextureCoord = aTextureCoord;",
"}"
].join('\n'))
var fragmentShader = gfx.createShader(gl,gl.FRAGMENT_SHADER,
[ "precision mediump float;",
"varying vec4 vColor;",
"uniform sampler2D uSampler;",
"varying highp vec2 vTextureCoord;",
"void main() {",
" vec4 textureColor = texture2D(uSampler, vTextureCoord);",
" gl_FragColor = vColor*textureColor;",
"}"].join('\n'))
var shaderProgram = gfx.createProgram(gl,vertexShader,fragmentShader)
shaderProgram.begin=function(gl) {
gl.useProgram(shaderProgram)
shaderProgram.aVertexPosition = gl.getAttribLocation(shaderProgram, "aVertexPosition")
gl.enableVertexAttribArray(shaderProgram.aVertexPosition)
shaderProgram.aTextureCoord = gl.getAttribLocation(shaderProgram, "aTextureCoord")
gl.enableVertexAttribArray(shaderProgram.aTextureCoord)
shaderProgram.aVertexColor = gl.getAttribLocation(shaderProgram,"aVertexColor")
gl.enableVertexAttribArray(shaderProgram.aVertexColor)
shaderProgram.uCameraMatrix = gl.getUniformLocation(shaderProgram, "uCameraMatrix")
shaderProgram.uSampler = gl.getUniformLocation(shaderProgram, "uSampler")
}
shaderProgram.end=function(gl) {
gl.disableVertexAttribArray(shaderProgram.aVertexPosition)
gl.disableVertexAttribArray(shaderProgram.aVertexColor)
gl.disableVertexAttribArray(shaderProgram.aTextureCoord)
}
return shaderProgram
}
AutoSpritesTexture.prototype.beginFastDraw = function(gl,camMatrix) {
this.shaderProgram.begin(gl)
gl.uniformMatrix4fv(this.shaderProgram.uCameraMatrix, false, camMatrix);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, this.texture);
gl.uniform1i(this.shaderProgram.uSampler, 0)
}
AutoSpritesTexture.prototype.fastDrawBuffer = function(gl,drawBuffer) {
if(drawBuffer.positionsIterator==0) return;
gl.bindBuffer(gl.ARRAY_BUFFER, this.positionBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new DataView(drawBuffer.positions.buffer,0,drawBuffer.positionsIterator*4), gl.DYNAMIC_DRAW)
gl.vertexAttribPointer(this.shaderProgram.aVertexPosition, 2, gl.FLOAT, false, 0, 0)
gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new DataView(drawBuffer.colors.buffer,0,drawBuffer.colorsIterator*4), gl.DYNAMIC_DRAW)
gl.vertexAttribPointer(this.shaderProgram.aVertexColor, 4, gl.FLOAT, false, 0, 0)
gl.bindBuffer(gl.ARRAY_BUFFER, this.coordBuffer)
gl.bufferData(gl.ARRAY_BUFFER, new DataView(drawBuffer.coords.buffer,0,drawBuffer.coordsIterator*4), gl.DYNAMIC_DRAW)
gl.vertexAttribPointer(this.shaderProgram.aTextureCoord, 2, gl.FLOAT, false, 0, 0)
//console.log("ASDB",drawBuffer.pointsCount,drawBuffer.positions.length,drawBuffer.colors.length, drawBuffer.coords.length)
gl.drawArrays(gl.TRIANGLES, 0, drawBuffer.pointsCount);
}
AutoSpritesTexture.prototype.endFastDraw = function(gl) {
this.shaderProgram.end(gl)
}
var createSpritesShaderProgram=function(gl) {
var vertexShader = gfx.createShader(gl,gl.VERTEX_SHADER,
[ "attribute vec2 aVertexPosition;",
"attribute vec4 aVertexColor;",
"attribute vec2 aTextureCoord;",
"uniform mat4 uCameraMatrix;",
"varying vec4 vColor;",
"varying highp vec2 vTextureCoord;",
"void main() {",
" gl_Position =uCameraMatrix*vec4(aVertexPosition, 0.0, 1.0);",
" vColor = aVertexColor;",
" vTextureCoord = aTextureCoord;",
"}"
].join('\n'))
var fragmentShader = gfx.createShader(gl,gl.FRAGMENT_SHADER,
[ "precision mediump float;",
"varying vec4 vColor;",
"uniform sampler2D uSampler;",
"varying highp vec2 vTextureCoord;",
"void main() {",
" vec4 textureColor = texture2D(uSampler, vTextureCoord);",
" gl_FragColor = vColor*textureColor;",
"}"].join('\n'))
var shaderProgram = gfx.createProgram(gl,vertexShader,fragmentShader)
shaderProgram.begin=function(gl) {
gl.useProgram(shaderProgram)
shaderProgram.aVertexPosition = gl.getAttribLocation(shaderProgram, "aVertexPosition")
gl.enableVertexAttribArray(shaderProgram.aVertexPosition)
shaderProgram.aTextureCoord = gl.getAttribLocation(shaderProgram, "aTextureCoord")
gl.enableVertexAttribArray(shaderProgram.aTextureCoord)
shaderProgram.aVertexColor = gl.getAttribLocation(shaderProgram,"aVertexColor")
gl.enableVertexAttribArray(shaderProgram.aVertexColor)
shaderProgram.uCameraMatrix = gl.getUniformLocation(shaderProgram, "uCameraMatrix")
shaderProgram.uSampler = gl.getUniformLocation(shaderProgram, "uSampler")
}
shaderProgram.end=function(gl) {
gl.disableVertexAttribArray(shaderProgram.aVertexPosition)
gl.disableVertexAttribArray(shaderProgram.aVertexColor)
gl.disableVertexAttribArray(shaderProgram.aTextureCoord)
}
return shaderProgram
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment