Example #1
0
	def __init__(self, name=None, parent=None, mode=gl.LINES, vertices=None):
		super(ShapeNode, self).__init__(name, parent)
		self.start = 0
		self.vao = gl.genVertexArrays(1)[0]
		self.vbo = gl.genBuffers(1)[0]
		self.mode = mode

		self.vertices = vertices

		gl.bindVertexArray(self.vao)
		gl.bindBuffer(gl.ARRAY_BUFFER, self.vbo)

		# Position attribute
		gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, sizeof(Vertex), Vertex.position.offset)
		gl.enableVertexAttribArray(0)

		# Color attribute
		gl.vertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, sizeof(Vertex), Vertex.color.offset)
		gl.enableVertexAttribArray(1)

		# unbind the vbo
		gl.bindBuffer(gl.ARRAY_BUFFER, 0)

		# Unbind the VAO
		gl.bindVertexArray(0)
Example #2
0
def initQuad():
    global quadVAO, quadVBO

    class QuadVertex(Structure):
        _fields_ = [
            ('position', Vec3),
            ('texcoord', Vec2)
        ]


    quadVertices = (QuadVertex * 4)(
        # Positions         # Texture Coords
       (( -1.0,  1.0, 0.0),  (0.0, 1.0)),
       (( -1.0, -1.0, 0.0),  (0.0, 0.0)),
       ((  1.0,  1.0, 0.0),  (1.0, 1.0)),
       ((  1.0, -1.0, 0.0),  (1.0, 0.0)),
    )

    # Setup plane VAO
    quadVAO = gl.genVertexArrays(1)[0]
    quadVBO = gl.genBuffers(1)[0]
    gl.bindVertexArray(quadVAO)
    gl.bindBuffer(gl.ARRAY_BUFFER, quadVBO)
    gl.bufferData(gl.ARRAY_BUFFER, bytes(quadVertices), gl.STATIC_DRAW)
    gl.enableVertexAttribArray(0)
    gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, sizeof(QuadVertex), QuadVertex.position.offset)
    gl.enableVertexAttribArray(1)
    gl.vertexAttribPointer(1, 2, gl.FLOAT, gl.FALSE, sizeof(QuadVertex), QuadVertex.texcoord.offset)
Example #3
0
 def draw(self, *args):
     gl.bindVertexArray(self.vao)
     gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.ebo)
     for mesh in self.obj_model['meshes']:
         dc = mesh['draw_call']
         gl.drawElements(gl.TRIANGLES, dc['count'], gl.UNSIGNED_INT, dc['start'] * self.obj_model['vertex_buffer']['stride'])
     gl.bindVertexArray(0);
Example #4
0
    def update(self):
        # print("UPDATING BUFFER INSTANCES")

        gl.bindBuffer(gl.ARRAY_BUFFER, self.vbo)

        # respecify the buffer data.
        instance_buffer_bytes = self.instances.get_data()
        gl.bufferData(gl.ARRAY_BUFFER, len(instance_buffer_bytes), gl.STREAM_DRAW)
        gl.bufferData(gl.ARRAY_BUFFER, instance_buffer_bytes, gl.STREAM_DRAW)
Example #5
0
	def vertices(self, vertices):
		self._vertex_data = (Vertex * len(vertices))(*[tuple([tuple(e) for e in v]) for v in vertices])
		self.count = len(vertices)

		gl.bindVertexArray(self.vao)
		gl.bindBuffer(gl.ARRAY_BUFFER, self.vbo)
		
		gl.bufferData(gl.ARRAY_BUFFER, bytes(self._vertex_data), gl.STATIC_DRAW)

		gl.bindBuffer(gl.ARRAY_BUFFER, 0)

		# Unbind the VAO
		gl.bindVertexArray(0)
