#+TITLE: Drawing a cube while rendering widgets
#+DATE: 2014-08-08 12:00:00 UTC
#+DESCRIPTION: Kivy example on setting up a display and drawing a basic triangle
#+FILETAGS: python:opengl:kivy:glsl
#+CATEGORY: python
#+SLUG: drawing-a-cube-while-rendering-widgets
#+BEGIN_COMMENT
.. title: Drawing a cube while rendering widgets
.. slug: drawing-a-cube-while-rendering-widgets
.. date: 2014-08-08 12:00:00 UTC
.. tags: python, opengl, kivy, glsl
.. category: python
.. description: Kivy example on setting up a display and drawing a basic triangle
.. type: text
#+END_COMMENT

#+CAPTION: Cube with widgets
[[../../../images/kivy/cube-with-widgets.png]]

[[http://bazaar.launchpad.net/~oly/python-examples/trunk/files/head:/python-examples/kivy/'][Example shaded 3D cube]]


Now we will expand on the previous example and add a cube class which will give us a cube to work with.
 
In this example we use a .kv file and create some widgets we also load a custom widget to load our scene into.

This should give you a good idea of rendering your own scene and usig kivy widgets to control the scene if you need to later on.

Pack a load of widgets around our model, we will not do anything with these other than draw them to the screen.

#+BEGIN_SRC python :tangle kivy-cube-widget-example.py
import kivy
kivy.require('1.0.7')

from kivy.app import App
from opengl_widget import OpenglWidget


class DemoApp(App):
    pass

if __name__ == '__main__':
    DemoApp().run()
#+END_SRC
        
below is the interface fille that is loaded to pack a load of widgets around our model, we will not do anything with these other than display them.

#+BEGIN_SRC yaml :tangle kivy-cube-widget-example.kv
#:kivy 1.0
FloatLayout:
    GridLayout:
        cols: 1
        row_force_default: False
        padding: 5
        BoxLayout:
            height: 80
            size_hint_y: None
            Button:
                text: 'Button 1'
            Button:
                text: 'Button 2'
        BoxLayout:
            Accordion:
                orientation: 'vertical'
                AccordionItem:
                    title: 'Panel 1'
                    Button:
                        text: 'Button 1'
                    Button:
                        text: 'Button 2'
                    Button:
                        text: 'Button 3'

                AccordionItem:
                    title: 'Panel 2'
                    Button:
                        text: 'Button 4'
                    Button:
                        text: 'Button 5'
                    Button:
                        text: 'Button 6'

                AccordionItem:
                    title: 'Panel 3'
                    Button:
                        text: 'Button 7'
                    Button:
                        text: 'Button 8'
                    Button:
                        text: 'Button 9'
            OpenglWidget:
                width: 200
                height: 200
            TreeView:
                label: 'Toolsets'
        BoxLayout:
            height: 40
            size_hint_y: None
            Button:
                text: 'Button 1'
            Button:
                text: 'button 2'
#+END_SRC
 
Very simple solid colour shader for our cube.

#+BEGIN_SRC glsl :tangle kivy-cube-widget-example.glsl
---VERTEX SHADER-------------------------------------------------------
#ifdef GL_ES
    precision highp float;
#endif

attribute vec3  v_pos;
attribute vec4  v_color;

uniform mat4 modelview_mat;
uniform mat4 projection_mat;

varying vec4 frag_color;

void main (void) {
    vec4 pos = modelview_mat * vec4(v_pos,1.0);
    gl_Position = projection_mat * pos;
    frag_color = v_color;
}


---FRAGMENT SHADER-----------------------------------------------------
#ifdef GL_ES
    precision highp float;
#endif

varying vec4 frag_color;
varying vec2 uv_vec;

uniform sampler2D tex;

void main (void){
    gl_FragColor = frag_color;
}
#+END_SRC

This is the meat of the code it creates a custom widget, it gets loaded from the interface file above and then handlers rendering the scene .

#+BEGIN_SRC python :tangle opengl_widget.py
import os
import sys
from kivy.app import App
from kivy.clock import Clock
from kivy.core.window import Window
from kivy.core.image import Image
from kivy.uix.widget import Widget
from kivy.resources import resource_find
from kivy.graphics.transformation import Matrix
from kivy.graphics.opengl import *
from kivy.graphics import *

from kivy.uix.widget import Widget
from kivy.graphics import Color, Ellipse

from numpy import array

class point:
    __slots__ = ['x', 'y', 'z', 'xyz', 'vertex']

    def __init__(self, p, c=(1, 0, 0)):
        """ Position in 3d space as a tuple or list, and colour in tuple or list format"""
        self.x, self.y, self.z = p
        self.vertex = array([self.x, self.y, self.z, c[0], c[1], c[2]], 'f')

class cube:
    def __init__(self, p1, color, size=0.5):
        self.color = array([1, 0, 0], 'f')
        self.points = (
            point((p1[0] - size, p1[1] + size, p1[2] - size), (color)),
            point((p1[0] - size, p1[1] + size, p1[2] + size), (color)), 
            point((p1[0] + size, p1[1] + size, p1[2] + size), (color)),
            point((p1[0] + size, p1[1] + size, p1[2] - size), (color)),
            
            point((p1[0] - size, p1[1] - size, p1[2] - size), (color)),
            point((p1[0] - size, p1[1] - size, p1[2] + size), (color)), 
            point((p1[0] + size, p1[1] - size, p1[2] + size), (color)),
            point((p1[0] + size, p1[1] - size, p1[2] - size), (color)),
            
            )

    def get_data(self):
        return (
            self.points[0].vertex, self.points[2].vertex, self.points[1].vertex, 
            self.points[0].vertex, self.points[3].vertex, self.points[2].vertex, 

            self.points[0].vertex, self.points[1].vertex, self.points[5].vertex, 
            self.points[0].vertex, self.points[5].vertex, self.points[4].vertex,

            self.points[0].vertex, self.points[7].vertex, self.points[3].vertex, 
            self.points[0].vertex, self.points[4].vertex, self.points[7].vertex,

            self.points[6].vertex, self.points[2].vertex, self.points[3].vertex, 
            self.points[6].vertex, self.points[3].vertex, self.points[7].vertex, 

            self.points[6].vertex, self.points[1].vertex, self.points[2].vertex,
            self.points[6].vertex, self.points[5].vertex, self.points[1].vertex,

            self.points[6].vertex, self.points[4].vertex, self.points[5].vertex,
            self.points[6].vertex, self.points[7].vertex, self.points[4].vertex,
        )



class OpenglWidget(Widget):
    def __init__(self, **kwargs):
        self.canvas = RenderContext(compute_normal_mat=True)
        self.canvas.shader.source = resource_find('kivy.glsl')

        self.c = cube((0, 0, 0), (1, 0, 0), 2.0)
        self.vertices = []
        for item in self.c.get_data():
            for a in item:
                self.vertices.append(a)
            self.vertices.append(1) # add alpha

        self.indices = range(0, len(self.vertices))

        with self.canvas:
            self.cb = Callback(self.setup_gl_context)
            PushMatrix()
            self.setup_scene()
            PopMatrix()
            self.cb = Callback(self.reset_gl_context)
        Clock.schedule_interval(self.update_glsl, 1 / 60.)

    def setup_gl_context(self, *args):
        glEnable(GL_DEPTH_TEST)

    def reset_gl_context(self, *args):
        glDisable(GL_DEPTH_TEST)

    def update_glsl(self, *largs):
        aspect = float(self.height) / float(self.width)
        projection_mat = Matrix()
        projection_mat.perspective(45.0, aspect, 1.0, 80.0)
        model = Matrix().look_at(
            0.0, 0.0, 25.0,
            0.0, 0.0, 0.0,
            0.0, 1.0, 0.0)

        self.canvas['projection_mat'] = projection_mat
        self.canvas['modelview_mat'] = model
        self.rot.angle += 1

    def setup_scene(self):
        Color(0, 0, 0, 1)
        PushMatrix()
        self.rot = Rotate(1, 0, 1, 0)

        vertex_format = [
            ('v_pos', 3, 'float'),
            ('v_color', 4, 'float'),
        ]

        UpdateNormalMatrix()
        self.mesh = Mesh(
            vertices=self.vertices,
            indices=self.indices,
            fmt=vertex_format,
            mode='triangles',
        )
        PopMatrix()
#+END_SRC
