#-*- coding:utf-8 -*-
# cython: profile=False

#  Copyright © 2009-2017  B. Clausius <barcc@gmx.de>
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.


# although this file is compiled with Python3 syntax, Cython needs at least division from __future__
from __future__ import print_function, division

# This line makes cython happy
global __name__, __package__    # pylint: disable=W0604
#px/__compiled = True
__compiled = False
#px/DEF OFFSCREEN = False
OFFSCREEN = False

#FIXME: do not use this functions
#px+from libc.stdlib cimport malloc, free
#px+from libc.string cimport memset
#px/from libc.math cimport cos, sin, tan, atan2, sqrt, M_PI, fabs
from math import cos, sin, tan, atan2, sqrt, pi as M_PI, fabs

#pxm>IF '[[GLDEBUG]]' == 'gldebug'
#px+from [[_debug_VARIANT]] cimport *
#pxm>IF '[[GLDEBUG]]' != 'gldebug'
#px+from gl_[[GLVARIANT]] cimport *
#pxm>IF_END
#px+cimport [[_gldraw_VARIANT]] as gldraw

#px:
from . import gldraw
from OpenGL.GLU import * # XXX: without the next line fails with python 3.5
from OpenGL.GL import *
from OpenGL.GL.ARB.vertex_shader import glGetActiveAttribARB as glGetActiveAttrib
#px.

#px/from libc.stdio cimport printf
def printf(fmt, *args): print(fmt % args, end='')
#px/from libc.stdio cimport puts
puts = print

#px/cdef enum: #
if True:
    DEBUG_MSGGL = 0x02
    DEBUG_MSGEXT = 0x08
    DEBUG_DRAW = 0x10
    DEBUG_PICK = 0x20
#px+cdef long debug
debug = 0

def set_debug_flags(module):
    global debug
    if module.DEBUG_MSGGL: debug |= DEBUG_MSGGL
    if module.DEBUG_MSGEXT: debug |= DEBUG_MSGEXT
    if module.DEBUG_DRAW: debug |= DEBUG_DRAW
    if module.DEBUG_PICK: debug |= DEBUG_PICK
    

#px/cdef struct AtlasData:
class AtlasData: pass
    #px+int x, w, h
    #px+char *data
        
#px/cdef struct RenderData:
class renderdata:
    #px+float bg_red
    #px+float bg_green
    #px+float bg_blue
    #px+int y
    #px+int width
    #px+int height
    #px+int pick_x
    #px+int pick_y

    #px+bint multisample
    #px/gldraw.mat4 modelview_matrix
    modelview_matrix = gldraw.mat4()
    #px/gldraw.mat4 projection_matrix
    projection_matrix = gldraw.mat4()
    #px/gldraw.mat4 picking_matrix
    picking_matrix = gldraw.mat4()

    #px+GLuint prog_render
    #px+GLuint projection_location
    #px+GLuint modelview_location
    #px+GLuint prog_hud  # only used if DEBUG_DRAW
    #px+GLuint prog_pick
    #px+GLuint picking_location
    #px+GLuint projection_pick_location
    #px+GLuint modelview_pick_location
    
    #px+bint shader_changed
    #px+char *shader_vertsrc
    #px+char *shader_fragsrc
    #px+char *hud_vertsrc
    #px+char *hud_fragsrc
    #px+char *pick_vertsrc
    #px+char *pick_fragsrc
    
    #px/AtlasData atlasdata[gldraw.MAX_FACES]
    atlasdata = [AtlasData() for i in range(gldraw.MAX_FACES)]
    #px+int atlaslen
    
    #px/float debug_modelview1[3]
    debug_modelview1 = [0.] * 3
    #px/float debug_modelview2[3]
    debug_modelview2 = [0.] * 3
    #px/int debug_viewport1[2]
    debug_viewport1 = [0] * 2
    #px/int debug_viewport2[2]
    debug_viewport2 = [0] * 2
#px+cdef RenderData renderdata


#px/cdef struct UIData:
class uidata:
    #px+bint synced
    
    #px+bint background_changed
    #px+float bg_red
    #px+float bg_green
    #px+float bg_blue
    
    #px+bint viewport_changed
    
    #px+bint rotation_changed
    #px+float rotationxhalf, rotationyhalf
    #px+bint rotation_quat_changed
    #px/gldraw.vec4 rotation_quat
    rotation_quat = gldraw.vec4()
    
    #px+int pick_x
    #px+int pick_y
    
    #px+bint frustum_changed
    #px+float fovy_radius
    #px+float fovy_radius_zoom
    #px+double bounding_sphere_radius
    
    #px+int multisample_changed
    
    #px+char *hud_vertsrc
    #px+char *hud_fragsrc
    #px+char *pick_vertsrc
    #px+char *pick_fragsrc
    
    #px+bint shaders_changed
    #px+char *shader_vertsrc
    #px+char *shader_fragsrc
    
    #px/AtlasData atlasdata[gldraw.MAX_FACES]
    atlasdata = [AtlasData() for i in range(gldraw.MAX_FACES)]
    #px+int atlaslen
    
    #px+bint data_changed
    #px+int vertexdatalen
    #px+char *vertexdata
    #px/long vertexpointers[gldraw.ATTRIB_CNT-1]
    vertexpointers = [None] * (gldraw.ATTRIB_CNT-1)
    #px+int nblocks
    #px/short cnts_block[gldraw.MAX_BLOCKS]
    cnts_block = [None] * gldraw.MAX_BLOCKS
    #px+int idx_debug
    #px+int cnt_debug
    #px+int cnt_pick
    #px+int transformations_count
    #px/gldraw.mat4 transformations[gldraw.MAX_TRANSFORMATIONS]
    transformations = [[[None]*4, [None]*4, [None]*4, [None]*4] for __t in range(gldraw.MAX_TRANSFORMATIONS)]
    
    #px+bint transformation_changed
    #px/short transformation_blocks[gldraw.MAX_BLOCKS]
    transformation_blocks = [None] * gldraw.MAX_BLOCKS
    
    #px+int animation_changed
    #px+int animation_blocks_count
    #px/short animation_blocks[gldraw.MAX_BLOCKS]
    animation_blocks = [None] * gldraw.MAX_BLOCKS
    #px+float angle
    #px+float rotation_x, rotation_y, rotation_z
