crt/lightmap.cpp
#include "lightmap.hpp"
void LightValue::add(const LightValue& o) {
// the contribution of each light adds up linearly
// http://www.scratchapixel.com/old/lessons/3d-basic-lessons/lesson-15-introduction-to-shading-and-radiometry/light-sources/
r += o.r;
g += o.g;
b += o.b;
}
void LightValue::set(const LightValue& o) {
r = o.r;
g = o.g;
b = o.b;
}
void LightValue::add(const LightValue& o, light_t weight) {
assert(0.0 <= weight && weight <= 1.0);
r += o.r * weight;
g += o.g * weight;
b += o.b * weight;
}
void LightValue::add(light_t l) {
r += l;
g += l;
b += l;
}
void LightValue::mul(light_t l) {
assert(0.0 <= l && l <= 1.0);
r *= l;
g *= l;
b *= l;
}
void LightValue::add(Img::rgb_t c, light_t l) {
r += (c.r / 255.0) * l;
g += (c.g / 255.0) * l;
b += (c.b / 255.0) * l;
}
void LightValue::finalize(Img::rgb_t& px) { // undefined state afterwards
// https://bheisler.github.io/post/writing-raytracer-in-rust-part-2/
r *= px.r;
g *= px.g;
b *= px.b;
// clamp result, not light value(s)
px.r = MIN(r, 255.0);
px.g = MIN(g, 255.0);
px.b = MIN(b, 255.0);
}
LightMap::LightMap(bool ss, unsigned ww, unsigned hh):
buf((LightValue*)calloc((ss? 2: 1)*ww*hh, sizeof(LightValue))),
w(ww), h(hh), sided(ss) {
assert(w && h);
}
LightMap::~LightMap() {
free(buf);
}
void LightMap::to_uv(unsigned su, unsigned sv, coord_t& u, coord_t& v) const {
assert(su < w && sv < h);
u = ((coord_t)su + 1.0) / ((coord_t)w + 1.0);
v = ((coord_t)sv + 1.0) / ((coord_t)h + 1.0);
assert(0.0 <= u && u <= 1.0);
assert(0.0 <= v && v <= 1.0);
}
LightValue& LightMap::at(side_t s, unsigned x, unsigned y) {
assert(x < w);
assert(y < h);
assert(s == SIDE_A || sided);
return buf[((unsigned)s*w*h)+(y*w)+(x)];
}
const LightValue& LightMap::at(side_t s, unsigned x, unsigned y) const {
assert(x < w);
assert(y < h);
assert(s == SIDE_A || sided);
return buf[((unsigned)s*w*h)+(y*w)+(x)];
}
void LightMap::at_uv(side_t s, coord_t u, coord_t v, LightValue& lv) const {
assert(0.0 <= u && u <= 1.0);
assert(0.0 <= v && v <= 1.0);
#ifndef LIGHTMAP_INTERPOLATE
lv.add(at(s, u*(w-1), v*(h-1)));
#else
u *= w-1;
v *= h-1;
coord_t uf = floor(u);
coord_t uc = ceil(u);
coord_t vf = floor(v);
coord_t vc = ceil(v);
coord_t duc = uc - u;
coord_t duf = 1.0 - duc;
coord_t dvc = vc - v;
coord_t dvf = 1.0 - dvc;
// TODO: Could trigger a full light raytracing when whe detect a shadow border here
lv.add(at(s, uf, vf), duc * dvc);
lv.add(at(s, uf, vc), duc * dvf);
lv.add(at(s, uc, vf), duf * dvc);
lv.add(at(s, uc, vc), duf * dvf);
#endif
}