Functions.cpp 76 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. /******************************************************************************/
  4. // FILE
  5. /******************************************************************************/
  6. bool SafeDel(C Str &name, ReadWriteSync &rws)
  7. {
  8. if(name.is()){WriteLock wl(rws); return FDelFile(name);} return true;
  9. }
  10. bool SafeOverwrite(File &src, C Str &dest, ReadWriteSync &rws)
  11. {
  12. return EE::SafeOverwrite(src, dest, null, null, S+'@'+TextHex(Random()), &rws);
  13. }
  14. bool SafeOverwriteChunk(File &src, C Str &dest, ReadWriteSync &rws)
  15. {
  16. return (src.size()>4) ? SafeOverwrite(src, dest, rws) : SafeDel(dest, rws);
  17. }
  18. bool SafeCopy(C Str &src, C Str &dest)
  19. {
  20. File f; return f.readStdTry(src) && EE::SafeOverwrite(f, dest, &NoTemp(FileInfoSystem(src).modify_time_utc), null, S+"@new"+Random());
  21. }
  22. /******************************************************************************/
  23. void RemoveChunk(C Str &file, C Str &chunk, ReadWriteSync &rws)
  24. {
  25. ReadLock rl(rws);
  26. File src; if(src.readTry(file))
  27. {
  28. File temp; ChunkWriter cw(temp.writeMem());
  29. for(ChunkReader cr(src); File *s=cr(); )
  30. if(!EqualPath(cr.name(), chunk))
  31. if(File *d=cw.beginChunk(cr.name(), cr.ver()))
  32. s->copy(*d);
  33. src.del();
  34. cw.endChunk();
  35. temp.pos(0); SafeOverwriteChunk(temp, file, rws);
  36. }
  37. }
  38. /******************************************************************************/
  39. cchar8 *SizeSuffix[]={"", " KB", " MB", " GB", " TB"};
  40. Str FileSize(long size, char dot)
  41. {
  42. const int f=10;
  43. size*=f;
  44. int i=0; for(; i<Elms(SizeSuffix)-1 && size>=1000*f; i++, size>>=10); // check for "1000*f" instead of "1024*f", because we want to avoid displaying things like "1 001 MB"
  45. Str s=TextInt(size/f, -1, 3); if(size<100*f && i){s+=dot; s+=size%10;} s+=SizeSuffix[i];
  46. return s;
  47. }
  48. Str FileSizeKB(long size)
  49. {
  50. const int f=10;
  51. size*=f;
  52. int i=1; size>>=10;
  53. Str s=TextInt(size/f, -1, 3); if(size<100*f && i){s+=','; s+=size%10;} s+=SizeSuffix[i];
  54. return s;
  55. }
  56. /******************************************************************************/
  57. void SavedImage (C Str &name) {if(ImagePtr e=ImagePtr ().find(name))if(!IsServer)e->load(name);} // on server the file may be compressed
  58. void SavedImageAtlas (C Str &name) {if(ImageAtlasPtr e=ImageAtlasPtr().find(name))if(!IsServer)e->load(name);}
  59. void SavedEditSkel (C Str &name) {}
  60. void SavedSkel (C Str &name) {if(Skeleton *e=Skeletons .find(name))if(!IsServer)e->load(name);}
  61. void SavedAnim (C Str &name) {if(Animation *e=Animations.find(name))if(!IsServer)e->load(name);}
  62. void SavedMesh (C Str &name) {if(MeshPtr e=MeshPtr ().find(name))if(!IsServer){CacheLock cl(Meshes); e->load(name);}}
  63. void SavedEditMtrl (C Str &name) {}
  64. void SavedEditWaterMtrl(C Str &name) {}
  65. void SavedEditPhysMtrl (C Str &name) {}
  66. void SavedMtrl (C Str &name) {if(MaterialPtr e= MaterialPtr().find(name))if(!IsServer)e->load(name);}
  67. void SavedWaterMtrl (C Str &name) {if(WaterMtrlPtr e=WaterMtrlPtr().find(name))if(!IsServer)e->load(name);}
  68. void SavedPhysMtrl (C Str &name) {if(PhysMtrl *e=PhysMtrls .find(name))if(!IsServer)e->load(name);}
  69. void SavedEditPhys (C Str &name) {}
  70. void SavedPhys (C Str &name) {if(PhysBodyPtr e=PhysBodyPtr().find(name))if(!IsServer){CacheLock cl(PhysBodies); e->load(name);}}
  71. void SavedEnum (C Str &name) {if(Enum *e=Enums .find(name))if(!IsServer)e->load(name);}
  72. void SavedEditEnum (C Str &name) {}
  73. void SavedEditObjPar (C Str &name) {if(EditObjectPtr e=EditObjectPtr().find(name))if(!IsServer)e->load(name);}
  74. void SavedGameObjPar (C Str &name) {if(ObjectPtr e= ObjectPtr().find(name))if(!IsServer){CacheLock cl(Objects); e->load(name);}}
  75. void SavedGameWayp (C Str &name) {if(Game::Waypoint *e=Game::Waypoints. find(name))if(!IsServer)e->load(name);}
  76. void SavedFont (C Str &name) {if(FontPtr e= FontPtr().find(name))if(!IsServer)e->load(name);}
  77. void SavedTextStyle (C Str &name) {if(TextStylePtr e= TextStylePtr().find(name))if(!IsServer)e->load(name);}
  78. void SavedPanelImage (C Str &name) {if(PanelImagePtr e=PanelImagePtr().find(name))if(!IsServer)e->load(name);}
  79. void SavedPanel (C Str &name) {if(PanelPtr e= PanelPtr().find(name))if(!IsServer)e->load(name);}
  80. void SavedGuiSkin (C Str &name) {if(GuiSkinPtr e= GuiSkinPtr().find(name))if(!IsServer)e->load(name);}
  81. void SavedGui (C Str &name) {}
  82. void SavedEnv (C Str &name) {if(EnvironmentPtr e=EnvironmentPtr().find(name))if(!IsServer)e->load(name);}
  83. void Saved(C Image &img , C Str &name) {if(ImagePtr e=ImagePtr().find(name))img.copy(*e);}
  84. void Saved(C ImageAtlas &img , C Str &name) {SavedImageAtlas(name);}
  85. void Saved(C IconSettings &icon, C Str &name) {}
  86. void Saved(C EditSkeleton &skel, C Str &name) {}
  87. void Saved(C Skeleton &skel, C Str &name) {if(Skeleton *e=Skeletons .find(name))*e=skel;}
  88. void Saved(C Animation &anim, C Str &name) {if(Animation *e=Animations.find(name))*e=anim;}
  89. void Saved(C Mesh &mesh, C Str &name) {if(MeshPtr e=MeshPtr ().find(name)){CacheLock cl(Meshes); e->create(mesh).setShader();}}
  90. void Saved(C EditMaterial &mtrl, C Str &name) {}
  91. void Saved(C EditWaterMtrl &mtrl, C Str &name) {}
  92. void Saved(C EditPhysMtrl &mtrl, C Str &name) {}
  93. void Saved(C Material &mtrl, C Str &name) {if( MaterialPtr e= MaterialPtr().find(name))*e=mtrl;}
  94. void Saved(C WaterMtrl &mtrl, C Str &name) {if(WaterMtrlPtr e=WaterMtrlPtr().find(name))*e=mtrl;}
  95. void Saved(C PhysMtrl &mtrl, C Str &name) {SavedPhysMtrl(name);}
  96. void Saved(C PhysBody &phys, C Str &name) {if(PhysBodyPtr e=PhysBodyPtr().find(name)){CacheLock cl(PhysBodies); *e=phys;}}
  97. void Saved(C Enum &enm , C Str &name) {if(Enum *e=Enums .find(name))*e=enm;}
  98. void Saved(C EditEnums &enms, C Str &name) {}
  99. void Saved(C EditObject &obj , C Str &name) {if(EditObjectPtr e=EditObjectPtr().find(name))*e=obj;}
  100. void Saved(C Object &obj , C Str &name) {if( ObjectPtr e= ObjectPtr().find(name)){CacheLock cl(Objects); *e=obj;}}
  101. void Saved(C EditWaypoint &wp , C Str &name) {}
  102. void Saved(C Game::Waypoint &wp , C Str &name) {if(Game::Waypoint *e=Game::Waypoints.find(name))*e=wp;}
  103. void Saved(C EditFont &font, C Str &name) {}
  104. void Saved(C Font &font, C Str &name) {SavedFont(name);}
  105. void Saved(C EditTextStyle &ts , C Str &name) {}
  106. void Saved(C EditPanelImage &pi , C Str &name) {}
  107. void Saved(C PanelImage &pi , C Str &name) {SavedPanelImage(name);}
  108. void Saved(C TextStyle &ts , C Str &name) {if(TextStylePtr e=TextStylePtr().find(name))*e=ts;}
  109. void Saved(C EditPanel &panl, C Str &name) {}
  110. void Saved(C Panel &panl, C Str &name) {if(PanelPtr e=PanelPtr().find(name))*e=panl;}
  111. void Saved(C EditGuiSkin &skin, C Str &name) {}
  112. void Saved(C GuiSkin &skin, C Str &name) {if(GuiSkinPtr e=GuiSkinPtr().find(name))*e=skin;}
  113. void Saved(C GuiObjs &gui , C Str &name) {}
  114. void Saved(C Lake &lake, C Str &name) {}
  115. void Saved(C River &rivr, C Str &name) {}
  116. void Saved(C EditEnv &env , C Str &name) {}
  117. void Saved(C Environment &env , C Str &name) {if(EnvironmentPtr e=EnvironmentPtr().find(name))*e=env;}
  118. void Saved(C Game::WorldSettings &s, C Str &name) {}
  119. bool Save (C Image &img , C Str &name ) {File f; img .save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name)){Saved(img , name); return true;} return false;}
  120. void Save (C ImageAtlas &img , C Str &name ) {File f; img .save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(img , name);}
  121. void Save (C IconSettings &icon, C Str &name ) {File f; icon.save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(icon, name);}
  122. void Save (C EditSkeleton &skel, C Str &name ) {File f; skel.save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(skel, name);}
  123. void Save (C Skeleton &skel, C Str &name ) {File f; skel.save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(skel, name);}
  124. void Save (C Animation &anim, C Str &name ) {File f; anim.save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(anim, name);}
  125. void Save (C Mesh &mesh, C Str &name, C Str &resource_path) {File f; mesh.save(f.writeMem(), resource_path.is() ? resource_path : GetPath(name)); f.pos(0); CacheLock cl(Meshes); if(SafeOverwrite(f, name))Saved(mesh, name);}
  126. void Save (C EditMaterial &mtrl, C Str &name ) {File f; mtrl.save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(mtrl, name);}
  127. void Save (C EditWaterMtrl &mtrl, C Str &name ) {File f; mtrl.save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(mtrl, name);}
  128. void Save (C EditPhysMtrl &mtrl, C Str &name ) {File f; mtrl.save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(mtrl, name);}
  129. void Save (C Material &mtrl, C Str &name, C Str &resource_path) {File f; mtrl.save(f.writeMem(), resource_path.is() ? resource_path : GetPath(name)); f.pos(0); if(SafeOverwrite(f, name))Saved(mtrl, name);}
  130. void Save (C WaterMtrl &mtrl, C Str &name, C Str &resource_path) {File f; mtrl.save(f.writeMem(), resource_path.is() ? resource_path : GetPath(name)); f.pos(0); if(SafeOverwrite(f, name))Saved(mtrl, name);}
  131. void Save (C PhysMtrl &mtrl, C Str &name ) {File f; mtrl.save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(mtrl, name);}
  132. void Save (C PhysBody &phys, C Str &name, C Str &resource_path) {File f; phys.save(f.writeMem(), resource_path.is() ? resource_path : GetPath(name)); f.pos(0); CacheLock cl(PhysBodies); if(SafeOverwrite(f, name))Saved(phys, name);}
  133. void Save (C Enum &enm , C Str &name ) {File f; enm .save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(enm , name);}
  134. void Save (C EditEnums &enms, C Str &name ) {File f; enms.save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(enms, name);}
  135. void Save (C EditObject &obj , C Str &name, C Str &resource_path) {File f; obj .save(f.writeMem(), resource_path.is() ? resource_path : GetPath(name)); f.pos(0); if(SafeOverwrite(f, name))Saved(obj , name);}
  136. void Save (C Object &obj , C Str &name, C Str &resource_path) {File f; obj .save(f.writeMem(), resource_path.is() ? resource_path : GetPath(name)); f.pos(0); CacheLock cl(Objects); if(SafeOverwrite(f, name))Saved(obj, name);}
  137. void Save (C EditWaypoint &wp , C Str &name ) {File f; wp .save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(wp , name);}
  138. void Save (C Game::Waypoint &wp , C Str &name ) {File f; wp .save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(wp , name);}
  139. void Save (C EditFont &font, C Str &name ) {File f; font.save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(font, name);}
  140. void Save (C Font &font, C Str &name ) {File f; font.save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(font, name);}
  141. void Save (C EditTextStyle &ts , C Str &name ) {File f; ts .save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(ts , name);}
  142. void Save (C TextStyle &ts , C Str &name, C Str &resource_path) {File f; ts .save(f.writeMem(), resource_path.is() ? resource_path : GetPath(name)); f.pos(0); if(SafeOverwrite(f, name))Saved(ts , name);}
  143. void Save (C EditPanelImage &pi , C Str &name ) {File f; pi .save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(pi , name);}
  144. void Save (C PanelImage &pi , C Str &name ) {File f; pi .save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(pi , name);}
  145. void Save (C EditPanel &panl, C Str &name ) {File f; panl.save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(panl, name);}
  146. void Save (C Panel &panl, C Str &name, C Str &resource_path) {File f; panl.save(f.writeMem(), resource_path.is() ? resource_path : GetPath(name)); f.pos(0); if(SafeOverwrite(f, name))Saved(panl, name);}
  147. void Save (C EditGuiSkin &skin, C Str &name ) {File f; skin.save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(skin, name);}
  148. void Save (C GuiSkin &skin, C Str &name, C Str &resource_path) {File f; skin.save(f.writeMem(), resource_path.is() ? resource_path : GetPath(name)); f.pos(0); if(SafeOverwrite(f, name))Saved(skin, name);}
  149. void Save (C GuiObjs &gui , C Str &name, C Str &resource_path) {File f; gui .save(f.writeMem(), resource_path.is() ? resource_path : GetPath(name)); f.pos(0); if(SafeOverwrite(f, name))Saved(gui , name);}
  150. void Save (C Lake &lake, C Str &name ) {File f; lake.save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name, WorldAreaSync))Saved(lake, name);}
  151. void Save (C River &rivr, C Str &name ) {File f; rivr.save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name, WorldAreaSync))Saved(rivr, name);}
  152. void Save (C EditEnv &env , C Str &name ) {File f; env .save(f.writeMem() ); f.pos(0); if(SafeOverwrite(f, name))Saved(env , name);}
  153. void Save (C Environment &env , C Str &name, C Str &resource_path) {File f; env .save(f.writeMem(), resource_path.is() ? resource_path : GetPath(name)); f.pos(0); if(SafeOverwrite(f, name))Saved(env , name);}
  154. void Save (C Game::WorldSettings &s, C Str &name, C Str &resource_path) {File f; s .save(f.writeMem(), resource_path.is() ? resource_path : GetPath(name)); f.pos(0); if(SafeOverwrite(f, name))Saved(s , name);}
  155. bool Load (Mesh &mesh, C Str &name, C Str &resource_path) {File f; if(f.readTry(name))return mesh.load(f, resource_path.is() ? resource_path : GetPath(name)); mesh.del (); return false;}
  156. bool Load (Material &mtrl, C Str &name, C Str &resource_path) {File f; if(f.readTry(name))return mtrl.load(f, resource_path.is() ? resource_path : GetPath(name)); mtrl.reset(); return false;}
  157. bool Load (WaterMtrl &mtrl, C Str &name, C Str &resource_path) {File f; if(f.readTry(name))return mtrl.load(f, resource_path.is() ? resource_path : GetPath(name)); mtrl.reset(); return false;}
  158. bool Load (EditObject &obj , C Str &name, C Str &resource_path) {File f; if(f.readTry(name))return obj .load(f, resource_path.is() ? resource_path : GetPath(name)); obj .del (); return false;}
  159. // other assets either don't use sub-assets, or are stored in game path and don't require "edit->game" path change
  160. bool SaveCode(C Str &code, C Str &name)
  161. {
  162. //FileText f; f.writeMem(HasUnicode(code) ? UTF_16 : ANSI); // avoid UTF_8 because it's slower to write/read, and as there can be lot of codes, we don't want to sacrifice performance when opening big projects
  163. FileText f; f.writeMem(HasUnicode(code) ? UTF_8 : ANSI); // FIXME restore above UTF_16 once github supports it, because now it can corrupt files
  164. f.fix_new_line=false; // don't write '\r' to reduce size and improve performance for saving/loading
  165. f.putText(code);
  166. return EE::SafeOverwrite(f, name);
  167. }
  168. Edit::ERROR_TYPE LoadCode(Str &code, C Str &name)
  169. {
  170. FileText f; if(f.read(name)){f.getAll(code); return f.ok() ? Edit::EE_ERR_NONE : Edit::EE_ERR_FILE_READ_ERROR;}
  171. code.clear(); return Edit::EE_ERR_FILE_NOT_FOUND;
  172. }
  173. void SavedBase(ELM_TYPE type, C Str &path) // called when saved the base version
  174. {
  175. if(ElmEdit(type))SavedEdit(type, path);
  176. else SavedGame(type, path);
  177. }
  178. void SavedCode(C Str &path) // called when saved code
  179. {
  180. }
  181. void SavedEdit(ELM_TYPE type, C Str &path) // called when saved the edit version
  182. {
  183. switch(type)
  184. {
  185. case ELM_SKEL : SavedEditSkel (path); break;
  186. case ELM_PHYS : SavedEditPhys (path); break;
  187. case ELM_ENUM : SavedEditEnum (path); break;
  188. case ELM_OBJ_CLASS : SavedEditObjPar (path); break;
  189. case ELM_OBJ : SavedEditObjPar (path); break;
  190. case ELM_MESH : SavedMesh (path); break;
  191. case ELM_MTRL : SavedEditMtrl (path); break;
  192. case ELM_WATER_MTRL: SavedEditWaterMtrl(path); break;
  193. case ELM_PHYS_MTRL : SavedEditPhysMtrl (path); break;
  194. }
  195. }
  196. void SavedGame(ELM_TYPE type, C Str &path) // called when saved the game version
  197. {
  198. switch(type)
  199. {
  200. case ELM_ENUM : SavedEnum (path); break;
  201. case ELM_OBJ_CLASS : SavedGameObjPar(path); break;
  202. case ELM_OBJ : SavedGameObjPar(path); break;
  203. case ELM_MESH : SavedMesh (path); break;
  204. case ELM_MTRL : SavedMtrl (path); break;
  205. case ELM_WATER_MTRL : SavedWaterMtrl (path); break;
  206. case ELM_PHYS_MTRL : SavedPhysMtrl (path); break;
  207. case ELM_SKEL : SavedSkel (path); break;
  208. case ELM_PHYS : SavedPhys (path); break;
  209. case ELM_ANIM : SavedAnim (path); break;
  210. case ELM_GUI_SKIN : SavedGuiSkin (path); break;
  211. case ELM_GUI : SavedGui (path); break;
  212. case ELM_FONT : SavedFont (path); break;
  213. case ELM_TEXT_STYLE : SavedTextStyle (path); break;
  214. case ELM_PANEL_IMAGE: SavedPanelImage(path); break;
  215. case ELM_PANEL : SavedPanel (path); break;
  216. case ELM_ENV : SavedEnv (path); break;
  217. case ELM_IMAGE : SavedImage (path); break;
  218. case ELM_IMAGE_ATLAS: SavedImageAtlas(path); break;
  219. case ELM_ICON : SavedImage (path); break;
  220. }
  221. }
  222. /******************************************************************************/
  223. // SYNC / UNDO
  224. /******************************************************************************/
  225. void MAX1(TimeStamp &time, C TimeStamp &src_time) {if(src_time>time)time=src_time; time++;} // set as max from both and increase by one, "time=Max(time, src_time)+1"
  226. bool Sync(TimeStamp &time, C TimeStamp &src_time) {if(src_time> time){time=src_time; return true;} return false;}
  227. bool Undo(TimeStamp &time, C TimeStamp &src_time) {if(src_time!=time){MAX1(time, src_time); return true;} return false;}
  228. /*<TYPE> bool UndoByValueEqual(TimeStamp &time, C TimeStamp &src_time, TYPE &data, C TYPE &src_data)
  229. {
  230. if(!Equal(data, src_data)){data=src_data; MAX1(time, src_time); return true;} return false;
  231. }*/
  232. /*<TYPE> bool UndoEqual(TimeStamp &time, C TimeStamp &src_time, TYPE &data, C TYPE &src_data) // ByTimeAndValue
  233. {
  234. return UndoByTime (time, src_time, data, src_data) // first check by time because it's faster
  235. || UndoByValueEqual(time, src_time, data, src_data);
  236. }*/
  237. void SetUndo(C Edit::_Undo &undos, Button &undo, Button &redo)
  238. {
  239. undo.enabled(undos.undosAvailable());
  240. redo.enabled(undos.redosAvailable());
  241. }
  242. /******************************************************************************/
  243. // IMAGE
  244. /******************************************************************************/
  245. DIR_ENUM GetCubeDir(int face)
  246. {
  247. switch(face)
  248. {
  249. default: return DIR_LEFT;
  250. case 1: return DIR_FORWARD;
  251. case 2: return DIR_RIGHT;
  252. case 3: return DIR_BACK;
  253. case 4: return DIR_DOWN;
  254. case 5: return DIR_UP;
  255. }
  256. }
  257. Str GetCubeFile(C Str &files, int face)
  258. {
  259. Mems<Edit::FileParams> faces=Edit::FileParams::Decode(files);
  260. return (faces.elms()==1) ? files : InRange(face, faces) ? faces[face].encode() : S;
  261. }
  262. Str SetCubeFile(Str files, int face, C Str &file) // put 'file' into specified 'face' and return all files
  263. {
  264. if(InRange(face, 6))
  265. {
  266. Mems<Edit::FileParams> faces=Edit::FileParams::Decode(files);
  267. if(faces.elms()==1){faces.setNum(6); REPAO(faces)=files;} // set all from original
  268. if(faces.elms()!=6)faces.clear(); // if invalid number then clear
  269. faces.setNum(6); // set 6 faces
  270. faces[face]=file; // set i-th face to target file
  271. files=Edit::FileParams::Encode(faces); // get all faces
  272. }
  273. return files;
  274. }
  275. /******************************************************************************/
  276. bool HasAlpha(C Image &image) // if image has alpha channel
  277. {
  278. if(!ImageTI[image.type()].a)return false;
  279. Vec4 min, max; if(image.stats(&min, &max, null))return !(Equal(min.w, 1, 2.5f/255) && Equal(max.w, 1, 2.5f/255));
  280. return true;
  281. }
  282. bool HasColor(C Image &image) // if image is not monochromatic
  283. {
  284. if(!ImageTI[image.type()].r && !ImageTI[image.type()].g && !ImageTI[image.type()].b)return false;
  285. if(image.type()==IMAGE_L8 || image.type()==IMAGE_L8A8)return false;
  286. Image temp; C Image *src=&image; if(ImageTI[src->hwType()].compressed)if(src->copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))src=&temp;else return true;
  287. FREPD(face, (src->mode()==IMAGE_CUBE) ? 6 : 1)if(src->lockRead(0, DIR_ENUM(face)))
  288. {
  289. REPD(z, src->ld())
  290. REPD(y, src->lh())
  291. REPD(x, src->lw())
  292. {
  293. Color c=src->color3D(x, y, z); if(Abs(c.r-c.g)>1 || Abs(c.r-c.b)>1){src->unlock(); return true;}
  294. }
  295. src->unlock();
  296. }
  297. return false;
  298. }
  299. bool NeedFixAlpha(Image &image, IMAGE_TYPE type, bool always)
  300. {
  301. return ((image.type()!=IMAGE_BC1 && type==IMAGE_BC1) || always) // if we're converting from non-BC1 to BC1, then since BC1 will make black pixels for alpha<128, we need to force full alpha (ETC2_A1 does that too, however if we didn't want it, we can just use ETC2)
  302. && ImageTI[image.type()].a; // no point in chaning alpha if the source doesn't have it
  303. }
  304. bool FixAlpha(Image &image, IMAGE_TYPE type, bool always) // returns if any change was made
  305. {
  306. if(NeedFixAlpha(image, type, always))
  307. {
  308. if(ImageTI[image.hwType()].compressed)return image.copyTry(image, -1, -1, -1, IMAGE_R8G8B8, IMAGE_SOFT, 1);
  309. if(image.lock())
  310. {
  311. REPD(y, image.h())
  312. REPD(x, image.w()){Color c=image.color(x, y); c.a=255; image.color(x, y, c);}
  313. image.unlock();
  314. return true;
  315. }
  316. }
  317. return false;
  318. }
  319. void ImageProps(C Image &image, UID *md5, IMAGE_TYPE *compress_type, uint flags) // calculate image MD5 (when in IMAGE_R8G8B8A8 type) and get best type for image compression
  320. {
  321. if((flags&FORCE_HQ ) && compress_type){*compress_type=IMAGE_BC7; compress_type=null;} // when forcing HQ set BC7 and set null so it no longer needs to be calculated, check this before IGNORE_ALPHA
  322. if((flags&IGNORE_ALPHA) && compress_type){*compress_type=IMAGE_BC1; compress_type=null;} // when ignoring alpha set BC1 and set null so it no longer needs to be calculated
  323. if(md5 || compress_type)
  324. {
  325. // set initial values
  326. if(md5 )md5->zero();
  327. if(compress_type)*compress_type=(SupportBC7 ? IMAGE_BC7 : IMAGE_BC3);
  328. // calculate
  329. if(!image.is())return;
  330. MD5 m;
  331. bool bc1=true, bc2=true, // BC1 uses 1 bit alpha (0 or 255), BC2 uses 4 bit alpha
  332. force_alpha=(md5 && (flags&IGNORE_ALPHA) && ImageTI[image.type()].a); // if we want hash and we want to ignore alpha, and source had alpha, then we need to adjust as if it has full alpha, this is done because: ignoring alpha may save the image in format that doesn't support the alpha channel, however if the same image is later used for something else, and now wants to use that alpha channel, then it needs to be created as a different texture (with different hash)
  333. FREPD(face, (image.mode()==IMAGE_CUBE) ? 6 : 1)
  334. {
  335. Image temp; C Image *src=&image;
  336. if((md5 && src->hwType()!=IMAGE_R8G8B8A8) // calculating hash requires RGBA format
  337. || (compress_type && ImageTI[src->hwType()].compressed) // checking compress_type requires color reads so copy to RGBA soft to make them faster
  338. || force_alpha) // forcing alpha requires modifying the alpha channel, so copy to 'temp' which we can modify
  339. if(src->extractMipMap(temp, IMAGE_R8G8B8A8, IMAGE_SOFT, 0, DIR_ENUM(face)))src=&temp;else return;
  340. if(src->lockRead(0, DIR_ENUM(face)))
  341. {
  342. if(force_alpha ) REPD(z, temp.d())
  343. REPD(y, temp.h())
  344. REPD(x, temp.w())temp.pixC(x, y, z).a=255; // set before calculating hash
  345. if(md5 )FREPD(z, src->d())
  346. FREPD(y, src->h())m.update(src->data() + y*src->pitch() + z*src->pitch2(), src->w()*4);
  347. if(compress_type) REPD(z, src->d())
  348. REPD(y, src->h())
  349. REPD(x, src->w())
  350. {
  351. Color c=src->color3D(x, y, z);
  352. byte bc2_a=((c.a*15+128)/255)*255/15;
  353. if(c.a> 1 && c.a<254 // BC1 supports only 0 and 255 alpha
  354. || c.a==0 && c.lum())bc1=false; // BC1 supports only black color at 0 alpha
  355. if(Abs(c.a-bc2_a)>1 )bc2=false;
  356. }
  357. src->unlock();
  358. }
  359. }
  360. if(md5 )*md5 =m();
  361. if(compress_type)*compress_type=(bc1 ? IMAGE_BC1 : SupportBC7 ? IMAGE_BC7 : bc2 ? IMAGE_BC2 : IMAGE_BC3); // prefer BC1 because it's 4-bit per pixel
  362. }
  363. }
  364. /******************************************************************************/
  365. void LoadTexture(C Project &proj, C UID &tex_id, Image &image, C VecI2 &size)
  366. {
  367. ImagePtr src=proj.texPath(tex_id);
  368. if(src)src->copyTry(image, size.x, size.y, 1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1);else image.del(); // always copy, because: src texture will always be compressed, also soft doesn't require locking
  369. }
  370. void ExtractBaseTextures(C Project &proj, C UID &base_0, C UID &base_1, Image *col, Image *alpha, Image *bump, Image *normal, Image *specular, Image *glow, C VecI2 &size)
  371. {
  372. uint tex=0;
  373. if(base_0.valid() && base_1.valid()) // both textures specified
  374. {
  375. if(col || bump)
  376. {
  377. Image b0; LoadTexture(proj, base_0, b0, size);
  378. if(col )col ->createSoft(b0.w(), b0.h(), 1, IMAGE_R8G8B8);
  379. if(bump)bump->createSoft(b0.w(), b0.h(), 1, IMAGE_L8);
  380. REPD(y, b0.h())
  381. REPD(x, b0.w())
  382. {
  383. Color c=b0.color(x, y);
  384. if(col ){col ->color(x, y, c ); if(c.r<254 || c.g<254 || c.b<254)tex|=BT_COLOR;}
  385. if(bump){bump->pixel(x, y, c.a); if(Abs(c.a-128)>1 && c.a<254)tex|=BT_BUMP ;} // BUMP_DEFAULT can be either 128 or 255
  386. }
  387. }
  388. if(alpha || normal || specular || glow)
  389. {
  390. Image b1; LoadTexture(proj, base_1, b1, size);
  391. if(alpha )alpha ->createSoft(b1.w(), b1.h(), 1, IMAGE_L8);
  392. if(normal )normal ->createSoft(b1.w(), b1.h(), 1, IMAGE_R8G8B8);
  393. if(specular)specular->createSoft(b1.w(), b1.h(), 1, IMAGE_L8);
  394. if(glow )glow ->createSoft(b1.w(), b1.h(), 1, IMAGE_L8);
  395. REPD(y, b1.h())
  396. REPD(x, b1.w())
  397. {
  398. Color c=b1.color(x, y); // #MaterialTextureChannelOrder
  399. /*if(old)
  400. {
  401. if(alpha ){alpha .pixel(x, y, c.b); if(c.b<254)tex|=BT_ALPHA ;}
  402. if(glow ){glow .pixel(x, y, c.b); if(c.b<254)tex|=BT_GLOW ;}
  403. if(specular){specular.pixel(x, y, c.r); if(c.r<254)tex|=BT_SPECULAR;}
  404. if(normal )
  405. {
  406. Vec n; n.xy.set((c.a-128)/127.0, (c.g-128)/127.0); n.z=Sqrt(1 - n.x*n.x - n.y*n.y);
  407. normal.color(x, y, Color(c.a, c.g, Mid(Round(n.z*127+128), 0, 255))); if(Abs(c.a-128)>1 || Abs(c.g-128)>1)tex|=BT_NORMAL;
  408. }
  409. }else*/
  410. {
  411. if(alpha ){alpha ->pixel(x, y, c.a); if(c.a<254)tex|=BT_ALPHA ;}
  412. if(glow ){glow ->pixel(x, y, c.a); if(c.a<254)tex|=BT_GLOW ;}
  413. if(specular){specular->pixel(x, y, c.b); if(c.b<254)tex|=BT_SPECULAR;}
  414. if(normal )
  415. {
  416. Vec n; n.xy.set((c.r-128)/127.0f, (c.g-128)/127.0f); n.z=Sqrt(1 - n.x*n.x - n.y*n.y);
  417. normal->color(x, y, Color(c.r, c.g, Mid(Round(n.z*127+128), 0, 255))); if(Abs(c.r-128)>1 || Abs(c.g-128)>1)tex|=BT_NORMAL;
  418. }
  419. }
  420. }
  421. }
  422. }else
  423. if(base_0.valid()) // only one texture specified
  424. {
  425. if(col || alpha)
  426. {
  427. Image b0; LoadTexture(proj, base_0, b0, size);
  428. if(col )col ->createSoft(b0.w(), b0.h(), 1, IMAGE_R8G8B8);
  429. if(alpha)alpha->createSoft(b0.w(), b0.h(), 1, IMAGE_L8);
  430. REPD(y, b0.h())
  431. REPD(x, b0.w())
  432. {
  433. Color c=b0.color(x, y);
  434. if(col ){col ->color(x, y, c ); if(c.r<254 || c.g<254 || c.b<254)tex|=BT_COLOR;}
  435. if(alpha){alpha->pixel(x, y, c.a); if(c.a<254 )tex|=BT_ALPHA;}
  436. }
  437. }
  438. }
  439. if(col && !(tex&BT_COLOR ))col ->del();
  440. if(alpha && !(tex&BT_ALPHA ))alpha ->del();
  441. if(bump && !(tex&BT_BUMP ))bump ->del();
  442. if(normal && !(tex&BT_NORMAL ))normal ->del();
  443. if(specular && !(tex&BT_SPECULAR))specular->del();
  444. if(glow && !(tex&BT_GLOW ))glow ->del();
  445. }
  446. void ExtractDetailTexture(C Project &proj, C UID &detail_tex, Image *col, Image *bump, Image *normal)
  447. {
  448. uint tex=0;
  449. if(detail_tex.valid())
  450. if(col || bump || normal)
  451. {
  452. Image det; LoadTexture(proj, detail_tex, det);
  453. if(col )col ->createSoft(det.w(), det.h(), 1, IMAGE_L8);
  454. if(bump )bump ->createSoft(det.w(), det.h(), 1, IMAGE_L8);
  455. if(normal)normal->createSoft(det.w(), det.h(), 1, IMAGE_R8G8B8);
  456. REPD(y, det.h())
  457. REPD(x, det.w())
  458. {
  459. Color c=det.color(x, y); // #MaterialTextureChannelOrder
  460. /*if(old)
  461. {
  462. if(col ){col .pixel(x, y, c.r); if(c.r<254)tex|=BT_COLOR;}
  463. if(bump ){bump.pixel(x, y, c.b); if(c.b<254)tex|=BT_BUMP ;}
  464. if(normal)
  465. {
  466. Vec n; n.xy.set((c.a-128)/127.0, (c.g-128)/127.0); n.z=Sqrt(1 - n.x*n.x - n.y*n.y);
  467. normal.color(x, y, Color(c.a, c.g, Mid(Round(n.z*127+128), 0, 255))); if(Abs(c.a-128)>1 || Abs(c.g-128)>1)tex|=BT_NORMAL;
  468. }
  469. }else*/
  470. {
  471. if(col ){col ->pixel(x, y, c.b); if(c.b<254)tex|=BT_COLOR;}
  472. if(bump ){bump->pixel(x, y, c.a); if(c.a<254)tex|=BT_BUMP ;}
  473. if(normal)
  474. {
  475. Vec n; n.xy.set((c.r-128)/127.0f, (c.g-128)/127.0f); n.z=Sqrt(1 - n.x*n.x - n.y*n.y);
  476. normal->color(x, y, Color(c.r, c.g, Mid(Round(n.z*127+128), 0, 255))); if(Abs(c.r-128)>1 || Abs(c.g-128)>1)tex|=BT_NORMAL;
  477. }
  478. }
  479. }
  480. }
  481. if(col && !(tex&BT_COLOR ))col ->del();
  482. if(bump && !(tex&BT_BUMP ))bump ->del();
  483. if(normal && !(tex&BT_NORMAL))normal->del();
  484. }
  485. UID MergedBaseTexturesID(C UID &base_0, C UID &base_1) // this function generates ID of a merged texture created from two base textures, formula for this function can be freely modified as in worst case merged textures will just get regenerated
  486. {
  487. MD5 id;
  488. id.update(&base_0, SIZE(base_0));
  489. id.update(&base_1, SIZE(base_1));
  490. return id();
  491. }
  492. /******************************************************************************/
  493. VecI ImageSize(C VecI &src, C VecI2 &custom, bool pow2)
  494. {
  495. VecI size=src;
  496. if( custom.x>0)size.x=custom.x;
  497. if( custom.y>0)size.y=custom.y;
  498. if(!custom.x && custom.y>0 && src.y)size.x=Max(1, (src.x*custom.y+src.y/2)/src.y); // keep aspect ratio
  499. if(!custom.y && custom.x>0 && src.x)size.y=Max(1, (src.y*custom.x+src.x/2)/src.x); // keep aspect ratio
  500. if(pow2)size.set(NearestPow2(size.x), NearestPow2(size.y), NearestPow2(size.z));
  501. return size;
  502. }
  503. bool EditToGameImage(Image &edit, Image &game, bool pow2, bool alpha_lum, ElmImage::TYPE type, int mode, int mip_maps, bool has_color, bool has_alpha, bool ignore_alpha, C VecI2 &custom_size, C int *force_type)
  504. {
  505. VecI size=ImageSize(edit.size3(), custom_size, pow2);
  506. Image temp, *src=&edit;
  507. if(force_type)
  508. {
  509. if(*force_type==IMAGE_NONE || *force_type<0)force_type=null;
  510. }
  511. if(alpha_lum)
  512. {
  513. if(&edit!=&game){src->copyTry(temp); src=&temp;}
  514. src->alphaFromBrightness().divRgbByAlpha();
  515. }
  516. if(ignore_alpha && ImageTI[src->type()].a) // if want to ignore alpha then set it to full as some compressed texture formats will benefit from better quality (like PVRTC)
  517. {
  518. if(mip_maps<0)mip_maps=((src->mipMaps()==1) ? 1 : 0); // source will have now only one mip-map so we can't use "-1", auto-detect instead
  519. if(mode <0)mode =src->mode(); // source will now be as IMAGE_SOFT so we can't use "-1", auto-detect instead
  520. if(src->copyTry(temp, -1, -1, -1, IMAGE_R8G8B8, IMAGE_SOFT, 1))src=&temp;
  521. }
  522. IMAGE_TYPE dest_type;
  523. if(force_type)dest_type=IMAGE_TYPE(*force_type);else
  524. if(type==ElmImage::ALPHA)dest_type=IMAGE_A8;else
  525. if(type==ElmImage::FULL )dest_type=(has_color ? IMAGE_R8G8B8A8 : has_alpha ? IMAGE_L8A8 : IMAGE_L8);else
  526. ImageProps(*src, null, &dest_type, (ignore_alpha ? IGNORE_ALPHA : 0) | ((type==ElmImage::COMPRESSED2) ? FORCE_HQ : 0));
  527. if(src->type()==IMAGE_L8 && dest_type==IMAGE_A8
  528. || src->type()==IMAGE_A8 && dest_type==IMAGE_L8)
  529. {
  530. Image temp2; if(temp2.createSoftTry(src->w(), src->h(), src->d(), dest_type) && src->lockRead())
  531. {
  532. REPD(z, temp2.d())
  533. REPD(y, temp2.h())
  534. REPD(x, temp2.w())temp2.pixel3D(x, y, z, src->pixel3D(x, y, z));
  535. src->unlock();
  536. Swap(temp, temp2); src=&temp;
  537. }
  538. }
  539. return src->copyTry(game, size.x, size.y, size.z, dest_type, mode, mip_maps, FILTER_BEST, true, true);
  540. }
  541. bool EditToGameImage(Image &edit, Image &game, C ElmImage &data, C int *force_type)
  542. {
  543. return EditToGameImage(edit, game, data.pow2(), data.alphaLum(), data.type, data.mode, data.mipMaps() ? 0 : 1, data.hasColor(), data.hasAlpha3(), data.ignoreAlpha(), data.size, force_type);
  544. }
  545. /******************************************************************************/
  546. void DrawPanelImage(C PanelImage &pi, C Rect &rect, bool draw_lines)
  547. {
  548. Vec2 size=rect.size()/5;
  549. Rect r=rect; r.extend(-size/3);
  550. Rect_LU lu(r.lu(), size ); Rect_RU ru(r.ru(), size.x*3, size.y );
  551. Rect_LD ld(r.ld(), size.x, size.y*3); Rect_RD rd(r.rd(), size.x*3, size.y*3);
  552. pi.draw(lu); pi.draw(ru);
  553. pi.draw(ld); pi.draw(rd);
  554. if(draw_lines)
  555. {
  556. pi.drawScaledLines(RED, lu); pi.drawScaledLines(RED, ru);
  557. pi.drawScaledLines(RED, ld); pi.drawScaledLines(RED, rd);
  558. lu.draw(TURQ, false); ru.draw(TURQ, false);
  559. ld.draw(TURQ, false); rd.draw(TURQ, false);
  560. }
  561. }
  562. /******************************************************************************/
  563. bool UpdateMtrlTex(C Image &src, Image &dest)
  564. {
  565. Image temp; if(src.copyTry(temp, -1, -1, -1, IMAGE_R8G8B8A8, IMAGE_SOFT, 1))
  566. {
  567. // old: r=spec, g=NrmY, b=alpha, a=NrmX
  568. // new: r=NrmX, g=NrmY, b=spec , a=alpha
  569. REPD(y, temp.h())
  570. REPD(x, temp.w())
  571. {
  572. Color c=temp.color(x, y);
  573. c.set(c.a, c.g, c.r, c.b);
  574. temp.color(x, y, c);
  575. }
  576. return temp.copyTry(dest, -1, -1, -1, src.type()==IMAGE_BC3 ? IMAGE_BC7 : src.type(), src.mode(), src.mipMaps(), FILTER_BEST, true, false, false, true);
  577. }
  578. return false;
  579. }
  580. /******************************************************************************/
  581. void AdjustMaterialParams(EditMaterial &edit, Material &game, uint old_base_tex, uint new_base_tex)
  582. {
  583. TimeStamp time; time.getUTC();
  584. game._adjustParams(old_base_tex, new_base_tex);
  585. SyncByValue (edit. tech_time, time, edit.tech , game.technique);
  586. SyncByValueEqual(edit. color_time, time, edit.color.w , game.color.w );
  587. SyncByValueEqual(edit.rough_bump_time, time, edit.bump , game.bump );
  588. SyncByValueEqual(edit.rough_bump_time, time, edit.rough , game.rough );
  589. SyncByValueEqual(edit. spec_time, time, edit.specular, game.specular );
  590. SyncByValueEqual(edit. glow_time, time, edit.glow , game.glow );
  591. }
  592. /******************************************************************************/
  593. bool ImportImage(Image &image, C Str &name, int type, int mode, int mip_maps, bool decompress)
  594. {
  595. if(image.ImportTry(name, type, mode, mip_maps))
  596. {
  597. if(image.compressed() && decompress && !image.copyTry(image, -1, -1, -1, IMAGE_R8G8B8A8))return false;
  598. return true;
  599. }
  600. /*if(name.is())
  601. {
  602. File f, dec; if(f.readTry(name+".cmpr"))if(Decompress(f, dec.writeMem()))
  603. {
  604. dec.pos(0); if(image.ImportTry(dec, type, mode, mip_maps))return true;
  605. }
  606. }*/
  607. return false;
  608. }
  609. /******************************************************************************/
  610. // TEXT
  611. /******************************************************************************/
  612. bool ValidChar(char c) {return c>=32 && c<128;}
  613. bool ValidText(C Str &text, int min, int max)
  614. {
  615. if(text.length()>=min && (text.length()<=max || max<0))
  616. {
  617. REPA(text)if(!ValidChar(text[i]))return false;
  618. return true;
  619. }
  620. return false;
  621. }
  622. bool ValidFileName(C Str &name) {return name.length()>=1 && CleanFileName(name)==name;}
  623. bool ValidFileNameForUpload(C Str &name, int max)
  624. {
  625. if(ValidFileName(name) && ValidText(name, 1, max))
  626. {
  627. REPA(name)if(name[i]=='@' || name[i]=='~')return false; // because of "@new" and "~" is special char on unix ?
  628. return true;
  629. }
  630. return false;
  631. }
  632. bool ValidPass(C Str &pass) {return ValidText(pass, 4, 16) && !HasUnicode(pass);}
  633. bool ValidEnum(C Str &name)
  634. {
  635. if(!name.is())return false;
  636. FREPA(name)
  637. {
  638. uint flag=CharFlag(name[i]);
  639. bool ok=((flag&(CHARF_ALPHA|CHARF_UNDER)) || (i && (flag&CHARF_DIG))); // digit cannot be used as first character
  640. if(! ok)return false;
  641. }
  642. return true;
  643. }
  644. bool ValidSupport(C Str &support)
  645. {
  646. return !support.is() || ValidEmail(support) || ValidURL(support);
  647. }
  648. bool ValidVideo(C Str &video)
  649. {
  650. return !video.is() || (ValidURL(video) && (StartsPath(video, "http://www.youtube.com/embed") || StartsPath(video, "https://www.youtube.com/embed")));
  651. }
  652. Str YouTubeEmbedToFull(C Str &video) {return Replace(video, "/embed/", "/watch?v=");}
  653. Str YouTubeFullToEmbed(C Str &video) {return Replace(video, "/watch?v=", "/embed/");}
  654. UID PassToMD5(C Str &pass) {return MD5Mem((Str8)CaseDown(pass), pass.length());}
  655. Str NameToEnum(C Str &name)
  656. {
  657. Str e; FREPA(name)
  658. {
  659. char c=name[i];
  660. if(c==' ')c='_';
  661. if(c>='0' && c<='9'
  662. || c>='a' && c<='z'
  663. || c>='A' && c<='Z'
  664. || c=='_')e+=c;
  665. }
  666. return e;
  667. }
  668. Str TimeAgo(C DateTime &date) {DateTime now; now.getUTC(); return TimeText(now-date, TIME_NAME_MED)+" ago";}
  669. char CountS(int n) {return (n==1) ? '\0' : 's';}
  670. Str Plural(Str name) // convert to plural name
  671. {
  672. bool case_up=Equal(CaseUp(name), name, true);
  673. char last =CaseDown(name.last());
  674. if(name=="child" )name+="ren" ;else // child -> children
  675. if(name=="potato" )name+="es" ;else // potato -> potatoes
  676. if(name=="hero" )name+="es" ;else // hero -> heroes
  677. if(name=="mouse" )name.remove(1, 4)+="ice" ;else // mouse -> mice
  678. if(name=="man" )name.remove(1, 2)+="en" ;else // man -> men
  679. if(name=="woman" )name.remove(3, 2)+="en" ;else // woman -> women
  680. if(name=="goose" )name.remove(1, 4)+="eese" ;else // goose -> geese
  681. if(name=="person" )name.remove(1, 5)+="eople";else // person -> people
  682. if(last=='y' )name.removeLast()+="ies" ;else // body -> bodies
  683. if(last=='x' || last=='h' || last=='s')name+="es" ;else // box -> boxes, mesh -> meshes, bus -> buses
  684. if(last=='f')
  685. {
  686. if(name!="dwarf" && name!="roof")name.removeLast()+="ves"; // leaf -> leaves, elf -> elves (dwarf -> dwarfs, roof -> roofs)
  687. }else
  688. if(Ends(name, "fe"))name.removeLast().removeLast()+="ves";else // life -> lives, knife -> knives
  689. name+='s';
  690. return case_up ? CaseUp(name) : name;
  691. }
  692. Str RemoveQuotes(Str s)
  693. {
  694. if(s.last ()=='"')s.removeLast();
  695. if(s.first()=='"')s.remove(0);
  696. return s;
  697. }
  698. /******************************************************************************/
  699. Str VecI2AsText(C VecI2 &v) // try to keep as one value if XY are the same
  700. {
  701. Str s; s=v.x; if(v.y!=v.x)s+=S+","+v.y;
  702. return s;
  703. }
  704. Vec2 TextVec2Ex(cchar *t)
  705. {
  706. return Contains(t, ',') ? TextVec2(t) : Vec2(TextFlt(t));
  707. }
  708. Vec TextVecEx(cchar *t)
  709. {
  710. return Contains(t, ',') ? TextVec(t) : Vec(TextFlt(t));
  711. }
  712. Str TextVecEx(C Vec &v, int precision)
  713. {
  714. return (Equal(v.x, v.y) && Equal(v.x, v.z)) ? TextReal(v.x, precision) : v.asText(precision);
  715. }
  716. /******************************************************************************/
  717. Str RelativePath (C Str &path) {return SkipStartPath(path, GetPath(App.exe()));}
  718. Str EditToGamePath( Str path)
  719. {
  720. Str out;
  721. for(path.tailSlash(false); ; )
  722. {
  723. Str base=GetBase(path); path=GetPath(path);
  724. if(!base.is() )return path.tailSlash(true)+out; // needed for "/xxx" unix style paths
  725. if( base=="Edit")return path.tailSlash(true)+"Game\\"+out; // replace "edit" with "game"
  726. out=(out.is() ? base.tailSlash(true)+out : base);
  727. }
  728. }
  729. /******************************************************************************/
  730. cchar8 *FormatSuffixes[]=
  731. {
  732. "_BC1",
  733. "_BC3", // for Web
  734. "_ETC1",
  735. "_ETC2",
  736. "_ETC2_A8",
  737. "_PVRTC1_2",
  738. "_PVRTC1_4",
  739. "_SIMPLE", // used for simplified Materials
  740. }; int FormatSuffixElms=Elms(FormatSuffixes);
  741. cchar8* FormatSuffixSimple() {return "_SIMPLE";}
  742. cchar8* FormatSuffix(IMAGE_TYPE type)
  743. {
  744. switch(type)
  745. {
  746. default: return null;
  747. case IMAGE_BC1: return "_BC1";
  748. case IMAGE_BC3: return "_BC3";
  749. case IMAGE_ETC1 : return "_ETC1";
  750. case IMAGE_ETC2 : return "_ETC2";
  751. case IMAGE_ETC2_A8: return "_ETC2_A8";
  752. case IMAGE_PVRTC1_2: return "_PVRTC1_2";
  753. case IMAGE_PVRTC1_4: return "_PVRTC1_4";
  754. }
  755. }
  756. Str8 ImageDownSizeSuffix(int size)
  757. {
  758. if(size>0 && size<INT_MAX)return S+"_"+size;
  759. return S;
  760. }
  761. /******************************************************************************/
  762. bool MonoTransform(C TextParam &p ) {return p.name=="grey" || p.name=="greyPhoto" || p.name=="bump" || (p.name=="channel" && p.value.length()==1);}
  763. bool ResizeTransform(C Str &name) {return name=="resize" || name=="resizeWrap" || name=="resizeClamp" || name=="resizeLinear" || name=="resizeCubic" || name=="resizeNoStretch" || name=="maxSize";}
  764. bool UVTransform(C Str &name) {return ResizeTransform(name) || name=="crop" || name=="swapXY" || name=="mirrorX" || name=="mirrorY";}
  765. bool NonMonoTransform(C Str &name) {return UVTransform(name) || name=="blur" || name=="normalize";} // if can change a mono image to non-mono, this is NOT the same as "!MonoTransform"
  766. bool HighPrecTransform(C Str &name)
  767. {
  768. return ResizeTransform(name)
  769. || name=="mulRGB" || name=="addRGB" || name=="mulAddRGB" || name=="addMulRGB" || name=="mulA"
  770. || name=="mulRGBS" || name=="mulRGBH" || name=="mulRGBHS"
  771. || name=="normalize"
  772. || name=="scale" || name=="scaleXY"
  773. || name=="lerpRGB" || name=="ilerpRGB"
  774. || name=="blur"
  775. || name=="bump"
  776. || name=="contrast" || name=="contrastLum" || name=="contrastAlphaWeight" || name=="contrastLumAlphaWeight"
  777. || name=="brightness" || name=="brightnessLum"
  778. || name=="gamma" || name=="gammaLum"
  779. || name=="greyPhoto"
  780. || name=="avgLum" || name=="medLum" || name=="avgContrastLum" || name=="medContrastLum"
  781. || name=="avgHue" || name=="medHue" || name=="addHue" || name=="setHue" || name=="contrastHue" || name=="contrastHueAlphaWeight" || name=="contrastHuePow"
  782. || name=="lerpHue" || name=="lerpHueSat" || name=="rollHue" || name=="rollHueSat" || name=="lerpHuePhoto" || name=="lerpHueSatPhoto" || name=="rollHuePhoto" || name=="rollHueSatPhoto"
  783. || name=="addSat" || name=="mulSat" || name=="mulSatPhoto" || name=="avgSat" || name=="contrastSat" || name=="contrastSatAlphaWeight"
  784. || name=="addHueSat" || name=="setHueSat" || name=="setHueSatPhoto"
  785. || name=="mulSatH" || name=="mulSatHS" || name=="mulSatHPhoto" || name=="mulSatHSPhoto";
  786. }
  787. TextParam* FindTransform(MemPtr<Edit::FileParams> files, C Str &name)
  788. {
  789. int files_with_names=0; REPA(files)if(files[i].name.is())files_with_names++; // count how many files have names(images) and aren't just transforms
  790. REPA(files)
  791. {
  792. Edit::FileParams &file=files[i];
  793. if(files_with_names<=1 || !file.name.is())REPA(file.params)
  794. {
  795. TextParam &p=file.params[i]; if(p.name==name && !Contains(p.value, '@'))return &p;
  796. }
  797. }
  798. return null;
  799. }
  800. void DelTransform(MemPtr<Edit::FileParams> files, C Str &name)
  801. {
  802. int files_with_names=0; REPA(files)if(files[i].name.is())files_with_names++; // count how many files have names(images) and aren't just transforms
  803. REPA(files)
  804. {
  805. Edit::FileParams &file=files[i];
  806. if(files_with_names<=1 || !file.name.is())REPA(file.params)
  807. {
  808. TextParam &p=file.params[i]; if(p.name==name && !Contains(p.value, '@'))
  809. {
  810. file.params.remove(i, true);
  811. return;
  812. }
  813. }
  814. }
  815. }
  816. void SetTransform(MemPtr<Edit::FileParams> files, C Str &name, C Str &value)
  817. {
  818. if(files.elms())
  819. {
  820. int files_with_names=0; REPA(files)if(files[i].name.is())files_with_names++; // count how many files have names(images) and aren't just transforms
  821. if( files_with_names>1) // if we have more than one image, then we need to make sure that we add the parameter not to one of the images, but as last file that has no name specified
  822. if(files.last().name.is()) // if the last file has a name specified
  823. files.New(); // create an empty file with no name
  824. Edit::FileParams &file=files.last(); // set param to the last file
  825. TextParam *p;
  826. REPA(file.params)
  827. {
  828. p=&file.params[i]; if(p->name==name && !Contains(p->value, '@'))goto found;
  829. }
  830. p=&file.params.New().setName(name);
  831. found:
  832. p->setValue(value);
  833. }
  834. }
  835. bool ForcesMono(C Str &file)
  836. {
  837. Mems<Edit::FileParams> files=Edit::FileParams::Decode(file);
  838. if(files.elms())
  839. {
  840. C Edit::FileParams &file=files.last();
  841. if(!file.name.is() || files.elms()==1)REPA(file.params) // go from end
  842. {
  843. C TextParam &param=file.params[i];
  844. if( MonoTransform(param ))return true;
  845. if(!NonMonoTransform(param.name))break;
  846. }
  847. }
  848. return false;
  849. }
  850. void SetTransform(Str &file, C Str &name, C Str &value)
  851. {
  852. Mems<Edit::FileParams> files=Edit::FileParams::Decode(file);
  853. SetTransform(files, name, value);
  854. file=Edit::FileParams::Encode(files);
  855. }
  856. Str BumpFromColTransform(C Str &color_map, int blur)
  857. {
  858. Mems<Edit::FileParams> files=Edit::FileParams::Decode(color_map);
  859. if(files.elms()==1)
  860. {
  861. Edit::FileParams &file=files[0];
  862. REPA(file.params)
  863. {
  864. TextParam &par=file.params[i]; if(par.name!="crop" && par.name!="swapXY" && par.name!="mirrorX" && par.name!="mirrorY")file.params.remove(i, true);
  865. }
  866. }
  867. SetTransform(files, "bump", (blur<0) ? S : S+blur);
  868. return Edit::FileParams::Encode(files);
  869. }
  870. /******************************************************************************/
  871. SOUND_CODEC TextSoundCodec(C Str &t)
  872. {
  873. if(t=="raw"
  874. || t=="wav"
  875. || t=="uncompressed")return SOUND_WAV;
  876. if(t=="vorbis" )return SOUND_SND_VORBIS;
  877. if(t=="opus" )return SOUND_SND_OPUS;
  878. return SOUND_NONE;
  879. }
  880. /******************************************************************************/
  881. // MESH
  882. /******************************************************************************/
  883. int VisibleVtxs (C MeshLod &mesh) {int num =0; REPA(mesh)if(!(mesh.parts[i].part_flag&MSHP_HIDDEN))num +=mesh.parts[i].vtxs (); return num ;}
  884. int VisibleTris (C MeshLod &mesh) {int num =0; REPA(mesh)if(!(mesh.parts[i].part_flag&MSHP_HIDDEN))num +=mesh.parts[i].tris (); return num ;}
  885. int VisibleTrisTotal (C MeshLod &mesh) {int num =0; REPA(mesh)if(!(mesh.parts[i].part_flag&MSHP_HIDDEN))num +=mesh.parts[i].trisTotal (); return num ;}
  886. int VisibleQuads (C MeshLod &mesh) {int num =0; REPA(mesh)if(!(mesh.parts[i].part_flag&MSHP_HIDDEN))num +=mesh.parts[i].quads (); return num ;}
  887. int VisibleSize (C MeshLod &mesh) {int size=0; REPA(mesh)if(!(mesh.parts[i].part_flag&MSHP_HIDDEN))size+=mesh.parts[i].render.memUsage(); return size;}
  888. flt VisibleLodQuality(C Mesh &mesh, int lod_index)
  889. {
  890. Clamp(lod_index, 0, mesh.lods());
  891. C MeshLod &base=mesh,
  892. &lod =mesh.lod(lod_index);
  893. Int v =VisibleVtxs (base),
  894. f =VisibleTrisTotal(base);
  895. return Avg(v ? flt(VisibleVtxs (lod))/v : 1,
  896. f ? flt(VisibleTrisTotal(lod))/f : 1);
  897. }
  898. /******************************************************************************/
  899. void KeepParams(C Mesh &src, Mesh &dest)
  900. {
  901. REPAD(d, dest)
  902. {
  903. MeshPart &dest_part=dest.parts[d];
  904. int src_part_i=-1;
  905. REPAD(s, src)
  906. {
  907. C MeshPart &src_part=src.parts[s];
  908. if(Equal(src_part.name, dest_part.name)) // if same name
  909. if(src_part_i<0 || Abs(s-d)<Abs(src_part_i-d))src_part_i=s; // if new index is closer to original index
  910. }
  911. if(InRange(src_part_i, src))
  912. {
  913. C MeshPart &src_part=src.parts[src_part_i];
  914. dest_part.part_flag=src_part.part_flag;
  915. dest_part.drawGroup(src_part.drawGroup(), src.drawGroupEnum());
  916. dest_part.variations(src_part.variations());
  917. REP(dest_part.variations())if(i)dest_part.variation(i, src_part.variation(i));
  918. }
  919. }
  920. dest.drawGroupEnum(src.drawGroupEnum()); // keep the same draw group enum
  921. }
  922. void EditToGameMesh(C Mesh &edit, Mesh &game, Skeleton *skel, Enum *draw_group, C Matrix *matrix)
  923. {
  924. game.create(edit, GameMeshFlagAnd);
  925. // cleanup mesh
  926. REPD(l, game.lods()) // have to go from end because we're removing LOD's
  927. {
  928. MeshLod &lod=game.lod(l);
  929. // remove LOD's
  930. if(NegativeSB(lod.dist2) // negative distance (marked as disabled)
  931. || InRange(l+1, game.lods()) && lod.dist2>=game.lod(l+1).dist2) // distance is higher than the next one (have to check next one and not previous one, because we need to delete those with negative distance first)
  932. {
  933. remove_lod:
  934. game.removeLod(l);
  935. }else
  936. {
  937. // remove hidden mesh parts
  938. REPA(lod)if(lod.parts[i].part_flag&MSHP_HIDDEN)lod.parts.remove(i);
  939. if(!lod.parts.elms())goto remove_lod;
  940. }
  941. }
  942. game.joinAll(true, true, false, MeshJoinAllTestVtxFlag, -1); // disable vtx weld, because: 1) mesh isn't scaled/transformed yet 2) it would be performed only if some parts were merged 3) there are no tangents yet. Instead let's always do it manually
  943. if(matrix)game.transform(*matrix); // transform before welding
  944. game.setAutoTanBin() // calculate tangents before welding
  945. .weldVtx(VTX_ALL, EPS, EPS_COL8_COS, -1).skeleton(skel).drawGroupEnum(draw_group).setBox(); // use 8-bit for vtx normals because they can't handle more anyway, always recalculate box because of transform and welding
  946. game.setRender().delBase();
  947. }
  948. /******************************************************************************/
  949. bool HasMaterial(C MeshPart &part, C MaterialPtr &material)
  950. {
  951. REP(part.variations())if(part.variation(i)==material)return true;
  952. return false;
  953. }
  954. /******************************************************************************/
  955. bool FixVtxNrm(MeshBase &base)
  956. {
  957. bool ok=false;
  958. MeshBase temp(base, VTX_POS|FACE_IND); temp.setNormals();
  959. if(base.vtxs()==temp.vtxs() && temp.vtx.nrm()) // safety checks
  960. {
  961. ok=true;
  962. REPA(base.vtx)
  963. {
  964. Vec &nrm=base.vtx.nrm(i); if(!nrm.any())
  965. {
  966. nrm=temp.vtx.nrm(i); if(!nrm.any())ok=false;
  967. }
  968. }
  969. }
  970. return ok;
  971. }
  972. void FixMesh(Mesh &mesh)
  973. {
  974. mesh.setBase(true).delRender() // create base if empty
  975. .material(null).variations(0) // clear any existing materials, they will be set later according to 'mtrls'
  976. .skeleton(null).drawGroupEnum(null) // clear 'skeleton', clear 'drawGroupEnum'
  977. .removeUnusedVtxs(); // remove unused vertexes
  978. // check if some vtx normals are wrong
  979. REP(mesh.lods())
  980. {
  981. MeshLod &lod=mesh.lod(i); REPA(lod)
  982. {
  983. MeshPart &part=lod.parts[i];
  984. MeshBase &base=part.base;
  985. if(base.vtx.nrm())REPA(base.vtx)if(!base.vtx.nrm(i).any()) // all zero
  986. {
  987. if(!FixVtxNrm(base)) // if didn't fix yet, then it's possible that vtx shares only co-planar faces
  988. {
  989. base.explodeVtxs();
  990. FixVtxNrm(base);
  991. base.weldVtx(VTX_ALL, EPSD, EPS_COL_COS, -1); // use small epsilon in case mesh is scaled down, do not remove degenerate faces because they're not needed because we're doing this only because of 'explodeVtxs'
  992. }
  993. break;
  994. }
  995. }
  996. }
  997. }
  998. bool SamePartInAllLods(C Mesh &mesh, int part)
  999. {
  1000. if(InRange(part, mesh.parts))
  1001. {
  1002. cchar8 *name=mesh.parts[part].name;
  1003. for(Int i=mesh.lods(); --i>=1; )
  1004. {
  1005. C MeshLod &lod=mesh.lod(i);
  1006. if(!InRange(part, lod) || !Equal(name, lod.parts[part].name))return false;
  1007. }
  1008. return true;
  1009. }
  1010. return false;
  1011. }
  1012. void SetDrawGroup(Mesh &mesh, MeshLod &lod, int part, int group, Enum *draw_group_enum)
  1013. {
  1014. if(SamePartInAllLods(mesh, part))
  1015. {
  1016. REP(mesh.lods())
  1017. {
  1018. MeshLod &lod=mesh.lod(i);
  1019. if(InRange(part, lod))lod.parts[part].drawGroup(group, draw_group_enum);
  1020. }
  1021. }else
  1022. {
  1023. if(InRange(part, lod))lod.parts[part].drawGroup(group, draw_group_enum);
  1024. }
  1025. }
  1026. /******************************************************************************/
  1027. // SKELETON
  1028. /******************************************************************************/
  1029. Str BoneNeutralName(C Str &name)
  1030. {
  1031. Str n=Replace(name, "right", CharAlpha);
  1032. n=Replace(n , "left" , CharAlpha);
  1033. n.replace('r', CharBeta).replace('l', CharBeta).replace('R', CharBeta).replace('L', CharBeta);
  1034. return n;
  1035. }
  1036. /******************************************************************************/
  1037. // OBJECT
  1038. /******************************************************************************/
  1039. // following functions are used to determine whether object should override mesh/phys
  1040. bool OverrideMeshSkel(C Mesh *mesh, C Skeleton *skel) {return (mesh && mesh->is()) || (skel && skel->is());}
  1041. bool OverridePhys (C PhysBody *body ) {return (body && body->is());}
  1042. int CompareObj(C Game::Area::Data::AreaObj &a, C Game::Area::Data::AreaObj &b) // this function is used for sorting object before they're saved into game area
  1043. {
  1044. if(int c=Compare(a.mesh().id() , b.mesh().id() ))return c; // first compare by mesh
  1045. if(int c=Compare(a.meshVariationID(), b.meshVariationID()))return c; // then compare by mesh variation
  1046. if(int c=Compare(a.matrix.pos , b.matrix.pos ))return c; // last compare by position
  1047. return 0;
  1048. }
  1049. /******************************************************************************/
  1050. // ANIMATION
  1051. /******************************************************************************/
  1052. void SetRootMoveRot(Animation &anim, C Vec *root_move, C Vec *root_rot)
  1053. {
  1054. if(root_rot)
  1055. {
  1056. const int precision=4; // number of keyframes per 90 deg, can be modified, this is needed because rotation interpolation is done by interpolating axis vectors, and few samples are needed to get smooth results
  1057. int num=(Equal(*root_rot, VecZero) ? Equal(anim.rootStart().angle(), 0) ? 0 : 1 : 1+Max(1, Round(root_rot->length()*(precision/PI_2))));
  1058. anim.keys.orns.setNum(num);
  1059. if(num)
  1060. {
  1061. OrientD orn=anim.rootStart();
  1062. anim.keys.orns[0].time=0;
  1063. anim.keys.orns[0].orn =orn;
  1064. if(num>=2)
  1065. {
  1066. num--;
  1067. VecD axis=*root_rot; dbl angle=axis.normalize(); MatrixD3 rot; rot.setRotate(axis, angle/num);
  1068. for(int i=1; i<=num; i++)
  1069. {
  1070. orn.mul(rot, true);
  1071. anim.keys.orns[i].time=flt(i)/num*anim.length();
  1072. anim.keys.orns[i].orn =orn;
  1073. }
  1074. }
  1075. }
  1076. }
  1077. if(root_move)
  1078. {
  1079. const int precision=10; // number of keyframes per meter, can be modified
  1080. bool no_rot=(!root_rot || anim.keys.orns.elms()<=1);
  1081. int num=(Equal(*root_move, VecZero) ? Equal(anim.rootStart().pos, VecZero) ? 0 : 1 : no_rot ? 2 : 1+Max(1, Round(root_move->length()*precision)));
  1082. anim.keys.poss.setNum(num);
  1083. if(num)
  1084. {
  1085. anim.keys.poss[0].time=0;
  1086. anim.keys.poss[0].pos =anim.rootStart().pos;
  1087. if(num>=2)
  1088. {
  1089. if(no_rot)
  1090. {
  1091. anim.keys.poss[1].time=anim.length();
  1092. anim.keys.poss[1].pos =anim.rootStart().pos+*root_move;
  1093. }else
  1094. {
  1095. num--;
  1096. VecD pos=anim.rootStart().pos, dir=*root_move/num, axis=*root_rot; dbl angle=axis.normalize(); MatrixD3 rot; rot.setRotate(axis, angle/num);
  1097. for(int i=1; i<=num; i++)
  1098. {
  1099. dir*=rot; pos+=dir;
  1100. anim.keys.poss[i].time=flt(i)/num*anim.length();
  1101. anim.keys.poss[i].pos =pos;
  1102. }
  1103. }
  1104. }
  1105. }
  1106. }
  1107. if(root_move || root_rot)
  1108. {
  1109. anim.keys.setTangents(anim.loop(), anim.length());
  1110. anim.setRootMatrix();
  1111. }
  1112. }
  1113. /******************************************************************************/
  1114. // MATH
  1115. /******************************************************************************/
  1116. // have to work with SIGN_BIT for special case of -0
  1117. // have to work with SIGN_BIT for special case of -0
  1118. /******************************************************************************/
  1119. int UniquePairs(int elms) {return elms*(elms-1)/2;}
  1120. /******************************************************************************/
  1121. bool Distance2D(C Vec2 &point, C Edge &edge, flt &dist, flt min_length) // calculate 2D distance between 'point' and 'edge' projected to screen, true is returned if 'edge' is at least partially visible (not behind camera), 'dist' will contain distance between point and edge
  1122. {
  1123. Edge e=edge;
  1124. if(Clip(e, Plane(ActiveCam.matrix.pos + D.viewFrom()*ActiveCam.matrix.z, -ActiveCam.matrix.z)))
  1125. {
  1126. Edge2 e2(PosToScreen(e.p[0]), PosToScreen(e.p[1]));
  1127. if(e2.length()<min_length)return false; // if edge is too short then skip it
  1128. dist=Dist(point, e2);
  1129. return true;
  1130. }
  1131. return false;
  1132. }
  1133. int MatrixAxis(C Vec2 &screen_pos, C Matrix &matrix)
  1134. {
  1135. int axis=-1;
  1136. flt d, dist=0;
  1137. if(Distance2D(screen_pos, Edge(matrix.pos, matrix.pos+matrix.x), d))if(axis<0 || d<dist){dist=d; axis=0;}
  1138. if(Distance2D(screen_pos, Edge(matrix.pos, matrix.pos+matrix.y), d))if(axis<0 || d<dist){dist=d; axis=1;}
  1139. if(Distance2D(screen_pos, Edge(matrix.pos, matrix.pos+matrix.z), d))if(axis<0 || d<dist){dist=d; axis=2;}
  1140. if(dist>0.05f)axis=-1;
  1141. return axis;
  1142. }
  1143. void MatrixAxis(Edit::Viewport4 &v4, C Matrix &matrix, int &axis, Vec *axis_vec)
  1144. {
  1145. int editing=-1; REPA(MT)if(MT.b(i, MT.touch(i) ? 0 : 1) && v4.getView(MT.guiObj(i))){editing=i; break;}
  1146. if( editing<0)
  1147. {
  1148. axis=-1;
  1149. if(editing< 0)REPA(MT)if(!MT.touch(i) && v4.getView(MT.guiObj(i))){editing=i; break;} // get mouse
  1150. if(editing>=0)if(Edit::Viewport4::View *view=v4.getView(MT.guiObj(editing)))
  1151. {
  1152. view->setViewportCamera();
  1153. axis=MatrixAxis(MT.pos(editing), matrix);
  1154. }
  1155. if(axis_vec)switch(axis)
  1156. {
  1157. case 0: *axis_vec=!matrix.x; break;
  1158. case 1: *axis_vec=!matrix.y; break;
  1159. case 2: *axis_vec=!matrix.z; break;
  1160. default: axis_vec->zero() ; break;
  1161. }
  1162. }
  1163. }
  1164. int GetNearestAxis(C Matrix &matrix, C Vec &dir)
  1165. {
  1166. flt dx=Abs(Dot(!matrix.x, dir)),
  1167. dy=Abs(Dot(!matrix.y, dir)),
  1168. dz=Abs(Dot(!matrix.z, dir));
  1169. return MaxI(dx, dy, dz);
  1170. }
  1171. bool UniformScale(C Matrix3 &m) {return UniformScale(m.scale());}
  1172. bool UniformScale(C Vec &s)
  1173. {
  1174. return Equal(s.x/s.y, 1)
  1175. && Equal(s.x/s.z, 1);
  1176. }
  1177. /******************************************************************************/
  1178. flt CamMoveScale(bool perspective ) {return perspective ? ActiveCam.dist*Tan(D.viewFov()/2) : D.viewFov();}
  1179. Vec2 MoveScale(Edit::Viewport4::View &view) {return Vec2(D.w()*2, D.h()*2)/view.viewport.size();}
  1180. flt AlignDirToCamEx(C Vec &dir, C Vec2 &delta, C Vec &cam_x, C Vec &cam_y) {return (!Vec2(Dot(cam_x, dir), Dot(cam_y, dir)) * delta).sum();}
  1181. Vec AlignDirToCam (C Vec &dir, C Vec2 &delta, C Vec &cam_x, C Vec &cam_y) {return !dir * AlignDirToCamEx(dir, delta, cam_x, cam_y);}
  1182. /******************************************************************************/
  1183. flt MatrixLength(C Vec &x, C Vec &y, C Vec &z, C Vec &dir) // matrix length along direction
  1184. {
  1185. return Abs(Dot(x, dir))
  1186. +Abs(Dot(y, dir))
  1187. +Abs(Dot(z, dir));
  1188. }
  1189. /******************************************************************************/
  1190. void Include(RectI &rect, C VecI2 &x) {if(rect.valid())rect|=x;else rect=x; }
  1191. void Include(RectI &rect, C RectI &x) {if(rect.valid())rect|=x;else rect=x; }
  1192. void Include(Rect &rect, C Rect &x) {if(rect.valid())rect|=x;else rect=x; }
  1193. void Include(Rect &rect, bool &is, C Vec2 &x) {if(is )rect|=x;else{is=true; rect=x;}}
  1194. void Include(Rect &rect, bool &is, C Rect &x) {if(is )rect|=x;else{is=true; rect=x;}}
  1195. void Include(Box &box , bool &is, C Vec &v) {if(is )box |=v;else{is=true; box =v;}}
  1196. void Include(Box &box , bool &is, C Box &b) {if(is )box |=b;else{is=true; box =b;}}
  1197. /******************************************************************************/
  1198. void DrawMatrix(C Matrix &matrix, int axis)
  1199. {
  1200. matrix.draw(); switch(axis)
  1201. {
  1202. case 0: DrawArrow2(RED , matrix.pos, matrix.pos+matrix.x, 0.005f); break;
  1203. case 1: DrawArrow2(GREEN, matrix.pos, matrix.pos+matrix.y, 0.005f); break;
  1204. case 2: DrawArrow2(BLUE , matrix.pos, matrix.pos+matrix.z, 0.005f); break;
  1205. }
  1206. }
  1207. /******************************************************************************/
  1208. // MISC
  1209. /******************************************************************************/
  1210. void Hide(GuiObj &go) {go.hide();}
  1211. /******************************************************************************/
  1212. Rect GetRect(C Rect &rect, Memt<Rect> &rects) // !! this will modify 'rects' !!
  1213. {
  1214. for(flt x=D.w(); ; ) // start with right side
  1215. {
  1216. for(flt y=D.h(); ; ) // start from top
  1217. {
  1218. Rect temp=rect; temp+=Vec2(x, y)-temp.ru(); // move to test position
  1219. if(!rects.elms())return temp;
  1220. if(temp.min.y<-D.h())break; // we're outside the screen
  1221. bool cuts=false;
  1222. REPA(rects)if(Cuts(temp, rects[i]))
  1223. {
  1224. MIN(y, rects[i].min.y-EPS);
  1225. cuts=true;
  1226. }
  1227. if(!cuts)return temp;
  1228. }
  1229. // find the maximum out of rectangles min.x and remove those rectangles
  1230. int found=-1; REPA(rects)if(found==-1 || rects[i].min.x>x){found=i; x=rects[i].min.x;}
  1231. REPA(rects)if(rects[i].min.x>=x-EPS)rects.remove(i);
  1232. }
  1233. }
  1234. /******************************************************************************/
  1235. void Include(MemPtr<UID> ids, C UID &id)
  1236. {
  1237. if(id.valid())ids.binaryInclude(id, Compare);
  1238. }
  1239. /******************************************************************************/
  1240. bool Same(C Memc<UID> &a, C Memc<UID> &b)
  1241. {
  1242. if(a.elms()!=b.elms())return false;
  1243. REPA(a)if(a[i]!=b[i])return false;
  1244. return true;
  1245. }
  1246. bool Same(C Memc<ObjData> &a, C Memc<ObjData> &b)
  1247. {
  1248. if(a.elms()!=b.elms())return false;
  1249. REPA(a)
  1250. {
  1251. C ObjData &oa=a[i];
  1252. REPA(b)
  1253. {
  1254. C ObjData &ob=b[i];
  1255. if(oa.id==ob.id)if(oa.equal(ob))goto oa_equal;else break;
  1256. }
  1257. return false; // not found or not equal
  1258. oa_equal:;
  1259. }
  1260. return true;
  1261. }
  1262. void GetNewer(C Memc<ObjData> &a, C Memc<ObjData> &b, Memc<UID> &newer) // get id's of 'a' objects that are newer than 'b'
  1263. {
  1264. REPA(a)
  1265. {
  1266. C ObjData &oa=a[i], *ob=null;
  1267. REPA(b)if(b[i].id==oa.id){ob=&b[i]; break;}
  1268. if(!ob || oa.newer(*ob))newer.add(oa.id);
  1269. }
  1270. }
  1271. /******************************************************************************/
  1272. bool EmbedObject(C Box &obj_box, C VecI2 &area_xy, flt area_size)
  1273. {
  1274. return obj_box.min.x/area_size<area_xy.x-0.5f || obj_box.max.x/area_size>area_xy.x+1.5f
  1275. || obj_box.min.z/area_size<area_xy.y-0.5f || obj_box.max.z/area_size>area_xy.y+1.5f;
  1276. }
  1277. /******************************************************************************/
  1278. bool SameOS(OS_VER a, OS_VER b)
  1279. {
  1280. return OSWindows(a) && OSWindows(b)
  1281. || OSMac (a) && OSMac (b)
  1282. || OSLinux (a) && OSLinux (b)
  1283. || OSAndroid(a) && OSAndroid(b)
  1284. || OSiOS (a) && OSiOS (b);
  1285. }
  1286. /******************************************************************************/
  1287. UID GetFileNameID(Str name)
  1288. {
  1289. for(; name.is(); name=GetPath(name)){UID id=FileNameID(name); if(id.valid())return id;}
  1290. return UIDZero;
  1291. }
  1292. UID AsID(C Elm *elm) {return elm ? elm->id : UIDZero;}
  1293. /******************************************************************************/
  1294. void SetPath(WindowIO &win_io, C Str &path, bool clear)
  1295. {
  1296. Mems<Edit::FileParams> fps=Edit::FileParams::Decode(path); if(fps.elms()==1)
  1297. {
  1298. Str first=FFirstUp(fps[0].name);
  1299. if(FileInfoSystem(first).type==FSTD_FILE)first=GetPath(first);
  1300. fps[0].name=SkipStartPath(fps[0].name, first);
  1301. win_io.path(S, first).textline.set(fps[0].encode()); return;
  1302. }else
  1303. if(fps.elms()>1)
  1304. {
  1305. win_io.path(S).textline.set(path); return;
  1306. }
  1307. if(clear)win_io.path(S).textline.clear();
  1308. }
  1309. /******************************************************************************/
  1310. bool ParamTypeID (PARAM_TYPE type ) {return type==PARAM_ID || type==PARAM_ID_ARRAY;}
  1311. bool ParamTypeCompatible(PARAM_TYPE a, PARAM_TYPE b) {return a==b || (ParamTypeID(a) && ParamTypeID(b));}
  1312. bool ParamCompatible (C Param &a, C Param &b) {return a.name==b.name && ParamTypeCompatible(a.type, b.type);}
  1313. /******************************************************************************/
  1314. /*void Add(MemPtr<Rename> diff, C Rename &rename, bool optimize=true)
  1315. {
  1316. if(optimize)REPA(diff)switch(diff[i].check(rename)) // need to go from the end
  1317. {
  1318. case Rename.REQUIRED: goto add; // we already know that this is required, so skip checking
  1319. case Rename.SAME : return; // no need to apply the same change twice
  1320. case Rename.REVERSE : diff.remove(i, true); return;
  1321. //case Rename.UNKNOWN : break; // keep on checking
  1322. }
  1323. add:
  1324. diff.add(rename);
  1325. }
  1326. void AddReverse(MemPtr<Rename> diff, C Rename &rename, bool optimize=true)
  1327. {
  1328. if(optimize)REPA(diff)switch(diff[i].check(rename)) // need to go from the end
  1329. {
  1330. case Rename.REQUIRED: goto add; // we already know that this is required, so skip checking
  1331. case Rename.SAME : diff.remove(i, true); return; // !! HERE SAME AND REVERSE ARE SWITCHED !!
  1332. case Rename.REVERSE : return; // no need to apply the same change twice !! HERE SAME AND REVERSE ARE SWITCHED !!
  1333. //case Rename.UNKNOWN : break; // keep on checking
  1334. }
  1335. add:
  1336. diff.New().set(rename.dest, rename.src); // we're reversing so we need to replace dest with src
  1337. }
  1338. void Diff(MemPtr<Rename> diff, C MemPtr<Rename> &current, C MemPtr<Rename> &desired, bool optimize=true)
  1339. {
  1340. diff.clear();
  1341. int min=Min(current.elms(), desired.elms()), equal=0; for(; equal<min; equal++)if(current[equal]!=desired[equal])break; // calculate amount of equal changes
  1342. // reverse 'current'
  1343. for(int i=current.elms(); --i>=equal; )AddReverse(diff, current[i], optimize); // need to go from back
  1344. // apply 'desired'
  1345. for(int i=equal; i<desired.elms(); i++)Add(diff, desired[i], optimize); // need to go from start
  1346. }
  1347. /******************************************************************************/
  1348. UNIT_TYPE UnitType(C Str &s)
  1349. {
  1350. if(s=="px" )return UNIT_PIXEL;
  1351. if(s=="pc" || s=='%' )return UNIT_PERCENT;
  1352. if(s=="pm" || s==CharPermil)return UNIT_PERMIL;
  1353. return UNIT_DEFAULT;
  1354. }
  1355. UNIT_TYPE GetUnitType(C Str &s)
  1356. {
  1357. if(s.is())
  1358. {
  1359. const uint flag_and=CHARF_DIG10|CHARF_SIGN|CHARF_ALPHA|CHARF_UNDER|CHARF_SPACE;
  1360. uint flag=CharFlag(s.last())&flag_and;
  1361. int pos =s.length()-1; for(; pos>0 && (CharFlag(s[pos-1])&flag_and)==flag; pos--);
  1362. return UnitType(s()+pos);
  1363. }
  1364. return UNIT_DEFAULT;
  1365. }
  1366. flt ConvertUnitType(flt value, flt full, UNIT_TYPE unit)
  1367. {
  1368. switch(unit)
  1369. {
  1370. default : return value;
  1371. case UNIT_PERCENT: return value/ 100*full;
  1372. case UNIT_PERMIL : return value/1000*full;
  1373. }
  1374. }
  1375. /******************************************************************************/
  1376. // GUI
  1377. /******************************************************************************/
  1378. Color BackgroundColor()
  1379. {
  1380. return Gui.backgroundColor();
  1381. }
  1382. Color BackgroundColorLight()
  1383. {
  1384. Color col=BackgroundColor();
  1385. byte lum=col.lum(), add=44; Color col_add(add); if(lum)col_add=ColorMul(col, flt(add)/lum); // set normalized color (col/col.lum)*add
  1386. return ColorAdd(col, col_add);
  1387. }
  1388. Color GuiListTextColor()
  1389. {
  1390. if(Gui.skin && Gui.skin->list.text_style)return Gui.skin->list.text_style->color;
  1391. return BLACK;
  1392. }
  1393. const Color LitSelColor=RED, SelColor=YELLOW, LitColor=CYAN, DefColor=WHITE, InvalidColor=PURPLE;
  1394. Color GetLitSelCol(bool lit, bool sel, C Color &none)
  1395. {
  1396. if(lit && sel)return LitSelColor;
  1397. if( sel)return SelColor;
  1398. if(lit )return LitColor;
  1399. return none;
  1400. }
  1401. bool ErrorCopy(C Str &src, C Str &dest)
  1402. {
  1403. Gui.msgBox(S, S+"Error copying\n\""+src+"\"\nto\n\""+dest+'"');
  1404. return false;
  1405. }
  1406. bool ErrorRecycle(C Str &name)
  1407. {
  1408. Gui.msgBox(S, S+"Error recycling\n\""+name+"\"");
  1409. return false;
  1410. }
  1411. bool ErrorCreateDir(C Str &name)
  1412. {
  1413. Gui.msgBox(S, S+"Error creating folder\n\""+name+"\"");
  1414. return false;
  1415. }
  1416. bool RecycleLoud (C Str &name ) {return FRecycle (name ) ? true : ErrorRecycle (name);}
  1417. bool CreateDirLoud(C Str &name ) {return FCreateDirs(name ) ? true : ErrorCreateDir(name);}
  1418. bool SafeCopyLoud (C Str &src, C Str &dest) {return SafeCopy (src, dest) ? true : ErrorCopy (src, dest);}
  1419. /******************************************************************************/
  1420. // SOUND
  1421. /******************************************************************************/
  1422. const BitRateQuality BitRateQualities[]=
  1423. {
  1424. {-0.2f, 32*1000}, // aoTuV only
  1425. {-0.1f, 45*1000},
  1426. { 0.0f, 64*1000},
  1427. { 0.1f, 80*1000},
  1428. { 0.2f, 96*1000},
  1429. { 0.3f, 112*1000},
  1430. { 0.4f, 128*1000},
  1431. { 0.5f, 160*1000},
  1432. { 0.6f, 192*1000},
  1433. { 0.7f, 224*1000},
  1434. { 0.8f, 256*1000},
  1435. { 0.9f, 320*1000},
  1436. { 1.0f, 500*1000},
  1437. };
  1438. flt VorbisBitRateToQuality(int rel_bit_rate) // relative bit rate in bits per second (bit rate for 44.1kHz stereo)
  1439. {
  1440. for(int i=1; i<Elms(BitRateQualities); i++)if(rel_bit_rate<=BitRateQualities[i].bit_rate)
  1441. {
  1442. C BitRateQuality &p=BitRateQualities[i-1],
  1443. &n=BitRateQualities[i ];
  1444. flt step=LerpR(p.bit_rate, n.bit_rate, rel_bit_rate);
  1445. return Lerp (p.quality , n.quality , step);
  1446. }
  1447. return 1;
  1448. }
  1449. /******************************************************************************/
  1450. // DEPRECATED
  1451. /******************************************************************************/
  1452. int DecIntV(File &f)
  1453. {
  1454. Byte v; f>>v;
  1455. Bool positive=((v>>6)&1);
  1456. UInt u=(v&63);
  1457. if(v&128)
  1458. {
  1459. f>>v; u|=((v&127)<<6);
  1460. if(v&128)
  1461. {
  1462. f>>v; u|=((v&127)<<(6+7));
  1463. if(v&128)
  1464. {
  1465. f>>v; u|=((v&127)<<(6+7+7));
  1466. if(v&128)
  1467. {
  1468. f>>v; u|=(v<<(6+7+7+7));
  1469. }
  1470. }
  1471. }
  1472. }
  1473. return positive ? u+1 : -Int(u);
  1474. }
  1475. void GetStr2(File &f, Str &s) {s=GetStr2(f);}
  1476. Str GetStr2(File &f)
  1477. {
  1478. Int length=DecIntV(f);
  1479. if( length<0) // unicode
  1480. {
  1481. CHS(length); MIN(length, f.left()/2);
  1482. Str s; s.reserve(length); REP(length){char c; f>>c; s+=c;} return s;
  1483. }else
  1484. if(length)
  1485. {
  1486. MIN(length, f.left());
  1487. Str8 s; s.reserve(length); REP(length){char8 c; f>>c; s+=c;} return s;
  1488. }
  1489. return S;
  1490. }
  1491. void PutStr(File &f, C Str &s)
  1492. {
  1493. uint length =s.length();
  1494. bool unicode=HasUnicode(s);
  1495. f.putUInt(unicode ? length^SIGN_BIT : length);
  1496. if(length)
  1497. {
  1498. if(unicode){ f.putN(s(), length);}
  1499. else {Str8 t=s; f.putN(t(), length);}
  1500. }
  1501. }
  1502. Str GetStr(File &f)
  1503. {
  1504. uint length=f.getUInt();
  1505. if( length&SIGN_BIT) // unicode
  1506. {
  1507. length^=SIGN_BIT; MIN(length, f.left()/2);
  1508. if(length){Str s; s.reserve(length); REP(length){char c; f>>c; s+=c;} return s;}
  1509. }else
  1510. {
  1511. MIN(length, f.left());
  1512. if (length){Str8 s; s.reserve(length); REP(length){char8 c; f>>c; s+=c;} return s;}
  1513. }
  1514. return S;
  1515. }
  1516. void GetStr(File &f, Str &s) {s=GetStr(f);}
  1517. Mems<Edit::FileParams> _DecodeFileParams(C Str &str)
  1518. {
  1519. Mems<Edit::FileParams> files; if(str.is())
  1520. {
  1521. Memc<Str> strs=Split(str, '|'); // get list of all files
  1522. files.setNum(strs.elms()); FREPA(files)
  1523. {
  1524. Edit::FileParams &file=files[i];
  1525. Memc<Str> fp=Split(strs[i], '?'); // file_name?params
  1526. file.name=(fp.elms() ? fp[0] : S);
  1527. if(fp.elms()>=2)
  1528. {
  1529. Memc<Str> name_vals=Split(fp[1], '&'); FREPA(name_vals)
  1530. {
  1531. Memc<Str> name_val=Split(name_vals[i], '=');
  1532. if(name_val.elms()==2)file.params.New().set(name_val[0], name_val[1]);
  1533. }
  1534. }
  1535. }
  1536. }
  1537. return files;
  1538. }
  1539. /******************************************************************************/
  1540. /******************************************************************************/
  1541. bool FileSizeGetter::created()C { return path.is();}
  1542. bool FileSizeGetter::busy() {cleanup(); return thread.active();}
  1543. bool FileSizeGetter::get()
  1544. {
  1545. cleanup();
  1546. if(elms_thread.elms())
  1547. {
  1548. SyncLocker locker(lock);
  1549. if(!elms.elms())Swap(elms_thread, elms);else
  1550. {
  1551. FREPA(elms_thread)elms.add(elms_thread[i]);
  1552. elms_thread.clear();
  1553. }
  1554. return true;
  1555. }
  1556. return false;
  1557. }
  1558. void FileSizeGetter::clear() {elms.clear();}
  1559. void FileSizeGetter::stop() {thread.stop();}
  1560. void FileSizeGetter::del()
  1561. {
  1562. thread.del(); // del the thread first
  1563. elms_thread.clear();
  1564. elms .clear();
  1565. path .clear();
  1566. }
  1567. void FileSizeGetter::get(C Str &path)
  1568. {
  1569. del();
  1570. if(path.is())
  1571. {
  1572. T.path=path;
  1573. thread.create(Func, this);
  1574. }
  1575. }
  1576. FileSizeGetter::~FileSizeGetter() {del();}
  1577. void FileSizeGetter::cleanup()
  1578. {
  1579. if(!thread.active())thread.del(); // delete to free resources
  1580. }
  1581. bool FileSizeGetter::Func(Thread &thread) {return ((FileSizeGetter*)thread.user)->func();}
  1582. bool FileSizeGetter::func()
  1583. {
  1584. for(FileFind ff(path); !thread.wantStop() && ff(); )
  1585. {
  1586. if(ff.type==FSTD_FILE)
  1587. {
  1588. UID id; if(DecodeFileName(ff.name, id))
  1589. {
  1590. SyncLocker locker(lock);
  1591. Elm &elm=elms_thread.New();
  1592. elm.id=id;
  1593. elm.file_size=ff.size;
  1594. }
  1595. }
  1596. }
  1597. return false;
  1598. }
  1599. Rename& Rename::set(C Str &src, C Str &dest) {T.src=src; T.dest=dest; return T;}
  1600. bool Rename::operator==(C Rename &rename)C {return Equal(src, rename.src, true) && Equal(dest, rename.dest, true);}
  1601. bool Rename::operator!=(C Rename &rename)C {return !(T==rename);}
  1602. /******************************************************************************/