#px+cdef UIData uidata

class syncpydata: pass
class uipydata: pass
pydata = {}


### quaternion functions

#pxm-FUNC P nogil
def quat_set_identity(quat:'gldraw.vec4 &'):
    quat[0] = 1.
    quat[1] = quat[2] = quat[3] = 0.
    
# (a+bi+cj+dk)(w+xi+yj+zk)
# (aw-bx-cy-dz) + (ax+bw+cz-dy)i + (ay-bz+cw+dx)j + (az+by-cx+dw)k
# ii = jj = kk = ijk = -1

#pxm-FUNC P nogil
def quat_mult(dest:'gldraw.vec4 &', src1:'gldraw.vec4 &', src2:'gldraw.vec4 &'):
    #px+cdef float a,b,c,d
    a = src1[0]*src2[0] - src1[1]*src2[1] - src1[2]*src2[2] - src1[3]*src2[3]
    b = src1[0]*src2[1] + src1[1]*src2[0] + src1[2]*src2[3] - src1[3]*src2[2]
    c = src1[0]*src2[2] - src1[1]*src2[3] + src1[2]*src2[0] + src1[3]*src2[1]
    d = src1[0]*src2[3] + src1[1]*src2[2] - src1[2]*src2[1] + src1[3]*src2[0]
    dest[0] = a
    dest[1] = b
    dest[2] = c
    dest[3] = d
    
##pxm-FUNC P nogil
#def quat_norm(quat:'gldraw.vec4 &'):
#    #px+cdef float n
#    n = sqrt(quat[0]*quat[0] + quat[1]*quat[1] + quat[2]*quat[2] + quat[3]*quat[3])
#    quat[0] /= n
#    quat[1] /= n
#    quat[2] /= n
#    quat[3] /= n
#    if quat[0] < 0.:
#        quat[0] = -quat[0]
#        quat[1] = -quat[1]
#        quat[2] = -quat[2]
#        quat[3] = -quat[3]
        
#pxm-FUNC P nogil
def quat_to_matrix(dest:'gldraw.mat4 &', src:'gldraw.vec4 &'):
    #px+cdef float wx2,wy2,wz2, xx2,xy2,xz2, yy2,yz2,zz2
    #px+cdef float nn
    nn = src[0]*src[0] + src[1]*src[1] + src[2]*src[2] + src[3]*src[3]
    
    wx2 = 2*src[0]*src[1] / nn
    wy2 = 2*src[0]*src[2] / nn
    wz2 = 2*src[0]*src[3] / nn
    xx2 = 2*src[1]*src[1] / nn
    xy2 = 2*src[1]*src[2] / nn
    xz2 = 2*src[1]*src[3] / nn
    yy2 = 2*src[2]*src[2] / nn
    yz2 = 2*src[2]*src[3] / nn
    zz2 = 2*src[3]*src[3] / nn
    
    dest[0][0] = 1-yy2-zz2; dest[1][0] = xy2-wz2;   dest[2][0] = xz2+wy2
    dest[0][1] = xy2+wz2;   dest[1][1] = 1-xx2-zz2; dest[2][1] = yz2-wx2
    dest[0][2] = xz2-wy2;   dest[1][2] = yz2+wx2;   dest[2][2] = 1-xx2-yy2
    
#pxm-FUNC P nogil
def quat_from_angle_axis(quat:'gldraw.vec4 &', angle:float, x:float, y:float, z:float):
    #px+cdef float sa, ca
    ca = cos(angle)
    sa = sin(angle)
    quat[0] = ca
    quat[1] = sa*x
    quat[2] = sa*y
    quat[3] = sa*z
    
### module state

def init_engine():
    gldraw.init_gldraw()
    if debug & DEBUG_MSGEXT:
        print('init module:', __name__)
        print('  from package:', __package__)
        print('  compiled:', __compiled)
        #px+print('  GL-type: [[GLVARIANT]]')
        print('  OFFSCREEN:', OFFSCREEN)
        
    uidata.synced = False
    uidata.background_changed = True
    uidata.viewport_changed = True
    uidata.rotation_changed = True
    uidata.rotation_quat_changed = False
    uidata.frustum_changed = True
    uidata.multisample_changed = 1
    uidata.transformation_changed = False
    uidata.animation_changed = 0
    uidata.data_changed = False
    uidata.shaders_changed = False
    
    uidata.bg_red = 0.0
    uidata.bg_green = 0.0
    uidata.bg_blue = 0.0
    renderdata.y = 0
    renderdata.width = 1
    renderdata.height = 1
    uidata.rotationxhalf = 0.0
    uidata.rotationyhalf = 0.0
    quat_set_identity(uidata.rotation_quat)
    uidata.pick_x = -1
    uidata.pick_y = -1
    renderdata.pick_x = -1
    renderdata.pick_y = -1
    #px+cdef float fovy_angle  # field of view angle
    fovy_angle = 33.0  # field of view angle
    uidata.fovy_radius = tan(fovy_angle * M_PI / 360.0)
    uidata.fovy_radius_zoom = uidata.fovy_radius  # zoom == 1.
    uidata.bounding_sphere_radius = 1.
    uidata.angle = 0
    
    # fill modelview_matrix
    gldraw.matrix_set_identity(renderdata.modelview_matrix)
    # fill projection_matrix
    gldraw.matrix_set_identity(renderdata.projection_matrix)
    renderdata.projection_matrix[2][3] = -1.
    renderdata.projection_matrix[3][3] = 0.
    # fill picking_matrix
    gldraw.matrix_set_identity(renderdata.picking_matrix)
    
    renderdata.shader_changed = False
    renderdata.prog_render = 0
    renderdata.prog_hud = 0
    renderdata.prog_pick = 0
    

#pxm-FUNC P nogil
def _sync_modelview_matrix_translation():
    renderdata.modelview_matrix[3][2] = -uidata.bounding_sphere_radius * (1/uidata.fovy_radius + 1.)
    
