Priemyselná stavba s generovaním tovaru - prekladište aj s tovarom


V tomto tútoriale sa vám zjednodušene pokúsim ukázať ako si postaviť nakladaciu rampu s generovaním tovaru.  Takto by mal vyzerať náš cieľ.


Použijeme foto-textúru pre samotnú nakladaciu rampu ktorú rozdelíme do viac menších súborov pre lepšie mapovanie. Samozrejme je možné pracovať aj s jednou spoločnou bitmapou, ale textúrovanie je zložitejšie. Vo všeobecnosti však platí, že pokiaľ je to možné je lepšie používať menšie súbory a viac súborov. Plochy sú lepšie zobrazované, kontrastnejšie a ostrejšie pretože môžete použiť väčšie rozlíšenie. Naviac práca je prehľadnejšia. V niektorých prípadoch hlavne u animovaných objektov je lepšie ale použiť spoločné bitmapy. V našom prípade použijem samostatnú bitmapu pre prednú stenu, pre vrch a pre žlto čierny pruh.  Tento žlto čierny pruh môže byť samozrejme súčasťou inej textúry ako pri čelnej stene, alebo aj samostatnou bitmapou.




Čo do textúr pri mnohej stavbe, ktorá nieje postavená presne podľa nejakého originálu ste v istej výhode, keďže internet je plný volne šíriteľných textúr hlavne rôznych betónov, stien, vegetácie, dlažieb, kovov, dreva, asfaltu a podobne, Využíva to viac či menej skoro každý tvorca objektov vrátane mňa. Aj dve z týchto tu použitých textúr majú svoj pôvod na internete. Tretia je z reklamného média k jednému 3D grafickému programu. Je to v mnohom jednoduchšie keďže nieje nutné hľadanie vhodného objektu predlohy,  čakania na vhodnú dobu pre fotenie a následných digitálnych úprav. Prácu sa vždy treba snažiť pokiaľ možno si čo najviac zjednodušiť.  Nedoporučujem však používať textúry z iných objektov a aj u voľne šíriteľných textúr si treba vždy pozrieť prípadné licenčné podmienky uvedené niekde na stránke odkiaľ ich sťahujete. Niektoré textúry totiž majú  obmedzenia (obvykle komerčné použitie, alebo uvedenie autora), ktoré treba rozhodne dodržať.  Ak si v niektorom prehliadači zadáte vyhľadať napríklad výraz "texture" a "download"  budete prekvapený koľko stránok venujúcich sa grafike sa vám  objaví. A okrem textúr na mnohej nájdete aj mnoho cenných informácii na tému úprav, fotenia a  vôbec grafiky.
Ak už fotíte tak vedzte, že sa fotí vždy kolmo na objekt a pri vysokom rozlíšení. A hlavne pokiaľ možno nie počas slnka, ale  keď je pod  mrakom, keď je denné svetlo rovnomerne rozptýlené. Inak je textúra v podstate nepoužiteľná v pôvodnom tvare a chce to mnohé úpravy, ktoré často viac uškodia než pomôžu..
Keď máte pripravené textúry, vytvorte si adresár povedzme NAKLADACIA_RAMPA a do nej ich prekopírujte. Teraz sa môžete pustiť do modelovania.
Začneme so samotnou nakladacou rampou a ako obvykle pripomínam, že popísaný spôsob je samozrejme iba jednou z mnoho možností ako sa dopracovať k cieľu. Každopádne sa snažte si zbytočne nekomplikovať život a modelujte čo najjednoduchšie, najefektnejšie a najrýchlejšie. Vezmite na vedomie, že aj mnou popísané kroky je možné spraviť inak a mnohé cielene popisujem tak aby som ukázal inú možnosť, alebo v iných tútorialoch mnou ešte nepopísaný a možný postup.
Vytvorme si teda najskôr jednoduchý pôdorys buď zo štvoruholníka ktorému po konverzii na EDITABLE SPLYNE pridáme príslušný počet bodov, ktoré presunieme do finálnych polôh, alebo spájaním liniek do jedného výsledného celku.



