• Advertisement
Sign in to follow this  

tilemap trouble

This topic is 4675 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey, I am having this weird bug with my tiles. The pixels seem to wrap 1px in both the x and the y coords. I have tried to fix this bug, but I just can't seem to figure out what it is. I think that it must be in the loading code, i'm not sure. I couldn't get a screen shot for some reason. Here is the project in question (9MB). Here is the most suspect code.
// FILE: TileMap.h by Richard Hughes. 30/11/2003
//
// DESC: This declares the CTileMap and assosiated classs

#pragma once

// INCLUDES
#include "MemoryManager.h"
#include "ErrorLogging.h"
#include "Graphics.h"
#include "Image.h"
#include "Util.h"
#include "FileInfo.h"

/*********************************************************************************************/
//
//	CLASS: tagTile
//
//	DESC: This is a tile
//
/*********************************************************************************************/

typedef struct tagTile : public IMemObject {
	tagTile( void )
	{
	}
	~tagTile( void )
	{
	}
	unsigned char* m_pData;    // the image data
	long           m_lWidth,   // the tiles width
		           m_lHeight;  // the tiles height
	unsigned long  m_dwFormat; // RGB RGBA
	unsigned long  m_dwInternalFormat; // RGB RGBA

	QUICK_SIZE;
}TILE, *LPTILE;

/*********************************************************************************************/
//
//	CLASS: CTileMap
//
//	DESC: This handles tilemaps. see doc for more info
//
/*********************************************************************************************/

class CTileMap : public IError {

	CPointer < TILE > m_Tiles;  // the tiles in the tilemap

public:
	CTileMap( void )
	{
	}
	~CTileMap( void )
	{
	}

	// this loads in a tile map directly
	ERROR_STRING Load( string strFName, int nHeight, int nWidth, long lTWidth, long lTHeight, 
					   int nHBorder, int nVBorder );

	// this loads certain tiles from a tilemap directly
	ERROR_STRING Load( string strFName, int nHeight, int nWidth, long lTWidth, long lTHeight, 
					   int nHBorder, int nVBorder, pint pList );

	// this loads in a tilemap directly via a tilemap file
	ERROR_STRING Load( string strFName );

	// this loads in a tilemap indirectly via a tilemap file
	ERROR_STRING Load( string strFName, pint pList, pstring pNames );

	// this returns a tile
	inline TILE& GetTile( int nIndex )
	{
		return ( m_Tiles[ nIndex ] );
	} // end GetTile

	QUICK_NAME( "CTileMap" );
	QUICK_SIZE;
}; // end CTileMap

// EOF: TileMap.h

// FILE: TileMap.cpp by Richard Hughes. 30/11/2003
//
// DESC: This defines the CTileMap class

// INCLUDES
#include "TileMap.h"

/*********************************************************************************************/
//
//	CLASS: CTileMap
//
//	DESC: This handles tilemaps. see doc for more info
//
/*********************************************************************************************/

// this loads in a tilemap
// it takes the tilemaps filename, the amount of cells heigh and wide, the height and the width of the tiles,
// and the horizontal and vertical border
// it returns the nessercary ERROR_STRING error
ERROR_STRING CTileMap::Load( string strFName, int nHeight, int nWidth, long lTWidth, long lTHeight, 
							 int nHBorder, int nVBorder )
{
	Enter( "Load_Direct" );


	int nBPP = 0; // the images BPP

	// load in the tilemap
	CImage Image;
	ERROR( Image.Load( strFName ) );

	// init the tiles
	m_Tiles.Init( nWidth*nHeight );

	// set the BPP
	nBPP = ( Image.GetFormat( ) == CF_RGB ) ? 3 : 4;

	// compute the tile area
	UCHAR *pSource, *pDest;    // pointers for saving
	long Width = lTWidth * nBPP;
	long Height = lTHeight * nBPP;
	long TileArea = Width*Height;

	/*
		extract the image data into the cells
	*/
	// get the image data
	unsigned char* p = Image.GetData( );
	pSource = Image.GetData( );

	// get the image width in bits ( RGB )
	long lImageWidth = Image.GetWidth( ) * nBPP;
	long l = 0;

	// loop thru and load in all of the cells
	for ( long idx = 0; idx < nWidth; idx++ )
		for ( long idy = 0; idy < nHeight; idy++ ) {
			l = idx+( idy*nWidth );

			// set the image stats
			m_Tiles[ l ].m_pData = new unsigned char[ TileArea ];
			memset( m_Tiles[ l ].m_pData, 0, sizeof ( unsigned char ) * TileArea );
			m_Tiles[ l ].m_lWidth = lTWidth;
			m_Tiles[ l ].m_lHeight = lTHeight;
			m_Tiles[ l ].m_dwFormat = Image.GetFormat( );
			m_Tiles[ l ].m_dwInternalFormat = Image.GetInternalFormat( );
			pDest = m_Tiles[ l ].m_pData;

			int cx = idx*Width;
			int cy = idy*lTHeight;

			// extract bitmap data
			pSource = Image.GetData( ) + cy*lImageWidth+cx;

			for ( int y = 0; y < lTHeight; y++ )
			{
				// copy over the first line of the tile
				memcpy( pDest, pSource, Width );

				// goto the next line
				pSource += lImageWidth;
				pDest += Width;
			} // end for
		} // end for

	RETURN_SUCCESS;
} // end CTileMap::Load
// FILE: 25DMap.h by Richard Hughes. 19/02/2005
//
// DESC: This file defines the 25DMap and assosiated classes

#ifndef __25DMap_h__
#define __25DMap_h__

// INCLUDES
#include <vector>
#include "ErrorLogging.h"
#include "Mesh.h"
#include "Tilemap.h"
#include "Animation.h"
#include "SpriteManager.h"

using namespace std;

/*********************************************************************************************/
//
//	CLASS: CTile
//
//	DESC: This is a tile
//
/*********************************************************************************************/

class CTile : public IError {
	
	char               m_strIdx[ MAX_STRING_LENGTH ]; // the tiles index
	CAnimation         m_Anim;   // this tiles animation
	int                m_nFrame; // the current animation frame
	CPointer < CQuad > m_pTiles; // the tile animations
	float              m_fX,     // the tiles x pos
		               m_fY;     // the tiles y pos
	int                m_nW,     // the tiles width
		               m_nH;     // the tiles height
	float              m_fSW,    // the scale width
		               m_fSH;    // the scale height

public:
	CTile( void )
	{
		m_nFrame = 0;
		m_fX = m_fY = 0.0f;
		m_nW = m_nH = 0;
		m_fSW = m_fSH = 1.0f;
	}
	~CTile( void )
	{
	}

