Trying to make a Dreamcast Mappy playback library

If you have any questions on programming, this is the place to ask them, whether you're a newbie or an experienced programmer. Discussion on programming in general is also welcome. We will help you with programming homework, but we will not do your work for you! Any porting requests must be made in Developmental Ideas.
OneThirty8
Damn Dirty Ape
Damn Dirty Ape
Posts: 5031
https://www.artistsworkshop.eu/meble-kuchenne-na-wymiar-warszawa-gdzie-zamowic/
Joined: Thu Nov 07, 2002 11:11 pm
Location: Saugerties, NY
Has thanked: 0
Been thanked: 0

Re: Trying to make a Dreamcast Mappy playback library

Post by OneThirty8 »

BB Hood wrote: Also Dreamcast SDLMappy port setups SDL the screen like:

Code: Select all

/* Set the video mode */
	screen = SDL_SetVideoMode(Screen_w, Screen_h, Screen_bpp, SDL_HWSURFACE&SDL_DOUBLEBUF|SDL_HWPALETTE);
SDL_HWSURFACE means 'Create the video surface in video memory'. Does that mean the PVR? If it does then when color keying is used

Code: Select all

SDL_SetColorKey(maplpDDSTiles[i], SDL_SRCCOLORKEY,
						*(Uint16 *)maplpDDSTiles[TRANSPBLOCK]->pixels);
