Skip to content

Instantly share code, notes, and snippets.

@nilium
Created March 15, 2010 20:35
Show Gist options
  • Select an option

  • Save nilium/333305 to your computer and use it in GitHub Desktop.

Select an option

Save nilium/333305 to your computer and use it in GitHub Desktop.
/*
Copyright (c) 2010 Noel R. Cower
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
use glew
import glew
GLTexturePack: class {
// if the texture is lost for some reason (e.g., switching contexts), set this to 0
// or on reset it will attempt to delete a texture that doesn't exist or, worse, exists
// but isn't the one owned by the texture pack
name: UInt
root: GLPackedTexture
width, height: Int
minFilter, magFilter, format: Int
wscale, hscale: Float
minMips:Int
init: func (=width, =height, =minFilter, =magFilter, =format, =minMips) {
if (minMips < 0) {
minMips = 0;
}
wscale = 1.0 / width
hscale = 1.0 / height
reset()
root = GLPackedTexture new()
root width = width
root height = height
root x = 0
root height = 0
root owner = this
}
name: func -> UInt { name }
bind: func {
if (name == 0) {
reset()
}
glBindTexture(GL_TEXTURE_2D, name)
}
// this assumes that GL_TEXTURE_2D is enabled
reset: func {
glDeleteTextures(1, name&)
name = 0
w := width
h := height
prevname: Int = 0
glGetIntegerv(GL_TEXTURE_BINDING_2D, prevname&)
glGenTextures(1, name&)
glBindTexture(GL_TEXTURE_2D, name)
glTexImage2D(GL_PROXY_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, null)
proxyw := 0
glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, proxyw&)
if (!proxyw) {
glBindTexture(GL_TEXTURE_2D, prevname)
glDeleteTextures(1, name&)
// TODO: error somehow or other
return
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter)
blankmem: Char[width*height]
level := 0
while (true) {
glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA8, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, blankmem)
if (w == 1 || h == 1 || level == minMips) {
break
}
minMips += 1
w = w/2
if (w == 0) {
w = 1
}
h = h/2
if (h == 0) {
h = 1
}
}
}
mergeEmptyCells: func {
root mergeEmptyCells()
}
__destroy__: func {
glDeleteTextures(1, name&)
name = 0
}
}
GLPackedTexture: class {
// can be adjusted for maximum penetr- quality.
MinPackingSize: static Int = 3
// public but read-only
filled: Bool
x, y, width, height: Int
owner: GLTexturePack
// private and if you touch them I'll cut your fingers off
_pwidth, _pheight: Int
right, bottom: GLPackedTexture
getEmptyCell: func (req_w, req_h: Int) -> This {
if (MinPackingSize <= (req_w < req_h ? req_w : req_h) &&
(width < height ? width : height) <= MinPackingSize) {
return null
}
if (filled || width < req_w || height < req_h) {
r:GLPackedTexture = null
if (right && bottom && !(bottom filled || right filled)) {
if (height <= width) {
tw := bottom width - req_w
if (0 < tw && tw < right width - req_w) {
r = bottom getEmptyCell(req_w, req_h)
if (!r) {
r = right getEmptyCell(req_w, req_h)
}
} else {
r = right getEmptyCell(req_w, req_h)
if (!r) {
r = bottom getEmptyCell(req_w, req_h)
}
}
} else {
th := bottom height - req_h
if (0 < th && th < right height - req_h) {
r = bottom getEmptyCell(req_w, req_h)
if (!r) {
r = right getEmptyCell(req_w, req_h)
}
} else {
r = right getEmptyCell(req_w, req_h)
if (!r) {
r = bottom getEmptyCell(req_w, req_h)
}
}
}
} else {
if (right) {
r = right getEmptyCell(req_w, req_h)
}
if (!r && bottom) {
r = bottom getEmptyCell(req_w, req_h)
}
}
return r
}
botBuffer := height - req_h
rightBuffer := width - req_w
if (right || bottom) {
if (botBuffer || !bottom) {
nb := GLPackedTexture new()
nb height = botBuffer
nb width = width
nb bottom = bottom
nb owner = owner
bottom = nb
}
if (rightBuffer || !right) {
nb := GLPackedTexture new()
nb height = height
nb width = rightBuffer
nb right = right
nb owner = owner
right = nb
}
} else {
right = GLPackedTexture new()
bottom = GLPackedTexture new()
if (botBuffer < rightBuffer) {
right width = rightBuffer
right height = req_h
bottom width = width
bottom height = botBuffer
} else {
right width = rightBuffer
right height = height
bottom width = req_w
bottom height = botBuffer
}
}
right x = x + req_w
right y = y
bottom x = x
bottom y = y + req_h
right owner = owner
bottom owner = owner
width = req_w
height = req_h
return this
}
name: func -> UInt {
owner name()
}
unload: func {
filled = false
owner mergeEmptyCells()
}
mergeEmptyCells: func {
if (right) {
right mergeEmptyCells()
}
if (bottom) {
bottom mergeEmptyCells()
}
if (!filled) {
if (right && !right filled && right height == height) {
width += right width
right = right right
}
if (bottom && !bottom filled && bottom width == width) {
height += bottom height
bottom = bottom bottom
}
}
}
getUVs: func (u0, v0, u1, v1: Float@) {
uvx := x as Float / owner width
uvy := y as Float / owner height
uvw := width as Float / owner width
uvh := height as Float / owner height
u0 = uvx
v0 = uvy
u1 = uvx + uvw
v1 = uvy + uvh
}
__destroy__: func {
if (filled) {
unload()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment