[email protected] 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. /******************************************************************************/
  4. Mems<int> ElmOrderArray;
  5. int ElmOrder(ELM_TYPE type) {return InRange(type, ElmOrderArray) ? ElmOrderArray[type] : ElmOrderArray.elms()+type;}
  6. void InitElmOrder()
  7. {
  8. Memt<ELM_TYPE> elms;
  9. elms.add(ELM_NONE );
  10. elms.add(ELM_FOLDER );
  11. elms.add(ELM_ENUM );
  12. elms.add(ELM_IMAGE );
  13. elms.add(ELM_IMAGE_ATLAS);
  14. elms.add(ELM_FONT );
  15. elms.add(ELM_TEXT_STYLE );
  16. elms.add(ELM_PANEL_IMAGE);
  17. elms.add(ELM_PANEL );
  18. elms.add(ELM_GUI_SKIN );
  19. elms.add(ELM_GUI );
  20. elms.add(ELM_SHADER );
  21. elms.add(ELM_MTRL );
  22. elms.add(ELM_WATER_MTRL );
  23. elms.add(ELM_PHYS_MTRL );
  24. elms.add(ELM_ANIM );
  25. elms.add(ELM_OBJ_CLASS );
  26. elms.add(ELM_OBJ );
  27. elms.add(ELM_MESH );
  28. elms.add(ELM_SKEL );
  29. elms.add(ELM_PHYS );
  30. elms.add(ELM_ICON_SETTS );
  31. elms.add(ELM_ICON );
  32. elms.add(ELM_ENV );
  33. elms.add(ELM_WORLD );
  34. elms.add(ELM_MINI_MAP );
  35. elms.add(ELM_SOUND );
  36. elms.add(ELM_VIDEO );
  37. elms.add(ELM_FILE );
  38. elms.add(ELM_CODE );
  39. elms.add(ELM_LIB );
  40. elms.add(ELM_APP );
  41. FREP(ELM_NUM)elms.include(ELM_TYPE(i)); // include elements that were not listed above (just in case)
  42. ElmOrderArray.setNum(elms.elms()); REPA(elms)ElmOrderArray(elms[i])=i;
  43. }
  44. /******************************************************************************/
  45. TexInfoGetter TIG;
  46. /******************************************************************************/
  47. /******************************************************************************/
  48. int ListElm::CompareIndex(C ListElm &a, C ListElm &b) {return Compare(ptr(&a), ptr(&b));}
  49. int ListElm::CompareName(C ListElm &a, C ListElm &b)
  50. {
  51. MemtN<C ListElm*, 128> as, bs;
  52. for(C ListElm *p=&a; ; ){as.add(p); p=p->vis_parent; if(!p)break;}
  53. for(C ListElm *p=&b; ; ){bs.add(p); p=p->vis_parent; if(!p)break;}
  54. int shared_parents=Min(as.elms(), bs.elms());
  55. FREP(shared_parents)
  56. {
  57. C ListElm &a=*as[as.elms()-1-i],
  58. &b=*bs[bs.elms()-1-i];
  59. if(int c=CompareNumber(a.name, b.name))return c;
  60. if(int c=CompareIndex (a , b ))return c;
  61. }
  62. return CompareIndex(a, b); // compare by index instead of returning 0, because only shared parents were checked, if 'a' is child of 'b' then we need to make sure that 'a' is listed after
  63. }
  64. int ListElm::CompareSize(C ListElm &a, C ListElm &b)
  65. {
  66. MemtN<C ListElm*, 128> as, bs;
  67. for(C ListElm *p=&a; ; ){as.add(p); p=p->vis_parent; if(!p)break;}
  68. for(C ListElm *p=&b; ; ){bs.add(p); p=p->vis_parent; if(!p)break;}
  69. int shared_parents=Min(as.elms(), bs.elms());
  70. FREP(shared_parents)
  71. {
  72. C ListElm &a=*as[as.elms()-1-i],
  73. &b=*bs[bs.elms()-1-i];
  74. if(int c=Compare (b.fileSize(), a.fileSize()))return c; // swap order, because most likely we'll be interested in biggest file sizes
  75. if(int c=CompareIndex(a , b ))return c;
  76. }
  77. return CompareIndex(a, b); // compare by index instead of returning 0, because only shared parents were checked, if 'a' is child of 'b' then we need to make sure that 'a' is listed after
  78. }
  79. int ListElm::CompareTexSharp(C ListElm &a, C ListElm &b)
  80. {
  81. MemtN<C ListElm*, 128> as, bs;
  82. for(C ListElm *p=&a; ; ){as.add(p); p=p->vis_parent; if(!p)break;}
  83. for(C ListElm *p=&b; ; ){bs.add(p); p=p->vis_parent; if(!p)break;}
  84. int shared_parents=Min(as.elms(), bs.elms());
  85. FREP(shared_parents)
  86. {
  87. C ListElm &a=*as[as.elms()-1-i],
  88. &b=*bs[bs.elms()-1-i];
  89. if(int c=Compare (a.texSharpness(), b.texSharpness()))return c;
  90. if(int c=CompareIndex(a , b ))return c;
  91. }
  92. return CompareIndex(a, b); // compare by index instead of returning 0, because only shared parents were checked, if 'a' is child of 'b' then we need to make sure that 'a' is listed after
  93. }
  94. Str ListElm::Size(C ListElm &data)
  95. {
  96. long size=data.fileSize(); if(!size)return S;
  97. Str s=FileSize(size); if(!data.size_known)s+='+';
  98. return s;
  99. }
  100. Str ListElm::TexSharp(C ListElm &data) {flt sharpness=data.texSharpness(); if(sharpness<2)return sharpness; return S;}
  101. void ListElm::IncludeTex(Memt<UID> &texs, C UID &tex_id) {if(tex_id.valid())texs.binaryInclude(tex_id, Compare);}
  102. void ListElm::IncludeTex(Memt<UID> &texs, C Elm &elm)
  103. {
  104. // material
  105. if(C ElmMaterial *mtrl_data=elm.mtrlData())
  106. {
  107. IncludeTex(texs, mtrl_data->base_0_tex);
  108. IncludeTex(texs, mtrl_data->base_1_tex);
  109. }
  110. // object
  111. if(Proj.list.include_texture_size_in_object)
  112. if(C ElmObj *obj_data=elm.objData())if(C Elm *mesh=Proj.findElm(obj_data->mesh_id))if(C ElmMesh *mesh_data=mesh->meshData()) // check for Obj->Mesh, and not directly Mesh, because ELM_MESH are always hidden, and wouldn't be processed for ICS_NEVER
  113. REPA(mesh_data->mtrl_ids)if(C Elm *mtrl=Proj.findElm(mesh_data->mtrl_ids[i]))IncludeTex(texs, *mtrl);
  114. }
  115. void ListElm::IncludeTex(Memt<UID> &texs, C ElmNode &node)
  116. {
  117. FREPA(node.children)
  118. {
  119. int child_i=node.children[i];
  120. C Elm &elm=Proj.elms[child_i];
  121. if( !elm.removed() || Proj.show_removed())
  122. if( elm.publish() || Proj.list.include_unpublished_elm_size) // can use 'publish' instead of 'finalPublish' because if this function is called, then the parent was already checked
  123. {
  124. IncludeTex(texs, elm);
  125. IncludeTex(texs, Proj.hierarchy[child_i]); // we shouldn't check for ICS_ALWAYS or ICS_FOLDED here
  126. }
  127. }
  128. }
  129. void ListElm::calcTexSize() // because texture size calculation is slow, it is calculated only on demand, it is slow because first we need to get all unique texture ID's, and then sum sizes of those textures, if we would sum all encountered texture ID's then we would get bigger values because the same texture ID's could be encountered multiple times
  130. {
  131. if(!tex_size_calculated)
  132. {
  133. tex_size_calculated=true;
  134. if(Proj.list.its && elm)
  135. if(elm->finalPublish() || Proj.list.include_unpublished_elm_size) // have to use 'finalPublish' because it's not called recursively, but can be called for any element at any time
  136. {
  137. Memt<UID> texs;
  138. IncludeTex(texs, *elm);
  139. if(Proj.list.ics)
  140. if(Proj.list.ics==Proj.list.ICS_ALWAYS || !(elm->opened() || Proj.list.list_all_children)) // ICS_ALWAYS or ICS_FOLDED
  141. {
  142. int i=Proj.elms.validIndex(elm); if(InRange(i, Proj.hierarchy))IncludeTex(texs, Proj.hierarchy[i]);
  143. }
  144. REPA(texs)includeValidTexSize(texs[i]);
  145. }
  146. }
  147. }
  148. long ListElm::fileSize()C {ConstCast(T).calcTexSize(); return size;}
  149. flt ListElm::texSharpness()C
  150. {
  151. flt sharpness=3;
  152. if(elm)if(C ElmMaterial *mtrl_data=elm->mtrlData())
  153. {
  154. if(mtrl_data->base_0_tex.valid())if(C TextureInfo *tex_info=TexInfos.find(mtrl_data->base_0_tex))MIN(sharpness, tex_info->sharpness);
  155. //if(mtrl_data.base_1_tex.valid())if(C TextureInfo *tex_info=TexInfos.find(mtrl_data.base_1_tex))MIN(sharpness, tex_info.sharpness); ignore base1
  156. }
  157. return sharpness;
  158. }
  159. void ListElm::resetColor() {color=color_temp;}
  160. void ListElm::highlight() {color.g=255;}
  161. bool ListElm::hasVisibleChildren()C {return opened_icon!=null;}
  162. void ListElm::hasVisibleChildren(bool has, bool opened)
  163. {
  164. if(!has )opened_icon=null;else
  165. if(opened)opened_icon=Proj.arrow_down ();else
  166. opened_icon=Proj.arrow_right();
  167. }
  168. bool ListElm::close()
  169. {
  170. if(hasVisibleChildren())
  171. {
  172. if(item && item->opened ){item->opened=false ; return true;}
  173. if(elm && elm ->opened()){elm ->opened(false); return true;}
  174. }
  175. return false;
  176. }
  177. void ListElm::includeValidTexSize(C UID &tex_id) // assumes that 'tex_id' is valid
  178. {
  179. if(C TextureInfo *tex_info=TexInfos.find(tex_id))if(tex_info->knownFileSize()){size+=tex_info->file_size; return;}
  180. size_known=false;
  181. }
  182. void ListElm::includeSize(Elm &elm)
  183. {
  184. if(Proj.list.its!=Proj.list.ITS_TEX) // add element size
  185. {
  186. if(elm.file_size<0) // if element file size is unknown
  187. {
  188. // TODO: what about 'ElmInFolder' (Worlds and MiniMaps)
  189. if(!ElmGame(elm.type))elm.file_size=0; // if doesn't have a game file, then currently we won't detect it, so set it as known
  190. }
  191. if(elm.file_size<0)size_known=false; // if at least one element has unknown size, then mark this as unknown too
  192. else size+=elm.file_size; // increase the size
  193. }
  194. }
  195. void ListElm::includeSize(C ListElm &src)
  196. {
  197. size_known&=src.size_known;
  198. size +=src.size;
  199. }
  200. ListElm& ListElm::set(ELM_TYPE type, C Str &name, bool edited, bool importing, bool removed, int depth, int vis_parent)
  201. {
  202. T.name =name;
  203. T.depth =depth;
  204. T.offset=(Proj.list.flat_is ? 0 : depth*Proj.list.textSizeActual()*0.6f);
  205. T.vis_parent_i=vis_parent;
  206. // icon
  207. icon=Proj.elmIcon(type)();
  208. // color
  209. if(edited )color=(importing ? PURPLE : RED);else if(importing)color.set(0, 128, 255, 255);else if(TextStyle *text_style=Proj.list.getTextStyle())color=text_style->color;else color=BLACK;
  210. if(removed)color.a=128;
  211. color_temp=color; // remember color in temp for fast restoring in 'elmHighlight'
  212. return T;
  213. }
  214. ListElm& ListElm::set(Elm &elm, ElmNode &node, int depth, int vis_parent, bool parent_removed)
  215. {
  216. T.elm=&elm;
  217. return set(elm.type, elm.name, FlagTest(node.flag, ELM_EDITED), elm.importing(), elm.removed() || parent_removed, depth, vis_parent);
  218. }
  219. ListElm& ListElm::set(EEItem &item, bool opened, int depth, bool parent_removed)
  220. {
  221. T.item=&item;
  222. hasVisibleChildren(item.children.elms()>0, opened);
  223. return set(item.type, item.base_name, FlagTest(item.flag, ELM_EDITED), false, parent_removed, depth, -1);
  224. }
  225. TexInfoGetter::~TexInfoGetter() {stopAndWait();}
  226. void TexInfoGetter::stop() {BackgroundThreads.cancelFunc(CalcTexSharpness);}
  227. void TexInfoGetter::stopAndWait()
  228. {
  229. stop();
  230. BackgroundThreads.waitFunc(CalcTexSharpness);
  231. tex_path .clear();
  232. tex_to_process .clear();
  233. tex_to_process1.clear();
  234. }
  235. int TexInfoGetter::ImageLoad(ImageHeader &header, C Str &name)
  236. {
  237. if(GetThreadId()==TIG.thread_id) // process only inside 'TIG'
  238. {
  239. header.mode=IMAGE_SOFT;
  240. //if(ImageTI[header.type].compressed)header.type=IMAGE_R8G8B8A8; no need to do that, because there are only one decompressions per mip-map (1. extracting 2nd mip map to RGBA, 2. comparing 1st mip map with upscaled), doing this would only increase memory usage
  241. MIN(header.mip_maps, 2); // we need only 2 mip maps
  242. }
  243. return 0;
  244. }
  245. flt TexInfoGetter::ImageSharpness(C Image &image)
  246. {
  247. if(image.mipMaps()>=2)
  248. {
  249. Image mip; image.extractMipMap(mip, IMAGE_R8G8B8A8, IMAGE_SOFT, 1); // get 2nd mip-map
  250. mip.copy(mip, image.w(), image.h(), image.d(), -1, -1, 1, FILTER_LINEAR, false); // upscale smaller mip-map to full image size, use linear filtering because we simulate GPU filtering
  251. ImageCompare ic; if(ic.compare(image, mip))return ic.avg_dif2;
  252. }
  253. return -1; // can't calculate
  254. }
  255. void TexInfoGetter::CalcTexSharpness(UID &tex_id, ptr user, int thread_index)
  256. {
  257. Image img;
  258. // no need for 'ThreadMayUseGPUData' because we use only IMAGE_SOFT
  259. Images.lock(); // lock because other threads may modify 'image_load_shrink' too
  260. TIG.thread_id=GetThreadId();
  261. int (*image_load_shrink)(ImageHeader &image_header, C Str &name)=D.image_load_shrink; // remember current
  262. D.image_load_shrink=ImageLoad ; bool ok=img.load(TIG.tex_path+EncodeFileName(tex_id));
  263. D.image_load_shrink=image_load_shrink; // restore
  264. Images.unlock();
  265. flt sharpness =ImageSharpness(img);
  266. if( sharpness>=0)
  267. {
  268. TexInfos(tex_id)->sharpness=sharpness;
  269. AtomicSet(TIG.got_new_data, true);
  270. }
  271. }
  272. void TexInfoGetter::getTexSharpnessFromProject()
  273. {
  274. if(Proj.list.tex_sharpness)
  275. {
  276. stopAndWait(); // stop first
  277. tex_path=Proj.tex_path;
  278. // get textures to process
  279. Memc<UID> proj_texs; Proj.getTextures(proj_texs, true); // process only existing
  280. FREPA(proj_texs)
  281. {
  282. C UID &tex_id=proj_texs[i];
  283. if(C TextureInfo *tex_info=TexInfos.find(tex_id))if(tex_info->knownSharpness())continue; // no need to process
  284. tex_to_process.add(tex_id);
  285. }
  286. // now when all array has been allocated and elements won't change their mem address, we can start processing
  287. FREPA(tex_to_process)BackgroundThreads.queue(tex_to_process[i], CalcTexSharpness);
  288. }else stop();
  289. }
  290. void TexInfoGetter::savedTex(C UID &tex_id)
  291. {
  292. if(Proj.list.tex_sharpness)
  293. {
  294. UID &process=tex_to_process1.New(); process=tex_id;
  295. BackgroundThreads.queue(process, CalcTexSharpness); // use 'process' and not 'tex_id' to have const_mem_addr
  296. }
  297. }
  298. ListElm::ListElm() : size_known(true), tex_size_calculated(false), depth(0), offset(0), color(BLACK), color_temp(BLACK), elm(null), item(null), opened_icon(null), icon(null), size(0) {}
  299. TexInfoGetter::TexInfoGetter() : got_new_data(false), thread_id(0) {}
  300. /******************************************************************************/