SIGN IN SIGN UP
ssloy / tinyrenderer UNCLAIMED

A brief computer graphics / rendering course

2022-01-13 18:23:57 +01:00
#pragma once
2015-01-17 19:39:58 +01:00
#include <cmath>
2015-01-26 11:05:52 +01:00
#include <cassert>
2015-01-25 21:17:26 +01:00
#include <iostream>
2015-01-23 10:45:24 +01:00
2020-08-25 15:42:18 +02:00
template<int n> struct vec {
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]; }
2015-01-26 17:26:16 +01:00
};
2020-08-25 15:42:18 +02:00
template<int n> double operator*(const vec<n>& lhs, const vec<n>& rhs) {
2025-02-20 23:45:08 +01:00
double ret = 0; // N.B. Do not ever, ever use such for loops! They are highly confusing.
for (int i=n; i--; ret+=lhs[i]*rhs[i]); // Here I used them as a tribute to old-school game programmers fighting for every CPU cycle.
return ret; // Once upon a time reverse loops were faster than the normal ones, it is not the case anymore.
2015-01-26 11:05:52 +01:00
}
2015-01-22 09:51:42 +01:00
2020-08-25 15:42:18 +02:00
template<int n> vec<n> operator+(const vec<n>& lhs, const vec<n>& rhs) {
vec<n> ret = lhs;
for (int i=n; i--; ret[i]+=rhs[i]);
return ret;
2015-01-17 19:39:58 +01:00
}
2020-08-25 15:42:18 +02:00
template<int n> vec<n> operator-(const vec<n>& lhs, const vec<n>& rhs) {
vec<n> ret = lhs;
for (int i=n; i--; ret[i]-=rhs[i]);
return ret;
2015-01-26 20:35:34 +01:00
}
2025-02-20 23:45:08 +01:00
template<int n> vec<n> operator*(const vec<n>& lhs, const double& rhs) {
2020-08-25 15:42:18 +02:00
vec<n> ret = lhs;
for (int i=n; i--; ret[i]*=rhs);
return ret;
2015-01-17 19:39:58 +01:00
}
2025-02-20 23:45:08 +01:00
template<int n> vec<n> operator*(const double& lhs, const vec<n> &rhs) {
return rhs * lhs;
2015-01-26 20:35:34 +01:00
}
2020-08-25 15:42:18 +02:00
template<int n> vec<n> operator/(const vec<n>& lhs, const double& rhs) {
vec<n> ret = lhs;
for (int i=n; i--; ret[i]/=rhs);
2015-01-26 11:05:52 +01:00
return ret;
}
2015-01-22 21:32:31 +01:00
2020-08-25 15:42:18 +02:00
template<int n> std::ostream& operator<<(std::ostream& out, const vec<n>& v) {
for (int i=0; i<n; i++) out << v[i] << " ";
return out;
2015-01-26 20:35:34 +01:00
}
2020-08-25 15:42:18 +02:00
template<> struct vec<2> {
double x = 0, y = 0;
2022-01-13 18:23:57 +01:00
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; }
2015-01-26 11:05:52 +01:00
};
2020-08-25 15:42:18 +02:00
template<> struct vec<3> {
double x = 0, y = 0, z = 0;
2022-01-13 18:23:57 +01:00
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; }
2025-02-20 23:45:08 +01:00
};
template<> struct vec<4> {
double x = 0, y = 0, z = 0, w = 0;
double& operator[](const int i) { assert(i>=0 && i<4); return i<2 ? (i ? y : x) : (2==i ? z : w); }
double operator[](const int i) const { assert(i>=0 && i<4); return i<2 ? (i ? y : x) : (2==i ? z : w); }
vec<2> xy() const { return {x, y}; }
vec<3> xyz() const { return {x, y, z}; }
2015-01-26 11:05:52 +01:00
};
2022-01-13 18:23:57 +01:00
typedef vec<2> vec2;
typedef vec<3> vec3;
typedef vec<4> vec4;
2025-02-20 23:45:08 +01:00
template<int n> double norm(const vec<n>& v) {
return std::sqrt(v*v);
}
template<int n> vec<n> normalized(const vec<n>& v) {
return v / norm(v);
}
inline vec3 cross(const vec3 &v1, const vec3 &v2) {
return {v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x};
}
2015-01-26 11:05:52 +01:00
2020-08-25 15:42:18 +02:00
template<int n> struct dt;
2015-01-26 11:05:52 +01:00
2020-08-25 15:42:18 +02:00
template<int nrows,int ncols> struct mat {
vec<ncols> rows[nrows] = {{}};
2015-01-26 11:05:52 +01:00
2020-08-25 15:42:18 +02:00
vec<ncols>& operator[] (const int idx) { assert(idx>=0 && idx<nrows); return rows[idx]; }
const vec<ncols>& operator[] (const int idx) const { assert(idx>=0 && idx<nrows); return rows[idx]; }
2015-01-26 11:05:52 +01:00
2020-08-25 15:42:18 +02:00
double det() const {
return dt<ncols>::det(*this);
2015-01-26 11:05:52 +01:00
}
2020-08-25 15:42:18 +02:00
double cofactor(const int row, const int col) const {
2025-02-21 15:55:15 +01:00
mat<nrows-1,ncols-1> submatrix;
for (int i=nrows-1; i--; )
for (int j=ncols-1;j--; submatrix[i][j]=rows[i+int(i>=row)][j+int(j>=col)]);
return submatrix.det() * ((row+col)%2 ? -1 : 1);
2015-01-26 11:05:52 +01:00
}
2020-08-25 15:42:18 +02:00
mat<nrows,ncols> invert_transpose() const {
2025-02-21 15:55:15 +01:00
mat<nrows,ncols> adjugate_transpose; // transpose to ease determinant computation, check the last line
for (int i=nrows; i--; )
for (int j=ncols; j--; adjugate_transpose[i][j]=cofactor(i,j));
return adjugate_transpose/(adjugate_transpose[0]*rows[0]);
2015-01-26 11:05:52 +01:00
}
2015-01-30 16:46:46 +01:00
2020-08-25 15:42:18 +02:00
mat<nrows,ncols> invert() const {
2015-01-30 16:46:46 +01:00
return invert_transpose().transpose();
}
2020-08-25 15:42:18 +02:00
mat<ncols,nrows> transpose() const {
mat<ncols,nrows> ret;
2025-02-20 23:45:08 +01:00
for (int i=ncols; i--; )
for (int j=nrows; j--; ret[i][j]=rows[j][i]);
2015-01-30 16:46:46 +01:00
return ret;
}
2015-01-22 21:32:31 +01:00
};
2025-02-20 23:45:08 +01:00
template<int nrows,int ncols> vec<ncols> operator*(const vec<nrows>& lhs, const mat<nrows,ncols>& rhs) {
return (mat<1,nrows>{{lhs}}*rhs)[0];
}
2020-08-25 15:42:18 +02:00
template<int nrows,int ncols> vec<nrows> operator*(const mat<nrows,ncols>& lhs, const vec<ncols>& rhs) {
vec<nrows> ret;
for (int i=nrows; i--; ret[i]=lhs[i]*rhs);
2015-01-26 17:26:16 +01:00
return ret;
2015-01-26 11:05:52 +01:00
}
2020-08-25 15:42:18 +02:00
template<int R1,int C1,int C2>mat<R1,C2> operator*(const mat<R1,C1>& lhs, const mat<C1,C2>& rhs) {
mat<R1,C2> result;
for (int i=R1; i--; )
2025-02-20 23:45:08 +01:00
for (int j=C2; j--; )
for (int k=C1; k--; result[i][j]+=lhs[i][k]*rhs[k][j]);
2020-08-25 15:42:18 +02:00
return result;
}
template<int nrows,int ncols>mat<nrows,ncols> operator*(const mat<nrows,ncols>& lhs, const double& val) {
mat<nrows,ncols> result;
for (int i=nrows; i--; result[i] = lhs[i]*val);
2015-01-26 22:55:56 +01:00
return result;
}
2015-01-26 17:48:17 +01:00
2020-08-25 15:42:18 +02:00
template<int nrows,int ncols>mat<nrows,ncols> operator/(const mat<nrows,ncols>& lhs, const double& val) {
mat<nrows,ncols> result;
for (int i=nrows; i--; result[i] = lhs[i]/val);
return result;
2015-01-26 17:48:17 +01:00
}
2020-08-25 15:42:18 +02:00
template<int nrows,int ncols>mat<nrows,ncols> operator+(const mat<nrows,ncols>& lhs, const mat<nrows,ncols>& rhs) {
mat<nrows,ncols> result;
for (int i=nrows; i--; )
for (int j=ncols; j--; result[i][j]=lhs[i][j]+rhs[i][j]);
return result;
}
template<int nrows,int ncols>mat<nrows,ncols> operator-(const mat<nrows,ncols>& lhs, const mat<nrows,ncols>& rhs) {
mat<nrows,ncols> result;
for (int i=nrows; i--; )
for (int j=ncols; j--; result[i][j]=lhs[i][j]-rhs[i][j]);
return result;
}
template<int nrows,int ncols> std::ostream& operator<<(std::ostream& out, const mat<nrows,ncols>& m) {
for (int i=0; i<nrows; i++) out << m[i] << std::endl;
2015-01-26 17:26:16 +01:00
return out;
2015-01-26 11:05:52 +01:00
}
2025-02-21 15:55:15 +01:00
template<int n> struct dt { // template metaprogramming to compute the determinant recursively
2020-08-25 15:42:18 +02:00
static double det(const mat<n,n>& src) {
double ret = 0;
2025-02-21 15:55:15 +01:00
for (int i=n; i--; ret += src[0][i] * src.cofactor(0,i));
2020-08-25 15:42:18 +02:00
return ret;
}
};
2025-02-21 15:55:15 +01:00
template<> struct dt<1> { // template specialization to stop the recursion
2020-08-25 15:42:18 +02:00
static double det(const mat<1,1>& src) {
return src[0][0];
}
};