2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
26 ===========================================================================
28 #include "../idlib/precompiled.h"
35 // Compute conservative shadow bounds as the intersection
36 // of the object's bounds' shadow volume and the light's bounds.
41 template <class T, int N>
46 MyArray( const MyArray<T,N> & cpy ) : s(cpy.s)
48 for(int i=0; i < s; i++)
52 void push_back(const T & i) {
59 T & operator[](int i) {
63 const T & operator[](int i) const {
67 unsigned int size() const {
77 // static int max_size;
80 typedef MyArray<int, 4> MyArrayInt;
81 //int MyArrayInt::max_size = 0;
82 typedef MyArray<idVec4, 16> MyArrayVec4;
83 //int MyArrayVec4::max_size = 0;
92 typedef MyArray<poly, 9> MyArrayPoly;
93 //int MyArrayPoly::max_size = 0;
101 typedef MyArray<edge, 15> MyArrayEdge;
102 //int MyArrayEdge::max_size = 0;
104 MyArrayInt four_ints(int a, int b, int c, int d)
114 idVec3 homogeneous_difference(idVec4 a, idVec4 b)
117 v.x = b.x * a.w - a.x * b.w;
118 v.y = b.y * a.w - a.y * b.w;
119 v.z = b.z * a.w - a.z * b.w;
123 // handles positive w only
124 idVec4 compute_homogeneous_plane(idVec4 a, idVec4 b, idVec4 c)
129 { t = a; a = b; b = c; c = t; }
131 { t = a; a = b; b = c; c = t; }
133 // can't handle 3 infinite points
137 idVec3 vb = homogeneous_difference(a, b);
138 idVec3 vc = homogeneous_difference(a, c);
140 idVec3 n = vb.Cross(vc);
147 v.w = - (n * idVec3(a.x, a.y, a.z)) / a.w ;
158 void add_quad( int va, int vb, int vc, int vd )
161 pg.vi = four_ints(va, vb, vc, vd);
162 pg.ni = four_ints(-1, -1, -1, -1);
163 pg.plane = compute_homogeneous_plane(v[va], v[vb], v[vc]);
167 void discard_neighbor_info()
169 for(unsigned int i = 0; i < p.size(); i++ )
171 MyArrayInt & ni = p[i].ni;
172 for(unsigned int j = 0; j < ni.size(); j++)
177 void compute_neighbors()
181 discard_neighbor_info();
186 for(int i = 0; i < P-1; i++ )
188 const MyArrayInt & vi = p[i].vi;
189 MyArrayInt & ni = p[i].ni;
192 // for each edge of that polygon
193 for(int ii=0; ii < Si; ii++)
196 int ii1 = (ii+1) % Si;
198 // continue if we've already found this neighbor
202 // check all remaining polygons
203 for(int j = i+1; j < P; j++ )
205 const MyArrayInt & vj = p[j].vi;
206 MyArrayInt & nj = p[j].ni;
209 for( int jj = 0; jj < Sj; jj++ )
212 int jj1 = (jj+1) % Sj;
213 if(vi[ii0] == vj[jj1] && vi[ii1] == vj[jj0])
226 else if ( vi[ii0] == vj[jj0] && vi[ii1] == vj[jj1] )
228 fprintf(stderr,"why am I here?\n");
238 void recompute_planes()
241 for(unsigned int i = 0; i < p.size(); i++ )
243 p[i].plane = compute_homogeneous_plane(v[p[i].vi[0]], v[p[i].vi[1]], v[p[i].vi[2]]);
247 void transform(const idMat4 & m)
249 for(unsigned int i=0; i < v.size(); i++ )
257 polyhedron PolyhedronFromBounds( const idBounds & b )
273 if( p.e.size() == 0 ) {
275 p.v.push_back(idVec4( -1, -1, 1, 1));
276 p.v.push_back(idVec4( 1, -1, 1, 1));
277 p.v.push_back(idVec4( 1, 1, 1, 1));
278 p.v.push_back(idVec4( -1, 1, 1, 1));
279 p.v.push_back(idVec4( -1, -1, -1, 1));
280 p.v.push_back(idVec4( 1, -1, -1, 1));
281 p.v.push_back(idVec4( 1, 1, -1, 1));
282 p.v.push_back(idVec4( -1, 1, -1, 1));
284 p.add_quad( 0, 1, 2, 3 );
285 p.add_quad( 7, 6, 5, 4 );
286 p.add_quad( 1, 0, 4, 5 );
287 p.add_quad( 2, 1, 5, 6 );
288 p.add_quad( 3, 2, 6, 7 );
289 p.add_quad( 0, 3, 7, 4 );
291 p.compute_neighbors();
292 p.recompute_planes();
293 p.v.empty(); // no need to copy this data since it'll be replaced
298 const idVec3 & min = b[0];
299 const idVec3 & max = b[1];
302 p2.v.push_back(idVec4( min.x, min.y, max.z, 1));
303 p2.v.push_back(idVec4( max.x, min.y, max.z, 1));
304 p2.v.push_back(idVec4( max.x, max.y, max.z, 1));
305 p2.v.push_back(idVec4( min.x, max.y, max.z, 1));
306 p2.v.push_back(idVec4( min.x, min.y, min.z, 1));
307 p2.v.push_back(idVec4( max.x, min.y, min.z, 1));
308 p2.v.push_back(idVec4( max.x, max.y, min.z, 1));
309 p2.v.push_back(idVec4( min.x, max.y, min.z, 1));
311 p2.recompute_planes();
316 polyhedron make_sv(const polyhedron & oc, idVec4 light)
318 static polyhedron lut[64];
321 for(unsigned int i = 0; i < 6; i++) {
322 if( ( oc.p[i].plane * light ) > 0 )
326 if( lut[index].e.size() == 0 )
328 polyhedron & ph = lut[index];
332 for( int j = 0; j < V; j++ )
334 idVec3 proj = homogeneous_difference( light, ph.v[j] );
335 ph.v.push_back( idVec4(proj.x, proj.y, proj.z, 0) );
340 for(unsigned int i=0; i < oc.p.size(); i++)
342 if( (oc.p[i].plane * light) > 0)
344 ph.p.push_back(oc.p[i]);
349 return ph = polyhedron();
351 ph.compute_neighbors();
356 for(int i=0; i < I; i++)
358 MyArrayInt & vi = ph.p[i].vi;
359 MyArrayInt & ni = ph.p[i].ni;
362 for(int j = 0; j < S; j++)
369 pg.vi = four_ints( a, b, b+V, a+V);
370 pg.ni = four_ints(-1, -1, -1, -1);
375 for(unsigned int i = 0; i < vpg.size(); i++)
376 ph.p.push_back(vpg[i]);
378 ph.compute_neighbors();
379 ph.v.empty(); // no need to copy this data since it'll be replaced
382 polyhedron ph2 = lut[index];
384 // initalize vertices
386 int V = ph2.v.size();
387 for( int j = 0; j < V; j++ )
389 idVec3 proj = homogeneous_difference( light, ph2.v[j] );
390 ph2.v.push_back( idVec4(proj.x, proj.y, proj.z, 0) );
393 // need to compute planes for the shadow volume (sv)
394 ph2.recompute_planes();
399 typedef MyArray<idVec4, 36> MySegments;
400 //int MySegments::max_size = 0;
402 void polyhedron_edges(polyhedron & a, MySegments & e)
405 if(a.e.size() == 0 && a.p.size() != 0)
406 a.compute_neighbors();
408 for(unsigned int i = 0; i < a.e.size(); i++)
410 e.push_back(a.v[a.e[i].vi[0]]);
411 e.push_back(a.v[a.e[i].vi[1]]);
416 // clip the segments of e by the planes of polyhedron a.
417 void clip_segments(const polyhedron & ph, MySegments & is, MySegments & os)
419 const MyArrayPoly & p = ph.p;
421 for(unsigned int i = 0; i < is.size(); i+=2 )
427 bool discard = false;
429 for(unsigned int j = 0; j < p.size(); j++ )
431 float da = a * p[j].plane;
432 float db = b * p[j].plane;
433 float rdw = 1/(da - db);
449 c = -db * rdw * a + da * rdw * b;
454 c = -db * rdw * a + da * rdw * b;
462 common->Printf("bad clip code!\n");
479 idMat4 make_idMat4(const float * m)
481 return idMat4( m[ 0], m[ 4], m[ 8], m[12],
482 m[ 1], m[ 5], m[ 9], m[13],
483 m[ 2], m[ 6], m[10], m[14],
484 m[ 3], m[ 7], m[11], m[15] );
487 idVec3 v4to3(const idVec4 & v)
489 return idVec3(v.x/v.w, v.y/v.w, v.z/v.w);
492 void draw_polyhedron( const viewDef_t *viewDef, const polyhedron & p, idVec4 color )
494 for(unsigned int i = 0; i < p.e.size(); i++)
496 viewDef->renderWorld->DebugLine( color, v4to3(p.v[p.e[i].vi[0]]), v4to3(p.v[p.e[i].vi[1]]));
500 void draw_segments( const viewDef_t *viewDef, const MySegments & s, idVec4 color )
502 for(unsigned int i = 0; i < s.size(); i+=2)
504 viewDef->renderWorld->DebugLine( color, v4to3(s[i]), v4to3(s[i+1]));
508 void world_to_hclip( const viewDef_t *viewDef, const idVec4 &global, idVec4 &clip ) {
512 for ( i = 0 ; i < 4 ; i ++ ) {
514 global[0] * viewDef->worldSpace.modelViewMatrix[ i + 0 * 4 ] +
515 global[1] * viewDef->worldSpace.modelViewMatrix[ i + 1 * 4 ] +
516 global[2] * viewDef->worldSpace.modelViewMatrix[ i + 2 * 4 ] +
517 global[3] * viewDef->worldSpace.modelViewMatrix[ i + 3 * 4 ];
521 for ( i = 0 ; i < 4 ; i ++ ) {
523 view[0] * viewDef->projectionMatrix[ i + 0 * 4 ] +
524 view[1] * viewDef->projectionMatrix[ i + 1 * 4 ] +
525 view[2] * viewDef->projectionMatrix[ i + 2 * 4 ] +
526 view[3] * viewDef->projectionMatrix[ i + 3 * 4 ];
530 idScreenRect R_CalcIntersectionScissor( const idRenderLightLocal * lightDef,
531 const idRenderEntityLocal * entityDef,
532 const viewDef_t * viewDef ) {
534 idMat4 omodel = make_idMat4( entityDef->modelMatrix );
535 idMat4 lmodel = make_idMat4( lightDef->modelMatrix );
537 // compute light polyhedron
538 polyhedron lvol = PolyhedronFromBounds( lightDef->frustumTris->bounds );
539 // transform it into world space
540 //lvol.transform( lmodel );
543 if ( r_useInteractionScissors.GetInteger() == -2 ) {
544 draw_polyhedron( viewDef, lvol, colorRed );
547 // compute object polyhedron
548 polyhedron vol = PolyhedronFromBounds( entityDef->referenceBounds );
550 //viewDef->renderWorld->DebugBounds( colorRed, lightDef->frustumTris->bounds );
551 //viewDef->renderWorld->DebugBox( colorBlue, idBox( model->Bounds(), entityDef->parms.origin, entityDef->parms.axis ) );
553 // transform it into world space
554 vol.transform( omodel );
557 if ( r_useInteractionScissors.GetInteger() == -2 ) {
558 draw_polyhedron( viewDef, vol, colorBlue );
561 // transform light position into world space
562 idVec4 lightpos = idVec4(lightDef->globalLightOrigin.x,
563 lightDef->globalLightOrigin.y,
564 lightDef->globalLightOrigin.z,
567 // generate shadow volume "polyhedron"
568 polyhedron sv = make_sv(vol, lightpos);
570 MySegments in_segs, out_segs;
572 // get shadow volume edges
573 polyhedron_edges(sv, in_segs);
574 // clip them against light bounds planes
575 clip_segments(lvol, in_segs, out_segs);
577 // get light bounds edges
578 polyhedron_edges(lvol, in_segs);
579 // clip them by the shadow volume
580 clip_segments(sv, in_segs, out_segs);
583 if ( r_useInteractionScissors.GetInteger() == -2 ) {
584 draw_segments( viewDef, out_segs, colorGreen );
589 for( unsigned int i = 0; i < out_segs.size(); i++ ) {
592 world_to_hclip( viewDef, out_segs[i], v );
595 return lightDef->viewLight->scissorRect;
598 idVec3 rv(v.x, v.y, v.z);
601 outbounds.AddPoint( rv );
604 // limit the bounds to avoid an inside out scissor rectangle due to floating point to short conversion
605 if ( outbounds[0].x < -1.0f ) {
606 outbounds[0].x = -1.0f;
608 if ( outbounds[1].x > 1.0f ) {
609 outbounds[1].x = 1.0f;
611 if ( outbounds[0].y < -1.0f ) {
612 outbounds[0].y = -1.0f;
614 if ( outbounds[1].y > 1.0f ) {
615 outbounds[1].y = 1.0f;
618 float w2 = ( viewDef->viewport.x2 - viewDef->viewport.x1 + 1 ) / 2.0f;
619 float x = viewDef->viewport.x1;
620 float h2 = ( viewDef->viewport.y2 - viewDef->viewport.y1 + 1 ) / 2.0f;
621 float y = viewDef->viewport.y1;
624 rect.x1 = outbounds[0].x * w2 + w2 + x;
625 rect.x2 = outbounds[1].x * w2 + w2 + x;
626 rect.y1 = outbounds[0].y * h2 + h2 + y;
627 rect.y2 = outbounds[1].y * h2 + h2 + y;
630 rect.Intersect( lightDef->viewLight->scissorRect );
633 if ( r_useInteractionScissors.GetInteger() == -2 && !rect.IsEmpty() ) {
634 viewDef->renderWorld->DebugScreenRect( colorYellow, rect, viewDef );