1 /* 2 ----------------------------------------------------------------------------- 3 This source file is part of OGRE 4 (Object-oriented Graphics Rendering Engine) 5 For the latest info, see http://www.ogre3d.org/ 6 7 Copyright (c) 2000-2014 Torus Knot Software Ltd 8 9 Permission is hereby granted, free of charge, to any person obtaining a copy 10 of this software and associated documentation files (the "Software"), to deal 11 in the Software without restriction, including without limitation the rights 12 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 copies of the Software, and to permit persons to whom the Software is 14 furnished to do so, subject to the following conditions: 15 16 The above copyright notice and this permission notice shall be included in 17 all copies or substantial portions of the Software. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 THE SOFTWARE. 26 ----------------------------------------------------------------------------- 27 */ 28 29 #include "OgreStableHeaders.h" 30 #include "OgreLight.h" 31 #include "OgreShadowCameraSetupPlaneOptimal.h" 32 #include "OgreNumerics.h" 33 #include "OgreMovablePlane.h" 34 35 #if OGRE_COMPILER == OGRE_COMPILER_MSVC 36 // we do a lot of PreciseReal -> Real in here, casting is messy 37 // disable: "conversion from 'double' to 'float', possible loss of data 38 # pragma warning (disable : 4244) 39 # pragma warning (disable : 4305) 40 #endif 41 42 43 namespace Ogre 44 { 45 // -------------------------------------------------------------------- computeConstrainedProjection(const Vector4 & pinhole,const std::vector<Vector4> & fpoint,const std::vector<Vector2> & constraint) const46 Matrix4 PlaneOptimalShadowCameraSetup::computeConstrainedProjection( 47 const Vector4& pinhole, 48 const std::vector<Vector4>& fpoint, 49 const std::vector<Vector2>& constraint) const 50 { 51 // NOTE: will assume the z coordinates should be decided such that 52 // the first 3 points (in fpoint) will have post projective 53 // z coordinates of about +1 and the 4th (in fpoint) will have a 54 // post projective z coordinate of about -1. 55 56 // TODO: could use SVD to avoid arbitrarily choosing one 57 // matrix element to be 1.0 (and thereby fix the scale). 58 59 Matrix4 ret; 60 int i; 61 bool incrPrecision = false; // use to control numerical solving 62 63 if(fpoint.size() < 4 || constraint.size() < 4) { 64 return Matrix4::IDENTITY; 65 } 66 67 // allocate memory 68 double **mat = NULL; 69 double **backmat = NULL; 70 { 71 mat = OGRE_ALLOC_T(double*, 11, MEMCATEGORY_SCENE_CONTROL); 72 if(incrPrecision) 73 backmat = OGRE_ALLOC_T(double*, 11, MEMCATEGORY_SCENE_CONTROL); 74 for(i=0; i<11; i++) 75 { 76 mat[i] = OGRE_ALLOC_T(double, 11, MEMCATEGORY_SCENE_CONTROL); 77 if(incrPrecision) 78 backmat[i] = OGRE_ALLOC_T(double, 11, MEMCATEGORY_SCENE_CONTROL); 79 } 80 } 81 82 // set up linear system to solve for all rows of projective matrix 83 // except for the 3rd which corresponds to mapping of z values 84 85 // we choose a nonzero element of the last row to set to the arbitrary 86 // constant 1.0. 87 int nzind = 3; 88 double col[11]; 89 double backcol[11]; 90 91 // fill in light position constraints 92 mat[0][0] = pinhole.x; 93 mat[0][1] = pinhole.y; 94 mat[0][2] = pinhole.z; 95 mat[0][3] = pinhole.w; 96 for(i=4; i<11; i++) 97 mat[0][i] = 0.0; 98 col[0] = 0.0; 99 100 for(i=0; i<11; i++) 101 mat[1][i] = 0.0; 102 mat[1][4] = pinhole.x; 103 mat[1][5] = pinhole.y; 104 mat[1][6] = pinhole.z; 105 mat[1][7] = pinhole.w; 106 col[1] = 0.0; 107 108 double larr[4]; 109 larr[0] = pinhole.x; 110 larr[1] = pinhole.y; 111 larr[2] = pinhole.z; 112 larr[3] = pinhole.w; 113 for(i=0; i<8; i++) 114 mat[2][i] = 0.0; 115 int ind = 8; 116 for(i=0; i<4; i++) 117 { 118 if(nzind == i) 119 continue; 120 mat[2][ind++] = larr[i]; 121 } 122 col[2] = -larr[nzind]; 123 124 // fill in all the other constraints 125 int row=3; 126 for(i=0; i<4; i++) 127 { 128 int j; 129 larr[0] = fpoint[i].x; 130 larr[1] = fpoint[i].y; 131 larr[2] = fpoint[i].z; 132 larr[3] = fpoint[i].w; 133 134 // lexel s coordinate constraint 135 for(j=0; j<4; j++) 136 mat[row][j] = larr[j]; 137 for(j=4; j<8; j++) 138 mat[row][j] = 0.0; 139 ind=8; 140 for(j=0; j<4; j++) 141 { 142 if(nzind==j) 143 continue; 144 mat[row][ind++] = larr[j] * (-constraint[i].x); 145 } 146 col[row] = larr[nzind] * constraint[i].x; 147 ++row; 148 149 // lexel t coordinate constraint 150 for(j=0; j<4; j++) 151 mat[row][j] = 0.0; 152 for(j=4; j<8; j++) 153 mat[row][j] = larr[j-4]; 154 155 ind=8; 156 for(j=0; j<4; j++) 157 { 158 if(nzind==j) 159 continue; 160 mat[row][ind++] = larr[j] * (-constraint[i].y); 161 } 162 col[row] = larr[nzind] * constraint[i].y; 163 ++row; 164 } 165 166 // copy the matrix and vector for later computation 167 if(incrPrecision) 168 { 169 for (i=0; i<11; i++) 170 { 171 for(int j=0; j<11; j++) 172 backmat[i][j] = mat[i][j]; 173 backcol[i] = col[i]; 174 } 175 } 176 177 // solve for the matrix elements 178 if(!NumericSolver::solveNxNLinearSysDestr(11, mat, col)) 179 { 180 // error solving for projective matrix (rows 1,2,4) 181 } 182 183 // get a little more precision 184 if(incrPrecision) 185 { 186 for (int k=0; k<3; k++) 187 { 188 double nvec[11]; 189 for(i=0; i<11; i++) 190 { 191 int j; 192 nvec[i] = backmat[i][0] * col[0]; 193 mat[i][0] = backmat[i][0]; 194 for(j=1; j<11; j++) 195 { 196 nvec[i] += backmat[i][j] * col[j]; 197 mat[i][j] = backmat[i][j]; 198 } 199 nvec[i] -= backcol[i]; 200 } 201 if(!NumericSolver::solveNxNLinearSysDestr(11, mat, nvec)) 202 { 203 // error solving for increased precision rows 1,2,4 204 } 205 for(i=0; i<11; i++) 206 col[i] -= nvec[i]; 207 } 208 } 209 210 double row4[4]; 211 ind = 8; 212 for(i=0; i<4; i++) 213 { 214 if (i == nzind) 215 row4[i] = 1.0; 216 else 217 row4[i] = col[ind++]; 218 } 219 220 221 // now solve for the 3rd row which affects depth precision 222 double zrow[4]; 223 224 // we want the affine skew such that isoplanes of constant depth are parallel to 225 // the world plane of interest 226 // NOTE: recall we perturbed the last fpoint off the plane, so we'll again modify 227 // this one since we want 3 points on the plane = far plane, and 1 on the near plane 228 int nearind = 3; 229 for(i=0; i<3; i++) 230 { 231 mat[i][0] = fpoint[i].x; 232 mat[i][1] = fpoint[i].y; 233 mat[i][2] = fpoint[i].z; 234 mat[i][3] = 1.0; 235 zrow[i] = (row4[0] * fpoint[i].x + 236 row4[1] * fpoint[i].y + 237 row4[2] * fpoint[i].z + 238 row4[3]) * 0.99 ; 239 } 240 mat[3][0] = fpoint[nearind].x; 241 mat[3][1] = fpoint[nearind].y; 242 mat[3][2] = fpoint[nearind].z; 243 mat[3][3] = 1.0; 244 zrow[3] = -row4[0] * fpoint[nearind].x - 245 row4[1] * fpoint[nearind].y - 246 row4[2] * fpoint[nearind].z - 247 row4[3] ; 248 249 // solve for the z row of the matrix 250 if(!NumericSolver::solveNxNLinearSysDestr(4, mat, zrow)) 251 { 252 // error solving for projective matrix (row 3) 253 } 254 255 // set projective texture matrix 256 ret = Matrix4( col[0], col[1], col[2], col[3], 257 col[4], col[5], col[6], col[7], 258 zrow[0], zrow[1], zrow[2], zrow[3], 259 row4[0], row4[1], row4[2], row4[3] ); 260 261 262 // check for clip 263 Vector4 testCoord = ret * fpoint[0]; 264 if(testCoord.w < 0.0) 265 ret = ret * (-1.0); 266 267 // free memory 268 for (i=0; i<11; i++) 269 { 270 if (mat[i]) 271 OGRE_FREE(mat[i], MEMCATEGORY_SCENE_CONTROL); 272 if (incrPrecision) 273 OGRE_FREE(backmat[i], MEMCATEGORY_SCENE_CONTROL); 274 } 275 OGRE_FREE(mat, MEMCATEGORY_SCENE_CONTROL); 276 if(incrPrecision) 277 OGRE_FREE(backmat, MEMCATEGORY_SCENE_CONTROL); 278 279 return ret; 280 281 } 282 283 // -------------------------------------------------------------------- 284 285 /// Construct object to consider a specified plane of interest PlaneOptimalShadowCameraSetup(const MovablePlane * plane)286 PlaneOptimalShadowCameraSetup::PlaneOptimalShadowCameraSetup(const MovablePlane* plane) 287 { 288 mPlane = plane; 289 } 290 291 /// Destructor ~PlaneOptimalShadowCameraSetup()292 PlaneOptimalShadowCameraSetup::~PlaneOptimalShadowCameraSetup() {} 293 294 /// Implements the plane optimal shadow camera setup algorithm getShadowCamera(const SceneManager * sm,const Camera * cam,const Viewport * vp,const Light * light,Camera * texCam,size_t iteration) const295 void PlaneOptimalShadowCameraSetup::getShadowCamera (const SceneManager *sm, const Camera *cam, 296 const Viewport *vp, const Light *light, Camera *texCam, size_t iteration) const 297 { 298 // get the plane transformed by the parent node(s) 299 // Also, make sure the plane is normalized 300 Plane worldPlane = mPlane->_getDerivedPlane(); 301 worldPlane.normalise(); 302 303 // get camera's projection matrix 304 Matrix4 camProjection = cam->getProjectionMatrix() * cam->getViewMatrix(); 305 306 // get the world points to constrain 307 std::vector<Vector4> vhull; 308 cam->forwardIntersect(worldPlane, &vhull); 309 if (vhull.size() < 4) 310 return; 311 312 // make sure the last point is a finite point (not point at infinity) 313 if (vhull[3].w == 0.0) 314 { 315 int finiteIndex = -1; 316 for (uint loopIndex = 0; loopIndex < vhull.size(); loopIndex++) 317 { 318 if (vhull[loopIndex].w != 0.0) 319 { 320 finiteIndex = loopIndex; 321 break; 322 } 323 } 324 if (finiteIndex == -1) 325 { 326 // there are no finite points, which means camera doesn't see plane of interest. 327 // so we don't care what the shadow map matrix is 328 // We'll map points off the shadow map so they aren't even stored 329 Matrix4 crazyMat(0.0, 0.0, 0.0, 5.0, 330 0.0, 0.0, 0.0, 5.0, 331 0.0, 0.0, 0.0, 5.0, 332 0.0, 0.0, 0.0, 1.0); 333 texCam->setCustomViewMatrix(true, Affine3::IDENTITY); 334 texCam->setCustomProjectionMatrix(true, crazyMat); 335 return; 336 } 337 // swap finite point to last point 338 std::swap(vhull[3], vhull[finiteIndex]); 339 } 340 vhull.resize(4); 341 342 // get the post-projective coordinate constraints 343 std::vector<Vector2> constraint; 344 for (int i=0; i<4; i++) 345 { 346 Vector4 postProjPt = camProjection * vhull[i]; 347 postProjPt *= 1.0 / postProjPt.w; 348 constraint.push_back(Vector2(postProjPt.x, postProjPt.y)); 349 } 350 351 // perturb one point so we don't have coplanarity 352 const Vector4& pinhole = light->getAs4DVector(); 353 const Vector4& oldPt = vhull.back(); 354 Vector4 newPt; 355 if (pinhole.w == 0) 356 { 357 // It's directional light 358 static const Real NEAR_SCALE = 100.0; 359 newPt = oldPt + (pinhole * (cam->getNearClipDistance() * NEAR_SCALE)); 360 } 361 else 362 { 363 // It's point or spotlight 364 Vector4 displacement = oldPt - pinhole; 365 Vector3 displace3 = Vector3(displacement.x, displacement.y, displacement.z); 366 Real dotProd = std::fabs(displace3.dotProduct(worldPlane.normal)); 367 static const Real NEAR_FACTOR = 0.05; 368 newPt = pinhole + (displacement * (cam->getNearClipDistance() * NEAR_FACTOR / dotProd)); 369 } 370 vhull.back() = newPt; 371 372 // solve for the matrix that stabilizes the plane 373 Matrix4 customMatrix = computeConstrainedProjection(pinhole, vhull, constraint); 374 375 if (pinhole.w == 0) 376 { 377 // TODO: factor into view and projection pieces. 378 // Note: In fact, it's unnecessary to factor into view and projection pieces, 379 // but if we do, we will more according with academic requirement :) 380 texCam->setCustomViewMatrix(true, Affine3::IDENTITY); 381 texCam->setCustomProjectionMatrix(true, customMatrix); 382 return; 383 } 384 385 Vector3 tempPos = Vector3(pinhole.x, pinhole.y, pinhole.z); 386 387 // factor into view and projection pieces 388 Affine3 translation(1.0, 0.0, 0.0, tempPos.x, 389 0.0, 1.0, 0.0, tempPos.y, 390 0.0, 0.0, 1.0, tempPos.z); 391 Affine3 invTranslation(1.0, 0.0, 0.0, -tempPos.x, 392 0.0, 1.0, 0.0, -tempPos.y, 393 0.0, 0.0, 1.0, -tempPos.z); 394 Matrix4 tempMatrix = customMatrix * translation; 395 Vector3 zRow(-tempMatrix[3][0], -tempMatrix[3][1], -tempMatrix[3][2]); 396 zRow.normalise(); 397 Vector3 up; 398 if (zRow.y == 1.0) 399 up = Vector3(1,0,0); 400 else 401 up = Vector3(0,1,0); 402 Vector3 xDir = up.crossProduct(zRow); 403 xDir.normalise(); 404 up = zRow.crossProduct(xDir); 405 Affine3 rotation(xDir.x, up.x, zRow.x, 0.0, 406 xDir.y, up.y, zRow.y, 0.0, 407 xDir.z, up.z, zRow.z, 0.0); 408 Matrix4 customProj = tempMatrix * rotation; 409 Affine3 customView(rotation.transpose() * invTranslation); 410 // note: now customProj * (0,0,0,1)^t = (0, 0, k, 0)^t for k some constant 411 // note: also customProj's 4th row is (0, 0, c, 0) for some negative c. 412 413 414 // set the shadow map camera 415 texCam->setCustomViewMatrix(true, customView); 416 texCam->setCustomProjectionMatrix(true, customProj); 417 } 418 419 } 420 421