W3DView.cpp 101 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006
  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: W3DView.cpp //////////////////////////////////////////////////////////////////////////////
  24. //
  25. // W3D implementation of the game view class. This view allows us to have
  26. // a "window" into the game world that can change its width, height as
  27. // well as camera positioning controls
  28. //
  29. // Author: Colin Day, April 2001
  30. //
  31. ///////////////////////////////////////////////////////////////////////////////////////////////////
  32. // SYSTEM INCLUDES ////////////////////////////////////////////////////////////////////////////////
  33. #include <stdlib.h>
  34. #include <windows.h>
  35. // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
  36. #include "Common/BuildAssistant.h"
  37. #include "Common/GlobalData.h"
  38. #include "Common/Module.h"
  39. #include "Common/RandomValue.h"
  40. #include "Common/ThingTemplate.h"
  41. #include "Common/PerfTimer.h"
  42. #include "Common/PlayerList.h"
  43. #include "Common/Player.h"
  44. #include "GameClient/Color.h"
  45. #include "GameClient/CommandXlat.h"
  46. #include "GameClient/Drawable.h"
  47. #include "GameClient/GameClient.h"
  48. #include "GameClient/GameWindowManager.h"
  49. #include "GameClient/Image.h"
  50. #include "GameClient/InGameUI.h"
  51. #include "GameClient/Line2D.h"
  52. #include "GameClient/SelectionInfo.h"
  53. #include "GameClient/Shell.h"
  54. #include "GameClient/TerrainVisual.h"
  55. #include "GameClient/Water.h"
  56. #include "GameLogic/AI.h" ///< For AI debug (yes, I'm cheating for now)
  57. #include "GameLogic/AIPathfind.h" ///< For AI debug (yes, I'm cheating for now)
  58. #include "GameLogic/ExperienceTracker.h"
  59. #include "GameLogic/GameLogic.h"
  60. #include "GameLogic/Module/AIUpdate.h"
  61. #include "GameLogic/Module/BodyModule.h"
  62. #include "GameLogic/Module/ContainModule.h"
  63. #include "GameLogic/Module/OpenContain.h"
  64. #include "GameLogic/Object.h"
  65. #include "GameLogic/ScriptEngine.h"
  66. #include "GameLogic/TerrainLogic.h" ///< @todo This should be TerrainVisual (client side)
  67. #include "W3DDevice/Common/W3DConvert.h"
  68. #include "W3DDevice/GameClient/HeightMap.h"
  69. #include "W3DDevice/GameClient/W3DAssetManager.h"
  70. #include "W3DDevice/GameClient/W3DDisplay.h"
  71. #include "W3DDevice/GameClient/W3DScene.h"
  72. #include "W3DDevice/GameClient/W3DView.h"
  73. #include "D3dx8math.h"
  74. #include "W3DDevice/GameClient/W3DShaderManager.h"
  75. #include "W3DDevice/GameClient/Module/W3DModelDraw.h"
  76. #include "W3DDevice/GameClient/W3DCustomScene.h"
  77. #include "WW3D2/DX8Renderer.h"
  78. #include "WW3D2/Light.h"
  79. #include "WW3D2/Camera.h"
  80. #include "WW3D2/Coltype.h"
  81. #include "WW3D2/PredLod.h"
  82. #include "WW3D2/WW3D.h"
  83. #include "WinMain.h" /** @todo Remove this, it's only here because we
  84. are using timeGetTime, but we can remove that
  85. when we have our own timer */
  86. #ifdef _INTERNAL
  87. // for occasional debugging...
  88. //#pragma optimize("", off)
  89. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  90. #endif
  91. // 30 fps
  92. Int TheW3DFrameLengthInMsec = 1000/LOGICFRAMES_PER_SECOND; // default is 33msec/frame == 30fps. but we may change it depending on sys config.
  93. static const Int MAX_REQUEST_CACHE_SIZE = 40; // Any size larger than 10, or examine code below for changes. jkmcd.
  94. static const Real DRAWABLE_OVERSCAN = 75.0f; ///< 3D world coords of how much to overscan in the 3D screen region
  95. //=================================================================================================
  96. inline Real minf(Real a, Real b) { if (a < b) return a; else return b; }
  97. inline Real maxf(Real a, Real b) { if (a > b) return a; else return b; }
  98. //-------------------------------------------------------------------------------------------------
  99. // Normalizes angle to +- PI.
  100. //-------------------------------------------------------------------------------------------------
  101. static void normAngle(Real &angle)
  102. {
  103. if (angle < -10*PI) {
  104. angle = 0;
  105. }
  106. if (angle > 10*PI) {
  107. angle = 0;
  108. }
  109. while (angle < -PI) {
  110. angle += 2*PI;
  111. }
  112. while (angle > PI) {
  113. angle -= 2*PI;
  114. }
  115. }
  116. #define TERRAIN_SAMPLE_SIZE 40.0f
  117. static Real getHeightAroundPos(Real x, Real y)
  118. {
  119. // terrain height + desired height offset == cameraOffset * actual zoom
  120. Real terrainHeight = TheTerrainLogic->getGroundHeight(x, y);
  121. // find best approximation of max terrain height we can see
  122. Real terrainHeightMax = terrainHeight;
  123. terrainHeightMax = max(terrainHeightMax, TheTerrainLogic->getGroundHeight(x+TERRAIN_SAMPLE_SIZE, y-TERRAIN_SAMPLE_SIZE));
  124. terrainHeightMax = max(terrainHeightMax, TheTerrainLogic->getGroundHeight(x-TERRAIN_SAMPLE_SIZE, y-TERRAIN_SAMPLE_SIZE));
  125. terrainHeightMax = max(terrainHeightMax, TheTerrainLogic->getGroundHeight(x+TERRAIN_SAMPLE_SIZE, y+TERRAIN_SAMPLE_SIZE));
  126. terrainHeightMax = max(terrainHeightMax, TheTerrainLogic->getGroundHeight(x-TERRAIN_SAMPLE_SIZE, y+TERRAIN_SAMPLE_SIZE));
  127. return terrainHeightMax;
  128. }
  129. //-------------------------------------------------------------------------------------------------
  130. //-------------------------------------------------------------------------------------------------
  131. W3DView::W3DView()
  132. {
  133. m_3DCamera = NULL;
  134. m_2DCamera = NULL;
  135. m_groundLevel = 10.0;
  136. m_cameraOffset.z = TheGlobalData->m_cameraHeight;
  137. m_cameraOffset.y = -(m_cameraOffset.z / tan(TheGlobalData->m_cameraPitch * (PI / 180.0)));
  138. m_cameraOffset.x = -(m_cameraOffset.y * tan(TheGlobalData->m_cameraYaw * (PI / 180.0)));
  139. m_viewFilterMode = FM_NULL_MODE;
  140. m_viewFilter = FT_NULL_FILTER;
  141. m_isWireFrameEnabled = m_nextWireFrameEnabled = FALSE;
  142. m_shakeOffset.x = 0.0f;
  143. m_shakeOffset.y = 0.0f;
  144. m_shakeIntensity = 0.0f;
  145. m_FXPitch = 1.0f;
  146. m_freezeTimeForCameraMovement = false;
  147. m_cameraHasMovedSinceRequest = true;
  148. m_locationRequests.clear();
  149. m_locationRequests.reserve(MAX_REQUEST_CACHE_SIZE + 10); // This prevents the vector from ever re-allocing
  150. } // end W3DView
  151. //-------------------------------------------------------------------------------------------------
  152. //-------------------------------------------------------------------------------------------------
  153. W3DView::~W3DView()
  154. {
  155. REF_PTR_RELEASE( m_2DCamera );
  156. REF_PTR_RELEASE( m_3DCamera );
  157. } // end ~W3DView
  158. //-------------------------------------------------------------------------------------------------
  159. /** Sets the height of the viewport, while maintaining original camera perspective. */
  160. //-------------------------------------------------------------------------------------------------
  161. void W3DView::setHeight(Int height)
  162. {
  163. // extend View functionality
  164. View::setHeight(height);
  165. Vector2 vMin,vMax;
  166. m_3DCamera->Set_Aspect_Ratio((Real)getWidth()/(Real)height);
  167. m_3DCamera->Get_Viewport(vMin,vMax);
  168. vMax.Y=(Real)(m_originY+height)/(Real)TheDisplay->getHeight();
  169. m_3DCamera->Set_Viewport(vMin,vMax);
  170. }
  171. //-------------------------------------------------------------------------------------------------
  172. /** Sets the width of the viewport, while maintaining original camera perspective. */
  173. //-------------------------------------------------------------------------------------------------
  174. void W3DView::setWidth(Int width)
  175. {
  176. // extend View functionality
  177. View::setWidth(width);
  178. Vector2 vMin,vMax;
  179. m_3DCamera->Set_Aspect_Ratio((Real)width/(Real)getHeight());
  180. m_3DCamera->Get_Viewport(vMin,vMax);
  181. vMax.X=(Real)(m_originX+width)/(Real)TheDisplay->getWidth();
  182. m_3DCamera->Set_Viewport(vMin,vMax);
  183. //we want to maintain the same scale, so we'll need to adjust the fov.
  184. //default W3D fov for full-screen is 50 degrees.
  185. m_3DCamera->Set_View_Plane((Real)width/(Real)TheDisplay->getWidth()*DEG_TO_RADF(50.0f),-1);
  186. }
  187. //-------------------------------------------------------------------------------------------------
  188. /** Sets location of top-left view corner on display */
  189. //-------------------------------------------------------------------------------------------------
  190. void W3DView::setOrigin( Int x, Int y)
  191. {
  192. // extend View functionality
  193. View::setOrigin(x,y);
  194. Vector2 vMin,vMax;
  195. m_3DCamera->Get_Viewport(vMin,vMax);
  196. vMin.X=(Real)x/(Real)TheDisplay->getWidth();
  197. vMin.Y=(Real)y/(Real)TheDisplay->getHeight();
  198. m_3DCamera->Set_Viewport(vMin,vMax);
  199. // bottom-right border was also moved my this call, so force an update of extents.
  200. setWidth(m_width);
  201. setHeight(m_height);
  202. }
  203. //-------------------------------------------------------------------------------------------------
  204. /** @todo This is inefficient. We should construct the matrix directly using vectors. */
  205. //-------------------------------------------------------------------------------------------------
  206. void W3DView::buildCameraTransform( Matrix3D *transform )
  207. {
  208. Vector3 sourcePos, targetPos;
  209. Real groundLevel = m_groundLevel; // 93.0f;
  210. Real zoom = getZoom();
  211. Real angle = getAngle();
  212. Real pitch = getPitch();
  213. Coord3D pos = *getPosition();
  214. // add in the camera shake, if any
  215. pos.x += m_shakeOffset.x;
  216. pos.y += m_shakeOffset.y;
  217. if (m_cameraConstraintValid)
  218. {
  219. pos.x = maxf(m_cameraConstraint.lo.x, pos.x);
  220. pos.x = minf(m_cameraConstraint.hi.x, pos.x);
  221. pos.y = maxf(m_cameraConstraint.lo.y, pos.y);
  222. pos.y = minf(m_cameraConstraint.hi.y, pos.y);
  223. }
  224. // set position of camera itself
  225. sourcePos.X = m_cameraOffset.x*zoom;
  226. sourcePos.Y = m_cameraOffset.y*zoom;
  227. sourcePos.Z = m_cameraOffset.z*zoom;
  228. #ifdef NOT_IN_USE
  229. if (TheGlobalData->m_isOffsetCameraZ && TheTerrainLogic)
  230. {
  231. sourcePos.Z += TheTerrainLogic->getGroundHeight(pos.x, pos.y);
  232. if (m_prevSourcePosZ != SOURCEPOS_INVALID)
  233. {
  234. const Real MAX_SPZ_VARIATION = 0.05f;
  235. Real spzMin = m_prevSourcePosZ*(1.0-MAX_SPZ_VARIATION);
  236. Real spzMax Coord3D center;
  237. = m_prevSourcePosZ*(1.0+MAX_SPZ_VARIATION);
  238. if (sourcePos.Z < spzMin) sourcePos.Z = spzMin;
  239. if (sourcePos.Z > spzMax) sourcePos.Z = spzMax;
  240. }
  241. m_prevSourcePosZ = sourcePos.Z;
  242. }
  243. #endif
  244. // camera looking at origin
  245. targetPos.X = 0;
  246. targetPos.Y = 0;
  247. targetPos.Z = 0;
  248. Real factor = 1.0 - (groundLevel/sourcePos.Z );
  249. // construct a matrix to rotate around the up vector by the given angle
  250. Matrix3D angleTransform( Vector3( 0.0f, 0.0f, 1.0f ), angle );
  251. // construct a matrix to rotate around the horizontal vector by the given angle
  252. Matrix3D pitchTransform( Vector3( 1.0f, 0.0f, 0.0f ), pitch );
  253. // rotate camera position (pitch, then angle)
  254. #ifdef ALLOW_TEMPORARIES
  255. sourcePos = pitchTransform * sourcePos;
  256. sourcePos = angleTransform * sourcePos;
  257. #else
  258. pitchTransform.mulVector3(sourcePos);
  259. angleTransform.mulVector3(sourcePos);
  260. #endif
  261. sourcePos *= factor;
  262. // translate to current XY position
  263. sourcePos.X += pos.x;
  264. sourcePos.Y += pos.y;
  265. sourcePos.Z += groundLevel;
  266. targetPos.X += pos.x;
  267. targetPos.Y += pos.y;
  268. targetPos.Z += groundLevel;
  269. // do m_FXPitch adjustment.
  270. Real height = sourcePos.Z - targetPos.Z;
  271. height *= m_FXPitch;
  272. targetPos.Z = sourcePos.Z - height;
  273. // build new camera transform
  274. transform->Make_Identity();
  275. transform->Look_At( sourcePos, targetPos, 0 );
  276. }
  277. //-------------------------------------------------------------------------------------------------
  278. //-------------------------------------------------------------------------------------------------
  279. void W3DView::calcCameraConstraints()
  280. {
  281. // const Matrix3D& cameraTransform = m_3DCamera->Get_Transform();
  282. // DEBUG_LOG(("*** rebuilding cam constraints\n"));
  283. // ok, now check to ensure that we can't see outside the map region,
  284. // and twiddle the camera if needed
  285. if (TheTerrainLogic)
  286. {
  287. Region3D mapRegion;
  288. TheTerrainLogic->getExtent( &mapRegion );
  289. /*
  290. Note the following restrictions on camera constraints!
  291. -- they assume that all maps are height 'm_groundLevel' at the edges.
  292. (since you need to add some "buffer" around the edges of your map
  293. anyway, this shouldn't be an issue.)
  294. -- for angles/pitches other than zero, it may show boundaries.
  295. since we currently plan the game to be restricted to this,
  296. it shouldn't be an issue.
  297. */
  298. Real maxEdgeZ = m_groundLevel;
  299. // const Real BORDER_FUDGE = MAP_XY_FACTOR * 1.414f;
  300. Coord3D center, bottom;
  301. ICoord2D screen;
  302. //Pick at the center
  303. screen.x=0.5f*getWidth()+m_originX;
  304. screen.y=0.5f*getHeight()+m_originY;
  305. Vector3 rayStart,rayEnd;
  306. getPickRay(&screen,&rayStart,&rayEnd);
  307. center.x = Vector3::Find_X_At_Z(maxEdgeZ, rayStart, rayEnd);
  308. center.y = Vector3::Find_Y_At_Z(maxEdgeZ, rayStart, rayEnd);
  309. center.z = maxEdgeZ;
  310. screen.y = m_originY+ 0.95f*getHeight();
  311. getPickRay(&screen,&rayStart,&rayEnd);
  312. bottom.x = Vector3::Find_X_At_Z(maxEdgeZ, rayStart, rayEnd);
  313. bottom.y = Vector3::Find_Y_At_Z(maxEdgeZ, rayStart, rayEnd);
  314. bottom.z = maxEdgeZ;
  315. center.x -= bottom.x;
  316. center.y -= bottom.y;
  317. Real offset = center.length();
  318. if (TheGlobalData->m_debugAI) {
  319. offset = -1000; // push out the constraints so we can look at staging areas.
  320. }
  321. m_cameraConstraint.lo.x = mapRegion.lo.x + offset;
  322. m_cameraConstraint.hi.x = mapRegion.hi.x - offset;
  323. // this looks inverted, but is correct
  324. m_cameraConstraint.lo.y = mapRegion.lo.y + offset;
  325. m_cameraConstraint.hi.y = mapRegion.hi.y - offset;
  326. m_cameraConstraintValid = true;
  327. }
  328. }
  329. //-------------------------------------------------------------------------------------------------
  330. /** Returns a world-space ray originating at a given screen pixel position
  331. and ending at the far clip plane for current camera. Screen coordinates
  332. assumed in absolute values relative to full display resolution.*/
  333. //-------------------------------------------------------------------------------------------------
  334. void W3DView::getPickRay(const ICoord2D *screen, Vector3 *rayStart, Vector3 *rayEnd)
  335. {
  336. Real logX,logY;
  337. //W3D Screen coordinates are -1 to 1, so we need to do some conversion:
  338. PixelScreenToW3DLogicalScreen(screen->x - m_originX,screen->y - m_originY, &logX, &logY,getWidth(),getHeight());
  339. *rayStart = m_3DCamera->Get_Position(); //get camera location
  340. m_3DCamera->Un_Project(*rayEnd,Vector2(logX,logY)); //get world space point
  341. *rayEnd -= *rayStart; //vector camera to world space point
  342. rayEnd->Normalize(); //make unit vector
  343. *rayEnd *= m_3DCamera->Get_Depth(); //adjust length to reach far clip plane
  344. *rayEnd += *rayStart; //get point on far clip plane along ray from camera.
  345. }
  346. //-------------------------------------------------------------------------------------------------
  347. /** set the transform matrix of m_3DCamera, based on m_pos & m_angle */
  348. //-------------------------------------------------------------------------------------------------
  349. void W3DView::setCameraTransform( void )
  350. {
  351. m_cameraHasMovedSinceRequest = true;
  352. Matrix3D cameraTransform( 1 );
  353. Real nearZ, farZ;
  354. m_3DCamera->Get_Clip_Planes(nearZ, farZ);
  355. // Set the near to MAP_XY_FACTOR. Improves zbuffer resolution.
  356. nearZ = MAP_XY_FACTOR;
  357. farZ = 1200.0f;
  358. if ((TheGlobalData && TheGlobalData->m_drawEntireTerrain) || (m_FXPitch<0.95f || m_zoom>1.05))
  359. { //need to extend far clip plane so entire terrain can be visible
  360. farZ *= MAP_XY_FACTOR;
  361. }
  362. m_3DCamera->Set_Clip_Planes(nearZ, farZ);
  363. #if defined(_DEBUG) || defined(_INTERNAL)
  364. if (TheGlobalData->m_useCameraConstraints)
  365. #endif
  366. {
  367. if (!m_cameraConstraintValid)
  368. {
  369. buildCameraTransform(&cameraTransform);
  370. m_3DCamera->Set_Transform( cameraTransform );
  371. calcCameraConstraints();
  372. }
  373. DEBUG_ASSERTLOG(m_cameraConstraintValid,("*** cam constraints are not valid!!!\n"));
  374. if (m_cameraConstraintValid)
  375. {
  376. Coord3D pos = *getPosition();
  377. pos.x = maxf(m_cameraConstraint.lo.x, pos.x);
  378. pos.x = minf(m_cameraConstraint.hi.x, pos.x);
  379. pos.y = maxf(m_cameraConstraint.lo.y, pos.y);
  380. pos.y = minf(m_cameraConstraint.hi.y, pos.y);
  381. setPosition(&pos);
  382. }
  383. }
  384. #if defined(_DEBUG) || defined(_INTERNAL)
  385. m_3DCamera->Set_View_Plane( m_FOV, -1 );
  386. #endif
  387. // rebuild it (even if we just did it due to camera constraints)
  388. buildCameraTransform( &cameraTransform );
  389. m_3DCamera->Set_Transform( cameraTransform );
  390. if (TheTerrainRenderObject)
  391. {
  392. RefRenderObjListIterator *it = W3DDisplay::m_3DScene->createLightsIterator();
  393. TheTerrainRenderObject->updateCenter(m_3DCamera, it);
  394. if (it)
  395. {
  396. W3DDisplay::m_3DScene->destroyLightsIterator(it);
  397. it = NULL;
  398. }
  399. }
  400. }
  401. //-------------------------------------------------------------------------------------------------
  402. //-------------------------------------------------------------------------------------------------
  403. void W3DView::init( void )
  404. {
  405. // extend View functionality
  406. View::init();
  407. setName("W3DView");
  408. // set default camera "lookat" point
  409. Coord3D pos;
  410. pos.x = 87.0f;
  411. pos.y = 77.0f;
  412. pos.z = 0;
  413. pos.x *= MAP_XY_FACTOR;
  414. pos.y *= MAP_XY_FACTOR;
  415. setPosition(&pos);
  416. // create our 3D camera
  417. m_3DCamera = NEW_REF( CameraClass, () );
  418. setCameraTransform();
  419. // create our 2D camera for the GUI overlay
  420. m_2DCamera = NEW_REF( CameraClass, () );
  421. m_2DCamera->Set_Position( Vector3( 0, 0, 1 ) );
  422. Vector2 min = Vector2( -1, -0.75f );
  423. Vector2 max = Vector2( +1, +0.75f );
  424. m_2DCamera->Set_View_Plane( min, max );
  425. m_2DCamera->Set_Clip_Planes( 0.995f, 2.0f );
  426. m_cameraConstraintValid = false;
  427. m_scrollAmountCutoff = TheGlobalData->m_scrollAmountCutoff;
  428. } // end init
  429. //-------------------------------------------------------------------------------------------------
  430. const Coord3D& W3DView::get3DCameraPosition() const
  431. {
  432. Vector3 camera = m_3DCamera->Get_Position();
  433. static Coord3D pos;
  434. pos.set( camera.X, camera.Y, camera.Z );
  435. return pos;
  436. }
  437. //-------------------------------------------------------------------------------------------------
  438. //-------------------------------------------------------------------------------------------------
  439. void W3DView::reset( void )
  440. {
  441. View::reset();
  442. // Just in case...
  443. setTimeMultiplier(1); // Set time rate back to 1.
  444. Coord3D arbitraryPos = { 0, 0, 0 };
  445. // Just move the camera to 0, 0, 0. It'll get repositioned at the beginning of the next game
  446. // anyways.
  447. resetCamera(&arbitraryPos, 1);
  448. setViewFilter((enum FilterTypes)0);
  449. Coord2D gb = { 0,0 };
  450. setGuardBandBias( &gb );
  451. }
  452. //-------------------------------------------------------------------------------------------------
  453. /** draw worker for drawables in the view region */
  454. //-------------------------------------------------------------------------------------------------
  455. static void drawDrawable( Drawable *draw, void *userData )
  456. {
  457. draw->draw( (View *)userData );
  458. } // end drawDrawable
  459. // ------------------------------------------------------------------------------------------------
  460. //-------------------------------------------------------------------------------------------------
  461. static void drawTerrainNormal( Drawable *draw, void *userData )
  462. {
  463. UnsignedInt color = GameMakeColor( 255, 255, 0, 255 );
  464. if (TheTerrainLogic)
  465. {
  466. Coord3D pos = *draw->getPosition();
  467. Coord3D normal;
  468. pos.z = TheTerrainLogic->getGroundHeight(pos.x, pos.y, &normal);
  469. const Real NORMLEN = 20;
  470. normal.x = pos.x + normal.x * NORMLEN;
  471. normal.y = pos.y + normal.y * NORMLEN;
  472. normal.z = pos.z + normal.z * NORMLEN;
  473. ICoord2D start, end;
  474. TheTacticalView->worldToScreen(&pos, &start);
  475. TheTacticalView->worldToScreen(&normal, &end);
  476. TheDisplay->drawLine(start.x, start.y, end.x, end.y, 1.0f, color);
  477. }
  478. }
  479. #if defined(_DEBUG) || defined(_INTERNAL)
  480. void drawDrawableExtents( Drawable *draw, void *userData ); // FORWARD DECLARATION
  481. // ------------------------------------------------------------------------------------------------
  482. // ------------------------------------------------------------------------------------------------
  483. static void drawContainedDrawable( Object *obj, void *userData )
  484. {
  485. Drawable *draw = obj->getDrawable();
  486. if( draw )
  487. drawDrawableExtents( draw, userData );
  488. } // end drawContainedDrawable
  489. //-------------------------------------------------------------------------------------------------
  490. //-------------------------------------------------------------------------------------------------
  491. static void drawDrawableExtents( Drawable *draw, void *userData )
  492. {
  493. UnsignedInt color = GameMakeColor( 0, 255, 0, 255 );
  494. switch( draw->getDrawableGeometryInfo().getGeomType() )
  495. {
  496. //---------------------------------------------------------------------------------------------
  497. case GEOMETRY_BOX:
  498. {
  499. Real angle = draw->getOrientation();
  500. Real c = (Real)cos(angle);
  501. Real s = (Real)sin(angle);
  502. Real exc = draw->getDrawableGeometryInfo().getMajorRadius()*c;
  503. Real eyc = draw->getDrawableGeometryInfo().getMinorRadius()*c;
  504. Real exs = draw->getDrawableGeometryInfo().getMajorRadius()*s;
  505. Real eys = draw->getDrawableGeometryInfo().getMinorRadius()*s;
  506. Coord3D pts[4];
  507. pts[0].x = draw->getPosition()->x - exc - eys;
  508. pts[0].y = draw->getPosition()->y + eyc - exs;
  509. pts[0].z = 0;
  510. pts[1].x = draw->getPosition()->x + exc - eys;
  511. pts[1].y = draw->getPosition()->y + eyc + exs;
  512. pts[1].z = 0;
  513. pts[2].x = draw->getPosition()->x + exc + eys;
  514. pts[2].y = draw->getPosition()->y - eyc + exs;
  515. pts[2].z = 0;
  516. pts[3].x = draw->getPosition()->x - exc + eys;
  517. pts[3].y = draw->getPosition()->y - eyc - exs;
  518. pts[3].z = 0;
  519. Real z = draw->getPosition()->z;
  520. for( int i = 0; i < 2; i++ )
  521. {
  522. for (int corner = 0; corner < 4; corner++)
  523. {
  524. ICoord2D start, end;
  525. pts[corner].z = z;
  526. pts[(corner+1)&3].z = z;
  527. TheTacticalView->worldToScreen(&pts[corner], &start);
  528. TheTacticalView->worldToScreen(&pts[(corner+1)&3], &end);
  529. TheDisplay->drawLine(start.x, start.y, end.x, end.y, 1.0f, color);
  530. }
  531. z += draw->getDrawableGeometryInfo().getMaxHeightAbovePosition();
  532. } // end for i
  533. break;
  534. } // end case box
  535. //---------------------------------------------------------------------------------------------
  536. case GEOMETRY_SPHERE: // not quite right, but close enough
  537. case GEOMETRY_CYLINDER:
  538. {
  539. Real angle, inc = PI/4.0f;
  540. Real radius = draw->getDrawableGeometryInfo().getMajorRadius();
  541. Coord3D pnt, lastPnt;
  542. ICoord2D start, end;
  543. Real z = draw->getPosition()->z;
  544. // draw cylinder
  545. for( int i=0; i<2; i++ )
  546. {
  547. angle = 0.0f;
  548. lastPnt.x = draw->getPosition()->x + radius * (Real)cos(angle);
  549. lastPnt.y = draw->getPosition()->y + radius * (Real)sin(angle);
  550. lastPnt.z = z;
  551. TheTacticalView->worldToScreen( &lastPnt, &end );
  552. for( angle = inc; angle <= 2.0f * PI; angle += inc )
  553. {
  554. pnt.x = draw->getPosition()->x + radius * (Real)cos(angle);
  555. pnt.y = draw->getPosition()->y + radius * (Real)sin(angle);
  556. pnt.z = z;
  557. TheTacticalView->worldToScreen( &pnt, &start );
  558. TheDisplay->drawLine( start.x, start.y, end.x, end.y, 1.0f, color );
  559. lastPnt = pnt;
  560. end = start;
  561. }
  562. // next time 'round, draw the top of the cylinder
  563. z += draw->getDrawableGeometryInfo().getMaxHeightAbovePosition();
  564. } // end for i
  565. // draw centerline
  566. pnt.x = draw->getPosition()->x;
  567. pnt.y = draw->getPosition()->y;
  568. pnt.z = draw->getPosition()->z;
  569. TheTacticalView->worldToScreen( &pnt, &start );
  570. pnt.z = draw->getPosition()->z + draw->getDrawableGeometryInfo().getMaxHeightAbovePosition();
  571. TheTacticalView->worldToScreen( &pnt, &end );
  572. TheDisplay->drawLine( start.x, start.y, end.x, end.y, 1.0f, color );
  573. break;
  574. } // case CYLINDER
  575. } // end switch
  576. // draw any extents for things that are contained by this
  577. Object *obj = draw->getObject();
  578. if( obj )
  579. {
  580. ContainModuleInterface *contain = obj->getContain();
  581. if( contain )
  582. contain->iterateContained( drawContainedDrawable, userData, FALSE );
  583. } // end if
  584. } // end drawDrawableExtents
  585. #endif
  586. //-------------------------------------------------------------------------------------------------
  587. /** An opportunity to draw something after all drawables have been drawn once */
  588. //-------------------------------------------------------------------------------------------------
  589. static void drawablePostDraw( Drawable *draw, void *userData )
  590. {
  591. Real FXPitch = TheTacticalView->getFXPitch();
  592. if (draw->isDrawableEffectivelyHidden() || FXPitch < 0.0f)
  593. return;
  594. Object* obj = draw->getObject();
  595. Int localPlayerIndex = ThePlayerList ? ThePlayerList->getLocalPlayer()->getPlayerIndex() : 0;
  596. #if defined(_DEBUG) || defined(_INTERNAL)
  597. ObjectShroudStatus ss = (!obj || !TheGlobalData->m_shroudOn) ? OBJECTSHROUD_CLEAR : obj->getShroudedStatus(localPlayerIndex);
  598. #else
  599. ObjectShroudStatus ss = (!obj) ? OBJECTSHROUD_CLEAR : obj->getShroudedStatus(localPlayerIndex);
  600. #endif
  601. if (ss > OBJECTSHROUD_PARTIAL_CLEAR)
  602. return;
  603. // draw the any "icon" UI for a drawable (health bars, veterency, etc);
  604. //*****
  605. //@TODO: Create a way to reject this call easily -- like objects that have no compatible modules.
  606. //*****
  607. //if( draw->getStatusBits() )
  608. //{
  609. draw->drawIconUI();
  610. //}
  611. #if defined(_DEBUG) || defined(_INTERNAL)
  612. // debug collision extents
  613. if( TheGlobalData->m_showCollisionExtents )
  614. drawDrawableExtents( draw, userData );
  615. #endif
  616. // debug terrain normals at object positions
  617. if( TheGlobalData->m_showTerrainNormals )
  618. drawTerrainNormal( draw, userData );
  619. TheGameClient->incrementRenderedObjectCount();
  620. } // end drawablePostDraw
  621. //-------------------------------------------------------------------------------------------------
  622. // Display AI debug visuals
  623. //-------------------------------------------------------------------------------------------------
  624. static void renderAIDebug( void )
  625. {
  626. }
  627. // ------------------------------------------------------------------------------------------------
  628. // ------------------------------------------------------------------------------------------------
  629. Bool W3DView::updateCameraMovements()
  630. {
  631. Bool didUpdate = false;
  632. if (m_doingZoomCamera)
  633. {
  634. zoomCameraOneFrame();
  635. didUpdate = true;
  636. }
  637. if (m_doingPitchCamera)
  638. {
  639. pitchCameraOneFrame();
  640. didUpdate = true;
  641. }
  642. if (m_doingRotateCamera) {
  643. m_previousLookAtPosition = *getPosition();
  644. rotateCameraOneFrame();
  645. didUpdate = true;
  646. } else if (m_doingMoveCameraOnWaypointPath) {
  647. m_previousLookAtPosition = *getPosition();
  648. moveAlongWaypointPath(TheW3DFrameLengthInMsec);
  649. didUpdate = true;
  650. }
  651. if (m_doingScriptedCameraLock)
  652. {
  653. didUpdate = true;
  654. }
  655. return didUpdate;
  656. }
  657. /** This function performs all actions which affect the camera transform or 3D objects
  658. rendered in this frame.
  659. MW: I moved this code out out W3DView::draw() so that we can get final camera and object
  660. positions before any rendering begins. This was necessary so that reflection textures
  661. (which update before the main rendering loop) could get a correct version of the scene.
  662. Without this change, the reflections were always 1 frame behind the non-reflected view.
  663. */
  664. void W3DView::updateView(void)
  665. {
  666. UPDATE();
  667. }
  668. //DECLARE_PERF_TIMER(W3DView_updateView)
  669. void W3DView::update(void)
  670. {
  671. //USE_PERF_TIMER(W3DView_updateView)
  672. Bool recalcCamera = false;
  673. Bool didScriptedMovement = false;
  674. #ifdef LOG_FRAME_TIMES
  675. __int64 curTime64,freq64;
  676. static __int64 prevTime64=0;
  677. QueryPerformanceFrequency((LARGE_INTEGER *)&freq64);
  678. QueryPerformanceCounter((LARGE_INTEGER *)&curTime64);
  679. freq64 /= 1000;
  680. Int elapsedTimeMs = (curTime64 - prevTime64)/freq64;
  681. prevTime64 = curTime64;
  682. #endif
  683. // Int elapsedTimeMs = TheW3DFrameLengthInMsec; // Assume a constant time flow. It just works out better. jba.
  684. if (TheTerrainRenderObject->doesNeedFullUpdate()) {
  685. RefRenderObjListIterator *it = W3DDisplay::m_3DScene->createLightsIterator();
  686. TheTerrainRenderObject->updateCenter(m_3DCamera, it);
  687. if (it)
  688. {
  689. W3DDisplay::m_3DScene->destroyLightsIterator(it);
  690. it = NULL;
  691. }
  692. }
  693. static Real followFactor = -1;
  694. ObjectID cameraLock = getCameraLock();
  695. if (cameraLock == INVALID_ID)
  696. {
  697. followFactor = -1;
  698. }
  699. if (cameraLock != INVALID_ID)
  700. {
  701. m_doingMoveCameraOnWaypointPath = false;
  702. Object* cameraLockObj = TheGameLogic->findObjectByID(cameraLock);
  703. Bool loseLock = false;
  704. // check if object has been destroyed or is dead -> lose lock
  705. if (cameraLockObj == NULL)
  706. {
  707. loseLock = true;
  708. }
  709. #if 0
  710. else
  711. {
  712. AIUpdateInterface *ai = cameraLockObj->getAIUpdateInterface();
  713. if (ai && ai->isDead())
  714. loseLock = true;
  715. }
  716. #endif
  717. if (loseLock)
  718. {
  719. setCameraLock(INVALID_ID);
  720. setCameraLockDrawable(NULL);
  721. followFactor = -1;
  722. }
  723. else
  724. {
  725. if (followFactor<0) {
  726. followFactor = 0.05f;
  727. } else {
  728. followFactor += 0.05f;
  729. if (followFactor>1.0f) followFactor = 1.0f;
  730. }
  731. if (getCameraLockDrawable() != NULL)
  732. {
  733. Drawable* cameraLockDrawable = (Drawable *)getCameraLockDrawable();
  734. if (!cameraLockDrawable)
  735. {
  736. setCameraLockDrawable(NULL);
  737. }
  738. else
  739. {
  740. Coord3D pos;
  741. Real boundingSphereRadius;
  742. Matrix3D transform;
  743. // this method must ONLY be called from the client, NEVER From the logic, not even indirectly.
  744. if (cameraLockDrawable->clientOnly_getFirstRenderObjInfo(&pos, &boundingSphereRadius, &transform))
  745. {
  746. Vector3 zaxis(0,0,1);
  747. Vector3 objPos;
  748. objPos.X = pos.x;
  749. objPos.Y = pos.y;
  750. objPos.Z = pos.z;
  751. //get position of top of object, assuming world z roughly along local z.
  752. objPos += boundingSphereRadius * 1.0f * zaxis;
  753. Vector3 objview = transform.Get_X_Vector(); //get view vector of object
  754. //move camera back behind object far enough not to intersect bounding sphere
  755. Vector3 camtran = objPos - objview * boundingSphereRadius*4.5f;
  756. Vector3 prevCamTran = m_3DCamera->Get_Position(); //get current camera position.
  757. Vector3 tranDiff = (camtran - prevCamTran); //vector old position to new position.
  758. camtran = prevCamTran + tranDiff * 0.1f; //slowly move camera to new position.
  759. Matrix3D camXForm;
  760. camXForm.Look_At(camtran,objPos,0);
  761. m_3DCamera->Set_Transform(camXForm);
  762. recalcCamera = false; //we already did it
  763. }
  764. }
  765. }
  766. else
  767. { Coord3D objpos = *cameraLockObj->getPosition();
  768. Coord3D curpos = *getPosition();
  769. // don't "snap" directly to the pos, but move there smoothly.
  770. Real snapThreshSqr = sqr(TheGlobalData->m_partitionCellSize);
  771. Real curDistSqr = sqr(curpos.x - objpos.x) + sqr(curpos.y - objpos.y);
  772. if ( m_snapImmediate)
  773. {
  774. // close enough.
  775. curpos.x = objpos.x;
  776. curpos.y = objpos.y;
  777. }
  778. else
  779. {
  780. // Real ratio = 1.0f - snapThreshSqr/curDistSqr;
  781. Real dx = objpos.x-curpos.x;
  782. Real dy = objpos.y-curpos.y;
  783. if (m_lockType == LOCK_TETHER)
  784. {
  785. //snapThreshSqr = sqr( m_lockDist * TheGlobalData->m_partitionCellSize );
  786. if (curDistSqr >= snapThreshSqr)
  787. {
  788. Real ratio = 1.0f - snapThreshSqr/curDistSqr;
  789. // move halfway there.
  790. curpos.x += dx*ratio*0.5f;
  791. curpos.y += dy*ratio*0.5f;
  792. }
  793. else
  794. {
  795. // we're inside our 'play' tolerance. Move slowly to the obj
  796. Real ratio = 0.01f * m_lockDist;
  797. Real dx = objpos.x-curpos.x;
  798. Real dy = objpos.y-curpos.y;
  799. curpos.x += dx*ratio;
  800. curpos.y += dy*ratio;
  801. }
  802. }
  803. else
  804. {
  805. curpos.x += dx*followFactor;
  806. curpos.y += dy*followFactor;
  807. }
  808. }
  809. if (!(TheScriptEngine->isTimeFrozenDebug() || TheScriptEngine->isTimeFrozenScript()) && !TheGameLogic->isGamePaused()) {
  810. m_previousLookAtPosition = *getPosition();
  811. }
  812. setPosition(&curpos);
  813. if (m_lockType == LOCK_FOLLOW)
  814. {
  815. // camera follow objects if they are flying
  816. if (cameraLockObj->isUsingAirborneLocomotor() && cameraLockObj->isAboveTerrainOrWater())
  817. {
  818. Matrix3D camXForm;
  819. Real oldZRot = m_angle;
  820. Real idealZRot = cameraLockObj->getOrientation() - M_PI_2;
  821. normAngle(oldZRot);
  822. normAngle(idealZRot);
  823. Real diff = idealZRot - oldZRot;
  824. normAngle(diff);
  825. if (m_snapImmediate)
  826. {
  827. m_angle = idealZRot;
  828. }
  829. else
  830. {
  831. m_angle += diff * 0.1f;
  832. }
  833. normAngle(m_angle);
  834. }
  835. }
  836. if (m_snapImmediate)
  837. m_snapImmediate = FALSE;
  838. m_groundLevel = objpos.z;
  839. didScriptedMovement = true;
  840. recalcCamera = true;
  841. }
  842. }
  843. }
  844. if (!(TheScriptEngine->isTimeFrozenDebug()/* || TheScriptEngine->isTimeFrozenScript()*/) && !TheGameLogic->isGamePaused()) {
  845. // If we aren't frozen for debug, allow the camera to follow scripted movements.
  846. if (updateCameraMovements()) {
  847. didScriptedMovement = true;
  848. recalcCamera = true;
  849. }
  850. } else {
  851. if (m_doingRotateCamera || m_doingMoveCameraOnWaypointPath || m_doingPitchCamera || m_doingZoomCamera || m_doingScriptedCameraLock) {
  852. didScriptedMovement = true; // don't mess up the scripted movement
  853. }
  854. }
  855. //
  856. // Process camera shake
  857. /// @todo Make this framerate-independent
  858. //
  859. if (m_shakeIntensity > 0.01f)
  860. {
  861. m_shakeOffset.x = m_shakeIntensity * m_shakeAngleCos;
  862. m_shakeOffset.y = m_shakeIntensity * m_shakeAngleSin;
  863. // fake a stiff spring/damper
  864. const Real dampingCoeff = 0.75f;
  865. m_shakeIntensity *= dampingCoeff;
  866. // spring is so "stiff", it pulls 180 degrees opposite each frame
  867. m_shakeAngleCos = -m_shakeAngleCos;
  868. m_shakeAngleSin = -m_shakeAngleSin;
  869. recalcCamera = true;
  870. }
  871. else
  872. {
  873. m_shakeIntensity = 0.0f;
  874. m_shakeOffset.x = 0.0f;
  875. m_shakeOffset.y = 0.0f;
  876. }
  877. /*
  878. * In order to have the camera follow the terrain in a non-dizzying way, we will have a
  879. * "desired height" value that the user sets. While scrolling, the actual height (set by
  880. * zoom) will not get updated unless we are scrolling uphill and our view either goes
  881. * underground or higher than the max allowed height. When the camera is at rest (not
  882. * scrolling), the zoom will move toward matching the desired height.
  883. */
  884. m_terrainHeightUnderCamera = getHeightAroundPos(m_pos.x, m_pos.y);
  885. m_currentHeightAboveGround = m_cameraOffset.z * m_zoom - m_terrainHeightUnderCamera;
  886. if (TheTerrainLogic && TheGlobalData && TheInGameUI && m_okToAdjustHeight && !TheGameLogic->isGamePaused())
  887. {
  888. Real desiredHeight = (m_terrainHeightUnderCamera + m_heightAboveGround);
  889. Real desiredZoom = desiredHeight / m_cameraOffset.z;
  890. #if !defined(_PLAYTEST)
  891. if (didScriptedMovement || (TheGameLogic->isInReplayGame() && TheGlobalData->m_useCameraInReplay))
  892. #else
  893. if (didScriptedMovement)
  894. #endif
  895. {
  896. // if we are in a scripted camera movement, take its height above ground as our desired height.
  897. m_heightAboveGround = m_currentHeightAboveGround;
  898. //DEBUG_LOG(("Frame %d: height above ground: %g %g %g %g\n", TheGameLogic->getFrame(), m_heightAboveGround,
  899. // m_cameraOffset.z, m_zoom, m_terrainHeightUnderCamera));
  900. }
  901. if (TheInGameUI->isScrolling())
  902. {
  903. // if scrolling, only adjust if we're too close or too far
  904. if (m_scrollAmount.length() < m_scrollAmountCutoff || (m_currentHeightAboveGround < m_minHeightAboveGround) || (TheGlobalData->m_enforceMaxCameraHeight && m_currentHeightAboveGround > m_maxHeightAboveGround))
  905. {
  906. Real zoomAdj = (desiredZoom - m_zoom)*TheGlobalData->m_cameraAdjustSpeed;
  907. if (fabs(zoomAdj) >= 0.0001) // only do positive
  908. {
  909. m_zoom += zoomAdj;
  910. recalcCamera = true;
  911. }
  912. }
  913. }
  914. else
  915. {
  916. // we're not scrolling; settle toward desired height above ground
  917. Real zoomAdj = (m_zoom - desiredZoom)*TheGlobalData->m_cameraAdjustSpeed;
  918. Real zoomAdjAbs = fabs(zoomAdj);
  919. if (zoomAdjAbs >= 0.0001 && !didScriptedMovement)
  920. {
  921. //DEBUG_LOG(("W3DView::update() - m_zoom=%g, desiredHeight=%g\n", m_zoom, desiredZoom));
  922. m_zoom -= zoomAdj;
  923. recalcCamera = true;
  924. }
  925. }
  926. }
  927. if (TheScriptEngine->isTimeFast()) {
  928. return; // don't draw - makes it faster :) jba.
  929. }
  930. if (recalcCamera)
  931. setCameraTransform();
  932. Region3D axisAlignedRegion;
  933. getAxisAlignedViewRegion(axisAlignedRegion);
  934. // render all of the visible Drawables
  935. /// @todo this needs to use a real region partition or something
  936. if (WW3D::Get_Frame_Time()) //make sure some time actually elapsed
  937. TheGameClient->iterateDrawablesInRegion( &axisAlignedRegion, drawDrawable, this );
  938. }
  939. //-------------------------------------------------------------------------------------------------
  940. /** Find region which contains all drawables in 3D space. */
  941. //-------------------------------------------------------------------------------------------------
  942. void W3DView::getAxisAlignedViewRegion(Region3D &axisAlignedRegion)
  943. {
  944. //
  945. // get the 4 points in 3D space of the 4 corners of the view, we will use a z = 0.0f
  946. // value so that we can get everything ... even stuff below the terrain
  947. //
  948. Coord3D box[ 4 ];
  949. getScreenCornerWorldPointsAtZ( &box[ 0 ], &box[ 1 ], &box[ 2 ], &box[ 3 ], 0.0f );
  950. //
  951. // take those 4 corners projected into the world and create an axis aligned bounding
  952. // box, we will use this box to iterate the drawables in 3D space
  953. //
  954. axisAlignedRegion.lo = box[ 0 ];
  955. axisAlignedRegion.hi = box[ 0 ];
  956. for( Int i = 0; i < 4; i++ )
  957. {
  958. if( box[ i ].x < axisAlignedRegion.lo.x )
  959. axisAlignedRegion.lo.x = box[ i ].x;
  960. if( box[ i ].y < axisAlignedRegion.lo.y )
  961. axisAlignedRegion.lo.y = box[ i ].y;
  962. if( box[ i ].x > axisAlignedRegion.hi.x )
  963. axisAlignedRegion.hi.x = box[ i ].x;
  964. if( box[ i ].y > axisAlignedRegion.hi.y )
  965. axisAlignedRegion.hi.y = box[ i ].y;
  966. } // end for i
  967. // low and high regions will be based of the extent of the map
  968. Region3D mapExtent;
  969. Real safeValue = 999999;
  970. TheTerrainLogic->getExtent( &mapExtent );
  971. axisAlignedRegion.lo.z = mapExtent.lo.z - safeValue;
  972. axisAlignedRegion.hi.z = mapExtent.hi.z + safeValue;
  973. // we want to overscan a little bit so that we get objects that are partially offscreen
  974. axisAlignedRegion.lo.x -= (DRAWABLE_OVERSCAN + m_guardBandBias.x);
  975. axisAlignedRegion.lo.y -= (DRAWABLE_OVERSCAN + m_guardBandBias.y + 60.0f );
  976. axisAlignedRegion.hi.x += (DRAWABLE_OVERSCAN + m_guardBandBias.x);
  977. axisAlignedRegion.hi.y += (DRAWABLE_OVERSCAN + m_guardBandBias.y);
  978. }
  979. // ------------------------------------------------------------------------------------------------
  980. // ------------------------------------------------------------------------------------------------
  981. void W3DView::setFadeParameters(Int fadeFrames, Int direction)
  982. {
  983. ScreenBWFilter::setFadeParameters(fadeFrames, direction);
  984. ScreenCrossFadeFilter::setFadeParameters(fadeFrames,direction);
  985. }
  986. void W3DView::set3DWireFrameMode(Bool enable)
  987. {
  988. m_nextWireFrameEnabled = enable;
  989. }
  990. //-------------------------------------------------------------------------------------------------
  991. /** Sets the view filter mode. */
  992. //-------------------------------------------------------------------------------------------------
  993. void W3DView::setViewFilterPos(const Coord3D *pos)
  994. {
  995. ScreenMotionBlurFilter::setZoomToPos(pos);
  996. }
  997. //-------------------------------------------------------------------------------------------------
  998. /** Sets the view filter mode. */
  999. //-------------------------------------------------------------------------------------------------
  1000. Bool W3DView::setViewFilterMode(enum FilterModes filterMode)
  1001. {
  1002. FilterModes oldMode = m_viewFilterMode; //save previous mode in case setup fails.
  1003. m_viewFilterMode = filterMode;
  1004. if (m_viewFilterMode != FM_NULL_MODE &&
  1005. m_viewFilter != FT_NULL_FILTER) {
  1006. if (!W3DShaderManager::filterSetup(m_viewFilter, m_viewFilterMode))
  1007. { //setup failed so restore previous mode.
  1008. m_viewFilterMode = oldMode;
  1009. return FALSE;
  1010. }
  1011. }
  1012. return TRUE;
  1013. }
  1014. //-------------------------------------------------------------------------------------------------
  1015. /** Sets the view filter. */
  1016. //-------------------------------------------------------------------------------------------------
  1017. Bool W3DView::setViewFilter(enum FilterTypes filter)
  1018. {
  1019. FilterTypes oldFilter = m_viewFilter; //save previous filter in case setup fails.
  1020. m_viewFilter = filter;
  1021. if (m_viewFilterMode != FM_NULL_MODE &&
  1022. m_viewFilter != FT_NULL_FILTER) {
  1023. if (!W3DShaderManager::filterSetup(m_viewFilter, m_viewFilterMode))
  1024. { //setup failed so restore previous mode.
  1025. m_viewFilter = oldFilter;
  1026. return FALSE;
  1027. };
  1028. }
  1029. return TRUE;
  1030. }
  1031. //-------------------------------------------------------------------------------------------------
  1032. /** Calculates how many pixels we scrolled since last frame for motion blur calculations. */
  1033. //-------------------------------------------------------------------------------------------------
  1034. void W3DView::calcDeltaScroll(Coord2D &screenDelta)
  1035. {
  1036. screenDelta.x = 0;
  1037. screenDelta.y = 0;
  1038. Vector3 prevPos(m_previousLookAtPosition.x,m_previousLookAtPosition.y, m_groundLevel);
  1039. Vector3 prevScreen;
  1040. if (m_3DCamera->Project( prevScreen, prevPos ) != CameraClass::INSIDE_FRUSTUM)
  1041. {
  1042. return;
  1043. }
  1044. Vector3 pos(m_pos.x,m_pos.y, m_groundLevel);
  1045. Vector3 screen;
  1046. if (m_3DCamera->Project( screen, pos ) != CameraClass::INSIDE_FRUSTUM)
  1047. {
  1048. return;
  1049. }
  1050. screenDelta.x = screen.X-prevScreen.X;
  1051. screenDelta.y = screen.Y-prevScreen.Y;
  1052. }
  1053. //-------------------------------------------------------------------------------------------------
  1054. /** Draw member for the W3D window, this will literally draw the window
  1055. * for this view */
  1056. //-------------------------------------------------------------------------------------------------
  1057. void W3DView::drawView( void )
  1058. {
  1059. DRAW();
  1060. }
  1061. //DECLARE_PERF_TIMER(W3DView_drawView)
  1062. void W3DView::draw( void )
  1063. {
  1064. //USE_PERF_TIMER(W3DView_drawView)
  1065. Bool skipRender = false;
  1066. Bool doExtraRender = false;
  1067. CustomScenePassModes customScenePassMode = SCENE_PASS_DEFAULT;
  1068. if (m_viewFilterMode &&
  1069. m_viewFilter > FT_NULL_FILTER &&
  1070. m_viewFilter < FT_MAX)
  1071. {
  1072. // Most likely will redirect rendering to a texture.
  1073. W3DShaderManager::filterPreRender(m_viewFilter, skipRender, customScenePassMode);
  1074. if (!skipRender && getCameraLock())
  1075. {
  1076. Object* cameraLockObj = TheGameLogic->findObjectByID(getCameraLock());
  1077. if (cameraLockObj)
  1078. {
  1079. Drawable *drawable = cameraLockObj->getDrawable();
  1080. drawable->setDrawableHidden(true);
  1081. }
  1082. }
  1083. }
  1084. if (!skipRender)
  1085. {
  1086. // Render 3D scene from our camera
  1087. W3DDisplay::m_3DScene->setCustomPassMode(customScenePassMode);
  1088. if (m_isWireFrameEnabled)
  1089. W3DDisplay::m_3DScene->Set_Extra_Pass_Polygon_Mode(SceneClass::EXTRA_PASS_CLEAR_LINE);
  1090. W3DDisplay::m_3DScene->doRender( m_3DCamera );
  1091. W3DDisplay::m_3DScene->Set_Extra_Pass_Polygon_Mode(SceneClass::EXTRA_PASS_DISABLE);
  1092. m_isWireFrameEnabled = m_nextWireFrameEnabled;
  1093. }
  1094. if (m_viewFilterMode &&
  1095. m_viewFilter > FT_NULL_FILTER &&
  1096. m_viewFilter < FT_MAX)
  1097. {
  1098. Coord2D deltaScroll;
  1099. calcDeltaScroll(deltaScroll);
  1100. Bool continueTheEffect;
  1101. continueTheEffect = W3DShaderManager::filterPostRender(m_viewFilter, m_viewFilterMode, deltaScroll,doExtraRender);
  1102. if (!skipRender && getCameraLock())
  1103. {
  1104. Object* cameraLockObj = TheGameLogic->findObjectByID(getCameraLock());
  1105. if (cameraLockObj)
  1106. {
  1107. Drawable *drawable = cameraLockObj->getDrawable();
  1108. drawable->setDrawableHidden(false);
  1109. RenderInfoClass rinfo(*m_3DCamera);
  1110. // Apply the camera and viewport (including depth range)
  1111. m_3DCamera->Apply();
  1112. TheDX8MeshRenderer.Set_Camera(&rinfo.Camera);
  1113. W3DDisplay::m_3DScene->renderSpecificDrawables(rinfo, 1, &drawable);
  1114. WW3D::Flush(rinfo);
  1115. }
  1116. }
  1117. if (!continueTheEffect)
  1118. {
  1119. // shut it down.
  1120. m_viewFilter = FT_NULL_FILTER;
  1121. m_viewFilterMode = FM_NULL_MODE;
  1122. }
  1123. }
  1124. //Some effects require that we render a modified version of the scene into a texture but also require
  1125. //an unaltered version in the framebuffer. So we re-render again into framebuffer after texture rendering
  1126. //was turned off by filterPostRender().
  1127. if (doExtraRender)
  1128. {
  1129. //Reset to normal scene rendering.
  1130. //The pass that rendered into a texture may have left the z-buffer in a weird state
  1131. //so clear it before rendering normal scene.
  1132. ///@todo: Don't clear z-buffer unless shader uses z-bias or anything else that would cause <= z to fail on normal render.
  1133. DX8Wrapper::Clear(false, true, Vector3(0.0f,0.0f,0.0f), TheWaterTransparency->m_minWaterOpacity); // Clear z but not color
  1134. W3DDisplay::m_3DScene->setCustomPassMode(SCENE_PASS_DEFAULT);
  1135. W3DDisplay::m_3DScene->doRender( m_3DCamera );
  1136. Coord2D deltaScroll;
  1137. W3DShaderManager::filterPostRender(m_viewFilter, m_viewFilterMode, deltaScroll, doExtraRender);
  1138. }
  1139. if( TheGlobalData->m_debugAI )
  1140. {
  1141. if (TheAI->pathfinder()->getDebugPath())
  1142. {
  1143. // setup screen clipping region
  1144. IRegion2D clipRegion;
  1145. clipRegion.lo.x = 0;
  1146. clipRegion.lo.y = 0;
  1147. clipRegion.hi.x = getWidth();
  1148. clipRegion.hi.y = getHeight();
  1149. UnsignedInt color = 0xFFFFFF00; //0xAARRGGBB
  1150. ICoord2D start, end;
  1151. PathNode *prevNode = TheAI->pathfinder()->getDebugPath()->getFirstNode();
  1152. if (worldToScreen( prevNode->getPosition(), &start )) {
  1153. TheDisplay->drawLine( start.x-3, start.y-3, start.x+3, start.y-3, 1.0f, color );
  1154. TheDisplay->drawLine( start.x+3, start.y-3, start.x+3, start.y+3, 1.0f, color );
  1155. TheDisplay->drawLine( start.x+3, start.y+3, start.x-3, start.y+3, 1.0f, color );
  1156. TheDisplay->drawLine( start.x-3, start.y+3, start.x-3, start.y-3, 1.0f, color );
  1157. }
  1158. for( PathNode *node = prevNode->getNext(); node; node = node->getNext() )
  1159. {
  1160. Int k;
  1161. Coord3D s, e;
  1162. Coord3D delta;
  1163. s = *node->getPosition();
  1164. e = *prevNode->getPosition();
  1165. delta.x = e.x-s.x;
  1166. delta.y = e.y-s.y;
  1167. delta.z = e.z-s.z;
  1168. for (k = 0; k<10; k++) {
  1169. Real factor1 = (k)/10.0;
  1170. Real factor2 = (k+1)/10.0;
  1171. s = *node->getPosition();
  1172. e = *node->getPosition();
  1173. s.x += delta.x*factor1;
  1174. s.y += delta.y*factor1;
  1175. s.z += delta.z*factor1;
  1176. e.x += delta.x*factor2;
  1177. e.y += delta.y*factor2;
  1178. e.z += delta.z*factor2;
  1179. Bool onScreen1 = worldToScreen( &e, &end );
  1180. Bool onScreen2 = worldToScreen( &s, &start );
  1181. if (!onScreen1 && !onScreen2) {
  1182. continue; // neither point visible.
  1183. }
  1184. ICoord2D clipStart, clipEnd;
  1185. if( ClipLine2D( &start, &end, &clipStart, &clipEnd, &clipRegion ) ) {
  1186. TheDisplay->drawLine( clipStart.x, clipStart.y, clipEnd.x, clipEnd.y, 1.0f, color );
  1187. }
  1188. }
  1189. prevNode = node;
  1190. if (node->getNext()) {
  1191. if (worldToScreen( node->getPosition(), &start )) {
  1192. TheDisplay->drawLine( start.x-4, start.y, start.x+3, start.y, 1.0f, color );
  1193. }
  1194. }
  1195. }
  1196. if (prevNode && worldToScreen( prevNode->getPosition(), &start )) {
  1197. TheDisplay->drawLine( start.x-4, start.y, start.x+3, start.y, 1.0f, color );
  1198. TheDisplay->drawLine( start.x, start.y-4, start.x, start.y+3, 1.0f, color );
  1199. }
  1200. color = 0xFFFF0000; //0xAARRGGBB
  1201. if (worldToScreen( TheAI->pathfinder()->getDebugPathPosition(), &start )) {
  1202. TheDisplay->drawLine( start.x-3, start.y, start.x+3, start.y, 1.0f, color );
  1203. TheDisplay->drawLine( start.x, start.y-3, start.x, start.y+3, 1.0f, color );
  1204. }
  1205. }
  1206. } // end if, show debug AI
  1207. #if defined(_DEBUG) || defined(_INTERNAL)
  1208. if( TheGlobalData->m_debugCamera )
  1209. {
  1210. UnsignedInt c = 0xaaffffff;
  1211. Coord3D worldPos = *getPosition();
  1212. worldPos.z = TheTerrainLogic->getGroundHeight(worldPos.x, worldPos.y);
  1213. Coord3D p1, p2;
  1214. ICoord2D s1, s2;
  1215. p1 = worldPos;
  1216. p1.x += TERRAIN_SAMPLE_SIZE;
  1217. p1.y += TERRAIN_SAMPLE_SIZE;
  1218. p1.z = TheTerrainLogic->getGroundHeight(p1.x, p1.y);
  1219. p2 = worldPos;
  1220. p2.x += TERRAIN_SAMPLE_SIZE;
  1221. p2.y -= TERRAIN_SAMPLE_SIZE;
  1222. p2.z = TheTerrainLogic->getGroundHeight(p2.x, p2.y);
  1223. worldToScreen( &p1, &s1 );
  1224. worldToScreen( &p2, &s2 );
  1225. TheDisplay->drawLine(s1.x, s1.y, s2.x, s2.y, 1.0f, c);
  1226. p1 = worldPos;
  1227. p1.x += TERRAIN_SAMPLE_SIZE;
  1228. p1.y -= TERRAIN_SAMPLE_SIZE;
  1229. p1.z = TheTerrainLogic->getGroundHeight(p1.x, p1.y);
  1230. p2 = worldPos;
  1231. p2.x -= TERRAIN_SAMPLE_SIZE;
  1232. p2.y -= TERRAIN_SAMPLE_SIZE;
  1233. p2.z = TheTerrainLogic->getGroundHeight(p2.x, p2.y);
  1234. worldToScreen( &p1, &s1 );
  1235. worldToScreen( &p2, &s2 );
  1236. TheDisplay->drawLine(s1.x, s1.y, s2.x, s2.y, 1.0f, c);
  1237. p1 = worldPos;
  1238. p1.x -= TERRAIN_SAMPLE_SIZE;
  1239. p1.y -= TERRAIN_SAMPLE_SIZE;
  1240. p1.z = TheTerrainLogic->getGroundHeight(p1.x, p1.y);
  1241. p2 = worldPos;
  1242. p2.x -= TERRAIN_SAMPLE_SIZE;
  1243. p2.y += TERRAIN_SAMPLE_SIZE;
  1244. p2.z = TheTerrainLogic->getGroundHeight(p2.x, p2.y);
  1245. worldToScreen( &p1, &s1 );
  1246. worldToScreen( &p2, &s2 );
  1247. TheDisplay->drawLine(s1.x, s1.y, s2.x, s2.y, 1.0f, c);
  1248. p1 = worldPos;
  1249. p1.x -= TERRAIN_SAMPLE_SIZE;
  1250. p1.y += TERRAIN_SAMPLE_SIZE;
  1251. p1.z = TheTerrainLogic->getGroundHeight(p1.x, p1.y);
  1252. p2 = worldPos;
  1253. p2.x += TERRAIN_SAMPLE_SIZE;
  1254. p2.y += TERRAIN_SAMPLE_SIZE;
  1255. p2.z = TheTerrainLogic->getGroundHeight(p2.x, p2.y);
  1256. worldToScreen( &p1, &s1 );
  1257. worldToScreen( &p2, &s2 );
  1258. TheDisplay->drawLine(s1.x, s1.y, s2.x, s2.y, 1.0f, c);
  1259. }
  1260. #endif
  1261. Region3D axisAlignedRegion;
  1262. getAxisAlignedViewRegion(axisAlignedRegion);
  1263. //
  1264. // there are several things we might want to do as a post pass on the objects after
  1265. // they are all drawn
  1266. /// @todo we might want to consider wiping this iterate out if there is nothing to post draw
  1267. //
  1268. TheGameClient->resetRenderedObjectCount();
  1269. TheGameClient->iterateDrawablesInRegion( &axisAlignedRegion, drawablePostDraw, this );
  1270. TheGameClient->flushTextBearingDrawables();
  1271. // Render 2D scene
  1272. W3DDisplay::m_2DScene->doRender( m_2DCamera );
  1273. }
  1274. // ------------------------------------------------------------------------------------------------
  1275. // ------------------------------------------------------------------------------------------------
  1276. void W3DView::setCameraLock(ObjectID id)
  1277. {
  1278. // If we're disabling camera movements, don't lock onto the object.
  1279. if (TheGlobalData->m_disableCameraMovement && id!=INVALID_ID) {
  1280. return;
  1281. }
  1282. View::setCameraLock(id);
  1283. m_doingScriptedCameraLock = FALSE;
  1284. }
  1285. // ------------------------------------------------------------------------------------------------
  1286. // ------------------------------------------------------------------------------------------------
  1287. void W3DView::setSnapMode( CameraLockType lockType, Real lockDist )
  1288. {
  1289. View::setSnapMode(lockType, lockDist);
  1290. m_doingScriptedCameraLock = TRUE;
  1291. }
  1292. //-------------------------------------------------------------------------------------------------
  1293. /** Scroll the view by the given delta in SCREEN COORDINATES, this interface
  1294. * assumes we will be scrolling along the X,Y plane */
  1295. //-------------------------------------------------------------------------------------------------
  1296. void W3DView::scrollBy( Coord2D *delta )
  1297. {
  1298. // if we haven't moved, ignore
  1299. if( delta && (delta->x != 0 || delta->y != 0) )
  1300. {
  1301. const Real SCROLL_RESOLUTION = 250.0f;
  1302. Vector3 world, worldStart, worldEnd;
  1303. Vector2 screen, start, end;
  1304. m_scrollAmount = *delta;
  1305. screen.X = delta->x;
  1306. screen.Y = delta->y;
  1307. start.X = getWidth();
  1308. start.Y = getHeight();
  1309. Real aspect = getWidth()/getHeight();
  1310. end.X = start.X + delta->x * SCROLL_RESOLUTION;
  1311. end.Y = start.Y + delta->y * SCROLL_RESOLUTION*aspect;
  1312. m_3DCamera->Device_To_World_Space( start, &worldStart );
  1313. m_3DCamera->Device_To_World_Space( end, &worldEnd );
  1314. world.X = worldEnd.X - worldStart.X;
  1315. world.Y = worldEnd.Y - worldStart.Y;
  1316. world.Z = worldEnd.Z - worldStart.Z;
  1317. // scroll by delta
  1318. Coord3D pos = *getPosition();
  1319. pos.x += world.X;
  1320. pos.y += world.Y;
  1321. //DEBUG_LOG(("Delta %.2f, %.2f\n", world.X, world.Z));
  1322. // no change to z
  1323. setPosition(&pos);
  1324. //m_cameraConstraintValid = false; // pos change does NOT invalidate cam constraints
  1325. m_doingRotateCamera = false;
  1326. // set new camera position
  1327. setCameraTransform();
  1328. } // end if
  1329. } // end scrollBy
  1330. //-------------------------------------------------------------------------------------------------
  1331. //-------------------------------------------------------------------------------------------------
  1332. void W3DView::forceRedraw()
  1333. {
  1334. // set the camera
  1335. setCameraTransform();
  1336. }
  1337. //-------------------------------------------------------------------------------------------------
  1338. /** Rotate the view around the up axis to the given angle. */
  1339. //-------------------------------------------------------------------------------------------------
  1340. void W3DView::setAngle( Real angle )
  1341. {
  1342. // Normalize to +-PI.
  1343. normAngle(angle);
  1344. // call our base class, we are adding functionality
  1345. View::setAngle( angle );
  1346. m_doingMoveCameraOnWaypointPath = false;
  1347. m_doingRotateCamera = false;
  1348. m_doingPitchCamera = false;
  1349. m_doingZoomCamera = false;
  1350. m_doingScriptedCameraLock = false;
  1351. // set the camera
  1352. setCameraTransform();
  1353. }
  1354. //-------------------------------------------------------------------------------------------------
  1355. /** Rotate the view around the horizontal (X) axis to the given angle. */
  1356. //-------------------------------------------------------------------------------------------------
  1357. void W3DView::setPitch( Real angle )
  1358. {
  1359. // call our base class, we are extending functionality
  1360. View::setPitch( angle );
  1361. m_doingMoveCameraOnWaypointPath = false;
  1362. m_doingRotateCamera = false;
  1363. m_doingPitchCamera = false;
  1364. m_doingZoomCamera = false;
  1365. m_doingScriptedCameraLock = false;
  1366. // set the camera
  1367. setCameraTransform();
  1368. }
  1369. //-------------------------------------------------------------------------------------------------
  1370. /** Set the view angle & pitch back to default */
  1371. //-------------------------------------------------------------------------------------------------
  1372. void W3DView::setAngleAndPitchToDefault( void )
  1373. {
  1374. // call our base class, we are adding functionality
  1375. View::setAngleAndPitchToDefault();
  1376. this->m_FXPitch = 1.0;
  1377. // set the camera
  1378. setCameraTransform();
  1379. }
  1380. //-------------------------------------------------------------------------------------------------
  1381. //-------------------------------------------------------------------------------------------------
  1382. void W3DView::setDefaultView(Real pitch, Real angle, Real maxHeight)
  1383. {
  1384. // MDC - we no longer want to rotate maps (design made all of them right to begin with)
  1385. // m_defaultAngle = angle * M_PI/180.0f;
  1386. m_defaultPitchAngle = pitch;
  1387. m_maxHeightAboveGround = TheGlobalData->m_maxCameraHeight*maxHeight;
  1388. if (m_minHeightAboveGround > m_maxHeightAboveGround)
  1389. m_maxHeightAboveGround = m_minHeightAboveGround;
  1390. }
  1391. //-------------------------------------------------------------------------------------------------
  1392. //-------------------------------------------------------------------------------------------------
  1393. void W3DView::setHeightAboveGround(Real z)
  1394. {
  1395. m_heightAboveGround = z;
  1396. // if our zoom is limited, we will stay within a predefined distance from the terrain
  1397. if( m_zoomLimited )
  1398. {
  1399. if (m_heightAboveGround < m_minHeightAboveGround)
  1400. m_heightAboveGround = m_minHeightAboveGround;
  1401. if (m_heightAboveGround > m_maxHeightAboveGround)
  1402. m_heightAboveGround = m_maxHeightAboveGround;
  1403. } // end if
  1404. m_doingMoveCameraOnWaypointPath = false;
  1405. m_doingRotateCamera = false;
  1406. m_doingPitchCamera = false;
  1407. m_doingZoomCamera = false;
  1408. m_doingScriptedCameraLock = false;
  1409. m_cameraConstraintValid = false; // recalc it.
  1410. setCameraTransform();
  1411. }
  1412. //-------------------------------------------------------------------------------------------------
  1413. //-------------------------------------------------------------------------------------------------
  1414. void W3DView::setZoom(Real z)
  1415. {
  1416. m_zoom = z;
  1417. if (m_zoom < m_minZoom)
  1418. m_zoom = m_minZoom;
  1419. if (m_zoom > m_maxZoom)
  1420. m_zoom = m_maxZoom;
  1421. m_doingMoveCameraOnWaypointPath = false;
  1422. m_doingRotateCamera = false;
  1423. m_doingPitchCamera = false;
  1424. m_doingZoomCamera = false;
  1425. m_doingScriptedCameraLock = false;
  1426. m_cameraConstraintValid = false; // recalc it.
  1427. setCameraTransform();
  1428. }
  1429. //-------------------------------------------------------------------------------------------------
  1430. //-------------------------------------------------------------------------------------------------
  1431. void W3DView::setZoomToDefault( void )
  1432. {
  1433. // default zoom has to be max, otherwise players will just zoom to max always
  1434. // terrain height + desired height offset == cameraOffset * actual zoom
  1435. // find best approximation of max terrain height we can see
  1436. Real terrainHeightMax = getHeightAroundPos(m_pos.x, m_pos.y);
  1437. Real desiredHeight = (terrainHeightMax + m_maxHeightAboveGround);
  1438. Real desiredZoom = desiredHeight / m_cameraOffset.z;
  1439. m_mcwpInfo.cameraZoom[2] = desiredZoom;//m_maxZoom;
  1440. DEBUG_LOG(("W3DView::setZoomToDefault() Current zoom: %g Desired zoom: %g\n", m_zoom, desiredZoom));
  1441. m_zoom = desiredZoom;
  1442. m_heightAboveGround = m_maxHeightAboveGround;
  1443. m_doingMoveCameraOnWaypointPath = false;
  1444. m_doingRotateCamera = false;
  1445. m_doingPitchCamera = false;
  1446. m_doingZoomCamera = false;
  1447. m_doingScriptedCameraLock = false;
  1448. m_cameraConstraintValid = false; // recalc it.
  1449. setCameraTransform();
  1450. }
  1451. //-------------------------------------------------------------------------------------------------
  1452. /** Set the horizontal field of view angle */
  1453. //-------------------------------------------------------------------------------------------------
  1454. void W3DView::setFieldOfView( Real angle )
  1455. {
  1456. View::setFieldOfView( angle );
  1457. #if defined(_DEBUG) || defined(_INTERNAL)
  1458. // this is only for testing, and recalculating the
  1459. // camera every frame is wasteful
  1460. setCameraTransform();
  1461. #endif
  1462. }
  1463. //-------------------------------------------------------------------------------------------------
  1464. /** Using the W3D camera translate the world coordinate to a screen coord.
  1465. Screen coordinates returned in absolute values relative to full display resolution. */
  1466. //-------------------------------------------------------------------------------------------------
  1467. Bool W3DView::worldToScreen( const Coord3D *w, ICoord2D *s )
  1468. {
  1469. // sanity
  1470. if( w == NULL || s == NULL )
  1471. return FALSE;
  1472. if( m_3DCamera )
  1473. {
  1474. Vector3 world;
  1475. Vector3 screen;
  1476. world.Set( w->x, w->y, w->z );
  1477. enum CameraClass::ProjectionResType projection = m_3DCamera->Project( screen, world );
  1478. if (projection != CameraClass::INSIDE_FRUSTUM && projection!=CameraClass::OUTSIDE_FRUSTUM)
  1479. {
  1480. // Can't get a valid number if it's beyond the clip planes. jba
  1481. s->x = 0;
  1482. s->y = 0;
  1483. return FALSE;
  1484. }
  1485. //
  1486. // note that the screen coord returned from the project W3D camera
  1487. // gave us a screen coords that range from (-1,-1) bottom left to
  1488. // (1,1) top right ... we are turning that into (0,0) upper left
  1489. // coords now
  1490. //
  1491. W3DLogicalScreenToPixelScreen( screen.X, screen.Y,
  1492. &s->x, &s->y,
  1493. getWidth(), getHeight());
  1494. s->x += m_originX; //convert viewport coordinates to full screen coordinates
  1495. s->y += m_originY;
  1496. // s->x = (getWidth() * (screen.X + 1.0f)) / 2.0f;
  1497. // s->y = (getHeight() * (-screen.Y + 1.0f)) / 2.0f;
  1498. if (projection != CameraClass::INSIDE_FRUSTUM)
  1499. {
  1500. return FALSE;
  1501. }
  1502. return TRUE;
  1503. } // end if
  1504. return FALSE;
  1505. } // end worldToScreen
  1506. //-------------------------------------------------------------------------------------------------
  1507. /** Using the W3D camera translate the screen coord to world coord */
  1508. //-------------------------------------------------------------------------------------------------
  1509. void W3DView::screenToWorld( const ICoord2D *s, Coord3D *w )
  1510. {
  1511. // sanity
  1512. if( s == NULL || w == NULL )
  1513. return;
  1514. if( m_3DCamera )
  1515. {
  1516. DEBUG_CRASH(("implement me"));
  1517. } // end if
  1518. } // end screenToWorld
  1519. //-------------------------------------------------------------------------------------------------
  1520. /** all the drawables in the view, that fall within the 2D screen region
  1521. * will call the callback function. The number of drawables that passed
  1522. * the test are returned.
  1523. Screen coordinates assumed in absolute values relative to full display resolution. */
  1524. //-------------------------------------------------------------------------------------------------
  1525. Int W3DView::iterateDrawablesInRegion( IRegion2D *screenRegion,
  1526. Bool (*callback)( Drawable *draw, void *userData ),
  1527. void *userData )
  1528. {
  1529. Bool inside = FALSE;
  1530. Int count = 0;
  1531. Drawable *draw;
  1532. Vector3 screen, world;
  1533. Coord3D pos;
  1534. Region2D normalizedRegion;
  1535. /** @todo we need to have partitions of which drawables are in the
  1536. view so we don't have to march through the whole list */
  1537. //
  1538. // to do this we are projecting the drawable centers onto the screen,
  1539. // the W3D camera->project method is used to do this and that method
  1540. // will return normalized screen coords from (-1,-1) bottom left to
  1541. // (1,1) top right, normalize our screen region for comparison
  1542. //
  1543. /// @todo use fast int->real type casts here later
  1544. Bool regionIsPoint = FALSE;
  1545. if( screenRegion )
  1546. {
  1547. if (screenRegion->height() == 0 && screenRegion->width() == 0)
  1548. {
  1549. regionIsPoint = TRUE;
  1550. }
  1551. normalizedRegion.lo.x = ((Real)(screenRegion->lo.x - m_originX) / (Real)getWidth()) * 2.0f - 1.0f;
  1552. normalizedRegion.lo.y = -(((Real)(screenRegion->hi.y - m_originY) / (Real)getHeight()) * 2.0f - 1.0f);
  1553. normalizedRegion.hi.x = ((Real)(screenRegion->hi.x - m_originX) / (Real)getWidth()) * 2.0f - 1.0f;
  1554. normalizedRegion.hi.y = -(((Real)(screenRegion->lo.y - m_originY) / (Real)getHeight()) * 2.0f - 1.0f);
  1555. } // end if
  1556. Drawable *onlyDrawableToTest = NULL;
  1557. if (regionIsPoint)
  1558. {
  1559. // Allow all drawables to be picked.
  1560. onlyDrawableToTest = pickDrawable(&screenRegion->lo, TRUE, (PickType) getPickTypesForContext(TheInGameUI->isInForceAttackMode()));
  1561. if (onlyDrawableToTest == NULL) {
  1562. return 0;
  1563. }
  1564. }
  1565. for( draw = TheGameClient->firstDrawable();
  1566. draw;
  1567. draw = draw->getNextDrawable() )
  1568. {
  1569. if (onlyDrawableToTest)
  1570. {
  1571. draw = onlyDrawableToTest;
  1572. inside = TRUE;
  1573. }
  1574. else
  1575. {
  1576. // not inside
  1577. inside = FALSE;
  1578. // no screen region, means all drawbles
  1579. if( screenRegion == NULL )
  1580. inside = TRUE;
  1581. else
  1582. {
  1583. // project the center of the drawable to the screen
  1584. /// @todo use a real 3D position in the drawable
  1585. pos = *draw->getPosition();
  1586. world.X = pos.x;
  1587. world.Y = pos.y;
  1588. world.Z = pos.z;
  1589. // project the world point to the screen
  1590. if( m_3DCamera->Project( screen, world ) == CameraClass::INSIDE_FRUSTUM &&
  1591. screen.X >= normalizedRegion.lo.x &&
  1592. screen.X <= normalizedRegion.hi.x &&
  1593. screen.Y >= normalizedRegion.lo.y &&
  1594. screen.Y <= normalizedRegion.hi.y )
  1595. {
  1596. inside = TRUE;
  1597. } // end if
  1598. }
  1599. } //end else
  1600. // if inside do the callback and count up
  1601. if( inside )
  1602. {
  1603. if( callback( draw, userData ) )
  1604. ++count;
  1605. } // end if
  1606. // If onlyDrawableToTest, then we should bail out now.
  1607. if (onlyDrawableToTest != NULL)
  1608. break;
  1609. } // end for draw
  1610. return count;
  1611. } // end iterateDrawablesInRegion
  1612. //-------------------------------------------------------------------------------------------------
  1613. /** cast a ray from the screen coords into the scene and return a drawable
  1614. * there if present. Screen coordinates assumed in absolute values relative
  1615. * to full display resolution. */
  1616. //-------------------------------------------------------------------------------------------------
  1617. Drawable *W3DView::pickDrawable( const ICoord2D *screen, Bool forceAttack, PickType pickType )
  1618. {
  1619. RenderObjClass *renderObj = NULL;
  1620. Drawable *draw = NULL;
  1621. DrawableInfo *drawInfo = NULL;
  1622. // sanity
  1623. if( screen == NULL )
  1624. return NULL;
  1625. // don't pick a drawable if there is a window under the cursor
  1626. GameWindow *window = NULL;
  1627. if (TheWindowManager)
  1628. window = TheWindowManager->getWindowUnderCursor(screen->x, screen->y);
  1629. while (window)
  1630. {
  1631. // check to see if it or any of its parents are opaque. If so, we can't select anything.
  1632. if (!BitTest( window->winGetStatus(), WIN_STATUS_SEE_THRU ))
  1633. return NULL;
  1634. window = window->winGetParent();
  1635. }
  1636. Vector3 rayStart,rayEnd;
  1637. getPickRay(screen,&rayStart,&rayEnd);
  1638. LineSegClass lineseg;
  1639. lineseg.Set(rayStart,rayEnd);
  1640. CastResultStruct result;
  1641. if (forceAttack)
  1642. result.ComputeContactPoint = true;
  1643. //Don't check against translucent or hidden objects
  1644. RayCollisionTestClass raytest(lineseg,&result,COLLISION_TYPE_ALL,false,false);
  1645. if( W3DDisplay::m_3DScene->castRay( raytest, false, (Int)pickType ) )
  1646. renderObj = raytest.CollidedRenderObj;
  1647. // for right now there is no drawable data in a render object which is // if we've found a render object, return our drawable associated with it,
  1648. // the terrain, therefore the userdata is NULL
  1649. /// @todo terrain and picking!
  1650. if( renderObj )
  1651. drawInfo = (DrawableInfo *)renderObj->Get_User_Data();
  1652. if (drawInfo)
  1653. draw=drawInfo->m_drawable;
  1654. return draw;
  1655. } // end pickDrawable
  1656. //-------------------------------------------------------------------------------------------------
  1657. /** convert a pixel (x,y) to a location in the world on the terrain.
  1658. Screen coordinates assumed in absolute values relative to full display resolution. */
  1659. //-------------------------------------------------------------------------------------------------
  1660. void W3DView::screenToTerrain( const ICoord2D *screen, Coord3D *world )
  1661. {
  1662. // sanity
  1663. if( screen == NULL || world == NULL || TheTerrainRenderObject == NULL )
  1664. return;
  1665. if (m_cameraHasMovedSinceRequest) {
  1666. m_locationRequests.clear();
  1667. m_cameraHasMovedSinceRequest = false;
  1668. }
  1669. if (m_locationRequests.size() > MAX_REQUEST_CACHE_SIZE) {
  1670. m_locationRequests.erase(m_locationRequests.begin(), m_locationRequests.begin() + 10);
  1671. }
  1672. // We insert them at the end for speed (no copies needed), but using the princ of locality, we should
  1673. // start searching where we most recently inserted
  1674. for (int i = m_locationRequests.size() - 1; i >= 0; --i) {
  1675. if (m_locationRequests[i].first.x == screen->x && m_locationRequests[i].first.y == screen->y) {
  1676. (*world) = m_locationRequests[i].second;
  1677. return;
  1678. }
  1679. }
  1680. Vector3 rayStart,rayEnd;
  1681. LineSegClass lineseg;
  1682. CastResultStruct result;
  1683. Vector3 intersection(0,0,0);
  1684. getPickRay(screen,&rayStart,&rayEnd);
  1685. lineseg.Set(rayStart,rayEnd);
  1686. RayCollisionTestClass raytest(lineseg,&result);
  1687. if( TheTerrainRenderObject->Cast_Ray(raytest) )
  1688. {
  1689. // get the point of intersection according to W3D
  1690. intersection = result.ContactPoint;
  1691. } // end if
  1692. // Pick bridges.
  1693. Vector3 bridgePt;
  1694. Drawable *bridge = TheTerrainLogic->pickBridge(rayStart, rayEnd, &bridgePt);
  1695. if (bridge && bridgePt.Z > intersection.Z) {
  1696. intersection = bridgePt;
  1697. }
  1698. world->x = intersection.X;
  1699. world->y = intersection.Y;
  1700. world->z = intersection.Z;
  1701. PosRequest req;
  1702. req.first = (*screen);
  1703. req.second = (*world);
  1704. m_locationRequests.push_back(req); // Insert this request at the end, requires no extra copies
  1705. } // end screenToTerrain
  1706. //-------------------------------------------------------------------------------------------------
  1707. //-------------------------------------------------------------------------------------------------
  1708. void W3DView::lookAt( const Coord3D *o )
  1709. {
  1710. Coord3D pos = *o;
  1711. // no, don't call the super-lookAt, since it will munge our coords
  1712. // as for a 2d view. just call setPosition.
  1713. //View::lookAt(&pos);
  1714. if (o->z > PATHFIND_CELL_SIZE_F+TheTerrainLogic->getGroundHeight(pos.x, pos.y)) {
  1715. // Pos.z is not used, so if we want to look at something off the ground,
  1716. // we have to look at the spot on the ground such that the object intersects
  1717. // with the look at vector in the center of the screen. jba.
  1718. Vector3 rayStart,rayEnd;
  1719. LineSegClass lineseg;
  1720. CastResultStruct result;
  1721. Vector3 intersection(0,0,0);
  1722. rayStart = m_3DCamera->Get_Position(); //get camera location
  1723. m_3DCamera->Un_Project(rayEnd,Vector2(0.0f,0.0f)); //get world space point
  1724. rayEnd -= rayStart; //vector camera to world space point
  1725. rayEnd.Normalize(); //make unit vector
  1726. rayEnd *= m_3DCamera->Get_Depth(); //adjust length to reach far clip plane
  1727. rayStart.Set(pos.x, pos.y, pos.z);
  1728. rayEnd += rayStart; //get point on far clip plane along ray from camera.
  1729. lineseg.Set(rayStart,rayEnd);
  1730. RayCollisionTestClass raytest(lineseg,&result);
  1731. if( TheTerrainRenderObject->Cast_Ray(raytest) )
  1732. {
  1733. // get the point of intersection according to W3D
  1734. pos.x = result.ContactPoint.X;
  1735. pos.y = result.ContactPoint.Y;
  1736. } // end if
  1737. }
  1738. pos.z = 0;
  1739. setPosition(&pos);
  1740. m_doingRotateCamera = false;
  1741. m_doingMoveCameraOnWaypointPath = false;
  1742. m_doingScriptedCameraLock = false;
  1743. setCameraTransform();
  1744. }
  1745. //-------------------------------------------------------------------------------------------------
  1746. //-------------------------------------------------------------------------------------------------
  1747. void W3DView::initHeightForMap( void )
  1748. {
  1749. m_groundLevel = TheTerrainLogic->getGroundHeight(m_pos.x, m_pos.y);
  1750. const Real MAX_GROUND_LEVEL = 120.0; // jba - starting ground level can't exceed this height.
  1751. if (m_groundLevel>MAX_GROUND_LEVEL) {
  1752. m_groundLevel = MAX_GROUND_LEVEL;
  1753. }
  1754. m_cameraOffset.z = m_groundLevel+TheGlobalData->m_cameraHeight;
  1755. m_cameraOffset.y = -(m_cameraOffset.z / tan(TheGlobalData->m_cameraPitch * (PI / 180.0)));
  1756. m_cameraOffset.x = -(m_cameraOffset.y * tan(TheGlobalData->m_cameraYaw * (PI / 180.0)));
  1757. m_cameraConstraintValid = false; // possible ground level change invalidates cam constraints
  1758. setCameraTransform();
  1759. }
  1760. //-------------------------------------------------------------------------------------------------
  1761. /** Move camera to in an interesting fashion. Sets up parameters that get
  1762. * evaluated in draw(). */
  1763. //-------------------------------------------------------------------------------------------------
  1764. void W3DView::moveCameraTo(const Coord3D *o, Int milliseconds, Int shutter, Bool orient)
  1765. {
  1766. m_mcwpInfo.waypoints[0] = *getPosition();
  1767. m_mcwpInfo.cameraAngle[0] = getAngle();
  1768. m_mcwpInfo.waySegLength[0] = 0;
  1769. m_mcwpInfo.waypoints[1] = *getPosition();
  1770. m_mcwpInfo.waySegLength[1] = 0;
  1771. m_mcwpInfo.waypoints[2] = *o;
  1772. m_mcwpInfo.waySegLength[2] = 0;
  1773. m_mcwpInfo.numWaypoints = 2;
  1774. if (milliseconds<1) milliseconds = 1;
  1775. m_mcwpInfo.totalTimeMilliseconds = milliseconds;
  1776. m_mcwpInfo.shutter = 1;
  1777. m_mcwpInfo.curSegment = 1;
  1778. m_mcwpInfo.curSegDistance = 0;
  1779. m_mcwpInfo.totalDistance = 0;
  1780. setupWaypointPath(orient);
  1781. if (m_mcwpInfo.totalTimeMilliseconds==1) {
  1782. // do it instantly.
  1783. moveAlongWaypointPath(1);
  1784. m_doingMoveCameraOnWaypointPath = true;
  1785. }
  1786. }
  1787. //-------------------------------------------------------------------------------------------------
  1788. /** Rotate the camera */
  1789. //-------------------------------------------------------------------------------------------------
  1790. void W3DView::rotateCamera(Real rotations, Int milliseconds)
  1791. {
  1792. m_rcInfo.numHoldFrames = 0;
  1793. m_rcInfo.trackObject = FALSE;
  1794. if (milliseconds<1) milliseconds = 1;
  1795. m_rcInfo.numFrames = milliseconds/TheW3DFrameLengthInMsec;
  1796. if (m_rcInfo.numFrames < 1) {
  1797. m_rcInfo.numFrames = 1;
  1798. }
  1799. m_rcInfo.curFrame = 0;
  1800. m_doingRotateCamera = true;
  1801. m_rcInfo.angle = 2*PI*rotations/m_rcInfo.numFrames;
  1802. m_rcInfo.startTimeMultiplier = m_timeMultiplier;
  1803. m_rcInfo.endTimeMultiplier = m_timeMultiplier;
  1804. m_doingMoveCameraOnWaypointPath = false;
  1805. }
  1806. //-------------------------------------------------------------------------------------------------
  1807. /** Rotate the camera to follow a unit */
  1808. //-------------------------------------------------------------------------------------------------
  1809. void W3DView::rotateCameraTowardObject(ObjectID id, Int milliseconds, Int holdMilliseconds)
  1810. {
  1811. m_rcInfo.trackObject = TRUE;
  1812. if (holdMilliseconds<1) holdMilliseconds = 0;
  1813. m_rcInfo.numHoldFrames = holdMilliseconds/TheW3DFrameLengthInMsec;
  1814. if (m_rcInfo.numHoldFrames < 1) {
  1815. m_rcInfo.numHoldFrames = 0;
  1816. }
  1817. if (milliseconds<1) milliseconds = 1;
  1818. m_rcInfo.numFrames = milliseconds/TheW3DFrameLengthInMsec;
  1819. if (m_rcInfo.numFrames < 1) {
  1820. m_rcInfo.numFrames = 1;
  1821. }
  1822. m_rcInfo.curFrame = 0;
  1823. m_doingRotateCamera = true;
  1824. m_rcInfo.angle = m_angle; // not used here
  1825. m_rcInfo.targetObjectID = id;
  1826. m_rcInfo.startTimeMultiplier = m_timeMultiplier;
  1827. m_rcInfo.endTimeMultiplier = m_timeMultiplier;
  1828. m_doingMoveCameraOnWaypointPath = false;
  1829. }
  1830. //-------------------------------------------------------------------------------------------------
  1831. /** Rotate camera to face a location */
  1832. //-------------------------------------------------------------------------------------------------
  1833. void W3DView::rotateCameraTowardPosition(const Coord3D *pLoc, Int milliseconds)
  1834. {
  1835. m_rcInfo.numHoldFrames = 0;
  1836. m_rcInfo.trackObject = FALSE;
  1837. if (milliseconds<1) milliseconds = 1;
  1838. m_rcInfo.numFrames = milliseconds/TheW3DFrameLengthInMsec;
  1839. if (m_rcInfo.numFrames < 1) {
  1840. m_rcInfo.numFrames = 1;
  1841. }
  1842. Coord3D curPos = *getPosition();
  1843. Vector3 dir(pLoc->x-curPos.x, pLoc->y-curPos.y, 0);
  1844. if (dir.Length()<0.1) return;
  1845. dir.Normalize();
  1846. Real angle = WWMath::Acos(dir.X);
  1847. if (dir.Y<0) {
  1848. angle = -angle;
  1849. }
  1850. // Default camera is rotated 90 degrees, so match.
  1851. angle -= PI/2;
  1852. normAngle(angle);
  1853. m_rcInfo.curFrame = 0;
  1854. m_doingRotateCamera = true;
  1855. m_rcInfo.angle = angle/m_rcInfo.numFrames; // not used here
  1856. m_rcInfo.targetObjectID = INVALID_ID;
  1857. m_rcInfo.startTimeMultiplier = m_timeMultiplier;
  1858. m_rcInfo.endTimeMultiplier = m_timeMultiplier;
  1859. m_doingMoveCameraOnWaypointPath = false;
  1860. }
  1861. //-------------------------------------------------------------------------------------------------
  1862. //-------------------------------------------------------------------------------------------------
  1863. void W3DView::zoomCamera( Real finalZoom, Int milliseconds )
  1864. {
  1865. if (milliseconds<1) milliseconds = 1;
  1866. m_zcInfo.numFrames = milliseconds/TheW3DFrameLengthInMsec;
  1867. if (m_zcInfo.numFrames < 1) {
  1868. m_zcInfo.numFrames = 1;
  1869. }
  1870. m_zcInfo.curFrame = 0;
  1871. m_doingZoomCamera = TRUE;
  1872. m_zcInfo.startZoom = m_zoom;
  1873. m_zcInfo.endZoom = finalZoom;
  1874. }
  1875. //-------------------------------------------------------------------------------------------------
  1876. //-------------------------------------------------------------------------------------------------
  1877. void W3DView::pitchCamera( Real finalPitch, Int milliseconds )
  1878. {
  1879. if (milliseconds<1) milliseconds = 1;
  1880. m_pcInfo.numFrames = milliseconds/TheW3DFrameLengthInMsec;
  1881. if (m_pcInfo.numFrames < 1) {
  1882. m_pcInfo.numFrames = 1;
  1883. }
  1884. m_pcInfo.curFrame = 0;
  1885. m_doingPitchCamera = TRUE;
  1886. m_pcInfo.startPitch = m_FXPitch;
  1887. m_pcInfo.endPitch = finalPitch;
  1888. }
  1889. //-------------------------------------------------------------------------------------------------
  1890. /** Sets the final zoom for a camera movement. */
  1891. //-------------------------------------------------------------------------------------------------
  1892. void W3DView::cameraModFinalZoom( Real finalZoom )
  1893. {
  1894. if (m_doingRotateCamera)
  1895. {
  1896. Real terrainHeightMax = getHeightAroundPos(m_pos.x, m_pos.y);
  1897. Real maxHeight = (terrainHeightMax + m_maxHeightAboveGround);
  1898. Real maxZoom = maxHeight / m_cameraOffset.z;
  1899. zoomCamera( finalZoom*maxZoom, (m_rcInfo.numFrames + m_rcInfo.numHoldFrames - m_rcInfo.curFrame)*TheW3DFrameLengthInMsec );
  1900. }
  1901. if (m_doingMoveCameraOnWaypointPath)
  1902. {
  1903. Coord3D pos = m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints];
  1904. Real terrainHeightMax = getHeightAroundPos(pos.x, pos.y);
  1905. Real maxHeight = (terrainHeightMax + m_maxHeightAboveGround);
  1906. Real maxZoom = maxHeight / m_cameraOffset.z;
  1907. zoomCamera( finalZoom*maxZoom, m_mcwpInfo.totalTimeMilliseconds - m_mcwpInfo.elapsedTimeMilliseconds );
  1908. }
  1909. }
  1910. //-------------------------------------------------------------------------------------------------
  1911. /** Sets the final zoom for a camera movement. */
  1912. //-------------------------------------------------------------------------------------------------
  1913. void W3DView::cameraModFreezeAngle(void)
  1914. {
  1915. if (m_doingRotateCamera) {
  1916. m_rcInfo.angle = 0; // Silly, but consistent.
  1917. m_rcInfo.targetObjectID = INVALID_ID;
  1918. }
  1919. if (m_doingMoveCameraOnWaypointPath) {
  1920. Int i;
  1921. // Real curDistance = 0;
  1922. for (i=0; i<m_mcwpInfo.numWaypoints; i++) {
  1923. m_mcwpInfo.cameraAngle[i+1] = m_mcwpInfo.cameraAngle[0];
  1924. }
  1925. }
  1926. }
  1927. // ------------------------------------------------------------------------------------------------
  1928. /** Sets the look toward point for a camera movement. */
  1929. // ------------------------------------------------------------------------------------------------
  1930. void W3DView::cameraModLookToward(Coord3D *pLoc)
  1931. {
  1932. if (m_doingRotateCamera) {
  1933. return; // Doesn't apply to rotate about a point.
  1934. }
  1935. if (m_doingMoveCameraOnWaypointPath) {
  1936. Int i;
  1937. // Real curDistance = 0;
  1938. for (i=2; i<=m_mcwpInfo.numWaypoints; i++) {
  1939. Coord3D start, mid, end;
  1940. Real factor = 0.5;
  1941. start = m_mcwpInfo.waypoints[i-1];
  1942. start.x += m_mcwpInfo.waypoints[i].x;
  1943. start.y += m_mcwpInfo.waypoints[i].y;
  1944. start.x /= 2;
  1945. start.y /= 2;
  1946. mid = m_mcwpInfo.waypoints[i];
  1947. end = m_mcwpInfo.waypoints[i];
  1948. end.x += m_mcwpInfo.waypoints[i+1].x;
  1949. end.y += m_mcwpInfo.waypoints[i+1].y;
  1950. end.x /= 2;
  1951. end.y /= 2;
  1952. Coord3D result = start;
  1953. result.x += factor*(end.x-start.x);
  1954. result.y += factor*(end.y-start.y);
  1955. result.x += (1-factor)*factor*(mid.x-end.x + mid.x-start.x);
  1956. result.y += (1-factor)*factor*(mid.y-end.y + mid.y-start.y);
  1957. result.z = 0;
  1958. Vector3 dir(pLoc->x-result.x, pLoc->y-result.y, 0);
  1959. if (dir.Length()<0.1) continue;
  1960. dir.Normalize();
  1961. Real angle = WWMath::Acos(dir.X);
  1962. if (dir.Y<0) {
  1963. angle = -angle;
  1964. }
  1965. // Default camera is rotated 90 degrees, so match.
  1966. angle -= PI/2;
  1967. normAngle(angle);
  1968. m_mcwpInfo.cameraAngle[i] = angle;
  1969. }
  1970. if (m_mcwpInfo.totalTimeMilliseconds==1) {
  1971. // do it instantly.
  1972. moveAlongWaypointPath(1);
  1973. m_doingMoveCameraOnWaypointPath = true;
  1974. }
  1975. }
  1976. }
  1977. // ------------------------------------------------------------------------------------------------
  1978. /** Sets the look toward point for the end of a camera movement. */
  1979. // ------------------------------------------------------------------------------------------------
  1980. void W3DView::cameraModFinalMoveTo(Coord3D *pLoc)
  1981. {
  1982. if (m_doingRotateCamera) {
  1983. return; // Doesn't apply to rotate about a point.
  1984. }
  1985. if (m_doingMoveCameraOnWaypointPath) {
  1986. Int i;
  1987. Coord3D start, delta;
  1988. start = m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints];
  1989. delta.x = pLoc->x - start.x;
  1990. delta.y = pLoc->y - start.y;
  1991. for (i=2; i<=m_mcwpInfo.numWaypoints; i++) {
  1992. Coord3D result = m_mcwpInfo.waypoints[i];
  1993. result.x += delta.x;
  1994. result.y += delta.y;
  1995. m_mcwpInfo.waypoints[i] = result;
  1996. }
  1997. }
  1998. }
  1999. // ------------------------------------------------------------------------------------------------
  2000. /** Sets the look toward point for the end of a camera movement. */
  2001. // ------------------------------------------------------------------------------------------------
  2002. void W3DView::cameraModFinalLookToward(Coord3D *pLoc)
  2003. {
  2004. if (m_doingRotateCamera) {
  2005. return; // Doesn't apply to rotate about a point.
  2006. }
  2007. if (m_doingMoveCameraOnWaypointPath) {
  2008. Int i;
  2009. Int min = m_mcwpInfo.numWaypoints-1;
  2010. if (min<2) min=2;
  2011. // Real curDistance = 0;
  2012. for (i=min; i<=m_mcwpInfo.numWaypoints; i++) {
  2013. Coord3D start, mid, end;
  2014. Real factor = 0.5;
  2015. start = m_mcwpInfo.waypoints[i-1];
  2016. start.x += m_mcwpInfo.waypoints[i].x;
  2017. start.y += m_mcwpInfo.waypoints[i].y;
  2018. start.x /= 2;
  2019. start.y /= 2;
  2020. mid = m_mcwpInfo.waypoints[i];
  2021. end = m_mcwpInfo.waypoints[i];
  2022. end.x += m_mcwpInfo.waypoints[i+1].x;
  2023. end.y += m_mcwpInfo.waypoints[i+1].y;
  2024. end.x /= 2;
  2025. end.y /= 2;
  2026. Coord3D result = start;
  2027. result.x += factor*(end.x-start.x);
  2028. result.y += factor*(end.y-start.y);
  2029. result.x += (1-factor)*factor*(mid.x-end.x + mid.x-start.x);
  2030. result.y += (1-factor)*factor*(mid.y-end.y + mid.y-start.y);
  2031. result.z = 0;
  2032. Vector3 dir(pLoc->x-result.x, pLoc->y-result.y, 0);
  2033. if (dir.Length()<0.1) continue;
  2034. dir.Normalize();
  2035. Real angle = WWMath::Acos(dir.X);
  2036. if (dir.Y<0) {
  2037. angle = -angle;
  2038. }
  2039. // Default camera is rotated 90 degrees, so match.
  2040. angle -= PI/2;
  2041. normAngle(angle);
  2042. if (i==m_mcwpInfo.numWaypoints) {
  2043. m_mcwpInfo.cameraAngle[i] = angle;
  2044. } else {
  2045. Real deltaAngle = angle - m_mcwpInfo.cameraAngle[i];
  2046. normAngle(deltaAngle);
  2047. angle = m_mcwpInfo.cameraAngle[i] + deltaAngle/2;
  2048. normAngle(angle);
  2049. m_mcwpInfo.cameraAngle[i] = angle;
  2050. }
  2051. }
  2052. }
  2053. }
  2054. // ------------------------------------------------------------------------------------------------
  2055. /** Sets the final time multiplier for a camera movement. */
  2056. // ------------------------------------------------------------------------------------------------
  2057. void W3DView::cameraModFinalTimeMultiplier(Int finalMultiplier)
  2058. {
  2059. if (m_doingZoomCamera)
  2060. m_zcInfo.endTimeMultiplier = finalMultiplier;
  2061. if (m_doingPitchCamera)
  2062. m_pcInfo.endTimeMultiplier = finalMultiplier;
  2063. if (m_doingRotateCamera) {
  2064. m_rcInfo.endTimeMultiplier = finalMultiplier;
  2065. } else if (m_doingMoveCameraOnWaypointPath) {
  2066. Int i;
  2067. Real curDistance = 0;
  2068. for (i=0; i<m_mcwpInfo.numWaypoints; i++) {
  2069. curDistance += m_mcwpInfo.waySegLength[i];
  2070. Real factor2 = curDistance / m_mcwpInfo.totalDistance;
  2071. Real factor1 = 1.0-factor2;
  2072. m_mcwpInfo.timeMultiplier[i+1] = REAL_TO_INT_FLOOR(0.5+m_mcwpInfo.timeMultiplier[i+1]*factor1 + finalMultiplier*factor2);
  2073. }
  2074. } else {
  2075. // If we aren't doing a camera movement, just set the time.
  2076. m_timeMultiplier = finalMultiplier;
  2077. }
  2078. }
  2079. // ------------------------------------------------------------------------------------------------
  2080. /** Sets the number of frames to average motion for a camera movement */
  2081. // ------------------------------------------------------------------------------------------------
  2082. void W3DView::cameraModRollingAverage(Int framesToAverage)
  2083. {
  2084. if (framesToAverage < 1) framesToAverage = 1;
  2085. m_mcwpInfo.rollingAverageFrames = framesToAverage;
  2086. }
  2087. // ------------------------------------------------------------------------------------------------
  2088. /** Sets the final pitch for a camera movement. */
  2089. // ------------------------------------------------------------------------------------------------
  2090. void W3DView::cameraModFinalPitch(Real finalPitch) {
  2091. if (m_doingRotateCamera) {
  2092. pitchCamera( finalPitch, ((m_rcInfo.numFrames + m_rcInfo.numHoldFrames) - m_rcInfo.curFrame) * TheW3DFrameLengthInMsec );
  2093. }
  2094. if (m_doingMoveCameraOnWaypointPath) {
  2095. pitchCamera( finalPitch, m_mcwpInfo.totalTimeMilliseconds - m_mcwpInfo.elapsedTimeMilliseconds );
  2096. }
  2097. }
  2098. // ------------------------------------------------------------------------------------------------
  2099. /** Move camera to a waypoint, resetting the default angle, pitch & zoom along the way.. */
  2100. // ------------------------------------------------------------------------------------------------
  2101. void W3DView::resetCamera(const Coord3D *location, Int milliseconds)
  2102. {
  2103. moveCameraTo(location, milliseconds, 0, false);
  2104. m_mcwpInfo.cameraAngle[2] = 0.0; // default angle.
  2105. m_angle = m_mcwpInfo.cameraAngle[0];
  2106. m_mcwpInfo.cameraFXPitch[2] = 1.0;
  2107. // terrain height + desired height offset == cameraOffset * actual zoom
  2108. // find best approximation of max terrain height we can see
  2109. //Real terrainHeightMax = getHeightAroundPos(m_pos.x, m_pos.y);
  2110. Real terrainHeightMax = getHeightAroundPos(location->x, location->y);
  2111. Real desiredHeight = (terrainHeightMax + m_maxHeightAboveGround);
  2112. Real desiredZoom = desiredHeight / m_cameraOffset.z;
  2113. m_mcwpInfo.cameraZoom[2] = desiredZoom;//m_maxZoom;
  2114. //Real terrainHeightMax = getHeightAroundPos(location->x, location->y);
  2115. //Real desiredHeight = (terrainHeightMax + m_maxHeightAboveGround);
  2116. //Real desiredZoom = desiredHeight / m_cameraOffset.z;
  2117. zoomCamera( desiredZoom, milliseconds ); // this isn't right... or is it?
  2118. pitchCamera( 1.0, milliseconds );
  2119. DEBUG_LOG(("W3DView::resetCamera() Current zoom: %g Desired zoom: %g Current pitch: %g Desired pitch: %g\n",
  2120. m_zoom, desiredZoom, m_pitchAngle, m_defaultPitchAngle));
  2121. }
  2122. // ------------------------------------------------------------------------------------------------
  2123. // ------------------------------------------------------------------------------------------------
  2124. Bool W3DView::isCameraMovementFinished(void)
  2125. {
  2126. if (m_viewFilter == FT_VIEW_MOTION_BLUR_FILTER) {
  2127. // Several of the motion blur effects are similar to camera movements.
  2128. if (m_viewFilterMode == FM_VIEW_MB_IN_AND_OUT_ALPHA ||
  2129. m_viewFilterMode == FM_VIEW_MB_IN_AND_OUT_SATURATE ||
  2130. m_viewFilterMode == FM_VIEW_MB_IN_ALPHA ||
  2131. m_viewFilterMode == FM_VIEW_MB_OUT_ALPHA ||
  2132. m_viewFilterMode == FM_VIEW_MB_IN_SATURATE ||
  2133. m_viewFilterMode == FM_VIEW_MB_OUT_SATURATE ) {
  2134. return true;
  2135. }
  2136. }
  2137. return !m_doingMoveCameraOnWaypointPath && !m_doingRotateCamera && !m_doingPitchCamera && !m_doingZoomCamera;
  2138. }
  2139. // ------------------------------------------------------------------------------------------------
  2140. /** Move camera along a waypoint path in an interesting fashion. Sets up parameters that get
  2141. * evaluated in draw(). */
  2142. // ------------------------------------------------------------------------------------------------
  2143. void W3DView::moveCameraAlongWaypointPath(Waypoint *pWay, Int milliseconds, Int shutter, Bool orient)
  2144. {
  2145. const Real MIN_DELTA = MAP_XY_FACTOR;
  2146. m_mcwpInfo.waypoints[0] = *getPosition();
  2147. m_mcwpInfo.cameraAngle[0] = getAngle();
  2148. m_mcwpInfo.waySegLength[0] = 0;
  2149. m_mcwpInfo.waypoints[1] = *getPosition();
  2150. m_mcwpInfo.numWaypoints = 1;
  2151. if (milliseconds<1) milliseconds = 1;
  2152. m_mcwpInfo.totalTimeMilliseconds = milliseconds;
  2153. m_mcwpInfo.shutter = shutter/TheW3DFrameLengthInMsec;
  2154. if (m_mcwpInfo.shutter<1) m_mcwpInfo.shutter = 1;
  2155. while (pWay && m_mcwpInfo.numWaypoints <MAX_WAYPOINTS) {
  2156. m_mcwpInfo.numWaypoints++;
  2157. m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints] = *pWay->getLocation();
  2158. if (pWay->getNumLinks()>0) {
  2159. pWay = pWay->getLink(0);
  2160. } else {
  2161. pWay = NULL;
  2162. }
  2163. Vector3 dir(m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints].x-m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints-1].x, m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints].y-m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints-1].y, 0);
  2164. if (dir.Length()<MIN_DELTA) {
  2165. if (pWay) {
  2166. m_mcwpInfo.numWaypoints--; // drop this one.
  2167. } else {
  2168. m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints-1] = m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints];
  2169. m_mcwpInfo.numWaypoints--; // Push this one back.
  2170. }
  2171. }
  2172. }
  2173. setupWaypointPath(orient);
  2174. }
  2175. // ------------------------------------------------------------------------------------------------
  2176. /** Calculates angles and distances for moving along a waypoint path. Sets up parameters that get
  2177. * evaluated in draw(). */
  2178. // ------------------------------------------------------------------------------------------------
  2179. void W3DView::setupWaypointPath(Bool orient)
  2180. {
  2181. m_mcwpInfo.curSegment = 1;
  2182. m_mcwpInfo.curSegDistance = 0;
  2183. m_mcwpInfo.totalDistance = 0;
  2184. m_mcwpInfo.rollingAverageFrames = 1;
  2185. Int i;
  2186. for (i=1; i<m_mcwpInfo.numWaypoints; i++) {
  2187. Vector3 dir(m_mcwpInfo.waypoints[i+1].x-m_mcwpInfo.waypoints[i].x, m_mcwpInfo.waypoints[i+1].y-m_mcwpInfo.waypoints[i].y, 0);
  2188. m_mcwpInfo.waySegLength[i] = dir.Length();
  2189. m_mcwpInfo.totalDistance += m_mcwpInfo.waySegLength[i];
  2190. dir.Normalize();
  2191. Real angle = getAngle();
  2192. if (orient) {
  2193. angle = WWMath::Acos(dir.X);
  2194. if (dir.Y<0) {
  2195. angle = -angle;
  2196. }
  2197. // Default camera is rotated 90 degrees, so match.
  2198. angle -= PI/2;
  2199. normAngle(angle);
  2200. }
  2201. //DEBUG_LOG(("Original Index %d, angle %.2f\n", i, angle*180/PI));
  2202. m_mcwpInfo.cameraAngle[i] = angle;
  2203. }
  2204. m_mcwpInfo.cameraAngle[1] = getAngle();
  2205. m_mcwpInfo.cameraAngle[m_mcwpInfo.numWaypoints] = m_mcwpInfo.cameraAngle[m_mcwpInfo.numWaypoints-1];
  2206. for (i=m_mcwpInfo.numWaypoints-1; i>1; i--) {
  2207. m_mcwpInfo.cameraAngle[i] = (m_mcwpInfo.cameraAngle[i] + m_mcwpInfo.cameraAngle[i-1]) / 2;
  2208. }
  2209. m_mcwpInfo.waySegLength[m_mcwpInfo.numWaypoints+1] = m_mcwpInfo.waySegLength[m_mcwpInfo.numWaypoints+1];
  2210. if (m_mcwpInfo.totalDistance<1.0) {
  2211. m_mcwpInfo.waySegLength[m_mcwpInfo.numWaypoints-1] += 1.0-m_mcwpInfo.totalDistance;
  2212. m_mcwpInfo.totalDistance = 1.0;
  2213. }
  2214. Real curDistance = 0;
  2215. Coord3D finalPos = m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints];
  2216. Real newGround = TheTerrainLogic->getGroundHeight(finalPos.x, finalPos.y);
  2217. for (i=0; i<=m_mcwpInfo.numWaypoints+1; i++) {
  2218. Real factor2 = curDistance / m_mcwpInfo.totalDistance;
  2219. Real factor1 = 1.0-factor2;
  2220. m_mcwpInfo.cameraFXPitch[i] = m_FXPitch;
  2221. m_mcwpInfo.cameraZoom[i] = m_zoom;
  2222. m_mcwpInfo.timeMultiplier[i] = m_timeMultiplier;
  2223. m_mcwpInfo.groundHeight[i] = m_groundLevel*factor1 + newGround*factor2;
  2224. curDistance += m_mcwpInfo.waySegLength[i];
  2225. //DEBUG_LOG(("New Index %d, angle %.2f\n", i, m_mcwpInfo.cameraAngle[i]*180/PI));
  2226. }
  2227. // Pad the end.
  2228. m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints+1] = m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints];
  2229. Coord3D cur = m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints];
  2230. Coord3D prev = m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints-1];
  2231. m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints+1].x += cur.x-prev.x;
  2232. m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints+1].y += cur.y-prev.y;
  2233. m_mcwpInfo.cameraAngle[m_mcwpInfo.numWaypoints+1] = m_mcwpInfo.cameraAngle[m_mcwpInfo.numWaypoints];
  2234. m_mcwpInfo.groundHeight[m_mcwpInfo.numWaypoints+1] = newGround;
  2235. cur = m_mcwpInfo.waypoints[2];
  2236. prev = m_mcwpInfo.waypoints[1];
  2237. m_mcwpInfo.waypoints[0].x -= cur.x-prev.x;
  2238. m_mcwpInfo.waypoints[0].y -= cur.y-prev.y;
  2239. m_doingMoveCameraOnWaypointPath = m_mcwpInfo.numWaypoints>1;
  2240. m_doingRotateCamera = false;
  2241. m_mcwpInfo.elapsedTimeMilliseconds = 0;
  2242. m_mcwpInfo.curShutter = m_mcwpInfo.shutter;
  2243. }
  2244. // ------------------------------------------------------------------------------------------------
  2245. // ------------------------------------------------------------------------------------------------
  2246. static Real makeQuadraticS(Real t)
  2247. {
  2248. // for t = linear 0-1, convert to quadratic s where 0==0, 0.5==0.5 && 1.0 == 1.0.
  2249. Real tPrime = t;
  2250. if (t<0.5) {
  2251. tPrime = 0.5 * (2*t*2*t);
  2252. } else {
  2253. tPrime = (t-0.5)*2;
  2254. tPrime = WWMath::Sqrt(tPrime);
  2255. tPrime = 0.5 + 0.5*(tPrime);
  2256. }
  2257. return tPrime*0.5 + t*0.5;
  2258. }
  2259. // ------------------------------------------------------------------------------------------------
  2260. // ------------------------------------------------------------------------------------------------
  2261. void W3DView::rotateCameraOneFrame(void)
  2262. {
  2263. m_rcInfo.curFrame++;
  2264. if (TheGlobalData->m_disableCameraMovement) {
  2265. if (m_rcInfo.curFrame >= m_rcInfo.numFrames + m_rcInfo.numHoldFrames) {
  2266. m_doingRotateCamera = false;
  2267. m_freezeTimeForCameraMovement = false;
  2268. }
  2269. return;
  2270. }
  2271. if (m_rcInfo.curFrame <= m_rcInfo.numFrames)
  2272. {
  2273. // not just holding; do the camera adjustment
  2274. Real factor = ((Real)m_rcInfo.curFrame)/m_rcInfo.numFrames;
  2275. if (m_rcInfo.trackObject)
  2276. {
  2277. const Object *obj = TheGameLogic->findObjectByID(m_rcInfo.targetObjectID);
  2278. if (obj)
  2279. {
  2280. // object has not been destroyed
  2281. m_rcInfo.targetObjectPos = *obj->getPosition();
  2282. }
  2283. Vector3 dir(m_rcInfo.targetObjectPos.x - m_pos.x, m_rcInfo.targetObjectPos.y - m_pos.y, 0);
  2284. if (dir.Length()>=0.1)
  2285. {
  2286. dir.Normalize();
  2287. Real angle = WWMath::Acos(dir.X);
  2288. if (dir.Y<0) {
  2289. angle = -angle;
  2290. }
  2291. // Default camera is rotated 90 degrees, so match.
  2292. angle -= PI/2;
  2293. normAngle(angle);
  2294. factor = 1.0f / (m_rcInfo.numFrames - m_rcInfo.curFrame + 1);
  2295. Real angleDiff = (angle - m_angle);
  2296. normAngle(angleDiff);
  2297. angleDiff *= factor;
  2298. m_angle += angleDiff;
  2299. }
  2300. }
  2301. else
  2302. {
  2303. m_angle += m_rcInfo.angle;
  2304. }
  2305. //m_zoom = m_rcInfo.startZoom + (m_rcInfo.endZoom-m_rcInfo.startZoom)*factor;
  2306. //m_FXPitch = m_rcInfo.startPitch + (m_rcInfo.endPitch-m_rcInfo.startPitch)*factor;
  2307. normAngle(m_angle);
  2308. //DEBUG_LOG(("\tm_angle:%g\n", m_angle));
  2309. m_timeMultiplier = m_rcInfo.startTimeMultiplier + REAL_TO_INT_FLOOR(0.5 + (m_rcInfo.endTimeMultiplier-m_rcInfo.startTimeMultiplier)*factor);
  2310. }
  2311. else if (m_rcInfo.curFrame <= m_rcInfo.numFrames + m_rcInfo.numHoldFrames && m_rcInfo.trackObject)
  2312. {
  2313. const Object *obj = TheGameLogic->findObjectByID(m_rcInfo.targetObjectID);
  2314. if (obj)
  2315. {
  2316. // object has not been destroyed
  2317. m_rcInfo.targetObjectPos = *obj->getPosition();
  2318. }
  2319. Vector3 dir(m_rcInfo.targetObjectPos.x - m_pos.x, m_rcInfo.targetObjectPos.y - m_pos.y, 0);
  2320. if (dir.Length()>=0.1)
  2321. {
  2322. dir.Normalize();
  2323. Real angle = WWMath::Acos(dir.X);
  2324. if (dir.Y<0) {
  2325. angle = -angle;
  2326. }
  2327. // Default camera is rotated 90 degrees, so match.
  2328. angle -= PI/2;
  2329. normAngle(angle);
  2330. m_angle = angle;
  2331. }
  2332. }
  2333. if (m_rcInfo.curFrame >= m_rcInfo.numFrames + m_rcInfo.numHoldFrames) {
  2334. m_doingRotateCamera = false;
  2335. m_freezeTimeForCameraMovement = false;
  2336. //m_zoom = m_rcInfo.endZoom;
  2337. //m_FXPitch = m_rcInfo.endPitch;
  2338. }
  2339. }
  2340. // ------------------------------------------------------------------------------------------------
  2341. // ------------------------------------------------------------------------------------------------
  2342. void W3DView::zoomCameraOneFrame(void)
  2343. {
  2344. m_zcInfo.curFrame++;
  2345. if (TheGlobalData->m_disableCameraMovement) {
  2346. if (m_zcInfo.curFrame >= m_zcInfo.numFrames) {
  2347. m_doingZoomCamera = false;
  2348. }
  2349. return;
  2350. }
  2351. if (m_zcInfo.curFrame <= m_zcInfo.numFrames)
  2352. {
  2353. // not just holding; do the camera adjustment
  2354. Real factor = ((Real)m_zcInfo.curFrame)/m_zcInfo.numFrames;
  2355. m_zoom = m_zcInfo.startZoom + (m_zcInfo.endZoom-m_zcInfo.startZoom)*factor;
  2356. }
  2357. if (m_zcInfo.curFrame >= m_zcInfo.numFrames) {
  2358. m_doingZoomCamera = false;
  2359. m_zoom = m_zcInfo.endZoom;
  2360. }
  2361. //DEBUG_LOG(("W3DView::zoomCameraOneFrame() - m_zoom = %g\n", m_zoom));
  2362. }
  2363. // ------------------------------------------------------------------------------------------------
  2364. // ------------------------------------------------------------------------------------------------
  2365. void W3DView::pitchCameraOneFrame(void)
  2366. {
  2367. m_pcInfo.curFrame++;
  2368. if (TheGlobalData->m_disableCameraMovement) {
  2369. if (m_pcInfo.curFrame >= m_pcInfo.numFrames) {
  2370. m_doingPitchCamera = false;
  2371. }
  2372. return;
  2373. }
  2374. if (m_pcInfo.curFrame <= m_pcInfo.numFrames)
  2375. {
  2376. // not just holding; do the camera adjustment
  2377. Real factor = ((Real)m_pcInfo.curFrame)/m_pcInfo.numFrames;
  2378. m_FXPitch = m_pcInfo.startPitch + (m_pcInfo.endPitch-m_pcInfo.startPitch)*factor;
  2379. }
  2380. if (m_pcInfo.curFrame >= m_pcInfo.numFrames) {
  2381. m_doingPitchCamera = false;
  2382. m_FXPitch = m_pcInfo.endPitch;
  2383. }
  2384. }
  2385. // ------------------------------------------------------------------------------------------------
  2386. // ------------------------------------------------------------------------------------------------
  2387. void W3DView::moveAlongWaypointPath(Int milliseconds)
  2388. {
  2389. m_mcwpInfo.elapsedTimeMilliseconds += milliseconds;
  2390. if (TheGlobalData->m_disableCameraMovement) {
  2391. if (m_mcwpInfo.elapsedTimeMilliseconds>m_mcwpInfo.totalTimeMilliseconds) {
  2392. m_doingMoveCameraOnWaypointPath = false;
  2393. m_freezeTimeForCameraMovement = false;
  2394. }
  2395. return;
  2396. }
  2397. if (m_mcwpInfo.elapsedTimeMilliseconds>m_mcwpInfo.totalTimeMilliseconds) {
  2398. m_doingMoveCameraOnWaypointPath = false;
  2399. m_freezeTimeForCameraMovement = false;
  2400. m_angle = m_mcwpInfo.cameraAngle[m_mcwpInfo.numWaypoints];
  2401. //m_zoom = m_mcwpInfo.cameraZoom[m_mcwpInfo.numWaypoints];
  2402. //m_FXPitch = m_mcwpInfo.cameraFXPitch[m_mcwpInfo.numWaypoints];
  2403. m_groundLevel = m_mcwpInfo.groundHeight[m_mcwpInfo.numWaypoints];
  2404. /////////////////////m_cameraOffset.z = m_groundLevel+TheGlobalData->m_cameraHeight;
  2405. m_cameraOffset.y = -(m_cameraOffset.z / tan(TheGlobalData->m_cameraPitch * (PI / 180.0)));
  2406. m_cameraOffset.x = -(m_cameraOffset.y * tan(TheGlobalData->m_cameraYaw * (PI / 180.0)));
  2407. Coord3D pos = m_mcwpInfo.waypoints[m_mcwpInfo.numWaypoints];
  2408. pos.z = 0;
  2409. setPosition(&pos);
  2410. // Note - assuming that the scripter knows what he is doing, we adjust the constraints so that
  2411. // the scripted action can occur.
  2412. m_cameraConstraint.lo.x = minf(m_cameraConstraint.lo.x, pos.x);
  2413. m_cameraConstraint.hi.x = maxf(m_cameraConstraint.hi.x, pos.x);
  2414. m_cameraConstraint.lo.y = minf(m_cameraConstraint.lo.y, pos.y);
  2415. m_cameraConstraint.hi.y = maxf(m_cameraConstraint.hi.y, pos.y);
  2416. return;
  2417. }
  2418. Int deltaTime = milliseconds;
  2419. Real distance = m_mcwpInfo.totalDistance * deltaTime / m_mcwpInfo.totalTimeMilliseconds;
  2420. m_mcwpInfo.curSegDistance += distance;
  2421. while (m_mcwpInfo.curSegDistance > m_mcwpInfo.waySegLength[m_mcwpInfo.curSegment]) {
  2422. m_mcwpInfo.curSegDistance -= m_mcwpInfo.waySegLength[m_mcwpInfo.curSegment];
  2423. m_mcwpInfo.curSegment++;
  2424. if (m_mcwpInfo.curSegment >= m_mcwpInfo.numWaypoints) {
  2425. m_mcwpInfo.totalTimeMilliseconds = 0; // Will end following next frame.
  2426. return;
  2427. }
  2428. }
  2429. Real avgFactor = 1.0/m_mcwpInfo.rollingAverageFrames;
  2430. m_mcwpInfo.curShutter--;
  2431. if (m_mcwpInfo.curShutter>0) {
  2432. return;
  2433. }
  2434. m_mcwpInfo.curShutter = m_mcwpInfo.shutter;
  2435. Real factor = m_mcwpInfo.curSegDistance / m_mcwpInfo.waySegLength[m_mcwpInfo.curSegment];
  2436. if (m_mcwpInfo.curSegment == m_mcwpInfo.numWaypoints-1) {
  2437. avgFactor = avgFactor + (1.0-avgFactor)*factor;
  2438. }
  2439. Real angle = getAngle();
  2440. Real factor1;
  2441. Real factor2;
  2442. factor1 = 1.0-factor;
  2443. //factor1 = makeQuadraticS(factor1);
  2444. factor2 = 1.0-factor1;
  2445. Real angle1 = m_mcwpInfo.cameraAngle[m_mcwpInfo.curSegment];
  2446. Real angle2 = m_mcwpInfo.cameraAngle[m_mcwpInfo.curSegment+1];
  2447. if (angle2-angle1 > PI) angle1 += 2*PI;
  2448. if (angle2-angle1 < -PI) angle1 -= 2*PI;
  2449. angle = angle1 * (factor1) + angle2 * (factor2);
  2450. normAngle(angle);
  2451. Real deltaAngle = angle-m_angle;
  2452. normAngle(deltaAngle);
  2453. if (fabs(deltaAngle) > PI/10) {
  2454. DEBUG_LOG(("Huh.\n"));
  2455. }
  2456. m_angle += avgFactor*(deltaAngle);
  2457. normAngle(m_angle);
  2458. /*
  2459. Real pitchFX = m_mcwpInfo.cameraFXPitch[m_mcwpInfo.curSegment]*factor1 +
  2460. m_mcwpInfo.cameraFXPitch[m_mcwpInfo.curSegment+1]*factor2;
  2461. m_FXPitch += avgFactor*(pitchFX-m_FXPitch);
  2462. Real cameraZoom = m_mcwpInfo.cameraZoom[m_mcwpInfo.curSegment]*factor1 +
  2463. m_mcwpInfo.cameraZoom[m_mcwpInfo.curSegment+1]*factor2;
  2464. m_zoom += avgFactor*(cameraZoom-m_zoom);
  2465. */
  2466. Real timeMultiplier = m_mcwpInfo.timeMultiplier[m_mcwpInfo.curSegment]*factor1 +
  2467. m_mcwpInfo.timeMultiplier[m_mcwpInfo.curSegment+1]*factor2;
  2468. m_timeMultiplier = REAL_TO_INT_FLOOR(0.5 + timeMultiplier);
  2469. m_groundLevel = m_mcwpInfo.groundHeight[m_mcwpInfo.curSegment]*factor1 +
  2470. m_mcwpInfo.groundHeight[m_mcwpInfo.curSegment+1]*factor2;
  2471. //////////////m_cameraOffset.z = m_groundLevel+TheGlobalData->m_cameraHeight;
  2472. m_cameraOffset.y = -(m_cameraOffset.z / tan(TheGlobalData->m_cameraPitch * (PI / 180.0)));
  2473. m_cameraOffset.x = -(m_cameraOffset.y * tan(TheGlobalData->m_cameraYaw * (PI / 180.0)));
  2474. Coord3D start, mid, end;
  2475. if (factor<0.5) {
  2476. start = m_mcwpInfo.waypoints[m_mcwpInfo.curSegment-1];
  2477. start.x += m_mcwpInfo.waypoints[m_mcwpInfo.curSegment].x;
  2478. start.y += m_mcwpInfo.waypoints[m_mcwpInfo.curSegment].y;
  2479. start.x /= 2;
  2480. start.y /= 2;
  2481. mid = m_mcwpInfo.waypoints[m_mcwpInfo.curSegment];
  2482. end = m_mcwpInfo.waypoints[m_mcwpInfo.curSegment];
  2483. end.x += m_mcwpInfo.waypoints[m_mcwpInfo.curSegment+1].x;
  2484. end.y += m_mcwpInfo.waypoints[m_mcwpInfo.curSegment+1].y;
  2485. end.x /= 2;
  2486. end.y /= 2;
  2487. factor += 0.5;
  2488. } else {
  2489. start = m_mcwpInfo.waypoints[m_mcwpInfo.curSegment];
  2490. start.x += m_mcwpInfo.waypoints[m_mcwpInfo.curSegment+1].x;
  2491. start.y += m_mcwpInfo.waypoints[m_mcwpInfo.curSegment+1].y;
  2492. start.x /= 2;
  2493. start.y /= 2;
  2494. mid = m_mcwpInfo.waypoints[m_mcwpInfo.curSegment+1];
  2495. end = m_mcwpInfo.waypoints[m_mcwpInfo.curSegment+1];
  2496. end.x += m_mcwpInfo.waypoints[m_mcwpInfo.curSegment+2].x;
  2497. end.y += m_mcwpInfo.waypoints[m_mcwpInfo.curSegment+2].y;
  2498. end.x /= 2;
  2499. end.y /= 2;
  2500. factor -= 0.5;
  2501. }
  2502. Coord3D result = start;
  2503. result.x += factor*(end.x-start.x);
  2504. result.y += factor*(end.y-start.y);
  2505. result.x += (1-factor)*factor*(mid.x-end.x + mid.x-start.x);
  2506. result.y += (1-factor)*factor*(mid.y-end.y + mid.y-start.y);
  2507. result.z = 0;
  2508. /*
  2509. static Real prevGround = 0;
  2510. DEBUG_LOG(("Dx %.2f, dy %.2f, DeltaANgle = %.2f, %.2f DeltaGround %.2f\n", m_pos.x-result.x, m_pos.y-result.y, deltaAngle, m_groundLevel, m_groundLevel-prevGround));
  2511. prevGround = m_groundLevel;
  2512. */
  2513. setPosition(&result);
  2514. // Note - assuming that the scripter knows what he is doing, we adjust the constraints so that
  2515. // the scripted action can occur.
  2516. m_cameraConstraint.lo.x = minf(m_cameraConstraint.lo.x, result.x);
  2517. m_cameraConstraint.hi.x = maxf(m_cameraConstraint.hi.x, result.x);
  2518. m_cameraConstraint.lo.y = minf(m_cameraConstraint.lo.y, result.y);
  2519. m_cameraConstraint.hi.y = maxf(m_cameraConstraint.hi.y, result.y);
  2520. }
  2521. // ------------------------------------------------------------------------------------------------
  2522. /** Add an impulse force to shake the camera.
  2523. * The camera shake is a simple simulation of an oscillating spring/damper.
  2524. * The idea is that some sort of shock has "pushed" the camera once, as an
  2525. * impluse, after which the camera vibrates back to its rest position.
  2526. * @todo This should be part of "View", not "W3DView". */
  2527. // ------------------------------------------------------------------------------------------------
  2528. void W3DView::shake( const Coord3D *epicenter, CameraShakeType shakeType )
  2529. {
  2530. Real angle = GameClientRandomValueReal( 0, 2*PI );
  2531. m_shakeAngleCos = (Real)cos( angle );
  2532. m_shakeAngleSin = (Real)sin( angle );
  2533. Real intensity = 0.0f;
  2534. switch( shakeType )
  2535. {
  2536. case SHAKE_SUBTLE:
  2537. intensity = TheGlobalData->m_shakeSubtleIntensity;
  2538. break;
  2539. case SHAKE_NORMAL:
  2540. intensity = TheGlobalData->m_shakeNormalIntensity;
  2541. break;
  2542. case SHAKE_STRONG:
  2543. intensity = TheGlobalData->m_shakeStrongIntensity;
  2544. break;
  2545. case SHAKE_SEVERE:
  2546. intensity = TheGlobalData->m_shakeSevereIntensity;
  2547. break;
  2548. case SHAKE_CINE_EXTREME:
  2549. intensity = TheGlobalData->m_shakeCineExtremeIntensity;
  2550. break;
  2551. case SHAKE_CINE_INSANE:
  2552. intensity = TheGlobalData->m_shakeCineInsaneIntensity;
  2553. break;
  2554. }
  2555. // intensity falls off with distance
  2556. const Coord3D *viewPos = getPosition();
  2557. Coord3D d;
  2558. d.x = epicenter->x - viewPos->x;
  2559. d.y = epicenter->y - viewPos->y;
  2560. /// @todo make this 3D once we have the real "lookat" spot
  2561. //d.z = epicenter->z - viewPos->z;
  2562. Real dist = (Real)sqrt( d.x*d.x + d.y*d.y );
  2563. if (dist > TheGlobalData->m_maxShakeRange)
  2564. return;
  2565. intensity *= 1.0f - (dist/TheGlobalData->m_maxShakeRange);
  2566. // add intensity and clamp
  2567. m_shakeIntensity += intensity;
  2568. //const Real maxIntensity = 10.0f;
  2569. const Real maxIntensity = 3.0f;
  2570. if (m_shakeIntensity > TheGlobalData->m_maxShakeIntensity)
  2571. m_shakeIntensity = maxIntensity;
  2572. }
  2573. //-------------------------------------------------------------------------------------------------
  2574. /** Transformt he screen pixel coord passed in, to a world coordinate at the specified
  2575. * z value */
  2576. //-------------------------------------------------------------------------------------------------
  2577. void W3DView::screenToWorldAtZ( const ICoord2D *s, Coord3D *w, Real z )
  2578. {
  2579. Vector3 rayStart, rayEnd;
  2580. getPickRay( s, &rayStart, &rayEnd );
  2581. w->x = Vector3::Find_X_At_Z( z, rayStart, rayEnd );
  2582. w->y = Vector3::Find_Y_At_Z( z, rayStart, rayEnd );
  2583. w->z = z;
  2584. } // end screenToWorldAtZ