LightMapPacker.cpp 150 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387
  1. /*
  2. ** Command & Conquer Renegade(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. *** Confidential - Westwood Studios ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : LightMap *
  23. * *
  24. * $Archive:: /Commando/Code/Tool $*
  25. * *
  26. * $Author:: Ian_l $*
  27. * *
  28. * $Modtime:: 7/09/01 4:30p $*
  29. * *
  30. * $Revision:: 50 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. // Includes.
  36. #include "StdAfx.h"
  37. #include "LightMap.h"
  38. #include "LightmapPacker.h"
  39. #include "OptionsDialog.h"
  40. #include "StringBuilder.h"
  41. #include "Targa.h"
  42. #include "TextureNameNode.h"
  43. #include "srFilter.hpp"
  44. #include <direct.h>
  45. #include <io.h>
  46. #include <stdlib.h>
  47. #include <winbase.h>
  48. // Defines.
  49. #define TEMPORARY_DIRECTORY_NAME "~L" // Name of temporary directory used to store packed lightmaps.
  50. #define PAGE_WIDTH 256 // Width & height of page in texels.
  51. #define PAGE_HEIGHT 256 // NOTE: Elected to use the biggest texture size that is
  52. // supported by the gamut of PC 3D graphics hardware in order
  53. // to maximize no. of lightmaps packed per page and therefore
  54. // minimize no. of texture swaps per rendered polygon.
  55. #define PACKING_BYTES (1024 * 1024) // No. of page bytes to consider for packing the current lightmap.
  56. #define ASSET_EXTENSION ".tga" // Extension (and file type) of lightmap assets.
  57. // Static data.
  58. unsigned LightmapPacker::_BasePageIndex = 0;
  59. char LightmapPacker::_Statistics [LightmapPacker::STATISTICS_COUNT][LightmapPacker::STATISTICS_STRING_SIZE] = {
  60. "No data available",
  61. "No data available",
  62. "No data available",
  63. "No data available",
  64. "No data available",
  65. "No data available",
  66. "No data available",
  67. "No data available",
  68. "No data available",
  69. "No data available"
  70. };
  71. /***********************************************************************************************
  72. * LightmapPacker::LightmapPacker -- *
  73. * *
  74. * INPUT: *
  75. * *
  76. * OUTPUT: *
  77. * *
  78. * WARNINGS: *
  79. * *
  80. * HISTORY: *
  81. * 8/18/99 IML : Created. *
  82. *=============================================================================================*/
  83. LightmapPacker::LightmapPacker()
  84. {
  85. char pathname [_MAX_PATH];
  86. OptionsDialog options;
  87. // Initialize.
  88. FillColor = options.Get_Fill_Color();
  89. FilterError = pow (options.Get_Filter_Error() * 4.919e-3l, 2.5l); // Map s.t. 50th percentile -> 0.03% RGB error.
  90. SampleRate = options.Get_Sample_Rate();
  91. PageBitDepth = options.Get_Bit_Depth();
  92. CurrentPageIndex = -1;
  93. Reset_Statistics();
  94. // Recreate the asset directory.
  95. strcpy (pathname, theApp.Working_Path());
  96. strcat (pathname, Asset_Directory());
  97. // Can the asset directory can be successfully created?
  98. if (_mkdir (pathname) == 0) {
  99. _BasePageIndex = 0;
  100. }
  101. // Create the placeholder texture name node.
  102. strcpy (pathname, theApp.Working_Path());
  103. strcat (pathname, "Placeholder.tga");
  104. PlaceholderTextureNameNodePtr = new TextureNameNode (pathname);
  105. ASSERT (PlaceholderTextureNameNodePtr != NULL);
  106. }
  107. /***********************************************************************************************
  108. * LightmapPacker::Finish -- *
  109. * *
  110. * INPUT: *
  111. * *
  112. * OUTPUT: *
  113. * *
  114. * WARNINGS: *
  115. * *
  116. * HISTORY: *
  117. * 8/18/99 IML : Created. *
  118. *=============================================================================================*/
  119. LightmapPacker::Finish()
  120. {
  121. // For each page...
  122. for (int pageindex = 0; pageindex < PagePtrs.Count(); pageindex++) {
  123. char pagepathname [_MAX_PATH];
  124. char *errormessage;
  125. // Pad fill color.
  126. PagePtrs [pageindex]->Fill (FillColor);
  127. // Update statistics about the page.
  128. Update_Statistics (*PagePtrs [pageindex]);
  129. // Save the page. Report any error.
  130. strcpy (pagepathname, theApp.Working_Path());
  131. strcat (pagepathname, Lightmap_Pathname (pageindex));
  132. errormessage = PagePtrs [pageindex]->Save (pagepathname);
  133. if (errormessage != NULL) throw (errormessage);
  134. }
  135. _BasePageIndex += PagePtrs.Count();
  136. Collate_Statistics();
  137. }
  138. /***********************************************************************************************
  139. * LightmapPacker::~LightmapPacker -- *
  140. * *
  141. * INPUT: *
  142. * *
  143. * OUTPUT: *
  144. * *
  145. * WARNINGS: Do not throw any exceptions in this (and any other) destructor because this *
  146. * destructor can itself be called during the exception as part of its 'clean-up'. *
  147. * *
  148. * HISTORY: *
  149. * 8/18/99 IML : Created. *
  150. *=============================================================================================*/
  151. LightmapPacker::~LightmapPacker()
  152. {
  153. // For each page...
  154. for (int pageindex = 0; pageindex < PagePtrs.Count(); pageindex++) {
  155. delete PagePtrs [pageindex];
  156. }
  157. delete PlaceholderTextureNameNodePtr;
  158. }
  159. /***********************************************************************************************
  160. * LightmapPacker::Submit -- Submit a triangular region of a lightmap for packing. *
  161. * *
  162. * INPUT: *
  163. * *
  164. * OUTPUT: *
  165. * *
  166. * WARNINGS: *
  167. * *
  168. * HISTORY: *
  169. * 07/28/00 IML : Created. *
  170. *=============================================================================================*/
  171. void LightmapPacker::Submit (PackingTriangle *principaltriangleptr, const DynamicVectorClass <Triangle> &adjtriangles)
  172. {
  173. const unsigned normalgroupid = 0;
  174. const unsigned placeholdergroupid = 1;
  175. TrianglePacker *trianglepackerptr, *mergedpackerptr;
  176. ASSERT (principaltriangleptr != NULL);
  177. // Is there an associated texture?
  178. if (principaltriangleptr->TextureNameNodePtr == NULL) {
  179. static const float _placeholderuvs [Triangle::VERTICES_COUNT][2] =
  180. {
  181. {0.0f, 0.0f},
  182. {1.0f, 0.0f},
  183. {0.0f, 1.0f},
  184. };
  185. DynamicVectorClass <Triangle> noadjtriangles;
  186. // Substitute a placeholder texture.
  187. principaltriangleptr->TextureNameNodePtr = PlaceholderTextureNameNodePtr;
  188. for (unsigned v = 0; v < Triangle::VERTICES_COUNT; v++) {
  189. principaltriangleptr->Vertices [v].UV.X = _placeholderuvs [v][0];
  190. principaltriangleptr->Vertices [v].UV.Y = _placeholderuvs [v][1];
  191. }
  192. // Create a triangle packer from the modified principal triangle with no adjacent triangles.
  193. // NOTE: Use a 'placeholder group' ID to indicate to the triangle packer that it should not be merged with non-placeholder triangles.
  194. trianglepackerptr = new TrianglePacker (principaltriangleptr, noadjtriangles, placeholdergroupid, EDGE_BLEND_THICKNESS, SampleRate, FillColor);
  195. ASSERT (trianglepackerptr != NULL);
  196. } else {
  197. // Create a triangle packer from the principal and adjacent triangles.
  198. trianglepackerptr = new TrianglePacker (principaltriangleptr, adjtriangles, normalgroupid, EDGE_BLEND_THICKNESS, SampleRate, FillColor);
  199. ASSERT (trianglepackerptr != NULL);
  200. }
  201. while (trianglepackerptr != NULL) {
  202. mergedpackerptr = trianglepackerptr;
  203. trianglepackerptr = Merge (mergedpackerptr);
  204. }
  205. TrianglePackerPtrs.Add (mergedpackerptr);
  206. // Statistics: each submission contains one triangular principal face.
  207. FaceCount++;
  208. }
  209. /***********************************************************************************************
  210. * LightmapPacker::Merge -- *
  211. * *
  212. * INPUT: *
  213. * *
  214. * OUTPUT: *
  215. * *
  216. * WARNINGS: *
  217. * *
  218. * HISTORY: *
  219. * 07/28/00 IML : Created. *
  220. *=============================================================================================*/
  221. TrianglePacker *LightmapPacker::Merge (TrianglePacker *trianglepackerptr)
  222. {
  223. TrianglePacker *resultantpackerptr;
  224. // See if the triangle packer can be merged with an existing triangle packer.
  225. resultantpackerptr = NULL;
  226. for (int t = 0; t < TrianglePackerPtrs.Count(); t++) {
  227. TrianglePacker *mergedpackerptr;
  228. mergedpackerptr = TrianglePackerPtrs [t]->Merge (*trianglepackerptr);
  229. if (mergedpackerptr != NULL) {
  230. // Does the merged packer fit within the maximum page size?
  231. if ((mergedpackerptr->Width() <= PAGE_WIDTH) && (mergedpackerptr->Height() <= PAGE_HEIGHT)) {
  232. unsigned mergedarea, unmergedarea;
  233. // Is the merged packer smaller in area than the sum of the two unmerged packers?
  234. mergedarea = mergedpackerptr->Width() * mergedpackerptr->Height();
  235. unmergedarea = (trianglepackerptr->Width() * trianglepackerptr->Height()) + (TrianglePackerPtrs [t]->Width() * TrianglePackerPtrs [t]->Height());
  236. if (mergedarea <= unmergedarea) {
  237. bool success;
  238. // Replace the two unmerged packers with the merged packer.
  239. delete trianglepackerptr;
  240. delete TrianglePackerPtrs [t];
  241. success = TrianglePackerPtrs.Delete (t);
  242. ASSERT (success);
  243. resultantpackerptr = mergedpackerptr;
  244. break;
  245. }
  246. }
  247. }
  248. }
  249. return (resultantpackerptr);
  250. }
  251. /***********************************************************************************************
  252. * LightmapPacker::Pack -- Pack all the triangular regions submitted thus far. *
  253. * *
  254. * INPUT: *
  255. * *
  256. * OUTPUT: *
  257. * *
  258. * WARNINGS: *
  259. * *
  260. * HISTORY: *
  261. * 05/18/00 IML : Created. *
  262. *=============================================================================================*/
  263. void LightmapPacker::Pack (ProceduralTexture *proceduraltexture)
  264. {
  265. TargaLoader targaloader (SampleRate, FillColor);
  266. // For each triangle packer...
  267. for (int t = 0; t < TrianglePackerPtrs.Count(); t++) {
  268. const unsigned rescalethreshold = 16; // When snapping to even dimension, 'round down' if above this size -
  269. // otherwise 'round up'.
  270. TrueColorTarga rasterizedtarga;
  271. unsigned unscaledwidth, unscaledheight;
  272. unsigned clampedwidth, clampedheight;
  273. unsigned padwidth, padheight;
  274. // Rasterize the triangle packer.
  275. TrianglePackerPtrs [t]->Rasterize (targaloader, proceduraltexture, rasterizedtarga);
  276. // Scale targa according to user-specified filter error.
  277. unscaledwidth = rasterizedtarga.Width();
  278. unscaledheight = rasterizedtarga.Height();
  279. rasterizedtarga.Scale (FilterError);
  280. // Scale targa to maximum page size (taking into account edge thickness).
  281. ASSERT (PAGE_WIDTH > (EDGE_BLEND_THICKNESS * 2));
  282. ASSERT (PAGE_HEIGHT > (EDGE_BLEND_THICKNESS * 2));
  283. clampedwidth = MIN (PAGE_WIDTH - (EDGE_BLEND_THICKNESS * 2), rasterizedtarga.Width());
  284. clampedheight = MIN (PAGE_HEIGHT - (EDGE_BLEND_THICKNESS * 2), rasterizedtarga.Height());
  285. if ((clampedwidth < rasterizedtarga.Width()) || (clampedheight < rasterizedtarga.Height())) {
  286. // Statistics: update no. of oversize targas.
  287. OversizeCount++;
  288. }
  289. // Snap to even dimensions.
  290. // NOTE: This will reduce texel bleeding as a side effect of mip-mapping or rendering at reduced resolutions.
  291. ASSERT (rescalethreshold < PAGE_WIDTH);
  292. ASSERT (rescalethreshold < PAGE_HEIGHT);
  293. if (clampedwidth > rescalethreshold) {
  294. clampedwidth -= (clampedwidth & 0x1);
  295. } else {
  296. clampedwidth += (clampedwidth & 0x1);
  297. }
  298. if (clampedheight > rescalethreshold) {
  299. clampedheight -= (clampedheight & 0x1);
  300. } else {
  301. clampedheight += (clampedheight & 0x1);
  302. }
  303. if (clampedwidth != rasterizedtarga.Width() || clampedheight != rasterizedtarga.Height()) {
  304. rasterizedtarga.Scale (clampedwidth, clampedheight);
  305. }
  306. // Add pad texels to compensate for edge texels that may have been lost due to filtered scaling and scaling to clamp to maximum page size.
  307. // NOTE: Pad to a multiple of 2 to maintain 'even' dimensions.
  308. padwidth = 2 * ceilf (EDGE_BLEND_THICKNESS * (1.0f - (((float) rasterizedtarga.Width()) / ((float) unscaledwidth))));
  309. padheight = 2 * ceilf (EDGE_BLEND_THICKNESS * (1.0f - (((float) rasterizedtarga.Height()) / ((float) unscaledheight))));
  310. rasterizedtarga.Pad (padwidth, padheight, FillColor, TrianglePackerPtrs [t]->Principal_Triangles());
  311. // Pack the targa into a lightmap page.
  312. Pack (rasterizedtarga, TrianglePackerPtrs [t]->Principal_Triangles());
  313. // Statistics: add targa's area before and after scaling.
  314. // NOTE: Although this is not desirable, it is possible for scaling to increase the targa's area.
  315. UnscaledTexelCount += unscaledwidth * unscaledheight;
  316. ScaledTexelCount += rasterizedtarga.Width() * rasterizedtarga.Height();
  317. // Update statistics about the triangle packer.
  318. Update_Statistics (*TrianglePackerPtrs [t]);
  319. // Clean-up.
  320. delete TrianglePackerPtrs [t];
  321. }
  322. // Reset.
  323. TrianglePackerPtrs.Clear();
  324. }
  325. /***********************************************************************************************
  326. * TargaLoader::TargaLoader -- *
  327. * *
  328. * INPUT: *
  329. * *
  330. * OUTPUT: *
  331. * *
  332. * WARNINGS: *
  333. * *
  334. * HISTORY: *
  335. * 07/30/00 IML : Created. *
  336. *=============================================================================================*/
  337. TargaLoader::TargaLoader (float samplerate, W3dRGBStruct fillcolor)
  338. {
  339. const unsigned cachesize = 1024; // Maximum no. of entries in cache.
  340. const unsigned cleanuplistsize = 256; // Initial size of clean-up list.
  341. SampleRate = samplerate;
  342. FillColor = fillcolor;
  343. // Size the cache and the clean-up list.
  344. Cache.Resize (cachesize);
  345. CleanupList.Set_Growth_Step (cleanuplistsize);
  346. }
  347. /***********************************************************************************************
  348. * TargaLoader::~TargaLoader -- *
  349. * *
  350. * INPUT: *
  351. * *
  352. * OUTPUT: *
  353. * *
  354. * WARNINGS: *
  355. * *
  356. * HISTORY: *
  357. * 07/30/00 IML : Created. *
  358. *=============================================================================================*/
  359. TargaLoader::~TargaLoader()
  360. {
  361. // Delete the targas in the clean-up list.
  362. for (int c = 0; c < CleanupList.Count(); c++) {
  363. ASSERT (CleanupList [c] != NULL);
  364. delete CleanupList [c];
  365. }
  366. }
  367. /***********************************************************************************************
  368. * TargaLoader::Load -- *
  369. * *
  370. * INPUT: *
  371. * *
  372. * OUTPUT: *
  373. * *
  374. * WARNINGS: *
  375. * *
  376. * HISTORY: *
  377. * 06/08/00 IML : Created. *
  378. *=============================================================================================*/
  379. TrueColorTarga *TargaLoader::Load (const Triangle &triangle)
  380. {
  381. TrueColorTarga *targaptr;
  382. unsigned index;
  383. index = Index (triangle.TextureID);
  384. // Is there a cache miss?
  385. if ((Cache [index].Ptr == NULL) || (Cache [index].ID != triangle.TextureID)) {
  386. char *errormessage;
  387. TextureNameNode *pathnamenodeptr;
  388. Vector2 t;
  389. Vector3 p;
  390. float length, localsamplerate, scalefactor;
  391. // There must be a texture pathname.
  392. pathnamenodeptr = triangle.TextureNameNodePtr;
  393. ASSERT (pathnamenodeptr != NULL);
  394. targaptr = new TrueColorTarga;
  395. ASSERT (targaptr != NULL);
  396. // Read the first targa asset file. Report any error.
  397. errormessage = targaptr->Load (pathnamenodeptr->TextureName);
  398. if (errormessage != NULL) throw (errormessage);
  399. // Replace fill texel with padding. If the targa is all fill color substitute the replacement targa.
  400. targaptr->Fill (FillColor);
  401. // Read all remaining targa asset files and add them to the first.
  402. pathnamenodeptr = pathnamenodeptr->Next;
  403. while (pathnamenodeptr != NULL) {
  404. TrueColorTarga additivetarga;
  405. errormessage = additivetarga.Load (pathnamenodeptr->TextureName);
  406. if (errormessage != NULL) throw (errormessage);
  407. // Replace fill texel with padding.
  408. additivetarga.Fill (FillColor);
  409. targaptr->Add (additivetarga);
  410. // Advance to next targa asset.
  411. pathnamenodeptr = pathnamenodeptr->Next;
  412. }
  413. // Calculate the sample rate of the targa (no. of texels per unit length).
  414. // NOTE: A typical length unit is metres (it does not matter which it is).
  415. t = triangle.Vertices [1].UV - triangle.Vertices [0].UV;
  416. t.Scale (targaptr->Width(), targaptr->Height());
  417. p = triangle.Vertices [1].Point - triangle.Vertices [0].Point;
  418. length = p.Length();
  419. // Degenerate triangles are not allowed!
  420. ASSERT (length > 0.0f);
  421. localsamplerate = t.Length() / length;
  422. // In the event that the targa is sampled, maximize sampling accuracy by scaling
  423. // to match the global sample rate.
  424. // NOTE: There is no advantage to scaling-up.
  425. scalefactor = SampleRate / localsamplerate;
  426. if (scalefactor < 1.0f) {
  427. targaptr->Scale (targaptr->Width() * scalefactor, targaptr->Height() * scalefactor);
  428. }
  429. // Add the existing targa to the clean-up list for removal later (in the destructor).
  430. // NOTE: It cannot be deleted yet because another object may be referencing it.
  431. if (Cache [index].Ptr != NULL) {
  432. CleanupList.Add (Cache [index].Ptr);
  433. }
  434. // Update the cache entry.
  435. Cache [index].ID = triangle.TextureID;
  436. Cache [index].Ptr = targaptr;
  437. } else {
  438. // Cache hit.
  439. targaptr = Cache [index].Ptr;
  440. }
  441. return (targaptr);
  442. }
  443. /***********************************************************************************************
  444. * LightmapPacker::Pack -- Pack a targa into a lightmap page. *
  445. * *
  446. * INPUT: *
  447. * *
  448. * OUTPUT: *
  449. * *
  450. * WARNINGS: *
  451. * *
  452. * HISTORY: *
  453. * 05/19/00 IML : Created. *
  454. *=============================================================================================*/
  455. void LightmapPacker::Pack (TrueColorTarga &targa, DynamicVectorClass <PackingTriangle*> &triangleptrs)
  456. {
  457. const float packingerror = FilterError * 0.5f;
  458. bool packed;
  459. // Attempt to pack the lightmap in the current page.
  460. if (CurrentPageIndex != -1) {
  461. packed = PagePtrs [CurrentPageIndex]->Pack (targa, packingerror, triangleptrs);
  462. } else {
  463. packed = false;
  464. }
  465. if (!packed) {
  466. const unsigned packingwindowsize = PACKING_BYTES / ((PAGE_WIDTH * PAGE_HEIGHT * PageBitDepth) / CHAR_BIT);
  467. int firstpage, lastpage;
  468. // Iterate over all existing pages (except the current page) that are in the packing window.
  469. firstpage = PagePtrs.Count() - 1;
  470. lastpage = MAX (firstpage - ((int) packingwindowsize) + 1, 0);
  471. for (int pageindex = firstpage; pageindex >= lastpage; pageindex--) {
  472. if (pageindex != CurrentPageIndex) {
  473. packed = PagePtrs [pageindex]->Pack (targa, packingerror, triangleptrs);
  474. if (packed) {
  475. CurrentPageIndex = pageindex;
  476. break;
  477. }
  478. }
  479. }
  480. // If still not packed then create a new page.
  481. if (!packed) {
  482. Page *pageptr;
  483. pageptr = new Page (PageBitDepth, FillColor);
  484. ASSERT (pageptr != NULL);
  485. PagePtrs.Add (pageptr);
  486. packed = pageptr->Pack (targa, packingerror, triangleptrs);
  487. // The first lightmap packed into a page must always succeed.
  488. ASSERT (packed);
  489. CurrentPageIndex = PagePtrs.Count() - 1;
  490. }
  491. // The current page index has changed so this will incur a texture swap.
  492. TextureSwapCount++;
  493. }
  494. LightmapCount++;
  495. // Store the page index in the triangles.
  496. for (int t = 0; t < triangleptrs.Count(); t++) {
  497. triangleptrs [t]->PackedTextureID = CurrentPageIndex;
  498. }
  499. }
  500. /***********************************************************************************************
  501. * LightmapPacker::Lightmap_Pathname -- *
  502. * *
  503. * INPUT: *
  504. * *
  505. * OUTPUT: *
  506. * *
  507. * WARNINGS: *
  508. * *
  509. * HISTORY: *
  510. * 8/18/99 IML : Created. *
  511. *=============================================================================================*/
  512. const char *LightmapPacker::Lightmap_Pathname (unsigned pageindex)
  513. {
  514. static char _pathname [_MAX_PATH];
  515. char filename [_MAX_FNAME];
  516. // Build pathname of lightmap based on lightmap's index.
  517. strcpy (_pathname, Asset_Directory());
  518. _ultoa (_BasePageIndex + pageindex, filename, 10);
  519. strcat (filename, ASSET_EXTENSION);
  520. strcat (_pathname, filename);
  521. return (_pathname);
  522. }
  523. /***********************************************************************************************
  524. * LightmapPacker::Asset_Directory -- *
  525. * *
  526. * INPUT: *
  527. * *
  528. * OUTPUT: *
  529. * *
  530. * WARNINGS: *
  531. * *
  532. * HISTORY: *
  533. * 05/15/00 IML : Created. *
  534. *=============================================================================================*/
  535. const char *LightmapPacker::Asset_Directory()
  536. {
  537. static bool _called = false;
  538. static char _assetdirectory [_MAX_PATH];
  539. // Optimization: only required to build asset directory once because it is invariant over
  540. // lifetime of application.
  541. if (!_called) {
  542. strcpy (_assetdirectory, TEMPORARY_DIRECTORY_NAME);
  543. strcat (_assetdirectory, theApp.Instance_Name());
  544. strcat (_assetdirectory, "\\");
  545. _called = true;
  546. }
  547. return (_assetdirectory);
  548. }
  549. /***********************************************************************************************
  550. * LightmapPacker::Asset_Directory -- *
  551. * *
  552. * INPUT: *
  553. * *
  554. * OUTPUT: *
  555. * *
  556. * WARNINGS: *
  557. * *
  558. * HISTORY: *
  559. * 8/18/99 IML : Created. *
  560. *=============================================================================================*/
  561. const char *LightmapPacker::Asset_Directory (const char *filename)
  562. {
  563. static char _pathname [_MAX_PATH];
  564. strcpy (_pathname, filename);
  565. strcat (_pathname, "+\\");
  566. return (_pathname);
  567. }
  568. /***********************************************************************************************
  569. * LightmapPacker::Delete_Assets -- *
  570. * *
  571. * INPUT: *
  572. * *
  573. * OUTPUT: *
  574. * *
  575. * WARNINGS: *
  576. * *
  577. * HISTORY: *
  578. * 8/18/99 IML : Created. *
  579. *=============================================================================================*/
  580. void LightmapPacker::Delete_Assets()
  581. {
  582. char pathname [_MAX_PATH];
  583. long handle;
  584. _finddata_t fileinfo;
  585. // Attempt to delete every file in the asset directory.
  586. strcpy (pathname, theApp.Working_Path());
  587. strcat (pathname, Asset_Directory());
  588. strcat (pathname, "*");
  589. strcat (pathname, ASSET_EXTENSION);
  590. handle = _findfirst (pathname, &fileinfo);
  591. if (handle != -1) {
  592. do {
  593. strcpy (pathname, theApp.Working_Path());
  594. strcat (pathname, Asset_Directory());
  595. strcat (pathname, fileinfo.name);
  596. DeleteFile (pathname);
  597. } while (_findnext (handle, &fileinfo) == 0);
  598. }
  599. // Clean-up.
  600. _findclose (handle);
  601. // Attempt to remove the asset directory.
  602. strcpy (pathname, theApp.Working_Path());
  603. strcat (pathname, Asset_Directory());
  604. _rmdir (pathname);
  605. }
  606. /***********************************************************************************************
  607. * LightmapPacker::Copy_Assets -- *
  608. * *
  609. * INPUT: *
  610. * *
  611. * OUTPUT: *
  612. * *
  613. * WARNINGS: *
  614. * *
  615. * HISTORY: *
  616. * 8/18/99 IML : Created. *
  617. *=============================================================================================*/
  618. void LightmapPacker::Copy_Assets (const char *pathname)
  619. {
  620. char savedrivename [_MAX_DRIVE];
  621. char savedirectoryname [_MAX_DIR];
  622. char savefilename [_MAX_FNAME];
  623. char loadpathname [_MAX_PATH];
  624. char savepath [_MAX_PATH];
  625. char savepathname [_MAX_PATH];
  626. long handle;
  627. _finddata_t fileinfo;
  628. // Attempt to copy every file in the load directory to the save directory.
  629. strcpy (loadpathname, theApp.Working_Path());
  630. strcat (loadpathname, Asset_Directory());
  631. strcat (loadpathname, "*");
  632. strcat (loadpathname, ASSET_EXTENSION);
  633. handle = _findfirst (loadpathname, &fileinfo);
  634. if (handle != -1) {
  635. // Now we know that there are assets so create the save directory.
  636. _splitpath (pathname, savedrivename, savedirectoryname, savefilename, NULL);
  637. strcpy (savepath, savedrivename);
  638. strcat (savepath, savedirectoryname);
  639. strcat (savepath, Asset_Directory (savefilename));
  640. _mkdir (savepath);
  641. do {
  642. strcpy (loadpathname, theApp.Working_Path());
  643. strcat (loadpathname, Asset_Directory());
  644. strcat (loadpathname, fileinfo.name);
  645. strcpy (savepathname, savepath);
  646. strcat (savepathname, fileinfo.name);
  647. CopyFile (loadpathname, savepathname, FALSE);
  648. } while (_findnext (handle, &fileinfo) == 0);
  649. }
  650. // Clean-up.
  651. _findclose (handle);
  652. }
  653. /***********************************************************************************************
  654. * LightmapPacker::Reset_Statistics -- *
  655. * *
  656. * INPUT: *
  657. * *
  658. * OUTPUT: *
  659. * *
  660. * WARNINGS: *
  661. * *
  662. * HISTORY: *
  663. * 8/18/99 IML : Created. *
  664. *=============================================================================================*/
  665. void LightmapPacker::Reset_Statistics()
  666. {
  667. FaceCount = 0;
  668. LightmapCount = 0;
  669. AdjacentFaceCount = 0;
  670. BlendedFaceCount = 0;
  671. EdgeBlendAreaSum = 0.0;
  672. UnscaledTexelCount = 0;
  673. ScaledTexelCount = 0;
  674. ReplicaEfficiencySum = 0.0;
  675. PackingEfficiencySum = 0.0;
  676. TextureSwapCount = 0;
  677. OversizeCount = 0;
  678. AllFillColorCount = 0;
  679. }
  680. /***********************************************************************************************
  681. * LightmapPacker::Update_Statistics -- *
  682. * *
  683. * INPUT: *
  684. * *
  685. * OUTPUT: *
  686. * *
  687. * WARNINGS: *
  688. * *
  689. * HISTORY: *
  690. * 8/18/99 IML : Created. *
  691. *=============================================================================================*/
  692. void LightmapPacker::Update_Statistics (const Page &page)
  693. {
  694. PackingEfficiencySum += page.Packing_Efficiency();
  695. ReplicaEfficiencySum += page.Replica_Efficiency();
  696. }
  697. /***********************************************************************************************
  698. * LightmapPacker::Update_Statistics -- *
  699. * *
  700. * INPUT: *
  701. * *
  702. * OUTPUT: *
  703. * *
  704. * WARNINGS: *
  705. * *
  706. * HISTORY: *
  707. * 07/31/00 IML : Created. *
  708. *=============================================================================================*/
  709. void LightmapPacker::Update_Statistics (const TrianglePacker &trianglepacker)
  710. {
  711. AdjacentFaceCount += trianglepacker.Adjacent_Face_Count();
  712. BlendedFaceCount += trianglepacker.Blended_Face_Count();
  713. EdgeBlendAreaSum += trianglepacker.Edge_Blend_Area();
  714. }
  715. /***********************************************************************************************
  716. * LightmapPacker::Collate_Statistics -- *
  717. * *
  718. * INPUT: *
  719. * *
  720. * OUTPUT: *
  721. * *
  722. * WARNINGS: *
  723. * *
  724. * HISTORY: *
  725. * 8/18/99 IML : Created. *
  726. *=============================================================================================*/
  727. void LightmapPacker::Collate_Statistics()
  728. {
  729. unsigned pagecount;
  730. float adjacentfaceblendfraction, edgeblendefficiency, scalingefficiency, packingefficiency, cullingefficiency, swappingefficiency;
  731. // Build statistics string.
  732. adjacentfaceblendfraction = (AdjacentFaceCount > 0) ? ((float) BlendedFaceCount) / ((float) AdjacentFaceCount) : 0.0f;
  733. edgeblendefficiency = (UnscaledTexelCount > 0) ? 1.0f - (EdgeBlendAreaSum / UnscaledTexelCount) : 0.0f;
  734. scalingefficiency = (UnscaledTexelCount > 0) ? 1.0f - (((float) ScaledTexelCount) / ((float) UnscaledTexelCount)) : 0.0f;
  735. pagecount = PagePtrs.Count();
  736. packingefficiency = (pagecount > 0) ? PackingEfficiencySum / pagecount : 0.0f;
  737. cullingefficiency = (pagecount > 0) ? ReplicaEfficiencySum / pagecount : 0.0f;
  738. swappingefficiency = (FaceCount > 0) ? 1.0f - (((float)TextureSwapCount) / FaceCount) : 0.0f;
  739. _snprintf (_Statistics [STATISTICS_PAGE_FORMAT], sizeof (_Statistics [STATISTICS_PAGE_FORMAT]) - 1, "%d x %d x %dbpp", PAGE_WIDTH, PAGE_HEIGHT, PageBitDepth);
  740. _snprintf (_Statistics [STATISTICS_LIGHTMAPS_PROCESSED], sizeof (_Statistics [STATISTICS_LIGHTMAPS_PROCESSED]) - 1, "%d", LightmapCount);
  741. _snprintf (_Statistics [STATISTICS_ADJACENT_FACE_BLEND_PERCENTAGE], sizeof (_Statistics [STATISTICS_ADJACENT_FACE_BLEND_PERCENTAGE]) - 1, "%.1f%%", adjacentfaceblendfraction * 100.0f);
  742. _snprintf (_Statistics [STATISTICS_EDGE_BLEND_EFFICIENCY], sizeof (_Statistics [STATISTICS_EDGE_BLEND_EFFICIENCY]) - 1, "%.1f%%", edgeblendefficiency * 100.0f);
  743. _snprintf (_Statistics [STATISTICS_SCALING_EFFICIENCY], sizeof (_Statistics [STATISTICS_SCALING_EFFICIENCY]) - 1, "%.1f%%", scalingefficiency * 100.0f);
  744. _snprintf (_Statistics [STATISTICS_PAGES_CREATED], sizeof (_Statistics [STATISTICS_PAGES_CREATED]) - 1, "%d", pagecount);
  745. _snprintf (_Statistics [STATISTICS_PACKING_EFFICIENCY], sizeof (_Statistics [STATISTICS_PACKING_EFFICIENCY]) - 1, "%.1f%%", packingefficiency * 100.0f);
  746. _snprintf (_Statistics [STATISTICS_CULLING_EFFICIENCY], sizeof (_Statistics [STATISTICS_CULLING_EFFICIENCY]) - 1, "%.1f%%", cullingefficiency * 100.0f);
  747. _snprintf (_Statistics [STATISTICS_TEXTURE_SWAP_EFFICIENCY], sizeof (_Statistics [STATISTICS_TEXTURE_SWAP_EFFICIENCY]) - 1, "%.1f%%", swappingefficiency * 100.0f);
  748. _snprintf (_Statistics [STATISTICS_OVERSIZE_LIGHTMAPS], sizeof (_Statistics [STATISTICS_TEXTURE_SWAP_EFFICIENCY]) - 1, "%d", OversizeCount);
  749. }
  750. /***********************************************************************************************
  751. * TrianglePacker::TrianglePacker -- *
  752. * *
  753. * INPUT: *
  754. * *
  755. * OUTPUT: *
  756. * *
  757. * WARNINGS: *
  758. * *
  759. * HISTORY: *
  760. * 07/28/00 IML : Created. *
  761. *=============================================================================================*/
  762. TrianglePacker::TrianglePacker (const PackingTriangle *principaltriangleptr, const DynamicVectorClass <Triangle> &adjtriangles, unsigned groupid, unsigned edgeblendthickness, float samplerate, W3dRGBStruct fillcolor)
  763. {
  764. Projection = Get_Projection (principaltriangleptr->Normal);
  765. GroupID = groupid;
  766. EdgeBlendThickness = edgeblendthickness;
  767. SampleRate = samplerate;
  768. FillColor = fillcolor;
  769. // Add the principal triangle.
  770. PrincipalTriangles.Add ((PackingTriangle*) principaltriangleptr);
  771. // Add the adjacent triangles, sorting them into the three groups COMMON, VALID & NONE.
  772. for (int a = 0; a < adjtriangles.Count(); a++) {
  773. if (Get_Projection (adjtriangles [a].Normal) == Projection) {
  774. AdjacentTriangles [ADJACENT_PROJECTION_COMMON].Add (adjtriangles [a]);
  775. } else {
  776. if (Can_Project (adjtriangles [a].Normal)) {
  777. AdjacentTriangles [ADJACENT_PROJECTION_VALID].Add (adjtriangles [a]);
  778. } else {
  779. AdjacentTriangles [ADJACENT_PROJECTION_NONE].Add (adjtriangles [a]);
  780. }
  781. }
  782. }
  783. // The triangle sets have been modifed - therefore set bounds.
  784. Set_Bounds();
  785. }
  786. /***********************************************************************************************
  787. * TrianglePacker::TrianglePacker -- *
  788. * *
  789. * INPUT: *
  790. * *
  791. * OUTPUT: *
  792. * *
  793. * WARNINGS: *
  794. * *
  795. * HISTORY: *
  796. * 07/28/00 IML : Created. *
  797. *=============================================================================================*/
  798. TrianglePacker::TrianglePacker (const TrianglePacker &trianglepacker)
  799. {
  800. int t, a;
  801. Projection = trianglepacker.Projection;
  802. GroupID = trianglepacker.GroupID;
  803. EdgeBlendThickness = trianglepacker.EdgeBlendThickness;
  804. SampleRate = trianglepacker.SampleRate;
  805. FillColor = trianglepacker.FillColor;
  806. LowerBound = trianglepacker.LowerBound;
  807. UpperBound = trianglepacker.UpperBound;
  808. // Copy the principal triangles.
  809. for (t = 0; t < trianglepacker.PrincipalTriangles.Count(); t++) {
  810. PrincipalTriangles.Add (trianglepacker.PrincipalTriangles [t]);
  811. }
  812. // Copy the adjacent triangles.
  813. for (a = 0; a < ADJACENT_PROJECTION_COUNT; a++) {
  814. for (t = 0; t < trianglepacker.AdjacentTriangles [a].Count(); t++) {
  815. AdjacentTriangles [a].Add (trianglepacker.AdjacentTriangles [a] [t]);
  816. }
  817. }
  818. }
  819. /***********************************************************************************************
  820. * TrianglePacker::Reset -- *
  821. * *
  822. * INPUT: *
  823. * *
  824. * OUTPUT: *
  825. * *
  826. * WARNINGS: *
  827. * *
  828. * HISTORY: *
  829. * 07/28/00 IML : Created. *
  830. *=============================================================================================*/
  831. void TrianglePacker::Set_Bounds()
  832. {
  833. const Vector2 edge (EdgeBlendThickness, EdgeBlendThickness);
  834. Vector2 lowerbound (+FLT_MAX, +FLT_MAX);
  835. Vector2 upperbound (-FLT_MAX, -FLT_MAX);
  836. // Calculate the smallest bounding rectangle that will contain the projected principal triangles.
  837. for (int t = 0; t < PrincipalTriangles.Count(); t++) {
  838. for (int v = 0; v < Triangle::VERTICES_COUNT; v++) {
  839. Vector2 uv;
  840. uv = Project (PrincipalTriangles [t]->Vertices [v].Point);
  841. lowerbound.Update_Min (uv);
  842. upperbound.Update_Max (uv);
  843. }
  844. }
  845. // Increase the bounds by the edge thickness to account for edge blending and snap to integer coordinates.
  846. lowerbound -= edge;
  847. upperbound += edge;
  848. LowerBound.Set (floorf (lowerbound.U), floorf (lowerbound.V));
  849. UpperBound.Set (ceilf (upperbound.U), ceilf (upperbound.V));
  850. }
  851. /***********************************************************************************************
  852. * TrianglePacker::Edge_Blend_Area -- *
  853. * *
  854. * INPUT: *
  855. * *
  856. * OUTPUT: *
  857. * *
  858. * WARNINGS: *
  859. * *
  860. * HISTORY: *
  861. * 07/28/00 IML : Created. *
  862. *=============================================================================================*/
  863. float TrianglePacker::Edge_Blend_Area() const
  864. {
  865. float principalareasum;
  866. unsigned width, height;
  867. principalareasum = 0.0f;
  868. for (int t = 0; t < PrincipalTriangles.Count(); t++) {
  869. Vector2 o, a, b;
  870. o = Project (PrincipalTriangles [t]->Vertices [0].Point);
  871. a = Project (PrincipalTriangles [t]->Vertices [1].Point) - o;
  872. b = Project (PrincipalTriangles [t]->Vertices [2].Point) - o;
  873. principalareasum += 0.5f * WWMath::Fabs (a.U * b.V - b.U * a.V);
  874. }
  875. width = Width();
  876. height = Height();
  877. ASSERT ((width * height) >= principalareasum);
  878. return ((width * height) - principalareasum);
  879. }
  880. /***********************************************************************************************
  881. * TrianglePacker::Merge -- *
  882. * *
  883. * INPUT: *
  884. * *
  885. * OUTPUT: *
  886. * *
  887. * WARNINGS: *
  888. * *
  889. * HISTORY: *
  890. * 07/28/00 IML : Created. *
  891. *=============================================================================================*/
  892. TrianglePacker *TrianglePacker::Merge (const TrianglePacker &trianglepacker)
  893. {
  894. TrianglePacker *mergedpackerptr;
  895. mergedpackerptr = NULL;
  896. // Do both triangle packers have the same projection, group ID, edge blend thickness, sample rate, and fill color?
  897. // If not, then they are not compatible packers and cannot be merged.
  898. if ((Projection == trianglepacker.Projection) &&
  899. (GroupID == trianglepacker.GroupID) &&
  900. (EdgeBlendThickness == trianglepacker.EdgeBlendThickness) &&
  901. (SampleRate == trianglepacker.SampleRate) &&
  902. (FillColor == trianglepacker.FillColor)) {
  903. // For each principal triangle in triangle packer...
  904. for (int p = 0; p < trianglepacker.PrincipalTriangles.Count(); p++) {
  905. // For each principal triangle in this...
  906. for (int q = 0; q < PrincipalTriangles.Count(); q++) {
  907. // Does one principal triangle abut the other?
  908. if (trianglepacker.PrincipalTriangles [p]->Abuts (*PrincipalTriangles [q])) {
  909. // Copy this.
  910. mergedpackerptr = new TrianglePacker (*this);
  911. ASSERT (mergedpackerptr != NULL);
  912. break;
  913. }
  914. }
  915. if (mergedpackerptr != NULL) break;
  916. }
  917. if (mergedpackerptr != NULL) {
  918. // For each principal triangle in triangle packer...
  919. for (int p = 0; p < trianglepacker.PrincipalTriangles.Count(); p++) {
  920. // Add it to the merged packer.
  921. mergedpackerptr->PrincipalTriangles.Add (trianglepacker.PrincipalTriangles [p]);
  922. }
  923. // Merge the adjacent triangles (do not allow duplicates).
  924. for (int a = 0; a < ADJACENT_PROJECTION_COUNT; a++) {
  925. for (int t = 0; t < trianglepacker.AdjacentTriangles [a].Count(); t++) {
  926. const Triangle *triangleptr;
  927. bool foundadjacent;
  928. triangleptr = &(trianglepacker.AdjacentTriangles [a][t]);
  929. foundadjacent = false;
  930. for (int m = 0; m < AdjacentTriangles [a].Count(); m++) {
  931. if (triangleptr->Is_Equivalent ((AdjacentTriangles [a]) [m])) {
  932. foundadjacent = true;
  933. break;
  934. }
  935. }
  936. if (!foundadjacent) {
  937. mergedpackerptr->AdjacentTriangles [a].Add ((trianglepacker.AdjacentTriangles [a]) [t]);
  938. }
  939. }
  940. }
  941. // The triangle sets have been modifed - therefore set bounds.
  942. mergedpackerptr->Set_Bounds();
  943. }
  944. }
  945. return (mergedpackerptr);
  946. }
  947. /***********************************************************************************************
  948. * TrianglePacker::Get_Projection -- *
  949. * *
  950. * INPUT: *
  951. * *
  952. * OUTPUT: *
  953. * *
  954. * WARNINGS: *
  955. * *
  956. * HISTORY: *
  957. * 06/19/00 IML : Created. *
  958. *=============================================================================================*/
  959. TrianglePacker::ProjectionEnum TrianglePacker::Get_Projection (const Vector3 &normal)
  960. {
  961. float x, y, z;
  962. x = WWMath::Fabs (normal.X);
  963. y = WWMath::Fabs (normal.Y);
  964. z = WWMath::Fabs (normal.Z);
  965. if (y >= x) {
  966. if (y >= z) {
  967. if (normal.Y >= 0.0f) {
  968. return (PROJECTION_Y_POSITIVE);
  969. } else {
  970. return (PROJECTION_Y_NEGATIVE);
  971. }
  972. } else {
  973. if (normal.Z >= 0.0f) {
  974. return (PROJECTION_Z_POSITIVE);
  975. } else {
  976. return (PROJECTION_Z_NEGATIVE);
  977. }
  978. }
  979. } else {
  980. if (x >= z) {
  981. if (normal.X >= 0.0f) {
  982. return (PROJECTION_X_POSITIVE);
  983. } else {
  984. return (PROJECTION_X_NEGATIVE);
  985. }
  986. } else {
  987. if (normal.Z >= 0.0f) {
  988. return (PROJECTION_Z_POSITIVE);
  989. } else {
  990. return (PROJECTION_Z_NEGATIVE);
  991. }
  992. }
  993. }
  994. }
  995. /***********************************************************************************************
  996. * TrianglePacker::Can_Project -- *
  997. * *
  998. * INPUT: *
  999. * *
  1000. * OUTPUT: *
  1001. * *
  1002. * WARNINGS: *
  1003. * *
  1004. * HISTORY: *
  1005. * 06/19/00 IML : Created. *
  1006. *=============================================================================================*/
  1007. bool TrianglePacker::Can_Project (const Vector3 &normal)
  1008. {
  1009. static const float _projectionnormals [PROJECTION_COUNT][3] =
  1010. {{ 0.0f, 1.0f, 0.0f},
  1011. { 0.0f, -1.0f, 0.0f},
  1012. { 0.0f, 0.0f, 1.0f},
  1013. { 0.0f, 0.0f, -1.0f},
  1014. { 1.0f, 0.0f, 0.0f},
  1015. {-1.0f, 0.0f, 0.0f}
  1016. };
  1017. const float maxprojectionangle = (80.0f * WWMATH_PI) / 180.0f; // Max. allowed angle between triangle and projection plane.
  1018. Vector3 projectionnormal;
  1019. float angle;
  1020. projectionnormal.Set (_projectionnormals [Projection][0], _projectionnormals [Projection][1], _projectionnormals [Projection][2]);
  1021. angle = acosf (Vector3::Dot_Product (normal, projectionnormal));
  1022. return (angle < maxprojectionangle);
  1023. }
  1024. /***********************************************************************************************
  1025. * TrianglePacker::Project -- *
  1026. * *
  1027. * INPUT: *
  1028. * *
  1029. * OUTPUT: *
  1030. * *
  1031. * WARNINGS: *
  1032. * *
  1033. * HISTORY: *
  1034. * 06/19/00 IML : Created. *
  1035. *=============================================================================================*/
  1036. Vector2 TrianglePacker::Project (const Vector3 &point) const
  1037. {
  1038. Vector2 t;
  1039. switch (Projection) {
  1040. case PROJECTION_Y_POSITIVE:
  1041. case PROJECTION_Y_NEGATIVE:
  1042. t.Set (point.X, point.Z);
  1043. break;
  1044. case PROJECTION_Z_POSITIVE:
  1045. case PROJECTION_Z_NEGATIVE:
  1046. t.Set (point.X, point.Y);
  1047. break;
  1048. case PROJECTION_X_POSITIVE:
  1049. case PROJECTION_X_NEGATIVE:
  1050. t.Set (point.Y, point.Z);
  1051. break;
  1052. }
  1053. t *= SampleRate;
  1054. return (t);
  1055. }
  1056. /***********************************************************************************************
  1057. * TrianglePacker::Rasterize -- *
  1058. * *
  1059. * INPUT: *
  1060. * *
  1061. * OUTPUT: *
  1062. * *
  1063. * WARNINGS: *
  1064. * *
  1065. * HISTORY: *
  1066. * 06/08/00 IML : Created. *
  1067. *=============================================================================================*/
  1068. void TrianglePacker::Rasterize (TargaLoader &targaloader, ProceduralTexture *proceduraltexture, TrueColorTarga &rasterizedtarga)
  1069. {
  1070. const unsigned pixeldepth = 24;
  1071. const unsigned sqrtforwardsamplespertexel = 4;
  1072. const unsigned sqrtbackwardsamplespertexel = 4;
  1073. const unsigned principalpriority = 2;
  1074. const unsigned adjacentpriority = 1;
  1075. // There must be at least one principal triangle to rasterize.
  1076. ASSERT (PrincipalTriangles.Count() >= 1);
  1077. unsigned width, height;
  1078. SampleSurface *forwardsurfaceptr, *backwardsurfaceptr;
  1079. int t;
  1080. unsigned projectiontrianglecount;
  1081. float right, top;
  1082. ProjectionTriangle *projectiontriangles;
  1083. Vector3 points [Triangle::VERTICES_COUNT];
  1084. Vector2 sourceuvs [Triangle::VERTICES_COUNT];
  1085. Vector2 projectionuvs [Triangle::VERTICES_COUNT];
  1086. TrueColorTarga *sourcetargaptr;
  1087. float oosqrtsamplespertexel;
  1088. Vector2 samplepoint;
  1089. int sampledt;
  1090. unsigned x, y, u, v;
  1091. float oowidth, ooheight;
  1092. width = Width();
  1093. height = Height();
  1094. // Allocate a sample surface to store destination->source (backward) samples.
  1095. backwardsurfaceptr = new SampleSurface (width, height, proceduraltexture);
  1096. ASSERT (backwardsurfaceptr != NULL);
  1097. // Allocate enough projection triangles to cover the principal triangles and those triangles that can be projected.
  1098. projectiontriangles = new ProjectionTriangle [PrincipalTriangles.Count() + AdjacentTriangles [ADJACENT_PROJECTION_COMMON].Count() + AdjacentTriangles [ADJACENT_PROJECTION_VALID].Count()];
  1099. ASSERT (projectiontriangles != NULL);
  1100. // For each principal triangle...
  1101. projectiontrianglecount = PrincipalTriangles.Count();
  1102. for (t = 0; t < (int) projectiontrianglecount; t++) {
  1103. for (int v = 0; v < Triangle::VERTICES_COUNT; v++) {
  1104. points [v] = PrincipalTriangles [t]->Vertices [v].Point;
  1105. sourceuvs [v] = PrincipalTriangles [t]->Vertices [v].UV;
  1106. projectionuvs [v] = Project (PrincipalTriangles [t]->Vertices [v].Point) - LowerBound;
  1107. }
  1108. sourcetargaptr = targaloader.Load (*PrincipalTriangles [t]);
  1109. projectiontriangles [t] = ProjectionTriangle (points, sourceuvs, sourcetargaptr, projectionuvs);
  1110. }
  1111. // For each adjacent triangle that is common or valid...
  1112. right = width;
  1113. top = height;
  1114. for (int a = ADJACENT_PROJECTION_COMMON; a <= ADJACENT_PROJECTION_VALID; a++) {
  1115. for (t = 0; t < AdjacentTriangles [a].Count(); t++) {
  1116. unsigned outcode, outcodeu, outcodev;
  1117. // Test the triangle for trivial rejection.
  1118. outcode = 0xf;
  1119. for (int v = 0; v < Triangle::VERTICES_COUNT; v++) {
  1120. projectionuvs [v] = Project (AdjacentTriangles [a] [t].Vertices [v].Point) - LowerBound;
  1121. outcodeu = (projectionuvs [v].U < 0.0f) | ((projectionuvs [v].U >= right) << 1);
  1122. outcodev = (projectionuvs [v].V < 0.0f) | ((projectionuvs [v].V >= top) << 1);
  1123. outcode &= (outcodeu | (outcodev << 2));
  1124. }
  1125. // Should the triangle be accepted (ie. not trivially rejected)?
  1126. if (outcode == 0) {
  1127. for (int v = 0; v < Triangle::VERTICES_COUNT; v++) {
  1128. points [v] = AdjacentTriangles [a] [t].Vertices [v].Point;
  1129. sourceuvs [v] = AdjacentTriangles [a] [t].Vertices [v].UV;
  1130. }
  1131. sourcetargaptr = targaloader.Load (AdjacentTriangles [a] [t]);
  1132. projectiontriangles [projectiontrianglecount] = ProjectionTriangle (points, sourceuvs, sourcetargaptr, projectionuvs);
  1133. projectiontrianglecount++;
  1134. }
  1135. }
  1136. }
  1137. // Sample the projection triangles into the backward surface.
  1138. oosqrtsamplespertexel = 1.0f / sqrtbackwardsamplespertexel;
  1139. sampledt = 0;
  1140. for (y = 0; y < height; y++) {
  1141. for (x = 0; x < width; x++) {
  1142. for (v = 0; v < sqrtbackwardsamplespertexel; v++) {
  1143. samplepoint.V = y + (v * oosqrtsamplespertexel);
  1144. for (u = 0; u < sqrtbackwardsamplespertexel; u++) {
  1145. samplepoint.U = x + (u * oosqrtsamplespertexel);
  1146. if (sampledt < PrincipalTriangles.Count()) {
  1147. if (backwardsurfaceptr->Sample (samplepoint, projectiontriangles [sampledt], principalpriority)) goto nextsample;
  1148. }
  1149. for (t = 0; t < PrincipalTriangles.Count(); t++) {
  1150. if (t != sampledt) {
  1151. if (backwardsurfaceptr->Sample (samplepoint, projectiontriangles [t], principalpriority)) {
  1152. sampledt = t;
  1153. goto nextsample;
  1154. }
  1155. }
  1156. }
  1157. if ((sampledt >= PrincipalTriangles.Count()) && (sampledt < (int) projectiontrianglecount)) {
  1158. if (backwardsurfaceptr->Sample (samplepoint, projectiontriangles [sampledt], adjacentpriority)) goto nextsample;
  1159. }
  1160. for (t = PrincipalTriangles.Count(); t < (int) projectiontrianglecount; t++) {
  1161. if (t != sampledt) {
  1162. if (backwardsurfaceptr->Sample (samplepoint, projectiontriangles [t], adjacentpriority)) {
  1163. sampledt = t;
  1164. goto nextsample;
  1165. }
  1166. }
  1167. }
  1168. nextsample:;
  1169. }
  1170. }
  1171. }
  1172. }
  1173. // If less than 50% of the texels were sampled in the backward rasterization prepare a forward rasterization.
  1174. forwardsurfaceptr = NULL;
  1175. if (backwardsurfaceptr->Sampling_Ratio() < 0.5f) {
  1176. // Allocate a sample surface to store source->destination (forward) samples.
  1177. forwardsurfaceptr = new SampleSurface (width, height, proceduraltexture);
  1178. ASSERT (forwardsurfaceptr != NULL);
  1179. for (t = 0; t < PrincipalTriangles.Count(); t++) {
  1180. Vector2 d, d0, d1, d2;
  1181. float projectionarea, maxlength, pseudoarea;
  1182. unsigned sqrtsamplecount;
  1183. float oosqrtsamplecount;
  1184. unsigned a, b;
  1185. float alpha, beta;
  1186. // Calculate the sample rate based on the triangle's projected area.
  1187. // NOTE: If the area is small relative to its longest edge use the edge length instead.
  1188. d = projectionuvs [2] - projectionuvs [0];
  1189. d0 = projectionuvs [1] - projectionuvs [0];
  1190. d1 = projectionuvs [2] - projectionuvs [1];
  1191. d2 = projectionuvs [0] - projectionuvs [2];
  1192. projectionarea = 0.5f * WWMath::Fabs (d.U * d0.V - d0.U * d.V);
  1193. maxlength = (float) WWMath::Sqrt (MAX (MAX (d0.Length2(), d1.Length2()), d2.Length2()));
  1194. pseudoarea = MAX (projectionarea, maxlength);
  1195. sqrtsamplecount = ceilf (WWMath::Sqrt (sqrtforwardsamplespertexel * sqrtforwardsamplespertexel * pseudoarea));
  1196. // Sample the projection triangle into the forward surface.
  1197. oosqrtsamplecount = 1.0f / sqrtsamplecount;
  1198. for (b = 0; b < sqrtsamplecount; b++) {
  1199. beta = b * oosqrtsamplecount;
  1200. for (a = 0; a < sqrtsamplecount; a++) {
  1201. alpha = a * oosqrtsamplecount;
  1202. if (alpha + beta <= 1.0f) {
  1203. forwardsurfaceptr->Sample (alpha, beta, projectiontriangles [t], principalpriority);
  1204. }
  1205. }
  1206. }
  1207. }
  1208. }
  1209. // Resize the targa which holds the resulting rasterization.
  1210. rasterizedtarga.Reformat (width, height, pixeldepth);
  1211. // Iterate over the backward (and possibly forward) surface and translate it into the rasterized targa.
  1212. for (y = 0; y < height; y++) {
  1213. unsigned miny, maxy, minx, maxx;
  1214. bool inrangeofprincipal;
  1215. unsigned a, b;
  1216. miny = MAX ((int) 0, ((int) y) - ((int) EdgeBlendThickness));
  1217. maxy = MIN (height - 1, y + EdgeBlendThickness);
  1218. for (x = 0; x < width; x++) {
  1219. W3dRGBStruct color;
  1220. minx = MAX ((int) 0, ((int) x) - ((int) EdgeBlendThickness));
  1221. maxx = MIN (width - 1, x + EdgeBlendThickness);
  1222. // Find out if this texel is within edge blend texels of a texel that has a 'principal sample'.
  1223. // NOTE: If so then this texel can potentially be read and therefore must contain valid data.
  1224. // Otherwise fill color will suffice.
  1225. inrangeofprincipal = false;
  1226. for (b = miny; b <= maxy; b++) {
  1227. for (a = minx; a <= maxx; a++) {
  1228. if (backwardsurfaceptr->Priority (a, b) >= principalpriority) {
  1229. inrangeofprincipal = true;
  1230. break;
  1231. }
  1232. if (forwardsurfaceptr != NULL) {
  1233. if (forwardsurfaceptr->Priority (a, b) >= principalpriority) {
  1234. inrangeofprincipal = true;
  1235. break;
  1236. }
  1237. }
  1238. }
  1239. if (inrangeofprincipal) break;
  1240. }
  1241. // Is texel within range of 'principal texel'?
  1242. if (inrangeofprincipal) {
  1243. // Does the backward surface's texel have a sample?
  1244. if (backwardsurfaceptr->Get_Color (x, y, color)) {
  1245. rasterizedtarga.Set_Color (x, y, color);
  1246. } else {
  1247. if (forwardsurfaceptr != NULL) {
  1248. if (forwardsurfaceptr->Get_Color (x, y, color)) {
  1249. rasterizedtarga.Set_Color (x, y, color);
  1250. } else {
  1251. rasterizedtarga.Set_Color (x, y, FillColor);
  1252. }
  1253. } else {
  1254. rasterizedtarga.Set_Color (x, y, FillColor);
  1255. }
  1256. }
  1257. } else {
  1258. rasterizedtarga.Set_Color (x, y, FillColor);
  1259. }
  1260. }
  1261. }
  1262. // Pad any fill color texels.
  1263. rasterizedtarga.Fill (FillColor);
  1264. // Set the packed UV's of each principal triangle to normalized projection UV's.
  1265. oowidth = 1.0f / width;
  1266. ooheight = 1.0f / height;
  1267. for (t = 0; t < PrincipalTriangles.Count(); t++) {
  1268. for (unsigned v = 0; v < Triangle::VERTICES_COUNT; v++) {
  1269. Vector2 uv;
  1270. uv = Project (PrincipalTriangles [t]->Vertices [v].Point) - LowerBound;
  1271. uv.Scale (oowidth, ooheight);
  1272. PrincipalTriangles [t]->PackedUVs [v] = uv;
  1273. }
  1274. }
  1275. // Clean-up.
  1276. if (forwardsurfaceptr != NULL) delete forwardsurfaceptr;
  1277. delete [] projectiontriangles;
  1278. delete backwardsurfaceptr;
  1279. }
  1280. /***********************************************************************************************
  1281. * ProjectionTriangle::ProjectionTriangle -- *
  1282. * *
  1283. * INPUT: *
  1284. * *
  1285. * OUTPUT: *
  1286. * *
  1287. * WARNINGS: *
  1288. * *
  1289. * HISTORY: *
  1290. * 06/08/00 IML : Created. *
  1291. *=============================================================================================*/
  1292. TrianglePacker::ProjectionTriangle::ProjectionTriangle (const Vector3 *points, const Vector2 *sourceuvs, const TrueColorTarga *sourcetargaptr, const Vector2 *projectionuvs)
  1293. {
  1294. Points [0] = points [0];
  1295. Points [1] = points [1] - points [0];
  1296. Points [2] = points [2] - points [0];
  1297. SourceUVs [0] = sourceuvs [0];
  1298. SourceUVs [1] = sourceuvs [1] - sourceuvs [0];
  1299. SourceUVs [2] = sourceuvs [2] - sourceuvs [0];
  1300. ProjectionUVs [0] = projectionuvs [0];
  1301. ProjectionUVs [1] = projectionuvs [1] - projectionuvs [0];
  1302. ProjectionUVs [2] = projectionuvs [2] - projectionuvs [0];
  1303. SourceTargaPtr = (TrueColorTarga*) sourcetargaptr;
  1304. }
  1305. /***********************************************************************************************
  1306. * TrueColorTarga::TrueColorTarga -- *
  1307. * *
  1308. * INPUT: *
  1309. * *
  1310. * OUTPUT: *
  1311. * *
  1312. * WARNINGS: *
  1313. * *
  1314. * HISTORY: *
  1315. * 9/27/99 IML : Created. *
  1316. *=============================================================================================*/
  1317. TrueColorTarga::TrueColorTarga()
  1318. : Targa()
  1319. {
  1320. const unsigned width = 1;
  1321. const unsigned height = 1;
  1322. const unsigned pixeldepth = 24;
  1323. // Check that width, height and pixel depth will fit into the Targa internal storage types.
  1324. ASSERT (width <= SHRT_MAX);
  1325. ASSERT (height <= SHRT_MAX);
  1326. ASSERT (pixeldepth <= SCHAR_MAX);
  1327. mFlags = TGAF_IMAGE;
  1328. Header.ImageType = TGA_TRUECOLOR;
  1329. Reformat (width, height, pixeldepth);
  1330. }
  1331. /***********************************************************************************************
  1332. * TrueColorTarga::TrueColorTarga -- *
  1333. * *
  1334. * INPUT: *
  1335. * *
  1336. * OUTPUT: *
  1337. * *
  1338. * WARNINGS: *
  1339. * *
  1340. * HISTORY: *
  1341. * 9/27/99 IML : Created. *
  1342. *=============================================================================================*/
  1343. TrueColorTarga::TrueColorTarga (unsigned width, unsigned height, unsigned pixeldepth)
  1344. : Targa()
  1345. {
  1346. // Check that width, height and pixel depth will fit into the Targa internal storage types.
  1347. ASSERT (width <= SHRT_MAX);
  1348. ASSERT (height <= SHRT_MAX);
  1349. ASSERT (pixeldepth <= SCHAR_MAX);
  1350. mFlags = TGAF_IMAGE;
  1351. Header.ImageType = TGA_TRUECOLOR;
  1352. Reformat (width, height, pixeldepth);
  1353. }
  1354. /***********************************************************************************************
  1355. * TrueColorTarga::TrueColorTarga -- *
  1356. * *
  1357. * INPUT: *
  1358. * *
  1359. * OUTPUT: *
  1360. * *
  1361. * WARNINGS: *
  1362. * *
  1363. * HISTORY: *
  1364. * 9/27/99 IML : Created. *
  1365. *=============================================================================================*/
  1366. TrueColorTarga::TrueColorTarga (unsigned width, unsigned height, unsigned pixeldepth, const W3dRGBStruct &clearcolor)
  1367. : Targa()
  1368. {
  1369. // Check that width, height and pixel depth will fit into the Targa internal storage types.
  1370. ASSERT (width <= SHRT_MAX);
  1371. ASSERT (height <= SHRT_MAX);
  1372. ASSERT (pixeldepth <= SCHAR_MAX);
  1373. mFlags = TGAF_IMAGE;
  1374. Header.ImageType = TGA_TRUECOLOR;
  1375. Reformat (width, height, pixeldepth, clearcolor);
  1376. }
  1377. /***********************************************************************************************
  1378. * TrueColorTarga::Reformat -- *
  1379. * *
  1380. * INPUT: *
  1381. * *
  1382. * OUTPUT: *
  1383. * *
  1384. * WARNINGS: *
  1385. * *
  1386. * HISTORY: *
  1387. * 9/27/99 IML : Created. *
  1388. *=============================================================================================*/
  1389. void TrueColorTarga::Reformat (unsigned width, unsigned height, unsigned pixeldepth)
  1390. {
  1391. size_t size;
  1392. Header.Width = width;
  1393. Header.Height = height;
  1394. Header.PixelDepth = pixeldepth;
  1395. size = width * height * TGA_BytesPerPixel (pixeldepth);
  1396. if (size > 0) {
  1397. // NOTE: Use malloc() or realloc() here because destructor will use free().
  1398. mImage = (char*) realloc (mImage, size);
  1399. ASSERT (mImage != NULL);
  1400. }
  1401. }
  1402. /***********************************************************************************************
  1403. * TrueColorTarga::Reformat -- *
  1404. * *
  1405. * INPUT: *
  1406. * *
  1407. * OUTPUT: *
  1408. * *
  1409. * WARNINGS: *
  1410. * *
  1411. * HISTORY: *
  1412. * 9/27/99 IML : Created. *
  1413. *=============================================================================================*/
  1414. void TrueColorTarga::Reformat (unsigned width, unsigned height, unsigned pixeldepth, const W3dRGBStruct &clearcolor)
  1415. {
  1416. UnpackedTexelStruct cleartexel (clearcolor.R, clearcolor.G, clearcolor.B, 0);
  1417. Reformat (width, height, pixeldepth);
  1418. Clear (cleartexel);
  1419. }
  1420. /***********************************************************************************************
  1421. * TrueColorTarga::Pixel_Format -- *
  1422. * *
  1423. * INPUT: *
  1424. * *
  1425. * OUTPUT: *
  1426. * *
  1427. * WARNINGS: *
  1428. * *
  1429. * HISTORY: *
  1430. * 9/27/99 IML : Created. *
  1431. *=============================================================================================*/
  1432. srPixelConvert::e_surfaceType TrueColorTarga::Pixel_Format (unsigned pixeldepth)
  1433. {
  1434. // Map a pixel depth (in bits) to a Targa supported pixel format.
  1435. switch (pixeldepth) {
  1436. case 16:
  1437. return (srPixelConvert::ARGB1555);
  1438. case 24:
  1439. return (srPixelConvert::RGB888);
  1440. case 32:
  1441. return (srPixelConvert::ARGB8888);
  1442. default:
  1443. ASSERT (FALSE);
  1444. break;
  1445. }
  1446. ASSERT (FALSE);
  1447. return (srPixelConvert::e_surfaceType (0));
  1448. }
  1449. /***********************************************************************************************
  1450. * TrueColorTarga::Clear -- *
  1451. * *
  1452. * INPUT: *
  1453. * *
  1454. * OUTPUT: *
  1455. * *
  1456. * WARNINGS: *
  1457. * *
  1458. * HISTORY: *
  1459. * 9/27/99 IML : Created. *
  1460. *=============================================================================================*/
  1461. void TrueColorTarga::Clear (const TrueColorTarga::UnpackedTexelStruct &cleartexel)
  1462. {
  1463. const unsigned bytespertexel = TGA_BytesPerPixel (Pixel_Depth());
  1464. unsigned char *texelptr;
  1465. for (unsigned y = 0; y < Height(); y++) {
  1466. texelptr = ((unsigned char*) GetImage()) + (Width() * y * bytespertexel);
  1467. for (unsigned x = 0; x < Width(); x++) {
  1468. Pack_Texel (cleartexel, texelptr, bytespertexel);
  1469. texelptr += bytespertexel;
  1470. }
  1471. }
  1472. }
  1473. /***********************************************************************************************
  1474. * TrueColorTarga::Load -- *
  1475. * *
  1476. * INPUT: *
  1477. * *
  1478. * OUTPUT: *
  1479. * *
  1480. * WARNINGS: *
  1481. * *
  1482. * HISTORY: *
  1483. * 9/27/99 IML : Created. *
  1484. *=============================================================================================*/
  1485. char *TrueColorTarga::Load (const char *pathname)
  1486. {
  1487. const char *loaderrormessage = "The image file ""%s"" cannot be loaded (load error: %d).\
  1488. Please ensure that the image file exists and is in TGA (Truevision TARGA) format.";
  1489. const char *formaterrormessage = "The image file ""%s"" is not in a suitable TGA format.\
  1490. Please ensure that the image file is in true color format (16, 24 or 32 bit).";
  1491. static char _messagebuffer [256];
  1492. long error;
  1493. bool valid;
  1494. StringBuilder errormessage (_messagebuffer, sizeof (_messagebuffer));
  1495. error = Targa::Load (pathname, TGAF_IMAGE);
  1496. if (error != 0) {
  1497. errormessage.Copy (loaderrormessage, pathname, error);
  1498. return (errormessage.String());
  1499. }
  1500. // Check for a specific subset of the available .TGA texel formats which are suitable for
  1501. // true color targas (ie. true color, not paletted, not monochrome).
  1502. valid = (Header.ImageType == TGA_TRUECOLOR) || (Header.ImageType == TGA_TRUECOLOR_ENCODED);
  1503. valid &= (Header.PixelDepth == 16) || (Header.PixelDepth == 24) || (Header.PixelDepth == 32);
  1504. if (!valid) {
  1505. errormessage.Copy (formaterrormessage, pathname);
  1506. return (errormessage.String());
  1507. }
  1508. // No error message.
  1509. return (NULL);
  1510. }
  1511. /***********************************************************************************************
  1512. * TrueColorTarga::Save -- *
  1513. * *
  1514. * INPUT: *
  1515. * *
  1516. * OUTPUT: *
  1517. * *
  1518. * WARNINGS: *
  1519. * *
  1520. * HISTORY: *
  1521. * 8/18/99 IML : Created. *
  1522. *=============================================================================================*/
  1523. char *TrueColorTarga::Save (const char *pathname)
  1524. {
  1525. const char *saveerrormessage = "Cannot save the image file ""%s"" (save error: %d).";
  1526. static char _messagebuffer [256];
  1527. long error;
  1528. StringBuilder errormessage (_messagebuffer, sizeof (_messagebuffer));
  1529. error = Targa::Save (pathname, TGAF_IMAGE);
  1530. if (error != 0) {
  1531. errormessage.Copy (saveerrormessage, pathname, error);
  1532. return (errormessage.String());
  1533. }
  1534. // No error message.
  1535. return (NULL);
  1536. }
  1537. /***********************************************************************************************
  1538. * TrueColorTarga::Blit -- *
  1539. * *
  1540. * INPUT: *
  1541. * *
  1542. * OUTPUT: *
  1543. * *
  1544. * WARNINGS: *
  1545. * *
  1546. * HISTORY: *
  1547. * 9/27/99 IML : Created. *
  1548. *=============================================================================================*/
  1549. void TrueColorTarga::Blit (TrueColorTarga &destination, unsigned x, unsigned y)
  1550. {
  1551. unsigned sourcebytespertexel, destbytespertexel;
  1552. unsigned maxc, maxr;
  1553. unsigned char *sourceptr, *destptr;
  1554. sourcebytespertexel = TGA_BytesPerPixel (Pixel_Depth());
  1555. destbytespertexel = TGA_BytesPerPixel (destination.Pixel_Depth());
  1556. // Currently, a true color targa cannot blit to itself.
  1557. ASSERT (this != &destination);
  1558. ASSERT (GetImage() != NULL);
  1559. ASSERT (destination.GetImage() != NULL);
  1560. // Sanity checks.
  1561. if ((x >= destination.Width()) || (y >= destination.Height())) return;
  1562. // Clip to destination.
  1563. if (x + Width() <= destination.Width()) {
  1564. maxc = Width();
  1565. } else {
  1566. maxc = destination.Width() - x;
  1567. }
  1568. if (y + Height() <= destination.Height()) {
  1569. maxr = Height();
  1570. } else {
  1571. maxr = destination.Height() - y;
  1572. }
  1573. // For each row...
  1574. for (unsigned r = 0; r < maxr; r++) {
  1575. sourceptr = ((unsigned char*) GetImage()) + (Width() * r * sourcebytespertexel);
  1576. destptr = ((unsigned char*) destination.GetImage()) + (((destination.Width() * (y + r)) + x) * destbytespertexel);
  1577. // For each column...
  1578. for (unsigned c = 0; c < maxc; c++) {
  1579. UnpackedTexelStruct unpackedtexel;
  1580. Pack_Texel (*Unpack_Texel (sourceptr, sourcebytespertexel, unpackedtexel), destptr, destbytespertexel);
  1581. // Advance to next texel.
  1582. sourceptr += sourcebytespertexel;
  1583. destptr += destbytespertexel;
  1584. }
  1585. }
  1586. }
  1587. /***********************************************************************************************
  1588. * TrueColorTarga::Scale -- *
  1589. * *
  1590. * INPUT: *
  1591. * *
  1592. * OUTPUT: *
  1593. * *
  1594. * WARNINGS: *
  1595. * *
  1596. * HISTORY: *
  1597. * 9/27/99 IML : Created. *
  1598. *=============================================================================================*/
  1599. void TrueColorTarga::Scale (TrueColorTarga &destination, unsigned width, unsigned height)
  1600. {
  1601. size_t size, newsize;
  1602. ColorSurface surface (Pixel_Format (Pixel_Depth()), Width(), Height());
  1603. SRBOOL success;
  1604. // Image data must exist.
  1605. ASSERT (GetImage() != NULL);
  1606. // Currently, source and destination pixel depths must match.
  1607. ASSERT (Pixel_Depth() == destination.Pixel_Depth());
  1608. // Clamp width, height.
  1609. if (width < 1) width = 1;
  1610. if (height < 1) height = 1;
  1611. // Define highest quality filter.
  1612. surface.setFilter (&srBSplineFilter);
  1613. // Enable clamping.
  1614. surface.setHClampMode (true);
  1615. surface.setVClampMode (true);
  1616. // Copy the targa source texel data to the Surrender surface.
  1617. size = Width() * Height() * TGA_BytesPerPixel (Pixel_Depth());
  1618. memcpy (surface.getDataPtr(), GetImage(), size);
  1619. // Scale.
  1620. success = surface.rescale (width, height);
  1621. ASSERT (success);
  1622. // Resize the destination to match that of the rescaled Surrender surface.
  1623. destination.Reformat (width, height, destination.Pixel_Depth());
  1624. // Copy the rescaled Surrender surface to the targa destination.
  1625. newsize = width * height * TGA_BytesPerPixel (destination.Pixel_Depth());
  1626. memcpy (destination.GetImage(), surface.getDataPtr(), newsize);
  1627. }
  1628. /***********************************************************************************************
  1629. * TrueColorTarga::Scale -- *
  1630. * *
  1631. * INPUT: *
  1632. * *
  1633. * OUTPUT: *
  1634. * *
  1635. * WARNINGS: *
  1636. * *
  1637. * HISTORY: *
  1638. * 10/11/99 IML : Created. *
  1639. *=============================================================================================*/
  1640. #define AVERAGE(a, b) \
  1641. (((a) + (b)) / 2) + (((a) + (b)) % 2)
  1642. #define BINARY_CHOP \
  1643. if (result == -1) { \
  1644. maxdimension = dimension; \
  1645. dimension = AVERAGE (dimension, mindimension); \
  1646. if (maxdimension == dimension) break; \
  1647. } else { \
  1648. if (result == 1) { \
  1649. mindimension = dimension; \
  1650. dimension = AVERAGE (dimension, maxdimension); \
  1651. if (mindimension == dimension) break; \
  1652. } else { \
  1653. break; \
  1654. } \
  1655. } \
  1656. void TrueColorTarga::Scale (TrueColorTarga &destination, float error)
  1657. {
  1658. const unsigned attemptcount = 8;
  1659. unsigned attempt, maxdimension, mindimension, dimension;
  1660. int result;
  1661. unsigned width, height;
  1662. TrueColorTarga *scaledtargaptr;
  1663. // Calculate width.
  1664. maxdimension = Width();
  1665. mindimension = 1;
  1666. dimension = AVERAGE (maxdimension, mindimension);
  1667. for (attempt = 0; attempt < attemptcount; attempt++) {
  1668. scaledtargaptr = new TrueColorTarga (dimension, Height(), Pixel_Depth());
  1669. ASSERT (scaledtargaptr != NULL);
  1670. Scale (*scaledtargaptr);
  1671. result = Compare (*scaledtargaptr, error);
  1672. delete scaledtargaptr;
  1673. BINARY_CHOP;
  1674. }
  1675. width = dimension;
  1676. // Calculate height.
  1677. maxdimension = Height();
  1678. mindimension = 1;
  1679. dimension = AVERAGE (maxdimension, mindimension);
  1680. for (attempt = 0; attempt < attemptcount; attempt++) {
  1681. scaledtargaptr = new TrueColorTarga (Width(), dimension, Pixel_Depth());
  1682. ASSERT (scaledtargaptr != NULL);
  1683. Scale (*scaledtargaptr);
  1684. result = Compare (*scaledtargaptr, error);
  1685. delete scaledtargaptr;
  1686. BINARY_CHOP;
  1687. }
  1688. height = dimension;
  1689. // Scale targa.
  1690. Scale (destination, width, height);
  1691. }
  1692. #undef AVERAGE
  1693. #undef BINARY_CHOP
  1694. /***********************************************************************************************
  1695. * TrueColorTarga::Transpose -- *
  1696. * *
  1697. * INPUT: *
  1698. * *
  1699. * OUTPUT: *
  1700. * *
  1701. * WARNINGS: *
  1702. * *
  1703. * HISTORY: *
  1704. * 10/07/99 IML : Created. *
  1705. *=============================================================================================*/
  1706. void TrueColorTarga::Transpose (TrueColorTarga &destination)
  1707. {
  1708. unsigned bytespertexel;
  1709. size_t size;
  1710. unsigned sourcestride;
  1711. unsigned char *sourceptr, *destinationptr, *stagingbuffer;
  1712. // Image data must exist.
  1713. ASSERT (GetImage() != NULL);
  1714. // Currently, source and destination pixel depths must match.
  1715. ASSERT (Pixel_Depth() == destination.Pixel_Depth());
  1716. bytespertexel = TGA_BytesPerPixel (Pixel_Depth());
  1717. size = Width() * Height() * bytespertexel;
  1718. stagingbuffer = new unsigned char [size];
  1719. ASSERT (stagingbuffer != NULL);
  1720. // Write transposed image data to staging buffer.
  1721. sourcestride = Width() * bytespertexel;
  1722. destinationptr = stagingbuffer;
  1723. for (unsigned y = 0; y < Width(); y++) {
  1724. sourceptr = ((unsigned char*) GetImage()) + (y * bytespertexel);
  1725. for (unsigned x = 0; x < Height(); x++) {
  1726. for (unsigned b = 0; b < bytespertexel; b++) {
  1727. *destinationptr++ = *(sourceptr + b);
  1728. }
  1729. sourceptr += sourcestride;
  1730. }
  1731. }
  1732. // Resize the destination to match that of the transposed source.
  1733. destination.Reformat (Height(), Width(), destination.Pixel_Depth());
  1734. // Copy transposed image data to destination.
  1735. memcpy (destination.GetImage(), stagingbuffer, size);
  1736. // Clean-up.
  1737. delete [] stagingbuffer;
  1738. }
  1739. /***********************************************************************************************
  1740. * TrueColorTarga::Compare -- *
  1741. * *
  1742. * INPUT: *
  1743. * *
  1744. * OUTPUT: *
  1745. * *
  1746. * WARNINGS: *
  1747. * *
  1748. * HISTORY: *
  1749. * 10/11/99 IML : Created. *
  1750. *=============================================================================================*/
  1751. int TrueColorTarga::Compare (TrueColorTarga &comparison, float epsilon)
  1752. {
  1753. unsigned bytespertexel, comparisonbytespertexel;
  1754. int epsilondelta;
  1755. float xscale, yscale;
  1756. int result;
  1757. unsigned char *imageptr;
  1758. ASSERT ((epsilon >= 0.0f) && (epsilon <= 1.0f));
  1759. bytespertexel = (size_t) TGA_BytesPerPixel (Pixel_Depth());
  1760. comparisonbytespertexel = (size_t) TGA_BytesPerPixel (comparison.Pixel_Depth());
  1761. epsilondelta = MAX (0, epsilon * 255.0f);
  1762. result = -1;
  1763. // Calculate destination:source scaling factors.
  1764. xscale = ((float) comparison.Width()) / Width();
  1765. yscale = ((float) comparison.Height()) / Height();
  1766. // For each texel in this targa measure the delta for the corresponding texel in the comparison targa.
  1767. imageptr = (unsigned char*) GetImage();
  1768. for (unsigned y = 0; y < Height(); y++) {
  1769. unsigned ys;
  1770. // NOTE: Position sample point at center of texel.
  1771. ys = MIN ((unsigned) ((y + 0.5f) * yscale), (unsigned) comparison.Height() - 1);
  1772. for (unsigned x = 0; x < Width(); x++) {
  1773. unsigned xs;
  1774. unsigned char *comparisonimageptr;
  1775. int delta;
  1776. UnpackedTexelStruct texel, comparisontexel;
  1777. // NOTE: Position sample point at center of texel.
  1778. xs = MIN ((unsigned) ((x + 0.5f) * xscale), (unsigned) comparison.Width() - 1);
  1779. comparisonimageptr = ((unsigned char*) comparison.GetImage()) + (((ys * comparison.Width()) + xs) * comparisonbytespertexel);
  1780. Unpack_Texel (imageptr, bytespertexel, texel);
  1781. Unpack_Texel (comparisonimageptr, comparisonbytespertexel, comparisontexel);
  1782. delta = abs (((int) texel.Byte [0]) - ((int) comparisontexel.Byte [0]));
  1783. for (unsigned b = 1; b < sizeof (texel.Byte); b++) {
  1784. delta = MAX (delta, abs (((int) texel.Byte [b]) - ((int) comparisontexel.Byte [b])));
  1785. }
  1786. if (delta > epsilondelta) {
  1787. // Return result.
  1788. return (1);
  1789. }
  1790. if (delta == epsilondelta) {
  1791. result = 0;
  1792. }
  1793. // Advance.
  1794. imageptr += bytespertexel;
  1795. }
  1796. }
  1797. // Return result.
  1798. return (result);
  1799. }
  1800. /***********************************************************************************************
  1801. * TrueColorTarga::Compare -- *
  1802. * *
  1803. * INPUT: *
  1804. * *
  1805. * OUTPUT: *
  1806. * *
  1807. * WARNINGS: *
  1808. * *
  1809. * HISTORY: *
  1810. * 10/11/99 IML : Created. *
  1811. *=============================================================================================*/
  1812. int TrueColorTarga::Compare (TrueColorTarga &comparison, unsigned x, unsigned y, float epsilon)
  1813. {
  1814. unsigned bytespertexel, comparisonbytespertexel;
  1815. int epsilondelta;
  1816. int result;
  1817. unsigned char *imageptr, *comparisonptr;
  1818. ASSERT ((epsilon >= 0.0f) && (epsilon <= 1.0f));
  1819. bytespertexel = (size_t) TGA_BytesPerPixel (Pixel_Depth());
  1820. comparisonbytespertexel = (size_t) TGA_BytesPerPixel (comparison.Pixel_Depth());
  1821. epsilondelta = MAX (0, epsilon * 255.0f);
  1822. result = -1;
  1823. // This targa must be contained by comparison targa.
  1824. if (x + Width() >= comparison.Width()) return (1);
  1825. if (y + Height() >= comparison.Height()) return (1);
  1826. // For each row...
  1827. for (unsigned r = 0; r < Height(); r++) {
  1828. imageptr = ((unsigned char*) GetImage()) + (Width() * r * bytespertexel);
  1829. comparisonptr = ((unsigned char*) comparison.GetImage()) + (((comparison.Width() * (y + r)) + x) * comparisonbytespertexel);
  1830. // For each column...
  1831. for (unsigned c = 0; c < Width(); c++) {
  1832. UnpackedTexelStruct texel, comparisontexel;
  1833. int delta;
  1834. Unpack_Texel (imageptr, bytespertexel, texel);
  1835. Unpack_Texel (comparisonptr, comparisonbytespertexel, comparisontexel);
  1836. delta = abs (((int) texel.Byte [0]) - ((int) comparisontexel.Byte [0]));
  1837. for (unsigned b = 1; b < sizeof (texel.Byte); b++) {
  1838. delta = MAX (delta, abs (((int) texel.Byte [b]) - ((int) comparisontexel.Byte [b])));
  1839. }
  1840. if (delta > epsilondelta) {
  1841. // Return result.
  1842. return (1);
  1843. }
  1844. if (delta == epsilondelta) {
  1845. result = 0;
  1846. }
  1847. // Advance.
  1848. imageptr += bytespertexel;
  1849. comparisonptr += comparisonbytespertexel;
  1850. }
  1851. }
  1852. // Return result.
  1853. return (result);
  1854. }
  1855. /***********************************************************************************************
  1856. * TrueColorTarga::Fill -- *
  1857. * *
  1858. * INPUT: *
  1859. * *
  1860. * OUTPUT: *
  1861. * *
  1862. * WARNINGS: *
  1863. * *
  1864. * HISTORY: *
  1865. * 9/27/99 IML : Created. *
  1866. *=============================================================================================*/
  1867. bool TrueColorTarga::Fill (const W3dRGBStruct &fillcolor)
  1868. {
  1869. const unsigned growthstep = 2048;
  1870. const UnpackedTexelStruct filltexel (fillcolor.R, fillcolor.G, fillcolor.B, 0);
  1871. DynamicVectorClass <PointStruct> *fourconnectedarrays;
  1872. unsigned index;
  1873. bool allfillcolor;
  1874. // Allocate dynamic arrays and initialize.
  1875. fourconnectedarrays = new DynamicVectorClass <PointStruct> [2];
  1876. ASSERT (fourconnectedarrays != NULL);
  1877. fourconnectedarrays [0].Set_Growth_Step (growthstep);
  1878. fourconnectedarrays [1].Set_Growth_Step (growthstep);
  1879. // Step thru all texels and pad all existing four-connected texels, and add new four-connected texel coordinates to dynamic array.
  1880. index = 0;
  1881. allfillcolor = true;
  1882. for (unsigned y = 0; y < Height(); y++) {
  1883. for (unsigned x = 0; x < Width(); x++) {
  1884. allfillcolor &= Fill_Four_Connected (x, y, filltexel, fourconnectedarrays [index]);
  1885. }
  1886. }
  1887. if (!allfillcolor) {
  1888. // While there are still unpadded texels...
  1889. while (fourconnectedarrays [index].Count() > 0) {
  1890. // For each four connected texel...
  1891. for (int e = 0; e < fourconnectedarrays [index].Count(); e++) {
  1892. Fill_Four_Connected ((fourconnectedarrays [index])[e].X, (fourconnectedarrays [index])[e].Y, filltexel, fourconnectedarrays [index ^ 1]);
  1893. }
  1894. fourconnectedarrays [index].Clear();
  1895. // Toggle dynamic array in which to place new four-connected texel coordinates.
  1896. index ^= 1;
  1897. }
  1898. }
  1899. // Clean-up.
  1900. delete [] fourconnectedarrays;
  1901. return (allfillcolor);
  1902. }
  1903. /***********************************************************************************************
  1904. * TrueColorTarga::Fill_Four_Connected -- *
  1905. * *
  1906. * INPUT: *
  1907. * *
  1908. * OUTPUT: *
  1909. * *
  1910. * WARNINGS: *
  1911. * *
  1912. * HISTORY: *
  1913. * 9/27/99 IML : Created. *
  1914. *=============================================================================================*/
  1915. bool TrueColorTarga::Fill_Four_Connected (unsigned x, unsigned y, const UnpackedTexelStruct &filltexel, DynamicVectorClass <PointStruct> &fourconnectedarray)
  1916. {
  1917. const unsigned fourconnectedcount = 4;
  1918. const unsigned bytespertexel = TGA_BytesPerPixel (Pixel_Depth());
  1919. static const int offsetx [fourconnectedcount] = {-1, +1, 0, 0};
  1920. static const int offsety [fourconnectedcount] = { 0, 0, -1, +1};
  1921. UnpackedTexelStruct unpackedtexel;
  1922. unsigned char *texelptr;
  1923. // Is texel (x, y) the fill color (ie. not already filled)?
  1924. texelptr = Get_Texel (x, y);
  1925. ASSERT (texelptr != NULL);
  1926. Unpack_Texel (texelptr, bytespertexel, unpackedtexel);
  1927. if (unpackedtexel == filltexel) {
  1928. unsigned adj;
  1929. bool fourconnected [fourconnectedcount];
  1930. unsigned fillcount, r, g, b;
  1931. // Initialize.
  1932. fillcount = 0;
  1933. r = g = b = 0;
  1934. // Fill the texel by averaging its non-fill color four-connected neighbors.
  1935. for (adj = 0; adj < fourconnectedcount; adj++) {
  1936. int adjx, adjy;
  1937. unsigned char *adjtexelptr;
  1938. fourconnected [adj] = false;
  1939. adjx = ((int) x) + offsetx [adj];
  1940. adjy = ((int) y) + offsety [adj];
  1941. adjtexelptr = Get_Texel (adjx, adjy);
  1942. if (adjtexelptr != NULL) {
  1943. Unpack_Texel (adjtexelptr, bytespertexel, unpackedtexel);
  1944. if (unpackedtexel == filltexel) {
  1945. fourconnected [adj] = true;
  1946. } else {
  1947. r += unpackedtexel.Red();
  1948. g += unpackedtexel.Green();
  1949. b += unpackedtexel.Blue();
  1950. fillcount++;
  1951. }
  1952. }
  1953. }
  1954. if (fillcount > 0) {
  1955. float oofillcount;
  1956. // Fill texel (x, y) with average of neighboring non-fill colors.
  1957. oofillcount = 1.0f / fillcount;
  1958. r = MIN (r * oofillcount, UCHAR_MAX);
  1959. g = MIN (g * oofillcount, UCHAR_MAX);
  1960. b = MIN (b * oofillcount, UCHAR_MAX);
  1961. UnpackedTexelStruct texel (r, g, b, 0);
  1962. Pack_Texel (texel, texelptr, bytespertexel);
  1963. // Add the coordinates of any neighboring fill colors.
  1964. for (adj = 0; adj < fourconnectedcount; adj++) {
  1965. if (fourconnected [adj]) {
  1966. PointStruct p;
  1967. p.X = ((int) x) + offsetx [adj];
  1968. p.Y = ((int) y) + offsety [adj];
  1969. fourconnectedarray.Add (p);
  1970. }
  1971. }
  1972. }
  1973. return (true);
  1974. } else {
  1975. return (false);
  1976. }
  1977. }
  1978. /***********************************************************************************************
  1979. * TrueColorTarga::Add -- *
  1980. * *
  1981. * INPUT: *
  1982. * *
  1983. * OUTPUT: *
  1984. * *
  1985. * WARNINGS: *
  1986. * *
  1987. * HISTORY: *
  1988. * 9/27/99 IML : Created. *
  1989. *=============================================================================================*/
  1990. void TrueColorTarga::Add (TrueColorTarga &targa)
  1991. {
  1992. unsigned width, height;
  1993. unsigned thisbytespertexel, targabytespertexel;
  1994. // Scale both targas to maximum width & height of both.
  1995. width = MAX (Width(), targa.Width());
  1996. height = MAX (Height(), targa.Height());
  1997. Scale (width, height);
  1998. targa.Scale (width, height);
  1999. // Step thru both targas and add the pixel data. Store result in this targa.
  2000. // For each row...
  2001. thisbytespertexel = TGA_BytesPerPixel (Pixel_Depth());
  2002. targabytespertexel = TGA_BytesPerPixel (targa.Pixel_Depth());
  2003. for (unsigned r = 0; r < height; r++) {
  2004. unsigned char *thisptr, *targaptr;
  2005. thisptr = ((unsigned char*) GetImage()) + (width * r * thisbytespertexel);
  2006. targaptr = ((unsigned char*) targa.GetImage()) + (width * r * targabytespertexel);
  2007. // For each column...
  2008. for (unsigned c = 0; c < width; c++) {
  2009. UnpackedTexelStruct thisunpackedtexel, targaunpackedtexel;
  2010. Unpack_Texel (thisptr, thisbytespertexel, thisunpackedtexel);
  2011. Unpack_Texel (targaptr, targabytespertexel, targaunpackedtexel);
  2012. thisunpackedtexel += targaunpackedtexel;
  2013. Pack_Texel (thisunpackedtexel, thisptr, thisbytespertexel);
  2014. // Advance to next texel.
  2015. thisptr += thisbytespertexel;
  2016. targaptr += targabytespertexel;
  2017. }
  2018. }
  2019. }
  2020. /***********************************************************************************************
  2021. * TrueColorTarga::Rasterize -- *
  2022. * *
  2023. * INPUT: *
  2024. * *
  2025. * OUTPUT: *
  2026. * *
  2027. * WARNINGS: *
  2028. * *
  2029. * HISTORY: *
  2030. * 05/22/00 IML : Created. *
  2031. *=============================================================================================*/
  2032. void TrueColorTarga::Rasterize (TrueColorTarga &destination, const W3dRGBStruct &fillcolor, unsigned vertexcount, const W3dRGBStruct *vertexcolors, Vector2 *vertexuvs)
  2033. {
  2034. const unsigned edgecount = 3;
  2035. const static float _vertexuvs [edgecount][2] =
  2036. {
  2037. {1.0f, 0.0f},
  2038. {0.0f, 0.0f},
  2039. {0.0f, 1.0f}
  2040. };
  2041. const UnpackedTexelStruct filltexel (fillcolor.R, fillcolor.G, fillcolor.B, 0);
  2042. const unsigned sqrtsamplecountminusone = 3;
  2043. const float oosqrtsamplecountminusone = 1.0f / sqrtsamplecountminusone;
  2044. const unsigned mindimension = 3;
  2045. Vector2 edges [edgecount];
  2046. float longestlength2, r, w, h;
  2047. unsigned longestedge;
  2048. Vector2 i, j;
  2049. float oowidthminustwo, ooheightminustwo;
  2050. unsigned bytespertexel;
  2051. unsigned char *destptr;
  2052. W3dRGBStruct colors [edgecount];
  2053. float oowidth, ooheight;
  2054. unsigned n;
  2055. // UV's must define a triangle.
  2056. ASSERT (vertexcount == edgecount);
  2057. // Define edges of source triangle.
  2058. edges [0] = vertexuvs [1] - vertexuvs [0];
  2059. edges [1] = vertexuvs [2] - vertexuvs [1];
  2060. edges [2] = vertexuvs [0] - vertexuvs [2];
  2061. // The destination triangle is a right-angled triangle that occupies one half of the
  2062. // destination targa. Size the destination targa usinh the following rules:
  2063. // (a) length of longest edge equals length of targa's diagonal (hypotenuse of destination triangle)
  2064. // (b) ratio of lengths of remaining two edges of source triangle equals ratio of dimensions of targa.
  2065. // Identify the longest edge.
  2066. longestlength2 = 0.0f;
  2067. for (unsigned e = 0; e < edgecount; e++) {
  2068. float length2 = edges [e].Length2();
  2069. if (length2 > longestlength2) {
  2070. longestedge = e;
  2071. longestlength2 = length2;
  2072. }
  2073. }
  2074. // Calculate width and height of destination targa.
  2075. r = edges [(longestedge + 1) % edgecount].Length() / edges [(longestedge + 2) % edgecount].Length();
  2076. w = WWMath::Sqrt (longestlength2 / (1.0f + r));
  2077. h = w / r;
  2078. // Resize the destination targa.
  2079. destination.Reformat (MAX (mindimension, (unsigned) (w * Width())), MAX (mindimension, (unsigned) (h * Height())), Pixel_Depth());
  2080. // Set up a coordinate system to define the source triangle in terms of the destination triangle.
  2081. i = -edges [(longestedge + 1) % edgecount];
  2082. j = edges [(longestedge + 2) % edgecount];
  2083. // Define colors that correspond to each vertex;
  2084. for (n = 0; n < vertexcount; n++) {
  2085. if (vertexcolors != NULL) {
  2086. colors [n] = vertexcolors [(n + longestedge) % edgecount];
  2087. } else {
  2088. colors [n] = fillcolor;
  2089. }
  2090. }
  2091. // Iterate over the destination triangle, sampling the source triangle at regular intervals.
  2092. // Also insert a one pixel wide filtered edge comprising interpolated vertex colors.
  2093. // For each destination texel...
  2094. bytespertexel = TGA_BytesPerPixel (Pixel_Depth());
  2095. oowidthminustwo = 1.0f / (destination.Width() - 2);
  2096. ooheightminustwo = 1.0f / (destination.Height() - 2);
  2097. destptr = (unsigned char*) destination.GetImage();
  2098. for (unsigned v = 0; v < destination.Height(); v++) {
  2099. for (unsigned u = 0; u < destination.Width(); u++) {
  2100. unsigned validsamplecount;
  2101. float r, g, b;
  2102. float fu, fv;
  2103. float oovalidsamplecount;
  2104. UnpackedTexelStruct averagetexel;
  2105. float clamped;
  2106. // For given destination texel subsample the source triangle.
  2107. validsamplecount = 0;
  2108. r = g = b = 0.0f;
  2109. for (unsigned sv = 0; sv <= sqrtsamplecountminusone; sv++) {
  2110. fv = (v + (sv * oosqrtsamplecountminusone) - 1.0f) * ooheightminustwo;
  2111. for (unsigned su = 0; su <= sqrtsamplecountminusone; su++) {
  2112. fu = (u + (su * oosqrtsamplecountminusone) - 1.0f) * oowidthminustwo;
  2113. // Which region does (fu, fv) fall in?
  2114. clamped = false;
  2115. if (fu < 0.0f) {
  2116. fu = 0.0f;
  2117. clamped = true;
  2118. } else {
  2119. if (fu > 1.0f) {
  2120. fu = 1.0f;
  2121. clamped = true;
  2122. }
  2123. }
  2124. if (fv < 0.0f) {
  2125. fv = 0.0f;
  2126. clamped = true;
  2127. } else {
  2128. if (fv > 1.0f) {
  2129. fv = 1.0f;
  2130. clamped = true;
  2131. }
  2132. }
  2133. if ((fu + fv) >= 1.0f) {
  2134. float ood = 1.0f / (fu + fv);
  2135. fu = fu * ood;
  2136. fv = fv * ood;
  2137. clamped = true;
  2138. }
  2139. if (!clamped) {
  2140. Vector2 p;
  2141. unsigned x, y;
  2142. UnpackedTexelStruct unpackedtexel;
  2143. unsigned char *sourceptr;
  2144. // Calculate source coordinates.
  2145. p = vertexuvs [(longestedge + 2) % 3] + (fu * i) + (fv * j);
  2146. x = (unsigned) MIN (MAX ((int) (p.X * Width()), 0), ((int) Width()) - 1);
  2147. y = (unsigned) MIN (MAX ((int) (p.Y * Height()), 0), ((int) Height()) - 1);
  2148. // Sample the source.
  2149. sourceptr = ((unsigned char*) GetImage()) + (((Width() * y) + x) * bytespertexel);
  2150. Unpack_Texel (sourceptr, bytespertexel, unpackedtexel);
  2151. r += unpackedtexel.Red();
  2152. g += unpackedtexel.Green();
  2153. b += unpackedtexel.Blue();
  2154. } else {
  2155. // Calculate source color.
  2156. r += 0.5f * ((colors [1].R * fu) + (colors [2].R * (1.0f - fu)) + (colors [2].R * (1.0f - fv)) + (colors [0].R * fv));
  2157. g += 0.5f * ((colors [1].G * fu) + (colors [2].G * (1.0f - fu)) + (colors [2].G * (1.0f - fv)) + (colors [0].G * fv));
  2158. b += 0.5f * ((colors [1].B * fu) + (colors [2].B * (1.0f - fu)) + (colors [2].B * (1.0f - fv)) + (colors [0].B * fv));
  2159. }
  2160. validsamplecount++;
  2161. }
  2162. }
  2163. // One or more samples must have been taken.
  2164. ASSERT (validsamplecount > 0);
  2165. // Destination texel = average of samples.
  2166. oovalidsamplecount = 1.0f / validsamplecount;
  2167. averagetexel.Set (r * oovalidsamplecount, g * oovalidsamplecount, b * oovalidsamplecount, 0);
  2168. Pack_Texel (averagetexel, destptr, bytespertexel);
  2169. // Advance.
  2170. destptr += bytespertexel;
  2171. }
  2172. }
  2173. // Replace source UV's with destination coordinates for caller.
  2174. oowidth = 1.0f / destination.Width();
  2175. ooheight = 1.0f / destination.Height();
  2176. for (n = 0; n < vertexcount; n++) {
  2177. vertexuvs [n].Set (_vertexuvs [((edgecount - 1 - longestedge) + n) % edgecount][0], _vertexuvs [((edgecount - 1 - longestedge) + n) % edgecount][1]);
  2178. // Scale and translate UV's to accomodate one texel wide filtered edge.
  2179. vertexuvs [n].U *= (destination.Width() - 2) * oowidth;
  2180. vertexuvs [n].V *= (destination.Height() - 2) * ooheight;
  2181. vertexuvs [n].U += oowidth;
  2182. vertexuvs [n].V += ooheight;
  2183. }
  2184. }
  2185. /***********************************************************************************************
  2186. * TrueColorTarga::Pad -- *
  2187. * *
  2188. * INPUT: *
  2189. * *
  2190. * OUTPUT: *
  2191. * *
  2192. * WARNINGS: *
  2193. * *
  2194. * HISTORY: *
  2195. * 05/22/00 IML : Created. *
  2196. *=============================================================================================*/
  2197. void TrueColorTarga::Pad (TrueColorTarga &destination, unsigned padwidth, unsigned padheight, const W3dRGBStruct &padcolor, DynamicVectorClass <PackingTriangle*> &triangleptrs)
  2198. {
  2199. unsigned w, h;
  2200. float oobw, oobh;
  2201. UnpackedTexelStruct padtexel (padcolor.R, padcolor.G, padcolor.B, 0);
  2202. Matrix3 uvmatrix;
  2203. // Optimization: was a non-zero pad thickness specified?
  2204. if ((padwidth > 0) || (padheight > 0)) {
  2205. // Round pad width and height to even no. to ensure that there is an equal amount of padding on each side.
  2206. padwidth += (padwidth & 0x1);
  2207. padheight += (padheight & 0x1);
  2208. // Calculate scaling and translation factors.
  2209. w = Width();
  2210. h = Height();
  2211. oobw = 1.0f / (w + padwidth);
  2212. oobh = 1.0f / (h + padheight);
  2213. if (this == &destination) {
  2214. TrueColorTarga targacopy (w, h, Pixel_Depth());
  2215. Blit (targacopy, 0, 0);
  2216. // Resize to match that of the padded targa.
  2217. Reformat (w + padwidth, h + padheight, Pixel_Depth());
  2218. // Clear to pad color.
  2219. Clear (padtexel);
  2220. // Blit copy to this targa. Offset by pad thickness.
  2221. targacopy.Blit (*this, padwidth >> 1, padheight >> 1);
  2222. } else {
  2223. // Resize the destination to match that of the padded targa.
  2224. destination.Reformat (w + padwidth, h + padheight, Pixel_Depth());
  2225. // Clear destination to pad color.
  2226. destination.Clear (padtexel);
  2227. // Blit this to destination. Offset by pad thickness.
  2228. Blit (destination, padwidth >> 1, padheight >> 1);
  2229. }
  2230. // Pad pad color.
  2231. destination.Fill (padcolor);
  2232. // Calculate scaling and translation matrix.
  2233. uvmatrix [0].Set (w * oobw, 0.0f, 0.0f);
  2234. uvmatrix [1].Set (0.0f, h * oobh, 0.0f);
  2235. uvmatrix [2].Set ((padwidth >> 1) * oobw, (padheight >> 1) * oobh, 1.0f);
  2236. // Transpose transform matrix.
  2237. // NOTE: This matrix is set up for post multiplication (ie. V' = M X V, matrix M, vector V).
  2238. uvmatrix = uvmatrix.Transpose();
  2239. // Apply transform to all triangles...
  2240. for (int t = 0; t < triangleptrs.Count(); t++) {
  2241. for (unsigned v = 0; v < Triangle::VERTICES_COUNT; v++) {
  2242. Vector3 uv;
  2243. // NOTE: Create a homogeneous vector (3-components) from the UV vector so that it can be transformed by the matrix.
  2244. uv.Set (triangleptrs [t]->PackedUVs [v].X, triangleptrs [t]->PackedUVs [v].Y, 1.0f);
  2245. uv = uvmatrix * uv;
  2246. triangleptrs [t]->PackedUVs [v].Set (uv.X, uv.Y);
  2247. }
  2248. }
  2249. }
  2250. }
  2251. /***********************************************************************************************
  2252. * TrianglePacker::SampleSurface::SampleSurface -- *
  2253. * *
  2254. * INPUT: *
  2255. * *
  2256. * OUTPUT: *
  2257. * *
  2258. * WARNINGS: *
  2259. * *
  2260. * HISTORY: *
  2261. * 06/12/00 IML : Created. *
  2262. *=============================================================================================*/
  2263. TrianglePacker::SampleSurface::SampleSurface (unsigned width, unsigned height, ProceduralTexture *blendtexture)
  2264. {
  2265. SampleStruct *sampleptr;
  2266. Width = width;
  2267. Height = height;
  2268. SampledTexelCount = 0;
  2269. Surface = new SampleStruct [width * height];
  2270. ASSERT (Surface != NULL);
  2271. // Initialize surface.
  2272. sampleptr = Surface;
  2273. for (unsigned s = 0; s < width * height; s++) {
  2274. sampleptr->Red = 0;
  2275. sampleptr->Green = 0;
  2276. sampleptr->Blue = 0;
  2277. sampleptr->Count = 0;
  2278. sampleptr->Priority = 0;
  2279. sampleptr++;
  2280. }
  2281. BlendTexture = blendtexture;
  2282. }
  2283. /***********************************************************************************************
  2284. * TrianglePacker::SampleSurface::Sample -- *
  2285. * *
  2286. * INPUT: *
  2287. * *
  2288. * OUTPUT: *
  2289. * *
  2290. * WARNINGS: *
  2291. * *
  2292. * HISTORY: *
  2293. * 06/12/00 IML : Created. *
  2294. *=============================================================================================*/
  2295. bool TrianglePacker::SampleSurface::Sample (const Vector2 &samplepoint, const ProjectionTriangle &projectiontriangle, unsigned priority)
  2296. {
  2297. const float epsilon = 0.0001f;
  2298. int u, v;
  2299. Vector2 t0, t1, t2;
  2300. float alpha, beta;
  2301. u = floorf (samplepoint.U);
  2302. v = floorf (samplepoint.V);
  2303. // NOTE: Sample point must be in range.
  2304. if ((u < 0) || (u >= (int) Width)) return (false);
  2305. if ((v < 0) || (v >= (int) Height)) return (false);
  2306. // Use the baricentric method to determine if the sample point lies inside
  2307. // the triangle (see Graphics Gems I p390).
  2308. t0 = samplepoint - projectiontriangle.ProjectionUVs [0];
  2309. t1 = projectiontriangle.ProjectionUVs [1];
  2310. t2 = projectiontriangle.ProjectionUVs [2];
  2311. if (WWMath::Fabs (t1.U) <= epsilon) {
  2312. beta = t0.U / t2.U;
  2313. if (beta <= 0.0f || beta >= 1.0f) return (false);
  2314. alpha = (t0.V - (beta * t2.V)) / t1.V;
  2315. } else {
  2316. beta = (t0.V * t1.U - t0.U * t1.V) / (t2.V * t1.U - t2.U * t1.V);
  2317. if (beta <= 0.0f || beta >= 1.0f) return (false);
  2318. alpha = (t0.U - beta * t2.U) / t1.U;
  2319. }
  2320. // Does the projection triangle contain the sample point?
  2321. if ((alpha > 0.0f) && ((alpha + beta) < 1.0f)) {
  2322. Vector2 uv;
  2323. W3dRGBStruct color;
  2324. SampleStruct *sampleptr;
  2325. // Use the baricentric coordinates (alpha, beta) to calculate the texture coordinates.
  2326. uv = (alpha * projectiontriangle.SourceUVs [1]) + (beta * projectiontriangle.SourceUVs [2]) + projectiontriangle.SourceUVs [0];
  2327. if (!projectiontriangle.SourceTargaPtr->Get_Color (uv, color)) return (false);
  2328. // Should the texel color be blended with a procedural texture?
  2329. if (BlendTexture != NULL) {
  2330. Vector3 p;
  2331. float v;
  2332. p = (alpha * projectiontriangle.Points [1]) + (beta * projectiontriangle.Points [2]) + projectiontriangle.Points [0];
  2333. v = BlendTexture->Value (p);
  2334. color.Set ((uint8) (color.R * v), (uint8) (color.G * v), (uint8) (color.B * v));
  2335. }
  2336. // Add the sample.
  2337. sampleptr = Surface + (v * Width + u);
  2338. sampleptr->Red += color.R;
  2339. sampleptr->Green += color.G;
  2340. sampleptr->Blue += color.B;
  2341. if (sampleptr->Count == 0) SampledTexelCount++;
  2342. sampleptr->Count++;
  2343. // Update priority.
  2344. if (priority > sampleptr->Priority) sampleptr->Priority = priority;
  2345. return (true);
  2346. } else {
  2347. return (false);
  2348. }
  2349. }
  2350. /***********************************************************************************************
  2351. * TrianglePacker::SampleSurface::Sample -- *
  2352. * *
  2353. * INPUT: *
  2354. * *
  2355. * OUTPUT: *
  2356. * *
  2357. * WARNINGS: *
  2358. * *
  2359. * HISTORY: *
  2360. * 06/12/00 IML : Created. *
  2361. *=============================================================================================*/
  2362. bool TrianglePacker::SampleSurface::Sample (float alpha, float beta, const ProjectionTriangle &projectiontriangle, unsigned priority)
  2363. {
  2364. Vector2 samplepoint;
  2365. int u, v;
  2366. Vector2 uv;
  2367. W3dRGBStruct color;
  2368. SampleStruct *sampleptr;
  2369. // Use the baricentric coordinates (alpha, beta) to calculate the sample point.
  2370. samplepoint = (alpha * projectiontriangle.ProjectionUVs [1]) + (beta * projectiontriangle.ProjectionUVs [2]) + projectiontriangle.ProjectionUVs [0];
  2371. u = floorf (samplepoint.U);
  2372. v = floorf (samplepoint.V);
  2373. // NOTE: Sample point must be in range.
  2374. if ((u < 0) || (u >= (int) Width)) return (false);
  2375. if ((v < 0) || (v >= (int) Height)) return (false);
  2376. // Use the baricentric coordinates (alpha, beta) to calculate the texture coordinates.
  2377. uv = (alpha * projectiontriangle.SourceUVs [1]) + (beta * projectiontriangle.SourceUVs [2]) + projectiontriangle.SourceUVs [0];
  2378. if (!projectiontriangle.SourceTargaPtr->Get_Color (uv, color)) return (false);
  2379. // Should the texel color be blended with a procedural texture?
  2380. if (BlendTexture != NULL) {
  2381. Vector3 p;
  2382. float v;
  2383. p = (alpha * projectiontriangle.Points [1]) + (beta * projectiontriangle.Points [2]) + projectiontriangle.Points [0];
  2384. v = BlendTexture->Value (p);
  2385. color.Set ((uint8) (color.R * v), (uint8) (color.G * v), (uint8) (color.B * v));
  2386. }
  2387. // Add the sample.
  2388. sampleptr = Surface + (v * Width + u);
  2389. sampleptr->Red += color.R;
  2390. sampleptr->Green += color.G;
  2391. sampleptr->Blue += color.B;
  2392. if (sampleptr->Count == 0) SampledTexelCount++;
  2393. sampleptr->Count++;
  2394. // Update priority.
  2395. if (priority > sampleptr->Priority) sampleptr->Priority = priority;
  2396. return (true);
  2397. }
  2398. /***********************************************************************************************
  2399. * Page::Page -- *
  2400. * *
  2401. * INPUT: *
  2402. * *
  2403. * OUTPUT: *
  2404. * *
  2405. * WARNINGS: *
  2406. * *
  2407. * HISTORY: *
  2408. * 9/27/99 IML : Created. *
  2409. *=============================================================================================*/
  2410. Page::Page (unsigned bitdepth, const W3dRGBStruct &clearcolor)
  2411. : TrueColorTarga (PAGE_WIDTH, PAGE_HEIGHT, bitdepth)
  2412. {
  2413. const UnpackedTexelStruct cleartexel (clearcolor.R, clearcolor.G, clearcolor.B, 0);
  2414. Region *regionptr;
  2415. // Clear this page to a recognizable color.
  2416. Clear (cleartexel);
  2417. // Create a single region for the entire page.
  2418. regionptr = new Region;
  2419. ASSERT (regionptr != NULL);
  2420. regionptr->Set (0, 0, PAGE_WIDTH - 1, PAGE_HEIGHT - 1);
  2421. VacantRegionList.Add_Head (regionptr);
  2422. Reset_Statistics();
  2423. }
  2424. /***********************************************************************************************
  2425. * Page::Page -- *
  2426. * *
  2427. * INPUT: *
  2428. * *
  2429. * OUTPUT: *
  2430. * *
  2431. * WARNINGS: *
  2432. * *
  2433. * HISTORY: *
  2434. * 9/27/99 IML : Created. *
  2435. *=============================================================================================*/
  2436. Page::~Page()
  2437. {
  2438. // Delete the used region list.
  2439. while (UsedRegionList.First_Valid()) {
  2440. delete UsedRegionList.First_Valid();
  2441. }
  2442. // Delete the region list.
  2443. while (VacantRegionList.First_Valid()) {
  2444. delete VacantRegionList.First_Valid();
  2445. }
  2446. }
  2447. /***********************************************************************************************
  2448. * Page::Reset_Statisitcs -- *
  2449. * *
  2450. * INPUT: *
  2451. * *
  2452. * OUTPUT: *
  2453. * *
  2454. * WARNINGS: *
  2455. * *
  2456. * HISTORY: *
  2457. * 9/27/99 IML : Created. *
  2458. *=============================================================================================*/
  2459. void Page::Reset_Statistics()
  2460. {
  2461. AssetCount = 0;
  2462. UsedTexelCount = 0;
  2463. ReplicaTexelCount = 0;
  2464. }
  2465. /***********************************************************************************************
  2466. * Page::Pack -- *
  2467. * *
  2468. * INPUT: *
  2469. * *
  2470. * OUTPUT: *
  2471. * *
  2472. * WARNINGS: *
  2473. * *
  2474. * HISTORY: *
  2475. * 05/19/00 IML : Created. *
  2476. *=============================================================================================*/
  2477. bool Page::Pack (TrueColorTarga &targa, float epsilon, DynamicVectorClass <PackingTriangle*> &triangleptrs)
  2478. {
  2479. TrueColorTarga *targaptr [TRANSPOSE_COUNT];
  2480. bool replica, packed;
  2481. TransposeEnum transposed;
  2482. unsigned cost;
  2483. Region targetregion;
  2484. // Targa must fit on this page.
  2485. ASSERT (targa.Width() <= Width());
  2486. ASSERT (targa.Height() <= Height());
  2487. targaptr [UNTRANSPOSED] = &targa;
  2488. // Create a transposed version of the targa.
  2489. targaptr [TRANSPOSED] = new TrueColorTarga (targaptr [UNTRANSPOSED]->Width(), targaptr [UNTRANSPOSED]->Height(), targaptr [UNTRANSPOSED]->Pixel_Depth());
  2490. ASSERT (targaptr [TRANSPOSED] != NULL);
  2491. targaptr [UNTRANSPOSED]->Blit (*targaptr [TRANSPOSED], 0, 0);
  2492. targaptr [TRANSPOSED]->Transpose();
  2493. // Attempt to find a replica targa on this page.
  2494. transposed = (targaptr [UNTRANSPOSED]->Width() >= targaptr [UNTRANSPOSED]->Height()) ? UNTRANSPOSED : TRANSPOSED;
  2495. replica = Replica_Region (*targaptr [transposed], epsilon, targetregion);
  2496. if (!replica) {
  2497. transposed = (transposed == UNTRANSPOSED) ? TRANSPOSED : UNTRANSPOSED;
  2498. replica = Replica_Region (*targaptr [transposed], epsilon, targetregion);
  2499. }
  2500. if (replica) {
  2501. // Update statistics.
  2502. ReplicaTexelCount += targaptr [transposed]->Width() * targaptr [transposed]->Height();
  2503. } else {
  2504. // Attempt to pack targa with width >= height first.
  2505. transposed = (targaptr [UNTRANSPOSED]->Width() >= targaptr [UNTRANSPOSED]->Height()) ? UNTRANSPOSED : TRANSPOSED;
  2506. // Can the targa be packed?
  2507. packed = Lowest_Cost_Region (*targaptr [transposed], cost, targetregion);
  2508. // If not packed then transpose the targa and attempt to pack it again.
  2509. if (!packed) {
  2510. transposed = (transposed == UNTRANSPOSED) ? TRANSPOSED : UNTRANSPOSED;
  2511. packed = Lowest_Cost_Region (*targaptr [transposed], cost, targetregion);
  2512. }
  2513. if (packed) {
  2514. // Region must be aligned on an even boundary. This will reduce texel bleeding as a result of mip-mapping.
  2515. ASSERT ((targetregion.X0 & 0x1) == 0x0);
  2516. ASSERT ((targetregion.Y0 & 0x1) == 0x0);
  2517. ASSERT ((targetregion.X1 & 0x1) == 0x1);
  2518. ASSERT ((targetregion.Y1 & 0x1) == 0x1);
  2519. Insert_Region (targetregion);
  2520. // Blit the targa onto this page.
  2521. targaptr [transposed]->Blit (*this, targetregion.X0, targetregion.Y0);
  2522. // Update statistics.
  2523. UsedTexelCount += targaptr [transposed]->Width() * targaptr [transposed]->Height();
  2524. }
  2525. }
  2526. // Transform the UV's to reflect the packing.
  2527. if (replica || packed) {
  2528. float su, sv; // Scaling factors of the targa within this page.
  2529. float tu, tv; // Translation of the targa within this page.
  2530. Matrix3 uvmatrix;
  2531. // Calculate scaling and translation factors.
  2532. su = ((float) (targaptr [UNTRANSPOSED]->Width())) / Width();
  2533. sv = ((float) (targaptr [UNTRANSPOSED]->Height())) / Height();
  2534. tu = ((float) targetregion.X0) / Width();
  2535. tv = ((float) targetregion.Y0) / Height();
  2536. // Was the targa transposed?
  2537. if (transposed == TRANSPOSED) {
  2538. // Define scaling/rotation vectors in transform matrix.
  2539. uvmatrix [0].Set (0.0f, su , 0.0f);
  2540. uvmatrix [1].Set (sv , 0.0f, 0.0f);
  2541. } else {
  2542. // Define scaling/rotation vectors in transform matrix.
  2543. uvmatrix [0].Set (su , 0.0f, 0.0f);
  2544. uvmatrix [1].Set (0.0f, sv , 0.0f);
  2545. }
  2546. // Define translation vector in transform matrix.
  2547. uvmatrix [2].Set (tu, tv, 1.0f);
  2548. // Transpose transform matrix.
  2549. // NOTE: Thus matrix is set up for post multiplication (ie. V' = M X V, matrix M, vector V).
  2550. uvmatrix = uvmatrix.Transpose();
  2551. // Iterate over all triangles and transform their packed UV's.
  2552. for (int t = 0; t < triangleptrs.Count(); t++) {
  2553. for (unsigned v = 0; v < Triangle::VERTICES_COUNT; v++) {
  2554. Vector3 uv;
  2555. // NOTE: Create a homogeneous vector (3-components) from the UV vector so that it can be transformed by the matrix.
  2556. uv.Set (triangleptrs [t]->PackedUVs [v].X, triangleptrs [t]->PackedUVs [v].Y, 1.0f);
  2557. uv = uvmatrix * uv;
  2558. triangleptrs [t]->PackedUVs [v].Set (uv.X, uv.Y);
  2559. }
  2560. }
  2561. // Update statistics.
  2562. AssetCount++;
  2563. }
  2564. // Clean-up.
  2565. delete targaptr [TRANSPOSED];
  2566. // Return success.
  2567. return (replica || packed);
  2568. }
  2569. /***********************************************************************************************
  2570. * Page::Replica_Region -- *
  2571. * * *
  2572. * INPUT: *
  2573. * *
  2574. * OUTPUT: *
  2575. * *
  2576. * WARNINGS: *
  2577. * *
  2578. * HISTORY: *
  2579. * 11/17/99 IML : Created. *
  2580. *=============================================================================================*/
  2581. bool Page::Replica_Region (TrueColorTarga &targa, float epsilon, Region &replicaregion)
  2582. {
  2583. bool replica;
  2584. Region *regionptr;
  2585. unsigned regionwidth, regionheight;
  2586. // Iterate over all used regions to see if one matches the targa to within tolerance.
  2587. replica = false;
  2588. regionptr = (Region*) UsedRegionList.First_Valid();
  2589. while (regionptr != NULL) {
  2590. regionwidth = regionptr->X1 - regionptr->X0 + 1;
  2591. regionheight = regionptr->Y1 - regionptr->Y0 + 1;
  2592. // If regions are the same size...
  2593. if ((regionwidth == targa.Width()) && (regionheight == targa.Height())) {
  2594. // If regions are deemed equal to within epsilon...
  2595. if (targa.Compare (*this, regionptr->X0, regionptr->Y0, epsilon) <= 0) {
  2596. replica = true;
  2597. replicaregion = *regionptr;
  2598. break;
  2599. }
  2600. }
  2601. // Next region.
  2602. regionptr = (Region*) regionptr->Next_Valid();
  2603. }
  2604. return (replica);
  2605. }
  2606. /***********************************************************************************************
  2607. * Page::Lowest_Cost_Region -- *
  2608. * * *
  2609. * INPUT: *
  2610. * *
  2611. * OUTPUT: *
  2612. * *
  2613. * WARNINGS: *
  2614. * *
  2615. * HISTORY: *
  2616. * 10/8/99 IML : Created. *
  2617. *=============================================================================================*/
  2618. bool Page::Lowest_Cost_Region (TrueColorTarga &targa, unsigned &lowestcost, Region &lowestcostregion)
  2619. {
  2620. unsigned width, height;
  2621. bool packed;
  2622. unsigned mintotalsubregioncount;
  2623. Region *regionptr;
  2624. // Find a vacant region in the page that will incur the lowest cost if it contains the targa asset.
  2625. width = targa.Width();
  2626. height = targa.Height();
  2627. packed = false;
  2628. mintotalsubregioncount = UINT_MAX;
  2629. regionptr = (Region*) VacantRegionList.First_Valid();
  2630. while (regionptr != NULL) {
  2631. Region candidateregion;
  2632. // Does the asset fit inside the region?
  2633. if (regionptr->Accomodates (width, height, candidateregion)) {
  2634. unsigned totalsubregioncount, subregioncount;
  2635. Region *fragmentregionptr, *subregions;
  2636. fragmentregionptr = (Region*) VacantRegionList.First_Valid();
  2637. totalsubregioncount = 0;
  2638. while (fragmentregionptr != NULL) {
  2639. // Calculate the fragmentation count for the fragment region.
  2640. if (fragmentregionptr->Intersects (candidateregion, subregioncount, &subregions)) {
  2641. totalsubregioncount += subregioncount;
  2642. }
  2643. // Next region.
  2644. fragmentregionptr = (Region*) fragmentregionptr->Next_Valid();
  2645. }
  2646. // Is this the minimum total subregion count?
  2647. if (totalsubregioncount < mintotalsubregioncount) {
  2648. packed = true;
  2649. lowestcostregion = candidateregion;
  2650. mintotalsubregioncount = totalsubregioncount;
  2651. }
  2652. }
  2653. // Next region.
  2654. regionptr = (Region*) regionptr->Next_Valid();
  2655. }
  2656. lowestcost = mintotalsubregioncount;
  2657. // Can the asset be packed on this page?
  2658. return (packed);
  2659. }
  2660. /***********************************************************************************************
  2661. * Page::Insert_Region -- *
  2662. * * *
  2663. * INPUT: *
  2664. * *
  2665. * OUTPUT: *
  2666. * *
  2667. * WARNINGS: *
  2668. * *
  2669. * HISTORY: *
  2670. * 10/8/99 IML : Created. *
  2671. *=============================================================================================*/
  2672. void Page::Insert_Region (const Region &insertionregion)
  2673. {
  2674. Region *regionptr;
  2675. // First, add the insertion region to the used region list.
  2676. regionptr = new Region (insertionregion);
  2677. ASSERT (regionptr != NULL);
  2678. UsedRegionList.Add_Head (regionptr);
  2679. // Walk down the vacant list. If any region intersects the target region, fragment it into
  2680. // 0 to 4 sub-regions, depending on the relative location of the clipped region.
  2681. regionptr = (Region*) VacantRegionList.First_Valid();
  2682. while (regionptr != NULL) {
  2683. unsigned subregioncount;
  2684. Region *subregions, *removalptr;
  2685. // If there is an intersection between the region and target region...
  2686. if (regionptr->Intersects (insertionregion, subregioncount, &subregions)) {
  2687. // Remove the original region, advance to next region.
  2688. removalptr = regionptr;
  2689. regionptr = (Region*) regionptr->Next_Valid();
  2690. delete removalptr;
  2691. // Add the subregions to the head of the list.
  2692. // NOTE: Adding to the head of the list will ensure that they will not get re-processed by the current 'list walker'.
  2693. for (int s = ((int) subregioncount) - 1; s >= 0; s--) {
  2694. // Only add the subregion if it is not contained by another region ie. avoid redundancy.
  2695. if (!Contains (subregions [s])) {
  2696. Region *newregionptr;
  2697. newregionptr = new Region (subregions [s]);
  2698. ASSERT (newregionptr != NULL);
  2699. VacantRegionList.Add_Head (newregionptr);
  2700. }
  2701. }
  2702. } else {
  2703. // Advance to next region.
  2704. regionptr = (Region*) regionptr->Next_Valid();
  2705. }
  2706. }
  2707. }
  2708. /***********************************************************************************************
  2709. * Page::Region::Accomodates -- *
  2710. * *
  2711. * INPUT: *
  2712. * *
  2713. * OUTPUT: *
  2714. * *
  2715. * WARNINGS: *
  2716. * *
  2717. * HISTORY: *
  2718. * 9/27/99 IML : Created. *
  2719. *=============================================================================================*/
  2720. bool Page::Region::Accomodates (unsigned width, unsigned height, Region &targetregion)
  2721. {
  2722. if ((width <= Width()) && (height <= Height())) {
  2723. // Locate the target region in the top-left corner of this region.
  2724. targetregion.Set (X0, Y0, X0 + width - 1, Y0 + height - 1);
  2725. return (true);
  2726. } else {
  2727. return (false);
  2728. }
  2729. }
  2730. /***********************************************************************************************
  2731. * Page::Region::Intersects -- *
  2732. * *
  2733. * INPUT: *
  2734. * *
  2735. * OUTPUT: *
  2736. * *
  2737. * WARNINGS: *
  2738. * *
  2739. * HISTORY: *
  2740. * 9/27/99 IML : Created. *
  2741. *=============================================================================================*/
  2742. bool Page::Region::Intersects (const Region &targetregion, unsigned &subregioncount, Region **subregions)
  2743. {
  2744. const unsigned maxsubregioncount = 4; // Maximum no. of fragmented subregions.
  2745. static Region _subregions [maxsubregioncount];
  2746. unsigned s;
  2747. // First, test the target region for trivial rejection against the left, right, top and bottom
  2748. // sides of this region respectively.
  2749. if (targetregion.X1 < X0) return (false);
  2750. if (targetregion.X0 > X1) return (false);
  2751. if (targetregion.Y1 < Y0) return (false);
  2752. if (targetregion.Y0 > Y1) return (false);
  2753. // Now it is known that the target region intersects this region in some way.
  2754. // Count and generate the subregions between the target region and this region.
  2755. s = 0;
  2756. if (targetregion.X0 > X0) {
  2757. _subregions [s].Set (X0, Y0, targetregion.X0 - 1, Y1);
  2758. s++;
  2759. }
  2760. if (targetregion.X1 < X1) {
  2761. _subregions [s].Set (targetregion.X1 + 1, Y0, X1, Y1);
  2762. s++;
  2763. }
  2764. if (targetregion.Y0 > Y0) {
  2765. _subregions [s].Set (X0, Y0, X1, targetregion.Y0 - 1);
  2766. s++;
  2767. }
  2768. if (targetregion.Y1 < Y1) {
  2769. _subregions [s].Set (X0, targetregion.Y1 + 1, X1, Y1);
  2770. s++;
  2771. }
  2772. subregioncount = s;
  2773. *subregions = _subregions;
  2774. return (true);
  2775. }
  2776. /***********************************************************************************************
  2777. * Page::Region::Contains -- *
  2778. * *
  2779. * INPUT: *
  2780. * *
  2781. * OUTPUT: *
  2782. * *
  2783. * WARNINGS: *
  2784. * *
  2785. * HISTORY: *
  2786. * 9/27/99 IML : Created. *
  2787. *=============================================================================================*/
  2788. bool Page::Contains (const Region &testregion)
  2789. {
  2790. Region *regionptr;
  2791. // Step down the vacant list until a region is found that contains the test region or end of list.
  2792. regionptr = (Region*) VacantRegionList.First_Valid();
  2793. while (regionptr != NULL) {
  2794. if ((testregion.X0 >= regionptr->X0) && (testregion.X1 <= regionptr->X1) && (testregion.Y0 >= regionptr->Y0) && (testregion.Y1 <= regionptr->Y1)) {
  2795. // Test region is contained by a region in the list.
  2796. return (true);
  2797. }
  2798. regionptr = (Region*) regionptr->Next_Valid();
  2799. }
  2800. // Test region is not contained by a region in the list.
  2801. return (false);
  2802. }