indexing description: "[ Example component to show implementation of a 3D scene component. Drawing: Implement 'draw' and put your OpenGL code there. Events: See EM_COMPONENT ]" date: "$Date$" revision: "$Revision$" class SIERPINSKI_DISPLAY inherit EM_3D_COMPONENT SINGLE_MATH export {NONE} all end create make feature {NONE} -- Initialisation make is -- Initialise default values. local point1, color1: GL_VECTOR_3D [REAL] point2, color2: GL_VECTOR_3D [REAL] point3, color3: GL_VECTOR_3D [REAL] point4, color4: GL_VECTOR_3D [REAL] fog_color: GL_VECTOR_4D [REAL] do make_3d_component set_tooltip ("drag to rotate / left click to increase detail / right click to decrease detail") subdivisions := 0 longitude := 10 latitude := 20 distance := 2.5 create point1.make_xyz (1, -0.4, 0) create point2.make_xyz (-cosine (Pi/3), -0.4, sine (Pi/3)) create point3.make_xyz (-cosine (Pi/3), -0.4, -sine (Pi/3)) create point4.make_xyz (0, 1, 0) create color1.make_xyz (1, 0, 0) create color2.make_xyz (0, 1, 0) create color3.make_xyz (0, 0, 1) create color4.make_xyz (1, 1, 1) create pyramid.make (point1, point2, point3, point4, color1, color2, color3, color4) pyramid.subdivide (subdivisions) -- Light create light.make (Em_gl_light0) light.ambient.set_xyzt (0, 0, 0, 1) light.specular.set_xyzt (1, 1, 1, 1) light.diffuse.set_xyzt (1, 1, 1, 1) light.position.set_xyz (0.5, 0.5, 0.5) light.apply_values -- Fog create fog_color.make_xyzt (0, 0, 0, 1.0) gl_fogi (Em_gl_fog_mode, Em_gl_exp2) gl_fogfv (Em_gl_fog_color, fog_color.pointer) gl_fogf (Em_gl_fog_density, 0.35) gl_hint (Em_gl_fog_hint, Em_gl_nicest) gl_fogf (Em_gl_fog_start, 1) gl_fogf (Em_gl_fog_end, 4) -- Event registration mouse_clicked_event.subscribe (agent handle_mouse_clicked) mouse_dragged_event.subscribe (agent handle_mouse_dragged) mouse_wheel_up_event.subscribe (agent handle_mouse_wheel_up) mouse_wheel_down_event.subscribe (agent handle_mouse_wheel_down) end feature -- Access subdivisions: INTEGER -- Level of subdivision longitude: REAL -- Longitude of the eye (in degree) around center (0,0,0) latitude: REAL -- Latitude of the eye (in degree) around center (0,0,0) distance: REAL -- Distance of the eye to center (0,0,0) pyramid: SIERPINSKI_PYRAMID -- Subdivided pyramid pyramid_display_list: INTEGER -- Display list id of pyramid light: GL_LIGHT -- OpenGL light feature -- Status report is_lighting_enabled: BOOLEAN -- Is lighting enabled? is_fog_enabled: BOOLEAN -- Is fog enabled? feature -- Status setting enable_lighting is -- Enable lighting. do is_lighting_enabled := True ensure lighting_enabled: is_lighting_enabled end disable_lighting is -- Disable lighting do is_lighting_enabled := False ensure lighting_disabled: not is_lighting_enabled end enable_fog is -- Enable fog. do is_fog_enabled := True ensure fog_enabled: is_fog_enabled end disable_fog is -- Disable fog do is_fog_enabled := False ensure fog_disabled: not is_fog_enabled end feature -- Basic operations increase_distance is -- Increase distance. do distance := distance + 0.5 if distance > 6 then distance := 6 end end decrease_distance is -- Decrease distance. do distance := distance - 0.5 if distance < 0 then distance := 0 end end increase_subdivisions is -- Increase subdivisions. do subdivisions := subdivisions + 1 pyramid.subdivide (subdivisions) -- Free display list to trigger generation of a new one gl_delete_lists (pyramid_display_list, 1) pyramid_display_list := 0 ensure subdivisions_increased: subdivisions = old subdivisions + 1 end decrease_subdivisions is -- Decrease subdivisions. do if subdivisions > 0 then subdivisions := subdivisions - 1 pyramid.subdivide (subdivisions) -- Free display list to trigger generation of a new one gl_delete_lists (pyramid_display_list, 1) pyramid_display_list := 0 end ensure subdivisions_decreased: old subdivisions > 0 implies subdivisions = old subdivisions - 1 end feature -- Drawing draw is -- Draw pyramid. do -- Set up eye position gl_translatef (0, 0, -distance) gl_rotatef (-longitude, 0, 1, 0) gl_rotatef (latitude, cosine (-longitude / 180 * pi),0,sine (-longitude / 180 * pi)) -- Generate a display list if necessary and call it if pyramid_display_list = 0 then pyramid_display_list := gl_gen_lists (1) gl_new_list (pyramid_display_list, Em_gl_compile) pyramid.draw gl_end_list end if is_lighting_enabled then gl_enable (Em_gl_lighting) gl_enable (Em_gl_color_material) gl_color_material (Em_gl_front_and_back, Em_gl_ambient_and_diffuse) light.enable end if is_fog_enabled then gl_enable (Em_gl_fog) end gl_call_list (pyramid_display_list) if is_lighting_enabled then gl_disable (Em_gl_color_material) gl_disable (Em_gl_lighting) end if is_fog_enabled then gl_disable (Em_gl_fog) end end feature {NONE} -- Mouse management handle_mouse_clicked (event: EM_MOUSEBUTTON_EVENT) is -- Handle mouse clicked event. local model_matrix, projection_matrix: ARRAY [DOUBLE] model_c, projection_c: ANY viewport: GL_VECTOR_4D [INTEGER] y_new: INTEGER result_x, result_y, result_z: DOUBLE temp: ANY click_1, click_2: GL_VECTOR_3D [DOUBLE] window_z: REAL do if event.is_left_button then increase_subdivisions elseif event.is_right_button and subdivisions > 0 then decrease_subdivisions end -- glu_un_project test: ---------------------------------- -- Vorbereitung fuer beide Varianten if video_subsystem.video_surface.is_opengl_blitting_enabled then video_subsystem.video_surface.disable_opengl_blitting end create model_matrix.make (0, 15) create projection_matrix.make (0, 15) create viewport.make_xyzt (0, 0, 0, 0) model_c := model_matrix.to_c projection_c := projection_matrix.to_c gl_get_doublev_external (Em_gl_modelview_matrix, $model_c) gl_get_doublev_external (Em_gl_projection_matrix, $projection_c) gl_get_integerv_external (Em_gl_viewport, viewport.pointer) y_new := video_subsystem.video_surface.height - event.screen_y -- OpenGL renders with (0,0) on bottom, mouse reports with (0,0) on top -- 1. Variante: Erzeuge Strahl durch Maus und teste anschliessend Schnittpunkte mit Objekten -- http://www.3dkingdoms.com/selection.html#point temp := glu_un_project_external (event.x.to_double, y_new.to_double, 0, $model_c, $projection_c, viewport.pointer, $result_x, $result_y, $result_z) create click_1.make_xyz (result_x, result_y, result_z) temp := glu_un_project_external (event.x.to_double, y_new.to_double, 1, $model_c, $projection_c, viewport.pointer, $result_x, $result_y, $result_z) create click_2.make_xyz (result_x, result_y, result_z) -- io.put_string ("Click ray: "+click_1.out+" to "+click_2.out+"%N") -- Jetzt testen, was Strahl von click1 bis click2 trifft, und was am naechsten ist. -- 2. Variante: Erzeuge Raumpunkt, der richtigen "Depth"-Wert erzeugt -- http://wiki.delphigl.com/index.php/GluUnProject gl_read_pixels (event.screen_x, y_new, 1, 1, Em_gl_depth_component, Em_gl_float, $window_z) temp := glu_un_project (event.screen_x, y_new, window_z, $model_c, $projection_c, viewport.pointer, $result_x, $result_y, $result_z) create click_1.make_xyz (result_x, result_y, result_z) -- io.put_string ("Click point: "+click_1.out+"%N") -- Jetzt testen, was am naechsten bei click1 ist. end handle_mouse_dragged (event: EM_MOUSEMOTION_EVENT) is -- Handle mouse dragged event. do longitude := longitude - event.motion.x latitude := latitude + event.motion.y if latitude > 90 then latitude := 90 elseif latitude < -90 then latitude := -90 end end handle_mouse_wheel_up is -- Handle mouse wheel up event. do distance := distance - 0.1 if distance < 0 then distance := 0 end end handle_mouse_wheel_down is -- Handle mouse wheel down event. do distance := distance + 0.1 if distance > 6 then distance := 6 end end end