// Renderer implementation. // BindTexture makes the texture available on the GPU. func (gc *opengl) BindTexture(tid *uint32, img image.Image, repeat bool) (err error) { if glerr := gl.GetError(); glerr != gl.NO_ERROR { log.Printf("opengl:bindTexture need to find and fix prior error %X", glerr) } if *tid == 0 { gl.GenTextures(1, tid) } gl.BindTexture(gl.TEXTURE_2D, *tid) // FUTURE: check if RGBA, or NRGBA are alpha pre-multiplied. The docs say yes // for RGBA but the data is from PNG files which are not pre-multiplied // and the go png Decode looks like its reading values directly. var ptr gl.Pointer bounds := img.Bounds() width, height := int32(bounds.Dx()), int32(bounds.Dy()) switch imgType := img.(type) { case *image.RGBA: i := img.(*image.RGBA) ptr = gl.Pointer(&(i.Pix[0])) case *image.NRGBA: i := img.(*image.NRGBA) ptr = gl.Pointer(&(i.Pix[0])) default: return fmt.Errorf("Unsupported image format %T", imgType) } gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, ptr) gl.GenerateMipmap(gl.TEXTURE_2D) gc.setTextureMode(*tid, repeat) if glerr := gl.GetError(); glerr != gl.NO_ERROR { err = fmt.Errorf("Failed binding texture %d\n", glerr) } return err }
// Renderer implementation. // BindMesh copies the given mesh data to the GPU // and initializes the vao and buffer references. func (gc *opengl) BindMesh(vao *uint32, vdata map[uint32]Data, fdata Data) error { if glerr := gl.GetError(); glerr != gl.NO_ERROR { return fmt.Errorf("BindMesh needs to find and fix prior error %X", glerr) } // Reuse existing vao's. if *vao == 0 { gl.GenVertexArrays(1, vao) } gl.BindVertexArray(*vao) for _, vbuff := range vdata { vd, ok := vbuff.(*vertexData) if ok && vd.rebind { gc.bindVertexBuffer(vd) vd.rebind = false } } if glerr := gl.GetError(); glerr != gl.NO_ERROR { return fmt.Errorf("BindMesh failed to bind vb %X", glerr) } if fd, ok := fdata.(*faceData); ok { if fd.rebind { gc.bindFaceBuffer(fd) fd.rebind = false } } if glerr := gl.GetError(); glerr != gl.NO_ERROR { return fmt.Errorf("BindMesh failed to bind fb %X", glerr) } return nil }
// drawScene renders the 3D models consisting of one VAO func (tag *trtag) drawScene() { gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) tag.checkError("gl.Clear") gl.UseProgram(tag.shaders) tag.checkError("gl.UseProgram") gl.BindVertexArray(tag.vao) tag.checkError("gl.BindVertexArray") // Use a modelview matrix and quaternion to rotate the 3D object. tag.mvp64.SetQ(lin.NewQ().SetAa(0, 1, 0, lin.Rad(-tag.rotateAngle))) tag.mvp64.TranslateMT(0, 0, -4) tag.mvp.Set(tag.mvp64.Mult(tag.mvp64, tag.persp)) gl.UniformMatrix4fv(tag.mvpRef, 1, false, tag.mvp.Pointer()) if err := gl.GetError(); err != 0 { fmt.Printf("gl.UniformMatrix error %d\n", err) } gl.DrawElements(gl.TRIANGLES, int32(len(tag.faces)), gl.UNSIGNED_BYTE, 0) if err := gl.GetError(); err != 0 { fmt.Printf("gl.DrawElements error %d\n", err) } // cleanup gl.UseProgram(0) tag.checkError("gl.UseProgram-0") gl.BindVertexArray(0) tag.checkError("gl.BindVertexArray-0") // rotate based on time... not on how fast the computer runs. if time.Now().Sub(tag.lastTime).Seconds() > 0.01 { tag.rotateAngle += 1 tag.lastTime = time.Now() } }
// checkError helps to debug OpenGL errors by printing out when the occur. func (tag *trtag) checkError(txt string) { cnt := 0 err := gl.GetError() for err != 0 { fmt.Printf("%s error %d::%d\n", txt, cnt, err) err = gl.GetError() cnt++ if cnt > 10 { os.Exit(-1) } } }
// BindFrame creates a framebuffer object with an associated texture. // http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-14-render-to-texture/ // http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/ func (gc *opengl) BindFrame(buf int, fbo, tid, db *uint32) (err error) { size := int32(1024) gl.GenFramebuffers(1, fbo) gl.BindFramebuffer(gl.FRAMEBUFFER, *fbo) // Create a texture specifically for the framebuffer. gl.GenTextures(1, tid) gl.BindTexture(gl.TEXTURE_2D, *tid) switch buf { case IMAGE_BUFF: gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Pointer(nil)) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) // Add a depth buffer to mimic the normal framebuffer behaviour for 3D objects. gl.GenRenderbuffers(1, db) gl.BindRenderbuffer(gl.RENDERBUFFER, *db) gl.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT, size, size) gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, *db) // Associate the texture with the framebuffer. gl.FramebufferTexture(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, *tid, 0) buffType := uint32(gl.COLOR_ATTACHMENT0) gl.DrawBuffers(1, &buffType) case DEPTH_BUFF: gl.TexImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT16, size, size, 0, gl.DEPTH_COMPONENT, gl.FLOAT, gl.Pointer(nil)) 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) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_FUNC, gl.LEQUAL) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_COMPARE_MODE, gl.COMPARE_REF_TO_TEXTURE) // Associate the texture with the framebuffer. gl.FramebufferTexture(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, *tid, 0) gl.DrawBuffer(gl.NONE) default: return fmt.Errorf("BindFrame unrecognized buffer type.") } // Report any problems. glerr := gl.CheckFramebufferStatus(gl.FRAMEBUFFER) if glerr != gl.FRAMEBUFFER_COMPLETE { return fmt.Errorf("BindFrame error %X", glerr) } if glerr := gl.GetError(); glerr != gl.NO_ERROR { err = fmt.Errorf("Failed binding framebuffer %X", glerr) } gl.BindFramebuffer(gl.FRAMEBUFFER, 0) // clean up by resetting to default framebuffer. return err }
// Renderer implementation. // BindShader compiles the shader and makes it available to the GPU. // It also adds the list of uniforms and vertex layout references to the // provided maps. func (gc *opengl) BindShader(vsh, fsh []string, uniforms map[string]int32, layouts map[string]uint32) (program uint32, err error) { program = gl.CreateProgram() // compile and link the shader program. if glerr := gl.BindProgram(program, vsh, fsh); glerr != nil { err = fmt.Errorf("Failed to create shader program: %s", glerr) return } // initialize the uniform and layout references gl.Uniforms(program, uniforms) gl.Layouts(program, layouts) if glerr := gl.GetError(); glerr != gl.NO_ERROR { log.Printf("shader:Bind need to find and fix error %X", glerr) } return }