Sprites and glTexSubImage()

This is a discussion on Sprites and glTexSubImage() within the Game Programming forums, part of the General Programming Boards category; Say I load in a large sprite map, and I want to show a portion on that sprite such as ...

  1. #1
    Software engineer
    Join Date
    Aug 2005
    Location
    Oregon
    Posts
    283

    Sprites and glTexSubImage()

    Say I load in a large sprite map, and I want to show a portion on that sprite such as (50,50) ending at (100, 100). Would I use glTexSubImage() to do this task? My book mentions it, but instead it says "Replaces a portion of an existing texture map." So, it seems like it's in reverse. Is there another function that I would use for this task?

  2. #2
    The Right Honourable psychopath's Avatar
    Join Date
    Mar 2004
    Location
    Where circles begin.
    Posts
    1,070
    To my knowledge, glTexSubImage() is a method for updating a texture that has been created already. If you were done with a texture, you could just use this function to put another texture in it's place, avoiding more glGenTextures() calls and such, as well as conserving memory.

    I've read stuff on using portions of textures like you said, but I really can't remember where. If I find it, i'll post it.
    Memorial University of Newfoundland
    Computer Science

    Mac and OpenGL evangelist.

  3. #3
    Software engineer
    Join Date
    Aug 2005
    Location
    Oregon
    Posts
    283
    Thanks... yeah, my hope is just to get a sprite map working like a typical classic Zelda game. The character's images would change if the player presses the downward key, and so on.

  4. #4
    The Right Honourable psychopath's Avatar
    Join Date
    Mar 2004
    Location
    Where circles begin.
    Posts
    1,070
    I haven't found anything to confirm this would work, but it sounds ok to me.

    Why not just load the sprite map into an array, and then create the cut images from the array?

    Array:
    Code:
    unsigned char pixels[IMAGE_WIDTH*IMAGE_HEIGHT][4];
    Then you could rebuild an image from the sprite map...
    Code:
    unsigned char newMap[CUT_WIDTH*CUT_HEIGHT][4];
    
    for(int i=0; i<CUT_WIDTH; i++){
     for(int j=0; j<CUT_HEIGHT; j++){
       newMap[i+j][0] = pixels[(CUT_X+CUT_Y)+(i+j)][0];
       newMap[i+j][1] = pixels[(CUT_X+CUT_Y)+(i+j)][1];
       newMap[i+j][2] = pixels[(CUT_X+CUT_Y)+(i+j)][2];
       newMap[i+j][3] = pixels[(CUT_X+CUT_Y)+(i+j)][3];
     }
    }
    Ok, that code probably wouldn't work, but the idea is generally the same. Then you would just index the cut images in an array or list, and swap them on a key press or another event.
    Memorial University of Newfoundland
    Computer Science

    Mac and OpenGL evangelist.

  5. #5
    Software engineer
    Join Date
    Aug 2005
    Location
    Oregon
    Posts
    283
    I see what you're saying. If I can load the entire sprite map into an array, then use a OGL function to read a portion from that array, that could possibly work. I assume OGL has something like this. I do appreciate your code though. I'll play around with it.

  6. #6
    The Right Honourable psychopath's Avatar
    Join Date
    Mar 2004
    Location
    Where circles begin.
    Posts
    1,070
    Well, you wouldn't use an OGL function to read a portion from the array. That's what those for loops in my above post are for. It takes the sprite map data (assumed to be 32 bit), and then copies a section of it into a new array, starting with CUT_X and CUT_Y, and ending with CUT_WIDTH and CUT_HEIGHT. Then you create new textures with these arrays copied from the sprite map. Then you have the textures, and you can swap them with glTexSubImage().
    Memorial University of Newfoundland
    Computer Science

    Mac and OpenGL evangelist.

  7. #7
    Software engineer
    Join Date
    Aug 2005
    Location
    Oregon
    Posts
    283
    That makes sense. I remember in DirectDraw every sprite had its own surface. I would just fill in a Win32 RECT function (left, right, top, bottom) in longs and tell DirectDraw this is what I want. So, I guess it does the same thing that you thought about. I guess DirectX likes to hide that stuff. Thanks for the help. I'll show my progress on here throughout the week.

  8. #8
    Software engineer
    Join Date
    Aug 2005
    Location
    Oregon
    Posts
    283
    I just came around glDrawArrays(). Is this what we need?

    Here's my code currently too. I got the TGA file to load, and it is currently showing the entire 512x512 sprite map in a 50x50 rectangle. Much more work to be done

    Code:
    #include "OpenGLSB.h"
    #include "GLTools.h"
    
    #define SCREEN_WIDTH		800
    #define SCREEN_HEIGHT		600
    
    #define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
    #define KEYUP(vk_code)   ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
    
    #define TEXTURE_MAPTILES	0
    #define TEXTURE_COUNT		1 
    GLuint textures[TEXTURE_COUNT];
    const char *szTextureFiles[TEXTURE_COUNT] = { "MapTiles.tga" };
    
    void RenderScene(void);
    void ResizeScene(GLint w, GLint h);
    void SetupRC(void);
    void ReleaseRC(void);
    void TimerFunc(GLint value);
    void GetInput(void); 
    
    int main(void) {
    	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    	glutInitWindowPosition(150, 150);
    	glutInitWindowSize(SCREEN_WIDTH, SCREEN_HEIGHT);
    	glutCreateWindow("OpenGL Console");
    	glutDisplayFunc(RenderScene);
    	glutReshapeFunc(ResizeScene);  
    	glutTimerFunc(33, TimerFunc, 0);
    
    	SetupRC();
    	glutMainLoop();
    	ReleaseRC();
    
    	return 0;
    }
    
    void SetupRC(void) {  
    	GLubyte *pBytes = NULL;
    	GLint iWidth, iHeight, iComponents;
    	GLenum eFormat;
    	GLint i; 
    	
    	glClearColor(0.0f, 0.0f, 1.0f, 1.0f);  
    	
    	glEnable(GL_TEXTURE_2D);
    	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    
    	// Load Textures
    	glGenTextures(TEXTURE_COUNT, textures);
    	
    	for (i = 0; i < TEXTURE_COUNT; i++) {
    		// Bind to next texture object
    		glBindTexture(GL_TEXTURE_2D, textures[i]);
     
    		// Load texture, set filter and wrap modes.
    		pBytes = gltLoadTGA(szTextureFiles[i], &iWidth, &iHeight, &iComponents, &eFormat);
    
    		if (pBytes != NULL) {  
    		gluBuild2DMipmaps(GL_TEXTURE_2D, iComponents, iWidth, iHeight, eFormat, GL_UNSIGNED_BYTE, pBytes);
    		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     
    		free(pBytes);  // Free origional texture data.
    		} else {
    			printf("Error reading file.\n");
    		}
    	}
    }
    
    void ReleaseRC(void) {
    	glDeleteTextures(TEXTURE_COUNT, textures);
    }
    
    void ResizeScene(GLint w, GLint h) {
    	if (h == 0) 
    		h = 1;
    
    	glViewport(0, 0, w, h);
    
    	glMatrixMode(GL_PROJECTION);
    	glLoadIdentity();
    
    	gluOrtho2D(-400.0, 400.0, -300.0, 300.0);
    
    	glMatrixMode(GL_MODELVIEW);
    	glLoadIdentity();
    }
    
    void TimerFunc(GLint value) {
    	GetInput();
    
    	glutPostRedisplay();
    	glutTimerFunc(33, TimerFunc, 0);
    }
    
    void GetInput(void) { 
    }
    
    void RenderScene(void) {  
    	glClear(GL_COLOR_BUFFER_BIT); 
    
    	glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_MAPTILES]);
    	glBegin(GL_QUADS); 
    		// Lower-left
    		glTexCoord2f(-1.0f, -1.0f); 
    		glVertex2f(-25.0f, -25.0f);
    
    		// Lower-right
    		glTexCoord2f(1.0f, -1.0f);
    		glVertex2f(25.0f, -25.0f);
    
    		// Upper-right
    		glTexCoord2f(1.0f, 1.0f);
    		glVertex2f(25.0f, 25.0f);
    
    		// Upper-left
    		glTexCoord2f(-1.0f, 1.0f);
    		glVertex2f(-25.0f, 25.0f); 
    	glEnd();
    
    	glutSwapBuffers();
    }

  9. #9
    The Right Honourable psychopath's Avatar
    Join Date
    Mar 2004
    Location
    Where circles begin.
    Posts
    1,070
    glDrawArrays() is for drawing arrays of verticies, texture coords, normals, etc.

    Your arrays of texture data would be supplied to something gluBuild2DMipmaps, just like your TGA data. For example, gltLoadTGA returns such an array, which you have stored in *pBytes. pBytes is then passed to OpenGL through gluBuild2DMipmaps, which constructs a usable texture from the pixel data in pBytes.

    What you would want to do is create a function similar to gltLoadTGA which returns an unsigned byte array, but would perhaps take an argument specifying the spritemap and some coords to create the texture from.

    Eg:
    Code:
    GLubyte GetCutTexture(GLubyte *spritemap, int x, int y, int w, int h){
     GLubyte *cutTexture;
    
     //TODO: you can fill this part in :)
    
     return cutTexture;
    }
    Memorial University of Newfoundland
    Computer Science

    Mac and OpenGL evangelist.

  10. #10
    Software engineer
    Join Date
    Aug 2005
    Location
    Oregon
    Posts
    283
    I can't read the language on this site, but I can with the code It looks like what I'm trying to do. Take a look at how he does the glTexCoord2f() function.

    http://www.gisdeveloper.co.kr/opengl...pengl11/11.htm

  11. #11
    Software engineer
    Join Date
    Aug 2005
    Location
    Oregon
    Posts
    283
    I did it I had to change his perspective projection into an ortho2d projection, but everything worked at the end I'm suprised I need masks for every single sprite though. I wonder if I can take a sprite, draw it as a mask first, then draw it as normal using the same image. Is that possible? It would save half as much as memory needed for sure. Anyway, I'm glad I got something going now.

    Here's the code:

    Code:
    #include "OpenGLSB.h"
    #include "GLTools.h"
    
    #define SCREEN_WIDTH		800
    #define SCREEN_HEIGHT		600
    
    #define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
    #define KEYUP(vk_code)   ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
    
    #define TEXTURE_BACK		0
    #define TEXTURE_MASK		1
    #define TEXTURE_SPRITE		2
    #define TEXTURE_COUNT		3 
    GLuint textures[TEXTURE_COUNT];
    const char *szTextureFiles[TEXTURE_COUNT] = { "back.tga", "mask.tga", "sprite.tga" };
    
    void RenderScene(void);
    void ResizeScene(GLint w, GLint h);
    void SetupRC(void);
    void ReleaseRC(void);
    void TimerFunc(GLint value);
    void GetInput(void); 
    
    int main(void) {
    	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    	glutInitWindowPosition(150, 150);
    	glutInitWindowSize(SCREEN_WIDTH, SCREEN_HEIGHT);
    	glutCreateWindow("OpenGL Console");
    	glutDisplayFunc(RenderScene);
    	glutReshapeFunc(ResizeScene);  
    	glutTimerFunc(33, TimerFunc, 0);
    
    	SetupRC();
    	glutMainLoop();
    	ReleaseRC();
    
    	return 0;
    }
    
    void SetupRC(void) {  
    	GLubyte *pBytes = NULL;
    	GLint iWidth, iHeight, iComponents;
    	GLenum eFormat;
    	GLint i; 
    	
    	glClearColor(0.0f, 0.0f, 1.0f, 1.0f);  
      
    	glEnable(GL_TEXTURE_2D);
    	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
    	glShadeModel(GL_SMOOTH);
    	
    	// Load Textures
    	glGenTextures(TEXTURE_COUNT, textures);
    	
    	for (i = 0; i < TEXTURE_COUNT; i++) {
    		// Bind to next texture object
    		glBindTexture(GL_TEXTURE_2D, textures[i]);
     
    		// Load texture, set filter and wrap modes.
    		pBytes = gltLoadTGA(szTextureFiles[i], &iWidth, &iHeight, &iComponents, &eFormat);
    
    		if (pBytes != NULL) {  
    		gluBuild2DMipmaps(GL_TEXTURE_2D, iComponents, iWidth, iHeight, eFormat, GL_UNSIGNED_BYTE, pBytes);
    		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 
     
    		free(pBytes);  // Free origional texture data.
    		} else {
    			printf("Error reading file.\n");
    		}
    	}
    }
    
    void ReleaseRC(void) {
    	glDeleteTextures(TEXTURE_COUNT, textures);
    }
    
    void ResizeScene(GLint w, GLint h) {
    	if (h == 0) 
    		h = 1;
    
    	glViewport(0, 0, w, h);
    
    	glMatrixMode(GL_PROJECTION);
    	glLoadIdentity();
    
    	gluOrtho2D(-400.0, 400.0, -300.0, 300.0);
    
    	glMatrixMode(GL_MODELVIEW);
    	glLoadIdentity();
    }
    
    void TimerFunc(GLint value) {
    	GetInput();
    
    	glutPostRedisplay();
    	glutTimerFunc(33, TimerFunc, 0);
    }
    
    void GetInput(void) { 
    	if (KEYDOWN(VK_ESCAPE))
    		exit(0);
    }
    
    void RenderScene(void) { 
    	static GLint frame = 0;
    	static GLfloat x = 0.0f;
    
    	glClear(GL_COLOR_BUFFER_BIT); 
    	glLoadIdentity();
    
    	glDisable(GL_BLEND);	
    	glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_BACK]);
    	glBegin(GL_QUADS); 
    		// Lower-left
    		glTexCoord2f(-1.0f, -1.0f); 
    		glVertex2f(-400.0f, -300.0f);
    
    		// Lower-right
    		glTexCoord2f(1.0f, -1.0f);
    		glVertex2f(400.0f, -300.0f);
    
    		// Upper-right
    		glTexCoord2f(1.0f, 1.0f);
    		glVertex2f(400.0f, 300.0f);
    
    		// Upper-left
    		glTexCoord2f(-1.0f, 1.0f);
    		glVertex2f(-400.0f, 300.0f); 
    	glEnd(); 
    
    	// Mask Sprite
    	glEnable(GL_BLEND);
    	glBlendFunc(GL_DST_COLOR, GL_ZERO);
    	glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_MASK]);
    	glBegin(GL_QUADS);
    		glTexCoord2f(0.0f, frame*0.5f); glVertex3f(-25.0f+x, -25.0f, 0.0f);     // <11-2>
    		glTexCoord2f(1.0f, frame*0.5f); glVertex3f(25.0f+x, -25.0f, 0.0f);      // <11-3>
    		glTexCoord2f(1.0f, frame*0.5f+0.5f); glVertex3f(25.0f+x, 25.0f, 0.0f);  // <11-4>
    		glTexCoord2f(0.0f, frame*0.5f+0.5f); glVertex3f(-25.0f+x, 25.0f, 0.0f); // <11-5>
    	glEnd();
    
    	// Sprite
    	glBlendFunc(GL_ONE, GL_ONE);
    	glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_SPRITE]);
    	glBegin(GL_QUADS);
    		glTexCoord2f(0.0f, frame*0.5f); glVertex3f(-25.0f+x, -25.0f, 0.0f);     // <14-2>
    		glTexCoord2f(1.0f, frame*0.5f); glVertex3f(25.0f+x, -25.0f, 0.0f);      // <14-3>
    		glTexCoord2f(1.0f, frame*0.5f+0.5f); glVertex3f(25.0f+x, 25.0f, 0.0f);  // <14-4>
    		glTexCoord2f(0.0f, frame*0.5f+0.5f); glVertex3f(-25.0f+x, 25.0f, 0.0f); // <14-5>
        glEnd();
    
    	glDisable(GL_BLEND);
    
    	if (frame == 0)
    		frame = 1;
    	else
    		frame = 0;
    
    	x += 0.025f;     
    	if(x>6.0f) x=-5.0f;   
     
    	glutSwapBuffers();
    }

  12. #12
    The Right Honourable psychopath's Avatar
    Join Date
    Mar 2004
    Location
    Where circles begin.
    Posts
    1,070
    Now why didn't I think of just offsetting the texture coords?

    Anyway, glad to hear you got it working.
    Memorial University of Newfoundland
    Computer Science

    Mac and OpenGL evangelist.

  13. #13
    Software engineer
    Join Date
    Aug 2005
    Location
    Oregon
    Posts
    283
    hehe, I guess we still live and learn Thanks for all the help. I still am curious if the masking sprite still needs to be in there. Say I have 50 sprite sheets, but then that means 50 sprite masking sheets as well. Is there a way to make the origional sprite think it's a mask, then redraw the real sprite ontop? The reason why I think this could be possible is because we already know what the sprite's figure looks like. If it can be smudged in black, I wonder if we can save a few 50 sprite maps on release. I don't know how this bottlenecks performance though.

  14. #14
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,598
    D3D still allows you to acces surfaces through IDirect3DTexture9::LockRect(), but there are more elegant solutions.....sometimes.

Popular pages Recent additions subscribe to a feed

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21