indexing description: "[ A scene which uses OpenGL to draw its content, and which can be rotated and zoomed using the mouse Inherit from this class and put your OpenGL drawing code in the feature `draw'. Overwrite predraw for all the tasks before drawing, and postdraw for those after. Overwrite set_default_settings to change some or all the default settings. Have a look at the code of `redraw' and `initialize_scene' from EM3D_SCENE to see which options of OpenGL are alredy set. To react on input events, redefine the various `handle_*' features. ]" date: "$Date$" revision: "$Revision$" deferred class EM3D_TRACKBALL_SCENE inherit EM3D_SCENE export {NONE} active_camera redefine make_scene, opengl_draw, handle_mouse_button_down_event, handle_mouse_button_up_event, handle_mouse_motion_event, initialize_scene end DOUBLE_MATH export {NONE} all end GL_FUNCTIONS export {NONE} all end EMGL_BASIC_RENDERER export {NONE} all end feature {NONE} -- Initialisation make_scene is -- Initialise default values. do Precursor {EM3D_SCENE} -- Remember width and height of the window window_width := video_subsystem.video_surface_width window_height := video_subsystem.video_surface_height -- Standard value covers whole the screen trackball_radius := sqrt(2.0) rotation_factor := 6.0 if camera_distance = 0 then camera_distance := 3.0 end end initialize_scene is -- Set up the camera do Precursor camera_rotation := scene_manager.world.new_child active_camera_node.remove_from_parent camera_rotation.add_child( active_camera_node ) active_camera_node.set_position ([0.0, 0.0,-camera_distance]) active_camera.set_up ( [0,1,0] ) active_camera.set_is_local_up ( true ) active_camera.set_target ( scene_manager.target.create_new ( "Main target" ) ) scene_manager.world.attach_object ( scene_manager.target.item_by_name ( "Main target" ) ) end feature -- Navigation handle_mouse_button_down_event (a_mouse_button_event: EM_MOUSEBUTTON_EVENT) is -- handle mouse button down do if a_mouse_button_event.is_left_button then rotate := true projection := project_to_trackball ([a_mouse_button_event.x, a_mouse_button_event.y]) elseif a_mouse_button_event.is_middle_button then zoom := true previous_zoom_pos := normalize_screen_coordinate ([a_mouse_button_event.x, a_mouse_button_event.y]) end end handle_mouse_motion_event (a_mouse_motion_event: EM_MOUSEMOTION_EVENT) is -- Handle mouse motion event local x, y: INTEGER rotation: EM_QUATERNION do if rotate then x := a_mouse_motion_event.x y := a_mouse_motion_event.y previous_projection := projection projection := project_to_trackball ([x, y]) rotation.set_from_arc (previous_projection, projection) -- We need a camra space rotation camera_rotation.rotate ( rotation ) elseif zoom then zoom_pos := normalize_screen_coordinate ([a_mouse_motion_event.x, a_mouse_motion_event.y]) active_camera_node.set_position ( active_camera_node.position * (1.0 + (zoom_pos.y - previous_zoom_pos.y)) ) previous_zoom_pos := zoom_pos end end handle_mouse_button_up_event (a_mouse_button_event: EM_MOUSEBUTTON_EVENT) is -- handle mouse button up do rotate := false zoom := false end feature {NONE} -- Navigation normalize_screen_coordinate(point: EM_VECTOR2I): EM_VECTOR2D is -- Translate screen coordinate into coordinate between -1.0 and 1.0 local x: DOUBLE y: DOUBLE do x := (2.0 * point.x) / window_width - 1.0 y := 1.0 - (2.0 * point.y) / window_height Result := [ x, y ] end project_to_trackball (point: EM_VECTOR2I): EM_VECTOR3D is -- project the points x and y on the trackball local x, y, z: DOUBLE point_radius: DOUBLE squared_z: DOUBLE do -- Scale x and y to a value between -1.0 and 1.0 x := ((2.0 * point.x) / window_width - 1.0) y := ((2.0 * point.y) / window_height - 1.0) point_radius := sqrt(x*x + y*y) -- this allowes the trackball to be smaller than the window -- btw: this enables the user to rotate the object only around the z axis -- this can already be done the other axis two axes if point_radius > trackball_radius then x := x / point_radius * trackball_radius y := y / point_radius * trackball_radius end squared_z := trackball_radius * trackball_radius - x * x - y * y if squared_z > 0 then z := - sqrt(squared_z) -- Keep in mind that opengl has a negative x axis else -- due to numerical round_off errors squared_z can become a small negative number and sqrt would fail z := 0 end Result := [x, y, z] ensure valid_x: Result.x.abs <= 1.0 valid_y: Result.y.abs <= 1.0 valid_z: Result.z <= 0 and Result.z.abs <= trackball_radius -- z must be smaller than zero because of the opengl coordinate system and we projected on the front hemi-sphere end feature -- Access camera_rotation: EM3D_SCENE_NODE camera_distance: DOUBLE show_movement_help: BOOLEAN feature -- Drawing opengl_draw is -- Do the actual drawing. Dont overwrite this function -- unless you want to flush the buffers on you own local direction: EM_VECTOR3D do -- Reset depth buffer and clear the screen emgl_clear (Em_gl_color_buffer_bit | Em_gl_depth_buffer_bit) -- Clear modelview matrix emgl_load_identity if active_camera/=void then active_camera.set_opengl end if show_movement_help and rotate then emgl_color3d( 1, 0, 0 ) x_arrow.render_opengl emgl_color3d( 0, 1, 0 ) y_arrow.render_opengl emgl_color3d( 0, 0, 1 ) z_arrow.render_opengl end -- Draw scene draw end feature{NONE} -- Implementation x_arrow: EM3D_ARROW is -- The x axis once create result.make( [0,0,0], [10,0,0] ) result.set_head_radius ( 1 ) result.set_radius ( 0.5 ) end y_arrow: EM3D_ARROW is -- The x axis once create result.make( [0,0,0], [0,10,0] ) result.set_head_radius ( 1 ) result.set_radius ( 0.5 ) end z_arrow: EM3D_ARROW is -- The x axis once create result.make( [0,0,0], [0,0,10] ) result.set_head_radius ( 1 ) result.set_radius ( 0.5 ) end feature -- Implementation axis_dir: EM_VECTOR3D -- TODO: remove window_width, window_height: INTEGER -- width and height of the window previous_projection: EM_VECTOR3D projection: EM_VECTOR3D -- Projection of the mouse on the trackball rotate: BOOLEAN -- Enables/Disables rotation zoom: BOOLEAN previous_zoom_pos: EM_VECTOR2D zoom_pos: EM_VECTOR2D -- Position of the cursor used for zooming trackball_radius: DOUBLE -- Size of the trackball (the window ranges from -1.0 to 1.0) -- If this value is changed, you'll probably also want to change -- rotation_factor rotation_factor: DOUBLE -- Factor which determines the speed of rotation -- Bigger value means faster rotation -- Examples: -- 1.0: When moving the mouse once across whole the trackpoint, the object rotates 180 degrees -- 2.0: When moving the mouse once across whole the trackpoint, the object rotates 360 degrees end