diff --git a/assets/assets.go b/assets/assets.go index 9713e99..03f143d 100755 --- a/assets/assets.go +++ b/assets/assets.go @@ -1,8 +1,8 @@ package assets var ( - Textures map[uint32]Texture = make(map[uint32]Texture) - TexturePaths map[string]uint32 = make(map[string]uint32) + Textures = make(map[uint32]Texture) + TexturePaths = make(map[string]uint32) ) func AddTextureToCache(t Texture) { diff --git a/assets/textures.go b/assets/textures.go index 4601a38..9a1c70d 100755 --- a/assets/textures.go +++ b/assets/textures.go @@ -4,8 +4,12 @@ import ( "bytes" "image" "image/color" + "image/jpeg" "image/png" + "io" "os" + "path" + "strings" "unsafe" "github.com/go-gl/gl/v4.1-core/gl" @@ -30,6 +34,18 @@ type TextureLoadOptions struct { TryLoadFromCache bool WriteToCache bool GenMipMaps bool + KeepPixelsInMem bool +} + +type Cubemap struct { + // These only exists for textures loaded from disk + RightPath string + LeftPath string + TopPath string + BotPath string + FrontPath string + BackPath string + TexID uint32 } func LoadTexturePNG(file string, loadOptions *TextureLoadOptions) (Texture, error) { @@ -56,28 +72,10 @@ func LoadTexturePNG(file string, loadOptions *TextureLoadOptions) (Texture, erro } tex := Texture{ - Path: file, - Width: int32(img.Bounds().Dx()), - Height: int32(img.Bounds().Dy()), - Pixels: make([]byte, img.Bounds().Dx()*img.Bounds().Dy()*4), + Path: file, } - //NOTE: Load bottom left to top right because this is the texture coordinate system used by OpenGL - //NOTE: We only support 8-bit channels (32-bit colors) for now - i := 0 - for y := img.Bounds().Dy() - 1; y >= 0; y-- { - for x := 0; x < img.Bounds().Dx(); x++ { - - c := color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA) - - tex.Pixels[i] = c.R - tex.Pixels[i+1] = c.G - tex.Pixels[i+2] = c.B - tex.Pixels[i+3] = c.A - - i += 4 - } - } + tex.Pixels, tex.Width, tex.Height = pixelsFromNrgbaPng(img) //Prepare opengl stuff gl.GenTextures(1, &tex.TexID) @@ -100,37 +98,21 @@ func LoadTexturePNG(file string, loadOptions *TextureLoadOptions) (Texture, erro AddTextureToCache(tex) } + if !loadOptions.KeepPixelsInMem { + tex.Pixels = nil + } + return tex, nil } -func LoadTextureInMemImg(img image.Image, loadOptions *TextureLoadOptions) (Texture, error) { +func LoadTextureInMemPngImg(img image.Image, loadOptions *TextureLoadOptions) (Texture, error) { if loadOptions == nil { loadOptions = &TextureLoadOptions{} } - tex := Texture{ - Width: int32(img.Bounds().Dx()), - Height: int32(img.Bounds().Dy()), - Pixels: make([]byte, img.Bounds().Dx()*img.Bounds().Dy()*4), - } - - //NOTE: Load bottom left to top right because this is the texture coordinate system used by OpenGL - //NOTE: We only support 8-bit channels (32-bit colors) for now - i := 0 - for y := img.Bounds().Dy() - 1; y >= 0; y-- { - for x := 0; x < img.Bounds().Dx(); x++ { - - c := color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA) - - tex.Pixels[i] = c.R - tex.Pixels[i+1] = c.G - tex.Pixels[i+2] = c.B - tex.Pixels[i+3] = c.A - - i += 4 - } - } + tex := Texture{} + tex.Pixels, tex.Width, tex.Height = pixelsFromNrgbaPng(img) //Prepare opengl stuff gl.GenTextures(1, &tex.TexID) @@ -153,5 +135,174 @@ func LoadTextureInMemImg(img image.Image, loadOptions *TextureLoadOptions) (Text AddTextureToCache(tex) } + if !loadOptions.KeepPixelsInMem { + tex.Pixels = nil + } + return tex, nil } + +func LoadTextureJpeg(file string, loadOptions *TextureLoadOptions) (Texture, error) { + + if loadOptions == nil { + loadOptions = &TextureLoadOptions{} + } + + if loadOptions.TryLoadFromCache { + if tex, ok := GetTextureFromCachePath(file); ok { + return tex, nil + } + } + + //Load from disk + fileBytes, err := os.ReadFile(file) + if err != nil { + return Texture{}, err + } + + img, err := jpeg.Decode(bytes.NewReader(fileBytes)) + if err != nil { + return Texture{}, err + } + + tex := Texture{ + Path: file, + } + + tex.Pixels, tex.Width, tex.Height = pixelsFromNrgbaPng(img) + + //Prepare opengl stuff + gl.GenTextures(1, &tex.TexID) + gl.BindTexture(gl.TEXTURE_2D, tex.TexID) + + // set the texture wrapping/filtering options (on the currently bound texture object) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) + + // load and generate the texture + gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, tex.Width, tex.Height, 0, gl.RGBA, gl.UNSIGNED_BYTE, unsafe.Pointer(&tex.Pixels[0])) + + if loadOptions.GenMipMaps { + gl.GenerateMipmap(tex.TexID) + } + + if loadOptions.WriteToCache { + AddTextureToCache(tex) + } + + if !loadOptions.KeepPixelsInMem { + tex.Pixels = nil + } + + return tex, nil +} + +func pixelsFromNrgbaPng(img image.Image) (pixels []byte, width, height int32) { + + //NOTE: Load bottom left to top right because this is the texture coordinate system used by OpenGL + //NOTE: We only support 8-bit channels (32-bit colors) for now + i := 0 + width, height = int32(img.Bounds().Dx()), int32(img.Bounds().Dy()) + pixels = make([]byte, img.Bounds().Dx()*img.Bounds().Dy()*4) + for y := img.Bounds().Dy() - 1; y >= 0; y-- { + for x := 0; x < img.Bounds().Dx(); x++ { + + c := color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA) + + pixels[i] = c.R + pixels[i+1] = c.G + pixels[i+2] = c.B + pixels[i+3] = c.A + + i += 4 + } + } + + return pixels, width, height +} + +func pixelsFromNrgbaJpg(img image.Image) (pixels []byte, width, height int32) { + + //NOTE: Load bottom left to top right because this is the texture coordinate system used by OpenGL + //NOTE: We only support 8-bit channels (32-bit colors) for now + i := 0 + width, height = int32(img.Bounds().Dx()), int32(img.Bounds().Dy()) + pixels = make([]byte, img.Bounds().Dx()*img.Bounds().Dy()*4) + for y := img.Bounds().Dy() - 1; y >= 0; y-- { + for x := 0; x < img.Bounds().Dx(); x++ { + + c := color.NRGBAModel.Convert(img.At(x, y)).(color.NRGBA) + + pixels[i] = c.R + pixels[i+1] = c.G + pixels[i+2] = c.B + pixels[i+3] = c.A + + i += 4 + } + } + + return pixels, width, height +} + +func LoadCubemapTextures(rightTex, leftTex, topTex, botTex, frontTex, backTex string) (Cubemap, error) { + + var imgDecoder func(r io.Reader) (image.Image, error) + var pixelDecoder func(image.Image) ([]byte, int32, int32) + ext := strings.ToLower(path.Ext(rightTex)) + if ext == ".jpg" || ext == ".jpeg" { + imgDecoder = jpeg.Decode + pixelDecoder = pixelsFromNrgbaJpg + } else if ext == ".png" { + imgDecoder = png.Decode + pixelDecoder = pixelsFromNrgbaPng + } else { + panic("unknown image extension: " + ext) + } + + cmap := Cubemap{ + RightPath: rightTex, + LeftPath: leftTex, + TopPath: topTex, + BotPath: botTex, + FrontPath: frontTex, + BackPath: backTex, + } + + texturePaths := []string{rightTex, leftTex, topTex, botTex, frontTex, backTex} + for i := 0; i < len(texturePaths); i++ { + + fPath := texturePaths[i] + + //Load from disk + fileBytes, err := os.ReadFile(fPath) + if err != nil { + return Cubemap{}, err + } + + img, err := imgDecoder(bytes.NewReader(fileBytes)) + if err != nil { + return Cubemap{}, err + } + + pixels, width, height := pixelDecoder(img) + + //Prepare opengl stuff + gl.GenTextures(1, &cmap.TexID) + gl.BindTexture(gl.TEXTURE_CUBE_MAP, cmap.TexID) + + // load and generate the texture + gl.TexImage2D(gl.TEXTURE_CUBE_MAP, 0, gl.RGBA8, int32(width), int32(height), 0, gl.RGBA, gl.UNSIGNED_BYTE, unsafe.Pointer(&pixels[0])) + } + + // set the texture wrapping/filtering options (on the currently bound texture object) + gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR) + gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR) + gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) + gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) + gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE) + + return cmap, nil +} diff --git a/main.go b/main.go index 3b5d7b5..b12e520 100755 --- a/main.go +++ b/main.go @@ -56,6 +56,8 @@ var ( debugDrawDepthBuffer bool debugDepthMat *materials.Material + + skyboxCmap assets.Cubemap ) type OurGame struct { @@ -169,6 +171,15 @@ func (g *OurGame) Init() { logging.ErrLog.Fatalln("Failed to load texture. Err: ", err) } + // skyboxCmap, err = assets.LoadCubemapTextures( + // "./res/textures/sb-right.jpg", "./res/textures/sb-left.jpg", + // "./res/textures/sb-top.jpg", "./res/textures/sb-bottom.jpg", + // "./res/textures/sb-front.jpg", "./res/textures/sb-back.jpg", + // ) + // if err != nil { + // logging.ErrLog.Fatalln("Failed to load cubemap. Err: ", err) + // } + //Configure material simpleMat.DiffuseTex = tex.TexID