[email protected] 219 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. /******************************************************************************/
  4. ProjectEx Proj;
  5. /******************************************************************************/
  6. ThreadSafeMap<UID, TextureInfo> TexInfos(Compare); // since Textures are generated from Hash, they will be the same for all projects, so we can keep it outside of 'Project' class
  7. State StateProject(UpdateProject, DrawProject, InitProject, ShutProject);
  8. /******************************************************************************/
  9. int CompareProjPath(C UID &a, C UID &b) {return ComparePathNumber(Proj.elmFullName(a), Proj.elmFullName(b));}
  10. /******************************************************************************/
  11. void HideProject()
  12. {
  13. Export.hide();
  14. ProjSettings.hide();
  15. MSM.hide();
  16. DST.hide();
  17. CreateMtrls.hide();
  18. ConvertToAtlas.hide();
  19. ConvertToDeAtlas.hide();
  20. RenameElm.hide();
  21. ReplaceName.hide();
  22. ReloadElm.hide();
  23. NewWorld.hide(); WorldEdit.grid_plane_level_win.hide(); WorldEdit.goto_area.hide();
  24. CopyElms.hide(); CopyElms.replace_elms.hide();
  25. SizeStats.hide();
  26. PublishRes.hide();
  27. EraseRemoved.hide();
  28. ElmProps.hide();
  29. SplitAnim.hide();
  30. RenameSlot.hide();
  31. RenameBone.hide();
  32. RenameEvent.hide();
  33. SetMtrlColor.hide();
  34. MulSoundVolume.hide();
  35. Publish.export_data .hide();
  36. ObjEdit.mesh_parts .rename.hide();
  37. ObjEdit.mesh_variations.rename.hide();
  38. ImportCode.del ();
  39. }
  40. /******************************************************************************/
  41. bool InitProject()
  42. {
  43. SetKbExclusive();
  44. Mode. show().disabled(false); // show this first before making 'Proj.visible' because first we need to show Code Editor and its menu, so when 'Proj.show' calls 'CodeEdit.resize' then it will set source code rect according to the menu rect
  45. Misc. show().disabled(false);
  46. Proj.menu .disabled(false);
  47. Proj.visible(!Misc.hide_proj());
  48. Theater.setVisibility(true);
  49. return true;
  50. }
  51. void ShutProject()
  52. {
  53. Mode .hide().disabled(true);
  54. Misc .hide().disabled(true);
  55. Proj.menu .disabled(true);
  56. Proj .hide();
  57. Theater.setVisibility(false);
  58. TexDownsize.close(); // close to disable menu
  59. HideProject();
  60. }
  61. /******************************************************************************/
  62. Elm* GuiObjToElm(GuiObj *go)
  63. {
  64. if(go)
  65. {
  66. if(go==& AnimEdit || go==&AnimEdit.preview)return AnimEdit.elm;
  67. if(go==& PhysMtrlEdit)return PhysMtrlEdit.elm;
  68. if(go==& MiniMapEdit)return MiniMapEdit.elm;
  69. if(go==& EnvEdit)return EnvEdit.elm;
  70. if(go==& EnumEdit)return EnumEdit.elm;
  71. if(go==& ImageEdit)return ImageEdit.elm;
  72. if(go==&ImageAtlasEdit)return ImageAtlasEdit.elm;
  73. if(go==& IconEdit)return IconEdit.elm;
  74. if(go==& IconSettsEdit)return IconSettsEdit.elm;
  75. if(go==& FontEdit)return FontEdit.elm;
  76. if(go==& TextStyleEdit)return TextStyleEdit.elm;
  77. if(go==&PanelImageEdit)return PanelImageEdit.elm;
  78. if(go==& PanelEdit)return PanelEdit.elm;
  79. if(go==& ObjClassEdit)return ObjClassEdit.elm;
  80. if(go==& SoundEdit)return SoundEdit.elm;
  81. if(go==& VideoEdit)return VideoEdit.elm;
  82. if(go==& AppPropsEdit)return AppPropsEdit.elm;
  83. if(go==& MtrlEdit)return MtrlEdit.elm;
  84. if(go==& WaterMtrlEdit)return WaterMtrlEdit.elm;
  85. if(go==& ObjEdit)return ObjEdit.obj_elm;
  86. if(go==& WorldEdit)return WorldEdit.elm;
  87. if(go==& GuiSkinEdit)return GuiSkinEdit.elm;
  88. if(go==& GuiEdit)return GuiEdit.elm;
  89. }
  90. return null;
  91. }
  92. bool UpdateProject()
  93. {
  94. CurTime.getUTC();
  95. Gui.update();
  96. Mode.resize();
  97. ObjEdit.resize();
  98. AnimEdit.resize();
  99. WorldEdit.resize();
  100. GuiSkinEdit.resize();
  101. GuiEdit.resize();
  102. CodeEdit.resize(false);
  103. Theater.resize();
  104. TexDownsize.resize();
  105. WorldEdit.updateCamCenter(); // update after gui so 'obj_pos' will work without delay
  106. Proj .update();
  107. Server .update(&Proj, false);
  108. Importer .update();
  109. Builder .update(true);
  110. Water .update(0.02f);
  111. D .grassUpdate();
  112. Clouds .layered.update();
  113. Physics .stopSimulation().startSimulation(); // to visualize actors used in physical material editors
  114. // mouse maximize window
  115. if(Ms.bp(3))
  116. if(!ObjEdit.contains(Gui.ms()) && !AnimEdit.contains(Gui.ms()) && !WorldEdit.contains(Gui.ms()) && !TexDownsize.contains(Gui.ms()))WindowToggle();
  117. // open element
  118. if(Kb.ctrl() && Ms.bp(0) && Gui.ms() && Gui.ms()->type()==GO_TEXTLINE)
  119. {
  120. Memc<Str > splits; Split(splits, Gui.ms()->asTextLine()(), '|');
  121. Memc<Elm*> elms ; FREPA(splits)if(Elm *elm=Proj.findElm(splits[i]))elms.add(elm);
  122. switch(elms.elms())
  123. {
  124. case 0: break;
  125. case 1: Proj.elmToggle(elms[0]); break;
  126. default: // cycle between elements
  127. {
  128. int last=-1; // cycle is not perfect if both Window and Fullscreen editors are listed, because there's no way to detect which one was used the last time
  129. if(GuiObj *desktop=Gui.desktop())REP(desktop->childNum())if(GuiObj *go=desktop->child(i))if(go->type()==GO_WINDOW && go->visible())if(Elm *elm=GuiObjToElm(go)) // start from top most visible children
  130. REPA(elms)if(elms[i]==elm){last=i; goto found;}
  131. REPA(Mode.order)if(Mode.available[Mode.order[i]])if(Elm *elm=Mode.elm(Mode.order[i]))
  132. REPA(elms)if(elms[i]==elm){last=i; goto found;}
  133. found:
  134. Proj.elmActivate(elms[Mod(last+1, elms.elms())]);
  135. }break;
  136. }
  137. }
  138. D.viewRect(null); // restore full viewport because it could have been changed by 'Viewport4.update' or 'setViewportCamera'
  139. return true;
  140. }
  141. void DrawProject()
  142. {
  143. IconEdit.makeIcon(); // call this first before any drawing
  144. // draw background
  145. if(Mode()<0 )D.clear(BackgroundColor());else
  146. if(Mode()==MODE_GUI )GuiEdit.drawPre();else
  147. if(Mode()==MODE_GUI_SKIN)GuiSkinEdit.draw();else
  148. if(Mode()==MODE_CODE )CodeEdit.CodeEditorInterface::draw();
  149. Gui.draw();
  150. Proj.draw();
  151. GuiEdit.draw();
  152. WorldEdit.higlight();
  153. //if(Importer.busy())D.text(0, D.h()-0.05, "Importing..");
  154. }
  155. /******************************************************************************/
  156. /******************************************************************************/
  157. cchar8 *ProjectEx::explore_desc="Open folder containing the source file from which this element was created",
  158. *ProjectEx::dis_publish_desc="Disable publishing for this element.\nThis option can be useful if you want to disable element from being published without having to remove it.\nThis can be used for example on:\n-Images that are used to create an Image Atlas, however the Images themself are not used in the game.\n-Object which is used to create an Icon, however the Object itself is not used in the game.",
  159. * ProjectEx::en_publish_desc="Disable publishing for this element.\nThis option can be useful if you want to disable element from being published without having to remove it.\nThis can be used for example on:\n-Images that are used to create an Image Atlas, however the Images themself are not used in the game.\n-Object which is used to create an Icon, however the Object itself is not used in the game.",
  160. * ProjectEx::reload_desc="Reload element from its original source";
  161. /******************************************************************************/
  162. void ProjectEx::OuterRegion::setRegionWidth(flt w)
  163. {
  164. Clamp(w, Misc.size().x, Misc.size().x*3);
  165. if(!Equal(w, rect().w()))
  166. {
  167. size(Vec2(w, rect().h()));
  168. ScreenChanged();
  169. }
  170. }
  171. GuiObj* ProjectEx::OuterRegion::test(C GuiPC &gpc, C Vec2 &pos, GuiObj* &mouse_wheel)
  172. {
  173. GuiObj *go=::EE::Region::test(gpc, pos, mouse_wheel);
  174. if(visible())
  175. {
  176. Rect r=rect()+gpc.offset;
  177. r.min.x=r.max.x;
  178. r.max.x+=Min(0.011f, Gui.resize_radius); // make less than 'resize_radius' because many Object Editor window buttons are located 0.01 away from the border
  179. if(Cuts(pos, r&gpc.clip))go=this;
  180. }
  181. return go;
  182. }
  183. void ProjectEx::OuterRegion::update(C GuiPC &gpc)
  184. {
  185. ::EE::Region::update(gpc);
  186. if(Gui.ms()==this)
  187. {
  188. if(!Ms.b(0)) // if not pressed then detect new resize, if pressed then keep previous
  189. {
  190. resize_on=false;
  191. Vec2 pos=Ms.pos()-gpc.offset;
  192. if(pos.x>=rect().max.x-D.pixelToScreenSize().x)resize_on=true;
  193. }else // button pressed
  194. if(resize_on) // resize
  195. {
  196. Vec2 pos=Ms.pos()-gpc.offset;
  197. setRegionWidth(pos.x-rect().min.x);
  198. }
  199. }else resize_on=false;
  200. }
  201. void ProjectEx::OuterRegion::draw()
  202. {
  203. if(resize_on && Gui.ms()==this)
  204. if(Image *image=Gui.image_resize_x())
  205. {
  206. Vec2 pos=Ms.pos(), size(MOUSE_IMAGE_SIZE*image->aspect(), MOUSE_IMAGE_SIZE); pos+=Vec2(-size.x, size.y)*0.5f;
  207. image->draw(Rect_LU(D.screenAlignedToPixel(pos), size));
  208. }
  209. }
  210. GuiObj& ProjectEx::OuterRegion::show(){if(hidden ()){::EE::GuiObj::show(); MtrlEdit.resize(); WaterMtrlEdit.resize(); CodeEdit.resize(); if(!Gui.window() && Mode()<0)Proj.list.kbSet();} return T;}
  211. GuiObj& ProjectEx::OuterRegion::hide(){if(visible()){::EE::GuiObj::hide(); MtrlEdit.resize(); WaterMtrlEdit.resize(); CodeEdit.resize(); Proj.updateMenu(); } return T;}
  212. GuiObj* ProjectEx::ElmList::Warning::test(C GuiPC &gpc, C Vec2 &pos, GuiObj* &mouse_wheel){return null;}
  213. void ProjectEx::ElmList::Warning::create(int data_abs_index, bool error, flt offset)
  214. {
  215. flt w=Proj.list.columnWidth(Proj.list.icon_col);
  216. ::EE::GuiImage::create(Rect_U(offset, 0, w, w), error ? Proj.exclamation : Proj.warning);
  217. rect_color.zero();
  218. Proj.list.addChild(T, data_abs_index, Proj.list.icon_col); // add after everything is setup
  219. }
  220. ListElm* ProjectEx::ElmList::curToListElm( )C {return List<ListElm>::operator()();}
  221. ListElm* ProjectEx::ElmList::litToListElm( )C {return visToData(lit);}
  222. Elm* ProjectEx::ElmList::operator()( )C {if(ListElm *e=visToData(cur ))return e->elm ; return null;}
  223. EEItem* ProjectEx::ElmList::visToItem( int visible )C {if(ListElm *e=visToData(visible ))return e->item; return null;}
  224. EEItem* ProjectEx::ElmList::absToItem( int absolute)C {if(ListElm *e=absToData(absolute))return e->item; return null;}
  225. Elm* ProjectEx::ElmList::visToElm( int visible )C {if(ListElm *e=visToData(visible ))return e->elm ; return null;}
  226. Elm* ProjectEx::ElmList::absToElm( int absolute)C {if(ListElm *e=absToData(absolute))return e->elm ; return null;}
  227. int ProjectEx::ElmList::itemToVis(C EEItem *data)C {if(data)REP(visibleElms())if(visToItem(i)==data)return i; return -1;}
  228. int ProjectEx::ElmList::itemToAbs(C EEItem *data)C {if(data)REP( totalElms())if(absToItem(i)==data)return i; return -1;}
  229. int ProjectEx::ElmList::elmToVis(C Elm *data)C {if(data)REP(visibleElms())if(visToElm (i)==data)return i; return -1;}
  230. int ProjectEx::ElmList::elmToAbs(C Elm *data)C {if(data)REP( totalElms())if(absToElm (i)==data)return i; return -1;}
  231. bool ProjectEx::ElmList::identitySort()C // if list is sorted in identity mode
  232. {
  233. FREPA(sort_column) // check in order
  234. {
  235. int col=sort_column[i];
  236. if( col< 0)continue; // unspecified = ignore
  237. if( col<=icon_col)return sort_swap[i]==false; // these columns sort in identity, so check if it's not swapped
  238. return false; // other columns never sort in identity
  239. }
  240. return true; // if all are unspecified then it's OK
  241. }
  242. void ProjectEx::ElmList::update(C GuiPC &gpc)
  243. {
  244. // process before update, because we're potentially eating a button
  245. if(tapped_vis>=0)
  246. {
  247. if(Time.appTime()>=tapped_time) // if enough time has passed to process it
  248. {
  249. if(tapped_open)
  250. if(Elm *elm=visToElm(tapped_vis)){elm->opened(!elm->opened()); Proj.refresh(false, false);}
  251. tapped_vis=-1;
  252. }else
  253. if(!tapped_open)Ms.eat(0); // don't process any new presses
  254. }
  255. ::EE::_List::update(gpc);
  256. if(Gui.kb()==this)
  257. {
  258. KbSc rename(KB_F2), reload(KB_R, KBSC_CTRL_CMD), copyTo(KB_T, KBSC_CTRL_CMD); // check manually, in case 'menu' is not created according to selection
  259. if(rename.pd())
  260. {
  261. rename.eat();
  262. if(Elm *elm=T())RenameElm.activate(elm);
  263. }else
  264. if(reload.pd())
  265. {
  266. reload.eat();
  267. Proj.setMenuListSel();
  268. Proj.reload(Proj.menu_list_sel);
  269. }else
  270. if(copyTo.pd())
  271. {
  272. copyTo.eat();
  273. CopyElms.display();
  274. }else
  275. if(!Kb.k.ctrlCmd())
  276. {
  277. if(Kb.k(KB_RIGHT))
  278. {
  279. if(ListElm *elm=curToListElm())
  280. {
  281. if(elm->hasVisibleChildren())
  282. {
  283. if(elm->item && !elm->item->opened ){elm->item->opened=true ; goto right_done;}
  284. if(elm->elm && !elm->elm ->opened()){elm->elm ->opened(true); goto right_done;}
  285. if(cur<elms()-1)setCur(cur+1);
  286. right_done:
  287. Proj.refresh(false, false);
  288. scrollTo(cur+4); // scroll after refresh
  289. }
  290. }
  291. }else
  292. if(Kb.k(KB_LEFT))
  293. {
  294. if(ListElm *elm=curToListElm())
  295. {
  296. if(!elm->close()) // try closing current element
  297. for(int i=cur-1; i>=0; i--)if(ListElm *e=visToData(i))if(e->offset<elm->offset){setCur(i); break;} // find first parent
  298. Proj.refresh(false, false);
  299. scrollTo(cur-4); // scroll after refresh
  300. }
  301. }else
  302. if(Kb.k(KB_ENTER))
  303. {
  304. Kb.eatKey();
  305. Proj.elmToggle(curToListElm());
  306. }/*else
  307. if(KbSc(KB_F4).pd()){if(Elm *elm=T())Proj.explore(*elm);}*/
  308. }
  309. menu.checkKeyboardShortcuts(); // check these before other menus are checked, but check after keys above, because menu shortcuts operate on 'menu_list_sel'
  310. }
  311. UID lit_elm_id=UIDZero; if(ListElm *elm=litToListElm())if(elm->elm)lit_elm_id=elm->elm->id;
  312. if( lit_elm_id!=T.lit_elm_id)
  313. {
  314. T.lit_elm_id=lit_elm_id;
  315. ObjEdit.param_edit.setSkin();
  316. WorldEdit.param_edit.setSkin();
  317. ObjClassEdit.setSkin();
  318. }
  319. }
  320. void ProjectEx::ElmChange::swap(ptr user)
  321. {
  322. // since we're swapping, then we need to adjust the 'type' so next change will be the opposite of this operation
  323. switch(type)
  324. {
  325. case PUBLISH_DISABLE: type=PUBLISH_ENABLE ; Proj. enablePublish(elms, false ); break;
  326. case PUBLISH_ENABLE : type=PUBLISH_DISABLE; Proj.disablePublish(elms, false, false); break;
  327. case REMOVE : type=RESTORE ; Proj. restore(elms, false ); break;
  328. case RESTORE : type=REMOVE ; Proj. remove (elms, false, false); break;
  329. case SET_PARENT : Proj. setElmParent(elm_parents, true); break;
  330. case SET_NAME : Proj. setElmNames (elm_names , true); break;
  331. }
  332. }
  333. void ProjectEx::NewFolder(ProjectEx &proj) {proj.newElm(ELM_FOLDER );}
  334. void ProjectEx::NewMtrl(ProjectEx &proj) {proj.newElm(ELM_MTRL );}
  335. void ProjectEx::NewWaterMtrl(ProjectEx &proj) {proj.newElm(ELM_WATER_MTRL );}
  336. void ProjectEx::NewPhysMtrl(ProjectEx &proj) {proj.newElm(ELM_PHYS_MTRL );}
  337. void ProjectEx::NewAnim(ProjectEx &proj) {proj.newElm(ELM_ANIM );}
  338. void ProjectEx::NewObject(ProjectEx &proj) {proj.newElm(ELM_OBJ );}
  339. void ProjectEx::NewObjectClass(ProjectEx &proj) {proj.newElm(ELM_OBJ_CLASS );}
  340. void ProjectEx::NewEnum(ProjectEx &proj) {proj.newElm(ELM_ENUM );}
  341. void ProjectEx::NewImage(ProjectEx &proj) {proj.newElm(ELM_IMAGE );}
  342. void ProjectEx::NewImageAtlas(ProjectEx &proj) {proj.newElm(ELM_IMAGE_ATLAS);}
  343. void ProjectEx::NewIconSetts(ProjectEx &proj) {proj.newElm(ELM_ICON_SETTS );}
  344. void ProjectEx::NewIcon(ProjectEx &proj) {proj.newElm(ELM_ICON );}
  345. void ProjectEx::NewFont(ProjectEx &proj) {proj.newElm(ELM_FONT );}
  346. void ProjectEx::NewPanelImage(ProjectEx &proj) {proj.newElm(ELM_PANEL_IMAGE);}
  347. void ProjectEx::NewTextStyle(ProjectEx &proj) {proj.newElm(ELM_TEXT_STYLE );}
  348. void ProjectEx::NewPanel(ProjectEx &proj) {proj.newElm(ELM_PANEL );}
  349. void ProjectEx::NewGuiSkin(ProjectEx &proj) {proj.newElm(ELM_GUI_SKIN );}
  350. void ProjectEx::NewGui(ProjectEx &proj) {proj.newElm(ELM_GUI );}
  351. void ProjectEx::NewSound(ProjectEx &proj) {proj.newElm(ELM_SOUND );}
  352. void ProjectEx::NewVideo(ProjectEx &proj) {proj.newElm(ELM_VIDEO );}
  353. void ProjectEx::NewRawFile(ProjectEx &proj) {proj.newElm(ELM_FILE );}
  354. void ProjectEx::NewLib(ProjectEx &proj) {proj.newElm(ELM_LIB );}
  355. void ProjectEx::NewApp(ProjectEx &proj) {proj.newElm(ELM_APP );}
  356. void ProjectEx::NewCode(ProjectEx &proj) {proj.newElm(ELM_CODE );}
  357. void ProjectEx::NewEnv(ProjectEx &proj) {proj.newElm(ELM_ENV );}
  358. void ProjectEx::NewMiniMap(ProjectEx &proj) {proj.newElm(ELM_MINI_MAP );}
  359. void ProjectEx::NewWorld(ProjectEx &proj) {::NewWorld.display();}
  360. void ProjectEx::ElmRename(ProjectEx &proj) {if(proj.menu_list_sel.elms()==1)if(Elm *elm=proj.findElm(proj.menu_list_sel[0]))RenameElm.activate(elm->id, elm->name);}
  361. void ProjectEx::ReplaceName(ProjectEx &proj) {::ReplaceName.activate(proj.menu_list_sel);}
  362. void ProjectEx::Remove(ProjectEx &proj) {proj.list.kbSet(); proj.remove (proj.menu_list_sel, true);}
  363. void ProjectEx::Restore(ProjectEx &proj) {proj.list.kbSet(); proj.restore (proj.menu_list_sel);}
  364. void ProjectEx::Reload(ProjectEx &proj) {proj.reload (proj.menu_list_sel);}
  365. void ProjectEx::CancelReload(ProjectEx &proj) {proj.cancelReload(proj.menu_list_sel);}
  366. void ProjectEx::SplitAnim(ProjectEx &proj) {if(proj.menu_list_sel.elms())::SplitAnim.activate(proj.menu_list_sel[0]);}
  367. void ProjectEx::EnablePublish(ProjectEx &proj) {proj.list.kbSet(); proj. enablePublish(proj.menu_list_sel);}
  368. void ProjectEx::DisablePublish(ProjectEx &proj) {proj.list.kbSet(); proj.disablePublish(proj.menu_list_sel, true);}
  369. void ProjectEx::UndoElmChange(ProjectEx &proj) {proj.elm_undos.undo();}
  370. void ProjectEx::RedoElmChange(ProjectEx &proj) {proj.elm_undos.redo();}
  371. void ProjectEx::Duplicate(ProjectEx &proj) {proj.list.kbSet(); proj.duplicate (proj.menu_list_sel);}
  372. void ProjectEx::CopyTo(ProjectEx &proj) {if(Demo)Gui.msgBox(S, "This option is not available in the demo version.");else CopyElms.display();}
  373. void ProjectEx::Expand(ProjectEx &proj) {proj.list.kbSet(); proj.expand (proj.menu_list_sel, proj.menu_list_sel_item);}
  374. void ProjectEx::ExpandAll(ProjectEx &proj) {proj.list.kbSet(); proj.expandAll (proj.menu_list_sel, proj.menu_list_sel_item);}
  375. void ProjectEx::Collapse(ProjectEx &proj) {proj.list.kbSet(); proj.collapse (proj.menu_list_sel, proj.menu_list_sel_item);}
  376. void ProjectEx::ExploreElm(ProjectEx &proj) {proj.list.kbSet(); if(proj.menu_list_sel.elms()==1)if(Elm *elm=proj.findElm(proj.menu_list_sel[0]))proj.explore(*elm);}
  377. void ProjectEx::Export(ProjectEx &proj) {if(proj.menu_list_sel.elms())::Export.activate(proj.menu_list_sel[0]);}
  378. void ProjectEx::Properties(ProjectEx &proj) {if(proj.menu_list_sel.elms()==1)if(Elm *elm=proj.findElm(proj.menu_list_sel[0]))ElmProps.activate(*elm);}
  379. void ProjectEx::FilterChanged(ProjectEx &proj) {proj.refresh(false, false);}
  380. void ProjectEx::ShowRemoved(ProjectEx &proj) {proj.refresh(false, false);}
  381. void ProjectEx::ShowTheater(ProjectEx &proj) {Theater.setVisibility(true);}
  382. void ProjectEx::SoundPlay(ProjectEx &proj) {if(proj.sound.playing() && proj.list.sound_play.lit_id==proj.list.sound_play.play_id)proj.sound.close();else{proj.sound.close(); if(Elm *elm=proj.findElm(proj.list.sound_play.lit_id, ELM_SOUND)){proj.list.sound_play.play_id=elm->id; proj.sound.play(proj.gamePath(*elm));}}}
  383. void ProjectEx::ImageMipMapOn(ProjectEx &proj) {proj.imageMipMap(proj.menu_list_sel, true );}
  384. void ProjectEx::ImageMipMapOff(ProjectEx &proj) {proj.imageMipMap(proj.menu_list_sel, false);}
  385. void ProjectEx::MtrlRGB1(ProjectEx &proj) {proj.mtrlRGB (proj.menu_list_sel, 1);}
  386. void ProjectEx::MtrlRGB(ProjectEx &proj) {SetMtrlColor.display (proj.menu_list_sel);}
  387. void ProjectEx::MtrlMulRGB(ProjectEx &proj) {SetMtrlColor.display (proj.menu_list_sel, true);}
  388. void ProjectEx::MtrlRGBCur(ProjectEx &proj) {if(MtrlEdit.elm)proj.mtrlRGB (proj.menu_list_sel, MtrlEdit.edit.color.xyz);else Gui.msgBox(S, "There's no Material opened");}
  389. void ProjectEx::MtrlAlpha(ProjectEx &proj) {proj.mtrlAlpha (proj.menu_list_sel);}
  390. void ProjectEx::MtrlCullOn(ProjectEx &proj) {proj.mtrlCull (proj.menu_list_sel, true );}
  391. void ProjectEx::MtrlCullOff(ProjectEx &proj) {proj.mtrlCull (proj.menu_list_sel, false);}
  392. void ProjectEx::MtrlFlipNrmYOn(ProjectEx &proj) {proj.mtrlFlipNrmY (proj.menu_list_sel, true );}
  393. void ProjectEx::MtrlFlipNrmYOff(ProjectEx &proj) {proj.mtrlFlipNrmY (proj.menu_list_sel, false);}
  394. void ProjectEx::MtrlReloadBaseTex(ProjectEx &proj) {proj.mtrlReloadTextures (proj.menu_list_sel, true, false, false, false, false);}
  395. void ProjectEx::MtrlMulTexCol(ProjectEx &proj) {proj.mtrlMulTexCol (proj.menu_list_sel);}
  396. void ProjectEx::MtrlMulTexRough(ProjectEx &proj) {proj.mtrlMulTexRough (proj.menu_list_sel);}
  397. void ProjectEx::MtrlMoveToObj(ProjectEx &proj) {proj.mtrlMoveToObj (proj.menu_list_sel);}
  398. void ProjectEx::MtrlMerge(ProjectEx &proj) {MSM .display(proj.menu_list_sel);}
  399. void ProjectEx::MtrlConvertToAtlas(ProjectEx &proj) {ConvertToAtlas .setElms(proj.menu_list_sel);}
  400. void ProjectEx::MtrlConvertToDeAtlas(ProjectEx &proj) {ConvertToDeAtlas.setElms(proj.menu_list_sel);}
  401. void ProjectEx::MtrlMobileTexSizeFull(ProjectEx &proj) {proj.mtrlDownsizeTexMobile(proj.menu_list_sel, 0);}
  402. void ProjectEx::MtrlMobileTexSizeHalf(ProjectEx &proj) {proj.mtrlDownsizeTexMobile(proj.menu_list_sel, 1);}
  403. void ProjectEx::MtrlMobileTexSizeQuarter(ProjectEx &proj) {proj.mtrlDownsizeTexMobile(proj.menu_list_sel, 2);}
  404. void ProjectEx::AnimClip(ProjectEx &proj) {proj.animClip (proj.menu_list_sel);}
  405. void ProjectEx::AnimLinear(ProjectEx &proj) {proj.animLinear(proj.menu_list_sel, true );}
  406. void ProjectEx::AnimCubic(ProjectEx &proj) {proj.animLinear(proj.menu_list_sel, false);}
  407. void ProjectEx::AnimLoopOn(ProjectEx &proj) {proj.animLoop (proj.menu_list_sel, true );}
  408. void ProjectEx::AnimLoopOff(ProjectEx &proj) {proj.animLoop (proj.menu_list_sel, false);}
  409. void ProjectEx::SoundImportAs(ptr mode) {Proj.soundImportAs(Proj.menu_list_sel, SoundEditor::Import_as[intptr(mode)].codec, SoundEditor::Import_as[intptr(mode)].bit_rate);}
  410. void ProjectEx::SoundMulVolume(ProjectEx &proj) {MulSoundVolume.display(proj.menu_list_sel);}
  411. void ProjectEx::TransformSet(ProjectEx &proj) {proj.transformSet (proj.menu_list_sel);}
  412. void ProjectEx::TransformApply(ProjectEx &proj) {proj.transformApply (proj.menu_list_sel);}
  413. void ProjectEx::TransformBottom(ProjectEx &proj) {proj.transformBottom (proj.menu_list_sel);}
  414. void ProjectEx::TransformBack(ProjectEx &proj) {proj.transformBack (proj.menu_list_sel);}
  415. void ProjectEx::TransformCenter(ProjectEx &proj) {proj.transformCenter (proj.menu_list_sel);}
  416. void ProjectEx::TransformCenterXZ(ProjectEx &proj) {proj.transformCenterXZ (proj.menu_list_sel);}
  417. void ProjectEx::TransformRotYMinBox(ProjectEx &proj) {proj.transformRotYMinBox(proj.menu_list_sel);}
  418. void ProjectEx::MeshRemVtxTex1(ProjectEx &proj) {proj.removeMeshVtx (proj.menu_list_sel, VTX_TEX1 );}
  419. void ProjectEx::MeshRemVtxTex2(ProjectEx &proj) {proj.removeMeshVtx (proj.menu_list_sel, VTX_TEX2 );}
  420. void ProjectEx::MeshRemVtxTex12(ProjectEx &proj) {proj.removeMeshVtx (proj.menu_list_sel, VTX_TEX1|VTX_TEX2);}
  421. void ProjectEx::MeshRemVtxCol(ProjectEx &proj) {proj.removeMeshVtx (proj.menu_list_sel, VTX_COLOR);}
  422. void ProjectEx::MeshRemVtxSkin(ProjectEx &proj) {proj.removeMeshVtx (proj.menu_list_sel, VTX_SKIN );}
  423. void ProjectEx::SetBody(ProjectEx &proj) {proj.objSetBody (proj.menu_list_sel, ObjEdit.mesh_elm ? ObjEdit.mesh_elm->id : UIDZero);}
  424. void ProjectEx::columnVisible(int column, bool visible)
  425. {
  426. flt col_width=list.columnWidth(column); // get column width before it's hidden, because after it's hidden, its width may be unavailable
  427. list.columnsHidden(!list.file_size && !list.tex_sharpness);
  428. setListPadding();
  429. list.columnVisible(column, visible);
  430. flt region_width=rect().w();
  431. if(visible)outer_region.setRegionWidth(Max(region_width, list.columnOffset(column+1)+region.slidebarSize())); // make room to display entire column
  432. else outer_region.setRegionWidth( region_width- col_width ); // decrease size by column width
  433. if(list.columnsHidden())list.sort(0, false); // if there are no columns visible then restore original sorting
  434. }
  435. void ProjectEx::ShowFileSize(ProjectEx &proj) {proj.showFileSize();}
  436. void ProjectEx::showFileSize()
  437. {
  438. list.file_size^=1;
  439. getFileSizes();
  440. columnVisible(list.size_col, list.file_size);
  441. }
  442. void ProjectEx::ShowTexSharp(ProjectEx &proj) {proj.showTexSharp();}
  443. void ProjectEx::showTexSharp()
  444. {
  445. list.tex_sharpness^=1;
  446. TIG.getTexSharpnessFromProject();
  447. columnVisible(list.tex_sharp_col, list.tex_sharpness);
  448. }
  449. void ProjectEx::IncludeUnpublishedElmSize(ProjectEx &proj) {proj.includeUnpublishedElmSize(!proj.list.include_unpublished_elm_size);}
  450. void ProjectEx::includeUnpublishedElmSize(bool on)
  451. {
  452. if(list.include_unpublished_elm_size!=on)
  453. {
  454. list.include_unpublished_elm_size^=1;
  455. refresh(false, false);
  456. list_options.menu("Include Unpublished Element Size", on, QUIET);
  457. }
  458. }
  459. void ProjectEx::IncludeTextureSizeInObject(ProjectEx &proj) {proj.includeTextureSizeInObject(!proj.list.include_texture_size_in_object);}
  460. void ProjectEx::includeTextureSizeInObject(bool on)
  461. {
  462. if(list.include_texture_size_in_object!=on)
  463. {
  464. list.include_texture_size_in_object^=1;
  465. refresh(false, false);
  466. list_options.menu("Include Texture Size/In Objects", on, QUIET);
  467. }
  468. }
  469. void ProjectEx::checkIncludeChildrenSize()
  470. {
  471. list_options.menu("Include Children Size/Never" , list.ics==ElmList::ICS_NEVER , QUIET);
  472. list_options.menu("Include Children Size/When Folded", list.ics==ElmList::ICS_FOLDED, QUIET);
  473. list_options.menu("Include Children Size/Always" , list.ics==ElmList::ICS_ALWAYS, QUIET);
  474. }
  475. void ProjectEx::checkIncludeTextureSize()
  476. {
  477. list_options.menu("Include Texture Size/Element Only" , list.its==ElmList::ITS_ELM , QUIET);
  478. list_options.menu("Include Texture Size/Element+Texture", list.its==ElmList::ITS_ELM_TEX, QUIET);
  479. list_options.menu("Include Texture Size/Texture Only" , list.its==ElmList::ITS_TEX , QUIET);
  480. }
  481. void ProjectEx::IncludeChildrenSize(ptr ics) {Proj.includeChildrenSize(ElmList::INCLUDE_CHILDREN_SIZE(intptr(ics)));}
  482. void ProjectEx::includeChildrenSize(ElmList::INCLUDE_CHILDREN_SIZE ics)
  483. {
  484. if(InRange(ics, ElmList::ICS_NUM))
  485. {
  486. if(list.ics!=ics){list.ics=ics; refresh(false, false);}
  487. checkIncludeChildrenSize();
  488. }
  489. }
  490. void ProjectEx::IncludeTextureSize(ptr its) {Proj.includeTextureSize(ElmList::INCLUDE_TEXTURE_SIZE(intptr(its)));}
  491. void ProjectEx::includeTextureSize(ElmList::INCLUDE_TEXTURE_SIZE its)
  492. {
  493. if(InRange(its, ElmList::ITS_NUM))
  494. {
  495. if(list.its!=its){list.its=its; refresh(false, false);}
  496. checkIncludeTextureSize();
  497. }
  498. }
  499. void ProjectEx::ListOnlyFolder(ProjectEx &proj) {proj.list.show_only_folder^=1; proj.refresh(false, false);}
  500. void ProjectEx::ListOnlyObj(ProjectEx &proj) {proj.list.show_only_obj ^=1; proj.refresh(false, false);}
  501. void ProjectEx::ListOnlyMtrl(ProjectEx &proj) {proj.list.show_only_mtrl ^=1; proj.refresh(false, false);}
  502. void ProjectEx::ListOnlyAnim(ProjectEx &proj) {proj.list.show_only_anim ^=1; proj.refresh(false, false);}
  503. void ProjectEx::ListOnlySound(ProjectEx &proj) {proj.list.show_only_sound ^=1; proj.refresh(false, false);}
  504. void ProjectEx::ListFlat(ProjectEx &proj) {proj.list.flat_want ^=1; proj.refresh(false, false);}
  505. void ProjectEx::setListPadding()
  506. {
  507. list.padding.set(list.columnsHidden() ? list.elmHeight() : 0, region.rect().h()/2);
  508. }
  509. C ImagePtr& ProjectEx::elmIcon(ELM_TYPE type)
  510. {
  511. switch(type)
  512. {
  513. case ELM_NONE : return ImageNull;
  514. case ELM_FOLDER : return icon_folder;
  515. case ELM_OBJ : return icon_obj;
  516. case ELM_OBJ_CLASS : return icon_class;
  517. case ELM_MESH : return icon_mesh;
  518. case ELM_MTRL : return icon_mtrl;
  519. case ELM_WATER_MTRL : return icon_water_mtrl;
  520. case ELM_PHYS_MTRL : return icon_phys_mtrl;
  521. case ELM_ANIM : return icon_anim;
  522. case ELM_ENV : return icon_env;
  523. case ELM_WORLD : return icon_world;
  524. case ELM_MINI_MAP : return icon_mini_map;
  525. case ELM_ENUM : return icon_enum;
  526. case ELM_FONT : return icon_font;
  527. case ELM_TEXT_STYLE : return icon_ts;
  528. case ELM_PANEL_IMAGE: return icon_panel_image;
  529. case ELM_PANEL : return icon_panel;
  530. case ELM_GUI_SKIN : return icon_gui_skin;
  531. case ELM_GUI : return icon_gui;
  532. case ELM_IMAGE : return icon_image;
  533. case ELM_IMAGE_ATLAS: return icon_image_atlas;
  534. case ELM_ICON_SETTS : return icon_icon_setts;
  535. case ELM_ICON : return icon_icon;
  536. case ELM_SOUND : return icon_sound;
  537. case ELM_VIDEO : return icon_video;
  538. case ELM_LIB : return icon_lib;
  539. case ELM_APP : return icon_app;
  540. case ELM_CODE : return icon_code;
  541. default : return icon_file;
  542. }
  543. }
  544. bool ProjectEx::contains(C Elm &a, C Elm *b)C {return ::ProjectHierarchy::contains(a, b);}
  545. Enum* ProjectEx::getEnumFromName(C Str &enum_name)
  546. {
  547. REPA(existing_enums)if(Elm *elm=findElm(existing_enums[i]))if(elm->name==enum_name)return Enums(gamePath(elm->id));
  548. return null;
  549. }
  550. C UID& ProjectEx::curApp() // warning: this relies on 'existing_apps' being setup properly
  551. {
  552. if(existing_apps.binaryHas(app_id, Compare))return app_id; // if desired app exists
  553. if(list.app_checks.elms())return list.app_checks[0].app_id; // return first visible checkbox/app
  554. if(existing_apps .elms())return existing_apps [0]; // return any existing app
  555. return UIDZero; // no application
  556. }
  557. GuiSkinPtr ProjectEx::appGuiSkin()C {return gamePath(CodeEdit.appGuiSkin());}
  558. bool ProjectEx::includeTex(C UID &tex_id)
  559. {
  560. if(tex_id.valid())if(texs.binaryInclude(tex_id, Compare))
  561. {
  562. Importer.includeTex(tex_id);
  563. return true;
  564. }
  565. return false;
  566. }
  567. Elm* ProjectEx::getObjMeshElm(C UID &obj_id, bool send_to_server, bool set_gui)
  568. {
  569. Elm *mesh_elm=null;
  570. if(Elm *obj_elm=findElm(obj_id))
  571. if(ElmObj *obj_data=obj_elm->objData())
  572. {
  573. if(!obj_data->mesh_id.valid()) // it is not specified at the moment
  574. {
  575. if(set_gui)setListCurSel();
  576. UID mesh_id=obj_elm->id+ELM_OFFSET_MESH;
  577. mesh_elm=&getElm(mesh_id); if(mesh_elm->type && mesh_elm->type!=ELM_MESH)return null; // can't create because this ID is used by something else
  578. if(!mesh_elm->type) // new element
  579. {
  580. mesh_elm->setName(ElmNameMesh).setParent(obj_elm).type=ELM_MESH;
  581. if(ElmMesh *mesh_data=mesh_elm->meshData()){mesh_data->newData(); mesh_data->obj_id=obj_elm->id;}
  582. Mesh mesh; mesh.save(editPath(mesh_elm->id)); // save a dummy mesh
  583. makeGameVer(*mesh_elm); // save mesh first before the object
  584. if(send_to_server)Server.setElmFull(mesh_elm->id); // send to server
  585. }
  586. obj_data->newVer();
  587. obj_data->mesh_id=mesh_elm->id; // assign mesh to obj before calling 'setMeshPhys'
  588. makeGameVer(*obj_elm); // because we've assigned a mesh to this object, we need to resave this object too
  589. if(send_to_server)Server.setElmShort(obj_elm->id);
  590. if(set_gui)setList();
  591. REPAO(WorldEdit.objs).setMeshPhys(); // call at the end
  592. }else mesh_elm=findElm(obj_data->mesh_id);
  593. }
  594. return mesh_elm;
  595. }
  596. Elm* ProjectEx::getObjSkelElm(C UID &obj_id, bool send_to_server, bool set_gui)
  597. {
  598. Elm *skel_elm=null;
  599. if(Elm *mesh_elm=getObjMeshElm(obj_id, send_to_server, set_gui)) // we need to have a mesh to have a skeleton
  600. if(ElmMesh *mesh_data=mesh_elm->meshData())
  601. if(Elm *obj_elm=findElm(obj_id, ELM_OBJ))
  602. {
  603. if(!mesh_data->skel_id.valid()) // it is not specified at the moment
  604. {
  605. if(set_gui)setListCurSel();
  606. UID skel_id=obj_elm->id+ELM_OFFSET_SKEL;
  607. skel_elm=&getElm(skel_id); if(skel_elm->type && skel_elm->type!=ELM_SKEL)return null; // can't create because this ID is used by something else
  608. if(!skel_elm->type) // new element
  609. {
  610. skel_elm->setName(ElmNameSkel).setParent(mesh_elm).type=ELM_SKEL;
  611. if(ElmSkel *skel_data=skel_elm->skelData()){skel_data->newData(); skel_data->mesh_id=mesh_elm->id; skel_data->transform=mesh_data->transform;}
  612. Skeleton skel; skel.save(gamePath(skel_elm->id)); // save a dummy skel
  613. if(send_to_server)Server.setElmFull(skel_elm->id);
  614. }
  615. mesh_data->newVer();
  616. mesh_data->skel_id=skel_elm->id; // assign skel to mesh
  617. makeGameVer(*mesh_elm); // because we've assigned a skel to this mesh, we need to resave this mesh too
  618. if(send_to_server)Server.setElmShort(mesh_elm->id);
  619. if(set_gui)setList();
  620. }else skel_elm=findElm(mesh_data->skel_id);
  621. }
  622. return skel_elm;
  623. }
  624. Elm* ProjectEx::getObjPhysElm(C UID &obj_id, bool send_to_server, bool set_gui)
  625. {
  626. Elm *phys_elm=null;
  627. if(Elm *mesh_elm=getObjMeshElm(obj_id, send_to_server, set_gui)) // we need to have a mesh to have a phys body
  628. if(ElmMesh *mesh_data=mesh_elm->meshData())
  629. if(Elm *obj_elm=findElm(obj_id, ELM_OBJ))
  630. {
  631. if(!mesh_data->phys_id.valid()) // it is not specified at the moment
  632. {
  633. if(set_gui)setListCurSel();
  634. UID phys_id=obj_elm->id+ELM_OFFSET_PHYS;
  635. phys_elm=&getElm(phys_id); if(phys_elm->type && phys_elm->type!=ELM_PHYS)return null; // can't create because this ID is used by something else
  636. if(!phys_elm->type) // new element
  637. {
  638. phys_elm->setName(ElmNamePhys).setParent(mesh_elm).type=ELM_PHYS;
  639. if(ElmPhys *phys_data=phys_elm->physData()){phys_data->newData(); phys_data->mesh_id=mesh_elm->id; phys_data->transform=mesh_data->transform;}
  640. PhysBody phys; phys.save(gamePath(phys_elm->id)); // save a dummy phys body
  641. if(send_to_server)Server.setElmFull(phys_elm->id);
  642. }
  643. mesh_data->newVer();
  644. mesh_data->phys_id=phys_elm->id; // assign phys to mesh
  645. makeGameVer(*obj_elm); // because we've assigned a phys to this object, we need to resave this object too
  646. if(send_to_server)Server.setElmShort(mesh_elm->id);
  647. if(set_gui)setList();
  648. }else phys_elm=findElm(mesh_data->phys_id);
  649. }
  650. return phys_elm;
  651. }
  652. void ProjectEx::setNewElm(Node<MenuElm> &n, C Str &prefix)
  653. {
  654. n.New().create(prefix+ElmTypeName[ELM_FOLDER] , NewFolder , T).desc(ElmTypeDesc[ELM_FOLDER]);
  655. n.New().create(prefix+ElmTypeName[ELM_ENUM] , NewEnum , T).desc(ElmTypeDesc[ELM_ENUM]);
  656. n.New().create(prefix+ElmTypeName[ELM_IMAGE] , NewImage , T).desc(ElmTypeDesc[ELM_IMAGE]);
  657. n.New().create(prefix+ElmTypeName[ELM_IMAGE_ATLAS], NewImageAtlas , T).desc(ElmTypeDesc[ELM_IMAGE_ATLAS]);
  658. n.New().create(prefix+ElmTypeName[ELM_FONT] , NewFont , T).desc(ElmTypeDesc[ELM_FONT]);
  659. n.New().create(prefix+ElmTypeName[ELM_TEXT_STYLE] , NewTextStyle , T).desc(ElmTypeDesc[ELM_TEXT_STYLE]);
  660. n.New().create(prefix+ElmTypeName[ELM_PANEL_IMAGE], NewPanelImage , T).desc(ElmTypeDesc[ELM_PANEL_IMAGE]);
  661. n.New().create(prefix+ElmTypeName[ELM_PANEL] , NewPanel , T).desc(ElmTypeDesc[ELM_PANEL]);
  662. n.New().create(prefix+ElmTypeName[ELM_GUI_SKIN] , NewGuiSkin , T).desc(ElmTypeDesc[ELM_GUI_SKIN]);
  663. n.New().create(prefix+ElmTypeName[ELM_GUI] , NewGui , T).desc(ElmTypeDesc[ELM_GUI]);
  664. n.New().create(prefix+ElmTypeName[ELM_MTRL] , NewMtrl , T).desc(ElmTypeDesc[ELM_MTRL]);
  665. n.New().create(prefix+ElmTypeName[ELM_WATER_MTRL] , NewWaterMtrl , T).desc(ElmTypeDesc[ELM_WATER_MTRL]);
  666. n.New().create(prefix+ElmTypeName[ELM_PHYS_MTRL] , NewPhysMtrl , T).desc(ElmTypeDesc[ELM_PHYS_MTRL]);
  667. n.New().create(prefix+ElmTypeName[ELM_ANIM] , NewAnim , T).desc(ElmTypeDesc[ELM_ANIM]);
  668. n.New().create(prefix+ElmTypeName[ELM_OBJ_CLASS] , NewObjectClass, T).desc(ElmTypeDesc[ELM_OBJ_CLASS]);
  669. n.New().create(prefix+ElmTypeName[ELM_OBJ] , NewObject , T).desc(ElmTypeDesc[ELM_OBJ]);
  670. n.New().create(prefix+ElmTypeName[ELM_ICON_SETTS] , NewIconSetts , T).desc(ElmTypeDesc[ELM_ICON_SETTS]);
  671. n.New().create(prefix+ElmTypeName[ELM_ICON] , NewIcon , T).desc(ElmTypeDesc[ELM_ICON]);
  672. n.New().create(prefix+ElmTypeName[ELM_ENV] , NewEnv , T).desc(ElmTypeDesc[ELM_ENV]);
  673. n.New().create(prefix+ElmTypeName[ELM_WORLD] , NewWorld , T).desc(ElmTypeDesc[ELM_WORLD]);
  674. n.New().create(prefix+ElmTypeName[ELM_MINI_MAP] , NewMiniMap , T).desc(ElmTypeDesc[ELM_MINI_MAP]);
  675. n.New().create(prefix+ElmTypeName[ELM_SOUND] , NewSound , T).desc(ElmTypeDesc[ELM_SOUND]);
  676. n.New().create(prefix+ElmTypeName[ELM_VIDEO] , NewVideo , T).desc(ElmTypeDesc[ELM_VIDEO]);
  677. n.New().create(prefix+ElmTypeName[ELM_FILE] , NewRawFile , T).desc(ElmTypeDesc[ELM_FILE]);
  678. n.New().create(prefix+ElmTypeName[ELM_LIB] , NewLib , T).desc(ElmTypeDesc[ELM_LIB]);
  679. n.New().create(prefix+ElmTypeName[ELM_APP] , NewApp , T).desc(ElmTypeDesc[ELM_APP]);
  680. n.New().create(prefix+ElmTypeName[ELM_CODE] , NewCode , T).desc(ElmTypeDesc[ELM_CODE]);
  681. }
  682. void ProjectEx::disableMenu(Menu &menu)
  683. {
  684. if(menu.hidden())Gui-=menu; // if the menu got hidden then unlink it from Gui, so its keyboard shortcuts won't be used
  685. }
  686. void ProjectEx::updateMenu()
  687. {
  688. disableMenu(list.menu);
  689. }
  690. void ProjectEx::create()
  691. {
  692. Gui+=outer_region.create().hide();
  693. {
  694. Node<MenuElm> n;
  695. n.New().create("Show File Size", ShowFileSize, T).flag(MENU_TOGGLABLE);
  696. Node<MenuElm> &children=(n+="Include Children Size");
  697. children.New().create("Never" , IncludeChildrenSize, ptr(ElmList::ICS_NEVER )).flag(MENU_TOGGLABLE);
  698. children.New().create("When Folded", IncludeChildrenSize, ptr(ElmList::ICS_FOLDED)).flag(MENU_TOGGLABLE);
  699. children.New().create("Always" , IncludeChildrenSize, ptr(ElmList::ICS_ALWAYS)).flag(MENU_TOGGLABLE);
  700. Node<MenuElm> &texture=(n+="Include Texture Size");
  701. texture.New().create("Element Only" , IncludeTextureSize, ptr(ElmList::ITS_ELM )).flag(MENU_TOGGLABLE);
  702. texture.New().create("Element+Texture", IncludeTextureSize, ptr(ElmList::ITS_ELM_TEX)).flag(MENU_TOGGLABLE);
  703. texture.New().create("Texture Only" , IncludeTextureSize, ptr(ElmList::ITS_TEX )).flag(MENU_TOGGLABLE);
  704. texture++;
  705. texture.New().create("In Objects", IncludeTextureSizeInObject, T).flag(MENU_TOGGLABLE).desc("Normally Texture File Sizes are included only in Material Elements.\nThis option will include Texture File Sizes additionally in Objects that reference them.");
  706. n.New().create("Include Unpublished Element Size", IncludeUnpublishedElmSize, T).flag(MENU_TOGGLABLE).desc("If include file size of elements that have Disabled Publishing");
  707. n.New().create("Show Texture Sharpness", ShowTexSharp, T).flag(MENU_TOGGLABLE).desc("Sharpness is calculated by comparing first and second mip-maps of a Material Texture");
  708. n++;
  709. n.New().create("List Only Folders" , ListOnlyFolder, T).flag(MENU_TOGGLABLE);
  710. n.New().create("List Only Objects" , ListOnlyObj , T).flag(MENU_TOGGLABLE);
  711. n.New().create("List Only Materials" , ListOnlyMtrl , T).flag(MENU_TOGGLABLE);
  712. n.New().create("List Only Animations", ListOnlyAnim , T).flag(MENU_TOGGLABLE);
  713. n.New().create("List Only Sounds" , ListOnlySound , T).flag(MENU_TOGGLABLE);
  714. n.New().create("List Flat" , ListFlat , T).flag(MENU_TOGGLABLE);
  715. n++;
  716. Node<MenuElm> &New=(n+="New"); setNewElm(New);
  717. outer_region+=list_options.create().setData(n).focusable(false); list_options.flag|=COMBOBOX_CONST_TEXT;
  718. checkIncludeChildrenSize();
  719. checkIncludeTextureSize();
  720. }
  721. outer_region+=show_removed.create().func(ShowRemoved, T).focusable(false).desc("Show removed elements\nKeyboard Shortcut: Alt+R"); show_removed.image="Gui/Misc/trash.img"; show_removed.mode=BUTTON_TOGGLE;
  722. outer_region+=theater .create().setImage("Gui/Misc/theater.img").func(ShowTheater, T).focusable(false).desc("Enable Theater mode allowing to preview multiple elements at the same time\nKeyboard Shortcut: Alt+4"); theater.mode=BUTTON_TOGGLE;
  723. outer_region+=filter.create().func(FilterChanged, T).desc(S+"Find element\nType element name or its ID to find it.\n\nKeyboard Shortcut: "+Kb.ctrlCmdName()+"+Shift+F"); filter.reset.show(); filter.show_find=true; filter.hint=S+"Find ("+KbSc(KB_F, KBSC_CTRL_CMD|KBSC_SHIFT).asText()+')';
  724. outer_region+=region.create();
  725. ListColumn lc[]=
  726. {
  727. ListColumn(MEMBER(ListElm, opened_icon), 0 , S ), // 0 "Opened"
  728. ListColumn(MEMBER(ListElm, icon ), 0 , S ), // 1 "Icon"
  729. ListColumn(MEMBER(ListElm, name ), LCW_DATA, "Name" ), // 2 Name
  730. ListColumn( ListElm:: Size , 0.2f , "Size" ), // 3 File Size, don't use LCW_DATA because that could be slow because ListElm tex size calculation is slow
  731. ListColumn( ListElm:: TexSharp , LCW_DATA, "Texture Sharpness"), // 4 Texture Sharpness
  732. }; list.icon_col=1; list.name_col=2; list.size_col=3; list.tex_sharp_col=4;
  733. lc[0].md.setCompareFunc(ListElm::CompareIndex );
  734. lc[1].md.setCompareFunc(ListElm::CompareIndex );
  735. lc[2].md.setCompareFunc(ListElm::CompareName );
  736. lc[3].md.setCompareFunc(ListElm::CompareSize );
  737. lc[4].md.setCompareFunc(ListElm::CompareTexSharp);
  738. region+=list.create(lc, Elms(lc), true);
  739. for(int i=list.name_col+1; i<list.columns(); i++)list.columnVisible(i, false);
  740. list.flag|=LIST_MULTI_SEL; list.draw_column=-1;
  741. list.setElmOffset(MEMBER(ListElm, offset)).setElmTextColor(MEMBER(ListElm, color)).setElmDesc(MEMBER(ListElm, desc));
  742. list.sound_play.create().func(SoundPlay, T).focusable(false).skin=&EmptyGuiSkin;
  743. icon_folder ="Gui/Elm/folder.img";
  744. icon_file ="Gui/Elm/file.img";
  745. icon_obj ="Gui/Elm/mesh.img";
  746. icon_class ="Gui/Elm/class.img";
  747. icon_mesh ="Gui/Elm/mesh.img";
  748. icon_mtrl ="Gui/Elm/material.img";
  749. icon_water_mtrl ="Gui/Elm/water_material.img";
  750. icon_phys_mtrl ="Gui/Elm/physics_material.img";
  751. icon_anim ="Gui/Elm/animation.img";
  752. icon_env ="Gui/Elm/environment.img";
  753. icon_world ="Gui/Elm/world.img";
  754. icon_mini_map ="Gui/Elm/mini_map.img";
  755. icon_enum ="Gui/Elm/enum.img";
  756. icon_image ="Gui/Elm/image.img";
  757. icon_image_atlas="Gui/Elm/image_atlas.img";
  758. icon_icon_setts ="Gui/Elm/icon_settings.img";
  759. icon_icon ="Gui/Elm/icon.img";
  760. icon_font ="Gui/Elm/font.img";
  761. icon_ts ="Gui/Elm/text.img";
  762. icon_panel_image="Gui/Elm/panel_image.img";
  763. icon_panel ="Gui/Elm/panel.img";
  764. icon_gui_skin ="Gui/Elm/gui_skin.img";
  765. icon_gui ="Gui/Elm/gui.img";
  766. icon_sound ="Gui/Elm/sound.img";
  767. icon_video ="Gui/Elm/video.img";
  768. icon_lib ="Gui/Elm/library.img";
  769. icon_app ="Gui/Elm/application.img";
  770. icon_code ="Gui/Elm/file.img";
  771. icon_play ="Gui/arrow_right_big.img";
  772. icon_stop ="Gui/Misc/stop.img";
  773. arrow_right ="Gui/arrow_right_big.img";
  774. arrow_down ="Gui/arrow_down_big.img";
  775. warning ="Gui/Misc/warning.img";
  776. exclamation ="Gui/Misc/exclamation.img";
  777. icon_light ="Gui/Misc/light.img";
  778. icon_particles ="Gui/Misc/particles.img";
  779. // menu
  780. Node<MenuElm> menu;
  781. ObjEdit.setMenu(menu, "OE ");
  782. AnimEdit.setMenu(menu, "AE ");
  783. WorldEdit.setMenu(menu, "WE ");
  784. Gui+=T.menu.create(menu).hide().disabled(true);
  785. }
  786. bool ProjectEx::testElmsNum()
  787. {
  788. if(Demo && elms.elms()>=MaxDemoProjElms)
  789. {
  790. Str server;
  791. if(Server.loggedIn()){Server.logout(); server="\nYou have been disconnected from the server.";}
  792. Gui.msgBox(S, S+"Demo version is limited to "+MaxDemoProjElms+" elements (including removed) in a project."+server);
  793. return false;
  794. }
  795. return true;
  796. }
  797. void ProjectEx::explore(Elm &elm)
  798. {
  799. Explore((elm.type==ELM_APP) ? CodeEdit.appPath(elm.name) : elmSrcFileFirst(&elm));
  800. }
  801. Str ProjectEx::newElmName(ELM_TYPE type, C UID &parent_id)
  802. {
  803. Elm *parent =findElm(parent_id);
  804. int parent_i=elms.validIndex(parent);
  805. ElmNode &parent_n=(InRange(parent_i, hierarchy) ? hierarchy[parent_i] : root);
  806. FREP(1000)
  807. {
  808. Str name =ElmTypeName[type]; if(i)name.space()+=i;
  809. bool exists=false; FREPA(parent_n.children)if(name==elms[parent_n.children[i]].name){exists=true; break;}
  810. if( !exists)return name;
  811. }
  812. return S;
  813. }
  814. void ProjectEx::newElm(ELM_TYPE type)
  815. {
  816. setMenuListSel();
  817. newElm(type, menu_list_sel.elms() ? menu_list_sel[0] : UIDZero);
  818. }
  819. Elm* ProjectEx::newElm(ELM_TYPE type, C UID &parent_id, C Str *name, bool refresh_elm_list)
  820. {
  821. if(InRange(type, ELM_NUM) && type!=ELM_NONE && type!=ELM_MESH && type!=ELM_SKEL && type!=ELM_PHYS && type!=ELM_SHADER && type!=ELM_WORLD
  822. && testElmsNum())
  823. {
  824. if(refresh_elm_list)setListCurSel();
  825. Str elm_name=(name ? *name : newElmName(type, parent_id));
  826. Elm *parent=findElm(parent_id); if(parent)parent->opened(true);
  827. Elm &elm=::Project::newElm(elm_name, parent_id, type);
  828. switch(type)
  829. {
  830. case ELM_CODE:
  831. {
  832. Str code; SaveCode(code, codePath(elm));
  833. if(ElmCode *data=elm.codeData()){data->newData(); data->from(code);}
  834. Server.setElmFull(elm.id);
  835. }break;
  836. case ELM_MTRL:
  837. {
  838. EditMaterial edit; edit.newData(); Save(edit, editPath(elm));
  839. if(ElmMaterial *data=elm.mtrlData()){data->newData(); data->from(edit);}
  840. makeGameVer(elm);
  841. Server.setElmFull(elm.id);
  842. }break;
  843. case ELM_WATER_MTRL:
  844. {
  845. EditWaterMtrl edit; edit.newData(); Save(edit, editPath(elm));
  846. if(ElmWaterMtrl *data=elm.waterMtrlData()){data->newData(); data->from(edit);}
  847. makeGameVer(elm);
  848. Server.setElmFull(elm.id);
  849. }break;
  850. case ELM_PHYS_MTRL:
  851. {
  852. EditPhysMtrl edit; edit.newData(); Save(edit, editPath(elm));
  853. if(ElmPhysMtrl *data=elm.physMtrlData()){data->newData(); data->from(edit);}
  854. makeGameVer(elm);
  855. Server.setElmFull(elm.id);
  856. }break;
  857. case ELM_FONT:
  858. {
  859. EditFont edit; edit.newData(); Save(edit, editPath(elm));
  860. if(ElmFont *data=elm.fontData()){data->newData(); data->from(edit);}
  861. elmReload(elm.id, false, false);
  862. Server.setElmFull(elm.id);
  863. }break;
  864. case ELM_PANEL_IMAGE:
  865. {
  866. EditPanelImage edit; edit.newData( ); Save(edit, editPath(elm));
  867. PanelImage game; edit.make(game, WorkerThreads, T); Save(game, gamePath(elm)); savedGame(elm); // makeGameVer(elm);
  868. if(ElmPanelImage *data=elm.panelImageData()){data->newData(); data->from(edit);}
  869. Server.setElmFull(elm.id);
  870. }break;
  871. case ELM_PANEL:
  872. {
  873. EditPanel edit; edit.newData(); Save(edit, editPath(elm));
  874. if(ElmPanel *data=elm.panelData()){data->newData(); data->from(edit);}
  875. makeGameVer(elm);
  876. Server.setElmFull(elm.id);
  877. }break;
  878. case ELM_GUI_SKIN:
  879. {
  880. EditGuiSkin edit; edit.newData(); Save(edit, editPath(elm));
  881. if(ElmGuiSkin *data=elm.guiSkinData()){data->newData(); data->from(edit);}
  882. makeGameVer(elm);
  883. Server.setElmFull(elm.id);
  884. }break;
  885. case ELM_TEXT_STYLE:
  886. {
  887. EditTextStyle edit; edit.newData(); Save(edit, editPath(elm));
  888. if(ElmTextStyle *data=elm.textStyleData()){data->newData(); data->from(edit);}
  889. makeGameVer(elm);
  890. Server.setElmFull(elm.id);
  891. }break;
  892. case ELM_ENV:
  893. {
  894. EditEnv edit; edit.newData(); Save(edit, editPath(elm));
  895. if(ElmEnv *data=elm.envData()){data->newData(); data->from(edit);}
  896. makeGameVer(elm);
  897. Server.setElmFull(elm.id);
  898. }break;
  899. case ELM_OBJ_CLASS:
  900. {
  901. EditObject edit; edit.newData(); edit.setType(true, elm.id, edit_path).setAccess(true, OBJ_ACCESS_CUSTOM); Save(edit, editPath(elm.id)); // type must be set at the moment of creation, also since this is an object class, then make all objects based on this have OBJ_ACCESS_CUSTOM access
  902. if(ElmObjClass *data=elm.objClassData()){data->newData(); data->from(edit);}
  903. makeGameVer(elm);
  904. Server.setElmFull(elm.id);
  905. }break;
  906. case ELM_OBJ:
  907. {
  908. EditObject edit; edit.newData(); Save(edit, editPath(elm.id));
  909. if(ElmObj *data=elm.objData()){data->newData(); data->from(edit);}
  910. makeGameVer(elm);
  911. Server.setElmFull(elm.id);
  912. }break;
  913. case ELM_ANIM:
  914. {
  915. Animation anim; anim.length(1, false);
  916. if(ElmAnim *data=elm.animData())
  917. {
  918. data->newData(); data->from(anim);
  919. if(parent && parent->type==ELM_OBJ)data->skel_id=objToSkel(parent);
  920. if(Elm *skel=findElm(data->skel_id))if(ElmSkel *skel_data=skel->skelData())data->transform=skel_data->transform;
  921. anim.linear(data->linear()).loop(data->loop());
  922. }
  923. anim.save(gamePath(elm)); savedGame(elm);
  924. Server.setElmFull(elm.id);
  925. }break;
  926. case ELM_IMAGE:
  927. {
  928. if(ElmImage *data=elm.imageData())
  929. {
  930. data->newData();
  931. data->mipMaps(CodeEdit.importImageMipMaps());
  932. }
  933. makeGameVer(elm); Server.setElmFull(elm.id);
  934. }break;
  935. case ELM_ENUM: {if(ElmData *data=elm.Data())data->newData(); makeGameVer(elm); Server.setElmFull(elm.id);} break;
  936. case ELM_GUI : {if(ElmGui *data=elm. guiData())data->newData(); GuiObjs go; go .save(gamePath(elm)); savedGame(elm); Server.setElmFull(elm.id);} break; // currently gui doesn't have edit version
  937. case ELM_ICON : {if(ElmIcon *data=elm. iconData())data->newData(); Image image; image.save(gamePath(elm)); savedGame(elm); Server.setElmFull(elm.id);} break; // save empty game image (edit can be left empty because it will be set to default)
  938. case ELM_IMAGE_ATLAS: {if(ElmImageAtlas *data=elm.imageAtlasData())data->newData(); ImageAtlas atlas; atlas.save(gamePath(elm)); savedGame(elm); Server.setElmFull(elm.id);} break; // save empty game atlas
  939. case ELM_SOUND: {if(ElmData *data=elm.Data())data->newData(); File f; f.writeTry(gamePath(elm)); f.del(); savedGame(elm); Server.setElmFull(elm.id);} break;
  940. case ELM_VIDEO: {if(ElmData *data=elm.Data())data->newData(); File f; f.writeTry(gamePath(elm)); f.del(); savedGame(elm); Server.setElmFull(elm.id);} break;
  941. case ELM_FILE : {if(ElmData *data=elm.Data())data->newData(); File f; f.writeTry(gamePath(elm)); f.del(); savedGame(elm); Server.setElmFull(elm.id);} break;
  942. case ELM_MINI_MAP:
  943. {
  944. if(MiniMapVer *ver=miniMapVerRequire(elm.id))
  945. {
  946. ver->changed=true;
  947. ver->time++;
  948. if(ElmMiniMap *data=elm.miniMapData()){data->newData(); data->copyTo(ver->settings);}
  949. createMiniMapPaths(elm.id); ver->settings.save(gamePath(elm.id).tailSlash(true)+"Settings");
  950. Server.setElmFull(elm.id);
  951. Server.setMiniMapSettings(elm.id, ver->settings, ver->time);
  952. }
  953. }break;
  954. default:
  955. {
  956. if(ElmData *data=elm.Data())data->newVer();
  957. Server.newElm(elm);
  958. }break;
  959. }
  960. if(refresh_elm_list)setList();
  961. if(type==ELM_CODE)activateSources(-1); // call after 'setList'
  962. if(!name) // name is only provided through Editor Network Interface or Drag and Drop file Importing
  963. {
  964. elmLocate(elm.id, true);
  965. RenameElm.activate(elm.id, elm_name);
  966. if(ElmChange *change=elm_undos.set(null, true))
  967. {
  968. change->type=ElmChange::RESTORE;
  969. change->name="New Element";
  970. change->elms.binaryInclude(elm.id, Compare);
  971. }
  972. }
  973. return &elm;
  974. }
  975. return null;
  976. }
  977. Elm* ProjectEx::newWorld(C Str &name, int area_size, int hm_res, C UID &parent_id, bool set_cur)
  978. {
  979. area_size=NearestPow2(area_size);
  980. hm_res =NearestPow2( hm_res );
  981. if(area_size>=32 && area_size<=128
  982. && hm_res >=32 && hm_res <=128)
  983. {
  984. setListCurSel();
  985. Elm *parent=findElm(parent_id); if(parent)parent->opened(true);
  986. Elm &elm=::Project::newElm(name, parent_id, ELM_WORLD);
  987. if(ElmWorld *ew=elm.worldData())
  988. {
  989. ew->newData();
  990. ew->area_size=area_size;
  991. ew-> hm_res = hm_res ;
  992. }
  993. makeGameVer(elm);
  994. Server.setElmFull(elm.id);
  995. setList();
  996. if(set_cur)list.setCur(list.elmToVis(&elm));
  997. return &elm;
  998. }
  999. return null;
  1000. }
  1001. void ProjectEx::setMtrl(Elm &mtrl, ImporterClass::Import::MaterialEx &src, C Str &src_file)
  1002. {
  1003. if(ElmMaterial *data=mtrl.mtrlData())
  1004. {
  1005. elmChanging(mtrl);
  1006. if(src.base_0 .is() && includeTex(src. base_0_id))saveTex(src.base_0 , src. base_0_id); Server.setTex(src. base_0_id);
  1007. if(src.base_1 .is() && includeTex(src. base_1_id))saveTex(src.base_1 , src. base_1_id); Server.setTex(src. base_1_id);
  1008. if(src.detail .is() && includeTex(src. detail_id))saveTex(src.detail , src. detail_id); Server.setTex(src. detail_id);
  1009. if(src.macro .is() && includeTex(src. macro_id))saveTex(src.macro , src. macro_id); Server.setTex(src. macro_id);
  1010. if(src.reflection.is() && includeTex(src.reflection_id))saveTex(src.reflection, src.reflection_id); Server.setTex(src.reflection_id);
  1011. if(src.light .is() && includeTex(src. light_id))saveTex(src.light , src. light_id); Server.setTex(src. light_id);
  1012. TimeStamp time; time.getUTC();
  1013. EditMaterial edit_mtrl; src.copyTo(edit_mtrl, time);
  1014. Save(edit_mtrl, editPath(mtrl));
  1015. makeGameVer(mtrl);
  1016. data->newVer();
  1017. data->setSrcFile(src_file, time);
  1018. data->from(edit_mtrl);
  1019. elmChanged(mtrl);
  1020. }
  1021. }
  1022. Elm& ProjectEx::newMtrl(ImporterClass::Import::MaterialEx &src, C UID parent_id, C Str &src_file) // create new material from 'src' !! this doesn't set elm list and doesn't send to the server !!
  1023. {
  1024. Elm &mtrl=::Project::newElm(src.name, parent_id, ELM_MTRL); mtrl.mtrlData()->newData();
  1025. setMtrl(mtrl, src, src_file);
  1026. return mtrl;
  1027. }
  1028. void ProjectEx::setElmName(Elm &elm, C Str &name, C TimeStamp &time)
  1029. {
  1030. elm.setName(name, time);
  1031. switch(elm.type)
  1032. {
  1033. case ELM_ENUM: makeGameVer(elm); break; // this relies on element name
  1034. }
  1035. switch(elm.type)
  1036. {
  1037. case ELM_ENUM :
  1038. case ELM_OBJ_CLASS: enumChanged(elm.id); break;
  1039. case ELM_FONT : fontChanged(); break;
  1040. case ELM_CODE : CodeEdit.sourceRename(elm.id); break;
  1041. case ELM_APP : CodeEdit.makeAuto(); break;
  1042. }
  1043. }
  1044. void ProjectEx::setElmNames(Memc<Edit::IDParam<Str> > &elms, bool adjust_elms) // 'adjust_elms'=if this is performed because of undo, and in that case we need to remember current names, so we can undo this change later
  1045. {
  1046. if(elms.elms())
  1047. {
  1048. TimeStamp time; time.getUTC();
  1049. Str name;
  1050. FREPA(elms)if(Elm *elm=findElm(elms[i].id)) // get element
  1051. {
  1052. name=elms[i].value; // remember name to change to
  1053. if(adjust_elms)elms[i].value=elm->name; // !! if we're adjusting, then set current name !!
  1054. setElmName(*elm, name, time);
  1055. Server.renameElm(*elm);
  1056. }
  1057. refresh(false, false);
  1058. }
  1059. }
  1060. void ProjectEx::renameElm(C UID &elm_id, C Str &name)
  1061. {
  1062. if(Elm *elm=findElm(elm_id))
  1063. if(!Equal(elm->name, name, true))
  1064. {
  1065. setElmName(*elm, name);
  1066. Server.renameElm(*elm);
  1067. refresh(false, false);
  1068. }
  1069. }
  1070. void ProjectEx::replaceName(C MemPtr<UID> &elm_ids, C Str &from, C Str &to)
  1071. {
  1072. if(elm_ids.elms())
  1073. {
  1074. ElmChange *change=elm_undos.set(null, true); if(!change)return;
  1075. change->type=ElmChange::SET_NAME;
  1076. change->name="Replace Name";
  1077. Str name;
  1078. FREPA(elm_ids)if(Elm *elm=findElm(elm_ids[i]))
  1079. {
  1080. name=Replace(elm->name, from, to); if(!Equal(elm->name, name, true))
  1081. {
  1082. Edit::IDParam<Str> &elm_name=change->elm_names.New();
  1083. elm_name.id =elm->id;
  1084. elm_name.value=name;
  1085. }
  1086. }
  1087. setElmNames(change->elm_names, true);
  1088. }
  1089. }
  1090. void ProjectEx::closeElm(C UID &elm_id) // close elements that keep handles to files (sounds/vidoes)
  1091. {
  1092. if(elm_id==list.sound_play.play_id)sound.close();
  1093. Preview.closeElm(elm_id);
  1094. SoundEdit.closeElm(elm_id);
  1095. VideoEdit.closeElm(elm_id);
  1096. }
  1097. void ProjectEx::paramEditObjChanged(C UID *obj_id)
  1098. {
  1099. ObjEdit.param_edit.objChanged(obj_id);
  1100. WorldEdit.param_edit.objChanged(obj_id);
  1101. ObjClassEdit.objChanged(obj_id);
  1102. }
  1103. void ProjectEx::remove(ElmNode &node, Memc<UID> &ids, Memc<UID> &removed, C TimeStamp &time) // process recursively to remove only parents without their children
  1104. {
  1105. FREPA(node.children)
  1106. {
  1107. int child_i=node.children[i];
  1108. ElmNode &child =hierarchy[child_i];
  1109. Elm &elm =elms [child_i];
  1110. if(!elm.removed()) // if exists
  1111. {
  1112. if(ids.has(elm.id)) // if want to remove
  1113. {
  1114. if(time>elm.removed_time){elm.setRemoved(true, time); removed.add(elm.id);} // remove self, but skip the children
  1115. }else // continue checking children
  1116. {
  1117. remove(child, ids, removed, time);
  1118. }
  1119. }
  1120. }
  1121. }
  1122. void ProjectEx::remove(Memc<UID> &ids, bool parents_only, bool set_undo)
  1123. {
  1124. if(ids.elms())
  1125. {
  1126. setListCurSel();
  1127. TimeStamp time; time.getUTC(); Memc<UID> removed;
  1128. if(parents_only)remove(root, ids, removed, time);else
  1129. REPA(ids)if(Elm *elm=findElm(ids[i]))if(!elm->removed() && time>elm->removed_time){elm->setRemoved(true, time); removed.add(elm->id);}
  1130. if(set_undo)if(ElmChange *change=elm_undos.set(null, true))
  1131. {
  1132. change->type=ElmChange::REMOVE;
  1133. change->name="Remove";
  1134. FREPA(removed)change->elms.binaryInclude(removed[i], Compare);
  1135. }
  1136. Server.removeElms(removed, true, time);
  1137. setList(false);
  1138. activateSources(); // rebuild sources if needed
  1139. WorldEdit.validateRefs();
  1140. paramEditObjChanged(); // removed elements should display in red in param list
  1141. }
  1142. }
  1143. void ProjectEx::restore(Memc<UID> &ids, bool set_undo)
  1144. {
  1145. if(ids.elms())
  1146. {
  1147. setListCurSel();
  1148. TimeStamp time; time.getUTC(); Memc<UID> restored;
  1149. REPA(ids)if(Elm *elm=findElm(ids[i]))if(elm->removed() && time>elm->removed_time){elm->setRemoved(false, time); restored.add(elm->id);}
  1150. if(set_undo)if(ElmChange *change=elm_undos.set(null, true))
  1151. {
  1152. change->type=ElmChange::RESTORE;
  1153. change->name="Restore";
  1154. FREPA(restored)change->elms.binaryInclude(restored[i], Compare);
  1155. }
  1156. Server.removeElms(restored, false, time);
  1157. setList(false);
  1158. Importer.investigate(ids); // call after setting list because may rely on hierarchy
  1159. activateSources(); // rebuild sources if needed
  1160. WorldEdit.validateRefs();
  1161. paramEditObjChanged(); // removed elements should display in red in param list
  1162. }
  1163. }
  1164. void ProjectEx::disablePublish(ElmNode &node, Memc<UID> &ids, Memc<UID> &processed, C TimeStamp &time) // process recursively to disable only parents without their children
  1165. {
  1166. FREPA(node.children)
  1167. {
  1168. int child_i=node.children[i];
  1169. ElmNode &child =hierarchy[child_i];
  1170. Elm &elm =elms [child_i];
  1171. if(!elm.noPublish()) // if has publishing enabled
  1172. {
  1173. if(ids.has(elm.id)) // if want to change
  1174. {
  1175. if(time>elm.no_publish_time){elm.setNoPublish(true, time); processed.add(elm.id);} // disable self, but skip the children
  1176. }else // continue checking children
  1177. {
  1178. disablePublish(child, ids, processed, time);
  1179. }
  1180. }
  1181. }
  1182. }
  1183. void ProjectEx::disablePublish(Memc<UID> &ids, bool parents_only, bool set_undo)
  1184. {
  1185. if(ids.elms())
  1186. {
  1187. TimeStamp time; time.getUTC(); Memc<UID> processed;
  1188. if(parents_only)disablePublish(root, ids, processed, time);else
  1189. REPA(ids)if(Elm *elm=findElm(ids[i]))if(!elm->noPublish() && time>elm->no_publish_time){elm->setNoPublish(true, time); processed.add(elm->id);}
  1190. if(set_undo)if(ElmChange *change=elm_undos.set(null, true))
  1191. {
  1192. change->type=ElmChange::PUBLISH_DISABLE;
  1193. change->name="Disable Publishing";
  1194. FREPA(processed)change->elms.binaryInclude(processed[i], Compare);
  1195. }
  1196. Server.noPublishElms(processed, true, time);
  1197. refresh(false);
  1198. activateSources(); // rebuild sources if needed
  1199. WorldEdit.validateRefs();
  1200. paramEditObjChanged(); // removed elements should display in red in param list
  1201. }
  1202. }
  1203. void ProjectEx::enablePublish(Memc<UID> &ids, bool set_undo)
  1204. {
  1205. if(ids.elms())
  1206. {
  1207. TimeStamp time; time.getUTC(); Memc<UID> processed;
  1208. REPA(ids)if(Elm *elm=findElm(ids[i]))if(elm->noPublish() && time>elm->no_publish_time){elm->setNoPublish(false, time); processed.add(elm->id);}
  1209. if(set_undo)if(ElmChange *change=elm_undos.set(null, true))
  1210. {
  1211. change->type=ElmChange::PUBLISH_ENABLE;
  1212. change->name="Enable Publishing";
  1213. FREPA(processed)change->elms.binaryInclude(processed[i], Compare);
  1214. }
  1215. Server.noPublishElms(processed, false, time);
  1216. refresh(false);
  1217. activateSources(); // rebuild sources if needed
  1218. WorldEdit.validateRefs();
  1219. paramEditObjChanged(); // removed elements should display in red in param list
  1220. }
  1221. }
  1222. void ProjectEx::reload(Memc<UID> &elm_ids)
  1223. {
  1224. if(elm_ids.elms()> 1)ReloadElm.activate(elm_ids);else
  1225. if(elm_ids.elms()==1)ReloadElm.activate(elm_ids[0]);
  1226. }
  1227. void ProjectEx::cancelReload(C MemPtr<UID> &elm_ids)
  1228. {
  1229. REPA(elm_ids)if(Elm *elm=findElm(elm_ids[i]))elm->importing(false);
  1230. Importer.cancel(elm_ids);
  1231. refresh(false, false);
  1232. }
  1233. void ProjectEx::removeMeshVtx(C MemPtr<UID> &elm_ids, uint flag)
  1234. {
  1235. if(flag)REPA(elm_ids)if(Elm *obj_elm=findElm(elm_ids[i], ELM_OBJ))if(ElmObj *obj_data=obj_elm->objData())if(Elm *mesh_elm=findElm(obj_data->mesh_id, ELM_MESH))
  1236. {
  1237. if(elm_ids[i]==ObjEdit.obj_id) // if this is currently edited object
  1238. {
  1239. ObjEdit.remVtx(flag);
  1240. }else
  1241. {
  1242. Mesh mesh; if(Load(mesh, editPath(mesh_elm->id), game_path))
  1243. {
  1244. bool changed=false;
  1245. REPD(l, mesh.lods())
  1246. {
  1247. MeshLod &lod=mesh.lod(l); REPA(lod)
  1248. {
  1249. MeshPart &part=mesh.parts[i]; if(part.flag()&flag){changed=true; part.exclude(flag);}
  1250. }
  1251. }
  1252. if(changed)
  1253. {
  1254. if(flag&VTX_SKIN)mesh.clearSkeleton();
  1255. Save(mesh, editPath(mesh_elm->id), game_path);
  1256. makeGameVer(*mesh_elm);
  1257. if(ElmMesh *mesh_data=mesh_elm->meshData())
  1258. {
  1259. mesh_data->newVer();
  1260. mesh_data->file_time.getUTC();
  1261. }
  1262. meshChanged(*mesh_elm);
  1263. Server.setElmLong(mesh_elm->id);
  1264. }
  1265. }
  1266. }
  1267. }
  1268. }
  1269. bool ProjectEx::forceImageSize(Str &file, C VecI2 &size, bool relative, TimeStamp &file_time, C TimeStamp &time)
  1270. {
  1271. Mems<Edit::FileParams> files=Edit::FileParams::Decode(file); if(files.elms())
  1272. {
  1273. //if(relative) // for relative we need to remove any existing "resize" before calling 'loadImages', actually do this always, because there are now many "resize" commands and we want to remove all of them
  1274. {
  1275. int files_with_names=0; REPA(files)if(files[i].name.is())files_with_names++; // count how many files have names(images) and aren't just transforms
  1276. REPA(files)
  1277. {
  1278. Edit::FileParams &file=files[i];
  1279. if(!file.name.is() || files_with_names<=1) // remove only if the name is empty (we operate on the entire image), or if there's only up to one image
  1280. {
  1281. file.params.removeData(file.findParam("resize"));
  1282. file.params.removeData(file.findParam("resizeWrap"));
  1283. file.params.removeData(file.findParam("resizeClamp"));
  1284. file.params.removeData(file.findParam("resizeLinear"));
  1285. file.params.removeData(file.findParam("resizeCubic"));
  1286. file.params.removeData(file.findParam("resizeNoStretch"));
  1287. if(!file.is())files.remove(i, true);
  1288. }
  1289. }
  1290. }
  1291. if(files.elms() && !(relative && !size.x && !size.y)) // "relative && !size" means original size, for which we don't need to do anything, because "resize" was already removed
  1292. {
  1293. VecI2 s=size;
  1294. if(relative) // for relative size, we need to get information about the source image size
  1295. {
  1296. Image temp; if(!loadImages(temp, Edit::FileParams::Encode(files)))return false; // if failed to load, then do nothing
  1297. s.set(Max(1, Shl(temp.w(), size.x)), Max(1, Shl(temp.h(), size.y)));
  1298. s.set(NearestPow2(s.x), NearestPow2(s.y)); // textures are gonna be resized to pow2 anyway, so force pow2 size, to avoid double resize
  1299. }
  1300. // set "resize" param into 'files'
  1301. if(s.any())SetTransform(files, "resize", VecI2AsText(s)); // only if any specified
  1302. }
  1303. file=Edit::FileParams::Encode(files);
  1304. file_time=time;
  1305. return true;
  1306. }
  1307. return false;
  1308. }
  1309. void ProjectEx::imageMipMap(C MemPtr<UID> &elm_ids, bool on)
  1310. {
  1311. REPA(elm_ids)
  1312. if(Elm *image=findElm(elm_ids[i], ELM_IMAGE))
  1313. if(ElmImage *image_data=image->imageData())
  1314. if(image_data->mipMaps()!=on)
  1315. if(ImageEdit.elm==image)ImageEdit.setMipMap(on);else
  1316. {
  1317. image_data->newVer();
  1318. image_data->mipMaps(on); image_data->mip_maps_time.now();
  1319. makeGameVer(*image);
  1320. Server.setElmShort(image->id);
  1321. }
  1322. }
  1323. void ProjectEx::mtrlRGB(C MemPtr<UID> &elm_ids, C Vec &rgb, bool mul)
  1324. {
  1325. if(!mul || rgb!=1)
  1326. REPA(elm_ids)
  1327. if(Elm *mtrl=findElm(elm_ids[i], ELM_MTRL))
  1328. if(ElmMaterial *mtrl_data=mtrl->mtrlData())
  1329. if(MtrlEdit.elm==mtrl)MtrlEdit.setRGB(mul ? MtrlEdit.edit.color.xyz*rgb : rgb);else
  1330. {
  1331. EditMaterial edit; if(edit.load(editPath(mtrl->id)))if(mul || edit.color.xyz!=rgb)
  1332. {
  1333. mtrl_data->newVer();
  1334. if(mul)edit.color.xyz*=rgb;else edit.color.xyz=rgb; edit.color_time.now();
  1335. Save(edit, editPath(mtrl->id));
  1336. makeGameVer(*mtrl);
  1337. Server.setElmLong(mtrl->id);
  1338. }
  1339. }
  1340. }
  1341. void ProjectEx::mtrlAlpha(C MemPtr<UID> &elm_ids)
  1342. {
  1343. REPA(elm_ids)
  1344. if(Elm *mtrl=findElm(elm_ids[i], ELM_MTRL))
  1345. if(ElmMaterial *mtrl_data=mtrl->mtrlData())
  1346. if(MtrlEdit.elm==mtrl)MtrlEdit.resetAlpha();else
  1347. {
  1348. EditMaterial edit; if(edit.load(editPath(mtrl->id)))
  1349. {
  1350. mtrl_data->newVer();
  1351. edit.resetAlpha();
  1352. Save(edit, editPath(mtrl->id));
  1353. makeGameVer(*mtrl);
  1354. Server.setElmLong(mtrl->id);
  1355. }
  1356. }
  1357. }
  1358. void ProjectEx::mtrlCull(C MemPtr<UID> &elm_ids, bool on)
  1359. {
  1360. REPA(elm_ids)
  1361. if(Elm *mtrl=findElm(elm_ids[i], ELM_MTRL))
  1362. if(ElmMaterial *mtrl_data=mtrl->mtrlData())
  1363. if(MtrlEdit.elm==mtrl)MtrlEdit.cull(on);else
  1364. {
  1365. EditMaterial edit; if(edit.load(editPath(mtrl->id)))if(edit.cull!=on)
  1366. {
  1367. mtrl_data->newVer();
  1368. edit.cull=on; edit.cull_time.now();
  1369. Save(edit, editPath(mtrl->id));
  1370. makeGameVer(*mtrl);
  1371. Server.setElmLong(mtrl->id);
  1372. }
  1373. }
  1374. }
  1375. void ProjectEx::mtrlFlipNrmY(C MemPtr<UID> &elm_ids, bool on)
  1376. {
  1377. REPA(elm_ids)
  1378. if(Elm *mtrl=findElm(elm_ids[i], ELM_MTRL))
  1379. if(ElmMaterial *mtrl_data=mtrl->mtrlData())
  1380. if(MtrlEdit.elm==mtrl)MtrlEdit.flipNrmY(on);else
  1381. {
  1382. EditMaterial edit; if(edit.load(editPath(mtrl->id)))if(edit.flip_normal_y!=on)
  1383. {
  1384. mtrl_data->newVer();
  1385. edit.flip_normal_y=on; edit.flip_normal_y_time.now();
  1386. Save(edit, editPath(mtrl->id));
  1387. //makeGameVer(*mtrl); not needed because 'flip_normal_y' is not stored in game, however if tex ID is changed, then it's handled by 'mtrlReloadTextures' below
  1388. if(!mtrlReloadTextures(mtrl->id, true, false, false, false, false))Server.setElmLong(mtrl->id); // 'Server.setElmLong' will be called by 'mtrlReloadTextures' unless it failed
  1389. }
  1390. }
  1391. }
  1392. void ProjectEx::mtrlDownsizeTexMobile(C MemPtr<UID> &elm_ids, byte downsize)
  1393. {
  1394. REPA(elm_ids)if(C Elm *mtrl=findElm(elm_ids[i]))if(C ElmMaterial *mtrl_data=mtrl->mtrlData())
  1395. {
  1396. Memt<UID> mtrls;
  1397. // include this material
  1398. if(mtrl_data->downsize_tex_mobile!=downsize)mtrls.add(mtrl->id);
  1399. // include other materials that have the same textures
  1400. if(mtrl_data->base_0_tex.valid() || mtrl_data->base_1_tex.valid())
  1401. FREPA(elms)if(C ElmMaterial *test=elms[i].mtrlData())
  1402. if(test->downsize_tex_mobile!=downsize && test->base_0_tex==mtrl_data->base_0_tex && test->base_1_tex==mtrl_data->base_1_tex)mtrls.binaryInclude(elms[i].id, Compare);
  1403. REPA(mtrls)if(Elm *mtrl=findElm(mtrls[i]))
  1404. {
  1405. if(MtrlEdit.elm==mtrl)MtrlEdit.downsizeTexMobile(downsize);else
  1406. {
  1407. EditMaterial edit; if(edit.load(editPath(mtrl->id)))if(ElmMaterial *mtrl_data=mtrl->mtrlData())
  1408. {
  1409. mtrl_data->newVer();
  1410. mtrl_data->downsize_tex_mobile=edit.downsize_tex_mobile=downsize; edit.downsize_tex_mobile_time.now();
  1411. Save(edit, editPath(mtrl->id));
  1412. //makeGameVer(*mtrl); this is not needed because 'downsize_tex_mobile' is not stored in the game version, instead textures are downsized during publishing
  1413. Server.setElmLong(mtrl->id); // Long is needed because 'downsize_tex_mobile_time' is only in edit
  1414. }
  1415. }
  1416. }
  1417. }
  1418. }
  1419. void ProjectEx::mtrlTexQualityiOS(C MemPtr<UID> &elm_ids, bool quality)
  1420. {
  1421. REPA(elm_ids)if(C Elm *mtrl=findElm(elm_ids[i]))if(C ElmMaterial *mtrl_data=mtrl->mtrlData())
  1422. {
  1423. Memt<UID> mtrls;
  1424. // include this material
  1425. if(mtrl_data->texQualityiOS()!=quality)mtrls.add(mtrl->id);
  1426. // include other materials that have the same textures
  1427. if(mtrl_data->base_0_tex.valid() || mtrl_data->base_1_tex.valid())
  1428. FREPA(elms)if(C ElmMaterial *test=elms[i].mtrlData())
  1429. if(test->texQualityiOS()!=quality && test->base_0_tex==mtrl_data->base_0_tex && test->base_1_tex==mtrl_data->base_1_tex)mtrls.binaryInclude(elms[i].id, Compare);
  1430. REPA(mtrls)if(Elm *mtrl=findElm(mtrls[i]))
  1431. {
  1432. if(MtrlEdit.elm==mtrl)MtrlEdit.texQualityiOS(quality);else
  1433. {
  1434. EditMaterial edit; if(edit.load(editPath(mtrl->id)))if(ElmMaterial *mtrl_data=mtrl->mtrlData())
  1435. {
  1436. mtrl_data->newVer();
  1437. mtrl_data->texQualityiOS(edit.high_quality_ios=quality); edit.high_quality_ios_time.now();
  1438. Save(edit, editPath(mtrl->id));
  1439. //makeGameVer(*mtrl); this is not needed because 'high_quality_ios' is not stored in the game version, instead textures are converted during publishing
  1440. Server.setElmLong(mtrl->id); // Long is needed because 'high_quality_ios_time' is only in edit
  1441. }
  1442. }
  1443. }
  1444. }
  1445. }
  1446. bool ProjectEx::mtrlMulTexCol(C MemPtr<UID> &elm_ids)
  1447. {
  1448. bool ok=true;
  1449. REPA(elm_ids)
  1450. {
  1451. EditMaterial edit; if(!mtrlGet(elm_ids[i], edit))ok=false;else
  1452. {
  1453. if(!Equal(edit.color.xyz, Vec(1)) && edit.color_map.is())
  1454. {
  1455. Mems<Edit::FileParams> fps=Edit::FileParams::Decode(edit.color_map);
  1456. Vec mul=edit.color.xyz; if(C TextParam *p=FindTransform(fps, "mulRGB"))mul*=TextVecEx(p->value);
  1457. if(Equal(mul, Vec(1)))DelTransform(fps, "mulRGB");
  1458. else SetTransform(fps, "mulRGB", TextVecEx(mul));
  1459. edit.color_map=Edit::FileParams::Encode(fps); edit.color_map_time.now();
  1460. edit.color.xyz=1; edit.color_time.now();
  1461. mtrlSync(elm_ids[i], edit, true, false, "mulTexCol");
  1462. }
  1463. }
  1464. }
  1465. return ok;
  1466. }
  1467. bool ProjectEx::mtrlMulTexRough(C MemPtr<UID> &elm_ids)
  1468. {
  1469. bool ok=true;
  1470. REPA(elm_ids)
  1471. {
  1472. EditMaterial edit; if(!mtrlGet(elm_ids[i], edit))ok=false;else
  1473. {
  1474. if(!Equal(edit.rough, 1) && edit.hasNormalMap())
  1475. {
  1476. edit.separateNormalMap();
  1477. Mems<Edit::FileParams> fps=Edit::FileParams::Decode(edit.normal_map);
  1478. flt mul=edit.rough; if(C TextParam *p=FindTransform(fps, "scaleXY"))mul*=p->asFlt();
  1479. if(Equal(mul, 1))DelTransform(fps, "scaleXY");
  1480. else SetTransform(fps, "scaleXY", TextVecEx(mul));
  1481. edit.normal_map=Edit::FileParams::Encode(fps); edit.normal_map_time.now();
  1482. edit.rough=1; edit.rough_bump_time.now();
  1483. mtrlSync(elm_ids[i], edit, true, false, "mulTexScale");
  1484. }
  1485. }
  1486. }
  1487. return ok;
  1488. }
  1489. void ProjectEx::mtrlMoveToObj(C MemPtr<UID> &elm_ids)
  1490. {
  1491. Memc<Edit::IDParam<UID> > moves;
  1492. FREPA(elm_ids)
  1493. {
  1494. C UID &mtrl_id=elm_ids[i], obj_id=mtrlToObj(mtrl_id); if(obj_id.valid())moves.New()=Edit::IDParam<UID>(mtrl_id, obj_id);
  1495. }
  1496. setElmParent(moves, false, true);
  1497. }
  1498. bool ProjectEx::mtrlGet(C UID &elm_id, EditMaterial &mtrl)
  1499. {
  1500. if(C Elm *elm=findElm(elm_id))
  1501. {
  1502. if( MtrlEdit.elm_id==elm_id){mtrl= MtrlEdit.edit; return true;}
  1503. if(WaterMtrlEdit.elm_id==elm_id){mtrl=WaterMtrlEdit.edit; return true;}
  1504. switch(elm->type)
  1505. {
  1506. case ELM_MTRL : return mtrl.load(editPath(elm_id));
  1507. case ELM_WATER_MTRL:
  1508. {
  1509. EditWaterMtrl water_mtrl; if(!water_mtrl.load(editPath(elm_id)))break;
  1510. mtrl=water_mtrl;
  1511. }return true;
  1512. }
  1513. }
  1514. return false;
  1515. }
  1516. bool ProjectEx::mtrlSync(C UID &elm_id, C EditMaterial &mtrl, bool reload_textures, bool adjust_params, cptr undo_change_type)
  1517. {
  1518. if(Elm *elm=findElm(elm_id))
  1519. {
  1520. if(MtrlEdit.elm_id==elm_id)
  1521. {
  1522. MtrlEdit.undos.set(undo_change_type);
  1523. uint base_tex=MtrlEdit.edit.baseTex(); // get current state of textures before making any change
  1524. if(uint changed =MtrlEdit.edit.sync(mtrl))
  1525. {
  1526. if(reload_textures)
  1527. {
  1528. if(changed&EditMaterial::CHANGED_BASE )MtrlEdit.rebuildBase(base_tex, FlagTest(changed, EditMaterial::CHANGED_FNY), adjust_params, true);
  1529. if(changed&EditMaterial::CHANGED_REFL )MtrlEdit.rebuildReflection();
  1530. if(changed&EditMaterial::CHANGED_DET )MtrlEdit.rebuildDetail();
  1531. if(changed&EditMaterial::CHANGED_MACRO)MtrlEdit.rebuildMacro();
  1532. if(changed&EditMaterial::CHANGED_LIGHT)MtrlEdit.rebuildLight();
  1533. }else
  1534. {
  1535. if(changed&(EditMaterial::CHANGED_BASE|EditMaterial::CHANGED_REFL|EditMaterial::CHANGED_DET|EditMaterial::CHANGED_MACRO|EditMaterial::CHANGED_LIGHT))mtrlTexChanged();
  1536. }
  1537. MtrlEdit.toGui();
  1538. MtrlEdit.setChanged();
  1539. D.setShader(MtrlEdit.game());
  1540. }
  1541. return true;
  1542. }
  1543. switch(elm->type)
  1544. {
  1545. case ELM_MTRL:
  1546. {
  1547. // load
  1548. EditMaterial edit; if(!mtrlGet(elm_id, edit))return false;
  1549. uint old_base_tex=edit.baseTex();
  1550. if(uint changed=edit.sync(mtrl)) // if changed anything
  1551. {
  1552. MaterialPtr game=gamePath(elm_id); if(!game)return false;
  1553. bool want_tan_bin=game->wantTanBin();
  1554. uint new_base_tex=edit.baseTex(); // use estimated base tex
  1555. if(reload_textures)
  1556. {
  1557. if(changed&EditMaterial::CHANGED_BASE )new_base_tex=mtrlCreateBaseTextures (edit, FlagTest(changed, EditMaterial::CHANGED_FNY)); // get precise base tex
  1558. if(changed&EditMaterial::CHANGED_REFL ) mtrlCreateReflectionTexture(edit);
  1559. if(changed&EditMaterial::CHANGED_DET ) mtrlCreateDetailTexture (edit);
  1560. if(changed&EditMaterial::CHANGED_MACRO) mtrlCreateMacroTexture (edit);
  1561. if(changed&EditMaterial::CHANGED_LIGHT) mtrlCreateLightTexture (edit);
  1562. }
  1563. edit.copyTo(*game, T);
  1564. if(adjust_params)AdjustMaterialParams(edit, *game, old_base_tex, new_base_tex);
  1565. // save
  1566. if(ElmMaterial *data=elm->mtrlData()){data->newVer(); data->from(edit);}
  1567. Save( edit, editPath(elm_id));
  1568. Save(*game, gamePath(elm_id)); savedGame(*elm);
  1569. Server.setElmLong(elm_id);
  1570. // process dependencies
  1571. if(want_tan_bin!=game->wantTanBin())mtrlSetAutoTanBin(elm_id);
  1572. if(changed&(EditMaterial::CHANGED_BASE|EditMaterial::CHANGED_REFL|EditMaterial::CHANGED_DET|EditMaterial::CHANGED_MACRO|EditMaterial::CHANGED_LIGHT))mtrlTexChanged();
  1573. D.setShader(game());
  1574. }
  1575. }return true;
  1576. }
  1577. }
  1578. return false;
  1579. }
  1580. bool ProjectEx::mtrlSync(C UID &elm_id, C Edit::Material &mtrl, bool reload_textures, bool adjust_params)
  1581. {
  1582. if(Elm *elm=findElm(elm_id))
  1583. {
  1584. if(MtrlEdit.elm_id==elm_id)
  1585. {
  1586. MtrlEdit.undos.set("sync");
  1587. uint base_tex=MtrlEdit.edit.baseTex(); // get current state of textures before making any change
  1588. if(uint changed =MtrlEdit.edit.sync(mtrl))
  1589. {
  1590. if(reload_textures)
  1591. {
  1592. if(changed&EditMaterial::CHANGED_BASE )MtrlEdit.rebuildBase(base_tex, FlagTest(changed, EditMaterial::CHANGED_FNY), adjust_params, true);
  1593. if(changed&EditMaterial::CHANGED_REFL )MtrlEdit.rebuildReflection();
  1594. if(changed&EditMaterial::CHANGED_DET )MtrlEdit.rebuildDetail();
  1595. if(changed&EditMaterial::CHANGED_MACRO)MtrlEdit.rebuildMacro();
  1596. if(changed&EditMaterial::CHANGED_LIGHT)MtrlEdit.rebuildLight();
  1597. }
  1598. MtrlEdit.toGui();
  1599. MtrlEdit.setChanged();
  1600. D.setShader(MtrlEdit.game());
  1601. }
  1602. return true;
  1603. }
  1604. switch(elm->type)
  1605. {
  1606. case ELM_MTRL:
  1607. {
  1608. // load
  1609. EditMaterial edit; if(!mtrlGet(elm_id, edit))return false;
  1610. uint old_base_tex=edit.baseTex();
  1611. if(uint changed=edit.sync(mtrl)) // if changed anything
  1612. {
  1613. MaterialPtr game=gamePath(elm_id); if(!game)return false;
  1614. bool want_tan_bin=game->wantTanBin();
  1615. uint new_base_tex=edit.baseTex(); // use estimated base tex
  1616. if(reload_textures)
  1617. {
  1618. if(changed&EditMaterial::CHANGED_BASE )new_base_tex=mtrlCreateBaseTextures (edit, FlagTest(changed, EditMaterial::CHANGED_FNY)); // get precise base tex
  1619. if(changed&EditMaterial::CHANGED_REFL ) mtrlCreateReflectionTexture(edit);
  1620. if(changed&EditMaterial::CHANGED_DET ) mtrlCreateDetailTexture (edit);
  1621. if(changed&EditMaterial::CHANGED_MACRO) mtrlCreateMacroTexture (edit);
  1622. if(changed&EditMaterial::CHANGED_LIGHT) mtrlCreateLightTexture (edit);
  1623. }
  1624. edit.copyTo(*game, T);
  1625. if(adjust_params)AdjustMaterialParams(edit, *game, old_base_tex, new_base_tex);
  1626. // save
  1627. if(ElmMaterial *data=elm->mtrlData()){data->newVer(); data->from(edit);}
  1628. Save( edit, editPath(elm_id));
  1629. Save(*game, gamePath(elm_id)); savedGame(*elm);
  1630. Server.setElmLong(elm_id);
  1631. // process dependencies
  1632. if(want_tan_bin!=game->wantTanBin())mtrlSetAutoTanBin(elm_id);
  1633. D.setShader(game());
  1634. }
  1635. }return true;
  1636. }
  1637. }
  1638. return false;
  1639. }
  1640. uint ProjectEx::createBaseTextures(Image &base_0, Image &base_1, C EditMaterial &material, bool changed_flip_normal_y)
  1641. {
  1642. MtrlImages mtrl_images;
  1643. mtrl_images.fromMaterial(material, T, changed_flip_normal_y);
  1644. return mtrl_images.createBaseTextures(base_0, base_1);
  1645. }
  1646. uint ProjectEx::mtrlCreateBaseTextures(EditMaterial &material, bool changed_flip_normal_y)
  1647. {
  1648. // TODO: generating textures when the sources were not found, will reuse existing images, but due to compression, the quality will be lost, and new textures will be generated even though images are the same, this is because BC7->RGBA->BC7 is not the same
  1649. Image base_0, base_1;
  1650. uint bt=createBaseTextures(base_0, base_1, material, changed_flip_normal_y);
  1651. UID old_tex_id;
  1652. IMAGE_TYPE ct;
  1653. // base 0
  1654. old_tex_id =material.base_0_tex; ImageProps(base_0, &material.base_0_tex, &ct, ForceHQMtrlBase0 ? FORCE_HQ : 0);
  1655. if(old_tex_id!=material.base_0_tex)material.color_map_time.getUTC(); // in order for 'base_0_tex' to sync, a base 0 texture time must be changed, but set it only if the new texture is different
  1656. if(base_0.is())
  1657. {
  1658. if(includeTex(material.base_0_tex))
  1659. {
  1660. base_0.copyTry(base_0, -1, -1, -1, ct, IMAGE_2D, 0, FILTER_BEST, false, false, false, false);
  1661. saveTex(base_0, material.base_0_tex);
  1662. }
  1663. Server.setTex(material.base_0_tex);
  1664. }
  1665. // base 1
  1666. old_tex_id =material.base_1_tex; ImageProps(base_1, &material.base_1_tex, &ct, ForceHQMtrlBase1 ? FORCE_HQ : 0);
  1667. if(old_tex_id!=material.base_1_tex)material.normal_map_time.getUTC(); // in order for 'base_1_tex' to sync, a base 1 texture time must be changed, but set it only if the new texture is different
  1668. if(base_1.is())
  1669. {
  1670. if(includeTex(material.base_1_tex))
  1671. {
  1672. base_1.copyTry(base_1, -1, -1, -1, ct, IMAGE_2D, 0, FILTER_BEST, false, false, false, true);
  1673. saveTex(base_1, material.base_1_tex);
  1674. }
  1675. Server.setTex(material.base_1_tex);
  1676. }
  1677. return bt;
  1678. }
  1679. bool ProjectEx::mtrlCreateReflectionTexture(Image &reflection, C EditMaterial &material)
  1680. {
  1681. bool loaded=false;
  1682. reflection.del();
  1683. Mems<Edit::FileParams> faces=Edit::FileParams::Decode(material.reflection_map);
  1684. if(faces.elms()==6) // 6 images specified
  1685. {
  1686. Image images[6]; REPA(images)
  1687. {
  1688. Image &image=images[i];
  1689. Str name=faces[i].name; UID image_id; if(DecodeFileName(name, image_id))name=editPath(image_id);else image_id.zero();
  1690. if(ImportImage(image, name, -1, IMAGE_SOFT, 1, true))
  1691. {
  1692. loaded=true;
  1693. if(Elm *elm=findElm(image_id))if(ElmImage *data=elm->imageData())if(data->mode==IMAGE_CUBE)image.crop(image, i*image.w()/6, 0, image.w()/6, image.h()); // crop to i-th face for cubes, we need to check ElmImage for cube, and not 'image.mode' because Cube ELM_IMAGE are stored in 6x1 Soft in editPath
  1694. }
  1695. }
  1696. if(loaded) // create only if any of the images were loaded, otherwise we don't need it
  1697. {
  1698. ImagePtr cur_reflection;
  1699. REPA(images) // check if any of the images didn't load
  1700. {
  1701. Image &image=images[i]; if(!image.is())
  1702. {
  1703. if(!cur_reflection)cur_reflection=texPath(material.reflection_tex); // load existing reflection map
  1704. if( cur_reflection)cur_reflection->extractMipMap(image, -1, IMAGE_SOFT, 0, GetCubeDir(i)); // extract from existing reflection map
  1705. }
  1706. }
  1707. reflection.ImportCubeTry(images[2], images[0], images[5], images[4], images[1], images[3], IMAGE_BC1, 1, true); // create already as compressed because IMAGE_CUBE can be only on GPU, so this speeds up the process
  1708. }
  1709. }else
  1710. if(faces.elms()) // if at least one face is specified
  1711. {
  1712. Str name=material.reflection_map; UID image_id; if(DecodeFileName(name, image_id))name=editPath(image_id);
  1713. if(ImportImage(reflection, name))
  1714. {
  1715. loaded=true;
  1716. reflection.toCube(reflection, NearestPow2(reflection.h()), IMAGE_BC1);
  1717. }
  1718. }
  1719. return !faces.elms() || loaded;
  1720. }
  1721. void ProjectEx::mtrlCreateReflectionTexture(EditMaterial &material)
  1722. {
  1723. Image reflection; if(mtrlCreateReflectionTexture(reflection, material)) // proceed only if there's no source, or succeeded with importing, this is to avoid clearing existing texture when failed to load
  1724. {
  1725. ImageProps(reflection, &material.reflection_tex, null, IGNORE_ALPHA); material.reflection_map_time.getUTC(); // in order for 'reflection_tex' to sync, 'reflection_map_time' time must be changed
  1726. if(reflection.is())
  1727. {
  1728. if(includeTex(material.reflection_tex))saveTex(reflection, material.reflection_tex);
  1729. Server.setTex(material.reflection_tex);
  1730. }
  1731. }
  1732. }
  1733. void ProjectEx::mtrlCreateDetailTexture(EditMaterial &material)
  1734. {
  1735. // !! here order of loading images is important, because we pass pointers to those images in subsequent loads !!
  1736. Image col, bump, normal;
  1737. bool col_ok=loadImage( col, material.detail_color ), // load before 'bump' , here 'col' and 'bump' are not yet available
  1738. bump_ok=loadImage( bump, material.detail_bump , false, &col, null, null ), // load before 'normal', here 'bump' is not yet available
  1739. nrm_ok=loadImage(normal, material.detail_normal, false, &col, null, &bump);
  1740. if(!bump_ok && !material.detail_normal.is())nrm_ok=false; // if bump map failed to load, and there is no dedicated normal map, and since it's possible that normal was created from the bump , which is not available, so normal needs to be marked as failed
  1741. if(col_ok || bump_ok || nrm_ok) // proceed only if succeeded with setting anything, this is to avoid clearing existing texture when all failed to load, continue if at least one succeeded, in case the image is different while others will be extracted from old version
  1742. {
  1743. ExtractDetailTexture(T, material.detail_tex, col_ok ? null : &col, bump_ok ? null : &bump, nrm_ok ? null : &normal);
  1744. Image detail; CreateDetailTexture(detail, col, bump, normal);
  1745. IMAGE_TYPE ct; ImageProps(detail, &material.detail_tex, &ct, (ForceHQMtrlDetail ? FORCE_HQ : 0) | (RemoveMtrlDetailBump ? IGNORE_ALPHA : 0)); material.detail_map_time.getUTC(); // in order for 'detail_tex' to sync, 'detail_map_time' time must be changed
  1746. if(detail.is())
  1747. {
  1748. if(includeTex(material.detail_tex))
  1749. {
  1750. FixAlpha(detail, ct, RemoveMtrlDetailBump);
  1751. detail.copyTry(detail, -1, -1, -1, ct, IMAGE_2D, 0, FILTER_BEST, false, false, false, true);
  1752. saveTex(detail, material.detail_tex);
  1753. }
  1754. Server.setTex(material.detail_tex);
  1755. }
  1756. }
  1757. }
  1758. void ProjectEx::mtrlCreateMacroTexture(EditMaterial &material)
  1759. {
  1760. Image macro; if(loadImage(macro, material.macro_map)) // proceed only if loaded ok
  1761. {
  1762. macro.resize(NearestPow2(macro.w()), NearestPow2(macro.h()), FILTER_BEST, false);
  1763. IMAGE_TYPE ct; ImageProps(macro, &material.macro_tex, &ct, IGNORE_ALPHA); material.macro_map_time.getUTC(); // in order for 'macro_tex' to sync, 'macro_map_time' time must be changed
  1764. if(macro.is())
  1765. {
  1766. if(includeTex(material.macro_tex))
  1767. {
  1768. FixAlpha(macro, ct);
  1769. macro.copyTry(macro, -1, -1, -1, ct, IMAGE_2D, 0, FILTER_BEST, false);
  1770. saveTex(macro, material.macro_tex);
  1771. }
  1772. Server.setTex(material.macro_tex);
  1773. }
  1774. }
  1775. }
  1776. void ProjectEx::mtrlCreateLightTexture(EditMaterial &material)
  1777. {
  1778. Image light; if(loadImage(light, material.light_map)) // proceed only if loaded ok
  1779. {
  1780. light.resize(NearestPow2(light.w()), NearestPow2(light.h()));
  1781. IMAGE_TYPE ct; ImageProps(light, &material.light_tex, &ct, IGNORE_ALPHA); material.light_map_time.getUTC(); // in order for 'light_tex' to sync, 'light_map_time' time must be changed
  1782. if(light.is())
  1783. {
  1784. if(includeTex(material.light_tex))
  1785. {
  1786. FixAlpha(light, ct);
  1787. light.copyTry(light, -1, -1, -1, ct, IMAGE_2D, 0);
  1788. saveTex(light, material.light_tex);
  1789. }
  1790. Server.setTex(material.light_tex);
  1791. }
  1792. }
  1793. }
  1794. bool ProjectEx::mtrlReloadTextures(C UID &elm_id, bool base, bool reflection, bool detail, bool macro, bool light)
  1795. {
  1796. if(!base && !reflection && !detail && !macro && !light)return true; // nothing to reload
  1797. if(Elm *elm=findElm(elm_id))
  1798. {
  1799. if(MtrlEdit.elm_id==elm_id)
  1800. {
  1801. MtrlEdit.undos.set("EI");
  1802. if(base )MtrlEdit.rebuildBase(0, false, false, true);
  1803. if(reflection)MtrlEdit.rebuildReflection();
  1804. if(detail )MtrlEdit.rebuildDetail();
  1805. if(macro )MtrlEdit.rebuildMacro();
  1806. if(light )MtrlEdit.rebuildLight();
  1807. return true;
  1808. }
  1809. if(WaterMtrlEdit.elm_id==elm_id)
  1810. {
  1811. WaterMtrlEdit.undos.set("EI");
  1812. if(base )WaterMtrlEdit.rebuildBase(0, false, false, true);
  1813. if(reflection)WaterMtrlEdit.rebuildReflection();
  1814. if(detail )WaterMtrlEdit.rebuildDetail();
  1815. if(macro )WaterMtrlEdit.rebuildMacro();
  1816. if(light )WaterMtrlEdit.rebuildLight();
  1817. return true;
  1818. }
  1819. switch(elm->type)
  1820. {
  1821. case ELM_MTRL:
  1822. {
  1823. // load
  1824. EditMaterial edit; if(!mtrlGet(elm_id, edit))return false;
  1825. MaterialPtr game=gamePath(elm_id); if(!game)return false;
  1826. bool want_tan_bin=game->wantTanBin();
  1827. // reload
  1828. if(base )mtrlCreateBaseTextures (edit, false);
  1829. if(reflection)mtrlCreateReflectionTexture(edit);
  1830. if(detail )mtrlCreateDetailTexture (edit);
  1831. if(macro )mtrlCreateMacroTexture (edit);
  1832. if(light )mtrlCreateLightTexture (edit);
  1833. // save because texture ID's have been changed
  1834. if(ElmMaterial *data=elm->mtrlData()){data->newVer(); data->from(edit);}
  1835. Save( edit, editPath(elm_id)); edit.copyTo(*game, T);
  1836. Save(*game, gamePath(elm_id)); savedGame(*elm);
  1837. Server.setElmLong(elm_id);
  1838. // process dependencies
  1839. if(want_tan_bin!=game->wantTanBin())mtrlSetAutoTanBin(elm_id);
  1840. D.setShader(game());
  1841. }return true;
  1842. case ELM_WATER_MTRL:
  1843. {
  1844. // load
  1845. EditWaterMtrl edit; if(!edit.load(editPath(elm_id)))return false;
  1846. WaterMtrlPtr game=gamePath(elm_id); if(!game)return false;
  1847. // reload
  1848. if(base )mtrlCreateBaseTextures (edit, false);
  1849. if(reflection)mtrlCreateReflectionTexture(edit);
  1850. if(detail )mtrlCreateDetailTexture (edit);
  1851. if(macro )mtrlCreateMacroTexture (edit);
  1852. if(light )mtrlCreateLightTexture (edit);
  1853. // save because texture ID's have been changed
  1854. if(ElmWaterMtrl *data=elm->waterMtrlData()){data->newVer(); data->from(edit);}
  1855. Save( edit, editPath(elm_id)); edit.copyTo(*game, T);
  1856. Save(*game, gamePath(elm_id)); savedGame(*elm);
  1857. Server.setElmLong(elm_id);
  1858. }return true;
  1859. }
  1860. }
  1861. return false;
  1862. }
  1863. void ProjectEx::mtrlReloadTextures(C MemPtr<UID> &elm_ids, bool base, bool reflection, bool detail, bool macro, bool light)
  1864. {
  1865. FREPA(elm_ids)mtrlReloadTextures(elm_ids[i], base, reflection, detail, macro, light);
  1866. }
  1867. Animation* ProjectEx::getAnim(C UID &elm_id, Animation &temp)C
  1868. {
  1869. Str file=gamePath(elm_id);
  1870. Animation *anim=Animations.find(file);
  1871. if(!anim){anim=&temp; anim->load(file);}
  1872. return anim;
  1873. }
  1874. bool ProjectEx::animGet(C UID &elm_id, Animation &anim)C
  1875. {
  1876. if(C Elm *elm=findElm(elm_id, ELM_ANIM))return anim.load(gamePath(*elm));
  1877. return false;
  1878. }
  1879. bool ProjectEx::animSet(C UID &elm_id, C Animation &anim)
  1880. {
  1881. if(Elm *elm=findElm(elm_id))
  1882. if(ElmAnim *anim_data=elm->animData())
  1883. {
  1884. anim_data->newVer();
  1885. anim_data->file_time.getUTC();
  1886. anim_data->linear(anim.linear()).linear_time.getUTC();
  1887. anim_data->loop (anim.loop ()). loop_time.getUTC();
  1888. Save(anim, gamePath(elm_id)); savedGame(*elm);
  1889. Server.setElmLong(elm_id);
  1890. if(AnimEdit.elm==elm)AnimEdit.toGui();
  1891. return true;
  1892. }
  1893. return false;
  1894. }
  1895. void ProjectEx::setAnimParams(Elm &elm_anim)
  1896. {
  1897. if(ElmAnim *anim_data=elm_anim.animData())
  1898. if(Animation *anim=Animations.get(gamePath(elm_anim.id)))
  1899. if(anim->loop ()!=anim_data->loop ()
  1900. || anim->linear()!=anim_data->linear())
  1901. {
  1902. anim->loop(anim_data->loop()).linear(anim_data->linear());
  1903. Save(*anim, gamePath(elm_anim.id)); savedGame(elm_anim);
  1904. }
  1905. }
  1906. void ProjectEx::animClip(C MemPtr<UID> &elm_ids)
  1907. {
  1908. REPA(elm_ids)
  1909. if(Elm *anim=findElm(elm_ids[i], ELM_ANIM))
  1910. if(ElmAnim *anim_data=anim->animData())
  1911. {
  1912. Animation temp, *animation=getAnim(anim->id, temp);
  1913. anim_data->newVer();
  1914. anim_data->file_time.getUTC();
  1915. animation->clipAuto();
  1916. Save(*animation, gamePath(anim->id)); savedGame(*anim);
  1917. Server.setElmLong(anim->id);
  1918. }
  1919. }
  1920. void ProjectEx::animLinear(C MemPtr<UID> &elm_ids, bool linear)
  1921. {
  1922. REPA(elm_ids)
  1923. if(Elm *anim=findElm(elm_ids[i], ELM_ANIM))
  1924. if(ElmAnim *anim_data=anim->animData())
  1925. if(anim_data->linear()!=linear)
  1926. {
  1927. Animation temp, *animation=getAnim(anim->id, temp);
  1928. anim_data->newVer();
  1929. anim_data->file_time.getUTC();
  1930. anim_data->linear(linear);
  1931. animation->linear(linear);
  1932. Save(*animation, gamePath(anim->id)); savedGame(*anim);
  1933. Server.setElmLong(anim->id);
  1934. if(AnimEdit.elm==anim)AnimEdit.toGui();
  1935. }
  1936. }
  1937. void ProjectEx::animLoop(C MemPtr<UID> &elm_ids, bool loop)
  1938. {
  1939. REPA(elm_ids)
  1940. if(Elm *anim=findElm(elm_ids[i], ELM_ANIM))
  1941. if(ElmAnim *anim_data=anim->animData())
  1942. if(anim_data->loop()!=loop)
  1943. {
  1944. Animation temp, *animation=getAnim(anim->id, temp);
  1945. anim_data->newVer();
  1946. anim_data->file_time.getUTC();
  1947. anim_data->loop(loop);
  1948. animation->loop(loop);
  1949. Save(*animation, gamePath(anim->id)); savedGame(*anim);
  1950. Server.setElmLong(anim->id);
  1951. if(AnimEdit.elm==anim)AnimEdit.toGui();
  1952. }
  1953. }
  1954. void ProjectEx::animSetTargetSkel(C MemPtr<UID> &anim_ids, C UID &skel_id)
  1955. {
  1956. bool changed=false;
  1957. Elm *skel_elm=findElm(skel_id, ELM_SKEL); UID sid=(skel_elm ? skel_elm->id : UIDZero);
  1958. Skeleton *skel; if(RenameAnimBonesOnSkelChange){skel=Skeletons(gamePath(sid)); if(ObjEdit.skel_elm==skel_elm)ObjEdit.flushMeshSkel();} // !! if this skeleton is currently edited, then we have to flush it, for example, it could have been empty at the start and we've just added bones, so 'new_skel' is empty and bones are only in 'ObjEdit.mesh_skel_temp' memory, and before changing 'anim_data.skel_id' !!
  1959. REPA(anim_ids)
  1960. if(Elm *anim=findElm(anim_ids[i]))
  1961. if(ElmAnim *anim_data=anim->animData())
  1962. if(anim_data->skel_id!=sid)
  1963. {
  1964. changed=true;
  1965. anim_data->newVer();
  1966. anim_data->skel_id=sid;
  1967. anim_data->skel_time.getUTC();
  1968. if(skel_elm)anim_data->transform=skel_elm->skelData()->transform;
  1969. bool file_changed=false;
  1970. if(RenameAnimBonesOnSkelChange && skel)
  1971. {
  1972. Animation temp, *animation=getAnim(anim->id, temp);
  1973. if(animation->setBoneNameTypeIndexesFromSkeleton(*skel))
  1974. {
  1975. file_changed=true;
  1976. anim_data->file_time.getUTC();
  1977. Save(*animation, gamePath(anim->id)); savedGame(*anim);
  1978. }
  1979. }
  1980. if(file_changed)Server.setElmLong(anim->id);else Server.setElmShort(anim->id);
  1981. if(AnimEdit.elm==anim)AnimEdit.toGui();
  1982. }
  1983. if(changed)refresh(false, false);
  1984. }
  1985. void ProjectEx::transformSet(C MemPtr<UID> &elm_ids)
  1986. {
  1987. if(!ObjEdit.obj_elm)Gui.msgBox(S, "No object is currently opened");else
  1988. {
  1989. Pose pose=PoseIdentity; if(ObjEdit.mesh_elm)if(ElmMesh *mesh_data=ObjEdit.mesh_elm->meshData())pose=mesh_data->transform;
  1990. REPA(elm_ids)
  1991. if(Elm *obj =findElm(elm_ids[i] ))if(ElmObj * obj_data=obj -> objData())
  1992. if(Elm *mesh=findElm(obj_data->mesh_id))if(ElmMesh *mesh_data=mesh->meshData())if(mesh_data->canHaveCustomTransform())
  1993. if(ObjEdit.mesh_elm!=mesh)
  1994. if(mesh_data->transform!=pose)
  1995. {
  1996. mesh_data->transform=pose; mesh_data->transform_time.getUTC(); mesh_data->newVer();
  1997. makeGameVer(*mesh);
  1998. meshTransformChanged(*mesh);
  1999. Server.setElmShort(mesh->id);
  2000. }
  2001. }
  2002. }
  2003. void ProjectEx::transformApply(C MemPtr<UID> &elm_ids)
  2004. {
  2005. if(!ObjEdit.obj_elm )Gui.msgBox(S, "No object is currently opened");else
  2006. if( ObjEdit.mode()!=ObjView::TRANSFORM)Gui.msgBox(S, "Transform mode is not enabled");else
  2007. if( ObjEdit.trans.trans==PoseIdentity)Gui.msgBox(S, "Transformation is empty" );else
  2008. {
  2009. Pose trans=ObjEdit.trans.trans;
  2010. REPA(elm_ids)
  2011. if(Elm *obj =findElm(elm_ids[i] ))if(ElmObj * obj_data=obj -> objData())
  2012. if(Elm *mesh=findElm(obj_data->mesh_id))if(ElmMesh *mesh_data=mesh->meshData())if(mesh_data->canHaveCustomTransform())
  2013. if(ObjEdit.mesh_elm==mesh)ObjEdit.trans.apply();else
  2014. {
  2015. mesh_data->transform*=trans; mesh_data->transform_time.getUTC(); mesh_data->newVer();
  2016. makeGameVer(*mesh);
  2017. meshTransformChanged(*mesh);
  2018. Server.setElmShort(mesh->id);
  2019. }
  2020. }
  2021. }
  2022. void ProjectEx::transformApply(C MemPtr<UID> &elm_ids, C Matrix &matrix)
  2023. {
  2024. REPA(elm_ids)
  2025. if(Elm *obj =findElm(elm_ids[i] ))if(ElmObj * obj_data=obj -> objData())
  2026. if(Elm *mesh=findElm(obj_data->mesh_id))if(ElmMesh *mesh_data=mesh->meshData())if(mesh_data->canHaveCustomTransform())
  2027. {
  2028. mesh_data->transform*=matrix; mesh_data->transform_time.getUTC(); mesh_data->newVer();
  2029. makeGameVer(*mesh);
  2030. meshTransformChanged(*mesh);
  2031. Server.setElmShort(mesh->id);
  2032. }
  2033. }
  2034. void ProjectEx::transformBottom(C MemPtr<UID> &elm_ids)
  2035. {
  2036. REPA(elm_ids)
  2037. if(Elm *obj =findElm(elm_ids[i] ))if(ElmObj * obj_data=obj -> objData())
  2038. if(Elm *mesh=findElm(obj_data->mesh_id))if(ElmMesh *mesh_data=mesh->meshData())if(mesh_data->canHaveCustomTransform())
  2039. if(mesh_data->box.valid())
  2040. {
  2041. Vec offset(0, -mesh_data->box.min.y, 0);
  2042. offset/=mesh_data->transform().orn();
  2043. mesh_data->transform+=offset; mesh_data->transform_time.getUTC(); mesh_data->newVer();
  2044. makeGameVer(*mesh);
  2045. meshTransformChanged(*mesh);
  2046. Server.setElmShort(mesh->id);
  2047. }
  2048. }
  2049. void ProjectEx::transformBack(C MemPtr<UID> &elm_ids)
  2050. {
  2051. REPA(elm_ids)
  2052. if(Elm *obj =findElm(elm_ids[i] ))if(ElmObj * obj_data=obj -> objData())
  2053. if(Elm *mesh=findElm(obj_data->mesh_id))if(ElmMesh *mesh_data=mesh->meshData())if(mesh_data->canHaveCustomTransform())
  2054. if(mesh_data->box.valid())
  2055. {
  2056. Vec offset(0, 0, -mesh_data->box.min.z);
  2057. offset/=mesh_data->transform().orn();
  2058. mesh_data->transform+=offset; mesh_data->transform_time.getUTC(); mesh_data->newVer();
  2059. makeGameVer(*mesh);
  2060. meshTransformChanged(*mesh);
  2061. Server.setElmShort(mesh->id);
  2062. }
  2063. }
  2064. void ProjectEx::transformCenter(C MemPtr<UID> &elm_ids)
  2065. {
  2066. REPA(elm_ids)
  2067. if(Elm *obj =findElm(elm_ids[i] ))if(ElmObj * obj_data= obj-> objData())
  2068. if(Elm *mesh=findElm(obj_data->mesh_id))if(ElmMesh *mesh_data=mesh->meshData())if(mesh_data->canHaveCustomTransform())
  2069. if(mesh_data->box.valid())
  2070. {
  2071. Vec offset=-mesh_data->box.center();
  2072. offset/=mesh_data->transform().orn();
  2073. mesh_data->transform+=offset; mesh_data->transform_time.getUTC(); mesh_data->newVer();
  2074. makeGameVer(*mesh);
  2075. meshTransformChanged(*mesh);
  2076. Server.setElmShort(mesh->id);
  2077. }
  2078. }
  2079. void ProjectEx::transformCenterXZ(C MemPtr<UID> &elm_ids)
  2080. {
  2081. REPA(elm_ids)
  2082. if(Elm *obj =findElm(elm_ids[i] ))if(ElmObj * obj_data=obj -> objData())
  2083. if(Elm *mesh=findElm(obj_data->mesh_id))if(ElmMesh *mesh_data=mesh->meshData())if(mesh_data->canHaveCustomTransform())
  2084. if(mesh_data->box.valid())
  2085. {
  2086. Vec offset=-mesh_data->box.center().x0z();
  2087. offset/=mesh_data->transform().orn();
  2088. mesh_data->transform+=offset; mesh_data->transform_time.getUTC(); mesh_data->newVer();
  2089. makeGameVer(*mesh);
  2090. meshTransformChanged(*mesh);
  2091. Server.setElmShort(mesh->id);
  2092. }
  2093. }
  2094. void ProjectEx::transformRotYMinBox(C MemPtr<UID> &elm_ids)
  2095. {
  2096. Mesh edit; Memt<Vec2> vtxs;
  2097. REPA(elm_ids)
  2098. if(Elm *obj =findElm(elm_ids[i] ))if(ElmObj * obj_data=obj -> objData())
  2099. if(Elm *mesh=findElm(obj_data->mesh_id))if(ElmMesh *mesh_data=mesh->meshData())if(mesh_data->canHaveCustomTransform() && mesh_data->box.valid() && Load(edit, editPath(*mesh), game_path))
  2100. {
  2101. Matrix matrix=mesh_data->transform();
  2102. C MeshLod &lod=edit; vtxs.reserve(lod.vtxs()); REPA(lod)
  2103. {
  2104. C MeshPart &part=lod.parts[i]; if(!(part.part_flag&MSHP_HIDDEN))if(C Vec *src=part.base.vtx.pos())
  2105. {
  2106. int vs=part.base.vtxs();
  2107. Vec2 *dest=&vtxs[vtxs.addNum(vs)];
  2108. REP(vs)dest[i]=(src[i]*matrix.orn()).xz();
  2109. }
  2110. }
  2111. Vec2 axis; if(BestFit(vtxs.data(), vtxs.elms(), axis))
  2112. {
  2113. flt a=Angle(axis); a=Frac(a, PI_2); if(a>EPS && a<PI_2-EPS)
  2114. {
  2115. if(a>PI_4)a-=PI_2;
  2116. mesh_data->transform=matrix.rotateY(a);
  2117. mesh_data->transform_time.getUTC(); mesh_data->newVer();
  2118. makeGameVer(*mesh);
  2119. meshTransformChanged(*mesh);
  2120. Server.setElmShort(mesh->id);
  2121. }
  2122. }
  2123. vtxs.clear();
  2124. }
  2125. }
  2126. void ProjectEx::objSetBody(C MemPtr<UID> &elm_ids, C UID &body_id)
  2127. {
  2128. if(elm_ids.elms())
  2129. {
  2130. Elm *body=findElm(body_id, ELM_MESH);
  2131. if( !body_id.valid() || body)
  2132. {
  2133. REPA(elm_ids)
  2134. if(Elm *obj =findElm(elm_ids[i] ))if(ElmObj * obj_data=obj -> objData())
  2135. if(Elm *mesh=findElm(obj_data->mesh_id))if(ElmMesh *mesh_data=mesh->meshData())
  2136. {
  2137. C UID &actual_body_id=((mesh->id==body_id) ? UIDZero : body_id); // if trying to set body to self, then clear it instead
  2138. if(mesh_data->body_id!=actual_body_id)
  2139. {
  2140. if(obj==ObjEdit.obj_elm)ObjEdit.setBody(actual_body_id);else
  2141. {
  2142. mesh_data->newVer();
  2143. mesh_data->body_id=actual_body_id;
  2144. mesh_data->body_time.getUTC();
  2145. meshTransformChanged(*mesh, true);
  2146. Server.setElmShort(mesh->id);
  2147. }
  2148. }
  2149. }
  2150. refresh(false, false);
  2151. }
  2152. }
  2153. }
  2154. void ProjectEx::soundImportAs(C MemPtr<UID> &elm_ids, SOUND_CODEC codec, int rel_bit_rate)
  2155. {
  2156. Memt<UID> reloading;
  2157. FREPA(elm_ids)if(Elm *elm=findElm(elm_ids[i], ELM_SOUND))
  2158. {
  2159. Mems<Edit::FileParams> files=Edit::FileParams::Decode(elm->srcFile()); if(files.elms())
  2160. {
  2161. Edit::FileParams &file=files[0];
  2162. if(codec )file.getParam("codec" ).setValue((codec==SOUND_WAV) ? "raw" : CodecName(codec));else file.params.removeData(file.findParam("codec" ), true);
  2163. if(rel_bit_rate>=0)file.getParam("relBitRate").setValue(rel_bit_rate );else file.params.removeData(file.findParam("relBitRate"), true); file.params.removeData(file.findParam("relativeBitRate"), true);
  2164. elm->setSrcFile(Edit::FileParams::Encode(files)); Server.setElmShort(elm->id); reloading.add(elm->id);
  2165. }
  2166. }
  2167. elmReload(reloading);
  2168. }
  2169. void ProjectEx::mulSoundVolume(C MemPtr<UID> &elm_ids, flt volume)
  2170. {
  2171. if(!Equal(volume, 1) && volume>=0)
  2172. {
  2173. Memt<UID> changed;
  2174. FREPA(elm_ids)if(Elm *elm=findElm(elm_ids[i], ELM_SOUND))
  2175. {
  2176. Mems<Edit::FileParams> fps=Edit::FileParams::Decode(elm->srcFile());
  2177. flt v=volume;
  2178. if(C TextParam *p=FindTransform(fps, "volume"))v*=p->asFlt();
  2179. if(Equal(v, 1) )DelTransform(fps, "volume");
  2180. else SetTransform(fps, "volume", TextReal(v, -3));
  2181. elm->setSrcFile(Edit::FileParams::Encode(fps)); Server.setElmShort(elm->id); changed.add(elm->id);
  2182. }
  2183. elmReload(changed);
  2184. }
  2185. }
  2186. void ProjectEx::adjustAnimations(C UID &skel_id, C EditSkeleton &old_edit_skel, C Skeleton &old_skel, C Skeleton &new_skel, C MemPtr<Mems<IndexWeight> > &bone_weights, int old_bone_as_root)
  2187. {
  2188. int difference=0; // 0=none, 1=only name/types, 2=full
  2189. MemtN<int, 256> old_to_new;
  2190. if(old_bone_as_root>=0){full_difference: difference=2;}else
  2191. {
  2192. old_to_new.setNum(old_skel.bones.elms()); // no need to initialize to -1 because all will be set in the loop and used only if "difference==1"
  2193. REPAD(ob, old_skel.bones) // check only old bones, because we have animations only for old bones
  2194. {
  2195. C SkelBone &old_bone=old_skel.bones[ob];
  2196. int new_bone_i=-1; REPD(nb, Min(bone_weights.elms(), new_skel.bones.elms()))
  2197. {
  2198. C Mems<IndexWeight> &weights=bone_weights[nb];
  2199. if(weights.elms()>1)goto full_difference; // if there's any new bone with more than 1 weight, then we have a full difference
  2200. if(weights.elms() && weights[0].index==ob)
  2201. {
  2202. if(new_bone_i>=0)goto full_difference; // if there was already another new bone referencing this old bone, then we have a full difference
  2203. new_bone_i=nb;
  2204. }
  2205. }
  2206. if(new_bone_i<0)goto full_difference; // bone not found = full difference
  2207. C SkelBone &new_bone=new_skel.bones[new_bone_i];
  2208. if( Dot (old_bone.dir , new_bone.dir )<0.9999f // orientation was changed
  2209. || Dot (old_bone.perp, new_bone.perp)<0.9999f
  2210. || !Equal(old_bone.pos , new_bone.pos ) // position was changed
  2211. || (old_bone.parent==0xFF)!=(new_bone.parent==0xFF) // one has parent, but the other one doesn't
  2212. )goto full_difference; // full difference
  2213. if(old_bone.parent!=0xFF /*&& new_bone.parent!=0xFF - we've already checked if one has parent, but the other one doesn't*/) // if both have parents
  2214. {
  2215. if(!InRange(new_bone.parent, bone_weights))goto full_difference; // can't access new parent
  2216. C Mems<IndexWeight> &new_parent=bone_weights[new_bone.parent];
  2217. if(new_parent.elms()!=1 || new_parent[0].index!=old_bone.parent)goto full_difference; // if new parent doesn't point to old parent
  2218. }
  2219. if(!Equal(old_bone.name , new_bone.name ) // name was changed
  2220. || old_bone.type !=new_bone.type // type was changed
  2221. || old_bone.type_index!=new_bone.type_index // type_index was changed
  2222. || old_bone.type_sub !=new_bone.type_sub // type_sub was changed
  2223. )difference=1; // set difference but keep checking if we find bigger
  2224. old_to_new[ob]=new_bone_i;
  2225. }
  2226. }
  2227. if(difference) // if there's any difference
  2228. REPA(elms) // iterate all elements
  2229. {
  2230. Elm &anim_elm=elms[i]; if(ElmAnim *anim_data=anim_elm.animData())if(anim_data->skel_id==skel_id) // process animations using this skeleton
  2231. {
  2232. Animation temp, *anim=getAnim(anim_elm.id, temp);
  2233. if(anim->is()) // process if has any data (this also skips animations that haven't finished downloading from the server)
  2234. {
  2235. if(difference==1) // if difference is only in name/types
  2236. REPA(anim->bones)
  2237. {
  2238. AnimBone &bone=anim->bones[i];
  2239. int old_bone_i=(RenameAnimBonesOnSkelChange ? old_skel.findBoneI(bone.name) // if we always adjust anim bone names then we can use this
  2240. : old_skel.findBoneI(bone.name, bone.type, bone.type_index, bone.type_sub)); // use types in case animation was from another skeleton and we haven't adjusted types, however avoid this because it can break in rare circumstances when for some reason 'old_skel' had 'setBoneTypes' called
  2241. if(InRange(old_bone_i, old_to_new))
  2242. {
  2243. int new_bone_i=old_to_new[old_bone_i]; if(InRange(new_bone_i, new_skel.bones)){bone.id()=new_skel.bones[new_bone_i]; continue;} // continue so we don't remove this bone
  2244. }
  2245. anim->bones.remove(i, true);
  2246. }else // have to perform full adjustment
  2247. {
  2248. anim->adjustForSameTransformWithDifferentSkeleton(old_skel, new_skel, old_bone_as_root, bone_weights, anim_data->rootFlags()|(old_edit_skel.rootZero() ? 0 : ROOT_BONE_POSITION|ROOT_START_IDENTITY));
  2249. anim->optimize(); // 'optimize' after 'adjustForSameTransformWithDifferentSkeleton' because it may generate lot of keyframes
  2250. }
  2251. anim_data->newVer();
  2252. anim_data->file_time.getUTC();
  2253. Save(*anim, gamePath(anim_elm.id)); savedGame(anim_elm);
  2254. Server.setElmLong(anim_elm.id);
  2255. }
  2256. }
  2257. }
  2258. }
  2259. void ProjectEx::offsetAnimations(C Skeleton &old_skel, C Skeleton &new_skel, C UID &skel_id)
  2260. {
  2261. REPA(elms) // iterate all project elements
  2262. {
  2263. Elm &anim_elm=elms[i]; if(ElmAnim *anim_data=anim_elm.animData())if(anim_data->skel_id==skel_id) // process animations using this skeleton
  2264. {
  2265. Animation temp, *anim=getAnim(anim_elm.id, temp);
  2266. if(anim->is()) // process if has any data (this also skips animations that haven't finished downloading from the server)
  2267. {
  2268. anim->adjustForSameSkeletonWithDifferentPose(old_skel, new_skel);
  2269. anim_data->newVer();
  2270. anim_data->file_time.getUTC();
  2271. Save(*anim, gamePath(anim_elm.id)); savedGame(anim_elm);
  2272. Server.setElmLong(anim_elm.id);
  2273. }
  2274. }
  2275. }
  2276. }
  2277. bool ProjectEx::validElm(Elm &elm)
  2278. {
  2279. if(elm.type)
  2280. if(elm.type==ELM_FOLDER || elm.type==ELM_LIB || elm.initialized())
  2281. {
  2282. if(elm.type==ELM_WORLD)return elm.worldData()->valid();
  2283. if(elm.type==ELM_OBJ)
  2284. {
  2285. ElmObj *data=elm.objData();
  2286. if(Elm *mesh=findElm(data->mesh_id))if(!validElm(*mesh))return false;
  2287. }
  2288. if(elm.type==ELM_MESH)
  2289. {
  2290. ElmMesh *data=elm.meshData();
  2291. if(Elm *skel=findElm(data->skel_id))if(!validElm(*skel))return false;
  2292. if(Elm *phys=findElm(data->phys_id))if(!validElm(*phys))return false;
  2293. }
  2294. return true;
  2295. }
  2296. return false;
  2297. }
  2298. void ProjectEx::duplicate(Memc<UID> &ids, MemPtr<UID> duplicated, C Str &suffix)
  2299. {
  2300. clearListSel();
  2301. bool manually_by_user=false;
  2302. Memc<UID> temp; if(!duplicated){duplicated.point(temp); manually_by_user=true;} duplicated.clear();
  2303. FREPA(ids) // !! process in order, this is important so 'duplicated' matches the 'ids' !!
  2304. if(Elm *src=findElm(ids[i]))if(validElm(*src))
  2305. {
  2306. if(src->type!=ELM_CODE)flushElm(src->id);
  2307. Elm &dup=::Project::newElm(); dup.type=src->type; // set 'type' before calling 'copyParams'
  2308. dup.copyParams(*src).setName(src->name+suffix); // call 'setName' after 'copyParams'
  2309. duplicated.add(dup.id);
  2310. switch(src->type)
  2311. {
  2312. case ELM_OBJ_CLASS:
  2313. {
  2314. EditObject params; params.load(editPath(*src)); params.setType(true, dup.id, edit_path); Save(params, editPath(dup)); makeGameVer(dup);
  2315. }break;
  2316. case ELM_CODE:
  2317. {
  2318. Str code; codeGet(src->id, code); SaveCode(code, codePath(dup));
  2319. }break;
  2320. case ELM_OBJ:
  2321. if(ElmObj *src_obj_data=src->objData())
  2322. if(ElmObj *dup_obj_data=dup.objData())
  2323. {
  2324. File f; if(f.readTry(editPath(*src)))SafeOverwrite(f, editPath(dup)); // copy obj edit file
  2325. if(Elm *src_mesh=findElm(src_obj_data->mesh_id)) // get source mesh
  2326. {
  2327. if(Elm *dup_mesh=getObjMeshElm(dup.id, false, false)) // grab the duplicated mesh, but don't send to the server yet
  2328. {
  2329. flushElm(src_mesh->id); // flush it first
  2330. dup_obj_data->mesh_id=dup_mesh->id;
  2331. dup_mesh->copyParams(*src_mesh, false); // copy params
  2332. if(f.readTry(editPath(*src_mesh)) && SafeOverwrite(f, editPath(*dup_mesh)))SavedMesh(editPath(*dup_mesh)); // copy mesh edit file
  2333. if(ElmMesh *src_mesh_data=src_mesh->meshData())
  2334. if(ElmMesh *dup_mesh_data=dup_mesh->meshData())
  2335. {
  2336. dup_mesh_data->obj_id=dup.id; // this will get cleared in 'copyParams', so reassign again
  2337. if(Elm *src_skel=findElm(src_mesh_data->skel_id)) // get source skel
  2338. {
  2339. if(Elm *dup_skel=getObjSkelElm(dup.id, false, false)) // grab the duplicated skeleton, but don't send to the server yet
  2340. {
  2341. flushElm(src_skel->id); // flush it first
  2342. dup_skel->copyParams(*src_skel, false); // copy params
  2343. dup_skel->skelData()->mesh_id=dup_mesh->id; // this will get cleared in 'copyParams', so reassign again
  2344. if(f.readTry(editPath(*src_skel)) && SafeOverwrite(f, editPath(*dup_skel))) SavedEditSkel(editPath(*dup_skel)); // copy skel edit file
  2345. if(f.readTry(gamePath(*src_skel)) && SafeOverwrite(f, gamePath(*dup_skel))){SavedSkel (gamePath(*dup_skel)); savedGame(*dup_skel);} // copy skel game file
  2346. Server.setElmFull(dup_skel->id); // send skel to server
  2347. }
  2348. }
  2349. if(Elm *src_phys=findElm(src_mesh_data->phys_id)) // get source phys
  2350. {
  2351. if(Elm *dup_phys=getObjPhysElm(dup.id, false, false)) // grab the duplicated phys body, but don't send to the server yet
  2352. {
  2353. flushElm(src_phys->id); // flush it first
  2354. dup_phys->copyParams(*src_phys, false); // copy params
  2355. dup_phys->physData()->mesh_id=dup_mesh->id; // this will get cleared in 'copyParams', so reassign again
  2356. if(f.readTry(editPath(*src_phys)) && SafeOverwrite(f, editPath(*dup_phys))) SavedEditPhys(editPath(*dup_phys)); // copy phys edit file
  2357. if(f.readTry(gamePath(*src_phys)) && SafeOverwrite(f, gamePath(*dup_phys))){SavedPhys (gamePath(*dup_phys)); savedGame(*dup_phys);} // copy phys game file
  2358. Server.setElmFull(dup_phys->id); // send phys to server
  2359. }
  2360. }
  2361. }
  2362. makeGameVer(*dup_mesh); // make game mesh at end (after setting skeleton)
  2363. Server.setElmFull(dup_mesh->id); // send mesh to server
  2364. }else dup_obj_data->mesh_id.zero(); // couldn't create a mesh
  2365. }
  2366. makeGameVer(dup); // make game obj at end (after mesh and phys)
  2367. }break;
  2368. case ELM_MINI_MAP: break; // do nothing
  2369. case ELM_WORLD: makeGameVer(dup); break; // save world settings
  2370. case ELM_ENV :
  2371. case ELM_ENUM :
  2372. case ELM_PANEL :
  2373. case ELM_TEXT_STYLE :
  2374. case ELM_MTRL :
  2375. case ELM_WATER_MTRL :
  2376. case ELM_PHYS_MTRL :
  2377. case ELM_GUI_SKIN :
  2378. case ELM_GUI :
  2379. case ELM_ICON :
  2380. case ELM_ICON_SETTS :
  2381. case ELM_IMAGE :
  2382. case ELM_IMAGE_ATLAS:
  2383. case ELM_FONT :
  2384. case ELM_PANEL_IMAGE:
  2385. case ELM_ANIM :
  2386. case ELM_SOUND :
  2387. case ELM_VIDEO :
  2388. case ELM_FILE :
  2389. {
  2390. File f; if(f.readTry(editPath(*src))) SafeOverwrite(f, editPath(dup));
  2391. if(f.readTry(gamePath(*src)))if(SafeOverwrite(f, gamePath(dup)))savedGame(dup);
  2392. }break;
  2393. }
  2394. Server.setElmFull(dup.id);
  2395. }
  2396. if(duplicated.elms())list_cur=duplicated.last();
  2397. list_sel=duplicated;
  2398. setList();
  2399. activateSources(); // rebuild sources if needed
  2400. if(manually_by_user)
  2401. {
  2402. if(duplicated.elms()==1)RenameElm.activate(duplicated[0]);
  2403. if(ElmChange *change=elm_undos.set(null, true))
  2404. {
  2405. change->type=ElmChange::RESTORE;
  2406. change->name="Copy";
  2407. REPA(duplicated)change->elms.binaryInclude(duplicated[i], Compare);
  2408. }
  2409. }
  2410. }
  2411. void ProjectEx::eraseElm(C UID &elm_id)
  2412. {
  2413. MtrlEdit.erasing(elm_id);
  2414. WaterMtrlEdit.erasing(elm_id);
  2415. PhysMtrlEdit.erasing(elm_id);
  2416. ObjEdit.erasing(elm_id);
  2417. WorldEdit.erasing(elm_id);
  2418. MiniMapEdit.erasing(elm_id);
  2419. EnvEdit.erasing(elm_id);
  2420. EnumEdit.erasing(elm_id);
  2421. ImageEdit.erasing(elm_id);
  2422. ImageAtlasEdit.erasing(elm_id);
  2423. IconSettsEdit.erasing(elm_id);
  2424. IconEdit.erasing(elm_id);
  2425. FontEdit.erasing(elm_id);
  2426. TextStyleEdit.erasing(elm_id);
  2427. PanelImageEdit.erasing(elm_id);
  2428. PanelEdit.erasing(elm_id);
  2429. GuiSkinEdit.erasing(elm_id);
  2430. GuiEdit.erasing(elm_id);
  2431. ObjClassEdit.erasing(elm_id);
  2432. SoundEdit.erasing(elm_id);
  2433. VideoEdit.erasing(elm_id);
  2434. AnimEdit.erasing(elm_id);
  2435. CodeEdit.erasing(elm_id);
  2436. AppPropsEdit.erasing(elm_id);
  2437. ImportTerrain.erasing(elm_id);
  2438. Synchronizer.erasing(elm_id);
  2439. ::Project::eraseElm(elm_id);
  2440. }
  2441. bool ProjectEx::eraseTex(C UID &tex_id)
  2442. {
  2443. Importer .excludeTex(tex_id);
  2444. Synchronizer.erasingTex(tex_id);
  2445. return ::Project::eraseTex (tex_id);
  2446. }
  2447. void ProjectEx::eraseWorldAreaObjs(C UID &world_id, C VecI2 &area_xy)
  2448. {
  2449. if(world_id==WorldEdit.elm_id)
  2450. if(Area *area=WorldEdit.findAreaLoaded(area_xy))
  2451. {
  2452. bool erased=false; REPA(area->objs)if(Obj *obj=area->objs[i])if(obj->removed){erased=true; WorldEdit.objs.removeData(obj, true);}
  2453. if(erased)area->setChangedObj();
  2454. return;
  2455. }
  2456. ::Project::eraseWorldAreaObjs(world_id, area_xy);
  2457. }
  2458. void ProjectEx::eraseRemoved()
  2459. {
  2460. Builder .stop(); // stop the builder in case it processes removed elements
  2461. Importer.stop();
  2462. pauseServer();
  2463. ::ProjectHierarchy::eraseRemoved();
  2464. setList();
  2465. Importer.investigate(root); // call after setting list because may rely on hierarchy
  2466. resumeServer(); // call after setting list because may rely on hierarchy
  2467. }
  2468. void ProjectEx::setElmParent(Memc<Edit::IDParam<UID> > &elms, bool adjust_elms, bool as_undo) // 'adjust_elms'=if this is performed because of undo, and in that case we need to remember current parents, so we can undo this change later
  2469. {
  2470. if(elms.elms())
  2471. {
  2472. ElmChange *change=null; if(as_undo)if(change=elm_undos.set(null, true))
  2473. {
  2474. change->type=ElmChange::SET_PARENT;
  2475. change->name="Change Parent";
  2476. }
  2477. TimeStamp time; time.getUTC();
  2478. FREPA(elms)
  2479. {
  2480. Elm *dest=findElm(elms[i].value); // !! first get desired parent before adjusting !! this can be null (no parent)
  2481. if(Elm *elm =findElm(elms[i].id )) // get element
  2482. {
  2483. if(adjust_elms)elms[i].value=elm->parent_id; // !! if we're adjusting, then set current parent, after setting 'dest' !!
  2484. if(!dest || ElmCanHaveChildren(dest->type))
  2485. if(ElmMovable(elm->type))if(!contains(*elm, dest))
  2486. {
  2487. if(change)
  2488. {
  2489. Edit::IDParam<UID> &elm_parent=change->elm_parents.New();
  2490. elm_parent.id =elm->id;
  2491. elm_parent.value=elm->parent_id; // remember the old parent
  2492. }
  2493. elm->setParent(dest, time);
  2494. Server.setElmParent(*elm);
  2495. }
  2496. }
  2497. }
  2498. setList();
  2499. activateSources(); // rebuild sources if needed
  2500. }
  2501. }
  2502. void ProjectEx::drag(Memc<UID> &elms, GuiObj *focus_obj, C Vec2 &screen_pos)
  2503. {
  2504. if(focus_obj==&list && elms.elms()) // move elements to another location
  2505. {
  2506. Elm *dest=list.visToElm(list.screenToVis(screen_pos)); if(dest)dest->opened(true);
  2507. if( !dest || ElmCanHaveChildren(dest->type))
  2508. {
  2509. ElmChange *change=elm_undos.set(null, true);
  2510. if(change)
  2511. {
  2512. change->type=ElmChange::SET_PARENT;
  2513. change->name="Change Parent";
  2514. }
  2515. TimeStamp time; time.getUTC();
  2516. FREPA(elms)if(Elm *elm=findElm(elms[i]))if(ElmMovable(elm->type))if(!contains(*elm, dest))
  2517. {
  2518. if(change)
  2519. {
  2520. Edit::IDParam<UID> &elm_parent=change->elm_parents.New();
  2521. elm_parent.id =elm->id;
  2522. elm_parent.value=elm->parent_id; // remember the old parent
  2523. }
  2524. elm->setParent(dest, time);
  2525. Server.setElmParent(*elm);
  2526. }
  2527. elms.clear(); // processed
  2528. refresh();
  2529. activateSources(); // rebuild sources if needed
  2530. }
  2531. }
  2532. }
  2533. void ProjectEx::collapse(Memc<UID> &ids, Memc<EEItem*> &items) {setListCurSel(); REPA(ids)if(Elm *elm=findElm(ids[i]))elm->opened(false); REPAO(items)->opened=false; setList(false, false);}
  2534. void ProjectEx::expand(Memc<UID> &ids, Memc<EEItem*> &items) {setListCurSel(); REPA(ids)if(Elm *elm=findElm(ids[i]))elm->opened(true ); REPAO(items)->opened=true ; setList(false, false);}
  2535. void ProjectEx::expandAll(Memc<UID> &ids, Memc<EEItem*> &items)
  2536. {
  2537. setListCurSel();
  2538. REPA(ids)
  2539. {
  2540. int e=findElmI(ids[i]); if(InRange(e, hierarchy))
  2541. {
  2542. elms[e].opened(true); // open this element
  2543. expandAll(hierarchy[e]); // open its children
  2544. }
  2545. }
  2546. REPAO(items)->opened=true; // TODO: here children too
  2547. setList(false, false);
  2548. }
  2549. void ProjectEx::expandAll(ElmNode &node)
  2550. {
  2551. REPA(node.children)
  2552. {
  2553. int child_i=node.children[i];
  2554. Elm &child =elms[child_i];
  2555. if( !child.removed() && ElmVisible(child.type))
  2556. {
  2557. child.opened(true);
  2558. ElmNode &child=hierarchy[child_i];
  2559. expandAll(child);
  2560. }
  2561. }
  2562. }
  2563. void ProjectEx::floodExisting(ElmNode &node, bool no_publish)
  2564. {
  2565. REPA(node.children)
  2566. {
  2567. int child_i=node.children[i];
  2568. Elm &child =elms[child_i];
  2569. if( !child.removed())
  2570. {
  2571. bool np=(no_publish | child.noPublish());
  2572. child.finalExists (true);
  2573. if(!np)child.finalPublish(true);
  2574. switch(child.type)
  2575. {
  2576. case ELM_ENUM : existing_enums .binaryInclude(child.id, Compare); break;
  2577. case ELM_OBJ_CLASS: existing_obj_classes.binaryInclude(child.id, Compare); break;
  2578. case ELM_FONT : existing_fonts .binaryInclude(child.id, Compare); if(!np)publish_fonts.binaryInclude(child.id, Compare); break;
  2579. case ELM_APP : existing_apps .binaryInclude(child.id, Compare); break;
  2580. }
  2581. ElmNode &child=hierarchy[child_i];
  2582. floodExisting(child, np);
  2583. }
  2584. }
  2585. }
  2586. void ProjectEx::setExisting()
  2587. {
  2588. REPAO(elms).resetFinal();
  2589. existing_apps .clear();
  2590. existing_fonts.clear();
  2591. Memc<UID> old_enums ; Swap(existing_enums , old_enums ); // this also clears 'existing_enums'
  2592. Memc<UID> old_obj_classes; Swap(existing_obj_classes, old_obj_classes); // this also clears 'existing_obj_classes'
  2593. Memc<UID> old_fonts ; Swap( publish_fonts , old_fonts ); // this also clears 'publish_fonts'
  2594. floodExisting(root);
  2595. if(!Same(existing_enums , old_enums )
  2596. || !Same(existing_obj_classes, old_obj_classes))enumChanged();
  2597. if(!Same( publish_fonts , old_fonts ))fontChanged();
  2598. }
  2599. int ProjectEx::CompareEnum(C Str &a, C Str &b) {return Compare(a, b);}
  2600. void ProjectEx::enumChanged(C UID &enum_id) // 'enum_id' can also point to OBJ_CLASS
  2601. {
  2602. obj_class_node.children.clear();
  2603. Memt<Str> obj_classes; FREPA(existing_obj_classes)if(Elm *obj_class=findElm(existing_obj_classes[i]))obj_classes.binaryInclude(obj_class->name, CompareEnum);
  2604. if(InRange(OBJ_ACCESS_TERRAIN, ObjAccessNamesElms))obj_class_node+=ObjAccessNames[OBJ_ACCESS_TERRAIN];
  2605. if(InRange(OBJ_ACCESS_GRASS , ObjAccessNamesElms))obj_class_node+=ObjAccessNames[OBJ_ACCESS_GRASS ];
  2606. if(InRange(OBJ_ACCESS_OVERLAY, ObjAccessNamesElms))obj_class_node+=ObjAccessNames[OBJ_ACCESS_OVERLAY];
  2607. if(obj_classes.elms())obj_class_node++;
  2608. FREPA(obj_classes)obj_class_node+=obj_classes[i];
  2609. param_type_node.children.clear();
  2610. ASSERT(PARAM_BOOL ==0); (param_type_node+="Bool").desc("Boolean value\nFalse/True");
  2611. ASSERT(PARAM_INT ==1); (param_type_node+="Int").desc("Integer value");
  2612. ASSERT(PARAM_FLT ==2); (param_type_node+="Real").desc("Floating point value");
  2613. ASSERT(PARAM_STR ==3); (param_type_node+="String").desc("Text string");
  2614. {
  2615. ASSERT(PARAM_ENUM==4); Node<MenuElm> &e=param_type_node+="Enum"; e.flag(e.flag()|MENU_NOT_SELECTABLE).desc("Custom enum");
  2616. Memt<Str> enums; FREPA(existing_enums)if(Elm *e=findElm(existing_enums[i]))enums.binaryInclude(e->name, CompareEnum);
  2617. FREPA(enums)e+=enums[i];
  2618. }
  2619. ASSERT(PARAM_VEC2 ==5); (param_type_node+="Vec2").desc("2D vector");
  2620. ASSERT(PARAM_VEC ==6); (param_type_node+="Vec").desc("3D vector");
  2621. ASSERT(PARAM_VEC4 ==7); (param_type_node+="Vec4").desc("4D vector");
  2622. ASSERT(PARAM_COLOR==8); (param_type_node+="Color").desc("Color value");
  2623. ASSERT(PARAM_ID ==9); (param_type_node+="Element ID").desc("Project element or custom ID\nIn order to set its value please drag and drop a project element to the textline, or manually enter an ID \"UID(..)\"\nYou can drag and drop multiple elements at the same time."); // can't use slash in the name because ComboBox doesn't support it
  2624. ObjClassEdit.enumChanged();
  2625. ObjEdit.enumChanged(enum_id);
  2626. WorldEdit.enumChanged();
  2627. CodeEdit.makeAuto (); // need to make auto header because it relies on enums
  2628. }
  2629. void ProjectEx::meshVariationChanged()
  2630. {
  2631. ObjEdit.meshVariationChanged();
  2632. WorldEdit.meshVariationChanged();
  2633. IconEdit.meshVariationChanged();
  2634. }
  2635. void ProjectEx::fontChanged()
  2636. {
  2637. font_node.children.clear();
  2638. Memt<Str> names; FREPA(publish_fonts)if(Elm *font=findElm(publish_fonts[i]))names.binaryInclude(font->name, CompareEnum);
  2639. font_node+=" "; // DefaultFont
  2640. FREPA(names)font_node+=names[i];
  2641. TextStyleEdit.fontChanged();
  2642. }
  2643. void ProjectEx::panelChanged(C UID &elm_id)
  2644. {
  2645. GuiSkinEdit.objs.setRect();
  2646. }
  2647. void ProjectEx::panelImageChanged(C UID &elm_id)
  2648. {
  2649. GuiSkinEdit.objs.setRect();
  2650. }
  2651. void ProjectEx::mtrlTexChanged()
  2652. {
  2653. if(list.file_size && list.its!=ElmList::ITS_ELM) // mtrl tex file size have changed
  2654. #if 1 // delayed
  2655. AtomicSet(TIG.got_new_data, true);
  2656. #else // immediate
  2657. refresh(false, false);
  2658. #endif
  2659. }
  2660. void ProjectEx::DragElmsStart(ProjectEx &proj)
  2661. {
  2662. // get elements from project list
  2663. proj.drag_list_sel.clear();
  2664. FREPA(proj.list.sel)if(ListElm *list_elm=proj.list.absToData(proj.list.sel[i]))if(list_elm->elm)proj.drag_list_sel.add(list_elm->elm->id); // list in order
  2665. // start drag
  2666. proj.dragElmsStart();
  2667. }
  2668. void ProjectEx::dragElmsStart()
  2669. {
  2670. // get element types
  2671. bool elm_types[ELM_NUM_ANY]; Zero(elm_types);
  2672. REPA(drag_list_sel)if(Elm *elm=findElm(drag_list_sel[i]))elm_types[elm->type]=true;
  2673. // enable compatible types
  2674. REPAD(i, elm_types)if(!elm_types[i])
  2675. REPAD(j, elm_types)if( elm_types[j] && ElmCompatible(ELM_TYPE(i), ELM_TYPE(j))){elm_types[i]=true; break;}
  2676. REPAO(PropEx::props).dragStart(elm_types);
  2677. }
  2678. void ProjectEx::DragElmsCancel(ProjectEx &proj)
  2679. {
  2680. REPAO(PropEx::props).dragEnd();
  2681. }
  2682. void ProjectEx::DragElmsFinish(ProjectEx &proj, GuiObj *obj, C Vec2 &screen_pos)
  2683. {
  2684. Drag(proj.drag_list_sel, obj, screen_pos);
  2685. REPAO(PropEx::props).dragEnd();
  2686. }
  2687. void ProjectEx::update()
  2688. {
  2689. // auto save
  2690. if(Time.appTime()>=save_time)save(SAVE_AUTO);
  2691. // elm sound play
  2692. {
  2693. if(Gui.ms()==&list.sound_play)list.lit=list.elmToVis(findElm(list.sound_play.lit_id));
  2694. Elm *elm=list.visToElm(list.lit);
  2695. if( elm && (elm->type==ELM_SOUND || sound.playing()))
  2696. {
  2697. list.sound_play.lit_id=elm->id;
  2698. list.sound_play.image =((sound.playing() && list.sound_play.lit_id==list.sound_play.play_id || elm->type!=ELM_SOUND) ? icon_stop : icon_play);
  2699. list.sound_play.posRU(Vec2(region.rect().w()-region.slidebarSize(), 0));
  2700. list.addChild(list.sound_play, list.visToAbs(list.lit), 0);
  2701. }else list-=list.sound_play;
  2702. }
  2703. // list updates
  2704. if(!(Time.frame()&63)) // process approximately once per second
  2705. {
  2706. bool refresh=false;
  2707. // file sizes
  2708. if(file_size_getter.created())
  2709. {
  2710. bool busy=file_size_getter.busy(); // !! check this before calling 'get' !!
  2711. if( file_size_getter.get ())
  2712. {
  2713. if(file_size_getter_step==0) // game files
  2714. {
  2715. REPA(file_size_getter.elms)
  2716. {
  2717. FileSizeGetter::Elm &file_size=file_size_getter.elms[i];
  2718. if(Elm *elm=findElm(file_size.id))if(elm->file_size<0)elm->file_size=file_size.file_size; // set file size only if unknown, because if it's known, then most likely it has more recent info than from 'FileSizeGetter'
  2719. }
  2720. }else // textures
  2721. {
  2722. REPA(file_size_getter.elms)
  2723. {
  2724. FileSizeGetter::Elm &file_size=file_size_getter.elms[i];
  2725. TexInfos(file_size.id)->file_size=file_size.file_size;
  2726. }
  2727. }
  2728. file_size_getter.clear();
  2729. refresh=true;
  2730. }
  2731. if(!busy) // proceed to the next step
  2732. if(file_size_getter_step==0)
  2733. {
  2734. file_size_getter_step=1;
  2735. file_size_getter.get(tex_path);
  2736. }
  2737. }
  2738. // texture info
  2739. if(TIG.got_new_data)
  2740. {
  2741. AtomicSet(TIG.got_new_data, false);
  2742. refresh=true;
  2743. }
  2744. if(refresh)T.refresh(false, false);
  2745. }
  2746. REPA(MT)
  2747. if(MT.guiObj(i)==&list)
  2748. if(ListElm *elm_gui=list.curToListElm())
  2749. {
  2750. if(EEItem *item=elm_gui->item) // Esenthel Engine Library (headers+sources+folders, all const)
  2751. {
  2752. if((list.flag&LIST_MULTI_SEL) ? list.selMode()==LSM_SET : true)if(MT.bp(i) && !MT.bd(i))elmToggle(item);
  2753. }else // use 'else' because after 'setList' the 'elm' pointer may be invalid
  2754. if(Elm *elm=elm_gui->elm)
  2755. {
  2756. if(MT.bp(i))Gui.drag(DragElmsFinish, T, MT.touch(i), DragElmsStart, DragElmsCancel);
  2757. if((list.flag&LIST_MULTI_SEL) ? list.selMode()==LSM_SET : true)
  2758. {
  2759. if(elm_gui->hasVisibleChildren() && MT.pos(i).x<=list.screenPos().x+elm_gui->offset+list.columnWidth(0)) // open arrow
  2760. {
  2761. if(MT.bp(i) && !MT.bd(i))
  2762. {
  2763. elm->opened(!elm->opened());
  2764. refresh(false, false);
  2765. }
  2766. }else
  2767. switch(elm->type)
  2768. {
  2769. case ELM_FOLDER:
  2770. case ELM_LIB :
  2771. case ELM_CODE :
  2772. {
  2773. if(MT.tappedFirst(i))
  2774. {
  2775. elmToggle(elm);
  2776. list.tapped_vis =list.cur;
  2777. list.tapped_time=Time.appTime()+Time.ad()+(MT.touch(i) ? TouchDoubleClickTime : DoubleClickTime); // disable mouse buttons during this time
  2778. list.tapped_open=false; // disable
  2779. }
  2780. }break;
  2781. case ELM_APP:
  2782. {
  2783. if(MT.tappedFirst(i))
  2784. {
  2785. list.tapped_vis =list.cur;
  2786. list.tapped_time=Time.appTime()+Time.ad()+(MT.touch(i) ? TouchDoubleClickTime : DoubleClickTime); // wait until potential double click, if it won't occur, then open element
  2787. list.tapped_open=true; // open
  2788. }else
  2789. if(MT.bd(i))
  2790. {
  2791. list.tapped_vis=-1; // cancel checking for tap open
  2792. elmToggle(elm);
  2793. MT.eat(i, 0); // eat so tap won't be called
  2794. }
  2795. }break;
  2796. default: if(MT.bd(i))elmToggle(elm); break;
  2797. }
  2798. }
  2799. }
  2800. }
  2801. if(region.contains(Gui.ms()))
  2802. {
  2803. if(Ms.bp(1))
  2804. {
  2805. if(Kb.ctrlCmd()) // paste elm ID to codes
  2806. {
  2807. if(Mode()==MODE_CODE)if(Elm *elm=list.visToElm(list.lit))if(ElmCanAccessID(elm->type))
  2808. {
  2809. CodeEdit.paste(Kb.shift() ? S+'"'+EncodeFileName(elm->id)+'"' : elm->id.asCString());
  2810. CodeEdit.kbSet();
  2811. }
  2812. }else // element conext menu
  2813. {
  2814. list.cur=list.lit; if(!list.sel.has(list.visToAbs(list.lit)))list.setCur(list.lit);
  2815. elmMenu(Ms.pos());
  2816. }
  2817. }else
  2818. if(Ms.bp(2))
  2819. {
  2820. if(Kb.ctrlCmd()) // copy elm ID to clipboard
  2821. {
  2822. if(Elm *elm=list.visToElm(list.lit))
  2823. {
  2824. ClipSet(ElmCanAccessID(elm->type) ? (Kb.shift() ? S+'"'+EncodeFileName(elm->id)+'"' : elm->id.asCString())+" /* "+Replace(elmFullName(elm), "*/", "*\\")+" */" : "This element ID is not available");
  2825. }
  2826. }else Misc.hide_proj.push(); // hide project view
  2827. }else
  2828. if(Ms.bp(4))
  2829. {
  2830. if(ListElm *elm=list.litToListElm())
  2831. {
  2832. if(!elm->close()) // try closing highlighted element
  2833. for(; --list.lit>=0; )if(ListElm *e=list.litToListElm())if(e->offset<elm->offset && e->close())break; // find first parent
  2834. refresh(false, false);
  2835. list.scrollTo(list.lit-4); // scroll after refresh
  2836. }
  2837. }
  2838. }
  2839. REPA(Touches)if(Touches[i].on() && Touches[i].guiObj()==&list && Touches[i].life()>=LongPressTime && !Touches[i].dragging() && !Gui.dragging() && !Gui.menu())
  2840. {
  2841. elmMenu(Touches[i].pos(), true);
  2842. Gui.dragCancel();
  2843. }
  2844. KbSc esc(KB_ESC);
  2845. if( esc.pd())
  2846. {
  2847. esc.eat();
  2848. if(Gui.kb()==&filter && filter().is())filter.clear();else Mode.kbToggle();
  2849. }
  2850. if(Gui.kb()==&filter && Kb.kf(KB_ENTER))
  2851. {
  2852. list.setCur(0);
  2853. list.kbSet();
  2854. }
  2855. updateMenu();
  2856. }
  2857. int ProjectEx::CompareChildren(C int &a, C int &b)
  2858. {
  2859. Elm &ea=Proj.elms[a], &eb=Proj.elms[b];
  2860. if(int c=Compare (ElmOrder(ea.type), ElmOrder(eb.type)))return c;
  2861. if(int c=CompareNumber( ea.name , eb.name ))return c;
  2862. return Compare ( ea.id , eb.id );
  2863. }
  2864. int ProjectEx::CompareChildren(C EEItem &a, C Elm &b)
  2865. {
  2866. if(int c=Compare(ElmOrder(a.type ), ElmOrder(b.type)))return c;
  2867. if(int c=Compare( a.base_name , b.name ))return c;
  2868. return 0;
  2869. }
  2870. bool ProjectEx::hasInvalid(ElmNode &node)
  2871. {
  2872. FREPA(node.children)
  2873. {
  2874. int child_i=node.children[i];
  2875. ElmNode &child =hierarchy[child_i];
  2876. Elm &elm =elms [child_i];
  2877. if(!elm.removed() && !elm.noPublish() && ElmVisible(elm.type))if(invalidRefs(elm) || hasInvalid(child))return true;
  2878. }
  2879. return false;
  2880. }
  2881. void ProjectEx::getActiveAppElms(Memt<Elm*> &app_elms, C UID &app_id, ElmNode &node, bool inside_valid)
  2882. {
  2883. REPA(node.children)
  2884. {
  2885. int child_i=node.children[i];
  2886. ElmNode &child =hierarchy[child_i];
  2887. Elm &elm =elms [child_i];
  2888. if(!elm.removed() && !elm.noPublish())
  2889. {
  2890. if(inside_valid)app_elms.add(&elm);
  2891. getActiveAppElms(app_elms, app_id, child, (elm.type==ELM_LIB) ? true : (elm.type==ELM_APP) ? (elm.id==app_id) : inside_valid); // include elements from all libraries and from active app only, in other case inherit valid from the parent
  2892. }
  2893. }
  2894. }
  2895. void ProjectEx::getActiveAppElms(Memt<Elm*> &app_elms)
  2896. {
  2897. getActiveAppElms(app_elms, curApp(), root, false); // set 'false' to ignore sources placed in root
  2898. }
  2899. void ProjectEx::activateSources(int rebuild) // -1=never, 0=auto, 1=always
  2900. {
  2901. UID cur_app=curApp(); if(app_id!=cur_app){CodeEdit.makeAuto(); app_id=cur_app;} // set last app id to currently available, 'makeAuto' because 'EE_APP_NAME' relies on active app
  2902. CodeEdit.clearActiveSources();
  2903. Memt<Elm*> app_elms; getActiveAppElms(app_elms); FREPA(app_elms)if(app_elms[i]->type==ELM_CODE)CodeEdit.activateSource(app_elms[i]->id);
  2904. if(rebuild>=0)CodeEdit.activateApp(rebuild>=1);else CodeEdit.makeAuto(); // if not activating app then call 'makeAuto' which will activate the auto header
  2905. }
  2906. void ProjectEx::ActivateApp(ElmList::AppCheck &app_check) {Proj.activateApp(app_check.app_id);}
  2907. bool ProjectEx::activateApp(C UID &elm_id )
  2908. {
  2909. if(findElm(elm_id, ELM_APP))
  2910. {
  2911. UID old_app=curApp(); // get old app
  2912. T.app_id=elm_id; // set new app
  2913. REPAO(list.app_checks).set(list.app_checks[i].app_id==app_id, QUIET); // set all checkboxes states
  2914. if(old_app!=curApp()) // if changed app
  2915. {
  2916. CodeEdit.makeAuto(); // we've changed app so we need to make auto here
  2917. activateSources(1);
  2918. }
  2919. return true;
  2920. }
  2921. return false;
  2922. }
  2923. void ProjectEx::setList(EEItem &item, int depth, bool parent_removed, bool parent_contains_name)
  2924. {
  2925. bool this_contains_name=false, child_contains_name=false;
  2926. if(filter().is())
  2927. {
  2928. this_contains_name=FlagTest(item.flag, ELM_CONTAINS_NAME);
  2929. child_contains_name=FlagTest(item.flag, ELM_CONTAINS_NAME_CHILD);
  2930. if(!(child_contains_name || this_contains_name || parent_contains_name))return;
  2931. }
  2932. bool opened=(item.opened || child_contains_name || FlagTest(item.flag, ELM_EDITED_CHILD));
  2933. ListElm &e=list.data.New().set(item, opened, depth, parent_removed);
  2934. if(opened) // list children
  2935. FREPA(item.children)setList(item.children[i], depth+1, parent_removed, parent_contains_name || this_contains_name);
  2936. }
  2937. void ProjectEx::includeElmFileSize(ListElm &e, ElmNode &node)
  2938. {
  2939. FREPA(node.children)
  2940. {
  2941. int child_i=node.children[i];
  2942. Elm &elm =elms[child_i];
  2943. if(!elm.removed() || show_removed())
  2944. if( elm.publish() || list.include_unpublished_elm_size)
  2945. {
  2946. e.includeSize(elm);
  2947. includeElmFileSize(e, hierarchy[child_i]);
  2948. }
  2949. }
  2950. }
  2951. void ProjectEx::includeElmNotVisibleFileSize(ListElm &e, ElmNode &node)
  2952. {
  2953. FREPA(node.children)
  2954. {
  2955. int child_i=node.children[i];
  2956. Elm &elm =elms[child_i];
  2957. if(!ElmVisible(elm.type))
  2958. if(!elm.removed() || show_removed())
  2959. if( elm.publish() || list.include_unpublished_elm_size)
  2960. {
  2961. e.includeSize(elm);
  2962. includeElmFileSize(e, hierarchy[child_i]);
  2963. }
  2964. }
  2965. }
  2966. bool ProjectEx::hasVisibleChildren(C ElmNode &node)C
  2967. {
  2968. REPA(node.children)
  2969. {
  2970. int child_i=node.children[i];
  2971. C Elm &elm =elms[child_i];
  2972. if(ElmVisible(elm.type))
  2973. {
  2974. C ElmNode &child=hierarchy[child_i];
  2975. if(!elm.removed() || show_removed() || FlagTest(child.flag, ELM_EDITED|ELM_EDITED_CHILD))
  2976. if(list.show_elm_type[elm.type] || hasVisibleChildren(child))return true;
  2977. }
  2978. }
  2979. return false;
  2980. }
  2981. void ProjectEx::setList(ElmNode &node, int depth, int vis_parent, bool parent_removed, bool parent_contains_name, bool parent_no_publish)
  2982. {
  2983. node.children.sort(CompareChildren);
  2984. EEItem *ee=null; if(&node==&root && CodeEdit.items.elms() && list.show_all_elm_types)ee=&CodeEdit.items.first();
  2985. FREPA(node.children)
  2986. {
  2987. int child_i=node.children[i];
  2988. ElmNode &child =hierarchy[child_i];
  2989. Elm &elm =elms [child_i];
  2990. if(ee && CompareChildren(*ee, elm)<0){setList(*ee, depth, parent_removed, parent_contains_name); ee=null;} // if "Esenthel Engine" item should be included before this element
  2991. if(!elm.removed() || show_removed() || FlagTest(child.flag, ELM_EDITED|ELM_EDITED_CHILD))if(ElmVisible(elm.type))
  2992. {
  2993. bool this_contains_name=false, child_contains_name=false;
  2994. if(filter().is())
  2995. {
  2996. this_contains_name=FlagTest(child.flag, ELM_CONTAINS_NAME);
  2997. child_contains_name=FlagTest(child.flag, ELM_CONTAINS_NAME_CHILD);
  2998. if(!(child_contains_name || this_contains_name || parent_contains_name))continue;
  2999. }
  3000. bool opened=(elm.opened() || child_contains_name || FlagTest(child.flag, ELM_EDITED_CHILD) || list.list_all_children), removed=(parent_removed || elm.removed()), no_publish=(parent_no_publish || elm.noPublish()), invalid=(!removed && !no_publish && invalidRefs(elm));
  3001. if(list.show_elm_type[elm.type])
  3002. {
  3003. int elm_list_index=list.data.elms();
  3004. list.data.New().set(elm, child, depth, vis_parent, parent_removed);
  3005. if(opened)setList(child, depth+1, list.flat_is ? -1 : elm_list_index, removed, filter_is_id ? false : (parent_contains_name || this_contains_name), no_publish);else // list children, don't set 'parent_contains_name' when filter is by ID to make sure that desired element is listed last and its children are not listed at all
  3006. if(!removed && !no_publish && !invalid)invalid=hasInvalid(child); // if not listing children, then check if any of them are invalid and mark self as invalid
  3007. ListElm &e=list.data[elm_list_index]; // !! get reference after calling 'setList' which may invalidate it if it was called after getting the ref !!
  3008. e.hasVisibleChildren(list.flat_is ? false : (list.data.elms()>elm_list_index+1 || hasVisibleChildren(child)), opened); // first check if any new elements were listed after this one, because this check is faster than 'hasVisibleChildren'
  3009. if(list.file_size)
  3010. if(!no_publish || list.include_unpublished_elm_size)
  3011. {
  3012. e.includeSize(elm);
  3013. bool included_all_children=false;
  3014. if(list.ics) // include children size
  3015. if(!opened || list.ics==ElmList::ICS_ALWAYS) // ICS_FOLDED or ICS_ALWAYS
  3016. {
  3017. if(opened && list.show_all_elms) // if element is opened (then it means we've listed its children), and if all elements are listed, then we can use an optimization by summing just the first children sizes from the list, instead of all children recursively, because those first children will already include their children sizes
  3018. for(int i=elm_list_index+1; i<list.data.elms(); i++) // iterate all elements added after this one (this will be its children and their children)
  3019. {
  3020. ListElm &child=list.data[i];
  3021. if(child.depth==depth+1)e.includeSize(child); // add only for direct children (and not children of children)
  3022. }else {includeElmFileSize(e, child); included_all_children=true;} // need to process all children recursively
  3023. }
  3024. if(!included_all_children) // if we haven't included all children yet
  3025. includeElmNotVisibleFileSize(e, child); // always include !ElmVisible without checking for 'ics', because they are never listed
  3026. }
  3027. if(invalid)
  3028. {
  3029. list.warnings.New().create(elm_list_index, true, e.offset);
  3030. e.desc="Element uses other elements which are not found, marked as removed or have publishing disabled";
  3031. }else
  3032. if(!removed && no_publish)
  3033. {
  3034. list.warnings.New().create(elm_list_index, false, e.offset);
  3035. e.desc="Element will not be included in publishing";
  3036. }
  3037. if(!removed && elm.type==ELM_APP && existing_apps.elms()>1) // create checkboxes only if there are more than 1 apps
  3038. {
  3039. flt x=Misc.rect().w()-region.slidebarSize()-D.pixelToScreenSize().x;
  3040. ElmList::AppCheck &app_check=list.app_checks.New();
  3041. app_check.create(Rect_RU(x, 0, list.elmHeight(), list.elmHeight())).func(ActivateApp, app_check).desc("Activate this application"); app_check.app_id=elm.id;
  3042. list.addChild(app_check, elm_list_index);
  3043. }
  3044. }else // if this element is hidden, then don't list it, but proceed to children, regardless if this element is opened or not
  3045. {
  3046. setList(child, depth, vis_parent, removed, filter_is_id ? false : (parent_contains_name || this_contains_name), no_publish); // list children, don't set 'parent_contains_name' when filter is by ID to make sure that desired element is listed last and its children are not listed at all
  3047. }
  3048. }
  3049. }
  3050. if(ee)setList(*ee, depth, parent_removed, parent_contains_name);
  3051. }
  3052. bool ProjectEx::setFilter(ElmNode &node)
  3053. {
  3054. if(node.children.elms())
  3055. {
  3056. byte flag=0;
  3057. int filter_path_length=filter_path.tailSlash(true).length();
  3058. REPA(node.children)
  3059. {
  3060. int child_i=node.children[i];
  3061. ElmNode &child =hierarchy[child_i];
  3062. Elm &elm =elms [child_i];
  3063. if(!elm.removed() || show_removed())
  3064. {
  3065. filter_path.clip(filter_path_length)+=elm.name;
  3066. bool this_contains=(ElmVisible(elm.type) && (filter_is_id ? (elm.id==filter_id) : ContainsAny(elm.name, filter()) && ContainsAll(filter_path, filter()))), // !! check this first because 'setFilter' below will modify the 'filter_path' !!
  3067. child_contains=setFilter(child); // !! check this second !!
  3068. FlagSet(child.flag, ELM_CONTAINS_NAME_CHILD, child_contains);
  3069. FlagSet(child.flag, ELM_CONTAINS_NAME , this_contains);
  3070. flag|=child.flag;
  3071. }
  3072. }
  3073. return FlagTest(flag, ELM_CONTAINS_NAME|ELM_CONTAINS_NAME_CHILD);
  3074. }
  3075. return false;
  3076. }
  3077. bool ProjectEx::setFilter(Memx<EEItem> &items)
  3078. {
  3079. if(items.elms())
  3080. {
  3081. byte flag=0;
  3082. int filter_path_length=filter_path.tailSlash(true).length();
  3083. REPA(items)
  3084. {
  3085. EEItem &item=items[i];
  3086. filter_path.clip(filter_path_length)+=item.base_name;
  3087. bool this_contains=(filter_is_id ? false : ContainsAny(item.base_name, filter()) && ContainsAll(filter_path, filter())), // !! check this first because 'setFilter' below will modify the 'filter_path' !!
  3088. child_contains=setFilter(item.children); // !! check this second !!
  3089. FlagSet(item.flag, ELM_CONTAINS_NAME_CHILD, child_contains);
  3090. FlagSet(item.flag, ELM_CONTAINS_NAME , this_contains);
  3091. flag|=item.flag;
  3092. }
  3093. return FlagTest(flag, ELM_CONTAINS_NAME|ELM_CONTAINS_NAME_CHILD);
  3094. }
  3095. return false;
  3096. }
  3097. ProjectEx& ProjectEx::editing(C UID &elm_id, bool force_open_parents)
  3098. {
  3099. int i=findElmI(elm_id); if(InRange(i, hierarchy))
  3100. {
  3101. hierarchy[i].flag|=ELM_EDITED;
  3102. if(force_open_parents)for(i=hierarchy[i].parent; InRange(i, hierarchy); i=hierarchy[i].parent)hierarchy[i].flag|=ELM_EDITED_CHILD;
  3103. }
  3104. return T;
  3105. }
  3106. bool ProjectEx::editing(Memx<EEItem> &items, C Str &name)
  3107. {
  3108. byte flag=0;
  3109. FREPA(items)
  3110. {
  3111. EEItem &item=items[i];
  3112. FlagSet(item.flag, ELM_EDITED , name.is() && EqualPath(item.full_name, name));
  3113. FlagSet(item.flag, ELM_EDITED_CHILD, editing(item.children, name));
  3114. flag|=item.flag;
  3115. }
  3116. return FlagTest(flag, ELM_EDITED|ELM_EDITED_CHILD);
  3117. }
  3118. ProjectEx& ProjectEx::editing(C Str &name) {editing(CodeEdit.items, name); return T;}
  3119. void ProjectEx::setList(bool set_hierarchy, bool set_existing)
  3120. {
  3121. if(set_hierarchy)setHierarchy();else REPAO(hierarchy).flag=0; // if no need to set whole 'hierarchy' then reset flag manually
  3122. if(set_existing )setExisting(); // after hierarchy
  3123. editing(MtrlEdit.elm_id).editing(WaterMtrlEdit.elm_id).editing(PhysMtrlEdit.elm_id).editing(ObjEdit.obj_id).editing(WorldEdit.elm_id).editing(MiniMapEdit.elm_id).editing(EnvEdit.elm_id).editing(EnumEdit.elm_id).editing(ObjClassEdit.elm_id).editing(FontEdit.elm_id).editing(PanelImageEdit.elm_id).editing(TextStyleEdit.elm_id).editing(PanelEdit.elm_id).editing(GuiSkinEdit.elm_id).editing(GuiEdit.elm_id).editing(ImageEdit.elm_id).editing(ImageAtlasEdit.elm_id).editing(IconSettsEdit.elm_id).editing(IconEdit.elm_id).editing(SoundEdit.elm_id).editing(VideoEdit.elm_id).editing(AnimEdit.elm_id).editing(CodeEdit.sourceCurId(), true).editing(CodeEdit.sourceCurName()).editing(AppPropsEdit.elm_id); // after hierarchy
  3124. if(filter().is())
  3125. {
  3126. filter_is_id=filter_id.fromText(filter());
  3127. filter_path.clear(); setFilter(root);
  3128. filter_path.clear(); setFilter(CodeEdit.items);
  3129. }else filter_is_id=false; // if there's no filter then clear 'filter_is_id' because some codes may check it without checking for "filter().is()"
  3130. list.show_all_elm_types=!(list.show_only_folder || list.show_only_obj || list.show_only_mtrl || list.show_only_anim || list.show_only_sound); REPAO(list.show_elm_type)=list.show_all_elm_types;
  3131. list.flat_is=(list.flat_want && !list.show_all_elm_types);
  3132. list.list_all_children=list.flat_is;
  3133. if(list.show_only_folder)list.show_elm_type[ELM_FOLDER]=true;
  3134. if(list.show_only_obj )list.show_elm_type[ELM_OBJ ]=true;
  3135. if(list.show_only_mtrl )list.show_elm_type[ELM_MTRL ]=true;
  3136. if(list.show_only_anim )list.show_elm_type[ELM_ANIM ]=true;
  3137. if(list.show_only_sound )list.show_elm_type[ELM_SOUND ]=true;
  3138. list.show_all_elms=(list.show_all_elm_types && !filter.is());
  3139. list.warnings .clear();
  3140. list.app_checks.clear();
  3141. list.data .clear();
  3142. setList(root);
  3143. REPAO(list.data).vis_parent=list.data.addr(list.data[i].vis_parent_i); // now that all 'data' have been created, we can convert indexes to pointers
  3144. list.setData(list.data);
  3145. if(list.app_checks.elms()){UID cur_app=curApp(); REPAO(list.app_checks).set(list.app_checks[i].app_id==cur_app, QUIET);} // set checkboxes state
  3146. if(lit_elm_id.valid() || lit_elm_name.is())elmHighlight(lit_elm_id, lit_elm_name, true);
  3147. // set cur/sel
  3148. list.cur=(list_cur.valid() ? list.elmToVis(findElm(list_cur)) : list.itemToVis(list_cur_item)); list_cur.zero(); list_cur_item=null;
  3149. list.sel.clear();
  3150. FREPA(list_sel ){int abs=list. elmToAbs(findElm(list_sel [i])); if(abs>=0)list.sel.add(abs);} list_sel .clear();
  3151. FREPA(list_sel_item){int abs=list.itemToAbs( list_sel_item[i] ); if(abs>=0)list.sel.add(abs);} list_sel_item.clear();
  3152. Theater.refreshData();
  3153. }
  3154. void ProjectEx::clearListSel() // clear list selection
  3155. {
  3156. list_sel .clear();
  3157. list_sel_item.clear();
  3158. }
  3159. void ProjectEx::setListSel(Memc<UID> &list_sel, MemPtr<EEItem*> list_sel_item)
  3160. {
  3161. list_sel .clear();
  3162. list_sel_item.clear();
  3163. FREPA(list.sel)if(ListElm *list_elm=list.absToData(list.sel[i]))
  3164. {
  3165. if(list_elm->elm )list_sel .add(list_elm->elm->id);
  3166. if(list_elm->item && list_sel_item)list_sel_item.add(list_elm->item );
  3167. }
  3168. }
  3169. void ProjectEx::setListCurSel() // needs to be called before adding/removing elements from 'elms'
  3170. {
  3171. list_cur.zero();
  3172. list_cur_item=null;
  3173. if(ListElm *list_elm=list.visToData(list.cur))
  3174. {
  3175. if(list_elm->elm)list_cur =list_elm->elm->id;
  3176. list_cur_item=list_elm->item ;
  3177. }
  3178. setListSel(list_sel, list_sel_item);
  3179. }
  3180. void ProjectEx::setMenuListSel()
  3181. {
  3182. setListSel(menu_list_sel, menu_list_sel_item);
  3183. }
  3184. ProjectEx& ProjectEx::refresh(bool set_hierarchy, bool set_existing)
  3185. {
  3186. setListCurSel(); setList(set_hierarchy, set_existing);
  3187. return T;
  3188. }
  3189. void ProjectEx::elmOpenParents(C UID &id, bool set_list)
  3190. {
  3191. if(Elm *elm=findElm(id))for(; elm=findElm(elm->parent_id); )elm->opened(true);
  3192. if(set_list)setList(false, false);
  3193. }
  3194. void ProjectEx::elmSelect(C MemPtr<UID> &elm_ids)
  3195. {
  3196. clearListSel();
  3197. Memt<UID> added;
  3198. FREPA(elm_ids) // add in order
  3199. if(Elm *elm=firstVisibleParent(findElm(elm_ids[i]))) // add visible (not hidden)
  3200. if(added.binaryInclude(elm->id, Compare)) // don't add the same element multiple times
  3201. {
  3202. elmOpenParents(elm->id, false);
  3203. list_sel.add(elm->id);
  3204. }
  3205. setList(false, false);
  3206. }
  3207. bool ProjectEx::elmReload(C MemPtr<UID> &elm_ids, bool remember_result, bool refresh_elm_list)
  3208. {
  3209. bool ok=true; REPA(elm_ids)if(Elm *elm=findElm(elm_ids[i])){if(elm->type!=ELM_FOLDER && elm->type!=ELM_LIB && elm->type!=ELM_APP)elm->importing(true);}else ok=false;
  3210. Importer.reload(elm_ids, remember_result);
  3211. if(refresh_elm_list)refresh(false, false);
  3212. return ok;
  3213. }
  3214. void ProjectEx::elmLocate(C UID &id, bool set_cur)
  3215. {
  3216. for(Elm *elm=findElm(id); elm; elm=findElm(elm->parent_id))
  3217. {
  3218. int vis=list.elmToVis(elm); if(vis>=0){if(set_cur)list.setCur(vis); list.scrollTo(vis, false, 1); return;}
  3219. }
  3220. }
  3221. void ProjectEx::elmHighlight(C UID &id, C Str &name, bool force)
  3222. {
  3223. if(lit_elm_id!=id || !EqualPath(lit_elm_name, name) || force)
  3224. {
  3225. lit_elm_id =id;
  3226. lit_elm_name=name;
  3227. REPAO(list.data).resetColor();
  3228. for(Elm *elm=findElm(id); elm; elm=findElm(elm->parent_id))
  3229. {
  3230. int vis=list.elmToVis(elm); if(InRange(vis, list.data)){list.data[vis].highlight(); return;}
  3231. }
  3232. if(name.is())if(EEItem **item_ptr=CodeEdit.items_sorted.binaryFind(name, CodeView::CompareItem))for(EEItem *item=*item_ptr; item; item=item->parent)
  3233. {
  3234. int vis=list.itemToVis(item); if(InRange(vis, list.data)){list.data[vis].highlight(); return;}
  3235. }
  3236. }
  3237. }
  3238. void ProjectEx::elmToggle(EEItem *item)
  3239. {
  3240. if(item)switch(item->type)
  3241. {
  3242. case ELM_FOLDER:
  3243. case ELM_LIB :
  3244. {
  3245. item->opened^=1;
  3246. refresh(false, false);
  3247. }break;
  3248. case ELM_CODE: CodeEdit.toggle(item); break;
  3249. }
  3250. }
  3251. void ProjectEx::elmToggle(Elm *elm)
  3252. {
  3253. if(elm)switch(elm->type)
  3254. {
  3255. case ELM_FOLDER:
  3256. case ELM_LIB : elm->opened(!elm->opened()); refresh(false, false); break;
  3257. case ELM_MTRL : MtrlEdit.toggle(elm); break;
  3258. case ELM_WATER_MTRL : WaterMtrlEdit.toggle(elm); break;
  3259. case ELM_PHYS_MTRL : PhysMtrlEdit.toggle(elm); break;
  3260. case ELM_OBJ : ObjEdit.toggle(elm); break;
  3261. case ELM_WORLD : WorldEdit.toggle(elm); break;
  3262. case ELM_MINI_MAP : MiniMapEdit.toggle(elm); break;
  3263. case ELM_ENV : EnvEdit.toggle(elm); break;
  3264. case ELM_ENUM : EnumEdit.toggle(elm); break;
  3265. case ELM_IMAGE : ImageEdit.toggle(elm); break;
  3266. case ELM_IMAGE_ATLAS: ImageAtlasEdit.toggle(elm); break;
  3267. case ELM_ICON : IconEdit.toggle(elm); break;
  3268. case ELM_ICON_SETTS : IconSettsEdit.toggle(elm); break;
  3269. case ELM_FONT : FontEdit.toggle(elm); break;
  3270. case ELM_TEXT_STYLE : TextStyleEdit.toggle(elm); break;
  3271. case ELM_PANEL_IMAGE: PanelImageEdit.toggle(elm); break;
  3272. case ELM_PANEL : PanelEdit.toggle(elm); break;
  3273. case ELM_GUI_SKIN : GuiSkinEdit.toggle(elm); break;
  3274. case ELM_GUI : GuiEdit.toggle(elm); break;
  3275. case ELM_OBJ_CLASS : ObjClassEdit.toggle(elm); break;
  3276. case ELM_SOUND : SoundEdit.toggle(elm); break;
  3277. case ELM_VIDEO : VideoEdit.toggle(elm); break;
  3278. case ELM_ANIM : AnimEdit.toggle(elm); break;
  3279. case ELM_CODE : CodeEdit.toggle(elm); break;
  3280. case ELM_APP : AppPropsEdit.toggle(elm); break;
  3281. case ELM_FILE: elmLocate(elm->id, true); break; // TODO: in the future replace with some Hex Viewer
  3282. }
  3283. }
  3284. void ProjectEx::elmToggle(ListElm *elm)
  3285. {
  3286. if(elm)
  3287. {
  3288. if(elm->elm)elmToggle(elm->elm );
  3289. else elmToggle(elm->item); // use else because after first toggle the pointer may be invalid
  3290. }
  3291. }
  3292. void ProjectEx::elmToggle(C UID &id) {return elmToggle(findElm(id));}
  3293. void ProjectEx::elmActivate(Elm *elm)
  3294. {
  3295. if(elm)switch(elm->type)
  3296. {
  3297. case ELM_MTRL : MtrlEdit.activate(elm); break;
  3298. case ELM_WATER_MTRL : WaterMtrlEdit.activate(elm); break;
  3299. case ELM_PHYS_MTRL : PhysMtrlEdit.activate(elm); break;
  3300. case ELM_OBJ : ObjEdit.activate(elm); break;
  3301. case ELM_WORLD : WorldEdit.activate(elm); break;
  3302. case ELM_MINI_MAP : MiniMapEdit.activate(elm); break;
  3303. case ELM_ENV : EnvEdit.activate(elm); break;
  3304. case ELM_ENUM : EnumEdit.activate(elm); break;
  3305. case ELM_IMAGE : ImageEdit.activate(elm); break;
  3306. case ELM_IMAGE_ATLAS: ImageAtlasEdit.activate(elm); break;
  3307. case ELM_ICON : IconEdit.activate(elm); break;
  3308. case ELM_ICON_SETTS : IconSettsEdit.activate(elm); break;
  3309. case ELM_FONT : FontEdit.activate(elm); break;
  3310. case ELM_TEXT_STYLE : TextStyleEdit.activate(elm); break;
  3311. case ELM_PANEL_IMAGE: PanelImageEdit.activate(elm); break;
  3312. case ELM_PANEL : PanelEdit.activate(elm); break;
  3313. case ELM_GUI_SKIN : GuiSkinEdit.activate(elm); break;
  3314. case ELM_GUI : GuiEdit.activate(elm); break;
  3315. case ELM_OBJ_CLASS : ObjClassEdit.activate(elm); break;
  3316. case ELM_SOUND : SoundEdit.activate(elm); break;
  3317. case ELM_VIDEO : VideoEdit.activate(elm); break;
  3318. case ELM_ANIM : AnimEdit.activate(elm); break;
  3319. case ELM_CODE : CodeEdit.activate(elm); break;
  3320. case ELM_APP : AppPropsEdit.activate(elm); break;
  3321. }
  3322. }
  3323. void ProjectEx::elmNext(C UID &elm_id, int dir)
  3324. {
  3325. if(Elm *elm=findElm(elm_id))
  3326. {
  3327. ELM_TYPE type=elm->type;
  3328. int vis=list.elmToVis(elm); if(vis>=0)REP(list.visibleElms()-1)
  3329. {
  3330. vis=Mod(vis+dir, list.visibleElms());
  3331. if(Elm *elm=list.visToElm(vis))if(elm->type==type)
  3332. {
  3333. list.scrollTo(vis, false, 0.5f); elmActivate(elm); break;
  3334. }
  3335. }
  3336. }
  3337. }
  3338. void ProjectEx::resize()
  3339. {
  3340. flt t=D.h(), b=-D.h(), w=Max(Misc.size().x, size().x); if(Misc.pos.x==0)
  3341. {
  3342. if(Misc.pos.y==0)MAX(b, Misc.rect().max.y);
  3343. else MIN(t, Misc.rect().min.y);
  3344. }
  3345. rect(Rect(-D.w(), b, -D.w()+w, t));
  3346. flt text_size=0.038f, h=text_size*1.5f;
  3347. theater .rect(Rect_RU( rect().w (), 0, h, h));
  3348. show_removed.rect(Rect_RU(theater .rect().lu(), h, h));
  3349. list_options.rect(Rect_RU(show_removed.rect().lu(), 0.85f*h, h));
  3350. filter .rect(Rect_LU(0, 0, list_options.rect().min.x, h));
  3351. region .rect(Rect (0, -rect().h(), rect().w(), -h));
  3352. list. elmHeight(text_size).textSize(text_size);
  3353. setListPadding();
  3354. list.columnWidth (0, text_size*0.8f);
  3355. list.columnWidth (1, text_size*0.8f);
  3356. //list.columnWidth (2, region.rect().w()-region.slidebarSize()-list.columnWidth(1)-list.columnWidth(0)); don't set fixed column width
  3357. list.sound_play.size(list.elmHeight());
  3358. }
  3359. void ProjectEx::elmMenu(C Vec2 &pos, bool touch)
  3360. {
  3361. int items=0, elms=0; Elm *elm=null; bool remove=false, restore=false, expand=false, expand_all=false, collapse=false, image=false, object=false, material=false, animation=false, sound=false, cancel_reload=false;
  3362. REPA(list.sel)if(ListElm *list_elm=list.absToData(list.sel[i]))
  3363. {
  3364. if( list_elm->item)items++;
  3365. if(Elm *e=list_elm->elm )
  3366. {
  3367. elms++; elm=e;
  3368. Elm *parent =findElm(e->parent_id);
  3369. bool parent_removed=(parent && parent->finalRemoved()); // if parent is removed (do extra check for 'findElm' in case 'parent_id' points to element which doesn't exist at all, for example it was copied from a different project)
  3370. if( !parent_removed)if(e->removed())restore=true;else remove=true;
  3371. if(e->importing())cancel_reload=true;
  3372. if(list_elm->hasVisibleChildren())
  3373. {
  3374. if(e->opened())collapse=true;else expand=true;
  3375. expand_all=true;
  3376. }
  3377. switch(e->type)
  3378. {
  3379. case ELM_IMAGE: image =true; break;
  3380. case ELM_OBJ : object =true; break;
  3381. case ELM_MTRL : material =true; break;
  3382. case ELM_ANIM : animation=true; break;
  3383. case ELM_SOUND: sound =true; break;
  3384. }
  3385. }
  3386. }
  3387. if(!elms && items)return; // don't do anything when just Items are selected
  3388. // setup menu
  3389. Node<MenuElm> n;
  3390. if(!elms)
  3391. {
  3392. setNewElm(n, "New ");
  3393. }else
  3394. {
  3395. if(elms==1)
  3396. {
  3397. expand=collapse=false; // hide expand/collapse if just 1 elm is selected
  3398. Node<MenuElm> &New=(n+="New"); setNewElm(New);
  3399. n++;
  3400. }
  3401. if(expand || expand_all || collapse)
  3402. {
  3403. if(expand )n.New().create("Expand" , Expand , T);
  3404. if(expand_all)n.New().create("Expand All", ExpandAll, T).desc("Expand all selected elements including their children");
  3405. if(collapse )n.New().create("Collapse" , Collapse , T);
  3406. n++;
  3407. }
  3408. if(elms==1)
  3409. {
  3410. n.New().create("Rename", ElmRename, T).kbsc(KbSc(KB_F2));
  3411. }else
  3412. {
  3413. n.New().create("Replace Name Part", ReplaceName, T);
  3414. }
  3415. if(remove )n.New().create("Remove" , Remove , T);
  3416. if(restore )n.New().create("Restore" , Restore , T);
  3417. n.New().create("Reload" , Reload , T).kbsc(KbSc(KB_R, KBSC_CTRL_CMD)).desc(reload_desc);
  3418. if(cancel_reload)n.New().create("Cancel Reload", CancelReload, T);
  3419. n++;
  3420. if(elms==1)
  3421. {
  3422. if(elm->type==ELM_ANIM)
  3423. {
  3424. n.New().create("Split Animation", SplitAnim, T);
  3425. n++;
  3426. }
  3427. n.New().create("Explore", ExploreElm, T)/*.kbsc(KbSc(KB_F4))*/.desc(explore_desc);
  3428. switch(elm->type)
  3429. {
  3430. case ELM_ANIM:
  3431. case ELM_IMAGE:
  3432. case ELM_IMAGE_ATLAS:
  3433. case ELM_FONT:
  3434. case ELM_PANEL_IMAGE:
  3435. case ELM_SKEL:
  3436. case ELM_PHYS_MTRL:
  3437. case ELM_ICON:
  3438. case ELM_CODE:
  3439. case ELM_MINI_MAP:
  3440. case ELM_SOUND:
  3441. case ELM_VIDEO:
  3442. case ELM_FILE:
  3443. case ELM_OBJ:
  3444. n.New().create("Export", Export, T);
  3445. break;
  3446. }
  3447. n.New().create("Properties", Properties, T);
  3448. n++;
  3449. }
  3450. if(image || object || material || animation || sound)
  3451. {
  3452. Node<MenuElm> &o=(n+="Operations");
  3453. if(image)
  3454. {
  3455. Node<MenuElm> &m=(o+="Image");
  3456. {
  3457. m.New().create("Enable Mip-Maps" , ImageMipMapOn , T).desc("This option will enable Mip-Maps for all selected images");
  3458. m.New().create("Disable Mip-Maps", ImageMipMapOff, T).desc("This option will disable Mip-Maps for all selected images");
  3459. }
  3460. }
  3461. if(material)
  3462. {
  3463. Node<MenuElm> &m=(o+="Material");
  3464. {
  3465. m.New().create("Set RGB to (1, 1, 1)" , MtrlRGB1 , T).desc("This option will set RGB color to (1, 1, 1) in all selected materials");
  3466. m.New().create("Set RGB to .." , MtrlRGB , T).desc("This option will set RGB color to manually specified value in all selected materials");
  3467. m.New().create("Set RGB to Edited Material", MtrlRGBCur, T).desc("This option will set RGB color to currently edited Material RGB value in all selected materials");
  3468. m.New().create("Set Alpha to 1 or 0.5" , MtrlAlpha , T).desc("This option will set Alpha value to 0.5 for alpha-tested techniques and to 1.0 for other techniques in all selected materials");
  3469. m.New().create("Multiply RGB by .." , MtrlMulRGB, T).desc("This option will multiply RGB color by manually specified value in all selected materials");
  3470. m++;
  3471. m.New().create("Enable Cull" , MtrlCullOn , T).desc("This option will set Cull value to true for all selected materials");
  3472. m.New().create("Disable Cull", MtrlCullOff, T).desc("This option will set Cull value to false for all selected materials");
  3473. m++;
  3474. m.New().create("Enable Flip Normal Y" , MtrlFlipNrmYOn , T).desc("This option will set Flip Normal Y value to true for all selected materials");
  3475. m.New().create("Disable Flip Normal Y", MtrlFlipNrmYOff, T).desc("This option will set Flip Normal Y value to false for all selected materials");
  3476. m++;
  3477. m.New().create("Merge into one" , MtrlMerge , T).desc("This option will merge all selected materials into one\nMaterial textures are not combined in this mode");
  3478. m.New().create("Convert to Atlas", MtrlConvertToAtlas, T).desc("This option will merge all selected materials into one\nMaterial textures will be combined together");
  3479. m++;
  3480. m.New().create("Reload Base Textures", MtrlReloadBaseTex, T);
  3481. m++;
  3482. m.New().create("Multiply Color Texture by Color" , MtrlMulTexCol , T);
  3483. m.New().create("Multiply Normal Texture by Roughness", MtrlMulTexRough, T);
  3484. m++;
  3485. m.New().create("Move to its Object", MtrlMoveToObj, T).desc("This option will move the Material Element to the Object it belongs to");
  3486. m++;
  3487. {
  3488. Node<MenuElm> &mts=(m+="Mobile Texture Size");
  3489. mts.New().create("Full" , MtrlMobileTexSizeFull , T);
  3490. mts.New().create("Half" , MtrlMobileTexSizeHalf , T);
  3491. mts.New().create("Quarter", MtrlMobileTexSizeQuarter, T);
  3492. ASSERT(MaxMaterialDownsize==3);
  3493. }
  3494. }
  3495. }
  3496. if(animation)
  3497. {
  3498. Node<MenuElm> &a=(o+="Animation");
  3499. {
  3500. a.New().create("Clip to first/last keyframe" , AnimClip , T).desc("This option will clip the animation range starting with first keyframe and ending with last keyframe");
  3501. a.New().create("Enable Looping" , AnimLoopOn , T).desc("This option will set loop mode to enabled for selected animations");
  3502. a.New().create("Disable Looping" , AnimLoopOff, T).desc("This option will set loop mode to disabled for selected animations");
  3503. a.New().create("Convert to Linear Interpolation", AnimLinear , T).desc("This option will set linear interpolation mode for selected animations, recommended for animations with high framerate");
  3504. a.New().create("Convert to Cubic Interpolation" , AnimCubic , T).desc("This option will set cubic interpolation mode for selected animations, recommended for animations with low framerate");
  3505. }
  3506. }
  3507. if(object)
  3508. {
  3509. Node<MenuElm> &t=(o+="Transform");
  3510. {
  3511. t.New().create("Set" , TransformSet , T).desc("This will set the transformation of selected objects to the same one as currently opened object");
  3512. t.New().create("Apply", TransformApply, T).desc("This will apply the current transformation from the currently opened object \"Transform\" tab, to selected objects");
  3513. t++;
  3514. t.New().create("Move Bottom to Y=0" , TransformBottom , T).desc("This will transform the selected objects so that their Bottom Y is at 0 position");
  3515. t.New().create("Move Back to Z=0" , TransformBack , T).desc("This will transform the selected objects so that their Back Z is at 0 position");
  3516. t.New().create("Move Center to (0, 0, 0)", TransformCenter , T).desc("This will transform the selected objects so that their Center is at (0, 0, 0) position");
  3517. t.New().create("Move Center XZ to (0, 0)", TransformCenterXZ, T).desc("This will transform the selected objects so that their Center.XZ is at (0, 0) position");
  3518. t++;
  3519. t.New().create("Rotate Y to Minimize Box", TransformRotYMinBox, T).desc("This will rotate selected objects along Y axis to minimize their bounding box");
  3520. }
  3521. Node<MenuElm> &m=(o+="Mesh");
  3522. {
  3523. Node<MenuElm> &r=(m+="Remove");
  3524. r.New().create("Vertex TexCoord1" , MeshRemVtxTex1 , T);
  3525. r.New().create("Vertex TexCoord2" , MeshRemVtxTex2 , T);
  3526. r.New().create("Vertex TexCoord1&2", MeshRemVtxTex12, T);
  3527. r++;
  3528. r.New().create("Vertex Color", MeshRemVtxCol, T);
  3529. r++;
  3530. r.New().create("Vertex Skin", MeshRemVtxSkin, T);
  3531. }
  3532. Node<MenuElm> &obj=(o+="Object");
  3533. {
  3534. obj.New().create("Set Body", SetBody, T).desc("This option will set the \"Body\" of all selected objects to the Object that is currently opened in the Object Editor");
  3535. }
  3536. m.New().create("Extract from Atlas", MtrlConvertToDeAtlas, T).desc("This option will extract the part of Material Texture that is used for selected Objects making the new texture smaller");
  3537. }
  3538. if(sound)
  3539. {
  3540. Node<MenuElm> &s=(o+="Sound");
  3541. {
  3542. FREP(SoundEditor::ImportAsElms)s.New().create(SoundEditor::Import_as[i].name, SoundImportAs, ptr(i));
  3543. s++;
  3544. s.New().create("Multiply Volume by ..", SoundMulVolume, T).desc("This option will multiply volume by manually specified value in all selected sounds");
  3545. }
  3546. }
  3547. }
  3548. {
  3549. Node<MenuElm> &c=(n+="Copy");
  3550. c.New().create("Here" , Duplicate, T).desc("This will duplicate selected elements in this Project");
  3551. c.New().create("To Another Project", CopyTo , T).desc("This will copy selected elements into another Project").kbsc(KbSc(KB_T, KBSC_CTRL_CMD));
  3552. }
  3553. {
  3554. Node<MenuElm> &c=(n+="Publishing");
  3555. c.New().create("Disable", DisablePublish, T).desc(dis_publish_desc);
  3556. c.New().create( "Enable", EnablePublish, T).desc( en_publish_desc);
  3557. }
  3558. }
  3559. if(elm_undos.changes())
  3560. {
  3561. if(n.children.elms())n++;
  3562. if(ElmChange *undo=elm_undos.getNextUndo())n.New().create(S+"Undo "+undo->name, UndoElmChange, T);
  3563. if(ElmChange *redo=elm_undos.getNextRedo())n.New().create(S+"Redo "+redo->name, RedoElmChange, T);
  3564. }
  3565. setMenuListSel();
  3566. Gui+=list.menu.create(n); list.menu.setSize(touch).posRU(pos).moveToBottom().activate(); // move to bottom so it gets keyboard priority
  3567. }
  3568. bool ProjectEx::drop(C Str &name, C UID &parent_id, Memt<UID> &new_folders)
  3569. {
  3570. switch(FileInfoSystem(name).type)
  3571. {
  3572. case FSTD_FILE:
  3573. {
  3574. Str ext=GetExt(name);
  3575. ELM_TYPE type=ELM_NONE;
  3576. switch(ExtType(ext))
  3577. {
  3578. case EXT_MESH : type=ELM_OBJ ; break;
  3579. case EXT_IMAGE: type=ELM_IMAGE; break;
  3580. }
  3581. if(!type)
  3582. if(ext=="wav" || ext=="ogg" || ext=="flac" || ext=="opus" || ext=="mp3" || ext=="m4a" || ext=="weba")type=ELM_SOUND;else
  3583. if(ext=="ogm" || ext=="ogv" || ext=="theora" || ext=="webm" )type=ELM_VIDEO;else
  3584. if(ext=="mtrl" )type=ELM_MTRL ;else
  3585. if(ext=="anim" )type=ELM_ANIM ;else
  3586. if(ext=="c" || ext=="cpp" || ext=="h" || ext=="cc" || ext=="cxx" || ext=="m" || ext=="mm" )type=ELM_CODE ;else
  3587. if(Ends(name, ".mesh.ascii") || Ends(name, ".xps.ascii") )type=ELM_OBJ ;else
  3588. type=ELM_FILE ;
  3589. if(Elm *elm=newElm(type, parent_id, &GetBaseNoExt(name), false))
  3590. {
  3591. elm->setSrcFile(CodeEdit.importPaths(name)); elmReload(elm->id, false, false); list_sel.add(elm->id); // no need to call 'Server.setElmShort' because 'Server.setElmFull' was already requested in 'newElm'
  3592. return true;
  3593. }
  3594. }break;
  3595. case FSTD_DIR:
  3596. {
  3597. bool added_folder; Elm &folder=getFolder(GetBase(name), parent_id, added_folder); folder.opened(true);
  3598. bool added_children=false; for(FileFind ff(name); ff(); )added_children|=drop(ff.pathName(), folder.id, new_folders);
  3599. if( added_folder && !added_children)elms.removeData(&folder, true);else
  3600. {
  3601. list_sel.add(folder.id); if(added_folder)new_folders.add(folder.id);
  3602. return true;
  3603. }
  3604. }break;
  3605. }
  3606. return false;
  3607. }
  3608. void ProjectEx::drop(Memc<Str> &names, C UID &parent_id)
  3609. {
  3610. Memt<UID> new_folders;
  3611. clearListSel();
  3612. FREPA(names)drop(names[i], parent_id, new_folders);
  3613. REPA(new_folders)if(Elm *elm=findElm(new_folders[i]))Server.newElm(*elm); // go from end because first are leafs
  3614. setList();
  3615. }
  3616. void ProjectEx::drop(Memc<Str> &names, GuiObj *focus_obj, C Vec2 &screen_pos)
  3617. {
  3618. if(region.contains(focus_obj) && names.elms())
  3619. {
  3620. names.sort(ComparePathNumberCI).reverseOrder(); // sort by path so they will be imported in order
  3621. Elm *parent=list.visToElm(list.screenToVis(screen_pos));
  3622. UID parent_id=(parent ? parent->id : UIDZero);
  3623. if(parent)parent->opened(true);
  3624. if(parent && parent->type==ELM_OBJ) // if we're dropping onto an object, then ask how we want to import stuff
  3625. {
  3626. Importer.import(*parent, names, screen_pos);
  3627. }else
  3628. {
  3629. drop(names, parent_id);
  3630. }
  3631. names.clear();
  3632. }
  3633. }
  3634. void ProjectEx::meshSetAutoTanBin(Elm &elm, C MaterialPtr &material)
  3635. {
  3636. ::Project::meshSetAutoTanBin(elm, material);
  3637. if(ObjEdit.mesh_elm && ObjEdit.mesh_elm->id==elm.id)ObjEdit.setAutoTanBin(material);
  3638. }
  3639. void ProjectEx::animTransformChanged(Elm &elm_anim)
  3640. {
  3641. ::Project::animTransformChanged(elm_anim);
  3642. if(&elm_anim==AnimEdit.elm)AnimEdit.toGui();
  3643. }
  3644. void ProjectEx::skelTransformChanged(C UID &skel_id)
  3645. {
  3646. ::Project::skelTransformChanged(skel_id);
  3647. if(ObjEdit.skel_elm && ObjEdit.skel_elm->id==skel_id)ObjEdit.skelTransformChanged();
  3648. }
  3649. void ProjectEx::objChanged(Elm &obj)
  3650. {
  3651. ::Project::objChanged(obj);
  3652. ObjEdit.param_edit.p->updateBase(edit_path);
  3653. REPAO(WorldEdit.objs).params.updateBase(edit_path); // updateBase for all objects in case they use this object
  3654. REPAO(WorldEdit.objs).setMeshPhys();
  3655. paramEditObjChanged(&obj.id);
  3656. ObjList.setChanged();
  3657. }
  3658. void ProjectEx::meshChanged(Elm &mesh)
  3659. {
  3660. ::Project::meshChanged(mesh);
  3661. if(ObjEdit.mesh_elm && ObjEdit.mesh_elm->id==mesh.id)ObjEdit.meshChanged();
  3662. if(Selection.elms() && Selection[0].mesh_proper.id()==mesh.id)WorldEdit.param_edit.meshVariationChanged();
  3663. }
  3664. void ProjectEx::elmChanging(Elm &elm)
  3665. {
  3666. switch(elm.type)
  3667. {
  3668. case ELM_ANIM: AnimEdit.elmChanging(elm.id); break;
  3669. }
  3670. }
  3671. void ProjectEx::elmChanged(Elm &elm)
  3672. {
  3673. switch(elm.type)
  3674. {
  3675. case ELM_ANIM : setAnimTransform (elm); setAnimParams(elm); AnimEdit.elmChanged(elm.id); break;
  3676. case ELM_SKEL : setSkelTransform (elm); skelChanged(elm); break;
  3677. case ELM_PHYS : if(!setPhysTransform (elm))physChanged(elm); setPhysParams(elm); break;
  3678. case ELM_MESH : meshTransformChanged(elm); break;
  3679. case ELM_MTRL : mtrlSetAutoTanBin(elm.id); MtrlEdit.elmChanged(elm.id); if(MaterialPtr mtrl=MaterialPtr().find(gamePath(elm.id)))D.setShader(mtrl()); break;
  3680. case ELM_WATER_MTRL : WaterMtrlEdit.elmChanged(elm.id); break;
  3681. case ELM_PHYS_MTRL : PhysMtrlEdit.elmChanged(elm.id); break;
  3682. case ELM_ENUM : enumChanged(elm.id); EnumEdit.elmChanged(elm.id); break;
  3683. case ELM_IMAGE : ImageEdit.elmChanged(elm.id); break;
  3684. case ELM_IMAGE_ATLAS: ImageAtlasEdit.elmChanged(elm.id); break;
  3685. case ELM_ICON_SETTS : IconSettsEdit.elmChanged(elm.id); break;
  3686. case ELM_FONT : fontChanged(); FontEdit.elmChanged(elm.id); break;
  3687. case ELM_TEXT_STYLE : TextStyleEdit.elmChanged(elm.id); break;
  3688. case ELM_PANEL_IMAGE: panelImageChanged(elm.id); PanelImageEdit.elmChanged(elm.id); break;
  3689. case ELM_PANEL : panelChanged(elm.id); PanelEdit.elmChanged(elm.id); break;
  3690. case ELM_GUI_SKIN : GuiSkinEdit.elmChanged(elm.id); break;
  3691. case ELM_GUI : GuiEdit.elmChanged(elm.id); break;
  3692. case ELM_OBJ : objChanged(elm); break;
  3693. case ELM_OBJ_CLASS : enumChanged(elm.id); objChanged(elm); ObjClassEdit.elmChanged(elm.id); break;
  3694. case ELM_SOUND : SoundEdit.elmChanged(elm.id); break;
  3695. case ELM_VIDEO : VideoEdit.elmChanged(elm.id); break;
  3696. case ELM_ENV : EnvEdit.elmChanged(elm.id); break;
  3697. case ELM_WORLD : WorldEdit.elmChanged(elm.id); break;
  3698. case ELM_MINI_MAP : MiniMapEdit.elmChanged(elm.id); break;
  3699. case ELM_APP : AppPropsEdit.elmChanged(elm.id); break;
  3700. }
  3701. IconEdit.elmChanged(elm.id); // process at the end (after all stuff was done)
  3702. ObjEdit.elmChanged(elm.id); // process at the end (after all stuff was done, like mesh transforms)
  3703. Preview.elmChanged(elm.id); // process at the end (after all stuff was done)
  3704. }
  3705. void ProjectEx::receivedData(Elm &elm, File &data, File &extra)
  3706. {
  3707. elmChanging(elm);
  3708. if(ElmHasFile(elm.type))
  3709. {
  3710. Str path;
  3711. if(ElmSendBoth(elm.type))
  3712. {
  3713. path=editPath(elm); if(SafeOverwrite(data , path)) SavedEdit(elm.type, path);
  3714. path=gamePath(elm); if(SafeOverwrite(extra, path)){SavedGame(elm.type, path); savedGame(elm, path);}
  3715. }else
  3716. {
  3717. path=basePath(elm); if(SafeOverwrite(data , path))SavedBase(elm.type, path);
  3718. }
  3719. }
  3720. data.pos(0); makeGameVer(elm, &data);
  3721. elmChanged(elm);
  3722. }
  3723. void ProjectEx::syncElm(Elm &elm, Elm &src, File &src_data, File &src_extra, bool sync_long)
  3724. {
  3725. bool elm_newer_src, src_newer_elm;
  3726. closeElm(elm.id);
  3727. elmChanging(elm);
  3728. UID body_id;
  3729. OBJ_PATH path_mode;
  3730. ObjectPtr obj; TerrainObj2 terrain; PhysPath phys;
  3731. bool world_valid=false; PathSettings path_settings;
  3732. switch(elm.type)
  3733. {
  3734. case ELM_MESH : body_id=elm.meshData()->body_id; break;
  3735. case ELM_OBJ_CLASS: path_mode=elm.objClassData()->pathSelf(); break;
  3736. case ELM_OBJ : obj=gamePath(elm); terrain=*obj; phys=*obj; break;
  3737. case ELM_WORLD : world_valid=elm.worldData()->valid(); elm.worldData()->copyTo(path_settings); break;
  3738. }
  3739. if(::Project::syncElm(elm, src, src_data, src_extra, sync_long, elm_newer_src, src_newer_elm))
  3740. {
  3741. elmChanged(elm);
  3742. switch(elm.type)
  3743. {
  3744. case ELM_MESH:
  3745. {
  3746. if(elm.meshData()->body_id!=body_id)meshTransformChanged(elm, true); // TODO: this is already called in 'elmChanged' above, however without checks for body, the right solution would be to call it only one time with body checks
  3747. }break;
  3748. case ELM_OBJ_CLASS:
  3749. {
  3750. if(elm.objClassData()->pathSelf()!=path_mode)verifyPathsForObjClass(elm.id); // if syncing changed the 'path_mode' then verify world paths
  3751. }break;
  3752. case ELM_OBJ:
  3753. {
  3754. if(TerrainObj2(*obj)!=terrain)rebuildEmbedForObj(elm.id ); // if syncing changed the 'terrainObj' then rebuild world embedded objects
  3755. if(PhysPath (*obj)!=phys )rebuildPathsForObj(elm.id, true); // if syncing changed the 'physPath' then rebuild world paths, 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
  3756. }break;
  3757. case ELM_WORLD:
  3758. {
  3759. PathSettings temp; elm.worldData()->copyTo(temp); if(path_settings!=temp)rebuildWorldAreas(elm); // if syncing changed 'path_settings' then paths need to be rebuilt
  3760. if(!world_valid && elm.worldData()->valid()) // if world settings became valid after syncing, then rebuild some elements
  3761. if(WorldVer *world_ver=worldVerGet(elm.id))
  3762. {
  3763. REPA(world_ver->lakes)
  3764. {
  3765. C UID &water_id =world_ver->lakes.lockedKey (i);
  3766. WaterVer &water_ver=world_ver->lakes.lockedData(i); // can't check for 'water_ver.removed' because that depends on preious building which not yet occured
  3767. Lake lake; if(lake.load(editLakePath(elm.id, water_id)))if(!lake.removed)rebuildWater(&lake, null, water_id, *world_ver);
  3768. }
  3769. REPA(world_ver->rivers)
  3770. {
  3771. C UID &water_id =world_ver->rivers.lockedKey (i);
  3772. WaterVer &water_ver=world_ver->rivers.lockedData(i); // can't check for 'water_ver.removed' because that depends on preious building which not yet occured
  3773. River river; if(river.load(editRiverPath(elm.id, water_id)))if(!river.removed)rebuildWater(null, &river, water_id, *world_ver);
  3774. }
  3775. }
  3776. }break;
  3777. }
  3778. }
  3779. // request from server
  3780. if(!sync_long && (src.newerFile(elm) || src_newer_elm))Server.getElmLong(elm.id); // if server has newer file version than local then request it
  3781. // send to server
  3782. if(elm.newerFile(src) || elm_newer_src)Server.setElmLong (elm.id);else // if local has newer file version than server then send it
  3783. if(elm.newerData(src) )Server.setElmShort(elm.id); // if local has any data newer than server then send it
  3784. }
  3785. bool ProjectEx::syncWaypoint(C UID &world_id, C UID &waypoint_id, Version &src_ver, EditWaypoint &src)
  3786. {
  3787. // if belongs to currently edited world
  3788. if(world_id.valid() && WorldEdit.elm_id==world_id)
  3789. {
  3790. if(Version *waypoint_ver=WorldEdit.ver->waypoints.get(waypoint_id))
  3791. if(src_ver!=*waypoint_ver)
  3792. if(EditWaypoint *waypoint=WorldEdit.waypoints.get(waypoint_id))
  3793. {
  3794. bool changed=waypoint->sync(src);
  3795. if( changed){WorldEdit.setChangedWaypoint(waypoint); WorldEdit.setVisibleWaypoints(); WaypointList.setChanged();}
  3796. if(waypoint->equal(src)){*waypoint_ver=src_ver; WorldEdit.ver->changed=true;}else if(changed){waypoint_ver->randomize(); WorldEdit.ver->changed=true;}
  3797. return changed;
  3798. }
  3799. return false;
  3800. }
  3801. EditWaypoint server =src;
  3802. bool changed=::Project::syncWaypoint(world_id, waypoint_id, src_ver, src); // this modifies 'src_ver' and 'src'
  3803. if(src.newer(server))Server.setWaypoint(world_id, waypoint_id, src_ver, src); // if after syncing local waypoint is newer than server, then send its data to the server
  3804. return changed;
  3805. }
  3806. bool ProjectEx::syncLake(C UID &world_id, C UID &lake_id, Version &src_ver, Lake &src)
  3807. {
  3808. // if belongs to currently edited world
  3809. if(world_id.valid() && WorldEdit.elm_id==world_id)
  3810. {
  3811. if(WaterVer *lake_ver=WorldEdit.ver->lakes.get(lake_id))
  3812. if(src_ver!=lake_ver->ver)
  3813. if(Lake *lake=WorldEdit.lakes.get(lake_id))
  3814. {
  3815. bool changed=lake->sync(src);
  3816. if( changed){WorldEdit.setChangedLake(lake); WorldEdit.setVisibleWaters();}
  3817. if(lake->equal(src)){lake_ver->ver=src_ver; WorldEdit.ver->changed=true;}else if(changed){lake_ver->ver.randomize(); WorldEdit.ver->changed=true;}
  3818. return changed;
  3819. }
  3820. return false;
  3821. }
  3822. Lake server =src;
  3823. bool changed=::Project::syncLake(world_id, lake_id, src_ver, src); // this modifies 'src_ver' and 'src'
  3824. if(src.newer(server))Server.setLake(world_id, lake_id, src_ver, src); // if after syncing local lake is newer than server, then send its data to the server
  3825. return changed;
  3826. }
  3827. bool ProjectEx::syncRiver(C UID &world_id, C UID &river_id, Version &src_ver, River &src)
  3828. {
  3829. // if belongs to currently edited world
  3830. if(world_id.valid() && WorldEdit.elm_id==world_id)
  3831. {
  3832. if(WaterVer *river_ver=WorldEdit.ver->rivers.get(river_id))
  3833. if(src_ver!=river_ver->ver)
  3834. if(River *river=WorldEdit.rivers.get(river_id))
  3835. {
  3836. bool changed=river->sync(src);
  3837. if( changed){WorldEdit.setChangedRiver(river); WorldEdit.setVisibleWaters();}
  3838. if(river->equal(src)){river_ver->ver=src_ver; WorldEdit.ver->changed=true;}else if(changed){river_ver->ver.randomize(); WorldEdit.ver->changed=true;}
  3839. return changed;
  3840. }
  3841. return false;
  3842. }
  3843. River server =src;
  3844. bool changed=::Project::syncRiver(world_id, river_id, src_ver, src); // this modifies 'src_ver' and 'src'
  3845. if(src.newer(server))Server.setRiver(world_id, river_id, src_ver, src); // if after syncing local lake is newer than server, then send its data to the server
  3846. return changed;
  3847. }
  3848. void ProjectEx::hmDel(C UID &world_id, C VecI2 &area_xy, C TimeStamp *time)
  3849. {
  3850. if(WorldEdit.elm_id==world_id)WorldEdit.hmDel( area_xy, time);
  3851. else ::Project::hmDel(world_id, area_xy, time);
  3852. }
  3853. Heightmap* ProjectEx::hmGet(C UID &world_id, C VecI2 &area_xy, Heightmap &temp)
  3854. {
  3855. // check if the world is currently being edited
  3856. if(world_id.valid() && WorldEdit.elm_id==world_id)
  3857. if(Area *area=WorldEdit.findAreaLoaded(area_xy))return area->hm;
  3858. return ::Project::hmGet(world_id, area_xy, temp);
  3859. }
  3860. uint ProjectEx::hmUpdate(C UID &world_id, C VecI2 &area_xy, uint area_sync_flag, C AreaVer &src_area, Heightmap &src_hm)
  3861. {
  3862. if(area_sync_flag)
  3863. {
  3864. if(world_id.valid() && WorldEdit.elm_id==world_id && WorldEdit.ver)
  3865. if(Cell<Area> *cell =WorldEdit. grid.find(area_xy))
  3866. if(AreaVer *area_ver=WorldEdit.ver->areas.get (area_xy))
  3867. {
  3868. Area &area=*cell->data(); if(area.loaded)
  3869. {
  3870. uint synced=0;
  3871. bool loaded=area.loaded;
  3872. area.load();
  3873. if(!area.hm)New(area.hm);
  3874. if(synced=area_ver->sync(src_area, *area.hm, src_hm, area_sync_flag))
  3875. {
  3876. area.setChanged();
  3877. WorldEdit.ver->rebuildArea(area_xy, synced);
  3878. }
  3879. if(!loaded)area.unload();
  3880. return synced;
  3881. }
  3882. }
  3883. return ::Project::hmUpdate(world_id, area_xy, area_sync_flag, src_area, src_hm);
  3884. }
  3885. return 0;
  3886. }
  3887. void ProjectEx::objGet(C UID &world_id, C VecI2 &area_xy, C Memc<UID> &obj_ids, Memc<ObjData> &objs)
  3888. {
  3889. if(world_id.valid() && WorldEdit.elm_id==world_id)
  3890. if(Area *area=WorldEdit.findAreaLoaded(area_xy))
  3891. {
  3892. REPA(area->objs)if(Obj *obj=area->objs[i]) // iterate all objects in the area
  3893. if(obj_ids.binaryHas(obj->id, Compare)) // if this is wanted object
  3894. objs.New()=*obj; // copy to output container
  3895. return;
  3896. }
  3897. ::Project::objGet(world_id, area_xy, obj_ids, objs);
  3898. }
  3899. bool ProjectEx::syncObj(C UID &world_id, C VecI2 &area_xy, Memc<ObjData> &objs, Map<VecI2, Memc<ObjData> > *obj_modified, Memc<UID> *local_newer)
  3900. {
  3901. if(world_id.valid() && WorldEdit.elm_id==world_id && WorldEdit.ver)
  3902. {
  3903. bool loaded_areas_changed=false; // if objects of loaded areas have changed
  3904. Memt<VecI2> unload_areas; // areas to unload after processing
  3905. Area &target_area=*WorldEdit.getArea(area_xy); if(!target_area.loaded)unload_areas.binaryInclude(target_area.xy, Compare);
  3906. REPA(objs)
  3907. {
  3908. ObjData &obj=objs[i];
  3909. ObjVer *obj_ver=WorldEdit.ver->obj.find(obj.id); // use 'find' to get null if not found
  3910. if(!obj_ver) // not present in this world, then add to target area
  3911. {
  3912. bool area_loaded=!unload_areas.binaryHas(target_area.xy, Compare); // if area is loaded (and won't be unloaded)
  3913. Obj &cur_obj=WorldEdit.objs.New(); cur_obj.id=obj.id;
  3914. cur_obj.sync(obj, edit_path);
  3915. cur_obj.attach(WorldEdit, &target_area); // call before 'setChangedEmbed'
  3916. WorldEdit.ver->changedObj(cur_obj, target_area.xy); // call before 'setChangedEmbed'
  3917. cur_obj.setChangedEmbed(); // call after 'attach', 'changedObj' and before 'setChangedPaths'
  3918. if(cur_obj.physPath())cur_obj.setChangedPaths(); // call after 'setChangedEmbed'
  3919. target_area.setChanged(); if(AreaVer *area_ver=target_area.getVer())area_ver->obj_ver.randomize();
  3920. if(area_loaded){loaded_areas_changed=true; target_area.delayedValidateRefs();}
  3921. }else
  3922. {
  3923. Area &cur_area=*WorldEdit.getArea(obj_ver->area_xy); if(!cur_area.loaded)unload_areas.binaryInclude(cur_area.xy, Compare);
  3924. bool area_loaded=(!unload_areas.binaryHas(target_area.xy, Compare) || !unload_areas.binaryHas(cur_area.xy, Compare)); // if area is loaded (and won't be unloaded)
  3925. cur_area.load();
  3926. REPA(cur_area.objs)if(cur_area.objs[i]->id==obj.id) // found that object
  3927. {
  3928. Obj &cur_obj =*cur_area.objs[i];
  3929. uint old_mesh_variation_id= cur_obj.params.mesh_variation_id;
  3930. TerrainObj old_terrain = cur_obj.terrainObj();
  3931. PhysPath old_phys = cur_obj.physPath();
  3932. TimeStamp old_matrix_time = cur_obj.matrix_time; // remember old matrix time before syncing
  3933. if(cur_obj.sync(obj, edit_path))
  3934. {
  3935. bool changed_matrix =(cur_obj.matrix_time>old_matrix_time),
  3936. changed_variation=(cur_obj.params.mesh_variation_id!=old_mesh_variation_id),
  3937. changed_embed =(cur_obj.terrainObj()!=old_terrain || changed_matrix || changed_variation), // if changed terrain, matrix or mesh variation
  3938. 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
  3939. Area &new_area=(changed_matrix ? target_area : cur_area); // check if received newer matrix
  3940. WorldEdit.ver->changedObj(cur_obj, new_area.xy); // call before 'setChangedEmbed'
  3941. if(changed_embed)cur_obj.setChangedEmbed(&new_area.xy); // call after 'changedObj' and before 'setChangedPaths' (use new_area coordinates)
  3942. cur_area.setChanged(); if(changed_phys_path)cur_obj.setChangedPaths(); if(AreaVer *area_ver=cur_area.getVer())area_ver->obj_ver.randomize(); // call 'setChangedPaths' after 'setChangedEmbed' for old area
  3943. cur_obj.attach(WorldEdit, &new_area); // attach to new area
  3944. new_area.setChanged(); if(changed_phys_path)cur_obj.setChangedPaths(); if(AreaVer *area_ver=new_area.getVer())area_ver->obj_ver.randomize(); // call 'setChangedPaths' after 'setChangedEmbed' for new area
  3945. if(area_loaded){loaded_areas_changed=true; cur_area.delayedValidateRefs(); new_area.delayedValidateRefs();}
  3946. }
  3947. break;
  3948. }
  3949. }
  3950. }
  3951. Memc<ObjData> target_objs; REPA(target_area.objs)target_objs.New()=*target_area.objs[i]; // copy before unloading
  3952. if(local_newer)GetNewer(target_objs, objs, *local_newer);
  3953. REPA(unload_areas)if(Area *area=WorldEdit.findArea(unload_areas[i]))if(!area->hasSelectedObj())area->unload(); // unload areas
  3954. if(loaded_areas_changed)ObjList.setChanged();
  3955. return Same(target_objs, objs);
  3956. }
  3957. return ::Project::syncObj(world_id, area_xy, objs, obj_modified, local_newer);
  3958. }
  3959. void ProjectEx::syncCodes()
  3960. {
  3961. CodeEdit.overwriteChanges(); // first overwrite any code changes
  3962. Memc<ElmTypeVer> elm_type_vers; FREPA(elms) // get list of project apps and codes
  3963. {
  3964. Elm &elm=elms[i];
  3965. if(elm.type==ELM_APP || elm.type==ELM_CODE)elm_type_vers.New().set(elm);
  3966. }
  3967. Server.syncCodes(elm_type_vers);
  3968. }
  3969. void ProjectEx::syncCodes(C Memc<ElmCodeData> &elm_code_datas)
  3970. {
  3971. bool new_source=false; // if downloaded a new source
  3972. Memc<ElmCodeData> send;
  3973. FREPA(elm_code_datas)
  3974. {
  3975. C ElmCodeData &ecd=elm_code_datas[i]; if(Elm *elm=findElm(ecd.id, ecd.type))switch(ecd.type)
  3976. {
  3977. case ELM_APP:
  3978. {
  3979. if(ElmApp *app_data=elm->appData())
  3980. {
  3981. if(app_data->sync (ecd.app, true))AppPropsEdit.elmChanged(elm->id);
  3982. if(app_data->newer(ecd.app ))send.New().set(*elm); // if after syncing, local copy has newer data than server, then include it in sending
  3983. }
  3984. }break;
  3985. case ELM_CODE:
  3986. {
  3987. if(ElmCode *code_data=elm->codeData())
  3988. {
  3989. Code code; LoadCode(code.current, codePath(*elm)); LoadCode(code.base, codeBasePath(*elm)); // load code from this computer
  3990. Str latest; if(CodeEdit.sourceDataGet(elm->id, latest))Swap(code.current, latest); // update to latest data taken from Code Editor, in case user made a change between network data exchange
  3991. if(ecd.ver) // if server data is valid then merge
  3992. {
  3993. if(!code_data->ver)new_source=true; // if local version didn't exist then enable 'new_source'
  3994. Str merged=(Server.canWriteCode() ? Merge(code.base, code.current, ecd.code.current) : ecd.code.current); // if can't change code then just grab version from the server
  3995. if(Equal(merged, ecd.code.current, true))code_data->ver=ecd.ver;else if(!Equal(merged, code.current, true))code_data->newVer(); // if same as server then set ver from server, else if changed then set new ver
  3996. code.current=merged;
  3997. code.base =ecd.code.current; // new base is latest server current
  3998. SaveCode(code.current, codePath(*elm)); SaveCode(code.base, codeBasePath(*elm)); // save code to this computer
  3999. CodeEdit.sourceDataSet(elm->id, code.current); // update Code Editor with latest source
  4000. }
  4001. if(Server.canWriteCode()
  4002. && !Equal(code.current, ecd.code.current, true))send.New().set(*elm, &code); // if after merging, local is different than server, then include it in sending
  4003. }
  4004. }break;
  4005. }
  4006. }
  4007. if(send.elms())Server.syncCodes(send); // if we have different elements, then send them
  4008. if(new_source)activateSources(); // if we've got a brand new source then reactivate sources in case it's used by the application
  4009. }
  4010. void ProjectEx::syncCodes(C Memc<ElmCodeBase> &elm_code_bases, bool resync)
  4011. {
  4012. FREPA(elm_code_bases)
  4013. {
  4014. C ElmCodeBase &ecb=elm_code_bases[i];
  4015. SaveCode(ecb.data, codeBasePath(ecb.id)); // here we save to base, as it is on the server
  4016. }
  4017. if(resync)syncCodes();
  4018. }
  4019. bool ProjectEx::codeGet(C UID &elm_id, Str &data)
  4020. {
  4021. if(CodeEdit.sourceDataGet(elm_id, data))return true;
  4022. if(Elm *elm=findElm(elm_id, ELM_CODE))return LoadCode(data, codePath(elm_id))==Edit::EE_ERR_NONE;
  4023. data.clear(); return false;
  4024. }
  4025. bool ProjectEx::codeSet(C UID &elm_id, C Str &data)
  4026. {
  4027. if(Elm *elm=findElm(elm_id, ELM_CODE))
  4028. {
  4029. elmChanging(*elm);
  4030. if(SaveCode(data, codePath(elm_id)))
  4031. {
  4032. elm->codeData()->newVer();
  4033. CodeEdit.sourceDataSet(elm_id, data);
  4034. elmChanged(*elm);
  4035. // don't send to server as codes are synced manually
  4036. return true;
  4037. }
  4038. }
  4039. return false;
  4040. }
  4041. bool ProjectEx::fileRead(C UID &elm_id, File &f)
  4042. {
  4043. if(Elm *elm=findElm(elm_id))switch(elm->type)
  4044. {
  4045. case ELM_FILE :
  4046. case ELM_SOUND:
  4047. case ELM_VIDEO: return f.readTry(gamePath(elm->id));
  4048. }
  4049. return false;
  4050. }
  4051. bool ProjectEx::fileSet(C UID &elm_id, File &f)
  4052. {
  4053. if(Elm *elm=findElm(elm_id))switch(elm->type)
  4054. {
  4055. case ELM_FILE :
  4056. case ELM_SOUND:
  4057. case ELM_VIDEO:
  4058. {
  4059. closeElm(elm_id);
  4060. elmChanging(*elm);
  4061. if(SafeOverwrite(f, gamePath(elm->id)))
  4062. {
  4063. elm->Data()->newVer();
  4064. if(ElmFile *data=elm-> fileData())data->file_time.getUTC();
  4065. if(ElmSound *data=elm->soundData())data->file_time.getUTC();
  4066. if(ElmVideo *data=elm->videoData())data->file_time.getUTC();
  4067. elmChanged(*elm);
  4068. savedGame(*elm);
  4069. Server.setElmLong(elm->id);
  4070. return true;
  4071. }
  4072. }break;
  4073. }
  4074. return false;
  4075. }
  4076. bool ProjectEx::imageGet(C UID &elm_id, Image &image)
  4077. {
  4078. if(Elm *elm=findElm(elm_id, ELM_IMAGE))return image.ImportTry(editPath(elm_id));
  4079. image.del(); return false;
  4080. }
  4081. bool ProjectEx::imageSet(C UID &elm_id, File &image, bool has_color, bool has_alpha)
  4082. {
  4083. if(Elm *elm=findElm(elm_id, ELM_IMAGE))if(ElmImage *image_data=elm->imageData())
  4084. {
  4085. elmChanging(*elm);
  4086. if(SafeOverwrite(image, editPath(elm->id)))
  4087. {
  4088. image_data->newVer();
  4089. image_data->file_time.getUTC();
  4090. image_data->hasColor(has_color);
  4091. image_data->hasAlpha(has_alpha);
  4092. makeGameVer(*elm);
  4093. elmChanged (*elm);
  4094. Server.setElmLong(elm->id);
  4095. return true;
  4096. }
  4097. }
  4098. return false;
  4099. }
  4100. bool ProjectEx::imageSet(C UID &elm_id, C Image &image)
  4101. {
  4102. if(Elm *elm=findElm(elm_id, ELM_IMAGE))
  4103. {
  4104. File f; f.writeMem(); if((image.mode()==IMAGE_3D) ? image.save(f) : image.ExportWEBP(f, 1, 1))
  4105. {
  4106. f.pos(0); return imageSet(elm_id, f, HasColor(image), HasAlpha(image));
  4107. }
  4108. }
  4109. return false;
  4110. }
  4111. bool ProjectEx::meshSet(C UID &elm_id, File &data)
  4112. {
  4113. Elm *elm=findElm(elm_id);
  4114. if(elm)if(ElmObj * obj_data=elm-> objData())elm=getObjMeshElm(elm->id); // if this is an object then get its mesh
  4115. if(elm)if(ElmMesh *mesh_data=elm->meshData()) // mesh
  4116. {
  4117. Mesh mesh; if(mesh.load(data, game_path))
  4118. {
  4119. mesh.setBase().delRender().keepOnly(EditMeshFlagAnd).skeleton(null);
  4120. mesh_data->draw_group_id=findElmID(Enums.id(mesh.drawGroupEnum()), ELM_ENUM);
  4121. mesh.drawGroupEnum(Enums(gamePath(mesh_data->draw_group_id))); // fix draw group enum
  4122. // fix materials
  4123. REP(mesh.lods())
  4124. {
  4125. MeshLod &lod=mesh.lod(i); REPA(lod)
  4126. {
  4127. MeshPart &part=lod.parts[i];
  4128. REP(part.variations())if(i)part.variation(i, gamePath(findElmID(part.variation(i).id(), ELM_MTRL)));
  4129. Str mtrls[4]; REPAO(mtrls)=gamePath(findElmID(part.multiMaterial(i).id(), ELM_MTRL)); part.multiMaterial(mtrls[0], mtrls[1], mtrls[2], mtrls[3]);
  4130. }
  4131. }
  4132. Save(mesh, editPath(elm->id), game_path);
  4133. makeGameVer(*elm);
  4134. mesh_data->file_time .getUTC();
  4135. mesh_data->draw_group_time.getUTC();
  4136. mesh_data->newVer();
  4137. if(ObjEdit.mesh_elm==elm)ObjEdit.reloadMeshSkel();
  4138. meshChanged(*elm);
  4139. Server.setElmLong(elm->id);
  4140. return true;
  4141. }
  4142. }
  4143. return false;
  4144. }
  4145. bool ProjectEx::modifyObj(C UID &obj_id, C MemPtr<Edit::ObjChange> &changes)
  4146. {
  4147. if(Elm *elm=findElm(obj_id))
  4148. if(elm->type==ELM_OBJ || elm->type==ELM_OBJ_CLASS)
  4149. if(EditObjectPtr params=editPath(obj_id))
  4150. {
  4151. elmChanging(*elm);
  4152. if(params->modify(changes, T))
  4153. {
  4154. if(ElmObj *obj_data =elm->objData ()){obj_data ->newVer(); obj_data ->from(*params);}
  4155. if(ElmObjClass *obj_class_data=elm->objClassData()){obj_class_data->newVer(); obj_class_data->from(*params);}
  4156. Save(*params, editPath(obj_id)); // save edit
  4157. makeGameVer(*elm);
  4158. elmChanged (*elm);
  4159. Server.setElmLong(obj_id);
  4160. }
  4161. return true;
  4162. }
  4163. return false;
  4164. }
  4165. bool ProjectEx::codeCheck()
  4166. {
  4167. if(Demo){Gui.msgBox(S, "Demo version doesn't support Code Synchronization"); return false;}
  4168. if(!valid()){Gui.msgBox(S, "Project is empty"); return false;}
  4169. return true;
  4170. }
  4171. Str ProjectEx::codeSyncPath()
  4172. {
  4173. return ProjectsPath+ProjectsCodePath+EncodeFileName(id).tailSlash(true);
  4174. }
  4175. void ProjectEx::codeExplore() {if(codeCheck()){Str path=codeSyncPath(); FCreateDirs(path); Explore(path);}}
  4176. bool ProjectEx::codeExport(bool gui)
  4177. {
  4178. if(codeCheck())
  4179. {
  4180. bool ok=true;
  4181. Str path=codeSyncPath(); FCreateDirs(path);
  4182. REPA(elms)
  4183. {
  4184. Elm &elm=elms[i]; if(elm.type==ELM_CODE && elm.finalExists())
  4185. {
  4186. // access code data
  4187. Str data; codeGet(elm.id, data);
  4188. // save to file
  4189. Str name=path+EncodeFileName(elm.id)+CodeSyncExt;
  4190. FileText f; if(f.write(name, HasUnicode(data) ? UTF_8 : ANSI))
  4191. {
  4192. f.putText(data);
  4193. ok&=f.flushOK();
  4194. }else
  4195. {
  4196. if(gui)Gui.msgBox(S, S+"Can't write to \""+name+"\"");
  4197. ok=false;
  4198. break;
  4199. }
  4200. }
  4201. }
  4202. if(gui)Explore(path);
  4203. return ok;
  4204. }
  4205. return false;
  4206. }
  4207. bool ProjectEx::codeImport(bool gui)
  4208. {
  4209. if(codeCheck())
  4210. {
  4211. bool ok=true;
  4212. if(gui)ImportCode.updates.clear();
  4213. Str path=codeSyncPath();
  4214. REPA(elms)
  4215. {
  4216. Elm &elm=elms[i]; if(elm.type==ELM_CODE && elm.finalExists())
  4217. {
  4218. // load from file
  4219. Str code, name=path+EncodeFileName(elm.id)+CodeSyncExt;
  4220. FileText f; if(f.read(name))
  4221. {
  4222. f.getAll(code);
  4223. // access code data
  4224. Str data; codeGet(elm.id, data);
  4225. // if different
  4226. if(!Equal(data, code, true))
  4227. {
  4228. if(gui)Swap(ImportCode.updates.New().set(elm.id).data, code); // remember for updating with gui
  4229. else ok&=codeSet(elm.id, code); // update instantly
  4230. }
  4231. }
  4232. }
  4233. }
  4234. if(gui)ImportCode.import();
  4235. return ok;
  4236. }
  4237. return false;
  4238. }
  4239. void ProjectEx::flush(SAVE_MODE save_mode)
  4240. {
  4241. ::Project::flush(save_mode);
  4242. ImageEdit .flush();
  4243. ImageAtlasEdit.flush();
  4244. IconSettsEdit .flush();
  4245. IconEdit .flush();
  4246. EnumEdit .flush();
  4247. FontEdit .flush();
  4248. TextStyleEdit .flush();
  4249. PanelImageEdit.flush();
  4250. PanelEdit .flush();
  4251. GuiSkinEdit .flush();
  4252. GuiEdit .flush();
  4253. ObjClassEdit .flush();
  4254. SoundEdit .flush();
  4255. VideoEdit .flush();
  4256. AnimEdit .flush();
  4257. MtrlEdit .flush();
  4258. WaterMtrlEdit .flush();
  4259. PhysMtrlEdit .flush();
  4260. ObjEdit .flush(save_mode);
  4261. WorldEdit .flush();
  4262. MiniMapEdit .flush();
  4263. EnvEdit .flush();
  4264. CodeEdit .flush();
  4265. AppPropsEdit .flush();
  4266. }
  4267. void ProjectEx::flushElm(C UID &elm_id)
  4268. {
  4269. if(ImageEdit .elm_id==elm_id)ImageEdit .flush();
  4270. if(ImageAtlasEdit.elm_id==elm_id)ImageAtlasEdit.flush();
  4271. if(IconSettsEdit .elm_id==elm_id)IconSettsEdit .flush();
  4272. if(IconEdit .elm_id==elm_id)IconEdit .flush();
  4273. if(EnumEdit .elm_id==elm_id)EnumEdit .flush();
  4274. if(FontEdit .elm_id==elm_id)FontEdit .flush();
  4275. if(TextStyleEdit .elm_id==elm_id)TextStyleEdit .flush();
  4276. if(PanelImageEdit.elm_id==elm_id)PanelImageEdit.flush();
  4277. if(PanelEdit .elm_id==elm_id)PanelEdit .flush();
  4278. if(GuiSkinEdit .elm_id==elm_id)GuiSkinEdit .flush();
  4279. if(GuiEdit .elm_id==elm_id)GuiEdit .flush();
  4280. if(ObjClassEdit .elm_id==elm_id)ObjClassEdit .flush();
  4281. if(SoundEdit .elm_id==elm_id)SoundEdit .flush();
  4282. if(VideoEdit .elm_id==elm_id)VideoEdit .flush();
  4283. if(AnimEdit .elm_id==elm_id)AnimEdit .flush();
  4284. MtrlEdit .flush(elm_id);
  4285. if(WaterMtrlEdit .elm_id==elm_id)WaterMtrlEdit .flush();
  4286. if(PhysMtrlEdit .elm_id==elm_id)PhysMtrlEdit .flush();
  4287. if(AppPropsEdit .elm_id==elm_id)AppPropsEdit .flush();
  4288. if(EnvEdit .elm_id==elm_id)EnvEdit .flush();
  4289. if(MiniMapEdit .elm_id==elm_id)MiniMapEdit .flush();
  4290. if(ObjEdit. obj_elm && ObjEdit. obj_elm->id==elm_id)ObjEdit.flushObj ();
  4291. if(ObjEdit.mesh_elm && ObjEdit.mesh_elm->id==elm_id
  4292. || ObjEdit.skel_elm && ObjEdit.skel_elm->id==elm_id)ObjEdit.flushMeshSkel();
  4293. if(ObjEdit.phys_elm && ObjEdit.phys_elm->id==elm_id)ObjEdit.flushPhys();
  4294. CodeEdit.sourceOverwrite(elm_id);
  4295. }
  4296. void ProjectEx::getFileSizes()
  4297. {
  4298. if(list.file_size && !file_size_getter.created())file_size_getter.get(game_path);
  4299. }
  4300. void ProjectEx::savedTex(C UID &tex_id, int size) {TexInfos(tex_id)->file_size=size; TIG.savedTex(tex_id);}
  4301. bool ProjectEx::saveTex(C Image &img, C UID &tex_id)
  4302. {
  4303. File f; img.save(f.writeMem()); f.pos(0);
  4304. Str name=texPath(tex_id); if(SafeOverwrite(f, name)){savedTex(tex_id, f.size()); Saved(img, name); return true;}
  4305. return false;
  4306. }
  4307. void ProjectEx::pauseServer()
  4308. {
  4309. Server.clearProj();
  4310. Server.projectOpen(UIDZero, S);
  4311. }
  4312. void ProjectEx::resumeServer()
  4313. {
  4314. if(!needUpdate() // can't connect to the server when the project needs to be updated first
  4315. && synchronize) // connect only if synchronization is enabled
  4316. {
  4317. Server.projectOpen(id, name);
  4318. Server.projectDataRequest();
  4319. }
  4320. }
  4321. LOAD_RESULT ProjectEx::open(C UID &id, C Str &name, C Str &path, Str &error, bool ignore_lock)
  4322. {
  4323. save_time=Time.appTime()+AutoSaveTime; // when opening new project, make sure that auto save won't be triggered too quickly
  4324. LOAD_RESULT result=::Project::open(id, name, path, error, ignore_lock);
  4325. if(LoadOK(result))
  4326. {
  4327. WorldEdit.setHmMtrl ( hm_mtrl_id );
  4328. WorldEdit.setWaterMtrl( water_mtrl_id );
  4329. FREPA(mtrl_brush_id)MtrlBrush.setMaterial (i, mtrl_brush_id[i]);
  4330. setList(); // this will also set 'Elm.finalRemoved' and 'Elm.finalPublish'
  4331. enumChanged(); // before 'activateSources' because this generates C++ auto header
  4332. fontChanged();
  4333. activateSources(CodeEdit.initialized() ? 1 : -1); // set active sources but without rebuilding so code editor doesn't need to be loaded every time (unless it's already loaded), call after 'enumChanged'
  4334. Importer.opened(T, root); // call after setting list because may rely on hierarchy
  4335. resumeServer();
  4336. // load all world versions so builder knows what to process
  4337. FREPA(elms){Elm &elm=elms[i]; if(elm.type==ELM_WORLD)worldVerGet(elm.id);}
  4338. getFileSizes();
  4339. if(list.tex_sharpness)TIG.getTexSharpnessFromProject();
  4340. }
  4341. ProjSettings.toGui();
  4342. return result;
  4343. }
  4344. bool ProjectEx::save(SAVE_MODE save_mode)
  4345. {
  4346. save_time=Time.appTime()+AutoSaveTime;
  4347. return ::Project::save(save_mode);
  4348. }
  4349. ProjectEx& ProjectEx::del()
  4350. {
  4351. file_size_getter.stop();
  4352. TIG.stop();
  4353. HideProject();
  4354. elm_undos.del();
  4355. mesh_mem.del();
  4356. sound.close();
  4357. lit_elm_id.zero(); lit_elm_name.clear();
  4358. ObjEdit.back_meshes.clear();
  4359. ImportTerrain.clearProj();
  4360. Preview.clearProj();
  4361. ObjPaint.clearProj();
  4362. Importer.clearProj();
  4363. MSM.clearProj();
  4364. DST.clearProj();
  4365. TexDownsize.clearProj();
  4366. CreateMtrls.clearProj();
  4367. ConvertToAtlas.clearProj();
  4368. ConvertToDeAtlas.clearProj();
  4369. Builder.stop();
  4370. pauseServer();
  4371. // 'TIG' can still do some processing because it doesn't operate on this project
  4372. file_size_getter_step=false;
  4373. file_size_getter.del();
  4374. ::ProjectHierarchy::del(); return T;
  4375. }
  4376. void ProjectEx::close()
  4377. {
  4378. // flush and close editors
  4379. ObjEdit.set(null);
  4380. MtrlEdit.set(null);
  4381. WaterMtrlEdit.set(null);
  4382. PhysMtrlEdit.set(null);
  4383. WorldEdit.set(null);
  4384. MiniMapEdit.set(null);
  4385. EnvEdit.set(null);
  4386. GuiEdit.set(null);
  4387. CodeEdit.clear();
  4388. CodeEdit.hideAll();
  4389. ImageEdit.set(null);
  4390. ImageAtlasEdit.set(null);
  4391. IconSettsEdit.set(null);
  4392. IconEdit.set(null);
  4393. EnumEdit.set(null);
  4394. FontEdit.set(null);
  4395. TextStyleEdit.set(null);
  4396. PanelImageEdit.set(null);
  4397. PanelEdit.set(null);
  4398. GuiSkinEdit.set(null);
  4399. ObjClassEdit.set(null);
  4400. SoundEdit.set(null);
  4401. VideoEdit.set(null);
  4402. AnimEdit.set(null);
  4403. AppPropsEdit.set(null);
  4404. ::Project::close();
  4405. setList();
  4406. enumChanged();
  4407. fontChanged();
  4408. }
  4409. void ProjectEx::pause()
  4410. {
  4411. ImageEdit.set(null);
  4412. ImageAtlasEdit.set(null);
  4413. IconSettsEdit.set(null);
  4414. IconEdit.set(null);
  4415. EnumEdit.set(null);
  4416. FontEdit.set(null);
  4417. TextStyleEdit.set(null);
  4418. PanelImageEdit.set(null);
  4419. PanelEdit.set(null);
  4420. GuiSkinEdit.set(null);
  4421. ObjClassEdit.set(null);
  4422. SoundEdit.set(null);
  4423. VideoEdit.set(null);
  4424. AnimEdit.set(null);
  4425. MtrlEdit.set(null);
  4426. WaterMtrlEdit.set(null);
  4427. PhysMtrlEdit.set(null);
  4428. AppPropsEdit.set(null);
  4429. CodeEdit.hideAll();
  4430. EnvEdit.set(null);
  4431. MiniMapEdit.set(null);
  4432. save();
  4433. ImportTerrain.clearProj();
  4434. pauseServer();
  4435. Importer.stop();
  4436. }
  4437. void ProjectEx::resume()
  4438. {
  4439. Importer.investigate(root); // call after setting list because may rely on hierarchy
  4440. resumeServer();
  4441. }
  4442. ProjectEx::ProjectEx() : filter_is_id(false), filter_id(UIDZero), lit_elm_id(UIDZero), list_cur(UIDZero), list_cur_item(null), save_time(0), elm_undos(false, this), file_size_getter_step(false) {}
  4443. ProjectEx::OuterRegion::OuterRegion() : resize_on(false) {}
  4444. ProjectEx::ElmList::ElmList() : ics(ICS_ALWAYS), its(ITS_ELM), show_all_elms(true), show_all_elm_types(true), show_only_folder(false), show_only_obj(false), show_only_mtrl(false), show_only_anim(false), show_only_sound(false), file_size(false), tex_sharpness(false), include_texture_size_in_object(false), include_unpublished_elm_size(false), flat_is(false), flat_want(false), list_all_children(false), tapped_open(false), tapped_vis(-1), icon_col(0), name_col(0), size_col(0), tex_sharp_col(0), tapped_time(0), lit_elm_id(UIDZero) {}
  4445. ProjectEx::ElmList::SoundPlay::SoundPlay() : lit_id(UIDZero), play_id(UIDZero) {}
  4446. ProjectEx::ElmList::AppCheck::AppCheck() : app_id(UIDZero) {}
  4447. ProjectEx::ElmChange::ElmChange() : type(NONE) {}
  4448. /******************************************************************************/