OpenGL - Caricamento delle trame in modo errato
Domanda
UPDATE:
Ho pubblicato il codice Renderer di seguito, poiché questo codice qui non sembra essere il problema.
Sto riscontrando un problema con un mio codice in cui quando provo a caricare più trame su openGL, una alla volta, fallisce in modo un po 'spettacolare, con il renderer che finisce per usare solo una singola trama. Ho fatto un po 'di debug per rintracciare l'errore a questa funzione, ma sto riscontrando problemi nel capire quale parte della funzione è in errore. Ci sono errori particolarmente ovvi che sto facendo che semplicemente non vedo, o c'è un difetto più sottile nel mio codice?
Ecco le strutture che utilizzo per archiviare le informazioni sulle trame e generalmente per tenere traccia di tutti i miei puntatori
typedef struct {
float Width;
float Height;
} texInfo;
typedef struct {
dshlib::utfstr ResourceName;
texInfo * TextureInfo;
GLuint TextureNum;
SDL_Surface * Image;
} texCacheItem;
Ed ecco l'attuale caricatore grafico WIP. Fondamentalmente, carica un file .png con nome da un archivio .zip usando una libreria pre-scritta (per inciso, viene testato con questo programma). Quindi viene caricato con libpng e quindi caricato come trama, con la cache inserita per accelerare il caricamento ed evitare di caricare una singola trama più di una volta. Ho omesso le dichiarazioni #include poiché erano solo cruft.
texCacheItem * loadGraphics(dshlib::utfstr FileName) {
for(int i = 0; i < NumTexCached; i++) { //First see if this texture has already been loaded
if(TextureCache[i]->ResourceName == FileName)
return TextureCache[i];
}
dshlib::utfstr FullFileName = "Data/Graphics/"; //If not, create the full file path in the archive
FullFileName += FileName;
dshlib::FilePtr file = resourceCtr.OpenFile(FullFileName); //And open the file
if (!file->IsOk()) { //If the file failed to load...
EngineState = ENGINESTATE_ERR;
return NULL;
}
SDL_Surface * T = loadPNG(file);
texCacheItem * Texture = new texCacheItem;
Texture->TextureInfo = new texInfo;
glGenTextures(1, &Texture->TextureNum); //Allocate one more texture and save the name to the texCacheItem
glBindTexture(GL_TEXTURE_2D, Texture->TextureNum); //Then create it
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, T->w, T->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, T->pixels);
Texture->TextureInfo->Width = (float)T->w; //Write the useful data
Texture->TextureInfo->Height = (float)T->h;
Texture->ResourceName = FileName; //And the caching info needed
Texture->Image = T; //And save the image for if it's needed later and for deleting
if (!TexCacheSize) { //If this is the first load this is 0, so allocate the first 8 Cache slots.
TexCacheSize = 8;
TextureCache = new texCacheItem*[8];
}
if(NumTexCached == TexCacheSize) { //If we're out of cache space
if (TexCacheSize == 32768) { //If too many cache items, error out
EngineState = ENGINESTATE_ERR;
return NULL;
}
TexCacheSize <<= 1; //Double cache size
texCacheItem ** NewSet = new texCacheItem*[TexCacheSize];
memcpy(NewSet, TextureCache, NumTexCached * sizeof(texCacheItem*)); //And copy over the old cache
delete TextureCache; //Delete the old cache
TextureCache = NewSet; //And assign the pointer to the new one
}
TextureCache[NumTexCached++] = Texture; //Store the texCacheItem to the Cache
file->Close(); //Close the file
file = NULL; //And NULL the smart pointer. [NTS: Confirm with Disch this is what won't cause a memory leak]
return Texture; //And return the loaded texture in texCacheItem form.
}
SDL_Surface *loadPNG(dshlib::FilePtr File)
{
Uint8 *PNGFile = new Uint8[(long)File->GetSize()];
File->GetAr<Uint8>(PNGFile, (long)File->GetSize());
return IMG_LoadPNG_RW(SDL_RWFromMem(PNGFile, (long)File->GetSize()));
}
Ecco il file di codice del renderer. Al momento è piuttosto disordinato, ci scusiamo per quello. level- > activeMap dice sostanzialmente al renderer quale "layer" della tilemap (0 è la parte anteriore, 3 la parte posteriore) per disegnare gli sprite sopra.
#include "../MegaJul.h"
void render(void) {
//Render the current tilemap to the screen
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -4.0f);
if (level) {
glBegin(GL_QUADS);
float dT = 32.0f / level->dTex;
float sX, fX, fXa, sY, tX, tY, sYa, sYb, sXa, tXa, tYa;
unsigned long m = level->mapDimensions[0] * level->mapDimensions[1];
float ai; long long t; Sint16 * p;
glBindTexture(GL_TEXTURE_2D, level->tilemap->TextureNum);
for (int i = 3; i >= 0; i--) {
if (level->layers[i]->mapPosition[0] > 0)
level->layers[i]->mapPosition[0] = 0;
if (level->layers[i]->mapPosition[0] < 0 - (signed long)((level->mapDimensions[0] - 21) * 32))
level->layers[i]->mapPosition[0] = 0 - (signed long)((level->mapDimensions[0] - 21) * 32);
if (level->layers[i]->mapPosition[1] < 0)
level->layers[i]->mapPosition[1] = 0;
if (level->layers[i]->mapPosition[1] > (signed long)((level->mapDimensions[1] - 16) * 32))
level->layers[i]->mapPosition[1] = (signed long)((level->mapDimensions[1] - 16) * 32);
if (i == level->activeMap) {
for (int j = 0; j < NumSprites; j++) {
glBindTexture(GL_TEXTURE_2D, Sprites[j]->Graphics->TextureNum);
Sprites[j]->render(level->layers[i]->mapPosition[0], level->layers[i]->mapPosition[1]);
}
for (int j = 0; j < NumBullets; j++) {
glBindTexture(GL_TEXTURE_2D, Bullets[j]->Texture->TextureNum);
Bullets[j]->render(level->layers[i]->mapPosition[0], level->layers[i]->mapPosition[1]);
}
}
glBindTexture(GL_TEXTURE_2D, level->tilemap->TextureNum);
t = 0 - ((level->layers[i]->mapPosition[0] - (level->layers[i]->mapPosition[0] % 32)) /32) + (((level->layers[i]->mapPosition[1] - (level->layers[i]->mapPosition[1] % 32)) /32) * level->mapDimensions[0]);
ai = (float)(3 - i); //Invert Z-Index
sX = (float)((level->layers[i]->mapPosition[0] % 32));
sY = (float)((level->layers[i]->mapPosition[1] % 32));
if (sX > 0)
sX -= 32;
if (sY < 0)
sY += 32;
fX = sX /= 32.0f;
sY /= 32.0f;
fXa = sXa = sX + 1.0f;
sYa = sY + 14.0f;
sYb = sY + 15.0f;
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 21; x++) {
p = level->tiles[level->layers[i]->map[t]]->position;
tX = p[0] / level->dTex;
tY = p[1] / level->dTex;
tXa = tX + dT;
tYa = tY + dT;
glTexCoord2f(tX, tYa); glVertex3f(fX, sYa, ai); // Bottom Left Of The Texture and Quad
glTexCoord2f(tXa,tYa); glVertex3f(fXa, sYa, ai); // Bottom Right Of The Texture and Quad
glTexCoord2f(tXa,tY); glVertex3f(fXa, sYb, ai); // Top Right Of The Texture and Quad
glTexCoord2f(tX, tY); glVertex3f(fX, sYb, ai); // Top Left Of The Texture and Quad
fX += 1.0f;
fXa += 1.0f;
t++;
if (t >= m) break;
}
sYb -= 1.0f; sYa -= 1.0f;
fXa = sXa; fX = sX;
t += level->mapDimensions[0] - 21; //21 is the number of tiles drawn on a line (20 visible + 1 extra for scrolling)
}
}
glEnd();
}
SDL_GL_SwapBuffers();
}
Ecco i segmenti di codice che impostano i dati della piastrella per gli sprite e il livello:
Livello:
void loadLevel(dshlib::utfstr FileName) {
-snip-
texCacheItem * Tex = loadGraphics(FileName);
if (!Tex) { //Load the tile graphics for the level
unloadLevel();
EngineState = ENGINESTATE_ERR;
return;
} else {
level->dTex = Tex->TextureInfo->Width;
level->tilemap = Tex;
}
-snip-
}
Sprite:
void SpriteBase::created() {
this->Graphics = loadGraphics(DefaultGFX());
-snip-
}
AGGIORNAMENTO 2:
Sid Farkus ha notato un grosso errore che ho fatto con il renderer, quindi ecco un renderer.cpp aggiornato:
#include "../MegaJul.h"
void render(void) {
//Render the current tilemap to the screen
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -4.0f);
if (level) {
float dT = 32.0f / level->dTex;
float sX, fX, fXa, sY, tX, tY, sYa, sYb, sXa, tXa, tYa;
unsigned long m = level->mapDimensions[0] * level->mapDimensions[1];
float ai; long long t; Sint16 * p;
for (int i = 3; i >= 0; i--) {
if (level->layers[i]->mapPosition[0] > 0)
level->layers[i]->mapPosition[0] = 0;
if (level->layers[i]->mapPosition[0] < 0 - (signed long)((level->mapDimensions[0] - 21) * 32))
level->layers[i]->mapPosition[0] = 0 - (signed long)((level->mapDimensions[0] - 21) * 32);
if (level->layers[i]->mapPosition[1] < 0)
level->layers[i]->mapPosition[1] = 0;
if (level->layers[i]->mapPosition[1] > (signed long)((level->mapDimensions[1] - 16) * 32))
level->layers[i]->mapPosition[1] = (signed long)((level->mapDimensions[1] - 16) * 32);
if (i == level->activeMap) {
for (int j = 0; j < NumSprites; j++) {
glBindTexture(GL_TEXTURE_2D, Sprites[j]->Graphics->TextureNum);
glBegin(GL_QUADS);
Sprites[j]->render(level->layers[i]->mapPosition[0], level->layers[i]->mapPosition[1]);
glEnd();
}
for (int j = 0; j < NumBullets; j++) {
glBindTexture(GL_TEXTURE_2D, Bullets[j]->Texture->TextureNum);
glBegin(GL_QUADS);
Bullets[j]->render(level->layers[i]->mapPosition[0], level->layers[i]->mapPosition[1]);
glEnd();
}
}
glBindTexture(GL_TEXTURE_2D, level->tilemap->TextureNum);
glBegin(GL_QUADS);
-snipped out renderer since it was bloat
glEnd();
}
SDL_GL_SwapBuffers();
}
Soluzione
Nel tuo renderer stai chiamando glBindTexture in modo appropriato? Sembra che il tuo renderer stia usando qualunque sia l'ultima texture caricata dall'ultima volta che hai chiamato glBindTexture. glBindTexture è ciò che dice alla texture OpenGL di usare per i tuoi poligoni.
Altri suggerimenti
Con il tuo codice di rendering posso vedere che stai chiamando BindTexture in un blocco glBegin / End. Dai documenti opengl:
GL_INVALID_OPERATION viene generato se glBindTexture viene eseguito tra l'esecuzione di glBegin e la corrispondente esecuzione di glEnd.
Sposta le tue chiamate BindTexture fuori dal blocco glBegin () / glEnd () e dovresti essere d'oro. Probabilmente dovrai avere più blocchi per adattarti al tuo stile di rendering.
modifica:
Con il codice aggiornato, assicurati di un paio di cose; le posizioni dello sprite sono visibili sullo schermo con l'attuale matrice di proiezione / vista modello e gli ID di trama dello sprite sono trame valide. Non c'è nulla di tecnicamente sbagliato che mi salta fuori adesso, ma i tuoi valori potrebbero essere fuori.
Suppongo che non sia possibile eseguire alcun debug o registrazione per questo? Se potessi, mi aspetto che sarebbe banale da diagnosticare.
La cosa principale che mi sembra pericolosa è che non stai controllando il valore di ritorno da loadPNG. Metterei qualcosa lì come la prima cosa che ho fatto.
Vorrei anche commentare il controllo iniziale per una trama già memorizzata nella cache. Se le cose iniziano a funzionare a quel punto, sai che è un problema con i nomi delle risorse o i nomi dei file (o il loro confronto).
Per inciso, sono sorpreso che tu stia usando classi e puntatori intelligenti ma stia rotolando il tuo std :: vector con puntatori e matrici nudi. ;)