Skip to content

Instantly share code, notes, and snippets.

@castano
Last active November 1, 2025 07:09
Show Gist options
  • Select an option

  • Save castano/4e58e4212bf337c957ce020287554d9d to your computer and use it in GitHub Desktop.

Select an option

Save castano/4e58e4212bf337c957ce020287554d9d to your computer and use it in GitHub Desktop.
Generates a DDS cubemap from a latlong HDR image.
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdint.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
typedef struct { float x, y, z; } Vec3;
inline Vec3 normalize(Vec3 v) {
float l = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z);
return (Vec3){ v.x/l, v.y/l, v.z/l };
}
inline void dir_to_uv(Vec3 d, float *u, float *v) {
float phi = atan2f(d.z, d.x);
float theta = acosf(d.y);
*u = (phi + M_PI) / (2.0f * M_PI);
*v = theta / M_PI;
}
inline Vec3 face_dir(int face, float u, float v) {
float x = 2.0f * u - 1.0f;
float y = 2.0f * v - 1.0f;
switch(face) {
case 0: return normalize((Vec3){ 1, -y, -x }); // +X
case 1: return normalize((Vec3){-1, -y, x }); // -X
case 2: return normalize((Vec3){ x, 1, y }); // +Y
case 3: return normalize((Vec3){ x, -1, -y }); // -Y
case 4: return normalize((Vec3){ x, -y, 1 }); // +Z
case 5: return normalize((Vec3){-x, -y, -1 }); // -Z
}
return (Vec3){0,0,0};
}
inline void sample_latlong(float *img, int w, int h, int n, float u, float v, float *out)
{
// Wrap horizontally and clamp vertically
u = fmodf(u, 1.0f);
if (u < 0.0f) u += 1.0f;
v = fminf(fmaxf(v, 0.0f), 1.0f);
float x = u * (w - 1);
float y = v * (h - 1);
int x0 = (int)floorf(x);
int y0 = (int)floorf(y);
int x1 = (x0 + 1) % w;
int y1 = y0 < h - 1 ? y0 + 1 : h - 1;
float fx = x - x0;
float fy = y - y0;
const float *p00 = img + (y0 * w + x0) * n;
const float *p10 = img + (y0 * w + x1) * n;
const float *p01 = img + (y1 * w + x0) * n;
const float *p11 = img + (y1 * w + x1) * n;
for (int i = 0; i < n; ++i) {
float c0 = p00[i] * (1 - fx) + p10[i] * fx;
float c1 = p01[i] * (1 - fx) + p11[i] * fx;
out[i] = c0 * (1 - fy) + c1 * fy;
}
}
#define DDS_MAGIC 0x20534444 // "DDS "
#define DDS_HEADER_FLAGS_TEXTURE 0x00001007
#define DDSCAPS_TEXTURE 0x00001000
#define DDSCAPS2_CUBEMAP 0x00000200
#define DDSCAPS2_CUBEMAP_ALL_FACES 0x0000FC00
#define DDPF_FOURCC 0x00000004
#define FOURCC_DX10 0x30315844 // "DX10"
#define DXGI_FORMAT_R32G32B32_FLOAT 6
#define D3D10_RESOURCE_DIMENSION_TEXTURE2D 3
#define D3D10_RESOURCE_MISC_TEXTURECUBE 0x4
#pragma pack(push,1)
typedef struct {
uint32_t size;
uint32_t flags;
uint32_t height;
uint32_t width;
uint32_t pitchOrLinearSize;
uint32_t depth;
uint32_t mipMapCount;
uint32_t reserved1[11];
struct {
uint32_t size;
uint32_t flags;
uint32_t fourCC;
uint32_t rgbBitCount;
uint32_t rBitMask;
uint32_t gBitMask;
uint32_t bBitMask;
uint32_t aBitMask;
} ddspf;
uint32_t caps;
uint32_t caps2;
uint32_t caps3;
uint32_t caps4;
uint32_t reserved2;
} DDS_HEADER;
typedef struct {
uint32_t dxgiFormat;
uint32_t resourceDimension;
uint32_t miscFlag;
uint32_t arraySize;
uint32_t miscFlags2;
} DDS_HEADER_DXT10;
#pragma pack(pop)
static void write_dds_cubemap_f32(const char *filename, float *faces[6], int size)
{
FILE *f = fopen(filename, "wb");
if (!f) {
perror("fopen");
return;
}
uint32_t magic = DDS_MAGIC;
fwrite(&magic, sizeof(magic), 1, f);
DDS_HEADER header = {0};
header.size = sizeof(DDS_HEADER);
header.flags = DDS_HEADER_FLAGS_TEXTURE;
header.height = size;
header.width = size;
header.pitchOrLinearSize = size * size * 3 * sizeof(float);
header.ddspf.size = 32;
header.ddspf.flags = DDPF_FOURCC;
header.ddspf.fourCC = FOURCC_DX10;
header.caps = DDSCAPS_TEXTURE;
header.caps2 = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_ALL_FACES;
fwrite(&header, sizeof(header), 1, f);
DDS_HEADER_DXT10 dx10 = {0};
dx10.dxgiFormat = DXGI_FORMAT_R32G32B32_FLOAT;
dx10.resourceDimension = D3D10_RESOURCE_DIMENSION_TEXTURE2D;
dx10.miscFlag = D3D10_RESOURCE_MISC_TEXTURECUBE;
dx10.arraySize = 6;
dx10.miscFlags2 = 0;
fwrite(&dx10, sizeof(dx10), 1, f);
for (int i = 0; i < 6; ++i)
fwrite(faces[i], sizeof(float), size * size * 3, f);
fclose(f);
}
int main(int argc, char **argv) {
if (argc < 3) {
printf("Usage: %s <input.hdr> <size>\n", argv[0]);
return 1;
}
int w, h, n;
float *img = stbi_loadf(argv[1], &w, &h, &n, 3);
if (!img) {
fprintf(stderr, "Error loading image %s\n", argv[1]);
return 1;
}
int size = atoi(argv[2]);
float *faces[6];
for (int f=0; f<6; ++f) faces[f] = (float*)malloc(size*size*3*sizeof(float));
for (int f=0; f<6; ++f)
for (int y=0; y<size; ++y)
for (int x=0; x<size; ++x) {
float u = (x + 0.5f)/size;
float v = (y + 0.5f)/size;
Vec3 dir = face_dir(f, u, v);
float su, sv;
dir_to_uv(dir, &su, &sv);
sample_latlong(img, w, h, 3, su, sv, &faces[f][(y*size+x)*3]);
}
write_dds_cubemap_f32("output.dds", faces, size);
for (int f=0; f<6; ++f) free(faces[f]);
stbi_image_free(img);
printf("Cubemap written to output.dds\n");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment