3
votes

My objective is to import a terrain map, more specific a GeoTIFF file, and assign colors or textures to the terrain. I also want to be able to fly through the terrain using key inputs and mouse movement.

Input data:

  1. self.vertices = numpy.array(vertices, dtype = 'f'): vertices = (x, y, z, r, g, b, tc1, tc2)

  2. self.indices = numpy.array(triangle, dtype = numpy.int32): triangle = (indices for vertices)

I assigned (1, 0, 0) for all the vertex colors and (0, 1) for texture coordinates. This is just to see whether the color change to red.

What I have done:

I already managed to create a terrain using a vertex array object (VAO) which contains a vertex buffer object (VBO) and assigned attributes for the vertex positions, colors and texture (not yet assigned). I also managed to set up the environment to be able to fly through the terrain using key inputs and mouse movement. By running the code below one should be able to see and fly around a small grey mesh with black triangle outlines.

from pygame.constants import *
from OpenGL.GLU import *
import pygame
import numpy
numpy.set_printoptions(suppress=True)
from OpenGL.GL import *
from OpenGL.GL.shaders import compileProgram, compileShader

class Meshi():
    def __init__(self):
        #Gert vertices and triangles
        if not pygame.init():
            raise TypeError('Unable to initalize pygame')
        #Get vertices and triangles
        vertices, triangles = self.getVertTri()
        z_scale = 0.00005

        #change z-scale
        for i in range(0, len(vertices)):
            if vertices[i][2] < 0:
                vertices[i][2] = 0
            else:
                vertices[i][2] = vertices[i][2] * z_scale

        #Add colour and texture coordinates to mesh
        vert = []
        color_in = [1.0, 0.0, 0.0, 0.0, 1.0]
        for ver in vertices:
            vert.append(ver + color_in)

        vertices = vert

        self.vertices = numpy.array(vertices, dtype = 'f')
        self.indices = numpy.array(triangles,dtype = numpy.int32)
        #Run pygame
        self.pygame_view()

    # Function that keeps all the vertices and triangle sides
    def getVertTri(self):
        vert_str=[[18.427083,-34.104583,14.097592],[18.427917,-34.104583,13.813885],[18.42875,-34.104583,11.798318],[18.429583,-34.104583,12.170123],[18.430417,-34.104583,13.23494],[18.43125,-34.104583,12.263627],[18.432083,-34.104583,10.543183],[18.432917,-34.104583,9.150859],[18.43375,-34.104583,7.531989],[18.434583,-34.104583,5.370541],[18.435417,-34.104583,4.947286],
                    [18.43625,-34.104583,4.369431],[18.437083,-34.104583,9.29184],[18.437917,-34.104583,11.300546],[18.43875,-34.104583,9.590596],[18.439583,-34.104583,7.006906],[18.427083,-34.105417,17.277378],[18.427917,-34.105417,12.070671],[18.42875,-34.105417,9.0447],[18.429583,-34.105417,7.853086],[18.430417,-34.105417,7.927739],[18.43125,-34.105417,8.325415],
                    [18.432083,-34.105417,8.505657],[18.432917,-34.105417,7.904904],[18.43375,-34.105417,7.7015],[18.434583,-34.105417,7.362363],[18.435417,-34.105417,7.08594],[18.43625,-34.105417,6.692467],[18.437083,-34.105417,1.177889],[18.437917,-34.105417,6.393278],[18.43875,-34.105417,8.610809],[18.439583,-34.105417,9.748986],[18.427083,-34.10625,29.354589],
                    [18.427917,-34.10625,24.616703],[18.42875,-34.10625,18.529903],[18.429583,-34.10625,13.677365],[18.430417,-34.10625,10.466432],[18.43125,-34.10625,9.469019],[18.432083,-34.10625,7.960031],[18.432917,-34.10625,7.115651],[18.43375,-34.10625,6.465553],[18.434583,-34.10625,5.50102],[18.435417,-34.10625,5.712739],[18.43625,-34.10625,6.946383],
                    [18.437083,-34.10625,4.263526],[18.437917,-34.10625,2.732419],[18.43875,-34.10625,5.488115],[18.439583,-34.10625,6.902328],[18.427083,-34.107083,30.979712],[18.427917,-34.107083,30.684374],[18.42875,-34.107083,26.90592],[18.429583,-34.107083,22.378777],[18.430417,-34.107083,19.191908],[18.43125,-34.107083,16.608807],[18.432083,-34.107083,13.171669],
                    [18.432917,-34.107083,10.77886],[18.43375,-34.107083,11.478356],[18.434583,-34.107083,11.300546],[18.435417,-34.107083,9.737173],[18.43625,-34.107083,6.484625],[18.437083,-34.107083,5.185645],[18.437917,-34.107083,3.770918],[18.43875,-34.107083,4.11032],[18.439583,-34.107083,4.947286],[18.427083,-34.107917,30.013678],[18.427917,-34.107917,32.894321],
                    [18.42875,-34.107917,31.857147],[18.429583,-34.107917,26.9683],[18.430417,-34.107917,21.895006],[18.43125,-34.107917,16.907623],[18.432083,-34.107917,12.721774],[18.432917,-34.107917,11.510896],[18.43375,-34.107917,14.044291],[18.434583,-34.107917,13.438006],[18.435417,-34.107917,12.538547],[18.43625,-34.107917,11.562454],[18.437083,-34.107917,9.263826],
                    [18.437917,-34.107917,6.071107],[18.43875,-34.107917,4.289905],[18.439583,-34.107917,4.057948],[18.427083,-34.10875,25.226519],[18.427917,-34.10875,26.609413],[18.42875,-34.10875,29.121534],[18.429583,-34.10875,27.900227],[18.430417,-34.10875,24.414494],[18.43125,-34.10875,20.20229],[18.432083,-34.10875,14.686948],[18.432917,-34.10875,10.620943],
                    [18.43375,-34.10875,11.136961],[18.434583,-34.10875,9.067697],[18.435417,-34.10875,9.031256],[18.43625,-34.10875,11.842289],[18.437083,-34.10875,14.412585],[18.437917,-34.10875,14.026602],[18.43875,-34.10875,10.77886],[18.439583,-34.10875,7.479022],[18.427083,-34.109583,24.316551],[18.427917,-34.109583,4.027079],[18.42875,-34.109583,13.726677],
                    [18.429583,-34.109583,23.883642],[18.430417,-34.109583,28.826719],[18.43125,-34.109583,29.248907],[18.432083,-34.109583,24.62047],[18.432917,-34.109583,14.576126],[18.43375,-34.109583,10.270979],[18.434583,-34.109583,9.1116],[18.435417,-34.109583,8.978557],[18.43625,-34.109583,10.877857],[18.437083,-34.109583,13.952817],[18.437917,-34.109583,17.318939],
                    [18.43875,-34.109583,18.3776],[18.439583,-34.109583,16.823097],[18.427083,-34.110417,31.066404],[18.427917,-34.110417,27.124994],[18.42875,-34.110417,14.584773],[18.429583,-34.110417,6.256494],[18.430417,-34.110417,14.902382],[18.43125,-34.110417,23.147758],[18.432083,-34.110417,26.993055],[18.432917,-34.110417,22.765322],[18.43375,-34.110417,17.905703],
                    [18.434583,-34.110417,13.544991],[18.435417,-34.110417,11.899844],[18.43625,-34.110417,14.348012],[18.437083,-34.110417,16.262051],[18.437917,-34.110417,17.542927],[18.43875,-34.110417,21.157032],[18.439583,-34.110417,23.491091],[18.427083,-34.11125,12.909849],[18.427917,-34.11125,20.651325],[18.42875,-34.11125,24.733046],[18.429583,-34.11125,22.71829],
                    [18.430417,-34.11125,12.409415],[18.43125,-34.11125,4.794024],[18.432083,-34.11125,14.560322],[18.432917,-34.11125,19.986332],[18.43375,-34.11125,21.261787],[18.434583,-34.11125,18.94504],[18.435417,-34.11125,17.467054],[18.43625,-34.11125,17.029078],[18.437083,-34.11125,15.266393],[18.437917,-34.11125,14.918492],[18.43875,-34.11125,16.444849],
                    [18.439583,-34.11125,18.332243],[18.427083,-34.112083,14.870467],[18.427917,-34.112083,10.946778],[18.42875,-34.112083,9.536969],[18.429583,-34.112083,17.428055],[18.430417,-34.112083,20.116079],[18.43125,-34.112083,14.013614],[18.432083,-34.112083,4.487359],[18.432917,-34.112083,4.302343],[18.43375,-34.112083,9.771387],[18.434583,-34.112083,11.65025],
                    [18.435417,-34.112083,11.8523],[18.43625,-34.112083,10.677254],[18.437083,-34.112083,8.148099],[18.437917,-34.112083,7.58765],[18.43875,-34.112083,5.033905],[18.439583,-34.112083,3.151809],[18.427083,-34.112917,4.697855],[18.427917,-34.112917,5.462211],[18.42875,-34.112917,6.136745],[18.429583,-34.112917,6.001783],[18.430417,-34.112917,9.035099],
                    [18.43125,-34.112917,13.059889],[18.432083,-34.112917,13.085486],[18.432917,-34.112917,11.08321],[18.43375,-34.112917,6.319217],[18.434583,-34.112917,4.270484],[18.435417,-34.112917,4.372147],[18.43625,-34.112917,4.910031],[18.437083,-34.112917,4.54907],[18.437917,-34.112917,3.606416],[18.43875,-34.112917,6.188358],[18.439583,-34.112917,10.144054],
                    [18.427083,-34.11375,8.589212],[18.427917,-34.11375,8.18161],[18.42875,-34.11375,7.444539],[18.429583,-34.11375,6.263077],[18.430417,-34.11375,5.025665],[18.43125,-34.11375,4.281593],[18.432083,-34.11375,4.90037],[18.432917,-34.11375,9.216936],[18.43375,-34.11375,10.723666],[18.434583,-34.11375,11.447696],[18.435417,-34.11375,11.056483],
                    [18.43625,-34.11375,10.136135],[18.437083,-34.11375,6.79003],[18.437917,-34.11375,2.968766],[18.43875,-34.11375,1.72892],[18.439583,-34.11375,6.459183]]
        tri_str = [[0,16,17],[0,17,1],[1,17,18],[1,18,2],[2,18,19],[2,19,3],[3,19,20],[3,20,4],[4,20,21],[4,21,5],[5,21,22],
                    [5,22,6],[6,22,23],[6,23,7],[7,23,24],[7,24,8],[8,24,25],[8,25,9],[9,25,26],[9,26,10],[10,26,27],[10,27,11],
                    [11,27,28],[11,28,12],[12,28,29],[12,29,13],[13,29,30],[13,30,14],[14,30,31],[14,31,15],[16,32,33],[16,33,17],[17,33,34],
                    [17,34,18],[18,34,35],[18,35,19],[19,35,36],[19,36,20],[20,36,37],[20,37,21],[21,37,38],[21,38,22],[22,38,39],[22,39,23],
                    [23,39,40],[23,40,24],[24,40,41],[24,41,25],[25,41,42],[25,42,26],[26,42,43],[26,43,27],[27,43,44],[27,44,28],[28,44,45],
                    [28,45,29],[29,45,46],[29,46,30],[30,46,47],[30,47,31],[32,48,49],[32,49,33],[33,49,50],[33,50,34],[34,50,51],[34,51,35],
                    [35,51,52],[35,52,36],[36,52,53],[36,53,37],[37,53,54],[37,54,38],[38,54,55],[38,55,39],[39,55,56],[39,56,40],[40,56,57],
                    [40,57,41],[41,57,58],[41,58,42],[42,58,59],[42,59,43],[43,59,60],[43,60,44],[44,60,61],[44,61,45],[45,61,62],[45,62,46],
                    [46,62,63],[46,63,47],[48,64,65],[48,65,49],[49,65,66],[49,66,50],[50,66,67],[50,67,51],[51,67,68],[51,68,52],[52,68,69],
                    [52,69,53],[53,69,70],[53,70,54],[54,70,71],[54,71,55],[55,71,72],[55,72,56],[56,72,73],[56,73,57],[57,73,74],[57,74,58],
                    [58,74,75],[58,75,59],[59,75,76],[59,76,60],[60,76,77],[60,77,61],[61,77,78],[61,78,62],[62,78,79],[62,79,63],[64,80,81],
                    [64,81,65],[65,81,82],[65,82,66],[66,82,83],[66,83,67],[67,83,84],[67,84,68],[68,84,85],[68,85,69],[69,85,86],[69,86,70],
                    [70,86,87],[70,87,71],[71,87,88],[71,88,72],[72,88,89],[72,89,73],[73,89,90],[73,90,74],[74,90,91],[74,91,75],[75,91,92],
                    [75,92,76],[76,92,93],[76,93,77],[77,93,94],[77,94,78],[78,94,95],[78,95,79],[80,96,97],[80,97,81],[81,97,98],[81,98,82],
                    [82,98,99],[82,99,83],[83,99,100],[83,100,84],[84,100,101],[84,101,85],[85,101,102],[85,102,86],[86,102,103],[86,103,87],[87,103,104],
                    [87,104,88],[88,104,105],[88,105,89],[89,105,106],[89,106,90],[90,106,107],[90,107,91],[91,107,108],[91,108,92],[92,108,109],[92,109,93],
                    [93,109,110],[93,110,94],[94,110,111],[94,111,95],[96,112,113],[96,113,97],[97,113,114],[97,114,98],[98,114,115],[98,115,99],[99,115,116],
                    [99,116,100],[100,116,117],[100,117,101],[101,117,118],[101,118,102],[102,118,119],[102,119,103],[103,119,120],[103,120,104],[104,120,121],[104,121,105],
                    [105,121,122],[105,122,106],[106,122,123],[106,123,107],[107,123,124],[107,124,108],[108,124,125],[108,125,109],[109,125,126],[109,126,110],[110,126,127],
                    [110,127,111],[112,128,129],[112,129,113],[113,129,130],[113,130,114],[114,130,131],[114,131,115],[115,131,132],[115,132,116],[116,132,133],[116,133,117],
                    [117,133,134],[117,134,118],[118,134,135],[118,135,119],[119,135,136],[119,136,120],[120,136,137],[120,137,121],[121,137,138],[121,138,122],[122,138,139],
                    [122,139,123],[123,139,140],[123,140,124],[124,140,141],[124,141,125],[125,141,142],[125,142,126],[126,142,143],[126,143,127],[128,144,145],[128,145,129],
                    [129,145,146],[129,146,130],[130,146,147],[130,147,131],[131,147,148],[131,148,132],[132,148,149],[132,149,133],[133,149,150],[133,150,134],[134,150,151],
                    [134,151,135],[135,151,152],[135,152,136],[136,152,153],[136,153,137],[137,153,154],[137,154,138],[138,154,155],[138,155,139],[139,155,156],[139,156,140],
                    [140,156,157],[140,157,141],[141,157,158],[141,158,142],[142,158,159],[142,159,143],[144,160,161],[144,161,145],[145,161,162],[145,162,146],[146,162,163],
                    [146,163,147],[147,163,164],[147,164,148],[148,164,165],[148,165,149],[149,165,166],[149,166,150],[150,166,167],[150,167,151],[151,167,168],[151,168,152],
                    [152,168,169],[152,169,153],[153,169,170],[153,170,154],[154,170,171],[154,171,155],[155,171,172],[155,172,156],[156,172,173],[156,173,157],[157,173,174],
                    [157,174,158],[158,174,175],[158,175,159],[160,176,177],[160,177,161],[161,177,178],[161,178,162],[162,178,179],[162,179,163],[163,179,180],[163,180,164],
                    [164,180,181],[164,181,165],[165,181,182],[165,182,166],[166,182,183],[166,183,167],[167,183,184],[167,184,168],[168,184,185],[168,185,169],[169,185,186],
                    [169,186,170],[170,186,187],[170,187,171],[171,187,188],[171,188,172],[172,188,189],[172,189,173],[173,189,190],[173,190,174],[174,190,191],[174,191,175]]

        return vert_str, tri_str

    # Game movement
    def key_pressed(self, keypress):
        if keypress[pygame.K_w]:
            glTranslatef(0, 0, 0.001)
        if keypress[pygame.K_s]:
            glTranslatef(0, 0, -0.001)
        if keypress[pygame.K_d]:
            glTranslatef(-0.00025, 0, 0)
        if keypress[pygame.K_a]:
            glTranslatef(0.00025, 0, 0)
        if keypress[pygame.K_e]:
            glTranslatef(0, 0.0004, 0)
        if keypress[pygame.K_q]:
            glTranslatef(0, -0.0004, 0)
                         
    def draw_mesh_vbo(self):
        vertex_src = """
        # version 330
        in layout(location = 0) vec3 a_position;
        in layout(location = 1) vec3 a_color;
        
        out vec3 v_color;
    
        void main()
        {
            gl_Position = vec4(a_position, 1.0);
            v_color = a_color;

        }
        """
        fragment_src = """
        # version 330
        in vec3 v_color;
 
        out vec4 out_color;
        void main()
        {
            out_color = vec4(v_color, 1.0);
        }
        """
        shader = compileProgram(compileShader(vertex_src, GL_VERTEX_SHADER), compileShader(fragment_src, GL_FRAGMENT_SHADER))

        self.ter_vao = glGenVertexArrays(1)
        glBindVertexArray(self.ter_vao)
        VBO = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, VBO)
        glBufferData(GL_ARRAY_BUFFER, self.vertices.itemsize * len(self.vertices) * 8, self.vertices, GL_STATIC_DRAW)
        EBO = glGenBuffers(1)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.indices.itemsize * len(self.indices) * 3, self.indices, GL_STATIC_DRAW)
        #pos = glGetAttribLocation(shader, "a_position")
        # position
        glEnableVertexAttribArray(0)
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, self.vertices.itemsize * 8, ctypes.c_void_p(0))
        # color
        #col = glGetAttribLocation(shader, "a_color")
        glEnableVertexAttribArray(1)
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, self.vertices.itemsize * 8, ctypes.c_void_p(12))
        # texture
        #textr = glGetAttribLocation(shader, "a_text")
        # glEnableVertexAttribArray(2)
        # glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, self.vertices.itemsize * 8, ctypes.c_void_p(24))
        glBindVertexArray(0)

        self.shady = shader
        glClearColor(0, 0.3, 0.1, 1)

    def pygame_loop(self, run, paused, displayCenter, up_down_angle, viewMatrix):
        clock = pygame.time.Clock()
        self.draw_mesh_vbo()

        #glUseProgram(self.shader)
        while run:
            clock.tick(100)
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    run = False
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE or event.key == pygame.K_RETURN:
                        run = False
                    if event.key == pygame.K_PAUSE or event.key == pygame.K_p:
                        paused = not paused
                        pygame.mouse.set_pos(displayCenter)
                if not paused:
                    if event.type == pygame.MOUSEMOTION:
                        mouseMove = [event.pos[i] - displayCenter[i] for i in range(2)]
                    pygame.mouse.set_pos(displayCenter)

            if not paused:
                # get keys
                keypress = pygame.key.get_pressed()
                # init model view matrix
                glLoadIdentity()
                # apply the look up and down
                up_down_angle += mouseMove[1] * 0.1
                glRotatef(up_down_angle, 1.0, 0.0, 0.0)
                # init the view matrix
                glPushMatrix()
                glLoadIdentity()
                # apply the movment
                self.key_pressed(keypress)
                # apply the left and right rotation
                glRotatef(mouseMove[0] * 0.02, 0.0, 1.0, 0.0)


                glMultMatrixf(viewMatrix)
                viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)
                glPopMatrix()
                # Close init the view matrix
                # Update current view
                glMultMatrixf(viewMatrix)
                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)


                glBindVertexArray(self.ter_vao)
                #glUseProgram(self.shady)
                glColor3f(0,0,0)
                glDrawElements(GL_LINES, len(self.indices) * 3, GL_UNSIGNED_INT, None)
                glColor4f(0.8, 0.8, 0.8, 0.0)
                glDrawElements(GL_TRIANGLES, len(self.indices) * 3, GL_UNSIGNED_INT, None)
                #glUseProgram(0)
                glBindVertexArray(0)


                pygame.display.set_caption("FPS: %.2f" % clock.get_fps())
                pygame.display.flip()
                pygame.time.wait(20)

    def pygame_view(self):
        #Pygame Start
        pygame.init()
        #Create a "first person" environment
        display = (1000, 600)
        scree = pygame.display.set_mode(display, DOUBLEBUF | OPENGL)
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_LIGHTING)
        glShadeModel(GL_SMOOTH)
        glEnable(GL_COLOR_MATERIAL)
        glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE)
        glEnable(GL_LIGHT0)
        glLightfv(GL_LIGHT0, GL_AMBIENT, [0.5, 0.5, 0.5, 1])
        glLightfv(GL_LIGHT0, GL_DIFFUSE, [1.0, 1.0, 1.0, 1])
        sphere = gluNewQuadric()

        glMatrixMode(GL_PROJECTION)
        gluPerspective(15, (1 * display[0] / display[1]), 0.001, 3000.0)
        glMatrixMode(GL_MODELVIEW)
        gluLookAt(self.vertices[50][0], self.vertices[50][1], 0, 0, -180, 0, 0, 0, 1)
        viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX)
        glLoadIdentity()

        # init mouse movement and center mouse on screen
        displayCenter = [scree.get_size()[i] // 2 for i in range(2)]
        mouseMove = [0, 0]
        pygame.mouse.set_pos(displayCenter)

        up_down_angle = 0.0
        paused = False
        run = True

        self.aspectratio = (1 * display[0] / display[1])
        #Run Pygame

        self.pygame_loop(run, paused, displayCenter, up_down_angle, viewMatrix)

        pygame.quit()

if __name__ == '__main__':
    mesh_dem = Meshi()

Next step:

Before I even try to implement textures, I first want to understand how shaders work by displaying the colors assigned to each vertex. The expected outcome in this example is to see a red mesh.

What I have tried:

Shader setup:

vertex_src = """
        # version 330
        in layout(location = 0) vec3 a_position;
        in layout(location = 1) vec3 a_color;
        out vec3 v_color;  
        void main()
        {
            gl_Position = vec4(a_position, 1.0);
            v_color = a_color;
        }
        """
        fragment_src = """
        # version 330
        in vec3 v_color;  
        out vec4 out_color;
        void main()
        {
            out_color = vec4(v_color, 1.0);
        }
        """
        shader = compileProgram(compileShader(vertex_src, GL_VERTEX_SHADER), compileShader(fragment_src, GL_FRAGMENT_SHADER))

        self.shady = shader

Implementing shader in while loop:

glBindVertexArray(self.ter_vao)
glUseProgram(self.shady)
glDrawElements(GL_TRIANGLES, len(self.indices) * 3, GL_UNSIGNED_INT, None)
glUseProgram(0)
glBindVertexArray(0)

When I use glUseProgram(shader) I only see the background color. I suspect it is becuase glUseProgram disables the model view, projections etc. Do I have to change the shaders to include the projections, place glUseProgram somewhere else or am I way off?

UPDATE: Thanks to Rabbid76 and Atilla Toth's OpenGL in Python Youtube series I created a workable programmable pipeline example (see answer below). I have not implemented GLM's matrix transformations yet but for now pyrr works fine. The example creates a mesh from coordinates and displays different colors based on height.

2
Try multiplying the positions by gl_ModelViewProjectionMatrix in the shader (I never remember whether the vertex or the matrix needs to come first in the multiplication, try both).user253751
Thanks user253751. It works by multiplying the matrix first and then the vertex. Although, as mentioned by Rabbid76 in the answer, I have to change the shader's version to 120 and is a old why of doing it.Kempie

2 Answers

4
votes

If you use a shader program, then the matrix transformations have to be done in the vertex shader.

The Legacy OpenGL matrix stack is deprecated. If you use a shader program and the legacy matrices, then you have to step back to a OpenGL Shading Language 1.20 (#version 120) shader.
In this version the built in uniforms like gl_ModelViewProjectionMatrix can be used. See Lighthouse3d.com - Hello World in GLSL.

e.g.:

# version 120
attribute vec3 a_position;
attribute vec3 a_color;

varying vec3 v_color;

void main()
{
    gl_Position = gl_ModelViewProjectionMatrix * vec4(a_position, 1.0);
    v_color = a_color;
}

But that is not "modern" OpenGL.


In a state of the art implementation, matrix Uniform variables are used:

# version 330
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_color;

out vec3 v_color;

uniform mat4 u_projection;
uniform mat4 u_view;
uniform mat4 u_model;

void main()
{
    gl_Position = u_projection * u_view * u_model * vec4(a_position, 1.0);
    v_color = a_color;
}

the location index glGetUniformLocation of the uniform can be get by and the value of the matrix uniform can be set by glUniformMatrix4fv.
Use library like PyGLM (this is a python wrapper for OpenGL Mathematics (GLM)), to compute the matrices.
Of course it is possible to compute the matrices with numpy.matrix, too. But this is a bit more tricky.

1
votes

Thanks to Rabbid76 and Atilla Toth's OpenGL in Python Youtube series I created a workable programmable pipeline example. I have not implemented GLM's matrix transformations yet but for now pyrr works fine. The example creates a mesh from coordinates and displays different colors based on height. You will also be able to fly around using the mouse, w,s a,d,q,e keys.

import numpy
import pandas
import glfw
from OpenGL.GL import *
from OpenGL.GL.shaders import compileProgram, compileShader
import pyrr
import glm
from pyrr import  matrix44
from math import sin, cos, radians



class threeD_viewer():
    def __init__(self, indi, tria, z_scale, x_st, y_st):

        self.cam = Camera(x_st, y_st)
        self.WIDTH, self.HEIGHT = 1280, 720
        self.lastX, self.LastY = self.WIDTH / 2, self.HEIGHT / 2
        self.first_mouse = True
        self.forward, self.backward, self.right, self.left, self.up, self.down = False, False, False, False, False, False

        self.x_start, self.y_start = x_st, y_st

        vertices = indi
        triangles = tria

        z_values = []
        for i in range(0, len(vertices)):
            if vertices[i][2] < 0:
                vertices[i][2] = 0
                z_values.append(vertices[i][2])
            else:
                vertices[i][2] = vertices[i][2] * z_scale
                z_values.append(vertices[i][2])
            temp_z = vertices[i][2]
            vertices[i][2] = vertices[i][1]
            vertices[i][1] = temp_z

        # Create a colour scheme to be based on height of the z axis
        self.color_theme_1 = [[0.000, 0.000, 0.804], [0.678, 1.000, 0.184], [0.000, 1.000, 0.000],
                              [0.196, 0.804, 0.196], [1.000, 0.843, 0.000], [0.000, 0.392, 0.000]
                            , [0.804, 0.361, 0.361], [0.333, 0.420, 0.184]]

        # Create heigth groups based on height quantiles
        z_df = pandas.DataFrame(z_values, columns=["z_value"])
        z_quant = z_df[z_df.z_value != 0].z_value.quantile([0.1, 0.3, 0.5, 0.75, 0.85, 0.9, 0.95, 0.98])
        z_quant = z_quant.tolist()

        # Add colour and texture to vertices
        vert = []
        for ver in vertices:
            if ver[1] == 0:
                color_in = self.color_theme_1[0] + [1.0, 0.0]
            elif ver[1] < z_quant[0]:
                color_in = self.color_theme_1[1] + [1.0, 0.0]
            elif ver[1] < z_quant[1]:
                color_in = self.color_theme_1[2] + [1.0, 0.0]
            elif ver[1] < z_quant[2]:
                color_in = self.color_theme_1[3] + [1.0, 0.0]
            elif ver[1] < z_quant[3]:
                color_in = self.color_theme_1[4] + [1.0, 0.0]
            elif ver[1] < z_quant[4]:
                color_in = self.color_theme_1[5] + [1.0, 0.0]
            elif ver[1] < z_quant[5]:
                color_in = self.color_theme_1[6] + [1.0, 0.0]
            elif ver[1] < z_quant[6]:
                color_in = self.color_theme_1[7] + [1.0, 0.0]
            else:
                vertices[i][1] = z_quant[6]
                color_in = [0.502, 0.000, 0.000, 0.0, 0.1]
            vert.append(ver + color_in)
        # print(vert)
        vertices = vert

        self.vertices = numpy.array(vertices, dtype='f')
        self.indices = numpy.array(triangles, dtype=numpy.int32)

        self.vertex_src = """
        # version 330

        in vec3 a_position;
        in vec3 a_color;

        uniform mat4 model;
        uniform mat4 projection;
        uniform mat4 view;

        out vec3 v_color;

        void main()
        {
            gl_Position = projection* view* vec4(a_position, 1.0);
            v_color = a_color;
        }
        """

        self.fragment_src = """
        # version 330

        in vec3 v_color;
        out vec4 out_color;

        void main()
        {
            out_color = vec4(v_color, 1.0);
        }
        """
        self.main()

    # the keyboard input callback
    def key_input_clb(self, window, key, scancode, action, mode):
        if key == glfw.KEY_ESCAPE and action == glfw.PRESS:
            glfw.set_window_should_close(window, True)

        if key == glfw.KEY_W and action == glfw.PRESS:
            self.forward = True
        elif key == glfw.KEY_W and action == glfw.RELEASE:
            self.forward = False
        if key == glfw.KEY_S and action == glfw.PRESS:
            self.backward = True
        elif key == glfw.KEY_S and action == glfw.RELEASE:
            self.backward = False
        if key == glfw.KEY_A and action == glfw.PRESS:
            self.left = True
        elif key == glfw.KEY_A and action == glfw.RELEASE:
            self.left = False
        if key == glfw.KEY_D and action == glfw.PRESS:
            self.right = True
        elif key == glfw.KEY_D and action == glfw.RELEASE:
            self.right = False
        if key == glfw.KEY_Q and action == glfw.PRESS:
            self.up = True
        elif key == glfw.KEY_Q and action == glfw.RELEASE:
            self.up = False
        if key == glfw.KEY_E and action == glfw.PRESS:
            self.down = True
        elif key == glfw.KEY_E and action == glfw.RELEASE:
            self.down = False

    def do_movement(self):
        if self.forward:
            self.cam.process_keyboard("FORWARD", 0.000008)
        if self.backward:
            self.cam.process_keyboard("BACKWARD", 0.000008)
        if self.right:
            self.cam.process_keyboard("RIGHT", 0.000008)
        if self.left:
            self.cam.process_keyboard("LEFT", 0.000008)
        if self.up:
            self.cam.process_keyboard("UP", 0.000008)
        if self.down:
            self.cam.process_keyboard("DOWN", 0.000008)

    def mmouse_look_clb(self, window, xpos, ypos):
        if self.first_mouse:
            self.lastX = xpos
            self.lastY = ypos
            self.first_mouse = False

        xoffset = xpos - self.lastX
        yoffset = self.lastY - ypos

        self.lastX = xpos
        self.lastY = ypos
        self.cam.process_mouse_movement(xoffset, yoffset)


    def window_resize(self, window, width, height):
        glViewport(0, 0, width, height)
        projection = pyrr.matrix44.create_perspective_projection_matrix(45, width / height,  0.0001, 100)
        proj_loc = glGetUniformLocation(self.shader, "projection")
        glUniformMatrix4fv(proj_loc, 1, GL_FALSE, projection)

    def main(self):

        # initializing glfw library
        if not glfw.init():
            raise Exception("glfw can not be initialized!")

        # creating the window
        window = glfw.create_window(self.WIDTH, self.HEIGHT, "My OpenGL window", None, None)

        # check if window was created
        if not window:
            glfw.terminate()
            raise Exception("glfw window can not be created!")

        # set window's position
        glfw.set_window_pos(window, 400, 200)

        # set the callback function for window resize
        glfw.set_window_size_callback(window, self.window_resize)

        glfw.set_cursor_pos_callback(window, self.mmouse_look_clb)
        glfw.set_input_mode(window, glfw.CURSOR, glfw.CURSOR_DISABLED)
        glfw.set_key_callback(window, self.key_input_clb)
        #glfw.set_cursor_enter_callback(window, self.mouse_enter_clb)

        # make the context current
        glfw.make_context_current(window)

        self.shader = compileProgram(compileShader(self.vertex_src, GL_VERTEX_SHADER),
                                     compileShader(self.fragment_src, GL_FRAGMENT_SHADER))


        VAO = glGenVertexArrays(1)
        glBindVertexArray(VAO)
        # Vertex Buffer Object
        VBO = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, VBO)
        glBufferData(GL_ARRAY_BUFFER, self.vertices.nbytes, self.vertices, GL_STATIC_DRAW)

        # Element Buffer Object
        EBO = glGenBuffers(1)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.indices.nbytes, self.indices, GL_STATIC_DRAW)

        glEnableVertexAttribArray(0)
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, self.vertices.itemsize * 8, ctypes.c_void_p(0))

        glEnableVertexAttribArray(1)
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, self.vertices.itemsize * 8, ctypes.c_void_p(12))

        glEnableVertexAttribArray(2)
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, self.vertices.itemsize * 8, ctypes.c_void_p(24))
        glBindVertexArray(0)



        glClearColor(0, 0.1, 0.1, 1)

        glEnable(GL_DEPTH_TEST)
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

        projection_2 = glm.perspective(45, self.WIDTH / self.HEIGHT, 0.001, 3000)
        projection = pyrr.matrix44.create_perspective_projection_matrix(45, self.WIDTH / self.HEIGHT, 0.0001, 100)

        # print(projection_2)
        # print(projection)

        glUseProgram(self.shader)
        model_loc = glGetUniformLocation(self.shader, "model")
        proj_loc = glGetUniformLocation(self.shader, "projection")
        view_loc = glGetUniformLocation(self.shader, "view")
        glUniformMatrix4fv(proj_loc, 1, GL_FALSE, projection)

        # the main application loop
        while not glfw.window_should_close(window):
            glfw.poll_events()
            self.do_movement()

            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

            view = self.cam.get_view_matrix()
            glUniformMatrix4fv(view_loc, 1, GL_FALSE, view)


            glBindVertexArray(VAO)
            glDrawElements(GL_TRIANGLES, len(self.indices) * 3, GL_UNSIGNED_INT, None)
            glBindVertexArray(0)

            glfw.swap_buffers(window)

        # terminate glfw, free up allocated resources
        glfw.terminate()


