# Draws a triangle using low-level OpenGL calls.

import array

from java.nio import ByteBuffer, ByteOrder, IntBuffer
from java.lang import Float

VERT_CMP_COUNT = 4  # vertex component count (x, y, z, w) -> 4
CLR_CMP_COUNT = 4  # color component count (r, g, b, a) -> 4

def allocateDirectFloatBuffer(n):
    return (ByteBuffer.allocateDirect(n * Float.SIZE / 8).
            order(ByteOrder.nativeOrder()).asFloatBuffer())

vertices = array.array('f', (0,) * 12)
colors = array.array('f', (0,) * 12)

vertexBuffer = allocateDirectFloatBuffer(12)
colorBuffer = allocateDirectFloatBuffer(12)

def setup():
    size(640, 360, P3D)
    
    # Loads a shader to render geometry w/out textures and lights.
    global sh
    sh = loadShader("frag.glsl", "vert.glsl")

    with beginPGL() as pgl:
	    # allocate buffer big enough to get all VBO ids back
	    intBuffer = IntBuffer.allocate(2)
	    pgl.genBuffers(2, intBuffer)
	    global vertexVboId
	    vertexVboId = intBuffer.get(0)
	
	    global colorVboId
	    colorVboId = intBuffer.get(1)

def draw():
    with g.beginPGL() as pgl:
	
	    background(0)
	
	    # The geometric transformations will be automatically passed
	    # to the shader.
	    rotate(frameCount * 0.01, width, height, 0)
	
	    updateGeometry()
	
	    sh.bind()
	
	    # get "vertex" attribute location in the shader
	    vertLoc = pgl.getAttribLocation(sh.glProgram, "vertex")
	    # enable array for "vertex" attribute
	    pgl.enableVertexAttribArray(vertLoc)
	
	    # get "color" attribute location in the shader
	    colorLoc = pgl.getAttribLocation(sh.glProgram, "color")
	    # enable array for "color" attribute
	    pgl.enableVertexAttribArray(colorLoc)
	
	    # BUFFER LAYOUT from updateGeometry()
	    #
	    # Vertex buffer:
	    #
	    #   xyzwxyzwxyzw...
	    #
	    #   |v1  |v2  |v3  |...
	    #   |0   |4   |8   |...
	    #   |xyzw|xyzw|xyzw|...
	    #
	    #
	    # Color buffer:
	    #
	    #   rgbargbargba...
	    #
	    #   |v1  |v2  |v3  |...
	    #   |0   |4   |8   |...
	    #   |rgba|rgba|rgba|...
	    #
	    # stride (values per vertex) is 4 floats in both cases
	    # vertex offset is 0 floats in both cases
	
	    vertexStride = VERT_CMP_COUNT * Float.BYTES
	    colorStride = CLR_CMP_COUNT * Float.BYTES
	    vertexOffset = 0 * Float.BYTES
	    colorOffset = 0 * Float.BYTES
	
	    # VERTEX
	    # bind VBO
	    pgl.bindBuffer(PGL.ARRAY_BUFFER, vertexVboId)
	    # fill VBO with data
	    pgl.bufferData(
	        PGL.ARRAY_BUFFER, Float.BYTES * len(vertices), vertexBuffer, PGL.DYNAMIC_DRAW)
	    # associate currently bound VBO with "vertex" shader attribute
	    pgl.vertexAttribPointer(
	        vertLoc, VERT_CMP_COUNT, PGL.FLOAT, False, vertexStride, vertexOffset)
	
	    # COLOR
	    # bind VBO
	    pgl.bindBuffer(PGL.ARRAY_BUFFER, colorVboId)
	    # fill VBO with data
	    pgl.bufferData(
	        PGL.ARRAY_BUFFER, Float.BYTES * len(colors), colorBuffer, PGL.DYNAMIC_DRAW)
	    # associate currently bound VBO with "color" shader attribute
	    pgl.vertexAttribPointer(
	        colorLoc, CLR_CMP_COUNT, PGL.FLOAT, False, colorStride, colorOffset)
	
	    # unbind VBOs
	    pgl.bindBuffer(PGL.ARRAY_BUFFER, 0)
	
	    pgl.drawArrays(PGL.TRIANGLES, 0, 3)
	
	    # disable arrays for attributes before unbinding the shader
	    pgl.disableVertexAttribArray(vertLoc)
	    pgl.disableVertexAttribArray(colorLoc)
	
	    sh.unbind()


def updateGeometry():
    # Vertex 1
    vertices[0] = 0
    vertices[1] = 0
    vertices[2] = 0
    vertices[3] = 1

    # Color 1
    colors[0] = 1
    colors[1] = 0
    colors[2] = 0
    colors[3] = 1

    # Vertex 2
    vertices[4] = width / 2
    vertices[5] = height
    vertices[6] = 0
    vertices[7] = 1

    # Color 2
    colors[4] = 0
    colors[5] = 1
    colors[6] = 0
    colors[7] = 1

    # Vertex 3
    vertices[8] = width
    vertices[9] = 0
    vertices[10] = 0
    vertices[11] = 1

    # Color 3
    colors[8] = 0
    colors[9] = 0
    colors[10] = 1
    colors[11] = 1

    vertexBuffer.rewind()
    vertexBuffer.put(vertices)
    vertexBuffer.rewind()

    colorBuffer.rewind()
    colorBuffer.put(colors)
    colorBuffer.rewind()
    