Understanding oamSet() parameter palette_alpha

Post Reply
ShotgunNinja
Posts: 34
Joined: Tue May 11, 2010 2:29 am

Understanding oamSet() parameter palette_alpha

Post by ShotgunNinja » Fri Aug 20, 2010 8:39 pm

Hello everyone, I've been working in C/C++ with libnds for a short while, and I've been making a C++ wrapper class for accessing Sprite attributes. In order to make it low-footprint, it only stores the sprite index and a boolean for which engine (main or sub) it's on until you modify it, at which point it makes a structure containing the arguments for oamSet() from the information stored in OAM at that index, and uses that information until you call update(), which feeds the information back in via oamSet(). I know this is a pretty bad design, and I'm not personally comfortable with it, but I am doing it this way in the hopes of not breaking the underlying sprite representation by writing information back using any means other than oamSet() calls. My intent is to have working code similar to this example:

Code: Select all

// Get the main OAM state.
OAMState oam(OAMState::MAIN);

// Initialize it to the sprite mapping and extended palette info, and enable it.
oam.init(SpriteMapping_Whatever, false);
oam.enable();

// Grab a handle to sprite 0.
Sprite* pSprite = oam.createSprite(0);

// Set the sprite's X and Y coordinates and graphics data, and update the OAM entry.
pSprite->setX(0);
pSprite->setY(0);
pSprite->setSize(SpriteSize_Whatever);
pSprite->setFormat(SpriteColorFormat_Whatever);
// I'm still deciding upon a format for moving around graphics data using C++, so for now I'm using the old-fashioned way of passing a uint16* around and dmaCopying memory.
uint16* gfx = oam.allocateGraphics(SpriteSize_Whatever, SpriteColorFormat_Whatever);
dmaCopy(someGfxDataPtr, gfx, pSprite->getByteSize());
pSprite->setGraphics(gfx);
pSprite->update();

// Grab the sprite's palette alpha value (as passed to oamSet()), then release the handle.
int alpha = pSprite->getPaletteAlpha(); // <--- How do I get the existing palette alpha value? Where is it located?
delete pSprite;

// Grab a handle to sprite 1.
Sprite* pOther = oam.createSprite(1);

// Set the sprite's palette alpha value to match sprite 0's alpha value, then update it and release the handle.
pOther->setPaletteAlpha(alpha);
pOther->update();
delete pOther;
The problem is, I don't quite understand where the palette_alpha information goes during an oamSet() call, so I can't rightfully read out the previously-set, existing value, while maintaining a low memory footprint AND ensuring similar information access for different instances of Sprite with the same index. Could anyone help me out with this?

zeromus
Posts: 212
Joined: Wed Mar 31, 2010 6:05 pm

Re: Understanding oamSet() parameter palette_alpha

Post by zeromus » Fri Aug 20, 2010 9:08 pm

you can see where it is stored in the source code to oamSet() in sprite.c of libnds

ShotgunNinja
Posts: 34
Joined: Tue May 11, 2010 2:29 am

Re: Understanding oamSet() parameter palette_alpha

Post by ShotgunNinja » Wed Aug 25, 2010 4:08 pm

Oh, I hadn't been able to locate the source for libnds, so I have just been using the precompiled libraries in the default download. Either way, though, I'm now working under the assumption that it stores in the SpriteEntry::palette field.

User avatar
vuurrobin
Posts: 219
Joined: Fri Jul 11, 2008 8:49 pm
Location: The Netherlands
Contact:

Re: Understanding oamSet() parameter palette_alpha

Post by vuurrobin » Wed Aug 25, 2010 4:44 pm

the sourcecode in in the sourceforge svn: https://devkitpro.svn.sourceforge.net/s ... tpro/trunk

as for oamSet()

Code: Select all

//---------------------------------------------------------------------------------
void oamSet(OamState* oam,	int id,  int x, int y, int priority,
 							int palette_alpha, SpriteSize size,SpriteColorFormat format,
 							const void* gfxOffset,
 							int affineIndex,
 							bool sizeDouble, bool hide, bool hflip, bool vflip, bool mosaic) {
//---------------------------------------------------------------------------------

	if(hide) {
		oam->oamMemory[id].attribute[0] = ATTR0_DISABLED;
		return;
	}

	oam->oamMemory[id].shape = SPRITE_SIZE_SHAPE(size);
	oam->oamMemory[id].size = SPRITE_SIZE_SIZE(size);
	oam->oamMemory[id].x = x;
	oam->oamMemory[id].y = y;
	oam->oamMemory[id].palette = palette_alpha;
	oam->oamMemory[id].priority = priority;
	oam->oamMemory[id].hFlip = hflip;
	oam->oamMemory[id].vFlip = vflip;
	oam->oamMemory[id].isMosaic = mosaic;
    oam->oamMemory[id].gfxIndex = oamGfxPtrToOffset(oam, gfxOffset);


    if(affineIndex >= 0 && affineIndex < 32) {
		oam->oamMemory[id].rotationIndex = affineIndex;
		oam->oamMemory[id].isSizeDouble = sizeDouble;
		oam->oamMemory[id].isRotateScale = true;
	} else {
		oam->oamMemory[id].isSizeDouble = false;
		oam->oamMemory[id].isRotateScale = false;
	}

	if(format != SpriteColorFormat_Bmp) {
		oam->oamMemory[id].colorMode = format;
	} else {
        oam->oamMemory[id].blendMode = format;
	}
}