#pxm-FUNC P nogil
def _sync_modelview_matrix_rotation():
    #px+cdef gldraw.vec4 *M
    #px+cdef float sx, sy, cx, cy
    #px+cdef float m00, m11, m12, m20
    
    #px/M = <gldraw.vec4*>&renderdata.modelview_matrix[0][0]
    M = renderdata.modelview_matrix
    
    sx = sin(uidata.rotationxhalf)
    sy = sin(uidata.rotationyhalf)
    cx = cos(uidata.rotationxhalf)
    cy = cos(uidata.rotationyhalf)
    
    m00 = 2*cx*cx - 1.
    m11 = 2*cy*cy - 1.
    m12 = 2*sy*cy
    m20 = 2*sx*cx
    # pylint: disable=C0321,C0326
    M[0][0] =  m00;         M[1][0] = 0.;   M[2][0] =  m20
    M[0][1] =  m12 * m20;   M[1][1] = m11;  M[2][1] = -m00 * m12
    M[0][2] = -m11 * m20;   M[1][2] = m12;  M[2][2] =  m00 * m11
    
def set_rotation_rel(x, y):
    #px+cdef gldraw.vec4 quat
    #px+cdef float nxy
    nxy = sqrt(x*x + y*y)
    quat_from_angle_axis(quat, nxy/50., y/nxy, x/nxy, 0.)
    quat_mult(uidata.rotation_quat, quat, uidata.rotation_quat)
    
    # avoid rotation about z-axis
    #uidata.rotation_quat[3] = 0.   # 1.
    #uidata.rotation_quat[3] *= .9  # 2.
    #quat_norm(uidata.rotation_quat)
    # 3.
    #quat_from_angle_axis(quat, fabs(uidata.rotation_quat[3])/10., 0., 0., 1.)
    #quat_mult(uidata.rotation_quat, quat, uidata.rotation_quat)
    
    uidata.rotation_quat_changed = True
    
#pxm-FUNC P nogil
def _sync_projection_matrix():
    # Taken from the OpenGL documentation for glFrustum:
    # glFrustum describes a perspective matrix that produces a perspective projection.
    # The current matrix (see glMatrixMode) is multiplied by this matrix and the result
    # replaces the current matrix, as if glMultMatrix were called with the following
    # matrix as its argument:
    #   [ 2*nearVal                     ]
    #   [ ----------     0        A   0 ]
    #   [ right-left                    ]
    #   [            2*nearVal          ]
    #   [     0      ---------    B   0 ]
    #   [            top-bottom         ]   [p00 0   A   0]
    #   [                               ] = [0   p11 B   0]
    #   [     0          0        C   D ]   [0   0   C   D]
    #   [                               ]   [0   0  -1   0]
    #   [                               ]
    #   [     0          0       -1   0 ]
    #   [                               ]
    #
    #     right+left        top+bottom          farVal+nearVal          2*farVal*nearVal
    # A = ----------    B = ----------    C = - --------------    D = - ----------------
    #     right-left        top-bottom          farVal-nearVal           farVal-nearVal
    #
    # Typically, the matrix mode is GL_PROJECTION, and
    # (left, bottom, -nearVal) and (right, top, -nearVal)
    # specify the points on the near clipping plane that are mapped to the lower left and
    # upper right corners of the window, assuming that the eye is located at (0, 0, 0).
    # -farVal specifies the location of the far clipping plane.
    # Both nearVal and farVal must be positive.
    #
    # right == -left, top == -bottom => A = B = 0
    #
    # p00 = 1 / fovy_radius / aspectx
    # p11 = 1 / fovy_radius / aspecty
    # C = -(1/fovy_radius/zoom + 1.)
    # D = -(1/fovy_radius/zoom + 2.) * bounding_sphere_radius/fovy_radius/zoom
    
    #px+cdef float _1_aspectx, _1_aspecty
    if renderdata.width < renderdata.height:
        _1_aspectx = 1.
        _1_aspecty = renderdata.width / renderdata.height
    else:
        _1_aspectx = renderdata.height / renderdata.width
        _1_aspecty = 1.
    renderdata.projection_matrix[0][0] = _1_aspectx / uidata.fovy_radius_zoom
    renderdata.projection_matrix[1][1] = _1_aspecty / uidata.fovy_radius_zoom
    renderdata.projection_matrix[2][2] = -(1/uidata.fovy_radius + 1.)
    renderdata.projection_matrix[3][2] = -(1/uidata.fovy_radius + 2.
                            ) * uidata.bounding_sphere_radius / uidata.fovy_radius
        
#pxm-FUNC P nogil
def _set_picking_matrix(x:int, y:int):
    # Set picking matrix, restrict drawing to one pixel of the viewport
    # same as:  _glLoadIdentity()
    #           _gluPickMatrix(x, y, 1, 1, viewport)
    # same as:  _glLoadIdentity()
    #           _glTranslatef(renderdata.width - 2*x, renderdata.height - 2*y, 0.)
    #           _glScalef(renderdata.width, renderdata.height, 1.0)
    renderdata.picking_matrix[3][0] = renderdata.width - 2*x
    renderdata.picking_matrix[3][1] = renderdata.height - 2*y
    renderdata.picking_matrix[0][0] = renderdata.width
    renderdata.picking_matrix[1][1] = renderdata.height
    
#pxm-FUNC P nogil
def _set_picking_matrix_identity():
    renderdata.picking_matrix[3][0] = 0.
    renderdata.picking_matrix[3][1] = 0.
    renderdata.picking_matrix[0][0] = 1.
    renderdata.picking_matrix[1][1] = 1.
    
def set_frustum(bounding_sphere_radius, zoom):
    uidata.frustum_changed = True
    if bounding_sphere_radius > 0:
        uidata.bounding_sphere_radius = bounding_sphere_radius
    uidata.fovy_radius_zoom = uidata.fovy_radius / zoom
    
def set_background_color(red, green, blue):
    uidata.background_changed = True
    uidata.bg_red = red
    uidata.bg_green = green
    uidata.bg_blue = blue
    
def set_antialiasing(multisample):
    uidata.multisample_changed = 2 if multisample else 1
    
def set_rotation_xy(x, y):
    x %= 360
    # pylint: disable=C0321
    if y < -120:     y = -120
    elif y > 120:    y = 120
    uidata.rotation_changed = True
    uidata.rotationxhalf = M_PI * x / 360.0
    uidata.rotationyhalf = M_PI * y / 360.0
    return x, y
    
