This is the start of a fairly simple graphics tutorial, aimed at the Dreamcast. I'll probably do a better version later, with a couple of examples, diagrams, decent formatting, and stuff like that, but this is just a quick draft of the first part.
It's aimed mostly at people who have a little bit of graphics knowledge, and have already got all the DC development kit set up, and know how to compile stuff. Anyway, here it goes. Any feedback would be appreciated.
Setting up a video mode
The first thing you should do when doing any kind of graphics programming is set up a video mode. On the Dreamcast, this isn't really that important - KallistiOS will automatically set the video hardware up into 640x480, 16 bit 60Hz mode. However, it's still a good idea to set the video mode manually, in case they change something.
This couldn't really be much simpler - we just need to call the function vid_set_mode, giving it our desired resolution and colour mode, and it'll do the rest:
display_mode can be one of:
DM_320x240 320 x 240, 60Hz (or VGA)
DM_640x480 640 x 480, 60Hz (or VGA)
DM_800x608 800 x 608, 60Hz (or VGA)
DM_256x256 256 x 256, 60Hz (or VGA)
DM_768x480 768 x 480, 60Hz (or VGA)
DM_768x576 768 x 576, 60Hz (or VGA)
DM_640x480_PAL_IL 640 x 480, 50Hz
DM_256x256_PAL_IL 256 x 256, 50Hz
DM_768x480_PAL_IL 768 x 480, 50Hz
DM_768x576_PAL_IL 768 x 576, 50Hz
The 60Hz modes will always be 60Hz, even on a PAL Dreamcast. The 50Hz modes will always be 50Hz, even on an NTSC Dreamcast. While most PAL TVs can display a 60Hz signal, some can not, and virtually no NTSC TVs can display a 50Hz signal. So it's usually safe to use the 60Hz modes, but you might want to put in an option to use 50Hz on PAL Dreamcasts. I'll go into more detail on that later.
One rather strange omission that is worth taking note of - there is no 320x240 50Hz mode. I don't know why this is, because it's certainly not too difficult to do - it was just never included in KOS. If anyone really needs it, there is a patch to add that mode (DM_320x240_PAL) to KOS 1.2.0
pixel_mode can be one of:
PM_RGB555 15-bit (xRRRRRGGGGGBBBBB)
PM_RGB565 16-bit (RRRRRGGGGGGBBBBB)
PM_RGB888 24-bit (RRRRRRRR GGGGGGGG BBBBBBBB)
The best mode to use on the Dreamcast is RGB565 - it provides fairly good colour quality, it's significantly faster than 24-bit mode, and it uses less memory. Just trust me on this - it's the best mode to use for pretty much everything that we're going to be doing.
So, to initialise the screen to 640x480 60Hz, in RGB565 colour mode, we'd use the following line:
vid_set_mode(DM_640x480, PM_RGB565);RGB565 colour mode
In RGB565 mode, the three colour components are packed into two bytes. The upper 5 bits contain the red data, the next 6 contain the green data, and the last 5 contain the blue data. Red and blue range from 0 to 31, and green ranges from 0 to 63. The reason that green is given more space is because the human eye is more sensitive to green.
Colours on a computer are typically represented using three bytes - one each for red, green and blue, ranging from 0 to 255. There's an easy way to convert from this format to the 16-bit RGB565 format that we need - we just use a macro:
#define PACK_PIXEL(r, g, b) ( ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3) )
That macro uses two AND operations to mask out the unnecessary bits of the red and green components, bit shifts the values to get them into the correct place, and ORs them together. For some strange reason, that macro isn't included anywhere in KOS.Drawing a single pixel
No matter how complex the graphics we're trying to make, no matter how many complex formulae and techniques we're using, no matter how long it's taken to draw our artwork, ALL 2D graphics come down to this - drawing a single pixel. For many things, where we need to plot multiple pixels at once, we can modify this routime to make it faster, but it'll still just be a variation on this. So how exactly do we draw a single pixel?
On the Dreamcast, the image currently being displayed on the screen is stored in an area of video memory called the framebuffer. The pixels are stored in order, from left to right, then from top to bottom, each as a single 16-bit value. So in order to find the location in the framebuffer of a single pixel, we can use this formula:
location = x_coordinate + (y_coordinate * width)
So, assuming a resolution of 640x480, we can do this:
location = x + (y * 640)
Now all we need to know is where the framebuffer is. KOS provides us with a pointer to this location, named vram_s. So all we need to do the get the location in memory of a single pixel on the screen is:
vram_s[x + (y * 640)]
From there, it should be easy to see how to draw a single pixel - we just need to set the value of that location in memory to the colour we want the pixel to be:
vram_s[x + (y * 640)] = PACK_PIXEL(r, g, b);Clipping
Now that all works fine, until we come across one additional problem - clipping. What happens if we were to try to write to negative X or Y coordinates, or write off the bottom or the right of the screen? We'd be writing either to the wrong part of the screen, or an area of memory that we aren't allowed to write to. We could cause all kinds of problems. So we just don't do it - we have to add some checking to make sure we aren't trying to draw outside the screen:
if((x >= 0) && (x < 640) && (y >= 0) && (y < 480))
vram_s[x + (y * 640)] = PACK_PIXEL(r, g, b);
Now we have a safe, effective way to draw a single pixel on the screen. Of course, it'd be more convenient as a macro:
#define DRAW_PIXEL(x, y, c) \
if((x >= 0) && (x < 640) && (y >= 0) && (y < 480)) \
vram_s[x + (y * 640)] = c;
Now, to draw a single pixel, we can just work out the colour we want to set it to using PACK_PIXEL, and the use DRAW_PIXEL to draw it.