also, looking at your code, your library is allocating memory with new, but expects that the user frees it. that may cause memory leaks.

ShotgunNinja
Posts: 34
Joined: Tue May 11, 2010 2:29 am

Re: Understanding oamSet() parameter palette_alpha

Post by ShotgunNinja » Thu Aug 26, 2010 4:11 pm

All right, thanks a lot! My assumption was correct, then.

Also, I've redone a lot of my code, adding things like a quadrupleton (aka 4 of 'em) pool allocation tracker into the OAMState's private static scope, more appropriately managing sprite allocation tracking, and other such things. I now have a working prototype (a simple raindrop animation), which I will be putting up on my MSOE student webpage in the near future, along with source code and project files.

However, I've come across a bit of a bug, which is similar enough to the original question to be notable here. In my test program, when I disable the rain animation, all the associated sprites are freed and returned to the sprite pool I developed, wiped out via oamClearSprite() EDIT: Might this nuke some vital information concerning graphics alignment in the sprite entry?, and the Sprite pointers are deleted and removed from the list within my Rain object. When the sprites are re-enabled, in order to render, they fetch the SpriteEntry* and convert its fields to oamSet()-appropriate values. However, when converting from SpriteEntry::gfxIndex to a VRAM pointer, I have been using oamGetGfxPtr(oam, pEntry->gfxIndex) behind the scenes, and setting that to the appropriate graphics pointer, but on the sprites for which this is done, the image appears... wrong. It's as if the wrong pointer is retrieved, or the wrong color depth or format is applied. Instead of a straight, light-blue raindrop, there is a scattering of light-blue pixels, as if the image has been spread out incorrectly. I'll put up a screenshot later, if anyone is interested.

The problem may lie within the way I fetch and deduce the SpriteSize value from an ObjSize and ObjShape value, along with a lookup table for the pixel count, or it may be how I get the SpriteColorFormat from the color and blend mode, but I don't believe either of those are the case. I think that the pointer retrieved by oamGetGfxPtr() is not the same as the pointer set within oamSet(). This may be caused by an improper setting of the sprite mapping, but none of my code other than the initialization step modifies the sprite mappings, so I believe that it may be a bug in the way the graphics pointer is being retrieved.

Right now, I am avoiding this issue by manually setting the graphics pointer each time I create a Sprite, but I have had to add a lot of code to deal with this dependency for the time being, and I am uncomfortable with doing so. Also, if my test assertion

Code: Select all

 gfxPtr == oamGetGfxPtr(pOam, oamGfxPtrToOffset(pOam, gfxPtr)); 
is proven false, then this could be a pretty serious bug in libnds. I will be doing some work today to test this, and I will get back with my findings the next time I post.

Thanks again, zeromus and vuurrobin, for your help.

EDIT:
My graphical settings are:
SpriteMapping_1D_128, and extendedPalette = false, for initializing the OAM state.
SpriteSize_32x32, and SpriteColorFormat_256Color, for all graphics allocations.
A PNG source image in 8-bit paletted mode, sized 128w x 32h (a 4x1 grid of 32x32 tiles), using the sprite.grit file taken from the animate_simple example, which works properly in all situations except with the aforementioned bug.
128 (32 * 32) for all graphics memory sizes and frame offsets (ie. the size parameter for dmaCopy() calls between the source image and allocated VRAM, done only within the Rain constructor).
No affine transformations or any other special settings.

I figured this might be important, because of the presence of a bug involving 16x16 sprites on the bug tracker. This bug concerns 32x32 sprites, not 16x16.

zeromus
Posts: 212
Joined: Wed Mar 31, 2010 6:05 pm

Re: Understanding oamSet() parameter palette_alpha

Post by zeromus » Fri Aug 27, 2010 12:40 am

what you're doing is insane, by the way. worst case is 64 bytes per oam for all the oamSet parameters, so 16K for your own copies of the main and sub engine sprites parameters. You need so low footprint that 16K is too much?

anyway you're so far off into the realm of crazy that I can't process all that text describing it your upside down dream world. It is a very fine description though, I will compliment you. Post buildable source code and you might get some eyeballs on it to debug.

As to your specific question, for 1D sprite mapping, the code is very simple:

oamGetGfxPtr:
return &SPRITE_GFX[(gfxOffsetIndex << oam->gfxOffsetStep) >> 1];

oamGfxPtrToOffset:
return ((u32)offset & 0xFFFFF) >> oam->gfxOffsetStep;;

you can tell by inspection whether that value round trips correctly.