class Camera:
    def __init__(self, xstart, ystart):
        self.camera_pos = glm.vec3(xstart+0.005, 0.002, ystart+0.01)
        self.camera_front = glm.vec3(0.0, 0.0, -200.0)
        self.camera_up = glm.vec3(0.0, 1.0, 0.0)
        self.camera_right = glm.vec3(1.0, 0.0, 0.0)

        self.mouse_sensitivity = 0.25
        self.jaw = -90.0
        self.pitch = 0.0

    def get_view_matrix(self):
        return matrix44.create_look_at(self.camera_pos, self.camera_pos + self.camera_front, self.camera_up)

    def process_mouse_movement(self, xoffset, yoffset, constrain_pitch=True):
        xoffset *= self.mouse_sensitivity
        yoffset *= self.mouse_sensitivity

        self.jaw += xoffset
        self.pitch += yoffset

        if constrain_pitch:
            if self.pitch > 45:
                self.pitch = 45
            if self.pitch < -45:
                self.pitch = -45

        self.update_camera_vectors()

    def update_camera_vectors(self):
        front = glm.vec3(0.0, 0.0, 0.0)
        front.x = cos(radians(self.jaw)) * cos(radians(self.pitch))
        front.y = sin(radians(self.pitch))
        front.z = sin(radians(self.jaw)) * cos(radians(self.pitch))

        self.camera_front = glm.normalize(front)
        self.camera_right = glm.normalize(glm.cross(self.camera_front, glm.vec3(0.0, 1.0, 0.0)))
        #self.camera_up = glm.normalize(glm.cross(self.camera_right, self.camera_front))

    # Camera method for the WASD movement
    def process_keyboard(self, direction, velocity):
        if direction == "FORWARD":
            self.camera_pos += self.camera_front * velocity
        if direction == "BACKWARD":
            self.camera_pos -= self.camera_front * velocity
        if direction == "LEFT":
            self.camera_pos -= self.camera_right * velocity
        if direction == "RIGHT":
            self.camera_pos += self.camera_right * velocity
        if direction == "UP":
            self.camera_pos += self.camera_up * velocity
        if direction == "DOWN":
            self.camera_pos -= self.camera_up * velocity


