Convert To De-Atlas.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. /******************************************************************************/
  4. ConvertToDeAtlasClass ConvertToDeAtlas;
  5. /******************************************************************************/
  6. /******************************************************************************/
  7. cchar8 *ConvertToDeAtlasClass::mode_t[]=
  8. {
  9. "Create as new objects (use for testing)",
  10. "Replace existing objects (and keep Original Elements)",
  11. "Replace existing objects (and Disable Publishing of Original Elements)",
  12. "Replace existing objects (and Remove Original Elements)",
  13. };
  14. /******************************************************************************/
  15. void ConvertToDeAtlasClass::Preview::draw(C GuiPC &gpc)
  16. {
  17. if(visible() && gpc.visible)
  18. {
  19. D.clip(gpc.clip);
  20. Rect r=rect()+gpc.offset;
  21. if(ImagePtr img=Proj.texPath(ConvertToDeAtlas.base_0_tex))
  22. {
  23. r=img->fit(r);
  24. ALPHA_MODE alpha=D.alpha(ALPHA_NONE);
  25. img->draw(r);
  26. Rect sr=ConvertToDeAtlas.source_rect; sr/=Vec2(ConvertToDeAtlas.tex_size);
  27. sr.min.y=1-sr.min.y;
  28. sr.max.y=1-sr.max.y;
  29. sr.min=r.lerp(sr.min);
  30. sr.max=r.lerp(sr.max);
  31. sr.draw(RED, false);
  32. D.alpha(alpha);
  33. }
  34. r.draw(Gui.borderColor(), false);
  35. }
  36. }
  37. Str ConvertToDeAtlasClass::TexSize(C VecI2 &size) {return S+size.x+'x'+size.y;}
  38. void ConvertToDeAtlasClass::Convert(ConvertToDeAtlasClass &cta) {cta.convertDo();}
  39. void ConvertToDeAtlasClass::convertMeshes(Memc<IDReplace> &mtrl_replace, C Rect *frac)
  40. {
  41. Vec2 mul, add; if(frac){mul=1/frac->size(); add=-frac->min*mul;}
  42. Memc<UID> duplicated; Proj.duplicate(objs, duplicated, (mode==NEW) ? " (De-Atlas)" : " (De-Atlas Backup)");
  43. // objs duplicated
  44. // NEW atlas
  45. // REPLACE backup
  46. if(mode==NEW)Swap(duplicated, objs);
  47. Memc<UID> &deatlased=objs, &non_deatlased=duplicated;
  48. REPA(deatlased)if(Elm *obj=Proj.findElm(deatlased[i]))if(ElmObj *obj_data=obj->objData())
  49. if(Elm *mesh_elm=Proj.findElm(obj_data->mesh_id, ELM_MESH))
  50. {
  51. if(ObjEdit.mesh_elm==mesh_elm)ObjEdit.flushMeshSkel();
  52. Mesh mesh; if(Load(mesh, Proj.editPath(mesh_elm->id), Proj.game_path))
  53. {
  54. REPD(l, mesh.lods())
  55. {
  56. MeshLod &lod=mesh.lod(l); REPA(lod)
  57. {
  58. MeshPart &part=lod.parts[i];
  59. bool changed_part=false, changed_multi_mtrl=false;
  60. MaterialPtr mtrls[4]; REPA(mtrls)
  61. {
  62. MaterialPtr &mtrl=mtrls[i];
  63. mtrl=part.multiMaterial(i);
  64. if(C IDReplace *id=ReplaceID(mtrl.id(), mtrl_replace)){changed_multi_mtrl=true; mtrl=Proj.gamePath(id->to);}
  65. }
  66. if(changed_multi_mtrl)
  67. {
  68. changed_part=true;
  69. part.multiMaterial(mtrls[0], mtrls[1], mtrls[2], mtrls[3]);
  70. }
  71. REP(part.variations())if(i)
  72. if(C IDReplace *id=ReplaceID(part.variation(i).id(), mtrl_replace)){changed_part=true; part.variation(i, Proj.gamePath(id->to));}
  73. if(changed_part)
  74. {
  75. MeshBase &base=part.base; if(frac && base.vtx.tex0())
  76. {
  77. base.fixTexOffset();
  78. REPA(base.vtx)
  79. {
  80. Vec2 &t=base.vtx.tex0(i);
  81. t*=mul; t+=add;
  82. }
  83. }
  84. }
  85. }
  86. }
  87. if(ElmMesh *mesh_data=mesh_elm->meshData()){mesh_data->newVer(); mesh_data->file_time.getUTC();}
  88. Save(mesh, Proj.editPath(mesh_elm->id), Proj.game_path);
  89. Proj.makeGameVer(*mesh_elm);
  90. Server.setElmLong(mesh_elm->id);
  91. if(ObjEdit.mesh_elm==mesh_elm)ObjEdit.reloadMeshSkel();
  92. }
  93. }
  94. // adjust parents, publishing and removed status
  95. TimeStamp time; time.getUTC();
  96. // process objects
  97. if(non_deatlased.elms()==deatlased.elms())
  98. {
  99. REPA(non_deatlased)if(Elm *elm=Proj.findElm(non_deatlased[i]))
  100. {
  101. if(mode==REPLACE_NO_PUBLISH || mode==REPLACE_REMOVE){elm->setParent(deatlased[i], time); Server.setElmParent(*elm);} // move 'non_deatlased' to 'deatlased'
  102. if(mode==REPLACE_NO_PUBLISH)elm->setNoPublish(true, time);else
  103. if(mode==REPLACE_REMOVE )elm->setRemoved (true, time);
  104. }
  105. if(mode==REPLACE_NO_PUBLISH)Server.noPublishElms(non_deatlased, true, time);else
  106. if(mode==REPLACE_REMOVE )Server.removeElms (non_deatlased, true, time);
  107. /*if(mode!=NEW) // move materials to old ones, from 'deatlased' -> 'non_deatlased'
  108. {
  109. REPA(mtrls)if(Elm *mtrl=Proj.findElm(mtrls[i]))
  110. {
  111. int index; if(deatlased.binarySearch(mtrl.parent_id, index, Compare)) // if is located in one of 'deatlased'
  112. {
  113. mtrl.setParent(non_deatlased[index], time); Server.setElmParent(*mtrl);
  114. }
  115. }
  116. }*/
  117. }
  118. if(deatlased.elms()==1) // if created only one object, then move all materials to it
  119. REPA(mtrl_replace)if(Elm *mtrl=Proj.findElm(mtrl_replace[i].to))
  120. {
  121. mtrl->setParent(deatlased[0], time); Server.setElmParent(*mtrl);
  122. }
  123. if(mode==REPLACE_NO_PUBLISH || mode==REPLACE_REMOVE) // check if we can remove/unpublish old materials
  124. {
  125. Proj.setList(); // first we need to reset the list to set 'finalRemoved' and 'finalPublish'
  126. Memc<UID> used_mtrls_publish, used_mtrls_exist, remove, unpublish;
  127. Proj.getUsedMaterials(used_mtrls_publish, true ); // get list of materials used by publishable elements
  128. if(mode==REPLACE_REMOVE)Proj.getUsedMaterials(used_mtrls_exist , false); // get list of materials used by existing elements
  129. REPA(mtrls)
  130. {
  131. C UID &mtrl_id=mtrls[i];
  132. if(mode==REPLACE_REMOVE && !used_mtrls_exist .binaryHas(mtrl_id, Compare))remove .add(mtrl_id);else // first check if we can remove , no other existing element uses this material
  133. if( !used_mtrls_publish.binaryHas(mtrl_id, Compare))unpublish.add(mtrl_id); // then check if at least we can unpublish, no other publishable element uses this material
  134. }
  135. Proj.remove (remove , false);
  136. Proj.disablePublish(unpublish, false);
  137. }
  138. }
  139. Str ConvertToDeAtlasClass::Process(C Str &name, C VecI2 &size, C Rect *crop, C VecI2 *resize)
  140. {
  141. if(name.is() && (crop || resize)) // don't add params to an empty file name
  142. {
  143. Mems<Edit::FileParams> files=Edit::FileParams::Decode(name);
  144. if(files.elms())
  145. {
  146. if(files.elms()>1)files.New(); // if there's more than 1 file, then add crop/resize parameters globally
  147. if(crop) // crop first
  148. {
  149. RectI r=Round(*crop*Vec2(size));
  150. files.last().params.New().set("crop", S+r.min.x+','+r.min.y+','+r.w()+','+r.h());
  151. }
  152. if(resize)files.last().params.New().set("resizeClamp", VecI2AsText(*resize)); // then resize
  153. return Edit::FileParams::Encode(files);
  154. }
  155. }
  156. return name;
  157. }
  158. void ConvertToDeAtlasClass::convertDo()
  159. {
  160. // convert before hiding because that may release resources
  161. if(mtrls.elms()) // first create textures
  162. {
  163. Proj.clearListSel();
  164. REPA(mtrls)MtrlEdit.flush(mtrls[i]); // flush first
  165. EditMaterial src_edit; src_edit.load(Proj.editPath(mtrls[0]));
  166. MtrlImages src; src.fromMaterial(src_edit, Proj, false);
  167. VecI2 color_size=src.color.size(), alpha_size=src.alpha.size(), bump_size=src.bump.size(), normal_size=src.normal.size(), spec_size=src.specular.size(), glow_size=src.glow.size();
  168. Rect frac=Rect(source_rect)/Vec2(tex_size);
  169. src.crop(frac);
  170. VecI2 final_size=finalSize();
  171. src.resize(final_size);
  172. Image base_0, base_1; src.createBaseTextures(base_0, base_1);
  173. IMAGE_TYPE ct; ImageProps(base_0, &base_0_tex, &ct, ForceHQMtrlBase0 ? FORCE_HQ : 0); if(Proj.includeTex(base_0_tex)){base_0.copyTry(base_0, -1, -1, -1, ct, IMAGE_2D, 0, FILTER_BEST, false, false, false, false); Proj.saveTex(base_0, base_0_tex);} Server.setTex(base_0_tex);
  174. ImageProps(base_1, &base_1_tex, &ct, ForceHQMtrlBase1 ? FORCE_HQ : 0); if(Proj.includeTex(base_1_tex)){base_1.copyTry(base_1, -1, -1, -1, ct, IMAGE_2D, 0, FILTER_BEST, false, false, false, true ); Proj.saveTex(base_1, base_1_tex);} Server.setTex(base_1_tex);
  175. C Rect *crop =((source_rect.min.any() || source_rect.max!=tex_size) ? &frac : null);
  176. C VecI2 *resize=((dest_size.x>0 || dest_size.y>0 ) ? &final_size : null);
  177. Memc<IDReplace> mtrl_replace;
  178. REPA(mtrls)if(Elm *elm_mtrl=Proj.findElm(mtrls[i]))
  179. {
  180. Elm &de_atlas=Proj.Project::newElm(); de_atlas.type=ELM_MTRL;
  181. de_atlas.copyParams(*elm_mtrl).setName(elm_mtrl->name+" (De-Atlas)").setRemoved(false); // call 'setName' after 'copyParams'
  182. EditMaterial edit; edit.load(Proj.editPath(elm_mtrl->id));
  183. edit. color_map=Process(src_edit. color_map, color_size, crop, resize);
  184. edit. alpha_map=Process(src_edit. alpha_map, alpha_size, crop, resize);
  185. edit. bump_map=Process(src_edit. bump_map, bump_size, crop, resize);
  186. edit. normal_map=Process(src_edit. normal_map, normal_size, crop, resize);
  187. edit.specular_map=Process(src_edit.specular_map, spec_size, crop, resize);
  188. edit. glow_map=Process(src_edit. glow_map, glow_size, crop, resize);
  189. edit.base_0_tex=base_0_tex;
  190. edit.base_1_tex=base_1_tex;
  191. if(ElmMaterial *mtrl_data=de_atlas.mtrlData())mtrl_data->from(edit);
  192. Save(edit, Proj.editPath(de_atlas.id));
  193. Proj.makeGameVer(de_atlas);
  194. Server.setElmFull(de_atlas.id);
  195. mtrl_replace.New().set(elm_mtrl->id, de_atlas.id);
  196. }
  197. mtrl_replace.sort(IDReplace::Compare);
  198. convertMeshes(mtrl_replace, crop);
  199. Proj.setList();
  200. }
  201. hide();
  202. }
  203. void ConvertToDeAtlasClass::clearProj()
  204. {
  205. objs.clear(); mtrls.clear(); base_0_tex.zero(); base_1_tex.zero();
  206. }
  207. void ConvertToDeAtlasClass::create()
  208. {
  209. Property &mode=add("De-Atlased Objects", MEMBER(ConvertToDeAtlasClass, mode)).setEnum(mode_t, Elms(mode_t)).desc("Existing object meshes need to have their UV adjusted.\nWith this option you can control if the adjusted objects:\n-Replace the old ones (keeping their Element ID)\nor\n-They are created as new objects (with new Element ID)");
  210. add("Source Left" , MEMBER(ConvertToDeAtlasClass, source_rect.min.x));
  211. add("Source Right" , MEMBER(ConvertToDeAtlasClass, source_rect.max.x));
  212. add("Source Top" , MEMBER(ConvertToDeAtlasClass, source_rect.min.y));
  213. add("Source Bottom" , MEMBER(ConvertToDeAtlasClass, source_rect.max.y));
  214. sw=&add();
  215. sh=&add();
  216. add("Force Width" , MEMBER(ConvertToDeAtlasClass, dest_size.x)).min(-1);
  217. add("Force Height" , MEMBER(ConvertToDeAtlasClass, dest_size.y)).min(-1);
  218. w=&add();
  219. h=&add();
  220. Rect r=::PropWin::create("Extract from Atlas", Vec2(0.02f, -0.02f), 0.036f, 0.043f, 0.2f); button[2].func(HideProjAct, SCAST(GuiObj, T)).show(); mode.combobox.resize(Vec2(0.73f, 0));
  221. autoData(this);
  222. T+=preview.create(Rect_LU(r.ru()+Vec2(0.02f, -0.06f), 1.4f));
  223. T+=t_tex_size.create(preview.rect().ru()+Vec2(-0.15f, 0.02f));
  224. T+=convert.create(Rect_U (r.down()-Vec2(0, 0.05f), 0.3f, 0.055f), "Convert").func(Convert, T);
  225. Vec2 size(preview.rect().max.x, Max(-convert.rect().min.y, -preview.rect().min.y));
  226. rect(Rect_C(0, size+0.02f+defaultInnerPaddingSize()));
  227. }
  228. VecI2 ConvertToDeAtlasClass::finalSize()C {return ImageSize(VecI(source_rect.size(), 1), dest_size, true).xy;}
  229. void ConvertToDeAtlasClass::setElms(C MemPtr<UID> &elm_ids)
  230. {
  231. clearProj();
  232. Str mtrl_color_map;
  233. FREPA(elm_ids) // process in order so materials are chosen from object that was selected first
  234. if(Elm *elm=Proj.findElm(elm_ids[i]))if(ElmObj *obj_data=elm->objData())if(Elm *mesh_elm=Proj.findElm(obj_data->mesh_id))if(ElmMesh *mesh_data=mesh_elm->meshData())
  235. {
  236. bool include_obj=false;
  237. REPA(mesh_data->mtrl_ids)if(Elm *elm_mtrl=Proj.findElm(mesh_data->mtrl_ids[i]))if(ElmMaterial *mtrl_data=elm_mtrl->mtrlData())
  238. if(mtrl_data->base_0_tex.valid() || mtrl_data->base_1_tex.valid())
  239. {
  240. if(!base_0_tex.valid() && !base_1_tex.valid())
  241. {
  242. EditMaterial mtrl; Proj.mtrlGet(elm_mtrl->id, mtrl); mtrl_color_map=mtrl.color_map;
  243. base_0_tex=mtrl_data->base_0_tex;
  244. base_1_tex=mtrl_data->base_1_tex;
  245. }
  246. if(mtrl_data->base_0_tex==base_0_tex
  247. && mtrl_data->base_1_tex==base_1_tex)
  248. {
  249. mtrls.binaryInclude(elm_mtrl->id, Compare);
  250. include_obj=true;
  251. }
  252. }
  253. if(include_obj)objs.binaryInclude(elm->id, Compare);
  254. }
  255. if(!objs.elms())Gui.msgBox(S, "No Objects/Materials to process");else
  256. {
  257. // get 'tex_size'
  258. tex_size=0;
  259. Image temp; if(Proj.loadImages(temp, mtrl_color_map))tex_size=temp.size(); // first try loading from source image, because it may not be pow2, and we want exact size
  260. if(!tex_size.all())if(ImagePtr base_0=Proj.texPath(base_0_tex))tex_size=base_0->size();
  261. t_tex_size.set(TexSize(tex_size));
  262. bool used_tex=false;
  263. Rect used_tex_rect(0, 0);
  264. REPA(objs)if(Elm *elm=Proj.findElm(objs[i]))if(ElmObj *obj_data=elm->objData())if(Elm *mesh_elm=Proj.findElm(obj_data->mesh_id))if(ElmMesh *mesh_data=mesh_elm->meshData())
  265. {
  266. Mesh mesh; if(ObjEdit.mesh_elm==mesh_elm)mesh.create(ObjEdit.mesh);else Load(mesh, Proj.editPath(mesh_elm->id), Proj.game_path); // load edit so we can have access to MeshBase
  267. REPD(l, mesh.lods())
  268. {
  269. C MeshLod &lod=mesh.lod(l); REPA(lod)
  270. {
  271. C MeshPart &part=lod.parts[i];
  272. C MeshBase &base=part.base; if(base.vtx.tex0())REP(4)
  273. {
  274. UID mtrl_id=part.multiMaterial(i).id(); if(mtrl_id.valid() && mtrls.binaryHas(mtrl_id, Compare))
  275. {
  276. REPA(base.tri)
  277. {
  278. C VecI &ind=base.tri.ind(i);
  279. Rect face_tex=Tri2(base.vtx.tex0(ind.x), base.vtx.tex0(ind.y), base.vtx.tex0(ind.z));
  280. face_tex-=Floor(face_tex.min);
  281. Include(used_tex_rect, used_tex, face_tex);
  282. }
  283. REPA(base.quad)
  284. {
  285. C VecI4 &ind=base.quad.ind(i);
  286. Rect face_tex=Quad2(base.vtx.tex0(ind.x), base.vtx.tex0(ind.y), base.vtx.tex0(ind.z), base.vtx.tex0(ind.w));
  287. face_tex-=Floor(face_tex.min);
  288. Include(used_tex_rect, used_tex, face_tex);
  289. }
  290. break;
  291. }
  292. }
  293. }
  294. }
  295. }
  296. if(used_tex)source_rect.set(Floor(used_tex_rect.min*(Vec2)tex_size), Ceil(used_tex_rect.max*(Vec2)tex_size));else source_rect.zero();
  297. toGui();
  298. activate();
  299. }
  300. }
  301. void ConvertToDeAtlasClass::drag(Memc<UID> &elms, GuiObj *focus_obj, C Vec2 &screen_pos)
  302. {
  303. }
  304. ConvertToDeAtlasClass& ConvertToDeAtlasClass::hide()
  305. {
  306. ::PropWin::hide();
  307. clearProj(); // release memory
  308. return T;
  309. }
  310. void ConvertToDeAtlasClass::update(C GuiPC &gpc)
  311. {
  312. ::EE::ClosableWindow::update(gpc);
  313. if(visible() && gpc.visible)
  314. {
  315. VecI2 size=finalSize();
  316. if(sw)sw->name.set(S+"Source Width: " +source_rect.w());
  317. if(sh)sh->name.set(S+"Source Height: "+source_rect.h());
  318. if(w)w->name.set(S+"Width: " +size.x);
  319. if(h)h->name.set(S+"Height: "+size.y);
  320. }
  321. }
  322. ConvertToDeAtlasClass::ConvertToDeAtlasClass() : base_0_tex(UIDZero), base_1_tex(UIDZero), mode(NEW), source_rect(0, 0, 0, 0), dest_size(0), tex_size(0), w(null), h(null), sw(null), sh(null) {}
  323. /******************************************************************************/