WorldHeightMap.cpp 67 KB

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