vertices = [[18.427083, -34.104583, 14.097592], [18.427917, -34.104583, 13.813885],
            [18.42875, -34.104583, 11.798318], [18.429583, -34.104583, 12.170123],
            [18.430417, -34.104583, 13.23494], [18.43125, -34.104583, 12.263627],
            [18.432083, -34.104583, 10.543183], [18.432917, -34.104583, 9.150859],
            [18.43375, -34.104583, 7.531989], [18.434583, -34.104583, 5.370541],
            [18.435417, -34.104583, 4.947286],
            [18.43625, -34.104583, 4.369431], [18.437083, -34.104583, 9.29184],
            [18.437917, -34.104583, 11.300546], [18.43875, -34.104583, 9.590596],
            [18.439583, -34.104583, 7.006906], [18.427083, -34.105417, 17.277378],
            [18.427917, -34.105417, 12.070671], [18.42875, -34.105417, 9.0447],
            [18.429583, -34.105417, 7.853086], [18.430417, -34.105417, 7.927739],
            [18.43125, -34.105417, 8.325415],
            [18.432083, -34.105417, 8.505657], [18.432917, -34.105417, 7.904904],
            [18.43375, -34.105417, 7.7015], [18.434583, -34.105417, 7.362363], [18.435417, -34.105417, 7.08594],
            [18.43625, -34.105417, 6.692467], [18.437083, -34.105417, 1.177889],
            [18.437917, -34.105417, 6.393278], [18.43875, -34.105417, 8.610809],
            [18.439583, -34.105417, 9.748986], [18.427083, -34.10625, 29.354589],
            [18.427917, -34.10625, 24.616703], [18.42875, -34.10625, 18.529903],
            [18.429583, -34.10625, 13.677365], [18.430417, -34.10625, 10.466432],
            [18.43125, -34.10625, 9.469019], [18.432083, -34.10625, 7.960031], [18.432917, -34.10625, 7.115651],
            [18.43375, -34.10625, 6.465553], [18.434583, -34.10625, 5.50102], [18.435417, -34.10625, 5.712739],
            [18.43625, -34.10625, 6.946383],
            [18.437083, -34.10625, 4.263526], [18.437917, -34.10625, 2.732419], [18.43875, -34.10625, 5.488115],
            [18.439583, -34.10625, 6.902328], [18.427083, -34.107083, 30.979712],
            [18.427917, -34.107083, 30.684374], [18.42875, -34.107083, 26.90592],
            [18.429583, -34.107083, 22.378777], [18.430417, -34.107083, 19.191908],
            [18.43125, -34.107083, 16.608807], [18.432083, -34.107083, 13.171669],
            [18.432917, -34.107083, 10.77886], [18.43375, -34.107083, 11.478356],
            [18.434583, -34.107083, 11.300546], [18.435417, -34.107083, 9.737173],
            [18.43625, -34.107083, 6.484625], [18.437083, -34.107083, 5.185645],
            [18.437917, -34.107083, 3.770918], [18.43875, -34.107083, 4.11032],
            [18.439583, -34.107083, 4.947286], [18.427083, -34.107917, 30.013678],
            [18.427917, -34.107917, 32.894321],
            [18.42875, -34.107917, 31.857147], [18.429583, -34.107917, 26.9683],
            [18.430417, -34.107917, 21.895006], [18.43125, -34.107917, 16.907623],
            [18.432083, -34.107917, 12.721774], [18.432917, -34.107917, 11.510896],
            [18.43375, -34.107917, 14.044291], [18.434583, -34.107917, 13.438006],
            [18.435417, -34.107917, 12.538547], [18.43625, -34.107917, 11.562454],
            [18.437083, -34.107917, 9.263826],
            [18.437917, -34.107917, 6.071107], [18.43875, -34.107917, 4.289905],
            [18.439583, -34.107917, 4.057948], [18.427083, -34.10875, 25.226519],
            [18.427917, -34.10875, 26.609413], [18.42875, -34.10875, 29.121534],
            [18.429583, -34.10875, 27.900227], [18.430417, -34.10875, 24.414494],
            [18.43125, -34.10875, 20.20229], [18.432083, -34.10875, 14.686948],
            [18.432917, -34.10875, 10.620943],
            [18.43375, -34.10875, 11.136961], [18.434583, -34.10875, 9.067697],
            [18.435417, -34.10875, 9.031256], [18.43625, -34.10875, 11.842289],
            [18.437083, -34.10875, 14.412585], [18.437917, -34.10875, 14.026602],
            [18.43875, -34.10875, 10.77886], [18.439583, -34.10875, 7.479022],
            [18.427083, -34.109583, 24.316551], [18.427917, -34.109583, 4.027079],
            [18.42875, -34.109583, 13.726677],
            [18.429583, -34.109583, 23.883642], [18.430417, -34.109583, 28.826719],
            [18.43125, -34.109583, 29.248907], [18.432083, -34.109583, 24.62047],
            [18.432917, -34.109583, 14.576126], [18.43375, -34.109583, 10.270979],
            [18.434583, -34.109583, 9.1116], [18.435417, -34.109583, 8.978557],
            [18.43625, -34.109583, 10.877857], [18.437083, -34.109583, 13.952817],
            [18.437917, -34.109583, 17.318939],
            [18.43875, -34.109583, 18.3776], [18.439583, -34.109583, 16.823097],
            [18.427083, -34.110417, 31.066404], [18.427917, -34.110417, 27.124994],
            [18.42875, -34.110417, 14.584773], [18.429583, -34.110417, 6.256494],
            [18.430417, -34.110417, 14.902382], [18.43125, -34.110417, 23.147758],
            [18.432083, -34.110417, 26.993055], [18.432917, -34.110417, 22.765322],
            [18.43375, -34.110417, 17.905703],
            [18.434583, -34.110417, 13.544991], [18.435417, -34.110417, 11.899844],
            [18.43625, -34.110417, 14.348012], [18.437083, -34.110417, 16.262051],
            [18.437917, -34.110417, 17.542927], [18.43875, -34.110417, 21.157032],
            [18.439583, -34.110417, 23.491091], [18.427083, -34.11125, 12.909849],
            [18.427917, -34.11125, 20.651325], [18.42875, -34.11125, 24.733046],
            [18.429583, -34.11125, 22.71829],
            [18.430417, -34.11125, 12.409415], [18.43125, -34.11125, 4.794024],
            [18.432083, -34.11125, 14.560322], [18.432917, -34.11125, 19.986332],
            [18.43375, -34.11125, 21.261787], [18.434583, -34.11125, 18.94504],
            [18.435417, -34.11125, 17.467054], [18.43625, -34.11125, 17.029078],
            [18.437083, -34.11125, 15.266393], [18.437917, -34.11125, 14.918492],
            [18.43875, -34.11125, 16.444849],
            [18.439583, -34.11125, 18.332243], [18.427083, -34.112083, 14.870467],
            [18.427917, -34.112083, 10.946778], [18.42875, -34.112083, 9.536969],
            [18.429583, -34.112083, 17.428055], [18.430417, -34.112083, 20.116079],
            [18.43125, -34.112083, 14.013614], [18.432083, -34.112083, 4.487359],
            [18.432917, -34.112083, 4.302343], [18.43375, -34.112083, 9.771387],
            [18.434583, -34.112083, 11.65025],
            [18.435417, -34.112083, 11.8523], [18.43625, -34.112083, 10.677254],
            [18.437083, -34.112083, 8.148099], [18.437917, -34.112083, 7.58765],
            [18.43875, -34.112083, 5.033905], [18.439583, -34.112083, 3.151809],
            [18.427083, -34.112917, 4.697855], [18.427917, -34.112917, 5.462211],
            [18.42875, -34.112917, 6.136745], [18.429583, -34.112917, 6.001783],
            [18.430417, -34.112917, 9.035099],
            [18.43125, -34.112917, 13.059889], [18.432083, -34.112917, 13.085486],
            [18.432917, -34.112917, 11.08321], [18.43375, -34.112917, 6.319217],
            [18.434583, -34.112917, 4.270484], [18.435417, -34.112917, 4.372147],
            [18.43625, -34.112917, 4.910031], [18.437083, -34.112917, 4.54907],
            [18.437917, -34.112917, 3.606416], [18.43875, -34.112917, 6.188358],
            [18.439583, -34.112917, 10.144054],
            [18.427083, -34.11375, 8.589212], [18.427917, -34.11375, 8.18161], [18.42875, -34.11375, 7.444539],
            [18.429583, -34.11375, 6.263077], [18.430417, -34.11375, 5.025665], [18.43125, -34.11375, 4.281593],
            [18.432083, -34.11375, 4.90037], [18.432917, -34.11375, 9.216936], [18.43375, -34.11375, 10.723666],
            [18.434583, -34.11375, 11.447696], [18.435417, -34.11375, 11.056483],
            [18.43625, -34.11375, 10.136135], [18.437083, -34.11375, 6.79003], [18.437917, -34.11375, 2.968766],
            [18.43875, -34.11375, 1.72892], [18.439583, -34.11375, 6.459183]]
