Created
April 12, 2022 00:19
-
-
Save j100002ben/9db1b2e9d667541239ea730bef4df5a9 to your computer and use it in GitHub Desktop.
g3n Framebuffer example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // renderer/fbo_renderer.go | |
| // Copyright 2016 The G3N Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package renderer | |
| import ( | |
| "errors" | |
| "github.com/g3n/engine/camera" | |
| "github.com/g3n/engine/core" | |
| "github.com/g3n/engine/gls" | |
| "github.com/g3n/engine/gui" | |
| "github.com/g3n/engine/light" | |
| "github.com/g3n/engine/math32" | |
| "github.com/g3n/engine/texture" | |
| "github.com/g3n/engine/util/helper" | |
| ) | |
| type FBORenderer struct { | |
| Renderer | |
| scene *core.Node | |
| camera *camera.Camera | |
| light *light.Ambient | |
| panel *gui.Panel | |
| tex *texture.Texture2D | |
| fbWidth int32 | |
| fbHeight int32 | |
| width float32 | |
| height float32 | |
| framebufferName uint32 | |
| renderedTexture uint32 | |
| attachment uint32 | |
| vx int32 | |
| vy int32 | |
| vw int32 | |
| vh int32 | |
| } | |
| func NewFBORenderer(gs *gls.GLS, fbwidth, fbheight int32, width, height float32, attachment uint32) (*FBORenderer, error) { | |
| r := &FBORenderer{} | |
| r.Renderer = *NewRenderer(gs) | |
| r.fbWidth = fbwidth | |
| r.fbHeight = fbheight | |
| r.width = width | |
| r.height = height | |
| // The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer. | |
| r.framebufferName = gs.GenFramebuffer() | |
| gs.BindFramebuffer(gls.FRAMEBUFFER, r.framebufferName) | |
| // The texture we're going to render to | |
| r.renderedTexture = gs.GenTexture() | |
| // "Bind" the newly created texture : all future texture functions will modify this texture | |
| gs.BindTexture(gls.TEXTURE_2D, r.renderedTexture) | |
| // Give an empty image to OpenGL ( the last "0" ) | |
| gs.TexImage2D(gls.TEXTURE_2D, 0, gls.RGBA, fbwidth, fbheight, gls.RGBA, gls.UNSIGNED_BYTE, nil) | |
| // Poor filtering. Needed ! | |
| gs.TexParameteri(gls.TEXTURE_2D, gls.TEXTURE_MAG_FILTER, gls.LINEAR) | |
| gs.TexParameteri(gls.TEXTURE_2D, gls.TEXTURE_MIN_FILTER, gls.LINEAR) | |
| // Set "renderedTexture" as our colour attachement #0 | |
| gs.FramebufferTexture2D(gls.FRAMEBUFFER, attachment, gls.TEXTURE_2D, r.renderedTexture) | |
| // Set the list of draw buffers. | |
| gs.DrawBuffer(attachment) | |
| // Always check that our framebuffer is ok | |
| if !gs.CheckFramebufferStatus(gls.FRAMEBUFFER) { | |
| return nil, errors.New("framebuffer not ready") | |
| } | |
| err := r.AddDefaultShaders() | |
| if err != nil { | |
| return nil, err | |
| } | |
| gs.BindFramebuffer(gls.FRAMEBUFFER, 0) | |
| // Create fbo scene | |
| r.scene = core.NewNode() | |
| r.scene.Add(helper.NewAxes(0)) | |
| // Create perspective camera | |
| r.camera = camera.New(1) | |
| r.scene.Add(r.camera) | |
| // Create and add ambient light to scene | |
| r.light = light.NewAmbient(&math32.Color{1.0, 1.0, 1.0}, 1) | |
| r.scene.Add(r.light) | |
| r.panel = gui.NewPanel(r.width, r.height) | |
| r.panel.SetContentSize(r.width, r.height) | |
| r.tex = texture.NewTexture2DFromGLSTexture(gs, r.TextureName()) | |
| r.panel.Material().AddTexture(r.tex) | |
| r.panel.Material().SetShader("fbopanel") | |
| return r, nil | |
| } | |
| func (r *FBORenderer) Scene() *core.Node { | |
| return r.scene | |
| } | |
| func (r *FBORenderer) Camera() *camera.Camera { | |
| return r.camera | |
| } | |
| func (r *FBORenderer) Light() *light.Ambient { | |
| return r.light | |
| } | |
| func (r *FBORenderer) Panel() *gui.Panel { | |
| return r.panel | |
| } | |
| func (r *FBORenderer) TextureName() uint32 { | |
| return r.renderedTexture | |
| } | |
| func (r *FBORenderer) PreRender() { | |
| r.vx, r.vy, r.vw, r.vh = r.gs.GetViewport() | |
| r.gs.Viewport(0, 0, r.fbWidth, r.fbHeight) | |
| r.gs.BindFramebuffer(gls.FRAMEBUFFER, r.framebufferName) | |
| r.gs.ClearColor(1, 1, 1, 0) | |
| r.gs.Clear(gls.COLOR_BUFFER_BIT | gls.DEPTH_BUFFER_BIT) | |
| } | |
| // Render renders the specified scene using the specified camera. Returns an an error. | |
| func (r *FBORenderer) Render(scene core.INode, cam camera.ICamera) error { | |
| err := r.Renderer.Render(scene, cam) | |
| r.gs.BindFramebuffer(gls.FRAMEBUFFER, 0) | |
| r.gs.Viewport(r.vx, r.vy, r.vw, r.vh) | |
| r.gs.ClearColor(0, 0, 0, 1) | |
| return err | |
| } | |
| const fbopanel_vertex_source = `#include <attributes> | |
| // Model uniforms | |
| uniform mat4 ModelMatrix; | |
| // Outputs for fragment shader | |
| out vec2 FragTexcoord; | |
| void main() { | |
| // Do not flip texture coordinates | |
| FragTexcoord = VertexTexcoord; | |
| // Set position | |
| vec4 pos = vec4(VertexPosition.xyz, 1); | |
| gl_Position = ModelMatrix * pos; | |
| } | |
| ` | |
| func (r *Renderer) EnblaeFBOPanel() { | |
| r.AddShader("fbopanel_vertex", fbopanel_vertex_source) | |
| r.AddProgram("fbopanel", "fbopanel_vertex", "panel_fragment") | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // gls/gls-desktop.go | |
| // Copyright 2016 The G3N Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| //go:build !wasm | |
| // +build !wasm | |
| package gls | |
| // #include <stdlib.h> | |
| // #include "glcorearb.h" | |
| // #include "glapi.h" | |
| import "C" | |
| import ( | |
| "fmt" | |
| "reflect" | |
| "unsafe" | |
| ) | |
| // GLS encapsulates the state of an OpenGL context and contains | |
| // methods to call OpenGL functions. | |
| type GLS struct { | |
| stats Stats // statistics | |
| prog *Program // current active shader program | |
| programs map[*Program]bool // shader programs cache | |
| checkErrors bool // check openGL API errors flag | |
| // Cache OpenGL state to avoid making unnecessary API calls | |
| activeTexture uint32 // cached last set active texture unit | |
| viewportX int32 // cached last set viewport x | |
| viewportY int32 // cached last set viewport y | |
| viewportWidth int32 // cached last set viewport width | |
| viewportHeight int32 // cached last set viewport height | |
| lineWidth float32 // cached last set line width | |
| sideView int // cached last set triangle side view mode | |
| frontFace uint32 // cached last set glFrontFace value | |
| depthFunc uint32 // cached last set depth function | |
| depthMask int // cached last set depth mask | |
| //stencilFunc | |
| stencilMask uint32 // cached last set stencil mask | |
| capabilities map[int]int // cached capabilities (Enable/Disable) | |
| blendEquation uint32 // cached last set blend equation value | |
| blendSrc uint32 // cached last set blend src value | |
| blendDst uint32 // cached last set blend equation destination value | |
| blendEquationRGB uint32 // cached last set blend equation rgb value | |
| blendEquationAlpha uint32 // cached last set blend equation alpha value | |
| blendSrcRGB uint32 // cached last set blend src rgb | |
| blendSrcAlpha uint32 // cached last set blend src alpha value | |
| blendDstRGB uint32 // cached last set blend destination rgb value | |
| blendDstAlpha uint32 // cached last set blend destination alpha value | |
| polygonModeFace uint32 // cached last set polygon mode face | |
| polygonModeMode uint32 // cached last set polygon mode mode | |
| polygonOffsetFactor float32 // cached last set polygon offset factor | |
| polygonOffsetUnits float32 // cached last set polygon offset units | |
| gobuf []byte // conversion buffer with GO memory | |
| cbuf []byte // conversion buffer with C memory | |
| } | |
| // New creates and returns a new instance of a GLS object, | |
| // which encapsulates the state of an OpenGL context. | |
| // This should be called only after an active OpenGL context | |
| // is established, such as by creating a new window. | |
| func New() (*GLS, error) { | |
| gs := new(GLS) | |
| gs.reset() | |
| // Load OpenGL functions | |
| err := C.glapiLoad() | |
| if err != 0 { | |
| return nil, fmt.Errorf("Error loading OpenGL") | |
| } | |
| gs.setDefaultState() | |
| gs.checkErrors = true | |
| // Preallocate conversion buffers | |
| size := 1 * 1024 | |
| gs.gobuf = make([]byte, size) | |
| p := C.malloc(C.size_t(size)) | |
| gs.cbuf = (*[1 << 30]byte)(unsafe.Pointer(p))[:size:size] | |
| return gs, nil | |
| } | |
| // SetCheckErrors enables/disables checking for errors after the | |
| // call of any OpenGL function. It is enabled by default but | |
| // could be disabled after an application is stable to improve the performance. | |
| func (gs *GLS) SetCheckErrors(enable bool) { | |
| if enable { | |
| C.glapiCheckError(1) | |
| } else { | |
| C.glapiCheckError(0) | |
| } | |
| gs.checkErrors = enable | |
| } | |
| // CheckErrors returns if error checking is enabled or not. | |
| func (gs *GLS) CheckErrors() bool { | |
| return gs.checkErrors | |
| } | |
| // reset resets the internal state kept of the OpenGL | |
| func (gs *GLS) reset() { | |
| gs.lineWidth = 0.0 | |
| gs.sideView = uintUndef | |
| gs.frontFace = 0 | |
| gs.depthFunc = 0 | |
| gs.depthMask = uintUndef | |
| gs.capabilities = make(map[int]int) | |
| gs.programs = make(map[*Program]bool) | |
| gs.prog = nil | |
| gs.activeTexture = uintUndef | |
| gs.blendEquation = uintUndef | |
| gs.blendSrc = uintUndef | |
| gs.blendDst = uintUndef | |
| gs.blendEquationRGB = 0 | |
| gs.blendEquationAlpha = 0 | |
| gs.blendSrcRGB = uintUndef | |
| gs.blendSrcAlpha = uintUndef | |
| gs.blendDstRGB = uintUndef | |
| gs.blendDstAlpha = uintUndef | |
| gs.polygonModeFace = 0 | |
| gs.polygonModeMode = 0 | |
| gs.polygonOffsetFactor = -1 | |
| gs.polygonOffsetUnits = -1 | |
| } | |
| // setDefaultState is used internally to set the initial state of OpenGL | |
| // for this context. | |
| func (gs *GLS) setDefaultState() { | |
| gs.ClearColor(0, 0, 0, 1) | |
| gs.ClearDepth(1) | |
| gs.ClearStencil(0) | |
| gs.Enable(DEPTH_TEST) | |
| //gs.DepthMask(true) | |
| gs.DepthFunc(LEQUAL) | |
| gs.FrontFace(CCW) | |
| gs.CullFace(BACK) | |
| gs.Enable(CULL_FACE) | |
| gs.Enable(BLEND) | |
| gs.BlendEquation(FUNC_ADD) | |
| gs.BlendFunc(SRC_ALPHA, ONE_MINUS_SRC_ALPHA) | |
| gs.Enable(VERTEX_PROGRAM_POINT_SIZE) | |
| gs.Enable(PROGRAM_POINT_SIZE) | |
| gs.Enable(MULTISAMPLE) | |
| gs.Enable(POLYGON_OFFSET_FILL) | |
| gs.Enable(POLYGON_OFFSET_LINE) | |
| gs.Enable(POLYGON_OFFSET_POINT) | |
| } | |
| // Stats copy the current values of the internal statistics structure | |
| // to the specified pointer. | |
| func (gs *GLS) Stats(s *Stats) { | |
| *s = gs.stats | |
| s.Shaders = len(gs.programs) | |
| } | |
| // ActiveTexture selects which texture unit subsequent texture state calls | |
| // will affect. The number of texture units an implementation supports is | |
| // implementation dependent, but must be at least 48 in GL 3.3. | |
| func (gs *GLS) ActiveTexture(texture uint32) { | |
| if gs.activeTexture == texture { | |
| return | |
| } | |
| C.glActiveTexture(C.GLenum(texture)) | |
| gs.activeTexture = texture | |
| } | |
| // AttachShader attaches the specified shader object to the specified program object. | |
| func (gs *GLS) AttachShader(program, shader uint32) { | |
| C.glAttachShader(C.GLuint(program), C.GLuint(shader)) | |
| } | |
| // BindBuffer binds a buffer object to the specified buffer binding point. | |
| func (gs *GLS) BindBuffer(target int, vbo uint32) { | |
| C.glBindBuffer(C.GLenum(target), C.GLuint(vbo)) | |
| } | |
| // BindTexture lets you create or use a named texture. | |
| func (gs *GLS) BindTexture(target int, tex uint32) { | |
| C.glBindTexture(C.GLenum(target), C.GLuint(tex)) | |
| } | |
| // BindVertexArray binds the vertex array object. | |
| func (gs *GLS) BindVertexArray(vao uint32) { | |
| C.glBindVertexArray(C.GLuint(vao)) | |
| } | |
| // BlendEquation sets the blend equations for all draw buffers. | |
| func (gs *GLS) BlendEquation(mode uint32) { | |
| if gs.blendEquation == mode { | |
| return | |
| } | |
| C.glBlendEquation(C.GLenum(mode)) | |
| gs.blendEquation = mode | |
| } | |
| // BlendEquationSeparate sets the blend equations for all draw buffers | |
| // allowing different equations for the RGB and alpha components. | |
| func (gs *GLS) BlendEquationSeparate(modeRGB uint32, modeAlpha uint32) { | |
| if gs.blendEquationRGB == modeRGB && gs.blendEquationAlpha == modeAlpha { | |
| return | |
| } | |
| C.glBlendEquationSeparate(C.GLenum(modeRGB), C.GLenum(modeAlpha)) | |
| gs.blendEquationRGB = modeRGB | |
| gs.blendEquationAlpha = modeAlpha | |
| } | |
| // BlendFunc defines the operation of blending for | |
| // all draw buffers when blending is enabled. | |
| func (gs *GLS) BlendFunc(sfactor, dfactor uint32) { | |
| if gs.blendSrc == sfactor && gs.blendDst == dfactor { | |
| return | |
| } | |
| C.glBlendFunc(C.GLenum(sfactor), C.GLenum(dfactor)) | |
| gs.blendSrc = sfactor | |
| gs.blendDst = dfactor | |
| } | |
| // BlendFuncSeparate defines the operation of blending for all draw buffers when blending | |
| // is enabled, allowing different operations for the RGB and alpha components. | |
| func (gs *GLS) BlendFuncSeparate(srcRGB uint32, dstRGB uint32, srcAlpha uint32, dstAlpha uint32) { | |
| if gs.blendSrcRGB == srcRGB && gs.blendDstRGB == dstRGB && | |
| gs.blendSrcAlpha == srcAlpha && gs.blendDstAlpha == dstAlpha { | |
| return | |
| } | |
| C.glBlendFuncSeparate(C.GLenum(srcRGB), C.GLenum(dstRGB), C.GLenum(srcAlpha), C.GLenum(dstAlpha)) | |
| gs.blendSrcRGB = srcRGB | |
| gs.blendDstRGB = dstRGB | |
| gs.blendSrcAlpha = srcAlpha | |
| gs.blendDstAlpha = dstAlpha | |
| } | |
| // BufferData creates a new data store for the buffer object currently | |
| // bound to target, deleting any pre-existing data store. | |
| func (gs *GLS) BufferData(target uint32, size int, data interface{}, usage uint32) { | |
| C.glBufferData(C.GLenum(target), C.GLsizeiptr(size), ptr(data), C.GLenum(usage)) | |
| } | |
| // ClearColor specifies the red, green, blue, and alpha values | |
| // used by glClear to clear the color buffers. | |
| func (gs *GLS) ClearColor(r, g, b, a float32) { | |
| C.glClearColor(C.GLfloat(r), C.GLfloat(g), C.GLfloat(b), C.GLfloat(a)) | |
| } | |
| // ClearDepth specifies the depth value used by Clear to clear the depth buffer. | |
| func (gs *GLS) ClearDepth(v float32) { | |
| C.glClearDepth(C.GLclampd(v)) | |
| } | |
| // ClearStencil specifies the index used by Clear to clear the stencil buffer. | |
| func (gs *GLS) ClearStencil(v int32) { | |
| C.glClearStencil(C.GLint(v)) | |
| } | |
| // Clear sets the bitplane area of the window to values previously | |
| // selected by ClearColor, ClearDepth, and ClearStencil. | |
| func (gs *GLS) Clear(mask uint) { | |
| C.glClear(C.GLbitfield(mask)) | |
| } | |
| // CompileShader compiles the source code strings that | |
| // have been stored in the specified shader object. | |
| func (gs *GLS) CompileShader(shader uint32) { | |
| C.glCompileShader(C.GLuint(shader)) | |
| } | |
| // CreateProgram creates an empty program object and returns | |
| // a non-zero value by which it can be referenced. | |
| func (gs *GLS) CreateProgram() uint32 { | |
| p := C.glCreateProgram() | |
| return uint32(p) | |
| } | |
| // CreateShader creates an empty shader object and returns | |
| // a non-zero value by which it can be referenced. | |
| func (gs *GLS) CreateShader(stype uint32) uint32 { | |
| h := C.glCreateShader(C.GLenum(stype)) | |
| return uint32(h) | |
| } | |
| // DeleteBuffers deletes nbuffer objects named | |
| // by the elements of the provided array. | |
| func (gs *GLS) DeleteBuffers(bufs ...uint32) { | |
| C.glDeleteBuffers(C.GLsizei(len(bufs)), (*C.GLuint)(&bufs[0])) | |
| gs.stats.Buffers -= len(bufs) | |
| } | |
| // DeleteShader frees the memory and invalidates the name | |
| // associated with the specified shader object. | |
| func (gs *GLS) DeleteShader(shader uint32) { | |
| C.glDeleteShader(C.GLuint(shader)) | |
| } | |
| // DeleteProgram frees the memory and invalidates the name | |
| // associated with the specified program object. | |
| func (gs *GLS) DeleteProgram(program uint32) { | |
| C.glDeleteProgram(C.GLuint(program)) | |
| } | |
| // DeleteTextures deletes ntextures named | |
| // by the elements of the provided array. | |
| func (gs *GLS) DeleteTextures(tex ...uint32) { | |
| C.glDeleteTextures(C.GLsizei(len(tex)), (*C.GLuint)(&tex[0])) | |
| gs.stats.Textures -= len(tex) | |
| } | |
| // DeleteVertexArrays deletes nvertex array objects named | |
| // by the elements of the provided array. | |
| func (gs *GLS) DeleteVertexArrays(vaos ...uint32) { | |
| C.glDeleteVertexArrays(C.GLsizei(len(vaos)), (*C.GLuint)(&vaos[0])) | |
| gs.stats.Vaos -= len(vaos) | |
| } | |
| // ReadPixels returns the current rendered image. | |
| // x, y: specifies the window coordinates of the first pixel that is read from the frame buffer. | |
| // width, height: specifies the dimensions of the pixel rectangle. | |
| // format: specifies the format of the pixel data. | |
| // format_type: specifies the data type of the pixel data. | |
| // more information: http://docs.gl/gl3/glReadPixels | |
| func (gs *GLS) ReadPixels(x, y, width, height, format, formatType int) []byte { | |
| size := uint32((width - x) * (height - y) * 4) | |
| C.glReadPixels(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(formatType), unsafe.Pointer(gs.gobufSize(size))) | |
| return gs.gobuf[:size] | |
| } | |
| // DepthFunc specifies the function used to compare each incoming pixel | |
| // depth value with the depth value present in the depth buffer. | |
| func (gs *GLS) DepthFunc(mode uint32) { | |
| if gs.depthFunc == mode { | |
| return | |
| } | |
| C.glDepthFunc(C.GLenum(mode)) | |
| gs.depthFunc = mode | |
| } | |
| // DepthMask enables or disables writing into the depth buffer. | |
| func (gs *GLS) DepthMask(flag bool) { | |
| if gs.depthMask == intTrue && flag { | |
| return | |
| } | |
| if gs.depthMask == intFalse && !flag { | |
| return | |
| } | |
| C.glDepthMask(bool2c(flag)) | |
| if flag { | |
| gs.depthMask = intTrue | |
| } else { | |
| gs.depthMask = intFalse | |
| } | |
| } | |
| func (gs *GLS) StencilOp(fail, zfail, zpass uint32) { | |
| // TODO save state | |
| C.glStencilOp(C.GLenum(fail), C.GLenum(zfail), C.GLenum(zpass)) | |
| } | |
| func (gs *GLS) StencilFunc(mode uint32, ref int32, mask uint32) { | |
| // TODO save state | |
| C.glStencilFunc(C.GLenum(mode), C.GLint(ref), C.GLuint(mask)) | |
| } | |
| // TODO doc | |
| // StencilMask enables or disables writing into the stencil buffer. | |
| func (gs *GLS) StencilMask(mask uint32) { | |
| if gs.stencilMask == mask { | |
| return | |
| } | |
| C.glStencilMask(C.GLuint(mask)) | |
| gs.stencilMask = mask | |
| } | |
| // DrawArrays renders primitives from array data. | |
| func (gs *GLS) DrawArrays(mode uint32, first int32, count int32) { | |
| C.glDrawArrays(C.GLenum(mode), C.GLint(first), C.GLsizei(count)) | |
| gs.stats.Drawcalls++ | |
| } | |
| // DrawElements renders primitives from array data. | |
| func (gs *GLS) DrawElements(mode uint32, count int32, itype uint32, start uint32) { | |
| C.glDrawElements(C.GLenum(mode), C.GLsizei(count), C.GLenum(itype), unsafe.Pointer(uintptr(start))) | |
| gs.stats.Drawcalls++ | |
| } | |
| // Enable enables the specified capability. | |
| func (gs *GLS) Enable(cap int) { | |
| if gs.capabilities[cap] == capEnabled { | |
| gs.stats.Caphits++ | |
| return | |
| } | |
| C.glEnable(C.GLenum(cap)) | |
| gs.capabilities[cap] = capEnabled | |
| } | |
| // Disable disables the specified capability. | |
| func (gs *GLS) Disable(cap int) { | |
| if gs.capabilities[cap] == capDisabled { | |
| gs.stats.Caphits++ | |
| return | |
| } | |
| C.glDisable(C.GLenum(cap)) | |
| gs.capabilities[cap] = capDisabled | |
| } | |
| // EnableVertexAttribArray enables a generic vertex attribute array. | |
| func (gs *GLS) EnableVertexAttribArray(index uint32) { | |
| C.glEnableVertexAttribArray(C.GLuint(index)) | |
| } | |
| // CullFace specifies whether front- or back-facing facets can be culled. | |
| func (gs *GLS) CullFace(mode uint32) { | |
| C.glCullFace(C.GLenum(mode)) | |
| } | |
| // FrontFace defines front- and back-facing polygons. | |
| func (gs *GLS) FrontFace(mode uint32) { | |
| if gs.frontFace == mode { | |
| return | |
| } | |
| C.glFrontFace(C.GLenum(mode)) | |
| gs.frontFace = mode | |
| } | |
| // GenFramebuffer generate framebuffer object names. | |
| func (gs *GLS) GenFramebuffer() uint32 { | |
| var buf uint32 | |
| C.glGenFramebuffers(1, (*C.GLuint)(&buf)) | |
| gs.stats.Buffers++ | |
| return buf | |
| } | |
| // GenRenderbuffers generate renderbuffer object names | |
| func (gs *GLS) GenRenderbuffer() uint32 { | |
| var buf uint32 | |
| C.glGenRenderbuffers(1, (*C.GLuint)(&buf)) | |
| gs.stats.Buffers++ | |
| return buf | |
| } | |
| // BindFramebuffer bind a framebuffer to a framebuffer target | |
| func (gs *GLS) BindFramebuffer(target int, frame uint32) { | |
| C.glBindFramebuffer(C.GLenum(target), C.GLuint(frame)) | |
| } | |
| // BindRenderbuffer bind a named renderbuffer object | |
| func (gs *GLS) BindRenderbuffer(target int, render uint32) { | |
| C.glBindRenderbuffer(C.GLenum(target), C.GLuint(render)) | |
| } | |
| // RenderbufferStorage establish data storage, format and dimensions of a renderbuffer object's image | |
| func (gs *GLS) RenderbufferStorage(target int, iformat uint32, width, height int32) { | |
| C.glRenderbufferStorage(C.GLenum(target), C.GLenum(iformat), C.GLsizei(width), C.GLsizei(height)) | |
| } | |
| // FramebufferRenderbuffer attach a renderbuffer as a logical buffer to the currently bound framebuffer object | |
| func (gs *GLS) FramebufferRenderbuffer(target int, attachment uint32, rtarget int, render uint32) { | |
| C.glFramebufferRenderbuffer(C.GLenum(target), C.GLenum(attachment), C.GLenum(rtarget), C.GLuint(render)) | |
| } | |
| // FramebufferTexture2D | |
| func (gs *GLS) FramebufferTexture2D(target uint32, attachment uint32, ttarget uint32, texture uint32) { | |
| C.glFramebufferTexture2D(C.GLenum(target), C.GLenum(attachment), C.GLenum(ttarget), C.GLuint(texture), C.GLint(0)) | |
| } | |
| // FramebufferTexture attach a level of a texture object as a logical buffer of a framebuffer object | |
| func (gs *GLS) FramebufferTexture(target uint32, attachment uint32, texture uint32) { | |
| C.glFramebufferTexture(C.GLenum(target), C.GLenum(attachment), C.GLuint(texture), C.GLint(0)) | |
| } | |
| // DrawBuffer specifies a list of color buffers to be drawn into | |
| func (gs *GLS) DrawBuffer(bufs ...uint32) { | |
| C.glDrawBuffers(C.GLsizei(len(bufs)), (*C.GLenum)(&bufs[0])) | |
| } | |
| // CheckFramebufferStatus | |
| func (gs *GLS) CheckFramebufferStatus(target uint32) bool { | |
| return C.glCheckFramebufferStatus(C.GLenum(target)) == FRAMEBUFFER_COMPLETE | |
| } | |
| // GenBuffer generates a buffer object name. | |
| func (gs *GLS) GenBuffer() uint32 { | |
| var buf uint32 | |
| C.glGenBuffers(1, (*C.GLuint)(&buf)) | |
| gs.stats.Buffers++ | |
| return buf | |
| } | |
| // GenerateMipmap generates mipmaps for the specified texture target. | |
| func (gs *GLS) GenerateMipmap(target uint32) { | |
| C.glGenerateMipmap(C.GLenum(target)) | |
| } | |
| // GenTexture generates a texture object name. | |
| func (gs *GLS) GenTexture() uint32 { | |
| var tex uint32 | |
| C.glGenTextures(1, (*C.GLuint)(&tex)) | |
| gs.stats.Textures++ | |
| return tex | |
| } | |
| // GenVertexArray generates a vertex array object name. | |
| func (gs *GLS) GenVertexArray() uint32 { | |
| var vao uint32 | |
| C.glGenVertexArrays(1, (*C.GLuint)(&vao)) | |
| gs.stats.Vaos++ | |
| return vao | |
| } | |
| // GetAttribLocation returns the location of the specified attribute variable. | |
| func (gs *GLS) GetAttribLocation(program uint32, name string) int32 { | |
| loc := C.glGetAttribLocation(C.GLuint(program), gs.gobufStr(name)) | |
| return int32(loc) | |
| } | |
| // GetProgramiv returns the specified parameter from the specified program object. | |
| func (gs *GLS) GetProgramiv(program, pname uint32, params *int32) { | |
| C.glGetProgramiv(C.GLuint(program), C.GLenum(pname), (*C.GLint)(params)) | |
| } | |
| // GetProgramInfoLog returns the information log for the specified program object. | |
| func (gs *GLS) GetProgramInfoLog(program uint32) string { | |
| var length int32 | |
| gs.GetProgramiv(program, INFO_LOG_LENGTH, &length) | |
| if length == 0 { | |
| return "" | |
| } | |
| C.glGetProgramInfoLog(C.GLuint(program), C.GLsizei(length), nil, gs.gobufSize(uint32(length))) | |
| return string(gs.gobuf[:length]) | |
| } | |
| // GetShaderInfoLog returns the information log for the specified shader object. | |
| func (gs *GLS) GetShaderInfoLog(shader uint32) string { | |
| var length int32 | |
| gs.GetShaderiv(shader, INFO_LOG_LENGTH, &length) | |
| if length == 0 { | |
| return "" | |
| } | |
| C.glGetShaderInfoLog(C.GLuint(shader), C.GLsizei(length), nil, gs.gobufSize(uint32(length))) | |
| return string(gs.gobuf[:length]) | |
| } | |
| // GetString returns a string describing the specified aspect of the current GL connection. | |
| func (gs *GLS) GetString(name uint32) string { | |
| cs := C.glGetString(C.GLenum(name)) | |
| return C.GoString((*C.char)(unsafe.Pointer(cs))) | |
| } | |
| // GetUniformLocation returns the location of a uniform variable for the specified program. | |
| func (gs *GLS) GetUniformLocation(program uint32, name string) int32 { | |
| loc := C.glGetUniformLocation(C.GLuint(program), gs.gobufStr(name)) | |
| return int32(loc) | |
| } | |
| // GetViewport returns the current viewport information. | |
| func (gs *GLS) GetViewport() (x, y, width, height int32) { | |
| return gs.viewportX, gs.viewportY, gs.viewportWidth, gs.viewportHeight | |
| } | |
| // LineWidth specifies the rasterized width of both aliased and antialiased lines. | |
| func (gs *GLS) LineWidth(width float32) { | |
| if gs.lineWidth == width { | |
| return | |
| } | |
| C.glLineWidth(C.GLfloat(width)) | |
| gs.lineWidth = width | |
| } | |
| // LinkProgram links the specified program object. | |
| func (gs *GLS) LinkProgram(program uint32) { | |
| C.glLinkProgram(C.GLuint(program)) | |
| } | |
| // GetShaderiv returns the specified parameter from the specified shader object. | |
| func (gs *GLS) GetShaderiv(shader, pname uint32, params *int32) { | |
| C.glGetShaderiv(C.GLuint(shader), C.GLenum(pname), (*C.GLint)(params)) | |
| } | |
| // Scissor defines the scissor box rectangle in window coordinates. | |
| func (gs *GLS) Scissor(x, y int32, width, height uint32) { | |
| C.glScissor(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height)) | |
| } | |
| // ShaderSource sets the source code for the specified shader object. | |
| func (gs *GLS) ShaderSource(shader uint32, src string) { | |
| csource := gs.cbufStr(src) | |
| C.glShaderSource(C.GLuint(shader), 1, (**C.GLchar)(unsafe.Pointer(&csource)), nil) | |
| } | |
| // TexImage2D specifies a two-dimensional texture image. | |
| func (gs *GLS) TexImage2D(target uint32, level int32, iformat int32, width int32, height int32, format uint32, itype uint32, data interface{}) { | |
| C.glTexImage2D(C.GLenum(target), | |
| C.GLint(level), | |
| C.GLint(iformat), | |
| C.GLsizei(width), | |
| C.GLsizei(height), | |
| C.GLint(0), | |
| C.GLenum(format), | |
| C.GLenum(itype), | |
| ptr(data)) | |
| } | |
| // CompressedTexImage2D specifies a two-dimensional compressed texture image. | |
| func (gs *GLS) CompressedTexImage2D(target uint32, level uint32, iformat uint32, width int32, height int32, size int32, data interface{}) { | |
| C.glCompressedTexImage2D(C.GLenum(target), | |
| C.GLint(level), | |
| C.GLenum(iformat), | |
| C.GLsizei(width), | |
| C.GLsizei(height), | |
| C.GLint(0), | |
| C.GLsizei(size), | |
| ptr(data)) | |
| } | |
| // TexParameteri sets the specified texture parameter on the specified texture. | |
| func (gs *GLS) TexParameteri(target uint32, pname uint32, param int32) { | |
| C.glTexParameteri(C.GLenum(target), C.GLenum(pname), C.GLint(param)) | |
| } | |
| // PolygonMode controls the interpretation of polygons for rasterization. | |
| func (gs *GLS) PolygonMode(face, mode uint32) { | |
| if gs.polygonModeFace == face && gs.polygonModeMode == mode { | |
| return | |
| } | |
| C.glPolygonMode(C.GLenum(face), C.GLenum(mode)) | |
| gs.polygonModeFace = face | |
| gs.polygonModeMode = mode | |
| } | |
| // PolygonOffset sets the scale and units used to calculate depth values. | |
| func (gs *GLS) PolygonOffset(factor float32, units float32) { | |
| if gs.polygonOffsetFactor == factor && gs.polygonOffsetUnits == units { | |
| return | |
| } | |
| C.glPolygonOffset(C.GLfloat(factor), C.GLfloat(units)) | |
| gs.polygonOffsetFactor = factor | |
| gs.polygonOffsetUnits = units | |
| } | |
| // Uniform1i sets the value of an int uniform variable for the current program object. | |
| func (gs *GLS) Uniform1i(location int32, v0 int32) { | |
| C.glUniform1i(C.GLint(location), C.GLint(v0)) | |
| gs.stats.Unisets++ | |
| } | |
| // Uniform1f sets the value of a float uniform variable for the current program object. | |
| func (gs *GLS) Uniform1f(location int32, v0 float32) { | |
| C.glUniform1f(C.GLint(location), C.GLfloat(v0)) | |
| gs.stats.Unisets++ | |
| } | |
| // Uniform2f sets the value of a vec2 uniform variable for the current program object. | |
| func (gs *GLS) Uniform2f(location int32, v0, v1 float32) { | |
| C.glUniform2f(C.GLint(location), C.GLfloat(v0), C.GLfloat(v1)) | |
| gs.stats.Unisets++ | |
| } | |
| // Uniform3f sets the value of a vec3 uniform variable for the current program object. | |
| func (gs *GLS) Uniform3f(location int32, v0, v1, v2 float32) { | |
| C.glUniform3f(C.GLint(location), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2)) | |
| gs.stats.Unisets++ | |
| } | |
| // Uniform4f sets the value of a vec4 uniform variable for the current program object. | |
| func (gs *GLS) Uniform4f(location int32, v0, v1, v2, v3 float32) { | |
| C.glUniform4f(C.GLint(location), C.GLfloat(v0), C.GLfloat(v1), C.GLfloat(v2), C.GLfloat(v3)) | |
| gs.stats.Unisets++ | |
| } | |
| // UniformMatrix3fv sets the value of one or many 3x3 float matrices for the current program object. | |
| func (gs *GLS) UniformMatrix3fv(location int32, count int32, transpose bool, pm *float32) { | |
| C.glUniformMatrix3fv(C.GLint(location), C.GLsizei(count), bool2c(transpose), (*C.GLfloat)(pm)) | |
| gs.stats.Unisets++ | |
| } | |
| // UniformMatrix4fv sets the value of one or many 4x4 float matrices for the current program object. | |
| func (gs *GLS) UniformMatrix4fv(location int32, count int32, transpose bool, pm *float32) { | |
| C.glUniformMatrix4fv(C.GLint(location), C.GLsizei(count), bool2c(transpose), (*C.GLfloat)(pm)) | |
| gs.stats.Unisets++ | |
| } | |
| // Uniform1fv sets the value of one or many float uniform variables for the current program object. | |
| func (gs *GLS) Uniform1fv(location int32, count int32, v *float32) { | |
| C.glUniform1fv(C.GLint(location), C.GLsizei(count), (*C.GLfloat)(v)) | |
| gs.stats.Unisets++ | |
| } | |
| // Uniform2fv sets the value of one or many vec2 uniform variables for the current program object. | |
| func (gs *GLS) Uniform2fv(location int32, count int32, v *float32) { | |
| C.glUniform2fv(C.GLint(location), C.GLsizei(count), (*C.GLfloat)(v)) | |
| gs.stats.Unisets++ | |
| } | |
| // Uniform3fv sets the value of one or many vec3 uniform variables for the current program object. | |
| func (gs *GLS) Uniform3fv(location int32, count int32, v *float32) { | |
| C.glUniform3fv(C.GLint(location), C.GLsizei(count), (*C.GLfloat)(v)) | |
| gs.stats.Unisets++ | |
| } | |
| // Uniform4fv sets the value of one or many vec4 uniform variables for the current program object. | |
| func (gs *GLS) Uniform4fv(location int32, count int32, v *float32) { | |
| C.glUniform4fv(C.GLint(location), C.GLsizei(count), (*C.GLfloat)(v)) | |
| gs.stats.Unisets++ | |
| } | |
| // VertexAttribPointer defines an array of generic vertex attribute data. | |
| func (gs *GLS) VertexAttribPointer(index uint32, size int32, xtype uint32, normalized bool, stride int32, offset uint32) { | |
| C.glVertexAttribPointer(C.GLuint(index), C.GLint(size), C.GLenum(xtype), bool2c(normalized), C.GLsizei(stride), unsafe.Pointer(uintptr(offset))) | |
| } | |
| // Viewport sets the viewport. | |
| func (gs *GLS) Viewport(x, y, width, height int32) { | |
| C.glViewport(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height)) | |
| gs.viewportX = x | |
| gs.viewportY = y | |
| gs.viewportWidth = width | |
| gs.viewportHeight = height | |
| } | |
| // UseProgram sets the specified program as the current program. | |
| func (gs *GLS) UseProgram(prog *Program) { | |
| if prog.handle == 0 { | |
| panic("Invalid program") | |
| } | |
| C.glUseProgram(C.GLuint(prog.handle)) | |
| gs.prog = prog | |
| // Inserts program in cache if not already there. | |
| if !gs.programs[prog] { | |
| gs.programs[prog] = true | |
| log.Debug("New Program activated. Total: %d", len(gs.programs)) | |
| } | |
| } | |
| // Ptr takes a slice or pointer (to a singular scalar value or the first | |
| // element of an array or slice) and returns its GL-compatible address. | |
| // | |
| // For example: | |
| // | |
| // var data []uint8 | |
| // ... | |
| // gl.TexImage2D(gl.TEXTURE_2D, ..., gl.UNSIGNED_BYTE, gl.Ptr(&data[0])) | |
| func ptr(data interface{}) unsafe.Pointer { | |
| if data == nil { | |
| return unsafe.Pointer(nil) | |
| } | |
| var addr unsafe.Pointer | |
| v := reflect.ValueOf(data) | |
| switch v.Type().Kind() { | |
| case reflect.Ptr: | |
| e := v.Elem() | |
| switch e.Kind() { | |
| case | |
| reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, | |
| reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, | |
| reflect.Float32, reflect.Float64: | |
| addr = unsafe.Pointer(e.UnsafeAddr()) | |
| default: | |
| panic(fmt.Errorf("unsupported pointer to type %s; must be a slice or pointer to a singular scalar value or the first element of an array or slice", e.Kind())) | |
| } | |
| case reflect.Uintptr: | |
| addr = unsafe.Pointer(v.Pointer()) | |
| case reflect.Slice: | |
| addr = unsafe.Pointer(v.Index(0).UnsafeAddr()) | |
| default: | |
| panic(fmt.Errorf("unsupported type %s; must be a slice or pointer to a singular scalar value or the first element of an array or slice", v.Type())) | |
| } | |
| return addr | |
| } | |
| // bool2c convert a Go bool to C.GLboolean | |
| func bool2c(b bool) C.GLboolean { | |
| if b { | |
| return C.GLboolean(1) | |
| } | |
| return C.GLboolean(0) | |
| } | |
| // gobufSize returns a pointer to static buffer with the specified size not including the terminator. | |
| // If there is available space, there is no memory allocation. | |
| func (gs *GLS) gobufSize(size uint32) *C.GLchar { | |
| if size+1 > uint32(len(gs.gobuf)) { | |
| gs.gobuf = make([]byte, size+1) | |
| } | |
| return (*C.GLchar)(unsafe.Pointer(&gs.gobuf[0])) | |
| } | |
| // gobufStr converts a Go String to a C string by copying it to a static buffer | |
| // and returning a pointer to the start of the buffer. | |
| // If there is available space, there is no memory allocation. | |
| func (gs *GLS) gobufStr(s string) *C.GLchar { | |
| p := gs.gobufSize(uint32(len(s) + 1)) | |
| copy(gs.gobuf, s) | |
| gs.gobuf[len(s)] = 0 | |
| return p | |
| } | |
| // cbufSize returns a pointer to static buffer with C memory | |
| // If there is available space, there is no memory allocation. | |
| func (gs *GLS) cbufSize(size uint32) *C.GLchar { | |
| if size > uint32(len(gs.cbuf)) { | |
| if len(gs.cbuf) > 0 { | |
| C.free(unsafe.Pointer(&gs.cbuf[0])) | |
| } | |
| p := C.malloc(C.size_t(size)) | |
| gs.cbuf = (*[1 << 30]byte)(unsafe.Pointer(p))[:size:size] | |
| } | |
| return (*C.GLchar)(unsafe.Pointer(&gs.cbuf[0])) | |
| } | |
| // cbufStr converts a Go String to a C string by copying it to a single pre-allocated buffer | |
| // using C memory and returning a pointer to the start of the buffer. | |
| // If there is available space, there is no memory allocation. | |
| func (gs *GLS) cbufStr(s string) *C.GLchar { | |
| p := gs.cbufSize(uint32(len(s) + 1)) | |
| copy(gs.cbuf, s) | |
| gs.cbuf[len(s)] = 0 | |
| return p | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "time" | |
| "github.com/g3n/engine/app" | |
| "github.com/g3n/engine/camera" | |
| "github.com/g3n/engine/core" | |
| "github.com/g3n/engine/geometry" | |
| "github.com/g3n/engine/gls" | |
| "github.com/g3n/engine/graphic" | |
| "github.com/g3n/engine/gui" | |
| "github.com/g3n/engine/light" | |
| "github.com/g3n/engine/material" | |
| "github.com/g3n/engine/math32" | |
| "github.com/g3n/engine/renderer" | |
| "github.com/g3n/engine/util/helper" | |
| "github.com/g3n/engine/window" | |
| ) | |
| func main() { | |
| // Create application and scene | |
| a := app.App() | |
| scene := core.NewNode() | |
| // Set the scene to be managed by the gui manager | |
| gui.Manager().Set(scene) | |
| // Create perspective camera | |
| cam := camera.New(1) | |
| cam.SetPosition(0, 0, 3) | |
| scene.Add(cam) | |
| // Set up orbit control for the camera | |
| camera.NewOrbitControl(cam) | |
| // Set up callback to update viewport and camera aspect ratio when the window is resized | |
| onResize := func(evname string, ev interface{}) { | |
| // Get framebuffer size and update viewport accordingly | |
| width, height := a.GetFramebufferSize() | |
| a.Gls().Viewport(0, 0, int32(width), int32(height)) | |
| // Update the camera's aspect ratio | |
| cam.SetAspect(float32(width) / float32(height)) | |
| } | |
| a.Subscribe(window.OnWindowSize, onResize) | |
| onResize("", nil) | |
| a.Renderer().EnblaeFBOPanel() | |
| fbWidth := int32(200) | |
| fbHeight := int32(350) | |
| wscale, hscale := a.GetScale() | |
| fboRenderer, err := renderer.NewFBORenderer(a.Gls(), fbWidth*int32(wscale), fbHeight*int32(hscale), | |
| float32(fbWidth), float32(fbHeight), gls.COLOR_ATTACHMENT0) | |
| if err != nil { | |
| panic(err) | |
| } | |
| // Create perspective camera | |
| fboRenderer.Camera().SetAspect(float32(fbWidth) / float32(fbHeight)) | |
| fboRenderer.Camera().SetProjection(camera.Perspective) | |
| fboRenderer.Camera().SetPosition(0, 0, 10) | |
| // setup ambient light to scene | |
| fboRenderer.Light().SetColor(&math32.Color{1.0, 1.0, 1.0}) | |
| fboRenderer.Light().SetIntensity(1.2) | |
| pointLight := light.NewPoint(&math32.Color{1, 1, 1}, 5.0) | |
| pointLight.SetPosition(1, 0, 2) | |
| fboRenderer.Scene().Add(pointLight) | |
| fboPanel := fboRenderer.Panel() | |
| fboPanel.SetPosition(30, 30) | |
| fboPanel.SetBorders(2, 2, 2, 2) | |
| fboPanel.SetBordersColor(&math32.Color{1, 1, 1}) | |
| scene.Add(fboPanel) | |
| // Create a blue torus and add it to the scene | |
| geom := geometry.NewTorus(1, .4, 12, 32, math32.Pi*2) | |
| mat := material.NewStandard(math32.NewColor("DarkBlue")) | |
| mesh := graphic.NewMesh(geom, mat) | |
| mesh.RotateX(math32.Pi / 4) | |
| mesh.RotateY(math32.Pi / 4) | |
| fboRenderer.Scene().Add(mesh) | |
| // Create and add a button to the scene | |
| btn := gui.NewButton("Make Red") | |
| btn.SetZLayerDelta(10) | |
| btn.SetPosition(10, 10) | |
| btn.SetSize(40, 40) | |
| btn.Subscribe(gui.OnClick, func(name string, ev interface{}) { | |
| mat.SetColor(math32.NewColor("DarkRed")) | |
| }) | |
| scene.Add(btn) | |
| // Create and add lights to the scene | |
| scene.Add(light.NewAmbient(&math32.Color{1.0, 1.0, 1.0}, 0.8)) | |
| // Create and add an axis helper to the scene | |
| scene.Add(helper.NewAxes(0.5)) | |
| // Set background color to gray | |
| a.Gls().ClearColor(0.5, 0.5, 0.5, 1.0) | |
| // Run the application | |
| a.Run(func(renderer *renderer.Renderer, deltaTime time.Duration) { | |
| a.Gls().Clear(gls.DEPTH_BUFFER_BIT | gls.STENCIL_BUFFER_BIT | gls.COLOR_BUFFER_BIT) | |
| fboRenderer.PreRender() | |
| // Update object in fbo scene | |
| fboRenderer.Render(fboRenderer.Scene(), fboRenderer.Camera()) | |
| renderer.Render(scene, cam) | |
| }) | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // texture/texture2D.go | |
| // Copyright 2016 The G3N Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // Package texture contains several types of textures which can be added to materials. | |
| package texture | |
| import ( | |
| "fmt" | |
| "image" | |
| "image/draw" | |
| _ "image/gif" | |
| _ "image/jpeg" | |
| _ "image/png" | |
| "os" | |
| "github.com/g3n/engine/util/logger" | |
| "github.com/g3n/engine/gls" | |
| ) | |
| // Package logger | |
| var log = logger.New("TEX", logger.Default) | |
| // Texture2D represents a texture | |
| type Texture2D struct { | |
| gs *gls.GLS // Pointer to OpenGL state | |
| refcount int // Current number of references | |
| texname uint32 // Texture handle | |
| magFilter uint32 // magnification filter | |
| minFilter uint32 // minification filter | |
| wrapS uint32 // wrap mode for s coordinate | |
| wrapT uint32 // wrap mode for t coordinate | |
| iformat int32 // internal format | |
| width int32 // texture width in pixels | |
| height int32 // texture height in pixels | |
| format uint32 // format of the pixel data | |
| formatType uint32 // type of the pixel data | |
| updateData bool // texture data needs to be sent | |
| updateParams bool // texture parameters needs to be sent | |
| genMipmap bool // generate mipmaps flag | |
| gsGenMipmap bool // generate mipmaps flag | |
| compressed bool // whether the texture is compressed | |
| size int32 // the size of the texture data in bytes | |
| data interface{} // array with texture data | |
| uniUnit gls.Uniform // Texture unit uniform location cache | |
| uniInfo gls.Uniform // Texture info uniform location cache | |
| udata struct { // Combined uniform data in 3 vec2: | |
| offsetX float32 | |
| offsetY float32 | |
| repeatX float32 | |
| repeatY float32 | |
| flipY float32 | |
| visible float32 | |
| } | |
| } | |
| func newTexture2D() *Texture2D { | |
| t := new(Texture2D) | |
| t.gs = nil | |
| t.refcount = 1 | |
| t.texname = 0 | |
| t.magFilter = gls.LINEAR | |
| t.minFilter = gls.LINEAR_MIPMAP_LINEAR | |
| t.wrapS = gls.CLAMP_TO_EDGE | |
| t.wrapT = gls.CLAMP_TO_EDGE | |
| t.updateData = false | |
| t.updateParams = true | |
| t.genMipmap = true | |
| // Initialize Uniform elements | |
| t.uniUnit.Init("MatTexture") | |
| t.uniInfo.Init("MatTexinfo") | |
| t.SetOffset(0, 0) | |
| t.SetRepeat(1, 1) | |
| t.SetFlipY(true) | |
| t.SetVisible(true) | |
| return t | |
| } | |
| // NewTexture2DFromImage creates and returns a pointer to a new Texture2D | |
| // using the specified image file as data. | |
| // Supported image formats are: PNG, JPEG and GIF. | |
| func NewTexture2DFromImage(imgfile string) (*Texture2D, error) { | |
| // Decodes image file into RGBA8 | |
| rgba, err := DecodeImage(imgfile) | |
| if err != nil { | |
| return nil, err | |
| } | |
| t := newTexture2D() | |
| t.SetFromRGBA(rgba) | |
| return t, nil | |
| } | |
| // NewTexture2DFromRGBA creates a new texture from a pointer to an RGBA image object. | |
| func NewTexture2DFromRGBA(rgba *image.RGBA) *Texture2D { | |
| t := newTexture2D() | |
| t.SetFromRGBA(rgba) | |
| return t | |
| } | |
| // NewTexture2DFromData creates a new texture from data | |
| func NewTexture2DFromData(width, height int, format int, formatType, iformat int, data interface{}) *Texture2D { | |
| t := newTexture2D() | |
| t.SetData(width, height, format, formatType, iformat, data) | |
| return t | |
| } | |
| // NewTexture2DFromCompressedData creates a new compressed texture from data | |
| func NewTexture2DFromCompressedData(width, height int, iformat int32, size int32, data interface{}) *Texture2D { | |
| t := newTexture2D() | |
| t.SetCompressedData(width, height, iformat, size, data) | |
| return t | |
| } | |
| // NewTexture2DFromGsTexture | |
| func NewTexture2DFromGsTexture(gs *gls.GLS, texname uint32) *Texture2D { | |
| t := newTexture2D() | |
| t.gsGenMipmap = true | |
| t.gs = gs | |
| t.texname = texname | |
| return t | |
| } | |
| // Incref increments the reference count for this texture | |
| // and returns a pointer to the geometry. | |
| // It should be used when this texture is shared by another | |
| // material. | |
| func (t *Texture2D) Incref() *Texture2D { | |
| t.refcount++ | |
| return t | |
| } | |
| // Dispose decrements this texture reference count and | |
| // if necessary releases OpenGL resources and C memory | |
| // associated with this texture. | |
| func (t *Texture2D) Dispose() { | |
| if t.refcount > 1 { | |
| t.refcount-- | |
| return | |
| } | |
| if t.gs != nil { | |
| t.gs.DeleteTextures(t.texname) | |
| t.gs = nil | |
| } | |
| } | |
| // SetUniformNames sets the names of the uniforms in the shader for sampler and texture info. | |
| func (t *Texture2D) SetUniformNames(sampler, info string) { | |
| t.uniUnit.Init(sampler) | |
| t.uniInfo.Init(info) | |
| } | |
| // GetUniformNames returns the names of the uniforms in the shader for sampler and texture info. | |
| func (t *Texture2D) GetUniformNames() (sampler, info string) { | |
| return t.uniUnit.Name(), t.uniInfo.Name() | |
| } | |
| // SetImage sets a new image for this texture | |
| func (t *Texture2D) SetImage(imgfile string) error { | |
| // Decodes image file into RGBA8 | |
| rgba, err := DecodeImage(imgfile) | |
| if err != nil { | |
| return err | |
| } | |
| t.SetFromRGBA(rgba) | |
| return nil | |
| } | |
| // SetFromRGBA sets the texture data from the specified image.RGBA object | |
| func (t *Texture2D) SetFromRGBA(rgba *image.RGBA) { | |
| t.SetData( | |
| rgba.Rect.Size().X, | |
| rgba.Rect.Size().Y, | |
| gls.RGBA, | |
| gls.UNSIGNED_BYTE, | |
| gls.RGBA8, | |
| rgba.Pix, | |
| ) | |
| } | |
| // SetData sets the texture data | |
| func (t *Texture2D) SetData(width, height int, format int, formatType, iformat int, data interface{}) { | |
| t.width = int32(width) | |
| t.height = int32(height) | |
| t.format = uint32(format) | |
| t.formatType = uint32(formatType) | |
| t.iformat = int32(iformat) | |
| t.compressed = false | |
| t.data = data | |
| t.updateData = true | |
| } | |
| // SetCompressedData sets the compressed texture data | |
| func (t *Texture2D) SetCompressedData(width, height int, iformat int32, size int32, data interface{}) { | |
| t.width = int32(width) | |
| t.height = int32(height) | |
| t.iformat = iformat | |
| t.compressed = true | |
| t.size = size | |
| t.data = data | |
| t.updateData = true | |
| } | |
| // SetVisible sets the visibility state of the texture | |
| func (t *Texture2D) SetVisible(state bool) { | |
| if state { | |
| t.udata.visible = 1 | |
| } else { | |
| t.udata.visible = 0 | |
| } | |
| } | |
| // Visible returns the current visibility state of the texture | |
| func (t *Texture2D) Visible() bool { | |
| if t.udata.visible == 0 { | |
| return false | |
| } | |
| return true | |
| } | |
| // SetMagFilter sets the filter to be applied when the texture element | |
| // covers more than on pixel. The default value is gls.Linear. | |
| func (t *Texture2D) SetMagFilter(magFilter uint32) { | |
| t.magFilter = magFilter | |
| t.updateParams = true | |
| } | |
| // SetMinFilter sets the filter to be applied when the texture element | |
| // covers less than on pixel. The default value is gls.Linear. | |
| func (t *Texture2D) SetMinFilter(minFilter uint32) { | |
| t.minFilter = minFilter | |
| t.updateParams = true | |
| } | |
| // SetWrapS set the wrapping mode for texture S coordinate | |
| // The default value is GL_CLAMP_TO_EDGE; | |
| func (t *Texture2D) SetWrapS(wrapS uint32) { | |
| t.wrapS = wrapS | |
| t.updateParams = true | |
| } | |
| // SetWrapT set the wrapping mode for texture T coordinate | |
| // The default value is GL_CLAMP_TO_EDGE; | |
| func (t *Texture2D) SetWrapT(wrapT uint32) { | |
| t.wrapT = wrapT | |
| t.updateParams = true | |
| } | |
| // SetRepeat set the repeat factor | |
| func (t *Texture2D) SetRepeat(x, y float32) { | |
| t.udata.repeatX = x | |
| t.udata.repeatY = y | |
| } | |
| // Repeat returns the current X and Y repeat factors | |
| func (t *Texture2D) Repeat() (float32, float32) { | |
| return t.udata.repeatX, t.udata.repeatY | |
| } | |
| // SetOffset sets the offset factor | |
| func (t *Texture2D) SetOffset(x, y float32) { | |
| t.udata.offsetX = x | |
| t.udata.offsetY = y | |
| } | |
| // Offset returns the current X and Y offset factors | |
| func (t *Texture2D) Offset() (float32, float32) { | |
| return t.udata.offsetX, t.udata.offsetY | |
| } | |
| // SetFlipY set the state for flipping the Y coordinate | |
| func (t *Texture2D) SetFlipY(state bool) { | |
| if state { | |
| t.udata.flipY = 1 | |
| } else { | |
| t.udata.flipY = 0 | |
| } | |
| } | |
| // Width returns the texture width in pixels | |
| func (t *Texture2D) Width() int { | |
| return int(t.width) | |
| } | |
| // Height returns the texture height in pixels | |
| func (t *Texture2D) Height() int { | |
| return int(t.height) | |
| } | |
| // Compressed returns whether this texture is compressed | |
| func (t *Texture2D) Compressed() bool { | |
| return t.compressed | |
| } | |
| // DecodeImage reads and decodes the specified image file into RGBA8. | |
| // The supported image files are PNG, JPEG and GIF. | |
| func DecodeImage(imgfile string) (*image.RGBA, error) { | |
| // Open image file | |
| file, err := os.Open(imgfile) | |
| if err != nil { | |
| return nil, err | |
| } | |
| defer file.Close() | |
| // Decodes image | |
| img, _, err := image.Decode(file) | |
| if err != nil { | |
| return nil, err | |
| } | |
| // Converts image to RGBA format | |
| rgba := image.NewRGBA(img.Bounds()) | |
| if rgba.Stride != rgba.Rect.Size().X*4 { | |
| return nil, fmt.Errorf("unsupported stride") | |
| } | |
| draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src) | |
| return rgba, nil | |
| } | |
| // RenderSetup is called by the material render setup | |
| func (t *Texture2D) RenderSetup(gs *gls.GLS, slotIdx, uniIdx int) { // Could have as input - TEXTURE0 (slot) and uni location | |
| // One time initialization | |
| if t.gs == nil { | |
| t.texname = gs.GenTexture() | |
| t.gs = gs | |
| } | |
| // Sets the texture unit for this texture | |
| gs.ActiveTexture(uint32(gls.TEXTURE0 + slotIdx)) | |
| gs.BindTexture(gls.TEXTURE_2D, t.texname) | |
| // Transfer texture data to OpenGL if necessary | |
| if t.updateData { | |
| if t.compressed { | |
| gs.CompressedTexImage2D( | |
| gls.TEXTURE_2D, | |
| 0, | |
| uint32(t.iformat), | |
| t.width, | |
| t.height, | |
| t.size, | |
| t.data, | |
| ) | |
| } else { | |
| gs.TexImage2D( | |
| gls.TEXTURE_2D, // texture type | |
| 0, // level of detail | |
| t.iformat, // internal format | |
| t.width, // width in texels | |
| t.height, // height in texels | |
| t.format, // format of supplied texture data | |
| t.formatType, // type of external format color component | |
| t.data, // image data | |
| ) | |
| } | |
| // Generates mipmaps if requested | |
| if t.genMipmap { | |
| gs.GenerateMipmap(gls.TEXTURE_2D) | |
| } | |
| // No data to send | |
| t.updateData = false | |
| } | |
| if t.gsGenMipmap { | |
| gs.GenerateMipmap(gls.TEXTURE_2D) | |
| } | |
| // Sets texture parameters if needed | |
| if t.updateParams { | |
| gs.TexParameteri(gls.TEXTURE_2D, gls.TEXTURE_MAG_FILTER, int32(t.magFilter)) | |
| gs.TexParameteri(gls.TEXTURE_2D, gls.TEXTURE_MIN_FILTER, int32(t.minFilter)) | |
| gs.TexParameteri(gls.TEXTURE_2D, gls.TEXTURE_WRAP_S, int32(t.wrapS)) | |
| gs.TexParameteri(gls.TEXTURE_2D, gls.TEXTURE_WRAP_T, int32(t.wrapT)) | |
| t.updateParams = false | |
| } | |
| // Transfer texture unit uniform | |
| var location int32 | |
| if uniIdx == 0 { | |
| location = t.uniUnit.Location(gs) | |
| } else { | |
| location = t.uniUnit.LocationIdx(gs, int32(uniIdx)) | |
| } | |
| gs.Uniform1i(location, int32(slotIdx)) | |
| // Transfer texture info combined uniform | |
| const vec2count = 3 | |
| location = t.uniInfo.LocationIdx(gs, vec2count*int32(uniIdx)) | |
| gs.Uniform2fv(location, vec2count, &t.udata.offsetX) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment