Initial commit

This commit is contained in:
github-classroom[bot] 2024-09-26 09:17:55 +00:00 committed by GitHub
commit 5d6a4935ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
1678 changed files with 351156 additions and 0 deletions

19
include/Camera.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef CAMERA_H
#define CAMERA_H
#include "Object.h"
#include <Eigen/Core>
struct Camera
{
// Origin or "eye"
Eigen::Vector3d e;
// orthonormal frame so that -w is the viewing direction.
Eigen::Vector3d u,v,w;
// image plane distance / focal length
double d;
// width and height of image plane
double width, height;
};
#endif

27
include/Object.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef OBJECT_H
#define OBJECT_H
#include <Eigen/Core>
struct Ray;
class Object
{
public:
// https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors
virtual ~Object() {}
// Intersect object with ray.
//
// Inputs:
// Ray ray to intersect with
// min_t minimum parametric distance to consider
// Outputs:
// t first intersection at ray.origin + t * ray.direction
// n surface normal at point of intersection
// Returns iff there a first intersection is found.
//
// The funny = 0 just ensures that this function is defined (as a no-op)
virtual bool intersect(
const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const = 0;
};
#endif

27
include/Plane.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef PLANE_H
#define PLANE_H
#include "Object.h"
#include <Eigen/Core>
class Plane : public Object
{
public:
// Point on plane
Eigen::Vector3d point;
// Normal of plane
Eigen::Vector3d normal;
// Intersect plane with ray.
//
// Inputs:
// Ray ray to intersect with
// min_t minimum parametric distance to consider
// Outputs:
// t first intersection at ray.origin + t * ray.direction
// n surface normal at point of intersection
// Returns iff there a first intersection is found.
bool intersect(
const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const;
};
#endif

15
include/Ray.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef RAY_H
#define RAY_H
#include <Eigen/Core>
struct Ray
{
Eigen::Vector3d origin;
// Not necessarily unit-length direction vector. (It is often useful to have
// non-unit length so that origin+t*direction lands on a special point when
// t=1.)
Eigen::Vector3d direction;
};
#endif

27
include/Sphere.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef SPHERE_H
#define SPHERE_H
#include "Sphere.h"
#include "Object.h"
#include <Eigen/Core>
class Sphere : public Object
{
public:
Eigen::Vector3d center;
double radius;
public:
// Intersect sphere with ray.
//
// Inputs:
// Ray ray to intersect with
// min_t minimum parametric distance to consider
// Outputs:
// t first intersection at ray.origin + t * ray.direction
// n surface normal at point of intersection
// Returns iff there a first intersection is found.
bool intersect(
const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const;
};
#endif

25
include/Triangle.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef TRIANGLE_H
#define TRIANGLE_H
#include "Object.h"
#include <Eigen/Core>
class Triangle : public Object
{
public:
// A triangle has three corners
std::tuple< Eigen::Vector3d, Eigen::Vector3d, Eigen::Vector3d> corners;
// Intersect a triangle with ray.
//
// Inputs:
// Ray ray to intersect with
// min_t minimum parametric distance to consider
// Outputs:
// t first intersection at ray.origin + t * ray.direction
// n surface normal at point of intersection
// Returns iff there a first intersection is found.
bool intersect(
const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const;
};
#endif

31
include/TriangleSoup.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef TRIANGLE_SOUP_H
#define TRIANGLE_SOUP_H
#include "Object.h"
#include <Eigen/Core>
#include <memory>
#include <vector>
// Forward declaration
class Triangle;
class TriangleSoup : public Object
{
public:
// A soup is just a set (list) of triangles
std::vector<std::shared_ptr<Object> > triangles;
// Intersect a triangle soup with ray.
//
// Inputs:
// Ray ray to intersect with
// min_t minimum parametric distance to consider
// Outputs:
// t first intersection at ray.origin + t * ray.direction
// n surface normal at point of intersection
// Returns iff there a first intersection is found.
bool intersect(
const Ray & ray, const double min_t, double & t, Eigen::Vector3d & n) const;
};
#endif

63
include/dirname.h Normal file
View file

@ -0,0 +1,63 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#ifndef IGL_DIRNAME_H
#define IGL_DIRNAME_H
#define IGL_INLINE inline
#include <string>
namespace igl
{
// Function like PHP's dirname: /etc/passwd --> /etc,
// Input:
// path string containing input path
// Returns string containing dirname (see php's dirname)
//
// See also: basename, pathinfo
IGL_INLINE std::string dirname(const std::string & path);
}
//
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include <algorithm>
IGL_INLINE std::string igl::dirname(const std::string & path)
{
if(path == "")
{
return std::string("");
}
// https://stackoverflow.com/a/3071694/148668
size_t found = path.find_last_of("/\\");
if(found == std::string::npos)
{
// No slashes found
return std::string(".");
}else if(found == 0)
{
// Slash is first char
return std::string(path.begin(),path.begin()+1);
}else if(found == path.length()-1)
{
// Slash is last char
std::string redo = std::string(path.begin(),path.end()-1);
return igl::dirname(redo);
}
// Return everything up to but not including last slash
return std::string(path.begin(),path.begin()+found);
}
#endif

31
include/first_hit.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef FIRST_HIT_H
#define FIRST_HIT_H
#include "Ray.h"
#include "Object.h"
#include <Eigen/Core>
#include <vector>
#include <memory>
// Find the first (visible) hit given a ray and a collection of scene objects
//
// Inputs:
// ray ray along which to search
// min_t minimum t value to consider (for viewing rays, this is typically at
// least the _parametric_ distance of the image plane to the camera)
// objects list of objects (shapes) in the scene
// Outputs:
// hit_id index into objects of object with first hit
// t _parametric_ distance along ray so that ray.origin+t*ray.direction is
// the hit location
// n surface normal at hit location
// Returns true iff a hit was found
bool first_hit(
const Ray & ray,
const double min_t,
const std::vector< std::shared_ptr<Object> > & objects,
int & hit_id,
double & t,
Eigen::Vector3d & n);
#endif

18590
include/json.hpp Normal file

File diff suppressed because it is too large Load diff

295
include/readSTL.h Normal file
View file

@ -0,0 +1,295 @@
// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#ifndef IGL_READSTL_H
#define IGL_READSTL_H
# define IGL_INLINE inline
#ifndef IGL_NO_EIGEN
# include <Eigen/Core>
#endif
#include <string>
#include <cstdio>
#include <vector>
namespace igl
{
// Inputs:
// stl_file pointer to already opened .stl file
// Outputs:
// stl_file closed file
template <typename TypeV, typename TypeF, typename TypeN>
IGL_INLINE bool readSTL(
FILE * stl_file,
std::vector<std::vector<TypeV> > & V,
std::vector<std::vector<TypeF> > & F,
std::vector<std::vector<TypeN> > & N);
template <typename TypeV, typename TypeF, typename TypeN>
IGL_INLINE bool readSTL(
const std::string & filename,
std::vector<std::vector<TypeV> > & V,
std::vector<std::vector<TypeF> > & F,
std::vector<std::vector<TypeN> > & N);
}
#include <iostream>
template <typename TypeV, typename TypeF, typename TypeN>
IGL_INLINE bool igl::readSTL(
const std::string & filename,
std::vector<std::vector<TypeV> > & V,
std::vector<std::vector<TypeF> > & F,
std::vector<std::vector<TypeN> > & N)
{
using namespace std;
// Should test for ascii
// Open file, and check for error
FILE * stl_file = fopen(filename.c_str(),"rb");
if(NULL==stl_file)
{
fprintf(stderr,"IOError: %s could not be opened...\n",
filename.c_str());
return false;
}
return readSTL(stl_file,V,F,N);
}
template <typename TypeV, typename TypeF, typename TypeN>
IGL_INLINE bool igl::readSTL(
FILE * stl_file,
std::vector<std::vector<TypeV> > & V,
std::vector<std::vector<TypeF> > & F,
std::vector<std::vector<TypeN> > & N)
{
using namespace std;
//stl_file = freopen(NULL,"rb",stl_file);
if(NULL==stl_file)
{
fprintf(stderr,"IOError: stl file could not be reopened as binary (1) ...\n");
return false;
}
V.clear();
F.clear();
N.clear();
// Specifically 80 character header
char header[80];
char solid[80];
bool is_ascii = true;
if(fread(header,1,80,stl_file) != 80)
{
cerr<<"IOError: too short (1)."<<endl;
goto close_false;
}
sscanf(header,"%s",solid);
if(string("solid") != solid)
{
// definitely **not** ascii
is_ascii = false;
}else
{
// might still be binary
char buf[4];
if(fread(buf,1,4,stl_file) != 4)
{
cerr<<"IOError: too short (3)."<<endl;
goto close_false;
}
size_t num_faces = *reinterpret_cast<unsigned int*>(buf);
fseek(stl_file,0,SEEK_END);
int file_size = ftell(stl_file);
if(file_size == 80 + 4 + (4*12 + 2) * num_faces)
{
is_ascii = false;
}else
{
is_ascii = true;
}
}
if(is_ascii)
{
// Rewind to end of header
//stl_file = fopen(filename.c_str(),"r");
//stl_file = freopen(NULL,"r",stl_file);
fseek(stl_file, 0, SEEK_SET);
if(NULL==stl_file)
{
fprintf(stderr,"IOError: stl file could not be reopened as ascii ...\n");
return false;
}
// Read 80 header
// Eat file name
#ifndef IGL_LINE_MAX
# define IGL_LINE_MAX 2048
#endif
char name[IGL_LINE_MAX];
if(NULL==fgets(name,IGL_LINE_MAX,stl_file))
{
cerr<<"IOError: ascii too short (2)."<<endl;
goto close_false;
}
// ascii
while(true)
{
int ret;
char facet[IGL_LINE_MAX],normal[IGL_LINE_MAX];
vector<TypeN > n(3);
double nd[3];
ret = fscanf(stl_file,"%s %s %lg %lg %lg",facet,normal,nd,nd+1,nd+2);
if(string("endsolid") == facet)
{
break;
}
if(ret != 5 ||
!(string("facet") == facet ||
string("faced") == facet) ||
string("normal") != normal)
{
cout<<"facet: "<<facet<<endl;
cout<<"normal: "<<normal<<endl;
cerr<<"IOError: bad format (1)."<<endl;
goto close_false;
}
// copy casts to Type
n[0] = (TypeN)nd[0];
n[1] = (TypeN)nd[1];
n[2] = (TypeN)nd[2];
N.push_back(n);
char outer[IGL_LINE_MAX], loop[IGL_LINE_MAX];
ret = fscanf(stl_file,"%s %s",outer,loop);
if(ret != 2 || string("outer") != outer || string("loop") != loop)
{
cerr<<"IOError: bad format (2)."<<endl;
goto close_false;
}
vector<TypeF> f;
while(true)
{
char word[IGL_LINE_MAX];
int ret = fscanf(stl_file,"%s",word);
if(ret == 1 && string("endloop") == word)
{
break;
}else if(ret == 1 && string("vertex") == word)
{
vector<TypeV> v(3);
double vd[3];
int ret = fscanf(stl_file,"%lg %lg %lg",vd,vd+1,vd+2);
if(ret != 3)
{
cerr<<"IOError: bad format (3)."<<endl;
goto close_false;
}
f.push_back((TypeF)V.size());
// copy casts to Type
v[0] = vd[0];
v[1] = vd[1];
v[2] = vd[2];
V.push_back(v);
}else
{
cerr<<"IOError: bad format (4)."<<endl;
goto close_false;
}
}
F.push_back(f);
char endfacet[IGL_LINE_MAX];
ret = fscanf(stl_file,"%s",endfacet);
if(ret != 1 || string("endfacet") != endfacet)
{
cerr<<"IOError: bad format (5)."<<endl;
goto close_false;
}
}
// read endfacet
goto close_true;
}else
{
// Binary
//stl_file = freopen(NULL,"rb",stl_file);
fseek(stl_file, 0, SEEK_SET);
// Read 80 header
char header[80];
if(fread(header,sizeof(char),80,stl_file)!=80)
{
cerr<<"IOError: bad format (6)."<<endl;
goto close_false;
}
// Read number of triangles
unsigned int num_tri;
if(fread(&num_tri,sizeof(unsigned int),1,stl_file)!=1)
{
cerr<<"IOError: bad format (7)."<<endl;
goto close_false;
}
V.resize(num_tri*3,vector<TypeV >(3,0));
N.resize(num_tri,vector<TypeN >(3,0));
F.resize(num_tri,vector<TypeF >(3,0));
for(int t = 0;t<(int)num_tri;t++)
{
// Read normal
float n[3];
if(fread(n,sizeof(float),3,stl_file)!=3)
{
cerr<<"IOError: bad format (8)."<<endl;
goto close_false;
}
// Read each vertex
for(int c = 0;c<3;c++)
{
F[t][c] = 3*t+c;
N[t][c] = (TypeN)n[c];
float v[3];
if(fread(v,sizeof(float),3,stl_file)!=3)
{
cerr<<"IOError: bad format (9)."<<endl;
goto close_false;
}
V[3*t+c][0] = v[0];
V[3*t+c][1] = v[1];
V[3*t+c][2] = v[2];
}
// Read attribute size
unsigned short att_count;
if(fread(&att_count,sizeof(unsigned short),1,stl_file)!=1)
{
cerr<<"IOError: bad format (10)."<<endl;
goto close_false;
}
}
goto close_true;
}
close_false:
fclose(stl_file);
return false;
close_true:
fclose(stl_file);
return true;
}
#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
// generated by autoexplicit.sh
template bool igl::readSTL<Eigen::Matrix<double, -1, -1, 1, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
// generated by autoexplicit.sh
template bool igl::readSTL<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<unsigned int, -1, 3, 1, -1, 3>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, 3, 1, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
// generated by autoexplicit.sh
template bool igl::readSTL<Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
// generated by autoexplicit.sh
template bool igl::readSTL<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
template bool igl::readSTL<Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<float, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> >&);
template bool igl::readSTL<Eigen::Matrix<double, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, 3, 1, -1, 3>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 1, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&);
#endif
#endif

137
include/read_json.h Normal file
View file

@ -0,0 +1,137 @@
#ifndef READ_JSON_H
#define READ_JSON_H
#include <vector>
#include <string>
#include <memory>
// Forward declaration
class Object;
// Read a scene description from a .json file
//
// Input:
// filename path to .json file
// Output:
// camera camera looking at the scene
// objects list of shared pointers to objects
inline bool read_json(
const std::string & filename,
Camera & camera,
std::vector<std::shared_ptr<Object> > & objects);
// Implementation
#include <json.hpp>
#include "readSTL.h"
#include "dirname.h"
#include "Object.h"
#include "Sphere.h"
#include "Plane.h"
#include "Triangle.h"
#include "TriangleSoup.h"
#include <Eigen/Geometry>
#include <fstream>
#include <iostream>
#include <cassert>
inline bool read_json(
const std::string & filename,
Camera & camera,
std::vector<std::shared_ptr<Object> > & objects)
{
// Heavily borrowing from
// https://github.com/yig/graphics101-raycasting/blob/master/parser.cpp
using json = nlohmann::json;
std::ifstream infile( filename );
if( !infile ) return false;
json j;
infile >> j;
// parse a vector
auto parse_Vector3d = [](const json & j) -> Eigen::Vector3d
{
return Eigen::Vector3d(j[0],j[1],j[2]);
};
// parse camera
auto parse_camera =
[&parse_Vector3d](const json & j, Camera & camera)
{
assert(j["type"] == "perspective" && "Only handling perspective cameras");
camera.d = j["focal_length"].get<double>();
camera.e = parse_Vector3d(j["eye"]);
camera.v = parse_Vector3d(j["up"]).normalized();
camera.w = -parse_Vector3d(j["look"]).normalized();
camera.u = camera.v.cross(camera.w);
camera.height = j["height"].get<double>();
camera.width = j["width"].get<double>();
};
parse_camera(j["camera"],camera);
auto parse_objects = [&parse_Vector3d,&filename](
const json & j,
std::vector<std::shared_ptr<Object> > & objects)
{
objects.clear();
for(const json & jobj : j)
{
if(jobj["type"] == "sphere")
{
std::shared_ptr<Sphere> sphere(new Sphere());
sphere->center = parse_Vector3d(jobj["center"]);
sphere->radius = jobj["radius"].get<double>();
objects.push_back(sphere);
}else if(jobj["type"] == "plane")
{
std::shared_ptr<Plane> plane(new Plane());
plane->point = parse_Vector3d(jobj["point"]);
plane->normal = parse_Vector3d(jobj["normal"]).normalized();
objects.push_back(plane);
}else if(jobj["type"] == "triangle")
{
std::shared_ptr<Triangle> tri(new Triangle());
tri->corners = std::make_tuple(
parse_Vector3d(jobj["corners"][0]),
parse_Vector3d(jobj["corners"][1]),
parse_Vector3d(jobj["corners"][2]));
objects.push_back(tri);
}else if(jobj["type"] == "soup")
{
std::vector<std::vector<double> > V;
std::vector<std::vector<double> > F;
std::vector<std::vector<int> > N;
{
#if defined(WIN32) || defined(_WIN32)
#define PATH_SEPARATOR std::string("\\")
#else
#define PATH_SEPARATOR std::string("/")
#endif
const std::string stl_path = jobj["stl"];
igl::readSTL(
igl::dirname(filename)+
PATH_SEPARATOR +
stl_path,
V,F,N);
}
std::shared_ptr<TriangleSoup> soup(new TriangleSoup());
for(int f = 0;f<F.size();f++)
{
std::shared_ptr<Triangle> tri(new Triangle());
tri->corners = std::make_tuple(
Eigen::Vector3d( V[(long long)(F[f][0])][0], V[(long long)(F[f][0])][1], V[(long long)(F[f][0])][2]),
Eigen::Vector3d( V[(long long)(F[f][1])][0], V[(long long)(F[f][1])][1], V[(long long)(F[f][1])][2]),
Eigen::Vector3d( V[(long long)(F[f][2])][0], V[(long long)(F[f][2])][1], V[(long long)(F[f][2])][2])
);
soup->triangles.push_back(tri);
}
objects.push_back(soup);
}
}
};
parse_objects(j["objects"],objects);
return true;
}
#endif

7890
include/stb_image.h Normal file

File diff suppressed because it is too large Load diff

1724
include/stb_image_write.h Normal file

File diff suppressed because it is too large Load diff

26
include/viewing_ray.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef VIEWING_RAY_H
#define VIEWING_RAY_H
#include "Ray.h"
#include "Camera.h"
// Construct a viewing ray given a camera and subscripts to a pixel
//
// Inputs:
// camera Perspective camera object
// i pixel row index
// j pixel column index
// width number of pixels width of image
// height number of pixels height of image
// Outputs:
// ray viewing ray starting at camera shooting through pixel. When t=1, then
// ray.origin + t*ray.direction should land exactly on the center of the
// pixel (i,j)
void viewing_ray(
const Camera & camera,
const int i,
const int j,
const int width,
const int height,
Ray & ray);
#endif