// Commit creates the actual texture from the atlas image data. // This should be called after all regions have been defined and set, // and before you start using the texture for display. func (a *TextureAtlas) Commit(target gl.GLenum) { gl.PushAttrib(gl.CURRENT_BIT | gl.ENABLE_BIT) gl.Enable(target) a.texture.Bind(target) gl.TexParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) gl.TexParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) gl.TexParameteri(target, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR) switch a.depth { case 4: gl.TexImage2D(target, 0, gl.RGBA, a.width, a.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, a.data) case 3: gl.TexImage2D(target, 0, gl.RGB, a.width, a.height, 0, gl.RGB, gl.UNSIGNED_BYTE, a.data) case 1: gl.TexImage2D(target, 0, gl.ALPHA, a.width, a.height, 0, gl.ALPHA, gl.UNSIGNED_BYTE, a.data) } gl.PopAttrib() }
func (t *Texture) FromImageRGBA(rgba *image.RGBA, level int) { With(t, func() { gl.TexImage2D(gl.TEXTURE_2D, level, gl.RGBA, rgba.Bounds().Dx(), rgba.Bounds().Dy(), 0, gl.RGBA, gl.UNSIGNED_BYTE, rgba.Pix) }) }
func createTexture(r io.Reader) (gl.Texture, error) { img, err := png.Decode(r) if err != nil { return gl.Texture(0), err } rgbaImg, ok := img.(*image.NRGBA) if !ok { return gl.Texture(0), errors.New("texture must be an NRGBA image") } textureId := gl.GenTexture() textureId.Bind(gl.TEXTURE_2D) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) // flip image: first pixel is lower left corner imgWidth, imgHeight := img.Bounds().Dx(), img.Bounds().Dy() data := make([]byte, imgWidth*imgHeight*4) lineLen := imgWidth * 4 dest := len(data) - lineLen for src := 0; src < len(rgbaImg.Pix); src += rgbaImg.Stride { copy(data[dest:dest+lineLen], rgbaImg.Pix[src:src+rgbaImg.Stride]) dest -= lineLen } gl.TexImage2D(gl.TEXTURE_2D, 0, 4, imgWidth, imgHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, data) return textureId, nil }
// Creates a new Framebuffer. func NewFramebuffer(w int, h int) (fb *Framebuffer, err error) { var ( buffer gl.Framebuffer texture gl.Texture ) buffer = gl.GenFramebuffer() buffer.Bind() texture = gl.GenTexture() texture.Bind(gl.TEXTURE_2D) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, 0, gl.RGBA, gl.UNSIGNED_BYTE, nil) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0) gl.DrawBuffer(gl.COLOR_ATTACHMENT0) if gl.CheckFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE { err = fmt.Errorf("Framebuffer could not be set up") return } fb = &Framebuffer{ Buffer: buffer, Texture: texture, Width: w, Height: h, } return }
func (self *OpenGLRenderer) glTexImage2D(textureType gl.GLenum, texture *render.Texture) { gl.TexImage2D(textureType, 0, gl.RGB, texture.Image.Width(), texture.Image.Height(), 0, gl.BGR, gl.UNSIGNED_BYTE, texture.Image.Bytes(), ) }
// loadFont loads the given font data. This does not deal with font scaling. // Scaling should be handled by the independent Bitmap/Truetype loaders. // We therefore expect the supplied image and charset to already be adjusted // to the correct font scale. // // The image should hold a sprite sheet, defining the graphical layout for // every glyph. The config describes font metadata. func loadFont(img *image.RGBA, config *FontConfig) (f *Font, err error) { f = new(Font) f.Config = config // Resize image to next power-of-two. img = glh.Pow2Image(img).(*image.RGBA) ib := img.Bounds() f.Width = ib.Dx() f.Height = ib.Dy() // Create the texture itself. It will contain all glyphs. // Individual glyph-quads display a subset of this texture. f.Texture = gl.GenTexture() f.Texture.Bind(gl.TEXTURE_2D) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, ib.Dx(), ib.Dy(), 0, gl.RGBA, gl.UNSIGNED_BYTE, img.Pix) // file, err := os.Create("font.png") // if err != nil { // log.Fatal(err) // } // err = png.Encode(file, img) // if err != nil { // log.Fatal(err) // } return }
// Initialize texture storage. _REQUIRED_ before using it as a framebuffer target. func (t *Texture) Init() { With(t, func() { // generate base level storage gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, t.W, t.H, 0, gl.RGBA, gl.UNSIGNED_BYTE, nil) // generate required number of mipmaps given texture dimensions gl.GenerateMipmap(gl.TEXTURE_2D) }) }
func (v *Video) Render() { for running { select { case dimensions := <-v.resize: v.ResizeEvent(dimensions[0], dimensions[1]) case val := <-v.tick: slice := make([]uint8, len(val)*3) for i := 0; i < len(val); i = i + 1 { slice[i*3+0] = (uint8)((val[i] >> 16) & 0xff) slice[i*3+1] = (uint8)((val[i] >> 8) & 0xff) slice[i*3+2] = (uint8)((val[i]) & 0xff) } gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) v.tex.Bind(gl.TEXTURE_2D) if ppu.OverscanEnabled { gl.TexImage2D(gl.TEXTURE_2D, 0, 3, 240, 224, 0, gl.RGB, gl.UNSIGNED_BYTE, slice) } else { gl.TexImage2D(gl.TEXTURE_2D, 0, 3, 256, 240, 0, gl.RGB, gl.UNSIGNED_BYTE, slice) } gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.Begin(gl.QUADS) gl.TexCoord2f(0.0, 1.0) gl.Vertex3f(-1.0, -1.0, 0.0) gl.TexCoord2f(1.0, 1.0) gl.Vertex3f(1.0, -1.0, 0.0) gl.TexCoord2f(1.0, 0.0) gl.Vertex3f(1.0, 1.0, 0.0) gl.TexCoord2f(0.0, 0.0) gl.Vertex3f(-1.0, 1.0, 0.0) gl.End() if v.screen != nil { sdl.GL_SwapBuffers() } v.frametick <- true } } }
func imageAlpha(pix []byte, width, height int) (*Sampler2D, error) { s := &Sampler2D{ tex: gl.GenTexture(), } s.bind() gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.R8, width, height, 0, gl.RED, gl.UNSIGNED_BYTE, pix) return s, nil }
func initTexture2(filename string) gl.Texture { img, err := glfw.ReadImage(filename+".tga", glfw.NoRescaleBit) if err != nil { panic(err) } rt := gl.GenTexture() gl.Enable(gl.TEXTURE_2D) rt.Bind(gl.TEXTURE_2D) gl.TexEnvf(gl.TEXTURE_ENV, gl.TEXTURE_ENV_MODE, gl.MODULATE) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) // gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT) // gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, img.Width(), img.Height(), 0, gl.RGBA, gl.UNSIGNED_BYTE, img.Data()) fmt.Println(filename, img.Width(), img.Height()) return rt }
func uploadTexture_RGBA32(w, h int, data []byte) gl.Texture { id := gl.GenTexture() id.Bind(gl.TEXTURE_2D) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int(w), int(h), 0, gl.RGBA, gl.UNSIGNED_BYTE, data) if gl.GetError() != gl.NO_ERROR { id.Delete() panic("Failed to load a texture") return 0 } return id }
func CreateTexture(img image.Image) (*Texture, error) { imgW, imgH := img.Bounds().Dx(), img.Bounds().Dy() imgDim := Vector2{float32(imgW), float32(imgH)} rgbaImg, ok := img.(*image.NRGBA) if !ok { return nil, errors.New("texture must be an NRGBA image") } textureId := gl.GenTexture() textureId.Bind(gl.TEXTURE_2D) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) gl.TexImage2D(gl.TEXTURE_2D, 0, 4, imgW, imgH, 0, gl.RGBA, gl.UNSIGNED_BYTE, rgbaImg.Pix) return &Texture{textureId, imgDim, false, false, false, nextTextureCacheId()}, nil }
func NewTextureEmpty(width int, height int, model color.Model) *Texture { internalFormat, typ, format, target, e := ColorModelToGLTypes(model) if e != nil { return nil } a := gl.GenTexture() a.Bind(target) gl.TexImage2D(target, 0, internalFormat, width, height, 0, typ, format, nil) t := &Texture{a, false, nil, format, typ, internalFormat, target, width, height} t.SetWraping(WrapS, ClampToEdge) t.SetWraping(WrapT, ClampToEdge) t.SetFiltering(Nearest, Nearest) ResourceManager.Add(t) return t }
func functionAdaptor(target gl.GLenum, iformat gl.GLint, format, tp gl.GLenum) texFunction { switch target { case gl.TEXTURE_1D: return func(level int, g *TextureGeometry, pixels interface{}) { gl.TexImage1D(gl.TEXTURE_1D, level, int(iformat), g.Width, 0, format, tp, pixels) } case gl.TEXTURE_2D: return func(level int, g *TextureGeometry, pixels interface{}) { gl.TexImage2D(gl.TEXTURE_2D, level, int(iformat), g.Width, g.Height, 0, format, tp, pixels) } case gl.TEXTURE_3D: return func(level int, g *TextureGeometry, pixels interface{}) { gl.TexImage3D(gl.TEXTURE_3D, level, int(iformat), g.Width, g.Height, g.Depth, 0, format, tp, pixels) } break } panic(0) }
func initTexture(filename string, width, height int) gl.Texture { file, err := os.Open(filename + ".png") if err != nil { panic(err) } defer file.Close() img, _, err := image.Decode(file) if err != nil { panic(err) } t := reflect.ValueOf(img) fmt.Println(t.Elem().Type().Name()) canvas := image.NewRGBA(image.Rect(0, 0, width, height)) for y := 0; y < height; y++ { for x := 0; x < width; x++ { r, g, b, a := img.At(x, y).RGBA() if (filename == "hex4v" || filename == "hexstar2" || filename == "hexborder") && r == 0 && g == 0 && b == 0 { a = 0 } // if filename == "hex5k" { // fmt.Println(r, g, b, a) // } base := 4*x + canvas.Stride*y canvas.Pix[base] = uint8(r) canvas.Pix[base+1] = uint8(g) canvas.Pix[base+2] = uint8(b) canvas.Pix[base+3] = uint8(a) } } rt := gl.GenTexture() gl.Enable(gl.TEXTURE_2D) rt.Bind(gl.TEXTURE_2D) gl.TexEnvf(gl.TEXTURE_ENV, gl.TEXTURE_ENV_MODE, gl.MODULATE) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) // gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT) // gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, canvas.Pix) return rt }
func (v *Video) Render(frame []byte, frame_w int, frame_h int) { gl.Clear(gl.COLOR_BUFFER_BIT) v.Texture.Bind(gl.TEXTURE_2D) gl.TexImage2D(gl.TEXTURE_2D, 0, 3, frame_w, frame_h, 0, gl.RGB, gl.UNSIGNED_BYTE, frame) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.Begin(gl.QUADS) gl.TexCoord2f(0.0, 1.0) gl.Vertex3f(-1.0, -1.0, 0.0) gl.TexCoord2f(1.0, 1.0) gl.Vertex3f(1.0, -1.0, 0.0) gl.TexCoord2f(1.0, 0.0) gl.Vertex3f(1.0, 1.0, 0.0) gl.TexCoord2f(0.0, 0.0) gl.Vertex3f(-1.0, 1.0, 0.0) gl.End() glfw.SwapBuffers() }
// Packs image into a texture of size * size dimensions func (i *ImagePacker) pack(size int) (SpriteSheet, error) { rootNode := newNode(size, size) for v, img := range i.images { err := rootNode.recInsert(img.w, img.h, v) if err != nil { return SpriteSheet{}, err } } nodeImage := image.NewRGBA(image.Rect(0, 0, size, size)) traverseNodes(rootNode, func(nd node) { draw.Draw(nodeImage, image.Rect(nd.rc.left, nd.rc.top, nd.rc.right, nd.rc.bottom), i.images[nd.id].image, image.ZP, draw.Src) i.sprites[nd.id].left = float32(nd.rc.left) / float32(size) i.sprites[nd.id].top = float32(nd.rc.bottom) / float32(size) i.sprites[nd.id].right = float32(nd.rc.right) / float32(size) i.sprites[nd.id].bottom = float32(nd.rc.top) / float32(size) i.sprites[nd.id].W = float32(nd.rc.right - nd.rc.left) i.sprites[nd.id].H = float32(nd.rc.bottom - nd.rc.top) }) texture := gl.GenTexture() texture.Bind(gl.TEXTURE_2D) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, nodeImage.Pix) texture.Unbind(gl.TEXTURE_2D) spriteSheet := NewSpriteSheet(texture, size, size) return spriteSheet, nil }
// bind the given atlas to the current GL context // // the current implementation is very stupid, since it will // upload the texture every single call. // // later, improve this to upload only if there is a real need for it func (a *Atlas) bind() error { // discard any possible error if err := checkGlError(); err != nil { return err } if gl.Object(a.gltex).IsTexture() { a.gltex = gl.GenTexture() } a.gltex.Bind(gl.TEXTURE_2D) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, a.data.Bounds().Dx(), a.data.Bounds().Dy(), 0, gl.RGBA, gl.UNSIGNED_BYTE, a.data.Pix) if err := checkGlError(gl.OUT_OF_MEMORY, gl.INVALID_OPERATION); err != nil { return err } 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_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR) gl.GenerateMipmap(gl.TEXTURE_2D) panicGlError() return nil }
func LoadTexture(filename string) gl.Texture { r, err := os.Open(filename) if err != nil { fmt.Fprintf(os.Stderr, "[e] %v\n", err) return 0 } defer r.Close() img, _, err := image.Decode(r) if err != nil { fmt.Fprintf(os.Stderr, "[e] %v\n", err) return 0 } // lazy way to ensure we have the correct image format for opengl rgbaImg := image.NewNRGBA(img.Bounds()) draw.Draw(rgbaImg, img.Bounds(), img, image.ZP, draw.Src) tex := gl.GenTexture() tex.Bind(gl.TEXTURE_2D) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) // flip image: first pixel is lower left corner w, h := img.Bounds().Dx(), img.Bounds().Dy() data := make([]byte, w*h*4) lineLen := w * 4 dest := len(data) - lineLen for src := 0; src < len(rgbaImg.Pix); src += rgbaImg.Stride { copy(data[dest:dest+lineLen], rgbaImg.Pix[src:src+rgbaImg.Stride]) dest -= lineLen } gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w, h, 0, gl.RGBA, gl.UNSIGNED_BYTE, data) return tex }
func NewTexture2(data interface{}, width int, height int, target gl.GLenum, internalFormat int, typ gl.GLenum, format gl.GLenum) *Texture { a := gl.GenTexture() a.Bind(target) gl.TexImage2D(target, 0, internalFormat, width, height, 0, typ, format, data) t := &Texture{a, false, data, format, typ, internalFormat, target, width, height} t.SetWraping(WrapS, ClampToEdge) t.SetWraping(WrapT, ClampToEdge) t.SetFiltering(Nearest, Nearest) //ansi := []float32{0} //gl.GetFloatv(gl.MAX_TEXTURE_MAX_ANISOTROPY_EXT, ansi) //gl.TexParameterf(target, gl.TEXTURE_MAX_ANISOTROPY_EXT, ansi[0]) t.PreloadRender() //Forcing texture to go to VRAM and prevent shuttering t.data = nil data = nil ResourceManager.Add(t) return t }
// Initialize texture storage. _REQUIRED_ before using it as a framebuffer target. func (t *Texture) Init() { With(t, func() { gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, t.W, t.H, 0, gl.RGBA, gl.UNSIGNED_BYTE, nil) }) }
func (video *SDLVideo) Run() { running := true frame := make([]uint32, 0xf000) for running { select { case ev := <-sdl.Events: var event Event switch e := ev.(type) { case sdl.QuitEvent: running = false event = &QuitEvent{} case sdl.KeyboardEvent: switch e.Keysym.Sym { case sdl.K_BACKQUOTE: if e.Type == sdl.KEYDOWN { video.overscan = !video.overscan } case sdl.K_1: if e.Type == sdl.KEYDOWN { video.ResizeEvent(256, 240) } case sdl.K_2: if e.Type == sdl.KEYDOWN { video.ResizeEvent(512, 480) } case sdl.K_3: if e.Type == sdl.KEYDOWN { video.ResizeEvent(768, 720) } case sdl.K_4: if e.Type == sdl.KEYDOWN { video.ResizeEvent(1024, 960) } case sdl.K_5: if e.Type == sdl.KEYDOWN { video.ResizeEvent(2560, 1440) } case sdl.K_p: if e.Type == sdl.KEYDOWN { event = &PauseEvent{} } case sdl.K_n: if e.Type == sdl.KEYDOWN { event = &FrameStepEvent{} } case sdl.K_q: if e.Type == sdl.KEYDOWN { running = false event = &QuitEvent{} } case sdl.K_l: if e.Type == sdl.KEYDOWN { event = &SavePatternTablesEvent{} } case sdl.K_r: if e.Type == sdl.KEYDOWN { event = &ResetEvent{} } case sdl.K_s: if e.Type == sdl.KEYDOWN { event = &RecordEvent{} } case sdl.K_d: if e.Type == sdl.KEYDOWN { event = &StopEvent{} } case sdl.K_KP_PLUS: if e.Type == sdl.KEYDOWN { event = &AudioRecordEvent{} } case sdl.K_KP_MINUS: if e.Type == sdl.KEYDOWN { event = &AudioStopEvent{} } case sdl.K_o: if e.Type == sdl.KEYDOWN { event = &CPUDecodeEvent{} } case sdl.K_i: if e.Type == sdl.KEYDOWN { event = &PPUDecodeEvent{} } case sdl.K_9: if e.Type == sdl.KEYDOWN { event = &ShowBackgroundEvent{} } case sdl.K_0: if e.Type == sdl.KEYDOWN { event = &ShowSpritesEvent{} } case sdl.K_F1: if e.Type == sdl.KEYDOWN { event = &SaveStateEvent{} } case sdl.K_F5: if e.Type == sdl.KEYDOWN { event = &LoadStateEvent{} } case sdl.K_F8: if e.Type == sdl.KEYDOWN { event = &FPSEvent{2.} } case sdl.K_F9: if e.Type == sdl.KEYDOWN { event = &FPSEvent{1.} } case sdl.K_F10: if e.Type == sdl.KEYDOWN { event = &FPSEvent{.75} } case sdl.K_F11: if e.Type == sdl.KEYDOWN { event = &FPSEvent{.5} } case sdl.K_F12: if e.Type == sdl.KEYDOWN { event = &FPSEvent{.25} } case sdl.K_KP0: if e.Type == sdl.KEYDOWN { event = &MuteEvent{} } case sdl.K_KP1: if e.Type == sdl.KEYDOWN { event = &MutePulse1Event{} } case sdl.K_KP2: if e.Type == sdl.KEYDOWN { event = &MutePulse2Event{} } case sdl.K_KP3: if e.Type == sdl.KEYDOWN { event = &MuteTriangleEvent{} } case sdl.K_KP4: if e.Type == sdl.KEYDOWN { event = &MuteNoiseEvent{} } case sdl.K_KP5: if e.Type == sdl.KEYDOWN { event = &MuteDMCEvent{} } } if event == nil && running { event = &ControllerEvent{ Button: button(e), Down: e.Type == sdl.KEYDOWN, } } } if event != nil { go func() { video.events <- event }() } case colors := <-video.input: index := 0 x, y := 0, 0 for _, c := range colors { if pixelInFrame(x, y, video.overscan) { frame[index] = video.palette[c] index++ } switch x { case 255: x = 0 y++ default: x++ } } video.framePool.Put(colors) gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) video.prog.Use() gl.ActiveTexture(gl.TEXTURE0) video.texture.Bind(gl.TEXTURE_2D) gl.TexImage2D(gl.TEXTURE_2D, 0, 3, video.frameWidth(), video.frameHeight(), 0, gl.RGBA, gl.UNSIGNED_INT_8_8_8_8, frame) gl.DrawArrays(gl.TRIANGLES, 0, 6) if video.screen != nil { sdl.GL_SwapBuffers() } } } }
func (v *Video) Render() { for running { select { case buf := <-v.videoTick: gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) v.prog.Use() gl.ActiveTexture(gl.TEXTURE0) v.texture.Bind(gl.TEXTURE_2D) gl.TexImage2D(gl.TEXTURE_2D, 0, 3, 240, 224, 0, gl.RGBA, gl.UNSIGNED_INT_8_8_8_8, buf) gl.DrawArrays(gl.TRIANGLES, 0, 6) if v.screen != nil { sdl.GL_SwapBuffers() v.fpsmanager.FramerateDelay() } case ev := <-sdl.Events: switch e := ev.(type) { case sdl.ResizeEvent: v.ResizeEvent(int(e.W), int(e.H)) case sdl.QuitEvent: os.Exit(0) case sdl.KeyboardEvent: switch e.Keysym.Sym { case sdl.K_ESCAPE: running = false case sdl.K_r: // Trigger reset interrupt if e.Type == sdl.KEYDOWN { // cpu.RequestInterrupt(InterruptReset) } case sdl.K_l: if e.Type == sdl.KEYDOWN { nes.LoadGameState() } case sdl.K_s: if e.Type == sdl.KEYDOWN { nes.SaveGameState() } case sdl.K_i: if e.Type == sdl.KEYDOWN { nes.AudioEnabled = !nes.AudioEnabled } case sdl.K_1: if e.Type == sdl.KEYDOWN { v.ResizeEvent(256, 240) } case sdl.K_2: if e.Type == sdl.KEYDOWN { v.ResizeEvent(512, 480) } case sdl.K_3: if e.Type == sdl.KEYDOWN { v.ResizeEvent(768, 720) } case sdl.K_4: if e.Type == sdl.KEYDOWN { v.ResizeEvent(1024, 960) } } switch e.Type { case sdl.KEYDOWN: nes.Pads[0].KeyDown(e, 0) case sdl.KEYUP: nes.Pads[0].KeyUp(e, 0) } } } } }
// Create a *Texture containing a rendering of `str` with `size`. // TODO: allow for alternative fonts func MakeText(str string, size float64) *Text { if str == "" { panic("Trying to build empty text") } defer OpenGLSentinel()() // TODO: Something if font doesn't exist fontBytes, err := ioutil.ReadFile(FontFile) if err != nil { log.Panic(err) } font, err := freetype.ParseFont(fontBytes) if err != nil { log.Panic(err) } fg, bg := image.White, image.Black c := freetype.NewContext() c.SetDPI(72) c.SetFont(font) c.SetFontSize(size) pt := freetype.Pt(0, int(c.PointToFix32(size)>>8)) s, err := c.DrawString(str, pt) if err != nil { log.Panic("Error: ", err) } spacing, offset := 1.5, 3 w := int(s.X >> 8) h := int(c.PointToFix32(size*spacing)>>8) - offset text := &Text{str: str, Texture: NewTexture(w, h)} if text.W > 4096 { text.W = 4096 } rgba := image.NewRGBA(image.Rect(0, 0, text.W, text.H)) draw.Draw(rgba, rgba.Bounds(), bg, image.ZP, draw.Src) c.SetClip(rgba.Bounds()) c.SetDst(rgba) c.SetSrc(fg) _, err = c.DrawString(text.str, pt) if err != nil { log.Panic("Error: ", err) } With(text, func() { gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, text.W, text.H, 0, gl.RGBA, gl.UNSIGNED_BYTE, rgba.Pix) }) if gl.GetError() != gl.NO_ERROR { log.Panic("Failed to load a texture, err = ", gl.GetError(), " str = ", str, " w = ", text.W, " h = ", text.H) } return text }
func main() { b, _ := ioutil.ReadFile("./roboto/roboto-light.ttf") font, ferr := tt.Parse(b) if ferr != nil { fmt.Println("can't parse font %v , len %v", ferr.Error(), len(b)) } fc := ft.NewContext() fc.SetFont(font) glfw.SetErrorCallback(errorCallback) if !glfw.Init() { panic("Can't init glfw!") } defer glfw.Terminate() window, err := glfw.CreateWindow(800, 600, "Testing", nil, nil) if err != nil { panic(err) } window.MakeContextCurrent() gl.Init() program := gl.CreateProgram() vertexShader := gl.CreateShader(gl.VERTEX_SHADER) vertexShader.Source(` attribute vec4 a_position; attribute vec2 a_coord; varying vec2 v_coord; void main() { gl_Position = a_position; v_coord = a_coord; } `) vertexShader.Compile() fmt.Printf("vertex: %v\n", vertexShader.GetInfoLog()) fragmentShader := gl.CreateShader(gl.FRAGMENT_SHADER) fragmentShader.Source(` varying vec2 v_coord; uniform sampler2D s_picture; uniform vec4 color; uniform bool has_picture; void main() { if(has_picture) { gl_FragColor = texture2D(s_picture, v_coord); } else { gl_FragColor = color; } } `) fragmentShader.Compile() fmt.Printf("fragment %v \n", fragmentShader.GetInfoLog()) program.AttachShader(vertexShader) program.AttachShader(fragmentShader) program.Link() // ini //gl.MatrixMode(gl.PROJECTION) //gl.Ortho(0, 640, 0, 480, 0, 1) gl.ClearColor(0.5, 0.5, 0.5, 0.0) root := widget.Widget{ Name: "Red", Rect: image.Rect(0, 0, 800, 600), Background: color.RGBA{255, 128, 128, 126}, OnClick: widget.ClickInner, OnDrag: widget.DragInner, OnHover: widget.HoverInner, OnResize: widget.ResizeItself, } blue := root.AddWidget(&widget.Widget{ Name: "Blue", Rect: image.Rect(100, 100, 200, 200), Image: LoadImage("./test.png"), Background: color.RGBA{128, 128, 255, 126}, OnClick: func(w *widget.Widget, p image.Point) { root.SetTop(w) fmt.Println("Clicked blue box") widget.ClickInner(w, p) }, OnDrag: func(w *widget.Widget, p image.Point, d image.Point) bool { widget.DragInner(w, p, d) widget.DragItself(w, p, d) return true }, OnResize: widget.ResizeItself, }) blue.AddWidget(&widget.Widget{ Name: "White", Rect: image.Rect(90, 90, 100, 100), Background: color.RGBA{250, 250, 250, 250}, OnDrag: func(w *widget.Widget, p image.Point, d image.Point) bool { widget.DragItself(w, p, d) blue.Resize(d) return true }, }) root.AddWidget(&widget.Widget{ Name: "Green", Rect: image.Rect(100, 300, 200, 400), Background: color.RGBA{128, 255, 128, 126}, OnClick: func(w *widget.Widget, p image.Point) { root.SetTop(w) w.Image = LoadImage("./test2.png") }, OnDrag: widget.DragItself, }) root.AddWidget(&widget.Widget{ Name: "Black", Rect: image.Rect(100, 400, 150, 450), Background: color.RGBA{0, 0, 0, 126}, OnHover: func(w *widget.Widget, p0 image.Point, p1 image.Point) { if p1.In(w.Rect) { w.Background = color.RGBA{255, 255, 255, 126} } else { w.Background = color.RGBA{0, 0, 0, 126} } }, }) white := root.AddWidget(&widget.Widget{ Name: "White", Text: "Меня зовут Светлана, я из города Иваново. «Единая Россия» очень много сделала достижений: они подняли экономик… экономику, мы стали более лучшие… одеваться, и не было того что щас — это очень большие достижения! В сельском хозяйстве очень хорошо. (Гладин: Что именно в сельском хозяйстве они сделали?) Стало больше… земель за-а… много, ну… я не знаю даже как сказать… засеивать больше земель… а-а-а вот, овощи там, рожь — вот это всё. Что еще сказать… Так как у нас страна многонациональная, у нас в Москве очень много людей, которые очень помогают нам… с других городов… (вопрос Гладина: Вы считаете это достижение «Единой России»?) Да, это большое достижение! Очень хорошее даже! Видите ну… да… Видите ну у нас в Иванове очень хорошая стала медицина… а…что ещё… благоустройство в городах хорошее… с жильём… никаких проблем. Люди подмогают очень хорошо", Rect: image.Rect(400, 200, 700, 500), Foreground: color.RGBA{0, 0, 0, 0}, Background: color.RGBA{255, 255, 255, 126}, OnDrag: func(w *widget.Widget, p image.Point, d image.Point) bool { root.SetTop(w) widget.DragInner(w, p, d) widget.DragItself(w, p, d) return true }, OnResize: widget.ResizeItself, Padding: image.Rect(20, 20, 20, 20), }) white.AddWidget(&widget.Widget{ Name: "White", Rect: image.Rect(290, 290, 300, 300), Background: color.RGBA{0, 0, 0, 250}, OnDrag: func(w *widget.Widget, p image.Point, d image.Point) bool { widget.DragItself(w, p, d) white.Resize(d) return true }, }) x0 := 0.0 y0 := 0.0 window.SetMouseButtonCallback(func(w *glfw.Window, but glfw.MouseButton, act glfw.Action, key glfw.ModifierKey) { xpos, ypos := w.GetCursorPosition() if act == glfw.Press { root.Click(image.Point{X: int(xpos), Y: int(ypos)}) x0, y0 = xpos, ypos } }) window.SetCursorPositionCallback(func(w *glfw.Window, xpos float64, ypos float64) { root.Hover(image.Point{X: int(x0), Y: int(y0)}, image.Point{X: int(xpos), Y: int(ypos)}) if w.GetMouseButton(glfw.MouseButtonLeft) == glfw.Press { root.Drag(image.Point{X: int(x0), Y: int(y0)}, image.Point{X: int(xpos - x0), Y: int(ypos - y0)}) x0, y0 = xpos, ypos } x0, y0 = xpos, ypos }) width0, height0 := window.GetSize() window.SetSizeCallback(func(w *glfw.Window, width int, height int) { gl.Viewport(0, 0, width, height) root.Rect.Max = image.Point{width, height} width0, height0 = width, height }) /*switch(color_type){ case PNG_COLOR_TYPE_GRAY: return GL_LUMINANCE; case PNG_COLOR_TYPE_GRAY_ALPHA: return GL_LUMINANCE_ALPHA; case PNG_COLOR_TYPE_RGB: return GL_RGB; case PNG_COLOR_TYPE_RGB_ALPHA: return GL_RGBA; */ /* here init texture pool texturePool := make([widget.Widget]texture) */ for !window.ShouldClose() { //Do OpenGL stuff program.Use() gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) root.DrawBy(image.Point{}, func(w *widget.Widget, corner image.Point) { var texture gl.Texture if w.Image != nil { texture = gl.GenTexture() texture.Bind(gl.TEXTURE_2D) gl.ActiveTexture(gl.TEXTURE0) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, w.Image.Rect.Dx(), w.Image.Rect.Dy(), 0, gl.RGBA, gl.UNSIGNED_BYTE, w.Image.Pix) } leftX := 2.0*float32(corner.X+w.Rect.Min.X)/float32(root.Rect.Dx()) - 1.0 leftY := 1.0 - 2.0*float32(corner.Y+w.Rect.Min.Y)/float32(root.Rect.Dy()) rightX := 2.0*float32(corner.X+w.Rect.Min.X)/float32(root.Rect.Dx()) - 1.0 rightY := 1.0 - 2.0*float32(corner.Y+w.Rect.Max.Y)/float32(root.Rect.Dy()) bottomX := 2.0*float32(corner.X+w.Rect.Max.X)/float32(root.Rect.Dx()) - 1.0 bottomY := 1.0 - 2.0*float32(corner.Y+w.Rect.Min.Y)/float32(root.Rect.Dy()) topX := 2.0*float32(corner.X+w.Rect.Max.X)/float32(root.Rect.Dx()) - 1.0 topY := 1.0 - 2.0*float32(corner.Y+w.Rect.Max.Y)/float32(root.Rect.Dy()) vertices := []float32{ leftX, leftY, rightX, rightY, bottomX, bottomY, topX, topY, } texturePoints := []float32{ 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, } s_picture := program.GetUniformLocation("s_picture") s_picture.Uniform1i(0) has_picture := program.GetUniformLocation("has_picture") if w.Image != nil { has_picture.Uniform1i(1) } else { has_picture.Uniform1i(0) } col := program.GetUniformLocation("color") r, g, b, a := w.Background.RGBA() col.Uniform4f(float32(r)/float32(0xFFFF), float32(g)/float32(0xFFFF), float32(b)/float32(0xFFFF), float32(a)/float32(0xFFFF)) gl.PixelStorei(gl.UNPACK_ALIGNMENT, gl.UNSIGNED_BYTE) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) a_position := program.GetAttribLocation("a_position") a_position.AttribPointer(2, gl.FLOAT, false, 0, vertices) a_position.EnableArray() a_coord := program.GetAttribLocation("a_coord") a_coord.AttribPointer(2, gl.FLOAT, false, 0, texturePoints) a_coord.EnableArray() gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) gl.Flush() texture.Delete() if len(w.Text) > 0 { rct := w.Rect rct.Max = rct.Max.Sub(w.Rect.Min) rct.Min = rct.Min.Sub(w.Rect.Min) fg := image.NewRGBA(rct) fgu := image.NewUniform(color.RGBA{0, 16, 32, 255}) draw.Draw(fg, fg.Bounds(), fgu, image.ZP, draw.Src) bg := image.NewRGBA(rct) bgu := image.NewUniform(color.RGBA{255, 255, 255, 255}) draw.Draw(bg, bg.Bounds(), bgu, image.ZP, draw.Src) lineHeight := 20.0 fc.SetDPI(100.0) fc.SetFont(font) fc.SetFontSize(12.0) fc.SetClip(bg.Bounds()) fc.SetDst(bg) fc.SetSrc(fg) p0 := ft.Pt(w.Padding.Min.X, w.Padding.Min.Y) p := p0 for _, s := range w.Text { p, _ = fc.DrawString(string(s), p) if int(p.X>>8) > rct.Max.X-w.Padding.Max.X-w.Padding.Min.X { p.X = p0.X p.Y += raster.Fix32(lineHeight * 256) } } var texture gl.Texture if bg != nil { texture = gl.GenTexture() texture.Bind(gl.TEXTURE_2D) gl.ActiveTexture(gl.TEXTURE0) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, bg.Rect.Dx(), bg.Rect.Dy(), 0, gl.RGBA, gl.UNSIGNED_BYTE, bg.Pix) } leftX := 2.0*float32(corner.X+w.Rect.Min.X)/float32(root.Rect.Dx()) - 1.0 leftY := 1.0 - 2.0*float32(corner.Y+w.Rect.Min.Y)/float32(root.Rect.Dy()) rightX := 2.0*float32(corner.X+w.Rect.Min.X)/float32(root.Rect.Dx()) - 1.0 rightY := 1.0 - 2.0*float32(corner.Y+w.Rect.Max.Y)/float32(root.Rect.Dy()) bottomX := 2.0*float32(corner.X+w.Rect.Max.X)/float32(root.Rect.Dx()) - 1.0 bottomY := 1.0 - 2.0*float32(corner.Y+w.Rect.Min.Y)/float32(root.Rect.Dy()) topX := 2.0*float32(corner.X+w.Rect.Max.X)/float32(root.Rect.Dx()) - 1.0 topY := 1.0 - 2.0*float32(corner.Y+w.Rect.Max.Y)/float32(root.Rect.Dy()) vertices := []float32{ leftX, leftY, rightX, rightY, bottomX, bottomY, topX, topY, } texturePoints := []float32{ 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, } s_picture := program.GetUniformLocation("s_picture") s_picture.Uniform1i(0) has_picture := program.GetUniformLocation("has_picture") if bg != nil { has_picture.Uniform1i(1) } else { has_picture.Uniform1i(0) } col := program.GetUniformLocation("color") r, g, b, a := w.Background.RGBA() col.Uniform4f(float32(r)/float32(0xFFFF), float32(g)/float32(0xFFFF), float32(b)/float32(0xFFFF), float32(a)/float32(0xFFFF)) gl.PixelStorei(gl.UNPACK_ALIGNMENT, gl.UNSIGNED_BYTE) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) a_position := program.GetAttribLocation("a_position") a_position.AttribPointer(2, gl.FLOAT, false, 0, vertices) a_position.EnableArray() a_coord := program.GetAttribLocation("a_coord") a_coord.AttribPointer(2, gl.FLOAT, false, 0, texturePoints) a_coord.EnableArray() gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) gl.Flush() texture.Delete() } }) window.SwapBuffers() glfw.PollEvents() } }
// loadFont loads the given font data. This does not deal with font scaling. // Scaling should be handled by the independent Bitmap/Truetype loaders. // We therefore expect the supplied image and charset to already be adjusted // to the correct font scale. // // The image should hold a sprite sheet, defining the graphical layout for // every glyph. The config describes font metadata. func loadFont(img *image.RGBA, config *FontConfig) (f *Font, err error) { f = new(Font) f.config = config // Resize image to next power-of-two. img = glh.Pow2Image(img).(*image.RGBA) ib := img.Bounds() // Create the texture itself. It will contain all glyphs. // Individual glyph-quads display a subset of this texture. f.texture = gl.GenTexture() f.texture.Bind(gl.TEXTURE_2D) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, ib.Dx(), ib.Dy(), 0, gl.RGBA, gl.UNSIGNED_BYTE, img.Pix) // Create display lists for each glyph. f.listbase = gl.GenLists(len(config.Glyphs)) texWidth := float32(ib.Dx()) texHeight := float32(ib.Dy()) for index, glyph := range config.Glyphs { // Update max glyph bounds. if glyph.Width > f.maxGlyphWidth { f.maxGlyphWidth = glyph.Width } if glyph.Height > f.maxGlyphHeight { f.maxGlyphHeight = glyph.Height } // Quad width/height vw := float32(glyph.Width) vh := float32(glyph.Height) // Texture coordinate offsets. tx1 := float32(glyph.X) / texWidth ty1 := float32(glyph.Y) / texHeight tx2 := (float32(glyph.X) + vw) / texWidth ty2 := (float32(glyph.Y) + vh) / texHeight // Advance width (or height if we render top-to-bottom) adv := float32(glyph.Advance) gl.NewList(f.listbase+uint(index), gl.COMPILE) { gl.Begin(gl.QUADS) { gl.TexCoord2f(tx1, ty2) gl.Vertex2f(0, 0) gl.TexCoord2f(tx2, ty2) gl.Vertex2f(vw, 0) gl.TexCoord2f(tx2, ty1) gl.Vertex2f(vw, vh) gl.TexCoord2f(tx1, ty1) gl.Vertex2f(0, vh) } gl.End() switch config.Dir { case LeftToRight: gl.Translatef(adv, 0, 0) case RightToLeft: gl.Translatef(-adv, 0, 0) case TopToBottom: gl.Translatef(0, -adv, 0) } } gl.EndList() } err = glh.CheckGLError() return }
func LoadTexture() { gl.ActiveTexture(gl.TEXTURE0) if err := CheckGlError(); err != nil { panic(err) } t := gl.GenTexture() if err := CheckGlError(); err != nil { panic(err) } const target = gl.TEXTURE_2D t.Bind(target) if err := CheckGlError(); err != nil { panic(err) } img, err := LoadImage("test.jpg") if err != nil { panic(err) } data := GlRgba(img, matrix23{1, 0, 0, 1, 0, 0}) size := img.Bounds().Size() gl.TexImage2D( target, 0, // Mipmap level. gl.SRGB8_ALPHA8, // Format inside OpenGL. size.X, // Width. size.Y, // Height. 0, // Border. Doc says it must be 0. gl.RGBA, // Format of the data that... gl.UNSIGNED_BYTE, // ... I give to OpenGL. data, // And the data itself. ) if err := CheckGlError(); err != nil { panic(err) } gl.GenerateMipmap(target) if err := CheckGlError(); err != nil { panic(err) } gl.TexParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR) if err := CheckGlError(); err != nil { panic(err) } gl.TexParameteri(target, gl.TEXTURE_MAG_FILTER, gl.LINEAR) if err := CheckGlError(); err != nil { panic(err) } gl.TexParameteri(target, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE) if err := CheckGlError(); err != nil { panic(err) } gl.TexParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) if err := CheckGlError(); err != nil { panic(err) } t.Unbind(target) globaltexture = t }
func LoadSkybox() { gl.ActiveTexture(gl.TEXTURE0) if err := CheckGlError(); err != nil { panic(err) } t := gl.GenTexture() if err := CheckGlError(); err != nil { panic(err) } t.Bind(gl.TEXTURE_CUBE_MAP) if err := CheckGlError(); err != nil { panic(err) } const filename = "Skybox_tut13_384x256.png" img, err := LoadImage(filename) if err != nil { panic(err) } size := img.Bounds().Size() w := size.X h := size.Y if w/3 != h/2 { panic("incorrect aspect ratio") } s := h / 2 // Size of one face of the cube. targets := [...]gl.GLenum{ gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_POSITIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, gl.TEXTURE_CUBE_MAP_POSITIVE_Z, } rs := []image.Rectangle{ image.Rect(0*s, 0*s, 1*s, 1*s), // 0 side. image.Rect(1*s, 0*s, 2*s, 1*s), // 1 side. image.Rect(2*s, 0*s, 3*s, 1*s), // 2 side. image.Rect(0*s, 1*s, 1*s, 2*s), // 3 bottom. image.Rect(1*s, 1*s, 2*s, 2*s), // 4 top. image.Rect(2*s, 1*s, 3*s, 2*s), // 5 side. } // redirect must end with 3 and 4. This places the floor at -z and the // sky at +z. // All the sides look properly oriented (grass down) on the +y side. // Considering that the sky is properly oriented the way it is, then // rectangle 5 contains the picture that matches it on the +y side. // Therefore, redirect must end with 5, 3, 4. // Problem now: all the other sides, and maybe the floor too, need to be // rotated in order to line up, that is having their sky on top. redirect := [...]int{0, 2, 1, 5, 3, 4} ID := matrix23{1, 0, 0, 1, 0, 0} ms := [...]matrix23{ matrix23{0, -1, 1, 0, 0, s - 1}, matrix23{0, 1, -1, 0, s - 1, 0}, matrix23{-1, 0, 0, -1, s - 1, s - 1}, ID, matrix23{-1, 0, 0, -1, s - 1, s - 1}, ID, } rgba := forceRGBA(img) for i, target := range targets { subimage := rgba.SubImage(rs[redirect[i]]) data := GlRgba(subimage, ms[i]) gl.TexImage2D( target, 0, // Mipmap level. gl.SRGB8_ALPHA8, // Format inside OpenGL. s, // Width. s, // Height. 0, // Border. Doc says it must be 0. gl.RGBA, // Format of the data that... gl.UNSIGNED_BYTE, // ... I give to OpenGL. data, // And the data itself. ) if err := CheckGlError(); err != nil { panic(err) } } const target = gl.TEXTURE_CUBE_MAP gl.GenerateMipmap(target) if err := CheckGlError(); err != nil { panic(err) } gl.TexParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR) if err := CheckGlError(); err != nil { panic(err) } gl.TexParameteri(target, gl.TEXTURE_MAG_FILTER, gl.LINEAR) if err := CheckGlError(); err != nil { panic(err) } gl.TexParameteri(target, gl.TEXTURE_WRAP_R, gl.CLAMP_TO_EDGE) if err := CheckGlError(); err != nil { panic(err) } gl.TexParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) if err := CheckGlError(); err != nil { panic(err) } gl.TexParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) if err := CheckGlError(); err != nil { panic(err) } t.Unbind(target) globaltexture = t }