1 module des.space.transform; 2 3 public import des.math.linear.vector; 4 public import des.math.linear.matrix; 5 6 import des.util.logsys; 7 8 import std.math; 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 float nflim = 1e-5; 114 115 mat4 self_mtr; 116 117 /// 118 abstract void recalc(); 119 120 invariant() 121 { 122 assert( _ratio > 0 ); 123 assert( _near > 0 && _near < _far ); 124 assert( _far > 0 ); 125 assert( nflim > 0 ); 126 assert( !!self_mtr ); 127 } 128 129 public: 130 131 @property 132 { 133 /// 134 float ratio() const { return _ratio; } 135 /// 136 float ratio( float v ) 137 in { assert( v !is float.nan ); } body 138 { 139 enum lim = 1000000.0f; 140 if( v <= 0 ) 141 { 142 v = 1 / lim; 143 logger.warn( "value <= 0, set to: ", 1 / lim ); 144 } 145 if( v > lim ) 146 { 147 v = lim; 148 logger.warn( "value > %s, set to: %s", lim, lim ); 149 } 150 _ratio = v; 151 recalc(); 152 return v; 153 } 154 155 /// 156 float near() const { return _near; } 157 /// 158 float near( float v ) 159 in { assert( v !is float.nan ); } body 160 { 161 if( v > _far ) 162 { 163 _far = v + nflim; 164 logger.warn( "value > far, far set to 'value + nflim': ", v + nflim ); 165 } 166 if( v < 0 ) 167 { 168 v = 0; 169 logger.warn( "value < 0, set to: 0" ); 170 } 171 _near = v; 172 recalc(); 173 return v; 174 } 175 176 /// 177 float far() const { return _far; } 178 /// 179 float far( float v ) 180 in { assert( v !is float.nan ); } body 181 { 182 if( v < nflim * 2 ) 183 { 184 v = nflim * 2; 185 logger.warn( "value < nflim * 2, set to: ", nflim * 2 ); 186 } 187 if( v < _near ) 188 { 189 _near = v - nflim; 190 logger.warn( "value < near, near set to 'value - nflim': ", v - nflim ); 191 } 192 _far = v; 193 recalc(); 194 return v; 195 } 196 197 /// 198 mat4 matrix() const { return self_mtr; } 199 } 200 } 201 202 /// 203 class PerspectiveTransform : ViewTransform 204 { 205 protected: 206 float _fov = 70; 207 208 override void recalc() { self_mtr = calcPerspective( _fov, _ratio, _near, _far ); } 209 210 invariant() { assert( _fov > 0 ); } 211 212 public: 213 214 @property 215 { 216 /// 217 float fov() const { return _fov; } 218 /// 219 float fov( float v ) 220 in { assert( v !is float.nan ); } body 221 { 222 enum minfov = 1e-5; 223 enum maxfov = 180 - minfov; 224 if( v < minfov ) 225 { 226 v = minfov; 227 logger.warn( "value < minfov, set to minfov: ", minfov ); 228 } 229 if( v > maxfov ) 230 { 231 v = maxfov; 232 logger.warn( "value > maxfov, set to maxfov: ", maxfov ); 233 } 234 _fov = v; 235 recalc(); 236 return v; 237 } 238 } 239 } 240 241 /// 242 class OrthoTransform : ViewTransform 243 { 244 protected: 245 246 float _scale = 1; 247 248 invariant() 249 { 250 assert( _scale > 0 ); 251 } 252 253 override void recalc() 254 { 255 auto s = 1.0 / _scale; 256 auto r = s * _ratio; 257 auto z = -2.0f / ( _far - _near ); 258 auto o = -( _far + _near ) / ( _far - _near ); 259 260 self_mtr = mat4( s, 0, 0, 0, 261 0, r, 0, 0, 262 0, 0, z, o, 263 0, 0, 0, 1 ); 264 } 265 266 public: 267 268 @property 269 { 270 /// 271 float scale() const { return _scale; } 272 /// 273 float scale( float v ) 274 in { assert( v !is float.nan ); } body 275 { 276 if( v < 1e-8 ) 277 { 278 v = 1e-8; 279 logger.warn( "value < 1e-8, set to 1e-8" ); 280 } 281 _scale = v; 282 recalc(); 283 return v; 284 } 285 } 286 } 287 288 private: 289 290 mat4 calcLookAt( in vec3 pos, in vec3 trg, in vec3 up ) 291 { 292 auto z = (pos-trg).e; 293 auto x = cross(up,z).e; 294 vec3 y; 295 if( x ) y = cross(z,x).e; 296 else 297 { 298 y = cross(z,vec3(1,0,0)).e; 299 x = cross(y,z).e; 300 } 301 return mat4( x.x, y.x, z.x, pos.x, 302 x.y, y.y, z.y, pos.y, 303 x.z, y.z, z.z, pos.z, 304 0, 0, 0, 1 ); 305 } 306 307 mat4 calcPerspective( float fov_degree, float ratio, float znear, float zfar ) 308 { 309 /+ fov conv to radians and div 2 +/ 310 float h = 1.0 / tan( fov_degree * PI / 360.0 ); 311 float w = h / ratio; 312 313 float depth = znear - zfar; 314 float q = ( znear + zfar ) / depth; 315 float n = ( 2.0f * znear * zfar ) / depth; 316 317 return mat4( w, 0, 0, 0, 318 0, h, 0, 0, 319 0, 0, q, n, 320 0, 0, -1, 0 ); 321 } 322 323 mat4 calcOrtho( float w, float h, float znear, float zfar ) 324 { 325 float x = znear - zfar; 326 return mat4( 2/w, 0, 0, 0, 327 0, 2/h, 0, 0, 328 0, 0, -1/x, 0, 329 0, 0, znear/x, 1 ); 330 }