1#!/usr/bin/env python3
2
3#
4#   This file is part of Magnum.
5#
6#   Original authors — credit is appreciated but not required:
7#
8#       2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 —
9#           Vladimír Vondruš <mosra@centrum.cz>
10#
11#   This is free and unencumbered software released into the public domain.
12#
13#   Anyone is free to copy, modify, publish, use, compile, sell, or distribute
14#   this software, either in source code form or as a compiled binary, for any
15#   purpose, commercial or non-commercial, and by any means.
16#
17#   In jurisdictions that recognize copyright laws, the author or authors of
18#   this software dedicate any and all copyright interest in the software to
19#   the public domain. We make this dedication for the benefit of the public
20#   at large and to the detriment of our heirs and successors. We intend this
21#   dedication to be an overt act of relinquishment in perpetuity of all
22#   present and future rights to this software under copyright law.
23#
24#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25#   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26#   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27#   THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
28#   IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29#   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30#
31
32import os
33
34from magnum import *
35from magnum import gl, meshtools, scenegraph, shaders, trade
36from magnum.platform.sdl2 import Application
37from magnum.scenegraph.matrix import Scene3D, Object3D
38
39class ColoredDrawable(scenegraph.Drawable3D):
40    def __init__(self, object: Object3D, drawables: scenegraph.DrawableGroup3D,
41                 mesh: gl.Mesh, shader: shaders.Phong, color: Color4):
42        scenegraph.Drawable3D.__init__(self, object, drawables)
43
44        self._mesh = mesh
45        self._shader = shader
46        self._color = color
47
48    def draw(self, transformation_matrix: Matrix4, camera: scenegraph.Camera3D):
49        self._shader.light_positions = [
50            camera.camera_matrix.transform_point((-3.0, 10.0, 10.0))
51        ]
52        self._shader.diffuse_color = self._color
53        self._shader.transformation_matrix = transformation_matrix
54        self._shader.normal_matrix = transformation_matrix.rotation_scaling()
55        self._shader.projection_matrix = camera.projection_matrix
56        self._mesh.draw(self._shader)
57
58class ViewerExample(Application):
59    def __init__(self):
60        configuration = self.Configuration()
61        configuration.title = "Magnum Python Viewer Example"
62        Application.__init__(self, configuration)
63
64        gl.Renderer.enable(gl.Renderer.Feature.DEPTH_TEST)
65        gl.Renderer.enable(gl.Renderer.Feature.FACE_CULLING)
66
67        # Scene and drawables
68        self._scene = Scene3D()
69        self._drawables = scenegraph.DrawableGroup3D()
70
71        # Every scene needs a camera
72        camera_object = Object3D(parent=self._scene)
73        camera_object.translate(Vector3.z_axis(5.0))
74        self._camera = scenegraph.Camera3D(camera_object)
75        self._camera.aspect_ratio_policy = scenegraph.AspectRatioPolicy.EXTEND
76        self._camera.projection_matrix = Matrix4.perspective_projection(
77            fov=Deg(35.0), aspect_ratio=1.0, near=0.01, far=100.0)
78        self._camera.viewport = self.framebuffer_size
79
80        # Base object, parent of all (for easy manipulation)
81        self._manipulator = Object3D(parent=self._scene)
82
83        # Setup renderer and shader defaults
84        gl.Renderer.enable(gl.Renderer.Feature.DEPTH_TEST)
85        gl.Renderer.enable(gl.Renderer.Feature.FACE_CULLING)
86        colored_shader = shaders.Phong()
87        colored_shader.ambient_color = Color3(0.06667)
88        colored_shader.shininess = 80.0
89
90        # Import Suzanne head and eyes (yes, sorry, it's all hardcoded here)
91        importer = trade.ImporterManager().load_and_instantiate('TinyGltfImporter')
92        importer.open_file(os.path.join(os.path.dirname(__file__),
93                                        '../viewer/scene.glb'))
94        suzanne_object = Object3D(parent=self._manipulator)
95        suzanne_mesh = meshtools.compile(
96            importer.mesh3d(importer.mesh3d_for_name('Suzanne')))
97        suzanne_eyes_mesh = meshtools.compile(
98            importer.mesh3d(importer.mesh3d_for_name('Eyes')))
99        self._suzanne = ColoredDrawable(suzanne_object, self._drawables,
100            suzanne_mesh, colored_shader, Color3(0.15, 0.49, 1.0))
101        self._suzanne_eyes = ColoredDrawable(suzanne_object, self._drawables,
102            suzanne_eyes_mesh, colored_shader, Color3(0.95))
103
104        self._previous_mouse_position = Vector2i()
105
106    def draw_event(self):
107        gl.default_framebuffer.clear(gl.FramebufferClear.COLOR|
108                                     gl.FramebufferClear.DEPTH)
109
110        self._camera.draw(self._drawables)
111        self.swap_buffers()
112
113    def mouse_move_event(self, event: Application.MouseMoveEvent):
114        if event.buttons & self.MouseMoveEvent.Buttons.LEFT:
115            delta = 1.0*(
116                Vector2(event.position - self._previous_mouse_position)/
117                Vector2(self.window_size))
118            self._manipulator.rotate_y_local(Rad(delta.x))
119            self._manipulator.rotate_x(Rad(delta.y))
120            self.redraw()
121
122        self._previous_mouse_position = event.position
123
124    def mouse_scroll_event(self, event: Application.MouseScrollEvent):
125        if not event.offset.y: return
126
127        # Distance to origin
128        distance = self._camera.object.transformation.translation.z
129
130        # Move 15% of the distance back or forward
131        self._camera.object.translate(Vector3.z_axis(
132            distance*(1.0 - (1.0/0.85 if event.offset.y > 0 else 0.85))))
133
134        self.redraw()
135
136exit(ViewerExample().exec())
137