How does it work? Since the PVR doesn't support color keying.
SDL_HWSURFACE does not mean the PVR like you're thinking. Your main screen surface can be a hardware surface, but that just means the framebuffer and that's the only surface that can be a hardware surface on the Dreamcast port of SDL (the one that comes with KOS, anyway--chui's port may be different). SDL color keying just doesn't draw those pixels that are the specified RGB value. All drawing is done in software using SDL, so most programs will typically draw the whole background, and then anything that goes in front of the background, and then anything that goes in front of that, so if you specify that the RGB color 255,0,255 as the "transparent" color in a given SDL_Surface and then blit that surface onto your main screen surface, SDL will just leave whatever was already at the given coordinate alone and your background image (if any) will show through.
User avatar
BB Hood
DC Developer
DC Developer
Posts: 189
Joined: Fri Mar 30, 2007 12:09 am
Has thanked: 41 times
Been thanked: 10 times

Re: Trying to make a Dreamcast Mappy playback library

Post by BB Hood »

I see. Thanks. I guess I will have to convert all the graphic data into ARGB1555 in order to use transparency.
User avatar
BlueCrab
The Crabby Overlord
The Crabby Overlord
Posts: 5661
Joined: Mon May 27, 2002 11:31 am
Location: Sailing the Skies of Arcadia
Has thanked: 9 times
Been thanked: 69 times
Contact:

Re: Trying to make a Dreamcast Mappy playback library

Post by BlueCrab »

BB Hood wrote:I see. Thanks. I guess I will have to convert all the graphic data into ARGB1555 in order to use transparency.
And don't forget that if you're planning to use ARGB1555 textures for transparency support, you must draw them either in the Punch-thru list or the Translucent list (or the alpha will be ignored).
User avatar
BB Hood
DC Developer
DC Developer
Posts: 189
Joined: Fri Mar 30, 2007 12:09 am
Has thanked: 41 times
Been thanked: 10 times

Re: Trying to make a Dreamcast Mappy playback library

Post by BB Hood »

I need help again... :( :oops: . For now I am trying to convert RGB565 to ARGB1555 using a (BlackAura's) function from this thread. Here is the stripped down code only dealing with 16 bit images:

Code: Select all

void convert_RGB565_to_ARGB1555(uint16 *src, uint16 *dst, int count, uint16 key) {
    int i;
    for(i = 0; i < count; i++)
    {
        if(*src == key) 
            *dst++ = 0x0000;
        else 
            *dst++ = (*src & 0x001F) | ((*src & 0xFFC0) >> 1) | 0x8000;
        *src++;
        
    }
}

int DCMappy::MapDecodeBGFX (void)
{
    int               i;
    int               bytes;
    uint16              *rawimagedata = NULL; // The raw graphics in whatever format the map is in [8bit;16bit]
    uint16            *swapped = NULL;  
    uint16            *argb1555 = NULL;
	pvr_ptr_t         mapblockgfxpt = NULL; 

        bytes = mapblockwidth*mapblockheight*2; 
        rawimagedata = (uint16 *)malloc(bytes*2);
        argb1555 = (uint16 *)malloc(bytes);
        swapped = (uint16 *)malloc(bytes);
      
    if ((rawimagedata == NULL) || (argb1555 == NULL) || (swapped == NULL)) { maperror = MER_OUTOFMEM; return -1; }
    
    // One block at a time filling up maplpDDSTiles
    for(i = 0; i < mapnumblockgfx; i++) {
            fread(rawimagedata, bytes, 1, mapfilept); 
            mapblockgfxpt = pvr_mem_malloc(bytes); 
            if(mapblockgfxpt==NULL) { maperror = MER_OUTOFMEM; return -1; }
            printf ("Allocated PVR Memory sucessfully\n");
            convert_RGB565_to_ARGB1555((uint16 *)rawimagedata, argb1555, bytes, 0);  // 0 for black to be transparent
            swap_and_load((uint8 *)argb1555, (uint8 *)swapped, mapblockwidth, mapblockheight);
            pvr_txr_load_ex((void *)swapped, mapblockgfxpt, mapblockwidth, mapblockheight, PVR_TXRLOAD_16BPP);
     
        
            // Setup a KOS Platform independent image struct to hold each gfx block
            maplpDDSTiles[i] = (kos_img_t *)malloc(sizeof(kos_img_t));  
            maplpDDSTiles[i]->w = mapblockwidth;   
            maplpDDSTiles[i]->h = mapblockheight;
            maplpDDSTiles[i]->data = (void *)mapblockgfxpt;
            maplpDDSTiles[i]->fmt = PVR_TXRFMT_ARGB1555; 
            maplpDDSTiles[i]->byte_count = bytes;
         
        mapblockgfxpt = NULL;
    } // End of mapnumblockgfx
    free(rawimagedata);
    free(argb1555);
    free(swapped);

	return 0;
}
When I don't use convert_RGB565_to_ARGB1555() everything works like it did before... its when I use it that I get treated to this in NullDC:

A0: Dreamcast Controller (01000000: Controller)
pvr: enabling vertical scaling for non-VGA
MapLoad Function Start
Thread 1/8c011134 allocated 2048 bytes at a4396720
Allocated PVR Memory sucessfully
Thread 1/8c011134 allocated 2048 bytes at a4396f40
Allocated PVR Memory sucessfully
Thread 1/8c011134 allocated 2048 bytes at a4397760
Allocated PVR Memory sucessfully
Thread 1/8c011134 allocated 2048 bytes at a4397f80

*** ASSERTION FAILURE ***
Assertion "next->prev_size == sz" failed at malloc.c:3225 in `do_check_free_chun
k'

-------- Stack Trace (innermost first) ---------
8c0156c6
8c015ae2
00000005
(invalid frame pointer)
-------------- End Stack Trace -----------------
arch: shutting down kernel

So I know something bad happens in the function. I know his function is correct but maybe my input parameters aren't. Any ideas?

Sorry I need so much help with this project :| .
User avatar
BlueCrab
The Crabby Overlord
The Crabby Overlord
Posts: 5661
Joined: Mon May 27, 2002 11:31 am
Location: Sailing the Skies of Arcadia
Has thanked: 9 times
Been thanked: 69 times
Contact:

Re: Trying to make a Dreamcast Mappy playback library

Post by BlueCrab »

You should definitely be doing the swap before you convert to ARGB1555, otherwise you'll screw up the colors, but that shouldn't cause the problem you're having there. Also, you probably should combine the swap function and the conversion function, but that's more of an optimization thing. Also, you seem to be allocating double the memory you need in the rawimagedata, but once again, that shouldn't cause your problem there.

What it looks like is happening is that the PVR's heap is being corrupted somehow. I don't see where it could be happening off the top of my head, so I'm kinda at a loss as to what's going wrong...
User avatar
BB Hood
DC Developer
DC Developer
Posts: 189
Joined: Fri Mar 30, 2007 12:09 am
Has thanked: 41 times
Been thanked: 10 times

Re: Trying to make a Dreamcast Mappy playback library

Post by BB Hood »

I guess I am going to have to mess around with the source to find out what is causing that in game error. Also does the following function, which is supposed to convert 8 bpp to ARGB1555, look correct?

Code: Select all

void convert_8BBP_to_ARGB1555(uint8 *image, uint16 *dst, uint8 *palette, int byte_count, uint16 ColorKey) {
    
    int i, r, g, b, v;
    
	for (i=0; i < byte_count; i++) {
        
		v = image[i];
		r = palette[v*3+0];
		g = palette[v*3+1];
		b = palette[v*3+2];
			
		v =   (((r >> 3) & 0x1f) << 10)
			| (((g >> 3) & 0x1f) << 5)
			| (((b >> 3) & 0x1f) << 0);
		
		if(v == (int)ColorKey) {
            v = 0x0000;
        } else {
            v = v | 0x8000;
        } 
		dst[i] = v;
	}
}
Thanks for letting me know it's a PVR heap problem.
BlackAura
DC Developer
DC Developer
Posts: 9951
Joined: Sun Dec 30, 2001 9:02 am
Has thanked: 0
Been thanked: 1 time

Re: Trying to make a Dreamcast Mappy playback library

Post by BlackAura »

I think I know what that is. That assertion is from the memory allocator - it indicates that something's corrupted the heap. Usually, this happens when you allocate a buffer of one size, and use it as if it were a different size. KOS allocates additional padding around each memory allocation, and checks it to make sure it hasn't been overwritten.

Interestingly, there's a recent thread discussing this feature in OpenBOR. It uses more memory, but it's intended to catch exactly this kind of problem.

Anyway, here's your problem:

Code: Select all

convert_RGB565_to_ARGB1555((uint16 *)rawimagedata, argb1555, bytes, 0);  // 0 for black to be transparent
The "count" argument in that function isn't the number of bytes - it's the number of pixels. Instead of converting a single tile of 2048 bytes (I assume it's a 32x32 image?), it's converting 2048 pixels (4096 bytes).

You could also avoid having to allocate the two additional buffers. First, make the convert function handle byte-swapping as well as format conversion. Then, call the convert function in-place, like this:

Code: Select all

// Convert image data from big-endian RGB565 to little-endian ARGB1555
void swap_convert_RGB565_to_ARGB1555(uint16 *src, uint16 *dst, int count, uint16 key) {
	int i;
	for(i = 0; i < count; i++)
	{
		// Swap the bytes while we're reading them
		// You'd probably have to include endian.h for this to work
		uint16 src_colour = *src++;
		uint16 colour = __byte_swap_short(src_colour);

		// Convert from RGB565 to ARGB1555
		if(colour == key)
			*dst++ = 0x0000;
		else
			*dst++ = (colour & 0x001F) | ((colour & 0xFFC0) >> 1) | 0x8000;
	}
}

pixels = mapblockwidth * mapblockheight;
bytes = pixels * 2; 

swap_convert_RGB565_to_ARGB1555(rawimagedata, rawimagedata, pixels, 0);
The 8-bit to ARGB1555 conversion function looks about right, although you really don't need it. From what I remember of Mappy's format, the 8-bit graphics share the same colour palette. The PVR supports 8-bit paletted textures, and they're not particularly difficult to handle.
User avatar
BB Hood
DC Developer
DC Developer
Posts: 189
Joined: Fri Mar 30, 2007 12:09 am
Has thanked: 41 times
Been thanked: 10 times

Re: Trying to make a Dreamcast Mappy playback library

Post by BB Hood »

Thanks BlackAura. Unfortunately

Code: Select all

__byte_swap_word(src_colour);
gives me a compiler error: error: impossible constraint in `asm'.

So instead I used this function:

Code: Select all

unsigned short int byteswaps(unsigned short int i)
{
    unsigned short int j;
	j = 0; 
    j = i&0xFF; 
    j <<= 8; 
    i >>= 8; 
    j |= i&0xFF; 
    return j;
}
and it works! Still would like the optimized asm function to work though :^/

If I still use 8-bit images can I still have transparency with them as well?
BlackAura
DC Developer
DC Developer
Posts: 9951
Joined: Sun Dec 30, 2001 9:02 am
Has thanked: 0
Been thanked: 1 time

Re: Trying to make a Dreamcast Mappy playback library

Post by BlackAura »

Yeah, you can have 8bpp images with transparency. You just need to make sure you're using a palette mode that supports an alpha channel (either ARGB1555 or ARGB8888 would be best), and that you set one of the colours to transparent.

I wouldn't bother about optimizing the byte swapping. It's not really going to make much difference in terms of performance. You'd probably speed the loading time up more noticeably by processing all of the graphics data in one go, rather than reading one tile at a time. That's more to do with the CD drive than anything else - each read operation requires the drive to seek to the correct location on the disc first, which can take longer than reading the data does. Reading everything in one go (or even the entire file) would be noticeably faster.

Assuming, of course, that the map loader is taking too long anyway. The format's pretty simple, so I don't think it's going to be a problem.
User avatar
BB Hood
DC Developer
DC Developer
Posts: 189
Joined: Fri Mar 30, 2007 12:09 am
Has thanked: 41 times
Been thanked: 10 times

Re: Trying to make a Dreamcast Mappy playback library

Post by BB Hood »

Quzar wrote:You could speed it up a bit by making the header only compile into a context if the values change.
I am having trouble with this. It makes a lot of sense not to compile the header again if you are going to use the same texture which is A LOT in Maps. I wrote something like this:

Code: Select all

Private Members of DCMappy Class:
pvr_poly_cxt_t cxt;
pvr_poly_hdr_t hdr;
kos_img_t       * Copy;   

void DCMappy::MapDrawBlockTR(kos_img_t *block, Block_Rect *Coordinates) {
     
    pvr_vertex_t vert;
    
    if(block != Copy) {
    pvr_poly_cxt_txr(&cxt, PVR_LIST_TR_POLY, block->fmt, block->w, block->h, (pvr_ptr_t)block->data, PVR_FILTER_NONE);
    pvr_poly_compile(&hdr, &cxt);
    Copy = block;
    }
    pvr_prim(&hdr, sizeof(hdr));

    vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f);    
    vert.oargb = 0;
    vert.flags = PVR_CMD_VERTEX;
    
    vert.x = Coordinates->x;
    vert.y = Coordinates->y;
    vert.z = 1;
    vert.u = 0.0;
    vert.v = 0.0;
    pvr_prim(&vert, sizeof(vert));
    
    vert.x = Coordinates->w + Coordinates->x;
    vert.u = 1.0;
    pvr_prim(&vert, sizeof(vert));
    
    vert.x = Coordinates->x;
    vert.y = Coordinates->h + Coordinates->y;
    vert.u = 0.0;
    vert.v = 1.0;
    pvr_prim(&vert, sizeof(vert));
    
    vert.x = Coordinates->w + Coordinates->x;
    vert.u = 1.0;
    vert.flags = PVR_CMD_VERTEX_EOL;
    pvr_prim(&vert, sizeof(vert));
}
It doesn't work but looks right to me. Am I missing something? I admit I don't know everything about what goes on behind the scenes when using functions like pvr_poly_cxt_txr(), pvr_poly_compile(), and pvr_prim(). Maybe its just the emulator but I can't test it right now on a dreamcast.

I'm mostly done with DCMappy but I haven't tested it with hexagonal and isometric maps yet. Just trying to optimize what I have so far.
OneThirty8
Damn Dirty Ape
Damn Dirty Ape
Posts: 5031
Joined: Thu Nov 07, 2002 11:11 pm
Location: Saugerties, NY
Has thanked: 0
Been thanked: 0

Re: Trying to make a Dreamcast Mappy playback library

Post by OneThirty8 »

BB Hood wrote:
Quzar wrote:You could speed it up a bit by making the header only compile into a context if the values change.
I am having trouble with this. It makes a lot of sense not to compile the header again if you are going to use the same texture which is A LOT in Maps. I wrote something like this:

Code: Select all

Private Members of DCMappy Class:
pvr_poly_cxt_t cxt;
pvr_poly_hdr_t hdr;
kos_img_t       * Copy;   

void DCMappy::MapDrawBlockTR(kos_img_t *block, Block_Rect *Coordinates) {
     
    pvr_vertex_t vert;
    
    if(block != Copy) {
    pvr_poly_cxt_txr(&cxt, PVR_LIST_TR_POLY, block->fmt, block->w, block->h, (pvr_ptr_t)block->data, PVR_FILTER_NONE);
    pvr_poly_compile(&hdr, &cxt);
    Copy = block;
    }
(snip...)
It doesn't work but looks right to me. Am I missing something? I admit I don't know everything about what goes on behind the scenes when using functions like pvr_poly_cxt_txr(), pvr_poly_compile(), and pvr_prim(). Maybe its just the emulator but I can't test it right now on a dreamcast.

I'm mostly done with DCMappy but I haven't tested it with hexagonal and isometric maps yet. Just trying to optimize what I have so far.
Unless I missed something somewhere, "block" is a kos_img_t struct stored in main memory. Therefore, you can't cast the pixel data as a pvr_ptr_t, because the pixel data is in main memory and not in PVR memory. What you need is a way to identify the pvr_ptr_t that is associated with "block," and use that in place of "(pvr_ptr_t)block->data" in your "pvr_poly_cxt_txr" line. Also, the width and height specified in your "pvr_poly_cxt_txr" must be powers of 2 that refer to the width and height of the PVR texture--Mappy tiles may already fit this requirement, but if your image is 60 pixels wide and 31 pixels tall, these will need to still be numbers like 64 and 32 respectively, and then you will need to just your u and v coordinates appropriately.

Some of this stuff may be obvious, but figured I'd give as complete an answer as I know how...
User avatar
BB Hood
DC Developer
DC Developer
Posts: 189
Joined: Fri Mar 30, 2007 12:09 am
Has thanked: 41 times
Been thanked: 10 times

Re: Trying to make a Dreamcast Mappy playback library

Post by BB Hood »

Oh I know. The block->data does point to PVR video memory but since kos_img_t struct wants its data as a void * I casted it to that when I loaded the tiles one by one. In my draw functions I just recast it back. Here is my graphics decoding function if that didn't make sense:

Code: Select all

int DCMappy::MapDecodeBGFX (void) {
    
    int               i;
    int               bytes;
    uint16            *rawimagedata = NULL; // The raw graphics in whatever format the map is in [8bit;16bit]
	pvr_ptr_t         mapblockgfxpt = NULL; 
	
    // Allocated raw image data memory(one block at a time) depending on the mapdepth[8:1, 16:2] 
    if(mapdepth == 8) {
        bytes = mapblockwidth*mapblockheight;        
        rawimagedata = (uint16 *)malloc(bytes);
        pvr_set_pal_format(PVR_PAL_ARGB8888);
    } else if(mapdepth == 16) {
        bytes = mapblockwidth*mapblockheight*2; 
        rawimagedata = (uint16 *)malloc(bytes);
    } else {
        #ifdef DEBUG_MAP   
        printf("MapDecodeBGFX(): Map Depth %d is not supported.", mapdepth);
        #endif
        return -1;
    } 
    
    if (rawimagedata == NULL) { 
        #ifdef DEBUG_MAP   
        printf("MapDecodeBGFX(): Ran out of memory to allocate for rawimagedata" );
        #endif
        return -1; 
    }
    
    // One block at a time filling up maplpDDSTiles
    for(i = 0; i < mapnumblockgfx; i++) {
        
         // Allocated PVR Memory(one block at a time) depending on the mapdepth 
        if(mapdepth == 8) {
            fread(rawimagedata, bytes, 1, mapfilept);      
            mapblockgfxpt = pvr_mem_malloc(bytes);
            if(mapblockgfxpt==NULL) {  
                #ifdef DEBUG_MAP   
                printf("MapDecodeBGFX(): Ran out of TEXTURE memory to allocate for mapblockgfxpt" );
                #endif
                return -1; 
            }
            pvr_txr_load_ex((void *)rawimagedata, mapblockgfxpt, mapblockwidth, mapblockheight, PVR_TXRLOAD_8BPP);
        } else if(mapdepth == 16) {
            fread(rawimagedata, bytes, 1, mapfilept); 
            mapblockgfxpt = pvr_mem_malloc(bytes);
            if(mapblockgfxpt==NULL) {  
                #ifdef DEBUG_MAP   
                printf("MapDecodeBGFX(): Ran out of TEXTURE memory to allocate for mapblockgfxpt" );
                #endif
                return -1; 
            }
            swap_convert_RGB565_to_ARGB1555(rawimagedata, rawimagedata, mapblockwidth*mapblockheight, ColorKey);
            pvr_txr_load_ex((void *)rawimagedata, mapblockgfxpt, mapblockwidth, mapblockheight, PVR_TXRLOAD_16BPP);
        }  
        
        // Setup a KOS Platform independent image struct to hold each gfx block
        maplpDDSTiles[i] = (kos_img_t *)malloc(sizeof(kos_img_t));  
        maplpDDSTiles[i]->w = mapblockwidth;   
        maplpDDSTiles[i]->h = mapblockheight;
        maplpDDSTiles[i]->data = (void *)mapblockgfxpt;
        maplpDDSTiles[i]->byte_count = bytes;

        if(mapdepth == 8) {
            maplpDDSTiles[i]->fmt = PVR_TXRFMT_PAL8BPP;           
        } else if(mapdepth == 16) {
            maplpDDSTiles[i]->fmt = PVR_TXRFMT_ARGB1555; 
        }  
        mapblockgfxpt = NULL;
    } // End of mapnumblockgfx
    
    for(i=0; i<mapnumblockstr; i++) {
		((BLKSTR *) mapblockstrpt)[i].bgoff /= (mapblockwidth*mapblockheight*((mapdepth+1)/8));
		((BLKSTR *) mapblockstrpt)[i].fgoff /= (mapblockwidth*mapblockheight*((mapdepth+1)/8));
		((BLKSTR *) mapblockstrpt)[i].fgoff2 /= (mapblockwidth*mapblockheight*((mapdepth+1)/8));
		((BLKSTR *) mapblockstrpt)[i].fgoff3 /= (mapblockwidth*mapblockheight*((mapdepth+1)/8));
		
		#ifdef DEBUG_MAP
		printf ("B%d: %d,%d,%d,%d\n", i, ((BLKSTR *) mapblockstrpt)[i].bgoff,((BLKSTR *) mapblockstrpt)[i].fgoff,((BLKSTR *) mapblockstrpt)[i].fgoff2 ,((BLKSTR *) mapblockstrpt)[i].fgoff3 );
		#endif
	}
    
    free(rawimagedata);

	return 0;
}
The thing I am trying to achieve is to not use the following functions unless I want to draw a sprite I haven't previously drawn before (the one right before).

Code: Select all

pvr_poly_cxt_txr(&cxt, PVR_LIST_TR_POLY, block->fmt, block->w, block->h, (pvr_ptr_t)block->data, PVR_FILTER_NONE);
    pvr_poly_compile(&hdr, &cxt);
I think dcmappy draws row by row so lets say I have a row of grass tiles in my map. I would only go through

Code: Select all

if(block != Copy) {
    pvr_poly_cxt_txr(&cxt, PVR_LIST_TR_POLY, block->fmt, block->w, block->h, (pvr_ptr_t)block->data, PVR_FILTER_NONE);
    pvr_poly_compile(&hdr, &cxt);
    Copy = block;
    }
once and use the same context and header for that whole row. Is that possible to do or no? Is my way of going about it the right way?

EDIT: NVM got it to work with what I explained/did above.
BlackAura
DC Developer
DC Developer
Posts: 9951
Joined: Sun Dec 30, 2001 9:02 am
Has thanked: 0
Been thanked: 1 time

Re: Trying to make a Dreamcast Mappy playback library

Post by BlackAura »

Unless you have a particular reason to keep all those kos_img_t structs around, you could just pre-compile all the polygon headers while loading the map.

So, instead of this bit:

Code: Select all

        maplpDDSTiles[i] = (kos_img_t *)malloc(sizeof(kos_img_t)); 
        maplpDDSTiles[i]->w = mapblockwidth;   
        maplpDDSTiles[i]->h = mapblockheight;
        maplpDDSTiles[i]->data = (void *)mapblockgfxpt;
        maplpDDSTiles[i]->byte_count = bytes;

        if(mapdepth == 8) {
            maplpDDSTiles[i]->fmt = PVR_TXRFMT_PAL8BPP;           
        } else if(mapdepth == 16) {
            maplpDDSTiles[i]->fmt = PVR_TXRFMT_ARGB1555;
        }  
You'd have something like:

Code: Select all

pvr_poly_hdr_t *tile_headers;
tile_headers = malloc(sizeof(pvr_poly_hdr_t) * mapnumblockgfx);
int textureFormat = (mapdepth == 8) ? PVR_TXRFMT_PAL8BPP : PVR_TXRFMT_ARGB1555;

// ....

pvr_poly_cxt_t cxt;
pvr_poly_cxt_txr(&cxt, PVR_LIST_TR_POLY, textureFormat, mapblockwidth, mapblockheight, mapblockgfxpt, PVR_FILTER_NONE);
pvr_poly_compile(&tile_headers[i], &cxt);
There's really no need to recompile the context every time it's used, unless you've changed some of the settings (texture base address, size, format, display list, blending mode, or whatever), so you might as well precompile them all.

Just as a quick explanation - the pvr_poly_hdr_t structure is a data structure used by the PVR hardware. It basically signals the beginning of a drawing command, and contains all of the state required by that drawing command - the active display list, texture information, blending modes, texture environment, and loads more. It's a small, 32-byte long chunk of data in a format that's easily readable by the hardware, but kind of difficult to manipulate in software. You need only send this if you want to change rendering state. If you're rendering two tiles with the same texture, as in your example, you don't need to send the second header at all.

The pvr_poly_cxt_t structure is a much larger structure containing all the possible options that can be set in a pvr_poly_hdr_t structure, but in a format that's much easier to manipulate. The pvr_poly_cxt functions populate a pvr_poly_cxt_t structure with some default settings. You can then change any other settings you like (blending mode on TR polygons is probably the best example). The pvr_poly_compile function converts the pvr_poly_cxt_t structure into a pvr_poly_hdr_t structure.

Once you have a compiled pvr_poly_hdr_t structure, you can just send it off to the PVR whenever you need to set that rendering state again. Unless you set a different rendering state, the pvr_poly_hdr_t structure will always be the same for a given texture.
User avatar
BB Hood
DC Developer
DC Developer
Posts: 189
Joined: Fri Mar 30, 2007 12:09 am
Has thanked: 41 times
Been thanked: 10 times

Re: Trying to make a Dreamcast Mappy playback library

Post by BB Hood »

WOW.. I would have never thought of that. I will definitely try it out. Thanks for that and the explanation.

EDIT: Works great!
BlackAura
DC Developer
DC Developer
Posts: 9951
Joined: Sun Dec 30, 2001 9:02 am
Has thanked: 0
Been thanked: 1 time

Re: Trying to make a Dreamcast Mappy playback library

Post by BlackAura »

You're welcome.
User avatar
BB Hood
DC Developer
DC Developer
Posts: 189
Joined: Fri Mar 30, 2007 12:09 am
Has thanked: 41 times
Been thanked: 10 times

Re: Trying to make a Dreamcast Mappy playback library

Post by BB Hood »

Alright its almost done but I have run into a little problem. Up until now i have been scrolling maps that are bigger than the 640x480. When I load something smaller it wont scroll because well it just displays the whole map instead. So I wanted to test scrolling smaller maps by making DCMappy only output on a 320x240 screen and this is what I got. The problem is how it scrolls. I want the screen to stay fixed and the edges of the displayed map to show tiles that are not totally on the screen. What I mean by that is if you look at the video it either draws a whole tile or none at all(not even halve of one). I know this has to do with the drawing function:

Code: Select all

int DCMappy::MapDrawBG (void) {

    if(pvr_state.list_reg_open != PVR_LIST_OP_POLY) {
        #ifdef DEBUG_MAP 
        printf("MapDrawBG() can only be called after pvr_list_begin(PVR_LIST_OP_POLY) and NOT after pvr_list_begin(PVR_LIST_TR_POLY).\n"); 
        #endif 
        return 0;
    }
    
	int			i, j, mapvclip, maphclip;
    int         mapxo, mapyo;
    int 		numtile, numanim;
    ANISTR	    * anim;
	Block_Rect  TileDestRect;
	
	mapxo=XPosition/mapblockwidth;
	mapyo=YPosition/mapblockheight;
	maphclip=XPosition%mapblockwidth;
	mapvclip=YPosition%mapblockheight;	
	
	for(i=0; i<((Screen_h/mapblockheight));i++)
		for(j=0; j<((Screen_w/mapblockwidth));j++)
		{	
			TileDestRect.y	= i*mapblockheight-mapvclip + MMOY;
			TileDestRect.x	= j*mapblockwidth-maphclip + MMOX;
			TileDestRect.h	= mapblockheight;
			TileDestRect.w	= mapblockwidth;
			
			numtile = mappt[mapxo+j+((mapyo+i)*mapwidth)]; 
			
			if (((mapxo+j)<mapwidth) && ((mapyo+i)<mapheight))
			    if (numtile>=0)
					MapDrawBlock(&maplpDDSTiles[((BLKSTR *) (mapblockstrpt+numtile))->bgoff], &TileDestRect);  
				else
				{
					anim = (ANISTR *) (mapanimstrendpt + numtile);
					numanim = mapanimstrpt[ANDTSize+anim->ancuroff+1]&0XFF;
					numanim <<= 8;
					numanim |= mapanimstrpt[ANDTSize+anim->ancuroff]&0XFF;
					MapDrawBlock(&maplpDDSTiles[((BLKSTR *) (mapblockstrpt+numanim))->bgoff], &TileDestRect); 
				}
		}
	
	return 0;
}
I just took this from SDLMappy and replaced my drawing tile function :oops: . I have thought of two things on how to solve this problem. Basically draw an 512x256 image (say a black image with an 320x240 transparent piece) over the map which is basically ignoring the problem and anybody who uses DCMappy (if at all :lol: ) could implement themselves outside of editing DCMappy. The other is to somehow draw(might be the wrong word here) the map on a 512x256 texture and clipping it with the U and V coordinates which I don't know can be done and if it is then it is probably very time consuming and wastes pvr memory. Any help or a nudge in the right direction would be greatly appreciated.
OneThirty8
Damn Dirty Ape
Damn Dirty Ape
Posts: 5031
Joined: Thu Nov 07, 2002 11:11 pm
Location: Saugerties, NY
Has thanked: 0
Been thanked: 0

Re: Trying to make a Dreamcast Mappy playback library

Post by OneThirty8 »

I think you need to do better clipping than you do with the SDL code. I'm experiencing something similar with some 3D code I'm working on. Basically, what is happening is you have some overdraw beyond the edge of your clipping window while scrolling. Also, test for maphclip and mapvclip to see if they're a value other than 0. If they are, add another row/column to your drawing loops.

Clipping the x and y coordinates of your edge tiles isn't hard. Just specify a clipping window and check to see if your tiles have any vertices with x values that are greater than the right edge, less than the left edge, and any y values greater than the bottom edge or less than the top edge. You don't have to test every tile, because anything below the top row but above the bottom row is going to be drawn in full, except the left and right edges (but again, anything in between is going to get drawn in full).

The only thing to take into account here is that your u and v values are going to change for the clipped tiles, so you will need some code to account for that. Since your tiles are all the same size and in a 2d environment, such code should be pretty easy to write. If it's not worth the bother, just draw blank rectangles at the edges like you said. I'd use 4 opaque untextured polys since they're easier for the 3d hardware to draw than a transparent poly with a hole in the middle. And then just test for maphclip and mapvclip and be done with the headache.
BlackAura
DC Developer
DC Developer
Posts: 9951
Joined: Sun Dec 30, 2001 9:02 am
Has thanked: 0
Been thanked: 1 time

Re: Trying to make a Dreamcast Mappy playback library

Post by BlackAura »

Unless you're doing 3D, don't bother clipping. The PVR can handle that just fine in hardware. Just draw every tile that's even partially on-screen. What you appear to be doing at the moment is only drawing tiles that are completely on-screen.

Essentially, instead of looping through by screen coordinates, calculate the extents of the map in map coordinates, and loop through that instead. Something like this:

Code: Select all

int x_min = XPosition / mapblockwidth;
int x_max = x_min + (Screen_w / mapblockwidth);
int y_min = YPosition / mapblockheight;
int y_max = y_min + (Screen_h / mapblockwidth);

for(i = y_min; i <= y_max; i++)
{
    for(j = x_min; j < x_max; j++)
    {
        // Pretty much the same drawing code goes here
    }
}
This way, it should render any tiles that are even partially on-screen. You probably want to clip x_min and y_min to zero, and x_max and y_max to the size of the map.
User avatar
BB Hood
DC Developer
DC Developer
Posts: 189
Joined: Fri Mar 30, 2007 12:09 am
Has thanked: 41 times
Been thanked: 10 times

Re: Trying to make a Dreamcast Mappy playback library

Post by BB Hood »

Thanks guys. I understand what OneThirty8 is saying but BlackAura I am confused.
BlackAura wrote:Unless you're doing 3D, don't bother clipping. The PVR can handle that just fine in hardware.
How can the PVR handle clipping in hardware if I don't tell it where to clip? My drawing block function will only draw the whole tile.

OneThirty8 wrote:Also, test for maphclip and mapvclip to see if they're a value other than 0. If they are, add another row/column to your drawing loops.
Doesn't this (^) solve the problem of not drawing partial tiles?
OneThirty8
Damn Dirty Ape
Damn Dirty Ape
Posts: 5031
Joined: Thu Nov 07, 2002 11:11 pm
Location: Saugerties, NY
Has thanked: 0
Been thanked: 0

Re: Trying to make a Dreamcast Mappy playback library

Post by OneThirty8 »

BB Hood wrote:Thanks guys. I understand what OneThirty8 is saying but BlackAura I am confused.
He basically said the same thing I said, except that clipping is not necessary. But he gave you an efficient means of doing it, whereas I did not. So what you basically want to do is just get the total height in tiles and total width in tiles using BlackAura's code, which will give you the extra row or column you will often need, and just draw your scene the same way you have been. If you're not drawing on the whole screen, just use black rectangles to cover up the extra portions that get drawn beyond the edge of your viewing window. That's what I would do. You should be able to use untextured polygons for that.
Post Reply