static-sites/bases/do-blog/resources/documents/python/kivy/draw-a-cube-while-rendering...

8.2 KiB
Executable File

Drawing a cube while rendering widgets

.. 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

/oly/static-sites/src/commit/cc0aa6aeb199bfc18658350d8b81a7ab6c634519/bases/do-blog/resources/images/kivy/cube-with-widgets.png
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()