first version

This commit is contained in:
Thomas Friedel 2018-01-24 14:19:32 +01:00
commit 4be47cd5ab
6 changed files with 1998 additions and 0 deletions

184
canvas.go Normal file
View file

@ -0,0 +1,184 @@
package canvas
import (
"unsafe"
"github.com/void6/lm"
)
// Canvas represents an area on the viewport on which to draw
// using a set of functions very similar to the HTML5 canvas
type Canvas struct {
x, y, w, h int
fx, fy, fw, fh float32
fill struct {
r, g, b, a float32
}
stroke struct {
r, g, b, a float32
lineWidth float32
}
path []pathPoint
}
type pathPoint struct {
pos lm.Vec2
move bool
}
// New creates a new canvas with the given viewport coordinates.
// While all functions on the canvas use the top left point as
// the origin, since GL uses the bottom left coordinate, the
// coordinates given here also use the bottom left as origin
func New(x, y, w, h int) *Canvas {
cv := &Canvas{
x: x, y: y, w: w, h: h,
fx: float32(x), fy: float32(y),
fw: float32(w), fh: float32(h),
}
cv.stroke.lineWidth = 1
return cv
}
func (cv *Canvas) xToGL(x float32) float32 { return x*2/cv.fw - 1 }
func (cv *Canvas) yToGL(y float32) float32 { return -y*2/cv.fh + 1 }
func (cv *Canvas) ptToGL(x, y float32) (fx, fy float32) { return x*2/cv.fw - 1, -y*2/cv.fh + 1 }
func (cv *Canvas) vecToGL(v lm.Vec2) (fx, fy float32) { return v[0]*2/cv.fw - 1, -v[1]*2/cv.fh + 1 }
// Activate makes the canvas active and sets the viewport. Only needs
// to be called if any other GL code changes the viewport
func (cv *Canvas) Activate() {
gli.Viewport(int32(cv.x), int32(cv.y), int32(cv.w), int32(cv.h))
}
var activeCanvas *Canvas
func (cv *Canvas) activate() {
if activeCanvas != cv {
activeCanvas = cv
cv.Activate()
}
}
// SetFillColor sets the color for any fill calls
func (cv *Canvas) SetFillColor(value ...interface{}) {
r, g, b, a, ok := parseColor(value...)
if ok {
cv.fill.r, cv.fill.g, cv.fill.b, cv.fill.a = r, g, b, a
}
}
// SetStrokeColor sets the color for any line drawing calls
func (cv *Canvas) SetStrokeColor(value ...interface{}) {
r, g, b, a, ok := parseColor(value...)
if ok {
cv.stroke.r, cv.stroke.g, cv.stroke.b, cv.stroke.a = r, g, b, a
}
}
// SetLineWidth sets the line width for any line drawing calls
func (cv *Canvas) SetLineWidth(width float32) {
cv.stroke.lineWidth = width
}
// FillRect fills a rectangle with the active color
func (cv *Canvas) FillRect(x, y, w, h float32) {
cv.activate()
gli.UseProgram(sr.id)
x0f, y0f := cv.ptToGL(x, y)
x1f, y1f := cv.ptToGL(x+w, y+h)
gli.BindBuffer(gl_ARRAY_BUFFER, buf)
data := [8]float32{x0f, y0f, x0f, y1f, x1f, y1f, x1f, y0f}
gli.BufferData(gl_ARRAY_BUFFER, len(data)*4, unsafe.Pointer(&data[0]), gl_STREAM_DRAW)
gli.VertexAttribPointer(sr.vertex, 2, gl_FLOAT, false, 0, nil)
gli.Uniform4f(sr.color, cv.fill.r, cv.fill.g, cv.fill.b, cv.fill.a)
gli.EnableVertexAttribArray(sr.vertex)
gli.DrawArrays(gl_TRIANGLE_FAN, 0, 4)
gli.DisableVertexAttribArray(sr.vertex)
}
func (cv *Canvas) BeginPath() {
if cv.path == nil {
cv.path = make([]pathPoint, 0, 100)
}
cv.path = cv.path[:0]
}
func (cv *Canvas) MoveTo(x, y float32) {
cv.path = append(cv.path, pathPoint{pos: lm.Vec2{x, y}, move: true})
}
func (cv *Canvas) LineTo(x, y float32) {
cv.path = append(cv.path, pathPoint{pos: lm.Vec2{x, y}, move: false})
}
func (cv *Canvas) ClosePath() {
if len(cv.path) == 0 {
return
}
cv.path = append(cv.path, pathPoint{pos: cv.path[0].pos, move: false})
}
func (cv *Canvas) Stroke() {
if len(cv.path) == 0 {
return
}
cv.activate()
gli.Enable(gl_STENCIL_TEST)
gli.ColorMask(false, false, false, false)
gli.StencilFunc(gl_ALWAYS, 1, 0xFF)
gli.StencilOp(gl_KEEP, gl_KEEP, gl_REPLACE)
gli.StencilMask(0xFF)
gli.Clear(gl_STENCIL_BUFFER_BIT)
gli.UseProgram(sr.id)
gli.Uniform4f(sr.color, cv.stroke.r, cv.stroke.g, cv.stroke.b, cv.stroke.a)
gli.EnableVertexAttribArray(sr.vertex)
gli.BindBuffer(gl_ARRAY_BUFFER, buf)
var buf [1000]float32
tris := buf[:0]
tris = append(tris, -1, -1, -1, 1, 1, 1, -1, -1, 1, 1, 1, -1)
p0 := cv.path[0].pos
for _, p := range cv.path {
if p.move {
p0 = p.pos
continue
}
p1 := p.pos
v := p1.Sub(p0).Norm()
v = lm.Vec2{v[1], -v[0]}.MulF(cv.stroke.lineWidth * 0.5)
x0f, y0f := cv.vecToGL(p0.Add(v))
x1f, y1f := cv.vecToGL(p1.Add(v))
x2f, y2f := cv.vecToGL(p1.Sub(v))
x3f, y3f := cv.vecToGL(p0.Sub(v))
tris = append(tris, x0f, y0f, x1f, y1f, x2f, y2f, x0f, y0f, x2f, y2f, x3f, y3f)
p0 = p1
}
gli.BufferData(gl_ARRAY_BUFFER, len(tris)*4, unsafe.Pointer(&tris[0]), gl_STREAM_DRAW)
gli.VertexAttribPointer(sr.vertex, 2, gl_FLOAT, false, 0, nil)
gli.DrawArrays(gl_TRIANGLES, 6, int32(len(tris)/2))
gli.ColorMask(true, true, true, true)
gli.StencilFunc(gl_EQUAL, 1, 0xFF)
gli.StencilMask(0)
gli.DrawArrays(gl_TRIANGLE_FAN, 0, 6)
gli.DisableVertexAttribArray(sr.vertex)
gli.Disable(gl_STENCIL_TEST)
}