triangles = [[0, 16, 17], [0, 17, 1], [1, 17, 18], [1, 18, 2], [2, 18, 19], [2, 19, 3], [3, 19, 20], [3, 20, 4],
           [4, 20, 21], [4, 21, 5], [5, 21, 22],
           [5, 22, 6], [6, 22, 23], [6, 23, 7], [7, 23, 24], [7, 24, 8], [8, 24, 25], [8, 25, 9], [9, 25, 26],
           [9, 26, 10], [10, 26, 27], [10, 27, 11],
           [11, 27, 28], [11, 28, 12], [12, 28, 29], [12, 29, 13], [13, 29, 30], [13, 30, 14], [14, 30, 31],
           [14, 31, 15], [16, 32, 33], [16, 33, 17], [17, 33, 34],
           [17, 34, 18], [18, 34, 35], [18, 35, 19], [19, 35, 36], [19, 36, 20], [20, 36, 37], [20, 37, 21],
           [21, 37, 38], [21, 38, 22], [22, 38, 39], [22, 39, 23],
           [23, 39, 40], [23, 40, 24], [24, 40, 41], [24, 41, 25], [25, 41, 42], [25, 42, 26], [26, 42, 43],
           [26, 43, 27], [27, 43, 44], [27, 44, 28], [28, 44, 45],
           [28, 45, 29], [29, 45, 46], [29, 46, 30], [30, 46, 47], [30, 47, 31], [32, 48, 49], [32, 49, 33],
           [33, 49, 50], [33, 50, 34], [34, 50, 51], [34, 51, 35],
           [35, 51, 52], [35, 52, 36], [36, 52, 53], [36, 53, 37], [37, 53, 54], [37, 54, 38], [38, 54, 55],
           [38, 55, 39], [39, 55, 56], [39, 56, 40], [40, 56, 57],
           [40, 57, 41], [41, 57, 58], [41, 58, 42], [42, 58, 59], [42, 59, 43], [43, 59, 60], [43, 60, 44],
           [44, 60, 61], [44, 61, 45], [45, 61, 62], [45, 62, 46],
           [46, 62, 63], [46, 63, 47], [48, 64, 65], [48, 65, 49], [49, 65, 66], [49, 66, 50], [50, 66, 67],
           [50, 67, 51], [51, 67, 68], [51, 68, 52], [52, 68, 69],
           [52, 69, 53], [53, 69, 70], [53, 70, 54], [54, 70, 71], [54, 71, 55], [55, 71, 72], [55, 72, 56],
           [56, 72, 73], [56, 73, 57], [57, 73, 74], [57, 74, 58],
           [58, 74, 75], [58, 75, 59], [59, 75, 76], [59, 76, 60], [60, 76, 77], [60, 77, 61], [61, 77, 78],
           [61, 78, 62], [62, 78, 79], [62, 79, 63], [64, 80, 81],
           [64, 81, 65], [65, 81, 82], [65, 82, 66], [66, 82, 83], [66, 83, 67], [67, 83, 84], [67, 84, 68],
           [68, 84, 85], [68, 85, 69], [69, 85, 86], [69, 86, 70],
           [70, 86, 87], [70, 87, 71], [71, 87, 88], [71, 88, 72], [72, 88, 89], [72, 89, 73], [73, 89, 90],
           [73, 90, 74], [74, 90, 91], [74, 91, 75], [75, 91, 92],
           [75, 92, 76], [76, 92, 93], [76, 93, 77], [77, 93, 94], [77, 94, 78], [78, 94, 95], [78, 95, 79],
           [80, 96, 97], [80, 97, 81], [81, 97, 98], [81, 98, 82],
           [82, 98, 99], [82, 99, 83], [83, 99, 100], [83, 100, 84], [84, 100, 101], [84, 101, 85],
           [85, 101, 102], [85, 102, 86], [86, 102, 103], [86, 103, 87], [87, 103, 104],
           [87, 104, 88], [88, 104, 105], [88, 105, 89], [89, 105, 106], [89, 106, 90], [90, 106, 107],
           [90, 107, 91], [91, 107, 108], [91, 108, 92], [92, 108, 109], [92, 109, 93],
           [93, 109, 110], [93, 110, 94], [94, 110, 111], [94, 111, 95], [96, 112, 113], [96, 113, 97],
           [97, 113, 114], [97, 114, 98], [98, 114, 115], [98, 115, 99], [99, 115, 116],
           [99, 116, 100], [100, 116, 117], [100, 117, 101], [101, 117, 118], [101, 118, 102], [102, 118, 119],
           [102, 119, 103], [103, 119, 120], [103, 120, 104], [104, 120, 121], [104, 121, 105],
           [105, 121, 122], [105, 122, 106], [106, 122, 123], [106, 123, 107], [107, 123, 124], [107, 124, 108],
           [108, 124, 125], [108, 125, 109], [109, 125, 126], [109, 126, 110], [110, 126, 127],
           [110, 127, 111], [112, 128, 129], [112, 129, 113], [113, 129, 130], [113, 130, 114], [114, 130, 131],
           [114, 131, 115], [115, 131, 132], [115, 132, 116], [116, 132, 133], [116, 133, 117],
           [117, 133, 134], [117, 134, 118], [118, 134, 135], [118, 135, 119], [119, 135, 136], [119, 136, 120],
           [120, 136, 137], [120, 137, 121], [121, 137, 138], [121, 138, 122], [122, 138, 139],
           [122, 139, 123], [123, 139, 140], [123, 140, 124], [124, 140, 141], [124, 141, 125], [125, 141, 142],
           [125, 142, 126], [126, 142, 143], [126, 143, 127], [128, 144, 145], [128, 145, 129],
           [129, 145, 146], [129, 146, 130], [130, 146, 147], [130, 147, 131], [131, 147, 148], [131, 148, 132],
           [132, 148, 149], [132, 149, 133], [133, 149, 150], [133, 150, 134], [134, 150, 151],
           [134, 151, 135], [135, 151, 152], [135, 152, 136], [136, 152, 153], [136, 153, 137], [137, 153, 154],
           [137, 154, 138], [138, 154, 155], [138, 155, 139], [139, 155, 156], [139, 156, 140],
           [140, 156, 157], [140, 157, 141], [141, 157, 158], [141, 158, 142], [142, 158, 159], [142, 159, 143],
           [144, 160, 161], [144, 161, 145], [145, 161, 162], [145, 162, 146], [146, 162, 163],
           [146, 163, 147], [147, 163, 164], [147, 164, 148], [148, 164, 165], [148, 165, 149], [149, 165, 166],
           [149, 166, 150], [150, 166, 167], [150, 167, 151], [151, 167, 168], [151, 168, 152],
           [152, 168, 169], [152, 169, 153], [153, 169, 170], [153, 170, 154], [154, 170, 171], [154, 171, 155],
           [155, 171, 172], [155, 172, 156], [156, 172, 173], [156, 173, 157], [157, 173, 174],
           [157, 174, 158], [158, 174, 175], [158, 175, 159], [160, 176, 177], [160, 177, 161], [161, 177, 178],
           [161, 178, 162], [162, 178, 179], [162, 179, 163], [163, 179, 180], [163, 180, 164],
           [164, 180, 181], [164, 181, 165], [165, 181, 182], [165, 182, 166], [166, 182, 183], [166, 183, 167],
           [167, 183, 184], [167, 184, 168], [168, 184, 185], [168, 185, 169], [169, 185, 186],
           [169, 186, 170], [170, 186, 187], [170, 187, 171], [171, 187, 188], [171, 188, 172], [172, 188, 189],
           [172, 189, 173], [173, 189, 190], [173, 190, 174], [174, 190, 191], [174, 191, 175]]
Z_scale = 0.00005
xStart = vertices[0][0]
yStart = vertices[0][1]

threeD_viewer(vertices, triangles, Z_scale, xStart, yStart)