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