#pxm-FUNC PD nogil
def render_resize(y:int, width:int, height:int):
    renderdata.y = y
    renderdata.width = width
    renderdata.height = height
    uidata.viewport_changed = True
    
def set_transformations(blocks):
    assert uidata.nblocks == len(blocks)
    #px+cdef int i
    #px+cdef short b
    for i, b, unused in blocks:
        uidata.transformation_blocks[i] = b
    uidata.transformation_changed = True
    
#pxm-FUNC P
def set_animation_start(blocks, axisx:float, axisy:float, axisz:float)->'cpdef':
    for i, (b, unused) in enumerate(blocks):
        uidata.animation_blocks[i] = b
    uidata.animation_blocks_count = i+1
    uidata.angle = 0.0
    uidata.rotation_x = axisx
    uidata.rotation_y = axisy
    uidata.rotation_z = axisz
    uidata.animation_changed = 1
    
#pxm-FUNC PD
def set_animation_next(angle:float):
    uidata.angle = -angle
    uidata.animation_changed = 2
    
def set_data(nblocks, vertexdata, vertexpointers, vertexinfo, transformations):
    pydata_postsync()
    #px+cdef int t,i,j
    assert nblocks <= gldraw.MAX_BLOCKS, (nblocks, gldraw.MAX_BLOCKS)
    uidata.nblocks = nblocks
    pydata['vertexdata'] = vertexdata
    uidata.vertexdatalen = len(vertexdata)
    uidata.vertexdata = vertexdata
    assert len(vertexpointers) == gldraw.ATTRIB_CNT-1
    for i in range(gldraw.ATTRIB_CNT-1):
        uidata.vertexpointers[i] = vertexpointers[i]
    cnts_block, uidata.idx_debug, uidata.cnt_debug, uidata.cnt_pick = vertexinfo
    assert len(cnts_block) == nblocks
    for i in range(nblocks):
        uidata.cnts_block[i] = cnts_block[i]
    assert len(transformations) <= gldraw.MAX_TRANSFORMATIONS, len(transformations)
    uidata.transformations_count = len(transformations)
    for t in range(uidata.transformations_count):
        for i in range(4):
            for j in range(4):
                uidata.transformations[t][i][j] = float(transformations[t][i][j])
    uidata.data_changed = True
    
def pydata_postsync():
    global pydata_backed
    if uidata.synced:
        pydata_backed = dict(pydata)
        uidata.synced = False
        
def update_shader_version(shader_src):
    assert shader_src.startswith(b'#version 120\n')
    #px/IF '[[GLVARIANT]]' == 'es2':
    if False:
        shader_src = b'#version 100\n' + shader_src.split(b'\n', 1)[1]
    return shader_src
    
def set_shaders(shader_vertsrc, shader_fragsrc):
    pydata_postsync()
    shader_vertsrc = update_shader_version(shader_vertsrc)
    shader_fragsrc = update_shader_version(shader_fragsrc)
    pydata['shader_vertsrc'] = shader_vertsrc
    uidata.shader_vertsrc = shader_vertsrc
    pydata['shader_fragsrc'] = shader_fragsrc
    uidata.shader_fragsrc = shader_fragsrc
    uidata.shaders_changed = True
    
def set_fixedshaders(fixedshaders):
    pydata_postsync()
    fixedshaders = [update_shader_version(s) for s in fixedshaders]
    pydata['fixedshaders'] = fixedshaders
    uidata.pick_vertsrc = fixedshaders[0]
    uidata.pick_fragsrc = fixedshaders[1]
    if debug & DEBUG_DRAW:
        uidata.hud_vertsrc = fixedshaders[2]
        uidata.hud_fragsrc = fixedshaders[1]
    
#pxm-FUNC PD nogil
def sync_set_fixedshaders():
    renderdata.pick_vertsrc = uidata.pick_vertsrc
    renderdata.pick_fragsrc = uidata.pick_fragsrc
    if debug & DEBUG_DRAW:
        renderdata.hud_vertsrc = uidata.hud_vertsrc
        renderdata.hud_fragsrc = uidata.hud_fragsrc
    
def set_pick_position(x, y):
    uidata.pick_x = x
    uidata.pick_y = y
    
#pxm-FUNC PD nogil
def sync_set_pick_position():
    renderdata.pick_x = uidata.pick_x
    renderdata.pick_y = uidata.pick_y
    
#pxm-FUNC P
def set_atlas_data(i:int, data:bytes, x:int, w:int, h:int)->'cpdef':
    assert i < gldraw.MAX_FACES, (i, gldraw.MAX_FACES)
    if i == 0:
        uipydata.atlasdata = []
    uipydata.atlasdata.append(data)
    uidata.atlasdata[i].x = x
    uidata.atlasdata[i].w = w
    uidata.atlasdata[i].h = h
    uidata.atlasdata[i].data = data
    uidata.atlaslen = i+1
    
#pxm-FUNC PD nogil
def sync_set_atlas_data():
    #px+cdef int i
    for i in range(uidata.atlaslen):
        renderdata.atlasdata[i].x = uidata.atlasdata[i].x
        renderdata.atlasdata[i].w = uidata.atlasdata[i].w
        renderdata.atlasdata[i].h = uidata.atlasdata[i].h
        renderdata.atlasdata[i].data = uidata.atlasdata[i].data
    renderdata.atlaslen = uidata.atlaslen
    
