Material.cpp 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. namespace EE{
  4. /******************************************************************************
  5. If Material has only 1 texture (base_0), then it contains:
  6. Base0: RGBA
  7. If Material has only 2 textures (base_0 and base_1), then they contain:
  8. Base0: RGB, Bump
  9. Base1: NrmX, NrmY, Spec, Alpha/Glow
  10. When changing the above to a different order, then look for "#MaterialTextureChannelOrder" text in Engine/Editor to update the codes.
  11. /******************************************************************************/
  12. #define CC4_MTRL CC4('M','T','R','L')
  13. #define BUMP_DEFAULT 255 // normally this should be 128, but because 255 in BC7 gives better precision for RGB, so instead use 255 and always set Material.bump=0 when bump is not used, 128 would also disable the option of using BC1
  14. #define BUMP_NORMAL_SCALE (1.0f/64) // 0.015625, this value should be close to average 'Material.bump' which are 0.015, 0.03, 0.05 (remember that in Editor that value may be scaled)
  15. // #MaterialTextureChannelOrder
  16. #define NRMX_CHANNEL 0
  17. #define NRMY_CHANNEL 1
  18. #define SPEC_CHANNEL 2
  19. #define GLOW_CHANNEL 3
  20. /******************************************************************************/
  21. static Int Compare(C UniqueMultiMaterialKey &a, C UniqueMultiMaterialKey &b)
  22. {
  23. if(a.m[0]<b.m[0])return -1; if(a.m[0]>b.m[0])return +1;
  24. if(a.m[1]<b.m[1])return -1; if(a.m[1]>b.m[1])return +1;
  25. if(a.m[2]<b.m[2])return -1; if(a.m[2]>b.m[2])return +1;
  26. if(a.m[3]<b.m[3])return -1; if(a.m[3]>b.m[3])return +1;
  27. return 0;
  28. }
  29. /******************************************************************************/
  30. DEFINE_CACHE(Material, Materials, MaterialPtr, "Material");
  31. Material MaterialDefault,
  32. MaterialDefaultNoCull;
  33. const Material *MaterialLast,
  34. *MaterialLast4[4]; // can't merge with 'MaterialLast' because that sets 'h_Material' but this sets 'h_MultiMaterial'
  35. Enum *MaterialUserShader,
  36. *MaterialUserType;
  37. MaterialPtr MaterialNull;
  38. ThreadSafeMap<UniqueMultiMaterialKey, UniqueMultiMaterialData> UniqueMultiMaterialMap(Compare);
  39. /******************************************************************************/
  40. Material::Material()
  41. {
  42. color .set(1, 1, 1, 1);
  43. ambient.set(0, 0, 0);
  44. specular =0;
  45. sss =0;
  46. glow =0;
  47. rough =1;
  48. bump =0.03f;
  49. tex_scale=1.0f;
  50. det_scale=4;
  51. det_power=0.3f;
  52. reflect =0.2f;
  53. cull =true;
  54. technique =MTECH_DEFAULT;
  55. user_shader=0;
  56. user_type =0;
  57. clear();
  58. validate();
  59. }
  60. Material::~Material()
  61. {
  62. #if !SYNC_LOCK_SAFE // if 'SyncLock' is not safe then crash may occur when trying to lock, to prevent that, check if we have any elements (this means cache was already initialized)
  63. if(UniqueMultiMaterialMap.elms())
  64. #endif
  65. {
  66. UniqueMultiMaterialMap.lock (); REPA(UniqueMultiMaterialMap){C UniqueMultiMaterialKey &key=UniqueMultiMaterialMap.lockedKey(i); if(key.m[0]==this || key.m[1]==this || key.m[2]==this || key.m[3]==this)UniqueMultiMaterialMap.remove(i);}
  67. UniqueMultiMaterialMap.unlock();
  68. }
  69. }
  70. /******************************************************************************/
  71. static Bool HasAlphaTest(MATERIAL_TECHNIQUE technique)
  72. {
  73. switch(technique)
  74. {
  75. case MTECH_ALPHA_TEST :
  76. case MTECH_GRASS :
  77. case MTECH_LEAF :
  78. case MTECH_TEST_BLEND_LIGHT :
  79. case MTECH_TEST_BLEND_LIGHT_GRASS:
  80. case MTECH_TEST_BLEND_LIGHT_LEAF : return true;
  81. default: return false;
  82. }
  83. }
  84. Bool Material::hasAlpha ()C {return technique==MTECH_ALPHA_TEST || technique==MTECH_GRASS || technique==MTECH_LEAF || technique==MTECH_BLEND || technique==MTECH_BLEND_LIGHT || technique==MTECH_BLEND_LIGHT_GRASS || technique==MTECH_BLEND_LIGHT_LEAF || technique==MTECH_TEST_BLEND_LIGHT || technique==MTECH_TEST_BLEND_LIGHT_GRASS || technique==MTECH_TEST_BLEND_LIGHT_LEAF;}
  85. Bool Material::hasAlphaBlend ()C {return technique==MTECH_BLEND || technique==MTECH_BLEND_LIGHT || technique==MTECH_BLEND_LIGHT_GRASS || technique==MTECH_BLEND_LIGHT_LEAF || technique==MTECH_TEST_BLEND_LIGHT || technique==MTECH_TEST_BLEND_LIGHT_GRASS || technique==MTECH_TEST_BLEND_LIGHT_LEAF;}
  86. Bool Material::hasAlphaBlendLight()C {return technique==MTECH_BLEND_LIGHT || technique==MTECH_BLEND_LIGHT_GRASS || technique==MTECH_BLEND_LIGHT_LEAF || technique==MTECH_TEST_BLEND_LIGHT || technique==MTECH_TEST_BLEND_LIGHT_GRASS || technique==MTECH_TEST_BLEND_LIGHT_LEAF;}
  87. Bool Material::hasGrass ()C {return technique==MTECH_GRASS || technique==MTECH_BLEND_LIGHT_GRASS || technique==MTECH_TEST_BLEND_LIGHT_GRASS ;}
  88. Bool Material::hasLeaf ()C {return technique==MTECH_LEAF || technique==MTECH_BLEND_LIGHT_LEAF || technique==MTECH_TEST_BLEND_LIGHT_LEAF;}
  89. /******************************************************************************/
  90. Bool Material::wantTanBin()C
  91. {
  92. // #MaterialTextureChannelOrder
  93. return (base_0 && bump >EPS_MATERIAL_BUMP) // bump is in Base0 Alpha
  94. || (base_1 && rough >EPS_COL ) // normal is in Base1 XY
  95. || (detail_map && det_power>EPS_COL ); // normal detail is in DetailMap XY
  96. }
  97. /******************************************************************************/
  98. Material& Material::reset () {T=MaterialDefault; return validate();}
  99. Material& Material::validate()
  100. {
  101. if(this==MaterialLast )MaterialLast =null;
  102. REPA(MaterialLast4)if(this==MaterialLast4[i])MaterialLast4[i]=null;
  103. _has_alpha_test=HasAlphaTest(technique); // !! set this first, because codes below rely on this !!
  104. _depth_write =!(hasAlphaBlend() && !hasAlphaTest ());
  105. //_coverage = (hasAlphaTest () && !hasAlphaBlend());
  106. _alpha_factor.set(0, 0, 0, FltToByte(T.glow));
  107. // set multi
  108. {
  109. _multi.color =color ;
  110. _multi.tex_scale=tex_scale;
  111. _multi.det_scale=det_scale;
  112. _multi.bump=((base_0 && base_1) ? bump : 0);
  113. // normal map
  114. if(base_1)
  115. {
  116. // normal.xy
  117. // (tex.normal*2-1)*rough == tex.normal*(2*rough)-rough
  118. _multi.normal_mul.c[NRMX_CHANNEL]=_multi.normal_mul.c[NRMY_CHANNEL]=2*rough;
  119. _multi.normal_add.c[NRMX_CHANNEL]=_multi.normal_add.c[NRMY_CHANNEL]= -rough;
  120. // specular
  121. _multi.normal_mul.c[SPEC_CHANNEL]=specular;
  122. _multi.normal_add.c[SPEC_CHANNEL]=0;
  123. // alpha/glow
  124. _multi.normal_mul.c[GLOW_CHANNEL]=glow;
  125. _multi.normal_add.c[GLOW_CHANNEL]=0;
  126. }else
  127. {
  128. _multi.normal_mul=0;
  129. _multi.normal_add.c[NRMX_CHANNEL]=0;
  130. _multi.normal_add.c[NRMY_CHANNEL]=0;
  131. _multi.normal_add.c[SPEC_CHANNEL]=specular;
  132. _multi.normal_add.c[GLOW_CHANNEL]=glow;
  133. }
  134. if(detail_map)
  135. {
  136. // (tex-0.5)*det_power == tex*det_power - det_power*0.5
  137. _multi.det_mul=det_power;
  138. _multi.det_add=det_power*-0.5f;
  139. }else
  140. {
  141. _multi.det_mul=0;
  142. _multi.det_add=0;
  143. }
  144. _multi.macro=(macro_map!=null);
  145. _multi.reflect=(reflection_map ? reflect : 0);
  146. }
  147. return T;
  148. }
  149. /******************************************************************************
  150. Bool Material::convertAlphaTest(Bool blend)
  151. {
  152. if(blend)
  153. {
  154. if(technique==MTECH_ALPHA_TEST){technique=MTECH_TEST_BLEND_LIGHT ; color.w=1; validate(); return true;}
  155. if(technique==MTECH_GRASS ){technique=MTECH_TEST_BLEND_LIGHT_GRASS; color.w=1; validate(); return true;}
  156. if(technique==MTECH_LEAF ){technique=MTECH_TEST_BLEND_LIGHT_LEAF ; color.w=1; validate(); return true;}
  157. }else
  158. {
  159. if(technique==MTECH_TEST_BLEND_LIGHT ){technique=MTECH_ALPHA_TEST; color.w=0.5f; validate(); return true;}
  160. if(technique==MTECH_TEST_BLEND_LIGHT_GRASS){technique=MTECH_GRASS ; color.w=0.5f; validate(); return true;}
  161. if(technique==MTECH_TEST_BLEND_LIGHT_LEAF ){technique=MTECH_LEAF ; color.w=0.5f; validate(); return true;}
  162. }
  163. return false;
  164. }
  165. /******************************************************************************/
  166. void Material::setSolid()C
  167. {
  168. if(MaterialLast!=this)
  169. {
  170. MaterialLast =this;
  171. MaterialLast4[0]=null; // because they use the same shader images
  172. if(_alpha_factor.a)Renderer._has_glow=true;
  173. Sh.h_ImageCol[0]->set( base_0());
  174. Sh.h_ImageNrm[0]->set( base_1());
  175. Sh.h_ImageDet[0]->set( detail_map());
  176. Sh.h_ImageMac[0]->set( macro_map());
  177. Sh.h_ImageRfl[0]->set(reflection_map());
  178. Sh.h_ImageLum ->set( light_map());
  179. Sh.h_Material ->set<MaterialParams>(T);
  180. }
  181. }
  182. void Material::setAmbient()C
  183. {
  184. if(MaterialLast!=this)
  185. {
  186. MaterialLast=this;
  187. // textures needed for alpha-test
  188. Sh.h_ImageCol[0]->set(base_0());
  189. Sh.h_ImageNrm[0]->set(base_1());
  190. Sh.h_Material ->set<MaterialParams>(T); // params needed for alpha-test and ambient
  191. }
  192. }
  193. void Material::setBlend()C
  194. {
  195. if(MaterialLast!=this)
  196. {
  197. MaterialLast=this;
  198. D.alphaFactor(_alpha_factor); if(_alpha_factor.a)Renderer._has_glow=true;
  199. Sh.h_ImageCol[0]->set( base_0());
  200. Sh.h_ImageNrm[0]->set( base_1());
  201. Sh.h_ImageDet[0]->set( detail_map());
  202. Sh.h_ImageMac[0]->set( macro_map());
  203. Sh.h_ImageRfl[0]->set(reflection_map());
  204. Sh.h_ImageLum ->set( light_map());
  205. Sh.h_Material ->set<MaterialParams>(T);
  206. }
  207. }
  208. void Material::setBlendForce()C
  209. {
  210. if(_alpha_factor.a && !hasAlpha()) // if has glow in material settings and on texture channel, then it means we need to disable it for forced blend, which operates on alpha instead of glow
  211. {
  212. if(MaterialLast==this)MaterialLast=null;
  213. D.alphaFactor(TRANSPARENT);
  214. }else
  215. {
  216. if(MaterialLast==this)return;
  217. MaterialLast= this;
  218. D.alphaFactor(_alpha_factor); if(_alpha_factor.a)Renderer._has_glow=true;
  219. }
  220. Sh.h_ImageCol[0]->set( base_0());
  221. Sh.h_ImageNrm[0]->set( base_1());
  222. Sh.h_ImageDet[0]->set( detail_map());
  223. Sh.h_ImageMac[0]->set( macro_map());
  224. Sh.h_ImageRfl[0]->set(reflection_map());
  225. Sh.h_ImageLum ->set( light_map());
  226. Sh.h_Material ->set<MaterialParams>(T);
  227. }
  228. void Material::setOutline()C
  229. {
  230. if(MaterialLast!=this)
  231. {
  232. MaterialLast=this;
  233. Sh.h_ImageCol[0]->set(base_0());
  234. Sh.h_ImageNrm[0]->set(base_1());
  235. Renderer.material_color->set(color); // only Material Color is used for potential alpha-testing
  236. }
  237. }
  238. void Material::setBehind()C
  239. {
  240. if(MaterialLast!=this)
  241. {
  242. MaterialLast=this;
  243. Sh.h_ImageCol[0]->set(base_0());
  244. Sh.h_ImageNrm[0]->set(base_1());
  245. Renderer.material_color->set(color); // only Material Color is used
  246. }
  247. }
  248. void Material::setShadow()C
  249. {
  250. if(hasAlphaTest() && MaterialLast!=this) // this shader needs params/textures only for alpha test (if used)
  251. {
  252. MaterialLast=this;
  253. Sh.h_ImageCol[0]->set(base_0());
  254. Sh.h_ImageNrm[0]->set(base_1());
  255. Renderer.material_color->set(color); // only Material Color is used
  256. }
  257. }
  258. void Material::setMulti(Int i)C
  259. {
  260. RANGE_ASSERT(i, Sh.h_MultiMaterial);
  261. if(MaterialLast4[i]!=this)
  262. {
  263. MaterialLast4[i]=this;
  264. if(!i)MaterialLast =null; // because they use the same shader images
  265. if(_alpha_factor.a)Renderer._has_glow=true;
  266. Sh.h_ImageCol [i]->set( base_0());
  267. Sh.h_ImageNrm [i]->set( base_1());
  268. Sh.h_ImageDet [i]->set( detail_map());
  269. Sh.h_ImageMac [i]->set( macro_map());
  270. Sh.h_ImageRfl [i]->set(reflection_map());
  271. Sh.h_MultiMaterial[i]->set(_multi );
  272. }
  273. }
  274. void Material::setAuto()C
  275. {
  276. switch(Renderer())
  277. {
  278. case RM_EARLY_Z :
  279. case RM_PREPARE :
  280. case RM_SOLID :
  281. case RM_SOLID_M :
  282. case RM_SIMPLE : setSolid(); break;
  283. case RM_AMBIENT : setAmbient(); break;
  284. case RM_FUR :
  285. case RM_CLOUD :
  286. case RM_BLEND :
  287. case RM_PALETTE :
  288. case RM_PALETTE1:
  289. case RM_OVERLAY : setBlend(); break;
  290. case RM_BEHIND : setBehind(); break;
  291. case RM_OUTLINE : setOutline(); break;
  292. case RM_SHADOW : setShadow(); break;
  293. }
  294. }
  295. /******************************************************************************/
  296. void Material::_adjustParams(UInt old_base_tex, UInt new_base_tex)
  297. {
  298. UInt changed=(old_base_tex^new_base_tex);
  299. if(changed&BT_BUMP)
  300. {
  301. if(!(new_base_tex&BT_BUMP))bump=0;else
  302. if(bump<=EPS_MATERIAL_BUMP)bump=MaterialDefault.bump;
  303. }
  304. if(changed&(BT_BUMP|BT_NORMAL))
  305. {
  306. if(!(new_base_tex&BT_BUMP) && !(new_base_tex&BT_NORMAL))rough=0;else
  307. if( rough<=EPS_COL)rough=MaterialDefault.rough;
  308. }
  309. if(changed&BT_SPECULAR)
  310. if((new_base_tex&BT_SPECULAR) && specular<=EPS_COL)specular=1;
  311. if(changed&BT_GLOW)
  312. if((new_base_tex&BT_GLOW) && glow<=EPS_COL)glow=1;
  313. if(changed&BT_ALPHA)
  314. {
  315. if(new_base_tex&BT_ALPHA)
  316. {
  317. if(!hasAlphaBlend() && color.w>=1-EPS_COL)color.w=0.5f;
  318. if(!hasAlpha () )technique=MTECH_ALPHA_TEST;
  319. }else
  320. {
  321. if(hasAlpha())technique=MTECH_DEFAULT; // disable alpha technique if alpha map is not available
  322. }
  323. }
  324. validate();
  325. }
  326. /******************************************************************************/
  327. Bool Material::saveData(File &f, CChar *path)C
  328. {
  329. f.putMulti(Byte(9), cull, Byte(technique))<<SCAST(C MaterialParams, T); // version
  330. // textures
  331. f.putStr( base_0.name(path)); // !! can't use 'id' because textures are stored in "Tex/" folder, so there's no point in using 'putAsset' !!
  332. f.putStr( base_1.name(path)); // !! can't use 'id' because textures are stored in "Tex/" folder, so there's no point in using 'putAsset' !!
  333. f.putStr( detail_map.name(path)); // !! can't use 'id' because textures are stored in "Tex/" folder, so there's no point in using 'putAsset' !!
  334. f.putStr( macro_map.name(path)); // !! can't use 'id' because textures are stored in "Tex/" folder, so there's no point in using 'putAsset' !!
  335. f.putStr(reflection_map.name(path)); // !! can't use 'id' because textures are stored in "Tex/" folder, so there's no point in using 'putAsset' !!
  336. f.putStr( light_map.name(path)); // !! can't use 'id' because textures are stored in "Tex/" folder, so there's no point in using 'putAsset' !!
  337. // user shader
  338. f.putStr(user_shader_name);
  339. f.putStr(user_type_name );
  340. return f.ok();
  341. }
  342. Bool Material::loadData(File &f, CChar *path)
  343. {
  344. MaterialParams &mp=T; Char temp[MAX_LONG_PATH];
  345. switch(f.decUIntV())
  346. {
  347. case 9:
  348. {
  349. f.getMulti(cull, technique)>>mp;
  350. f.getStr(temp ); base_0.require(temp, path);
  351. f.getStr(temp ); base_1.require(temp, path);
  352. f.getStr(temp ); detail_map.require(temp, path);
  353. f.getStr(temp ); macro_map.require(temp, path);
  354. f.getStr(temp ); reflection_map.require(temp, path);
  355. f.getStr(temp ); light_map.require(temp, path);
  356. f.getStr(user_shader_name); user_shader=(MaterialUserShader ? MaterialUserShader->find(user_shader_name) : 0);
  357. f.getStr(user_type_name ); user_type =(MaterialUserType ? MaterialUserType ->find(user_type_name ) : 0);
  358. }break;
  359. case 8:
  360. {
  361. f.getMulti(cull, technique)>>mp;
  362. f._getStr2(temp ); base_0.require(temp, path);
  363. f._getStr2(temp ); base_1.require(temp, path);
  364. f._getStr2(temp ); detail_map.require(temp, path);
  365. f._getStr2(temp ); macro_map.require(temp, path);
  366. f._getStr2(temp ); reflection_map.require(temp, path);
  367. f._getStr2(temp ); light_map.require(temp, path);
  368. f._getStr2(user_shader_name); user_shader=(MaterialUserShader ? MaterialUserShader->find(user_shader_name) : 0);
  369. f._getStr2(user_type_name ); user_type =(MaterialUserType ? MaterialUserType ->find(user_type_name ) : 0);
  370. }break;
  371. case 7:
  372. {
  373. f>>mp>>cull>>technique;
  374. f._getStr(temp ); base_0.require(temp, path);
  375. f._getStr(temp ); base_1.require(temp, path);
  376. f._getStr(temp ); detail_map.require(temp, path);
  377. f._getStr(temp ); macro_map.require(temp, path);
  378. f._getStr(temp ); reflection_map.require(temp, path);
  379. f._getStr(temp ); light_map.require(temp, path);
  380. f._getStr(user_shader_name); user_shader=(MaterialUserShader ? MaterialUserShader->find(user_shader_name) : 0);
  381. f._getStr(user_type_name ); user_type =(MaterialUserType ? MaterialUserType ->find(user_type_name ) : 0);
  382. }break;
  383. case 6:
  384. {
  385. f>>mp>>cull>>technique; user_type=0; user_type_name.clear();
  386. f._getStr(temp); base_0.require(temp, path);
  387. f._getStr(temp); base_1.require(temp, path);
  388. f._getStr(temp); detail_map.require(temp, path);
  389. f._getStr(temp); macro_map.require(temp, path);
  390. f._getStr(temp); reflection_map.require(temp, path);
  391. f._getStr(temp); light_map.require(temp, path);
  392. user_shader_name=f._getStr8(); user_shader=(MaterialUserShader ? MaterialUserShader->find(user_shader_name) : 0);
  393. }break;
  394. case 5:
  395. {
  396. f>>mp>>cull>>technique; user_type=0; user_type_name.clear();
  397. f._getStr(temp); base_0.require(temp, path);
  398. f._getStr(temp); base_1.require(temp, path);
  399. f._getStr(temp); detail_map.require(temp, path);
  400. f._getStr(temp); reflection_map.require(temp, path);
  401. f._getStr(temp); light_map.require(temp, path);
  402. macro_map=null;
  403. user_shader_name=f._getStr8(); user_shader=(MaterialUserShader ? MaterialUserShader->find(user_shader_name) : 0);
  404. }break;
  405. case 4:
  406. {
  407. f>>mp>>cull>>technique; user_shader=user_type=0; user_shader_name.clear(); user_type_name.clear();
  408. f._getStr(temp); base_0.require(temp, path);
  409. f._getStr(temp); base_1.require(temp, path);
  410. f._getStr(temp); detail_map.require(temp, path);
  411. f._getStr(temp); reflection_map.require(temp, path);
  412. f._getStr(temp); light_map.require(temp, path);
  413. macro_map=null;
  414. }break;
  415. case 3:
  416. {
  417. f>>color>>ambient>>specular>>sss>>glow>>rough>>bump>>det_scale>>det_power>>reflect>>cull>>technique; tex_scale=1; user_shader=user_type=0; user_shader_name.clear(); user_type_name.clear();
  418. base_0.require(f._getStr8(), path);
  419. base_1.require(f._getStr8(), path);
  420. detail_map.require(f._getStr8(), path);
  421. reflection_map.require(f._getStr8(), path);
  422. light_map.require(f._getStr8(), path);
  423. macro_map=null;
  424. }break;
  425. case 2:
  426. {
  427. f.skip(1);
  428. f>>color>>specular>>sss>>glow>>rough>>bump>>det_scale>>det_power>>reflect>>cull>>technique; ambient=0; tex_scale=1; user_shader=user_type=0; user_shader_name.clear(); user_type_name.clear();
  429. if(technique==MTECH_FUR){det_power=color.w; color.w=1;}
  430. base_0.require(f._getStr8(), path);
  431. base_1.require(f._getStr8(), path);
  432. detail_map.require(f._getStr8(), path);
  433. reflection_map.require(f._getStr8(), path);
  434. light_map=null;
  435. macro_map=null;
  436. }break;
  437. case 1:
  438. {
  439. f.skip(1);
  440. f>>color>>specular>>glow>>rough>>bump>>det_scale>>det_power>>reflect>>cull>>technique; sss=0; ambient=0; tex_scale=1; user_shader=user_type=0; user_shader_name.clear(); user_type_name.clear();
  441. if(technique==MTECH_FUR){det_power=color.w; color.w=1;}
  442. base_0.require(f._getStr8(), path);
  443. base_1.require(f._getStr8(), path);
  444. detail_map.require(f._getStr8(), path);
  445. reflection_map.require(f._getStr8(), path);
  446. light_map=null;
  447. macro_map=null;
  448. }break;
  449. case 0:
  450. {
  451. f.skip(1);
  452. f>>color>>specular>>glow>>rough>>bump>>det_scale>>det_power>>reflect>>cull; sss=0; ambient=0; tex_scale=1; user_shader=user_type=0; user_shader_name.clear(); user_type_name.clear(); user_shader_name.clear();
  453. switch(f.getByte())
  454. {
  455. default: technique=MTECH_DEFAULT ; break;
  456. case 1 : technique=MTECH_ALPHA_TEST; break;
  457. case 4 : technique=MTECH_FUR ; break;
  458. case 5 : technique=MTECH_GRASS ; break;
  459. }
  460. if(technique==MTECH_FUR){det_power=color.w; color.w=1;}
  461. Char8 tex[80];
  462. f>>tex; base_0.require(tex, path);
  463. f>>tex; base_1.require(tex, path);
  464. f>>tex; detail_map.require(tex, path);
  465. f>>tex; reflection_map.require(tex, path);
  466. light_map=null;
  467. macro_map=null;
  468. }break;
  469. default: goto error;
  470. }
  471. if(f.ok()){validate(); return true;}
  472. error:
  473. reset(); return false;
  474. }
  475. /******************************************************************************/
  476. Bool Material::save(File &f, CChar *path)C
  477. {
  478. f.putUInt(CC4_MTRL);
  479. return saveData(f, path);
  480. }
  481. Bool Material::load(File &f, CChar *path)
  482. {
  483. if(f.getUInt()==CC4_MTRL)return loadData(f, path);
  484. reset(); return false;
  485. }
  486. Bool Material::save(C Str &name)C
  487. {
  488. File f; if(f.writeTry(name)){if(save(f, _GetPath(name)) && f.flush())return true; f.del(); FDelFile(name);}
  489. return false;
  490. }
  491. Bool Material::load(C Str &name)
  492. {
  493. File f; if(f.readTry(name))return load(f, _GetPath(name));
  494. reset(); return false;
  495. }
  496. /******************************************************************************/
  497. void MaterialClear() // must be called: after changing 'Renderer.mode', after changing textures, after changing 'D.alphaFactor'
  498. {
  499. MaterialLast =null;
  500. REPAO(MaterialLast4)=null;
  501. }
  502. /******************************************************************************/
  503. UInt CreateBaseTextures(Image &base_0, Image &base_1, C Image &col, C Image &alpha, C Image &bump, C Image &normal, C Image &specular, C Image &glow, Bool resize_to_pow2, Bool flip_normal_y, FILTER_TYPE filter)
  504. {
  505. UInt ret=0;
  506. Image dest_0, dest_1;
  507. {
  508. Image col_temp; C Image * col_src=& col; if( col_src->compressed())if( col_src->copyTry( col_temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1)) col_src=& col_temp;else goto error;
  509. Image alpha_temp; C Image * alpha_src=& alpha; if( alpha_src->compressed())if( alpha_src->copyTry( alpha_temp, -1, -1, -1, IMAGE_L8A8 , IMAGE_SOFT, 1)) alpha_src=& alpha_temp;else goto error;
  510. Image bump_temp; C Image * bump_src=& bump; if( bump_src->compressed())if( bump_src->copyTry( bump_temp, -1, -1, -1, IMAGE_L8 , IMAGE_SOFT, 1)) bump_src=& bump_temp;else goto error;
  511. Image normal_temp; C Image * normal_src=& normal; if( normal_src->compressed())if( normal_src->copyTry( normal_temp, -1, -1, -1, IMAGE_R8G8 , IMAGE_SOFT, 1)) normal_src=& normal_temp;else goto error;
  512. Image specular_temp; C Image *specular_src=&specular; if(specular_src->compressed())if(specular_src->copyTry(specular_temp, -1, -1, -1, IMAGE_L8 , IMAGE_SOFT, 1))specular_src=&specular_temp;else goto error;
  513. Image glow_temp; C Image * glow_src=& glow; if( glow_src->compressed())if( glow_src->copyTry( glow_temp, -1, -1, -1, IMAGE_L8A8 , IMAGE_SOFT, 1)) glow_src=& glow_temp;else goto error;
  514. // set alpha
  515. // 1. Glow Map shouldn't be stored in #1 texture because of difficulties when drawing multi-materials
  516. // 2. Alpha Map is incompatible with Glow Map
  517. if(!alpha_src->is() && ImageTI[col_src->type()].a) // if there's no alpha map but there is alpha in color map
  518. {
  519. Byte min_alpha=255;
  520. alpha_src=&alpha_temp.create(col_src->w(), col_src->h(), 1, IMAGE_A8, IMAGE_SOFT, 1);
  521. if(col_src->lockRead())
  522. {
  523. REPD(y, col_src->h())
  524. REPD(x, col_src->w())
  525. {
  526. Byte a=col_src->color(x, y).a;
  527. alpha_temp.pixel(x, y, a);
  528. MIN(min_alpha, a);
  529. }
  530. col_src->unlock();
  531. }
  532. if(min_alpha>=253)alpha_temp.del(); // alpha channel in color map is almost fully white
  533. }else
  534. if(alpha_src->is() && ImageTI[alpha_src->type()].channels>1 && ImageTI[alpha_src->type()].a) // if alpha has both RGB and Alpha channels, then check which one to use
  535. if(alpha_src->lockRead())
  536. {
  537. Byte min_alpha=255, min_lum=255;
  538. REPD(y, alpha_src->h())
  539. REPD(x, alpha_src->w())
  540. {
  541. Color c=alpha_src->color(x, y);
  542. MIN(min_alpha, c.a );
  543. MIN(min_lum , c.lum());
  544. }
  545. alpha_src->unlock();
  546. if(min_alpha>=253 && min_lum<253)if(alpha_src->copyTry(alpha_temp, -1, -1, -1, IMAGE_L8, IMAGE_SOFT, 1))alpha_src=&alpha_temp;else goto error; // alpha channel is almost fully white -> use luminance as alpha
  547. }
  548. // alpha is incompatible with glow map
  549. if(alpha_src->is())glow_src=&glow_temp.del(); // if 'alpha' is available then delete 'glow' (alpha has higher priority)
  550. // if we're using two textures
  551. Bool tex2=(bump_src->is() || normal_src->is() || specular_src->is() || glow_src->is());
  552. // set what textures do we have (set this before 'normal' is generated from 'bump')
  553. if( col_src->is())ret|=BT_COLOR ;
  554. if( alpha_src->is())ret|=BT_ALPHA ;
  555. if( bump_src->is())ret|=BT_BUMP ;
  556. if( normal_src->is())ret|=BT_NORMAL ;
  557. if(specular_src->is())ret|=BT_SPECULAR;
  558. if( glow_src->is())ret|=BT_GLOW ;
  559. // generate textures, below operate on separate set of temporary images, in case one source image is used for both texture, but they will be used at different sizes (to avoid double stretching and loss of quality)
  560. // 1st texture
  561. if(tex2) // put bump in W channel
  562. {
  563. Int w=Max(col_src->w(), bump_src->w()),
  564. h=Max(col_src->h(), bump_src->h()); if(resize_to_pow2){w=NearestPow2(w); h=NearestPow2(h);}
  565. Image col_temp; C Image *cs= col_src; if(cs->is() && (cs->w()!=w || cs->h()!=h))if(cs->copyTry( col_temp, w, h, -1, -1, IMAGE_SOFT, 1, filter, false))cs=& col_temp;else goto error;
  566. Image bump_temp; C Image *bs=bump_src; if(bs->is() && (bs->w()!=w || bs->h()!=h))if(bs->copyTry(bump_temp, w, h, -1, -1, IMAGE_SOFT, 1, filter, false))bs=&bump_temp;else goto error;
  567. dest_0.createSoftTry(w, h, 1, IMAGE_R8G8B8A8);
  568. if(!cs->is() || cs->lockRead())
  569. {
  570. if(!bs->is() || bs->lockRead())
  571. {
  572. REPD(y, dest_0.h())
  573. REPD(x, dest_0.w()){Color c=(cs->is() ? cs->color(x, y) : WHITE); c.a=(bs->is() ? bs->color(x, y).lum() : BUMP_DEFAULT); dest_0.color(x, y, c);}
  574. bs->unlock();
  575. }
  576. cs->unlock();
  577. }
  578. }else // put alpha in W channel
  579. {
  580. Int w=Max(col_src->w(), alpha_src->w()),
  581. h=Max(col_src->h(), alpha_src->h()); if(resize_to_pow2){w=NearestPow2(w); h=NearestPow2(h);}
  582. Image col_temp; C Image *cs= col_src; if(cs->is() && (cs->w()!=w || cs->h()!=h))if(cs->copyTry( col_temp, w, h, -1, -1, IMAGE_SOFT, 1, filter, false))cs=& col_temp;else goto error;
  583. Image alpha_temp; C Image *as=alpha_src; if(as->is() && (as->w()!=w || as->h()!=h))if(as->copyTry(alpha_temp, w, h, -1, -1, IMAGE_SOFT, 1, filter, false))as=&alpha_temp;else goto error;
  584. dest_0.createSoftTry(w, h, 1, IMAGE_R8G8B8A8);
  585. if(!cs->is() || cs->lockRead())
  586. {
  587. if(!as->is() || as->lockRead())
  588. {
  589. Int alpha_component=(ImageTI[as->type()].a ? 3 : 0); // use Alpha or Red in case src is R8
  590. REPD(y, dest_0.h())
  591. REPD(x, dest_0.w()){Color c=(cs->is() ? cs->color(x, y) : WHITE); c.a=(as->is() ? as->color(x, y).c[alpha_component] : 255); dest_0.color(x, y, c);} // full alpha
  592. as->unlock();
  593. }
  594. cs->unlock();
  595. }
  596. }
  597. // 2nd texture
  598. if(tex2)
  599. {
  600. Int w=Max(normal_src->w(), specular_src->w(), alpha_src->w(), glow_src->w()),
  601. h=Max(normal_src->h(), specular_src->h(), alpha_src->h(), glow_src->h()); if(resize_to_pow2){w=NearestPow2(w); h=NearestPow2(h);}
  602. C Image *bump=null;
  603. if( bump_src->is() && !normal_src->is() )bump= bump_src;else // if bump available and normal not, then create normal from bump
  604. if(normal_src->is() && normal_src->monochromatic())bump=normal_src; // if normal is provided as monochromatic, then treat it as bump and convert to normal
  605. if(bump) // create normal from bump
  606. {
  607. MAX(w, bump->w());
  608. MAX(h, bump->h()); if(resize_to_pow2){w=NearestPow2(w); h=NearestPow2(h);}
  609. if(bump->w()!=w || bump->h()!=h)if(bump->copyTry(normal_temp, w, h, -1, -1, IMAGE_SOFT, 1, filter, false))bump=&normal_temp;else goto error;
  610. bump->bumpToNormal(normal_temp, AvgF(w, h)*BUMP_NORMAL_SCALE); normal_src=&normal_temp;
  611. flip_normal_y=false; // no need to flip since normal map generated from bump is always correct
  612. }
  613. dest_1.createSoftTry(w, h, 1, IMAGE_R8G8B8A8);
  614. Image normal_temp; C Image *ns= normal_src; if(ns->is() && (ns->w()!=w || ns->h()!=h))if(ns->copyTry( normal_temp, w, h, -1, -1, IMAGE_SOFT, 1, filter, false))ns=& normal_temp;else goto error;
  615. Image specular_temp; C Image *ss=specular_src; if(ss->is() && (ss->w()!=w || ss->h()!=h))if(ss->copyTry(specular_temp, w, h, -1, -1, IMAGE_SOFT, 1, filter, false))ss=&specular_temp;else goto error;
  616. Image alpha_temp; C Image *as= alpha_src; if(as->is() && (as->w()!=w || as->h()!=h))if(as->copyTry( alpha_temp, w, h, -1, -1, IMAGE_SOFT, 1, filter, false))as=& alpha_temp;else goto error;
  617. Image glow_temp; C Image *gs= glow_src; if(gs->is() && (gs->w()!=w || gs->h()!=h))if(gs->copyTry( glow_temp, w, h, -1, -1, IMAGE_SOFT, 1, filter, false))gs=& glow_temp;else goto error;
  618. if(!ns->is() || ns->lockRead())
  619. {
  620. if(!ss->is() || ss->lockRead())
  621. {
  622. if(!as->is() || as->lockRead())
  623. {
  624. if(!gs->is() || gs->lockRead())
  625. {
  626. Int alpha_component=(ImageTI[as->type()].a ? 3 : 0); // use Alpha or Red in case src is R8
  627. REPD(y, dest_1.h())
  628. REPD(x, dest_1.w())
  629. {
  630. Color nrm =(ns->is() ? ns->color(x, y) : Color(128, 128, 255, 0)); if(flip_normal_y)nrm.g=255-nrm.g;
  631. Byte spec=(ss->is() ? ss->color(x, y).lum() : 255);
  632. Byte alpha_glow;
  633. if(as->is()) alpha_glow=as->color(x, y).c[alpha_component];else
  634. if(gs->is()){Color c=gs->color(x, y); alpha_glow=DivRound(c.lum()*c.a, 255);}else
  635. alpha_glow=255;
  636. Color c;
  637. c.c[NRMX_CHANNEL]=nrm.r;
  638. c.c[NRMY_CHANNEL]=nrm.g;
  639. c.c[SPEC_CHANNEL]=spec;
  640. c.c[GLOW_CHANNEL]=alpha_glow;
  641. dest_1.color(x, y, c);
  642. }
  643. gs->unlock();
  644. }
  645. as->unlock();
  646. }
  647. ss->unlock();
  648. }
  649. ns->unlock();
  650. }
  651. }
  652. }
  653. error:
  654. Swap(dest_0, base_0);
  655. Swap(dest_1, base_1);
  656. return ret;
  657. }
  658. void CreateDetailTexture(Image &detail, C Image &col, C Image &bump, C Image &normal, Bool resize_to_pow2, Bool flip_normal_y, FILTER_TYPE filter)
  659. {
  660. Image dest;
  661. {
  662. Image col_temp; C Image * col_src=& col; if( col_src->compressed())if( col_src->copyTry( col_temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1)) col_src=& col_temp;else goto error;
  663. Image bump_temp; C Image * bump_src=& bump; if( bump_src->compressed())if( bump_src->copyTry( bump_temp, -1, -1, -1, IMAGE_L8 , IMAGE_SOFT, 1)) bump_src=& bump_temp;else goto error;
  664. Image normal_temp; C Image *normal_src=&normal; if(normal_src->compressed())if(normal_src->copyTry(normal_temp, -1, -1, -1, IMAGE_R8G8 , IMAGE_SOFT, 1))normal_src=&normal_temp;else goto error;
  665. Int w=Max(col_src->w(), bump_src->w(), normal_src->w()),
  666. h=Max(col_src->h(), bump_src->h(), normal_src->h()); if(resize_to_pow2){w=NearestPow2(w); h=NearestPow2(h);}
  667. C Image *cs= col_src; if(cs->is() && (cs->w()!=w || cs->h()!=h))if(cs->copyTry( col_temp, w, h, -1, -1, IMAGE_SOFT, 1, filter, false))cs=& col_temp;else goto error;
  668. C Image *bs= bump_src; if(bs->is() && (bs->w()!=w || bs->h()!=h))if(bs->copyTry( bump_temp, w, h, -1, -1, IMAGE_SOFT, 1, filter, false))bs=& bump_temp;else goto error;
  669. C Image *ns=normal_src; if(ns->is() && (ns->w()!=w || ns->h()!=h))if(ns->copyTry(normal_temp, w, h, -1, -1, IMAGE_SOFT, 1, filter, false))ns=&normal_temp;else goto error;
  670. if(bs->is() && !ns->is()){bs->bumpToNormal(normal_temp, AvgF(w, h)*BUMP_NORMAL_SCALE); ns=&normal_temp; flip_normal_y=false;} // create normal from bump map, no need to flip since normal map generated from bump is always correct
  671. dest.createSoftTry(w, h, 1, IMAGE_R8G8B8A8);
  672. REPD(y, dest.h())
  673. REPD(x, dest.w())
  674. {
  675. Color nrm =(ns->is() ? ns->color(x, y) : Color(128, 128, 255, 0)); if(flip_normal_y)nrm.g=255-nrm.g;
  676. Byte col =(cs->is() ? cs->color(x, y).lum() : 128);
  677. Byte bump=(bs->is() ? bs->color(x, y).lum() : BUMP_DEFAULT);
  678. dest.color(x, y, Color(nrm.r, nrm.g, col, bump)); // #MaterialTextureChannelOrder
  679. }
  680. }
  681. error:
  682. Swap(dest, detail);
  683. }
  684. Bool CreateBumpFromColor(Image &bump, C Image &col, Flt min_blur_range, Flt max_blur_range, Threads *threads)
  685. {
  686. Image col_temp; C Image *col_src=&col; if(col_src->compressed())if(col_src->copyTry(col_temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))col_src=&col_temp;else goto error;
  687. {
  688. Image bump_temp; if(bump_temp.createSoftTry(col.w(), col.h(), 1, IMAGE_F32)) // operate on temporary in case "&bump==&col", create as high precision to get good quality for blur/normalize
  689. {
  690. if(col_src->lockRead())
  691. {
  692. REPD(y, bump_temp.h())
  693. REPD(x, bump_temp.w())bump_temp.pixF(x, y)=col_src->colorF(x, y).xyz.max();
  694. col_src->unlock();
  695. if(min_blur_range<0)min_blur_range=0; // auto
  696. if(max_blur_range<0)max_blur_range=3; // auto
  697. Bool first=true;
  698. Flt power=1;
  699. Image bump_step;
  700. for(Flt blur=max_blur_range; ; ) // start with max range, because it's the most important, so we want it to be precise, and then we will go with half steps down
  701. {
  702. bump_temp.blur(first ? bump : bump_step, blur, false, threads); // always set the first blur into 'bump' to set it as base, or in case we finish after one step
  703. if(!first)
  704. {
  705. REPD(y, bump.h())
  706. REPD(x, bump.w())bump.pixF(x, y)+=bump_step.pixF(x, y)*power;
  707. }
  708. if(blur<=min_blur_range)break;
  709. first =false;
  710. blur *=0.5f; if(blur<1)blur=0; // if we reach below 1 blur, then go straight to 0 to avoid doing 0.5, 0.25, 0.125, ..
  711. power*=0.5f;
  712. }
  713. bump.normalize();
  714. return true;
  715. }
  716. }
  717. }
  718. error:
  719. bump.del(); return false;
  720. }
  721. /******************************************************************************/
  722. static inline Flt LightSpecular(C Vec &nrm, C Vec &light_dir, C Vec &eye_dir, Flt power=64)
  723. {
  724. #if 1 // blinn
  725. return Pow(Sat(Dot(nrm, !(light_dir+eye_dir))), power);
  726. #else // phong
  727. Vec reflection=!(nrm*(2*Dot(nrm, light_dir)) - light_dir);
  728. return Pow(Sat(Dot(reflection, eye_dir)), power);
  729. #endif
  730. }
  731. Bool MergeBaseTextures(Image &base_0, C Material &material, Int image_type, Int max_image_size, C Vec *light_dir, Flt light_power, Flt spec_mul, FILTER_TYPE filter)
  732. {
  733. if(material.base_0 && material.base_0->is()
  734. && material.base_1 && material.base_1->is()) // if have both textures
  735. {
  736. // dimensions
  737. VecI2 size=Max(material.base_0->size(), material.base_1->size());
  738. if(max_image_size>0)
  739. {
  740. MIN(size.x, max_image_size);
  741. MIN(size.y, max_image_size);
  742. }
  743. Image color; // operate on temp variable in case 'base_0' argument is set to other images used in this func
  744. if(material.base_0->copyTry(color, size.x, size.y, 1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1, filter, false)) // create new color map
  745. {
  746. Image b1; // 'base_1' resized to 'color' resolution
  747. MAX(light_power, 0);
  748. spec_mul*=material.specular*light_power/255.0f;
  749. Flt nrm_mul =material.rough /127.0f,
  750. glow_mul =material.glow*(2*1.75f/255.0f), // *2 because shaders use this multiplier, *1.75 because shaders iterate over few pixels and take the max out of them (this is just approximation)
  751. glow_blur=0.07f;
  752. Bool has_alpha=material.hasAlpha(),
  753. has_nrm =( light_dir && material.rough *light_power>0.01f),
  754. has_spec =( light_dir && material.specular*light_power>0.01f),
  755. has_glow =(!has_alpha && material.glow >0.01f);
  756. if( (has_alpha || has_nrm || has_spec || has_glow) && material.base_1->copyTry(b1, color.w(), color.h(), 1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1, filter, false))
  757. {
  758. // setup alpha
  759. if(has_alpha)
  760. REPD(y, color.h())
  761. REPD(x, color.w()){Color c=color.color(x, y); c.a=b1.color(x, y).c[GLOW_CHANNEL]; color.color(x, y, c);}
  762. // setup glow (before baking normals)
  763. Image glow; if(has_glow && glow.createSoftTry(color.w(), color.h(), 1, IMAGE_F32_3)) // use Vec because we're storing glow with multiplier
  764. {
  765. REPD(y, color.h())
  766. REPD(x, color.w())
  767. {
  768. Vec4 c=color.colorF(x, y) ; // RGB
  769. Byte g=b1 .color (x, y).c[GLOW_CHANNEL]; // glow
  770. c.xyz*=g*glow_mul;
  771. glow.colorF(x, y, c);
  772. }
  773. glow.blur(RoundPos(glow.size().avgF()*glow_blur), false);
  774. }
  775. // bake normal map
  776. if(has_nrm || has_spec)
  777. {
  778. Flt light=Sat(light_dir->z)*light_power, // light intensity at flat normal without ambient
  779. ambient=1-light; // setup ambient so light intensity at flat normal is 1
  780. REPD(y, color.h())
  781. REPD(x, color.w())
  782. {
  783. // I'm assuming that the texture plane is XY plane with Z=0, and facing us (towards -Z) just like browsing image in some viewer
  784. Color col=color.color(x, y),
  785. nrm=b1 .color(x, y);
  786. Vec n;
  787. n.x=(nrm.c[NRMX_CHANNEL]-128)*nrm_mul; // NrmX increases towards right
  788. n.y=(128-nrm.c[NRMY_CHANNEL])*nrm_mul; // NrmY increases towards down
  789. n.z=-CalcZ(n.xy);
  790. n.normalize();
  791. if(has_nrm)
  792. {
  793. Flt d=Sat(-Dot(n, *light_dir)), l=ambient + light_power*d;
  794. col=ColorBrightness(col, l);
  795. }
  796. if(has_spec)if(Byte s=nrm.c[SPEC_CHANNEL])
  797. {
  798. Flt spec=LightSpecular(-n, *light_dir, Vec(0, 0, 1))*spec_mul;
  799. Color cs=ColorBrightness(s*spec); cs.a=0;
  800. col=ColorAdd(col, cs);
  801. }
  802. color.color(x, y, col);
  803. }
  804. }
  805. // apply glow map (after baking normal)
  806. if(glow.is())
  807. REPD(y, color.h())
  808. REPD(x, color.w())
  809. {
  810. Color c=color.color(x, y),
  811. g=glow .color(x, y); g.a=0;
  812. color.color(x, y, ColorAdd(c, g));
  813. }
  814. }else has_alpha=false; // disable alpha if failed to copy image
  815. if(!has_alpha) // set full alpha channel
  816. REPD(y, color.h())
  817. REPD(x, color.w()){Color c=color.color(x, y); c.a=255; color.color(x, y, c);}
  818. // image type
  819. if(image_type<=0)
  820. {
  821. image_type=material.base_0->type();
  822. if(has_alpha)image_type=ImageTypeIncludeAlpha(IMAGE_TYPE(image_type)); // convert image type to one with alpha channel
  823. else image_type=ImageTypeExcludeAlpha(IMAGE_TYPE(image_type)); // convert image type to one without alpha channel
  824. }
  825. if(image_type==IMAGE_PVRTC1_2 || image_type==IMAGE_PVRTC1_4)size=NearestPow2(size.avgI()); // PVRTC1 must be square and pow2
  826. // final copy
  827. if(color.copyTry(color, size.x, size.y, 1, image_type, material.base_0->mode(), (material.base_0->mipMaps()>1) ? 0 : 1, filter, false))
  828. {
  829. Swap(base_0, color);
  830. return true;
  831. }
  832. }
  833. }
  834. return false;
  835. }
  836. /******************************************************************************/
  837. static Bool CanBeRemoved(C Material &material) {return material.canBeRemoved();} // Renderer Instancing doesn't use incRef/decRef for more performance, so we need to do additional checking for materials if they can be removed from cache, by checking if they're not assigned to any instance
  838. void ShutMaterial() {Materials.del();}
  839. void InitMaterial()
  840. {
  841. MaterialDefault.cull=true;
  842. MaterialDefault.validate();
  843. MaterialDefaultNoCull=MaterialDefault;
  844. MaterialDefaultNoCull.cull=false;
  845. MaterialUserShader=Enums.get("Enum/material_user_shader.enum");
  846. MaterialUserType =Enums.get("Enum/material_user_type.enum" );
  847. Materials.canBeRemoved(CanBeRemoved);
  848. }
  849. /******************************************************************************/
  850. }
  851. /******************************************************************************/