/* * Created by Brett Terpstra 6920201 on 16/10/22. * Copyright (c) 2022 Brett Terpstra. All Rights Reserved. */ #ifndef STEP_2_WORLD_H #define STEP_2_WORLD_H #include #include #include #include #include #include namespace Raytracing { class SphereObject : public Object { private: PRECISION_TYPE radius; public: SphereObject(const Vec4& position, PRECISION_TYPE radius, Material* material): radius(radius), Object(material, position) {} [[nodiscard]] virtual HitData checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const; virtual Object* clone(){ return new SphereObject(position, radius, material); } }; class TriangleObject : public Object { private: Triangle theTriangle; public: TriangleObject(const Vec4& position, Triangle theTriangle, Material* material): Object(material, position), theTriangle(std::move(theTriangle)) {} [[nodiscard]] virtual HitData checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const; virtual Object* clone() { return new TriangleObject(position, theTriangle, material); } }; class ModelObject : public Object { private: std::vector triangles; ModelData& data; // basically we have to store this crap here because c++ loves to copy stuff std::vector createdTreeObjects; BVHTree* tree; public: ModelObject(const Vec4& position, ModelData& data, Material* material): Object(material, position), data(data) { // since all of this occurs before the main ray tracing algorithm it's fine to do sequentially triangles = data.toTriangles(); this->aabb = data.aabb; createdTreeObjects = Raytracing::ModelData::createBVHTree(triangles, position); tree = new BVHTree(createdTreeObjects); } [[nodiscard]] virtual HitData checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const; virtual Object* clone() { return new ModelObject(position, data, material); } virtual ~ModelObject() { for (auto* p : createdTreeObjects) delete(p); delete(tree); } }; class DiffuseMaterial : public Material { private: public: explicit DiffuseMaterial(const Vec4& scatterColor): Material(scatterColor) {} [[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const; }; class MetalMaterial : public Material { protected: static inline Vec4 reflect(const Vec4& incomingVector, const Vec4& normal) { return incomingVector - 2 * Vec4::dot(incomingVector, normal) * normal; } public: explicit MetalMaterial(const Vec4& metalColor): Material(metalColor) {} [[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const; }; class BrushedMetalMaterial : public MetalMaterial { private: PRECISION_TYPE fuzzyness; public: explicit BrushedMetalMaterial(const Vec4& metalColor, PRECISION_TYPE fuzzyness): MetalMaterial(metalColor), fuzzyness(fuzzyness) {} [[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const; }; class World { private: // store all the objects in the world, std::vector objects; /*TODO: create a kd-tree or bvh version to store the objects * this way we can easily tell if a ray is near and object or not * saving on computation */ // TODO: the above todo has been done, now we need to test the performance advantage of the BVH BVHTree* bvhTree = nullptr; std::unordered_map materials; public: World() = default; World(const World& world) = delete; World(const World&& world) = delete; // call this after you've added all the objects to the world. (Called by the raycaster class) void generateBVH(); inline void add(Object* object) { objects.push_back(object); } inline void addMaterial(const std::string& materialName, Material* mat) { materials.insert({materialName, mat}); } inline Material* getMaterial(const std::string& materialName) { return materials.at(materialName); } [[nodiscard]] virtual std::pair checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const; ~World(); }; } #endif //STEP_2_WORLD_H