├── VerStarting ├── NetSock │ ├── NetSock.url │ ├── README.md │ ├── test.cpp │ ├── NetSock.h │ ├── LICENSE │ └── NetSock.cpp ├── light.h ├── scene.h ├── test_helper.cc ├── aabb.h ├── ray.h ├── texture.h ├── primitive_triangle.h ├── camera.h ├── test_helper.h ├── primitive.h ├── octtree_test.cc ├── material.h ├── aabb.cc ├── objreader.h ├── math3d_test.cc ├── Makefile ├── octtree.h ├── mythtracer.h ├── network.h ├── texture.cc ├── camera.cc ├── main_local.cc ├── network.cc ├── main_net_worker.cc ├── primitive_triangle.cc ├── octtree.cc ├── math3d.h ├── main_net_master.cc ├── mythtracer.cc └── objreader.cc ├── README.md ├── .gitignore └── LICENSE /VerStarting/NetSock/NetSock.url: -------------------------------------------------------------------------------- 1 | [InternetShortcut] 2 | URL=https://github.com/gynvael/NetSock 3 | -------------------------------------------------------------------------------- /VerStarting/light.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "math3d.h" 3 | 4 | namespace raytracer { 5 | 6 | using math3d::V3D; 7 | 8 | class Light { 9 | public: 10 | V3D position; 11 | V3D ambient; 12 | V3D diffuse; 13 | V3D specular; 14 | }; 15 | 16 | 17 | }; // namespace raytracer 18 | 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MythTracer 2 | Just another ray tracer. Actually it's an educational raytracer I made as an example of something that can be both optimized and parallelized for my programming livestreams. Don't expect too much from it :) 3 | 4 | Model which I'm using: 5 | https://www.cgtrader.com/free-3d-models/interior/living-room/living-room-ussu-design 6 | 7 | -------------------------------------------------------------------------------- /VerStarting/scene.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "octtree.h" 4 | #include "material.h" 5 | #include "light.h" 6 | 7 | namespace raytracer { 8 | 9 | class Scene { 10 | public: 11 | OctTree tree; 12 | MaterialMap materials; 13 | TextureMap textures; 14 | std::vector lights; 15 | }; 16 | 17 | }; 18 | -------------------------------------------------------------------------------- /VerStarting/test_helper.cc: -------------------------------------------------------------------------------- 1 | #include "test_helper.h" 2 | 3 | namespace test { 4 | 5 | bool EqVectors(const V3D& a, 6 | const V3D& b, 7 | V3D::basetype epsilon) { 8 | const V3D::basetype dx = fabs(a.x() - b.x()); 9 | const V3D::basetype dy = fabs(a.y() - b.y()); 10 | const V3D::basetype dz = fabs(a.z() - b.z()); 11 | return dx < epsilon && dy < epsilon && dz < epsilon; 12 | } 13 | 14 | } // namespace test 15 | -------------------------------------------------------------------------------- /VerStarting/aabb.h: -------------------------------------------------------------------------------- 1 | #include "math3d.h" 2 | 3 | namespace raytracer { 4 | 5 | using math3d::V3D; 6 | 7 | class AABB { 8 | public: 9 | bool FullyContains(const AABB& aabb) const; 10 | bool Contains(const AABB& aabb) const; 11 | bool Contains(const V3D& point) const; 12 | void Extend(const AABB& aabb); 13 | void Extend(const V3D& point); 14 | std::pair GetCenterWHD() const; 15 | 16 | V3D min, max; 17 | }; 18 | 19 | } // namespace raytracer 20 | 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Images 35 | *.png 36 | *.raw 37 | *.mp4 38 | *.avi 39 | *.gif 40 | 41 | # Models 42 | Models/ 43 | 44 | # Random things 45 | Attic/ 46 | -------------------------------------------------------------------------------- /VerStarting/NetSock/README.md: -------------------------------------------------------------------------------- 1 | # NetSock 2 | A simple networking (socket) library for C++ for Windows and systems 3 | based on Linux kernel. It shouldn't really be used for any real stuff, 4 | but it's OK for prototyping and small tools. Also, please note that 5 | this library was created about 10 years ago and may contain bugs. A 6 | lot of bugs. You have been warned :) 7 | 8 | **NOTE**: It does `SO\_REUSEADDR` on every listening socket - you might 9 | want to comment it out on a server where you're worried about other 10 | apps binding to the same port. 11 | 12 | See LICENSE file for licensing details. 13 | 14 | 15 | -------------------------------------------------------------------------------- /VerStarting/ray.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "math3d.h" 4 | 5 | namespace raytracer { 6 | 7 | using math3d::V3D; 8 | 9 | class OctTree; 10 | class Triangle; 11 | 12 | class Ray { 13 | public: 14 | Ray(V3D org, V3D dir) : origin(org), direction(dir) { } 15 | V3D origin; 16 | V3D direction; // Assume and always make sure the direction vector is 17 | // normalized. 18 | 19 | private: 20 | friend OctTree; 21 | friend Triangle; 22 | V3D inv_direction; // 1.0 / direction, used by octtree/triangle for some 23 | // optimizations. 24 | }; 25 | 26 | } // namespace raytracer 27 | 28 | -------------------------------------------------------------------------------- /VerStarting/texture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "math3d.h" 7 | #include "texture.h" 8 | 9 | namespace raytracer { 10 | 11 | using math3d::V3D; 12 | 13 | class Texture { 14 | public: 15 | // Retrieves the interpolated color for the uv location at the given distance. 16 | V3D GetColorAt(double u, double v, double distance) const; 17 | 18 | static Texture *LoadFromFile(const char *fname); 19 | 20 | size_t width = 0; 21 | size_t height = 0; 22 | std::vector colors; 23 | }; 24 | 25 | typedef std::unordered_map> TextureMap; 26 | 27 | } // namespace raytracer 28 | 29 | -------------------------------------------------------------------------------- /VerStarting/NetSock/test.cpp: -------------------------------------------------------------------------------- 1 | // Windows : g++ test.cpp NetSock.cpp -lws2_32 2 | // GNU/Linux: g++ test.cpp 3 | #include 4 | #include "NetSock.h" 5 | 6 | int main() { 7 | NetSock::InitNetworking(); // Initialize WinSock 8 | 9 | NetSock s; 10 | int ret; 11 | unsigned char buffer[8] = {0}; 12 | 13 | if (!s.Connect("127.0.0.1", 1333)) 14 | return 1; // Some error handling. 15 | 16 | // Write some ASCII string. 17 | ret = s.Write((unsigned char*)"asdf", 4); 18 | if (ret != 4) 19 | return 2; // Some error handling. 20 | 21 | // Read some ASCII string. 22 | ret = s.Read(buffer, sizeof(buffer) - 1); 23 | if (ret <= 0) 24 | return 3; // Some error handling. 25 | 26 | // Write out the string. 27 | puts((char*)buffer); 28 | 29 | s.Disconnect(); 30 | 31 | return 0; 32 | } 33 | 34 | -------------------------------------------------------------------------------- /VerStarting/primitive_triangle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include "primitive.h" 7 | 8 | namespace raytracer { 9 | 10 | class Triangle : public Primitive { 11 | public: 12 | ~Triangle() override; 13 | AABB GetAABB() const override; 14 | bool IntersectRay(const Ray& ray, V3D *point, 15 | V3D::basetype *distance) const override; 16 | V3D GetNormal(const V3D& point) const override; 17 | V3D GetUVW(const V3D& point) const override; 18 | 19 | std::string Serialize() const override; 20 | static bool Deserialize( 21 | std::unique_ptr *primitive, 22 | const std::string& data); 23 | 24 | void CacheAABB(); 25 | 26 | V3D vertex[3]{}; 27 | V3D normal[3]{}; 28 | V3D uvw[3]{}; 29 | AABB cached_aabb; 30 | }; 31 | 32 | } // namespace raytracer 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Gynvael Coldwind 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /VerStarting/camera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "math3d.h" 5 | #include "ray.h" 6 | 7 | namespace raytracer { 8 | 9 | using math3d::V3D; 10 | using math3d::M4D; 11 | 12 | class Camera { 13 | public: 14 | class Sensor { 15 | public: 16 | Ray GetRay(int x, int y) const; 17 | 18 | private: 19 | void Reset(); 20 | V3D delta_scanline; 21 | V3D delta_pixel; 22 | V3D start_point; 23 | 24 | // Set by parent. 25 | int width, height; 26 | const Camera *cam; 27 | 28 | friend Camera; 29 | }; 30 | 31 | V3D origin; 32 | V3D::basetype pitch, yaw, roll; // X Y and Z accis. 33 | V3D::basetype aov; // Angle of view, in degrees. 34 | 35 | V3D GetDirection() const; 36 | Sensor GetSensor(int width, int height) const; 37 | 38 | static const size_t kSerializedSize = 39 | /* origin */ sizeof(V3D) + 40 | /* pitch */ sizeof(V3D::basetype) + 41 | /* yaw */ sizeof(V3D::basetype) + 42 | /* roll */ sizeof(V3D::basetype) + 43 | /* aov */ sizeof(V3D::basetype); 44 | void Serialize(std::vector *bytes); 45 | bool Deserialize(const std::vector& bytes); 46 | }; 47 | 48 | } // namespace raytracer 49 | 50 | -------------------------------------------------------------------------------- /VerStarting/test_helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include "math3d.h" 7 | 8 | using math3d::V3D; 9 | using math3d::ToStr; 10 | 11 | namespace test { 12 | 13 | template 14 | inline void TestErrorMsg(T a, T b, const char *str_a, int line) { 15 | std::cerr << "Test at line " << line << " failed: " << str_a << "\n" 16 | << " was : " << a << "\n" 17 | << " should be: " << b << std::endl; 18 | } 19 | 20 | template 21 | inline void TestEq(T a, T b, const char *str_a, int line) { 22 | if (a != b) { 23 | TestErrorMsg(a, b, str_a, line); 24 | } 25 | } 26 | 27 | template <> 28 | inline void TestEq(double a, double b, const char *str_a, int line) { 29 | if (fabs(a - b) >= 0.0000001) { 30 | TestErrorMsg(a, b, str_a, line); 31 | } 32 | } 33 | 34 | template <> 35 | inline void TestEq(float a, float b, const char *str_a, int line) { 36 | if (fabs(a - b) >= 0.0000001f) { 37 | TestErrorMsg(a, b, str_a, line); 38 | } 39 | } 40 | 41 | template <> 42 | inline void TestEq(long double a, long double b, const char *str_a, int line) { 43 | if (fabs(a - b) >= 0.0000001L) { 44 | TestErrorMsg(a, b, str_a, line); 45 | } 46 | } 47 | 48 | bool EqVectors(const V3D& a, 49 | const V3D& b, 50 | V3D::basetype epsilon); 51 | 52 | template <> 53 | inline void TestEq(V3D a, V3D b, const char *str_a, int line) { 54 | if (!EqVectors(a, b, 0.0000001)) { 55 | TestErrorMsg(a, b, str_a, line); 56 | } 57 | } 58 | 59 | #define TESTEQ(a, b) TestEq((a), (b), #a, __LINE__) 60 | 61 | } // namespace test 62 | -------------------------------------------------------------------------------- /VerStarting/primitive.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include "aabb.h" 7 | #include "math3d.h" 8 | #include "ray.h" 9 | #include "material.h" 10 | 11 | namespace raytracer { 12 | 13 | class Primitive { 14 | public: 15 | virtual ~Primitive() { }; 16 | 17 | // Returns the axis-aligned bounding box of the primitive. 18 | virtual AABB GetAABB() const = 0; 19 | 20 | // Returns true if the primitive intersected with the given ray, as well as 21 | // the point of intersection and distance from ray origin to the intersection 22 | // point. 23 | virtual bool IntersectRay(const Ray& ray, V3D *point, 24 | V3D::basetype *distance) const = 0; 25 | 26 | // Returns normal in the specified point. 27 | virtual V3D GetNormal(const V3D& point) const = 0; 28 | 29 | // Returns texture coords (UVW mapping) at the specified point. 30 | virtual V3D GetUVW(const V3D& point) const = 0; 31 | 32 | // Return serialized primitive. 33 | // TODO(gynvael): Actually provide an implementation of this to dump all the 34 | // common properties. Perhaps also add a deserialize method or function. 35 | virtual std::string Serialize() const = 0; 36 | // Note: Each implementation should have a Deserialize method: 37 | // static bool Deserialize( 38 | // std::unique_ptr *primitive, const std::string& data); 39 | 40 | // Common primitive properties go here. 41 | Material *mtl = nullptr; // The primitive is not the owner of this object. 42 | int debug_line_no = 0; // Line in the input file (if any) where this 43 | // primitive was defined. 44 | }; 45 | 46 | } // namespace raytracer 47 | -------------------------------------------------------------------------------- /VerStarting/octtree_test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "octtree.h" 3 | #include "primitive_triangle.h" 4 | #include "test_helper.h" 5 | 6 | 7 | using namespace test; 8 | using raytracer::OctTree; 9 | using raytracer::Primitive; 10 | using raytracer::Triangle; 11 | using raytracer::Ray; 12 | using math3d::V3D; 13 | 14 | int main(void) { 15 | OctTree tree; 16 | 17 | // + 1,1 18 | // /| 19 | // / | 20 | // 0,0 +--+ 1,0 21 | Triangle *tr0 = new Triangle(); 22 | tr0->vertex[0] = { 1, 1, 0 }; 23 | tr0->vertex[1] = { 1, 0, 0 }; 24 | tr0->vertex[2] = { 0, 0, 0 }; 25 | tree.AddPrimitive(tr0); 26 | 27 | Triangle *tr1 = new Triangle(); 28 | tr1->vertex[0] = { 1, 1, 1 }; 29 | tr1->vertex[1] = { 1, 0, 1 }; 30 | tr1->vertex[2] = { 0, 0, 1 }; 31 | tree.AddPrimitive(tr1); 32 | 33 | tree.Finalize(); 34 | 35 | { 36 | Ray front{ 37 | { 0.9, 0.9, -10.0 }, 38 | { 0.0, 0.0, 1.0 } 39 | }; 40 | 41 | V3D point; 42 | V3D::basetype distance; 43 | auto p = tree.IntersectRay(front, &point, &distance); 44 | TESTEQ(p, (const Primitive*)tr0); 45 | } 46 | 47 | { 48 | Ray back{ 49 | { 0.9, 0.9, 10.0 }, 50 | { 0.0, 0.0, -1.0 } 51 | }; 52 | 53 | V3D point; 54 | V3D::basetype distance; 55 | auto p = tree.IntersectRay(back, &point, &distance); 56 | TESTEQ(p, (const Primitive*)tr1); 57 | } 58 | 59 | { 60 | Ray miss{ 61 | { 5.0, 5.0, 5.0 }, 62 | { 0.0, 0.0, 1.0 } 63 | }; 64 | 65 | V3D point; 66 | V3D::basetype distance; 67 | auto p = tree.IntersectRay(miss, &point, &distance); 68 | TESTEQ(p, (const Primitive*)nullptr); 69 | } 70 | 71 | 72 | return 0; 73 | } 74 | 75 | -------------------------------------------------------------------------------- /VerStarting/material.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "math3d.h" 6 | #include "texture.h" 7 | 8 | namespace raytracer { 9 | 10 | using math3d::V3D; 11 | 12 | class Material { 13 | public: 14 | // Ambient lightning for the object, as well as diffuse and specular 15 | // reflectivity strength/color. 16 | // In MTL files: Ka, Kd and Ks respectively. 17 | V3D ambient{}, diffuse{}, specular{}; 18 | 19 | // Pointer to a texture object that can be used together with ambient 20 | // to get the color. 21 | Texture *tex = nullptr; 22 | 23 | // The exponent for the specular reflectiveness. The higher the exponenta, 24 | // the more condense the blink is. 25 | // In MTL files: Ns. 26 | V3D::basetype specular_exp = 0.0; 27 | 28 | // The mirror-like effect of the surface. Zero means no reflection at all. 29 | // In MTL files: Refl (non-standard). 30 | V3D::basetype reflectance = 0.0; 31 | 32 | // The transparency of the the surface. Zero means full opaque. 33 | // In MTL files: Tr (or d as d=1-Tr). 34 | V3D::basetype transparency = 0.0; 35 | 36 | // TODO(gynvael): Add translucancy coeficient and diffused reflection. 37 | 38 | // The color filter of the material (only if it's translucent) using RGB 39 | // model. E.g. 0 1 1 means "green and blue go through, but red is removed 40 | // from the light color". 41 | // In MTL files: Tf. 42 | V3D transmission_filter{}; 43 | 44 | // The index of refraction, i.e. how much does the light bend when entering 45 | // the surface. 46 | // In MTL files: Ni. 47 | V3D::basetype refraction_index = 0.0; 48 | }; 49 | 50 | typedef std::unordered_map> MaterialMap; 51 | 52 | } // namespace raytracer 53 | 54 | -------------------------------------------------------------------------------- /VerStarting/aabb.cc: -------------------------------------------------------------------------------- 1 | #include "aabb.h" 2 | 3 | namespace raytracer { 4 | 5 | bool AABB::FullyContains(const AABB& aabb) const { 6 | return Contains(aabb.min) && Contains(aabb.max); 7 | } 8 | 9 | bool AABB::Contains(const AABB& aabb) const { 10 | // Calculate the distance between the centers and compare vs width, height and 11 | // depth. 12 | auto this_pair = GetCenterWHD(); 13 | auto aabb_pair = aabb.GetCenterWHD(); 14 | 15 | auto& this_center = this_pair.first; 16 | auto& this_whd = this_pair.second; 17 | 18 | auto& aabb_center = aabb_pair.first; 19 | auto& aabb_whd = aabb_pair.second; 20 | 21 | return ((fabs(this_center.v[0] - aabb_center.v[0]) * 2.0 <= 22 | this_whd.v[0] + aabb_whd.v[0])) && 23 | ((fabs(this_center.v[1] - aabb_center.v[1]) * 2.0 <= 24 | this_whd.v[1] + aabb_whd.v[1])) && 25 | ((fabs(this_center.v[2] - aabb_center.v[2]) * 2.0 <= 26 | this_whd.v[2] + aabb_whd.v[2])); 27 | } 28 | 29 | bool AABB::Contains(const V3D& point) const { 30 | return point.v[0] >= min.v[0] && point.v[0] <= max.v[0] && 31 | point.v[1] >= min.v[1] && point.v[1] <= max.v[1] && 32 | point.v[2] >= min.v[2] && point.v[2] <= max.v[2]; 33 | } 34 | 35 | void AABB::Extend(const AABB& aabb) { 36 | for (int i = 0; i < 3; i++) { 37 | min.v[i] = std::min(min.v[i], aabb.min.v[i]); 38 | max.v[i] = std::max(max.v[i], aabb.max.v[i]); 39 | } 40 | } 41 | 42 | void AABB::Extend(const V3D& point) { 43 | for (int i = 0; i < 3; i++) { 44 | min.v[i] = std::min(min.v[i], point.v[i]); 45 | max.v[i] = std::max(max.v[i], point.v[i]); 46 | } 47 | } 48 | 49 | std::pair AABB::GetCenterWHD() const { 50 | return { 51 | min + (max - min) / 2, 52 | max - min 53 | }; 54 | } 55 | 56 | } // namespace raytracer 57 | -------------------------------------------------------------------------------- /VerStarting/objreader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // Wavefront .obj 3D scene and .mtl material readers. 3 | #include 4 | #include 5 | #include 6 | #include "scene.h" 7 | #include "math3d.h" 8 | 9 | namespace raytracer { 10 | 11 | using math3d::V3D; 12 | 13 | class ObjFileReader { 14 | public: 15 | bool ReadObjFile(Scene *scene, const char *fname); 16 | 17 | private: 18 | bool ReadVertex(const char *line); 19 | bool ReadNormals(const char *line); 20 | bool ReadUVW(const char *line); 21 | bool ReadFace(const char *line); 22 | bool ReadMaterialLibrary(const char *line); 23 | bool ReadUseMaterial(const char *line); 24 | bool ReadNotImplemented(const char *); 25 | 26 | std::string base_directory; 27 | Scene *scene; 28 | 29 | // Temporary storage used while processing. 30 | std::vector vertices; 31 | std::vector texcoords; 32 | std::vector normals; 33 | Material *selected_material; 34 | int line_no; 35 | }; 36 | 37 | class MtlFileReader { 38 | public: 39 | bool ReadMtlFile(Scene *scene, const char *fname); 40 | 41 | private: 42 | void CommitMaterial(); 43 | bool ReadNewMaterial(const char *line); 44 | bool ReadAmbient(const char *line); 45 | bool ReadDiffuse(const char *line); 46 | bool ReadSpecular(const char *line); 47 | bool ReadSpecularExp(const char *line); 48 | bool ReadReflectance(const char *line); 49 | bool ReadTransparancy(const char *line); 50 | bool ReadTransmissionFilter(const char *line); 51 | bool ReadRefractionIndex(const char *line); 52 | bool ReadTexture(const char *line); 53 | bool ReadNotImplemented(const char *); 54 | 55 | Texture *GetTexture(const char *fname); 56 | 57 | std::string base_directory; 58 | Scene *scene; 59 | 60 | // Temporary storage used while processing. 61 | std::unique_ptr mtl; 62 | std::string mtl_name; 63 | }; 64 | 65 | }; // namespace raytracer 66 | 67 | -------------------------------------------------------------------------------- /VerStarting/math3d_test.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "math3d.h" 6 | #include "test_helper.h" 7 | 8 | using namespace test; 9 | using math3d::V3D; 10 | using math3d::ToStr; 11 | 12 | int main(void) { 13 | V3D a{1.0, 2.0, 3.0}; 14 | TESTEQ(a, (V3D{1.0, 2.0, 3.0})); 15 | 16 | V3D b; 17 | TESTEQ(b, (V3D{0.0, 0.0, 0.0})); 18 | 19 | b.x() = 4.0; 20 | b.y() = 5.0; 21 | b.z() = 6.0; 22 | TESTEQ(b, (V3D{4.0, 5.0, 6.0})); 23 | 24 | V3D c(a); 25 | TESTEQ(c, (V3D{1.0, 2.0, 3.0})); 26 | 27 | c = b; 28 | TESTEQ(c, (V3D{4.0, 5.0, 6.0})); 29 | 30 | c = a; 31 | TESTEQ(c += a, (V3D{2.0, 4.0, 6.0})); 32 | 33 | c = a; 34 | TESTEQ(c -= a, (V3D{0.0, 0.0, 0.0})); 35 | 36 | c = a; 37 | TESTEQ(c *= a, (V3D{1.0, 4.0, 9.0})); 38 | 39 | c = a; 40 | TESTEQ(c /= a, (V3D{1.0, 1.0, 1.0})); 41 | 42 | c = a; 43 | TESTEQ(c *= 3.0, (V3D{3.0, 6.0, 9.0})); 44 | 45 | c = a; 46 | TESTEQ(c *= 3.0, (V3D{3.0, 6.0, 9.0})); 47 | 48 | c = a; 49 | TESTEQ(c + a, (V3D{2.0, 4.0, 6.0})); 50 | TESTEQ(c - a, (V3D{0.0, 0.0, 0.0})); 51 | TESTEQ(c * a, (V3D{1.0, 4.0, 9.0})); 52 | TESTEQ(c / a, (V3D{1.0, 1.0, 1.0})); 53 | TESTEQ(-c, (V3D{-1.0, -2.0, -3.0})); 54 | TESTEQ(+c, (V3D{1.0, 2.0, 3.0})); 55 | 56 | c = (V3D{1.0, 0.0, 0.0}); 57 | TESTEQ(c.Length(), 1.0); 58 | TESTEQ(c.SqrLength(), 1.0); 59 | 60 | c = (V3D{0.0, 1.0, 0.0}); 61 | TESTEQ(c.Length(), 1.0); 62 | TESTEQ(c.SqrLength(), 1.0); 63 | 64 | c = (V3D{0.0, 0.0, 1.0}); 65 | TESTEQ(c.Length(), 1.0); 66 | TESTEQ(c.SqrLength(), 1.0); 67 | 68 | c = (V3D{1.0, 2.0, 3.0}); 69 | TESTEQ(c.Length(), 3.7416573867739413); 70 | TESTEQ(c.SqrLength(), 14.0); 71 | 72 | a = (V3D{1.0, 1.0, 1.0}); 73 | b = (V3D{2.0, 2.0, 2.0}); 74 | TESTEQ(a.Distance(b), b.Distance(a)); 75 | TESTEQ(a.Distance(b), 1.7320508075688772); 76 | 77 | a = (V3D{1.0, 2.0, 3.0}); 78 | b = (V3D{5.0, 4.0, 3.0}); 79 | TESTEQ(a.Dot(b), b.Dot(a)); 80 | TESTEQ(a.Dot(b), 22.0); 81 | 82 | TESTEQ(a.Cross(b), (V3D{-6.0, 12.0, -6.0})); 83 | TESTEQ(b.Cross(a), (V3D{6.0, -12.0, 6.0})); 84 | 85 | a = (V3D{1.0, 2.0, 3.0}); 86 | b = a; 87 | a.Norm(); 88 | TESTEQ(a, (V3D{0.2672612419124, 0.5345224838248, 0.8017837257372})); 89 | TESTEQ(b.DupNorm(), (V3D{0.2672612419124, 0.5345224838248, 0.8017837257372})); 90 | 91 | return 0; 92 | } 93 | 94 | -------------------------------------------------------------------------------- /VerStarting/Makefile: -------------------------------------------------------------------------------- 1 | CXX=g++ 2 | CFLAGS=-Wall -Wextra -O3 -ggdb -fno-omit-frame-pointer \ 3 | -std=c++1z \ 4 | -D_USE_MATH_DEFINES \ 5 | #-march=broadwell -mtune=intel -mavx2 6 | 7 | ifeq ($(OS),Windows_NT) 8 | WINSOCK=-lws2_32 9 | else 10 | WINSOCK= 11 | endif 12 | 13 | 14 | %.o: %.cc 15 | $(CXX) $(CFLAGS) -c -o $@ $< -fopenmp 16 | 17 | math3d_test: math3d_test.o test_helper.o 18 | $(CXX) $(CFLAGS) \ 19 | math3d_test.o \ 20 | test_helper.o \ 21 | -o math3d_test 22 | 23 | octtree_test: octtree_test.o aabb.o octtree.o primitive_triangle.o test_helper.o 24 | $(CXX) $(CFLAGS) \ 25 | octtree_test.o \ 26 | octtree.o \ 27 | primitive_triangle.o \ 28 | test_helper.o \ 29 | aabb.o \ 30 | -o octtree_test 31 | 32 | mythtracer: mythtracer.o objreader.o octtree.o primitive_triangle.o aabb.o camera.o texture.o main_local.o 33 | $(CXX) $(CFLAGS) \ 34 | mythtracer.o \ 35 | objreader.o \ 36 | octtree.o \ 37 | primitive_triangle.o \ 38 | aabb.o \ 39 | camera.o \ 40 | texture.o \ 41 | main_local.o \ 42 | -o mythtracer \ 43 | -lgomp -lSDL2 -lSDL2_image 44 | 45 | mythtracer_worker: mythtracer.o objreader.o octtree.o primitive_triangle.o aabb.o camera.o texture.o main_net_worker.o network.o 46 | g++ $(CFLAGS) \ 47 | mythtracer.o \ 48 | objreader.o \ 49 | octtree.o \ 50 | primitive_triangle.o \ 51 | aabb.o \ 52 | camera.o \ 53 | texture.o \ 54 | main_net_worker.o \ 55 | network.o \ 56 | -o mythtracer_worker \ 57 | NetSock/NetSock.cpp \ 58 | -lgomp -lSDL2 -lSDL2_image $(WINSOCK) -static-libgcc -static-libstdc++ 59 | 60 | mythtracer_master: mythtracer.o objreader.o octtree.o primitive_triangle.o aabb.o camera.o texture.o main_net_master.o network.o 61 | g++ $(CFLAGS) \ 62 | mythtracer.o \ 63 | objreader.o \ 64 | octtree.o \ 65 | primitive_triangle.o \ 66 | aabb.o \ 67 | camera.o \ 68 | texture.o \ 69 | main_net_master.o \ 70 | network.o \ 71 | -o mythtracer_master \ 72 | NetSock/NetSock.cpp \ 73 | -lpthread -fopenmp -lSDL2 -lSDL2_image -lSDL2main \ 74 | -lgomp -lSDL2 -lSDL2_image $(WINSOCK) 75 | 76 | test: math3d_test octtree_test 77 | ./math3d_test 78 | ./octtree_test 79 | 80 | clean: 81 | ifeq ($(OS),Windows_NT) 82 | del *.o 83 | else 84 | rm *.o 85 | endif 86 | 87 | 88 | run: mythtracer_worker 89 | ./mythtracer_worker 90 | 91 | all: mythtracer test 92 | 93 | -------------------------------------------------------------------------------- /VerStarting/octtree.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "math3d.h" 8 | #include "primitive.h" 9 | 10 | namespace raytracer { 11 | 12 | using math3d::V3D; 13 | 14 | class OctTree { 15 | public: 16 | OctTree(); 17 | 18 | // Adds a primitive to the temporary list. This method must not be callled 19 | // after the tree is finalized or otherwise the behaviour is undefined. 20 | // The OctTree becomes the new owner of the object and will call delete on 21 | // it when destructing. 22 | void AddPrimitive(Primitive *p); 23 | 24 | // Finalize the tree. It won't be possibel to add any new primitives, but it 25 | // will be possible to use the intersection methods. 26 | void Finalize(); 27 | 28 | // Finds the closest ray-primitive intersection point and returns a pointer 29 | // to the primitive (the OctTree remains the owner of this pointer), the 30 | // intersection point and the distance between the ray origin and the 31 | // intersection point. 32 | // Returns nullptr in case the ray didn't intersect any primitives. 33 | const Primitive* IntersectRay( 34 | const Ray& ray, // Ray to check against. 35 | V3D *point, // Intersection point. 36 | V3D::basetype *distance // Distance to intersection. 37 | ) const; 38 | 39 | AABB GetAABB() const; 40 | 41 | private: 42 | // The minimum primitives required to make a split. 43 | static const int SPLIT_BOUNDARY = 16; 44 | 45 | // A node might either have both primitives or Nodes. 46 | // A node is not the owner of any of the objects that is contains pointers to. 47 | struct Node { 48 | std::vector primitives; 49 | std::vector nodes; // Always either 0 or 8 nodes. 50 | 51 | V3D center; 52 | AABB aabb; 53 | 54 | void CalcCenter(); 55 | void AttemptSplit(); 56 | 57 | // Check is this node colides with the ray. 58 | bool NodeIntersectRay(const Ray& ray, V3D::basetype *dist) const; 59 | 60 | // Returns a primitive (if any) that intersects with the ray with the 61 | // lowest distance. 62 | const Primitive* PrimitiveIntersectRay( 63 | const Ray& ray, V3D *point, V3D::basetype *distance) const; 64 | }; 65 | 66 | Node root; 67 | std::list> primitives; 68 | }; 69 | 70 | } // namespace raytracer 71 | 72 | -------------------------------------------------------------------------------- /VerStarting/mythtracer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "camera.h" 5 | #include "objreader.h" 6 | #include "octtree.h" 7 | 8 | namespace raytracer { 9 | using math3d::V3D; 10 | 11 | const int MAX_RECURSION_LEVEL = 5; 12 | 13 | struct PerPixelDebugInfo { 14 | int line_no; 15 | V3D point; 16 | }; 17 | 18 | class WorkChunk { 19 | public: 20 | // Input. 21 | int image_width, image_height; 22 | int chunk_x, chunk_y; 23 | int chunk_width, chunk_height; 24 | Camera camera; 25 | 26 | // Note: Camera is not being serialized/deserialized. 27 | // TODO(gynvael): Mabe change this? It's somewhat weird. 28 | static const size_t kSerializedInputSize = 29 | /* image_width */ sizeof(uint32_t) + 30 | /* image_height */ sizeof(uint32_t) + 31 | /* chunk_x */ sizeof(uint32_t) + 32 | /* chunk_y */ sizeof(uint32_t) + 33 | /* chunk_width */ sizeof(uint32_t) + 34 | /* chunk_height */ sizeof(uint32_t); 35 | 36 | void SerializeInput(std::vector *bytes); 37 | bool DeserializeInput(const std::vector& bytes); 38 | 39 | // Output. 40 | std::vector output_bitmap; 41 | std::vector output_debug; 42 | 43 | // TODO(gynvael): Add PerPixelDebugInfo serialization. 44 | static const size_t kSerializedOutputMinimumSize = 45 | /* number of bytes */ sizeof(uint32_t); 46 | /* followed by said amount of bytes */ 47 | 48 | // Note: To deserialize WorkChunk output please be sure to fill in the 49 | // chunk_width and chunk_height fields. 50 | bool SerializeOutput(std::vector *bytes); 51 | bool DeserializeOutput(const std::vector& bytes); 52 | 53 | }; 54 | 55 | class MythTracer { 56 | public: 57 | Scene *GetScene(); 58 | 59 | bool LoadObj(const char *fname); 60 | 61 | bool RayTrace( 62 | int image_width, int image_height, 63 | Camera *camera, 64 | std::vector *output_bitmap); 65 | 66 | bool RayTrace(WorkChunk *chunk); 67 | 68 | private: 69 | Scene scene; 70 | bool was_scene_finalized = false; 71 | 72 | V3D TraceRayWorker( 73 | const Ray& ray, int level, 74 | bool in_object, // Used in transparency. 75 | V3D::basetype current_reflection_coef, 76 | PerPixelDebugInfo *debug); 77 | V3D TraceRay(const Ray& ray, PerPixelDebugInfo *debug); 78 | void V3DtoRGB(const V3D& v, uint8_t rgb[3]); 79 | }; 80 | 81 | } // namespace raytracer 82 | 83 | -------------------------------------------------------------------------------- /VerStarting/network.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include "NetSock/NetSock.h" 6 | #include "math3d.h" 7 | #include "scene.h" 8 | #include "camera.h" 9 | #include "mythtracer.h" 10 | 11 | namespace raytracer { 12 | 13 | using math3d::V3D; 14 | 15 | namespace netproto { 16 | // Wire format is the following: 17 | // 4 bytes Tag 18 | // 8 bytes Sender/Destination ID (8-byte string) 19 | // 4 bytes Length (excluding Tag, ID and Length) 20 | // N bytes Data 21 | // The wire format of each class is known only to the class itself. 22 | // The Tag, ID and Length are automatically sent; there is no need for the class 23 | // to add them manually to the bytes vector, however the Sender/Destination ID 24 | // field must be filled. Worker puts its own ID in the Sender ID fields. Master 25 | // puts the Destination ID there. 26 | // All values are LE. 27 | 28 | enum class kCommunicationSide { 29 | kMaster, 30 | kWorker 31 | }; 32 | 33 | class NetworkProto { 34 | public: 35 | virtual ~NetworkProto() { }; 36 | virtual std::string GetTag() const = 0; 37 | std::vector bytes; 38 | std::string id; // Sender/Destination ID. 39 | }; 40 | 41 | // Worker->Master: Worker ready to receive the scene. 42 | class WorkerReady : public NetworkProto { 43 | public: 44 | static WorkerReady* Make(const std::string &sender_id); 45 | std::string GetTag() const override { 46 | return "RDY!"; 47 | }; 48 | }; 49 | 50 | // Master->Worker: Serialized scene (primitives, textures, lights). 51 | class MasterScene : public NetworkProto { 52 | public: 53 | static MasterScene* Make(const std::string &destination_id, Scene *scene); 54 | std::string GetTag() const override { 55 | return "SCNE"; 56 | }; 57 | }; 58 | 59 | // TODO(gynvael): Perhaps merge this with WORK? 60 | // Master->Worker: Serialized Camera. 61 | class MasterSetCamera : public NetworkProto { 62 | public: 63 | static MasterSetCamera* Make( 64 | const std::string &destination_id, Camera *camera); 65 | std::string GetTag() const override { 66 | return "CAMR"; 67 | }; 68 | }; 69 | 70 | // Master->Worker: Serialized WorkChunk. 71 | class MasterRenderOrder : public NetworkProto { 72 | public: 73 | static MasterRenderOrder* Make( 74 | const std::string &destination_id, WorkChunk *chunk); 75 | std::string GetTag() const override { 76 | return "WORK"; 77 | }; 78 | }; 79 | 80 | // Worker->Master: Bitmap and logs/stats. 81 | class WorkerRenderResult : public NetworkProto { 82 | public: 83 | // Uses only the output_* part of WorkChunk. 84 | static WorkerRenderResult* Make( 85 | const std::string &sender_id, WorkChunk *chunk); 86 | 87 | std::string GetTag() const override { 88 | return "PXLS"; 89 | }; 90 | }; 91 | 92 | bool SendPacket(NetSock *s, NetworkProto *packet); 93 | NetworkProto* ReceivePacket(NetSock *s, kCommunicationSide side); 94 | 95 | } // namespace netproto 96 | 97 | } // namespace raytracer 98 | 99 | -------------------------------------------------------------------------------- /VerStarting/texture.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "math3d.h" 4 | #include "texture.h" 5 | 6 | 7 | namespace raytracer { 8 | 9 | using math3d::V3D; 10 | 11 | V3D Texture::GetColorAt(double u, double v, double distance) const { 12 | (void)distance; // TODO(gynvael): Add mipmaps. 13 | 14 | u = fmod(u, 1.0); 15 | v = fmod(v, 1.0); 16 | if (u < 0.0) u += 1.0; 17 | if (v < 0.0) v += 1.0; 18 | 19 | // Flip the vertical. 20 | v = 1.0 - v; 21 | 22 | double x = u * (double)(width - 1); 23 | double y = v * (double)(height - 1); 24 | 25 | size_t base_x = (size_t)x; 26 | size_t base_y = (size_t)y; 27 | 28 | size_t coords[4][2] = { 29 | { base_x, 30 | base_y }, 31 | { base_x + 1 == width ? base_x : base_x + 1, 32 | base_y }, 33 | { base_x, 34 | base_y + 1 == height ? base_y : base_y + 1 }, 35 | { base_x + 1 == width ? base_x : base_x + 1, 36 | base_y + 1 == height ? base_y : base_y + 1 } 37 | }; 38 | 39 | V3D c[4]; 40 | for (int i = 0; i < 4; i++) { 41 | c[i] = colors.at(coords[i][0] + coords[i][1] * width); 42 | } 43 | 44 | double dist_x = fmod(x, 1.0); 45 | double dist_y = fmod(y, 1.0); 46 | 47 | double area[4] = { 48 | (1.0 - dist_x) * (1.0 - dist_y), 49 | dist_x * (1.0 - dist_y), 50 | (1.0 - dist_x) * dist_y, 51 | dist_x * dist_y 52 | }; 53 | 54 | return c[0] * area[0] + 55 | c[1] * area[1] + 56 | c[2] * area[2] + 57 | c[3] * area[3]; 58 | } 59 | 60 | Texture *Texture::LoadFromFile(const char *fname) { 61 | fprintf(stderr, "info: loading texture \"%s\"\n", fname); 62 | struct SDLSurfaceDeleter { 63 | void operator()(SDL_Surface *s) const { 64 | SDL_FreeSurface(s); 65 | } 66 | }; 67 | 68 | std::unique_ptr s(IMG_Load(fname)); 69 | if (s == nullptr) { 70 | return nullptr; 71 | } 72 | 73 | // Some sanity checks. 74 | if (s->w <= 0 || s->h <= 0 || s->w > 30000 || s->h > 30000) { 75 | fprintf(stderr, "error: texture pretty insane \"%s\" (%i, %i)\n", 76 | fname, s->w, s->h); 77 | return nullptr; 78 | } 79 | 80 | // Convert to RGB if needed. 81 | if (s->format->format != SDL_PIXELFORMAT_RGBA32) { 82 | fprintf(stderr, "info: converting texture to RGBA32\n"); 83 | s.reset(SDL_ConvertSurfaceFormat(s.get(), SDL_PIXELFORMAT_RGBA32, 0)); 84 | if (s == nullptr) { 85 | fprintf(stderr, "error: texture conversion failed \"%s\"\n", fname); 86 | return nullptr; 87 | } 88 | } 89 | 90 | // Allocate and convert texture. 91 | std::unique_ptr tex(new Texture); 92 | tex->width = (size_t)s->w; 93 | tex->height = (size_t)s->h; 94 | tex->colors.resize(tex->width * tex->height); 95 | 96 | size_t idx = 0; 97 | uint8_t *px = (uint8_t*)s->pixels; 98 | for (size_t j = 0; j < tex->height; j++) { 99 | for (size_t i = 0; i < tex->width; i++, idx++, px += 4) { 100 | tex->colors[idx] = { 101 | (double)px[0] / 255.0, 102 | (double)px[1] / 255.0, 103 | (double)px[2] / 255.0 104 | }; 105 | } 106 | } 107 | 108 | return tex.release(); 109 | } 110 | 111 | }; // namespace raytracer 112 | 113 | -------------------------------------------------------------------------------- /VerStarting/camera.cc: -------------------------------------------------------------------------------- 1 | #include "camera.h" 2 | #include 3 | 4 | namespace raytracer { 5 | 6 | using math3d::V3D; 7 | using math3d::M4D; 8 | 9 | V3D Camera::GetDirection() const { 10 | // Note: Roll is skipped as it wouldn't change the vector anyway. 11 | V3D dir{0.0, 0.0, 1.0}; 12 | return 13 | M4D::RotationYDeg(yaw) * 14 | M4D::RotationXDeg(pitch) * dir; 15 | } 16 | 17 | Camera::Sensor Camera::GetSensor(int width, int height) const { 18 | Sensor s; 19 | s.width = width; 20 | s.height = height; 21 | s.cam = this; 22 | s.Reset(); 23 | 24 | return s; 25 | } 26 | 27 | void Camera::Sensor::Reset() { 28 | // Calculate the vertival AOV angle. 29 | auto aov_vertical = (V3D::basetype(height) / V3D::basetype(width)) * cam->aov; 30 | 31 | // Calculate the frustum based on AOE. 32 | M4D rot_left = M4D::RotationYDeg(cam->aov / 2.0); 33 | M4D rot_right = M4D::RotationYDeg(-cam->aov / 2.0); 34 | M4D rot_top = M4D::RotationZDeg(aov_vertical / 2.0); 35 | M4D rot_bottom = M4D::RotationZDeg(-aov_vertical / 2.0); 36 | 37 | M4D rot_left_top = rot_top * rot_left; 38 | M4D rot_right_top = rot_bottom * rot_right; 39 | M4D rot_left_bottom = rot_bottom * rot_left; 40 | 41 | V3D dir{0.0, 0.0, 1.0}; 42 | 43 | V3D frustum_top_left = rot_left_top * dir; 44 | V3D frustum_top_right = rot_right_top * dir; 45 | V3D frustum_bottom_left = rot_left_bottom * dir; 46 | 47 | // Rotate the frustum in the direction of the camera. 48 | M4D frustum_rotation = 49 | M4D::RotationYDeg(cam->yaw) * 50 | M4D::RotationXDeg(cam->pitch) * 51 | M4D::RotationZDeg(cam->roll); 52 | 53 | frustum_top_left = frustum_rotation * frustum_top_left; 54 | frustum_top_right = frustum_rotation * frustum_top_right; 55 | frustum_bottom_left = frustum_rotation * frustum_bottom_left; 56 | 57 | // Calculate horizontal and vertical deltas. 58 | delta_scanline = 59 | (frustum_bottom_left - frustum_top_left) / V3D::basetype(height); 60 | delta_pixel = 61 | (frustum_top_right - frustum_top_left) / V3D::basetype(width); 62 | start_point = frustum_top_left; 63 | } 64 | 65 | Ray Camera::Sensor::GetRay(int x, int y) const { 66 | V3D direction = start_point + (delta_scanline * y) + (delta_pixel * x); 67 | direction.Norm(); 68 | return { cam->origin, direction }; 69 | } 70 | 71 | void Camera::Serialize(std::vector *bytes) { 72 | bytes->resize(kSerializedSize); 73 | 74 | // TODO(gynvael): Make this sane, plz. 75 | uint8_t *ptr = &(*bytes)[0]; 76 | memcpy(ptr, &origin, sizeof(V3D)); ptr += sizeof(V3D); 77 | memcpy(ptr, &pitch, sizeof(V3D::basetype)); ptr += sizeof(V3D::basetype); 78 | memcpy(ptr, &yaw, sizeof(V3D::basetype)); ptr += sizeof(V3D::basetype); 79 | memcpy(ptr, &roll, sizeof(V3D::basetype)); ptr += sizeof(V3D::basetype); 80 | memcpy(ptr, &aov, sizeof(V3D::basetype)); 81 | } 82 | 83 | bool Camera::Deserialize(const std::vector& bytes) { 84 | if (bytes.size() != kSerializedSize) { 85 | return false; 86 | } 87 | 88 | // TODO(gynvael): Make this sane, plz. 89 | const uint8_t *ptr = &bytes[0]; 90 | memcpy(&origin, ptr, sizeof(V3D)); ptr += sizeof(V3D); 91 | memcpy(&pitch, ptr, sizeof(V3D::basetype)); ptr += sizeof(V3D::basetype); 92 | memcpy(&yaw, ptr, sizeof(V3D::basetype)); ptr += sizeof(V3D::basetype); 93 | memcpy(&roll, ptr, sizeof(V3D::basetype)); ptr += sizeof(V3D::basetype); 94 | memcpy(&aov, ptr, sizeof(V3D::basetype)); 95 | return true; 96 | } 97 | 98 | } // namespace raytracer 99 | 100 | -------------------------------------------------------------------------------- /VerStarting/NetSock/NetSock.h: -------------------------------------------------------------------------------- 1 | // NetSock socket helper class 2 | // code by gynvael.coldwind//vx 3 | // http://gynvael.vexillium.org 4 | // http://vexillium.org 5 | // 6 | // additional thx to: 7 | // Mateusz "j00ru" Jurczyk 8 | // and others 9 | // 10 | // Version: 2017-07-11 11 | // 12 | // LICENSE 13 | // Copyright 2017 Gynvael Coldwind 14 | // 15 | // Licensed under the Apache License, Version 2.0 (the "License"); 16 | // you may not use this file except in compliance with the License. 17 | // You may obtain a copy of the License at 18 | // 19 | // http://www.apache.org/licenses/LICENSE-2.0 20 | // 21 | // Unless required by applicable law or agreed to in writing, software 22 | // distributed under the License is distributed on an "AS IS" BASIS, 23 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | // See the License for the specific language governing permissions and 25 | // limitations under the License. 26 | // 27 | #pragma once 28 | #ifdef __unix__ 29 | # include 30 | # include 31 | # include 32 | #else 33 | # include 34 | # include 35 | #endif 36 | #include 37 | 38 | using namespace std; 39 | 40 | class NetSock 41 | { 42 | private: 43 | unsigned int ip; 44 | unsigned short port; 45 | 46 | unsigned int bindip; 47 | unsigned int bindport; 48 | 49 | int mode; 50 | int socket; 51 | char str_ip[16], str_bindip[16]; 52 | 53 | bool isUDP; 54 | bool UDPCanBroadcast; 55 | 56 | public: 57 | NetSock(); 58 | ~NetSock(); 59 | 60 | static const int SYNCHRONIC; 61 | static const int ASYNCHRONIC; 62 | 63 | bool ListenUDP(unsigned short bindport, const char *bindhost); 64 | bool ListenAllUDP(unsigned short bindport); 65 | 66 | bool Connect(const char* host, unsigned short port); 67 | bool Connect(unsigned int ip, unsigned short port); 68 | bool SetMode(int mode); 69 | bool Disconnect(); 70 | bool Listen(unsigned short port, const char *bindip); 71 | bool ListenAll(unsigned short port); 72 | NetSock *Accept(); 73 | int Read(void *Buffer, int Size); 74 | int GetDescriptor() const; 75 | 76 | // Reads exactly Size bytes. Does not return until then, 77 | // unless an error has occured. 78 | // If it's used on a non-blocking socket, it switches the 79 | // socket to blocking mode until all the data is read, 80 | // and switches it back to non-blocking mode after that. 81 | int ReadAll(void *Buffer, int Size); 82 | 83 | // Write and WriteAll work the same in blocking sockets. 84 | // In non-blocking, Write writes only so much data that does 85 | // not cause a block. WriteAll on the other hand makes sure 86 | // all the data is transmited. 87 | int Write(const void *Buffer, int Size); 88 | int WriteAll(const void *Buffer, int Size); 89 | 90 | // Use BroadcastUDP to send packets to broadcast addresses. 91 | // Due to some change in Windows 7 BroadcastUDP cannot use 255.255.255.255 as source address, 92 | // currently you need to use an interface specific broadcast address (e.g. 192.168.1.255). 93 | int WriteUDP(const char* host, unsigned short port, const void *buffer, int size); 94 | int BroadcastUDP(const char* broadcast, unsigned short port, const void *buffer, int size); 95 | int ReadUDP(void *buffer, int size, char *srchost, unsigned short *srcport); 96 | 97 | unsigned short GetPort() const; 98 | unsigned int GetIP() const; 99 | const char *GetStrIP(); 100 | 101 | unsigned short GetBindPort() const; 102 | unsigned int GetBindIP() const; 103 | const char *GetStrBindIP(); 104 | 105 | // Inits winsock on Windows 106 | static bool InitNetworking(void); 107 | }; 108 | 109 | -------------------------------------------------------------------------------- /VerStarting/main_local.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #ifdef __unix__ 5 | # include 6 | # include 7 | #else 8 | # include 9 | #endif 10 | #include "mythtracer.h" 11 | #include "camera.h" 12 | #include "octtree.h" 13 | 14 | using math3d::V3D; 15 | using raytracer::MythTracer; 16 | using raytracer::AABB; 17 | using raytracer::PerPixelDebugInfo; 18 | using raytracer::Camera; 19 | using raytracer::Light; 20 | const int W = 1920/4; // 960 480 21 | const int H = 1080/4; // 540 270 22 | 23 | 24 | int main(void) { 25 | puts("Creating anim/ directory"); 26 | #ifdef __unix__ 27 | mkdir("anim", 0700); 28 | #else 29 | _mkdir("anim"); 30 | #endif 31 | 32 | printf("Resolution: %u %u\n", W, H); 33 | 34 | MythTracer mt; 35 | if (!mt.LoadObj("../Models/Living Room USSU Design.obj")) { 36 | return 1; 37 | } 38 | 39 | AABB aabb = mt.GetScene()->tree.GetAABB(); 40 | printf("%f %f %f x %f %f %f\n", 41 | aabb.min.v[0], 42 | aabb.min.v[1], 43 | aabb.min.v[2], 44 | aabb.max.v[0], 45 | aabb.max.v[1], 46 | aabb.max.v[2]); 47 | 48 | std::vector debug(W * H); 49 | std::vector bitmap(W * H * 3); 50 | int frame = 0; 51 | for (double angle = 0.0; angle <= 360.0; angle += 2.0, frame++) { 52 | 53 | // Skip some frames 54 | if (frame <= 73) { 55 | continue; 56 | } 57 | 58 | // Really good camera setting. 59 | /*Camera cam{ 60 | { 300.0, 57.0, 160.0 }, 61 | 0.0, 180.0, 0.0, 62 | 110.0 63 | };*/ 64 | 65 | // Camera set a lamp. 66 | /*Camera cam{ 67 | { 250.0, 50.0, -20.0 }, 68 | -90.0, 180.0, 0.0, 69 | 110.0 70 | };*/ 71 | 72 | Camera cam{ 73 | { 300.0, 107.0, 40.0 }, 74 | 30.0, angle + 90, 0.0, 75 | 110.0 76 | }; 77 | 78 | // XXX: light at camera 79 | mt.GetScene()->lights.clear(); 80 | mt.GetScene()->lights.push_back( 81 | Light{ 82 | { 231.82174, 81.69966, -27.78259 }, 83 | { 0.3, 0.3, 0.3 }, 84 | { 1.0, 1.0, 1.0 }, 85 | { 1.0, 1.0, 1.0 } 86 | }); 87 | 88 | mt.GetScene()->lights.push_back( 89 | Light{ 90 | { 200, 80.0, 0 }, 91 | { 0.0, 0.0, 0.0 }, 92 | { 0.3, 0.3, 0.3 }, 93 | { 0.3, 0.3, 0.3 } 94 | }); 95 | 96 | mt.GetScene()->lights.push_back( 97 | Light{ 98 | { 200, 80.0, 80 }, 99 | { 0.0, 0.0, 0.0 }, 100 | { 0.3, 0.3, 0.3 }, 101 | { 0.3, 0.3, 0.3 } 102 | }); 103 | 104 | mt.GetScene()->lights.push_back( 105 | Light{ 106 | { 200, 80.0, 160 }, 107 | { 0.0, 0.0, 0.0 }, 108 | { 0.3, 0.3, 0.3 }, 109 | { 0.3, 0.3, 0.3 } 110 | }); 111 | 112 | /*raytracer::WorkChunk chunk{ 113 | W, H, 114 | 100, 100, 100, 100, 115 | &cam, 116 | &bitmap, 117 | nullptr 118 | };*/ 119 | 120 | //mt.RayTrace(&chunk); 121 | 122 | mt.RayTrace(W, H, &cam, &bitmap); 123 | 124 | 125 | puts("Writing"); 126 | 127 | char fname[256]; 128 | sprintf(fname, "anim/dump_%.5i.raw", frame); 129 | 130 | FILE *f = fopen(fname, "wb"); 131 | fwrite(&bitmap[0], bitmap.size(), 1, f); 132 | fclose(f); 133 | 134 | /*sprintf(fname, "anim/dump_%.5i.txt", frame); 135 | f = fopen(fname, "w"); 136 | int idx = 0; 137 | for (int j = 0; j < H; j++) { 138 | for (int i = 0; i < W; i++, idx++) { 139 | fprintf(f, "%i, %i: line %i point %s\n", 140 | i, j, 141 | debug[idx].line_no, 142 | V3DStr(debug[idx].point)); 143 | } 144 | } 145 | fclose(f); 146 | */ 147 | 148 | //break; 149 | } 150 | 151 | puts("Done"); 152 | 153 | 154 | return 0; 155 | } 156 | 157 | -------------------------------------------------------------------------------- /VerStarting/network.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "network.h" 4 | 5 | namespace raytracer { 6 | namespace netproto { 7 | 8 | WorkerReady* WorkerReady::Make(const std::string &sender_id) { 9 | auto packet = std::make_unique(); 10 | packet->id = sender_id; 11 | return packet.release(); 12 | } 13 | 14 | MasterScene* MasterScene::Make( 15 | const std::string &destination_id, Scene *) { 16 | // TODO(gynvael): Add proper scene support. 17 | auto packet = std::make_unique(); 18 | packet->id = destination_id; 19 | return packet.release(); 20 | } 21 | 22 | MasterSetCamera* MasterSetCamera::Make( 23 | const std::string &destination_id, Camera *camera) { 24 | auto packet = std::make_unique(); 25 | packet->id = destination_id; 26 | camera->Serialize(&packet->bytes); 27 | return packet.release(); 28 | } 29 | 30 | MasterRenderOrder* MasterRenderOrder::Make( 31 | const std::string &destination_id, WorkChunk *chunk) { 32 | auto packet = std::make_unique(); 33 | packet->id = destination_id; 34 | chunk->SerializeInput(&packet->bytes); 35 | return packet.release(); 36 | } 37 | 38 | WorkerRenderResult* WorkerRenderResult::Make( 39 | const std::string &sender_id, WorkChunk *chunk) { 40 | auto packet = std::make_unique(); 41 | packet->id = sender_id; 42 | if (!chunk->SerializeOutput(&packet->bytes)) { 43 | return nullptr; 44 | } 45 | return packet.release(); 46 | } 47 | 48 | // Helper functions. 49 | 50 | // TODO(gynvael): Neither serialization nor network helper functions respect the 51 | // LE setting. Add this everywhere. 52 | 53 | bool SendPacket(NetSock *s, NetworkProto *packet) { 54 | // Prepare packet. 55 | packet->id.resize(8); 56 | 57 | // Send the wire format details. 58 | if (s->WriteAll(packet->GetTag().c_str(), 4) != 4) { 59 | return false; 60 | } 61 | 62 | if (s->WriteAll(packet->id.data(), 8) != 8) { 63 | return false; 64 | } 65 | 66 | if (packet->bytes.size() > std::numeric_limits::max()) { 67 | // TODO(gynvael): Error message. 68 | return false; 69 | } 70 | 71 | uint32_t sz = packet->bytes.size(); 72 | 73 | if (s->WriteAll(&sz, 4) != 4) { 74 | return false; 75 | } 76 | 77 | // TODO(gynvael): Change netsock to use sane types plz. 78 | if ((uint32_t)s->WriteAll(&packet->bytes[0], sz) != sz) { 79 | return false; 80 | } 81 | 82 | return true; 83 | } 84 | 85 | NetworkProto* ReceivePacket(NetSock *s, kCommunicationSide side) { 86 | uint8_t bytes[4 + 8 + 4]{}; 87 | int ret = s->ReadAll(bytes, sizeof(bytes)); 88 | if (ret != sizeof(bytes)) { 89 | return nullptr; 90 | } 91 | 92 | std::string tag((char*)bytes, 4); 93 | std::string id((char*)bytes + 4, 8); 94 | uint32_t length; 95 | memcpy(&length, bytes + 4 + 8, sizeof(uint32_t)); 96 | 97 | // TODO(gynvael): Some early per-packed max size checks would be useful. 98 | 99 | // Sanity check. 100 | // TODO(gynvael): Actually make a better check for SCNE and PXLS later on. 101 | if (length > 1024 * 1024) { 102 | return nullptr; 103 | } 104 | 105 | std::vector payload; 106 | payload.resize(length); 107 | ret = s->ReadAll(&payload[0], length); 108 | if (ret != (int)length) { 109 | return nullptr; 110 | } 111 | 112 | NetworkProto *packet = nullptr; 113 | 114 | if (side == kCommunicationSide::kMaster && tag == "RDY!") { 115 | packet = new WorkerReady; 116 | } else if (side == kCommunicationSide::kWorker && tag == "SCNE") { 117 | packet = new MasterScene; 118 | } else if (side == kCommunicationSide::kWorker && tag == "CAMR") { 119 | packet = new MasterSetCamera; 120 | } else if (side == kCommunicationSide::kWorker && tag == "WORK") { 121 | packet = new MasterRenderOrder; 122 | } else if (side == kCommunicationSide::kMaster && tag == "PXLS") { 123 | packet = new WorkerRenderResult; 124 | } else { 125 | return nullptr; 126 | } 127 | 128 | packet->id = std::move(id); 129 | packet->bytes = std::move(payload); 130 | 131 | // TODO(gynvael): Think about deserializing here. 132 | return packet; 133 | } 134 | 135 | } // namespace netproto 136 | } // namespace raytracer 137 | 138 | -------------------------------------------------------------------------------- /VerStarting/main_net_worker.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #ifdef __unix__ 5 | # include 6 | # include 7 | #else 8 | # include 9 | #endif 10 | #include 11 | #include 12 | #include "mythtracer.h" 13 | #include "camera.h" 14 | #include "octtree.h" 15 | #include "network.h" 16 | 17 | using math3d::V3D; 18 | using namespace raytracer; 19 | 20 | 21 | int main(int argc, char **argv) { 22 | if (argc != 3) { 23 | puts("usage: mythtracer_worker \n" 24 | "note : tag should have at most 8 characters"); 25 | return 1; 26 | } 27 | 28 | 29 | MythTracer mt; 30 | if (!mt.LoadObj("../Models/Living Room USSU Design.obj")) { 31 | return 1; 32 | } 33 | 34 | mt.GetScene()->lights.clear(); 35 | mt.GetScene()->lights.push_back( 36 | Light{ 37 | { 231.82174, 81.69966, -27.78259 }, 38 | { 0.3, 0.3, 0.3 }, 39 | { 1.0, 1.0, 1.0 }, 40 | { 1.0, 1.0, 1.0 } 41 | }); 42 | 43 | mt.GetScene()->lights.push_back( 44 | Light{ 45 | { 200, 80.0, 0 }, 46 | { 0.0, 0.0, 0.0 }, 47 | { 0.3, 0.3, 0.3 }, 48 | { 0.3, 0.3, 0.3 } 49 | }); 50 | 51 | mt.GetScene()->lights.push_back( 52 | Light{ 53 | { 200, 80.0, 80 }, 54 | { 0.0, 0.0, 0.0 }, 55 | { 0.3, 0.3, 0.3 }, 56 | { 0.3, 0.3, 0.3 } 57 | }); 58 | 59 | mt.GetScene()->lights.push_back( 60 | Light{ 61 | { 200, 80.0, 160 }, 62 | { 0.0, 0.0, 0.0 }, 63 | { 0.3, 0.3, 0.3 }, 64 | { 0.3, 0.3, 0.3 } 65 | }); 66 | 67 | NetSock::InitNetworking(); 68 | 69 | std::string id(argv[1]); 70 | printf("Name of this worker: %s\n", id.c_str()); 71 | 72 | for (;;) { 73 | puts("Connecting..."); 74 | 75 | auto s = std::make_unique(); 76 | if (!s->Connect(argv[2], 12345)) { 77 | printf("error: failed to connect to %s:12345\n", argv[2]); 78 | 79 | // Sleep. 80 | std::this_thread::sleep_for(1s); 81 | continue; 82 | } 83 | 84 | puts("Connected!"); 85 | std::unique_ptr p; 86 | 87 | // Introduce to the server. 88 | p.reset(netproto::WorkerReady::Make(id)); 89 | if (!netproto::SendPacket(s.get(), p.get())) { 90 | printf("error: disconnected when sending RDY!\n"); 91 | fflush(stdout); 92 | std::this_thread::sleep_for(2s); 93 | continue; 94 | } 95 | 96 | // Wait for work. 97 | Camera cam; 98 | for (;;) { 99 | p.reset(netproto::ReceivePacket( 100 | s.get(), netproto::kCommunicationSide::kWorker)); 101 | if (p == nullptr) { 102 | printf("error: invalid proto or disconnected\n"); 103 | fflush(stdout); 104 | std::this_thread::sleep_for(2s); 105 | break; 106 | } 107 | 108 | if (p->GetTag() == "CAMR") { 109 | if (!cam.Deserialize(p->bytes)) { 110 | printf("error: failed to deserialize camera\n"); 111 | fflush(stdout); 112 | std::this_thread::sleep_for(2s); 113 | break; 114 | } 115 | 116 | printf("Received new camera settings:\n" 117 | "Position : %f %f %f\n" 118 | "Pitch/yaw/roll: %f, %f, %f\n" 119 | "Angle of view : %f deg\n", 120 | cam.origin.v[0], cam.origin.v[1], cam.origin.v[2], 121 | cam.pitch, cam.yaw, cam.roll, 122 | cam.aov); 123 | fflush(stdout); 124 | continue; 125 | } 126 | 127 | if (p->GetTag() == "WORK") { 128 | WorkChunk work; 129 | if (!work.DeserializeInput(p->bytes)) { 130 | printf("error: failed to deserialize work chunk\n"); 131 | fflush(stdout); 132 | std::this_thread::sleep_for(2s); 133 | break; 134 | } 135 | 136 | size_t sz = work.chunk_width * work.chunk_height; 137 | printf("Received work:\n" 138 | "Final resolution : %i x %i (24bpp)\n" 139 | "Chunk position : %i, %i\n" 140 | "Chunk size : %i x %i\n" 141 | "Initial ray count: %i rays\n", 142 | work.image_width, work.image_height, 143 | work.chunk_x, work.chunk_y, 144 | work.chunk_width, work.chunk_height, 145 | (int)sz); 146 | 147 | printf("Rendering"); fflush(stdout); 148 | work.output_bitmap.resize(sz * 3); 149 | work.camera = cam; 150 | if (!mt.RayTrace(&work)) { 151 | printf("error: failed while raytracing (weird); exiting\n"); 152 | fflush(stdout); 153 | exit(1); 154 | } 155 | 156 | puts("Done! Sending chunk to master."); 157 | p.reset(netproto::WorkerRenderResult::Make(id, &work)); 158 | if (!netproto::SendPacket(s.get(), p.get())) { 159 | printf("error: disconnected when sending PXLS\n"); 160 | fflush(stdout); 161 | std::this_thread::sleep_for(2s); 162 | break; 163 | } 164 | 165 | printf("Sent! %i points to %s!\n", (int)sz, id.c_str()); 166 | } 167 | } 168 | } 169 | 170 | return 0; 171 | } 172 | 173 | -------------------------------------------------------------------------------- /VerStarting/primitive_triangle.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "primitive_triangle.h" 6 | 7 | namespace raytracer { 8 | 9 | using math3d::V3D; 10 | 11 | Triangle::~Triangle() { 12 | } 13 | 14 | AABB Triangle::GetAABB() const { 15 | return cached_aabb; 16 | } 17 | 18 | void Triangle::CacheAABB() { 19 | AABB aabb{vertex[0], vertex[0]}; 20 | aabb.Extend(vertex[1]); 21 | aabb.Extend(vertex[2]); 22 | cached_aabb.min = aabb.min; 23 | cached_aabb.max = aabb.max; 24 | } 25 | 26 | // http://www.mathopenref.com/heronsformula.html 27 | static V3D::basetype AreaOfTriangle( 28 | V3D::basetype a, V3D::basetype b, V3D::basetype c) { 29 | V3D::basetype p = (a + b + c) / 2.0; 30 | V3D::basetype area_sqr = p * (p - a) * (p - b) * (p - c); 31 | 32 | // It seems that due to floating point inaccuracies it's possible to get a 33 | // negative result here when we are dealing with a triangle having all points 34 | // on the same line (i.e. with a zero size area). 35 | if (area_sqr < 0.0) { 36 | return 0.0; 37 | } 38 | 39 | return sqrt(area_sqr); 40 | } 41 | 42 | // https://classes.soe.ucsc.edu/cmps160/Fall10/resources/barycentricInterpolation.pdf 43 | V3D Triangle::GetNormal(const V3D& point) const { 44 | // Using barycentric interpolation. There might be a better / faster way to 45 | // do it. 46 | V3D::basetype a = vertex[0].Distance(vertex[1]); 47 | V3D::basetype b = vertex[1].Distance(vertex[2]); 48 | V3D::basetype c = vertex[2].Distance(vertex[0]); 49 | 50 | V3D::basetype p0 = point.Distance(vertex[0]); 51 | V3D::basetype p1 = point.Distance(vertex[1]); 52 | V3D::basetype p2 = point.Distance(vertex[2]); 53 | 54 | V3D::basetype n0 = AreaOfTriangle(b, p2, p1); 55 | V3D::basetype n1 = AreaOfTriangle(c, p0, p2); 56 | V3D::basetype n2 = AreaOfTriangle(a, p1, p0); 57 | 58 | V3D::basetype n = n0 + n1 + n2; 59 | 60 | return (normal[0] * n0 + normal[1] * n1 + normal[2] * n2) / n; 61 | } 62 | 63 | V3D Triangle::GetUVW(const V3D& point) const { 64 | V3D::basetype a = vertex[0].Distance(vertex[1]); 65 | V3D::basetype b = vertex[1].Distance(vertex[2]); 66 | V3D::basetype c = vertex[2].Distance(vertex[0]); 67 | 68 | V3D::basetype p0 = point.Distance(vertex[0]); 69 | V3D::basetype p1 = point.Distance(vertex[1]); 70 | V3D::basetype p2 = point.Distance(vertex[2]); 71 | 72 | V3D::basetype n0 = AreaOfTriangle(b, p2, p1); 73 | V3D::basetype n1 = AreaOfTriangle(c, p0, p2); 74 | V3D::basetype n2 = AreaOfTriangle(a, p1, p0); 75 | 76 | V3D::basetype n = n0 + n1 + n2; 77 | 78 | return (uvw[0] * n0 + uvw[1] * n1 + uvw[2] * n2) / n; 79 | } 80 | 81 | bool Triangle::IntersectRay(const Ray& ray, V3D *point, 82 | V3D::basetype *distance) const { 83 | // A quick ray-AABB(triangle) test that is faster than ray-triangle test 84 | // itself, so it acts as a quick negative test. 85 | AABB aabb = GetAABB(); 86 | const V3D& dirfrac = ray.inv_direction; 87 | 88 | V3D::basetype t1 = (aabb.min.x() - ray.origin.x()) * dirfrac.x(); 89 | V3D::basetype t2 = (aabb.max.x() - ray.origin.x()) * dirfrac.x(); 90 | V3D::basetype t3 = (aabb.min.y() - ray.origin.y()) * dirfrac.y(); 91 | V3D::basetype t4 = (aabb.max.y() - ray.origin.y()) * dirfrac.y(); 92 | V3D::basetype t5 = (aabb.min.z() - ray.origin.z()) * dirfrac.z(); 93 | V3D::basetype t6 = (aabb.max.z() - ray.origin.z()) * dirfrac.z(); 94 | 95 | // If tmax is less than zero, ray (line) is intersecting AABB, but the whole 96 | // AABB is behind the ray. 97 | V3D::basetype tmax = std::min({ 98 | std::max(t1, t2), std::max(t3, t4), std::max(t5, t6)}); 99 | if (tmax < 0.0) { 100 | return false; 101 | } 102 | 103 | // If tmin is greater than tmax, ray doesn't intersect AABB. 104 | V3D::basetype tmin = std::max({ 105 | std::min(t1, t2), std::min(t3, t4), std::min(t5, t6)}); 106 | if (tmin > tmax) { 107 | return false; 108 | } 109 | 110 | // Moller-Trumbore intersection algorithm, as presented on Wikipedia. 111 | V3D e1 = vertex[1] - vertex[0]; 112 | V3D e2 = vertex[2] - vertex[0]; 113 | 114 | V3D pvec = ray.direction.Cross(e2); 115 | V3D::basetype det = e1.Dot(pvec); 116 | 117 | // Check if ray is parallel to the plane. 118 | if (det >= -0.00000001 && det < 0.00000001) { 119 | return false; 120 | } 121 | 122 | V3D::basetype inv_det = 1.0 / det; 123 | V3D tvec = ray.origin - vertex[0]; 124 | V3D::basetype u = tvec.Dot(pvec) * inv_det; 125 | if (u < 0.0 || u > 1.0) { 126 | return false; 127 | } 128 | 129 | V3D qvec = tvec.Cross(e1); 130 | V3D::basetype v = ray.direction.Dot(qvec) * inv_det; 131 | if (v < 0.0 || u + v > 1.0) { 132 | return false; 133 | } 134 | 135 | V3D::basetype final_distance = e2.Dot(qvec) * inv_det; 136 | if (final_distance < 0.0) { 137 | // Intersection is behind the camera. 138 | return false; 139 | } 140 | *distance = final_distance; 141 | *point = ray.origin + ray.direction * *distance; 142 | return true; 143 | } 144 | 145 | std::string Triangle::Serialize() const{ 146 | return "nope"; // TODO 147 | } 148 | 149 | bool Triangle::Deserialize( 150 | std::unique_ptr *primitive, 151 | const std::string& data) { 152 | (void)primitive; 153 | (void)data; 154 | return false; // TODO 155 | } 156 | 157 | } // namespace raytracer 158 | -------------------------------------------------------------------------------- /VerStarting/octtree.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "octtree.h" 3 | 4 | namespace raytracer { 5 | 6 | OctTree::OctTree() { } 7 | 8 | void OctTree::AddPrimitive(Primitive *p) { 9 | primitives.push_back(std::unique_ptr(p)); 10 | 11 | // Update axis-aligned bounding box. 12 | AABB aabb = p->GetAABB(); 13 | root.aabb.Extend(aabb); 14 | } 15 | 16 | void OctTree::Finalize() { 17 | printf("Triangles: %u\n", (unsigned int)primitives.size()); 18 | root.primitives.reserve(primitives.size()); 19 | for (auto& p : primitives) { 20 | root.primitives.push_back(p.get()); 21 | } 22 | 23 | root.AttemptSplit(); 24 | } 25 | 26 | const Primitive* OctTree::IntersectRay( 27 | const Ray& ray, V3D *point, V3D::basetype *distance) const { 28 | Ray working_ray(ray); 29 | // TODO(gynvael): Actually do declare operators for T op V3D. 30 | working_ray.inv_direction.v[0] = 1.0 / working_ray.direction.v[0]; 31 | working_ray.inv_direction.v[1] = 1.0 / working_ray.direction.v[1]; 32 | working_ray.inv_direction.v[2] = 1.0 / working_ray.direction.v[2]; 33 | 34 | V3D::basetype dist; 35 | if (!root.NodeIntersectRay(working_ray, &dist)) { 36 | return nullptr; 37 | } 38 | 39 | return root.PrimitiveIntersectRay(working_ray, point, distance); 40 | } 41 | 42 | AABB OctTree::GetAABB() const { 43 | return root.aabb; 44 | } 45 | 46 | void OctTree::Node::CalcCenter() { 47 | center.v[0] = aabb.min.v[0] + (aabb.max.v[0] - aabb.min.v[0]) / 2.0; 48 | center.v[1] = aabb.min.v[1] + (aabb.max.v[1] - aabb.min.v[1]) / 2.0; 49 | center.v[2] = aabb.min.v[2] + (aabb.max.v[2] - aabb.min.v[2]) / 2.0; 50 | } 51 | 52 | void OctTree::Node::AttemptSplit() { 53 | if (primitives.size() < SPLIT_BOUNDARY) { 54 | return; 55 | } 56 | 57 | CalcCenter(); 58 | nodes.resize(8); 59 | 60 | // Bottom nodes. 61 | nodes[0].aabb = { 62 | aabb.min, 63 | center 64 | }; 65 | 66 | nodes[1].aabb = { 67 | { center.v[0], aabb.min.v[1], aabb.min.v[2] }, 68 | { aabb.max.v[0], center.v[1], center.v[2] } 69 | }; 70 | 71 | nodes[2].aabb = { 72 | { aabb.min.v[0], aabb.min.v[1], center.v[2] }, 73 | { center.v[0], center.v[1], aabb.max.v[2] } 74 | }; 75 | 76 | nodes[3].aabb = { 77 | { center.v[0], aabb.min.v[1], center.v[2] }, 78 | { aabb.max.v[0], center.v[1], aabb.max.v[2] } 79 | }; 80 | 81 | // Top nodes. 82 | nodes[4].aabb = { 83 | { aabb.min.v[0], center.v[1], aabb.min.v[2] }, 84 | { center.v[0], aabb.max.v[1], center.v[2] } 85 | }; 86 | 87 | nodes[5].aabb = { 88 | { center.v[0], center.v[1], aabb.min.v[2] }, 89 | { aabb.max.v[0], aabb.max.v[1], center.v[2] } 90 | }; 91 | 92 | nodes[6].aabb = { 93 | { aabb.min.v[0], center.v[1], center.v[2] }, 94 | { center.v[0], aabb.max.v[1], aabb.max.v[2] } 95 | }; 96 | 97 | nodes[7].aabb = { 98 | center, 99 | aabb.max 100 | }; 101 | 102 | // Check whether the primitive is fully contained in one of the nodes. 103 | // If not, it should remain within this node. 104 | std::vector remaining; 105 | 106 | for (Primitive *p : primitives) { 107 | AABB p_aabb = p->GetAABB(); 108 | bool subnode_found = false; 109 | 110 | for (Node& n : nodes) { 111 | // TODO(gynvael): Use primitive x AABB intersection. 112 | if (n.aabb.FullyContains(p_aabb)) { 113 | n.primitives.push_back(p); 114 | subnode_found = true; 115 | break; 116 | } 117 | } 118 | 119 | if (!subnode_found) { 120 | remaining.push_back(p); 121 | } 122 | } 123 | 124 | //printf("%s %s -- %i / %i\n", V3DStr(aabb.min), V3DStr(aabb.max), 125 | // (int)remaining.size(), (int)primitives.size()); 126 | 127 | // Clear and free capacity. 128 | primitives.clear(); 129 | remaining.swap(primitives); 130 | 131 | // Split the nodes. 132 | for (Node& n : nodes) { 133 | n.AttemptSplit(); 134 | } 135 | } 136 | 137 | // https://gamedev.stackexchange.com/questions/18436 138 | bool OctTree::Node::NodeIntersectRay( 139 | const Ray& ray, V3D::basetype *dist) const { 140 | // TODO(gynvael): Move the code from here and Triangle::IntersectRay to AABB. 141 | const V3D& dirfrac = ray.inv_direction; 142 | 143 | V3D::basetype t1 = (aabb.min.x() - ray.origin.x()) * dirfrac.x(); 144 | V3D::basetype t2 = (aabb.max.x() - ray.origin.x()) * dirfrac.x(); 145 | V3D::basetype t3 = (aabb.min.y() - ray.origin.y()) * dirfrac.y(); 146 | V3D::basetype t4 = (aabb.max.y() - ray.origin.y()) * dirfrac.y(); 147 | V3D::basetype t5 = (aabb.min.z() - ray.origin.z()) * dirfrac.z(); 148 | V3D::basetype t6 = (aabb.max.z() - ray.origin.z()) * dirfrac.z(); 149 | 150 | // If tmax is less than zero, ray (line) is intersecting AABB, but the whole 151 | // AABB is behind the ray. 152 | V3D::basetype tmax = std::min({ 153 | std::max(t1, t2), std::max(t3, t4), std::max(t5, t6)}); 154 | if (tmax < 0.0) { 155 | return false; 156 | } 157 | 158 | // If tmin is greater than tmax, ray doesn't intersect AABB. 159 | V3D::basetype tmin = std::max({ 160 | std::min(t1, t2), std::min(t3, t4), std::min(t5, t6)}); 161 | if (tmin > tmax) { 162 | return false; 163 | } 164 | 165 | *dist = tmin; 166 | return true; 167 | } 168 | 169 | const Primitive* OctTree::Node::PrimitiveIntersectRay( 170 | const Ray& ray, V3D *point, V3D::basetype *distance) const { 171 | 172 | const Primitive *closest_primitive = nullptr; 173 | V3D::basetype closest_distance{}; 174 | V3D closest_point; 175 | 176 | // Start by looking through the list of primitives contained in this node. 177 | for (const Primitive *p : primitives) { 178 | V3D intersection_point; 179 | V3D::basetype intersection_distance; 180 | if (!p->IntersectRay(ray, &intersection_point, &intersection_distance)) { 181 | continue; 182 | } 183 | 184 | // Calculate the distance and check if it's closer than the previously 185 | // discovered primitive (if any). 186 | if (closest_primitive != nullptr && 187 | intersection_distance > closest_distance) { 188 | // Previously discovered was closer. Continue. 189 | continue; 190 | } 191 | 192 | // The newly discovered primitive is the closest. 193 | closest_primitive = p; 194 | closest_distance = intersection_distance; 195 | closest_point = intersection_point; 196 | } 197 | 198 | // Check which children (if any) the ray intersects with and sort them by 199 | // distance. 200 | using node_distance = std::pair; 201 | std::vector considered_children; 202 | considered_children.reserve(8); 203 | 204 | for (const Node& n : nodes) { 205 | V3D::basetype dist; 206 | if (!n.NodeIntersectRay(ray, &dist)) { 207 | continue; 208 | } 209 | 210 | considered_children.emplace_back(&n, dist); 211 | } 212 | 213 | std::sort(considered_children.begin(), considered_children.end(), 214 | [](const node_distance& a, const node_distance &b) { 215 | return a.second < b.second; 216 | }); 217 | 218 | // Check if there is a closer primitive in children nodes. 219 | for (const auto [n_ptr, dist] : considered_children) { 220 | const Node& n = *n_ptr; 221 | 222 | V3D intersection_point; 223 | V3D::basetype intersection_distance; 224 | const Primitive *intersection_primitive = n.PrimitiveIntersectRay( 225 | ray, &intersection_point, &intersection_distance); 226 | 227 | if (intersection_primitive == nullptr) { 228 | continue; 229 | } 230 | 231 | // Calculate the distance and check if it's closer than the previously 232 | // discovered primitive (if any). 233 | if (closest_primitive != nullptr && 234 | intersection_distance > closest_distance) { 235 | // Previously discovered was closer. Continue. 236 | continue; 237 | } 238 | 239 | // The newly discovered primitive is the closest. 240 | closest_primitive = intersection_primitive; 241 | closest_distance = intersection_distance; 242 | closest_point = intersection_point; 243 | 244 | // Since the nodes were sorted by distance, there will be no closer 245 | // primitive. 246 | break; 247 | } 248 | 249 | // Return the found coliding point, if any. 250 | if (closest_primitive == nullptr) { 251 | return nullptr; 252 | } 253 | 254 | *point = closest_point; 255 | *distance = closest_distance; 256 | return closest_primitive; 257 | } 258 | 259 | } // namespace raytracer 260 | 261 | -------------------------------------------------------------------------------- /VerStarting/math3d.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // Math types, classes and functions useful for 3D stuff. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace math3d { 10 | 11 | template 12 | class V3D_Base { 13 | public: 14 | typedef T basetype; 15 | 16 | /*V3D_Base() : v{} { }; 17 | V3D_Base(T x, T y, T z) : v{x, y, z} { }; 18 | V3D_Base(const V3D_Base& a) : v{a.v[0], a.v[1], a.v[2]} { }; 19 | V3D_Base(V3D_Base&& a) : v{a.v[0], a.v[1], a.v[2]} { }; 20 | 21 | V3D_Base& operator=(const V3D_Base& a) { 22 | v[0] = a.v[0]; v[1] = a.v[1]; v[2] = a.v[2]; 23 | return *this; 24 | } 25 | 26 | V3D_Base& operator=( V3D_Base&& a) { 27 | v[0] = a.v[0]; v[1] = a.v[1]; v[2] = a.v[2]; 28 | return *this; 29 | }*/ 30 | 31 | V3D_Base operator+(const V3D_Base& b) const { 32 | return V3D_Base{v[0] + b.v[0], v[1] + b.v[1], v[2] + b.v[2]}; 33 | } 34 | 35 | V3D_Base operator-(const V3D_Base& b) const { 36 | return V3D_Base{v[0] - b.v[0], v[1] - b.v[1], v[2] - b.v[2]}; 37 | } 38 | 39 | V3D_Base operator-() const { 40 | return V3D_Base{-v[0], -v[1], -v[2]}; 41 | } 42 | 43 | V3D_Base operator+() const { 44 | return *this; 45 | } 46 | 47 | V3D_Base& operator+=(const V3D_Base& b) { 48 | v[0] += b.v[0]; v[1] += b.v[1]; v[2] += b.v[2]; 49 | return *this; 50 | } 51 | 52 | V3D_Base& operator-=(const V3D_Base& b) { 53 | v[0] -= b.v[0]; v[1] -= b.v[1]; v[2] -= b.v[2]; 54 | return *this; 55 | } 56 | 57 | // The * and / operations between vectors do the * and / on separate scalar 58 | // values. See Dot and Cross methods for dot product and cross product. 59 | V3D_Base operator*(const V3D_Base& b) const { 60 | return V3D_Base{v[0] * b.v[0], v[1] * b.v[1], v[2] * b.v[2]}; 61 | } 62 | 63 | V3D_Base operator/(const V3D_Base& b) const { 64 | return V3D_Base{v[0] / b.v[0], v[1] / b.v[1], v[2] / b.v[2]}; 65 | } 66 | 67 | V3D_Base& operator*=(const V3D_Base& b) { 68 | v[0] *= b.v[0]; v[1] *= b.v[1]; v[2] *= b.v[2]; 69 | return *this; 70 | } 71 | 72 | V3D_Base& operator/=(const V3D_Base& b) { 73 | v[0] /= b.v[0]; v[1] /= b.v[1]; v[2] /= b.v[2]; 74 | return *this; 75 | } 76 | 77 | // Scalar operations. 78 | V3D_Base& operator*=(T b) { 79 | v[0] *= b; v[1] *= b; v[2] *= b; 80 | return *this; 81 | } 82 | 83 | V3D_Base& operator/=(T b) { 84 | v[0] /= b; v[1] /= b; v[2] /= b; 85 | return *this; 86 | } 87 | 88 | V3D_Base operator*(T n) const { 89 | return V3D_Base{v[0] * n, v[1] * n, v[2] * n}; 90 | } 91 | 92 | V3D_Base operator/(T n) const { 93 | return V3D_Base{v[0] / n, v[1] / n, v[2] / n}; 94 | } 95 | 96 | // Convenience functions. 97 | T SqrLength() const { 98 | return v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; 99 | } 100 | 101 | T Length() const { 102 | return sqrt(SqrLength()); 103 | } 104 | 105 | T SqrDistance(const V3D_Base& a) const { 106 | const T dx = a.v[0] - v[0]; 107 | const T dy = a.v[1] - v[1]; 108 | const T dz = a.v[2] - v[2]; 109 | return dx * dx + dy * dy + dz * dz; 110 | } 111 | 112 | T Distance(const V3D_Base& a) const { 113 | return sqrt(SqrDistance(a)); 114 | } 115 | 116 | T Dot(const V3D_Base& a) const { 117 | return a.v[0] * v[0] + a.v[1] * v[1] + a.v[2] * v[2]; 118 | } 119 | 120 | V3D_Base Cross(const V3D_Base& a) const { 121 | return V3D_Base{ 122 | v[1] * a.v[2] - v[2] * a.v[1], 123 | v[2] * a.v[0] - v[0] * a.v[2], 124 | v[0] * a.v[1] - v[1] * a.v[0] 125 | }; 126 | } 127 | 128 | void Norm() { 129 | const T l = Length(); 130 | v[0] /= l; v[1] /= l; v[2] /= l; 131 | } 132 | 133 | V3D_Base DupNorm() const { 134 | const T l = Length(); 135 | return V3D_Base{v[0] / l, v[1] / l, v[2] / l}; 136 | } 137 | 138 | template 139 | friend std::ostream& operator<<(std::ostream &os, const V3D_Base& a); 140 | 141 | T v[3]{}; 142 | 143 | // Getters acting as aliases. 144 | T& x() { return v[0]; } 145 | T& y() { return v[1]; } 146 | T& z() { return v[2]; } 147 | 148 | T& r() { return v[0]; } 149 | T& g() { return v[1]; } 150 | T& b() { return v[2]; } 151 | 152 | const T& x() const { return v[0]; } 153 | const T& y() const { return v[1]; } 154 | const T& z() const { return v[2]; } 155 | 156 | const T& r() const { return v[0]; } 157 | const T& g() const { return v[1]; } 158 | const T& b() const { return v[2]; } 159 | }; 160 | 161 | template 162 | std::ostream& operator<<(std::ostream &os, const V3D_Base& a) { 163 | os << std::fixed << std::setprecision(5) 164 | << a.v[0] << ", " << a.v[1] << ", " << a.v[2]; 165 | return os; 166 | } 167 | 168 | template 169 | std::string ToStr(const T& a) { 170 | std::ostringstream s; 171 | s << a; 172 | return s.str(); 173 | } 174 | #define V3DStr(a) math3d::ToStr(a).c_str() 175 | 176 | // Angle conversion. 177 | inline double Deg2Rad(double angle) { 178 | return (angle * M_PI) / 180.0; 179 | } 180 | 181 | // Matrix operations. 182 | 183 | template 184 | class M4D_Base { 185 | public: 186 | typedef T basetype; 187 | 188 | M4D_Base operator*(const M4D_Base& a) { 189 | M4D_Base res; 190 | for (size_t j = 0; j < 4; j++) { 191 | for (size_t i = 0; i < 4; i++) { 192 | res.m[j][i] = m[j][0] * a.m[0][i] + 193 | m[j][1] * a.m[1][i] + 194 | m[j][2] * a.m[2][i] + 195 | m[j][3] * a.m[3][i]; 196 | } 197 | } 198 | 199 | return res; 200 | } 201 | 202 | M4D_Base& operator*=(const M4D_Base& a) { 203 | M4D_Base res = *this * a; 204 | *this = res; 205 | return *this; 206 | } 207 | 208 | // Note: The fourth element of the vector is always assumed to be 1. 209 | template 210 | V3D_Base operator*(const V3D_Base& a) { 211 | return V3D_Base{ 212 | m[0][0] * a.v[0] + m[0][1] * a.v[1] + m[0][2] * a.v[2] + m[0][3], 213 | m[1][0] * a.v[0] + m[1][1] * a.v[1] + m[1][2] * a.v[2] + m[0][3], 214 | m[2][0] * a.v[0] + m[2][1] * a.v[1] + m[2][2] * a.v[2] + m[0][3] 215 | }; 216 | } 217 | 218 | void ResetIdentity() { 219 | for (size_t j = 0; j < 4; j++) { 220 | for (size_t i = 0; i < 4; i++) { 221 | m[j][i] = (i == j) ? 1.0 : 0.0; 222 | } 223 | } 224 | } 225 | 226 | void ResetRotationXRad(T angle) { 227 | *this = { 228 | 1.0, 0.0, 0.0, 0.0, 229 | 0.0, cos(angle), -sin(angle), 0.0, 230 | 0.0, sin(angle), cos(angle), 0.0, 231 | 0.0, 0.0, 0.0, 1.0 232 | }; 233 | } 234 | 235 | void ResetRotationYRad(T angle) { 236 | *this = { 237 | cos(angle), 0.0, sin(angle), 0.0, 238 | 0.0, 1.0, 0.0, 0.0, 239 | -sin(angle), 0.0, cos(angle), 0.0, 240 | 0.0, 0.0, 0.0, 1.0 241 | }; 242 | } 243 | 244 | void ResetRotationZRad(T angle) { 245 | *this = { 246 | cos(angle), -sin(angle), 0.0, 0.0, 247 | sin(angle), cos(angle), 0.0, 0.0, 248 | 0.0, 0.0, 1.0, 0.0, 249 | 0.0, 0.0, 0.0, 1.0 250 | }; 251 | } 252 | 253 | static M4D_Base RotationXRad(T angle) { 254 | M4D_Base m; 255 | m.ResetRotationXRad(angle); 256 | return m; 257 | } 258 | 259 | static M4D_Base RotationYRad(T angle) { 260 | M4D_Base m; 261 | m.ResetRotationYRad(angle); 262 | return m; 263 | } 264 | 265 | static M4D_Base RotationZRad(T angle) { 266 | M4D_Base m; 267 | m.ResetRotationZRad(angle); 268 | return m; 269 | } 270 | 271 | static M4D_Base RotationXDeg(T angle) { 272 | M4D_Base m; 273 | m.ResetRotationXRad(Deg2Rad(angle)); 274 | return m; 275 | } 276 | 277 | static M4D_Base RotationYDeg(T angle) { 278 | M4D_Base m; 279 | m.ResetRotationYRad(Deg2Rad(angle)); 280 | return m; 281 | } 282 | 283 | static M4D_Base RotationZDeg(T angle) { 284 | M4D_Base m; 285 | m.ResetRotationZRad(Deg2Rad(angle)); 286 | return m; 287 | } 288 | 289 | template 290 | friend std::ostream& operator<<(std::ostream &os, const M4D_Base& a); 291 | 292 | T m[4][4]{}; 293 | }; 294 | 295 | template 296 | std::ostream& operator<<(std::ostream &os, const M4D_Base& a) { 297 | os << std::fixed << std::setprecision(5) 298 | << "[ " << a.m[0][0] << ", " << a.m[0][1] << ", " 299 | << a.m[0][2] << ", " << a.m[0][3] << " \n" 300 | << " " << a.m[1][0] << ", " << a.m[1][1] << ", " 301 | << a.m[1][2] << ", " << a.m[1][3] << " \n" 302 | << " " << a.m[2][0] << ", " << a.m[2][1] << ", " 303 | << a.m[2][2] << ", " << a.m[2][3] << " \n" 304 | << " " << a.m[3][0] << ", " << a.m[3][1] << ", " 305 | << a.m[3][2] << ", " << a.m[3][3] << " ]\n"; 306 | return os; 307 | } 308 | 309 | #define M4DStr(a) math3d::ToStr(a).c_str() 310 | 311 | 312 | // Useful typedefs. 313 | typedef V3D_Base V3D; 314 | typedef M4D_Base M4D; 315 | } // namespace math3d 316 | 317 | -------------------------------------------------------------------------------- /VerStarting/main_net_master.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #ifdef __unix__ 5 | # include 6 | # include 7 | #else 8 | # include 9 | #endif 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "mythtracer.h" 16 | #include "camera.h" 17 | #include "octtree.h" 18 | #include "network.h" 19 | 20 | using math3d::V3D; 21 | using namespace raytracer; 22 | const int W = 1920; // 960 480 23 | const int H = 1080; // 540 270 24 | const int CHUNK_W = 128; 25 | const int CHUNK_H = 128; 26 | 27 | class ReadyWorkChunk { 28 | public: 29 | std::unique_ptr work; 30 | std::string id; // Who has done the work. 31 | }; 32 | 33 | std::mutex g_work_available_guard; 34 | std::list> g_work_available; 35 | 36 | std::mutex g_work_finished_guard; 37 | std::list> g_work_finished; 38 | 39 | // https://stackoverflow.com/questions/10890242/get-the-status-of-a-stdfuture 40 | /*template 41 | bool is_ready(std::future const& f) 42 | { return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready; } 43 | */ 44 | 45 | 46 | void CommitWorkChunk(WorkChunk *work, const std::string& id) { 47 | auto ready = std::make_unique(); 48 | ready->work.reset(work); 49 | ready->id = id; 50 | 51 | std::lock_guard lock(g_work_finished_guard); 52 | g_work_finished.push_back(std::move(ready)); 53 | } 54 | 55 | void ReturnWorkChunk(WorkChunk *work_ptr) { 56 | puts("Returning work to queue."); 57 | std::lock_guard lock(g_work_available_guard); 58 | std::unique_ptr work(work_ptr); 59 | g_work_available.push_back(std::move(work)); 60 | } 61 | 62 | WorkChunk *GetWorkChunk() { 63 | // Work might not be available immediately. 64 | WorkChunk *work = nullptr; 65 | for (;;) 66 | { 67 | { 68 | std::lock_guard lock(g_work_available_guard); 69 | if (!g_work_available.empty()) { 70 | work = g_work_available.front().release(); 71 | g_work_available.pop_front(); 72 | break; 73 | } 74 | } 75 | 76 | std::this_thread::sleep_for(100ms); 77 | } 78 | 79 | return work; 80 | } 81 | 82 | void WorkerHandler(NetSock *sock) { 83 | unique_ptr s(sock); 84 | char addr[32]{}; 85 | sprintf(addr, "%s:%u", s->GetStrIP(), s->GetPort()); 86 | 87 | // Handle initial RDY! packet. 88 | std::unique_ptr p( 89 | netproto::ReceivePacket(s.get(), netproto::kCommunicationSide::kMaster)); 90 | if (p == nullptr) { 91 | printf("WH:%s: invalid proto or disconnected before RDY\n", addr); 92 | fflush(stdout); 93 | return; 94 | } 95 | 96 | if (p->GetTag() != "RDY!") { 97 | printf("WH:%s: expected RDY!, got %s\n", addr, p->GetTag().c_str()); 98 | fflush(stdout); 99 | return; 100 | } 101 | 102 | std::string id = p->id; 103 | p.reset(); 104 | 105 | printf("WH:%s is %s\n", addr, id.c_str()); 106 | 107 | // Until the worker disconnects or times out, send it stuff to do. 108 | // Unless there is no more stuff to do. 109 | for (;;) { 110 | struct WorkReturner { 111 | void operator()(WorkChunk *work) const { 112 | ReturnWorkChunk(work); 113 | }; 114 | }; 115 | std::unique_ptr work(GetWorkChunk()); 116 | if (work == nullptr) { 117 | // Done! 118 | break; 119 | } 120 | 121 | // Send camera. 122 | p.reset(netproto::MasterSetCamera::Make(id, &work->camera)); 123 | if (!netproto::SendPacket(s.get(), p.get())) { 124 | printf("WH:%s: failed to send or disconnected\n", id.c_str()); 125 | fflush(stdout); 126 | return; 127 | } 128 | 129 | // Send work chunk. 130 | p.reset(netproto::MasterRenderOrder::Make(id, work.get())); 131 | if (!netproto::SendPacket(s.get(), p.get())) { 132 | printf("WH:%s: failed to send or disconnected\n", id.c_str()); 133 | fflush(stdout); 134 | return; 135 | } 136 | 137 | printf("WH:%s: camera and work sent\n", id.c_str()); 138 | fflush(stdout); 139 | 140 | // Wait for response. 141 | p.reset(netproto::ReceivePacket(s.get(), 142 | netproto::kCommunicationSide::kMaster)); 143 | if (p == nullptr) { 144 | printf("WH:%s: invalid proto or disconnected\n", id.c_str()); 145 | fflush(stdout); 146 | return; 147 | } 148 | 149 | if (p->GetTag() != "PXLS") { 150 | printf("WH:%s: expected PXLS, got %s\n", id.c_str(), p->GetTag().c_str()); 151 | fflush(stdout); 152 | return; 153 | } 154 | 155 | if (!work->DeserializeOutput(p->bytes)) { 156 | printf("WH:%s: failed to deserialize PXLS\n", id.c_str()); 157 | fflush(stdout); 158 | return; 159 | } 160 | 161 | printf("WH:%s: sent in pixels!\n", id.c_str()); 162 | fflush(stdout); 163 | 164 | CommitWorkChunk(work.release(), id); 165 | } 166 | 167 | printf("WH:%s: no more work, disconnecting!\n", addr); 168 | fflush(stdout); 169 | } 170 | 171 | void ConnectionHandler(NetSock *server_sock) { 172 | std::unique_ptr server(server_sock); 173 | 174 | printf("CH: Listening at: %s:%u\n", 175 | server->GetStrBindIP(), server->GetBindPort()); 176 | fflush(stdout); 177 | 178 | // TODO: Add a future/promise to know when to exit. 179 | for (;;) { 180 | NetSock *s = server->Accept(); 181 | if (s == nullptr) { 182 | continue; 183 | } 184 | 185 | std::thread worker_handler_thread(WorkerHandler, s); 186 | worker_handler_thread.detach(); 187 | 188 | printf("CH: New connection from %s:%u\n", 189 | server->GetStrIP(), server->GetPort()); 190 | fflush(stdout); 191 | } 192 | } 193 | 194 | // TODO(gynvael): Add scene support. 195 | size_t GenerateWork(const MythTracer&, const Camera& cam, 196 | int width, int height) { 197 | std::lock_guard lock(g_work_available_guard); 198 | // TODO(gynvael): Add an assert that .empty() is true. 199 | g_work_available.clear(); 200 | 201 | size_t total_chunk_count = 0; 202 | for (int j = 0; j < height; j += CHUNK_H) { 203 | for (int i = 0; i < width; i += CHUNK_W, total_chunk_count++) { 204 | int chunk_width = std::min(CHUNK_W, width - i); 205 | int chunk_height = std::min(CHUNK_H, height - j); 206 | 207 | auto work = std::make_unique(); 208 | work->image_width = width; 209 | work->image_height = height; 210 | work->chunk_x = i; 211 | work->chunk_y = j; 212 | work->chunk_width = chunk_width; 213 | work->chunk_height = chunk_height; 214 | work->camera = cam; 215 | 216 | g_work_available.push_back(std::move(work)); 217 | } 218 | } 219 | 220 | return total_chunk_count; 221 | } 222 | 223 | void BlitWorkChunk(std::vector *bitmap, WorkChunk *work) { 224 | auto src_bitmap = work->output_bitmap; 225 | 226 | for (int j = 0; j < work->chunk_height; j++) { 227 | for (int i = 0; i < work->chunk_width; i++) { 228 | const size_t dst_idx = 229 | ((j + work->chunk_y) * work->image_width + (i + work->chunk_x)) * 3; 230 | const size_t src_idx = (j * work->chunk_width + i) * 3; 231 | for (int k = 0; k < 3; k++) { 232 | bitmap->at(dst_idx + k) = src_bitmap.at(src_idx + k); 233 | } 234 | } 235 | } 236 | } 237 | 238 | 239 | int main(void) { 240 | puts("Creating /anim directory"); 241 | #ifdef __unix__ 242 | mkdir("anim", 0700); 243 | #else 244 | _mkdir("anim"); 245 | #endif 246 | 247 | puts("Loading scene..."); 248 | MythTracer mt; 249 | if (!mt.LoadObj("../Models/Living Room USSU Design.obj")) { 250 | return 1; 251 | } 252 | 253 | // Add lights. 254 | mt.GetScene()->lights.clear(); 255 | mt.GetScene()->lights.push_back( 256 | Light{ 257 | { 231.82174, 81.69966, -27.78259 }, 258 | { 0.3, 0.3, 0.3 }, 259 | { 1.0, 1.0, 1.0 }, 260 | { 1.0, 1.0, 1.0 } 261 | }); 262 | 263 | mt.GetScene()->lights.push_back( 264 | Light{ 265 | { 200, 80.0, 0 }, 266 | { 0.0, 0.0, 0.0 }, 267 | { 0.3, 0.3, 0.3 }, 268 | { 0.3, 0.3, 0.3 } 269 | }); 270 | 271 | mt.GetScene()->lights.push_back( 272 | Light{ 273 | { 200, 80.0, 80 }, 274 | { 0.0, 0.0, 0.0 }, 275 | { 0.3, 0.3, 0.3 }, 276 | { 0.3, 0.3, 0.3 } 277 | }); 278 | 279 | mt.GetScene()->lights.push_back( 280 | Light{ 281 | { 200, 80.0, 160 }, 282 | { 0.0, 0.0, 0.0 }, 283 | { 0.3, 0.3, 0.3 }, 284 | { 0.3, 0.3, 0.3 } 285 | }); 286 | 287 | printf("Resolution: %u %u\n", W, H); 288 | 289 | // Really good camera setting. 290 | Camera cam{ 291 | { 300.0, 57.0, 160.0 }, 292 | 0.0, 180.0, 0.0, 293 | 110.0 294 | }; 295 | 296 | std::vector bitmap(W * H * 3); 297 | 298 | puts("Starting server..."); 299 | NetSock::InitNetworking(); 300 | auto server = std::make_unique(); 301 | // TODO(gynvael): Add option to listen to only specific interfaces. 302 | const short tcp_port = 12345; 303 | if (!server->ListenAll(tcp_port)) { 304 | fprintf(stderr, "error: failed to listen on TCP port %u\n", tcp_port); 305 | return 1; 306 | } 307 | 308 | std::thread connection_thread(ConnectionHandler, server.release()); 309 | 310 | size_t total_work_chunks = 0; 311 | size_t completed_work_chunks = 0; 312 | int frame = 0; 313 | time_t last_dump = time(nullptr); 314 | for (;;) { 315 | // Check if work for the frame needs to be generated. 316 | if (total_work_chunks == 0) { 317 | puts("Generating new work..."); fflush(stdout); 318 | completed_work_chunks = 0; 319 | total_work_chunks = GenerateWork(mt, cam, W, H); 320 | } 321 | 322 | // Check if any new work items finished. 323 | { 324 | std::lock_guard lock(g_work_finished_guard); 325 | if (!g_work_finished.empty()) { 326 | // Apply work chunks to the bitmap. 327 | for (const auto& ready : g_work_finished) { 328 | BlitWorkChunk(&bitmap, ready->work.get()); 329 | } 330 | 331 | g_work_finished.clear(); 332 | } 333 | } 334 | 335 | // Perhaps dump the current frame. 336 | if (time(nullptr) > last_dump + 2) { 337 | FILE *f = fopen("anim/frame_dump.raw", "wb"); 338 | fwrite(&bitmap[0], bitmap.size(), 1, f); 339 | fclose(f); 340 | last_dump = time(nullptr); 341 | puts("Saved frame to disk."); 342 | } 343 | 344 | // Check if frame is ready. 345 | if (total_work_chunks == completed_work_chunks) { 346 | puts("Writing frame..."); 347 | char fname[256]; 348 | sprintf(fname, "anim/dump_%.5i.raw", frame); 349 | 350 | FILE *f = fopen(fname, "wb"); 351 | fwrite(&bitmap[0], bitmap.size(), 1, f); 352 | fclose(f); 353 | 354 | // Reset stuff. 355 | memset(&bitmap[0], 0, bitmap.size()); 356 | total_work_chunks = 0; 357 | 358 | // TODO(gynvael): Iterate frame. 359 | frame++; 360 | 361 | continue; // Don't sleep. 362 | } 363 | 364 | // Sleep. 365 | std::this_thread::sleep_for(100ms); 366 | } 367 | 368 | puts("Done"); 369 | 370 | return 0; 371 | } 372 | 373 | -------------------------------------------------------------------------------- /VerStarting/NetSock/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /VerStarting/NetSock/NetSock.cpp: -------------------------------------------------------------------------------- 1 | // NetSock socket helper class 2 | // code by gynvael.coldwind//vx 3 | // http://gynvael.vexillium.org 4 | // http://vexillium.org 5 | // 6 | // additional thx to: 7 | // Mateusz "j00ru" Jurczyk 8 | // and others 9 | // 10 | // Version: 2017-07-11 11 | // 12 | // LICENSE 13 | // Copyright 2017 Gynvael Coldwind 14 | // 15 | // Licensed under the Apache License, Version 2.0 (the "License"); 16 | // you may not use this file except in compliance with the License. 17 | // You may obtain a copy of the License at 18 | // 19 | // http://www.apache.org/licenses/LICENSE-2.0 20 | // 21 | // Unless required by applicable law or agreed to in writing, software 22 | // distributed under the License is distributed on an "AS IS" BASIS, 23 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 24 | // See the License for the specific language governing permissions and 25 | // limitations under the License. 26 | // 27 | #if defined(_WIN32) && !defined(WIN32) 28 | # define WIN32 29 | #endif 30 | 31 | #if defined(WIN32) 32 | # include 33 | # include 34 | #elif defined(__unix__) 35 | # include 36 | # include 37 | # include 38 | # include 39 | # include 40 | # include 41 | # include 42 | # include 43 | # include 44 | # define closesocket(a) close(a) 45 | #endif 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include "NetSock.h" 52 | 53 | const int NetSock::SYNCHRONIC = 1; 54 | const int NetSock::ASYNCHRONIC = 2; 55 | 56 | NetSock::NetSock() 57 | { 58 | this->socket = -1; 59 | this->ip = 0x00000000; 60 | this->mode = 0; 61 | this->port = 0; 62 | this->isUDP = false; 63 | this->UDPCanBroadcast = false; 64 | 65 | this->bindip = 0; 66 | this->bindport = 0; 67 | 68 | this->str_ip[0] = '\0'; 69 | this->str_bindip[0] = '\0'; 70 | } 71 | 72 | NetSock::~NetSock() 73 | { 74 | this->Disconnect(); 75 | } 76 | 77 | bool 78 | NetSock::InitNetworking(void) 79 | { 80 | #ifdef _WIN32 81 | WSADATA wsdat; 82 | memset(&wsdat, 0, sizeof(wsdat)); 83 | 84 | if(WSAStartup(MAKEWORD(2,2), &wsdat)) 85 | return false; 86 | #endif 87 | 88 | return true; 89 | } 90 | 91 | bool 92 | NetSock::ListenUDP(unsigned short bindport, const char *bindhost) 93 | { 94 | int ret; 95 | sockaddr_in desc; 96 | 97 | desc.sin_family = AF_INET; 98 | desc.sin_addr.s_addr = inet_addr(bindhost); // TODO: fix this 99 | desc.sin_port = htons(bindport); 100 | memset(desc.sin_zero, 0, sizeof(desc.sin_zero)); 101 | 102 | ret = ::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 103 | if(ret == -1) 104 | return false; 105 | 106 | this->socket = ret; 107 | this->bindport = bindport; 108 | this->bindip = htonl(desc.sin_addr.s_addr); 109 | 110 | this->isUDP = true; 111 | 112 | // bind ip it! 113 | if(bind(this->socket, (sockaddr*)&desc, sizeof(sockaddr)) == -1) 114 | return false; 115 | 116 | return true; 117 | } 118 | 119 | bool 120 | NetSock::ListenAllUDP(unsigned short bindport) 121 | { 122 | return this->ListenUDP(bindport, "0.0.0.0"); 123 | } 124 | 125 | bool 126 | NetSock::Listen(unsigned short port, const char *bindip) 127 | { 128 | sockaddr_in desc; 129 | int ret; 130 | 131 | desc.sin_family = AF_INET; 132 | desc.sin_addr.s_addr = inet_addr(bindip); 133 | desc.sin_port = htons(port); 134 | 135 | ret = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 136 | if(ret == -1) { 137 | return false; 138 | } 139 | 140 | this->socket = ret; 141 | 142 | int enable = 1; 143 | if (setsockopt(this->socket, SOL_SOCKET, 144 | SO_REUSEADDR, (const char*)&enable, sizeof(int)) < 0) { 145 | return false; 146 | } 147 | 148 | if(bind(this->socket, (sockaddr*)&desc, sizeof(sockaddr)) == -1) { 149 | return false; 150 | } 151 | this->bindip = desc.sin_addr.s_addr; 152 | this->bindport = port; 153 | 154 | if(listen(this->socket, SOMAXCONN) == -1) { 155 | return false; 156 | } 157 | 158 | return true; 159 | } 160 | 161 | bool 162 | NetSock::ListenAll(unsigned short port) 163 | { 164 | return this->Listen(port, "0.0.0.0"); 165 | } 166 | 167 | NetSock * 168 | NetSock::Accept() 169 | { 170 | sockaddr_in desc; 171 | int remote; 172 | #if defined(WIN32) 173 | int size; 174 | #elif defined(__unix__) 175 | socklen_t size; 176 | #endif 177 | 178 | size = sizeof(sockaddr); 179 | remote = accept(this->socket, (sockaddr*)&desc, &size); 180 | if(remote == -1) 181 | return NULL; 182 | 183 | NetSock *NewSock = new NetSock; 184 | 185 | NewSock->socket = remote; 186 | NewSock->port = htons(desc.sin_port); 187 | memcpy(&NewSock->ip, &desc.sin_addr.s_addr, 4); 188 | 189 | return NewSock; 190 | } 191 | 192 | bool 193 | NetSock::Connect(const char* host, unsigned short port) 194 | { 195 | unsigned int ip; 196 | 197 | ip = (unsigned int)inet_addr(host); 198 | if(ip == INADDR_NONE) 199 | { 200 | struct hostent *hostip; 201 | 202 | // resolve 203 | hostip = gethostbyname(host); 204 | if(!hostip) 205 | return false; 206 | 207 | memcpy(&ip, hostip->h_addr_list[0], 4); 208 | } 209 | 210 | return this->Connect(htonl(ip), port); 211 | } 212 | 213 | bool 214 | NetSock::Connect(unsigned int ip, unsigned short port) 215 | { 216 | struct sockaddr_in sock_info; 217 | int ret, sock = -1; 218 | 219 | if(this->socket != -1) 220 | throw "Socket already exists"; 221 | 222 | ret = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 223 | if(ret == -1) 224 | throw "Socket create failed"; 225 | 226 | sock = ret; 227 | 228 | // sock info 229 | sock_info.sin_family = AF_INET; 230 | sock_info.sin_addr.s_addr = htonl(ip); 231 | sock_info.sin_port = htons(port); 232 | memset(sock_info.sin_zero, 0, sizeof(sock_info.sin_zero)); 233 | 234 | // conn 235 | ret = connect(sock, (struct sockaddr*)&sock_info, sizeof( struct sockaddr )); 236 | if(ret == -1) 237 | { 238 | closesocket(sock); 239 | return false; 240 | } 241 | 242 | // ok 243 | this->ip = ip; 244 | this->port = port; 245 | this->mode = this->SYNCHRONIC; 246 | this->socket = sock; 247 | return true; 248 | } 249 | 250 | bool 251 | NetSock::SetMode(int mode) 252 | { 253 | #if defined(WIN32) 254 | unsigned long mode_temp; 255 | #endif 256 | if(this->socket == -1) 257 | return false; 258 | 259 | if(this->mode == mode) 260 | return true; 261 | 262 | switch(mode) 263 | { 264 | case NetSock::SYNCHRONIC: 265 | #if defined(WIN32) 266 | mode_temp = 0; 267 | ioctlsocket(this->socket, FIONBIO, &mode_temp); 268 | #else 269 | fcntl(this->socket, F_SETFL, 0); 270 | #endif 271 | break; 272 | 273 | case NetSock::ASYNCHRONIC: 274 | #if defined(WIN32) 275 | mode_temp = 1; 276 | ioctlsocket(this->socket, FIONBIO, &mode_temp); 277 | #else 278 | fcntl(this->socket, F_SETFL, O_NONBLOCK); 279 | #endif 280 | break; 281 | } 282 | 283 | this->mode = mode; 284 | return true; 285 | } 286 | 287 | bool 288 | NetSock::Disconnect() 289 | { 290 | if(this->socket == -1) 291 | return false; 292 | 293 | closesocket(this->socket); 294 | this->socket = -1; 295 | this->ip = 0x00000000; 296 | this->mode = 0; 297 | this->port = 0; 298 | return true; 299 | } 300 | 301 | int 302 | NetSock::Read(void *Buffer, int Size) 303 | { 304 | if(this->socket == -1) 305 | return -1; 306 | 307 | // Windows requires a char* cast here. Won't make a difference for 308 | // *nix I guess. 309 | return recv(this->socket, (char*)Buffer, Size, 0); 310 | } 311 | 312 | int 313 | NetSock::ReadAll(void *Buffer, int Size) 314 | { 315 | if(this->socket == -1) { 316 | return -1; 317 | } 318 | 319 | // If non-blocking, switch to blocking mode to 320 | // save CPU cycles. This function is always blocking. 321 | int old_mode = this->mode; 322 | if(this->mode == ASYNCHRONIC) 323 | this->SetMode(SYNCHRONIC); // Ignore fail. 324 | 325 | // Read data. 326 | int received = 0; 327 | bool error_state = false; 328 | 329 | while(received != Size) 330 | { 331 | // Read a portion of data. 332 | int left = Size - received; 333 | 334 | int ret = this->Read((int8_t*)Buffer + received, left); 335 | if(ret <= 0) 336 | { 337 | error_state = true; 338 | break; 339 | } 340 | 341 | received += ret; 342 | } 343 | 344 | // Switch back mode if needed. 345 | if(old_mode == ASYNCHRONIC) 346 | this->SetMode(ASYNCHRONIC); 347 | 348 | // Return. 349 | if(error_state) { 350 | return -1; 351 | } 352 | 353 | return received; 354 | } 355 | 356 | int 357 | NetSock::Write(const void *Buffer, int Size) 358 | { 359 | if(this->socket == -1) 360 | return -1; 361 | 362 | // Windows requires a const char* cast here. Won't make a 363 | // difference for *nix I guess. 364 | return send(this->socket, (const char*)Buffer, Size, 0); 365 | } 366 | 367 | int 368 | NetSock::WriteAll(const void *Buffer, int Size) 369 | { 370 | if(this->socket == -1) 371 | return -1; 372 | 373 | int ret, ptr = 0; 374 | 375 | while(ptr != Size) 376 | { 377 | // Windows requires a const char* cast here. Won't make a 378 | // difference for *nix I guess. 379 | ret = send(this->socket, (const char*)((int8_t*)Buffer + ptr), 380 | Size - ptr, 0); 381 | 382 | // Thx to the anonymous . for pointing a bug that was previously 383 | // here. 384 | if(ret == 0) 385 | { 386 | continue; // TODO: Add some sleep here, since obviously 387 | // it cannot be sent now 388 | } 389 | else if(ret == -1) 390 | { 391 | // What's the problem? 392 | #ifdef __unix__ 393 | if(errno == EAGAIN || errno == EWOULDBLOCK) 394 | continue; 395 | #elif _WIN32 396 | if(WSAGetLastError() == WSAEWOULDBLOCK) 397 | continue; 398 | #endif 399 | 400 | // Seems it was an error. 401 | return 0; 402 | } 403 | 404 | ptr += ret; 405 | } 406 | 407 | return Size; 408 | } 409 | 410 | 411 | unsigned short 412 | NetSock::GetPort() const 413 | { 414 | return this->port; 415 | } 416 | 417 | unsigned int 418 | NetSock::GetIP() const 419 | { 420 | return this->ip; 421 | } 422 | 423 | const char * 424 | NetSock::GetStrIP() 425 | { 426 | // cached ? 427 | if(this->str_ip[0]) 428 | return this->str_ip; 429 | 430 | in_addr in_addr_ip; 431 | memcpy(&in_addr_ip, &this->ip, 4); 432 | 433 | strncpy(this->str_ip, inet_ntoa(in_addr_ip), 16); 434 | this->str_ip[15] = '\0'; 435 | return this->str_ip; 436 | } 437 | 438 | unsigned short 439 | NetSock::GetBindPort() const 440 | { 441 | return this->bindport; 442 | } 443 | 444 | unsigned int 445 | NetSock::GetBindIP() const 446 | { 447 | return this->bindip; 448 | } 449 | 450 | const char * 451 | NetSock::GetStrBindIP() 452 | { 453 | // cached ? 454 | if(this->str_bindip[0]) 455 | return this->str_bindip; 456 | 457 | in_addr in_bind_ip; 458 | memcpy(&in_bind_ip, &this->bindip, 4); 459 | 460 | strncpy(this->str_bindip, inet_ntoa(in_bind_ip), 16); 461 | this->str_bindip[15] = '\0'; 462 | return this->str_bindip; 463 | } 464 | 465 | int 466 | NetSock::BroadcastUDP(const char* broadcast, unsigned short port, const void *buffer, int size) 467 | { 468 | // Sanity check 469 | if(this->socket == -1) 470 | return -1; 471 | 472 | // Address 473 | struct sockaddr_in sock_info; 474 | 475 | memset(&sock_info, 0, sizeof(sock_info)); 476 | 477 | sock_info.sin_family = AF_INET; 478 | sock_info.sin_addr.s_addr = inet_addr(broadcast); 479 | sock_info.sin_port = htons(port); 480 | 481 | if(!this->UDPCanBroadcast) 482 | { 483 | #ifdef WIN32 484 | int OptVal = 1; 485 | if(setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char*)&OptVal, sizeof(BOOL)) == 486 | SOCKET_ERROR) 487 | return -2; 488 | 489 | // Double check if this works. 490 | int OptValLen = sizeof(OptVal); 491 | if(getsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char*)&OptVal, &OptValLen) == SOCKET_ERROR) 492 | return -3; 493 | #else 494 | int OptVal = 1; 495 | if(setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char*)&OptVal, sizeof(OptVal)) == -1) 496 | return -2; 497 | #endif 498 | 499 | UDPCanBroadcast = true; 500 | } 501 | 502 | memset(sock_info.sin_zero, 0, sizeof(sock_info.sin_zero)); 503 | 504 | #ifndef WIN32_OLD 505 | return sendto(socket, (const char*)buffer, size, 0, (struct sockaddr*)&sock_info, sizeof(struct sockaddr)); 506 | #else 507 | return sendto(socket, buffer, size, 0, (struct sockaddr*)&sock_info, sizeof(struct sockaddr)); 508 | #endif 509 | } 510 | 511 | int 512 | NetSock::WriteUDP(const char* host, unsigned short port, const void *buffer, int size) 513 | { 514 | // Sanity check 515 | if(this->socket == -1) 516 | return -1; 517 | 518 | // Address 519 | struct sockaddr_in sock_info; 520 | 521 | memset(&sock_info, 0, sizeof(sock_info)); 522 | 523 | sock_info.sin_family = AF_INET; 524 | sock_info.sin_addr.s_addr = inet_addr(host); // Change this to 'host' later 525 | sock_info.sin_port = htons(port); 526 | 527 | memset(sock_info.sin_zero, 0, sizeof(sock_info.sin_zero)); 528 | 529 | #ifndef WIN32_OLD 530 | return sendto(socket, (const char*)buffer, size, 0, (struct sockaddr*)&sock_info, sizeof(struct sockaddr)); 531 | #else 532 | return sendto(socket, buffer, size, 0, (struct sockaddr*)&sock_info, sizeof(struct sockaddr)); 533 | #endif 534 | } 535 | 536 | int 537 | NetSock::ReadUDP(void *buffer, int size, char *srchost, unsigned short *srcport) 538 | { 539 | // Sanity check 540 | if(this->socket == -1) 541 | return -1; 542 | 543 | // Recieve 544 | struct sockaddr_in srcaddr; 545 | #if !defined (WIN32_OLD) && !defined (__unix__) 546 | typedef int socklen_t; 547 | #endif 548 | socklen_t len = sizeof(srcaddr); 549 | #ifndef WIN32_OLD 550 | int ret = recvfrom(socket, (char*)buffer, size, 0, (struct sockaddr*)&srcaddr, &len); 551 | #else 552 | int ret = recvfrom(socket, buffer, size, 0, (struct sockaddr*)&srcaddr, &len); 553 | #endif 554 | 555 | // Fill some info ? 556 | if(ret > 0) 557 | { 558 | if(srchost) 559 | { 560 | strncpy(srchost, inet_ntoa(srcaddr.sin_addr), 16); // htonl? 561 | srchost[15] = '\0'; 562 | } 563 | 564 | if(srcport) 565 | { 566 | *srcport = htons(srcaddr.sin_port); 567 | } 568 | } 569 | 570 | // Done 571 | return ret; 572 | } 573 | 574 | int NetSock::GetDescriptor() const { 575 | return this->socket; 576 | } 577 | 578 | -------------------------------------------------------------------------------- /VerStarting/mythtracer.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "mythtracer.h" 8 | 9 | using namespace raytracer; 10 | using math3d::V3D; 11 | using math3d::M4D; 12 | 13 | V3D MythTracer::TraceRayWorker( 14 | const Ray& ray, int level, 15 | bool in_object, // Used in transparency. 16 | V3D::basetype current_reflection_coef, 17 | PerPixelDebugInfo *debug) { 18 | V3D intersection_point; 19 | V3D::basetype intersection_distance; 20 | auto primitive = scene.tree.IntersectRay( 21 | ray, &intersection_point, &intersection_distance); 22 | 23 | if (primitive == nullptr) { 24 | if (debug != nullptr) { 25 | debug->line_no = -1; 26 | debug->point = { NAN, NAN, NAN /* Batman! */ }; 27 | } 28 | 29 | // Background color. 30 | return { 0.0, 0.0, 0.0 }; 31 | } 32 | 33 | if (debug != nullptr) { 34 | debug->line_no = primitive->debug_line_no; 35 | debug->point = intersection_point; 36 | } 37 | 38 | V3D normal = primitive->GetNormal(intersection_point); 39 | 40 | V3D towards_camera = -ray.direction; 41 | V3D::basetype normal_ray_dot = normal.Dot(towards_camera); 42 | if (normal_ray_dot < 0.0) { 43 | normal = -normal; 44 | normal_ray_dot = normal.Dot(towards_camera); 45 | } 46 | 47 | // If no other material information is available, use only the normal-ray dot 48 | // product. 49 | if (primitive->mtl == nullptr) { 50 | normal_ray_dot = (normal_ray_dot + 1.0) * 0.5; 51 | return { normal_ray_dot, normal_ray_dot, normal_ray_dot }; 52 | } 53 | 54 | // Calculate the actual color. 55 | // Based on https://en.wikipedia.org/wiki/Phong_reflection_model 56 | auto mtl = primitive->mtl; 57 | 58 | V3D surface_color = mtl->ambient; 59 | if (mtl->tex) { 60 | V3D uvw = primitive->GetUVW(intersection_point); 61 | V3D tex_color = mtl->tex->GetColorAt( 62 | uvw.v[0], uvw.v[1], intersection_distance); 63 | surface_color *= tex_color; 64 | } 65 | 66 | // Ray reflection. 67 | // http://paulbourke.net/geometry/reflected/ 68 | V3D reflected_direction = 69 | ray.direction - normal * (2 * ray.direction.Dot(normal)); 70 | Ray reflected_ray{ 71 | // TODO(gynvael): Pick a better epsilon. 72 | intersection_point + (reflected_direction * 0.0001), 73 | reflected_direction 74 | }; 75 | 76 | V3D color{}; 77 | 78 | for (const auto& light : scene.lights) { 79 | V3D light_direction = light.position - intersection_point; 80 | light_direction.Norm(); 81 | 82 | // Ambient light is always effective. 83 | color += light.ambient * 84 | surface_color; 85 | 86 | // Cast a ray between the intersection point and the light to determine 87 | // whether the light affects the given point (or whether the point is in 88 | // the shadow). 89 | // Traverse through all transparent or translucent surfaces. 90 | V3D light_power{1.0, 1.0, 1.0}; 91 | bool in_shadow = false; 92 | 93 | bool traversing_through_object = false; 94 | for (V3D start_point = intersection_point;;) { 95 | Ray shadow_ray{ 96 | // TODO(gynvael): Pick a better epsilon. 97 | start_point + (light_direction * 0.00001), 98 | light_direction 99 | }; 100 | 101 | V3D::basetype light_distance = 102 | start_point.Distance(light.position); 103 | 104 | V3D shadow_intersection_point; 105 | V3D::basetype shadow_distance; 106 | auto shadow_primitive = scene.tree.IntersectRay( 107 | shadow_ray, &shadow_intersection_point, &shadow_distance); 108 | 109 | if (shadow_primitive == nullptr) { 110 | // Nothing found. Done. 111 | break; 112 | } 113 | 114 | // Perhaps the light was closer. 115 | if (shadow_distance > light_distance) { 116 | // Primitive was behind the light source. 117 | break; 118 | } 119 | 120 | // If the primitive is not transparent, then we are in a shadow. 121 | if (shadow_primitive->mtl->transparency == 0.0) { 122 | light_power = { 0.0, 0.0, 0.0 }; 123 | in_shadow = true; 124 | break; 125 | } 126 | 127 | 128 | // Some light passes through. 129 | if (!traversing_through_object) { 130 | light_power *= shadow_primitive->mtl->transmission_filter * 131 | shadow_primitive->mtl->transparency; 132 | } 133 | 134 | traversing_through_object = !traversing_through_object; 135 | 136 | // Change the starting point and continue. 137 | start_point = shadow_intersection_point + (light_direction * 0.0000001); 138 | 139 | // There is an unlikely event that the new starting point is actually 140 | // behind the light. In such case, break. 141 | if (intersection_point.SqrDistance(start_point) > 142 | intersection_point.SqrDistance(light.position)) { 143 | // Already behind the light. No more shadow opportunities. 144 | break; 145 | } 146 | 147 | // If the light power is below the ambient threashold, just stop here and 148 | // mark as shadow. 149 | if (light_power.v[0] <= 0.001 && 150 | light_power.v[1] <= 0.001 && 151 | light_power.v[2] <= 0.001) { 152 | light_power = { 0.0, 0.0, 0.0 }; 153 | in_shadow = true; 154 | break; 155 | } 156 | } 157 | 158 | // Actually do use ambient for light power. 159 | light_power.v[0] = std::max(light_power.v[0], light.ambient.v[0]); 160 | light_power.v[1] = std::max(light_power.v[1], light.ambient.v[1]); 161 | light_power.v[2] = std::max(light_power.v[2], light.ambient.v[2]); 162 | 163 | color += mtl->diffuse * 164 | surface_color * 165 | light_direction.Dot(normal) * 166 | light.diffuse * 167 | light_power; 168 | 169 | if (!in_shadow) { 170 | auto refl_dot = reflected_direction.Dot(towards_camera); 171 | if (refl_dot > 0) { 172 | color += mtl->specular * 173 | surface_color * 174 | pow(refl_dot, mtl->specular_exp) * 175 | light.specular; 176 | } 177 | } 178 | } 179 | 180 | // Reflection. 181 | if (level < MAX_RECURSION_LEVEL && 182 | mtl->reflectance > 0.0 && 183 | current_reflection_coef > 0.01 && 184 | !in_object) { 185 | color += TraceRayWorker( 186 | reflected_ray, 187 | level + 1, in_object, current_reflection_coef * mtl->reflectance, 188 | nullptr) * mtl->reflectance; 189 | } 190 | 191 | // Refration. 192 | if (level < MAX_RECURSION_LEVEL && mtl->transparency > 0.0) { 193 | V3D::basetype refraction_index = mtl->refraction_index; 194 | if (in_object) { 195 | //refraction_index = 1.0 / refraction_index; 196 | } 197 | 198 | V3D::basetype partial_res = 199 | 1.0 - refraction_index * refraction_index * ( 200 | 1.0 - normal_ray_dot * normal_ray_dot); 201 | 202 | // Due to floating point inacuracies this might be below zero. Saturate 203 | // at zero in that case. 204 | if (partial_res < 0.0) { 205 | partial_res = 0.0; 206 | } 207 | 208 | V3D refracted_direction = ray.direction; 209 | // TODO(gynvael): Fix this math. 210 | //normal * (refraction_index * normal_ray_dot - sqrt(partial_res)) - 211 | //ray.direction * refraction_index; 212 | refracted_direction.Norm(); 213 | 214 | Ray refracted_ray{ 215 | // TODO(gynvael): Pick a better epsilon. 216 | intersection_point + refracted_direction * 0.00001, 217 | refracted_direction 218 | }; 219 | 220 | color += TraceRayWorker( 221 | refracted_ray, 222 | level + 1, !in_object, 223 | current_reflection_coef, 224 | nullptr) * mtl->transmission_filter * mtl->transparency; 225 | } 226 | 227 | return color; 228 | } 229 | 230 | V3D MythTracer::TraceRay( 231 | const Ray& ray, PerPixelDebugInfo *debug) { 232 | return TraceRayWorker(ray, 0, false, 1.0, debug); 233 | } 234 | 235 | void MythTracer::V3DtoRGB(const V3D& v, uint8_t rgb[3]) { 236 | for (int i = 0; i < 3; i++) { 237 | rgb[i] = v.v[i] > 1.0 ? 255 : 238 | v.v[i] < 0.0 ? 0 : 239 | (uint8_t)(v.v[i] * 255); 240 | } 241 | } 242 | 243 | Scene *MythTracer::GetScene() { 244 | return &scene; 245 | } 246 | 247 | bool MythTracer::LoadObj(const char *fname) { 248 | puts("Reading .OBJ file."); 249 | ObjFileReader objreader; 250 | if (!objreader.ReadObjFile(&scene, fname)) { 251 | return false; 252 | } 253 | 254 | was_scene_finalized = false; 255 | return true; 256 | } 257 | 258 | bool MythTracer::RayTrace( 259 | int image_width, int image_height, 260 | Camera *camera, 261 | std::vector *output_bitmap) { 262 | WorkChunk chunk{ 263 | image_width, image_height, 264 | 0, 0, image_width, image_height, 265 | *camera, {}, {} 266 | }; 267 | chunk.output_bitmap.resize(image_width * image_height * 3); 268 | 269 | bool res = RayTrace(&chunk); 270 | if (!res) { 271 | return false; 272 | } 273 | 274 | *output_bitmap = std::move(chunk.output_bitmap); 275 | 276 | return true; 277 | } 278 | 279 | 280 | bool MythTracer::RayTrace(WorkChunk *chunk) { 281 | if (!was_scene_finalized) { 282 | puts("Finalizing tree."); 283 | scene.tree.Finalize(); 284 | was_scene_finalized = true; 285 | } 286 | 287 | puts("Rendering."); 288 | const clock_t tm_start = clock(); 289 | Camera::Sensor sensor = chunk->camera.GetSensor( 290 | chunk->image_width, chunk->image_height); 291 | 292 | #pragma omp parallel 293 | { 294 | #pragma omp for 295 | for (int j = 0; j < chunk->chunk_height; j++) { 296 | for (int i = 0; i < chunk->chunk_width; i++) { 297 | V3D color = TraceRay( 298 | sensor.GetRay(chunk->chunk_x + i, chunk->chunk_y + j), 299 | !chunk->output_debug.empty() ? 300 | &chunk->output_debug[j * chunk->chunk_width + i] : nullptr); 301 | V3DtoRGB(color, &chunk->output_bitmap[(j * chunk->chunk_width + i) * 3]); 302 | } 303 | putchar('.'); fflush(stdout); 304 | } 305 | } 306 | 307 | const clock_t tm_end = clock(); 308 | const float tm = (float)(tm_end - tm_start) / (float)CLOCKS_PER_SEC; 309 | printf("%.3fs\n", tm); 310 | 311 | return true; 312 | } 313 | 314 | void WorkChunk::SerializeInput(std::vector *bytes) { 315 | bytes->resize(kSerializedInputSize); 316 | 317 | // TODO(gynvael): Make this sane. 318 | uint8_t *ptr = &(*bytes)[0]; 319 | 320 | uint32_t u_image_width = image_width; 321 | uint32_t u_image_height = image_height; 322 | uint32_t u_chunk_x = chunk_x; 323 | uint32_t u_chunk_y = chunk_y; 324 | uint32_t u_chunk_width = chunk_width; 325 | uint32_t u_chunk_height = chunk_height; 326 | 327 | memcpy(ptr, &u_image_width, sizeof(uint32_t)); ptr += sizeof(uint32_t); 328 | memcpy(ptr, &u_image_height, sizeof(uint32_t)); ptr += sizeof(uint32_t); 329 | memcpy(ptr, &u_chunk_x, sizeof(uint32_t)); ptr += sizeof(uint32_t); 330 | memcpy(ptr, &u_chunk_y, sizeof(uint32_t)); ptr += sizeof(uint32_t); 331 | memcpy(ptr, &u_chunk_width, sizeof(uint32_t)); ptr += sizeof(uint32_t); 332 | memcpy(ptr, &u_chunk_height, sizeof(uint32_t)); 333 | } 334 | 335 | bool WorkChunk::DeserializeInput(const std::vector& bytes) { 336 | if (bytes.size() != kSerializedInputSize) { 337 | return false; 338 | } 339 | 340 | uint32_t u_image_width; 341 | uint32_t u_image_height; 342 | uint32_t u_chunk_x; 343 | uint32_t u_chunk_y; 344 | uint32_t u_chunk_width; 345 | uint32_t u_chunk_height; 346 | 347 | // TODO(gynvael): Make this sane. 348 | const uint8_t *ptr = &bytes[0]; 349 | memcpy(&u_image_width, ptr, sizeof(uint32_t)); ptr += sizeof(uint32_t); 350 | memcpy(&u_image_height, ptr, sizeof(uint32_t)); ptr += sizeof(uint32_t); 351 | memcpy(&u_chunk_x, ptr, sizeof(uint32_t)); ptr += sizeof(uint32_t); 352 | memcpy(&u_chunk_y, ptr, sizeof(uint32_t)); ptr += sizeof(uint32_t); 353 | memcpy(&u_chunk_width, ptr, sizeof(uint32_t)); ptr += sizeof(uint32_t); 354 | memcpy(&u_chunk_height, ptr, sizeof(uint32_t)); 355 | 356 | // A set of constraints. 357 | // TODO(gynvael): Break this up, add error messages. Here and everywhere else. 358 | if (u_image_width > 100000 || 359 | u_image_height > 100000 || 360 | u_chunk_x > u_image_width || 361 | u_chunk_y > u_image_height || 362 | u_chunk_width > u_image_width || 363 | u_chunk_height > u_image_height || 364 | u_chunk_x + u_chunk_width > u_image_width || 365 | u_chunk_y + u_chunk_height > u_image_height || 366 | u_image_width == 0 || 367 | u_image_height == 0 || 368 | u_chunk_width == 0 || 369 | u_chunk_height == 0) { 370 | return false; 371 | } 372 | 373 | image_width = u_image_width; 374 | image_height = u_image_height; 375 | chunk_x = u_chunk_x; 376 | chunk_y = u_chunk_y; 377 | chunk_width = u_chunk_width; 378 | chunk_height = u_chunk_height; 379 | 380 | return true; 381 | } 382 | 383 | bool WorkChunk::SerializeOutput(std::vector *bytes) { 384 | if (output_bitmap.size() > std::numeric_limits::max()) { 385 | fprintf(stderr, "error: too large WorkerChunk, cannot serialize\n"); 386 | return false; 387 | } 388 | 389 | uint32_t sz = output_bitmap.size(); 390 | 391 | // TODO(gynvael): Make this sane. 392 | bytes->resize(sizeof(uint32_t) + sz); 393 | uint8_t *ptr = &(*bytes)[0]; 394 | memcpy(ptr, &sz, sizeof(uint32_t)); ptr += sizeof(uint32_t); 395 | memcpy(ptr, &output_bitmap[0], output_bitmap.size()); 396 | return true; 397 | } 398 | 399 | bool WorkChunk::DeserializeOutput(const std::vector& bytes) { 400 | if (bytes.size() < kSerializedOutputMinimumSize) { 401 | return false; 402 | } 403 | 404 | // TODO(gynvael): Make this sane. 405 | const uint8_t *ptr = &bytes[0]; 406 | 407 | uint32_t sz; 408 | memcpy(&sz, ptr, sizeof(uint32_t)); ptr += sizeof(uint32_t); 409 | 410 | uint64_t partial_chunk_sz = (uint64_t)chunk_width * (uint64_t)chunk_height; 411 | if (sz / 3 != partial_chunk_sz || sz % 3 != 0) { 412 | return false; 413 | } 414 | 415 | uint64_t chunk_sz = partial_chunk_sz * 3; 416 | // TODO(gynvael): This check is probably redundant. Remove if so. 417 | if (chunk_sz != sz) { 418 | return false; 419 | } 420 | 421 | if (chunk_sz > std::numeric_limits::max()) { 422 | return false; 423 | } 424 | 425 | output_bitmap.resize(chunk_sz); 426 | memcpy(&output_bitmap[0], ptr, chunk_sz); 427 | 428 | return true; 429 | } 430 | 431 | -------------------------------------------------------------------------------- /VerStarting/objreader.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "math3d.h" 9 | #include "objreader.h" 10 | #include "primitive_triangle.h" 11 | #include "texture.h" 12 | 13 | namespace raytracer { 14 | 15 | using math3d::V3D; 16 | 17 | // Deleter shamelessly taken from dev.krzaq.cc ;) 18 | struct FileDeleter { 19 | void operator()(FILE *f) const { 20 | fclose(f); 21 | } 22 | }; 23 | 24 | bool ObjFileReader::ReadNotImplemented(const char *) { 25 | return true; 26 | } 27 | 28 | bool ObjFileReader::ReadMaterialLibrary(const char *line) { 29 | char fname[256]; 30 | if (sscanf(line, "mtllib %255[^\n]", fname) != 1) { 31 | fprintf(stderr, "warning: unsupported mtllib format \"%s\"\n", line); 32 | return false; 33 | } 34 | 35 | std::string path = base_directory.empty() ? 36 | fname : 37 | base_directory + "/" + fname; 38 | 39 | MtlFileReader mtlreader; 40 | return mtlreader.ReadMtlFile(scene, path.c_str()); 41 | } 42 | 43 | bool ObjFileReader::ReadVertex(const char *line) { 44 | double x, y, z; 45 | if (sscanf(line, "v %lf %lf %lf", &x, &y, &z) != 3) { 46 | fprintf(stderr, "warning: unsupported vertex format \"%s\"\n", line); 47 | return false; 48 | } 49 | 50 | vertices.push_back({x, y, z}); 51 | return true; 52 | } 53 | 54 | bool ObjFileReader::ReadUVW(const char *line) { 55 | double u, v, w = 0.0; // w is optional. 56 | if (sscanf(line, "vt %lf %lf %lf", &u, &v, &w) < 2) { 57 | fprintf(stderr, "warning: unsupported texcoord format \"%s\"\n", line); 58 | return false; 59 | } 60 | 61 | texcoords.push_back({u, v, w}); 62 | return true; 63 | } 64 | 65 | bool ObjFileReader::ReadNormals(const char *line) { 66 | double x, y, z; 67 | if (sscanf(line, "vn %lf %lf %lf", &x, &y, &z) != 3) { 68 | fprintf(stderr, "warning: unsupported normal format \"%s\"\n", line); 69 | return false; 70 | } 71 | 72 | normals.push_back({x, y, z}); 73 | return true; 74 | } 75 | 76 | bool ObjFileReader::ReadUseMaterial(const char *line) { 77 | char name[128]; 78 | // Note: There is a small chancee that some MTL/OBJ files might have these 79 | // names with spaces - in such case this code should be changed. 80 | if (sscanf(line, "usemtl %127s", name) != 1) { 81 | fprintf(stderr, "warning: unsupported newmtl format\n"); 82 | return false; 83 | } 84 | 85 | auto mtl_itr = scene->materials.find(name); 86 | if (mtl_itr == scene->materials.end()) { 87 | fprintf(stderr, "warning: material \"%s\" not found\n", name); 88 | selected_material = nullptr; 89 | return true; // Keep parsing. 90 | } 91 | 92 | selected_material = mtl_itr->second.get(); 93 | return true; 94 | } 95 | 96 | bool ObjFileReader::ReadFace(const char *line) { 97 | // Can be one of four formats: 98 | // f v ... 99 | // f v/vt ... 100 | // f v//vn ... 101 | // f v/vt/vn ... 102 | 103 | std::stringstream s(line); 104 | std::string token; 105 | s >> token; // Ignore the first one. 106 | 107 | std::vector vertex_indexes; 108 | std::vector normal_indexes; 109 | std::vector texcoord_indexes; 110 | 111 | while (s.good()) { 112 | s >> token; 113 | if (s.eof()) { 114 | break; 115 | } 116 | 117 | int v = 0, vt = 0, vn = 0; 118 | if (sscanf(token.c_str(), "%i/%i/%i", &v, &vt, &vn) != 3 && 119 | sscanf(token.c_str(), "%i//%i", &v, &vn) != 2 && 120 | sscanf(token.c_str(), "%i/%i", &v, &vt) != 2 && 121 | sscanf(token.c_str(), "%i", &v) != 1) { 122 | fprintf(stderr, "warning: unsupported face format \"%s\"\n", 123 | token.c_str()); 124 | return false; 125 | } 126 | 127 | // The indexes in the file are 1-based. Correct them. 128 | v -= 1; vt -= 1; vn -= 1; 129 | 130 | vertex_indexes.push_back(v); 131 | normal_indexes.push_back(vn); 132 | texcoord_indexes.push_back(vt); 133 | } 134 | 135 | if (vertex_indexes.size() != 3 && vertex_indexes.size() != 4) { 136 | fprintf(stderr, "warning: unsupported face count (%i)\n %s\n", 137 | (int)vertex_indexes.size(), line); 138 | return false; 139 | } 140 | 141 | // If it's a quad, add duplicate the first vertex as an additional one. 142 | if (vertex_indexes.size() == 4) { 143 | vertex_indexes.push_back(vertex_indexes[0]); 144 | normal_indexes.push_back(normal_indexes[0]); 145 | texcoord_indexes.push_back(texcoord_indexes[0]); 146 | } 147 | 148 | // Add triangle(s) to the scene. 149 | // First: 0 1 2 150 | // Second (if any): 2 3 0 151 | for (size_t i = 3; i <= vertex_indexes.size(); i += 2) { 152 | Triangle *tr = new Triangle(); 153 | 154 | for (size_t j = 0; j < 3; j++) { 155 | tr->vertex[j] = vertices[vertex_indexes[i - 3 + j]]; 156 | } 157 | 158 | // Add normals (if any). 159 | if (normal_indexes[i - 3 + 0] != -1 && 160 | normal_indexes[i - 3 + 1] != -1 && 161 | normal_indexes[i - 3 + 2] != -1) { 162 | for (size_t j = 0; j < 3; j++) { 163 | tr->normal[j] = normals[normal_indexes[i - 3 + j]]; 164 | } 165 | } 166 | 167 | // Add texture coordinates (if any). 168 | if (texcoord_indexes[i - 3 + 0] != -1 && 169 | texcoord_indexes[i - 3 + 1] != -1 && 170 | texcoord_indexes[i - 3 + 2] != -1) { 171 | for (size_t j = 0; j < 3; j++) { 172 | tr->uvw[j] = texcoords[texcoord_indexes[i - 3 + j]]; 173 | } 174 | } 175 | 176 | // Add material (if any). 177 | tr->mtl = selected_material; 178 | 179 | // Add debug information. 180 | tr->debug_line_no = line_no; 181 | 182 | // XXX 183 | tr->CacheAABB(); 184 | 185 | // Scene takes ownership of the Triangle object. 186 | scene->tree.AddPrimitive(tr); 187 | } 188 | 189 | return true; 190 | } 191 | 192 | static std::string GetDirectoryPart(const std::string& path) { 193 | size_t found = path.find_last_of("/\\"); 194 | if (found == std::string::npos) { 195 | return ""; 196 | } 197 | 198 | return path.substr(0, found); 199 | } 200 | 201 | bool ObjFileReader::ReadObjFile(Scene *scene, const char *fname) { 202 | // Reset the temporary containers, just in case. 203 | vertices.resize(0); 204 | texcoords.resize(0); 205 | normals.resize(0); 206 | 207 | this->scene = scene; 208 | base_directory = GetDirectoryPart(fname); 209 | selected_material = nullptr; 210 | line_no = 0; 211 | 212 | // Parse the OBJ file. 213 | std::unique_ptr f(fopen(fname, "r")); 214 | if (f == nullptr) { 215 | fprintf(stderr, "error: file \"%s\" not found\n", fname); 216 | return false; 217 | } 218 | 219 | std::unordered_map< 220 | std::string, bool(ObjFileReader::*)(const char *)> handlers({ 221 | { "v", &ObjFileReader::ReadVertex }, 222 | { "vn", &ObjFileReader::ReadNormals }, 223 | { "vt", &ObjFileReader::ReadUVW }, 224 | { "v", &ObjFileReader::ReadVertex }, 225 | { "f", &ObjFileReader::ReadFace }, 226 | { "mtllib", &ObjFileReader::ReadMaterialLibrary }, 227 | { "usemtl", &ObjFileReader::ReadUseMaterial }, 228 | { "s", &ObjFileReader::ReadNotImplemented }, 229 | { "g", &ObjFileReader::ReadNotImplemented }, 230 | { "o", &ObjFileReader::ReadNotImplemented } 231 | }); 232 | 233 | for (;;line_no++) { 234 | char line[128]; 235 | if (fgets(line, sizeof(line), f.get()) == nullptr) { 236 | break; 237 | } 238 | 239 | char *cp = strrchr(line,'\r'); 240 | if(cp != nullptr){ 241 | *cp = '\0'; 242 | } 243 | 244 | cp = strrchr(line,'\n'); 245 | if(cp != nullptr){ 246 | *cp = '\0'; 247 | } 248 | 249 | char token[16]{}; 250 | if (sscanf(line, "%15s", token) != 1) { 251 | // Skip empty lines. 252 | continue; 253 | } 254 | 255 | std::string token_str(token); 256 | 257 | // Ignore comments. 258 | if (token_str[0] == '#') { 259 | continue; 260 | } 261 | 262 | auto handler_itr = handlers.find(token_str); 263 | if (handler_itr == handlers.end()) { 264 | fprintf(stderr, "warning: unknown OBJ feature \"%s\"\n", token); 265 | continue; 266 | } 267 | 268 | if (!(this->*handler_itr->second)(line)) { 269 | return false; 270 | } 271 | } 272 | 273 | return true; 274 | } 275 | 276 | 277 | void MtlFileReader::CommitMaterial() { 278 | if (mtl != nullptr) { 279 | // Commit the current material. 280 | scene->materials[mtl_name] = std::move(mtl); 281 | mtl_name.clear(); 282 | } 283 | } 284 | 285 | bool MtlFileReader::ReadNotImplemented(const char *) { 286 | return true; 287 | } 288 | 289 | bool MtlFileReader::ReadNewMaterial(const char *line) { 290 | CommitMaterial(); 291 | 292 | char name[128]; 293 | // Note: There is a small chancee that some MTL/OBJ files might have these 294 | // names with spaces - in such case this code should be changed. 295 | if (sscanf(line, "newmtl %127s", name) != 1) { 296 | fprintf(stderr, "warning: unsupported newmtl format\n"); 297 | return false; 298 | } 299 | 300 | mtl.reset(new Material); 301 | mtl_name = name; 302 | return true; 303 | } 304 | 305 | bool MtlFileReader::ReadAmbient(const char *line) { 306 | if (mtl == nullptr) { 307 | fprintf(stderr, "warning: material not ready; missing newmtl\n"); 308 | return false; 309 | } 310 | 311 | double r, g, b; 312 | if (sscanf(line, " Ka %lf %lf %lf", &r, &g, &b) != 3) { 313 | fprintf(stderr, "warning: unsupported Ka format \"%s\"\n", line); 314 | return false; 315 | } 316 | 317 | mtl->ambient = { r, g, b }; 318 | return true; 319 | } 320 | 321 | bool MtlFileReader::ReadDiffuse(const char *line) { 322 | if (mtl == nullptr) { 323 | fprintf(stderr, "warning: material not ready; missing newmtl\n"); 324 | return false; 325 | } 326 | 327 | double r, g, b; 328 | if (sscanf(line, " Kd %lf %lf %lf", &r, &g, &b) != 3) { 329 | fprintf(stderr, "warning: unsupported Kd format \"%s\"\n", line); 330 | return false; 331 | } 332 | 333 | mtl->diffuse = { r, g, b }; 334 | return true; 335 | } 336 | 337 | bool MtlFileReader::ReadSpecular(const char *line) { 338 | if (mtl == nullptr) { 339 | fprintf(stderr, "warning: material not ready; missing newmtl\n"); 340 | return false; 341 | } 342 | 343 | double r, g, b; 344 | if (sscanf(line, " Ks %lf %lf %lf", &r, &g, &b) != 3) { 345 | fprintf(stderr, "warning: unsupported Ks format \"%s\"\n", line); 346 | return false; 347 | } 348 | 349 | mtl->specular = { r, g, b }; 350 | return true; 351 | } 352 | 353 | bool MtlFileReader::ReadSpecularExp(const char *line) { 354 | if (mtl == nullptr) { 355 | fprintf(stderr, "warning: material not ready; missing newmtl\n"); 356 | return false; 357 | } 358 | 359 | double value; 360 | if (sscanf(line, " Ns %lf", &value) != 1) { 361 | fprintf(stderr, "warning: unsupported Ns format \"%s\"\n", line); 362 | return false; 363 | } 364 | 365 | mtl->specular_exp = value; 366 | return true; 367 | } 368 | 369 | bool MtlFileReader::ReadReflectance(const char *line) { 370 | if (mtl == nullptr) { 371 | fprintf(stderr, "warning: material not ready; missing newmtl\n"); 372 | return false; 373 | } 374 | 375 | double value; 376 | if (sscanf(line, " Refl %lf", &value) != 1) { 377 | fprintf(stderr, "warning: unsupported Refl format \"%s\"\n", line); 378 | return false; 379 | } 380 | 381 | mtl->reflectance = value; 382 | return true; 383 | } 384 | 385 | bool MtlFileReader::ReadTransparancy(const char *line) { 386 | if (mtl == nullptr) { 387 | fprintf(stderr, "warning: material not ready; missing newmtl\n"); 388 | return false; 389 | } 390 | 391 | double value; 392 | if (sscanf(line, " Tr %lf", &value) != 1) { 393 | fprintf(stderr, "warning: unsupported Tr format \"%s\"\n", line); 394 | return false; 395 | } 396 | 397 | mtl->transparency = value; 398 | return true; 399 | } 400 | 401 | bool MtlFileReader::ReadRefractionIndex(const char *line) { 402 | if (mtl == nullptr) { 403 | fprintf(stderr, "warning: material not ready; missing newmtl\n"); 404 | return false; 405 | } 406 | 407 | double value; 408 | if (sscanf(line, " Ni %lf", &value) != 1) { 409 | fprintf(stderr, "warning: unsupported Ni format \"%s\"\n", line); 410 | return false; 411 | } 412 | 413 | mtl->refraction_index = value; 414 | return true; 415 | } 416 | 417 | bool MtlFileReader::ReadTransmissionFilter(const char *line) { 418 | if (mtl == nullptr) { 419 | fprintf(stderr, "warning: material not ready; missing newmtl\n"); 420 | return false; 421 | } 422 | 423 | double r, g, b; 424 | if (sscanf(line, " Tf %lf %lf %lf", &r, &g, &b) != 3) { 425 | fprintf(stderr, "warning: unsupported Tf format \"%s\"\n", line); 426 | return false; 427 | } 428 | 429 | mtl->transmission_filter = { r, g, b }; 430 | return true; 431 | } 432 | 433 | Texture *MtlFileReader::GetTexture(const char *fname) { 434 | // If the texture has already been loaded before, just fetch it. 435 | auto tex_itr = scene->textures.find(fname); 436 | if (tex_itr != scene->textures.end()) { 437 | return tex_itr->second.get(); 438 | } 439 | 440 | // Actually load the texture. 441 | std::string path = base_directory.empty() ? 442 | fname : 443 | base_directory + "/" + fname; 444 | 445 | Texture *tex = Texture::LoadFromFile(path.c_str()); 446 | if (tex == nullptr) { 447 | fprintf(stderr, "error: cannot load texture \"%s\"\n", fname); 448 | return nullptr; 449 | } 450 | 451 | scene->textures[fname].reset(tex); 452 | return tex; 453 | } 454 | 455 | bool MtlFileReader::ReadTexture(const char *line) { 456 | if (mtl == nullptr) { 457 | fprintf(stderr, "warning: material not ready; missing newmtl\n"); 458 | return false; 459 | } 460 | 461 | char fname[256]; 462 | if (sscanf(line, " map_Ka %255[^\n]", fname) != 1) { 463 | fprintf(stderr, "warning: unsupported map_ka format \"%s\"\n", line); 464 | return false; 465 | } 466 | 467 | mtl->tex = GetTexture(fname); 468 | 469 | return mtl->tex != nullptr; 470 | } 471 | 472 | bool MtlFileReader::ReadMtlFile(Scene *scene, const char *fname) { 473 | // Reset the temporary storage. 474 | mtl.reset(); 475 | mtl_name.clear(); 476 | 477 | this->scene = scene; 478 | base_directory = GetDirectoryPart(fname); 479 | 480 | // Parse the MTL file. 481 | std::unique_ptr f(fopen(fname, "r")); 482 | if (f == nullptr) { 483 | fprintf(stderr, "error: file \"%s\" not found\n", fname); 484 | return false; 485 | } 486 | 487 | std::unordered_map< 488 | std::string, bool(MtlFileReader::*)(const char *)> handlers({ 489 | { "newmtl", &MtlFileReader::ReadNewMaterial }, 490 | { "Ns", &MtlFileReader::ReadSpecularExp }, 491 | { "Ni", &MtlFileReader::ReadRefractionIndex }, 492 | { "d", &MtlFileReader::ReadNotImplemented }, 493 | { "Tr", &MtlFileReader::ReadTransparancy }, 494 | { "Refl", &MtlFileReader::ReadReflectance }, 495 | { "Tf", &MtlFileReader::ReadTransmissionFilter }, 496 | { "illum", &MtlFileReader::ReadNotImplemented }, 497 | { "Ka", &MtlFileReader::ReadAmbient }, 498 | { "Kd", &MtlFileReader::ReadDiffuse }, 499 | { "Ks", &MtlFileReader::ReadSpecular }, 500 | { "Ke", &MtlFileReader::ReadNotImplemented }, // XXX light 501 | { "map_Ka", &MtlFileReader::ReadTexture }, 502 | { "map_Kd", &MtlFileReader::ReadNotImplemented } 503 | }); 504 | 505 | while (true) { 506 | char line[128]; 507 | if (fgets(line, sizeof(line), f.get()) == nullptr) { 508 | break; 509 | } 510 | 511 | char *cp = strrchr(line,'\r'); 512 | if(cp != nullptr){ 513 | *cp = '\0'; 514 | } 515 | 516 | cp = strrchr(line,'\n'); 517 | if(cp != nullptr){ 518 | *cp = '\0'; 519 | } 520 | 521 | char token[16]{}; 522 | if (sscanf(line, "%15s", token) != 1) { 523 | // Skip empty lines. 524 | continue; 525 | } 526 | 527 | std::string token_str(token); 528 | 529 | // Ignore comments. 530 | if (token_str[0] == '#') { 531 | continue; 532 | } 533 | 534 | auto handler_itr = handlers.find(token_str); 535 | if (handler_itr == handlers.end()) { 536 | fprintf(stderr, "warning: unknown MTL feature \"%s\"\n", token); 537 | continue; 538 | } 539 | 540 | if (!(this->*handler_itr->second)(line)) { 541 | return false; 542 | } 543 | } 544 | 545 | // Commit the remaining material, if any. 546 | CommitMaterial(); 547 | 548 | return true; 549 | } 550 | 551 | }; // namespace raytracer 552 | 553 | 554 | --------------------------------------------------------------------------------