PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/OgreMain/src/OgreShadowCameraSetupPlaneOptimal.cpp

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