#pxm-FUNC PD nogil
def sync():
    uidata.synced = True
    if uidata.background_changed:
        renderdata.bg_red = uidata.bg_red
        renderdata.bg_green = uidata.bg_green
        renderdata.bg_blue = uidata.bg_blue
        uidata.background_changed = False
    if uidata.viewport_changed:
        _sync_projection_matrix()
        uidata.viewport_changed = False
    if uidata.rotation_changed:
        _sync_modelview_matrix_rotation()
        uidata.rotation_changed = False
    if uidata.rotation_quat_changed:
        quat_to_matrix(renderdata.modelview_matrix, uidata.rotation_quat)
        uidata.rotation_quat_changed = False
    if uidata.frustum_changed:
        _sync_modelview_matrix_translation()
        _sync_projection_matrix()
        uidata.frustum_changed = False
    if uidata.multisample_changed:
        renderdata.multisample = uidata.multisample_changed - 1
        uidata.multisample_changed = 0
    if uidata.data_changed:
        gldraw.sync_vertexdata(uidata.vertexdatalen, uidata.vertexdata, uidata.vertexpointers)
        gldraw.sync_blocks(uidata.nblocks, uidata.cnts_block, uidata.idx_debug, uidata.cnt_debug, uidata.cnt_pick)
        gldraw.sync_transformations(uidata.transformations_count, uidata.transformations)
        uidata.data_changed = False
    if uidata.transformation_changed:
        gldraw.sync_block_transformations(uidata.transformation_blocks)
        uidata.transformation_changed = False
    if uidata.animation_changed == 2:
        gldraw.sync_animation_next(uidata.angle, uidata.rotation_x, uidata.rotation_y, uidata.rotation_z)
        uidata.animation_changed = 0
    elif uidata.animation_changed == 1:
        gldraw.sync_animation_start(uidata.animation_blocks_count, uidata.animation_blocks)
        uidata.animation_changed = 0
    if uidata.shaders_changed:
        renderdata.shader_changed = True
        renderdata.shader_vertsrc = uidata.shader_vertsrc
        renderdata.shader_fragsrc = uidata.shader_fragsrc
        uidata.shaders_changed = False
        
### GL state

#pxm-FUNC P nogil
def _gl_print_string(msg:'char *', name:'GLenum'):
    #px/printf('%s %s\n', msg, <char*>glGetString(name))
    printf('%s %s\n', msg, glGetString(name))
    
#pxm-FUNC P nogil
def _gl_print_float(msg:'char *', name:'GLenum'):
    #px+cdef GLfloat i
    #px/glGetFloatv(name, &i)
    i = glGetFloatv(name)
    printf('%s %f\n', msg, i)
    
#pxm-FUNC P nogil
def _gl_print_integer(msg:'char *', name:'GLenum'):
    #px+cdef GLint i
    #px/glGetIntegerv(name, &i)
    i = glGetIntegerv(name)
    printf('%s %d\n', msg, i)
    
#pxm-FUNC P nogil
def _gl_print_bool(msg:'char *', name:'GLenum'):
    #px+cdef GLboolean i
    #px+glGetBooleanv(name, &i)
    #px/printf('%s %d\n', msg, <int>i)
    printf('%s %d\n', msg, glGetBooleanv(name))
    
#pxm-FUNC PD nogil
def gl_init():
    if debug & DEBUG_MSGGL:
        puts('GL Strings:')
        _gl_print_string('  GL Vendor:', GL_VENDOR)
        _gl_print_string('  GL Renderer:', GL_RENDERER)
        _gl_print_string('  GL Version:', GL_VERSION)
        _gl_print_string('  GL Shading Language Version:', GL_SHADING_LANGUAGE_VERSION)
        #_gl_print_string('  GL Extensions:', GL_EXTENSIONS)
        _gl_print_integer('  GL_SAMPLE_BUFFERS:', GL_SAMPLE_BUFFERS)
        _gl_print_float('  GL_SAMPLE_COVERAGE_VALUE:', GL_SAMPLE_COVERAGE_VALUE)
        _gl_print_bool('  GL_SAMPLE_COVERAGE_INVERT:', GL_SAMPLE_COVERAGE_INVERT)
        _gl_print_integer('  GL_SAMPLES:', GL_SAMPLES)
        #px/IF '[[GLVARIANT]]' == 'ogl':
        if True:
            printf('  GL_MULTISAMPLE: %d\n', glIsEnabled(GL_MULTISAMPLE))
        #print('  GL_SAMPLE_ALPHA_TO_COVERAGE:', glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE))
        #print('  GL_SAMPLE_COVERAGE:', glIsEnabled(GL_SAMPLE_COVERAGE))
        _gl_print_integer('  GL_MAX_VERTEX_ATTRIBS:', GL_MAX_VERTEX_ATTRIBS)
        _gl_print_integer('  GL_MAX_TEXTURE_SIZE:', GL_MAX_TEXTURE_SIZE)
    gldraw.gl_init_buffers()
    if debug & DEBUG_DRAW:
        gl_create_hud_program()
    gl_create_pick_program()
    
#pxm-FUNC PD nogil
def gl_exit():
    gldraw.gl_delete_buffers()
    if renderdata.prog_render > 0:  glDeleteProgram(renderdata.prog_render)
    if renderdata.prog_hud > 0:     glDeleteProgram(renderdata.prog_hud)
    if renderdata.prog_pick > 0:    glDeleteProgram(renderdata.prog_pick)
    renderdata.prog_render = 0
    renderdata.prog_hud = 0
    renderdata.prog_pick = 0
    
### render functions

#pxm-FUNC PD nogil
def gl_set_atlas_texture(width:int, height:int):
    #px+cdef int i, x, w, h
    #px+cdef char *data = <char*>malloc(width*height*4)
    #px+memset(data, 0, width*height*4)
    #FIXME: GL_INVALID_OPERATION, but it works, at least on the phone a call to glTexImage2D is needed before glTexSubImage2D
    #px+glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data)
    #px+free(data)
    for i in range(renderdata.atlaslen):
        x, w, h = renderdata.atlasdata[i].x, renderdata.atlasdata[i].w, renderdata.atlasdata[i].h
        data = renderdata.atlasdata[i].data
        glTexSubImage2D(GL_TEXTURE_2D, 0, x, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data)
    
#pxm-FUNC P nogil
def _gl_set_matrix(location:'GLint', matrix:'gldraw.mat4 &'):
    #px/glUniformMatrix4fv(location, 1, GL_FALSE, &matrix[0][0])
    glUniformMatrix4fv(location, 1, GL_FALSE, matrix)
    
