This commit is contained in:
Araq 2013-07-24 23:07:28 +02:00
commit 9acdf94cc0
14 changed files with 663 additions and 1 deletions

View file

@ -0,0 +1,57 @@
import
tri_engine/config,
tri_engine/math/vec
from strutils import
formatFloat,
TFloatFormat,
`%`
from unsigned import
`shr`,
`and`
type
TColor* = tuple[r, g, b, a: TR]
converter toColor*(o: uint32): TColor =
## Convert an integer to a color. This is mostly useful when the integer is specified as a hex
## literal such as 0xFF00007F, which is 100% red, with 50% alpha.
## TODO: turn this into a template that can take either 4 or 8 characters?
((((o and 0xff000000'u32) shr 24).TR / 255.0).TR,
(((o and 0xff0000'u32) shr 16).TR / 255.0).TR,
(((o and 0xff00'u32) shr 8).TR / 255.0).TR,
(((o and 0xff'u32)).TR / 255.0).TR)
converter toV4*(o: TColor): TV4[TR] =
cast[TV4[TR]](o)
proc newColor*(r, g, b: TR=0.0, a: TR=1.0): TColor =
(r, g, b, a)
proc white*(rgb, a: TR=1.0): TColor =
(rgb, rgb, rgb, a)
proc red*(r, a: TR=1.0): TColor =
newColor(r=r, a=a)
proc green*(g, a: TR=1.0): TColor =
newColor(g=g, a=a)
proc yellow*(rg, a: TR=1.0): TColor =
newColor(r=rg, g=rg, a=a)
proc blue*(b, a: TR=1.0): TColor =
newColor(b=b, a=a)
proc cyan*(gb, a: TR=1.0): TColor =
newColor(g=gb, b=gb, a=a)
proc purple*(rb, a: TR=1.0): TColor =
newColor(r=rb, b=rb, a=a)
proc `$`*(o: TColor): string =
proc f(f: float): string =
f.formatFloat(precision=2, format=ffDecimal)
"(r: $#, g: $#, b: $#, s: $#)" % [f(o.r), f(o.g), f(o.b), f(o.a)]

View file

@ -0,0 +1,61 @@
import
opengl,
tri_engine/math/vec
export
opengl
type
EGL* = object of E_Base
EGL_code* = object of EGL
code*: EGL_err
EGL_err {.pure.} = enum
none = GL_NO_ERROR
invalidEnum = GL_INVALID_ENUM
invalidVal = GL_INVALID_VALUE
invalidOp = GL_INVALID_OPERATION
stackOverflow = GL_STACK_OVERFLOW
stackUnderflow = GL_STACK_UNDERFLOW
outOfMem = GL_OUT_OF_MEMORY
invalidFramebufferOp = GL_INVALID_FRAMEBUFFER_OPERATION
unknown
proc newGL_codeException*(msg: string, code: EGL_err): ref EGL_code =
result = newException(EGL_code, $code)
result.code = code
proc getErr*(): EGL_err =
result = glGetError().EGL_err
if result notin {EGL_err.none,
EGL_err.invalidEnum,
EGL_err.invalidVal,
EGL_err.invalidOp,
EGL_err.invalidFramebufferOp,
EGL_err.outOfMem,
EGL_err.stackUnderflow,
EGL_err.stackOverflow}:
return EGL_err.unknown
proc errCheck*() =
let err = getErr()
if err != EGL_err.none:
raise newGL_codeException($err, err)
macro `?`*(call: expr{nkCall}): expr =
result = call
# Can't yet reference foreign symbols in macros.
#errCheck()
when defined(doublePrecision):
const
glRealType* = cGLdouble
else:
const
glRealType* = cGLfloat
proc setUniformV4*[T](loc: GLint, vecs: var openarray[TV4[T]]) =
glUniform4fv(loc, vecs.len.GLsizei, cast[PGLfloat](vecs[0].addr))
proc setUniformV4*[T](loc: GLint, vec: TV4[T]) =
var vecs = [vec]
setUniformV4(loc, vecs)

View file

@ -0,0 +1,157 @@
import
math,
tri_engine/config,
tri_engine/gfx/gl/gl,
tri_engine/gfx/tex,
tri_engine/gfx/color,
tri_engine/math/vec,
tri_engine/math/rect,
tri_engine/math/circle
import strutils
type
TVert* = tuple[pos: TV2[TR], texCoord: TV2[TR]]
TVertAttrib* = object
i* : GLuint
size* : GLint
stride* : GLsizei
offset* : PGLvoid
TVertMode* = enum
vmTriStrip = GLtriangleStrip,
vmTriFan = GLtriangleFan
TZ_range* = range[-100_000..100_000]
PPrimitive* = ref object
verts* : seq[TVert]
indices* : seq[GLushort]
arrBufId* : GLuint
elemArrBufId* : GLuint
tex* : TTex
color* : TColor
vertMode* : TVertMode
z* : int
proc newVert*(pos, texCoord: TV2): TVert =
(pos, texCoord)
proc newVertQuad*(min, minRight, maxLeft, max: TV2[TR]): seq[TVert] =
@[newVert(min, newV2()),
newVert(minRight, newV2(x=1.0)),
newVert(maxLeft, newV2(y=1.0)),
newVert(max, newV2xy(1.0))
]
proc newVert*(rect: rect.TRect): seq[TVert] =
newVertQuad(rect.min, newV2(rect.max.x, rect.min.y), newV2(rect.min.x, rect.max.y), rect.max)
proc newVertAttrib(i: GLuint, size: GLint, stride: GLsizei, offset: PGLvoid): TVertAttrib =
TVertAttrib(i: i, size: size, stride: stride, offset: offset)
proc genBuf*[T](vboTarget, objUsage: GLenum, data: var openarray[T]): GLuint =
result = 0.GLuint
?glGenBuffers(1, result.addr)
?glBindBuffer(vboTarget, result)
let size = (data.len * T.sizeof).GLsizeiptr
?glBufferData(vboTarget, size, data[0].addr, objUsage)
proc newPrimitive*(verts: var seq[TVert],
vertMode=vmTriStrip,
tex=whiteTex(),
color=white(),
z: TZ_range=0): PPrimitive =
var indices = newSeq[GLushort](verts.len)
for i in 0 .. <verts.len:
indices[i] = i.GLushort
new(result)
result.verts = verts
result.indices = indices
result.arrBufId = genBuf(GLarrayBuffer, GL_STATIC_DRAW, verts)
result.elemArrBufId = genBuf(GLelementArrayBuffer, GL_STATIC_DRAW, indices)
result.tex = tex
result.color = color
result.vertMode = vertMode
result.z = z
proc bindBufs*(o: PPrimitive) =
?glBindBuffer(GLarrayBuffer, o.arrBufId)
?glBindBuffer(GLelementArrayBuffer, o.elemArrBufId)
proc enableVertAttribArrs*() =
?glEnableVertexAttribArray(0)
?glEnableVertexAttribArray(1)
proc disableVertAttribArrs*() =
?glDisableVertexAttribArray(1)
?glDisableVertexAttribArray(0)
proc setVertAttribPointers*() =
let vertSize {.global.} = TVert.sizeof.GLint
?glVertexAttribPointer(0, 2, glRealType, false, vertSize, nil)
?glVertexAttribPointer(1, 2, glRealType, false, vertSize, cast[PGLvoid](TR.sizeof * 2))
proc updVerts*(o: PPrimitive, start, `end`: int, f: proc(i: int, vert: var TVert)) =
assert start <= `end`
assert `end` < o.verts.len
for i in start..`end`:
f(i, o.verts[i])
?glBindBuffer(GLarrayBuffer, o.arrBufId)
let byteLen = `end` - start + 1 * TVert.sizeof
let byteOffset = start * TVert.sizeof
?glBufferSubData(GLarrayBuffer,
byteOffset.GLintptr, # Offset. Is this right?
byteLen.GLsizeiptr, # Size.
cast[PGLvoid](cast[int](o.verts[0].addr) + byteOffset))
proc updAllVerts(o: PPrimitive, f: proc(i: int, vert: var TVert)) =
for i in 0 .. <o.verts.len:
f(i, o.verts[i])
?glBindBuffer(GLarrayBuffer, o.arrBufId)
# Discard old buffer before creating a new one.
let byteLen = (o.verts.len * TVert.sizeof).GLsizeiptr
?glBufferData(GLarrayBuffer, byteLen, nil, GLstaticDraw)
?glBufferData(GLarrayBuffer, byteLen, o.verts[0].addr, GLstaticDraw)
proc newVertCircle*(circle: TCircle, nSegs: Natural=0): seq[TVert] =
let nSegs = if nSegs == 0:
(circle.r.sqrt() * 400.0).int # TODO: Base this on the window resolution?
else:
max(nSegs, 3)
let theta: TR = (PI * 2.0) / (nSegs.TR)
let tanFactor = theta.tan()
let radialFactor = theta.cos()
var x = circle.r
var y: TR = 0.0
result = newSeq[TVert](nSegs)
#result[0] = newVert(circle.p, newV2xy(0.5))
for i in 1 .. <nSegs:
let pos = newV2(x + circle.p.x, y + circle.p.y)
let texCoord = pos * newV2xy(1.0 / circle.r)
result[i] = newVert(pos, texCoord)
let tx = -y
let ty = x
x += tx * tanFactor
y += ty * tanFactor
x *= radialFactor
y *= radialFactor
result.add(result[1])
proc newPrimitiveCircle*(circle: TCircle,
nSegs: Natural=0,
tex=whiteTex(),
color=white(),
z: TZ_range=0): PPrimitive =
var verts = newVertCircle(circle, nSegs)
newPrimitive(verts, vmTriFan, tex, color, z)

View file

@ -0,0 +1,103 @@
import
pure/os,
tri_engine/gfx/gl/gl
type
TShader* = object
id*: GL_handle
TShaderType* {.pure.} = enum
frag = GL_FRAGMENT_SHADER,
vert = GL_VERTEX_SHADER
E_Shader* = object of E_Base
E_UnknownShaderType* = object of E_Shader
converter pathToShaderType*(s: string): TShaderType =
case s.splitFile().ext:
of ".vs":
return TShaderType.vert
of ".fs":
return TShaderType.frag
else:
raise newException(E_UnknownShaderType, "Can't determine shader type from file extension: " & s)
proc setSrc*(shader: TShader, src: string) =
var s = src.cstring
?glShaderSource(shader.id, 1, cast[cstringarray](s.addr), nil)
proc newShader*(id: GL_handle): TShader =
if id != 0 and not (?glIsShader(id)).bool:
raise newException(E_GL, "Invalid shader ID: " & $id)
result.id = id
proc shaderInfoLog*(o: TShader): string =
var log {.global.}: array[0..1024, char]
var logLen: GLsizei
?glGetShaderInfoLog(o.id, log.len.GLsizei, logLen, cast[PGLchar](log.addr))
cast[string](log.addr).substr(0, logLen)
proc compile*(shader: TShader, path="") =
?glCompileShader(shader.id)
var compileStatus = 0.GLint
?glGetShaderIv(shader.id, GL_COMPILE_STATUS, compileStatus.addr)
if compileStatus == 0:
raise newException(E_GL, if path.len == 0:
shaderInfoLog(shader)
else:
path & ":\n" & shaderInfoLog(shader)
)
proc newShaderFromSrc*(src: string, `type`: TShaderType): TShader =
result.id = ?glCreateShader(`type`.GLenum)
result.setSrc(src)
result.compile()
proc newShaderFromFile*(path: string): TShader =
newShaderFromSrc(readFile(path), path)
type
TProgram* = object
id*: GL_handle
shaders: seq[TShader]
proc attach*(o: TProgram, shader: TShader) =
?glAttachShader(o.id, shader.id)
proc infoLog*(o: TProgram): string =
var log {.global.}: array[0..1024, char]
var logLen: GLsizei
?glGetProgramInfoLog(o.id, log.len.GLsizei, logLen, cast[PGLchar](log.addr))
cast[string](log.addr).substr(0, logLen)
proc link*(o: TProgram) =
?glLinkProgram(o.id)
var linkStatus = 0.GLint
?glGetProgramIv(o.id, GL_LINK_STATUS, linkStatus.addr)
if linkStatus == 0:
raise newException(E_GL, o.infoLog())
proc validate*(o: TProgram) =
?glValidateProgram(o.id)
var validateStatus = 0.GLint
?glGetProgramIv(o.id, GL_VALIDATE_STATUS, validateStatus.addr)
if validateStatus == 0:
raise newException(E_GL, o.infoLog())
proc newProgram*(shaders: seq[TShader]): TProgram =
result.id = ?glCreateProgram()
if result.id == 0:
return
for shader in shaders:
if shader.id == 0:
return
?result.attach(shader)
result.shaders = shaders
result.link()
result.validate()
proc use*(o: TProgram) =
?glUseProgram(o.id)

View file

@ -0,0 +1,31 @@
import
tri_engine/gfx/gl/gl
type
TTex* = object
id*: GLuint
var gWhiteTex = TTex(id: 0)
proc setTexParams() =
?glTexParameteri(GLtexture2D, GLtextureMinFilter, GLlinear)
#glTexParameteri(GLtexture2D, GLtextureMagFilter, GLlinear)
?glTexParameteri(GLtexture2D, GLTextureMagFilter, GLnearest)
?glTexParameteri(GLtexture2D, GLTextureWrapS, GLClampToEdge)
?glTexParameteri(GLtexture2D, GLTextureWrapT, GLClampToEdge)
proc whiteTex*(): TTex =
if gWhiteTex.id.int != 0:
return gWhiteTex
?glGenTextures(1, gWhiteTex.id.addr)
?glBindTexture(GLtexture2D, gWhiteTex.id)
setTexParams()
var pixel = [255'u8, 255'u8, 255'u8, 255'u8]
?glTexImage2D(GLtexture2D, 0, GL_RGBA, 1, 1, 0, GL_BGRA, cGLUnsignedByte, pixel[0].addr)
?glBindTexture(GLtexture2D, 0)
result = gWhiteTex