text: # coding=utf-8
  """Tarea 2"""
  
  import glfw
  from OpenGL.GL import *
  import OpenGL.GL.shaders
  import numpy as np
  import sys
  import os.path
  sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  import grafica.transformations as tr
  import grafica.basic_shapes as bs
  import grafica.scene_graph as sg
  import grafica.easy_shaders as es
  import grafica.lighting_shaders as ls
  import grafica.performance_monitor as pm
  from grafica.assets_path import getAssetPath
  
  __author__ = "Patricio A. Viñals M."
  __license__ = "MIT"
  
  # A class to store the application control
  class Controller:
      def __init__(self):
          self.fillPolygon = True
          self.showAxis = True
          self.viewPos = np.array([10,10,10])
          self.camUp = np.array([0, 1, 0])
          self.distance = 10
  
  
  controller = Controller()
  
  def setPlot(pipeline, mvpPipeline):
      projection = tr.perspective(45, float(width)/float(height), 0.1, 100)
  
      glUseProgram(mvpPipeline.shaderProgram)
      glUniformMatrix4fv(glGetUniformLocation(mvpPipeline.shaderProgram, "projection"), 1, GL_TRUE, projection)
  
      glUseProgram(pipeline.shaderProgram)
      glUniformMatrix4fv(glGetUniformLocation(pipeline.shaderProgram, "projection"), 1, GL_TRUE, projection)
      
      glUniform3f(glGetUniformLocation(pipeline.shaderProgram, "La"), 1.0, 1.0, 1.0)
      glUniform3f(glGetUniformLocation(pipeline.shaderProgram, "Ld"), 1.0, 1.0, 1.0)
      glUniform3f(glGetUniformLocation(pipeline.shaderProgram, "Ls"), 1.0, 1.0, 1.0)
  
      glUniform3f(glGetUniformLocation(pipeline.shaderProgram, "Ka"), 0.2, 0.2, 0.2)
      glUniform3f(glGetUniformLocation(pipeline.shaderProgram, "Kd"), 0.9, 0.9, 0.9)
      glUniform3f(glGetUniformLocation(pipeline.shaderProgram, "Ks"), 1.0, 1.0, 1.0)
  
      glUniform3f(glGetUniformLocation(pipeline.shaderProgram, "lightPosition"), 5, 5, 5)
      
      glUniform1ui(glGetUniformLocation(pipeline.shaderProgram, "shininess"), 1000)
      glUniform1f(glGetUniformLocation(pipeline.shaderProgram, "constantAttenuation"), 0.001)
      glUniform1f(glGetUniformLocation(pipeline.shaderProgram, "linearAttenuation"), 0.1)
      glUniform1f(glGetUniformLocation(pipeline.shaderProgram, "quadraticAttenuation"), 0.01)
  
  def setView(pipeline, mvpPipeline):
      view = tr.lookAt(
              controller.viewPos,
              np.array([0,0,0]),
              controller.camUp
          )
  
      glUseProgram(mvpPipeline.shaderProgram)
      glUniformMatrix4fv(glGetUniformLocation(mvpPipeline.shaderProgram, "view"), 1, GL_TRUE, view)
  
      glUseProgram(pipeline.shaderProgram)
      glUniformMatrix4fv(glGetUniformLocation(pipeline.shaderProgram, "view"), 1, GL_TRUE, view)
      glUniform3f(glGetUniformLocation(pipeline.shaderProgram, "viewPosition"), controller.viewPos[0], controller.viewPos[1], controller.viewPos[2])
      
  
  def on_key(window, key, scancode, action, mods):
  
      if action != glfw.PRESS:
          return
      
      global controller
  
      if key == glfw.KEY_SPACE:
          controller.fillPolygon = not controller.fillPolygon
  
      elif key == glfw.KEY_LEFT_CONTROL:
          controller.showAxis = not controller.showAxis
  
      elif key == glfw.KEY_ESCAPE:
          glfw.set_window_should_close(window, True)
      
      elif key == glfw.KEY_1:
          controller.viewPos = np.array([controller.distance,controller.distance,controller.distance]) #Vista diagonal 1
          controller.camUp = np.array([0,1,0])
      
      elif key == glfw.KEY_2:
          controller.viewPos = np.array([0,0,controller.distance]) #Vista frontal
          controller.camUp = np.array([0,1,0])
  
      elif key == glfw.KEY_3:
          controller.viewPos = np.array([controller.distance,0,controller.distance]) #Vista lateral
          controller.camUp = np.array([0,1,0])
  
      elif key == glfw.KEY_4:
          controller.viewPos = np.array([0,controller.distance,0]) #Vista superior
          controller.camUp = np.array([1,0,0])
      
      elif key == glfw.KEY_5:
          controller.viewPos = np.array([controller.distance,controller.distance,-controller.distance]) #Vista diagonal 2
          controller.camUp = np.array([0,1,0])
      
      elif key == glfw.KEY_6:
          controller.viewPos = np.array([-controller.distance,controller.distance,-controller.distance]) #Vista diagonal 2
          controller.camUp = np.array([0,1,0])
      
      elif key == glfw.KEY_7:
          controller.viewPos = np.array([-controller.distance,controller.distance,controller.distance]) #Vista diagonal 2
          controller.camUp = np.array([0,1,0])
      
      else:
          print('Unknown key')
  
  def createGPUShape(pipeline, shape):
      gpuShape = es.GPUShape().initBuffers()
      pipeline.setupVAO(gpuShape)
      gpuShape.fillBuffers(shape.vertices, shape.indices, GL_STATIC_DRAW)
  
      return gpuShape
  
  #NOTA: Aqui creas tu escena. En escencia, sólo tendrías que modificar esta función.
  def createScene(pipeline):
      sphere = createGPUShape(pipeline, bs.createColorSphereTarea2(0.0, 0.59, 0.78))
      cone = createGPUShape(pipeline, bs.createColorConeTarea2(1.0,0.73,0.03))
      cube = createGPUShape(pipeline, bs.createColorCubeTarea2(1.0,0.0,0.0))
      cylinder = createGPUShape(pipeline, bs.createColorCylinderTarea2(0.0,1.0,0.0))
  
      sphereNode = sg.SceneGraphNode('sphere')
      sphereNode.transform = tr.matmul([tr.translate(1.0, 1.0, 1.0), tr.uniformScale(0.5)])
      sphereNode.childs += [sphere]
  
      cubeNode = sg.SceneGraphNode('cube')
      cubeNode.transform = tr.matmul([tr.translate(-1.0, 1.0, 1.0) , tr.uniformScale(0.5)])
      cubeNode.childs += [cube]
  
      cylinderNode = sg.SceneGraphNode('cylinder')
      cylinderNode.transform = tr.matmul([tr.translate(1.0, 1.0, -1.0) , tr.uniformScale(0.5)])
      cylinderNode.childs += [cylinder]
      
      coneNode = sg.SceneGraphNode('cone')
      coneNode.transform = tr.matmul([tr.translate(-1.0, 1.0, -1.0) , tr.uniformScale(0.5)])
      coneNode.childs += [cone]
  
      scene = sg.SceneGraphNode('system')
      scene.childs += [coneNode]
      scene.childs += [sphereNode]
      scene.childs += [cubeNode]
      scene.childs += [cylinderNode]
      
      return scene
  
  if __name__ == "__main__":
  
      # Initialize glfw
      if not glfw.init():
          glfw.set_window_should_close(window, True)
  
      width = 800
      height = 800
      title = "Tarea 2"
      window = glfw.create_window(width, height, title, None, None)
  
      if not window:
          glfw.terminate()
          glfw.set_window_should_close(window, True)
  
      glfw.make_context_current(window)
  
      # Connecting the callback function 'on_key' to handle keyboard events
      glfw.set_key_callback(window, on_key)
  
      # Assembling the shader program (pipeline) with both shaders
      mvpPipeline = es.SimpleModelViewProjectionShaderProgram()
      pipeline = ls.SimpleGouraudShaderProgram()
      
      # Telling OpenGL to use our shader program
      glUseProgram(mvpPipeline.shaderProgram)
  
      # Setting up the clear screen color
      glClearColor(0.85, 0.85, 0.85, 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)
  
      # Creating shapes on GPU memory
      cpuAxis = bs.createAxis(7)
      gpuAxis = es.GPUShape().initBuffers()
      mvpPipeline.setupVAO(gpuAxis)
      gpuAxis.fillBuffers(cpuAxis.vertices, cpuAxis.indices, GL_STATIC_DRAW)
  
      #NOTA: Aqui creas un objeto con tu escena
      dibujo = createScene(pipeline)
  
      setPlot(pipeline, mvpPipeline)
  
      perfMonitor = pm.PerformanceMonitor(glfw.get_time(), 0.5)
  
      # glfw will swap buffers as soon as possible
      glfw.swap_interval(0)
  
      while not glfw.window_should_close(window):
  
          # Measuring performance
          perfMonitor.update(glfw.get_time())
          glfw.set_window_title(window, title + str(perfMonitor))
  
          # Using GLFW to check for input events
          glfw.poll_events()
  
          # Clearing the screen in both, color and depth
          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
  
          # 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)
  
          setView(pipeline, mvpPipeline)
  
          if controller.showAxis:
              glUseProgram(mvpPipeline.shaderProgram)
              glUniformMatrix4fv(glGetUniformLocation(mvpPipeline.shaderProgram, "model"), 1, GL_TRUE, tr.identity())
              mvpPipeline.drawCall(gpuAxis, GL_LINES)
  
          #NOTA: Aquí dibujas tu objeto de escena
          glUseProgram(pipeline.shaderProgram)
          sg.drawSceneGraphNode(dibujo, pipeline, "model")
          
  
          # Once the render is done, buffers are swapped, showing only the complete scene.
          glfw.swap_buffers(window)
  
      # freeing GPU memory
      gpuAxis.clear()
      dibujo.clear()
      
  
      glfw.terminate()