#pxm-FUNC PD nogil
def gl_render():
    if renderdata.shader_changed:
        gl_create_render_program()
        renderdata.shader_changed = False
    glEnable(GL_DEPTH_TEST)
    glEnable(GL_CULL_FACE)
    glCullFace(GL_BACK)
    glFrontFace(GL_CCW)
    gldraw.gl_enable_data()
    glViewport(0, renderdata.y, renderdata.width, renderdata.height)
    if debug & DEBUG_PICK:
        _set_picking_matrix_identity()
        _gl_render_pick()
    else:
        glUseProgram(renderdata.prog_render)
        #px/IF '[[GLVARIANT]]' == 'ogl':
        if True:
            if renderdata.multisample:
                glEnable(GL_MULTISAMPLE)
            else:
                glDisable(GL_MULTISAMPLE)
        #px+IF OFFSCREEN:
        #px+    glClearColor(renderdata.bg_red, renderdata.bg_green, renderdata.bg_blue, 0.)
        #px/ELSE:
        if True:
            glClearColor(renderdata.bg_red, renderdata.bg_green, renderdata.bg_blue, 1.)
        _gl_set_matrix(renderdata.projection_location, renderdata.projection_matrix)
        _gl_set_matrix(renderdata.modelview_location, renderdata.modelview_matrix)
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        gldraw.gl_draw_cube()
    if debug & DEBUG_DRAW:
        gldraw.gl_draw_cube_debug()
    glClearColor(0, 0, 0, 0)
    glUseProgram(0)
    gldraw.gl_disable_data()
    glDisable(GL_CULL_FACE)
    glDisable(GL_DEPTH_TEST)
        
#pxm-FUNC PD nogil
def gl_render_select_debug():
    #px/cdef GLfloat selectdata[12]
    selectdata = [0.] * 12
    selectdata[0] = renderdata.debug_modelview1[0]
    selectdata[1] = renderdata.debug_modelview1[1]
    selectdata[2] = renderdata.debug_modelview1[2]
    selectdata[3] = renderdata.debug_modelview2[0]
    selectdata[4] = renderdata.debug_modelview2[1]
    selectdata[5] = renderdata.debug_modelview2[2]
    selectdata[6] = renderdata.debug_viewport1[0] / renderdata.width * 2 - 1
    selectdata[7] = renderdata.debug_viewport1[1] / renderdata.height * 2 - 1
    selectdata[8] = 0
    selectdata[9] = renderdata.debug_viewport2[0] / renderdata.width * 2 - 1
    selectdata[10] = renderdata.debug_viewport2[1] / renderdata.height * 2 - 1
    selectdata[11] = 0
    gldraw.gl_enable_data()
    glUseProgram(renderdata.prog_render)
    glViewport(0, renderdata.y, renderdata.width, renderdata.height)
    #px/gldraw.gl_draw_select_debug(&selectdata[0], sizeof(selectdata), renderdata.prog_hud)
    gldraw.gl_draw_select_debug(selectdata, 0, renderdata.prog_hud)
    glUseProgram(0)
    gldraw.gl_disable_data()
    

### picking functions

#pxm-FUNC P nogil
def _gl_render_pick():
    glUseProgram(renderdata.prog_pick)
    #px/IF '[[GLVARIANT]]' == 'ogl':
    if True:
        glDisable(GL_MULTISAMPLE)
    glClearColor(0., 0., 0., 1.)
    _gl_set_matrix(renderdata.picking_location, renderdata.picking_matrix)
    _gl_set_matrix(renderdata.projection_pick_location, renderdata.projection_matrix)
    _gl_set_matrix(renderdata.modelview_pick_location, renderdata.modelview_matrix)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    gldraw.gl_pick_cube()
    
#pxm-FUNC PD nogil
def gl_pick_polygons()->int:
    #px+cdef unsigned char pixel[4]
    #px+cdef int index
    
    if not (0 <= renderdata.pick_x < renderdata.width and 0 <= renderdata.pick_y < renderdata.height):
        return 0
    _set_picking_matrix(renderdata.pick_x, renderdata.pick_y)
    glEnable(GL_DEPTH_TEST)
    glEnable(GL_CULL_FACE)
    glCullFace(GL_BACK)
    glFrontFace(GL_CCW)
    gldraw.gl_enable_data()
    glViewport(0, 0, 1, 1)
    _gl_render_pick()
    gldraw.gl_disable_data()
    glDisable(GL_CULL_FACE)
    glDisable(GL_DEPTH_TEST)
    #px/glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel)
    pixel = glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, [[[0, 0, 0, 0]]])[0][0]
    return pixel[0]<<4 | pixel[1] | pixel[2]>>4
    
#pxm-FUNC P nogil
def _modelview_to_viewport(vvect:'float *', mvect:'int *'):
    #px+cdef gldraw.vec4 *M
    #px+cdef gldraw.vec4 *P
    #px+cdef float u0, u1, u2, v0, v1, v3
    
    #px/M = <gldraw.vec4*>&renderdata.modelview_matrix[0][0]
    M = renderdata.modelview_matrix
    #px/P = <gldraw.vec4*>&renderdata.projection_matrix[0][0]
    P = renderdata.projection_matrix
    
    # u = M^T * vvect
    #assert M[1][0] == 0
    u0 = M[0][0]*vvect[0] + M[1][0]*vvect[1] + M[2][0]*vvect[2] + M[3][0]
    u1 = M[0][1]*vvect[0] + M[1][1]*vvect[1] + M[2][1]*vvect[2] + M[3][1]
    u2 = M[0][2]*vvect[0] + M[1][2]*vvect[1] + M[2][2]*vvect[2] + M[3][2]
    #u3 = 1.
    
    # v = P * u
    v0 = P[0][0] * u0 + P[1][0] * u1 + P[2][0] * u2 + P[3][0] #* u3
    v1 = P[0][1] * u0 + P[1][1] * u1 + P[2][1] * u2 + P[3][1] #* u3
    #v2 = P[0][2] * u0 + P[1][2] * u1 + P[2][2] * u2 + P[3][2] * u3
    v3 = P[0][3] * u0 + P[1][3] * u1 + P[2][3] * u2 + P[3][3] #* u3
    
    mvect[0] = int((v0 / v3 + 1) / 2 * renderdata.width)
    mvect[1] = int((v1 / v3 + 1) / 2 * renderdata.height)
    
