Welcome to the exciting new Python-flavored future of Magnum! Have fun, but please note this functionality is heavily experimental at the moment. Most APIs are missing, documentation is very sparse and everything is still evolving. Use at your own risk.

Examples

Examples for the Python bindings.

The magnum-examples repository contains a few examples in pure Python in the src/python/ directory. These currently mirror the C++ examples and show how to achieve the same in Python.

Your First Triangle

Basic rendering with builtin shaders. Fully equivalent to the C++ version.

import array

from magnum import *
from magnum import gl, platform, shaders

class TriangleExample(platform.Application):
    def __init__(self):
        configuration = self.Configuration()
        configuration.title = "Magnum Python Triangle Example"
        platform.Application.__init__(self, configuration)

        buffer = gl.Buffer()
        buffer.set_data(array.array('f', [
            -0.5, -0.5, 1.0, 0.0, 0.0,
             0.5, -0.5, 0.0, 1.0, 0.0,
             0.0,  0.5, 0.0, 0.0, 1.0
        ]))

        self._mesh = gl.Mesh()
        self._mesh.count = 3
        self._mesh.add_vertex_buffer(buffer, 0, 5*4,
            shaders.VertexColor2D.POSITION)
        self._mesh.add_vertex_buffer(buffer, 2*4, 5*4,
            shaders.VertexColor2D.COLOR3)

        self._shader = shaders.VertexColor2D()

    def draw_event(self):
        gl.default_framebuffer.clear(gl.FramebufferClear.COLOR)

        self._mesh.draw(self._shader)
        self.swap_buffers()

exit(TriangleExample().exec())

Primitives

Importing mesh data, 3D transformations and input handling. Equivalent to the C++ version except that it uses meshtools.compile() instead of interleaving the data by hand — the low-level MeshTools APIs are not exposed yet.

from magnum import *
from magnum import gl, meshtools, platform, primitives, shaders

class PrimitivesExample(platform.Application):
    def __init__(self):
        configuration = self.Configuration()
        configuration.title = "Magnum Python Primitives Example"
        platform.Application.__init__(self, configuration)

        gl.Renderer.enable(gl.Renderer.Feature.DEPTH_TEST)
        gl.Renderer.enable(gl.Renderer.Feature.FACE_CULLING)

        self._mesh = meshtools.compile(primitives.cube_solid())
        self._shader = shaders.Phong()

        self._transformation = (
            Matrix4.rotation_x(Deg(30.0))@
            Matrix4.rotation_y(Deg(40.0)))
        self._projection = (
            Matrix4.perspective_projection(
                fov=Deg(35.0), aspect_ratio=1.33333, near=0.01, far=100.0)@
            Matrix4.translation(Vector3.z_axis(-10.0)))
        self._color = Color3.from_hsv(Deg(35.0), 1.0, 1.0)
        self._previous_mouse_position = Vector2i()

    def draw_event(self):
        gl.default_framebuffer.clear(gl.FramebufferClear.COLOR|
                                     gl.FramebufferClear.DEPTH)

        self._shader.light_positions = [(7.0, 5.0, 2.5)]
        self._shader.light_colors = [Color3(1.0)]
        self._shader.diffuse_color = self._color
        self._shader.ambient_color = Color3.from_hsv(self._color.hue(), 1.0, 0.3)
        self._shader.transformation_matrix = self._transformation
        self._shader.normal_matrix = self._transformation.rotation_scaling()
        self._shader.projection_matrix = self._projection

        self._mesh.draw(self._shader)
        self.swap_buffers()

    def mouse_release_event(self, event: platform.Application.MouseEvent):
        self._color = Color3.from_hsv(self._color.hue() + Deg(50.0), 1.0, 1.0)
        self.redraw()

    def mouse_move_event(self, event: platform.Application.MouseMoveEvent):
        if event.buttons & self.MouseMoveEvent.Buttons.LEFT:
            delta = 1.0*(
                Vector2(event.position - self._previous_mouse_position)/
                Vector2(self.window_size()))
            self._transformation = (
                Matrix4.rotation_x(Rad(delta.y))@
                self._transformation@
                Matrix4.rotation_y(Rad(delta.x)))
            self.redraw()

        self._previous_mouse_position = event.position

