// // Copy the vertices, normals and element indices into vertex buffers // func (terrain *Terrain) CreateObject() { // Generate the vertex buffer object gl.GenBuffers(1, &terrain.VBOVertices) gl.BindBuffer(gl.ARRAY_BUFFER, terrain.VBOVertices) gl.BufferData(gl.ARRAY_BUFFER, int(len(terrain.Vertices)*3*4), gl.Ptr(terrain.Vertices), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) /* Store the normals in a buffer object */ gl.GenBuffers(1, &terrain.VBONormals) gl.BindBuffer(gl.ARRAY_BUFFER, terrain.VBONormals) gl.BufferData(gl.ARRAY_BUFFER, int(len(terrain.Normals)*3*4), gl.Ptr(terrain.Normals), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) /* Store the Colors in a buffer object */ gl.GenBuffers(1, &terrain.VBOColors) gl.BindBuffer(gl.ARRAY_BUFFER, terrain.VBOColors) gl.BufferData(gl.ARRAY_BUFFER, int(len(terrain.Colors)*5*4), gl.Ptr(terrain.Colors), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) // Generate a buffer for the indices gl.GenBuffers(1, &terrain.VBOIndices) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, terrain.VBOIndices) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, int(len(terrain.Indices)*SizeOfUint16), gl.Ptr(terrain.Indices), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) }
func makeTexture(filename string) uint32 { fp, err := os.Open(filename) x(err) img, _, err := image.Decode(fp) fp.Close() x(err) rgba := image.NewRGBA(img.Bounds()) if rgba.Stride != rgba.Rect.Size().X*4 { x(errors.New("unsupported stride")) } draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src) var texture uint32 gl.GenTextures(1, &texture) gl.BindTexture(gl.TEXTURE_2D, texture) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) gl.TexImage2D( gl.TEXTURE_2D, 0, // target, level gl.RGB8, // internal format int32(rgba.Rect.Size().X), // width int32(rgba.Rect.Size().Y), // height 0, // border gl.RGBA, gl.UNSIGNED_BYTE, // external format, type gl.Ptr(rgba.Pix)) // pixels return texture }
func (cube *Cube) MakeVBO() { // Create a vertex buffer object to store vertices for the cube gl.GenBuffers(1, &cube.bufferObject) gl.BindBuffer(gl.ARRAY_BUFFER, cube.bufferObject) gl.BufferData(gl.ARRAY_BUFFER, len(*cube.vertexPositions)*4, gl.Ptr(*cube.vertexPositions), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) // Create a vertex buffer object to store vertex colours for the cube gl.GenBuffers(1, &cube.coloursObject) gl.BindBuffer(gl.ARRAY_BUFFER, cube.coloursObject) gl.BufferData(gl.ARRAY_BUFFER, len(*cube.vertexColours)*4, gl.Ptr(*cube.vertexColours), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) // Create the normals buffer for the cube gl.GenBuffers(1, &cube.normalsObject) gl.BindBuffer(gl.ARRAY_BUFFER, cube.normalsObject) gl.BufferData(gl.ARRAY_BUFFER, len(*cube.normals)*4, gl.Ptr(*cube.normals), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) }
func makeResources() *gResources { r := gResources{ vertexBuffer: makeBuffer(gl.ARRAY_BUFFER, gl.Ptr(gVertexBufferData), 4*len(gVertexBufferData)), elementBuffer: makeBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.Ptr(gElementBufferData), 4*len(gElementBufferData)), } r.textures[0] = makeTexture("hello1.png") r.textures[1] = makeTexture("hello2.png") r.vertexShader = makeShader(gl.VERTEX_SHADER, vertex_glsl) r.fragmentShader = makeShader(gl.FRAGMENT_SHADER, fragment_glsl) r.program = makeProgram(r.vertexShader, r.fragmentShader) r.uniforms.fadeFactor = gl.GetUniformLocation(r.program, gl.Str("fade_factor\x00")) r.uniforms.textures[0] = gl.GetUniformLocation(r.program, gl.Str("textures[0]\x00")) r.uniforms.textures[1] = gl.GetUniformLocation(r.program, gl.Str("textures[1]\x00")) r.attributes.position = gl.GetAttribLocation(r.program, gl.Str("position\x00")) return &r }
func (objectLoader *WavefrontObject) CreateObject() { for _, object := range objectLoader.Objects { // Sets the Model in the Initial position object.Model = mgl32.Ident4() if wrapper.DEBUG { // Print the object fmt.Println(object) } // Generate the vertex buffer object gl.GenBuffers(1, &object.VertexBufferObjectVertices) gl.BindBuffer(gl.ARRAY_BUFFER, object.VertexBufferObjectVertices) gl.BufferData(gl.ARRAY_BUFFER, int(len(object.Vertex)*4), gl.Ptr(&(object.Vertex[0])), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) // Obj might not have normals if len(object.Normals) != 0 { // Store the normals in a buffer object gl.GenBuffers(1, &object.VertexBufferObjectNormals) gl.BindBuffer(gl.ARRAY_BUFFER, object.VertexBufferObjectNormals) gl.BufferData(gl.ARRAY_BUFFER, int(len(object.Normals)*4), gl.Ptr(&(object.Normals[0])), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) } // Generate a buffer for the indices gl.GenBuffers(1, &object.VertexBufferObjectFaces) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, object.VertexBufferObjectFaces) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, int(len(object.Faces)*3), gl.Ptr(&(object.Faces[0])), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) if len(object.Coordinates) != 0 { // Generate a buffer for the Texture Coordinates gl.GenBuffers(1, &object.VertexBufferObjectTextureCoords) gl.BindBuffer(gl.ARRAY_BUFFER, object.VertexBufferObjectTextureCoords) gl.BufferData(gl.ARRAY_BUFFER, int(len(object.Coordinates)*2)*2, gl.Ptr(&(object.Coordinates[0])), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) } } }
func updateScreenTexture(texture uint32, pixels []maths.Rgba, width, height int) { gl.ActiveTexture(gl.TEXTURE0) gl.BindTexture(gl.TEXTURE_2D, texture) gl.TexSubImage2D( gl.TEXTURE_2D, 0, 0, 0, int32(width), int32(height), gl.RGBA, gl.FLOAT, gl.Ptr(pixels)) }
func (loader *Loader) LoadTexture(file string) (uint32, error) { imgFile, err := os.Open(file) if err != nil { // Get the Folder of the current Executable dir, err := osext.ExecutableFolder() if err != nil { return 0, err } // Read the file and return content or error var secondErr error imgFile, secondErr = os.Open(fmt.Sprintf("%s/%s", dir, file)) if secondErr != nil { return 0, secondErr } } img, _, err := image.Decode(imgFile) if err != nil { return 0, err } rgba := image.NewRGBA(img.Bounds()) if rgba.Stride != rgba.Rect.Size().X*4 { return 0, fmt.Errorf("unsupported stride") } draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src) var texture uint32 gl.GenTextures(1, &texture) gl.ActiveTexture(gl.TEXTURE0) gl.BindTexture(gl.TEXTURE_2D, texture) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) gl.TexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, int32(rgba.Rect.Size().X), int32(rgba.Rect.Size().Y), 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(rgba.Pix)) return texture, nil }
func (glr *GlRenderer) initScreen() { var err error glr.program, err = createScreenShader() if err != nil { panic(err) } gl.UseProgram(glr.program) projection := mgl32.Ortho2D(-1, 1, 1, -1) projectionUniform := gl.GetUniformLocation(glr.program, gl.Str("projection\x00")) gl.UniformMatrix4fv(projectionUniform, 1, false, &projection[0]) textureUniform := gl.GetUniformLocation(glr.program, gl.Str("tex\x00")) gl.Uniform1i(textureUniform, 0) glr.texture = createScreenTexture(glr.width, glr.height) gl.GenVertexArrays(1, &glr.vao) gl.BindVertexArray(glr.vao) var quadVertices = []float32{ // X, Y, Z, U, V 1.0, -1.0, 0.0, 1.0, 0.0, -1.0, -1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0, -1.0, -1.0, 0.0, 0.0, 0.0, -1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, } var vbo uint32 gl.GenBuffers(1, &vbo) gl.BindBuffer(gl.ARRAY_BUFFER, vbo) gl.BufferData(gl.ARRAY_BUFFER, len(quadVertices)*4, gl.Ptr(quadVertices), gl.STATIC_DRAW) vertAttrib := uint32(gl.GetAttribLocation(glr.program, gl.Str("vert\x00"))) gl.EnableVertexAttribArray(vertAttrib) gl.VertexAttribPointer(vertAttrib, 3, gl.FLOAT, false, 5*4, gl.PtrOffset(0)) texCoordAttrib := uint32(gl.GetAttribLocation(glr.program, gl.Str("vertTexCoord\x00"))) gl.EnableVertexAttribArray(texCoordAttrib) gl.VertexAttribPointer(texCoordAttrib, 2, gl.FLOAT, false, 5*4, gl.PtrOffset(3*4)) gl.Enable(gl.DEPTH_TEST) gl.ClearColor(1.0, 1.0, 1.0, 1.0) }
func makeResources() *gResources { r := gResources{ vertexBuffer1: makeBuffer(gl.ARRAY_BUFFER, gl.Ptr(gVertexBufferData1), 4*len(gVertexBufferData1)), elementBuffer1: makeBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.Ptr(gElementBufferData1), 4*len(gElementBufferData1)), vertexBuffer2: makeBuffer(gl.ARRAY_BUFFER, gl.Ptr(gVertexBufferData2), 4*len(gVertexBufferData2)), elementBuffer2: makeBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.Ptr(gElementBufferData2), 4*len(gElementBufferData2)), colorBuffer2: makeBuffer(gl.ARRAY_BUFFER, gl.Ptr(gColorBufferData2), 4*len(gColorBufferData2)), } r.vertexShader1 = makeShader(gl.VERTEX_SHADER, vertex_glsl1) r.fragmentShader1 = makeShader(gl.FRAGMENT_SHADER, fragment_glsl1) r.program1 = makeProgram(r.vertexShader1, r.fragmentShader1) r.vertexShader2 = makeShader(gl.VERTEX_SHADER, vertex_glsl2) r.fragmentShader2 = makeShader(gl.FRAGMENT_SHADER, fragment_glsl2) r.program2 = makeProgram(r.vertexShader2, r.fragmentShader2) r.uniforms2.xmul = gl.GetUniformLocation(r.program2, gl.Str("xmul\x00")) r.uniforms2.ymul = gl.GetUniformLocation(r.program2, gl.Str("ymul\x00")) r.uniforms2.sin = gl.GetUniformLocation(r.program2, gl.Str("sin\x00")) r.uniforms2.cos = gl.GetUniformLocation(r.program2, gl.Str("cos\x00")) r.attributes1.position = gl.GetAttribLocation(r.program1, gl.Str("position\x00")) r.attributes2.position = gl.GetAttribLocation(r.program2, gl.Str("position\x00")) r.attributes2.color = gl.GetAttribLocation(r.program2, gl.Str("vertexColor\x00")) // circle gColorBufferData3 := make([]float32, 0, 126*3) gVertexBufferData3 := make([]float32, 0, 126*2) gElementBufferData3 := make([]uint32, 0, 126) r.len3 = 0 for i := float64(0); i < 2*math.Pi; i += .05 { rd, g, b := hsb2rgb(float32(i/(2*math.Pi)), 1, 1) gColorBufferData3 = append(gColorBufferData3, rd, g, b) gVertexBufferData3 = append(gVertexBufferData3, float32(math.Sin(i)), float32(math.Cos(i))) gElementBufferData3 = append(gElementBufferData3, uint32(r.len3)) r.len3++ } r.vertexBuffer3 = makeBuffer(gl.ARRAY_BUFFER, gl.Ptr(gVertexBufferData3), 4*len(gVertexBufferData3)) r.elementBuffer3 = makeBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.Ptr(gElementBufferData3), 4*len(gElementBufferData3)) r.colorBuffer3 = makeBuffer(gl.ARRAY_BUFFER, gl.Ptr(gColorBufferData3), 4*len(gColorBufferData3)) return &r }
func CreateMesh(verticies []float32, texturePaths []string, shader *Shader) (*Mesh, error) { modelView := mgl32.Ident4() // // Load the texture textures := make([]uint32, 0) // fmt.Println(texturePaths) for _, texturePath := range texturePaths { // fmt.Println("Paths", texturePath, texturePaths[i]) texture, err := createTexture(texturePath) if err != nil { return nil, err } textures = append(textures, texture) } // // // Configure the vertex data var vao uint32 gl.GenVertexArrays(1, &vao) gl.BindVertexArray(vao) // var vbo uint32 gl.GenBuffers(1, &vbo) gl.BindBuffer(gl.ARRAY_BUFFER, vbo) gl.BufferData(gl.ARRAY_BUFFER, len(verticies)*4, gl.Ptr(verticies), gl.STATIC_DRAW) // gl.EnableVertexAttribArray(shader.attributes["vert"]) gl.VertexAttribPointer(shader.attributes["vert"], 3, gl.FLOAT, false, 5*4, gl.PtrOffset(0)) // gl.EnableVertexAttribArray(shader.attributes["vertTexCoord"]) gl.VertexAttribPointer(shader.attributes["vertTexCoord"], 2, gl.FLOAT, false, 5*4, gl.PtrOffset(3*4)) gl.BindVertexArray(0) gl.BindBuffer(gl.ARRAY_BUFFER, 0) return &Mesh{ modelView: modelView, textures: textures, verticies: verticies, vao: vao, vbo: vbo, }, nil }
func createTexture(file string) (uint32, error) { imgFile, err := os.Open(file) if err != nil { return 0, err } img, _, err := image.Decode(imgFile) if err != nil { return 0, err } rgba := image.NewRGBA(img.Bounds()) if rgba.Stride != rgba.Rect.Size().X*4 { return 0, fmt.Errorf("unsupported stride") } draw.Draw(rgba, rgba.Bounds(), img, image.Point{0, 0}, draw.Src) var texture uint32 gl.GenTextures(1, &texture) gl.ActiveTexture(gl.TEXTURE0) gl.BindTexture(gl.TEXTURE_2D, texture) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) gl.TexImage2D( gl.TEXTURE_2D, 0, gl.RGBA, int32(rgba.Rect.Size().X), int32(rgba.Rect.Size().Y), 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(rgba.Pix)) return texture, nil }
// Make a sphere from two triangle fans (one at each pole) and triangle strips along latitudes // This version uses indexed vertex buffers for both the fans at the poles and the latitude strips func (sphere *Sphere) MakeSphereVBO() { var i uint32 // Calculate the number of vertices required in sphere sphere.numSphereVertices = 2 + ((sphere.numLats - 1) * sphere.numLongs) pColours := make([]float32, (sphere.numSphereVertices * 4)) pVertices, pNormals := sphere.MakeUnitSphere() // Define colours as the x,y,z components of the sphere vertices for i = 0; i < sphere.numSphereVertices; i++ { pColours[i*4] = pVertices[i*3] pColours[i*4+1] = pVertices[i*3+1] pColours[i*4+2] = pVertices[i*3+2] pColours[i*4+3] = 1.0 } /* Generate the vertex buffer object */ gl.GenBuffers(1, &sphere.sphereBufferObject) gl.BindBuffer(gl.ARRAY_BUFFER, sphere.sphereBufferObject) gl.BufferData(gl.ARRAY_BUFFER, int(4*sphere.numSphereVertices*3), gl.Ptr(pVertices), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) /* Store the normals in a buffer object */ gl.GenBuffers(1, &sphere.sphereNormals) gl.BindBuffer(gl.ARRAY_BUFFER, sphere.sphereNormals) gl.BufferData(gl.ARRAY_BUFFER, int(4*sphere.numSphereVertices*3), gl.Ptr(pNormals), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) /* Store the colours in a buffer object */ gl.GenBuffers(1, &sphere.sphereColours) gl.BindBuffer(gl.ARRAY_BUFFER, sphere.sphereColours) gl.BufferData(gl.ARRAY_BUFFER, int(4*sphere.numSphereVertices*4), gl.Ptr(pColours), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) /* Calculate the number of indices in our index array and allocate memory for it */ numIndices := ((sphere.numLongs*2)+2)*(sphere.numLats-1) + ((sphere.numLongs + 2) * 2) pIndices := make([]uint32, numIndices) // fill "indices" to define triangle strips var index int = 0 // Current index // Define indices for the first triangle fan for one pole for i = 0; i < sphere.numLongs+1; i++ { pIndices[index] = i index++ } pIndices[index] = 1 // Join last triangle in the triangle fan index++ var j uint32 var start uint32 = 1 // Start index for each latitude row for j = 0; j < sphere.numLats-2; j++ { for i = 0; i < sphere.numLongs; i++ { pIndices[index] = start + i index++ pIndices[index] = start + i + sphere.numLongs index++ } // close the triangle strip loop by going back to the first vertex in the loop pIndices[index] = start index++ // close the triangle strip loop by going back to the first vertex in the loop pIndices[index] = start + sphere.numLongs index++ start += sphere.numLongs } // Define indices for the last triangle fan for the south pole region for i = sphere.numSphereVertices - 1; i > sphere.numSphereVertices-sphere.numLongs-2; i-- { pIndices[index] = i index++ } pIndices[index] = sphere.numSphereVertices - 2 // Tie up last triangle in fan index++ // Generate a buffer for the indices gl.GenBuffers(1, &sphere.elementBuffer) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, sphere.elementBuffer) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, int(numIndices*4), gl.Ptr(pIndices), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) }
func (cog *Cog) MakeCogVBO() { var i uint32 // Calculate the number of vertices required in sphere cog.numCogVertices = ((cog.VerticesPerDisk + 2) * 4) pVertices, pNormals := cog.MakeUnitcog() pColours := make([]float32, ((cog.VerticesPerDisk * 4) * 4)) // Define colours as the x,y,z components of the cog vertices for i = 0; i < (cog.VerticesPerDisk * 4); i++ { pColours[i*4] = 0.3 + pVertices[i*2] pColours[i*4+1] = 0.5 + pVertices[i*2+1] pColours[i*4+2] = 0.3 + pVertices[i*2+2] pColours[i*4+3] = 1.0 } /* Generate the vertex buffer object */ gl.GenBuffers(1, &cog.cogBufferObject) gl.BindBuffer(gl.ARRAY_BUFFER, cog.cogBufferObject) gl.BufferData(gl.ARRAY_BUFFER, int(8*len(pVertices)*3), gl.Ptr(pVertices), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) /* Store the normals in a buffer object */ gl.GenBuffers(1, &cog.cogNormals) gl.BindBuffer(gl.ARRAY_BUFFER, cog.cogNormals) gl.BufferData(gl.ARRAY_BUFFER, int(8*len(pNormals)*3), gl.Ptr(pNormals), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) /* Store the colours in a buffer object */ gl.GenBuffers(1, &cog.cogColours) gl.BindBuffer(gl.ARRAY_BUFFER, cog.cogColours) gl.BufferData(gl.ARRAY_BUFFER, int(8*len(pColours)*4), gl.Ptr(pColours), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) /* Calculate the number of indices in our index array and allocate memory for it */ numIndices := (2 * (cog.VerticesPerDisk + 4)) * 2 pIndices := make([]uint32, numIndices) // fill "indices" to define triangle strips var index int = 0 // Current index // Define indices for the first triangle fan for one pole for i = 0; i < cog.VerticesPerDisk+1; i++ { pIndices[index] = i index++ } // Join last triangle in the triangle fan pIndices[index] = 1 index++ // Creates the Sides for i = 1; i < (cog.VerticesPerDisk*2)+1; i++ { pIndices[index] = i + cog.VerticesPerDisk index++ } // Join last triangle in the triangle fan pIndices[index] = 1 + cog.VerticesPerDisk index++ // Define indices for the last triangle fan for the south pole region // Start on a corner to avoid breaking the model pIndices[index] = ((cog.VerticesPerDisk + 3) - 2) + (cog.VerticesPerDisk * 3) index++ // Go to Center and keep lopping till the end for i = (cog.VerticesPerDisk + 3) - 1; i >= 1; i-- { pIndices[index] = i + (cog.VerticesPerDisk * 3) index++ } // Generate a buffer for the indices gl.GenBuffers(1, &cog.elementBuffer) gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, cog.elementBuffer) gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, int(numIndices*4), gl.Ptr(pIndices), gl.STATIC_DRAW) gl.BindBuffer(gl.ARRAY_BUFFER, 0) }