#pxm-FUNC P nogil
def _viewport_to_modelview(vvect:'int *', mvect:'float *'):
    #px+cdef gldraw.vec4 *MT
    #px+cdef gldraw.vec4 *P
    #px+cdef float u0, u1, u2, u3, v0, v1, v2
    
    v0 = vvect[0] / renderdata.width * 2 - 1
    v1 = vvect[1] / renderdata.height * 2 - 1
    v2 = 0
    #v3 = 1
    
    # P * P^-1 = I
    # a 0 0 0     A 0 0 0     aA 0  0  0      E = -eD/c
    # 0 b 0 0  *  0 B 0 0  =  0  bB 0  0 ,    
    # 0 0 e c     0 0 0 D     0  0  cC 0      
    # 0 0 d 0     0 0 C E     0  0  0  dD     
    #px/P = <gldraw.vec4*>&renderdata.projection_matrix[0][0]
    P = renderdata.projection_matrix
    # u = P^-1 * v
    u0 = 1 / P[0][0] * v0
    u1 = 1 / P[1][1] * v1
    u2 = 1 / P[2][3]
    #assert u2 == -1
    u3 = - P[2][2] / P[3][2] / P[2][3]
    
    # MT * MT^-1 = I
    # a b c 0     a 0 f 0     1 0 0 0    U = -cz
    # 0 d e 0  *  b d g 0  =  0 1 0 0,   V = -ez
    # f g h 0     c e h 0     0 0 1 0    W = -hz
    # 0 0 z 1     U V W 1     0 0 0 1
    #px/MT = <gldraw.vec4*>&renderdata.modelview_matrix[0][0]
    MT = renderdata.modelview_matrix
    # v = M^-1 * u
    v0 = MT[0][0]*u0 + MT[0][1]*u1 + MT[0][2]*u2 - MT[0][2] * MT[3][2]*u3
    v1 = MT[1][0]*u0 + MT[1][1]*u1 + MT[1][2]*u2 - MT[1][2] * MT[3][2]*u3
    v2 = MT[2][0]*u0 + MT[2][1]*u1 + MT[2][2]*u2 - MT[2][2] * MT[3][2]*u3
    #v3 = u3
    
    mvect[0] = v0 / u3
    mvect[1] = v1 / u3
    mvect[2] = v2 / u3
    
def get_cursor_angle(d):
    #px+cdef float angle
    #px+cdef int i
    #px+cdef float x, y
    renderdata.debug_viewport1[0] = uidata.pick_x
    renderdata.debug_viewport1[1] = uidata.pick_y
    _viewport_to_modelview(renderdata.debug_viewport1, renderdata.debug_modelview1)
    for i in range(3):
        renderdata.debug_modelview2[i] = d[i] + renderdata.debug_modelview1[i]
    _modelview_to_viewport(renderdata.debug_modelview2, renderdata.debug_viewport2)
    x = renderdata.debug_viewport1[0] - renderdata.debug_viewport2[0]
    y = renderdata.debug_viewport1[1] - renderdata.debug_viewport2[1]
    angle = atan2(x, y) * 180.0 / M_PI
    return angle
    
    
### shader functions

#pxm-FUNC P nogil
def _gl_print_shader_log(shader:'GLuint'):
    #px+cdef GLint log_len
    #px+cdef GLsizei length
    #px+cdef char log[1024]
    #px/glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_len)
    log_len = glGetShaderiv(shader, GL_INFO_LOG_LENGTH)
    if log_len > 0:
        puts('==== shader info log:')
        #px/glGetShaderInfoLog(shader, 1023, &length, log)
        log = glGetShaderInfoLog(shader).decode('utf-8')
        printf('%s', log)
        puts('====')
    else:
        puts('==== empty shader info log')
        
#pxm-FUNC P nogil
def _gl_print_program_log(program:'GLuint'):
    #px+cdef GLint log_len
    #px+cdef GLsizei length
    #px+cdef char log[1024]
    #px/glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_len)
    log_len = glGetProgramiv(program, GL_INFO_LOG_LENGTH)
    if log_len > 0:
        puts('==== program info log:')
        #px/glGetProgramInfoLog(program, 1023, &length, log)
        log = glGetProgramInfoLog(program).decode('utf-8')
        printf('%s', log)
        puts('====')
    else:
        puts('==== empty program info log')
        
#pxm-FUNC P nogil
def _gl_create_compiled_shader(shadertype:'GLenum', source:'char *')->'GLuint':
    #px+cdef GLuint shader
    #px+cdef const_GLchar_ptr pchar
    #px+cdef GLint compile_status
    
    shader = glCreateShader(shadertype)
    if shader == 0:
        puts('Failed to create shader')
        return 0
    #px+pchar = <const_GLchar_ptr><char*>source
    #px/glShaderSource(shader, 1, &pchar, NULL)
    glShaderSource(shader, source.decode('utf-8')) # PyOpenGL needs str currently
    glCompileShader(shader)
    if debug & DEBUG_MSGGL:
        #px/glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &compile_status)
        compile_status = glGetShaderiv(shader, GL_INFO_LOG_LENGTH)
        printf('GL_INFO_LOG_LENGTH %d\n', compile_status)
        compile_status = compile_status <= 1
        if compile_status:
            #px/glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status)
            compile_status = glGetShaderiv(shader, GL_COMPILE_STATUS)
    else:
        #px/glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status)
        compile_status = glGetShaderiv(shader, GL_COMPILE_STATUS)
    if not compile_status:
        _gl_print_shader_log(shader)
    return shader
    
#pxm-FUNC P nogil
def _get_program_iv(program:'GLuint', pname:'GLenum')->'GLint':
    #px+cdef GLint param
    #px/glGetProgramiv(program, pname, &param)
    param = int(glGetProgramiv(program, pname))
    return param
    