exit(PrimitivesExample().exec())

Primitives, using a scene graph

Same behavior as above, but this time handling transformations using the scene graph. Compared to doing the same in C++ there’s less worrying about data ownership, as the reference counting handles most of it.

from magnum import *
from magnum import gl, meshtools, platform, primitives, scenegraph, shaders
from magnum.scenegraph.matrix import Scene3D, Object3D

class CubeDrawable(scenegraph.Drawable3D):
    def __init__(self, object: Object3D, drawables: scenegraph.DrawableGroup3D,
                 mesh: gl.Mesh, shader: shaders.Phong, color: Color4):
        scenegraph.Drawable3D.__init__(self, object, drawables)

        self._mesh = mesh
        self._shader = shader
        self.color = color # Settable from outside

    def draw(self, transformation_matrix: Matrix4, camera: scenegraph.Camera3D):
        self._shader.light_positions = [
            camera.camera_matrix.transform_point((7.0, 5.0, 2.5))
        ]
        self._shader.light_colors = [Color3(1.0)]
        self._shader.diffuse_color = self.color
        self._shader.ambient_color = Color3.from_hsv(self.color.hue(), 1.0, 0.3)
        self._shader.transformation_matrix = transformation_matrix
        self._shader.normal_matrix = transformation_matrix.rotation_scaling()
        self._shader.projection_matrix = camera.projection_matrix
        self._mesh.draw(self._shader)

class PrimitivesSceneGraphExample(platform.Application):
    def __init__(self):
        configuration = self.Configuration()
        configuration.title = "Magnum Python Primitives + SceneGraph Example"
        platform.Application.__init__(self, configuration)

        gl.Renderer.enable(gl.Renderer.Feature.DEPTH_TEST)
        gl.Renderer.enable(gl.Renderer.Feature.FACE_CULLING)

        # Scene and drawables
        self._scene = Scene3D()
        self._drawables = scenegraph.DrawableGroup3D()

        # Camera setup
        camera_object = Object3D(self._scene)
        camera_object.translate(Vector3.z_axis(10.0))
        self._camera = scenegraph.Camera3D(camera_object)
        self._camera.projection_matrix = Matrix4.perspective_projection(
            fov=Deg(35.0), aspect_ratio=1.33333, near=0.01, far=100.0)

        # Cube object and drawable
        self._cube = Object3D(self._scene)
        self._cube.rotate_y(Deg(40.0))
        self._cube.rotate_x(Deg(30.0))
        self._cube_drawable = CubeDrawable(self._cube, self._drawables,
            meshtools.compile(primitives.cube_solid()), shaders.Phong(),
            Color3.from_hsv(Deg(35.0), 1.0, 1.0))

        self._previous_mouse_position = Vector2i()

    def draw_event(self):
        gl.default_framebuffer.clear(gl.FramebufferClear.COLOR|
                                     gl.FramebufferClear.DEPTH)

        self._camera.draw(self._drawables)
        self.swap_buffers()

    def mouse_release_event(self, event: platform.Application.MouseEvent):
        self._cube_drawable.color = Color3.from_hsv(
            self._cube_drawable.color.hue() + Deg(50.0), 1.0, 1.0)
        self.redraw()

    def mouse_move_event(self, event: platform.Application.MouseMoveEvent):
        if event.buttons & self.MouseMoveEvent.Buttons.LEFT:
            delta = 1.0*(
                Vector2(event.position - self._previous_mouse_position)/
                Vector2(self.window_size()))
            self._cube.rotate_y_local(Rad(delta.x))
            self._cube.rotate_x(Rad(delta.y))
            self.redraw()

        self._previous_mouse_position = event.position

exit(PrimitivesSceneGraphExample().exec())