	// this loads the tile
	ERROR_STRING Load( CPointer < CTileMap > pMap, int w, int h, string strFName );

//.....

#endif /* __25DMap_h__*/

// EOF: 25DMap.h

// FILE: 25DMap.cpp by Richard Hughes. 19/02/2005
//
// DESC: This file defines the 25DMap and assosiated classes

// INCLUDES
#include "25DMap.h"
#include "TexturePool.h"
#include "Texture.h"
#include "Sprite.h"

/*********************************************************************************************/
//
//	CLASS: CTile
//
//	DESC: This is a tile
//
/*********************************************************************************************/

// this loads the tile
// it takes the tilemap to load the tiles from, the tile dimentions and the tile file name
// it returns the necessary ERROR_STRING error
ERROR_STRING CTile::Load( CPointer < CTileMap > pMap, int w, int h, string strFName )
{
	Enter( "Load" );

	if ( !pMap )
		RETURN_POINTER_NULL( "pMap" );

	FILE*  fp      = NULL;  // the file pointer
	string str     = "";    // temp
	int    n       = 0;     // temp
	float  f       = 0.0f;  // temp
	bool   bKey    = false; // whether to use a color key for this tile
	int    r1      = 0;     // the min red component of the color key
	int    r2      = 0;     // the max red component of the color key
	int    g1      = 0;     // the min green component of the color key
	int    g2      = 0;     // the max green component of the color key
	int    b1      = 0;     // the min blue component of the color key
	int    b2      = 0;     // the max blue component of the color key
	int    nSize   = 0;     // the size of the animation
	pint   pAnim;           // the animation indexes
	pfloat pTimes;          // the frame times

	// set the tile dimentions
	m_nW = w; m_nH = h;

	// load in the tile file
	// open the file
	fp = fopen( strFName.data( ), "r" );
	if ( POINTER_NULL( fp ) )
		RETURN_NOT_OPENED( strFName );

	// read in the index
	fscanf( fp, "%s", m_strIdx );

	// read in the number of frames
	fscanf( fp, "%d", &nSize );

	// init the animations
	m_pTiles.Init( nSize );
	pAnim.Init( nSize );
	pTimes.Init( nSize );

	// read in the animations
	for ( int i = 0; i < nSize; i++ )
	{
		fscanf( fp, "%d", &n );
		fscanf( fp, "%f", &f );
		pAnim[ i ] = n;
		pTimes[ i ] = f;
	} // end for

	// read in the color key if there is one
	if ( fscanf( fp, "%d", &r1 ) != EOF &&
		 fscanf( fp, "%d", &r2 ) != EOF &&
		 fscanf( fp, "%d", &g1 ) != EOF &&
		 fscanf( fp, "%d", &g2 ) != EOF &&
		 fscanf( fp, "%d", &b1 ) != EOF &&
		 fscanf( fp, "%d", &b2 ) != EOF )
			bKey = true;

	// close the file
	fclose( fp );
	fflush( fp );
	fp = NULL;

	// load in the indexes
	for ( i = 0; i < nSize; i++ )
	{
		// setup the quad
		m_pTiles[ i ].Init( w,h, 0,0, VB_TEX2D_1 );

		// load in the tile
		str = strFName + ToString( " : %d", i );
		CTexturePool::GetSingleton( ).LoadTex( pMap->GetTile( *pAnim[ i ] ), str, bKey, r1,r2, g1,g2, b1,b2 );

		// attach tex
		CTexturePool::GetSingleton( ).AttachTex( &m_pTiles[ i ], str, false );

		// set the animation index
		pAnim[ i ] = i;
	} // end for

	// create the animation
	m_Anim.Set( pAnim, pTimes, 0, m_strIdx );
    
	RETURN_SUCCESS;
} // end CTile::Load
// EOF: 25DMap.cpp

NOTE: The code above has now been edited down to be more specific. Thanks, [Edited by - rpg_code_master on April 1, 2005 5:38:48 AM]

Share this post


Link to post
Share on other sites
Advertisement
I just skimmed through your code. You seem to use OpenGL or something to render your tiles? I didn't quite get what you meant by "wrap 1px in both x an the y coords", but if it is what I think it is, then you could try to shrink your tiles' texture size 0.5 pixels from all edges.

So if your texture co-ordinates are (0, 0) to (32, 32) then make that (0.5, 0.5) to (31.5, 31.5).

Share this post


Link to post
Share on other sites
I have narrowed down the code and have also mustered up a screen shot.

If you notice, the tiles seem to have wrapped 1px in the x and y directions. Note the top left, the sand path has grass on the wrong side and the ship in the bottom right the decking is incorect etc... This incorectness is only 1px I believe.

Share this post


Link to post
Share on other sites
In that code I don't see any indication (a) of how you load the textures, (b) how you set up the rendering system, or (c) how you do the rendering. These are the areas that are likely to be the problem, not the (edited) samples above.

The only suggestion I can give without any information on your rendering system is that your texture mode might be set to 'wrap' when it needs to be set to 'clamp'. How to do that will all depend on how you're actually rendering those graphics.

Share this post


Link to post
Share on other sites
There are two questions:
- what is your tile drawing code ?
Do you draw textured quads using integer coordinates or floating point vertices coords ? What is the unit width and height of your quad in world coordinates ?
- what is your texture tile loading code ?
What is the unit size of a texture tile in pixel ? If you load from a big texture bitmap multiple texture tiles, how do you calculate starting and ending texture coordinates for each tile ?

Ghostly yours,
Red.

Share this post


Link to post
Share on other sites
Ah, yes! Read my post again. I'm sure that will fix this problem :)

Basically you want your texture co-ordinates to point to the middle of the pixels, not their edges. That's why you add/subtract 0.5 pixels from the its size. Note that you do not change the actual size of the tile.

Otherwise the colour of the pixel next to it will have an influence.

Share this post


Link to post
Share on other sites
I'm pretty sure that your getting these results from using linear filtering, switch to point filtering and it will look just as it does on your images. If you want to use linear filtering without these borders render everything to a texture and then use linear filtering on the render texture.

Share this post


Link to post
Share on other sites
Yes, I use linear filtering. Whats point filtering? How do I implement that in OpenGL?

The tile drawing code is simply rendering a quad. The texture coords for that are 0,0 0,1 1,1 1,0. If you would like to see the code, download the project. It's all self explanitory.

I'm not going to post anymore code because there is alot of code posted.

To get the tile data, I copy the pixels from the main loaded image. That code is posted in the tile loading code.

Share this post


Link to post
Share on other sites
Machinoid, I use 0.0,0.0 1.0,1.0 0.0,1.0 1.0,0.0 as texture coordinates because the quad does not know the size of the tile. How can I point to the middle of the pixels?

I tried 0.05 and 0.95, the produces better results but it squashes the tile.

Share this post


Link to post
Share on other sites
I believe your problems will be solved using glTexParameterf. The filtering and wrapping modes are set via that.

Share this post


Link to post
Share on other sites
do something like this right after calling glTexImage2D (or when the tileset-texture is bound with glBindTexture):

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

hope this helps.

Share this post


Link to post
Share on other sites
I use the following code and get the above result

glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST_MIPMAP_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
glTexEnvi( GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_REPLACE );

Share this post


Link to post
Share on other sites
When I use the code:-

glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST_MIPMAP_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
glTexEnvi( GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_REPLACE );

I get this result:-



As you can see, the weird wrapping seems to go, but those borders seems to apeare(?).

Share this post


Link to post
Share on other sites
i think its your rendering of the tiles because in the clamp mode the gfx are correct its just that they are 1 pixel apart from each other.

nh = ( n / m_nW ); // +1
nh = m_nH - nh; // +1

possibly these 2 lines but i could not compile the code to test.

Share this post


Link to post
Share on other sites
No, that doesn't affect the result.

Here is the rendering code:-

This renders the map basicly.


// this renders the layer
inline bool Render( void )
{
if ( POINTER_NULL( m_pSet ) )
return false;

CTile* pTile = NULL; // saves on operators calls
int nSize = 0; // number of tiles to render
int i = 0; // counter
int n = 0; // temp
int nw=0,nh=0; // temp
int idx = 0; // index var
int mw = ( m_nSW > m_nW ) ? m_nW : m_nSW, // used to clamp small maps (see FillRenderList() w)
mh = ( m_nSH > m_nH ) ? m_nH : m_nSH; // used to clamp small maps (see FillRenderList() h)

// clip
Clip( );

// render the layer
for ( int h = 0, th = 0; h < mh; h++, th += m_nTH )
for ( int w = 0, tw = 0; w < mw; w++, tw += m_nTW ) {

// get the tile index
idx = w + ( h * mw );
n = *m_pnRenderList[ idx ];
idx = *m_Layer[ n ];
if ( idx == -1 )
continue;
pTile = m_pSet->GetTile( idx ).GetData( );

// set the tiles position
pTile->SetScale( m_fSW, m_fSH );
nh = ( n / m_nW ) + 1;
nh = m_nH - nh + 1;
nw = ( ( n ) % m_nW );
pTile->SetPos( -( ( m_fXOff )-( m_nTW * m_fSW * nw ) ),( -m_fYOff )+( ( m_nTH * m_fSH * nh ) ) );

// render the tile
pTile->Render( );
} // end for w
return true;
} // end Render




Here is the Clip function:-


// this clips the map
inline bool Clip( void )
{
// fill in the render list
//float f = 0.0f; // temp
//f = GetTrueHeight( ) / ( ( m_nSH+1.0f ) * m_nTH * m_fSH );
//f = 1.0f / f;
FillRenderList( ( m_fXOff / ( m_fSW * m_nTW ) ), -( ( ( m_fYOff-( GetTrueHeight( )-g_nScreenHeight ) ) / ( m_fSH * m_nTH ) ) ) );
return true;
} // end Clip




FillRenderList:-


// this fills in a render list
inline void FillRenderList( int nX, int nY )
{
int x = 0, y = 0, n = 0, n2 = 0; // temp and counters
int w = m_nSW, h = m_nSH; // used to make sure there are not weird things happening if the map is smaller than the screen

// clamp
if ( nX < 0 ) nX = 0;
if ( nY < 0 ) nY = 0;
if ( ( nX + m_nSW ) > m_nW ) nX = m_nW - m_nSW;
if ( ( nY + m_nSH ) > m_nH ) nY = m_nH - m_nSH;
if ( m_nSW > m_nW ) {
nX = 0;
w = m_nW;
} // end if
if ( m_nSH > m_nH ) {
nY = 0;
h = m_nH;
} // end if

// fill in the render list
for ( y = 0; y < h; y++ ) {
for ( x = 0; x < w; x++ )
{
// get the idxs
n = x + ( y * w );
n2 = x + nX + ( ( nY + y ) * m_nW );

// set the idx
m_pnRenderList[ n ] = n2;
} // end for x
} // end for y
} // end FillRenderList




This render code produces the above screen.

Because the tiles are 1px appart, I thought if I decresased the tile dimentions by 1px, then it would be ok. But in the Render method, when I decrement m_nTW and m_nTH, then increment them at the end of the method,


inline bool Render( void )
{
//....
--m_nTW;
--m_nTH;
//Render the tiles loop
++m_nTW;
++m_nTH;
} // end Render




I get this result:-



Its a zillion times better, just not yet right.

If you look closely, I think the outer pixel of the tiles are missing...

Share this post


Link to post
Share on other sites
Quote:
Original post by rpg_code_master
Here is the rendering code:-


Really, you posted everything but the rendering code. The rendering process is the line or lines of code that put the graphics on the screen, not the various tile-shuffling that leads up to that. Having said that, I expect the problem is in that scaling and positioning. Why not simplify all that and substitute in a few constants to narrow down where the error is introduced. Or print some of the coordinates to a file so that you can analyze whether the tiles are being spaced too far apart, or if the texture coordinates are set incorrectly.

Anyway, you'll want to keep the GL_CLAMP, and I don't see a call for GL_NEAREST_MIPMAP_NEAREST over GL_NEAREST unless you definitely need mipmaps.

Share this post


Link to post
Share on other sites
why not substitute one of the tiles for a tile with a different colour on all four sides of the tile eg red at top green on left blue on right white on bottom that will show if any of the gfx is missing.

try taking shifting by another pixel


inline bool Render( void )
{
//....
--m_nTW;
--m_nTH;
--m_nTW;
--m_nTH
//Render the tiles loop
++m_nTW;
++m_nTH;
++m_nTW;
++m_nTH;
} // end Render


just to see what affect it has.

Share this post


Link to post
Share on other sites
I've had a very similar problem with DirectX, I'd didnt solve it but I another project my mapping was off and it was because my bitmap template of tiles was not a ^2 size (eg, 128x128, 256x256, 512x512 etc). I used the EXACT same code and it worked with ^2 sized template and not without.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement