Adding modding folder with a hacked build.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
package graphics
|
||||
|
||||
import "github.com/inkyblackness/hacked/ui/opengl"
|
||||
|
||||
// BitmapTexture wraps an OpenGL handle for a downloaded image.
|
||||
type BitmapTexture struct {
|
||||
gl opengl.OpenGL
|
||||
handle uint32
|
||||
|
||||
width, height float32
|
||||
u, v float32
|
||||
|
||||
pixelData []byte
|
||||
}
|
||||
|
||||
func powerOfTwo(value int) int {
|
||||
result := 2
|
||||
|
||||
for (result < value) && (result < 0x1000) {
|
||||
result *= 2
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// NewBitmapTexture downloads the provided raw data to OpenGL and returns a BitmapTexture instance.
|
||||
func NewBitmapTexture(gl opengl.OpenGL, width, height int, pixelData []byte) *BitmapTexture {
|
||||
textureWidth := powerOfTwo(width)
|
||||
textureHeight := powerOfTwo(height)
|
||||
tex := &BitmapTexture{
|
||||
gl: gl,
|
||||
handle: gl.GenTextures(1)[0],
|
||||
|
||||
width: float32(width),
|
||||
height: float32(height),
|
||||
|
||||
pixelData: pixelData,
|
||||
}
|
||||
tex.u = tex.width / float32(textureWidth)
|
||||
tex.v = tex.height / float32(textureHeight)
|
||||
|
||||
paddedData := make([]byte, textureWidth*textureHeight)
|
||||
for y := 0; y < height; y++ {
|
||||
inStart := y * width
|
||||
outOffset := y * textureWidth
|
||||
for x := 0; x < width; x++ {
|
||||
value := pixelData[inStart+x]
|
||||
paddedData[outOffset] = value
|
||||
outOffset++
|
||||
}
|
||||
}
|
||||
|
||||
gl.BindTexture(opengl.TEXTURE_2D, tex.handle)
|
||||
gl.TexImage2D(opengl.TEXTURE_2D, 0, opengl.RED, int32(textureWidth), int32(textureHeight),
|
||||
0, opengl.RED, opengl.UNSIGNED_BYTE, paddedData)
|
||||
gl.TexParameteri(opengl.TEXTURE_2D, opengl.TEXTURE_MAG_FILTER, opengl.NEAREST)
|
||||
gl.TexParameteri(opengl.TEXTURE_2D, opengl.TEXTURE_MIN_FILTER, opengl.NEAREST)
|
||||
gl.GenerateMipmap(opengl.TEXTURE_2D)
|
||||
gl.BindTexture(opengl.TEXTURE_2D, 0)
|
||||
|
||||
return tex
|
||||
}
|
||||
|
||||
// Dispose releases the OpenGL texture.
|
||||
func (tex *BitmapTexture) Dispose() {
|
||||
if tex.handle != 0 {
|
||||
tex.gl.DeleteTextures([]uint32{tex.handle})
|
||||
tex.handle = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Handle returns the texture handle.
|
||||
func (tex *BitmapTexture) Handle() uint32 {
|
||||
return tex.handle
|
||||
}
|
||||
|
||||
// PixelData returns the original paletted pixel data.
|
||||
func (tex *BitmapTexture) PixelData() []byte {
|
||||
return tex.pixelData
|
||||
}
|
||||
|
||||
// Size returns the dimensions of the bitmap, in pixels.
|
||||
func (tex *BitmapTexture) Size() (width, height float32) {
|
||||
return tex.width, tex.height
|
||||
}
|
||||
|
||||
// UV returns the maximum U and V values for the bitmap. The bitmap will be
|
||||
// stored in a power-of-two texture, which may be larger than the bitmap.
|
||||
func (tex *BitmapTexture) UV() (u, v float32) {
|
||||
return tex.u, tex.v
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package graphics
|
||||
|
||||
import (
|
||||
"github.com/inkyblackness/hacked/ss1/content/bitmap"
|
||||
"github.com/inkyblackness/hacked/ui/opengl"
|
||||
)
|
||||
|
||||
// FrameCacheKey is used to identify a cached frame.
|
||||
type FrameCacheKey uint16
|
||||
|
||||
// FrameCache loads bitmaps with their palettes and provides OpenGL textures.
|
||||
type FrameCache struct {
|
||||
gl opengl.OpenGL
|
||||
|
||||
textures map[FrameCacheKey]*BitmapTexture
|
||||
palettes map[FrameCacheKey]*PaletteTexture
|
||||
|
||||
keyCounter uint16
|
||||
}
|
||||
|
||||
// NewFrameCache returns a new instance.
|
||||
func NewFrameCache(gl opengl.OpenGL) *FrameCache {
|
||||
cache := &FrameCache{
|
||||
gl: gl,
|
||||
textures: make(map[FrameCacheKey]*BitmapTexture),
|
||||
palettes: make(map[FrameCacheKey]*PaletteTexture),
|
||||
}
|
||||
return cache
|
||||
}
|
||||
|
||||
// AllocateKey returns a new key to be used with uploading a new frame.
|
||||
func (cache *FrameCache) AllocateKey() FrameCacheKey {
|
||||
cache.keyCounter++
|
||||
return FrameCacheKey(cache.keyCounter)
|
||||
}
|
||||
|
||||
// SetTexture registers a texture based on given bitmap under given key.
|
||||
// The bitmap should contain a palette, otherwise it will most likely not be displayed.
|
||||
func (cache *FrameCache) SetTexture(key FrameCacheKey, width, height uint16, pixels []byte, palette *bitmap.Palette) {
|
||||
cache.DropTextureForKey(key)
|
||||
tex := NewBitmapTexture(cache.gl, int(width), int(height), pixels)
|
||||
cache.textures[key] = tex
|
||||
if palette == nil {
|
||||
return
|
||||
}
|
||||
pal := NewPaletteTexture(cache.gl, palette)
|
||||
cache.palettes[key] = pal
|
||||
}
|
||||
|
||||
// DropTextureForKey removes the currently cached frame.
|
||||
func (cache *FrameCache) DropTextureForKey(key FrameCacheKey) {
|
||||
if tex, existing := cache.textures[key]; existing {
|
||||
tex.Dispose()
|
||||
delete(cache.textures, key)
|
||||
}
|
||||
if pal, existing := cache.palettes[key]; existing {
|
||||
pal.Dispose()
|
||||
delete(cache.palettes, key)
|
||||
}
|
||||
}
|
||||
|
||||
// HandlesForKey returns the OpenGL handles for both palette and texture for given key.
|
||||
func (cache FrameCache) HandlesForKey(key FrameCacheKey) (palette uint32, texture uint32) {
|
||||
if tex, existing := cache.textures[key]; existing {
|
||||
texture = tex.Handle()
|
||||
}
|
||||
if pal, existing := cache.palettes[key]; existing {
|
||||
palette = pal.Handle()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Texture returns the buffered texture for given key.
|
||||
func (cache *FrameCache) Texture(key FrameCacheKey) *BitmapTexture {
|
||||
return cache.textures[key]
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package graphics
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/inkyblackness/hacked/ss1/content/bitmap"
|
||||
"github.com/inkyblackness/hacked/ss1/resource"
|
||||
"github.com/inkyblackness/hacked/ss1/world/ids"
|
||||
"github.com/inkyblackness/hacked/ui/opengl"
|
||||
)
|
||||
|
||||
// PaletteCache loads palettes and provides OpenGL textures.
|
||||
type PaletteCache struct {
|
||||
gl opengl.OpenGL
|
||||
localizer resource.Localizer
|
||||
|
||||
palettes map[resource.Key]*PaletteTexture
|
||||
}
|
||||
|
||||
// NewPaletteCache returns a new instance.
|
||||
func NewPaletteCache(gl opengl.OpenGL, localizer resource.Localizer) *PaletteCache {
|
||||
cache := &PaletteCache{
|
||||
gl: gl,
|
||||
localizer: localizer,
|
||||
palettes: make(map[resource.Key]*PaletteTexture),
|
||||
}
|
||||
return cache
|
||||
}
|
||||
|
||||
// InvalidateResources lets the cache remove any palette from resources that are specified in the given slice.
|
||||
func (cache *PaletteCache) InvalidateResources(ids []resource.ID) {
|
||||
for _, id := range ids {
|
||||
for key, texture := range cache.palettes {
|
||||
if key.ID == id {
|
||||
texture.Dispose()
|
||||
delete(cache.palettes, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Palette returns the palette with given index - if available.
|
||||
func (cache *PaletteCache) Palette(index int) (*PaletteTexture, error) {
|
||||
key := resource.KeyOf(ids.GamePalettesStart.Plus(index), resource.LangAny, 0)
|
||||
pal, existing := cache.palettes[key]
|
||||
if existing {
|
||||
return pal, nil
|
||||
}
|
||||
selector := cache.localizer.LocalizedResources(key.Lang)
|
||||
view, err := selector.Select(key.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if view.ContentType() != resource.Palette {
|
||||
return nil, resource.ErrWrongType(key, resource.Palette)
|
||||
}
|
||||
reader, err := view.Block(key.Index)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var palette bitmap.Palette
|
||||
err = binary.Read(reader, binary.LittleEndian, &palette)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pal = NewPaletteTexture(cache.gl, &palette)
|
||||
cache.palettes[key] = pal
|
||||
|
||||
return pal, nil
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package graphics
|
||||
|
||||
import (
|
||||
"github.com/inkyblackness/hacked/ss1/content/bitmap"
|
||||
"github.com/inkyblackness/hacked/ui/opengl"
|
||||
)
|
||||
|
||||
// PaletteTexture contains a palette stored as OpenGL texture.
|
||||
type PaletteTexture struct {
|
||||
gl opengl.OpenGL
|
||||
|
||||
handle uint32
|
||||
palette bitmap.Palette
|
||||
}
|
||||
|
||||
// NewPaletteTexture creates a new PaletteTexture instance.
|
||||
func NewPaletteTexture(gl opengl.OpenGL, palette *bitmap.Palette) *PaletteTexture {
|
||||
tex := &PaletteTexture{
|
||||
gl: gl,
|
||||
handle: gl.GenTextures(1)[0],
|
||||
}
|
||||
|
||||
tex.Update(palette)
|
||||
|
||||
return tex
|
||||
}
|
||||
|
||||
// Dispose releases the OpenGL texture.
|
||||
func (tex *PaletteTexture) Dispose() {
|
||||
if tex.handle != 0 {
|
||||
tex.gl.DeleteTextures([]uint32{tex.handle})
|
||||
tex.handle = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Palette returns the original palette.
|
||||
func (tex *PaletteTexture) Palette() bitmap.Palette {
|
||||
return tex.palette
|
||||
}
|
||||
|
||||
// Handle returns the texture handle.
|
||||
func (tex *PaletteTexture) Handle() uint32 {
|
||||
return tex.handle
|
||||
}
|
||||
|
||||
// Update reloads the palette.
|
||||
func (tex *PaletteTexture) Update(palette *bitmap.Palette) {
|
||||
gl := tex.gl
|
||||
const bytesPerRGBA = 4
|
||||
const colors = 256
|
||||
var data [colors * bytesPerRGBA]byte
|
||||
|
||||
for i := 0; i < colors; i++ {
|
||||
entry := palette[i]
|
||||
data[i*bytesPerRGBA+0] = entry.Red
|
||||
data[i*bytesPerRGBA+1] = entry.Green
|
||||
data[i*bytesPerRGBA+2] = entry.Blue
|
||||
data[i*bytesPerRGBA+3] = 0xFF
|
||||
}
|
||||
data[3] = 0x00
|
||||
|
||||
gl.BindTexture(opengl.TEXTURE_2D, tex.handle)
|
||||
gl.TexImage2D(opengl.TEXTURE_2D, 0, opengl.RGBA, int32(colors), 1, 0, opengl.RGBA, opengl.UNSIGNED_BYTE, data[:])
|
||||
gl.TexParameteri(opengl.TEXTURE_2D, opengl.TEXTURE_MAG_FILTER, opengl.NEAREST)
|
||||
gl.TexParameteri(opengl.TEXTURE_2D, opengl.TEXTURE_MIN_FILTER, opengl.NEAREST)
|
||||
gl.GenerateMipmap(opengl.TEXTURE_2D)
|
||||
gl.BindTexture(opengl.TEXTURE_2D, 0)
|
||||
|
||||
tex.palette = *palette
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package graphics
|
||||
|
||||
import (
|
||||
"github.com/inkyblackness/hacked/ss1"
|
||||
"github.com/inkyblackness/hacked/ss1/content/bitmap"
|
||||
"github.com/inkyblackness/hacked/ss1/resource"
|
||||
"github.com/inkyblackness/hacked/ui/opengl"
|
||||
)
|
||||
|
||||
const (
|
||||
errReferenceNotFound ss1.StringError = "reference not existing"
|
||||
errReferenceWrongDimensions ss1.StringError = "reference has wrong dimensions"
|
||||
)
|
||||
|
||||
// TextureCache loads bitmaps and provides OpenGL textures.
|
||||
type TextureCache struct {
|
||||
gl opengl.OpenGL
|
||||
localizer resource.Localizer
|
||||
|
||||
textures map[resource.Key]*BitmapTexture
|
||||
}
|
||||
|
||||
// NewTextureCache returns a new instance.
|
||||
func NewTextureCache(gl opengl.OpenGL, localizer resource.Localizer) *TextureCache {
|
||||
cache := &TextureCache{
|
||||
gl: gl,
|
||||
localizer: localizer,
|
||||
textures: make(map[resource.Key]*BitmapTexture),
|
||||
}
|
||||
return cache
|
||||
}
|
||||
|
||||
// InvalidateResources lets the cache remove any textures from resources that are specified in the given slice.
|
||||
func (cache *TextureCache) InvalidateResources(ids []resource.ID) {
|
||||
for _, id := range ids {
|
||||
for key, texture := range cache.textures {
|
||||
if key.ID == id {
|
||||
texture.Dispose()
|
||||
delete(cache.textures, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Texture returns the texture with given key - if available.
|
||||
func (cache *TextureCache) Texture(key resource.Key) (*BitmapTexture, error) {
|
||||
return cache.TextureReferenced(key, nil)
|
||||
}
|
||||
|
||||
// TextureReferenced returns the texture with given key - if available.
|
||||
// Should the underlying bitmap have to be loaded, then the given reference is taken as a basis for compressed bitmaps.
|
||||
// If reference is nil, then no reference is used.
|
||||
func (cache *TextureCache) TextureReferenced(key resource.Key, reference *resource.Key) (*BitmapTexture, error) {
|
||||
tex, existing := cache.textures[key]
|
||||
if existing {
|
||||
return tex, nil
|
||||
}
|
||||
selector := cache.localizer.LocalizedResources(key.Lang)
|
||||
view, err := selector.Select(key.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if view.ContentType() != resource.Bitmap {
|
||||
return nil, resource.ErrWrongType(key, resource.Bitmap)
|
||||
}
|
||||
reader, err := view.Block(key.Index)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bmp, err := bitmap.DecodeReferenced(reader, func(width, height int16) ([]byte, error) {
|
||||
buf := make([]byte, int(width)*int(height))
|
||||
if reference == nil {
|
||||
return buf, nil
|
||||
}
|
||||
refTex, refExisting := cache.textures[*reference]
|
||||
if !refExisting {
|
||||
return nil, errReferenceNotFound
|
||||
}
|
||||
refWidth, refHeight := refTex.Size()
|
||||
if (int16(refWidth) != width) || (int16(refHeight) != height) {
|
||||
return nil, errReferenceWrongDimensions
|
||||
}
|
||||
copy(buf, refTex.PixelData())
|
||||
return buf, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tex = NewBitmapTexture(cache.gl, int(bmp.Header.Width), int(bmp.Header.Height), bmp.Pixels)
|
||||
cache.textures[key] = tex
|
||||
|
||||
return tex, nil
|
||||
}
|
||||
Reference in New Issue
Block a user