Heightmap.cpp 92 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************
  5. Material Indexes can be set in any of the first 3 slots, example: 0, 15, 0
  6. Material Indexes for MtrlCombo's however need to be sorted: 15, 7, 0, 0 (from biggest to smallest),
  7. so there won't be MtrlCombo's with same materials but different order (like 7, 15, 0, 0)
  8. Color image can be IMAGE_R8G8B8 , IMAGE_F32_3, IMAGE_NONE (all white)
  9. Material Blend image can be IMAGE_R8G8B8A8, IMAGE_F32_4
  10. /******************************************************************************/
  11. #define LODS 4
  12. #define MATERIAL2_EPS 0.01f // was 0.055f but smaller value is needed now because of multi-material per-pixel blending, low value is also important for 2 materials different in brightness, for example dark dirt vs bright snow
  13. #define MATERIAL3_EPS 0.01f // was 0.055f but smaller value is needed now because of multi-material per-pixel blending, low value is also important for 2 materials different in brightness, for example dark dirt vs bright snow
  14. #define MATERIAL4_EPS 0.01f // was 0.055f but smaller value is needed now because of multi-material per-pixel blending, low value is also important for 2 materials different in brightness, for example dark dirt vs bright snow
  15. #define VTX_HEIGHTMAP 1
  16. #define VTX_COMPRESS 1
  17. #define MTRL_BLEND_HP 0 // 0-faster and smaller memory usage
  18. #define VMC_CONTINUOUS 0 // 0-faster !! enabling makes building slower 22fps vs 20fps so keep at zero !! this was an attempt to remove dynamic memory allocation by holding one continuous buffer for vtx mtrl combos
  19. /******************************************************************************/
  20. ASSERT(MAX_HM_RES<=129); // various places use UShort to limit for 16-bit (including 'MtrlCombo.vtxs,tris', 'VtxMtrlCombo.mc_vtx_index')
  21. /******************************************************************************/
  22. static void AdjustSharpness(Flt &sharpness, Int count)
  23. {
  24. if(count)sharpness/=count;
  25. #if DEBUG
  26. //LogN(S+sharpness);
  27. // test results:
  28. // min 0.0001
  29. // avg 0.0014
  30. // max 0.0064
  31. #endif
  32. Flt avg=0.0014f;
  33. sharpness=Mid(Pow(sharpness/avg, 0.33f), 0.6f, 2.0f);
  34. }
  35. static Flt GetLodDist(Int lod_index, Flt sharpness)
  36. {
  37. return (1<<lod_index) * ((lod_index==3) ? 1.5f : 1.0f) * sharpness;
  38. }
  39. static VecB ColorB(C Image &image, Int x, Int y) // !! assumes that 'x, y' are in range !!
  40. {
  41. switch(image.hwType())
  42. {
  43. case IMAGE_R8G8B8: return image.pixB3(x, y);
  44. case IMAGE_F32_3 :
  45. {
  46. Vec c=image.pixF3(x, y)*255;
  47. return VecB(Mid(RoundPos(c.x), 0, 255),
  48. Mid(RoundPos(c.y), 0, 255),
  49. Mid(RoundPos(c.z), 0, 255));
  50. }
  51. }
  52. return 0;
  53. }
  54. static Vec4 MtrlBlendF(C Image &image, Int x, Int y) // !! assumes that 'x, y' are in range !!
  55. {
  56. switch(image.hwType())
  57. {
  58. case IMAGE_F32_4 : return image.pixF4(x, y);
  59. case IMAGE_R8G8B8A8: {C VecB4 &mb=image.pixB4(x, y); return Vec4(mb.x/255.0f, mb.y/255.0f, mb.z/255.0f, mb.w/255.0f);}
  60. }
  61. return 0;
  62. }
  63. static VecB4 MtrlBlendB(C Image &image, Int x, Int y) // !! assumes that 'x, y' are in range !!
  64. {
  65. switch(image.hwType())
  66. {
  67. case IMAGE_R8G8B8A8: return image.pixB4(x, y);
  68. case IMAGE_F32_4 :
  69. {
  70. Vec4 mb=image.pixF4(x, y); if(Flt sum=mb.sum())mb*=255/sum;
  71. return VecB4(Mid(RoundPos(mb.x), 0, 255),
  72. Mid(RoundPos(mb.y), 0, 255),
  73. Mid(RoundPos(mb.z), 0, 255),
  74. Mid(RoundPos(mb.w), 0, 255));
  75. }
  76. }
  77. return 0;
  78. }
  79. #define SHARPNESS 6 // the higher sharpness, the sharper vtx normals of LOD, however it also causes noise
  80. #define SHARPNESS2 (SHARPNESS*SHARPNESS)
  81. static const Int DownSizeWeights[3][3]=
  82. {
  83. {65536/SHARPNESS2, 65536/SHARPNESS , 65536/SHARPNESS2},
  84. {65536/SHARPNESS , 65536/1 , 65536/SHARPNESS },
  85. {65536/SHARPNESS2, 65536/SHARPNESS , 65536/SHARPNESS2},
  86. };
  87. /******************************************************************************/
  88. // MANAGE
  89. /******************************************************************************/
  90. Heightmap& Heightmap::del()
  91. {
  92. _height .del();
  93. _color .del();
  94. _mtrl_index.del();
  95. _mtrl_blend.del();
  96. _materials .del();
  97. return T;
  98. }
  99. /******************************************************************************/
  100. Heightmap& Heightmap::create(Int res, Flt height, C MaterialPtr &material, Bool align_height_to_neighbors, C Heightmap *h_l, C Heightmap *h_r, C Heightmap *h_b, C Heightmap *h_f, C Heightmap *h_lb, C Heightmap *h_lf, C Heightmap *h_rb, C Heightmap *h_rf)
  101. {
  102. if(res>0)
  103. {
  104. Clamp(res, 2, MAX_HM_RES); res=NearestPow2(res)|1; Int res1=res-1;
  105. _height .createSoft(res, res, 1, IMAGE_F32 );
  106. _mtrl_index.createSoft(res, res, 1, IMAGE_R8G8B8A8);
  107. _mtrl_blend.createSoft(res, res, 1, IMAGE_R8G8B8A8);
  108. _color .del ();
  109. _materials .clear();
  110. VecB4 index(getMaterialIndex0(material), 0, 0, 0),
  111. blend(index.x ? 255 : 0 , 0, 0, 0); // if no material (0=null) is selected then set blending to complete zero to be instantly changed when applying a new material later
  112. // material images
  113. REPD(y, res)REPD(x, res)_mtrl_index.pixB4(x, y)=index;
  114. REPD(y, res)REPD(x, res)_mtrl_blend.pixB4(x, y)=blend;
  115. // align to neighbors
  116. if(!h_l && !h_r && !h_b && !h_f && !h_lb && !h_lf && !h_rb && !h_rf) // no neighbors
  117. {
  118. REPD(y, res)
  119. REPD(x, res)_height.pixF(x, y)=height;
  120. }else // align to neighbors
  121. {
  122. // height
  123. if(!align_height_to_neighbors)
  124. {
  125. REPD(y, res)
  126. REPD(x, res)_height.pixF(x, y)=height;
  127. }else
  128. {
  129. // clear borders
  130. REP(res)
  131. {
  132. _height.pixF(i, 0 )=height;
  133. _height.pixF(i, res1)=height;
  134. _height.pixF(0 , i)=height;
  135. _height.pixF(res1, i)=height;
  136. }
  137. // set borders from neighbors
  138. if( h_l )REP(res)_height.pixF( 0, i)=h_l ->_height.pixF(res1, i);
  139. if( h_r )REP(res)_height.pixF(res1, i)=h_r ->_height.pixF( 0, i);
  140. if( h_b )REP(res)_height.pixF( i, 0)=h_b ->_height.pixF( i, res1);
  141. if( h_f )REP(res)_height.pixF( i, res1)=h_f ->_height.pixF( i, 0);
  142. if( h_lb) _height.pixF( 0, 0)=h_lb->_height.pixF(res1, res1);
  143. if( h_lf) _height.pixF( 0, res1)=h_lf->_height.pixF(res1, 0);
  144. if( h_rb) _height.pixF(res1, 0)=h_rb->_height.pixF( 0, res1);
  145. if( h_rf) _height.pixF(res1, res1)=h_rf->_height.pixF( 0, 0);
  146. if(!h_l ){Flt b= _height.pixF( 0, 0), f=_height.pixF( 0, res1); REP(res)_height.pixF( 0, i)=Lerp(b, f, i/Flt(res1));}
  147. if(!h_r ){Flt b= _height.pixF(res1, 0), f=_height.pixF(res1, res1); REP(res)_height.pixF(res1, i)=Lerp(b, f, i/Flt(res1));}
  148. if(!h_b ){Flt l= _height.pixF( 0, 0), r=_height.pixF(res1, 0); REP(res)_height.pixF(i, 0)=Lerp(l, r, i/Flt(res1));}
  149. if(!h_f ){Flt l= _height.pixF( 0, res1), r=_height.pixF(res1, res1); REP(res)_height.pixF(i, res1)=Lerp(l, r, i/Flt(res1));}
  150. // blend center
  151. for(Int y=1; y<res1; y++)
  152. for(Int x=1; x<res1; x++)
  153. {
  154. Int sx, sy;
  155. Flt h=0, power=0, p;
  156. sx= 0; sy= y; p=1.0f/Dist2(x-sx, y-sy); h+=p*_height.pixF(sx, sy); power+=p;
  157. sx=res1; sy= y; p=1.0f/Dist2(x-sx, y-sy); h+=p*_height.pixF(sx, sy); power+=p;
  158. sx= x; sy= 0; p=1.0f/Dist2(x-sx, y-sy); h+=p*_height.pixF(sx, sy); power+=p;
  159. sx= x; sy=res1; p=1.0f/Dist2(x-sx, y-sy); h+=p*_height.pixF(sx, sy); power+=p;
  160. _height.pixF(x, y)=h/power; // 'power' will never be zero in this case
  161. }
  162. }
  163. // material
  164. {
  165. if(h_l)REP(res)
  166. {
  167. VecB4 &index=_mtrl_index.pixB4(0, i); index=h_l->_mtrl_index.pixB4(res1, i);
  168. index.x=getMaterialIndex0(h_l->material(index.x));
  169. index.y=getMaterialIndex0(h_l->material(index.y));
  170. index.z=getMaterialIndex0(h_l->material(index.z));
  171. index.w=getMaterialIndex0(h_l->material(index.w));
  172. _mtrl_blend.pixB4(0, i)=MtrlBlendB(h_l->_mtrl_blend, res1, i);
  173. }
  174. if(h_r)REP(res)
  175. {
  176. VecB4 &index=_mtrl_index.pixB4(res1, i); index=h_r->_mtrl_index.pixB4(0, i);
  177. index.x=getMaterialIndex0(h_r->material(index.x));
  178. index.y=getMaterialIndex0(h_r->material(index.y));
  179. index.z=getMaterialIndex0(h_r->material(index.z));
  180. index.w=getMaterialIndex0(h_r->material(index.w));
  181. _mtrl_blend.pixB4(res1, i)=MtrlBlendB(h_r->_mtrl_blend, 0, i);
  182. }
  183. if(h_b)REP(res)
  184. {
  185. VecB4 &index=_mtrl_index.pixB4(i, 0); index=h_b->_mtrl_index.pixB4(i, res1);
  186. index.x=getMaterialIndex0(h_b->material(index.x));
  187. index.y=getMaterialIndex0(h_b->material(index.y));
  188. index.z=getMaterialIndex0(h_b->material(index.z));
  189. index.w=getMaterialIndex0(h_b->material(index.w));
  190. _mtrl_blend.pixB4(i, 0)=MtrlBlendB(h_b->_mtrl_blend, i, res1);
  191. }
  192. if(h_f)REP(res)
  193. {
  194. VecB4 &index=_mtrl_index.pixB4(i, res1); index=h_f->_mtrl_index.pixB4(i, 0);
  195. index.x=getMaterialIndex0(h_f->material(index.x));
  196. index.y=getMaterialIndex0(h_f->material(index.y));
  197. index.z=getMaterialIndex0(h_f->material(index.z));
  198. index.w=getMaterialIndex0(h_f->material(index.w));
  199. _mtrl_blend.pixB4(i, res1)=MtrlBlendB(h_f->_mtrl_blend, i, 0);
  200. }
  201. if(h_lb)
  202. {
  203. VecB4 &index=_mtrl_index.pixB4(0, 0); index=h_lb->_mtrl_index.pixB4(res1, res1);
  204. index.x=getMaterialIndex0(h_lb->material(index.x));
  205. index.y=getMaterialIndex0(h_lb->material(index.y));
  206. index.z=getMaterialIndex0(h_lb->material(index.z));
  207. index.w=getMaterialIndex0(h_lb->material(index.w));
  208. _mtrl_blend.pixB4(0, 0)=MtrlBlendB(h_lb->_mtrl_blend, res1, res1);
  209. }
  210. if(h_rb)
  211. {
  212. VecB4 &index=_mtrl_index.pixB4(res1, 0); index=h_rb->_mtrl_index.pixB4(0, res1);
  213. index.x=getMaterialIndex0(h_rb->material(index.x));
  214. index.y=getMaterialIndex0(h_rb->material(index.y));
  215. index.z=getMaterialIndex0(h_rb->material(index.z));
  216. index.w=getMaterialIndex0(h_rb->material(index.w));
  217. _mtrl_blend.pixB4(res1, 0)=MtrlBlendB(h_rb->_mtrl_blend, 0, res1);
  218. }
  219. if(h_lf)
  220. {
  221. VecB4 &index=_mtrl_index.pixB4(0, res1); index=h_lf->_mtrl_index.pixB4(res1, 0);
  222. index.x=getMaterialIndex0(h_lf->material(index.x));
  223. index.y=getMaterialIndex0(h_lf->material(index.y));
  224. index.z=getMaterialIndex0(h_lf->material(index.z));
  225. index.w=getMaterialIndex0(h_lf->material(index.w));
  226. _mtrl_blend.pixB4(0, res1)=MtrlBlendB(h_lf->_mtrl_blend, res1, 0);
  227. }
  228. if(h_rf)
  229. {
  230. VecB4 &index=_mtrl_index.pixB4(res1, res1); index=h_rf->_mtrl_index.pixB4(0, 0);
  231. index.x=getMaterialIndex0(h_rf->material(index.x));
  232. index.y=getMaterialIndex0(h_rf->material(index.y));
  233. index.z=getMaterialIndex0(h_rf->material(index.z));
  234. index.w=getMaterialIndex0(h_rf->material(index.w));
  235. _mtrl_blend.pixB4(res1, res1)=MtrlBlendB(h_rf->_mtrl_blend, 0, 0);
  236. }
  237. }
  238. }
  239. }else del();
  240. return T;
  241. }
  242. /******************************************************************************/
  243. Heightmap& Heightmap::create(C Heightmap &src)
  244. {
  245. if(this!=&src)
  246. {
  247. src._height .copyTry(_height );
  248. src._color .copyTry(_color , -1, -1, -1, IMAGE_R8G8B8);
  249. src._mtrl_index.copyTry(_mtrl_index);
  250. src._mtrl_blend.copyTry(_mtrl_blend, -1, -1, -1, IMAGE_R8G8B8A8);
  251. _materials=src._materials;
  252. }
  253. return T;
  254. }
  255. /******************************************************************************/
  256. void Heightmap::createFromQuarter(C Heightmap &src, Bool right, Bool forward, C Game::WorldSettings &settings)
  257. {
  258. src._height .crop(_height , right ? (src._height .w()-1)/2 : 0, forward ? (src._height .h()-1)/2 : 0, (src._height .w()-1)/2+1, (src._height .h()-1)/2+1);
  259. src._color .crop(_color , right ? (src._color .w()-1)/2 : 0, forward ? (src._color .h()-1)/2 : 0, (src._color .w()-1)/2+1, (src._color .h()-1)/2+1);
  260. src._mtrl_index.crop(_mtrl_index, right ? (src._mtrl_index.w()-1)/2 : 0, forward ? (src._mtrl_index.h()-1)/2 : 0, (src._mtrl_index.w()-1)/2+1, (src._mtrl_index.h()-1)/2+1);
  261. src._mtrl_blend.crop(_mtrl_blend, right ? (src._mtrl_blend.w()-1)/2 : 0, forward ? (src._mtrl_blend.h()-1)/2 : 0, (src._mtrl_blend.w()-1)/2+1, (src._mtrl_blend.h()-1)/2+1);
  262. _materials=src._materials;
  263. REPD(y, _height.h())
  264. REPD(x, _height.w())_height.pixF(x, y)*=2; // heightmap scaling is proportional to area size, so we need to adjust it
  265. resize(settings.hmRes());
  266. }
  267. struct HeightmapQuad
  268. {
  269. C Heightmap *h;
  270. Int x, y;
  271. };
  272. void Heightmap::createFromQuad(C Heightmap *lb, C Heightmap *rb, C Heightmap *lf, C Heightmap *rf, C Game::WorldSettings &settings)
  273. {
  274. _materials.clear();
  275. // clear all images at start, and overwrite only if we have source data, this is because the heightmaps overlap each other in 1 pixel, and when clearing quarter without heightmap we could potentially erase 1 pixel line data from other
  276. _height .createSoft(settings.hmRes(), settings.hmRes(), 1, IMAGE_F32 ); _height .clear();
  277. _mtrl_index.createSoft(settings.hmRes(), settings.hmRes(), 1, IMAGE_R8G8B8A8); _mtrl_index.clear();
  278. _mtrl_blend.createSoft(settings.hmRes(), settings.hmRes(), 1, IMAGE_R8G8B8A8); _mtrl_blend.clear();
  279. if((lb && lb->_color.is()) || (rb && rb->_color.is()) || (lf && lf->_color.is()) || (rf && rf->_color.is()))
  280. {
  281. _color.createSoft(settings.hmRes(), settings.hmRes(), 1, IMAGE_R8G8B8);
  282. REPD(y, _color.h())
  283. REPD(x, _color.w())_color.pixB3(x, y)=255;
  284. }else _color.del();
  285. Heightmap temp; // keep outside of the loop
  286. const Int temp_size=(settings.hmRes()-1)/2+1;
  287. HeightmapQuad hqs[]=
  288. {
  289. {lb, 0, 0},
  290. {rb, 1, 0},
  291. {lf, 0, 1},
  292. {rf, 1, 1},
  293. };
  294. REPA(hqs)
  295. {
  296. C HeightmapQuad &hq=hqs[i]; if(hq.h)
  297. {
  298. Int ofs_x=(hq.x ? temp_size-1 : 0),
  299. ofs_y=(hq.y ? temp_size-1 : 0);
  300. temp.create(*hq.h); temp.resize(temp_size);
  301. REPD(y, temp_size)
  302. REPD(x, temp_size)
  303. {
  304. Int dest_x=ofs_x+x,
  305. dest_y=ofs_y+y;
  306. VecB4 mi= temp._mtrl_index.pixB4 (x, y);
  307. _height .pixF (dest_x, dest_y)= temp._height .pixF (x, y)/2; // heightmap scaling is proportional to area size, so we need to adjust it
  308. if(temp._color.is())_color .pixB3(dest_x, dest_y)= ColorB(temp._color , x, y);
  309. _mtrl_blend.pixB4(dest_x, dest_y)=MtrlBlendB(temp._mtrl_blend, x, y);
  310. _mtrl_index.pixB4(dest_x, dest_y).set(getMaterialIndex0(temp.material(mi.x)),
  311. getMaterialIndex0(temp.material(mi.y)),
  312. getMaterialIndex0(temp.material(mi.z)),
  313. getMaterialIndex0(temp.material(mi.w)));
  314. }
  315. }//else clear - don't do this, because we could erase 1 pixel line of other heightmap which was already set, instead, all heightmap data is cleared at start
  316. }
  317. }
  318. /******************************************************************************/
  319. // GET
  320. /******************************************************************************/
  321. Bool Heightmap::is ()C {return _height.is();}
  322. Int Heightmap::resolution()C {return _height.w ();}
  323. Bool Heightmap::hasColor ()C {return _color .is();}
  324. UInt Heightmap::memUsage()C
  325. {
  326. return _height .memUsage()
  327. +_color .memUsage()
  328. +_mtrl_index.memUsage()
  329. +_mtrl_blend.memUsage()
  330. +_materials .memUsage();
  331. }
  332. Flt Heightmap::height (Int x, Int y )C {return (InRange(x, _height.w()) && InRange(y, _height.h())) ? _height.pixF (x, y) : 0 ;}
  333. Flt Heightmap::heightLinear(Flt x, Flt y )C {return _height.pixelFLinear(x, y) ;}
  334. void Heightmap::height (Int x, Int y, Flt height) {if (InRange(x, _height.w()) && InRange(y, _height.h())) _height.pixF (x, y)=height;}
  335. /******************************************************************************/
  336. Color Heightmap::color (Int x, Int y)C {return (InRange(x, _color.w()) && InRange(y, _color.h())) ? _color.color (x, y) : WHITE ;}
  337. Vec Heightmap::colorF(Int x, Int y)C {return (InRange(x, _color.w()) && InRange(y, _color.h())) ? _color.colorF(x, y).xyz : VecOne;}
  338. void Heightmap::color(Int x, Int y, C Color &color)
  339. {
  340. if(InRange(x, _height.w()) && InRange(y, _height.h())) // check '_height' size because '_color' may not exist yet, as it is optional
  341. {
  342. if(!_color.is() && (color.r!=255 || color.g!=255 || color.b!=255)) // create if doesn't exist yet but is needed
  343. {
  344. _color.createSoftTry(_height.w(), _height.h(), 1, IMAGE_R8G8B8);
  345. REPD(y, _color.h())
  346. REPD(x, _color.w())_color.pixB3(x, y)=255;
  347. }
  348. _color.color(x, y, color);
  349. }
  350. }
  351. void Heightmap::colorF(Int x, Int y, C Vec &color)
  352. {
  353. if(InRange(x, _height.w()) && InRange(y, _height.h())) // check '_height' size because '_color' may not exist yet, as it is optional
  354. {
  355. if(!_color.is() && (color.x!=1 || color.y!=1 || color.z!=1)) // create if doesn't exist yet but is needed
  356. {
  357. _color.createSoftTry(_height.w(), _height.h(), 1, IMAGE_F32_3);
  358. REPD(y, _color.h())
  359. REPD(x, _color.w())_color.pixF3(x, y)=1;
  360. }
  361. if(_color.is())
  362. {
  363. if(_color.type()!=IMAGE_F32_3)_color.copy(_color, -1, -1, -1, IMAGE_F32_3); // convert to high precision
  364. _color.pixF3(x, y)=color;
  365. }
  366. }
  367. }
  368. /******************************************************************************/
  369. void Heightmap::mtrlBlendHP()
  370. {
  371. if(_mtrl_blend.hwType()==IMAGE_R8G8B8A8)
  372. {
  373. Image temp; temp.create(_mtrl_blend.w(), _mtrl_blend.h(), 1, IMAGE_F32_4, IMAGE_SOFT, 1, false); // use 'create' to have 'Exit' on fail, because the methods that use 'mtrlBlendHP', assume that it succeeded
  374. REPD(y, temp.h())
  375. REPD(x, temp.w())
  376. {
  377. VecB4 mb=_mtrl_blend.pixB4(x, y);
  378. temp.pixF4(x, y).set(mb.x/255.0f, mb.y/255.0f, mb.z/255.0f, mb.w/255.0f);
  379. }
  380. Swap(_mtrl_blend, temp);
  381. }
  382. }
  383. /******************************************************************************/
  384. Bool Heightmap::getMaterial(Int x, Int y, VecB4 &mtrl_index, VecB4 &mtrl_blend)C
  385. {
  386. if(InRange(x, _mtrl_index.w())
  387. && InRange(y, _mtrl_index.h()))
  388. {
  389. mtrl_index= _mtrl_index.pixB4(x, y);
  390. mtrl_blend=MtrlBlendB(_mtrl_blend, x, y);
  391. return true;
  392. }
  393. mtrl_index.zero(); mtrl_blend.zero(); return false;
  394. }
  395. Bool Heightmap::getMaterial(Int x, Int y, VecB4 &mtrl_index, Vec4 &mtrl_blend)C
  396. {
  397. if(InRange(x, _mtrl_index.w())
  398. && InRange(y, _mtrl_index.h()))
  399. {
  400. mtrl_index= _mtrl_index.pixB4(x, y);
  401. mtrl_blend=MtrlBlendF(_mtrl_blend, x, y);
  402. return true;
  403. }
  404. mtrl_index.zero(); mtrl_blend.zero(); return false;
  405. }
  406. /******************************************************************************/
  407. Bool Heightmap::getMaterial(Int x, Int y, MaterialPtr &m0, MaterialPtr &m1, MaterialPtr &m2, MaterialPtr &m3, VecB4 &mtrl_blend)C
  408. {
  409. if(InRange(x, _mtrl_index.w())
  410. && InRange(y, _mtrl_index.h()))
  411. {
  412. mtrl_blend=MtrlBlendB(_mtrl_blend, x, y);
  413. C VecB4 &mtrl_index= _mtrl_index.pixB4(x, y);
  414. if(InRange(mtrl_index.x, _materials))m0=_materials[mtrl_index.x];else m0=null;
  415. if(InRange(mtrl_index.y, _materials))m1=_materials[mtrl_index.y];else m1=null;
  416. if(InRange(mtrl_index.z, _materials))m2=_materials[mtrl_index.z];else m2=null;
  417. if(InRange(mtrl_index.w, _materials))m3=_materials[mtrl_index.w];else m3=null;
  418. return true;
  419. }
  420. m0=null; m1=null; m2=null; m3=null; mtrl_blend.zero(); return false;
  421. }
  422. Bool Heightmap::getMaterial(Int x, Int y, MaterialPtr &m0, MaterialPtr &m1, MaterialPtr &m2, MaterialPtr &m3, Vec4 &mtrl_blend)C
  423. {
  424. if(InRange(x, _mtrl_index.w())
  425. && InRange(y, _mtrl_index.h()))
  426. {
  427. mtrl_blend=MtrlBlendF(_mtrl_blend, x, y);
  428. C VecB4 &mtrl_index= _mtrl_index.pixB4(x, y);
  429. if(InRange(mtrl_index.x, _materials))m0=_materials[mtrl_index.x];else m0=null;
  430. if(InRange(mtrl_index.y, _materials))m1=_materials[mtrl_index.y];else m1=null;
  431. if(InRange(mtrl_index.z, _materials))m2=_materials[mtrl_index.z];else m2=null;
  432. if(InRange(mtrl_index.w, _materials))m3=_materials[mtrl_index.w];else m3=null;
  433. return true;
  434. }
  435. m0=null; m1=null; m2=null; m3=null; mtrl_blend.zero(); return false;
  436. }
  437. /******************************************************************************/
  438. void Heightmap::setMaterial(Int x, Int y, C VecB4 &mtrl_index, C VecB4 &mtrl_blend)
  439. {
  440. if(InRange(x, _mtrl_index.w())
  441. && InRange(y, _mtrl_index.h()))
  442. {
  443. VecB4 &mi=_mtrl_index.pixB4(x, y);
  444. switch(_mtrl_blend.hwType())
  445. {
  446. case IMAGE_R8G8B8A8:
  447. {
  448. VecB4 &mb=_mtrl_blend.pixB4(x, y);
  449. if(InRange(mtrl_index.x, _materials)){mi.x=mtrl_index.x; mb.x=mtrl_blend.x;}else{mi.x=0; mb.x=0;}
  450. if(InRange(mtrl_index.y, _materials)){mi.y=mtrl_index.y; mb.y=mtrl_blend.y;}else{mi.y=0; mb.y=0;}
  451. if(InRange(mtrl_index.z, _materials)){mi.z=mtrl_index.z; mb.z=mtrl_blend.z;}else{mi.z=0; mb.z=0;}
  452. if(InRange(mtrl_index.w, _materials)){mi.w=mtrl_index.w; mb.w=mtrl_blend.w;}else{mi.w=0; mb.w=0;}
  453. // normalizing blends would be slow, so just keep as it is, because they will be normalized when building
  454. }break;
  455. case IMAGE_F32_4:
  456. {
  457. Vec4 &mb=_mtrl_blend.pixF4(x, y);
  458. if(InRange(mtrl_index.x, _materials)){mi.x=mtrl_index.x; mb.x=mtrl_blend.x;}else{mi.x=0; mb.x=0;}
  459. if(InRange(mtrl_index.y, _materials)){mi.y=mtrl_index.y; mb.y=mtrl_blend.y;}else{mi.y=0; mb.y=0;}
  460. if(InRange(mtrl_index.z, _materials)){mi.z=mtrl_index.z; mb.z=mtrl_blend.z;}else{mi.z=0; mb.z=0;}
  461. if(InRange(mtrl_index.w, _materials)){mi.w=mtrl_index.w; mb.w=mtrl_blend.w;}else{mi.w=0; mb.w=0;}
  462. if(Flt sum=mb.sum())mb/=sum; // normalize blends
  463. }break;
  464. }
  465. }
  466. }
  467. void Heightmap::setMaterial(Int x, Int y, C VecB4 &mtrl_index, C Vec4 &mtrl_blend)
  468. {
  469. if(InRange(x, _mtrl_index.w())
  470. && InRange(y, _mtrl_index.h()))
  471. {
  472. mtrlBlendHP();
  473. VecB4 &mi=_mtrl_index.pixB4(x, y);
  474. Vec4 &mb=_mtrl_blend.pixF4(x, y);
  475. if(InRange(mtrl_index.x, _materials)){mi.x=mtrl_index.x; mb.x=mtrl_blend.x;}else{mi.x=0; mb.x=0;}
  476. if(InRange(mtrl_index.y, _materials)){mi.y=mtrl_index.y; mb.y=mtrl_blend.y;}else{mi.y=0; mb.y=0;}
  477. if(InRange(mtrl_index.z, _materials)){mi.z=mtrl_index.z; mb.z=mtrl_blend.z;}else{mi.z=0; mb.z=0;}
  478. if(InRange(mtrl_index.w, _materials)){mi.w=mtrl_index.w; mb.w=mtrl_blend.w;}else{mi.w=0; mb.w=0;}
  479. if(Flt sum=mb.sum())mb/=sum; // normalize blends
  480. }
  481. }
  482. /******************************************************************************/
  483. void Heightmap::setMaterial(Int x, Int y, C MaterialPtr &m0, C MaterialPtr &m1, C MaterialPtr &m2, C MaterialPtr &m3, C VecB4 &mtrl_blend)
  484. {
  485. if(InRange(x, _mtrl_index.w())
  486. && InRange(y, _mtrl_index.h()))
  487. {
  488. _mtrl_index.pixB4(x, y).set(getMaterialIndex0(m0), getMaterialIndex0(m1), getMaterialIndex0(m2), getMaterialIndex0(m3));
  489. switch(_mtrl_blend.hwType())
  490. {
  491. case IMAGE_R8G8B8A8:
  492. {
  493. VecB4 &mb=_mtrl_blend.pixB4(x, y); mb=mtrl_blend;
  494. // normalizing blends would be slow, so just keep as it is, because they will be normalized when building
  495. }break;
  496. case IMAGE_F32_4:
  497. {
  498. Vec4 &mb=_mtrl_blend.pixF4(x, y); mb=mtrl_blend;
  499. if(Flt sum=mb.sum())mb/=sum; // normalize blends
  500. }break;
  501. }
  502. }
  503. }
  504. void Heightmap::setMaterial(Int x, Int y, C MaterialPtr &m0, C MaterialPtr &m1, C MaterialPtr &m2, C MaterialPtr &m3, C Vec4 &mtrl_blend)
  505. {
  506. if(InRange(x, _mtrl_index.w())
  507. && InRange(y, _mtrl_index.h()))
  508. {
  509. mtrlBlendHP();
  510. _mtrl_index.pixB4(x, y).set(getMaterialIndex0(m0), getMaterialIndex0(m1), getMaterialIndex0(m2), getMaterialIndex0(m3));
  511. Vec4 &mb=_mtrl_blend.pixF4(x, y); mb=mtrl_blend;
  512. if(Flt sum=mb.sum())mb/=sum; // normalize blends
  513. }
  514. }
  515. /******************************************************************************/
  516. void Heightmap::addMaterial(Int x, Int y, C MaterialPtr &m, Flt mtrl_blend)
  517. {
  518. if(InRange(x, _mtrl_index.w())
  519. && InRange(y, _mtrl_index.h()))
  520. {
  521. mtrlBlendHP();
  522. VecB4 &mi=_mtrl_index.pixB4(x, y);
  523. Vec4 &mb=_mtrl_blend.pixF4(x, y);
  524. if(!m) // adding null material means setting a hole, this is how Editor works
  525. {
  526. mi.zero();
  527. mb.zero();
  528. }else
  529. {
  530. Int index=getMaterialIndex(m);
  531. if( index>0) // skip -1 and 0 null materials
  532. {
  533. Int c;
  534. if(mi.x==index)c=0;else
  535. if(mi.y==index)c=1;else
  536. if(mi.z==index)c=2;else
  537. if(mi.w==index)c=3;else
  538. c=-1;
  539. if(c>=0) // material already exists in pixel
  540. {
  541. mb.c[c]+=mtrl_blend; // increase its power
  542. }else // new material
  543. {
  544. // replace the least powerful
  545. c=mb.minI();
  546. mi.c[c]=index;
  547. mb.c[c]=mtrl_blend;
  548. }
  549. // normalize blends
  550. if(Flt sum=mb.sum())mb/=sum;
  551. // remove insignificant materials, this is to prevent too many materials residing in memory while they're unused anymore
  552. if(mb.x<1.0f/255){mi.x=0; mb.x=0;}
  553. if(mb.y<1.0f/255){mi.y=0; mb.y=0;}
  554. if(mb.z<1.0f/255){mi.z=0; mb.z=0;}
  555. if(mb.w<1.0f/255){mi.w=0; mb.w=0;}
  556. }
  557. }
  558. }
  559. }
  560. /******************************************************************************/
  561. // SET
  562. /******************************************************************************
  563. /*void Heightmap::setMinMaxY(Mesh &mesh) // this function should be multi-threaded safe
  564. {
  565. Int step=1..4; // can be >1 for approximation
  566. for(Int y=0; y<_height.h(); y+=step)
  567. for(Int x=0; x<_height.w(); x+=step)
  568. {
  569. Flt h=_height.pixF(x, y);
  570. if(!x && !y)mesh.box.min.y=mesh.box.max.y=h; // first pixel
  571. else mesh.box.includeY(h); // next pixels
  572. }
  573. mesh.lod_center.y=mesh.box.centerY();
  574. }
  575. void Heightmap::setNormal(Mesh &mesh, Int quality, C Heightmap *h_l, C Heightmap *h_r, C Heightmap *h_b, C Heightmap *h_f) // this function should be multi-threaded safe
  576. {
  577. Int res=resolution();
  578. if( res>=2)
  579. {
  580. Int step=(1<<quality);
  581. Flt sharpness=0; Int sharpness_count=0;
  582. Vec vtx_nrm_map[MAX_HM_RES][MAX_HM_RES]; // [y][x]
  583. C Image *hl=(h_l ? &h_l->_height : null),
  584. *hr=(h_r ? &h_r->_height : null),
  585. *hb=(h_b ? &h_b->_height : null),
  586. *hf=(h_f ? &h_f->_height : null);
  587. Int res1 =res-1;
  588. Flt nrm_y=2.0f/res1;
  589. for(Int y=0; y<res; y+=step)
  590. for(Int x=0; x<res; x+=step)
  591. {
  592. Flt h=_height.pixF(x, y);
  593. // normal
  594. Vec &nrm=vtx_nrm_map[y][x];
  595. Flt l, r;
  596. if(x!= 0)l=_height.pixF(x-1, y);else if(hl)l=hl->pixF(res-2, y);else{nrm.x=(_height.pixF( 0, y)-_height.pixF( 1, y))*2; goto processed_x;}
  597. if(x!=res1)r=_height.pixF(x+1, y);else if(hr)r=hr->pixF( 1, y);else{nrm.x=(_height.pixF(res-2, y)-_height.pixF(res1, y))*2; goto processed_x;}
  598. nrm.x=l-r;
  599. sharpness+=Abs(h-Avg(l, r)); sharpness_count++;
  600. processed_x:
  601. Flt b, f;
  602. if(y!= 0)b=_height.pixF(x, y-1);else if(hb)b=hb->pixF(x, res-2);else{nrm.z=(_height.pixF(x, 0)-_height.pixF(x, 1))*2; goto processed_z;}
  603. if(y!=res1)f=_height.pixF(x, y+1);else if(hf)f=hf->pixF(x, 1);else{nrm.z=(_height.pixF(x, res-2)-_height.pixF(x, res1))*2; goto processed_z;}
  604. nrm.z=b-f;
  605. sharpness+=Abs(h-Avg(b, f)); sharpness_count++;
  606. processed_z:
  607. nrm.y=nrm_y; nrm.normalize();
  608. }
  609. AdjustSharpness(sharpness, sharpness_count);
  610. FREPD(l, mesh.lods())
  611. {
  612. MeshLod &lod=mesh.lod(l);
  613. lod.dist(GetLodDist(l, sharpness));
  614. REPA(lod)
  615. {
  616. MeshRender &mshr =lod.parts[i].render;
  617. Int ofs_pos=mshr.vtxOfs(VTX_POS),
  618. ofs_nrm=mshr.vtxOfs(VTX_NRM);
  619. if(ofs_pos>=0 && ofs_nrm>=0)if(Byte *vtx=mshr.vtxLock())
  620. {
  621. Int dx =_height.w(),
  622. dy =_height.h();
  623. Vec *vtx_pos=(Vec *)(vtx+ofs_pos);
  624. Byte *vtx_nrm=(Byte*)(vtx+ofs_nrm);
  625. REP(mshr.vtxs())
  626. {
  627. Int x=RoundPos(vtx_pos->x*(dx-1)); Clamp(x, 0, res1);
  628. Int y=RoundPos(vtx_pos->z*(dy-1)); Clamp(y, 0, res1);
  629. if(!mshr.storageCompress())*(Vec *)vtx_nrm= vtx_nrm_map[y][x] ;else
  630. if( mshr.storageSigned ())*(VecB4*)vtx_nrm=NrmToSByte4(vtx_nrm_map[y][x]);else
  631. *(VecB4*)vtx_nrm=NrmToUByte4(vtx_nrm_map[y][x]);
  632. vtx_pos =(Vec*)(((Byte*)vtx_pos)+mshr.vtxSize());
  633. vtx_nrm+=mshr.vtxSize();
  634. }
  635. mshr.vtxUnlock();
  636. }
  637. }
  638. SoftenNormalBorders(vtx_nrm_map, res, step<<l);
  639. }
  640. }
  641. }
  642. /******************************************************************************/
  643. void Heightmap::clearMaterials() {_materials.clear();}
  644. void Heightmap::cleanMaterials() // this function should be multi-threaded safe
  645. {
  646. if(_materials.elms()>1)
  647. {
  648. Bool is[256]; ZeroN(is, _materials.elms()); // assume all are unused
  649. Byte remap[256];
  650. switch(_mtrl_blend.hwType())
  651. {
  652. case IMAGE_R8G8B8A8:
  653. {
  654. REPD(y, _mtrl_index.h())
  655. REPD(x, _mtrl_index.w())
  656. {
  657. C VecB4 &mi=_mtrl_index.pixB4(x, y),
  658. &mb=_mtrl_blend.pixB4(x, y);
  659. if(mb.x>0)is[mi.x]=true;
  660. if(mb.y>0)is[mi.y]=true;
  661. if(mb.z>0)is[mi.z]=true;
  662. if(mb.w>0)is[mi.w]=true;
  663. }
  664. }break;
  665. case IMAGE_F32_4:
  666. {
  667. REPD(y, _mtrl_index.h())
  668. REPD(x, _mtrl_index.w())
  669. {
  670. C VecB4 &mi=_mtrl_index.pixB4(x, y);
  671. C Vec4 &mb=_mtrl_blend.pixF4(x, y);
  672. if(mb.x>0)is[mi.x]=true;
  673. if(mb.y>0)is[mi.y]=true;
  674. if(mb.z>0)is[mi.z]=true;
  675. if(mb.w>0)is[mi.w]=true;
  676. }
  677. }break;
  678. }
  679. if(_materials.clean(is, remap)) // if any was removed
  680. REPD(y, _mtrl_index.h())
  681. REPD(x, _mtrl_index.w())
  682. {
  683. VecB4 &mi=_mtrl_index.pixB4(x, y);
  684. mi.x=remap[mi.x];
  685. mi.y=remap[mi.y];
  686. mi.z=remap[mi.z];
  687. mi.w=remap[mi.w];
  688. // mtrl blends don't need to be set to zero for removed materials, because materials with blend>0 are never removed
  689. }
  690. }
  691. }
  692. void Heightmap::cleanColor()
  693. {
  694. if(_color.is())
  695. {
  696. REPD(y, _color.h())
  697. REPD(x, _color.w()){Color c=_color.color(x, y); if(c.r!=255 || c.g!=255 || c.b!=255)return;} // if color is not white, then we need the color map, so return without deleting it
  698. _color.del(); // we don't need to save color (because it's all white) so we can just delete it
  699. }
  700. }
  701. void Heightmap::clean()
  702. {
  703. cleanColor ();
  704. cleanMaterials();
  705. }
  706. /******************************************************************************/
  707. // BUILD
  708. /******************************************************************************/
  709. static inline VecB4 VtxMtrlBlend(C VecB4 &mtrl_index, C VecB4 &pix_ind, C VecB4 &pix_blend)
  710. {
  711. VecI4 mtrl_blend(0);
  712. //if(mtrl_index.x) we can assume that this will always be != 0 since mtrl_index is sorted for mtrl combos
  713. {
  714. if(mtrl_index.x==pix_ind.x)mtrl_blend.x+=pix_blend.x;else
  715. if(mtrl_index.x==pix_ind.y)mtrl_blend.x+=pix_blend.y;else
  716. if(mtrl_index.x==pix_ind.z)mtrl_blend.x+=pix_blend.z;else
  717. if(mtrl_index.x==pix_ind.w)mtrl_blend.x+=pix_blend.w;
  718. }
  719. if(mtrl_index.y) // checking this will prevent adding material blends to null materials (in case source pixel mtrl_index=null and mtrl_blend>0), this is also a speedup because in most cases vertexes don't have second material, so codes below don't need to be executed
  720. {
  721. if(mtrl_index.y==pix_ind.x)mtrl_blend.y+=pix_blend.x;else
  722. if(mtrl_index.y==pix_ind.y)mtrl_blend.y+=pix_blend.y;else
  723. if(mtrl_index.y==pix_ind.z)mtrl_blend.y+=pix_blend.z;else
  724. if(mtrl_index.y==pix_ind.w)mtrl_blend.y+=pix_blend.w;
  725. // since 'mtrl_index' is sorted, then 'mtrl_index.z' will be set only if 'mtrl_index.y' is, so we can check the following in this scope
  726. if(mtrl_index.z) // checking this will prevent adding material blends to null materials (in case source pixel mtrl_index=null and mtrl_blend>0), this is also a speedup because in most cases vertexes don't have third material, so codes below don't need to be executed
  727. {
  728. if(mtrl_index.z==pix_ind.x)mtrl_blend.z+=pix_blend.x;else
  729. if(mtrl_index.z==pix_ind.y)mtrl_blend.z+=pix_blend.y;else
  730. if(mtrl_index.z==pix_ind.z)mtrl_blend.z+=pix_blend.z;else
  731. if(mtrl_index.z==pix_ind.w)mtrl_blend.z+=pix_blend.w;
  732. // since 'mtrl_index' is sorted, then 'mtrl_index.w' will be set only if 'mtrl_index.z' is, so we can check the following in this scope
  733. if(mtrl_index.w) // checking this will prevent adding material blends to null materials (in case source pixel mtrl_index=null and mtrl_blend>0), this is also a speedup because in most cases vertexes don't have third material, so codes below don't need to be executed
  734. {
  735. if(mtrl_index.w==pix_ind.x)mtrl_blend.w+=pix_blend.x;else
  736. if(mtrl_index.w==pix_ind.y)mtrl_blend.w+=pix_blend.y;else
  737. if(mtrl_index.w==pix_ind.z)mtrl_blend.w+=pix_blend.z;else
  738. if(mtrl_index.w==pix_ind.w)mtrl_blend.w+=pix_blend.w;
  739. }
  740. }
  741. }
  742. Flt mul=0;
  743. if(Int sum=mtrl_blend.sum())mul=255.0f/sum;//else mtrl_blend.x=; since below we're not using 'mtrl_blend.x' then we don't need to set it
  744. VecB4 b;
  745. b.w=Mid(RoundPos(mtrl_blend.w*mul), 0, 255);
  746. b.z=Mid(RoundPos(mtrl_blend.z*mul), 0, 255);
  747. b.y=Mid(RoundPos(mtrl_blend.y*mul), 0, 255);
  748. b.x=Mid(255-b.y-b.z-b.w , 0, 255); // make sure that sum of all channels is 255, do this for the first channel, in case 'mtrl_blend' was 0, normally this would set zero too, based on "Mid(RoundPos(mtrl_blend.x*255), 0, 255)", however we need full blend for the main material so this will set 255 in that case
  749. return b;
  750. }
  751. static inline VecB4 VtxMtrlBlend(C VecB4 &mtrl_index, C VecB4 &pix_ind, C Vec4 &pix_blend)
  752. {
  753. Vec4 mtrl_blend(0);
  754. //if(mtrl_index.x) we can assume that this will always be != 0 since mtrl_index is sorted for mtrl combos
  755. {
  756. if(mtrl_index.x==pix_ind.x)mtrl_blend.x+=pix_blend.x;else
  757. if(mtrl_index.x==pix_ind.y)mtrl_blend.x+=pix_blend.y;else
  758. if(mtrl_index.x==pix_ind.z)mtrl_blend.x+=pix_blend.z;else
  759. if(mtrl_index.x==pix_ind.w)mtrl_blend.x+=pix_blend.w;
  760. }
  761. if(mtrl_index.y) // checking this will prevent adding material blends to null materials (in case source pixel mtrl_index=null and mtrl_blend>0), this is also a speedup because in most cases vertexes don't have second material, so codes below don't need to be executed
  762. {
  763. if(mtrl_index.y==pix_ind.x)mtrl_blend.y+=pix_blend.x;else
  764. if(mtrl_index.y==pix_ind.y)mtrl_blend.y+=pix_blend.y;else
  765. if(mtrl_index.y==pix_ind.z)mtrl_blend.y+=pix_blend.z;else
  766. if(mtrl_index.y==pix_ind.w)mtrl_blend.y+=pix_blend.w;
  767. // since 'mtrl_index' is sorted, then 'mtrl_index.z' will be set only if 'mtrl_index.y' is, so we can check the following in this scope
  768. if(mtrl_index.z) // checking this will prevent adding material blends to null materials (in case source pixel mtrl_index=null and mtrl_blend>0), this is also a speedup because in most cases vertexes don't have third material, so codes below don't need to be executed
  769. {
  770. if(mtrl_index.z==pix_ind.x)mtrl_blend.z+=pix_blend.x;else
  771. if(mtrl_index.z==pix_ind.y)mtrl_blend.z+=pix_blend.y;else
  772. if(mtrl_index.z==pix_ind.z)mtrl_blend.z+=pix_blend.z;else
  773. if(mtrl_index.z==pix_ind.w)mtrl_blend.z+=pix_blend.w;
  774. // since 'mtrl_index' is sorted, then 'mtrl_index.w' will be set only if 'mtrl_index.z' is, so we can check the following in this scope
  775. if(mtrl_index.w) // checking this will prevent adding material blends to null materials (in case source pixel mtrl_index=null and mtrl_blend>0), this is also a speedup because in most cases vertexes don't have third material, so codes below don't need to be executed
  776. {
  777. if(mtrl_index.w==pix_ind.x)mtrl_blend.w+=pix_blend.x;else
  778. if(mtrl_index.w==pix_ind.y)mtrl_blend.w+=pix_blend.y;else
  779. if(mtrl_index.w==pix_ind.z)mtrl_blend.w+=pix_blend.z;else
  780. if(mtrl_index.w==pix_ind.w)mtrl_blend.w+=pix_blend.w;
  781. }
  782. }
  783. }
  784. if(Flt sum=mtrl_blend.sum())
  785. {
  786. sum=255/sum;
  787. mtrl_blend.w*=sum;
  788. mtrl_blend.z*=sum;
  789. mtrl_blend.y*=sum;
  790. //mtrl_blend.x*=sum; since below we're not using 'mtrl_blend.x' then we don't need to set it
  791. }//else mtrl_blend.x=255; since below we're not using 'mtrl_blend.x' then we don't need to set it
  792. VecB4 b;
  793. b.w=Mid(RoundPos(mtrl_blend.w), 0, 255);
  794. b.z=Mid(RoundPos(mtrl_blend.z), 0, 255);
  795. b.y=Mid(RoundPos(mtrl_blend.y), 0, 255);
  796. b.x=Mid(255-b.y-b.z-b.w , 0, 255); // make sure that sum of all channels is 255, do this for the first channel, in case 'mtrl_blend' was 0, normally this would set zero too, based on "Mid(RoundPos(mtrl_blend.x*255), 0, 255)", however we need full blend for the main material so this will set 255 in that case
  797. return b;
  798. }
  799. /******************************************************************************/
  800. struct MtrlCombo // Material Combination
  801. {
  802. VecB4 mtrl_index ; // use 4-byte here even if MAX_MTRL==3, because comparing VecB4==VecB4 is faster than VecB==VecB because for VecB4 we can just compare the "UInt u" member
  803. SByte ofs_pos ,
  804. ofs_nrm ,
  805. ofs_color ,
  806. ofs_material;
  807. UShort vtxs ,
  808. tris ;
  809. void reset() {vtxs=tris=0;}
  810. };
  811. #if VMC_CONTINUOUS
  812. struct VtxMtrlCombo
  813. {
  814. UShort next_add, // index of next VMC for the same vertex, 0 if none (normally this would be an absolute index, however since there can be ~98K elements, then we would need UInt, but since the vertexes will usually be close together, then we use UShort and use it as relative index), the highest number of VMC is for the first lod, which only uses quads, other LODs can use triangles/edge skirts which could set vertexes first which are further apart, however since they are LOD's then the total number of triangles is smaller, and the next index should be in the range of 64K always
  815. mtrl_combo, // index of material combo
  816. mc_vtx_index; // vertex index in that material combo
  817. };
  818. struct Heightmap::BuildMem
  819. {
  820. };
  821. #else
  822. #define VMC_NULL 0xFFFF
  823. struct VtxMtrlCombo
  824. {
  825. UShort mtrl_combo, // index of material combo, VMC_NULL if none
  826. mc_vtx_index; // vertex index in that material combo
  827. };
  828. struct VtxMtrlCombos
  829. {
  830. VtxMtrlCombo vmc[16]; // 8 for edge skirting 0, 10 for edge skirting 1, 16 for edge skirting 2
  831. /* This is the max amount of material combinations per vertex affected by each face
  832. Worst case scenario is a vertex for LOD near the corner:
  833. +----+---/+
  834. | /| / |
  835. | / | / |
  836. | / | / |
  837. |/ | / |
  838. <\ | / |
  839. | \ | / |
  840. | \ | / |
  841. | \|/ |
  842. +----+----+ LeftUp 2 tris, RightUp 2 tris 2+2+2+4 = 6+4 = 10
  843. | ///|\ /| LeftDown 4 tris, RightDown 2 tris
  844. </// | \/ |
  845. |/ / | | |
  846. +-<--|--+-+
  847. */
  848. void clear()
  849. {
  850. vmc[0].mtrl_combo=VMC_NULL; // set at start the first as non-existing
  851. }
  852. void setUsedByMtrlCombo(UInt mtrl_combo, Memt<MtrlCombo> &mtrl_combos)
  853. {
  854. FREPA(vmc)
  855. {
  856. VtxMtrlCombo &v=vmc[i];
  857. UInt mc=v.mtrl_combo;
  858. if( mc==mtrl_combo)return; // found
  859. if( mc==VMC_NULL ) // create new one
  860. {
  861. v.mtrl_combo =mtrl_combo;
  862. v.mc_vtx_index=mtrl_combos[mtrl_combo].vtxs++;
  863. if(InRange(i+1, vmc))vmc[i+1].mtrl_combo=VMC_NULL; // set next as non-existing
  864. return;
  865. }
  866. }
  867. Exit("VMC"); // if there's no room for mtrl combos, then it means the max amount was calculated incorrectly
  868. }
  869. Int vtxIndexInMtrlCombo(UInt mtrl_combo)
  870. {
  871. FREPA(vmc)
  872. {
  873. VtxMtrlCombo &v=vmc[i];
  874. UInt mc=v.mtrl_combo;
  875. if( mc==mtrl_combo)return v.mc_vtx_index;
  876. if( mc==VMC_NULL )break; // reached the end
  877. }
  878. Exit("VMC"); return 0; // we shouldn't reach this place
  879. }
  880. };
  881. struct Heightmap::BuildMem // this is always used
  882. {
  883. VtxMtrlCombos vmc[MAX_HM_RES][MAX_HM_RES]; // [y][x]
  884. };
  885. struct Heightmap::BuildMemSoft : Heightmap::BuildMem // !! must inherit from 'BuildMem' !! this is used only for HM_SOFT
  886. {
  887. Vec vtx_nrm[MAX_HM_RES][MAX_HM_RES]; // [y][x]
  888. };
  889. #endif
  890. /******************************************************************************/
  891. #if MTRL_BLEND_HP // Material Blend types
  892. typedef Flt MB1;
  893. typedef Flt MB1L;
  894. typedef Vec4 MBN;
  895. typedef Vec4 MBNL;
  896. #else
  897. typedef Byte MB1;
  898. typedef Int MB1L;
  899. typedef VecB4 MBN;
  900. typedef VecI4 MBNL;
  901. #endif
  902. struct Builder
  903. {
  904. Int cur_mtrl_combo;
  905. VecB4 cur_mtrl_index;
  906. MB1L material_blends[256];
  907. UShort map_mtrl_combo[MAX_HM_RES-1][MAX_HM_RES-1], // [y][x]
  908. edg_mtrl_combo[4 ][MAX_HM_RES-1],
  909. tri_mtrl_combo[4 ][MAX_HM_RES-1];
  910. Byte occlusion [MAX_HM_RES ][MAX_HM_RES ]; // [y][x]
  911. Flt frac [MAX_HM_RES ];
  912. #if VTX_COMPRESS
  913. VecB4 vtx_nrm [MAX_HM_RES ][MAX_HM_RES ]; // [y][x]
  914. #else
  915. Vec vtx_nrm [MAX_HM_RES ][MAX_HM_RES ]; // [y][x]
  916. #endif
  917. //Vec vtx_pos [MAX_HM_RES ][MAX_HM_RES ]; // [y][x]
  918. //Vec vtx_tan [MAX_HM_RES ][MAX_HM_RES ]; // [y][x]
  919. //Vec2 vtx_tex [MAX_HM_RES ][MAX_HM_RES ]; // [y][x]
  920. //VecB4 mtrl_index [MAX_HM_RES ][MAX_HM_RES ]; // [y][x]
  921. MBN mtrl_blend [MAX_HM_RES ][MAX_HM_RES ]; // [y][x]
  922. Memt<MtrlCombo> mtrl_combos;
  923. #if VMC_CONTINUOUS
  924. UInt vmc_elms, // number of vmc currently used
  925. vmc_first[MAX_HM_RES][MAX_HM_RES]; // [y][x] index of the first vmc for a given vertex location, ~0 if none
  926. VtxMtrlCombo vmc[(MAX_HM_RES-1)*(MAX_HM_RES-1)*2*3]; // (MAX_HM_RES-1)*(MAX_HM_RES-1)=max quads, *2=max tris, *3=max unique mtrl combos per tri vertex = 98K when MAX_HM_RES-1=128
  927. #else
  928. VtxMtrlCombos (&vmc)[MAX_HM_RES][MAX_HM_RES];
  929. #endif
  930. Mems<MeshPart> *lod_parts;
  931. Image &height, &mtrl_index;
  932. Builder(Image &height, Image &mtrl_index
  933. #if !VMC_CONTINUOUS
  934. , VtxMtrlCombos (&vmc)[MAX_HM_RES][MAX_HM_RES]
  935. #endif
  936. ) : height(height), mtrl_index(mtrl_index)
  937. #if !VMC_CONTINUOUS
  938. , vmc(vmc)
  939. #endif
  940. {
  941. Zero(material_blends); // initialize 'material_blends' to zeros which is required by 'processMaterials'
  942. lod_parts=null;
  943. cur_mtrl_index.set(255, 255, 255, 255); // !! set all to 255 so it will be always different than tested materials, since the actual values will never be set to (255,255,255,255) since the same material will get set only in the first channel (255,0,0,0), and in comparisons we don't need to check "cur_mtrl_combo<0 || cur_mtrl_index!=mtrl_index" but just "cur_mtrl_index!=mtrl_index"
  944. cur_mtrl_combo=-1;
  945. }
  946. void clear(Int res)
  947. {
  948. #if VMC_CONTINUOUS
  949. vmc_elms=0;
  950. REPD(y, res)
  951. REPD(x, res)vmc_first[y][x]=~0;
  952. #else
  953. REPD(y, res)
  954. REPD(x, res)vmc[y][x].clear();
  955. #endif
  956. REPAO(mtrl_combos).reset();
  957. }
  958. INLINE VecB4& mtrlIndex(Int x, Int y)
  959. {
  960. return mtrl_index.pixB4(x, y);
  961. }
  962. Int getMtrlComboIndex(C VecB4 &mtrl_index) // get index of material combination for given material indexes, 'mtrl_index' must be sorted (having components from biggest value to smallest), this function should be multi-threaded safe
  963. {
  964. // first find existing
  965. REPA(mtrl_combos)if(mtrl_combos[i].mtrl_index==mtrl_index)return i;
  966. // add new one
  967. MtrlCombo &mtrl_combo=mtrl_combos.New();
  968. mtrl_combo.mtrl_index=mtrl_index;
  969. mtrl_combo.reset();
  970. return mtrl_combos.elms()-1;
  971. }
  972. INLINE void processMaterials(VecB4 &mtrl_index, Byte *mi, MB1 *mb, Int mtrls)
  973. {
  974. // set materials blend
  975. REP(mtrls)material_blends[mi[i]]+=mb[i]; // 'material_blends' is assumed to be set to zeros on function start
  976. // get materials with highest blend
  977. MBNL mtrl_blend=0;
  978. REP(mtrls)
  979. {
  980. Byte j=mi[i];
  981. if(MB1L b=material_blends[j])
  982. {
  983. material_blends[j]=0; // restore 'material_blends' to initial zeros, this will also prevent the same material from being processed again in this loop
  984. if(j) // don't add null materials (this could happen in case null material had non-zero blend set)
  985. {
  986. // add to 'mtrl_index' and 'mtrl_blend' while preserving order of most important materials
  987. FREPA(mtrl_blend)if(b>mtrl_blend.c[i]) // check if this material has higher blend than any listed
  988. {
  989. for(Int last=Elms(mtrl_index)-1; last>i; last--) // move others to the right ('last' is the index of last material)
  990. {
  991. mtrl_index.c[last]=mtrl_index.c[last-1];
  992. mtrl_blend.c[last]=mtrl_blend.c[last-1];
  993. }
  994. // set this material to the i-th slot
  995. mtrl_index.c[i]=j;
  996. mtrl_blend.c[i]=b;
  997. break;
  998. }
  999. }
  1000. }
  1001. }
  1002. // remove minor materials
  1003. if(mtrl_index.c[1]){if(mtrl_blend.c[1]<=mtrl_blend.x *MATERIAL2_EPS)mtrl_index.c[1]=mtrl_index.c[2]=mtrl_index.c[3]=0;else // if #1 is much smaller than #0 , then remove #1,#2,#3
  1004. if(mtrl_index.c[2]){if(mtrl_blend.c[2]<=mtrl_blend.xy .sum()*MATERIAL3_EPS) mtrl_index.c[2]=mtrl_index.c[3]=0;else // if #2 is much smaller than #0+#1 , then remove #2,#3
  1005. if(mtrl_index.c[3]){if(mtrl_blend.c[3]<=mtrl_blend.xyz.sum()*MATERIAL4_EPS) mtrl_index.c[3]=0;}}} // if #3 is much smaller than #0+#1+#2, then remove #3
  1006. // sort by indexes (required by 'getMtrlComboIndex')
  1007. if(mtrl_index.c[1])
  1008. {
  1009. if(mtrl_index.c[2])
  1010. {
  1011. if(mtrl_index.c[3])
  1012. {
  1013. if(mtrl_index.c[3]>mtrl_index.c[0])Swap(mtrl_index.c[3], mtrl_index.c[0]);
  1014. if(mtrl_index.c[3]>mtrl_index.c[1])Swap(mtrl_index.c[3], mtrl_index.c[1]);
  1015. if(mtrl_index.c[3]>mtrl_index.c[2])Swap(mtrl_index.c[3], mtrl_index.c[2]);
  1016. }
  1017. if(mtrl_index.c[2]>mtrl_index.c[0])Swap(mtrl_index.c[2], mtrl_index.c[0]);
  1018. if(mtrl_index.c[2]>mtrl_index.c[1])Swap(mtrl_index.c[2], mtrl_index.c[1]);
  1019. }
  1020. if(mtrl_index.c[1]>mtrl_index.c[0])Swap(mtrl_index.c[1], mtrl_index.c[0]);
  1021. }
  1022. }
  1023. #if VMC_CONTINUOUS
  1024. void setUsedByMtrlCombo(Int x, Int y, Int mtrl_combo, Memt<MtrlCombo> &mtrl_combos)
  1025. {
  1026. UInt i=vmc_first[y][x];
  1027. if( i==~0) // not yet added
  1028. {
  1029. RANGE_ASSERT(vmc_elms, vmc);
  1030. VtxMtrlCombo &v=vmc[vmc_elms]; vmc_first[y][x]=vmc_elms++;
  1031. v.next_add =0;
  1032. v.mtrl_combo =mtrl_combo;
  1033. v.mc_vtx_index=mtrl_combos[mtrl_combo].vtxs++;
  1034. }else
  1035. for(;;)
  1036. {
  1037. VtxMtrlCombo &c=vmc[i];
  1038. if(c.mtrl_combo==mtrl_combo)return; // already listed by this mtrl combo
  1039. if(c.next_add)i+=c.next_add;else // proceed to the next one
  1040. { // add new one
  1041. RANGE_ASSERT(vmc_elms, vmc);
  1042. c.next_add=vmc_elms-i;
  1043. VtxMtrlCombo &v=vmc[vmc_elms++];
  1044. v.next_add =0;
  1045. v.mtrl_combo =mtrl_combo;
  1046. v.mc_vtx_index=mtrl_combos[mtrl_combo].vtxs++;
  1047. }
  1048. }
  1049. }
  1050. Int vtxIndexInMtrlCombo(Int x, Int y, Int mtrl_combo)
  1051. {
  1052. for(UInt i=vmc_first[y][x]; ; )
  1053. {
  1054. RANGE_ASSERT(i, vmc_elms);
  1055. VtxMtrlCombo &v=vmc[i];
  1056. if(v.mtrl_combo==mtrl_combo)return v.mc_vtx_index;
  1057. DEBUG_ASSERT(v.next_add!=0, "vtxIndexInMtrlCombo");
  1058. i+=v.next_add;
  1059. }
  1060. }
  1061. #else
  1062. INLINE void setUsedByMtrlCombo(Int x, Int y, Int mtrl_combo, Memt<MtrlCombo> &mtrl_combos) { vmc[y][x]. setUsedByMtrlCombo(mtrl_combo, mtrl_combos);}
  1063. INLINE Int vtxIndexInMtrlCombo(Int x, Int y, Int mtrl_combo ) {return vmc[y][x].vtxIndexInMtrlCombo(mtrl_combo);}
  1064. #endif
  1065. Int getMtrlComboForTri(C VecI2 &p0, C VecI2 &p1, C VecI2 &p2)
  1066. {
  1067. VecB4 mtrl_index=0;
  1068. // get nearby vertexes
  1069. VecB4 mi[3];
  1070. MBN mb[3];
  1071. if((mi[0]=mtrlIndex(p0.x, p0.y)).any() // get material indexes
  1072. && (mi[1]=mtrlIndex(p1.x, p1.y)).any()
  1073. && (mi[2]=mtrlIndex(p2.x, p2.y)).any())
  1074. {
  1075. mb[0]=mtrl_blend[p0.y][p0.x]; // get material blends
  1076. mb[1]=mtrl_blend[p1.y][p1.x];
  1077. mb[2]=mtrl_blend[p2.y][p2.x];
  1078. #if __GNUC__
  1079. processMaterials(mtrl_index, (Byte*)mi, (MB1*)mb, 3*4); // !! GCC may put the variables in the array in non-continuous memory, this cast fixes that !!
  1080. #else
  1081. processMaterials(mtrl_index, mi[0].c, mb[0].c, 3*4);
  1082. #endif
  1083. }
  1084. // find element with current materials
  1085. if(/*cur_mtrl_combo<0 || */cur_mtrl_index!=mtrl_index) // new element is needed
  1086. {
  1087. cur_mtrl_combo=getMtrlComboIndex(mtrl_index);
  1088. cur_mtrl_index=mtrl_index;
  1089. }
  1090. setUsedByMtrlCombo(p0.x, p0.y, cur_mtrl_combo, mtrl_combos);
  1091. setUsedByMtrlCombo(p1.x, p1.y, cur_mtrl_combo, mtrl_combos);
  1092. setUsedByMtrlCombo(p2.x, p2.y, cur_mtrl_combo, mtrl_combos);
  1093. mtrl_combos[cur_mtrl_combo].tris++;
  1094. return cur_mtrl_combo;
  1095. }
  1096. Int getMtrlComboForQuad(Int x, Int y, Int x1, Int y1)
  1097. {
  1098. VecB4 mtrl_index=0;
  1099. // get nearby vertexes
  1100. VecB4 mi[4];
  1101. MBN mb[4];
  1102. if((mi[0]=mtrlIndex(x , y1)).any() // get material indexes
  1103. && (mi[1]=mtrlIndex(x1, y1)).any()
  1104. && (mi[2]=mtrlIndex(x1, y )).any()
  1105. && (mi[3]=mtrlIndex(x , y )).any())
  1106. {
  1107. mb[0]=mtrl_blend[y1][x ]; // get material blends
  1108. mb[1]=mtrl_blend[y1][x1];
  1109. mb[2]=mtrl_blend[y ][x1];
  1110. mb[3]=mtrl_blend[y ][x ];
  1111. #if __GNUC__
  1112. processMaterials(mtrl_index, (Byte*)mi, (MB1*)mb, 4*4); // !! GCC may put the variables in the array in non-continuous memory, this cast fixes that !!
  1113. #else
  1114. processMaterials(mtrl_index, mi[0].c, mb[0].c, 4*4);
  1115. #endif
  1116. }
  1117. // find element with current materials
  1118. if(/*cur_mtrl_combo<0 || */cur_mtrl_index!=mtrl_index) // new element is needed
  1119. {
  1120. cur_mtrl_combo=getMtrlComboIndex(mtrl_index);
  1121. cur_mtrl_index=mtrl_index;
  1122. }
  1123. setUsedByMtrlCombo(x , y , cur_mtrl_combo, mtrl_combos);
  1124. setUsedByMtrlCombo(x1, y , cur_mtrl_combo, mtrl_combos);
  1125. setUsedByMtrlCombo(x , y1, cur_mtrl_combo, mtrl_combos);
  1126. setUsedByMtrlCombo(x1, y1, cur_mtrl_combo, mtrl_combos);
  1127. mtrl_combos[cur_mtrl_combo].tris+=2;
  1128. return cur_mtrl_combo;
  1129. }
  1130. void setTriFace(C VecI2 &p0, C VecI2 &p1, C VecI2 &p2, Int mtrl_combo_index)
  1131. {
  1132. MeshRender &mshr=(*lod_parts)[mtrl_combo_index].render;
  1133. if(Byte *ind=(Byte*)mshr.indLockedData())
  1134. {
  1135. Int i0=vtxIndexInMtrlCombo(p0.x, p0.y, mtrl_combo_index),
  1136. i1=vtxIndexInMtrlCombo(p1.x, p1.y, mtrl_combo_index),
  1137. i2=vtxIndexInMtrlCombo(p2.x, p2.y, mtrl_combo_index);
  1138. ind+=mshr.tris()*mshr.triIndSize();
  1139. if(mshr._ib.bit16()){U16 *d=(U16*)ind; d[0]=i0; d[1]=i1; d[2]=i2;}
  1140. else {U32 *d=(U32*)ind; d[0]=i0; d[1]=i1; d[2]=i2;}
  1141. mshr._tris++;
  1142. }
  1143. }
  1144. void setQuadFace(Int x, Int y, Int x1, Int y1, Int mtrl_combo_index)
  1145. {
  1146. MeshRender &mshr=(*lod_parts)[mtrl_combo_index].render;
  1147. if(Byte *ind=(Byte*)mshr.indLockedData())
  1148. {
  1149. Int lb=vtxIndexInMtrlCombo(x , y , mtrl_combo_index),
  1150. lf=vtxIndexInMtrlCombo(x , y1, mtrl_combo_index),
  1151. rb=vtxIndexInMtrlCombo(x1, y , mtrl_combo_index),
  1152. rf=vtxIndexInMtrlCombo(x1, y1, mtrl_combo_index);
  1153. ind+=mshr.tris()*mshr.triIndSize();
  1154. Flt hlb=height.pixF(x , y ),
  1155. hlf=height.pixF(x , y1),
  1156. hrb=height.pixF(x1, y ),
  1157. hrf=height.pixF(x1, y1);
  1158. if(Abs(hlb-hrf)<Abs(hrb-hlf))
  1159. {
  1160. if(mshr._ib.bit16()){U16 *d=(U16*)ind; d[0]=lb; d[1]=lf; d[2]=rf; d[3]=rf; d[4]=rb; d[5]=lb;}
  1161. else {U32 *d=(U32*)ind; d[0]=lb; d[1]=lf; d[2]=rf; d[3]=rf; d[4]=rb; d[5]=lb;}
  1162. }else
  1163. {
  1164. if(mshr._ib.bit16()){U16 *d=(U16*)ind; d[0]=lb; d[1]=lf; d[2]=rb; d[3]=rb; d[4]=lf; d[5]=rf;}
  1165. else {U32 *d=(U32*)ind; d[0]=lb; d[1]=lf; d[2]=rb; d[3]=rb; d[4]=lf; d[5]=rf;}
  1166. }
  1167. mshr._tris+=2;
  1168. }
  1169. }
  1170. void setTriFaceSoft(C VecI2 &p0, C VecI2 &p1, C VecI2 &p2, Int mtrl_combo_index)
  1171. {
  1172. MeshBase &mshb=(*lod_parts)[mtrl_combo_index].base;
  1173. if(VecI *ind=mshb.tri.ind()) // this can be null for null materials
  1174. ind[mshb.tri._elms++].set(vtxIndexInMtrlCombo(p0.x, p0.y, mtrl_combo_index),
  1175. vtxIndexInMtrlCombo(p1.x, p1.y, mtrl_combo_index),
  1176. vtxIndexInMtrlCombo(p2.x, p2.y, mtrl_combo_index));
  1177. }
  1178. void setQuadFaceSoft(Int x, Int y, Int x1, Int y1, Int mtrl_combo_index)
  1179. {
  1180. MeshBase &mshb=(*lod_parts)[mtrl_combo_index].base;
  1181. if(VecI *ind=mshb.tri.ind()) // this can be null for null materials
  1182. {
  1183. Int lb=vtxIndexInMtrlCombo(x , y , mtrl_combo_index),
  1184. lf=vtxIndexInMtrlCombo(x , y1, mtrl_combo_index),
  1185. rb=vtxIndexInMtrlCombo(x1, y , mtrl_combo_index),
  1186. rf=vtxIndexInMtrlCombo(x1, y1, mtrl_combo_index);
  1187. Int *d=ind[mshb.tri._elms].c; mshb.tri._elms+=2;
  1188. Flt hlb=height.pixF(x , y ),
  1189. hlf=height.pixF(x , y1),
  1190. hrb=height.pixF(x1, y ),
  1191. hrf=height.pixF(x1, y1);
  1192. if(Abs(hlb-hrf)<Abs(hrb-hlf))
  1193. {
  1194. d[0]=lb; d[1]=lf; d[2]=rf; d[3]=rf; d[4]=rb; d[5]=lb;
  1195. }else
  1196. {
  1197. d[0]=lb; d[1]=lf; d[2]=rb; d[3]=rb; d[4]=lf; d[5]=rf;
  1198. }
  1199. }
  1200. }
  1201. // use NOINLINE to avoid compilers inlining because of big stack usage
  1202. NOINLINE void downSampleNormals(Int res, Int step, Int edge_step)
  1203. {
  1204. #if VTX_COMPRESS
  1205. const Bool storage_signed=D.meshStorageSigned();
  1206. const Int ofs=step/2, edge_ofs=(edge_step+1)/2, edge_soft_range=edge_ofs*3, res_step=res-step, res1=res-1; // use "(edge_step+1)/2" which gives same results as "Max(1, edge_step/2)" for pow2 but faster
  1207. // first store results into 'temp', if we would store directly in 'vtx_nrm' then one softening would affect all others
  1208. VecB4 temp[MAX_HM_RES/2-1][MAX_HM_RES/2], // !! since we don't use this for processing edges, then we can allocate 2 elements less "(MAX_HM_RES/2+1)-2" = "MAX_HM_RES/2-1", however for [y][x] X dimension use "MAX_HM_RES/2" to get Pow2 size and faster addressing
  1209. temp_edge[4][MAX_HM_RES]; // 4 because of 4 edges
  1210. // process inner cells (without the edges)
  1211. for(Int y=step, ty=0; y<res_step; y+=step, ty++) // !! if changed to include edges then adjust 'temp' size !!
  1212. for(Int x=step, tx=0; x<res_step; x+=step, tx++) // !! if changed to include edges then adjust 'temp' size !!
  1213. {
  1214. VecI nrm=0; Int weight=0;
  1215. for(Int sy=y-ofs, wy=0; wy<3; sy+=ofs, wy++)//if(InRange(sy, res)) !! not needed since we're not processing edges here !!
  1216. for(Int sx=x-ofs, wx=0; wx<3; sx+=ofs, wx++)//if(InRange(sx, res)) !! not needed since we're not processing edges here !!
  1217. {
  1218. C Int w=DownSizeWeights[wy][wx]; weight+=w;
  1219. C VecB4 &n= vtx_nrm[sy][sx];
  1220. if(storage_signed)
  1221. {
  1222. nrm.x+=w*I8(n.x);
  1223. nrm.y+=w*I8(n.y);
  1224. nrm.z+=w*I8(n.z);
  1225. }else
  1226. {
  1227. nrm.x+=w*(n.x-128);
  1228. nrm.y+=w*(n.y-128);
  1229. nrm.z+=w*(n.z-128);
  1230. }
  1231. }
  1232. nrm/=weight; // !! assumes weight>0 !!
  1233. VecB4 &n=temp[ty][tx];
  1234. if(storage_signed)
  1235. {
  1236. n.x=nrm.x;
  1237. n.y=nrm.y;
  1238. n.z=nrm.z;
  1239. }else
  1240. {
  1241. n.x=nrm.x+128;
  1242. n.y=nrm.y+128;
  1243. n.z=nrm.z+128;
  1244. }
  1245. }
  1246. // process outer edges
  1247. for(Int i=0; i<res; i+=edge_step) // march along main axis
  1248. {
  1249. VecI nrm[4]={0, 0, 0, 0}; Int weight[4]={0, 0, 0, 0};
  1250. for(Int si=Max(i-edge_soft_range, 0), si_end=Min(res1, i+edge_soft_range); si<=si_end; si+=edge_ofs)
  1251. //for(Int j=0; j<=ofs; j+=ofs) march along secondary axis, currently not implemented, to achieve edge normals having same values on neighbor areas, if we would average with the vtxs on the inside, then areas would have different edge normals, alternatively we could check for vtx normals of neighborhood areas, but that would require doing more calculations
  1252. {
  1253. C Int w=1;
  1254. weight[0]+=w;
  1255. weight[1]+=w;
  1256. weight[2]+=w;
  1257. weight[3]+=w;
  1258. C VecB4 &n0=vtx_nrm[ 0][si],
  1259. &n1=vtx_nrm[res1][si],
  1260. &n2=vtx_nrm[si][ 0],
  1261. &n3=vtx_nrm[si][res1];
  1262. if(storage_signed)
  1263. {
  1264. nrm[0].x+=w*I8(n0.x); nrm[0].y+=w*I8(n0.y); nrm[0].z+=w*I8(n0.z);
  1265. nrm[1].x+=w*I8(n1.x); nrm[1].y+=w*I8(n1.y); nrm[1].z+=w*I8(n1.z);
  1266. nrm[2].x+=w*I8(n2.x); nrm[2].y+=w*I8(n2.y); nrm[2].z+=w*I8(n2.z);
  1267. nrm[3].x+=w*I8(n3.x); nrm[3].y+=w*I8(n3.y); nrm[3].z+=w*I8(n3.z);
  1268. }else
  1269. {
  1270. nrm[0].x+=w*(n0.x-128); nrm[0].y+=w*(n0.y-128); nrm[0].z+=w*(n0.z-128);
  1271. nrm[1].x+=w*(n1.x-128); nrm[1].y+=w*(n1.y-128); nrm[1].z+=w*(n1.z-128);
  1272. nrm[2].x+=w*(n2.x-128); nrm[2].y+=w*(n2.y-128); nrm[2].z+=w*(n2.z-128);
  1273. nrm[3].x+=w*(n3.x-128); nrm[3].y+=w*(n3.y-128); nrm[3].z+=w*(n3.z-128);
  1274. }
  1275. }
  1276. REPD(e, 4)
  1277. {
  1278. VecI &edge_nrm=nrm[e];
  1279. edge_nrm/=weight[e]; // !! assumes weight>0 !!
  1280. VecB4 &n=temp_edge[e][i];
  1281. if(storage_signed)
  1282. {
  1283. n.x=edge_nrm.x;
  1284. n.y=edge_nrm.y;
  1285. n.z=edge_nrm.z;
  1286. }else
  1287. {
  1288. n.x=edge_nrm.x+128;
  1289. n.y=edge_nrm.y+128;
  1290. n.z=edge_nrm.z+128;
  1291. }
  1292. }
  1293. }
  1294. // store results
  1295. for(Int y=step, ty=0; y<res_step; y+=step, ty++)
  1296. for(Int x=step, tx=0; x<res_step; x+=step, tx++)vtx_nrm[y][x]=temp[ty][tx];
  1297. for(Int i=0; i<res; i+=edge_step)
  1298. {
  1299. vtx_nrm[ 0][i]=temp_edge[0][i];
  1300. vtx_nrm[res1][i]=temp_edge[1][i];
  1301. vtx_nrm[i][ 0]=temp_edge[2][i];
  1302. vtx_nrm[i][res1]=temp_edge[3][i];
  1303. }
  1304. #else
  1305. TODO
  1306. #endif
  1307. }
  1308. // use NOINLINE to avoid compilers inlining because of big stack usage
  1309. NOINLINE void downSampleNormalsSoft(Int res, Int step, Int edge_step, Vec (&vtx_nrm)[MAX_HM_RES][MAX_HM_RES])
  1310. {
  1311. const Int ofs=step/2, edge_ofs=(edge_step+1)/2, edge_soft_range=edge_ofs*3, res_step=res-step, res1=res-1; // use "(edge_step+1)/2" which gives same results as "Max(1, edge_step/2)" for pow2 but faster
  1312. // first store results into 'temp', if we would store directly in 'vtx_nrm' then one softening would affect all others
  1313. Vec temp[MAX_HM_RES/2-1][MAX_HM_RES/2], // !! since we don't use this for processing edges, then we can allocate 2 elements less "(MAX_HM_RES/2+1)-2" = "MAX_HM_RES/2-1", however for [y][x] X dimension use "MAX_HM_RES/2" to get Pow2 size and faster addressing
  1314. temp_edge[4][MAX_HM_RES]; // 4 because of 4 edges
  1315. // process inner cells (without the edges)
  1316. for(Int y=step, ty=0; y<res_step; y+=step, ty++) // !! if changed to include edges then adjust 'temp' size !!
  1317. for(Int x=step, tx=0; x<res_step; x+=step, tx++) // !! if changed to include edges then adjust 'temp' size !!
  1318. {
  1319. Vec nrm=0; Int weight=0;
  1320. for(Int sy=y-ofs, wy=0; wy<3; sy+=ofs, wy++)//if(InRange(sy, res)) !! not needed since we're not processing edges here !!
  1321. for(Int sx=x-ofs, wx=0; wx<3; sx+=ofs, wx++)//if(InRange(sx, res)) !! not needed since we're not processing edges here !!
  1322. {
  1323. C Int w=DownSizeWeights[wy][wx]; weight+=w;
  1324. C Vec &n= vtx_nrm[sy][sx];
  1325. nrm.x+=w*n.x;
  1326. nrm.y+=w*n.y;
  1327. nrm.z+=w*n.z;
  1328. }
  1329. nrm/=weight; // !! assumes weight>0 !!
  1330. temp[ty][tx]=nrm;
  1331. }
  1332. // process outer edges
  1333. for(Int i=0; i<res; i+=edge_step) // march along main axis
  1334. {
  1335. Vec nrm[4]={0, 0, 0, 0}; Int weight[4]={0, 0, 0, 0};
  1336. for(Int si=Max(i-edge_soft_range, 0), si_end=Min(res1, i+edge_soft_range); si<=si_end; si+=edge_ofs)
  1337. //for(Int j=0; j<=ofs; j+=ofs) march along secondary axis, currently not implemented, to achieve edge normals having same values on neighbor areas, if we would average with the vtxs on the inside, then areas would have different edge normals, alternatively we could check for vtx normals of neighborhood areas, but that would require doing more calculations
  1338. {
  1339. C Int w=1;
  1340. weight[0]+=w;
  1341. weight[1]+=w;
  1342. weight[2]+=w;
  1343. weight[3]+=w;
  1344. C Vec &n0=vtx_nrm[ 0][si],
  1345. &n1=vtx_nrm[res1][si],
  1346. &n2=vtx_nrm[si][ 0],
  1347. &n3=vtx_nrm[si][res1];
  1348. nrm[0].x+=w*n0.x; nrm[0].y+=w*n0.y; nrm[0].z+=w*n0.z;
  1349. nrm[1].x+=w*n1.x; nrm[1].y+=w*n1.y; nrm[1].z+=w*n1.z;
  1350. nrm[2].x+=w*n2.x; nrm[2].y+=w*n2.y; nrm[2].z+=w*n2.z;
  1351. nrm[3].x+=w*n3.x; nrm[3].y+=w*n3.y; nrm[3].z+=w*n3.z;
  1352. }
  1353. REPD(e, 4)
  1354. {
  1355. Vec &edge_nrm=nrm[e];
  1356. edge_nrm/=weight[e]; // !! assumes weight>0 !!
  1357. temp_edge[e][i]=edge_nrm;
  1358. }
  1359. }
  1360. // store results
  1361. for(Int y=step, ty=0; y<res_step; y+=step, ty++)
  1362. for(Int x=step, tx=0; x<res_step; x+=step, tx++)vtx_nrm[y][x]=temp[ty][tx];
  1363. for(Int i=0; i<res; i+=edge_step)
  1364. {
  1365. vtx_nrm[ 0][i]=temp_edge[0][i];
  1366. vtx_nrm[res1][i]=temp_edge[1][i];
  1367. vtx_nrm[i][ 0]=temp_edge[2][i];
  1368. vtx_nrm[i][res1]=temp_edge[3][i];
  1369. }
  1370. }
  1371. void downSampleMaterials(Int res, Int step)
  1372. {
  1373. #if 0 // this doesn't improve the quality greatly, so let's just skip it
  1374. Int ofs=step; step<<=1;
  1375. for(Int y=0; y<res; y+=step)
  1376. for(Int x=0; x<res; x+=step)
  1377. {
  1378. // set materials blend
  1379. Byte mtrl_indexes[3*3*4]; Int mtrl_indexes_num=0;
  1380. for(Int sy=y-ofs; sy<=y+ofs; sy+=ofs)if(InRange(sy, res))
  1381. for(Int sx=x-ofs; sx<=x+ofs; sx+=ofs)if(InRange(sx, res))
  1382. {
  1383. VecB4 &mi=T.mtrl_index[sy][sx];
  1384. Vec4 &mb=T.mtrl_blend[sy][sx]; this could be VecB4
  1385. REPA(mi)
  1386. {
  1387. RANGE_ASSERT(mtrl_indexes_num, mtrl_indexes);
  1388. mtrl_indexes[mtrl_indexes_num++]=mi.c[i];
  1389. material_blends[mi.c[i]]+=mb.c[i];
  1390. }
  1391. }
  1392. // get materials with highest blend
  1393. VecB4 mtrl_index=0;
  1394. Vec4 mtrl_blend=0;
  1395. REP( mtrl_indexes_num)
  1396. {
  1397. Byte j=mtrl_indexes[i];
  1398. if(Flt b=material_blends[j])
  1399. {
  1400. material_blends[j]=0; // restore 'material_blends' to initial zeros
  1401. FREPA(mtrl_blend)if(b>mtrl_blend.c[i])
  1402. {
  1403. for(Int last=Elms(mtrl_index); --last>i; )
  1404. {
  1405. mtrl_index.c[last]=mtrl_index.c[last-1];
  1406. mtrl_blend.c[last]=mtrl_blend.c[last-1];
  1407. }
  1408. mtrl_index.c[i]=j;
  1409. mtrl_blend.c[i]=b;
  1410. break;
  1411. }
  1412. }
  1413. }
  1414. // store
  1415. T.mtrl_index[y][x]=mtrl_index;
  1416. T.mtrl_blend[y][x]=mtrl_blend;
  1417. }
  1418. #endif
  1419. }
  1420. };
  1421. /******************************************************************************/
  1422. // use NOINLINE to avoid compilers inlining because of big stack usage
  1423. NOINLINE Bool Heightmap::buildEx2(Mesh &mesh, Int quality, Flt tex_scale, UInt flag, BuildMemSoft /* !! Warning: this may be only 'BuildMem' !! */ &mem, C Heightmap *h_l, C Heightmap *h_r, C Heightmap *h_b, C Heightmap *h_f, C Heightmap *h_lb, C Heightmap *h_lf, C Heightmap *h_rb, C Heightmap *h_rf) // this function should be multi-threaded safe
  1424. {
  1425. Int res=resolution();
  1426. // validate params
  1427. Clamp(quality, 0, 2);
  1428. if(h_l && h_l ->resolution()!=res)h_l =null;
  1429. if(h_r && h_r ->resolution()!=res)h_r =null;
  1430. if(h_b && h_b ->resolution()!=res)h_b =null;
  1431. if(h_f && h_f ->resolution()!=res)h_f =null;
  1432. if(h_lb && h_lb->resolution()!=res)h_lb=null;
  1433. if(h_lf && h_lf->resolution()!=res)h_lf=null;
  1434. if(h_rb && h_rb->resolution()!=res)h_rb=null;
  1435. if(h_rf && h_rf->resolution()!=res)h_rf=null;
  1436. C Image *hl =(h_l ? &h_l ->_height : null),
  1437. *hr =(h_r ? &h_r ->_height : null),
  1438. *hb =(h_b ? &h_b ->_height : null),
  1439. *hf =(h_f ? &h_f ->_height : null),
  1440. *hlb=(h_lb ? &h_lb->_height : null),
  1441. *hlf=(h_lf ? &h_lf->_height : null),
  1442. *hrb=(h_rb ? &h_rb->_height : null),
  1443. *hrf=(h_rf ? &h_rf->_height : null);
  1444. // constants
  1445. const Int res1 =res-1,
  1446. step =(1<<quality),
  1447. ao_step =3,
  1448. ao_range=2*ao_step;
  1449. const Flt nrm_y =2.00f/res1,
  1450. ao_mul =0.19f*res1;
  1451. const Bool ambient_occlusion=FlagTest(flag, HM_AO),
  1452. soft =FlagTest(flag, HM_SOFT),
  1453. build_null_mtrl =FlagTest(flag, HM_BUILD_NULL_MTRL);
  1454. // vars
  1455. Flt sharpness=0; Int sharpness_count=0;
  1456. Builder builder(_height, _mtrl_index
  1457. #if !VMC_CONTINUOUS
  1458. , mem.vmc
  1459. #endif
  1460. );
  1461. Flt min_y= FLT_MAX,
  1462. max_y=-FLT_MAX;
  1463. VecB4 (*const NrmToByte4)(C Vec &v)=(D.meshStorageSigned() ? NrmToSByte4 : NrmToUByte4);
  1464. // set per-vertex data and calculate min/max height
  1465. for(Int y=0; y<res; y+=step)
  1466. {
  1467. builder.frac[y]=y/Flt(res1);
  1468. //Flt fy=y/Flt(res1);
  1469. for(Int x=0; x<res; x+=step)
  1470. {
  1471. //Flt fx=x/Flt(res1);
  1472. //builder.mtrl_index[y][x]=_mtrl_index.pixB4(x, y);
  1473. #if MTRL_BLEND_HP
  1474. if(_mtrl_blend.hwType()==IMAGE_F32_4)builder.mtrl_blend[y][x]=_mtrl_blend.pixF4(x, y);
  1475. else builder.mtrl_blend[y][x]=_mtrl_blend.pixB4(x, y); // doesn't need to be scaled if all blends have the same scale
  1476. #else
  1477. builder.mtrl_blend[y][x]=MtrlBlendB(_mtrl_blend, x, y);
  1478. #endif
  1479. Flt h=_height.pixF(x, y);
  1480. // min/max height
  1481. MIN(min_y, h);
  1482. MAX(max_y, h);
  1483. // normal
  1484. #if VTX_COMPRESS
  1485. Vec nrm;
  1486. #else
  1487. Vec &nrm=builder.vtx_nrm[y][x];
  1488. #endif
  1489. Vec2 ddh;
  1490. Flt l, r;
  1491. if(x!= 0)l=_height.pixF(x-1, y);else if(hl)l=hl->pixF(res-2, y);else{nrm.x=(_height.pixF( 0, y)-_height.pixF( 1, y))*2; goto processed_x;}
  1492. if(x!=res1)r=_height.pixF(x+1, y);else if(hr)r=hr->pixF( 1, y);else{nrm.x=(_height.pixF(res-2, y)-_height.pixF(res1, y))*2; goto processed_x;}
  1493. nrm.x=l-r;
  1494. sharpness+=Abs(h-Avg(l, r)); sharpness_count++;
  1495. processed_x:
  1496. Flt b, f;
  1497. if(y!= 0)b=_height.pixF(x, y-1);else if(hb)b=hb->pixF(x, res-2);else{nrm.z=(_height.pixF(x, 0)-_height.pixF(x, 1))*2; goto processed_z;}
  1498. if(y!=res1)f=_height.pixF(x, y+1);else if(hf)f=hf->pixF(x, 1);else{nrm.z=(_height.pixF(x, res-2)-_height.pixF(x, res1))*2; goto processed_z;}
  1499. nrm.z=b-f;
  1500. sharpness+=Abs(h-Avg(b, f)); sharpness_count++;
  1501. processed_z:
  1502. ddh=nrm.xz();
  1503. nrm.y=nrm_y; nrm.normalize();
  1504. #if VTX_COMPRESS
  1505. if(soft) mem.vtx_nrm[y][x]=nrm;
  1506. else builder.vtx_nrm[y][x]=NrmToByte4(nrm);
  1507. #endif
  1508. // pos, tex, tan
  1509. //builder.vtx_pos[y][x].set(fx, h, fy);
  1510. #if !VTX_HEIGHTMAP
  1511. builder.vtx_tan[y][x]=!PointOnPlane(Vec(1, 0, 0), nrm);
  1512. builder.vtx_tex[y][x].set(( fx+area._xz.x)*tex_scale,
  1513. (-fy-area._xz.y)*tex_scale);
  1514. #endif
  1515. if(ambient_occlusion)
  1516. {
  1517. Flt occl=0, power=EPS; // EPS prevents division by zero
  1518. ddh*=-0.5f;
  1519. for(Int dy=-ao_range; dy<=ao_range; dy+=ao_step)
  1520. for(Int dx=-ao_range; dx<=ao_range; dx+=ao_step)
  1521. {
  1522. Int tx=x+dx,
  1523. ty=y+dy;
  1524. Flt th;
  1525. if(tx<0)
  1526. {
  1527. if(ty< 0){if(hlb)th=hlb->pixF(tx+res1, ty+res1);else th=h +(_height.pixF(0, 0)-_height.pixF(1, 1))*Dist(dx, dy);}else
  1528. if(ty>=res){if(hlf)th=hlf->pixF(tx+res1, ty-res1);else th=h +(_height.pixF(0, res1)-_height.pixF(1, res1-1))*Dist(dx, dy);}else
  1529. {if(hl )th=hl ->pixF(tx+res1, ty );else th=_height.pixF(x, ty)-(_height.pixF(0, ty)-_height.pixF(1, ty))*dx;} // here 'dx' is negative
  1530. }else
  1531. if(tx>=res)
  1532. {
  1533. if(ty< 0){if(hrb)th=hrb->pixF(tx-res1, ty+res1);else th=h +(_height.pixF(res1, 0)-_height.pixF(res1-1, 1))*Dist(dx, dy);}else
  1534. if(ty>=res){if(hrf)th=hrf->pixF(tx-res1, ty-res1);else th=h +(_height.pixF(res1, res1)-_height.pixF(res1-1, res1-1))*Dist(dx, dy);}else
  1535. {if(hr )th=hr ->pixF(tx-res1, ty );else th=_height.pixF(x, ty)+(_height.pixF(res1, ty)-_height.pixF(res1-1, ty))*dx;} // here 'dx' is positive
  1536. }else
  1537. {
  1538. if(ty< 0){if(hb)th=hb->pixF(tx, ty+res1);else th=_height.pixF(tx, y)-(_height.pixF(tx, 0)-_height.pixF(tx, 1))*dy;}else // here 'dy' is negative
  1539. if(ty>=res){if(hf)th=hf->pixF(tx, ty-res1);else th=_height.pixF(tx, y)+(_height.pixF(tx, res1)-_height.pixF(tx, res1-1))*dy;}else // here 'dy' is positive
  1540. th=_height.pixF(tx, ty);
  1541. }
  1542. Flt d=th - h - dx*ddh.x - dy*ddh.y,
  1543. o=Sat(d*ao_mul),
  1544. p=Lerp(0.3f, 1.0f, BlendSqr(Dist(dx, dy)/ao_range));
  1545. occl +=p*o;
  1546. power+=p;
  1547. }
  1548. builder.occlusion[y][x]=FltToByte(1-occl/power);
  1549. }
  1550. }
  1551. }
  1552. #if 0
  1553. mesh.box .set(0, min_y, 0, 1, max_y, 1);
  1554. mesh.lod_center.set(0.5f, mesh.box.centerY(), 0.5f);
  1555. #else
  1556. mesh.ext.ext.set(0.5f, (max_y-min_y)*0.5f, 0.5f);
  1557. mesh.lod_center=mesh.ext.pos.set(0.5f, (min_y+max_y)*0.5f, 0.5f);
  1558. #endif
  1559. // adjust sharpness
  1560. AdjustSharpness(sharpness, sharpness_count);
  1561. // downsample
  1562. REP(quality)builder.downSampleMaterials(res, 1<<i);
  1563. Image color; if(T._color.is()){T._color.copyTry(color); REP(quality)color.resize(color.w()/2+1, color.h()/2+1, FILTER_LINEAR, true, false, true);}
  1564. // set LOD levels
  1565. mesh.setLods(Max(1, Min(LODS, BitHi(Unsigned(res1)))-quality)); // BitHi(2)->1 (1 lod), BitHi(4)->2 (2 lods), BitHi(8)->3 (3 lods)
  1566. FREPD(l, mesh.lods())
  1567. {
  1568. MeshLod &lod=mesh.lod(l); builder.lod_parts=&lod.parts;
  1569. const Int L =l+quality, // lod affected by quality
  1570. edge_quality=Max(l-2, 0), // !! this affects number of elements in 'VtxMtrlCombos' !!
  1571. LE =L+edge_quality, // lod affected by quality and edge quality
  1572. L_2 =(L ? 1<<(L-1) : 0),
  1573. Step =(step<<edge_quality); // step affected by edge quality
  1574. Int from =( 0>>L),
  1575. to =(res1>>L);
  1576. if(l)
  1577. {
  1578. from++;
  1579. to --;
  1580. // downsample
  1581. if(soft)builder.downSampleNormalsSoft(res, step<<l, Step, mem.vtx_nrm); // normals
  1582. else builder.downSampleNormals (res, step<<l, Step ); // normals
  1583. builder.downSampleMaterials (res, step<<(l-1) ); // materials
  1584. color.resize(color.w()/2+1, color.h()/2+1, FILTER_LINEAR, true, false, true); // color map
  1585. }
  1586. builder.clear(res);
  1587. // for each face, test the index of Heightmap Element, and count the number of vtxs/tris/quads
  1588. for(Int y=from; y<to; y++)
  1589. for(Int x=from; x<to; x++)builder.map_mtrl_combo[y][x]=builder.getMtrlComboForQuad(x<<L, y<<L, (x+1)<<L, (y+1)<<L);
  1590. // add edges
  1591. if(l)
  1592. {
  1593. for(Int i=0; i<res1; i+=Step)
  1594. {
  1595. Int x=((i+L_2)>>L)<<L;
  1596. if(x<=0 )x= (1<<L);else
  1597. if(x>=res1)x=res1-(1<<L);
  1598. builder.edg_mtrl_combo[0][i]=builder.getMtrlComboForTri(VecI2(i+Step, 0), VecI2(i , 0), VecI2(x, 1<<L )); // back
  1599. builder.edg_mtrl_combo[1][i]=builder.getMtrlComboForTri(VecI2(i , res1), VecI2(i+Step, res1), VecI2(x, res1-(1<<L))); // forward
  1600. builder.edg_mtrl_combo[2][i]=builder.getMtrlComboForTri(VecI2(0 , i ), VecI2(0 , i+Step), VecI2( 1<<L , x)); // left
  1601. builder.edg_mtrl_combo[3][i]=builder.getMtrlComboForTri(VecI2(res1, i+Step), VecI2(res1, i ), VecI2(res1-(1<<L), x)); // right
  1602. }
  1603. for(Int i=from; i<to; i++)
  1604. {
  1605. Int e=((i+i+1)<<(L-1));
  1606. builder.tri_mtrl_combo[0][i]=builder.getMtrlComboForTri(VecI2( i <<L, 1<<L ), VecI2((i+1)<<L, 1<<L ), VecI2(e, 0)); // back
  1607. builder.tri_mtrl_combo[1][i]=builder.getMtrlComboForTri(VecI2((i+1)<<L, res1-(1<<L)), VecI2( i <<L, res1-(1<<L)), VecI2(e, res1)); // forward
  1608. builder.tri_mtrl_combo[2][i]=builder.getMtrlComboForTri(VecI2( 1<<L , (i+1)<<L), VecI2( 1<<L , i <<L), VecI2( 0, e)); // left
  1609. builder.tri_mtrl_combo[3][i]=builder.getMtrlComboForTri(VecI2(res1-(1<<L), i <<L), VecI2(res1-(1<<L), (i+1)<<L), VecI2(res1, e)); // right
  1610. }
  1611. }
  1612. // create mesh lods
  1613. if(lod.parts.elms()!=builder.mtrl_combos.elms())lod.create(builder.mtrl_combos.elms());
  1614. lod.dist(GetLodDist(l, sharpness));
  1615. // create mesh parts
  1616. FREPA(builder.mtrl_combos)
  1617. {
  1618. MtrlCombo &mtrl_combo=builder.mtrl_combos[i];
  1619. MeshPart &part = lod.parts [i];
  1620. UInt flag=VTX_POS|VTX_NRM|TRI_IND;
  1621. if(!VTX_HEIGHTMAP)flag|=VTX_TEX0|VTX_TAN;
  1622. if(ambient_occlusion || color.is())flag|=VTX_COLOR;
  1623. if( mtrl_combo.mtrl_index.y/* || mtrl_combo.mtrl_index.z || mtrl_combo.mtrl_index.w*/)flag|=VTX_MATERIAL;else // 'mtrl_index' in 'mtrl_combo' is always sorted, so we only need to check if 2nd material is not null
  1624. if(!mtrl_combo.mtrl_index.x && !build_null_mtrl)mtrl_combo.reset(); // prevent creating null material
  1625. if(soft)
  1626. {
  1627. MeshBase &mshb=part.base;
  1628. mshb.create(mtrl_combo.vtxs, 0, mtrl_combo.tris, 0, flag);
  1629. mshb.tri._elms=0; // clear so we can use it as progress index for adding new tris
  1630. }else
  1631. {
  1632. MeshRender &mshr=part.render;
  1633. if(!mshr.create(mtrl_combo.vtxs, mtrl_combo.tris, flag, VTX_COMPRESS))return false;
  1634. if(mshr.vtxs() && !mshr.vtxLock(LOCK_WRITE))return false;
  1635. if(mshr.tris() && !mshr.indLock(LOCK_WRITE))return false;
  1636. mshr._tris=0; // clear so we can use it as progress index for adding new tris
  1637. mtrl_combo.ofs_pos =mshr.vtxOfs(VTX_POS );
  1638. mtrl_combo.ofs_nrm =mshr.vtxOfs(VTX_NRM );
  1639. mtrl_combo.ofs_color =mshr.vtxOfs(VTX_COLOR );
  1640. mtrl_combo.ofs_material=mshr.vtxOfs(VTX_MATERIAL);
  1641. }
  1642. }
  1643. // store in mesh
  1644. if(soft)
  1645. {
  1646. // set vertexes
  1647. REPD(y, res)
  1648. REPD(x, res)
  1649. {
  1650. #if VMC_CONTINUOUS
  1651. UInt i=builder.vmc_first[y][x]; if(i!=~0)
  1652. for(;;)
  1653. {
  1654. VtxMtrlCombo &vmc=builder.vmc[i]; Int mc=vmc.mtrl_combo;
  1655. #else
  1656. VtxMtrlCombos &vmc_xy=mem.vmc[y][x]; FREPA(vmc_xy.vmc)
  1657. {
  1658. VtxMtrlCombo &vmc = vmc_xy.vmc [ i]; Int mc=vmc.mtrl_combo; if(mc==VMC_NULL)break;
  1659. #endif
  1660. MtrlCombo &mtrl_combo= builder.mtrl_combos[mc];
  1661. MeshBase &mshb =(*builder.lod_parts )[mc].base;
  1662. if(mshb.vtx._elms) // this can be 0 for null materials
  1663. {
  1664. Int vtx_index=vmc.mc_vtx_index;
  1665. mshb.vtx.pos(vtx_index).set(builder.frac[x], _height.pixF(x, y), builder.frac[y]); // position
  1666. mshb.vtx.nrm(vtx_index)=mem.vtx_nrm[y][x]; // normal
  1667. if(mshb.vtx.material()) // material
  1668. {
  1669. VecB4 &vtx_mtrl=mshb.vtx.material(vtx_index);
  1670. C VecB4 & mc_mtrl=mtrl_combo.mtrl_index,
  1671. & hm_mtrl= _mtrl_index.pixB4(x, y);
  1672. #if 1
  1673. vtx_mtrl=VtxMtrlBlend(mc_mtrl, hm_mtrl, builder.mtrl_blend[y][x]);
  1674. #else
  1675. if(_mtrl_blend.hwType()==IMAGE_F32_4)vtx_mtrl=VtxMtrlBlend(mc_mtrl, hm_mtrl, _mtrl_blend.pixF4(x, y));
  1676. else vtx_mtrl=VtxMtrlBlend(mc_mtrl, hm_mtrl, _mtrl_blend.pixB4(x, y));
  1677. #endif
  1678. }
  1679. if(mshb.vtx.color()) // color
  1680. {
  1681. Color &vtx_color=mshb.vtx.color(vtx_index);
  1682. if(ambient_occlusion)
  1683. {
  1684. Byte occl=builder.occlusion[y][x];
  1685. if(color.is())vtx_color=ColorBrightnessB(color.color(x>>L, y>>L), occl);
  1686. else vtx_color.set(occl);
  1687. }else vtx_color=color.color(x>>L, y>>L); // in this case 'Image color' always exists, because "mtrl_combo.ofs_color>=0" implies that vertex contains color, and it was created only if 'ambient_occlusion' or 'Image color' exists
  1688. }
  1689. }
  1690. #if VMC_CONTINUOUS
  1691. if(vmc.next_add)i+=vmc.next_add;else break;
  1692. #endif
  1693. }
  1694. }
  1695. // set faces
  1696. {
  1697. // quads
  1698. for(Int y=from; y<to; y++)
  1699. for(Int x=from; x<to; x++)builder.setQuadFaceSoft(x<<L, y<<L, (x+1)<<L, (y+1)<<L, builder.map_mtrl_combo[y][x]);
  1700. // tri (edge-skirts)
  1701. if(l)
  1702. {
  1703. for(Int i=0; i<res1; i+=Step)
  1704. {
  1705. Int x=((i+L_2)>>L)<<L;
  1706. if(x<=0 )x= (1<<L);else
  1707. if(x>=res1)x=res1-(1<<L);
  1708. builder.setTriFaceSoft(VecI2(i+Step, 0), VecI2(i , 0), VecI2(x, (1<<L)), builder.edg_mtrl_combo[0][i]); // back
  1709. builder.setTriFaceSoft(VecI2(i , res1), VecI2(i+Step, res1), VecI2(x, res1-(1<<L)), builder.edg_mtrl_combo[1][i]); // forward
  1710. builder.setTriFaceSoft(VecI2(0 , i ), VecI2(0 , i+Step), VecI2( 1<<L , x), builder.edg_mtrl_combo[2][i]); // left
  1711. builder.setTriFaceSoft(VecI2(res1, i+Step), VecI2(res1, i ), VecI2(res1-(1<<L), x), builder.edg_mtrl_combo[3][i]); // right
  1712. }
  1713. for(Int i=from; i<to; i++)
  1714. {
  1715. Int e=((i+i+1)<<(L-1));
  1716. builder.setTriFaceSoft(VecI2( i <<L, 1<<L ), VecI2((i+1)<<L, 1<<L ), VecI2(e, 0), builder.tri_mtrl_combo[0][i]); // back
  1717. builder.setTriFaceSoft(VecI2((i+1)<<L, res1-(1<<L)), VecI2( i <<L, res1-(1<<L)), VecI2(e, res1), builder.tri_mtrl_combo[1][i]); // forward
  1718. builder.setTriFaceSoft(VecI2( 1<<L , (i+1)<<L), VecI2( 1<<L , i <<L), VecI2( 0, e), builder.tri_mtrl_combo[2][i]); // left
  1719. builder.setTriFaceSoft(VecI2(res1-(1<<L), i <<L), VecI2(res1-(1<<L), (i+1)<<L), VecI2(res1, e), builder.tri_mtrl_combo[3][i]); // right
  1720. }
  1721. }
  1722. }
  1723. }else
  1724. {
  1725. // set vertexes
  1726. REPD(y, res)
  1727. REPD(x, res)
  1728. {
  1729. #if VMC_CONTINUOUS
  1730. UInt i=builder.vmc_first[y][x]; if(i!=~0)
  1731. for(;;)
  1732. {
  1733. VtxMtrlCombo &vmc=builder.vmc[i]; Int mc=vmc.mtrl_combo;
  1734. #else
  1735. VtxMtrlCombos &vmc_xy=mem.vmc[y][x]; FREPA(vmc_xy.vmc)
  1736. {
  1737. VtxMtrlCombo &vmc = vmc_xy.vmc [ i]; Int mc=vmc.mtrl_combo; if(mc==VMC_NULL)break;
  1738. #endif
  1739. MtrlCombo &mtrl_combo= builder.mtrl_combos[mc];
  1740. MeshRender &mshr =(*builder.lod_parts )[mc].render;
  1741. if(mshr.vtxLockedData())
  1742. {
  1743. Byte *v=(Byte*)mshr.vtxLockedData()+vmc.mc_vtx_index*mshr.vtxSize();
  1744. ((Vec*)(v+mtrl_combo.ofs_pos))->set(builder.frac[x], _height.pixF(x, y), builder.frac[y]); // position
  1745. #if VTX_COMPRESS
  1746. *((VecB4*)(v+mtrl_combo.ofs_nrm))=builder.vtx_nrm[y][x]; // normal
  1747. #else
  1748. *((Vec *)(v+mtrl_combo.ofs_nrm))=builder.vtx_nrm[y][x]; // normal
  1749. #endif
  1750. if(mtrl_combo.ofs_material>=0) // material
  1751. {
  1752. VecB4 &vtx_mtrl=*((VecB4*)(v+mtrl_combo.ofs_material));
  1753. C VecB4 & mc_mtrl=mtrl_combo.mtrl_index,
  1754. & hm_mtrl= _mtrl_index.pixB4(x, y);
  1755. #if 1
  1756. vtx_mtrl=VtxMtrlBlend(mc_mtrl, hm_mtrl, builder.mtrl_blend[y][x]);
  1757. #else
  1758. if(_mtrl_blend.hwType()==IMAGE_F32_4)vtx_mtrl=VtxMtrlBlend(mc_mtrl, hm_mtrl, _mtrl_blend.pixF4(x, y));
  1759. else vtx_mtrl=VtxMtrlBlend(mc_mtrl, hm_mtrl, _mtrl_blend.pixB4(x, y));
  1760. #endif
  1761. }
  1762. if(mtrl_combo.ofs_color>=0) // color
  1763. {
  1764. Color &vtx_color=*((Color*)(v+mtrl_combo.ofs_color));
  1765. if(ambient_occlusion)
  1766. {
  1767. Byte occl=builder.occlusion[y][x];
  1768. if(color.is())vtx_color=ColorBrightnessB(color.color(x>>L, y>>L), occl);
  1769. else vtx_color.set(occl);
  1770. }else vtx_color=color.color(x>>L, y>>L); // in this case 'Image color' always exists, because "mtrl_combo.ofs_color>=0" implies that vertex contains color, and it was created only if 'ambient_occlusion' or 'Image color' exists
  1771. }
  1772. }
  1773. #if VMC_CONTINUOUS
  1774. if(vmc.next_add)i+=vmc.next_add;else break;
  1775. #endif
  1776. }
  1777. }
  1778. // set faces
  1779. {
  1780. // quads
  1781. for(Int y=from; y<to; y++)
  1782. for(Int x=from; x<to; x++)builder.setQuadFace(x<<L, y<<L, (x+1)<<L, (y+1)<<L, builder.map_mtrl_combo[y][x]);
  1783. // tri (edge-skirts)
  1784. if(l)
  1785. {
  1786. for(Int i=0; i<res1; i+=Step)
  1787. {
  1788. Int x=((i+L_2)>>L)<<L;
  1789. if(x<=0 )x= (1<<L);else
  1790. if(x>=res1)x=res1-(1<<L);
  1791. builder.setTriFace(VecI2(i+Step, 0), VecI2(i , 0), VecI2(x, (1<<L)), builder.edg_mtrl_combo[0][i]); // back
  1792. builder.setTriFace(VecI2(i , res1), VecI2(i+Step, res1), VecI2(x, res1-(1<<L)), builder.edg_mtrl_combo[1][i]); // forward
  1793. builder.setTriFace(VecI2(0 , i ), VecI2(0 , i+Step), VecI2( 1<<L , x), builder.edg_mtrl_combo[2][i]); // left
  1794. builder.setTriFace(VecI2(res1, i+Step), VecI2(res1, i ), VecI2(res1-(1<<L), x), builder.edg_mtrl_combo[3][i]); // right
  1795. }
  1796. for(Int i=from; i<to; i++)
  1797. {
  1798. Int e=((i+i+1)<<(L-1));
  1799. builder.setTriFace(VecI2( i <<L, 1<<L ), VecI2((i+1)<<L, 1<<L ), VecI2(e, 0), builder.tri_mtrl_combo[0][i]); // back
  1800. builder.setTriFace(VecI2((i+1)<<L, res1-(1<<L)), VecI2( i <<L, res1-(1<<L)), VecI2(e, res1), builder.tri_mtrl_combo[1][i]); // forward
  1801. builder.setTriFace(VecI2( 1<<L , (i+1)<<L), VecI2( 1<<L , i <<L), VecI2( 0, e), builder.tri_mtrl_combo[2][i]); // left
  1802. builder.setTriFace(VecI2(res1-(1<<L), i <<L), VecI2(res1-(1<<L), (i+1)<<L), VecI2(res1, e), builder.tri_mtrl_combo[3][i]); // right
  1803. }
  1804. }
  1805. }
  1806. }
  1807. // finalize mesh parts
  1808. REPA(builder.mtrl_combos) // go from the end to delete
  1809. {
  1810. MeshPart &part=lod.parts[i];
  1811. // first check if empty, then remove, this can happen for example if material combo was created for main LOD, but a smaller LOD didn't have this, or for null material materials with "build_null_mtrl==false"
  1812. if(soft)
  1813. {
  1814. MeshBase &base=part.base;
  1815. if(!base.is())goto remove_part;
  1816. }else
  1817. {
  1818. MeshRender &mshr=part.render;
  1819. if(!mshr.is())goto remove_part;
  1820. mshr.indUnlock();
  1821. mshr.vtxUnlock();
  1822. }
  1823. {
  1824. MtrlCombo &mtrl_combo=builder.mtrl_combos[i];
  1825. VecB4 &mtrl_index=mtrl_combo.mtrl_index;
  1826. #if VTX_HEIGHTMAP
  1827. part._vtx_heightmap=tex_scale; // !! set before calling 'multiMaterial' because this value affects the shader !!
  1828. #endif
  1829. part.multiMaterial(_materials[mtrl_index.c[0]], _materials[mtrl_index.c[1]], _materials[mtrl_index.c[2]], _materials[mtrl_index.c[3]], soft ? -1 : l); // set shaders only for hardware mode
  1830. }
  1831. continue; // continue without removing this part
  1832. remove_part:
  1833. lod.parts.remove(i, true);
  1834. }
  1835. }
  1836. return true;
  1837. }
  1838. Bool Heightmap::buildEx(Mesh &mesh, Int quality, Flt tex_scale, UInt flag, BuildMemSoft &mem, C Heightmap *h_l, C Heightmap *h_r, C Heightmap *h_b, C Heightmap *h_f, C Heightmap *h_lb, C Heightmap *h_lf, C Heightmap *h_rb, C Heightmap *h_rf) // this function should be multi-threaded safe
  1839. {
  1840. if(is())
  1841. {
  1842. // buildEx2 was separated because it uses a very big stack
  1843. if(buildEx2(mesh, quality, tex_scale, flag, mem, h_l, h_r, h_b, h_f, h_lb, h_lf, h_rb, h_rf))
  1844. {
  1845. // remove empty lods
  1846. REPD(l, mesh.lods())if(l && !mesh.lod(l).is())mesh.removeLod(l);
  1847. mesh.sortByMaterials();
  1848. return true;
  1849. }
  1850. }
  1851. mesh.del(); return false;
  1852. }
  1853. /******************************************************************************/
  1854. void Heightmap::build(Mesh &dest_mesh, Int quality, Flt tex_scale, UInt flag, C Heightmap *l, C Heightmap *r, C Heightmap *b, C Heightmap *f, C Heightmap *lb, C Heightmap *lf, C Heightmap *rb, C Heightmap *rf, Mems<Byte> *temp_mem)
  1855. {
  1856. if(is())
  1857. {
  1858. // !! prefer 'Mems' because 'Memc' for example will round up memory size to nearest Pow2
  1859. Int build_mem_size=((flag&HM_SOFT) ? SIZE(BuildMemSoft) : SIZE(BuildMem));
  1860. Ptr build_mem; if(temp_mem)build_mem=temp_mem->setNum(build_mem_size).data();else build_mem=Alloc(build_mem_size);
  1861. buildEx(dest_mesh, quality, tex_scale, flag, *(BuildMemSoft*)build_mem, l, r, b, f, lb, lf, rb, rf);
  1862. if(!temp_mem)Free(build_mem);
  1863. }else dest_mesh.del();
  1864. }
  1865. /******************************************************************************/
  1866. void Heightmap::resize(Int res)
  1867. {
  1868. Clamp(res, 2, MAX_HM_RES); res=NearestPow2(res)|1;
  1869. if(res!=resolution())
  1870. {
  1871. _height .resize(res, res, FILTER_BEST, true, false, true);
  1872. _color .resize(res, res, FILTER_BEST, true, false, true);
  1873. _mtrl_index.resize(res, res, FILTER_NONE, true, false, true);
  1874. _mtrl_blend.resize(res, res, FILTER_NONE, true, false, true);
  1875. }
  1876. }
  1877. /******************************************************************************/
  1878. // IO
  1879. /******************************************************************************/
  1880. static Bool SaveAs(C Image &image, File &f, IMAGE_TYPE type)
  1881. {
  1882. if(!image.is() || image.type()==type)return image.saveData(f);
  1883. Image temp; if(image.copyTry(temp, -1, -1, -1, type))return temp.saveData(f);
  1884. return false;
  1885. }
  1886. static Bool LoadAs(Image &image, File &f, IMAGE_TYPE type)
  1887. {
  1888. if(!image.loadData(f))return false;
  1889. if( image.is() && image.type()!=type)if(!image.copyTry(image, -1, -1, -1, type))return false;
  1890. return true;
  1891. }
  1892. static Bool AddEmptyAlpha(Image &image)
  1893. {
  1894. if(image.copyTry(image, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))if(image.lock())
  1895. {
  1896. REPD(y, image.lh())
  1897. REPD(x, image.lw())image.pixC(x, y).a=0;
  1898. image.unlock();
  1899. return true;
  1900. }
  1901. return false;
  1902. }
  1903. Bool Heightmap::save(File &f, CChar *path)C
  1904. {
  1905. f.cmpUIntV(2); // version
  1906. if( _height .saveData(f ))
  1907. if(SaveAs(_color , f, IMAGE_R8G8B8 ))
  1908. if( _mtrl_index.saveData(f ))
  1909. if(SaveAs(_mtrl_blend, f, IMAGE_R8G8B8A8))
  1910. if(_materials.save(f, path))
  1911. return f.ok();
  1912. return false;
  1913. }
  1914. Bool Heightmap::load(File &f, CChar *path)
  1915. {
  1916. del();
  1917. switch(f.decUIntV()) // version
  1918. {
  1919. case 2:
  1920. {
  1921. if(_height .loadData(f))
  1922. if(_color .loadData(f))
  1923. if(_mtrl_index.loadData(f))
  1924. if(_mtrl_blend.loadData(f))
  1925. if(_materials .load (f, path))
  1926. if(f.ok())return true;
  1927. }break;
  1928. case 1:
  1929. {
  1930. if( _height .loadData(f) )
  1931. if(LoadAs(_color , f, IMAGE_R8G8B8))
  1932. if(LoadAs(_mtrl_index, f, IMAGE_R8G8B8))if(AddEmptyAlpha(_mtrl_index))
  1933. if(LoadAs(_mtrl_blend, f, IMAGE_R8G8B8))if(AddEmptyAlpha(_mtrl_blend))
  1934. if(_materials.load(f, path))
  1935. if(f.ok())return true;
  1936. }break;
  1937. case 0:
  1938. {
  1939. if( _height .loadData(f) )
  1940. if(LoadAs(_color , f, IMAGE_R8G8B8))
  1941. if(LoadAs(_mtrl_index, f, IMAGE_B8G8R8))
  1942. if(LoadAs(_mtrl_blend, f, IMAGE_R8G8B8))
  1943. {
  1944. if(_mtrl_index.hwType()==IMAGE_B8G8R8)_mtrl_index._type=_mtrl_index._hw_type=IMAGE_R8G8B8; // old version used 'IMAGE_B8G8R8' and the index for the first material was stored in first byte which is blue
  1945. if(_materials.loadOld(f, path))
  1946. {
  1947. // check if any material index addresses non-existing material (this for some reason happened on one machine)
  1948. REPD(y, _mtrl_index.h())
  1949. REPD(x, _mtrl_index.w())
  1950. {
  1951. VecB &v=_mtrl_index.pixB3(x, y);
  1952. {
  1953. if(v.x>=_materials.elms())v.x=0;
  1954. if(v.y>=_materials.elms())v.y=0;
  1955. if(v.z>=_materials.elms())v.z=0;
  1956. }
  1957. }
  1958. if(f.ok())
  1959. if(AddEmptyAlpha(_mtrl_index))
  1960. if(AddEmptyAlpha(_mtrl_blend))
  1961. return true;
  1962. }
  1963. }
  1964. }break;
  1965. }
  1966. del(); return false;
  1967. }
  1968. /******************************************************************************/
  1969. }
  1970. /******************************************************************************/