Skip to content

Commit

Permalink
TGAColor constexpr constructor + a bit of polishing
Browse files Browse the repository at this point in the history
  • Loading branch information
ssloy committed Jan 5, 2023
1 parent b0af025 commit c6ef780
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 66 deletions.
16 changes: 7 additions & 9 deletions geometry.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
#include <iostream>

template<int n> struct vec {
double & operator[](const int i) { assert(i>=0 && i<n); return data[i]; }
double operator[](const int i) const { assert(i>=0 && i<n); return data[i]; }
double data[n] = {0};
double& operator[](const int i) { assert(i>=0 && i<n); return data[i]; }
double operator[](const int i) const { assert(i>=0 && i<n); return data[i]; }
double norm2() const { return *this * *this; }
double norm() const { return std::sqrt(norm2()); }
double data[n] = {0};
};

template<int n> double operator*(const vec<n>& lhs, const vec<n>& rhs) {
Expand Down Expand Up @@ -65,23 +65,21 @@ template<int n> std::ostream& operator<<(std::ostream& out, const vec<n>& v) {
}

template<> struct vec<2> {
double x = 0, y = 0;
double& operator[](const int i) { assert(i>=0 && i<2); return i ? y : x; }
double operator[](const int i) const { assert(i>=0 && i<2); return i ? y : x; }
double norm2() const { return *this * *this; }
double norm() const { return std::sqrt(norm2()); }
vec & normalize() { *this = (*this)/norm(); return *this; }

double x{}, y{};
vec<2> normalized() { return (*this)/norm(); }
};

template<> struct vec<3> {
double x = 0, y = 0, z = 0;
double& operator[](const int i) { assert(i>=0 && i<3); return i ? (1==i ? y : z) : x; }
double operator[](const int i) const { assert(i>=0 && i<3); return i ? (1==i ? y : z) : x; }
double norm2() const { return *this * *this; }
double norm() const { return std::sqrt(norm2()); }
vec & normalize() { *this = (*this)/norm(); return *this; }

double x{}, y{}, z{};
vec<3> normalized() { return (*this)/norm(); }
};

typedef vec<2> vec2;
Expand Down
19 changes: 9 additions & 10 deletions main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@

constexpr int width = 800; // output image size
constexpr int height = 800;

const vec3 light_dir{1,1,1}; // light source
const vec3 eye{1,1,3}; // camera position
const vec3 center{0,0,0}; // camera direction
const vec3 up{0,1,0}; // camera up vector
constexpr vec3 light_dir{1,1,1}; // light source
constexpr vec3 eye{1,1,3}; // camera position
constexpr vec3 center{0,0,0}; // camera direction
constexpr vec3 up{0,1,0}; // camera up vector

extern mat<4,4> ModelView; // "OpenGL" state matrices
extern mat<4,4> Projection;
Expand All @@ -21,7 +20,7 @@ struct Shader : IShader {
mat<3,3> view_tri; // triangle in view coordinates

Shader(const Model &m) : model(m) {
uniform_l = proj<3>((ModelView*embed<4>(light_dir, 0.))).normalize(); // transform the light vector to view coordinates
uniform_l = proj<3>((ModelView*embed<4>(light_dir, 0.))).normalized(); // transform the light vector to view coordinates
}

virtual void vertex(const int iface, const int nthvert, vec4& gl_Position) {
Expand All @@ -33,19 +32,19 @@ struct Shader : IShader {
}

virtual bool fragment(const vec3 bar, TGAColor &gl_FragColor) {
vec3 bn = (varying_nrm*bar).normalize(); // per-vertex normal interpolation
vec3 bn = (varying_nrm*bar).normalized(); // per-vertex normal interpolation
vec2 uv = varying_uv*bar; // tex coord interpolation

// for the math refer to the tangent space normal mapping lecture
// https://github.com/ssloy/tinyrenderer/wiki/Lesson-6bis-tangent-space-normal-mapping
mat<3,3> AI = mat<3,3>{ {view_tri.col(1) - view_tri.col(0), view_tri.col(2) - view_tri.col(0), bn} }.invert();
vec3 i = AI * vec3{varying_uv[0][1] - varying_uv[0][0], varying_uv[0][2] - varying_uv[0][0], 0};
vec3 j = AI * vec3{varying_uv[1][1] - varying_uv[1][0], varying_uv[1][2] - varying_uv[1][0], 0};
mat<3,3> B = mat<3,3>{ {i.normalize(), j.normalize(), bn} }.transpose();
mat<3,3> B = mat<3,3>{ {i.normalized(), j.normalized(), bn} }.transpose();

vec3 n = (B * model.normal(uv)).normalize(); // transform the normal from the texture to the tangent space
vec3 n = (B * model.normal(uv)).normalized(); // transform the normal from the texture to the tangent space
double diff = std::max(0., n*uniform_l); // diffuse light intensity
vec3 r = (n*(n*uniform_l)*2 - uniform_l).normalize(); // reflected light direction, specular mapping is described here: https://github.com/ssloy/tinyrenderer/wiki/Lesson-6-Shaders-for-the-software-renderer
vec3 r = (n*(n*uniform_l)*2 - uniform_l).normalized(); // reflected light direction, specular mapping is described here: https://github.com/ssloy/tinyrenderer/wiki/Lesson-6-Shaders-for-the-software-renderer
double spec = std::pow(std::max(-r.z, 0.), 5+sample2D(model.specular(), uv)[0]); // specular intensity, note that the camera lies on the z-axis (in view), therefore simple -r.z

TGAColor c = sample2D(model.diffuse(), uv);
Expand Down
4 changes: 1 addition & 3 deletions model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Model::Model(const std::string filename) {
iss >> trash >> trash;
vec3 n;
for (int i=0;i<3;i++) iss >> n[i];
norms.push_back(n.normalize());
norms.push_back(n.normalized());
} else if (!line.compare(0, 3, "vt ")) {
iss >> trash >> trash;
vec2 uv;
Expand All @@ -38,12 +38,10 @@ Model::Model(const std::string filename) {
}
if (3!=cnt) {
std::cerr << "Error: the obj file is supposed to be triangulated" << std::endl;
in.close();
return;
}
}
}
in.close();
std::cerr << "# v# " << nverts() << " f# " << nfaces() << " vt# " << tex_coord.size() << " vn# " << norms.size() << std::endl;
load_texture(filename, "_diffuse.tga", diffusemap );
load_texture(filename, "_nm_tangent.tga", normalmap );
Expand Down
6 changes: 3 additions & 3 deletions our_gl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ void projection(const double f) { // check https://en.wikipedia.org/wiki/Camera_
}

void lookat(const vec3 eye, const vec3 center, const vec3 up) { // check https://github.com/ssloy/tinyrenderer/wiki/Lesson-5-Moving-the-camera
vec3 z = (center-eye).normalize();
vec3 x = cross(up,z).normalize();
vec3 y = cross(z, x).normalize();
vec3 z = (center-eye).normalized();
vec3 x = cross(up,z).normalized();
vec3 y = cross(z, x).normalized();
mat<4,4> Minv = {{{x.x,x.y,x.z,0}, {y.x,y.y,y.z,0}, {z.x,z.y,z.z,0}, {0,0,0,1}}};
mat<4,4> Tr = {{{1,0,0,-eye.x}, {0,1,0,-eye.y}, {0,0,1,-eye.z}, {0,0,0,1}}};
ModelView = Minv*Tr;
Expand Down
26 changes: 7 additions & 19 deletions tgaimage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,21 @@ TGAImage::TGAImage(const int w, const int h, const int bpp) : w(w), h(h), bpp(bp

bool TGAImage::read_tga_file(const std::string filename) {
std::ifstream in;
in.open (filename, std::ios::binary);
in.open(filename, std::ios::binary);
if (!in.is_open()) {
std::cerr << "can't open file " << filename << "\n";
in.close();
return false;
}
TGAHeader header;
in.read(reinterpret_cast<char *>(&header), sizeof(header));
if (!in.good()) {
in.close();
std::cerr << "an error occured while reading the header\n";
return false;
}
w = header.width;
h = header.height;
bpp = header.bitsperpixel>>3;
if (w<=0 || h<=0 || (bpp!=GRAYSCALE && bpp!=RGB && bpp!=RGBA)) {
in.close();
std::cerr << "bad bpp (or width/height) value\n";
return false;
}
Expand All @@ -32,18 +29,15 @@ bool TGAImage::read_tga_file(const std::string filename) {
if (3==header.datatypecode || 2==header.datatypecode) {
in.read(reinterpret_cast<char *>(data.data()), nbytes);
if (!in.good()) {
in.close();
std::cerr << "an error occured while reading the data\n";
return false;
}
} else if (10==header.datatypecode||11==header.datatypecode) {
if (!load_rle_data(in)) {
in.close();
std::cerr << "an error occured while reading the data\n";
return false;
}
} else {
in.close();
std::cerr << "unknown file format " << (int)header.datatypecode << "\n";
return false;
}
Expand All @@ -52,7 +46,6 @@ bool TGAImage::read_tga_file(const std::string filename) {
if (header.imagedescriptor & 0x10)
flip_horizontally();
std::cerr << w << "x" << h << "/" << bpp*8 << "\n";
in.close();
return true;
}

Expand Down Expand Up @@ -110,55 +103,47 @@ bool TGAImage::write_tga_file(const std::string filename, const bool vflip, cons
constexpr std::uint8_t extension_area_ref[4] = {0, 0, 0, 0};
constexpr std::uint8_t footer[18] = {'T','R','U','E','V','I','S','I','O','N','-','X','F','I','L','E','.','\0'};
std::ofstream out;
out.open (filename, std::ios::binary);
out.open(filename, std::ios::binary);
if (!out.is_open()) {
std::cerr << "can't open file " << filename << "\n";
out.close();
return false;
}
TGAHeader header;
TGAHeader header = {};
header.bitsperpixel = bpp<<3;
header.width = w;
header.height = h;
header.datatypecode = (bpp==GRAYSCALE?(rle?11:3):(rle?10:2));
header.imagedescriptor = vflip ? 0x00 : 0x20; // top-left or bottom-left origin
out.write(reinterpret_cast<const char *>(&header), sizeof(header));
if (!out.good()) {
out.close();
std::cerr << "can't dump the tga file\n";
return false;
}
if (!rle) {
out.write(reinterpret_cast<const char *>(data.data()), w*h*bpp);
if (!out.good()) {
std::cerr << "can't unload raw data\n";
out.close();
return false;
}
} else if (!unload_rle_data(out)) {
out.close();
std::cerr << "can't unload rle data\n";
return false;
}
out.write(reinterpret_cast<const char *>(developer_area_ref), sizeof(developer_area_ref));
if (!out.good()) {
std::cerr << "can't dump the tga file\n";
out.close();
return false;
}
out.write(reinterpret_cast<const char *>(extension_area_ref), sizeof(extension_area_ref));
if (!out.good()) {
std::cerr << "can't dump the tga file\n";
out.close();
return false;
}
out.write(reinterpret_cast<const char *>(footer), sizeof(footer));
if (!out.good()) {
std::cerr << "can't dump the tga file\n";
out.close();
return false;
}
out.close();
return true;
}

Expand Down Expand Up @@ -205,7 +190,10 @@ bool TGAImage::unload_rle_data(std::ofstream &out) const {
TGAColor TGAImage::get(const int x, const int y) const {
if (!data.size() || x<0 || y<0 || x>=w || y>=h)
return {};
return TGAColor(data.data()+(x+y*w)*bpp, bpp);
TGAColor ret = {0, 0, 0, 0, bpp};
const std::uint8_t *p = data.data()+(x+y*w)*bpp;
for (int i=bpp; i--; ret.bgra[i] = p[i]);
return ret;
}

void TGAImage::set(int x, int y, const TGAColor &c) {
Expand Down
38 changes: 16 additions & 22 deletions tgaimage.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,24 @@

#pragma pack(push,1)
struct TGAHeader {
std::uint8_t idlength{};
std::uint8_t colormaptype{};
std::uint8_t datatypecode{};
std::uint16_t colormaporigin{};
std::uint16_t colormaplength{};
std::uint8_t colormapdepth{};
std::uint16_t x_origin{};
std::uint16_t y_origin{};
std::uint16_t width{};
std::uint16_t height{};
std::uint8_t bitsperpixel{};
std::uint8_t imagedescriptor{};
std::uint8_t idlength = 0;
std::uint8_t colormaptype = 0;
std::uint8_t datatypecode = 0;
std::uint16_t colormaporigin = 0;
std::uint16_t colormaplength = 0;
std::uint8_t colormapdepth = 0;
std::uint16_t x_origin = 0;
std::uint16_t y_origin = 0;
std::uint16_t width = 0;
std::uint16_t height = 0;
std::uint8_t bitsperpixel = 0;
std::uint8_t imagedescriptor = 0;
};
#pragma pack(pop)

struct TGAColor {
std::uint8_t bgra[4] = {0,0,0,0};
std::uint8_t bytespp = {0};

TGAColor() = default;
TGAColor(const std::uint8_t R, const std::uint8_t G, const std::uint8_t B, const std::uint8_t A=255) : bgra{B,G,R,A}, bytespp(4) { }
TGAColor(const std::uint8_t *p, const std::uint8_t bpp) : bytespp(bpp) {
for (int i=bpp; i--; bgra[i] = p[i]);
}
std::uint8_t bytespp = 4;
std::uint8_t& operator[](const int i) { return bgra[i]; }
};

Expand All @@ -49,9 +43,9 @@ struct TGAImage {
bool load_rle_data(std::ifstream &in);
bool unload_rle_data(std::ofstream &out) const;

int w = 0;
int h = 0;
int bpp = 0;
int w = 0;
int h = 0;
std::uint8_t bpp = 0;
std::vector<std::uint8_t> data = {};
};

0 comments on commit c6ef780

Please sign in to comment.