forked from TheSuperHackers/GeneralsGameCode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathW3DVolumetricShadow.cpp
More file actions
3901 lines (3198 loc) · 127 KB
/
W3DVolumetricShadow.cpp
File metadata and controls
3901 lines (3198 loc) · 127 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
** Command & Conquer Generals(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////////////////////////////////////////////////////////////////////////////////
// //
// (c) 2001-2003 Electronic Arts Inc. //
// //
////////////////////////////////////////////////////////////////////////////////
// FILE: W3DVolumetricShadow.cpp ///////////////////////////////////////////////////////////
//
// Real time shadow volume representations
//
// Author: Colin Day, January 2001
// Adapted for W3D: Mark Wilczynski October 2001
//
//
///////////////////////////////////////////////////////////////////////////////
///@todo: Must cap shadow volumes if we ever allow camera inside the volumes.
///@todo: Find better way to determine when shadow volumes need updating - lights move, objects move.
// SYSTEM INCLUDES ////////////////////////////////////////////////////////////
#include <assert.h>
// USER INCLUDES //////////////////////////////////////////////////////////////
#include "always.h"
#include "GameClient/View.h"
#include "WW3D2/camera.h"
#include "WW3D2/light.h"
#include "WW3D2/dx8wrapper.h"
#include "WW3D2/hlod.h"
#include "WW3D2/mesh.h"
#include "WW3D2/meshmdl.h"
#include "Lib/BaseType.h"
#include "W3DDevice/GameClient/HeightMap.h"
#include "d3dx8math.h"
#include "Common/GlobalData.h"
#include "Common/DrawModule.h"
#include "W3DDevice/GameClient/W3DVolumetricShadow.h"
#include "W3DDevice/GameClient/W3DShadow.h"
#include "WW3D2/statistics.h"
#include "GameLogic/TerrainLogic.h"
#include "WW3D2/dx8caps.h"
#include "GameClient/Drawable.h"
// Global Variables and Functions /////////////////////////////////////////////
W3DVolumetricShadowManager *TheW3DVolumetricShadowManager=nullptr;
extern const FrustumClass *shadowCameraFrustum; //defined in W3DShadow.
///////////////////////////////////////////////////////////////////////////////
// DEFINITIONS ////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// when the change in angle from the object to the light source
// (represented in degrees in the first number below)
// is large enough the shadow info will be reconstructed
const Real cosAngleToCare = cos ((0.2 * PI) / 180.0); //1.5 degree difference
#define MAX_SILHOUETTE_EDGES 1024 //maximum number of shadov volume sides or edges in silhoutte
#define SHADOW_EXTRUSION_BUFFER 0.1f //amount to extend shadow volume beyond what's required to hit ground.
#define AIRBORNE_UNIT_GROUND_DELTA 2.0f
#define MAX_SHADOW_LENGTH_SCALE_FACTOR 1.0f //amount shadow can extend beyond the objects normal bounding sphere
#define MAX_SHADOW_LENGTH_EXTRA_AIRBORNE_SCALE_FACTOR 1.5f //scales MAX_SHADOW_LENGTH_SCALE_FACTOR a little more for flying units
#define MAX_EXTRUSION_LENGTH (512.0f*MAP_XY_FACTOR) //maximum length of a shadow extrusion - assumed as 512x512 cell map for now.
#define MAX_SHADOW_EXTRUSION_UNDER_OBJECT_BEFORE_CLAMP 5.0f //maximum amount that shadow can reach below object base (z-position) before we clamp it's length to reduce artifacts.
#define SHADOW_SAMPLING_INTERVAL (MAP_XY_FACTOR * 2.0f) //stepsize along ray used to find lowest point on terrain within shadow's reach.
#define OVERHANGING_OBJECT_CLAMP_ANGLE (80.0f/180.0f*PI) //for objects that are right on a cliff edge, clamp light angle to cast a nearly vertical shadow.
//#define SV_DEBUG
//#define SV_DEBUG_BOUNDS
struct SHADOW_STATIC_VOLUME_VERTEX //vertex structure passed to D3D
{
float x,y,z;
};
#define SHADOW_STATIC_VOLUME_FVF D3DFVF_XYZ
#ifdef SV_DEBUG //in debug mode, dynamic shadows are rendered with random diffuse color
struct SHADOW_DYNAMIC_VOLUME_VERTEX //vertex structure passed to D3D
{
float x,y,z;
DWORD diffuse;
};
#define SHADOW_DYNAMIC_VOLUME_FVF D3DFVF_XYZ|D3DFVF_DIFFUSE
#else
typedef struct SHADOW_STATIC_VOLUME_VERTEX SHADOW_DYNAMIC_VOLUME_VERTEX;
#define SHADOW_DYNAMIC_VOLUME_FVF D3DFVF_XYZ
#endif
LPDIRECT3DVERTEXBUFFER8 shadowVertexBufferD3D=nullptr; ///<D3D vertex buffer
LPDIRECT3DINDEXBUFFER8 shadowIndexBufferD3D=nullptr; ///<D3D index buffer
int nShadowVertsInBuf=0; //model vetices in vertex buffer
int nShadowStartBatchVertex=0;
int nShadowIndicesInBuf=0; //model vetices in vertex buffer
int nShadowStartBatchIndex=0;
int SHADOW_VERTEX_SIZE=4096;
int SHADOW_INDEX_SIZE=8192;
//Rough bounding box around visible portion of the terrain
//useful for quick culling
static Real bcX;
static Real bcY;
static Real bcZ;
static Real beX;
static Real beY;
static Real beZ;
static LPDIRECT3DVERTEXBUFFER8 lastActiveVertexBuffer=nullptr;
/** A simple structure to hold random geometry (vertices, polygons, etc.). We'll use this
* to store shadow volumes. */
struct Geometry
{
enum VisibleState {
STATE_UNKNOWN = CollisionMath::BOTH,
STATE_VISIBLE = CollisionMath::INSIDE,
STATE_INVISIBLE = CollisionMath::OUTSIDE,
};
Geometry() : m_verts(nullptr),m_indices(nullptr),m_numPolygon(0),m_numVertex(0),m_flags(0) {}
~Geometry() { Release();}
Int Create( Int numVertices, Int numPolygons )
{
if (numVertices)
if((m_verts=NEW Vector3[numVertices]) == nullptr)
return FALSE;
if (numPolygons)
if((m_indices=NEW UnsignedShort[numPolygons*3]) == nullptr)
return FALSE;
m_numPolygon=numPolygons;
m_numVertex=numVertices;
m_numActivePolygon=0;
m_numActiveVertex=0;
return TRUE;
}
void Release()
{
delete [] m_verts;
m_verts=nullptr;
delete [] m_indices;
m_indices=nullptr;
m_numActivePolygon=m_numPolygon=0;
m_numActiveVertex=m_numVertex=0;
}
Int GetFlags () { return m_flags;}
void SetFlags (Int flags) { m_flags = flags;}
Int GetNumPolygon () { return m_numPolygon;}
Int GetNumVertex () { return m_numVertex;}
Int GetNumActivePolygon () { return m_numActivePolygon;}
Int GetNumActiveVertex () { return m_numActiveVertex;}
Int SetNumActivePolygon (Int numPolygons) { return m_numActivePolygon=numPolygons;}
Int SetNumActiveVertex (Int numVertices) { return m_numActiveVertex=numVertices;}
UnsignedShort *GetPolygonIndex (long dwPolyId, short *psIndexList, int dwNSize) const
{
*psIndexList++ = m_indices[dwPolyId*3];
*psIndexList++ = m_indices[dwPolyId*3+1];
*psIndexList++ = m_indices[dwPolyId*3+2];
return &m_indices[dwPolyId];
}
Int SetPolygonIndex (long dwPolyId, short *psIndexList, int dwNSize)
{
m_indices[dwPolyId*3]=psIndexList[0];
m_indices[dwPolyId*3+1]=psIndexList[1];
m_indices[dwPolyId*3+2]=psIndexList[2];
return 3;
}
Vector3 *GetVertex (int dwVertId)
{
return &m_verts[dwVertId];
}
Vector3 *SetVertex (int dwVertId, Vector3 *pvVertex)
{
m_verts[dwVertId]=*pvVertex;
return pvVertex;
}
///Find a vertex within given range
Int FindVertexInRange (Int start, Int end, Vector3 *pvVertex)
{
for (Int i=start; i<end; i++)
{
if ((m_verts[i]-*pvVertex).Length2() == 0)
return i;
}
return -1;
}
AABoxClass &getBoundingBox() {return m_boundingBox;}
void setBoundingBox(const AABoxClass &box) {m_boundingBox=box;}
void setBoundingSphere(const SphereClass &sphere) {m_boundingSphere=sphere;}
SphereClass &getBoundingSphere() {return m_boundingSphere;}
void setVisibleState(VisibleState state) {m_visibleState=state;}
VisibleState getVisibleState() {return m_visibleState;}
private:
Vector3 *m_verts;
UnsignedShort *m_indices;
Int m_numPolygon;
Int m_numVertex;
Int m_numActivePolygon; ///<number of polygons filled with valid data
Int m_numActiveVertex; ///<number of vertices filled with valid data
Int m_flags; ///<geometry attribute flags - static vs. dynamic, etc.
AABoxClass m_boundingBox; ///<object space bounding box of shadow volume
SphereClass m_boundingSphere; ///<object space bounding sphere of shadow volume
VisibleState m_visibleState; ///<flag if this geometry was visible in this frame.
};
// CONST //////////////////////////////////////////////////////////////////////
const Int MAX_POLYGON_NEIGHBORS = 3; // we use nothing but triangles for
// geometry polygons so we have at
// most 3 neighbors
const Int NO_NEIGHBOR = -1; // entry value for neighbor when there isn't one
const Byte POLY_VISIBLE = 0x01; // polygon is visible from light
const Byte POLY_PROCESSED = 0x02; // this poly has been processed
// STRUCT /////////////////////////////////////////////////////////////////////
// NeighborEdge ---------------------------------------------------------------
typedef struct _NeighborEdge
{
Short neighborIndex; // index of polygon who is our neighbor, if there is
// not a neighbor it contains NO_NEIGHBOR
Short neighborEdgeIndex[ 2 ]; // the two vertex indices that represent the
// shared edge
} NeighborEdge;
// PolygonNeighbor ------------------------------------------------------------
struct PolyNeighbor
{
Short myIndex; // our polygon index so we know who we are
Byte status; // status flags used when processing neighbors
NeighborEdge neighbor[ MAX_POLYGON_NEIGHBORS ];
};
/**This class holds original mesh specific data and geometry. The meshes stored in this
class have been cleaned to remove replicated vertices and also cache mesh data needed for
faster silhouette computation. A model can contain many meshes for which we need to store
separate data so they can move relative to each other.*/
class W3DShadowGeometryMesh
{
//for the sake of speed, give direct access to classes that need this data.
friend class W3DShadowGeometry;
friend class W3DVolumetricShadow;
public:
W3DShadowGeometryMesh();
~W3DShadowGeometryMesh();
/// @todo: Cache/Store face normals someplace so they are not recomputed when lights move.
Vector3 *GetPolygonNormal (long dwPolyNormId, Vector3 *pvNorm)
{
if (m_polygonNormals)
return &(*pvNorm=m_polygonNormals[dwPolyNormId]);
short indexList[3];
Vector3 vertexList[3];
//get vertex indices for this polygon
GetPolygonIndex(dwPolyNormId,indexList,3);
//get the vertices
GetVertex(indexList[0],&vertexList[0]);
GetVertex(indexList[1],&vertexList[1]);
GetVertex(indexList[2],&vertexList[2]);
//compute triangle normal by crossing 2 edges
Vector3 edge1=vertexList[1]-vertexList[0];
Vector3 edge2=vertexList[1]-vertexList[2];
#ifdef ALLOW_TEMPORARIES
*pvNorm=Vector3::Cross_Product(edge2,edge1);
pvNorm->Normalize();
#else
Vector3::Normalized_Cross_Product(edge2,edge1, pvNorm);
#endif
return pvNorm;
}
int GetNumPolygon () const {return m_numPolygons;}
/// given loaded geometry this builds the polygon neighbor information
void buildPolygonNeighbors();
void buildPolygonNormals()
{
if (!m_polygonNormals)
{ //need to allocate storage
Vector3 *tempVec = NEW Vector3[m_numPolygons];
for (int i=0; i<m_numPolygons; i++)
{
GetPolygonNormal(i,&tempVec[i]);
}
m_polygonNormals = tempVec;
}
}
protected:
/// creating and deleting storage for the polygon neighbors
Bool allocateNeighbors( Int numPolys );
void deleteNeighbors();
// geometry shadow data access
PolyNeighbor *GetPolyNeighbor( Int polyIndex );
int GetNumVertex () { return m_numVerts;}
///Get indices to the 3 vertices of this face.
virtual int GetPolygonIndex (long dwPolyId, short *psIndexList, int dwNSize) const
{ const TriIndex *polyi=&m_polygons[dwPolyId];
*psIndexList++ = m_parentVerts[polyi->I];
*psIndexList++ = m_parentVerts[polyi->J];
*psIndexList++ = m_parentVerts[polyi->K];
return 3;
}
virtual Vector3 *GetVertex (int dwVertId, Vector3 *pvVertex)
{
*pvVertex=m_verts[dwVertId];
return pvVertex;
}
MeshClass *m_mesh; ///< W3D mesh for this geometry
Int m_meshRobjIndex; ///<index of this mesh within hlod robj
const Vector3 *m_verts; ///<array of vertices
Vector3 *m_polygonNormals; ///<array of face normals
Int m_numVerts; ///< number of actual vertices after duplicates are removed.
Int m_numPolygons; ///<number of polygons in source geometry
const TriIndex *m_polygons; ///<array of 3 vertex indices per face
UnsignedShort *m_parentVerts; ///<array of parent vertex indices for each vertex.
/// the neighbor info indexed by polygon id
PolyNeighbor *m_polyNeighbors;
Int m_numPolyNeighbors; // length of m_polyNeighbors and the number of polygons
// in our current geometry.
W3DShadowGeometry *m_parentGeometry; // mesh hierarchy containing this mesh.
};
#ifdef DO_TERRAIN_SHADOW_VOLUMES
//Custom version of W3DShadowGeometryMesh for meshes stored as heightmap
class W3DShadowGeometryHeightmapMesh : public W3DShadowGeometryMesh
{
public:
virtual int GetPolygonIndex (long dwPolyId, short *psIndexList, int dwNSize) const;
virtual Vector3 *GetVertex (int dwVertId, Vector3 *pvVertex);
W3DShadowGeometryHeightmapMesh() : m_patchOriginX(0),m_patchOriginY(0) { }
void setPatchOrigin(Int x, Int y) {m_patchOriginX=x; m_patchOriginY=y;}
void getPatchOrigin(Int *x, Int *y) {*x=m_patchOriginX; *y=m_patchOriginY;}
void setPatchSize(Int size) {m_width=size; m_numPolygons=(size-1)*(size-1)*2;}
Int getPatchSize() {return m_width;}
protected:
Int m_heightmapPitch; ///<width of full heightmap of which this mesh is a sub-rectangle
Int m_width; ///<patch width
Int m_patchOriginX; ///<location of patch within parent heightmap
Int m_patchOriginY; ///<location of patch within parent heightmap
};
int W3DShadowGeometryHeightmapMesh::GetPolygonIndex (long dwPolyId, short *psIndexList, int dwNSize) const
{
//Find top left vertex of cell containing polygon
WorldHeightMap *map=nullptr;
if (TheTerrainRenderObject)
map=TheTerrainRenderObject->getMap();
if (!map)
return 0;
Int row=dwPolyId/((m_width-1)<<1);
Int column=(dwPolyId>>1)-row*((m_width-1));
#ifdef FLIP_TRIANGLES
UnsignedByte alpha[4];
float UA[4], VA[4];
Bool flipForBlend;
map->getAlphaUVData(column+m_patchOriginX, row+m_patchOriginY, UA, VA, alpha, &flipForBlend);
if (flipForBlend)
{
if (dwPolyId &1)
{ psIndexList[0]=row*m_width+column+1;
psIndexList[1]=(row+1)*m_width+column+1;
psIndexList[2]=(row+1)*m_width+column;
}
else
{ psIndexList[0]=row*m_width+column;
psIndexList[1]=row*m_width+column+1;
psIndexList[2]=(row+1)*m_width+column;
}
}
else
#endif
{ if (dwPolyId &1)
{ psIndexList[0]=row*m_width+column;
psIndexList[1]=row*m_width+column+1;
psIndexList[2]=(row+1)*m_width+column+1;
}
else
{ psIndexList[0]=row*m_width+column;
psIndexList[1]=(row+1)*m_width+column+1;
psIndexList[2]=(row+1)*m_width+column;
}
}
return 3;
}
Vector3 *W3DShadowGeometryHeightmapMesh::GetVertex (int dwVertId, Vector3 *pvVertex)
{
WorldHeightMap *map=nullptr;
if (TheTerrainRenderObject)
map=TheTerrainRenderObject->getMap();
if (!map)
return nullptr;
Int row=dwVertId/m_width;
Int column=dwVertId-row*m_width;
UnsignedByte *data=map->getDataPtr();
pvVertex->X=(m_patchOriginX+column)*MAP_XY_FACTOR;
pvVertex->Y=(m_patchOriginY+row)*MAP_XY_FACTOR;
pvVertex->Z=(Real)data[(m_patchOriginX+column)+(m_patchOriginY+row)*map->getXExtent()]*MAP_HEIGHT_SCALE;
return pvVertex;
}
Bool isPatchShadowed(W3DShadowGeometryHeightmapMesh *hm_mesh)
{
WorldHeightMap *map=nullptr;
Short poly[ 3 ];
Vector3 vertex;
Vector3 normal,lightVector;
Int firstVisible=0;
Int testVisible;
if (TheTerrainRenderObject)
map=TheTerrainRenderObject->getMap();
if (!map)
return nullptr;
hm_mesh->GetPolygonNormal( 0, &normal );
// get the vertex indices at this polygon
hm_mesh->GetPolygonIndex( 0, poly, 3 );
//
// find out "lightVector" to this polygon
//
// since our light source could be very close to the object and that
// would change the shadow we are going to say that the light vector
// is from the light position to one of the vertices in the polygon.
// To be more correct we should use the center of the polygon but
// this is a good approximation ... an ever broader approximation that
// we could use would be the object center
//
hm_mesh->GetVertex( poly[ 0 ], &vertex );
lightVector= vertex - LightPosWorld[0];
//
// dot the light vector with the normal of the polygon to see if the
// poly is visible from this location
//
if( Vector3::Dot_Product( lightVector, normal ) < 0.0f )
firstVisible=1;
for (Int i=1; i<hm_mesh->GetNumPolygon(); i++)
{
hm_mesh->GetPolygonNormal( i, &normal );
// get the vertex indices at this polygon
hm_mesh->GetPolygonIndex( i, poly, 3 );
//
// find out "lightVector" to this polygon
//
// since our light source could be very close to the object and that
// would change the shadow we are going to say that the light vector
// is from the light position to one of the vertices in the polygon.
// To be more correct we should use the center of the polygon but
// this is a good approximation ... an ever broader approximation that
// we could use would be the object center
//
hm_mesh->GetVertex( poly[ 0 ], &vertex );
lightVector= vertex - LightPosWorld[0];
//
// dot the light vector with the normal of the polygon to see if the
// poly is visible from this location
//
testVisible=0;
if( Vector3::Dot_Product( lightVector, normal ) < 0.0f )
testVisible=1;
// if (testVisible ^ firstVisible)
// return TRUE; //found polys facing different directions to sun, will cast shadow
if (!testVisible)
return TRUE; //some part of mesh not facing light, so it could cast a shadow
}
return FALSE;
}
#define SV_MAX_TERRAIN_MESHES 16
static W3DShadowGeometryHeightmapMesh terrainMeshes[SV_MAX_TERRAIN_MESHES];
static Int numTerrainMeshes=0;
void W3DVolumetricShadowManager::loadTerrainShadows()
{
WorldHeightMap *map=nullptr;
Int patchSize=3;
if (TheTerrainRenderObject)
map=TheTerrainRenderObject->getMap();
if (!map)
return;
for (Int y=0; y<map->getYExtent(); y += patchSize-1)
{
for (Int x=0; x<map->getXExtent(); x += patchSize-1)
{
W3DShadowGeometryHeightmapMesh *hm_mesh=&terrainMeshes[numTerrainMeshes];
hm_mesh->setPatchOrigin(x,y);
hm_mesh->setPatchSize(patchSize);
if(isPatchShadowed(hm_mesh))
{ //some polygons in this patch cast shadows, need to generate a mesh
hm_mesh->buildPolygonNeighbors();
numTerrainMeshes++;
}
}
}
/* W3DShadowGeometryHeightmapMesh hm_mesh;
short indexList[3];
Vector3 vertexList[3];
*/
/* W3DShadow *shadow = NEW W3DShadow;
// add to our shadow list through the shadow next links
shadow->m_next = m_shadowList;
m_shadowList = shadow;
*/
}
#endif //DO_TERRAIN_SHADOW_VOLUMES
/** This class will wrap any shadow casting geometry with additional
data needed for efficient shadow volume generation. The W3DVolumetricShadowManager
will allocate these structures and hash them for quick re-use on other
models sharing the same geometry.*/
class W3DShadowGeometry : public RefCountClass, public HashableClass
{
public:
W3DShadowGeometry() { };
~W3DShadowGeometry() { };
virtual const char * Get_Key() { return m_namebuf; }
Int init (RenderObjClass *robj);
Int initFromHLOD (RenderObjClass *robj); ///<initialize the geometry from a W3D HLOD object.
Int initFromMesh (RenderObjClass *robj);///<initialize the geometry from a W3D Mesh object.
const char * Get_Name() const { return m_namebuf;}
void Set_Name(const char *name)
{
strlcpy(m_namebuf,name,sizeof(m_namebuf));
}
Int getMeshCount() { return m_meshCount;}
W3DShadowGeometryMesh *getMesh(Int index) { return &m_meshList[index];}
int GetNumTotalVertex () { return m_numTotalsVerts;} ///<total number of vertices in all meshes of this geometry
private:
char m_namebuf[2*W3D_NAME_LEN]; ///<name of model hierarchy
W3DShadowGeometryMesh m_meshList[MAX_SHADOW_CASTER_MESHES]; ///<collection of meshes for this geometry.
Int m_meshCount; ///<number of meshes in hierarchy
Int m_numTotalsVerts; ///<number of verts in entire hierarchy
};
#define MAX_SHADOW_VOLUME_VERTS 16384
Int W3DShadowGeometry::initFromHLOD(RenderObjClass *robj)
{
HLodClass *hlod=(HLodClass *)robj;
//locations of parent vertices inside the vertex array after duplicate
//vertices are removed.
UnsignedShort vertParent[MAX_SHADOW_VOLUME_VERTS];
Int i,j,k,newVertexCount;
Int top = hlod->Get_LOD_Count()-1;
W3DShadowGeometryMesh *geomMesh=&m_meshList[m_meshCount];
m_numTotalsVerts=0;
for (i = 0; i < hlod->Get_Lod_Model_Count(top); i++)
{
if (hlod->Peek_Lod_Model(top,i) && hlod->Peek_Lod_Model(top,i)->Class_ID() == RenderObjClass::CLASSID_MESH)
{
DEBUG_ASSERTCRASH(m_meshCount < MAX_SHADOW_CASTER_MESHES, ("Too many shadow sub-meshes"));
geomMesh->m_mesh = (MeshClass *)hlod->Peek_Lod_Model(top,i);
geomMesh->m_meshRobjIndex=i;
if ((geomMesh->m_mesh->Is_Alpha() || geomMesh->m_mesh->Is_Translucent()) && !geomMesh->m_mesh->Peek_Model()->Get_Flag(MeshGeometryClass::CAST_SHADOW))
continue; //transparent meshes that don't have forced shadows will not cast volumetric shadows
MeshModelClass *mm = geomMesh->m_mesh->Peek_Model();
geomMesh->m_numVerts=mm->Get_Vertex_Count();
geomMesh->m_verts=mm->Get_Vertex_Array();
geomMesh->m_numPolygons=mm->Get_Polygon_Count();
geomMesh->m_polygons=mm->Get_Polygon_Array();
if (geomMesh->m_numVerts > MAX_SHADOW_VOLUME_VERTS)
return FALSE; //too many vertices to process
//reset index of all vertices
memset(vertParent,0xffffffff,sizeof(vertParent));
newVertexCount=geomMesh->m_numVerts;
//Find all duplicated vertices.
for (j=0; j<geomMesh->m_numVerts; j++)
{
if (vertParent[j] != 0xffff)
continue; //this vertex has already been processed
const Vector3 *v_curr=&geomMesh->m_verts[j];
for (k=j+1; k<geomMesh->m_numVerts; k++)
{
Vector3 len(*v_curr - geomMesh->m_verts[k]);
if (len.Length2() == 0)
{ //found duplicate vertex
vertParent[k]=j;
newVertexCount--; //decrease total vertices since duplicate found.
}
}
vertParent[j]=j; //first instance of new vertex
}
geomMesh->m_parentVerts = NEW UnsignedShort[geomMesh->m_numVerts];
memcpy(geomMesh->m_parentVerts,vertParent,sizeof(UnsignedShort)*geomMesh->m_numVerts);
geomMesh->m_numVerts=newVertexCount; //adjust actual vertex count to ignore duplicates
m_numTotalsVerts += newVertexCount;
geomMesh->m_parentGeometry = this;
// build our neighboring polygon information
geomMesh->buildPolygonNeighbors();
geomMesh++;
m_meshCount++;
}
}
// for (i = 0; i < AdditionalModels.Count(); i++) {
// res |= AdditionalModels[i].Model->Cast_Ray(raytest);
// }
return m_meshCount != 0;
}
Int W3DShadowGeometry::initFromMesh(RenderObjClass *robj)
{
//locations of parent vertices inside the vertex array after duplicate
//vertices are removed.
UnsignedShort vertParent[MAX_SHADOW_VOLUME_VERTS];
Int j,k,newVertexCount;
W3DShadowGeometryMesh *geomMesh=&m_meshList[m_meshCount];
assert (m_meshCount < MAX_SHADOW_CASTER_MESHES);
geomMesh->m_mesh = (MeshClass *)robj;
geomMesh->m_meshRobjIndex = -1; //robj is the mesh so no index needed.
if (((geomMesh->m_mesh->Is_Alpha() || geomMesh->m_mesh->Is_Translucent()) && !geomMesh->m_mesh->Peek_Model()->Get_Flag(MeshGeometryClass::CAST_SHADOW)))
return FALSE; //transparent meshes that don't have forced shadows will not cast volumetric shadows
MeshModelClass *mm = geomMesh->m_mesh->Peek_Model();
geomMesh->m_numVerts=mm->Get_Vertex_Count();
geomMesh->m_verts=mm->Get_Vertex_Array();
geomMesh->m_numPolygons=mm->Get_Polygon_Count();
geomMesh->m_polygons=mm->Get_Polygon_Array();
if (geomMesh->m_numVerts > MAX_SHADOW_VOLUME_VERTS)
return FALSE; //too many vertices to process
//reset index of all vertices
memset(vertParent,0xffffffff,sizeof(vertParent));
newVertexCount=geomMesh->m_numVerts;
//Find all duplicated vertices.
for (j=0; j<geomMesh->m_numVerts; j++)
{
if (vertParent[j] != 0xffff)
continue; //this vertex has already been processed
const Vector3 *v_curr=&geomMesh->m_verts[j];
for (k=j+1; k<geomMesh->m_numVerts; k++)
{
Vector3 len(*v_curr - geomMesh->m_verts[k]);
if (len.Length2() == 0)
{ //found duplicate vertex
vertParent[k]=j;
newVertexCount--; //decrease total vertices since duplicate found.
}
}
vertParent[j]=j; //first instance of new vertex
}
geomMesh->m_parentVerts = NEW UnsignedShort[geomMesh->m_numVerts];
memcpy(geomMesh->m_parentVerts,vertParent,sizeof(UnsignedShort)*geomMesh->m_numVerts);
geomMesh->m_numVerts=newVertexCount; //adjust actual vertex count to ignore duplicates
geomMesh->m_parentGeometry = this;
m_meshCount=1;
m_numTotalsVerts=newVertexCount;
// build our neighboring polygon information
geomMesh->buildPolygonNeighbors();
return TRUE;
}
Int W3DShadowGeometry::init(RenderObjClass *robj)
{
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// MEMBER DEFINITIONS /////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// W3DShadowGeometry =============================================================
// ============================================================================
W3DShadowGeometryMesh::W3DShadowGeometryMesh()
{
// init polygon neighbor information
m_polyNeighbors = nullptr;
m_numPolyNeighbors = 0;
m_parentVerts = nullptr;
m_polygonNormals = nullptr;
}
// ~W3DShadowGeometry ============================================================
// ============================================================================
W3DShadowGeometryMesh::~W3DShadowGeometryMesh()
{
// remove our neighbor list information allocated
deleteNeighbors();
delete [] m_parentVerts;
delete [] m_polygonNormals;
}
// GetPolyNeighbor ============================================================
// Return the poly neighbor structure at the given index
// ============================================================================
PolyNeighbor *W3DShadowGeometryMesh::GetPolyNeighbor( Int polyIndex )
{
// sanity
if( polyIndex < 0 || polyIndex >= m_numPolyNeighbors )
{
// DBGPRINTF(( "Invalid neighbor index '%d'\n", polyIndex ));
assert( 0 );
return nullptr;
}
return &m_polyNeighbors[ polyIndex ];
}
// buildPolygonNeighbors ======================================================
// Whenever we set a new geometry we want to build some information about
// the faces in the new geometry so that we can efficiently traverse across
// the surface to neighboring polygons
// ============================================================================
void W3DShadowGeometryMesh::buildPolygonNeighbors()
{
Int numPolys;
Int i, j;
// how many polygons are in our geometry
numPolys = GetNumPolygon();
//
// if there are no polygons for this geometry then we should have no
// neighbor information
//
if( numPolys == 0 )
{
//
// if our geometry has somehow deformed and we used to have polygon
// neighbor information we should delete it before we bail
//
if( m_numPolyNeighbors != 0 )
deleteNeighbors();
return; // nothing to see here people, move along
}
//
// in the event that this geometry can deform on the fly or we are
// building our neighbor information for the very first time ...
// if our current geometry has a different number of polygons than
// we had previously calculated we need to delete and reallocate a
// new storate space for the neighbor information
//
if( numPolys != m_numPolyNeighbors )
{
// delete the old neighbor storage
deleteNeighbors();
// allocate a new pool for neighbor information
if( allocateNeighbors( numPolys ) == FALSE )
return;
}
//
// initialize all polygon neighbor information to none and assign our
// own reference to myIndex so we know who we are
//
for( i = 0; i < m_numPolyNeighbors; i++ )
{
// assign our own identification
m_polyNeighbors[ i ].myIndex = i;
// assign our neighbors to none
for( j = 0; j < MAX_POLYGON_NEIGHBORS; j++ )
m_polyNeighbors[ i ].neighbor[ j ].neighborIndex = NO_NEIGHBOR;
}
// assign polygon data for each of our polygons
for( i = 0; i < m_numPolyNeighbors; i++ )
{
Short poly[ 3 ]; // vertex indices for this polygon
Short otherPoly[ 3 ]; // vertex indices for other polygon
Vector3 vNorm;
// get the indices of the three triangle points for this polygon
GetPolygonIndex( i, poly, 3 );
GetPolygonNormal(i,&vNorm);
// find the neighbors of this polygon
for( j = 0; j < m_numPolyNeighbors; j++ )
{
Int a, b;
Int index1, index2;
Int index1Pos[2]; //positions of shared edge vertices in triangle list. (0,1 or 2)
Int diff1,diff2;
// ignore our own polygon
if( i == j )
continue;
// get the vertex index information for this other polygon
GetPolygonIndex( j, otherPoly, 3 );
//
// if 2 of the 3 vertex indices are the same then these polygons
// are neighbors
//
//Also check if winding order of vertices on edge is opposite.
//If vertices are in same order as our polygon, then it's
//not a valid edge because the neighbor is flipped.
index1 = -1;
index2 = -1;
for( a = 0; a < 3; a++ )
for( b = 0; b < 3; b++ )
if( poly[ a ] == otherPoly[ b ] )
{
if( index1 == -1 )
{ index1 = poly[ a ]; // record matching index1
index1Pos[0]=a;
index1Pos[1]=b;
}
else if( index2 == -1 )
{ //Check direction of edge in each polygon. If they are same direction skip it.
diff1 = a-index1Pos[0];
diff2 = b-index1Pos[1];
if ( ((diff1&0x80000000)^((abs(diff1)&2)<<30)) != ((diff2&0x80000000)^((abs(diff2)&2)<<30)))
{ Vector3 vOtherNorm;
GetPolygonNormal(j,&vOtherNorm);
//Check if the 2 polygons face in exactly opposite directions - don't allow this type of neighbor.
if (fabs(Vector3::Dot_Product(vOtherNorm,vNorm) + 1.0f) <= 0.01f)
continue;
index2 = poly[ a ]; // record matching index2
}
else
continue;
}
else
{//This is the same poly facing opposite direction. //assert( 0 ); // should never match 3 vertices!
index1=index2=-1;
continue;
}
}
if( index1 != -1 && index2 != -1 )
{
//
// the polygon j is a neighbor of our polygon i, put the j
// index into the first free neighbor slot for polygon i
//
for( a = 0; a < MAX_POLYGON_NEIGHBORS; a++ )
if( m_polyNeighbors[ i ].neighbor[ a ].neighborIndex == NO_NEIGHBOR )
{
// record the neighbor index
m_polyNeighbors[ i ].neighbor[ a ].neighborIndex = j;
// record the sharded edge vertex indices
m_polyNeighbors[ i ].neighbor[ a ].neighborEdgeIndex[ 0 ] = index1;
m_polyNeighbors[ i ].neighbor[ a ].neighborEdgeIndex[ 1 ] = index2;
break; // exit for a
}
//
// error condition, if our counter a is at the max number
// of neighbors, which is 3 for a triangle mesh, we did something
// wrong here because that would mean we found a 4th match!
//
if (a == MAX_POLYGON_NEIGHBORS)
{
Vector3 pv[3];
// char errorText[255];
GetVertex (poly[0], &pv[0]);
GetVertex (poly[1], &pv[1]);
GetVertex (poly[2], &pv[2]);
pv[0] = pv[0] + pv[1] + pv[2];
pv[0] /= 3.0f; //find center of polygon
// sprintf(errorText,"%s: Shadow Polygon with too many neighbors at %f,%f,%f",m_parentGeometry->Get_Name(),pv[0].X,pv[0].Y,pv[0].Z);
// DEBUG_LOG(("****%s Shadow Polygon with too many neighbors at %f,%f,%f",m_parentGeometry->Get_Name(),pv[0].X,pv[0].Y,pv[0].Z));
// DEBUG_ASSERTCRASH(a != MAX_POLYGON_NEIGHBORS,(errorText));
}
}
}
}
}
// allocateNeighbors ==========================================================
// Allocate storage for the polygon neighbors and record its size
// ============================================================================
Bool W3DShadowGeometryMesh::allocateNeighbors( Int numPolys )
{
// assure we're not re-allocating without deleting
assert( m_numPolyNeighbors == 0 );
assert( m_polyNeighbors == nullptr );
// allocate the list
m_polyNeighbors = NEW PolyNeighbor[ numPolys ];
if( m_polyNeighbors == nullptr )
{
// DBGPRINTF(( "Unable to allocate polygon neighbors\n" ));
assert( 0 );
return FALSE;
}