Created
February 15, 2026 10:01
-
-
Save leonkasovan/1ac33df252767ea240f332a4d81b8690 to your computer and use it in GitHub Desktop.
Fix rendering on android
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
| // This is almost identical to render_gles.go except it uses a VAO | |
| // for GLES 3.2 which is the main version that runs on modern | |
| // Android (ARM). Work adapted from Leon Kasovan | |
| //go:build android | |
| package main | |
| import ( | |
| "bytes" | |
| "encoding/binary" | |
| "fmt" | |
| "math" | |
| "runtime" | |
| "strings" | |
| "sync" | |
| "unsafe" | |
| mgl "github.com/go-gl/mathgl/mgl32" | |
| gl "github.com/leonkasovan/gl/v3.2/gles2" | |
| "github.com/veandco/go-sdl2/sdl" | |
| "golang.org/x/mobile/exp/f32" | |
| ) | |
| // ------------------------------------------------------------------ | |
| // ShaderProgram_GLES32 | |
| type ShaderProgram_GLES32 struct { | |
| // Program | |
| program uint32 | |
| // Attributes | |
| a map[string]int32 | |
| // Uniforms | |
| u map[string]int32 | |
| // Texture_GLES32 units | |
| t map[string]int | |
| } | |
| var shaderCompileMutex sync.Mutex | |
| func (r *Renderer_GLES32) newShaderProgram(vert, frag, geo, id string, crashWhenFail bool) (s *ShaderProgram_GLES32, err error) { | |
| // LOCK THE THREAD HERE | |
| shaderCompileMutex.Lock() | |
| defer shaderCompileMutex.Unlock() | |
| runtime.LockOSThread() | |
| defer runtime.UnlockOSThread() | |
| Logcat("GLES: [LOCKED] Starting: " + id) | |
| var vertObj, fragObj, prog uint32 | |
| vertObj, err = r.compileShader(gl.VERTEX_SHADER, vert) | |
| if err != nil { | |
| return nil, err | |
| } | |
| Logcat("GLES: Vertex Obj created: " + id) | |
| fragObj, err = r.compileShader(gl.FRAGMENT_SHADER, frag) | |
| if err != nil { | |
| return nil, err | |
| } | |
| Logcat("GLES: Frag Obj created: " + id) | |
| // IMPORTANT: Geometry shaders are very unstable on GLES 3.2 mobile. | |
| // For now, let's force skip them to see if we can reach the main menu. | |
| if false && len(geo) > 0 { | |
| if geoObj, err := r.compileShader(gl.GEOMETRY_SHADER, geo); chkEX(err, "Shader compliation error on "+id+"\n", crashWhenFail) { | |
| return nil, err | |
| } else { | |
| if prog, err = r.linkProgram(vertObj, fragObj, geoObj); chkEX(err, "Link program error on "+id+"\n", crashWhenFail) { | |
| return nil, err | |
| } | |
| } | |
| } else { | |
| Logcat("GLES: Entering linkProgram...") | |
| prog, err = r.linkProgram(vertObj, fragObj) | |
| if err != nil { | |
| return nil, err | |
| } | |
| } | |
| Logcat("GLES: Program linked, creating struct...") | |
| s = &ShaderProgram_GLES32{program: prog} | |
| s.a = make(map[string]int32) | |
| s.u = make(map[string]int32) | |
| s.t = make(map[string]int) | |
| Logcat("GLES: Shader initialization complete for: " + id) | |
| return s, nil | |
| } | |
| func (r *ShaderProgram_GLES32) glStr(s string) *uint8 { | |
| return gl.Str(s + "\x00") | |
| } | |
| func (s *ShaderProgram_GLES32) RegisterAttributes(names ...string) { | |
| for _, name := range names { | |
| cstr := gl.Str(name + "\x00") | |
| loc := gl.GetAttribLocation(s.program, cstr) | |
| s.a[name] = loc | |
| Logcat(fmt.Sprintf("GLES: Attribute [%s] mapped to %d", name, loc)) | |
| } | |
| } | |
| func (s *ShaderProgram_GLES32) RegisterUniforms(names ...string) { | |
| for _, name := range names { | |
| cstr := gl.Str(name + "\x00") | |
| loc := gl.GetUniformLocation(s.program, cstr) | |
| s.u[name] = loc | |
| Logcat(fmt.Sprintf("GLES: Uniform [%s] mapped to %d", name, loc)) | |
| } | |
| } | |
| func (s *ShaderProgram_GLES32) RegisterTextures(names ...string) { | |
| for _, name := range names { | |
| cstr := gl.Str(name + "\x00") | |
| loc := gl.GetUniformLocation(s.program, cstr) | |
| s.u[name] = loc | |
| s.t[name] = len(s.t) | |
| Logcat(fmt.Sprintf("GLES: Texture [%s] mapped to %d", name, loc)) | |
| } | |
| } | |
| func (r *Renderer_GLES32) compileShader(shaderType uint32, src string) (uint32, error) { | |
| shader := gl.CreateShader(shaderType) | |
| // 1. SMART HEADER INJECTION | |
| // GLES 3.0+ drivers REQUIRE the version to be the very first line. | |
| // If your file doesn't have it, we add it here. | |
| fullSrc := src | |
| if !strings.HasPrefix(strings.TrimSpace(src), "#version") { | |
| // Anchor to 300 es for best mobile compatibility | |
| header := "#version 310 es\n" | |
| fullSrc = header + src | |
| } | |
| // Ensure null-termination for CGO | |
| fullSrc = fullSrc + "\x00" | |
| typeName := "VERTEX" | |
| if shaderType == gl.FRAGMENT_SHADER { | |
| typeName = "FRAGMENT" | |
| } | |
| Logcat(fmt.Sprintf("GLES: Compiling %s Shader...", typeName)) | |
| // Logcat("DEBUG SHADER SRC:\n" + fullSrc) // Keep for emergencies | |
| // 2. MEMORY PINNING | |
| csource, free := gl.Strs(fullSrc) | |
| defer free() | |
| gl.ShaderSource(shader, 1, csource, nil) | |
| gl.CompileShader(shader) | |
| // 3. STATUS CHECK | |
| var status int32 | |
| gl.GetShaderiv(shader, gl.COMPILE_STATUS, (*int32)(unsafe.Pointer(&status))) | |
| if status == 0 { | |
| var logLength int32 | |
| gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, (*int32)(unsafe.Pointer(&logLength))) | |
| if logLength > 0 { | |
| logBytes := make([]byte, logLength) | |
| gl.GetShaderInfoLog(shader, logLength, nil, (*uint8)(unsafe.Pointer(&logBytes[0]))) | |
| err := fmt.Errorf("GLES %s Shader Err: %s", typeName, string(logBytes)) | |
| Logcat("GLES Error: " + err.Error()) | |
| return 0, err | |
| } | |
| return 0, fmt.Errorf("GLES %s Shader Err: Unknown error", typeName) | |
| } | |
| Logcat(fmt.Sprintf("GLES: %s ready.", typeName)) | |
| return shader, nil | |
| } | |
| func (r *Renderer_GLES32) linkProgram(params ...uint32) (program uint32, err error) { | |
| program = gl.CreateProgram() | |
| for _, param := range params { | |
| gl.AttachShader(program, param) | |
| } | |
| // if len(params) > 2 { | |
| // // Geometry Shader Params | |
| // gl.ProgramParameteri(program, gl.GEOMETRY_INPUT_TYPE, gl.TRIANGLES) | |
| // gl.ProgramParameteri(program, gl.GEOMETRY_OUTPUT_TYPE, gl.TRIANGLE_STRIP) | |
| // gl.ProgramParameteri(program, gl.GEOMETRY_VERTICES_OUT, 3*6) | |
| // } | |
| Logcat("GLES: Linking program...") | |
| gl.LinkProgram(program) | |
| // Mark shaders for deletion when the program is deleted | |
| for _, param := range params { | |
| gl.DetachShader(program, param) | |
| gl.DeleteShader(param) | |
| } | |
| var ok int32 | |
| gl.GetProgramiv(program, gl.LINK_STATUS, &ok) | |
| if ok == 0 { | |
| var size, l int32 | |
| gl.GetProgramiv(program, gl.INFO_LOG_LENGTH, &size) | |
| if size > 0 { | |
| str := make([]byte, size+1) | |
| gl.GetProgramInfoLog(program, size, &l, &str[0]) | |
| err = fmt.Errorf("Link error: %s", string(str[:l])) | |
| } else { | |
| err = fmt.Errorf("Unknown link error") | |
| } | |
| Logcat("GLES: " + err.Error()) | |
| gl.DeleteProgram(program) | |
| return 0, err | |
| } | |
| Logcat("GLES: Link Successful!") | |
| return program, nil | |
| } | |
| // ------------------------------------------------------------------ | |
| // Texture_GLES32 | |
| type Texture_GLES32 struct { | |
| width int32 | |
| height int32 | |
| depth int32 | |
| filter bool | |
| handle uint32 | |
| } | |
| // Generate a new texture name | |
| func (r *Renderer_GLES32) newTexture(width, height, depth int32, filter bool) (t Texture) { | |
| var h uint32 | |
| gl.ActiveTexture(gl.TEXTURE0) | |
| gl.GenTextures(1, &h) | |
| t = &Texture_GLES32{width, height, depth, filter, h} | |
| runtime.SetFinalizer(t, func(t *Texture_GLES32) { | |
| sys.mainThreadTask <- func() { | |
| gl.DeleteTextures(1, &t.handle) | |
| } | |
| }) | |
| return | |
| } | |
| func (r *Renderer_GLES32) newPaletteTexture() Texture { | |
| return r.newTexture(256, 1, 32, false) | |
| } | |
| func (r *Renderer_GLES32) newModelTexture(width, height, depth int32, filter bool) Texture { | |
| return r.newTexture(width, height, depth, filter) | |
| } | |
| func (r *Renderer_GLES32) newDataTexture(width, height int32) (t Texture) { | |
| var h uint32 | |
| gl.ActiveTexture(gl.TEXTURE0) | |
| gl.GenTextures(1, &h) | |
| t = &Texture_GLES32{width, height, 32, false, h} | |
| runtime.SetFinalizer(t, func(t *Texture_GLES32) { | |
| sys.mainThreadTask <- func() { | |
| gl.DeleteTextures(1, &t.handle) | |
| } | |
| }) | |
| gl.BindTexture(gl.TEXTURE_2D, h) | |
| //gl.TexImage2D(gl.TEXTURE_2D, 0, 32, t.width, t.height, 0, 36, gl.FLOAT, nil) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) | |
| return | |
| } | |
| func (r *Renderer_GLES32) newHDRTexture(width, height int32) (t Texture) { | |
| var h uint32 | |
| gl.ActiveTexture(gl.TEXTURE0) | |
| gl.GenTextures(1, &h) | |
| t = &Texture_GLES32{width, height, 24, false, h} | |
| runtime.SetFinalizer(t, func(t *Texture_GLES32) { | |
| sys.mainThreadTask <- func() { | |
| gl.DeleteTextures(1, &t.handle) | |
| } | |
| }) | |
| gl.BindTexture(gl.TEXTURE_2D, h) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT) | |
| return | |
| } | |
| func (r *Renderer_GLES32) newCubeMapTexture(widthHeight int32, mipmap bool, lowestMipLevel int32) (t Texture) { | |
| var h uint32 | |
| gl.ActiveTexture(gl.TEXTURE0) | |
| gl.GenTextures(1, &h) | |
| t = &Texture_GLES32{widthHeight, widthHeight, 24, false, h} | |
| runtime.SetFinalizer(t, func(t *Texture_GLES32) { | |
| sys.mainThreadTask <- func() { | |
| gl.DeleteTextures(1, &t.handle) | |
| } | |
| }) | |
| gl.BindTexture(gl.TEXTURE_CUBE_MAP, h) | |
| for i := 0; i < 6; i++ { | |
| gl.TexImage2D(uint32(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i), 0, gl.RGB32F, widthHeight, widthHeight, 0, gl.RGB, gl.FLOAT, nil) | |
| } | |
| if mipmap { | |
| gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR) | |
| gl.GenerateMipmap(gl.TEXTURE_CUBE_MAP) | |
| } else { | |
| gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR) | |
| } | |
| gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR) | |
| gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) | |
| gl.TexParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) | |
| return | |
| } | |
| // Bind a texture and upload texel data to it | |
| func (t *Texture_GLES32) SetData(data []byte) { | |
| var interp int32 = gl.NEAREST | |
| if t.filter { | |
| interp = gl.LINEAR | |
| } | |
| // GLES 3.x requires a sized internal format (GL_R8, GL_RGB8, GL_RGBA8). | |
| // The upload (pixel-transfer) format uses unsized enums (GL_RED, GL_RGB, GL_RGBA). | |
| sizedFormat := t.MapSizedInternalFormat(Max(t.depth, 8)) | |
| uploadFormat := t.MapUploadFormat(Max(t.depth, 8)) | |
| // Drain stale errors | |
| for gl.GetError() != 0 { | |
| } | |
| gl.BindTexture(gl.TEXTURE_2D, t.handle) | |
| gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1) | |
| if data != nil { | |
| gl.TexImage2D(gl.TEXTURE_2D, 0, int32(sizedFormat), t.width, t.height, 0, uploadFormat, gl.UNSIGNED_BYTE, unsafe.Pointer(&data[0])) | |
| } else { | |
| gl.TexImage2D(gl.TEXTURE_2D, 0, int32(sizedFormat), t.width, t.height, 0, uploadFormat, gl.UNSIGNED_BYTE, nil) | |
| } | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, interp) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, interp) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) | |
| } | |
| func (t *Texture_GLES32) SetSubData(data []byte, x, y, width, height int32) { | |
| var interp int32 = gl.NEAREST | |
| if t.filter { | |
| interp = gl.LINEAR | |
| } | |
| uploadFormat := t.MapUploadFormat(Max(t.depth, 8)) | |
| gl.BindTexture(gl.TEXTURE_2D, t.handle) | |
| gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1) | |
| if data != nil { | |
| gl.TexSubImage2D(gl.TEXTURE_2D, 0, x, y, width, height, uploadFormat, gl.UNSIGNED_BYTE, unsafe.Pointer(&data[0])) | |
| } else { | |
| gl.TexSubImage2D(gl.TEXTURE_2D, 0, x, y, width, height, uploadFormat, gl.UNSIGNED_BYTE, nil) | |
| } | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, interp) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, interp) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) | |
| // gl.PixelStorei(gl.UNPACK_ROW_LENGTH, 0) | |
| } | |
| func (t *Texture_GLES32) SetSubDataStride(data []byte, x, y, width, height, stride int32) { | |
| var interp int32 = gl.NEAREST | |
| if t.filter { | |
| interp = gl.LINEAR | |
| } | |
| // Drain any stale GL errors so the check below only reports errors from this call. | |
| for gl.GetError() != 0 { | |
| } | |
| // Use the same upload format that SetData / SetSubData use for this texture depth. | |
| uploadFormat := t.MapUploadFormat(Max(t.depth, 8)) | |
| // Bytes per pixel for the upload format. | |
| var bpp int32 = 4 | |
| switch uploadFormat { | |
| case gl.RED: | |
| bpp = 1 | |
| case gl.RGB: | |
| bpp = 3 | |
| } | |
| gl.BindTexture(gl.TEXTURE_2D, t.handle) | |
| gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1) | |
| // Only set UNPACK_ROW_LENGTH when the source stride differs from | |
| // width * bpp; leaving it at 0 (the default) is safer on mobile | |
| // drivers that have buggy UNPACK_ROW_LENGTH support. | |
| useRowLength := stride != width*bpp | |
| if useRowLength { | |
| gl.PixelStorei(gl.UNPACK_ROW_LENGTH, stride/bpp) | |
| } | |
| if data != nil { | |
| gl.TexSubImage2D(gl.TEXTURE_2D, 0, x, y, width, height, uploadFormat, gl.UNSIGNED_BYTE, unsafe.Pointer(&data[0])) | |
| } else { | |
| gl.TexSubImage2D(gl.TEXTURE_2D, 0, x, y, width, height, uploadFormat, gl.UNSIGNED_BYTE, nil) | |
| } | |
| if err := gl.GetError(); err != 0 { | |
| Logcat(fmt.Sprintf("GL ERROR in SetSubDataStride: %v | x:%d y:%d w:%d h:%d s:%d bpp:%d tex:%dx%d d:%d", | |
| err, x, y, width, height, stride, bpp, t.width, t.height, t.depth)) | |
| } | |
| if useRowLength { | |
| gl.PixelStorei(gl.UNPACK_ROW_LENGTH, 0) | |
| } | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, interp) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, interp) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) | |
| } | |
| func (t *Texture_GLES32) SetDataG(data []byte, mag, min, ws, wt TextureSamplingParam) { | |
| sizedFormat := t.MapSizedInternalFormat(Max(t.depth, 8)) | |
| uploadFormat := t.MapUploadFormat(Max(t.depth, 8)) | |
| gl.BindTexture(gl.TEXTURE_2D, t.handle) | |
| gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1) | |
| gl.TexImage2D(gl.TEXTURE_2D, 0, int32(sizedFormat), t.width, t.height, 0, uploadFormat, gl.UNSIGNED_BYTE, unsafe.Pointer(&data[0])) | |
| gl.GenerateMipmap(gl.TEXTURE_2D) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, int32(mag)) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, int32(min)) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, int32(ws)) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, int32(wt)) | |
| } | |
| func (t *Texture_GLES32) SetPixelData(data []float32) { | |
| gl.BindTexture(gl.TEXTURE_2D, t.handle) | |
| gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1) | |
| gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, t.width, t.height, 0, gl.RGBA, gl.FLOAT, unsafe.Pointer(&data[0])) | |
| } | |
| func (t Texture_GLES32) CopyData(src *Texture) { | |
| gl.BindTexture(gl.TEXTURE_2D, 0) // Unbind whatever is currently bound | |
| srcES := (*src).(*Texture_GLES32) | |
| var fbo uint32 | |
| gl.GenFramebuffers(1, &fbo) | |
| gl.BindFramebuffer(gl.READ_FRAMEBUFFER, fbo) | |
| gl.FramebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, srcES.handle, 0) | |
| gl.BindTexture(gl.TEXTURE_2D, t.handle) | |
| // Copy the old texture data into the top-left of the new, larger texture | |
| gl.CopyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, srcES.width, srcES.height) | |
| gl.BindFramebuffer(gl.READ_FRAMEBUFFER, 0) | |
| gl.DeleteFramebuffers(1, &fbo) | |
| } | |
| func (t *Texture_GLES32) SetRGBPixelData(data []float32) { | |
| gl.BindTexture(gl.TEXTURE_2D, t.handle) | |
| gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1) | |
| gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB32F, t.width, t.height, 0, gl.RGB, gl.FLOAT, unsafe.Pointer(&data[0])) | |
| } | |
| // Return whether texture has a valid handle | |
| func (t *Texture_GLES32) IsValid() bool { | |
| return t.width != 0 && t.height != 0 && t.handle != 0 | |
| } | |
| func (t *Texture_GLES32) GetWidth() int32 { | |
| return t.width | |
| } | |
| func (t *Texture_GLES32) GetHeight() int32 { | |
| return t.height | |
| } | |
| func (t *Texture_GLES32) MapUploadFormat(i int32) uint32 { | |
| var lut = map[int32]uint32{ | |
| 8: gl.RED, | |
| 24: gl.RGB, | |
| 32: gl.RGBA, | |
| } | |
| return lut[i] | |
| } | |
| // MapSizedInternalFormat returns the sized internal format required by GLES 3.x | |
| // glTexImage2D. Unsized formats (GL_RED, GL_RGB, GL_RGBA) are not valid as | |
| // internalformat on GLES and produce GL_INVALID_OPERATION (0x502). | |
| func (t *Texture_GLES32) MapSizedInternalFormat(i int32) uint32 { | |
| var lut = map[int32]uint32{ | |
| 8: gl.R8, | |
| 24: gl.RGB8, | |
| 32: gl.RGBA8, | |
| } | |
| return lut[i] | |
| } | |
| // ------------------------------------------------------------------ | |
| // Renderer_GLES32 | |
| type Renderer_GLES32 struct { | |
| fbo uint32 | |
| fbo_texture uint32 | |
| // Normal rendering | |
| rbo_depth uint32 | |
| // MSAA rendering | |
| fbo_f uint32 | |
| fbo_f_texture *Texture_GLES32 | |
| // Shadow Map | |
| fbo_shadow uint32 | |
| fbo_shadow_cube_texture uint32 | |
| fbo_env uint32 | |
| // Postprocessing FBOs | |
| fbo_pp []uint32 | |
| fbo_pp_texture []uint32 | |
| // Post-processing shaders | |
| postVertBuffer uint32 | |
| postShaderSelect []*ShaderProgram_GLES32 | |
| // Shader and vertex data for primitive rendering | |
| spriteShader *ShaderProgram_GLES32 | |
| vertexBuffer uint32 | |
| // Shader and index data for 3D model rendering | |
| shadowMapShader *ShaderProgram_GLES32 | |
| modelShader *ShaderProgram_GLES32 | |
| panoramaToCubeMapShader *ShaderProgram_GLES32 | |
| cubemapFilteringShader *ShaderProgram_GLES32 | |
| modelVertexBuffer [2]uint32 | |
| modelIndexBuffer [2]uint32 | |
| vao uint32 | |
| enableModel bool | |
| enableShadow bool | |
| GLES32State | |
| } | |
| type GLES32State struct { | |
| program uint32 | |
| depthTest bool | |
| depthMask bool | |
| invertFrontFace bool | |
| doubleSided bool | |
| blendEnabled bool | |
| blendEquation BlendEquation | |
| blendSrc BlendFunc | |
| blendDst BlendFunc | |
| scissorRect [4]int32 | |
| scissorEnabled bool | |
| lastSpriteTexture [8]uint32 | |
| useNormal bool | |
| useTangent bool | |
| useVertColor bool | |
| useJoint0 bool | |
| useJoint1 bool | |
| useOutlineAttribute bool | |
| //useUV bool // Safer not to cache this one because sprites also use it | |
| } | |
| func (r *Renderer_GLES32) GetName() string { | |
| return "OpenGL ES 3.2" | |
| } | |
| // init 3D model shader | |
| func (r *Renderer_GLES32) InitModelShader() error { | |
| var err error | |
| if r.enableShadow { | |
| r.modelShader, err = r.newShaderProgram(modelVertShader, "#define ENABLE_SHADOW\n"+modelFragShader, "", "Model Shader", false) | |
| } else { | |
| r.modelShader, err = r.newShaderProgram(modelVertShader, modelFragShader, "", "Model Shader", false) | |
| } | |
| if err != nil { | |
| return err | |
| } | |
| r.modelShader.RegisterAttributes( | |
| "position", // Same position as spriteShader | |
| "uv", // Same position as spriteShader | |
| "inVertexId", | |
| "normalIn", | |
| "tangentIn", | |
| "vertColor", | |
| "joints_0", | |
| "joints_1", | |
| "weights_0", | |
| "weights_1", | |
| "outlineAttributeIn", // Not in shadowMapShader | |
| ) | |
| r.modelShader.RegisterUniforms("model", "view", "projection", "normalMatrix", "unlit", "baseColorFactor", "add", "mult", "useTexture", "useNormalMap", "useMetallicRoughnessMap", "useEmissionMap", "neg", "gray", "hue", | |
| "enableAlpha", "alphaThreshold", "numJoints", "morphTargetWeight", "morphTargetOffset", "morphTargetTextureDimension", "numTargets", "numVertices", | |
| "metallicRoughness", "ambientOcclusionStrength", "emission", "environmentIntensity", "mipCount", "meshOutline", | |
| "cameraPosition", "environmentRotation", "texTransform", "normalMapTransform", "metallicRoughnessMapTransform", "ambientOcclusionMapTransform", "emissionMapTransform", | |
| "lightMatrices[0]", "lightMatrices[1]", "lightMatrices[2]", "lightMatrices[3]", | |
| "lights[0].direction", "lights[0].range", "lights[0].color", "lights[0].intensity", "lights[0].position", "lights[0].innerConeCos", "lights[0].outerConeCos", "lights[0].type", "lights[0].shadowBias", "lights[0].shadowMapFar", | |
| "lights[1].direction", "lights[1].range", "lights[1].color", "lights[1].intensity", "lights[1].position", "lights[1].innerConeCos", "lights[1].outerConeCos", "lights[1].type", "lights[1].shadowBias", "lights[1].shadowMapFar", | |
| "lights[2].direction", "lights[2].range", "lights[2].color", "lights[2].intensity", "lights[2].position", "lights[2].innerConeCos", "lights[2].outerConeCos", "lights[2].type", "lights[2].shadowBias", "lights[2].shadowMapFar", | |
| "lights[3].direction", "lights[3].range", "lights[3].color", "lights[3].intensity", "lights[3].position", "lights[3].innerConeCos", "lights[3].outerConeCos", "lights[3].type", "lights[3].shadowBias", "lights[3].shadowMapFar", | |
| ) | |
| r.modelShader.RegisterTextures("tex", "morphTargetValues", "jointMatrices", "normalMap", "metallicRoughnessMap", "ambientOcclusionMap", "emissionMap", "lambertianEnvSampler", "GGXEnvSampler", "GGXLUT", | |
| "shadowCubeMap") | |
| if r.enableShadow { | |
| r.shadowMapShader, err = r.newShaderProgram(shadowVertShader, shadowFragShader, shadowGeoShader, "Shadow Map Shader", false) | |
| if err != nil { | |
| return err | |
| } | |
| r.shadowMapShader.RegisterAttributes( | |
| "position", // Same position as spriteShader | |
| "uv", // Same position as spriteShader | |
| "inVertexId", | |
| "normalIn", | |
| "tangentIn", | |
| "vertColor", | |
| "joints_0", | |
| "joints_1", | |
| "weights_0", | |
| "weights_1", | |
| ) | |
| r.shadowMapShader.RegisterUniforms("model", "lightMatrices[0]", "lightMatrices[1]", "lightMatrices[2]", "lightMatrices[3]", "lightMatrices[4]", "lightMatrices[5]", | |
| "lightMatrices[6]", "lightMatrices[7]", "lightMatrices[8]", "lightMatrices[9]", "lightMatrices[10]", "lightMatrices[11]", | |
| "lightMatrices[12]", "lightMatrices[13]", "lightMatrices[14]", "lightMatrices[15]", "lightMatrices[16]", "lightMatrices[17]", | |
| "lightMatrices[18]", "lightMatrices[19]", "lightMatrices[20]", "lightMatrices[21]", "lightMatrices[22]", "lightMatrices[23]", | |
| "lights[0].type", "lights[1].type", "lights[2].type", "lights[3].type", "lights[0].position", "lights[1].position", "lights[2].position", "lights[3].position", | |
| "lights[0].shadowMapFar", "lights[1].shadowMapFar", "lights[2].shadowMapFar", "lights[3].shadowMapFar", "numJoints", "morphTargetWeight", "morphTargetOffset", "morphTargetTextureDimension", | |
| "numTargets", "numVertices", "enableAlpha", "alphaThreshold", "baseColorFactor", "useTexture", "texTransform", "layerOffset", "lightIndex") | |
| r.shadowMapShader.RegisterTextures("morphTargetValues", "jointMatrices", "tex") | |
| } | |
| r.panoramaToCubeMapShader, err = r.newShaderProgram(identVertShader, panoramaToCubeMapFragShader, "", "Panorama To Cubemap Shader", false) | |
| if err != nil { | |
| return err | |
| } | |
| r.panoramaToCubeMapShader.RegisterAttributes("VertCoord") | |
| r.panoramaToCubeMapShader.RegisterUniforms("currentFace") | |
| r.panoramaToCubeMapShader.RegisterTextures("panorama") | |
| r.cubemapFilteringShader, err = r.newShaderProgram(identVertShader, cubemapFilteringFragShader, "", "Cubemap Filtering Shader", false) | |
| if err != nil { | |
| return err | |
| } | |
| r.cubemapFilteringShader.RegisterAttributes("VertCoord") | |
| r.cubemapFilteringShader.RegisterUniforms("sampleCount", "distribution", "width", "currentFace", "roughness", "intensityScale", "isLUT") | |
| r.cubemapFilteringShader.RegisterTextures("cubeMap") | |
| return nil | |
| } | |
| // Render initialization. | |
| // Creates the default shaders, the framebuffer and enables MSAA. | |
| func (r *Renderer_GLES32) Init() { | |
| chk(gl.Init(func(name string) unsafe.Pointer { | |
| return eglGetProcAddress(name) | |
| })) | |
| if runtime.GOOS != "android" { | |
| sys.errLog.Printf("Using OpenGL %v (%v)", gl.GetString(gl.VERSION), gl.GetString(gl.RENDERER)) | |
| } else { | |
| Logcat(fmt.Sprintf("Using OpenGL %s (%s)", gl.GoStr(gl.GetString(gl.VERSION)), gl.GoStr(gl.GetString(gl.RENDERER)))) | |
| } | |
| // Logcat("GLES: Querying Max Samples") | |
| // var maxSamples int32 | |
| // gl.GetIntegerv(gl.MAX_SAMPLES, &maxSamples) | |
| // if sys.msaa > maxSamples { | |
| // sys.cfg.SetValueUpdate("Video.MSAA", maxSamples) | |
| // sys.msaa = maxSamples | |
| // } | |
| sys.msaa = 0 | |
| Logcat("GLES: Past MSAA check") | |
| r.postShaderSelect = make([]*ShaderProgram_GLES32, 1+len(sys.cfg.Video.ExternalShaders)) | |
| // Data buffers for rendering | |
| postVertData := f32.Bytes(binary.LittleEndian, -1, -1, 1, -1, -1, 1, 1, 1) | |
| r.enableModel = sys.cfg.Video.EnableModel | |
| r.enableShadow = sys.cfg.Video.EnableModelShadow | |
| Logcat("GLES: About to Gen VAO") | |
| gl.GenVertexArrays(1, &r.vao) | |
| gl.BindVertexArray(r.vao) | |
| Logcat("GLES: VAO Bound") | |
| gl.GenBuffers(1, &r.postVertBuffer) | |
| Logcat("GLES: PostVertBuffer Generated") | |
| gl.BindBuffer(gl.ARRAY_BUFFER, r.postVertBuffer) | |
| Logcat(fmt.Sprintf("GLES: Data Size: %d", len(postVertData))) | |
| if len(postVertData) > 0 { | |
| gl.BufferData(gl.ARRAY_BUFFER, len(postVertData), unsafe.Pointer(&postVertData[0]), gl.STATIC_DRAW) | |
| Logcat("GLES: PostVertBuffer Data Uploaded") | |
| } else { | |
| Logcat("GLES: ERROR - postVertData is empty!") | |
| } | |
| gl.GenBuffers(1, &r.vertexBuffer) | |
| Logcat("GLES: VertexBuffer Generated") | |
| gl.GenBuffers(1, &r.modelVertexBuffer[0]) | |
| gl.GenBuffers(1, &r.modelVertexBuffer[1]) | |
| Logcat("GLES: ModelVertexBuffers Generated") | |
| gl.GenBuffers(1, &r.modelIndexBuffer[0]) | |
| gl.GenBuffers(1, &r.modelIndexBuffer[1]) | |
| Logcat("GLES: ModelIndexBuffers Generated") | |
| // Sprite shader | |
| r.spriteShader, _ = r.newShaderProgram(vertShader, fragShader, "", "Main Shader", true) | |
| r.spriteShader.RegisterAttributes("position", "uv") | |
| r.spriteShader.RegisterUniforms("modelview", "projection", "x1x2x4x3", | |
| "alpha", "tint", "mask", "neg", "gray", "add", "mult", "isFlat", "isRgba", "isTrapez", "hue") | |
| r.spriteShader.RegisterTextures("pal", "tex") | |
| if r.enableModel { | |
| if err := r.InitModelShader(); err != nil { | |
| r.enableModel = false | |
| } | |
| } | |
| // Compile postprocessing shaders | |
| // Calculate total amount of shaders loaded. | |
| r.postShaderSelect = make([]*ShaderProgram_GLES32, 1+len(sys.cfg.Video.ExternalShaders)) | |
| // External Shaders | |
| for i := 0; i < len(sys.cfg.Video.ExternalShaders); i++ { | |
| r.postShaderSelect[i], _ = r.newShaderProgram(string(sys.externalShaders[0][i])+"\x00", | |
| string(sys.externalShaders[1][i])+"\x00", "", fmt.Sprintf("Postprocess Shader #%v", i), true) | |
| r.postShaderSelect[i].RegisterAttributes("VertCoord", "TexCoord") | |
| loc := r.postShaderSelect[i].a["TexCoord"] | |
| gl.VertexAttribPointer(uint32(loc), 3, gl.FLOAT, false, 5*4, gl.PtrOffset(2*4)) | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| r.postShaderSelect[i].RegisterUniforms("Texture", "TextureSize", "CurrentTime") | |
| } | |
| // Ident shader (no postprocessing). This is the last one | |
| identShader, _ := r.newShaderProgram(identVertShader, identFragShader, "", "Identity Postprocess", true) | |
| identShader.RegisterAttributes("VertCoord", "TexCoord") | |
| identShader.RegisterUniforms("Texture", "TextureSize", "CurrentTime") | |
| r.postShaderSelect[len(r.postShaderSelect)-1] = identShader | |
| gl.ActiveTexture(gl.TEXTURE0) | |
| // create a texture for r.fbo | |
| gl.GenTextures(1, &r.fbo_texture) | |
| gl.BindTexture(gl.TEXTURE_2D, r.fbo_texture) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) | |
| // Don't change this from gl.RGBA. | |
| // It breaks mixing between subtractive and additive. | |
| Logcat("GLES: Creating RGBA Textures") | |
| gl.TexImage2D( | |
| gl.TEXTURE_2D, | |
| 0, | |
| gl.RGBA, | |
| sys.scrrect[2], | |
| sys.scrrect[3], | |
| 0, | |
| gl.RGBA, | |
| gl.UNSIGNED_BYTE, | |
| nil, | |
| ) | |
| r.fbo_pp = make([]uint32, 2) | |
| r.fbo_pp_texture = make([]uint32, 2) | |
| // Shaders might use negative values, so we need a format that supports them. | |
| // RGBA8_SNORM is NOT color-renderable on GLES 3.x, which makes the FBO | |
| // incomplete and breaks multi-pass postprocessing. RGBA16F is both | |
| // color-renderable and supports negative values. | |
| // r.fbo_pp_texture | |
| for i := 0; i < 2; i++ { | |
| gl.GenTextures(1, &(r.fbo_pp_texture[i])) | |
| gl.BindTexture(gl.TEXTURE_2D, r.fbo_pp_texture[i]) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) | |
| gl.TexImage2D( | |
| gl.TEXTURE_2D, | |
| 0, | |
| gl.RGBA16F, | |
| sys.scrrect[2], | |
| sys.scrrect[3], | |
| 0, | |
| gl.RGBA, | |
| gl.FLOAT, | |
| nil, | |
| ) | |
| } | |
| // done with r.fbo_texture, unbind it | |
| gl.BindTexture(gl.TEXTURE_2D, 0) | |
| //r.rbo_depth = gl.CreateRenderbuffer() | |
| gl.GenRenderbuffers(1, &r.rbo_depth) | |
| gl.BindRenderbuffer(gl.RENDERBUFFER, r.rbo_depth) | |
| if sys.msaa > 0 { | |
| //gl.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, int(sys.scrrect[2]), int(sys.scrrect[3])) | |
| gl.RenderbufferStorageMultisample(gl.RENDERBUFFER, sys.msaa, gl.DEPTH_COMPONENT16, sys.scrrect[2], sys.scrrect[3]) | |
| } else { | |
| gl.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, sys.scrrect[2], sys.scrrect[3]) | |
| } | |
| gl.BindRenderbuffer(gl.RENDERBUFFER, 0) | |
| if sys.msaa > 0 { | |
| r.fbo_f_texture = r.newTexture(sys.scrrect[2], sys.scrrect[3], 32, false).(*Texture_GLES32) | |
| r.fbo_f_texture.SetData(nil) | |
| } else { | |
| //r.rbo_depth = gl.CreateRenderbuffer() | |
| //gl.BindRenderbuffer(gl.RENDERBUFFER, r.rbo_depth) | |
| //gl.RenderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, int(sys.scrrect[2]), int(sys.scrrect[3])) | |
| //gl.BindRenderbuffer(gl.RENDERBUFFER, gl.NoRenderbuffer) | |
| } | |
| // create an FBO for our r.fbo, which is then for r.fbo_texture | |
| gl.GenFramebuffers(1, &r.fbo) | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, r.fbo) | |
| if sys.msaa > 0 { | |
| gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D_MULTISAMPLE, r.fbo_texture, 0) | |
| gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, r.rbo_depth) | |
| if status := gl.CheckFramebufferStatus(gl.FRAMEBUFFER); status != gl.FRAMEBUFFER_COMPLETE { | |
| sys.errLog.Printf("framebuffer create failed: 0x%x", status) | |
| fmt.Printf("framebuffer create failed: 0x%x \n", status) | |
| } | |
| gl.GenFramebuffers(1, &r.fbo_f) | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, r.fbo_f) | |
| gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, r.fbo_f_texture.handle, 0) | |
| } else { | |
| gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, r.fbo_texture, 0) | |
| gl.FramebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, r.rbo_depth) | |
| } | |
| // create our two FBOs for our postprocessing needs | |
| for i := 0; i < 2; i++ { | |
| gl.GenFramebuffers(1, &(r.fbo_pp[i])) | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, r.fbo_pp[i]) | |
| gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, r.fbo_pp_texture[i], 0) | |
| } | |
| // create an FBO for our model stuff | |
| if r.enableModel { | |
| if r.enableShadow { | |
| gl.GenFramebuffers(1, &r.fbo_shadow) | |
| gl.ActiveTexture(gl.TEXTURE0) | |
| gl.GenTextures(1, &r.fbo_shadow_cube_texture) | |
| gl.BindTexture(gl.TEXTURE_CUBE_MAP_ARRAY, r.fbo_shadow_cube_texture) | |
| gl.TexStorage3D(gl.TEXTURE_CUBE_MAP_ARRAY, 1, gl.DEPTH_COMPONENT24, 1024, 1024, 4*6) | |
| gl.TexParameteri(gl.TEXTURE_CUBE_MAP_ARRAY, gl.TEXTURE_MAG_FILTER, gl.NEAREST) | |
| gl.TexParameteri(gl.TEXTURE_CUBE_MAP_ARRAY, gl.TEXTURE_MIN_FILTER, gl.NEAREST) | |
| gl.TexParameteri(gl.TEXTURE_CUBE_MAP_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) | |
| gl.TexParameteri(gl.TEXTURE_CUBE_MAP_ARRAY, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, r.fbo_shadow) | |
| // Actually attach the texture to the FBO | |
| gl.FramebufferTexture(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, r.fbo_shadow_cube_texture, 0) | |
| // gl.DrawBuffer(gl.NONE) | |
| bufs := []uint32{gl.NONE} | |
| gl.DrawBuffers(1, &bufs[0]) | |
| gl.ReadBuffer(gl.NONE) | |
| if status := gl.CheckFramebufferStatus(gl.FRAMEBUFFER); status != gl.FRAMEBUFFER_COMPLETE { | |
| Logcat(fmt.Sprintf("[GLES32] framebuffer create failed: 0x%x", status)) | |
| sys.errLog.Printf("[GLES32] framebuffer create failed: 0x%x", status) | |
| } | |
| } | |
| gl.GenFramebuffers(1, &r.fbo_env) | |
| } | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, 0) | |
| } | |
| func (r *Renderer_GLES32) Close() { | |
| } | |
| func (r *Renderer_GLES32) IsModelEnabled() bool { | |
| return r.enableModel | |
| } | |
| func (r *Renderer_GLES32) IsShadowEnabled() bool { | |
| return r.enableShadow | |
| } | |
| func (r *Renderer_GLES32) BeginFrame(clearColor bool) { | |
| gl.BindVertexArray(r.vao) | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, r.fbo) | |
| gl.Viewport(0, 0, sys.scrrect[2], sys.scrrect[3]) | |
| if clearColor { | |
| gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) | |
| } else { | |
| gl.Clear(gl.DEPTH_BUFFER_BIT) | |
| } | |
| } | |
| func (r *Renderer_GLES32) EndFrame() { | |
| if len(r.fbo_pp) == 0 { | |
| return | |
| } | |
| // tell GL to use our vertex array object | |
| // this'll be where our quad is stored | |
| gl.BindVertexArray(r.vao) | |
| x, y, width, height := int32(0), int32(0), int32(sys.scrrect[2]), int32(sys.scrrect[3]) | |
| time := sdl.GetPerformanceCounter() // consistent time across all shaders | |
| if sys.msaa > 0 { | |
| gl.BindFramebuffer(gl.DRAW_FRAMEBUFFER, r.fbo_f) | |
| gl.BindFramebuffer(gl.READ_FRAMEBUFFER, r.fbo) | |
| gl.BlitFramebuffer(x, y, width, height, x, y, width, height, gl.COLOR_BUFFER_BIT, gl.LINEAR) | |
| } | |
| var scaleMode int32 // GL enum | |
| if sys.cfg.Video.WindowScaleMode { | |
| scaleMode = gl.LINEAR | |
| } else { | |
| scaleMode = gl.NEAREST | |
| } | |
| // set the viewport to the unscaled bounds for post-processing | |
| gl.Viewport(x, y, width, height) | |
| // clear both of our post-processing FBOs to make sure | |
| // nothing's there. the output is set later | |
| for i := 0; i < 2; i++ { | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, r.fbo_pp[i]) | |
| gl.Clear(gl.COLOR_BUFFER_BIT) | |
| } | |
| gl.ActiveTexture(gl.TEXTURE0) // later referred to by Texture_GL | |
| fbo_texture := r.fbo_texture | |
| if sys.msaa > 0 { | |
| fbo_texture = r.fbo_f_texture.handle | |
| } | |
| // disable blending | |
| r.SetBlending(false, 0, 0, 0) | |
| for i := 0; i < len(r.postShaderSelect); i++ { | |
| postShader := r.postShaderSelect[i] | |
| // this is here because it is undefined | |
| // behavior to write to the same FBO | |
| if i%2 == 0 { | |
| // ping! our first post-processing FBO is the output | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, r.fbo_pp[0]) | |
| if i == 0 { | |
| // first pass, use fbo_texture | |
| gl.BindTexture(gl.TEXTURE_2D, fbo_texture) | |
| } else { | |
| // not the first pass, use the second post-processing FBO | |
| gl.BindTexture(gl.TEXTURE_2D, r.fbo_pp_texture[1]) | |
| } | |
| } else { | |
| // pong! our second post-processing FBO is the output | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, r.fbo_pp[1]) | |
| // our first post-processing FBO is the input | |
| gl.BindTexture(gl.TEXTURE_2D, r.fbo_pp_texture[0]) | |
| } | |
| if i >= len(r.postShaderSelect)-1 { | |
| // this is the last shader, | |
| // so we ask GL to scale it and output it | |
| // to FB0, the default frame buffer that the user sees | |
| x, y, width, height := sys.window.GetScaledViewportSize() | |
| gl.Viewport(x, y, width, height) | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, 0) | |
| // clear FB0 just to make sure | |
| gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) | |
| } | |
| // tell GL we want to use our shader program | |
| r.UseProgram(postShader.program) | |
| // set post-processing parameters | |
| gl.Uniform1i(postShader.u["Texture"], 0) | |
| gl.Uniform2f(postShader.u["TextureSize"], float32(width), float32(height)) | |
| gl.Uniform1f(postShader.u["CurrentTime"], float32(time)) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, scaleMode) | |
| gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, scaleMode) | |
| // this actually draws the image to the FBO | |
| // by constructing a quad (2 tris) | |
| gl.BindBuffer(gl.ARRAY_BUFFER, r.postVertBuffer) | |
| // construct the UVs of the quad | |
| loc := postShader.a["VertCoord"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointer(uint32(loc), 2, gl.FLOAT, false, 0, nil) | |
| // construct the quad and draw it | |
| gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| } | |
| } | |
| func (r *Renderer_GLES32) Await() { | |
| gl.Finish() | |
| } | |
| func (r *Renderer_GLES32) MapBlendEquation(i BlendEquation) uint32 { | |
| var BlendEquationLUT = map[BlendEquation]uint32{ | |
| BlendAdd: gl.FUNC_ADD, | |
| BlendReverseSubtract: gl.FUNC_REVERSE_SUBTRACT, | |
| } | |
| return BlendEquationLUT[i] | |
| } | |
| func (r *Renderer_GLES32) MapBlendFunction(i BlendFunc) uint32 { | |
| var BlendFunctionLUT = map[BlendFunc]uint32{ | |
| BlendOne: gl.ONE, | |
| BlendZero: gl.ZERO, | |
| BlendSrcAlpha: gl.SRC_ALPHA, | |
| BlendOneMinusSrcAlpha: gl.ONE_MINUS_SRC_ALPHA, | |
| } | |
| return BlendFunctionLUT[i] | |
| } | |
| func (r *Renderer_GLES32) MapPrimitiveMode(i PrimitiveMode) uint32 { | |
| var PrimitiveModeLUT = map[PrimitiveMode]uint32{ | |
| LINES: gl.LINES, | |
| LINE_LOOP: gl.LINE_LOOP, | |
| LINE_STRIP: gl.LINE_STRIP, | |
| TRIANGLES: gl.TRIANGLES, | |
| TRIANGLE_STRIP: gl.TRIANGLE_STRIP, | |
| TRIANGLE_FAN: gl.TRIANGLE_FAN, | |
| } | |
| return PrimitiveModeLUT[i] | |
| } | |
| func (r *Renderer_GLES32) SetDepthTest(depthTest bool) { | |
| if depthTest != r.depthTest { | |
| r.depthTest = depthTest | |
| if depthTest { | |
| gl.Enable(gl.DEPTH_TEST) | |
| gl.DepthFunc(gl.LESS) | |
| } else { | |
| gl.Disable(gl.DEPTH_TEST) | |
| } | |
| } | |
| } | |
| func (r *Renderer_GLES32) SetDepthMask(depthMask bool) { | |
| if depthMask != r.depthMask { | |
| r.depthMask = depthMask | |
| gl.DepthMask(depthMask) | |
| } | |
| } | |
| func (r *Renderer_GLES32) SetFrontFace(invertFrontFace bool) { | |
| if invertFrontFace != r.invertFrontFace { | |
| r.invertFrontFace = invertFrontFace | |
| if invertFrontFace { | |
| gl.FrontFace(gl.CW) | |
| } else { | |
| gl.FrontFace(gl.CCW) | |
| } | |
| } | |
| } | |
| func (r *Renderer_GLES32) SetCullFace(doubleSided bool) { | |
| if doubleSided != r.doubleSided { | |
| r.doubleSided = doubleSided | |
| if !doubleSided { | |
| gl.Enable(gl.CULL_FACE) | |
| gl.CullFace(gl.BACK) | |
| } else { | |
| gl.Disable(gl.CULL_FACE) | |
| } | |
| } | |
| } | |
| func (r *Renderer_GLES32) UseProgram(program uint32) { | |
| if r.program != program { | |
| gl.UseProgram(program) | |
| r.program = program | |
| // Clear cache between shaders | |
| for i := range r.lastSpriteTexture { | |
| r.lastSpriteTexture[i] = 0 | |
| } | |
| } | |
| } | |
| func (r *Renderer_GLES32) SetBlending(enable bool, eq BlendEquation, src, dst BlendFunc) { | |
| if enable != r.blendEnabled { | |
| if enable { | |
| r.blendEnabled = true | |
| gl.Enable(gl.BLEND) | |
| } else { | |
| r.blendEnabled = false | |
| gl.Disable(gl.BLEND) | |
| } | |
| } | |
| if enable { | |
| if eq != r.blendEquation { | |
| r.blendEquation = eq | |
| gl.BlendEquation(r.MapBlendEquation(eq)) | |
| } | |
| if src != r.blendSrc || dst != r.blendDst { | |
| r.blendSrc = src | |
| r.blendDst = dst | |
| gl.BlendFunc(r.MapBlendFunction(src), r.MapBlendFunction(dst)) | |
| } | |
| } | |
| } | |
| func (r *Renderer_GLES32) SetPipeline(eq BlendEquation, src, dst BlendFunc) { | |
| gl.BindVertexArray(r.vao) | |
| r.UseProgram(r.spriteShader.program) | |
| r.SetBlending(true, eq, src, dst) | |
| // Must bind buffer before enabling attributes | |
| gl.BindBuffer(gl.ARRAY_BUFFER, r.vertexBuffer) | |
| loc := r.spriteShader.a["position"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 2, gl.FLOAT, false, 16, 0) | |
| loc = r.spriteShader.a["uv"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 2, gl.FLOAT, false, 16, 8) | |
| } | |
| func (r *Renderer_GLES32) prepareShadowMapPipeline(bufferIndex uint32) { | |
| r.UseProgram(r.shadowMapShader.program) | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, r.fbo_shadow) | |
| gl.Viewport(0, 0, 1024, 1024) | |
| gl.Enable(gl.TEXTURE_2D) | |
| gl.Disable(gl.BLEND) | |
| gl.Enable(gl.DEPTH_TEST) | |
| gl.DepthFunc(gl.LESS) | |
| gl.DepthMask(true) | |
| gl.BlendEquation(gl.FUNC_ADD) | |
| gl.BlendFunc(gl.ONE, gl.ZERO) | |
| if r.invertFrontFace { | |
| gl.FrontFace(gl.CW) | |
| } else { | |
| gl.FrontFace(gl.CCW) | |
| } | |
| if !r.doubleSided { | |
| gl.Enable(gl.CULL_FACE) | |
| gl.CullFace(gl.BACK) | |
| } else { | |
| gl.Disable(gl.CULL_FACE) | |
| } | |
| r.depthTest = true | |
| r.depthMask = true | |
| r.blendEquation = BlendAdd | |
| r.blendSrc = BlendOne | |
| r.blendDst = BlendZero | |
| gl.BindBuffer(gl.ARRAY_BUFFER, r.modelVertexBuffer[bufferIndex]) | |
| gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, r.modelIndexBuffer[bufferIndex]) | |
| gl.FramebufferTexture(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, r.fbo_shadow_cube_texture, 0) | |
| gl.Clear(gl.DEPTH_BUFFER_BIT) | |
| } | |
| func (r *Renderer_GLES32) setShadowMapPipeline(doubleSided, invertFrontFace, useUV, useNormal, useTangent, useVertColor, useJoint0, useJoint1 bool, numVertices, vertAttrOffset uint32) { | |
| r.SetFrontFace(invertFrontFace) | |
| r.SetCullFace(doubleSided) | |
| loc := r.shadowMapShader.a["inVertexId"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 1, gl.INT, false, 0, uintptr(vertAttrOffset)) | |
| offset := vertAttrOffset + 4*numVertices | |
| loc = r.shadowMapShader.a["position"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 3, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 12 * numVertices | |
| loc = r.shadowMapShader.a["uv"] | |
| if useUV { | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 2, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 8 * numVertices | |
| } else { | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib2f(uint32(loc), 0, 0) | |
| } | |
| if useNormal { | |
| offset += 12 * numVertices | |
| } | |
| if useTangent { | |
| offset += 16 * numVertices | |
| } | |
| if useVertColor { | |
| loc = r.shadowMapShader.a["vertColor"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 4, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 16 * numVertices | |
| } else { | |
| loc = r.shadowMapShader.a["vertColor"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 1, 1, 1, 1) | |
| } | |
| if useJoint0 { | |
| r.useJoint0 = true | |
| loc = r.shadowMapShader.a["joints_0"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 4, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 16 * numVertices | |
| loc = r.shadowMapShader.a["weights_0"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 4, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 16 * numVertices | |
| if useJoint1 { | |
| r.useJoint1 = true | |
| loc = r.shadowMapShader.a["joints_1"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 4, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 16 * numVertices | |
| loc = r.shadowMapShader.a["weights_1"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 4, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 16 * numVertices | |
| } else if r.useJoint1 { | |
| r.useJoint1 = false | |
| loc = r.shadowMapShader.a["joints_1"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| loc = r.shadowMapShader.a["weights_1"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| } | |
| } else if r.useJoint0 { | |
| r.useJoint0 = false | |
| r.useJoint1 = false | |
| loc = r.shadowMapShader.a["joints_0"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| loc = r.shadowMapShader.a["weights_0"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| loc = r.shadowMapShader.a["joints_1"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| loc = r.shadowMapShader.a["weights_1"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| } | |
| } | |
| func (r *Renderer_GLES32) ReleaseShadowPipeline() { | |
| loc := r.shadowMapShader.a["inVertexId"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| loc = r.shadowMapShader.a["position"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| loc = r.shadowMapShader.a["uv"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib2f(uint32(loc), 0, 0) | |
| loc = r.shadowMapShader.a["vertColor"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 1, 1, 1, 1) | |
| loc = r.shadowMapShader.a["joints_0"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| loc = r.shadowMapShader.a["weights_0"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| loc = r.shadowMapShader.a["joints_1"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| loc = r.shadowMapShader.a["weights_1"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| //gl.Disable(gl.TEXTURE_2D) | |
| gl.DepthMask(true) | |
| gl.Disable(gl.DEPTH_TEST) | |
| gl.Disable(gl.CULL_FACE) | |
| gl.Disable(gl.BLEND) | |
| r.useJoint0 = false | |
| r.useJoint1 = false | |
| } | |
| func (r *Renderer_GLES32) prepareModelPipeline(bufferIndex uint32, env *Environment) { | |
| r.UseProgram(r.modelShader.program) | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, r.fbo) | |
| gl.Viewport(0, 0, sys.scrrect[2], sys.scrrect[3]) | |
| gl.Clear(gl.DEPTH_BUFFER_BIT) | |
| gl.Enable(gl.TEXTURE_2D) | |
| gl.Enable(gl.TEXTURE_CUBE_MAP) | |
| gl.Enable(gl.BLEND) | |
| if r.depthTest { | |
| gl.Enable(gl.DEPTH_TEST) | |
| gl.DepthFunc(gl.LESS) | |
| } else { | |
| gl.Disable(gl.DEPTH_TEST) | |
| } | |
| gl.DepthMask(r.depthMask) | |
| if r.invertFrontFace { | |
| gl.FrontFace(gl.CW) | |
| } else { | |
| gl.FrontFace(gl.CCW) | |
| } | |
| if !r.doubleSided { | |
| gl.Enable(gl.CULL_FACE) | |
| gl.CullFace(gl.BACK) | |
| } else { | |
| gl.Disable(gl.CULL_FACE) | |
| } | |
| gl.BlendEquation(r.MapBlendEquation(r.blendEquation)) | |
| gl.BlendFunc(r.MapBlendFunction(r.blendSrc), r.MapBlendFunction(r.blendDst)) | |
| gl.BindBuffer(gl.ARRAY_BUFFER, r.modelVertexBuffer[bufferIndex]) | |
| gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, r.modelIndexBuffer[bufferIndex]) | |
| if r.enableShadow { | |
| loc, unit := r.modelShader.u["shadowCubeMap"], r.modelShader.t["shadowCubeMap"] | |
| gl.ActiveTexture((uint32(gl.TEXTURE0 + unit))) | |
| gl.BindTexture(gl.TEXTURE_CUBE_MAP_ARRAY, r.fbo_shadow_cube_texture) | |
| gl.Uniform1i(loc, int32(unit)) | |
| } | |
| if env != nil { | |
| loc, unit := r.modelShader.u["lambertianEnvSampler"], r.modelShader.t["lambertianEnvSampler"] | |
| gl.ActiveTexture((uint32(gl.TEXTURE0 + unit))) | |
| gl.BindTexture(gl.TEXTURE_CUBE_MAP, env.lambertianTexture.tex.(*Texture_GLES32).handle) | |
| gl.Uniform1i(loc, int32(unit)) | |
| loc, unit = r.modelShader.u["GGXEnvSampler"], r.modelShader.t["GGXEnvSampler"] | |
| gl.ActiveTexture((uint32(gl.TEXTURE0 + unit))) | |
| gl.BindTexture(gl.TEXTURE_CUBE_MAP, env.GGXTexture.tex.(*Texture_GLES32).handle) | |
| gl.Uniform1i(loc, int32(unit)) | |
| loc, unit = r.modelShader.u["GGXLUT"], r.modelShader.t["GGXLUT"] | |
| gl.ActiveTexture((uint32(gl.TEXTURE0 + unit))) | |
| gl.BindTexture(gl.TEXTURE_2D, env.GGXLUT.tex.(*Texture_GLES32).handle) | |
| gl.Uniform1i(loc, int32(unit)) | |
| loc = r.modelShader.u["environmentIntensity"] | |
| gl.Uniform1f(loc, env.environmentIntensity) | |
| loc = r.modelShader.u["mipCount"] | |
| gl.Uniform1i(loc, env.mipmapLevels) | |
| loc = r.modelShader.u["environmentRotation"] | |
| rotationMatrix := mgl.Rotate3DX(math.Pi).Mul3(mgl.Rotate3DY(0.5 * math.Pi)) | |
| rotationM := rotationMatrix[:] | |
| gl.UniformMatrix3fv(loc, 1, false, &rotationM[0]) | |
| } else { | |
| loc, unit := r.modelShader.u["lambertianEnvSampler"], r.modelShader.t["lambertianEnvSampler"] | |
| gl.ActiveTexture((uint32(gl.TEXTURE0 + unit))) | |
| gl.BindTexture(gl.TEXTURE_CUBE_MAP, 0) | |
| gl.Uniform1i(loc, int32(unit)) | |
| loc, unit = r.modelShader.u["GGXEnvSampler"], r.modelShader.t["GGXEnvSampler"] | |
| gl.ActiveTexture((uint32(gl.TEXTURE0 + unit))) | |
| gl.BindTexture(gl.TEXTURE_CUBE_MAP, 0) | |
| gl.Uniform1i(loc, int32(unit)) | |
| loc, unit = r.modelShader.u["GGXLUT"], r.modelShader.t["GGXLUT"] | |
| gl.ActiveTexture((uint32(gl.TEXTURE0 + unit))) | |
| gl.BindTexture(gl.TEXTURE_2D, 0) | |
| gl.Uniform1i(loc, int32(unit)) | |
| loc = r.modelShader.u["environmentIntensity"] | |
| gl.Uniform1f(loc, 0) | |
| } | |
| } | |
| func (r *Renderer_GLES32) SetModelPipeline(eq BlendEquation, src, dst BlendFunc, depthTest, depthMask, doubleSided, invertFrontFace, | |
| useUV, useNormal, useTangent, useVertColor, useJoint0, useJoint1, useOutlineAttribute bool, numVertices, vertAttrOffset uint32) { | |
| r.SetDepthTest(depthTest) | |
| r.SetDepthMask(depthMask) | |
| r.SetFrontFace(invertFrontFace) | |
| r.SetCullFace(doubleSided) | |
| r.SetBlending(true, eq, src, dst) | |
| loc := r.modelShader.a["inVertexId"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 1, gl.INT, false, 0, uintptr(vertAttrOffset)) | |
| offset := vertAttrOffset + 4*numVertices | |
| loc = r.modelShader.a["position"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 3, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 12 * numVertices | |
| loc = r.modelShader.a["uv"] | |
| if useUV { | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 2, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 8 * numVertices | |
| } else { | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib2f(uint32(loc), 0, 0) | |
| } | |
| if useNormal { | |
| r.useNormal = true | |
| loc = r.modelShader.a["normalIn"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 3, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 12 * numVertices | |
| } else if r.useNormal { | |
| r.useNormal = false | |
| loc = r.modelShader.a["normalIn"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib3f(uint32(loc), 0, 0, 0) | |
| } | |
| if useTangent { | |
| r.useTangent = true | |
| loc = r.modelShader.a["tangentIn"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 4, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 16 * numVertices | |
| } else if r.useTangent { | |
| r.useTangent = false | |
| loc = r.modelShader.a["tangentIn"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| } | |
| if useVertColor { | |
| r.useVertColor = true | |
| loc = r.modelShader.a["vertColor"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 4, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 16 * numVertices | |
| } else if r.useVertColor { | |
| r.useVertColor = false | |
| loc = r.modelShader.a["vertColor"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 1, 1, 1, 1) | |
| } | |
| if useJoint0 { | |
| r.useJoint0 = true | |
| loc = r.modelShader.a["joints_0"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 4, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 16 * numVertices | |
| loc = r.modelShader.a["weights_0"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 4, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 16 * numVertices | |
| if useJoint1 { | |
| r.useJoint1 = true | |
| loc = r.modelShader.a["joints_1"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 4, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 16 * numVertices | |
| loc = r.modelShader.a["weights_1"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 4, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 16 * numVertices | |
| } else if r.useJoint1 { | |
| r.useJoint1 = false | |
| loc = r.modelShader.a["joints_1"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| loc = r.modelShader.a["weights_1"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| } | |
| } else if r.useJoint0 { | |
| r.useJoint0 = false | |
| r.useJoint1 = false | |
| loc = r.modelShader.a["joints_0"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| loc = r.modelShader.a["weights_0"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| loc = r.modelShader.a["joints_1"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| loc = r.modelShader.a["weights_1"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| } | |
| if useOutlineAttribute { | |
| r.useOutlineAttribute = true | |
| loc = r.modelShader.a["outlineAttributeIn"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 4, gl.FLOAT, false, 0, uintptr(offset)) | |
| offset += 16 * numVertices | |
| } else if r.useOutlineAttribute { | |
| r.useOutlineAttribute = false | |
| loc = r.modelShader.a["outlineAttributeIn"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| } | |
| } | |
| func (r *Renderer_GLES32) SetMeshOulinePipeline(invertFrontFace bool, meshOutline float32) { | |
| r.SetFrontFace(invertFrontFace) | |
| r.SetDepthTest(true) | |
| r.SetDepthMask(true) | |
| loc := r.modelShader.u["meshOutline"] | |
| gl.Uniform1f(loc, meshOutline) | |
| } | |
| func (r *Renderer_GLES32) ReleaseModelPipeline() { | |
| loc := r.modelShader.a["inVertexId"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| loc = r.modelShader.a["position"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| loc = r.modelShader.a["uv"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib2f(uint32(loc), 0, 0) | |
| loc = r.modelShader.a["normalIn"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib3f(uint32(loc), 0, 0, 0) | |
| loc = r.modelShader.a["tangentIn"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| loc = r.modelShader.a["vertColor"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 1, 1, 1, 1) | |
| loc = r.modelShader.a["joints_0"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| loc = r.modelShader.a["weights_0"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| loc = r.modelShader.a["joints_1"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| loc = r.modelShader.a["weights_1"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| loc = r.modelShader.a["outlineAttributeIn"] | |
| gl.DisableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttrib4f(uint32(loc), 0, 0, 0, 0) | |
| //gl.Disable(gl.TEXTURE_2D) | |
| gl.DepthMask(true) | |
| gl.Disable(gl.DEPTH_TEST) | |
| gl.Disable(gl.CULL_FACE) | |
| r.useNormal = false | |
| r.useTangent = false | |
| r.useVertColor = false | |
| r.useJoint0 = false | |
| r.useJoint1 = false | |
| r.useOutlineAttribute = false | |
| } | |
| func (r *Renderer_GLES32) ReadPixels(data []uint8, width, height int) { | |
| // we defer the EndFrame(), SwapBuffers(), and BeginFrame() calls that were previously below now to | |
| // a single spot in order to prevent the blank screenshot bug on single digit FPS | |
| gl.BindFramebuffer(gl.READ_FRAMEBUFFER, 0) | |
| gl.ReadPixels(0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, unsafe.Pointer(&data[0])) | |
| } | |
| func (r *Renderer_GLES32) EnableScissor(x, y, width, height int32) { | |
| // Flip Y to OpenGL convention | |
| realY := sys.scrrect[3] - (y + height) | |
| if r.scissorEnabled && | |
| r.scissorRect[0] == x && r.scissorRect[1] == realY && | |
| r.scissorRect[2] == width && r.scissorRect[3] == height { | |
| return | |
| } | |
| if !r.scissorEnabled { | |
| gl.Enable(gl.SCISSOR_TEST) | |
| r.scissorEnabled = true | |
| } | |
| gl.Scissor(x, realY, width, height) | |
| r.scissorRect = [4]int32{x, realY, width, height} | |
| } | |
| func (r *Renderer_GLES32) DisableScissor() { | |
| if r.scissorEnabled { | |
| gl.Disable(gl.SCISSOR_TEST) | |
| r.scissorEnabled = false | |
| r.scissorRect = [4]int32{0, 0, 0, 0} | |
| } | |
| } | |
| func (r *Renderer_GLES32) SetUniformI(name string, val int) { | |
| loc := r.spriteShader.u[name] | |
| gl.Uniform1i(loc, int32(val)) | |
| } | |
| func (r *Renderer_GLES32) SetUniformF(name string, values ...float32) { | |
| loc := r.spriteShader.u[name] | |
| switch len(values) { | |
| case 1: | |
| gl.Uniform1f(loc, values[0]) | |
| case 2: | |
| gl.Uniform2f(loc, values[0], values[1]) | |
| case 3: | |
| gl.Uniform3f(loc, values[0], values[1], values[2]) | |
| case 4: | |
| gl.Uniform4f(loc, values[0], values[1], values[2], values[3]) | |
| } | |
| } | |
| func (r *Renderer_GLES32) SetUniformFv(name string, values []float32) { | |
| loc := r.spriteShader.u[name] | |
| switch len(values) { | |
| case 2: | |
| gl.Uniform2fv(loc, 1, &values[0]) | |
| case 3: | |
| gl.Uniform3fv(loc, 1, &values[0]) | |
| case 4: | |
| gl.Uniform4fv(loc, 1, &values[0]) | |
| } | |
| } | |
| func (r *Renderer_GLES32) SetUniformMatrix(name string, value []float32) { | |
| loc := r.spriteShader.u[name] | |
| gl.UniformMatrix4fv(loc, 1, false, &value[0]) | |
| } | |
| func (r *Renderer_GLES32) SetTexture(name string, tex Texture) { | |
| t := tex.(*Texture_GLES32) | |
| loc, unit := r.spriteShader.u[name], r.spriteShader.t[name] | |
| if r.lastSpriteTexture[unit] == t.handle { | |
| return | |
| } | |
| r.lastSpriteTexture[unit] = t.handle | |
| gl.ActiveTexture(uint32(gl.TEXTURE0 + unit)) | |
| gl.BindTexture(gl.TEXTURE_2D, t.handle) | |
| gl.Uniform1i(loc, int32(unit)) | |
| } | |
| func (r *Renderer_GLES32) SetModelUniformI(name string, val int) { | |
| loc := r.modelShader.u[name] | |
| gl.Uniform1i(loc, int32(val)) | |
| } | |
| func (r *Renderer_GLES32) SetModelUniformF(name string, values ...float32) { | |
| loc := r.modelShader.u[name] | |
| switch len(values) { | |
| case 1: | |
| gl.Uniform1f(loc, values[0]) | |
| case 2: | |
| gl.Uniform2f(loc, values[0], values[1]) | |
| case 3: | |
| gl.Uniform3f(loc, values[0], values[1], values[2]) | |
| case 4: | |
| gl.Uniform4f(loc, values[0], values[1], values[2], values[3]) | |
| } | |
| } | |
| func (r *Renderer_GLES32) SetModelUniformFv(name string, values []float32) { | |
| loc := r.modelShader.u[name] | |
| switch len(values) { | |
| case 2: | |
| gl.Uniform2fv(loc, 1, &values[0]) | |
| case 3: | |
| gl.Uniform3fv(loc, 1, &values[0]) | |
| case 4: | |
| gl.Uniform4fv(loc, 1, &values[0]) | |
| case 8: | |
| gl.Uniform4fv(loc, 2, &values[0]) | |
| } | |
| } | |
| func (r *Renderer_GLES32) SetModelUniformMatrix(name string, value []float32) { | |
| loc := r.modelShader.u[name] | |
| gl.UniformMatrix4fv(loc, 1, false, &value[0]) | |
| } | |
| func (r *Renderer_GLES32) SetModelUniformMatrix3(name string, value []float32) { | |
| loc := r.modelShader.u[name] | |
| gl.UniformMatrix3fv(loc, 1, false, &value[0]) | |
| } | |
| func (r *Renderer_GLES32) SetModelTexture(name string, tex Texture) { | |
| t := tex.(*Texture_GLES32) | |
| loc, unit := r.modelShader.u[name], r.modelShader.t[name] | |
| gl.ActiveTexture((uint32(gl.TEXTURE0 + unit))) | |
| gl.BindTexture(gl.TEXTURE_2D, t.handle) | |
| gl.Uniform1i(loc, int32(unit)) | |
| } | |
| func (r *Renderer_GLES32) SetShadowMapUniformI(name string, val int) { | |
| loc := r.shadowMapShader.u[name] | |
| gl.Uniform1i(loc, int32(val)) | |
| } | |
| func (r *Renderer_GLES32) SetShadowMapUniformF(name string, values ...float32) { | |
| loc := r.shadowMapShader.u[name] | |
| switch len(values) { | |
| case 1: | |
| gl.Uniform1f(loc, values[0]) | |
| case 2: | |
| gl.Uniform2f(loc, values[0], values[1]) | |
| case 3: | |
| gl.Uniform3f(loc, values[0], values[1], values[2]) | |
| case 4: | |
| gl.Uniform4f(loc, values[0], values[1], values[2], values[3]) | |
| } | |
| } | |
| func (r *Renderer_GLES32) SetShadowMapUniformFv(name string, values []float32) { | |
| loc := r.shadowMapShader.u[name] | |
| switch len(values) { | |
| case 2: | |
| gl.Uniform2fv(loc, 1, &values[0]) | |
| case 3: | |
| gl.Uniform3fv(loc, 1, &values[0]) | |
| case 4: | |
| gl.Uniform4fv(loc, 1, &values[0]) | |
| case 8: | |
| gl.Uniform4fv(loc, 2, &values[0]) | |
| } | |
| } | |
| func (r *Renderer_GLES32) SetShadowMapUniformMatrix(name string, value []float32) { | |
| loc := r.shadowMapShader.u[name] | |
| gl.UniformMatrix4fv(loc, 1, false, &value[0]) | |
| } | |
| func (r *Renderer_GLES32) SetShadowMapUniformMatrix3(name string, value []float32) { | |
| loc := r.shadowMapShader.u[name] | |
| gl.UniformMatrix3fv(loc, 1, false, &value[0]) | |
| } | |
| func (r *Renderer_GLES32) SetShadowMapTexture(name string, tex Texture) { | |
| t := tex.(*Texture_GLES32) | |
| loc, unit := r.shadowMapShader.u[name], r.shadowMapShader.t[name] | |
| gl.ActiveTexture((uint32(gl.TEXTURE0 + unit))) | |
| gl.BindTexture(gl.TEXTURE_2D, t.handle) | |
| gl.Uniform1i(loc, int32(unit)) | |
| } | |
| func (r *Renderer_GLES32) SetShadowFrameTexture(i uint32) { | |
| gl.FramebufferTexture(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, r.fbo_shadow_cube_texture, 0) | |
| } | |
| func (r *Renderer_GLES32) SetShadowFrameCubeTexture(i uint32) { | |
| gl.FramebufferTexture(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, r.fbo_shadow_cube_texture, 0) | |
| } | |
| func (r *Renderer_GLES32) SetVertexData(values ...float32) { | |
| data := f32.Bytes(binary.LittleEndian, values...) | |
| gl.BindBuffer(gl.ARRAY_BUFFER, r.vertexBuffer) | |
| gl.BufferData(gl.ARRAY_BUFFER, len(data), unsafe.Pointer(&data[0]), gl.STATIC_DRAW) | |
| } | |
| func (r *Renderer_GLES32) SetModelVertexData(bufferIndex uint32, values []byte) { | |
| gl.BindBuffer(gl.ARRAY_BUFFER, r.modelVertexBuffer[bufferIndex]) | |
| gl.BufferData(gl.ARRAY_BUFFER, len(values), unsafe.Pointer(&values[0]), gl.STATIC_DRAW) | |
| } | |
| func (r *Renderer_GLES32) SetModelIndexData(bufferIndex uint32, values ...uint32) { | |
| data := new(bytes.Buffer) | |
| binary.Write(data, binary.LittleEndian, values) | |
| gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, r.modelIndexBuffer[bufferIndex]) | |
| gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, len(values)*4, unsafe.Pointer(&data.Bytes()[0]), gl.STATIC_DRAW) | |
| } | |
| func (r *Renderer_GLES32) RenderQuad() { | |
| gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) | |
| } | |
| func (r *Renderer_GLES32) RenderElements(mode PrimitiveMode, count, offset int) { | |
| gl.DrawElementsWithOffset(r.MapPrimitiveMode(mode), int32(count), gl.UNSIGNED_INT, uintptr(offset)) | |
| } | |
| func (r *Renderer_GLES32) RenderShadowMapElements(mode PrimitiveMode, count, offset int) { | |
| r.RenderElements(mode, count, offset) | |
| } | |
| func (r *Renderer_GLES32) RenderCubeMap(envTex Texture, cubeTex Texture) { | |
| envTexture := envTex.(*Texture_GLES32) | |
| cubeTexture := cubeTex.(*Texture_GLES32) | |
| textureSize := cubeTexture.width | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, r.fbo_env) | |
| gl.Viewport(0, 0, textureSize, textureSize) | |
| r.UseProgram(r.panoramaToCubeMapShader.program) | |
| loc := r.panoramaToCubeMapShader.a["VertCoord"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 2, gl.FLOAT, false, 0, 0) | |
| data := f32.Bytes(binary.LittleEndian, -1, -1, 1, -1, -1, 1, 1, 1) | |
| gl.BindBuffer(gl.ARRAY_BUFFER, r.vertexBuffer) | |
| gl.BufferData(gl.ARRAY_BUFFER, len(data), unsafe.Pointer(&data[0]), gl.STATIC_DRAW) | |
| loc, unit := r.panoramaToCubeMapShader.u["panorama"], r.panoramaToCubeMapShader.t["panorama"] | |
| gl.ActiveTexture((uint32(gl.TEXTURE0 + unit))) | |
| gl.BindTexture(gl.TEXTURE_2D, envTexture.handle) | |
| gl.Uniform1i(loc, int32(unit)) | |
| for i := 0; i < 6; i++ { | |
| gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, uint32(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i), cubeTexture.handle, 0) | |
| gl.Clear(gl.COLOR_BUFFER_BIT) | |
| loc := r.panoramaToCubeMapShader.u["currentFace"] | |
| gl.Uniform1i(loc, int32(i)) | |
| gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) | |
| } | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, r.fbo) | |
| gl.BindTexture(gl.TEXTURE_CUBE_MAP, cubeTexture.handle) | |
| gl.GenerateMipmap(gl.TEXTURE_CUBE_MAP) | |
| } | |
| func (r *Renderer_GLES32) RenderFilteredCubeMap(distribution int32, cubeTex Texture, filteredTex Texture, mipmapLevel, sampleCount int32, roughness float32) { | |
| cubeTexture := cubeTex.(*Texture_GLES32) | |
| filteredTexture := filteredTex.(*Texture_GLES32) | |
| textureSize := filteredTexture.width | |
| currentTextureSize := textureSize >> mipmapLevel | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, r.fbo_env) | |
| gl.Viewport(0, 0, currentTextureSize, currentTextureSize) | |
| r.UseProgram(r.cubemapFilteringShader.program) | |
| loc := r.cubemapFilteringShader.a["VertCoord"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 2, gl.FLOAT, false, 0, 0) | |
| data := f32.Bytes(binary.LittleEndian, -1, -1, 1, -1, -1, 1, 1, 1) | |
| gl.BindBuffer(gl.ARRAY_BUFFER, r.vertexBuffer) | |
| gl.BufferData(gl.ARRAY_BUFFER, len(data), unsafe.Pointer(&data[0]), gl.STATIC_DRAW) | |
| loc, unit := r.cubemapFilteringShader.u["cubeMap"], r.cubemapFilteringShader.t["cubeMap"] | |
| gl.ActiveTexture((uint32(gl.TEXTURE0 + unit))) | |
| gl.BindTexture(gl.TEXTURE_CUBE_MAP, cubeTexture.handle) | |
| gl.Uniform1i(loc, int32(unit)) | |
| loc = r.cubemapFilteringShader.u["sampleCount"] | |
| gl.Uniform1i(loc, sampleCount) | |
| loc = r.cubemapFilteringShader.u["distribution"] | |
| gl.Uniform1i(loc, distribution) | |
| loc = r.cubemapFilteringShader.u["width"] | |
| gl.Uniform1i(loc, textureSize) | |
| loc = r.cubemapFilteringShader.u["roughness"] | |
| gl.Uniform1f(loc, roughness) | |
| loc = r.cubemapFilteringShader.u["intensityScale"] | |
| gl.Uniform1f(loc, 1) | |
| loc = r.cubemapFilteringShader.u["isLUT"] | |
| gl.Uniform1i(loc, 0) | |
| for i := 0; i < 6; i++ { | |
| gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, uint32(gl.TEXTURE_CUBE_MAP_POSITIVE_X+i), filteredTexture.handle, mipmapLevel) | |
| gl.Clear(gl.COLOR_BUFFER_BIT) | |
| loc := r.cubemapFilteringShader.u["currentFace"] | |
| gl.Uniform1i(loc, int32(i)) | |
| gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) | |
| } | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, r.fbo) | |
| } | |
| func (r *Renderer_GLES32) RenderLUT(distribution int32, cubeTex Texture, lutTex Texture, sampleCount int32) { | |
| cubeTexture := cubeTex.(*Texture_GLES32) | |
| lutTexture := lutTex.(*Texture_GLES32) | |
| textureSize := lutTexture.width | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, r.fbo_env) | |
| gl.Viewport(0, 0, textureSize, textureSize) | |
| r.UseProgram(r.cubemapFilteringShader.program) | |
| loc := r.cubemapFilteringShader.a["VertCoord"] | |
| gl.EnableVertexAttribArray(uint32(loc)) | |
| gl.VertexAttribPointerWithOffset(uint32(loc), 2, gl.FLOAT, false, 0, 0) | |
| data := f32.Bytes(binary.LittleEndian, -1, -1, 1, -1, -1, 1, 1, 1) | |
| gl.BindBuffer(gl.ARRAY_BUFFER, r.vertexBuffer) | |
| gl.BufferData(gl.ARRAY_BUFFER, len(data), unsafe.Pointer(&data[0]), gl.STATIC_DRAW) | |
| loc, unit := r.cubemapFilteringShader.u["cubeMap"], r.cubemapFilteringShader.t["cubeMap"] | |
| gl.ActiveTexture((uint32(gl.TEXTURE0 + unit))) | |
| gl.BindTexture(gl.TEXTURE_CUBE_MAP, cubeTexture.handle) | |
| gl.Uniform1i(loc, int32(unit)) | |
| loc = r.cubemapFilteringShader.u["sampleCount"] | |
| gl.Uniform1i(loc, sampleCount) | |
| loc = r.cubemapFilteringShader.u["distribution"] | |
| gl.Uniform1i(loc, distribution) | |
| loc = r.cubemapFilteringShader.u["width"] | |
| gl.Uniform1i(loc, textureSize) | |
| loc = r.cubemapFilteringShader.u["roughness"] | |
| gl.Uniform1f(loc, 0) | |
| loc = r.cubemapFilteringShader.u["intensityScale"] | |
| gl.Uniform1f(loc, 1) | |
| loc = r.cubemapFilteringShader.u["currentFace"] | |
| gl.Uniform1i(loc, 0) | |
| loc = r.cubemapFilteringShader.u["isLUT"] | |
| gl.Uniform1i(loc, 1) | |
| gl.BindTexture(gl.TEXTURE_2D, lutTexture.handle) | |
| gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA32F, lutTexture.width, lutTexture.height, 0, gl.RGBA, gl.FLOAT, nil) | |
| gl.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, lutTexture.handle, 0) | |
| gl.Clear(gl.COLOR_BUFFER_BIT) | |
| gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) | |
| gl.BindFramebuffer(gl.FRAMEBUFFER, r.fbo) | |
| } | |
| func (r *Renderer_GLES32) PerspectiveProjectionMatrix(angle, aspect, near, far float32) mgl.Mat4 { | |
| return mgl.Perspective(angle, aspect, near, far) | |
| } | |
| func (r *Renderer_GLES32) OrthographicProjectionMatrix(left, right, bottom, top, near, far float32) mgl.Mat4 { | |
| ret := mgl.Ortho(left, right, bottom, top, near, far) | |
| return ret | |
| } | |
| func (r *Renderer_GLES32) NewWorkerThread() bool { | |
| return false | |
| } | |
| func (r *Renderer_GLES32) SetVSync(interval int) { | |
| sdl.GLSetSwapInterval(interval) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment