00001 /* -*- C++ -*- ------------------------------------------------------------ 00002 00003 Copyright (c) 2007 Jesse Anders and Demian Nave http://cmldev.net/ 00004 00005 The Configurable Math Library (CML) is distributed under the terms of the 00006 Boost Software License, v1.0 (see cml/LICENSE for details). 00007 00008 *-----------------------------------------------------------------------*/ 00013 #ifndef picking_h 00014 #define picking_h 00015 00016 #include <cml/mathlib/projection.h> 00017 00018 /* Functions for picking with rays, volumes, and drag-enclosed volumes. */ 00019 00020 namespace cml { 00021 00022 /* Support function for extracting the near and far depth range values from 00023 * a viewport matrix. 00024 */ 00025 00026 namespace detail { 00027 00028 // NOTE: Changed 'near' and 'far' to 'n' and 'f' to work around windows.h 00029 // 'near' and 'far' macros. 00030 00031 template < class MatT, typename Real > void 00032 depth_range_from_viewport_matrix(const MatT& viewport, Real& n, Real& f) 00033 { 00034 detail::CheckMatHomogeneous3D(viewport); 00035 00036 n = viewport.basis_element(3,2); 00037 f = viewport.basis_element(2,2) + n; 00038 } 00039 00040 } // namespace detail 00041 00042 /* Make a pick ray given screen coordinates and view, projection, and viewport 00043 * matrices. The origin of the ray lies in the near plane of the frustum; the 00044 * direction vector extends to the far plane if 'normalize' is false, and is 00045 * made unit-length if 'normalize' is true (its default value). 00046 * 00047 * Note that the origin of the ray lies in the near plane rather than 00048 * coinciding with the position of the virtual camera, as the latter gives 00049 * incorrect results when the projection is orthographic. 00050 * 00051 * Note also that the screen y coordinate increases from bottom to top rather 00052 * than top to bottom. If mouse coordinates are returned in window space where 00053 * the y coordinate increases from top to bottom (as is often the case), the 00054 * y value should be recomputed as 'y = <window height> - y' before being 00055 * submitted to this function. 00056 */ 00057 00058 template < class MatT_1, class MatT_2, class MatT_3, typename E, class A > 00059 void make_pick_ray( 00060 E pick_x, 00061 E pick_y, 00062 const MatT_1& view, 00063 const MatT_2& projection, 00064 const MatT_3& viewport, 00065 vector<E,A>& origin, 00066 vector<E,A>& direction, 00067 bool normalize = true) 00068 { 00069 typedef vector<E,A> vector_type; 00070 typedef typename vector_type::value_type value_type; 00071 00072 // NOTE: Changed 'near' and 'far' to 'n' and 'f' to work around 00073 // windows.h 'near' and 'far' macros. 00074 value_type n, f; 00075 detail::depth_range_from_viewport_matrix(viewport, n, f); 00076 00077 origin = 00078 unproject_point( 00079 view,projection,viewport,vector_type(pick_x,pick_y,n) 00080 ); 00081 direction = 00082 unproject_point( 00083 view,projection,viewport,vector_type(pick_x,pick_y,f) 00084 ) - origin; 00085 if (normalize) { 00086 direction.normalize(); 00087 } 00088 } 00089 00090 /* Make a pick volume given the screen coordinates of the center of the 00091 * picking rect, the width and height of the picking rect, and view and 00092 * projection matrices. 00093 * 00094 * The volume is loaded into the 'planes' array. The planes are of the form 00095 * ax+by+cz+d = 0, and are in the order left, right, bottom, top, near, far. 00096 * 00097 * The z_clip argument should be either z_clip_neg_one or z_clip_zero, and 00098 * should correspond to the near z-clipping range of the projection matrix 00099 * argument. 00100 * 00101 * The 'normalize' argument indicates whether the output planes should be 00102 * normalized; its default value is 'true'. 00103 * 00104 * Note that the screen y coordinate increases from bottom to top rather 00105 * than top to bottom. If mouse coordinates are returned in window space where 00106 * the y coordinate increases from top to bottom (as is often the case), the 00107 * y value should be recomputed as 'y = <window height> - y' before being 00108 * submitted to this function. 00109 */ 00110 00111 template < class MatT_1, class MatT_2, typename Real > 00112 void make_pick_volume( 00113 Real pick_x, 00114 Real pick_y, 00115 Real pick_width, 00116 Real pick_height, 00117 Real viewport_x, 00118 Real viewport_y, 00119 Real viewport_width, 00120 Real viewport_height, 00121 const MatT_1& view, 00122 const MatT_2& projection, 00123 Real planes[6][4], 00124 ZClip z_clip, 00125 bool normalize = true) 00126 { 00127 // FIXME: Should be promoted type... 00128 typedef matrix< 00129 Real, fixed<4,4>, 00130 typename MatT_1::basis_orient, typename MatT_1::layout > 00131 matrix_type; 00132 00133 matrix_type pick; 00134 matrix_pick( 00135 pick, pick_x, pick_y, pick_width, pick_height, 00136 viewport_x, viewport_y, viewport_width, viewport_height 00137 ); 00138 cml::extract_frustum_planes( 00139 view,detail::matrix_concat_transforms_4x4(projection,pick), 00140 planes,z_clip,normalize); 00141 } 00142 00143 /* Make a pick volume given two opposite corners of a rectangle in screen 00144 * space, and view and projection matrices. The corners of the screen rect 00145 * need not be in any particular 'order' with regard to the values of the 00146 * coordinates. 00147 * 00148 * The volume is loaded into the 'planes' array. The planes are of the form 00149 * ax+by+cz+d = 0, and are in the order left, right, bottom, top, near, far. 00150 * 00151 * The z_clip argument should be either z_clip_neg_one or z_clip_zero, and 00152 * should correspond to the near z-clipping range of the projection matrix 00153 * argument. 00154 * 00155 * The 'normalize' argument indicates whether the output planes should be 00156 * normalized; its default value is 'true'. 00157 * 00158 * Note that the screen y coordinate increases from bottom to top rather 00159 * than top to bottom. If mouse coordinates are returned in window space where 00160 * the y coordinate increases from top to bottom (as is often the case), the 00161 * y value should be recomputed as 'y = <window height> - y' before being 00162 * submitted to this function. 00163 */ 00164 00165 template < class MatT_1, class MatT_2, typename Real > 00166 void make_pick_drag_volume( 00167 Real pick_x1, 00168 Real pick_y1, 00169 Real pick_x2, 00170 Real pick_y2, 00171 Real viewport_x, 00172 Real viewport_y, 00173 Real viewport_width, 00174 Real viewport_height, 00175 const MatT_1& view, 00176 const MatT_2& projection, 00177 Real planes[6][4], 00178 ZClip z_clip, 00179 bool normalize = true) 00180 { 00181 typedef Real value_type; 00182 00183 make_pick_volume( 00184 (pick_x1+pick_x2)*value_type(.5), 00185 (pick_y1+pick_y2)*value_type(.5), 00186 std::fabs(pick_x2-pick_x1), 00187 std::fabs(pick_y2-pick_y1), 00188 viewport_x, viewport_y, viewport_width, viewport_height, 00189 view, projection, planes, z_clip, normalize 00190 ); 00191 } 00192 00193 } // namespace cml 00194 00195 #endif