#+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