215
color.go Normal file
View file

@ -0,0 +1,215 @@
package canvas
import (
"fmt"
"image/color"
"strconv"
"strings"
)
func colorToGL(color color.Color) (r, g, b, a float32) {
ir, ig, ib, ia := color.RGBA()
r = float32(ir) / 65535
g = float32(ig) / 65535
b = float32(ib) / 65535
a = float32(ia) / 65535
return
}
func parseHexRune(rn rune) (int, bool) {
switch {
case rn >= '0' && rn <= '9':
return int(rn - '0'), true
case rn >= 'a' && rn <= 'f':
return int(rn-'a') + 10, true
case rn >= 'A' && rn <= 'F':
return int(rn-'A') + 10, true
}
return 0, false
}
func parseHexRunePair(rn1, rn2 rune) (int, bool) {
i1, ok := parseHexRune(rn1)
if !ok {
return 0, false
}
i2, ok := parseHexRune(rn2)
if !ok {
return 0, false
}
return i1*16 + i2, true
}
func parseColorComponent(value interface{}) (float32, bool) {
switch v := value.(type) {
case float32:
return v, true
case float64:
return float32(v), true
case int:
return float32(v) / 255, true
case uint:
return float32(v) / 255, true
case uint8:
return float32(v) / 255, true
case string:
if len(v) == 0 {
return 0, false
}
if v[0] == '#' {
str := v[1:]
if len(str) > 2 {
return 0, false
}
conv, err := strconv.ParseUint(v[1:], 16, 8)
if err != nil {
return 0, false
}
return float32(conv) / 255, true
} else if strings.ContainsRune(v, '.') {
conv, err := strconv.ParseFloat(v, 32)
if err != nil {
return 0, false
}
if conv < 0 {
conv = 0
} else if conv > 1 {
conv = 1
}
return float32(conv), true
} else {
conv, err := strconv.ParseUint(v, 10, 8)
if err != nil {
return 0, false
}
return float32(conv) / 255, true
}
}
return 0, false
}
func parseColor(value ...interface{}) (r, g, b, a float32, ok bool) {
a = 1
if len(value) == 1 {
switch v := value[0].(type) {
case color.Color:
r, g, b, a = colorToGL(v)
ok = true
return
case [3]float32:
return v[0], v[1], v[2], 1, true
case [4]float32:
return v[0], v[1], v[2], v[3], true
case [3]float64:
return float32(v[0]), float32(v[1]), float32(v[2]), 1, true
case [4]float64:
return float32(v[0]), float32(v[1]), float32(v[2]), float32(v[3]), true
case [3]int:
return float32(v[0]) / 255, float32(v[1]) / 255, float32(v[2]) / 255, 1, true
case [4]int:
return float32(v[0]) / 255, float32(v[1]) / 255, float32(v[2]) / 255, float32(v[3]) / 255, true
case [3]uint:
return float32(v[0]) / 255, float32(v[1]) / 255, float32(v[2]) / 255, 1, true
case [4]uint:
return float32(v[0]) / 255, float32(v[1]) / 255, float32(v[2]) / 255, float32(v[3]) / 255, true
case [3]uint8:
return float32(v[0]) / 255, float32(v[1]) / 255, float32(v[2]) / 255, 1, true
case [4]uint8:
return float32(v[0]) / 255, float32(v[1]) / 255, float32(v[2]) / 255, float32(v[3]) / 255, true
case string:
if len(v) == 0 {
return
}
if v[0] == '#' {
str := v[1:]
if len(str) == 3 || len(str) == 4 {
var ir, ig, ib int
ia := 255
ir, ok = parseHexRune(rune(str[0]))
if !ok {
return
}
ir = ir*16 + ir
ig, ok = parseHexRune(rune(str[1]))
if !ok {
return
}
ig = ig*16 + ig
ib, ok = parseHexRune(rune(str[2]))
if !ok {
return
}
ib = ib*16 + ib
if len(str) == 4 {
ia, ok = parseHexRune(rune(str[3]))
if !ok {
return
}
ia = ia*16 + ia
}
return float32(ir) / 255, float32(ig) / 255, float32(ib) / 255, float32(ia) / 255, true
} else if len(str) == 6 || len(str) == 8 {
var ir, ig, ib int
ia := 255
ir, ok = parseHexRunePair(rune(str[0]), rune(str[1]))
if !ok {
return
}
ig, ok = parseHexRunePair(rune(str[2]), rune(str[3]))
if !ok {
return
}
ib, ok = parseHexRunePair(rune(str[4]), rune(str[5]))
if !ok {
return
}
if len(str) == 4 {
ia, ok = parseHexRunePair(rune(str[6]), rune(str[7]))
if !ok {
return
}
}
return float32(ir) / 255, float32(ig) / 255, float32(ib) / 255, float32(ia) / 255, true
} else {
return
}
} else {
v = strings.Replace(v, " ", "", -1)
var ir, ig, ib, ia int
n, err := fmt.Sscanf(v, "rgb(%d,%d,%d)", &ir, &ig, &ib)
if err == nil && n == 3 {
return float32(ir) / 255, float32(ig) / 255, float32(ib) / 255, 1, true
}
n, err = fmt.Sscanf(v, "rgba(%d,%d,%d,%d)", &ir, &ig, &ib, &ia)
if err == nil && n == 4 {
return float32(ir) / 255, float32(ig) / 255, float32(ib) / 255, float32(ia) / 255, true
}
}
}
} else if len(value) == 3 || len(value) == 4 {
var pr, pg, pb, pa float32
pr, ok = parseColorComponent(value[0])
if !ok {
return
}
pg, ok = parseColorComponent(value[1])
if !ok {
return
}
pb, ok = parseColorComponent(value[2])
if !ok {
return
}
if len(value) == 4 {
pa, ok = parseColorComponent(value[3])
if !ok {
return
}
} else {
pa = 1
}
return pr, pg, pb, pa, true
}
return 0, 0, 0, 1, false
}