ShotgunNinja
Posts: 34
Joined: Tue May 11, 2010 2:29 am

Re: Understanding oamSet() parameter palette_alpha

Post by ShotgunNinja » Fri Aug 27, 2010 3:18 pm

Yeah... A bit more delving revealed that half of what I wrote makes no sense at all. It turns out that what I speculated in my edit was the cause of the bug all along; I'd been using oamClearSprite(), which writes over the SpriteColorFormat entry for the sprite. :oops: I've rewritten it so instead of calling oamClearSprite(), the code manually sets the SpriteEntry's isRotateScale and isHidden fields to hide it. Plus I've reverted to manually resetting the graphics pointer on sprite "allocation", which is more reliable anyway.

Anyway, disregarding that, here are my project files, code and precompiled .nds file for my rain test in its current state. Pressing up/down changes the number of sprites used as raindrops, and pressing the A button toggles the rain on and off.

I'm going to go back and modify a lot of the code, doing things like changing classes into C++ structs, and altering the Sprite class so it doesn't use a temporary attribute structure, and instead sets/gets the sprite values directly to/from OAM memory. Feel free to tell me where I'm screwing up; I'm mostly self-taught, and I make mistakes all the time, so any criticism helps.

EDIT: Also, I'm just grabbing temporary copies of the attributes of individual SpriteEntries, which comes out to about 20-30 bytes per temporary copy (which could be optimized by changing data types, but for now I'm leaving them the same as the parameters to oamSet()). I'm not double-buffering the entire OAM region; that would be insane. In most cases, this reduces OAM fetches, and it allows for deferred updating of OAM memory, to prevent graphical glitches involving VBLANK/HBLANK occurring halfway through updating a sprite's attributes (just in case a frame update takes a bit longer than expected...)
Attachments
rain.zip
(133.8 KiB) Downloaded 389 times

zeromus
Posts: 212
Joined: Wed Mar 31, 2010 6:05 pm

Re: Understanding oamSet() parameter palette_alpha

Post by zeromus » Fri Aug 27, 2010 6:23 pm

What you should be doing is "rendering" data structures for raindrops into oam. If you have a method called Raindrop::RenderToOAM(OamState*, int id) then you're probably doing it right. loop through your sprites, pick an oam slot (probably just go through them sequentially) and set the oam's parameters in the sprite's RenderToOAM method. do this every frame. then, when you want to add bulldogs to run through the rain, chasing wikipe-tan, you can stop rendering some raindrops and render the characters in the correct order (so as to cause them to have the correct priority with respect to the rain.)

the way you're trying to do things is 10x excessively convoluted. I believe your rain demo could be done in about 30 lines of code. maybe all your fluff is helpful for something, but it would take a more complex demo to prove it. at any rate whatever the heck it is you're trying to do with roundtripping things through oam is just silly.

imagine oam is write-only and try again.

ShotgunNinja
Posts: 34
Joined: Tue May 11, 2010 2:29 am

Re: Understanding oamSet() parameter palette_alpha

Post by ShotgunNinja » Fri Aug 27, 2010 8:52 pm

Hey, thanks for the rapid feedback! Let me address each thing in order.
What you should be doing is "rendering" data structures for raindrops into oam. If you have a method called Raindrop::RenderToOAM(OamState*, int id) then you're probably doing it right. loop through your sprites, pick an oam slot (probably just go through them sequentially) and set the oam's parameters in the sprite's RenderToOAM method. do this every frame. then, when you want to add bulldogs to run through the rain, chasing wikipe-tan, you can stop rendering some raindrops and render the characters in the correct order (so as to cause them to have the correct priority with respect to the rain.)
I have the method Sprite::update(), which could easily be modified to take the next OAM ID from the OAMState upon rendering, rather than allocating a fixed OAM value.
the way you're trying to do things is 10x excessively convoluted. I believe your rain demo could be done in about 30 lines of code. maybe all your fluff is helpful for something, but it would take a more complex demo to prove it. at any rate whatever the heck it is you're trying to do with roundtripping things through oam is just silly.

imagine oam is write-only and try again.
I do have a tendency to over-design things, but I have been thinking of the OAM in the wrong light up until now. Now that I know that the order of OAM entries is important for rendering priority, I can redesign my system to be much simpler, making the temporary attribute structure not temporary, merging it with the Sprite class, and redoing the relationship between Sprites and the OAMState.

Also, just a side note: You're obviously far more experienced than I am, but you could probably do better by being a little less blunt. I'm not insane, and I don't live in an upside-down dream world. I'm just a little misguided and inexperienced. Thanks again! :)

WinterMute
Site Admin
Posts: 1845
Joined: Tue Aug 09, 2005 3:21 am
Location: UK
Contact:

Re: Understanding oamSet() parameter palette_alpha

Post by WinterMute » Fri Aug 27, 2010 9:42 pm

Hang around us for long enough and you'll go slightly insane :P
Help keep devkitPro toolchains free, Donate today

Personal Blog

Post Reply

Who is online

Users browsing this forum: No registered users and 14 guests