Označíme si vertexy, klikneme na tlačítko REFINE a pridáme do spodnej časti ešte jeden bod, ktorý bude pre neskôr s extrude vytiahnutú rampu.


Teraz si vyberieme objekt a vytvoríme z neho klon. Ten zmenšíme tak aby vznikla medzera o šírke 0.1m ktorá neskôr bude špeciálnou plochou pre žlto čierny pás okraja nakladacej rampy. Zároveň z tohoto klonu odstránime v predlohe pridaný posledný vertex.


Ako poznámku k množstvu možností ako prísť k rovnakému výsledku tu chcem spomenúť, že napríklad k tomuto výsledku je možné prísť aj z jednej čiary bez klonovania, keď jej jednoducho dáme šírku 0.1m - hodnote vedľa tlačítka OUTLINE. Tak alebo tak z vnútorného okraja vytvoríme ešte jeden klon ktorý bude tvoriť vrch nakladacej rampy.
Znovu si označíme vonkajší obvod a pripojíme s ATACH k nemu vnútorný obvod. Označíme ho ako povedzme "obvod". Vyberieme čiaru a pravou myšou SUB-OBJECT S - TOP LEVEL.


Následne v menu MODFIERS -  MESH EDITING - EXTRUDE pôdorys vytiahneme do výšky 1.4m.  


Tak dostaneme obvod z ktorého po prevode na EDITABLE MESH a výbere polygónov odstránime všetky vnútorné a spodné plochy.


Teraz si vyberieme vyklonovaný vrch rampy a rovnakým postupom ho s EXTRUDE vytiahneme do výšky 0.0m Tak dostaneme v podstate plane plochu s nulovou výškou, ktorú v ose Z zdvihneme ako celok do výšky 1.4m, pripojíme k obvodu a vo vertexoch spojíme do jedného celku.


Ako ďalšie si z druhej strany označíme samostatný polygón na zvislej stene a pomocou EXTRUDE do vzdialeností 10m vytiahneme túto plochu smerom von.


Odstránime spodnú plochu a čelnú plochu vytiahnutého nábehu, ktorá je úž teraz nepotrebná. Následne vyberieme polygóny a odstránime z bočných strán nábehu vždy po jednom trojuholníku. Nakoniec vyberieme vertexy, označíme krajné vonkajšie horné vertexy nábehu a os Z im nastavíme na hodnotu 0.0m. Zároveň ich zvaríme so spodnými. Tým máme nábehovú rampu hotovú.


Ostáva nám spraviť schody na opačnej strane buď ako množinu, alebo jednoducho opäť z kriviek bočného pohľadu a ich vytiahnutím.


Odstránime spodnú a vnútornú plochu a schody pripojíme k nakladacej rampe. Samotná stavba je hotová a je čas ju otextúrovať. Načítame teda všetky bitmapy. Teraz si zvolíme polygóny a označíme bočné vonkajšie plochy, ktorým pomocou APPLY v materiál editore pridelíme  textúru pre bok so žlto čiernym pásom. Tú istú aj schodom. Potom vyberte vrch nakladacej rampy a prideľte jej textúru  horného asfaltu . A nakoniec vyberte tenký pás a prideľte mu textúru žlto čierneho pruhu, ak je v samostatnej bitmape, alebo tiež čelnú stenu s pasom z ktorej pri mapovaní vyberieme iba tento pruh.. Nakoniec pomocou MODIFIERS - UV COORDINATES - UVW MAP ako BOX namapujete spoločne textúru. Pomocou  MODIFIERS - UV COORDINATES - UNVRAP UVW postupne dolaďte všetky textúry a plochy.




Hlavne pri žltočiernom páse dbajte na presné sedenie vzorov. Dôležité je aby ste krajné body mali rovnako posunuté ako vertexy plôch kde sa v rohoch plochy spájajú. Ak použijete UVW MAP spraví to za vás 3dmax (gmax) a vy to musíte iba doladiť ak naťahujete oblasť mapovania. Zachovaný musí byť hlavne uhol vo vzťahu k dĺžke. Používajte na úpravu preto nie ručné presúvanie bodov, ale hľavne funciu SCALE  (tretia ikonka v okne edit UVWs) v príslušnej oreintácii a vo vzťahu k celu.