104
made_shaders.go Executable file
View file

@ -0,0 +1,104 @@
package canvas
import (
"errors"
"fmt"
"strings"
)
type solidShader struct {
id uint32
vertex uint32
color int32
}
func loadSolidShader() (*solidShader, error) {
var vs, fs, program uint32
{
csource, freeFunc := gli.Strs(solidVS + "\x00")
defer freeFunc()
vs = gli.CreateShader(gl_VERTEX_SHADER)
gli.ShaderSource(vs, 1, csource, nil)
gli.CompileShader(vs)
var logLength int32
gli.GetShaderiv(vs, gl_INFO_LOG_LENGTH, &logLength)
if logLength > 0 {
shLog := strings.Repeat("\x00", int(logLength+1))
gli.GetShaderInfoLog(vs, logLength, nil, gli.Str(shLog))
fmt.Printf("VERTEX_SHADER compilation log:\n\n%s\n", shLog)
}
var status int32
gli.GetShaderiv(vs, gl_COMPILE_STATUS, &status)
if status != gl_TRUE {
gli.DeleteShader(vs)
return nil, errors.New("Error compiling GL_VERTEX_SHADER shader part")
}
if glErr := gli.GetError(); glErr != gl_NO_ERROR {
return nil, errors.New("error compiling shader part, glError: " + fmt.Sprint(glErr))
}
}
{
csource, freeFunc := gli.Strs(solidFS + "\x00")
defer freeFunc()
fs = gli.CreateShader(gl_FRAGMENT_SHADER)
gli.ShaderSource(fs, 1, csource, nil)
gli.CompileShader(fs)
var logLength int32
gli.GetShaderiv(fs, gl_INFO_LOG_LENGTH, &logLength)
if logLength > 0 {
shLog := strings.Repeat("\x00", int(logLength+1))
gli.GetShaderInfoLog(fs, logLength, nil, gli.Str(shLog))
fmt.Printf("FRAGMENT_SHADER compilation log:\n\n%s\n", shLog)
}
var status int32
gli.GetShaderiv(fs, gl_COMPILE_STATUS, &status)
if status != gl_TRUE {
gli.DeleteShader(fs)
return nil, errors.New("Error compiling GL_FRAGMENT_SHADER shader part")
}
if glErr := gli.GetError(); glErr != gl_NO_ERROR {
return nil, errors.New("error compiling shader part, glError: " + fmt.Sprint(glErr))
}
}
{
program = gli.CreateProgram()
gli.AttachShader(program, vs)
gli.AttachShader(program, fs)
gli.LinkProgram(program)
var logLength int32
gli.GetProgramiv(program, gl_INFO_LOG_LENGTH, &logLength)
if logLength > 0 {
shLog := strings.Repeat("\x00", int(logLength+1))
gli.GetProgramInfoLog(program, logLength, nil, gli.Str(shLog))
fmt.Printf("Shader link log:\n\n%s\n", shLog)
}
var status int32
gli.GetProgramiv(program, gl_LINK_STATUS, &status)
if status != gl_TRUE {
gli.DeleteShader(vs)
gli.DeleteShader(fs)
return nil, errors.New("error linking shader")
}
if glErr := gli.GetError(); glErr != gl_NO_ERROR {
return nil, errors.New("error linking shader, glError: " + fmt.Sprint(glErr))
}
}
result := &solidShader{}
result.id = program
result.vertex = uint32(gli.GetAttribLocation(program, gli.Str("vertex\x00")))
result.color = gli.GetUniformLocation(program, gli.Str("color\x00"))
return result, nil
}