Example #6
0
def load_model_data(obj_data):
	vertex_buffer = obj_data['vertex_buffer']
	vertex_buffer_format = vertex_buffer['format']
	vertex_buffer_bytes = base64.b64decode(vertex_buffer['bytes'])

	# set up the vao and vbo
	vao = gl.genVertexArrays(1)[0]
	vbo, ebo = gl.genBuffers(2)

	# 1. Bind Vertex Array Object
	gl.bindVertexArray(vao)

    # 2. Copy our vertices array in a buffer for OpenGL to use
	gl.bindBuffer(gl.ARRAY_BUFFER, vbo)
	gl.bufferData(gl.ARRAY_BUFFER, vertex_buffer_bytes, gl.STATIC_DRAW)

	# Position attribute
	gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, vertex_buffer['stride'], vertex_buffer_format['position']['offset'])
	gl.enableVertexAttribArray(0)

	# Normal attribute
	if 'normal' in vertex_buffer_format:
		gl.vertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, vertex_buffer['stride'], vertex_buffer_format['normal']['offset'])
		gl.enableVertexAttribArray(1)

	if 'texcoord' in vertex_buffer_format:
		gl.vertexAttribPointer(2, 2, gl.FLOAT, gl.FALSE, vertex_buffer['stride'], vertex_buffer_format['texcoord']['offset'])
		gl.enableVertexAttribArray(2)


	# load the index buffer
	index_buffer = obj_data['index_buffer']
	index_buffer['bytes'] = base64.b64decode(index_buffer['bytes'])

	gl.bindBuffer(gl.ARRAY_BUFFER, ebo)
	gl.bufferData(gl.ARRAY_BUFFER, index_buffer['bytes'], gl.STATIC_DRAW)

	gl.bindBuffer(gl.ARRAY_BUFFER, 0)
	gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0)
	gl.bindVertexArray(0)

	return vao, vbo, ebo
Example #7
0
    def __init__(self, index_buffer_bytes, vertex_buffer_bytes, vertex_buffer_attribs, meshes, instance_type, instance_wrapper_type=None):
        super(Model, self).__init__()
        self.instance_type = instance_type
        self.instance_wrapper_type = instance_wrapper_type

        self.vbo = gl.genBuffers(1)[0]

        # Copy our vertices array in a buffer for OpenGL to use
        gl.bindBuffer(gl.ARRAY_BUFFER, self.vbo)
        gl.bufferData(gl.ARRAY_BUFFER, vertex_buffer_bytes, gl.STATIC_DRAW)

        self.ebo = gl.genBuffers(1)[0]

        # populate the elemeent buffer
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,self.ebo)
        gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, index_buffer_bytes, gl.STATIC_DRAW)

        self.vertex_buffer_attribs = vertex_buffer_attribs

        self.meshes = meshes     

        gl.bindBuffer(gl.ARRAY_BUFFER, 0)
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0)
Example #8
0
    def __init__(self, model):
        super(ModelInstances, self).__init__()

        self.model = model

        self.instance_type = self.model.instance_type
        self.instance_wrapper = self.model.instance_wrapper_type
        self.instances = Set(self.instance_type)

        # the vao will contain all the binding for the mesh and instance data
        self.vao = gl.genVertexArrays(1)[0]
        gl.bindVertexArray(self.vao)

        # this vbo contains the instance data
        instance_buffer_bytes = self.instances.get_data()
        self.vbo = gl.genBuffers(1)[0]
        gl.bindBuffer(gl.ARRAY_BUFFER, self.vbo)
        gl.bufferData(gl.ARRAY_BUFFER, instance_buffer_bytes, gl.STREAM_DRAW)

        instance_buffer_attribs = BufferAttribs(sizeof(self.instance_type), 1)

        if hasattr(self.instance_type, 'model_matrix'):
            instance_buffer_attribs.add_mat(self.MODEL_MATRIX_LOCATION, 4, gl.FLOAT, gl.FALSE, self.instance_type.model_matrix.offset)

        if hasattr(self.instance_type, 'linear_attenuation'):
            instance_buffer_attribs.add_mat(self.LINEAR_ATTENUATION_LOCATION, 1, gl.FLOAT, gl.FALSE, self.instance_type.linear_attenuation.offset)

        if hasattr(self.instance_type, 'quadratic_attenuation'):
            instance_buffer_attribs.add_mat(self.QUADRATIC_ATTENUATION_LOCATION, 1, gl.FLOAT, gl.FALSE, self.instance_type.quadratic_attenuation.offset)

        if hasattr(self.instance_type, 'diffuse'):
            instance_buffer_attribs.add_mat(self.DIFFUSE_LOCATION, 3, gl.FLOAT, gl.FALSE, self.instance_type.diffuse.offset)

        instance_buffer_attribs.bind()

        # bind the models buffers and attribute data
        gl.bindBuffer(gl.ARRAY_BUFFER, self.model.vbo)
        self.model.vertex_buffer_attribs.bind()
        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, self.model.ebo)

        gl.bindVertexArray(0)
