diff --git a/RayTracer/shape/Triangle.cpp b/RayTracer/shape/Triangle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9aa2e942bd29e42c5873d97173477a772bff170a --- /dev/null +++ b/RayTracer/shape/Triangle.cpp @@ -0,0 +1,65 @@ +#pragma once + +#include "Triangle.h" + +namespace shapes { +Triangle::Triangle(util::Vertex p1, util::Vertex p2, util::Vertex p3, + const std::shared_ptr<material::Material>& material) + : p1(p1), p2(p2), p3(p3), material(material) { + recalculateBB(); +} + +util::AxisAlignedBoundingBox Triangle::bounds() const { + return bb; +} +void Triangle::recalculateBB() { + const util::Vec3 xx = p1.position; + const util::Vec3 yy = p2.position; + const util::Vec3 zz = p3.position; + const util::Vec3 minBound = + util::Vec3(std::min<float>({xx.x(), yy.x(), zz.x()}), + std::min<float>({xx.y(), yy.y(), zz.y()}), + std::min<float>({xx.z(), yy.z(), zz.z()})); + const util::Vec3 maxBound = + util::Vec3(std::max<float>({xx.x(), yy.x(), zz.x()}), + std::max<float>({xx.y(), yy.y(), zz.y()}), + std::max<float>({xx.z(), yy.z(), zz.z()})); + bb = util::AxisAlignedBoundingBox(minBound, maxBound); +} + +std::optional<cam::Hit> Triangle::intersect(const cam::Ray& r) const { + util::Vec3 e1 = p2.position - p1.position; + util::Vec3 e2 = p3.position - p1.position; + util::Vec3 pvec = util::cross(r.d, e2); + float det = util::dot(e1, pvec); + if (det < cam::epsilon) return std::nullopt; + + util::Vec3 tvec = r.x0 - p1.position; + float u = util::dot(tvec, pvec) / det; + if (u < 0 || u > 1) return std::nullopt; + + util::Vec3 qvec = util::cross(tvec, e1); + float v = util::dot(r.d, qvec) / det; + if (v < 0 || u + v > 1) return std::nullopt; + + float t = util::dot(e2, qvec) / det; + util::Vec3 hit = r.x0 + r.d * t; + + float w = 1 - u - v; + /* + std::cout << p1().normal << std::endl; + std::cout << p2().normal << std::endl; + std::cout << p3().normal << std::endl; + std::cout << u << std::endl; + std::cout << v << std::endl; + std::cout << w << std::endl;*/ + // auto bary_normal = + // (u * p1().normal + v * p2().normal + w * p3().normal).normalize(); + auto cross_normal = + util::cross(p2.position - p1.position, p3.position - p1.position) + .normalize(); + // if (util::dot(bary_normal, cross_normal) < 0) + // std::cout << "Hm" << std::endl; + return std::optional<cam::Hit>(cam::Hit(hit, cross_normal, t, material)); +} +} // namespace shapes diff --git a/RayTracer/shape/Triangle.h b/RayTracer/shape/Triangle.h new file mode 100644 index 0000000000000000000000000000000000000000..03d5e98e469e1ef2fa9f1d65c9111af62bca4324 --- /dev/null +++ b/RayTracer/shape/Triangle.h @@ -0,0 +1,31 @@ +#pragma once + +#include <memory> +#include <optional> + +#include "../camera/Hit.h" +#include "../tools/AxisAlignedBoundingBox.h" +#include "../tools/Vertex.h" + +namespace shapes { +class Triangle { + public: + Triangle(util::Vertex p1, util::Vertex p2, util::Vertex p3, + const std::shared_ptr<material::Material>& material); + std::optional<cam::Hit> intersect(const cam::Ray& r) const; + util::AxisAlignedBoundingBox bounds() const; + void recalculateBB(); + + util::SurfacePoint sampleLight() const; + util::Vec3 calculateLightEmission(const util::SurfacePoint& p, + const util::Vec3& d) const; + + const std::shared_ptr<material::Material>& material; + + private: + const util::Vertex p1; + const util::Vertex p2; + const util::Vertex p3; + util::AxisAlignedBoundingBox bb; +}; +} // namespace shapes diff --git a/RayTracer/shape/TriangleMesh.cpp b/RayTracer/shape/TriangleMesh.cpp new file mode 100644 index 0000000000000000000000000000000000000000..667bc1be0fe9ecaa7d3ca358f93f0af210aecf60 --- /dev/null +++ b/RayTracer/shape/TriangleMesh.cpp @@ -0,0 +1,56 @@ +#pragma once + +#include "TriangleMesh.h" + +#include <sstream> +#include <string> + +#include "../tools/ObjectLoader.h" + +namespace shapes { +TriangleMesh::TriangleMesh(std::vector<Triangle> triangles) + : triangles(triangles), + material(nullptr), + hierarchy(Group(util::identity())) { +} +TriangleMesh::TriangleMesh(std::istream& in, + const std::shared_ptr<material::Material>& material) + : material(material), hierarchy(Group(util::identity())) { + triangles = util::loadObj(in, material); +} +std::optional<cam::Hit> TriangleMesh::intersect(const cam::Ray& r) const { + std::optional<cam::Hit> result = std::nullopt; + + for (auto tri : triangles) { + // if (tri.bounds().intersects(r)) { + std::optional<cam::Hit> temp = tri.intersect(r); + if (temp) { + if (r.in_range(temp->scalar())) { + if (!result) { + result = temp; + } else if (result->scalar() > temp->scalar()) { + result = temp; + } + } + } + //} + } + return result; +} +util::AxisAlignedBoundingBox TriangleMesh::bounds() const { + return util::AxisAlignedBoundingBox(util::Vec3(-2), util::Vec3(2)); +} + +util::SurfacePoint TriangleMesh::sampleLight() const { + return util::SurfacePoint(util::Vec3(), 0, material); +} +util::Vec3 TriangleMesh::calculateLightEmission(const util::SurfacePoint& p, + const util::Vec3& d) const { + return util::Vec3(); +} +util::AxisAlignedBoundingBox TriangleMesh::initBB() { + util::AxisAlignedBoundingBox init = triangles[0].bounds(); + for (auto tri : triangles) init + tri.bounds(); + return init; +} +} // namespace shapes diff --git a/RayTracer/shape/TriangleMesh.h b/RayTracer/shape/TriangleMesh.h new file mode 100644 index 0000000000000000000000000000000000000000..d612b7bad6b6a32a4267ee66e65237f61ba84432 --- /dev/null +++ b/RayTracer/shape/TriangleMesh.h @@ -0,0 +1,32 @@ +#pragma once + +#include <vector> + +#include "../tools/Vertex.h" +#include "Group.h" +#include "Light.h" +#include "Shape.h" +#include "Triangle.h" + +namespace shapes { +class TriangleMesh : public Light, public Shape { + public: + TriangleMesh(std::vector<Triangle> triangles); + TriangleMesh(std::istream& in, + const std::shared_ptr<material::Material>& material); + std::optional<cam::Hit> intersect(const cam::Ray& r) const override; + util::AxisAlignedBoundingBox bounds() const override; + + util::SurfacePoint sampleLight() const override; + util::Vec3 calculateLightEmission(const util::SurfacePoint& p, + const util::Vec3& d) const override; + + public: + std::shared_ptr<material::Material> material; + std::vector<Triangle> triangles; + + private: + Group hierarchy; + util::AxisAlignedBoundingBox initBB(); +}; +} // namespace shapes diff --git a/RayTracer/tools/ObjectLoader.cpp b/RayTracer/tools/ObjectLoader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d9c25d20a736f5b2fa778bfd1c00fb65a2740510 --- /dev/null +++ b/RayTracer/tools/ObjectLoader.cpp @@ -0,0 +1,76 @@ +#pragma once + +#include "ObjectLoader.h" + +#include <sstream> + +#include "Vertex.h" + +namespace util { +std::vector<shapes::Triangle> loadObj( + std::istream& in, const std::shared_ptr<material::Material>& material) { + std::vector<Vertex> vertices; + std::vector<Vec3> v; + std::vector<Vec3> vt; + std::vector<Vec3> vn; + + std::string lineStr; + while (std::getline(in, lineStr)) { + std::istringstream lineSS(lineStr); + std::string lineType; + lineSS >> lineType; + + // vertex + if (lineType == "v") { + float x = 0, y = 0, z = 0; + lineSS >> x >> y >> z; + v.push_back(Vec3(x, y, z)); + } + + // texture + if (lineType == "vt") { + float u = 0, v = 0; + lineSS >> u >> v; + vt.push_back(Vec3(u, v, 0)); + } + + // normal + if (lineType == "vn") { + float i = 0, j = 0, k = 0; + lineSS >> i >> j >> k; + vn.push_back(Vec3(i, j, k).normalize()); + } + + // polygon + if (lineType == "f") { + std::string refStr; + while (lineSS >> refStr) { + std::istringstream ref(refStr); + std::string vStr, vtStr, vnStr; + std::getline(ref, vStr, '/'); + std::getline(ref, vtStr, '/'); + std::getline(ref, vnStr, '/'); + int currentv = atoi(vStr.c_str()); + int currentvt = atoi(vtStr.c_str()); + int currentvn = atoi(vnStr.c_str()); + currentv = (currentv >= 0 ? currentv : v.size() + currentv); + currentvt = + (currentvt >= 0 ? currentvt : vt.size() + currentvt); + currentvn = + (currentvn >= 0 ? currentvn : vn.size() + currentvn); + Vertex vert; + vert.position = v[currentv - 1]; + vert.texcoord = v[currentvt - 1]; + vert.normal = v[currentvn - 1]; + vertices.push_back(vert); + } + } + } + std::vector<shapes::Triangle> triangles; + for (int i = 0; i < vertices.size(); i += 3) { + triangles.push_back( + {vertices[i + 0], vertices[i + 1], vertices[i + 2], material}); + } + return triangles; +} +} // namespace util \ No newline at end of file diff --git a/RayTracer/tools/ObjectLoader.h b/RayTracer/tools/ObjectLoader.h new file mode 100644 index 0000000000000000000000000000000000000000..ec4db0d9bfd193bf442afd6118dceab419f0391c --- /dev/null +++ b/RayTracer/tools/ObjectLoader.h @@ -0,0 +1,12 @@ +#pragma once + +#include <fstream> +#include <vector> + +#include "../shape/Triangle.h" + +namespace util { +std::vector<shapes::Triangle> loadObj( + std::istream& in, + const std::shared_ptr<material::Material>& material = nullptr); +} // namespace util \ No newline at end of file