318
make_shaders.go Normal file
View file

@ -0,0 +1,318 @@
//+build ignore
package main
import (
"bytes"
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"io/ioutil"
"log"
"regexp"
"strings"
"unicode"
)
func main() {
// find the go files in the current directory
pkg, err := build.Default.ImportDir(".", 0)
if err != nil {
log.Fatalf("Could not process directory: %s", err)
}
vsMap := make(map[string]string)
fsMap := make(map[string]string)
// go through each file and find const raw string literals with
fset := token.NewFileSet()
for _, goFile := range pkg.GoFiles {
parsedFile, err := parser.ParseFile(fset, goFile, nil, 0)
if err != nil {
log.Fatalf("Failed to parse file %s: %s", goFile, err)
}
for _, obj := range parsedFile.Scope.Objects {
isVS := strings.HasSuffix(obj.Name, "VS")
isFS := strings.HasSuffix(obj.Name, "FS")
var shaderCode string
if isVS || isFS {
if value, ok := obj.Decl.(*ast.ValueSpec); ok && len(value.Values) == 1 {
if lit, ok := value.Values[0].(*ast.BasicLit); ok {
shaderCode = lit.Value
}
}
}
if shaderCode != "" {
baseName := obj.Name[:len(obj.Name)-2]
if isVS {
vsMap[baseName] = shaderCode
} else if isFS {
fsMap[baseName] = shaderCode
}
}
}
}
for name := range vsMap {
if _, ok := fsMap[name]; !ok {
log.Println("Warning: Vertex shader with no corresponding fragment shader (" + name + ")")
delete(vsMap, name)
continue
}
}
for name := range fsMap {
if _, ok := vsMap[name]; !ok {
log.Println("Warning: Fragment shader with no corresponding vertex shader (" + name + ")")
delete(fsMap, name)
continue
}
}
goCode := &bytes.Buffer{}
buildCodeHeader(goCode)
for name, vs := range vsMap {
fs := fsMap[name]
vs = vs[1 : len(vs)-1]
fs = fs[1 : len(fs)-1]
inputs := shaderFindInputVariables(vs + "\n" + fs)
buildCode(goCode, name, inputs)
}
err = ioutil.WriteFile("made_shaders.go", goCode.Bytes(), 0777)
if err != nil {
log.Fatalf("Could not write made_shaders.go: %s", err)
}
}
type ShaderInput struct {
Name string
IsAttrib bool
IsUniform bool
}
func shaderFindInputVariables(source string) []ShaderInput {
inputs := make([]ShaderInput, 0, 10)
varDefSplitter := regexp.MustCompile("[ \t\r\n,]+")
lines := shaderGetTopLevelLines(source)
for _, line := range lines {
line = strings.TrimSpace(line)
if len(line) == 0 || line[0] == '#' {
continue
}
if strings.Contains(line, "{") {
break
}
parts := varDefSplitter.Split(line, -1)
isAttrib := parts[0] == "attribute"
isUniform := parts[0] == "uniform"
if !isAttrib && !isUniform {
continue
}
for i := 2; i < len(parts); i++ {
inputs = append(inputs, ShaderInput{
Name: parts[i],
IsAttrib: isAttrib,
IsUniform: isUniform})
}
}
return inputs
}
func shaderGetTopLevelLines(source string) []string {
sourceBytes := []byte(source)
l := len(sourceBytes)
if l == 0 {
return make([]string, 0)
}
var inPrecompiledStatement, inLineComment, inBlockComment, inString, stringEscape, lastWasWhitespace bool
curlyBraceDepth := 0
topLevelLines := make([]string, 0, 100)
currentLine := make([]byte, 0, 1000)
var c0, c1 byte = ' ', ' '
for i := 0; i < l; i++ {
c1 = sourceBytes[i]
isWhitespace := unicode.IsSpace(rune(c1))
if !inBlockComment && !inString && c0 == '/' && c1 == '/' {
inLineComment = true
if len(currentLine) > 0 {
currentLine = currentLine[:len(currentLine)-1]
}
} else if !inBlockComment && !inString && c1 == '#' {
inPrecompiledStatement = true
} else if !inBlockComment && !inLineComment && !inPrecompiledStatement && !inString && c0 == '/' && c1 == '*' {
inBlockComment = true
if len(currentLine) > 0 {
currentLine = currentLine[:len(currentLine)-1]
}
} else if !inBlockComment && !inString && !inLineComment && !inPrecompiledStatement && c1 == '"' {
inString = true
} else if inString && !stringEscape && c1 == '\\' {
stringEscape = true
} else if inString && stringEscape {
stringEscape = false
} else if inString && !stringEscape && c1 == '"' {
inString = false
} else if !inBlockComment && !inLineComment && !inPrecompiledStatement && !inString && c1 == '{' {
if curlyBraceDepth == 0 {
topLevelLines = append(topLevelLines, string(currentLine))
currentLine = currentLine[:0]
}
curlyBraceDepth++
}
if !inBlockComment && !inLineComment && !inPrecompiledStatement && !inString && curlyBraceDepth == 0 {
if c1 == ';' {
topLevelLines = append(topLevelLines, string(currentLine))
currentLine = currentLine[:0]
} else if !isWhitespace {
currentLine = append(currentLine, c1)
} else if !lastWasWhitespace {
currentLine = append(currentLine, ' ')
}
}
if !inBlockComment && !inLineComment && !inPrecompiledStatement && !inString && c1 == '}' {
curlyBraceDepth--
} else if (inLineComment || inPrecompiledStatement) && (c1 == '\r' || c1 == '\n') {
inLineComment = false
inPrecompiledStatement = false
} else if inBlockComment && c0 == '*' && c1 == '/' {
inBlockComment = false
}
lastWasWhitespace = isWhitespace
c0 = c1
}
topLevelLines = append(topLevelLines, string(currentLine))
return topLevelLines
}
const compilePart = `
{
csource, freeFunc := gli.Strs(SHADER_SRC + "\x00")
defer freeFunc()
SHADER_VAR = gli.CreateShader(gl_SHADER_TYPE)
gli.ShaderSource(SHADER_VAR, 1, csource, nil)
gli.CompileShader(SHADER_VAR)
var logLength int32
gli.GetShaderiv(SHADER_VAR, gl_INFO_LOG_LENGTH, &logLength)
if logLength > 0 {
shLog := strings.Repeat("\x00", int(logLength+1))
gli.GetShaderInfoLog(SHADER_VAR, logLength, nil, gli.Str(shLog))
fmt.Printf("SHADER_TYPE compilation log:\n\n%s\n", shLog)
}
var status int32
gli.GetShaderiv(SHADER_VAR, gl_COMPILE_STATUS, &status)
if status != gl_TRUE {
gli.DeleteShader(SHADER_VAR)
return nil, errors.New("Error compiling GL_SHADER_TYPE shader part")
}
if glErr := gli.GetError(); glErr != gl_NO_ERROR {
return nil, errors.New("error compiling shader part, glError: " + fmt.Sprint(glErr))
}
}
`
const linkPart = `
{
program = gli.CreateProgram()
gli.AttachShader(program, vs)
gli.AttachShader(program, fs)
gli.LinkProgram(program)
var logLength int32
gli.GetProgramiv(program, gl_INFO_LOG_LENGTH, &logLength)
if logLength > 0 {
shLog := strings.Repeat("\x00", int(logLength+1))
gli.GetProgramInfoLog(program, logLength, nil, gli.Str(shLog))
fmt.Printf("Shader link log:\n\n%s\n", shLog)
}
var status int32
gli.GetProgramiv(program, gl_LINK_STATUS, &status)
if status != gl_TRUE {
gli.DeleteShader(vs)
gli.DeleteShader(fs)
return nil, errors.New("error linking shader")
}
if glErr := gli.GetError(); glErr != gl_NO_ERROR {
return nil, errors.New("error linking shader, glError: " + fmt.Sprint(glErr))
}
}
`
func capitalize(s string) string {
return strings.ToUpper(s[:1]) + s[1:]
}
func buildCodeHeader(buf *bytes.Buffer) {
fmt.Fprint(buf, "package canvas\n\n")
fmt.Fprint(buf, "import (\n")
fmt.Fprint(buf, "\t\"errors\"\n")
fmt.Fprint(buf, "\t\"fmt\"\n")
fmt.Fprint(buf, "\t\"strings\"\n")
fmt.Fprint(buf, ")\n\n")
}
func buildCode(buf *bytes.Buffer, baseName string, inputs []ShaderInput) {
shaderName := baseName + "Shader"
vsName := baseName + "VS"
fsName := baseName + "FS"
fmt.Fprintf(buf, "type %s struct {\n", shaderName)
fmt.Fprint(buf, "\tid uint32\n")
for _, input := range inputs {
if input.IsAttrib {
fmt.Fprintf(buf, "\t%s uint32\n", input.Name)
} else if input.IsUniform {
fmt.Fprintf(buf, "\t%s int32\n", input.Name)
}
}
fmt.Fprint(buf, "}\n\n")
fmt.Fprintf(buf, "func load%s() (*%s, error) {\n", capitalize(shaderName), shaderName)
fmt.Fprint(buf, "\tvar vs, fs, program uint32\n")
var part string
part = strings.Replace(compilePart, "SHADER_SRC", vsName, -1)
part = strings.Replace(part, "SHADER_TYPE", "VERTEX_SHADER", -1)
part = strings.Replace(part, "SHADER_VAR", "vs", -1)
fmt.Fprint(buf, part)
part = strings.Replace(compilePart, "SHADER_SRC", fsName, -1)
part = strings.Replace(part, "SHADER_TYPE", "FRAGMENT_SHADER", -1)
part = strings.Replace(part, "SHADER_VAR", "fs", -1)
fmt.Fprint(buf, part)
fmt.Fprint(buf, linkPart)
fmt.Fprint(buf, "\n")
fmt.Fprintf(buf, "\tresult := &%s{}\n", shaderName)
fmt.Fprint(buf, "\tresult.id = program\n")
for _, input := range inputs {
if input.IsAttrib {
fmt.Fprintf(buf, "\tresult.%s = uint32(gli.GetAttribLocation(program, gli.Str(\"%s\\x00\")))\n", input.Name, input.Name)
} else if input.IsUniform {
fmt.Fprintf(buf, "\tresult.%s = gli.GetUniformLocation(program, gli.Str(\"%s\\x00\"))\n", input.Name, input.Name)
}
}
fmt.Fprint(buf, "\n")
fmt.Fprint(buf, "\treturn result, nil\n")
fmt.Fprint(buf, "}\n")
}

63
opengl.go Normal file
View file

@ -0,0 +1,63 @@
package canvas
import (
"fmt"
)
var gli GL
//go:generate go run make_shaders.go
//go:generate go fmt
func glError() error {
glErr := gli.GetError()
if glErr != gl_NO_ERROR {
return fmt.Errorf("GL Error: %x", glErr)
}
return nil
}
var (
buf uint32
sr *solidShader
)
func LoadGL(glimpl GL) (err error) {
gli = glimpl
gli.GetError() // clear error state
sr, err = loadSolidShader()
if err != nil {
return
}
err = glError()
if err != nil {
return
}
gli.GenBuffers(1, &buf)
err = glError()
if err != nil {
return
}
gli.Enable(gl_BLEND)
gli.BlendFunc(gl_SRC_ALPHA, gl_ONE_MINUS_SRC_ALPHA)
return
}
var solidVS = `
attribute vec2 vertex;
void main() {
gl_Position = vec4(vertex, 0.0, 1.0);
}`
var solidFS = `
#ifdef GL_ES
precision mediump float;
#endif
uniform vec4 color;
void main() {
gl_FragColor = color;
}`

1114
openglinterface.go Normal file

File diff suppressed because it is too large Load diff