Example #9
0
def main():
	glfw.setErrorCallback(error_callback)

	glfw.init()
	glfw.windowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
	glfw.windowHint(glfw.CONTEXT_VERSION_MINOR, 3)
	glfw.windowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
	# glfw.windowHint(glfw.RESIZABLE, gl.FALSE)
	glfw.windowHint(glfw.OPENGL_FORWARD_COMPAT, 1)

	window = glfw.createWindow(800, 600, "LearnOpenGL")

	if window is None:
		print('could not open window.')
		glfw.terminate()
		sys.exit()

	window.makeContextCurrent()
	window.setKeyCallback(key_callback)

	gl.init()
	err = gl.getError()
	print(gl)
	if err:
		print("WINDOW OPEN ERROR:", err)

	vertexShader = gl.createShader(gl.VERTEX_SHADER)
	gl.shaderSource(vertexShader, [VERTEX_SHADER_SOURCE]);
	gl.compileShader(vertexShader)

	success = gl.getShaderiv(vertexShader, gl.COMPILE_STATUS)
	print("VERTEX SHADER COMPILE STATUS:",success)
	if not success:
		infoLog = gl.getShaderInfoLog(vertexShader)
		print("ERROR COMPILING SHADER:", infoLog)
		sys.exit()

	fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
	gl.shaderSource(fragmentShader, [FRAGMENT_SHADER_SOURCE]);
	gl.compileShader(fragmentShader)

	success = gl.getShaderiv(fragmentShader, gl.COMPILE_STATUS)
	print("FRAGMENT SHADER COMPILE STATUS:",success)
	if not success:
		infoLog = gl.getShaderInfoLog(fragmentShader)
		print("ERROR COMPILING SHADER:", infoLog)
		sys.exit()

	shaderProgram = gl.createProgram()
	gl.attachShader(shaderProgram, vertexShader)
	gl.attachShader(shaderProgram, fragmentShader)
	gl.linkProgram(shaderProgram)

	gl.getProgramiv(shaderProgram, gl.LINK_STATUS)
	print("SHADER PROGRAM LINK STATUS:",success)
	if not success:
		infoLog = gl.getProgramInfoLog(shaderProgram)
		print("ERROR LINKING SHADER PROGRAM:", infoLog)
		sys.exit()

	assert(gl.isProgram(shaderProgram))
	gl.useProgram(shaderProgram)
	#gl.deleteShader(vertexShader)
	#gl.deleteShader(fragmentShader)

	vao = gl.genVertexArrays(1)[0]
	vbo, ebo = gl.genBuffers(2)
	print(vao, vbo, ebo)

	# 1. Bind Vertex Array Object
	gl.bindVertexArray(vao)

    # 2. Copy our vertices array in a buffer for OpenGL to use
	gl.bindBuffer(gl.ARRAY_BUFFER, vbo)
	gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)

    # 2. Copy our index array in a buffer for OpenGL to use
	gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo)
	gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW)

    # 3. Then set our vertex attributes pointers
	gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 3 * sizeof(gl.GLfloat), None)
	gl.enableVertexAttribArray(0)

	# 4. unbind the vbo
	gl.bindBuffer(gl.ARRAY_BUFFER, 0)

	# 4. Unbind the VAO
	gl.bindVertexArray(0)

	while not window.shouldClose():
		glfw.pollEvents()

		framebuffer_width, framebuffer_height = window.getFramebufferSize()
		gl.viewport(0, 0, framebuffer_width, framebuffer_height)
		gl.clearColor(0.2, 0.3, 0.3, 1.0)
		gl.clear(gl.COLOR_BUFFER_BIT)

		gl.useProgram(shaderProgram)
		gl.bindVertexArray(vao)
		gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, 0)
		# gl.drawArrays(gl.TRIANGLES, 0, 3); 
		gl.bindVertexArray(0)

		window.swapBuffers()

	gl.deleteVertexArrays([vao])
	gl.deleteBuffers([vbo, ebo])
