Skip to content

Instantly share code, notes, and snippets.

@TsurHerman
Last active March 14, 2017 11:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TsurHerman/a2126a640388c3456e9672635e9067ef to your computer and use it in GitHub Desktop.
Save TsurHerman/a2126a640388c3456e9672635e9067ef to your computer and use it in GitHub Desktop.
using ModernGL
function gen_buffer(f,n::Integer)
res = Array{GLuint}(n)
f(n,res)
if any(res .<= 0)
error("invalid id. OpenGL Context active?")
end
return res
end
function glGenVertexArrays(n::Integer)
f = ModernGL.glGenVertexArrays
n == 1 ? begin res = gen_buffer(f,1);res = res[1] end : gen_buffer(f,n) ;
end
function glGenBuffers(n::Integer)
f = ModernGL.glGenBuffers
n == 1 ? begin res = gen_buffer(f,1);res = res[1] end : gen_buffer(f,n) ;
end
###########################shaders.jl
#=
module glUtils
=#
function getinfolog(obj::GLuint)
# Return the info log for obj, whether it be a shader or a program.
isShader = glIsShader(obj)
getiv = isShader == GL_TRUE ? glGetShaderiv : glGetProgramiv
get_log = isShader == GL_TRUE ? glGetShaderInfoLog : glGetProgramInfoLog
# Get the maximum possible length for the descriptive error message
maxlength = GLint[0]
getiv(obj, GL_INFO_LOG_LENGTH, maxlength)
maxlength = first(maxlength)
# Return the text of the message if there is any
if maxlength > 0
buffer = zeros(GLchar, maxlength+1)
sizei = GLsizei[0]
get_log(obj, maxlength, sizei, buffer)
length = first(sizei)
return false,String(buffer)
else
return true,"success"
end
end
function iscompiled(shader::GLuint)
success = GLint[0]
glGetShaderiv(shader, GL_COMPILE_STATUS, success)
return first(success) == GL_TRUE
end
function islinked(program::GLuint)
success = GLint[0]
glGetProgramiv(program, GL_LINK_STATUS, success)
return first(success) == GL_TRUE
end
" loadshaders(path, shadername_type:Array{Tuple{String,GLenum},1})"
function loadshaders(path::String, S::Array{Tuple{String,GLenum},1})
prog_id = glCreateProgram();
shader_id = Vector{GLenum}()
for s in S
sid = compile(joinpath(path,s[1]),s[2])
glAttachShader(prog_id, sid)
push!(shader_id,sid)
end
print("linking...")
glLinkProgram(prog_id)
info_success,msg = getinfolog(prog_id)
success = islinked(prog_id)
success ? print("$(msg)\n") : error("[SHADER ERROR]$(msg)")
for sid in shader_id
glDetachShader(prog_id,sid)
glDeleteShader(sid)
end
glUseProgram(prog_id)
return prog_id
end
"""
compile(fname,SHADER_TYPE)
"""
function compile(fname::String,SHADER_TYPE::GLuint)
source = open(fname) do f
readstring(f)
end
shaderid = glCreateShader(SHADER_TYPE)
print("Compiling shader $(fname)...")
glShaderSource(shaderid, 1, [source,] , C_NULL)
glCompileShader(shaderid)
info_success,msg = getinfolog(shaderid)
success = iscompiled(shaderid)
success ? print("$(msg)\n") :
begin
print("\n $(msg)");
error("[$(GLENUM(SHADER_TYPE).name) ERROR!]")
end
return shaderid
end
#####################################
###GLProgram###############################################
abstract glBuffer
type ElementsArrayBuffer <: glBuffer
id::GLuint
verticesize::Integer
nvertices::Integer
eltype::GLenum
ElementsArrayBuffer() = new(glGenBuffers(1),0,0,GL_UNSIGNED_INT)
ElementsArrayBuffer{T<:Integer}(data::Array{T,2}) = begin
VA = ElementsArrayBuffer();
update!(VA,data)
VA
end
end
export ElementsArrayBuffer
type VertexAttrBuffer <: glBuffer
id::GLuint
verticesize::Integer
nvertices::Integer
eltype::GLenum
VertexAttrBuffer() = new(glGenBuffers(1),0,0,GL_FLOAT)
VertexAttrBuffer{T<:Real}(data::Array{T,2}) = begin
VA = VertexAttrBuffer();
update!(VA,data)
VA
end
end
export VertexAttrBuffer
function update!{T<:Real}(va::glBuffer , data::Array{T,2})
isa(va,VertexAttrBuffer) && (data = convert(Array{Float32},data))
isa(va,ElementsArrayBuffer) && (data = convert(Array{UInt32},data))
BufType = isa(va,ElementsArrayBuffer) ? GL_ELEMENT_ARRAY_BUFFER :GL_ARRAY_BUFFER
#TODO insert a more elaborate error msg
if !(va.verticesize == 0 || va.verticesize == size(data,1))
error("updateing a binded buffer - size mismatch")
end
va.nvertices = size(data,2)
va.verticesize = size(data,1)
assert(va.verticesize <= 4)
glBindBuffer(BufType, va.id)
glBufferData(BufType, sizeof(data), data, GL_STATIC_DRAW)
end
export update!
type ShaderInput
inputtype::Symbol
location::Int32
size::Int32
GLtype::GLenum
GLtype_name::Symbol
jeltype::Type
_bindedto
ShaderInput(int,loc,sz,glt) =
new(int,loc,sz,glt,GLENUM(glt).name,gl2jeltype(glt),nothing)
end
function activeinputs(prog::Integer)
inputs = Dict{String,ShaderInput}()
buf = zeros(GLchar,81)
nchar = GLsizei[0]
sz = GLint[0]
GLtype = GLenum[0]
enumerate(gl_f,enum,loc,sym) =
begin
num_inputs = Int32[0]
glGetProgramiv(prog,enum,num_inputs)
num_inputs = first(num_inputs)
for i = 0:(num_inputs-1)
gl_f(prog,i,80,nchar,sz,GLtype,buf)
name = String(buf[1:nchar[1]])
m = match(r"^([^[]*)",name)
name = m.captures[1]
#print("$(name) $(sym) $(sz[1])\n")
inputs[name] = ShaderInput(sym,loc(prog,name),sz[1],GLtype[1])
end
end
enumerate(glGetActiveAttrib,GL_ACTIVE_ATTRIBUTES,glGetAttribLocation,:VertexAttr)
enumerate(glGetActiveUniform,GL_ACTIVE_UNIFORMS,glGetUniformLocation,:Uniform)
inputs
end
type GLprogram
id::GLuint
indices::ElementsArrayBuffer
inputs::Dict{String,ShaderInput}
vertices::Int32
GLprogram(path::String, S::Array{Tuple{String,GLenum},1}) =
begin
id = loadshaders(path,S)
glUseProgram(id)
inputs = activeinputs(id)
new(id,ElementsArrayBuffer(),inputs,0)
end
end
export GLprogram
function setvertex_attr(prog::GLprogram,location::Integer,va_array::VertexAttrBuffer)
glUseProgram(prog.id);
glBindBuffer(GL_ARRAY_BUFFER, va_array.id);
glVertexAttribPointer(location, va_array.verticesize ,GL_FLOAT,GL_FALSE,0,C_NULL)
glEnableVertexAttribArray(location)
end
global lastused = -1
function use(P::GLprogram,force::Bool = true)
(lastused == P.id && !force) && return;
glUseProgram(P.id);
lastUsedProg = P.id
I = P.inputs;
skip_names = ["gl_InstanceID"]
foreach(I[key] for key in keys(I) if I[key].inputtype == :VertexAttr && !any(key .== skip_names) ) do I
for i=1:I.size
setvertex_attr(P,I.location+i-1,I._bindedto[i]);
end
end
end
export use
function gl2jeltype(typ::GLenum)
FLoatTypes = (GL_FLOAT,GL_FLOAT_VEC2,GL_FLOAT_VEC3,GL_FLOAT_VEC4,
GL_FLOAT_MAT2,GL_FLOAT_MAT3,GL_FLOAT_MAT4,GL_FLOAT_MAT3x2,
GL_FLOAT_MAT2x3 ,GL_FLOAT_MAT4x3 ,GL_FLOAT_MAT3x4);
IntTypes = (GL_INT,GL_INT_VEC2,GL_INT_VEC3,GL_INT_VEC4);
if any([FLoatTypes...] .== typ) return Float32 end
if any([IntTypes...] .== typ) return Int32 end
error("[gl2jtype UNMATCHED UNIFORM type please fix!]")
end
using StaticArrays
function setuniform(prog::GLprogram,name::String,offset::Integer,value)
value = isa(value,Array) && any([size(value)...] .== 1) ? (value...) : value
jeltype = prog.inputs[name].jeltype;
location = prog.inputs[name].location + offset
suffix = jeltype == Float32 ? "f" : jeltype == Int32 ? "i" : jeltype == UInt32 ? "ui" :"ERROR";
if isa(value,Real) #single value
mvalue = convert(jeltype,value)
glfunc = eval(Symbol("glProgramUniform1",suffix))
glfunc(prog.id,location,mvalue)
elseif isa(value,Tuple)
mvalue = convert(NTuple{length(value),jeltype},value)
glfunc = eval(Symbol("glProgramUniform",length(mvalue),suffix))
glfunc(prog.id,location,mvalue...)
elseif isa(value,Matrix)
mvalue = convert(Array{Float32,2},value)
(M,N) = size(mvalue)
mat_suffix = M == N ? M : string(N,'x',M)
glfunc = eval(Symbol("glProgramUniformMatrix",mat_suffix,"fv"))
glfunc(prog.id,location,1,false,mvalue)
elseif isa(value,StaticArray)
if eltype(value) != Float32
value = SMatrix{size(value)...,Float32}(value)
end
(M,N) = size(value)
mat_suffix = M == N ? M : string(N,'x',M)
glfunc = eval(Symbol("glProgramUniformMatrix",mat_suffix,"fv"))
glfunc(prog.id,location,1,false,pointer_from_objref(value))
end
end
import Base.setindex!
function setindex!(prog::GLprogram,value,param::String,offset=0)
param == "_indices" && (return set_indices(prog,value))
if !haskey(prog.inputs,param)
warn("[GLProgram] *$(param)* does not exist -- maybe it was optimized out?")
return false
end
# if isa(value,Vector)
# for i=0:length(Vector)-1
# setindex!(prog,value[i],(param,offset+i))
# end
# end
#from here we have a single value lets do some conversions and asserts
si = prog.inputs[param]
if offset >= si.size
error("[GLprogram] **$(param)* size exceeded ")
end
if si.inputtype == :Uniform
setuniform(prog,param,offset,value)
elseif si.inputtype == :VertexAttr
if si._bindedto == nothing
si._bindedto = Vector{VertexAttrBuffer}(si.size)
end
if isa(value,VertexAttrBuffer)
#setvertex_attr(prog,si.location+offset,value)
si._bindedto[offset+1] = value
prog.vertices = value.nvertices
elseif isa(value,Matrix)
prog.vertices = size(value,2);
if isdefined(si._bindedto,offset+1) #update binded buffer
update!(si._bindedto[offset+1] , value)
else
va = VertexAttrBuffer(value)
#setvertex_attr(prog,si.location+offset,va)
si._bindedto[offset+1] = va
end
else
error("[GLprogram] unexpected input");
end
else
error("[GLProgram] unexpected input type")
end
end
function set_indices(prog,value::Matrix)
update!(prog.indices,value)
end
function set_indices(prog,value::ElementsArrayBuffer)
glDeleteBuffers(1,[prog.indices])
prog.indices = value
end
function draw(prog::GLprogram,primitive::GLenum = GL_TRIANGLES,start::Integer = 0,_end::Integer = -1)
use(prog)
(_end==-1) && (_end = prog.vertices)
glDrawArrays(primitive, start, _end);
end
export draw
function drawindexed(prog::GLprogram; start::Integer = 0,_end::Integer = -1, instances = 1)
use(prog)
(_end==-1) && (_end = prog.indices.nvertices)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, prog.indices.id);
nv = prog.indices.nvertices
vsz = prog.indices.verticesize
primitive = [GL_POINTS GL_LINES GL_TRIANGLES]
glDrawElementsInstancedEXT(primitive[vsz] , vsz * _end, prog.indices.eltype ,
Ptr{Void}(vsz*start*sizeof(prog.indices.eltype)),instances)
end
export drawindexed
function setvar(progs,varname,data)
foreach(progs) do prog
prog[varname] = data
end
end
function setvar(progs,varname::Tuple,data)
foreach(progs) do prog
prog[varname...] = data
end
end
function setvar(prog::GLprogram,varname,data)
prog[varname] = data
end
export setvar
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment