From e50e53b3ea82926ab01858e5728e489bd38d6878 Mon Sep 17 00:00:00 2001 From: quou Date: Sun, 21 Jul 2024 20:22:36 +1000 Subject: texture compression --- Makefile | 19 ++++-- convtexture.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++ intermediate/gun.bmp | Bin 0 -> 16522 bytes testbc1.c | 74 ++++++++++++++++++++++ 4 files changed, 257 insertions(+), 5 deletions(-) create mode 100644 convtexture.c create mode 100644 intermediate/gun.bmp create mode 100644 testbc1.c diff --git a/Makefile b/Makefile index 33c0c01..792b8b1 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ int_dir = intermediate data_dir = data target = 3de convmesh = convmesh +convtexture = convtexture packassets = packassets pack = pack @@ -29,8 +30,11 @@ mesh_sources = \ $(int_dir)/monkey.obj \ $(int_dir)/cube.obj +texture_sources = \ + $(int_dir)/gun.bmp \ + meshes = $(mesh_sources:$(int_dir)/%.obj=$(data_dir)/%.msh) -assets = $(meshes) +textures = $(texture_sources:$(int_dir)/%.bmp=$(data_dir)/%.bc1) all: $(target) $(pack) @@ -43,19 +47,23 @@ $(target): $(objects) $(meshes): $(data_dir)/%.msh : $(int_dir)/%.obj | $(convmesh) $(data_dir) ./$(convmesh) $< $@ -$(convassets): convassets.c - $(tool_compiler) $(cflags) -o $@ $< +$(textures): $(data_dir)/%.bc1 : $(int_dir)/%.bmp | $(convtexture) $(data_dir) + ./$(convtexture) $< $@ $(convmesh): convmesh.c $(tool_compiler) $(cflags) -o $@ $< +$(convtexture): convtexture.c + $(tool_compiler) $(cflags) -o $@ $< -lm + $(packassets): packassets.c $(tool_compiler) packassets.c $(cflags) -o $@ -$(pack): $(packassets) $(meshes) +$(pack): $(packassets) $(meshes) $(textures) ./$(packassets) \ $(data_dir) \ - $(notdir $(meshes)) + $(notdir $(meshes)) \ + $(notdir $(textures)) $(data_dir): mkdir -p $(data_dir) @@ -63,6 +71,7 @@ $(data_dir): clean: rm -f $(packassets) rm -f $(convmesh) + rm -f $(convtexture) rm -rf data rm *.d rm *.o diff --git a/convtexture.c b/convtexture.c new file mode 100644 index 0000000..a07c331 --- /dev/null +++ b/convtexture.c @@ -0,0 +1,169 @@ +#include "render.h" +#include +#include + +typedef struct { + float r, g, b; +} vec3; + +typedef struct { + vec3 start, end; + unsigned char indices[4 * 4]; +} Block; + +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; + fwrite(&w, 1, 4, f); + fwrite(&h, 1, 4, f); + 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); + } + } +} + +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; + Colour pixel; + + if (argc < 3) { + fprintf(stderr, "Usage: %s infile outfile.\n", argv[0]); + return 1; + } + + infile = fopen(argv[1], "rb"); + if (!infile) { + fprintf(stderr, "Failed to open %s.\n", argv[1]); + return 2; + } + + fread(bmp_magic, 2, 1, infile); + if (bmp_magic[0] != 'B' || bmp_magic[1] != 'M') { + fprintf(stderr, "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) { + fprintf(stderr, "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) { + fprintf(stderr, "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) { + fprintf(stderr, "Failed to open %s.\n", argv[2]); + return 6; + } + compress_bc1(buffer, bmp_w, bmp_h, outfile); + fclose(outfile); + return 0; +} diff --git a/intermediate/gun.bmp b/intermediate/gun.bmp new file mode 100644 index 0000000..d64cb6c Binary files /dev/null and b/intermediate/gun.bmp differ diff --git a/testbc1.c b/testbc1.c new file mode 100644 index 0000000..2b9aab4 --- /dev/null +++ b/testbc1.c @@ -0,0 +1,74 @@ +#include +#include + +#include "render.h" + +typedef struct { + float r, g, b; +} vec3; + +void lerp(vec3* d, const vec3* a, const vec3* b, float t) { + d->r = a->r + t * (b->r - a->r); + d->g = a->g + t * (b->g - a->g); + d->b = a->b + t * (b->b - a->b); +} + +Colour get_pixel(unsigned* data, int x, int y, int w) { + vec3 start, end, col; + float t; + int i; + Colour r = { 0 }; + unsigned palette = data[(x / 4 + y / 4 * (w / 4)) * 2]; + unsigned indices = data[(x / 4 + y / 4 * (w / 4)) * 2 + 1]; + start.r = (float)((palette & (31 << 27)) >> 27) / 32.0f; + start.g = (float)((palette & (63 << 21)) >> 21) / 64.0f; + start.b = (float)((palette & (31 << 16)) >> 16) / 32.0f; + end.r = (float)((palette & (31 << 11)) >> 11) / 32.0f; + end.g = (float)((palette & (63 << 5)) >> 5) / 64.0f; + end.b = (float)((palette & (31 )) ) / 32.0f; + i = x % 4 + (y % 4) * 4; + i = (indices & (3 << i * 2)) >> i * 2; + t = (float)i / 3.0f; + lerp(&col, &start, &end, t); + r.r = (int)(col.r * 255.0f); + r.g = (int)(col.g * 255.0f); + r.b = (int)(col.b * 255.0f); + return r; +} + +int main(int argc, const char** argv) { + FILE* infile, * outfile; + unsigned* data; + int w, h, x, y, cw, ch; + unsigned palette, indices; + vec3 colour; + if (argc < 3) { + fprintf(stderr, "Usage: %s infile outfile.\n", argv[0]); + return 1; + } + infile = fopen(argv[1], "rb"); + if (!infile) { + fprintf(stderr, "Failed to open %s.\n", argv[1]); + return 2; + } + outfile = fopen(argv[2], "w"); + if (!infile) { + fprintf(stderr, "Failed to open %s.\n", argv[2]); + return 3; + } + fread(&w, 1, 4, infile); + fread(&h, 1, 4, infile); + cw = w / 4; + ch = h / 4; + data = malloc(cw * ch * 8); + fread(data, 1, cw * ch * 8, infile); + fprintf(outfile, "P3\n"); + fprintf(outfile, "%d %d\n", w, h); + fprintf(outfile, "255\n"); + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) { + Colour c = get_pixel(data, x, y, w); + fprintf(outfile, "%d %d %d\n", c.r, c.g, c.b); + } + } +} -- cgit v1.2.3-54-g00ecf