def main():
    glfw.setErrorCallback(error_callback)

    glfw.init()
    glfw.windowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
    glfw.windowHint(glfw.CONTEXT_VERSION_MINOR, 3)
    glfw.windowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
    # glfw.windowHint(glfw.RESIZABLE, gl.FALSE)
    glfw.windowHint(glfw.OPENGL_FORWARD_COMPAT, 1)

    window = glfw.createWindow(WIDTH, HEIGHT, "LearnOpenGL")

    if window is None:
        print('could not open window.')
        glfw.terminate()
        sys.exit()


    inputMap = InputMap()

    window.makeContextCurrent()
    window.setKeyCallback(inputMap.key_callback)
    window.setCursorPosCallback(inputMap.mouse_callback)
    #window.setScrollCallback(inputMap.scroll_callback)
    window.setInputMode(glfw.CURSOR, glfw.CURSOR_DISABLED)

    gl.init()
    err = gl.getError()
    if err:
        print("WINDOW OPEN ERROR:", err)

    camera = Camera(position=Vec3(0.0, 0.0, 3.0))
    lastX = 400
    lastY = 300
    firstMouse = True
    deltaTime = 0.0
    lastFrame = 0.0

    gl.enable(gl.DEPTH_TEST)

    lampShaderProgram = Shader(LAMP_VERTEX_SHADER_SOURCE, LAMP_FRAGMENT_SHADER_SOURCE)
    lightingShaderProgram = Shader(LIGHTING_VERTEX_SHADER_SOURCE, LIGHTING_FRAGMENT_SHADER_SOURCE)
    lightingShaderProgram.use()

    lightVAO, containerVAO = gl.genVertexArrays(2)
    vbo = gl.genBuffers(1)[0]

    # 1. Bind Vertex Array Object
    gl.bindVertexArray(containerVAO)

    # 2. Copy our vertices array in a buffer for OpenGL to use
    gl.bindBuffer(gl.ARRAY_BUFFER, vbo)
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)

    # Position attribute
    gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, sizeof(Vertex), Vertex.position.offset)
    gl.enableVertexAttribArray(0)

    # Normal attribute
    gl.vertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, sizeof(Vertex), Vertex.normal.offset)
    gl.enableVertexAttribArray(1)

    # 4. Unbind the VAO
    gl.bindVertexArray(0)


    # 1. Bind Vertex Array Object
    gl.bindVertexArray(lightVAO)

    # Position attribute
    gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, sizeof(Vertex), Vertex.position.offset)
    gl.enableVertexAttribArray(0)

    # 4. Unbind the VAO
    gl.bindVertexArray(0)

    # 4. unbind the vbo
    gl.bindBuffer(gl.ARRAY_BUFFER, 0)

    lightPos = Vec3(1.2, 1.0, 2.0)

    while not window.shouldClose():
        inputMap.begin_frame()
        glfw.pollEvents()
        input = inputMap.get_input()
        camera.processInput(input, 1.0/30.0)

        framebuffer_width, framebuffer_height = window.getFramebufferSize()
        gl.viewport(0, 0, framebuffer_width, framebuffer_height)
        gl.clearColor(0.2, 0.3, 0.3, 1.0)
        gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT)

        lightingShaderProgram.use()
        lightingShaderProgram.uniforms.objectColor = Vec3(1.0, 0.5, 0.31)
        lightingShaderProgram.uniforms.lightColor = Vec3(1.0, 0.5, 1.0)
        lightingShaderProgram.uniforms.lightPos = lightPos
        lightingShaderProgram.uniforms.viewPos = camera.position

        # create transformations
        view = camera.getViewMatrix()
        projection = Mat4.Perspective(45.0, float(WIDTH) / float(HEIGHT), 0.1, 100.0)

        # assign the transformations
        lightingShaderProgram.uniforms.view = view
        lightingShaderProgram.uniforms.projection = projection

        # draw the container
        gl.bindVertexArray(containerVAO)
        model = Mat4.Identity()

        lightingShaderProgram.uniforms.model = model
        gl.drawArrays(gl.TRIANGLES, 0, 36)
        gl.bindVertexArray(0)

        # draw the lamp
        lampShaderProgram.use()

        lampShaderProgram.uniforms.view = view
        lampShaderProgram.uniforms.projection = projection

        model = Mat4.Translation(lightPos) * Mat4.Scale(0.2)

        lampShaderProgram.uniforms.model = model

        gl.bindVertexArray(lightVAO)
        gl.drawArrays(gl.TRIANGLES, 0, 36)
        gl.bindVertexArray(0)

        window.swapBuffers()

    lampShaderProgram.delete()
    lightingShaderProgram.delete()

    gl.deleteVertexArrays([containerVAO, lightVAO])
    gl.deleteBuffers([vbo])