Takto máme samotnú nakladaciu rampu hotovú a pre kontrolu ju vyexportujeme do TRS pričom definovaná bude zatiaľ ako jednoduchý scenery objekt len za použitia základných tagov v config.txt.




Ako ďalšie musíme definovať pomocné body pre koľaj. Vytvoríme si pomocné plane na dĺžku rozdelené na dve plochy a o šírke 2m a dĺžke budúcej koľaje. Pridelíme mu pracovnú textúru pražcov s koľajnicami pre lepšie znázornenie. Stredová čiara rozdeľujúca plane na dve časti nám bude dobre slúžiť ako vodiaca čiara pre umiestnenie pomocných bodov koľajnice. Zvolíme si teda v editačnej oblasti HELPERS - POINT




a položíme na os päť bodov ktoré označíme ako a.track0a, a.track0b, a.track0c,  a.track0d a a.track0e pričom bod b bude zarovno so začiatkom rampy, bod c v strede a bod d na konci rampy. Body "a" a "e" budú na vonkajších okrajoch.



Body "a" a "e" sú k pripojeniu externej koľaje a tri stredné na definovanie triggra nakladania a vykladania. Dôležitá je orientácia osí. Na krajných bodov musí os Y smerovať vždy smerom von z koľaje.
To isté spravíme aj na opačnej strane kde body pomenujeme ako a.track1a, a.track1b, a.track1c, a.track1d a a.track1e. Body sú vo výške (os Z) na hodneote 0.0
Objekt môžeme opäť vyexportovať pre kontrolu správneho umiestnenia koľají a prípadne body upraviť. Ak sú koľaje korektné, zmažeme pomocné koľaje a vytvoríme si nové pomocné plane o rozmeroch hornej plochy a rozdelené podľa počtu krabíc tovaru ktoré sa nám budú generovať na rampe. Ich rozmer pôdorysu záleží od typu tovaru. V našom prípade počítajme 1.2 x 1.2m. Do krížov potom umiestnime pomocné  body ktoré označíme ako a.goods_in01 až a.goods_in25 pre nakládku a goods_out01 až a.goods_out25 pre vykládku (podľa počtu bodov). To sú miesta kde sa v hre bude generovať tovar.




Samozrejme že body môžu byť aj mimo samotnej nakladacej rampy, napríklad na tráve. Ich výška (os Z) musí byť adekvátna ich položenia. Na rampe teda na hodnote 1.4m a na trave 0.0. Je možné body umiestňovať aj nad seba a tak dosiahnúť zaujimavý efekt, kde sa vám tovar bude kopiť na seba ako v reále. Tu treba ale presne vziať do úvahy výšku tovaru + výšku umiestnenia spodného. A hlavne poradové číslo musí byť vyššie ako má spodný tovar. Inak by sa nám zobrazila krabica s tovarom vo vzduchu.  A samozrejme aj tu je dôležitá orientácia bodov, ktorá určuje ako sa bude tovar zobrazovať. Natočením bodov po ose Z dosiahnete rôzne orientovanie v priestore čo pôsobí často realistickejšie ako neprirodzene podľa pravítka pravidelne vyrovnaný tovar. Je to častá chyba mnohého tvorcu, že sa nesnaží napodobiť zdanlivý chaos realu.  Záleží však aj od tovaru.




Vyexportujeme našu rampu do TRS a vygenerujeme comfig.txt ktorý by mal obsahovať nasledovné tagy.

