nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: #include "texture.h" nuclear@0: #include "object.h" nuclear@0: nuclear@0: static inline Color sample_image(const Image &img, float u, float v, Texture::WrapMode wrapping); nuclear@0: nuclear@0: Texture::Texture() nuclear@0: : sampling(SampleMode::nearest), wrapping(WrapMode::repeat) nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: Texture::~Texture() {} nuclear@0: nuclear@0: void Texture::set_name(const char *name) nuclear@0: { nuclear@0: this->name = name; nuclear@0: } nuclear@0: nuclear@0: const char *Texture::get_name() const nuclear@0: { nuclear@0: return name.c_str(); nuclear@0: } nuclear@0: nuclear@0: void Texture::set_sampling_mode(SampleMode mode) nuclear@0: { nuclear@0: sampling = mode; nuclear@0: } nuclear@0: nuclear@0: Texture::SampleMode Texture::get_sampling_mode() const nuclear@0: { nuclear@0: return sampling; nuclear@0: } nuclear@0: nuclear@0: void Texture::set_wrapping_mode(WrapMode mode) nuclear@0: { nuclear@0: wrapping = mode; nuclear@0: } nuclear@0: nuclear@0: Texture::WrapMode Texture::get_wrapping_mode() const nuclear@0: { nuclear@0: return wrapping; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: Color Texture::sample(const HitPoint &hit) const nuclear@0: { nuclear@0: return sample(hit.texcoord.x, hit.texcoord.y, 0.0f); nuclear@0: } nuclear@0: nuclear@0: Image *Texture2D::get_image(int idx) nuclear@0: { nuclear@0: return &img; nuclear@0: } nuclear@0: nuclear@0: const Image *Texture2D::get_image(int idx) const nuclear@0: { nuclear@0: return &img; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: bool Texture2D::load(const char *fname) nuclear@0: { nuclear@0: name = fname; nuclear@0: return img.load(fname); nuclear@0: } nuclear@0: nuclear@0: Color Texture2D::sample(float u, float v, float w) const nuclear@0: { nuclear@0: return sample_image(img, u, v, wrapping); nuclear@0: } nuclear@0: nuclear@0: TextureCube::TextureCube() nuclear@0: { nuclear@0: wrapping = WrapMode::clamp; nuclear@0: } nuclear@0: nuclear@0: Image *TextureCube::get_image(int idx) nuclear@0: { nuclear@0: return &img[idx]; nuclear@0: } nuclear@0: nuclear@0: const Image *TextureCube::get_image(int idx) const nuclear@0: { nuclear@0: return &img[idx]; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: bool TextureCube::load(const char *fname) nuclear@0: { nuclear@0: // assume it's a cubemap descriptor file (face path per line) nuclear@0: FILE *fp; nuclear@0: if(!(fp = fopen(fname, "r"))) { nuclear@0: fprintf(stderr, "failed to open the cubemap descriptor %s: %s\n", fname, strerror(errno)); nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: name = fname; nuclear@0: nuclear@0: char *prefix = (char*)alloca(strlen(fname) + 1); nuclear@0: strcpy(prefix, fname); nuclear@0: char *ptr = strrchr(prefix, '/'); nuclear@0: if(!ptr) { nuclear@0: ptr = prefix; nuclear@0: } nuclear@0: *ptr = 0; nuclear@0: nuclear@0: int xsz = 0, ysz = 0; nuclear@0: nuclear@0: // load the faces nuclear@0: char buf[512]; nuclear@0: for(int i=0; i<6; i++) { nuclear@0: if(!fgets(buf, sizeof buf, fp)) { nuclear@0: fprintf(stderr, "invalid cubemap descriptor file: %s\n", fname); nuclear@0: return false; nuclear@0: } nuclear@0: if(buf[strlen(buf) - 1] == '\n') { nuclear@0: buf[strlen(buf) - 1] = 0; nuclear@0: } nuclear@0: nuclear@0: std::string path = std::string(prefix) + "/" + std::string(buf); nuclear@0: if(!img[i].load(path.c_str())) { nuclear@0: fprintf(stderr, "failed to load image: %s\n", path.c_str()); nuclear@0: fclose(fp); nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: if(i == 0) { nuclear@0: xsz = img[i].xsz; nuclear@0: ysz = img[i].ysz; nuclear@0: } else { nuclear@0: if(img[i].xsz != xsz || img[i].ysz != ysz) { nuclear@0: fprintf(stderr, "cubemap %s face image %s size (%dx%d) doesn't match the previous faces (%dx%d)\n", nuclear@0: fname, path.c_str(), img[i].xsz, img[i].ysz, xsz, ysz); nuclear@0: fclose(fp); nuclear@0: return false; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: Color TextureCube::sample(float u, float v, float w) const nuclear@0: { nuclear@0: int face; nuclear@0: Vector2 uv; nuclear@0: nuclear@0: float abs_u = fabs(u); nuclear@0: float abs_v = fabs(v); nuclear@0: float abs_w = fabs(w); nuclear@0: nuclear@0: if(abs_u > abs_v && abs_u > abs_w) { nuclear@0: if(u >= 0.0) { nuclear@0: face = 0; nuclear@0: uv.x = w / abs_u; nuclear@0: uv.y = v / abs_u; nuclear@0: } else { nuclear@0: face = 1; nuclear@0: uv.x = -w / abs_u; nuclear@0: uv.y = v / abs_u; nuclear@0: } nuclear@0: } else if(abs_v > abs_w) { nuclear@0: if(v >= 0.0) { nuclear@0: face = 2; nuclear@0: uv.x = u / abs_v; nuclear@0: uv.y = w / abs_v; nuclear@0: } else { nuclear@0: face = 3; nuclear@0: uv.x = u / abs_v; nuclear@0: uv.y = -w / abs_v; nuclear@0: } nuclear@0: } else { nuclear@0: if(w >= 0.0) { nuclear@0: face = 5; nuclear@0: uv.x = -u / abs_w; nuclear@0: uv.y = v / abs_w; nuclear@0: } else { nuclear@0: face = 4; nuclear@0: uv.x = u / abs_w; nuclear@0: uv.y = v / abs_w; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return sample_image(img[face], uv.x * 0.5 + 0.5, uv.y * 0.5 + 0.5, wrapping); nuclear@0: } nuclear@0: nuclear@0: Color TextureCube::sample(const HitPoint &hit) const nuclear@0: { nuclear@0: return sample(hit.normal.x, hit.normal.y, hit.normal.z); nuclear@0: } nuclear@0: nuclear@0: Texture *load_texture(const char *fname) nuclear@0: { nuclear@0: if(access(fname, R_OK) == -1) { nuclear@0: fprintf(stderr, "failed to load texture %s: %s\n", fname, strerror(errno)); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: Texture2D *tex2d = new Texture2D; nuclear@0: if(tex2d->load(fname)) { nuclear@0: return tex2d; nuclear@0: } nuclear@0: delete tex2d; nuclear@0: nuclear@0: TextureCube *texcube = new TextureCube; nuclear@0: if(texcube->load(fname)) { nuclear@0: return texcube; nuclear@0: } nuclear@0: delete texcube; nuclear@0: nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: #define CLAMP(x, lo, hi) ((x) < (lo) ? (lo) : ((x) > (hi) ? (hi) : (x))) nuclear@0: nuclear@0: static inline Color sample_image(const Image &img, float u, float v, Texture::WrapMode wrapping) nuclear@0: { nuclear@0: int x = (int)round(u * img.xsz); nuclear@0: int y = (int)round((1.0 - v) * img.ysz); nuclear@0: nuclear@0: if(wrapping == Texture::WrapMode::clamp) { nuclear@0: x = CLAMP(x, 0, img.xsz - 1); nuclear@0: y = CLAMP(y, 0, img.ysz - 1); nuclear@0: } else { nuclear@0: x %= img.xsz; nuclear@0: y %= img.ysz; nuclear@0: nuclear@0: if(x < 0) nuclear@0: x += img.xsz; nuclear@0: if(y < 0) nuclear@0: y += img.ysz; nuclear@0: } nuclear@0: nuclear@0: return img.pixels[y * img.xsz + x]; nuclear@0: }