// 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.mvp64.Mult(tag.mvp64, tag.persp) tag.mvp32 = renderMatrix(tag.mvp64, tag.mvp32) gl.UniformMatrix4fv(tag.mvpRef, 1, false, tag.mvp32.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, gl.Pointer(nil)) 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() } }
// Implements Renderer interface. func (gc *opengl) BindShader(sh *data.Shader) (pref uint32, err error) { pref = gl.CreateProgram() // FUTURE: get rid of BindAttribLocation and use layout instead. // this needs GLSL 330 instead of 150 // eg: layout(location=0) in vec4 in_position; gl.BindAttribLocation(pref, 0, "in_v") // matches vattr in bindModel gl.BindAttribLocation(pref, 1, "in_n") // matches nattr in bindModel gl.BindAttribLocation(pref, 2, "in_t") // matches tattr in bindModel // compile and link the shader program. if glerr := gl.BindProgram(pref, sh.Vsh, sh.Fsh); glerr != nil { err = fmt.Errorf("Failed to create shader program: %s\n", glerr) return } // initialize the uniform references var errmsg string for label, _ := range sh.Uniforms { if uid := gl.GetUniformLocation(pref, label); uid >= 0 { sh.Uniforms[label] = uid } else { errnum := gl.GetError() errmsg += fmt.Sprintf("No %s uniform in shader %X\n", label, errnum) } } if len(errmsg) > 0 { err = errors.New(errmsg) } if glerr := gl.GetError(); glerr != gl.NO_ERROR { log.Printf("opengl:bindShader need to find and fix error %X", glerr) } return }
// Implements Renderer interface. func (gc *opengl) BindTexture(texture *data.Texture) (err error) { if texture == nil { return } if glerr := gl.GetError(); glerr != gl.NO_ERROR { log.Printf("opengl:bindTexture need to find and fix prior error %X", glerr) } gl.GenTextures(1, &(texture.Tid)) gl.BindTexture(gl.TEXTURE_2D, texture.Tid) // ensure image is in RGBA format b := texture.Img.Bounds() rgba := image.NewRGBA(image.Rect(0, 0, b.Dx(), b.Dy())) draw.Draw(rgba, rgba.Bounds(), texture.Img, b.Min, draw.Src) width, height := int32(b.Dx()), int32(b.Dy()) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Pointer(&(rgba.Pix[0]))) gl.GenerateMipmap(gl.TEXTURE_2D) 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_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR) if glerr := gl.GetError(); glerr != gl.NO_ERROR { err = fmt.Errorf("Failed binding texture %s\n", texture.Name) } return }
// 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) } } }
// Implements Renderer interface. func (gc *opengl) BindModel(mesh *data.Mesh) (err error) { if glerr := gl.GetError(); glerr != gl.NO_ERROR { log.Printf("opengl:bindModel need to find and fix prior error %X", glerr) } // Gather the one scene into this one vertex array object. gl.GenVertexArrays(1, &(mesh.Vao)) gl.BindVertexArray(mesh.Vao) // vertex data. var vbuff uint32 gl.GenBuffers(1, &vbuff) gl.BindBuffer(gl.ARRAY_BUFFER, vbuff) gl.BufferData(gl.ARRAY_BUFFER, int64(len(mesh.V)*4), gl.Pointer(&(mesh.V[0])), gl.STATIC_DRAW) var vattr uint32 = 0 gl.VertexAttribPointer(vattr, 4, gl.FLOAT, false, 0, 0) gl.EnableVertexAttribArray(vattr) // normal data. var nbuff uint32 gl.GenBuffers(1, &nbuff) gl.BindBuffer(gl.ARRAY_BUFFER, nbuff) gl.BufferData(gl.ARRAY_BUFFER, int64(len(mesh.N)*4), gl.Pointer(&(mesh.N[0])), gl.STATIC_DRAW) var nattr uint32 = 1 gl.VertexAttribPointer(nattr, 3, gl.FLOAT, false, 0, 0) gl.EnableVertexAttribArray(nattr) // texture coordatinate, 2 float32's if len(mesh.T) > 0 { var tbuff uint32 gl.GenBuffers(1, &tbuff) gl.BindBuffer(gl.ARRAY_BUFFER, tbuff) gl.BufferData(gl.ARRAY_BUFFER, int64(len(mesh.T)*4), gl.Pointer(&(mesh.T[0])), gl.STATIC_DRAW) var tattr uint32 = 2 gl.VertexAttribPointer(tattr, 2, gl.FLOAT, false, 0, 0) gl.EnableVertexAttribArray(tattr) } // faces data, uint16 in this case, so 2 bytes per element. var fbuff uint32 gl.GenBuffers(1, &fbuff) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, fbuff) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, int64(len(mesh.F)*2), gl.Pointer(&(mesh.F[0])), gl.STATIC_DRAW) if glerr := gl.GetError(); glerr != gl.NO_ERROR { err = fmt.Errorf("Failed binding model %s\n", mesh.Name) } return }
// Implements Renderer interface. func (gc *opengl) BindGlyphs(mesh *data.Mesh) (err error) { if glerr := gl.GetError(); glerr != gl.NO_ERROR { log.Printf("opengl:bindGlyphs need to find and fix prior error %X", glerr) } // Reuse existing vertex array object, otherwise create one. if mesh.Vao == 0 { gl.GenVertexArrays(1, &(mesh.Vao)) } gl.BindVertexArray(mesh.Vao) // Reuse existing vertex buffer, otherwise create one. if mesh.Vbuf == 0 { gl.GenBuffers(1, &(mesh.Vbuf)) } gl.BindBuffer(gl.ARRAY_BUFFER, mesh.Vbuf) gl.BufferData(gl.ARRAY_BUFFER, int64(len(mesh.V)*4), gl.Pointer(&(mesh.V[0])), gl.DYNAMIC_DRAW) var vattr uint32 = 0 gl.VertexAttribPointer(vattr, 4, gl.FLOAT, false, 0, 0) gl.EnableVertexAttribArray(vattr) // Reuse existing texture buffer, otherwise create one. if mesh.Tbuf == 0 { gl.GenBuffers(1, &(mesh.Tbuf)) } gl.BindBuffer(gl.ARRAY_BUFFER, mesh.Tbuf) gl.BufferData(gl.ARRAY_BUFFER, int64(len(mesh.T)*4), gl.Pointer(&(mesh.T[0])), gl.DYNAMIC_DRAW) var tattr uint32 = 2 gl.VertexAttribPointer(tattr, 2, gl.FLOAT, false, 0, 0) gl.EnableVertexAttribArray(tattr) // Reuse existing faces buffer, otherwise create one. if mesh.Fbuf == 0 { gl.GenBuffers(1, &mesh.Fbuf) } gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, mesh.Fbuf) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, int64(len(mesh.F)*2), gl.Pointer(&(mesh.F[0])), gl.DYNAMIC_DRAW) if glerr := gl.GetError(); glerr != gl.NO_ERROR { err = fmt.Errorf("Failed binding glyphs %s\n", mesh.Name) } return }
// initTexture loads the texture and binds it to the graphics device. func (tb *tbtag) initTexture() { texture := &data.Texture{} loader := data.NewLoader() if loader.Load("image", &texture); texture != nil { tb.texture = texture gl.GenTextures(1, &texture.Tid) gl.BindTexture(gl.TEXTURE_2D, texture.Tid) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) // ensure image is in RGBA format b := texture.Img.Bounds() rgba := image.NewRGBA(image.Rect(0, 0, b.Dx(), b.Dy())) draw.Draw(rgba, rgba.Bounds(), texture.Img, b.Min, draw.Src) width, height := int32(b.Dx()), int32(b.Dy()) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Pointer(&(rgba.Pix[0]))) if glerr := gl.GetError(); glerr != gl.NO_ERROR { fmt.Printf("Failed binding texture image.png\n") } } else { fmt.Println("Could not load image.png file.") } }