kuid                                         <kuid:XXXXXX:XXXXXX>
region                                       TUTORIAL
type                                          "Priemysel"
kind                                          "industry"
category-class                          "BIN"
kuid-table {
    gen_goods                             <kuid:-3:10013>
}
light                                          1
rotstep                                      0.3
script                                        "goodsshed_gengoods"
class                                         "goodsshed_gengoods"
icon-texture                               "ico_rampa.bmp"
mesh-table {
    default {
        mesh                                 "rampa.im"
        auto-create                        1
        effects {
            arrow0 {
                kind                    "attachment"
                att                       "a.track0a"
                default-mesh        <kuid:-3:10092>
                surveyor-only        1
            }
            arrow1 {
                kind                  "attachment"
                att                     "a.track0e"
                default-mesh        <kuid:-3:10092>
                surveyor-only        1
            }
            arrow2 {
                kind                   "attachment"
                att                     "a.track1a"
                default-mesh        <kuid:-3:10092>
                surveyor-only        1
            }
            arrow3 {
                kind            "attachment"
                att               "a.track1e"
                default-mesh        <kuid:-3:10092>
                surveyor-only        1
            }
        }
    }
}
attached-track {
    out_track {
        track                    <kuid:-1:15>
        vertices {
            0                "a.track0a"
            1                "a.track0b"
            2                "a.track0c"
            3                "a.track0d"
            4                "a.track0e"
        }
    }
    in_track {
        track                    <kuid:-1:15>
        vertices {
            0                "a.track1a"
            1                "a.track1b"
            2                "a.track1c"
            3                "a.track1d"
            4                "a.track1e"
        }
    }
}
queues {
    goods_in_q {
        size                           25
        initial-count                8
        product-kuid                <kuid:-3:10013>
        attachment-points {
            0                "a.goods_in01"
            1                "a.goods_in02"
            2                "a.goods_in03"
            3                "a.goods_in04"
            4                "a.goods_in05"
            5                "a.goods_in06"
            6                "a.goods_in07"
            7                "a.goods_in08"
            8                "a.goods_in09"
            9                "a.goods_in10"
            10                "a.goods_in11"
            11                "a.goods_in12"
            12                "a.goods_in13"
            13                "a.goods_in14"
            14                "a.goods_in15"
            15                "a.goods_in16"
            16                "a.goods_in17"
            17                "a.goods_in18"
            18                "a.goods_in19"
            19                "a.goods_in20"
            20                "a.goods_in21"
            21                "a.goods_in22"
            22                "a.goods_in23"
            23                "a.goods_in24"
            24                "a.goods_in25"
        }
    }
    goods_out_q {
        size                             25
        initial-count                12
        product-kuid                <kuid:-3:10013>
        attachment-points {
            0                "a.goods_out01"
            1                "a.goods_out02"
            2                "a.goods_out03"
            3                "a.goods_out04"
            4                "a.goods_out05"
            5                "a.goods_out06"
            6                "a.goods_out07"
            7                "a.goods_out08"
            8                "a.goods_out09"
            9                "a.goods_out10"
            10                "a.goods_out11"
            11                "a.goods_out12"
            12                "a.goods_out13"
            13                "a.goods_out14"
            14                "a.goods_out15"
            15                "a.goods_out16"
            16                "a.goods_out17"
            17                "a.goods_out18"
            18                "a.goods_out19"
            19                "a.goods_out20"
            20                "a.goods_out21"
            21                "a.goods_out22"
            22                "a.goods_out23"
            23                "a.goods_out24"
            24                "a.goods_out25"
        }
    }
}
processes {
    goodsshed_consumer {
        start-enabled                1
        duration                       30
        inputs {
            0 {
                amount            3
                queue             "goods_in_q"
            }
        }
        outputs {
            0 {
                amount            3
                queue             "goods_out_q"
            }
        }
    }
}
attached-trigger {
    goods_out_t0 {
        att                    "a.track0b"
        radius                    5
    }
    goods_out_t1 {
        att                    "a.track0c"
        radius                    5
    }
    goods_out_t2 {
        att                    "a.track0d"
        radius                    5
    }
    goods_in_t0 {
        att                    "a.track1b"
        radius                    5
    }
    goods_in_t1 {
        att                    "a.track1c"
        radius                    5
    }
    goods_in_t2 {
        att                    "a.track1d"
        radius                    5
    }
}
icon0                                     <kuid:-3:10164>
asset-filename                        "rampa"


