import gh_node
import gh_object
import gh_camera
import gh_gpu_program
import gh_utils
import gh_renderer
import gh_window
import gh_input
import math




_PI_OVER_180 = 3.14159265 / 180.0



class vec3:
  def __init__(self):
    self.x = 0
    self.y = 0
    self.z = 0
    
  def neg(self):
    self.x = -self.x
    self.y = -self.y
    self.z = -self.z
    
  def dot(self, v2):
    return self.x*v2.x + self.y*v2.y + self.z*v2.z

  def cross(self, v2):
    v = vec3()
    v.x = self.y*v2.z - self.z*v2.y
    v.y = self.z*v2.x - self.x*v2.z
    v.z = self.x*v2.y - self.y*v2.x
    return v

  def square_len(self):
    return self.x*self.x + self.y*self.y + self.z*self.z
  
  def len(self):
    return math.sqrt(self.square_len())
    
  def normalize(self):
    norm = self.len()
    multiplier = 1
    if (norm > 0):
      multiplier = 1.0/norm
    else:
      multiplier = 0.00001
		
    self.x = self.x * multiplier
    self.y = self.y * multiplier
    self.z = self.z * multiplier
    
  def distance(self, v2):
    return math.sqrt(self.square_distance(v2))
    









class gxcamera:
  def __init__(self):
    self._ctrl_key = 0
    self._mouse_wheel_delta = 0
    self._do_rotate = 0
    self._do_pan = 0
    self._pan_mx = 0
    self._pan_my = 0
    self._pan_speed_factor = 0.25
    self._old_mouse_x = 0
    self._old_mouse_y = 0
    self._orbit_yaw = 0
    self._orbit_pitch = 0
    self._keyboard_speed = 0.5
    self._lookat_x = 0
    self._lookat_y = 0
    self._lookat_z = 0
    self._MODE_ORBIT = 1
    self._MODE_FLY = 2
    self._MODE_FPS = 3
    self._mode = self._MODE_ORBIT
    self._grid = 0
    self._grid_program = 0
    self._is_moving = 0



  def set_mode(self, mode):
    self._mode = mode


  def set_mode_orbit(self):
    self._mode = self._MODE_ORBIT

  def set_mode_fly(self):
    self._mode = self._MODE_FLY

  def set_mode_fps(self):
    self._mode = self._MODE_FPS






  def create_perspective(self, fov, is_vertical_fov, viewport_x, viewport_y, viewport_width, viewport_height, znear, zfar):
    aspect = viewport_width / viewport_height
    camera = gh_camera.create_persp_v2(fov, is_vertical_fov, aspect, znear, zfar)
    gh_camera.set_viewport(camera, viewport_x, viewport_y, viewport_width, viewport_height)
    gh_camera.set_position(camera, 0, 0, 20)
    gh_camera.set_lookat(camera, 0, 0, 0, 1)
    gh_camera.setupvec(camera, 0, 1, 0, 0)
    return camera





  def update_perspective(self, camera, fov, is_vertical_fov, viewport_x, viewport_y, viewport_width, viewport_height, znear, zfar):
    aspect = viewport_width / viewport_height
    gh_camera.update_persp_v2(camera, fov, is_vertical_fov, aspect, znear, zfar)
    gh_camera.set_viewport(camera, viewport_x, viewport_y, viewport_width, viewport_height)




  def _init_camera_angles(self, camera, pitch, yaw):
    self._orbit_yaw = yaw
    self._orbit_pitch = pitch



  def _init_camera_orbit(self, camera, x, y):
    self._old_mouse_x = x
    self._old_mouse_y = y






  def _rotate_camera_position_around_point(self, camera, lookat_point_x, lookat_point_y, lookat_point_z, pitch, yaw, roll):
    cam_pos_x, cam_pos_y, cam_pos_z = gh_object.get_position(camera)
    vx = cam_pos_x - lookat_point_x
    vy = cam_pos_y - lookat_point_y
    vz = cam_pos_z - lookat_point_z
    angX = pitch * _PI_OVER_180
    angY = yaw * _PI_OVER_180
    angZ = roll * _PI_OVER_180
    mag = math.sqrt(vx*vx + vy*vy + vz*vz)
    new_cam_pos_x = lookat_point_x + mag * math.cos(angY) * math.cos(angX)
    new_cam_pos_y = lookat_point_y + mag * math.sin(angX)
    new_cam_pos_z = lookat_point_z + mag * math.sin(angY) * math.cos(angX)
    gh_camera.set_position(camera, new_cam_pos_x, new_cam_pos_y, new_cam_pos_z)
    gh_camera.set_lookat(camera, lookat_point_x, lookat_point_y, lookat_point_z, 1)








  def _rotate_camera_orbit(self, camera, mouse_x, mouse_y, lookat_x, lookat_y, lookat_z):
    dx = mouse_x - self._old_mouse_x
    dy = mouse_y - self._old_mouse_y

    self._old_mouse_x = mouse_x
    self._old_mouse_y = mouse_y

    dyaw = dx * 0.1
    dpitch = dy * 0.1

    self._orbit_yaw = self._orbit_yaw + dyaw
    self._orbit_pitch = self._orbit_pitch + dpitch


    if (self._orbit_pitch >= 89.0):
      self._orbit_pitch = 89.0

    if (self._orbit_pitch <= -89.0):
      self._orbit_pitch = -89.0

    self._rotate_camera_position_around_point(camera, lookat_x, lookat_y, lookat_z, self._orbit_pitch, self._orbit_yaw, 0.0)







  def orbit(self, camera, lookat_x, lookat_y, lookat_z):
    LEFT_BUTTON = 1
    self.orbit_v2(camera, lookat_x, lookat_y, lookat_z, LEFT_BUTTON)





  def orbit_v2(self, camera, lookat_x, lookat_y, lookat_z, mouse_button):
    # LEFT_BUTTON = 1
    #  RIGHT_BUTTON = 2
    is_down = gh_input.mouse_get_button_state(mouse_button) 
    if ((is_down == 1) and (self._do_rotate == 0)):
      mx, my = gh_input.mouse_get_position()

      if (gh_utils.get_platform() == 2): # macos 
        self._init_camera_orbit(camera, mx, my)
      else: # Windows and Linux
        self._init_camera_orbit(camera, mx, my)

      self._do_rotate = 1

    if (is_down == 0):
      self._do_rotate = 0

    if (self._do_rotate == 1):
      mx, my = gh_input.mouse_getpos()
      
      if ((mx != self._old_mouse_x) or (my != self._old_mouse_y)):
        if (gh_utils.get_platform() == 2): # macos  
          self._rotate_camera_orbit(camera, mx, my, lookat_x, lookat_y, lookat_z)
        else: # Windows and Linux
          self._rotate_camera_orbit(camera, mx, my, lookat_x, lookat_y, lookat_z)






  def init_orientation(self, camera, lookat_x, lookat_y, lookat_z, pitch, yaw):
    self._init_camera_angles(camera, pitch, yaw)
    self._rotate_camera_position_around_point(camera, lookat_x, lookat_y, lookat_z, pitch, yaw, 0)





  def move_along_view(self, camera, distance):
    px, py, pz = gh_camera.get_position(camera)
    vx, vy, vz = gh_camera.get_view(camera)
    px = px + (vx * distance)
    py = py + (vy * distance)
    pz = pz + (vz * distance)
    gh_camera.set_position(camera, px, py, pz)







  def move_along_view_xz(self, camera, distance):
    px, py, pz = gh_camera.get_position(camera)
    vx, vy, vz = gh_camera.get_view(camera)
    px = px + (vx * distance)
    pz = pz + (vz * distance)
    gh_camera.set_position(camera, px, py, pz)



  def rotate_view(self, camera, pitch, yaw):
    gh_camera.set_yaw(camera, yaw)
    gh_camera.set_pitch(camera, pitch)



  def strafe_h(self, camera, dist, update_lookat):
    self.strafe_h_v2(camera, dist, update_lookat)



  def strafe_h_v2(self, camera, dist, update_lookat):
    v = vec3()
    u = vec3()
    v.x, v.y, v.z = gh_camera.get_view(camera)
    v.neg()
    u.x, u.y, u.z = gh_camera.get_up_vector(camera)
    xvec = v.cross(u)
    p = vec3()
    p.x, p.y, p.z = gh_camera.get_position(camera)
    #p = p + (xvec * dist)   #  Error data: TypeError("unsupported operand type(s) for *: 'vec3' and 'float'")
    p.x = p.x + (xvec.x * dist)
    p.y = p.y + (xvec.y * dist)
    p.z = p.z + (xvec.z * dist)
    gh_camera.set_position(camera, p.x, p.y, p.z)

    if (update_lookat == 1):
      lookat_x = self._lookat_x + (xvec.x * dist)
      lookat_y = self._lookat_y + (xvec.y * dist)
      lookat_z = self._lookat_z + (xvec.z * dist)
      self.set_orbit_lookat(camera, lookat_x, lookat_y, lookat_z)  






  def strafe_h_xz(self, camera, dist, update_lookat):
    v = vec3()
    u = vec3()
    v.x, v.y, v.z = gh_camera.get_view(camera)
    v.neg()
    u.x, u.y, u.z = gh_camera.get_up_vector(camera)
    xvec = v.cross(u)
    p = vec3()
    p.x, p.y, p.z = gh_camera.get_position(camera)
    p.x = p.x + (xvec.x * dist)
    p.z = p.z + (xvec.z * dist)
    gh_camera.set_position(camera, p.x, p.y, p.z)

    if (update_lookat == 1):
      lookat_x = self._lookat_x + (xvec.x * dist)
      lookat_y = self._lookat_y + (xvec.y * dist)
      lookat_z = self._lookat_z + (xvec.z * dist)
      self.set_orbit_lookat(camera, lookat_x, lookat_y, lookat_z)  






  def strafe_v(self, camera, dist, update_lookat):
    self.strafe_v_v2(camera, dist, update_lookat)



  def strafe_v_v2(self, camera, dist, update_lookat):
    # Vertical strafe
    v = vec3()
    u = vec3()
    v.x, v.y, v.z = gh_camera.get_view(camera)
    v.neg()
    u.x, u.y, u.z = gh_camera.get_up_vector(camera)
    #xvec = v.cross(u)
    p = vec3()
    p.x, p.y, p.z = gh_camera.get_position(camera)
    #p = p + (u * dist) #  Error data: TypeError("unsupported operand type(s) for *: 'vec3' and 'float'")
    p.x = p.x + (u.x * dist)
    p.y = p.y + (u.y * dist)
    p.z = p.z + (u.z * dist)
    gh_camera.set_position(camera, p.x, p.y, p.z)

    if (update_lookat == 1):
      lookat_x = self._lookat_x + (u.x * dist)
      lookat_y = self._lookat_y + (u.y * dist)
      lookat_z = self._lookat_z + (u.z * dist)
      self.set_orbit_lookat(camera, lookat_x, lookat_y, lookat_z)  



  def set_keyboard_speed(self, speed):
    self._keyboard_speed = speed











  def ComputeProjectiveScaleFactor(self, camera, lookat_x, lookat_y, lookat_z, screen_height, fov):
    fov_rad = fov * _PI_OVER_180
    focalLength = (0.5 * screen_height) / (math.tan(fov_rad / 2))
    px, py, pz = gh_camera.get_position(camera)
    dx = lookat_x - px
    dy = lookat_y - py
    dz = lookat_z - pz
    pivotDistance = math.sqrt((dx*dx) + (dy*dy) + (dz*dz))
    S = pivotDistance / focalLength
    return S






  def pan(self, camera, speed_factor):
    KC_LEFT_CTRL = 29
    is_ctrl_down = gh_input.keyboard_is_key_down(KC_LEFT_CTRL) 
    is_leftbutton_down = 0

    dx = 0
    dy = 0

    self._ctrl_key = is_ctrl_down
    if (is_ctrl_down == 1):
      LEFT_BUTTON = 1
      is_leftbutton_down = gh_input.mouse_get_button_state(LEFT_BUTTON) 
      mx, my = gh_input.mouse_get_position()
      if (is_leftbutton_down == 1):
        #gh_utils.trace("PAN - LEFT button down %d;%d" % (mx, my))
        self._pan_mx = mx
        self._pan_my = my
        self._do_pan = 1

      dx = mx - self._pan_mx
      dy = my - self._pan_my
      self._pan_mx = mx
      self._pan_my = my


      if ((is_leftbutton_down == 1) and (self._do_pan == 0)):
        mx, my = gh_input.mouse_get_position()
        self._pan_mx = mx
        self._pan_my = my
        self._do_pan = 1


      mx, my = gh_input.mouse_get_position()
      dx = mx - self._pan_mx
      dy = my - self._pan_my
      self._pan_mx = mx
      self._pan_my = my
    
    if (is_leftbutton_down == 0):
      self._do_pan = 0
  

    if (self._do_pan == 1):
      self.strafe_h(camera, dx * speed_factor, 1)
      self.strafe_v(camera, dy * speed_factor, 1)




  
  def pan2(self, camera, speed_factor, mouse_button):
    is_mousebutton_down = gh_input.mouse_get_button_state(mouse_button) 
    if ((is_mousebutton_down == 1) and (self._do_pan == 0)):
      mx, my = gh_input.mouse_get_position()
      self._pan_mx = mx
      self._pan_my = my
      self._do_pan = 1

    if (is_mousebutton_down == 0):
      self._do_pan = 0

    if (self._do_pan == 1):
      mx, my = gh_input.mouse_get_position()
      dx = (mx - self._pan_mx)
      dy = (my - self._pan_my)
      self._pan_mx = mx
      self._pan_my = my

      distx = dx * speed_factor
      disty = dy * speed_factor
      self.strafe_h(camera, distx, 1)
      self.strafe_v(camera, disty, 1)
  



  def is_moving(self):
    return self._is_moving






  def update_fly(self, camera, dt):
    # OSX platform.
    KC_UP = 200
    KC_LEFT = 203
    KC_RIGHT = 205
    KC_DOWN = 208
    
    #
    # Override for Windows plateform.
    """
    if (gh_utils.get_platform() == 1) then
      KC_UP = 72
      KC_DOWN = 80
      KC_RIGHT = 77
      KC_LEFT = 75
    end
    """

    KC_W = 17
    KC_S = 31
    KC_A = 30
    KC_D = 32
    KC_R = 19
    KC_F = 33
    

    cam_dist = self._keyboard_speed * dt
    if ((gh_input.keyboard_is_key_down(KC_W) == 1) or (gh_input.keyboard_is_key_down(KC_UP) == 1)):
      self.move_along_view(camera, cam_dist)
      self._is_moving = 1

    if ((gh_input.keyboard_is_key_down(KC_S) == 1) or (gh_input.keyboard_is_key_down(KC_DOWN) == 1)):
      self.move_along_view(camera, -cam_dist)
      self._is_moving = 1

    if ((gh_input.keyboard_is_key_down(KC_A) == 1) or (gh_input.keyboard_is_key_down(KC_LEFT) == 1)):
      self.strafe_h_v2(camera, cam_dist, 0)
      self._is_moving = 1

    if ((gh_input.keyboard_is_key_down(KC_D) == 1) or (gh_input.keyboard_is_key_down(KC_RIGHT) == 1)):
      self.strafe_h_v2(camera, -cam_dist, 0)
      self._is_moving = 1


    if (gh_input.keyboard_is_key_down(KC_R) == 1):
      self.strafe_v_v2(camera, cam_dist, 0)
      self._is_moving = 1

    if (gh_input.keyboard_is_key_down(KC_F) == 1):
      self.strafe_v_v2(camera, -cam_dist, 0)
      self._is_moving = 1
    
    LEFT_BUTTON = 1
    is_down = gh_input.mouse_get_button_state(LEFT_BUTTON) 
    if ((is_down == 1) and (self._do_rotate == 0)):
      self._old_mouse_x, self._old_mouse_y = gh_input.mouse_getpos()
      self._do_rotate = 1
    
    if (is_down == 0):
      self._do_rotate = 0
    

    if (self._do_rotate == 1):
      mx, my = gh_input.mouse_getpos()
      
      mouse_dx = (mx - self._old_mouse_x) * _PI_OVER_180
      mouse_dy =(my - self._old_mouse_y) * _PI_OVER_180

      self._old_mouse_x = mx
      self._old_mouse_y = my
      
      mouse_speed = 10.0
      self.rotate_view(camera, -mouse_dy * mouse_speed, -mouse_dx * mouse_speed)








  def update_walk_fps(self, camera, dt):

    # OSX platform.
    KC_UP = 200
    KC_LEFT = 203
    KC_RIGHT = 205
    KC_DOWN = 208
    
    """
    # Override for Windows plateform.
    if (gh_utils.get_platform() == 1) then
      KC_UP = 72
      KC_DOWN = 80
      KC_RIGHT = 77
      KC_LEFT = 75
    end
    """

    KC_W = 17
    KC_S = 31
    KC_A = 30
    KC_D = 32
    KC_R = 19
    KC_F = 33
    

    cam_dist = self._keyboard_speed * dt
    if ((gh_input.keyboard_is_key_down(KC_W) == 1) or (gh_input.keyboard_is_key_down(KC_UP) == 1)):
      self.move_along_view_xz(camera, cam_dist)
      self._is_moving = 1
    
    if ((gh_input.keyboard_is_key_down(KC_S) == 1) or (gh_input.keyboard_is_key_down(KC_DOWN) == 1)):
      self.move_along_view_xz(camera, -cam_dist)
      self._is_moving = 1

    if ((gh_input.keyboard_is_key_down(KC_A) == 1) or (gh_input.keyboard_is_key_down(KC_LEFT) == 1)):
      self.strafe_h_xz(camera, cam_dist, 0)
      self._is_moving = 1

    if ((gh_input.keyboard_is_key_down(KC_D) == 1) or (gh_input.keyboard_is_key_down(KC_RIGHT) == 1)):
      self.strafe_h_xz(camera, -cam_dist, 0)
      self._is_moving = 1
    
    if (gh_input.keyboard_is_key_down(KC_R) == 1):
      self.strafe_v_v2(camera, cam_dist, 0)
      self._is_moving = 1

    if (gh_input.keyboard_is_key_down(KC_F) == 1):
      self.strafe_v_v2(camera, -cam_dist, 0)
      self._is_moving = 1
    
    
    self._do_rotate = 1
    

    if (self._do_rotate == 1):
      mouse_dx = 0
      mouse_dy = 0
      
      if (gh_utils.get_platform() == 2): # return 1 if Windows, 2 if osx, 3 if linux, 4 if rpi and 5 if tinker boarb
      
        mouse_dx, mouse_dy = gh_input.mouse_get_position_delta()
        gh_input.mouse_reset_position_delta()

        mouse_dx = mouse_dx * _PI_OVER_180
        mouse_dy = mouse_dy * _PI_OVER_180
        
        gh_input.mouse_set_position(winW/2, winH/2)

      else:
        mx, my = gh_input.mouse_get_position_v2()

        mouse_dx = (mx - winW/2) * _PI_OVER_180
        mouse_dy =(my - winH/2) * _PI_OVER_180
        
        gh_input.mouse_set_position(winW/2, winH/2)
        
        self._old_mouse_x = mx
        self._old_mouse_y = my

      
      mouse_speed = 10.0
      self.rotate_view(camera, -mouse_dy * mouse_speed, -mouse_dx * mouse_speed)
      









  def set_orbit_lookat(self, camera, lookat_x, lookat_y, lookat_z):
    self._lookat_x = lookat_x
    self._lookat_y = lookat_y
    self._lookat_z = lookat_z
    gh_camera.set_lookat(camera, lookat_x, lookat_y, lookat_z, 1.0)



  def update_orbit(self, camera, dt, lookat_x, lookat_y, lookat_z):
    LEFT_BUTTON = 1
    self.update_orbit_v2(camera, dt, lookat_x, lookat_y, lookat_z, LEFT_BUTTON)




  def update_orbit_v2(self, camera, dt, lookat_x, lookat_y, lookat_z, mouse_button):
    # OSX platform.
    KC_UP = 200
    KC_LEFT = 203
    KC_RIGHT = 205
    KC_DOWN = 208

    KC_W = 17
    KC_S = 31
    KC_A = 30
    KC_D = 32

    wheel_delta = gh_input.mouse_get_wheel_delta()
    self._mouse_wheel_delta = wheel_delta
    gh_input.mouse_reset_wheel_delta()
    
    distance = self._keyboard_speed * dt * 10
    
    if ((wheel_delta > 0) or (gh_input.keyboard_is_key_down(KC_W) == 1) or (gh_input.keyboard_is_key_down(KC_UP) == 1)):
      self.move_along_view(camera, distance)

    if ((wheel_delta < 0) or (gh_input.keyboard_is_key_down(KC_S) == 1) or (gh_input.keyboard_is_key_down(KC_DOWN) == 1)):
      self.move_along_view(camera, -distance)
    
    self.orbit_v2(camera, lookat_x, lookat_y, lookat_z, mouse_button)







  def update_move_along_view(self, camera, dt):
    KC_UP = 200
    KC_LEFT = 203
    KC_RIGHT = 205
    KC_DOWN = 208

    KC_W = 17
    KC_S = 31
    KC_A = 30
    KC_D = 32

    
    wheel_delta = gh_input.mouse_get_wheel_delta()
    self._mouse_wheel_delta = wheel_delta
    gh_input.mouse_reset_wheel_delta()
    
    distance = self._keyboard_speed * dt * 10
    
    if ((wheel_delta > 0) or (gh_input.keyboard_is_key_down(KC_W) == 1) or (gh_input.keyboard_is_key_down(KC_UP) == 1)):
      self.move_along_view(camera, distance)

    if ((wheel_delta < 0) or (gh_input.keyboard_is_key_down(KC_S) == 1) or (gh_input.keyboard_is_key_down(KC_DOWN) == 1)):
      self.move_along_view(camera, -distance)






  def update(self, camera, dt):

    # Windows platform.
    if (gh_utils.get_platform() == 1):
      gh_window.keyboard_update_buffer(0)
  

    self._is_moving = 0
    # PAN with [CTRL] + mouse right click
    #self.pan(camera, self._pan_speed_factor)

    self._do_pan = 0
    
    if (self._do_pan == 0):
      if (self._mode == self._MODE_ORBIT):
        self.update_orbit(camera, dt, self._lookat_x, self._lookat_y, self._lookat_z)  
      
      elif (self._mode == self._MODE_FLY):
        self.update_fly(camera, dt)  
        
      elif (self._mode == self._MODE_FPS):
        self.update_walk_fps(camera, dt)  
      






  def update_v2(self, camera, dt):

    # Windows platform.
    if (gh_utils.get_platform() == 1):
      gh_window.keyboard_update_buffer(0)
  
    self._is_moving = 0


    RIGHT_BUTTON = 2
    speed_factor = self._keyboard_speed * 0.5 * dt
    # PAN with mouse right click only
    self.pan2(camera, speed_factor, RIGHT_BUTTON)


    
    if (self._do_pan == 0):
      if (self._mode == self._MODE_ORBIT):
        self.update_orbit(camera, dt, self._lookat_x, self._lookat_y, self._lookat_z)  
      
      elif (self._mode == self._MODE_FLY):
        self.update_fly(camera, dt)  
        
      elif (self._mode == self._MODE_FPS):
        self.update_walk_fps(camera, dt)  
      



  def draw_tripod(self, camera):
    gh_utils.tripod_visualizer_camera_render(camera, 0, 0, 100, 100)




  def draw_ref_grid(self, x_size, z_size, x_subdiv, z_subdiv):
    grid = self._grid
    if (grid == 0):
      grid = gh_utils.grid_create()
      self._grid = grid
      
      gh_utils.grid_set_lines_color(grid, 0.7, 0.7, 0.7, 1.0)
      gh_utils.grid_set_main_lines_color(grid, 1.0, 1.0, 0.0, 1.0)
      gh_utils.grid_set_main_x_axis_color(grid, 1.0, 0.0, 0.0, 1.0)
      gh_utils.grid_set_main_z_axis_color(grid, 0.0, 0.0, 1.0, 1.0)
      display_main_lines = 1
      display_lines = 1
      gh_utils.grid_set_display_lines_options(grid, display_main_lines, display_lines)
      
      grid_program_vs_gl3=" \
      #version 150 \
      in vec4 gxl3d_Position; \
      in vec4 gxl3d_Color; \
      uniform mat4 gxl3d_ModelViewProjectionMatrix; \
      out vec4 Vertex_Color; \
      void main() \
      { \
        gl_Position = gxl3d_ModelViewProjectionMatrix * gxl3d_Position; \
        Vertex_Color = gxl3d_Color; \
      }"

      grid_program_ps_gl3=" \
      #version 150 \
      in vec4 Vertex_Color; \
      out vec4 FragColor; \
      void main (void) \
      { \
        FragColor = Vertex_Color;  \
      }"
      
      grid_program_vs_gl2=" \
      #version 120 \
      uniform mat4 gxl3d_ModelViewProjectionMatrix; \
      varying vec4 Vertex_Color; \
      void main() \
      { \
        gl_Position = gxl3d_ModelViewProjectionMatrix * gl_Vertex; \
        Vertex_Color = gl_Color; \
      }"

      grid_program_ps_gl2=" \
      #version 120 \
      varying vec4 Vertex_Color; \
      void main (void) \
      { \
        gl_FragColor = Vertex_Color;  \
      }"

      grid_program_vs_gles2=" \
      attribute vec4 gxl3d_Position; \
      attribute vec4 gxl3d_Color; \
      uniform mat4 gxl3d_ModelViewProjectionMatrix; \
      varying vec4 Vertex_Color; \
      void main() \
      { \
        gl_Position = gxl3d_ModelViewProjectionMatrix * gxl3d_Position; \
        Vertex_Color = gxl3d_Color; \
      }"

      grid_program_ps_gles2=" \
      varying highp vec4 Vertex_Color; \
      void main (void) \
      { \
        gl_FragColor = Vertex_Color;  \
      }"

      if (gh_renderer.is_opengl_es() == 1):
        self._grid_program = gh_gpu_program.create(grid_program_vs_gles2, grid_program_ps_gles2)
      else:
        if (gh_renderer.get_api_version_major() >= 3):
          self._grid_program = gh_gpu_program.create(grid_program_vs_gl3, grid_program_ps_gl3)
        else:
          self._grid_program = gh_gpu_program.create(grid_program_vs_gl2, grid_program_ps_gl2)


    gh_utils.grid_set_geometry_params(grid, x_size, z_size, x_subdiv, z_subdiv)
    gh_gpu_program.bind(self._grid_program)
    gh_object.render(grid)

