WorldHeightMap.cpp 79 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578
  1. /*
  2. ** Command & Conquer Generals Zero Hour(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. // WorldHeightMap.cpp
  24. // Class to encapsulate height map.
  25. // Author: John Ahlquist, April 2001
  26. #define INSTANTIATE_WELL_KNOWN_KEYS
  27. #include "windows.h"
  28. #include "stdlib.h"
  29. #include <string.h>
  30. #include "Common/STLTypedefs.h"
  31. #include "Common/DataChunk.h"
  32. //#include "Common/GameFileSystem.h"
  33. #include "Common/FileSystem.h" // for LOAD_TEST_ASSETS
  34. #include "Common/GlobalData.h"
  35. #include "Common/MapReaderWriterInfo.h"
  36. #include "Common/TerrainTypes.h"
  37. #include "Common/ThingFactory.h"
  38. #include "Common/ThingTemplate.h"
  39. #include "Common/WellKnownKeys.h"
  40. #include "GameLogic/PolygonTrigger.h"
  41. #include "GameLogic/SidesList.h"
  42. #include "W3DDevice/GameClient/WorldHeightMap.h"
  43. #include "W3DDevice/GameClient/TileData.h"
  44. #include "W3DDevice/GameClient/HeightMap.h"
  45. #include "W3DDevice/GameClient/TerrainTex.h"
  46. #include "W3DDevice/GameClient/W3DShadow.h"
  47. #include "Common/file.h"
  48. #ifdef _INTERNAL
  49. // for occasional debugging...
  50. //#pragma optimize("", off)
  51. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  52. #endif
  53. #define K_OBSOLETE_HEIGHT_MAP_VERSION 8
  54. #define PATHFIND_CLIFF_SLOPE_LIMIT_F 9.8f
  55. // -----------------------------------------------------------
  56. static AsciiString validateName(AsciiString n, Int flags)
  57. {
  58. return n;
  59. }
  60. /* ********* GDIFileStream class ****************************/
  61. class GDIFileStream : public InputStream
  62. {
  63. protected:
  64. File* m_file;
  65. public:
  66. GDIFileStream(File* pFile):m_file(pFile) {};
  67. virtual Int read(void *pData, Int numBytes) {
  68. return(m_file->read(pData, numBytes));
  69. };
  70. };
  71. /* ********* MapObject class ****************************/
  72. /*static*/ MapObject *MapObject::TheMapObjectListPtr = NULL;
  73. /*static*/ Dict MapObject::TheWorldDict;
  74. MapObject::MapObject(Coord3D loc, AsciiString name, Real angle, Int flags, const Dict* props,
  75. const ThingTemplate *thingTemplate )
  76. {
  77. m_objectName = validateName( name, flags );
  78. m_thingTemplate = thingTemplate;
  79. m_nextMapObject = NULL;
  80. m_location = loc;
  81. m_angle = normalizeAngle(angle);
  82. m_color = (0xff)<<8; // Bright green.
  83. m_flags = flags;
  84. m_renderObj = NULL;
  85. m_shadowObj = NULL;
  86. m_runtimeFlags = 0;
  87. // Note - do NOT set TheKey_objectSelectable on creation - allow it to follow the .ini value unless specified by user action. jba. [3/20/2003]
  88. if (props)
  89. {
  90. m_properties = *props;
  91. }
  92. else
  93. {
  94. m_properties.setInt(TheKey_objectInitialHealth, 100);
  95. m_properties.setBool(TheKey_objectEnabled, true);
  96. m_properties.setBool(TheKey_objectIndestructible, false);
  97. m_properties.setBool(TheKey_objectUnsellable, false);
  98. m_properties.setBool(TheKey_objectPowered, true);
  99. m_properties.setBool(TheKey_objectRecruitableAI, true);
  100. m_properties.setBool(TheKey_objectTargetable, false );
  101. }
  102. for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i )
  103. setBridgeRenderObject( (BridgeTowerType)i, NULL );
  104. }
  105. MapObject::~MapObject(void)
  106. {
  107. setRenderObj(NULL);
  108. setShadowObj(NULL);
  109. if (m_nextMapObject) {
  110. MapObject *cur = m_nextMapObject;
  111. MapObject *next;
  112. while (cur) {
  113. next = cur->getNext();
  114. cur->setNextMap(NULL); // prevents recursion.
  115. cur->deleteInstance();
  116. cur = next;
  117. }
  118. }
  119. for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i )
  120. setBridgeRenderObject( (BridgeTowerType)i, NULL );
  121. }
  122. MapObject *MapObject::duplicate(void)
  123. {
  124. MapObject *pObj = newInstance( MapObject)(m_location, m_objectName, m_angle, m_flags, &m_properties, m_thingTemplate);
  125. pObj->setColor(getColor());
  126. pObj->m_runtimeFlags = m_runtimeFlags;
  127. return pObj;
  128. }
  129. void MapObject::setRenderObj(RenderObjClass *pObj)
  130. {
  131. REF_PTR_SET(m_renderObj, pObj);
  132. }
  133. void MapObject::setBridgeRenderObject( BridgeTowerType type, RenderObjClass* renderObj )
  134. {
  135. if( type >= 0 && type < BRIDGE_MAX_TOWERS )
  136. REF_PTR_SET( m_bridgeTowers[ type ], renderObj );
  137. }
  138. RenderObjClass* MapObject::getBridgeRenderObject( BridgeTowerType type )
  139. {
  140. if( type >= 0 && type < BRIDGE_MAX_TOWERS )
  141. return m_bridgeTowers[ type ];
  142. return NULL;
  143. }
  144. void MapObject::validate(void)
  145. {
  146. verifyValidTeam();
  147. verifyValidUniqueID();
  148. }
  149. void MapObject::verifyValidTeam(void)
  150. {
  151. // if this map object has a valid team, then do nothing.
  152. // if it has an invalid team, the place it on the default neutral team, (by clearing the
  153. // existing team name.)
  154. Bool exists;
  155. AsciiString teamName = getProperties()->getAsciiString(TheKey_originalOwner, &exists);
  156. if (exists) {
  157. Bool valid = false;
  158. int numSides = TheSidesList->getNumTeams();
  159. for (int i = 0; i < numSides; ++i) {
  160. TeamsInfo *teamInfo = TheSidesList->getTeamInfo(i);
  161. if (!teamInfo) {
  162. continue;
  163. }
  164. Bool itBetter;
  165. AsciiString testAgainstTeamName = teamInfo->getDict()->getAsciiString(TheKey_teamName, &itBetter);
  166. if (itBetter) {
  167. if (testAgainstTeamName.compare(teamName) == 0) {
  168. valid = true;
  169. }
  170. }
  171. }
  172. if (!valid) {
  173. getProperties()->remove(TheKey_originalOwner);
  174. }
  175. }
  176. }
  177. void MapObject::verifyValidUniqueID(void)
  178. {
  179. Bool exists;
  180. AsciiString uniqueID = getProperties()->getAsciiString(TheKey_uniqueID, &exists);
  181. MapObject *obj = MapObject::getFirstMapObject();
  182. // -1 is the sentinel
  183. int highestIndex = -1;
  184. while (obj) {
  185. if (obj == this) {
  186. // the first object is THIS OBJECT, cause we've already been added.
  187. obj = obj->getNext();
  188. continue;
  189. }
  190. if (obj->isWaypoint()) {
  191. // waypoints throw this off. Sad but true. :-(
  192. obj = obj->getNext();
  193. continue;
  194. }
  195. Bool iterateExists;
  196. AsciiString tempStr = obj->getProperties()->getAsciiString(TheKey_uniqueID, &iterateExists);
  197. const char* lastSpace = tempStr.reverseFind(' ');
  198. int testIndex = -1;
  199. if (lastSpace) {
  200. testIndex = atoi(lastSpace);
  201. }
  202. if (testIndex > highestIndex) {
  203. highestIndex = testIndex;
  204. }
  205. break;
  206. }
  207. int indexOfThisObject = highestIndex + 1;
  208. const char* thingName;
  209. if (getThingTemplate()) {
  210. thingName = getThingTemplate()->getName().str();
  211. } else if (isWaypoint()) {
  212. thingName = getWaypointName().str();
  213. } else {
  214. thingName = getName().str();
  215. }
  216. const char* pName = thingName;
  217. while (*thingName) {
  218. if ((*thingName) == '/') {
  219. pName = thingName + 1;
  220. }
  221. ++thingName;
  222. }
  223. AsciiString newID;
  224. if (isWaypoint()) {
  225. newID.format("%s", pName);
  226. } else {
  227. newID.format("%s %d", pName, indexOfThisObject);
  228. }
  229. getProperties()->setAsciiString(TheKey_uniqueID, newID);
  230. }
  231. void MapObject::fastAssignAllUniqueIDs(void)
  232. {
  233. // here's what we do. Take all of them, push them onto a stack. Then, pop each one, setting its id.
  234. // should be much faster than what we currently do.
  235. MapObject *pMapObj = getFirstMapObject();
  236. std::stack<MapObject*> objStack;
  237. Int actualNumObjects = 0;
  238. while (pMapObj) {
  239. ++actualNumObjects;
  240. objStack.push(pMapObj);
  241. pMapObj = pMapObj->getNext();
  242. }
  243. Int indexOfThisObject = 0;
  244. while (actualNumObjects) {
  245. MapObject *obj = objStack.top();
  246. const char* thingName;
  247. if (obj->getThingTemplate()) {
  248. thingName = obj->getThingTemplate()->getName().str();
  249. } else if (obj->isWaypoint()) {
  250. thingName = obj->getWaypointName().str();
  251. } else {
  252. thingName = obj->getName().str();
  253. }
  254. const char* pName = thingName;
  255. while (*thingName) {
  256. if ((*thingName) == '/') {
  257. pName = thingName + 1;
  258. }
  259. ++thingName;
  260. }
  261. AsciiString newID;
  262. if (obj->isWaypoint()) {
  263. newID.format("%s", pName);
  264. } else {
  265. newID.format("%s %d", pName, indexOfThisObject);
  266. }
  267. obj->getProperties()->setAsciiString(TheKey_uniqueID, newID);
  268. objStack.pop();
  269. ++indexOfThisObject;
  270. --actualNumObjects;
  271. }
  272. }
  273. void MapObject::setThingTemplate(const ThingTemplate *thing)
  274. {
  275. m_thingTemplate = thing;
  276. m_objectName = thing->getName();
  277. }
  278. void MapObject::setName(AsciiString name)
  279. {
  280. m_objectName = name;
  281. }
  282. WaypointID MapObject::getWaypointID() { return (WaypointID)getProperties()->getInt(TheKey_waypointID); }
  283. AsciiString MapObject::getWaypointName() { return getProperties()->getAsciiString(TheKey_waypointName); }
  284. void MapObject::setWaypointID(Int i) { getProperties()->setInt(TheKey_waypointID, i); }
  285. void MapObject::setWaypointName(AsciiString n) { getProperties()->setAsciiString(TheKey_waypointName, n); }
  286. /*static */ Int MapObject::countMapObjectsWithOwner(const AsciiString& n)
  287. {
  288. Int count = 0;
  289. for (MapObject *pMapObj = MapObject::getFirstMapObject(); pMapObj; pMapObj = pMapObj->getNext())
  290. {
  291. if (pMapObj->getProperties()->getAsciiString(TheKey_originalOwner) == n)
  292. ++count;
  293. }
  294. return count;
  295. }
  296. //-------------------------------------------------------------------------------------------------
  297. const ThingTemplate *MapObject::getThingTemplate( void ) const
  298. {
  299. if (m_thingTemplate)
  300. return (const ThingTemplate*) m_thingTemplate->getFinalOverride();
  301. return NULL;
  302. }
  303. /* ********* WorldHeightMap class ****************************/
  304. TileData *WorldHeightMap::m_alphaTiles[NUM_ALPHA_TILES]={0,0,0,0,0,0,0,0,0,0,0,0};
  305. //
  306. // WorldHeightMap destructor .
  307. //
  308. WorldHeightMap::~WorldHeightMap(void)
  309. {
  310. if (m_data) {
  311. delete(m_data);
  312. m_data = NULL;
  313. }
  314. if (m_tileNdxes) {
  315. delete(m_tileNdxes);
  316. m_tileNdxes = NULL;
  317. }
  318. if (m_blendTileNdxes) {
  319. delete(m_blendTileNdxes);
  320. m_blendTileNdxes = NULL;
  321. }
  322. if (m_extraBlendTileNdxes) {
  323. delete(m_extraBlendTileNdxes);
  324. m_extraBlendTileNdxes = NULL;
  325. }
  326. if (m_cliffInfoNdxes) {
  327. delete(m_cliffInfoNdxes);
  328. m_cliffInfoNdxes = NULL;
  329. }
  330. if (m_cellFlipState)
  331. { delete (m_cellFlipState);
  332. m_cellFlipState = NULL;
  333. }
  334. if (m_seismicUpdateFlag)
  335. { delete (m_seismicUpdateFlag);
  336. m_seismicUpdateFlag = NULL;
  337. }
  338. if (m_seismicZVelocities)
  339. { delete (m_seismicZVelocities);
  340. m_seismicZVelocities = NULL;
  341. }
  342. if (m_cellCliffState)
  343. { delete (m_cellCliffState);
  344. m_cellCliffState = NULL;
  345. }
  346. int i;
  347. for (i=0; i<NUM_SOURCE_TILES; i++) {
  348. REF_PTR_RELEASE(m_sourceTiles[i]);
  349. REF_PTR_RELEASE(m_edgeTiles[i]);
  350. }
  351. for (i=0; i<NUM_ALPHA_TILES; i++) {
  352. REF_PTR_RELEASE(m_alphaTiles[i]);
  353. }
  354. REF_PTR_RELEASE(m_terrainTex);
  355. REF_PTR_RELEASE(m_alphaTerrainTex);
  356. REF_PTR_RELEASE(m_alphaEdgeTex);
  357. }
  358. void WorldHeightMap::freeListOfMapObjects(void)
  359. {
  360. if (MapObject::TheMapObjectListPtr)
  361. {
  362. MapObject::TheMapObjectListPtr->deleteInstance();
  363. MapObject::TheMapObjectListPtr = NULL;
  364. }
  365. MapObject::getWorldDict()->clear();
  366. }
  367. /**
  368. WorldHeightMap - create a new height map for class WorldHeightMap.
  369. Note that there is 1 m_numBlendedTiles, which is the implied
  370. transparent tile for non-blended tiles.
  371. */
  372. WorldHeightMap::WorldHeightMap():
  373. m_width(0), m_height(0), m_dataSize(0), m_data(NULL), m_cellFlipState(NULL), m_seismicUpdateFlag(NULL), m_seismicZVelocities(NULL),
  374. m_drawOriginX(0), m_drawOriginY(0),
  375. m_numTextureClasses(0),
  376. m_drawWidthX(NORMAL_DRAW_WIDTH), m_drawHeightY(NORMAL_DRAW_HEIGHT),
  377. m_tileNdxes(NULL), m_blendTileNdxes(NULL), m_extraBlendTileNdxes(NULL), m_cliffInfoNdxes(NULL),
  378. m_terrainTexHeight(1), m_alphaTexHeight(1), m_cellCliffState(NULL),
  379. #ifdef EVAL_TILING_MODES
  380. m_tileMode(TILE_4x4),
  381. #endif
  382. m_numCliffInfo(1),
  383. m_terrainTex(NULL), m_alphaTerrainTex(NULL), m_numBitmapTiles(0), m_numBlendedTiles(1)
  384. {
  385. Int i;
  386. for (i=0; i<NUM_SOURCE_TILES; i++) {
  387. m_sourceTiles[i] = NULL;
  388. m_edgeTiles[i] = NULL;
  389. }
  390. TheSidesList->validateSides();
  391. setupAlphaTiles();
  392. }
  393. #ifdef EVAL_TILING_MODES
  394. static Bool ParseFunkyTilingDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
  395. {
  396. WorldHeightMap *pThis = (WorldHeightMap *)userData;
  397. *((Int *)&pThis->m_tileMode) = file.readInt();
  398. return true;
  399. }
  400. #endif
  401. /**
  402. * WorldHeightMap - read a height map from a file.
  403. * Format is Chunky.
  404. *
  405. * Input: ChunkInputStream,
  406. *
  407. */
  408. WorldHeightMap::WorldHeightMap(ChunkInputStream *pStrm, Bool logicalDataOnly):
  409. m_width(0), m_height(0), m_dataSize(0), m_data(NULL), m_cellFlipState(NULL), m_seismicUpdateFlag(NULL), m_seismicZVelocities(NULL),
  410. m_drawOriginX(0), m_cellCliffState(NULL), m_drawOriginY(0),
  411. m_numTextureClasses(0),
  412. m_drawWidthX(NORMAL_DRAW_WIDTH), m_drawHeightY(NORMAL_DRAW_HEIGHT),
  413. m_tileNdxes(NULL), m_blendTileNdxes(NULL), m_extraBlendTileNdxes(NULL), m_cliffInfoNdxes(NULL),
  414. m_terrainTexHeight(1), m_alphaTexHeight(1),
  415. #ifdef EVAL_TILING_MODES
  416. m_tileMode(TILE_4x4),
  417. #endif
  418. m_numCliffInfo(1),
  419. m_terrainTex(NULL), m_alphaTerrainTex(NULL), m_numBitmapTiles(0), m_numBlendedTiles(1)
  420. {
  421. int i;
  422. for (i=0; i<NUM_SOURCE_TILES; i++) {
  423. m_sourceTiles[i]=NULL;
  424. m_edgeTiles[i]=NULL;
  425. }
  426. if (TheGlobalData && TheGlobalData->m_stretchTerrain) {
  427. m_drawWidthX=STRETCH_DRAW_WIDTH;
  428. m_drawHeightY=STRETCH_DRAW_HEIGHT;
  429. }
  430. DataChunkInput file( pStrm );
  431. if (logicalDataOnly) {
  432. file.registerParser( AsciiString("HeightMapData"), AsciiString::TheEmptyString, ParseSizeOnlyInChunk );
  433. file.registerParser( AsciiString("WorldInfo"), AsciiString::TheEmptyString, ParseWorldDictDataChunk );
  434. file.registerParser( AsciiString("ObjectsList"), AsciiString::TheEmptyString, ParseObjectsDataChunk );
  435. freeListOfMapObjects(); // just in case.
  436. file.registerParser( AsciiString("PolygonTriggers"), AsciiString::TheEmptyString, PolygonTrigger::ParsePolygonTriggersDataChunk );
  437. PolygonTrigger::deleteTriggers(); // just in case.
  438. TheSidesList->emptySides();
  439. file.registerParser(AsciiString("SidesList"), AsciiString::TheEmptyString, SidesList::ParseSidesDataChunk );
  440. } else {
  441. file.registerParser( AsciiString("HeightMapData"), AsciiString::TheEmptyString, ParseHeightMapDataChunk );
  442. file.registerParser( AsciiString("BlendTileData"), AsciiString::TheEmptyString, ParseBlendTileDataChunk );
  443. #ifdef EVAL_TILING_MODES
  444. file.registerParser( AsciiString("FUNKY_TILING"), AsciiString::TheEmptyString, ParseFunkyTilingDataChunk );
  445. #endif
  446. file.registerParser( AsciiString("GlobalLighting"), AsciiString::TheEmptyString, ParseLightingDataChunk );
  447. }
  448. if (!file.parse(this)) {
  449. throw(ERROR_CORRUPT_FILE_FORMAT);
  450. }
  451. // patch bad maps.
  452. if (!logicalDataOnly) {
  453. for(i=0; i<m_dataSize; i++) {
  454. if (m_cliffInfoNdxes[i]<0 || m_cliffInfoNdxes[i]>= m_numCliffInfo) {
  455. m_cliffInfoNdxes[i] = 0;
  456. }
  457. if (m_blendTileNdxes[i]<0 || m_blendTileNdxes[i]>= m_numBlendedTiles) {
  458. m_blendTileNdxes[i] = 0;
  459. }
  460. if (m_extraBlendTileNdxes[i]<0 || m_extraBlendTileNdxes[i]>= m_numBlendedTiles) {
  461. m_extraBlendTileNdxes[i] = 0;
  462. }
  463. }
  464. }
  465. if (TheGlobalData && TheGlobalData->m_drawEntireTerrain) {
  466. m_drawWidthX=m_width;
  467. m_drawHeightY=m_height;
  468. }
  469. if (m_drawWidthX > m_width) {
  470. m_drawWidthX = m_width;
  471. }
  472. if (m_drawHeightY > m_height) {
  473. m_drawHeightY = m_height;
  474. }
  475. TheSidesList->validateSides();
  476. setupAlphaTiles();
  477. }
  478. /** Optimized version of method to get triangle flip state of a terrain cell. Use this
  479. * instead of getAlphaUVData() whenever possible.
  480. */
  481. Bool WorldHeightMap::getFlipState(Int xIndex, Int yIndex) const
  482. {
  483. if (xIndex<0 || yIndex<0) return false;
  484. if (yIndex>=m_height) return false;
  485. if (xIndex>=m_width) return false;
  486. if (!m_cellFlipState) return false;
  487. return m_cellFlipState[yIndex*m_flipStateWidth + (xIndex >> 3)] & (1<<(xIndex&0x7));
  488. }
  489. /** Sets the value of the flip state bit.
  490. */
  491. void WorldHeightMap::setFlipState(Int xIndex, Int yIndex, Bool value)
  492. {
  493. if (xIndex<0 || yIndex<0) return ;
  494. if (yIndex>=m_height) return ;
  495. if (xIndex>=m_width) return ;
  496. if (!m_cellFlipState) return ;
  497. UnsignedByte *curVal = &m_cellFlipState[yIndex*m_flipStateWidth + (xIndex >> 3)];
  498. if (value) {
  499. *curVal |= (1<<(xIndex&0x7));
  500. } else {
  501. *curVal &= ~(1<<(xIndex&0x7));
  502. }
  503. }
  504. /** Clears all flip state bits.
  505. */
  506. void WorldHeightMap::clearFlipStates(void) {
  507. if (m_cellFlipState) {
  508. memset(m_cellFlipState,0,m_flipStateWidth*m_height); //clear all flags
  509. }
  510. }
  511. //////////////////////////////////////////////////////////////////////////////m_SeismicUpdateFlag
  512. Bool WorldHeightMap::getSeismicUpdateFlag(Int xIndex, Int yIndex) const
  513. {
  514. if (xIndex<0 || yIndex<0) return false;
  515. if (yIndex>=m_height) return false;
  516. if (xIndex>=m_width) return false;
  517. if (!m_seismicUpdateFlag) return false;
  518. return m_seismicUpdateFlag[yIndex*m_seismicUpdateWidth + (xIndex >> 3)] & (1<<(xIndex&0x7));
  519. }
  520. void WorldHeightMap::setSeismicUpdateFlag(Int xIndex, Int yIndex, Bool value)
  521. {
  522. if (xIndex<0 || yIndex<0) return ;
  523. if (yIndex>=m_height) return ;
  524. if (xIndex>=m_width) return ;
  525. if (!m_seismicUpdateFlag) return ;
  526. UnsignedByte *curVal = &m_seismicUpdateFlag[yIndex*m_seismicUpdateWidth + (xIndex >> 3)];
  527. if (value) {
  528. *curVal |= (1<<(xIndex&0x7));
  529. } else {
  530. *curVal &= ~(1<<(xIndex&0x7));
  531. }
  532. }
  533. void WorldHeightMap::clearSeismicUpdateFlags(void)
  534. {
  535. if (m_seismicUpdateFlag) {
  536. memset(m_seismicUpdateFlag,0,m_seismicUpdateWidth*m_height); //clear all flags
  537. }
  538. }
  539. ///////////////////////////////////////////////m_SeismicZVelocities
  540. Real WorldHeightMap::getSeismicZVelocity(Int xIndex, Int yIndex) const
  541. {
  542. if (xIndex<0 || yIndex<0) return false;
  543. if (yIndex>=m_height) return false;
  544. if (xIndex>=m_width) return false;
  545. if (!m_seismicZVelocities) return false;
  546. return m_seismicZVelocities[yIndex*m_width + xIndex];
  547. }
  548. void WorldHeightMap::setSeismicZVelocity(Int xIndex, Int yIndex, Real value)
  549. {
  550. if (xIndex<0 || yIndex<0) return ;
  551. if (yIndex>=m_height) return ;
  552. if (xIndex>=m_width) return ;
  553. if (!m_seismicZVelocities) return ;
  554. m_seismicZVelocities[yIndex*m_width + xIndex] = value;
  555. }
  556. void WorldHeightMap::fillSeismicZVelocities( Real value )
  557. {
  558. if (!m_seismicZVelocities) return ;
  559. for (Int idx = 0; idx < m_width*m_height; ++idx)
  560. m_seismicZVelocities[idx] = value;
  561. }
  562. Real WorldHeightMap::getBilinearSampleSeismicZVelocity( Int x, Int y)
  563. {
  564. if ( x < 0 || y < 0 ) return 0;
  565. if ( y >= m_height ) return 0;
  566. if ( x >= m_width ) return 0;
  567. if (!m_seismicZVelocities) return 0;
  568. Real collector = 0.0f;
  569. Real divisor = 0.0f;
  570. collector += m_seismicZVelocities[ y * m_width + x ];
  571. ++divisor;
  572. if ( y > 0 )
  573. {
  574. collector += m_seismicZVelocities[ (y-1) * m_width + x ];//bottom
  575. ++divisor;
  576. if( x > 0 )
  577. {
  578. collector += m_seismicZVelocities[ (y-1) * m_width + (x-1) ];//lower left
  579. ++divisor;
  580. }
  581. if ( x < m_width-1 )
  582. {
  583. collector += m_seismicZVelocities[ (y-1) * m_width + (x+1) ];//lower right
  584. ++divisor;
  585. }
  586. }
  587. if ( y < m_height-1 )
  588. {
  589. collector += m_seismicZVelocities[ (y+1) * m_width + x ];//top
  590. ++divisor;
  591. if( x > 0 )
  592. {
  593. collector += m_seismicZVelocities[ (y+1) * m_width + (x-1) ];//upper left
  594. ++divisor;
  595. }
  596. if ( x < m_width-1 )
  597. {
  598. collector += m_seismicZVelocities[ (y+1) * m_width + (x+1) ];//upper right
  599. ++divisor;
  600. }
  601. }
  602. if( x > 0 )
  603. {
  604. collector += m_seismicZVelocities[ y * m_width + (x-1) ];//left
  605. ++divisor;
  606. }
  607. if ( x < m_width-1 )
  608. {
  609. collector += m_seismicZVelocities[ y * m_width + (x+1) ];//right
  610. ++divisor;
  611. }
  612. collector /= divisor;
  613. return collector;
  614. }
  615. /** Get whether the cell is a cliff cell (impassable to ground vehicles).
  616. */
  617. Bool WorldHeightMap::getCliffState(Int xIndex, Int yIndex) const
  618. {
  619. if (xIndex<0 || yIndex<0) return false;
  620. if (yIndex>=m_height) return false;
  621. if (xIndex>=m_width) return false;
  622. if (!m_cellCliffState) return false;
  623. return m_cellCliffState[yIndex*m_flipStateWidth + (xIndex >> 3)] & (1<<(xIndex&0x7));
  624. }
  625. //=============================================================================
  626. // setCliffState
  627. //=============================================================================
  628. /** Sets the cliff state for a given cell. */
  629. //=============================================================================
  630. void WorldHeightMap::setCliffState(Int xIndex, Int yIndex, Bool state)
  631. {
  632. if (xIndex<0 || yIndex<0) return;
  633. if (yIndex>=m_height) return;
  634. if (xIndex>=m_width) return;
  635. if (!m_cellCliffState) return;
  636. UnsignedByte flagByte = m_cellCliffState[yIndex*m_flipStateWidth + (xIndex >> 3)];
  637. UnsignedByte flagMask = (1<<(xIndex&0x7));
  638. if (state) {
  639. flagByte |= flagMask;
  640. } else {
  641. flagByte &= (~flagMask);
  642. }
  643. m_cellCliffState[yIndex*m_flipStateWidth + (xIndex >> 3)] = flagByte;
  644. }
  645. Bool WorldHeightMap::ParseWorldDictDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
  646. {
  647. Dict d = file.readDict();
  648. *MapObject::getWorldDict() = d;
  649. Bool exists;
  650. Int theWeather = MapObject::getWorldDict()->getInt(TheKey_weather, &exists);
  651. if (exists) {
  652. TheWritableGlobalData->m_weather = (Weather) theWeather;
  653. }
  654. return true;
  655. }
  656. /**
  657. * WorldHeightMap::ParseLightingDataChunk - read a global lights chunk.
  658. * Format is the newer CHUNKY format.
  659. * See WHeightMapEdit.cpp for the writer.
  660. * Input: DataChunkInput
  661. *
  662. */
  663. Bool WorldHeightMap::ParseLightingDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
  664. {
  665. TheWritableGlobalData->m_timeOfDay = (TimeOfDay)file.readInt();
  666. Int i;
  667. GlobalData::TerrainLighting initLightValues = { { 0,0,0},{0,0,0},{0,0,-1.0f}};
  668. // initialize the directions of the lights to not be totally invalid, in case old maps are read
  669. for (i=0; i<4; i++) {
  670. for (Int j=0;j<MAX_GLOBAL_LIGHTS; j++) {
  671. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][j]=initLightValues;
  672. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][j]=initLightValues;
  673. }
  674. }
  675. for (i=0; i<4; i++) {
  676. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][0].ambient.red = file.readReal();
  677. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][0].ambient.green = file.readReal();
  678. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][0].ambient.blue = file.readReal();
  679. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][0].diffuse.red = file.readReal();
  680. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][0].diffuse.green = file.readReal();
  681. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][0].diffuse.blue = file.readReal();
  682. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][0].lightPos.x = file.readReal();
  683. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][0].lightPos.y = file.readReal();
  684. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][0].lightPos.z = file.readReal();
  685. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][0].ambient.red = file.readReal();
  686. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][0].ambient.green = file.readReal();
  687. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][0].ambient.blue = file.readReal();
  688. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][0].diffuse.red = file.readReal();
  689. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][0].diffuse.green = file.readReal();
  690. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][0].diffuse.blue = file.readReal();
  691. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][0].lightPos.x = file.readReal();
  692. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][0].lightPos.y = file.readReal();
  693. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][0].lightPos.z = file.readReal();
  694. if (info->version >= K_LIGHTING_VERSION_2) {
  695. for (Int j=1; j<3; j++) //added support for 2 extra object lights
  696. {
  697. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][j].ambient.red = file.readReal();
  698. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][j].ambient.green = file.readReal();
  699. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][j].ambient.blue = file.readReal();
  700. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][j].diffuse.red = file.readReal();
  701. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][j].diffuse.green = file.readReal();
  702. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][j].diffuse.blue = file.readReal();
  703. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][j].lightPos.x = file.readReal();
  704. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][j].lightPos.y = file.readReal();
  705. TheWritableGlobalData->m_terrainObjectsLighting[i+TIME_OF_DAY_FIRST][j].lightPos.z = file.readReal();
  706. }
  707. }
  708. if (info->version >= K_LIGHTING_VERSION_3) {
  709. for (Int j=1; j<3; j++) //added support for 2 extra terrain lights
  710. {
  711. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][j].ambient.red = file.readReal();
  712. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][j].ambient.green = file.readReal();
  713. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][j].ambient.blue = file.readReal();
  714. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][j].diffuse.red = file.readReal();
  715. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][j].diffuse.green = file.readReal();
  716. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][j].diffuse.blue = file.readReal();
  717. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][j].lightPos.x = file.readReal();
  718. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][j].lightPos.y = file.readReal();
  719. TheWritableGlobalData->m_terrainLighting[i+TIME_OF_DAY_FIRST][j].lightPos.z = file.readReal();
  720. }
  721. }
  722. }
  723. if (!file.atEndOfChunk()) {
  724. UnsignedInt shadowColor = file.readInt();
  725. if (TheW3DShadowManager) {
  726. TheW3DShadowManager->setShadowColor(shadowColor);
  727. }
  728. }
  729. DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Unexpected data left over."));
  730. return true;
  731. }
  732. /**
  733. * WorldHeightMap::ParseObjectsDataChunk - read a height map chunk.
  734. * Format is the newer CHUNKY format.
  735. * See WHeightMapEdit.cpp for the writer.
  736. * Input: DataChunkInput
  737. *
  738. */
  739. Bool WorldHeightMap::ParseObjectsDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
  740. {
  741. file.m_currentObject = NULL;
  742. file.registerParser( AsciiString("Object"), info->label, ParseObjectDataChunk );
  743. return (file.parse(userData));
  744. }
  745. /**
  746. * WorldHeightMap::ParseHeightMapData - read a height map chunk.
  747. * Format is the newer CHUNKY format.
  748. * See WHeightMapEdit.cpp for the writer.
  749. * Input: DataChunkInput
  750. *
  751. */
  752. Bool WorldHeightMap::ParseHeightMapDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
  753. {
  754. WorldHeightMap *pThis = (WorldHeightMap *)userData;
  755. return pThis->ParseHeightMapData(file, info, userData);
  756. }
  757. /**
  758. * WorldHeightMap::ParseHeightMapData - read a height map chunk.
  759. * Format is the newer CHUNKY format.
  760. * See WHeightMapEdit.cpp for the writer.
  761. * Input: DataChunkInput
  762. *
  763. */
  764. Bool WorldHeightMap::ParseHeightMapData(DataChunkInput &file, DataChunkInfo *info, void *userData)
  765. {
  766. m_width = file.readInt();
  767. m_height = file.readInt();
  768. if (info->version >= K_HEIGHT_MAP_VERSION_3) {
  769. m_borderSize = file.readInt();
  770. } else {
  771. m_borderSize = 0;
  772. }
  773. if (info->version >= K_HEIGHT_MAP_VERSION_4) {
  774. Int numBorders = file.readInt();
  775. m_boundaries.resize(numBorders);
  776. for (int i = 0; i < numBorders; ++i) {
  777. m_boundaries[i].x = file.readInt();
  778. m_boundaries[i].y = file.readInt();
  779. }
  780. } else {
  781. m_boundaries.resize(1);
  782. m_boundaries[0].x = m_width - 2 * m_borderSize;
  783. m_boundaries[0].y = m_height - 2 * m_borderSize;
  784. }
  785. m_dataSize = file.readInt();
  786. m_data = MSGNEW("WorldHeightMap_ParseHeightMapData") UnsignedByte[m_dataSize];
  787. if (m_dataSize <= 0 || (m_dataSize != (m_width*m_height))) {
  788. throw ERROR_CORRUPT_FILE_FORMAT ;
  789. }
  790. Int numBytesX = (m_width+7)/8; //how many bytes to fit all bitflags
  791. Int numBytesY = m_height;
  792. m_seismicUpdateWidth=numBytesX;
  793. m_seismicUpdateFlag = MSGNEW("WorldHeightMap::ParseHeightMapData _ m_seismicUpdateFlag allocated") UnsignedByte[numBytesX*numBytesY];
  794. clearSeismicUpdateFlags();
  795. m_seismicZVelocities = MSGNEW("WorldHeightMap_ParseHeightMapData _ zvelocities allocated") Real[m_dataSize];
  796. fillSeismicZVelocities( 0 );
  797. file.readArrayOfBytes((char *)m_data, m_dataSize);
  798. // Resize me.
  799. if (info->version == K_HEIGHT_MAP_VERSION_1) {
  800. Int newWidth = (m_width+1)/2;
  801. Int newHeight = (m_height+1)/2;
  802. Int i, j;
  803. for (i=0; i<newHeight; i++) {
  804. for (j=0; j<newWidth; j++) {
  805. m_data[i*newWidth+j] = m_data[2*i*m_width+2*j];
  806. }
  807. }
  808. }
  809. DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Unexpected data left over."));
  810. return true;
  811. }
  812. /**
  813. * WorldHeightMap::ParseHeightMapData - read a height map chunk.
  814. * Format is the newer CHUNKY format.
  815. * See WHeightMapEdit.cpp for the writer.
  816. * Input: DataChunkInput
  817. *
  818. */
  819. Bool WorldHeightMap::ParseSizeOnlyInChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
  820. {
  821. WorldHeightMap *pThis = (WorldHeightMap *)userData;
  822. return pThis->ParseSizeOnly(file, info, userData);
  823. }
  824. /**
  825. * WorldHeightMap::ParseHeightMapData - read a height map chunk.
  826. * Format is the newer CHUNKY format.
  827. * See WHeightMapEdit.cpp for the writer.
  828. * Input: DataChunkInput
  829. *
  830. */
  831. Bool WorldHeightMap::ParseSizeOnly(DataChunkInput &file, DataChunkInfo *info, void *userData)
  832. {
  833. m_width = file.readInt();
  834. m_height = file.readInt();
  835. if (info->version >= K_HEIGHT_MAP_VERSION_3) {
  836. m_borderSize = file.readInt();
  837. } else {
  838. m_borderSize = 0;
  839. }
  840. if (info->version >= K_HEIGHT_MAP_VERSION_4) {
  841. Int numBorders = file.readInt();
  842. m_boundaries.resize(numBorders);
  843. for (int i = 0; i < numBorders; ++i) {
  844. m_boundaries[i].x = file.readInt();
  845. m_boundaries[i].y = file.readInt();
  846. }
  847. } else {
  848. m_boundaries.resize(1);
  849. m_boundaries[0].x = m_width - 2 * m_borderSize;
  850. m_boundaries[0].y = m_height - 2 * m_borderSize;
  851. }
  852. m_dataSize = file.readInt();
  853. m_data = MSGNEW("WorldHeightMap_ParseSizeOnly") UnsignedByte[m_dataSize];
  854. if (m_dataSize <= 0 || (m_dataSize != (m_width*m_height))) {
  855. throw ERROR_CORRUPT_FILE_FORMAT ;
  856. }
  857. file.readArrayOfBytes((char *)m_data, m_dataSize);
  858. // Resize me.
  859. if (info->version == K_HEIGHT_MAP_VERSION_1) {
  860. Int newWidth = (m_width+1)/2;
  861. Int newHeight = (m_height+1)/2;
  862. Int i, j;
  863. for (i=0; i<newHeight; i++) {
  864. for (j=0; j<newWidth; j++) {
  865. m_data[i*newWidth+j] = m_data[2*i*m_width+2*j];
  866. }
  867. }
  868. m_width = newWidth;
  869. m_height = newHeight;
  870. }
  871. return true;
  872. }
  873. /**
  874. * WorldHeightMap::ParseBlendTileDataChunk - read a blend tile info chunk.
  875. * Format is the newer CHUNKY format.
  876. * See WHeightMapEdit.cpp for the writer.
  877. * Input: DataChunkInput
  878. *
  879. */
  880. Bool WorldHeightMap::ParseBlendTileDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
  881. {
  882. WorldHeightMap *pThis = (WorldHeightMap *)userData;
  883. return pThis->ParseBlendTileData(file, info, userData);
  884. }
  885. /** Function to read in the tiles for a texture class. */
  886. void WorldHeightMap::readTexClass(TXTextureClass *texClass, TileData **tileData)
  887. {
  888. char path[_MAX_PATH];
  889. path[0] = 0;
  890. File *theFile = NULL;
  891. // get the file from the description in TheTerrainTypes
  892. TerrainType *terrain = TheTerrainTypes->findTerrain( texClass->name );
  893. char texturePath[ _MAX_PATH ];
  894. if (terrain==NULL)
  895. {
  896. #ifdef LOAD_TEST_ASSETS
  897. theFile = TheFileSystem->openFile( texClass->name.str(), File::READ|File::BINARY);
  898. #endif
  899. }
  900. else
  901. {
  902. sprintf( texturePath, "%s%s", TERRAIN_TGA_DIR_PATH, terrain->getTexture().str() );
  903. theFile = TheFileSystem->openFile( texturePath, File::READ|File::BINARY);
  904. }
  905. if (theFile != NULL) {
  906. GDIFileStream theStream(theFile);
  907. InputStream *pStr = &theStream;
  908. Int numTiles = WorldHeightMap::countTiles(pStr);
  909. theFile->seek(0, File::START);
  910. if (numTiles >= texClass->numTiles) {
  911. numTiles = texClass->numTiles;
  912. Int width;
  913. for (width = 10; width >= 1; width--) {
  914. if (numTiles >= width*width) {
  915. numTiles = width*width;
  916. break;
  917. }
  918. }
  919. WorldHeightMap::readTiles(pStr, tileData+texClass->firstTile, width);
  920. }
  921. theFile->close();
  922. }
  923. }
  924. /**
  925. * WorldHeightMap::ParseBlendTileData - read a blend tile info chunk.
  926. * Format is the newer CHUNKY format.
  927. * See WHeightMapEdit.cpp for the writer.
  928. * Input: DataChunkInput
  929. *
  930. */
  931. Bool WorldHeightMap::ParseBlendTileData(DataChunkInput &file, DataChunkInfo *info, void *userData)
  932. {
  933. int i, j;
  934. Int len = file.readInt();
  935. if (m_dataSize != len) {
  936. throw ERROR_CORRUPT_FILE_FORMAT ;
  937. }
  938. m_tileNdxes = MSGNEW("WorldHeightMap_ParseBlendTileData") Short[m_dataSize];
  939. m_cliffInfoNdxes = MSGNEW("WorldHeightMap_ParseBlendTileData") Short[m_dataSize];
  940. m_blendTileNdxes = MSGNEW("WorldHeightMap_ParseBlendTileData") Short[m_dataSize];
  941. m_extraBlendTileNdxes = MSGNEW("WorldHeightMap_ParseBlendTileData") Short[m_dataSize];
  942. // Note - we have one less cell than the width & height. But for paranoia, allocate
  943. // extra row. jba.
  944. //
  945. Int numBytesX = (m_width+7)/8; //how many bytes to fit all bitflags
  946. Int numBytesY = m_height;
  947. m_flipStateWidth=numBytesX;
  948. m_cellFlipState = MSGNEW("WorldHeightMap_getTerrainTexture") UnsignedByte[numBytesX*numBytesY];
  949. m_cellCliffState = MSGNEW("WorldHeightMap_getTerrainTexture") UnsignedByte[numBytesX*numBytesY];
  950. memset(m_cellFlipState,0,numBytesX*numBytesY); //clear all flags
  951. memset(m_cellCliffState,0,numBytesX*numBytesY); //clear all flags
  952. file.readArrayOfBytes((char*)m_tileNdxes, m_dataSize*sizeof(Short));
  953. file.readArrayOfBytes((char*)m_blendTileNdxes, m_dataSize*sizeof(Short));
  954. if (info->version >= K_BLEND_TILE_VERSION_6) {
  955. file.readArrayOfBytes((char*)m_extraBlendTileNdxes, m_dataSize*sizeof(Short));
  956. //Allow clearing of extra blend tiles via ini and resaving of map.
  957. //Useful for flushing out initial maps made with buggy 3-way blending.
  958. if (!TheGlobalData->m_use3WayTerrainBlends)
  959. memset(m_extraBlendTileNdxes,0,m_dataSize*sizeof(Short));
  960. }
  961. if (info->version >= K_BLEND_TILE_VERSION_5) {
  962. file.readArrayOfBytes((char*)m_cliffInfoNdxes, m_dataSize*sizeof(Short));
  963. }
  964. if (info->version >= K_BLEND_TILE_VERSION_7) {
  965. if (info->version==K_BLEND_TILE_VERSION_7) {
  966. Int byteWidth = (m_width+1)/8; // previous incorrect length that got used to save the file. jba. [4/3/2003]
  967. UnsignedByte *data = new UnsignedByte[m_height*byteWidth];
  968. file.readArrayOfBytes((char*)data, m_height*byteWidth);
  969. for (j=0; j<m_height; j++) {
  970. for (i=0; i<byteWidth; i++) {
  971. m_cellCliffState[j*m_flipStateWidth + i] = data[j*byteWidth + i];
  972. }
  973. }
  974. } else {
  975. file.readArrayOfBytes((char*)m_cellCliffState, m_height*m_flipStateWidth);
  976. }
  977. } else {
  978. initCliffFlagsFromHeights();
  979. }
  980. m_numBitmapTiles = file.readInt();
  981. DEBUG_ASSERTCRASH(m_numBitmapTiles>0 && m_numBitmapTiles<2048, ("Unlikely numBitmapTiles."));
  982. m_numBlendedTiles = file.readInt();
  983. DEBUG_ASSERTCRASH(m_numBlendedTiles>0 && m_numBlendedTiles<NUM_BLEND_TILES+1, ("Unlikely numBlendedTiles."));
  984. if (info->version >= K_BLEND_TILE_VERSION_5) {
  985. m_numCliffInfo = file.readInt();
  986. } else {
  987. m_numCliffInfo = 1; // cliffInfo[0] is the default info.
  988. }
  989. // --> file loading here
  990. m_numTextureClasses = file.readInt();
  991. DEBUG_ASSERTCRASH(m_numTextureClasses>0 && m_numTextureClasses<200, ("Unlikely m_numTextureClasses."));
  992. for (i=0; i<m_numTextureClasses; i++) {
  993. m_textureClasses[i].globalTextureClass = -1;
  994. m_textureClasses[i].firstTile = file.readInt();
  995. m_textureClasses[i].numTiles = file.readInt();
  996. m_textureClasses[i].width = file.readInt();
  997. // legacy GDF data
  998. // used to read "m_textureClasses[i].isGDF = file.readInt();"
  999. /* Int legacy = */ file.readInt();
  1000. m_textureClasses[i].name = file.readAsciiString();
  1001. readTexClass(&m_textureClasses[i], m_sourceTiles);
  1002. }
  1003. m_numEdgeTextureClasses = 0;
  1004. m_numEdgeTiles = 0;
  1005. if (info->version >= K_BLEND_TILE_VERSION_4) {
  1006. m_numEdgeTiles = file.readInt();
  1007. m_numEdgeTextureClasses = file.readInt();
  1008. for (i=0; i<m_numEdgeTextureClasses; i++) {
  1009. m_edgeTextureClasses[i].globalTextureClass = -1;
  1010. m_edgeTextureClasses[i].firstTile = file.readInt();
  1011. m_edgeTextureClasses[i].numTiles = file.readInt();
  1012. m_edgeTextureClasses[i].width = file.readInt();
  1013. m_edgeTextureClasses[i].name = file.readAsciiString();
  1014. readTexClass(&m_edgeTextureClasses[i], m_edgeTiles);
  1015. }
  1016. }
  1017. for (i=1; i<m_numBlendedTiles; i++) {
  1018. Int flag;
  1019. m_blendedTiles[i].blendNdx = file.readInt();
  1020. m_blendedTiles[i].horiz = file.readByte();
  1021. m_blendedTiles[i].vert = file.readByte();
  1022. m_blendedTiles[i].rightDiagonal = file.readByte();
  1023. m_blendedTiles[i].leftDiagonal = file.readByte();
  1024. m_blendedTiles[i].inverted = file.readByte();
  1025. //Allow clearing of extra blend tiles via ini and resaving of map.
  1026. //Useful for flushing out initial maps made with buggy 3-way blending.
  1027. if (!TheGlobalData->m_use3WayTerrainBlends)
  1028. m_blendedTiles[i].inverted &= ~FLIPPED_MASK; //filter out extra flips from 3-way
  1029. if (info->version >= K_BLEND_TILE_VERSION_3) {
  1030. m_blendedTiles[i].longDiagonal = file.readByte();
  1031. } else {
  1032. m_blendedTiles[i].longDiagonal = false;
  1033. }
  1034. if (info->version >= K_BLEND_TILE_VERSION_4) {
  1035. m_blendedTiles[i].customBlendEdgeClass = file.readInt();
  1036. } else {
  1037. m_blendedTiles[i].customBlendEdgeClass = -1;
  1038. }
  1039. flag = file.readInt();
  1040. DEBUG_ASSERTCRASH(flag==FLAG_VAL, ("Invalid format."));
  1041. if (flag != FLAG_VAL) {
  1042. throw ERROR_CORRUPT_FILE_FORMAT;
  1043. }
  1044. }
  1045. if (info->version >= K_BLEND_TILE_VERSION_5) {
  1046. for (i=1; i<m_numCliffInfo; i++) {
  1047. m_cliffInfo[i].tileIndex = file.readInt();
  1048. m_cliffInfo[i].u0 = file.readReal();
  1049. m_cliffInfo[i].v0 = file.readReal();
  1050. m_cliffInfo[i].u1 = file.readReal();
  1051. m_cliffInfo[i].v1 = file.readReal();
  1052. m_cliffInfo[i].u2 = file.readReal();
  1053. m_cliffInfo[i].v2 = file.readReal();
  1054. m_cliffInfo[i].u3 = file.readReal();
  1055. m_cliffInfo[i].v3 = file.readReal();
  1056. m_cliffInfo[i].flip = file.readByte();
  1057. m_cliffInfo[i].mutant = file.readByte();
  1058. }
  1059. }
  1060. // Resize me.
  1061. if (info->version == K_BLEND_TILE_VERSION_1) {
  1062. Int newWidth = (m_width+1)/2;
  1063. Int newHeight = (m_height+1)/2;
  1064. Int i, j;
  1065. for (i=0; i<newHeight; i++) {
  1066. for (j=0; j<newWidth; j++) {
  1067. m_tileNdxes[i*newWidth+j] = m_tileNdxes[2*i*m_width+2*j];
  1068. m_blendTileNdxes[i*newWidth+j] = 0;
  1069. m_extraBlendTileNdxes[i*newWidth+j] = 0;
  1070. m_cliffInfoNdxes[i*newWidth+j] = 0;
  1071. }
  1072. }
  1073. m_numBlendedTiles = 1;
  1074. m_numCliffInfo = 1;
  1075. m_width= newWidth;
  1076. m_height = newHeight;
  1077. m_dataSize = m_width*m_height;
  1078. }
  1079. DEBUG_ASSERTCRASH(file.atEndOfChunk(), ("Unexpected data left over."));
  1080. return true;
  1081. }
  1082. /**
  1083. * WorldHeightMap::ParseObjectData - read a object info chunk.
  1084. * Format is the newer CHUNKY format.
  1085. * See WHeightMapEdit.cpp for the writer.
  1086. * Input: DataChunkInput
  1087. *
  1088. */
  1089. Bool WorldHeightMap::ParseObjectDataChunk(DataChunkInput &file, DataChunkInfo *info, void *userData)
  1090. {
  1091. WorldHeightMap *pThis = (WorldHeightMap *)file.m_userData;
  1092. return pThis->ParseObjectData(file, info, userData, info->version >= K_OBJECTS_VERSION_2);
  1093. }
  1094. /**
  1095. * WorldHeightMap::ParseObjectData - read a object info chunk.
  1096. * Format is the newer CHUNKY format.
  1097. * See WHeightMapEdit.cpp for the writer.
  1098. * Input: DataChunkInput
  1099. *
  1100. */
  1101. Bool WorldHeightMap::ParseObjectData(DataChunkInput &file, DataChunkInfo *info, void *userData, Bool readDict)
  1102. {
  1103. MapObject *pPrevious = (MapObject *)file.m_currentObject;
  1104. Coord3D loc;
  1105. loc.x = file.readReal();
  1106. loc.y = file.readReal();
  1107. loc.z = file.readReal();
  1108. Real minZ = -100*MAP_XY_FACTOR;
  1109. Real maxZ = (255*10)*MAP_HEIGHT_SCALE;
  1110. if (info->version <= K_OBJECTS_VERSION_2) {
  1111. loc.z = 0;
  1112. }
  1113. Real angle = file.readReal();
  1114. Int flags = file.readInt();
  1115. AsciiString name = file.readAsciiString();
  1116. Dict d;
  1117. if (readDict)
  1118. {
  1119. d = file.readDict();
  1120. }
  1121. if (loc.z<minZ || loc.z>maxZ) {
  1122. DEBUG_LOG(("Removing object at z height %f\n", loc.z));
  1123. return true;
  1124. }
  1125. MapObject *pThisOne;
  1126. // create the map object
  1127. pThisOne = newInstance( MapObject )( loc, name, angle, flags, &d,
  1128. TheThingFactory->findTemplate( name, FALSE ) );
  1129. //DEBUG_LOG(("obj %s owner %s\n",name.str(),d.getAsciiString(TheKey_originalOwner).str()));
  1130. if (pThisOne->getProperties()->getType(TheKey_waypointID) == Dict::DICT_INT)
  1131. pThisOne->setIsWaypoint();
  1132. if (pThisOne->getProperties()->getType(TheKey_lightHeightAboveTerrain) == Dict::DICT_REAL)
  1133. pThisOne->setIsLight();
  1134. if (pThisOne->getProperties()->getType(TheKey_scorchType) == Dict::DICT_INT)
  1135. pThisOne->setIsScorch();
  1136. if (pPrevious) {
  1137. DEBUG_ASSERTCRASH(MapObject::TheMapObjectListPtr != NULL && pPrevious->getNext() == NULL, ("Bad linkage."));
  1138. pPrevious->setNextMap(pThisOne);
  1139. } else {
  1140. DEBUG_ASSERTCRASH(MapObject::TheMapObjectListPtr == NULL, ("Bad linkage."));
  1141. MapObject::TheMapObjectListPtr = pThisOne;
  1142. }
  1143. file.m_currentObject = pThisOne;
  1144. return true;
  1145. }
  1146. // Targa format: Header
  1147. typedef struct {
  1148. UnsignedByte idLength;
  1149. UnsignedByte colorMapType; // 0 = rgb, 1 = indexed.
  1150. UnsignedByte imageType; //0x1 = indexed, 0x2 = rgb, 0x8 = rle.
  1151. UnsignedByte colorMapInfo[5]; // we ignore, only do rgb.
  1152. Short xOrigin;
  1153. Short yOrigin;
  1154. Short imageWidth;
  1155. Short imageHeight;
  1156. UnsignedByte pixelDepth;
  1157. UnsignedByte flags; // &0x0F = alpha channel bits, &0x10 is right to left flag,
  1158. // 0x20 is top to bottom flag. (0x0? is left to right, bottom to top)
  1159. // 0x3? is top to bottom, right to left.
  1160. } TTargaHeader;
  1161. // followed by idLength bytes of ascii data
  1162. // followed by pixel data
  1163. // followed by optional data.
  1164. /// Count how many tiles come in from a targa file.
  1165. Int WorldHeightMap::countTiles(InputStream *pStr, Bool *halfTile)
  1166. {
  1167. TTargaHeader hdr;
  1168. if (halfTile) {
  1169. *halfTile = false;
  1170. }
  1171. Int len = pStr->read(&hdr,sizeof(hdr));
  1172. if (len!=sizeof(hdr)) return(0);
  1173. Int tileWidth = hdr.imageWidth/TILE_PIXEL_EXTENT;
  1174. Int tileHeight = hdr.imageHeight/TILE_PIXEL_EXTENT;
  1175. if (hdr.colorMapType != 0) {
  1176. return(0); // we don't do indexed at this time. jba.
  1177. }
  1178. if (hdr.imageType != 0x2 && hdr.imageType != 0xA) {
  1179. return(0); // we don't do indexed at this time. jba.
  1180. }
  1181. if (hdr.pixelDepth < 24) return(false);
  1182. if (hdr.pixelDepth > 32) return(false);
  1183. // 3x3 gives 9,
  1184. // 2x2 gives 4,
  1185. // 1x1 gives 1,
  1186. // else 0;
  1187. if (tileWidth>10 || tileHeight>10) return(0); // don't do huge images, or bad files.
  1188. if (tileWidth>=10 && tileHeight >=10) return(100);
  1189. if (tileWidth>=9 && tileHeight >=9) return(81);
  1190. if (tileWidth>=8 && tileHeight >=8) return(64);
  1191. if (tileWidth>=7 && tileHeight >=7) return(49);
  1192. if (tileWidth>=6 && tileHeight >=6) return(36);
  1193. if (tileWidth>=5 && tileHeight >=5) return(25);
  1194. if (tileWidth>=4 && tileHeight >=4) return(16);
  1195. if (tileWidth>=3 && tileHeight >=3) return(9);
  1196. if (tileWidth>=2 && tileHeight >=2) return(4);
  1197. if (tileWidth>=1 && tileHeight >=1) return(1);
  1198. if (halfTile && hdr.imageHeight==TILE_PIXEL_EXTENT/2 && hdr.imageWidth==TILE_PIXEL_EXTENT/2) {
  1199. *halfTile = true;
  1200. return 1;
  1201. }
  1202. return(0);
  1203. }
  1204. /*Break down a .tga file into a collection of tiles. numRows * numRows total tiles.*/
  1205. Bool WorldHeightMap::readTiles(InputStream *pStr, TileData **tiles, Int numRows)
  1206. {
  1207. TTargaHeader hdr;
  1208. pStr->read(&hdr, sizeof(hdr));
  1209. Int tileWidth = hdr.imageWidth/TILE_PIXEL_EXTENT;
  1210. Int tileHeight = hdr.imageHeight/TILE_PIXEL_EXTENT;
  1211. if (hdr.imageHeight==TILE_PIXEL_EXTENT/2) {
  1212. tileHeight = 1;
  1213. }
  1214. if (hdr.imageWidth==TILE_PIXEL_EXTENT/2) {
  1215. tileWidth = 1;
  1216. }
  1217. if (tileWidth<numRows && tileHeight<numRows) {
  1218. return(false);
  1219. }
  1220. Bool compressed = false;
  1221. if (hdr.imageType & 0x08) {
  1222. compressed = true;
  1223. }
  1224. int row = 0;
  1225. int column = 0;
  1226. int bytesPerPixel = (hdr.pixelDepth+7)/8;
  1227. if (bytesPerPixel < 3) return(false);
  1228. if (bytesPerPixel > 4) return(false);
  1229. int i;
  1230. for (i=0; i<numRows*numRows; i++) {
  1231. if (tiles[i] == NULL)
  1232. tiles[i] = MSGNEW("WorldHeightMap_readTiles") TileData;
  1233. }
  1234. UnsignedByte buf[4];
  1235. int repeatCount = 0;
  1236. // Bool read = false;
  1237. Bool running = false;
  1238. for (row = 0; row < numRows*TILE_PIXEL_EXTENT; row++) {
  1239. for (column=0; column<hdr.imageWidth; column++) {
  1240. UnsignedByte r, g, b, a;
  1241. if (row < hdr.imageHeight) {
  1242. if (compressed && repeatCount==0) {
  1243. UnsignedByte flag;
  1244. pStr->read(&flag, 1);
  1245. repeatCount = flag&0x7f;
  1246. repeatCount++;
  1247. if (flag&0x80) {
  1248. running = true;
  1249. pStr->read(buf, bytesPerPixel);
  1250. } else {
  1251. running = false;
  1252. }
  1253. }
  1254. if (compressed) repeatCount--;
  1255. if (!running) {
  1256. pStr->read(buf, bytesPerPixel);
  1257. }
  1258. r = buf[2]; g = buf[1]; b = buf[0];
  1259. if (bytesPerPixel==4) {
  1260. a = buf[3];
  1261. } else {
  1262. a = 255;// solid alpha.
  1263. }
  1264. } else {
  1265. r = g = b = a = 0;
  1266. }
  1267. if (column >= (numRows*TILE_PIXEL_EXTENT)) continue;
  1268. int tileNdx = (column/TILE_PIXEL_EXTENT) + numRows*(row/TILE_PIXEL_EXTENT);
  1269. int pixelNdx = (column%TILE_PIXEL_EXTENT) + TILE_PIXEL_EXTENT*(row%TILE_PIXEL_EXTENT);
  1270. UnsignedByte *pixel = tiles[tileNdx]->getDataPtr();
  1271. pixel += pixelNdx*TILE_BYTES_PER_PIXEL;
  1272. *pixel++ = b;
  1273. *pixel++ = g;
  1274. *pixel++ = r;
  1275. *pixel = a;
  1276. }
  1277. DEBUG_ASSERTCRASH(repeatCount==0, ("Invalid tga."));
  1278. }
  1279. for (i=0; i<numRows*numRows; i++) {
  1280. tiles[i]->updateMips();
  1281. }
  1282. return(true);
  1283. }
  1284. /** updateTileTexturePositions - assigns each tile a location in the texture.
  1285. */
  1286. Int WorldHeightMap::updateTileTexturePositions(Int *edgeHeight)
  1287. {
  1288. Int i, j;
  1289. Int maxHeight = 0;
  1290. const Int tilesPerRow = TEXTURE_WIDTH/(TILE_PIXEL_EXTENT+TILE_OFFSET);
  1291. Bool availableGrid[tilesPerRow][tilesPerRow];
  1292. Int row, column;
  1293. for (row=0; row<tilesPerRow; row++) {
  1294. for (column=0; column<tilesPerRow; column++) {
  1295. availableGrid[row][column] = true;
  1296. }
  1297. }
  1298. for (i=0; i<m_numBitmapTiles; i++) {
  1299. if (m_sourceTiles[i]) {
  1300. m_sourceTiles[i]->m_tileLocationInTexture.x = 0;
  1301. m_sourceTiles[i]->m_tileLocationInTexture.y = 0;
  1302. }
  1303. }
  1304. /* put the normal tiles into the terrain texture */
  1305. Int texClass;
  1306. Int tileWidth;
  1307. for (tileWidth = tilesPerRow; tileWidth>0; tileWidth--) {
  1308. for (texClass=0; texClass<m_numTextureClasses; texClass++) {
  1309. Int width = m_textureClasses[texClass].width;
  1310. if (width != tileWidth) continue;
  1311. // Find an available block of space.
  1312. Bool found = false;
  1313. for (row=0; row<tilesPerRow-width+1 && !found; row++) {
  1314. for (column=0; column<tilesPerRow-width+1 && !found; column++) {
  1315. if (availableGrid[row][column]) {
  1316. Bool open = true;
  1317. for (i=0; i<width && open; i++) {
  1318. for (j=0; j<width&&open; j++) {
  1319. if (!availableGrid[row+j][column+i]) {
  1320. open = false;
  1321. }
  1322. }
  1323. }
  1324. if (open) found = true;
  1325. break;
  1326. }
  1327. }
  1328. if (found) break;
  1329. }
  1330. if (!found) {
  1331. m_textureClasses[texClass].positionInTexture.x = 0;
  1332. m_textureClasses[texClass].positionInTexture.y = 0;
  1333. continue;
  1334. }
  1335. Int xOrigin = TILE_OFFSET/2 + column*(TILE_PIXEL_EXTENT+TILE_OFFSET);
  1336. Int yOrigin = TILE_OFFSET/2 + row*(TILE_PIXEL_EXTENT+TILE_OFFSET);
  1337. m_textureClasses[texClass].positionInTexture.x = xOrigin;
  1338. m_textureClasses[texClass].positionInTexture.y = yOrigin;
  1339. Int classHeight = yOrigin + width*TILE_PIXEL_EXTENT+ TILE_OFFSET/2;
  1340. if (maxHeight < classHeight) maxHeight = classHeight;
  1341. for (i=0; i<width; i++) {
  1342. for (j=0; j<width; j++) {
  1343. availableGrid[row+j][column+i] = false;
  1344. Int baseNdx = m_textureClasses[texClass].firstTile + i + j*width;
  1345. // In case we are just checking for room...
  1346. if (m_sourceTiles[baseNdx] == NULL) continue;
  1347. Int x = xOrigin + i*TILE_PIXEL_EXTENT;
  1348. Int y = yOrigin + (width-j-1)*TILE_PIXEL_EXTENT;
  1349. m_sourceTiles[baseNdx]->m_tileLocationInTexture.x = x;
  1350. m_sourceTiles[baseNdx]->m_tileLocationInTexture.y = y;
  1351. }
  1352. }
  1353. }
  1354. }
  1355. for (i=0; i<m_numBitmapTiles; i++) {
  1356. if (m_edgeTiles[i]) {
  1357. m_edgeTiles[i]->m_tileLocationInTexture.x = 0;
  1358. m_edgeTiles[i]->m_tileLocationInTexture.y = 0;
  1359. }
  1360. }
  1361. /* put the blend edge tiles into the blend edges texture */
  1362. Int maxEdgeHeight = 0;
  1363. // Reset the grid, cause we're using a different texture now.
  1364. for (row=0; row<tilesPerRow; row++) {
  1365. for (column=0; column<tilesPerRow; column++) {
  1366. availableGrid[row][column] = true;
  1367. }
  1368. }
  1369. for (texClass=0; texClass<m_numEdgeTextureClasses; texClass++) {
  1370. Int width = m_edgeTextureClasses[texClass].width;
  1371. // Find an available block of space.
  1372. Bool found = false;
  1373. for (row=0; row<tilesPerRow-width+1 && !found; row++) {
  1374. for (column=0; column<tilesPerRow-width+1 && !found; column++) {
  1375. if (availableGrid[row][column]) {
  1376. Bool open = true;
  1377. for (i=0; i<width && open; i++) {
  1378. for (j=0; j<width&&open; j++) {
  1379. if (!availableGrid[row+j][column+i]) {
  1380. open = false;
  1381. }
  1382. }
  1383. }
  1384. if (open) found = true;
  1385. break;
  1386. }
  1387. }
  1388. if (found) break;
  1389. }
  1390. if (!found) {
  1391. m_edgeTextureClasses[texClass].positionInTexture.x = 0;
  1392. m_edgeTextureClasses[texClass].positionInTexture.y = 0;
  1393. continue;
  1394. }
  1395. Int xOrigin = TILE_OFFSET/2 + column*(TILE_PIXEL_EXTENT+TILE_OFFSET);
  1396. Int yOrigin = TILE_OFFSET/2 + row*(TILE_PIXEL_EXTENT+TILE_OFFSET);
  1397. m_edgeTextureClasses[texClass].positionInTexture.x = xOrigin;
  1398. m_edgeTextureClasses[texClass].positionInTexture.y = yOrigin;
  1399. Int classHeight = yOrigin + width*TILE_PIXEL_EXTENT+ TILE_OFFSET/2;
  1400. if (maxEdgeHeight < classHeight) maxEdgeHeight = classHeight;
  1401. for (i=0; i<width; i++) {
  1402. for (j=0; j<width; j++) {
  1403. availableGrid[row+j][column+i] = false;
  1404. Int baseNdx = m_edgeTextureClasses[texClass].firstTile + i + j*width;
  1405. // In case we are just checking for room...
  1406. if (m_edgeTiles[baseNdx] == NULL) continue;
  1407. Int x = xOrigin + i*TILE_PIXEL_EXTENT;
  1408. Int y = yOrigin + (width-j-1)*TILE_PIXEL_EXTENT;
  1409. // Use negative offsets to differentiate between tiles & edges.
  1410. m_edgeTiles[baseNdx]->m_tileLocationInTexture.x = x;
  1411. m_edgeTiles[baseNdx]->m_tileLocationInTexture.y = y;
  1412. }
  1413. }
  1414. }
  1415. if (edgeHeight) *edgeHeight = maxEdgeHeight;
  1416. return maxHeight;
  1417. }
  1418. /** getUVData - Gets the texture coordinates to use. See getTerrainTexture.
  1419. */
  1420. void WorldHeightMap::getUVForNdx(Int tileNdx, float *minU, float *minV, float *maxU, float*maxV, Bool fullTile)
  1421. {
  1422. Short baseNdx = tileNdx>>2;
  1423. if (m_sourceTiles[baseNdx] == NULL) {
  1424. // Missing texture.
  1425. *minU = *minV = *maxU = *maxV = 0.0f;
  1426. return;
  1427. }
  1428. ICoord2D pos = m_sourceTiles[baseNdx]->m_tileLocationInTexture;
  1429. *minU = pos.x;
  1430. *minV = pos.y;
  1431. *maxU = *minU+TILE_PIXEL_EXTENT;
  1432. *maxV = *minV+TILE_PIXEL_EXTENT;
  1433. #ifdef EVAL_TILING_MODES
  1434. if (m_tileMode == TILE_8x8) {
  1435. *maxU = *minU+TILE_PIXEL_EXTENT/2.0f;
  1436. *maxV = *minV+TILE_PIXEL_EXTENT/2.0f;
  1437. } else if (m_tileMode == TILE_6x6) {
  1438. *maxU = *minU+2.0f*TILE_PIXEL_EXTENT/3.0f;
  1439. *maxV = *minV+2.0f*TILE_PIXEL_EXTENT/3.0f;
  1440. } else {
  1441. *maxU = *minU+TILE_PIXEL_EXTENT;
  1442. *maxV = *minV+TILE_PIXEL_EXTENT;
  1443. }
  1444. #endif
  1445. *minU/=TEXTURE_WIDTH;
  1446. *minV/=m_terrainTexHeight;
  1447. *maxU/=TEXTURE_WIDTH;
  1448. *maxV/=m_terrainTexHeight;
  1449. if (!fullTile) {
  1450. // Tiles are 64x64 pixels, height grids map to 32x32.
  1451. // So get the proper quadrant of the tile.
  1452. Real midX = (*minU+*maxU)/2;
  1453. Real midY = (*minV+*maxV)/2;
  1454. if (tileNdx&2) { // y's are flipped.
  1455. *maxV = midY;
  1456. } else {
  1457. *minV = midY;
  1458. }
  1459. if (tileNdx&1) {
  1460. *minU = midX;
  1461. } else {
  1462. *maxU = midX;
  1463. }
  1464. }
  1465. }
  1466. /** getUVData - Gets the texture coordinates to use. See getTerrainTexture.
  1467. */
  1468. void WorldHeightMap::getUVForBlend(Int edgeClass, Region2D *range)
  1469. {
  1470. ICoord2D pos = m_edgeTextureClasses[edgeClass].positionInTexture;
  1471. Int width = m_edgeTextureClasses[edgeClass].width;
  1472. range->lo.x = (Real)pos.x/TEXTURE_WIDTH;
  1473. range->lo.y = (Real)pos.y/m_alphaEdgeHeight;
  1474. range->hi.x = ((Real)pos.x + width*TILE_PIXEL_EXTENT)/TEXTURE_WIDTH;
  1475. range->hi.y = ((Real)pos.y + width*TILE_PIXEL_EXTENT)/m_alphaEdgeHeight;
  1476. }
  1477. /// Get whether something is cliff indexed with the offset that HeightMapRenderObjClass uses built in.
  1478. Bool WorldHeightMap::isCliffMappedTexture(Int x, Int y) {
  1479. Int ndx = x+m_drawOriginX+m_width*(y+m_drawOriginY);
  1480. if (ndx>=0 && ndx<m_dataSize) {
  1481. return m_cliffInfoNdxes[ndx] != 0;
  1482. }
  1483. return false;
  1484. };
  1485. /** getUVData - Gets the texture coordinates to use. See getTerrainTexture.
  1486. xIndex and yIndex are the integer coorddinates into the height map.
  1487. U and V are the texture coordiantes for the 4 corners of a height map cell.
  1488. fullTile is true if we are doing 1/2 resolution height map, and require a full
  1489. tile to texture a cell. Otherwise, we use quarter tiles per cell.
  1490. */
  1491. Bool WorldHeightMap::getUVData(Int xIndex, Int yIndex, float U[4], float V[4], Bool fullTile)
  1492. {
  1493. #define dont_SHOW_THE_TEXTURE_FOR_DEBUG 1
  1494. #if SHOW_THE_TEXTURE_FOR_DEBUG
  1495. // This is debug code that just shows the generated texture laid on the terrain.
  1496. // For debugging ;) jba.
  1497. xIndex += m_drawOriginX;
  1498. yIndex += m_drawOriginY;
  1499. float nU= xIndex;
  1500. float xU = xIndex+1;
  1501. float nV = 48-yIndex-1;
  1502. float xV = 48-yIndex;
  1503. float k = 48;
  1504. nU /= k;
  1505. xU /= k;
  1506. k = k*m_terrainTexHeight/TEXTURE_WIDTH;
  1507. nV /= k;
  1508. xV /= k;
  1509. U[0] = nU; U[1] = xU; U[2] = xU; U[3] = nU;
  1510. V[0] = xV; V[1] = xV; V[2] = nV; V[3] = nV;
  1511. return(true);
  1512. #else
  1513. xIndex += m_drawOriginX;
  1514. yIndex += m_drawOriginY;
  1515. Int ndx = (yIndex*m_width)+xIndex;
  1516. if ((ndx<m_dataSize) && m_tileNdxes) {
  1517. Short tileNdx = m_tileNdxes[ndx];
  1518. return getUVForTileIndex(ndx, tileNdx, U, V, fullTile);
  1519. }
  1520. return false;
  1521. #endif
  1522. }
  1523. /** getUVForTileIndex - Gets the texture coordinates to use. See getTerrainTexture.
  1524. ndx is the index into the linear height array.
  1525. tileNdx is the index into the texture tiles array.
  1526. U and V are the texture coordiantes for the 4 corners of a height map cell.
  1527. fullTile is true if we are doing 1/2 resolution height map, and require a full
  1528. tile to texture a cell. Otherwise, we use quarter tiles per cell.
  1529. */
  1530. Bool WorldHeightMap::getUVForTileIndex(Int ndx, Short tileNdx, float U[4], float V[4], Bool fullTile)
  1531. {
  1532. Real nU, nV, xU, xV;
  1533. nU=nV=xU=xV = 0.0f;
  1534. Int tilesPerRow = TEXTURE_WIDTH/(2*TILE_PIXEL_EXTENT+TILE_OFFSET);
  1535. tilesPerRow *= 4;
  1536. if ((ndx<m_dataSize) && m_tileNdxes) {
  1537. getUVForNdx(tileNdx, &nU, &nV, &xU, &xV, fullTile);
  1538. U[0] = nU; U[1] = xU; U[2] = xU; U[3] = nU;
  1539. V[0] = xV; V[1] = xV; V[2] = nV; V[3] = nV;
  1540. if (TheGlobalData && !TheGlobalData->m_adjustCliffTextures) {
  1541. return false;
  1542. }
  1543. if (nU==0.0) {
  1544. return false; // missing texture.
  1545. }
  1546. if (fullTile) {
  1547. return false;
  1548. }
  1549. if (m_cliffInfoNdxes[ndx]) {
  1550. TCliffInfo info = m_cliffInfo[m_cliffInfoNdxes[ndx]];
  1551. Bool tilesMatch = false;
  1552. Int ndx1 = tileNdx>>2;
  1553. Int ndx2 = info.tileIndex>>2;
  1554. Int i;
  1555. for (i=0; i<this->m_numTextureClasses; i++) {
  1556. if (ndx1 >= m_textureClasses[i].firstTile && ndx1 < m_textureClasses[i].firstTile + m_textureClasses[i].numTiles) {
  1557. tilesMatch = ndx2 >= m_textureClasses[i].firstTile && ndx2 < m_textureClasses[i].firstTile + m_textureClasses[i].numTiles;
  1558. //tilesMatch = true;
  1559. break;
  1560. }
  1561. }
  1562. if (tilesMatch) {
  1563. Real minU = m_textureClasses[i].positionInTexture.x;
  1564. Real maxV = m_textureClasses[i].positionInTexture.y + m_textureClasses[i].width*TILE_PIXEL_EXTENT;
  1565. minU/=TEXTURE_WIDTH;
  1566. maxV/=m_terrainTexHeight;
  1567. Real vFactor = TEXTURE_WIDTH/m_terrainTexHeight;
  1568. U[0] = info.u0+minU;
  1569. U[1] = info.u1+minU;
  1570. U[2] = info.u2+minU;
  1571. U[3] = info.u3+minU;
  1572. V[0] = info.v0*vFactor+maxV;
  1573. V[1] = info.v1*vFactor+maxV;
  1574. V[2] = info.v2*vFactor+maxV;
  1575. V[3] = info.v3*vFactor+maxV;
  1576. return info.flip;
  1577. }
  1578. }
  1579. #define DO_OLD_UV
  1580. #ifdef DO_OLD_UV
  1581. // old uv adjustment for cliffs
  1582. static Real STRETCH_LIMIT = 1.5f; // If it is stretching less than this, don't adjust.
  1583. static Real TILE_LIMIT = 4.0; // Our tiles are currently 4 cells wide & tall, so dont'
  1584. // adjust to more than 4.0.
  1585. static Real TALL_STRETCH_LIMIT = 2.0f;
  1586. static Real DIAMOND_STRETCH_LIMIT = 2.4f;
  1587. static Real HEIGHT_SCALE = MAP_HEIGHT_SCALE / MAP_XY_FACTOR;
  1588. Real nU, nV, xU, xV;
  1589. nU=nV=xU=xV = 0.0f;
  1590. Int tilesPerRow = TEXTURE_WIDTH/(2*TILE_PIXEL_EXTENT+TILE_OFFSET);
  1591. tilesPerRow *= 4;
  1592. getUVForNdx(tileNdx, &nU, &nV, &xU, &xV, fullTile);
  1593. U[0] = nU; U[1] = xU; U[2] = xU; U[3] = nU;
  1594. V[0] = xV; V[1] = xV; V[2] = nV; V[3] = nV;
  1595. if (TheGlobalData && !TheGlobalData->m_adjustCliffTextures) {
  1596. return false;
  1597. }
  1598. if (nU==0.0) {
  1599. return false; // missing texture.
  1600. }
  1601. if (fullTile) {
  1602. return false;
  1603. }
  1604. // check for excessive heights.
  1605. if (ndx < this->m_dataSize - m_width - 1) {
  1606. Int h0 = m_data[ndx];
  1607. Int h1 = m_data[ndx+1];
  1608. Int h2 = m_data[ndx+m_width+1];
  1609. Int h3 = m_data[ndx+m_width];
  1610. Int minH, maxH;
  1611. minH = maxH = h0;
  1612. if (minH>h1) minH = h1;
  1613. if (maxH<h1) maxH = h1;
  1614. if (minH>h2) minH = h2;
  1615. if (maxH<h2) maxH = h2;
  1616. if (minH>h3) minH = h3;
  1617. if (maxH<h3) maxH = h3;
  1618. // Int avgH = (h1+h2+h3+h0+2)/4;
  1619. Int deltaH = maxH-minH;
  1620. Int below = 0;
  1621. Int above = 0;
  1622. Int belowLimit = minH+(2*deltaH+1)/3;
  1623. Int aboveLimit = minH+(deltaH+1)/3;
  1624. if (h0<belowLimit) below++;
  1625. if (h1<belowLimit) below++;
  1626. if (h2<belowLimit) below++;
  1627. if (h3<belowLimit) below++;
  1628. if (h0>aboveLimit) above++;
  1629. if (h1>aboveLimit) above++;
  1630. if (h2>aboveLimit) above++;
  1631. if (h3>aboveLimit) above++;
  1632. if (deltaH*HEIGHT_SCALE < STRETCH_LIMIT) {
  1633. return false;
  1634. }
  1635. Short baseNdx = tileNdx>>2;
  1636. Short texClass;
  1637. for (texClass=0; texClass<m_numTextureClasses; texClass++) {
  1638. if (m_textureClasses[texClass].firstTile<0) {
  1639. continue;
  1640. }
  1641. // see if the blend tile is in a texture class, and get the right tile for xIndex, yIndex.
  1642. if (baseNdx >= m_textureClasses[texClass].firstTile &&
  1643. baseNdx < m_textureClasses[texClass].firstTile+m_textureClasses[texClass].numTiles) {
  1644. break;
  1645. }
  1646. }
  1647. if (texClass>= m_numTextureClasses) return false;
  1648. Real nUb, nVb, xUb, xVb;
  1649. nUb = m_textureClasses[texClass].positionInTexture.x;
  1650. nVb = m_textureClasses[texClass].positionInTexture.y;
  1651. xUb = nUb+m_textureClasses[texClass].width*TILE_PIXEL_EXTENT;
  1652. xVb = nVb+m_textureClasses[texClass].width*TILE_PIXEL_EXTENT;
  1653. nUb/=TEXTURE_WIDTH;
  1654. nVb/=m_terrainTexHeight;
  1655. xUb/=TEXTURE_WIDTH;
  1656. xVb/=m_terrainTexHeight;
  1657. // Now covers texture bounds.
  1658. // too much stretch.
  1659. Real divisor = TILE_LIMIT/(deltaH*HEIGHT_SCALE);
  1660. if (divisor > TILE_LIMIT) divisor = TILE_LIMIT;
  1661. if (divisor < 1.0f) divisor = 1.0f;
  1662. Real deltaV = (xVb-nVb);
  1663. // Real deltaU = (xUb-nUb);
  1664. if (above != 1 && below != 1 && (above!=2 || below != 2)) {
  1665. // diamond shaped. Use default if it is not too stretched, as
  1666. // the fix is not that appealing either.
  1667. if (deltaH*HEIGHT_SCALE < DIAMOND_STRETCH_LIMIT) {
  1668. return false;
  1669. }
  1670. }
  1671. if (below==1 || above>below) { //(avgH > minH + (2*deltaH+2)/3)
  1672. // we got one low guy.
  1673. if (h0==minH) {
  1674. V[0] = nV+deltaV/divisor;
  1675. } else if (h1 == minH) {
  1676. V[1] = nV+deltaV/divisor;
  1677. } else if (h2 == minH) {
  1678. V[2] = xV-deltaV/divisor;
  1679. } else if (h3 == minH) {
  1680. V[3] = xV-deltaV/divisor;
  1681. }
  1682. #if 0
  1683. nU = nV = xU = xV = 1.0f;
  1684. U[0] = nU; U[1] = xU; U[2] = xU; U[3] = nU;
  1685. V[0] = xV; V[1] = xV; V[2] = nV; V[3] = nV;
  1686. return false;
  1687. #endif
  1688. } else if (above==1 || below>above) { //(avgH < minH + (deltaH+1)/3)
  1689. // we got one high guy
  1690. if (h0==maxH) {
  1691. V[0] = nV+deltaV/divisor;
  1692. } else if (h1 == maxH) {
  1693. V[1] = nV+deltaV/divisor;
  1694. } else if (h2 == maxH) {
  1695. V[2] = xV-deltaV/divisor;
  1696. } else if (h3 == maxH) {
  1697. V[3] = xV-deltaV/divisor;
  1698. }
  1699. #if 0
  1700. nU = nV = xU = xV = 0.0f;
  1701. U[0] = nU; U[1] = xU; U[2] = xU; U[3] = nU;
  1702. V[0] = xV; V[1] = xV; V[2] = nV; V[3] = nV;
  1703. return false;
  1704. #endif
  1705. } else {
  1706. // we got two up and two down.
  1707. if (deltaH*HEIGHT_SCALE < TALL_STRETCH_LIMIT) {
  1708. return false;
  1709. }
  1710. #if 0
  1711. nU = nV = xU = xV = 0.0f;
  1712. U[0] = nU; U[1] = xU; U[2] = xU; U[3] = nU;
  1713. V[0] = xV; V[1] = xV; V[2] = nV; V[3] = nV;
  1714. return;
  1715. #endif
  1716. Real dx = (h3-h2)*HEIGHT_SCALE;
  1717. dx = sqrt(1+dx*dx); // lenght of the bottom of the cell
  1718. Real dy = (h3-h0)*HEIGHT_SCALE;
  1719. dy = sqrt(1+dy*dy); // length of the left side.
  1720. if (dx<STRETCH_LIMIT) dx = 1.0f; // don't make a seam unless there is great stretch.
  1721. if (dy<STRETCH_LIMIT) dy = 1.0f; // don't make a seam unless there is great stretch.
  1722. if (dx>TILE_LIMIT) dx = TILE_LIMIT; // don't tile past the texture's edge.
  1723. if (dy>TILE_LIMIT) dy = TILE_LIMIT; // don't tile past the texture's edge.
  1724. dx *= xU-nU;
  1725. dy *= xV-nV;
  1726. U[0] = nU; U[1] = nU+dx; U[2] = nU+dx; U[3] = nU;
  1727. V[0] = nV+dy; V[1] = nV+dy; V[2] = nV; V[3] = nV;
  1728. if (below==1) {
  1729. below = 1;
  1730. }
  1731. // recalc for point 1.
  1732. dx = (h1-h0)*HEIGHT_SCALE;
  1733. dx = sqrt(1+dx*dx); // lenght of the bottom of the cell
  1734. dy = (h2-h1)*HEIGHT_SCALE;
  1735. dy = sqrt(1+dy*dy); // length of the left side.
  1736. if (dx<STRETCH_LIMIT) dx = 1.0f; // don't make a seam unless there is great stretch.
  1737. if (dy<STRETCH_LIMIT) dy = 1.0f; // don't make a seam unless there is great stretch.
  1738. if (dx>TILE_LIMIT) dx = TILE_LIMIT; // don't tile past the texture's edge.
  1739. if (dy>TILE_LIMIT) dy = TILE_LIMIT; // don't tile past the texture's edge.
  1740. dx *= xU-nU;
  1741. dy *= xV-nV;
  1742. U[1] = U[0]+dx;
  1743. V[1] = V[3] + dy;
  1744. }
  1745. // Make sure we are within the texture;
  1746. Real adjU = 0;
  1747. Real adjV = 0;
  1748. Int i;
  1749. for (i=0; i<4; i++) {
  1750. if (nVb - V[i] > adjV) adjV = nVb - V[i];
  1751. }
  1752. for (i=0; i<4; i++) {
  1753. V[i] += adjV;
  1754. }
  1755. adjV = 0;
  1756. for (i=0; i<4; i++) {
  1757. if (U[i] - xUb > adjU) adjU = U[i]-xUb;
  1758. if (V[i] - xVb > adjV) adjV = V[i]-xVb;
  1759. }
  1760. for (i=0; i<4; i++) {
  1761. U[i] -= adjU;
  1762. V[i] -= adjV;
  1763. }
  1764. }
  1765. return true;
  1766. //
  1767. #endif
  1768. }
  1769. return false;
  1770. }
  1771. ///@todo: Are the different "if" cases mutually exclusive? If so, should add else statements.
  1772. Bool WorldHeightMap::getExtraAlphaUVData(Int xIndex, Int yIndex, float U[4], float V[4], UnsignedByte alpha[4], Bool *needFlip, Bool *cliff)
  1773. {
  1774. Int ndx = (yIndex*m_width)+xIndex;
  1775. *needFlip = FALSE;
  1776. *cliff = FALSE;
  1777. if ( (ndx>=0) && (ndx<m_dataSize) && m_tileNdxes) {
  1778. Short blendNdx = m_extraBlendTileNdxes[ndx];
  1779. if (blendNdx == 0) {
  1780. return FALSE;
  1781. } else {
  1782. *cliff = getUVForTileIndex(ndx, m_blendedTiles[blendNdx].blendNdx, U, V, FALSE);
  1783. alpha[0] = alpha[1] = alpha[2] = alpha[3] = 0;
  1784. if (m_blendedTiles[blendNdx].horiz) {
  1785. // Horizontals don't need flipping unless forced because of 3way blend
  1786. // and a diagonal in base blend layer.
  1787. *needFlip = m_blendedTiles[blendNdx].inverted & FLIPPED_MASK;
  1788. if (m_blendedTiles[blendNdx].inverted & INVERTED_MASK) {
  1789. alpha[0] = alpha[3] = 255;
  1790. } else {
  1791. alpha[1] = alpha[2] = 255;
  1792. }
  1793. }
  1794. if (m_blendedTiles[blendNdx].vert) {
  1795. // Verticals don't need flipping unless forced because of 3way blend
  1796. // and a diagonal in base blend layer.
  1797. *needFlip = m_blendedTiles[blendNdx].inverted & FLIPPED_MASK;
  1798. if (m_blendedTiles[blendNdx].inverted & INVERTED_MASK) {
  1799. alpha[0] = alpha[1] = 255;
  1800. } else {
  1801. alpha[2] = alpha[3] = 255;
  1802. }
  1803. }
  1804. if (m_blendedTiles[blendNdx].rightDiagonal) {
  1805. if (m_blendedTiles[blendNdx].inverted & INVERTED_MASK) {
  1806. alpha[1] = 255;
  1807. if (m_blendedTiles[blendNdx].longDiagonal) {
  1808. alpha[0] = 255;
  1809. alpha[2] = 255;
  1810. }
  1811. } else {
  1812. // Uninverted right diagonals need flipping.
  1813. *needFlip = TRUE;
  1814. alpha[2] = 255;
  1815. if (m_blendedTiles[blendNdx].longDiagonal) {
  1816. alpha[1] = 255;
  1817. alpha[3] = 255;
  1818. }
  1819. }
  1820. }
  1821. if (m_blendedTiles[blendNdx].leftDiagonal) {
  1822. if (m_blendedTiles[blendNdx].inverted & INVERTED_MASK) {
  1823. // Inverted left diagonals need flipping.
  1824. *needFlip = TRUE;
  1825. alpha[0] = 255;
  1826. if (m_blendedTiles[blendNdx].longDiagonal) {
  1827. alpha[1] = 255;
  1828. alpha[3] = 255;
  1829. }
  1830. } else {
  1831. alpha[3] = 255;
  1832. if (m_blendedTiles[blendNdx].longDiagonal) {
  1833. alpha[0] = 255;
  1834. alpha[2] = 255;
  1835. }
  1836. }
  1837. }
  1838. if (m_blendedTiles[blendNdx].customBlendEdgeClass>=0) {
  1839. alpha[0] = alpha[1] = alpha[2] = alpha[3] = 0;
  1840. // No alpha blend, so never need to flip.
  1841. *needFlip = FALSE;
  1842. }
  1843. }
  1844. }
  1845. return TRUE;
  1846. }
  1847. /** getUVData - Gets the texture coordinates to use with the alpha texture.
  1848. xIndex and yIndex are the integer coorddinates into the height map.
  1849. U and V are the texture coordiantes for the 4 corners of a height map cell.
  1850. fullTile is true if we are doing 1/2 resolution height map, and require a full
  1851. tile to texture a cell. Otherwise, we use quarter tiles per cell.
  1852. flip is set if we need to flip the diagonal across the cell to make the
  1853. alpha coordinates blend properly. Filling a square with 2 triangles is not symmetrical :)
  1854. */
  1855. void WorldHeightMap::getAlphaUVData(Int xIndex, Int yIndex, float U[4], float V[4],
  1856. UnsignedByte alpha[4], Bool *flip, Bool fullTile)
  1857. {
  1858. xIndex += m_drawOriginX;
  1859. yIndex += m_drawOriginY;
  1860. Int ndx = (yIndex*m_width)+xIndex;
  1861. Bool stretchedForCliff = false;
  1862. Bool needFlip = false;
  1863. if ((ndx<m_dataSize) && m_tileNdxes) {
  1864. Short blendNdx = m_blendTileNdxes[ndx];
  1865. if (fullTile) blendNdx = 0;
  1866. if (blendNdx == 0) {
  1867. stretchedForCliff = getUVForTileIndex(ndx, m_tileNdxes[ndx], U, V, fullTile);
  1868. alpha[0] = alpha[1] = alpha[2] = alpha[3] = 0;
  1869. // No alpha blend, so never need to flip.
  1870. needFlip = false;
  1871. } else {
  1872. stretchedForCliff = getUVForTileIndex(ndx, m_blendedTiles[blendNdx].blendNdx, U, V, fullTile);
  1873. alpha[0] = alpha[1] = alpha[2] = alpha[3] = 0;
  1874. if (m_blendedTiles[blendNdx].horiz) {
  1875. // Horizontals don't need flipping unless forced because of 3way blend.
  1876. needFlip = m_blendedTiles[blendNdx].inverted & FLIPPED_MASK;
  1877. if (m_blendedTiles[blendNdx].inverted & INVERTED_MASK) {
  1878. alpha[0] = alpha[3] = 255;
  1879. } else {
  1880. alpha[1] = alpha[2] = 255;
  1881. }
  1882. }
  1883. if (m_blendedTiles[blendNdx].vert) {
  1884. // Verticals don't need flipping unless forced because of 3way blend.
  1885. needFlip = m_blendedTiles[blendNdx].inverted & FLIPPED_MASK;
  1886. if (m_blendedTiles[blendNdx].inverted & INVERTED_MASK) {
  1887. alpha[0] = alpha[1] = 255;
  1888. } else {
  1889. alpha[2] = alpha[3] = 255;
  1890. }
  1891. }
  1892. if (m_blendedTiles[blendNdx].rightDiagonal) {
  1893. if (m_blendedTiles[blendNdx].inverted & INVERTED_MASK) {
  1894. alpha[1] = 255;
  1895. if (m_blendedTiles[blendNdx].longDiagonal) {
  1896. alpha[0] = 255;
  1897. alpha[2] = 255;
  1898. }
  1899. } else {
  1900. // Uninverted right diagonals need flipping.
  1901. needFlip = true;
  1902. alpha[2] = 255;
  1903. if (m_blendedTiles[blendNdx].longDiagonal) {
  1904. alpha[1] = 255;
  1905. alpha[3] = 255;
  1906. }
  1907. }
  1908. }
  1909. if (m_blendedTiles[blendNdx].leftDiagonal) {
  1910. if (m_blendedTiles[blendNdx].inverted & INVERTED_MASK) {
  1911. // Inverted left diagonals need flipping.
  1912. needFlip = true;
  1913. alpha[0] = 255;
  1914. if (m_blendedTiles[blendNdx].longDiagonal) {
  1915. alpha[1] = 255;
  1916. alpha[3] = 255;
  1917. }
  1918. } else {
  1919. alpha[3] = 255;
  1920. if (m_blendedTiles[blendNdx].longDiagonal) {
  1921. alpha[0] = 255;
  1922. alpha[2] = 255;
  1923. }
  1924. }
  1925. }
  1926. if (m_blendedTiles[blendNdx].customBlendEdgeClass>=0) {
  1927. alpha[0] = alpha[1] = alpha[2] = alpha[3] = 0;
  1928. // No alpha blend, so never need to flip.
  1929. needFlip = false;
  1930. }
  1931. }
  1932. }
  1933. if (stretchedForCliff) {
  1934. // If we had to stretch for clif, check heights.
  1935. Int p0=getHeight(xIndex, yIndex);
  1936. Int p1=getHeight(xIndex+1, yIndex);
  1937. Int p2=getHeight(xIndex+1, yIndex+1);
  1938. Int p3=getHeight(xIndex, yIndex+1);
  1939. Int dz1 = abs(p0-p2);
  1940. Int dz2 = abs(p1-p3);
  1941. needFlip = dz1>dz2;
  1942. }
  1943. #ifdef FLIP_TRIANGLES
  1944. *flip = needFlip;
  1945. #endif
  1946. }
  1947. void WorldHeightMap::setTextureLOD(Int lod)
  1948. {
  1949. if (m_terrainTex)
  1950. m_terrainTex->setLOD(lod);
  1951. }
  1952. TextureClass *WorldHeightMap::getTerrainTexture(void)
  1953. {
  1954. if (m_terrainTex == NULL) {
  1955. Int edgeHeight;
  1956. Int height = updateTileTexturePositions(&edgeHeight);
  1957. Int pow2Height = 1;
  1958. while (pow2Height<height) {
  1959. pow2Height *=2;
  1960. }
  1961. REF_PTR_RELEASE(m_terrainTex);
  1962. m_terrainTex = MSGNEW("WorldHeightMap_getTerrainTexture") TerrainTextureClass(pow2Height);
  1963. m_terrainTexHeight = m_terrainTex->update(this);
  1964. char buf[64];
  1965. sprintf(buf, "Base tex height %d\n", pow2Height);
  1966. DEBUG_LOG((buf));
  1967. REF_PTR_RELEASE(m_alphaTerrainTex);
  1968. m_alphaTerrainTex = MSGNEW("WorldHeightMap_getTerrainTexture") AlphaTerrainTextureClass(m_terrainTex);
  1969. pow2Height = 1;
  1970. while (pow2Height<edgeHeight) {
  1971. pow2Height *=2;
  1972. }
  1973. REF_PTR_RELEASE(m_alphaEdgeTex);
  1974. m_alphaEdgeTex = MSGNEW("WorldHeightMap_getTerrainTexture") AlphaEdgeTextureClass(pow2Height);
  1975. m_alphaEdgeHeight = m_alphaEdgeTex->update(this);
  1976. //Generate lookup table for determining triangle order in each terrain cell.
  1977. //Not the best place to put this but getAlphaUVData() requires a valid terrain
  1978. //texture to return valid values.
  1979. for (Int y=0; y<(m_height-1); y++)
  1980. for (Int x=0; x<(m_width-1); x++)
  1981. {
  1982. UnsignedByte alpha[4];
  1983. float UA[4], VA[4];
  1984. Bool flipForBlend;
  1985. getAlphaUVData(x, y, UA, VA, alpha, &flipForBlend, false);
  1986. m_cellFlipState[y*m_flipStateWidth+(x>>3)] |= flipForBlend << (x & 0x7);
  1987. DEBUG_ASSERTCRASH ((y*m_flipStateWidth+(x>>3)) < (m_flipStateWidth * m_height), ("Bad range"));
  1988. }
  1989. }
  1990. return m_terrainTex;
  1991. }
  1992. TextureClass *WorldHeightMap::getAlphaTerrainTexture(void)
  1993. {
  1994. if (m_alphaTerrainTex == NULL) {
  1995. getTerrainTexture();
  1996. }
  1997. return m_alphaTerrainTex;
  1998. }
  1999. TextureClass *WorldHeightMap::getEdgeTerrainTexture(void)
  2000. {
  2001. if (m_alphaEdgeTex == NULL) {
  2002. getTerrainTexture();
  2003. }
  2004. return m_alphaEdgeTex;
  2005. }
  2006. TerrainTextureClass *WorldHeightMap::getFlatTexture(Int xCell, Int yCell, Int cellWidth, Int pixelsPerCell)
  2007. {
  2008. if (TheWritableGlobalData->m_textureReductionFactor) {
  2009. if (TheWritableGlobalData->m_textureReductionFactor>1) {
  2010. pixelsPerCell /= 4;
  2011. } else {
  2012. pixelsPerCell /= 2;
  2013. }
  2014. }
  2015. Int pow2Height = 1;
  2016. while (pow2Height<cellWidth*pixelsPerCell) {
  2017. pow2Height *=2;
  2018. }
  2019. TerrainTextureClass *newTexture = MSGNEW("WorldHeightMap_getTerrainTexture") TerrainTextureClass(pow2Height, pow2Height);
  2020. newTexture->updateFlat(this, xCell, yCell, cellWidth, pixelsPerCell);
  2021. return newTexture;
  2022. }
  2023. Bool WorldHeightMap::setDrawOrg(Int xOrg, Int yOrg)
  2024. {
  2025. Int newX, newY;
  2026. Int newWidth, newHeight;
  2027. newX = xOrg;
  2028. newY = yOrg;
  2029. newWidth = m_drawWidthX;
  2030. newHeight = m_drawHeightY;
  2031. if (TheGlobalData && TheGlobalData->m_stretchTerrain) {
  2032. newWidth=STRETCH_DRAW_WIDTH;
  2033. newHeight=STRETCH_DRAW_HEIGHT;
  2034. }
  2035. if (TheGlobalData && TheGlobalData->m_drawEntireTerrain) {
  2036. newWidth=m_width;
  2037. newHeight=m_height;
  2038. }
  2039. if (newWidth > m_width) newWidth = m_width;
  2040. if (newHeight > m_height) newHeight = m_height;
  2041. if (newX > m_width - newWidth) newX = m_width-newWidth;
  2042. if (newX<0) newX=0;
  2043. if (newY > m_height - newHeight) newY = m_height - newHeight;
  2044. if (newY<0) newY=0;
  2045. Bool anythingDifferent = (m_drawOriginX!=newX) ||
  2046. (m_drawOriginY!=newY) ||
  2047. (m_drawWidthX!=newWidth) ||
  2048. (m_drawHeightY!=newHeight) ;
  2049. if (anythingDifferent) {
  2050. m_drawOriginX=newX;
  2051. m_drawOriginY=newY;
  2052. m_drawWidthX=newWidth;
  2053. m_drawHeightY=newHeight;
  2054. return(true);
  2055. }
  2056. return(false);
  2057. }
  2058. /** Gets global texture class. */
  2059. Int WorldHeightMap::getTextureClass(Int xIndex, Int yIndex, Bool baseClass)
  2060. {
  2061. Int ndx = (yIndex*m_width)+xIndex;
  2062. DEBUG_ASSERTCRASH((ndx>=0 && ndx<this->m_dataSize),("oops"));
  2063. if (ndx<0 || ndx >= this->m_dataSize) return(-1);
  2064. Int textureNdx = m_tileNdxes[ndx];
  2065. if (!baseClass && (m_blendTileNdxes[ndx] != 0 || m_extraBlendTileNdxes[ndx] != 0)) {
  2066. return(-1); // blended, so not of the original class.
  2067. }
  2068. return getTextureClassFromNdx(textureNdx);
  2069. }
  2070. /** Sets all the cliff flags in map based on height. */
  2071. void WorldHeightMap::initCliffFlagsFromHeights()
  2072. {
  2073. Int xIndex, yIndex;
  2074. for (xIndex=0; xIndex<m_width-1; xIndex++) {
  2075. for (yIndex=0; yIndex<m_height-1; yIndex++) {
  2076. setCellCliffFlagFromHeights(xIndex, yIndex);
  2077. }
  2078. }
  2079. }
  2080. /** Sets the cliff flag for a cell based on height. */
  2081. void WorldHeightMap::setCellCliffFlagFromHeights(Int xIndex, Int yIndex)
  2082. {
  2083. Real height1 = getHeight(xIndex, yIndex)*MAP_HEIGHT_SCALE;
  2084. Real height2 = getHeight(xIndex+1, yIndex)*MAP_HEIGHT_SCALE;
  2085. Real height3 = getHeight(xIndex, yIndex+1)*MAP_HEIGHT_SCALE;
  2086. Real height4 = getHeight(xIndex+1, yIndex+1)*MAP_HEIGHT_SCALE;
  2087. Real minZ = height1;
  2088. if (minZ > height2) minZ = height2;
  2089. if (minZ > height3) minZ = height3;
  2090. if (minZ > height4) minZ = height4;
  2091. Real maxZ = height1;
  2092. if (maxZ < height2) maxZ = height2;
  2093. if (maxZ < height3) maxZ = height3;
  2094. if (maxZ < height4) maxZ = height4;
  2095. const Real cliffRange = PATHFIND_CLIFF_SLOPE_LIMIT_F;
  2096. Bool isCliff = (maxZ-minZ > cliffRange);
  2097. setCliffState(xIndex, yIndex, isCliff);
  2098. }
  2099. /** Gets global texture class. */
  2100. Int WorldHeightMap::getTextureClassFromNdx(Int tileNdx)
  2101. {
  2102. Int i;
  2103. tileNdx = tileNdx>>2;
  2104. for (i=0; i<m_numTextureClasses; i++) {
  2105. if (m_textureClasses[i].firstTile<0) {
  2106. continue;
  2107. }
  2108. // see if the blend tile is in a texture class, and get the right tile for xIndex, yIndex.
  2109. if (tileNdx >= m_textureClasses[i].firstTile &&
  2110. tileNdx < m_textureClasses[i].firstTile+m_textureClasses[i].numTiles) {
  2111. return(m_textureClasses[i].globalTextureClass);
  2112. }
  2113. }
  2114. return(-1);
  2115. }
  2116. TXTextureClass WorldHeightMap::getTextureFromIndex( Int textureIndex )
  2117. {
  2118. return m_textureClasses[textureIndex];
  2119. }
  2120. void WorldHeightMap::getTerrainColorAt(Real x, Real y, RGBColor *pColor)
  2121. {
  2122. Int xIndex = REAL_TO_INT_FLOOR(x/MAP_XY_FACTOR);
  2123. Int yIndex = REAL_TO_INT_FLOOR(y/MAP_XY_FACTOR);
  2124. xIndex += m_borderSize;
  2125. yIndex += m_borderSize;
  2126. pColor->red = pColor->green = pColor->blue = 0;
  2127. if (xIndex<0) xIndex = 0;
  2128. if (yIndex<0) yIndex = 0;
  2129. if (xIndex >= m_width) xIndex = m_width-1;
  2130. if (yIndex >= m_height) yIndex = m_height-1;
  2131. Int ndx = (yIndex*m_width)+xIndex;
  2132. if (ndx<0 || ndx >= this->m_dataSize) return;
  2133. Int tileNdx = m_tileNdxes[ndx];
  2134. tileNdx = tileNdx>>2; // We pack 4 grids into a tile.
  2135. TileData *pTile = getSourceTile(tileNdx);
  2136. if (pTile) {
  2137. // pTile contains the bitmap data for 4 squares.
  2138. // Get the data mipped down to one pixel for the tile.
  2139. UnsignedByte *pData = pTile->getRGBDataForWidth(1);
  2140. // Data is in microsoft bgra format.
  2141. pColor->red = pData[2]/255.0;
  2142. pColor->green = pData[1]/255.0;
  2143. pColor->blue = pData[0]/255.0;
  2144. }
  2145. }
  2146. AsciiString WorldHeightMap::getTerrainNameAt(Real x, Real y)
  2147. {
  2148. Int xIndex = REAL_TO_INT_FLOOR(x/MAP_XY_FACTOR);
  2149. Int yIndex = REAL_TO_INT_FLOOR(y/MAP_XY_FACTOR);
  2150. xIndex += m_borderSize;
  2151. yIndex += m_borderSize;
  2152. if (xIndex<0) xIndex = 0;
  2153. if (yIndex<0) yIndex = 0;
  2154. if (xIndex >= m_width) xIndex = m_width-1;
  2155. if (yIndex >= m_height) yIndex = m_height-1;
  2156. Int ndx = (yIndex*m_width)+xIndex;
  2157. if (ndx<0 || ndx >= this->m_dataSize) return AsciiString::TheEmptyString;
  2158. Int tileNdx = m_tileNdxes[ndx];
  2159. tileNdx = tileNdx>>2; // We pack 4 grids into a tile.
  2160. Int i;
  2161. for (i=0; i<this->m_numTextureClasses; i++) {
  2162. if (tileNdx >= m_textureClasses[i].firstTile && tileNdx < m_textureClasses[i].firstTile + m_textureClasses[i].numTiles) {
  2163. return(m_textureClasses[i].name);
  2164. }
  2165. }
  2166. return AsciiString::TheEmptyString;
  2167. }
  2168. static UnsignedByte s_buffer[DATA_LEN_BYTES];
  2169. static UnsignedByte s_blendBuffer[DATA_LEN_BYTES];
  2170. UnsignedByte * WorldHeightMap::getPointerToTileData(Int xIndex, Int yIndex, Int width)
  2171. {
  2172. Int ndx = (yIndex*m_width)+xIndex;
  2173. if (yIndex<0 || xIndex<0 || xIndex>=m_width || yIndex>=m_height) {
  2174. return NULL;
  2175. }
  2176. if (ndx<0 || ndx>=m_dataSize) {
  2177. return NULL;
  2178. }
  2179. TBlendTileInfo *pBlend = NULL;
  2180. Short tileNdx = m_tileNdxes[ndx];
  2181. if (getRawTileData(tileNdx, width, s_buffer, DATA_LEN_BYTES)) {
  2182. Short blendTileNdx = m_blendTileNdxes[ndx];
  2183. if (blendTileNdx>0 && blendTileNdx < NUM_BLEND_TILES) {
  2184. pBlend = &m_blendedTiles[blendTileNdx];
  2185. if (getRawTileData(pBlend->blendNdx, width, s_blendBuffer, DATA_LEN_BYTES)) {
  2186. UnsignedByte *pAlpha = getRGBAlphaDataForWidth(width, pBlend);
  2187. pAlpha += 3; // skip over the rgb to the a.
  2188. Int i, limit;
  2189. limit = width*width;
  2190. UnsignedByte *pBlendData = s_blendBuffer;
  2191. UnsignedByte *pDestData = s_buffer;
  2192. for (i=0; i<limit; i++) {
  2193. Int r,g,b,a;
  2194. b = *pBlendData++;
  2195. g = *pBlendData++;
  2196. r = *pBlendData++; *pBlendData++;
  2197. a = *pAlpha; pAlpha += 4;
  2198. *pDestData++ = ((b*a)/255) + (((*pDestData)*(255-a))/255);
  2199. *pDestData++ = ((g*a)/255) + (((*pDestData)*(255-a))/255);
  2200. *pDestData++ = ((r*a)/255) + (((*pDestData)*(255-a))/255);
  2201. *pDestData++ = 255; // just skip alpha. not really used. jba.
  2202. }
  2203. }
  2204. }
  2205. return(s_buffer);
  2206. }
  2207. return(NULL);
  2208. }
  2209. #define K_HORIZ 0
  2210. #define K_VERT 1
  2211. #define K_LDIAG 2
  2212. #define K_RDIAG 3
  2213. #define K_LLDIAG 4
  2214. #define K_LRDIAG 5
  2215. #define K_DIR_MOD 0x05
  2216. #define K_INV 6
  2217. UnsignedByte *WorldHeightMap::getRGBAlphaDataForWidth(Int width, TBlendTileInfo *pBlend)
  2218. {
  2219. Int alphaTileNdx = 0;
  2220. if (pBlend->horiz) {
  2221. alphaTileNdx = K_HORIZ;
  2222. } else if (pBlend->vert) {
  2223. alphaTileNdx = K_VERT;
  2224. } else if (pBlend->rightDiagonal) {
  2225. alphaTileNdx = K_RDIAG;
  2226. if (pBlend->longDiagonal) alphaTileNdx=K_LRDIAG;
  2227. } else if (pBlend->leftDiagonal) {
  2228. alphaTileNdx = K_LDIAG;
  2229. if (pBlend->longDiagonal) alphaTileNdx=K_LLDIAG;
  2230. }
  2231. if (pBlend->inverted) {
  2232. alphaTileNdx += K_INV;
  2233. }
  2234. return m_alphaTiles[alphaTileNdx]->getRGBDataForWidth(width);
  2235. }
  2236. void WorldHeightMap::setupAlphaTiles(void)
  2237. {
  2238. TBlendTileInfo blendInfo;
  2239. if (m_alphaTiles[0] != NULL) return;
  2240. Int k;
  2241. for (k=0; k<NUM_ALPHA_TILES; k++) {
  2242. memset(&blendInfo, 0, sizeof(blendInfo));
  2243. Int baseK = k;
  2244. if (k>=K_INV) {
  2245. blendInfo.inverted = true;
  2246. baseK -= K_INV;
  2247. }
  2248. switch(baseK) {
  2249. case K_HORIZ : blendInfo.horiz = true; break;
  2250. case K_VERT : blendInfo.vert = true; break;
  2251. case K_LDIAG : blendInfo.leftDiagonal = true; break;
  2252. case K_RDIAG : blendInfo.rightDiagonal = true; break;
  2253. case K_LLDIAG : blendInfo.leftDiagonal = true; blendInfo.longDiagonal = true; break;
  2254. case K_LRDIAG : blendInfo.rightDiagonal = true; blendInfo.longDiagonal = true; break;
  2255. } // end of case.
  2256. m_alphaTiles[k] = new TileData;
  2257. TileData *pTile = m_alphaTiles[k];
  2258. Int i, j;
  2259. UnsignedByte *pDest = pTile->getDataPtr();
  2260. for (j=0; j<TILE_PIXEL_EXTENT; j++) {
  2261. for (i=0; i<TILE_PIXEL_EXTENT; i++) {
  2262. Int h = i;
  2263. Int v = j;
  2264. Int alpha = 255; // 0 - 255.
  2265. if (blendInfo.horiz) {
  2266. if (!blendInfo.inverted) h = TILE_PIXEL_EXTENT-h-1;
  2267. alpha = (alpha*h)/(TILE_PIXEL_EXTENT-1);
  2268. } else if (blendInfo.vert) {
  2269. if (!blendInfo.inverted) v = TILE_PIXEL_EXTENT-v-1;
  2270. alpha = (alpha*v)/(TILE_PIXEL_EXTENT-1);
  2271. } else if (blendInfo.rightDiagonal) {
  2272. h = TILE_PIXEL_EXTENT-h-1;
  2273. if (!blendInfo.inverted) v = TILE_PIXEL_EXTENT-v-1;
  2274. v += h; // angled
  2275. if (blendInfo.longDiagonal) {
  2276. v -= TILE_PIXEL_EXTENT;
  2277. }
  2278. alpha = (alpha*v)/(TILE_PIXEL_EXTENT-1);
  2279. } else if (blendInfo.leftDiagonal) {
  2280. if (!blendInfo.inverted) v = TILE_PIXEL_EXTENT-v-1;
  2281. v += h; // angled
  2282. if (blendInfo.longDiagonal) {
  2283. v -= TILE_PIXEL_EXTENT;
  2284. }
  2285. alpha = (alpha*v)/(TILE_PIXEL_EXTENT-1);
  2286. }
  2287. if (alpha > 255) alpha = 255;
  2288. if (alpha<0) alpha = 0;
  2289. alpha = 255-alpha;
  2290. pDest += 3; // skip blue, green & red bytes.
  2291. *pDest = alpha; // alpha.
  2292. //*pDest = 255;
  2293. pDest++;
  2294. }
  2295. }
  2296. pTile->updateMips();
  2297. }
  2298. }
  2299. Bool WorldHeightMap::getRawTileData(Short tileNdx, Int width,
  2300. UnsignedByte *buffer, Int bufLen)
  2301. {
  2302. TileData *pSrc = NULL;
  2303. if (tileNdx/4 < NUM_SOURCE_TILES) {
  2304. pSrc = m_sourceTiles[tileNdx/4];
  2305. }
  2306. if (bufLen < (width*width*TILE_BYTES_PER_PIXEL)) {
  2307. return(false);
  2308. }
  2309. if (pSrc && pSrc->hasRGBDataForWidth(2*width)) {
  2310. Int j;
  2311. UnsignedByte *pSrcData = pSrc->getRGBDataForWidth(2*width);
  2312. Int xOffset=0;
  2313. Int yOffset=0;
  2314. if (tileNdx & 1) xOffset = width;
  2315. if (tileNdx & 2) yOffset = width;
  2316. for (j=0; j<width; j++) {
  2317. UnsignedByte *pDestData = buffer;
  2318. pDestData += j*(width)*TILE_BYTES_PER_PIXEL;
  2319. UnsignedByte *pSrc = pSrcData;
  2320. pSrc += (j+yOffset)*width*TILE_BYTES_PER_PIXEL*2;
  2321. pSrc += xOffset*TILE_BYTES_PER_PIXEL;
  2322. memcpy(pDestData, pSrc, width*TILE_BYTES_PER_PIXEL);
  2323. }
  2324. return(true);
  2325. }
  2326. return(false);
  2327. }