Keď si to lepšie pozriete vidíte, že je to vlastne to isté ako už popísaný config.txt v mojom  tutorialy o stavbe nástupišťa s produkciou pasažierov. Preto to nebudem opakovať. Rozdiel je akurát v časti  processes kde sa definuje časť pre nakládku - inputs - a časť pre vykládku - outputs. Dôležite je aj tu aby sa identifikátory zhodovali s identifikátormi v scripte, ktorý je ďalšou dôležitou časťou pre samotnú správnu činnosť nakladacej rampy a ktorého názov sme definovali v config.txt ako goodsshed_gengoods.gs.

include "GenericIndustry.gs"


class goodsshed_gengoods isclass GenericIndustry
{
  ProductQueue goodsOutQueue, goodsInQueue;
  bool scriptletEnabled = true;

  int lumberWBRemain = 0;

  thread void ViewDetails(void)
  {
    if (!info)
    {
      info = Constructors.NewBrowser();
     
      info.LoadHTMLString(HTMLWindow.GetCompleteIndustryViewDetailsHTMLCode(me, scriptletEnabled));
    info.SetWindowRect(100, 80, 500, 545);
    }
  }


  bool TriggerSupportsStoppedLoad(Vehicle vehicle, string triggerName)
  {
    if (itc.IsTrainCommand(vehicle.GetMyTrain(), Industry.LOAD_COMMAND))
    {
      bool vehicleToTrain = vehicle.GetFacingRelativeToTrain();
      int direction = vehicle.GetRelationToTrack(me, "out_track");
      if (!vehicleToTrain)
        direction = -direction;

      if (direction == Vehicle.DIRECTION_BACKWARD and triggerName == "goods_out_t0")
        return true;
      if (direction == Vehicle.DIRECTION_FORWARD and triggerName == "goods_out_t2")
        return true;

      if (triggerName == "goods_out_t0" or triggerName == "goods_out_t1" or triggerName == "goods_out_t2")
        if (vehicle.GetMyTrain().IsStopped())
          return true;
     }
   
    if (itc.IsTrainCommand(vehicle.GetMyTrain(), Industry.UNLOAD_COMMAND))
    {
      bool vehicleToTrain = vehicle.GetFacingRelativeToTrain();
      int direction = vehicle.GetRelationToTrack(me, "in_track");
      if (!vehicleToTrain)
        direction = -direction;

      if (direction == Vehicle.DIRECTION_BACKWARD and triggerName == "goods_in_t2")
        return true;
      if (direction == Vehicle.DIRECTION_FORWARD and triggerName == "goods_in_t0")
        return true;

      if (triggerName == "goods_in_t0" or triggerName == "goods_in_t1" or triggerName == "goods_in_t2")
        if (vehicle.GetMyTrain().IsStopped())
          return true;
    }

    return false;
  }


  void PerformStoppedLoad(Vehicle vehicle, string triggerName)
  {
    if (triggerName == "goods_out_t0" or triggerName == "goods_out_t1" or triggerName == "goods_out_t2")
    {
      int available = goodsOutQueue.GetQueueCount();
      LoadingReport report = CreateLoadingReport(goodsOutQueue, available);
      vehicle.LoadProduct(report);
    }
    if (triggerName == "goods_in_t0" or triggerName == "goods_in_t1" or triggerName == "goods_in_t2")
    {
      int available = goodsInQueue.GetQueueSpace();
      LoadingReport report = CreateUnloadingReport(goodsInQueue, available);
      vehicle.UnloadProduct(report);
    }
  }

  thread void GoodsShedMain(void)
  {
    Message msg;

    wait()
    {
      Vehicle vehicle;
      string triggerName;

      on "Scriptlet-Enabled", "1":
      {
        if (!scriptletEnabled)
        {
          scriptletEnabled = true;
          SetProcessEnabled("goodsshed_consumer", true);
        }
        continue;
      }

      on "Scriptlet-Enabled", "0":
      {
        if (scriptletEnabled)
        {
          scriptletEnabled = false;
          SetProcessEnabled("goodsshed_consumer", false);
        }
        continue;
      }
    }
  }


  public void Init(void)
  {
    inherited();

    goodsOutQueue = GetQueue("goods_out_q");
    goodsInQueue = GetQueue("goods_in_q");

    AddAssetToIndustryProductInfo("gen_goods", "goods_in_q", "goodsshed_consumer", true);
    AddAssetToIndustryProductInfo("gen_goods", "goods_out_q", "goodsshed_consumer", false);
    GoodsShedMain();
  }

