├── 1-1-Context_Creation.py ├── 2-1-Drawing_Polygons-Triangle.py ├── 2-2-Drawing_Polygons-Triangle_Uniform.py ├── 2-3-Drawing_Polygons-Color_Triangle.py ├── 2-4-Drawing_Polygons-Triangle_Elements.py ├── 3-1-Textures-Basic.py ├── 3-2-Textures-Multitexture.py ├── 4-1-Transformations-Transformation.py ├── 4-2-Transformations-3D.py ├── 5-1-Depth_and_Stencils-Cube.py ├── 5-2-Depth_and_Stencils-Reflection.py ├── README.md ├── euclid.py ├── sample.png └── sample2.png /1-1-Context_Creation.py: -------------------------------------------------------------------------------- 1 | # Tutorial: https://open.gl/context 2 | # Original Sample Code: ... 3 | 4 | import pyglet 5 | from pyglet.gl import * 6 | 7 | window = pyglet.window.Window(800, 600, "OpenGL") 8 | window.set_location(100, 100) 9 | 10 | @window.event 11 | def on_draw(): 12 | pass 13 | 14 | @window.event 15 | def on_key_press(symbol, modifiers): 16 | pass 17 | 18 | @window.event 19 | def on_key_release(symbol, modifiers): 20 | print symbol 21 | 22 | def update(dt): 23 | pass 24 | pyglet.clock.schedule(update) 25 | 26 | pyglet.app.run() 27 | -------------------------------------------------------------------------------- /2-1-Drawing_Polygons-Triangle.py: -------------------------------------------------------------------------------- 1 | # Tutorial: https://open.gl/drawing 2 | # Original Sample Code: https://github.com/Overv/Open.GL/blob/master/content/code/c2_triangle.txt 3 | 4 | import pyglet 5 | from pyglet.gl import * 6 | from ctypes import * 7 | 8 | 9 | window = pyglet.window.Window(800, 600, "OpenGL") 10 | window.set_location(100, 100) 11 | 12 | 13 | # Shaders (Vertex and Fragment shaders) 14 | vertexSource = """ 15 | #version 130 16 | 17 | in vec2 position; 18 | 19 | void main() 20 | { 21 | gl_Position = vec4(position, 0.0, 1.0); 22 | } 23 | """ 24 | fragmentSource = """ 25 | #version 130 26 | 27 | out vec4 outColor; 28 | 29 | void main() 30 | { 31 | outColor = vec4(1.0, 1.0, 1.0, 1.0); 32 | } 33 | """ 34 | 35 | 36 | # Vertex Input 37 | ## Vertex Array Objects 38 | vao = GLuint() 39 | glGenVertexArrays(1, pointer(vao)) 40 | glBindVertexArray(vao) 41 | 42 | ## Vertex Buffer Object 43 | vbo = GLuint() 44 | glGenBuffers(1, pointer(vbo)) # Generate 1 buffer 45 | 46 | vertices = [0.0, 0.5, 47 | 0.5, -0.5, 48 | -0.5, -0.5] 49 | ## Convert the verteces array to a GLfloat array, usable by glBufferData 50 | vertices_ctype = (GLfloat * len(vertices))(*vertices) 51 | 52 | ## Upload data to GPU 53 | glBindBuffer(GL_ARRAY_BUFFER, vbo) 54 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_ctype), vertices_ctype, GL_STATIC_DRAW) 55 | 56 | 57 | # Compile shaders and combining them into a program 58 | ## Create and compile the vertex shader 59 | count = len(vertexSource) 60 | src = (c_char_p * count)(*vertexSource) 61 | vertexShader = glCreateShader(GL_VERTEX_SHADER) 62 | glShaderSource(vertexShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 63 | glCompileShader(vertexShader) 64 | 65 | ## Create and compile the fragment shader 66 | count = len(fragmentSource) 67 | src = (c_char_p * count)(*fragmentSource) 68 | fragmentShader = glCreateShader(GL_FRAGMENT_SHADER) 69 | glShaderSource(fragmentShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 70 | glCompileShader(fragmentShader) 71 | 72 | ## Link the vertex and fragment shader into a shader program 73 | shaderProgram = glCreateProgram() 74 | glAttachShader(shaderProgram, vertexShader) 75 | glAttachShader(shaderProgram, fragmentShader) 76 | glBindFragDataLocation(shaderProgram, 0, "outColor") 77 | glLinkProgram(shaderProgram) 78 | glUseProgram(shaderProgram) 79 | 80 | 81 | # Making the link between vertex data and attributes 82 | posAttrib = glGetAttribLocation(shaderProgram, "position") 83 | glEnableVertexAttribArray(posAttrib) 84 | glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0) 85 | 86 | 87 | @window.event 88 | def on_draw(): 89 | # Set clear color 90 | glClearColor(0.0, 0.0, 0.0, 1.0) 91 | #Clear the screen to black 92 | glClear(GL_COLOR_BUFFER_BIT) 93 | 94 | #Draw a triangle from the 3 vertices 95 | glDrawArrays(GL_TRIANGLES, 0, 3) 96 | 97 | @window.event 98 | def on_key_press(symbol, modifiers): 99 | pass 100 | 101 | @window.event 102 | def on_key_release(symbol, modifiers): 103 | pass 104 | 105 | def update(dt): 106 | pass 107 | pyglet.clock.schedule(update) 108 | 109 | 110 | pyglet.app.run() 111 | -------------------------------------------------------------------------------- /2-2-Drawing_Polygons-Triangle_Uniform.py: -------------------------------------------------------------------------------- 1 | # Tutorial: https://open.gl/drawing 2 | # Original Sample Code: https://github.com/Overv/Open.GL/blob/master/content/code/c2_triangle_uniform.txt 3 | 4 | import pyglet 5 | from pyglet.gl import * 6 | from ctypes import * 7 | import math, time 8 | 9 | 10 | window = pyglet.window.Window(800, 600, "OpenGL") 11 | window.set_location(100, 100) 12 | 13 | 14 | # Shaders (Vertex and Fragment shaders) 15 | vertexSource = """ 16 | #version 130 17 | 18 | in vec2 position; 19 | 20 | void main() 21 | { 22 | gl_Position = vec4(position, 0.0, 1.0); 23 | } 24 | """ 25 | fragmentSource = """ 26 | #version 130 27 | 28 | uniform vec3 triangleColor; 29 | 30 | out vec4 outColor; 31 | 32 | void main() 33 | { 34 | outColor = vec4(triangleColor, 1.0); 35 | } 36 | """ 37 | 38 | 39 | # Vertex Input 40 | ## Vertex Array Objects 41 | vao = GLuint() 42 | glGenVertexArrays(1, pointer(vao)) 43 | glBindVertexArray(vao) 44 | 45 | ## Vertex Buffer Object 46 | vbo = GLuint() 47 | glGenBuffers(1, pointer(vbo)) # Generate 1 buffer 48 | 49 | vertices = [0.0, 0.5, 50 | 0.5, -0.5, 51 | -0.5, -0.5] 52 | ## Convert the verteces array to a GLfloat array, usable by glBufferData 53 | vertices_ctype = (GLfloat * len(vertices))(*vertices) 54 | 55 | ## Upload data to GPU 56 | glBindBuffer(GL_ARRAY_BUFFER, vbo) 57 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_ctype), vertices_ctype, GL_STATIC_DRAW) 58 | 59 | 60 | # Compile shaders and combining them into a program 61 | ## Create and compile the vertex shader 62 | count = len(vertexSource) 63 | src = (c_char_p * count)(*vertexSource) 64 | vertexShader = glCreateShader(GL_VERTEX_SHADER) 65 | glShaderSource(vertexShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 66 | glCompileShader(vertexShader) 67 | 68 | ## Create and compile the fragment shader 69 | count = len(fragmentSource) 70 | src = (c_char_p * count)(*fragmentSource) 71 | fragmentShader = glCreateShader(GL_FRAGMENT_SHADER) 72 | glShaderSource(fragmentShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 73 | glCompileShader(fragmentShader) 74 | 75 | ## Link the vertex and fragment shader into a shader program 76 | shaderProgram = glCreateProgram() 77 | glAttachShader(shaderProgram, vertexShader) 78 | glAttachShader(shaderProgram, fragmentShader) 79 | glBindFragDataLocation(shaderProgram, 0, "outColor") 80 | glLinkProgram(shaderProgram) 81 | glUseProgram(shaderProgram) 82 | 83 | 84 | # Making the link between vertex data and attributes 85 | posAttrib = glGetAttribLocation(shaderProgram, "position") 86 | glEnableVertexAttribArray(posAttrib) 87 | glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0) 88 | 89 | uniColor = glGetUniformLocation(shaderProgram, "triangleColor") 90 | 91 | 92 | @window.event 93 | def on_draw(): 94 | # Set the color of the triangle 95 | alpha = (math.sin(time.clock() * 4.0) + 1.0) / 2.0 96 | glUniform3f(uniColor, alpha, 0.0, 0.0) 97 | 98 | # Set clear color 99 | glClearColor(0.0, 0.0, 0.0, 1.0) 100 | # Clear the screen to black 101 | glClear(GL_COLOR_BUFFER_BIT) 102 | 103 | # Draw a triangle from the 3 vertices 104 | glDrawArrays(GL_TRIANGLES, 0, 3) 105 | 106 | @window.event 107 | def on_key_press(symbol, modifiers): 108 | pass 109 | 110 | @window.event 111 | def on_key_release(symbol, modifiers): 112 | pass 113 | 114 | def update(dt): 115 | pass 116 | pyglet.clock.schedule(update) 117 | 118 | 119 | pyglet.app.run() 120 | -------------------------------------------------------------------------------- /2-3-Drawing_Polygons-Color_Triangle.py: -------------------------------------------------------------------------------- 1 | # Tutorial: https://open.gl/drawing 2 | # Original Sample Code: https://github.com/Overv/Open.GL/blob/master/content/code/c2_color_triangle.txt 3 | 4 | import pyglet 5 | from pyglet.gl import * 6 | from ctypes import * 7 | import math, time 8 | 9 | 10 | window = pyglet.window.Window(800, 600, "OpenGL") 11 | window.set_location(100, 100) 12 | 13 | 14 | # Shaders (Vertex and Fragment shaders) 15 | vertexSource = """ 16 | #version 130 17 | 18 | in vec2 position; 19 | in vec3 color; 20 | 21 | out vec3 Color; 22 | 23 | void main() 24 | { 25 | Color = color; 26 | gl_Position = vec4(position, 0.0, 1.0); 27 | } 28 | """ 29 | fragmentSource = """ 30 | #version 130 31 | 32 | in vec3 Color; 33 | 34 | out vec4 outColor; 35 | 36 | void main() 37 | { 38 | outColor = vec4(Color, 1.0); 39 | } 40 | """ 41 | 42 | 43 | # Vertex Input 44 | ## Vertex Array Objects 45 | vao = GLuint() 46 | glGenVertexArrays(1, pointer(vao)) 47 | glBindVertexArray(vao) 48 | 49 | ## Vertex Buffer Object 50 | vbo = GLuint() 51 | glGenBuffers(1, pointer(vbo)) # Generate 1 buffer 52 | 53 | vertices = [0.0, 0.5, 1.0, 0.0, 0.0, 54 | 0.5, -0.5, 0.0, 1.0, 0.0, 55 | -0.5, -0.5, 0.0, 0.0, 1.0] 56 | ## Convert the verteces array to a GLfloat array, usable by glBufferData 57 | vertices_ctype = (GLfloat * len(vertices))(*vertices) 58 | 59 | ## Upload data to GPU 60 | glBindBuffer(GL_ARRAY_BUFFER, vbo) 61 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_ctype), vertices_ctype, GL_STATIC_DRAW) 62 | 63 | 64 | # Compile shaders and combining them into a program 65 | ## Create and compile the vertex shader 66 | count = len(vertexSource) 67 | src = (c_char_p * count)(*vertexSource) 68 | vertexShader = glCreateShader(GL_VERTEX_SHADER) 69 | glShaderSource(vertexShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 70 | glCompileShader(vertexShader) 71 | 72 | ## Create and compile the fragment shader 73 | count = len(fragmentSource) 74 | src = (c_char_p * count)(*fragmentSource) 75 | fragmentShader = glCreateShader(GL_FRAGMENT_SHADER) 76 | glShaderSource(fragmentShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 77 | glCompileShader(fragmentShader) 78 | 79 | ## Link the vertex and fragment shader into a shader program 80 | shaderProgram = glCreateProgram() 81 | glAttachShader(shaderProgram, vertexShader) 82 | glAttachShader(shaderProgram, fragmentShader) 83 | glBindFragDataLocation(shaderProgram, 0, "outColor") 84 | glLinkProgram(shaderProgram) 85 | glUseProgram(shaderProgram) 86 | 87 | 88 | # Making the link between vertex data and attributes 89 | posAttrib = glGetAttribLocation(shaderProgram, "position") 90 | glEnableVertexAttribArray(posAttrib) 91 | glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 92 | 5*sizeof(GLfloat), 0) 93 | 94 | colAttrib = glGetAttribLocation(shaderProgram, "color") 95 | glEnableVertexAttribArray(colAttrib) 96 | glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE, 97 | 5*sizeof(GLfloat), 2*sizeof(GLfloat)) 98 | 99 | 100 | @window.event 101 | def on_draw(): 102 | # Set clear color 103 | glClearColor(0.0, 0.0, 0.0, 1.0) 104 | # Clear the screen to black 105 | glClear(GL_COLOR_BUFFER_BIT) 106 | 107 | # Draw a triangle from the 3 vertices 108 | glDrawArrays(GL_TRIANGLES, 0, 3) 109 | 110 | @window.event 111 | def on_key_press(symbol, modifiers): 112 | pass 113 | 114 | @window.event 115 | def on_key_release(symbol, modifiers): 116 | pass 117 | 118 | def update(dt): 119 | pass 120 | pyglet.clock.schedule(update) 121 | 122 | 123 | pyglet.app.run() 124 | -------------------------------------------------------------------------------- /2-4-Drawing_Polygons-Triangle_Elements.py: -------------------------------------------------------------------------------- 1 | # Tutorial: https://open.gl/drawing 2 | # Original Sample Code: https://github.com/Overv/Open.GL/blob/master/content/code/c2_triangle_elements.txt 3 | 4 | import pyglet 5 | from pyglet.gl import * 6 | from ctypes import * 7 | import math, time 8 | 9 | 10 | window = pyglet.window.Window(800, 600, "OpenGL") 11 | window.set_location(100, 100) 12 | 13 | 14 | # Shaders (Vertex and Fragment shaders) 15 | vertexSource = """ 16 | #version 130 17 | 18 | in vec2 position; 19 | in vec3 color; 20 | 21 | out vec3 Color; 22 | 23 | void main() 24 | { 25 | Color = color; 26 | gl_Position = vec4(position, 0.0, 1.0); 27 | } 28 | """ 29 | fragmentSource = """ 30 | #version 130 31 | 32 | in vec3 Color; 33 | 34 | out vec4 outColor; 35 | 36 | void main() 37 | { 38 | outColor = vec4(Color, 1.0); 39 | } 40 | """ 41 | 42 | 43 | # Vertex Input 44 | ## Vertex Array Objects 45 | vao = GLuint() 46 | glGenVertexArrays(1, pointer(vao)) 47 | glBindVertexArray(vao) 48 | 49 | ## Vertex Buffer Object 50 | vbo = GLuint() 51 | glGenBuffers(1, pointer(vbo)) # Generate 1 buffer 52 | 53 | vertices = [-0.5, 0.5, 1.0, 0.0, 0.0, 54 | 0.5, 0.5, 0.0, 1.0, 0.0, 55 | 0.5, -0.5, 0.0, 0.0, 1.0, 56 | -0.5, -0.5, 1.0, 1.0, 1.0] 57 | ## Convert the verteces array to a GLfloat array, usable by glBufferData 58 | vertices_ctype = (GLfloat * len(vertices))(*vertices) 59 | 60 | ## Upload data to GPU 61 | glBindBuffer(GL_ARRAY_BUFFER, vbo) 62 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_ctype), vertices_ctype, GL_STATIC_DRAW) 63 | 64 | 65 | # Compile shaders and combining them into a program 66 | ## Create and compile the vertex shader 67 | count = len(vertexSource) 68 | src = (c_char_p * count)(*vertexSource) 69 | vertexShader = glCreateShader(GL_VERTEX_SHADER) 70 | glShaderSource(vertexShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 71 | glCompileShader(vertexShader) 72 | 73 | ## Create and compile the fragment shader 74 | count = len(fragmentSource) 75 | src = (c_char_p * count)(*fragmentSource) 76 | fragmentShader = glCreateShader(GL_FRAGMENT_SHADER) 77 | glShaderSource(fragmentShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 78 | glCompileShader(fragmentShader) 79 | 80 | ## Link the vertex and fragment shader into a shader program 81 | shaderProgram = glCreateProgram() 82 | glAttachShader(shaderProgram, vertexShader) 83 | glAttachShader(shaderProgram, fragmentShader) 84 | glBindFragDataLocation(shaderProgram, 0, "outColor") 85 | glLinkProgram(shaderProgram) 86 | glUseProgram(shaderProgram) 87 | 88 | 89 | # Element array 90 | ebo = GLuint() 91 | glGenBuffers(1, pointer(ebo)) 92 | 93 | elements = [0, 1, 2, 94 | 2, 3, 0] 95 | elements_ctype = (GLuint * len(elements))(*elements) 96 | 97 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo) 98 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements_ctype), elements_ctype, GL_STATIC_DRAW) 99 | 100 | 101 | # Making the link between vertex data and attributes 102 | posAttrib = glGetAttribLocation(shaderProgram, "position") 103 | glEnableVertexAttribArray(posAttrib) 104 | glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 105 | 5*sizeof(GLfloat), 0) 106 | 107 | colAttrib = glGetAttribLocation(shaderProgram, "color") 108 | glEnableVertexAttribArray(colAttrib) 109 | glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE, 110 | 5*sizeof(GLfloat), 2*sizeof(GLfloat)) 111 | 112 | 113 | @window.event 114 | def on_draw(): 115 | # Set clear color 116 | glClearColor(0.0, 0.0, 0.0, 1.0) 117 | # Clear the screen to black 118 | glClear(GL_COLOR_BUFFER_BIT) 119 | 120 | # Draw a triangle from the 3 vertices 121 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0) 122 | 123 | @window.event 124 | def on_key_press(symbol, modifiers): 125 | pass 126 | 127 | @window.event 128 | def on_key_release(symbol, modifiers): 129 | pass 130 | 131 | def update(dt): 132 | pass 133 | pyglet.clock.schedule(update) 134 | 135 | 136 | pyglet.app.run() 137 | -------------------------------------------------------------------------------- /3-1-Textures-Basic.py: -------------------------------------------------------------------------------- 1 | # Tutorial: https://open.gl/textures 2 | # Original Sample Code: https://github.com/Overv/Open.GL/blob/master/content/code/c3_basic.txt 3 | 4 | import pyglet 5 | from pyglet.gl import * 6 | from ctypes import * 7 | import math, time 8 | 9 | 10 | window = pyglet.window.Window(800, 600, "OpenGL") 11 | window.set_location(100, 100) 12 | 13 | 14 | # Shaders (Vertex and Fragment shaders) 15 | vertexSource = """ 16 | #version 130 17 | 18 | in vec2 position; 19 | in vec3 color; 20 | in vec2 texcoord; 21 | 22 | out vec3 Color; 23 | out vec2 Texcoord; 24 | 25 | void main() { 26 | Color = color; 27 | Texcoord = texcoord; 28 | gl_Position = vec4(position, 0.0, 1.0); 29 | } 30 | """ 31 | fragmentSource = """ 32 | #version 130 33 | 34 | in vec3 Color; 35 | in vec2 Texcoord; 36 | 37 | out vec4 outColor; 38 | 39 | uniform sampler2D tex; 40 | 41 | void main() { 42 | outColor = texture(tex, Texcoord) * vec4(Color, 1.0); 43 | } 44 | """ 45 | 46 | 47 | # Vertex Input 48 | ## Vertex Array Objects 49 | vao = GLuint() 50 | glGenVertexArrays(1, pointer(vao)) 51 | glBindVertexArray(vao) 52 | 53 | ## Vertex Buffer Object 54 | vbo = GLuint() 55 | glGenBuffers(1, pointer(vbo)) # Generate 1 buffer 56 | 57 | ## Position Color Texcoords 58 | vertices = [-0.5, 0.5, 1.0, 0.0, 0.0, 0.0, 1.0, # Top-left 59 | 0.5, 0.5, 0.0, 1.0, 0.0, 1.0, 1.0, # Top-right 60 | 0.5, -0.5, 0.0, 0.0, 1.0, 1.0, 0.0, # Bottom-right 61 | -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 0.0] # Bottom-left 62 | 63 | ## Convert the verteces array to a GLfloat array, usable by glBufferData 64 | vertices_ctype = (GLfloat * len(vertices))(*vertices) 65 | 66 | ## Upload data to GPU 67 | glBindBuffer(GL_ARRAY_BUFFER, vbo) 68 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_ctype), vertices_ctype, GL_STATIC_DRAW) 69 | 70 | 71 | # Compile shaders and combining them into a program 72 | ## Create and compile the vertex shader 73 | count = len(vertexSource) 74 | src = (c_char_p * count)(*vertexSource) 75 | vertexShader = glCreateShader(GL_VERTEX_SHADER) 76 | glShaderSource(vertexShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 77 | glCompileShader(vertexShader) 78 | 79 | ## Create and compile the fragment shader 80 | count = len(fragmentSource) 81 | src = (c_char_p * count)(*fragmentSource) 82 | fragmentShader = glCreateShader(GL_FRAGMENT_SHADER) 83 | glShaderSource(fragmentShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 84 | glCompileShader(fragmentShader) 85 | 86 | ## Link the vertex and fragment shader into a shader program 87 | shaderProgram = glCreateProgram() 88 | glAttachShader(shaderProgram, vertexShader) 89 | glAttachShader(shaderProgram, fragmentShader) 90 | glBindFragDataLocation(shaderProgram, 0, "outColor") 91 | glLinkProgram(shaderProgram) 92 | glUseProgram(shaderProgram) 93 | 94 | 95 | # Element array 96 | ebo = GLuint() 97 | glGenBuffers(1, pointer(ebo)) 98 | 99 | elements = [0, 1, 2, 100 | 2, 3, 0] 101 | elements_ctype = (GLuint * len(elements))(*elements) 102 | 103 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo) 104 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements_ctype), elements_ctype, GL_STATIC_DRAW) 105 | 106 | 107 | # Making the link between vertex data and attributes 108 | posAttrib = glGetAttribLocation(shaderProgram, "position") 109 | glEnableVertexAttribArray(posAttrib) 110 | glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), 0) 111 | 112 | colAttrib = glGetAttribLocation(shaderProgram, "color") 113 | glEnableVertexAttribArray(colAttrib) 114 | glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), 2 * sizeof(GLfloat)) 115 | 116 | texAttrib = glGetAttribLocation(shaderProgram, "texcoord") 117 | glEnableVertexAttribArray(texAttrib) 118 | glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), 5 * sizeof(GLfloat)) 119 | 120 | 121 | # Load texture 122 | tex = GLuint() 123 | glGenTextures(1, pointer(tex)) 124 | 125 | image = pyglet.image.load("sample.png") 126 | width, height = image.width, image.height 127 | image = image.get_data('RGB', width * 3) 128 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image) 129 | 130 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 131 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 132 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) 133 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) 134 | 135 | 136 | @window.event 137 | def on_draw(): 138 | # Set clear color 139 | glClearColor(0.0, 0.0, 0.0, 1.0) 140 | # Clear the screen to black 141 | glClear(GL_COLOR_BUFFER_BIT) 142 | 143 | # Draw a rectangle from the 2 triangles using 6 indices 144 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0) 145 | 146 | @window.event 147 | def on_key_press(symbol, modifiers): 148 | pass 149 | 150 | @window.event 151 | def on_key_release(symbol, modifiers): 152 | pass 153 | 154 | def update(dt): 155 | pass 156 | pyglet.clock.schedule(update) 157 | 158 | 159 | pyglet.app.run() 160 | -------------------------------------------------------------------------------- /3-2-Textures-Multitexture.py: -------------------------------------------------------------------------------- 1 | # Tutorial: https://open.gl/textures 2 | # Original Sample Code: https://github.com/Overv/Open.GL/blob/master/content/code/c3_multitexture.txt 3 | 4 | import pyglet 5 | from pyglet.gl import * 6 | from ctypes import * 7 | import math, time 8 | 9 | 10 | window = pyglet.window.Window(800, 600, "OpenGL") 11 | window.set_location(100, 100) 12 | 13 | 14 | # Shaders (Vertex and Fragment shaders) 15 | vertexSource = """ 16 | #version 130 17 | 18 | in vec2 position; 19 | in vec3 color; 20 | in vec2 texcoord; 21 | 22 | out vec3 Color; 23 | out vec2 Texcoord; 24 | 25 | void main() { 26 | Color = color; 27 | Texcoord = texcoord; 28 | gl_Position = vec4(position, 0.0, 1.0); 29 | } 30 | """ 31 | fragmentSource = """ 32 | #version 130 33 | 34 | in vec3 Color; 35 | in vec2 Texcoord; 36 | 37 | out vec4 outColor; 38 | 39 | uniform sampler2D texKitten; 40 | uniform sampler2D texPuppy; 41 | 42 | void main() { 43 | outColor = mix(texture(texKitten, Texcoord), texture(texPuppy, Texcoord), 0.5); 44 | } 45 | """ 46 | 47 | 48 | # Vertex Input 49 | ## Vertex Array Objects 50 | vao = GLuint() 51 | glGenVertexArrays(1, pointer(vao)) 52 | glBindVertexArray(vao) 53 | 54 | ## Vertex Buffer Object 55 | vbo = GLuint() 56 | glGenBuffers(1, pointer(vbo)) # Generate 1 buffer 57 | 58 | ## Position Color Texcoords 59 | vertices = [-0.5, 0.5, 1.0, 0.0, 0.0, 0.0, 1.0, # Top-left 60 | 0.5, 0.5, 0.0, 1.0, 0.0, 1.0, 1.0, # Top-right 61 | 0.5, -0.5, 0.0, 0.0, 1.0, 1.0, 0.0, # Bottom-right 62 | -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 0.0] # Bottom-left 63 | 64 | ## Convert the verteces array to a GLfloat array, usable by glBufferData 65 | vertices_gl = (GLfloat * len(vertices))(*vertices) 66 | 67 | ## Upload data to GPU 68 | glBindBuffer(GL_ARRAY_BUFFER, vbo) 69 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_gl), vertices_gl, GL_STATIC_DRAW) 70 | 71 | 72 | # Compile shaders and combining them into a program 73 | ## Create and compile the vertex shader 74 | count = len(vertexSource) 75 | src = (c_char_p * count)(*vertexSource) 76 | vertexShader = glCreateShader(GL_VERTEX_SHADER) 77 | glShaderSource(vertexShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 78 | glCompileShader(vertexShader) 79 | 80 | ## Create and compile the fragment shader 81 | count = len(fragmentSource) 82 | src = (c_char_p * count)(*fragmentSource) 83 | fragmentShader = glCreateShader(GL_FRAGMENT_SHADER) 84 | glShaderSource(fragmentShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 85 | glCompileShader(fragmentShader) 86 | 87 | ## Link the vertex and fragment shader into a shader program 88 | shaderProgram = glCreateProgram() 89 | glAttachShader(shaderProgram, vertexShader) 90 | glAttachShader(shaderProgram, fragmentShader) 91 | glBindFragDataLocation(shaderProgram, 0, "outColor") 92 | glLinkProgram(shaderProgram) 93 | glUseProgram(shaderProgram) 94 | 95 | 96 | # Element array 97 | ebo = GLuint() 98 | glGenBuffers(1, pointer(ebo)) 99 | 100 | elements = [0, 1, 2, 101 | 2, 3, 0] 102 | elements_gl = (GLuint * len(elements))(*elements) 103 | 104 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo) 105 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements_gl), elements_gl, GL_STATIC_DRAW) 106 | 107 | 108 | # Making the link between vertex data and attributes 109 | posAttrib = glGetAttribLocation(shaderProgram, "position") 110 | glEnableVertexAttribArray(posAttrib) 111 | glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), 0) 112 | 113 | ## colAttrib returns -1 (is not used) 114 | #colAttrib = glGetAttribLocation(shaderProgram, "color") 115 | #glEnableVertexAttribArray(colAttrib) 116 | #glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), 2 * sizeof(GLfloat)) 117 | 118 | texAttrib = glGetAttribLocation(shaderProgram, "texcoord") 119 | glEnableVertexAttribArray(texAttrib) 120 | glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), 5 * sizeof(GLfloat)) 121 | 122 | 123 | # Load textures 124 | textures = [0] * 2 125 | textures_ctype = (GLuint * len(textures))(*textures) 126 | glGenTextures(2, textures_ctype) 127 | 128 | glActiveTexture(GL_TEXTURE0) 129 | glBindTexture(GL_TEXTURE_2D, textures_ctype[0]) 130 | 131 | image = pyglet.image.load("sample.png") 132 | width, height = image.width, image.height 133 | image = image.get_data('RGB', width * 3) 134 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image) 135 | 136 | glUniform1i(glGetUniformLocation(shaderProgram, "texKitten"), 0) 137 | 138 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 139 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 140 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) 141 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) 142 | 143 | glActiveTexture(GL_TEXTURE1) 144 | glBindTexture(GL_TEXTURE_2D, textures_ctype[1]) 145 | 146 | image = pyglet.image.load("sample2.png") 147 | width, height = image.width, image.height 148 | image = image.get_data('RGB', width * 3) 149 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image) 150 | 151 | glUniform1i(glGetUniformLocation(shaderProgram, "texPuppy"), 1) 152 | 153 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 154 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 155 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) 156 | 157 | 158 | @window.event 159 | def on_draw(): 160 | # Set clear color 161 | glClearColor(0.0, 0.0, 0.0, 1.0) 162 | # Clear the screen to black 163 | glClear(GL_COLOR_BUFFER_BIT) 164 | 165 | # Draw a rectangle from the 2 triangles using 6 indices 166 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0) 167 | 168 | @window.event 169 | def on_key_press(symbol, modifiers): 170 | pass 171 | 172 | @window.event 173 | def on_key_release(symbol, modifiers): 174 | pass 175 | 176 | def update(dt): 177 | pass 178 | pyglet.clock.schedule(update) 179 | 180 | 181 | pyglet.app.run() 182 | -------------------------------------------------------------------------------- /4-1-Transformations-Transformation.py: -------------------------------------------------------------------------------- 1 | # Tutorial: https://open.gl/transformations 2 | # Original Sample Code: https://github.com/Overv/Open.GL/blob/master/content/code/c4_transformation.txt 3 | 4 | import pyglet 5 | from pyglet.gl import * 6 | from ctypes import * 7 | from euclid import * 8 | import math, time 9 | 10 | 11 | window = pyglet.window.Window(800, 600, "OpenGL") 12 | window.set_location(100, 100) 13 | 14 | 15 | # Shaders (Vertex and Fragment shaders) 16 | vertexSource = """ 17 | #version 130 18 | 19 | in vec2 position; 20 | in vec3 color; 21 | in vec2 texcoord; 22 | 23 | out vec3 Color; 24 | out vec2 Texcoord; 25 | uniform mat4 trans; 26 | 27 | void main() { 28 | Color = color; 29 | Texcoord = texcoord; 30 | gl_Position = trans * vec4(position, 0.0, 1.0); 31 | } 32 | """ 33 | fragmentSource = """ 34 | #version 130 35 | 36 | in vec3 Color; 37 | in vec2 Texcoord; 38 | 39 | out vec4 outColor; 40 | 41 | uniform sampler2D texKitten; 42 | uniform sampler2D texPuppy; 43 | 44 | void main() { 45 | outColor = mix(texture(texKitten, Texcoord), texture(texPuppy, Texcoord), 0.5); 46 | } 47 | """ 48 | 49 | 50 | # Vertex Input 51 | ## Vertex Array Objects 52 | vao = GLuint() 53 | glGenVertexArrays(1, pointer(vao)) 54 | glBindVertexArray(vao) 55 | 56 | ## Vertex Buffer Object 57 | vbo = GLuint() 58 | glGenBuffers(1, pointer(vbo)) # Generate 1 buffer 59 | 60 | ## Position Color Texcoords 61 | vertices = [-0.5, 0.5, 1.0, 0.0, 0.0, 0.0, 1.0, # Top-left 62 | 0.5, 0.5, 0.0, 1.0, 0.0, 1.0, 1.0, # Top-right 63 | 0.5, -0.5, 0.0, 0.0, 1.0, 1.0, 0.0, # Bottom-right 64 | -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 0.0] # Bottom-left 65 | 66 | ## Convert the verteces array to a GLfloat array, usable by glBufferData 67 | vertices_gl = (GLfloat * len(vertices))(*vertices) 68 | 69 | ## Upload data to GPU 70 | glBindBuffer(GL_ARRAY_BUFFER, vbo) 71 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_gl), vertices_gl, GL_STATIC_DRAW) 72 | 73 | 74 | # Compile shaders and combining them into a program 75 | ## Create and compile the vertex shader 76 | count = len(vertexSource) 77 | src = (c_char_p * count)(*vertexSource) 78 | vertexShader = glCreateShader(GL_VERTEX_SHADER) 79 | glShaderSource(vertexShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 80 | glCompileShader(vertexShader) 81 | 82 | ## Create and compile the fragment shader 83 | count = len(fragmentSource) 84 | src = (c_char_p * count)(*fragmentSource) 85 | fragmentShader = glCreateShader(GL_FRAGMENT_SHADER) 86 | glShaderSource(fragmentShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 87 | glCompileShader(fragmentShader) 88 | 89 | ## Link the vertex and fragment shader into a shader program 90 | shaderProgram = glCreateProgram() 91 | glAttachShader(shaderProgram, vertexShader) 92 | glAttachShader(shaderProgram, fragmentShader) 93 | glBindFragDataLocation(shaderProgram, 0, "outColor") 94 | glLinkProgram(shaderProgram) 95 | glUseProgram(shaderProgram) 96 | 97 | 98 | # Element array 99 | ebo = GLuint() 100 | glGenBuffers(1, pointer(ebo)) 101 | 102 | elements = [0, 1, 2, 103 | 2, 3, 0] 104 | elements_gl = (GLuint * len(elements))(*elements) 105 | 106 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo) 107 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements_gl), elements_gl, GL_STATIC_DRAW) 108 | 109 | 110 | # Making the link between vertex data and attributes 111 | posAttrib = glGetAttribLocation(shaderProgram, "position") 112 | glEnableVertexAttribArray(posAttrib) 113 | glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), 0) 114 | 115 | ## colAttrib returns -1 (is not used) 116 | #colAttrib = glGetAttribLocation(shaderProgram, "color") 117 | #glEnableVertexAttribArray(colAttrib) 118 | #glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), 2 * sizeof(GLfloat)) 119 | 120 | texAttrib = glGetAttribLocation(shaderProgram, "texcoord") 121 | glEnableVertexAttribArray(texAttrib) 122 | glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), 5 * sizeof(GLfloat)) 123 | 124 | 125 | # Load textures 126 | textures = [0] * 2 127 | textures_ctype = (GLuint * len(textures))(*textures) 128 | glGenTextures(2, textures_ctype) 129 | 130 | glActiveTexture(GL_TEXTURE0); 131 | glBindTexture(GL_TEXTURE_2D, textures_ctype[0]) 132 | 133 | image = pyglet.image.load("sample.png") 134 | width, height = image.width, image.height 135 | image = image.get_data('RGB', width * 3) 136 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image) 137 | 138 | glUniform1i(glGetUniformLocation(shaderProgram, "texKitten"), 0) 139 | 140 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 141 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 142 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) 143 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) 144 | 145 | glActiveTexture(GL_TEXTURE1) 146 | glBindTexture(GL_TEXTURE_2D, textures_ctype[1]) 147 | 148 | image = pyglet.image.load("sample2.png") 149 | width, height = image.width, image.height 150 | image = image.get_data('RGB', width * 3) 151 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image) 152 | 153 | glUniform1i(glGetUniformLocation(shaderProgram, "texPuppy"), 1) 154 | 155 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 156 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 157 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) 158 | 159 | uniTrans = glGetUniformLocation(shaderProgram, "trans") 160 | 161 | 162 | @window.event 163 | def on_draw(): 164 | # Set clear color 165 | glClearColor(0.0, 0.0, 0.0, 1.0) 166 | # Clear the screen to black 167 | glClear(GL_COLOR_BUFFER_BIT) 168 | 169 | trans = Quaternion.new_rotate_axis(time.clock() * math.pi, Vector3(0, 0, 1)) 170 | trans = trans.get_matrix()[:] 171 | trans_ctype = (GLfloat * len(trans))(*trans) 172 | glUniformMatrix4fv(uniTrans, 1, GL_FALSE, trans_ctype) 173 | 174 | # Draw a rectangle from the 2 triangles using 6 indices 175 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0) 176 | 177 | @window.event 178 | def on_key_press(symbol, modifiers): 179 | pass 180 | 181 | @window.event 182 | def on_key_release(symbol, modifiers): 183 | pass 184 | 185 | def update(dt): 186 | pass 187 | pyglet.clock.schedule(update) 188 | 189 | 190 | pyglet.app.run() 191 | -------------------------------------------------------------------------------- /4-2-Transformations-3D.py: -------------------------------------------------------------------------------- 1 | # Tutorial: https://open.gl/transformations 2 | # Original Sample Code: https://github.com/Overv/Open.GL/blob/master/content/code/c4_3d.txt 3 | 4 | import pyglet 5 | from pyglet.gl import * 6 | from ctypes import * 7 | from euclid import * 8 | import math, time 9 | 10 | 11 | window = pyglet.window.Window(800, 600, "OpenGL") 12 | window.set_location(100, 100) 13 | 14 | 15 | # Shaders (Vertex and Fragment shaders) 16 | vertexSource = """ 17 | #version 130 18 | 19 | in vec2 position; 20 | in vec3 color; 21 | in vec2 texcoord; 22 | 23 | out vec3 Color; 24 | out vec2 Texcoord; 25 | 26 | uniform mat4 model; 27 | uniform mat4 view; 28 | uniform mat4 proj; 29 | 30 | void main() { 31 | Color = color; 32 | Texcoord = texcoord; 33 | gl_Position = proj * view * model * vec4(position, 0.0, 1.0); 34 | } 35 | """ 36 | fragmentSource = """ 37 | #version 130 38 | 39 | in vec3 Color; 40 | in vec2 Texcoord; 41 | 42 | out vec4 outColor; 43 | 44 | uniform sampler2D texKitten; 45 | uniform sampler2D texPuppy; 46 | 47 | void main() { 48 | outColor = mix(texture(texKitten, Texcoord), texture(texPuppy, Texcoord), 0.5); 49 | } 50 | """ 51 | 52 | 53 | # Vertex Input 54 | ## Vertex Array Objects 55 | vao = GLuint() 56 | glGenVertexArrays(1, pointer(vao)) 57 | glBindVertexArray(vao) 58 | 59 | ## Vertex Buffer Object 60 | vbo = GLuint() 61 | glGenBuffers(1, pointer(vbo)) # Generate 1 buffer 62 | 63 | ## Position Color Texcoords 64 | vertices = [-0.5, 0.5, 1.0, 0.0, 0.0, 0.0, 1.0, # Top-left 65 | 0.5, 0.5, 0.0, 1.0, 0.0, 1.0, 1.0, # Top-right 66 | 0.5, -0.5, 0.0, 0.0, 1.0, 1.0, 0.0, # Bottom-right 67 | -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 0.0] # Bottom-left 68 | 69 | ## Convert the verteces array to a GLfloat array, usable by glBufferData 70 | vertices_gl = (GLfloat * len(vertices))(*vertices) 71 | 72 | ## Upload data to GPU 73 | glBindBuffer(GL_ARRAY_BUFFER, vbo) 74 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_gl), vertices_gl, GL_STATIC_DRAW) 75 | 76 | 77 | # Compile shaders and combining them into a program 78 | ## Create and compile the vertex shader 79 | count = len(vertexSource) 80 | src = (c_char_p * count)(*vertexSource) 81 | vertexShader = glCreateShader(GL_VERTEX_SHADER) 82 | glShaderSource(vertexShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 83 | glCompileShader(vertexShader) 84 | 85 | ## Create and compile the fragment shader 86 | count = len(fragmentSource) 87 | src = (c_char_p * count)(*fragmentSource) 88 | fragmentShader = glCreateShader(GL_FRAGMENT_SHADER) 89 | glShaderSource(fragmentShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 90 | glCompileShader(fragmentShader) 91 | 92 | ## Link the vertex and fragment shader into a shader program 93 | shaderProgram = glCreateProgram() 94 | glAttachShader(shaderProgram, vertexShader) 95 | glAttachShader(shaderProgram, fragmentShader) 96 | glBindFragDataLocation(shaderProgram, 0, "outColor") 97 | glLinkProgram(shaderProgram) 98 | glUseProgram(shaderProgram) 99 | 100 | 101 | # Element array 102 | ebo = GLuint() 103 | glGenBuffers(1, pointer(ebo)) 104 | 105 | elements = [0, 1, 2, 106 | 2, 3, 0] 107 | elements_gl = (GLuint * len(elements))(*elements) 108 | 109 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo) 110 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements_gl), elements_gl, GL_STATIC_DRAW) 111 | 112 | 113 | # Making the link between vertex data and attributes 114 | posAttrib = glGetAttribLocation(shaderProgram, "position") 115 | glEnableVertexAttribArray(posAttrib) 116 | glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), 0) 117 | 118 | ## colAttrib returns -1 (is not used) 119 | #colAttrib = glGetAttribLocation(shaderProgram, "color") 120 | #glEnableVertexAttribArray(colAttrib) 121 | #glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), 2 * sizeof(GLfloat)) 122 | 123 | texAttrib = glGetAttribLocation(shaderProgram, "texcoord") 124 | glEnableVertexAttribArray(texAttrib) 125 | glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 7 * sizeof(GLfloat), 5 * sizeof(GLfloat)) 126 | 127 | 128 | # Load textures 129 | textures = [0] * 2 130 | textures_ctype = (GLuint * len(textures))(*textures) 131 | glGenTextures(2, textures_ctype) 132 | 133 | glActiveTexture(GL_TEXTURE0) 134 | glBindTexture(GL_TEXTURE_2D, textures_ctype[0]) 135 | 136 | image = pyglet.image.load("sample.png") 137 | width, height = image.width, image.height 138 | image = image.get_data('RGB', width * 3) 139 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image) 140 | 141 | glUniform1i(glGetUniformLocation(shaderProgram, "texKitten"), 0) 142 | 143 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 144 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 145 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) 146 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) 147 | 148 | glActiveTexture(GL_TEXTURE1) 149 | glBindTexture(GL_TEXTURE_2D, textures_ctype[1]) 150 | 151 | image = pyglet.image.load("sample2.png") 152 | width, height = image.width, image.height 153 | image = image.get_data('RGB', width * 3) 154 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image) 155 | 156 | glUniform1i(glGetUniformLocation(shaderProgram, "texPuppy"), 1) 157 | 158 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 159 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 160 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) 161 | 162 | # Set up projection 163 | eye = Vector3(1.2, 1.2, 1.2) 164 | at = Vector3(0.0, 0.0, 0.0) 165 | up = Vector3(0.0, 0.0, 1.0) 166 | view = Matrix4.new_look_at(eye, at, up) 167 | view = view[:] 168 | view_ctype = (GLfloat * len(view))(*view) 169 | uniView = glGetUniformLocation(shaderProgram, "view") 170 | glUniformMatrix4fv(uniView, 1, GL_FALSE, view_ctype) 171 | 172 | proj = Matrix4.new_perspective(math.radians(45.0), 800.0 / 600.0, 1.0, 10.0) 173 | proj = proj[:] 174 | proj_ctype = (GLfloat * len(proj))(*proj) 175 | uniProj = glGetUniformLocation(shaderProgram, "proj") 176 | glUniformMatrix4fv(uniProj, 1, GL_FALSE, proj_ctype) 177 | 178 | uniModel = glGetUniformLocation(shaderProgram, "model") 179 | 180 | 181 | @window.event 182 | def on_draw(): 183 | # Set clear color 184 | glClearColor(0.0, 0.0, 0.0, 1.0) 185 | # Clear the screen to black 186 | glClear(GL_COLOR_BUFFER_BIT) 187 | 188 | # Calculate transformation 189 | model = Quaternion.new_rotate_axis(time.clock() * math.pi, Vector3(0, 0, 1)) 190 | model = model.get_matrix() 191 | model = model[:] 192 | model_ctype = (GLfloat * len(model))(*model) 193 | glUniformMatrix4fv(uniModel, 1, GL_FALSE, model_ctype) 194 | 195 | # Draw a rectangle from the 2 triangles using 6 indices 196 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0) 197 | 198 | @window.event 199 | def on_key_press(symbol, modifiers): 200 | pass 201 | 202 | @window.event 203 | def on_key_release(symbol, modifiers): 204 | pass 205 | 206 | def update(dt): 207 | pass 208 | pyglet.clock.schedule(update) 209 | 210 | 211 | pyglet.app.run() 212 | -------------------------------------------------------------------------------- /5-1-Depth_and_Stencils-Cube.py: -------------------------------------------------------------------------------- 1 | # Tutorial: https://open.gl/depthstencils 2 | # Original Sample Code: https://github.com/Overv/Open.GL/blob/master/content/code/c5_cube.txt 3 | 4 | import pyglet 5 | from pyglet.gl import * 6 | from ctypes import * 7 | from euclid import * 8 | import math, time 9 | 10 | 11 | window = pyglet.window.Window(800, 600, "OpenGL") 12 | window.set_location(100, 100) 13 | 14 | 15 | # Shaders (Vertex and Fragment shaders) 16 | vertexSource = """ 17 | #version 130 18 | 19 | in vec3 position; 20 | in vec3 color; 21 | in vec2 texcoord; 22 | 23 | out vec3 Color; 24 | out vec2 Texcoord; 25 | 26 | uniform mat4 model; 27 | uniform mat4 view; 28 | uniform mat4 proj; 29 | 30 | void main() { 31 | Color = color; 32 | Texcoord = texcoord; 33 | gl_Position = proj * view * model * vec4(position, 1.0); 34 | } 35 | """ 36 | fragmentSource = """ 37 | #version 130 38 | 39 | in vec3 Color; 40 | in vec2 Texcoord; 41 | 42 | out vec4 outColor; 43 | 44 | uniform sampler2D texKitten; 45 | uniform sampler2D texPuppy; 46 | 47 | void main() { 48 | outColor = vec4(Color, 1.0) * mix(texture(texKitten, Texcoord), texture(texPuppy, Texcoord), 0.5); 49 | } 50 | """ 51 | 52 | 53 | # Vertex Input 54 | ## Vertex Array Objects 55 | vao = GLuint() 56 | glGenVertexArrays(1, pointer(vao)) 57 | glBindVertexArray(vao) 58 | 59 | ## Vertex Buffer Object 60 | vbo = GLuint() 61 | glGenBuffers(1, pointer(vbo)) # Generate 1 buffer 62 | 63 | vertices = [-0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 64 | 0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 65 | 0.5, 0.5, -0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 66 | 0.5, 0.5, -0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 67 | -0.5, 0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 68 | -0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 69 | 70 | -0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 71 | 0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 72 | 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 73 | 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 74 | -0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 75 | -0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 76 | 77 | -0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 78 | -0.5, 0.5, -0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 79 | -0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 80 | -0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 81 | -0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 82 | -0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 83 | 84 | 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 85 | 0.5, 0.5, -0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 86 | 0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 87 | 0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 88 | 0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 89 | 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 90 | 91 | -0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 92 | 0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 93 | 0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 94 | 0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 95 | -0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 96 | -0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 97 | 98 | -0.5, 0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 99 | 0.5, 0.5, -0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 100 | 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 101 | 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 102 | -0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 103 | -0.5, 0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0] 104 | 105 | ## Convert the verteces array to a GLfloat array, usable by glBufferData 106 | vertices_gl = (GLfloat * len(vertices))(*vertices) 107 | 108 | ## Upload data to GPU 109 | glBindBuffer(GL_ARRAY_BUFFER, vbo) 110 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_gl), vertices_gl, GL_STATIC_DRAW) 111 | 112 | 113 | # Compile shaders and combining them into a program 114 | ## Create and compile the vertex shader 115 | count = len(vertexSource) 116 | src = (c_char_p * count)(*vertexSource) 117 | vertexShader = glCreateShader(GL_VERTEX_SHADER) 118 | glShaderSource(vertexShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 119 | glCompileShader(vertexShader) 120 | 121 | ## Create and compile the fragment shader 122 | count = len(fragmentSource) 123 | src = (c_char_p * count)(*fragmentSource) 124 | fragmentShader = glCreateShader(GL_FRAGMENT_SHADER) 125 | glShaderSource(fragmentShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 126 | glCompileShader(fragmentShader) 127 | 128 | ## Link the vertex and fragment shader into a shader program 129 | shaderProgram = glCreateProgram() 130 | glAttachShader(shaderProgram, vertexShader) 131 | glAttachShader(shaderProgram, fragmentShader) 132 | glBindFragDataLocation(shaderProgram, 0, "outColor") 133 | glLinkProgram(shaderProgram) 134 | glUseProgram(shaderProgram) 135 | 136 | 137 | # Element array 138 | ebo = GLuint() 139 | glGenBuffers(1, pointer(ebo)) 140 | 141 | elements = [0, 1, 2, 142 | 2, 3, 0] 143 | elements_gl = (GLuint * len(elements))(*elements) 144 | 145 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo) 146 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements_gl), elements_gl, GL_STATIC_DRAW) 147 | 148 | 149 | # Making the link between vertex data and attributes 150 | posAttrib = glGetAttribLocation(shaderProgram, "position") 151 | glEnableVertexAttribArray(posAttrib) 152 | glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), 0) 153 | 154 | colAttrib = glGetAttribLocation(shaderProgram, "color") 155 | glEnableVertexAttribArray(colAttrib) 156 | glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), 3 * sizeof(GLfloat)) 157 | 158 | texAttrib = glGetAttribLocation(shaderProgram, "texcoord") 159 | glEnableVertexAttribArray(texAttrib) 160 | glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), 6 * sizeof(GLfloat)) 161 | 162 | 163 | # Load textures 164 | textures = [0] * 2 165 | textures_ctype = (GLuint * len(textures))(*textures) 166 | glGenTextures(2, textures_ctype) 167 | 168 | glActiveTexture(GL_TEXTURE0) 169 | glBindTexture(GL_TEXTURE_2D, textures_ctype[0]) 170 | 171 | image = pyglet.image.load("sample.png") 172 | width, height = image.width, image.height 173 | image = image.get_data('RGB', width * 3) 174 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image) 175 | 176 | glUniform1i(glGetUniformLocation(shaderProgram, "texKitten"), 0) 177 | 178 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 179 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 180 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) 181 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) 182 | 183 | glActiveTexture(GL_TEXTURE1) 184 | glBindTexture(GL_TEXTURE_2D, textures_ctype[1]) 185 | 186 | image = pyglet.image.load("sample2.png") 187 | width, height = image.width, image.height 188 | image = image.get_data('RGB', width * 3) 189 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image) 190 | 191 | glUniform1i(glGetUniformLocation(shaderProgram, "texPuppy"), 1) 192 | 193 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 194 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 195 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) 196 | 197 | 198 | uniModel = glGetUniformLocation(shaderProgram, "model") 199 | 200 | # Set up projection 201 | eye = Vector3(1.5, 1.5, 1.5) 202 | at = Vector3(0.0, 0.0, 0.0) 203 | up = Vector3(0.0, 0.0, 1.0) 204 | view = Matrix4.new_look_at(eye, at, up) 205 | view = view[:] 206 | view_ctype = (GLfloat * len(view))(*view) 207 | uniView = glGetUniformLocation(shaderProgram, "view") 208 | glUniformMatrix4fv(uniView, 1, GL_FALSE, view_ctype) 209 | 210 | proj = Matrix4.new_perspective(math.radians(45.0), 800.0 / 600.0, 1.0, 10.0) 211 | proj = proj[:] 212 | proj_ctype = (GLfloat * len(proj))(*proj) 213 | uniProj = glGetUniformLocation(shaderProgram, "proj") 214 | glUniformMatrix4fv(uniProj, 1, GL_FALSE, proj_ctype) 215 | 216 | 217 | @window.event 218 | def on_draw(): 219 | # Set clear color 220 | glClearColor(0.0, 0.0, 0.0, 1.0) 221 | # Clear the screen to black 222 | glClear(GL_COLOR_BUFFER_BIT) 223 | 224 | # Calculate transformation 225 | model = Quaternion.new_rotate_axis(time.clock() * math.pi, Vector3(0, 0, 1)) 226 | model = model.get_matrix() 227 | model = model[:] 228 | model_ctype = (GLfloat * len(model))(*model) 229 | glUniformMatrix4fv(uniModel, 1, GL_FALSE, model_ctype) 230 | 231 | # Draw a rectangle from the 2 triangles using 6 indices 232 | glDrawArrays(GL_TRIANGLES, 0, 36) 233 | 234 | @window.event 235 | def on_key_press(symbol, modifiers): 236 | pass 237 | 238 | @window.event 239 | def on_key_release(symbol, modifiers): 240 | pass 241 | 242 | def update(dt): 243 | pass 244 | pyglet.clock.schedule(update) 245 | 246 | 247 | pyglet.app.run() 248 | -------------------------------------------------------------------------------- /5-2-Depth_and_Stencils-Reflection.py: -------------------------------------------------------------------------------- 1 | # Tutorial: https://open.gl/depthstencils 2 | # Original Sample Code: https://github.com/Overv/Open.GL/blob/master/content/code/c5_reflection.txt 3 | 4 | import pyglet 5 | from pyglet.gl import * 6 | from ctypes import * 7 | from euclid import * 8 | import math, time 9 | 10 | allowstencil = pyglet.gl.Config(stencil_size=8) 11 | window = pyglet.window.Window(800, 600, "OpenGL", config=allowstencil) 12 | window.set_location(100, 100) 13 | 14 | 15 | # Shaders (Vertex and Fragment shaders) 16 | vertexSource = """ 17 | #version 130 18 | 19 | in vec3 position; 20 | in vec3 color; 21 | in vec2 texcoord; 22 | 23 | out vec3 Color; 24 | out vec2 Texcoord; 25 | 26 | uniform mat4 model; 27 | uniform mat4 view; 28 | uniform mat4 proj; 29 | uniform vec3 overrideColor; 30 | 31 | void main() { 32 | Color = overrideColor * color; 33 | Texcoord = texcoord; 34 | gl_Position = proj * view * model * vec4(position, 1.0); 35 | } 36 | """ 37 | fragmentSource = """ 38 | #version 130 39 | 40 | in vec3 Color; 41 | in vec2 Texcoord; 42 | 43 | out vec4 outColor; 44 | 45 | uniform sampler2D texKitten; 46 | uniform sampler2D texPuppy; 47 | 48 | void main() { 49 | outColor = vec4(Color, 1.0) * mix(texture(texKitten, Texcoord), texture(texPuppy, Texcoord), 0.5); 50 | } 51 | """ 52 | 53 | 54 | # Initialize OpenGL 55 | glEnable(GL_DEPTH_TEST) 56 | 57 | 58 | # Vertex Input 59 | ## Vertex Array Objects 60 | vao = GLuint() 61 | glGenVertexArrays(1, pointer(vao)) 62 | glBindVertexArray(vao) 63 | 64 | ## Vertex Buffer Object 65 | vbo = GLuint() 66 | glGenBuffers(1, pointer(vbo)) # Generate 1 buffer 67 | 68 | vertices = [-0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 69 | 0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 70 | 0.5, 0.5, -0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 71 | 0.5, 0.5, -0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 72 | -0.5, 0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 73 | -0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 74 | 75 | -0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 76 | 0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 77 | 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 78 | 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 79 | -0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 80 | -0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 81 | 82 | -0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 83 | -0.5, 0.5, -0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 84 | -0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 85 | -0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 86 | -0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 87 | -0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 88 | 89 | 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 90 | 0.5, 0.5, -0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 91 | 0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 92 | 0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 93 | 0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 94 | 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 95 | 96 | -0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 97 | 0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 98 | 0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 99 | 0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 100 | -0.5, -0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 101 | -0.5, -0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 102 | 103 | -0.5, 0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 104 | 0.5, 0.5, -0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 105 | 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 106 | 0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 0.0, 107 | -0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 0.0, 0.0, 108 | -0.5, 0.5, -0.5, 1.0, 1.0, 1.0, 0.0, 1.0, 109 | 110 | -1.0, -1.0, -0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 111 | 1.0, -1.0, -0.5, 0.0, 0.0, 0.0, 1.0, 0.0, 112 | 1.0, 1.0, -0.5, 0.0, 0.0, 0.0, 1.0, 1.0, 113 | 1.0, 1.0, -0.5, 0.0, 0.0, 0.0, 1.0, 1.0, 114 | -1.0, 1.0, -0.5, 0.0, 0.0, 0.0, 0.0, 1.0, 115 | -1.0, -1.0, -0.5, 0.0, 0.0, 0.0, 0.0, 0.0 116 | ] 117 | 118 | ## Convert the verteces array to a GLfloat array, usable by glBufferData 119 | vertices_gl = (GLfloat * len(vertices))(*vertices) 120 | 121 | ## Upload data to GPU 122 | glBindBuffer(GL_ARRAY_BUFFER, vbo) 123 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_gl), vertices_gl, GL_STATIC_DRAW) 124 | 125 | 126 | # Compile shaders and combining them into a program 127 | ## Create and compile the vertex shader 128 | count = len(vertexSource) 129 | src = (c_char_p * count)(*vertexSource) 130 | vertexShader = glCreateShader(GL_VERTEX_SHADER) 131 | glShaderSource(vertexShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 132 | glCompileShader(vertexShader) 133 | 134 | ## Create and compile the fragment shader 135 | count = len(fragmentSource) 136 | src = (c_char_p * count)(*fragmentSource) 137 | fragmentShader = glCreateShader(GL_FRAGMENT_SHADER) 138 | glShaderSource(fragmentShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None) 139 | glCompileShader(fragmentShader) 140 | 141 | ## Link the vertex and fragment shader into a shader program 142 | shaderProgram = glCreateProgram() 143 | glAttachShader(shaderProgram, vertexShader) 144 | glAttachShader(shaderProgram, fragmentShader) 145 | glBindFragDataLocation(shaderProgram, 0, "outColor") 146 | glLinkProgram(shaderProgram) 147 | glUseProgram(shaderProgram) 148 | 149 | 150 | # Making the link between vertex data and attributes 151 | posAttrib = glGetAttribLocation(shaderProgram, "position") 152 | glEnableVertexAttribArray(posAttrib) 153 | glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), 0) 154 | 155 | colAttrib = glGetAttribLocation(shaderProgram, "color") 156 | glEnableVertexAttribArray(colAttrib) 157 | glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), 3 * sizeof(GLfloat)) 158 | 159 | texAttrib = glGetAttribLocation(shaderProgram, "texcoord") 160 | glEnableVertexAttribArray(texAttrib) 161 | glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), 6 * sizeof(GLfloat)) 162 | 163 | 164 | # Load textures 165 | textures = [0] * 2 166 | textures_ctype = (GLuint * len(textures))(*textures) 167 | glGenTextures(2, textures_ctype) 168 | 169 | glActiveTexture(GL_TEXTURE0) 170 | glBindTexture(GL_TEXTURE_2D, textures_ctype[0]) 171 | 172 | image = pyglet.image.load("sample.png") 173 | width, height = image.width, image.height 174 | image = image.get_data('RGB', width * 3) 175 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image) 176 | 177 | glUniform1i(glGetUniformLocation(shaderProgram, "texKitten"), 0) 178 | 179 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 180 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 181 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) 182 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) 183 | 184 | glActiveTexture(GL_TEXTURE1) 185 | glBindTexture(GL_TEXTURE_2D, textures_ctype[1]) 186 | 187 | image = pyglet.image.load("sample2.png") 188 | width, height = image.width, image.height 189 | image = image.get_data('RGB', width * 3) 190 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image) 191 | 192 | glUniform1i(glGetUniformLocation(shaderProgram, "texPuppy"), 1) 193 | 194 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) 195 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) 196 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) 197 | 198 | uniModel = glGetUniformLocation(shaderProgram, "model") 199 | 200 | # Set up projection 201 | eye = Vector3(2.5, 2.5, 2.0) 202 | at = Vector3(0.0, 0.0, 0.0) 203 | up = Vector3(0.0, 0.0, 1.0) 204 | view = Matrix4.new_look_at(eye, at, up) 205 | view = view[:] 206 | view_ctype = (GLfloat * len(view))(*view) 207 | uniView = glGetUniformLocation(shaderProgram, "view") 208 | glUniformMatrix4fv(uniView, 1, GL_FALSE, view_ctype) 209 | 210 | proj = Matrix4.new_perspective(math.radians(45.0), 800.0 / 600.0, 1.0, 10.0) 211 | proj = proj[:] 212 | proj_ctype = (GLfloat * len(proj))(*proj) 213 | uniProj = glGetUniformLocation(shaderProgram, "proj") 214 | glUniformMatrix4fv(uniProj, 1, GL_FALSE, proj_ctype) 215 | 216 | uniColor = glGetUniformLocation(shaderProgram, "overrideColor") 217 | 218 | 219 | @window.event 220 | def on_draw(): 221 | # Set clear color 222 | glClearColor(1.0, 1.0, 1.0, 1.0) 223 | # Clear the screen to white and ? 224 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 225 | 226 | # Calculate transformation 227 | model = Quaternion.new_rotate_axis(time.clock() * math.pi, Vector3(0, 0, 1)) 228 | model = model.get_matrix() 229 | model_list = model[:] 230 | model_list_ctype = (GLfloat * len(model_list))(*model_list) 231 | glUniformMatrix4fv(uniModel, 1, GL_FALSE, model_list_ctype) 232 | 233 | # Draw a rectangle from the 2 triangles using 6 indices 234 | glDrawArrays(GL_TRIANGLES, 0, 36) 235 | 236 | glEnable(GL_STENCIL_TEST) 237 | 238 | # Draw floor 239 | glStencilFunc(GL_ALWAYS, 1, 0xFF) 240 | glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE) 241 | glStencilMask(0xFF) 242 | glDepthMask(GL_FALSE) 243 | glClear(GL_STENCIL_BUFFER_BIT) 244 | 245 | glDrawArrays(GL_TRIANGLES, 36, 6) 246 | 247 | # Draw cube reflection 248 | glStencilFunc(GL_EQUAL, 1, 0xFF) 249 | glStencilMask(0x00) 250 | glDepthMask(GL_TRUE) 251 | 252 | model.translate(0, 0, -1).scale(1, 1, -1) 253 | model_list = model[:] 254 | model_list_ctype = (GLfloat * len(model_list))(*model_list) 255 | glUniformMatrix4fv(uniModel, 1, GL_FALSE, model_list_ctype) 256 | 257 | glUniform3f(uniColor, 0.3, 0.3, 0.3) 258 | glDrawArrays(GL_TRIANGLES, 0, 36) 259 | glUniform3f(uniColor, 1.0, 1.0, 1.0) 260 | 261 | glDisable(GL_STENCIL_TEST) 262 | 263 | @window.event 264 | def on_key_press(symbol, modifiers): 265 | pass 266 | 267 | @window.event 268 | def on_key_release(symbol, modifiers): 269 | pass 270 | 271 | def update(dt): 272 | pass 273 | pyglet.clock.schedule(update) 274 | 275 | 276 | pyglet.app.run() 277 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | open.gl-tutorials-to-pyglet 2 | =========================== 3 | 4 | Porting of the http://open.gl/ OpenGL tutorials sample code from C++ and SFML to Python and Pyglet 5 | 6 | Tested with Pyglet 1.2. 7 | 8 | TODO: 9 | 10 | - The first 5 parts of the tutorial are done, finish the remaining 3. 11 | - Get the bug fix of euclid.py into the official repository. 12 | - The texture comes out reversed in 5-1 and 5-2, fix it by altering the tex coordinates. 13 | 14 | ## Notes on translating from C++ and SFML to Python and Pyglet 15 | 16 | More specifically the translation happens like so: 17 | - C++ to Python and ctypes 18 | - SFML to Pyglet 19 | - GLEW to pyglet.gl 20 | - SOIL to pyglet.image 21 | - GLM to pyeuclid (there is a bug in the `Matrix4.new_look_at` method in the official version, use the version in this repository until the patch goes upstream) 22 | 23 | Notes: 24 | 25 | - `from pyglet.gl import *` gives you access to OpenGL. 26 | - To create GLfloat arrays/lists you just create an ordinary list and then convert it like so: `(GLfloat * len(list1))(*list1)` , and the same for GLuint: `(GLuint * len(list2))(*list2)` 27 | - You sometimes need to use python ctypes: `from ctypes import *` , in terms of the open.gl tutorials you mainly have to use pointer and sizeof for the C++ & and sizeof equivilants. 28 | - Shader code is the same as in C++ given it is it's own language, you just put it in a string or in a seperate file, but to use it with `glShaderSource` you have to use the `cast` ctype, `pointer` and `POINTER` like so: `glShaderSource(fragmentShader, count, cast(pointer(src), POINTER(POINTER(c_char))), None)`. 29 | - Texture coordinates are reversed in pyglet. 30 | - pyeuclid uses radians instead of degrees. 31 | - To use the stencil buffer you have to enable it in the window config: `allowstencil = pyglet.gl.Config(stencil_size=8)` and `window = pyglet.window.Window(800, 600, "OpenGL", config=allowstencil)` 32 | -------------------------------------------------------------------------------- /euclid.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # euclid graphics maths module 4 | # 5 | # Copyright (c) 2006 Alex Holkner 6 | # Alex.Holkner@mail.google.com 7 | # 8 | # This library is free software; you can redistribute it and/or modify it 9 | # under the terms of the GNU Lesser General Public License as published by the 10 | # Free Software Foundation; either version 2.1 of the License, or (at your 11 | # option) any later version. 12 | # 13 | # This library is distributed in the hope that it will be useful, but WITHOUT 14 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 | # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License 16 | # for more details. 17 | # 18 | # You should have received a copy of the GNU Lesser General Public License 19 | # along with this library; if not, write to the Free Software Foundation, 20 | # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 | 22 | '''euclid graphics maths module 23 | 24 | Documentation and tests are included in the file "euclid.txt", or online 25 | at http://code.google.com/p/pyeuclid 26 | ''' 27 | 28 | __docformat__ = 'restructuredtext' 29 | __version__ = '$Id$' 30 | __revision__ = '$Revision$' 31 | 32 | import math 33 | import operator 34 | import types 35 | 36 | # Some magic here. If _use_slots is True, the classes will derive from 37 | # object and will define a __slots__ class variable. If _use_slots is 38 | # False, classes will be old-style and will not define __slots__. 39 | # 40 | # _use_slots = True: Memory efficient, probably faster in future versions 41 | # of Python, "better". 42 | # _use_slots = False: Ordinary classes, much faster than slots in current 43 | # versions of Python (2.4 and 2.5). 44 | _use_slots = True 45 | 46 | # If True, allows components of Vector2 and Vector3 to be set via swizzling; 47 | # e.g. v.xyz = (1, 2, 3). This is much, much slower than the more verbose 48 | # v.x = 1; v.y = 2; v.z = 3, and slows down ordinary element setting as 49 | # well. Recommended setting is False. 50 | _enable_swizzle_set = False 51 | 52 | # Requires class to derive from object. 53 | if _enable_swizzle_set: 54 | _use_slots = True 55 | 56 | # Implement _use_slots magic. 57 | class _EuclidMetaclass(type): 58 | def __new__(cls, name, bases, dct): 59 | if '__slots__' in dct: 60 | dct['__getstate__'] = cls._create_getstate(dct['__slots__']) 61 | dct['__setstate__'] = cls._create_setstate(dct['__slots__']) 62 | if _use_slots: 63 | return type.__new__(cls, name, bases + (object,), dct) 64 | else: 65 | if '__slots__' in dct: 66 | del dct['__slots__'] 67 | return types.ClassType.__new__(types.ClassType, name, bases, dct) 68 | 69 | @classmethod 70 | def _create_getstate(cls, slots): 71 | def __getstate__(self): 72 | d = {} 73 | for slot in slots: 74 | d[slot] = getattr(self, slot) 75 | return d 76 | return __getstate__ 77 | 78 | @classmethod 79 | def _create_setstate(cls, slots): 80 | def __setstate__(self, state): 81 | for name, value in state.items(): 82 | setattr(self, name, value) 83 | return __setstate__ 84 | 85 | __metaclass__ = _EuclidMetaclass 86 | 87 | class Vector2: 88 | __slots__ = ['x', 'y'] 89 | __hash__ = None 90 | 91 | def __init__(self, x=0, y=0): 92 | self.x = x 93 | self.y = y 94 | 95 | def __copy__(self): 96 | return self.__class__(self.x, self.y) 97 | 98 | copy = __copy__ 99 | 100 | def __repr__(self): 101 | return 'Vector2(%.2f, %.2f)' % (self.x, self.y) 102 | 103 | def __eq__(self, other): 104 | if isinstance(other, Vector2): 105 | return self.x == other.x and \ 106 | self.y == other.y 107 | else: 108 | assert hasattr(other, '__len__') and len(other) == 2 109 | return self.x == other[0] and \ 110 | self.y == other[1] 111 | 112 | def __ne__(self, other): 113 | return not self.__eq__(other) 114 | 115 | def __nonzero__(self): 116 | return self.x != 0 or self.y != 0 117 | 118 | def __len__(self): 119 | return 2 120 | 121 | def __getitem__(self, key): 122 | return (self.x, self.y)[key] 123 | 124 | def __setitem__(self, key, value): 125 | l = [self.x, self.y] 126 | l[key] = value 127 | self.x, self.y = l 128 | 129 | def __iter__(self): 130 | return iter((self.x, self.y)) 131 | 132 | def __getattr__(self, name): 133 | try: 134 | return tuple([(self.x, self.y)['xy'.index(c)] \ 135 | for c in name]) 136 | except ValueError: 137 | raise AttributeError, name 138 | 139 | if _enable_swizzle_set: 140 | # This has detrimental performance on ordinary setattr as well 141 | # if enabled 142 | def __setattr__(self, name, value): 143 | if len(name) == 1: 144 | object.__setattr__(self, name, value) 145 | else: 146 | try: 147 | l = [self.x, self.y] 148 | for c, v in map(None, name, value): 149 | l['xy'.index(c)] = v 150 | self.x, self.y = l 151 | except ValueError: 152 | raise AttributeError, name 153 | 154 | def __add__(self, other): 155 | if isinstance(other, Vector2): 156 | # Vector + Vector -> Vector 157 | # Vector + Point -> Point 158 | # Point + Point -> Vector 159 | if self.__class__ is other.__class__: 160 | _class = Vector2 161 | else: 162 | _class = Point2 163 | return _class(self.x + other.x, 164 | self.y + other.y) 165 | else: 166 | assert hasattr(other, '__len__') and len(other) == 2 167 | return Vector2(self.x + other[0], 168 | self.y + other[1]) 169 | __radd__ = __add__ 170 | 171 | def __iadd__(self, other): 172 | if isinstance(other, Vector2): 173 | self.x += other.x 174 | self.y += other.y 175 | else: 176 | self.x += other[0] 177 | self.y += other[1] 178 | return self 179 | 180 | def __sub__(self, other): 181 | if isinstance(other, Vector2): 182 | # Vector - Vector -> Vector 183 | # Vector - Point -> Point 184 | # Point - Point -> Vector 185 | if self.__class__ is other.__class__: 186 | _class = Vector2 187 | else: 188 | _class = Point2 189 | return _class(self.x - other.x, 190 | self.y - other.y) 191 | else: 192 | assert hasattr(other, '__len__') and len(other) == 2 193 | return Vector2(self.x - other[0], 194 | self.y - other[1]) 195 | 196 | 197 | def __rsub__(self, other): 198 | if isinstance(other, Vector2): 199 | return Vector2(other.x - self.x, 200 | other.y - self.y) 201 | else: 202 | assert hasattr(other, '__len__') and len(other) == 2 203 | return Vector2(other.x - self[0], 204 | other.y - self[1]) 205 | 206 | def __mul__(self, other): 207 | assert type(other) in (int, long, float) 208 | return Vector2(self.x * other, 209 | self.y * other) 210 | 211 | __rmul__ = __mul__ 212 | 213 | def __imul__(self, other): 214 | assert type(other) in (int, long, float) 215 | self.x *= other 216 | self.y *= other 217 | return self 218 | 219 | def __div__(self, other): 220 | assert type(other) in (int, long, float) 221 | return Vector2(operator.div(self.x, other), 222 | operator.div(self.y, other)) 223 | 224 | 225 | def __rdiv__(self, other): 226 | assert type(other) in (int, long, float) 227 | return Vector2(operator.div(other, self.x), 228 | operator.div(other, self.y)) 229 | 230 | def __floordiv__(self, other): 231 | assert type(other) in (int, long, float) 232 | return Vector2(operator.floordiv(self.x, other), 233 | operator.floordiv(self.y, other)) 234 | 235 | 236 | def __rfloordiv__(self, other): 237 | assert type(other) in (int, long, float) 238 | return Vector2(operator.floordiv(other, self.x), 239 | operator.floordiv(other, self.y)) 240 | 241 | def __truediv__(self, other): 242 | assert type(other) in (int, long, float) 243 | return Vector2(operator.truediv(self.x, other), 244 | operator.truediv(self.y, other)) 245 | 246 | 247 | def __rtruediv__(self, other): 248 | assert type(other) in (int, long, float) 249 | return Vector2(operator.truediv(other, self.x), 250 | operator.truediv(other, self.y)) 251 | 252 | def __neg__(self): 253 | return Vector2(-self.x, 254 | -self.y) 255 | 256 | __pos__ = __copy__ 257 | 258 | def __abs__(self): 259 | return math.sqrt(self.x ** 2 + \ 260 | self.y ** 2) 261 | 262 | magnitude = __abs__ 263 | 264 | def magnitude_squared(self): 265 | return self.x ** 2 + \ 266 | self.y ** 2 267 | 268 | def normalize(self): 269 | d = self.magnitude() 270 | if d: 271 | self.x /= d 272 | self.y /= d 273 | return self 274 | 275 | def normalized(self): 276 | d = self.magnitude() 277 | if d: 278 | return Vector2(self.x / d, 279 | self.y / d) 280 | return self.copy() 281 | 282 | def dot(self, other): 283 | assert isinstance(other, Vector2) 284 | return self.x * other.x + \ 285 | self.y * other.y 286 | 287 | def cross(self): 288 | return Vector2(self.y, -self.x) 289 | 290 | def reflect(self, normal): 291 | # assume normal is normalized 292 | assert isinstance(normal, Vector2) 293 | d = 2 * (self.x * normal.x + self.y * normal.y) 294 | return Vector2(self.x - d * normal.x, 295 | self.y - d * normal.y) 296 | 297 | def angle(self, other): 298 | """Return the angle to the vector other""" 299 | return math.acos(self.dot(other) / (self.magnitude()*other.magnitude())) 300 | 301 | def project(self, other): 302 | """Return one vector projected on the vector other""" 303 | n = other.normalized() 304 | return self.dot(n)*n 305 | 306 | class Vector3: 307 | __slots__ = ['x', 'y', 'z'] 308 | __hash__ = None 309 | 310 | def __init__(self, x=0, y=0, z=0): 311 | self.x = x 312 | self.y = y 313 | self.z = z 314 | 315 | def __copy__(self): 316 | return self.__class__(self.x, self.y, self.z) 317 | 318 | copy = __copy__ 319 | 320 | def __repr__(self): 321 | return 'Vector3(%.2f, %.2f, %.2f)' % (self.x, 322 | self.y, 323 | self.z) 324 | 325 | def __eq__(self, other): 326 | if isinstance(other, Vector3): 327 | return self.x == other.x and \ 328 | self.y == other.y and \ 329 | self.z == other.z 330 | else: 331 | assert hasattr(other, '__len__') and len(other) == 3 332 | return self.x == other[0] and \ 333 | self.y == other[1] and \ 334 | self.z == other[2] 335 | 336 | def __ne__(self, other): 337 | return not self.__eq__(other) 338 | 339 | def __nonzero__(self): 340 | return self.x != 0 or self.y != 0 or self.z != 0 341 | 342 | def __len__(self): 343 | return 3 344 | 345 | def __getitem__(self, key): 346 | return (self.x, self.y, self.z)[key] 347 | 348 | def __setitem__(self, key, value): 349 | l = [self.x, self.y, self.z] 350 | l[key] = value 351 | self.x, self.y, self.z = l 352 | 353 | def __iter__(self): 354 | return iter((self.x, self.y, self.z)) 355 | 356 | def __getattr__(self, name): 357 | try: 358 | return tuple([(self.x, self.y, self.z)['xyz'.index(c)] \ 359 | for c in name]) 360 | except ValueError: 361 | raise AttributeError, name 362 | 363 | if _enable_swizzle_set: 364 | # This has detrimental performance on ordinary setattr as well 365 | # if enabled 366 | def __setattr__(self, name, value): 367 | if len(name) == 1: 368 | object.__setattr__(self, name, value) 369 | else: 370 | try: 371 | l = [self.x, self.y, self.z] 372 | for c, v in map(None, name, value): 373 | l['xyz'.index(c)] = v 374 | self.x, self.y, self.z = l 375 | except ValueError: 376 | raise AttributeError, name 377 | 378 | 379 | def __add__(self, other): 380 | if isinstance(other, Vector3): 381 | # Vector + Vector -> Vector 382 | # Vector + Point -> Point 383 | # Point + Point -> Vector 384 | if self.__class__ is other.__class__: 385 | _class = Vector3 386 | else: 387 | _class = Point3 388 | return _class(self.x + other.x, 389 | self.y + other.y, 390 | self.z + other.z) 391 | else: 392 | assert hasattr(other, '__len__') and len(other) == 3 393 | return Vector3(self.x + other[0], 394 | self.y + other[1], 395 | self.z + other[2]) 396 | __radd__ = __add__ 397 | 398 | def __iadd__(self, other): 399 | if isinstance(other, Vector3): 400 | self.x += other.x 401 | self.y += other.y 402 | self.z += other.z 403 | else: 404 | self.x += other[0] 405 | self.y += other[1] 406 | self.z += other[2] 407 | return self 408 | 409 | def __sub__(self, other): 410 | if isinstance(other, Vector3): 411 | # Vector - Vector -> Vector 412 | # Vector - Point -> Point 413 | # Point - Point -> Vector 414 | if self.__class__ is other.__class__: 415 | _class = Vector3 416 | else: 417 | _class = Point3 418 | return Vector3(self.x - other.x, 419 | self.y - other.y, 420 | self.z - other.z) 421 | else: 422 | assert hasattr(other, '__len__') and len(other) == 3 423 | return Vector3(self.x - other[0], 424 | self.y - other[1], 425 | self.z - other[2]) 426 | 427 | 428 | def __rsub__(self, other): 429 | if isinstance(other, Vector3): 430 | return Vector3(other.x - self.x, 431 | other.y - self.y, 432 | other.z - self.z) 433 | else: 434 | assert hasattr(other, '__len__') and len(other) == 3 435 | return Vector3(other.x - self[0], 436 | other.y - self[1], 437 | other.z - self[2]) 438 | 439 | def __mul__(self, other): 440 | if isinstance(other, Vector3): 441 | # TODO component-wise mul/div in-place and on Vector2; docs. 442 | if self.__class__ is Point3 or other.__class__ is Point3: 443 | _class = Point3 444 | else: 445 | _class = Vector3 446 | return _class(self.x * other.x, 447 | self.y * other.y, 448 | self.z * other.z) 449 | else: 450 | assert type(other) in (int, long, float) 451 | return Vector3(self.x * other, 452 | self.y * other, 453 | self.z * other) 454 | 455 | __rmul__ = __mul__ 456 | 457 | def __imul__(self, other): 458 | assert type(other) in (int, long, float) 459 | self.x *= other 460 | self.y *= other 461 | self.z *= other 462 | return self 463 | 464 | def __div__(self, other): 465 | assert type(other) in (int, long, float) 466 | return Vector3(operator.div(self.x, other), 467 | operator.div(self.y, other), 468 | operator.div(self.z, other)) 469 | 470 | 471 | def __rdiv__(self, other): 472 | assert type(other) in (int, long, float) 473 | return Vector3(operator.div(other, self.x), 474 | operator.div(other, self.y), 475 | operator.div(other, self.z)) 476 | 477 | def __floordiv__(self, other): 478 | assert type(other) in (int, long, float) 479 | return Vector3(operator.floordiv(self.x, other), 480 | operator.floordiv(self.y, other), 481 | operator.floordiv(self.z, other)) 482 | 483 | 484 | def __rfloordiv__(self, other): 485 | assert type(other) in (int, long, float) 486 | return Vector3(operator.floordiv(other, self.x), 487 | operator.floordiv(other, self.y), 488 | operator.floordiv(other, self.z)) 489 | 490 | def __truediv__(self, other): 491 | assert type(other) in (int, long, float) 492 | return Vector3(operator.truediv(self.x, other), 493 | operator.truediv(self.y, other), 494 | operator.truediv(self.z, other)) 495 | 496 | 497 | def __rtruediv__(self, other): 498 | assert type(other) in (int, long, float) 499 | return Vector3(operator.truediv(other, self.x), 500 | operator.truediv(other, self.y), 501 | operator.truediv(other, self.z)) 502 | 503 | def __neg__(self): 504 | return Vector3(-self.x, 505 | -self.y, 506 | -self.z) 507 | 508 | __pos__ = __copy__ 509 | 510 | def __abs__(self): 511 | return math.sqrt(self.x ** 2 + \ 512 | self.y ** 2 + \ 513 | self.z ** 2) 514 | 515 | magnitude = __abs__ 516 | 517 | def magnitude_squared(self): 518 | return self.x ** 2 + \ 519 | self.y ** 2 + \ 520 | self.z ** 2 521 | 522 | def normalize(self): 523 | d = self.magnitude() 524 | if d: 525 | self.x /= d 526 | self.y /= d 527 | self.z /= d 528 | return self 529 | 530 | def normalized(self): 531 | d = self.magnitude() 532 | if d: 533 | return Vector3(self.x / d, 534 | self.y / d, 535 | self.z / d) 536 | return self.copy() 537 | 538 | def dot(self, other): 539 | assert isinstance(other, Vector3) 540 | return self.x * other.x + \ 541 | self.y * other.y + \ 542 | self.z * other.z 543 | 544 | def cross(self, other): 545 | assert isinstance(other, Vector3) 546 | return Vector3(self.y * other.z - self.z * other.y, 547 | -self.x * other.z + self.z * other.x, 548 | self.x * other.y - self.y * other.x) 549 | 550 | def reflect(self, normal): 551 | # assume normal is normalized 552 | assert isinstance(normal, Vector3) 553 | d = 2 * (self.x * normal.x + self.y * normal.y + self.z * normal.z) 554 | return Vector3(self.x - d * normal.x, 555 | self.y - d * normal.y, 556 | self.z - d * normal.z) 557 | 558 | def rotate_around(self, axis, theta): 559 | """Return the vector rotated around axis through angle theta. Right hand rule applies""" 560 | 561 | # Adapted from equations published by Glenn Murray. 562 | # http://inside.mines.edu/~gmurray/ArbitraryAxisRotation/ArbitraryAxisRotation.html 563 | x, y, z = self.x, self.y,self.z 564 | u, v, w = axis.x, axis.y, axis.z 565 | 566 | # Extracted common factors for simplicity and efficiency 567 | r2 = u**2 + v**2 + w**2 568 | r = math.sqrt(r2) 569 | ct = math.cos(theta) 570 | st = math.sin(theta) / r 571 | dt = (u*x + v*y + w*z) * (1 - ct) / r2 572 | return Vector3((u * dt + x * ct + (-w * y + v * z) * st), 573 | (v * dt + y * ct + ( w * x - u * z) * st), 574 | (w * dt + z * ct + (-v * x + u * y) * st)) 575 | 576 | def angle(self, other): 577 | """Return the angle to the vector other""" 578 | return math.acos(self.dot(other) / (self.magnitude()*other.magnitude())) 579 | 580 | def project(self, other): 581 | """Return one vector projected on the vector other""" 582 | n = other.normalized() 583 | return self.dot(n)*n 584 | 585 | # a b c 586 | # e f g 587 | # i j k 588 | 589 | class Matrix3: 590 | __slots__ = list('abcefgijk') 591 | 592 | def __init__(self): 593 | self.identity() 594 | 595 | def __copy__(self): 596 | M = Matrix3() 597 | M.a = self.a 598 | M.b = self.b 599 | M.c = self.c 600 | M.e = self.e 601 | M.f = self.f 602 | M.g = self.g 603 | M.i = self.i 604 | M.j = self.j 605 | M.k = self.k 606 | return M 607 | 608 | copy = __copy__ 609 | def __repr__(self): 610 | return ('Matrix3([% 8.2f % 8.2f % 8.2f\n' \ 611 | ' % 8.2f % 8.2f % 8.2f\n' \ 612 | ' % 8.2f % 8.2f % 8.2f])') \ 613 | % (self.a, self.b, self.c, 614 | self.e, self.f, self.g, 615 | self.i, self.j, self.k) 616 | 617 | def __getitem__(self, key): 618 | return [self.a, self.e, self.i, 619 | self.b, self.f, self.j, 620 | self.c, self.g, self.k][key] 621 | 622 | def __setitem__(self, key, value): 623 | L = self[:] 624 | L[key] = value 625 | (self.a, self.e, self.i, 626 | self.b, self.f, self.j, 627 | self.c, self.g, self.k) = L 628 | 629 | def __mul__(self, other): 630 | if isinstance(other, Matrix3): 631 | # Caching repeatedly accessed attributes in local variables 632 | # apparently increases performance by 20%. Attrib: Will McGugan. 633 | Aa = self.a 634 | Ab = self.b 635 | Ac = self.c 636 | Ae = self.e 637 | Af = self.f 638 | Ag = self.g 639 | Ai = self.i 640 | Aj = self.j 641 | Ak = self.k 642 | Ba = other.a 643 | Bb = other.b 644 | Bc = other.c 645 | Be = other.e 646 | Bf = other.f 647 | Bg = other.g 648 | Bi = other.i 649 | Bj = other.j 650 | Bk = other.k 651 | C = Matrix3() 652 | C.a = Aa * Ba + Ab * Be + Ac * Bi 653 | C.b = Aa * Bb + Ab * Bf + Ac * Bj 654 | C.c = Aa * Bc + Ab * Bg + Ac * Bk 655 | C.e = Ae * Ba + Af * Be + Ag * Bi 656 | C.f = Ae * Bb + Af * Bf + Ag * Bj 657 | C.g = Ae * Bc + Af * Bg + Ag * Bk 658 | C.i = Ai * Ba + Aj * Be + Ak * Bi 659 | C.j = Ai * Bb + Aj * Bf + Ak * Bj 660 | C.k = Ai * Bc + Aj * Bg + Ak * Bk 661 | return C 662 | elif isinstance(other, Point2): 663 | A = self 664 | B = other 665 | P = Point2(0, 0) 666 | P.x = A.a * B.x + A.b * B.y + A.c 667 | P.y = A.e * B.x + A.f * B.y + A.g 668 | return P 669 | elif isinstance(other, Vector2): 670 | A = self 671 | B = other 672 | V = Vector2(0, 0) 673 | V.x = A.a * B.x + A.b * B.y 674 | V.y = A.e * B.x + A.f * B.y 675 | return V 676 | else: 677 | other = other.copy() 678 | other._apply_transform(self) 679 | return other 680 | 681 | def __imul__(self, other): 682 | assert isinstance(other, Matrix3) 683 | # Cache attributes in local vars (see Matrix3.__mul__). 684 | Aa = self.a 685 | Ab = self.b 686 | Ac = self.c 687 | Ae = self.e 688 | Af = self.f 689 | Ag = self.g 690 | Ai = self.i 691 | Aj = self.j 692 | Ak = self.k 693 | Ba = other.a 694 | Bb = other.b 695 | Bc = other.c 696 | Be = other.e 697 | Bf = other.f 698 | Bg = other.g 699 | Bi = other.i 700 | Bj = other.j 701 | Bk = other.k 702 | self.a = Aa * Ba + Ab * Be + Ac * Bi 703 | self.b = Aa * Bb + Ab * Bf + Ac * Bj 704 | self.c = Aa * Bc + Ab * Bg + Ac * Bk 705 | self.e = Ae * Ba + Af * Be + Ag * Bi 706 | self.f = Ae * Bb + Af * Bf + Ag * Bj 707 | self.g = Ae * Bc + Af * Bg + Ag * Bk 708 | self.i = Ai * Ba + Aj * Be + Ak * Bi 709 | self.j = Ai * Bb + Aj * Bf + Ak * Bj 710 | self.k = Ai * Bc + Aj * Bg + Ak * Bk 711 | return self 712 | 713 | def identity(self): 714 | self.a = self.f = self.k = 1. 715 | self.b = self.c = self.e = self.g = self.i = self.j = 0 716 | return self 717 | 718 | def scale(self, x, y): 719 | self *= Matrix3.new_scale(x, y) 720 | return self 721 | 722 | def translate(self, x, y): 723 | self *= Matrix3.new_translate(x, y) 724 | return self 725 | 726 | def rotate(self, angle): 727 | self *= Matrix3.new_rotate(angle) 728 | return self 729 | 730 | # Static constructors 731 | def new_identity(cls): 732 | self = cls() 733 | return self 734 | new_identity = classmethod(new_identity) 735 | 736 | def new_scale(cls, x, y): 737 | self = cls() 738 | self.a = x 739 | self.f = y 740 | return self 741 | new_scale = classmethod(new_scale) 742 | 743 | def new_translate(cls, x, y): 744 | self = cls() 745 | self.c = x 746 | self.g = y 747 | return self 748 | new_translate = classmethod(new_translate) 749 | 750 | def new_rotate(cls, angle): 751 | self = cls() 752 | s = math.sin(angle) 753 | c = math.cos(angle) 754 | self.a = self.f = c 755 | self.b = -s 756 | self.e = s 757 | return self 758 | new_rotate = classmethod(new_rotate) 759 | 760 | def determinant(self): 761 | return (self.a*self.f*self.k 762 | + self.b*self.g*self.i 763 | + self.c*self.e*self.j 764 | - self.a*self.g*self.j 765 | - self.b*self.e*self.k 766 | - self.c*self.f*self.i) 767 | 768 | def inverse(self): 769 | tmp = Matrix3() 770 | d = self.determinant() 771 | 772 | if abs(d) < 0.001: 773 | # No inverse, return identity 774 | return tmp 775 | else: 776 | d = 1.0 / d 777 | 778 | tmp.a = d * (self.f*self.k - self.g*self.j) 779 | tmp.b = d * (self.c*self.j - self.b*self.k) 780 | tmp.c = d * (self.b*self.g - self.c*self.f) 781 | tmp.e = d * (self.g*self.i - self.e*self.k) 782 | tmp.f = d * (self.a*self.k - self.c*self.i) 783 | tmp.g = d * (self.c*self.e - self.a*self.g) 784 | tmp.i = d * (self.e*self.j - self.f*self.i) 785 | tmp.j = d * (self.b*self.i - self.a*self.j) 786 | tmp.k = d * (self.a*self.f - self.b*self.e) 787 | 788 | return tmp 789 | 790 | # a b c d 791 | # e f g h 792 | # i j k l 793 | # m n o p 794 | 795 | class Matrix4: 796 | __slots__ = list('abcdefghijklmnop') 797 | 798 | def __init__(self): 799 | self.identity() 800 | 801 | def __copy__(self): 802 | M = Matrix4() 803 | M.a = self.a 804 | M.b = self.b 805 | M.c = self.c 806 | M.d = self.d 807 | M.e = self.e 808 | M.f = self.f 809 | M.g = self.g 810 | M.h = self.h 811 | M.i = self.i 812 | M.j = self.j 813 | M.k = self.k 814 | M.l = self.l 815 | M.m = self.m 816 | M.n = self.n 817 | M.o = self.o 818 | M.p = self.p 819 | return M 820 | 821 | copy = __copy__ 822 | 823 | 824 | def __repr__(self): 825 | return ('Matrix4([% 8.2f % 8.2f % 8.2f % 8.2f\n' \ 826 | ' % 8.2f % 8.2f % 8.2f % 8.2f\n' \ 827 | ' % 8.2f % 8.2f % 8.2f % 8.2f\n' \ 828 | ' % 8.2f % 8.2f % 8.2f % 8.2f])') \ 829 | % (self.a, self.b, self.c, self.d, 830 | self.e, self.f, self.g, self.h, 831 | self.i, self.j, self.k, self.l, 832 | self.m, self.n, self.o, self.p) 833 | 834 | def __getitem__(self, key): 835 | return [self.a, self.e, self.i, self.m, 836 | self.b, self.f, self.j, self.n, 837 | self.c, self.g, self.k, self.o, 838 | self.d, self.h, self.l, self.p][key] 839 | 840 | def __setitem__(self, key, value): 841 | L = self[:] 842 | L[key] = value 843 | (self.a, self.e, self.i, self.m, 844 | self.b, self.f, self.j, self.n, 845 | self.c, self.g, self.k, self.o, 846 | self.d, self.h, self.l, self.p) = L 847 | 848 | def __mul__(self, other): 849 | if isinstance(other, Matrix4): 850 | # Cache attributes in local vars (see Matrix3.__mul__). 851 | Aa = self.a 852 | Ab = self.b 853 | Ac = self.c 854 | Ad = self.d 855 | Ae = self.e 856 | Af = self.f 857 | Ag = self.g 858 | Ah = self.h 859 | Ai = self.i 860 | Aj = self.j 861 | Ak = self.k 862 | Al = self.l 863 | Am = self.m 864 | An = self.n 865 | Ao = self.o 866 | Ap = self.p 867 | Ba = other.a 868 | Bb = other.b 869 | Bc = other.c 870 | Bd = other.d 871 | Be = other.e 872 | Bf = other.f 873 | Bg = other.g 874 | Bh = other.h 875 | Bi = other.i 876 | Bj = other.j 877 | Bk = other.k 878 | Bl = other.l 879 | Bm = other.m 880 | Bn = other.n 881 | Bo = other.o 882 | Bp = other.p 883 | C = Matrix4() 884 | C.a = Aa * Ba + Ab * Be + Ac * Bi + Ad * Bm 885 | C.b = Aa * Bb + Ab * Bf + Ac * Bj + Ad * Bn 886 | C.c = Aa * Bc + Ab * Bg + Ac * Bk + Ad * Bo 887 | C.d = Aa * Bd + Ab * Bh + Ac * Bl + Ad * Bp 888 | C.e = Ae * Ba + Af * Be + Ag * Bi + Ah * Bm 889 | C.f = Ae * Bb + Af * Bf + Ag * Bj + Ah * Bn 890 | C.g = Ae * Bc + Af * Bg + Ag * Bk + Ah * Bo 891 | C.h = Ae * Bd + Af * Bh + Ag * Bl + Ah * Bp 892 | C.i = Ai * Ba + Aj * Be + Ak * Bi + Al * Bm 893 | C.j = Ai * Bb + Aj * Bf + Ak * Bj + Al * Bn 894 | C.k = Ai * Bc + Aj * Bg + Ak * Bk + Al * Bo 895 | C.l = Ai * Bd + Aj * Bh + Ak * Bl + Al * Bp 896 | C.m = Am * Ba + An * Be + Ao * Bi + Ap * Bm 897 | C.n = Am * Bb + An * Bf + Ao * Bj + Ap * Bn 898 | C.o = Am * Bc + An * Bg + Ao * Bk + Ap * Bo 899 | C.p = Am * Bd + An * Bh + Ao * Bl + Ap * Bp 900 | return C 901 | elif isinstance(other, Point3): 902 | A = self 903 | B = other 904 | P = Point3(0, 0, 0) 905 | P.x = A.a * B.x + A.b * B.y + A.c * B.z + A.d 906 | P.y = A.e * B.x + A.f * B.y + A.g * B.z + A.h 907 | P.z = A.i * B.x + A.j * B.y + A.k * B.z + A.l 908 | return P 909 | elif isinstance(other, Vector3): 910 | A = self 911 | B = other 912 | V = Vector3(0, 0, 0) 913 | V.x = A.a * B.x + A.b * B.y + A.c * B.z 914 | V.y = A.e * B.x + A.f * B.y + A.g * B.z 915 | V.z = A.i * B.x + A.j * B.y + A.k * B.z 916 | return V 917 | else: 918 | other = other.copy() 919 | other._apply_transform(self) 920 | return other 921 | 922 | def __imul__(self, other): 923 | assert isinstance(other, Matrix4) 924 | # Cache attributes in local vars (see Matrix3.__mul__). 925 | Aa = self.a 926 | Ab = self.b 927 | Ac = self.c 928 | Ad = self.d 929 | Ae = self.e 930 | Af = self.f 931 | Ag = self.g 932 | Ah = self.h 933 | Ai = self.i 934 | Aj = self.j 935 | Ak = self.k 936 | Al = self.l 937 | Am = self.m 938 | An = self.n 939 | Ao = self.o 940 | Ap = self.p 941 | Ba = other.a 942 | Bb = other.b 943 | Bc = other.c 944 | Bd = other.d 945 | Be = other.e 946 | Bf = other.f 947 | Bg = other.g 948 | Bh = other.h 949 | Bi = other.i 950 | Bj = other.j 951 | Bk = other.k 952 | Bl = other.l 953 | Bm = other.m 954 | Bn = other.n 955 | Bo = other.o 956 | Bp = other.p 957 | self.a = Aa * Ba + Ab * Be + Ac * Bi + Ad * Bm 958 | self.b = Aa * Bb + Ab * Bf + Ac * Bj + Ad * Bn 959 | self.c = Aa * Bc + Ab * Bg + Ac * Bk + Ad * Bo 960 | self.d = Aa * Bd + Ab * Bh + Ac * Bl + Ad * Bp 961 | self.e = Ae * Ba + Af * Be + Ag * Bi + Ah * Bm 962 | self.f = Ae * Bb + Af * Bf + Ag * Bj + Ah * Bn 963 | self.g = Ae * Bc + Af * Bg + Ag * Bk + Ah * Bo 964 | self.h = Ae * Bd + Af * Bh + Ag * Bl + Ah * Bp 965 | self.i = Ai * Ba + Aj * Be + Ak * Bi + Al * Bm 966 | self.j = Ai * Bb + Aj * Bf + Ak * Bj + Al * Bn 967 | self.k = Ai * Bc + Aj * Bg + Ak * Bk + Al * Bo 968 | self.l = Ai * Bd + Aj * Bh + Ak * Bl + Al * Bp 969 | self.m = Am * Ba + An * Be + Ao * Bi + Ap * Bm 970 | self.n = Am * Bb + An * Bf + Ao * Bj + Ap * Bn 971 | self.o = Am * Bc + An * Bg + Ao * Bk + Ap * Bo 972 | self.p = Am * Bd + An * Bh + Ao * Bl + Ap * Bp 973 | return self 974 | 975 | def transform(self, other): 976 | A = self 977 | B = other 978 | P = Point3(0, 0, 0) 979 | P.x = A.a * B.x + A.b * B.y + A.c * B.z + A.d 980 | P.y = A.e * B.x + A.f * B.y + A.g * B.z + A.h 981 | P.z = A.i * B.x + A.j * B.y + A.k * B.z + A.l 982 | w = A.m * B.x + A.n * B.y + A.o * B.z + A.p 983 | if w != 0: 984 | P.x /= w 985 | P.y /= w 986 | P.z /= w 987 | return P 988 | 989 | def identity(self): 990 | self.a = self.f = self.k = self.p = 1. 991 | self.b = self.c = self.d = self.e = self.g = self.h = \ 992 | self.i = self.j = self.l = self.m = self.n = self.o = 0 993 | return self 994 | 995 | def scale(self, x, y, z): 996 | self *= Matrix4.new_scale(x, y, z) 997 | return self 998 | 999 | def translate(self, x, y, z): 1000 | self *= Matrix4.new_translate(x, y, z) 1001 | return self 1002 | 1003 | def rotatex(self, angle): 1004 | self *= Matrix4.new_rotatex(angle) 1005 | return self 1006 | 1007 | def rotatey(self, angle): 1008 | self *= Matrix4.new_rotatey(angle) 1009 | return self 1010 | 1011 | def rotatez(self, angle): 1012 | self *= Matrix4.new_rotatez(angle) 1013 | return self 1014 | 1015 | def rotate_axis(self, angle, axis): 1016 | self *= Matrix4.new_rotate_axis(angle, axis) 1017 | return self 1018 | 1019 | def rotate_euler(self, heading, attitude, bank): 1020 | self *= Matrix4.new_rotate_euler(heading, attitude, bank) 1021 | return self 1022 | 1023 | def rotate_triple_axis(self, x, y, z): 1024 | self *= Matrix4.new_rotate_triple_axis(x, y, z) 1025 | return self 1026 | 1027 | def transpose(self): 1028 | (self.a, self.e, self.i, self.m, 1029 | self.b, self.f, self.j, self.n, 1030 | self.c, self.g, self.k, self.o, 1031 | self.d, self.h, self.l, self.p) = \ 1032 | (self.a, self.b, self.c, self.d, 1033 | self.e, self.f, self.g, self.h, 1034 | self.i, self.j, self.k, self.l, 1035 | self.m, self.n, self.o, self.p) 1036 | 1037 | def transposed(self): 1038 | M = self.copy() 1039 | M.transpose() 1040 | return M 1041 | 1042 | # Static constructors 1043 | def new(cls, *values): 1044 | M = cls() 1045 | M[:] = values 1046 | return M 1047 | new = classmethod(new) 1048 | 1049 | def new_identity(cls): 1050 | self = cls() 1051 | return self 1052 | new_identity = classmethod(new_identity) 1053 | 1054 | def new_scale(cls, x, y, z): 1055 | self = cls() 1056 | self.a = x 1057 | self.f = y 1058 | self.k = z 1059 | return self 1060 | new_scale = classmethod(new_scale) 1061 | 1062 | def new_translate(cls, x, y, z): 1063 | self = cls() 1064 | self.d = x 1065 | self.h = y 1066 | self.l = z 1067 | return self 1068 | new_translate = classmethod(new_translate) 1069 | 1070 | def new_rotatex(cls, angle): 1071 | self = cls() 1072 | s = math.sin(angle) 1073 | c = math.cos(angle) 1074 | self.f = self.k = c 1075 | self.g = -s 1076 | self.j = s 1077 | return self 1078 | new_rotatex = classmethod(new_rotatex) 1079 | 1080 | def new_rotatey(cls, angle): 1081 | self = cls() 1082 | s = math.sin(angle) 1083 | c = math.cos(angle) 1084 | self.a = self.k = c 1085 | self.c = s 1086 | self.i = -s 1087 | return self 1088 | new_rotatey = classmethod(new_rotatey) 1089 | 1090 | def new_rotatez(cls, angle): 1091 | self = cls() 1092 | s = math.sin(angle) 1093 | c = math.cos(angle) 1094 | self.a = self.f = c 1095 | self.b = -s 1096 | self.e = s 1097 | return self 1098 | new_rotatez = classmethod(new_rotatez) 1099 | 1100 | def new_rotate_axis(cls, angle, axis): 1101 | assert(isinstance(axis, Vector3)) 1102 | vector = axis.normalized() 1103 | x = vector.x 1104 | y = vector.y 1105 | z = vector.z 1106 | 1107 | self = cls() 1108 | s = math.sin(angle) 1109 | c = math.cos(angle) 1110 | c1 = 1. - c 1111 | 1112 | # from the glRotate man page 1113 | self.a = x * x * c1 + c 1114 | self.b = x * y * c1 - z * s 1115 | self.c = x * z * c1 + y * s 1116 | self.e = y * x * c1 + z * s 1117 | self.f = y * y * c1 + c 1118 | self.g = y * z * c1 - x * s 1119 | self.i = x * z * c1 - y * s 1120 | self.j = y * z * c1 + x * s 1121 | self.k = z * z * c1 + c 1122 | return self 1123 | new_rotate_axis = classmethod(new_rotate_axis) 1124 | 1125 | def new_rotate_euler(cls, heading, attitude, bank): 1126 | # from http://www.euclideanspace.com/ 1127 | ch = math.cos(heading) 1128 | sh = math.sin(heading) 1129 | ca = math.cos(attitude) 1130 | sa = math.sin(attitude) 1131 | cb = math.cos(bank) 1132 | sb = math.sin(bank) 1133 | 1134 | self = cls() 1135 | self.a = ch * ca 1136 | self.b = sh * sb - ch * sa * cb 1137 | self.c = ch * sa * sb + sh * cb 1138 | self.e = sa 1139 | self.f = ca * cb 1140 | self.g = -ca * sb 1141 | self.i = -sh * ca 1142 | self.j = sh * sa * cb + ch * sb 1143 | self.k = -sh * sa * sb + ch * cb 1144 | return self 1145 | new_rotate_euler = classmethod(new_rotate_euler) 1146 | 1147 | def new_rotate_triple_axis(cls, x, y, z): 1148 | m = cls() 1149 | 1150 | m.a, m.b, m.c = x.x, y.x, z.x 1151 | m.e, m.f, m.g = x.y, y.y, z.y 1152 | m.i, m.j, m.k = x.z, y.z, z.z 1153 | 1154 | return m 1155 | new_rotate_triple_axis = classmethod(new_rotate_triple_axis) 1156 | 1157 | def new_look_at(cls, eye, at, up): 1158 | z = (eye - at).normalized() 1159 | x = up.cross(z).normalized() 1160 | y = z.cross(x) 1161 | 1162 | m = cls.new_rotate_triple_axis(x, y, z) 1163 | m.transpose() 1164 | m.d, m.h, m.l = -x.dot(eye), -y.dot(eye), -z.dot(eye) 1165 | return m 1166 | new_look_at = classmethod(new_look_at) 1167 | 1168 | def new_perspective(cls, fov_y, aspect, near, far): 1169 | # from the gluPerspective man page 1170 | f = 1 / math.tan(fov_y / 2) 1171 | self = cls() 1172 | assert near != 0.0 and near != far 1173 | self.a = f / aspect 1174 | self.f = f 1175 | self.k = (far + near) / (near - far) 1176 | self.l = 2 * far * near / (near - far) 1177 | self.o = -1 1178 | self.p = 0 1179 | return self 1180 | new_perspective = classmethod(new_perspective) 1181 | 1182 | def determinant(self): 1183 | return ((self.a * self.f - self.e * self.b) 1184 | * (self.k * self.p - self.o * self.l) 1185 | - (self.a * self.j - self.i * self.b) 1186 | * (self.g * self.p - self.o * self.h) 1187 | + (self.a * self.n - self.m * self.b) 1188 | * (self.g * self.l - self.k * self.h) 1189 | + (self.e * self.j - self.i * self.f) 1190 | * (self.c * self.p - self.o * self.d) 1191 | - (self.e * self.n - self.m * self.f) 1192 | * (self.c * self.l - self.k * self.d) 1193 | + (self.i * self.n - self.m * self.j) 1194 | * (self.c * self.h - self.g * self.d)) 1195 | 1196 | def inverse(self): 1197 | tmp = Matrix4() 1198 | d = self.determinant(); 1199 | 1200 | if abs(d) < 0.001: 1201 | # No inverse, return identity 1202 | return tmp 1203 | else: 1204 | d = 1.0 / d; 1205 | 1206 | tmp.a = d * (self.f * (self.k * self.p - self.o * self.l) + self.j * (self.o * self.h - self.g * self.p) + self.n * (self.g * self.l - self.k * self.h)); 1207 | tmp.e = d * (self.g * (self.i * self.p - self.m * self.l) + self.k * (self.m * self.h - self.e * self.p) + self.o * (self.e * self.l - self.i * self.h)); 1208 | tmp.i = d * (self.h * (self.i * self.n - self.m * self.j) + self.l * (self.m * self.f - self.e * self.n) + self.p * (self.e * self.j - self.i * self.f)); 1209 | tmp.m = d * (self.e * (self.n * self.k - self.j * self.o) + self.i * (self.f * self.o - self.n * self.g) + self.m * (self.j * self.g - self.f * self.k)); 1210 | 1211 | tmp.b = d * (self.j * (self.c * self.p - self.o * self.d) + self.n * (self.k * self.d - self.c * self.l) + self.b * (self.o * self.l - self.k * self.p)); 1212 | tmp.f = d * (self.k * (self.a * self.p - self.m * self.d) + self.o * (self.i * self.d - self.a * self.l) + self.c * (self.m * self.l - self.i * self.p)); 1213 | tmp.j = d * (self.l * (self.a * self.n - self.m * self.b) + self.p * (self.i * self.b - self.a * self.j) + self.d * (self.m * self.j - self.i * self.n)); 1214 | tmp.n = d * (self.i * (self.n * self.c - self.b * self.o) + self.m * (self.b * self.k - self.j * self.c) + self.a * (self.j * self.o - self.n * self.k)); 1215 | 1216 | tmp.c = d * (self.n * (self.c * self.h - self.g * self.d) + self.b * (self.g * self.p - self.o * self.h) + self.f * (self.o * self.d - self.c * self.p)); 1217 | tmp.g = d * (self.o * (self.a * self.h - self.e * self.d) + self.c * (self.e * self.p - self.m * self.h) + self.g * (self.m * self.d - self.a * self.p)); 1218 | tmp.k = d * (self.p * (self.a * self.f - self.e * self.b) + self.d * (self.e * self.n - self.m * self.f) + self.h * (self.m * self.b - self.a * self.n)); 1219 | tmp.o = d * (self.m * (self.f * self.c - self.b * self.g) + self.a * (self.n * self.g - self.f * self.o) + self.e * (self.b * self.o - self.n * self.c)); 1220 | 1221 | tmp.d = d * (self.b * (self.k * self.h - self.g * self.l) + self.f * (self.c * self.l - self.k * self.d) + self.j * (self.g * self.d - self.c * self.h)); 1222 | tmp.h = d * (self.c * (self.i * self.h - self.e * self.l) + self.g * (self.a * self.l - self.i * self.d) + self.k * (self.e * self.d - self.a * self.h)); 1223 | tmp.l = d * (self.d * (self.i * self.f - self.e * self.j) + self.h * (self.a * self.j - self.i * self.b) + self.l * (self.e * self.b - self.a * self.f)); 1224 | tmp.p = d * (self.a * (self.f * self.k - self.j * self.g) + self.e * (self.j * self.c - self.b * self.k) + self.i * (self.b * self.g - self.f * self.c)); 1225 | 1226 | return tmp; 1227 | 1228 | 1229 | class Quaternion: 1230 | # All methods and naming conventions based off 1231 | # http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions 1232 | 1233 | # w is the real part, (x, y, z) are the imaginary parts 1234 | __slots__ = ['w', 'x', 'y', 'z'] 1235 | 1236 | def __init__(self, w=1, x=0, y=0, z=0): 1237 | self.w = w 1238 | self.x = x 1239 | self.y = y 1240 | self.z = z 1241 | 1242 | def __copy__(self): 1243 | Q = Quaternion() 1244 | Q.w = self.w 1245 | Q.x = self.x 1246 | Q.y = self.y 1247 | Q.z = self.z 1248 | return Q 1249 | 1250 | copy = __copy__ 1251 | 1252 | def __repr__(self): 1253 | return 'Quaternion(real=%.2f, imag=<%.2f, %.2f, %.2f>)' % \ 1254 | (self.w, self.x, self.y, self.z) 1255 | 1256 | def __mul__(self, other): 1257 | if isinstance(other, Quaternion): 1258 | Ax = self.x 1259 | Ay = self.y 1260 | Az = self.z 1261 | Aw = self.w 1262 | Bx = other.x 1263 | By = other.y 1264 | Bz = other.z 1265 | Bw = other.w 1266 | Q = Quaternion() 1267 | Q.x = Ax * Bw + Ay * Bz - Az * By + Aw * Bx 1268 | Q.y = -Ax * Bz + Ay * Bw + Az * Bx + Aw * By 1269 | Q.z = Ax * By - Ay * Bx + Az * Bw + Aw * Bz 1270 | Q.w = -Ax * Bx - Ay * By - Az * Bz + Aw * Bw 1271 | return Q 1272 | elif isinstance(other, Vector3): 1273 | w = self.w 1274 | x = self.x 1275 | y = self.y 1276 | z = self.z 1277 | Vx = other.x 1278 | Vy = other.y 1279 | Vz = other.z 1280 | ww = w * w 1281 | w2 = w * 2 1282 | wx2 = w2 * x 1283 | wy2 = w2 * y 1284 | wz2 = w2 * z 1285 | xx = x * x 1286 | x2 = x * 2 1287 | xy2 = x2 * y 1288 | xz2 = x2 * z 1289 | yy = y * y 1290 | yz2 = 2 * y * z 1291 | zz = z * z 1292 | return other.__class__(\ 1293 | ww * Vx + wy2 * Vz - wz2 * Vy + \ 1294 | xx * Vx + xy2 * Vy + xz2 * Vz - \ 1295 | zz * Vx - yy * Vx, 1296 | xy2 * Vx + yy * Vy + yz2 * Vz + \ 1297 | wz2 * Vx - zz * Vy + ww * Vy - \ 1298 | wx2 * Vz - xx * Vy, 1299 | xz2 * Vx + yz2 * Vy + \ 1300 | zz * Vz - wy2 * Vx - yy * Vz + \ 1301 | wx2 * Vy - xx * Vz + ww * Vz) 1302 | else: 1303 | other = other.copy() 1304 | other._apply_transform(self) 1305 | return other 1306 | 1307 | def __imul__(self, other): 1308 | assert isinstance(other, Quaternion) 1309 | Ax = self.x 1310 | Ay = self.y 1311 | Az = self.z 1312 | Aw = self.w 1313 | Bx = other.x 1314 | By = other.y 1315 | Bz = other.z 1316 | Bw = other.w 1317 | self.x = Ax * Bw + Ay * Bz - Az * By + Aw * Bx 1318 | self.y = -Ax * Bz + Ay * Bw + Az * Bx + Aw * By 1319 | self.z = Ax * By - Ay * Bx + Az * Bw + Aw * Bz 1320 | self.w = -Ax * Bx - Ay * By - Az * Bz + Aw * Bw 1321 | return self 1322 | 1323 | def __abs__(self): 1324 | return math.sqrt(self.w ** 2 + \ 1325 | self.x ** 2 + \ 1326 | self.y ** 2 + \ 1327 | self.z ** 2) 1328 | 1329 | magnitude = __abs__ 1330 | 1331 | def magnitude_squared(self): 1332 | return self.w ** 2 + \ 1333 | self.x ** 2 + \ 1334 | self.y ** 2 + \ 1335 | self.z ** 2 1336 | 1337 | def identity(self): 1338 | self.w = 1 1339 | self.x = 0 1340 | self.y = 0 1341 | self.z = 0 1342 | return self 1343 | 1344 | def rotate_axis(self, angle, axis): 1345 | self *= Quaternion.new_rotate_axis(angle, axis) 1346 | return self 1347 | 1348 | def rotate_euler(self, heading, attitude, bank): 1349 | self *= Quaternion.new_rotate_euler(heading, attitude, bank) 1350 | return self 1351 | 1352 | def rotate_matrix(self, m): 1353 | self *= Quaternion.new_rotate_matrix(m) 1354 | return self 1355 | 1356 | def conjugated(self): 1357 | Q = Quaternion() 1358 | Q.w = self.w 1359 | Q.x = -self.x 1360 | Q.y = -self.y 1361 | Q.z = -self.z 1362 | return Q 1363 | 1364 | def normalize(self): 1365 | d = self.magnitude() 1366 | if d != 0: 1367 | self.w /= d 1368 | self.x /= d 1369 | self.y /= d 1370 | self.z /= d 1371 | return self 1372 | 1373 | def normalized(self): 1374 | d = self.magnitude() 1375 | if d != 0: 1376 | Q = Quaternion() 1377 | Q.w = self.w / d 1378 | Q.x = self.x / d 1379 | Q.y = self.y / d 1380 | Q.z = self.z / d 1381 | return Q 1382 | else: 1383 | return self.copy() 1384 | 1385 | def get_angle_axis(self): 1386 | if self.w > 1: 1387 | self = self.normalized() 1388 | angle = 2 * math.acos(self.w) 1389 | s = math.sqrt(1 - self.w ** 2) 1390 | if s < 0.001: 1391 | return angle, Vector3(1, 0, 0) 1392 | else: 1393 | return angle, Vector3(self.x / s, self.y / s, self.z / s) 1394 | 1395 | def get_euler(self): 1396 | t = self.x * self.y + self.z * self.w 1397 | if t > 0.4999: 1398 | heading = 2 * math.atan2(self.x, self.w) 1399 | attitude = math.pi / 2 1400 | bank = 0 1401 | elif t < -0.4999: 1402 | heading = -2 * math.atan2(self.x, self.w) 1403 | attitude = -math.pi / 2 1404 | bank = 0 1405 | else: 1406 | sqx = self.x ** 2 1407 | sqy = self.y ** 2 1408 | sqz = self.z ** 2 1409 | heading = math.atan2(2 * self.y * self.w - 2 * self.x * self.z, 1410 | 1 - 2 * sqy - 2 * sqz) 1411 | attitude = math.asin(2 * t) 1412 | bank = math.atan2(2 * self.x * self.w - 2 * self.y * self.z, 1413 | 1 - 2 * sqx - 2 * sqz) 1414 | return heading, attitude, bank 1415 | 1416 | def get_matrix(self): 1417 | xx = self.x ** 2 1418 | xy = self.x * self.y 1419 | xz = self.x * self.z 1420 | xw = self.x * self.w 1421 | yy = self.y ** 2 1422 | yz = self.y * self.z 1423 | yw = self.y * self.w 1424 | zz = self.z ** 2 1425 | zw = self.z * self.w 1426 | M = Matrix4() 1427 | M.a = 1 - 2 * (yy + zz) 1428 | M.b = 2 * (xy - zw) 1429 | M.c = 2 * (xz + yw) 1430 | M.e = 2 * (xy + zw) 1431 | M.f = 1 - 2 * (xx + zz) 1432 | M.g = 2 * (yz - xw) 1433 | M.i = 2 * (xz - yw) 1434 | M.j = 2 * (yz + xw) 1435 | M.k = 1 - 2 * (xx + yy) 1436 | return M 1437 | 1438 | # Static constructors 1439 | def new_identity(cls): 1440 | return cls() 1441 | new_identity = classmethod(new_identity) 1442 | 1443 | def new_rotate_axis(cls, angle, axis): 1444 | assert(isinstance(axis, Vector3)) 1445 | axis = axis.normalized() 1446 | s = math.sin(angle / 2) 1447 | Q = cls() 1448 | Q.w = math.cos(angle / 2) 1449 | Q.x = axis.x * s 1450 | Q.y = axis.y * s 1451 | Q.z = axis.z * s 1452 | return Q 1453 | new_rotate_axis = classmethod(new_rotate_axis) 1454 | 1455 | def new_rotate_euler(cls, heading, attitude, bank): 1456 | Q = cls() 1457 | c1 = math.cos(heading / 2) 1458 | s1 = math.sin(heading / 2) 1459 | c2 = math.cos(attitude / 2) 1460 | s2 = math.sin(attitude / 2) 1461 | c3 = math.cos(bank / 2) 1462 | s3 = math.sin(bank / 2) 1463 | 1464 | Q.w = c1 * c2 * c3 - s1 * s2 * s3 1465 | Q.x = s1 * s2 * c3 + c1 * c2 * s3 1466 | Q.y = s1 * c2 * c3 + c1 * s2 * s3 1467 | Q.z = c1 * s2 * c3 - s1 * c2 * s3 1468 | return Q 1469 | new_rotate_euler = classmethod(new_rotate_euler) 1470 | 1471 | def new_rotate_matrix(cls, m): 1472 | if m[0*4 + 0] + m[1*4 + 1] + m[2*4 + 2] > 0.00000001: 1473 | t = m[0*4 + 0] + m[1*4 + 1] + m[2*4 + 2] + 1.0 1474 | s = 0.5/math.sqrt(t) 1475 | 1476 | return cls( 1477 | s*t, 1478 | (m[1*4 + 2] - m[2*4 + 1])*s, 1479 | (m[2*4 + 0] - m[0*4 + 2])*s, 1480 | (m[0*4 + 1] - m[1*4 + 0])*s 1481 | ) 1482 | 1483 | elif m[0*4 + 0] > m[1*4 + 1] and m[0*4 + 0] > m[2*4 + 2]: 1484 | t = m[0*4 + 0] - m[1*4 + 1] - m[2*4 + 2] + 1.0 1485 | s = 0.5/math.sqrt(t) 1486 | 1487 | return cls( 1488 | (m[1*4 + 2] - m[2*4 + 1])*s, 1489 | s*t, 1490 | (m[0*4 + 1] + m[1*4 + 0])*s, 1491 | (m[2*4 + 0] + m[0*4 + 2])*s 1492 | ) 1493 | 1494 | elif m[1*4 + 1] > m[2*4 + 2]: 1495 | t = -m[0*4 + 0] + m[1*4 + 1] - m[2*4 + 2] + 1.0 1496 | s = 0.5/math.sqrt(t) 1497 | 1498 | return cls( 1499 | (m[2*4 + 0] - m[0*4 + 2])*s, 1500 | (m[0*4 + 1] + m[1*4 + 0])*s, 1501 | s*t, 1502 | (m[1*4 + 2] + m[2*4 + 1])*s 1503 | ) 1504 | 1505 | else: 1506 | t = -m[0*4 + 0] - m[1*4 + 1] + m[2*4 + 2] + 1.0 1507 | s = 0.5/math.sqrt(t) 1508 | 1509 | return cls( 1510 | (m[0*4 + 1] - m[1*4 + 0])*s, 1511 | (m[2*4 + 0] + m[0*4 + 2])*s, 1512 | (m[1*4 + 2] + m[2*4 + 1])*s, 1513 | s*t 1514 | ) 1515 | new_rotate_matrix = classmethod(new_rotate_matrix) 1516 | 1517 | def new_interpolate(cls, q1, q2, t): 1518 | assert isinstance(q1, Quaternion) and isinstance(q2, Quaternion) 1519 | Q = cls() 1520 | 1521 | costheta = q1.w * q2.w + q1.x * q2.x + q1.y * q2.y + q1.z * q2.z 1522 | if costheta < 0.: 1523 | costheta = -costheta 1524 | q1 = q1.conjugated() 1525 | elif costheta > 1: 1526 | costheta = 1 1527 | 1528 | theta = math.acos(costheta) 1529 | if abs(theta) < 0.01: 1530 | Q.w = q2.w 1531 | Q.x = q2.x 1532 | Q.y = q2.y 1533 | Q.z = q2.z 1534 | return Q 1535 | 1536 | sintheta = math.sqrt(1.0 - costheta * costheta) 1537 | if abs(sintheta) < 0.01: 1538 | Q.w = (q1.w + q2.w) * 0.5 1539 | Q.x = (q1.x + q2.x) * 0.5 1540 | Q.y = (q1.y + q2.y) * 0.5 1541 | Q.z = (q1.z + q2.z) * 0.5 1542 | return Q 1543 | 1544 | ratio1 = math.sin((1 - t) * theta) / sintheta 1545 | ratio2 = math.sin(t * theta) / sintheta 1546 | 1547 | Q.w = q1.w * ratio1 + q2.w * ratio2 1548 | Q.x = q1.x * ratio1 + q2.x * ratio2 1549 | Q.y = q1.y * ratio1 + q2.y * ratio2 1550 | Q.z = q1.z * ratio1 + q2.z * ratio2 1551 | return Q 1552 | new_interpolate = classmethod(new_interpolate) 1553 | 1554 | # Geometry 1555 | # Much maths thanks to Paul Bourke, http://astronomy.swin.edu.au/~pbourke 1556 | # --------------------------------------------------------------------------- 1557 | 1558 | class Geometry: 1559 | def _connect_unimplemented(self, other): 1560 | raise AttributeError, 'Cannot connect %s to %s' % \ 1561 | (self.__class__, other.__class__) 1562 | 1563 | def _intersect_unimplemented(self, other): 1564 | raise AttributeError, 'Cannot intersect %s and %s' % \ 1565 | (self.__class__, other.__class__) 1566 | 1567 | _intersect_point2 = _intersect_unimplemented 1568 | _intersect_line2 = _intersect_unimplemented 1569 | _intersect_circle = _intersect_unimplemented 1570 | _connect_point2 = _connect_unimplemented 1571 | _connect_line2 = _connect_unimplemented 1572 | _connect_circle = _connect_unimplemented 1573 | 1574 | _intersect_point3 = _intersect_unimplemented 1575 | _intersect_line3 = _intersect_unimplemented 1576 | _intersect_sphere = _intersect_unimplemented 1577 | _intersect_plane = _intersect_unimplemented 1578 | _connect_point3 = _connect_unimplemented 1579 | _connect_line3 = _connect_unimplemented 1580 | _connect_sphere = _connect_unimplemented 1581 | _connect_plane = _connect_unimplemented 1582 | 1583 | def intersect(self, other): 1584 | raise NotImplementedError 1585 | 1586 | def connect(self, other): 1587 | raise NotImplementedError 1588 | 1589 | def distance(self, other): 1590 | c = self.connect(other) 1591 | if c: 1592 | return c.length 1593 | return 0.0 1594 | 1595 | def _intersect_point2_circle(P, C): 1596 | return abs(P - C.c) <= C.r 1597 | 1598 | def _intersect_line2_line2(A, B): 1599 | d = B.v.y * A.v.x - B.v.x * A.v.y 1600 | if d == 0: 1601 | return None 1602 | 1603 | dy = A.p.y - B.p.y 1604 | dx = A.p.x - B.p.x 1605 | ua = (B.v.x * dy - B.v.y * dx) / d 1606 | if not A._u_in(ua): 1607 | return None 1608 | ub = (A.v.x * dy - A.v.y * dx) / d 1609 | if not B._u_in(ub): 1610 | return None 1611 | 1612 | return Point2(A.p.x + ua * A.v.x, 1613 | A.p.y + ua * A.v.y) 1614 | 1615 | def _intersect_line2_circle(L, C): 1616 | a = L.v.magnitude_squared() 1617 | b = 2 * (L.v.x * (L.p.x - C.c.x) + \ 1618 | L.v.y * (L.p.y - C.c.y)) 1619 | c = C.c.magnitude_squared() + \ 1620 | L.p.magnitude_squared() - \ 1621 | 2 * C.c.dot(L.p) - \ 1622 | C.r ** 2 1623 | det = b ** 2 - 4 * a * c 1624 | if det < 0: 1625 | return None 1626 | sq = math.sqrt(det) 1627 | u1 = (-b + sq) / (2 * a) 1628 | u2 = (-b - sq) / (2 * a) 1629 | if not L._u_in(u1): 1630 | u1 = max(min(u1, 1.0), 0.0) 1631 | if not L._u_in(u2): 1632 | u2 = max(min(u2, 1.0), 0.0) 1633 | 1634 | # Tangent 1635 | if u1 == u2: 1636 | return Point2(L.p.x + u1 * L.v.x, 1637 | L.p.y + u1 * L.v.y) 1638 | 1639 | return LineSegment2(Point2(L.p.x + u1 * L.v.x, 1640 | L.p.y + u1 * L.v.y), 1641 | Point2(L.p.x + u2 * L.v.x, 1642 | L.p.y + u2 * L.v.y)) 1643 | 1644 | def _connect_point2_line2(P, L): 1645 | d = L.v.magnitude_squared() 1646 | assert d != 0 1647 | u = ((P.x - L.p.x) * L.v.x + \ 1648 | (P.y - L.p.y) * L.v.y) / d 1649 | if not L._u_in(u): 1650 | u = max(min(u, 1.0), 0.0) 1651 | return LineSegment2(P, 1652 | Point2(L.p.x + u * L.v.x, 1653 | L.p.y + u * L.v.y)) 1654 | 1655 | def _connect_point2_circle(P, C): 1656 | v = P - C.c 1657 | v.normalize() 1658 | v *= C.r 1659 | return LineSegment2(P, Point2(C.c.x + v.x, C.c.y + v.y)) 1660 | 1661 | def _connect_line2_line2(A, B): 1662 | d = B.v.y * A.v.x - B.v.x * A.v.y 1663 | if d == 0: 1664 | # Parallel, connect an endpoint with a line 1665 | if isinstance(B, Ray2) or isinstance(B, LineSegment2): 1666 | p1, p2 = _connect_point2_line2(B.p, A) 1667 | return p2, p1 1668 | # No endpoint (or endpoint is on A), possibly choose arbitrary point 1669 | # on line. 1670 | return _connect_point2_line2(A.p, B) 1671 | 1672 | dy = A.p.y - B.p.y 1673 | dx = A.p.x - B.p.x 1674 | ua = (B.v.x * dy - B.v.y * dx) / d 1675 | if not A._u_in(ua): 1676 | ua = max(min(ua, 1.0), 0.0) 1677 | ub = (A.v.x * dy - A.v.y * dx) / d 1678 | if not B._u_in(ub): 1679 | ub = max(min(ub, 1.0), 0.0) 1680 | 1681 | return LineSegment2(Point2(A.p.x + ua * A.v.x, A.p.y + ua * A.v.y), 1682 | Point2(B.p.x + ub * B.v.x, B.p.y + ub * B.v.y)) 1683 | 1684 | def _connect_circle_line2(C, L): 1685 | d = L.v.magnitude_squared() 1686 | assert d != 0 1687 | u = ((C.c.x - L.p.x) * L.v.x + (C.c.y - L.p.y) * L.v.y) / d 1688 | if not L._u_in(u): 1689 | u = max(min(u, 1.0), 0.0) 1690 | point = Point2(L.p.x + u * L.v.x, L.p.y + u * L.v.y) 1691 | v = (point - C.c) 1692 | v.normalize() 1693 | v *= C.r 1694 | return LineSegment2(Point2(C.c.x + v.x, C.c.y + v.y), point) 1695 | 1696 | def _connect_circle_circle(A, B): 1697 | v = B.c - A.c 1698 | d = v.magnitude() 1699 | if A.r >= B.r and d < A.r: 1700 | #centre B inside A 1701 | s1,s2 = +1, +1 1702 | elif B.r > A.r and d < B.r: 1703 | #centre A inside B 1704 | s1,s2 = -1, -1 1705 | elif d >= A.r and d >= B.r: 1706 | s1,s2 = +1, -1 1707 | v.normalize() 1708 | return LineSegment2(Point2(A.c.x + s1 * v.x * A.r, A.c.y + s1 * v.y * A.r), 1709 | Point2(B.c.x + s2 * v.x * B.r, B.c.y + s2 * v.y * B.r)) 1710 | 1711 | 1712 | class Point2(Vector2, Geometry): 1713 | def __repr__(self): 1714 | return 'Point2(%.2f, %.2f)' % (self.x, self.y) 1715 | 1716 | def intersect(self, other): 1717 | return other._intersect_point2(self) 1718 | 1719 | def _intersect_circle(self, other): 1720 | return _intersect_point2_circle(self, other) 1721 | 1722 | def connect(self, other): 1723 | return other._connect_point2(self) 1724 | 1725 | def _connect_point2(self, other): 1726 | return LineSegment2(other, self) 1727 | 1728 | def _connect_line2(self, other): 1729 | c = _connect_point2_line2(self, other) 1730 | if c: 1731 | return c._swap() 1732 | 1733 | def _connect_circle(self, other): 1734 | c = _connect_point2_circle(self, other) 1735 | if c: 1736 | return c._swap() 1737 | 1738 | class Line2(Geometry): 1739 | __slots__ = ['p', 'v'] 1740 | 1741 | def __init__(self, *args): 1742 | if len(args) == 3: 1743 | assert isinstance(args[0], Point2) and \ 1744 | isinstance(args[1], Vector2) and \ 1745 | type(args[2]) == float 1746 | self.p = args[0].copy() 1747 | self.v = args[1] * args[2] / abs(args[1]) 1748 | elif len(args) == 2: 1749 | if isinstance(args[0], Point2) and isinstance(args[1], Point2): 1750 | self.p = args[0].copy() 1751 | self.v = args[1] - args[0] 1752 | elif isinstance(args[0], Point2) and isinstance(args[1], Vector2): 1753 | self.p = args[0].copy() 1754 | self.v = args[1].copy() 1755 | else: 1756 | raise AttributeError, '%r' % (args,) 1757 | elif len(args) == 1: 1758 | if isinstance(args[0], Line2): 1759 | self.p = args[0].p.copy() 1760 | self.v = args[0].v.copy() 1761 | else: 1762 | raise AttributeError, '%r' % (args,) 1763 | else: 1764 | raise AttributeError, '%r' % (args,) 1765 | 1766 | if not self.v: 1767 | raise AttributeError, 'Line has zero-length vector' 1768 | 1769 | def __copy__(self): 1770 | return self.__class__(self.p, self.v) 1771 | 1772 | copy = __copy__ 1773 | 1774 | def __repr__(self): 1775 | return 'Line2(<%.2f, %.2f> + u<%.2f, %.2f>)' % \ 1776 | (self.p.x, self.p.y, self.v.x, self.v.y) 1777 | 1778 | p1 = property(lambda self: self.p) 1779 | p2 = property(lambda self: Point2(self.p.x + self.v.x, 1780 | self.p.y + self.v.y)) 1781 | 1782 | def _apply_transform(self, t): 1783 | self.p = t * self.p 1784 | self.v = t * self.v 1785 | 1786 | def _u_in(self, u): 1787 | return True 1788 | 1789 | def intersect(self, other): 1790 | return other._intersect_line2(self) 1791 | 1792 | def _intersect_line2(self, other): 1793 | return _intersect_line2_line2(self, other) 1794 | 1795 | def _intersect_circle(self, other): 1796 | return _intersect_line2_circle(self, other) 1797 | 1798 | def connect(self, other): 1799 | return other._connect_line2(self) 1800 | 1801 | def _connect_point2(self, other): 1802 | return _connect_point2_line2(other, self) 1803 | 1804 | def _connect_line2(self, other): 1805 | return _connect_line2_line2(other, self) 1806 | 1807 | def _connect_circle(self, other): 1808 | return _connect_circle_line2(other, self) 1809 | 1810 | class Ray2(Line2): 1811 | def __repr__(self): 1812 | return 'Ray2(<%.2f, %.2f> + u<%.2f, %.2f>)' % \ 1813 | (self.p.x, self.p.y, self.v.x, self.v.y) 1814 | 1815 | def _u_in(self, u): 1816 | return u >= 0.0 1817 | 1818 | class LineSegment2(Line2): 1819 | def __repr__(self): 1820 | return 'LineSegment2(<%.2f, %.2f> to <%.2f, %.2f>)' % \ 1821 | (self.p.x, self.p.y, self.p.x + self.v.x, self.p.y + self.v.y) 1822 | 1823 | def _u_in(self, u): 1824 | return u >= 0.0 and u <= 1.0 1825 | 1826 | def __abs__(self): 1827 | return abs(self.v) 1828 | 1829 | def magnitude_squared(self): 1830 | return self.v.magnitude_squared() 1831 | 1832 | def _swap(self): 1833 | # used by connect methods to switch order of points 1834 | self.p = self.p2 1835 | self.v *= -1 1836 | return self 1837 | 1838 | length = property(lambda self: abs(self.v)) 1839 | 1840 | class Circle(Geometry): 1841 | __slots__ = ['c', 'r'] 1842 | 1843 | def __init__(self, center, radius): 1844 | assert isinstance(center, Vector2) and type(radius) == float 1845 | self.c = center.copy() 1846 | self.r = radius 1847 | 1848 | def __copy__(self): 1849 | return self.__class__(self.c, self.r) 1850 | 1851 | copy = __copy__ 1852 | 1853 | def __repr__(self): 1854 | return 'Circle(<%.2f, %.2f>, radius=%.2f)' % \ 1855 | (self.c.x, self.c.y, self.r) 1856 | 1857 | def _apply_transform(self, t): 1858 | self.c = t * self.c 1859 | 1860 | def intersect(self, other): 1861 | return other._intersect_circle(self) 1862 | 1863 | def _intersect_point2(self, other): 1864 | return _intersect_point2_circle(other, self) 1865 | 1866 | def _intersect_line2(self, other): 1867 | return _intersect_line2_circle(other, self) 1868 | 1869 | def connect(self, other): 1870 | return other._connect_circle(self) 1871 | 1872 | def _connect_point2(self, other): 1873 | return _connect_point2_circle(other, self) 1874 | 1875 | def _connect_line2(self, other): 1876 | c = _connect_circle_line2(self, other) 1877 | if c: 1878 | return c._swap() 1879 | 1880 | def _connect_circle(self, other): 1881 | return _connect_circle_circle(other, self) 1882 | 1883 | # 3D Geometry 1884 | # ------------------------------------------------------------------------- 1885 | 1886 | def _connect_point3_line3(P, L): 1887 | d = L.v.magnitude_squared() 1888 | assert d != 0 1889 | u = ((P.x - L.p.x) * L.v.x + \ 1890 | (P.y - L.p.y) * L.v.y + \ 1891 | (P.z - L.p.z) * L.v.z) / d 1892 | if not L._u_in(u): 1893 | u = max(min(u, 1.0), 0.0) 1894 | return LineSegment3(P, Point3(L.p.x + u * L.v.x, 1895 | L.p.y + u * L.v.y, 1896 | L.p.z + u * L.v.z)) 1897 | 1898 | def _connect_point3_sphere(P, S): 1899 | v = P - S.c 1900 | v.normalize() 1901 | v *= S.r 1902 | return LineSegment3(P, Point3(S.c.x + v.x, S.c.y + v.y, S.c.z + v.z)) 1903 | 1904 | def _connect_point3_plane(p, plane): 1905 | n = plane.n.normalized() 1906 | d = p.dot(plane.n) - plane.k 1907 | return LineSegment3(p, Point3(p.x - n.x * d, p.y - n.y * d, p.z - n.z * d)) 1908 | 1909 | def _connect_line3_line3(A, B): 1910 | assert A.v and B.v 1911 | p13 = A.p - B.p 1912 | d1343 = p13.dot(B.v) 1913 | d4321 = B.v.dot(A.v) 1914 | d1321 = p13.dot(A.v) 1915 | d4343 = B.v.magnitude_squared() 1916 | denom = A.v.magnitude_squared() * d4343 - d4321 ** 2 1917 | if denom == 0: 1918 | # Parallel, connect an endpoint with a line 1919 | if isinstance(B, Ray3) or isinstance(B, LineSegment3): 1920 | return _connect_point3_line3(B.p, A)._swap() 1921 | # No endpoint (or endpoint is on A), possibly choose arbitrary 1922 | # point on line. 1923 | return _connect_point3_line3(A.p, B) 1924 | 1925 | ua = (d1343 * d4321 - d1321 * d4343) / denom 1926 | if not A._u_in(ua): 1927 | ua = max(min(ua, 1.0), 0.0) 1928 | ub = (d1343 + d4321 * ua) / d4343 1929 | if not B._u_in(ub): 1930 | ub = max(min(ub, 1.0), 0.0) 1931 | return LineSegment3(Point3(A.p.x + ua * A.v.x, 1932 | A.p.y + ua * A.v.y, 1933 | A.p.z + ua * A.v.z), 1934 | Point3(B.p.x + ub * B.v.x, 1935 | B.p.y + ub * B.v.y, 1936 | B.p.z + ub * B.v.z)) 1937 | 1938 | def _connect_line3_plane(L, P): 1939 | d = P.n.dot(L.v) 1940 | if not d: 1941 | # Parallel, choose an endpoint 1942 | return _connect_point3_plane(L.p, P) 1943 | u = (P.k - P.n.dot(L.p)) / d 1944 | if not L._u_in(u): 1945 | # intersects out of range, choose nearest endpoint 1946 | u = max(min(u, 1.0), 0.0) 1947 | return _connect_point3_plane(Point3(L.p.x + u * L.v.x, 1948 | L.p.y + u * L.v.y, 1949 | L.p.z + u * L.v.z), P) 1950 | # Intersection 1951 | return None 1952 | 1953 | def _connect_sphere_line3(S, L): 1954 | d = L.v.magnitude_squared() 1955 | assert d != 0 1956 | u = ((S.c.x - L.p.x) * L.v.x + \ 1957 | (S.c.y - L.p.y) * L.v.y + \ 1958 | (S.c.z - L.p.z) * L.v.z) / d 1959 | if not L._u_in(u): 1960 | u = max(min(u, 1.0), 0.0) 1961 | point = Point3(L.p.x + u * L.v.x, L.p.y + u * L.v.y, L.p.z + u * L.v.z) 1962 | v = (point - S.c) 1963 | v.normalize() 1964 | v *= S.r 1965 | return LineSegment3(Point3(S.c.x + v.x, S.c.y + v.y, S.c.z + v.z), 1966 | point) 1967 | 1968 | def _connect_sphere_sphere(A, B): 1969 | v = B.c - A.c 1970 | d = v.magnitude() 1971 | if A.r >= B.r and d < A.r: 1972 | #centre B inside A 1973 | s1,s2 = +1, +1 1974 | elif B.r > A.r and d < B.r: 1975 | #centre A inside B 1976 | s1,s2 = -1, -1 1977 | elif d >= A.r and d >= B.r: 1978 | s1,s2 = +1, -1 1979 | 1980 | v.normalize() 1981 | return LineSegment3(Point3(A.c.x + s1* v.x * A.r, 1982 | A.c.y + s1* v.y * A.r, 1983 | A.c.z + s1* v.z * A.r), 1984 | Point3(B.c.x + s2* v.x * B.r, 1985 | B.c.y + s2* v.y * B.r, 1986 | B.c.z + s2* v.z * B.r)) 1987 | 1988 | def _connect_sphere_plane(S, P): 1989 | c = _connect_point3_plane(S.c, P) 1990 | if not c: 1991 | return None 1992 | p2 = c.p2 1993 | v = p2 - S.c 1994 | v.normalize() 1995 | v *= S.r 1996 | return LineSegment3(Point3(S.c.x + v.x, S.c.y + v.y, S.c.z + v.z), 1997 | p2) 1998 | 1999 | def _connect_plane_plane(A, B): 2000 | if A.n.cross(B.n): 2001 | # Planes intersect 2002 | return None 2003 | else: 2004 | # Planes are parallel, connect to arbitrary point 2005 | return _connect_point3_plane(A._get_point(), B) 2006 | 2007 | def _intersect_point3_sphere(P, S): 2008 | return abs(P - S.c) <= S.r 2009 | 2010 | def _intersect_line3_sphere(L, S): 2011 | a = L.v.magnitude_squared() 2012 | b = 2 * (L.v.x * (L.p.x - S.c.x) + \ 2013 | L.v.y * (L.p.y - S.c.y) + \ 2014 | L.v.z * (L.p.z - S.c.z)) 2015 | c = S.c.magnitude_squared() + \ 2016 | L.p.magnitude_squared() - \ 2017 | 2 * S.c.dot(L.p) - \ 2018 | S.r ** 2 2019 | det = b ** 2 - 4 * a * c 2020 | if det < 0: 2021 | return None 2022 | sq = math.sqrt(det) 2023 | u1 = (-b + sq) / (2 * a) 2024 | u2 = (-b - sq) / (2 * a) 2025 | if not L._u_in(u1): 2026 | u1 = max(min(u1, 1.0), 0.0) 2027 | if not L._u_in(u2): 2028 | u2 = max(min(u2, 1.0), 0.0) 2029 | return LineSegment3(Point3(L.p.x + u1 * L.v.x, 2030 | L.p.y + u1 * L.v.y, 2031 | L.p.z + u1 * L.v.z), 2032 | Point3(L.p.x + u2 * L.v.x, 2033 | L.p.y + u2 * L.v.y, 2034 | L.p.z + u2 * L.v.z)) 2035 | 2036 | def _intersect_line3_plane(L, P): 2037 | d = P.n.dot(L.v) 2038 | if not d: 2039 | # Parallel 2040 | return None 2041 | u = (P.k - P.n.dot(L.p)) / d 2042 | if not L._u_in(u): 2043 | return None 2044 | return Point3(L.p.x + u * L.v.x, 2045 | L.p.y + u * L.v.y, 2046 | L.p.z + u * L.v.z) 2047 | 2048 | def _intersect_plane_plane(A, B): 2049 | n1_m = A.n.magnitude_squared() 2050 | n2_m = B.n.magnitude_squared() 2051 | n1d2 = A.n.dot(B.n) 2052 | det = n1_m * n2_m - n1d2 ** 2 2053 | if det == 0: 2054 | # Parallel 2055 | return None 2056 | c1 = (A.k * n2_m - B.k * n1d2) / det 2057 | c2 = (B.k * n1_m - A.k * n1d2) / det 2058 | return Line3(Point3(c1 * A.n.x + c2 * B.n.x, 2059 | c1 * A.n.y + c2 * B.n.y, 2060 | c1 * A.n.z + c2 * B.n.z), 2061 | A.n.cross(B.n)) 2062 | 2063 | class Point3(Vector3, Geometry): 2064 | def __repr__(self): 2065 | return 'Point3(%.2f, %.2f, %.2f)' % (self.x, self.y, self.z) 2066 | 2067 | def intersect(self, other): 2068 | return other._intersect_point3(self) 2069 | 2070 | def _intersect_sphere(self, other): 2071 | return _intersect_point3_sphere(self, other) 2072 | 2073 | def connect(self, other): 2074 | return other._connect_point3(self) 2075 | 2076 | def _connect_point3(self, other): 2077 | if self != other: 2078 | return LineSegment3(other, self) 2079 | return None 2080 | 2081 | def _connect_line3(self, other): 2082 | c = _connect_point3_line3(self, other) 2083 | if c: 2084 | return c._swap() 2085 | 2086 | def _connect_sphere(self, other): 2087 | c = _connect_point3_sphere(self, other) 2088 | if c: 2089 | return c._swap() 2090 | 2091 | def _connect_plane(self, other): 2092 | c = _connect_point3_plane(self, other) 2093 | if c: 2094 | return c._swap() 2095 | 2096 | class Line3: 2097 | __slots__ = ['p', 'v'] 2098 | 2099 | def __init__(self, *args): 2100 | if len(args) == 3: 2101 | assert isinstance(args[0], Point3) and \ 2102 | isinstance(args[1], Vector3) and \ 2103 | type(args[2]) == float 2104 | self.p = args[0].copy() 2105 | self.v = args[1] * args[2] / abs(args[1]) 2106 | elif len(args) == 2: 2107 | if isinstance(args[0], Point3) and isinstance(args[1], Point3): 2108 | self.p = args[0].copy() 2109 | self.v = args[1] - args[0] 2110 | elif isinstance(args[0], Point3) and isinstance(args[1], Vector3): 2111 | self.p = args[0].copy() 2112 | self.v = args[1].copy() 2113 | else: 2114 | raise AttributeError, '%r' % (args,) 2115 | elif len(args) == 1: 2116 | if isinstance(args[0], Line3): 2117 | self.p = args[0].p.copy() 2118 | self.v = args[0].v.copy() 2119 | else: 2120 | raise AttributeError, '%r' % (args,) 2121 | else: 2122 | raise AttributeError, '%r' % (args,) 2123 | 2124 | # XXX This is annoying. 2125 | #if not self.v: 2126 | # raise AttributeError, 'Line has zero-length vector' 2127 | 2128 | def __copy__(self): 2129 | return self.__class__(self.p, self.v) 2130 | 2131 | copy = __copy__ 2132 | 2133 | def __repr__(self): 2134 | return 'Line3(<%.2f, %.2f, %.2f> + u<%.2f, %.2f, %.2f>)' % \ 2135 | (self.p.x, self.p.y, self.p.z, self.v.x, self.v.y, self.v.z) 2136 | 2137 | p1 = property(lambda self: self.p) 2138 | p2 = property(lambda self: Point3(self.p.x + self.v.x, 2139 | self.p.y + self.v.y, 2140 | self.p.z + self.v.z)) 2141 | 2142 | def _apply_transform(self, t): 2143 | self.p = t * self.p 2144 | self.v = t * self.v 2145 | 2146 | def _u_in(self, u): 2147 | return True 2148 | 2149 | def intersect(self, other): 2150 | return other._intersect_line3(self) 2151 | 2152 | def _intersect_sphere(self, other): 2153 | return _intersect_line3_sphere(self, other) 2154 | 2155 | def _intersect_plane(self, other): 2156 | return _intersect_line3_plane(self, other) 2157 | 2158 | def connect(self, other): 2159 | return other._connect_line3(self) 2160 | 2161 | def _connect_point3(self, other): 2162 | return _connect_point3_line3(other, self) 2163 | 2164 | def _connect_line3(self, other): 2165 | return _connect_line3_line3(other, self) 2166 | 2167 | def _connect_sphere(self, other): 2168 | return _connect_sphere_line3(other, self) 2169 | 2170 | def _connect_plane(self, other): 2171 | c = _connect_line3_plane(self, other) 2172 | if c: 2173 | return c 2174 | 2175 | class Ray3(Line3): 2176 | def __repr__(self): 2177 | return 'Ray3(<%.2f, %.2f, %.2f> + u<%.2f, %.2f, %.2f>)' % \ 2178 | (self.p.x, self.p.y, self.p.z, self.v.x, self.v.y, self.v.z) 2179 | 2180 | def _u_in(self, u): 2181 | return u >= 0.0 2182 | 2183 | class LineSegment3(Line3): 2184 | def __repr__(self): 2185 | return 'LineSegment3(<%.2f, %.2f, %.2f> to <%.2f, %.2f, %.2f>)' % \ 2186 | (self.p.x, self.p.y, self.p.z, 2187 | self.p.x + self.v.x, self.p.y + self.v.y, self.p.z + self.v.z) 2188 | 2189 | def _u_in(self, u): 2190 | return u >= 0.0 and u <= 1.0 2191 | 2192 | def __abs__(self): 2193 | return abs(self.v) 2194 | 2195 | def magnitude_squared(self): 2196 | return self.v.magnitude_squared() 2197 | 2198 | def _swap(self): 2199 | # used by connect methods to switch order of points 2200 | self.p = self.p2 2201 | self.v *= -1 2202 | return self 2203 | 2204 | length = property(lambda self: abs(self.v)) 2205 | 2206 | class Sphere: 2207 | __slots__ = ['c', 'r'] 2208 | 2209 | def __init__(self, center, radius): 2210 | assert isinstance(center, Vector3) and type(radius) == float 2211 | self.c = center.copy() 2212 | self.r = radius 2213 | 2214 | def __copy__(self): 2215 | return self.__class__(self.c, self.r) 2216 | 2217 | copy = __copy__ 2218 | 2219 | def __repr__(self): 2220 | return 'Sphere(<%.2f, %.2f, %.2f>, radius=%.2f)' % \ 2221 | (self.c.x, self.c.y, self.c.z, self.r) 2222 | 2223 | def _apply_transform(self, t): 2224 | self.c = t * self.c 2225 | 2226 | def intersect(self, other): 2227 | return other._intersect_sphere(self) 2228 | 2229 | def _intersect_point3(self, other): 2230 | return _intersect_point3_sphere(other, self) 2231 | 2232 | def _intersect_line3(self, other): 2233 | return _intersect_line3_sphere(other, self) 2234 | 2235 | def connect(self, other): 2236 | return other._connect_sphere(self) 2237 | 2238 | def _connect_point3(self, other): 2239 | return _connect_point3_sphere(other, self) 2240 | 2241 | def _connect_line3(self, other): 2242 | c = _connect_sphere_line3(self, other) 2243 | if c: 2244 | return c._swap() 2245 | 2246 | def _connect_sphere(self, other): 2247 | return _connect_sphere_sphere(other, self) 2248 | 2249 | def _connect_plane(self, other): 2250 | c = _connect_sphere_plane(self, other) 2251 | if c: 2252 | return c 2253 | 2254 | class Plane: 2255 | # n.p = k, where n is normal, p is point on plane, k is constant scalar 2256 | __slots__ = ['n', 'k'] 2257 | 2258 | def __init__(self, *args): 2259 | if len(args) == 3: 2260 | assert isinstance(args[0], Point3) and \ 2261 | isinstance(args[1], Point3) and \ 2262 | isinstance(args[2], Point3) 2263 | self.n = (args[1] - args[0]).cross(args[2] - args[0]) 2264 | self.n.normalize() 2265 | self.k = self.n.dot(args[0]) 2266 | elif len(args) == 2: 2267 | if isinstance(args[0], Point3) and isinstance(args[1], Vector3): 2268 | self.n = args[1].normalized() 2269 | self.k = self.n.dot(args[0]) 2270 | elif isinstance(args[0], Vector3) and type(args[1]) == float: 2271 | self.n = args[0].normalized() 2272 | self.k = args[1] 2273 | else: 2274 | raise AttributeError, '%r' % (args,) 2275 | 2276 | else: 2277 | raise AttributeError, '%r' % (args,) 2278 | 2279 | if not self.n: 2280 | raise AttributeError, 'Points on plane are colinear' 2281 | 2282 | def __copy__(self): 2283 | return self.__class__(self.n, self.k) 2284 | 2285 | copy = __copy__ 2286 | 2287 | def __repr__(self): 2288 | return 'Plane(<%.2f, %.2f, %.2f>.p = %.2f)' % \ 2289 | (self.n.x, self.n.y, self.n.z, self.k) 2290 | 2291 | def _get_point(self): 2292 | # Return an arbitrary point on the plane 2293 | if self.n.z: 2294 | return Point3(0., 0., self.k / self.n.z) 2295 | elif self.n.y: 2296 | return Point3(0., self.k / self.n.y, 0.) 2297 | else: 2298 | return Point3(self.k / self.n.x, 0., 0.) 2299 | 2300 | def _apply_transform(self, t): 2301 | p = t * self._get_point() 2302 | self.n = t * self.n 2303 | self.k = self.n.dot(p) 2304 | 2305 | def intersect(self, other): 2306 | return other._intersect_plane(self) 2307 | 2308 | def _intersect_line3(self, other): 2309 | return _intersect_line3_plane(other, self) 2310 | 2311 | def _intersect_plane(self, other): 2312 | return _intersect_plane_plane(self, other) 2313 | 2314 | def connect(self, other): 2315 | return other._connect_plane(self) 2316 | 2317 | def _connect_point3(self, other): 2318 | return _connect_point3_plane(other, self) 2319 | 2320 | def _connect_line3(self, other): 2321 | return _connect_line3_plane(other, self) 2322 | 2323 | def _connect_sphere(self, other): 2324 | return _connect_sphere_plane(other, self) 2325 | 2326 | def _connect_plane(self, other): 2327 | return _connect_plane_plane(other, self) 2328 | 2329 | -------------------------------------------------------------------------------- /sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/01AutoMonkey/open.gl-tutorials-to-pyglet/e1490e2bfd92c645fae9fb1f1532adb5833255c3/sample.png -------------------------------------------------------------------------------- /sample2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/01AutoMonkey/open.gl-tutorials-to-pyglet/e1490e2bfd92c645fae9fb1f1532adb5833255c3/sample2.png --------------------------------------------------------------------------------