From 7eee648883aeda494734e59c41c97c21691a200d Mon Sep 17 00:00:00 2001 From: Syndamia Date: Mon, 1 Jul 2024 16:17:36 +0300 Subject: [PATCH] [project-medium] Impelemented PointClouds --- data/pointcloud.hexray | 175 +++++++++++++++++++++++++++++++++++++++++ flake.lock | 27 +++++++ flake.nix | 26 ++++++ src/main.cpp | 2 +- src/mesh.cpp | 163 ++++++++++++++++++++++++++++++++++++++ src/mesh.h | 9 ++- src/scene.cpp | 1 + 7 files changed, 401 insertions(+), 2 deletions(-) create mode 100644 data/pointcloud.hexray create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/data/pointcloud.hexray b/data/pointcloud.hexray new file mode 100644 index 0000000..2c60cb5 --- /dev/null +++ b/data/pointcloud.hexray @@ -0,0 +1,175 @@ +// + +// 1. First, some global settings +GlobalSettings { + frameWidth 960 + frameHeight 540 + ambientLight (0.28, 0.30, 0.35) + maxTraceDepth 5 + wantAA off + prepassSamples 0 + interactive on + //foveatedRadius 75 +} + +PointLight { + pos (160, 80, 40) + color (1, 1, 1) + power 10000 +} + +// 2. A camera +// (to enable the spherical lens camera (which introduces heavy spherical abberation)), replace "Camera" with "SphericalLensCamera" + +Camera camera { + pos (120, 22, 60) + yaw 11.46 + pitch -15 + roll 0.0 + fov 100 + aspectRatio 1.777 + //stereoSeparation 0.25 // comment this out to disable the stereoscopic effect + dof off + numSamples 100 + fNumber 5.6 + autoFocus off + focalPlaneDist 21.48 +} + +/* +// top view +Camera camera { + pos (130, 85, 75) + yaw 11.46 + pitch -90 + roll 0.0 + fov 90 + aspect 1.333 +} +*/ + +/// Floor /// + +Plane floor { + y 0 + limit 64 +} + +CheckerTexture checker { + color1 (0.5, 0.5, 0.25) + color2 (0.25, 0.25, 0.25) + scaling 8 +} + +Lambert floorShader { + color (1, 1, 1) + texture checker +} + +// 3. A floor node, using a plane as a geometry, and a flat shader with a checker texture +Node floorNode { + geometry floor + shader floorShader + translate (100, 0, 96) +} + +/// Dice /// + +Mesh dice { + file "geom/truncated_cube.obj" + faceted true +} + +BumpTexture diceBump { + file "texture/zar-bump.bmp" + strength 5 +} + +BitmapTexture dice_texture { + file "texture/zar-texture.bmp" + scaling 1 +} + +Lambert flat { + color (1,1,1) + texture dice_texture +} + +/// Wineglass /// + +PointCloud wineglass { + file "geom/newwine.obj" + backfaceCulling false +} + +Phong white_flat { + color (0.7, 0.7, 0.7) + exponent 200 +} + +Reflection reflection { + multiplier 0.96 +} + +Refraction refraction { + ior 1.5 + multiplier 0.96 +} + +Fresnel fresnel { + ior 1.5 +} + +Layered glass { + layer refraction (1, 1, 1) + layer reflection (1, 1, 1) fresnel +} + +Node wineglass { + geometry wineglass + shader white_flat + //shader glass + translate (126, 0, 80) + scale (17, 17, 17) +} + +/// Teapot /// + + +PointCloud teapot { + file "geom/teapot_hires.obj" +} + +CheckerTexture teapot_checker { + color1 (0.9, 0.1, 0.1) + color2 (0.05, 0.05, 0.07) + scaling 0.1 +} + +Phong teapot_lambert { + color (1, 1, 1) + texture teapot_checker + exponent 30 +} + +Layered teapot_layered { + layer teapot_lambert (1, 1, 1) + layer white_flat (0.16, 0.16, 0.16) + layer reflection (0.05, 0.07, 0.09) +} + +Node teapotNode { + geometry teapot + //shader teapot_layered + shader teapot_lambert + translate (110, 19, 90) + rotate (-27, 0, 30) + scale (7.5, 7.5, 7.5) +} + + + +// 5. The cubemap environment: +CubemapEnvironment environment { + folder "env/forest" +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..25db693 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1716061101, + "narHash": "sha256-H0eCta7ahEgloGIwE/ihkyGstOGu+kQwAiHvwVoXaA0=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "e7cc61784ddf51c81487637b3031a6dd2d6673a2", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..d0a99fd --- /dev/null +++ b/flake.nix @@ -0,0 +1,26 @@ +# Този файл, заедно с flake.lock са си за моета система, да имам копие ако случайно изтрия нещо +{ + description = "hexray raytracer in FMI elective"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11"; + }; + + outputs = inputs: + let + pkgs = inputs.nixpkgs.legacyPackages."x86_64-linux"; + in { + devShells."x86_64-linux".default = pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + cmake + pkg-config + ]; + + buildInputs = with pkgs; [ + SDL2 + openexr_3 + zlib + ]; + }; + }; +} diff --git a/src/main.cpp b/src/main.cpp index fdca0f5..61386f3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -525,7 +525,7 @@ void renderThreadEntry() { } } -const char* DEFAULT_SCENE = "data/simple.hexray"; +const char* DEFAULT_SCENE = "data/pointcloud.hexray"; int main(int argc, char** argv) { diff --git a/src/mesh.cpp b/src/mesh.cpp index 36ea203..9bf3ea2 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -394,3 +394,166 @@ void Mesh::prepareTriangles() t.dNdy.normalize(); } } + +/* Triangle operations */ + +Vector triangleNormal(const Triangle& t, const std::vector& points) { + return normalize((points[t.v[1]] - points[t.v[0]]) ^ (points[t.v[2]] - points[t.v[0]])); +} + +bool operator==(const Triangle& lhs, const Triangle& rhs) { + return + (lhs.v[0] == rhs.v[0] || lhs.v[0] == rhs.v[1] || lhs.v[0] == rhs.v[2]) && + (lhs.v[1] == rhs.v[0] || lhs.v[1] == rhs.v[1] || lhs.v[1] == rhs.v[2]) && + (lhs.v[2] == rhs.v[0] || lhs.v[2] == rhs.v[1] || lhs.v[2] == rhs.v[2]); +} + +/* Closest two points */ + +struct ClosePoint { + int index = -1; + double distance = INFINITY; + + ClosePoint& operator=(const ClosePoint& rhs) { + this->index = rhs.index; + this->distance = rhs.distance; + return *this; + } + + friend bool operator<(const ClosePoint& lhs, const ClosePoint& rhs) { + return lhs.distance < rhs.distance; + } +}; + +std::pair closestTwoPoints(const Vector& p, const std::vector& points, const std::vector& unavailable) { + ClosePoint first, second; + for (ClosePoint curr = { 0 }; curr.index < points.size(); curr.index++) { + if (unavailable[curr.index]) continue; + + curr.distance = (p - points[curr.index]).length(); + // curr == p + if (curr.distance < 0.00001) continue; + + // would first, second and p form a (left-direction) triangle + bool perpenToFirst = first.index != -1; + if (perpenToFirst) { + double dotp = dot(normalize(p - points[first.index]), normalize(p - points[curr.index])); + perpenToFirst = 0.0 < dotp && dotp < 0.2; + } + + if (curr < first) { + if (perpenToFirst) + second = first; + first = curr; + } + else if (curr < second && perpenToFirst) { + second = curr; + } + } + return { first.index, second.index }; +} + +/* Generate triangles */ + +struct MarkedVertices { +private: + int size; +public: + std::vector markings; + int total; + + MarkedVertices(int size) { + this->size = size; + markings = std::vector(size, false); + total = 0; + } + + bool all() { + return total >= size; + } + + void add(int index) { + markings[index] = true; + total++; + } + + void clear() { + markings.assign(size, false); + total = 0; + } +}; + +void PointCloud::generateTriangles() { + MarkedVertices unavailable(vertices.size()); + std::pair triangleForming; + bool validTriangle; + Triangle t; + t.t[0] = 0; t.t[1] = 0; t.t[2] = 0; + + for (int i = 0; i < vertices.size(); i++) { + t.v[0] = i; + + unavailable.clear(); + do { + validTriangle = false; + + triangleForming = closestTwoPoints(vertices[i], vertices, unavailable.markings); + if (triangleForming.first < 0 || triangleForming.second < 0) break; + + t.v[1] = triangleForming.first; + t.v[2] = triangleForming.second; + unavailable.add(triangleForming.first); + unavailable.add(triangleForming.second); + + validTriangle = true; + + for (const Triangle& ct : triangles) { + if (ct == t) { + validTriangle = false; + break; + } + } + } while (!validTriangle && !unavailable.all()); + + if (validTriangle) { + triangles.push_back(t); + } + } +} + +bool PointCloud::loadFromOBJ(const char* filename) +{ + FILE* f = fopen(filename, "rt"); + + if (!f) return false; + + Vector sentinel(0, 0, 0); + vertices = {sentinel}; + normals = {sentinel}; + uvs = {sentinel}; + + char line[10000]; + while (fgets(line, sizeof(line), f)) { + if (line[0] == '#') continue; + // + std::vector tokens = tokenize(line); + if (tokens.empty()) continue; + // + if (tokens[0] == "v") { + vertices.push_back(Vector( + toDouble(tokens[1]), + toDouble(tokens[2]), + toDouble(tokens[3]) + )); + continue; + } + } + + fclose(f); + + printf("Calculating triangle mesh for %s...\n", filename); + generateTriangles(); + + prepareTriangles(); + return true; +} diff --git a/src/mesh.h b/src/mesh.h index e300d6a..f8f0e5c 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -94,7 +94,7 @@ public: ~Mesh(); - bool loadFromOBJ(const char* filename); + virtual bool loadFromOBJ(const char* filename); void baseProperties(ParsedBlock& pb) { @@ -122,3 +122,10 @@ public: virtual bool intersect(const Ray& ray, IntersectionInfo& info) override; }; + +class PointCloud : public Mesh { + void generateTriangles(); + +public: + virtual bool loadFromOBJ(const char* filename) override; +}; diff --git a/src/scene.cpp b/src/scene.cpp index e319eeb..410129d 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -798,6 +798,7 @@ SceneElement* DefaultSceneParser::newSceneElement(const char* className) if (!strcmp(className, "CubemapEnvironment")) return new CubemapEnvironment; if (!strcmp(className, "Camera")) return new Camera; if (!strcmp(className, "Mesh")) return new Mesh; + if (!strcmp(className, "PointCloud")) return new PointCloud; if (!strcmp(className, "Heightfield")) return new Heightfield; if (!strcmp(className, "BumpTexture")) return new BumpTexture; if (!strcmp(className, "Bumps")) return new Bumps; -- 2.42.0