Example #11
0
def initCube():
    global cubeVAO, cubeVBO
    vertices = (Vertex * 36)(
        # Back face
        ((-0.5, -0.5, -0.5), ( 0.0,  0.0, -1.0), (0.0, 0.0)), # Bottom-left
        (( 0.5,  0.5, -0.5), ( 0.0,  0.0, -1.0), (1.0, 1.0)), # top-right
        (( 0.5, -0.5, -0.5), ( 0.0,  0.0, -1.0), (1.0, 0.0)), # bottom-right
        (( 0.5,  0.5, -0.5), ( 0.0,  0.0, -1.0), (1.0, 1.0)), # top-right
        ((-0.5, -0.5, -0.5), ( 0.0,  0.0, -1.0), (0.0, 0.0)), # bottom-left
        ((-0.5,  0.5, -0.5), ( 0.0,  0.0, -1.0), (0.0, 1.0)), # top-left
        # Front face   
        ((-0.5, -0.5,  0.5), ( 0.0,  0.0,  1.0), (0.0, 0.0)), # bottom-left
        (( 0.5, -0.5,  0.5), ( 0.0,  0.0,  1.0), (1.0, 0.0)), # bottom-right
        (( 0.5,  0.5,  0.5), ( 0.0,  0.0,  1.0), (1.0, 1.0)), # top-right
        (( 0.5,  0.5,  0.5), ( 0.0,  0.0,  1.0), (1.0, 1.0)), # top-right
        ((-0.5,  0.5,  0.5), ( 0.0,  0.0,  1.0), (0.0, 1.0)), # top-left
        ((-0.5, -0.5,  0.5), ( 0.0,  0.0,  1.0), (0.0, 0.0)), # bottom-left
        # Left face  
        ((-0.5,  0.5,  0.5), (-1.0,  0.0,  0.0), (1.0, 0.0)), # top-right
        ((-0.5,  0.5, -0.5), (-1.0,  0.0,  0.0), (1.0, 1.0)), # top-left
        ((-0.5, -0.5, -0.5), (-1.0,  0.0,  0.0), (0.0, 1.0)), # bottom-left
        ((-0.5, -0.5, -0.5), (-1.0,  0.0,  0.0), (0.0, 1.0)), # bottom-left
        ((-0.5, -0.5,  0.5), (-1.0,  0.0,  0.0), (0.0, 0.0)), # bottom-right
        ((-0.5,  0.5,  0.5), (-1.0,  0.0,  0.0), (1.0, 0.0)), # top-right
        # Right face  
        (( 0.5,  0.5,  0.5), ( 1.0,  0.0,  0.0), (1.0, 0.0)), # top-left
        (( 0.5, -0.5, -0.5), ( 1.0,  0.0,  0.0), (0.0, 1.0)), # bottom-right
        (( 0.5,  0.5, -0.5), ( 1.0,  0.0,  0.0), (1.0, 1.0)), # top-right
        (( 0.5, -0.5, -0.5), ( 1.0,  0.0,  0.0), (0.0, 1.0)), # bottom-right
        (( 0.5,  0.5,  0.5), ( 1.0,  0.0,  0.0), (1.0, 0.0)), # top-left
        (( 0.5, -0.5,  0.5), ( 1.0,  0.0,  0.0), (0.0, 0.0)), # bottom-left
        # Bottom face  
        ((-0.5, -0.5, -0.5), ( 0.0, -1.0,  0.0), (0.0, 1.0)), # top-right
        (( 0.5, -0.5, -0.5), ( 0.0, -1.0,  0.0), (1.0, 1.0)), # top-left
        (( 0.5, -0.5,  0.5), ( 0.0, -1.0,  0.0), (1.0, 0.0)), # bottom-left
        (( 0.5, -0.5,  0.5), ( 0.0, -1.0,  0.0), (1.0, 0.0)), # bottom-left
        ((-0.5, -0.5,  0.5), ( 0.0, -1.0,  0.0), (0.0, 0.0)), # bottom-right
        ((-0.5, -0.5, -0.5), ( 0.0, -1.0,  0.0), (0.0, 1.0)), # top-right
        # Top face  
        ((-0.5,  0.5, -0.5), ( 0.0,  1.0,  0.0), (0.0, 1.0)), # top-left
        (( 0.5,  0.5,  0.5), ( 0.0,  1.0,  0.0), (1.0, 0.0)), # bottom-right
        (( 0.5,  0.5, -0.5), ( 0.0,  1.0,  0.0), (1.0, 1.0)), # top-right
        (( 0.5,  0.5,  0.5), ( 0.0,  1.0,  0.0), (1.0, 0.0)), # bottom-right
        ((-0.5,  0.5, -0.5), ( 0.0,  1.0,  0.0), (0.0, 1.0)), # top-left
        ((-0.5,  0.5,  0.5), ( 0.0,  1.0,  0.0), (0.0, 0.0))  # bottom-left
    )

    cubeVAO = gl.genVertexArrays(1)[0]
    cubeVBO = gl.genBuffers(1)[0]
    # Fill buffer
    gl.bindBuffer(gl.ARRAY_BUFFER, cubeVBO);
    gl.bufferData(gl.ARRAY_BUFFER, bytes(vertices), gl.STATIC_DRAW)

    # Link vertex attributes
    gl.bindVertexArray(cubeVAO)
    gl.enableVertexAttribArray(0)
    gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, sizeof(Vertex), Vertex.position.offset)
    gl.enableVertexAttribArray(1)
    gl.vertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, sizeof(Vertex), Vertex.normal.offset)
    gl.enableVertexAttribArray(2)
    gl.vertexAttribPointer(2, 2, gl.FLOAT, gl.FALSE, sizeof(Vertex), Vertex.texcoord.offset)
    gl.bindBuffer(gl.ARRAY_BUFFER, 0)
    gl.bindVertexArray(0)
