Project.cpp 177 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. /******************************************************************************/
  4. uint CC4_PRDT=CC4('P', 'R', 'D', 'T'); // Project Data
  5. /******************************************************************************/
  6. /******************************************************************************/
  7. Project::Project() : text_data(false), synchronize(true), compress_level(9), cipher(CIPHER_NONE), compress_type(COMPRESS_NONE), material_simplify(MS_NEVER), id(UIDZero), app_id(UIDZero), hm_mtrl_id(UIDZero), water_mtrl_id(UIDZero)
  8. {
  9. REPAO(cipher_key)=0;
  10. REPAO(mtrl_brush_id).zero();
  11. world_vers.mode(CACHE_DUMMY); // to allow creating new elements
  12. mini_map_vers.mode(CACHE_DUMMY); // to allow creating new elements
  13. }
  14. Project& Project::del()
  15. {
  16. // delete this first in case it uses this project members
  17. world_vers .del(); mini_map_vers .del();
  18. world_paths.del(); mini_map_paths.del();
  19. elms.del();
  20. texs.del(); texs_update.del();
  21. cipher=CIPHER_NONE; REPAO(cipher_key)=0;
  22. compress_type =COMPRESS_NONE;
  23. compress_level=9;
  24. material_simplify=MS_NEVER;
  25. cipher_time=cipher_key_time=compress_type_time=compress_level_time=material_simplify_time=0;
  26. text_data=false; synchronize=true; app_id.zero(); hm_mtrl_id.zero(); water_mtrl_id.zero(); REPAO(mtrl_brush_id).zero();
  27. path.del(); code_path.del(); code_base_path.del(); edit_path.del(); game_path.del(); temp_path.del(); tex_path.del(); temp_tex_path.del(); temp_tex_dynamic_path.del();
  28. id.zero(); name.del();
  29. return T;
  30. }
  31. int Project::CompareID(C Elm &elm, C UID &id) {return Compare(elm.id, id);}
  32. bool Project::valid()C {return id.valid();}
  33. bool Project::needUpdate()C {return texs_update.elms()>0;}
  34. bool Project::hasElm(C UID &id)C { return id.valid() && elms.binaryHas (id, CompareID);}
  35. int Project::findElmI(C UID &id)C {int index; return (id.valid() && elms.binarySearch(id, index, CompareID)) ? index : -1;}
  36. Elm* Project::findElm(C UID &id) {return elms.addr(findElmI(id));}
  37. C Elm* Project::findElm(C UID &id)C {return elms.addr(findElmI(id));}
  38. Elm& Project::getElm(C UID &id) {int index; if(elms.binarySearch(id, index, CompareID))return elms[index]; Elm &elm=elms.NewAt(index); elm.id=id; return elm;}
  39. Elm& Project::newElm( ) // create new element with random id
  40. {
  41. #if 0 // simple version
  42. for(;;){Elm &elm=getElm(UID().randomizeValid()); if(!elm.type)return elm;}
  43. #else // version that tests that we don't overlap with any existing ID's using ELM_OFFSET_NUM range
  44. UID id, test; again: id.randomizeValid(); test=id-ELM_OFFSET_NUM;
  45. REP(ELM_OFFSET_NUM*2+1){if(!test.valid() || hasElm(test))goto again; test++;}
  46. return getElm(id);
  47. #endif
  48. }
  49. Elm& Project::newElm(C Str &name, C UID &parent_id, ELM_TYPE type) {Elm &elm=newElm().setName(name, NewElmTime).setParent(parent_id, NewElmTime); elm.type=type; return elm;}
  50. Elm* Project::findElmByPath(C Str &path)
  51. {
  52. if(path.is())
  53. {
  54. UID parent=UIDZero; Str p=path; for(;;)
  55. {
  56. Str name=GetStart(p); p=GetStartNot(p);
  57. Elm *found_elm=null;
  58. REPA(elms)
  59. {
  60. Elm &elm=elms[i]; if(elm.parent_id==parent && elm.name==name && ElmVisible(elm.type)) // don't list hidden types
  61. {
  62. found_elm=&elm;
  63. if(!elm.removed())break; // stop looking if this element exists
  64. }
  65. }
  66. if(!p.is() )return found_elm;
  67. if(!found_elm)break;
  68. parent=found_elm->id;
  69. }
  70. }
  71. return null;
  72. }
  73. Elm* Project::findElm(C Str &text)
  74. {
  75. UID id; if(id.fromText(text))return findElm(id);
  76. return findElmByPath(text);
  77. }
  78. UID Project::findElmID(C Str &text) {if(Elm *elm=findElm(text))return elm->id; return UIDZero;}
  79. Elm* Project::findElm(C UID &id , ELM_TYPE type) {if( Elm *elm=findElm(id ))if(elm->type==type)return elm ; return null ;}
  80. C Elm* Project::findElm(C UID &id , ELM_TYPE type)C{if(C Elm *elm=findElm(id ))if(elm->type==type)return elm ; return null ;}
  81. Elm* Project::findElm(C Str &text, ELM_TYPE type) {if( Elm *elm=findElm(text))if(elm->type==type)return elm ; return null ;}
  82. UID Project::findElmID(C UID &id , ELM_TYPE type) {if( Elm *elm=findElm(id , type ))return elm->id; return UIDZero;}
  83. UID Project::findElmID(C Str &text, ELM_TYPE type) {if( Elm *elm=findElm(text, type ))return elm->id; return UIDZero;}
  84. Elm* Project::findElmImage(C Str &text) {if(Elm *elm=findElm (text))if(ElmImageLike(elm->type))return elm ; return null ;}
  85. UID Project::findElmImageID(C Str &text) {if(Elm *elm=findElmImage(text ))return elm->id; return UIDZero;}
  86. Elm* Project::findElmByTexture(C UID &tex_id)
  87. {
  88. Elm *ret=null;
  89. REPA(elms) // find first material containing this texture
  90. {
  91. Elm &elm=elms[i]; if(elm.data && elm.data->containsTex(tex_id, true))
  92. {
  93. if(elm.finalPublish())return &elm; // if found a publishable then return immediately
  94. if(elm.finalExists ())ret =&elm; // save existing for later
  95. }
  96. }
  97. return ret;
  98. }
  99. Str Project::elmFullName(C UID &elm_id, int max_elms)C {if(C Elm *elm=findElm(elm_id))return elmFullName(elm, max_elms); return elm_id.valid() ? UnknownName : S;}
  100. Str Project::elmFullName(C Elm *elm , int max_elms)C
  101. {
  102. int length=0; Memt<C Elm*> processed; Str name;
  103. for(; elm && processed.include(elm); )
  104. {
  105. if(ElmVisible(elm->type)) // don't include name of elements that are not visible
  106. {
  107. if(!max_elms--){processed.removeLast(); name.reserve(length+3)="..\\"; break;} // if reached the allowed limit
  108. length+=elm->name.length()+1; // 1 extra for '\\'
  109. }
  110. elm=findElm(elm->parent_id);
  111. }
  112. name.reserve(length); bool slash=false;
  113. REPA(processed)
  114. {
  115. C Elm &elm=*processed[i]; if(ElmVisible(elm.type))
  116. {
  117. if(slash)name+='\\';else slash=true;
  118. name+=elm.name;
  119. }
  120. }
  121. return name;
  122. }
  123. Str Project::basePath(C Elm &elm )C {return elm.id.valid() ? (ElmEdit(elm.type) ? edit_path : game_path)+EncodeFileName(elm.id) : S;}
  124. Str Project::codeBasePath(C UID &elm_id)C {return elm_id.valid() ? code_base_path +EncodeFileName(elm_id)+CodeExt : S;}
  125. Str Project::codePath(C UID &elm_id)C {return elm_id.valid() ? code_path +EncodeFileName(elm_id)+CodeExt : S;}
  126. Str Project::editPath(C UID &elm_id)C {return elm_id.valid() ? edit_path +EncodeFileName(elm_id) : S;}
  127. Str Project::gamePath(C UID &elm_id)C {return elm_id.valid() ? game_path +EncodeFileName(elm_id) : S;}
  128. Str Project::codeBasePath(C Elm &elm )C {return codeBasePath(elm.id);}
  129. Str Project::codePath(C Elm &elm )C {return codePath(elm.id);}
  130. Str Project::editPath(C Elm &elm )C {return editPath(elm.id);}
  131. Str Project::gamePath(C Elm &elm )C {return gamePath(elm.id);}
  132. Str Project::texPath(C UID &tex_id)C {return tex_id.valid() ? tex_path +EncodeFileName(tex_id) : S;}
  133. Str Project::texDynamicPath(C UID &tex_id)C {return tex_id.valid() ? temp_tex_dynamic_path +EncodeFileName(tex_id) : S;}
  134. Str Project::texFormatPath(C UID &tex_id, cchar8 *format, int downsize)C {return tex_id.valid() ? temp_tex_path +EncodeFileName(tex_id)+format+ImageDownSizeSuffix(downsize) : S;}
  135. Str Project::formatPath(C UID &elm_id, cchar8 *suffix )C {return elm_id.valid() ? temp_path +EncodeFileName(elm_id)+suffix : S;}
  136. Str Project::gameAreaPath(C UID &world_id, C VecI2 &area_xy)C {return game_path+EncodeFileName(world_id)+"\\Area\\"+area_xy;}
  137. Str Project::editAreaPath(C UID &world_id, C VecI2 &area_xy)C {return edit_path+EncodeFileName(world_id)+"\\Area\\"+area_xy;}
  138. Str Project::gameWaypointPath(C UID &world_id, C UID &waypoint_id)C {return game_path+EncodeFileName(world_id)+"\\Waypoint\\"+EncodeFileName(waypoint_id);}
  139. Str Project::editWaypointPath(C UID &world_id, C UID &waypoint_id)C {return edit_path+EncodeFileName(world_id)+"\\Waypoint\\"+EncodeFileName(waypoint_id);}
  140. bool Project::waypointExists(C UID &world_id, C UID &waypoint_id)C {return FExist(gameWaypointPath(world_id, waypoint_id));}
  141. Str Project::EditLakePath(C Str &edit_path, C UID &world_id, C UID & lake_id) {return edit_path+EncodeFileName(world_id)+"\\Lake\\" +EncodeFileName( lake_id);}
  142. Str Project::EditRiverPath(C Str &edit_path, C UID &world_id, C UID &river_id) {return edit_path+EncodeFileName(world_id)+"\\River\\"+EncodeFileName(river_id);}
  143. Str Project::editLakePath( C UID &world_id, C UID & lake_id)C {return EditLakePath (edit_path, world_id, lake_id);}
  144. Str Project::editRiverPath( C UID &world_id, C UID &river_id)C {return EditRiverPath(edit_path, world_id, river_id);}
  145. Str Project::worldVerPath(C UID &world_id)C{return editPath(world_id)+WorldVerSuffix;}
  146. WorldVer* Project::worldVerFind(C UID &world_id) {return world_vers.find(worldVerPath(world_id));}
  147. WorldVer* Project::worldVerGet(C UID &world_id) {return world_vers.get (worldVerPath(world_id));}
  148. WorldVer* Project::worldVerRequire(C UID &world_id) {return world_vers (worldVerPath(world_id));}
  149. Str Project::miniMapVerPath(C UID &mini_map_id)C{return editPath(mini_map_id);}
  150. MiniMapVer* Project::miniMapVerFind(C UID &mini_map_id) {return mini_map_vers.find(miniMapVerPath(mini_map_id));}
  151. MiniMapVer* Project::miniMapVerGet(C UID &mini_map_id) {return mini_map_vers.get (miniMapVerPath(mini_map_id));}
  152. MiniMapVer* Project::miniMapVerRequire(C UID &mini_map_id) {return mini_map_vers (miniMapVerPath(mini_map_id));}
  153. UID Project::physToMesh(C Elm *phys) {if(phys)if(C ElmPhys *phys_data=phys->physData())return phys_data->mesh_id; return UIDZero;}
  154. UID Project::animToSkel(C Elm *anim) {if(anim)if(C ElmAnim *anim_data=anim->animData())return anim_data->skel_id; return UIDZero;}
  155. UID Project::skelToMesh(C Elm *skel) {if(skel)if(C ElmSkel *skel_data=skel->skelData())return skel_data->mesh_id; return UIDZero;}
  156. UID Project::meshToSkel(C Elm *mesh) {if(mesh)if(C ElmMesh *mesh_data=mesh->meshData())return mesh_data->skel_id; return UIDZero;}
  157. UID Project::meshToObj(C Elm *mesh) {if(mesh)if(C ElmMesh *mesh_data=mesh->meshData())return mesh_data-> obj_id; return UIDZero;}
  158. UID Project::objToMesh(C Elm *obj ) {if(obj )if(C ElmObj * obj_data= obj-> objData())return obj_data->mesh_id; return UIDZero;}
  159. UID Project::animToMesh(C Elm *anim) {return skelToMesh(animToSkel(anim));}
  160. UID Project::animToObj(C Elm *anim) {return meshToObj (animToMesh(anim));}
  161. UID Project::skelToObj(C Elm *skel) {return meshToObj (skelToMesh(skel));}
  162. UID Project::physToObj(C Elm *phys) {return meshToObj (physToMesh(phys));}
  163. UID Project::objToSkel(C Elm *obj ) {return meshToSkel( objToMesh(obj ));}
  164. UID Project::animToSkel(C UID &anim_id) {return animToSkel(findElm(anim_id));}
  165. UID Project::animToMesh(C UID &anim_id) {return animToMesh(findElm(anim_id));}
  166. UID Project::animToObj(C UID &anim_id) {return animToObj (findElm(anim_id));}
  167. UID Project::skelToMesh(C UID &skel_id) {return skelToMesh(findElm(skel_id));}
  168. UID Project::skelToObj(C UID &skel_id) {return skelToObj (findElm(skel_id));}
  169. UID Project::meshToSkel(C UID &mesh_id) {return meshToSkel(findElm(mesh_id));}
  170. UID Project::meshToObj(C UID &mesh_id) {return meshToObj (findElm(mesh_id));}
  171. UID Project::physToObj(C UID &phys_id) {return physToObj (findElm(phys_id));}
  172. UID Project::objToMesh(C UID & obj_id) {return objToMesh(findElm( obj_id));}
  173. UID Project::objToSkel(C UID & obj_id) {return objToSkel(findElm( obj_id));}
  174. Elm* Project::objToMeshElm(C Elm * obj ) {return findElm(objToMesh( obj ), ELM_MESH);}
  175. Elm* Project::objToMeshElm(C UID & obj_id) {return findElm(objToMesh( obj_id), ELM_MESH);}
  176. Elm* Project::skelToObjElm(C Elm *skel ) {return findElm(skelToObj(skel ), ELM_OBJ );}
  177. Elm* Project::skelToObjElm(C UID &skel_id) {return findElm(skelToObj(skel_id), ELM_OBJ );}
  178. Elm* Project::meshToObjElm(C Elm *mesh ) {return findElm(meshToObj(mesh ), ELM_OBJ );}
  179. Elm* Project::meshToObjElm(C UID &mesh_id) {return findElm(meshToObj(mesh_id), ELM_OBJ );}
  180. Elm* Project::animToObjElm(C Elm *anim ) {return findElm(animToObj(anim ), ELM_OBJ );}
  181. Elm* Project::animToObjElm(C UID &anim_id) {return findElm(animToObj(anim_id), ELM_OBJ );}
  182. Elm* Project::physToObjElm(C Elm *phys ) {return findElm(physToObj(phys ), ELM_OBJ );}
  183. Elm* Project::physToObjElm(C UID &phys_id) {return findElm(physToObj(phys_id), ELM_OBJ );}
  184. Elm* Project::mtrlToMeshElm(C UID &mtrl_id)
  185. {
  186. if(mtrl_id.valid())REPA(elms){Elm &elm=elms[i]; if(C ElmMesh *mesh_data=elm.meshData())if(mesh_data->mtrl_ids.binaryHas(mtrl_id, Compare))return &elm;}
  187. return null;
  188. }
  189. UID Project::mtrlToObj(C UID &mtrl_id)
  190. {
  191. if(Elm *mesh=mtrlToMeshElm(mtrl_id))return meshToObj(mesh);
  192. return UIDZero;
  193. }
  194. void Project::getTextures(MemPtr<UID> texs, bool existing_elms)C // 'existing_elms'=if get only from elements that exist (are not removed)
  195. {
  196. texs.clear();
  197. REPA(elms){C Elm &elm=elms[i]; if(existing_elms ? elm.finalExists() : true)if(elm.data)elm.data->listTexs(texs);} // get textures from elements
  198. }
  199. void Project::getUsedMaterials(MemPtr<UID> used, bool publish)C // !! this needs to have hierarchy set !! 'publish'=if true then get materials used by publishable elements, if false then get materials used by existing elements
  200. {
  201. used.clear();
  202. REPA(elms)
  203. {
  204. C Elm &elm=elms[i]; if(publish ? elm.finalPublish() : elm.finalExists())switch(elm.type)
  205. {
  206. case ELM_MESH:
  207. {
  208. if(C ElmMesh *mesh_data=elm.meshData())REPA(mesh_data->mtrl_ids)used.binaryInclude(mesh_data->mtrl_ids[i], Compare);
  209. }break;
  210. }
  211. }
  212. }
  213. Enum* Project::getEnum(C UID &enum_id)
  214. {
  215. return Enums(gamePath(enum_id));
  216. }
  217. void Project::getMeshSkels(ElmMesh *mesh_data, UID *mesh_skel, UID *body_skel) // get skeletons for specified mesh
  218. {
  219. if(mesh_skel)*mesh_skel=(mesh_data ? mesh_data->skel_id : UIDZero);
  220. // get the last body
  221. if(body_skel)
  222. {
  223. for(Memt<UID> bodies; mesh_data && bodies.include(mesh_data->body_id); )if(Elm *body=findElm(mesh_data->body_id))mesh_data=body->meshData();
  224. *body_skel=(mesh_data ? mesh_data->skel_id : UIDZero);
  225. }
  226. }
  227. void Project::getMeshSkels(ElmMesh *mesh_data, Skeleton* *mesh_skel, Skeleton* *body_skel) // get skeletons for specified mesh
  228. {
  229. UID mesh_skel_id, body_skel_id;
  230. getMeshSkels(mesh_data, mesh_skel ? &mesh_skel_id : null, body_skel ? &body_skel_id : null);
  231. if(mesh_skel)*mesh_skel=Skeletons(gamePath(mesh_skel_id));
  232. if(body_skel)*body_skel=Skeletons(gamePath(body_skel_id));
  233. }
  234. bool Project::getObjBox(C UID &elm_id, Box &box) // get box of ELM_OBJ (mesh.box|phys.box)
  235. {
  236. Memt<UID> processed; for(UID id=elm_id; processed.binaryInclude(id, Compare); ) // to avoid potential infinite loops (A is based on A)
  237. if(Elm *obj_elm=findElm(id))if(ElmObj *obj=obj_elm->objData())
  238. {
  239. if(Elm *mesh_elm=findElm(obj->mesh_id))if(ElmMesh *mesh=mesh_elm->meshData())
  240. {
  241. bool have=false; if(mesh->box.valid())if(have)box|=mesh->box;else{box=mesh->box; have=true;}
  242. if(Elm *phys_elm=findElm(mesh->phys_id))if(ElmPhys *phys=phys_elm->physData())if(phys->box.valid())if(have)box|=phys->box;else{box=phys->box; have=true;}
  243. return have;
  244. }
  245. id=obj->base_id; // check base
  246. }
  247. box.zero(); return false;
  248. }
  249. bool Project::getObjTerrain(C UID &elm_id) // get if ELM_OBJ has OBJ_ACCESS_TERRAIN
  250. {
  251. Memt<UID> processed; for(UID id=elm_id; processed.binaryInclude(id, Compare); ) // to avoid potential infinite loops (A is based on A)
  252. if(Elm *obj_elm=findElm(id))
  253. {
  254. if(ElmObjClass *obj=obj_elm->objClassData())return obj->terrain();
  255. if(ElmObj *obj=obj_elm-> objData()){if(obj->ovrAccess())return obj->terrain(); id=obj->base_id;} // check base
  256. }
  257. return true;
  258. }
  259. OBJ_PATH Project::getObjPath(C UID &elm_id) // get OBJ_PATH of ELM_OBJ
  260. {
  261. Memt<UID> processed; for(UID id=elm_id; processed.binaryInclude(id, Compare); ) // to avoid potential infinite loops (A is based on A)
  262. if(Elm *obj_elm=findElm(id))
  263. {
  264. if(ElmObjClass *obj=obj_elm->objClassData()) return obj->pathSelf();
  265. if(ElmObj *obj=obj_elm-> objData()){if(obj->ovrPath())return obj->pathSelf(); id=obj->base_id;} // check base
  266. }
  267. return OBJ_PATH_CREATE;
  268. }
  269. RectI Project::getWorldAreas(C UID &world_id, bool water)
  270. {
  271. RectI rect(0, -1);
  272. if(Elm *world=findElm(world_id, ELM_WORLD))
  273. if(WorldVer *world_ver=worldVerGet(world_id))
  274. {
  275. Include(rect, world_ver->getTerrainAreas ());
  276. Include(rect, world_ver->getObjAreas ());
  277. Include(rect, world_ver->getObjEmbedAreas());
  278. if(water)Include(rect, world_ver->getLakeAreas ());
  279. if(water)Include(rect, world_ver->getRiverAreas ());
  280. }
  281. return rect;
  282. }
  283. int Project::worldAreasToRebuild(C UID *world_id)
  284. {
  285. int n=0;
  286. if(!world_id)REPA(world_vers)n+=world_vers.lockedData(i).rebuild.elms();else // check all worlds
  287. if(WorldVer *world_ver=worldVerFind(*world_id)) n+=world_ver->rebuild.elms(); // check only specified world
  288. return n;
  289. }
  290. bool Project::materialSimplify(Edit::EXE_TYPE type)C
  291. {
  292. switch(material_simplify)
  293. {
  294. default : return false; // MS_NEVER
  295. case MS_MOBILE: return type==Edit::EXE_APK || type==Edit::EXE_IOS;
  296. case MS_ALWAYS: return true;
  297. }
  298. }
  299. bool Project::isBasedOnObjs(C Elm &elm, C Memt<UID> &objs)C // check if 'elm' is based on 'objs' (assumes that 'objs' is sorted)
  300. {
  301. Memt<UID> processed;
  302. if(elm.type==ELM_OBJ || elm.type==ELM_OBJ_CLASS)for(UID id=elm.id; processed.binaryInclude(id, Compare); ) // to avoid potential infinite loops (A is based on A)
  303. if(C Elm *obj_elm=findElm(id))
  304. {
  305. if(objs.binaryHas(id, Compare))return true; // if this object is in the 'objs'
  306. if(C ElmObj *obj=obj_elm->objData())id=obj->base_id; // proceed to base
  307. }
  308. return false;
  309. }
  310. void Project::getExtendedObjs(C Memt<UID> &objs, Memt<UID> &exts)C // get list of all objects that are based on 'objs' (assumes that 'objs' is sorted)
  311. {
  312. REPA(elms){C Elm &elm=elms[i]; if(isBasedOnObjs(elm, objs))exts.binaryInclude(elm.id, Compare);}
  313. }
  314. bool Project::idToValid(C UID &id) // if target is valid (not removed)
  315. {
  316. if(id.valid())
  317. {
  318. if(Elm *elm=findElm(id))return elm->finalPublish();
  319. CacheLock cl(world_vers);
  320. REPA(world_vers) // check all worlds for objects/waypoints
  321. {
  322. WorldVer &world_ver=world_vers.lockedData(i);
  323. if(ObjVer * obj_ver=world_ver.obj .find(id))return !obj_ver->removed();
  324. //if( world_ver.waypoints.find(id))return ;
  325. }
  326. }
  327. return true;
  328. }
  329. Str Project::idToText(C UID &id, bool *valid, ID_MODE id_mode) // 'valid'=if target is valid (not removed)
  330. {
  331. if(valid)*valid=true;
  332. if(id.valid())
  333. {
  334. if(Elm *elm=findElm(id)) // check project element
  335. {
  336. if(valid && elm->finalNoPublish())*valid=false;
  337. return elmFullName(elm);
  338. }
  339. CacheLock cl(world_vers);
  340. REPA(world_vers) // check all worlds for objects/waypoints
  341. {
  342. WorldVer &world_ver=world_vers.lockedData(i);
  343. if(ObjVer * obj_ver=world_ver.obj .find(id)){if(valid && obj_ver->removed())*valid=false; return (S+"Object " +((id_mode==ID_SKIP_UNKNOWN) ? S : id.asCString())).space()+"in World \""+elmFullName(world_ver.world_id)+'"';}
  344. if( world_ver.waypoints.find(id)){ return (S+"Waypoint "+((id_mode==ID_SKIP_UNKNOWN) ? S : id.asCString())).space()+"in World \""+elmFullName(world_ver.world_id)+'"';}
  345. }
  346. }
  347. if(id_mode==ID_ALWAYS
  348. || id_mode==ID_SKIP_ZERO && id.valid())return id.asCString();
  349. return S;
  350. }
  351. bool Project::invalidSrc(C Str &src, Str *invalid)C // if specified and is not present
  352. {
  353. if(src.is())
  354. {
  355. Mems<Edit::FileParams> files=Edit::FileParams::Decode(src); REPA(files)
  356. {
  357. C Str &name=files[i].name; if(name.is())
  358. {
  359. UID id; if(DecodeFileName(name, id)) // project element
  360. {
  361. C Elm *elm=findElm(id); if(!elm){if(invalid)*invalid=name; return true;} // INVALID
  362. }else // source file
  363. {
  364. if(FileInfoSystem(name ).type!=FSTD_FILE
  365. //&& FileInfoSystem(name+".cmpr").type!=FSTD_FILE // check for compressed alternative
  366. )
  367. {
  368. if(invalid)*invalid=name; return true; // INVALID
  369. }
  370. }
  371. }
  372. }
  373. }
  374. if(invalid)invalid->clear(); return false; // OK
  375. }
  376. bool Project::invalidTexSrc(C Str &src, Str *invalid)C // if specified and is not present
  377. {
  378. if(src.is())
  379. {
  380. Mems<Edit::FileParams> files=Edit::FileParams::Decode(src); REPA(files)
  381. {
  382. C Str &name=files[i].name; if(name.is())
  383. {
  384. UID id;
  385. if(name[0]=='<') // another image
  386. {
  387. }else
  388. if(DecodeFileName(name, id)) // project element
  389. {
  390. C Elm *elm=findElm(id); if(!elm || !ElmImageLike(elm->type)){if(invalid)*invalid=name; return true;} // INVALID
  391. }else // source file
  392. {
  393. if(FileInfoSystem(name ).type!=FSTD_FILE
  394. //&& FileInfoSystem(name+".cmpr").type!=FSTD_FILE // check for compressed alternative
  395. )
  396. {
  397. if(invalid)*invalid=name; return true; // INVALID
  398. }
  399. }
  400. }
  401. }
  402. }
  403. if(invalid)invalid->clear(); return false; // OK
  404. }
  405. bool Project::invalidTex(C UID &tex_id )C {return tex_id.valid() && !texs.binaryHas(tex_id, Compare);}
  406. bool Project::invalidRef(C UID &elm_id, bool optional)C // is specified and is not present
  407. {
  408. if(!elm_id.valid())return false; // empty/null reference is valid
  409. if(C Elm *elm=findElm(elm_id))return optional ? elm->finalRemoved() : elm->finalNoPublish(); // if it's optional, then we need to check if it exists, if required then we need to check if exists and is publishable
  410. return true; // element was not found = invalid
  411. }
  412. bool Project::invalidRefs(Elm &elm) // check if this element has invalid references
  413. {
  414. switch(elm.type)
  415. {
  416. case ELM_OBJ: if(ElmObj *data=elm.objData())
  417. {
  418. if(Elm *mesh_elm=findElm(data->mesh_id))if(invalidRefs(*mesh_elm))return true; // process mesh because it's hidden
  419. return invalidRef(data->mesh_id) || invalidRef(data->base_id);
  420. }break;
  421. case ELM_MESH: if(ElmMesh *data=elm.meshData())
  422. {
  423. //if(Elm * obj_elm=findElm(data. obj_id))if(invalidRefs(* obj_elm))return true; here we don't do this because 'obj' is a parent and is visible
  424. if(Elm *skel_elm=findElm(data->skel_id))if(invalidRefs(*skel_elm))return true; // process skel because it's hidden
  425. if(Elm *phys_elm=findElm(data->phys_id))if(invalidRefs(*phys_elm))return true; // process phys because it's hidden
  426. REPA(data->mtrl_ids)if(invalidRef(data->mtrl_ids[i]))return true;
  427. return invalidRef(data->obj_id) || invalidRef(data->skel_id) || invalidRef(data->body_id) || invalidRef(data->phys_id) || invalidRef(data->draw_group_id);
  428. }break;
  429. case ELM_SKEL: if(ElmSkel *data=elm.skelData())
  430. {
  431. return invalidRef(data->mesh_id);
  432. }break;
  433. case ELM_PHYS: if(ElmPhys *data=elm.physData())
  434. {
  435. return invalidRef(data->mesh_id) || invalidRef(data->mtrl_id);
  436. }break;
  437. case ELM_MTRL: if(ElmMaterial *data=elm.mtrlData())
  438. {
  439. return invalidTex(data->base_0_tex) || invalidTex(data->base_1_tex) || invalidTex(data->detail_tex) || invalidTex(data->macro_tex) || invalidTex(data->reflection_tex) || invalidTex(data->light_tex);
  440. }break;
  441. case ELM_WATER_MTRL: if(ElmWaterMtrl *data=elm.waterMtrlData())
  442. {
  443. return invalidTex(data->base_0_tex) || invalidTex(data->base_1_tex) || invalidTex(data->reflection_tex);
  444. }break;
  445. case ELM_ANIM: if(ElmAnim *data=elm.animData())
  446. {
  447. return invalidRef(data->skel_id);
  448. }break;
  449. case ELM_IMAGE_ATLAS: if(ElmImageAtlas *data=elm.imageAtlasData())
  450. {
  451. REPA(data->images)if(!data->images[i].removed && invalidRef(data->images[i].id, true))return true;
  452. }break;
  453. case ELM_ICON: if(ElmIcon *data=elm.iconData())
  454. {
  455. return invalidRef(data->icon_settings_id, true) || invalidRef(data->obj_id, true);
  456. }break;
  457. case ELM_TEXT_STYLE: if(ElmTextStyle *data=elm.textStyleData())
  458. {
  459. return invalidRef(data->font_id);
  460. }break;
  461. case ELM_PANEL_IMAGE: if(ElmPanelImage *data=elm.panelImageData())
  462. {
  463. REPA(data->image_ids)if(invalidRef(data->image_ids[i], true))return true;
  464. }break;
  465. case ELM_PANEL: if(ElmPanel *data=elm.panelData())
  466. {
  467. REPA(data->image_ids)if(invalidRef(data->image_ids[i]))return true;
  468. }break;
  469. case ELM_GUI_SKIN: if(ElmGuiSkin *data=elm.guiSkinData())
  470. {
  471. REPA(data->elm_ids)if(invalidRef(data->elm_ids[i]))return true;
  472. }break;
  473. case ELM_ENV: if(ElmEnv *data=elm.envData())
  474. {
  475. REPA(data->cloud_id)if(invalidRef(data->cloud_id[i]))return true;
  476. return invalidRef(data->sun_id) || invalidRef(data->skybox_id) || invalidRef(data->star_id);
  477. }break;
  478. case ELM_WORLD: if(ElmWorld *data=elm.worldData())
  479. {
  480. return invalidRef(data->env_id);
  481. }break;
  482. case ELM_MINI_MAP: if(ElmMiniMap *data=elm.miniMapData())
  483. {
  484. return invalidRef(data->world_id, true) || invalidRef(data->env_id, true);
  485. }break;
  486. case ELM_APP: if(ElmApp *data=elm.appData())
  487. {
  488. return invalidRef(data->icon, true) || invalidRef(data->image_portrait, true) || invalidRef(data->image_landscape, true);
  489. }break;
  490. }
  491. return false;
  492. }
  493. Elm& Project::getFolder(C Str &name, C UID &parent_id, bool &added, bool ignore_removed)
  494. {
  495. // can't use hierarchy because this func is used when new elements are added
  496. added=false; REPA(elms)if(elms[i].parent_id==parent_id && elms[i].name==name && (ignore_removed ? !elms[i].removed() : true))return elms[i];
  497. added=true ; return newElm(name, parent_id, ELM_FOLDER);
  498. }
  499. UID Project::getPathID(C Str &path, C UID &parent_id, bool ignore_removed) // create all project element folders from 'path' and return last element's id
  500. {
  501. UID parent=parent_id;
  502. for(Str p=path; p.is(); )
  503. {
  504. Str start=GetStart (p);
  505. p=GetStartNot(p);
  506. bool added; parent=getFolder(start, parent, added, ignore_removed).id;
  507. }
  508. return parent;
  509. }
  510. bool Project::getWorldPaths(C UID &world_id, Str &world_edit_path, Str &world_game_path)
  511. {
  512. if(world_id.valid())
  513. {
  514. world_edit_path=T.edit_path+EncodeFileName(world_id).tailSlash(true);
  515. world_game_path=T.game_path+EncodeFileName(world_id).tailSlash(true);
  516. return true;
  517. }else
  518. {
  519. world_edit_path.clear();
  520. world_game_path.clear();
  521. return false;
  522. }
  523. }
  524. void Project::createWorldPaths(C UID &world_id)
  525. {
  526. if(world_id.valid() && world_paths.binaryInclude(world_id, Compare)) // create paths only at first time
  527. {
  528. Str edit, game; if(getWorldPaths(world_id, edit, game))
  529. {
  530. FCreateDirs(edit+"Area");
  531. FCreateDirs(game+"Area");
  532. FCreateDirs(edit+"Waypoint");
  533. FCreateDirs(game+"Waypoint");
  534. FCreateDirs(edit+"Lake");
  535. FCreateDirs(edit+"River");
  536. }
  537. }
  538. }
  539. void Project::createMiniMapPaths(C UID &mini_map_id)
  540. {
  541. if(mini_map_id.valid() && mini_map_paths.binaryInclude(mini_map_id, Compare)) // create paths only at first time
  542. FCreateDirs(gamePath(mini_map_id));
  543. }
  544. int Project::ChannelIndex(char c)
  545. {
  546. switch(c)
  547. {
  548. case 'r': case 'R': case 'x': case 'X': return 0;
  549. case 'g': case 'G': case 'y': case 'Y': return 1;
  550. case 'b': case 'B': case 'z': case 'Z': return 2;
  551. case 'a': case 'A': case 'w': case 'W': return 3;
  552. }
  553. return -1;
  554. }
  555. IMAGE_TYPE Project::HighPrecType(IMAGE_TYPE type)
  556. {
  557. switch(type)
  558. {
  559. case IMAGE_R8 : case IMAGE_I8: case IMAGE_I16: case IMAGE_I24: case IMAGE_I32: return IMAGE_F32;
  560. case IMAGE_R8G8 : return IMAGE_F32_2;
  561. case IMAGE_R8G8B8: return IMAGE_F32_3;
  562. default : return IMAGE_F32_4;
  563. }
  564. }
  565. void Project::MakeHighPrec(Image &image)
  566. {
  567. if(!image.highPrecision())image.copyTry(image, -1, -1, -1, HighPrecType(image.type()));
  568. }
  569. void Project::ContrastLum(Image &image, flt contrast, flt avg_lum, C BoxI &box)
  570. {
  571. if(contrast!=1 && image.lock())
  572. {
  573. for(int z=box.min.z; z<box.max.z; z++)
  574. for(int y=box.min.y; y<box.max.y; y++)
  575. for(int x=box.min.x; x<box.max.x; x++)
  576. {
  577. Vec4 c=image.color3DF(x, y, z);
  578. flt c_lum=c.xyz.max(), want_lum=(c_lum-avg_lum)*contrast+avg_lum;
  579. if(c_lum>EPS)c.xyz*=want_lum/c_lum;else c.xyz=want_lum;
  580. image.color3DF(x, y, z, c);
  581. }
  582. image.unlock();
  583. }
  584. }
  585. void Project::AvgContrastLum(Image &image, flt contrast, dbl avg_lum, C BoxI &box)
  586. {
  587. if(avg_lum && image.lock()) // lock for writing because we will use this lock for applying contrast too
  588. {
  589. dbl contrast_total=0, weight_total=0;
  590. for(int z=box.min.z; z<box.max.z; z++)
  591. for(int y=box.min.y; y<box.max.y; y++)
  592. for(int x=box.min.x; x<box.max.x; x++)
  593. {
  594. Vec4 c=image.color3DF(x, y, z); dbl c_lum=c.xyz.max();
  595. if(dbl d=c_lum-avg_lum)
  596. {
  597. dbl contrast=Abs(d)/avg_lum, // div by 'avg_lum' so for bright values contrast will be proportionally the same
  598. weight=Sqr(d); // squared distance from avg_lum
  599. contrast_total+=weight*contrast;
  600. weight_total+=weight;
  601. }
  602. }
  603. if(weight_total)if(contrast_total/=weight_total)ContrastLum(image, contrast/contrast_total, avg_lum, box);
  604. image.unlock();
  605. }
  606. }
  607. void Project::AddHue(Image &image, flt hue, C BoxI &box)
  608. {
  609. hue=Frac(hue);
  610. if(hue && image.lock())
  611. {
  612. for(int z=box.min.z; z<box.max.z; z++)
  613. for(int y=box.min.y; y<box.max.y; y++)
  614. for(int x=box.min.x; x<box.max.x; x++)
  615. {
  616. Vec4 c=image.color3DF(x, y, z);
  617. c.xyz=RgbToHsb(c.xyz);
  618. c.x +=hue;
  619. c.xyz=HsbToRgb(c.xyz);
  620. image.color3DF(x, y, z, c);
  621. }
  622. image.unlock();
  623. }
  624. }
  625. void Project::MulRGBH(Image &image, flt red, flt yellow, flt green, flt cyan, flt blue, flt purple, C BoxI &box)
  626. {
  627. flt mul[]={red, yellow, green, cyan, blue, purple, red, yellow}; // red and yellow are listed extra for wraparound
  628. REP(6)if(mul[i]!=1)goto mul; return; mul:
  629. for(int z=box.min.z; z<box.max.z; z++)
  630. for(int y=box.min.y; y<box.max.y; y++)
  631. for(int x=box.min.x; x<box.max.x; x++)
  632. {
  633. Vec4 c=image.color3DF(x, y, z);
  634. Vec hsb=RgbToHsb(c.xyz);
  635. flt hue=hsb.x*6; int hue_i=Trunc(hue); flt hue_frac=hue-hue_i;
  636. flt hue_mul=Lerp(mul[hue_i], mul[hue_i+1], hue_frac);
  637. c.xyz*=hue_mul;
  638. image.color3DF(x, y, z, c);
  639. }
  640. }
  641. void Project::MulRGBHS(Image &image, flt red, flt yellow, flt green, flt cyan, flt blue, flt purple, C BoxI &box)
  642. {
  643. flt mul[]={red, yellow, green, cyan, blue, purple, red, yellow}; // red and yellow are listed extra for wraparound
  644. REP(6)if(mul[i]!=1)goto mul; return; mul:
  645. for(int z=box.min.z; z<box.max.z; z++)
  646. for(int y=box.min.y; y<box.max.y; y++)
  647. for(int x=box.min.x; x<box.max.x; x++)
  648. {
  649. Vec4 c=image.color3DF(x, y, z);
  650. Vec hsb=RgbToHsb(c.xyz);
  651. flt hue=hsb.x*6; int hue_i=Trunc(hue); flt hue_frac=hue-hue_i;
  652. flt hue_mul=Lerp(mul[hue_i], mul[hue_i+1], hue_frac);
  653. c.xyz*=Lerp(1.0f, hue_mul, hsb.y);
  654. image.color3DF(x, y, z, c);
  655. }
  656. }
  657. void Project::MulSatH(Image &image, flt red, flt yellow, flt green, flt cyan, flt blue, flt purple, bool sat, bool photo, C BoxI &box)
  658. {
  659. flt mul[]={red, yellow, green, cyan, blue, purple, red, yellow}; // red and yellow are listed extra for wraparound
  660. REP(6)if(mul[i]!=1)goto mul; return; mul:
  661. for(int z=box.min.z; z<box.max.z; z++)
  662. for(int y=box.min.y; y<box.max.y; y++)
  663. for(int x=box.min.x; x<box.max.x; x++)
  664. {
  665. Vec4 c=image.color3DF(x, y, z);
  666. flt lin_lum; if(photo)lin_lum=LinearLumOfSRGBColor(c.xyz);
  667. Vec hsb=RgbToHsb(c.xyz);
  668. flt hue=hsb.x*6; int hue_i=Trunc(hue); flt hue_frac=hue-hue_i;
  669. flt sat_mul=Lerp(mul[hue_i], mul[hue_i+1], hue_frac);
  670. if(sat)sat_mul=Lerp(1.0f, sat_mul, hsb.y);
  671. hsb.y*=sat_mul;
  672. c.xyz=HsbToRgb(hsb);
  673. if(photo)
  674. {
  675. c.xyz=SRGBToLinear(c.xyz);
  676. if(flt cur_lin_lum=LinearLumOfLinearColor(c.xyz))c.xyz*=lin_lum/cur_lin_lum;
  677. c.xyz=LinearToSRGB(c.xyz);
  678. }
  679. image.color3DF(x, y, z, c);
  680. }
  681. }
  682. flt Project::HueDelta(flt a, flt b) // returns -0.5 .. 0.5
  683. {
  684. flt d=Frac(b-a); if(d>0.5f)d-=1; return d;
  685. }
  686. Vec2 Project::LerpToMad(flt from, flt to) {return Vec2(to-from, from);}
  687. Vec2 Project::ILerpToMad(flt from, flt to) {return Vec2(1/(to-from), from/(from-to));}
  688. flt Project::FloatSelf(flt x) {return x;}
  689. flt Project::PowMax(flt x, flt y) {return (x<=0) ? 0 : Pow(x, y);}
  690. void Project::IncludeAlpha(Image &image) {image.copyTry(image, -1, -1, -1, ImageTypeIncludeAlpha(image.type()));}
  691. void Project::TransformImage(Image &image, C MemPtr<TextParam> &params, bool clamp)
  692. {
  693. int high_prec=0; REPA(params)high_prec+=HighPrecTransform(params[i].name); // how many transforms are high precision
  694. if( high_prec>1)MakeHighPrec(image);
  695. FREPA(params)
  696. {
  697. TextParam param=params[i];
  698. BoxI box(0, image.size3());
  699. int at_pos=TextPosI(param.value, '@'); if(at_pos>=0)
  700. {
  701. VecI4 v=TextVecI4(param.value()+at_pos+1);
  702. RectI r(v.xy, v.xy+v.zw);
  703. box&=BoxI(VecI(r.min, 0), VecI(r.max, box.max.z));
  704. param.value.clip(at_pos);
  705. }
  706. if(param.name=="crop")
  707. {
  708. VecI4 v=TextVecI4(param.value);
  709. image.crop(image, v.x, v.y, v.z, v.w);
  710. }else
  711. if(param.name=="resize" || param.name=="resizeWrap" || param.name=="resizeClamp" || param.name=="resizeLinear" || param.name=="resizeCubic" || param.name=="resizeNoStretch")
  712. {
  713. VecI2 s;
  714. if(param.value=="quarter")s.set(Max(1, image.w()/4), Max(1, image.h()/4));else
  715. if(param.value=="half" )s.set(Max(1, image.w()/2), Max(1, image.h()/2));else
  716. if(param.value=="double" )s=image.size()*2;else
  717. {
  718. Vec2 sf; if(Contains(param.value, ','))sf=param.asVec2();else sf=param.asFlt();
  719. UNIT_TYPE unit=GetUnitType(param.value);
  720. s.x=Round(ConvertUnitType(sf.x, image.w(), unit));
  721. s.y=Round(ConvertUnitType(sf.y, image.h(), unit));
  722. }
  723. s=ImageSize(image.size3(), s, false).xy;
  724. image.resize(s.x, s.y, (param.name=="resizeNoStretch") ? FILTER_NO_STRETCH : (param.name=="resizeLinear") ? FILTER_LINEAR : (param.name=="resizeCubic") ? FILTER_CUBIC_FAST : FILTER_BEST, (param.name=="resizeClamp") ? true : (param.name=="resizeWrap" || param.name=="resizeNoStretch") ? false : clamp);
  725. }else
  726. if(param.name=="maxSize")
  727. {
  728. VecI2 s;
  729. if(param.value=="quarter")s.set(Max(1, image.w()/4), Max(1, image.h()/4));else
  730. if(param.value=="half" )s.set(Max(1, image.w()/2), Max(1, image.h()/2));else
  731. if(param.value=="double" )s=image.size()*2;else
  732. {
  733. Vec2 sf; if(Contains(param.value, ','))sf=param.asVec2();else sf=param.asFlt();
  734. UNIT_TYPE unit=GetUnitType(param.value);
  735. s.x=Round(ConvertUnitType(sf.x, image.w(), unit));
  736. s.y=Round(ConvertUnitType(sf.y, image.h(), unit));
  737. }
  738. s=ImageSize(image.size3(), s, false).xy;
  739. image.resize(Min(image.w(), s.x), Min(image.h(), s.y), FILTER_BEST, clamp);
  740. }else
  741. if(param.name=="tile")image.tile(param.asInt());else
  742. if(param.name=="inverseRGB")
  743. {
  744. if(image.highPrecision())
  745. {
  746. for(int z=box.min.z; z<box.max.z; z++)
  747. for(int y=box.min.y; y<box.max.y; y++)
  748. for(int x=box.min.x; x<box.max.x; x++){Vec4 c=image.color3DF(x, y, z); c.xyz=1-c.xyz; image.color3DF(x, y, z, c);}
  749. }else
  750. {
  751. for(int z=box.min.z; z<box.max.z; z++)
  752. for(int y=box.min.y; y<box.max.y; y++)
  753. for(int x=box.min.x; x<box.max.x; x++){Color c=image.color3D(x, y, z); c.r=255-c.r; c.g=255-c.g; c.b=255-c.b; image.color3D(x, y, z, c);}
  754. }
  755. }else
  756. if(param.name=="inverseR")
  757. {
  758. if(image.highPrecision())
  759. {
  760. for(int z=box.min.z; z<box.max.z; z++)
  761. for(int y=box.min.y; y<box.max.y; y++)
  762. for(int x=box.min.x; x<box.max.x; x++){Vec4 c=image.color3DF(x, y, z); c.x=1-c.x; image.color3DF(x, y, z, c);}
  763. }else
  764. {
  765. for(int z=box.min.z; z<box.max.z; z++)
  766. for(int y=box.min.y; y<box.max.y; y++)
  767. for(int x=box.min.x; x<box.max.x; x++){Color c=image.color3D(x, y, z); c.r=255-c.r; image.color3D(x, y, z, c);}
  768. }
  769. }else
  770. if(param.name=="inverseG")
  771. {
  772. if(image.highPrecision())
  773. {
  774. for(int z=box.min.z; z<box.max.z; z++)
  775. for(int y=box.min.y; y<box.max.y; y++)
  776. for(int x=box.min.x; x<box.max.x; x++){Vec4 c=image.color3DF(x, y, z); c.y=1-c.y; image.color3DF(x, y, z, c);}
  777. }else
  778. {
  779. for(int z=box.min.z; z<box.max.z; z++)
  780. for(int y=box.min.y; y<box.max.y; y++)
  781. for(int x=box.min.x; x<box.max.x; x++){Color c=image.color3D(x, y, z); c.g=255-c.g; image.color3D(x, y, z, c);}
  782. }
  783. }else
  784. if(param.name=="inverseRG")
  785. {
  786. if(image.highPrecision())
  787. {
  788. for(int z=box.min.z; z<box.max.z; z++)
  789. for(int y=box.min.y; y<box.max.y; y++)
  790. for(int x=box.min.x; x<box.max.x; x++){Vec4 c=image.color3DF(x, y, z); c.x=1-c.x; c.y=1-c.y; image.color3DF(x, y, z, c);}
  791. }else
  792. {
  793. for(int z=box.min.z; z<box.max.z; z++)
  794. for(int y=box.min.y; y<box.max.y; y++)
  795. for(int x=box.min.x; x<box.max.x; x++){Color c=image.color3D(x, y, z); c.r=255-c.r; c.g=255-c.g; image.color3D(x, y, z, c);}
  796. }
  797. }else
  798. if(param.name=="swapRG")
  799. {
  800. if(image.highPrecision())
  801. {
  802. for(int z=box.min.z; z<box.max.z; z++)
  803. for(int y=box.min.y; y<box.max.y; y++)
  804. for(int x=box.min.x; x<box.max.x; x++){Vec4 c=image.color3DF(x, y, z); Swap(c.x, c.y); image.color3DF(x, y, z, c);}
  805. }else
  806. {
  807. for(int z=box.min.z; z<box.max.z; z++)
  808. for(int y=box.min.y; y<box.max.y; y++)
  809. for(int x=box.min.x; x<box.max.x; x++){Color c=image.color3D(x, y, z); Swap(c.r, c.g); image.color3D(x, y, z, c);}
  810. }
  811. }else
  812. if(param.name=="swapXY")
  813. {
  814. Image temp; temp.createSoftTry(image.h(), image.w(), image.d(), image.type());
  815. if(temp.highPrecision())
  816. {
  817. REPD(y, image.h())
  818. REPD(x, image.w())temp.colorF(y, x, image.colorF(x, y));
  819. }else
  820. {
  821. REPD(y, image.h())
  822. REPD(x, image.w())temp.color(y, x, image.color(x, y));
  823. }
  824. Swap(temp, image);
  825. }else
  826. if(param.name=="mirrorX")image.mirrorX();else
  827. if(param.name=="mirrorY")image.mirrorY();else
  828. if(param.name=="normalize")image.normalize(true, true, true, true, &box);else
  829. if(param.name=="sat")
  830. {
  831. for(int z=box.min.z; z<box.max.z; z++)
  832. for(int y=box.min.y; y<box.max.y; y++)
  833. for(int x=box.min.x; x<box.max.x; x++)
  834. {
  835. Vec4 c=image.color3DF(x, y, z);
  836. c.sat();
  837. image.color3DF(x, y, z, c);
  838. }
  839. }else
  840. if(param.name=="blur")
  841. {
  842. UNIT_TYPE unit=GetUnitType(param.value);
  843. Vec r =TextVecEx (param.value);
  844. r.x=ConvertUnitType(r.x, image.w(), unit);
  845. r.y=ConvertUnitType(r.y, image.h(), unit);
  846. r.z=ConvertUnitType(r.z, image.d(), unit);
  847. image.blur(r, clamp, &WorkerThreads);
  848. }else
  849. if(param.name=="lerpRGB")
  850. {
  851. Memc<Str> c; Split(c, param.value, ',');
  852. switch(c.elms())
  853. {
  854. case 2: {Vec2 ma=LerpToMad(TextFlt(c[0]), TextFlt(c[1])); image.mulAdd(Vec4(Vec(ma.x), 1), Vec4(Vec(ma.y), 0), &box);} break;
  855. case 6: {Vec2 ma[3]={LerpToMad(TextFlt(c[0]), TextFlt(c[3])), LerpToMad(TextFlt(c[1]), TextFlt(c[4])), LerpToMad(TextFlt(c[2]), TextFlt(c[5]))}; image.mulAdd(Vec4(ma[0].x, ma[1].x, ma[2].x, 1), Vec4(ma[0].y, ma[1].y, ma[2].y, 0), &box);} break;
  856. }
  857. }else
  858. if(param.name=="ilerpRGB")
  859. {
  860. Memc<Str> c; Split(c, param.value, ',');
  861. switch(c.elms())
  862. {
  863. case 2: {Vec2 ma=ILerpToMad(TextFlt(c[0]), TextFlt(c[1])); image.mulAdd(Vec4(Vec(ma.x), 1), Vec4(Vec(ma.y), 0), &box);} break;
  864. case 6: {Vec2 ma[3]={ILerpToMad(TextFlt(c[0]), TextFlt(c[3])), ILerpToMad(TextFlt(c[1]), TextFlt(c[4])), ILerpToMad(TextFlt(c[2]), TextFlt(c[5]))}; image.mulAdd(Vec4(ma[0].x, ma[1].x, ma[2].x, 1), Vec4(ma[0].y, ma[1].y, ma[2].y, 0), &box);} break;
  865. }
  866. }else
  867. if(param.name=="mulA" ){flt alpha=param.asFlt(); if(alpha!=1){IncludeAlpha(image); image.mulAdd(Vec4(1, 1, 1, alpha), 0, &box);}}else
  868. if(param.name=="mulRGB")image.mulAdd(Vec4(TextVecEx(param.value), 1), 0, &box);else
  869. if(param.name=="addRGB")image.mulAdd(1, Vec4(TextVecEx(param.value), 0), &box);else
  870. if(param.name=="mulAddRGB")
  871. {
  872. Memc<Str> c; Split(c, param.value, ',');
  873. switch(c.elms())
  874. {
  875. case 2: image.mulAdd(Vec4(Vec(TextFlt(c[0])), 1), Vec4(Vec(TextFlt(c[1])), 0), &box); break;
  876. case 6: image.mulAdd(Vec4(TextFlt(c[0]), TextFlt(c[1]), TextFlt(c[2]), 1), Vec4(TextFlt(c[3]), TextFlt(c[4]), TextFlt(c[5]), 0), &box); break;
  877. }
  878. }else
  879. if(param.name=="addMulRGB")
  880. {
  881. Memc<Str> c; Split(c, param.value, ',');
  882. switch(c.elms())
  883. {
  884. // x=x*m+a, x=(x+A)*M
  885. case 2: {flt add=TextFlt(c[0]), mul=TextFlt(c[1]); image.mulAdd(Vec4(Vec(mul), 1), Vec4(Vec(add*mul), 0), &box);} break;
  886. case 6: {Vec add(TextFlt(c[0]), TextFlt(c[1]), TextFlt(c[2])), mul(TextFlt(c[3]), TextFlt(c[4]), TextFlt(c[5])); image.mulAdd(Vec4( mul , 1), Vec4( add*mul , 0), &box);} break;
  887. }
  888. }else
  889. if(param.name=="mulRGBS")
  890. {
  891. Vec mul=TextVecEx(param.value);
  892. if(mul!=VecOne)
  893. for(int z=box.min.z; z<box.max.z; z++)
  894. for(int y=box.min.y; y<box.max.y; y++)
  895. for(int x=box.min.x; x<box.max.x; x++)
  896. {
  897. Vec4 c=image.color3DF(x, y, z);
  898. flt sat=RgbToHsb(c.xyz).y;
  899. c.x=Lerp(c.x, c.x*mul.x, sat); // red
  900. c.y=Lerp(c.y, c.y*mul.y, sat); // green
  901. c.z=Lerp(c.z, c.z*mul.z, sat); // blue
  902. image.color3DF(x, y, z, c);
  903. }
  904. }else
  905. if(param.name=="mulRGBH")
  906. {
  907. Mems<Str> vals; Split(vals, param.value, ',');
  908. switch(vals.elms())
  909. {
  910. case 1: {flt v=TextFlt(vals[0]); MulRGBH(image, v, v, v, v, v, v, box);} break;
  911. case 3: MulRGBH(image, TextFlt(vals[0]), 1, TextFlt(vals[1]), 1, TextFlt(vals[2]), 1, box); break;
  912. case 6: MulRGBH(image, TextFlt(vals[0]), TextFlt(vals[1]), TextFlt(vals[2]), TextFlt(vals[3]), TextFlt(vals[4]), TextFlt(vals[5]), box); break;
  913. }
  914. }else
  915. if(param.name=="mulRGBHS")
  916. {
  917. Mems<Str> vals; Split(vals, param.value, ',');
  918. switch(vals.elms())
  919. {
  920. case 1: {flt v=TextFlt(vals[0]); MulRGBHS(image, v, v, v, v, v, v, box);} break;
  921. case 3: MulRGBHS(image, TextFlt(vals[0]), 1, TextFlt(vals[1]), 1, TextFlt(vals[2]), 1, box); break;
  922. case 6: MulRGBHS(image, TextFlt(vals[0]), TextFlt(vals[1]), TextFlt(vals[2]), TextFlt(vals[3]), TextFlt(vals[4]), TextFlt(vals[5]), box); break;
  923. }
  924. }else
  925. if(param.name=="gamma")
  926. {
  927. Vec g=TextVecEx(param.value);
  928. if(g!=VecOne)
  929. for(int z=box.min.z; z<box.max.z; z++)
  930. for(int y=box.min.y; y<box.max.y; y++)
  931. for(int x=box.min.x; x<box.max.x; x++){Vec4 c=image.color3DF(x, y, z); c.xyz.set(PowMax(c.x, g.x), PowMax(c.y, g.y), PowMax(c.z, g.z)); image.color3DF(x, y, z, c);}
  932. }else
  933. if(param.name=="gammaLum")
  934. {
  935. flt g=param.asFlt();
  936. if(g!=1)
  937. for(int z=box.min.z; z<box.max.z; z++)
  938. for(int y=box.min.y; y<box.max.y; y++)
  939. for(int x=box.min.x; x<box.max.x; x++){Vec4 c=image.color3DF(x, y, z); if(flt lum=c.xyz.max()){c.xyz*=PowMax(lum, g)/lum; image.color3DF(x, y, z, c);}}
  940. }else
  941. if(param.name=="brightness")
  942. {
  943. Vec b=TextVecEx(param.value), mul; if(b.any())
  944. {
  945. flt (*R)(flt);
  946. flt (*G)(flt);
  947. flt (*B)(flt);
  948. if(!b.x){b.x=1; mul.x=1; R=FloatSelf;}else if(b.x<0){b.x=SigmoidSqrt(b.x); mul.x=1/SigmoidSqrtInv(b.x); R=SigmoidSqrtInv;}else{mul.x=1/SigmoidSqrt(b.x); R=SigmoidSqrt;}
  949. if(!b.y){b.y=1; mul.y=1; G=FloatSelf;}else if(b.y<0){b.y=SigmoidSqrt(b.y); mul.y=1/SigmoidSqrtInv(b.y); G=SigmoidSqrtInv;}else{mul.y=1/SigmoidSqrt(b.y); G=SigmoidSqrt;}
  950. if(!b.z){b.z=1; mul.z=1; B=FloatSelf;}else if(b.z<0){b.z=SigmoidSqrt(b.z); mul.z=1/SigmoidSqrtInv(b.z); B=SigmoidSqrtInv;}else{mul.z=1/SigmoidSqrt(b.z); B=SigmoidSqrt;}
  951. for(int z=box.min.z; z<box.max.z; z++)
  952. for(int y=box.min.y; y<box.max.y; y++)
  953. for(int x=box.min.x; x<box.max.x; x++)
  954. {
  955. Vec4 c=image.color3DF(x, y, z);
  956. c.xyz=Sqr(c.xyz);
  957. c.x=R(c.x*b.x)*mul.x;
  958. c.y=G(c.y*b.y)*mul.y;
  959. c.z=B(c.z*b.z)*mul.z;
  960. c.xyz=Sqrt(c.xyz);
  961. image.color3DF(x, y, z, c);
  962. }
  963. }
  964. }else
  965. if(param.name=="brightnessLum")
  966. {
  967. flt b=param.asFlt(), mul; flt (*f)(flt);
  968. if(b)
  969. {
  970. if(b<0){b=SigmoidSqrt(b); mul=1/SigmoidSqrtInv(b); f=SigmoidSqrtInv;}else{mul=1/SigmoidSqrt(b); f=SigmoidSqrt;}
  971. for(int z=box.min.z; z<box.max.z; z++)
  972. for(int y=box.min.y; y<box.max.y; y++)
  973. for(int x=box.min.x; x<box.max.x; x++)
  974. {
  975. Vec4 c=image.color3DF(x, y, z);
  976. if(flt l=c.xyz.max())
  977. {
  978. flt new_lum=Sqr(l);
  979. new_lum=f(new_lum*b)*mul;
  980. new_lum=Sqrt(new_lum);
  981. c.xyz*=new_lum/l;
  982. image.color3DF(x, y, z, c);
  983. }
  984. }
  985. }
  986. }else
  987. if(param.name=="contrast")
  988. {
  989. Vec contrast=TextVecEx(param.value); if(contrast!=VecOne)
  990. {
  991. Vec4 avg; if(image.stats(null, null, &avg, null, null, null, &box))
  992. {
  993. // col=(col-avg)*contrast+avg
  994. // col=col*contrast+avg*(1-contrast)
  995. image.mulAdd(Vec4(contrast, 1), Vec4(avg.xyz*(Vec(1)-contrast), 0), &box);
  996. }
  997. }
  998. }else
  999. if(param.name=="contrastAlphaWeight")
  1000. {
  1001. Vec contrast=TextVecEx(param.value); if(contrast!=VecOne)
  1002. {
  1003. Vec avg; if(image.stats(null, null, null, null, null, &avg, &box))
  1004. {
  1005. image.mulAdd(Vec4(contrast, 1), Vec4(avg*(Vec(1)-contrast), 0), &box);
  1006. }
  1007. }
  1008. }else
  1009. if(param.name=="contrastLum")
  1010. {
  1011. flt contrast=param.asFlt(); if(contrast!=1)
  1012. {
  1013. Vec4 avg; if(image.stats(null, null, &avg, null, null, null, &box))ContrastLum(image, contrast, avg.xyz.max(), box);
  1014. }
  1015. }else
  1016. if(param.name=="contrastLumAlphaWeight")
  1017. {
  1018. flt contrast=param.asFlt(); if(contrast!=1)
  1019. {
  1020. Vec avg; if(image.stats(null, null, null, null, null, &avg, &box))ContrastLum(image, contrast, avg.max(), box);
  1021. }
  1022. }else
  1023. if(param.name=="contrastHue")
  1024. {
  1025. flt contrast=param.asFlt(); if(contrast!=1)
  1026. {
  1027. Vec4 avg; if(image.stats(null, null, &avg, null, null, null, &box) && image.lock())
  1028. {
  1029. flt avg_hue=RgbToHsb(avg.xyz).x;
  1030. for(int z=box.min.z; z<box.max.z; z++)
  1031. for(int y=box.min.y; y<box.max.y; y++)
  1032. for(int x=box.min.x; x<box.max.x; x++)
  1033. {
  1034. Vec4 c=image.color3DF(x, y, z);
  1035. c.xyz=RgbToHsb(c.xyz);
  1036. flt d_hue=HueDelta(avg_hue, c.x);
  1037. d_hue*=contrast;
  1038. Clamp(d_hue, -0.5f, 0.5f); // clamp so we don't go back
  1039. c.x=d_hue+avg_hue;
  1040. c.xyz=HsbToRgb(c.xyz);
  1041. image.color3DF(x, y, z, c);
  1042. }
  1043. image.unlock();
  1044. }
  1045. }
  1046. }else
  1047. if(param.name=="contrastHueAlphaWeight")
  1048. {
  1049. flt contrast=param.asFlt(); if(contrast!=1)
  1050. {
  1051. Vec avg; if(image.stats(null, null, null, null, null, &avg, &box) && image.lock())
  1052. {
  1053. flt avg_hue=RgbToHsb(avg).x;
  1054. for(int z=box.min.z; z<box.max.z; z++)
  1055. for(int y=box.min.y; y<box.max.y; y++)
  1056. for(int x=box.min.x; x<box.max.x; x++)
  1057. {
  1058. Vec4 c=image.color3DF(x, y, z);
  1059. c.xyz=RgbToHsb(c.xyz);
  1060. flt d_hue=HueDelta(avg_hue, c.x);
  1061. d_hue*=contrast;
  1062. Clamp(d_hue, -0.5f, 0.5f); // clamp so we don't go back
  1063. c.x=d_hue+avg_hue;
  1064. c.xyz=HsbToRgb(c.xyz);
  1065. image.color3DF(x, y, z, c);
  1066. }
  1067. image.unlock();
  1068. }
  1069. }
  1070. }else
  1071. if(param.name=="contrastHuePow")
  1072. {
  1073. flt contrast=param.asFlt(); if(contrast!=1)
  1074. {
  1075. Vec4 avg; if(image.stats(null, null, &avg, null, null, null, &box) && image.lock())
  1076. {
  1077. flt avg_hue=RgbToHsb(avg.xyz).x;
  1078. for(int z=box.min.z; z<box.max.z; z++)
  1079. for(int y=box.min.y; y<box.max.y; y++)
  1080. for(int x=box.min.x; x<box.max.x; x++)
  1081. {
  1082. Vec4 c=image.color3DF(x, y, z);
  1083. c.xyz=RgbToHsb(c.xyz);
  1084. flt d_hue=HueDelta(avg_hue, c.x);
  1085. d_hue=Sign(d_hue)*Pow(Abs(d_hue)*2, contrast)/2; // *2 to get -1..1 range
  1086. Clamp(d_hue, -0.5f, 0.5f); // clamp so we don't go back
  1087. c.x=d_hue+avg_hue;
  1088. c.xyz=HsbToRgb(c.xyz);
  1089. image.color3DF(x, y, z, c);
  1090. }
  1091. image.unlock();
  1092. }
  1093. }
  1094. }else
  1095. if(param.name=="contrastSat")
  1096. {
  1097. flt contrast=param.asFlt(); if(contrast!=1)
  1098. {
  1099. flt avg; if(image.statsSat(null, null, &avg, null, null, null, &box) && image.lock())
  1100. {
  1101. for(int z=box.min.z; z<box.max.z; z++)
  1102. for(int y=box.min.y; y<box.max.y; y++)
  1103. for(int x=box.min.x; x<box.max.x; x++)
  1104. {
  1105. Vec4 c=image.color3DF(x, y, z);
  1106. c.xyz=RgbToHsb(c.xyz);
  1107. c.y=(c.y-avg)*contrast+avg;
  1108. c.xyz=HsbToRgb(c.xyz);
  1109. image.color3DF(x, y, z, c);
  1110. }
  1111. image.unlock();
  1112. }
  1113. }
  1114. }else
  1115. if(param.name=="contrastSatAlphaWeight")
  1116. {
  1117. flt contrast=param.asFlt(); if(contrast!=1)
  1118. {
  1119. flt avg; if(image.statsSat(null, null, null, null, null, &avg, &box) && image.lock())
  1120. {
  1121. for(int z=box.min.z; z<box.max.z; z++)
  1122. for(int y=box.min.y; y<box.max.y; y++)
  1123. for(int x=box.min.x; x<box.max.x; x++)
  1124. {
  1125. Vec4 c=image.color3DF(x, y, z);
  1126. c.xyz=RgbToHsb(c.xyz);
  1127. c.y=(c.y-avg)*contrast+avg;
  1128. c.xyz=HsbToRgb(c.xyz);
  1129. image.color3DF(x, y, z, c);
  1130. }
  1131. image.unlock();
  1132. }
  1133. }
  1134. }else
  1135. if(param.name=="avgSat")
  1136. {
  1137. flt avg; if(image.statsSat(null, null, &avg, null, null, null, &box))if(avg && image.lock())
  1138. {
  1139. flt mul=param.asFlt()/avg;
  1140. for(int z=box.min.z; z<box.max.z; z++)
  1141. for(int y=box.min.y; y<box.max.y; y++)
  1142. for(int x=box.min.x; x<box.max.x; x++)
  1143. {
  1144. Vec4 c=image.color3DF(x, y, z);
  1145. c.xyz=RgbToHsb(c.xyz);
  1146. c.y*=mul;
  1147. c.xyz=HsbToRgb(c.xyz);
  1148. image.color3DF(x, y, z, c);
  1149. }
  1150. image.unlock();
  1151. }
  1152. }else
  1153. if(param.name=="avgLum")
  1154. {
  1155. Vec4 avg; if(image.stats(null, null, &avg, null, null, null, &box))if(flt avg_l=avg.xyz.max())image.mulAdd(Vec4(Vec(param.asFlt()/avg_l), 1), 0, &box);
  1156. }else
  1157. if(param.name=="medLum")
  1158. {
  1159. Vec4 med; if(image.stats(null, null, null, &med, null, null, &box))if(flt med_l=med.xyz.max())image.mulAdd(Vec4(Vec(param.asFlt()/med_l), 1), 0, &box);
  1160. }else
  1161. if(param.name=="avgContrastLum")
  1162. {
  1163. Vec4 avg; if(image.stats(null, null, &avg, null, null, null, &box))AvgContrastLum(image, param.asFlt(), avg.xyz.max(), box);
  1164. }else
  1165. if(param.name=="medContrastLum")
  1166. {
  1167. Vec4 med; if(image.stats(null, null, null, &med, null, null, &box))AvgContrastLum(image, param.asFlt(), med.xyz.max(), box);
  1168. }else
  1169. if(param.name=="addHue")AddHue(image, param.asFlt(), box);else
  1170. if(param.name=="avgHue")
  1171. {
  1172. if(image.lock()) // lock for writing because we will use this lock for applying hue too
  1173. {
  1174. Vec4 col; if(image.stats(null, null, &col, null, null, null, &box))AddHue(image, HueDelta(RgbToHsb(col.xyz).x, param.asFlt()), box);
  1175. image.unlock();
  1176. }
  1177. }else
  1178. if(param.name=="medHue")
  1179. {
  1180. if(image.lock()) // lock for writing because we will use this lock for applying hue too
  1181. {
  1182. Vec4 col; if(image.stats(null, null, null, &col, null, null, &box))AddHue(image, HueDelta(RgbToHsb(col.xyz).x, param.asFlt()), box);
  1183. image.unlock();
  1184. }
  1185. }else
  1186. if(param.name=="addSat")
  1187. {
  1188. flt sat=param.asFlt(); if(sat && image.lock())
  1189. {
  1190. for(int z=box.min.z; z<box.max.z; z++)
  1191. for(int y=box.min.y; y<box.max.y; y++)
  1192. for(int x=box.min.x; x<box.max.x; x++)
  1193. {
  1194. Vec4 c=image.color3DF(x, y, z);
  1195. c.xyz=RgbToHsb(c.xyz);
  1196. c.y+=sat;
  1197. c.xyz=HsbToRgb(c.xyz);
  1198. image.color3DF(x, y, z, c);
  1199. }
  1200. image.unlock();
  1201. }
  1202. }else
  1203. if(param.name=="mulSat")
  1204. {
  1205. flt sat=param.asFlt(); if(sat!=1 && image.lock())
  1206. {
  1207. for(int z=box.min.z; z<box.max.z; z++)
  1208. for(int y=box.min.y; y<box.max.y; y++)
  1209. for(int x=box.min.x; x<box.max.x; x++)
  1210. {
  1211. Vec4 c=image.color3DF(x, y, z);
  1212. c.xyz=RgbToHsb(c.xyz);
  1213. c.y*=sat;
  1214. c.xyz=HsbToRgb(c.xyz);
  1215. image.color3DF(x, y, z, c);
  1216. }
  1217. image.unlock();
  1218. }
  1219. }else
  1220. if(param.name=="mulSatPhoto")
  1221. {
  1222. flt sat=param.asFlt(); if(sat!=1 && image.lock())
  1223. {
  1224. for(int z=box.min.z; z<box.max.z; z++)
  1225. for(int y=box.min.y; y<box.max.y; y++)
  1226. for(int x=box.min.x; x<box.max.x; x++)
  1227. {
  1228. Vec4 c=image.color3DF(x, y, z);
  1229. flt lin_lum=LinearLumOfSRGBColor(c.xyz);
  1230. c.xyz=RgbToHsb(c.xyz);
  1231. c.y*=sat;
  1232. c.xyz=HsbToRgb(c.xyz);
  1233. c.xyz=SRGBToLinear(c.xyz);
  1234. if(flt cur_lin_lum=LinearLumOfLinearColor(c.xyz))c.xyz*=lin_lum/cur_lin_lum;
  1235. c.xyz=LinearToSRGB(c.xyz);
  1236. image.color3DF(x, y, z, c);
  1237. }
  1238. image.unlock();
  1239. }
  1240. }else
  1241. if(param.name=="mulSatH"
  1242. || param.name=="mulSatHS")
  1243. {
  1244. bool sat=(param.name=="mulSatHS");
  1245. Mems<Str> vals; Split(vals, param.value, ',');
  1246. switch(vals.elms())
  1247. {
  1248. case 1: {flt v=TextFlt(vals[0]); MulSatH(image, v, v, v, v, v, v, sat, false, box);} break;
  1249. case 3: MulSatH(image, TextFlt(vals[0]), 1, TextFlt(vals[1]), 1, TextFlt(vals[2]), 1, sat, false, box); break;
  1250. case 6: MulSatH(image, TextFlt(vals[0]), TextFlt(vals[1]), TextFlt(vals[2]), TextFlt(vals[3]), TextFlt(vals[4]), TextFlt(vals[5]), sat, false, box); break;
  1251. }
  1252. }else
  1253. if(param.name=="mulSatHPhoto"
  1254. || param.name=="mulSatHSPhoto")
  1255. {
  1256. bool sat=(param.name=="mulSatHSPhoto");
  1257. Mems<Str> vals; Split(vals, param.value, ',');
  1258. switch(vals.elms())
  1259. {
  1260. case 1: {flt v=TextFlt(vals[0]); MulSatH(image, v, v, v, v, v, v, sat, true, box);} break;
  1261. case 3: MulSatH(image, TextFlt(vals[0]), 1, TextFlt(vals[1]), 1, TextFlt(vals[2]), 1, sat, true, box); break;
  1262. case 6: MulSatH(image, TextFlt(vals[0]), TextFlt(vals[1]), TextFlt(vals[2]), TextFlt(vals[3]), TextFlt(vals[4]), TextFlt(vals[5]), sat, true, box); break;
  1263. }
  1264. }else
  1265. if(param.name=="addHueSat")
  1266. {
  1267. Vec2 hue_sat=param.asVec2(); if(hue_sat.any() && image.lock())
  1268. {
  1269. for(int z=box.min.z; z<box.max.z; z++)
  1270. for(int y=box.min.y; y<box.max.y; y++)
  1271. for(int x=box.min.x; x<box.max.x; x++)
  1272. {
  1273. Vec4 c=image.color3DF(x, y, z);
  1274. c.xyz=RgbToHsb(c.xyz);
  1275. c.xy+=hue_sat;
  1276. c.xyz=HsbToRgb(c.xyz);
  1277. image.color3DF(x, y, z, c);
  1278. }
  1279. image.unlock();
  1280. }
  1281. }else
  1282. if(param.name=="setHue")
  1283. {
  1284. if(image.lock())
  1285. {
  1286. flt hue=param.asFlt();
  1287. for(int z=box.min.z; z<box.max.z; z++)
  1288. for(int y=box.min.y; y<box.max.y; y++)
  1289. for(int x=box.min.x; x<box.max.x; x++)
  1290. {
  1291. Vec4 c=image.color3DF(x, y, z);
  1292. c.xyz=RgbToHsb(c.xyz);
  1293. c.x=hue;
  1294. c.xyz=HsbToRgb(c.xyz);
  1295. image.color3DF(x, y, z, c);
  1296. }
  1297. image.unlock();
  1298. }
  1299. }else
  1300. if(param.name=="setHueSat")
  1301. {
  1302. if(image.lock())
  1303. {
  1304. Vec2 hue_sat=param.asVec2();
  1305. Vec rgb=HsbToRgb(Vec(hue_sat, 1));
  1306. for(int z=box.min.z; z<box.max.z; z++)
  1307. for(int y=box.min.y; y<box.max.y; y++)
  1308. for(int x=box.min.x; x<box.max.x; x++)
  1309. {
  1310. Vec4 c=image.color3DF(x, y, z);
  1311. c.xyz=rgb*c.xyz.max();
  1312. image.color3DF(x, y, z, c);
  1313. }
  1314. image.unlock();
  1315. }
  1316. }else
  1317. if(param.name=="setHueSatPhoto") // photometric
  1318. {
  1319. if(image.lock())
  1320. {
  1321. Vec2 hue_sat=param.asVec2();
  1322. Vec rgb=HsbToRgb(Vec(hue_sat, 1));
  1323. for(int z=box.min.z; z<box.max.z; z++)
  1324. for(int y=box.min.y; y<box.max.y; y++)
  1325. for(int x=box.min.x; x<box.max.x; x++)
  1326. {
  1327. Vec4 c=image.color3DF(x, y, z);
  1328. c.xyz=rgb*SRGBLumOfSRGBColor(c.xyz);
  1329. image.color3DF(x, y, z, c);
  1330. }
  1331. image.unlock();
  1332. }
  1333. }else
  1334. if(param.name=="lerpHue")
  1335. {
  1336. Vec2 hue_alpha=param.asVec2(); if(hue_alpha.y && image.lock())
  1337. {
  1338. for(int z=box.min.z; z<box.max.z; z++)
  1339. for(int y=box.min.y; y<box.max.y; y++)
  1340. for(int x=box.min.x; x<box.max.x; x++)
  1341. {
  1342. Vec4 c=image.color3DF(x, y, z);
  1343. Vec hsb=RgbToHsb(c.xyz);
  1344. hsb.x=hue_alpha.x;
  1345. c.xyz=Lerp(c.xyz, HsbToRgb(hsb), hue_alpha.y);
  1346. image.color3DF(x, y, z, c);
  1347. }
  1348. image.unlock();
  1349. }
  1350. }else
  1351. if(param.name=="lerpHueSat")
  1352. {
  1353. Vec hue_sat_alpha=param.asVec(); if(hue_sat_alpha.z && image.lock())
  1354. {
  1355. Vec rgb=HsbToRgb(Vec(hue_sat_alpha.xy, 1));
  1356. for(int z=box.min.z; z<box.max.z; z++)
  1357. for(int y=box.min.y; y<box.max.y; y++)
  1358. for(int x=box.min.x; x<box.max.x; x++)
  1359. {
  1360. Vec4 c=image.color3DF(x, y, z);
  1361. c.xyz=Lerp(c.xyz, rgb*c.xyz.max(), hue_sat_alpha.z);
  1362. image.color3DF(x, y, z, c);
  1363. }
  1364. image.unlock();
  1365. }
  1366. }else
  1367. if(param.name=="rollHue")
  1368. {
  1369. Vec2 hue_alpha=param.asVec2(); if(hue_alpha.y && image.lock())
  1370. {
  1371. for(int z=box.min.z; z<box.max.z; z++)
  1372. for(int y=box.min.y; y<box.max.y; y++)
  1373. for(int x=box.min.x; x<box.max.x; x++)
  1374. {
  1375. Vec4 c=image.color3DF(x, y, z);
  1376. Vec hsb=RgbToHsb(c.xyz);
  1377. hsb.x+=HueDelta(hsb.x, hue_alpha.x)*hue_alpha.y;
  1378. c.xyz=HsbToRgb(hsb);
  1379. image.color3DF(x, y, z, c);
  1380. }
  1381. image.unlock();
  1382. }
  1383. }else
  1384. if(param.name=="rollHueSat")
  1385. {
  1386. Vec hue_sat_alpha=param.asVec(); if(hue_sat_alpha.z && image.lock())
  1387. {
  1388. for(int z=box.min.z; z<box.max.z; z++)
  1389. for(int y=box.min.y; y<box.max.y; y++)
  1390. for(int x=box.min.x; x<box.max.x; x++)
  1391. {
  1392. Vec4 c=image.color3DF(x, y, z);
  1393. Vec hsb=RgbToHsb(c.xyz);
  1394. hsb.x+=HueDelta(hsb.x, hue_sat_alpha.x)*hue_sat_alpha.z;
  1395. hsb.y =Lerp (hsb.y, hue_sat_alpha.y, hue_sat_alpha.z);
  1396. c.xyz=HsbToRgb(hsb);
  1397. image.color3DF(x, y, z, c);
  1398. }
  1399. image.unlock();
  1400. }
  1401. }else
  1402. if(param.name=="rollHuePhoto")
  1403. {
  1404. Vec2 hue_alpha=param.asVec2(); if(hue_alpha.y && image.lock())
  1405. {
  1406. for(int z=box.min.z; z<box.max.z; z++)
  1407. for(int y=box.min.y; y<box.max.y; y++)
  1408. for(int x=box.min.x; x<box.max.x; x++)
  1409. {
  1410. Vec4 c=image.color3DF(x, y, z);
  1411. if(flt l=SRGBLumOfSRGBColor(c.xyz))
  1412. {
  1413. Vec hsb=RgbToHsb(c.xyz);
  1414. hsb.x+=HueDelta(hsb.x, hue_alpha.x)*hue_alpha.y;
  1415. c.xyz=HsbToRgb(hsb);
  1416. c.xyz*=l/SRGBLumOfSRGBColor(c.xyz);
  1417. image.color3DF(x, y, z, c);
  1418. }
  1419. }
  1420. image.unlock();
  1421. }
  1422. }else
  1423. if(param.name=="rollHueSatPhoto")
  1424. {
  1425. Vec hue_sat_alpha=param.asVec(); if(hue_sat_alpha.z && image.lock())
  1426. {
  1427. for(int z=box.min.z; z<box.max.z; z++)
  1428. for(int y=box.min.y; y<box.max.y; y++)
  1429. for(int x=box.min.x; x<box.max.x; x++)
  1430. {
  1431. Vec4 c=image.color3DF(x, y, z);
  1432. if(flt l=SRGBLumOfSRGBColor(c.xyz))
  1433. {
  1434. Vec hsb=RgbToHsb(c.xyz);
  1435. hsb.x+=HueDelta(hsb.x, hue_sat_alpha.x)*hue_sat_alpha.z;
  1436. hsb.y =Lerp (hsb.y, hue_sat_alpha.y, hue_sat_alpha.z);
  1437. c.xyz=HsbToRgb(hsb);
  1438. c.xyz*=l/SRGBLumOfSRGBColor(c.xyz);
  1439. image.color3DF(x, y, z, c);
  1440. }
  1441. }
  1442. image.unlock();
  1443. }
  1444. }else
  1445. if(param.name=="grey")
  1446. {
  1447. if(image.lock())
  1448. {
  1449. for(int z=box.min.z; z<box.max.z; z++)
  1450. for(int y=box.min.y; y<box.max.y; y++)
  1451. for(int x=box.min.x; x<box.max.x; x++)
  1452. {
  1453. Vec4 c=image.color3DF(x, y, z);
  1454. c.xyz=c.xyz.max();
  1455. image.color3DF(x, y, z, c);
  1456. }
  1457. image.unlock();
  1458. }
  1459. }else
  1460. if(param.name=="greyPhoto")
  1461. {
  1462. if(image.lock())
  1463. {
  1464. for(int z=box.min.z; z<box.max.z; z++)
  1465. for(int y=box.min.y; y<box.max.y; y++)
  1466. for(int x=box.min.x; x<box.max.x; x++)
  1467. {
  1468. Vec4 c=image.color3DF(x, y, z);
  1469. c.xyz=SRGBLumOfSRGBColor(c.xyz);
  1470. image.color3DF(x, y, z, c);
  1471. }
  1472. image.unlock();
  1473. }
  1474. }else
  1475. if(param.name=="channel")
  1476. {
  1477. int channels=param.value.length();
  1478. if( channels>=1 && channels<=4)
  1479. {
  1480. int chn[4]; REPAO(chn)=ChannelIndex(param.value[i]);
  1481. Image temp;
  1482. if(image.highPrecision())
  1483. {
  1484. temp.createSoftTry(image.w(), image.h(), image.d(), channels==1 ? IMAGE_F32 : channels==2 ? IMAGE_F32_2 : channels==3 ? IMAGE_F32_3 : IMAGE_F32_4);
  1485. Vec4 d(0, 0, 0, 1);
  1486. REPD(z, image.d())
  1487. REPD(y, image.h())
  1488. REPD(x, image.w())
  1489. {
  1490. Vec4 c=image.color3DF(x, y, z);
  1491. REPA(d.c){int ch=chn[i]; if(InRange(ch, c.c))d.c[i]=c.c[ch];}
  1492. temp.color3DF(x, y, z, d);
  1493. }
  1494. }else
  1495. {
  1496. temp.createSoftTry(image.w(), image.h(), image.d(), channels==1 ? IMAGE_R8 : channels==2 ? IMAGE_R8G8 : channels==3 ? IMAGE_R8G8B8 : IMAGE_R8G8B8A8);
  1497. Color d(0, 0, 0, 255);
  1498. REPD(z, image.d())
  1499. REPD(y, image.h())
  1500. REPD(x, image.w())
  1501. {
  1502. Color c=image.color3D(x, y, z);
  1503. REPA(d.c){int ch=chn[i]; if(InRange(ch, c.c))d.c[i]=c.c[ch];}
  1504. temp.color3D(x, y, z, d);
  1505. }
  1506. }
  1507. Swap(temp, image);
  1508. }
  1509. }else
  1510. if(param.name=="alphaFromBrightness" || param.name=="alphaFromLum" || param.name=="alphaFromLuminance")
  1511. {
  1512. image.alphaFromBrightness();
  1513. }else
  1514. if(param.name=="bump")
  1515. {
  1516. Vec2 blur=-1; // x=min, y=max, -1=auto
  1517. if(param.value.is())
  1518. {
  1519. UNIT_TYPE unit=GetUnitType(param.value);
  1520. flt full=image.size().avgF();
  1521. if(Contains(param.value, ','))
  1522. {
  1523. blur=param.asVec2(); // use 2 values if specified
  1524. blur.x=ConvertUnitType(blur.x, full, unit);
  1525. blur.y=ConvertUnitType(blur.y, full, unit);
  1526. }else
  1527. {
  1528. blur.y=param.asFlt(); // if 1 value specified then use as max
  1529. blur.y=ConvertUnitType(blur.y, full, unit);
  1530. }
  1531. }
  1532. CreateBumpFromColor(image, image, blur.x, blur.y, &WorkerThreads);
  1533. }else
  1534. if(param.name=="scale") // the formula is ok (for normal too), it works as if the bump was scaled vertically by 'scale' factor
  1535. {
  1536. flt scale=param.asFlt(); if(scale!=1)
  1537. {
  1538. if(image.monochromatic())image.mulAdd(Vec4(Vec(scale), 1), Vec4(Vec(-0.5f*scale+0.5f), 0), &box);else // if image is monochromatic then we need to transform all RGB together
  1539. if(!scale )image.mulAdd(Vec4(Vec( 0), 1), Vec4( 0.5f, 0.5f, 1, 0), &box);else // if zero scale then set Vec(0.5, 0.5, 1)
  1540. if(image.lock())
  1541. {
  1542. scale=1/scale;
  1543. for(int z=box.min.z; z<box.max.z; z++)
  1544. for(int y=box.min.y; y<box.max.y; y++)
  1545. for(int x=box.min.x; x<box.max.x; x++)
  1546. {
  1547. Vec4 c=image.color3DF(x, y, z); Vec &n=c.xyz;
  1548. n=n*2-1;
  1549. if(!image.highPrecision())n=DequantizeNormal(n); // TODO: image could have 'MakeHighPrec'
  1550. n.z*=scale;
  1551. n.normalize();
  1552. n=n*0.5f+0.5f;
  1553. image.color3DF(x, y, z, c);
  1554. }
  1555. image.unlock();
  1556. }
  1557. }
  1558. }else
  1559. if(param.name=="scaleXY")
  1560. {
  1561. Vec2 r=TextVec2Ex(param.value);
  1562. // v2=(v2-0.5)*r+0.5
  1563. // v2=v2*r -0.5*r+0.5
  1564. if(image.monochromatic()){flt a=r.avg(); image.mulAdd(Vec4(Vec(a), 1), Vec4(Vec(-0.5f*a+0.5f) , 0), &box);} // if image is monochromatic then we need to transform all RGB together
  1565. else image.mulAdd(Vec4( r, 1, 1), Vec4( -0.5f*r+0.5f, 0, 0), &box);
  1566. }else
  1567. if(param.name=="fixTransparent")
  1568. {
  1569. image.transparentToNeighbor(true, param.value.is() ? param.asFlt() : 1);
  1570. }
  1571. }
  1572. }
  1573. bool Project::loadImage(Image &image, C Edit::FileParams &fp, bool clamp, C Image *color, C Image *spec, C Image *bump)C
  1574. {
  1575. if(!fp.name.is()){image.del(); return true;}
  1576. Str name=fp.name;
  1577. bool lum_to_alpha=false;
  1578. UID image_id;
  1579. if(name[0]=='<')
  1580. {
  1581. if( name=="<color>" && color)color->copyTry(image);else
  1582. if((name=="<spec>" || name=="<specular>") && spec )spec ->copyTry(image);else
  1583. if( name=="<bump>" && bump )bump ->copyTry(image);else
  1584. image.del();
  1585. goto imported;
  1586. }
  1587. if(DecodeFileName(name, image_id))
  1588. {
  1589. name=editPath(image_id); // if the filename is in UID format then it's ELM_IMAGE
  1590. if(C Elm *image=findElm(image_id))if(C ElmImage *data=image->imageData())lum_to_alpha=data->alphaLum();
  1591. }
  1592. if(ImportImage(image, name, -1, IMAGE_SOFT, 1, true))
  1593. {
  1594. imported:
  1595. if(lum_to_alpha)image.alphaFromBrightness().divRgbByAlpha();
  1596. TransformImage(image, ConstCast(fp.params), clamp);
  1597. return true;
  1598. }
  1599. image.del(); return false;
  1600. }
  1601. bool Project::loadImages(Image &image, C Str &src, bool clamp, C Color &background, C Image *color, C Image *spec, C Image *bump)C
  1602. {
  1603. Mems<Edit::FileParams> fps=Edit::FileParams::Decode(src);
  1604. if(!fps.elms()){image.del(); return true;}
  1605. if( fps.elms()==1 && !fps[0].findParam("position") && !fps[0].findParam("pos"))return loadImage(image, fps[0], clamp, color, spec, bump); // can load as a single image only if doesn't have position specified
  1606. image.del();
  1607. bool ok=true, hp=false;
  1608. Image single;
  1609. REPA(fps)if(C TextParam *p=fps[i].findParam("mode"))if(p->value!="set"){hp=true; break;}
  1610. FREPA(fps)if(loadImage(single, fps[i], clamp, color, spec, bump)) // process in order
  1611. {
  1612. VecI2 pos=0; {C TextParam *p=fps[i].findParam("position"); if(!p)p=fps[i].findParam("pos"); if(p)pos=p->asVecI2();}
  1613. VecI2 size=single.size()+pos;
  1614. APPLY_MODE mode=APPLY_SET; if(C TextParam *p=fps[i].findParam("mode"))
  1615. {
  1616. if(p->value=="blend" )mode=APPLY_BLEND;else
  1617. if(p->value=="blendPremultiplied" || p->value=="premultipliedBlend")mode=APPLY_BLEND_PREMUL;else
  1618. if(p->value=="mul" )mode=APPLY_MUL;else
  1619. if(p->value=="div" )mode=APPLY_DIV;else
  1620. if(p->value=="add" )mode=APPLY_ADD;else
  1621. if(p->value=="sub" )mode=APPLY_SUB;else
  1622. if(p->value=="max" )mode=APPLY_MAX;
  1623. }
  1624. if(size.x>image.w() || size.y>image.h()) // make room for 'single', do this even if 'single' doesn't exist, because we may have 'pos' specified
  1625. {
  1626. VecI2 old_size=image.size();
  1627. if(image.is())image.crop(image, 0, 0, Max(image.w(), size.x), Max(image.h(), size.y));
  1628. else {image.createSoftTry(size.x, size.y, 1, (hp || single.highPrecision()) ? IMAGE_F32_4 : IMAGE_R8G8B8A8); image.clear();}
  1629. if(background!=TRANSPARENT)
  1630. REPD(y, image.h())
  1631. REPD(x, image.w())if(x>=old_size.x || y>=old_size.y)image.color(x, y, background);
  1632. }
  1633. if(single.is())
  1634. {
  1635. // put 'single' into image
  1636. if(single.highPrecision())MakeHighPrec(image);
  1637. REPD(y, single.h())
  1638. REPD(x, single.w())
  1639. {
  1640. Vec4 s=single.colorF(x, y);
  1641. if(mode==APPLY_SET)
  1642. {
  1643. image.colorF(x+pos.x, y+pos.y, s);
  1644. }else
  1645. {
  1646. Vec4 d=image.colorF(x+pos.x, y+pos.y);
  1647. switch(mode)
  1648. {
  1649. case APPLY_BLEND : d = Blend(d, s); break;
  1650. case APPLY_BLEND_PREMUL: d =PremultipliedBlend(d, s); break;
  1651. case APPLY_MUL : d*=s; break;
  1652. case APPLY_DIV : d/=s; break;
  1653. case APPLY_ADD : d+=s; break;
  1654. case APPLY_SUB : d-=s; break;
  1655. case APPLY_MAX : d=Max(s, d); break;
  1656. }
  1657. image.colorF(x+pos.x, y+pos.y, d);
  1658. }
  1659. }
  1660. }else TransformImage(image, fps[i].params, clamp); // if this 'single' is empty, then apply params to the entire 'image', so we can process entire atlas
  1661. }else ok=false;
  1662. return ok;
  1663. }
  1664. void Project::savedGame(Elm &elm, C Str &name) {elm.file_size=FSize(name);}
  1665. void Project::savedGame(Elm &elm ) {savedGame(elm, gamePath(elm));}
  1666. void Project::makeGameVer(Elm &elm, File *file)
  1667. {
  1668. if(IsServer)return; // this doesn't need to be performed on the server
  1669. Str file_edit_path=editPath(elm), file_game_path=gamePath(elm);
  1670. switch(elm.type)
  1671. {
  1672. case ELM_MTRL:
  1673. {
  1674. EditMaterial edit; edit.load(file_edit_path);
  1675. Material game; edit.copyTo(game, T); Save(game, file_game_path); savedGame(elm, file_game_path);
  1676. }break;
  1677. case ELM_WATER_MTRL:
  1678. {
  1679. EditWaterMtrl edit; edit.load(file_edit_path);
  1680. WaterMtrl game; edit.copyTo(game, T); Save(game, file_game_path); savedGame(elm, file_game_path);
  1681. }break;
  1682. case ELM_PHYS_MTRL:
  1683. {
  1684. EditPhysMtrl edit; edit.load(file_edit_path);
  1685. PhysMtrl game; edit.copyTo(game); Save(game, file_game_path); savedGame(elm, file_game_path);
  1686. }break;
  1687. case ELM_MESH:
  1688. {
  1689. ElmMesh *data=elm.meshData(); Matrix matrix; if(data)matrix=data->transform();else matrix.identity(); Skeleton *body_skel; getMeshSkels(data, null, &body_skel); Enum *draw_group=(data ? getEnum(data->draw_group_id) : null);
  1690. Mesh mesh; if(file)mesh.load(*file, game_path);else Load(mesh, file_edit_path, game_path); EditToGameMesh(mesh, mesh, body_skel, draw_group, &matrix);
  1691. Save(mesh, file_game_path); if(data)data->from(mesh); savedGame(elm, file_game_path);
  1692. }break;
  1693. case ELM_ENUM:
  1694. {
  1695. EditEnums edit; if(file)edit.load(*file);else edit.load(file_edit_path);
  1696. Enum game; edit.copyTo(game, elm.name); Save(game, file_game_path); savedGame(elm, file_game_path);
  1697. }break;
  1698. case ELM_IMAGE:
  1699. {
  1700. Image image; image.ImportTry(file_edit_path); EditToGameImage(image, image, *elm.imageData());
  1701. Save( image, file_game_path); savedGame(elm, file_game_path);
  1702. }break;
  1703. case ELM_IMAGE_ATLAS: break; // not done here
  1704. case ELM_ICON : break; // not done here
  1705. case ELM_FONT : break; // not done here
  1706. case ELM_PANEL_IMAGE : // not done here
  1707. {
  1708. /*EditPanelImage edit; edit.load(file_edit_path);
  1709. PanelImage game; edit.make(game); Save(game, file_game_path); */
  1710. }break;
  1711. case ELM_OBJ:
  1712. {
  1713. ElmObj * obj_data=( elm. objData() ); Elm *mesh_elm=(obj_data ? findElm(obj_data->mesh_id) : null);
  1714. ElmMesh *mesh_data=(mesh_elm ? mesh_elm->meshData() : null);
  1715. // set mesh/phys only if they're not empty
  1716. bool override_mesh=( obj_data && obj_data->mesh_id.valid()); if(override_mesh)if( MeshPtr mesh=gamePath( obj_data->mesh_id))override_mesh&=OverrideMeshSkel(mesh(), mesh->skeleton());
  1717. bool override_phys=(mesh_data && mesh_data->phys_id.valid()); if(override_phys)if(PhysBodyPtr phys=gamePath(mesh_data->phys_id))override_phys&=OverridePhys (phys());
  1718. EditObjectPtr edit=file_edit_path;
  1719. Object game; edit->copyTo(game, T, true, override_mesh ? &obj_data->mesh_id : null, override_phys ? &mesh_data->phys_id : null); Save(game, file_game_path); savedGame(elm, file_game_path);
  1720. }break;
  1721. case ELM_OBJ_CLASS:
  1722. {
  1723. EditObjectPtr edit=file_edit_path;
  1724. Object game; edit->copyTo(game, T, false, null, null); Save(game, file_game_path); savedGame(elm, file_game_path);
  1725. }break;
  1726. case ELM_TEXT_STYLE:
  1727. {
  1728. EditTextStyle edit; edit.load(file_edit_path);
  1729. TextStyle game; edit.copyTo(game, T); Save(game, file_game_path); savedGame(elm, file_game_path);
  1730. }break;
  1731. case ELM_GUI_SKIN:
  1732. {
  1733. EditGuiSkin edit; edit.load(file_edit_path);
  1734. GuiSkin game; edit.copyTo(game, T); Save(game, file_game_path); savedGame(elm, file_game_path);
  1735. }break;
  1736. case ELM_PANEL:
  1737. {
  1738. EditPanel edit; edit.load(file_edit_path);
  1739. Panel game; edit.copyTo(game, T); Save(game, file_game_path); savedGame(elm, file_game_path);
  1740. }break;
  1741. case ELM_ENV:
  1742. {
  1743. EditEnv edit; edit.load(file_edit_path);
  1744. Environment game; edit.copyTo(game, T); Save(game, file_game_path); savedGame(elm, file_game_path);
  1745. }break;
  1746. case ELM_WORLD: if(ElmWorld *data=elm.worldData())if(data->valid())
  1747. {
  1748. Str world_edit_path, world_game_path;
  1749. createWorldPaths(elm.id);
  1750. if(getWorldPaths(elm.id, world_edit_path, world_game_path))
  1751. {
  1752. Game::WorldSettings settings; data->copyTo(settings, T); Save(settings, world_game_path+"Settings", game_path);
  1753. }
  1754. }break;
  1755. case ELM_MINI_MAP: break; // not done here
  1756. }
  1757. }
  1758. void Project::removeOrphanedElms()
  1759. {
  1760. Memt<UID> used;
  1761. TimeStamp time; time.getUTC();
  1762. // get all used meshes
  1763. FREPA(elms) // go forward because most likely 'used' ID's will be increasing
  1764. {
  1765. C Elm &elm=elms[i]; if(C ElmObj *obj_data=elm.objData())used.binaryInclude(obj_data->mesh_id, Compare);
  1766. }
  1767. // remove unused meshes
  1768. REPA(elms)
  1769. {
  1770. Elm &elm=elms[i]; if(elm.type==ELM_MESH && !elm.removed() && !used.binaryHas(elm.id, Compare))elm.setRemoved(true, time);
  1771. }
  1772. used.clear();
  1773. // get all used skel
  1774. FREPA(elms) // go forward because most likely 'used' ID's will be increasing
  1775. {
  1776. C Elm &elm=elms[i]; if(C ElmMesh *mesh_data=elm.meshData())if(mesh_data->skel_id.valid())used.binaryInclude(mesh_data->skel_id, Compare);
  1777. }
  1778. // remove unused skel
  1779. REPA(elms)
  1780. {
  1781. Elm &elm=elms[i]; if(elm.type==ELM_SKEL && !elm.removed() && !used.binaryHas(elm.id, Compare))elm.setRemoved(true, time);
  1782. }
  1783. used.clear();
  1784. // get all used phys
  1785. FREPA(elms) // go forward because most likely 'used' ID's will be increasing
  1786. {
  1787. C Elm &elm=elms[i]; if(C ElmMesh *mesh_data=elm.meshData())if(mesh_data->phys_id.valid())used.binaryInclude(mesh_data->phys_id, Compare);
  1788. }
  1789. // remove unused phys
  1790. REPA(elms)
  1791. {
  1792. Elm &elm=elms[i]; if(elm.type==ELM_PHYS && !elm.removed() && !used.binaryHas(elm.id, Compare))elm.setRemoved(true, time);
  1793. }
  1794. }
  1795. void Project::eraseElm(C UID &elm_id)
  1796. {
  1797. if(Elm *elm=findElm(elm_id))
  1798. {
  1799. if(elm->type==ELM_WORLD) // worlds need to have their 'WorldVer' removed from the cache
  1800. {
  1801. CacheLock cl(world_vers); REPA(world_vers)if(world_vers.lockedData(i).world_id==elm_id)world_vers.removeData(&world_vers.lockedData(i));
  1802. }
  1803. if(elm->type==ELM_MINI_MAP) // mini maps need to have their 'MiniMapVer' removed from the cache
  1804. {
  1805. CacheLock cl(mini_map_vers); REPA(mini_map_vers)if(mini_map_vers.lockedData(i).mini_map_id==elm_id)mini_map_vers.removeData(&mini_map_vers.lockedData(i));
  1806. }
  1807. // elms.removeData(elm, true); don't remove element here, in case some 'eraseElm' function uses 'hierarchy' or 'elms' containers (for example CodeEditor.removeSource uses findSource and SourceLoc.setID which uses sourceFullName which uses hierarchy and elms)
  1808. if(elm->type==ELM_CODE)
  1809. {
  1810. FDelFile(codePath (elm_id));
  1811. FDelFile(codeBasePath(elm_id));
  1812. }else
  1813. {
  1814. FDel(editPath(elm_id)); // delete edit version
  1815. FDel(gamePath(elm_id)); // delete game version
  1816. REP(FormatSuffixElms)FDel(formatPath(elm_id, FormatSuffixes[i])); // delete all possible formats (these can be folder, for example Mini Maps)
  1817. }
  1818. }
  1819. }
  1820. bool Project::eraseElms(C MemPtr<UID> &elm_ids)
  1821. {
  1822. if(elm_ids.elms())
  1823. {
  1824. FREPA(elm_ids)eraseElm(elm_ids[i]); // process removal of all elements
  1825. FREPA(elm_ids)elms.removeData(findElm(elm_ids[i]), true); // remove from container
  1826. return true;
  1827. }
  1828. return false;
  1829. }
  1830. void Project::eraseTexFormats(C UID &tex_id)
  1831. {
  1832. REPD(format, FormatSuffixElms+1) // process 1 extra format as empty
  1833. REPD(downsize, MaxMaterialDownsize)
  1834. FDelFile(texFormatPath(tex_id, format ? FormatSuffixes[format-1] : null, downsize));
  1835. }
  1836. bool Project::eraseTex(C UID &_tex_id)
  1837. {
  1838. UID tex_id=_tex_id; // copy this to a temp var in case '_tex_id' actually belongs to 'texs' container which we're removing from below, which would make that UID invalid !!
  1839. if(texs.binaryExclude(tex_id, Compare)) // if found and removed
  1840. {
  1841. FDelFile(texPath(tex_id)); // delete the texture
  1842. eraseTexFormats(tex_id);
  1843. return true;
  1844. }
  1845. return false;
  1846. }
  1847. bool Project::eraseTexs()
  1848. {
  1849. bool erased=false;
  1850. // erase regular textures
  1851. Memt<UID> used; getTextures(used);
  1852. REPA(texs)if(!used.binaryHas(texs[i], Compare))erased|=eraseTex(texs[i]); // go from the end because of removal
  1853. // erase dynamic textures
  1854. used.clear();
  1855. REPA(elms)if(ElmMaterial *mtrl_data=elms[i].mtrlData())
  1856. if(mtrl_data->base_0_tex.valid() && mtrl_data->base_1_tex.valid())
  1857. used.binaryInclude(MergedBaseTexturesID(mtrl_data->base_0_tex, mtrl_data->base_1_tex), Compare);
  1858. for(FileFind ff(temp_tex_dynamic_path); ff(); )
  1859. {
  1860. bool tex_used=false;
  1861. UID tex_id; if(DecodeFileName(ff.name, tex_id))
  1862. {
  1863. tex_used=used.binaryHas(tex_id, Compare);
  1864. if(!tex_used)eraseTexFormats(tex_id);
  1865. }
  1866. if(!tex_used)FDelFile(ff.pathName());
  1867. }
  1868. return erased;
  1869. }
  1870. void Project::eraseWorldAreaObjs(C UID &world_id, C VecI2 &area_xy)
  1871. {
  1872. Str name=editAreaPath(world_id, area_xy);
  1873. Chunks chunks; chunks.load(name, WorldAreaSync);
  1874. if(Chunk *chunk=chunks.findChunk("Object"))
  1875. {
  1876. Memc<ObjData> objs;
  1877. if(LoadEditObject(chunk->ver, File().readMem(chunk->data(), chunk->elms()), objs, edit_path)) // load edit objects
  1878. {
  1879. REPA(objs)if(objs[i].removed)objs.remove(i, true);
  1880. SaveEditObject(chunks, objs, edit_path);
  1881. chunks.save(name, WorldAreaSync);
  1882. }
  1883. }
  1884. }
  1885. bool Project::eraseWorldObjs()
  1886. {
  1887. bool erased=false;
  1888. CacheLock lock(world_vers);
  1889. Memc<VecI2> areas;
  1890. REPA(world_vers) // iterate all worlds
  1891. {
  1892. WorldVer &world_ver=world_vers.lockedData(i);
  1893. MapLock ml(world_ver.obj);
  1894. REPA(world_ver.obj) // iterate all objects
  1895. {
  1896. ObjVer &obj_ver=world_ver.obj.lockedData(i);
  1897. if(obj_ver.removed()) // if object is removed
  1898. {
  1899. world_ver.setChanged();
  1900. areas.binaryInclude(obj_ver.area_xy, Compare); // mark for processing
  1901. world_ver.obj.remove(i); // we're completely erasing so remove from obj database too !! after this call don't operate on 'obj_ver' as it was just removed !!
  1902. }
  1903. }
  1904. if(areas.elms())
  1905. {
  1906. erased=true;
  1907. REPA(areas)eraseWorldAreaObjs(world_ver.world_id, areas[i]);
  1908. areas.clear();
  1909. }
  1910. }
  1911. return erased;
  1912. }
  1913. void Project::quickUpdateVersion(int ver) // this is called inside 'load', it occurs when opening projects and loading from EsenthelProject file, we can't modify files here !!
  1914. {
  1915. if(ver<0)return;
  1916. if(ver<=21) // there was a bug in 'duplicate' which did not set 'mesh_id' correctly, so fix it
  1917. REPA(elms)
  1918. {
  1919. Elm &elm=elms[i]; if(ElmMesh *mesh_data=elm.meshData())
  1920. {
  1921. if(Elm *skel_elm=findElm(mesh_data->skel_id))if(ElmSkel *skel_data=skel_elm->skelData())skel_data->mesh_id=elm.id;
  1922. if(Elm *phys_elm=findElm(mesh_data->phys_id))if(ElmPhys *phys_data=phys_elm->physData())phys_data->mesh_id=elm.id;
  1923. }
  1924. }
  1925. if(ver<=45) // 'ElmMesh' didn't have 'obj_id'
  1926. REPA(elms)
  1927. {
  1928. Elm &mesh_elm=elms[i]; if(ElmMesh *mesh_data=mesh_elm.meshData())REPA(elms)if(ElmObj *obj_data=elms[i].objData())if(obj_data->mesh_id==mesh_elm.id)
  1929. {
  1930. mesh_data->obj_id=elms[i].id; break;
  1931. }
  1932. }
  1933. if(ver<=27) // project version 27 and below
  1934. {
  1935. // mtrl base 1 and detail textures need to have channels swapped
  1936. texs_update.clear();
  1937. REPA(elms)
  1938. {
  1939. C Elm &elm=elms[i]; switch(elm.type)
  1940. {
  1941. case ELM_MTRL: if(C ElmMaterial *mtrl_data=elm.mtrlData())
  1942. {
  1943. if(mtrl_data->base_1_tex.valid() && texs.binaryHas(mtrl_data->base_1_tex, Compare))texs_update.binaryInclude(mtrl_data->base_1_tex, Compare);
  1944. if(mtrl_data->detail_tex.valid() && texs.binaryHas(mtrl_data->detail_tex, Compare))texs_update.binaryInclude(mtrl_data->detail_tex, Compare);
  1945. }break;
  1946. case ELM_WATER_MTRL: if(C ElmWaterMtrl *water_mtrl_data=elm.waterMtrlData())
  1947. {
  1948. if(water_mtrl_data->base_1_tex.valid() && texs.binaryHas(water_mtrl_data->base_1_tex, Compare))texs_update.binaryInclude(water_mtrl_data->base_1_tex, Compare);
  1949. }break;
  1950. }
  1951. }
  1952. // src file was encoded differently
  1953. REPA(elms)
  1954. {
  1955. Elm &elm=elms[i]; if(elm.data)
  1956. {
  1957. Mems<Edit::FileParams> files=_DecodeFileParams(elm.srcFile());
  1958. if(elm.type==ELM_ANIM || elm.type==ELM_MTRL)FREPA(files)
  1959. {
  1960. Edit::FileParams &file=files[i];
  1961. file.name.tailSlash(false); // DAE has empty animation names, so this could have been "file.dae/"
  1962. Str path=GetPath(file.name);
  1963. if(ExtType(GetExt(path))==EXT_MESH) // if file was stored as "file.ext/inner_name", for example "Character.fbx/run" run animation inside fbx file
  1964. {
  1965. file.getParam("name").value=GetBase(file.name);
  1966. file.name=path;
  1967. }
  1968. }
  1969. elm.data->src_file=Edit::FileParams::Encode(files);
  1970. }
  1971. }
  1972. }
  1973. if(ver<=46) // 46 and below used '|' as separator
  1974. {
  1975. REPA(elms)
  1976. {
  1977. Elm &elm=elms[i]; if(elm.type!=ELM_ANIM && elm.data)elm.data->src_file.replace('|', '\n'); // keep anim as | because some animations can have | in the name "Para.fbx?name=Para|SitLoop"
  1978. }
  1979. }
  1980. #if 0 // force upload of skeletons
  1981. REPA(elms)
  1982. {
  1983. Elm &elm=elms[i]; if(ElmSkel *skel_data=elm.skelData())if(skel_data.ver)
  1984. {
  1985. skel_data.newVer();
  1986. skel_data.file_time.getUTC();
  1987. }
  1988. }
  1989. #endif
  1990. #if 0 // fix transforms (this can be executed if copied Project Data file from the Server version to the Client version)
  1991. don't use as it may be broken (skeletons not transformed properly)
  1992. REPA(elms){Elm &elm=elms[i]; if(ElmSkel *skel_data=elm.skelData())if(Elm *mesh_elm=findElm(skel_data.mesh_id))if(ElmMesh *mesh_data=mesh_elm.meshData())skel_data.transform=mesh_data.transform;} // skeletons first
  1993. REPA(elms){Elm &elm=elms[i]; if(ElmAnim *anim_data=elm.animData())if(Elm *skel_elm=findElm(anim_data.skel_id))if(ElmSkel *skel_data=skel_elm.skelData())anim_data.transform=skel_data.transform;} // animations next
  1994. #endif
  1995. #if 0 // test transforms
  1996. REPA(elms)
  1997. {
  1998. Elm &elm=elms[i]; switch(elm.type)
  1999. {
  2000. case ELM_ANIM: if(ElmAnim *anim_data=elm.animData())if(Elm *skel_elm=findElm(anim_data.skel_id))if(ElmSkel *skel_data=skel_elm.skelData())if(anim_data.transform!=skel_data.transform)LogN(S+"Different Anim-Skel transform:\n"+Project.elmFullName(&elm)+'\n'+anim_data.transform.asText()+'\n'+skel_data.transform.asText()); break;
  2001. case ELM_SKEL: if(ElmSkel *skel_data=elm.skelData())if(Elm *mesh_elm=findElm(skel_data.mesh_id))if(ElmMesh *mesh_data=mesh_elm.meshData())if(skel_data.transform!=mesh_data.transform)LogN(S+"Different Skel-Mesh transform:\n"+Project.elmFullName(&elm)+'\n'+skel_data.transform.asText()+'\n'+mesh_data.transform.asText()); break;
  2002. }
  2003. }
  2004. #endif
  2005. #if 0 // test mesh->obj references count
  2006. REPA(elms)
  2007. {
  2008. Elm &elm=elms[i]; switch(elm.type)
  2009. {
  2010. case ELM_MESH:
  2011. {
  2012. int count=0; REPA(elms)
  2013. {
  2014. Elm &obj_elm=elms[i]; if(ElmObj *obj_data=obj_elm.objData())if(obj_data.mesh_id==elm.id)count++;
  2015. }
  2016. if(count!=1)Exit(S+elmFullName(&elm)+" is used by "+count+" Objects");
  2017. }break;
  2018. }
  2019. }
  2020. #endif
  2021. }
  2022. void Project::updateVersion(int ver, bool this_project, C MemPtr<UID> &elm_ids) // if 'elm_ids' is null, then all elements are processed, this is called inside 'open' or after copying elements to project
  2023. {
  2024. if(ver<0)return;
  2025. File f, temp, *src;
  2026. bool compress_skel=(IsServer && ElmCompressable(ELM_SKEL)),
  2027. compress_anim=(IsServer && ElmCompressable(ELM_ANIM));
  2028. // first process compression changes to convert old to latest compression state
  2029. if(IsServer && ver<=27 && ElmCompressable(ELM_SKEL)) // ver 27 and below kept skeleton uncompressed on the server, and if we want it to be compressed now, then compress it:
  2030. {
  2031. REPA(elms)if(elms[i].type==ELM_SKEL)
  2032. {
  2033. Str path=gamePath(elms[i]); if(f.readTry(path) && Compress(f, temp.writeMem(), ClientNetworkCompression, ClientNetworkCompressionLevel))
  2034. {
  2035. f.del(); temp.pos(0); SafeOverwrite(temp, path);
  2036. }
  2037. }
  2038. }
  2039. if(ver<=48)
  2040. {
  2041. // have to update animations first with old skeleton bone types
  2042. REP(elm_ids ? elm_ids.elms() : elms.elms())
  2043. if(Elm *elm=(elm_ids ? findElm(elm_ids[i]) : &elms[i]))
  2044. switch(elm->type)
  2045. {
  2046. case ELM_CODE: if(ver<=48) // in ver 48 and below, codes were stored in "Edit" folder without extension
  2047. {
  2048. Code code; if(code.load(editPath(*elm)))
  2049. {
  2050. if(SaveCode(code.current, codePath(*elm)))FDelFile(editPath(*elm));
  2051. if(!IsServer)
  2052. {
  2053. if(code.base.is())SaveCode(code.base, codeBasePath(*elm));
  2054. else FDelFile(codeBasePath(*elm));
  2055. }
  2056. }
  2057. }break;
  2058. case ELM_ANIM: if(ver<=47)if(ElmAnim *anim_data=elm->animData())
  2059. {
  2060. src=&f; if(src->readTry(gamePath(*elm)))
  2061. {
  2062. if(compress_anim)if(Decompress(*src, temp, true)){src=&temp; src->pos(0);}else src=null;
  2063. if(src)
  2064. {
  2065. Animation anim; if(anim.load(*src))
  2066. {
  2067. // load skeleton
  2068. if(Elm *skel_elm=findElm(anim_data->skel_id))
  2069. {
  2070. src=&f; if(src->readTry(gamePath(*skel_elm)))
  2071. {
  2072. if(compress_skel)if(Decompress(*src, temp, true)){src=&temp; src->pos(0);}else src=null;
  2073. if(src)
  2074. {
  2075. Skeleton skel; if(skel.load(*src))
  2076. {
  2077. if(ver<=47)
  2078. {
  2079. anim.setBoneNameTypeIndexesFromSkeleton(skel); // Warning: be careful with calling this method when 'Skeleton.setBoneTypes' was called because it could mess up links if 'skel' bone types indexes have changed due to some changes in how 'setBoneTypes' works, so we call it as the first thing
  2080. skel.setBoneTypes(); // !! if we do this here, then we have to do it for skeletons below with same 'ver' check !! make sure we have the latest types
  2081. anim.setBoneTypeIndexesFromSkeleton(skel); // after we've set bone names, we can update bone types to latest skel data
  2082. }
  2083. }
  2084. }
  2085. }
  2086. }
  2087. // save animation
  2088. anim.save(f.writeMem()); src=&f; src->pos(0);
  2089. if(compress_anim)if(Compress(*src, temp.writeMem(), ClientNetworkCompression, ClientNetworkCompressionLevel)){src=&temp; src->pos(0);}else src=null;
  2090. if(src)SafeOverwrite(*src, gamePath(*elm));
  2091. }
  2092. }
  2093. }
  2094. }break;
  2095. }
  2096. REP(elm_ids ? elm_ids.elms() : elms.elms())
  2097. if(Elm *elm=(elm_ids ? findElm(elm_ids[i]) : &elms[i]))
  2098. switch(elm->type)
  2099. {
  2100. case ELM_SKEL: if(ver<=47)
  2101. {
  2102. src=&f; if(src->readTry(gamePath(*elm)))
  2103. {
  2104. if(compress_skel)if(Decompress(*src, temp, true)){src=&temp; src->pos(0);}else continue;
  2105. Skeleton skel; if(skel.load(*src))
  2106. {
  2107. if(ver<=47)skel.setBoneTypes(); // !! we can call it only if we've adjusted animations to new bone types above with the same 'ver' check !!
  2108. skel.save(f.writeMem()); src=&f; src->pos(0);
  2109. if(compress_skel)if(Compress(*src, temp.writeMem(), ClientNetworkCompression, ClientNetworkCompressionLevel)){src=&temp; src->pos(0);}else src=null;
  2110. if(src)SafeOverwrite(*src, gamePath(*elm));
  2111. if(ver<=21 && !FExistSystem(editPath(*elm))) // if there's no skeleton edit version
  2112. {
  2113. // create skeleton edit version
  2114. if(ElmSkel *skel_data=elm->skelData())skel.transform(~skel_data->transform()); // !! transform after saving game version, and before creating edit version !!
  2115. EditSkeleton edit; edit.create(skel, null);
  2116. edit.save(f.writeMem()); src=&f; src->pos(0);
  2117. if(compress_skel)if(Compress(*src, temp.writeMem(), ClientNetworkCompression, ClientNetworkCompressionLevel)){src=&temp; src->pos(0);}else src=null;
  2118. if(src)SafeOverwrite(*src, editPath(*elm));
  2119. }
  2120. }
  2121. }
  2122. }break;
  2123. case ELM_MTRL: if(ver<=46)if(ElmMaterial *mtrl_data=elm->mtrlData()) // ver 46 and below used '|' as separator, and didn't have some 'ElmMaterial' members set
  2124. {
  2125. EditMaterial edit; if(edit.load(editPath(elm->id)))
  2126. {
  2127. if(ver<=46)
  2128. {
  2129. edit. color_map .replace('|', '\n');
  2130. edit. alpha_map .replace('|', '\n');
  2131. edit. bump_map .replace('|', '\n');
  2132. edit. normal_map .replace('|', '\n');
  2133. edit.specular_map .replace('|', '\n');
  2134. edit. glow_map .replace('|', '\n');
  2135. edit.detail_color .replace('|', '\n');
  2136. edit.detail_bump .replace('|', '\n');
  2137. edit.detail_normal .replace('|', '\n');
  2138. edit. macro_map.replace('|', '\n');
  2139. edit.reflection_map.replace('|', '\n');
  2140. edit. light_map.replace('|', '\n');
  2141. Save(edit, editPath(elm->id));
  2142. mtrl_data->from(edit);
  2143. }
  2144. }
  2145. }break;
  2146. }
  2147. }
  2148. #if 0 // reset game meshes
  2149. {
  2150. int c=0;
  2151. REPA(elms)if(elms[i].type==ELM_MESH)
  2152. {
  2153. if(!((c++)&0xFF))
  2154. {
  2155. Materials.delayRemoveNow();
  2156. Images .delayRemoveNow();
  2157. }
  2158. makeGameVer(elms[i]);
  2159. }
  2160. }
  2161. #endif
  2162. #if 0 // reset mesh bone maps
  2163. {
  2164. int c=0;
  2165. REPA(elms)if(elms[i].type==ELM_MESH)if(ElmMesh *mesh_data=elms[i].meshData())if(mesh_data.skel_id.valid())
  2166. {
  2167. Skeleton skel; Mesh mesh; if(Load(mesh, editPath(elms[i]), game_path))if(skel.load(gamePath(mesh_data.skel_id)))
  2168. {
  2169. mesh.skeleton(&skel, true).skeleton(null);
  2170. Save(mesh, editPath(elms[i]), game_path);
  2171. mesh_data.newVer();
  2172. mesh_data.file_time.getUTC();
  2173. makeGameVer(elms[i]);
  2174. if(!((c++)&0xFF))
  2175. {
  2176. Materials.delayRemoveNow();
  2177. Images .delayRemoveNow();
  2178. }
  2179. }
  2180. }
  2181. }
  2182. #endif
  2183. #if 0 // reset game skeletons
  2184. {
  2185. REPA(elms)if(elms[i].type==ELM_SKEL)
  2186. {
  2187. Skeleton skel; if(skel.load(gamePath(elms[i])))Save(skel, gamePath(elms[i]));
  2188. }
  2189. }
  2190. #endif
  2191. #if 0 // test anim loop
  2192. {
  2193. REPA(elms)if(elms[i].type==ELM_ANIM)
  2194. {
  2195. Animation anim; if(!anim.load(gamePath(elms[i])))Exit("anim load"); if(anim.loop()!=elms[i].animData().loop())LogN(S+anim.loop()+' '+Project.elmFullName(&elms[i]));
  2196. }
  2197. }
  2198. #endif
  2199. }
  2200. void Project::textData(bool on)
  2201. {
  2202. if(text_data!=on)
  2203. {
  2204. text_data=on;
  2205. if(save())
  2206. {
  2207. if(!text_data && path.is())FDelFile(path+"Data.txt"); // if disabled text_data then delete the text file
  2208. }
  2209. }
  2210. }
  2211. void Project::meshSetAutoTanBin(Elm &elm, C MaterialPtr &material)
  2212. {
  2213. if(elm.type==ELM_MESH)
  2214. {
  2215. Mesh mesh; if(mesh.load(gamePath(elm.id)))
  2216. {
  2217. bool changed=false;
  2218. REP(mesh.lods())
  2219. {
  2220. MeshLod &lod=mesh.lod(i); REPA(lod)
  2221. {
  2222. MeshPart &part=lod.parts[i]; if(!material || HasMaterial(part, material))
  2223. {
  2224. uint flag =((part.base.flag()|part.render.flag())&VTX_TAN_BIN); part.setAutoTanBin();
  2225. if( flag!=((part.base.flag()|part.render.flag())&VTX_TAN_BIN))changed=true;
  2226. }
  2227. }
  2228. }
  2229. if(changed){Save(mesh, gamePath(elm.id)); savedGame(elm);}
  2230. }
  2231. }
  2232. }
  2233. void Project::mtrlSetAutoTanBin(C UID &mtrl_id)
  2234. {
  2235. MaterialPtr material;
  2236. if(mtrl_id.valid())REPA(elms)
  2237. {
  2238. Elm &elm=elms[i];
  2239. if(C ElmMesh *data=elm.meshData())
  2240. if(data->mtrl_ids.binaryHas(mtrl_id, Compare))
  2241. {
  2242. if(!material)material=gamePath(mtrl_id); // load only when needed
  2243. meshSetAutoTanBin(elm, material);
  2244. }
  2245. }
  2246. }
  2247. void Project::animTransformChanged(Elm &elm_anim)
  2248. {
  2249. }
  2250. void Project::setAnimTransform(Elm &elm_anim)
  2251. {
  2252. if(ElmAnim *anim_data=elm_anim.animData())
  2253. if(Elm *skel_elm=findElm(anim_data->skel_id))
  2254. if(ElmSkel *skel_data=skel_elm->skelData())
  2255. if(anim_data->transform!=skel_data->transform)
  2256. if(Animation *anim=Animations.get(gamePath(elm_anim.id)))
  2257. if(Skeleton *skel=Skeletons.get(gamePath(skel_elm->id)))
  2258. if(!anim->bones.elms() || skel->is()) // if there are 'anim.bones' then process only if the skeleton is valid (known bones, because 'Animation.transform' relies on correct bone information)
  2259. {
  2260. anim->transform(GetTransform(anim_data->transform(), skel_data->transform()), *skel);
  2261. Save(*anim, gamePath(elm_anim.id)); savedGame(elm_anim);
  2262. anim_data->transform=skel_data->transform;
  2263. animTransformChanged(elm_anim);
  2264. }
  2265. }
  2266. void Project::setPhysParams(Elm &phys_elm)
  2267. {
  2268. if(ElmPhys *phys_data=phys_elm.physData())
  2269. if(PhysBodyPtr phys=PhysBodyPtr().get(gamePath(phys_elm.id)))
  2270. {
  2271. PhysMtrl *mtrl=PhysMtrls(gamePath(phys_data->mtrl_id));
  2272. {CacheLock cl(PhysBodies); phys->density=phys_data->density; phys->material=mtrl;}
  2273. Save(*phys, gamePath(phys_elm.id)); savedGame(phys_elm);
  2274. }
  2275. }
  2276. void Project::skelTransformChanged(C UID &skel_id)
  2277. {
  2278. if(skel_id.valid())REPA(elms)
  2279. {
  2280. Elm &elm=elms[i]; if(ElmAnim *data=elm.animData())if(data->skel_id==skel_id)setAnimTransform(elm); // transform all animations linked with this skeleton
  2281. }
  2282. }
  2283. void Project::skelChanged(Elm &skel_elm)
  2284. {
  2285. if(skel_elm.type==ELM_SKEL)
  2286. {
  2287. REPA(elms)
  2288. {
  2289. Elm &elm=elms[i];
  2290. bool uses_skel=false;
  2291. switch(elm.type)
  2292. {
  2293. case ELM_MESH:
  2294. {
  2295. UID body_skel; getMeshSkels(elm.meshData(), null, &body_skel);
  2296. if( body_skel==skel_elm.id)uses_skel=true; // if mesh uses the skeleton
  2297. }break;
  2298. /*case ELM_ANIM: if(C ElmAnim *anim_data=elm.animData())
  2299. {
  2300. if(anim_data.skel_id==skel_elm.id)uses_skel=true;
  2301. }break;*/
  2302. }
  2303. if(uses_skel)makeGameVer(elm);
  2304. }
  2305. // because animation transforms will be adjusted only when skeleton is available ('setAnimTransform': "if(!anim.bones.elms() || skel.is())") then upon skeleton being changed it's possible we've received it from the server and now we need to transform the animations
  2306. skelTransformChanged(skel_elm.id);
  2307. }
  2308. }
  2309. void Project::setSkelTransform(Elm &skel_elm)
  2310. {
  2311. if(ElmSkel *skel_data=skel_elm.skelData())
  2312. if(Elm *mesh_elm=findElm(skel_data->mesh_id))
  2313. if(ElmMesh *mesh_data=mesh_elm->meshData())
  2314. if(skel_data->transform!=mesh_data->transform)
  2315. if(Skeleton *skel=Skeletons.get(gamePath(skel_elm.id)))
  2316. {
  2317. Matrix matrix=GetTransform(skel_data->transform(), mesh_data->transform());
  2318. skel->transform(matrix);
  2319. // edit version doesn't need to be changed since it's always in Identity
  2320. Save(*skel, gamePath(skel_elm.id)); savedGame(skel_elm);
  2321. skel_data->transform=mesh_data->transform;
  2322. skelTransformChanged(skel_elm.id);
  2323. }
  2324. }
  2325. bool Project::setPhysTransform(Elm &phys_elm)
  2326. {
  2327. if(ElmPhys *phys_data=phys_elm.physData())
  2328. if(Elm *mesh_elm=findElm(phys_data->mesh_id))
  2329. if(ElmMesh *mesh_data=mesh_elm->meshData())
  2330. if(phys_data->transform!=mesh_data->transform)
  2331. if(PhysBodyPtr phys=PhysBodyPtr().get(gamePath(phys_elm.id)))
  2332. {
  2333. Matrix matrix=GetTransform(phys_data->transform(), mesh_data->transform());
  2334. {CacheLock cl(PhysBodies); phys->transform(matrix);}
  2335. Save(*phys, gamePath(phys_elm.id)); savedGame(phys_elm);
  2336. phys_data->transform=mesh_data->transform;
  2337. phys_data->from(*phys);
  2338. physChanged(phys_elm);
  2339. return true;
  2340. }
  2341. return false;
  2342. }
  2343. void Project::meshTransformChanged(Elm &mesh_elm, bool body_changed) {Memt<UID> processed; meshTransformChanged(mesh_elm, body_changed, processed);}
  2344. void Project::meshTransformChanged(Elm &mesh_elm, bool body_changed, Memt<UID> &processed) // can't do default param "=Memt<UID>()" on Mac
  2345. {
  2346. if(processed.binaryInclude(mesh_elm.id, Compare)) // to avoid potential infinite loops
  2347. if(ElmMesh *mesh_data=mesh_elm.meshData())
  2348. {
  2349. // force transform from body
  2350. bool transform_changed=false;
  2351. if(Elm *body_elm=findElm(mesh_data->body_id))
  2352. if(ElmMesh *body_mesh_data=body_elm->meshData())if(mesh_data->transform!=body_mesh_data->transform)
  2353. {
  2354. mesh_data->transform=body_mesh_data->transform;
  2355. transform_changed=true;
  2356. }
  2357. if(body_changed || transform_changed)makeGameVer(mesh_elm); // have to make game mesh if body changes (because we will have a new target skeleton) or transform changes
  2358. // set skel+phys transforms according to this transform
  2359. if(Elm *skel_elm=findElm(mesh_data->skel_id))setSkelTransform(*skel_elm);
  2360. if(Elm *phys_elm=findElm(mesh_data->phys_id))setPhysTransform(*phys_elm);
  2361. // set cloths that use this mesh as a body
  2362. REPA(elms)
  2363. {
  2364. Elm &elm=elms[i]; if(ElmMesh *cloth_mesh_data=elm.meshData())if(cloth_mesh_data->body_id==mesh_elm.id)meshTransformChanged(elm, body_changed, processed);
  2365. }
  2366. // notify of change
  2367. meshChanged(mesh_elm);
  2368. }
  2369. }
  2370. void Project::rebuildEmbedForObj(C UID &obj ) {Memt<UID> objs, exts; objs.add(obj); getExtendedObjs(objs, exts); rebuildEmbedForObjs(exts);}
  2371. void Project::rebuildEmbedForObjs(Memt<UID> &objs)
  2372. {
  2373. // verify embed for all objects based on 'objs'
  2374. REPA(world_vers)
  2375. {
  2376. WorldVer &world_ver=world_vers.lockedData(i);
  2377. REPA(world_ver.obj)
  2378. {
  2379. ObjVer &obj=world_ver.obj.lockedData(i);
  2380. if(!obj.removed() && objs.binaryHas(obj.elm_obj_id, Compare)) // if object exists and its base is contained in 'objs'
  2381. {
  2382. C UID &obj_id=world_ver.obj.lockedKey(i);
  2383. rebuildEmbedObj(obj_id, obj.area_xy, world_ver, true); // 'rebuild_game_area_objs=true' because obj base changed, and world object embed state could be changed
  2384. }
  2385. }
  2386. }
  2387. }
  2388. void Project::rebuildPathsForObj(C UID &obj , bool only_not_ovr) {Memt<UID> objs, exts; objs.add(obj); getExtendedObjs(objs, exts); rebuildPathsForObjs(exts, only_not_ovr);}
  2389. void Project::rebuildPathsForObjs(Memt<UID> &objs, bool only_not_ovr) // 'only_not_ovr'=if rebuild paths only for objects not overriding the path mode
  2390. {
  2391. // rebuild paths for all areas which have objects based on 'objs'
  2392. REPA(world_vers)
  2393. {
  2394. WorldVer &world_ver=world_vers.lockedData(i);
  2395. REPA(world_ver.obj)
  2396. {
  2397. ObjVer &obj=world_ver.obj.lockedData(i);
  2398. if(!obj.removed() && objs.binaryHas(obj.elm_obj_id, Compare)) // if object exists and its base is contained in 'objs'
  2399. if(only_not_ovr ? !obj.ovrPath() : obj.path(T)!=OBJ_PATH_IGNORE) // if custom condition met, or want to create paths
  2400. {
  2401. C UID &obj_id=world_ver.obj.lockedKey(i);
  2402. world_ver.rebuildPaths(obj_id, obj.area_xy);
  2403. }
  2404. }
  2405. }
  2406. }
  2407. void Project::verifyPathsForObjClass(C UID &obj_class)
  2408. {
  2409. if(obj_class.valid())
  2410. {
  2411. Memt<UID> objs, exts;
  2412. REPA(elms) // iterate all objects
  2413. {
  2414. Elm &elm=elms[i]; if(ElmObj *data=elm.objData())if(data->base_id==obj_class && !data->ovrPath()) // if they have this base and don't override path
  2415. if(Elm *mesh_elm=findElm( data->mesh_id))if(ElmMesh *mesh_data=mesh_elm->meshData()) // if has mesh
  2416. if(Elm *phys_elm=findElm(mesh_data->phys_id))if(ElmPhys *phys_data=phys_elm->physData())if(phys_data->hasBody()) // if has phys
  2417. {
  2418. objs.binaryInclude(elm.id, Compare);
  2419. }
  2420. }
  2421. getExtendedObjs(objs, exts);
  2422. rebuildPathsForObjs(exts, true); // rebuild only for objects that don't override paths (if they override then it means that changing the base doesn't affect their path mode), we must rebuild this also for objects with final path mode set to ignore, in case we've just disabled paths
  2423. }
  2424. }
  2425. void Project::physChanged(Elm &phys)
  2426. {
  2427. Memt<UID> objs, exts; // list of all objects which use this phys body
  2428. if(ElmPhys *phys_data=phys.physData())REPA(elms) // iterate all objects
  2429. {
  2430. Elm &obj=elms[i]; if(ElmObj *obj_data=obj.objData())if(obj_data->mesh_id==phys_data->mesh_id) // check if object mesh matches phys mesh
  2431. {
  2432. objs.binaryInclude(obj.id, Compare);
  2433. }
  2434. }
  2435. getExtendedObjs(objs, exts);
  2436. rebuildEmbedForObjs(exts);
  2437. rebuildPathsForObjs(exts, false);
  2438. }
  2439. void Project::objChanged(Elm &obj)
  2440. {
  2441. {CacheLock cl(EditObjects); REPA(EditObjects)EditObjects.lockedData(i).updateBase(edit_path);}
  2442. {CacheLock cl( Objects); REPA( Objects) Objects.lockedData(i).updateBase();}
  2443. }
  2444. void Project::meshChanged(Elm &mesh) // objects rely on mesh information (physical body "obj -> mesh -> phys"), so rebuild objects
  2445. {
  2446. Memt<UID> objs, exts;
  2447. REPA(elms)
  2448. {
  2449. Elm &obj=elms[i]; if(ElmObj *obj_data=obj.objData())if(obj_data->mesh_id==mesh.id)
  2450. {
  2451. makeGameVer(obj);
  2452. objChanged(obj);
  2453. objs.binaryInclude(obj.id, Compare);
  2454. }
  2455. }
  2456. getExtendedObjs(objs, exts);
  2457. rebuildEmbedForObjs(exts);
  2458. }
  2459. void Project::rebuildWorldAreas(Elm &elm, uint flag)
  2460. {
  2461. if(elm.type==ELM_WORLD && flag)
  2462. if(WorldVer *world_ver=worldVerGet(elm.id))
  2463. REPA(world_ver->areas)world_ver->rebuildAreaNeighbor(world_ver->areas.lockedKey(i), flag);
  2464. }
  2465. void Project::hmDel(C UID &world_id, C VecI2 &area_xy, C TimeStamp *time)
  2466. {
  2467. if(world_id.valid())
  2468. if(WorldVer *world_ver=worldVerGet(world_id))
  2469. {
  2470. createWorldPaths(world_id);
  2471. world_ver->changed=true;
  2472. world_ver->rebuildArea(area_xy, AREA_SYNC_REMOVED);
  2473. world_ver->areas.get(area_xy)->hm_removed_time=(time ? *time : TimeStamp().getUTC());
  2474. RemoveChunk(editAreaPath(world_id, area_xy), "Heightmap", WorldAreaSync);
  2475. }
  2476. }
  2477. Heightmap* Project::hmGet(C UID &world_id, C VecI2 &area_xy, Heightmap &temp)
  2478. {
  2479. if(world_id.valid())
  2480. {
  2481. if(LoadEditHeightmap(editAreaPath(world_id, area_xy), temp, game_path))return &temp;
  2482. }
  2483. return null;
  2484. }
  2485. uint Project::hmUpdate(C UID &world_id, C VecI2 &area_xy, uint area_sync_flag, C AreaVer &src_area, Heightmap &src_hm)
  2486. {
  2487. if(world_id.valid() && area_sync_flag)
  2488. if(WorldVer *world_ver=worldVerRequire(world_id))
  2489. {
  2490. AreaVer *dest_area=world_ver->areas.get(area_xy);
  2491. Heightmap dest_hm;
  2492. Str name=editAreaPath(world_id, area_xy);
  2493. Chunks chunks; chunks.load(name, WorldAreaSync);
  2494. Chunk *chunk=chunks.findChunk("Heightmap"); if(!chunk)chunk=&chunks.chunks.New();else if(chunk->ver==0)dest_hm.load(File().readMem(chunk->data(), chunk->elms()), game_path);
  2495. if(uint synced=dest_area->sync(src_area, dest_hm, src_hm, area_sync_flag))
  2496. {
  2497. world_ver->changed=true;
  2498. world_ver->rebuildArea(area_xy, synced);
  2499. File temp; dest_hm.save(temp.writeMem(), game_path); temp.pos(0); chunk->create("Heightmap", 0, temp);
  2500. createWorldPaths(world_id);
  2501. chunks.save(name, WorldAreaSync);
  2502. return synced;
  2503. }
  2504. }
  2505. return 0;
  2506. }
  2507. void Project::objGet(C UID &world_id, C VecI2 &area_xy, C Memc<UID> &obj_ids, Memc<ObjData> &objs) // assumes that 'obj_ids' is sorted
  2508. {
  2509. if(world_id.valid())
  2510. {
  2511. Memc<ObjData> file_objs;
  2512. if(LoadEditObject(editAreaPath(world_id, area_xy), file_objs, edit_path)) // load objects from file
  2513. REPA(file_objs) // iterate all file objects
  2514. if(obj_ids.binaryHas(file_objs[i].id, Compare)) // if this is wanted object
  2515. Swap(objs.New(), file_objs[i]); // move to output container (Swap without remove is okay since we're iterating 'file_objs' from the end)
  2516. }
  2517. }
  2518. Heightmap* Project::hmObjGet(C UID &world_id, C VecI2 &area_xy, Heightmap &temp, Memc<ObjData> &objs, bool get_hm, bool get_objs)
  2519. {
  2520. if(world_id.valid())
  2521. {
  2522. if(LoadEdit(editAreaPath(world_id, area_xy), get_hm ? &temp : null, get_objs ? &objs : null, game_path, edit_path))return get_hm ? &temp : null;
  2523. }
  2524. return null;
  2525. }
  2526. bool Project::syncElm(Elm &elm, Elm &src, File &src_data, File &src_extra, bool sync_long, bool &elm_newer_src, bool &src_newer_elm)
  2527. {
  2528. elm_newer_src=false;
  2529. src_newer_elm=false; // later this needs to be set only if we don't have long data and need to get it
  2530. bool has_file=(sync_long || ElmFileInShort(elm.type));
  2531. uint data_changed=elm.syncData(src), file_changed=0;
  2532. Str path=basePath(elm), game_path=gamePath(elm);
  2533. if(elm.type==src.type)switch(elm.type)
  2534. {
  2535. case ELM_MTRL:
  2536. {
  2537. ElmMaterial &mtrl_data=*elm.mtrlData(), &src_mtrl_data=*src.mtrlData();
  2538. if(has_file) // has 'EditMaterial'
  2539. {
  2540. EditMaterial mtrl, src_mtrl; mtrl.load(path); src_mtrl.load(src_data);
  2541. if(file_changed=mtrl.sync(src_mtrl)){mtrl_data.from(mtrl); Save(mtrl, path); makeGameVer(elm);}
  2542. if(mtrl_data.equal(src_mtrl_data) && mtrl.equal(src_mtrl))mtrl_data.ver=src_mtrl_data.ver;else if(data_changed || file_changed)mtrl_data.newVer();
  2543. elm_newer_src=mtrl.newer(src_mtrl);
  2544. //src_newer_elm=src_mtrl.newer(mtrl); no need to set because we already have everything
  2545. }
  2546. }break;
  2547. case ELM_WATER_MTRL:
  2548. {
  2549. ElmWaterMtrl &mtrl_data=*elm.waterMtrlData(), &src_mtrl_data=*src.waterMtrlData();
  2550. if(has_file) // has 'EditWaterMtrl'
  2551. {
  2552. EditWaterMtrl mtrl, src_mtrl; mtrl.load(path); src_mtrl.load(src_data);
  2553. if(file_changed=mtrl.sync(src_mtrl)){mtrl_data.from(mtrl); Save(mtrl, path); makeGameVer(elm);}
  2554. if(mtrl_data.equal(src_mtrl_data) && mtrl.equal(src_mtrl))mtrl_data.ver=src_mtrl_data.ver;else if(data_changed || file_changed)mtrl_data.newVer();
  2555. elm_newer_src=mtrl.newer(src_mtrl);
  2556. //src_newer_elm=src_mtrl.newer(mtrl); no need to set because we already have everything
  2557. }
  2558. }break;
  2559. case ELM_PHYS_MTRL:
  2560. {
  2561. ElmPhysMtrl &mtrl_data=*elm.physMtrlData(), &src_mtrl_data=*src.physMtrlData();
  2562. if(has_file) // has 'EditPhysMtrl'
  2563. {
  2564. EditPhysMtrl mtrl, src_mtrl; mtrl.load(path); src_mtrl.load(src_data);
  2565. if(file_changed=mtrl.sync(src_mtrl)){mtrl_data.from(mtrl); Save(mtrl, path); makeGameVer(elm);}
  2566. if(mtrl_data.equal(src_mtrl_data) && mtrl.equal(src_mtrl))mtrl_data.ver=src_mtrl_data.ver;else if(data_changed || file_changed)mtrl_data.newVer();
  2567. elm_newer_src=mtrl.newer(src_mtrl);
  2568. //src_newer_elm=src_mtrl.newer(mtrl); no need to set because we already have everything
  2569. }
  2570. }break;
  2571. case ELM_IMAGE:
  2572. {
  2573. ElmImage &image_data=*elm.imageData(), &src_image_data=*src.imageData();
  2574. if(has_file && image_data.syncFile(src_image_data)){file_changed=true; SafeOverwrite(src_data, path);} // we're saving to Edit so there's no need to call 'SavedImage'
  2575. if((data_changed&CHANGE_AFFECT_FILE) || file_changed)makeGameVer(elm);
  2576. }break;
  2577. case ELM_IMAGE_ATLAS:
  2578. {
  2579. ElmImageAtlas &image_data=*elm.imageAtlasData(), &src_image_data=*src.imageAtlasData();
  2580. if(has_file && image_data.syncFile(src_image_data)){file_changed=true; if(SafeOverwrite(src_data, path)){SavedImageAtlas(path); savedGame(elm, game_path);}}
  2581. }break;
  2582. case ELM_ICON_SETTS:
  2583. {
  2584. if(has_file) // has 'IconSettings'
  2585. {
  2586. ElmIconSetts &icon_data=*elm.iconSettsData(), &src_icon_data=*src.iconSettsData();
  2587. IconSettings icon, src_icon; icon.load(path); src_icon.load(src_data);
  2588. if(file_changed=icon.sync(src_icon)){icon_data.from(icon); Save(icon, path); makeGameVer(elm);}
  2589. if(icon_data.equal(src_icon_data) && icon.equal(src_icon))icon_data.ver=src_icon_data.ver;else if(data_changed || file_changed)icon_data.newVer();
  2590. elm_newer_src=icon.newer(src_icon);
  2591. //src_newer_elm=src_icon.newer(icon); no need to set because we already have everything
  2592. }
  2593. }break;
  2594. case ELM_ICON:
  2595. {
  2596. if(has_file && elm.iconData()->syncFile(*src.iconData())){file_changed=true; if(SafeOverwrite(src_data, path)){SavedImage(path); savedGame(elm, game_path);}}
  2597. }break;
  2598. case ELM_FONT: // for fonts we first send short: data+edit (ElmFont+EditFont), if EditFont is newer, then we need to request long: data+edit+game
  2599. {
  2600. if(has_file) // has 'EditFont'
  2601. {
  2602. ElmFont &font_data=*elm.fontData(), &src_font_data=*src.fontData();
  2603. EditFont font, src_font; font.load(path); src_font.load(src_data);
  2604. if(sync_long) // has 'Font'
  2605. { // we can synchronize 'edit' only if we have 'game', because both of them are tied together and saved at the same time
  2606. if(file_changed=font.sync(src_font)){font_data.from(font); Save(font, path); if(SafeOverwrite(src_extra, game_path)){SavedFont(game_path); savedGame(elm, game_path);}}
  2607. }else
  2608. {
  2609. src_newer_elm=src_font.newer(font); // check if we need to request long (game)
  2610. }
  2611. if(font_data.equal(src_font_data) && font.equal(src_font))font_data.ver=src_font_data.ver;else if(data_changed || file_changed)font_data.newVer();
  2612. elm_newer_src=font.newer(src_font);
  2613. }
  2614. }break;
  2615. case ELM_PANEL_IMAGE:
  2616. {
  2617. if(has_file) // has 'EditPanelImage'
  2618. {
  2619. ElmPanelImage &panel_image_data=*elm.panelImageData(), &src_panel_image_data=*src.panelImageData();
  2620. EditPanelImage panel_image, src_panel_image; panel_image.load(path); src_panel_image.load(src_data);
  2621. if(sync_long) // has 'PanelImage'
  2622. { // we can synchronize 'edit' only if we have 'game', because both of them are tied together and saved at the same time
  2623. if(file_changed=panel_image.sync(src_panel_image)){panel_image_data.from(panel_image); Save(panel_image, path); if(SafeOverwrite(src_extra, game_path)){SavedPanelImage(game_path); savedGame(elm, game_path);}}
  2624. }else
  2625. {
  2626. src_newer_elm=src_panel_image.newer(panel_image); // check if we need to request long (game)
  2627. }
  2628. if(panel_image_data.equal(src_panel_image_data) && panel_image.equal(src_panel_image))panel_image_data.ver=src_panel_image_data.ver;else if(data_changed || file_changed)panel_image_data.newVer();
  2629. elm_newer_src=panel_image.newer(src_panel_image);
  2630. }
  2631. }break;
  2632. case ELM_TEXT_STYLE:
  2633. {
  2634. if(has_file) // has 'EditTextStyle'
  2635. {
  2636. ElmTextStyle &ts_data=*elm.textStyleData(), &src_ts_data=*src.textStyleData();
  2637. EditTextStyle ts, src_ts; ts.load(path); src_ts.load(src_data);
  2638. if(file_changed=ts.sync(src_ts)){ts_data.from(ts); Save(ts, path); makeGameVer(elm);}
  2639. if(ts_data.equal(src_ts_data) && ts.equal(src_ts))ts_data.ver=src_ts_data.ver;else if(data_changed || file_changed)ts_data.newVer();
  2640. elm_newer_src=ts.newer(src_ts);
  2641. //src_newer_elm=src_ts.newer(ts); no need to set because we already have everything
  2642. }
  2643. }break;
  2644. case ELM_PANEL:
  2645. {
  2646. if(has_file) // has 'EditPanel'
  2647. {
  2648. ElmPanel &panel_data=*elm.panelData(), &src_panel_data=*src.panelData();
  2649. EditPanel panel, src_panel; panel.load(path); src_panel.load(src_data);
  2650. if(file_changed=panel.sync(src_panel)){panel_data.from(panel); Save(panel, path); makeGameVer(elm);}
  2651. if(panel_data.equal(src_panel_data) && panel.equal(src_panel))panel_data.ver=src_panel_data.ver;else if(data_changed || file_changed)panel_data.newVer();
  2652. elm_newer_src=panel.newer(src_panel);
  2653. //src_newer_elm=src_panel.newer(panel); no need to set because we already have everything
  2654. }
  2655. }break;
  2656. case ELM_GUI_SKIN:
  2657. {
  2658. if(has_file) // has 'EditGuiSkin'
  2659. {
  2660. ElmGuiSkin &gui_skin_data=*elm.guiSkinData(), &src_gui_skin_data=*src.guiSkinData();
  2661. EditGuiSkin gui_skin, src_gui_skin; gui_skin.load(path); src_gui_skin.load(src_data);
  2662. if(file_changed=gui_skin.sync(src_gui_skin)){gui_skin_data.from(gui_skin); Save(gui_skin, path); makeGameVer(elm);}
  2663. if(gui_skin_data.equal(src_gui_skin_data) && gui_skin.equal(src_gui_skin))gui_skin_data.ver=src_gui_skin_data.ver;else if(data_changed || file_changed)gui_skin_data.newVer();
  2664. elm_newer_src=gui_skin.newer(src_gui_skin);
  2665. //src_newer_elm=src_gui_skin.newer(gui_skin); no need to set because we already have everything
  2666. }
  2667. }break;
  2668. case ELM_ENV:
  2669. {
  2670. if(has_file) // has 'EditEnv'
  2671. {
  2672. ElmEnv &env_data=*elm.envData(), &src_env_data=*src.envData();
  2673. EditEnv env, src_env; env.load(path); src_env.load(src_data);
  2674. if(file_changed=env.sync(src_env)){env_data.from(env); Save(env, path); makeGameVer(elm);}
  2675. if(env_data.equal(src_env_data) && env.equal(src_env))env_data.ver=src_env_data.ver;else if(data_changed || file_changed)env_data.newVer();
  2676. elm_newer_src=env.newer(src_env);
  2677. //src_newer_elm=src_env.newer(env); no need to set because we already have everything
  2678. }
  2679. }break;
  2680. case ELM_MESH:
  2681. {
  2682. ElmMesh &mesh_data=*elm.meshData(), &src_mesh_data=*src.meshData();
  2683. Elm *rebuild=null;
  2684. if(has_file && mesh_data.syncFile(src_mesh_data))
  2685. {
  2686. if(!IsServer) // check if mesh existence has changed for possible 'makeGameVer' of object which depends on mesh existence, this doesn't need to be done on the server
  2687. {
  2688. Mesh original, updated;
  2689. Load(original, path, T.game_path); updated.load(src_data, T.game_path); src_data.pos(0);
  2690. if(OverrideMeshSkel(&original, null)!=OverrideMeshSkel(&updated, null))rebuild=meshToObjElm(&elm); // set object of this mesh to be rebuilt
  2691. }
  2692. file_changed=true; if(SafeOverwrite(src_data, path))SavedMesh(path);
  2693. }
  2694. if((data_changed&CHANGE_AFFECT_FILE) || file_changed){src_data.pos(0); makeGameVer(elm, has_file ? &src_data : null);}
  2695. if(rebuild)makeGameVer(*rebuild); // rebuild after 'makeGameVer' of the mesh
  2696. }break;
  2697. case ELM_SKEL:
  2698. {
  2699. ElmSkel &skel_data=*elm.skelData(), &src_skel_data=*src.skelData();
  2700. if(has_file && skel_data.syncFile(src_skel_data))
  2701. {
  2702. Elm *rebuild=null; if(!IsServer) // check if skeleton existence has changed for possible 'makeGameVer' of object which depends on skeleton existence, this doesn't need to be done on the server
  2703. {
  2704. Skeleton original, updated;
  2705. original.load(game_path); updated.load(src_extra); src_extra.pos(0);
  2706. if(OverrideMeshSkel(null, &original)!=OverrideMeshSkel(null, &updated))rebuild=skelToObjElm(&elm); // set object of this skeleton to be rebuilt
  2707. }
  2708. file_changed=true; if(SafeOverwrite(src_data, path))SavedEditSkel(path);
  2709. if(SafeOverwrite(src_extra, game_path))
  2710. {
  2711. SavedSkel(game_path); savedGame(elm, game_path);
  2712. if(rebuild)makeGameVer(*rebuild); // rebuild after saving the skeleton
  2713. }
  2714. }
  2715. }break;
  2716. case ELM_PHYS:
  2717. {
  2718. ElmPhys &phys_data=*elm.physData(), &src_phys_data=*src.physData();
  2719. if(has_file && phys_data.syncFile(src_phys_data))
  2720. {
  2721. Elm *rebuild=null; if(!IsServer) // check if phys body existence has changed for possible 'makeGameVer' of object which depends on phys body existence, this doesn't need to be done on the server
  2722. {
  2723. PhysBody original, updated;
  2724. original.load(path); updated.load(src_data, T.game_path); src_data.pos(0);
  2725. if(OverridePhys(&original)!=OverridePhys(&updated))rebuild=physToObjElm(&elm); // set object of this phys body to be rebuilt
  2726. }
  2727. file_changed=true; if(SafeOverwrite(src_data, path))
  2728. {
  2729. SavedPhys(path); savedGame(elm, game_path);
  2730. if(rebuild)makeGameVer(*rebuild); // rebuild after saving the phys body
  2731. }
  2732. }
  2733. }break;
  2734. case ELM_ANIM:
  2735. {
  2736. ElmAnim &anim_data=*elm.animData(), &src_anim_data=*src.animData();
  2737. if(has_file && anim_data.syncFile(src_anim_data)){file_changed=true; /*anim_data.from(anim);*/ if(SafeOverwrite(src_data, path)){SavedAnim(path); savedGame(elm, game_path);}}
  2738. }break;
  2739. case ELM_WORLD:
  2740. {
  2741. if(data_changed&CHANGE_AFFECT_FILE)makeGameVer(elm);
  2742. }break;
  2743. case ELM_MINI_MAP:
  2744. {
  2745. }break;
  2746. case ELM_ENUM:
  2747. {
  2748. ElmEnum &enum_data=*elm.enumData(), &src_enum_data=*src.enumData();
  2749. EditEnums enums, src_enums; enums.load(path); src_enums.load(src_data);
  2750. if(file_changed=enums.sync(src_enums)){enum_data.from(enums); Save(enums, path); makeGameVer(elm);}
  2751. if(enum_data.equal(src_enum_data) && enums.equal(src_enums))enum_data.ver=src_enum_data.ver;else if(data_changed || file_changed)enum_data.newVer();
  2752. elm_newer_src=enums.newer(src_enums);
  2753. //src_newer_elm=src_enums.newer(enums); no need to set because we already have everything
  2754. }break;
  2755. case ELM_OBJ:
  2756. {
  2757. ElmObj &obj_data=*elm.objData(), &src_obj_data=*src.objData();
  2758. EditObject params, src_params; if(IsServer)Load(params, path, edit_path);else params=*EditObjectPtr(path); src_params.load(src_data, edit_path); // 'EditObject' must use 'edit_path', on client load from cache in case we're editing the file
  2759. if(file_changed=params.sync(src_params, edit_path)){obj_data.from(params); Save(params, path, edit_path);} // 'EditObject' must use 'edit_path'
  2760. if((data_changed&CHANGE_AFFECT_FILE) || file_changed)makeGameVer(elm);
  2761. if(obj_data.equal(src_obj_data) && params.equal(src_params))obj_data.ver=src_obj_data.ver;else if(data_changed || file_changed)obj_data.newVer();
  2762. elm_newer_src=params.newer(src_params);
  2763. //src_newer_elm=src_params.newer(params); no need to set because we already have everything
  2764. }break;
  2765. case ELM_OBJ_CLASS:
  2766. {
  2767. ElmObjClass &obj_data=*elm.objClassData(), &src_obj_data=*src.objClassData();
  2768. EditObject params, src_params; if(IsServer)Load(params, path, edit_path);else params=*EditObjectPtr(path); src_params.load(src_data, edit_path); // 'EditObject' must use 'edit_path', on client load from cache in case we're editing the file
  2769. if(file_changed=params.sync(src_params, edit_path)){obj_data.from(params); Save(params, path, edit_path);} // 'EditObject' must use 'edit_path'
  2770. if((data_changed&CHANGE_AFFECT_FILE) || file_changed)makeGameVer(elm);
  2771. if(obj_data.equal(src_obj_data) && params.equal(src_params))obj_data.ver=src_obj_data.ver;else if(data_changed || file_changed)obj_data.newVer();
  2772. elm_newer_src=params.newer(src_params);
  2773. //src_newer_elm=src_params.newer(params); no need to set because we already have everything
  2774. }break;
  2775. case ELM_GUI:
  2776. {
  2777. if(has_file && elm.guiData()->syncFile(*src.guiData())){file_changed=true; SafeOverwrite(src_data, path);}
  2778. }break;
  2779. case ELM_SOUND:
  2780. {
  2781. if(has_file && elm.soundData()->syncFile(*src.soundData())){file_changed=true; SafeOverwrite(src_data, path);}
  2782. }break;
  2783. case ELM_VIDEO:
  2784. {
  2785. if(has_file && elm.videoData()->syncFile(*src.videoData())){file_changed=true; SafeOverwrite(src_data, path);}
  2786. }break;
  2787. case ELM_FILE:
  2788. {
  2789. if(has_file && elm.fileData()->syncFile(*src.fileData())){file_changed=true; SafeOverwrite(src_data, path);}
  2790. }break;
  2791. //case ELM_CODE: break; // this is synchronized manually elsewhere
  2792. //case ELM_APP : break; // this is synchronized manually elsewhere
  2793. }
  2794. return data_changed || file_changed;
  2795. }
  2796. uint Project::syncArea(C UID &world_id, C VecI2 &area_xy, uint area_sync_flag, C AreaVer &src_area, Heightmap &src_hm, Memc<ObjData> &src_objs, Memc<UID> *local_objs_newer)
  2797. {
  2798. if(world_id.valid())
  2799. if(WorldVer *world_ver=worldVerRequire(world_id))
  2800. if(AreaVer *dest_area=world_ver->areas.get(area_xy))
  2801. {
  2802. createWorldPaths(world_id);
  2803. if(area_sync_flag&AREA_SYNC_OBJ) // do this before heightmap sync because those use 'return'
  2804. {
  2805. if(syncObj(world_id, area_xy, src_objs, null, local_objs_newer)){dest_area->obj_ver=src_area.obj_ver; world_ver->changed=true;} // if after syncing objects they are in equal state, then set same 'obj_ver' as the source
  2806. }
  2807. if((area_sync_flag&AREA_SYNC_REMOVED) && dest_area->hasHm() && !src_area.hasHm() && !AreaVer::HasHm(src_area.hm_removed_time, dest_area->hm_height_time)) // dest has heightmap, and shouldn't have after syncing
  2808. {
  2809. hmDel(world_id, area_xy, &src_area.hm_removed_time); return AREA_SYNC_REMOVED;
  2810. }
  2811. if(dest_area->hasHm() || AreaVer::HasHm(dest_area->hm_removed_time, src_area.hm_height_time)) // dest has heightmap or it will have after syncing
  2812. {
  2813. uint do_sync_flag=(((area_sync_flag&AREA_SYNC_HEIGHT) && src_area.hm_height_time>dest_area->hm_height_time) ? AREA_SYNC_HEIGHT : 0)
  2814. |(((area_sync_flag&AREA_SYNC_MTRL ) && src_area. hm_mtrl_time>dest_area-> hm_mtrl_time) ? AREA_SYNC_MTRL : 0)
  2815. |(((area_sync_flag&AREA_SYNC_COLOR ) && src_area. hm_color_time>dest_area-> hm_color_time) ? AREA_SYNC_COLOR : 0);
  2816. if(do_sync_flag)return hmUpdate(world_id, area_xy, area_sync_flag, src_area, src_hm); // if any element is newer
  2817. }
  2818. }
  2819. return 0;
  2820. }
  2821. Project::AreaSyncObjData::AreaSyncObjData(Project &proj, C UID &world_id, WorldVer &world_ver) : proj(proj), world_id(world_id), world_ver(world_ver) {}
  2822. Project::AreaSyncObj::~AreaSyncObj()
  2823. {
  2824. if(changed)
  2825. {
  2826. area_ver->obj_ver.randomize();
  2827. SaveEditObject(chunks, objs, edit_path ); chunks.save(chunk_edit_path, WorldAreaSync); // save edit
  2828. if(!IsServer){chunks.load(chunk_game_path, WorldAreaSync); SaveGameObject(chunks, objs, *project, *world_ver); chunks.save(chunk_game_path, WorldAreaSync);} // save game
  2829. }
  2830. }
  2831. bool Project::AreaSyncObj::Create(AreaSyncObj &area, C VecI2 &area_xy, ptr asod_ptr)
  2832. {
  2833. AreaSyncObjData &asod=*(AreaSyncObjData*)asod_ptr;
  2834. area.xy =area_xy;
  2835. area.world_ver =&asod.world_ver;
  2836. area.area_ver =asod.world_ver.areas.get(area_xy);
  2837. area.project =&asod.proj;
  2838. area.chunk_edit_path=asod.proj.editAreaPath(asod.world_id, area_xy);
  2839. area.chunk_game_path=asod.proj.gameAreaPath(asod.world_id, area_xy);
  2840. area.game_path =asod.proj.game_path;
  2841. area.edit_path =asod.proj.edit_path;
  2842. area.chunks.load(area.chunk_edit_path, WorldAreaSync);
  2843. if(Chunk *chunk=area.chunks.findChunk("Object"))LoadEditObject(chunk->ver, File().readMem(chunk->data(), chunk->elms()), area.objs, area.edit_path); // load edit objects
  2844. return true;
  2845. }
  2846. void Project::rebuildEmbedObj(C UID &world_obj_instance_id, C VecI2 &area_xy, WorldVer &world_ver, bool rebuild_game_area_objs) // this must be called after 'changedObj'
  2847. {
  2848. if(!IsServer) // this doesn't need to be performed on the server
  2849. if(Elm *world=findElm(world_ver.world_id))
  2850. if(ElmWorld *world_data=world->worldData())
  2851. if(world_data->valid())
  2852. if(ObjVer *obj_ver=world_ver.obj.find(world_obj_instance_id)) // get obj ver
  2853. {
  2854. RectI *is_embed=world_ver.obj_embed.find(world_obj_instance_id); // if already is embedded
  2855. bool want_embed=false; RectI want_embed_rect; Box box;
  2856. if(!obj_ver->removed() && obj_ver->terrain(T) && getObjBox(obj_ver->elm_obj_id, box)) // if can be embedded
  2857. {
  2858. box*=obj_ver->matrix();
  2859. flt area_size=world_data->area_size;
  2860. if(EmbedObject(box, area_xy, area_size)) // if should be embedded
  2861. {
  2862. want_embed=true; want_embed_rect.set(Floor(box.min.xz()/area_size), Floor(box.max.xz()/area_size));
  2863. }
  2864. }
  2865. // rebuild if needed
  2866. if( is_embed)world_ver.rebuildEmbedObj( *is_embed ); // rebuild old areas
  2867. if(want_embed)world_ver.rebuildEmbedObj(want_embed_rect); // rebuild new areas
  2868. if(rebuild_game_area_objs && (is_embed!=null)!=want_embed)world_ver.rebuildGameAreaObjs(area_xy); // if changing embedded state then we need to rebuild game area objects (add or remove the object from area obj list)
  2869. // setup in world ver (do this after using 'is_embed')
  2870. {
  2871. MapLock ml(world_ver.obj_embed); // lock before changing 'obj_embed'
  2872. if(want_embed)*world_ver.obj_embed(world_obj_instance_id)=want_embed_rect;else world_ver.obj_embed.removeKey(world_obj_instance_id); world_ver.changed=true;
  2873. }
  2874. }
  2875. }
  2876. void Project::rebuildWater(Lake *lake, River *river, C UID &water_id, WorldVer &world_ver)
  2877. {
  2878. if(!IsServer) // this doesn't need to be performed on the server
  2879. if(lake || river)
  2880. if(Elm *world=findElm(world_ver.world_id))
  2881. if(ElmWorld *world_data=world->worldData())
  2882. if(world_data->valid())
  2883. if(WaterVer *water_ver=(lake ? world_ver.lakes.find(water_id) : world_ver.rivers.find(water_id)))
  2884. {
  2885. flt area_size=world_data->area_size;
  2886. RectI old_area=water_ver ->areas,
  2887. new_area(0, -1);
  2888. Rect rect; if(lake ? (!lake->removed && lake->getRect(rect)) : (!river->removed && river->getRect(rect)))new_area.set(Floor(rect.min/area_size), Floor(rect.max/area_size));
  2889. // rebuild if needed
  2890. if(old_area.valid())world_ver.rebuildWater(old_area);
  2891. if(new_area.valid())world_ver.rebuildWater(new_area);
  2892. // setup in world ver
  2893. if(lake ){MapLock ml(world_ver.lakes ); water_ver->areas=new_area; world_ver.changed=true;} // lock before changing 'world_ver'
  2894. if(river){MapLock ml(world_ver.rivers); water_ver->areas=new_area; world_ver.changed=true;} // lock before changing 'world_ver'
  2895. }
  2896. }
  2897. bool Project::syncObj(C UID &world_id, C VecI2 &area_xy, Memc<ObjData> &objs, Map<VecI2, Memc<ObjData> > *obj_modified, Memc<UID> *local_newer)
  2898. {
  2899. if(world_id.valid())
  2900. if(WorldVer *world_ver=worldVerRequire(world_id))
  2901. {
  2902. AreaSyncObjData asod(T, world_id, *world_ver);
  2903. Map<VecI2, AreaSyncObj> areas(Compare, AreaSyncObj::Create, &asod);
  2904. AreaSyncObj *target_area=areas(area_xy);
  2905. REPA(objs)
  2906. {
  2907. ObjData &obj=objs[i];
  2908. ObjVer *obj_ver=world_ver->obj.find(obj.id); // use 'find' to get null if not found
  2909. if(!obj_ver) // not present in this world, then add to target area
  2910. {
  2911. if(obj_modified)(*obj_modified)(target_area->xy)->New()=obj;
  2912. target_area->changed=true; world_ver->changed=true; createWorldPaths(world_id);
  2913. world_ver->changedObj(obj, target_area->xy); // call before 'rebuildEmbedObj'
  2914. target_area->objs.New()=obj;
  2915. if(!IsServer )rebuildEmbedObj(obj.id, target_area->xy, *world_ver, false); // call after 'changedObj' (doesn't need to be performed on the server), 'rebuild_game_area_objs=false' because we're saving game area here anyway
  2916. if(!IsServer && obj.physPath())world_ver->rebuildPaths(obj.id, target_area->xy); // don't check for 'physPath' on server because loading 'Object' may fail (doesn't need to be performed on the server)
  2917. }else // present in some area
  2918. if(AreaSyncObj *cur_area=areas(obj_ver->area_xy)) // load that area
  2919. REPA(cur_area->objs)if(cur_area->objs[i].id==obj.id) // found that object
  2920. {
  2921. ObjData &cur_obj =cur_area->objs[i];
  2922. TimeStamp old_matrix_time=cur_obj.matrix_time ; // remember old matrix time before syncing
  2923. TerrainObj old_terrain =cur_obj.terrainObj(); // remember old terrain obj before syncing
  2924. PhysPath old_phys; if(!IsServer)old_phys=cur_obj.physPath(); // don't check for 'physPath' on server because loading 'Object' may fail (doesn't need to be performed on the server)
  2925. if(cur_obj.sync(obj, edit_path)) // if performed any change
  2926. {
  2927. bool changed_matrix =(cur_obj.matrix_time>old_matrix_time),
  2928. changed_embed =false; if(!IsServer)changed_embed =(cur_obj.terrainObj()!=old_terrain || changed_matrix); // if changed terrain or changed matrix (doesn't need to be performed on the server)
  2929. bool changed_phys_path=false; if(!IsServer)changed_phys_path=(cur_obj.physPath ()!=old_phys || (changed_matrix && (old_phys || cur_obj.physPath()))); // if changed phys, or if changed matrix and have phys (doesn't need to be performed on the server)
  2930. AreaSyncObj &new_area=(changed_matrix ? *target_area : *cur_area); // check if received newer position
  2931. if(obj_modified)(*obj_modified)(new_area.xy)->New()=cur_obj;
  2932. cur_area->changed=true; world_ver->changed=true; createWorldPaths(world_id);
  2933. world_ver->changedObj(cur_obj, new_area.xy); // call before 'rebuildEmbedObj'
  2934. if(changed_embed )rebuildEmbedObj(cur_obj.id, new_area.xy, *world_ver, false); // call after 'changedObj' and before 'rebuildPaths', 'rebuild_game_area_objs=false' because we're saving game area here anyway
  2935. if(changed_phys_path)world_ver->rebuildPaths(obj.id, cur_area->xy); // call after 'rebuildEmbedObj' for old area
  2936. if(cur_area!=target_area && changed_matrix) // we're moving to different area
  2937. {
  2938. target_area->changed=true;
  2939. if(changed_phys_path)world_ver->rebuildPaths(obj.id, target_area->xy); // call after 'rebuildEmbedObj' for new area
  2940. Swap(target_area->objs.New(), cur_obj); cur_area->objs.remove(i); // move to new area and remove from old area
  2941. }
  2942. }
  2943. break;
  2944. }
  2945. }
  2946. if(local_newer)GetNewer(target_area->objs, objs, *local_newer);
  2947. return Same(target_area->objs, objs);
  2948. }
  2949. return false;
  2950. }
  2951. bool Project::syncWaypoint(C UID &world_id, C UID &waypoint_id, Version &src_ver, EditWaypoint &src) // this should modify 'src_ver' and 'src' according to final data, because Server CS_SET_WORLD_WAYPOINT relies on that
  2952. {
  2953. if(world_id.valid())
  2954. if(WorldVer *world_ver =worldVerRequire(world_id))
  2955. if(Version *waypoint_ver=world_ver->waypoints.get(waypoint_id))
  2956. if(src_ver!=*waypoint_ver)
  2957. {
  2958. Str edit=editWaypointPath(world_id, waypoint_id);
  2959. EditWaypoint waypoint; waypoint.load(edit);
  2960. bool changed=waypoint.sync(src);
  2961. if( changed)
  2962. {
  2963. createWorldPaths(world_id);
  2964. Save(waypoint, edit); // save edit
  2965. if(!IsServer){Game::Waypoint w; Str game=gameWaypointPath(world_id, waypoint_id); if(waypoint.copyTo(w))Save(w, game);else FDelFile(game);} // make game
  2966. }
  2967. if(waypoint.equal(src)){*waypoint_ver=src_ver; world_ver->changed=true;}else if(changed){waypoint_ver->randomize(); world_ver->changed=true;}
  2968. // set output because of comment at start of function
  2969. src_ver=*waypoint_ver;
  2970. src = waypoint;
  2971. return changed;
  2972. }
  2973. return false;
  2974. }
  2975. bool Project::syncLake(C UID &world_id, C UID &lake_id, Version &src_ver, Lake &src) // this should modify 'src_ver' and 'src' according to final data, because Server CS_SET_WORLD_WAYPOINT relies on that
  2976. {
  2977. if(world_id.valid())
  2978. if(WorldVer *world_ver=worldVerRequire(world_id))
  2979. if(WaterVer *lake_ver =world_ver->lakes.get(lake_id))
  2980. if(src_ver!=lake_ver->ver)
  2981. {
  2982. Str edit=editLakePath(world_id, lake_id);
  2983. Lake lake; lake.load(edit);
  2984. bool changed=lake.sync(src);
  2985. if( changed)
  2986. {
  2987. createWorldPaths(world_id);
  2988. Save(lake, edit); // save edit
  2989. rebuildWater(&lake, null, lake_id, *world_ver);
  2990. }
  2991. if(lake.equal(src)){lake_ver->ver=src_ver; world_ver->changed=true;}else if(changed){lake_ver->ver.randomize(); world_ver->changed=true;}
  2992. // set output because of comment at start of function
  2993. src_ver=lake_ver->ver;
  2994. src =lake;
  2995. return changed;
  2996. }
  2997. return false;
  2998. }
  2999. bool Project::syncRiver(C UID &world_id, C UID &river_id, Version &src_ver, River &src) // this should modify 'src_ver' and 'src' according to final data, because Server CS_SET_WORLD_WAYPOINT relies on that
  3000. {
  3001. if(world_id.valid())
  3002. if(WorldVer *world_ver=worldVerRequire(world_id))
  3003. if(WaterVer *river_ver=world_ver->rivers.get(river_id))
  3004. if(src_ver!=river_ver->ver)
  3005. {
  3006. Str edit=editRiverPath(world_id, river_id);
  3007. River river; river.load(edit);
  3008. bool changed=river.sync(src);
  3009. if( changed)
  3010. {
  3011. createWorldPaths(world_id);
  3012. Save(river, edit); // save edit
  3013. rebuildWater(null, &river, river_id, *world_ver);
  3014. }
  3015. if(river.equal(src)){river_ver->ver=src_ver; world_ver->changed=true;}else if(changed){river_ver->ver.randomize(); world_ver->changed=true;}
  3016. // set output because of comment at start of function
  3017. src_ver=river_ver->ver;
  3018. src =river;
  3019. return changed;
  3020. }
  3021. return false;
  3022. }
  3023. bool Project::syncMiniMapSettings(C UID &mini_map_id, C Game::MiniMap::Settings &settings, C TimeStamp &settings_time)
  3024. {
  3025. if(mini_map_id.valid())
  3026. if(MiniMapVer *mini_map_ver=miniMapVerRequire(mini_map_id))
  3027. if(settings_time>mini_map_ver->time)
  3028. {
  3029. FDelInside(gamePath(mini_map_id));
  3030. createMiniMapPaths(mini_map_id);
  3031. mini_map_ver->time =settings_time;
  3032. mini_map_ver->settings=settings; settings.save(gamePath(mini_map_id).tailSlash(true)+"Settings");
  3033. mini_map_ver->images.clear();
  3034. mini_map_ver->changed=true;
  3035. return true;
  3036. }
  3037. return false;
  3038. }
  3039. bool Project::syncMiniMapImage(C UID &mini_map_id, C VecI2 &image_xy, C TimeStamp &image_time, File &image_data)
  3040. {
  3041. if(mini_map_id.valid())
  3042. if(MiniMapVer *mini_map_ver=miniMapVerRequire(mini_map_id))
  3043. if(image_time==mini_map_ver->time)
  3044. {
  3045. Str image_name=gamePath(mini_map_id).tailSlash(true)+image_xy;
  3046. if( image_data.is()) // if image data exists then save it
  3047. {
  3048. image_data.pos(0); if(SafeOverwrite(image_data, image_name))if(mini_map_ver->images.binaryInclude(image_xy, Compare))mini_map_ver->changed=true;
  3049. }else // otherwise delete it
  3050. {
  3051. FDelFile(image_name); if(mini_map_ver->images.binaryExclude(image_xy, Compare))mini_map_ver->changed=true;
  3052. }
  3053. return true;
  3054. }
  3055. return false;
  3056. }
  3057. bool Project::newerSettings(C Project &src)C
  3058. {
  3059. return cipher_time>src.cipher_time || cipher_key_time>src.cipher_key_time || compress_type_time>src.compress_type_time
  3060. || compress_level_time>src.compress_level_time || material_simplify_time>src.material_simplify_time;
  3061. }
  3062. bool Project::oldSettings(C TimeStamp &now)C
  3063. {
  3064. return cipher_time<now && cipher_key_time<now && compress_type_time<now && compress_level_time<now && material_simplify_time<now;
  3065. }
  3066. bool Project::syncSettings(C Project &src)
  3067. {
  3068. bool changed=false;
  3069. changed|=Sync(cipher_time , src.cipher_time , cipher , src.cipher );
  3070. changed|=Sync(compress_type_time , src.compress_type_time , compress_type , src.compress_type );
  3071. changed|=Sync(compress_level_time , src.compress_level_time , compress_level , src.compress_level );
  3072. changed|=Sync(material_simplify_time, src.material_simplify_time, material_simplify, src.material_simplify);
  3073. if(Sync(cipher_key_time, src.cipher_key_time)){changed=true; Copy(cipher_key, src.cipher_key);}
  3074. return changed;
  3075. }
  3076. void Project::initSettings(C Project &src) // this is called when finished copying elements to an empty project (for example after importing *.EsenthelProject file)
  3077. {
  3078. syncSettings(src);
  3079. app_id=src.app_id;
  3080. hm_mtrl_id= src. hm_mtrl_id;
  3081. water_mtrl_id= src.water_mtrl_id;
  3082. Copy(mtrl_brush_id, src.mtrl_brush_id, SIZE(mtrl_brush_id));
  3083. }
  3084. void Project::flush(SAVE_MODE save_mode)
  3085. {
  3086. {CacheLock cl( world_vers); REPA( world_vers) world_vers.lockedData(i).flush();}
  3087. {CacheLock cl(mini_map_vers); REPA(mini_map_vers)mini_map_vers.lockedData(i).flush();}
  3088. }
  3089. bool Project::loadOldSettings(File &f)
  3090. {
  3091. if(f.getUInt()==CC4('P', 'R', 'S', 'T'))
  3092. {
  3093. UID proj_id; byte encrypt_key[32];
  3094. switch(f.decUIntV())
  3095. {
  3096. default: goto error;
  3097. case 3:
  3098. {
  3099. f>>proj_id; GetStr2(f, name); f>>cipher>>encrypt_key>>compress_type>>compress_level>>material_simplify
  3100. >>cipher_time>>cipher_key_time>>compress_type_time>>compress_level_time>>material_simplify_time;
  3101. f>>app_id>>hm_mtrl_id>>water_mtrl_id; FREPA(mtrl_brush_id)f>>mtrl_brush_id[i];
  3102. }break;
  3103. case 2:
  3104. {
  3105. byte max_tex_size; TimeStamp max_tex_size_time;
  3106. f>>proj_id; GetStr2(f, name); f>>cipher>>encrypt_key>>compress_type>>compress_level>>material_simplify>>max_tex_size
  3107. >>cipher_time>>cipher_key_time>>compress_type_time>>compress_level_time>>material_simplify_time>>max_tex_size_time;
  3108. f>>app_id>>hm_mtrl_id>>water_mtrl_id; FREPA(mtrl_brush_id)f>>mtrl_brush_id[i];
  3109. }break;
  3110. case 1:
  3111. {
  3112. f>>proj_id; GetStr(f, name);
  3113. f>>cipher>>encrypt_key>>compress_type>>compress_level>>material_simplify>>cipher_time>>cipher_key_time>>compress_type_time>>compress_level_time>>material_simplify_time;
  3114. f>>app_id>>hm_mtrl_id>>water_mtrl_id; FREPA(mtrl_brush_id)f>>mtrl_brush_id[i];
  3115. }break;
  3116. case 0:
  3117. {
  3118. f>>proj_id; GetStr(f, name);
  3119. f>>cipher>>encrypt_key>>compress_type>>compress_level>>cipher_time>>cipher_key_time>>compress_type_time>>compress_level_time;
  3120. f>>app_id>>hm_mtrl_id>>water_mtrl_id; FREPA(mtrl_brush_id)f>>mtrl_brush_id[i];
  3121. }break;
  3122. }
  3123. if(f.ok())return true;
  3124. }
  3125. error:
  3126. return false;
  3127. }
  3128. bool Project::loadOldSettings(C Str &name) {File f; return f.readTry(name) && loadOldSettings(f);}
  3129. bool Project::loadOldSettings2(C Str &name) {return loadOldSettings(name) || loadOldSettings(name+".old");}
  3130. bool Project::save(File &f, bool network, SAVE_DATA mode)C
  3131. {
  3132. f.putUInt(CC4_PRDT);
  3133. // short header first, which will be used when loading project list (only ID and name)
  3134. f.cmpUIntV(4); // version
  3135. if(network)f<<id; // ID needed only for network, on local it is obtained from folder name
  3136. f<<name;
  3137. if(!network)f<<synchronize;
  3138. if(mode>=SAVE_SETTINGS)
  3139. {
  3140. // 'ProjectVersion'
  3141. f.cmpUIntV(ProjectVersion);
  3142. // settings
  3143. f.cmpUIntV(0); // version
  3144. f<<cipher<<cipher_key<<compress_type<<compress_level<<material_simplify
  3145. <<cipher_time<<cipher_key_time<<compress_type_time<<compress_level_time<<material_simplify_time;
  3146. if(!network){f<<text_data<<app_id<<hm_mtrl_id<<water_mtrl_id; FREPA(mtrl_brush_id)f<<mtrl_brush_id[i];}
  3147. if(mode>=SAVE_ALL)
  3148. {
  3149. // data
  3150. f.cmpUIntV(0); // version
  3151. f.cmpUIntV(elms.elms()); FREPAO(elms).save(f, network, network);
  3152. f.cmpUIntV(texs.elms()); FREPA (texs)f<<texs[i];
  3153. // update
  3154. if(!network) // not sent over network, because projects are first updated locally fully, and sent once everything finished
  3155. {
  3156. f.cmpUIntV(texs_update.elms()); FREPA(texs_update)f<<texs_update[i];
  3157. }
  3158. }
  3159. }
  3160. return f.ok();
  3161. }
  3162. LOAD_RESULT Project::load(File &f, int &ver, bool network, SAVE_DATA mode)
  3163. {
  3164. ver=-1;
  3165. del();
  3166. if(!f.left ())return LOAD_EMPTY;
  3167. if( f.getUInt()!=CC4_PRDT)goto error;
  3168. switch(f.decUIntV())
  3169. {
  3170. default: goto newer;
  3171. case 4:
  3172. {
  3173. if(network)f>>id;
  3174. f>>name;
  3175. if(!network)f>>synchronize;
  3176. if(mode>=SAVE_SETTINGS)
  3177. {
  3178. // 'ProjectVersion'
  3179. ver=f.decUIntV(); if(ver>ProjectVersion)goto newer; // if project requires a newer version then don't load it
  3180. // settings
  3181. switch(f.decUIntV())
  3182. {
  3183. default: goto newer;
  3184. case 0:
  3185. {
  3186. f>>cipher>>cipher_key>>compress_type>>compress_level>>material_simplify
  3187. >>cipher_time>>cipher_key_time>>compress_type_time>>compress_level_time>>material_simplify_time;
  3188. if(!network){f>>text_data>>app_id>>hm_mtrl_id>>water_mtrl_id; FREPA(mtrl_brush_id)f>>mtrl_brush_id[i];}
  3189. }break;
  3190. }
  3191. // data
  3192. if(mode>=SAVE_ALL)
  3193. switch(f.decUIntV())
  3194. {
  3195. default: goto newer;
  3196. case 0:
  3197. {
  3198. elms.setNum(f.decUIntV()); FREPA(elms)if(!elms[i].load(f, network, network)){if(f.ok())goto newer; goto error;}
  3199. texs.setNum(f.decUIntV()); FREPA(texs)f>>texs[i];
  3200. // update
  3201. if(!network){texs_update.setNum(f.decUIntV()); FREPA(texs_update)f>>texs_update[i];}
  3202. }break;
  3203. }
  3204. }
  3205. }break;
  3206. case 3:
  3207. {
  3208. if(network)f>>id;
  3209. GetStr2(f, name);
  3210. if(!network)f>>synchronize;
  3211. if(mode>=SAVE_SETTINGS)
  3212. {
  3213. // 'ProjectVersion'
  3214. ver=f.decUIntV(); if(ver>ProjectVersion)goto newer; // if project requires a newer version then don't load it
  3215. // settings
  3216. switch(f.decUIntV())
  3217. {
  3218. default: goto newer;
  3219. case 0:
  3220. {
  3221. f>>cipher>>cipher_key>>compress_type>>compress_level>>material_simplify
  3222. >>cipher_time>>cipher_key_time>>compress_type_time>>compress_level_time>>material_simplify_time;
  3223. if(!network){f>>text_data>>app_id>>hm_mtrl_id>>water_mtrl_id; FREPA(mtrl_brush_id)f>>mtrl_brush_id[i];}
  3224. }break;
  3225. }
  3226. // data
  3227. if(mode>=SAVE_ALL)
  3228. switch(f.decUIntV())
  3229. {
  3230. default: goto newer;
  3231. case 0:
  3232. {
  3233. elms.setNum(f.decUIntV()); FREPA(elms)if(!elms[i].load(f, network, network)){if(f.ok())goto newer; goto error;}
  3234. texs.setNum(f.decUIntV()); FREPA(texs)f>>texs[i];
  3235. // update
  3236. if(!network){texs_update.setNum(f.decUIntV()); FREPA(texs_update)f>>texs_update[i];}
  3237. }break;
  3238. }
  3239. }
  3240. }break;
  3241. case 2:
  3242. {
  3243. if(network)f>>id;
  3244. GetStr2(f, name);
  3245. if(mode>=SAVE_SETTINGS)
  3246. {
  3247. // 'ProjectVersion'
  3248. ver=f.decUIntV(); if(ver>ProjectVersion)goto newer; // if project requires a newer version then don't load it
  3249. // settings
  3250. switch(f.decUIntV())
  3251. {
  3252. default: goto newer;
  3253. case 1:
  3254. {
  3255. f>>cipher>>cipher_key>>compress_type>>compress_level>>material_simplify
  3256. >>cipher_time>>cipher_key_time>>compress_type_time>>compress_level_time>>material_simplify_time;
  3257. if(!network){f>>text_data>>app_id>>hm_mtrl_id>>water_mtrl_id; FREPA(mtrl_brush_id)f>>mtrl_brush_id[i];}
  3258. }break;
  3259. case 0:
  3260. {
  3261. byte encrypt_key[32];
  3262. f>>cipher>>encrypt_key>>compress_type>>compress_level>>material_simplify
  3263. >>cipher_time>>cipher_key_time>>compress_type_time>>compress_level_time>>material_simplify_time;
  3264. if(!network){f>>text_data>>app_id>>hm_mtrl_id>>water_mtrl_id; FREPA(mtrl_brush_id)f>>mtrl_brush_id[i];}
  3265. }break;
  3266. }
  3267. // data
  3268. if(mode>=SAVE_ALL)
  3269. switch(f.decUIntV())
  3270. {
  3271. default: goto newer;
  3272. case 0:
  3273. {
  3274. elms.setNum(f.decUIntV()); FREPA(elms)if(!elms[i].load(f, network, network)){if(f.ok())goto newer; goto error;}
  3275. texs.setNum(f.decUIntV()); FREPA(texs)f>>texs[i];
  3276. // update
  3277. if(!network){texs_update.setNum(f.decUIntV()); FREPA(texs_update)f>>texs_update[i];}
  3278. }break;
  3279. }
  3280. }
  3281. }break;
  3282. case 1:
  3283. {
  3284. ver=f.decUIntV();
  3285. f.getUID();
  3286. if(mode>=SAVE_ALL)
  3287. {
  3288. elms.setNum(f.decUIntV()); FREPA(elms)if(!elms[i].load(f, network, network)){if(f.ok())goto newer; goto error;}
  3289. texs.setNum(f.decUIntV()); FREPA(texs)f>>texs[i];
  3290. // update
  3291. if(ver>27){texs_update.setNum(f.decUIntV()); FREPA(texs_update)f>>texs_update[i];}
  3292. }
  3293. }break;
  3294. case 0:
  3295. {
  3296. ver=0;
  3297. f.getUID();
  3298. if(mode>=SAVE_ALL)
  3299. {
  3300. elms.setNum(f.decUIntV()); FREPA(elms)if(!elms[i].load(f, network, network)){if(f.ok())goto newer; goto error;}
  3301. texs.setNum(f.decUIntV()); FREPA(texs)f>>texs[i];
  3302. }
  3303. }break;
  3304. }
  3305. if(f.ok())
  3306. {
  3307. if(mode>=SAVE_ALL)quickUpdateVersion(ver);
  3308. return LOAD_OK;
  3309. }
  3310. error:
  3311. del(); return LOAD_ERROR;
  3312. newer:
  3313. del(); return LOAD_NEWER;
  3314. }
  3315. void Project::save(MemPtr<TextNode> nodes)C
  3316. {
  3317. const bool save_all=true; // this is needed when not calling 'del' in 'load'
  3318. nodes.New().set ("Version" , ProjectVersion);
  3319. nodes.New().set ("Name" , name);
  3320. if(save_all || synchronize ) nodes.New().set ("Synchronize" , synchronize);
  3321. if(save_all || cipher ) nodes.New().set ("Encrypt" , cipher);
  3322. REPA(cipher_key)if(save_all || cipher_key[i]){nodes.New().setRaw("EncryptionKey" , cipher_key); break;}
  3323. if(save_all || compress_type) nodes.New().set ("Compress" , CompressionName(compress_type));
  3324. nodes.New().set ("CompressLevel" , compress_level);
  3325. nodes.New().set ("SimplifyMaterials" , material_simplify);
  3326. nodes.New().set ("EncryptTime" , cipher_time.text());
  3327. nodes.New().set ("EncryptionKeyTime" , cipher_key_time.text());
  3328. nodes.New().set ("CompressTime" , compress_type_time.text());
  3329. nodes.New().set ("CompressLevelTime" , compress_level_time.text());
  3330. nodes.New().set ("SimplifyMaterialsTime", material_simplify_time.text());
  3331. if(save_all || elms.elms())
  3332. {
  3333. TextNode &node=nodes.New().setName("Elements");
  3334. FREPAO(elms).save(node.nodes.New()); // save in order
  3335. }
  3336. if(save_all || texs.elms())
  3337. {
  3338. TextNode &node=nodes.New().setName("Textures");
  3339. FREPA(texs)node.nodes.New().setValueFN(texs[i]); // save in order
  3340. }
  3341. }
  3342. LOAD_RESULT Project::load(C MemPtr<TextNode> &nodes, int &ver, Str &error) // !! this assumes that binary was already loaded and 'ver' already set !!
  3343. {
  3344. error.clear();
  3345. //del(); don't delete, instead let text values override existing members from binary, so we can keep settings not saved in text files, such as element IMPORTING/OPENED, and local user Project Settings (current heightmap material, etc.)
  3346. FREPA(nodes)
  3347. {
  3348. C TextNode &node=nodes[i];
  3349. if(node.name=="Version" ){node.getValue(ver); if(ver>ProjectVersion)goto newer;}else
  3350. if(node.name=="Name" )node.getValue(name);else
  3351. if(node.name=="Synchronize" )synchronize=node.asBool1();else
  3352. if(node.name=="Encrypt" )cipher=(CIPHER_TYPE)node.asInt();else
  3353. if(node.name=="EncryptionKey" )node.getValueRaw(cipher_key);else
  3354. if(node.name=="Compress" ){REP(COMPRESS_NUM)if(node.value==CompressionName(COMPRESS_TYPE(i))){compress_type=COMPRESS_TYPE(i); break;}}else
  3355. if(node.name=="CompressLevel" )compress_level =node.asInt ();else
  3356. if(node.name=="SimplifyMaterials" )material_simplify =(MATERIAL_SIMPLIFY)node.asInt();else
  3357. if(node.name=="EncryptTime" )cipher_time =node.asText();else
  3358. if(node.name=="EncryptionKeyTime" )cipher_key_time =node.asText();else
  3359. if(node.name=="CompressTime" )compress_type_time =node.asText();else
  3360. if(node.name=="CompressLevelTime" )compress_level_time =node.asText();else
  3361. if(node.name=="SimplifyMaterialsTime")material_simplify_time=node.asText();else
  3362. if(node.name=="Elements" )
  3363. {
  3364. // remember 'IMPORTING' and 'OPENED' which are not saved in text
  3365. Memc<UID> importing, opened;
  3366. FREPA(elms)
  3367. {
  3368. C Elm &elm=elms[i];
  3369. if(elm.importing())importing.add(elm.id);
  3370. if(elm.opened ())opened .add(elm.id);
  3371. }
  3372. elms.clear(); // clear all binary elements, and keep those only from the text file, also text based load methods assume that element are clean (don't have existing data, but straight after constructor)
  3373. FREPA(node.nodes) // process in order because they are sorted to avoid moving elements in 'getElm'
  3374. {
  3375. C TextNode &elm_node=node.nodes[i];
  3376. UID id; if(id.fromText(elm_node.name) && id.valid())
  3377. {
  3378. Elm &elm=getElm(id);
  3379. if( elm.type){error=S+"Element \""+elm_node.name+"\" listed more than 1 time"; goto error;}
  3380. if( !elm.load(elm_node, error))goto error;
  3381. }else {error=S+"Invalid Element ID \""+elm_node.name+'"'; goto error;}
  3382. }
  3383. FREPA(importing)if(Elm *elm=findElm(importing[i]))elm->importing(true);
  3384. FREPA(opened )if(Elm *elm=findElm(opened [i]))elm->opened (true);
  3385. }else
  3386. if(node.name=="Textures")
  3387. {
  3388. texs.clear();
  3389. FREPA(node.nodes) // process in order because they are sorted to avoid moving elements in 'texs.binaryInclude'
  3390. {
  3391. UID id; if(node.nodes[i].getValue(id) && id.valid())texs.binaryInclude(id, Compare); // use 'binaryInclude' in case text file is messed up
  3392. else {error=S+"Invalid Texture ID \""+node.nodes[i].value+'"'; goto error;}
  3393. }
  3394. }
  3395. }
  3396. quickUpdateVersion(ver);
  3397. return LOAD_OK;
  3398. error:
  3399. del(); return LOAD_ERROR;
  3400. newer:
  3401. del(); return LOAD_NEWER;
  3402. }
  3403. LOAD_RESULT Project::load(C Str &name, int &ver, SAVE_DATA mode) {File f; if(f.readTry(name))return load(f, ver, false, mode); ver=-1; del(); return LOAD_EMPTY;}
  3404. bool Project::save(C Str &name )C {File f; save(f.writeMem()); f.pos(0); return SafeOverwrite(f, name);}
  3405. LOAD_RESULT Project::load2(C Str &name, int &ver, SAVE_DATA mode) {LOAD_RESULT result=load(name, ver, mode); if(result==LOAD_EMPTY || result==LOAD_ERROR)result=load(name+".old", ver, mode); return result;}
  3406. bool Project::save2(C Str &name )C {if(FExistSystem(name))if(!FRename(name, name+".old"))return false; return save(name);}
  3407. bool Project::saveTxt(C Str &name)C
  3408. {
  3409. if(name.is())
  3410. {
  3411. TextData data; save(data.nodes);
  3412. Str temp=name+"@new";
  3413. if(data.save(temp) && FRename(temp, name))return true;
  3414. FDelFile(temp);
  3415. }
  3416. return false;
  3417. }
  3418. LOAD_RESULT Project::loadTxt(C Str &name, int &ver, Str &error) // !! this assumes that binary was already loaded and 'ver' already set !!
  3419. {
  3420. FileText file; if(file.read(name))
  3421. {
  3422. TextData data; if(data.load(file))return load(data.nodes, ver, error);
  3423. VecI2 col_line; char c=file.posInfo(file.pos()-1, col_line); // if TextData failed to load, due to unexpected character, then it stopped reading after that character, so get what was last read
  3424. error=S+"Unexpected character '"+c+"', Column: "+(col_line.x+1)+", Line: "+(col_line.y+1)+", in File: \""+Str(name).replace(' ', Nbsp)+'"'; ver=-1; del(); return LOAD_ERROR;
  3425. }else
  3426. if(FExistSystem(name)){error=S+"Can't access file \""+Str(name).replace(' ', Nbsp)+"\""; ver=-1; del(); return LOAD_ERROR;}
  3427. error.clear(); return LOAD_EMPTY; // here unlike 'load', 'ver' is not cleared and 'del' is not called for LOAD_EMPTY because we assume that binary was already loaded
  3428. }
  3429. LOAD_RESULT Project::load3(Str path, int &ver, Str &error, SAVE_DATA mode) // this loads "Data" and "Data.txt" (and old "Settings" file)
  3430. {
  3431. path.tailSlash(true);
  3432. error.clear();
  3433. LOAD_RESULT result=load2(path+"Data", ver, mode);
  3434. if(LoadOK(result))
  3435. {
  3436. if(ver>=0 && ver<=34)loadOldSettings2(path+"Settings"); // ver 34 and below had settings in a separate file
  3437. if(text_data && !IsServer) // this should not be done on the Server
  3438. {
  3439. LOAD_RESULT text_result=loadTxt(path+"Data.txt", ver, error);
  3440. if(text_result!=LOAD_EMPTY)result=text_result;
  3441. }
  3442. }
  3443. return result;
  3444. }
  3445. bool Project::isProject(C FileFind &ff) // this will only set 'id' and 'name', but not 'path'
  3446. {
  3447. if(ff.type==FSTD_DIR)
  3448. {
  3449. UID id; if(DecodeFileName(ff.name, id))
  3450. {
  3451. int ver; Str error; LOAD_RESULT result=load3(ff.pathName(), ver, error, SAVE_ID_NAME);
  3452. switch(result)
  3453. {
  3454. case LOAD_OK :
  3455. case LOAD_NEWER :
  3456. case LOAD_LOCKED: T.id=id; return true; // don't set 'path' because if 'close' is called later then it would save data
  3457. }
  3458. }
  3459. }
  3460. return false;
  3461. }
  3462. void Project::setIDPath(C UID &id, C Str &path)
  3463. {
  3464. T.id=id;
  3465. T.path=path; T.path.tailSlash(true);
  3466. code_path=T.path+"Code\\"; code_base_path=code_path+"Base\\";
  3467. edit_path=T.path+"Edit\\";
  3468. game_path=T.path+"Game\\";
  3469. temp_path=T.path+"Temp\\";
  3470. tex_path=game_path+"Tex\\";
  3471. temp_tex_path=temp_path+"Tex\\";
  3472. temp_tex_dynamic_path=temp_tex_path+"Dynamic\\";
  3473. }
  3474. LOAD_RESULT Project::open(C UID &id, C Str &name, C Str &path, Str &error, bool ignore_lock)
  3475. {
  3476. error.clear();
  3477. UID _id=id; Str _name=name, _path=path; _path.tailSlash(true); // copy to temp vars in case params are set to this, and would get cleared in 'close'
  3478. close(); // close existing project
  3479. if(!_id.valid()){error="Invalid ID" ; return LOAD_ERROR;} // ID must be valid
  3480. if(!_path.is() ){error="Invalid Path"; return LOAD_ERROR;} // path must be valid
  3481. Str lock_name=_path+"Lock";
  3482. if(!ignore_lock && FExistSystem(lock_name))return LOAD_LOCKED; // check if it's already opened, do this at start so the project won't be opened
  3483. File f; f.writeTry(lock_name); f.del(); // lock it
  3484. int ver; LOAD_RESULT result=load3(_path, ver, error);
  3485. if(LoadOK(result))
  3486. {
  3487. if(result==LOAD_EMPTY)T.name=_name;
  3488. setIDPath(_id, _path);
  3489. FCreateDirs(code_path);
  3490. FCreateDirs(edit_path);
  3491. FCreateDirs(game_path);
  3492. FCreateDirs( tex_path);
  3493. if(!IsServer)
  3494. {
  3495. FCreateDirs(code_base_path);
  3496. FCreateDirs(temp_path);
  3497. FCreateDirs(temp_tex_path);
  3498. FCreateDirs(temp_tex_dynamic_path);
  3499. }
  3500. updateVersion(ver);
  3501. if(ver<=34) // ver 34 and below had settings in a separate file
  3502. if(save2(_path+"Data")) // save with new format to include settings
  3503. {
  3504. FDelFile(_path+"Settings" ); // delete old settings file
  3505. FDelFile(_path+"Settings.old"); // delete old settings file
  3506. }
  3507. }else FDelFile(lock_name); // unlock on error
  3508. return result;
  3509. }
  3510. bool Project::save(SAVE_MODE save_mode)
  3511. {
  3512. bool ok=true;
  3513. if(path.is())
  3514. {
  3515. flush(save_mode); // flush first in case flushing will modify some data (like elm versions for example)
  3516. if(!save2(path+"Data"))
  3517. {
  3518. ok=false;
  3519. Gui.msgBox(S, "Can't save Project Data");
  3520. }
  3521. if(text_data && !IsServer) // this should not be done on the Server
  3522. if(!saveTxt(path+"Data.txt"))
  3523. {
  3524. ok=false;
  3525. Gui.msgBox(S, "Can't save Project Data.txt");
  3526. }
  3527. }
  3528. return ok;
  3529. }
  3530. void Project::close()
  3531. {
  3532. save();
  3533. if(path.is())FDelFile(path+"Lock"); // unlock
  3534. del ();
  3535. }
  3536. void ElmNode::clear() {added=false; flag=0; parent=-1; children.clear();}
  3537. ProjectHierarchy& ProjectHierarchy::del()
  3538. {
  3539. root.clear();
  3540. hierarchy.del();
  3541. ::Project::del(); return T;
  3542. }
  3543. void ProjectHierarchy::floodRemoved(Memc<UID> &removed, ElmNode &node, bool parent_removed)
  3544. {
  3545. FREPA(node.children) // list in order
  3546. {
  3547. int child_i =node.children[i];
  3548. ElmNode &child =hierarchy[child_i];
  3549. Elm &elm =elms [child_i];
  3550. bool elm_removed=(elm.removed() || parent_removed);
  3551. if(elm_removed)removed.add(elm.id);
  3552. floodRemoved(removed, child, elm_removed);
  3553. }
  3554. }
  3555. void ProjectHierarchy::floodHierarchy(ElmNode &node)
  3556. {
  3557. REPA(node.children)
  3558. {
  3559. int child_i=node.children[i];
  3560. ElmNode &child =hierarchy[child_i];
  3561. child.added=true;
  3562. floodHierarchy(child);
  3563. }
  3564. }
  3565. void ProjectHierarchy::addHierarchy(ElmNode &node, int node_i, ElmNode &target, int target_i)
  3566. {
  3567. if(InRange(node.parent, hierarchy))hierarchy[node.parent].children.exclude(node_i); // remove from previous parent
  3568. node.parent=target_i; target.children.add(node_i); // add to new parent
  3569. node.added=true; // mark as added
  3570. floodHierarchy(node); // add children of node
  3571. }
  3572. void ProjectHierarchy::setHierarchy()
  3573. {
  3574. root.clear(); hierarchy.clear().setNum(elms.elms());
  3575. FREPA(elms)
  3576. {
  3577. Elm &elm=elms[i];
  3578. int parent=findElmI(elm.parent_id); hierarchy[i].parent=parent;
  3579. if(InRange(parent, hierarchy))hierarchy[parent].children.add(i);else root.children.add(i);
  3580. }
  3581. floodHierarchy(root);
  3582. // here still may be some unadded elements, those which have parents incorrectly setup in a loop (A -> B -> A -> B), they can also have children (C -> B)
  3583. Memt<int> parents;
  3584. FREPA(elms)
  3585. {
  3586. ElmNode &node=hierarchy[i];
  3587. if(!node.added) // if not yet added
  3588. {
  3589. // check if it goes in a loop
  3590. parents.clear();
  3591. for(ElmNode *cur=&node; ; )
  3592. {
  3593. int parent=cur->parent; if(!InRange(parent, elms))goto no_loop; // if it has no parent
  3594. if(!parents.include(parent)) // if parent was present in the parents list, then this is a loop
  3595. {
  3596. addHierarchy(node, i, root, -1);
  3597. break;
  3598. }
  3599. cur=&hierarchy[parent]; // proceed to next parent
  3600. }
  3601. no_loop:;
  3602. }
  3603. }
  3604. }
  3605. bool ProjectHierarchy::contains(C Elm &a, C Elm *b)C // if 'a' contains 'b'
  3606. {
  3607. if(!b)return false;
  3608. int ai=elms.validIndex(&a),
  3609. bi=elms.validIndex( b);
  3610. for(; InRange(bi, hierarchy); ){if(bi==ai)return true; bi=hierarchy[bi].parent;}
  3611. return false;
  3612. }
  3613. int ProjectHierarchy::depth(C Elm *elm)C
  3614. {
  3615. int depth=-1; for(int i=elms.validIndex(elm); InRange(i, hierarchy); i=hierarchy[i].parent)depth++;
  3616. return depth;
  3617. }
  3618. Elm* ProjectHierarchy::firstParent(Elm *elm, ELM_TYPE type)
  3619. {
  3620. for(int i=elms.validIndex(elm); InRange(i, hierarchy); i=hierarchy[i].parent)
  3621. {
  3622. Elm &elm=elms[i]; if(elm.type==type)return &elm;
  3623. }
  3624. return null;
  3625. }
  3626. Elm* ProjectHierarchy::firstVisibleParent(Elm *elm)
  3627. {
  3628. for(int i=elms.validIndex(elm); InRange(i, hierarchy); i=hierarchy[i].parent)
  3629. {
  3630. Elm &elm=elms[i]; if(ElmVisible(elm.type))return &elm;
  3631. }
  3632. return null;
  3633. }
  3634. Str ProjectHierarchy::elmSrcFileFirst(C Elm *elm)C
  3635. {
  3636. for(int i=elms.validIndex(elm); InRange(i, hierarchy); i=hierarchy[i].parent)
  3637. {
  3638. C Elm &elm=elms[i]; if(elm.srcFile().is())
  3639. {
  3640. Mems<Edit::FileParams> files=Edit::FileParams::Decode(elm.srcFile());
  3641. FREPA(files)
  3642. {
  3643. Str path=FFirstUp(files[i].name); if(path.is())return path;
  3644. }
  3645. }
  3646. }
  3647. return S;
  3648. }
  3649. Elm* ProjectHierarchy::findElmByPath(C Str &path)
  3650. {
  3651. if(path.is())
  3652. {
  3653. ElmNode *parent=&root; Str p=path; for(;;)
  3654. {
  3655. Str name=GetStart(p); p=GetStartNot(p);
  3656. Elm *found_elm =null;
  3657. ElmNode *found_node=null;
  3658. REPA(parent->children)
  3659. {
  3660. int child_i=parent->children[i]; Elm &elm=elms[child_i]; if(elm.name==name && ElmVisible(elm.type)) // don't list hidden types
  3661. {
  3662. found_elm =&elm;
  3663. found_node=&hierarchy[child_i];
  3664. if(!elm.removed())break; // stop looking if this element exists
  3665. }
  3666. }
  3667. if(!p.is() )return found_elm;
  3668. if(!found_node)break;
  3669. parent=found_node;
  3670. }
  3671. }
  3672. return null;
  3673. }
  3674. Str ProjectHierarchy::elmFullName(C UID &elm_id, int max_elms)C {return ::Project::elmFullName(elm_id, max_elms);}
  3675. Str ProjectHierarchy::elmFullName(C Elm *elm , int max_elms)C
  3676. {
  3677. int length=0; Memt<C Elm*> processed; Str name;
  3678. for(int i=elms.validIndex(elm); InRange(i, hierarchy); i=hierarchy[i].parent)
  3679. {
  3680. C Elm &elm=elms[i]; if(ElmVisible(elm.type)) // don't include name of elements that are not visible
  3681. {
  3682. if(!max_elms--){name.reserve(length+3)="..\\"; break;} // if reached the allowed limit
  3683. processed.add(&elm); length+=elm.name.length()+1; // 1 extra for '\\'
  3684. }
  3685. }
  3686. name.reserve(length); REPA(processed){name+=processed[i]->name; if(i)name+='\\';}
  3687. return name;
  3688. }
  3689. void ProjectHierarchy::eraseRemoved()
  3690. {
  3691. removeOrphanedElms();
  3692. bool erased=false; Memc<UID> remove; floodRemoved(remove, root);
  3693. erased|=eraseElms (remove);
  3694. erased|=eraseTexs ();
  3695. erased|=eraseWorldObjs();
  3696. if(erased)save(); // save immediately after erase, just in case
  3697. }
  3698. Project::AreaSyncObj::AreaSyncObj() : changed(false), xy(0), area_ver(null), world_ver(null), project(null) {}
  3699. ElmNode::ElmNode() : added(false), flag(0), parent(-1) {}
  3700. /******************************************************************************/