first version
This commit is contained in:
commit
4be47cd5ab
6 changed files with 1998 additions and 0 deletions
184
canvas.go
Normal file
184
canvas.go
Normal 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
215
color.go
Normal 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
104
made_shaders.go
Executable 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
318
make_shaders.go
Normal 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
63
opengl.go
Normal 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
1114
openglinterface.go
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue