def test_translation( self ): root = SceneNode( '/root' ) child = SceneNode( '/child' ) root.add_child( child ) # # Initial state # test_translation( self, root.transform, [0.0, 0.0, 0.0] ) test_translation( self, root.world_transform, [0.0, 0.0, 0.0] ) test_translation( self, child.transform, [0.0, 0.0, 0.0] ) test_translation( self, child.world_transform, [0.0, 0.0, 0.0] ) # # Root Translate += 1.0, 1.0, 1.0 # root.transform.translation += [1.0, 1.0, 1.0] test_translation( self, root.transform, [1.0, 1.0, 1.0] ) test_translation( self, root.world_transform, [1.0, 1.0, 1.0] ) test_translation( self, child.transform, [0.0, 0.0, 0.0] ) test_translation( self, child.world_transform, [1.0, 1.0, 1.0] ) # # Child Translate -= 1.0, 1.0, 1.0 # child.transform.translation -= [1.0, 1.0, 1.0] test_translation( self, root.transform, [1.0, 1.0, 1.0] ) test_translation( self, root.world_transform, [1.0, 1.0, 1.0] ) test_translation( self, child.transform, [-1.0,-1.0,-1.0] ) test_translation( self, child.world_transform, [0.0, 0.0, 0.0] )
def test_child( self ): root = SceneNode( '/root' ) child = SceneNode( '/child' ) root.add_child( child ) test_initial_state( self, root ) test_initial_state( self, child )
def setup_scene( self ): # create an fps display self.fps_display = pyglet.clock.ClockDisplay() # create a list of renderables self.renderables = [] # create a scene self.scene_node = SceneNode( 'root' ) self.grid_node = SceneNode( 'grid' ) self.scene_node.add_child( self.grid_node ) self.grid_render_node = RenderCallbackNode( 'mesh', grid.initialise_grid, grid.render_grid ) self.grid_node.add_child( self.grid_render_node ) # add to our list of renderables self.renderables.append( self.grid_render_node ) # move the grid backward so we can see it # and move it down so we start above it self.grid_node.transform.inertial.translate( [ 0.0, 0.0, -80.0 ] ) # create a camera and a view matrix self.view_matrix = ProjectionViewMatrix( self.viewport.aspect_ratio, fov = 45.0, near_clip = 1.0, far_clip = 200.0 ) # create a camera self.camera = CameraNode( 'camera', self.view_matrix ) self.scene_node.add_child( self.camera ) # move the camera up so it starts above the grid self.camera.transform.inertial.translate( [ 0.0, 20.0, 0.0 ] ) # assign a camera controller # we'll use the 6 degrees of freedom # camera for this one self.camera_controller = SixDOF_Controller( self.camera.transform )
def setup_scene(): # create a scene # we'll create the scene as a tree # to demonstrate the depth-first iteration # technique we will use to render it self.scene_root = SceneNode( 'root' ) # the letter indicates the tier the node # is on, a = tier 1, b = tier 2, etc. self.a1 = SceneNode( 'a1' ) self.b1 = SceneNode( 'b1' ) self.b2 = SceneNode( 'b2' ) self.c1 = SceneNode( 'c1' ) self.c2 = SceneNode( 'c2' ) self.c3 = SceneNode( 'c3' ) # the tree looks as follows # / c1 # / b1 - c2 # root - a1 # \ b2 - c3 self.scene_root.add_child( self.a1 ) self.a1.add_child( self.b1 ) self.a1.add_child( self.b2 ) self.b1.add_child( self.c1 ) self.b1.add_child( self.c2 ) self.b2.add_child( self.c3 ) # if we set the nodes local scale (transform) # it will be affected by the parent's scale. # by setting the world scale (world_transform) # we are ignoring the parent's scale. # re-attaching the node would invalidate this. self.a1.world_transform.scale = [2.0, 2.0, 2.0] self.b1.world_transform.scale = [1.0, 1.0, 1.0] self.b2.world_transform.scale = [1.0, 1.0, 1.0] self.c1.world_transform.scale = [0.8, 0.8, 0.8] self.c2.world_transform.scale = [0.8, 0.8, 0.8] self.c3.world_transform.scale = [0.8, 0.8, 0.8] # move our scene nodes # leave a1 at the centre self.b1.transform.object.translate( [10.0, 0.0, 0.0 ] ) self.b2.transform.object.translate([-10.0, 0.0, 0.0 ] ) self.c1.transform.object.translate( [ 5.0, 0.0, 0.0 ] ) self.c2.transform.object.translate( [-5.0, 0.0, 0.0 ] ) self.c3.transform.object.translate( [ 5.0, 0.0, 0.0 ] ) # rotate the our b nodes so they tilting forward self.b1.transform.object.rotate_x( math.pi / 4.0 ) self.b2.transform.object.rotate_x( math.pi / 4.0 )
def setup_scene( self ): super( Application, self ).setup_scene() # enable texturing glEnable( GL_TEXTURE_2D ) # set our gl clear colour glClearColor( 0.2, 0.2, 0.2, 1.0 ) # create a scene node to hold our GOL renderable self.gol_node = SceneNode( "GOL_Node" ) self.scene_node.add_child( self.gol_node ) # get the maximum viewport size max_viewport_size = (c_int * 2)() glGetIntegerv( GL_MAX_VIEWPORT_DIMS, max_viewport_size ) max_viewport_size = (max_viewport_size[ 0 ], max_viewport_size[ 1 ]) print "Max viewport size", max_viewport_size # make the GOL board # ensure it is a power of 2 for ou texture board_size = (2048, 2048) if board_size > max_viewport_size: board_size = max_viewport_size self.gol = GOL_Renderable( board_size ) # add our renderable to the scene self.gol_node.add_child( self.gol ) # add to our list of renderables self.renderables.append( self.gol )
def setup_scene( self ): # create an fps display self.fps_display = pyglet.clock.ClockDisplay() # store a list of our renderables self.renderables = [] # create a scene self.scene_node = SceneNode( 'root' ) self.mesh_node = SceneNode( 'obj' ) self.scene_node.add_child( self.mesh_node ) # create a mesh object and render node self.mesh = OBJ_Mesh( 'examples/data/obj/cessna.obj' ) self.mesh_render_node = RenderCallbackNode( 'mesh', self.initialise_mesh, self.render_mesh ) self.mesh_node.add_child( self.mesh_render_node ) # add to our list of renderables self.renderables.append( self.mesh_render_node ) # create a camera and a view matrix self.view_matrix = ProjectionViewMatrix( self.viewport.aspect_ratio, fov = 45.0, near_clip = 1.0, far_clip = 200.0 ) # create a camera self.camera = CameraNode( 'camera', self.view_matrix ) self.scene_node.add_child( self.camera ) # move the camera so we can see the model self.camera.transform.object.translate( [ 0.0, 20.0, 30.0 ] ) # rotate the camera so it is pointing down self.camera.transform.object.rotate_x( -math.pi / 4.0 )
def test_scale( self ): root = SceneNode( '/root' ) child = SceneNode( '/child' ) root.add_child( child ) # # Initial state # test_scale( self, root.transform, [1.0, 1.0, 1.0] ) test_scale( self, root.world_transform, [1.0, 1.0, 1.0] ) test_scale( self, child.transform, [1.0, 1.0, 1.0] ) test_scale( self, child.world_transform, [1.0, 1.0, 1.0] ) # # Root Scale = 2.0 # root.transform.scale = [2.0, 2.0, 2.0] test_scale( self, root.transform, [2.0, 2.0, 2.0] ) test_scale( self, root.world_transform, [2.0, 2.0, 2.0] ) test_scale( self, child.transform, [1.0, 1.0, 1.0] ) test_scale( self, child.world_transform, [2.0, 2.0, 2.0] ) # # Child World Scale = 2.0 # child.world_transform.scale = [1.0, 1.0, 1.0] test_scale( self, root.transform, [2.0, 2.0, 2.0] ) test_scale( self, root.world_transform, [2.0, 2.0, 2.0] ) test_scale( self, child.transform, [0.5, 0.5, 0.5] ) test_scale( self, child.world_transform, [1.0, 1.0, 1.0] ) # # Root Scale = 1.0 # root.transform.scale = [1.0, 1.0, 1.0] test_scale( self, root.transform, [1.0, 1.0, 1.0] ) test_scale( self, root.world_transform, [1.0, 1.0, 1.0] ) test_scale( self, child.transform, [0.5, 0.5, 0.5] ) test_scale( self, child.world_transform, [0.5, 0.5, 0.5] )
def setup_scene(self): """Creates the scene to be rendered. Creates our camera, scene graph, """ # don't call 'SimpleApplication's setup_scene CoreApplication.setup_scene(self) # setup our GL state # enable z buffer glEnable(GL_DEPTH_TEST) # enable back face culling glEnable(GL_CULL_FACE) glCullFace(GL_BACK) # load our texture # use the PIL decoder as the pyglet one is broken # and loads most images as greyscale path = os.path.join(os.path.dirname(__file__), "../../data/md2/sydney.bmp") image = Image.open(path) self.texture = Texture2D(GL_TEXTURE_2D) self.texture.bind() self.texture.set_min_mag_filter(min=GL_LINEAR, mag=GL_LINEAR) # load the image from PIL # MD2 textures are inverted pygly.pil_texture.set_pil_image(self.texture, image, flip=False) self.texture.unbind() # create a grid of cubes self.grid_root = SceneNode("grid_root") self.scene_node.add_child(self.grid_root) # store a list of renderables path = os.path.join(os.path.dirname(__file__), "../../data/md2/sydney.md2") self.mesh_node = RenderCallbackNode("mesh", None, self.render_node) # rotate the mesh to face the camera self.mesh_node.transform.object.rotate_y(math.pi) self.mesh_node.mesh = MD2_Mesh(path) self.mesh_node.mesh.load() # attach to our scene graph self.grid_root.add_child(self.mesh_node) # scale the node # self.mesh_node.transform.scale = 0.2 # store current animation self.animation_number = 0 self.set_animation(self.animation_number)
def test_translation_with_rotation( self ): root = SceneNode( '/root' ) child = SceneNode( '/child' ) root.add_child( child ) # # Rotate 180 deg (1 * pi) about the Y axis (yaw) # root.transform.object.rotate_y( math.pi ) identity = matrix44.identity() root_matrix = matrix44.create_from_y_rotation( math.pi ) # # Translate the child node # child.transform.translation = [1.0, 1.0, 1.0] test_translation( self, root.transform, [0.0, 0.0, 0.0] ) test_translation( self, root.world_transform, [0.0, 0.0, 0.0] ) test_translation( self, child.transform, [1.0, 1.0, 1.0] ) # Y does not invert test_translation( self, child.world_transform, [-1.0, 1.0,-1.0] )
def setup_scene( self ): """Creates the scene to be rendered. Creates our camera, scene graph, """ # don't call 'SimpleApplication's setup_scene CoreApplication.setup_scene( self ) # setup our GL state # enable z buffer glEnable( GL_DEPTH_TEST ) # enable back face culling glEnable( GL_CULL_FACE ) glCullFace( GL_BACK ) # create a grid of cubes self.grid_root = SceneNode( 'grid_root' ) self.scene_node.add_child( self.grid_root ) # store a list of renderables path = os.path.join( os.path.dirname( __file__ ), '../data/obj/capsule.obj' ) self.mesh_node = RenderCallbackNode( 'mesh', None, self.render_node ) # rotate the mesh to face the camera self.mesh_node.transform.object.rotate_y( math.pi ) self.mesh_node.mesh = OBJ_Mesh( path ) self.mesh_node.mesh.load() # attach to our scene graph self.grid_root.add_child( self.mesh_node ) # scale the node self.mesh_node.transform.scale = 1.0 # create a list of groups to render # by default, render all groups # this may be in-efficient if data is contained in # multiple groups self.groups = self.mesh_node.mesh.data.meshes.keys()
def setup_camera(): # add another node that our camera will be under # we can rotate this node to show how the cameras work self.camera2_parent = SceneNode( 'camera2_parent' ) self.scene_root.add_child( self.camera2_parent ) # create our camera node self.camera2 = CameraNode( 'camera2', pyrr.matrix44.create_identity() ) self.camera2_parent.add_child( self.camera2 ) # place the camera at the same position as the previous one #self.camera2.transform.translation = self.camera.transform.translation #self.camera2.transform.orientation = self.camera.transform.orientation #self.camera2.transform.scale = self.camera.transform.scale self.camera2.transform.object.translate( [ 0.0, 0.0, 35.0 ] )
def create_cubes(): # create a grid of cubes self.grid_root = SceneNode( 'grid_root' ) self.scene_root.add_child( self.grid_root ) self.grid_root.transform.scale = [2.0, 2.0, 2.0] # create a number of cubes # the grid will extend from -5 to +5 x,z = numpy.mgrid[ -5:5:11j, -5:5:11j ] x = x.flatten() z = z.flatten() positions = numpy.vstack( (x, numpy.zeros( x.shape ), z ) ) positions = positions.T # set the distance of the cubes # cube is -1 -> 1 # so distance is 2 positions *= 2.5 # store a list of renderables self.renderables = [] for position in positions: node = SceneNode( 'node-%s' % position ) node.transform.inertial.translation = position self.grid_root.add_child( node ) self.renderables.append( node ) # create a range of colours from 0.1 -> 0.5 self.cube_colours = numpy.linspace( 0.1, 0.5, len(positions) ) # make them consistent for RGBA self.cube_colours = self.cube_colours.repeat( 4 ) self.cube_colours.shape = (-1, 4) # replace the Blue and Alpha value self.cube_colours[:,2] = 0.5 self.cube_colours[:,3] = 0.5
class MD2_Application(SimpleApplication): def setup(self): super(MD2_Application, self).setup() self.setup_keyboard() print "Press any key to move to the next animation" def setup_keyboard(self): self.window.push_handlers(on_key_release=self.on_key_release) def on_key_release(self, *args): self.increment_animation() def setup_viewports(self): super(MD2_Application, self).setup_viewports() self.colours[0] = (1.0, 1.0, 1.0, 1.0) def setup_camera(self): super(MD2_Application, self).setup_camera() # move the camera self.cameras[0].transform.inertial.translate([0.0, -3.0, 0.0]) # tilt the camera downward self.cameras[0].transform.object.rotate_x(math.pi / 8.0) def setup_scene(self): """Creates the scene to be rendered. Creates our camera, scene graph, """ # don't call 'SimpleApplication's setup_scene CoreApplication.setup_scene(self) # setup our GL state # enable z buffer glEnable(GL_DEPTH_TEST) # enable back face culling glEnable(GL_CULL_FACE) glCullFace(GL_BACK) # load our texture # use the PIL decoder as the pyglet one is broken # and loads most images as greyscale path = os.path.join(os.path.dirname(__file__), "../../data/md2/sydney.bmp") image = Image.open(path) self.texture = Texture2D(GL_TEXTURE_2D) self.texture.bind() self.texture.set_min_mag_filter(min=GL_LINEAR, mag=GL_LINEAR) # load the image from PIL # MD2 textures are inverted pygly.pil_texture.set_pil_image(self.texture, image, flip=False) self.texture.unbind() # create a grid of cubes self.grid_root = SceneNode("grid_root") self.scene_node.add_child(self.grid_root) # store a list of renderables path = os.path.join(os.path.dirname(__file__), "../../data/md2/sydney.md2") self.mesh_node = RenderCallbackNode("mesh", None, self.render_node) # rotate the mesh to face the camera self.mesh_node.transform.object.rotate_y(math.pi) self.mesh_node.mesh = MD2_Mesh(path) self.mesh_node.mesh.load() # attach to our scene graph self.grid_root.add_child(self.mesh_node) # scale the node # self.mesh_node.transform.scale = 0.2 # store current animation self.animation_number = 0 self.set_animation(self.animation_number) def increment_animation(self): self.animation_number += 1 if self.animation_number >= len(self.mesh_node.mesh.animations): self.animation_number = 0 self.set_animation(self.animation_number) def set_animation(self, number): self.animation_number = number self.animation = pymesh.md2.MD2.animations.keys()[number] start, end = self.mesh_node.mesh.animation_start_end_frame(self.animation) num_frames = self.mesh_node.mesh.num_frames if start >= num_frames or end >= num_frames: print 'Animation "%s" not present' % self.animation return self.set_animation(0) self.mesh_node.mesh.frame_1 = start self.mesh_node.mesh.frame_2 = start + 1 # some animations have only 1 frame if self.mesh_node.mesh.frame_2 > end: self.mesh_node.mesh.frame_2 = end print "Animation:", self.animation def step(self, dt): """Updates our scene and triggers the on_draw event. This is scheduled in our __init__ method and called periodically by pyglet's event callbacks. We need to manually call 'on_draw' as we patched it our of pyglets event loop when we patched it out with pygly.monkey_patch. Because we called 'on_draw', we also need to perform the buffer flip at the end. """ # setup the scene # rotate the scene nodes about their vertical axis self.grid_root.transform.object.rotate_y(dt * 0.2) # update our frame rates frame_rate = self.mesh_node.mesh.frame_rate # increment our frame self.mesh_node.mesh.interpolation += dt * frame_rate # calculate the current and next frame # and the blending fraction fraction, whole = math.modf(self.mesh_node.mesh.interpolation) whole = int(whole) # check if we're moving to the next keyframe if whole > 0: # ensure fraction remains < 1.0 self.mesh_node.mesh.interpolation = fraction # increment our frames self.mesh_node.mesh.frame_1 += whole self.mesh_node.mesh.frame_2 += whole # get the animation's start and end frame start, end = self.mesh_node.mesh.animation_start_end_frame(self.animation) num_frames = self.mesh_node.mesh.num_frames if start >= num_frames: start = num_frames end = num_frames print "Animation has insufficient frames" elif end >= num_frames: end = num_frames print "Animation has insufficient frames" # ensure we don't go outside the animation animation_size = (end - start) + 1 if self.mesh_node.mesh.frame_1 > end: self.mesh_node.mesh.frame_1 -= animation_size if self.mesh_node.mesh.frame_2 > end: self.mesh_node.mesh.frame_2 -= animation_size # this will trigger the draw event and buffer flip CoreApplication.step(self, dt) def render_scene(self, camera): """Renders each renderable in the scene using the current projection and model view matrix. The original GL state will be restored upon leaving this function. """ projection = camera.view_matrix.matrix model_view = camera.model_view # bind our diffuse texture glActiveTexture(GL_TEXTURE0) self.texture.bind() # update the model view world_matrix = self.mesh_node.world_transform.matrix current_mv = matrix44.multiply(world_matrix, model_view) # render a cube self.mesh_node.render(projection=projection, model_view=current_mv) glActiveTexture(GL_TEXTURE0) self.texture.unbind() def render_node(self, node, **kwargs): node.mesh.render(**kwargs)
class Application( object ): def __init__( self ): super( Application, self ).__init__() # setup our opengl requirements config = pyglet.gl.Config( depth_size = 16, double_buffer = True ) # create our window self.window = pyglet.window.Window( fullscreen = False, width = 1024, height = 768, resizable = True, vsync = False, config = config ) # create a viewport self.viewport = RatioViewport( self.window, [ [0.0, 0.0], [1.0, 1.0] ] ) # create our input devices self.keyboard = Keyboard( self.window ) self.mouse = Mouse( self.window ) # setup our scene self.setup_scene() # setup our text self.setup_text() # listen for on_draw events self.window.push_handlers( on_draw = self.on_draw ) # setup our update loop the app # we'll render at 60 fps frequency = 60.0 self.update_delta = 1.0 / frequency # over-ride the frequency and render at full speed self.update_delta = -1 # use a pyglet callback for our render loop pyglet.clock.schedule_interval( self.step, self.update_delta ) def setup_scene( self ): # create an fps display self.fps_display = pyglet.clock.ClockDisplay() # create a list of renderables self.renderables = [] # create a scene self.scene_node = SceneNode( 'root' ) self.grid_node = SceneNode( 'grid' ) self.scene_node.add_child( self.grid_node ) self.grid_render_node = RenderCallbackNode( 'mesh', grid.initialise_grid, grid.render_grid ) self.grid_node.add_child( self.grid_render_node ) # add to our list of renderables self.renderables.append( self.grid_render_node ) # move the grid backward so we can see it # and move it down so we start above it self.grid_node.transform.inertial.translate( [ 0.0, 0.0, -80.0 ] ) # create a camera and a view matrix self.view_matrix = ProjectionViewMatrix( self.viewport.aspect_ratio, fov = 45.0, near_clip = 1.0, far_clip = 200.0 ) # create a camera self.camera = CameraNode( 'camera', self.view_matrix ) self.scene_node.add_child( self.camera ) # move the camera up so it starts above the grid self.camera.transform.inertial.translate( [ 0.0, 20.0, 0.0 ] ) # assign a camera controller # we'll use the 6 degrees of freedom # camera for this one self.camera_controller = SixDOF_Controller( self.camera.transform ) def setup_text( self ): self.help_label = pyglet.text.HTMLLabel( """ <b>6-DOF Camera demo</b> <ul> <li>Mouse: look around</li> <li>W,A,S,D: move around</li> <li>Space: move up</li> <li>Shift: move down</li> </ul> """, multiline = True, x = 0, y = 50, width = 500, anchor_x = 'left', anchor_y = 'bottom', ) self.help_label.color = (255,255,255,255) def run( self ): pyglet.app.run() def step( self, dt ): # update the Camera camera_speed = 40.0 # handle input # this looks complex, but all we're doing # is checking for WASD / Arrows # and then sending forward, backward, etc # to the camera controller with an amount that # is scaled by the current time delta if self.keyboard[ self.keyboard.keys.W ] or self.keyboard[ self.keyboard.keys.UP ]: # move forward self.camera_controller.translate_forward( camera_speed * dt ) if self.keyboard[ self.keyboard.keys.S ] or self.keyboard[ self.keyboard.keys.DOWN ]: # move backward self.camera_controller.translate_backward( camera_speed * dt ) if self.keyboard[ self.keyboard.keys.D ] or self.keyboard[ self.keyboard.keys.RIGHT ]: # move right self.camera_controller.translate_right( camera_speed * dt ) if self.keyboard[ self.keyboard.keys.A ] or self.keyboard[ self.keyboard.keys.LEFT ]: # move right self.camera_controller.translate_left( camera_speed * dt ) if self.keyboard[ self.keyboard.keys.SPACE ]: # move up self.camera_controller.translate_up( camera_speed * dt ) if self.keyboard[ self.keyboard.keys.LSHIFT ]: # move up self.camera_controller.translate_down( camera_speed * dt ) # handle camera rotation # get the relative movement of the mouse # since the last frame mouse_relative = self.mouse.relative_position # the base movement speed we use for # scaling with the mouse movements # this value just feels about right mouse_speed = 0.006 # scale the mouse movement by the relative value # DON'T multiply by the time delta here # think about it, it's not what you want! frame_pitch = math.pi * mouse_speed * mouse_relative[ 1 ] frame_yaw = -math.pi * mouse_speed * mouse_relative[ 0 ] # check for mouse inverts, for us freaks... # WE HAVE RIGHTS TOO! invert_y = True if invert_y == True: frame_pitch = -frame_pitch # pass the mouse movement to the camera controller self.camera_controller.orient( pitch = frame_pitch, yaw = frame_yaw ) # reset our mouse relative position # we should do this each time we take a reading # or the delta will continue to accumulate self.mouse.clear_delta() # manually dispatch the on_draw event # as we patched it out of the idle loop self.window.dispatch_event( 'on_draw' ) # display the frame buffer self.window.flip() def on_draw( self ): # render the scene self.render() # render our help text self.help_label.draw() # render the fps self.fps_display.draw() def render( self ): # # setup # # activate the window self.window.switch_to() # activate our viewport self.viewport.switch_to() # setup our viewport properties self.viewport.push_viewport_attributes() # update the view matrix aspect ratio self.camera.view_matrix.aspect_ratio = self.viewport.aspect_ratio # apply our view matrix and camera translation self.camera.view_matrix.push_view_matrix() self.camera.push_model_view() # # render # # clear our frame buffer and depth buffer pygly.gl.set_scissor( self.viewport.rect ) glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) # render our grid for renderable in self.renderables: renderable.render() # # tear down # # pop our view matrix and camera translation self.camera.pop_model_view() self.camera.view_matrix.pop_view_matrix() # reset our gl state self.viewport.pop_viewport_attributes() # # reset state # # set our viewport to the entire window pygly.gl.set_scissor( pygly.window.create_rectangle( self.window ) ) pygly.gl.set_viewport( pygly.window.create_rectangle( self.window ) )
def setup_scene(self): """Creates the scene to be rendered. Creates our camera, scene graph, """ # don't call 'SimpleApplication's setup_scene CoreApplication.setup_scene(self) # setup our GL state # enable z buffer glEnable(GL_DEPTH_TEST) # enable back face culling glEnable(GL_CULL_FACE) glCullFace(GL_BACK) # load our texture # use the PIL decoder as the pyglet one is broken # and loads most images as greyscale path = os.path.join(os.path.dirname(__file__), '../../data/md2/sydney.bmp') image = Image.open(path) self.texture = Texture2D(GL_TEXTURE_2D) self.texture.bind() self.texture.set_min_mag_filter(min=GL_LINEAR, mag=GL_LINEAR) # load the image from PIL # MD2 textures are inverted pygly.pil_texture.set_pil_image(self.texture, image, flip=False) self.texture.unbind() # create a grid of cubes self.grid_root = SceneNode('grid_root') self.scene_node.add_child(self.grid_root) # create a number of cubes # the grid will extend from -5 to +5 x, z = numpy.mgrid[-5:5:11j, -5:5:11j] x = x.flatten() z = z.flatten() positions = numpy.vstack((x, numpy.zeros(x.shape), z)) positions = positions.T # set the distance between the models positions *= 4.5 # store a list of renderables self.renderables = [] path = os.path.join(os.path.dirname(__file__), '../../data/md2/sydney.md2') for position in positions: node = RenderCallbackNode('node-%s' % position, None, self.render_node) node.mesh = MD2_Mesh(path) node.mesh.load() # attach to our scene graph self.grid_root.add_child(node) self.renderables.append(node) # move and scale the node node.transform.inertial.translation = position node.transform.scale = 0.2 # create a range of animation times # 0.0 <= x < num_frames self.frames = numpy.linspace(0.0, float( self.renderables[0].mesh.num_frames), len(positions), endpoint=False) # create an array that will store our frame rates self.frame_rate = numpy.zeros(len(self.renderables), dtype=numpy.float) self.animation = ''
class MD2_Application(SimpleApplication): def setup(self): super(MD2_Application, self).setup() self.setup_keyboard() print 'Press any key to move to the next animation' def setup_keyboard(self): self.window.push_handlers(on_key_release=self.on_key_release) def on_key_release(self, *args): self.increment_animation() def setup_viewports(self): super(MD2_Application, self).setup_viewports() self.colours[0] = (1.0, 1.0, 1.0, 1.0) def setup_camera(self): super(MD2_Application, self).setup_camera() # move the camera self.cameras[0].transform.inertial.translate([0.0, -3.0, 0.0]) # tilt the camera downward self.cameras[0].transform.object.rotate_x(math.pi / 8.0) def setup_scene(self): """Creates the scene to be rendered. Creates our camera, scene graph, """ # don't call 'SimpleApplication's setup_scene CoreApplication.setup_scene(self) # setup our GL state # enable z buffer glEnable(GL_DEPTH_TEST) # enable back face culling glEnable(GL_CULL_FACE) glCullFace(GL_BACK) # load our texture # use the PIL decoder as the pyglet one is broken # and loads most images as greyscale path = os.path.join(os.path.dirname(__file__), '../../data/md2/sydney.bmp') image = Image.open(path) self.texture = Texture2D(GL_TEXTURE_2D) self.texture.bind() self.texture.set_min_mag_filter(min=GL_LINEAR, mag=GL_LINEAR) # load the image from PIL # MD2 textures are inverted pygly.pil_texture.set_pil_image(self.texture, image, flip=False) self.texture.unbind() # create a grid of cubes self.grid_root = SceneNode('grid_root') self.scene_node.add_child(self.grid_root) # store a list of renderables path = os.path.join(os.path.dirname(__file__), '../../data/md2/sydney.md2') self.mesh_node = RenderCallbackNode('mesh', None, self.render_node) # rotate the mesh to face the camera self.mesh_node.transform.object.rotate_y(math.pi) self.mesh_node.mesh = MD2_Mesh(path) self.mesh_node.mesh.load() # attach to our scene graph self.grid_root.add_child(self.mesh_node) # scale the node #self.mesh_node.transform.scale = 0.2 # store current animation self.animation_number = 0 self.set_animation(self.animation_number) def increment_animation(self): self.animation_number += 1 if self.animation_number >= len(self.mesh_node.mesh.animations): self.animation_number = 0 self.set_animation(self.animation_number) def set_animation(self, number): self.animation_number = number self.animation = pymesh.md2.MD2.animations.keys()[number] start, end = self.mesh_node.mesh.animation_start_end_frame( self.animation) num_frames = self.mesh_node.mesh.num_frames if start >= num_frames or end >= num_frames: print 'Animation "%s" not present' % self.animation return self.set_animation(0) self.mesh_node.mesh.frame_1 = start self.mesh_node.mesh.frame_2 = start + 1 # some animations have only 1 frame if self.mesh_node.mesh.frame_2 > end: self.mesh_node.mesh.frame_2 = end print 'Animation:', self.animation def step(self, dt): """Updates our scene and triggers the on_draw event. This is scheduled in our __init__ method and called periodically by pyglet's event callbacks. We need to manually call 'on_draw' as we patched it our of pyglets event loop when we patched it out with pygly.monkey_patch. Because we called 'on_draw', we also need to perform the buffer flip at the end. """ # setup the scene # rotate the scene nodes about their vertical axis self.grid_root.transform.object.rotate_y(dt * 0.2) # update our frame rates frame_rate = self.mesh_node.mesh.frame_rate # increment our frame self.mesh_node.mesh.interpolation += dt * frame_rate # calculate the current and next frame # and the blending fraction fraction, whole = math.modf(self.mesh_node.mesh.interpolation) whole = int(whole) # check if we're moving to the next keyframe if whole > 0: # ensure fraction remains < 1.0 self.mesh_node.mesh.interpolation = fraction # increment our frames self.mesh_node.mesh.frame_1 += whole self.mesh_node.mesh.frame_2 += whole # get the animation's start and end frame start, end = self.mesh_node.mesh.animation_start_end_frame( self.animation) num_frames = self.mesh_node.mesh.num_frames if start >= num_frames: start = num_frames end = num_frames print 'Animation has insufficient frames' elif end >= num_frames: end = num_frames print 'Animation has insufficient frames' # ensure we don't go outside the animation animation_size = (end - start) + 1 if self.mesh_node.mesh.frame_1 > end: self.mesh_node.mesh.frame_1 -= animation_size if self.mesh_node.mesh.frame_2 > end: self.mesh_node.mesh.frame_2 -= animation_size # this will trigger the draw event and buffer flip CoreApplication.step(self, dt) def render_scene(self, camera): """Renders each renderable in the scene using the current projection and model view matrix. The original GL state will be restored upon leaving this function. """ projection = camera.view_matrix.matrix model_view = camera.model_view # bind our diffuse texture glActiveTexture(GL_TEXTURE0) self.texture.bind() # update the model view world_matrix = self.mesh_node.world_transform.matrix current_mv = matrix44.multiply(world_matrix, model_view) # render a cube self.mesh_node.render(projection=projection, model_view=current_mv) glActiveTexture(GL_TEXTURE0) self.texture.unbind() def render_node(self, node, **kwargs): node.mesh.render(**kwargs)
class Application(object): def __init__(self): super(Application, self).__init__() # setup our opengl requirements config = pyglet.gl.Config(depth_size=16, double_buffer=True) # create our window self.window = pyglet.window.Window(fullscreen=False, width=1024, height=768, resizable=True, vsync=False, config=config) # create a viewport self.viewport = RatioViewport(self.window, [[0.0, 0.0], [1.0, 1.0]]) # create our input devices self.keyboard = Keyboard(self.window) self.mouse = Mouse(self.window) # setup our scene self.setup_scene() # setup our text self.setup_text() # listen for on_draw events self.window.push_handlers(on_draw=self.on_draw) # setup our update loop the app # we'll render at 60 fps frequency = 60.0 self.update_delta = 1.0 / frequency # over-ride the frequency and render at full speed self.update_delta = -1 # use a pyglet callback for our render loop pyglet.clock.schedule_interval(self.step, self.update_delta) def setup_scene(self): # create an fps display self.fps_display = pyglet.clock.ClockDisplay() # create a list of renderables self.renderables = [] # create a scene self.scene_node = SceneNode('root') self.grid_node = SceneNode('grid') self.scene_node.add_child(self.grid_node) self.grid_render_node = RenderCallbackNode('mesh', grid.initialise_grid, grid.render_grid) self.grid_node.add_child(self.grid_render_node) # add to our list of renderables self.renderables.append(self.grid_render_node) # move the grid backward so we can see it # and move it down so we start above it self.grid_node.transform.inertial.translate([0.0, 0.0, -80.0]) # create a camera and a view matrix self.view_matrix = ProjectionViewMatrix(self.viewport.aspect_ratio, fov=45.0, near_clip=1.0, far_clip=200.0) # create a camera self.camera = CameraNode('camera', self.view_matrix) self.scene_node.add_child(self.camera) # move the camera up so it starts above the grid self.camera.transform.inertial.translate([0.0, 20.0, 0.0]) # assign a camera controller # we'll use the 6 degrees of freedom # camera for this one self.camera_controller = SixDOF_Controller(self.camera.transform) def setup_text(self): self.help_label = pyglet.text.HTMLLabel( """ <b>6-DOF Camera demo</b> <ul> <li>Mouse: look around</li> <li>W,A,S,D: move around</li> <li>Space: move up</li> <li>Shift: move down</li> </ul> """, multiline=True, x=0, y=50, width=500, anchor_x='left', anchor_y='bottom', ) self.help_label.color = (255, 255, 255, 255) def run(self): pyglet.app.run() def step(self, dt): # update the Camera camera_speed = 40.0 # handle input # this looks complex, but all we're doing # is checking for WASD / Arrows # and then sending forward, backward, etc # to the camera controller with an amount that # is scaled by the current time delta if self.keyboard[self.keyboard.keys.W] or self.keyboard[ self.keyboard.keys.UP]: # move forward self.camera_controller.translate_forward(camera_speed * dt) if self.keyboard[self.keyboard.keys.S] or self.keyboard[ self.keyboard.keys.DOWN]: # move backward self.camera_controller.translate_backward(camera_speed * dt) if self.keyboard[self.keyboard.keys.D] or self.keyboard[ self.keyboard.keys.RIGHT]: # move right self.camera_controller.translate_right(camera_speed * dt) if self.keyboard[self.keyboard.keys.A] or self.keyboard[ self.keyboard.keys.LEFT]: # move right self.camera_controller.translate_left(camera_speed * dt) if self.keyboard[self.keyboard.keys.SPACE]: # move up self.camera_controller.translate_up(camera_speed * dt) if self.keyboard[self.keyboard.keys.LSHIFT]: # move up self.camera_controller.translate_down(camera_speed * dt) # handle camera rotation # get the relative movement of the mouse # since the last frame mouse_relative = self.mouse.relative_position # the base movement speed we use for # scaling with the mouse movements # this value just feels about right mouse_speed = 0.006 # scale the mouse movement by the relative value # DON'T multiply by the time delta here # think about it, it's not what you want! frame_pitch = math.pi * mouse_speed * mouse_relative[1] frame_yaw = -math.pi * mouse_speed * mouse_relative[0] # check for mouse inverts, for us freaks... # WE HAVE RIGHTS TOO! invert_y = True if invert_y == True: frame_pitch = -frame_pitch # pass the mouse movement to the camera controller self.camera_controller.orient(pitch=frame_pitch, yaw=frame_yaw) # reset our mouse relative position # we should do this each time we take a reading # or the delta will continue to accumulate self.mouse.clear_delta() # manually dispatch the on_draw event # as we patched it out of the idle loop self.window.dispatch_event('on_draw') # display the frame buffer self.window.flip() def on_draw(self): # render the scene self.render() # render our help text self.help_label.draw() # render the fps self.fps_display.draw() def render(self): # # setup # # activate the window self.window.switch_to() # activate our viewport self.viewport.switch_to() # setup our viewport properties self.viewport.push_viewport_attributes() # update the view matrix aspect ratio self.camera.view_matrix.aspect_ratio = self.viewport.aspect_ratio # apply our view matrix and camera translation self.camera.view_matrix.push_view_matrix() self.camera.push_model_view() # # render # # clear our frame buffer and depth buffer pygly.gl.set_scissor(self.viewport.rect) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # render our grid for renderable in self.renderables: renderable.render() # # tear down # # pop our view matrix and camera translation self.camera.pop_model_view() self.camera.view_matrix.pop_view_matrix() # reset our gl state self.viewport.pop_viewport_attributes() # # reset state # # set our viewport to the entire window pygly.gl.set_scissor(pygly.window.create_rectangle(self.window)) pygly.gl.set_viewport(pygly.window.create_rectangle(self.window))
def test_rotation( self ): """ Rotation and Inheritance """ # we'll add a child to a root node # we'll move the child # rotate the root # and check the child is where it should be # the child should be moved somewhere that will # make it easy to check root = SceneNode( '/root' ) child = SceneNode( '/child' ) root.add_child( child ) # # Rotate 180 deg (1 * pi) about the Y axis (yaw) # root.transform.object.rotate_y( math.pi ) identity = matrix44.identity() root_matrix = matrix44.create_from_y_rotation( math.pi ) # root object test_axis( self, root.transform.object, root_matrix ) test_axis( self, root.transform.inertial, identity ) test_axis( self, root.world_transform.object, root_matrix ) test_axis( self, root.world_transform.inertial, identity ) child_matrix = matrix44.identity() test_axis( self, child.transform.object, child_matrix ) test_axis( self, child.transform.inertial, identity ) test_axis( self, child.world_transform.object, root_matrix ) test_axis( self, child.world_transform.inertial, identity ) # check the node matrix matches what we're seeing in # the transform axis values self.assertTrue( numpy.allclose( root.transform.matrix, root_matrix ), "Root Local Matrix incorrect" ) self.assertTrue( numpy.allclose( root.world_transform.matrix, root_matrix ), "Root RootMatrix incorrect" ) self.assertTrue( numpy.allclose( child.transform.matrix, identity ), "Child Local Matrix incorrect" ) self.assertTrue( numpy.allclose( child.world_transform.matrix, root_matrix ), "Child RootMatrix incorrect" ) # # Rotate 180 deg (1 * pi) about the X axis (pitch) # # rotate 180 deg / 1pi about the x axis (pitch) child.transform.object.rotate_x( math.pi ) child_matrix = matrix44.multiply( matrix44.create_from_x_rotation( math.pi ), child_matrix ) child_world = matrix44.multiply( child_matrix, root_matrix ) # root object test_axis( self, root.transform.object, root_matrix ) test_axis( self, root.transform.inertial, identity ) test_axis( self, root.world_transform.object, root_matrix ) test_axis( self, root.world_transform.inertial, identity ) test_axis( self, child.transform.object, child_matrix ) test_axis( self, child.transform.inertial, identity ) test_axis( self, child.world_transform.object, child_world ) test_axis( self, child.world_transform.inertial, identity ) # check the node matrix matches what we're seeing in # the transform axis values self.assertTrue( numpy.allclose( root.transform.matrix, root_matrix ), "Root Local Matrix incorrect" ) self.assertTrue( numpy.allclose( root.world_transform.matrix, root_matrix ), "Root RootMatrix incorrect" ) self.assertTrue( numpy.allclose( child.transform.matrix, child_matrix ), "Child Local Matrix incorrect" ) self.assertTrue( numpy.allclose( child.world_transform.matrix, child_world ), "Child RootMatrix incorrect" )
class MD2_Application( SimpleApplication ): def setup_viewports( self ): super( MD2_Application, self ).setup_viewports() self.colours[ 0 ] = (1.0,1.0,1.0,1.0) def setup_scene( self ): """Creates the scene to be rendered. Creates our camera, scene graph, """ # don't call 'SimpleApplication's setup_scene CoreApplication.setup_scene( self ) # setup our GL state # enable z buffer glEnable( GL_DEPTH_TEST ) # enable back face culling glEnable( GL_CULL_FACE ) glCullFace( GL_BACK ) # load our texture # use the PIL decoder as the pyglet one is broken # and loads most images as greyscale path = os.path.join( os.path.dirname( __file__ ), '../../data/md2/sydney.bmp' ) image = Image.open( path ) self.texture = Texture2D( GL_TEXTURE_2D ) self.texture.bind() self.texture.set_min_mag_filter( min = GL_LINEAR, mag = GL_LINEAR ) # load the image from PIL # MD2 textures are inverted pygly.pil_texture.set_pil_image( self.texture, image, flip = False ) self.texture.unbind() # create a grid of cubes self.grid_root = SceneNode( 'grid_root' ) self.scene_node.add_child( self.grid_root ) # create a number of cubes # the grid will extend from -5 to +5 x,z = numpy.mgrid[ -5:5:11j, -5:5:11j ] x = x.flatten() z = z.flatten() positions = numpy.vstack( (x, numpy.zeros( x.shape ), z ) ) positions = positions.T # set the distance between the models positions *= 4.5 # store a list of renderables self.renderables = [] path = os.path.join( os.path.dirname( __file__ ), '../../data/md2/sydney.md2' ) for position in positions: node = RenderCallbackNode( 'node-%s' % position, None, self.render_node ) node.mesh = MD2_Mesh( path ) node.mesh.load() # attach to our scene graph self.grid_root.add_child( node ) self.renderables.append( node ) # move and scale the node node.transform.inertial.translation = position node.transform.scale = 0.2 # create a range of animation times # 0.0 <= x < num_frames self.frames = numpy.linspace( 0.0, float(self.renderables[ 0 ].mesh.num_frames), len(positions), endpoint = False ) # create an array that will store our frame rates self.frame_rate = numpy.zeros( len(self.renderables), dtype = numpy.float ) self.animation = '' def step( self, dt ): """Updates our scene and triggers the on_draw event. This is scheduled in our __init__ method and called periodically by pyglet's event callbacks. We need to manually call 'on_draw' as we patched it our of pyglets event loop when we patched it out with pygly.monkey_patch. Because we called 'on_draw', we also need to perform the buffer flip at the end. """ # setup the scene # rotate the scene nodes about their vertical axis self.grid_root.transform.object.rotate_y( dt * 0.2 ) # update our frame rates self.frame_rate[:] = [ mesh_node.mesh.frame_rate for mesh_node in self.renderables ] # increment our frame #self.frames += dt * fps self.frames += dt * self.frame_rate numpy.mod( self.frames, self.renderables[ 0 ].mesh.num_frames, self.frames ) # print the animation name of the first mesh curr_anim = self.renderables[ 0 ].mesh.animation if self.animation != curr_anim: self.animation = curr_anim print 'Curren animation:', self.animation # this will trigger the draw event and buffer flip CoreApplication.step( self, dt ) def render_scene( self, camera ): """Renders each renderable in the scene using the current projection and model view matrix. The original GL state will be restored upon leaving this function. """ projection = camera.view_matrix.matrix model_view = camera.model_view # bind our diffuse texture glActiveTexture( GL_TEXTURE0 ) self.texture.bind() # iterate through our renderables for node, frame in zip(self.renderables, self.frames): # update the model view world_matrix = node.world_transform.matrix current_mv = matrix44.multiply( world_matrix, model_view ) fraction, frame1 = math.modf( frame ) frame2 = (frame1 + 1.0) % node.mesh.num_frames # update the frame node.mesh.frame_1 = int(frame1) node.mesh.frame_2 = int(frame2) node.mesh.interpolation = fraction # render a cube node.render( projection = projection, model_view = current_mv ) glActiveTexture( GL_TEXTURE0 ) self.texture.unbind() def render_node( self, node, **kwargs ): node.mesh.render( **kwargs )
class Scene( scene_scene_graph.Scene ): def __init__( self, core_profile = True ): super( Scene, self ).__init__( core_profile ) def initialise( self ): super( Scene, self ).initialise() def set_gl_state(): # setup our GL state # enable scissoring for viewports GL.glEnable( GL.GL_SCISSOR_TEST ) set_gl_state() self.viewport2 = None def setup_camera(): # add another node that our camera will be under # we can rotate this node to show how the cameras work self.camera2_parent = SceneNode( 'camera2_parent' ) self.scene_root.add_child( self.camera2_parent ) # create our camera node self.camera2 = CameraNode( 'camera2', pyrr.matrix44.create_identity() ) self.camera2_parent.add_child( self.camera2 ) # place the camera at the same position as the previous one #self.camera2.transform.translation = self.camera.transform.translation #self.camera2.transform.orientation = self.camera.transform.orientation #self.camera2.transform.scale = self.camera.transform.scale self.camera2.transform.object.translate( [ 0.0, 0.0, 35.0 ] ) setup_camera() def on_window_resized( self, width, height ): #super( Scene, self ).on_window_resized( width, height ) def update_viewports(): half_width = width / 2.0 # update the viewport self.viewport = pyrr.rectangle.create_from_position( x = 0, y = 0, width = half_width, height = height ) self.viewport2 = pyrr.rectangle.create_from_position( x = half_width, y = 0, width = half_width, height = height ) print self.viewport, self.viewport2 def update_cameras(): # update the projection matrix # we need to do this or the rendering will become skewed with each # resize of viewport change aspect_ratio = pyrr.rectangle.aspect_ratio( self.viewport ) self.camera.projection_matrix = pyrr.matrix44.create_perspective_projection_matrix( fovy = 80.0, aspect = aspect_ratio, near = 1.0, far = 100.0 ) # update the projection matrix # we need to do this or the rendering will become skewed with each # resize of viewport change aspect_ratio = pyrr.rectangle.aspect_ratio( self.viewport2 ) self.camera2.projection_matrix = pyrr.matrix44.create_perspective_projection_matrix( fovy = 80.0, aspect = aspect_ratio, near = 1.0, far = 100.0 ) update_viewports() update_cameras() def step( self, dt ): super( Scene, self ).step( dt ) # setup the scene # rotate the scene nodes about their vertical axis self.camera2_parent.transform.object.rotate_x( dt ) def render( self ): # render viewport 1 super( Scene, self ).render() # change our clear colour to make it clear where the viewport is GL.glClearColor( 1.0, 1.0, 1.0, 1.0 ) # render the viewport self.render_viewport( self.viewport2, self.camera2 )
def setup_scene(): # create a scene # we'll create the scene as a tree # to demonstrate the depth-first iteration # technique we will use to render it self.scene_root = SceneNode( 'root' )
def setup_scene(): # create a scene # we'll create the scene as a tree # to demonstrate the depth-first iteration # technique we will use to render it self.scene_root = SceneNode('root') # the letter indicates the tier the node # is on, a = tier 1, b = tier 2, etc. self.a1 = SceneNode('a1') self.b1 = SceneNode('b1') self.b2 = SceneNode('b2') self.c1 = SceneNode('c1') self.c2 = SceneNode('c2') self.c3 = SceneNode('c3') # the tree looks as follows # / c1 # / b1 - c2 # root - a1 # \ b2 - c3 self.scene_root.add_child(self.a1) self.a1.add_child(self.b1) self.a1.add_child(self.b2) self.b1.add_child(self.c1) self.b1.add_child(self.c2) self.b2.add_child(self.c3) # if we set the nodes local scale (transform) # it will be affected by the parent's scale. # by setting the world scale (world_transform) # we are ignoring the parent's scale. # re-attaching the node would invalidate this. self.a1.world_transform.scale = [2.0, 2.0, 2.0] self.b1.world_transform.scale = [1.0, 1.0, 1.0] self.b2.world_transform.scale = [1.0, 1.0, 1.0] self.c1.world_transform.scale = [0.8, 0.8, 0.8] self.c2.world_transform.scale = [0.8, 0.8, 0.8] self.c3.world_transform.scale = [0.8, 0.8, 0.8] # move our scene nodes # leave a1 at the centre self.b1.transform.object.translate([10.0, 0.0, 0.0]) self.b2.transform.object.translate([-10.0, 0.0, 0.0]) self.c1.transform.object.translate([5.0, 0.0, 0.0]) self.c2.transform.object.translate([-5.0, 0.0, 0.0]) self.c3.transform.object.translate([5.0, 0.0, 0.0]) # rotate the our b nodes so they tilting forward self.b1.transform.object.rotate_x(math.pi / 4.0) self.b2.transform.object.rotate_x(math.pi / 4.0)
class Scene( scene.Scene ): def __init__( self, core_profile = True ): super( Scene, self ).__init__( core_profile ) @property def name( self ): return "Basic" def initialise( self ): super( Scene, self ).initialise() def set_gl_state(): # setup our GL state # disable z buffer # because we're rendering transparent cubes GL.glDisable( GL.GL_DEPTH_TEST ) # enable back face culling GL.glEnable( GL.GL_CULL_FACE ) GL.glCullFace( GL.GL_BACK ) # enable alpha testing GL.glEnable( GL.GL_BLEND ) GL.glBlendFunc( GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA ) set_gl_state() # create a viewport # this will be updated before we begin self.viewport = None # create a cube to render self.cube = renderable_colour_cube.create( self.core_profile ) def setup_scene(): # create a scene # we'll create the scene as a tree # to demonstrate the depth-first iteration # technique we will use to render it self.scene_root = SceneNode( 'root' ) setup_scene() def setup_camera(): # create our camera node self.camera = CameraNode( 'camera', pyrr.matrix44.create_identity() ) self.scene_root.add_child( self.camera ) # move the camera so we're not inside # the root scene node's debug cube self.camera.transform.object.translate( [ 0.0, 30.0, 35.0 ] ) # tilt the camera downward self.camera.transform.object.rotate_x(-math.pi / 4.0 ) setup_camera() def create_cubes(): # create a grid of cubes self.grid_root = SceneNode( 'grid_root' ) self.scene_root.add_child( self.grid_root ) self.grid_root.transform.scale = [2.0, 2.0, 2.0] # create a number of cubes # the grid will extend from -5 to +5 x,z = numpy.mgrid[ -5:5:11j, -5:5:11j ] x = x.flatten() z = z.flatten() positions = numpy.vstack( (x, numpy.zeros( x.shape ), z ) ) positions = positions.T # set the distance of the cubes # cube is -1 -> 1 # so distance is 2 positions *= 2.5 # store a list of renderables self.renderables = [] for position in positions: node = SceneNode( 'node-%s' % position ) node.transform.inertial.translation = position self.grid_root.add_child( node ) self.renderables.append( node ) # create a range of colours from 0.1 -> 0.5 self.cube_colours = numpy.linspace( 0.1, 0.5, len(positions) ) # make them consistent for RGBA self.cube_colours = self.cube_colours.repeat( 4 ) self.cube_colours.shape = (-1, 4) # replace the Blue and Alpha value self.cube_colours[:,2] = 0.5 self.cube_colours[:,3] = 0.5 create_cubes() def on_window_resized( self, width, height ): # update the viewport self.viewport = pyrr.rectangle.create_from_position( x = 0, y = 0, width = width, height = height ) # update the projection matrix # we need to do this or the rendering will become skewed with each # resize of viewport change aspect_ratio = pyrr.rectangle.aspect_ratio( self.viewport ) self.camera.projection_matrix = pyrr.matrix44.create_perspective_projection_matrix( fovy = 80.0, aspect = aspect_ratio, near = 1.0, far = 100.0 ) def step( self, dt ): super( Scene, self ).step( dt ) # setup the scene # rotate the scene nodes about their vertical axis self.grid_root.transform.object.rotate_y( dt * 0.2 ) def render( self ): super( Scene, self ).render() # sort our scene # extract the positions of all our renderables positions = [ node.world_transform.translation for node in self.renderables ] # sort our renderables based on their position # from the camera # sort based on the -Z axis (the direction the # camera faces) sorted = pygly.sort.sort_radius_back_to_front( self.camera.world_transform.translation, -(self.camera.transform.object.z), self.renderables, positions ) def render_core(): # activate our viewport pygly.viewport.set_viewport( self.viewport ) projection = self.camera.projection_matrix model_view = self.camera.model_view for node, colour in zip(sorted, self.cube_colours): # get the node's world matrix world_matrix = node.world_transform.matrix # apply the camera's model view to the node's matrix current_mv = pyrr.matrix44.multiply( world_matrix, model_view ) # render a cube self.cube.draw( projection, current_mv, colour ) def render_legacy(): # activate our viewport pygly.viewport.set_viewport( self.viewport ) # load our projection matrix with pygly.gl.mode_and_matrix( GL.GL_PROJECTION, self.camera.projection_matrix ): with pygly.gl.mode_and_matrix( GL.GL_MODELVIEW, self.camera.model_view ): # iterate through the scene graph for node, colour in zip(sorted, self.cube_colours): world_matrix = node.world_transform.matrix # multiply the existing model view matrix # by the model's world matrix # then render a cube with pygly.gl.multiply_matrix( world_matrix ): self.cube.draw( colour ) # render the scene if self.core_profile: render_core() else: render_legacy()
class MD5_Application( SimpleApplication ): def setup( self ): super( MD5_Application, self ).setup() self.setup_keyboard() print 'Press any key to move to the next animation' def setup_keyboard( self ): self.window.push_handlers( on_key_release = self.on_key_release ) def on_key_release( self, *args ): self.increment_animation() def setup_viewports( self ): super( MD5_Application, self ).setup_viewports() self.colours[ 0 ] = (0.5,0.5,0.5,1.0) def setup_cameras( self ): super( MD5_Application, self ).setup_cameras() # move the camera self.cameras[ 0 ].transform.inertial.translate( #[ 0.0, 70.0, 100.0 ] [ 0.0,-3.0, 10.0 ] ) # tilt the camera downward self.cameras[ 0 ].transform.object.rotate_x( math.pi / 8.0 ) def setup_scene( self ): """Creates the scene to be rendered. Creates our camera, scene graph, """ # don't call 'SimpleApplication's setup_scene CoreApplication.setup_scene( self ) # setup our GL state # enable z buffer glEnable( GL_DEPTH_TEST ) # enable back face culling #glEnable( GL_CULL_FACE ) glDisable( GL_CULL_FACE ) #glCullFace( GL_BACK ) # create a grid of cubes self.grid_root = SceneNode( 'grid_root' ) self.scene_node.add_child( self.grid_root ) self.mesh_node = RenderCallbackNode( 'mesh', None, self.render_node ) # rotate the mesh to face the camera self.mesh_node.transform.object.rotate_x( -math.pi * 0.5 ) # store a list of renderables mesh_path = os.path.join( os.path.dirname( __file__ ), '../data/md5/boblampclean.md5mesh' #'../data/md5/md5/cyberdemon/cyberdemon.md5mesh' ) anim_path = os.path.join( os.path.dirname( __file__ ), '../data/md5/boblampclean.md5anim' #'../data/md5/md5/cyberdemon/idle.md5anim' ) # load our md5 data self.md5mesh = MD5_Mesh() self.md5mesh.load( mesh_path ) self.md5anim = MD5_Anim() self.md5anim.load( anim_path ) # load the gl mesh self.mesh_node.mesh = Mesh( self.md5mesh ) # load our bindpose self.mesh_node.baseframe = BaseFrameSkeleton( self.md5mesh ) # load some test skeletons self.mesh_node.anim = Animation( self.md5anim ) # set to base frame self.mesh_node.mesh.set_skeleton( self.mesh_node.baseframe ) # load a skeleton renderer self.mesh_node.skeleton = SkeletonRenderer() #self.mesh_node.skeleton.set_skeleton( self.mesh_node.baseframe ) self.mesh_node.skeleton.set_skeleton( self.mesh_node.anim.skeleton( 0 ) ) # create a list of frames self.frames = [ skeleton for skeleton in self.mesh_node.anim ] #self.frames = [] self.frames.append( self.mesh_node.baseframe ) self.frame = 0 self.time_accumulator = 0.0 self.time_per_frame = 1.0 / self.mesh_node.anim.frame_rate # attach to our scene graph self.grid_root.add_child( self.mesh_node ) # scale the node self.mesh_node.transform.scale = 0.5 def step( self, dt ): """Updates our scene and triggers the on_draw event. This is scheduled in our __init__ method and called periodically by pyglet's event callbacks. We need to manually call 'on_draw' as we patched it our of pyglets event loop when we patched it out with pygly.monkey_patch. Because we called 'on_draw', we also need to perform the buffer flip at the end. """ # setup the scene # rotate the scene nodes about their vertical axis self.grid_root.transform.object.rotate_y( dt * 0.2 ) # this will trigger the draw event and buffer flip CoreApplication.step( self, dt ) if self.time_accumulator == 0.0: self.mesh_node.mesh.set_skeleton( self.frames[ self.frame ] ) self.mesh_node.skeleton.set_skeleton( self.frames[ self.frame ] ) self.time_accumulator += dt if self.time_accumulator > self.time_per_frame: self.time_accumulator = 0.0 self.frame += 1 self.frame %= len( self.frames ) def render_scene( self, camera ): """Renders each renderable in the scene using the current projection and model view matrix. The original GL state will be restored upon leaving this function. """ projection = camera.view_matrix.matrix model_view = camera.model_view # update the model view world_matrix = self.mesh_node.world_transform.matrix current_mv = matrix44.multiply( world_matrix, model_view ) # render a cube self.mesh_node.render( projection = projection, model_view = current_mv ) def render_node( self, node, **kwargs ): node.mesh.render( **kwargs ) node.skeleton.render( **kwargs )
class Scene( scene.Scene ): def __init__( self, core_profile = True ): super( Scene, self ).__init__( core_profile ) @property def name( self ): return "Basic" def initialise( self ): super( Scene, self ).initialise() def set_gl_state(): # setup our GL state # enable z buffer GL.glEnable( GL.GL_DEPTH_TEST ) # enable back face culling GL.glEnable( GL.GL_CULL_FACE ) GL.glCullFace( GL.GL_BACK ) set_gl_state() # create a viewport # this will be updated before we begin self.viewport = None # create a cube to render self.cube = renderable_cube.create( self.core_profile ) def setup_scene(): # create a scene # we'll create the scene as a tree # to demonstrate the depth-first iteration # technique we will use to render it self.scene_root = SceneNode( 'root' ) # the letter indicates the tier the node # is on, a = tier 1, b = tier 2, etc. self.a1 = SceneNode( 'a1' ) self.b1 = SceneNode( 'b1' ) self.b2 = SceneNode( 'b2' ) self.c1 = SceneNode( 'c1' ) self.c2 = SceneNode( 'c2' ) self.c3 = SceneNode( 'c3' ) # the tree looks as follows # / c1 # / b1 - c2 # root - a1 # \ b2 - c3 self.scene_root.add_child( self.a1 ) self.a1.add_child( self.b1 ) self.a1.add_child( self.b2 ) self.b1.add_child( self.c1 ) self.b1.add_child( self.c2 ) self.b2.add_child( self.c3 ) # if we set the nodes local scale (transform) # it will be affected by the parent's scale. # by setting the world scale (world_transform) # we are ignoring the parent's scale. # re-attaching the node would invalidate this. self.a1.world_transform.scale = [2.0, 2.0, 2.0] self.b1.world_transform.scale = [1.0, 1.0, 1.0] self.b2.world_transform.scale = [1.0, 1.0, 1.0] self.c1.world_transform.scale = [0.8, 0.8, 0.8] self.c2.world_transform.scale = [0.8, 0.8, 0.8] self.c3.world_transform.scale = [0.8, 0.8, 0.8] # move our scene nodes # leave a1 at the centre self.b1.transform.object.translate( [10.0, 0.0, 0.0 ] ) self.b2.transform.object.translate([-10.0, 0.0, 0.0 ] ) self.c1.transform.object.translate( [ 5.0, 0.0, 0.0 ] ) self.c2.transform.object.translate( [-5.0, 0.0, 0.0 ] ) self.c3.transform.object.translate( [ 5.0, 0.0, 0.0 ] ) # rotate the our b nodes so they tilting forward self.b1.transform.object.rotate_x( math.pi / 4.0 ) self.b2.transform.object.rotate_x( math.pi / 4.0 ) setup_scene() def setup_camera(): # create our camera node self.camera = CameraNode( 'camera', pyrr.matrix44.create_identity() ) self.scene_root.add_child( self.camera ) # move the camera so we're not inside # the root scene node's debug cube self.camera.transform.object.translate( [ 0.0, 30.0, 35.0 ] ) # tilt the camera downward self.camera.transform.object.rotate_x(-math.pi / 4.0 ) setup_camera() def on_window_resized( self, width, height ): # update the viewport self.viewport = pyrr.rectangle.create_from_position( x = 0, y = 0, width = width, height = height ) # update the projection matrix # we need to do this or the rendering will become skewed with each # resize of viewport change aspect_ratio = pyrr.rectangle.aspect_ratio( self.viewport ) self.camera.projection_matrix = pyrr.matrix44.create_perspective_projection_matrix( fovy = 80.0, aspect = aspect_ratio, near = 1.0, far = 100.0 ) def step( self, dt ): super( Scene, self ).step( dt ) # setup the scene # rotate the scene nodes about their vertical axis self.a1.transform.object.rotate_y( dt ) self.b1.transform.object.rotate_y( dt ) self.b2.transform.object.rotate_y( dt ) self.c1.transform.object.rotate_y( dt ) self.c2.transform.object.rotate_y( dt ) self.c3.transform.object.rotate_y( dt ) def render( self ): super( Scene, self ).render() # this code is split into 2 functions to support the # multiple_viewports scene which inherits from this class self.render_viewport( self.viewport, self.camera ) def render_viewport( self, viewport, camera ): def render_core(): projection = camera.projection_matrix model_view = camera.model_view for node in self.scene_root.dfs(): if isinstance( node, CameraNode ): continue # get the node's world matrix world_matrix = node.world_transform.matrix # apply the camera's model view to the node's matrix current_mv = pyrr.matrix44.multiply( world_matrix, model_view ) # render a cube self.cube.draw( projection, current_mv ) def render_legacy(): # load our projection matrix with pygly.gl.mode_and_matrix( GL.GL_PROJECTION, camera.projection_matrix ): with pygly.gl.mode_and_matrix( GL.GL_MODELVIEW, camera.model_view ): # iterate through the scene graph for node in self.scene_root.dfs(): # don't render cameras if isinstance( node, CameraNode ): continue world_matrix = node.world_transform.matrix # multiply the existing model view matrix # by the model's world matrix # then render a cube with pygly.gl.multiply_matrix( world_matrix ): self.cube.draw() # activate our viewport pygly.viewport.set_viewport( viewport ) # set our scissor to the main viewport pygly.viewport.set_scissor( viewport ) # clear the colour and depth buffer GL.glClear( GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT ) # render the scene if self.core_profile: render_core() else: render_legacy()
def setup_scene( self ): """Creates the scene to be rendered. Creates our camera, scene graph, """ # don't call 'SimpleApplication's setup_scene CoreApplication.setup_scene( self ) # setup our GL state # enable z buffer glEnable( GL_DEPTH_TEST ) # enable back face culling #glEnable( GL_CULL_FACE ) glDisable( GL_CULL_FACE ) #glCullFace( GL_BACK ) # create a grid of cubes self.grid_root = SceneNode( 'grid_root' ) self.scene_node.add_child( self.grid_root ) self.mesh_node = RenderCallbackNode( 'mesh', None, self.render_node ) # rotate the mesh to face the camera self.mesh_node.transform.object.rotate_x( -math.pi * 0.5 ) # store a list of renderables mesh_path = os.path.join( os.path.dirname( __file__ ), '../data/md5/boblampclean.md5mesh' #'../data/md5/md5/cyberdemon/cyberdemon.md5mesh' ) anim_path = os.path.join( os.path.dirname( __file__ ), '../data/md5/boblampclean.md5anim' #'../data/md5/md5/cyberdemon/idle.md5anim' ) # load our md5 data self.md5mesh = MD5_Mesh() self.md5mesh.load( mesh_path ) self.md5anim = MD5_Anim() self.md5anim.load( anim_path ) # load the gl mesh self.mesh_node.mesh = Mesh( self.md5mesh ) # load our bindpose self.mesh_node.baseframe = BaseFrameSkeleton( self.md5mesh ) # load some test skeletons self.mesh_node.anim = Animation( self.md5anim ) # set to base frame self.mesh_node.mesh.set_skeleton( self.mesh_node.baseframe ) # load a skeleton renderer self.mesh_node.skeleton = SkeletonRenderer() #self.mesh_node.skeleton.set_skeleton( self.mesh_node.baseframe ) self.mesh_node.skeleton.set_skeleton( self.mesh_node.anim.skeleton( 0 ) ) # create a list of frames self.frames = [ skeleton for skeleton in self.mesh_node.anim ] #self.frames = [] self.frames.append( self.mesh_node.baseframe ) self.frame = 0 self.time_accumulator = 0.0 self.time_per_frame = 1.0 / self.mesh_node.anim.frame_rate # attach to our scene graph self.grid_root.add_child( self.mesh_node ) # scale the node self.mesh_node.transform.scale = 0.5
def test_tree( self ): root = SceneNode( 'root' ) child1_1 = SceneNode( 'child1_1' ) child1_2 = SceneNode( 'child1_2' ) child2_1 = SceneNode( 'child2_1' ) child2_2 = SceneNode( 'child2_2' ) root.add_child( child1_1 ) root.add_child( child1_2 ) child1_1.add_child( child2_1 ) child1_2.add_child( child2_2 ) self.assertTrue( child1_1.parent is root, "Parent not set correctly" ) self.assertTrue( child1_1 in root.children, "Child not in parents child list" ) self.assertTrue( child1_2.parent is root, "Parent not set correctly" ) self.assertTrue( child1_2 in root.children, "Child not in parents child list" ) self.assertTrue( child2_1.parent is child1_1, "Parent not set correctly" ) self.assertTrue( child2_1 in child1_1.children, "Child not in parents child list" ) self.assertTrue( child2_2.parent is child1_2, "Parent not set correctly" ) self.assertTrue( child2_2 in child1_2.children, "Child not in parents child list" ) child1_2.remove_child( child2_2 ) child1_1.add_child( child2_2 ) self.assertTrue( child2_2.parent is child1_1, "Parent not set correctly" ) self.assertTrue( child2_2 in child1_1.children, "Child not in parents child list" ) self.assertFalse( child2_2 in child1_2.children, "Child not removed from parents child list" )
class BaseApplication(object): def __init__(self, caption=None): super(BaseApplication, self).__init__() self.setup_window(caption) self.setup_input() self.setup_ui() self.setup_scene_root() self.setup_camera() self.setup_scene() self.setup_events() def setup_window(self, caption): # setup our opengl requirements config = pyglet.gl.Config(depth_size=16, double_buffer=True, major_version=2, minor_version=1) # create our window self.window = pyglet.window.Window( fullscreen=False, width=1024, height=768, caption=caption, resizable=True, config=config ) # listen for on_draw events self.window.push_handlers(on_draw=self.on_draw) # create a viewport self.viewport = RatioViewport(self.window, [[0.0, 0.0], [1.0, 1.0]]) def setup_input(self): # create our keyboard device self.keyboard = Keyboard(self.window) # register for keypresses self.keyboard.digital.push_handlers(on_digital_input=self.on_key_event) # create our mouse device self.mouse = Mouse(self.window) def setup_events(self): # setup our update loop the app # we'll render at 60 fps frequency = 60.0 self.update_delta = 1.0 / frequency # use a pyglet callback for our render loop pyglet.clock.schedule_interval(self.step, self.update_delta) def setup_ui(self): # create an fps display self.fps_display = pyglet.clock.ClockDisplay() def setup_scene_root(self): # create a list of renderables self.renderables = [] # create a scene self.scene_node = SceneNode("root") def setup_camera(self): # create a camera and a view matrix self.view_matrix = ProjectionViewMatrix(self.viewport.aspect_ratio, fov=45.0, near_clip=1.0, far_clip=200.0) # create a camera self.camera = CameraNode("camera", self.view_matrix) self.scene_node.add_child(self.camera) def setup_scene(self): # CREATE SCENE HERE pass def run(self): pyglet.app.run() def step(self, dt): # update our mouse self.update_mouse(dt) # update the scene here self.update_scene(dt) # manually dispatch the on_draw event # as we patched it out of the idle loop self.window.dispatch_event("on_draw") # display the frame buffer self.window.flip() def update_mouse(self, dt): # USE MOUSE VALUES HERE pass # reset the relative position of the mouse self.mouse.clear_delta() def on_key_event(self, digital, event, key): # HANDLE KEYBOARD INPUT HERE pass def update_scene(self, dt): # UPDATE SCENE HERE pass def on_draw(self): # render the scene self.render() # render the fps self.fps_display.draw() def render(self): # # setup # # set our window self.window.switch_to() # activate our viewport self.viewport.switch_to() # scissor to our viewport self.viewport.scissor_to_viewport() # setup our viewport properties glPushAttrib(GL_ALL_ATTRIB_BITS) # update the view matrix aspect ratio self.camera.view_matrix.aspect_ratio = self.viewport.aspect_ratio # apply our view matrix and camera translation self.camera.view_matrix.push_view_matrix() self.camera.push_model_view() # # render # # clear our frame buffer and depth buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) self.render_3d() # # tear down # # pop our view matrix and camera translation self.camera.pop_model_view() self.camera.view_matrix.pop_view_matrix() # pop our viewport attributes glPopAttrib() # # reset state # # set our viewport to the entire window pygly.gl.set_scissor(pygly.window.create_rectangle(self.window)) pygly.gl.set_viewport(pygly.window.create_rectangle(self.window)) # # render 2d # self.render_2d() def render_2d(self): # render the fps self.fps_display.draw()
def setup_scene( self ): """Creates the scene to be rendered. Creates our camera, scene graph, """ # don't call 'SimpleApplication's setup_scene CoreApplication.setup_scene( self ) # setup our GL state # enable z buffer glEnable( GL_DEPTH_TEST ) # enable back face culling glEnable( GL_CULL_FACE ) glCullFace( GL_BACK ) # load our texture # use the PIL decoder as the pyglet one is broken # and loads most images as greyscale path = os.path.join( os.path.dirname( __file__ ), '../../data/md2/sydney.bmp' ) image = Image.open( path ) self.texture = Texture2D( GL_TEXTURE_2D ) self.texture.bind() self.texture.set_min_mag_filter( min = GL_LINEAR, mag = GL_LINEAR ) # load the image from PIL # MD2 textures are inverted pygly.pil_texture.set_pil_image( self.texture, image, flip = False ) self.texture.unbind() # create a grid of cubes self.grid_root = SceneNode( 'grid_root' ) self.scene_node.add_child( self.grid_root ) # create a number of cubes # the grid will extend from -5 to +5 x,z = numpy.mgrid[ -5:5:11j, -5:5:11j ] x = x.flatten() z = z.flatten() positions = numpy.vstack( (x, numpy.zeros( x.shape ), z ) ) positions = positions.T # set the distance between the models positions *= 4.5 # store a list of renderables self.renderables = [] path = os.path.join( os.path.dirname( __file__ ), '../../data/md2/sydney.md2' ) for position in positions: node = RenderCallbackNode( 'node-%s' % position, None, self.render_node ) node.mesh = MD2_Mesh( path ) node.mesh.load() # attach to our scene graph self.grid_root.add_child( node ) self.renderables.append( node ) # move and scale the node node.transform.inertial.translation = position node.transform.scale = 0.2 # create a range of animation times # 0.0 <= x < num_frames self.frames = numpy.linspace( 0.0, float(self.renderables[ 0 ].mesh.num_frames), len(positions), endpoint = False ) # create an array that will store our frame rates self.frame_rate = numpy.zeros( len(self.renderables), dtype = numpy.float ) self.animation = ''
class OBJ_Application(SimpleApplication): def setup_viewports(self): super(OBJ_Application, self).setup_viewports() self.colours[0] = (0.1, 0.1, 0.1, 1.0) def setup_camera(self): super(OBJ_Application, self).setup_camera() # move the camera self.cameras[0].transform.inertial.translate([0.0, -3.0, -5.0]) # tilt the camera downward self.cameras[0].transform.object.rotate_x(math.pi / 8.0) def setup_scene(self): """Creates the scene to be rendered. Creates our camera, scene graph, """ # don't call 'SimpleApplication's setup_scene CoreApplication.setup_scene(self) # setup our GL state # enable z buffer glEnable(GL_DEPTH_TEST) # enable back face culling glEnable(GL_CULL_FACE) glCullFace(GL_BACK) # create a grid of cubes self.grid_root = SceneNode('grid_root') self.scene_node.add_child(self.grid_root) # store a list of renderables path = os.path.join(os.path.dirname(__file__), '../data/obj/capsule.obj') self.mesh_node = RenderCallbackNode('mesh', None, self.render_node) # rotate the mesh to face the camera self.mesh_node.transform.object.rotate_y(math.pi) self.mesh_node.mesh = OBJ_Mesh(path) self.mesh_node.mesh.load() # attach to our scene graph self.grid_root.add_child(self.mesh_node) # scale the node self.mesh_node.transform.scale = 1.0 # create a list of groups to render # by default, render all groups # this may be in-efficient if data is contained in # multiple groups self.groups = self.mesh_node.mesh.data.meshes.keys() def step(self, dt): """Updates our scene and triggers the on_draw event. This is scheduled in our __init__ method and called periodically by pyglet's event callbacks. We need to manually call 'on_draw' as we patched it our of pyglets event loop when we patched it out with pygly.monkey_patch. Because we called 'on_draw', we also need to perform the buffer flip at the end. """ # setup the scene # rotate the scene nodes about their vertical axis self.grid_root.transform.object.rotate_y(dt * 0.2) # this will trigger the draw event and buffer flip CoreApplication.step(self, dt) def render_scene(self, camera): """Renders each renderable in the scene using the current projection and model view matrix. The original GL state will be restored upon leaving this function. """ projection = camera.view_matrix.matrix model_view = camera.model_view # update the model view world_matrix = self.mesh_node.world_transform.matrix current_mv = matrix44.multiply(world_matrix, model_view) # render a cube self.mesh_node.render(projection=projection, model_view=current_mv, groups=self.groups) def render_node(self, node, **kwargs): node.mesh.render(**kwargs)
def setup_scene_root(self): # create a list of renderables self.renderables = [] # create a scene self.scene_node = SceneNode("root")
class Application( object ): def __init__( self ): super( Application, self ).__init__() # setup our opengl requirements config = pyglet.gl.Config( depth_size = 16, double_buffer = True ) # create our window self.window = pyglet.window.Window( fullscreen = False, width = 1024, height = 768, resizable = True, vsync = False, config = config ) # create a viewport self.viewport = RatioViewport( self.window, [ [0.0, 0.0], [1.0, 1.0] ] ) # setup our scene self.setup_scene() # listen for on_draw events self.window.push_handlers( on_draw = self.on_draw ) # setup our update loop the app # we'll render at 60 fps frequency = 60.0 self.update_delta = 1.0 / frequency # over-ride the frequency and render at full speed self.update_delta = -1 # use a pyglet callback for our render loop pyglet.clock.schedule_interval( self.step, self.update_delta ) def setup_scene( self ): # create an fps display self.fps_display = pyglet.clock.ClockDisplay() # store a list of our renderables self.renderables = [] # create a scene self.scene_node = SceneNode( 'root' ) self.mesh_node = SceneNode( 'obj' ) self.scene_node.add_child( self.mesh_node ) # create a mesh object and render node self.mesh = OBJ_Mesh( 'examples/data/obj/cessna.obj' ) self.mesh_render_node = RenderCallbackNode( 'mesh', self.initialise_mesh, self.render_mesh ) self.mesh_node.add_child( self.mesh_render_node ) # add to our list of renderables self.renderables.append( self.mesh_render_node ) # create a camera and a view matrix self.view_matrix = ProjectionViewMatrix( self.viewport.aspect_ratio, fov = 45.0, near_clip = 1.0, far_clip = 200.0 ) # create a camera self.camera = CameraNode( 'camera', self.view_matrix ) self.scene_node.add_child( self.camera ) # move the camera so we can see the model self.camera.transform.object.translate( [ 0.0, 20.0, 30.0 ] ) # rotate the camera so it is pointing down self.camera.transform.object.rotate_x( -math.pi / 4.0 ) def run( self ): pyglet.app.run() def step( self, dt ): # rotate the mesh about it's own vertical axis self.mesh_node.transform.object.rotate_y( dt ) # manually dispatch the on_draw event # as we patched it out of the idle loop self.window.dispatch_event( 'on_draw' ) # display the frame buffer self.window.flip() def on_draw( self ): # render the scene self.render() # render the fps self.fps_display.draw() def initialise_mesh( self ): # load the obj mesh from the file self.mesh.load() def render_mesh( self ): # render the mesh self.mesh.render() def set_gl_state( self ): # enable z buffer glEnable( GL_DEPTH_TEST ) # enable smooth shading glShadeModel( GL_SMOOTH ) # rescale only normals for lighting glEnable( GL_RESCALE_NORMAL ) # enable scissoring for viewports glEnable( GL_SCISSOR_TEST ) # enable back face culling glEnable( GL_CULL_FACE ) glCullFace( GL_BACK ) # setup lighting for our viewport glEnable( GL_LIGHTING ) # set an ambient light level glAmbient = glLightModelfv( GL_LIGHT_MODEL_AMBIENT, (GLfloat * 4)( *[ 0.8, 0.8, 0.8, 1.0 ] ) ) # create a light glEnable( GL_LIGHT0 ) glLightfv( GL_LIGHT0, GL_POSITION, (GLfloat * 4)( *[0.0, 20.0, 30.0, 1.0] ) ) def render( self ): # # setup # # set our window self.window.switch_to() # activate our viewport self.viewport.switch_to() # scissor to our viewport self.viewport.scissor_to_viewport() # setup our viewport properties glPushAttrib( GL_ALL_ATTRIB_BITS ) self.set_gl_state() # update the view matrix aspect ratio self.camera.view_matrix.aspect_ratio = self.viewport.aspect_ratio # apply our view matrix and camera translation self.camera.view_matrix.push_view_matrix() self.camera.push_model_view() # # render # # clear our frame buffer and depth buffer glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) for renderable in self.renderables: renderable.render() # # tear down # # pop our view matrix and camera translation self.camera.pop_model_view() self.camera.view_matrix.pop_view_matrix() # pop our viewport attributes glPopAttrib() # # reset state # # set our viewport to the entire window pygly.gl.set_scissor( pygly.window.create_rectangle( self.window ) ) pygly.gl.set_viewport( pygly.window.create_rectangle( self.window ) )
class Application( BaseApplication ): def __init__( self ): self.print_opengl_versions() super( Application, self ).__init__( "PyGLy - Conway's Game of Life" ) def print_opengl_versions( self ): # get OpenGL version print "OpenGL version", gl_info.get_version() # get GLSL version plain = string_at(glGetString(GL_SHADING_LANGUAGE_VERSION)).split(' ')[0] major, minor = map(int, plain.split('.')) version = major * 100 + minor print "GLSL Version", version def setup_camera( self ): super( Application, self ).setup_camera() # move the camera so we can see the scene self.camera.transform.inertial.translate( [ 0.0, 0.0, 5.0 ] ) def setup_ui( self ): super( Application, self ).setup_ui() self.help_label = pyglet.text.HTMLLabel( """ <b>Game of Life</b> """, multiline = True, x = 10, y = 50, width = 500, anchor_x = 'left', anchor_y = 'bottom', ) self.help_label.color = (255,255,255,255) def setup_scene( self ): super( Application, self ).setup_scene() # enable texturing glEnable( GL_TEXTURE_2D ) # set our gl clear colour glClearColor( 0.2, 0.2, 0.2, 1.0 ) # create a scene node to hold our GOL renderable self.gol_node = SceneNode( "GOL_Node" ) self.scene_node.add_child( self.gol_node ) # get the maximum viewport size max_viewport_size = (c_int * 2)() glGetIntegerv( GL_MAX_VIEWPORT_DIMS, max_viewport_size ) max_viewport_size = (max_viewport_size[ 0 ], max_viewport_size[ 1 ]) print "Max viewport size", max_viewport_size # make the GOL board # ensure it is a power of 2 for ou texture board_size = (2048, 2048) if board_size > max_viewport_size: board_size = max_viewport_size self.gol = GOL_Renderable( board_size ) # add our renderable to the scene self.gol_node.add_child( self.gol ) # add to our list of renderables self.renderables.append( self.gol ) def update_mouse( self, dt ): # USE MOUSE VALUES HERE pass # reset the relative position of the mouse super( Application, self ).update_mouse( dt ) def on_key_event( self, digital, event, key ): # HANDLE KEYBOARD INPUT HERE pass def update_scene( self, dt ): # rotate the GOL board self.gol_node.transform.object.rotate_y( dt * 0.3 ) # add the delta to the accumulated time self.gol.dt += dt def render_3d( self ): # enable z buffer glEnable( GL_DEPTH_TEST ) # disable lighting glDisable( GL_LIGHTING ) # enable smooth shading glShadeModel( GL_SMOOTH ) # rescale only normals for lighting glEnable( GL_RESCALE_NORMAL ) # enable scissoring for viewports glEnable( GL_SCISSOR_TEST ) # enable back face culling #glEnable( GL_CULL_FACE ) #glCullFace( GL_BACK ) glDisable( GL_CULL_FACE ) # render each object for renderable in self.renderables: renderable.render() def render_2d( self ): super( Application, self ).render_2d() self.help_label.draw()
class Scene(scene.Scene): def __init__(self, core_profile=True): super(Scene, self).__init__(core_profile) @property def name(self): return "Basic" def initialise(self): super(Scene, self).initialise() def set_gl_state(): # setup our GL state # enable z buffer GL.glEnable(GL.GL_DEPTH_TEST) # enable back face culling GL.glEnable(GL.GL_CULL_FACE) GL.glCullFace(GL.GL_BACK) set_gl_state() # create a viewport # this will be updated before we begin self.viewport = None # create a cube to render self.cube = renderable_cube.create(self.core_profile) def setup_scene(): # create a scene # we'll create the scene as a tree # to demonstrate the depth-first iteration # technique we will use to render it self.scene_root = SceneNode('root') # the letter indicates the tier the node # is on, a = tier 1, b = tier 2, etc. self.a1 = SceneNode('a1') self.b1 = SceneNode('b1') self.b2 = SceneNode('b2') self.c1 = SceneNode('c1') self.c2 = SceneNode('c2') self.c3 = SceneNode('c3') # the tree looks as follows # / c1 # / b1 - c2 # root - a1 # \ b2 - c3 self.scene_root.add_child(self.a1) self.a1.add_child(self.b1) self.a1.add_child(self.b2) self.b1.add_child(self.c1) self.b1.add_child(self.c2) self.b2.add_child(self.c3) # if we set the nodes local scale (transform) # it will be affected by the parent's scale. # by setting the world scale (world_transform) # we are ignoring the parent's scale. # re-attaching the node would invalidate this. self.a1.world_transform.scale = [2.0, 2.0, 2.0] self.b1.world_transform.scale = [1.0, 1.0, 1.0] self.b2.world_transform.scale = [1.0, 1.0, 1.0] self.c1.world_transform.scale = [0.8, 0.8, 0.8] self.c2.world_transform.scale = [0.8, 0.8, 0.8] self.c3.world_transform.scale = [0.8, 0.8, 0.8] # move our scene nodes # leave a1 at the centre self.b1.transform.object.translate([10.0, 0.0, 0.0]) self.b2.transform.object.translate([-10.0, 0.0, 0.0]) self.c1.transform.object.translate([5.0, 0.0, 0.0]) self.c2.transform.object.translate([-5.0, 0.0, 0.0]) self.c3.transform.object.translate([5.0, 0.0, 0.0]) # rotate the our b nodes so they tilting forward self.b1.transform.object.rotate_x(math.pi / 4.0) self.b2.transform.object.rotate_x(math.pi / 4.0) setup_scene() def setup_camera(): # create our camera node self.camera = CameraNode('camera', pyrr.matrix44.create_identity()) self.scene_root.add_child(self.camera) # move the camera so we're not inside # the root scene node's debug cube self.camera.transform.object.translate([0.0, 30.0, 35.0]) # tilt the camera downward self.camera.transform.object.rotate_x(-math.pi / 4.0) setup_camera() def on_window_resized(self, width, height): # update the viewport self.viewport = pyrr.rectangle.create_from_position(x=0, y=0, width=width, height=height) # update the projection matrix # we need to do this or the rendering will become skewed with each # resize of viewport change aspect_ratio = pyrr.rectangle.aspect_ratio(self.viewport) self.camera.projection_matrix = pyrr.matrix44.create_perspective_projection_matrix( fovy=80.0, aspect=aspect_ratio, near=1.0, far=100.0) def step(self, dt): super(Scene, self).step(dt) # setup the scene # rotate the scene nodes about their vertical axis self.a1.transform.object.rotate_y(dt) self.b1.transform.object.rotate_y(dt) self.b2.transform.object.rotate_y(dt) self.c1.transform.object.rotate_y(dt) self.c2.transform.object.rotate_y(dt) self.c3.transform.object.rotate_y(dt) def render(self): super(Scene, self).render() # this code is split into 2 functions to support the # multiple_viewports scene which inherits from this class self.render_viewport(self.viewport, self.camera) def render_viewport(self, viewport, camera): def render_core(): projection = camera.projection_matrix model_view = camera.model_view for node in self.scene_root.dfs(): if isinstance(node, CameraNode): continue # get the node's world matrix world_matrix = node.world_transform.matrix # apply the camera's model view to the node's matrix current_mv = pyrr.matrix44.multiply(world_matrix, model_view) # render a cube self.cube.draw(projection, current_mv) def render_legacy(): # load our projection matrix with pygly.gl.mode_and_matrix(GL.GL_PROJECTION, camera.projection_matrix): with pygly.gl.mode_and_matrix(GL.GL_MODELVIEW, camera.model_view): # iterate through the scene graph for node in self.scene_root.dfs(): # don't render cameras if isinstance(node, CameraNode): continue world_matrix = node.world_transform.matrix # multiply the existing model view matrix # by the model's world matrix # then render a cube with pygly.gl.multiply_matrix(world_matrix): self.cube.draw() # activate our viewport pygly.viewport.set_viewport(viewport) # set our scissor to the main viewport pygly.viewport.set_scissor(viewport) # clear the colour and depth buffer GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) # render the scene if self.core_profile: render_core() else: render_legacy()
class MD2_Application(SimpleApplication): def setup_viewports(self): super(MD2_Application, self).setup_viewports() self.colours[0] = (1.0, 1.0, 1.0, 1.0) def setup_scene(self): """Creates the scene to be rendered. Creates our camera, scene graph, """ # don't call 'SimpleApplication's setup_scene CoreApplication.setup_scene(self) # setup our GL state # enable z buffer glEnable(GL_DEPTH_TEST) # enable back face culling glEnable(GL_CULL_FACE) glCullFace(GL_BACK) # load our texture # use the PIL decoder as the pyglet one is broken # and loads most images as greyscale path = os.path.join(os.path.dirname(__file__), '../../data/md2/sydney.bmp') image = Image.open(path) self.texture = Texture2D(GL_TEXTURE_2D) self.texture.bind() self.texture.set_min_mag_filter(min=GL_LINEAR, mag=GL_LINEAR) # load the image from PIL # MD2 textures are inverted pygly.pil_texture.set_pil_image(self.texture, image, flip=False) self.texture.unbind() # create a grid of cubes self.grid_root = SceneNode('grid_root') self.scene_node.add_child(self.grid_root) # create a number of cubes # the grid will extend from -5 to +5 x, z = numpy.mgrid[-5:5:11j, -5:5:11j] x = x.flatten() z = z.flatten() positions = numpy.vstack((x, numpy.zeros(x.shape), z)) positions = positions.T # set the distance between the models positions *= 4.5 # store a list of renderables self.renderables = [] path = os.path.join(os.path.dirname(__file__), '../../data/md2/sydney.md2') for position in positions: node = RenderCallbackNode('node-%s' % position, None, self.render_node) node.mesh = MD2_Mesh(path) node.mesh.load() # attach to our scene graph self.grid_root.add_child(node) self.renderables.append(node) # move and scale the node node.transform.inertial.translation = position node.transform.scale = 0.2 # create a range of animation times # 0.0 <= x < num_frames self.frames = numpy.linspace(0.0, float( self.renderables[0].mesh.num_frames), len(positions), endpoint=False) # create an array that will store our frame rates self.frame_rate = numpy.zeros(len(self.renderables), dtype=numpy.float) self.animation = '' def step(self, dt): """Updates our scene and triggers the on_draw event. This is scheduled in our __init__ method and called periodically by pyglet's event callbacks. We need to manually call 'on_draw' as we patched it our of pyglets event loop when we patched it out with pygly.monkey_patch. Because we called 'on_draw', we also need to perform the buffer flip at the end. """ # setup the scene # rotate the scene nodes about their vertical axis self.grid_root.transform.object.rotate_y(dt * 0.2) # update our frame rates self.frame_rate[:] = [ mesh_node.mesh.frame_rate for mesh_node in self.renderables ] # increment our frame #self.frames += dt * fps self.frames += dt * self.frame_rate numpy.mod(self.frames, self.renderables[0].mesh.num_frames, self.frames) # print the animation name of the first mesh curr_anim = self.renderables[0].mesh.animation if self.animation != curr_anim: self.animation = curr_anim print 'Curren animation:', self.animation # this will trigger the draw event and buffer flip CoreApplication.step(self, dt) def render_scene(self, camera): """Renders each renderable in the scene using the current projection and model view matrix. The original GL state will be restored upon leaving this function. """ projection = camera.view_matrix.matrix model_view = camera.model_view # bind our diffuse texture glActiveTexture(GL_TEXTURE0) self.texture.bind() # iterate through our renderables for node, frame in zip(self.renderables, self.frames): # update the model view world_matrix = node.world_transform.matrix current_mv = matrix44.multiply(world_matrix, model_view) fraction, frame1 = math.modf(frame) frame2 = (frame1 + 1.0) % node.mesh.num_frames # update the frame node.mesh.frame_1 = int(frame1) node.mesh.frame_2 = int(frame2) node.mesh.interpolation = fraction # render a cube node.render(projection=projection, model_view=current_mv) glActiveTexture(GL_TEXTURE0) self.texture.unbind() def render_node(self, node, **kwargs): node.mesh.render(**kwargs)
class Scene(scene_scene_graph.Scene): def __init__(self, core_profile=True): super(Scene, self).__init__(core_profile) def initialise(self): super(Scene, self).initialise() def set_gl_state(): # setup our GL state # enable scissoring for viewports GL.glEnable(GL.GL_SCISSOR_TEST) set_gl_state() self.viewport2 = None def setup_camera(): # add another node that our camera will be under # we can rotate this node to show how the cameras work self.camera2_parent = SceneNode('camera2_parent') self.scene_root.add_child(self.camera2_parent) # create our camera node self.camera2 = CameraNode('camera2', pyrr.matrix44.create_identity()) self.camera2_parent.add_child(self.camera2) # place the camera at the same position as the previous one #self.camera2.transform.translation = self.camera.transform.translation #self.camera2.transform.orientation = self.camera.transform.orientation #self.camera2.transform.scale = self.camera.transform.scale self.camera2.transform.object.translate([0.0, 0.0, 35.0]) setup_camera() def on_window_resized(self, width, height): #super( Scene, self ).on_window_resized( width, height ) def update_viewports(): half_width = width / 2.0 # update the viewport self.viewport = pyrr.rectangle.create_from_position( x=0, y=0, width=half_width, height=height) self.viewport2 = pyrr.rectangle.create_from_position( x=half_width, y=0, width=half_width, height=height) print(self.viewport, self.viewport2) def update_cameras(): # update the projection matrix # we need to do this or the rendering will become skewed with each # resize of viewport change aspect_ratio = pyrr.rectangle.aspect_ratio(self.viewport) self.camera.projection_matrix = pyrr.matrix44.create_perspective_projection_matrix( fovy=80.0, aspect=aspect_ratio, near=1.0, far=100.0) # update the projection matrix # we need to do this or the rendering will become skewed with each # resize of viewport change aspect_ratio = pyrr.rectangle.aspect_ratio(self.viewport2) self.camera2.projection_matrix = pyrr.matrix44.create_perspective_projection_matrix( fovy=80.0, aspect=aspect_ratio, near=1.0, far=100.0) update_viewports() update_cameras() def step(self, dt): super(Scene, self).step(dt) # setup the scene # rotate the scene nodes about their vertical axis self.camera2_parent.transform.object.rotate_x(dt) def render(self): # render viewport 1 super(Scene, self).render() # change our clear colour to make it clear where the viewport is GL.glClearColor(1.0, 1.0, 1.0, 1.0) # render the viewport self.render_viewport(self.viewport2, self.camera2)
class OBJ_Application( SimpleApplication ): def setup_viewports( self ): super( OBJ_Application, self ).setup_viewports() self.colours[ 0 ] = (0.1,0.1,0.1,1.0) def setup_camera( self ): super( OBJ_Application, self ).setup_camera() # move the camera self.cameras[ 0 ].transform.inertial.translate( [ 0.0,-3.0,-5.0 ] ) # tilt the camera downward self.cameras[ 0 ].transform.object.rotate_x( math.pi / 8.0 ) def setup_scene( self ): """Creates the scene to be rendered. Creates our camera, scene graph, """ # don't call 'SimpleApplication's setup_scene CoreApplication.setup_scene( self ) # setup our GL state # enable z buffer glEnable( GL_DEPTH_TEST ) # enable back face culling glEnable( GL_CULL_FACE ) glCullFace( GL_BACK ) # create a grid of cubes self.grid_root = SceneNode( 'grid_root' ) self.scene_node.add_child( self.grid_root ) # store a list of renderables path = os.path.join( os.path.dirname( __file__ ), '../data/obj/capsule.obj' ) self.mesh_node = RenderCallbackNode( 'mesh', None, self.render_node ) # rotate the mesh to face the camera self.mesh_node.transform.object.rotate_y( math.pi ) self.mesh_node.mesh = OBJ_Mesh( path ) self.mesh_node.mesh.load() # attach to our scene graph self.grid_root.add_child( self.mesh_node ) # scale the node self.mesh_node.transform.scale = 1.0 # create a list of groups to render # by default, render all groups # this may be in-efficient if data is contained in # multiple groups self.groups = self.mesh_node.mesh.data.meshes.keys() def step( self, dt ): """Updates our scene and triggers the on_draw event. This is scheduled in our __init__ method and called periodically by pyglet's event callbacks. We need to manually call 'on_draw' as we patched it our of pyglets event loop when we patched it out with pygly.monkey_patch. Because we called 'on_draw', we also need to perform the buffer flip at the end. """ # setup the scene # rotate the scene nodes about their vertical axis self.grid_root.transform.object.rotate_y( dt * 0.2 ) # this will trigger the draw event and buffer flip CoreApplication.step( self, dt ) def render_scene( self, camera ): """Renders each renderable in the scene using the current projection and model view matrix. The original GL state will be restored upon leaving this function. """ projection = camera.view_matrix.matrix model_view = camera.model_view # update the model view world_matrix = self.mesh_node.world_transform.matrix current_mv = matrix44.multiply( world_matrix, model_view ) # render a cube self.mesh_node.render( projection = projection, model_view = current_mv, groups = self.groups ) def render_node( self, node, **kwargs ): node.mesh.render( **kwargs )