HeightMap.cpp 152 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: Heightmap.cpp ////////////////////////////////////////////////
  24. //-----------------------------------------------------------------------------
  25. //
  26. // Westwood Studios Pacific.
  27. //
  28. // Confidential Information
  29. // Copyright (C) 2001 - All Rights Reserved
  30. //
  31. //-----------------------------------------------------------------------------
  32. //
  33. // Project: RTS3
  34. //
  35. // File name: Heightmap.cpp
  36. //
  37. // Created: Mark W., John Ahlquist, April/May 2001
  38. //
  39. // Desc: Draw the terrain and scorchmarks in a scene.
  40. //
  41. //-----------------------------------------------------------------------------
  42. //-----------------------------------------------------------------------------
  43. // Includes
  44. //-----------------------------------------------------------------------------
  45. #include "W3DDevice/GameClient/heightmap.h"
  46. #include <stdio.h>
  47. #include <stdlib.h>
  48. #include <string.h>
  49. #include <assetmgr.h>
  50. #include <texture.h>
  51. #include <tri.h>
  52. #include <colmath.h>
  53. #include <coltest.h>
  54. #include <rinfo.h>
  55. #include <camera.h>
  56. #include <d3dx8core.h>
  57. #include "Common/GlobalData.h"
  58. #include "Common/PerfTimer.h"
  59. #include "GameClient/TerrainVisual.h"
  60. #include "GameClient/View.h"
  61. #include "GameClient/Water.h"
  62. #include "GameLogic/AIPathfind.h"
  63. #include "GameLogic/TerrainLogic.h"
  64. #include "W3DDevice/GameClient/TerrainTex.h"
  65. #include "W3DDevice/GameClient/W3DDynamicLight.h"
  66. #include "W3DDevice/GameClient/W3DScene.h"
  67. #include "W3DDevice/GameClient/W3DTerrainTracks.h"
  68. #include "W3DDevice/GameClient/W3DBibBuffer.h"
  69. #include "W3DDevice/GameClient/W3DTreeBuffer.h"
  70. #include "W3DDevice/GameClient/W3DRoadBuffer.h"
  71. #include "W3DDevice/GameClient/W3DBridgeBuffer.h"
  72. #include "W3DDevice/GameClient/W3DWaypointBuffer.h"
  73. #include "W3DDevice/GameClient/W3DCustomEdging.h"
  74. #include "W3DDevice/GameClient/WorldHeightMap.h"
  75. #include "W3DDevice/GameClient/W3DShaderManager.h"
  76. #include "W3DDevice/GameClient/W3DShadow.h"
  77. #include "W3DDevice/GameClient/W3DWater.h"
  78. #include "W3DDevice/GameClient/W3DShroud.h"
  79. #include "WW3D2/DX8Wrapper.h"
  80. #include "WW3D2/Light.h"
  81. #include "WW3D2/Scene.h"
  82. #include "W3DDevice/GameClient/W3DPoly.h"
  83. #include "W3DDevice/GameClient/W3DCustomScene.h"
  84. #include "Common/PerfTimer.h"
  85. #include "Common/UnitTimings.h" //Contains the DO_UNIT_TIMINGS define jba.
  86. #ifdef _INTERNAL
  87. // for occasional debugging...
  88. //#pragma optimize("", off)
  89. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  90. #endif
  91. #define no_OPTIMIZED_HEIGHTMAP_LIGHTING 01
  92. // Doesn't work well. jba.
  93. //-----------------------------------------------------------------------------
  94. // Private Data
  95. //-----------------------------------------------------------------------------
  96. #define SC_DETAIL_BLEND ( SHADE_CNST(ShaderClass::PASS_LEQUAL, ShaderClass::DEPTH_WRITE_ENABLE, ShaderClass::COLOR_WRITE_ENABLE, ShaderClass::SRCBLEND_ONE, \
  97. ShaderClass::DSTBLEND_ZERO, ShaderClass::FOG_DISABLE, ShaderClass::GRADIENT_MODULATE, ShaderClass::SECONDARY_GRADIENT_DISABLE, ShaderClass::TEXTURING_ENABLE, \
  98. ShaderClass::ALPHATEST_DISABLE, ShaderClass::CULL_MODE_ENABLE, ShaderClass::DETAILCOLOR_SCALE, ShaderClass::DETAILALPHA_DISABLE) )
  99. static ShaderClass detailOpaqueShader(SC_DETAIL_BLEND);
  100. //-----------------------------------------------------------------------------
  101. // Global Functions & Data
  102. //-----------------------------------------------------------------------------
  103. /// The one-of for the terrain rendering object.
  104. HeightMapRenderObjClass *TheTerrainRenderObject=NULL;
  105. /** Entry point so that trees can be drawn at the appropriate point in the rendering pipe for
  106. transparent objects. */
  107. void DoTrees(RenderInfoClass & rinfo)
  108. {
  109. if (TheTerrainRenderObject) {
  110. TheTerrainRenderObject->renderTrees(&rinfo.Camera);
  111. }
  112. }
  113. void oversizeTheTerrain(Int amount)
  114. {
  115. if (TheTerrainRenderObject)
  116. {
  117. TheTerrainRenderObject->oversizeTerrain(amount);
  118. }
  119. }
  120. #define DEFAULT_MAX_FRAME_EXTRABLEND_TILES 256 //default number of terrain tiles rendered per call (must fit in one VB)
  121. #define DEFAULT_MAX_MAP_EXTRABLEND_TILES 2048 //default size of array allocated to hold all map extra blend tiles.
  122. #define DEFAULT_MAX_BATCH_SHORELINE_TILES 512 //maximum number of terrain tiles rendered per call (must fit in one VB)
  123. #define DEFAULT_MAX_MAP_SHORELINE_TILES 4096 //default size of array allocated to hold all map shoreline tiles.
  124. #define ADJUST_FROM_INDEX_TO_REAL(k) ((k-m_map->getBorderSize())*MAP_XY_FACTOR)
  125. inline Int IABS(Int x) { if (x>=0) return x; return -x;};
  126. //-----------------------------------------------------------------------------
  127. // Private Functions
  128. //-----------------------------------------------------------------------------
  129. //=============================================================================
  130. // HeightMapRenderObjClass::freeMapResources
  131. //=============================================================================
  132. /** Frees the w3d resources used to draw the terrain. */
  133. //=============================================================================
  134. Int HeightMapRenderObjClass::freeMapResources(void)
  135. {
  136. #ifdef DO_SCORCH
  137. freeScorchBuffers();
  138. #endif
  139. REF_PTR_RELEASE(m_indexBuffer);
  140. if (m_vertexBufferTiles) {
  141. for (int i=0; i<m_numVertexBufferTiles; i++)
  142. REF_PTR_RELEASE(m_vertexBufferTiles[i]);
  143. delete m_vertexBufferTiles;
  144. m_vertexBufferTiles = NULL;
  145. }
  146. if (m_vertexBufferBackup) {
  147. for (int i=0; i<m_numVertexBufferTiles; i++)
  148. delete m_vertexBufferBackup[i];
  149. delete m_vertexBufferBackup;
  150. m_vertexBufferBackup = NULL;
  151. }
  152. m_numVertexBufferTiles = 0;
  153. #ifdef PRE_TRANSFORM_VERTEX
  154. if (m_xformedVertexBuffer) {
  155. for (int i=0; i<m_numVertexBufferTiles; i++)
  156. m_xformedVertexBuffer[i]->Release();
  157. delete m_xformedVertexBuffer;
  158. m_xformedVertexBuffer = NULL;
  159. }
  160. #endif
  161. REF_PTR_RELEASE(m_vertexMaterialClass);
  162. REF_PTR_RELEASE(m_stageZeroTexture);
  163. REF_PTR_RELEASE(m_stageOneTexture);
  164. REF_PTR_RELEASE(m_stageTwoTexture);
  165. REF_PTR_RELEASE(m_stageThreeTexture);
  166. REF_PTR_RELEASE(m_destAlphaTexture);
  167. REF_PTR_RELEASE(m_map);
  168. return 0;
  169. }
  170. //=============================================================================
  171. // HeightMapRenderObjClass::doTheLight
  172. //=============================================================================
  173. /** Calculates the diffuse lighting for a vertex in the terrain, taking all of the
  174. static lights into account as well. It is possible to just use the normal in the
  175. vertex and let D3D do the lighting, but it is slower to render, and can only
  176. handle 4 lights at this point. */
  177. //=============================================================================
  178. void HeightMapRenderObjClass::doTheLight(VERTEX_FORMAT *vb, Vector3*light, Vector3*normal, RefRenderObjListIterator *pLightsIterator, UnsignedByte alpha)
  179. {
  180. #ifdef USE_NORMALS
  181. vb->nx = normal->X;
  182. vb->ny = normal->Y;
  183. vb->nz = normal->Z;
  184. #else
  185. Real shadeR, shadeG, shadeB;
  186. Real shade;
  187. shadeR = TheGlobalData->m_terrainAmbient[0].red; //only the first terrain light contributes to ambient
  188. shadeG = TheGlobalData->m_terrainAmbient[0].green;
  189. shadeB = TheGlobalData->m_terrainAmbient[0].blue;
  190. if (pLightsIterator) {
  191. for (pLightsIterator->First(); !pLightsIterator->Is_Done(); pLightsIterator->Next())
  192. {
  193. LightClass *pLight = (LightClass*)pLightsIterator->Peek_Obj();
  194. Vector3 lightDirection(vb->x, vb->y, vb->z);
  195. Real factor = 1.0f;
  196. switch(pLight->Get_Type()) {
  197. case LightClass::POINT:
  198. case LightClass::SPOT: {
  199. Vector3 lightLoc = pLight->Get_Position();
  200. lightDirection -= lightLoc;
  201. double range, midRange;
  202. pLight->Get_Far_Attenuation_Range(midRange, range);
  203. if (vb->x < lightLoc.X-range) continue;
  204. if (vb->x > lightLoc.X+range) continue;
  205. if (vb->y < lightLoc.Y-range) continue;
  206. if (vb->y > lightLoc.Y+range) continue;
  207. Real dist = lightDirection.Length();
  208. if (dist >= range) continue;
  209. if (midRange < 0.1) continue;
  210. #if 1
  211. factor = 1.0f - (dist - midRange) / (range - midRange);
  212. #else
  213. // f = 1.0 / (atten0 + d*atten1 + d*d/atten2);
  214. if (fabs(range-midRange)<1e-5) {
  215. // if the attenuation range is too small assume uniform with cutoff
  216. factor = 1.0;
  217. } else {
  218. factor = 1.0f/(0.1+dist/midRange + 5.0f*dist*dist/(range*range));
  219. }
  220. #endif
  221. factor = WWMath::Clamp(factor,0.0f,1.0f);
  222. }
  223. break;
  224. case LightClass::DIRECTIONAL:
  225. lightDirection = pLight->Get_Transform().Get_Z_Vector();
  226. factor = 1.0;
  227. break;
  228. };
  229. lightDirection.Normalize();
  230. Vector3 lightRay(-lightDirection.X, -lightDirection.Y, -lightDirection.Z);
  231. shade = Vector3::Dot_Product(lightRay, *normal);
  232. shade *= factor;
  233. Vector3 diffuse;
  234. pLight->Get_Diffuse(&diffuse);
  235. Vector3 ambient;
  236. pLight->Get_Ambient(&ambient);
  237. if (shade > 1.0) shade = 1.0;
  238. if(shade < 0.0f) shade = 0.0f;
  239. shadeR += shade*diffuse.X;
  240. shadeG += shade*diffuse.Y;
  241. shadeB += shade*diffuse.Z;
  242. shadeR += factor*ambient.X;
  243. shadeG += factor*ambient.Y;
  244. shadeB += factor*ambient.Z;
  245. }
  246. }
  247. // Add in global diffuse value.
  248. const RGBColor *terrainDiffuse;
  249. for (Int lightIndex=0; lightIndex < TheGlobalData->m_numGlobalLights; lightIndex++)
  250. {
  251. shade = Vector3::Dot_Product(light[lightIndex], *normal);
  252. if (shade > 1.0) shade = 1.0;
  253. if(shade < 0.0f) shade = 0.0f;
  254. terrainDiffuse=&TheGlobalData->m_terrainDiffuse[lightIndex];
  255. shadeR += shade*terrainDiffuse->red;
  256. shadeG += shade*terrainDiffuse->green;
  257. shadeB += shade*terrainDiffuse->blue;
  258. }
  259. if (shadeR > 1.0) shadeR = 1.0;
  260. if(shadeR < 0.0f) shadeR = 0.0f;
  261. if (shadeG > 1.0) shadeG = 1.0;
  262. if(shadeG < 0.0f) shadeG = 0.0f;
  263. if (shadeB > 1.0) shadeB = 1.0;
  264. if(shadeB < 0.0f) shadeB = 0.0f;
  265. if (m_useDepthFade && vb->z <= TheGlobalData->m_waterPositionZ)
  266. { //height is below water level
  267. //reduce lighting values based on light fall off as it travels through water.
  268. float depthScale = (1.4f - vb->z)/TheGlobalData->m_waterPositionZ;
  269. shadeR *= 1.0f - depthScale * (1.0f-m_depthFade.X);
  270. shadeG *= 1.0f - depthScale * (1.0f-m_depthFade.Y);
  271. shadeB *= 1.0f - depthScale * (1.0f-m_depthFade.Z);
  272. }
  273. shadeR*=255.0f;
  274. shadeG*=255.0f;
  275. shadeB*=255.0f;
  276. vb->diffuse = REAL_TO_INT(shadeB) | (REAL_TO_INT(shadeG) << 8) | (REAL_TO_INT(shadeR) << 16) | ((Int)alpha << 24);
  277. #endif
  278. }
  279. //=============================================================================
  280. // HeightMapRenderObjClass::doTheDynamicLight
  281. //=============================================================================
  282. /** Calculates the diffuse lighting as affected by dynamic lighting. */
  283. //=============================================================================
  284. UnsignedInt HeightMapRenderObjClass::doTheDynamicLight(VERTEX_FORMAT *vb, VERTEX_FORMAT *vbMirror, Vector3*light, Vector3*normal, W3DDynamicLight *pLights[], Int numLights)
  285. {
  286. #ifdef USE_NORMALS
  287. return;
  288. #else
  289. Real shadeR, shadeG, shadeB;
  290. Int diffuse = vbMirror->diffuse;
  291. #ifdef _DEBUG
  292. //vbMirror->diffuse += 30; // Shows which vertexes are geting touched by dynamic light. debug only.
  293. #endif
  294. // (gth) avoiding the extra divides (compiler unfortunately didn't do this automatically...)
  295. const float oo255 = (1.0f/255.0f);
  296. shadeR = ((diffuse>>16)&0x00FF) * oo255;
  297. shadeG = ((diffuse>>8)&0x00FF) * oo255;
  298. shadeB = (diffuse&0x00FF) * oo255;
  299. Int alpha = (diffuse>>24)&0x00FF;
  300. Int k;
  301. for (k=0; k<numLights; k++) {
  302. W3DDynamicLight *pLight = pLights[k];
  303. if (!pLight->isEnabled()) {
  304. continue; // he is turned off.
  305. }
  306. Vector3 lightDirection(vbMirror->x, vbMirror->y, vbMirror->z);
  307. Real factor = 1.0f;
  308. switch(pLight->Get_Type()) {
  309. case LightClass::POINT:
  310. case LightClass::SPOT: {
  311. Vector3 lightLoc = pLight->Get_Position();
  312. lightDirection -= lightLoc;
  313. double range, midRange;
  314. pLight->Get_Far_Attenuation_Range(midRange, range);
  315. Real dist = lightDirection.Length();
  316. if (dist >= range) continue;
  317. if (midRange < 0.1) continue;
  318. factor = 1.0f - (dist - midRange) / (range - midRange);
  319. factor = WWMath::Clamp(factor,0.0f,1.0f);
  320. // (gth) normalize here since we have the length
  321. lightDirection /= dist;
  322. }
  323. break;
  324. case LightClass::DIRECTIONAL:
  325. pLight->Get_Spot_Direction(lightDirection);
  326. factor = 1.0;
  327. break;
  328. };
  329. // (gth) unneeded due to above normalization
  330. //lightDirection.Normalize();
  331. Vector3 lightRay(-lightDirection.X, -lightDirection.Y, -lightDirection.Z);
  332. Real shade = Vector3::Dot_Product(lightRay, *normal);
  333. shade *= factor;
  334. Vector3 diffuse;
  335. pLight->Get_Diffuse(&diffuse);
  336. Vector3 ambient;
  337. pLight->Get_Ambient(&ambient);
  338. if (shade > 1.0) shade = 1.0;
  339. if(shade < 0.0f) shade = 0.0f;
  340. shadeR += shade*diffuse.X;
  341. shadeG += shade*diffuse.Y;
  342. shadeB += shade*diffuse.Z;
  343. shadeR += factor*ambient.X;
  344. shadeG += factor*ambient.Y;
  345. shadeB += factor*ambient.Z;
  346. }
  347. if (shadeR > 1.0) shadeR = 1.0;
  348. if(shadeR < 0.0f) shadeR = 0.0f;
  349. if (shadeG > 1.0) shadeG = 1.0;
  350. if(shadeG < 0.0f) shadeG = 0.0f;
  351. if (shadeB > 1.0) shadeB = 1.0;
  352. if(shadeB < 0.0f) shadeB = 0.0f;
  353. shadeR*=255.0f;
  354. shadeG*=255.0f;
  355. shadeB*=255.0f;
  356. // (gth) faster float to int conversion, return the result so we can re-use it.
  357. // vb->diffuse=REAL_TO_INT(shadeB) | (REAL_TO_INT(shadeG) << 8) | (REAL_TO_INT(shadeR) << 16) | ((int)alpha << 24);
  358. UnsignedInt light_val = WWMath::Float_To_Int_Chop(shadeB) | (WWMath::Float_To_Int_Chop(shadeG) << 8) | (WWMath::Float_To_Int_Chop(shadeR) << 16) | ((int)alpha << 24);
  359. vb->diffuse = light_val;
  360. return light_val;
  361. #endif
  362. }
  363. //=============================================================================
  364. // HeightMapRenderObjClass::getXWithOrigin
  365. //=============================================================================
  366. /** Gets the x index that corresponds to the data. For example, if the columns
  367. are shifted by 3, index 3 is actually the first row of polygons, or 0. Yes it
  368. is confusing, but it makes sliding the map 10x faster. */
  369. //=============================================================================
  370. Int HeightMapRenderObjClass::getXWithOrigin(Int x)
  371. {
  372. x -= m_originX;
  373. if (x<0) x+= m_x-1;
  374. if (x>= m_x-1) x-=m_x-1;
  375. #ifdef _DEBUG
  376. DEBUG_ASSERTCRASH (x>=0, ("X out of range."));
  377. DEBUG_ASSERTCRASH (x<m_x-1, ("X out of range."));
  378. #endif
  379. if (x<0) x = 0;
  380. if (x>= m_x-1) x=m_x-1;
  381. return x;
  382. }
  383. //=============================================================================
  384. // HeightMapRenderObjClass::getYWithOrigin
  385. //=============================================================================
  386. /** Gets the y index that corresponds to the data. For example, if the rows
  387. are shifted by 3, index 3 is actually the first row of polygons, or 0. Yes it
  388. is confusing, but it makes sliding the map 10x faster. */
  389. //=============================================================================
  390. Int HeightMapRenderObjClass::getYWithOrigin(Int y)
  391. {
  392. y -= m_originY;
  393. if (y<0) y+= m_y-1;
  394. if (y>= m_y-1) y-=m_y-1;
  395. #ifdef _DEBUG
  396. DEBUG_ASSERTCRASH (y>=0, ("Y out of range."));
  397. DEBUG_ASSERTCRASH (y<m_y-1, ("Y out of range."));
  398. #endif
  399. if (y<0) y = 0;
  400. if (y>= m_y-1) y=m_y-1;
  401. return y;
  402. }
  403. //=============================================================================
  404. // HeightMapRenderObjClass::updateVB
  405. //=============================================================================
  406. /** Update a rectangular block of the given Vertex Buffer.
  407. data is expected to be an array same dimensions as current heightmap
  408. mapped into this VB.
  409. */
  410. //=============================================================================
  411. Int HeightMapRenderObjClass::updateVB(DX8VertexBufferClass *pVB, char *data, Int x0, Int y0, Int x1, Int y1, Int originX, Int originY, WorldHeightMap *pMap, RefRenderObjListIterator *pLightsIterator)
  412. {
  413. Int i,j;
  414. Vector3 lightRay[MAX_GLOBAL_LIGHTS];
  415. const Coord3D *lightPos;
  416. Int xCoord, yCoord;
  417. Int vn0,un0,vp1,up1;
  418. Vector3 l2r,n2f,normalAtTexel;
  419. Int vertsPerRow=(VERTEX_BUFFER_TILE_LENGTH)*4; //vertices per row of VB
  420. Int cellOffset = 1;
  421. if (m_halfResMesh) {
  422. cellOffset = 2;
  423. }
  424. REF_PTR_SET(m_map, pMap); //update our heightmap pointer in case it changed since last call.
  425. if (m_vertexBufferTiles && pMap)
  426. {
  427. #ifdef _DEBUG
  428. assert(x0 >= originX && y0 >= originY && x1>x0 && y1>y0 && x1<=originX+VERTEX_BUFFER_TILE_LENGTH && y1<=originY+VERTEX_BUFFER_TILE_LENGTH);
  429. #endif
  430. DX8VertexBufferClass::WriteLockClass lockVtxBuffer(pVB);
  431. VERTEX_FORMAT *vbHardware = (VERTEX_FORMAT*)lockVtxBuffer.Get_Vertex_Array();
  432. VERTEX_FORMAT *vBase = (VERTEX_FORMAT*)data;
  433. // Note that we are building the vertex buffer data in the memory buffer, data.
  434. // At the bottom, we will copy the final vertex data for one cell into the
  435. // hardware vertex buffer.
  436. for (j=y0; j<y1; j++)
  437. {
  438. VERTEX_FORMAT *vb = vBase;
  439. if (m_halfResMesh) {
  440. if (j&1) continue;
  441. vb += ((j-originY)/2)*vertsPerRow/2; //skip to correct row in vertex buffer
  442. vb += ((x0-originX)/2)*4; //skip to correct vertex in row.
  443. } else {
  444. vb += (j-originY)*vertsPerRow; //skip to correct row in vertex buffer
  445. vb += (x0-originX)*4; //skip to correct vertex in row.
  446. }
  447. vn0 = getYWithOrigin(j)-cellOffset;
  448. if (vn0 < -pMap->getDrawOrgY())
  449. vn0=-pMap->getDrawOrgY();
  450. vp1 = getYWithOrigin(j+cellOffset)+cellOffset;
  451. if (vp1 >= pMap->getYExtent()-pMap->getDrawOrgY())
  452. vp1=pMap->getYExtent()-pMap->getDrawOrgY()-1;
  453. yCoord = getYWithOrigin(j)+pMap->getDrawOrgY();
  454. for (i=x0; i<x1; i++)
  455. {
  456. if (m_halfResMesh) {
  457. if (i&1) continue;
  458. }
  459. un0 = getXWithOrigin(i)-cellOffset;
  460. if (un0 < -pMap->getDrawOrgX())
  461. un0=-pMap->getDrawOrgX();
  462. up1 = getXWithOrigin(i+cellOffset)+cellOffset;
  463. if (up1 >= pMap->getXExtent()-pMap->getDrawOrgX())
  464. up1=pMap->getXExtent()-pMap->getDrawOrgX()-1;
  465. xCoord = getXWithOrigin(i)+pMap->getDrawOrgX();
  466. //update the 4 vertices in this block
  467. float U[4], V[4];
  468. UnsignedByte alpha[4];
  469. float UA[4], VA[4];
  470. Bool flipForBlend = false; // True if the blend needs the triangles flipped.
  471. if (pMap) {
  472. pMap->getUVData(getXWithOrigin(i),getYWithOrigin(j),U, V, m_halfResMesh);
  473. pMap->getAlphaUVData(getXWithOrigin(i),getYWithOrigin(j), UA, VA, alpha, &flipForBlend, m_halfResMesh);
  474. }
  475. for (Int lightIndex=0; lightIndex < TheGlobalData->m_numGlobalLights; lightIndex++)
  476. {
  477. lightPos=&TheGlobalData->m_terrainLightPos[lightIndex];
  478. lightRay[lightIndex].Set(-lightPos->x,-lightPos->y, -lightPos->z);
  479. }
  480. //top-left sample
  481. l2r.Set(2*MAP_XY_FACTOR,0,MAP_HEIGHT_SCALE*(pMap->getDisplayHeight(getXWithOrigin(i)+cellOffset, getYWithOrigin(j)) - pMap->getDisplayHeight(un0, getYWithOrigin(j))));
  482. n2f.Set(0,2*MAP_XY_FACTOR,MAP_HEIGHT_SCALE*(pMap->getDisplayHeight(getXWithOrigin(i), (getYWithOrigin(j)+cellOffset)) - pMap->getDisplayHeight(getXWithOrigin(i), vn0)));
  483. #ifdef ALLOW_TEMPORARIES
  484. normalAtTexel= Normalize(Vector3::Cross_Product(l2r,n2f));
  485. #else
  486. Vector3::Normalized_Cross_Product(l2r, n2f, &normalAtTexel);
  487. #endif
  488. vb->x=xCoord;
  489. vb->y=yCoord;
  490. vb->z= ((float)pMap->getDisplayHeight(getXWithOrigin(i), getYWithOrigin(j)))*MAP_HEIGHT_SCALE;
  491. vb->x = ADJUST_FROM_INDEX_TO_REAL(vb->x);
  492. vb->y = ADJUST_FROM_INDEX_TO_REAL(vb->y);
  493. vb->u1=U[0];
  494. vb->v1=V[0];
  495. vb->u2=UA[0];
  496. vb->v2=VA[0];
  497. doTheLight(vb, lightRay, &normalAtTexel, pLightsIterator, alpha[0]);
  498. vb++;
  499. //top-right sample
  500. l2r.Set(2*MAP_XY_FACTOR,0,MAP_HEIGHT_SCALE*(pMap->getDisplayHeight(up1 , getYWithOrigin(j) ) - pMap->getDisplayHeight(getXWithOrigin(i) , getYWithOrigin(j) )));
  501. n2f.Set(0,2*MAP_XY_FACTOR,MAP_HEIGHT_SCALE*(pMap->getDisplayHeight(getXWithOrigin(i)+cellOffset , (getYWithOrigin(j)+cellOffset) ) - pMap->getDisplayHeight(getXWithOrigin(i)+cellOffset , vn0 )));
  502. #ifdef ALLOW_TEMPORARIES
  503. normalAtTexel= Normalize(Vector3::Cross_Product(l2r,n2f));
  504. #else
  505. Vector3::Normalized_Cross_Product(l2r, n2f, &normalAtTexel);
  506. #endif
  507. vb->x=xCoord+cellOffset;
  508. vb->y=yCoord;
  509. vb->z= ((float)pMap->getDisplayHeight(getXWithOrigin(i)+cellOffset, getYWithOrigin(j)))*MAP_HEIGHT_SCALE;
  510. vb->x = ADJUST_FROM_INDEX_TO_REAL(vb->x);
  511. vb->y = ADJUST_FROM_INDEX_TO_REAL(vb->y);
  512. vb->u1=U[1];
  513. vb->v1=V[1];
  514. vb->u2=UA[1];
  515. vb->v2=VA[1];
  516. doTheLight(vb, lightRay, &normalAtTexel, pLightsIterator, alpha[1]);
  517. vb++;
  518. //bottom-right sample
  519. l2r.Set(2*MAP_XY_FACTOR,0,MAP_HEIGHT_SCALE*(pMap->getDisplayHeight(up1 , (getYWithOrigin(j)+cellOffset) ) - pMap->getDisplayHeight(getXWithOrigin(i) , (getYWithOrigin(j)+cellOffset) )));
  520. n2f.Set(0,2*MAP_XY_FACTOR,MAP_HEIGHT_SCALE*(pMap->getDisplayHeight(getXWithOrigin(i)+cellOffset , vp1 ) - pMap->getDisplayHeight(getXWithOrigin(i)+cellOffset , getYWithOrigin(j) )));
  521. #ifdef ALLOW_TEMPORARIES
  522. normalAtTexel= Normalize(Vector3::Cross_Product(l2r,n2f));
  523. #else
  524. Vector3::Normalized_Cross_Product(l2r, n2f, &normalAtTexel);
  525. #endif
  526. vb->x=xCoord+cellOffset;
  527. if (yCoord + 1 == pMap->getDrawOrgY() + m_y - 1) {
  528. vb->y=yCoord+1;
  529. } else {
  530. vb->y=yCoord+cellOffset;
  531. }
  532. vb->z= ((float)pMap->getDisplayHeight(getXWithOrigin(i)+cellOffset, getYWithOrigin(j)+cellOffset))*MAP_HEIGHT_SCALE;
  533. vb->x = ADJUST_FROM_INDEX_TO_REAL(vb->x);
  534. vb->y = ADJUST_FROM_INDEX_TO_REAL(vb->y);
  535. vb->u1=U[2];
  536. vb->v1=V[2];
  537. vb->u2=UA[2];
  538. vb->v2=VA[2];
  539. doTheLight(vb, lightRay, &normalAtTexel, pLightsIterator, alpha[2]);
  540. vb++;
  541. //bottom-left sample
  542. l2r.Set(2*MAP_XY_FACTOR,0,MAP_HEIGHT_SCALE*(pMap->getDisplayHeight(getXWithOrigin(i)+cellOffset , (getYWithOrigin(j)+cellOffset) ) - pMap->getDisplayHeight(un0 , (getYWithOrigin(j)+cellOffset) )));
  543. n2f.Set(0,2*MAP_XY_FACTOR,MAP_HEIGHT_SCALE*(pMap->getDisplayHeight(getXWithOrigin(i) , vp1 ) - pMap->getDisplayHeight(getXWithOrigin(i) , getYWithOrigin(j) )));
  544. #ifdef ALLOW_TEMPORARIES
  545. normalAtTexel= Normalize(Vector3::Cross_Product(l2r,n2f));
  546. #else
  547. Vector3::Normalized_Cross_Product(l2r, n2f, &normalAtTexel);
  548. #endif
  549. if (xCoord == pMap->getDrawOrgX()) {
  550. vb->x=xCoord;
  551. //if (vb->x < 0) vb->x = 0;
  552. } else {
  553. vb->x=xCoord;
  554. }
  555. if (yCoord + 1 == pMap->getDrawOrgY() + m_y - 1) {
  556. vb->y=yCoord+1;
  557. } else {
  558. vb->y=yCoord+cellOffset;
  559. }
  560. vb->z= ((float)pMap->getDisplayHeight(getXWithOrigin(i), getYWithOrigin(j)+cellOffset))*MAP_HEIGHT_SCALE;
  561. vb->x = ADJUST_FROM_INDEX_TO_REAL(vb->x);
  562. vb->y = ADJUST_FROM_INDEX_TO_REAL(vb->y);
  563. vb->u1=U[3];
  564. vb->v1=V[3];
  565. vb->u2=UA[3];
  566. vb->v2=VA[3];
  567. doTheLight(vb, lightRay, &normalAtTexel, pLightsIterator, alpha[3]);
  568. vb++;
  569. VERTEX_FORMAT *pCurVertices = vb-4;
  570. #ifdef FLIP_TRIANGLES // jba - reduces "diamonding" in some cases, not others. Better cliffs, though.
  571. VERTEX_FORMAT tmpVertex;
  572. if (flipForBlend) {
  573. tmpVertex = pCurVertices[0];
  574. pCurVertices[0] = pCurVertices[1];
  575. pCurVertices[1] = pCurVertices[2];
  576. pCurVertices[2] = pCurVertices[3];
  577. pCurVertices[3] = tmpVertex;
  578. }
  579. #endif
  580. if (m_showImpassableAreas) {
  581. // Color impassable cells "red"
  582. DEBUG_ASSERTCRASH(PATHFIND_CELL_SIZE_F == MAP_XY_FACTOR, ("Pathfind must be terrain cell size, or this code needs reworking. John A."));
  583. Real borderHiX = (pMap->getXExtent()-2*pMap->getBorderSize())*MAP_XY_FACTOR;
  584. Real borderHiY = (pMap->getYExtent()-2*pMap->getBorderSize())*MAP_XY_FACTOR;
  585. Bool border = pCurVertices[0].x == -MAP_XY_FACTOR || pCurVertices[0].y == -MAP_XY_FACTOR;
  586. Bool cliffMapped = pMap->isCliffMappedTexture(getXWithOrigin(i), getYWithOrigin(j));
  587. if (pCurVertices[0].x == borderHiX) {
  588. border = true;
  589. }
  590. if (pCurVertices[0].y == borderHiY) {
  591. border = true;
  592. }
  593. Bool isCliff = pMap->getCliffState(getXWithOrigin(i)+pMap->getDrawOrgX(), getYWithOrigin(j)+pMap->getDrawOrgY())
  594. || showAsVisibleCliff(getXWithOrigin(i) + pMap->getDrawOrgX(), getYWithOrigin(j)+pMap->getDrawOrgY());
  595. if ( isCliff || border || cliffMapped) {
  596. Int cellX, cellY;
  597. for (cellX=0; cellX<2; cellX++) {
  598. for (cellY=0; cellY<2; cellY++) {
  599. Int vertex = cellX+2*cellY;
  600. if (border) {
  601. Bool doBorder = false;
  602. if (pCurVertices[vertex].y >= 0 && pCurVertices[vertex].y <= borderHiY) {
  603. if (pCurVertices[vertex].x == 0 || pCurVertices[vertex].x == borderHiX) {
  604. doBorder = true;
  605. }
  606. }
  607. if (pCurVertices[vertex].x >= 0 && pCurVertices[vertex].x <= borderHiX) {
  608. if (pCurVertices[vertex].y == 0 || pCurVertices[vertex].y == borderHiY) {
  609. doBorder = true;
  610. }
  611. }
  612. if (doBorder) {
  613. pCurVertices[vertex].diffuse &= 0xFF0000ff; // blue with alpha.
  614. }
  615. } else if (isCliff) {
  616. pCurVertices[vertex].diffuse &= 0xFFFF0000; // red with alpha.
  617. }
  618. if (cliffMapped && vertex==0) {
  619. pCurVertices[vertex].diffuse &= 0xFF000000; // Black.
  620. pCurVertices[vertex].diffuse |= 0xff00; // Add green.
  621. }
  622. }
  623. }
  624. }
  625. }
  626. // Note - We have been building the vertex buffer in the memory location.
  627. // Now copy the set of vertices into the hardware buffer.
  628. // We don't copy the whole vertex buffer because we often update only
  629. // a couple of rows and its a lot faster to just copy the ones that change.
  630. Int offset = pCurVertices - vBase;
  631. memcpy(vbHardware+offset, pCurVertices, 4*sizeof(VERTEX_FORMAT));
  632. }
  633. }
  634. return 0; //success.
  635. }
  636. return -1;
  637. }
  638. //=============================================================================
  639. // HeightMapRenderObjClass::updateVBForLight
  640. //=============================================================================
  641. /** Update the dynamic lighting values only in a rectangular block of the given Vertex Buffer.
  642. The vertex locations and texture coords are unchanged.
  643. */
  644. Int HeightMapRenderObjClass::updateVBForLight(DX8VertexBufferClass *pVB, char *data, Int x0, Int y0, Int x1, Int y1, Int originX, Int originY, W3DDynamicLight *pLights[], Int numLights)
  645. {
  646. #if (OPTIMIZED_HEIGHTMAP_LIGHTING) // (gth) if optimizations are enabled, jump over to the "optimized" version of this function.
  647. return updateVBForLightOptimized( pVB, data, x0, y0, x1, y1, originX, originY, pLights, numLights );
  648. #endif
  649. Int i,j,k;
  650. Int vn0,un0,vp1,up1;
  651. Vector3 l2r,n2f,normalAtTexel;
  652. Int vertsPerRow=(VERTEX_BUFFER_TILE_LENGTH)*4; //vertices per row of VB
  653. if (m_vertexBufferTiles && m_map)
  654. {
  655. #ifdef _DEBUG
  656. assert(x0 >= originX && y0 >= originY && x1>x0 && y1>y0 && x1<=originX+VERTEX_BUFFER_TILE_LENGTH && y1<=originY+VERTEX_BUFFER_TILE_LENGTH);
  657. #endif
  658. DX8VertexBufferClass::WriteLockClass lockVtxBuffer(pVB);
  659. VERTEX_FORMAT *vBase = (VERTEX_FORMAT*)lockVtxBuffer.Get_Vertex_Array();
  660. VERTEX_FORMAT *vb;
  661. for (j=y0; j<y1; j++)
  662. {
  663. if (m_halfResMesh) {
  664. if (j&1) continue;
  665. }
  666. Int yCoord = getYWithOrigin(j)+m_map->getDrawOrgY()-m_map->getBorderSize();
  667. Bool intersect = false;
  668. for (k=0; k<numLights; k++) {
  669. if (pLights[k]->m_minY <= yCoord+1 &&
  670. pLights[k]->m_maxY >= yCoord) {
  671. intersect = true;
  672. }
  673. if (pLights[k]->m_prevMinY <= yCoord+1 &&
  674. pLights[k]->m_prevMaxY >= yCoord) {
  675. intersect = true;
  676. }
  677. }
  678. if (!intersect) {
  679. continue;
  680. }
  681. vn0 = getYWithOrigin(j)-1;
  682. if (vn0 < -m_map->getDrawOrgY())
  683. vn0=-m_map->getDrawOrgY();
  684. vp1 = getYWithOrigin(j+1)+1;
  685. if (vp1 >= m_map->getYExtent()-m_map->getDrawOrgY())
  686. vp1=m_map->getYExtent()-m_map->getDrawOrgY()-1;
  687. for (i=x0; i<x1; i++)
  688. {
  689. if (m_halfResMesh) {
  690. if (i&1) continue;
  691. }
  692. Int xCoord = getXWithOrigin(i)+m_map->getDrawOrgX()-m_map->getBorderSize();
  693. Bool intersect = false;
  694. for (k=0; k<numLights; k++) {
  695. if (pLights[k]->m_minX <= xCoord+1 &&
  696. pLights[k]->m_maxX >= xCoord &&
  697. pLights[k]->m_minY <= yCoord+1 &&
  698. pLights[k]->m_maxY >= yCoord) {
  699. intersect = true;
  700. }
  701. if (pLights[k]->m_prevMinX <= xCoord+1 &&
  702. pLights[k]->m_prevMaxX >= xCoord &&
  703. pLights[k]->m_prevMinY <= yCoord+1 &&
  704. pLights[k]->m_prevMaxY >= yCoord) {
  705. intersect = true;
  706. }
  707. }
  708. if (!intersect) {
  709. continue;
  710. }
  711. // vb is the pointer to the vertex in the hardware dx8 vertex buffer.
  712. Int offset = (j-originY)*vertsPerRow+4*(i-originX);
  713. if (m_halfResMesh) {
  714. offset = (j-originY)*vertsPerRow/4+2*(i-originX);
  715. }
  716. vb = vBase + offset; //skip to correct row in vertex buffer
  717. // vbMirror is the pointer to the vertex in our memory based copy.
  718. // The important point is that we can read out of our copy to get the original
  719. // diffuse color, and xyz location. It is VERY SLOW to read out of the
  720. // hardware vertex buffer, possibly worse... jba.
  721. VERTEX_FORMAT *vbMirror = ((VERTEX_FORMAT*)data) + offset;
  722. un0 = getXWithOrigin(i)-1;
  723. if (un0 < -m_map->getDrawOrgX())
  724. un0=-m_map->getDrawOrgX();
  725. up1 = getXWithOrigin(i+1)+1;
  726. if (up1 >= m_map->getXExtent()-m_map->getDrawOrgX())
  727. up1=m_map->getXExtent()-m_map->getDrawOrgX()-1;
  728. Vector3 lightRay(0,0,0);
  729. //top-left sample
  730. l2r.Set(2*MAP_XY_FACTOR,0,MAP_HEIGHT_SCALE*(m_map->getDisplayHeight(getXWithOrigin(i)+1, getYWithOrigin(j)) - m_map->getDisplayHeight(un0, getYWithOrigin(j))));
  731. n2f.Set(0,2*MAP_XY_FACTOR,MAP_HEIGHT_SCALE*(m_map->getDisplayHeight(getXWithOrigin(i), (getYWithOrigin(j)+1)) - m_map->getDisplayHeight(getXWithOrigin(i), vn0)));
  732. #ifdef ALLOW_TEMPORARIES
  733. normalAtTexel= Normalize(Vector3::Cross_Product(l2r,n2f));
  734. #else
  735. Vector3::Normalized_Cross_Product(l2r, n2f, &normalAtTexel);
  736. #endif
  737. doTheDynamicLight(vb, vbMirror, &lightRay, &normalAtTexel, pLights, numLights);
  738. vb++; vbMirror++;
  739. //top-right sample
  740. l2r.Set(2*MAP_XY_FACTOR,0,MAP_HEIGHT_SCALE*(m_map->getDisplayHeight(up1 , getYWithOrigin(j) ) - m_map->getDisplayHeight(getXWithOrigin(i) , getYWithOrigin(j) )));
  741. n2f.Set(0,2*MAP_XY_FACTOR,MAP_HEIGHT_SCALE*(m_map->getDisplayHeight(getXWithOrigin(i)+1 , (getYWithOrigin(j)+1) ) - m_map->getDisplayHeight(getXWithOrigin(i)+1 , vn0 )));
  742. #ifdef ALLOW_TEMPORARIES
  743. normalAtTexel= Normalize(Vector3::Cross_Product(l2r,n2f));
  744. #else
  745. Vector3::Normalized_Cross_Product(l2r, n2f, &normalAtTexel);
  746. #endif
  747. doTheDynamicLight(vb, vbMirror, &lightRay, &normalAtTexel, pLights, numLights);
  748. vb++; vbMirror++;
  749. //bottom-right sample
  750. l2r.Set(2*MAP_XY_FACTOR,0,MAP_HEIGHT_SCALE*(m_map->getDisplayHeight(up1 , (getYWithOrigin(j)+1) ) - m_map->getDisplayHeight(getXWithOrigin(i) , (getYWithOrigin(j)+1) )));
  751. n2f.Set(0,2*MAP_XY_FACTOR,MAP_HEIGHT_SCALE*(m_map->getDisplayHeight(getXWithOrigin(i)+1 , vp1 ) - m_map->getDisplayHeight(getXWithOrigin(i)+1 , getYWithOrigin(j) )));
  752. #ifdef ALLOW_TEMPORARIES
  753. normalAtTexel= Normalize(Vector3::Cross_Product(l2r,n2f));
  754. #else
  755. Vector3::Normalized_Cross_Product(l2r, n2f, &normalAtTexel);
  756. #endif
  757. doTheDynamicLight(vb, vbMirror, &lightRay, &normalAtTexel, pLights, numLights);
  758. vb++; vbMirror++;
  759. //bottom-left sample
  760. l2r.Set(2*MAP_XY_FACTOR,0,MAP_HEIGHT_SCALE*(m_map->getDisplayHeight(getXWithOrigin(i)+1 , (getYWithOrigin(j)+1) ) - m_map->getDisplayHeight(un0 , (getYWithOrigin(j)+1) )));
  761. n2f.Set(0,2*MAP_XY_FACTOR,MAP_HEIGHT_SCALE*(m_map->getDisplayHeight(getXWithOrigin(i) , vp1 ) - m_map->getDisplayHeight(getXWithOrigin(i) , getYWithOrigin(j) )));
  762. #ifdef ALLOW_TEMPORARIES
  763. normalAtTexel= Normalize(Vector3::Cross_Product(l2r,n2f));
  764. #else
  765. Vector3::Normalized_Cross_Product(l2r, n2f, &normalAtTexel);
  766. #endif
  767. doTheDynamicLight(vb, vbMirror, &lightRay, &normalAtTexel, pLights, numLights);
  768. vb++; vbMirror++;
  769. }
  770. }
  771. return 0; //success.
  772. }
  773. return -1;
  774. }
  775. Int HeightMapRenderObjClass::updateVBForLightOptimized(DX8VertexBufferClass *pVB, char *data, Int x0, Int y0, Int x1, Int y1, Int originX, Int originY, W3DDynamicLight *pLights[], Int numLights)
  776. {
  777. Int i,j,k;
  778. Int vn0,un0,vp1,up1;
  779. Vector3 l2r,n2f,normalAtTexel;
  780. Int vertsPerRow=(VERTEX_BUFFER_TILE_LENGTH)*4; //vertices per row of VB
  781. if (m_vertexBufferTiles && m_map)
  782. {
  783. #ifdef _DEBUG
  784. assert(x0 >= originX && y0 >= originY && x1>x0 && y1>y0 && x1<=originX+VERTEX_BUFFER_TILE_LENGTH && y1<=originY+VERTEX_BUFFER_TILE_LENGTH);
  785. #endif
  786. DX8VertexBufferClass::WriteLockClass lockVtxBuffer(pVB);
  787. VERTEX_FORMAT *vBase = (VERTEX_FORMAT*)lockVtxBuffer.Get_Vertex_Array();
  788. VERTEX_FORMAT *vb;
  789. //
  790. // (gth) the optimization in this function is to take advantage of verts in the same
  791. // x,y position who have already computed their lighting. To do this, we need to set up
  792. // some offsets in the vertex buffer. I've computed these offsets to be consistent with
  793. // the formula's that Generals is using but in the case of the "half-res-mesh" I'm not
  794. // sure things are correct...
  795. //
  796. Int quad_right_offset;
  797. Int quad_below_offset;
  798. Int quad_below_right_offset;
  799. if (m_halfResMesh == false) {
  800. // offset = (j-originY)*vertsPerRow+4*(i-originX);
  801. quad_right_offset = 4;
  802. quad_below_offset = vertsPerRow;
  803. quad_below_right_offset = vertsPerRow + 4;
  804. } else {
  805. // offset = (j-originY)*vertsPerRow/4+2*(i-originX);
  806. quad_right_offset = 2;
  807. quad_below_offset = vertsPerRow/4;
  808. quad_below_right_offset = vertsPerRow/4 + 2;
  809. }
  810. //
  811. // i,j loop over the quads affected by the light. Each quad has its *own* 4 vertices. This
  812. // means that for any vertex position on the map, there are actually 4 copies of the vertex.
  813. //
  814. for (j=y0; j<y1; j++)
  815. {
  816. if (m_halfResMesh) {
  817. if (j&1) continue;
  818. }
  819. Int yCoord = getYWithOrigin(j)+m_map->getDrawOrgY()-m_map->getBorderSize();
  820. Bool intersect = false;
  821. for (k=0; k<numLights; k++) {
  822. if (pLights[k]->m_minY <= yCoord+1 &&
  823. pLights[k]->m_maxY >= yCoord) {
  824. intersect = true;
  825. }
  826. if (pLights[k]->m_prevMinY <= yCoord+1 &&
  827. pLights[k]->m_prevMaxY >= yCoord) {
  828. intersect = true;
  829. }
  830. }
  831. if (!intersect) {
  832. continue;
  833. }
  834. vn0 = getYWithOrigin(j)-1;
  835. if (vn0 < -m_map->getDrawOrgY())
  836. vn0=-m_map->getDrawOrgY();
  837. vp1 = getYWithOrigin(j+1)+1;
  838. if (vp1 >= m_map->getYExtent()-m_map->getDrawOrgY())
  839. vp1=m_map->getYExtent()-m_map->getDrawOrgY()-1;
  840. for (i=x0; i<x1; i++)
  841. {
  842. if (m_halfResMesh) {
  843. if (i&1) continue;
  844. }
  845. Int xCoord = getXWithOrigin(i)+m_map->getDrawOrgX()-m_map->getBorderSize();
  846. Bool intersect = false;
  847. for (k=0; k<numLights; k++) {
  848. if (pLights[k]->m_minX <= xCoord+1 &&
  849. pLights[k]->m_maxX >= xCoord &&
  850. pLights[k]->m_minY <= yCoord+1 &&
  851. pLights[k]->m_maxY >= yCoord) {
  852. intersect = true;
  853. }
  854. if (pLights[k]->m_prevMinX <= xCoord+1 &&
  855. pLights[k]->m_prevMaxX >= xCoord &&
  856. pLights[k]->m_prevMinY <= yCoord+1 &&
  857. pLights[k]->m_prevMaxY >= yCoord) {
  858. intersect = true;
  859. }
  860. }
  861. if (!intersect) {
  862. continue;
  863. }
  864. // vb is the pointer to the vertex in the hardware dx8 vertex buffer.
  865. Int offset = (j-originY)*vertsPerRow+4*(i-originX);
  866. if (m_halfResMesh) {
  867. offset = (j-originY)*vertsPerRow/4+2*(i-originX);
  868. }
  869. vb = vBase + offset; //skip to correct row in vertex buffer
  870. // vbMirror is the pointer to the vertex in our memory based copy.
  871. // The important point is that we can read out of our copy to get the original
  872. // diffuse color, and xyz location. It is VERY SLOW to read out of the
  873. // hardware vertex buffer, possibly worse... jba.
  874. VERTEX_FORMAT *vbMirror = ((VERTEX_FORMAT*)data) + offset;
  875. VERTEX_FORMAT *vbaseMirror = ((VERTEX_FORMAT*)data);
  876. un0 = getXWithOrigin(i)-1;
  877. if (un0 < -m_map->getDrawOrgX())
  878. un0=-m_map->getDrawOrgX();
  879. up1 = getXWithOrigin(i+1)+1;
  880. if (up1 >= m_map->getXExtent()-m_map->getDrawOrgX())
  881. up1=m_map->getXExtent()-m_map->getDrawOrgX()-1;
  882. Vector3 lightRay(0,0,0);
  883. //
  884. // (gth) Following the set of rules below lets us take advantage of lighting values that have
  885. // been previously computed. The idea is to copy them ahead to future quads that will need them
  886. // and then not compute them when we get to those quads. This also avoids having to read-back
  887. // from the vertex buffer but we do jump around in memory... probably bad anyway, maybe we should
  888. // compute into a temporary buffer and copy all at once...
  889. //
  890. unsigned long light_copy;
  891. // top-left sample -> only compute when i==0 and j==0
  892. if ((i==x0) && (j==y0)) {
  893. l2r.Set(2*MAP_XY_FACTOR,0,MAP_HEIGHT_SCALE*(m_map->getDisplayHeight(getXWithOrigin(i)+1, getYWithOrigin(j)) - m_map->getDisplayHeight(un0, getYWithOrigin(j))));
  894. n2f.Set(0,2*MAP_XY_FACTOR,MAP_HEIGHT_SCALE*(m_map->getDisplayHeight(getXWithOrigin(i), (getYWithOrigin(j)+1)) - m_map->getDisplayHeight(getXWithOrigin(i), vn0)));
  895. Vector3::Normalized_Cross_Product(l2r, n2f, &normalAtTexel);
  896. doTheDynamicLight(vb, vbMirror, &lightRay, &normalAtTexel, pLights, numLights);
  897. }
  898. vb++; vbMirror++;
  899. //top-right sample -> compute when j==0, then copy to (right,0)
  900. if (j==y0) {
  901. l2r.Set(2*MAP_XY_FACTOR,0,MAP_HEIGHT_SCALE*(m_map->getDisplayHeight(up1 , getYWithOrigin(j) ) - m_map->getDisplayHeight(getXWithOrigin(i) , getYWithOrigin(j) )));
  902. n2f.Set(0,2*MAP_XY_FACTOR,MAP_HEIGHT_SCALE*(m_map->getDisplayHeight(getXWithOrigin(i)+1 , (getYWithOrigin(j)+1) ) - m_map->getDisplayHeight(getXWithOrigin(i)+1 , vn0 )));
  903. Vector3::Normalized_Cross_Product(l2r, n2f, &normalAtTexel);
  904. light_copy = doTheDynamicLight(vb, vbMirror, &lightRay, &normalAtTexel, pLights, numLights);
  905. if (i < x1-1) {
  906. // copy light to (right,0)
  907. (vBase + offset + quad_right_offset)->diffuse = (light_copy&0x00FFFFFF) | ((vbaseMirror + offset + quad_right_offset)->diffuse&0xff000000) ;
  908. }
  909. }
  910. vb++; vbMirror++;
  911. //bottom-right sample -> always compute, then copy to (right,3), (down,1), (down+right,0)
  912. l2r.Set(2*MAP_XY_FACTOR,0,MAP_HEIGHT_SCALE*(m_map->getDisplayHeight(up1 , (getYWithOrigin(j)+1) ) - m_map->getDisplayHeight(getXWithOrigin(i) , (getYWithOrigin(j)+1) )));
  913. n2f.Set(0,2*MAP_XY_FACTOR,MAP_HEIGHT_SCALE*(m_map->getDisplayHeight(getXWithOrigin(i)+1 , vp1 ) - m_map->getDisplayHeight(getXWithOrigin(i)+1 , getYWithOrigin(j) )));
  914. Vector3::Normalized_Cross_Product(l2r, n2f, &normalAtTexel);
  915. light_copy = doTheDynamicLight(vb, vbMirror, &lightRay, &normalAtTexel, pLights, numLights);
  916. if (i < x1-1) {
  917. // copy light to (right,3)
  918. //(vBase + offset + quad_right_offset + 3)->diffuse = light_copy;
  919. (vBase + offset + quad_right_offset + 3)->diffuse = (light_copy&0x00FFFFFF) | ((vbaseMirror + offset + quad_right_offset + 3)->diffuse&0xff000000) ;
  920. }
  921. if (j < y1-1) {
  922. // copy light to (down,1)
  923. //(vBase + offset + quad_below_offset + 1)->diffuse = light_copy;
  924. (vBase + offset + quad_right_offset + 1)->diffuse = (light_copy&0x00FFFFFF) | ((vbaseMirror + offset + quad_right_offset + 1)->diffuse&0xff000000) ;
  925. }
  926. if ((i < x1-1) && (j < y1-1)) {
  927. // copy light to (right+down,0)
  928. //(vBase + offset + quad_below_right_offset)->diffuse = light_copy;
  929. (vBase + offset + quad_right_offset)->diffuse = (light_copy&0x00FFFFFF) | ((vbaseMirror + offset + quad_right_offset)->diffuse&0xff000000) ;
  930. }
  931. vb++; vbMirror++;
  932. //bottom-left sample -> compute when i==0, otherwise copy from (left,2)
  933. if (i==x0) {
  934. l2r.Set(2*MAP_XY_FACTOR,0,MAP_HEIGHT_SCALE*(m_map->getDisplayHeight(getXWithOrigin(i)+1 , (getYWithOrigin(j)+1) ) - m_map->getDisplayHeight(un0 , (getYWithOrigin(j)+1) )));
  935. n2f.Set(0,2*MAP_XY_FACTOR,MAP_HEIGHT_SCALE*(m_map->getDisplayHeight(getXWithOrigin(i) , vp1 ) - m_map->getDisplayHeight(getXWithOrigin(i) , getYWithOrigin(j) )));
  936. Vector3::Normalized_Cross_Product(l2r, n2f, &normalAtTexel);
  937. light_copy = doTheDynamicLight(vb, vbMirror, &lightRay, &normalAtTexel, pLights, numLights);
  938. if (j < y1-1) {
  939. // copy light to (down,0)
  940. //(vBase + offset + quad_below_offset)->diffuse = light_copy;
  941. (vBase + offset + quad_below_offset)->diffuse = (light_copy&0x00FFFFFF) | ((vbaseMirror + offset + quad_below_offset)->diffuse&0xff000000) ;
  942. }
  943. }
  944. vb++; vbMirror++;
  945. }
  946. }
  947. return 0; //success.
  948. }
  949. return -1;
  950. }
  951. //=============================================================================
  952. // HeightMapRenderObjClass::doPartialUpdate
  953. //=============================================================================
  954. /** Updates a partial block of vertices from [x0,y0 to x1,y1]
  955. The coordinates in partialRange are map cell coordinates, relative to the entire map.
  956. The vertex coordinates and texture coordinates, as well as static lighting are updated.
  957. */
  958. void HeightMapRenderObjClass::doPartialUpdate(const IRegion2D &partialRange, WorldHeightMap *htMap, RefRenderObjListIterator *pLightsIterator)
  959. {
  960. // Adjust range into the current drawn map range.
  961. Int minX = partialRange.lo.x - htMap->getDrawOrgX();
  962. Int maxX = partialRange.hi.x - htMap->getDrawOrgX();
  963. Int minY = partialRange.lo.y - htMap->getDrawOrgY();
  964. Int maxY = partialRange.hi.y - htMap->getDrawOrgY();
  965. if (minX<0) minX = 0;
  966. if (minY<0) minY = 0;
  967. if (maxX > m_x-1) maxX = m_x-1;
  968. if (maxY > m_y-1) maxY = m_y-1;
  969. if (maxX < minX) return;
  970. if (maxY < minY) return;
  971. if (m_originX == 0 && m_originY == 0) {
  972. // simple case.
  973. updateBlock(minX, minY, maxX, maxY,
  974. htMap, pLightsIterator);
  975. }
  976. else
  977. {
  978. minY = minY+m_originY;
  979. maxY = maxY+m_originY;
  980. if (minY> m_y-1) {
  981. minY -= m_y-1;
  982. maxY -= m_y-1;
  983. }
  984. if (maxY > m_y-1) {
  985. maxY -= m_y-1;
  986. updateBlock(0, minY, m_x-1, m_y-1, htMap, pLightsIterator);
  987. updateBlock(0, 0, m_x-1, maxY, htMap, pLightsIterator);
  988. } else {
  989. updateBlock(0, minY, m_x-1, maxY, htMap, pLightsIterator);
  990. }
  991. }
  992. if (!m_extraBlendTilePositions)
  993. { //Need to allocate memory
  994. m_extraBlendTilePositions = NEW Int[DEFAULT_MAX_MAP_EXTRABLEND_TILES];
  995. m_extraBlendTilePositionsSize = DEFAULT_MAX_MAP_EXTRABLEND_TILES;
  996. }
  997. //Find list of all extra blend tiles used on map. These are tiles with 3 materials/textures
  998. //over the same tile and require an extra render pass.
  999. Int i, j;
  1000. //First remove any existing extra blend tiles within this partial region
  1001. for (j=0; j<m_numExtraBlendTiles; j++)
  1002. { Int x = m_extraBlendTilePositions[j] & 0xffff;
  1003. Int y = m_extraBlendTilePositions[j] >> 16;
  1004. if (x >= partialRange.lo.x && x < partialRange.hi.x &&
  1005. y >= partialRange.lo.y && y < partialRange.hi.y)
  1006. { //this tile is inside region being updated so remove it by shifting tile array
  1007. memcpy(m_extraBlendTilePositions+j,m_extraBlendTilePositions+j+1,(m_numExtraBlendTiles-1-j)*sizeof(Int));
  1008. m_numExtraBlendTiles--;
  1009. j--; //need to look at index j again because this tile was removed
  1010. }
  1011. }
  1012. for (j=partialRange.lo.y; j<partialRange.hi.y; j++)
  1013. for (i=partialRange.lo.x; i<partialRange.hi.x; i++)
  1014. {
  1015. if (j<0 || i<0) continue;
  1016. Real U[4],V[4];
  1017. UnsignedByte alpha[4];
  1018. Bool flipState,cliffState;
  1019. if (htMap->getExtraAlphaUVData(i,j,U,V,alpha,&flipState, &cliffState))
  1020. { if (m_numExtraBlendTiles >= m_extraBlendTilePositionsSize)
  1021. { //no more room to store extra blend tiles so enlarge the buffer.
  1022. Int *tempPositions=NEW Int[m_extraBlendTilePositionsSize+512];
  1023. memcpy(tempPositions, m_extraBlendTilePositions, m_extraBlendTilePositionsSize*sizeof(Int));
  1024. delete [] m_extraBlendTilePositions;
  1025. //enlarge by more tiles to reduce memory trashing
  1026. m_extraBlendTilePositions = tempPositions;
  1027. m_extraBlendTilePositionsSize += 512;
  1028. }
  1029. //Pack x and y position into single integer since maps are limited in size
  1030. m_extraBlendTilePositions[m_numExtraBlendTiles]=i | (j <<16);
  1031. m_numExtraBlendTiles++;
  1032. }
  1033. }
  1034. updateShorelineTiles(partialRange.lo.x,partialRange.lo.y,partialRange.hi.x,partialRange.hi.y,htMap);
  1035. updateViewImpassableAreas(TRUE, minX, maxX, minY, maxY);
  1036. }
  1037. //=============================================================================
  1038. // HeightMapRenderObjClass::updateBlock
  1039. //=============================================================================
  1040. /** Updates a block of vertices from [x0,y0 to x1,y1]
  1041. The vertex coordinates and texture coordinates, as well as static lighting are updated.
  1042. */
  1043. Int HeightMapRenderObjClass::updateBlock(Int x0, Int y0, Int x1, Int y1, WorldHeightMap *pMap, RefRenderObjListIterator *pLightsIterator)
  1044. {
  1045. #ifdef _DEBUG
  1046. DEBUG_ASSERTCRASH(x0>=0&&y0>=0 && x1<m_x && y1<m_y && x0<=x1 && y0<=y1, ("Invalid updates."));
  1047. #endif
  1048. Invalidate_Cached_Bounding_Volumes();
  1049. if (pMap) {
  1050. REF_PTR_SET(m_stageZeroTexture, pMap->getTerrainTexture());
  1051. REF_PTR_SET(m_stageOneTexture, pMap->getAlphaTerrainTexture());
  1052. }
  1053. Int i,j;
  1054. DX8VertexBufferClass **pVB;
  1055. Int originX,originY;
  1056. //step through each vertex buffer that needs updating
  1057. for (j=0; j<m_numVBTilesY; j++)
  1058. {
  1059. originY=j*VERTEX_BUFFER_TILE_LENGTH; //location of this VB on the large full-size heightmap
  1060. Int yMin, yMax;
  1061. yMin = originY;
  1062. if (y0>yMin) yMin = y0;
  1063. yMax = originY+VERTEX_BUFFER_TILE_LENGTH;
  1064. if (y1<yMax) yMax = y1;
  1065. if (yMin >= yMax) {
  1066. continue;
  1067. }
  1068. for (i=0; i<m_numVBTilesX; i++)
  1069. {
  1070. originX=i*VERTEX_BUFFER_TILE_LENGTH; //location of this VB on the large full-size heightmap
  1071. Int xMin, xMax;
  1072. xMin = originX;
  1073. if (xMin<x0) xMin = x0;
  1074. xMax = originX+VERTEX_BUFFER_TILE_LENGTH;
  1075. if (xMax>x1) xMax = x1;
  1076. if (xMin >= xMax) {
  1077. continue;
  1078. }
  1079. pVB=m_vertexBufferTiles+j*m_numVBTilesX+i; //point to correct row/column of vertex buffers
  1080. char **pData = m_vertexBufferBackup+j*m_numVBTilesX+i;
  1081. updateVB(*pVB, *pData, xMin, yMin, xMax, yMax, originX, originY, pMap, pLightsIterator);
  1082. }
  1083. }
  1084. return 0;
  1085. }
  1086. //=============================================================================
  1087. // HeightMapRenderObjClass::getTileBoundingBox
  1088. //=============================================================================
  1089. /** Calculate the bounding box for given terrain tile. Right now each terrain tile is equivalent
  1090. to one of the vertex buffers holding heightmap vertices. */
  1091. //=============================================================================
  1092. AABoxClass & HeightMapRenderObjClass::getTileBoundingBox(AABoxClass *aabox, Int x, Int y)
  1093. {
  1094. Vector3 vmin;
  1095. Vector3 vmax;
  1096. Int xOffset = 0;
  1097. Int yOffset = 0;
  1098. if (m_map) {
  1099. xOffset = m_map->getDrawOrgX();
  1100. yOffset = m_map->getDrawOrgY();
  1101. }
  1102. vmin.Set(x*VERTEX_BUFFER_TILE_LENGTH+xOffset,y*VERTEX_BUFFER_TILE_LENGTH+yOffset,m_minHeight);
  1103. vmax.Set((x+1)*VERTEX_BUFFER_TILE_LENGTH+xOffset,(y+1)*VERTEX_BUFFER_TILE_LENGTH+yOffset,m_maxHeight);
  1104. aabox->Init_Min_Max(vmin,vmax);
  1105. return *aabox;
  1106. }
  1107. #ifdef DO_SCORCH
  1108. //=============================================================================
  1109. // HeightMapRenderObjClass::drawScorches
  1110. //=============================================================================
  1111. /** Draws the scorch marks. */
  1112. //=============================================================================
  1113. void HeightMapRenderObjClass::drawScorches(void)
  1114. {
  1115. updateScorches();
  1116. if (m_curNumScorchIndices == 0) {
  1117. return;
  1118. }
  1119. DX8Wrapper::Set_Index_Buffer(m_indexScorch,0);
  1120. DX8Wrapper::Set_Vertex_Buffer(m_vertexScorch);
  1121. DX8Wrapper::Set_Texture(0,m_scorchTexture);
  1122. if (Is_Hidden() == 0) {
  1123. DX8Wrapper::Draw_Triangles( 0,m_curNumScorchIndices/3, 0, m_curNumScorchVertices);
  1124. }
  1125. }
  1126. #endif
  1127. //-----------------------------------------------------------------------------
  1128. // Public Functions
  1129. //-----------------------------------------------------------------------------
  1130. //=============================================================================
  1131. // HeightMapRenderObjClass::~HeightMapRenderObjClass
  1132. //=============================================================================
  1133. /** Destructor. Releases w3d assets. */
  1134. //=============================================================================
  1135. HeightMapRenderObjClass::~HeightMapRenderObjClass(void)
  1136. {
  1137. freeMapResources();
  1138. if (m_treeBuffer) {
  1139. delete m_treeBuffer;
  1140. m_treeBuffer = NULL;
  1141. }
  1142. if (m_bibBuffer) {
  1143. delete m_bibBuffer;
  1144. m_bibBuffer = NULL;
  1145. }
  1146. #ifdef TEST_CUSTOM_EDGING
  1147. if (m_customEdging) {
  1148. delete m_customEdging;
  1149. m_customEdging = NULL;
  1150. }
  1151. #endif
  1152. #ifdef DO_ROADS
  1153. if (m_roadBuffer) {
  1154. delete m_roadBuffer;
  1155. m_roadBuffer = NULL;
  1156. }
  1157. #endif
  1158. if (m_bridgeBuffer) {
  1159. delete m_bridgeBuffer;
  1160. }
  1161. if( m_waypointBuffer )
  1162. {
  1163. delete m_waypointBuffer;
  1164. }
  1165. if (m_shroud) {
  1166. delete m_shroud;
  1167. }
  1168. if (m_extraBlendTilePositions)
  1169. delete [] m_extraBlendTilePositions;
  1170. if (m_shoreLineTilePositions)
  1171. delete [] m_shoreLineTilePositions;
  1172. }
  1173. //=============================================================================
  1174. // HeightMapRenderObjClass::HeightMapRenderObjClass
  1175. //=============================================================================
  1176. /** Constructor. Mostly nulls out the member variables. */
  1177. //=============================================================================
  1178. HeightMapRenderObjClass::HeightMapRenderObjClass(void)
  1179. {
  1180. m_x=0;
  1181. m_y=0;
  1182. m_needFullUpdate = false;
  1183. m_showImpassableAreas = false;
  1184. m_originX = 0;
  1185. m_originY = 0;
  1186. m_updating = false;
  1187. //Set height to the maximum value that can be stored.
  1188. //We should refine this with actual value.
  1189. m_maxHeight=(pow(256.0, sizeof(HeightSampleType))-1.0)*MAP_HEIGHT_SCALE;
  1190. m_minHeight=0;
  1191. m_numExtraBlendTiles=0;
  1192. m_extraBlendTilePositions=NULL;
  1193. m_extraBlendTilePositionsSize=0;
  1194. m_shoreLineTilePositions=NULL;
  1195. m_numShoreLineTiles=0;
  1196. m_shoreLineTilePositionsSize=0;
  1197. m_currentMinWaterOpacity = -1.0f;
  1198. m_numVertexBufferTiles=0;
  1199. m_indexBuffer=NULL;
  1200. m_vertexMaterialClass=NULL;
  1201. #ifdef PRE_TRANSFORM_VERTEX
  1202. m_xformedVertexBuffer = NULL;
  1203. #endif
  1204. m_stageZeroTexture=NULL;
  1205. m_stageOneTexture=NULL;
  1206. m_stageTwoTexture=NULL;
  1207. m_stageThreeTexture=NULL;
  1208. m_destAlphaTexture=NULL;
  1209. m_vertexBufferTiles=NULL;
  1210. m_vertexBufferBackup=NULL;
  1211. m_map=NULL;
  1212. m_depthFade.X = 0.0f;
  1213. m_depthFade.Y = 0.0f;
  1214. m_depthFade.Z = 0.0f;
  1215. m_useDepthFade = false;
  1216. m_disableTextures = false;
  1217. TheTerrainRenderObject = this;
  1218. m_treeBuffer = NULL;
  1219. m_treeBuffer = NEW W3DTreeBuffer;
  1220. m_bibBuffer = NULL;
  1221. m_bibBuffer = NEW W3DBibBuffer;
  1222. m_curImpassableSlope = 45.0f; // default to 45 degrees.
  1223. #ifdef TEST_CUSTOM_EDGING
  1224. m_customEdging = NULL;
  1225. m_customEdging = NEW W3DCustomEdging;
  1226. #endif
  1227. m_bridgeBuffer = NULL;
  1228. m_bridgeBuffer = NEW W3DBridgeBuffer;
  1229. m_waypointBuffer = NEW W3DWaypointBuffer;
  1230. #ifdef DO_ROADS
  1231. m_roadBuffer = NULL;
  1232. m_roadBuffer = NEW W3DRoadBuffer;
  1233. #endif
  1234. #ifdef DO_SCORCH
  1235. m_vertexScorch = NULL;
  1236. m_indexScorch = NULL;
  1237. m_scorchTexture = NULL;
  1238. clearAllScorches();
  1239. #endif
  1240. #if defined(_DEBUG) || defined(_INTERNAL)
  1241. if (TheGlobalData->m_shroudOn)
  1242. m_shroud = NEW W3DShroud;
  1243. else
  1244. m_shroud = NULL;
  1245. #else
  1246. m_shroud = NEW W3DShroud;
  1247. #endif
  1248. DX8Wrapper::SetCleanupHook(this);
  1249. }
  1250. //=============================================================================
  1251. // HeightMapRenderObjClass::adjustTerrainLOD
  1252. //=============================================================================
  1253. /** Adjust the terrain Level Of Detail. If adj > 0 , increases LOD 1 step, if
  1254. adj < 0 decreases it one step, if adj==0, then just sets up for the current LOD */
  1255. //=============================================================================
  1256. void HeightMapRenderObjClass::adjustTerrainLOD(Int adj)
  1257. {
  1258. if (adj>0 && TheGlobalData->m_terrainLOD<TERRAIN_LOD_MAX) TheWritableGlobalData->m_terrainLOD=(TerrainLOD)(TheGlobalData->m_terrainLOD+1);
  1259. if (adj<0 && TheGlobalData->m_terrainLOD>TERRAIN_LOD_MIN) TheWritableGlobalData->m_terrainLOD=(TerrainLOD)(TheGlobalData->m_terrainLOD-1);
  1260. switch (TheGlobalData->m_terrainLOD) {
  1261. case TERRAIN_LOD_MIN: TheWritableGlobalData->m_useCloudMap = false;
  1262. TheWritableGlobalData->m_useLightMap = false ;
  1263. TheWritableGlobalData->m_useWaterPlane = false;
  1264. TheWritableGlobalData->m_stretchTerrain = false;
  1265. TheWritableGlobalData->m_useHalfHeightMap = true;
  1266. break;
  1267. case TERRAIN_LOD_HALF_CLOUDS: TheWritableGlobalData->m_useCloudMap = true;
  1268. TheWritableGlobalData->m_useLightMap = true;
  1269. TheWritableGlobalData->m_useWaterPlane = false;
  1270. TheWritableGlobalData->m_stretchTerrain = false;
  1271. TheWritableGlobalData->m_useHalfHeightMap = true;
  1272. break;
  1273. case TERRAIN_LOD_STRETCH_NO_CLOUDS: TheWritableGlobalData->m_useCloudMap = false;
  1274. TheWritableGlobalData->m_useLightMap = false;
  1275. TheWritableGlobalData->m_useWaterPlane = false;
  1276. TheWritableGlobalData->m_stretchTerrain = true;
  1277. TheWritableGlobalData->m_useHalfHeightMap = false;
  1278. break;
  1279. case TERRAIN_LOD_STRETCH_CLOUDS: TheWritableGlobalData->m_useCloudMap = true;
  1280. TheWritableGlobalData->m_useLightMap = true;
  1281. TheWritableGlobalData->m_useWaterPlane = false;
  1282. TheWritableGlobalData->m_stretchTerrain = true;
  1283. TheWritableGlobalData->m_useHalfHeightMap = false;
  1284. break;
  1285. case TERRAIN_LOD_NO_CLOUDS: TheWritableGlobalData->m_useCloudMap = false;
  1286. TheWritableGlobalData->m_useLightMap = false;
  1287. TheWritableGlobalData->m_useWaterPlane = false;
  1288. TheWritableGlobalData->m_stretchTerrain = false;
  1289. TheWritableGlobalData->m_useHalfHeightMap = false;
  1290. break;
  1291. default:
  1292. case TERRAIN_LOD_NO_WATER: TheWritableGlobalData->m_useCloudMap = true;
  1293. TheWritableGlobalData->m_useLightMap = true;
  1294. TheWritableGlobalData->m_useWaterPlane = false;
  1295. TheWritableGlobalData->m_stretchTerrain = false;
  1296. TheWritableGlobalData->m_useHalfHeightMap = false;
  1297. break;
  1298. case TERRAIN_LOD_MAX: TheWritableGlobalData->m_useCloudMap = true;
  1299. TheWritableGlobalData->m_useLightMap = true;
  1300. TheWritableGlobalData->m_useWaterPlane = true;
  1301. TheWritableGlobalData->m_stretchTerrain = false;
  1302. TheWritableGlobalData->m_useHalfHeightMap = false;
  1303. break;
  1304. }
  1305. if (m_map==NULL) return;
  1306. m_map->setDrawOrg(m_map->getDrawOrgX(), m_map->getDrawOrgX());
  1307. if (m_shroud)
  1308. m_shroud->reset(); //need reset here since initHeightData will load new shroud.
  1309. this->initHeightData(m_map->getDrawWidth(),
  1310. m_map->getDrawHeight(), m_map, NULL);
  1311. staticLightingChanged();
  1312. if (TheTacticalView) {
  1313. TheTacticalView->setAngle(TheTacticalView->getAngle() + 1);
  1314. TheTacticalView->setAngle(TheTacticalView->getAngle() - 1);
  1315. }
  1316. }
  1317. //=============================================================================
  1318. // HeightMapRenderObjClass::ReleaseResources
  1319. //=============================================================================
  1320. /** Releases all w3d assets, to prepare for Reset device call. */
  1321. //=============================================================================
  1322. void HeightMapRenderObjClass::ReleaseResources(void)
  1323. {
  1324. if (m_treeBuffer) {
  1325. m_treeBuffer->freeTreeBuffers();
  1326. }
  1327. if (m_bibBuffer) {
  1328. m_bibBuffer->freeBibBuffers();
  1329. }
  1330. #ifdef TEST_CUSTOM_EDGING
  1331. m_customEdging ->freeEdgingBuffers();
  1332. #endif
  1333. if (m_bridgeBuffer) {
  1334. m_bridgeBuffer->freeBridgeBuffers();
  1335. }
  1336. if( m_waypointBuffer )
  1337. {
  1338. m_waypointBuffer->freeWaypointBuffers();
  1339. }
  1340. // We need to save the map.
  1341. WorldHeightMap *pMap=NULL;
  1342. REF_PTR_SET(pMap, m_map);
  1343. freeMapResources();
  1344. m_map = pMap; // ref_ptr_set has already incremented the ref count.
  1345. if (TheWaterRenderObj)
  1346. TheWaterRenderObj->ReleaseResources();
  1347. if (TheTerrainTracksRenderObjClassSystem)
  1348. TheTerrainTracksRenderObjClassSystem->ReleaseResources();
  1349. if (TheW3DShadowManager)
  1350. TheW3DShadowManager->ReleaseResources();
  1351. if (m_shroud)
  1352. { m_shroud->reset();
  1353. m_shroud->ReleaseResources();
  1354. }
  1355. //Release any resources that may be used by custom pixel/vertex shaders
  1356. W3DShaderManager::shutdown();
  1357. #ifdef DO_ROADS
  1358. if (m_roadBuffer) {
  1359. m_roadBuffer->freeRoadBuffers();
  1360. }
  1361. #endif
  1362. }
  1363. //=============================================================================
  1364. // HeightMapRenderObjClass::ReAcquireResources
  1365. //=============================================================================
  1366. /** Reallocates all W3D assets after a reset.. */
  1367. //=============================================================================
  1368. void HeightMapRenderObjClass::ReAcquireResources(void)
  1369. {
  1370. W3DShaderManager::init(); //reaquire resources which may be needed by custom shaders
  1371. if (TheWaterRenderObj)
  1372. TheWaterRenderObj->ReAcquireResources();
  1373. if (TheTerrainTracksRenderObjClassSystem)
  1374. TheTerrainTracksRenderObjClassSystem->ReAcquireResources();
  1375. if (TheW3DShadowManager)
  1376. TheW3DShadowManager->ReAcquireResources();
  1377. if (m_shroud)
  1378. m_shroud->ReAcquireResources();
  1379. Int x = m_x;
  1380. Int y = m_y;
  1381. if (m_map)
  1382. {
  1383. this->initHeightData(x,y,m_map, NULL);
  1384. // Tell lights to update next time through.
  1385. m_needFullUpdate = true;
  1386. }
  1387. if (m_treeBuffer) {
  1388. m_treeBuffer->allocateTreeBuffers();
  1389. }
  1390. if (m_bibBuffer) {
  1391. m_bibBuffer->allocateBibBuffers();
  1392. }
  1393. #ifdef TEST_CUSTOM_EDGING
  1394. m_customEdging ->allocateEdgingBuffers();
  1395. #endif
  1396. if (m_bridgeBuffer) {
  1397. m_bridgeBuffer->allocateBridgeBuffers();
  1398. }
  1399. //Waypoint buffers are done dynamically. One line, one node (just rendered multiple times accessing other data).
  1400. //Internally creates it if needed.
  1401. #ifdef DO_ROADS
  1402. if (m_roadBuffer) {
  1403. m_roadBuffer->allocateRoadBuffers();
  1404. m_roadBuffer->loadRoads();
  1405. }
  1406. #endif
  1407. if (TheTacticalView)
  1408. { TheTacticalView->forceRedraw(); //force map to update itself for the current camera position.
  1409. //for some reason we need to do it twice otherwise we sometimes end up with a black map until
  1410. //the player moves.
  1411. TheTacticalView->forceRedraw();
  1412. }
  1413. }
  1414. //=============================================================================
  1415. // HeightMapRenderObjClass::updateMacroTexture
  1416. //=============================================================================
  1417. /** Updates the macro noise/lightmap texture (pass 3) */
  1418. //=============================================================================
  1419. void HeightMapRenderObjClass::updateMacroTexture(AsciiString textureName)
  1420. {
  1421. m_macroTextureName = textureName;
  1422. // Release texture.
  1423. REF_PTR_RELEASE(m_stageThreeTexture);
  1424. // Reallocate texture.
  1425. m_stageThreeTexture=NEW LightMapTerrainTextureClass(m_macroTextureName);
  1426. }
  1427. //=============================================================================
  1428. // HeightMapRenderObjClass::reset
  1429. //=============================================================================
  1430. /** Updates the macro noise/lightmap texture (pass 3) */
  1431. //=============================================================================
  1432. void HeightMapRenderObjClass::reset(void)
  1433. {
  1434. if (m_treeBuffer) {
  1435. m_treeBuffer->clearAllTrees();
  1436. }
  1437. clearAllScorches();
  1438. #ifdef TEST_CUSTOM_EDGING
  1439. m_customEdging ->clearAllEdging();
  1440. #endif
  1441. #ifdef DO_ROADS
  1442. if (m_roadBuffer) {
  1443. m_roadBuffer->clearAllRoads();
  1444. }
  1445. #endif
  1446. if (m_bridgeBuffer) {
  1447. m_bridgeBuffer->clearAllBridges();
  1448. }
  1449. if (m_bibBuffer) {
  1450. m_bibBuffer->clearAllBibs();
  1451. }
  1452. m_showAsVisibleCliff.clear();
  1453. if (m_shroud)
  1454. { m_shroud->reset();
  1455. m_shroud->setBorderShroudLevel((W3DShroudLevel)TheGlobalData->m_shroudAlpha); //assume border is always black at start.
  1456. }
  1457. }
  1458. /**@todo: Ray intersection needs to be optimized with some sort of grid-tracing
  1459. (ala line drawing). We should also try making the search in a front->back order
  1460. relative to the ray so we can early exit as soon as we have a hit.
  1461. *
  1462. //=============================================================================
  1463. // HeightMapRenderObjClass::Cast_Ray
  1464. //=============================================================================
  1465. /** Return intersection of a ray with the heightmap mesh.
  1466. This is a quick version that just checks every polygon inside
  1467. a 2D bounding rectangle of the ray projected onto the heightfield plane.
  1468. For most of our view-picking cases the ray in almost perpendicular to the
  1469. map plane so this is very quick (small bounding box). But it can become slow
  1470. for arbitrary rays such as those used in AI visbility checks.(2 units on
  1471. opposite corners of the map would check every polygon in the map).
  1472. */
  1473. //=============================================================================
  1474. bool HeightMapRenderObjClass::Cast_Ray(RayCollisionTestClass & raytest)
  1475. {
  1476. TriClass tri;
  1477. Bool hit = false;
  1478. Int X,Y;
  1479. Vector3 normal,P0,P1,P2,P3;
  1480. if (!m_map)
  1481. return false; //need valid pointer to heightmap samples
  1482. //HeightSampleType *pData = m_map->getDataPtr();
  1483. //Clip ray to extents of heightfield
  1484. AABoxClass hbox;
  1485. LineSegClass lineseg,lineseg2;
  1486. CastResultStruct result;
  1487. Int StartCellX = 0;
  1488. Int EndCellX = 0;
  1489. Int StartCellY = 0;
  1490. Int EndCellY = 0;
  1491. const Int overhang = 2*VERTEX_BUFFER_TILE_LENGTH+m_map->getBorderSize(); // Allow picking past the edge for scrolling & objects.
  1492. Vector3 minPt(MAP_XY_FACTOR*(-overhang), MAP_XY_FACTOR*(-overhang), -MAP_XY_FACTOR);
  1493. Vector3 maxPt(MAP_XY_FACTOR*(m_map->getXExtent()+overhang),
  1494. MAP_XY_FACTOR*(m_map->getYExtent()+overhang), MAP_HEIGHT_SCALE*m_map->getMaxHeightValue()+MAP_XY_FACTOR);
  1495. MinMaxAABoxClass mmbox(minPt, maxPt);
  1496. hbox.Init(mmbox);
  1497. lineseg=raytest.Ray;
  1498. //Set initial ray endpoints
  1499. P0 = raytest.Ray.Get_P0();
  1500. P1 = raytest.Ray.Get_P1();
  1501. result.ComputeContactPoint=true;
  1502. Int p;
  1503. for (p=0; p<3; p++) {
  1504. //find intersection point of ray and terrain bounding box
  1505. result.Reset();
  1506. result.ComputeContactPoint=true;
  1507. if (CollisionMath::Collide(lineseg,hbox,&result))
  1508. { //ray intersects terrain or starts inside the terrain.
  1509. if (!result.StartBad) //check if start point inside terrain
  1510. P0 = result.ContactPoint; //make intersection point the new start of the ray.
  1511. //reverse direction of original ray and clip again to extent of
  1512. //heightmap
  1513. result.Fraction=1.0f; //reset the result
  1514. result.StartBad=false;
  1515. lineseg2.Set(lineseg.Get_P1(),lineseg.Get_P0()); //reverse line segment
  1516. if (CollisionMath::Collide(lineseg2,hbox,&result))
  1517. { if (!result.StartBad) //check if end point inside terrain
  1518. P1 = result.ContactPoint; //make intersection point the new end pont of ray
  1519. }
  1520. } else {
  1521. if (p==0) return(false);
  1522. break;
  1523. }
  1524. // Take the 2D bounding box of ray and check heights
  1525. // inside this box for intersection.
  1526. if (P0.X > P1.X) { //flip start/end points
  1527. StartCellX = REAL_TO_INT_FLOOR(P1.X/MAP_XY_FACTOR);
  1528. EndCellX = REAL_TO_INT_CEIL(P0.X/MAP_XY_FACTOR);
  1529. } else {
  1530. StartCellX = REAL_TO_INT_FLOOR(P0.X/MAP_XY_FACTOR);
  1531. EndCellX = REAL_TO_INT_CEIL(P1.X/MAP_XY_FACTOR);
  1532. }
  1533. if (P0.Y > P1.Y) { //flip start/end points
  1534. StartCellY = REAL_TO_INT_FLOOR(P1.Y/MAP_XY_FACTOR);
  1535. EndCellY = REAL_TO_INT_CEIL(P0.Y/MAP_XY_FACTOR);
  1536. } else {
  1537. StartCellY = REAL_TO_INT_FLOOR(P0.Y/MAP_XY_FACTOR);
  1538. EndCellY = REAL_TO_INT_CEIL(P1.Y/MAP_XY_FACTOR);
  1539. }
  1540. Int i, j, minHt, maxHt;
  1541. minHt = m_map->getMaxHeightValue();
  1542. maxHt = 0;
  1543. for (j=StartCellY; j<=EndCellY; j++) {
  1544. for (i=StartCellX; i<=EndCellX; i++) {
  1545. Short cur = getClipHeight(i+m_map->getBorderSize(),j+m_map->getBorderSize());
  1546. if (cur<minHt) minHt = cur;
  1547. if (maxHt<cur) maxHt = cur;
  1548. }
  1549. }
  1550. Vector3 minPt(MAP_XY_FACTOR*(StartCellX-1), MAP_XY_FACTOR*(StartCellY-1), MAP_HEIGHT_SCALE*(minHt-1));
  1551. Vector3 maxPt(MAP_XY_FACTOR*(EndCellX+1), MAP_XY_FACTOR*(EndCellY+1), MAP_HEIGHT_SCALE*(maxHt+1));
  1552. MinMaxAABoxClass mmbox(minPt, maxPt);
  1553. hbox.Init(mmbox);
  1554. }
  1555. raytest.Result->ComputeContactPoint=true; //tell CollisionMath that we need point.
  1556. // Adjust indexes into the bordered height map.
  1557. StartCellX += m_map->getBorderSize();
  1558. EndCellX += m_map->getBorderSize();
  1559. StartCellY += m_map->getBorderSize();
  1560. EndCellY += m_map->getBorderSize();
  1561. Int offset;
  1562. for (offset = 1; offset < 5; offset *= 3) {
  1563. for (Y=StartCellY-offset; Y<=EndCellY+offset; Y++) {
  1564. //if (Y<0) continue;
  1565. //if (Y>=m_map->getYExtent()-1) continue;
  1566. for (X=StartCellX-offset; X<=EndCellX+offset; X++) {
  1567. //test the 2 triangles in this cell
  1568. // 3-----2
  1569. // | /|
  1570. // | / |
  1571. // |/ |
  1572. // 0-----1
  1573. //bottom triangle first
  1574. P0.X=ADJUST_FROM_INDEX_TO_REAL(X);
  1575. P0.Y=ADJUST_FROM_INDEX_TO_REAL(Y);
  1576. P0.Z=MAP_HEIGHT_SCALE*(float)getClipHeight(X, Y);
  1577. P1.X=ADJUST_FROM_INDEX_TO_REAL(X+1);
  1578. P1.Y=ADJUST_FROM_INDEX_TO_REAL(Y);
  1579. P1.Z=MAP_HEIGHT_SCALE*(float)getClipHeight(X+1, Y);
  1580. P2.X=ADJUST_FROM_INDEX_TO_REAL(X+1);
  1581. P2.Y=ADJUST_FROM_INDEX_TO_REAL(Y+1);
  1582. P2.Z=MAP_HEIGHT_SCALE*(float)getClipHeight(X+1, Y+1);
  1583. P3.X=ADJUST_FROM_INDEX_TO_REAL(X);
  1584. P3.Y=ADJUST_FROM_INDEX_TO_REAL(Y+1);
  1585. P3.Z=MAP_HEIGHT_SCALE*(float)getClipHeight(X, Y+1);
  1586. tri.V[0] = &P0;
  1587. tri.V[1] = &P1;
  1588. tri.V[2] = &P2;
  1589. tri.N = &normal;
  1590. tri.Compute_Normal();
  1591. hit = hit || (Bool)CollisionMath::Collide(raytest.Ray, tri, raytest.Result);
  1592. if (raytest.Result->StartBad)
  1593. return true;
  1594. //top triangle
  1595. tri.V[0] = &P2;
  1596. tri.V[1] = &P3;
  1597. tri.V[2] = &P0;
  1598. tri.N = &normal;
  1599. tri.Compute_Normal();
  1600. hit = hit || (Bool)CollisionMath::Collide(raytest.Ray, tri, raytest.Result);
  1601. if (hit)
  1602. raytest.Result->SurfaceType = SURFACE_TYPE_DEFAULT; ///@todo: WW3D uses this to return dirt, grass, etc. Do we need this?
  1603. }
  1604. // Don't break. It is possible to intersect 2 triangles, and the second is closer. if (hit) break;
  1605. }
  1606. // Don't break. It is possible to intersect 2 triangles, and the second is closer. if (hit) break;
  1607. }
  1608. return hit;
  1609. }
  1610. //=============================================================================
  1611. // HeightMapRenderObjClass::getHeightMapHeight
  1612. //=============================================================================
  1613. /** return the height and normal of the triangle plane containing given location within heightmap. */
  1614. //=============================================================================
  1615. Real HeightMapRenderObjClass::getHeightMapHeight(Real x, Real y, Coord3D* normal) const
  1616. {
  1617. if (m_map == NULL)
  1618. {
  1619. if (normal)
  1620. {
  1621. // return a default normal pointing up
  1622. normal->x = 0.0f;
  1623. normal->y = 0.0f;
  1624. normal->z = 1.0f;
  1625. }
  1626. return 0;
  1627. }
  1628. float height;
  1629. // 3-----2
  1630. // | /|
  1631. // | / |
  1632. // |/ |
  1633. // 0-----1
  1634. //Find surrounding grid points
  1635. const Real MAP_XY_FACTOR_INV = 1.0f / MAP_XY_FACTOR;
  1636. float xdiv = x * MAP_XY_FACTOR_INV;
  1637. float ydiv = y * MAP_XY_FACTOR_INV;
  1638. float ixf = floorf(xdiv);
  1639. float iyf = floorf(ydiv);
  1640. float fx = xdiv - ixf; //get fraction
  1641. float fy = ydiv - iyf; //get fraction
  1642. // since ixf & iyf are already floor'ed, we can use the fastest f->i conversion we have...
  1643. Int ix = REAL_TO_INT_FLOOR(ixf) + m_map->getBorderSize();
  1644. Int iy = REAL_TO_INT_FLOOR(iyf) + m_map->getBorderSize();
  1645. Int xExtent = m_map->getXExtent();
  1646. // Check for extent-3, not extent-1: we go into the next row/column of data for smoothed triangle points, so extent-1
  1647. // goes off the end...
  1648. if (ix > (xExtent-3) || iy > (m_map->getYExtent()-3) || iy < 1 || ix < 1)
  1649. {
  1650. // sample point is not on the heightmap
  1651. if (normal)
  1652. {
  1653. // return a default normal pointing up
  1654. normal->x = 0.0f;
  1655. normal->y = 0.0f;
  1656. normal->z = 1.0f;
  1657. }
  1658. return getClipHeight(ix, iy) * MAP_HEIGHT_SCALE;
  1659. }
  1660. const UnsignedByte* data = m_map->getDataPtr();
  1661. int idx = ix + iy*xExtent;
  1662. float p0 = data[idx];
  1663. float p2 = data[idx + xExtent + 1];
  1664. if (fy > fx) // test if we are in the upper triangle
  1665. {
  1666. float p3 = data[idx + xExtent];
  1667. height = (p3 + (1.0f-fy)*(p0-p3) + fx*(p2-p3)) * MAP_HEIGHT_SCALE;
  1668. }
  1669. else
  1670. {
  1671. // we are in the lower triangle
  1672. float p1 = data[idx + 1];
  1673. height = (p1 + fy*(p2-p1) + (1.0f-fx)*(p0-p1)) * MAP_HEIGHT_SCALE;
  1674. }
  1675. if (normal) {
  1676. // 9 8
  1677. //
  1678. //10 3-----2 7
  1679. // | /|
  1680. // | / |
  1681. // |/ |
  1682. //11 0-----1 6
  1683. //
  1684. // 4 5
  1685. //Find surrounding grid points for smoothed normals.
  1686. int idx4 = ix + (iy-1)*xExtent;
  1687. int idx0 = ix + iy*xExtent;
  1688. int idx3 = ix + iy*xExtent+xExtent;
  1689. int idx9 = ix + (iy+2)*xExtent;
  1690. UnsignedByte d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11;
  1691. d0 = data[idx0];
  1692. d1 = data[idx0+1];
  1693. d2 = data[idx3+1];
  1694. d3 = data[idx3];
  1695. d4 = data[idx4];
  1696. d5 = data[idx4+1];
  1697. d6 = data[idx0+2];
  1698. d7 = data[idx3+2];
  1699. d8 = data[idx9+1];
  1700. d9 = data[idx9];
  1701. d10 = data[idx3-1];
  1702. d11 = data[idx0-1];
  1703. Real deltaZ_X0 = d1-d11;
  1704. Real deltaZ_X1 = d6-d0;
  1705. Real deltaZ_X2 = d7-d3;
  1706. Real deltaZ_X3 = d6-d0;
  1707. Real deltaZ_Y0 = d3-d4;
  1708. Real deltaZ_Y1 = d2-d5;
  1709. Real deltaZ_Y2 = d8-d1;
  1710. Real deltaZ_Y3 = d9-d0;
  1711. // Interpolate to get the smoothed valued.
  1712. Real deltaZ_X_Left = deltaZ_X0*(1.0f-fx) + fx*deltaZ_X3;
  1713. Real deltaZ_X_Right = deltaZ_X1*(1.0f-fx) + fx*deltaZ_X2;
  1714. Real deltaZ_X = deltaZ_X_Left*(1.0-fy) + fy*deltaZ_X_Right;
  1715. Real deltaZ_Y_Left = deltaZ_Y0*(1.0f-fx) + fx*deltaZ_Y3;
  1716. Real deltaZ_Y_Right = deltaZ_Y1*(1.0f-fx) + fx*deltaZ_Y2;
  1717. Real deltaZ_Y = deltaZ_Y_Left*(1.0-fy) + fy*deltaZ_Y_Right;
  1718. Vector3 l2r, n2f, normalAtTexel;
  1719. l2r.Set(2*MAP_XY_FACTOR/MAP_HEIGHT_SCALE, 0, deltaZ_X);
  1720. n2f.Set(0, 2*MAP_XY_FACTOR/MAP_HEIGHT_SCALE, deltaZ_Y);
  1721. Vector3::Normalized_Cross_Product(l2r,n2f, &normalAtTexel);
  1722. normal->x = normalAtTexel.X;
  1723. normal->y = normalAtTexel.Y;
  1724. normal->z = normalAtTexel.Z;
  1725. }
  1726. return height;
  1727. }
  1728. //=============================================================================
  1729. Bool HeightMapRenderObjClass::isClearLineOfSight(const Coord3D& pos, const Coord3D& posOther) const
  1730. {
  1731. if (m_map == NULL)
  1732. return false; // doh. should not happen.
  1733. #define DO_BRESENHAM
  1734. #ifdef DO_BRESENHAM
  1735. /*
  1736. this is WAY faster, though not quite as accurate... however, the inaccuracy
  1737. is pretty minimal, so we really should force other code to live with it. (srj)
  1738. */
  1739. const Real MAP_XY_FACTOR_INV = 1.0f / MAP_XY_FACTOR;
  1740. Int borderSize = m_map->getBorderSize();
  1741. Int start_x = REAL_TO_INT_FLOOR(pos.x * MAP_XY_FACTOR_INV) + borderSize;
  1742. Int start_y = REAL_TO_INT_FLOOR(pos.y * MAP_XY_FACTOR_INV) + borderSize;
  1743. Int end_x = REAL_TO_INT_FLOOR(posOther.x * MAP_XY_FACTOR_INV) + borderSize;
  1744. Int end_y = REAL_TO_INT_FLOOR(posOther.y * MAP_XY_FACTOR_INV) + borderSize;
  1745. Int delta_x = abs(end_x - start_x); // The difference between the x's
  1746. Int delta_y = abs(end_y - start_y); // The difference between the y's
  1747. Int x = start_x; // Start x off at the first pixel
  1748. Int y = start_y; // Start y off at the first pixel
  1749. Int xinc1, xinc2;
  1750. if (end_x >= start_x) // The x-values are increasing
  1751. {
  1752. xinc1 = 1;
  1753. xinc2 = 1;
  1754. }
  1755. else // The x-values are decreasing
  1756. {
  1757. xinc1 = -1;
  1758. xinc2 = -1;
  1759. }
  1760. Int yinc1, yinc2;
  1761. if (end_y >= start_y) // The y-values are increasing
  1762. {
  1763. yinc1 = 1;
  1764. yinc2 = 1;
  1765. }
  1766. else // The y-values are decreasing
  1767. {
  1768. yinc1 = -1;
  1769. yinc2 = -1;
  1770. }
  1771. Int den, num, numadd, numpixels;
  1772. Bool checkY = true;
  1773. if (delta_x >= delta_y) // There is at least one x-value for every y-value
  1774. {
  1775. xinc1 = 0; // Don't change the x when numerator >= denominator
  1776. yinc2 = 0; // Don't change the y for every iteration
  1777. den = delta_x;
  1778. num = delta_x / 2;
  1779. numadd = delta_y;
  1780. numpixels = delta_x; // There are more x-values than y-values
  1781. }
  1782. else // There is at least one y-value for every x-value
  1783. {
  1784. checkY = false;
  1785. xinc2 = 0; // Don't change the x for every iteration
  1786. yinc1 = 0; // Don't change the y when numerator >= denominator
  1787. den = delta_y;
  1788. num = delta_y / 2;
  1789. numadd = delta_x;
  1790. numpixels = delta_y; // There are more y-values than x-values
  1791. }
  1792. Real nsInv = 1.0f / numpixels;
  1793. Real z = pos.z;
  1794. Real dz = posOther.z - z;
  1795. Real zinc = dz * nsInv;
  1796. Bool result = true;
  1797. const UnsignedByte* data = m_map->getDataPtr();
  1798. Int xExtent = m_map->getXExtent();
  1799. Int yExtent = m_map->getYExtent();
  1800. for (Int curpixel = 0; curpixel < numpixels; curpixel++)
  1801. {
  1802. if (x < 0 ||
  1803. y < 0 ||
  1804. x >= xExtent-1 ||
  1805. y >= yExtent-1)
  1806. {
  1807. // once we go off the map, we're done
  1808. break;
  1809. }
  1810. Int idx = x + y*xExtent;
  1811. float height = data[idx];
  1812. height = __max(height, data[idx + 1]);
  1813. height = __max(height, data[idx + xExtent]);
  1814. height = __max(height, data[idx + xExtent + 1]);
  1815. height *= MAP_HEIGHT_SCALE;
  1816. // if terrainHeight > z, we can't see, so punt.
  1817. // add a little fudge to account for slop.
  1818. const Real LOS_FUDGE = 0.5f;
  1819. if (height > z + LOS_FUDGE)
  1820. {
  1821. result = false;
  1822. break;
  1823. }
  1824. // we're above the max height of the terrain and still looking up, so we're done.
  1825. // (don't bother for reverse test, since that doesn't generally happen)
  1826. if (z >= getMaxHeight() && zinc > 0.0f)
  1827. {
  1828. break;
  1829. }
  1830. z += zinc;
  1831. // continue with the maintenance.
  1832. num += numadd; // Increase the numerator by the top of the fraction
  1833. if (num >= den) // Check if numerator >= denominator
  1834. {
  1835. num -= den; // Calculate the new numerator value
  1836. x += xinc1; // Change the x as appropriate
  1837. y += yinc1; // Change the y as appropriate
  1838. }
  1839. x += xinc2; // Change the x as appropriate
  1840. y += yinc2; // Change the y as appropriate
  1841. }
  1842. return result;
  1843. #else
  1844. // walk a line from obj to objOther and
  1845. // find the highest point in between 'em. while
  1846. // we're doing this, also estimate the point on the
  1847. // line at the same x,y as the high-terrain-point.
  1848. Real fx = pos.x;
  1849. Real fy = pos.y;
  1850. Real fz = pos.z;
  1851. Real fdx = posOther.x - fx;
  1852. Real fdy = posOther.y - fy;
  1853. Real fdz = posOther.z - fz;
  1854. // What's the largest step size that will be accurate enough?
  1855. // Currently we use a step size of about 2 "feet", which
  1856. // seems acceptable accuracy. If performance here is inadequate,
  1857. // we can try increasing the step size, but be sure to retest
  1858. // accuracy.
  1859. Real len = ceilf(sqrtf(fdx*fdx + fdy*fdy));
  1860. const Real STEP_LEN = 2.0f;
  1861. Int numSteps = REAL_TO_INT_CEIL(len / STEP_LEN);
  1862. if (numSteps < 1) numSteps = 1;
  1863. Real fnsInv = 1.0f / numSteps;
  1864. Real fxinc = fdx * fnsInv;
  1865. Real fyinc = fdy * fnsInv;
  1866. Real fzinc = fdz * fnsInv;
  1867. while (numSteps--)
  1868. {
  1869. Real terrainHeight = getHeightMapHeight( fx, fy, NULL );
  1870. // if terrainHeight > fz, we can't see, so punt.
  1871. // add a little fudge to account for slop.
  1872. const Real LOS_FUDGE = 0.5f;
  1873. if (terrainHeight > fz + LOS_FUDGE)
  1874. {
  1875. return false;
  1876. }
  1877. // we're above the max height of the terrain and still looking up, so we're done.
  1878. // (don't bother for reverse test, since that doesn't generally happen)
  1879. if (fz >= getMaxHeight() && fzinc > 0.0f)
  1880. {
  1881. return true;
  1882. }
  1883. fx += fxinc;
  1884. fy += fyinc;
  1885. fz += fzinc;
  1886. }
  1887. return true;
  1888. #endif
  1889. }
  1890. //=============================================================================
  1891. // HeightMapRenderObjClass::getMaxCellHeight
  1892. //=============================================================================
  1893. /** Returns maximum height of the 4 corners containing the given point */
  1894. //=============================================================================
  1895. Real HeightMapRenderObjClass::getMaxCellHeight(Real x, Real y) const
  1896. {
  1897. float p0,p1,p2,p3;
  1898. float height;
  1899. // 3-----2
  1900. // | /|
  1901. // | / |
  1902. // |/ |
  1903. // 0-----1
  1904. //Find surrounding grid points
  1905. if (m_map == NULL)
  1906. { //sample point is not on the heightmap
  1907. return 0.0f; //return default height
  1908. }
  1909. Int offset = 1;
  1910. Int iX = x/MAP_XY_FACTOR;
  1911. Int iY = y/MAP_XY_FACTOR;
  1912. iX += m_map->getBorderSize();
  1913. iY += m_map->getBorderSize();
  1914. if (iX<0) iX = 0;
  1915. if (iY<0) iY = 0;
  1916. if (iX >= (m_map->getXExtent()-1)) {
  1917. iX = m_map->getXExtent()-2;
  1918. }
  1919. if (iY >= (m_map->getYExtent()-1)) {
  1920. iY = m_map->getYExtent()-2;
  1921. }
  1922. if (m_halfResMesh) {
  1923. offset = 2;
  1924. iX = (iX/2)*2;
  1925. iY = (iY/2)*2;
  1926. }
  1927. UnsignedByte *data = m_map->getDataPtr();
  1928. p0=data[iX+iY*m_map->getXExtent()]*MAP_HEIGHT_SCALE;
  1929. p1=data[(iX+offset)+iY*m_map->getXExtent()]*MAP_HEIGHT_SCALE;
  1930. p2=data[(iX+offset)+(iY+offset)*m_map->getXExtent()]*MAP_HEIGHT_SCALE;
  1931. p3=data[iX+(iY+offset)*m_map->getXExtent()]*MAP_HEIGHT_SCALE;
  1932. height=p0;
  1933. height=__max(height,p1);
  1934. height=__max(height,p2);
  1935. height=__max(height,p3);
  1936. return height;
  1937. }
  1938. //=============================================================================
  1939. // HeightMapRenderObjClass::isCliffCell
  1940. //=============================================================================
  1941. /** Returns true if the cell containing the point is a cliff cell */
  1942. //=============================================================================
  1943. Bool HeightMapRenderObjClass::isCliffCell(Real x, Real y)
  1944. {
  1945. if (m_map == NULL)
  1946. { //sample point is not on the heightmap
  1947. return false;
  1948. }
  1949. Int iX = x/MAP_XY_FACTOR;
  1950. Int iY = y/MAP_XY_FACTOR;
  1951. iX += m_map->getBorderSize();
  1952. iY += m_map->getBorderSize();
  1953. if (iX<0) iX = 0;
  1954. if (iY<0) iY = 0;
  1955. if (iX >= (m_map->getXExtent()-1)) {
  1956. iX = m_map->getXExtent()-2;
  1957. }
  1958. if (iY >= (m_map->getYExtent()-1)) {
  1959. iY = m_map->getYExtent()-2;
  1960. }
  1961. return m_map->getCliffState(iX, iY);
  1962. }
  1963. //=============================================================================
  1964. //=============================================================================
  1965. Bool HeightMapRenderObjClass::showAsVisibleCliff(Int xIndex, Int yIndex) const
  1966. {
  1967. Int xSize = m_map->getXExtent();
  1968. return m_showAsVisibleCliff[xIndex + yIndex * xSize];
  1969. }
  1970. //=============================================================================
  1971. //=============================================================================
  1972. Bool HeightMapRenderObjClass::evaluateAsVisibleCliff(Int xIndex, Int yIndex, Real valuesGreaterThanRad)
  1973. {
  1974. // This one never changes, so don't bother recomputing it.
  1975. static const Real distance[4] =
  1976. {
  1977. 0.0f,
  1978. 1.0 * MAP_XY_FACTOR,
  1979. sqrt(2.0f) * MAP_XY_FACTOR,
  1980. 1.0 * MAP_XY_FACTOR,
  1981. };
  1982. // Note: getHeight will protect us from going out of bounds by returning 0 if we give it
  1983. // a value outside of its bounds.
  1984. UnsignedByte bytes[4] =
  1985. {
  1986. m_map->getHeight(xIndex + 0, yIndex + 0),
  1987. m_map->getHeight(xIndex + 1, yIndex + 0),
  1988. m_map->getHeight(xIndex + 1, yIndex + 1),
  1989. m_map->getHeight(xIndex + 0, yIndex + 1),
  1990. };
  1991. Real heights[4] =
  1992. {
  1993. INT_TO_REAL(bytes[0]) * MAP_HEIGHT_SCALE,
  1994. INT_TO_REAL(bytes[1]) * MAP_HEIGHT_SCALE,
  1995. INT_TO_REAL(bytes[2]) * MAP_HEIGHT_SCALE,
  1996. INT_TO_REAL(bytes[3]) * MAP_HEIGHT_SCALE,
  1997. };
  1998. Bool anyImpassable = FALSE;
  1999. for (Int i = 1; i < 4 && !anyImpassable; ++i) {
  2000. if (fabs((heights[i] - heights[0]) / distance[i]) > valuesGreaterThanRad) {
  2001. anyImpassable = TRUE;
  2002. }
  2003. }
  2004. return anyImpassable;
  2005. }
  2006. //=============================================================================
  2007. // HeightMapRenderObjClass::oversizeTerrain
  2008. //=============================================================================
  2009. /** Sets the terrain oversize amount. */
  2010. //=============================================================================
  2011. void HeightMapRenderObjClass::oversizeTerrain(Int tilesToOversize)
  2012. {
  2013. Int width = WorldHeightMap::NORMAL_DRAW_WIDTH;
  2014. Int height = WorldHeightMap::NORMAL_DRAW_HEIGHT;
  2015. if (tilesToOversize>0 && tilesToOversize<5)
  2016. {
  2017. width += 32*tilesToOversize;
  2018. height += 32*tilesToOversize;
  2019. if (width>m_map->getXExtent())
  2020. width = m_map->getXExtent();
  2021. if (height>m_map->getYExtent())
  2022. height = m_map->getYExtent();
  2023. }
  2024. Int dx = width-m_map->getDrawWidth();
  2025. Int dy = height-m_map->getDrawHeight();
  2026. m_map->setDrawWidth(width);
  2027. m_map->setDrawHeight(height);
  2028. dx /= 2;
  2029. dy /= 2;
  2030. Int newOrgX = m_map->getDrawOrgX()-dx;
  2031. Int newOrgy = m_map->getDrawOrgY()-dy;
  2032. if (newOrgX<0) newOrgX=0;
  2033. if (newOrgy<0) newOrgy=0;
  2034. m_map->setDrawOrg(newOrgX,newOrgy);
  2035. m_originX = 0;
  2036. m_originY = 0;
  2037. if (m_shroud)
  2038. m_shroud->reset();
  2039. //delete m_shroud;
  2040. //m_shroud = NULL;
  2041. initHeightData(m_map->getDrawWidth(), m_map->getDrawHeight(), m_map, NULL);
  2042. m_needFullUpdate = true;
  2043. }
  2044. //=============================================================================
  2045. // HeightMapRenderObjClass::Get_Obj_Space_Bounding_Sphere
  2046. //=============================================================================
  2047. /** WW3D method that returns object bounding sphere used in frustum culling*/
  2048. //=============================================================================
  2049. void HeightMapRenderObjClass::Get_Obj_Space_Bounding_Sphere(SphereClass & sphere) const
  2050. {
  2051. Vector3 ObjSpaceCenter((float)m_x*0.5f*MAP_XY_FACTOR,(float)m_y*0.5f*MAP_XY_FACTOR,(float)m_minHeight+(m_maxHeight-m_minHeight)*0.5f);
  2052. float length = ObjSpaceCenter.Length();
  2053. if (m_map) {
  2054. ObjSpaceCenter.X += m_map->getDrawOrgX()*MAP_XY_FACTOR;
  2055. ObjSpaceCenter.Y += m_map->getDrawOrgY()*MAP_XY_FACTOR;
  2056. }
  2057. sphere.Init(ObjSpaceCenter, length);
  2058. }
  2059. //=============================================================================
  2060. // HeightMapRenderObjClass::Get_Obj_Space_Bounding_Box
  2061. //=============================================================================
  2062. /** WW3D method that returns object bounding box used in collision detection*/
  2063. //=============================================================================
  2064. void HeightMapRenderObjClass::Get_Obj_Space_Bounding_Box(AABoxClass & box) const
  2065. {
  2066. Vector3 minPt(0,0,m_minHeight);
  2067. Vector3 maxPt((float)m_x*MAP_XY_FACTOR,(float)m_y*MAP_XY_FACTOR,(float)m_maxHeight);
  2068. if (m_map) {
  2069. minPt.X += m_map->getDrawOrgX()*MAP_XY_FACTOR;
  2070. minPt.Y += m_map->getDrawOrgY()*MAP_XY_FACTOR;
  2071. maxPt.X += m_map->getDrawOrgX()*MAP_XY_FACTOR;
  2072. maxPt.Y += m_map->getDrawOrgY()*MAP_XY_FACTOR;
  2073. }
  2074. MinMaxAABoxClass minMaxBox(minPt, maxPt);
  2075. box.Init(minMaxBox);
  2076. }
  2077. //-------------------------------------------------------------------------------------------------
  2078. /** Get the 3D extent of the terrain visible through the camera. Return value
  2079. is false if no part of terrain is visible. This function returns a worse
  2080. case bounding volume based on lowest/highest points in entire terrain. It
  2081. does not optimize the volume to heights actually visible. Unlike some of
  2082. the other methods, this function is guaranteed not to miss any visible
  2083. polygons. The ignoreMaxHeight flag is used to return a box that uses the
  2084. camera position as the maximum height instead of the terrain - good for getting
  2085. a volume enclosing things that can float above terrain.
  2086. */
  2087. //-------------------------------------------------------------------------------------------------
  2088. Bool HeightMapRenderObjClass::getMaximumVisibleBox(const FrustumClass &frustum, AABoxClass *box, Bool ignoreMaxHeight)
  2089. {
  2090. //create a plane from the lowest point on the terrain
  2091. PlaneClass groundPlane(Vector3(0,0,1), m_minHeight);
  2092. //clip each side of the view frustum to ground plane
  2093. float clipFraction;
  2094. Vector3 ClippedCorners[8];
  2095. ClippedCorners[0]=frustum.Corners[0];
  2096. for (Int i=0; i<4; i++)
  2097. { ClippedCorners[i]=frustum.Corners[i];
  2098. if (groundPlane.Compute_Intersection(frustum.Corners[i],frustum.Corners[i+4],&clipFraction))
  2099. { //edge intersects the terrain
  2100. ClippedCorners[i+4]=frustum.Corners[i]+(frustum.Corners[i+4]-frustum.Corners[i])*clipFraction;
  2101. }
  2102. else
  2103. ClippedCorners[i+4]=frustum.Corners[i+4];
  2104. }
  2105. if (box)
  2106. box->Init(ClippedCorners,8);
  2107. return TRUE;
  2108. }
  2109. //=============================================================================
  2110. // HeightMapRenderObjClass::Class_ID
  2111. //=============================================================================
  2112. /** returns the class id, so the scene can tell what kind of render object it has. */
  2113. //=============================================================================
  2114. Int HeightMapRenderObjClass::Class_ID(void) const
  2115. {
  2116. return RenderObjClass::CLASSID_TILEMAP;
  2117. }
  2118. //=============================================================================
  2119. // HeightMapRenderObjClass::Clone
  2120. //=============================================================================
  2121. /** Not used, but required virtual method. */
  2122. //=============================================================================
  2123. RenderObjClass * HeightMapRenderObjClass::Clone(void) const
  2124. {
  2125. assert(false);
  2126. return NULL;
  2127. }
  2128. //=============================================================================
  2129. // HeightMapRenderObjClass::loadRoadsAndBridges
  2130. //=============================================================================
  2131. /** Loads the roads from the map objects. */
  2132. //=============================================================================
  2133. void HeightMapRenderObjClass::loadRoadsAndBridges(W3DTerrainLogic *pTerrainLogic, Bool saveGame)
  2134. {
  2135. if (DX8Wrapper::_Get_D3D_Device8() && (DX8Wrapper::_Get_D3D_Device8()->TestCooperativeLevel()) != D3D_OK)
  2136. return; //device not ready to render anything
  2137. #ifdef DO_ROADS
  2138. if (m_roadBuffer) {
  2139. m_roadBuffer->loadRoads();
  2140. }
  2141. #endif
  2142. if (m_bridgeBuffer) {
  2143. m_bridgeBuffer->loadBridges(pTerrainLogic, saveGame);
  2144. }
  2145. }
  2146. // ============================================================================
  2147. // HeightMapRenderObjClass::worldBuilderUpdateBridgeTowers
  2148. // ============================================================================
  2149. /** The worldbuilder has it's own method here to update the visual representation
  2150. * of the bridge towers */
  2151. // ============================================================================
  2152. void HeightMapRenderObjClass::worldBuilderUpdateBridgeTowers( W3DAssetManager *assetManager,
  2153. SimpleSceneClass *scene )
  2154. {
  2155. if( m_bridgeBuffer )
  2156. m_bridgeBuffer->worldBuilderUpdateBridgeTowers( assetManager, scene );
  2157. }
  2158. void HeightMapRenderObjClass::setShoreLineDetail(void)
  2159. {
  2160. if (!m_map)
  2161. return;
  2162. Int m_mapDX=m_map->getXExtent();
  2163. Int m_mapDY=m_map->getYExtent();
  2164. //Find all shoreline tiles so they can get extra alpha blend
  2165. updateShorelineTiles(0,0,m_mapDX-1,m_mapDY-1,m_map);
  2166. }
  2167. /**Scan through our map and record all tiles which cross a water plane and are within visible depth under
  2168. water.*/
  2169. void HeightMapRenderObjClass::updateShorelineTiles(Int minX, Int minY, Int maxX, Int maxY, WorldHeightMap *pMap)
  2170. {
  2171. Int border = pMap->getBorderSize();
  2172. //Clamp region to valid terrain tiles
  2173. if (minX<0)
  2174. minX = 0;
  2175. if (minY<0)
  2176. minY = 0;
  2177. if (maxX > (pMap->getXExtent() - 1))
  2178. maxX = (pMap->getXExtent() - 1);
  2179. if (maxY > (pMap->getYExtent() - 1))
  2180. maxY = (pMap->getYExtent() - 1);
  2181. if (!m_shoreLineTilePositions)
  2182. { //Need to allocate memory
  2183. m_shoreLineTilePositions = NEW shoreLineTileInfo[DEFAULT_MAX_MAP_SHORELINE_TILES];
  2184. m_shoreLineTilePositionsSize = DEFAULT_MAX_MAP_SHORELINE_TILES;
  2185. }
  2186. //Find list of all extra blend tiles used on map. These are tiles with 3 materials/textures
  2187. //over the same tile and require an extra render pass.
  2188. //First remove any existing extra blend tiles within this partial region
  2189. for (Int j=0; j<m_numShoreLineTiles; j++)
  2190. { Int x = m_shoreLineTilePositions[j].m_xy & 0xffff;
  2191. Int y = m_shoreLineTilePositions[j].m_xy >> 16;
  2192. if (x >= minX && x < maxX &&
  2193. y >= minY && y < maxY)
  2194. { //this tile is inside region being updated so remove it by shifting tile array
  2195. memcpy(m_shoreLineTilePositions+j,m_shoreLineTilePositions+j+1,(m_numShoreLineTiles-1-j)*sizeof(shoreLineTileInfo));
  2196. m_numShoreLineTiles--;
  2197. j--; //look at current tile again since it was removed.
  2198. }
  2199. }
  2200. if (TheWaterTransparency->m_transparentWaterDepth == 0 || !TheGlobalData->m_showSoftWaterEdge)
  2201. return;
  2202. Int waterSide;
  2203. Real waterZ0,waterZ1,waterZ2,waterZ3;
  2204. Real terrainZ0, terrainZ1, terrainZ2, terrainZ3;
  2205. //Figure out maximum depth of water before we reach the m_minWaterOpacity value. Depths greater than this don't need
  2206. //custom shoreline tiles because they will get their opacity from the default value stored in the frame buffer during
  2207. //a screen clear operation.
  2208. Real transparentDepth=TheWaterTransparency->m_transparentWaterDepth*TheWaterTransparency->m_minWaterOpacity;
  2209. Real depthScaleFactor = 1.0f/transparentDepth;
  2210. for (j=minY; j<maxY; j++)
  2211. for (Int i=minX; i<maxX; i++)
  2212. {
  2213. waterSide=(waterZ0=TheWaterRenderObj->getWaterHeight((i-border)*MAP_XY_FACTOR,(j-border)*MAP_XY_FACTOR)) > ((terrainZ0=MAP_HEIGHT_SCALE*pMap->getHeight(i,j)));
  2214. waterSide |=((waterZ1=TheWaterRenderObj->getWaterHeight((i-border+1)*MAP_XY_FACTOR,(j-border)*MAP_XY_FACTOR)) > ((terrainZ1=MAP_HEIGHT_SCALE*pMap->getHeight(i+1,j)))) << 1;
  2215. waterSide |=((waterZ2=TheWaterRenderObj->getWaterHeight((i-border+1)*MAP_XY_FACTOR,(j-border+1)*MAP_XY_FACTOR)) > ((terrainZ2=MAP_HEIGHT_SCALE*pMap->getHeight(i+1,j+1)))) << 2;
  2216. waterSide |=((waterZ3=TheWaterRenderObj->getWaterHeight((i-border)*MAP_XY_FACTOR,(j-border+1)*MAP_XY_FACTOR)) > ((terrainZ3=MAP_HEIGHT_SCALE*pMap->getHeight(i,j+1)))) << 3;
  2217. if (!waterSide || (waterZ0*waterZ1*waterZ2*waterZ3) <= 0)
  2218. continue; //all verts are on positive (surface) side of water so don't need blending. Or one of them is outside the water plane bounds (waterHeight <= 0!)
  2219. //Check if mix of under/over water vertices or some vertices within depth fade region.
  2220. if (waterSide < 0xf || (waterZ0 - terrainZ0) < transparentDepth ||
  2221. (waterZ1 - terrainZ1) < transparentDepth || (waterZ2 - terrainZ2) < transparentDepth
  2222. || (waterZ3 - terrainZ3) < transparentDepth)
  2223. { //add tile to set that needs shoreline blending.
  2224. if (m_numShoreLineTiles >= m_shoreLineTilePositionsSize)
  2225. { //no more room to store extra blend tiles so enlarge the buffer.
  2226. shoreLineTileInfo *tempPositions=NEW shoreLineTileInfo[m_shoreLineTilePositionsSize+512];
  2227. memcpy(tempPositions, m_shoreLineTilePositions, m_shoreLineTilePositionsSize*sizeof(shoreLineTileInfo));
  2228. delete [] m_shoreLineTilePositions;
  2229. //enlarge by more tiles to reduce memory trashing
  2230. m_shoreLineTilePositions = tempPositions;
  2231. m_shoreLineTilePositionsSize += 512;
  2232. }
  2233. //Pack x and y position into single integer since maps are limited in size
  2234. shoreLineTileInfo *shoreInfo=&m_shoreLineTilePositions[m_numShoreLineTiles];
  2235. shoreInfo->m_xy=i | (j <<16);
  2236. shoreInfo->t0=(waterZ0 - terrainZ0)*depthScaleFactor;
  2237. shoreInfo->t1=(waterZ1 - terrainZ1)*depthScaleFactor;
  2238. shoreInfo->t2=(waterZ2 - terrainZ2)*depthScaleFactor;
  2239. shoreInfo->t3=(waterZ3 - terrainZ3)*depthScaleFactor;
  2240. m_numShoreLineTiles++;
  2241. }
  2242. }
  2243. }
  2244. /** Generate a lookup table for arbitrary angled impassable area viewing. */
  2245. void HeightMapRenderObjClass::updateViewImpassableAreas(Bool partial, Int minX, Int maxX, Int minY, Int maxY)
  2246. {
  2247. Int xSize = m_map->getXExtent();
  2248. Int ySize = m_map->getYExtent();
  2249. if (m_showAsVisibleCliff.size() != xSize * ySize) {
  2250. m_showAsVisibleCliff.resize(xSize * ySize);
  2251. }
  2252. if (!partial) {
  2253. minX = 0;
  2254. minY = 0;
  2255. maxX = xSize;
  2256. maxY = ySize;
  2257. }
  2258. // save calculating the tangent over and over again.
  2259. Real tanImpassableRad = tan(m_curImpassableSlope / 360.f * 2 * PI);
  2260. for (Int j = minY; j < maxY; ++j) {
  2261. for (Int i = minX; i < maxX; ++i) {
  2262. m_showAsVisibleCliff[i + j * xSize] = evaluateAsVisibleCliff(i, j, tanImpassableRad);
  2263. }
  2264. }
  2265. }
  2266. /** Generate a lookup table which can be used to generate an
  2267. alpha value from a given set of uv coordinates. Currently used
  2268. for smoothing water/terrain border*/
  2269. void HeightMapRenderObjClass::initDestAlphaLUT(void)
  2270. {
  2271. if (!m_destAlphaTexture)
  2272. return;
  2273. SurfaceClass *surf=m_destAlphaTexture->Get_Surface_Level();
  2274. if (surf)
  2275. {
  2276. Int pitch;
  2277. UnsignedInt *pData=(UnsignedInt*)surf->Lock(&pitch);
  2278. Int maxOpacity=(Int)(TheWaterTransparency->m_minWaterOpacity * 255.0f);
  2279. Int alpha;
  2280. if (pData)
  2281. {
  2282. //Fill texture with alpha gradient
  2283. for (Int x=0; x<256; x++)
  2284. {
  2285. alpha = x;
  2286. if (alpha > maxOpacity)
  2287. alpha = maxOpacity;
  2288. *pData=(alpha<<24)|0x00ffffff;
  2289. pData++;
  2290. }
  2291. surf->Unlock();
  2292. }
  2293. m_destAlphaTexture->Set_U_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
  2294. m_destAlphaTexture->Set_V_Addr_Mode(TextureClass::TEXTURE_ADDRESS_CLAMP);
  2295. REF_PTR_RELEASE(surf);
  2296. m_currentMinWaterOpacity = TheWaterTransparency->m_minWaterOpacity;
  2297. }
  2298. }
  2299. //=============================================================================
  2300. // HeightMapRenderObjClass::initHeightData
  2301. //=============================================================================
  2302. /** Allocate a heightmap of x by y vertices and fill with initial height values.
  2303. Also allocates all rendering resources such as vertex buffers, index buffers,
  2304. shaders, and materials.*/
  2305. //=============================================================================
  2306. Int HeightMapRenderObjClass::initHeightData(Int x, Int y, WorldHeightMap *pMap, RefRenderObjListIterator *pLightsIterator)
  2307. {
  2308. Int i,j;
  2309. // Int vertsPerRow=x*2-2;
  2310. // Int vertsPerColumn=y*2-2;
  2311. REF_PTR_SET(m_map,pMap); //update our heightmap pointer in case it changed since last call.
  2312. if (m_shroud)
  2313. m_shroud->init(m_map,TheGlobalData->m_partitionCellSize,TheGlobalData->m_partitionCellSize);
  2314. #ifdef DO_ROADS
  2315. m_roadBuffer->setMap(m_map);
  2316. #endif
  2317. HeightSampleType *data = NULL;
  2318. if (pMap) {
  2319. data = pMap->getDataPtr();
  2320. }
  2321. m_numExtraBlendTiles = 0;
  2322. m_numShoreLineTiles = 0;
  2323. //Do some preprocessing on map to extract useful data
  2324. if (pMap)
  2325. {
  2326. //Find min/max values for all terrain heights, useful for rendering optimization
  2327. Int m_mapDX=pMap->getXExtent();
  2328. Int m_mapDY=pMap->getYExtent();
  2329. Int i, j, minHt, maxHt;
  2330. minHt = pMap->getMaxHeightValue();
  2331. maxHt = 0;
  2332. for (j=0; j<m_mapDY; j++) {
  2333. for (i=0; i<m_mapDX; i++) {
  2334. Short cur = pMap->getHeight(i,j);
  2335. if (cur<minHt) minHt = cur;
  2336. if (maxHt<cur) maxHt = cur;
  2337. }
  2338. }
  2339. m_minHeight = minHt * MAP_HEIGHT_SCALE;
  2340. m_maxHeight = maxHt * MAP_HEIGHT_SCALE;
  2341. if (!m_extraBlendTilePositions)
  2342. { //Need to allocate memory
  2343. m_extraBlendTilePositions = NEW Int[DEFAULT_MAX_MAP_EXTRABLEND_TILES];
  2344. m_extraBlendTilePositionsSize = DEFAULT_MAX_MAP_EXTRABLEND_TILES;
  2345. }
  2346. //Find list of all extra blend tiles used on map. These are tiles with 3 materials/textures
  2347. //over the same tile and require an extra render pass.
  2348. for (j=0; j<(m_mapDY-1); j++)
  2349. for (i=0; i<(m_mapDX-1); i++)
  2350. {
  2351. Real U[4],V[4];
  2352. UnsignedByte alpha[4];
  2353. Bool flipState,cliffState;
  2354. if (pMap->getExtraAlphaUVData(i,j,U,V,alpha,&flipState, &cliffState))
  2355. { if (m_numExtraBlendTiles >= m_extraBlendTilePositionsSize)
  2356. { //no more room to store extra blend tiles so enlarge the buffer.
  2357. Int *tempPositions=NEW Int[m_extraBlendTilePositionsSize+512];
  2358. memcpy(tempPositions, m_extraBlendTilePositions, m_extraBlendTilePositionsSize*sizeof(Int));
  2359. delete [] m_extraBlendTilePositions;
  2360. //enlarge by more tiles to reduce memory trashing
  2361. m_extraBlendTilePositions = tempPositions;
  2362. m_extraBlendTilePositionsSize += 512;
  2363. }
  2364. //Pack x and y position into single integer since maps are limited in size
  2365. m_extraBlendTilePositions[m_numExtraBlendTiles]=i | (j <<16);
  2366. m_numExtraBlendTiles++;
  2367. }
  2368. }
  2369. //Find all shoreline tiles so they can get extra alpha blend
  2370. updateShorelineTiles(0,0,m_mapDX-1,m_mapDY-1,pMap);
  2371. if (TheWaterTransparency->m_minWaterOpacity != m_currentMinWaterOpacity)
  2372. initDestAlphaLUT();
  2373. }
  2374. Set_Force_Visible(TRUE); //terrain is always visible.
  2375. m_halfResMesh = TheGlobalData->m_useHalfHeightMap;
  2376. m_originX = 0;
  2377. m_originY = 0;
  2378. m_needFullUpdate = true;
  2379. m_scorchesInBuffer = 0;
  2380. m_curNumScorchVertices=0;
  2381. m_curNumScorchIndices=0;
  2382. // If the size changed, we need to allocate.
  2383. Bool needToAllocate = (x != m_x || y != m_y);
  2384. // If the textures aren't allocated (usually because of a hardware reset) need to allocate.
  2385. if (m_stageOneTexture == NULL) {
  2386. needToAllocate = true;
  2387. }
  2388. if (data && needToAllocate)
  2389. { //requested heightmap different from old one.
  2390. //allocate a new one.
  2391. freeMapResources(); //free old data and ib/vb
  2392. REF_PTR_SET(m_map,pMap); //update our heightmap pointer in case it changed since last call.
  2393. m_stageTwoTexture=NEW CloudMapTerrainTextureClass;
  2394. m_stageThreeTexture=NEW LightMapTerrainTextureClass(m_macroTextureName);
  2395. m_destAlphaTexture=MSGNEW("TextureClass") TextureClass(256,1,WW3D_FORMAT_A8R8G8B8,TextureClass::MIP_LEVELS_1);
  2396. initDestAlphaLUT();
  2397. #ifdef DO_SCORCH
  2398. allocateScorchBuffers();
  2399. #endif
  2400. //Create static index buffers. These will index the vertex buffers holding the map.
  2401. m_indexBuffer=NEW_REF(DX8IndexBufferClass,(VERTEX_BUFFER_TILE_LENGTH*VERTEX_BUFFER_TILE_LENGTH*2*3));
  2402. // Fill up the IB
  2403. DX8IndexBufferClass::WriteLockClass lockIdxBuffer(m_indexBuffer);
  2404. UnsignedShort *ib=lockIdxBuffer.Get_Index_Array();
  2405. for (j=0; j<(VERTEX_BUFFER_TILE_LENGTH*VERTEX_BUFFER_TILE_LENGTH*4); j+=VERTEX_BUFFER_TILE_LENGTH*4)
  2406. {
  2407. for (i=j; i<(j+VERTEX_BUFFER_TILE_LENGTH*4); i+=4) //4 vertices per 2x2 block
  2408. {
  2409. ib[0]=i;
  2410. ib[1]=i+2;
  2411. ib[2]=i+3;
  2412. ib[3]=i;
  2413. ib[4]=i+1;
  2414. ib[5]=i+2;
  2415. ib+=6; //skip the 6 indices we just filled
  2416. }
  2417. }
  2418. //Get number of vertex buffers needed to hold current map
  2419. //First round dimensions to next multiple of VERTEX_BUFFER_TILE_LENGTH since that's our
  2420. //blocksize
  2421. m_numVBTilesX=1;
  2422. for (i=VERTEX_BUFFER_TILE_LENGTH+1; i<x;)
  2423. { i+=VERTEX_BUFFER_TILE_LENGTH;
  2424. m_numVBTilesX++;
  2425. }
  2426. m_numVBTilesY=1;
  2427. for (j=VERTEX_BUFFER_TILE_LENGTH+1; j<y;)
  2428. { j+=VERTEX_BUFFER_TILE_LENGTH;
  2429. m_numVBTilesY++;
  2430. }
  2431. m_numBlockColumnsInLastVB=(x-1)%VERTEX_BUFFER_TILE_LENGTH; //right border within last VB
  2432. m_numBlockRowsInLastVB=(y-1)%VERTEX_BUFFER_TILE_LENGTH; //bottom border within last VB
  2433. m_numVertexBufferTiles=m_numVBTilesX*m_numVBTilesY;
  2434. m_x=x;
  2435. m_y=y;
  2436. m_vertexBufferTiles = NEW DX8VertexBufferClass*[m_numVertexBufferTiles];
  2437. m_vertexBufferBackup = NEW char *[m_numVertexBufferTiles];
  2438. Int numVertex = VERTEX_BUFFER_TILE_LENGTH*2*VERTEX_BUFFER_TILE_LENGTH*2;
  2439. #ifdef PRE_TRANSFORM_VERTEX
  2440. D3DDEVICE_CREATION_PARAMETERS parms;
  2441. DX8Wrapper::_Get_D3D_Device8()->GetCreationParameters(&parms);
  2442. Bool softwareVertexProcessing = 0!=(parms.BehaviorFlags&D3DCREATE_SOFTWARE_VERTEXPROCESSING);
  2443. if (m_xformedVertexBuffer == NULL && softwareVertexProcessing) {
  2444. m_xformedVertexBuffer = NEW IDirect3DVertexBuffer8*[m_numVertexBufferTiles];
  2445. }
  2446. #endif
  2447. for (i=0; i<m_numVertexBufferTiles; i++) {
  2448. #ifdef USE_NORMALS
  2449. m_vertexBufferTiles[i]=NEW_REF(DX8VertexBufferClass,(DX8_FVF_XYZNUV2,numVertex,DX8VertexBufferClass::USAGE_DEFAULT));
  2450. #else
  2451. m_vertexBufferTiles[i]=NEW_REF(DX8VertexBufferClass,(DX8_VERTEX_FORMAT,numVertex,DX8VertexBufferClass::USAGE_DEFAULT));
  2452. #endif
  2453. m_vertexBufferBackup[i] = NEW char[numVertex*sizeof(VERTEX_FORMAT)];
  2454. #ifdef PRE_TRANSFORM_VERTEX
  2455. if (m_xformedVertexBuffer) {
  2456. DX8Wrapper::_Get_D3D_Device8()->CreateVertexBuffer(
  2457. D3DXGetFVFVertexSize(D3DFVF_XYZRHW |D3DFVF_DIFFUSE|D3DFVF_TEX2)*numVertex,
  2458. D3DUSAGE_WRITEONLY|D3DUSAGE_DYNAMIC,
  2459. D3DFVF_XYZRHW |D3DFVF_DIFFUSE|D3DFVF_TEX2,
  2460. D3DPOOL_DEFAULT,
  2461. &m_xformedVertexBuffer[i]);
  2462. }
  2463. #endif
  2464. }
  2465. //go with a preset material for now.
  2466. #ifdef USE_NORMALS
  2467. m_vertexMaterialClass= NEW VertexMaterialClass();
  2468. m_vertexMaterialClass->Set_Shininess(0.0);
  2469. m_vertexMaterialClass->Set_Ambient(1,1,1);
  2470. m_vertexMaterialClass->Set_Diffuse(1,1,1);
  2471. m_vertexMaterialClass->Set_Specular(0,0,0);
  2472. m_vertexMaterialClass->Set_Opacity(0);
  2473. m_vertexMaterialClass->Set_Lighting(true);
  2474. #else
  2475. m_vertexMaterialClass=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
  2476. #endif
  2477. m_shaderClass = detailOpaqueShader; // ShaderClass::_PresetOpaqueShader;
  2478. }
  2479. updateBlock(0,0,x-1,y-1,pMap,pLightsIterator);
  2480. return 0;
  2481. }
  2482. #ifdef DO_SCORCH
  2483. //=============================================================================
  2484. // HeightMapRenderObjClass::freeScorchBuffers
  2485. //=============================================================================
  2486. /** Frees the vertex buffers for scorches.*/
  2487. //=============================================================================
  2488. void HeightMapRenderObjClass::freeScorchBuffers(void)
  2489. {
  2490. REF_PTR_RELEASE(m_vertexScorch);
  2491. REF_PTR_RELEASE(m_indexScorch);
  2492. REF_PTR_RELEASE(m_scorchTexture);
  2493. }
  2494. //=============================================================================
  2495. // HeightMapRenderObjClass::allocateScorchBuffers
  2496. //=============================================================================
  2497. /** Allocates the vertex buffer and texture for scorches.*/
  2498. //=============================================================================
  2499. void HeightMapRenderObjClass::allocateScorchBuffers(void)
  2500. {
  2501. m_vertexScorch=NEW_REF(DX8VertexBufferClass,(DX8_FVF_XYZDUV1,MAX_SCORCH_VERTEX,DX8VertexBufferClass::USAGE_DEFAULT));
  2502. m_indexScorch=NEW_REF(DX8IndexBufferClass,(MAX_SCORCH_INDEX));
  2503. m_scorchTexture=NEW ScorchTextureClass;
  2504. m_scorchesInBuffer = 0; // If we just allocated the buffers, we got no scorches in the buffer.
  2505. m_curNumScorchVertices=0;
  2506. m_curNumScorchIndices=0;
  2507. #ifdef _DEBUG
  2508. Vector3 loc(4*MAP_XY_FACTOR,4*MAP_XY_FACTOR,0);
  2509. addScorch(loc, 1*MAP_XY_FACTOR, SCORCH_1);
  2510. loc.Y += 10*MAP_XY_FACTOR;
  2511. loc.X += 5*MAP_XY_FACTOR;
  2512. addScorch(loc, 3*MAP_XY_FACTOR, SCORCH_1);
  2513. #endif
  2514. }
  2515. //=============================================================================
  2516. // HeightMapRenderObjClass::updateScorches
  2517. //=============================================================================
  2518. /** Builds the vertex buffer data for drawing the scorches.*/
  2519. //=============================================================================
  2520. void HeightMapRenderObjClass::updateScorches(void)
  2521. {
  2522. if (m_scorchesInBuffer > 1) {
  2523. return;
  2524. }
  2525. if (!m_indexScorch || !m_vertexScorch) {
  2526. return;
  2527. }
  2528. m_curNumScorchVertices = 0;
  2529. m_curNumScorchIndices = 0;
  2530. DX8IndexBufferClass::WriteLockClass lockIdxBuffer(m_indexScorch);
  2531. UnsignedShort *ib=lockIdxBuffer.Get_Index_Array();
  2532. UnsignedShort *curIb = ib;
  2533. DX8VertexBufferClass::WriteLockClass lockVtxBuffer(m_vertexScorch);
  2534. VertexFormatXYZDUV1 *vb = (VertexFormatXYZDUV1*)lockVtxBuffer.Get_Vertex_Array();
  2535. VertexFormatXYZDUV1 *curVb = vb;
  2536. Int curScorch;
  2537. Real shadeR, shadeG, shadeB;
  2538. shadeR = TheGlobalData->m_terrainAmbient[0].red;
  2539. shadeG = TheGlobalData->m_terrainAmbient[0].green;
  2540. shadeB = TheGlobalData->m_terrainAmbient[0].blue;
  2541. shadeR += TheGlobalData->m_terrainDiffuse[0].red/2;
  2542. shadeG += TheGlobalData->m_terrainDiffuse[0].green/2;
  2543. shadeB += TheGlobalData->m_terrainDiffuse[0].blue/2;
  2544. shadeR*=255.0f;
  2545. shadeG*=255.0f;
  2546. shadeB*=255.0f;
  2547. Int diffuse=REAL_TO_INT(shadeB) | (REAL_TO_INT(shadeG) << 8) | (REAL_TO_INT(shadeR) << 16) | ((int)255 << 24);
  2548. m_scorchesInBuffer = 0;
  2549. for (curScorch=m_numScorches-1; curScorch>=0; curScorch--) {
  2550. m_scorchesInBuffer++;
  2551. Real radius = m_scorches[curScorch].radius;
  2552. Vector3 loc = m_scorches[curScorch].location;
  2553. Int type = m_scorches[curScorch].scorchType;
  2554. if (type<0) {
  2555. type = 0;
  2556. }
  2557. if (type >= SCORCH_MARKS_IN_TEXTURE) {
  2558. type = 0;
  2559. }
  2560. Real amtToFloat = 0;
  2561. if (m_halfResMesh) {
  2562. amtToFloat = MAP_HEIGHT_SCALE/8;
  2563. }
  2564. Int minX = REAL_TO_INT_FLOOR((loc.X-radius)/MAP_XY_FACTOR);
  2565. Int minY = REAL_TO_INT_FLOOR((loc.Y-radius)/MAP_XY_FACTOR);
  2566. if (minX<-m_map->getBorderSize()) minX=-m_map->getBorderSize();
  2567. if (minY<-m_map->getBorderSize()) minY=-m_map->getBorderSize();
  2568. Int maxX = REAL_TO_INT_CEIL((loc.X+radius)/MAP_XY_FACTOR);
  2569. Int maxY = REAL_TO_INT_CEIL((loc.Y+radius)/MAP_XY_FACTOR);
  2570. maxX++; maxY++;
  2571. if (maxX > m_map->getXExtent()-m_map->getBorderSize()) {
  2572. maxX = m_map->getXExtent()-m_map->getBorderSize();
  2573. }
  2574. if (maxY > m_map->getYExtent()-m_map->getBorderSize()) {
  2575. maxY = m_map->getYExtent()-m_map->getBorderSize();
  2576. }
  2577. Int startVertex = m_curNumScorchVertices;
  2578. Int i, j;
  2579. for (j=minY; j<maxY; j++) {
  2580. for (i=minX; i<maxX; i++) {
  2581. if (m_curNumScorchVertices >= MAX_SCORCH_VERTEX) return;
  2582. curVb->diffuse = diffuse;
  2583. Real theZ;
  2584. theZ = amtToFloat+((float)getClipHeight(i+m_map->getBorderSize(),j+m_map->getBorderSize())*MAP_HEIGHT_SCALE);
  2585. if (m_halfResMesh) {
  2586. theZ = amtToFloat + this->getMaxCellHeight(i, j);
  2587. Real amt2 = amtToFloat + getMaxCellHeight(i-1, j-1);
  2588. if (amt2 > theZ) {
  2589. theZ = amt2;
  2590. }
  2591. }
  2592. // The scorchmarks are spaced out by 1.5 in the texture.
  2593. Real uOffset = (type%SCORCH_PER_ROW) * 1.5f;
  2594. Real vOffset = (type/SCORCH_PER_ROW) * 1.5f;
  2595. Real X = i*MAP_XY_FACTOR;
  2596. Real Y = j*MAP_XY_FACTOR;
  2597. curVb->u1 = (uOffset + 0.5f + (X - loc.X)/(2*radius)) / (SCORCH_PER_ROW+1);
  2598. curVb->v1 = (vOffset + 0.5f + (Y - loc.Y)/(2*radius)) / (SCORCH_PER_ROW+1);
  2599. curVb->x = X;
  2600. curVb->y = Y;
  2601. curVb->z = theZ;
  2602. curVb++;
  2603. m_curNumScorchVertices++;
  2604. }
  2605. }
  2606. Int yOffset = maxX-minX;
  2607. for (j=0; j<maxY-minY-1; j++) {
  2608. for (i=0; i<maxX-minX-1; i++) {
  2609. if (m_curNumScorchIndices+6 > MAX_SCORCH_INDEX) return;
  2610. Int xNdx = i+minX+m_map->getBorderSize();
  2611. Int yNdx = j+minY+m_map->getBorderSize();
  2612. Bool flipForBlend = m_map->getFlipState(xNdx, yNdx);
  2613. #if 0
  2614. UnsignedByte alpha[4];
  2615. float UA[4], VA[4];
  2616. m_map->getAlphaUVData(xNdx, yNdx, UA, VA, alpha, &flipForBlend, false);
  2617. #endif
  2618. if (flipForBlend) {
  2619. *curIb++ = startVertex + j*yOffset + i+1;
  2620. *curIb++ = startVertex + j*yOffset + i+yOffset;
  2621. *curIb++ = startVertex + j*yOffset + i;
  2622. *curIb++ = startVertex + j*yOffset + i+1;
  2623. *curIb++ = startVertex + j*yOffset + i+1+yOffset;
  2624. *curIb++ = startVertex + j*yOffset + i+yOffset;
  2625. }
  2626. else
  2627. {
  2628. *curIb++ = startVertex + j*yOffset + i;
  2629. *curIb++ = startVertex + j*yOffset + i+1+yOffset;
  2630. *curIb++ = startVertex + j*yOffset + i+yOffset;
  2631. *curIb++ = startVertex + j*yOffset + i;
  2632. *curIb++ = startVertex + j*yOffset + i+1;
  2633. *curIb++ = startVertex + j*yOffset + i+1+yOffset;
  2634. }
  2635. m_curNumScorchIndices+=6;
  2636. }
  2637. }
  2638. }
  2639. }
  2640. #endif
  2641. //=============================================================================
  2642. // HeightMapRenderObjClass::clearAllScorches
  2643. //=============================================================================
  2644. /** Removes all scorches. */
  2645. //=============================================================================
  2646. void HeightMapRenderObjClass::clearAllScorches(void)
  2647. {
  2648. #ifdef DO_SCORCH
  2649. m_numScorches=0;
  2650. m_scorchesInBuffer=0;
  2651. #endif
  2652. }
  2653. //=============================================================================
  2654. // HeightMapRenderObjClass::addScorch
  2655. //=============================================================================
  2656. /** Adds a scorch mark. */
  2657. //=============================================================================
  2658. void HeightMapRenderObjClass::addScorch(Vector3 location, Real radius, Scorches type)
  2659. {
  2660. #ifdef DO_SCORCH
  2661. if (m_numScorches >= MAX_SCORCH_MARKS) {
  2662. Int i;
  2663. for (i=0; i<MAX_SCORCH_MARKS-1; i++) {
  2664. m_scorches[i] = m_scorches[i+1];
  2665. }
  2666. m_numScorches--;
  2667. }
  2668. Int i;
  2669. Real limit = radius/4;
  2670. for (i=0; i<m_numScorches; i++) {
  2671. if ( abs(location.X-m_scorches[i].location.X) < limit &&
  2672. abs(location.Y-m_scorches[i].location.Y) < limit &&
  2673. abs(radius - m_scorches[i].radius) < limit &&
  2674. m_scorches[i].scorchType == type) {
  2675. return; // basically a duplicate.
  2676. }
  2677. }
  2678. m_scorches[m_numScorches].location = location;
  2679. m_scorches[m_numScorches].radius = radius;
  2680. m_scorches[m_numScorches].scorchType = type;
  2681. m_numScorches++;
  2682. m_scorchesInBuffer = 0; // force buffer regenerations.
  2683. #endif
  2684. }
  2685. //=============================================================================
  2686. // HeightMapRenderObjClass::getStaticDiffuse
  2687. //=============================================================================
  2688. /** Gets the static diffuse color value for a terrain vertex.*/
  2689. //=============================================================================
  2690. Int HeightMapRenderObjClass::getStaticDiffuse(Int x, Int y)
  2691. {
  2692. if (x<0) x = 0;
  2693. if (y<0) y = 0;
  2694. if (x >= m_map->getXExtent())
  2695. x=m_map->getXExtent()-1;
  2696. if (y >= m_map->getYExtent())
  2697. y=m_map->getYExtent()-1;
  2698. if (m_halfResMesh) {
  2699. x&=0xffffffe;
  2700. y&=0xffffffe;
  2701. }
  2702. if (m_map == NULL) {
  2703. return(0);
  2704. }
  2705. Vector3 l2r,n2f,normalAtTexel;
  2706. Int vn0,un0,vp1,up1;
  2707. Int cellOffset = 1;
  2708. if (m_halfResMesh) {
  2709. cellOffset = 2;
  2710. }
  2711. vn0 = y-cellOffset;
  2712. vp1 = y+cellOffset;
  2713. if (vp1 >= m_map->getYExtent())
  2714. vp1=m_map->getYExtent()-1;
  2715. if (vn0<0) vn0 = 0;
  2716. un0 = x-cellOffset;
  2717. up1 = x+cellOffset;
  2718. if (un0 < 0)
  2719. un0=0;
  2720. if (up1 >= m_map->getXExtent())
  2721. up1=m_map->getXExtent()-1;
  2722. Vector3 lightRay[MAX_GLOBAL_LIGHTS];
  2723. const Coord3D *lightPos;
  2724. for (Int lightIndex=0; lightIndex < TheGlobalData->m_numGlobalLights; lightIndex++)
  2725. {
  2726. lightPos=&TheGlobalData->m_terrainLightPos[lightIndex];
  2727. lightRay[lightIndex].Set(-lightPos->x,-lightPos->y, -lightPos->z);
  2728. }
  2729. //top-left sample
  2730. l2r.Set(2*MAP_XY_FACTOR,0,MAP_HEIGHT_SCALE*(m_map->getHeight(up1, y) - m_map->getHeight(un0, y)));
  2731. n2f.Set(0,2*MAP_XY_FACTOR,MAP_HEIGHT_SCALE*(m_map->getHeight(x, vp1) - m_map->getHeight(x, vn0)));
  2732. #ifdef ALLOW_TEMPORARIES
  2733. normalAtTexel= Normalize(Vector3::Cross_Product(l2r,n2f));
  2734. #else
  2735. Vector3::Normalized_Cross_Product(l2r,n2f, &normalAtTexel);
  2736. #endif
  2737. VERTEX_FORMAT vertex;
  2738. vertex.x=ADJUST_FROM_INDEX_TO_REAL(x);
  2739. vertex.y=ADJUST_FROM_INDEX_TO_REAL(y);
  2740. vertex.z= ((float)m_map->getHeight(x,y))*MAP_HEIGHT_SCALE;
  2741. vertex.u1=0;
  2742. vertex.v1=0;
  2743. vertex.u2=1;
  2744. vertex.v2=1;
  2745. RTS3DScene *pMyScene = (RTS3DScene *)Scene;
  2746. if (pMyScene) {
  2747. RefRenderObjListIterator *it = pMyScene->createLightsIterator();
  2748. doTheLight(&vertex, lightRay, &normalAtTexel, it, 1.0f);
  2749. if (it) {
  2750. pMyScene->destroyLightsIterator(it);
  2751. it = NULL;
  2752. }
  2753. } else {
  2754. doTheLight(&vertex, lightRay, &normalAtTexel, NULL, 1.0f);
  2755. }
  2756. #ifdef USE_NORMALS
  2757. return(0xffffffff);
  2758. #else
  2759. return vertex.diffuse;
  2760. #endif
  2761. #define not_VERTS_MATCH
  2762. #ifdef VERTS_MATCH
  2763. Int i,j;
  2764. if (m_halfResMesh) {
  2765. x&=0xffffffe;
  2766. y&=0xffffffe;
  2767. }
  2768. Real X = x*MAP_XY_FACTOR;
  2769. Real Y = y*MAP_XY_FACTOR;
  2770. // This code digs the diffuse out of the vertex buffer.
  2771. // It makes sense if the road vertexes match the terrain.
  2772. // However, they don't match anymore. jba.
  2773. Int yCoordMin = m_map->getDrawOrgY();
  2774. Int yCoordMax = m_y+m_map->getDrawOrgY()-1;
  2775. Int xCoordMin = m_map->getDrawOrgX();
  2776. Int xCoordMax = m_x+m_map->getDrawOrgX()-1;
  2777. if (x<xCoordMin || y<yCoordMin || x> xCoordMax || y>yCoordMax) {
  2778. return(0);
  2779. }
  2780. if (x==xCoordMax) x--;
  2781. if (y==yCoordMax) y--;
  2782. x -= xCoordMin;
  2783. y -= yCoordMin;
  2784. y += m_originY;
  2785. if (y<0) y+= m_y-1;
  2786. if (y> m_y-1) y-=m_y-1;
  2787. if (y<0) y = 0;
  2788. if (y>= m_y-1) y=m_y-1;
  2789. x += m_originX;
  2790. if (x<0) x+= m_x-1;
  2791. if (x> m_x-1) x-=m_x-1;
  2792. if (x<0) x = 0;
  2793. if (x>= m_x-1) x=m_x-1;
  2794. i = 0;
  2795. while (x>VERTEX_BUFFER_TILE_LENGTH) {
  2796. i++;
  2797. x -= VERTEX_BUFFER_TILE_LENGTH;
  2798. }
  2799. if (x==VERTEX_BUFFER_TILE_LENGTH) x--;
  2800. j = 0;
  2801. while (y>VERTEX_BUFFER_TILE_LENGTH) {
  2802. j++;
  2803. y -= VERTEX_BUFFER_TILE_LENGTH;
  2804. }
  2805. if (y==VERTEX_BUFFER_TILE_LENGTH) y--;
  2806. char **pData = m_vertexBufferBackup+j*m_numVBTilesX+i;
  2807. Int vertsPerRow=(VERTEX_BUFFER_TILE_LENGTH)*4; //vertices per row of VB
  2808. if (m_halfResMesh) {
  2809. x/=2;
  2810. y/=2;
  2811. vertsPerRow /= 2;
  2812. }
  2813. VERTEX_FORMAT *vbMirror = ((VERTEX_FORMAT*)(*pData)) + (y)*vertsPerRow+4*(x);
  2814. if ( vbMirror[0].x==X && vbMirror[0].y==Y) {
  2815. return(vbMirror[0].diffuse);
  2816. }
  2817. if ( vbMirror[3].x==X && vbMirror[3].y==Y) {
  2818. return(vbMirror[3].diffuse);
  2819. }
  2820. if ( vbMirror[1].x==X && vbMirror[1].y==Y) {
  2821. return(vbMirror[1].diffuse);
  2822. }
  2823. if ( vbMirror[2].x==X && vbMirror[2].y==Y) {
  2824. return(vbMirror[2].diffuse);
  2825. }
  2826. #ifdef _DEBUG
  2827. char buf[256];
  2828. sprintf(buf, "(%f,%f) -> mirror (%f, %f)\n", X, Y, vbMirror->x, vbMirror->y);
  2829. ::OutputDebugString(buf);
  2830. #endif
  2831. return(vbMirror->diffuse);
  2832. #endif
  2833. }
  2834. //=============================================================================
  2835. // HeightMapRenderObjClass::On_Frame_Update
  2836. //=============================================================================
  2837. /** Updates the diffuse color values in the vertices as affected by the dynamic lights.*/
  2838. //=============================================================================
  2839. void HeightMapRenderObjClass::On_Frame_Update(void)
  2840. {
  2841. Int i,j,k;
  2842. DX8VertexBufferClass **pVB;
  2843. Int originX,originY;
  2844. if (Scene==NULL) return;
  2845. RTS3DScene *pMyScene = (RTS3DScene *)Scene;
  2846. RefRenderObjListIterator pDynamicLightsIterator(pMyScene->getDynamicLights());
  2847. if (m_map == NULL) {
  2848. return;
  2849. }
  2850. #ifdef DO_UNIT_TIMINGS
  2851. #pragma MESSAGE("*** WARNING *** DOING DO_UNIT_TIMINGS!!!!")
  2852. return;
  2853. #endif
  2854. #ifdef EXTENDED_STATS
  2855. if (DX8Wrapper::stats.m_disableTerrain) {
  2856. return;
  2857. }
  2858. #endif
  2859. Int numDynaLights=0;
  2860. W3DDynamicLight *enabledLights[MAX_ENABLED_DYNAMIC_LIGHTS];
  2861. Int yCoordMin = m_map->getDrawOrgY();
  2862. Int yCoordMax = m_y+m_map->getDrawOrgY();
  2863. Int xCoordMin = m_map->getDrawOrgX();
  2864. Int xCoordMax = m_x+m_map->getDrawOrgX();
  2865. for (pDynamicLightsIterator.First(); !pDynamicLightsIterator.Is_Done(); pDynamicLightsIterator.Next())
  2866. {
  2867. W3DDynamicLight *pLight = (W3DDynamicLight*)pDynamicLightsIterator.Peek_Obj();
  2868. pLight->m_processMe = false;
  2869. if (pLight->m_enabled || pLight->m_priorEnable) {
  2870. Real range = pLight->Get_Attenuation_Range();
  2871. if (pLight->m_priorEnable) {
  2872. pLight->m_prevMinX = pLight->m_minX;
  2873. pLight->m_prevMinY = pLight->m_minY;
  2874. pLight->m_prevMaxX = pLight->m_maxX;
  2875. pLight->m_prevMaxY = pLight->m_maxY;
  2876. }
  2877. Vector3 pos = pLight->Get_Position();
  2878. pLight->m_minX = (pos.X-range)/MAP_XY_FACTOR;
  2879. pLight->m_maxX = (pos.X+range)/MAP_XY_FACTOR+1.0f;
  2880. pLight->m_minY = (pos.Y-range)/MAP_XY_FACTOR;
  2881. pLight->m_maxY = (pos.Y+range)/MAP_XY_FACTOR+1.0f;
  2882. if (!pLight->m_priorEnable) {
  2883. pLight->m_prevMinX = pLight->m_minX;
  2884. pLight->m_prevMinY = pLight->m_minY;
  2885. pLight->m_prevMaxX = pLight->m_maxX;
  2886. pLight->m_prevMaxY = pLight->m_maxY;
  2887. }
  2888. if (pLight->m_minX < xCoordMax &&
  2889. pLight->m_minY < yCoordMax &&
  2890. pLight->m_maxX > xCoordMin &&
  2891. pLight->m_maxY > yCoordMin) {
  2892. pLight->m_processMe = TRUE;
  2893. } else if (pLight->m_prevMinX < xCoordMax &&
  2894. pLight->m_prevMinY < yCoordMax &&
  2895. pLight->m_prevMaxX > xCoordMin &&
  2896. pLight->m_prevMaxY > yCoordMin) {
  2897. pLight->m_processMe = TRUE;
  2898. } else {
  2899. pLight->m_processMe = false;
  2900. }
  2901. if (pLight->m_processMe) {
  2902. enabledLights[numDynaLights] = pLight;
  2903. numDynaLights++;
  2904. if (numDynaLights == MAX_ENABLED_DYNAMIC_LIGHTS) {
  2905. break;
  2906. }
  2907. }
  2908. }
  2909. pLight->m_priorEnable = pLight->m_enabled;
  2910. }
  2911. if (numDynaLights > 0) {
  2912. //step through each vertex buffer that needs updating
  2913. for (j=0; j<m_numVBTilesY; j++)
  2914. {
  2915. originY=j*VERTEX_BUFFER_TILE_LENGTH; //location of this VB on the large full-size heightmap
  2916. Int yMin, yMax;
  2917. yMin = originY;
  2918. yMax = originY+VERTEX_BUFFER_TILE_LENGTH;
  2919. Bool intersect = false;
  2920. Int yCoordMin = getYWithOrigin(yMin)+m_map->getDrawOrgY()-m_map->getBorderSize();
  2921. Int yCoordMax = getYWithOrigin(yMax-1)+m_map->getDrawOrgY()+1-m_map->getBorderSize();
  2922. if (yCoordMax>yCoordMin) {
  2923. // no wrap occurred.
  2924. for (k=0; k<numDynaLights; k++) {
  2925. if (enabledLights[k]->m_minY < yCoordMax &&
  2926. enabledLights[k]->m_maxY > yCoordMin) {
  2927. intersect = true;
  2928. break;
  2929. }
  2930. if (enabledLights[k]->m_prevMinY < yCoordMax &&
  2931. enabledLights[k]->m_prevMaxY > yCoordMin) {
  2932. intersect = true;
  2933. break;
  2934. }
  2935. }
  2936. } else {
  2937. // wrap occurred, so we are outside of this range.
  2938. int tmp=yCoordMin;
  2939. yCoordMin = yCoordMax;
  2940. yCoordMax = tmp;
  2941. for (k=0; k<numDynaLights; k++) {
  2942. if (enabledLights[k]->m_minY <= yCoordMin ||
  2943. enabledLights[k]->m_maxY >= yCoordMax) {
  2944. intersect = true;
  2945. break;
  2946. }
  2947. if (enabledLights[k]->m_prevMinY <= yCoordMin ||
  2948. enabledLights[k]->m_prevMaxY >= yCoordMax) {
  2949. intersect = true;
  2950. break;
  2951. }
  2952. }
  2953. }
  2954. if (!intersect) {
  2955. continue;
  2956. }
  2957. for (i=0; i<m_numVBTilesX; i++)
  2958. {
  2959. originX=i*VERTEX_BUFFER_TILE_LENGTH; //location of this VB on the large full-size heightmap
  2960. Int xMin, xMax;
  2961. xMin = originX;
  2962. xMax = originX+VERTEX_BUFFER_TILE_LENGTH;
  2963. Bool intersect = false;
  2964. Int xCoordMin = getXWithOrigin(xMin)+m_map->getDrawOrgX()-m_map->getBorderSize();
  2965. Int xCoordMax = getXWithOrigin(xMax-1)+m_map->getDrawOrgX()+1-m_map->getBorderSize();
  2966. if (xCoordMax>xCoordMin) {
  2967. // no wrap occurred.
  2968. for (k=0; k<numDynaLights; k++) {
  2969. if (enabledLights[k]->m_minX < xCoordMax &&
  2970. enabledLights[k]->m_maxX > xCoordMin) {
  2971. intersect = true;
  2972. break;
  2973. }
  2974. if (enabledLights[k]->m_prevMinX < xCoordMax &&
  2975. enabledLights[k]->m_prevMaxX > xCoordMin) {
  2976. intersect = true;
  2977. break;
  2978. }
  2979. }
  2980. } else {
  2981. // wrap occurred, so we are outside of this range.
  2982. int tmp=xCoordMin;
  2983. xCoordMin = xCoordMax;
  2984. xCoordMax = tmp;
  2985. for (k=0; k<numDynaLights; k++) {
  2986. if (enabledLights[k]->m_minX <= xCoordMin ||
  2987. enabledLights[k]->m_maxX >= xCoordMax) {
  2988. intersect = true;
  2989. break;
  2990. }
  2991. if (enabledLights[k]->m_prevMinX <= xCoordMin ||
  2992. enabledLights[k]->m_prevMaxX >= xCoordMax) {
  2993. intersect = true;
  2994. break;
  2995. }
  2996. }
  2997. }
  2998. if (!intersect) {
  2999. continue;
  3000. }
  3001. pVB=m_vertexBufferTiles+j*m_numVBTilesX+i; //point to correct row/column of vertex buffers
  3002. char **pData = m_vertexBufferBackup+j*m_numVBTilesX+i;
  3003. updateVBForLight(*pVB, *pData, xMin, yMin, xMax, yMax, originX,originY, enabledLights, numDynaLights);
  3004. }
  3005. }
  3006. }
  3007. }
  3008. //=============================================================================
  3009. // HeightMapRenderObjClass::addTree
  3010. //=============================================================================
  3011. /** Adds a tree to the tree buffer.*/
  3012. //=============================================================================
  3013. void HeightMapRenderObjClass::addTree(Coord3D location, Real scale, Real angle,
  3014. AsciiString name, Bool visibleInMirror)
  3015. {
  3016. m_treeBuffer->addTree(location, scale, angle, name, visibleInMirror);
  3017. };
  3018. //=============================================================================
  3019. // HeightMapRenderObjClass::addTerrainBib
  3020. //=============================================================================
  3021. /** Adds a terrainBib to the bib buffer.*/
  3022. //=============================================================================
  3023. void HeightMapRenderObjClass::addTerrainBib(Vector3 corners[4],
  3024. ObjectID id, Bool highlight)
  3025. {
  3026. m_bibBuffer->addBib(corners, id, highlight);
  3027. };
  3028. //=============================================================================
  3029. // HeightMapRenderObjClass::addTerrainBib
  3030. //=============================================================================
  3031. /** Adds a terrainBib to the bib buffer.*/
  3032. //=============================================================================
  3033. void HeightMapRenderObjClass::addTerrainBibDrawable(Vector3 corners[4],
  3034. DrawableID id, Bool highlight)
  3035. {
  3036. m_bibBuffer->addBibDrawable(corners, id, highlight);
  3037. };
  3038. //=============================================================================
  3039. // HeightMapRenderObjClass::removeAllTerrainBibs
  3040. //=============================================================================
  3041. /** Removes all terrainBib highlighting from the bib buffer.*/
  3042. //=============================================================================
  3043. void HeightMapRenderObjClass::removeTerrainBibHighlighting()
  3044. {
  3045. m_bibBuffer->removeHighlighting( );
  3046. };
  3047. //=============================================================================
  3048. // HeightMapRenderObjClass::removeAllTerrainBibs
  3049. //=============================================================================
  3050. /** Removes all terrainBibs from the bib buffer.*/
  3051. //=============================================================================
  3052. void HeightMapRenderObjClass::removeAllTerrainBibs()
  3053. {
  3054. m_bibBuffer->clearAllBibs( );
  3055. };
  3056. //=============================================================================
  3057. // HeightMapRenderObjClass::removeTerrainBib
  3058. //=============================================================================
  3059. /** Removes a terrainBib from the bib buffer.*/
  3060. //=============================================================================
  3061. void HeightMapRenderObjClass::removeTerrainBib(ObjectID id)
  3062. {
  3063. m_bibBuffer->removeBib( id );
  3064. };
  3065. //=============================================================================
  3066. // HeightMapRenderObjClass::removeTerrainBib
  3067. //=============================================================================
  3068. /** Removes a terrainBib from the bib buffer.*/
  3069. //=============================================================================
  3070. void HeightMapRenderObjClass::removeTerrainBibDrawable(DrawableID id)
  3071. {
  3072. m_bibBuffer->removeBibDrawable( id );
  3073. };
  3074. //=============================================================================
  3075. // HeightMapRenderObjClass::staticLightingChanged
  3076. //=============================================================================
  3077. /** Notification that all lighting needs to be recalculated. */
  3078. //=============================================================================
  3079. void HeightMapRenderObjClass::staticLightingChanged( void )
  3080. {
  3081. // Cause the terrain to get updated with new lighting.
  3082. m_needFullUpdate = true;
  3083. // Cause the scorches to get updated with new lighting.
  3084. m_scorchesInBuffer = 0; // If we just allocated the buffers, we got no scorches in the buffer.
  3085. m_curNumScorchVertices=0;
  3086. m_curNumScorchIndices=0;
  3087. }
  3088. //=============================================================================
  3089. // HeightMapRenderObjClass::setTimeOfDay
  3090. //=============================================================================
  3091. /** When the time of day changes, the lighting changes and we need to update. */
  3092. //=============================================================================
  3093. void HeightMapRenderObjClass::setTimeOfDay( TimeOfDay tod )
  3094. {
  3095. staticLightingChanged();
  3096. }
  3097. //=============================================================================
  3098. // HeightMapRenderObjClass::Notify_Added
  3099. //=============================================================================
  3100. /** W3D render object method, we use it to add ourselves to tthe update
  3101. list, so On_Frame_Update gets called. */
  3102. //=============================================================================
  3103. void HeightMapRenderObjClass::Notify_Added(SceneClass * scene)
  3104. {
  3105. RenderObjClass::Notify_Added(scene);
  3106. scene->Register(this,SceneClass::ON_FRAME_UPDATE);
  3107. }
  3108. #define CENTER_LIMIT 2
  3109. #define BIG_JUMP 16
  3110. #define WIDE_STEP 32
  3111. static Int visMinX, visMinY, visMaxX, visMaxY;
  3112. static Bool check(const FrustumClass & frustum, WorldHeightMap *pMap, Int x, Int y)
  3113. {
  3114. if (x<0 || y<0) return(false);
  3115. if (x>= pMap->getXExtent() || y>= pMap->getYExtent()) return(false);
  3116. if (x >= visMinX && y >= visMinY && x <=visMaxX && y <= visMaxY) {
  3117. return(true);
  3118. }
  3119. Int height = pMap->getHeight(x, y);
  3120. Vector3 loc((x-pMap->getBorderSize())*MAP_XY_FACTOR, (y-pMap->getBorderSize())*MAP_XY_FACTOR, height*MAP_HEIGHT_SCALE);
  3121. if (CollisionMath::Overlap_Test(frustum,loc) == CollisionMath::INSIDE) {
  3122. if (x<visMinX) visMinX=x;
  3123. if (x>visMaxX) visMaxX=x;
  3124. if (y<visMinY) visMinY=y;
  3125. if (y>visMaxY) visMaxY=y;
  3126. return(true);
  3127. }
  3128. return(false);
  3129. }
  3130. static void calcVis(const FrustumClass & frustum, WorldHeightMap *pMap, Int minX, Int minY, Int maxX, Int maxY, Int limit)
  3131. {
  3132. if (maxX-minX<2) return;
  3133. if (maxY-minY<2) return;
  3134. if (minX >=visMinX && minY >= visMinY && maxX <=visMaxX && maxY <= visMaxY) {
  3135. return;
  3136. }
  3137. Int midX = (minX+maxX)/2;
  3138. Int midY = (minY+maxY)/2;
  3139. Bool recurse1 = maxX-minX>=limit;
  3140. Bool recurse2 = recurse1;
  3141. Bool recurse3 = recurse1;
  3142. Bool recurse4 = recurse1;
  3143. /* boxes are:
  3144. 1 2
  3145. 3 4 */
  3146. if (check(frustum, pMap, midX, maxY)) {
  3147. recurse1=true;
  3148. recurse2=true;
  3149. }
  3150. if (check(frustum, pMap, midX, minY)) {
  3151. recurse3=true;
  3152. recurse4=true;
  3153. }
  3154. if (check(frustum, pMap, midX, midY)) {
  3155. recurse1=true;
  3156. recurse2=true;
  3157. recurse3=true;
  3158. recurse4=true;
  3159. }
  3160. if (check(frustum, pMap, minX, midY)) {
  3161. recurse1=true;
  3162. recurse3=true;
  3163. }
  3164. if (check(frustum, pMap, maxX, midY)) {
  3165. recurse2=true;
  3166. recurse4=true;
  3167. }
  3168. if (recurse1) {
  3169. calcVis(frustum, pMap, minX, midY, midX, maxY, limit);
  3170. }
  3171. if (recurse2) {
  3172. calcVis(frustum, pMap, midX, midY, maxX, maxY, limit);
  3173. }
  3174. if (recurse3) {
  3175. calcVis(frustum, pMap, minX, minY, midX, midY, limit);
  3176. }
  3177. if (recurse4) {
  3178. calcVis(frustum, pMap, midX, minY, maxX, midY, limit);
  3179. }
  3180. }
  3181. //=============================================================================
  3182. // HeightMapRenderObjClass::updateCenter
  3183. //=============================================================================
  3184. /** Updates the positioning of the drawn portion of the height map in the
  3185. heightmap. As the view slides around, this determines what is the actually
  3186. rendered portion of the terrain. Only a 96x96 section is rendered at any time,
  3187. even though maps can be up to 1024x1024. This function determines which subset
  3188. is rendered. */
  3189. //=============================================================================
  3190. void HeightMapRenderObjClass::updateCenter(CameraClass *camera , RefRenderObjListIterator *pLightsIterator)
  3191. {
  3192. if (m_map==NULL) {
  3193. return;
  3194. }
  3195. if (m_updating) {
  3196. return;
  3197. }
  3198. if (m_vertexBufferTiles ==NULL)
  3199. return; //did not initialize resources yet.
  3200. m_treeBuffer->doFullUpdate(); // Tell the trees to update for view change.
  3201. #ifdef TEST_CUSTOM_EDGING
  3202. m_customEdging->doFullUpdate();
  3203. #endif
  3204. m_updating = true;
  3205. if (m_needFullUpdate) {
  3206. m_needFullUpdate = false;
  3207. updateBlock(0, 0, m_x-1, m_y-1, m_map, pLightsIterator);
  3208. #ifdef DO_ROADS
  3209. if (m_roadBuffer) {
  3210. m_roadBuffer->updateLighting();
  3211. }
  3212. #endif
  3213. m_bridgeBuffer->doFullUpdate();
  3214. m_bridgeBuffer->updateCenter(camera, pLightsIterator);
  3215. m_updating = false;
  3216. return;
  3217. }
  3218. m_bridgeBuffer->updateCenter(camera, pLightsIterator);
  3219. if (m_x >= m_map->getXExtent() && m_y >= m_map->getYExtent()) {
  3220. m_updating = false;
  3221. return; // no need to center.
  3222. }
  3223. Int cellOffset = 1;
  3224. if (m_halfResMesh) {
  3225. cellOffset = 2;
  3226. }
  3227. // determine the ray corresponding to the camera and distance to projection plane
  3228. Matrix3D camera_matrix = camera->Get_Transform();
  3229. Vector3 camera_location = camera->Get_Position();
  3230. Vector3 rayLocation;
  3231. Vector3 rayDirection;
  3232. Vector3 rayDirectionPt;
  3233. // the projected ray has the same origin as the camera
  3234. rayLocation = camera_location;
  3235. // determine the location of the screen coordinate in camera-model space
  3236. const ViewportClass &viewport = camera->Get_Viewport();
  3237. Int i, j, minHt;
  3238. Real intersectionZ;
  3239. minHt = m_map->getMaxHeightValue();
  3240. for (i=0; i<m_x; i++) {
  3241. for (j=0; j<m_y; j++) {
  3242. Short cur = m_map->getDisplayHeight(i,j);
  3243. if (cur<minHt) minHt = cur;
  3244. }
  3245. }
  3246. intersectionZ = (float)minHt;
  3247. // float aspect = camera->Get_Aspect_Ratio();
  3248. Vector2 min,max;
  3249. camera->Get_View_Plane(min,max);
  3250. float xscale = (max.X - min.X);
  3251. float yscale = (max.Y - min.Y);
  3252. float zmod = -1.0; // Scene->vpd; // Note: view plane distance is now always 1.0 from the camera
  3253. float minX = 200000;
  3254. float maxX = -minX;
  3255. float minY = 200000;
  3256. float maxY = -minY;
  3257. for (i=0; i<2; i++) {
  3258. for (j=0; j<2; j++) {
  3259. float xmod = (-i + 0.5 + viewport.Min.X) * zmod * xscale;// / aspect;
  3260. float ymod = (j - 0.5 - viewport.Min.Y) * zmod * yscale;// * aspect;
  3261. // transform the screen coordinates by the camera's matrix into world coordinates.
  3262. float x = zmod * camera_matrix[0][2] + xmod * camera_matrix[0][0] + ymod * camera_matrix[0][1];
  3263. float y = zmod * camera_matrix[1][2] + xmod * camera_matrix[1][0] + ymod * camera_matrix[1][1];
  3264. float z = zmod * camera_matrix[2][2] + xmod * camera_matrix[2][0] + ymod * camera_matrix[2][1];
  3265. rayDirection.Set(x,y,z);
  3266. rayDirection.Normalize();
  3267. rayDirectionPt = rayLocation+rayDirection;
  3268. x = Vector3::Find_X_At_Z(intersectionZ, rayLocation, rayDirectionPt);
  3269. y = Vector3::Find_Y_At_Z(intersectionZ, rayLocation, rayDirectionPt);
  3270. if (x<minX) minX = x;
  3271. if (x>maxX) maxX = x;
  3272. if (y<minY) minY = y;
  3273. if (y>maxY) maxY = y;
  3274. }
  3275. }
  3276. // convert back to cell indexes.
  3277. minX /= MAP_XY_FACTOR;
  3278. maxX /= MAP_XY_FACTOR;
  3279. minY /= MAP_XY_FACTOR;
  3280. maxY /= MAP_XY_FACTOR;
  3281. minX += m_map->getBorderSize();
  3282. maxX += m_map->getBorderSize();
  3283. minY += m_map->getBorderSize();
  3284. maxY += m_map->getBorderSize();
  3285. visMinX = m_map->getXExtent();
  3286. visMinY = m_map->getYExtent();
  3287. visMaxX = 0;
  3288. visMaxY = 0;
  3289. ///< @todo find out why values go out of range
  3290. if (minX<0) minX=0;
  3291. if (minY<0) minY=0;
  3292. if (maxX > visMinX) maxX = visMinX;
  3293. if (maxY > visMinY) maxY = visMinY;
  3294. const FrustumClass & frustum = camera->Get_Frustum();
  3295. Int limit = (maxX-minX)/2;
  3296. if (limit > WIDE_STEP/2) {
  3297. limit=WIDE_STEP/2;
  3298. }
  3299. calcVis(frustum, m_map, minX-WIDE_STEP/2, minY-WIDE_STEP/2, maxX+WIDE_STEP/2, maxY+WIDE_STEP/2, limit);
  3300. if (m_map) {
  3301. Int newOrgX;
  3302. if (visMaxX-visMinX > m_x) {
  3303. newOrgX = (maxX+minX)/2-m_x/2.0;
  3304. } else {
  3305. newOrgX = (visMaxX+visMinX)/2-m_x/2.0;
  3306. }
  3307. Int newOrgY;
  3308. if (visMaxY - visMinY > m_y) {
  3309. newOrgY = visMinY+1;
  3310. } else {
  3311. newOrgY = (visMaxY+visMinY)/2-m_y/2.0;
  3312. }
  3313. if (TheTacticalView->getFieldOfView() != 0) {
  3314. newOrgX = (visMaxX+visMinX)/2-m_x/2.0;
  3315. newOrgY = (visMaxY+visMinY)/2-m_y/2.0;
  3316. }
  3317. if (m_halfResMesh) {
  3318. newOrgX &= 0xFFFFFFFE;
  3319. newOrgY &= 0xFFFFFFFE;
  3320. }
  3321. Int deltaX = newOrgX - m_map->getDrawOrgX();
  3322. Int deltaY = newOrgY - m_map->getDrawOrgY();
  3323. if (IABS(deltaX) > m_x/2 || IABS(deltaY)>m_x/2) {
  3324. m_map->setDrawOrg(newOrgX, newOrgY);
  3325. m_originY = 0;
  3326. m_originX = 0;
  3327. updateBlock(0, 0, m_x-1, m_y-1, m_map, pLightsIterator);
  3328. m_updating = false;
  3329. return;
  3330. }
  3331. if (abs(deltaX)>CENTER_LIMIT || abs(deltaY)>CENTER_LIMIT) {
  3332. if (abs(deltaY) >= CENTER_LIMIT) {
  3333. if (m_map->setDrawOrg(m_map->getDrawOrgX(), newOrgY)) {
  3334. Int minY = 0;
  3335. Int maxY = 0;
  3336. deltaY -= newOrgY - m_map->getDrawOrgY();
  3337. m_originY += deltaY;
  3338. if (m_originY >= m_y-1) m_originY -= m_y-1;
  3339. if (deltaY<0) {
  3340. minY = m_originY;
  3341. maxY = m_originY-deltaY;
  3342. } else {
  3343. minY = m_originY - deltaY;
  3344. maxY = m_originY;
  3345. }
  3346. minY-=cellOffset;
  3347. if (m_originY < 0) m_originY += m_y-1;
  3348. if (minY<0) {
  3349. minY += m_y-1;
  3350. if (minY<0) minY = 0;
  3351. updateBlock(0, minY, m_x-1, m_y-1, m_map, pLightsIterator);
  3352. updateBlock(0, 0, m_x-1, maxY, m_map, pLightsIterator);
  3353. } else {
  3354. updateBlock(0, minY, m_x-1, maxY, m_map, pLightsIterator);
  3355. }
  3356. }
  3357. // It is much more efficient to update a cople of columns one frame, and then
  3358. // a couple of rows. So if we aren't "jumping" to a new view, and have done X
  3359. // recently, return.
  3360. if (abs(deltaX) < BIG_JUMP && !m_doXNextTime) {
  3361. m_updating = false;
  3362. m_doXNextTime = true;
  3363. return; // Only do the y this frame. Do x next frame. jba.
  3364. }
  3365. }
  3366. if (abs(deltaX) > CENTER_LIMIT) {
  3367. m_doXNextTime = false;
  3368. newOrgX = m_map->getDrawOrgX() + deltaX;
  3369. if (m_map->setDrawOrg(newOrgX, m_map->getDrawOrgY())) {
  3370. Int minX = 0;
  3371. Int maxX = 0;
  3372. deltaX -= newOrgX - m_map->getDrawOrgX();
  3373. m_originX += deltaX;
  3374. if (m_originX >= m_x-1) m_originX -= m_x-1;
  3375. if (deltaX<0) {
  3376. minX = m_originX;
  3377. maxX = m_originX-deltaX;
  3378. } else {
  3379. minX = m_originX - deltaX;
  3380. maxX = m_originX;
  3381. }
  3382. minX-=cellOffset;
  3383. maxX+=cellOffset;
  3384. if (m_originX < 0) m_originX += m_x-1;
  3385. if (minX<0) {
  3386. minX += m_x-1;
  3387. if (minX<0) minX = 0;
  3388. updateBlock(minX,0,m_x-1, m_y-1, m_map, pLightsIterator);
  3389. updateBlock(0,0,maxX, m_y-1, m_map, pLightsIterator);
  3390. } else {
  3391. updateBlock(minX,0,maxX, m_y-1, m_map, pLightsIterator);
  3392. }
  3393. }
  3394. }
  3395. }
  3396. }
  3397. m_updating = false;
  3398. }
  3399. //=============================================================================
  3400. // HeightMapRenderObjClass::Render
  3401. //=============================================================================
  3402. /** Renders (draws) the terrain. */
  3403. //=============================================================================
  3404. //DECLARE_PERF_TIMER(Terrain_Render)
  3405. void HeightMapRenderObjClass::Render(RenderInfoClass & rinfo)
  3406. {
  3407. //USE_PERF_TIMER(Terrain_Render)
  3408. Int i,j,devicePasses;
  3409. W3DShaderManager::ShaderTypes st;
  3410. Bool doCloud = TheGlobalData->m_useCloudMap;
  3411. Matrix3D tm(Transform);
  3412. #if 0 // There is some weirdness sometimes with the dx8 static buffers.
  3413. // This usually fixes terrain flashing. jba.
  3414. static Int delay = 1;
  3415. delay --;
  3416. if (delay<1) {
  3417. delay = 1;
  3418. static Int ndx = -1;
  3419. ndx++;
  3420. if (ndx>=m_numVertexBufferTiles) {
  3421. ndx = 0;
  3422. }
  3423. DX8VertexBufferClass::WriteLockClass lockVtxBuffer(m_vertexBufferTiles[ndx]);
  3424. VERTEX_FORMAT *vb = (VERTEX_FORMAT*)lockVtxBuffer.Get_Vertex_Array();
  3425. vb = 0;
  3426. }
  3427. #endif
  3428. // If there are trees, tell them to draw at the transparent time to draw.
  3429. if (m_treeBuffer) {
  3430. m_treeBuffer->setIsTerrain();
  3431. }
  3432. #ifdef DO_UNIT_TIMINGS
  3433. #pragma MESSAGE("*** WARNING *** DOING DO_UNIT_TIMINGS!!!!")
  3434. return;
  3435. #endif
  3436. #ifdef EXTENDED_STATS
  3437. if (DX8Wrapper::stats.m_disableTerrain) {
  3438. return;
  3439. }
  3440. #endif
  3441. DX8Wrapper::Set_Light_Environment(rinfo.light_environment);
  3442. // Force shaders to update.
  3443. m_stageTwoTexture->restore();
  3444. DX8Wrapper::Set_Texture(0,NULL);
  3445. DX8Wrapper::Set_Texture(1,NULL);
  3446. ShaderClass::Invalidate();
  3447. // tm.Scale(ObjSpaceExtent);
  3448. DX8Wrapper::Set_Transform(D3DTS_WORLD,tm);
  3449. //Apply the shader and material
  3450. DX8Wrapper::Set_Index_Buffer(m_indexBuffer,0);
  3451. Bool doMultiPassWireFrame=FALSE;
  3452. if (((RTS3DScene *)rinfo.Camera.Get_User_Data())->getCustomPassMode() == SCENE_PASS_ALPHA_MASK ||
  3453. ((SceneClass *)rinfo.Camera.Get_User_Data())->Get_Extra_Pass_Polygon_Mode() == SceneClass::EXTRA_PASS_CLEAR_LINE)
  3454. {
  3455. if (WW3D::Is_Texturing_Enabled())
  3456. { //first pass where we just fill the z-buffer
  3457. devicePasses=1; //one pass solid, next in wireframe.
  3458. doMultiPassWireFrame=TRUE;
  3459. if (rinfo.Additional_Pass_Count())
  3460. {
  3461. rinfo.Peek_Additional_Pass(0)->Install_Materials();
  3462. renderTerrainPass(&rinfo.Camera);
  3463. rinfo.Peek_Additional_Pass(0)->UnInstall_Materials();
  3464. return;
  3465. }
  3466. }
  3467. else
  3468. { //wireframe pass
  3469. //Set to vertex diffuse lighting
  3470. DX8Wrapper::Set_Material(m_vertexMaterialClass);
  3471. //Set shader to non-textured solid color from vertex
  3472. DX8Wrapper::Set_Shader(ShaderClass::_PresetOpaqueSolidShader);
  3473. devicePasses=1; //one pass solid, next in wireframe.
  3474. DX8Wrapper::Apply_Render_State_Changes();
  3475. DX8Wrapper::Set_DX8_Texture_Stage_State( 0, D3DTSS_COLORARG2, D3DTA_TFACTOR );
  3476. DX8Wrapper::Set_DX8_Render_State(D3DRS_TEXTUREFACTOR,0xff808080);
  3477. doMultiPassWireFrame=TRUE;
  3478. renderTerrainPass(&rinfo.Camera);
  3479. DX8Wrapper::Set_DX8_Render_State(D3DRS_TEXTUREFACTOR,0xff008000);
  3480. return;
  3481. }
  3482. }
  3483. else
  3484. {
  3485. DX8Wrapper::Set_Material(m_vertexMaterialClass);
  3486. DX8Wrapper::Set_Shader(m_shaderClass);
  3487. if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT) {
  3488. doCloud = false;
  3489. }
  3490. st=W3DShaderManager::ST_TERRAIN_BASE; //set default shader
  3491. //set correct shader based on current settings
  3492. if (!ShaderClass::Is_Backface_Culling_Inverted())
  3493. { //not reflection pass
  3494. if (TheGlobalData->m_useLightMap && doCloud)
  3495. { st=W3DShaderManager::ST_TERRAIN_BASE_NOISE12;
  3496. }
  3497. else
  3498. if (TheGlobalData->m_useLightMap)
  3499. { //lightmap only
  3500. st=W3DShaderManager::ST_TERRAIN_BASE_NOISE2;
  3501. }
  3502. else
  3503. if (doCloud)
  3504. { //cloudmap only
  3505. st=W3DShaderManager::ST_TERRAIN_BASE_NOISE1;
  3506. }
  3507. }
  3508. else
  3509. { //reflection pass, just do base texture
  3510. st=W3DShaderManager::ST_TERRAIN_BASE;
  3511. }
  3512. //Find number of passes required to render current shader
  3513. devicePasses=W3DShaderManager::getShaderPasses(st);
  3514. if (m_disableTextures)
  3515. devicePasses=1; //force to 1 lighting-only pass
  3516. //Specify all textures that this shader may need.
  3517. W3DShaderManager::setTexture(0,m_stageZeroTexture);
  3518. W3DShaderManager::setTexture(1,m_stageZeroTexture);
  3519. W3DShaderManager::setTexture(2,m_stageTwoTexture); //cloud
  3520. W3DShaderManager::setTexture(3,m_stageThreeTexture);//noise
  3521. //Disable writes to destination alpha channel (if there is one)
  3522. if (DX8Wrapper::getBackBufferFormat() == WW3D_FORMAT_A8R8G8B8)
  3523. DX8Wrapper::Set_DX8_Render_State(D3DRS_COLORWRITEENABLE,D3DCOLORWRITEENABLE_BLUE|D3DCOLORWRITEENABLE_GREEN|D3DCOLORWRITEENABLE_RED);
  3524. }
  3525. Int pass;
  3526. for (pass=0; pass<devicePasses; pass++) {
  3527. #ifdef TIMING_TESTS
  3528. #endif
  3529. if (!doMultiPassWireFrame) //multi-pass wireframe doesn't use regular shaders.
  3530. {
  3531. if (m_disableTextures ) {
  3532. DX8Wrapper::Set_Shader(ShaderClass::_PresetOpaque2DShader);
  3533. DX8Wrapper::Set_Texture(0,NULL);
  3534. } else {
  3535. W3DShaderManager::setShader(st, pass);
  3536. }
  3537. }
  3538. for (j=0; j<m_numVBTilesY; j++)
  3539. for (i=0; i<m_numVBTilesX; i++)
  3540. {
  3541. static int count = 0;
  3542. count++;
  3543. Int numPolys = VERTEX_BUFFER_TILE_LENGTH*VERTEX_BUFFER_TILE_LENGTH*2;
  3544. Int numVertex = (VERTEX_BUFFER_TILE_LENGTH*2)*(VERTEX_BUFFER_TILE_LENGTH*2);
  3545. if (m_halfResMesh) {
  3546. numPolys /= 4;
  3547. numVertex /= 4;
  3548. }
  3549. DX8Wrapper::Set_Vertex_Buffer(m_vertexBufferTiles[j*m_numVBTilesX+i]);
  3550. #ifdef PRE_TRANSFORM_VERTEX
  3551. if (m_xformedVertexBuffer && pass==0) {
  3552. // Note - m_xformedVertexBuffer should only be used for non T&L hardware. jba.
  3553. DX8Wrapper::Apply_Render_State_Changes();
  3554. int code = DX8Wrapper::_Get_D3D_Device8()->ProcessVertices(0, 0, numVertex, m_xformedVertexBuffer[j*m_numVBTilesX+i], 0);
  3555. ::OutputDebugString("did process vertex\n");
  3556. }
  3557. if (m_xformedVertexBuffer) {
  3558. // Note - m_xformedVertexBuffer should only be used for non T&L hardware. jba.
  3559. DX8Wrapper::Apply_Render_State_Changes();
  3560. DX8Wrapper::_Get_D3D_Device8()->SetStreamSource(
  3561. 0,
  3562. m_xformedVertexBuffer[j*m_numVBTilesX+i],
  3563. D3DXGetFVFVertexSize(D3DFVF_XYZRHW |D3DFVF_DIFFUSE|D3DFVF_TEX2));
  3564. DX8Wrapper::_Get_D3D_Device8()->SetVertexShader(D3DFVF_XYZRHW |D3DFVF_DIFFUSE|D3DFVF_TEX2);
  3565. }
  3566. #endif
  3567. if (Is_Hidden() == 0) {
  3568. DX8Wrapper::Draw_Triangles( 0,numPolys, 0, numVertex);
  3569. }
  3570. }
  3571. }
  3572. if (!doMultiPassWireFrame)
  3573. {
  3574. if (pass) //shader was applied at least once?
  3575. W3DShaderManager::resetShader(st);
  3576. //Draw feathered shorelines
  3577. renderShoreLines(&rinfo.Camera);
  3578. //Do additional pass over any tiles that have 3 textures blended together.
  3579. if (TheGlobalData->m_use3WayTerrainBlends)
  3580. renderExtraBlendTiles();
  3581. #ifdef TEST_CUSTOM_EDGING
  3582. // Draw edging just before last pass.
  3583. DX8Wrapper::Set_Texture(0,NULL);
  3584. DX8Wrapper::Set_Texture(1,NULL);
  3585. m_stageTwoTexture->restore();
  3586. Int yCoordMin = m_map->getDrawOrgY();
  3587. Int yCoordMax = m_y+m_map->getDrawOrgY()-1;
  3588. Int xCoordMin = m_map->getDrawOrgX();
  3589. Int xCoordMax = m_x+m_map->getDrawOrgX()-1;
  3590. m_customEdging->drawEdging(m_map, xCoordMin, xCoordMax, yCoordMin, yCoordMax,
  3591. m_stageZeroTexture, doCloud?m_stageTwoTexture:NULL, TheGlobalData->m_useLightMap?m_stageThreeTexture:NULL);
  3592. #endif
  3593. #ifdef DO_ROADS
  3594. DX8Wrapper::Set_Texture(0,NULL);
  3595. DX8Wrapper::Set_Texture(1,NULL);
  3596. m_stageTwoTexture->restore();
  3597. ShaderClass::Invalidate();
  3598. if (!ShaderClass::Is_Backface_Culling_Inverted()) {
  3599. DX8Wrapper::Set_Material(m_vertexMaterialClass);
  3600. if (Scene) {
  3601. RTS3DScene *pMyScene = (RTS3DScene *)Scene;
  3602. RefRenderObjListIterator pDynamicLightsIterator(pMyScene->getDynamicLights());
  3603. m_roadBuffer->drawRoads(&rinfo.Camera, doCloud?m_stageTwoTexture:NULL, TheGlobalData->m_useLightMap?m_stageThreeTexture:NULL,
  3604. m_disableTextures,xCoordMin-m_map->getBorderSize(), xCoordMax-m_map->getBorderSize(), yCoordMin-m_map->getBorderSize(), yCoordMax-m_map->getBorderSize(), &pDynamicLightsIterator);
  3605. }
  3606. }
  3607. #endif
  3608. #ifdef DO_SCORCH
  3609. DX8Wrapper::Set_Texture(0,NULL);
  3610. DX8Wrapper::Set_Texture(1,NULL);
  3611. m_stageTwoTexture->restore();
  3612. ShaderClass::Invalidate();
  3613. if (!ShaderClass::Is_Backface_Culling_Inverted()) {
  3614. drawScorches();
  3615. }
  3616. #endif
  3617. DX8Wrapper::Set_Texture(0,NULL);
  3618. DX8Wrapper::Set_Texture(1,NULL);
  3619. m_stageTwoTexture->restore();
  3620. ShaderClass::Invalidate();
  3621. DX8Wrapper::Apply_Render_State_Changes();
  3622. m_bridgeBuffer->drawBridges(&rinfo.Camera, m_disableTextures, m_stageTwoTexture);
  3623. if (TheTerrainTracksRenderObjClassSystem)
  3624. TheTerrainTracksRenderObjClassSystem->flush();
  3625. if (m_shroud && rinfo.Additional_Pass_Count())
  3626. {
  3627. rinfo.Peek_Additional_Pass(0)->Install_Materials();
  3628. renderTerrainPass(&rinfo.Camera);
  3629. rinfo.Peek_Additional_Pass(0)->UnInstall_Materials();
  3630. }
  3631. ShaderClass::Invalidate();
  3632. DX8Wrapper::Apply_Render_State_Changes();
  3633. }
  3634. else
  3635. m_bridgeBuffer->drawBridges(&rinfo.Camera, m_disableTextures, m_stageTwoTexture);
  3636. m_waypointBuffer->drawWaypoints(rinfo);
  3637. m_bibBuffer->renderBibs();
  3638. // We do some custom blending, so tell the shader class to reset everything.
  3639. DX8Wrapper::Set_Texture(0,NULL);
  3640. DX8Wrapper::Set_Texture(1,NULL);
  3641. m_stageTwoTexture->restore();
  3642. ShaderClass::Invalidate();
  3643. DX8Wrapper::Set_Material(NULL);
  3644. }
  3645. /**Render parts of terrain that are along the coast line and have vertices directly under the
  3646. water plane. Applying a custom render to these polygons allows for a smoother land->water
  3647. transition*/
  3648. void HeightMapRenderObjClass::renderShoreLines(CameraClass *pCamera)
  3649. {
  3650. if (!TheGlobalData->m_showSoftWaterEdge || TheWaterTransparency->m_transparentWaterDepth==0 || m_numShoreLineTiles == 0)
  3651. return;
  3652. //Check if video card is capable of using this effect
  3653. if (DX8Wrapper::getBackBufferFormat() != WW3D_FORMAT_A8R8G8B8)
  3654. return; //can't apply effect on cards without destination alpha
  3655. Int vertexCount = 0;
  3656. Int indexCount = 0;
  3657. Int xExtent = m_map->getXExtent();
  3658. Int border = m_map->getBorderSize();
  3659. Int drawEdgeY=m_map->getDrawOrgY()+m_map->getDrawHeight()-1;
  3660. Int drawEdgeX=m_map->getDrawOrgX()+m_map->getDrawWidth()-1;
  3661. if (drawEdgeX > (m_map->getXExtent()-1))
  3662. drawEdgeX = m_map->getXExtent()-1;
  3663. if (drawEdgeY > (m_map->getYExtent()-1))
  3664. drawEdgeY = m_map->getYExtent()-1;
  3665. Int drawStartX=m_map->getDrawOrgX();
  3666. Int drawStartY=m_map->getDrawOrgY();
  3667. const UnsignedByte* data = m_map->getDataPtr();
  3668. Int j=0;
  3669. ShaderClass unlitShader=ShaderClass::_PresetOpaque2DShader;
  3670. unlitShader.Set_Depth_Compare(ShaderClass::PASS_LEQUAL);
  3671. DX8Wrapper::Set_Shader(unlitShader);
  3672. VertexMaterialClass *vmat=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
  3673. DX8Wrapper::Set_Material(vmat);
  3674. REF_PTR_RELEASE(vmat);
  3675. DX8Wrapper::Set_Texture(0,m_destAlphaTexture);
  3676. DX8Wrapper::Set_Transform(D3DTS_WORLD,Matrix3D(1));
  3677. //Enabled writes to destination alpha only
  3678. DX8Wrapper::Set_DX8_Render_State(D3DRS_COLORWRITEENABLE,D3DCOLORWRITEENABLE_ALPHA);
  3679. DX8Wrapper::Set_DX8_Texture_Stage_State(0, D3DTSS_TEXCOORDINDEX, 0);
  3680. while (j != m_numShoreLineTiles)
  3681. {
  3682. { //Need to put this in another code block so vb/ib gets automatically locked/unlocked by destructors
  3683. DynamicVBAccessClass vb_access(BUFFER_TYPE_DYNAMIC_DX8,dynamic_fvf_type,DEFAULT_MAX_BATCH_SHORELINE_TILES*4);
  3684. DynamicIBAccessClass ib_access(BUFFER_TYPE_DYNAMIC_DX8,DEFAULT_MAX_BATCH_SHORELINE_TILES*6);
  3685. DynamicVBAccessClass::WriteLockClass lock(&vb_access);
  3686. VertexFormatXYZNDUV2 *vb= lock.Get_Formatted_Vertex_Array();
  3687. DynamicIBAccessClass::WriteLockClass lockib(&ib_access);
  3688. UnsignedShort *ib=lockib.Get_Index_Array();
  3689. if (!ib || !vb)
  3690. { DX8Wrapper::Set_DX8_Render_State(D3DRS_COLORWRITEENABLE,D3DCOLORWRITEENABLE_BLUE|D3DCOLORWRITEENABLE_GREEN|D3DCOLORWRITEENABLE_RED);
  3691. return;
  3692. }
  3693. //Loop over visible terrain and extract all the tiles that need shoreline blend
  3694. for (; j<m_numShoreLineTiles; j++)
  3695. {
  3696. if (vertexCount >= (DEFAULT_MAX_BATCH_SHORELINE_TILES*4))
  3697. break; //no room in vertex buffer
  3698. shoreLineTileInfo *shoreInfo=&m_shoreLineTilePositions[j];
  3699. Int x = shoreInfo->m_xy & 0xffff;
  3700. Int y = shoreInfo->m_xy >> 16;
  3701. if (x >= drawStartX && x < drawEdgeX && y >= drawStartY && y < drawEdgeY)
  3702. { //this tile is inside visible region
  3703. Int idx = x+y*xExtent;
  3704. Real p0=data[idx]*MAP_HEIGHT_SCALE;
  3705. Real p1=data[idx+1]*MAP_HEIGHT_SCALE;
  3706. Real p2=data[idx + 1 + xExtent]*MAP_HEIGHT_SCALE;
  3707. Real p3=data[idx + xExtent]*MAP_HEIGHT_SCALE;
  3708. vb->x=(x-border)*MAP_XY_FACTOR;
  3709. vb->y=(y-border)*MAP_XY_FACTOR;
  3710. vb->z=p0;
  3711. vb->u1=shoreInfo->t0;
  3712. vb->v1=0;
  3713. vb++;
  3714. vb->x=(x+1-border)*MAP_XY_FACTOR;
  3715. vb->y=(y-border)*MAP_XY_FACTOR;
  3716. vb->z=p1;
  3717. vb->u1=shoreInfo->t1;
  3718. vb->v1=0;
  3719. vb++;
  3720. vb->x=(x+1-border)*MAP_XY_FACTOR;
  3721. vb->y=(y+1-border)*MAP_XY_FACTOR;
  3722. vb->z=p2;
  3723. vb->u1=shoreInfo->t2;
  3724. vb->v1=0;
  3725. vb++;
  3726. vb->x=(x-border)*MAP_XY_FACTOR;
  3727. vb->y=(y+1-border)*MAP_XY_FACTOR;
  3728. vb->z=p3;
  3729. vb->u1=shoreInfo->t3;
  3730. vb->v1=0;
  3731. vb++;
  3732. if (m_map->getFlipState(x,y))
  3733. {
  3734. ib[0]=1+vertexCount;
  3735. ib[1]=3+vertexCount;
  3736. ib[2]=0+vertexCount;
  3737. ib[3]=1+vertexCount;
  3738. ib[4]=2+vertexCount;
  3739. ib[5]=3+vertexCount;
  3740. }
  3741. else
  3742. {
  3743. ib[0]=0+vertexCount;
  3744. ib[1]=2+vertexCount;
  3745. ib[2]=3+vertexCount;
  3746. ib[3]=0+vertexCount;
  3747. ib[4]=1+vertexCount;
  3748. ib[5]=2+vertexCount;
  3749. }
  3750. ib += 6;
  3751. vertexCount +=4;
  3752. indexCount +=6;
  3753. }
  3754. }
  3755. DX8Wrapper::Set_Index_Buffer(ib_access,0);
  3756. DX8Wrapper::Set_Vertex_Buffer(vb_access);
  3757. }//lock and fill ib/vb
  3758. if (indexCount > 0 && vertexCount > 0)
  3759. DX8Wrapper::Draw_Triangles( 0,indexCount/3, 0, vertexCount); //draw a quad, 2 triangles, 4 verts
  3760. vertexCount=0;
  3761. indexCount=0;
  3762. }//for all shore tiles
  3763. //Disable writes to destination alpha
  3764. DX8Wrapper::Set_DX8_Render_State(D3DRS_COLORWRITEENABLE,D3DCOLORWRITEENABLE_BLUE|D3DCOLORWRITEENABLE_GREEN|D3DCOLORWRITEENABLE_RED);
  3765. ShaderClass::Invalidate();
  3766. }
  3767. ///Performs additional terrain rendering pass, blending in the black shroud texture.
  3768. void HeightMapRenderObjClass::renderTerrainPass(CameraClass *pCamera)
  3769. {
  3770. DX8Wrapper::Set_Transform(D3DTS_WORLD,Matrix3D(1));
  3771. //Apply the shader and material
  3772. DX8Wrapper::Set_Index_Buffer(m_indexBuffer,0);
  3773. for (Int j=0; j<m_numVBTilesY; j++)
  3774. for (Int i=0; i<m_numVBTilesX; i++)
  3775. {
  3776. static int count = 0;
  3777. count++;
  3778. Int numPolys = VERTEX_BUFFER_TILE_LENGTH*VERTEX_BUFFER_TILE_LENGTH*2;
  3779. Int numVertex = (VERTEX_BUFFER_TILE_LENGTH*2)*(VERTEX_BUFFER_TILE_LENGTH*2);
  3780. if (m_halfResMesh) {
  3781. numPolys /= 4;
  3782. numVertex /= 4;
  3783. }
  3784. DX8Wrapper::Set_Vertex_Buffer(m_vertexBufferTiles[j*m_numVBTilesX+i]);
  3785. #ifdef PRE_TRANSFORM_VERTEX
  3786. if (m_xformedVertexBuffer && pass==0) {
  3787. // Note - m_xformedVertexBuffer should only be used for non T&L hardware. jba.
  3788. DX8Wrapper::Apply_Render_State_Changes();
  3789. int code = DX8Wrapper::_Get_D3D_Device8()->ProcessVertices(0, 0, numVertex, m_xformedVertexBuffer[j*m_numVBTilesX+i], 0);
  3790. ::OutputDebugString("did process vertex\n");
  3791. }
  3792. if (m_xformedVertexBuffer) {
  3793. // Note - m_xformedVertexBuffer should only be used for non T&L hardware. jba.
  3794. DX8Wrapper::Apply_Render_State_Changes();
  3795. DX8Wrapper::_Get_D3D_Device8()->SetStreamSource(
  3796. 0,
  3797. m_xformedVertexBuffer[j*m_numVBTilesX+i],
  3798. D3DXGetFVFVertexSize(D3DFVF_XYZRHW |D3DFVF_DIFFUSE|D3DFVF_TEX2));
  3799. DX8Wrapper::_Get_D3D_Device8()->SetVertexShader(D3DFVF_XYZRHW |D3DFVF_DIFFUSE|D3DFVF_TEX2);
  3800. }
  3801. #endif
  3802. if (Is_Hidden() == 0) {
  3803. DX8Wrapper::Draw_Triangles( 0,numPolys, 0, numVertex);
  3804. }
  3805. }
  3806. }
  3807. //=============================================================================
  3808. // HeightMapRenderObjClass::renderTrees
  3809. //=============================================================================
  3810. /** Renders (draws) the trees. Since the trees are transparent, this has to be
  3811. called after flush. */
  3812. //=============================================================================
  3813. void HeightMapRenderObjClass::renderTrees(CameraClass * camera)
  3814. {
  3815. #ifdef EXTENDED_STATS
  3816. if (DX8Wrapper::stats.m_disableObjects) {
  3817. return;
  3818. }
  3819. #endif
  3820. if (m_map==NULL) return;
  3821. if (Scene==NULL) return;
  3822. if (m_treeBuffer) {
  3823. Matrix3D tm(Transform);
  3824. DX8Wrapper::Set_Transform(D3DTS_WORLD,tm);
  3825. DX8Wrapper::Set_Material(m_vertexMaterialClass);
  3826. RTS3DScene *pMyScene = (RTS3DScene *)Scene;
  3827. RefRenderObjListIterator pDynamicLightsIterator(pMyScene->getDynamicLights());
  3828. m_treeBuffer->drawTrees(camera, &pDynamicLightsIterator);
  3829. }
  3830. }
  3831. /** Renders an additoinal terrain pass including only those tiles which have more than 2 textures
  3832. blended together. Used primarily for corner cases where 3 different textures meet.*/
  3833. void HeightMapRenderObjClass::renderExtraBlendTiles(void)
  3834. {
  3835. Int vertexCount = 0;
  3836. Int indexCount = 0;
  3837. Int xExtent = m_map->getXExtent();
  3838. Int border = m_map->getBorderSize();
  3839. static Int maxBlendTiles = DEFAULT_MAX_FRAME_EXTRABLEND_TILES;
  3840. if (!m_numExtraBlendTiles)
  3841. return; //nothing to draw
  3842. if (maxBlendTiles > 10000) //we can only fit about 10000 tiles into a single VB.
  3843. maxBlendTiles = 10000;
  3844. DynamicVBAccessClass vb_access(BUFFER_TYPE_DYNAMIC_DX8,DX8_FVF_XYZNDUV2,maxBlendTiles*4);
  3845. DynamicIBAccessClass ib_access(BUFFER_TYPE_DYNAMIC_DX8,maxBlendTiles*6);
  3846. {
  3847. DynamicVBAccessClass::WriteLockClass lock(&vb_access);
  3848. VertexFormatXYZNDUV2* vb= lock.Get_Formatted_Vertex_Array();
  3849. DynamicIBAccessClass::WriteLockClass lockib(&ib_access);
  3850. UnsignedShort *ib=lockib.Get_Index_Array();
  3851. if (!vb || !ib) return;
  3852. const UnsignedByte* data = m_map->getDataPtr();
  3853. //Loop over visible terrain and extract all the tiles that need extra blend
  3854. Int drawEdgeY=m_map->getDrawOrgY()+m_map->getDrawHeight()-1;
  3855. Int drawEdgeX=m_map->getDrawOrgX()+m_map->getDrawWidth()-1;
  3856. if (drawEdgeX > (m_map->getXExtent()-1))
  3857. drawEdgeX = m_map->getXExtent()-1;
  3858. if (drawEdgeY > (m_map->getYExtent()-1))
  3859. drawEdgeY = m_map->getYExtent()-1;
  3860. Int drawStartX=m_map->getDrawOrgX();
  3861. Int drawStartY=m_map->getDrawOrgY();
  3862. for (Int j=0; j<m_numExtraBlendTiles; j++)
  3863. {
  3864. if (vertexCount >= (maxBlendTiles*4))
  3865. break; //no room in vertex buffer
  3866. Real U[4],V[4];
  3867. UnsignedByte alpha[4];
  3868. Bool flipState,cliffState;
  3869. Int x = m_extraBlendTilePositions[j] & 0xffff;
  3870. Int y = m_extraBlendTilePositions[j] >> 16;
  3871. if (x >= drawStartX && x < drawEdgeX &&
  3872. y >= drawStartY && y < drawEdgeY &&
  3873. m_map->getExtraAlphaUVData(x,y,U,V,alpha,&flipState, &cliffState))
  3874. { //this tile is inside visible region and has 3rd blend layer.
  3875. Int idx = x+y*xExtent;
  3876. Real p0=data[idx]*MAP_HEIGHT_SCALE;
  3877. Real p1=data[idx+1]*MAP_HEIGHT_SCALE;
  3878. Real p2=data[idx + 1 + xExtent]*MAP_HEIGHT_SCALE;
  3879. Real p3=data[idx + xExtent]*MAP_HEIGHT_SCALE;
  3880. if (cliffState && abs(p0-p2) > abs(p1-p3)) //cliffs sometimes force a flip
  3881. flipState = TRUE;
  3882. vb->x=(x-border)*MAP_XY_FACTOR;
  3883. vb->y=(y-border)*MAP_XY_FACTOR;
  3884. vb->z=p0;
  3885. vb->diffuse=(alpha[0]<<24)|(getStaticDiffuse(x,y) & 0x00ffffff);
  3886. vb->u1=U[0];
  3887. vb->v1=V[0];
  3888. vb++;
  3889. vb->x=(x+1-border)*MAP_XY_FACTOR;
  3890. vb->y=(y-border)*MAP_XY_FACTOR;
  3891. vb->z=p1;
  3892. vb->diffuse=(alpha[1]<<24)|(getStaticDiffuse(x+1,y) & 0x00ffffff);
  3893. vb->u1=U[1];
  3894. vb->v1=V[1];
  3895. vb++;
  3896. vb->x=(x+1-border)*MAP_XY_FACTOR;
  3897. vb->y=(y+1-border)*MAP_XY_FACTOR;
  3898. vb->z=p2;
  3899. vb->diffuse=(alpha[2]<<24)|(getStaticDiffuse(x+1,y+1) & 0x00ffffff);
  3900. vb->u1=U[2];
  3901. vb->v1=V[2];
  3902. vb++;
  3903. vb->x=(x-border)*MAP_XY_FACTOR;
  3904. vb->y=(y+1-border)*MAP_XY_FACTOR;
  3905. vb->z=p3;
  3906. vb->diffuse=(alpha[3]<<24)|(getStaticDiffuse(x,y+1) & 0x00ffffff);
  3907. vb->u1=U[3];
  3908. vb->v1=V[3];
  3909. vb++;
  3910. if (flipState)
  3911. {
  3912. ib[0]=1+vertexCount;
  3913. ib[1]=3+vertexCount;
  3914. ib[2]=0+vertexCount;
  3915. ib[3]=1+vertexCount;
  3916. ib[4]=2+vertexCount;
  3917. ib[5]=3+vertexCount;
  3918. }
  3919. else
  3920. {
  3921. ib[0]=0+vertexCount;
  3922. ib[1]=2+vertexCount;
  3923. ib[2]=3+vertexCount;
  3924. ib[3]=0+vertexCount;
  3925. ib[4]=1+vertexCount;
  3926. ib[5]=2+vertexCount;
  3927. }
  3928. ib += 6;
  3929. vertexCount +=4;
  3930. indexCount +=6;
  3931. }//tile has 3rd blend layer and is visible
  3932. } //for all extre blend tiles
  3933. }//unlock vertex buffer
  3934. if (vertexCount)
  3935. {
  3936. //Check if we couldn't fit all blend tiles into vertex buffer so we can enlarge it for next frame.
  3937. if (vertexCount == (maxBlendTiles*4))
  3938. maxBlendTiles += 16; //enlarge by 16 to reduce trashing.
  3939. ShaderClass::Invalidate(); //invalidate to force shader to reset since we directly changed states
  3940. DX8Wrapper::Set_Index_Buffer(ib_access,0);
  3941. DX8Wrapper::Set_Vertex_Buffer(vb_access);
  3942. VertexMaterialClass *vmat=VertexMaterialClass::Get_Preset(VertexMaterialClass::PRELIT_DIFFUSE);
  3943. DX8Wrapper::Set_Material(vmat);
  3944. REF_PTR_RELEASE(vmat);
  3945. ShaderClass shader=ShaderClass::_PresetOpaqueShader;
  3946. shader.Set_Depth_Mask(ShaderClass::DEPTH_WRITE_DISABLE); //disable writes to z
  3947. DX8Wrapper::Set_Shader(shader);
  3948. if (TheGlobalData->m_use3WayTerrainBlends == 2)
  3949. {
  3950. shader.Set_Primary_Gradient(ShaderClass::GRADIENT_DISABLE); //disable lighting.
  3951. shader.Set_Texturing(ShaderClass::TEXTURING_DISABLE); //disable texturing.
  3952. DX8Wrapper::Set_Shader(shader);
  3953. DX8Wrapper::Set_Texture(0,NULL); //debug mode which draws terrain tiles in white.
  3954. if (Is_Hidden() == 0) {
  3955. DX8Wrapper::Draw_Triangles( 0,indexCount/3, 0, vertexCount); //draw a quad, 2 triangles, 4 verts
  3956. }
  3957. }
  3958. else
  3959. {
  3960. W3DShaderManager::setTexture(0,m_stageOneTexture);
  3961. W3DShaderManager::setTexture(1,m_stageTwoTexture); //cloud
  3962. W3DShaderManager::setTexture(2,m_stageThreeTexture); //noise/lightmap
  3963. W3DShaderManager::ShaderTypes st = W3DShaderManager::ST_ROAD_BASE;
  3964. Bool doCloud = TheGlobalData->m_useCloudMap;
  3965. if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT) {
  3966. doCloud = false;
  3967. }
  3968. if (TheGlobalData->m_useLightMap && doCloud)
  3969. {
  3970. st = W3DShaderManager::ST_ROAD_BASE_NOISE12;
  3971. }
  3972. else if (TheGlobalData->m_useLightMap)
  3973. { //lightmap only
  3974. st = W3DShaderManager::ST_ROAD_BASE_NOISE2;
  3975. }
  3976. else if (doCloud)
  3977. { //cloudmap only
  3978. st = W3DShaderManager::ST_ROAD_BASE_NOISE1;
  3979. }
  3980. Int devicePasses=W3DShaderManager::getShaderPasses(st);
  3981. for (Int pass=0; pass < devicePasses; pass++)
  3982. {
  3983. W3DShaderManager::setShader(st, pass);
  3984. //Draw all this road type.
  3985. if (Is_Hidden() == 0) {
  3986. DX8Wrapper::Draw_Triangles( 0,indexCount/3, 0, vertexCount); //draw a quad, 2 triangles, 4 verts
  3987. }
  3988. }
  3989. W3DShaderManager::resetShader(st);
  3990. }
  3991. }
  3992. }