1 module des.space.transform; 2 3 public import des.math.linear.vector; 4 public import des.math.linear.matrix; 5 6 import std.math; 7 import std.exception; 8 import std..string : format; 9 10 /// 11 interface Transform 12 { 13 /// 14 mat4 matrix() @property const; 15 16 /// 17 protected final static mat4 getMatrix( const(Transform) tr ) 18 { 19 if( tr !is null ) 20 return tr.matrix; 21 return mat4.diag(1); 22 } 23 } 24 25 /// 26 class SimpleTransform : Transform 27 { 28 protected: 29 mat4 mtr; /// 30 31 public: 32 @property 33 { 34 /// 35 mat4 matrix() const { return mtr; } 36 /// 37 void matrix( in mat4 m ) { mtr = m; } 38 } 39 } 40 41 /// 42 class TransformList : Transform 43 { 44 Transform[] list; /// 45 enum Order { DIRECT, REVERSE } 46 Order order = Order.DIRECT; /// 47 48 /// 49 @property mat4 matrix() const 50 { 51 mat4 buf; 52 if( order == Order.DIRECT ) 53 foreach( tr; list ) 54 buf *= tr.matrix; 55 else 56 foreach_reverse( tr; list ) 57 buf *= tr.matrix; 58 return buf; 59 } 60 } 61 62 /// 63 class CachedTransform : Transform 64 { 65 protected: 66 mat4 mtr; /// 67 Transform transform_source; /// 68 69 public: 70 71 /// 72 this( Transform ntr ) { setTransform( ntr ); } 73 74 /// 75 void setTransform( Transform ntr ) 76 { 77 transform_source = ntr; 78 recalc(); 79 } 80 81 /// 82 void recalc() 83 { 84 if( transform_source !is null ) 85 mtr = transform_source.matrix; 86 else mtr = mat4.diag(1); 87 } 88 89 /// 90 @property mat4 matrix() const { return mtr; } 91 } 92 93 /// 94 class LookAtTransform : Transform 95 { 96 /// 97 vec3 pos=vec3(0), target=vec3(0), up=vec3(0,0,1); 98 99 /// 100 @property mat4 matrix() const 101 { return calcLookAt( pos, target, up ); } 102 } 103 104 /// 105 class ViewTransform : Transform 106 { 107 protected: 108 109 float _ratio = 4.0f / 3.0f; 110 float _near = 1e-1; 111 float _far = 1e5; 112 113 mat4 self_mtr; 114 115 /// 116 abstract void recalc(); 117 118 invariant() 119 { 120 assert( _ratio > 0 ); 121 assert( 0 < _near && _near < _far ); 122 assert( !!self_mtr ); 123 } 124 125 public: 126 127 enum MAX_RATIO = 65536; 128 129 @property 130 { 131 /// 132 float ratio() const { return _ratio; } 133 /// 134 float ratio( float v ) 135 { 136 checkLimit( 1.0f / MAX_RATIO, v, MAX_RATIO, "min ratio", "ratio value", "max ratio" ); 137 _ratio = v; 138 recalc(); 139 return v; 140 } 141 142 /// 143 float near() const { return _near; } 144 /// 145 float near( float v ) 146 { 147 checkLimit( 0, v, _far, "zero", "near value", "far value" ); 148 _near = v; 149 recalc(); 150 return v; 151 } 152 153 /// 154 float far() const { return _far; } 155 /// 156 float far( float v ) 157 { 158 checkLimit( _near, v, float.max, "near value", "far value", "float.max" ); 159 enforce( v > _near ); 160 _far = v; 161 recalc(); 162 return v; 163 } 164 165 /// 166 mat4 matrix() const { return self_mtr; } 167 } 168 169 protected: 170 171 final void checkLimit(string file=__FILE__,size_t line=__LINE__) 172 ( float min_value, float value, float max_value, 173 string min_name, string name, string max_name ) const 174 { 175 enforce( min_value < value, new Exception( format( "%s (%s) less that %s (%s)", 176 name, value, min_name, min_value ), 177 file, line ) ); 178 179 enforce( value < max_value, new Exception( format( "%s (%s) more that %s (%s)", 180 name, value, max_name, max_value ), 181 file, line ) ); 182 } 183 } 184 185 /// 186 class PerspectiveTransform : ViewTransform 187 { 188 protected: 189 float _fov = 70; 190 191 override void recalc() { self_mtr = calcPerspective( _fov, _ratio, _near, _far ); } 192 193 invariant() { assert( _fov > 0 ); } 194 195 public: 196 197 enum MIN_FOV = 1e-5; 198 enum MAX_FOV = 180 - MIN_FOV; 199 200 @property 201 { 202 /// 203 float fov() const { return _fov; } 204 /// 205 float fov( float v ) 206 { 207 checkLimit( MIN_FOV, v, MAX_FOV, "min fov", "fov value", "max fov" ); 208 _fov = v; 209 recalc(); 210 return v; 211 } 212 } 213 } 214 215 /// 216 class OrthoTransform : ViewTransform 217 { 218 protected: 219 220 float _scale = 1; 221 222 invariant() { assert( _scale > 0 ); } 223 224 override void recalc() 225 { 226 auto s = 1.0 / _scale; 227 auto r = s * _ratio; 228 auto z = -2.0f / ( _far - _near ); 229 auto o = -( _far + _near ) / ( _far - _near ); 230 231 self_mtr = mat4( s, 0, 0, 0, 232 0, r, 0, 0, 233 0, 0, z, o, 234 0, 0, 0, 1 ); 235 } 236 237 public: 238 239 @property 240 { 241 /// 242 float scale() const { return _scale; } 243 /// 244 float scale( float v ) 245 { 246 checkLimit( 0, v, float.max, "zero", "scale value", "float.max" ); 247 _scale = v; 248 recalc(); 249 return v; 250 } 251 } 252 } 253 254 private: 255 256 mat4 calcLookAt( in vec3 pos, in vec3 trg, in vec3 up ) 257 in { 258 assert( !!pos ); 259 assert( !!trg ); 260 assert( !!up ); 261 } 262 out(mtr) { assert( !!mtr ); } 263 body { 264 auto z = (pos-trg).e; 265 auto x = cross(up,z).e; 266 vec3 y; 267 if( x ) y = cross(z,x).e; 268 else 269 { 270 y = cross(z,vec3(1,0,0)).e; 271 x = cross(y,z).e; 272 } 273 return mat4( x.x, y.x, z.x, pos.x, 274 x.y, y.y, z.y, pos.y, 275 x.z, y.z, z.z, pos.z, 276 0, 0, 0, 1 ); 277 } 278 279 mat4 calcPerspective( float fov_degree, float ratio, float znear, float zfar ) 280 in { 281 assert( fov_degree > 0 ); 282 assert( ratio > 0 ); 283 assert( znear !is float.nan ); 284 assert( zfar !is float.nan ); 285 } 286 out(mtr) { assert( !!mtr ); } 287 body { 288 /+ fov conv to radians and div 2 +/ 289 float h = 1.0 / tan( fov_degree * PI / 360.0 ); 290 float w = h / ratio; 291 292 float depth = znear - zfar; 293 float q = ( znear + zfar ) / depth; 294 float n = ( 2.0f * znear * zfar ) / depth; 295 296 return mat4( w, 0, 0, 0, 297 0, h, 0, 0, 298 0, 0, q, n, 299 0, 0, -1, 0 ); 300 } 301 302 mat4 calcOrtho( float w, float h, float znear, float zfar ) 303 in { 304 assert( w > 0 ); 305 assert( h > 0 ); 306 assert( znear !is float.nan ); 307 assert( zfar !is float.nan ); 308 } 309 out(mtr) { assert( !!mtr ); } 310 body { 311 float x = znear - zfar; 312 return mat4( 2/w, 0, 0, 0, 313 0, 2/h, 0, 0, 314 0, 0, -1/x, 0, 315 0, 0, znear/x, 1 ); 316 }