├── .gitignore ├── src ├── GLError.jl ├── shader.jl ├── GLUtils.jl ├── Logging.jl ├── GLRender.jl ├── GLBuffer.jl ├── GLInit.jl ├── GLAbstraction.jl ├── GLRenderObject.jl ├── GLShapes.jl ├── GLMatrixMath.jl ├── GLExtendedFunctions.jl ├── GLShader.jl ├── GLTypes.jl ├── GLCamera.jl ├── GLUniforms.jl └── GLTexture.jl ├── REQUIRE ├── test ├── texture1.jl ├── uniforms.jl ├── texture.jl ├── runtests.jl ├── accessors.jl └── mesh_test.jl ├── .travis.yml ├── example └── simple_triangle.jl ├── LICENSE.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/GLError.jl: -------------------------------------------------------------------------------- 1 | immutable GLInvalidContext <: Exception 2 | msg::String 3 | end -------------------------------------------------------------------------------- /src/shader.jl: -------------------------------------------------------------------------------- 1 | begin 2 | local const SHADER_DICT = @compat(Dict{ASCIIString, GLProgram}()) 3 | function createshader() 4 | end 5 | end -------------------------------------------------------------------------------- /REQUIRE: -------------------------------------------------------------------------------- 1 | julia 0.3- 2 | ModernGL 3 | ImmutableArrays 4 | Images 5 | Reactive 6 | Mustache 7 | Quaternions 8 | Color 9 | FixedPointNumbers 10 | Lumberjack 11 | Compat -------------------------------------------------------------------------------- /test/texture1.jl: -------------------------------------------------------------------------------- 1 | using GLAbstraction, GLPlot 2 | 3 | createdisplay(async=true) 4 | 5 | obj = glplot(rand(Float32, 20,20), color=Vec4(1,0,0,1), primitive=CUBE()) 6 | 7 | gpuz = obj[:z] 8 | 9 | gpuz[1,1] = float32(2) 10 | 11 | obj[:postrender, renderinstanced] = (obj.vertexarray, 3) -------------------------------------------------------------------------------- /test/uniforms.jl: -------------------------------------------------------------------------------- 1 | a = vec4(0) 2 | b = vec2(2) 3 | c = vec4(b..., 1,2) 4 | d = ivec4(b..., 1,2) 5 | d = uivec4(b..., 1,2) 6 | 7 | 8 | m = Matrix4x3(9f0) 9 | m2 = mat3(9f0) 10 | m2 = mat3(vec3(0), vec3(0), vec3(9)) 11 | 12 | 13 | gluniform(int32(1), a) 14 | gluniform(int32(1), vec4[a,a]) 15 | gluniform(int32(1), m) 16 | gluniform(int32(1), mat4x3[m, m]) -------------------------------------------------------------------------------- /src/GLUtils.jl: -------------------------------------------------------------------------------- 1 | macro gputime(codeblock) 2 | quote 3 | local const query = GLuint[1] 4 | local const elapsed_time = GLuint64[1] 5 | local const done = GLint[0] 6 | glGenQueries(1, query) 7 | glBeginQuery(GL_TIME_ELAPSED, query[1]) 8 | value = $(esc(codeblock)) 9 | glEndQuery(GL_TIME_ELAPSED) 10 | 11 | while (done[1] != 1) 12 | glGetQueryObjectiv(query[1], 13 | GL_QUERY_RESULT_AVAILABLE, 14 | done) 15 | end 16 | glGetQueryObjectui64v(query[1], GL_QUERY_RESULT, elapsed_time) 17 | println("Time Elapsed: ", elapsed_time[1] / 1000000.0, "ms") 18 | end 19 | end -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - clang 4 | notifications: 5 | email: false 6 | env: 7 | matrix: 8 | - JULIAVERSION="juliareleases" 9 | - JULIAVERSION="julianightlies" 10 | before_install: 11 | - sudo add-apt-repository ppa:staticfloat/julia-deps -y 12 | - sudo add-apt-repository ppa:staticfloat/${JULIAVERSION} -y 13 | - sudo apt-get update -qq -y 14 | - sudo apt-get install libpcre3-dev julia -y 15 | - sudo apt-get install libXxf86vm-dev # GLFW dependencies 16 | - "export DISPLAY=:99.0" 17 | - "sh -e /etc/init.d/xvfb start" 18 | - if [[ -a .git/shallow ]]; then git fetch --unshallow; fiscript: 19 | - julia -e 'Pkg.init(); Pkg.clone(pwd()); Pkg.test("GLAbstraction")' 20 | -------------------------------------------------------------------------------- /example/simple_triangle.jl: -------------------------------------------------------------------------------- 1 | using ModernGL, GLWindow, GLAbstraction, GLFW 2 | 3 | 4 | const window = createwindow("Example", 512, 512) 5 | 6 | 7 | const vsh = """ 8 | #version 130 9 | in vec2 position; 10 | 11 | void main() { 12 | gl_Position = vec4(position, 0.0, 1.0); 13 | } 14 | """ 15 | 16 | const fsh = """ 17 | #version 130 18 | out vec4 outColor; 19 | 20 | void main() { 21 | outColor = vec4(1.0, 1.0, 1.0, 1.0); 22 | } 23 | """ 24 | 25 | const triangle = RenderObject( 26 | [:position => GLBuffer(GLfloat[0.0, 0.5, 0.5, -0.5, -0.5,-0.5], 2)], 27 | GLProgram(vsh, fsh, "vert", "frag")) 28 | postrender!(triangle, render, triangle.vertexarray) 29 | 30 | glClearColor(0, 0, 0, 1) 31 | 32 | 33 | while !GLFW.WindowShouldClose(window.nativewindow) 34 | 35 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 36 | 37 | render(triangle) 38 | 39 | GLFW.SwapBuffers(window.nativewindow) 40 | GLFW.PollEvents() 41 | end 42 | GLFW.Terminate() 43 | -------------------------------------------------------------------------------- /src/Logging.jl: -------------------------------------------------------------------------------- 1 | function openglerrorcallback( 2 | source::GLenum, typ::GLenum, 3 | id::GLuint, severity::GLenum, 4 | length::GLsizei, message::Ptr{GLchar}, 5 | userParam::Ptr{Void} 6 | ) 7 | errormessage = "\n"* 8 | " ________________________________________________________________\n"* 9 | "|\n"* 10 | "| OpenGL Error!\n"* 11 | "| source: $(GLENUM(source).name) :: type: $(GLENUM(typ).name)\n"* 12 | "| "*ascii(bytestring(message, length))*"\n"* 13 | "|________________________________________________________________\n" 14 | 15 | if typ == GL_DEBUG_TYPE_ERROR 16 | println(errormessage) 17 | else 18 | error(errormessage) 19 | end 20 | nothing 21 | end 22 | 23 | global const _openglerrorcallback = cfunction(openglerrorcallback, Void, 24 | (GLenum, GLenum, 25 | GLuint, GLenum, 26 | GLsizei, Ptr{GLchar}, 27 | Ptr{Void})) -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The GLAbstraction.jl package is licensed under the MIT "Expat" License: 2 | 3 | > Copyright (c) 2014: Simon Danisch. 4 | > 5 | > Permission is hereby granted, free of charge, to any person obtaining 6 | > a copy of this software and associated documentation files (the 7 | > "Software"), to deal in the Software without restriction, including 8 | > without limitation the rights to use, copy, modify, merge, publish, 9 | > distribute, sublicense, and/or sell copies of the Software, and to 10 | > permit persons to whom the Software is furnished to do so, subject to 11 | > the following conditions: 12 | > 13 | > The above copyright notice and this permission notice shall be 14 | > included in all copies or substantial portions of the Software. 15 | > 16 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | > IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | > CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | > TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | > SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GLAbstraction 2 | A simple library, which makes the use of OpenGL a little bit more convinient and Julian. 3 | If you have any questions, please open an issue. I'm working lazily on the documentation, so if no one asks, there won't be any documentation ;) 4 | 5 | ### Features 6 | * Some linear algebrae, to do all kinds of transformations. 7 | * Aliases for ImmutableArrays, which are more GLSL alike (e.g. Vector3{Float32} -> Vec3, Matrix4x4{Float32} -> Mat4) 8 | * All the different glUniform functions are wrapped and the right function is determined via multiple dispatch (just works for ImmutableArrays and Real numbers) 9 | * Buffers and Texture objects are wrapped, with best support for arrays of ImmutableArrays. 10 | * Shader loading is simplified and offers templated shaders and interactive editing of shaders and type/error checks. 11 | * Offers the type `RenderOject`, which helps you preparing the OpenGL state to render data with a shader. 12 | * Event handling with [React](https://github.com/shashi/React.jl) 13 | * Two camera types (PerspectiveCamera and OrthogonalCamera), which can be instantiated with a list of React signals from GLWindow. You can also supply your own signals. 14 | * Some wrappers for often used functions, with embedded error handling and more Julian syntax 15 | 16 | 17 | 18 | 19 | ### Example: 20 | ```julia 21 | 22 | 23 | ``` 24 | 25 | 26 | 27 | #Status 28 | There is still quite a bit missing. 29 | -------------------------------------------------------------------------------- /test/texture.jl: -------------------------------------------------------------------------------- 1 | using GLAbstraction, ImmutableArrays, ModernGL, GLWindow, Images, Color 2 | using Base.Test 3 | 4 | global const window = createwindow("Mesh Display", 1000, 1000, debugging = false) 5 | N = 100 6 | 7 | 8 | intensity2Df = Texture([Vec1(0) for i=1:N, j=1:N]) 9 | intensity2Di = Texture([iVec1(0) for i=1:N, j=1:N]) 10 | intensity2Dui = Texture([uVec1(0f0) for i=1:N, j=1:N]) 11 | 12 | rg2Df = Texture([Vec2(0) for i=1:N, j=1:N]) 13 | rg2Di = Texture([iVec2(0) for i=1:N, j=1:N]) 14 | rg2Dui = Texture([uVec2(0f0) for i=1:N, j=1:N]) 15 | 16 | rgb2Df = Texture([Vec3(0) for i=1:N, j=1:N]) 17 | rgb2Di = Texture([iVec3(0) for i=1:N, j=1:N]) 18 | rgb2Dui = Texture([uVec3(0f0) for i=1:N, j=1:N]) 19 | 20 | rgba2Df = Texture([Vec4(0) for i=1:N, j=1:N]) 21 | rgba2Di = Texture([iVec4(0) for i=1:N, j=1:N]) 22 | rgba2Dui = Texture([uVec4(0f0) for i=1:N, j=1:N]) 23 | 24 | 25 | z = Matrix{Vec4}[Vec4[Vec4(0f0) for i=1:N, j=1:N] for i=1:10] 26 | arraytexture = Texture(z) 27 | 28 | 29 | 30 | 31 | 32 | texparams = [ 33 | (GL_TEXTURE_MIN_FILTER, GL_LINEAR), 34 | (GL_TEXTURE_MAG_FILTER, GL_LINEAR), 35 | (GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE), 36 | (GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 37 | ] 38 | 39 | @test toglsltype_string(intensity2Df) == "uniform sampler2D" 40 | @test toglsltype_string(rg2Df) == "uniform sampler2D" 41 | @test toglsltype_string(rgb2Df) == "uniform sampler2D" 42 | @test toglsltype_string(rgba2Df) == "uniform sampler2D" 43 | 44 | 45 | @test typeof(intensity2Df).parameters == (Vec1, 1,2) 46 | @test typeof(rgba2Df).parameters == (Vec4, 4,2) 47 | -------------------------------------------------------------------------------- /src/GLRender.jl: -------------------------------------------------------------------------------- 1 | 2 | function render(list::AbstractVector) 3 | for elem in list 4 | render(elem) 5 | end 6 | end 7 | 8 | function render(renderobject::RenderObject, vertexarray=renderobject.vertexarray) 9 | for elem in renderobject.prerenderfunctions 10 | elem[1](elem[2]...) 11 | end 12 | program = vertexarray.program 13 | glUseProgram(program.id) 14 | for (key,value) in program.uniformloc 15 | haskey(renderobject.uniforms, key) && gluniform(value..., renderobject.uniforms[key]) 16 | end 17 | for elem in renderobject.postrenderfunctions 18 | elem[1](elem[2]...) 19 | end 20 | end 21 | 22 | function render(vao::GLVertexArray, mode::GLenum=GL_TRIANGLES) 23 | glBindVertexArray(vao.id) 24 | if vao.indexlength > 0 25 | glDrawElements(mode, vao.indexlength, GL_UNSIGNED_INT, C_NULL) 26 | else 27 | glDrawArrays(mode, 0, vao.length) 28 | end 29 | end 30 | 31 | function renderinstanced(vao::GLVertexArray, amount::Integer, primitive=GL_TRIANGLES) 32 | glBindVertexArray(vao.id) 33 | #If you get an error here, notify me and try: 34 | #glDrawElementsInstancedEXT(primitive, vao.indexlength, GL_UNSIGNED_INT, C_NULL, amount) 35 | glDrawElementsInstanced(primitive, vao.indexlength, GL_UNSIGNED_INT, C_NULL, amount) 36 | end 37 | 38 | #handle all uniform objects 39 | 40 | 41 | 42 | ############################################################################################## 43 | # Generic render functions 44 | ##### 45 | function enabletransparency() 46 | glEnablei(GL_BLEND, 0) 47 | glDisablei(GL_BLEND, 1) 48 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) 49 | end 50 | 51 | -------------------------------------------------------------------------------- /src/GLBuffer.jl: -------------------------------------------------------------------------------- 1 | cardinality{T, C}(::GLBuffer{T, C}) = C 2 | Base.length(b::GLBuffer) = b.length 3 | #Function to deal with any Immutable type with Real as Subtype 4 | function GLBuffer{T <: Union(AbstractArray, AbstractFixedVector)}( 5 | buffer::Vector{T}; 6 | buffertype::GLenum = GL_ARRAY_BUFFER, usage::GLenum = GL_STATIC_DRAW 7 | ) 8 | #This is a workaround, to deal with all kinds of immutable vector types 9 | ptrtype, cardinality = opengl_compatible(T) 10 | ptr = convert(Ptr{ptrtype}, pointer(buffer)) 11 | GLBuffer{ptrtype, cardinality}(ptr, sizeof(buffer), buffertype, usage) 12 | end 13 | 14 | function GLBuffer{T <: Real}( 15 | buffer::Vector{T}, cardinality::Int; 16 | buffertype::GLenum = GL_ARRAY_BUFFER, usage::GLenum = GL_STATIC_DRAW 17 | ) 18 | GLBuffer{T, cardinality}(convert(Ptr{T}, pointer(buffer)), sizeof(buffer), buffertype, usage) 19 | end 20 | function indexbuffer{T<:Integer}(buffer::Vector{T}; usage::GLenum = GL_STATIC_DRAW) 21 | GLBuffer(buffer, 1, buffertype = GL_ELEMENT_ARRAY_BUFFER, usage=usage) 22 | end 23 | function indexbuffer{T<:Union(AbstractArray, AbstractFixedVector)}(buffer::Vector{T}; usage::GLenum = GL_STATIC_DRAW) 24 | GLBuffer(buffer, buffertype = GL_ELEMENT_ARRAY_BUFFER, usage=usage) 25 | end 26 | 27 | function update!{T}(b::GLBuffer{T,1}, data::Vector{Vector1{T}}) 28 | glBindBuffer(b.buffertype, b.id) 29 | glBufferSubData(b.buffertype, 0, sizeof(data), data) 30 | end 31 | function update!{T}(b::GLBuffer{T,2}, data::Vector{Vector2{T}}) 32 | glBindBuffer(b.buffertype, b.id) 33 | glBufferSubData(b.buffertype, 0, sizeof(data), data) 34 | end 35 | function update!{T}(b::GLBuffer{T,3}, data::Vector{Vector3{T}}) 36 | glBindBuffer(b.buffertype, b.id) 37 | glBufferSubData(b.buffertype, 0, sizeof(data), data) 38 | end 39 | function update!{T}(b::GLBuffer{T,4}, data::Vector{Vector4{T}}) 40 | glBindBuffer(b.buffertype, b.id) 41 | glBufferSubData(b.buffertype, 0, sizeof(data), data) 42 | end 43 | -------------------------------------------------------------------------------- /test/runtests.jl: -------------------------------------------------------------------------------- 1 | using GLAbstraction, ImmutableArrays, ModernGL 2 | using GLFW # <- need GLFW for context initialization.. Hopefully replaced by some native initialization 3 | using Base.Test 4 | 5 | # initilization, with GLWindow this reduces to "createwindow("name", w,h)" 6 | GLFW.Init() 7 | GLFW.WindowHint(GLFW.SAMPLES, 4) 8 | 9 | @osx_only begin 10 | GLFW.WindowHint(GLFW.CONTEXT_VERSION_MAJOR, 3) 11 | GLFW.WindowHint(GLFW.CONTEXT_VERSION_MINOR, 3) 12 | GLFW.WindowHint(GLFW.OPENGL_FORWARD_COMPAT, GL_TRUE) 13 | GLFW.WindowHint(GLFW.OPENGL_PROFILE, GLFW.OPENGL_CORE_PROFILE) 14 | end 15 | window = GLFW.CreateWindow(512,512, "test") 16 | GLFW.MakeContextCurrent(window) 17 | GLFW.ShowWindow(window) 18 | 19 | init_glutils() 20 | 21 | #vertex and fragment shader 22 | vsh = " 23 | {{GLSL_VERSION}} 24 | 25 | in vec2 vertex; 26 | 27 | void main() { 28 | gl_Position = vec4(vertex, 0.0, 1.0); 29 | } 30 | " 31 | 32 | fsh = " 33 | {{GLSL_VERSION}} 34 | 35 | out vec4 frag_color; 36 | 37 | void main() { 38 | frag_color = vec4(1.0, 0.0, 1.0, 1.0); 39 | } 40 | " 41 | 42 | # Test for creating a GLBuffer with a 1D Julia Array 43 | # You need to supply the cardinality, as it can't be inferred 44 | # indexbuffer is a shortcut for GLBuffer(GLUint[0,1,2,2,3,0], 1, buffertype = GL_ELEMENT_ARRAY_BUFFER) 45 | indexes = indexbuffer(GLuint[0,1,2]) 46 | # Test for creating a GLBuffer with a 1D Julia Array of Vectors 47 | #v = Vec2f[Vec2f(0.0, 0.5), Vec2f(0.5, -0.5), Vec2f(-0.5,-0.5)] 48 | 49 | v = Float32[0.0, 0.5, 0.5, -0.5, -0.5,-0.5] 50 | verts = GLBuffer(v, 2) 51 | 52 | # lets define some uniforms 53 | # uniforms are shader variables, which are supposed to stay the same for an entire draw call 54 | 55 | 56 | const triangle = RenderObject( 57 | [ 58 | :vertex => verts, 59 | :name_doesnt_matter_for_indexes => indexes 60 | ], 61 | TemplateProgram(vsh, fsh, "vertex", "fragment")) 62 | 63 | postrender!(triangle, render, triangle.vertexarray) 64 | 65 | #require("uniforms") 66 | 67 | glClearColor(0,0,0,1) 68 | for i=1:100 69 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 70 | render(triangle) 71 | GLFW.SwapBuffers(window) 72 | GLFW.PollEvents() 73 | sleep(0.01) 74 | end 75 | GLFW.Terminate() 76 | -------------------------------------------------------------------------------- /src/GLInit.jl: -------------------------------------------------------------------------------- 1 | const INIT_FUNCTION_LIST = Function[] 2 | 3 | 4 | 5 | function init_after_context_creation(f::Function) 6 | push!(INIT_FUNCTION_LIST, f) 7 | end 8 | 9 | function init_glutils() 10 | createcontextinfo(OPENGL_CONTEXT) 11 | for elem in INIT_FUNCTION_LIST 12 | elem() 13 | end 14 | end 15 | 16 | 17 | global const OPENGL_CONTEXT = Dict{Symbol, Any}() 18 | global GLSL_VERSION = "" 19 | global GLSL_VARYING_QUALIFIER = "" 20 | 21 | function createcontextinfo(dict) 22 | global GLSL_VERSION, GLSL_VARYING_QUALIFIER 23 | glsl = split(bytestring(glGetString(GL_SHADING_LANGUAGE_VERSION)), ['.', ' ']) 24 | if length(glsl) >= 2 25 | glsl = VersionNumber(int(glsl[1]), int(glsl[2])) 26 | if glsl.major == 1 && glsl.minor <= 2 27 | error("OpenGL shading Language version too low. Try updating graphic driver!") 28 | end 29 | GLSL_VERSION = string(glsl.major) * rpad(string(glsl.minor),2,"0") 30 | if glsl.major >= 1 31 | GLSL_VARYING_QUALIFIER = "in" 32 | else 33 | GLSL_VARYING_QUALIFIER = "varying" 34 | end 35 | else 36 | error("Unexpected version number string. Please report this bug! Version string: $(glsl)") 37 | end 38 | 39 | glv = split(bytestring(glGetString(GL_VERSION)), ['.', ' ']) 40 | if length(glv) >= 2 41 | glv = VersionNumber(int(glv[1]), int(glv[2])) 42 | if glv.major < 3 43 | error("OpenGL version too low. Try updating graphic driver!") 44 | end 45 | else 46 | error("Unexpected version number string. Please report this bug! Version string: $(glsl)") 47 | end 48 | dict[:glsl_version] = glsl 49 | dict[:gl_version] = glv 50 | dict[:gl_vendor] = bytestring(glGetString(GL_VENDOR)) 51 | dict[:gl_renderer] = bytestring(glGetString(GL_RENDERER)) 52 | dict[:maxtexturesize] = glGetIntegerv(GL_MAX_TEXTURE_SIZE) 53 | 54 | n = glGetIntegerv(GL_NUM_EXTENSIONS) 55 | test = [glGetStringi(GL_EXTENSIONS, i) for i = 0:(n[1]-1)] 56 | 57 | end 58 | function get_glsl_version_string() 59 | if isempty(GLSL_VERSION) 60 | error("couldn't get GLSL version, GLUTils not initialized, or context not created?") 61 | end 62 | return "#version $(GLSL_VERSION)\n" 63 | end 64 | 65 | get_glsl_out_qualifier_string() = GLSL_VARYING_QUALIFIER == "in" ? "out" : GLSL_VARYING_QUALIFIER 66 | get_glsl_in_qualifier_string() = GLSL_VARYING_QUALIFIER 67 | maxtexturesize() = OPENGL_CONTEXT[:maxtexturesize] -------------------------------------------------------------------------------- /test/accessors.jl: -------------------------------------------------------------------------------- 1 | using GLAbstraction, GLWindow, ModernGL 2 | immutable Vec3 <: AbstractFixedVector{Float32, 3} 3 | x::Float32 4 | y::Float32 5 | z::Float32 6 | end 7 | Vec3() = Vec3(0f0,0f0,0f0) 8 | Base.length(::Type{Vec3}) = 3 9 | Base.eltype(::Type{Vec3}) = Float32 10 | createwindow("test", 2,2) 11 | tdata = [Vec3() Vec3(); Vec3() Vec3()] 12 | test_assert = [Vec3(33,0,0) Vec3(55,55,0); Vec3(44,44,0) Vec3(33,77,77)] 13 | a = copy(tdata) 14 | b = Texture(copy(tdata), keepinram=true) 15 | 16 | setindex1D!(a, 33, 1, 1) 17 | setindex1D!(a, 33, 2, 1) 18 | setindex1D!(a, 33, 3, 1) 19 | setindex1D!(a, 33, 4, 1) 20 | setindex1D!(a, [44,44], 2, 1:2) 21 | setindex1D!(a, [55,55], 3, 1:2) 22 | setindex1D!(a, [77,77], 4, 2:3) 23 | 24 | 25 | setindex1D!(b, 33, 1, 1) 26 | setindex1D!(b, 33, 2, 1) 27 | setindex1D!(b, 33, 3, 1) 28 | setindex1D!(b, 33, 4, 1) 29 | setindex1D!(b, [44,44], 2, 1:2) 30 | setindex1D!(b, [55,55], 3, 1:2) 31 | setindex1D!(b, [77,77], 4, 2:3) 32 | 33 | result = Array(Vec3, b.dims...) 34 | glBindTexture(b.texturetype, b.id) 35 | glGetTexImage(b.texturetype, 0, b.format, b.pixeltype, result) 36 | 37 | println(a) 38 | println(result) 39 | println(b.data) 40 | 41 | println(test_assert) 42 | @assert a == result 43 | @assert b.data == result 44 | @assert test_assert == result 45 | 46 | test_data = zeros(Float32, 5,5) 47 | a = Texture(copy(test_data), keepinram=true) 48 | 49 | a[1] = 66f0 50 | a[2] = 57f0 51 | a[3] = 77f0 52 | a[end-1] = 66f0 53 | a[end-2] = 57f0 54 | a[end-3] = 77f0 55 | 56 | a[1,3] = 784f0 57 | a[3,2] = 999f0 58 | 59 | test_data[1] = 66f0 60 | test_data[2] = 57f0 61 | test_data[3] = 77f0 62 | test_data[end-1] = 66f0 63 | test_data[end-2] = 57f0 64 | test_data[end-3] = 77f0 65 | 66 | test_data[1,3] = 784f0 67 | test_data[3,2] = 999f0 68 | 69 | result = Array(Float32, a.dims...) 70 | glBindTexture(a.texturetype, a.id) 71 | glGetTexImage(a.texturetype, 0, a.format, a.pixeltype, result) 72 | println(a.data) 73 | println("---------------------") 74 | println(result) 75 | println("---------------------") 76 | println(test_data) 77 | @assert a.data == result 78 | @assert a.data == test_data 79 | 80 | 81 | test_data = rand(Float32, 5,5) 82 | a = Texture(copy(test_data), keepinram=true) 83 | 84 | test_data[1:4,5] = Float32[0,1,2,3] 85 | test_data[5,1:4] = Float32[0,1,2,3] 86 | 87 | test_data[2:5,3:5] = eye(Float32, 4, 3) 88 | test_data[1:end,1:2] = eye(Float32, 5, 2) 89 | 90 | a[1:4,5] = Float32[0,1,2,3] 91 | a[5,1:4] = Float32[0,1,2,3] 92 | 93 | a[2:5,3:5] = eye(Float32, 4, 3) 94 | a[1:end,1:2] = eye(Float32, 5, 2) 95 | 96 | 97 | 98 | 99 | result = Array(Float32, a.dims...) 100 | glBindTexture(a.texturetype, a.id) 101 | glGetTexImage(a.texturetype, 0, a.format, a.pixeltype, result) 102 | println(a.data) 103 | println("---------------------") 104 | println(result) 105 | println("---------------------") 106 | println(test_data) 107 | @assert a.data == result 108 | @assert a.data == test_data -------------------------------------------------------------------------------- /src/GLAbstraction.jl: -------------------------------------------------------------------------------- 1 | module GLAbstraction 2 | 3 | using ImmutableArrays 4 | using ModernGL 5 | using Reactive 6 | using Quaternions 7 | using Color 8 | using FixedPointNumbers 9 | using Compat 10 | import Images: imread, colorspace, Image, AbstractGray, RGB4, ARGB, Images 11 | #import Lumberjack 12 | import Mustache 13 | import Base.delete! 14 | 15 | 16 | include("GLInit.jl") 17 | export init_after_context_creation 18 | export init_glutils 19 | export get_glsl_version_string 20 | export get_glsl_in_qualifier_string 21 | export get_glsl_out_qualifier_string 22 | 23 | 24 | include("GLTypes.jl") 25 | export GLProgram # Shader/program object 26 | export Texture # Texture object, basically a 1/2/3D OpenGL data array 27 | export update! # gets the data of texture as a Julia Array 28 | export data 29 | export AbstractFixedVector # First step into the direction of integrating FixedSizeArrays 30 | export RenderObject # An object which holds all GPU handles and datastructes to ready for rendering by calling render(obj) 31 | export prerender! # adds a function to a RenderObject, which gets executed befor setting the OpenGL render state 32 | export postrender! # adds a function to a RenderObject, which gets executed after setting the OpenGL render states 33 | export instancedobject # simplification for creating a RenderObject which renders instances 34 | export GLVertexArray # VertexArray wrapper object 35 | export GLBuffer # OpenGL Buffer object wrapper 36 | export indexbuffer # Shortcut to create an OpenGL Buffer object for indexes (1D, cardinality of one and GL_ELEMENT_ARRAY_BUFFER set) 37 | export opengl_compatible # infers if a type is opengl compatible and returns stats like cardinality and eltype (will be deprecated) 38 | export cardinality # returns the cardinality of the elements of a buffer 39 | export Circle # Simple circle object 40 | export Rectangle # Simple rectangle object 41 | export AABB # bounding slab 42 | export Shape # Abstract shape type 43 | export setindex1D! # Sets the index of an Array{FixedSizeVector, x}, making the FixedSizeVector accessible via an index 44 | export Style # Style Type, which is used to choose different visualization/editing styles via multiple dispatch 45 | export mergedefault! # merges a style dict via a given style 46 | include("GLExtendedFunctions.jl") 47 | export glTexImage 48 | 49 | include("GLUniforms.jl") 50 | export gluniform # wrapper of all the OpenGL gluniform functions, which call the correct gluniform function via multiple dispatch. Example: gluniform(location, x::Matrix4x4) = gluniformMatrix4fv(location, x) 51 | export toglsltype_string # infers a glsl type string from a julia type. Example: Matrix4x4 -> uniform mat4 52 | # Also exports Macro generated GLSL alike aliases for float32 Matrices and Vectors 53 | # only difference to GLSL: first character is uppercase uppercase 54 | 55 | include("GLMatrixMath.jl") 56 | export scalematrix 57 | export lookat 58 | export perspectiveprojection 59 | export orthographicprojection 60 | export translationmatrix, translatematrix_y, translatematrix_z 61 | export rotationmatrix_x, rotationmatrix_y, rotationmatrix_z 62 | export rotation 63 | export qrotation # quaternion rotation 64 | export transformationmatrix 65 | export Pivot # Pivot object, putting axis, scale position into one object 66 | export rotationmatrix4 # returns a 4x4 rotation matrix 67 | export perspective 68 | 69 | 70 | include("GLRender.jl") 71 | export render 72 | export enabletransparency 73 | export renderinstanced 74 | 75 | include("GLShader.jl") 76 | export readshader 77 | export glsl_variable_access 78 | export createview 79 | export TemplateProgram # Creates a shader from a Mustache view and and a shader file, which uses mustache syntax to replace values. 80 | 81 | include("GLCamera.jl") 82 | export OrthographicCamera 83 | export PerspectiveCamera 84 | export OrthographicPixelCamera 85 | 86 | include("GLShapes.jl") 87 | export gencircle 88 | export genquadstrip 89 | export isinside 90 | export gencube 91 | export genquad 92 | export gencubenormals 93 | export mergemesh 94 | 95 | include("GLUtils.jl") 96 | export @gputime 97 | 98 | end # module 99 | -------------------------------------------------------------------------------- /src/GLRenderObject.jl: -------------------------------------------------------------------------------- 1 | 2 | macro inject(x::Symbol) 3 | t = eval(x) 4 | @assert isa(t, DataType) 5 | returnValue = Expr[] 6 | for i =1:length(t.types) 7 | push!(returnValue, Expr(:(::), t.names[i], t.types[i].name.name)) 8 | end 9 | esc((Expr(:block, returnValue...))) 10 | end 11 | 12 | abstract RenderObject{Dimensionality} 13 | 14 | immutable CommonRenderObjectAttributes 15 | uniforms::Dict{Symbol, Any} 16 | vertexarray::GLVertexArray 17 | prerenderfunctions::Dict{Function, Tuple} 18 | postrenderfunctions::Dict{Function, Tuple} 19 | id::GLushort 20 | end 21 | 22 | type Instanced{PrimitiveDimensionality} <: RenderObject{3} 23 | primitive::Mesh{PrimitiveDimensionality} 24 | instances::Int 25 | end 26 | type Mesh{Dimensionality} <: RenderObject{Dimensionality} 27 | primitive::CommonRenderObjectAttributes 28 | instances::Int 29 | end 30 | immutable Text{Dimensionality} <: RenderObject{Dimensionality} 31 | @inject CommonRenderObjectAttributes 32 | end 33 | immutable Volume <: RenderObject{3} 34 | @inject CommonRenderObjectAttributes 35 | end 36 | 37 | begin 38 | local NEXT_RENDER_OBJECT_ID = zero(GLushorts) 39 | function RenderObject(data::Dict{Symbol, Any}, program::GLProgram; editables=Dict{Symbol,Input}()) 40 | NEXT_RENDER_OBJECT_ID::GLushort += 1 41 | 42 | buffers = filter((key, value) -> isa(value, GLBuffer), data) 43 | uniforms = filter((key, value) -> !isa(value, GLBuffer), data) 44 | uniforms[:objectid] = objectid # automatucally integrate object ID, will be discarded if shader doesn't use it 45 | 46 | if length(buffers) > 0 47 | vertexArray = GLVertexArray(Dict{Symbol, GLBuffer}(buffers), program) 48 | else 49 | vertexarray 50 | end 51 | textureTarget::GLint = -1 52 | uniformtypesandnames = uniform_name_type(program.id) # get active uniforms and types from program 53 | optimizeduniforms = map(uniformtypesandnames) do elem 54 | name = elem[1] 55 | typ = elem[2] 56 | if !haskey(uniforms, name) 57 | error("not sufficient uniforms supplied. Missing: ", name, " type: ", GLENUM(typ).name) 58 | end 59 | value = uniforms[name] 60 | if !is_correct_uniform_type(value, GLENUM(typ)) 61 | error("Uniform ", name, " not of correct type. Expected: ", GLENUM(typ).name, ". Got: ", typeof(value)) 62 | end 63 | if isa(value, Input) 64 | editables[name] = value 65 | end 66 | (name, value) 67 | end # only use active uniforms && check the type 68 | 69 | new(Dict{Symbol, Any}(optimizeduniforms), uniforms, vertexArray, Dict{Function, Tuple}(), Dict{Function, Tuple}(), objectid) 70 | end 71 | end 72 | function Base.show(io::IO, obj::RenderObject) 73 | println(io, "RenderObject with ID: ", obj.id) 74 | 75 | println(io, "uniforms: ") 76 | for (name, uniform) in obj.uniforms 77 | println(io, " ", name, "\n ", uniform) 78 | end 79 | println(io, "vertexarray length: ", obj.vertexarray.length) 80 | println(io, "vertexarray indexlength: ", obj.vertexarray.indexlength) 81 | end 82 | RenderObject{T}(data::Dict{Symbol, T}, program::GLProgram) = RenderObject(Dict{Symbol, Any}(data), program) 83 | 84 | immutable Field{Symbol} 85 | end 86 | 87 | Base.getindex(obj::RenderObject, symbol::Symbol) = obj.uniforms[symbol] 88 | Base.setindex!(obj::RenderObject, value, symbol::Symbol) = obj.uniforms[symbol] = value 89 | 90 | Base.getindex(obj::RenderObject, symbol::Symbol, x::Function) = getindex(obj, Field{symbol}(), x) 91 | Base.getindex(obj::RenderObject, ::Field{:prerender}, x::Function) = obj.prerenderfunctions[x] 92 | Base.getindex(obj::RenderObject, ::Field{:postrender}, x::Function) = obj.postrenderfunctions[x] 93 | 94 | Base.setindex!(obj::RenderObject, value, symbol::Symbol, x::Function) = setindex!(obj, value, Field{symbol}(), x) 95 | Base.setindex!(obj::RenderObject, value, ::Field{:prerender}, x::Function) = obj.prerenderfunctions[x] = value 96 | Base.setindex!(obj::RenderObject, value, ::Field{:postrender}, x::Function) = obj.postrenderfunctions[x] = value 97 | 98 | 99 | function instancedobject(data, program::GLProgram, amount::Integer, primitive::GLenum=GL_TRIANGLES) 100 | obj = RenderObject(data, program) 101 | postrender!(obj, renderinstanced, obj.vertexarray, amount, primitive) 102 | obj 103 | end 104 | 105 | function pushfunction!(target::Dict{Function, Tuple}, fs...) 106 | func = fs[1] 107 | args = Any[] 108 | for i=2:length(fs) 109 | elem = fs[i] 110 | if isa(elem, Function) 111 | target[func] = tuple(args...) 112 | func = elem 113 | args = Any[] 114 | else 115 | push!(args, elem) 116 | end 117 | end 118 | target[func] = tuple(args...) 119 | end 120 | prerender!(x::RenderObject, fs...) = pushfunction!(x.prerenderfunctions, fs...) 121 | postrender!(x::RenderObject, fs...) = pushfunction!(x.postrenderfunctions, fs...) -------------------------------------------------------------------------------- /src/GLShapes.jl: -------------------------------------------------------------------------------- 1 | 2 | 3 | # #in developement 4 | # immutable Polygon{T} <: Shape 5 | # points::Array{T, 1} 6 | # boundingBox::Rectangle 7 | # gl::RenderStyle 8 | # function Polygon(polygon::Array{T, 1}, color::GLColor, border::Float32, texture::Texture) 9 | # @assert length(polygon) % 2 == 0 10 | # boundingBox = Rectangle(-Inf32, -Inf32, Inf32, Inf32) 11 | # for i=1:length(polygon) - 1 12 | # x = polygon[i] 13 | # y = polygon[i + 1] 14 | # if x < boundingBox.width 15 | # boundingBox.width = x 16 | # elseif x > boundingBox.x 17 | # boundingBox.x = x 18 | # end 19 | # if y < boundingBox.height 20 | # boundingBox.height = y 21 | # elseif y > boundingBox.y 22 | # boundingBox.y = y 23 | # end 24 | # end 25 | # #gl = RenderStyle(color, border, texture, GLVertexArray(["position" => polygon], flatshader, primitiveMode = GL_TRIANGLE_FAN)) 26 | # #new(polygon, boundingBox, gl) 27 | # end 28 | # end 29 | 30 | 31 | # function isinside(polygon::Polygon, x::Real, y::Real) 32 | # a = polygon.points 33 | # c = false 34 | # i = length(a) - 1 35 | # for (x1, y1) in a 36 | # (x0, y0) = a[i % length(a) + 1] 37 | # if (y1 < y) != (y0 > y) && 38 | # (x < (x0-x1) * (y-y1) / (y0-y1) + x1) 39 | # c = ~c 40 | # end 41 | # i += 1 42 | # end 43 | # return c 44 | # end 45 | 46 | function isinside(circle::Circle, x::Real, y::Real) 47 | xD = abs(circle.x - x) - circle.r 48 | yD = abs(circle.y - y) - circle.r 49 | xD <= 0 && yD <= 0 50 | end 51 | 52 | function isinside(rect::Rectangle, x::Real, y::Real) 53 | rect.x <= x && rect.y <= y && rect.x + rect.w >= x && rect.y + rect.h >= y 54 | end 55 | 56 | function genquad{T <: Real}(x::T, y::T, width::T, height::T) 57 | v = T[ 58 | x, y, 59 | x, y + height, 60 | x+ width, y + height, 61 | x + width, y] 62 | 63 | uv = T[ 64 | 0, 0, 65 | 0, 1, 66 | 1, 1, 67 | 1, 0 68 | ] 69 | 70 | indexes = GLuint[0,1,2,2,3,0] 71 | v, uv , indexes 72 | end 73 | genquad(x::Real) = genquad(x, x) 74 | genquad(x::Real, y::Real) = genquad(0, 0, promote(x, y)...) 75 | genquad(x::Real, y::Real, width::Real, height::Real) = genquad(promote(x, x, width, height)...) 76 | 77 | function genquad{T}(downleft::Vector3{T}, width::Vector3{T}, height::Vector3{T}) 78 | v = Vector3{T}[ 79 | downleft, 80 | downleft + height, 81 | downleft + width + height, 82 | downleft + width 83 | ] 84 | uv = Vector2{T}[ 85 | Vector2{T}(0, 1), 86 | Vector2{T}(0, 0), 87 | Vector2{T}(1, 0), 88 | Vector2{T}(1, 1) 89 | ] 90 | indexes = GLuint[0,1,2,2,3,0] 91 | 92 | normal = unit(cross(width, height)) 93 | (v, uv, Vector3{T}[normal for i=1:4], indexes) 94 | end 95 | 96 | function gencircle(r, x, y, amount) 97 | slice = (2*pi) / amount 98 | result = GLfloat[x,y] 99 | for i = 0:amount-1 100 | angle = slice * i 101 | push!(result, float32(x + r * cos(angle)), float32(y + r * sin(angle))) 102 | end 103 | push!(result, float32(x + r * cos(0)), float32(y + r * sin(0))) 104 | return result 105 | end 106 | function genquadstrip(x::GLfloat, y::GLfloat, spacing::GLfloat, width::GLfloat, height::GLfloat, amount::Int) 107 | vertices = Array(GLfloat, amount * 2 * 6) 108 | for i = 1:amount 109 | vTemp = createQuad(x + ((width+ spacing) * (i-1)) , y, width, height) 110 | vertices[(i-1)*6*2 + 1:i*6*2] = vTemp 111 | end 112 | return vertices 113 | end 114 | # I just just create a meshtype, but I can't use the one from meshes, as its not parametric, and doesn't have variable vertex attributes 115 | # I deal with it later... 116 | function mergemesh{T}(a::(Vector{Vector3{T}}, Vector{Vector2{T}}, Vector{Vector3{T}}, Vector{GLuint}), 117 | (b::(Vector{Vector3{T}}, Vector{Vector2{T}}, Vector{Vector3{T}}, Vector{GLuint}))...) 118 | v = a[1] 119 | uv = a[2] 120 | n = a[3] 121 | i = a[4] 122 | for elem in b 123 | append!(i, elem[4] + length(v)) 124 | append!(v, elem[1]) 125 | append!(uv, elem[2]) 126 | append!(n, elem[3]) 127 | end 128 | 129 | return (v, uv, n, i) 130 | end 131 | 132 | function gencubenormals{T}(base_edge::Vector3{T}, wx::Vector3{T}, wy::Vector3{T}, hz::Vector3{T}) 133 | mergemesh( 134 | genquad(base_edge + hz, wx,wy), # Top 135 | genquad(base_edge, wy, wx), # Bottom 136 | genquad(base_edge + wx, wy, hz), # Right 137 | genquad(base_edge, hz, wy), # Left 138 | genquad(base_edge, wx, hz), # Back 139 | genquad(base_edge + wy, hz, wx) #Front 140 | ) 141 | 142 | end 143 | function gencube(x,y,z) 144 | vertices = Float32[ 145 | 0.0, 0.0, z, 146 | x, 0.0, z, 147 | x, y, z, 148 | 0.0, y, z, 149 | # back 150 | 0.0, 0.0, 0.0, 151 | x, 0.0, 0.0, 152 | x, y, 0.0, 153 | 0.0, y, 0.0 154 | ] 155 | uv = Float32[ 156 | 0.0, 0.0, 1.0, 157 | 1.0, 0.0, 1.0, 158 | 1.0, 1.0, 1.0, 159 | 0.0, 1.0, 1.0, 160 | # back 161 | 0.0, 0.0, 0.0, 162 | 1.0, 0.0, 0.0, 163 | 1.0, 1.0, 0.0, 164 | 0.0, 1.0, 0.0 165 | ] 166 | indexes = GLuint[ 167 | #front 168 | 0, 1, 2, 169 | 2, 3, 0, 170 | # top 171 | 3, 2, 6, 172 | 6, 7, 3, 173 | # back 174 | 7, 6, 5, 175 | 5, 4, 7, 176 | # bottom 177 | 4, 5, 1, 178 | 1, 0, 4, 179 | # left 180 | 4, 0, 3, 181 | 3, 7, 4, 182 | # right 183 | 1, 5, 6, 184 | 6, 2, 1] 185 | return (vertices, uv, indexes) 186 | end 187 | -------------------------------------------------------------------------------- /test/mesh_test.jl: -------------------------------------------------------------------------------- 1 | using GLWindow, GLAbstraction, GLFW, ModernGL, ImmutableArrays, WavefrontObj, FixedPointNumbers, Images, Color, Reactive 2 | using GLPlot #toopengl 3 | # window creation 4 | window = createwindow("OBJ-Viewer", 1000, 1000, debugging = false) 5 | cam = PerspectiveCamera(window.inputs, Vec3(2,2,0.5), Vec3(0.0)) 6 | 7 | # render objects creation 8 | shader = TemplateProgram(Pkg.dir("GLPlot", "src", "shader", "standard.vert"), Pkg.dir("GLPlot", "src", "shader", "phongblinn.frag")) 9 | RGBAU8 = AlphaColorValue{RGB{Ufixed8}, Ufixed8} 10 | Color.rgba(r::Real, g::Real, b::Real, a::Real) = AlphaColorValue(RGB{Float32}(r,g,b), float32(a)) 11 | rgbaU8(r::Real, g::Real, b::Real, a::Real) = AlphaColorValue(RGB{Ufixed8}(r,g,b), ufixed8(a)) 12 | #GLPlot.toopengl{T <: AbstractRGB}(colorinput::Input{T}) = toopengl(lift(x->AlphaColorValue(x, one(T)), RGBA{T}, colorinput)) 13 | tohsva(rgba) = AlphaColorValue(convert(HSV, rgba.c), rgba.alpha) 14 | torgba(hsva) = AlphaColorValue(convert(RGB, hsva.c), hsva.alpha) 15 | tohsva(h,s,v,a) = AlphaColorValue(HSV(float32(h), float32(s), float32(v)), float32(a)) 16 | Base.convert{T <: AbstractAlphaColorValue}(typ::Type{T}, x::AbstractAlphaColorValue) = AlphaColorValue(convert(RGB{eltype(typ)}, x.c), convert(eltype(typ), x.alpha)) 17 | 18 | 19 | function unitGeometry{T}(geometry::Vector{Vector3{T}}) 20 | assert(!isempty(geometry)) 21 | 22 | xmin = typemax(T) 23 | ymin = typemax(T) 24 | zmin = typemax(T) 25 | 26 | xmax = typemin(T) 27 | ymax = typemin(T) 28 | zmax = typemin(T) 29 | 30 | for vertex in geometry 31 | xmin = min(xmin, vertex[1]) 32 | ymin = min(ymin, vertex[2]) 33 | zmin = min(zmin, vertex[3]) 34 | 35 | xmax = max(xmax, vertex[1]) 36 | ymax = max(ymax, vertex[2]) 37 | zmax = max(zmax, vertex[3]) 38 | end 39 | 40 | xmiddle = xmin + (xmax - xmin) / 2; 41 | ymiddle = ymin + (ymax - ymin) / 2; 42 | zmiddle = zmin + (zmax - zmin) / 2; 43 | scale = 2 / max(xmax - xmin, ymax - ymin, zmax - zmin); 44 | 45 | result = similar(geometry) 46 | 47 | for i = 1:length(result) 48 | result[i] = Vector3{T}((geometry[i][1] - xmiddle) * scale, 49 | (geometry[i][2] - ymiddle) * scale, 50 | (geometry[i][3] - zmiddle) * scale 51 | ); 52 | end 53 | 54 | return result 55 | end 56 | 57 | 58 | objpath = "Butterfly/Butterfly.obj" 59 | 60 | assets_path = "Butterfly/" 61 | 62 | obj = readObjFile(objpath, faceindextype=GLuint, vertextype=Float32, compute_normals = false, triangulate = false) 63 | computeNormals!(obj, smooth_normals = true, override = false) 64 | triangulate!(obj) 65 | 66 | # center geometry 67 | obj.vertices = unitGeometry(obj.vertices) 68 | 69 | # load mtl files if present 70 | materials = WavefrontMtlMaterial{Float32}[] 71 | 72 | for mtllib in obj.mtllibs 73 | materials = [materials, readMtlFile( assets_path*mtllib, colortype=Float32 )] 74 | end 75 | 76 | 77 | 78 | 79 | render_objects = RenderObject[] 80 | 81 | compiled_materials = Dict() 82 | 83 | const light = Vec3[Vec3(1.0,1.0,1.0), Vec3(0.1,0.1,0.1), Vec3(0.9,0.9,0.9), Vec3(20.0,20.0,20.0)] 84 | const material = Vec3[Vec3(1.0,1.0,1.0), Vec3(1.0,1.0,1.0), Vec3(1.0,1.0,1.0), Vec3(1.0,1.0,1.0)] 85 | const tmaterial = Vec4[Vec4(1.0,1.0,1.0,1.0), Vec4(1.0,1.0,1.0,1.0), Vec4(1.0,1.0,1.0,1.0), Vec4(1.0,1.0,1.0,1.0)] 86 | const tmaterialused = GLint[-1,-1,-1,-1] 87 | 88 | for material_name in collect(keys(obj.materials)) 89 | 90 | (vs, nvs, uvs, fcs) = compileMaterial(obj, material_name) 91 | 92 | # hack: invert normals for glabstraction 93 | nvs = -nvs 94 | 95 | # holding global references seems necessary here 96 | # compiled_materials[material_name] = (vs, nvs, uvs, fcs, lines) 97 | data = [ 98 | :vertex => GLBuffer(vs), 99 | :normal => GLBuffer(nvs), 100 | :uv => GLBuffer(uvs), 101 | :indexes => indexbuffer(fcs), 102 | :view => cam.view, 103 | :projection => cam.projection, 104 | :normalmatrix => cam.normalmatrix, 105 | :eyeposition => cam.eyeposition, 106 | :model => eye(Mat4), 107 | :material => material, 108 | :light => light, 109 | :textures_used => tmaterialused, 110 | ] 111 | 112 | # search for a material with the given name 113 | texture_array = Any[] 114 | for mtl in materials 115 | if mtl.name == material_name 116 | data[:material] = Vec3[mtl.diffuse, mtl.ambient, mtl.specular, Vec3(mtl.specular_exponent)] 117 | println(data[:material]) 118 | if mtl.diffuse_texture != "" 119 | data[:textures_used][1] = length(texture_array) # insert texture array index 120 | push!(texture_array, imread(assets_path*"Texture/"*mtl.diffuse_texture).data) 121 | end 122 | if mtl.ambient_texture != "" 123 | data[:textures_used][2] = length(texture_array) # insert texture array index 124 | push!(texture_array, imread(assets_path*"Texture/"*mtl.ambient_texture).data) 125 | end 126 | if mtl.specular_texture != "" 127 | data[:textures_used][3] = length(texture_array) # insert texture array index 128 | push!(texture_array, imread(assets_path*"Texture/"*mtl.specular_texture).data) 129 | end 130 | #= 131 | if mtl.bump_texture != "" 132 | data[:textures_used] = true 133 | data[:tmaterialused][1] = 0 # insert texture array index 134 | push!(texture_array, imread(assets_path*mtl.diffuse_texture).data) 135 | end 136 | =# 137 | break 138 | end 139 | end 140 | 141 | if !isempty(texture_array) 142 | data[:textures_used] = true 143 | data[:texture_maps] = Texture(convert(Vector{Matrix{eltype(first(texture_array))}}, texture_array)) 144 | else 145 | data[:texture_maps] = Texture(Matrix{RGBA{Ufixed8}}[fill(rgbaU8(0,0,0,0), 1,1)]) 146 | end 147 | 148 | ro = RenderObject(data, shader) 149 | 150 | postrender!(ro, render, ro.vertexarray) 151 | #postrender!(ro, render, ro.vertexarray, GL_LINES) 152 | 153 | push!(render_objects, ro) 154 | end 155 | 156 | # OpenGL setup 157 | glClearColor(0.2,0.2,0.2,1) 158 | glDisable(GL_CULL_FACE) 159 | glEnable(GL_DEPTH_TEST) 160 | 161 | lift(window.inputs[:framebuffer_size]) do wh 162 | glViewport(0,0,wh...) 163 | end 164 | # Loop until the user closes the window 165 | while !GLFW.WindowShouldClose(window.nativewindow) 166 | 167 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 168 | 169 | # render materials separately 170 | for ro in render_objects 171 | render(ro) 172 | end 173 | 174 | yield() # this is needed for react to work 175 | 176 | GLFW.SwapBuffers(window.nativewindow) 177 | GLFW.PollEvents() 178 | end 179 | 180 | GLFW.Terminate() 181 | -------------------------------------------------------------------------------- /src/GLMatrixMath.jl: -------------------------------------------------------------------------------- 1 | function scalematrix{T}(scale::Vector3{T}) 2 | result = eye(T, 4, 4) 3 | result[1,1] = scale[1] 4 | result[2,2] = scale[2] 5 | result[3,3] = scale[3] 6 | return Matrix4x4(result) 7 | end 8 | 9 | 10 | translationmatrix_x{T}(x::T) = translationmatrix( Vector3{T}(x, 0, 0)) 11 | translationmatrix_y{T}(y::T) = translationmatrix( Vector3{T}(0, y, 0)) 12 | translationmatrix_z{T}(z::T) = translationmatrix( Vector3{T}(0, 0, z)) 13 | 14 | function translationmatrix{T}(translation::Vector3{T}) 15 | result = eye(T, 4, 4) 16 | result[1:3,4] = translation 17 | 18 | return Matrix4x4(result) 19 | end 20 | function rotate{T}(angle::T, axis::Vector3{T}) 21 | rotationmatrix(qrotation(convert(Array, axis), angle)) 22 | end 23 | 24 | function rotationmatrix_x{T}(angle::T) 25 | Matrix4x4{T}( 26 | Vector4{T}(1, 0, 0, 0), 27 | Vector4{T}(0, cos(angle), -sin(angle), 0), 28 | Vector4{T}(0, sin(angle), cos(angle), 0), 29 | Vector4{T}(0, 0, 0, 1)) 30 | end 31 | function rotationmatrix_z{T}(angle::T) 32 | Matrix4x4{T}( 33 | Vector4{T}(cos(angle), 0, sin(angle), 0), 34 | Vector4{T}(0, 1, 0, 0), 35 | Vector4{T}(-sin(angle), 0, cos(angle), 0), 36 | Vector4{T}(0, 0, 0, 1)) 37 | end 38 | function rotationmatrix_z{T}(angle::T) 39 | Matrix4x4{T}( 40 | Vector4{T}(cos(angle), -sin(angle), 0, 0), 41 | Vector4{T}(sin(angle), cos(angle), 0, 0), 42 | Vector4{T}(0, 0, 1, 0), 43 | Vector4{T}(0, 0, 0, 1)) 44 | end 45 | #= 46 | function rotationmatrix{T}(angle::T, axis::Vector3{T}) 47 | x = axis[1] 48 | y = axis[2] 49 | z = axis[3] 50 | a = angle 51 | m1 = Vector4{T}( x^2 * (1 - cos(a)) + cos(a) , x*y * (1 - cos(a)) - z*sin(a), x*z * (1 - cos(a)) + y*sin(a), 0) 52 | m2 = Vector4{T}( x*y * (1 - cos(a)) + z*sin(a), y^2 * (1 - cos(a)) + cos(a) , y*z * (1 - cos(a)) - x*sin(a), 0) 53 | m3 = Vector4{T}( x*z * (1 - cos(a)) - y*sin(a), y*z * (1 - cos(a)) + x*sin(a), z^2 * (1 - cos(a)) + cos(a) , 0) 54 | m4 = Vector4{T}(0, 0, 0, 1) 55 | Matrix4x4(m1, m2, m3, m4) 56 | end 57 | =# 58 | #= 59 | Create view frustum 60 | 61 | Parameters 62 | ---------- 63 | left : float 64 | Left coordinate of the field of view. 65 | right : float 66 | Left coordinate of the field of view. 67 | bottom : float 68 | Bottom coordinate of the field of view. 69 | top : float 70 | Top coordinate of the field of view. 71 | znear : float 72 | Near coordinate of the field of view. 73 | zfar : float 74 | Far coordinate of the field of view. 75 | 76 | Returns 77 | ------- 78 | M : array 79 | View frustum matrix (4x4). 80 | =# 81 | function frustum{T}(left::T, right::T, bottom::T, top::T, znear::T, zfar::T) 82 | @assert right != left "right is equal to left: $right" 83 | @assert bottom != top "bottom is equal to top: $bottom" 84 | @assert znear != zfar "znear is equal to zfar: $znear" 85 | M = zeros(T, 4, 4) 86 | M[1, 1] = +2.0 * znear / (right - left) 87 | M[3, 1] = (right + left) / (right - left) 88 | M[2, 2] = +2.0 * znear / (top - bottom) 89 | M[4, 2] = (top + bottom) / (top - bottom) 90 | M[3, 3] = -(zfar + znear) / (zfar - znear) 91 | M[4, 3] = -2.0 * znear * zfar / (zfar - znear) 92 | M[3, 4] = -1.0 93 | return Matrix4x4(M) 94 | end 95 | #= 96 | Create perspective projection matrix 97 | 98 | Parameters 99 | ---------- 100 | fovy : float 101 | The field of view along the y axis. 102 | aspect : float 103 | Aspect ratio of the view. 104 | znear : float 105 | Near coordinate of the field of view. 106 | zfar : float 107 | Far coordinate of the field of view. 108 | 109 | Returns 110 | ------- 111 | M : array 112 | Perspective projection matrix (4x4). 113 | =# 114 | function perspective(fovyRadians, aspect, zNear, zFar ) 115 | 116 | f = tan((( pi/2 ) - ( 0.5f0 * fovyRadians ))) 117 | rangeInv = ( 1.0f / ( zNear - zFar ) ) 118 | return Matrix4x4( 119 | Vector4( ( f / aspect ), 0.0f, 0.0f, 0.0f ), 120 | Vector4( 0.0f, f, 0.0f, 0.0f ), 121 | Vector4( 0.0f, 0.0f, ( ( zNear + zFar ) * rangeInv ), -1.0f ), 122 | Vector4( 0.0f, 0.0f, ( ( ( zNear * zFar ) * rangeInv ) * 2.0f ), 0.0f ) 123 | ); 124 | end 125 | function perspectiveprojection{T}(fovy::T, aspect::T, znear::T, zfar::T) 126 | 127 | @assert znear != zfar 128 | h = convert(T, tan(fovy / 360.0 * pi) * znear) 129 | w = convert(T, h * aspect) 130 | return frustum(-w, w, -h, h, znear, zfar) 131 | end 132 | 133 | 134 | function lookat{T}(eyePos::Vector3{T}, lookAt::Vector3{T}, up::Vector3{T}) 135 | 136 | zaxis = unit(eyePos - lookAt) 137 | xaxis = unit(cross(up, zaxis)) 138 | yaxis = unit(cross(zaxis, xaxis)) 139 | 140 | viewMatrix = eye(T, 4,4) 141 | viewMatrix[1,1:3] = xaxis 142 | viewMatrix[2,1:3] = yaxis 143 | viewMatrix[3,1:3] = zaxis 144 | 145 | Matrix4x4(viewMatrix) * translationmatrix(-eyePos) 146 | end 147 | 148 | function orthographicprojection{T}( 149 | left::T, right::T, 150 | bottom::T, top::T, 151 | znear::T, zfar::T) 152 | 153 | @assert right != left 154 | @assert bottom != top 155 | @assert znear != zfar 156 | 157 | matrix = zeros(T, 4,4) 158 | 159 | matrix[1,1] = 2.0/(right-left) 160 | matrix[1,4] = -(right+left)/(right-left) 161 | matrix[2,2] = 2.0/(top-bottom) 162 | matrix[2,4] = -(top+bottom)/(top-bottom) 163 | matrix[3,3] = -2.0/(zfar-znear) 164 | matrix[3,4] = -(zfar+znear)/(zfar-znear) 165 | matrix[4,4] = 1.0 166 | Matrix4x4(matrix) 167 | end 168 | 169 | 170 | import Base: (*) 171 | function (*){T}(q::Quaternion{T}, v::Vector3{T}) 172 | t = 2 * cross(Vector3(q.v1, q.v2, q.v3), v) 173 | v + q.s * t + cross(Vector3(q.v1, q.v2, q.v3), t) 174 | end 175 | function Quaternions.qrotation{T<:Real}(axis::Vector3{T}, theta::T) 176 | u = unit(axis) 177 | s = sin(theta/2) 178 | Quaternion(cos(theta/2), s*u.e1, s*u.e2, s*u.e3, true) 179 | end 180 | 181 | immutable Pivot{T} 182 | 183 | origin::Vector3{T} 184 | 185 | xaxis::Vector3{T} 186 | yaxis::Vector3{T} 187 | zaxis::Vector3{T} 188 | 189 | rotation::Quaternion 190 | 191 | translation::Vector3{T} 192 | scale::Vector3{T} 193 | 194 | end 195 | function rotationmatrix4{T}(q::Quaternion{T}) 196 | sx, sy, sz = 2q.s*q.v1, 2q.s*q.v2, 2q.s*q.v3 197 | xx, xy, xz = 2q.v1^2, 2q.v1*q.v2, 2q.v1*q.v3 198 | yy, yz, zz = 2q.v2^2, 2q.v2*q.v3, 2q.v3^2 199 | 200 | Matrix4x4([1-(yy+zz) xy-sz xz+sy 0; 201 | xy+sz 1-(xx+zz) yz-sx 0; 202 | xz-sy yz+sx 1-(xx+yy) 0; 203 | 0 0 0 1]) 204 | end 205 | function transformationmatrix(p::Pivot) 206 | ( 207 | translationmatrix(p.origin)* #go to origin 208 | Matrix4x4(rotationmatrix4(p.rotation))* 209 | #scalematrix(p.scale)* 210 | translationmatrix(-p.origin)* # go back to origin 211 | translationmatrix(p.translation) 212 | 213 | ) 214 | end 215 | #Calculate rotation between two vectors 216 | function rotation{T}(u::Vector3{T}, v::Vector3{T}) 217 | # It is important that the inputs are of equal length when 218 | # calculating the half-way vector. 219 | u = unit(u) 220 | v = unit(v) 221 | 222 | # Unfortunately, we have to check for when u == -v, as u + v 223 | # in this case will be (0, 0, 0), which cannot be normalized. 224 | if (u == -v) 225 | # 180 degree rotation around any orthogonal vector 226 | other = (abs(dot(u, Vector3{T}(1,0,0))) < 1.0) ? Vector3{T}(1,0,0) : Vector3{T}(0,1,0) 227 | return qrotation(unit(cross(u, other)), 180) 228 | end 229 | 230 | half = unit(u + v) 231 | return Quaternion(dot(u, half), cross(u, half)...) 232 | end 233 | 234 | 235 | -------------------------------------------------------------------------------- /src/GLExtendedFunctions.jl: -------------------------------------------------------------------------------- 1 | #= 2 | This is the place, where I put functions, which are so annoying in OpenGL, that I felt the need to wrap them and make them more "Julian" 3 | Its also to do some more complex error handling, not handled by the debug callback 4 | =# 5 | 6 | function ModernGL.glGetAttachedShaders(program::GLuint) 7 | actualLength = Array(GLsizei, 1) 8 | shaders = Array(GLuint, 2) 9 | glGetAttachedShaders(program, 2, actualLength, shaders) 10 | if actualLength[1] == 2 11 | return shaders 12 | else 13 | error("glGetAttachedShaders: no shaders attached or other error") 14 | end 15 | end 16 | 17 | 18 | get_attribute_location(program::GLuint, name::Symbol) = get_attribute_location(program, string(name)) 19 | function get_attribute_location(program::GLuint, name::ASCIIString) 20 | const location::GLint = glGetAttribLocation(program, name) 21 | if location == -1 22 | error("Named attribute (:$(name)) is not an active attribute in the specified program object or\n 23 | the name starts with the reserved prefix gl_\n") 24 | elseif location == GL_INVALID_OPERATION 25 | error("program is not a value generated by OpenGL or\n 26 | program is not a program object or\n 27 | program has not been successfully linked") 28 | end 29 | location 30 | end 31 | 32 | get_uniform_location(program::GLuint, name::Symbol) = get_uniform_location(program, string(name)) 33 | function get_uniform_location(program::GLuint, name::ASCIIString) 34 | const location::GLint = glGetUniformLocation(program, name) 35 | if location == -1 36 | error("Named uniform (:$(name)) is not an active attribute in the specified program object or\nthe name starts with the reserved prefix gl_\n") 37 | elseif location == GL_INVALID_OPERATION 38 | error("program is not a value generated by OpenGL or\n 39 | program is not a program object or\n 40 | program has not been successfully linked") 41 | end 42 | location 43 | end 44 | 45 | function ModernGL.glGetActiveUniform(programID::GLuint, index::Integer) 46 | const actualLength = GLsizei[1] 47 | const uniformSize = GLint[1] 48 | const typ = GLenum[1] 49 | const maxcharsize = glGetProgramiv(programID, GL_ACTIVE_UNIFORM_MAX_LENGTH) 50 | const name = Array(GLchar, maxcharsize) 51 | 52 | glGetActiveUniform(programID, index, maxcharsize, actualLength, uniformSize, typ, name) 53 | if actualLength[1] > 0 54 | uname = bytestring(pointer(name), actualLength[1]) 55 | uname = symbol(replace(uname, r"\[\d*\]", "")) # replace array brackets. This is not really a good solution. 56 | (uname, typ[1], uniformSize[1]) 57 | else 58 | error("No active uniform at given index. Index: ", index) 59 | end 60 | end 61 | function ModernGL.glGetActiveAttrib(programID::GLuint, index::Integer) 62 | const actualLength = GLsizei[1] 63 | const attributeSize = GLint[1] 64 | const typ = GLenum[1] 65 | const maxcharsize = glGetProgramiv(programID, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH) 66 | const name = Array(GLchar, maxcharsize) 67 | 68 | glGetActiveAttrib(programID, index, maxcharsize, actualLength, attributeSize, typ, name) 69 | if actualLength[1] > 0 70 | uname = bytestring(pointer(name), actualLength[1]) 71 | uname = symbol(replace(uname, r"\[\d*\]", "")) # replace array brackets. This is not really a good solution. 72 | (uname, typ[1], attributeSize[1]) 73 | else 74 | error("No active uniform at given index. Index: ", index) 75 | end 76 | end 77 | function ModernGL.glGetProgramiv(programID::GLuint, variable::GLenum) 78 | const result = GLint[-1] 79 | glGetProgramiv(programID, variable, result) 80 | result[1] 81 | end 82 | function ModernGL.glGetIntegerv(variable::GLenum) 83 | const result = GLint[-1] 84 | glGetIntegerv(uint32(variable), result) 85 | result[1] 86 | end 87 | 88 | function ModernGL.glGenBuffers() 89 | const result = GLuint[0] 90 | glGenBuffers(1, result) 91 | id = result[1] 92 | if id <= 0 93 | error("glGenBuffers returned invalid id. OpenGL Context active?") 94 | end 95 | id 96 | end 97 | function ModernGL.glGenVertexArrays() 98 | const result = GLuint[0] 99 | glGenVertexArrays(1, result) 100 | id = result[1] 101 | if id <=0 102 | error("glGenVertexArrays returned invalid id. OpenGL Context active?") 103 | end 104 | id 105 | end 106 | function ModernGL.glGenTextures() 107 | const result = GLuint[0] 108 | glGenTextures(1, result) 109 | id = result[1] 110 | if id <= 0 111 | error("glGenTextures returned invalid id. OpenGL Context active?") 112 | end 113 | id 114 | end 115 | function ModernGL.glGenFramebuffers() 116 | const result = GLuint[0] 117 | glGenFramebuffers(1, result) 118 | id = result[1] 119 | if id <= 0 120 | error("glGenFramebuffers returned invalid id. OpenGL Context active?") 121 | end 122 | id 123 | end 124 | 125 | function ModernGL.glGetTexLevelParameteriv(target::GLenum, level, name::GLenum) 126 | result = GLint[0] 127 | glGetTexLevelParameteriv(target, level, name, result) 128 | result[1] 129 | end 130 | function checktexture(target::GLenum) 131 | 132 | end 133 | function glTexImage(ttype::GLenum, level::Integer, internalFormat::GLenum, w::Integer, h::Integer, d::Integer, border::Integer, format::GLenum, datatype::GLenum, data) 134 | 135 | glTexImage3D(GL_PROXY_TEXTURE_3D, level, internalFormat, w, h, d, border, format, datatype, C_NULL) 136 | for l in 0:level 137 | result = glGetTexLevelParameteriv(GL_PROXY_TEXTURE_3D, l, GL_TEXTURE_WIDTH) 138 | if result == 0 139 | error("glTexImage 3D: width too large. Width: ", w) 140 | end 141 | result = glGetTexLevelParameteriv(GL_PROXY_TEXTURE_3D, l,GL_TEXTURE_HEIGHT) 142 | if result == 0 143 | error("glTexImage 3D: height too large. height: ", h) 144 | end 145 | result = glGetTexLevelParameteriv(GL_PROXY_TEXTURE_3D, l, GL_TEXTURE_DEPTH) 146 | if result == 0 147 | error("glTexImage 3D: depth too large. Depth: ", d) 148 | end 149 | result = glGetTexLevelParameteriv(GL_PROXY_TEXTURE_3D, l, GL_TEXTURE_INTERNAL_FORMAT) 150 | if result == 0 151 | error("glTexImage 3D: internal format not valid. format: ", GLENUM(internalFormat).name) 152 | end 153 | end 154 | glTexImage3D(ttype, level, internalFormat, w, h, d, border, format, datatype, data) 155 | end 156 | function glTexImage(ttype::GLenum, level::Integer, internalFormat::GLenum, w::Integer, h::Integer, border::Integer, format::GLenum, datatype::GLenum, data) 157 | maxsize = glGetIntegerv(GL_MAX_TEXTURE_SIZE) 158 | glTexImage2D(GL_PROXY_TEXTURE_2D, level, internalFormat, w, h, border, format, datatype, C_NULL) 159 | for l in 0:level 160 | result = glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, l, GL_TEXTURE_WIDTH) 161 | if result == 0 162 | error("glTexImage 2D: width too large. Width: ", w) 163 | end 164 | result = glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, l, GL_TEXTURE_HEIGHT) 165 | if result == 0 166 | error("glTexImage 2D: height too large. height: ", h) 167 | end 168 | result = glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, l, GL_TEXTURE_INTERNAL_FORMAT) 169 | if result == 0 170 | error("glTexImage 2D: internal format not valid. format: ", GLENUM(internalFormat).name) 171 | end 172 | end 173 | glTexImage2D(ttype, level, internalFormat, w, h, border, format, datatype, data) 174 | end 175 | function glTexImage(ttype::GLenum, level::Integer, internalFormat::GLenum, w::Integer, border::Integer, format::GLenum, datatype::GLenum, data) 176 | glTexImage1D(GL_PROXY_TEXTURE_1D, level, internalFormat, w, border, format, datatype, C_NULL) 177 | for l in 0:level 178 | result = glGetTexLevelParameteriv(GL_PROXY_TEXTURE_1D, l, GL_TEXTURE_WIDTH) 179 | if result == 0 180 | error("glTexImage 1D: width too large. Width: ", w) 181 | end 182 | result = glGetTexLevelParameteriv(GL_PROXY_TEXTURE_1D, l, GL_TEXTURE_INTERNAL_FORMAT) 183 | if result == 0 184 | error("glTexImage 1D: internal format not valid. format: ", GLENUM(internalFormat).name) 185 | end 186 | end 187 | glTexImage1D(ttype, level, internalFormat, w, border, format, datatype, data) 188 | end 189 | 190 | 191 | ModernGL.glViewport(x::Rectangle) = glViewport(x.x, x.y, x.w, x.h) 192 | 193 | 194 | function ModernGL.glGenRenderbuffers(format::GLenum, attachment::GLenum, dimensions) 195 | renderbuffer = GLuint[0] 196 | glGenRenderbuffers(1, renderbuffer) 197 | glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer[1]) 198 | glRenderbufferStorage(GL_RENDERBUFFER, format, dimensions...) 199 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderbuffer[1]) 200 | renderbuffer[1] 201 | end -------------------------------------------------------------------------------- /src/GLShader.jl: -------------------------------------------------------------------------------- 1 | 2 | 3 | function getinfolog(obj::GLuint) 4 | # Return the info log for obj, whether it be a shader or a program. 5 | isShader = glIsShader(obj) 6 | getiv = isShader == GL_TRUE ? glGetShaderiv : glGetProgramiv 7 | getInfo = isShader == GL_TRUE ? glGetShaderInfoLog : glGetProgramInfoLog 8 | 9 | # Get the maximum possible length for the descriptive error message 10 | int::Array{GLint, 1} = [0] 11 | getiv(obj, GL_INFO_LOG_LENGTH, int) 12 | maxlength = int[1] 13 | # Return the text of the message if there is any 14 | if maxlength > 0 15 | buffer = zeros(GLchar, maxlength) 16 | sizei::Array{GLsizei, 1} = [0] 17 | getInfo(obj, maxlength, sizei, buffer) 18 | length = sizei[1] 19 | bytestring(pointer(buffer), length) 20 | else 21 | "success" 22 | end 23 | end 24 | 25 | function validateshader(shader) 26 | success::Array{GLint, 1} = [0] 27 | glGetShaderiv(shader, GL_COMPILE_STATUS, success) 28 | success[1] == GL_TRUE 29 | end 30 | 31 | function readshader(shadercode::ASCIIString, shaderType, path::String) 32 | 33 | const source = bytestring(shadercode) 34 | const sourcePTR::Ptr{GLchar} = convert(Ptr{GLchar}, pointer(source)) 35 | 36 | shaderID::GLuint = glCreateShader(shaderType) 37 | @assert shaderID > 0 38 | glShaderSource(shaderID, 1, convert(Ptr{Uint8}, pointer([sourcePTR])), 0) 39 | glCompileShader(shaderID) 40 | 41 | if !validateshader(shaderID) 42 | for (i,line) in enumerate(split(shadercode, "\n")) 43 | println(i, " ", line) 44 | end 45 | log = getinfolog(shaderID) 46 | error(path * "\n" * log) 47 | end 48 | 49 | return shaderID 50 | end 51 | function readshader(fileStream::IOStream, shaderType, name) 52 | @assert isopen(fileStream) 53 | return readShader(readall(fileStream), shaderType, name) 54 | end 55 | 56 | function update(vertcode::ASCIIString, fragcode::ASCIIString, vpath::String, fpath::String, program) 57 | try 58 | vertid = readshader(vertcode, GL_VERTEX_SHADER, vpath) 59 | fragid = readshader(fragcode, GL_FRAGMENT_SHADER, fpath) 60 | glUseProgram(0) 61 | oldid = glGetAttachedShaders(program) 62 | glDetachShader(program, oldid[1]) 63 | glDetachShader(program, oldid[2]) 64 | 65 | glAttachShader(program, vertid) 66 | glAttachShader(program, fragid) 67 | 68 | glLinkProgram(program) 69 | glDeleteShader(vertid) 70 | glDeleteShader(fragid) 71 | catch theerror 72 | println(theerror) 73 | end 74 | end 75 | 76 | 77 | glsl_variable_access{T,D}(keystring, ::Texture{T, 1, D}) = "texture($(keystring), uv).r;" 78 | glsl_variable_access{T,D}(keystring, ::Texture{T, 2, D}) = "texture($(keystring), uv).rg;" 79 | glsl_variable_access{T,D}(keystring, ::Texture{T, 3, D}) = "texture($(keystring), uv).rgb;" 80 | glsl_variable_access{T,D}(keystring, ::Texture{T, 4, D}) = "texture($(keystring), uv).rgba;" 81 | glsl_variable_access{T,D}(keystring, ::Texture{T, 4, D}) = "texture($(keystring), uv).rgba;" 82 | 83 | glsl_variable_access(keystring, ::Union(Real, GLBuffer, AbstractArray, AbstractRGB, AbstractAlphaColorValue)) = keystring*";" 84 | 85 | glsl_variable_access(keystring, s::Signal) = glsl_variable_access(keystring, s.value) 86 | glsl_variable_access(keystring, t::Any) = error("no glsl variable calculation available for :",keystring, " of type ", typeof(t)) 87 | 88 | 89 | function createview(x::Dict{Symbol, Any}, keys) 90 | view = Dict{ASCIIString, ASCIIString}() 91 | for (key,value) in x 92 | if !isa(value, String) 93 | keystring = string(key) 94 | typekey = keystring*"_type" 95 | calculationkey = keystring*"_calculation" 96 | if in(typekey, keys) 97 | view[keystring*"_type"] = toglsltype_string(value) 98 | end 99 | if in(calculationkey, keys) 100 | view[keystring*"_calculation"] = glsl_variable_access(keystring, value) 101 | end 102 | end 103 | end 104 | view 105 | end 106 | mustachekeys(mustache::Mustache.MustacheTokens) = map(x->x[2], filter(x-> x[1] == "name", mustache.tokens)) 107 | 108 | 109 | function GLProgram( vertex::ASCIIString, fragment::ASCIIString, vertpath::String, fragpath::String; 110 | fragdatalocation=(Int, ASCIIString)[]) 111 | 112 | vertexShaderID::GLuint = readshader(vertex, GL_VERTEX_SHADER, vertpath) 113 | fragmentShaderID::GLuint = readshader(fragment, GL_FRAGMENT_SHADER, fragpath) 114 | p = glCreateProgram() 115 | 116 | @assert p > 0 117 | glAttachShader(p, vertexShaderID) 118 | glAttachShader(p, fragmentShaderID) 119 | for elem in fragdatalocation 120 | glBindFragDataLocation(p, elem...) 121 | end 122 | glLinkProgram(p) 123 | 124 | glDeleteShader(vertexShaderID) # Can be deleted, as they will still be linked to Program and released after program gets released 125 | glDeleteShader(fragmentShaderID) 126 | 127 | nametypedict = Dict{Symbol, GLenum}(uniform_name_type(p)) 128 | attriblist = attribute_name_type(p) 129 | 130 | texturetarget = -1 131 | uniformlocationdict = map( elem -> begin 132 | name = elem[1] 133 | typ = elem[2] 134 | loc = get_uniform_location(p, name) 135 | if istexturesampler(typ) 136 | texturetarget += 1 137 | return (name, (loc, texturetarget)) 138 | else 139 | return (name, (loc,)) 140 | end 141 | end, nametypedict) 142 | 143 | return GLProgram(p, vertpath, fragpath, nametypedict, Dict{Symbol,Tuple}(uniformlocationdict)) 144 | end 145 | function GLProgram(vertex_file_path::ASCIIString, fragment_file_path::ASCIIString) 146 | 147 | vertsource = readall(open(vertex_file_path)) 148 | fragsource = readall(open(fragment_file_path)) 149 | vertname = basename(vertex_file_path) 150 | fragname = basename(fragment_file_path) 151 | GLProgram(vertsource, fragsource, vertex_file_path, fragment_file_path) 152 | end 153 | 154 | # REAALLY ugly way of doing this.. I still don't completely know, why my other approaches haven't worked 155 | function watch_file_react(filename) 156 | f = open(filename) 157 | firstcontent = readall(f) 158 | close(f) 159 | file_edited = lift(x->x[1], Bool, foldl((v0, v1) -> begin 160 | t = mtime(filename) 161 | (!isapprox(0.0, v0[2] - t), t) 162 | end, (false, mtime(filename)), every(1.0))) 163 | return lift(x -> begin 164 | f = open(filename) 165 | content = readall(f) 166 | close(f) 167 | content 168 | end, keepwhen(file_edited, false, file_edited)) 169 | end 170 | 171 | function TemplateProgram( 172 | vertex_file_path::AbstractString, fragment_file_path::AbstractString; 173 | view::Dict{ASCIIString, ASCIIString} = Dict{ASCIIString, ASCIIString}(), 174 | attributes::Dict{Symbol, Any} = Dict{Symbol, Any}(), 175 | fragdatalocation=(Int, ASCIIString)[] 176 | ) 177 | 178 | if haskey(view, "in") || haskey(view, "out") || haskey(view, "GLSL_VERSION") 179 | println("warning: using internal keyword \"$(in/out/GLSL_VERSION)\" for shader template. The value will be overwritten") 180 | end 181 | extension = "" #Still empty, but might be replaced by a platform dependant extension string 182 | if haskey(view, "GLSL_EXTENSIONS") 183 | #to do: check custom extension... 184 | #for now we just append the extensions 185 | extension *= "\n" * view["GLSL_EXTENSIONS"] 186 | end 187 | internaldata = @compat Dict( 188 | "out" => get_glsl_out_qualifier_string(), 189 | "in" => get_glsl_in_qualifier_string(), 190 | "GLSL_VERSION" => get_glsl_version_string(), 191 | 192 | "GLSL_EXTENSIONS" => extension 193 | ) 194 | view = merge(internaldata, view) 195 | sources = lift( (vertex_file_path, fragment_file_path) -> begin 196 | vertex_tm = Mustache.parse(vertex_file_path) 197 | fragment_tm = Mustache.parse(fragment_file_path) 198 | 199 | vertex_view = merge(createview(attributes, mustachekeys(vertex_tm)), view) 200 | fragment_view = merge(createview(attributes, mustachekeys(fragment_tm)), view) 201 | vertsource = replace(replace(Mustache.render(vertex_tm, vertex_view), "/", "/"), ">", ">") 202 | fragsource = replace(replace(Mustache.render(fragment_tm, fragment_view), "/", "/"), ">", ">") 203 | (vertsource, fragsource) 204 | end, watch_file_react(vertex_file_path), watch_file_react(fragment_file_path)) 205 | 206 | #just using one view for vert and frag shader plus workaround for mustache bug 207 | p = GLProgram(sources.value[1], sources.value[2], vertex_file_path, fragment_file_path, fragdatalocation=fragdatalocation) 208 | lift( x-> update(x[1], x[2], vertex_file_path, fragment_file_path, p.id), sources) 209 | p 210 | end 211 | 212 | 213 | function TemplateProgram( 214 | vertex_source::AbstractString, fragment_source::AbstractString, 215 | vertex_name::ASCIIString, fragment_name::ASCIIString; 216 | view::Dict{ASCIIString, ASCIIString} = Dict{ASCIIString, ASCIIString}(), 217 | attributes::Dict{Symbol, Any}=Dict{Symbol, Any}(), 218 | fragdatalocation=(Int, ASCIIString)[] 219 | ) 220 | 221 | if haskey(view, "in") || haskey(view, "out") || haskey(view, "GLSL_VERSION") 222 | println("warning: using internal keyword \"$(in/out/GLSL_VERSION)\" for shader template. The value will be overwritten") 223 | end 224 | extension = "" #Still empty, but might be replaced by a platform dependant extension string 225 | if haskey(view, "GLSL_EXTENSIONS") 226 | #to do: check if extension is available... 227 | #for now we just append the extensions 228 | extension *= "\n" * view["GLSL_EXTENSIONS"] 229 | end 230 | internaldata = @compat Dict( 231 | "out" => get_glsl_out_qualifier_string(), 232 | "in" => get_glsl_in_qualifier_string(), 233 | "GLSL_VERSION" => get_glsl_version_string(), 234 | 235 | "GLSL_EXTENSIONS" => extension 236 | ) 237 | view = merge(internaldata, view) 238 | sources = lift( (vertex_file_path, fragment_file_path) -> begin 239 | vertex_tm = Mustache.parse(vertex_file_path) 240 | fragment_tm = Mustache.parse(fragment_file_path) 241 | 242 | vertex_view = merge(createview(attributes, mustachekeys(vertex_tm)), view) 243 | fragment_view = merge(createview(attributes, mustachekeys(fragment_tm)), view) 244 | vertsource = replace(Mustache.render(vertex_tm, vertex_view), "/", "/") 245 | fragsource = replace(Mustache.render(fragment_tm, fragment_view), "/", "/") 246 | (vertsource, fragsource) 247 | end, Input(vertex_source), Input(fragment_source)) 248 | 249 | #just using one view for vert and frag shader plus workaround for mustache bug 250 | p = GLProgram(sources.value[1], sources.value[2], vertex_name, fragment_name, fragdatalocation=fragdatalocation) 251 | lift( x-> update(x[1], x[2], vertex_name, fragment_name, p.id), sources) 252 | p 253 | end -------------------------------------------------------------------------------- /src/GLTypes.jl: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | abstract Shape 3 | immutable Circle{T <: Real} <: Shape 4 | x::T 5 | y::T 6 | r::T 7 | end 8 | 9 | type Rectangle{T <: Real} <: Shape 10 | x::T 11 | y::T 12 | w::T 13 | h::T 14 | end 15 | #Axis Aligned Bounding Box 16 | immutable AABB{T} 17 | min::Vector3{T} 18 | max::Vector3{T} 19 | end 20 | ############################################################################ 21 | 22 | type GLProgram 23 | id::GLuint 24 | vertpath::String 25 | fragpath::String 26 | nametype::Dict{Symbol, GLenum} 27 | uniformloc::Dict{Symbol, Tuple} 28 | function GLProgram(id::GLuint, vertpath::String, fragpath::String, nametype::Dict{Symbol, GLenum}, uniformloc::Dict{Symbol, Tuple}) 29 | obj = new(id, vertpath, fragpath, nametype, uniformloc) 30 | end 31 | end 32 | 33 | 34 | ############################################ 35 | # Framebuffers and the like 36 | 37 | immutable RenderBuffer 38 | id::GLuint 39 | format::GLenum 40 | function RenderBuffer(format, dimension) 41 | @assert length(dimensions) == 2 42 | id = GLuint[0] 43 | glGenRenderbuffers(1, id) 44 | glBindRenderbuffer(GL_RENDERBUFFER, id[1]) 45 | glRenderbufferStorage(GL_RENDERBUFFER, format, dimension...) 46 | new(id, format) 47 | end 48 | end 49 | function resize!(rb::RenderBuffer, newsize::AbstractArray) 50 | if length(newsize) != 2 51 | error("RenderBuffer needs to be 2 dimensional. Dimension found: ", newsize) 52 | end 53 | glBindRenderbuffer(GL_RENDERBUFFER, rb.id) 54 | glRenderbufferStorage(GL_RENDERBUFFER, rb.format, newsize...) 55 | end 56 | 57 | immutable FrameBuffer{T} 58 | id::GLuint 59 | attachments::Vector{Any} 60 | 61 | function FrameBuffer(dimensions::Input) 62 | fb = glGenFramebuffers() 63 | glBindFramebuffer(GL_FRAMEBUFFER, fb) 64 | end 65 | end 66 | function resize!(fbo::FrameBuffer, newsize::AbstractArray) 67 | if length(newsize) != 2 68 | error("FrameBuffer needs to be 2 dimensional. Dimension found: ", newsize) 69 | end 70 | for elem in fbo.attachments 71 | resize!(elem) 72 | end 73 | 74 | end 75 | 76 | ######################################################################################## 77 | 78 | #= 79 | immutable Texture{T <: TEXTURE_COMPATIBLE_NUMBER_TYPES, ColorDIM, NDIM} 80 | id::GLuint 81 | pixeltype::GLenum 82 | internalformat::GLenum 83 | format::GLenum 84 | dims::Vector{Int} 85 | end 86 | =# 87 | include("GLTexture.jl") 88 | ######################################################################## 89 | 90 | opengl_compatible{C <: AbstractAlphaColorValue}(T::Type{C}) = eltype(T), 4 91 | opengl_compatible{C <: RGB4}(T::Type{C}) = eltype(T), 4 92 | 93 | opengl_compatible{C <: ColorValue}(T::Type{C}) = eltype(T), 3 94 | opengl_compatible{C <: AbstractGray}(T::Type{C}) = eltype(T), 1 95 | 96 | function opengl_compatible(T::DataType) 97 | if T <: Number 98 | return T, 1 99 | end 100 | if !isbits(T) 101 | error("only pointer free, immutable types are supported for upload to OpenGL. Found type: $(T)") 102 | end 103 | elemtype = T.types[1] 104 | if !(elemtype <: Real) 105 | error("only real numbers are allowed as element types for upload to OpenGL. Found type: $(T) with $(ptrtype)") 106 | end 107 | if !all(x -> x == elemtype , T.types) 108 | error("all values in $(T) need to have the same type to create a GLBuffer") 109 | end 110 | cardinality = length(names(T)) 111 | if cardinality > 4 112 | error("there should be at most 4 values in $(T) to create a GLBuffer") 113 | end 114 | elemtype, cardinality 115 | end 116 | 117 | type GLBuffer{T <: Real, Cardinality} 118 | id::GLuint 119 | length::Int 120 | buffertype::GLenum 121 | usage::GLenum 122 | 123 | function GLBuffer(ptr::Ptr{T}, size::Int, buffertype::GLenum, usage::GLenum) 124 | @assert size % sizeof(T) == 0 125 | _length = div(size, sizeof(T)) 126 | @assert _length % Cardinality == 0 127 | _length = div(_length, Cardinality) 128 | 129 | id = glGenBuffers() 130 | glBindBuffer(buffertype, id) 131 | glBufferData(buffertype, size, ptr, usage) 132 | glBindBuffer(buffertype, 0) 133 | 134 | obj = new(id, _length, buffertype, usage) 135 | end 136 | end 137 | include("GLBuffer.jl") 138 | 139 | type GLVertexArray 140 | program::GLProgram 141 | id::GLuint 142 | length::Int 143 | indexlength::Int # is negative if not indexed 144 | 145 | function GLVertexArray(bufferDict::Dict{Symbol, GLBuffer}, program::GLProgram) 146 | @assert !isempty(bufferDict) 147 | #get the size of the first array, to assert later, that all have the same size 148 | indexSize = -1 149 | _length = -1 150 | id = glGenVertexArrays() 151 | glBindVertexArray(id) 152 | for (name, value) in bufferDict 153 | buffer = value 154 | if buffer.buffertype == GL_ELEMENT_ARRAY_BUFFER 155 | glBindBuffer(buffer.buffertype, buffer.id) 156 | indexSize = buffer.length * cardinality(buffer) 157 | else 158 | attribute = string(name) 159 | if _length == -1 160 | _length = length(buffer) 161 | end 162 | if _length != length(buffer) 163 | error("buffer $attribute has not the same length as the other buffers. Has: $(buffer.length). Should have: $_length") 164 | end 165 | glBindBuffer(buffer.buffertype, buffer.id) 166 | attribLocation = get_attribute_location(program.id, attribute) 167 | 168 | glVertexAttribPointer(attribLocation, cardinality(buffer), GL_FLOAT, GL_FALSE, 0, C_NULL) 169 | glEnableVertexAttribArray(attribLocation) 170 | end 171 | end 172 | glBindVertexArray(0) 173 | new(program, id, _length, indexSize) 174 | end 175 | end 176 | function GLVertexArray(bufferDict::Dict{ASCIIString, GLBuffer}, program::GLProgram) 177 | GLVertexArray(Dict{Symbol, GLBuffer}(map(elem -> (symbol(elem[1]), elem[2]), bufferDict)), program) 178 | end 179 | 180 | ################################################################################## 181 | 182 | type RenderObject 183 | uniforms::Dict{Symbol, Any} 184 | alluniforms::Dict{Symbol, Any} 185 | vertexarray::GLVertexArray 186 | prerenderfunctions::Dict{Function, Tuple} 187 | postrenderfunctions::Dict{Function, Tuple} 188 | id::GLushort 189 | boundingbox::Function # workaround for having lazy boundingbox queries, while not using multiple dispatch for boundingbox function (No type hierarchy for RenderObjects) 190 | 191 | objectid::GLushort = 0 192 | 193 | function RenderObject(data::Dict{Symbol, Any}, program::GLProgram, bbf::Function=(x)->error("boundingbox not implemented")) 194 | objectid::GLushort += 1 195 | 196 | buffers = filter((key, value) -> isa(value, GLBuffer), data) 197 | uniforms = filter((key, value) -> !isa(value, GLBuffer), data) 198 | uniforms[:objectid] = objectid # automatucally integrate object ID, will be discarded if shader doesn't use it 199 | 200 | if length(buffers) > 0 201 | vertexarray = GLVertexArray(Dict{Symbol, GLBuffer}(buffers), program) 202 | else 203 | error("no buffers supplied") 204 | end 205 | uniformtypesandnames = uniform_name_type(program.id) # get active uniforms and types from program 206 | optimizeduniforms = Dict{Symbol, Any}() 207 | for (uniform_name, typ) in uniformtypesandnames 208 | if haskey(uniforms, uniform_name) 209 | optimizeduniforms[uniform_name] = uniforms[uniform_name] 210 | end 211 | end # only use active uniforms && check the type 212 | new(optimizeduniforms, uniforms, vertexarray, Dict{Function, Tuple}(), Dict{Function, Tuple}(), objectid, bbf) 213 | end 214 | end 215 | function Base.show(io::IO, obj::RenderObject) 216 | println(io, "RenderObject with ID: ", obj.id) 217 | 218 | println(io, "uniforms: ") 219 | for (name, uniform) in obj.uniforms 220 | println(io, " ", name, "\n ", uniform) 221 | end 222 | println(io, "vertexarray length: ", obj.vertexarray.length) 223 | println(io, "vertexarray indexlength: ", obj.vertexarray.indexlength) 224 | end 225 | RenderObject{T}(data::Dict{Symbol, T}, program::GLProgram) = RenderObject(Dict{Symbol, Any}(data), program) 226 | 227 | immutable Field{Symbol} 228 | end 229 | 230 | Base.getindex(obj::RenderObject, symbol::Symbol) = obj.uniforms[symbol] 231 | Base.setindex!(obj::RenderObject, value, symbol::Symbol) = obj.uniforms[symbol] = value 232 | 233 | Base.getindex(obj::RenderObject, symbol::Symbol, x::Function) = getindex(obj, Field{symbol}(), x) 234 | Base.getindex(obj::RenderObject, ::Field{:prerender}, x::Function) = obj.prerenderfunctions[x] 235 | Base.getindex(obj::RenderObject, ::Field{:postrender}, x::Function) = obj.postrenderfunctions[x] 236 | 237 | Base.setindex!(obj::RenderObject, value, symbol::Symbol, x::Function) = setindex!(obj, value, Field{symbol}(), x) 238 | Base.setindex!(obj::RenderObject, value, ::Field{:prerender}, x::Function) = obj.prerenderfunctions[x] = value 239 | Base.setindex!(obj::RenderObject, value, ::Field{:postrender}, x::Function) = obj.postrenderfunctions[x] = value 240 | 241 | function instancedobject(data, amount::Integer, program::GLProgram, primitive::GLenum=GL_TRIANGLES, bbf::Function=(x)->error("boundingbox not implemented")) 242 | obj = RenderObject(data, program, bbf) 243 | postrender!(obj, renderinstanced, obj.vertexarray, amount, primitive) 244 | obj 245 | end 246 | 247 | function pushfunction!(target::Dict{Function, Tuple}, fs...) 248 | func = fs[1] 249 | args = Any[] 250 | for i=2:length(fs) 251 | elem = fs[i] 252 | if isa(elem, Function) 253 | target[func] = tuple(args...) 254 | func = elem 255 | args = Any[] 256 | else 257 | push!(args, elem) 258 | end 259 | end 260 | target[func] = tuple(args...) 261 | end 262 | prerender!(x::RenderObject, fs...) = pushfunction!(x.prerenderfunctions, fs...) 263 | postrender!(x::RenderObject, fs...) = pushfunction!(x.postrenderfunctions, fs...) 264 | 265 | function Base.delete!(x::Any) 266 | x = 0 267 | end 268 | function Base.delete!(x::Dict) 269 | for (k,v) in x 270 | if !contains(string(k), "dontdelete") 271 | delete!(v) 272 | delete!(x, k) 273 | end 274 | end 275 | end 276 | function Base.delete!(x::Array) 277 | while !isempty(x) 278 | elem = pop!(x) 279 | delete!(elem) 280 | end 281 | end 282 | function Base.delete!(x::GLProgram) 283 | glDeleteProgram(x.id) 284 | end 285 | function Base.delete!(x::GLBuffer) 286 | glDeleteBuffers(1, [x.id]) 287 | end 288 | function Base.delete!(x::Texture) 289 | glDeleteTextures(1, [x.id]) 290 | end 291 | function Base.delete!(x::GLVertexArray) 292 | glDeleteVertexArrays(1, [x.id]) 293 | end 294 | function Base.delete!(x::RenderObject) 295 | delete!(x.uniforms) 296 | delete!(x.vertexarray) 297 | end 298 | 299 | 300 | 301 | #################################################################################### 302 | 303 | #= 304 | Style Type, which is used to choose different visualization/editing styles via multiple dispatch 305 | Usage pattern: 306 | visualize(::Style{:Default}, ...) = do something 307 | visualize(::Style{:MyAwesomeNewStyle}, ...) = do something different 308 | =# 309 | immutable Style{StyleValue} 310 | end 311 | Style(x::Symbol) = Style{x}() 312 | Style() = Style{:Default}() 313 | mergedefault!{S}(style::Style{S}, styles, customdata) = merge!(copy(styles[S]), Dict{Symbol, Any}(customdata)) 314 | -------------------------------------------------------------------------------- /src/GLCamera.jl: -------------------------------------------------------------------------------- 1 | abstract Camera{T} 2 | immutable OrthographicCamera{T} <: Camera{T} 3 | window_size::Signal{Vector4{Int}} 4 | view::Signal{Matrix4x4{T}} 5 | projection::Signal{Matrix4x4{T}} 6 | projectionview::Signal{Matrix4x4{T}} 7 | end 8 | immutable PerspectiveCamera{T} <: Camera{T} 9 | pivot::Signal{Pivot{T}} 10 | window_size::Signal{Vector4{Int}} 11 | nearclip::Signal{T} 12 | farclip::Signal{T} 13 | fov::Signal{T} 14 | view::Signal{Matrix4x4{T}} 15 | projection::Signal{Matrix4x4{T}} 16 | projectionview::Signal{Matrix4x4{T}} 17 | normalmatrix::Signal{Matrix3x3{T}} 18 | eyeposition::Signal{Vector3{T}} 19 | lookat::Signal{Vector3{T}} 20 | up::Signal{Vector3{T}} 21 | end 22 | 23 | 24 | 25 | function mousediff{T}(v0::(Bool, Vector2{T}, Vector2{T}), clicked::Bool, pos::Vector2{T}) 26 | clicked0, pos0, pos0diff = v0 27 | if clicked0 && clicked 28 | return (clicked, pos, pos - pos0) 29 | end 30 | return (clicked, pos, Vector2(0.0)) 31 | end 32 | 33 | 34 | #= 35 | Creates an orthographic camera with the pixel perfect plane in z == 0 36 | Signals needed: 37 | [ 38 | :window_size => Input(Vector2{Int}), 39 | :buttonspressed => Input(IntSet()), 40 | :mousebuttonspressed => Input(IntSet()), 41 | :mouseposition => mouseposition, -> Panning 42 | :scroll_y => Input(0) -> Zoomig 43 | ] 44 | =# 45 | function OrthographicPixelCamera(inputs::Dict{Symbol, Any}) 46 | 47 | mouseposition = inputs[:mouseposition] 48 | buttonspressed = inputs[:buttonspressed] 49 | 50 | #Should be rather in Image coordinates 51 | view = foldl(eye(Mat4), 52 | inputs[:scroll_x], inputs[:scroll_y], buttonspressed) do v0, scroll_x, scroll_y, buttonset 53 | 54 | translatevec = Vec3(0f0) 55 | if scroll_x == 0f0 56 | if in(341, buttonset) # left strg 57 | translatevec = Vec3(scroll_y*10f0, 0f0, 0f0) 58 | else 59 | translatevec = Vec3(0f0, scroll_y*10f0, 0f0) 60 | end 61 | else 62 | translatevec = Vec3(scroll_x*10f0, scroll_y*10f0, 0f0) 63 | end 64 | v0 * translationmatrix(translatevec) 65 | end 66 | 67 | OrthographicCamera( 68 | inputs[:window_size], 69 | view, 70 | Input(-10f0), # nearclip 71 | Input(10f0) # farclip 72 | ) 73 | 74 | end 75 | 76 | #= 77 | Creates an orthographic camera from a dict of signals 78 | Signals needed: 79 | [ 80 | :window_size => Input(Vector2{Int}), 81 | :buttonspressed => Input(IntSet()), 82 | :mousebuttonspressed => Input(IntSet()), 83 | :mouseposition => mouseposition, -> Panning 84 | :scroll_y => Input(0) -> Zoomig 85 | ] 86 | =# 87 | function OrthographicCamera(inputs::Dict{Symbol, Any}) 88 | 89 | mouseposition = inputs[:mouseposition] 90 | clicked = inputs[:mousebuttonspressed] 91 | keypressed = inputs[:buttonspressed] 92 | 93 | zoom = foldl((a,b) -> float32(a+(b*0.1f0)) , 1.0f0, inputs[:scroll_y]) 94 | 95 | #Should be rather in Image coordinates 96 | normedposition = lift((a,b) -> Vector2((a./b[3:4])...), inputs[:mouseposition], inputs[:window_size]) 97 | clickedwithoutkeyL = lift((mb, kb) -> in(0, mb) && isempty(kb), Bool, clicked, keypressed) 98 | translate = lift(x-> float32(x[3]), Vec2, # Extract the mouseposition from the diff tuple 99 | keepwhen(clickedwithoutkeyL, (false, Vector2(0.0), Vector2(0.0)), # Don't do unnecessary updates, so just signal when mouse is actually clicked 100 | foldl(mousediff, (false, Vector2(0.0), Vector2(0.0)), # Get the difference, starting when the mouse is down 101 | clickedwithoutkeyL, normedposition))) 102 | OrthographicCamera( 103 | inputs[:window_size], 104 | zoom, 105 | translate, 106 | normedposition 107 | ) 108 | 109 | end 110 | #= 111 | Creates an orthographic camera from signals, controlling the camera 112 | Args: 113 | 114 | window_size: Size of the window 115 | zoom: Zoom 116 | translatevec: Panning 117 | normedposition: Pivot for translations 118 | 119 | =# 120 | function OrthographicCamera{T}( 121 | windows_size::Signal{Vector4{Int}}, 122 | view::Signal{Matrix4x4{T}}, 123 | 124 | nearclip::Signal{T}, 125 | farclip::Signal{T} 126 | ) 127 | 128 | projection = lift(Matrix4x4{T}, 129 | windows_size, nearclip, farclip) do wh, near, far 130 | 131 | left, bottom, right, top = (zero(T), zero(T), convert(T, wh[3]), convert(T, wh[4])) 132 | 133 | if (right != left && 134 | bottom != top && 135 | near != far) 136 | return orthographicprojection(left, right, bottom, top, near, far) 137 | else 138 | return eye(Matrix4x4{T}) 139 | end 140 | end 141 | #projection = Input(eye(Mat4)) 142 | #view = Input(eye(Mat4)) 143 | projectionview = lift(*, Matrix4x4{T}, projection, view) 144 | 145 | OrthographicCamera{T}( 146 | windows_size, 147 | view, 148 | projection, 149 | projectionview 150 | ) 151 | 152 | end 153 | #= 154 | Creates an orthographic camera from signals, controlling the camera 155 | Args: 156 | 157 | window_size: Size of the window 158 | zoom: Zoom 159 | translatevec: Panning 160 | normedposition: Pivot for translations 161 | 162 | =# 163 | function OrthographicCamera{T}( 164 | windows_size::Signal{Vector4{Int}}, 165 | zoom::Signal{T}, 166 | translatevec::Signal{Vector2{T}}, 167 | normedposition::Signal{Vector2{Float64}} 168 | ) 169 | 170 | projection = lift(Matrix4x4{T}, windows_size) do wh 171 | if wh[3] < 1 || wh[4] < 1 172 | return eye(Matrix4x4{T}) 173 | end 174 | # change the aspect ratio, to always display an image with the right dimensions 175 | # this behaviour should definitely be changed, as soon as the camera is used for anything else. 176 | wh = wh[3] > wh[4] ? ((wh[3]/wh[4]), 1f0) : (1f0,(wh[4]/wh[3])) 177 | orthographicprojection(0f0, convert(T, wh[1]), 0f0, convert(T, wh[2]), -1f0, 10f0) 178 | end 179 | 180 | scale = lift(x -> scalematrix(Vector3{T}(x, x, one(T))), zoom) 181 | transaccum = foldl(+, Vector2(zero(T)), translatevec) 182 | translate = lift(x-> translationmatrix(Vector3(x..., zero(T))), transaccum) 183 | 184 | view = lift((s, t) -> begin 185 | pivot = Vec3(normedposition.value..., zero(T)) 186 | translationmatrix(pivot)*s*translationmatrix(-pivot)*t 187 | end, Matrix4x4{T}, scale, translate) 188 | 189 | projectionview = lift(*, Matrix4x4{T}, projection, view) 190 | 191 | OrthographicCamera{T}( 192 | windows_size, 193 | projection, 194 | view, 195 | projectionview 196 | ) 197 | 198 | end 199 | 200 | 201 | #= 202 | Creates a perspective camera from a dict of signals 203 | 204 | Args: 205 | 206 | inputs: Dict of signals, looking like this: 207 | [ 208 | :window_size => Input(Vector2{Int}), 209 | :buttonspressed => Input(IntSet()), 210 | :mousebuttonspressed => Input(IntSet()), 211 | :mouseposition => mouseposition, -> Panning + Rotation 212 | :scroll_y => Input(0) -> Zoomig 213 | ] 214 | eyeposition: Position of the camera 215 | lookatvec: Point the camera looks at 216 | =# 217 | function PerspectiveCamera{T}(inputs::Dict{Symbol,Any}, eyeposition::Vector3{T}, lookatvec::Vector3{T}) 218 | 219 | mouseposition = inputs[:mouseposition] 220 | clicked = inputs[:mousebuttonspressed] 221 | keypressed = inputs[:buttonspressed] 222 | 223 | clickedwithoutkeyL = lift((mb, kb) -> in(0, mb) && isempty(kb), Bool, clicked, keypressed) 224 | clickedwithoutkeyM = lift((mb, kb) -> in(2, mb) && isempty(kb), Bool, clicked, keypressed) 225 | 226 | nokeydown = lift((kb) -> isempty(kb), Bool, keypressed) 227 | anymousedown = lift((mb) -> !isempty(mb), Bool, clicked) 228 | 229 | mousedraggdiffL = lift(x->x[3], Vector2{Float64}, foldl(mousediff, (false, Vector2(0.0), Vector2(0.0)), clickedwithoutkeyL, mouseposition)) 230 | mousedraggdiffM = lift(x->x[3], Vector2{Float64}, foldl(mousediff, (false, Vector2(0.0), Vector2(0.0)), clickedwithoutkeyM, mouseposition)) 231 | 232 | speed = 50f0 233 | xtheta = Input(0f0) 234 | ytheta = lift(x-> float32(-x[2]) / speed, Float32, mousedraggdiffL) 235 | ztheta = lift(x-> float32(x[1]) / speed, Float32, mousedraggdiffL) 236 | 237 | 238 | xtrans = lift(x-> float32(x*0.1f0), Float32, inputs[:scroll_y]) 239 | ytrans = lift(x-> -float32(x[1]) / speed, Float32, mousedraggdiffM) 240 | ztrans = lift(x-> float32(x[2]) / speed, Float32, mousedraggdiffM) 241 | 242 | fov = Input(41f0) 243 | 244 | cam = PerspectiveCamera( 245 | inputs[:window_size], 246 | 247 | eyeposition, 248 | lookatvec, 249 | 250 | xtheta, 251 | ytheta, 252 | ztheta, 253 | 254 | xtrans, 255 | ytrans, 256 | ztrans, 257 | 258 | Input(41f0), 259 | Input(1f0), 260 | Input(100f0) 261 | ) 262 | end 263 | 264 | 265 | 266 | #= 267 | Creates a perspective camera from signals, controlling the camera 268 | Args: 269 | 270 | window_size: Size of the window 271 | zoom: Zoom 272 | eyeposition: Position of the camera 273 | lookatvec: Point the camera looks at 274 | 275 | xtheta: xrotation angle 276 | ytheta: yrotation angle 277 | ztheta: zrotation angle 278 | 279 | xtrans: x translation 280 | ytrans: y translation 281 | ztrans: z translation 282 | fov: Field of View 283 | nearclip: Near clip plane 284 | farclip: Far clip plane 285 | 286 | =# 287 | function PerspectiveCamera{T <: Real}( 288 | window_size::Signal{Vector4{Int}},# = iVec2(50,50), 289 | 290 | eyeposition::Vector3{T}, 291 | lookatvec::Vector3{T}, 292 | xtheta::Signal{T}, 293 | ytheta::Signal{T}, 294 | ztheta::Signal{T}, 295 | 296 | xtrans::Signal{T}, 297 | ytrans::Signal{T}, 298 | ztrans::Signal{T}, 299 | 300 | fov::Signal{T}, 301 | 302 | nearclip::Signal{T}, 303 | farclip::Signal{T} 304 | ) 305 | eyepositionstart = Vector3{T}(1,0,0) 306 | origin = lookatvec 307 | vup = Vector3{T}(0,0,1) 308 | xaxis = eyeposition - origin 309 | yaxis = cross(xaxis, vup) 310 | zaxis = cross(yaxis, xaxis) 311 | 312 | translate = Vector3{T}(0,0,0) 313 | 314 | 315 | p0 = Pivot(origin, xaxis, yaxis, zaxis, Quaternion(1f0,0f0,0f0,0f0), translate, Vector3{T}(1)) 316 | 317 | 318 | pivot = foldl((v0, v1) -> begin 319 | xt, yt, zt, xtr, ytr, ztr = v1 320 | 321 | xaxis = v0.rotation * v0.xaxis # rotate the axis 322 | yaxis = v0.rotation * v0.yaxis 323 | zaxis = v0.rotation * v0.zaxis 324 | 325 | xrot = qrotation(xaxis, xt) 326 | yrot = qrotation(yaxis, yt) 327 | zrot = qrotation(vup, zt) 328 | 329 | v1rot = zrot*xrot*yrot*v0.rotation 330 | 331 | v1trans = yaxis*ytr + zaxis*ztr 332 | accumtrans = v1trans + v0.translation 333 | 334 | Pivot(v0.origin + v1trans, v0.xaxis, v0.yaxis, v0.zaxis, v1rot, accumtrans + v0.xaxis*xtr, v0.scale) 335 | 336 | end, p0, lift(tuple, xtheta, ytheta, ztheta, xtrans, ytrans, ztrans)) 337 | 338 | modelmatrix = lift(transformationmatrix, Matrix4x4{T}, pivot) 339 | positionvec = lift((m,v) -> (r=(convert(Array,m)*T[v...,1]) ; Vector3(r[1:3])), Vector3{T}, modelmatrix, Input(eyeposition)) 340 | up = lift(p->p.rotation * p.zaxis , Vector3{T}, pivot) 341 | lookatvec1 = lift(p->p.origin , Vector3{T}, pivot) 342 | 343 | view = lift(lookat, Matrix4x4{T}, positionvec, lookatvec1, up) 344 | 345 | window_ratio = lift(x -> x[3] / x[4], T, window_size) 346 | projection = lift(perspectiveprojection, Matrix4x4{T}, fov, window_ratio, nearclip, farclip) 347 | 348 | projectionview = lift(*, Matrix4x4{T}, projection, view) 349 | 350 | normalmatrix = lift(x -> inv(Matrix3x3(x))', Matrix3x3{T}, projectionview) 351 | 352 | return PerspectiveCamera{T}( 353 | pivot, 354 | window_size, 355 | nearclip, 356 | farclip, 357 | fov, 358 | view, 359 | projection, 360 | projectionview, 361 | normalmatrix, 362 | positionvec, 363 | lookatvec1, 364 | up 365 | ) 366 | end 367 | 368 | 369 | -------------------------------------------------------------------------------- /src/GLUniforms.jl: -------------------------------------------------------------------------------- 1 | # Uniforms are OpenGL variables that stay the same for the entirety of a drawcall. 2 | # There are a lot of functions, to upload them, as OpenGL doesn't rely on multiple dispatch. 3 | # here is my approach, to handle all of the uniforms with one function, namely gluniform 4 | # For uniforms, the Vector and Matrix types from ImmutableArrays should be used, as they map the relation almost 1:1 5 | 6 | GLSL_COMPATIBLE_NUMBER_TYPES = [GLdouble, GLfloat, GLint, GLuint] 7 | 8 | GLSL_PREFIX = @compat Dict( 9 | GLdouble => "d", 10 | GLfloat => "", 11 | GLint => "i", 12 | GLuint => "u", 13 | Uint8 => "u", 14 | Uint16 => "u" 15 | ) 16 | 17 | GL_POSTFIX = @compat Dict( 18 | GLdouble => "dv", 19 | GLfloat => "fv", 20 | GLint => "iv", 21 | GLuint => "uiv" 22 | ) 23 | 24 | # Generates uniform upload functions for ImmutableArrays. 25 | # Also it defines glsl alike aliases and constructors. 26 | # This probably shouldn't be done in the same function, but its for now the easiest solution. 27 | macro genuniformfunctions(maxdim::Integer) 28 | glslVector = "Vec" 29 | glslMatrix = "Mat" 30 | 31 | imVector = "Vector" 32 | imMatrix = "Matrix" 33 | expressions = Any[] 34 | 35 | for n=1:maxdim, typ in GLSL_COMPATIBLE_NUMBER_TYPES 36 | glslalias = symbol(string(GLSL_PREFIX[typ],glslVector,n)) 37 | name = symbol(string(imVector, n)) 38 | imalias = :($name {$typ}) 39 | uniformfunc = symbol(string("glUniform", n, GL_POSTFIX[typ])) 40 | if n == 1 # define also single valued uniform functions 41 | uniformfunc = symbol(string("glUniform", n, chop(GL_POSTFIX[typ]))) 42 | push!(expressions, :(gluniform(location::GLint, x::$typ) = $uniformfunc(location, x))) 43 | end 44 | push!(expressions, :(typealias $glslalias $imalias)) # glsl alike type alias 45 | push!(expressions, :(gluniform(location::Integer, x::$imalias) = (tmp = [x;] ; $uniformfunc(location, 1, pointer(tmp))))) # uniform function for single uniforms 46 | push!(expressions, :(gluniform(location::Integer, x::Vector{$imalias}) = $uniformfunc(location, length(x), pointer(x)))) #uniform function for arrays of uniforms 47 | if n > 1 48 | push!(expressions, :($glslalias(x::Real) = $name(convert($typ, x)))) # Single valued constructor 49 | end 50 | push!(expressions, Expr(:export, glslalias)) 51 | 52 | 53 | ######################################################################### 54 | if n != 1 55 | push!(expressions, :(toglsltype_string(x::Type{$imalias}) = $(lowercase(string("uniform ", glslalias))))) # method for shader type mapping 56 | push!(expressions, :(toglsltype_string(x::$imalias) = $(lowercase(string("uniform ", glslalias))))) # method for shader type mapping 57 | end 58 | end 59 | for n=2:maxdim, n2=2:maxdim, typ in [GLdouble, GLfloat] 60 | glsldim = n==n2 ? "$n" : "$(n)x$(n2)" 61 | glslalias = symbol(string(GLSL_PREFIX[typ], glslMatrix, glsldim)) 62 | name = symbol(string(imMatrix, n,"x",n2)) 63 | imalias = :($name {$typ}) 64 | uniformfunc = symbol(string("glUniformMatrix", glsldim, GL_POSTFIX[typ])) 65 | 66 | push!(expressions, :(typealias $glslalias $imalias)) #GLSL alike alias 67 | push!(expressions, :(gluniform(location::Integer, x::$imalias) = (tmp = [x;] ; $uniformfunc(location, 1, GL_FALSE, pointer(tmp))))) # uniform function for single uniforms 68 | push!(expressions, :(gluniform(location::Integer, x::Vector{$imalias}) = $uniformfunc(location, length(x), GL_FALSE, pointer(x)))) #uniform function for arrays of uniforms 69 | push!(expressions, :($glslalias(x::Real) = $name(convert($typ, x)))) # Single valued constructor 70 | push!(expressions, Expr(:export, glslalias)) 71 | ######################################################################### 72 | push!(expressions, :(toglsltype_string(x::$imalias) = $(lowercase(string("uniform ", glslalias))))) # method for shader type mapping 73 | end 74 | return esc(Expr(:block, expressions...)) 75 | end 76 | 77 | # Extend Vector class a bit 78 | Base.length{T}(::Type{Vector1{T}}) = 1 79 | Base.length{T}(::Type{Vector2{T}}) = 2 80 | Base.length{T}(::Type{Vector3{T}}) = 3 81 | Base.length{T}(::Type{Vector4{T}}) = 4 82 | 83 | Base.size{T}(::Type{Matrix4x4{T}}) = (4,4) 84 | Base.size{T}(::Type{Matrix3x3{T}}) = (3,3) 85 | Base.size{T}(::Type{Matrix2x2{T}}) = (2,2) 86 | 87 | Base.size{T}(::Type{Matrix2x4{T}}) = (2,4) 88 | Base.size{T}(::Type{Matrix3x4{T}}) = (3,4) 89 | 90 | Base.size{T}(::Type{Matrix2x3{T}}) = (2,3) 91 | Base.size{T}(::Type{Matrix4x3{T}}) = (4,3) 92 | 93 | Base.size{T}(::Type{Matrix3x2{T}}) = (3,2) 94 | Base.size{T}(::Type{Matrix4x2{T}}) = (4,2) 95 | 96 | @genuniformfunctions 4 97 | 98 | #Some additional uniform functions, not related to Imutable Arrays 99 | gluniform(location::Integer, target::Integer, t::Texture) = gluniform(convert(GLint, location), convert(GLint, target), t) 100 | gluniform(location::Integer, target::Integer, t::Signal) = gluniform(convert(GLint, location), convert(GLint, target), t.value) 101 | function gluniform(location::GLint, target::GLint, t::Texture) 102 | activeTarget = GL_TEXTURE0 + uint32(target) 103 | glActiveTexture(activeTarget) 104 | glBindTexture(t.texturetype, t.id) 105 | gluniform(location, target) 106 | end 107 | gluniform(location::Integer, x::Signal) = gluniform(location, x.value) 108 | 109 | gluniform(location::Integer, x::Union(GLubyte, GLushort, GLuint)) = glUniform1ui(location, x) 110 | gluniform(location::Integer, x::Union(GLbyte, GLshort, GLint, Bool)) = glUniform1i(location, x) 111 | 112 | # Needs to be 113 | gluniform(location::Integer, x::RGB{Float32}) = (tmp = [x;] ; glUniform3fv(location, 1, convert(Ptr{Float32}, pointer(tmp)))) 114 | gluniform(location::Integer, x::AlphaColorValue{RGB{Float32}, Float32}) = (tmp = [x;] ; glUniform4fv(location, 1, convert(Ptr{Float32}, pointer(tmp)))) 115 | 116 | gluniform{T <: AbstractRGB}(location::Integer, x::Vector{T}) = gluniform(location, reinterpret(Vector3{eltype(T)}, x)) 117 | gluniform{T <: AbstractAlphaColorValue}(location::Integer, x::Vector{T}) = gluniform(location, reinterpret(Vector4{eltype(T)}, x)) 118 | 119 | #Uniform upload functions for julia arrays... 120 | gluniform(location::GLint, x::Vector{Float32}) = glUniform1fv(location, length(x), pointer(x)) 121 | gluniform(location::GLint, x::Vector{GLint}) = glUniform1iv(location, length(x), pointer(x)) 122 | gluniform(location::GLint, x::Vector{GLuint}) = glUniform1uiv(location, length(x), pointer(x)) 123 | 124 | 125 | glsl_prefix(x::DataType) = GLSL_PREFIX[x] 126 | glsl_prefix{T <: FixedPoint}(x::Type{T}) = "" 127 | 128 | toglsltype_string{T, C, D}(t::Texture{T, C, D}) = string("uniform ", glsl_prefix(eltype(T)),"sampler", D, "D") 129 | toglsltype_string(t::GLfloat) = "uniform float" 130 | toglsltype_string(t::GLuint) = "uniform uint" 131 | toglsltype_string(t::GLint) = "uniform int" 132 | toglsltype_string(t::Signal) = toglsltype_string(t.value) 133 | toglsltype_string(t::StepRange) = toglsltype_string(Vec3(first(t), step(t), last(t))) 134 | 135 | toglsltype_string(t::AbstractAlphaColorValue) = toglsltype_string(Vec4) 136 | toglsltype_string(t::AbstractRGB) = toglsltype_string(Vec3) 137 | 138 | function toglsltype_string(t::GLBuffer) 139 | typ = cardinality(t) > 1 ? "vec$(cardinality(t))" : "float" 140 | "$(get_glsl_in_qualifier_string()) $typ" 141 | end 142 | 143 | 144 | 145 | UNIFORM_TYPES = Union(AbstractArray, ColorValue, AbstractAlphaColorValue) 146 | 147 | # This is needed to varify, that the correct uniform is uploaded to a shader 148 | # Should partly be integrated into the genuniformfunctions macro 149 | is_correct_uniform_type(a, b) = false 150 | 151 | is_unsigned_uniform_type{T}(::Type{T}) = eltype(T) <: Unsigned 152 | is_integer_uniform_type{T}(::Type{T}) = eltype(T) <: Integer 153 | is_float_uniform_type{T}(::Type{T}) = eltype(T) <: FloatingPoint || eltype(T) <: FixedPoint 154 | is_bool_uniform_type{T}(::Type{T}) = eltype(T) <: Bool || is_integer_uniform_type(T) 155 | 156 | 157 | is_correct_uniform_type{AnySym}(x::Signal, glenum::GLENUM{AnySym, GLenum}) = is_correct_uniform_type(x.value, glenum) 158 | 159 | is_correct_uniform_type(x::Real, ::GLENUM{:GL_BOOL, GLenum}) = is_bool_uniform_type(typeof(x)) 160 | is_correct_uniform_type(x::Real, ::GLENUM{:GL_UNSIGNED_INT, GLenum}) = is_unsigned_uniform_type(typeof(x)) 161 | is_correct_uniform_type(x::Real, ::GLENUM{:GL_INT, GLenum}) = is_integer_uniform_type(typeof(x)) 162 | is_correct_uniform_type(x::Real, ::GLENUM{:GL_FLOAT, GLenum}) = is_float_uniform_type(typeof(x)) 163 | 164 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_BOOL, GLenum}) = is_bool_uniform_type(T) && length(T) == 1 165 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_BOOL_VEC2, GLenum}) = is_bool_uniform_type(T) && length(T) == 2 166 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_BOOL_VEC3, GLenum}) = is_bool_uniform_type(T) && length(T) == 3 167 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_BOOL_VEC4, GLenum}) = is_bool_uniform_type(T) && length(T) == 4 168 | 169 | 170 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_UNSIGNED_INT, GLenum}) = is_unsigned_uniform_type(T) && length(T) == 1 171 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_UNSIGNED_INT_VEC2, GLenum}) = is_unsigned_uniform_type(T) && length(T) == 2 172 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_UNSIGNED_INT_VEC3, GLenum}) = is_unsigned_uniform_type(T) && length(T) == 3 173 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_UNSIGNED_INT_VEC4, GLenum}) = is_unsigned_uniform_type(T) && length(T) == 4 174 | 175 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_INT, GLenum}) = is_integer_uniform_type(T) && length(T) == 1 176 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_INT_VEC2, GLenum}) = is_integer_uniform_type(T) && length(T) == 2 177 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_INT_VEC3, GLenum}) = is_integer_uniform_type(T) && length(T) == 3 178 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_INT_VEC4, GLenum}) = is_integer_uniform_type(T) && length(T) == 4 179 | 180 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_FLOAT, GLenum}) = is_float_uniform_type(T) && length(T) == 1 181 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_FLOAT_VEC2, GLenum}) = is_float_uniform_type(T) && length(T) == 2 182 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_FLOAT_VEC3, GLenum}) = is_float_uniform_type(T) && length(T) == 3 183 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_FLOAT_VEC4, GLenum}) = is_float_uniform_type(T) && length(T) == 4 184 | 185 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_FLOAT_MAT2, GLenum}) = is_float_uniform_type(T) && size(T) == (2,2) 186 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_FLOAT_MAT3, GLenum}) = is_float_uniform_type(T) && size(T) == (3,3) 187 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_FLOAT_MAT4, GLenum}) = is_float_uniform_type(T) && size(T) == (4,4) 188 | 189 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_FLOAT_MAT3x2, GLenum}) = is_float_uniform_type(T) && size(T) == (3,2) 190 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_FLOAT_MAT4x2, GLenum}) = is_float_uniform_type(T) && size(T) == (4,2) 191 | 192 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_FLOAT_MAT2x3, GLenum}) = is_float_uniform_type(T) && size(T) == (2,3) 193 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_FLOAT_MAT4x3, GLenum}) = is_float_uniform_type(T) && size(T) == (4,3) 194 | 195 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_FLOAT_MAT2x3, GLenum}) = is_float_uniform_type(T) && size(T) == (2,3) 196 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_FLOAT_MAT4x3, GLenum}) = is_float_uniform_type(T) && size(T) == (4,3) 197 | 198 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_FLOAT_MAT2x4, GLenum}) = is_float_uniform_type(T) && size(T) == (2,4) 199 | is_correct_uniform_type{T <: UNIFORM_TYPES}(::T, ::GLENUM{:GL_FLOAT_MAT3x4, GLenum}) = is_float_uniform_type(T) && size(T) == (3,4) 200 | 201 | is_correct_uniform_type{T, C}(::Texture{T, C, 1}, ::GLENUM{:GL_SAMPLER_1D, GLenum}) = is_float_uniform_type(T) 202 | is_correct_uniform_type{T, C}(::Texture{T, C, 2}, ::GLENUM{:GL_SAMPLER_2D, GLenum}) = is_float_uniform_type(T) 203 | is_correct_uniform_type{T, C}(::Texture{T, C, 3}, ::GLENUM{:GL_SAMPLER_3D, GLenum}) = is_float_uniform_type(T) 204 | 205 | is_correct_uniform_type{T, C}(::Texture{T, C, 1}, ::GLENUM{:GL_UNSIGNED_INT_SAMPLER_1D, GLenum}) = is_unsigned_uniform_type(T) 206 | is_correct_uniform_type{T, C}(::Texture{T, C, 2}, ::GLENUM{:GL_UNSIGNED_INT_SAMPLER_2D, GLenum}) = is_unsigned_uniform_type(T) 207 | is_correct_uniform_type{T, C}(::Texture{T, C, 3}, ::GLENUM{:GL_UNSIGNED_INT_SAMPLER_3D, GLenum}) = is_unsigned_uniform_type(T) 208 | 209 | is_correct_uniform_type{T, C}(::Texture{T, C, 1}, ::GLENUM{:GL_INT_SAMPLER_1D, GLenum}) = is_integer_uniform_type(T) 210 | is_correct_uniform_type{T, C}(::Texture{T, C, 2}, ::GLENUM{:GL_INT_SAMPLER_2D, GLenum}) = is_integer_uniform_type(T) 211 | is_correct_uniform_type{T, C}(::Texture{T, C, 3}, ::GLENUM{:GL_INT_SAMPLER_3D, GLenum}) = is_integer_uniform_type(T) 212 | 213 | 214 | 215 | 216 | function uniform_type(targetuniform::GLenum) 217 | if haskey(UNIFORM_TYPE_ENUM_DICT, targetuniform) 218 | return UNIFORM_TYPE_ENUM_DICT[targetuniform] 219 | else 220 | error("Unrecognized Uniform Enum. Enum found: ", GLENUM(targetuniform).name) 221 | end 222 | end 223 | 224 | function uniform_name_type(program::GLuint) 225 | uniformLength = glGetProgramiv(program, GL_ACTIVE_UNIFORMS) 226 | if uniformLength == 0 227 | return () 228 | else 229 | nametypelist = ntuple(uniformLength, i -> glGetActiveUniform(program, i-1)[1:2]) # take size and name 230 | return nametypelist 231 | end 232 | end 233 | function attribute_name_type(program::GLuint) 234 | uniformLength = glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES) 235 | if uniformLength == 0 236 | return () 237 | else 238 | nametypelist = ntuple(uniformLength, i -> glGetActiveAttrib(program, i-1)[1:2]) # take size and name 239 | return nametypelist 240 | end 241 | end 242 | function istexturesampler(typ::GLenum) 243 | return ( 244 | typ == GL_SAMPLER_1D || typ == GL_SAMPLER_2D || typ == GL_SAMPLER_3D || 245 | typ == GL_UNSIGNED_INT_SAMPLER_1D || typ == GL_UNSIGNED_INT_SAMPLER_2D || typ == GL_UNSIGNED_INT_SAMPLER_3D || 246 | typ == GL_INT_SAMPLER_1D || typ == GL_INT_SAMPLER_2D || typ == GL_INT_SAMPLER_3D || 247 | typ == GL_SAMPLER_1D_ARRAY || typ == GL_SAMPLER_2D_ARRAY || 248 | typ == GL_UNSIGNED_INT_SAMPLER_1D_ARRAY || typ == GL_UNSIGNED_INT_SAMPLER_2D_ARRAY || 249 | typ == GL_INT_SAMPLER_1D_ARRAY || typ == GL_INT_SAMPLER_2D_ARRAY 250 | ) 251 | end 252 | 253 | 254 | 255 | -------------------------------------------------------------------------------- /src/GLTexture.jl: -------------------------------------------------------------------------------- 1 | abstract AbstractFixedVector{T, NDim} 2 | 3 | SupportedEltypes = Union(Real, AbstractFixedVector, AbstractArray, ColorValue, AbstractAlphaColorValue) 4 | 5 | begin 6 | #Supported datatypes 7 | local const TO_GL_TYPE = @compat Dict( 8 | GLubyte => GL_UNSIGNED_BYTE, 9 | GLbyte => GL_BYTE, 10 | GLuint => GL_UNSIGNED_INT, 11 | GLushort => GL_UNSIGNED_SHORT, 12 | GLshort => GL_SHORT, 13 | GLint => GL_INT, 14 | GLfloat => GL_FLOAT 15 | ) 16 | glpixelformat{T <: Real}(x::Type{T}) = get(TO_GL_TYPE, x) do 17 | error("Type: $(x) not supported as pixel datatype") 18 | end 19 | glpixelformat{T <: FixedPoint}(x::Type{T}) = glpixelformat(FixedPointNumbers.rawtype(x)) 20 | glpixelformat{T <: SupportedEltypes}(x::Type{T}) = glpixelformat(eltype(x)) 21 | glpixelformat(x::SupportedEltypes) = glpixelformat(eltype(x)) 22 | end 23 | #Supported texture modes/dimensions 24 | begin 25 | local const TO_GL_TEXTURE_TYPE = @compat Dict( 26 | 1 => GL_TEXTURE_1D, 27 | 2 => GL_TEXTURE_2D, 28 | 3 => GL_TEXTURE_3D 29 | ) 30 | default_texturetype(ndim::Integer) = get(TO_GL_TEXTURE_TYPE, ndim) do 31 | error("Dimensionality: $(ndim), not supported for OpenGL texture") 32 | end 33 | end 34 | 35 | type Texture{T <: SupportedEltypes, ColorDIM, NDIM} 36 | id::GLuint 37 | texturetype::GLenum 38 | pixeltype::GLenum 39 | internalformat::GLenum 40 | format::GLenum 41 | dims::Vector{Int} 42 | data::Array{T, NDIM} 43 | end 44 | function Texture{T}(data::Ptr{T}, dims, ttype::GLenum, internalformat::GLenum, format::GLenum, parameters::Vector{(GLenum, GLenum)}, keepinram::Bool) 45 | @assert all(x -> x > 0, dims) 46 | 47 | id = glGenTextures() 48 | glBindTexture(ttype, id) 49 | 50 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1) 51 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0) 52 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0) 53 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0) 54 | 55 | for elem in parameters 56 | glTexParameteri(ttype, elem...) 57 | end 58 | 59 | pixeltype = glpixelformat(T) 60 | 61 | glTexImage(ttype, 0, internalformat, dims..., 0, format, pixeltype, data) 62 | NDim = length(dims) 63 | ColorDim = length(T) 64 | if keepinram 65 | if data == C_NULL 66 | obj = Texture{T, ColorDim, NDim}(id, ttype, pixeltype, internalformat, format, [dims...], Array(T, dims...)) 67 | else 68 | obj = Texture{T, ColorDim, NDim}(id, ttype, pixeltype, internalformat, format, [dims...], copy(pointer_to_array(data, tuple(dims...)))) 69 | end 70 | else 71 | obj = Texture{T, ColorDim, NDim}(id, ttype, pixeltype, internalformat, format, [dims...], Array(T, (dims*0)...)) 72 | end 73 | obj 74 | end 75 | 76 | #= 77 | Main constructor, which shouldn't be used. It will initializes all the missing values and pass it to the inner Texture constructor 78 | =# 79 | function Texture{T <: SupportedEltypes}(data::Vector{Array{T,2}}, texture_properties::Vector{(Symbol, Any)}) 80 | Base.length{ET <: Real}(::Type{ET}) = 1 81 | 82 | NDim = 3 83 | ColorDim = length(T) 84 | defaults = gendefaults(texture_properties, ColorDim, T, NDim) 85 | Texture(data, GL_TEXTURE_2D_ARRAY, defaults[:internalformat], defaults[:format], defaults[:parameters]) 86 | end 87 | 88 | function Texture{T <: SupportedEltypes}(data::Vector{Array{T,2}}, ttype::GLenum, internalformat::GLenum, format::GLenum, parameters::Vector{(GLenum, GLenum)}) 89 | id = glGenTextures() 90 | glBindTexture(ttype, id) 91 | 92 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1) 93 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0) 94 | glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0) 95 | glPixelStorei(GL_UNPACK_SKIP_ROWS, 0) 96 | 97 | for elem in [ 98 | (GL_TEXTURE_MIN_FILTER, GL_LINEAR), 99 | (GL_TEXTURE_MAG_FILTER, GL_LINEAR), 100 | (GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE), 101 | (GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE), 102 | ] 103 | glTexParameteri(ttype, elem...) 104 | end 105 | 106 | pixeltype = glpixelformat(T) 107 | 108 | layers = length(data) 109 | dims = map(size, data) 110 | maxdims = foldl((0,0), dims) do v0, x 111 | a = max(v0[1], x[1]) 112 | b = max(v0[2], x[2]) 113 | (a,b) 114 | end 115 | glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, internalformat, maxdims..., layers) 116 | for (layer, texel) in enumerate(data) 117 | width, height = size(texel) 118 | glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, layer-1, width, height, 1, format, pixeltype, texel) 119 | end 120 | ColorDim = length(T) 121 | Texture{T, ColorDim, 3}(id, ttype, pixeltype, internalformat, format, [maxdims..., layers], Array(T, 0,0,0)) 122 | 123 | end 124 | function default_colorformat(colordim::Integer, isinteger::Bool, colororder::String) 125 | if colordim > 4 126 | error("no colors with dimension > 4 allowed. Dimension given: ", colordim) 127 | end 128 | sym = "GL_" 129 | # Handle that colordim == 1 => RED instead of R 130 | color = colordim == 1 ? "RED" : colororder[1:colordim] 131 | integer = isinteger ? "_INTEGER" : "" 132 | sym *= color * integer 133 | return eval(symbol(sym)) 134 | end 135 | 136 | default_colorformat{T <: Real}(colordim::Type{T}) = default_colorformat(1, T <: Integer, "RED") 137 | default_colorformat{T <: AbstractArray}(colordim::Type{T}) = default_colorformat(length(T), eltype(T) <: Integer, "RGBA") 138 | default_colorformat{T <: AbstractFixedVector}(colordim::Type{T}) = default_colorformat(length(T), eltype(T) <: Integer, "RGBA") 139 | function default_colorformat{T <: AbstractAlphaColorValue}(colordim::Type{T}) 140 | colororder = string(T.parameters[1].name.name) * "A" 141 | return default_colorformat(length(T), eltype(T) <: Integer, colororder) 142 | end 143 | default_colorformat{T <: ColorValue}(colordim::Type{T}) = default_colorformat(length(T), eltype(T) <: Integer, string(T.name.name)) 144 | 145 | function default_internalcolorformat(colordim::Int, typ::DataType) 146 | if colordim > 4 || colordim < 1 147 | error("$(colordim)-dimensional colors not supported") 148 | end 149 | eltyp = eltype(typ) 150 | sym = "GL_" 151 | sym *= "RGBA"[1:colordim] 152 | bits = sizeof(eltyp) * 8 153 | sym *= bits <= 32 ? string(bits) : error("$(typ) has too many bits") 154 | if eltyp <: FloatingPoint 155 | sym *= "F" 156 | elseif eltyp <: FixedPoint 157 | sym *= eltyp <: Ufixed ? "" : "_SNORM" 158 | elseif eltyp <: Signed 159 | sym *= "I" 160 | elseif eltyp <: Unsigned 161 | sym *= "UI" 162 | end 163 | return eval(symbol(sym)) 164 | end 165 | 166 | function default_textureparameters(dim::Int, typ::DataType) 167 | interpolation = typ <: Integer ? GL_NEAREST : GL_LINEAR # Integer texture are not allowed to interpolate! 168 | parameters = [ 169 | (GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE), 170 | (GL_TEXTURE_MIN_FILTER, interpolation), 171 | (GL_TEXTURE_MAG_FILTER, interpolation) 172 | ] 173 | if dim <= 3 && dim > 1 174 | push!(parameters, (GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE )) 175 | end 176 | if dim == 3 177 | push!(parameters, (GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE )) 178 | end 179 | parameters 180 | end 181 | 182 | # As the default parameters of a texture are dependent on the texture, this is done by a function. 183 | # The function overwrites the defaults with values from texture_properties, so that one can customize the defaults 184 | # Here is a good place for parameter checking, yet not implemented though... 185 | function gendefaults(texture_properties::Vector{(Symbol, Any)}, ColorDim::Integer, Typ::DataType, NDim::Integer) 186 | return merge(@compat(Dict( 187 | :internalformat => default_internalcolorformat(ColorDim, Typ), 188 | :parameters => default_textureparameters(NDim, eltype(Typ)), 189 | :texturetype => default_texturetype(NDim), 190 | :format => default_colorformat(Typ), 191 | :keepinram => false 192 | )), Dict{Symbol, Any}(texture_properties)) 193 | end 194 | 195 | #= 196 | As Texture has a lot of variations with the same Keyword arguments, I decided to 197 | map the keywords into one array, which I pass to the actual constructor 198 | =# 199 | Texture(data... ; texture_properties...) = Texture(data..., convert(Vector{(Symbol, Any)}, texture_properties)) 200 | 201 | #= 202 | Main constructor, which shouldn't be used. It will initializes all the missing values and pass it to the inner Texture constructor 203 | =# 204 | function Texture{T <: SupportedEltypes}(data::Ptr{T}, dims::AbstractVector, texture_properties::Vector{(Symbol, Any)}) 205 | Base.length{ET <: Real}(::Type{ET}) = 1 206 | NDim = length(dims) 207 | ColorDim = length(T) 208 | defaults = gendefaults(texture_properties, ColorDim, T, NDim) 209 | Texture(data, dims, defaults[:texturetype], defaults[:internalformat], defaults[:format], defaults[:parameters], defaults[:keepinram]) 210 | end 211 | 212 | #= 213 | Constructor for empty initialization with NULL pointer instead of an array with data. 214 | You just need to pass the wanted color/vector type and the dimensions. 215 | To which values the texture gets initialized is driver dependent 216 | =# 217 | function Texture{T <: SupportedEltypes}(datatype::Type{T}, dims::AbstractVector, texture_properties::Vector{(Symbol, Any)}) 218 | Texture(convert(Ptr{T}, C_NULL), dims, texture_properties) 219 | end 220 | 221 | #= 222 | Constructor for a normal array, with color or Abstract Arrays as elements. 223 | So Array{Real, 2} == Texture2D with 1D Color dimension 224 | Array{Vec1/2/3/4, 2} == Texture2D with 1/2/3/4D Color dimension 225 | Colors from Colors.jl should mostly work as well 226 | =# 227 | function Texture{T <: SupportedEltypes, NDim}(image::Array{T, NDim}, texture_properties::Vector{(Symbol, Any)}) 228 | Texture(pointer(image), [size(image)...], texture_properties) 229 | end 230 | #= 231 | Some special treatmend for types, with alpha in the First place 232 | =# 233 | function Texture{T <: Real, NDim}(image::Array{ARGB{T}, NDim}, texture_properties::Vector{(Symbol, Any)}) 234 | data = map(image) do colorvalue 235 | AlphaColorValue(colorvalue.c, colorvalue.alpha) 236 | end 237 | Texture(pointer(data), [size(data)...], texture_properties) 238 | end 239 | 240 | #= 241 | Creates a texture from an image, which lays on path 242 | =# 243 | function Texture(path::String, texture_properties::Vector{(Symbol, Any)}) 244 | #isdefined(:Images) || eval(Expr(:using, :Images)) 245 | Texture(imread(path), texture_properties) 246 | end 247 | #= 248 | Creates a texture from an Image 249 | =# 250 | function Texture(image::Image, texture_properties::Vector{(Symbol, Any)}) 251 | data = image.data 252 | Texture(mapslices(reverse, data, ndims(data)), texture_properties) 253 | end 254 | 255 | 256 | 257 | width(t::Texture) = size(t,1) 258 | height(t::Texture) = size(t,2) 259 | depth(t::Texture) = size(t,3) 260 | 261 | Base.length(t::Texture) = prod(t.dims) 262 | Base.size(t::Texture) = tuple(t.dims...) 263 | 264 | function Base.show{T,C,D}(io::IO, t::Texture{T,C,D}) 265 | println(io, "Texture$(D)D: ") 266 | println(io, " ID: ", t.id) 267 | println(io, " Size: ", reduce("[ColorDim: $(C)]", t.dims) do v0, v1 268 | v0*"x"*string(v1) 269 | end) 270 | println(io, " Julia pixel type: ", T) 271 | println(io, " OpenGL pixel type: ", GLENUM(t.pixeltype).name) 272 | println(io, " Format: ", GLENUM(t.format).name) 273 | println(io, " Internal format: ", GLENUM(t.internalformat).name) 274 | end 275 | 276 | Base.eltype{T,C,D}(t::Texture{T, C, D}) = T 277 | Base.size{T,C,D}(t::Texture{T,C,D}) = t.dims 278 | Base.size{T,C,D}(t::Texture{T,C,D}, I::Integer) = t.dims[I] 279 | Base.endof(t::Texture) = prod(t.dims) 280 | Base.ndims{T, C, D}(t::Texture{T, C, D}) = D 281 | 282 | # Resize Texture 283 | function Base.resize!{T, CD, ND}(t::Texture{T,CD, ND}, newdims) 284 | if newdims != t.dims 285 | glBindTexture(t.texturetype, t.id) 286 | glTexImage(t.texturetype, 0, t.internalformat, newdims..., 0, t.format, t.pixeltype, C_NULL) 287 | if !isempty(t.data) 288 | tmp = Array(T, newdims...) 289 | if ndims(t.data) == 2 290 | if newdims[1] >= t.dims[1] && newdims[2] >= t.dims[2] 291 | tmp[1:size(t.data, 1), 1:size(t.data,2)] = t.data 292 | elseif newdims[1] >= t.dims[1] && newdims[2] <= t.dims[2] 293 | println("sizet: ", size(t.data)) 294 | println("sizemp: ", size(tmp)) 295 | println("newdims: ", newdims) 296 | tmp[1:size(t.data, 1), 1:end] = t.data[1:end, 1:newdims[2]] 297 | elseif newdims[1] <= t.dims[1] && newdims[2] >= t.dims[2] 298 | tmp[1:end, 1:size(t.data,2)] = t.data[1:newdims[1], 1:end] 299 | end 300 | else 301 | tmp[1:size(t.data, 1)] = t.data 302 | end 303 | t.data = tmp 304 | end 305 | t.dims[1:end] = newdims 306 | end 307 | end 308 | 309 | 310 | function Base.setindex!{T <: SupportedEltypes, ColorDim}(t::Texture{T, ColorDim, 1}, value, i::Integer) 311 | update!(t, value, i) 312 | end 313 | function Base.setindex!{T <: SupportedEltypes, ColorDim, IT1 <: Integer}(t::Texture{T, ColorDim, 1}, value, i::UnitRange{IT1}) 314 | update!(t, value, first(i)) 315 | end 316 | 317 | function Base.getindex(t::Texture, x...) 318 | if isempty(t.data) 319 | error("Texture doesn't keep a copy in RAM. Try creating the texture with keepinram=true") 320 | else 321 | getindex(t.data, x...) 322 | end 323 | end 324 | 325 | 326 | function Base.setindex!{T <: SupportedEltypes, ColorDim}(t::Texture{T, ColorDim, 2}, value, i, j::Integer) 327 | update!(t, value, first(i), j) 328 | end 329 | function Base.setindex!{T <: SupportedEltypes, ColorDim}(t::Texture{T, ColorDim, 2}, value, i::Integer) 330 | a = mod(i-1, size(t, 1))+1 331 | b = div(i-1, size(t, 1))+1 332 | setindex!(t, value, a, b) 333 | end 334 | function Base.setindex!{T <: SupportedEltypes, ColorDim}(t::Texture{T, ColorDim, 2}, value, i::Integer, j::Integer) 335 | update!(t, value, i, j) 336 | end 337 | function Base.setindex!{T <: SupportedEltypes, ColorDim, IT2 <: Integer}(t::Texture{T, ColorDim, 2}, value, i::Integer, j::UnitRange{IT2}) 338 | update!(t, value, i, first(j), 1, length(j)) 339 | end 340 | function Base.setindex!{T <: SupportedEltypes, ColorDim, IT1 <: Integer}(t::Texture{T, ColorDim, 2}, value, i::UnitRange{IT1}, j::Integer) 341 | update!(t, value, first(i), j, length(i), 1) 342 | end 343 | function Base.setindex!{T <: SupportedEltypes, ColorDim, IT1 <: Integer, IT2 <: Integer}(t::Texture{T, ColorDim, 2}, value, i::UnitRange{IT1}, j::UnitRange{IT2}) 344 | update!(t, value, first(i), first(j), length(i), length(j)) 345 | end 346 | 347 | 348 | # Instead of having so many methods, this should rather be solved by a macro or with better fixed size arrays 349 | 350 | function update!{T <: SupportedEltypes, ColorDim}(t::Texture{T, ColorDim, 1}, newvalue::Array{T, 1}, xoffset =1) 351 | update!(t, newvalue, xoffset, length(newvalue)) 352 | end 353 | function update!{T <: SupportedEltypes, ColorDim}(t::Texture{T, ColorDim, 1}, newvalue::T, xoffset = 1) 354 | update!(t, [newvalue], xoffset, 1) 355 | end 356 | 357 | function update!{T <: SupportedEltypes, ColorDim}(t::Texture{T, ColorDim, 1}, newvalue::Array, xoffset, _width) 358 | if (xoffset-1 + _width) > width(t) 359 | error("Out of bounds in texture, index ", xoffset, " width: ", _width, " texture:\n", t) 360 | end 361 | glBindTexture(t.texturetype, t.id) 362 | glTexSubImage1D(t.texturetype, 0, xoffset-1, _width, t.format, t.pixeltype, newvalue) 363 | if !isempty(t.data) 364 | t.data[xoffset:xoffset+_width-1] = newvalue 365 | end 366 | end 367 | 368 | function update!{T <: SupportedEltypes, ColorDim}(t::Texture{T, ColorDim, 2}, newvalue::Array{T, 1}, xoffset = 1, yoffset = 1) 369 | update!(t, newvalue, xoffset, yoffset, length(newvalue), 1) 370 | end 371 | function update!{T <: SupportedEltypes, ColorDim}(t::Texture{T, ColorDim, 2}, newvalue::Array{T, 2}, xoffset = 1, yoffset = 1) 372 | update!(t, newvalue, xoffset, yoffset, size(newvalue)...) 373 | end 374 | 375 | function update!{T <: SupportedEltypes, ColorDim}(t::Texture{T, ColorDim, 2}, newvalue::T, xoffset = 1, yoffset = 1) 376 | update!(t, [newvalue], xoffset, yoffset, 1, 1) 377 | end 378 | 379 | 380 | function update!{T <: SupportedEltypes, ColorDim}(t::Texture{T, ColorDim, 2}, newvalue::Array, xoffset, yoffset, _width, _height) 381 | if (xoffset-1 + _width) > width(t) && (yoffset-1 + _height) > height(t) 382 | error("Out of bounds in texture, xindex ", xoffset, " width: ", _width, " yindex: ", yoffset, " height: ", _height, " in texture:\n", t) 383 | end 384 | glBindTexture(t.texturetype, t.id) 385 | glTexSubImage2D(t.texturetype, 0, xoffset-1, yoffset-1, _width, _height, t.format, t.pixeltype, newvalue) 386 | if !isempty(t.data) 387 | t.data[xoffset:xoffset+_width-1, yoffset:yoffset+_height-1] = newvalue 388 | end 389 | end 390 | 391 | function Base.setindex!{T <: AbstractFixedVector, ElType}(a::Vector{T}, x::ElType, i::Integer, accessor::Integer) 392 | @assert eltype(T) == ElType # ugly workaround for not having triangular dispatch 393 | @assert length(a) >= i 394 | cardinality = length(T) 395 | @assert accessor <= cardinality 396 | ptr = convert(Ptr{ElType}, pointer(a)) 397 | unsafe_store!(ptr, x, ((i-1)*cardinality)+accessor) 398 | end 399 | function Base.setindex!{T <: AbstractFixedVector, ElType}(a::Vector{T}, x::Vector{ElType}, i::Integer, accessor::UnitRange) 400 | @assert eltype(T) == ElType 401 | @assert length(a) >= i 402 | cardinality = length(T) 403 | @assert length(accessor) <= cardinality 404 | ptr = convert(Ptr{ElType}, pointer(a)) 405 | unsafe_copy!(ptr + (sizeof(ElType)*((i-1)*cardinality)), pointer(x), length(accessor)) 406 | end 407 | function Base.setindex!{T <: AbstractFixedVector, ElType, CDim}(a::Texture{T, CDim, 1}, x::ElType, i::Integer, accessor::Integer) 408 | a.data[i, accessor] = x 409 | a[i] = a.data[i] 410 | end 411 | function Base.setindex!{T <: AbstractFixedVector, ElType, CDim}(a::Texture{T, CDim, 1}, x::Vector{ElType}, i::Integer, accessor::UnitRange) 412 | a.data[i, accessor] = x 413 | a[i] = a.data[i] 414 | end 415 | 416 | 417 | function setindex1D!{T <: AbstractFixedVector, ElType}(a::Union(Matrix{T}, Vector{T}), x::ElType, i::Integer, accessor::Integer) 418 | if length(a) < i 419 | error("Out of Bounds. 1D index: ", i, " Matrix: ", typeof(a), " length: ", length(a), " size: ", size(a)) 420 | end 421 | cardinality = length(T) 422 | if length(accessor) > cardinality 423 | error("Out of Bounds. 1D index: ", i, " Matrix: ", typeof(a), " length: ", length(a), " size: ", size(a)) 424 | end 425 | 426 | ptr = convert(Ptr{eltype(T)}, pointer(a)) 427 | unsafe_store!(ptr, convert(eltype(T), x), ((i-1)*cardinality)+accessor) 428 | end 429 | function setindex1D!{T <: AbstractFixedVector, ElType}(a::Union(Matrix{T}, Vector{T}), x::Vector{ElType}, i::Integer, accessor::UnitRange) 430 | if length(a) < i 431 | error("Out of Bounds. 1D index: ", i, " Matrix: ", typeof(a), " length: ", length(a), " size: ", size(a)) 432 | end 433 | cardinality = length(T) 434 | if length(accessor) > cardinality 435 | error("Out of Bounds. 1D index: ", i, " Matrix: ", typeof(a), " length: ", length(a), " size: ", size(a)) 436 | end 437 | eltp = eltype(T) 438 | x = convert(Vector{eltp}, x) 439 | ptr = convert(Ptr{eltp}, pointer(a)) 440 | unsafe_copy!(ptr + (sizeof(eltp)*((i-1)*(cardinality)+first(accessor-1))), pointer(x), length(accessor)) 441 | end 442 | 443 | 444 | function setindex1D!{T <: AbstractFixedVector, ElType, CDim}(a::Texture{T, CDim, 2}, x::ElType, i::Integer, accessor::Integer) 445 | if isempty(a.data) 446 | error("Texture doesn't have a copy in ram. Please create Texture with keepeinram=true, for using setindex/1D!") 447 | end 448 | setindex1D!(a.data, x, i, accessor) 449 | a[i] = a.data[i] 450 | end 451 | function setindex1D!{T <: AbstractFixedVector, ElType, CDim}(a::Texture{T, CDim, 2}, x::Vector{ElType}, i::Integer, accessor::UnitRange) 452 | if isempty(a.data) 453 | error("Texture doesn't have a copy in ram. Please create Texture with keepeinram=true, for using setindex/1D!") 454 | end 455 | setindex1D!(a.data, x, i, accessor) 456 | a[i] = a.data[i] 457 | end 458 | 459 | 460 | 461 | function Images.data{T, ColorDim, NDim}(t::Texture{T, ColorDim, NDim}) 462 | if isempty(t.data) 463 | result = Array(T, t.dims...) 464 | glBindTexture(t.texturetype, t.id) 465 | glGetTexImage(t.texturetype, 0, t.format, t.pixeltype, result) 466 | return result 467 | else 468 | t.data 469 | end 470 | end 471 | --------------------------------------------------------------------------------