diff --git a/aux6/ej-cilindro.py b/aux6/ej-cilindro.py new file mode 100644 index 0000000..c7a15cf --- /dev/null +++ b/aux6/ej-cilindro.py @@ -0,0 +1,225 @@ +# coding=utf-8 +""" +Crea un cilindro en 3D. + +@author ppizarror +""" + +# Library imports +import glfw +from OpenGL.GL import * +import sys + +import lib.transformations2 as tr2 +import lib.basic_shapes as bs +import lib.easy_shaders as es +import lib.camera as cam +from lib.mathlib import Point3 +import numpy as np + +# Import extended shapes +import lib.basic_shapes_extended as bs_ext + +# Import lights +import lib.lights as light + + +# A class to store the application control +class Controller: + def __init__(self): + self.fillPolygon = True + + +# Global controller as communication with the callback function +controller = Controller() + +# Create camera +camera = cam.CameraR(r=3, center=Point3()) +camera.set_r_vel(0.1) + + +# noinspection PyUnusedLocal +def on_key(window_obj, key, scancode, action, mods): + global controller + global obj_light + + if action == glfw.REPEAT or action == glfw.PRESS: + # Move the camera position + if key == glfw.KEY_LEFT: + camera.rotate_phi(-4) + elif key == glfw.KEY_RIGHT: + camera.rotate_phi(4) + elif key == glfw.KEY_UP: + camera.rotate_theta(-4) + elif key == glfw.KEY_DOWN: + camera.rotate_theta(4) + elif key == glfw.KEY_A: + camera.close() + elif key == glfw.KEY_D: + camera.far() + + # Move the center of the camera + elif key == glfw.KEY_I: + camera.move_center_x(-0.05) + elif key == glfw.KEY_K: + camera.move_center_x(0.05) + elif key == glfw.KEY_J: + camera.move_center_y(-0.05) + elif key == glfw.KEY_L: + camera.move_center_y(0.05) + elif key == glfw.KEY_U: + camera.move_center_z(-0.05) + elif key == glfw.KEY_O: + camera.move_center_z(0.05) + + if action != glfw.PRESS: + return + + if key == glfw.KEY_SPACE: + controller.fillPolygon = not controller.fillPolygon + + elif key == glfw.KEY_ESCAPE: + sys.exit() + + elif key == glfw.KEY_Z: + obj_light.change_color(np.random.random(), np.random.random(), np.random.random()) + + +if __name__ == '__main__': + + # Initialize glfw + if not glfw.init(): + sys.exit() + + width = 800 + height = 800 + + window = glfw.create_window(width, height, 'Cilindro bonito', None, None) + + if not window: + glfw.terminate() + sys.exit() + + glfw.make_context_current(window) + + # Connecting the callback function 'on_key' to handle keyboard events + glfw.set_key_callback(window, on_key) + + # Creating shader programs for textures and for colores + colorShaderProgram = es.SimpleModelViewProjectionShaderProgram() + phongPipeline = es.SimplePhongShaderProgram() + + # Setting up the clear screen color + glClearColor(0.15, 0.15, 0.15, 1.0) + + # As we work in 3D, we need to check which part is in front, + # and which one is at the back + glEnable(GL_DEPTH_TEST) + + # Create models + gpuAxis = es.toGPUShape(bs.createAxis(1)) + obj_axis = bs_ext.AdvancedGPUShape(gpuAxis, shader=colorShaderProgram) + + # Create cilynder, the objective is create many cuads from the bottom, top and + # mantle. The cilynder is parametrized using an angle theta, a radius r and + # the height + h = 1 + r = 0.25 + + # Latitude and longitude of the cylinder, latitude subdivides theta, longitude + # subdivides h + lat = 20 + lon = 20 + + # Angle step + dang = 2 * np.pi / lat + + # Color + color = { + 'r': 1, # Red + 'g': 0, # Green + 'b': 0, # Blue + } + + cylinder_shape = [] # Store shapes + + # Create mantle + for i in range(lon): # Vertical component + for j in range(lat): # Horizontal component + + # Angle on step j + ang = dang * j + + # Here we create a quad from 4 vertices + # + # a/---- b/ + # | | + # c ---- d + a = [r * np.cos(ang), r * np.sin(ang), h / lon * (i + 1)] + b = [r * np.cos(ang + dang), r * np.sin(ang + dang), h / lon * (i + 1)] + d = [r * np.cos(ang + dang), r * np.sin(ang + dang), h / lon * i] + c = [r * np.cos(ang), r * np.sin(ang), h / lon * i] + + # Create quad + shape = bs_ext.create4VertexColorNormal(a, b, d, c, color['r'], color['g'], color['b']) + cylinder_shape.append(es.toGPUShape(shape)) + + # Add the two covers + for j in range(lat): + ang = dang * j + + # Bottom + a = [0, 0, 0] + b = [r * np.cos(ang), r * np.sin(ang), 0] + c = [r * np.cos(ang + dang), r * np.sin(ang + dang), 0] + shape = bs_ext.createTriangleColorNormal(c, b, a, color['r'], color['g'], color['b']) + cylinder_shape.append(es.toGPUShape(shape)) + + # Top + a = [0, 0, h] + b = [r * np.cos(ang), r * np.sin(ang), h] + c = [r * np.cos(ang + dang), r * np.sin(ang + dang), h] + shape = bs_ext.createTriangleColorNormal(c, b, a, color['r'], color['g'], color['b']) + cylinder_shape.append(es.toGPUShape(shape)) + + # Create cylinder object + obj_cylinder = bs_ext.AdvancedGPUShape(cylinder_shape, shader=phongPipeline) + + # Create light + obj_light = light.Light(phongPipeline, [5, 5, 5], [1, 1, 1]) + + # Main execution loop + while not glfw.window_should_close(window): + # Using GLFW to check for input events + glfw.poll_events() + + # Filling or not the shapes depending on the controller state + if controller.fillPolygon: + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) + else: + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) + + # Clearing the screen in both, color and depth + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) + + # Create projection + # projection = tr2.ortho(-1, 1, -1, 1, 0.1, 100) + projection = tr2.perspective(45, float(width) / float(height), 0.1, 100) + + # Get camera view matrix + view = camera.get_view() + + # Place light + obj_light.place() + + # Draw objects + obj_axis.draw(view, projection, mode=GL_LINES) + obj_cylinder.draw(view, projection) + + # Update target cube object + t = tr2.translate(camera.get_center_x(), camera.get_center_y(), camera.get_center_z()) + + # Once the drawing is rendered, buffers are swap so an uncomplete drawing is never seen. + glfw.swap_buffers(window) + + glfw.terminate() diff --git a/aux6/ej-curvas.py b/aux6/ej-curvas.py index a2350c5..f950100 100644 --- a/aux6/ej-curvas.py +++ b/aux6/ej-curvas.py @@ -136,7 +136,7 @@ def on_key(window_obj, key, scancode, action, mods): s2 = (-0.5, 0, 0) s3 = (-0.5, 0.55, 0) s4 = (0.5, 0.55, 0) - gpuTexturePlane = es.toGPUShape(bs_ext.createTexture4Vertex('shrek.png', s1, s2, s3, s4), GL_REPEAT, GL_LINEAR) + gpuTexturePlane = es.toGPUShape(bs_ext.create4VertexTexture('shrek.png', s1, s2, s3, s4), GL_REPEAT, GL_LINEAR) obj_planeS = bs_ext.AdvancedGPUShape(gpuTexturePlane, shader=textureShaderProgram) obj_planeS.rotationX(np.pi / 2) @@ -145,7 +145,7 @@ def on_key(window_obj, key, scancode, action, mods): s2 = (-0.5, 0, 0) s3 = (-0.5, 0.55, 0) s4 = (0.5, 0.55, 0) - gpuTexturePlane = es.toGPUShape(bs_ext.createTexture4Vertex('ricardo.png', s1, s2, s3, s4), GL_REPEAT, GL_LINEAR) + gpuTexturePlane = es.toGPUShape(bs_ext.create4VertexTexture('ricardo.png', s1, s2, s3, s4), GL_REPEAT, GL_LINEAR) obj_planeB = bs_ext.AdvancedGPUShape(gpuTexturePlane, shader=textureShaderProgram) obj_planeB.rotationZ(np.pi) obj_planeB.scale(1, 2, 1) diff --git a/aux6/lib/basic_shapes_extended.py b/aux6/lib/basic_shapes_extended.py index 15a634a..d81334a 100644 --- a/aux6/lib/basic_shapes_extended.py +++ b/aux6/lib/basic_shapes_extended.py @@ -16,6 +16,7 @@ from OpenGL.GL import glUseProgram as _glUseProgram import lib.transformations2 as _tr import lib.tripy as _tripy +from lib.mathlib import _normal_3_points as _normal3 # Merged shape @@ -234,7 +235,7 @@ def createColorPlaneFromCurve(curve, triangulate, r, g, b, center=None): x1, y1 = i[0] x2, y2 = i[1] x3, y3 = i[2] - shape = createColorTriangle((x1, y1, 0), (x2, y2, 0), (x3, y3, 0), r, g, b) + shape = createTriangleColor((x1, y1, 0), (x2, y2, 0), (x3, y3, 0), r, g, b) shapes.append(_toGPUShape(shape)) else: if center is None: @@ -243,18 +244,106 @@ def createColorPlaneFromCurve(curve, triangulate, r, g, b, center=None): x1, y1 = curve[i] x2, y2 = curve[(i + 1) % len(curve)] c1, c2 = center - shape = createColorTriangle((x1, y1, 0), (x2, y2, 0), (c1, c2, 0), r, g, b) + shape = createTriangleColor((x1, y1, 0), (x2, y2, 0), (c1, c2, 0), r, g, b) shapes.append(_toGPUShape(shape)) return AdvancedGPUShape(shapes) -def createColorTriangle(p1, p2, p3, r, g, b): +def create4VertexTexture(image_filename, p1, p2, p3, p4, nx=1, ny=1): """ - Creates a triangle with color. + Creates a 4-vertex poly with texture. + :param image_filename: Image :param p1: Vertex (x,y,z) :param p2: Vertex (x,y,z) :param p3: Vertex (x,y,z) + :param p4: Vertex (x,y,z) + :param nx: Texture coord pos + :param ny: Texture coord pos + :return: + """ + # Extend + p1 = __vertexUnpack3(p1) + p2 = __vertexUnpack3(p2) + p3 = __vertexUnpack3(p3) + p4 = __vertexUnpack3(p4) + + # Dissamble vertices + x1, y1, z1 = p1 + x2, y2, z2 = p2 + x3, y3, z3 = p3 + x4, y4, z4 = p4 + + # Defining locations and texture coordinates for each vertex of the shape + vertices = [ + x1, y1, z1, 0, 0, + x2, y2, z2, nx, 0, + x3, y3, z3, nx, ny, + x4, y4, z4, 0, ny + ] + + # Defining connections among vertices + # We have a triangle every 3 indices specified + indices = [ + 0, 1, 2, + 2, 3, 0] + + return _Shape(vertices, indices, image_filename) + + +def create4VertexTextureNormal(image_filename, p1, p2, p3, p4, nx=1, ny=1): + """ + Creates a 4-vertex poly with texture. + + :param image_filename: Image + :param p1: Vertex (x,y,z) + :param p2: Vertex (x,y,z) + :param p3: Vertex (x,y,z) + :param p4: Vertex (x,y,z) + :param nx: Texture coord pos + :param ny: Texture coord pos + :return: + """ + # Extend + p1 = __vertexUnpack3(p1) + p2 = __vertexUnpack3(p2) + p3 = __vertexUnpack3(p3) + p4 = __vertexUnpack3(p4) + + # Dissamble vertices + x1, y1, z1 = p1 + x2, y2, z2 = p2 + x3, y3, z3 = p3 + x4, y4, z4 = p4 + + # Calculate the normal + normal = _normal3(p3, p2, p1) + + # Defining locations and texture coordinates for each vertex of the shape + vertices = [ + x1, y1, z1, 0, 0, normal.get_x(), normal.get_y(), normal.get_z(), + x2, y2, z2, nx, 0, normal.get_x(), normal.get_y(), normal.get_z(), + x3, y3, z3, nx, ny, normal.get_x(), normal.get_y(), normal.get_z(), + x4, y4, z4, 0, ny, normal.get_x(), normal.get_y(), normal.get_z() + ] + + # Defining connections among vertices + # We have a triangle every 3 indices specified + indices = [ + 0, 1, 2, + 2, 3, 0] + + return _Shape(vertices, indices, image_filename) + + +def create4VertexColor(p1, p2, p3, p4, r, g, b): + """ + Creates a 4-vertex poly with color. + + :param p1: Vertex (x,y,z) + :param p2: Vertex (x,y,z) + :param p3: Vertex (x,y,z) + :param p4: Vertex (x,y,z) :param r: Red color :param g: Green color :param b: Blue color @@ -264,11 +353,13 @@ def createColorTriangle(p1, p2, p3, r, g, b): p1 = __vertexUnpack3(p1) p2 = __vertexUnpack3(p2) p3 = __vertexUnpack3(p3) + p4 = __vertexUnpack3(p4) # Dissamble vertices x1, y1, z1 = p1 x2, y2, z2 = p2 x3, y3, z3 = p3 + x4, y4, z4 = p4 # Defining locations and color vertices = [ @@ -276,28 +367,30 @@ def createColorTriangle(p1, p2, p3, r, g, b): x1, y1, z1, r, g, b, x2, y2, z2, r, g, b, x3, y3, z3, r, g, b, + x4, y4, z4, r, g, b, ] # Defining connections among vertices # We have a triangle every 3 indices specified indices = [ - 0, 1, 2 + 0, 1, 2, + 2, 3, 0 ] return _Shape(vertices, indices) -def createTexture4Vertex(image_filename, p1, p2, p3, p4, nx=1, ny=1): +def create4VertexColorNormal(p1, p2, p3, p4, r, g, b): """ - Creates a 4-vertex poly with texture. + Creates a 4-vertex figure with color and normals. - :param image_filename: Image :param p1: Vertex (x,y,z) :param p2: Vertex (x,y,z) :param p3: Vertex (x,y,z) :param p4: Vertex (x,y,z) - :param nx: Texture coord pos - :param ny: Texture coord pos + :param r: Red color + :param g: Green color + :param b: Blue color :return: """ # Extend @@ -312,24 +405,29 @@ def createTexture4Vertex(image_filename, p1, p2, p3, p4, nx=1, ny=1): x3, y3, z3 = p3 x4, y4, z4 = p4 - # Defining locations and texture coordinates for each vertex of the shape + # Calculate the normal + normal = _normal3(p3, p2, p1) + + # Defining locations and color vertices = [ - x1, y1, z1, 0, 0, - x2, y2, z2, nx, 0, - x3, y3, z3, nx, ny, - x4, y4, z4, 0, ny + # X, Y, Z, R, G, B, + x1, y1, z1, r, g, b, normal.get_x(), normal.get_y(), normal.get_z(), + x2, y2, z2, r, g, b, normal.get_x(), normal.get_y(), normal.get_z(), + x3, y3, z3, r, g, b, normal.get_x(), normal.get_y(), normal.get_z(), + x4, y4, z4, r, g, b, normal.get_x(), normal.get_y(), normal.get_z() ] # Defining connections among vertices # We have a triangle every 3 indices specified indices = [ 0, 1, 2, - 2, 3, 0] + 2, 3, 0 + ] - return _Shape(vertices, indices, image_filename) + return _Shape(vertices, indices) -def createTextureTriangle(image_filename, p1, p2, p3, nx=1, ny=1): +def createTriangleTexture(image_filename, p1, p2, p3, nx=1, ny=1): """ Creates a triangle with textures. @@ -361,3 +459,121 @@ def createTextureTriangle(image_filename, p1, p2, p3, nx=1, ny=1): ] return _Shape(vertices, indices, image_filename) + + +def createTriangleTextureNormal(image_filename, p1, p2, p3, nx=1, ny=1): + """ + Creates a triangle with textures. + + :param image_filename: Image + :param p1: Vertex (x,y,z) + :param p2: Vertex (x,y,z) + :param p3: Vertex (x,y,z) + :param nx: Texture coord pos + :param ny: Texture coord pos + :return: + """ + # Dissamble vertices + x1, y1, z1 = p1 + x2, y2, z2 = p2 + x3, y3, z3 = p3 + + # Calculate the normal + normal = _normal3(p3, p2, p1) + + # Defining locations and texture coordinates for each vertex of the shape + vertices = [ + # X, Y, Z, U, V + x1, y1, z1, (nx + ny) / 2, nx, normal.get_x(), normal.get_y(), normal.get_z(), + x2, y2, z2, 0.0, 0.0, normal.get_x(), normal.get_y(), normal.get_z(), + x3, y3, z3, ny, 0.0, normal.get_x(), normal.get_y(), normal.get_z() + ] + + # Defining connections among vertices + # We have a triangle every 3 indices specified + indices = [ + 0, 1, 2 + ] + + return _Shape(vertices, indices, image_filename) + + +def createTriangleColor(p1, p2, p3, r, g, b): + """ + Creates a triangle with color. + + :param p1: Vertex (x,y,z) + :param p2: Vertex (x,y,z) + :param p3: Vertex (x,y,z) + :param r: Red color + :param g: Green color + :param b: Blue color + :return: + """ + # Extend + p1 = __vertexUnpack3(p1) + p2 = __vertexUnpack3(p2) + p3 = __vertexUnpack3(p3) + + # Dissamble vertices + x1, y1, z1 = p1 + x2, y2, z2 = p2 + x3, y3, z3 = p3 + + # Defining locations and color + vertices = [ + # X, Y, Z, R, G, B, + x1, y1, z1, r, g, b, + x2, y2, z2, r, g, b, + x3, y3, z3, r, g, b, + ] + + # Defining connections among vertices + # We have a triangle every 3 indices specified + indices = [ + 0, 1, 2 + ] + + return _Shape(vertices, indices) + + +def createTriangleColorNormal(p1, p2, p3, r, g, b): + """ + Creates a triangle with color. + + :param p1: Vertex (x,y,z) + :param p2: Vertex (x,y,z) + :param p3: Vertex (x,y,z) + :param r: Red color + :param g: Green color + :param b: Blue color + :return: + """ + # Extend + p1 = __vertexUnpack3(p1) + p2 = __vertexUnpack3(p2) + p3 = __vertexUnpack3(p3) + + # Dissamble vertices + x1, y1, z1 = p1 + x2, y2, z2 = p2 + x3, y3, z3 = p3 + + # Calculate the normal + normal = _normal3(p3, p2, p1) + + # Defining locations and color + vertices = [ + # X, Y, Z, R, G, B, + x1, y1, z1, r, g, b, normal.get_x(), normal.get_y(), normal.get_z(), + x2, y2, z2, r, g, b, normal.get_x(), normal.get_y(), normal.get_z(), + x3, y3, z3, r, g, b, normal.get_x(), normal.get_y(), normal.get_z(), + ] + + # Defining connections among vertices + # We have a triangle every 3 indices specified + indices = [ + 0, 1, 2 + ] + + return _Shape(vertices, indices) diff --git a/aux6/lib/lights.py b/aux6/lib/lights.py new file mode 100644 index 0000000..728d735 --- /dev/null +++ b/aux6/lib/lights.py @@ -0,0 +1,127 @@ +""" +DefiniciĆ³n de clase de luces. + +@author ppizarror +""" + +from OpenGL.GL import glUseProgram as _glUseProgram +from OpenGL.GL import glUniform3f as _glUniform3f +from OpenGL.GL import glGetUniformLocation as _glGetUniformLocation +from OpenGL.GL import glUniform1ui as _glUniform1ui +from OpenGL.GL import glUniform1f as _glUniform1f + + +class Light(object): + def __init__(self, shader, position, color, shininess=100, constantAttenuation=0.001, linearAttenuation=0.1, + qudraticAttenuation=0.01): + """ + Constructor. + + :param shader: Shader class + :param position: Light position + :type position: list + :param color: Color of the light + :type color: list + :param shininess: Shininess + :type shininess: float + :param constantAttenuation: Light constant attenuation + :type constantAttenuation: float + :param linearAttenuation: Light linear attenuation + :type linearAttenuation: float + :param qudraticAttenuation: Light quadratic attenuation + :type qudraticAttenuation: float + """ + assert isinstance(color, list), 'Color is not a list' + assert len(color) == 3, 'Color must have 3 components' + assert isinstance(position, list), 'Position is not a list' + assert len(position) == 3, 'Position must have 3 components' + self._shader = shader + self._position = position + self._color = color + self._shininess = shininess + self._cAtt = constantAttenuation + self._lAtt = linearAttenuation + self._qAtt = qudraticAttenuation + self._enabled = True + + def move_position(self, dx=0, dy=0, dz=0): + """ + Move light position. + + :param dx: + :param dy: + :param dz: + :return: + """ + self._position[0] += dx + self._position[1] += dy + self._position[2] += dz + + def set_position(self, x, y, z): + """ + Set light position + + :param x: + :param y: + :param z: + :return: + """ + self._position[0] = x + self._position[1] = y + self._position[2] = z + + def change_color(self, r, g, b): + """ + Change light color. + + :param r: + :param g: + :param b: + :return: + """ + self._color[0] = r + self._color[1] = g + self._color[2] = b + + def enable(self): + """ + Enable light. + + :return: + """ + self._enabled = True + + def disable(self): + """ + Disable light. + + :return: + """ + self._enabled = False + + def set_shader(self, shader): + """ + Change the light shader. + + :param shader: Shader program + :return: + """ + self._shader = shader + + def place(self): + """ + Place light on engine. + + :return: + """ + if not self._enabled: + return + _glUseProgram(self._shader.shaderProgram) + _glUniform3f(_glGetUniformLocation(self._shader.shaderProgram, 'lightColor'), + self._color[0], self._color[1], self._color[2]) + _glUniform3f(_glGetUniformLocation(self._shader.shaderProgram, 'lightPos'), + self._position[0], self._position[1], self._position[2]) + _glUniform1ui(_glGetUniformLocation(self._shader.shaderProgram, 'shininess'), self._shininess) + _glUniform1f(_glGetUniformLocation(self._shader.shaderProgram, 'constantAttenuation'), self._cAtt) + _glUniform1f(_glGetUniformLocation(self._shader.shaderProgram, 'linearAttenuation'), self._lAtt) + _glUniform1f(_glGetUniformLocation(self._shader.shaderProgram, 'quadraticAttenuation'), self._qAtt)