Dreamcast textures
Posted: Fri Apr 25, 2014 9:20 pm
I have done a lot of research on the topic of Dreamcast textures lately, and I've made some major discoveries in the process. The top one being that any format can be compressed, including normal maps and paletted textures.
I've created a tool that can convert images to any format supported by the Dreamcast. You can find it here:
https://github.com/tvspelsfreak/texconv
Some key features:
* Full support for all Dreamcast formats.
* Supports compression of all formats.
* Support for strided textures.
* Mipmaps can be generated automatically or be input by the user, or any mix of the two.
* Can generate previews of all texture formats. No need to load the texture on hardware to see what it looks like.
* Can generate images that visualize codebook usage for compressed textures.
The readme has a good deal of information, so I'll include it here as well. I'm sure there's a lot of stuff I forgot to mention but I'll fill in the blanks later. I haven't released a loader either, but the file format is pretty simple and documented in the readme.
I doubt it's possible to try out compressed bumpmaps or compressed paletted textures in emulators since I haven't seen a single emulator that handles it. I even doubt any game ever made use of it since it was added to the official SDK about a month before the system was discontinued.
Let me know if you find any issues with the converter. It should be fully platform independent, but I've only tested it on linux. Also, the file handling should be endian-independent, but I've only tested it on little endian.
The size of a compressed texture is always 1/8th the size of an uncompressed one of the same format, plus 2kB of overhead for the codebook. So when compared to an ordinary uncompressed 16-bit texture we get:
8:1 ratio + 2kB overhead for compressed 16-bit textures.
16:1 ratio + 2kB overhead for compressed 8-bit paletted textures.
32:1 ratio + 2kB overhead for compressed 4-bit paletted textures.
The overhead is pretty significant for compressed paletted textures so the real max compression ratio is just past 31:1. Keep in mind that today's texture compression schemes usually have 4:1 to 8:1 ratios. So a 31:1 hardware compression ratio on a console released back in 1998 is pretty amazing.
Also, because of the fixed overhead, it's not worth using compression on very small textures since they'll end up larger than their uncompressed versions.
And now for some compression comparison images. These were all generated with the preview option.
Original image:
16-bit compressed
8-bit paletted
8-bit paletted, compressed
4-bit paletted
4-bit paletted, compressed
Original image:
16-bit compressed
8-bit paletted
8-bit paletted, compressed
4-bit paletted
4-bit paletted, compressed
Original image:
16-bit compressed
8-bit paletted
8-bit paletted, compressed
4-bit paletted
4-bit paletted, compressed
As you can see, the higher compression ratios do a pretty good job on monochromatic textures like the last one. The quality loss is evident in the first two though, especially if you choose to have mipmaps as well.
I also made a quick video to show off the compressed normal maps.
And finally, here are a couple of images showing off what happens when you generate a preview of a mipmapped texture and what the vqcodeusage option produces.
Original image:
Mipmapped preview
vqcodeusage option
I've created a tool that can convert images to any format supported by the Dreamcast. You can find it here:
https://github.com/tvspelsfreak/texconv
Some key features:
* Full support for all Dreamcast formats.
* Supports compression of all formats.
* Support for strided textures.
* Mipmaps can be generated automatically or be input by the user, or any mix of the two.
* Can generate previews of all texture formats. No need to load the texture on hardware to see what it looks like.
* Can generate images that visualize codebook usage for compressed textures.
The readme has a good deal of information, so I'll include it here as well. I'm sure there's a lot of stuff I forgot to mention but I'll fill in the blanks later. I haven't released a loader either, but the file format is pretty simple and documented in the readme.
I doubt it's possible to try out compressed bumpmaps or compressed paletted textures in emulators since I haven't seen a single emulator that handles it. I even doubt any game ever made use of it since it was added to the official SDK about a month before the system was discontinued.
Let me know if you find any issues with the converter. It should be fully platform independent, but I've only tested it on linux. Also, the file handling should be endian-independent, but I've only tested it on little endian.
Code: Select all
texconv is a utility for creating textures for the SEGA Dreamcast hardware.
Requires Qt 5.2 or newer.
Supports all image formats supported by Qt.
At the time I'm writing this, these formats are supported:
Format Description Qt's support
BMP Windows Bitmap Read/write
GIF Graphic Interchange Format (optional) Read
JPG Joint Photographic Experts Group Read/write
JPEG Joint Photographic Experts Group Read/write
PNG Portable Network Graphics Read/write
PBM Portable Bitmap Read
PGM Portable Graymap Read
PPM Portable Pixmap Read/write
XBM X11 Bitmap Read/write
XPM X11 Pixmap Read/write
USAGE
=====
texconv --in <filename> --out <filename> --format <pixelformat> [flags...]
EXAMPLES
========
texconv --in img.jpg --out a.tex --format RGB565
Creates an RGB565 texture called 'a.tex' using 'img.jpg' as input.
texconv --in img.jpg --out a.tex --format PAL8BPP
Creates an 8-bit paletted texture called 'a.tex' using 'img.jpg' as input.
A palette file 'a.tex.pal' will also be created.
texconv --in img.jpg --out a.tex --format YUV422 --compress --mipmap
Creates a compressed, mipmapped YUV texture 'a.tex' using 'img.jpg' as
input.
texconv --in img1.jpg --in img2.png --format RGB565 --mipmap
Also assuming "img1.jpg" is 64x64 and 'img2.png' is 16x16, creates a
mipmapped RGB565 texture with mipmap levels like this:
64x64 - 'img1.jpg'
32x32 - 'img1.jpg' (downscaled)
16x16 - 'img2.png'
8x8 - 'img2.png' (downscaled)
4x4 - 'img2.png' (downscaled)
2x2 - 'img2.png' (downscaled)
1x1 - 'img2.png' (downscaled)
texconv --in img.jpg --out a.tex --format PAL4BPP --compress
--preview preview.png --vqcodeusage --usage.png
Creates a compressed 4-bit paletted texture 'a.tex' using 'img.jpg' as
input. A palette file 'a.tex.pal' will also be created. A preview file
'preview.png' showing what the texture looks is generated as well as
an image 'usage.png' that visualizes codebook usage for 'a.tex'.
GENERAL INFO
============
These are all limitations set by the hardware.
* Input image dimensions must be one of the following: 8, 16, 32, 64, 128,
256, 512 or 1024. There are two exceptions to this rule, see the -mipmap
and -stride flags for more info.
* Mipmapped and compressed textures must be square.
* Strided textures can't be compressed, twiddled or mipmapped.
* Bumpmaps and paletted textures can't be strided.
PIXEL FORMATS
=============
RGB565
16-bit RGB texture without alpha.
ARGB1555
16-bit texture with 1-bit alpha. Each texel is either fully opaque or fully
transparent.
ARGB4444
16-bit texture with full alpha.
YUV422
16-bit texture without alpha. The YUV color space takes human perception
into account, and thus offers higher percieved quality than the RGB color
space.
BUMPMAP
16-bit normal map. Each texel consist of a pair of 8-bit values (S and R)
which represent a normal in spherical coordinates.
S = polar angle. 0-255 maps to 0-89 degrees.
R = azimuthal angle. 0-255 maps to 0-359 degrees.
PAL4BPP
4BPP paletted texture. The palette can contain a maximum of 16 colors.
If the input images contain more colors than the palette can hold,
the color count will be automatically reduced by the converter.
An additional palette file <outfile>.pal will be written for this format.
PAL8BPP
8BPP paletted texture. The palette can contain a maximum of 256 colors.
If the input images contain more colors than the palette can hold,
the color count will be automatically reduced by the converter.
An additional palette file <outfile>.pal will be written for this format.
PROGRAM FLAGS
=============
-i <filename> or -in <filename>
One or more images to use as input. Specify one image for non-mipmapped
textures and one or more images for mipmapped textures.
-o <filename> or -out <filename>
Output file.
-f <format> or -format <format>
One of the aforementioned pixel formats.
-m or -mipmap
Generate/allow mipmaps. If this flag is specified, you can supply
additional images down to a 1x1 size with the '-in' flag to be used for the
different mipmap levels. Keep in mind though that at least one image must
be 8x8 or larger. Any missing mipmap levels will be generated by
downscaling the next size up.
-c or -compress
Output a compressed texture. Compressed textures are 1/8th the size of
their uncompressed counterparts, plus a 2kB codebook overhead per texture.
-s or -stride
Output a strided texture. Strided textures allow for the width of the
texture to be any multiple of 32 from 32 to 992. See the topic on strided
textures for more info.
-p <filename> or -preview <filename>
Generate a preview image showing what the texture looks like.
-v or -verbose
Extra printouts. The converter will only print warnings and errors unless
this flags is set.
-n or -nearest
Use nearest-neighbor filtering when generating missing mipmap levels. This
is the default filter for paletted, mipmapped textures to avoid introducing
additional colors to the palette.
-b or -bilinear
Use bilinear filtering when generating missing mipmap levels. This is the
default filter for all 16-bit textures, for higher quality mipmaps.
-vqcodeusage <filename>
Outputs an image that visualizes compression code usage. Will only do
something for compressed textures.
TEXTURE FILE FORMAT
===================
Each texture starts with a 16-byte header:
typedef struct {
char id[4]; // 'DTEX'
short width;
short height;
int type;
int size;
} header_t;
It is then followed by 'size' bytes of texture data which can be uploaded
directly to VRAM. The size will always be a multiple of 32 bytes to allow
for DMA transfers.
'type' contains the various flags and the pixel format packed together:
bits 0-4 : Stride setting.
The width of stride textures is NOT stored in 'width'. To get the actual
width, multiply the stride setting by 32. The next power of two size up
from the stride width will be stored in 'width'.
bit 25 : Stride flag
0 = Non-strided
1 = Strided
bit 26 : Untwiddled flag
0 = Twiddled
1 = Untwiddled
bits 27-29 : Pixel format
0 = ARGB1555
1 = RGB565
2 = ARGB4444
3 = YUV422
4 = BUMPMAP
5 = PAL4BPP
6 = PAL8BPP
bit 30 : Compressed flag
0 = Uncompressed
1 = Compressed
bit 31 : Mipmapped flag
0 = No mipmaps
1 = Mipmapped
PALETTE FILE FORMAT
===================
Each palette starts with an 8-byte header:
typedef struct {
char id[4]; // 'DPAL'
int numcolors;
} header_t;
It is then followed by 'numcolors' 32-bit packed ARGB values.
The Dreamcast supports four different palette color formats, RGB565, ARGB1555
ARGB4444 and ARGB8888. The palette format only uses the last one. But it can
easily be converted by the user to any of the other formats when the palette
is loaded. See the 'to16BPP' function if you need to know how to do the
conversion.
TWIDDLED TEXTURES
=================
For twiddled textures, the texels will be arranged in a way that is more cache
friendly. Adjacent texels will be closer to each other in memory compared to
textures stored in normal scan order. This increases rendering performance.
All textures (except for strided ones) output by the converter are twiddled.
STRIDED TEXTURES
================
Strided textures allow for the width of the texture to be any multiple of 32
from 32 to 992. The stride texture width is a global setting on the Dreamcast,
so all strided textures rendered in the same frame must have the same width.
They also cannot be twiddled, mipmapped or compressed, and cannot be used
with the bumpmap or paletted formats.
Since they cannot be twiddled, the rendering performance will also be lowered
for strided textures.
The texture width for strided textures will be set to the next power of two
size up while the actual size is divided by 32 and packed in the 'type' field.
This is because the width set in the polygon header must be a power of two.
So for a 320x256 texture you would get:
width = 512
height = 256
packed stride setting = 10 (320/32)
So basically, when loading a strided texture, set the width in the polygon
header to 'width' and set the global stride setting to the packed setting
in 'type'.
You will also need to specify the U texture coordinate as if the texture
is actually 'width' wide.
COMPRESSED TEXTURES
===================
All textures (except strided ones) can be compressed using vector quantization.
This is a hardware feature on the Dreamcast and using compressed textures will
increase rendering performance at the possible cost of quality.
The compressed texture data always starts with a 2kB codebook followed by
compressed index data. The codebook contains 256 blocks of texels. For 16-bit
textures, each block represents 2x2 texels.
The codebook is followed by (width/blockwidth)x(height/blockheight) 8-bit
indices where each index refers to a block in the codebook. So if the first
index is 35, it means that the top left 2x2 pixel block in the resulting
texture is block 35 in the codebook.
Compressed paletted textures work the same way except the blocks represent 4x4
4-bit indices (for PAL4BPP) or 2x4 8-bit indices (for PAL8BPP). So there's an
extra level of lookup involved.
8:1 ratio + 2kB overhead for compressed 16-bit textures.
16:1 ratio + 2kB overhead for compressed 8-bit paletted textures.
32:1 ratio + 2kB overhead for compressed 4-bit paletted textures.
The overhead is pretty significant for compressed paletted textures so the real max compression ratio is just past 31:1. Keep in mind that today's texture compression schemes usually have 4:1 to 8:1 ratios. So a 31:1 hardware compression ratio on a console released back in 1998 is pretty amazing.
Also, because of the fixed overhead, it's not worth using compression on very small textures since they'll end up larger than their uncompressed versions.
And now for some compression comparison images. These were all generated with the preview option.
Original image:
16-bit compressed
8-bit paletted
8-bit paletted, compressed
4-bit paletted
4-bit paletted, compressed
Original image:
16-bit compressed
8-bit paletted
8-bit paletted, compressed
4-bit paletted
4-bit paletted, compressed
Original image:
16-bit compressed
8-bit paletted
8-bit paletted, compressed
4-bit paletted
4-bit paletted, compressed
As you can see, the higher compression ratios do a pretty good job on monochromatic textures like the last one. The quality loss is evident in the first two though, especially if you choose to have mipmaps as well.
I also made a quick video to show off the compressed normal maps.
And finally, here are a couple of images showing off what happens when you generate a preview of a mipmapped texture and what the vqcodeusage option produces.
Original image:
Mipmapped preview
vqcodeusage option