  public Requirement[] GetRequirements(void)
  {
    Requirement[] ret = new Requirement[0];

    if (goodsInQueue.GetQueueCount() < 4 or lumberWBRemain > 0) 
    {
      ResourceRequirement req = new ResourceRequirement();

      req.resource = goodsInQueue.GetProductFilter().GetProducts()[0];
     
      req.amount = 10;       
      if (goodsInQueue.GetQueueCount() < 2 and lumberWBRemain == 0)
        lumberWBRemain = 10;

      req.dst = me;
      req.dstQueue = goodsInQueue;

      ret[ret.size()] = req;
    }

    return ret;
  }

  public void AppendDriverDestinations(string[] destNames, string[] destTracks)
  {
    destNames[destNames.size()] = "General Goods Pickup";
    destTracks[destTracks.size()] = "out_track";

    destNames[destNames.size()] = "General Goods Drop off";
    destTracks[destTracks.size()] = "in_track";

  }
};

Ako vidíte aj ten je v podstate zhodný so scriptom pre nástupište  

A preto aj tu sa nebudem opakovať. Snáď iba znova pripomeniem, že sa jedná o programovací jazyk a preto je dobré si o ňom naštudovať čo najviac.

Ako ďalšie aby naša prekladacia rampa bola kompletná si ešte vytvorme vlastný tovar, ktorým bude jednoduchá paleta reziva.
Ako textúru som opäť použil volne dostupnú textúru z jednej internetovej stránky, pričom čelnú stranu som ručne vytvoril z časti bočnej ako novú textúru.


bočno-vrchnú a čelnú

Vytvoríme si teda v 3dmax, alebo gmax jednoduchý box o rozmeroch 1.2 x 1.2 x 1m a umiestnime ho do stredu na koordináty 0,0,0. Odstránime spodnú stranu a namapujeme obidve textúry, ktoré pridelíme kvádru tak, že dve steny budú namapované čelnou a zvyšné bočno-vrchnou.

d

Objekt vyexportujeme ako product objekt, pričom config.txt bude obsahovať nasledovné tagy.

kuid                                        <kuid:XXXXXX:XXXXXX>
instance-type                          "resource"
kind                                        "product"
product-category                    <kuid:-3:10042>
category-class                        "IC"
icon-texture                            "ico_tovar.bmp"
mass                                       500
trainz-build                              2
mesh-table {
    default {
        mesh                              "tovar.im"
    }
}
allows-mixing                           0
asset-filename                          "tovar"
username                                "Paleta s drevom"

Dôležité je hlavne product-category pre identifikáciu typu tovaru a mass ktorý udáva váhu jednoty. Viac o tom a aj o ostatných tagoch ktoré vám sú prípadne ešte neznáme nájdete v CCG dokumentácii v časti o produktoch.
Nakoniec ešte vygenerujeme ikonu 64x64 pixelov ico_tovar.bmp pre TRS  a náš tovar zo stiahnutej obrazovky, ktorú nahráme do adresáru kde je tovar pod názvom definovaným v config.txt ako "ico_tovar.bmp". Budeťe ju potrebovať neskôr v TRS keď budete nový tovar pridelovať vagónom ktoré ho majú prepravovať.




A rovnako ikonu 64x64 pixelov ico_rampa.bmp tiež definovanú v config.txt pre našu rampu pre TRS a zobrazovanú v cestovnom poriadku, ktorú nahráme do adresáru rampy.




Teraz vymeníme kuid tovaru v configu prekladacej rampy (pôvodne kuid:-3:10013) za kuid nášho tovaru a výsledok je vidieť na nasledovných obrázkoch. Samozrejme je možné našu rampu ešte rozšíriť o nočný mód, animovaný žeriav a ďalšie detaily. Ale to už je vecou vašej fantázie.






Rampa má 91 polygónov a v sekcii download je možné si ju stiahnuť. Tovar má 10 polygónov a je stiahnuteľný ako súčasť rampy. Prípadné otázky posielajte emailom.