Drawing a cube while rendering widgets



cube-with-widgets.png

Figure 1: Cube with widgets

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.

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()

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.

#: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'

Very simple solid colour shader for our cube.

---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;
}

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 .

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()

Comments