Example #12
0
def main():
    global lastFrame, planeVAO, planeVBO

    # Init GLFW
    glfw.setErrorCallback(error_callback)

    glfw.init()
    glfw.windowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
    glfw.windowHint(glfw.CONTEXT_VERSION_MINOR, 3)
    glfw.windowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
    # glfw.windowHint(glfw.RESIZABLE, gl.FALSE)
    glfw.windowHint(glfw.OPENGL_FORWARD_COMPAT, 1)

    window = glfw.createWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL")

    if window is None:
        print('could not open window.')
        glfw.terminate()
        sys.exit()

    inputMap = InputMap()

    window.makeContextCurrent()
    window.setKeyCallback(inputMap.key_callback)
    window.setCursorPosCallback(inputMap.mouse_callback)
    #window.setScrollCallback(inputMap.scroll_callback)
    window.setInputMode(glfw.CURSOR, glfw.CURSOR_DISABLED)

    # some versions of glfw cause an opengl error when creating a window, make sure that's ignored.
    gl.init()
    err = gl.getError()
    if err:
        print("WINDOW OPEN ERROR:", err)

    # Define the viewport dimensions
    VP_WIDTH, VP_HEIGHT = window.getFramebufferSize()
    gl.viewport(0, 0, VP_WIDTH, VP_HEIGHT)

    # Setup some OpenGL options
    gl.enable(gl.DEPTH_TEST);

    # Setup and compile our shaders
    shaderProgram = Shader(SHADOW_MAPPING_VERT, SHADOW_MAPPING_FRAG);
    simpleDepthShader = Shader(SHADOW_MAPPING_DEPTH_VERT, SHADOW_MAPPING_DEPTH_FRAG);
    debugDepthQuad = Shader(DEBUG_QUAD_VERT, DEBUG_QUAD_FRAG);

    # Set texture samples
    shaderProgram.use();
    # shaderProgram.uniforms.diffuseTexture = 0
    # shaderProgram.uniforms.shadowMap = 1

    # print(dir(Vertex))
    planeVertices = (Vertex * 6)(
        # Positions             # Normals           # Texture Coords
        (( 25.0, -0.5,  25.0),  (0.0,  1.0,  0.0),  (25.0,  0.0)),
        ((-25.0, -0.5, -25.0),  (0.0,  1.0,  0.0),  ( 0.0, 25.0)),
        ((-25.0, -0.5,  25.0),  (0.0,  1.0,  0.0),  ( 0.0,  0.0)),

        (( 25.0, -0.5,  25.0),  (0.0,  1.0,  0.0),  (25.0,  0.0)),
        (( 25.0, -0.5, -25.0),  (0.0,  1.0,  0.0),  (25.0, 25.0)),
        ((-25.0, -0.5, -25.0),  (0.0,  1.0,  0.0),  ( 0.0, 25.0))
    )

    # Setup plane VAO
    planeVAO = gl.genVertexArrays(1)[0]
    planeVBO = gl.genBuffers(1)[0]

    gl.bindVertexArray(planeVAO)
    gl.bindBuffer(gl.ARRAY_BUFFER, planeVBO)
    gl.bufferData(gl.ARRAY_BUFFER, bytes(planeVertices), gl.STATIC_DRAW)
    gl.enableVertexAttribArray(0)
    gl.vertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, sizeof(Vertex), Vertex.position.offset)
    gl.enableVertexAttribArray(1)
    gl.vertexAttribPointer(1, 3, gl.FLOAT, gl.FALSE, sizeof(Vertex), Vertex.normal.offset)
    gl.enableVertexAttribArray(2)
    gl.vertexAttribPointer(2, 2, gl.FLOAT, gl.FALSE, sizeof(Vertex), Vertex.texcoord.offset)
    gl.bindVertexArray(0)

    # Light source
    lightPos = Vec3(-2.0, 4.0, -1.0)

    # Load textures
    woodTexture = load_texture("learningopengl/resources/textures/wood.png")

    # Configure depth map FBO
    SHADOW_WIDTH = SHADOW_HEIGHT = 4096
    

    depthMapFBO = gl.genFramebuffers(1)[0]
    
    # - Create depth texture
    depthMap = gl.genTextures(1)[0]
    
    gl.bindTexture(gl.TEXTURE_2D, depthMap)

    gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, gl.DEPTH_COMPONENT, gl.FLOAT, None)
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_BORDER)
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_BORDER)
    borderColor = Vec4(1.0, 1.0, 1.0, 1.0)
    gl.texParameterfv(gl.TEXTURE_2D, gl.TEXTURE_BORDER_COLOR, borderColor)

    gl.bindFramebuffer(gl.FRAMEBUFFER, depthMapFBO)
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthMap, 0)
    gl.drawBuffer(gl.NONE)
    gl.readBuffer(gl.NONE);
    gl.bindFramebuffer(gl.FRAMEBUFFER, 0);

    gl.clearColor(0.1, 0.1, 0.1, 1.0)

    # Game loop
    while not window.shouldClose():
        # Set frame time
        currentFrame = time.perf_counter()
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        # Check and call events
        inputMap.begin_frame()
        glfw.pollEvents()
        input = inputMap.get_input()
        camera.processInput(input, 1.0/30.0)

        # Change light position over time
        lightPos.z = math.cos(time.perf_counter()) * 2.0

        # 1. Render depth of scene to texture (from light's perspective)
        # - Get light projection/view matrix.
        near_plane = 1.0
        far_plane = 7.5
        lightProjection = Mat4.Ortho(-10.0, 10.0, -10.0, 10.0, near_plane, far_plane)
        # lightProjection = glm::perspective(45.0f, (GLfloat)SHADOW_WIDTH / (GLfloat)SHADOW_HEIGHT, near_plane, far_plane); // Note that if you use a perspective projection matrix you'll have to change the light position as the current light position isn't enough to reflect the whole scene.
        lightView = Mat4.LookAt(lightPos, Vec3(0.0, 0.0, 0.0), Vec3(1.0, 1.0, 1.0))
        lightSpaceMatrix = lightProjection * lightView;
        # - now render scene from light's point of view
        simpleDepthShader.use()
        simpleDepthShader.uniforms.lightSpaceMatrix = lightSpaceMatrix
        # glUniformMatrix4fv(glGetUniformLocation(simpleDepthShader.Program, "lightSpaceMatrix"), 1, GL_FALSE, glm::value_ptr(lightSpaceMatrix));
        gl.viewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT)
        gl.bindFramebuffer(gl.FRAMEBUFFER, depthMapFBO)
        gl.clear(gl.DEPTH_BUFFER_BIT);
        RenderScene(simpleDepthShader);
        gl.bindFramebuffer(gl.FRAMEBUFFER, 0)

        # 2. Render scene as normal
        gl.viewport(0, 0, VP_WIDTH, VP_HEIGHT)
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
        shaderProgram.use()
        # TODO: use zoon
        projection = Mat4.Perspective(45.0, float(SCR_WIDTH) / float(SCR_HEIGHT), 0.1, 100.0)
        view = camera.getViewMatrix()
        shaderProgram.uniforms.projection = projection
        shaderProgram.uniforms.view = view

        shaderProgram.uniforms.lightPos = lightPos
        shaderProgram.uniforms.viewPos = camera.position
        shaderProgram.uniforms.lightSpaceMatrix = lightSpaceMatrix

        shaderProgram.uniforms.shadows = 1
        shaderProgram.uniforms.diffuseTexture = woodTexture
        shaderProgram.uniforms.shadowMap = depthMap

        RenderScene(shaderProgram)

        # 3. DEBUG: visualize depth map by rendering it to plane
        debugDepthQuad.use()
        # these are not actually used.
        #debugDepthQuad.uniforms.near_plane = near_plane
        #debugDepthQuad.uniforms.far_plane = far_plane

        debugDepthQuad.uniforms.depthMap = depthMap
        # RenderQuad() # uncomment this line to see depth map


        # Swap the buffers
        window.swapBuffers()

    return 0