diff options
| author | quou <quou@disroot.org> | 2024-12-23 14:41:01 +1100 | 
|---|---|---|
| committer | quou <quou@disroot.org> | 2024-12-23 14:41:01 +1100 | 
| commit | b293168cc158d65f1a5146f155921ff82119d1bc (patch) | |
| tree | 08cc5ee235b4b7ca6ead45c43f9b407664971613 /convtexture.c | |
| parent | 58245585cbe77e6c03ebe13f29e10393ff3c45b4 (diff) | |
Texture conversion and loading
Diffstat (limited to 'convtexture.c')
| -rw-r--r-- | convtexture.c | 217 | 
1 files changed, 217 insertions, 0 deletions
diff --git a/convtexture.c b/convtexture.c new file mode 100644 index 0000000..a04e3c9 --- /dev/null +++ b/convtexture.c @@ -0,0 +1,217 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "plat.h" +#include "str.h" +#include "vid_enums.h" + +typedef struct { +	float r, g, b; +} vec3; + +typedef struct { +	unsigned char r, g, b, a; +} Colour; + +typedef struct { +	Colour* pixels; +	int w, h; +} Image; + +typedef struct { +	vec3 start, end; +	unsigned char indices[4 * 4]; +} Block; + +const char* format_names[] = { +#define x(n) #n, +	texture_format_xmacro() +#undef x +}; + +void get_block(Block* block, Colour* pixels, int stride) { +	int x, y, i; +	vec3 cols[4 * 4]; +	vec3 s, e; +	float cd; +	for (y = 0; y < 4; y++) { +		for (x = 0; x < 4; x++) { +			Colour pixel = pixels[x + y * stride]; +			vec3 vec; +			vec.r = (float)pixel.r / 255.0f; +			vec.g = (float)pixel.g / 255.0f; +			vec.b = (float)pixel.b / 255.0f; +			cols[x + y * 4] = vec; +		} +	} +	cd = -1.0f; +	for (i = 0; i < 4 * 4; i++) { +		int j; +		vec3* a = &cols[i]; +		for (j = i + 1; j < 4 * 4; j++) { +			vec3* b = &cols[j]; +			float d = +				(b->r - a->r) * (b->r - a->r) + +				(b->g - a->g) * (b->g - a->g) + +				(b->b - a->b) * (b->b - a->b); +			if (d > cd) { +				cd = d; +				s = *a; +				e = *b; +			} +		} +	} +	for (i = 0; i < 4 * 4; i++) { +		vec3* col = &cols[i]; +		float d; +		d = +			(e.r - col->r) * (e.r - col->r) + +			(e.g - col->g) * (e.g - col->g) + +			(e.b - col->b) * (e.b - col->b); +		if (cd < 0.0001f) +			block->indices[i] = 0; +		else { +			d = d / cd; +			block->indices[i] = (int)((1.0f - d) * 3.1f); +		} +	} +	block->start = s; +	block->end = e; +} + +void compress_block(const Block* block, FILE* f) { +	int i; +	unsigned start = +		((unsigned)(block->start.r * 31.9999f) << 11) | +		((unsigned)(block->start.g * 63.9999f) << 5) | +		((unsigned)(block->start.b * 31.9999f)); +	unsigned end = +		((unsigned)(block->end.r * 31.9999f) << 11) | +		((unsigned)(block->end.g * 63.9999f) << 5) | +		((unsigned)(block->end.b * 31.9999f)); +	unsigned palette = (start << 16) | end; +	unsigned indices = 0; +	fwrite(&palette, 1, 4, f); +	for (i = 0; i < 4 * 4; i++) { +		indices |= block->indices[i] << (i * 2); +	} +	fwrite(&indices, 1, 4, f); +} + +void compress_bc1(Colour* pixels, int w, int h, FILE* f) { +	int x, y, cw, ch; +	Block block; +	cw = w / 4; +	ch = h / 4; +	for (y = 0; y < ch; y++) { +		for (x = 0; x < cw; x++) { +			get_block(&block, &pixels[x * 4 + y * 4 * w], w); +			compress_block(&block, f); +		} +	} +} + +void convert(Image* image, Texture_Format target, FILE* f) { +	fwrite("TXTR", 1, 4, f); +	fwrite(&image->w, 1, 4, f); +	fwrite(&image->h, 1, 4, f); +	fwrite(&target, 1, 4, f); +	switch (target) { +		case texture_format_bc1: +			compress_bc1(image->pixels, image->w, image->h, f); +			break; +		default: +			print_err("Unsupported target format.\n"); +			pbreak(40); +	} +} + +Texture_Format texture_format_from_string(const char* s) { +	int i, e = sizeof format_names / sizeof *format_names; +	for (i = 0; i < e; i++) +		if (string_equal(s, format_names[i])) +			return (Texture_Format)i; +	print_err("Invalid texture format %s\n", s); +	pbreak(10); +	return (Texture_Format)0; +} + +int main(int argc, const char** argv) { +	FILE* outfile, * infile; +	char bmp_magic[2]; +	unsigned bmp_offset; +	int bmp_w, bmp_h, s, x, y; +	unsigned short bmp_bits; +	Colour* buffer; +	Image img; +	Colour pixel; +	Texture_Format target; + +	if (argc < 4) { +		print_err("Usage: %s infile outfile format.\n", argv[0]); +		return 1; +	} + +	infile = fopen(argv[1], "rb"); +	if (!infile) { +		print_err("Failed to open %s.\n", argv[1]); +		return 2; +	} +	target = texture_format_from_string(argv[3]); + +	fread(bmp_magic, 2, 1, infile); +	if (bmp_magic[0] != 'B' || bmp_magic[1] != 'M') { +		print_err("Not a valid bitmap file.\n"); +		return 3; +	} + +	fseek(infile, 10, SEEK_SET); +	fread(&bmp_offset, 4, 1, infile); +	fseek(infile, 18, SEEK_SET); +	fread(&bmp_w,    4, 1, infile); +	fread(&bmp_h,    4, 1, infile); +	fseek(infile, 28, SEEK_SET); +	fread(&bmp_bits, 2, 1, infile); + +	if (bmp_bits != 32) { +		print_err("Bitmap must have 32 bit pixels. Instead has %d bit pixels.\n", bmp_bits); +		return 4; +	} + +	if (bmp_w % 4 != 0 || bmp_h % 4 != 0) { +		print_err("Bitmap must have a size divisible by four.\n"); +		return 5; +	} + +	fseek(infile, bmp_offset, SEEK_SET); +	s = bmp_w * bmp_h; +	buffer = malloc(s * 4); +	/* Flip & read. */ +	for (y = 0; y < bmp_h; y++) { +		for (x = 0; x < bmp_w; x++) { +			unsigned char t; +			fread(&pixel, 1, 4, infile); +			t = pixel.r; +			pixel.r = pixel.b; +			pixel.b = t; +			if (pixel.a == 0) { +				pixel.r = 0; +				pixel.g = 0; +				pixel.b = 0; +			} +			buffer[x + (bmp_h - y - 1) * bmp_w] = pixel; +		} +	} +	outfile = fopen(argv[2], "wb"); +	if (!outfile) { +		print_err("Failed to open %s.\n", argv[2]); +		return 6; +	} +	img.pixels = buffer; +	img.w = bmp_w; +	img.h = bmp_h; +	convert(&img, target, outfile); +	fclose(outfile); +	return 0; +} +  |