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