indexing description: "[ A frustum is like an infinite pyramid cutoff by two planes: the near and the far plane. It can be used for various applications like representing a visible area or projection area. ]" date: "$Date$" revision: "$Revision$" class EM3D_FRUSTUM inherit EM3D_MOVABLE redefine make end EM3D_CONSTANTS export {NONE} all {ANY} em3d_ortho, em3d_perspective undefine copy, is_equal end EMGL_VIEW export {NONE} all undefine copy, is_equal end EMGLU_VIEW export {NONE} all undefine copy, is_equal end EMGL_SETTINGS export {NONE} all undefine copy, is_equal end create make feature -- Initialisation make( a_scene_manager: EM3D_SCENE_MANAGER ) is -- Init local p: EM_VECTOR3D do Precursor (a_scene_manager) projection_type := em3d_perspective fovy := 45 near_dist := 0.01 far_dist := 10000 aspect := 1.3333 update_frustum end feature -- Status report projection_type: INTEGER fovy: DOUBLE -- Specifies the field of view angle, in degrees, in the y direction. near_dist, far_dist: DOUBLE -- Specifies the distance from the viewer to the near/far clipping plane (always positive). left, right, bottom, top: DOUBLE -- Specify the coordinates for the left, right, bottom, top horizontal clipping planes. -- Used for 'em3d_ortho' projection aspect: DOUBLE -- Specifies the aspect ratio that determines the field of view in the x direction. -- The aspect ratio is the ratio of x (width) to y (height). frustum_planes: like frustum_planes_internal is -- Frustum planes specify the box surrounding the frustum do if has_changed then update_frustum end result := frustum_planes_internal end feature -- Status setting set_left (a_left: DOUBLE) is -- Set `left' to `a_left'. do left := a_left has_changed_internal := true ensure definition_of_set_left: left = a_left end set_right (a_right: DOUBLE) is -- Set `right' to `a_right'. do right := a_right has_changed_internal := true ensure definition_of_set_right: right = a_right end set_top ( a_top: DOUBLE) is -- Set `top' to `a_top'. do top := a_top has_changed_internal := true ensure definition_of_set_top: top = a_top end set_bottom (a_bottom: DOUBLE) is -- Set `bottom' to `a_bottom'. do bottom := a_bottom has_changed_internal := true ensure definition_of_set_bottom: bottom = a_bottom end set_projection_type (a_projection_type: INTEGER) is -- Set `projection' to `a_projection'. require a_projection_valid: a_projection_type = em3d_ortho or a_projection_type = em3d_perspective do projection_type := a_projection_type has_changed_internal := true ensure definition_of_set_projection: projection_type = a_projection_type end set_fovy (a_fov: DOUBLE) is -- Set `fov' to `a_fov'. require a_fov_valid: a_fov > 0 and a_fov <= 180 do fovy := a_fov has_changed_internal := true ensure definition_of_set_fov: fovy = a_fov end set_near_dist (a_near_dist: DOUBLE) is -- Set `near_dist' to `a_near_dist'. require a_near_dist_positive: a_near_dist > 0 a_near_dist_smaller_far_dist: a_near_dist < far_dist do near_dist := a_near_dist has_changed_internal := true ensure definition_of_set_near_dist: near_dist = a_near_dist end set_far_dist (a_far_dist: DOUBLE) is -- Set `far_dist' to `a_far_dist'. require near_dist_smaller_a_far_dist: a_far_dist > near_dist do far_dist := a_far_dist has_changed_internal := true ensure definition_of_set_far_dist: far_dist = a_far_dist end set_aspect (a_aspect: DOUBLE) is -- Set `aspect' to `a_aspect'. require a_aspect_valid: a_aspect > 0 do aspect := a_aspect has_changed_internal := true ensure definition_of_set_aspect: aspect = a_aspect end feature -- Miscellaneous projection_matrix: EM_MATRIX44 is -- The projection matrix of the current frustum do emgl_matrix_mode( em_gl_modelview ) emgl_push_matrix emgl_load_identity if projection_type = em3d_perspective then emglu_perspective( fovy, aspect, near_dist, far_dist ) else emgl_ortho( left, right, bottom, top, near_dist, far_dist ) end result := emgl_get_modelview_matrix emgl_pop_matrix end modelview_matrix: EM_MATRIX44 is -- The projection matrix of the current frustum local direction: EM_VECTOR3D do emgl_matrix_mode( em_gl_modelview ) emgl_push_matrix emgl_load_identity direction := orientation.axis_of_rotation emgl_rotated( orientation.angle_of_rotation_degree, direction.x, direction.y, direction.z ) emgl_translated( position.x, position.y, position.z ) result := emgl_get_modelview_matrix emgl_pop_matrix end frustum_matrix: EM_MATRIX44 is -- The projection matrix of the current frustum local direction: EM_VECTOR3D do emgl_matrix_mode( em_gl_modelview ) emgl_push_matrix emgl_load_identity if projection_type = em3d_perspective then emglu_perspective( fovy, aspect, near_dist, far_dist ) else emgl_ortho( left, right, bottom, top, near_dist, far_dist ) end direction := orientation.axis_of_rotation emgl_rotated( orientation.angle_of_rotation_degree, direction.x, direction.y, direction.z ) emgl_translated( position.x, position.y, position.z ) result := emgl_get_modelview_matrix emgl_pop_matrix end update_frustum is -- Update the frustum planes local i: INTEGER fm: EM_MATRIX44 do has_changed_internal := false old_position := position old_orientation := orientation if frustum_planes_internal=void or else (frustum_planes_internal.lower<0 and frustum_planes_internal.lower>5) then create frustum_planes_internal.make(0,5) -- from i:=frustum_planes_internal.lower until i>frustum_planes_internal.upper loop -- frustum_planes_internal[i] := [ 1,1,0,0 ] -- i := i + 1 -- end end fm := frustum_matrix frustum_planes_internal.item(em3d_frustum_plane_right ).set( fm[1,4]-fm[1,1], fm[2,4]-fm[2,1], fm[3,4]-fm[3,1], fm[4,4]-fm[4,1] ) frustum_planes_internal.item(em3d_frustum_plane_left ).set( fm[1,4]+fm[1,1], fm[2,4]+fm[2,1], fm[3,4]+fm[3,1], fm[4,4]+fm[4,1] ) frustum_planes_internal.item(em3d_frustum_plane_top ).set( fm[1,4]-fm[1,2], fm[2,4]-fm[2,2], fm[3,4]-fm[3,2], fm[4,4]-fm[4,2] ) frustum_planes_internal.item(em3d_frustum_plane_bottom).set( fm[1,4]+fm[1,2], fm[2,4]+fm[2,2], fm[3,4]+fm[3,2], fm[4,4]+fm[4,2] ) frustum_planes_internal.item(em3d_frustum_plane_far ).set( fm[1,4]-fm[1,3], fm[2,4]-fm[2,3], fm[3,4]-fm[3,3], fm[4,4]-fm[4,3] ) frustum_planes_internal.item(em3d_frustum_plane_near ).set( fm[1,4]+fm[1,3], fm[2,4]+fm[2,3], fm[3,4]+fm[3,3], fm[4,4]+fm[4,3] ) end feature {EM3D_SCENE_NODE} -- Basic operations render_opengl is -- Nothing do -- check false end end feature -- testing functions print_orientation is -- print out orientation do io.new_line io.put_string("orientation: rotation: " + orientation.angle_of_rotation_degree.out + " axis: x: " + orientation.axis_of_rotation.x.out + " y: " + orientation.axis_of_rotation.y.out + " z: " + orientation.axis_of_rotation.z.out) io.new_line end print_position is -- print out position of the camera do io.new_line io.put_string ("position: x " + position.x.out + ", y: " + position.y.out + ", z: " + position.z.out) io.new_line end print_fm is -- print out the matrix local fm: EM_MATRIX44 i, j: INTEGER do io.new_line io.put_string ("frustum matrix:") io.new_line fm := frustum_matrix from i := 1 until i > 4 loop from j := 1 until j > 4 loop io.put_string (fm[i,j].out + " ") j := j + 1 end io.new_line i := i + 1 end io.new_line end print_planes is -- print out the planes local i : INTEGER do io.new_line io.put_string ("internal_planes:") io.new_line from i := frustum_planes_internal.lower until i > frustum_planes_internal.upper loop io.put_string ("plane " + i.out + ": a: " + frustum_planes_internal.item(i).a.out + ", b: " + frustum_planes_internal.item(i).b.out + ", c: " + frustum_planes_internal.item(i).c.out + ", d: " + frustum_planes_internal.item(i).d.out) io.new_line i := i + 1 end io.new_line end feature -- inside Tests insideTest_Point(p: EM_VECTOR3D):INTEGER is -- calculate if p is outside the frustum or not require p /= void local i:INTEGER do create Result Result := em3d_frustum_inside from i := frustum_planes_internal.lower until i > frustum_planes_internal.upper loop if (frustum_planes_internal[i].distance_to_point(p) < 0) then Result := em3d_frustum_outside end i := i + 1 end end insideTest_Sphere(m: EM_VECTOR3D; r: DOUBLE):INTEGER is -- calculate if the sphere with m and radius r is outside frustum or inside or intersects require m /= void r /= void local distance: DOUBLE stop: BOOLEAN i: INTEGER do create Result Result := em3d_frustum_inside stop := false from i := frustum_planes_internal.lower until i > frustum_planes_internal.upper or stop loop distance := frustum_planes_internal[i].distance_to_point(m) --io.put_string ("distance to " + i.out + " " + distance.out) --io.new_line if (distance < -r) then Result := em3d_frustum_outside stop := true elseif (distance < r) then Result := em3d_frustum_intersection stop := true end i := i + 1 end end -- FRUSTUM_INSIDE: INTEGER is 1 -- FRUSTUM_INTERSECTION: INTEGER is 2 -- FRUSTUM_OUTSIDE: INTEGER is 0 feature {EM3D_SCENE_NODE} -- Access position : EM_VECTOR3D is -- The position of the frustum do result := world_position end orientation : EM_QUATERNION is -- The orientation of the frustum do result := world_orientation end feature {NONE} --Access has_changed_internal: BOOLEAN frustum_planes_internal: ARRAY[ EM_PLANE ] old_position: like position old_orientation: like orientation has_changed: BOOLEAN is -- Type your comment here do result := has_changed_internal or (old_position /= position) or (old_orientation /= orientation) end invariant fovy_valid: fovy > 0 and fovy <= 180 near_dist_smaller_far_dist: near_dist < far_dist near_dist_positive: near_dist > 0 projection_valid: projection_type = em3d_ortho or projection_type = em3d_perspective aspect_valid: aspect > 0 end