Python Forum
PyGlet Trouble Drawing Cube. - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: Python Coding (https://python-forum.io/forum-7.html)
+--- Forum: Game Development (https://python-forum.io/forum-11.html)
+--- Thread: PyGlet Trouble Drawing Cube. (/thread-7126.html)



PyGlet Trouble Drawing Cube. - Windspar - Dec-21-2017

I missing something. Just can't get that cube to draw.
Using numpy and modern Opengl. My graphic card support upto 4.6.
import pyglet
from pyglet.gl import *
from pyglet.graphics.vertexbuffer import create_buffer
from random import randrange
import numpy as np
import ctypes
import math

#           base on gl3n row major
def perspective(width, height, fov, near, far):
    array = np.array([0 for i in range(16)], dtype=float).reshape(4,4)
    mat = np.matrix(array)
    aspect = width/height
    top = near * math.tan(fov * (math.pi/360.0))
    bottom = -top
    right = top * aspect
    left = -right

    mat[0,0] = (2 * near) / (right - left)
    mat[0,2] = (right + left) / (right - left)
    mat[1,1] = (2 * near) / (top - bottom)
    mat[1,2] = (top + bottom) / (top - bottom)
    mat[2,2] = -(far + near) / (far - near)
    mat[2,3] = -(2 * far + near)  / (far - near)
    mat[3,2] = -1
    return mat

def normalized(array):
    if np.linalg.norm(array) != 0:
        return array / np.linalg.norm(array)
    return array

def matrix_idenity():
    return np.matrix(np.identity(4, dtype=float))

def look_at(eye, target, up):
    look_dir = normalized(target - eye)
    up_dir = normalized(up)
    right_dir = normalized(np.cross(look_dir, up_dir))
    perp_up_dir = np.cross(right_dir, look_dir)

    mat = matrix_idenity()
    mat[0] = np.append(right_dir, [-np.dot(eye, right_dir)])
    mat[1] = np.append(perp_up_dir, [-np.dot(eye, perp_up_dir)])
    mat[2] = np.append(-look_dir, [np.dot(eye, look_dir)])

    return mat

class Face:
    FRONT = 1     # counter clockwise
    BACK = 2
    LEFT = 4     # counter clockwise
    RIGHT = 8
    TOP = 16     # counter clockwise
    BOTTOM = 32
    ALL = 63

def create_cube(size, face=Face.ALL, element=True):
    array = np.array([
    size, size, size,       #0  + + +
    -size, -size, -size,    #1  - - -
    size, -size, -size,     #2  + - -
    size, size, -size,      #3  + + -
    -size, size, -size,     #4  - + -
    -size, size, size,      #5  - + +
    -size, -size, size,     #6  - - +
    size, -size, size       #7  + - +
    ], GLfloat)

    elements = np.array([], GLuint)
    if face & Face.BACK:
        elements = np.append(elements, [1,3,2,3,1,4])
    if face & Face.FRONT:
        elements = np.append(elements, [1,7,0,0,5,6])
    if face & Face.LEFT:
        elements = np.append(elements, [5,4,1,1,6,5])
    if face & Face.RIGHT:
        elements = np.append(elements, [0,2,3,2,0,7])
    if face & Face.TOP:
        elements = np.append(elements, [4,0,3,0,4,5])
    if face & Face.BOTTOM:
        elements = np.append(elements, [1,2,7,7,6,1])

    if element:
        return array, elements

    pylist = []
    for i in elements:
        n = i * 3
        pylist.extend(array[n:n + 3])

    return np.array(pylist, GLfloat)

class Shader:
    def __init__(self):
        self.program = GLuint(glCreateProgram())

        vbuffer = open('vshader.glsl', 'r')
        vshader = glCreateShader(GL_VERTEX_SHADER)
        vbuff = ctypes.create_string_buffer(vbuffer.read().encode())
        vtext = ctypes.cast(ctypes.pointer(ctypes.pointer(vbuff)), ctypes.POINTER(ctypes.POINTER(GLchar)))
        glShaderSource(vshader, 1, vtext, None)
        glCompileShader(vshader)

        success = GLint()
        infoLog = ctypes.create_string_buffer(512)
        glGetShaderiv(vshader, GL_COMPILE_STATUS, ctypes.byref(success))
        if not success:
            glGetShaderInfoLog(vshader, 512, None, infoLog)
            print("Error: Vertex Shader ", infoLog.value)

        fbuffer = open('fshader.glsl', 'r')
        fshader = glCreateShader(GL_FRAGMENT_SHADER)
        fbuff = ctypes.create_string_buffer(fbuffer.read().encode())
        ftext = ctypes.cast(ctypes.pointer(ctypes.pointer(fbuff)), ctypes.POINTER(ctypes.POINTER(GLchar)))
        glShaderSource(fshader, 1, ftext, None)
        glCompileShader(fshader)

        glGetShaderiv(fshader, GL_COMPILE_STATUS, ctypes.byref(success))
        if not success:
            glGetShaderInfoLog(fshader, 512, None, infoLog)
            print("Error: Fragment Shader ", infoLog.value)

        glAttachShader(self.program, vshader)
        glAttachShader(self.program, fshader)
        glLinkProgram(self.program)

        glGetProgramiv(self.program, GL_LINK_STATUS, ctypes.byref(success))
        if not success:
            glGetShaderInfoLog(fshader, 512, None, infoLog)
            print("Error: Program ", infoLog.value)

        # clean up
        glDeleteShader(vshader)
        glDeleteShader(fshader)
        vbuffer.close()
        fbuffer.close()

    def uniformMatrix4fv(self, key, count, transpose, value):
        buff = ctypes.create_string_buffer(key.encode())
        location = glGetUniformLocation(self.program, buff)
        nvalue = np.array(value, GLfloat) # .flatten()
        nref = nvalue.ctypes.data_as(ctypes.POINTER(ctypes.c_float))
        glUniformMatrix4fv(location, count, transpose, nref)

    def use(self):
        glUseProgram(self.program)

    def destroy(self):
        glDeleteProgram(self.program)

class Window(pyglet.window.Window):
    def __init__(self):
        pyglet.window.Window.__init__(self)
        self.context.config.alpha_size = 8
        self.context.config.buffer_size = 32
        self.context.config.sample_buffers = 1
        self.context.config.samples = 4

        glEnable(GL_DEPTH_TEST)
        glDepthFunc(GL_LESS)
        glClearColor(0.1, 0.0, 0.0, 1.0)
        glViewport(0, 0, self.width, self.height)
        print(self.width, self.height)

        self.model = matrix_idenity()
        eye = np.array([0,0,4], dtype=float)
        self.view = look_at(eye,
            eye + np.array([0,0,4], dtype=float),
            np.array([0,0,4], dtype=float))

        self.projection = perspective(self.width, self.height, 45.0, 1.0, 100.0)

        self.vertices = create_cube(0.4, element=False)
        self.vertices[2::3] -= 8
        print(self.vertices[:6])
        colors = []
        for i in range(6):
            color = [randrange(20, 256), randrange(20, 256), randrange(20, 256)]
            colors.extend(color + color + color + color + color + color)

        #self.cube = pyglet.graphics.vertex_list(36, ('v3f', verticies), ('c3B/static', colors))
        self.vao = GLuint()
        glGenVertexArrays(1, self.vao)
        glBindVertexArray(self.vao)

        self.vbo = create_buffer(self.vertices.nbytes, GL_ARRAY_BUFFER, GL_STATIC_DRAW)
        self.vbo.bind()
        self.vbo.set_data(self.vertices.ctypes.data)


        #glGenBuffers(1, pointer(self.VBO))
        #glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_STATIC_DRAW)
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, self.vertices.nbytes, 0);
        glEnableVertexAttribArray(0);
        glBindVertexArray(0);

        self.shader = Shader()

    def on_draw(self):
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        self.shader.use()
        self.shader.uniformMatrix4fv('view', 1, GL_TRUE, self.view)
        self.shader.uniformMatrix4fv('model', 1, GL_TRUE, self.model)
        self.shader.uniformMatrix4fv('proj', 1, GL_TRUE, self.projection)

        glBindVertexArray(self.vao);
        glDrawArrays(GL_TRIANGLES, 0, len(self.vertices))
        glBindVertexArray(0)

if __name__ == '__main__':
    window = Window()
    pyglet.app.run()
Shaders
#version 330 core

layout (location = 0) in vec3 position;

uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;

void main()
{
	gl_Position = proj * view * model * vec4(position, 1.0f);
}
#version 330 core

void main()
{
	gl_FragColor = vec4(0.0, 0.0, 0.6, 1.0);
}



RE: PyGlet Trouble Drawing Cube. - Windspar - Dec-25-2017

I have the cube drawing but the color is off. Color is to be on 1 side.
import pyglet
from pyglet.gl import *
from pyglet.graphics.vertexbuffer import create_buffer
from random import randrange
import numpy as np
import ctypes
import math
import os

#           base on gl3n row major
def perspective(width, height, fov, near, far):
    array = np.array([0 for i in range(16)], dtype=GLfloat).reshape(4,4)
    mat = np.matrix(array)
    aspect = width/height
    top = near * math.tan(fov * (math.pi/360.0))
    bottom = -top
    right = top * aspect
    left = -right

    mat[0,0] = (2 * near) / (right - left)
    mat[0,2] = (right + left) / (right - left)
    mat[1,1] = (2 * near) / (top - bottom)
    mat[1,2] = (top + bottom) / (top - bottom)
    mat[2,2] = -(far + near) / (far - near)
    mat[2,3] = -(2 * far + near)  / (far - near)
    mat[3,2] = -1
    return mat

def normalized(array):
    if np.linalg.norm(array) != 0:
        return array / np.linalg.norm(array)
    return array

def matrix_idenity():
    return np.matrix(np.identity(4, GLfloat))

def look_at(eye, target, up):
    look_dir = normalized(target - eye)
    up_dir = normalized(up)
    right_dir = normalized(np.cross(look_dir, up_dir))
    perp_up_dir = np.cross(right_dir, look_dir)

    mat = matrix_idenity()
    mat[0] = np.append(right_dir, [-np.dot(eye, right_dir)])
    mat[1] = np.append(perp_up_dir, [-np.dot(eye, perp_up_dir)])
    mat[2] = np.append(-look_dir, [np.dot(eye, look_dir)])

    return mat

def unit_vector(data):
    data = np.array(data, dtype=GLfloat, copy=True)
    if data.ndim == 1:
        data /= math.sqrt(np.dot(data, data))
        return data

def rotation(alpha, axis):
    cosa = math.cos(alpha)
    sina = math.sin(alpha)
    axis = unit_vector(axis)
    R = np.diag([cosa, cosa, cosa])
    R += np.outer(axis, axis) * (1.0 - cosa)
    axis *= sina
    R += np.array([[0.0, -axis[2], axis[1]],
                   [axis[2], 0.0, -axis[0]],
                   [-axis[1], axis[0], 0.0]], GLfloat)
    M = np.identity(4)
    M[:3, :3] = R
    return M

class Face:
    FRONT = 1     # counter clockwise
    BACK = 2
    LEFT = 4     # counter clockwise
    RIGHT = 8
    TOP = 16     # counter clockwise
    BOTTOM = 32
    ALL = 63

def create_cube(size, face=Face.ALL, element=True):
    array = np.array([
    size, size, size,       #0  + + + right   top      front
    -size, -size, -size,    #1  - - - left    bottom   back
    size, -size, -size,     #2  + - - right   bottom   back
    size, size, -size,      #3  + + - right   top      back
    -size, size, -size,     #4  - + - left    top      back
    -size, size, size,      #5  - + + left    top      front
    -size, -size, size,     #6  - - + left    bottom   front
    size, -size, size       #7  + - + right   bottom   front
    ], GLfloat)

    elements = np.array([], GLuint)
    if face & Face.BACK:
        elements = np.append(elements, [1,3,2,3,1,4])
    if face & Face.FRONT:
        elements = np.append(elements, [6,7,0,0,5,6])
    if face & Face.LEFT:
        elements = np.append(elements, [5,4,1,1,6,5])
    if face & Face.RIGHT:
        elements = np.append(elements, [0,2,3,2,0,7])
    if face & Face.TOP:
        elements = np.append(elements, [4,0,3,0,4,5])
    if face & Face.BOTTOM:
        elements = np.append(elements, [1,2,7,7,6,1])

    if element:
        return array, elements

    pylist = []
    for i in elements:
        n = i * 3
        pylist.extend(array[n:n + 3])

    return np.array(pylist, GLfloat)

class Shader:
    def __init__(self, vstring, fstring):
        self.program = GLuint(glCreateProgram())

        if not os.path.exists(vstring):
            print(vstring, "doesn't exists")
            return

        if not os.path.exists(fstring):
            print(fstring, "doesn't exists")
            return

        vbuffer = open(vstring, 'r')
        vshader = glCreateShader(GL_VERTEX_SHADER)
        vbuff = ctypes.create_string_buffer(vbuffer.read().encode())
        vtext = ctypes.cast(ctypes.pointer(ctypes.pointer(vbuff)), ctypes.POINTER(ctypes.POINTER(GLchar)))
        glShaderSource(vshader, 1, vtext, None)
        glCompileShader(vshader)

        success = GLint()
        infoLog = ctypes.create_string_buffer(512)
        glGetShaderiv(vshader, GL_COMPILE_STATUS, ctypes.byref(success))
        if not success:
            glGetShaderInfoLog(vshader, 512, None, infoLog)
            print("Error: Vertex Shader ", infoLog.value)

        fbuffer = open(fstring, 'r')
        fshader = glCreateShader(GL_FRAGMENT_SHADER)
        fbuff = ctypes.create_string_buffer(fbuffer.read().encode())
        ftext = ctypes.cast(ctypes.pointer(ctypes.pointer(fbuff)), ctypes.POINTER(ctypes.POINTER(GLchar)))
        glShaderSource(fshader, 1, ftext, None)
        glCompileShader(fshader)

        glGetShaderiv(fshader, GL_COMPILE_STATUS, ctypes.byref(success))
        if not success:
            glGetShaderInfoLog(fshader, 512, None, infoLog)
            print("Error: Fragment Shader ", infoLog.value)

        glAttachShader(self.program, vshader)
        glAttachShader(self.program, fshader)
        glLinkProgram(self.program)

        glGetProgramiv(self.program, GL_LINK_STATUS, ctypes.byref(success))
        if not success:
            glGetShaderInfoLog(fshader, 512, None, infoLog)
            print("Error: Program ", infoLog.value)

        # clean up
        glDeleteShader(vshader)
        glDeleteShader(fshader)
        vbuffer.close()
        fbuffer.close()

    def uniformMatrix4fv(self, key, count, transpose, value):
        buff = ctypes.create_string_buffer(key.encode())
        location = glGetUniformLocation(self.program, buff)
        nvalue = np.array(value, GLfloat) # .flatten()
        nref = nvalue.ctypes.data_as(ctypes.POINTER(ctypes.c_float))
        glUniformMatrix4fv(location, count, transpose, nref)

    def use(self):
        glUseProgram(self.program)

    def destroy(self):
        glDeleteProgram(self.program)

class Window(pyglet.window.Window):
    def __init__(self):
        pyglet.window.Window.__init__(self)
        self.context.config.alpha_size = 8
        self.context.config.buffer_size = 32
        self.context.config.sample_buffers = 1
        self.context.config.samples = 4

        glEnable(GL_DEPTH_TEST)
        glDepthFunc(GL_LESS)
        glClearColor(0.1, 0.0, 0.0, 1.0)
        glViewport(0, 0, self.width, self.height)

        self.model = matrix_idenity()
        self.view = look_at(
            np.array([4,3,3], dtype=GLfloat),
            np.array([0,0,0], dtype=GLfloat),
            np.array([0,1,0], dtype=GLfloat))

        self.projection = perspective(self.width, self.height, 45.0, 1.0, 100.0)
        self.mvp = self.projection * self.view * self.model

        self.vertices = create_cube(0.6, element=False)
        print("How many vertices.", len(self.vertices) / 3)
        color_choice = [[1.0,0.0,0.0], [0.0,1.0,0.0], [0.0,0.0,1.0],
            [1.0,0.0,1.0], [1.0,1.0,0.0], [0.0,0.5,1.0]]
        colors = []
        for i in range(6):
            color = color_choice[i]
            colors.extend(color + color + color + color + color + color)
        self.colors = np.array(colors, GLfloat)
        print("How many colors.", len(self.colors) / 3)

        self.vao = GLuint()
        glGenVertexArrays(1, self.vao)
        glBindVertexArray(self.vao)

        self.vbo = create_buffer(self.vertices.nbytes, GL_ARRAY_BUFFER, GL_STATIC_DRAW)
        self.vbo.set_data(self.vertices.ctypes.data)
        self.vbo.bind()

        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
        glEnableVertexAttribArray(0);

        self.color_vbo = create_buffer(self.colors.nbytes, GL_ARRAY_BUFFER, GL_STATIC_DRAW)
        self.color_vbo.set_data(self.colors.ctypes.data)
        self.color_vbo.bind()

        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
        glEnableVertexAttribArray(1);
        glBindVertexArray(0);

        self.tick = 0.0
        self.shader = Shader('vshader.glsl', 'fshader.glsl')
        pyglet.clock.schedule_interval(self.tick_update, 1.0 / 60.0)

    def tick_update(self, dt):
        self.tick += dt
        self.model = rotation(self.tick, np.array([0.1, 0.2, 0.3], GLfloat))
        self.mvp = self.projection * self.view * self.model

    def on_draw(self):
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        self.shader.use()
        self.shader.uniformMatrix4fv('mvp', 1, GL_TRUE, self.mvp)

        glBindVertexArray(self.vao);
        glDrawArrays(GL_TRIANGLES, 0, len(self.vertices))
        glBindVertexArray(0)

    def clean_up(self):
        self.vbo.delete()
        self.color_vbo.delete()
        glDeleteVertexArrays(1, self.vao)

if __name__ == '__main__':
    window = Window()
    pyglet.app.run()
    window.clean_up()
shaders : edit replace duplicate fragment shader with vertex shader
#version 330 core

layout (location = 0) in vec3 position;
layout (location = 0) in vec3 color;

out vec3 fragColor;
uniform mat4 mvp;

void main()
{
	gl_Position = mvp * vec4(position, 1.0f);
    fragColor = color;
}
#version 330 core

in vec3 fragColor;
out vec3 color;

void main()
{
	color = fragColor;
}

Okay all figure out. Replace duplicate fragment shader above.
Color mistake was because I forgot to change layout location to 1.
layout (location = 0) in vec3 color;
to
layout (location = 1) in vec3 color;



RE: PyGlet Trouble Drawing Cube. - nilamo - Jan-02-2018

Thanks for sharing what the solution was :)

Also, thanks for sharing some code, I was recently wondering if python could use shaders easily, and it doesn't look that difficult at all.


RE: PyGlet Trouble Drawing Cube. - Windspar - Jan-02-2018

Shaders has nothing to do with python. Shaders are all opengl.
I was pulling my hair out. Dealing with pyglet and ctypes for the first time.
Doing this in D with opengl was simple. No ctypes.

Wish PyGlet wrap the cytpes. For I don't have to deal with them.
Need another option for opengl in python with no ctypes.