Skip to content

Instantly share code, notes, and snippets.

@xvedejas
Created October 29, 2015 03:43
Show Gist options
  • Save xvedejas/b37e0babbf6c0f9c9ffa to your computer and use it in GitHub Desktop.
Save xvedejas/b37e0babbf6c0f9c9ffa to your computer and use it in GitHub Desktop.
#!/usr/bin/python3
# This is a sample bit of code that shows how to draw arbitrary axis-aligned
# boxes in 3D space.
# based on: https://gist.github.com/binarycrusader/5823716a1da5f0273504
import ctypes
import numpy as np
import time
from OpenGL import GL, GLU, GLUT
from OpenGL.GL import shaders
from OpenGL.arrays import vbo
import sdl2, sdl2.sdlimage, sdl2.ext
def sdl_load(resource):
image = sdl2.sdlimage.IMG_Load(resource)
try:
image.contents # Gives null pointer exception on failure
except ValueError:
print(sdl2.SDL_GetError(image))
exit(1)
return image
def gl_load(path):
img = sdl_load(path).contents
array = sdl2.ext.pixels3d(img)
mode = GL.GL_RGBA
texture = GL.glGenTextures(1)
GL.glActiveTexture(GL.GL_TEXTURE0)
GL.glBindTexture(GL.GL_TEXTURE_2D, texture)
GL.glTexImage2D(GL.GL_TEXTURE_2D, 0,mode , img.w, img.h, 0, mode, GL.GL_UNSIGNED_BYTE, array)
GL.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST)
GL.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR)
return texture
class Box():
# This vertex data defines a cube (minus the bottom face) in terms of a
# strip of triangles
vertex_data = np.array([
# X Y Z U V
0, 0, -1, 2/3, 2/3,
1, 0, 0, 1/3, 1,
0, 0, 0, 2/3, 1,
1, 0, 1, 1/3, 2/3,
1, 0, 0, 1/3, 1,
0, 0, 1, 2/3, 2/3,
1, 1, 1, 1/3, 1/3,
1, 0, 0, 0, 2/3,
1, 0, 1, 1/3, 2/3,
1, 1, 0, 0, 1/3,
1, 0, 0, 0, 2/3,
1, 1, 1, 1/3, 1/3,
0, 1, 1, 2/3, 1/3,
1, 0, 1, 1/3, 2/3,
0, 0, 1, 2/3, 2/3,
1, 1, 1, 1/3, 1/3,
1, 0, 1, 1/3, 2/3,
0, 1, 1, 2/3, 1/3,
0, 1, 0, 1, 1/3,
0, 0, 1, 2/3, 2/3,
0, 0, 0, 1, 2/3,
0, 1, 1, 2/3, 1/3,
0, 0, 1, 2/3, 2/3,
0, 1, 0, 1, 1/3,
0, 1, 0, 2/3, 0,
1, 1, 1, 1/3, 1/3,
0, 1, 1, 2/3, 1/3,
1, 1, 0, 1/3, 0,
1, 1, 1, 1/3, 1/3,
0, 1, 0, 2/3, 0,
], dtype=np.float32)
def __init__(self, position, size, texture, shader_program):
self.position = position
self.size = size
x, y, z = position
w, l, h = size
#self.matrix = np.matrix([[1, 0, 0, x], [0, 1, 0, y], [0, 0, 1, z], [0, 0, 0, 1]], dtype=np.float32)
self.matrix = np.matrix([[w, 0, 0, 0], [0, l, 0, 0], [0, 0, h, 0], [x, y, z, 1]], dtype=np.float32)
self.shader_program = shader_program
# Core OpenGL requires that at least one OpenGL vertex array be bound
self.VAO = GL.glGenVertexArrays(1)
GL.glBindVertexArray(self.VAO)
# Need VBO for triangle vertices and texture UV coordinates
self.VBO = GL.glGenBuffers(1)
GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.VBO)
GL.glBufferData(GL.GL_ARRAY_BUFFER, self.vertex_data.nbytes, self.vertex_data, GL.GL_STATIC_DRAW)
# enable array and set up data
positionAttrib = GL.glGetAttribLocation(self.shader_program, 'position')
coordsAttrib = GL.glGetAttribLocation(self.shader_program, 'texCoords')
GL.glEnableVertexAttribArray(0)
GL.glEnableVertexAttribArray(1)
GL.glVertexAttribPointer(positionAttrib, 3, GL.GL_FLOAT, GL.GL_FALSE, 4*5, None)
GL.glVertexAttribPointer(coordsAttrib, 2, GL.GL_FLOAT, GL.GL_TRUE, 4*5, ctypes.c_void_p(4*3))
self.texture = texture
self.texUnitUniform = GL.glGetUniformLocation(self.shader_program, 'texUnit')
# Finished
GL.glBindBuffer(GL.GL_ARRAY_BUFFER, 0)
GL.glBindVertexArray(0)
def render(self, matrix):
GL.glUseProgram(self.shader_program)
MVP = GL.glGetUniformLocation(self.shader_program, "MVPMatrix");
GL.glUniformMatrix4fv(MVP, 1, GL.GL_FALSE, self.matrix * matrix);
try:
# Activate texture
GL.glActiveTexture(GL.GL_TEXTURE0)
GL.glBindTexture(GL.GL_TEXTURE_2D, self.texture)
GL.glUniform1i(self.texUnitUniform, 0)
# Activate array
GL.glBindVertexArray(self.VAO)
# draw triangle
GL.glDrawArrays(GL.GL_TRIANGLES, 0, len(self.vertex_data)//5)
finally:
GL.glBindVertexArray(0)
GL.glUseProgram(0)
class PipelineGL():
def __init__(self):
return
def __enter__(self):
sdl2.SDL_Init(sdl2.SDL_INIT_EVERYTHING)
sdl2.sdlttf.TTF_Init()
self.window = sdl2.SDL_CreateWindow(b"demo",
sdl2.SDL_WINDOWPOS_UNDEFINED,
sdl2.SDL_WINDOWPOS_UNDEFINED,
800, 600,
sdl2.SDL_WINDOW_OPENGL)
self.surface = sdl2.SDL_GetWindowSurface(self.window)
# Change this if using hardware acceleration
self.renderer = sdl2.SDL_CreateSoftwareRenderer(self.surface, -1, 0)
# Force OpenGL 3.3 'core' context. Must set *before* creating GL context
sdl2.video.SDL_GL_SetAttribute(sdl2.video.SDL_GL_CONTEXT_MAJOR_VERSION, 3)
sdl2.video.SDL_GL_SetAttribute(sdl2.video.SDL_GL_CONTEXT_MINOR_VERSION, 3)
sdl2.video.SDL_GL_SetAttribute(sdl2.video.SDL_GL_CONTEXT_PROFILE_MASK,
sdl2.video.SDL_GL_CONTEXT_PROFILE_CORE)
self.context = sdl2.SDL_GL_CreateContext(self.window)
# Setup GL shaders, data, etc.
vertexShader = shaders.compileShader("""
#version 330
layout (location=0) in vec3 position;
layout (location=1) in vec2 texCoords;
uniform mat4 MVPMatrix;
out vec2 theCoords;
void main()
{
gl_Position = MVPMatrix * vec4(position, 1);
//gl_Position = vec4(position, 1);
theCoords = texCoords;
}
""", GL.GL_VERTEX_SHADER)
fragmentShader = shaders.compileShader("""
#version 330
uniform sampler2D texUnit;
in vec2 theCoords;
out vec4 outputColour;
void main()
{
outputColour = texture(texUnit, theCoords);
}
""", GL.GL_FRAGMENT_SHADER)
self.shader_program = shaders.compileProgram(vertexShader, fragmentShader)
texture = gl_load(b'test.png')
self.boxes = [Box((0.0, 1.0, 0.0), (1.0, 1.0, 1.0), texture, self.shader_program),
Box((0.0, 0.0, 1.0), (1.0, 1.0, 1.0), texture, self.shader_program),
Box((1.0, 0.0, 0.0), (1.0, 1.0, 1.0), texture, self.shader_program),]
event = sdl2.SDL_Event()
return self
def __exit__(self, t, value, traceback):
sdl2.SDL_GL_DeleteContext(self.context)
sdl2.SDL_DestroyWindow(self.window)
sdl2.SDL_Quit()
def render(self):
GL.glClearColor(0, 0, 0, 1)
GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)
zoom = 0.2
MVP = np.matrix([[zoom, 0, 0, 0],
[0, zoom, 0, 0],
[0, 0, zoom, 0],
[0, 0, 0, 1.0]],
dtype=np.float32)
#~ a = 135 / 180 * np.pi
a = (time.time() % 4) / 2 * np.pi
MVP *= np.matrix([[np.cos(a), np.sin(a), 0, 0],
[-np.sin(a), np.cos(a), 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]],
dtype=np.float32)
b = (time.time() % 8) / 4 * np.pi
MVP *= np.matrix([[1, 0, 0, 0],
[0, np.cos(b), np.sin(b), 0],
[0, -np.sin(b), np.cos(b), 0],
[0, 0, 0, 1]],
dtype=np.float32)
for box in self.boxes:
box.render(MVP)
sdl2.SDL_GL_SwapWindow(self.window)
sdl2.SDL_Delay(10)
def run():
with PipelineGL() as pgl:
running = True
while running:
## handle events here
pgl.render()
return 0
if __name__ == "__main__":
exit(run())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment