Publish.cpp 63 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. /******************************************************************************/
  4. /******************************************************************************
  5. Publishing is best done in separate state so:
  6. -we don't allow editing project elements during paking
  7. -we don't allow receiving project elements from the server during paking
  8. Windows:
  9. Engine.pak
  10. Project.pak
  11. EngineEmbed.pak (for engine data embedded into EXE, used when 'appEmbedEngineData')
  12. App.pak (for app specific data embedded into EXE, used always )
  13. Android:
  14. Engine.pak
  15. Project.pak (will contain only app specific data when "!appPublishProjData")
  16. /******************************************************************************/
  17. PublishClass Publish;
  18. State StatePublish(UpdatePublish, DrawPublish, InitPublish, ShutPublish);
  19. Memb<PakFileData> PublishFiles;
  20. Memc<ImageGenerate> PublishGenerate;
  21. Memc<ImageConvert> PublishConvert;
  22. Memc<Mems<byte> > PublishFileData; // for file data allocated dynamically
  23. SyncLock PublishLock;
  24. bool PublishOk, PublishNoCompile, PublishOpenIDE, PublishDataAsPak, PublishDataOnly, PublishEsProj;
  25. int PublishAreasLeft, PublishPVRTCUse;
  26. PUBLISH_STAGE PublishStage;
  27. Str PublishPath,
  28. PublishBinPath, // "Bin/" path (must include tail slash)
  29. PublishProjectDataPath, // "Project.pak" path
  30. PublishExePath,
  31. PublishErrorMessage;
  32. Button PublishSkipOptimize;
  33. ComboBox PublishPVRTCQuality;
  34. Text PublishPVRTCQualityText;
  35. TextWhite PublishPVRTCQualityTextStyle;
  36. Edit::EXE_TYPE PublishExeType =Edit::EXE_EXE;
  37. Edit::BUILD_MODE PublishBuildMode=Edit::BUILD_BUILD;
  38. PublishResult PublishRes;
  39. WindowIO PublishEsProjIO;
  40. /******************************************************************************/
  41. bool PublishDataNeeded(Edit::EXE_TYPE exe, Edit::BUILD_MODE mode) {return exe==Edit::EXE_NEW || exe==Edit::EXE_APK || exe==Edit::EXE_IOS;}
  42. /******************************************************************************/
  43. void PublishDo()
  44. {
  45. if(Demo)
  46. {
  47. Gui.msgBox(S, "Demo version doesn't allow publishing."); return;
  48. if(Proj.cipher || Proj.compress_type){Gui.msgBox(S, "Encryption and Compression are not available in the demo version"); return;}
  49. }
  50. CodeEdit.publish();
  51. }
  52. void PublishEsProjAs(C Str &path, ptr user)
  53. {
  54. StartPublish(S, Edit::EXE_EXE, Edit::BUILD_PUBLISH, true, path, false, true);
  55. }
  56. bool StartPublish(C Str &exe_name, Edit::EXE_TYPE exe_type, Edit::BUILD_MODE build_mode, bool no_compile, C Str &custom_project_data_path, bool open_ide, bool es_proj)
  57. {
  58. PublishRes.del();
  59. PublishExePath =exe_name;
  60. PublishExeType =exe_type;
  61. PublishBuildMode=build_mode;
  62. PublishNoCompile=no_compile;
  63. PublishOpenIDE =open_ide;
  64. PublishEsProj =es_proj;
  65. PublishDataOnly =(no_compile && custom_project_data_path.is());
  66. PublishPath .clear();
  67. PublishBinPath .clear();
  68. PublishProjectDataPath.clear();
  69. if(PublishEsProj)Publish.cipher.clear(); // EsenthelProject is never encrypted
  70. else Publish.cipher.set(Proj);
  71. if(PublishEsProj)CodeEdit.saveChanges();
  72. if(PublishDataOnly || PublishEsProj) // data only
  73. {
  74. PublishPath =custom_project_data_path; FCreateDirs(GetPath(PublishPath));
  75. PublishProjectDataPath=custom_project_data_path;
  76. PublishDataAsPak=true;
  77. }else // executable
  78. {
  79. bool physx_dll=(PHYSX_DLL && Physics.engine()==PHYS_ENGINE_PHYSX && CodeEdit.appPublishPhysxDll());
  80. if(exe_type==Edit::EXE_EXE || exe_type==Edit::EXE_DLL)
  81. {
  82. PublishPath=ProjectsPath+ProjectsPublishPath+GetBase(CodeEdit.appPath(CodeEdit.appName())).tailSlash(true); if(!FDelInside(PublishPath)){Gui.msgBox(S, S+"Can't delete \""+PublishPath+'"'); return false;}
  83. PublishDataAsPak=CodeEdit.appPublishDataAsPak();
  84. if(!FExistSystem(PublishPath) && !FCreateDirs(PublishPath)){Gui.msgBox(S, S+"Can't create \""+PublishPath+'"'); return false;}
  85. if(!CodeEdit.appEmbedEngineData() || CodeEdit.appPublishProjData() || physx_dll) // if want to store something in "Bin" folder
  86. {
  87. PublishBinPath=PublishPath+"Bin\\"; if(!FCreateDirs(PublishBinPath)){Gui.msgBox(S, S+"Can't create \""+PublishBinPath+'"'); return false;}
  88. if(CodeEdit.appPublishProjData() && PublishDataAsPak)PublishProjectDataPath=PublishBinPath+"Project.pak";
  89. Memt<Str> files;
  90. if(!CodeEdit.appEmbedEngineData() && PublishDataAsPak)files.add("Engine.pak");
  91. if(physx_dll)
  92. {
  93. cchar8 *physx_files32[]=
  94. {
  95. "PhysX3_x86.dll",
  96. "PhysX3Common_x86.dll",
  97. "PhysX3Cooking_x86.dll",
  98. "PxFoundation_x86.dll",
  99. };
  100. cchar8 *physx_files64[]=
  101. {
  102. "PhysX3_x64.dll",
  103. "PhysX3Common_x64.dll",
  104. "PhysX3Cooking_x64.dll",
  105. "PxFoundation_x64.dll",
  106. };
  107. if(CodeEdit.config32Bit())FREPA(physx_files32)files.add(physx_files32[i]);
  108. else FREPA(physx_files64)files.add(physx_files64[i]);
  109. }
  110. FREPA(files)if(!FCopy(BinPath().tailSlash(true)+files[i], PublishBinPath+GetBase(files[i]))){Gui.msgBox(S, S+"Can't copy \""+files[i]+'"'); return false;}
  111. }
  112. if(CodeEdit.appPublishSteamDll())
  113. {
  114. cchar8 *name=(CodeEdit.config32Bit() ? "steam_api.dll" : "steam_api64.dll");
  115. if(!FCopy(S+"Code/Windows/"+name, PublishPath+name)){Gui.msgBox(S, S+"Can't copy \""+name+'"'); return false;}
  116. }
  117. if(CodeEdit.appPublishOpenVRDll())
  118. {
  119. cchar8 *name=(CodeEdit.config32Bit() ? "openvr_api.32.dll" : "openvr_api.64.dll");
  120. if(!FCopy(S+"Code/Windows/"+name, PublishPath+"openvr_api.dll")){Gui.msgBox(S, S+"Can't copy \""+name+'"'); return false;}
  121. }
  122. if(PublishExePath.is())if(!FCopy(PublishExePath, PublishPath+GetBase(PublishExePath))){Gui.msgBox(S, S+"Can't copy \""+GetBase(PublishExePath)+'"'); return false;}
  123. }else
  124. if(exe_type==Edit::EXE_NEW)
  125. {
  126. PublishDataAsPak=true; // always set to true because files inside exe can't be modified by the app, so there's no point in storing them separately
  127. //if(CodeEdit.appPublishProjData()) always setup 'PublishProjectDataPath' because even if we don't include Project data, we still include App data
  128. {
  129. PublishProjectDataPath=CodeEdit.windowsProjectPakPath(); if(!PublishProjectDataPath.is()){Gui.msgBox(S, "Invalid path for project data file"); return false;}
  130. FCreateDirs(GetPath(PublishProjectDataPath));
  131. }
  132. }else
  133. if(exe_type==Edit::EXE_WEB)
  134. {
  135. if(build_mode==Edit::BUILD_PLAY)
  136. {
  137. PublishPath=GetPath(GetPath(PublishExePath)).tailSlash(true); // convert from "../Emscripten/Debug/App.*" to "../Emscripten/" so we can reuse the same "Engine.pak" and "Project.pak" for Debug/Release configurations
  138. }else
  139. {
  140. PublishPath=ProjectsPath+ProjectsPublishPath+GetBase(CodeEdit.appPath(CodeEdit.appName())).tailSlash(true); if(!FDelInside(PublishPath)){Gui.msgBox(S, S+"Can't delete \""+PublishPath+'"'); return false;}
  141. if(!FExistSystem(PublishPath) && !FCreateDirs(PublishPath)){Gui.msgBox(S, S+"Can't create \""+PublishPath+'"'); return false;}
  142. }
  143. PublishDataAsPak=CodeEdit.appPublishDataAsPak();
  144. if(1/*!CodeEdit.appEmbedEngineData()*/ || CodeEdit.appPublishProjData()) // if want to store something in "Bin" folder, embed engine data should be ignored (we always need engine.pak for web)
  145. {
  146. PublishBinPath=PublishPath/*+"Bin\\"*/; if(!FExistSystem(PublishBinPath) && !FCreateDirs(PublishBinPath)){Gui.msgBox(S, S+"Can't create \""+PublishBinPath+'"'); return false;}
  147. if(CodeEdit.appPublishProjData() && PublishDataAsPak)PublishProjectDataPath=PublishBinPath+"Project.pak";
  148. Memt<Str> files;
  149. if(1/*!CodeEdit.appEmbedEngineData()*/ && PublishDataAsPak)files.add("Web/Engine.pak"); // embed engine data should be ignored (we always need Engine.pak for web)
  150. FREPA(files)if(!FCopy(BinPath().tailSlash(true)+files[i], PublishBinPath+GetBase(files[i]))){Gui.msgBox(S, S+"Can't copy \""+files[i]+'"'); return false;}
  151. }
  152. if(PublishExePath.is())
  153. {
  154. if(!FCopy(GetExtNot(PublishExePath)+".Esenthel.html", PublishPath+GetBaseNoExt(PublishExePath)+".html")){Gui.msgBox(S, S+"Can't copy \""+GetBaseNoExt(PublishExePath)+".html\""); return false;}
  155. if(!FCopy(GetExtNot(PublishExePath)+".js" , PublishPath+GetBaseNoExt(PublishExePath)+".js" )){Gui.msgBox(S, S+"Can't copy \""+GetBaseNoExt(PublishExePath)+".js\"" ); return false;}
  156. if(!FCopy(GetExtNot(PublishExePath)+".wasm" , PublishPath+GetBaseNoExt(PublishExePath)+".wasm")){Gui.msgBox(S, S+"Can't copy \""+GetBaseNoExt(PublishExePath)+".wasm\""); return false;}
  157. }
  158. }else
  159. if(exe_type==Edit::EXE_MAC)
  160. {
  161. PublishPath=ProjectsPath+ProjectsPublishPath+GetBase(CodeEdit.appPath(CodeEdit.appName())).tailSlash(true); if(!FDelInside(PublishPath)){Gui.msgBox(S, S+"Can't delete \""+PublishPath+'"'); return false;}
  162. PublishDataAsPak=CodeEdit.appPublishDataAsPak();
  163. if(!FExistSystem(PublishPath) && !FCreateDirs(PublishPath)){Gui.msgBox(S, S+"Can't create \""+PublishPath+'"'); return false;}
  164. if(!CodeEdit.appEmbedEngineData() || CodeEdit.appPublishProjData()) // if want to store something in "Bin" folder
  165. {
  166. PublishBinPath=PublishPath+"Bin\\"; if(!FCreateDirs(PublishBinPath)){Gui.msgBox(S, S+"Can't create \""+PublishBinPath+'"'); return false;}
  167. if(CodeEdit.appPublishProjData() && PublishDataAsPak)PublishProjectDataPath=PublishBinPath+"Project.pak";
  168. Memt<Str> files;
  169. if(!CodeEdit.appEmbedEngineData() && PublishDataAsPak)files.add("Engine.pak");
  170. FREPA(files)if(!FCopy(BinPath().tailSlash(true)+files[i], PublishBinPath+files[i])){Gui.msgBox(S, S+"Can't copy \""+files[i]+'"'); return false;}
  171. }
  172. if(PublishExePath.is())if((FileInfoSystem(PublishExePath).type==FSTD_FILE) ? !FCopy(PublishExePath, PublishPath+GetBase(PublishExePath)) : !FCopyDir(PublishExePath, PublishPath+GetBase(PublishExePath))){Gui.msgBox(S, S+"Can't copy \""+GetBase(PublishExePath)+'"'); return false;}
  173. }else
  174. if(exe_type==Edit::EXE_LINUX)
  175. {
  176. PublishPath=ProjectsPath+ProjectsPublishPath+GetBase(CodeEdit.appPath(CodeEdit.appName())).tailSlash(true); if(!FDelInside(PublishPath)){Gui.msgBox(S, S+"Can't delete \""+PublishPath+'"'); return false;}
  177. PublishDataAsPak=CodeEdit.appPublishDataAsPak();
  178. if(!FExistSystem(PublishPath) && !FCreateDirs(PublishPath)){Gui.msgBox(S, S+"Can't create \""+PublishPath+'"'); return false;}
  179. if(!CodeEdit.appEmbedEngineData() || CodeEdit.appPublishProjData() || physx_dll || CodeEdit.appPublishSteamDll() || CodeEdit.appPublishOpenVRDll()) // if want to store something in "Bin" folder
  180. {
  181. PublishBinPath=PublishPath+"Bin\\"; if(!FCreateDirs(PublishBinPath)){Gui.msgBox(S, S+"Can't create \""+PublishBinPath+'"'); return false;}
  182. if(CodeEdit.appPublishProjData() && PublishDataAsPak)PublishProjectDataPath=PublishBinPath+"Project.pak";
  183. Memt<Str> files;
  184. if(!CodeEdit.appEmbedEngineData() && PublishDataAsPak)files.add("Engine.pak");
  185. if(physx_dll)
  186. {
  187. cchar8 *physx_files[]=
  188. {
  189. "libPhysX3_x64.so",
  190. "libPhysX3Common_x64.so",
  191. "libPhysX3Cooking_x64.so",
  192. "libPxFoundation_x64.so",
  193. };
  194. FREPA(physx_files)files.add(physx_files[i]);
  195. }
  196. if(CodeEdit.appPublishSteamDll ())files.add("libsteam_api.so");
  197. if(CodeEdit.appPublishOpenVRDll())files.add("libopenvr_api.so");
  198. FREPA(files)if(!FCopy(BinPath().tailSlash(true)+files[i], PublishBinPath+files[i])){Gui.msgBox(S, S+"Can't copy \""+files[i]+'"'); return false;}
  199. }
  200. if(PublishExePath.is())if((FileInfoSystem(PublishExePath).type==FSTD_FILE) ? !FCopy(PublishExePath, PublishPath+GetBase(PublishExePath)) : !FCopyDir(PublishExePath, PublishPath+GetBase(PublishExePath))){Gui.msgBox(S, S+"Can't copy \""+GetBase(PublishExePath)+'"'); return false;}
  201. }else
  202. if(exe_type==Edit::EXE_APK)
  203. {
  204. PublishDataAsPak=true; // always set to true because files inside APK (assets) can't be modified by the app, so there's no point in storing them separately
  205. //if(CodeEdit.appPublishProjData()) always setup 'PublishProjectDataPath' because even if we don't include Project data, we still include App data
  206. {
  207. PublishProjectDataPath=CodeEdit.androidProjectPakPath(); if(!PublishProjectDataPath.is()){Gui.msgBox(S, "Invalid path for project data file"); return false;}
  208. FCreateDirs(GetPath(PublishProjectDataPath));
  209. }
  210. }else
  211. if(exe_type==Edit::EXE_IOS)
  212. {
  213. PublishDataAsPak=true; // always set to true because files inside iOS APP can't be modified by the app, so there's no point in storing them separately
  214. //if(CodeEdit.appPublishProjData()) always setup 'PublishProjectDataPath' because even if we don't include Project data, we still include App data
  215. {
  216. PublishProjectDataPath=CodeEdit.iOSProjectPakPath(); if(!PublishProjectDataPath.is()){Gui.msgBox(S, "Invalid path for project data file"); return false;}
  217. FCreateDirs(GetPath(PublishProjectDataPath));
  218. }
  219. }else {Gui.msgBox(S, "Invalid application type."); return false;}
  220. }
  221. if(build_mode!=Edit::BUILD_PUBLISH) // if there's no need to wait for fully complete data (for example we want to play/debug game on mobile which requires PAK creation)
  222. {
  223. Proj.flush(); // flush what we've changed
  224. SetPublishFiles(PublishFiles, PublishGenerate, PublishConvert, PublishFileData); // detect files for packing
  225. if(!PublishGenerate.elms() && !PublishConvert.elms()) // if there are no elements to generate and convert
  226. if(PublishDataAsPak) // if we're creating a pak
  227. if(!PublishProjectDataPath.is() // if we don't want to create project data pak (no file)
  228. || FileInfoSystem(PublishProjectDataPath).modify_time_utc>CodeEdit.appEmbedSettingsTime() && PakEqual(PublishFiles, PublishProjectDataPath, Publish.cipher())){PublishSuccess(); return true;} // or if pak is similar to what we want then exit already
  229. }
  230. StatePublish.set(StateFadeTime);
  231. return true;
  232. }
  233. /******************************************************************************/
  234. void ImageGenerateProcess(ImageGenerate &generate, ptr user, int thread_index)
  235. {
  236. if(!Publish.progress.stop)
  237. {
  238. ThreadMayUseGPUData();
  239. generate.process();
  240. {SyncLocker locker(PublishLock); Publish.progress.progress+=1.0f/PublishGenerate.elms();}
  241. }
  242. }
  243. void ImageConvertProcess(ImageConvert &convert, ptr user, int thread_index)
  244. {
  245. if(!Publish.progress.stop)
  246. {
  247. ThreadMayUseGPUData();
  248. convert.process(&Publish.progress.stop);
  249. {SyncLocker locker(PublishLock); Publish.progress.progress+=1.0f/PublishConvert.elms();}
  250. }
  251. }
  252. bool PublishFunc(Thread &thread)
  253. {
  254. // generate
  255. PublishStage=PUBLISH_MTRL_SIMPLIFY; Publish.progress.progress=0; WorkerThreads.process1(PublishGenerate, ImageGenerateProcess);
  256. // convert
  257. PublishStage=PUBLISH_TEX_OPTIMIZE; Publish.progress.progress=0; WorkerThreads.process1(PublishConvert, ImageConvertProcess);
  258. // pak
  259. PublishStage=PUBLISH_PUBLISH; Publish.progress.progress=0;
  260. if(PublishDataAsPak)
  261. {
  262. if(!PublishProjectDataPath.is())PublishOk=true;else
  263. PublishOk=PakCreate(PublishFiles, PublishProjectDataPath, PAK_SET_HASH, Publish.cipher(), PublishEsProj ? EsenthelProjectCompression : Proj.compress_type, PublishEsProj ? EsenthelProjectCompressionLevel : Proj.compress_level, &PublishErrorMessage, &Publish.progress);
  264. }else
  265. {
  266. Memc<Str> dest_paths;
  267. FREPA(PublishFiles)
  268. {
  269. if(Publish.progress.stop)return false;
  270. C PakFileData &pfd=PublishFiles[i];
  271. if(pfd.type!=FSTD_DIR)
  272. {
  273. Str dest=PublishBinPath+pfd.name, dest_path=GetPath(dest);
  274. if(dest_paths.binaryInclude(dest_path, ComparePathCI))FCreateDirs(dest_path); // create path only once
  275. if(!FCopy(pfd.data.name, dest))
  276. {
  277. PublishErrorMessage="Error copying file.";
  278. if(!FExist(pfd.data.name))PublishErrorMessage.line()+="File not found.";
  279. PublishErrorMessage.line()+=S+"File: \""+pfd.name+'"';
  280. if(Elm *elm=Proj.findElm(GetFileNameID(pfd.name)))PublishErrorMessage.line()+=S+"Element: \""+Proj.elmFullName(elm)+'"';
  281. return false;
  282. }
  283. }
  284. Publish.progress.progress=flt(i)/PublishFiles.elms();
  285. }
  286. PublishOk=true;
  287. }
  288. return false;
  289. }
  290. /******************************************************************************/
  291. Texture* GetTexture(MemPtr<Texture> textures, C UID &tex_id)
  292. {
  293. if(tex_id.valid())
  294. {
  295. int index; if(textures.binarySearch(tex_id, index, Texture::CompareTex))return &textures[index];
  296. Texture &tex=textures.NewAt(index); tex.id=tex_id;
  297. return &tex;
  298. }
  299. return null;
  300. }
  301. void AddPublishFiles(Memt<Elm*> &elms, MemPtr<PakFileData> files, Memc<ImageGenerate> &generate, Memc<ImageConvert> &convert)
  302. {
  303. Memx<Texture> publish_texs; // textures to be published, need to use Memx because below pointers are stored
  304. C bool android=(PublishExeType==Edit::EXE_APK), iOS=(PublishExeType==Edit::EXE_IOS), web=(PublishExeType==Edit::EXE_WEB);
  305. // elements
  306. FREPA(elms)if(Elm *elm=elms[i])if(ElmPublish(elm->type))
  307. {
  308. if(elm->type==ELM_WORLD) // world
  309. {
  310. Str world_path=EncodeFileName(elm->id), world_edit_path_src, world_game_path_src;
  311. PakFileData &pfd=files.New(); pfd.name=world_path; pfd.type=FSTD_DIR; // world folder
  312. if(Proj.getWorldPaths(elm->id, world_edit_path_src, world_game_path_src))
  313. if(WorldVer *ver=Proj.worldVerGet(elm->id))
  314. {
  315. world_path.tailSlash(true);
  316. PakFileData &pfd=files.New(); // world settings
  317. pfd.name =world_path +"Settings";
  318. pfd.data.set(world_game_path_src+"Settings");
  319. Str area_path=world_path +"Area\\",
  320. src_area_path=world_game_path_src+"Area\\",
  321. waypoint_path=world_path +"Waypoint\\",
  322. src_waypoint_path=world_game_path_src+"Waypoint\\";
  323. FREPA(ver->areas) // process in order to avoid re-sorting
  324. {
  325. Str src=src_area_path+ver->areas.lockedKey(i); if(FExist(src)) // if an area was created, but later its data was removed, then the AreaVer will remain, however the area data file may be removed, because of that, we need to check if it exists
  326. {
  327. PakFileData &pfd=files.New(); // world area
  328. pfd.name=area_path+ver->areas.lockedKey(i);
  329. pfd.data.set(src);
  330. }
  331. }
  332. FREPA(ver->waypoints) // process in order to avoid re-sorting
  333. {
  334. Str file_id=EncodeFileName(ver->waypoints.lockedKey(i));
  335. if(FExist(src_waypoint_path+file_id)) // waypoint game file can be deleted when it was removed so we need to check if it exists
  336. {
  337. PakFileData &pfd=files.New(); // world waypoint
  338. pfd.name = waypoint_path+file_id;
  339. pfd.data.set(src_waypoint_path+file_id);
  340. }
  341. }
  342. }
  343. }else
  344. if(elm->type==ELM_MINI_MAP) // mini map
  345. {
  346. Str mini_map_path=EncodeFileName(elm->id), mini_map_game_path_src, mini_map_formats_path;
  347. PakFileData &pfd=files.New(); pfd.name=mini_map_path; pfd.type=FSTD_DIR; // mini map folder
  348. if(ElmMiniMap *data=elm->miniMapData())
  349. if(MiniMapVer *ver=Proj.miniMapVerGet(elm->id))
  350. {
  351. mini_map_path.tailSlash(true);
  352. mini_map_game_path_src=Proj.gamePath(elm->id).tailSlash(true);
  353. PakFileData &pfd=files.New(); // mini map settings
  354. pfd.name =mini_map_path +"Settings";
  355. pfd.data.set(mini_map_game_path_src+"Settings");
  356. IMAGE_TYPE dest_type=IMAGE_NONE;
  357. if(android || iOS) // convert for mobile, desktop/web already has IMAGE_BC1 chosen
  358. {
  359. dest_type=(android ? (AndroidETC2 ? IMAGE_ETC2 : IMAGE_ETC1) : IMAGE_PVRTC1_4);
  360. mini_map_formats_path=Proj.formatPath(elm->id, FormatSuffix(dest_type));
  361. mini_map_formats_path.tailSlash(true);
  362. FCreateDirs(mini_map_formats_path);
  363. }
  364. FREPA(ver->images) // process in order to avoid re-sorting
  365. {
  366. PakFileData &pfd=files.New(); // mini map image
  367. pfd.name =mini_map_path +ver->images[i];
  368. pfd.data.set(mini_map_game_path_src+ver->images[i]);
  369. if(dest_type) // convert
  370. {
  371. Str src_name=pfd.data.name,
  372. dest_name=mini_map_formats_path+ver->images[i];
  373. pfd.data.set(dest_name); // adjust pak file path
  374. FileInfo src_fi(src_name);
  375. if(Compare(src_fi.modify_time_utc, FileInfoSystem(dest_name).modify_time_utc, 1)) // if different (compare just modify time, because sizes will always be different due to different formats)
  376. convert.New().set(src_name, dest_name, src_fi.modify_time_utc, dest_type, true, true); // create new conversion
  377. }
  378. }
  379. }
  380. }else // regular files
  381. {
  382. PakFileData &pfd=files.New();
  383. pfd.name = EncodeFileName(elm->id);
  384. pfd.data.set( Proj.gamePath(elm->id));
  385. pfd.compress_mode=(ElmPublishNoCompress(elm->type) ? COMPRESS_DISABLE : COMPRESS_ENABLE);
  386. if(elm->type==ELM_MTRL)if(ElmMaterial *data=elm->mtrlData()) // material
  387. {
  388. bool regenerate=false,
  389. uses_tex_bump=data->usesTexBump(),
  390. uses_tex_glow=data->usesTexGlow();
  391. byte downsize =((android || iOS) ? data->downsize_tex_mobile : 0);
  392. UID base_0_tex=data->base_0_tex, src_tex=UIDZero,
  393. base_1_tex=data->base_1_tex;
  394. // simplify material
  395. if(base_0_tex.valid() && base_1_tex.valid() && Proj.materialSimplify(PublishExeType))
  396. {
  397. uses_tex_bump=uses_tex_glow=false; // these textures are removed when merging
  398. // adjust base texture ID's
  399. src_tex=base_0_tex; // mark it as dynamically generated texture
  400. base_0_tex=MergedBaseTexturesID(base_0_tex, base_1_tex);
  401. base_1_tex.zero();
  402. // make a copy of the material with adjusted textures
  403. Str src_name=pfd.data.name,
  404. dest_name=Proj.formatPath(elm->id, FormatSuffixSimple());
  405. pfd.data.set(dest_name); // adjust pak file path
  406. FileInfo src_fi(src_name);
  407. if(Compare(src_fi.modify_time_utc, FileInfoSystem(dest_name).modify_time_utc, 1)) // if different (compare just modify time, because sizes will always be different due to different formats)
  408. {
  409. MaterialPtr mtrl=src_name; // use 'MaterialPtr' to access it from cache if available
  410. Material temp=*mtrl;
  411. temp.base_0=Proj.texPath(base_0_tex); // the texture will be initially saved to 'texDynamicPath' however in PAK it will be created in 'texPath' so save the reference there
  412. temp.base_1=null;
  413. // adjust specular related parameters
  414. {
  415. flt avg_specular=0.5f;
  416. Vec4 avg; if(mtrl->base_1)if(mtrl->base_1->stats(null, null, &avg))avg_specular=avg.z; // specular is packed in BLUE channel, #MaterialTextureChannelOrder
  417. temp.specular*=avg_specular;
  418. temp.reflect *=avg_specular;
  419. }
  420. if(temp.technique==MTECH_DEFAULT)temp.glow=0; // disable glow if it's possible that there was a glow map
  421. File f; temp.save(f.writeMem(), Proj.game_path); f.pos(0); SafeOverwrite(f, dest_name, &src_fi.modify_time_utc);
  422. }
  423. // merge textures
  424. FileInfo src_base_0(Proj.texPath (data->base_0_tex)); // get modify time of the original texture in case it was modified later (for example due to mip map blur)
  425. Str dest_base_0=Proj.texDynamicPath( base_0_tex) ; // get path of the merged texture
  426. if(Compare(src_base_0.modify_time_utc, FileInfoSystem(dest_base_0).modify_time_utc, 1)) // if different (compare just modify time, because sizes will always be different due to different formats)
  427. {
  428. generate.New().set(src_name, dest_base_0, src_base_0.modify_time_utc);
  429. regenerate=true; // this tex will be regenerated
  430. }
  431. }
  432. // !! 'GetTexture' needs to be called always because it adds texture to publish list !!
  433. bool hq=(iOS && data->texQualityiOS());
  434. Texture *t0; if( t0=GetTexture(publish_texs, base_0_tex)){t0->downSize(downsize); if(ForceHQMtrlBase0 || hq)t0->keep_hq=true; t0->src_tex_id=src_tex; t0->regenerate|=regenerate;}
  435. Texture *t1; if( t1=GetTexture(publish_texs, base_1_tex)){t1->downSize(downsize); if(ForceHQMtrlBase1 )t1->keep_hq=true; t1->mtrl_base_1=true;}
  436. if(Texture *t=GetTexture(publish_texs, data-> detail_tex)){t ->downSize(downsize); if(ForceHQMtrlDetail )t ->keep_hq=true; if(!RemoveMtrlDetailBump)t->uses_alpha=true;} // Detail uses Alpha for bump unless it's removed
  437. if(Texture *t=GetTexture(publish_texs, data-> macro_tex)) t ->downSize(downsize); // doesn't use Alpha, 'GetTexture' needs to be called
  438. if(Texture *t=GetTexture(publish_texs, data-> light_tex)) t ->downSize(downsize); // doesn't use Alpha, 'GetTexture' needs to be called
  439. if(Texture *t=GetTexture(publish_texs, data->reflection_tex)){} // doesn't use Alpha, 'GetTexture' needs to be called
  440. // check which base textures use Alpha Channel, #MaterialTextureChannelOrder
  441. if(t1) // having 'base_1' texture means that 'base_0' alpha channel is bump intensity and 'base_1' is alpha channel opacity
  442. {
  443. if(t0)if( uses_tex_bump)t0->uses_alpha=true; // Alpha used for bump
  444. if(data->usesTexAlpha() || uses_tex_glow)t1->uses_alpha=true; // Alpha used for opacity/glow
  445. }else // 'base_1' is not present, meaning that 'base_0' alpha channel can contain opacity
  446. if(t0)
  447. {
  448. if(data->usesTexAlpha())t0->uses_alpha=true; // Alpha used for opacity
  449. }
  450. }
  451. if(elm->type==ELM_WATER_MTRL)if(ElmWaterMtrl *data=elm->waterMtrlData()) // water material
  452. {
  453. // !! 'GetTexture' needs to be called always because it adds texture to publish list !!
  454. Texture *t0; if( t0=GetTexture(publish_texs, data-> base_0_tex)){if(ForceHQMtrlBase0)t0->keep_hq=true;} // doesn't use Alpha
  455. Texture *t1; if( t1=GetTexture(publish_texs, data-> base_1_tex)){if(ForceHQMtrlBase1)t1->keep_hq=true; t1->mtrl_base_1=true;} // doesn't use Alpha
  456. if(Texture *t=GetTexture(publish_texs, data->reflection_tex)){} // doesn't use Alpha, 'GetTexture' needs to be called
  457. // check which base textures use Alpha Channel, #MaterialTextureChannelOrder
  458. if(t1) // having 'base_1' texture means that 'base_0' alpha channel is bump intensity and 'base_1' is alpha channel opacity
  459. {
  460. if(t0)if(data->usesTexBump())t0->uses_alpha=true; // Alpha used for bump
  461. }
  462. }
  463. // try optimizing images for target platform
  464. if(elm->type==ELM_IMAGE) // image
  465. if(android || iOS || web) // desktop platform already has the best format chosen through 'EditToGameImage' and 'ImageProps'
  466. if(ElmImage *data=elm->imageData())
  467. if(IMAGE_TYPE dest_type=(android ? data->androidType() : iOS ? data->iOSType() : data->webType())) // want to use custom type
  468. {
  469. Str src_name=pfd.data.name,
  470. dest_name=Proj.formatPath(elm->id, FormatSuffix(dest_type));
  471. pfd.data.set(dest_name); // adjust pak file path
  472. FileInfo src_fi(src_name);
  473. if(Compare(src_fi.modify_time_utc, FileInfoSystem(dest_name).modify_time_utc, 1)) // if different (compare just modify time, because sizes will always be different due to different formats)
  474. convert.New().set(Proj.editPath(elm->id), dest_name, src_fi.modify_time_utc, *data, dest_type); // create new conversion (set src from edit path to get better quality)
  475. }
  476. // try optimizing icons for target platform
  477. if(elm->type==ELM_ICON) // icon
  478. if(android || iOS || web) // desktop platform already has the best format chosen through 'EditToGameImage' and 'ImageProps'
  479. if(ElmIcon *data=elm->iconData())
  480. if(IMAGE_TYPE dest_type=(android ? data->androidType(&Proj) : iOS ? data->iOSType(&Proj) : data->webType(&Proj))) // want to use custom type
  481. {
  482. Str src_name=pfd.data.name,
  483. dest_name=Proj.formatPath(elm->id, FormatSuffix(dest_type));
  484. pfd.data.set(dest_name); // adjust pak file path
  485. FileInfo src_fi(src_name);
  486. if(Compare(src_fi.modify_time_utc, FileInfoSystem(dest_name).modify_time_utc, 1)) // if different (compare just modify time, because sizes will always be different due to different formats)
  487. convert.New().set(Proj.gamePath(elm->id), dest_name, src_fi.modify_time_utc, *data, dest_type); // create new conversion (set src from game path because icons have only game version)
  488. }
  489. // try optimizing atlases for target platform
  490. if(elm->type==ELM_IMAGE_ATLAS) // image atlas
  491. if(android || iOS || (web && !WebBC7)) // desktop platform already has the best format chosen during image atlas creation
  492. if(ElmImageAtlas *data=elm->imageAtlasData())
  493. if(IMAGE_TYPE dest_type=(android ? IMAGE_ETC2_A8 : iOS ? IMAGE_PVRTC1_4 : IMAGE_BC3)) // we assume that atlas images contain transparency
  494. {
  495. Str src_name=pfd.data.name,
  496. dest_name=Proj.formatPath(elm->id, FormatSuffix(dest_type));
  497. pfd.data.set(dest_name); // adjust pak file path
  498. FileInfo src_fi(src_name);
  499. if(Compare(src_fi.modify_time_utc, FileInfoSystem(dest_name).modify_time_utc, 1)) // if different (compare just modify time, because sizes will always be different due to different formats)
  500. convert.New().set(Proj.gamePath(elm->id), dest_name, src_fi.modify_time_utc, *data, dest_type); // create new conversion (set src from game path because image atlases have only game version)
  501. }
  502. // try optimizing fonts for target platform
  503. if(elm->type==ELM_FONT) // font
  504. if(android || iOS || (web && !WebBC7)) // desktop platform already has the best format chosen during font creation
  505. if(ElmFont *data=elm->fontData())
  506. if(IMAGE_TYPE dest_type=((android || iOS) ? IMAGE_ETC2_A8 : IMAGE_BC3)) // for Android/iOS GL_ES 3.0+ use IMAGE_ETC2_A8, don't use IMAGE_PVRTC1_2/IMAGE_PVRTC1_4 because the quality is too low
  507. {
  508. Str src_name=pfd.data.name,
  509. dest_name=Proj.formatPath(elm->id, FormatSuffix(dest_type));
  510. pfd.data.set(dest_name); // adjust pak file path
  511. FileInfo src_fi(src_name);
  512. if(Compare(src_fi.modify_time_utc, FileInfoSystem(dest_name).modify_time_utc, 1)) // if different (compare just modify time, because sizes will always be different due to different formats)
  513. convert.New().set(Proj.gamePath(elm->id), dest_name, src_fi.modify_time_utc, *data, dest_type); // create new conversion (set src from game path because fonts have only game version)
  514. }
  515. // try optimizing panel images for target platform
  516. if(elm->type==ELM_PANEL_IMAGE) // panel image
  517. if(android || iOS || (web && !WebBC7)) // desktop platform already has the best format chosen during image atlas creation
  518. if(ElmPanelImage *data=elm->panelImageData())
  519. if(IMAGE_TYPE dest_type=(android ? IMAGE_ETC2_A8 : iOS ? IMAGE_PVRTC1_4 : IMAGE_BC3)) // we assume that atlas images contain transparency
  520. {
  521. Str src_name=pfd.data.name,
  522. dest_name=Proj.formatPath(elm->id, FormatSuffix(dest_type));
  523. pfd.data.set(dest_name); // adjust pak file path
  524. FileInfo src_fi(src_name);
  525. if(Compare(src_fi.modify_time_utc, FileInfoSystem(dest_name).modify_time_utc, 1)) // if different (compare just modify time, because sizes will always be different due to different formats)
  526. convert.New().set(Proj.gamePath(elm->id), dest_name, src_fi.modify_time_utc, *data, dest_type); // create new conversion (set src from game path because image atlases have only game version)
  527. }
  528. }
  529. }
  530. // textures
  531. int tex_not_found=0;
  532. FREPA(publish_texs)
  533. {
  534. C Texture &tex=publish_texs[i]; const bool dynamic=tex.src_tex_id.valid();
  535. if(Proj.texs.binaryHas(tex.id, Compare) || dynamic)
  536. {
  537. PakFileData &pfd=files.New();
  538. pfd.name=S+"Tex/"+EncodeFileName(tex.id); // dest name
  539. pfd.data.set(dynamic ? Proj.texDynamicPath(tex.id) : Proj.texPath(tex.id)); // src name
  540. // change type
  541. int change_type=-1;
  542. if(android)change_type=(tex.uses_alpha ? IMAGE_ETC2_A8 : (AndroidETC2 || tex.keep_hq) ? IMAGE_ETC2 : IMAGE_ETC1 );else
  543. if(iOS )change_type=( tex.keep_hq ? IMAGE_PVRTC1_4 : IMAGE_PVRTC1_2);else
  544. if(web )change_type=(WebBC7 ? ((tex.uses_alpha || tex.keep_hq) ? -1 : IMAGE_BC1) // texture could have alpha, however if we're not using it, then reduce to BC1 because it's only 4-bit per pixel
  545. : tex.uses_alpha ? IMAGE_BC3 : IMAGE_BC1);else // if BC7 not supported for Web, then use BC3
  546. //if(!tex.uses_alpha && !tex.keep_hq )change_type=IMAGE_BC1;else // texture could have alpha, however if we're not using it, then reduce to BC1 because it's only 4-bit per pixel, actually don't do this because it would require calling 'ImageLoadHeader' which is an IO operation and could be slow for many textures
  547. {}
  548. // change size
  549. int max_size=INT_MAX; //((tex.max_size>0) ? tex.max_size : INT_MAX);
  550. if(change_type>=0 || max_size!=INT_MAX || tex.downsize) // if any change is desired
  551. {
  552. // detect if we actually need to retype/resize
  553. if((change_type>=0 || max_size!=INT_MAX) && !tex.downsize) // in this detection we can change only 'change_type' and 'max_size', however if 'downsize' is set, then we will always convert, so no need to check this
  554. {
  555. ImageHeader header; if(ImageLoadHeader(dynamic ? Proj.texPath(tex.src_tex_id) : pfd.data.name, header)) // if this is a dynamically generated texture then get the header from its source, as the dynamic texture may not exist yet
  556. {
  557. if(header.type ==change_type)change_type=-1 ; // if image already is of that type , then disable retyping
  558. if(header.size.max()<= max_size) max_size=INT_MAX; // if image size fits into the limit, then disable resizing
  559. }
  560. }
  561. if(change_type>=0 || max_size!=INT_MAX || tex.downsize)
  562. {
  563. Str src_name=pfd.data.name,
  564. dest_name=Proj.texFormatPath(tex.id, FormatSuffix(IMAGE_TYPE(change_type)), tex.downsize);
  565. pfd.data.set(dest_name); // adjust pak file path
  566. FileInfo src_fi(src_name);
  567. if(tex.regenerate // if texture is going to be regenerated in this process, then always allow converting it
  568. || Compare(src_fi.modify_time_utc, FileInfoSystem(dest_name).modify_time_utc, 1)) // if different (compare just modify time, because sizes will always be different due to different formats)
  569. convert.New().set(src_name, dest_name, src_fi.modify_time_utc, change_type, !tex.uses_alpha, false, tex.mtrl_base_1, tex.downsize, max_size); // create new conversion
  570. }
  571. }
  572. }else
  573. {
  574. tex_not_found++;
  575. }
  576. }
  577. if(tex_not_found)Gui.msgBox(S, S+tex_not_found+" Texture"+CountS(tex_not_found)+" were not found");
  578. }
  579. /******************************************************************************/
  580. void SetPublishFiles(Memb<PakFileData> &files, Memc<ImageGenerate> &generate, Memc<ImageConvert> &convert, Memc<Mems<byte> > &file_data)
  581. {
  582. files .clear();
  583. generate .clear();
  584. convert .clear();
  585. file_data.clear();
  586. if(PublishEsProj) // publish as *.EsenthelProject
  587. {
  588. Project temp; temp=Proj;
  589. Memc<UID> remove; Proj.floodRemoved(remove, Proj.root); remove.sort(Compare);
  590. REPA(temp.elms)if(remove.binaryHas(temp.elms[i].id, Compare))temp.elms.removeValid(i, true);
  591. temp.getTextures(temp.texs); // keep only used textures
  592. // project "Data" file
  593. {
  594. File file; temp.save(file.writeMem()); file.pos(0);
  595. Mems<byte> &data=file_data.New(); data.setNum(file.size()).loadRawData(file);
  596. PakFileData &pfd =files.New();
  597. pfd.name="Data";
  598. pfd.modify_time_utc.getUTC();
  599. pfd.data.set(data.data(), data.elms());
  600. }
  601. // elements
  602. FREPA(temp.elms)
  603. {
  604. Elm &elm=temp.elms[i];
  605. if(elm.type==ELM_WORLD)
  606. {
  607. Str world_edit_path=S+"Edit/"+EncodeFileName(elm.id),
  608. world_game_path=S+"Game/"+EncodeFileName(elm.id),
  609. world_edit_path_src, world_game_path_src;
  610. {PakFileData &pfd=files.New(); pfd.name=world_edit_path; pfd.type=FSTD_DIR;} // world edit folder
  611. {PakFileData &pfd=files.New(); pfd.name=world_game_path; pfd.type=FSTD_DIR;} // world game folder
  612. if(Proj.getWorldPaths(elm.id, world_edit_path_src, world_game_path_src))
  613. if(WorldVer *ver=Proj.worldVerGet(elm.id))
  614. {
  615. world_edit_path.tailSlash(true);
  616. world_game_path.tailSlash(true);
  617. { // world edit data
  618. PakFileData &pfd=files.New();
  619. pfd.name =world_edit_path +"Data";
  620. pfd.data.set(world_edit_path_src+"Data");
  621. }
  622. { // world game settings
  623. PakFileData &pfd=files.New();
  624. pfd.name =world_game_path +"Settings";
  625. pfd.data.set(world_game_path_src+"Settings");
  626. }
  627. // areas, waypoints and water
  628. Str area_game_path =world_game_path +"Area\\",
  629. area_edit_path =world_edit_path +"Area\\",
  630. area_game_path_src=world_game_path_src+"Area\\",
  631. area_edit_path_src=world_edit_path_src+"Area\\",
  632. waypoint_game_path =world_game_path +"Waypoint\\",
  633. waypoint_edit_path =world_edit_path +"Waypoint\\",
  634. waypoint_game_path_src=world_game_path_src+"Waypoint\\",
  635. waypoint_edit_path_src=world_edit_path_src+"Waypoint\\",
  636. lake_edit_path =world_edit_path +"Lake\\",
  637. lake_edit_path_src=world_edit_path_src+"Lake\\",
  638. river_edit_path =world_edit_path +"River\\",
  639. river_edit_path_src=world_edit_path_src+"River\\";
  640. FREPA(ver->areas) // areas, process in order to avoid re-sorting
  641. {
  642. Str src;
  643. // edit
  644. src=area_edit_path_src+ver->areas.lockedKey(i); if(FExist(src)) // if an area was created, but later its data was removed, then the AreaVer will remain, however the area data file may be removed, because of that, we need to check if it exists
  645. {
  646. PakFileData &pfd=files.New();
  647. pfd.name=area_edit_path+ver->areas.lockedKey(i);
  648. pfd.data.set(src);
  649. }
  650. // game
  651. src=area_game_path_src+ver->areas.lockedKey(i); if(FExist(src)) // if an area was created, but later its data was removed, then the AreaVer will remain, however the area data file may be removed, because of that, we need to check if it exists
  652. {
  653. PakFileData &pfd=files.New();
  654. pfd.name=area_game_path+ver->areas.lockedKey(i);
  655. pfd.data.set(src);
  656. }
  657. }
  658. FREPA(ver->waypoints) // waypoints, process in order to avoid re-sorting
  659. {
  660. Str file_id=EncodeFileName(ver->waypoints.lockedKey(i));
  661. { // edit
  662. PakFileData &pfd=files.New();
  663. pfd.name =waypoint_edit_path +file_id;
  664. pfd.data.set(waypoint_edit_path_src+file_id);
  665. }
  666. if(FExist(waypoint_game_path_src+file_id)) // waypoint game file can be deleted when it was removed so we need to check if it exists
  667. {
  668. PakFileData &pfd=files.New();
  669. pfd.name =waypoint_game_path +file_id;
  670. pfd.data.set(waypoint_game_path_src+file_id);
  671. }
  672. }
  673. FREPA(ver->lakes) // lakes, process in order to avoid re-sorting
  674. {
  675. Str file_id=EncodeFileName(ver->lakes.lockedKey(i));
  676. PakFileData &pfd=files.New();
  677. pfd.name =lake_edit_path +file_id;
  678. pfd.data.set(lake_edit_path_src+file_id);
  679. }
  680. FREPA(ver->rivers) // rivers, process in order to avoid re-sorting
  681. {
  682. Str file_id=EncodeFileName(ver->rivers.lockedKey(i));
  683. PakFileData &pfd=files.New();
  684. pfd.name =river_edit_path +file_id;
  685. pfd.data.set(river_edit_path_src+file_id);
  686. }
  687. }
  688. }else
  689. if(elm.type==ELM_MINI_MAP) // mini-map
  690. {
  691. { // edit
  692. PakFileData &pfd=files.New();
  693. pfd.name =S+"Edit/"+EncodeFileName(elm.id);
  694. pfd.data.set( Proj.editPath(elm.id));
  695. }
  696. { // game
  697. Str mini_map_path=S+"Game/"+EncodeFileName(elm.id), mini_map_game_path_src;
  698. PakFileData &pfd=files.New(); pfd.name=mini_map_path; pfd.type=FSTD_DIR; // mini map folder
  699. if(ElmMiniMap *data=elm.miniMapData())
  700. if(MiniMapVer *ver=Proj.miniMapVerGet(elm.id))
  701. {
  702. mini_map_path.tailSlash(true);
  703. mini_map_game_path_src=Proj.gamePath(elm.id).tailSlash(true);
  704. PakFileData &pfd=files.New(); // mini map settings
  705. pfd.name =mini_map_path +"Settings";
  706. pfd.data.set(mini_map_game_path_src+"Settings");
  707. FREPA(ver->images) // process in order to avoid re-sorting
  708. {
  709. PakFileData &pfd=files.New(); // mini map image
  710. pfd.name =mini_map_path +ver->images[i];
  711. pfd.data.set(mini_map_game_path_src+ver->images[i]);
  712. }
  713. }
  714. }
  715. }else // regular element
  716. {
  717. if(elm.type==ELM_CODE)
  718. {
  719. PakFileData &pfd=files.New();
  720. pfd.name =S+"Code/"+EncodeFileName(elm.id)+CodeExt; // keep extension so when using copy elms to another project, we have consistency between copying from both *.EsenthelProject and normal projects
  721. pfd.data.set( Proj.codePath(elm.id));
  722. // don't save code base
  723. }
  724. if(ElmEdit(elm.type))
  725. {
  726. PakFileData &pfd=files.New();
  727. pfd.name =S+"Edit/"+EncodeFileName(elm.id);
  728. pfd.data.set( Proj.editPath(elm.id));
  729. pfd.compress_mode=((elm.type==ELM_IMAGE) ? COMPRESS_DISABLE : COMPRESS_ENABLE); // in Edit folder images are stored using JPG/PNG/WEBP
  730. }
  731. if(ElmGame(elm.type))
  732. {
  733. PakFileData &pfd=files.New();
  734. pfd.name =S+"Game/"+EncodeFileName(elm.id);
  735. pfd.data.set( Proj.gamePath(elm.id));
  736. pfd.compress_mode=((elm.type==ELM_VIDEO) ? COMPRESS_DISABLE : COMPRESS_ENABLE); // videos are already compressed, some sounds may be stored as WAV, so try to compress them if possible
  737. }
  738. }
  739. }
  740. // textures
  741. FREPA(temp.texs)
  742. {
  743. C UID &tex_id=temp.texs[i];
  744. PakFileData &pfd=files.New();
  745. pfd.name =S+"Game/Tex/"+EncodeFileName(tex_id);
  746. pfd.data.set( Proj.texPath(tex_id));
  747. }
  748. }else
  749. {
  750. // Engine.pak files (add this as first to preserve order of loading "Engine.pak" and then "Project.pak", in case some project files overwrite engine files)
  751. if(!CodeEdit.appEmbedEngineData() && !PublishDataAsPak)
  752. {
  753. Pak engine; if(engine.load(BinPath().tailSlash(true)+"Engine.pak"))FREPA(engine)
  754. {
  755. C PakFile &pf =engine.file (i );
  756. C Str &name=engine.fullName(pf);
  757. PakFileData &pfd =files.New();
  758. pfd.name =name;
  759. pfd.data.set(name);
  760. pfd.type=pf.type();
  761. pfd.modify_time_utc=pf.modify_time_utc;
  762. pfd.xxHash64_32=pf.data_xxHash64_32;
  763. }
  764. }
  765. // Project files
  766. if(CodeEdit.appPublishProjData() || PublishDataOnly)
  767. {
  768. Memt<Elm*> elms; FREPA(Proj.elms) // process in order
  769. {
  770. Elm &elm=Proj.elms[i]; if(elm.finalPublish() && ElmPublish(elm.type))elms.add(&elm);
  771. }
  772. AddPublishFiles(elms, files, generate, convert);
  773. }else
  774. if(PublishExeType==Edit::EXE_NEW || PublishExeType==Edit::EXE_APK || PublishExeType==Edit::EXE_IOS) // for Windows New, Android and iOS if Project data is not included, then include only App data
  775. {
  776. Memt<Elm*> elms; Proj.getActiveAppElms(elms);
  777. AddPublishFiles(elms, files, generate, convert);
  778. }
  779. }
  780. }
  781. void GetPublishFiles(Memb<PakFileData> &files) // this is to be called outside of publishing just to get a list of files
  782. {
  783. Memc<ImageGenerate> generate;
  784. Memc<ImageConvert > convert;
  785. Memc<Mems<byte> > file_data;
  786. PublishDataAsPak=true;
  787. PublishDataOnly =true;
  788. PublishExeType =Edit::EXE_EXE;
  789. PublishEsProj =false;
  790. SetPublishFiles(files, generate, convert, file_data);
  791. }
  792. cchar8 *PVRTCQuality[]=
  793. {
  794. "Lowest - Fastest",
  795. "Low - Fast",
  796. "Normal",
  797. "High - Slow",
  798. "Highest - Slowest",
  799. };
  800. void PVRTCQualityChanged(ptr) {SetPVRTCQuality(PublishPVRTCQuality());}
  801. /******************************************************************************/
  802. bool InitPublish()
  803. {
  804. PublishOk=false;
  805. PublishErrorMessage.clear();
  806. Publish.progress.reset();
  807. SetKbExclusive();
  808. Proj.pause(); // this will also flush all unsaved data which is crucial for publishing
  809. PublishAreasLeft=Proj.worldAreasToRebuild();
  810. UpdateProgress.create(Rect_C(0, -0.05f, 1, 0.045f));
  811. PublishPVRTCQualityTextStyle.reset().align.set(0, 1); PublishPVRTCQualityTextStyle.size=0.055f;
  812. Gui+=PublishSkipOptimize .create(Rect_C(0, -0.20f, 0.45f, 0.08f), "Skip for now").focusable(false); PublishSkipOptimize.mode=BUTTON_TOGGLE;
  813. Gui+=PublishPVRTCQuality .create(Rect_C(0, -0.40f, 0.45f, 0.06f), PVRTCQuality, Elms(PVRTCQuality)).set(GetPVRTCQuality(), QUIET).func(PVRTCQualityChanged).focusable(false);
  814. Gui+=PublishPVRTCQualityText.create(PublishPVRTCQuality.rect().up(), "PVRTC Compression Quality:", &PublishPVRTCQualityTextStyle);
  815. return true;
  816. }
  817. void ShutPublish()
  818. {
  819. PublishCancel();
  820. UpdateThread .del(); // delete the thread first
  821. UpdateProgress .del();
  822. PublishFiles .del();
  823. PublishGenerate .del();
  824. PublishConvert .del();
  825. PublishFileData .del();
  826. PublishSkipOptimize .del();
  827. PublishPVRTCQuality .del();
  828. PublishPVRTCQualityText.del();
  829. Proj.resume();
  830. WindowSetNormal();
  831. WindowFlash();
  832. if(!PublishOk)Gui.msgBox("Publishing Failed", PublishErrorMessage);
  833. }
  834. /******************************************************************************/
  835. void PublishSuccess(C Str &open_path, C Str &text)
  836. {
  837. PublishRes.display(text);
  838. Explore(open_path);
  839. }
  840. void PublishSuccess()
  841. {
  842. if(PublishOpenIDE)
  843. {
  844. CodeEdit.CodeEditorInterface::openIDE();
  845. }else
  846. if(PublishDataNeeded(PublishExeType, PublishBuildMode) && !PublishNoCompile) // we've published data, now we need to compile the code
  847. {
  848. CodeEdit.codeDo(PublishBuildMode);
  849. }else
  850. if(PublishExeType==Edit::EXE_WEB && PublishBuildMode==Edit::BUILD_PLAY)
  851. {
  852. Run("emrun", S+'"'+PublishPath+GetBase(PublishExePath)+'"');
  853. }else
  854. {
  855. Str text;
  856. if(PublishProjectDataPath.is())text=S+(PublishEsProj ? "Project size: " : "Project data size: ")+FileSize(FSize(PublishProjectDataPath));
  857. else text="Publishing succeeded";
  858. PublishSuccess(PublishPath, text);
  859. }
  860. }
  861. void PublishCancel() {UpdateThread.stop(); Publish.progress.stop=true; WindowSetWorking();}
  862. /******************************************************************************/
  863. bool UpdatePublish()
  864. {
  865. if(Kb.bp(KB_ESC))PublishCancel();
  866. if(Publish.progress.stop) // when wanting to stop then wait until thread finishes
  867. {
  868. if(!UpdateThread.active())
  869. {
  870. SetProjectState();
  871. PublishOk=false; if(!PublishErrorMessage.is())PublishErrorMessage="Publishing breaked on user request";
  872. }
  873. }else
  874. {
  875. if(!UpdateThread.created())
  876. {
  877. UpdateProgress.set(PublishAreasLeft-Proj.worldAreasToRebuild(), PublishAreasLeft);
  878. Builder.update(false);
  879. if(Builder.finished())
  880. {
  881. WorldEdit.flush(); // flush any world areas that were builded
  882. SetPublishFiles(PublishFiles, PublishGenerate, PublishConvert, PublishFileData);
  883. UpdateThread.create(PublishFunc);
  884. }
  885. }else
  886. if(!UpdateThread.active()) // finished
  887. {
  888. SetProjectState();
  889. if(PublishOk)PublishSuccess();
  890. }else
  891. UpdateProgress.set(Publish.progress.progress);
  892. WindowSetProgress(UpdateProgress());
  893. }
  894. int sleep=1000/30;
  895. if(!App.maximized())REPA(MT)if(MT.b(i))if(!MT.guiObj(i) || MT.guiObj(i)==Gui.desktop()){WindowMove(MT.pixelDelta(i).x, MT.pixelDelta(i).y); sleep=1;}
  896. Time.wait(sleep);
  897. Gui.update();
  898. Server.update(null, true); // it's very important to set 'busy' so no commands are processed during publishing
  899. if(Ms.bp(3))WindowToggle();
  900. PublishSkipOptimize .visible(PublishStage==PUBLISH_TEX_OPTIMIZE && !Publish.progress.stop);
  901. PublishPVRTCQuality .visible(PublishSkipOptimize.visible() && !PublishSkipOptimize() && PublishPVRTCUse);
  902. PublishPVRTCQualityText.visible(PublishPVRTCQuality.visible());
  903. return true;
  904. }
  905. /******************************************************************************/
  906. void DrawPublish()
  907. {
  908. D.clear(BackgroundColor());
  909. if(Publish.progress.stop)
  910. {
  911. D.text(0, 0.05f, (PublishPVRTCUse ? "Waiting for PVRTC to finish" : "Stopping"));
  912. }else
  913. {
  914. if(!UpdateThread.created())
  915. {
  916. D.text(0, 0.05f, S+"Waiting for "+Proj.worldAreasToRebuild()+" world areas to finish building");
  917. }else
  918. {
  919. Str text; switch(PublishStage)
  920. {
  921. case PUBLISH_MTRL_SIMPLIFY: text="Simplifying Materials"; break;
  922. case PUBLISH_TEX_OPTIMIZE : text=(PublishSkipOptimize() ? PublishPVRTCUse ? "Waiting for PVRTC to finish" : "Copying Textures" : "Optimizing Textures"); break;
  923. default : text=(PublishEsProj ? "Compressing Project" : "Publishing Project"); break;
  924. }
  925. D.text(0, 0.05f, text);
  926. }
  927. GuiPC gpc;
  928. gpc.visible=gpc.enabled=true;
  929. gpc.client_rect=gpc.clip.set(-D.w(), -D.h(), D.w(), D.h());
  930. gpc.offset.zero();
  931. UpdateProgress.draw(gpc);
  932. D.clip();
  933. }
  934. if(PublishPVRTCUse)
  935. {
  936. TextStyleParams ts; ts.align.set(0, -1); ts.size=0.05f; D.text(ts, Gui.desktop()->rect(), "Compressing PVRTC (iOS Texture Format) - this may take a while.\nMaking sure textures look beautiful and use little space.");
  937. }
  938. Gui.draw();
  939. }
  940. /******************************************************************************/
  941. /******************************************************************************/
  942. SyncLock ImageConvert::Lock;
  943. /******************************************************************************/
  944. void ImageGenerate::set(C Str &src_mtrl, C Str &dest_base_0, C DateTime &time) {T.src_mtrl=src_mtrl; T.dest_base_0=dest_base_0; T.time=time;}
  945. void ImageGenerate::process()
  946. {
  947. Image base_0;
  948. MaterialPtr mtrl=src_mtrl;
  949. if(mtrl && MergeBaseTextures(base_0, *mtrl)){File f; if(base_0.save(f.writeMem())){f.pos(0); SafeOverwrite(f, dest_base_0, &time);}}
  950. }
  951. void ImageConvert::set(C Str &src, C Str &dest, C DateTime &time, int type, bool ignore_alpha, bool clamp, bool mtrl_base_1, byte downsize, int max_size)
  952. {
  953. T.family=IMAGE; T.src=src; T.dest=dest; T.time=time; T.type=type; T.ignore_alpha=ignore_alpha; T.clamp=clamp; T.mtrl_base_1=mtrl_base_1; T.downsize=downsize; T.max_size=max_size;
  954. }
  955. void ImageConvert::set(C Str &src, C Str &dest, C DateTime &time, C ElmImage &data, IMAGE_TYPE type)
  956. {
  957. T.family=ELM_IMAGE; T.src=src; T.dest=dest; T.time=time; T.pow2=data.pow2(); T.mip_maps=(data.mipMaps() ? 0 : 1); T.alpha_lum=data.alphaLum(); T.has_color=data.hasColor(); T.has_alpha=data.hasAlpha3(); T.ignore_alpha=data.ignoreAlpha(); T.type=type; T.mode=data.mode; T.size=data.size;
  958. }
  959. void ImageConvert::set(C Str &src, C Str &dest, C DateTime &time, C ElmIcon &data, IMAGE_TYPE type)
  960. {
  961. T.family=ELM_IMAGE; T.src=src; T.dest=dest; T.time=time; T.pow2=false; T.has_color=data.hasColor(); T.has_alpha=data.hasAlpha(); T.type=type;
  962. }
  963. void ImageConvert::set(C Str &src, C Str &dest, C DateTime &time, C ElmImageAtlas &data, IMAGE_TYPE type)
  964. {
  965. T.family=ELM_IMAGE_ATLAS; T.src=src; T.dest=dest; T.time=time; T.type=type;
  966. }
  967. void ImageConvert::set(C Str &src, C Str &dest, C DateTime &time, C ElmFont &data, IMAGE_TYPE type)
  968. {
  969. T.family=ELM_FONT; T.src=src; T.dest=dest; T.time=time; T.type=type;
  970. }
  971. void ImageConvert::set(C Str &src, C Str &dest, C DateTime &time, C ElmPanelImage &data, IMAGE_TYPE type)
  972. {
  973. T.family=ELM_PANEL_IMAGE; T.src=src; T.dest=dest; T.time=time; T.type=type;
  974. }
  975. bool ImageConvert::SkipOptimize(int &type, DateTime &time) // skip formats which are slow to convert
  976. {
  977. if(PublishSkipOptimize())if(type==IMAGE_BC7 || type==IMAGE_PVRTC1_2 || type==IMAGE_PVRTC1_4 || type==IMAGE_ETC1 || type==IMAGE_ETC2 || type==IMAGE_ETC2_A1 || type==IMAGE_ETC2_A8){type=-1; time.decDay(); return true;} // use default type and set previous date, so the file will be regenerated next time
  978. return false;
  979. }
  980. void ImageConvert::process(C bool *stop)C
  981. {
  982. DateTime time=T.time; if(!time.valid())time=FileInfo(src).modify_time_utc; // 'time' could've been empty if the file didn't exist yet at the moment of setting up this object (this can happen for dynamically generated textures)
  983. int type=T.type; bool skip=SkipOptimize(type, time);
  984. SyncLockerEx locker(Lock, type==IMAGE_PVRTC1_2 || type==IMAGE_PVRTC1_4); // PVRTC texture compression is already multi-threaded and uses a lot of memory, so allow only one at a time
  985. if(stop && *stop)return;
  986. skip|=SkipOptimize(type, time); // call this again after the 'locker' got unlocked
  987. PublishPVRTC pvrtc(type==IMAGE_PVRTC1_2 || type==IMAGE_PVRTC1_4);
  988. switch(family)
  989. {
  990. case ELM_IMAGE:
  991. {
  992. Image image; if(image.ImportTry(src))if(EditToGameImage(image, image, pow2, alpha_lum, ElmImage::COMPRESSED, mode, mip_maps, has_color, has_alpha, ignore_alpha, size, &type))
  993. {
  994. File f; if(image.save(f.writeMem())){f.pos(0); SafeOverwrite(f, dest, &time);} // save using specified time
  995. }
  996. }break;
  997. case ELM_IMAGE_ATLAS:
  998. {
  999. ImageAtlas atlas; if(atlas.load(src))
  1000. {
  1001. FREPA(atlas.images)
  1002. {
  1003. Image &image=atlas.images[i];
  1004. VecI2 size=image.size(), old=size; if(type==IMAGE_PVRTC1_2 || type==IMAGE_PVRTC1_4)size=CeilPow2(size.max()); // image must be square for PVRTC. Unlike for regular textures, for atlases it's better to use 'CeilPow2', to make sure we don't make textures much smaller than original.
  1005. if(image.copyTry(image, size.x, size.y, -1, type, mode, mip_maps, FILTER_BEST, clamp, true))
  1006. {
  1007. #if 0 // don't do any adjustments because we may want to detect image proportions based on 'trimmed_size' (Esenthel RTS does that)
  1008. if(size!=old) // if size is different then adjust the parts
  1009. REPAD(p, atlas.parts) // iterate all parts
  1010. {
  1011. ImageAtlas.Part &part=atlas.parts[p];
  1012. if(part.image_index==i) // if this part is stored in converted image
  1013. {
  1014. part.original_size=Round(Vec2(part.original_size*size)/old);
  1015. part. trimmed_size=Round(Vec2(part. trimmed_size*size)/old);
  1016. part. trim_pos =Round(Vec2(part. trim_pos *size)/old);
  1017. part.center_offset.set(part.trim_pos.x-part.original_size.x*0.5f, -part.trim_pos.y+part.original_size.y*0.5f);
  1018. }
  1019. }
  1020. #endif
  1021. }
  1022. }
  1023. File f; if(atlas.save(f.writeMem())){f.pos(0); SafeOverwrite(f, dest, &time);} // save using specified time
  1024. }
  1025. }break;
  1026. case ELM_FONT:
  1027. {
  1028. Font font; if(font.load(src))
  1029. {
  1030. font.imageType(IMAGE_TYPE(type));
  1031. File f; if(font.save(f.writeMem())){f.pos(0); SafeOverwrite(f, dest, &time);} // save using specified time
  1032. }
  1033. }break;
  1034. case ELM_PANEL_IMAGE:
  1035. {
  1036. PanelImage panel_image; if(panel_image.load(src))
  1037. {
  1038. VecI2 size=panel_image.image.size(); if(type==IMAGE_PVRTC1_2 || type==IMAGE_PVRTC1_4)size=CeilPow2(size.max()); // image must be square for PVRTC. Unlike for regular textures, for panel images it's better to use 'CeilPow2', to make sure we don't make textures much smaller than original.
  1039. panel_image.image.copyTry(panel_image.image, size.x, size.y, -1, type, -1, -1, FILTER_BEST, true, true);
  1040. File f; if(panel_image.save(f.writeMem())){f.pos(0); SafeOverwrite(f, dest, &time);} // save using specified time
  1041. }
  1042. }break;
  1043. case IMAGE:
  1044. {
  1045. if(skip) // just copy
  1046. {
  1047. locker.on(); // when just copying then limit to only one thread
  1048. File f; if(f.readTry(src))SafeOverwrite(f, dest, &time);
  1049. }else
  1050. if(C ImagePtr &image=src)
  1051. {
  1052. Image temp, *s=image();
  1053. VecI size=s->size3(); if(type==IMAGE_PVRTC1_2 || type==IMAGE_PVRTC1_4)size.xy=NearestPow2(size.xy.avgI()); // image must be square for PVRTC
  1054. if(downsize) // downsize
  1055. {
  1056. size.x=Max(1, size.x>>downsize);
  1057. size.y=Max(1, size.y>>downsize);
  1058. size.z=Max(1, size.z>>downsize);
  1059. }
  1060. if(max_size>0) // limit size
  1061. {
  1062. MIN(size.x, max_size);
  1063. MIN(size.y, max_size);
  1064. MIN(size.z, max_size);
  1065. }
  1066. int mip_maps=T.mip_maps, mode=T.mode;
  1067. if(ignore_alpha && NeedFixAlpha(*s, IMAGE_TYPE(type))) // if we won't need alpha
  1068. {
  1069. if(mip_maps<0)mip_maps=((s->mipMaps()==1) ? 1 : 0); // source will have now only one mip-map so we can't use "-1", auto-detect instead
  1070. if(mode <0)mode =s->mode(); // source will now be as IMAGE_SOFT so we can't use "-1", auto-detect instead
  1071. if(type <0)type =s->type(); // source will now be as IMAGE_R8G8B8 so we can't use "-1", auto-detect instead
  1072. if(s->copyTry(temp, -1, -1, -1, IMAGE_R8G8B8, IMAGE_SOFT, 1))s=&temp;
  1073. }
  1074. if(s->copyTry(temp, size.x, size.y, size.z, type, mode, mip_maps, FILTER_BEST, clamp, false, false, mtrl_base_1))
  1075. {
  1076. File f; if(temp.save(f.writeMem())){f.pos(0); SafeOverwrite(f, dest, &time);} // save using specified time
  1077. }
  1078. }
  1079. }break;
  1080. }
  1081. }
  1082. void PublishResult::OK(PublishResult &pr) {pr.del();}
  1083. void PublishResult::SizeStats(PublishResult &pr) {pr.del(); ::SizeStats.display(pr.path, Publish.cipher());}
  1084. void PublishResult::display(C Str &text)
  1085. {
  1086. path=(PublishProjectDataPath.is() ? PublishProjectDataPath : PublishBinPath);
  1087. Gui+=::EE::Window::create(Rect_C(0, 0, 1, 0.34f), "Publishing succeeded"); button[2].show();
  1088. T+=ok .create(Rect_D(clientWidth()*(PublishEsProj ? 2 : 1)/4, -clientHeight()+0.04f, 0.3f, 0.06f), "OK" ).func(OK , T);
  1089. T+=size_stats.create(Rect_D(clientWidth()* 3 /4, -clientHeight()+0.04f, 0.3f, 0.06f), "Size Stats").func(SizeStats, T).hidden(PublishEsProj);
  1090. T+=T.text .create(Rect(0.02f, ok.rect().max.y, clientWidth()-0.02f, 0), text);
  1091. activate();
  1092. }
  1093. PublishPVRTC::PublishPVRTC(bool on) : on(on) {if(on)AtomicInc(PublishPVRTCUse);}
  1094. PublishPVRTC::~PublishPVRTC( ) {if(on)AtomicDec(PublishPVRTCUse);}
  1095. void PublishClass::ExportData(C Str &name, PublishClass &publish) {StartPublish(S, publish.export_data_exe, Edit::BUILD_PUBLISH, true, name);}
  1096. void PublishClass::create()
  1097. {
  1098. export_data.create("pak", S, S, ExportData, ExportData, T);
  1099. }
  1100. void PublishClass::exportData(Edit::EXE_TYPE exe) {export_data_exe=exe; export_data.save();}
  1101. Texture& Texture::downSize(int size) {MAX(downsize, size); return T;}
  1102. int Texture::CompareTex(C Texture &tex, C UID &tex_id) {return Compare(tex.id, tex_id);}
  1103. ImageConvert::ImageConvert() : pow2(false), clamp(true ), alpha_lum(false), has_color(true ), has_alpha(true ), ignore_alpha(false), mtrl_base_1(false), downsize( 0), mip_maps( -1), max_size( 0), size( 0), family(ELM_IMAGE), type(-1), mode(-1) {}
  1104. PublishClass::PublishClass() : export_data_exe(Edit::EXE_EXE) {}
  1105. Texture::Texture() : src_tex_id(UIDZero), uses_alpha(false), keep_hq(false), mtrl_base_1(false), regenerate(false), downsize(0) {}
  1106. /******************************************************************************/