Dreamcast textures

Anything related in any way to game development as a whole is welcome here. Tell us about your game, grace us with your project, show us your new YouTube video, etc.

Moderator: PC Supremacists

Post Reply
Tvspelsfreak
Chaos Rift Junior
Chaos Rift Junior
Posts: 272
Joined: Wed Sep 29, 2004 5:53 pm
Favorite Gaming Platforms: NES, SNES
Programming Language of Choice: C/C++
Location: Umeå, Sweden
Contact:

Dreamcast textures

Post by Tvspelsfreak »

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.

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.
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:
Image
16-bit compressed
8-bit paletted
8-bit paletted, compressed
4-bit paletted
4-bit paletted, compressed

Original image:
Image
16-bit compressed
8-bit paletted
8-bit paletted, compressed
4-bit paletted
4-bit paletted, compressed

Original image:
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:
Image
Mipmapped preview
vqcodeusage option
User avatar
Falco Girgis
Elysian Shadows Team
Elysian Shadows Team
Posts: 10294
Joined: Thu May 20, 2004 2:04 pm
Current Project: Elysian Shadows
Favorite Gaming Platforms: Dreamcast, SNES, NES
Programming Language of Choice: C/++
Location: Studio Vorbis, AL
Contact:

Re: Dreamcast textures

Post by Falco Girgis »

Jesus. Fucking. Christ. Almighty...

The biggest problem we were facing with the DC build has just been solved... You can even compress an already paletted texture? What the FUCK?! :shock:

I will be integrating this with ESTk right after the Kickstarter. :twisted:

edit: Jesus christ dude, I'm so fucking impressed... This is AMAZING progress, especially for a game like ours, where VRAM is our primary concern...

and I was also wondering what the best way to determine the compression scheme would be, but I could literally let the artists decide with your previewer... Thank god for your command-line arguments. Good call. ;)
Tvspelsfreak
Chaos Rift Junior
Chaos Rift Junior
Posts: 272
Joined: Wed Sep 29, 2004 5:53 pm
Favorite Gaming Platforms: NES, SNES
Programming Language of Choice: C/C++
Location: Umeå, Sweden
Contact:

Re: Dreamcast textures

Post by Tvspelsfreak »

Thanks man! :)

Yeah, the preview stuff is something I've always wanted. It's difficult to get a good look at the textures on the actual hardware, especially when you throw mipmaps into the equation.

I forgot to mention something about the bumpmap conversion. It expects you to input a normal map, not a height map. It doesn't handle height maps at all. Also, the normal map parsing is set so that the blue channel (0..255) represents Z (0..1). Some normal maps might not use the full positive range, but expects it to represent Z (-1..1). I know Doom 3 does that.

Also, I should mention that the Dreamcast has 1024 on-chip palette entries. Enough for 4 8-bit palettes or 64 4-bit palettes. So there's a limit to how many paletted textures you can effectively have. I used to have a tool that could sync textures to share the same palette as long as the total color count was still within the palette limits. I guess an updated version of that would be nice to have. And now that I think about it, I could do a vector quantization pass to create an optimal shared palette for multiple textures even if the total color count is way beyond what fits in the palette...
User avatar
Falco Girgis
Elysian Shadows Team
Elysian Shadows Team
Posts: 10294
Joined: Thu May 20, 2004 2:04 pm
Current Project: Elysian Shadows
Favorite Gaming Platforms: Dreamcast, SNES, NES
Programming Language of Choice: C/++
Location: Studio Vorbis, AL
Contact:

Re: Dreamcast textures

Post by Falco Girgis »

Yeah, I think an updated version of palsync would be very nice to have!

And the normal map part is fine, that's what our engine loads anyway (rather than a height map). I may have to add some intermediary code to ESTk to fuck with the Z channel of the normal map before passing it to your tool, but that's not a big deal at all. :D
User avatar
dandymcgee
ES Beta Backer
ES Beta Backer
Posts: 4709
Joined: Tue Apr 29, 2008 3:24 pm
Current Project: https://github.com/dbechrd/RicoTech
Favorite Gaming Platforms: NES, Sega Genesis, PS2, PC
Programming Language of Choice: C
Location: San Francisco
Contact:

Re: Dreamcast textures

Post by dandymcgee »

This is some seriously impressive stuff man! Very interesting read, thank you for posting it here.
Falco Girgis wrote:It is imperative that I can broadcast my narcissistic commit strings to the Twitter! Tweet Tweet, bitches! :twisted:
User avatar
Light-Dark
Dreamcast Developer
Dreamcast Developer
Posts: 307
Joined: Sun Mar 13, 2011 7:57 pm
Current Project: 2D RPG & NES Platformer
Favorite Gaming Platforms: NES,SNES,N64,Genesis,Dreamcast,PC,Xbox360
Programming Language of Choice: C/++
Location: Canada

Re: Dreamcast textures

Post by Light-Dark »

:worship: Great job and timing man! I've coincidentally been looking into/working on integrating texture compression into the Dreamcast build of my engine. Thanks for sharing this:D!
<tpw_rules> LightDark: java is a consequence of inverse moore's law: every 18 months, the average program will be twice as slow. therefore, computers always run at the same percevied speed. java's invention was a monumental step
Image
Post Reply