Skip to content

Instantly share code, notes, and snippets.

@j100002ben
Created April 12, 2022 00:19
Show Gist options
  • Save j100002ben/9db1b2e9d667541239ea730bef4df5a9 to your computer and use it in GitHub Desktop.
Save j100002ben/9db1b2e9d667541239ea730bef4df5a9 to your computer and use it in GitHub Desktop.
g3n Framebuffer example
// 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")
}
// 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 n​buffer 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 n​textures 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 n​vertex 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
}
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)
})
}
// 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