Tein ohjelman ( C++ / OpenGL ), joka lataa 3D mallin. Kun muutin ohjelman varaamaan new operaattorilla muistia, ohjelma kaatui heti. Kaatuuko ohjelma siksi, kun pointterit joita käytetään muistin varaamiseen ja sitten taulukkona ovat structin sisällä? Kun structin sisään laittaa suoraan taulukon kooksi vaikkapa 1000, niin ohjelma kyllä toimii.
Vai onkohan tässä taas kyseessä jokin muu virhe?
Ohjelma on tyyliin:
struct TVerteksi { float x,y,z; }; struct TObjekti { TVerteksi *verteksi; }; void Lataa3DMalli( TObjekti *pobjekti, char* filename ) { // Luetaan montako verteksiä malli sisältää. p_objekti->verteksi = new TVerteksi [VerteksienMaara]; }
Tapahtuuko virhe mallin lataamisessa, vai jossain muussa ohjelman osassa? Antamasi koodinpätkän perusteella tekemäni testi toimi kyllä ihan hyvin, oli verteksien määrä mikä tahansa.
Arvaan jo, vaikken koko koodia näekkään... jossain vaiheessa ohjelmaa yritetään käyttää osoitinta jota ei ole alustettu. Monimutkaisemmissa koodeissa suurimpia bugien aiheuttajia, ehdottomasti;)
Joo kyllä nimenomaan mallin latauksessa. Käännettäessä ja linkittäessä ei tule minkäänlaisia virheitä tai varoituksia.
No itse asiassa tuo TObjekti structi sisältää vielä polygonien indeksit (structi) ja tekstuurimappaukseen tarvittavat koordinaatit. Ne ovat structissa järjestyksessä:
struct TObjekti { TVerteksi *verteksi; // Verteksien taulukko (x,y,z) TPolygoni *polygoni; // Polygonien taulukko (a,b,c) TMapCoord *mapcoord; // Tekstuurin koordinaatit (u,v) };
Ensimmäisen taulukon eli verteksien varaaminen kyllä onnistuu, mutta toisessa ja kolmannessa alkaa tulla kaatumisia. Tai no ohjelma suorittaa laittoman toiminnon tms. heti.
Luin jostakin, että luokkien (olio-ohjemointia) konstruktorit ja destruktorit ovat hyödyllisiä dynaamisen muistin varaamisessa. Pitäisiköhän siirtyä näihin? Mallin datan ja funktioiden (piirto?) yhteenliittäminen saattaisi olla kätevää.
No pitänee vielä alkaa tarkistamaan koodia. Taidanpa tehdä vielä pienen ohjelman, joka testaa vielä tuota muistin varaamista structissa.
Itse varailen structeille muistia myös itse oliolle. Kun luot oliot TObject, varaatko sen seuraavasti?
TObject * objekti
Aina kun itse varaan tuolla tavalla, niin ohjelma kaatuu koska itse oliolle ei ole varattu muistia. Lisäisin siis seuraavan rivin:
void Lataa3DMalli( TObjekti *pobjekti, char* filename ){ // Luetaan montako verteksiä malli sisältää. p_objekti = new TObjekti; //Tämän lisäsin p_objekti->verteksi = new TVerteksi [VerteksienMaara]; }
Hyvä tapa olisi jos muisti pitää varata jostain syystä jossain muualla kuin ko. funktiossa:
void Lataa3DMalli( TObjekti *pobjekti, char* filename ) { // Tarkistetaan onko oliolle varattu muistia if (!pobjekti) return; // Luetaan montako verteksiä malli sisältää. p_objekti->verteksi = new TVerteksi [VerteksienMaara]; }
Jeeh.
Juu..pitänee kokeilla. Huomasin juuri, että Lataa3DMalli funktiossa vaihtelevat pobjekti ja p_objekti. Oma kirjoitusvirhe. :) ( Kyse on siis samasta pointterista. )
Tein yhden kokeiluohjelman ( Win 32 Console ) ja sinne structiin pointtereita ja sitten varaamaan muistia. Tämä kyllä näyttäisi toimivan, mutta sitten 3d mallin latausohjelmassa itse muistin varaus toimii, mutta taulukoiden käyttö kaataa ohjelman.
Itse alan olla jo aika vakaasti sitä mieltä, että structissa ei käytännössä voi olla useita ajon aikana varattavia taulukoita. Luulisin että se on ennemminkin vakion kokoinen. Eli kun esim. structissa on vaikkapa 5 int tyyppistä muuttujaa, ja tehdään yksi muuttuja tämn structin pohjalta, niin silloin varataan muistia vain tuon int muuttujan verran. Ehkä minun kannattaa laittaa vain structin jokin riittävän suuri taulukko vertekseille ja olla siihen tyytyväinen.
Miksiköhän juuri missään (tutoriaaleissa) ei kerrota tästä asiasta?
Kokeilin googlella hakusanoja unsized array ja struct, mutta eipä löytynyt juuri mitään.
Tässä eräs lainaus yhdeltä nettisivulta:
"Unsized arrays can appear only as the last member of a structure." - Microsoft specific
"Unsized arrays can appear only as the last member of a structure"
Mitä potaskaa... Itse käytän structeissa määrittelemättömiä taulukoita erittäin paljon, siellä sun täällä. Itselläni paikka ei vaikuta mitenkään. Miksi se edes vaikuttaisi?
Kannattaisi ennemmin laittaa tuo lataaja palauttamaan pointteri ladattuun muistiin, eli tee taulukostasi funktio joka palauttaa TObjekti-muuttujan pointterin.
TObjekti * Lataa3DMalli(char * filename){ TObjekti * temp; temp = new TObjekti; //Varataan tilaa itse rakenteelle if(temp == NULL)return NULL; //Luetaan verteksimäärä temp->verteksi = new TVerteksi[VerteksienMaara]; if(temp->verteksi == NULL){ delete temp; //Tämä varattiin jo, ei jätetä muhimaan... return NULL; } return temp; //Palautetaan onnellinen lopputulos. }
Juu, missään nimessä ei kannata vertekseille fixed-size taulukkoa tehdä, vaan kannattaa tehdä juuri niin kuin sqwiik opasti.
Jos softa ei ala toimia niin paljastele lisää koodia.
Vihdoin alkaa näyttää paremmalta. Kiitokset kaikille. Muutin 3d loaderia sqwiikin ehdottaman tavan mukaan ja toimii hienosti! Itse asiassa vika taisi olla tyyppien int ja unsigned short eroista.
Onhan nyt kuitenkin vielä niin, että kaikki mallille tarvittava muisti varataan tuossa funktiossa ja se palauttaa pointterin, jonka avulla mallia voi sitten käsitellä(piirrellä). Eli kun haluan ladata mallin, niin teen ensin TObjekti tyyppisen pointterin.
( Pitää olla selvillä, että tulee tehtyä oikein, eikä ohjelma kaadu inhottavasti. )
Eli jotenkin seuraavasti:
TObjekti * objekti; objekti = Lataa3DMalli( "malli.3ds" ); //piirto //Haetaan verteksin x koordinaatin arvo seuraavasti x = objekti->verteksi[joku].x;
No..pitänee vielä testata kunnolla.
Jep, noin sen pitäisi toimia juu, muista myös tuhota objekti delete:llä kun et enää sitä tarvitse.
Aihe on jo aika vanha, joten et voi enää vastata siihen.