| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952 |
- /*
- ** 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/W3DGranny.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"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- // Global Variables and Functions /////////////////////////////////////////////
- W3DVolumetricShadowManager *TheW3DVolumetricShadowManager=NULL;
- 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=NULL; ///<D3D vertex buffer
- LPDIRECT3DINDEXBUFFER8 shadowIndexBufferD3D=NULL; ///<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=NULL;
- /** 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(void) : m_verts(NULL),m_indices(NULL),m_numPolygon(0),m_numVertex(0),m_flags(0) {}
- ~Geometry(void) { Release();}
- Int Create( Int numVertices, Int numPolygons )
- {
- if (numVertices)
- if((m_verts=NEW Vector3[numVertices]) == 0)
- return FALSE;
- if (numPolygons)
- if((m_indices=NEW UnsignedShort[numPolygons*3]) == 0)
- return FALSE;
- m_numPolygon=numPolygons;
- m_numVertex=numVertices;
- m_numActivePolygon=0;
- m_numActiveVertex=0;
- return TRUE;
- }
- void Release(void)
- { if (m_verts)
- { delete [] m_verts;
- m_verts=NULL;
- }
- if (m_indices)
- { delete [] m_indices;
- m_indices=NULL;
- }
- m_numActivePolygon=m_numPolygon=0;
- m_numActiveVertex=m_numVertex=0;
- }
- Int GetFlags (void) { return m_flags;}
- void SetFlags (Int flags) { m_flags = flags;}
- Int GetNumPolygon (void) { return m_numPolygon;}
- Int GetNumVertex (void) { return m_numVertex;}
- Int GetNumActivePolygon (void) { return m_numActivePolygon;}
- Int GetNumActiveVertex (void) { 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(void) {return m_boundingBox;}
- void setBoundingBox(const AABoxClass &box) {m_boundingBox=box;}
- void setBoundingSphere(const SphereClass &sphere) {m_boundingSphere=sphere;}
- SphereClass &getBoundingSphere(void) {return m_boundingSphere;}
- void setVisibleState(VisibleState state) {m_visibleState=state;}
- VisibleState getVisibleState(void) {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( void );
- W3DShadowGeometryMesh::~W3DShadowGeometryMesh( void );
- /// @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 (void) const {return m_numPolygons;}
- /// given loaded geometry this builds the polygon neighbor information
- void buildPolygonNeighbors( void );
- void buildPolygonNormals(void)
- {
- 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( void );
- // geometry shadow data access
- PolyNeighbor *GetPolyNeighbor( Int polyIndex );
- int GetNumVertex (void) { return m_numVerts;}
- ///Get indices to the 3 vertices of this face.
- virtual int GetPolygonIndex (long dwPolyId, short *psIndexList, int dwNSize) const
- { const Vector3i *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 Vector3i *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.
- }; //end of meshInfo
- #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(void) : 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(void) {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=NULL;
- 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, false);
- 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=NULL;
- if (TheTerrainRenderObject)
- map=TheTerrainRenderObject->getMap();
- if (!map)
- return NULL;
- 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=NULL;
- Short poly[ 3 ];
- Vector3 vertex;
- Vector3 normal,lightVector;
- Int firstVisible=0;
- Int testVisible;
- if (TheTerrainRenderObject)
- map=TheTerrainRenderObject->getMap();
- if (!map)
- return NULL;
- 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(void)
- {
- WorldHeightMap *map=NULL;
- 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( void ) { };
- ~W3DShadowGeometry( void ) { };
- virtual const char * Get_Key( void ) { 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(void) const { return m_namebuf;}
- void Set_Name(const char *name)
- { memset(m_namebuf,0,sizeof(m_namebuf)); //pad with zero so always ends with null character.
- strncpy(m_namebuf,name,sizeof(m_namebuf)-1);
- }
- Int getMeshCount(void) { return m_meshCount;}
- W3DShadowGeometryMesh *getMesh(Int index) { return &m_meshList[index];}
-
- int GetNumTotalVertex (void) { 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;
- // m_robj=robj;
- /* //code to deal with granny - don't think we'll use shadow volumes on these!?
- granny_file *fileInfo=robj->getPrototype().m_file;
- for (Int modelIndex=0; modelIndex<fileInfo->ModelCount; modelIndex++)
- {
- granny_model *sourceModel = fileInfo->Models[modelIndex];
- if (stricmp(sourceModel->Name,"AABOX") == 0)
- { //found a collision box, copy out data
- int MeshCount = sourceModel->MeshBindingCount;
- if (MeshCount==1)
- {
- granny_mesh *sourceMesh = sourceModel->MeshBindings[0].Mesh;
- granny_pn33_vertex *Vertices = (granny_pn33_vertex *)sourceMesh->PrimaryVertexData->Vertices;
- Vector3 points[24];
- assert (sourceMesh->PrimaryVertexData->VertexCount <= 24);
- for (Int boxVertex=0; boxVertex<sourceMesh->PrimaryVertexData->VertexCount; boxVertex++)
- { points[boxVertex].Set(Vertices[boxVertex].Position[0],
- Vertices[boxVertex].Position[1],
- Vertices[boxVertex].Position[2]);
- }
- box.Init(points,sourceMesh->PrimaryVertexData->VertexCount);
- }
- }
- else
- { //mesh is part of model
- int meshCount = sourceModel->MeshBindingCount;
- for (Int meshIndex=0; meshIndex<meshCount; meshIndex++)
- {
- granny_mesh *sourceMesh = sourceModel->MeshBindings[meshIndex].Mesh;
- if (sourceMesh->PrimaryVertexData)
- vertexCount+=sourceMesh->PrimaryVertexData->VertexCount;
- }
- }
- }*/
- }
- ///////////////////////////////////////////////////////////////////////////////
- // MEMBER DEFINITIONS /////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////
- // W3DShadowGeometry =============================================================
- // ============================================================================
- W3DShadowGeometryMesh::W3DShadowGeometryMesh( void )
- {
- // init polygon neighbor information
- m_polyNeighbors = NULL;
- m_numPolyNeighbors = 0;
- m_parentVerts = NULL;
- m_polygonNormals = NULL;
- } // end W3DShadowGeometry
- // ~W3DShadowGeometry ============================================================
- // ============================================================================
- W3DShadowGeometryMesh::~W3DShadowGeometryMesh( void )
- {
- // remove our neighbor list information allocated
- deleteNeighbors();
- if (m_parentVerts) {
- delete [] m_parentVerts;
- }
- if (m_polygonNormals)
- delete [] m_polygonNormals;
- } // end ~W3DShadowGeometry
- // 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 NULL;
- } // en dif
- return &m_polyNeighbors[ polyIndex ];
- } // end GetPolyNeighbor
- // buildPolygonNeighbors ======================================================
- // Whenever we set a new geometry we want to build some information about
- // the faces in the new geometry so that we can efficienty traverse across
- // the surface to neighboring polygons
- // ============================================================================
- void W3DShadowGeometryMesh::buildPolygonNeighbors( void )
- {
- 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
- } // end if
- //
- // 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;
- } // end if
- //
- // 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;
- } // end for i
- // 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;
- }
- } // end if
- 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
- } // end if
- //
- // 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\n",m_parentGeometry->Get_Name(),pv[0].X,pv[0].Y,pv[0].Z));
- // DEBUG_ASSERTCRASH(a != MAX_POLYGON_NEIGHBORS,(errorText));
- }
- } // end if
- } // end for j
- } // end for i
- } // end buildPolygonNeighbors
- // 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 == NULL );
- // allocate the list
- m_polyNeighbors = NEW PolyNeighbor[ numPolys ];
- if( m_polyNeighbors == NULL )
- {
- // DBGPRINTF(( "Unable to allocate polygon neighbors\n" ));
- assert( 0 );
- return FALSE;
- } // end if
- // list is now acutally allocated
- m_numPolyNeighbors = numPolys;
- return TRUE; // success!
- } // end allocateNeighbors
- // deleteNeighbors ============================================================
- // Delete all polygon neighbor storage and information
- // ============================================================================
- void W3DShadowGeometryMesh::deleteNeighbors( void )
- {
- // delete list
- if( m_polyNeighbors )
- {
- delete [] m_polyNeighbors;
- m_polyNeighbors = NULL;
- m_numPolyNeighbors = 0;
- } // end if
- // sanity error checking
- assert( m_numPolyNeighbors == 0 );
- assert( m_polyNeighbors == NULL );
- } // end deleteNeighbors
- //#include "Common/ThingTemplate.h"
- // updateOptimalExtrusionPadding ==============================================
- // Use raycasting to figure out a shadow extrusion length that guarantees that
- // the highest point of the object is extruded long enough to hit some ground.
- // This is a very slow operation so only do once for static non-moving objects.
- // ============================================================================
- void W3DVolumetricShadow::updateOptimalExtrusionPadding(void)
- {
- if (m_robj)
- {
- // DrawableInfo *drawInfo=(DrawableInfo *)m_robj->Get_User_Data();
- // Drawable *draw = drawInfo->m_drawable;
- // if (strstr(draw->getTemplate()->getName().str(),"Right02") != 0)
- // draw = draw; //debug code for China06 wacky bridge shadow
- // get the light
- Vector3 lightPosWorld=TheW3DShadowManager->getLightPosWorld(0);
- // check if object has a limit/clamp on shadow length and adjust light
- // position of necessary.
- if (m_shadowLengthScale)
- { //Find light's distance from origin in xy plane
- Real lightXYDistance = sqrt(lightPosWorld.X*lightPosWorld.X + lightPosWorld.Y * lightPosWorld.Y);
- Real newZ=lightXYDistance*m_shadowLengthScale;
- if (newZ > lightPosWorld.Z)
- { //clamped z component is higher than actual light position allows so adjust it.
- lightPosWorld.Z = newZ;
- }
- }
- // find maximum shadow length which will not cause any corners of the object's bounding box
- // to cast shadows that drop significantly below the object's base. This will help avoid
- // artifacts when we have an object on a cliff/hill casting shadows onto the ground below.
- // We need this hack because the terrain does not cast shadows and looks weird when objects
- // sitting on an incline cast shadows down below.
- Vector3 objPos=m_robj->Get_Position();
- Vector3 lastValidTerrainPoint = objPos;
- Real baseGroundHeight=objPos.Z;
- const AABoxClass &box=m_robj->Get_Bounding_Box();
- Vector3 lightRay,shadowRay;
- LineSegClass lineseg;
- CastResultStruct result;
- Vector3 Corners[4];
- RayCollisionTestClass raytest(lineseg,&result);
- //Get vertices of top of bounding box since they will generate the longest shadow
- Corners[0]=box.Center+box.Extent; //top right corner
- Corners[1]=Corners[0];
- Corners[1].X -= 2.0f*box.Extent.X; //top left corner
- Corners[2]=Corners[1];
- Corners[2].Y -= 2.0f*box.Extent.Y; //bottom left corner
- Corners[3]=Corners[2];
- Corners[3].X += 2.0f*box.Extent.X; //bottom right corner
- //find the corner that causes the longest shadow projection
- //and clamp light position to make sure it falls on even ground about
- //the same height as the object's base.
- for (Int i=0; i<4; i++)
- {
- //Cast ray from top volume corners onto ground plane
- lightRay = Corners[i] - lightPosWorld; //vector light to corner
- lightRay.Normalize();
- raytest.Ray.Set(Corners[i],Corners[i]+lightRay*MAX_EXTRUSION_LENGTH);
- result.Reset();
- //find out where this ray intersects terrain.
- if (TheTerrainRenderObject->Cast_Ray(raytest) && !raytest.Result->StartBad)
- { //Found intersection point where shadow has its maximum length. Do a quick
- //search to see if terrain falls significantly below the height of the object
- //anywhere between the base and the intersection point. If so, we either need
- //to extend shadow extrusion or make the light angle more vertical.
- shadowRay.Set(result.ContactPoint-Corners[i]); //vector from object corner to terrain intersection.
- shadowRay.Z = 0; //remove z-component since we'll be sampling along the xy plane.
- //walk along the shadow/light direction vector looking for large dips - indicating object
- //is on hill or cliff.
- Real len=shadowRay.Length();
- Int numSteps=REAL_TO_INT_CEIL(len/SHADOW_SAMPLING_INTERVAL);
- Real stepSize = 1.0f/(Real)numSteps;
- Vector3 terrainPoint;
- Real t=stepSize;
- for (Int j=0; j<numSteps; j++)
- {
- terrainPoint = Corners[i] + shadowRay*t;
- terrainPoint.Z=0; //ignore height
-
- Real terrainHeight=TheTerrainRenderObject->getHeightMapHeight(terrainPoint.X,terrainPoint.Y,NULL);
- if (terrainHeight < (objPos.Z - MAX_SHADOW_EXTRUSION_UNDER_OBJECT_BEFORE_CLAMP)) //check if terrain dips more than 10 units under object.
- {
- if (j == 0) //this is the initial point so object must be right on the edge of a cliff.
- { baseGroundHeight = terrainHeight; //force extrusion all the way down cliff.
- Real tanAngle=tan(OVERHANGING_OBJECT_CLAMP_ANGLE); //clamp to about 89 degrees or close to vertical lightpos.
- setShadowLengthScale(tanAngle); //update the clamp angle to shorted shadow enough so this corner on flat ground.
- break;
- }
- //Find ray from last valid terrain contact point to object box corner.
- Vector3 clampRay(Corners[i]-lastValidTerrainPoint);
- Real clampAngle=asin(clampRay.Z/clampRay.Length());
- if (clampAngle >= (PI/2.0f) || clampAngle <= 0)
- clampAngle = OVERHANGING_OBJECT_CLAMP_ANGLE; //clamp to about 89 degrees or close to vertical lightpos.
- Real tanAngle=tan(clampAngle);
- if (tanAngle > m_shadowLengthScale)
- setShadowLengthScale(tanAngle); //update the clamp angle to shorted shadow enough so this corner on flat ground.
- break;
- }
- if (terrainHeight < baseGroundHeight)
- { baseGroundHeight = terrainHeight; //point was below object but within safety margin so record it's position.
- lastValidTerrainPoint = terrainPoint;
- lastValidTerrainPoint.Z = baseGroundHeight;
- }
- t+=stepSize;
- }
- }
- }
- m_extraExtrusionPadding = objPos.Z - baseGroundHeight + SHADOW_EXTRUSION_BUFFER;
- DEBUG_ASSERTCRASH(m_extraExtrusionPadding <= (255.0f*MAP_HEIGHT_SCALE),("Warning: Volumetric Shadow UpdateOptimalExtrusionPadding too large"));
- }
- }
- // getRenderCost ============================================================
- // Returns number of draw calls for this shadow.
- // ============================================================================
- #if defined(_DEBUG) || defined(_INTERNAL)
- void W3DVolumetricShadow::getRenderCost(RenderCost & rc) const
- {
- Int drawCount = 0;
- if (m_geometry && m_isEnabled && !m_isInvisibleEnabled && TheGlobalData->m_useShadowVolumes)
- {
- Int i,j;
- HLodClass *hlod=(HLodClass *)m_robj;
- MeshClass *mesh;
- Int meshIndex;
- for( i = 0; i < MAX_SHADOW_LIGHTS; i++ )
- {
- for (j=0; j<m_geometry->getMeshCount(); j++)
- {
- meshIndex=m_geometry->getMesh(j)->m_meshRobjIndex;
- if (meshIndex >= 0)
- mesh = (MeshClass *)hlod->Peek_Lod_Model(0,meshIndex);
- else
- mesh = (MeshClass *)m_robj;
- if (mesh && mesh->Is_Not_Hidden_At_All())
- drawCount++;
- }
- }
- }
- rc.addShadowDrawCalls(drawCount*2);
- }
- #endif
- /************************************ New Buffered Rendering Code ************************/
- void W3DVolumetricShadow::RenderVolume(Int meshIndex, Int lightIndex)
- {
- HLodClass *hlod=(HLodClass *)m_robj;
- MeshClass *mesh=NULL;
- Int meshRobjIndex=m_geometry->getMesh(meshIndex)->m_meshRobjIndex;
- if (meshRobjIndex >= 0)
- mesh = (MeshClass *)hlod->Peek_Lod_Model(0,meshRobjIndex);
- else
- mesh = (MeshClass *)m_robj;
- if (mesh)
- {
- #ifdef SV_DEBUG_BOUNDS
- RenderMeshVolumeBounds(meshIndex,lightIndex, &mesh->Get_Transform());
- #endif
- if (m_shadowVolume[0][ meshIndex ]->GetFlags() & SHADOW_DYNAMIC)
- RenderDynamicMeshVolume(meshIndex,lightIndex,&mesh->Get_Transform());
- else
- RenderMeshVolume(meshIndex,lightIndex,&mesh->Get_Transform());
- }
- }
- void W3DVolumetricShadow::RenderMeshVolume(Int meshIndex, Int lightIndex, const Matrix3D *meshXform)
- {
- Geometry *geometry;
- Int numVerts, numPolys, numIndex;
- //Get D3D Device used by W3D for quicker access.
- LPDIRECT3DDEVICE8 m_pDev=DX8Wrapper::_Get_D3D_Device8();
- if (!m_pDev)
- return;
- geometry = m_shadowVolume[lightIndex][ meshIndex ];
- //
- // if our count is out of sync with our geometry data something
- // is wrong here
- //
- assert( geometry );
- // get geometry requirements
- numVerts = geometry->GetNumActiveVertex();
- numPolys = geometry->GetNumActivePolygon();
- numIndex = numPolys * 3;
- // reject shadows with no data
- if( numVerts == 0 || numPolys == 0 )
- return;
- Matrix4 mWorld(*meshXform);
- ///@todo: W3D always does transpose on all of matrix sets. Slow??? Better to hack view matrix.
- m_pDev->SetTransform(D3DTS_WORLD,(_D3DMATRIX *)&mWorld.Transpose());
-
- W3DBufferManager::W3DVertexBufferSlot *vbSlot=m_shadowVolumeVB[lightIndex][ meshIndex ];
- if (!vbSlot)
- return;
- if (vbSlot->m_VB->m_DX8VertexBuffer->Get_DX8_Vertex_Buffer() != lastActiveVertexBuffer)
- { lastActiveVertexBuffer=vbSlot->m_VB->m_DX8VertexBuffer->Get_DX8_Vertex_Buffer();
- m_pDev->SetStreamSource(0,lastActiveVertexBuffer,
- vbSlot->m_VB->m_DX8VertexBuffer->FVF_Info().Get_FVF_Size()); //12 bytes per vertex.
- }
- DEBUG_ASSERTCRASH(vbSlot->m_size >= numVerts,("Overflowing Shadow Vertex Buffer Slot"));
- W3DBufferManager::W3DIndexBufferSlot *ibSlot=m_shadowVolumeIB[lightIndex][ meshIndex ];
- if (!ibSlot)
- return;
- DEBUG_ASSERTCRASH(ibSlot->m_size >= numIndex,("Overflowing Shadow Index Buffer Slot"));
- m_pDev->SetIndices(ibSlot->m_IB->m_DX8IndexBuffer->Get_DX8_Index_Buffer(),vbSlot->m_start);
- if (DX8Wrapper::_Is_Triangle_Draw_Enabled())
- {
- Debug_Statistics::Record_DX8_Polys_And_Vertices(numPolys,numVerts,ShaderClass::_PresetOpaqueShader);
- m_pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,numVerts,ibSlot->m_start,numPolys);
- }
- }
- void W3DVolumetricShadow::RenderDynamicMeshVolume(Int meshIndex, Int lightIndex, const Matrix3D *meshXform)
- {
- Geometry *geometry;
- Int numVerts, numPolys, numIndex;
- SHADOW_DYNAMIC_VOLUME_VERTEX* pvVertices;
- UnsignedShort *pvIndices;
- //Get D3D Device used by W3D for quicker access.
- LPDIRECT3DDEVICE8 m_pDev=DX8Wrapper::_Get_D3D_Device8();
- if (!m_pDev)
- return;
- geometry = m_shadowVolume[lightIndex][ meshIndex ];
- //
- // if our count is out of sync with our geometry data something
- // is wrong here
- //
- assert( geometry );
- // get geometry requirements
- numVerts = geometry->GetNumActiveVertex();
- numPolys = geometry->GetNumActivePolygon();
- numIndex = numPolys * 3;
- // reject shadows with no data
- if( numVerts == 0 || numPolys == 0 )
- return;
- if (nShadowVertsInBuf > (SHADOW_VERTEX_SIZE-numVerts)) //check if room for model verts
- { //flush the buffer by drawing the contents and re-locking again
- if (shadowVertexBufferD3D->Lock(0,numVerts*sizeof(SHADOW_DYNAMIC_VOLUME_VERTEX),(unsigned char**)&pvVertices,D3DLOCK_DISCARD) != D3D_OK)
- return;
- nShadowVertsInBuf=0;
- nShadowStartBatchVertex=0;
- }
- else
- { if (shadowVertexBufferD3D->Lock(nShadowVertsInBuf*sizeof(SHADOW_DYNAMIC_VOLUME_VERTEX),numVerts*sizeof(SHADOW_DYNAMIC_VOLUME_VERTEX), (unsigned char**)&pvVertices,D3DLOCK_NOOVERWRITE) != D3D_OK)
- return;
- }
- #ifdef SV_DEBUG
- srand(0x1345465);
- #endif
- if(pvVertices)
- {
- #ifdef SV_DEBUG
- for (Int i=0; i<numVerts; i++)
- {
- (*((Vector3 *)pvVertices))=*geometry->GetVertex(i); //cast is valid since both start with xyz
- pvVertices->diffuse=(rand()%255) | ((rand()%255)<<8) | ((rand()%255)<<16);
- pvVertices++;
- }
- #else
- memcpy(pvVertices,geometry->GetVertex(0),numVerts*sizeof(SHADOW_DYNAMIC_VOLUME_VERTEX));
- #endif
- }
- shadowVertexBufferD3D->Unlock();
- if (nShadowIndicesInBuf > (SHADOW_INDEX_SIZE-numIndex)) //check if room for model verts
- { //flush the buffer by drawing the contents and re-locking again
- if (shadowIndexBufferD3D->Lock(0,numIndex*sizeof(short),(unsigned char**)&pvIndices,D3DLOCK_DISCARD) != D3D_OK)
- return;
- nShadowIndicesInBuf=0;
- nShadowStartBatchIndex=0;
- }
- else
- { if (shadowIndexBufferD3D->Lock(nShadowIndicesInBuf*sizeof(short),numIndex*sizeof(short), (unsigned char**)&pvIndices,D3DLOCK_NOOVERWRITE) != D3D_OK)
- return;
- }
- if(pvIndices)
- {
- memcpy(pvIndices,geometry->GetPolygonIndex(0,(short *)pvIndices,3),numPolys*3*sizeof(short));
- }
- shadowIndexBufferD3D->Unlock();
- m_pDev->SetIndices(shadowIndexBufferD3D,nShadowStartBatchVertex);
-
- Matrix4 mWorld(*meshXform);
- m_pDev->SetTransform(D3DTS_WORLD,(_D3DMATRIX *)&mWorld.Transpose());
- if (shadowVertexBufferD3D != lastActiveVertexBuffer)
- { m_pDev->SetStreamSource(0,shadowVertexBufferD3D,sizeof(SHADOW_DYNAMIC_VOLUME_VERTEX));
- lastActiveVertexBuffer = shadowVertexBufferD3D;
- }
- if (DX8Wrapper::_Is_Triangle_Draw_Enabled())
- {
- Debug_Statistics::Record_DX8_Polys_And_Vertices(numPolys,numVerts,ShaderClass::_PresetOpaqueShader);
- m_pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,numVerts,nShadowStartBatchIndex,numPolys);
- }
- nShadowVertsInBuf += numVerts;
- nShadowStartBatchVertex=nShadowVertsInBuf;
- nShadowIndicesInBuf += numIndex;
- nShadowStartBatchIndex=nShadowIndicesInBuf;
- }
- /** Debug function to draw bounding boxes around shadow volumes */
- void W3DVolumetricShadow::RenderMeshVolumeBounds(Int meshIndex, Int lightIndex, const Matrix3D *meshXform)
- {
- Geometry *geometry;
- Int numVerts, numPolys, numIndex;
- SHADOW_DYNAMIC_VOLUME_VERTEX* pvVertices;
- UnsignedShort *pvIndices;
- // Vertex Positions as a function of the box extents
- static Vector3 _BoxVerts[8] =
- {
- Vector3( 1.0f, 1.0f, 1.0f ), // +z ring of 4 verts
- Vector3( -1.0f, 1.0f, 1.0f ),
- Vector3( -1.0f,-1.0f, 1.0f ),
- Vector3( 1.0f,-1.0f, 1.0f ),
- Vector3( 1.0f, 1.0f,-1.0f ), // -z ring of 4 verts;
- Vector3( -1.0f, 1.0f,-1.0f ),
- Vector3( -1.0f,-1.0f,-1.0f ),
- Vector3( 1.0f,-1.0f,-1.0f ),
- };
- // Face Connectivity
- static Vector3i _BoxFaces[12] =
- {
- Vector3i( 0,1,2 ), // +z faces
- Vector3i( 0,2,3 ),
- Vector3i( 4,7,6 ), // -z faces
- Vector3i( 4,6,5 ),
- Vector3i( 0,3,7 ), // +x faces
- Vector3i( 0,7,4 ),
- Vector3i( 1,5,6 ), // -x faces
- Vector3i( 1,6,2 ),
- Vector3i( 4,5,1 ), // +y faces
- Vector3i( 4,1,0 ),
- Vector3i( 3,2,6 ), // -y faces
- Vector3i( 3,6,7 )
- };
- static Vector3 verts[8];
- //Get D3D Device used by W3D for quicker access.
- LPDIRECT3DDEVICE8 m_pDev=DX8Wrapper::_Get_D3D_Device8();
- if (!m_pDev)
- return;
- Vector3 meshPosition;
- meshXform->Get_Translation(&meshPosition); //current mesh position
- geometry = m_shadowVolume[lightIndex][ meshIndex ];
- AABoxClass &aab=geometry->getBoundingBox();
- // compute the vertex positions
- meshPosition += aab.Center; //get world space position of bounding box
- for (int ivert=0; ivert<8; ivert++)
- {
- verts[ivert].X = meshPosition.X + _BoxVerts[ivert][0] * aab.Extent.X;
- verts[ivert].Y = meshPosition.Y + _BoxVerts[ivert][1] * aab.Extent.Y;
- verts[ivert].Z = meshPosition.Z + _BoxVerts[ivert][2] * aab.Extent.Z;
- }
- //
- // if our count is out of sync with our geometry data something
- // is wrong here
- //
- assert( geometry );
- // get geometry requirements
- numVerts = 8;
- numPolys = 12;
- numIndex = numPolys * 3;
- // reject shadows with no data
- if( numVerts == 0 || numPolys == 0 )
- return;
- if (nShadowVertsInBuf > (SHADOW_VERTEX_SIZE-numVerts)) //check if room for model verts
- { //flush the buffer by drawing the contents and re-locking again
- if (shadowVertexBufferD3D->Lock(0,numVerts*sizeof(SHADOW_DYNAMIC_VOLUME_VERTEX),(unsigned char**)&pvVertices,D3DLOCK_DISCARD) != D3D_OK)
- return;
- nShadowVertsInBuf=0;
- nShadowStartBatchVertex=0;
- }
- else
- { if (shadowVertexBufferD3D->Lock(nShadowVertsInBuf*sizeof(SHADOW_DYNAMIC_VOLUME_VERTEX),numVerts*sizeof(SHADOW_DYNAMIC_VOLUME_VERTEX), (unsigned char**)&pvVertices,D3DLOCK_NOOVERWRITE) != D3D_OK)
- return;
- }
- srand(0x1345465);
- if(pvVertices)
- { for (Int i=0; i<8; i++)
- {
- pvVertices->x=verts[i][0];
- pvVertices->y=verts[i][1];
- pvVertices->z=verts[i][2];
- #ifdef SV_DEBUG
- pvVertices->diffuse=(rand()%255) | ((rand()%255)<<8) | ((rand()%255)<<16);
- #endif
- pvVertices++;
- }
- }
- shadowVertexBufferD3D->Unlock();
- if (nShadowIndicesInBuf > (SHADOW_INDEX_SIZE-numIndex)) //check if room for model verts
- { //flush the buffer by drawing the contents and re-locking again
- if (shadowIndexBufferD3D->Lock(0,numIndex*sizeof(short),(unsigned char**)&pvIndices,D3DLOCK_DISCARD) != D3D_OK)
- return;;
- nShadowIndicesInBuf=0;
- nShadowStartBatchIndex=0;
- }
- else
- { if (shadowIndexBufferD3D->Lock(nShadowIndicesInBuf*sizeof(short),numIndex*sizeof(short), (unsigned char**)&pvIndices,D3DLOCK_NOOVERWRITE) != D3D_OK)
- return;
- }
- if(pvIndices)
- {
- for (Int i=0; i<numPolys; i++,pvIndices+=3)
- {
- pvIndices[0] = _BoxFaces[i][0];
- pvIndices[1] = _BoxFaces[i][1];
- pvIndices[2] = _BoxFaces[i][2];
- }
- }
- shadowIndexBufferD3D->Unlock();
- m_pDev->SetIndices(shadowIndexBufferD3D,nShadowStartBatchVertex);
- //todo: replace this with mesh transform
- Matrix4 mWorld(1); //identity since boxes are pre-transformed to world space.
- m_pDev->SetTransform(D3DTS_WORLD,(_D3DMATRIX *)&mWorld.Transpose());
-
- m_pDev->SetStreamSource(0,shadowVertexBufferD3D,sizeof(SHADOW_DYNAMIC_VOLUME_VERTEX));
- m_pDev->SetVertexShader(SHADOW_DYNAMIC_VOLUME_FVF);
- m_pDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,numVerts,nShadowStartBatchIndex,numPolys);
- nShadowVertsInBuf += numVerts;
- nShadowStartBatchVertex=nShadowVertsInBuf;
- nShadowIndicesInBuf += numIndex;
- nShadowStartBatchIndex=nShadowIndicesInBuf;
- }
- // Shadow =====================================================================
- // Shadow default constructor
- // ============================================================================
- W3DVolumetricShadow::W3DVolumetricShadow( void )
- {
- Int i,j;
- m_next = NULL;
- m_geometry = NULL;
- m_shadowLengthScale = 0.0f;
- m_extraExtrusionPadding = 0.0f;
- m_robj = NULL;
- m_isEnabled = TRUE;
- m_isInvisibleEnabled = FALSE;
- for (j=0; j < MAX_SHADOW_CASTER_MESHES; j++)
- { m_numSilhouetteIndices[j] = 0;
- m_maxSilhouetteEntries[j] = 0;
- m_silhouetteIndex[j] = NULL;
- m_shadowVolumeCount[j] = 0;
- }
- for( i = 0; i < MAX_SHADOW_LIGHTS; i++ )
- {
- for (j=0; j < MAX_SHADOW_CASTER_MESHES; j++)
- {
- m_shadowVolume[ i ][j] = NULL;
- m_shadowVolumeVB[i][j] = NULL;
- m_shadowVolumeIB[i][j] = NULL;
- m_shadowVolumeRenderTask[i][j].m_parentShadow = this;
- m_shadowVolumeRenderTask[i][j].m_meshIndex = (UnsignedByte)j;
- m_shadowVolumeRenderTask[i][j].m_lightIndex = (UnsignedByte)i;
- m_objectXformHistory[ i ][j].Make_Identity();
- m_lightPosHistory[ i ][j] = Vector3(0,0,0);
- }
- } // end for i
- } // end W3DVolumetricShadow
- // ~W3DVolumetricShadow ====================================================================
- // W3DVolumetricShadow destructor
- // ============================================================================
- W3DVolumetricShadow::~W3DVolumetricShadow( void )
- {
- Int i,j;
- // we must free any silhouette data allocated
- for (j = 0; j < MAX_SHADOW_CASTER_MESHES; j++)
- deleteSilhouette(j);
- // free any shadow volume data
- for( i = 0; i < MAX_SHADOW_LIGHTS; i++ )
- { for (j = 0; j < MAX_SHADOW_CASTER_MESHES; j++)
- { if( m_shadowVolume[ i ][j] )
- delete m_shadowVolume[ i ][j];
- if( m_shadowVolumeVB[i][j])
- TheW3DBufferManager->releaseSlot(m_shadowVolumeVB[i][j]);
- if( m_shadowVolumeIB[i][j])
- TheW3DBufferManager->releaseSlot(m_shadowVolumeIB[i][j]);
- }
- }
- if (m_geometry)
- REF_PTR_RELEASE(m_geometry);
- m_geometry=NULL;
- m_robj=NULL;
- } // end ~W3DVolumetricShadow
- void W3DVolumetricShadow::SetGeometry( W3DShadowGeometry *geometry )
- {
- Short numPrevVertices = 0;
- Short numNewVertices = 0;
- //
- // our geometry has changed, we need to allocate enough memory for the
- // silhouette data. If silhouette data is present it must be reallocated
- // to accomoddate the new size if smaller
- //
- // if we had previous geometry how many vertices did it have
- for (Int i=0; i<MAX_SHADOW_CASTER_MESHES; i++)
- {
- if( m_geometry )
- numPrevVertices = m_geometry->getMesh(i)->GetNumVertex();
- // now many vertices does our new geometry have
- if( geometry )
- numNewVertices = geometry->getMesh(i)->GetNumVertex();
- //
- // TODO: Colin, may want to change this in the future
- // if our new geometry requires more memory allocate it, if it requires
- // less we'll leave it around for future switches in geometry
- //
- if( numNewVertices > numPrevVertices )
- {
- deleteSilhouette(i);
- if( allocateSilhouette(i, numNewVertices ) == FALSE )
- return;
- } // end if
- }
- // assign the new geometry, possible over an old geometry
- m_geometry = geometry;
- } // end SetGeometry
- /**Called once per frame for each object, when necessary it will reconstruct
- the shadow volume for this shadow from the silhouette of the geometry
- and any light sources
- */
- void W3DVolumetricShadow::Update()
- {
- static Int currentTime, lastTime, delay = 0;
- // OBJECT_PILE
- // static Vector3 originCompareVector(0,0,9999);
- static Vector3 originCompareVector(0,0,0);
- Vector3 pos;
- // sanity
- if( m_geometry == NULL)
- return;
- //
- // for now we will just rebuild a shadow volume every so often, this
- // should be changed to be built only when the light angle sufficiently
- // changes or when the object rotation sufficiently changes
- //
- // currentTime = timeGetTime();
- // if( currentTime - lastTime >= delay )
- {
- pos=m_robj->Get_Position();
- if (pos == originCompareVector)
- { //the transform on this object was never set so we can't make any determination
- //if it's visible or what the shadow looks like.
- return;
- }
- //Check if this is a "flying" unit. Flying units are defined as anything that moves more than
- //AIRBORNE_UNIT_GROUND_DELTA world units above ground. This will allow "jumping" units like rocket
- //buggies from being flagged as "flying". We minimize the number of flying units because their shadow
- //volumes are extruded longer to reach the lowest point on the map. Regular shadows only extend far
- //enough to reach the base of the model (presumes base is already touching the ground).
- Real groundHeight;
- if (TheTerrainLogic)
- groundHeight=TheTerrainLogic->getGroundHeight(pos.X,pos.Y); //logic knows about bridges so use if available.
- else
- groundHeight=TheTerrainRenderObject->getHeightMapHeight(pos.X,pos.Y, NULL);
- if (fabs(pos.Z - groundHeight) >= AIRBORNE_UNIT_GROUND_DELTA)
- {
- Real extent = MAX_SHADOW_LENGTH_EXTRA_AIRBORNE_SCALE_FACTOR * m_robjExtent;
- if (WWMath::Fabs(pos.X - bcX) > (beX + extent) ||
- WWMath::Fabs(pos.Y - bcY) > (beY + extent) ||
- WWMath::Fabs(pos.Z - bcZ) > (beZ + extent))
- return; //shadow can't be visible so no point in updating.
-
- //this unit is above ground, extend shadow volume to reach lowest point on the terrain plus extra bit to make
- //sure shadow goes under ground.
- updateVolumes(fabs(pos.Z - TheTerrainRenderObject->getMinHeight()) + SHADOW_EXTRUSION_BUFFER);
- }
- else
- { //normal object that is not floating above ground so we don't need to extend the shadow lower than the object's
- //base since it should be sitting directly at ground level.
- if (WWMath::Fabs(pos.X - bcX) > (beX + m_robjExtent) ||
- WWMath::Fabs(pos.Y - bcY) > (beY + m_robjExtent) ||
- WWMath::Fabs(pos.Z - bcZ) > (beZ + m_robjExtent))
- return; //shadow can't be visible so no point in updating.
-
- //check if this object has never had it's extrusion length updated. Will only be true for
- //immobile objects because finding an optimal extrusion length is expensive.
- if (!m_extraExtrusionPadding)
- updateOptimalExtrusionPadding();
- updateVolumes(m_extraExtrusionPadding);
- }
- // floorZ = 2.0f; //lower slightly so shadows go under ground.
-
- // update delay time
- lastTime = currentTime;
- } // end if
- } // end Update
- /** Update shadow volumes belonging to all meshes of this shadow caster.
- * Use zoffset to extend shadows below object's base by given amount.
- */
- void W3DVolumetricShadow::updateVolumes(Real zoffset)
- {
- Int i,j;
- HLodClass *hlod=(HLodClass *)m_robj;
- MeshClass *mesh;
- static AABoxClass aaBox;
- static SphereClass sphere;
- Int meshIndex;
- DEBUG_ASSERTCRASH(hlod != NULL,("updateVolumes : hlod is NULL!"));
- Bool parentVis=m_robj->Is_Really_Visible();
- for( i = 0; i < MAX_SHADOW_LIGHTS; i++ )
- {
- for (j=0; j<m_geometry->getMeshCount(); j++)
- {
- meshIndex=m_geometry->getMesh(j)->m_meshRobjIndex;
- if (meshIndex >= 0)
- mesh = (MeshClass *)hlod->Peek_Lod_Model(0,meshIndex);
- else
- mesh = (MeshClass *)m_robj;
- if (mesh)
- {
- if (!mesh->Is_Not_Hidden_At_All())
- continue;
- /**@todo: Getting the transform of the mesh may be forcing a full hierarchy evaluation.
- Expensive for off-screen models... do we really need this? */
- //Extend floor of model by 'zoffset' to compensate for flying units.
- updateMeshVolume(j, i, &mesh->Get_Transform(), mesh->Get_Bounding_Box(),m_robj->Get_Position().Z - zoffset);
- //update visibility if not set yet
- if (m_shadowVolume[i][j])
- {
- if(m_shadowVolume[i][j]->getVisibleState() == Geometry::STATE_UNKNOWN)
- { //Updating the mesh volume didn't update the visibility, must do it here.
- //First check against bounding sphere
- if (parentVis)
- { //parent is visible so most likely all sub_objects are also visible so probably all shadows also visible.
- //skip additional visibility tests
- m_shadowVolume[i][j]->setVisibleState(Geometry::STATE_VISIBLE);
- }
- else
- { sphere=m_shadowVolume[i][j]->getBoundingSphere();
- sphere.Center += mesh->Get_Transform().Get_Translation();
- CollisionMath::OverlapType result=CollisionMath::Overlap_Test(*shadowCameraFrustum,sphere);
- if (result == CollisionMath::OVERLAPPED)
- { //do a more accurate test against bounding box.
- aaBox=m_shadowVolume[i][j]->getBoundingBox();
- aaBox.Translate(mesh->Get_Transform().Get_Translation()); //translate bounding box to world space.
- if (CollisionMath::Overlap_Test(*shadowCameraFrustum,aaBox) != CollisionMath::OUTSIDE)
- m_shadowVolume[i][j]->setVisibleState(Geometry::STATE_VISIBLE);
- else
- m_shadowVolume[i][j]->setVisibleState(Geometry::STATE_INVISIBLE);
- }
- else
- m_shadowVolume[i][j]->setVisibleState((Geometry::VisibleState)result);
- }
- }
- if (m_shadowVolume[i][j]->getVisibleState() == Geometry::STATE_VISIBLE)
- { //shadow volume is visible. Add it to list of rendertasks.
- W3DBufferManager::W3DVertexBufferSlot *vbSlot=m_shadowVolumeVB[i][j];
- if (vbSlot)
- { //add to static mesh volume list.
- W3DBufferManager::W3DRenderTask *oldTask=vbSlot->m_VB->m_renderTaskList;
- vbSlot->m_VB->m_renderTaskList=&m_shadowVolumeRenderTask[i][j];
- vbSlot->m_VB->m_renderTaskList->m_nextTask=oldTask;
- }
- else
- {
- TheW3DVolumetricShadowManager->addDynamicShadowTask(&m_shadowVolumeRenderTask[i][j]);
- }
- }
- }
- }
- } // end for j
- } // end for, i
- }
- /*floorZ is the assumed ground height below the model. The code will try to extrude shadows just long enough to hit this point in order
- to reduce fill rate usage.*/
- void W3DVolumetricShadow::updateMeshVolume(Int meshIndex, Int lightIndex, const Matrix3D *meshXform, const AABoxClass &meshBox, float floorZ )
- {
- Vector3 lightPosObject;
- Matrix4 worldToObject;
- Vector3 objectCenter;
- Vector3 toLight;
- Vector3 toPrevLight;
- Vector3 prevPosToLight;
- Vector3 lightPosWorld;
- Vector3 prevObjPosition;
- //Figuring out if mesh has rotated is cheaper (no normalization) than figuring out if light angle has changed.
- //So we divide the 2 tests. Also, our light (sun) almost never moves so no second test needed at all.
- Bool isMeshRotating = false; //flag if mesh has rotated since last update. Translation doesn't matter for infinite light source.
- Bool isLightMoving = false; //flag if light has moved since last update.
- Matrix4 objectToWorld(*meshXform);
- Matrix4 *prevXForm=&m_objectXformHistory[ lightIndex ][meshIndex];
- //
- // build the shadow silhouette and construct shadow volume from
- // this light location. The for loop wrapped around this is
- // theoretical code for future enhancements of multiple lights that
- // cast shadows
- //
- #ifdef ASSUME_NEAR_LIGHTSOURCE
- if (memcmp(&objectToWorld,prevXForm,sizeof(objectToWorld)))
- isMeshRotating = true; //mesh transform has not changed since last update.
- #else
- //When dealing with infinite light sources, we can assume that the shadow doesn't
- //change much based on object position. Only the orientation to light matters.
- Real cosAngle = fabs (Vector3::Dot_Product((Vector3 &)(prevXForm->operator [](0)),(Vector3 &)(objectToWorld.operator [](0))));
- if (cosAngle >= cosAngleToCare)
- { cosAngle = fabs (Vector3::Dot_Product((Vector3 &)(prevXForm->operator [](1)),(Vector3 &)(objectToWorld.operator [](1))));
- if (cosAngle >= cosAngleToCare)
- {
- cosAngle = fabs (Vector3::Dot_Product((Vector3 &)(prevXForm->operator [](2)),(Vector3 &)(objectToWorld.operator [](2))));
- if (cosAngle < cosAngleToCare)
- isMeshRotating=true;
- }
- else
- isMeshRotating =true;
- }
- else
- isMeshRotating =true;
- #endif
- // get the light
- lightPosWorld = TheW3DShadowManager->getLightPosWorld(lightIndex);
- // get the object
- meshXform->Get_Translation(&objectCenter); //current mesh position
- // check if object has a limit/clamp on shadow length and adjust light
- // position of necessary.
- if (m_shadowLengthScale)
- { //Find light's distance from origin in xy plane
- Real lightXYDistance = sqrt(lightPosWorld.X*lightPosWorld.X + lightPosWorld.Y * lightPosWorld.Y);
- Real newZ=lightXYDistance*m_shadowLengthScale;
- if (newZ > lightPosWorld.Z)
- { //clamped z component is higher than actual light position allows so adjust it.
- lightPosWorld.Z = newZ;
- }
- }
- if (lightPosWorld != m_lightPosHistory[ lightIndex ][meshIndex])
- { //Light position has moved, see if enough to matter
- // compute vector from the light to the current object position
- toLight = objectCenter - lightPosWorld;
- toLight.Normalize();
- // compute vector from the previous light to the object position
- toPrevLight = objectCenter - m_lightPosHistory[ lightIndex ][meshIndex];
- toPrevLight.Normalize();
- Real cosAngle = fabs (Vector3::Dot_Product(toLight,toPrevLight));
- if (cosAngle < cosAngleToCare) //less than 45 degree change
- isLightMoving =true;
- }
- else
- ///@todo: Find a better way to deal with this - use maximum extrusion once! Also avoid hit for units climbing hills.
- if (fabs(objectCenter.Z - prevXForm->operator [](2).W) > SHADOW_EXTRUSION_BUFFER)
- isLightMoving = true; //treat model rising just like rotation since volume needs update for longer extrusion.
- // reconstruct if needed
- if (isLightMoving || isMeshRotating)
- {
- //
- // transform the light in the world to object space, we
- // care only about the rotation of components for the coordinate
- // system change, not the translations
- //
- Real det;
- D3DXMatrixInverse((D3DXMATRIX*)&worldToObject, &det, (D3DXMATRIX*)&objectToWorld);
- // find out light position in object space
- Matrix4::Transform_Vector(worldToObject,lightPosWorld,&lightPosObject);
- //Updating shadow volumes is expensive, so verify that this volume is even visible.
- //Generate bounding box around shadow volume by extruding AABB corners
- AABoxClass box(meshBox); //copy current mesh bounding box (will be smaller than shadow box).
- SphereClass sphere; //rough bounding sphere of shadow volume - based on box.
- Vector3 Corners[8];
- Vector3 lightRay;
- Real vectorScale,vectorScaleTemp, vectorScaleMax;
- Real length;
- //Get vertices of top of bounding box
- Corners[0]=box.Center+box.Extent; //top right corner
- Corners[1]=Corners[0];
- Corners[1].X -= 2.0f*box.Extent.X; //top left corner
- Corners[2]=Corners[1];
- Corners[2].Y -= 2.0f*box.Extent.Y; //bottom left corner
- Corners[3]=Corners[2];
- Corners[3].X += 2.0f*box.Extent.X; //bottom right corner
- //Project top volume corners onto ground plane
- lightRay = Corners[0] - lightPosWorld; //vector light to corner
- length= 1.0f/lightRay.Length();
- lightRay *= length;
- vectorScaleMax=vectorScale=(Real)fabs((Corners[0].Z-floorZ)/lightRay.Z); //length of vector from top corner to ground.
- Corners[4]=Corners[0]+lightRay*vectorScale;
- vectorScaleMax *= length;
- lightRay = Corners[1] - lightPosWorld; //vector light to corner
- length= 1.0f/lightRay.Length();
- lightRay *= length;
- vectorScaleTemp=(Real)fabs((Corners[1].Z-floorZ)/lightRay.Z); //length of vector from top corner to ground.
- Corners[5]=Corners[1]+lightRay*vectorScaleTemp;
- vectorScaleTemp *= length;
- if (vectorScaleTemp > vectorScaleMax)
- vectorScaleMax=vectorScaleTemp; //keep track of maximum required extrusion length.
- lightRay = Corners[2] - lightPosWorld; //vector light to corner
- length= 1.0f/lightRay.Length();
- lightRay *= length;
- vectorScale=(Real)fabs((Corners[2].Z-floorZ)/lightRay.Z); //length of vector from top corner to ground.
- Corners[6]=Corners[2]+lightRay*vectorScale;
- vectorScale *= length;
- if (vectorScale > vectorScaleMax)
- vectorScaleMax=vectorScale; //keep track of maximum required extrusion length.
- lightRay = Corners[3] - lightPosWorld; //vector light to corner
- length= 1.0f/lightRay.Length();
- lightRay *= length;
- vectorScaleTemp=(Real)fabs((Corners[3].Z-floorZ)/lightRay.Z); //length of vector from top corner to ground.
- Corners[7]=Corners[3]+lightRay*vectorScaleTemp;
- vectorScaleTemp *= length;
- if (vectorScaleTemp > vectorScaleMax)
- vectorScaleMax=vectorScaleTemp; //keep track of maximum required extrusion length.
- box.Init(Corners, 8); //generate a new bounding box
- sphere.Init(box.Center,box.Extent.Length()); //generate object space bounding sphere containing box.
- CollisionMath::OverlapType result=CollisionMath::Overlap_Test(*shadowCameraFrustum,sphere);
- if (result == CollisionMath::OVERLAPPED) //do a more accurate test
- result=CollisionMath::Overlap_Test(*shadowCameraFrustum, box);
-
- if (result != CollisionMath::OUTSIDE)
- {
- //
- // reset the silhouette data and build a new one from this light
- // source perspective
- //
- if (m_numSilhouetteIndices[meshIndex] != 0)
- { //this silhouette was built before and is being updated.
- //this probably means it will change again in the future.
- //make future updates faster by pre-caching face normals.
- m_geometry->getMesh(meshIndex)->buildPolygonNormals();
- }
- resetSilhouette(meshIndex);
- buildSilhouette(meshIndex, &lightPosObject);
- //
- // in a multiple shadow situation we would be allocating a volume
- // for this current shadow light, not the 0 index volume all the time
- //
- if (!m_shadowVolume[ lightIndex ][meshIndex])
- allocateShadowVolume( lightIndex,meshIndex );
- if( m_shadowVolumeVB[ lightIndex ][meshIndex] )
- { //Updating an existing vertex buffer shadow volume. This means we're
- //probably dealing with an animated mesh. Update flags to reflect this fact.
- if (isMeshRotating || isLightMoving)
- {
- if (isMeshRotating)
- { //rotating meshes will most likely need updates each frame, so stop using static vertex buffers.
- m_shadowVolume[ lightIndex ][meshIndex]->SetFlags(
- m_shadowVolume[ lightIndex ][meshIndex]->GetFlags() | SHADOW_DYNAMIC);
- }
- //release memory used to store vertices/polygons
- resetShadowVolume( lightIndex,meshIndex ); //free vertex buffers since not used for dynamic.
- //Resize the shadow volume since we'll need room to store the vertices in memory instead of VB.
- allocateShadowVolume( lightIndex,meshIndex );
- }
- }
- //
- // construct the shadow volume at this light position in the
- // passed shadow volume geometry index
- //
- if (m_shadowVolume[ lightIndex ][meshIndex]->GetFlags() & SHADOW_DYNAMIC)
- constructVolume( &lightPosObject, vectorScaleMax, lightIndex, meshIndex );
- else
- constructVolumeVB( &lightPosObject, vectorScaleMax, lightIndex, meshIndex );
- //
- // store the current light position and orientation that
- // we constructed shadow info at
- //
- m_objectXformHistory[ lightIndex ][meshIndex] = objectToWorld;
- m_lightPosHistory[lightIndex][meshIndex] = lightPosWorld;
- box.Translate(-objectCenter); //translate box to object space.
- m_shadowVolume[ lightIndex ][meshIndex]->setBoundingBox(box);
- sphere.Center -= objectCenter;
- m_shadowVolume[ lightIndex ][meshIndex]->setBoundingSphere(sphere);
- m_shadowVolume[ lightIndex ][meshIndex]->setVisibleState(Geometry::STATE_VISIBLE); //this volume needs rendering.
- }//end if inside view frustum
- else
- if (m_shadowVolume[ lightIndex ][meshIndex])
- { //outside view frustum, shadow wasn't updated.
- box.Translate(-objectCenter); //translate box to object space.
- m_shadowVolume[ lightIndex ][meshIndex]->setBoundingBox(box);
- sphere.Center -= objectCenter;
- m_shadowVolume[ lightIndex ][meshIndex]->setBoundingSphere(sphere);
- m_shadowVolume[ lightIndex ][meshIndex]->setVisibleState(Geometry::STATE_INVISIBLE);
- }
- } // end if
- else
- { //not reconstructing volume, so don't know if visible or not.
- if (m_shadowVolume[ lightIndex ][meshIndex])
- m_shadowVolume[ lightIndex ][meshIndex]->setVisibleState(Geometry::STATE_UNKNOWN);
- }
- }
- // addSilhouetteEdge ==========================================================
- // It has been determined that the polygon neighbor in the "neighborIndex"
- // of "visible" needs to be added to the silhouette. We will add those two
- // vertex indices to the silhouette in the order they were specified in
- // "visible" to assure that the constructed edge is in counter clockwise order
- // ============================================================================
- void W3DVolumetricShadow::addSilhouetteEdge(Int meshIndex, PolyNeighbor *visible, PolyNeighbor *hidden )
- {
- Int i;
- Int neighborIndex = 0;
- Short visibleIndexList[ 3 ];
- Short edgeStart, edgeEnd;
- W3DShadowGeometryMesh *geomMesh=m_geometry->getMesh(meshIndex);
- // sanity
- assert( visible && hidden );
- //
- // which index in the neighbor list of "visible" refers to the
- // polygon "hidden"
- //
- for( i = 0; i < MAX_POLYGON_NEIGHBORS; i++ )
- {
- if( visible->neighbor[ i ].neighborIndex == hidden->myIndex )
- {
- neighborIndex = i;
- break; // exit for
- } // end if
- } // end for i
- // get the three vertex indices of "visible"
- geomMesh->GetPolygonIndex( visible->myIndex, visibleIndexList, 3 );
- //
- // we know that 2 of the 3 vertex indices will be present in the edge.
- // will construct the edge as follows to ensure we have counter
- // clockwise order. note that this assumes the vertices of the
- // polygons specified in the geometry are in counter clockwise order,
- // which they are
- //
- // 1) [ v1 Absent, v2 Present, v3 Present ] -> edge = (v2, v3)
- // 2) [ v1 Present, v2 Absent, v3 Present ] -> edge = (v3, v1)
- // 3) [ v1 Present, v2 Present, v3 Absent ] -> edge = (v1, v2)
- //
- if( (visibleIndexList[ 0 ] !=
- visible->neighbor[ neighborIndex ].neighborEdgeIndex[ 0 ]) &&
- (visibleIndexList[ 0 ] !=
- visible->neighbor[ neighborIndex ].neighborEdgeIndex[ 1 ]) )
- {
- // case 1 above
- edgeStart = visibleIndexList[ 1 ];
- edgeEnd = visibleIndexList[ 2 ];
- } // end if
- else if( (visibleIndexList[ 1 ] !=
- visible->neighbor[ neighborIndex ].neighborEdgeIndex[ 0 ]) &&
- (visibleIndexList[ 1 ] !=
- visible->neighbor[ neighborIndex ].neighborEdgeIndex[ 1 ]) )
- {
- // case 2 above
- edgeStart = visibleIndexList[ 2 ];
- edgeEnd = visibleIndexList[ 0 ];
- } // end if
- else
- {
- // case 3 above
- edgeStart = visibleIndexList[ 0 ];
- edgeEnd = visibleIndexList[ 1 ];
- } // end if
- // add to silhouette edge list
- addSilhouetteIndices(meshIndex, edgeStart, edgeEnd );
- } // end addSilhouetteEdge
- // addNeighborlessEdges =======================================================
- // Given a polygon neighbor information, it has been determined that this
- // polygon is visible and has edges which are not connected to other
- // polygons, these edges need to be added to the silhouette. The edge(s)
- // must be added in such an order that we create silhouette edges in a
- // counter clockwise order.
- // ============================================================================
- void W3DVolumetricShadow::addNeighborlessEdges(Int meshIndex, PolyNeighbor *us )
- {
- Short vertexIndexList[ 3 ];
- Int i, j;
- Short edgeStart, edgeEnd;
- Bool addEdge;
- // sanity
- assert( us );
- W3DShadowGeometryMesh *geomMesh = m_geometry->getMesh(meshIndex);
- // get the vertex index list from the geometry
- geomMesh->GetPolygonIndex( us->myIndex, vertexIndexList, 3 );
- //
- // go through each edge, if these indices to NOT appear TOGETHER in
- // neighbor list then we must add it.
- //
- for( i = 0; i < 3; i++ )
- {
- // get the edge start and end vertex indices
- edgeStart = vertexIndexList[ i ];
- if( i == 2 )
- edgeEnd = vertexIndexList[ 0 ]; // wraps to begging of list
- else
- edgeEnd = vertexIndexList[ i + 1 ];
- // do these two vertices appear in a neighbor list of the poly?
- addEdge = TRUE;
- for( j = 0; j < MAX_POLYGON_NEIGHBORS; j++ )
- {
- if( us->neighbor[ j ].neighborIndex != NO_NEIGHBOR )
- {
- if( (us->neighbor[ j ].neighborEdgeIndex[ 0 ] == edgeStart &&
- us->neighbor[ j ].neighborEdgeIndex[ 1 ] == edgeEnd) ||
- (us->neighbor[ j ].neighborEdgeIndex[ 1 ] == edgeStart &&
- us->neighbor[ j ].neighborEdgeIndex[ 0 ] == edgeEnd) )
- {
- addEdge = FALSE;
- break; // exit for j, no need to search on
- } // end if
- } // end if
- } // end for j
- // add the edge if no neighbors have that edge
- if( addEdge == TRUE )
- {
- addSilhouetteIndices(meshIndex, edgeStart, edgeEnd );
- } // end if
- } // end for i
- } // end addNeighborlessEdges
- // addSilhouetteIndices =======================================================
- // Add these two indices to the silhouette data
- // ============================================================================
- void W3DVolumetricShadow::addSilhouetteIndices(Int meshIndex, Short edgeStart, Short edgeEnd )
- {
- // DBGPRINTF(( "addSilhouetteIndices: Adding (%d,%d), the storage before the add is = (%d/%d)\n",
- // edgeStart, edgeEnd, m_numSilhouetteIndices, m_maxSilhouetteEntries ));
- // add to silhouette edge list
- assert( m_numSilhouetteIndices[meshIndex] < m_maxSilhouetteEntries[meshIndex] );
- m_silhouetteIndex[meshIndex][ m_numSilhouetteIndices[meshIndex]++ ] = edgeStart;
- assert( m_numSilhouetteIndices[meshIndex] < m_maxSilhouetteEntries[meshIndex] );
- m_silhouetteIndex[meshIndex][ m_numSilhouetteIndices[meshIndex]++ ] = edgeEnd;
- } // end if
- // buildSilhouette ============================================================
- // Given a light position, and our polygon neighbor information this will
- // build the silhouette of the object edges from the given light position
- // ============================================================================
- void W3DVolumetricShadow::buildSilhouette(Int meshIndex, Vector3 *lightPosObject)
- {
- PolyNeighbor *polyNeighbor; // the poly we're looking at right now
- Vector3 normal; // normal of current polygon
- Vector3 lightVector; // vector from light to polygon
- Bool visibleNeighborless;
- Int numPolys; // number of polys in our geometry
- W3DShadowGeometryMesh *geomMesh;
- Int i, j;
- Int meshEdgeStart=0; //index to first edge contributed by specific mesh
- //
- // go through each of our shadow geometry polygon info and find out
- // which polys are visible from this light source and which ones are not
- //
- geomMesh = m_geometry->getMesh(meshIndex);
- //record where this meshes indices will begin.
- meshEdgeStart=m_numSilhouetteIndices[meshIndex];
- numPolys = geomMesh->GetNumPolygon();
- for( i = 0; i < numPolys; i++ )
- {
- Short poly[ 3 ];
- Vector3 vertex;
- // get this polygon neighbor information
- polyNeighbor = geomMesh->GetPolyNeighbor( i );
- // take this opportunity to initialize our processing flags to zero
- polyNeighbor->status = 0;
- // get the normal for this polygon
- geomMesh->GetPolygonNormal( i, &normal );
- // get the vertex indices at this polygon
- geomMesh->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
- //
- geomMesh->GetVertex( poly[ 0 ], &vertex );
- lightVector= vertex - *lightPosObject;
- //
- // 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 )
- BitSet( polyNeighbor->status, POLY_VISIBLE );
- } // end for i
- //
- // check all our polys using our poly neighbors, where one poly neighbor
- // is not the same visible status as a neighbor that represents a
- // silhouette edge
- //
- for( i = 0; i < numPolys; i++ )
- {
- PolyNeighbor *otherNeighbor;
- // get this poly neighbor ... this is "us"
- polyNeighbor = geomMesh->GetPolyNeighbor( i );
- // initialize ourselves to not be a visible edge
- visibleNeighborless = FALSE;
- // check our 3 potential neighbors
- for( j = 0; j < MAX_POLYGON_NEIGHBORS; j++ )
- {
- // initialize this neighbor to nuttin
- otherNeighbor = NULL;
- // get our neighbor if present and cull them if processed
- if( polyNeighbor->neighbor[ j ].neighborIndex != NO_NEIGHBOR )
- {
- // get the jth polygon neighbor ... this is "them"
- otherNeighbor =
- geomMesh->GetPolyNeighbor(
- polyNeighbor->neighbor[ j ].neighborIndex );
- //
- // ignore neighbors that are marked as processed as those
- // onces have already detected edges if present
- //
- if( BitTest( otherNeighbor->status, POLY_PROCESSED ) )
- continue; // for j
- } // end if
- //
- // finally, if our own visible status is different from our
- // neighbor visible status then that defines an edge we must
- // add to the silhouette. Also, a visible polygon that has
- // no neighbor automatically makes a silhouette edge. Note that
- // if we have no neighbor we just record the fact that we have
- // real model end edges to add after this inner j loop;
- //
- if( BitTest( polyNeighbor->status, POLY_VISIBLE ) )
- {
- // check for no neighbor edges
- if( otherNeighbor == NULL )
- {
- visibleNeighborless = TRUE;
- } // end if
- else if( BitTest( otherNeighbor->status, POLY_VISIBLE ) == FALSE )
- {
- // "we" are visible and "they" are not
- addSilhouetteEdge(meshIndex, polyNeighbor, otherNeighbor );
- } // end if
- } // end if
- else if( otherNeighbor != NULL &&
- BitTest( otherNeighbor->status, POLY_VISIBLE ) )
- {
- // "they" are visible and "we" are not
- addSilhouetteEdge(meshIndex, otherNeighbor, polyNeighbor );
- } // end else
- } // end for j
- //
- // if this polygon is visible, add any edges that are not
- // neighbors of adjacent polygons.
- //
- if( visibleNeighborless == TRUE )
- {
- addNeighborlessEdges(meshIndex, polyNeighbor );
- } // end if
- //
- // this polyNeighbor is now considered "processed", any other
- // polygons that reference back to this one can ignore their
- // processing cause any edges were already detected
- //
- BitSet( polyNeighbor->status, POLY_PROCESSED );
- } // end for i
-
- //record number of edge indices contrinuted by this mesh
- m_numIndicesPerMesh[meshIndex]=m_numSilhouetteIndices[meshIndex]-meshEdgeStart;
-
- } // end buildSilhouette
- // constructVolume ============================================================
- // Given a fresh new geometry class called "shadowVolume" to hold the actual
- // shadow volume data, this method will create the shadow volume polygons
- // given the information in the current silhouette of this Shadow and the
- // light source position.
- //
- // The light source should be in object space and the shadow volume polygon
- // data is also constructed in object space.
- //
- // The polygon we will create for a given edge will be that edge extruded
- // out in the direction away from the light source. This conceptual 4 sided
- // polygon is however broken up into two triangles for storage.
- //
- // This version is designed to construct the volume inside a system memory
- // buffer - to be rendered via a dynamic vertex buffer.
- //
- // ============================================================================
- void W3DVolumetricShadow::constructVolume( Vector3 *lightPosObject,Real shadowExtrudeDistance, Int volumeIndex, Int meshIndex )
- {
- Geometry *shadowVolume;
- Vector3 extrude2; // the polypoints extruded from edge and light
- Vector3 edgeVertex2; // second edge of silhouette
- Short indexList[ 3 ];
- Int i,k;
- Int vertexCount;
- Int polygonCount;
- Int indicesPerMesh;
- W3DShadowGeometryMesh *geomMesh;
- // sanity
- if( volumeIndex < 0 ||
- volumeIndex >= MAX_SHADOW_LIGHTS ||
- lightPosObject == NULL )
- {
- assert( 0 );
- return;
- } // end if
- // get the geometry struct we're storing the actual shadow volume data in
- shadowVolume = m_shadowVolume[ volumeIndex ][meshIndex];
- if( shadowVolume == NULL )
- {
- // DBGPRINTF(( "No volume allocated at index '%d'\n", volumeIndex ));
- assert( 0 );
- return;
- } // end if
- // step through each of the silhouette pairs
- vertexCount = 0;
- polygonCount = 0;
- indicesPerMesh=m_numIndicesPerMesh[meshIndex];
- if (!indicesPerMesh)
- return; //nothing to draw
- geomMesh = m_geometry->getMesh(meshIndex);
- shadowVolume->SetNumActivePolygon(0);
- shadowVolume->SetNumActiveVertex(0);
- #ifdef RECORD_SHADOW_STRIP_STATS
- Int numStrips=0; //keeps track of number of strips generated.
- Int stripLength=1; //keeps track of segments in strip (each being 2 triangles).
- Int maxStripLength=0; //keeps track of longest strip generated.
- #endif
- Short *silhouetteIndices=m_silhouetteIndex[meshIndex];
- //Initialize first strip info
- Short stripStartIndex=silhouetteIndices[ 0 ];
- Short stripStartVertex=0;
- //Insert the first vertex and extrusion into strip.
- // get edge point
- geomMesh->GetVertex( silhouetteIndices[ 0 ], &edgeVertex2 );
- // take one edge point and extrude away from the light
- extrude2 = edgeVertex2 - *lightPosObject;
- extrude2 *= shadowExtrudeDistance;
- extrude2 += edgeVertex2;
- shadowVolume->SetVertex( vertexCount, &edgeVertex2 );
- shadowVolume->SetVertex( vertexCount + 1, &extrude2 );
- vertexCount=2;
- Int lastEdgeVertex2Index=0;
- Int lastExtrude2Index=1;
- for( i = 0; i < indicesPerMesh; i += 2 )
- {
- Short currentEdgeEnd=silhouetteIndices[i+1];
- //look for edge connected to this one and move it next to this edge
- //in index list. Rendering edges back-to-back improves vertex cache usage.
- for (k=i+2; k<indicesPerMesh; k+=2)
- if (silhouetteIndices[k]==currentEdgeEnd)
- { //swap the two edges
- Int tempIndex=*(Int *)(&silhouetteIndices[i+2]);
- *(Int *)&silhouetteIndices[i+2]=*(Int *)&silhouetteIndices[k];
- *(Int *)&silhouetteIndices[k]=tempIndex;
- break;
- }
- if (k >= indicesPerMesh)
- { //reached end of strip. Insert final edge.
- //Check if last edge wraps around to start.
- if (currentEdgeEnd == stripStartIndex)
- { //add end of strip that wraps around to start (forming closed cylinder/shape)
- //
- // add the polygon consisting of the two edge vertices and the
- // first extruded point
- //
- indexList[ 0 ] = lastEdgeVertex2Index; // lastedgeVertex2 index
- indexList[ 1 ] = lastExtrude2Index; // lastextrude2 index
- indexList[ 2 ] = stripStartVertex; // edgeVertex2 index
- shadowVolume->SetPolygonIndex( polygonCount, indexList, 3 );
- indexList[ 0 ] = stripStartVertex; // edgeVertex2 index
- indexList[ 1 ] = lastExtrude2Index; // extrude1 index
- indexList[ 2 ] = stripStartVertex+1; // extrude2 index
- shadowVolume->SetPolygonIndex( polygonCount + 1, indexList, 3 );
- }
- else
- { //add end of strip. Finishes the last 2 polygons.
- geomMesh->GetVertex( currentEdgeEnd, &edgeVertex2 );
- shadowVolume->SetVertex( vertexCount, &edgeVertex2 );
- //
- // add the polygon consisting of the two edge vertices and the
- // first extruded point
- //
- indexList[ 0 ] = lastEdgeVertex2Index; // lastedgeVertex2 index
- indexList[ 1 ] = lastExtrude2Index; // lastextrude2 index
- indexList[ 2 ] = vertexCount; // edgeVertex2 index
- shadowVolume->SetPolygonIndex( polygonCount, indexList, 3 );
- // take the other edge point and extrude away from light
- extrude2 = edgeVertex2 - *lightPosObject;
- extrude2 *= shadowExtrudeDistance;
- extrude2 += edgeVertex2;
- // add the one new vertex
- shadowVolume->SetVertex( vertexCount + 1, &extrude2 );
- indexList[ 0 ] = vertexCount; // edgeVertex2 index
- indexList[ 1 ] = lastExtrude2Index; // extrude1 index
- indexList[ 2 ] = vertexCount+1; // extrude2 index
- shadowVolume->SetPolygonIndex( polygonCount + 1, indexList, 3 );
- lastEdgeVertex2Index=vertexCount;
- lastExtrude2Index=vertexCount+1;
- vertexCount += 2;
- }
- if ((i+2) >= indicesPerMesh)
- { //finished with all silhouette edges
- polygonCount += 2;
- #ifdef RECORD_SHADOW_STRIP_STATS
- numStrips++;
- #endif
- break; //reached end of all edges
- }
-
- //Start a new strip by adding first vertex and extrusion.
- geomMesh->GetVertex( silhouetteIndices[ i+2 ], &edgeVertex2 );
- // take one edge point and extrude away from the light
- extrude2 = edgeVertex2 - *lightPosObject;
- extrude2 *= shadowExtrudeDistance;
- extrude2 += edgeVertex2;
- lastEdgeVertex2Index=vertexCount;
- lastExtrude2Index=vertexCount + 1;
- //record start of new strip info
- stripStartIndex=silhouetteIndices[ i+2 ];
- stripStartVertex=lastEdgeVertex2Index;
- shadowVolume->SetVertex( lastEdgeVertex2Index, &edgeVertex2 );
- shadowVolume->SetVertex( lastExtrude2Index, &extrude2 );
- vertexCount += 2;
- polygonCount += 2;
- #ifdef RECORD_SHADOW_STRIP_STATS
- numStrips++;
- stripLength=1;
- #endif
- continue;
- }
- else
- { //continue existing strip by adding extra vertex and extrusion
- geomMesh->GetVertex( currentEdgeEnd, &edgeVertex2 );
- shadowVolume->SetVertex( vertexCount, &edgeVertex2 );
- //
- // add the polygon consisting of the two edge vertices and the
- // first extruded point
- //
- indexList[ 0 ] = lastEdgeVertex2Index; // lastedgeVertex2 index
- indexList[ 1 ] = lastExtrude2Index; // lastextrude2 index
- indexList[ 2 ] = vertexCount; // edgeVertex2 index
- shadowVolume->SetPolygonIndex( polygonCount, indexList, 3 );
- // take the other edge point and extrude away from light
- extrude2 = edgeVertex2 - *lightPosObject;
- extrude2 *= shadowExtrudeDistance;
- extrude2 += edgeVertex2;
- // add the one new vertex
- shadowVolume->SetVertex( vertexCount + 1, &extrude2 );
- indexList[ 0 ] = vertexCount; // edgeVertex2 index
- indexList[ 1 ] = lastExtrude2Index; // extrude1 index
- indexList[ 2 ] = vertexCount+1; // extrude2 index
- shadowVolume->SetPolygonIndex( polygonCount + 1, indexList, 3 );
- lastEdgeVertex2Index=vertexCount;
- lastExtrude2Index=vertexCount+1;
- vertexCount += 2;
- polygonCount += 2;
- }
- #ifdef RECORD_SHADOW_STRIP_STATS
- //Continuing strip.
- stripLength++;
- maxStripLength=__max(maxStripLength,stripLength);
- #endif
- }
- shadowVolume->SetNumActivePolygon(polygonCount);
- shadowVolume->SetNumActiveVertex(vertexCount);
- } // end constructVolume
- // constructVolumeVB ==========================================================
- // Given a fresh new geometry class called "shadowVolume" to hold the actual
- // shadow volume data, this method will create the shadow volume polygons
- // given the information in the current silhouette of this Shadow and the
- // light source position.
- //
- // The light source should be in object space and the shadow volume polygon
- // data is also constructed in object space.
- //
- // The polygon we will create for a given edge will be that edge extruded
- // out in the direction away from the light source. This conceptual 4 sided
- // polygon is however broken up into two triangles for storage.
- //
- // This version is designed to construct the volume directly inside a vertex
- // buffer so it's optimal for static geometry. Since it's assumed to be called
- // only once per model, we can use some more expensive computations to generate
- // the volume.
- //
- // ============================================================================
- void W3DVolumetricShadow::constructVolumeVB( Vector3 *lightPosObject,Real shadowExtrudeDistance, Int volumeIndex, Int meshIndex )
- {
- Geometry *shadowVolume;
- Vector3 extrude2; // the polypoints extruded from edge and light
- Vector3 edgeVertex2; // second edge of silhouette
- Int i,k;
- Int vertexCount;
- Int polygonCount;
- Int indicesPerMesh;
- W3DShadowGeometryMesh *geomMesh;
- W3DBufferManager::W3DVertexBufferSlot *vbSlot;
- W3DBufferManager::W3DIndexBufferSlot *ibSlot;
- // sanity
- if( volumeIndex < 0 ||
- volumeIndex >= MAX_SHADOW_LIGHTS ||
- lightPosObject == NULL )
- {
- assert( 0 );
- return;
- } // end if
- // get the geometry struct we're storing the actual shadow volume data in
- shadowVolume = m_shadowVolume[ volumeIndex ][meshIndex];
- if( shadowVolume == NULL )
- {
- // DBGPRINTF(( "No volume allocated at index '%d'\n", volumeIndex ));
- assert( 0 );
- return;
- } // end if
- //*****************************************************************************************/
- //Do an initial pass through silhouette data to determine the actual vertex/polygon counts.
- //This number can't be determined any other way since it depends on degree of vertex sharing
- //in model. We don't want to overallocate because vertex buffer space is limited.
- //This pass is also used to sort the edges so they are all connected in strip order.
- {
- #ifdef RECORD_SHADOW_STRIP_STATS
- Int numStrips=0; //keeps track of number of strips generated.
- Int stripLength=1; //keeps track of segments in strip (each being 2 triangles).
- Int maxStripLength=0; //keeps track of longest strip generated.
- #endif
- // step through each of the silhouette pairs
- vertexCount = 0;
- polygonCount = 0;
- indicesPerMesh=m_numIndicesPerMesh[meshIndex];
- if (!indicesPerMesh)
- return; //nothing to draw
- Short *silhouetteIndices=m_silhouetteIndex[meshIndex];
- //Initialize first strip info
- Short stripStartIndex=silhouetteIndices[ 0 ];
- Short stripStartVertex=0;
- vertexCount=2;
- Int lastEdgeVertex2Index=0;
- Int lastExtrude2Index=1;
- for( i = 0; i < indicesPerMesh; i += 2 )
- {
- Short currentEdgeEnd=silhouetteIndices[i+1];
- //look for edge connected to this one and move it next to this edge
- //in index list. Rendering edges back-to-back improves vertex cache usage.
- for (k=i+2; k<indicesPerMesh; k+=2)
- if (silhouetteIndices[k]==currentEdgeEnd)
- { //swap the two edges
- Int tempIndex=*(Int *)(&silhouetteIndices[i+2]);
- *(Int *)&silhouetteIndices[i+2]=*(Int *)&silhouetteIndices[k];
- *(Int *)&silhouetteIndices[k]=tempIndex;
- break;
- }
- if (k >= indicesPerMesh)
- { //reached end of strip. Insert final edge.
- //Check if last edge wraps around to start.
- if (currentEdgeEnd == stripStartIndex)
- { //add end of strip that wraps around to start (forming closed cylinder/shape)
- //
- // add the polygon consisting of the two edge vertices and the
- // first extruded point
- //
- }
- else
- { //add end of strip. Finishes the last 2 polygons.
- lastEdgeVertex2Index=vertexCount;
- lastExtrude2Index=vertexCount+1;
- vertexCount += 2;
- }
- if ((i+2) >= indicesPerMesh)
- { //finished with all silhouette edges
- polygonCount += 2;
- #ifdef RECORD_SHADOW_STRIP_STATS
- numStrips++;
- #endif
- break; //reached end of all edges
- }
-
- lastEdgeVertex2Index=vertexCount;
- lastExtrude2Index=vertexCount + 1;
- //record start of new strip info
- stripStartIndex=silhouetteIndices[ i+2 ];
- stripStartVertex=lastEdgeVertex2Index;
- vertexCount += 2;
- polygonCount += 2;
- #ifdef RECORD_SHADOW_STRIP_STATS
- numStrips++;
- stripLength=1;
- #endif
- continue;
- }
- else
- { //continue existing strip by adding extra vertex and extrusion
- lastEdgeVertex2Index=vertexCount;
- lastExtrude2Index=vertexCount+1;
- vertexCount += 2;
- polygonCount += 2;
- }
- #ifdef RECORD_SHADOW_STRIP_STATS
- //Continuing strip.
- stripLength++;
- maxStripLength=__max(maxStripLength,stripLength);
- #endif
- }
- } //initial pass to determine vertex/polygon counts.
- //***********************************************************************************************
- DEBUG_ASSERTCRASH(m_shadowVolumeVB[ volumeIndex ][meshIndex] == NULL,("Updating Existing Static Vertex Buffer Shadow"));
- vbSlot=m_shadowVolumeVB[ volumeIndex ][meshIndex] = TheW3DBufferManager->getSlot(W3DBufferManager::VBM_FVF_XYZ,
- vertexCount);
- DEBUG_ASSERTCRASH(vbSlot != NULL, ("Can't allocate vertex buffer slot for shadow volume"));
- DEBUG_ASSERTCRASH(vbSlot->m_size >= vertexCount,("Overflowing Shadow Vertex Buffer Slot"));
- DEBUG_ASSERTCRASH(m_shadowVolume[ volumeIndex ][meshIndex]->GetNumPolygon() == 0,("Updating Existing Static Shadow Volume"));
- DEBUG_ASSERTCRASH(m_shadowVolumeIB[ volumeIndex ][meshIndex] == NULL,("Updating Existing Static Index Buffer Shadow"));
- ibSlot=m_shadowVolumeIB[ volumeIndex ][meshIndex] = TheW3DBufferManager->getSlot(polygonCount*3);
- DEBUG_ASSERTCRASH(ibSlot != NULL, ("Can't allocate index buffer slot for shadow volume"));
- DEBUG_ASSERTCRASH(ibSlot->m_size >= (polygonCount*3),("Overflowing Shadow Index Buffer Slot"));
- if (!ibSlot || !vbSlot)
- { //could not allocate storage to hold buffers
- if (ibSlot)
- TheW3DBufferManager->releaseSlot(ibSlot);
- if (vbSlot)
- TheW3DBufferManager->releaseSlot(vbSlot);
- m_shadowVolumeIB[ volumeIndex ][meshIndex]=NULL;
- m_shadowVolumeVB[ volumeIndex ][meshIndex]=NULL;
- return;
- }
- geomMesh = m_geometry->getMesh(meshIndex);
- DX8VertexBufferClass::AppendLockClass lockVtxBuffer(vbSlot->m_VB->m_DX8VertexBuffer,vbSlot->m_start,vertexCount);
- VertexFormatXYZ *vb = (VertexFormatXYZ*)lockVtxBuffer.Get_Vertex_Array();
- if (vb == NULL)
- return;
- DX8IndexBufferClass::AppendLockClass lockIdxBuffer(ibSlot->m_IB->m_DX8IndexBuffer,ibSlot->m_start,polygonCount*3);
- UnsignedShort *ib = (UnsignedShort*)lockIdxBuffer.Get_Index_Array();
- if (ib == NULL)
- return;
- shadowVolume->SetNumActivePolygon(polygonCount);
- shadowVolume->SetNumActiveVertex(vertexCount);
- Short *silhouetteIndices=m_silhouetteIndex[meshIndex];
- //Initialize first strip info
- Short stripStartIndex=silhouetteIndices[ 0 ];
- Short stripStartVertex=0;
- //Insert the first vertex and extrusion into strip.
- // get edge point
- geomMesh->GetVertex( silhouetteIndices[ 0 ], &edgeVertex2 );
- // take one edge point and extrude away from the light
- extrude2 = edgeVertex2 - *lightPosObject;
- extrude2 *= shadowExtrudeDistance;
- extrude2 += edgeVertex2;
- *vb++ = *(VertexFormatXYZ *)&edgeVertex2;
- *vb++ = *(VertexFormatXYZ *)&extrude2;
- vertexCount=2;
- polygonCount=0;
- Int lastEdgeVertex2Index=0;
- Int lastExtrude2Index=1;
- for( i = 0; i < indicesPerMesh; i += 2 )
- {
- Short currentEdgeEnd=silhouetteIndices[i+1];
- //Check if another edge is connected to this one, edges were sorted in initial
- //pass so only need to check the next one.
- if (((i+2) >= indicesPerMesh) || silhouetteIndices[i+2] != currentEdgeEnd)
- { //reached end of strip. Insert final edge.
- //Check if last edge wraps around to start.
- if (currentEdgeEnd == stripStartIndex)
- { //add end of strip that wraps around to start (forming closed cylinder/shape)
- //
- // add the polygon consisting of the two edge vertices and the
- // first extruded point
- //
- ib[ 0 ] = lastEdgeVertex2Index; // lastedgeVertex2 index
- ib[ 4 ] = ib[ 1 ] = lastExtrude2Index; // lastextrude2 index
- ib[ 3 ] = ib[ 2 ] = stripStartVertex; // edgeVertex2 index
- ib[ 5 ] = stripStartVertex+1; // extrude2 index
- ib += 6; //skip past 2 triangles just added.
- }
- else
- { //add end of strip. Finishes the last 2 polygons.
- geomMesh->GetVertex( currentEdgeEnd, &edgeVertex2 );
- *vb++ = *(VertexFormatXYZ *)&edgeVertex2;
- //
- // add the polygon consisting of the two edge vertices and the
- // first extruded point
- //
- ib[ 0 ] = lastEdgeVertex2Index; // lastedgeVertex2 index
- ib[ 4 ] = ib[ 1 ] = lastExtrude2Index; // lastextrude2 index
- ib[ 3 ] = ib[ 2 ] = vertexCount; // edgeVertex2 index
- ib[ 5 ] = vertexCount+1; // extrude2 index
- ib += 6; //skip past 2 triangles just added.
- // take the other edge point and extrude away from light
- extrude2 = edgeVertex2 - *lightPosObject;
- extrude2 *= shadowExtrudeDistance;
- extrude2 += edgeVertex2;
- // add the one new vertex
- *vb++ = *(VertexFormatXYZ *)&extrude2;
- lastEdgeVertex2Index=vertexCount;
- lastExtrude2Index=vertexCount+1;
- vertexCount += 2;
- }
- if ((i+2) >= indicesPerMesh)
- { //finished with all silhouette edges
- polygonCount += 2;
- break; //reached end of all edges
- }
-
- //Start a new strip by adding first vertex and extrusion.
- geomMesh->GetVertex( silhouetteIndices[ i+2 ], &edgeVertex2 );
- // take one edge point and extrude away from the light
- extrude2 = edgeVertex2 - *lightPosObject;
- extrude2 *= shadowExtrudeDistance;
- extrude2 += edgeVertex2;
- lastEdgeVertex2Index=vertexCount;
- lastExtrude2Index=vertexCount + 1;
- //record start of new strip info
- stripStartIndex=silhouetteIndices[ i+2 ];
- stripStartVertex=lastEdgeVertex2Index;
- *vb++ = *(VertexFormatXYZ *)&edgeVertex2;
- *vb++ = *(VertexFormatXYZ *)&extrude2;
- vertexCount += 2;
- polygonCount += 2;
- continue;
- }
- else
- { //continue existing strip by adding extra vertex and extrusion
- geomMesh->GetVertex( currentEdgeEnd, &edgeVertex2 );
- *vb++ = *(VertexFormatXYZ *)&edgeVertex2;
- //
- // add the polygon consisting of the two edge vertices and the
- // first extruded point
- //
- ib[ 0 ] = lastEdgeVertex2Index; // lastedgeVertex2 index
- ib[ 4 ] = ib[ 1 ] = lastExtrude2Index; // lastextrude2 index
- ib[ 3 ] = ib[ 2 ] = vertexCount; // edgeVertex2 index
- ib[ 5 ] = vertexCount+1; // extrude2 index
- ib += 6; //skip past 2 triangles just added
- // take the other edge point and extrude away from light
- extrude2 = edgeVertex2 - *lightPosObject;
- extrude2 *= shadowExtrudeDistance;
- extrude2 += edgeVertex2;
- // add the one new vertex
- *vb++ = *(VertexFormatXYZ *)&extrude2;
-
- lastEdgeVertex2Index=vertexCount;
- lastExtrude2Index=vertexCount+1;
- vertexCount += 2;
- polygonCount += 2;
- }
- }
- // DEBUG_ASSERTLOG(polygonCount == vertexCount, ("WARNING***Shadow volume mesh not optimal: %s\n",m_geometry->Get_Name()));
- } // end constructVolume
- // allocateShadowVolume =======================================================
- // Allocate a space for us to construct the shadow volume in
- // ============================================================================
- Bool W3DVolumetricShadow::allocateShadowVolume( Int volumeIndex, Int meshIndex )
- {
- Int numVertices, numPolygons;
- Geometry *shadowVolume;
- // sanity
- if( volumeIndex < 0 || volumeIndex >= MAX_SHADOW_LIGHTS )
- {
- // DBGPRINTF(( "Illegal allocate shadow volume index '%d'\n", volumeIndex ));
- assert( 0 );
- return FALSE;
- } // end if
- if ((shadowVolume = m_shadowVolume[ volumeIndex ][meshIndex]) == 0)
- {
- // poolify
- shadowVolume = NEW Geometry; // create the new geometry
- // we now have one more valid geometry volume
- m_shadowVolumeCount[meshIndex]++;
- }
- if( shadowVolume == NULL )
- {
- // DBGPRINTF(( "Unable to allocate '%d' shadow volume\n", volumeIndex ));
- assert( 0 );
- // we now have one more valid geometry volume
- m_shadowVolumeCount[meshIndex]--;
- return FALSE;
- } // end if
- // assign to list
- m_shadowVolume[ volumeIndex ][meshIndex] = shadowVolume;
- //
- // polygons are determined from the edges extruded from the light.
- // since we have a list of disjoint edge pairs we will have a 4 sided
- // polygon for each edge pair, however we will be splitting the
- // 4 sided polys into 2 triangles so it just works out that num polys
- // is the number of silhouette indices
- //
- numPolygons = m_maxSilhouetteEntries[meshIndex];
- //
- // vertices are an extrusion of the edges, and since we have disjoint
- // pairs of edge indices we will have num indices * 2 actual vertex
- // points. it may be a good future optimization to not duplicate
- // any vertices that appear twice here, but then again the cost over
- // optimizing this routine over the rendering and transformation
- // optimization may not be worth it
- //
- numVertices = m_maxSilhouetteEntries[meshIndex] * 2;
- //Only allocate space here for dynamic shadows. Shadows for static/non-animated
- //models will be stored in vertex buffers which are allocated once exact size
- //is known.
- if (shadowVolume->GetFlags() & SHADOW_DYNAMIC)
- {
- //for dynamic shadow casters, we need to allocate the maximum amount of vertices that could ever be required.
- // if (m_shadowVolumeVB[ volumeIndex ][meshIndex])
- // TheW3DBufferManager->releaseSlot(m_shadowVolumeVB[ volumeIndex ][meshIndex]);
- // m_shadowVolumeVB[ volumeIndex ][meshIndex] = TheW3DVertexBufferManager->getSlot(W3DVertexBufferManager::VBM_FVF_XYZ, numVertices);
- // allocate memory for the vertices and polygons
- if( shadowVolume->Create( numVertices, numPolygons ) == FALSE )
- {
- // DBGPRINTF(( "Unable to create shadow volume\n" ));
- assert( 0 );
- delete shadowVolume;
- return FALSE;
- } // end if
- }
- return TRUE; // success
- } // end allocateShadowVolume
- // deleteShadowVolume =========================================================
- // Free all resources allocated to the shadow volume(s)
- // ============================================================================
- void W3DVolumetricShadow::deleteShadowVolume( Int volumeIndex )
- {
- // sanity
- if( volumeIndex < 0 || volumeIndex >= MAX_SHADOW_LIGHTS )
- {
- // DBGPRINTF(( "Illegal delete shadow volume index '%d'\n", volumeIndex ));
- assert( 0 );
- return;
- } // end if
- // delete it!
- for (Int meshIndex=0; meshIndex<MAX_SHADOW_CASTER_MESHES; meshIndex++)
- {
- if( m_shadowVolume[ volumeIndex ][meshIndex] )
- {
- delete m_shadowVolume[ volumeIndex ][meshIndex];
- m_shadowVolume[ volumeIndex ][meshIndex] = NULL;
- // we now have one less shadow volume
- m_shadowVolumeCount[meshIndex]--;
- } // end if
- }
- } // end deleteShadowVolume
- // resetShadowVolume ==========================================================
- // Reset the contents of the shadow volume information. Since we're using
- // a geometry class it would be ideal if these structures had a reset
- // option where their resoures were released back to a pool rather than
- // delete and allocate new storage space
- // ============================================================================
- void W3DVolumetricShadow::resetShadowVolume( Int volumeIndex, Int meshIndex )
- {
- Geometry *geometry;
- // sanity
- if( volumeIndex < 0 || volumeIndex >= MAX_SHADOW_LIGHTS )
- {
- // DBGPRINTF(( "Illegal reset shadow volume index '%d'\n", volumeIndex ));
- assert( 0 );
- return;
- } // end if
- geometry = m_shadowVolume[ volumeIndex ][meshIndex];
- //Release buffers used to hold shadow volume geometry
- if (geometry)
- { if (m_shadowVolumeVB[volumeIndex][meshIndex])
- { TheW3DBufferManager->releaseSlot(m_shadowVolumeVB[volumeIndex][meshIndex]);
- m_shadowVolumeVB[volumeIndex][meshIndex]=NULL;
- }
- if (m_shadowVolumeIB[ volumeIndex ][meshIndex])
- { TheW3DBufferManager->releaseSlot(m_shadowVolumeIB[volumeIndex][meshIndex]);
- m_shadowVolumeIB[volumeIndex][meshIndex]=NULL;
- }
- geometry->Release();
- }
- } // end resetShadowVolume
- // allocateSilhouette =========================================================
- // Allocate space for new silhouette storage, the number of vertices passed
- // in is the total vertices in the model, a silhouette must be able to
- // accomodate that as a series of disjoint edge pairs, otherwise known
- // as numVertices * 2
- // ============================================================================
- Bool W3DVolumetricShadow::allocateSilhouette(Int meshIndex, Int numVertices )
- {
- Int numEntries = numVertices * 5; ///@todo: HACK, HACK... Should be 2!
- // sanity
- assert( m_silhouetteIndex[meshIndex] == NULL &&
- m_numSilhouetteIndices[meshIndex] == 0 &&
- numEntries > 0 );
- // allocate memory
- m_silhouetteIndex[meshIndex] = NEW short[ numEntries ];
- if( m_silhouetteIndex[meshIndex] == NULL )
- {
-
- // DBGPRINTF(( "Unable to allcoate silhouette storage '%d'\n", numEntries ));
- assert( 0 );
- return FALSE;
- } // end if
-
- // set our list to empty just to be clean
- m_numSilhouetteIndices[meshIndex] = 0;
-
- // save the size of our silhouette list
- m_maxSilhouetteEntries[meshIndex] = numEntries;
- return TRUE; // success
- } // end allocateSilhouette
- // deleteSilhouette ===========================================================
- // Delete all silhouette data and memory allocated
- // ============================================================================
- void W3DVolumetricShadow::deleteSilhouette( Int meshIndex )
- {
- if( m_silhouetteIndex[meshIndex])
- delete [] m_silhouetteIndex[meshIndex];
- m_silhouetteIndex[meshIndex] = NULL;
- m_numSilhouetteIndices[meshIndex] = 0;
- } // end deletesilhouette
- // resetSilhouette ============================================================
- // Resets the silhouette to empty, it does NOT free any of the memory
- // allocated for silhouette data
- // ============================================================================
- void W3DVolumetricShadow::resetSilhouette( Int meshIndex )
- {
- m_numSilhouetteIndices[meshIndex] = 0;
- } // end resetSilhouette
- // renderStencilShadows =======================================================
- // The stencil buffer now has our shadow information in it, take that
- // info and draw a big transparent rectangle over the screen for the final
- // shadow pass wherever there is data in the stencil buffer
- // ============================================================================
- void W3DVolumetricShadowManager::renderStencilShadows( void )
- {
- LPDIRECT3DDEVICE8 m_pDev=DX8Wrapper::_Get_D3D_Device8();
- if (!m_pDev)
- return; //need device to render anything.
- struct _TRANSLITVERTEX {
- D3DXVECTOR4 p;
- DWORD color; // diffuse color
- } v[4];
- Int xpos, ypos, width, height;
- TheTacticalView->getOrigin(&xpos,&ypos);
- width=TheTacticalView->getWidth();
- height=TheTacticalView->getHeight();
- v[0].p = D3DXVECTOR4( xpos+width, ypos+height, 0.0f, 1.0f );
- v[1].p = D3DXVECTOR4( xpos+width, 0, 0.0f, 1.0f );
- v[2].p = D3DXVECTOR4( xpos, ypos+height, 0.0f, 1.0f );
- v[3].p = D3DXVECTOR4( xpos, 0, 0.0f, 1.0f );
- v[0].color = TheW3DShadowManager->getShadowColor();
- v[1].color = TheW3DShadowManager->getShadowColor();
- v[2].color = TheW3DShadowManager->getShadowColor();
- v[3].color = TheW3DShadowManager->getShadowColor();
- //draw polygons like this is very inefficient but for only 2 triangles, it's
- //not worth bothering with index/vertex buffers.
- m_pDev->SetVertexShader(D3DFVF_XYZRHW | D3DFVF_DIFFUSE);
- // Use alpha blending to draw the transparent shadow
- m_pDev->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
- // m_pDev->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
- // m_pDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
- m_pDev->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR);
- m_pDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ZERO );
- // Set stencil states
- m_pDev->SetRenderState( D3DRS_ZENABLE, TRUE );
- m_pDev->SetRenderState(D3DRS_ZFUNC, D3DCMP_ALWAYS);
- // Only write where stencil val >= 1 (count indicates # of shadows that
- // overlap that pixel)
- m_pDev->SetRenderState( D3DRS_STENCILENABLE, TRUE );
- m_pDev->SetRenderState( D3DRS_STENCILFUNC, D3DCMP_LESSEQUAL ); //reference value is less or equal to stencil
- m_pDev->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_KEEP );
- //Upper bits of stencil could be used for storing occluded models which are player colored. So we mask out those
- //pixels and only use the lower bits for shadow calculations.
- m_pDev->SetRenderState( D3DRS_STENCILMASK, ~TheW3DShadowManager->getStencilShadowMask());
- m_pDev->SetRenderState( D3DRS_STENCILREF, 0x1 );
- m_pDev->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
- if (DX8Wrapper::_Is_Triangle_Draw_Enabled())
- m_pDev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v, sizeof(_TRANSLITVERTEX));
- m_pDev->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
- m_pDev->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
- // turn off the stencil buffer
- m_pDev->SetRenderState( D3DRS_STENCILENABLE, FALSE );
- } // end renderStencilShadows
- void W3DVolumetricShadowManager::renderShadows( Bool forceStencilFill )
- {
- W3DVolumetricShadow *shadow;
- Int numRenderedShadows = 0;
- AABoxClass bbox;
- SphereClass bsphere;
-
- //Get a bounding box around our visible universe. Bounded by terrain and the sky
- //so much tighter fitting volume than what's actually visible. This will cull
- //particles falling under the ground.
-
- TheTerrainRenderObject->getMaximumVisibleBox(*shadowCameraFrustum, &bbox, TRUE);
-
- bcX = bbox.Center.X;
- bcY = bbox.Center.Y;
- bcZ = bbox.Center.Z;
- beX = bbox.Extent.X;
- beY = bbox.Extent.Y;
- beZ = bbox.Extent.Z;
- if (m_shadowList && TheGlobalData->m_useShadowVolumes)
- {
- LPDIRECT3DDEVICE8 m_pDev=DX8Wrapper::_Get_D3D_Device8();
- if (!m_pDev)
- return; //need device to render anything.
- //According to Nvidia there's a D3D bug that happens if you don't start with a
- //new dynamic VB each frame - so we force a DISCARD by overflowing the counter.
- nShadowIndicesInBuf = 0xffff;
- nShadowVertsInBuf = 0xffff;
- //Set W3D to some known state
- VertexMaterialClass *vmat=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
- DX8Wrapper::Set_Material(vmat);
- REF_PTR_RELEASE(vmat);
- DX8Wrapper::Set_Shader(ShaderClass::_PresetOpaqueShader);
- DX8Wrapper::Set_Texture(0,NULL); //turn off textures
- DX8Wrapper::Set_Texture(1,NULL); //turn off textures
- DX8Wrapper::Apply_Render_State_Changes(); //force update of view and projection matrices
- // turn off z writing
- m_pDev->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
- m_pDev->SetRenderState( D3DRS_ZENABLE, TRUE );
- m_pDev->SetRenderState(D3DRS_ZWRITEENABLE , FALSE);
- m_pDev->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
- m_pDev->SetRenderState(D3DRS_FOGENABLE, FALSE);
- // setup the TMU to default
- m_pDev->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
- m_pDev->SetRenderState(D3DRS_LIGHTING, FALSE);
- m_pDev->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
- m_pDev->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
- m_pDev->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG2);
- m_pDev->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
- m_pDev->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 );
- m_pDev->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_DISABLE);
- m_pDev->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
- m_pDev->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 1 );
- m_pDev->SetTexture(0,NULL);
- m_pDev->SetTexture(1,NULL);
- DWORD oldColorWriteEnable=0x12345678;
- #ifdef SV_DEBUG
- m_pDev->SetRenderState(D3DRS_ALPHABLENDENABLE , TRUE);
- m_pDev->SetRenderState( D3DRS_STENCILENABLE, FALSE );
- m_pDev->SetRenderState( D3DRS_SRCBLEND, /*D3DBLEND_DESTCOLOR*/D3DBLEND_ONE );
- m_pDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ZERO );
- m_pDev->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
- #else
- //disable writes to color buffer
- if (DX8Caps::Get_Default_Caps().PrimitiveMiscCaps & D3DPMISCCAPS_COLORWRITEENABLE)
- { DX8Wrapper::_Get_D3D_Device8()->GetRenderState(D3DRS_COLORWRITEENABLE, &oldColorWriteEnable);
- DX8Wrapper::Set_DX8_Render_State(D3DRS_COLORWRITEENABLE,0);
- }
- else
- { //device does not support disabling writes to color buffer so fake it through alpha blending
- m_pDev->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ZERO );
- m_pDev->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE );
- m_pDev->SetRenderState(D3DRS_ALPHABLENDENABLE , TRUE);
- }
- m_pDev->SetRenderState( D3DRS_STENCILENABLE, TRUE );
- #endif
- //Any pixels with stencil already set to 128 contains a potential occluder. If this pixels also has any of the player
- //color stencil bits also set, it means that it's an occluded player color and we need to NOT render shadows here. We
- //do this determination by comparing the value in the combined bits against a value containing only a potential occluder.
- //If the value of just the potential occluder bit is >= than the combined bits, then we know none of the player color
- //bits were set and it's okay to render shadow.
- if (TheW3DShadowManager->getStencilShadowMask() == 0x80808080)
- m_pDev->SetRenderState( D3DRS_STENCILFUNC, D3DCMP_NOTEQUAL ); //in this mode, MSB indicates occluded player pixels.
- else
- m_pDev->SetRenderState( D3DRS_STENCILFUNC, D3DCMP_GREATEREQUAL ); //in this mode, multiple bits indicate occluded player pixels.
- m_pDev->SetRenderState( D3DRS_STENCILREF, 0x80808080 ); //isolate MSB, it's used to indicate pixels containing potential occluders.
- m_pDev->SetRenderState( D3DRS_STENCILMASK, TheW3DShadowManager->getStencilShadowMask()); //isolate upper bits containing PotentialOccluderBit|PlayerColorBits
- m_pDev->SetRenderState( D3DRS_STENCILWRITEMASK,0xffffffff );
- m_pDev->SetRenderState( D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP );
- m_pDev->SetRenderState( D3DRS_STENCILFAIL, D3DSTENCILOP_KEEP );
- m_pDev->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_INCR );
-
- m_pDev->SetVertexShader(SHADOW_DYNAMIC_VOLUME_FVF);
- m_pDev->SetRenderState(D3DRS_CULLMODE,D3DCULL_CW);
- // m_pDev->SetRenderState(D3DRS_ZBIAS,1); ///@todo: See if this helps or makes things worse.
- //m_pDev->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);
- lastActiveVertexBuffer=NULL; //reset
- m_dynamicShadowVolumesToRender=NULL; //clear list of pending dynamic shadows
- W3DVolumetricShadowRenderTask *shadowDynamicTasksStart,*shadowDynamicTask;
-
- // step through each of our shadows and render
- for( shadow = m_shadowList; shadow; shadow = shadow->m_next )
- {
- if (shadow->m_isEnabled && !shadow->m_isInvisibleEnabled)
- {
- //Record last added task
- shadowDynamicTasksStart=m_dynamicShadowVolumesToRender;
- shadow->Update();
- shadowDynamicTask=m_dynamicShadowVolumesToRender;
- while (shadowDynamicTask != shadowDynamicTasksStart)
- { //update() added a dynamic shadow
- //dynamic shadow columes don't need to wait in queue since they
- //all use the same vertex buffer. Flush them ASAP.
- shadow->RenderVolume(shadowDynamicTask->m_meshIndex,shadowDynamicTask->m_lightIndex);
- //move to next dynamic task
- shadowDynamicTask=(W3DVolumetricShadowRenderTask *)shadowDynamicTask->m_nextTask;
- numRenderedShadows++;
- }
- }
- } // end for
- // Set vertex format to that used by static shadow volumes
- m_pDev->SetVertexShader(W3DBufferManager::getDX8Format(W3DBufferManager::VBM_FVF_XYZ));
- //Empty queue of static shadow volumes to render.
- W3DBufferManager::W3DVertexBuffer *nextVb;
- W3DVolumetricShadowRenderTask *nextTask;
- for (nextVb=TheW3DBufferManager->getNextVertexBuffer(NULL,W3DBufferManager::VBM_FVF_XYZ);nextVb != NULL; nextVb=TheW3DBufferManager->getNextVertexBuffer(nextVb,W3DBufferManager::VBM_FVF_XYZ))
- {
- nextTask=(W3DVolumetricShadowRenderTask *)nextVb->m_renderTaskList;
- while (nextTask)
- {
- nextTask->m_parentShadow->RenderVolume(nextTask->m_meshIndex,nextTask->m_lightIndex);
- nextTask=(W3DVolumetricShadowRenderTask *)nextTask->m_nextTask;
- numRenderedShadows++;
- }
- }
- // change the stencil op to decrement
- m_pDev->SetRenderState( D3DRS_STENCILPASS, D3DSTENCILOP_DECRSAT);
- //
- // invert normals of shadow volumes so we can decrement in the
- // stencil buffer and render
- //
- m_pDev->SetRenderState(D3DRS_CULLMODE,D3DCULL_CCW);
- for (nextVb=TheW3DBufferManager->getNextVertexBuffer(NULL,W3DBufferManager::VBM_FVF_XYZ);nextVb != NULL; nextVb=TheW3DBufferManager->getNextVertexBuffer(nextVb,W3DBufferManager::VBM_FVF_XYZ))
- {
- nextTask=(W3DVolumetricShadowRenderTask *)nextVb->m_renderTaskList;
- while (nextTask)
- {
- nextTask->m_parentShadow->RenderVolume(nextTask->m_meshIndex,nextTask->m_lightIndex);
- nextTask=(W3DVolumetricShadowRenderTask *)nextTask->m_nextTask;
- }
- }
- m_pDev->SetVertexShader(SHADOW_DYNAMIC_VOLUME_FVF);
- //flush any dynamic shadow volumes
- shadowDynamicTask=m_dynamicShadowVolumesToRender;
- while (shadowDynamicTask)
- { //dynamic shadow columes don't need to wait in queue since they
- //all use the same vertex buffer. Flush them ASAP.
- shadowDynamicTask->m_parentShadow->RenderVolume(shadowDynamicTask->m_meshIndex,shadowDynamicTask->m_lightIndex);
- shadowDynamicTask=(W3DVolumetricShadowRenderTask *)shadowDynamicTask->m_nextTask;
- }
- //Reset all render tasks for next frame.
- for (nextVb=TheW3DBufferManager->getNextVertexBuffer(NULL,W3DBufferManager::VBM_FVF_XYZ);nextVb != NULL; nextVb=TheW3DBufferManager->getNextVertexBuffer(nextVb,W3DBufferManager::VBM_FVF_XYZ))
- {
- nextVb->m_renderTaskList=NULL;
- }
- m_pDev->SetRenderState(D3DRS_CULLMODE,D3DCULL_CW);
- // m_pDev->SetRenderState(D3DRS_ZBIAS,0); ///@todo: See if this helps or makes things worse.
- //m_pDev->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);
- if (oldColorWriteEnable != 0x12345678)
- DX8Wrapper::Set_DX8_Render_State(D3DRS_COLORWRITEENABLE,oldColorWriteEnable);
- //
- // render the big transparent square of shadows in the stencil buffer
- // to the screen
- //
- ///@todo: Put this check back in after water is fixed so it doesn't require shadow rendering to fix alpha.
- // if (numRenderedShadows)
- renderStencilShadows();
- m_pDev->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
- m_pDev->SetRenderState(D3DRS_ALPHABLENDENABLE , FALSE);
- m_pDev->SetRenderState(D3DRS_LIGHTING, FALSE);
- DX8Wrapper::Invalidate_Cached_Render_States();
- }
- else
- if (forceStencilFill)
- { //no shadows to render, but still need to fill stencil buffer
- //for other effects.
- //Set W3D to some known state
- VertexMaterialClass *vmat=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
- DX8Wrapper::Set_Material(vmat);
- REF_PTR_RELEASE(vmat);
- DX8Wrapper::Set_Shader(ShaderClass::_PresetOpaqueShader);
- DX8Wrapper::Set_Texture(0,NULL);
- DX8Wrapper::Apply_Render_State_Changes(); //force update of view and projection matrices
- renderStencilShadows();
- DX8Wrapper::Invalidate_Cached_Render_States();
- }
- } // end RenderShadows
- /** This class will manage shadow geometry for each render object. Shadow geometry may
- be the same as render geometry but doesn't need to be. This allows lower LOD versions of
- the geometry to be used in shadow calculations. Shadow geometry also keeps extended vertex
- connectivity information that's not used during rendering.
- */
- class W3DShadowGeometryManager
- {
- public:
- W3DShadowGeometryManager(void);
- ~W3DShadowGeometryManager(void);
- int Load_Geom(RenderObjClass *robj, const char *name);
- W3DShadowGeometry * Get_Geom(const char * name);
- W3DShadowGeometry * Peek_Geom(const char * name);
- Bool Add_Geom(W3DShadowGeometry *new_anim);
- void Free_All_Geoms(void);
- void Register_Missing( const char * name );
- Bool Is_Missing( const char * name );
- void Reset_Missing( void );
- private:
- HashTableClass * GeomPtrTable;
- HashTableClass * MissingGeomTable;
- friend class W3DShadowGeometryManagerIterator;
- };
- /*
- ** An Iterator to get to all loaded W3DShadowGeometries in a W3DShadowGeometryManager
- */
- class W3DShadowGeometryManagerIterator : public HashTableIteratorClass {
- public:
- W3DShadowGeometryManagerIterator( W3DShadowGeometryManager & manager ) : HashTableIteratorClass( *manager.GeomPtrTable ) {}
- W3DShadowGeometry * Get_Current_Geom( void );
- };
- /** Used to cause a rebuild of all shadow volumes*/
- void W3DVolumetricShadowManager::invalidateCachedLightPositions(void)
- {
- if (!m_shadowList)
- return; //there are no shadows to render.
- W3DVolumetricShadow *shadow;
- Vector3 vec(0,0,0);
- // step through each of our shadows and update previous light position.
- for( shadow = m_shadowList; shadow; shadow = shadow->m_next )
- {
- for(Int i = 0; i < MAX_SHADOW_LIGHTS; i++ )
- {
- for (Int meshIndex=0; meshIndex<MAX_SHADOW_CASTER_MESHES; meshIndex++)
- {
- shadow->setLightPosHistory(i,meshIndex,vec);
- }
- }
- } // end for
- }
- // W3DVolumetricShadowManager =============================================================
- // ============================================================================
- W3DVolumetricShadowManager::W3DVolumetricShadowManager( void )
- {
- m_shadowList = NULL;
- m_W3DShadowGeometryManager = NEW W3DShadowGeometryManager;
- TheW3DBufferManager = NEW W3DBufferManager;
- } // end ShadowManager
- // ~W3DVolumetricShadowManager ============================================================
- // ============================================================================
- W3DVolumetricShadowManager::~W3DVolumetricShadowManager( void )
- {
- ReleaseResources();
- delete m_W3DShadowGeometryManager;
- m_W3DShadowGeometryManager = NULL;
- delete TheW3DBufferManager;
- TheW3DBufferManager=NULL;
- //all shadows should be freed up at this point but check anyway
- assert(m_shadowList==NULL);
- } // end ~W3DVolumetricShadowManager
- /** Releases all W3D/D3D assets before a reset.. */
- void W3DVolumetricShadowManager::ReleaseResources(void)
- {
- if (shadowIndexBufferD3D)
- shadowIndexBufferD3D->Release();
- if (shadowVertexBufferD3D)
- shadowVertexBufferD3D->Release();
- shadowIndexBufferD3D=NULL;
- shadowVertexBufferD3D=NULL;
- if (TheW3DBufferManager)
- { TheW3DBufferManager->ReleaseResources();
- invalidateCachedLightPositions(); //vertex buffers need to be refilled.
- }
- }
- /** (Re)allocates all W3D/D3D assets after a reset.. */
- Bool W3DVolumetricShadowManager::ReAcquireResources(void)
- {
- ReleaseResources();
- LPDIRECT3DDEVICE8 m_pDev=DX8Wrapper::_Get_D3D_Device8();
- DEBUG_ASSERTCRASH(m_pDev, ("Trying to ReAquireResources on W3DVolumetricShadowManager without device"));
- if (FAILED(m_pDev->CreateIndexBuffer
- (
- SHADOW_INDEX_SIZE*sizeof(WORD),
- D3DUSAGE_WRITEONLY|D3DUSAGE_DYNAMIC,
- D3DFMT_INDEX16,
- D3DPOOL_DEFAULT,
- &shadowIndexBufferD3D
- )))
- return FALSE;
- if (shadowVertexBufferD3D == NULL)
- { // Create vertex buffer
- if (FAILED(m_pDev->CreateVertexBuffer
- (
- SHADOW_VERTEX_SIZE*sizeof(SHADOW_DYNAMIC_VOLUME_VERTEX),
- D3DUSAGE_WRITEONLY|D3DUSAGE_DYNAMIC,
- 0,
- D3DPOOL_DEFAULT,
- &shadowVertexBufferD3D
- )))
- return FALSE;
- }
- if (TheW3DBufferManager)
- if (!TheW3DBufferManager->ReAcquireResources())
- return FALSE;
- return TRUE;
- }
- // Init =======================================================================
- // User called initialization
- // ============================================================================
- Bool W3DVolumetricShadowManager::init( void )
- {
- return TRUE;
- } // end Init
- // Reset ======================================================================
- // Reset our list of shadows to empty
- // ============================================================================
- void W3DVolumetricShadowManager::reset( void )
- {
- assert (m_shadowList == NULL);
- m_W3DShadowGeometryManager->Free_All_Geoms();
- TheW3DBufferManager->freeAllBuffers();
- } // end Reset
- // addShadow ==================================================================
- // Add the shadows for this hierarchy to the shadow management for
- // rendering.
- // ============================================================================
- W3DVolumetricShadow* W3DVolumetricShadowManager::addShadow(RenderObjClass *robj, Shadow::ShadowTypeInfo *shadowInfo, Drawable *draw)
- {
- if (!DX8Wrapper::Has_Stencil() || !robj || !TheGlobalData->m_useShadowVolumes)
- return NULL; //right now we require a stencil buffer
- W3DShadowGeometry *sg=NULL;
- if (!robj)
- return NULL; //must have a render object in order to read shadow geometry
- const char *name=robj->Get_Name();
- if (!name)
- return NULL;
- sg=m_W3DShadowGeometryManager->Get_Geom(name);
- if (sg==NULL)
- { //did not find a cached copy of the shadow geometry, create a new one
- m_W3DShadowGeometryManager->Load_Geom(robj,name);
- //try loading again
- sg=m_W3DShadowGeometryManager->Get_Geom(name);
- if (sg==NULL)
- return NULL; //could not create the shadow geometry
- }
- W3DVolumetricShadow *shadow = NEW W3DVolumetricShadow; // poolify
- // sanity
- if( shadow == NULL )
- return NULL;
- shadow->setRenderObject(robj);
- shadow->SetGeometry(sg);
- SphereClass sphere;
- robj->Get_Obj_Space_Bounding_Sphere(sphere);
- shadow->setRenderObjExtent(sphere.Radius*MAX_SHADOW_LENGTH_SCALE_FACTOR);
- Real sunElevationAngleTan = 0;
- if (shadowInfo->m_sizeX)
- { //need to adjust sun elevation for this model in order to limit shadow length
- sunElevationAngleTan=tan(shadowInfo->m_sizeX/180.0f*PI);
- }
- shadow->setShadowLengthScale(sunElevationAngleTan);
- if (!draw || !draw->isKindOf(KINDOF_IMMOBILE))
- shadow->setOptimalExtrusionPadding(SHADOW_EXTRUSION_BUFFER);
- // add to our shadow list through the shadow next links
- shadow->m_next = m_shadowList;
- m_shadowList = shadow;
- return shadow;
- }
- /** removeShadow ===========================================================
- Removes the shadows for this hierarchy from the shadow manger. No further
- shadows from this caster will be rendered.
- ===========================================================================
- */
- void W3DVolumetricShadowManager::removeShadow(W3DVolumetricShadow *shadow)
- {
- W3DVolumetricShadow *prev_shadow=NULL;
- W3DVolumetricShadow *next_shadow=NULL;
- //search for this shadow
- for( next_shadow = m_shadowList; next_shadow; prev_shadow=next_shadow, next_shadow = next_shadow->m_next )
- {
- if (next_shadow == shadow)
- {
- if (prev_shadow)
- prev_shadow->m_next=shadow->m_next;
- else
- m_shadowList=shadow->m_next;
- delete shadow;
- break;
- }
- } // end for
- }
- /** removeAllShadows ===========================================================
- Removes all shadows from the shadow manger. No further
- shadows will be rendered.
- ===========================================================================
- */
- void W3DVolumetricShadowManager::removeAllShadows(void)
- {
- W3DVolumetricShadow *cur_shadow=NULL;
- W3DVolumetricShadow *next_shadow=m_shadowList;
- m_shadowList = NULL;
- //search for this shadow
- for( cur_shadow = next_shadow; cur_shadow; cur_shadow = next_shadow )
- {
- next_shadow = cur_shadow->m_next;
- cur_shadow->m_next = NULL;
- delete cur_shadow;
- } // end for
- }
- W3DShadowGeometryManager::W3DShadowGeometryManager(void)
- {
- // Create the hash tables
- GeomPtrTable = NEW HashTableClass( 2048 );
- MissingGeomTable = NEW HashTableClass( 2048 );
- }
- W3DShadowGeometryManager::~W3DShadowGeometryManager(void)
- {
- Free_All_Geoms();
- delete GeomPtrTable;
- GeomPtrTable = NULL;
- delete MissingGeomTable;
- MissingGeomTable = NULL;
- }
- /** Release all loaded animations */
- void W3DShadowGeometryManager::Free_All_Geoms(void)
- {
- // Make an iterator, and release all ptrs
- W3DShadowGeometryManagerIterator it( *this );
- for( it.First(); !it.Is_Done(); it.Next() ) {
- W3DShadowGeometry *geom = it.Get_Current_Geom();
- geom->Release_Ref();
- }
- // Then clear the table
- GeomPtrTable->Reset();
- }
- /** Find animation in cache */
- W3DShadowGeometry * W3DShadowGeometryManager::Peek_Geom(const char * name)
- {
- return (W3DShadowGeometry*)GeomPtrTable->Find( name );
- }
- /** Get animation from cache and increment its reference count */
- W3DShadowGeometry * W3DShadowGeometryManager::Get_Geom(const char * name)
- {
- W3DShadowGeometry * geom = Peek_Geom( name );
- if ( geom != NULL ) {
- geom->Add_Ref();
- }
- return geom;
- }
- /** Add animation to cache */
- Bool W3DShadowGeometryManager::Add_Geom(W3DShadowGeometry *new_geom)
- {
- WWASSERT (new_geom != NULL);
- // Increment the refcount on the new animation and add it to our table.
- new_geom->Add_Ref ();
- GeomPtrTable->Add( new_geom );
- return true;
- }
- /*
- ** An entry for a table of anims not found, so we can quickly determine their loss
- */
- class MissingGeomClass : public HashableClass {
- public:
- MissingGeomClass( const char * name ) : Name( name ) {}
- virtual ~MissingGeomClass( void ) {}
- virtual const char * Get_Key( void ) { return Name; }
- private:
- StringClass Name;
- };
- /*
- ** Missing Geoms
- **
- ** The idea here, allow the system to register which anims are determined to be missing
- ** so that if they are asked for again, we can quickly return NULL, without searching the
- ** disk again.
- */
- void W3DShadowGeometryManager::Register_Missing( const char * name )
- {
- MissingGeomTable->Add( NEW MissingGeomClass( name ) );
- }
- Bool W3DShadowGeometryManager::Is_Missing( const char * name )
- {
- return ( MissingGeomTable->Find( name ) != NULL );
- }
- /** Create shadow geometry from a reference W3D RenderObject*/
- int W3DShadowGeometryManager::Load_Geom(RenderObjClass *robj, const char *name)
- {
- Bool res=FALSE;
- W3DShadowGeometry * newgeom = NEW W3DShadowGeometry;
- if (newgeom == NULL) {
- goto Error;
- }
- SET_REF_OWNER( newgeom );
- newgeom->Set_Name(name);
- switch (robj->Class_ID())
- {
- case RenderObjClass::CLASSID_HLOD:
- res=newgeom->initFromHLOD(robj);
- break;
- case RenderObjClass::CLASSID_MESH:
- res=newgeom->initFromMesh(robj);
- break;
- default:
- break; //unknown render object type
- };
- if (res != TRUE)
- { // load failed!
- newgeom->Release_Ref();
- //DEBUG_LOG(("****Shadow Volume Creation Failed on %s\n",name));
- goto Error;
- } else if (Peek_Geom(newgeom->Get_Name()) != NULL)
- { // duplicate exists!
- newgeom->Release_Ref(); // Release the one we just loaded
- goto Error;
- } else
- { Add_Geom( newgeom );
- newgeom->Release_Ref();
- }
- return 0;
- Error:
- return 1;
- }
- /*
- ** Iterator converter from HashableClass to GrannyAnimClass
- */
- W3DShadowGeometry * W3DShadowGeometryManagerIterator::Get_Current_Geom( void )
- {
- return (W3DShadowGeometry *)Get_Current();
- }
|