#pxm-FUNC P nogil
def _gl_print_program_info(program:'GLuint')->'GLint':
    #px+cdef GLint link_status, info_log_length, param, aaml, auml
    #px+cdef int i
    #px+cdef GLsizei alength
    #px+cdef GLint asize, location
    #px+cdef GLenum atype
    #px+cdef char aname[1024]
    
    printf('shader program info %d\n', program)
    glValidateProgram(program)
    printf('  delete status %d\n',   _get_program_iv(program, GL_DELETE_STATUS))
    link_status = _get_program_iv(program, GL_LINK_STATUS)
    printf('  link status %d\n',     link_status)
    printf('  validate status %d\n', _get_program_iv(program, GL_VALIDATE_STATUS))
    info_log_length = _get_program_iv(program, GL_INFO_LOG_LENGTH)
    printf('  info log length %d\n', info_log_length)
    printf('  attached shaders %d\n',_get_program_iv(program, GL_ATTACHED_SHADERS))
    aaml = _get_program_iv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH)
    printf('  active attribute max length %d\n', aaml)
    auml = _get_program_iv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH)
    printf('  active uniform max length %d\n', auml)
    
    #px/glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &param)
    param = glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES)
    printf('active attributes %d:\n', param)
    for i in range(param):
        #px/glGetActiveAttrib(program, i, 1023, &alength, &asize, &atype, aname)
        aname, asize, atype = glGetActiveAttrib(program, i); alength = len(aname)
        location = glGetAttribLocation(program, aname)
        printf('  %d %-*s length=%d size=%d type=%d location=%d\n', i, aaml+3, aname, alength, asize, atype, location)
        
    #px/glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &param)
    param = glGetProgramiv(program, GL_ACTIVE_UNIFORMS)
    printf('active uniforms %d:\n', param)
    for i in range(param):
        #px/glGetActiveUniform(program, i, 1023, &alength, &asize, &atype, aname)
        aname, asize, atype = glGetActiveUniform(program, i); alength = len(aname)
        location = glGetUniformLocation(program, aname)
        printf('  %d %-*s length=%d size=%d type=%d location=%d\n', i, auml+3, aname, alength, asize, atype, location)
    return link_status and info_log_length <= 1
        
#pxm-FUNC P nogil
def _gl_program_add_shaders(program:'GLuint', vertex_source:'char *', fragment_source:'char *'):
    #px+cdef GLuint vertex_shader = 0, fragment_shader = 0
    #px+cdef GLint link_status
    
    if debug & DEBUG_MSGGL: puts('  creating vertex shader')
    vertex_shader = _gl_create_compiled_shader(GL_VERTEX_SHADER, vertex_source)
    if vertex_shader:
        glAttachShader(program, vertex_shader)
    else:
        puts('error: no vertex shader')
        
    if debug & DEBUG_MSGGL: puts('  creating fragment shader')
    fragment_shader = _gl_create_compiled_shader(GL_FRAGMENT_SHADER, fragment_source)
    if fragment_shader:
        glAttachShader(program, fragment_shader)
    else:
        puts('error: no fragment shader')
        
    glLinkProgram(program)
    if debug & DEBUG_MSGGL:
        link_status = _gl_print_program_info(program)
    else:
        #px/glGetProgramiv(program, GL_LINK_STATUS, &link_status)
        link_status = glGetProgramiv(program, GL_LINK_STATUS)
    if not link_status:
        printf('link status %d\n', link_status)
        _gl_print_program_log(program)
    if vertex_shader:
        glDetachShader(program, vertex_shader)
        glDeleteShader(vertex_shader)
    if fragment_shader:
        glDetachShader(program, fragment_shader)
        glDeleteShader(fragment_shader)
    
#pxm-FUNC PD nogil
def gl_create_render_program():
    #px+cdef GLint location
    if debug & DEBUG_MSGGL:  puts('Creating render shaders:')
    if renderdata.prog_render > 0:
        glDeleteProgram(renderdata.prog_render)
    renderdata.prog_render = glCreateProgram()
    glBindAttribLocation(renderdata.prog_render, gldraw.ATTRIB_LOCATION+0, b'vertex_attr')
    glBindAttribLocation(renderdata.prog_render, gldraw.ATTRIB_LOCATION+1, b'normal_attr')
    glBindAttribLocation(renderdata.prog_render, gldraw.ATTRIB_LOCATION+2, b'color_attr')
    glBindAttribLocation(renderdata.prog_render, gldraw.ATTRIB_LOCATION+3, b'texcoord_attr')
    glBindAttribLocation(renderdata.prog_render, gldraw.ATTRIB_LOCATION+4, b'barycentric_attr')
    _gl_program_add_shaders(renderdata.prog_render, renderdata.shader_vertsrc, renderdata.shader_fragsrc)
    if renderdata.prog_render > 0:
        glUseProgram(renderdata.prog_render)
        location = glGetUniformLocation(renderdata.prog_render, b'tex')
        glUniform1i(location, 0) # 0 is the texture unit (-> glActiveTexture)
        renderdata.modelview_location = glGetUniformLocation(renderdata.prog_render, b'modelview')
        renderdata.projection_location = glGetUniformLocation(renderdata.prog_render, b'projection')
        location = glGetUniformLocation(renderdata.prog_render, b'object')
        gldraw.gl_init_object_location(location)
        
#pxm-FUNC P nogil
def gl_create_hud_program():
    #px+cdef GLint location
    if debug & DEBUG_MSGGL: puts('Creating "hud" shaders:')
    if renderdata.prog_hud > 0:
        glDeleteProgram(renderdata.prog_hud)
    renderdata.prog_hud = glCreateProgram()
    glBindAttribLocation(renderdata.prog_hud, gldraw.ATTRIB_LOCATION+0, b'vertex_attr')
    glBindAttribLocation(renderdata.prog_hud, gldraw.ATTRIB_LOCATION+2, b'color_attr')
    _gl_program_add_shaders(renderdata.prog_hud, renderdata.hud_vertsrc, renderdata.hud_fragsrc)
    if renderdata.prog_hud > 0:
        glUseProgram(renderdata.prog_hud)
    
#pxm-FUNC P nogil
def gl_create_pick_program():
    if debug & DEBUG_MSGGL: puts('Creating "pick" shaders:')
    if renderdata.prog_pick > 0:
        glDeleteProgram(renderdata.prog_pick)
    renderdata.prog_pick = glCreateProgram()
    glBindAttribLocation(renderdata.prog_pick, gldraw.PICKATTRIB_LOCATION+0, b'vertex_attr')
    glBindAttribLocation(renderdata.prog_pick, gldraw.PICKATTRIB_LOCATION+1, b'color_attr')
    _gl_program_add_shaders(renderdata.prog_pick, renderdata.pick_vertsrc, renderdata.pick_fragsrc)
    if renderdata.prog_pick > 0:
        glUseProgram(renderdata.prog_pick)
        renderdata.picking_location = glGetUniformLocation(renderdata.prog_pick, b'picking')
        renderdata.projection_pick_location = glGetUniformLocation(renderdata.prog_pick, b'projection')
        renderdata.modelview_pick_location = glGetUniformLocation(renderdata.prog_pick, b'modelview')
    

