├── .gitignore ├── 3p ├── .gitignore ├── build │ └── .gitignore ├── clean.bash ├── make.bash ├── patches │ ├── glm-msgpack.patch │ ├── lpack.patch │ ├── luajit-tweaks.patch │ └── msgpack-auto_ptr-fix.patch ├── scripts │ ├── download.bash │ ├── glm-0.9.4.1.bash │ ├── ilmbase-1.0.3.bash │ ├── libuv-0.9.8.bash │ ├── lpack.bash │ ├── luajit-2.0.0.bash │ ├── main.bash │ ├── msgpack-0.5.7.bash │ └── openexr-1.7.1.bash ├── src │ └── .gitignore └── tmp │ └── .gitignore ├── LICENSE ├── README.md ├── bin └── .gitignore ├── config.lua ├── frlib ├── base │ ├── common.lua │ ├── geometric.lua │ ├── mat4.lua │ ├── sample.lua │ ├── transform.lua │ ├── trig.lua │ ├── types.lua │ ├── vec2.lua │ ├── vec3.lua │ └── vec4.lua ├── extras.lua ├── flexrender.lua ├── loaders │ ├── meshes.lua │ ├── shaders.lua │ └── textures.lua ├── primitives │ └── simple.lua └── shaders │ ├── lights.lua │ └── phong.lua ├── obj └── .gitignore ├── premake4.lua ├── scenes ├── assets │ ├── .gitignore │ └── download.bash ├── backdrop_shader.lua ├── bunnies.lua ├── cornell-models.lua ├── cornell.lua ├── field.lua ├── mirror_shader.lua ├── shiny_tile_shader.lua ├── test.lua ├── tile_shader.lua ├── toon_shader.lua ├── toystore.lua └── wood_shader.lua ├── scripts ├── logusage.bash ├── profile.bash └── report.bash └── src ├── baseline ├── baseline.cpp ├── engine.cpp └── engine.hpp ├── render ├── engine.cpp ├── engine.hpp └── render.cpp ├── shared ├── scripting.hpp ├── scripting │ ├── config_script.cpp │ ├── config_script.hpp │ ├── scene_script.cpp │ ├── scene_script.hpp │ ├── script.cpp │ ├── script.hpp │ ├── shader_script.cpp │ ├── shader_script.hpp │ ├── texture_script.cpp │ └── texture_script.hpp ├── types.hpp ├── types │ ├── bounding_box.cpp │ ├── bounding_box.hpp │ ├── buffer.cpp │ ├── buffer.hpp │ ├── buffer_op.hpp │ ├── bvh.cpp │ ├── bvh.hpp │ ├── camera.cpp │ ├── camera.hpp │ ├── config.cpp │ ├── config.hpp │ ├── fat_ray.cpp │ ├── fat_ray.hpp │ ├── hit_record.cpp │ ├── hit_record.hpp │ ├── image.cpp │ ├── image.hpp │ ├── light_list.cpp │ ├── light_list.hpp │ ├── linear_node.cpp │ ├── linear_node.hpp │ ├── linked_node.cpp │ ├── linked_node.hpp │ ├── local_geometry.cpp │ ├── local_geometry.hpp │ ├── material.cpp │ ├── material.hpp │ ├── mesh.cpp │ ├── mesh.hpp │ ├── message.cpp │ ├── message.hpp │ ├── net_node.cpp │ ├── net_node.hpp │ ├── primitive_info.cpp │ ├── primitive_info.hpp │ ├── ray_forward.hpp │ ├── render_stats.hpp │ ├── shader.cpp │ ├── shader.hpp │ ├── slim_ray.cpp │ ├── slim_ray.hpp │ ├── texture.cpp │ ├── texture.hpp │ ├── traversal_state.cpp │ ├── traversal_state.hpp │ ├── traversal_stats.hpp │ ├── triangle.cpp │ ├── triangle.hpp │ ├── vertex.cpp │ ├── vertex.hpp │ └── work_results.hpp ├── utils.hpp └── utils │ ├── cmdline.cpp │ ├── cmdline.hpp │ ├── library.cpp │ ├── library.hpp │ ├── network.hpp │ ├── printers.cpp │ ├── printers.hpp │ ├── spacecode.cpp │ ├── spacecode.hpp │ ├── tostring.hpp │ ├── tout.hpp │ └── uncopyable.hpp └── worker ├── engine.cpp ├── engine.hpp ├── ray_queue.cpp ├── ray_queue.hpp └── worker.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated build scripts. 2 | /Makefile 3 | /*.make 4 | 5 | # Profiling results. 6 | /profile.txt 7 | 8 | # Rendered images and stats. 9 | /*.exr 10 | /*.csv 11 | -------------------------------------------------------------------------------- /3p/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependency build logs. 2 | /build.log 3 | -------------------------------------------------------------------------------- /3p/build/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in here. 2 | * 3 | -------------------------------------------------------------------------------- /3p/clean.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Where are we in the filesystem? 4 | BASEPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | 6 | echo "======================================================================" 7 | echo " CLEAN STARTING @ "`date` 8 | echo "======================================================================" 9 | echo "" 10 | 11 | rm -rf $BASEPATH/build/* 12 | rm -rf $BASEPATH/tmp/* 13 | 14 | echo "======================================================================" 15 | echo " CLEAN COMPLETE @ "`date` 16 | echo "======================================================================" 17 | echo "" 18 | -------------------------------------------------------------------------------- /3p/make.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Where are we in the filesystem? 4 | BASEPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | 6 | # Where are we saving the build log? 7 | BUILDLOG=$BASEPATH/build.log 8 | 9 | # Create/clear the build log. 10 | echo "" > $BUILDLOG 11 | 12 | # Off we go! 13 | source $BASEPATH/scripts/main.bash | tee -a $BUILDLOG 14 | -------------------------------------------------------------------------------- /3p/patches/glm-msgpack.patch: -------------------------------------------------------------------------------- 1 | diff -Naur glm-0.9.4.1/glm/core/type_vec1.hpp glm-0.9.4.1-patch/glm/core/type_vec1.hpp 2 | --- glm-0.9.4.1/glm/core/type_vec1.hpp 2013-02-07 22:47:56.645812699 -0800 3 | +++ glm-0.9.4.1-patch/glm/core/type_vec1.hpp 2013-02-07 22:47:22.609425392 -0800 4 | @@ -35,6 +35,8 @@ 5 | #include "type_size.hpp" 6 | #include "_swizzle.hpp" 7 | 8 | +#include "msgpack.hpp" 9 | + 10 | namespace glm{ 11 | namespace detail 12 | { 13 | @@ -68,6 +70,8 @@ 14 | union {value_type x, r, s;}; 15 | # endif//GLM_COMPONENT 16 | 17 | + MSGPACK_DEFINE(x); 18 | + 19 | ////////////////////////////////////// 20 | // Accesses 21 | 22 | diff -Naur glm-0.9.4.1/glm/core/type_vec2.hpp glm-0.9.4.1-patch/glm/core/type_vec2.hpp 23 | --- glm-0.9.4.1/glm/core/type_vec2.hpp 2013-02-07 22:48:00.485781101 -0800 24 | +++ glm-0.9.4.1-patch/glm/core/type_vec2.hpp 2013-02-07 22:47:30.846024575 -0800 25 | @@ -35,6 +35,8 @@ 26 | #include "type_size.hpp" 27 | #include "_swizzle.hpp" 28 | 29 | +#include "msgpack.hpp" 30 | + 31 | namespace glm{ 32 | namespace detail 33 | { 34 | @@ -97,6 +99,8 @@ 35 | # endif//(defined(GLM_SWIZZLE)) 36 | # endif//GLM_COMPONENT 37 | 38 | + MSGPACK_DEFINE(x, y); 39 | + 40 | ////////////////////////////////////// 41 | // Accesses 42 | 43 | diff -Naur glm-0.9.4.1/glm/core/type_vec3.hpp glm-0.9.4.1-patch/glm/core/type_vec3.hpp 44 | --- glm-0.9.4.1/glm/core/type_vec3.hpp 2013-02-07 22:48:02.502431166 -0800 45 | +++ glm-0.9.4.1-patch/glm/core/type_vec3.hpp 2013-02-07 22:47:33.256004815 -0800 46 | @@ -35,6 +35,8 @@ 47 | #include "type_size.hpp" 48 | #include "_swizzle.hpp" 49 | 50 | +#include "msgpack.hpp" 51 | + 52 | namespace glm{ 53 | namespace detail 54 | { 55 | @@ -98,6 +100,8 @@ 56 | # endif//(defined(GLM_SWIZZLE)) 57 | # endif//GLM_COMPONENT 58 | 59 | + MSGPACK_DEFINE(x, y, z); 60 | + 61 | ////////////////////////////////////// 62 | // Accesses 63 | 64 | diff -Naur glm-0.9.4.1/glm/core/type_vec4.hpp glm-0.9.4.1-patch/glm/core/type_vec4.hpp 65 | --- glm-0.9.4.1/glm/core/type_vec4.hpp 2013-02-07 22:48:04.375749075 -0800 66 | +++ glm-0.9.4.1-patch/glm/core/type_vec4.hpp 2013-02-07 22:47:35.792650676 -0800 67 | @@ -35,6 +35,8 @@ 68 | #include "type_size.hpp" 69 | #include "_swizzle.hpp" 70 | 71 | +#include "msgpack.hpp" 72 | + 73 | namespace glm{ 74 | namespace detail 75 | { 76 | @@ -99,6 +101,8 @@ 77 | # endif//(defined(GLM_SWIZZLE)) 78 | # endif//GLM_COMPONENT 79 | 80 | + MSGPACK_DEFINE(x, y, z, w); 81 | + 82 | ////////////////////////////////////// 83 | // Accesses 84 | 85 | -------------------------------------------------------------------------------- /3p/patches/lpack.patch: -------------------------------------------------------------------------------- 1 | diff -Naur pack/Makefile pack-patch/Makefile 2 | --- pack/Makefile 2007-06-29 15:22:54.000000000 -0700 3 | +++ pack-patch/Makefile 2012-05-11 13:27:29.777076507 -0700 4 | @@ -1,13 +1,13 @@ 5 | # makefile for pack library for Lua 6 | 7 | # change these to reflect your Lua installation 8 | -LUA= /tmp/lhf/lua-5.1.2 9 | +LUA= $(BASEPATH)/tmp/LuaJIT-2.0.0 10 | LUAINC= $(LUA)/src 11 | LUALIB= $(LUA)/src 12 | LUABIN= $(LUA)/src 13 | 14 | # probably no need to change anything below here 15 | -CFLAGS= $(INCS) $(WARN) -O2 $G 16 | +CFLAGS= $(INCS) $(WARN) -fPIC -O2 $G 17 | WARN= -ansi -pedantic -Wall 18 | INCS= -I$(LUAINC) 19 | 20 | @@ -20,7 +20,7 @@ 21 | all: test 22 | 23 | test: $T 24 | - $(LUABIN)/lua $(TEST) 25 | + $(LUABIN)/luajit $(TEST) 26 | 27 | o: $(MYLIB).o 28 | 29 | -------------------------------------------------------------------------------- /3p/patches/luajit-tweaks.patch: -------------------------------------------------------------------------------- 1 | diff -Naur LuaJIT-2.0.0/src/Makefile LuaJIT-2.0.0-patch/src/Makefile 2 | --- LuaJIT-2.0.0/src/Makefile 2012-11-08 02:10:00.000000000 -0800 3 | +++ LuaJIT-2.0.0-patch/src/Makefile 2013-02-07 22:15:04.809910332 -0800 4 | @@ -49,7 +49,7 @@ 5 | # the binaries to a different machine you could also use: -march=native 6 | # 7 | CCOPT_x86= -march=i686 8 | -CCOPT_x64= 9 | +CCOPT_x64= -march=native 10 | CCOPT_arm= 11 | CCOPT_ppc= 12 | CCOPT_ppcspe= 13 | @@ -72,10 +72,10 @@ 14 | # as dynamic mode. 15 | # 16 | # Mixed mode creates a static + dynamic library and a statically linked luajit. 17 | -BUILDMODE= mixed 18 | +#BUILDMODE= mixed 19 | # 20 | # Static mode creates a static library and a statically linked luajit. 21 | -#BUILDMODE= static 22 | +BUILDMODE= static 23 | # 24 | # Dynamic mode creates a dynamic library and a dynamically linked luajit. 25 | # Note: this executable will only run when the library is installed! 26 | -------------------------------------------------------------------------------- /3p/patches/msgpack-auto_ptr-fix.patch: -------------------------------------------------------------------------------- 1 | diff -Naur msgpack-0.5.7/src/msgpack/unpack.hpp msgpack-0.5.7-patch/src/msgpack/unpack.hpp 2 | --- msgpack-0.5.7/src/msgpack/unpack.hpp 2011-04-23 07:19:15.000000000 -0700 3 | +++ msgpack-0.5.7-patch/src/msgpack/unpack.hpp 2011-11-21 23:43:39.073677014 -0800 4 | @@ -307,7 +307,10 @@ 5 | const char* data, size_t len, size_t* offset) 6 | { 7 | msgpack::object obj; 8 | +#pragma GCC diagnostic push 9 | +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" 10 | std::auto_ptr z(new zone()); 11 | +#pragma GCC diagnostic pop 12 | 13 | unpack_return ret = (unpack_return)msgpack_unpack( 14 | data, len, offset, z.get(), 15 | -------------------------------------------------------------------------------- /3p/scripts/download.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | function download { 4 | if [ ! -f $BASEPATH/src/$1.tar.gz ]; then 5 | echo "" 6 | echo "==> Downloading $1" 7 | wget -O $BASEPATH/src/$1.tar.gz http://flexrender.org/files/$1.tar.gz 8 | fi 9 | } 10 | 11 | function download_packages { 12 | echo "" 13 | echo "======================================================================" 14 | echo " DOWNLOADING PACKAGES @ "`date` 15 | echo "======================================================================" 16 | 17 | download "LuaJIT-2.0.0" 18 | download "lpack" 19 | download "libuv-0.9.8" 20 | download "glm-0.9.4.1" 21 | download "msgpack-0.5.7" 22 | download "ilmbase-1.0.3" 23 | download "openexr-1.7.1" 24 | } 25 | -------------------------------------------------------------------------------- /3p/scripts/glm-0.9.4.1.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | function build_glm { 4 | PACKAGENAME="glm-0.9.4.1" 5 | 6 | echo "" 7 | echo "======================================================================" 8 | echo " BUILDING $PACKAGENAME @ "`date` 9 | echo "======================================================================" 10 | 11 | echo "" 12 | echo "==> Unpacking $PACKAGENAME." 13 | tar xvf $BASEPATH/src/$PACKAGENAME.tar.gz --directory=$BASEPATH/tmp 14 | 15 | echo "" 16 | echo "==> Patching $PACKAGENAME." 17 | patch --directory=$BASEPATH/tmp/$PACKAGENAME -p1 < $BASEPATH/patches/glm-msgpack.patch 18 | 19 | echo "" 20 | echo "==> Copying $PACKAGENAME." 21 | cp --recursive $BASEPATH/tmp/$PACKAGENAME/glm $BASEPATH/build/include 22 | } 23 | -------------------------------------------------------------------------------- /3p/scripts/ilmbase-1.0.3.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | function build_ilmbase { 4 | PACKAGENAME="ilmbase-1.0.3" 5 | 6 | echo "" 7 | echo "======================================================================" 8 | echo " BUILDING $PACKAGENAME @ "`date` 9 | echo "======================================================================" 10 | 11 | echo "" 12 | echo "==> Unpacking $PACKAGENAME." 13 | tar xvf $BASEPATH/src/$PACKAGENAME.tar.gz --directory=$BASEPATH/tmp 14 | 15 | echo "" 16 | echo "==> Configuring $PACKAGENAME." 17 | pushd $BASEPATH/tmp/$PACKAGENAME 18 | ./configure --prefix=$BASEPATH/build --enable-static=yes --enable-shared=no 19 | popd 20 | 21 | echo "" 22 | echo "==> Making $PACKAGENAME." 23 | make --directory=$BASEPATH/tmp/$PACKAGENAME --jobs=4 install 24 | } 25 | -------------------------------------------------------------------------------- /3p/scripts/libuv-0.9.8.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | function build_libuv { 4 | PACKAGENAME="libuv-0.9.8" 5 | 6 | echo "" 7 | echo "======================================================================" 8 | echo " BUILDING $PACKAGENAME @ "`date` 9 | echo "======================================================================" 10 | 11 | echo "" 12 | echo "==> Unpacking $PACKAGENAME." 13 | tar xvf $BASEPATH/src/$PACKAGENAME.tar.gz --directory=$BASEPATH/tmp 14 | 15 | echo "" 16 | echo "==> Making $PACKAGENAME." 17 | make --directory=$BASEPATH/tmp/$PACKAGENAME --jobs=4 18 | cp $BASEPATH/tmp/$PACKAGENAME/libuv.a $BASEPATH/build/lib/libuv.a 19 | cp -R $BASEPATH/tmp/$PACKAGENAME/include/* $BASEPATH/build/include 20 | } 21 | -------------------------------------------------------------------------------- /3p/scripts/lpack.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | function build_lpack { 4 | PACKAGENAME="lpack" 5 | 6 | echo "" 7 | echo "======================================================================" 8 | echo " BUILDING $PACKAGENAME @ "`date` 9 | echo "======================================================================" 10 | 11 | echo "" 12 | echo "==> Unpacking $PACKAGENAME." 13 | tar xvf $BASEPATH/src/$PACKAGENAME.tar.gz --directory=$BASEPATH/tmp 14 | 15 | echo "" 16 | echo "==> Patching $PACKAGENAME." 17 | patch --directory=$BASEPATH/tmp/pack -p1 < $BASEPATH/patches/lpack.patch 18 | 19 | echo "" 20 | echo "==> Making $PACKAGENAME." 21 | make --directory=$BASEPATH/tmp/pack BASEPATH=$BASEPATH 22 | cp $BASEPATH/tmp/pack/pack.so $BASEPATH/build/lib 23 | } 24 | -------------------------------------------------------------------------------- /3p/scripts/luajit-2.0.0.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | function build_luajit { 4 | PACKAGENAME="LuaJIT-2.0.0" 5 | 6 | echo "" 7 | echo "======================================================================" 8 | echo " BUILDING $PACKAGENAME @ "`date` 9 | echo "======================================================================" 10 | 11 | echo "" 12 | echo "==> Unpacking $PACKAGENAME." 13 | tar xvf $BASEPATH/src/$PACKAGENAME.tar.gz --directory=$BASEPATH/tmp 14 | 15 | echo "" 16 | echo "==> Patching $PACKAGENAME." 17 | patch --directory=$BASEPATH/tmp/$PACKAGENAME -p1 < $BASEPATH/patches/luajit-tweaks.patch 18 | 19 | echo "" 20 | echo "==> Making $PACKAGENAME." 21 | make --directory=$BASEPATH/tmp/$PACKAGENAME --jobs=4 install PREFIX=$BASEPATH/build 22 | } 23 | -------------------------------------------------------------------------------- /3p/scripts/main.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | echo "======================================================================" 4 | echo " BUILD STARTING @ "`date` 5 | echo "======================================================================" 6 | 7 | source $BASEPATH/scripts/download.bash 8 | download_packages 9 | 10 | source $BASEPATH/scripts/luajit-2.0.0.bash 11 | build_luajit 12 | 13 | source $BASEPATH/scripts/lpack.bash 14 | build_lpack 15 | 16 | source $BASEPATH/scripts/libuv-0.9.8.bash 17 | build_libuv 18 | 19 | source $BASEPATH/scripts/glm-0.9.4.1.bash 20 | build_glm 21 | 22 | source $BASEPATH/scripts/msgpack-0.5.7.bash 23 | build_msgpack 24 | 25 | source $BASEPATH/scripts/ilmbase-1.0.3.bash 26 | build_ilmbase 27 | 28 | source $BASEPATH/scripts/openexr-1.7.1.bash 29 | build_openexr 30 | 31 | echo "" 32 | echo "======================================================================" 33 | echo " BUILD COMPLETE @ "`date` 34 | echo "======================================================================" 35 | -------------------------------------------------------------------------------- /3p/scripts/msgpack-0.5.7.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | function build_msgpack { 4 | PACKAGENAME="msgpack-0.5.7" 5 | 6 | echo "" 7 | echo "======================================================================" 8 | echo " BUILDING $PACKAGENAME @ "`date` 9 | echo "======================================================================" 10 | 11 | echo "" 12 | echo "==> Unpacking $PACKAGENAME." 13 | tar xvf $BASEPATH/src/$PACKAGENAME.tar.gz --directory=$BASEPATH/tmp 14 | 15 | echo "" 16 | echo "==> Patching $PACKAGENAME." 17 | patch --directory=$BASEPATH/tmp/$PACKAGENAME -p1 < $BASEPATH/patches/msgpack-auto_ptr-fix.patch 18 | 19 | echo "" 20 | echo "==> Configuring $PACKAGENAME." 21 | pushd $BASEPATH/tmp/$PACKAGENAME 22 | ./configure --prefix=$BASEPATH/build --enable-static=yes --enable-shared=no 23 | popd 24 | 25 | echo "" 26 | echo "==> Making $PACKAGENAME." 27 | make --directory=$BASEPATH/tmp/$PACKAGENAME --jobs=4 install 28 | } 29 | -------------------------------------------------------------------------------- /3p/scripts/openexr-1.7.1.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | function build_openexr { 4 | PACKAGENAME="openexr-1.7.1" 5 | 6 | echo "" 7 | echo "======================================================================" 8 | echo " BUILDING $PACKAGENAME @ "`date` 9 | echo "======================================================================" 10 | 11 | echo "" 12 | echo "==> Unpacking $PACKAGENAME." 13 | tar xvf $BASEPATH/src/$PACKAGENAME.tar.gz --directory=$BASEPATH/tmp 14 | 15 | echo "" 16 | echo "==> Configuring $PACKAGENAME." 17 | pushd $BASEPATH/tmp/$PACKAGENAME 18 | PKG_CONFIG_PATH=$BASEPATH/build/lib/pkgconfig ./configure --prefix=$BASEPATH/build --enable-static=yes --enable-shared=no --disable-ilmbasetest 19 | popd 20 | 21 | echo "" 22 | echo "==> Making $PACKAGENAME." 23 | make --directory=$BASEPATH/tmp/$PACKAGENAME --jobs=4 install 24 | } 25 | -------------------------------------------------------------------------------- /3p/src/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in here. 2 | * 3 | -------------------------------------------------------------------------------- /3p/tmp/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in here. 2 | * 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy of 2 | this software and associated documentation files (the "Software"), to deal in 3 | the Software without restriction, including without limitation the rights to 4 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 5 | of the Software, and to permit persons to whom the Software is furnished to do 6 | so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlexRender 2 | 3 | This is the source code for the renderer implementation described in the paper 4 | *FlexRender: A Distributed Rendering Architecture for Ray Tracing Huge Scenes 5 | on Commodity Hardware*, presented at GRAPP 2013. FlexRender distributes 6 | geometry among a cluster of commodity machines to keep the scene in-core within 7 | the cluster, and passes rays around as messages using a feed-forward messaging 8 | architecture that never requires replies. This keeps communication and 9 | rendering completely asychronous. 10 | 11 | For more information about FlexRender, including the paper and slides from the 12 | presentation, see [flexrender.org](http://www.flexrender.org). 13 | 14 | ## Build Instructions 15 | 16 | We rely on several third-party libraries, but anything that you won't find on a 17 | standard Linux distro is downloaded, built, and statically linked by our 18 | dependency build script. To build FlexRender itself, you'll need a C++ compiler 19 | with reasonable C++11 support (GCC 4.7+ is known to work) and 20 | [Premake](http://industriousone.com/premake), a build script generator. 21 | 22 | First, check out the source code and run the dependency build script. This only 23 | needs to be done once. (No packages are installed, everything is built and 24 | linked within the `3p/` directory so no root privileges are required.) 25 | 26 | git clone https://github.com/bobsomers/flexrender.git 27 | cd flexrender/3p 28 | ./make.bash 29 | 30 | Next, generate GNU Makefiles for building FlexRender by running Premake. Then, 31 | run make (optionally with `config=release` for a release build). 32 | 33 | cd .. 34 | premake4 gmake 35 | make config=release 36 | 37 | This should leave you with 3 binaries in the `bin/` directory. The `flexrender` 38 | and `flexworker` executables are the renderer and worker respectively. The 39 | `baseline` is the image plane decomposition. 40 | 41 | ## Directory Layout 42 | 43 | * `3p/` All third-party libraries and build scripts. 44 | * `frlib/` Lua libraries for scene files and FlexRender shaders. 45 | * `scenes/` Some example scenes and shaders. 46 | * `scripts/` Handy scripts for profiling. 47 | * `src/[baseline|render|worker]` Code specific to the baseline, renderer, and worker executables. 48 | * `src/shared` Shared code for the libfr static library. 49 | * `config.lua` Example renderer configuration. 50 | 51 | ## Example Run 52 | 53 | In two shells, we run two flexworkers. One on the default port (19400), the 54 | other on port 19401. 55 | 56 | bin/flexworker 57 | bin/flexworker -p 19401 58 | 59 | In a third shell, we run the renderer, giving it the path to its renderer 60 | configuration (config.lua) and the scene file. 61 | 62 | bin/flexrender config.lua scenes/cornell.lua 63 | 64 | When finished, this should write out cornell.exr. It will also write out the 65 | image buffers of each worker, as well as a CSV file for each worker with 66 | rendering statistics. 67 | 68 | ## License 69 | 70 | The FlexRender source code is licensed under the MIT license. We encourage you 71 | to use the code, ideas, and techniques to enhance your own research. 72 | 73 | If anything from FlexRender does aid your research, please cite the paper 74 | appropriately. 75 | -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in here. 2 | * 3 | -------------------------------------------------------------------------------- /config.lua: -------------------------------------------------------------------------------- 1 | -- Import frlib. 2 | package.path = "frlib/?.lua;" .. package.path 3 | local fr = require "flexrender" 4 | 5 | -- Handy aliases. 6 | local vec2 = fr.vec2 7 | local vec3 = fr.vec3 8 | 9 | network { 10 | workers = { 11 | "127.0.0.1:19400", 12 | "127.0.0.1:19401" 13 | }, 14 | runaway = 2 -- percent 15 | } 16 | 17 | output { 18 | size = vec2(1024, 768), 19 | name = "cornell", 20 | buffers = { 21 | -- no auxilliary image buffers 22 | } 23 | } 24 | 25 | render { 26 | antialiasing = 1, 27 | samples = 10, 28 | bounces = 3, 29 | threshold = 0.0001, 30 | min = vec3(-10, -10, -10), 31 | max = vec3(10, 10, 10), 32 | } 33 | -------------------------------------------------------------------------------- /frlib/base/common.lua: -------------------------------------------------------------------------------- 1 | -- Import relevant base types. 2 | local types = require "base.types" 3 | local common = types.common 4 | 5 | -- Forwarding constructor based on type name. 6 | function common.__call(parent, ...) 7 | local obj = parent.new(...) 8 | setmetatable(obj, parent) 9 | return obj 10 | end 11 | 12 | -- Getter lookup. 13 | function common.__index(table, key) 14 | local mt = getmetatable(table) 15 | local func = mt.getters[key] 16 | if func == nil then 17 | error("no member named '" .. key .. "'", 2) 18 | end 19 | return func(table) 20 | end 21 | 22 | -- Setter lookup. 23 | function common.__newindex(table, key, value) 24 | local mt = getmetatable(table) 25 | local func = mt.setters[key] 26 | if func == nil then 27 | error("no member named '" .. key .. "'", 2) 28 | end 29 | func(table, value) 30 | end 31 | 32 | -- Module exports. 33 | return nil 34 | -------------------------------------------------------------------------------- /frlib/base/geometric.lua: -------------------------------------------------------------------------------- 1 | -- Import relevant base types. 2 | local types = require "base.types" 3 | require "base.vec2" 4 | require "base.vec3" 5 | require "base.vec4" 6 | 7 | -- Module aliases. 8 | local vec2 = types.vec2 9 | local vec3 = types.vec3 10 | local vec4 = types.vec4 11 | 12 | -- Computes the scalar length of a vector. 13 | local function length(v) 14 | local mt = getmetatable(v) 15 | 16 | if mt == vec2 then 17 | return math.sqrt(v[1] * v[1] + v[2] * v[2]) 18 | elseif mt == vec3 then 19 | return math.sqrt(v[1] * v[1] + v[2] * v[2] + v[3] * v[3]) 20 | elseif mt == vec4 then 21 | return math.sqrt(v[1] * v[1] + v[2] * v[2] + v[3] * v[3] + v[4] * v[4]) 22 | end 23 | 24 | error("length is only defined for vec2/vec3/vec4", 2) 25 | end 26 | 27 | -- Computes the distance between two points, represented as vectors. 28 | local function distance(p1, p2) 29 | local mt = getmetatable(p1) 30 | 31 | if not(mt == vec2 or mt == vec3 or mt == vec4) then 32 | error("distance is only defined for vec2/vec3/vec4", 2) 33 | end 34 | if mt ~= getmetatable(p2) then 35 | error("arguments to distance must be of the same type", 2) 36 | end 37 | 38 | return length(p1 - p2) 39 | end 40 | 41 | -- Computes the dot product of two vectors. 42 | local function dot(a, b) 43 | local mt = getmetatable(a) 44 | 45 | if not(mt == vec2 or mt == vec3 or mt == vec4) then 46 | error("dot is only defined for vec2/vec3/vec4", 2) 47 | end 48 | if mt ~= getmetatable(b) then 49 | error("arguments to dot must be of the same type", 2) 50 | end 51 | 52 | if mt == vec2 then 53 | return a[1] * b[1] + a[2] * b[2] 54 | elseif mt == vec3 then 55 | return a[1] * b[1] + a[2] * b[2] + a[3] * b[3] 56 | elseif mt == vec4 then 57 | return a[1] * b[1] + a[2] * b[2] + a[3] * b[3] + a[4] * b[4] 58 | end 59 | end 60 | 61 | -- Computes the cross product of two 3D vectors. 62 | local function cross(a, b) 63 | if getmetatable(a) ~= vec3 or getmetatable(b) ~= vec3 then 64 | error("cross is only defined for vec3", 2) 65 | end 66 | 67 | return vec3.frnew(a[2] * b[3] - b[2] * a[3], 68 | a[3] * b[1] - b[3] * a[1], 69 | a[1] * b[2] - b[1] * a[2]) 70 | end 71 | 72 | -- Normalizes a vector to unit length. 73 | local function normalize(v) 74 | local mt = getmetatable(v) 75 | 76 | if not(mt == vec2 or mt == vec3 or mt == vec4) then 77 | error("normalize is only defined for vec2/vec3/vec4", 2) 78 | end 79 | 80 | return v / length(v) 81 | end 82 | 83 | -- Computes the reflection vector based on the incident vector i and surface 84 | -- normal n. 85 | local function reflect(i, n) 86 | n = normalize(n) 87 | return i - 2 * dot(n, i) * n 88 | end 89 | 90 | -- Computes the refraction vector based on the incident vector i, surface 91 | -- normal n, and ratio of indices of refraction eta. Returns false (instead of 92 | -- a vector) if total internal reflection occurs. 93 | local function refract(i, n, eta) 94 | i = normalize(i) 95 | n = normalize(n) 96 | local ndoti = dot(n, i) 97 | local k = 1 - eta * eta * (1 - ndoti * ndoti) 98 | if k < 0 then 99 | return false 100 | end 101 | 102 | return eta * i - (eta * ndoti + math.sqrt(k)) * n 103 | end 104 | 105 | -- Module exports. 106 | return { 107 | length = length, 108 | distance = distance, 109 | dot = dot, 110 | cross = cross, 111 | normalize = normalize, 112 | reflect = reflect, 113 | refract = refract 114 | } 115 | -------------------------------------------------------------------------------- /frlib/base/sample.lua: -------------------------------------------------------------------------------- 1 | -- Import relevant base types. 2 | local types = require "base.types" 3 | require "base.vec2" 4 | require "base.vec3" 5 | require "base.vec4" 6 | require "base.mat4" 7 | local geometric = require "base.geometric" 8 | 9 | -- Module aliases. 10 | local vec2 = types.vec2 11 | local vec3 = types.vec3 12 | local vec4 = types.vec4 13 | local mat4 = types.mat4 14 | local dot = geometric.dot 15 | local cross = geometric.cross 16 | local normalize = geometric.normalize 17 | 18 | -- Samples the hemisphere above the surface normal n with a cosine-weighted 19 | -- sampling and returns a random direction (unit) vector within that hemisphere. 20 | local function hemisample(n) 21 | -- First, sample a unit disc by picking an x value and restricting the 22 | -- y value to be on the disc. 23 | local x = 2 * math.random() - 1 24 | local limit = math.sqrt(1 - x * x) 25 | local y = (2 * math.random() - 1) * limit 26 | 27 | -- Find the point above the disc to sample the hemisphere with a cosine 28 | -- weight (Malley's method). 29 | local z = math.sqrt(math.max(0, 1 - x * x - y * y)) 30 | 31 | -- Build an orthonormal basis from the surface normal. 32 | local w = n 33 | 34 | -- First try using the x-axis to build a basis. 35 | local t = vec3(1, 0, 0) 36 | if math.abs(dot(t, w)) > 0.9 then 37 | -- Too close to being colinear with the x-axis, use the y-axis instead. 38 | t = vec3(0, 1, 0) 39 | end 40 | local u = normalize(cross(t, w)) 41 | 42 | -- Complete the basis! 43 | local v = normalize(cross(w, u)) 44 | 45 | -- Build the transformation matrix. 46 | local m = mat4(vec4(u, 0), 47 | vec4(v, 0), 48 | vec4(w, 0), 49 | vec4(0, 0, 0, 1)) 50 | 51 | -- Transform our direction vector into the basis. 52 | return normalize(vec3(m * vec4(x, y, z, 0))) 53 | end 54 | 55 | -- Module exports. 56 | return { 57 | hemisample = hemisample 58 | } 59 | -------------------------------------------------------------------------------- /frlib/base/transform.lua: -------------------------------------------------------------------------------- 1 | -- Import relevant base types. 2 | local types = require "base.types" 3 | require "base.vec3" 4 | require "base.vec4" 5 | require "base.mat4" 6 | 7 | -- Import utility functions. 8 | local geometric = require "base.geometric" 9 | 10 | -- Module aliases. 11 | local vec3 = types.vec3 12 | local vec4 = types.vec4 13 | local mat4 = types.mat4 14 | local normalize = geometric.normalize 15 | 16 | -- Creates an affine scaling matrix. If v is a vec3, it is a non-uniform scale. 17 | -- If v is a number, it is a uniform scale. 18 | local function scale(v) 19 | v = vec3.frcoerce(v) 20 | return mat4.frnew(vec4.frnew(v[1], 0, 0, 0), 21 | vec4.frnew(0, v[2], 0, 0), 22 | vec4.frnew(0, 0, v[3], 0), 23 | vec4.frnew(0, 0, 0, 1)) 24 | end 25 | 26 | -- Creates an affine rotation matrix. Expects v to be a vector indicating the 27 | -- axis of rotation. Expects a to be a number indicating the angle of rotation 28 | -- around that axis, in radians. 29 | local function rotate(a, v) 30 | if type(a) ~= "number" then 31 | error("rotate expects the angle to be a number (in radians)", 2) 32 | end 33 | v = vec3.frcoerce(v) 34 | 35 | local c = math.cos(a) 36 | local s = math.sin(a) 37 | local axis = normalize(v) 38 | 39 | -- Credit: GLM, in matrix_transform.inl 40 | local d = vec4.frnew(c + (1 - c) * axis[1] * axis[1], 41 | (1 - c) * axis[1] * axis[2] + s * axis[3], 42 | (1 - c) * axis[1] * axis[3] - s * axis[2], 43 | 0) 44 | local e = vec4.frnew((1 - c) * axis[2] * axis[1] - s * axis[3], 45 | c + (1 - c) * axis[2] * axis[2], 46 | (1 - c) * axis[2] * axis[3] + s * axis[1], 47 | 0) 48 | local f = vec4.frnew((1 - c) * axis[3] * axis[1] + s * axis[2], 49 | (1 - c) * axis[3] * axis[2] - s * axis[1], 50 | c + (1 - c) * axis[3] * axis[3], 51 | 0) 52 | 53 | return mat4.frnew(d, e, f, vec4.frnew(0, 0, 0, 1)) 54 | end 55 | 56 | -- Creates an affine translation matrix. If v is a vec3, it is a non-uniform 57 | -- translation. If v is a number, it is a uniform translation. 58 | local function translate(v) 59 | v = vec3.frcoerce(v) 60 | 61 | return mat4.frnew(vec4.frnew(1, 0, 0, 0), 62 | vec4.frnew(0, 1, 0, 0), 63 | vec4.frnew(0, 0, 1, 0), 64 | vec4(v, 1)) 65 | end 66 | 67 | -- Module exports. 68 | return { 69 | scale = scale, 70 | rotate = rotate, 71 | translate = translate 72 | } 73 | -------------------------------------------------------------------------------- /frlib/base/trig.lua: -------------------------------------------------------------------------------- 1 | -- Convert degrees to radians. 2 | local function radians(d) 3 | return math.pi / 180 * d 4 | end 5 | 6 | -- Convert radians to degrees. 7 | local function degrees(r) 8 | return 180 / math.pi * r 9 | end 10 | 11 | -- Module exports. 12 | return { 13 | radians = radians, 14 | degrees = degrees 15 | } 16 | -------------------------------------------------------------------------------- /frlib/base/types.lua: -------------------------------------------------------------------------------- 1 | -- Define all base types. 2 | local common = {} 3 | local vec2 = {} 4 | local vec3 = {} 5 | local vec4 = {} 6 | local mat4 = {} 7 | 8 | -- Module exports. 9 | return { 10 | common = common, 11 | vec2 = vec2, 12 | vec3 = vec3, 13 | vec4 = vec4, 14 | mat4 = mat4 15 | } 16 | -------------------------------------------------------------------------------- /frlib/base/vec2.lua: -------------------------------------------------------------------------------- 1 | -- Import relevant base types. 2 | local types = require "base.types" 3 | require "base.common" 4 | 5 | -- Module aliases. 6 | local vec2 = types.vec2 7 | local vec3 = types.vec3 8 | local vec4 = types.vec4 9 | 10 | -- Forward index getter to common type. 11 | vec2.__index = types.common.__index 12 | 13 | -- Forward index setter to common type. 14 | vec2.__newindex = types.common.__newindex 15 | 16 | -- Getters. 17 | vec2.getters = { 18 | x = function(v) return v[1] end, 19 | y = function(v) return v[2] end, 20 | u = function(v) return v[1] end, 21 | v = function(v) return v[2] end 22 | } 23 | 24 | -- Setters. 25 | vec2.setters = { 26 | x = function(v, val) v[1] = val end, 27 | y = function(v, val) v[2] = val end, 28 | u = function(v, val) v[1] = val end, 29 | v = function(v, val) v[2] = val end 30 | } 31 | 32 | -- Constructor that provides the following forms: 33 | -- vec2(x) -> (x is a number) 34 | -- vec2(v) -> (v is a vec2/vec3/vec4) 35 | -- vec2(x, y) -> (x, y are numbers) 36 | function vec2.new(...) 37 | local v = nil 38 | 39 | local nargs = select("#", ...) 40 | if nargs == 1 then 41 | local arg = select(1, ...) 42 | local argmt = getmetatable(arg) 43 | if type(arg) == "number" then 44 | v = {arg, arg} 45 | elseif argmt == vec2 or argmt == vec3 or argmt == vec4 then 46 | v = {arg[1], arg[2]} 47 | else 48 | error("invalid construction of vec2", 3) 49 | end 50 | elseif nargs == 2 then 51 | local args = {select(1, ...), select(2, ...)} 52 | if type(args[1]) ~= "number" or type(args[2]) ~= "number" then 53 | error("invalid construction of vec2", 3) 54 | end 55 | v = {args[1], args[2]} 56 | else 57 | error("invalid construction of vec2", 3) 58 | end 59 | 60 | setmetatable(v, vec2) 61 | return v 62 | end 63 | 64 | -- Fast constructor for internal use only. 65 | function vec2.frnew(x, y) 66 | local v = {x, y} 67 | setmetatable(v, vec2) 68 | return v 69 | end 70 | 71 | -- Attempts to coerce val to a vec2 by construction. 72 | function vec2.frcoerce(val) 73 | if getmetatable(val) == vec2 then 74 | return val 75 | end 76 | 77 | local result, v = pcall(vec2.new, val) 78 | if result then 79 | return v 80 | end 81 | 82 | error("no known conversion to vec2", 3) 83 | end 84 | 85 | -- Converts the vector to a string representation. 86 | function vec2.__tostring(v) 87 | return table.concat {"vec2<", v[1], ", ", v[2], ">"} 88 | end 89 | 90 | -- Adds two vectors component-wise, coercing the arguments as necessary. 91 | -- Always produces a vec2. 92 | function vec2.__add(a, b) 93 | a = vec2.frcoerce(a) 94 | b = vec2.frcoerce(b) 95 | return vec2.frnew(a[1] + b[1], a[2] + b[2]) 96 | end 97 | 98 | -- Subtracts two vectors component-wise, coercing the arguments as necessary. 99 | -- Always produces a vec2. 100 | function vec2.__sub(a, b) 101 | a = vec2.frcoerce(a) 102 | b = vec2.frcoerce(b) 103 | return vec2.frnew(a[1] - b[1], a[2] - b[2]) 104 | end 105 | 106 | -- Multiplies two vectors component-wise, coercing the arguments as necessary. 107 | -- Always produces a vec2. 108 | function vec2.__mul(a, b) 109 | a = vec2.frcoerce(a) 110 | b = vec2.frcoerce(b) 111 | return vec2.frnew(a[1] * b[1], a[2] * b[2]) 112 | end 113 | 114 | -- Divides two vectors component-wise, coercing the arguments as necessary. 115 | -- Always produces a vec2. 116 | function vec2.__div(a, b) 117 | a = vec2.frcoerce(a) 118 | b = vec2.frcoerce(b) 119 | return vec2.frnew(a[1] / b[1], a[2] / b[2]) 120 | end 121 | 122 | -- Negates a vector component-wise. 123 | -- Always produces a vec2. 124 | function vec2.__unm(v) 125 | return vec2.frnew(-v[1], -v[2]) 126 | end 127 | 128 | -- Module exports. 129 | return nil 130 | -------------------------------------------------------------------------------- /frlib/base/vec3.lua: -------------------------------------------------------------------------------- 1 | -- Import relevant base types. 2 | local types = require "base.types" 3 | require "base.common" 4 | 5 | -- Module aliases. 6 | local vec2 = types.vec2 7 | local vec3 = types.vec3 8 | local vec4 = types.vec4 9 | 10 | -- Forward index getter to common type. 11 | vec3.__index = types.common.__index 12 | 13 | -- Forward index setter to common type. 14 | vec3.__newindex = types.common.__newindex 15 | 16 | -- Getters. 17 | vec3.getters = { 18 | x = function(v) return v[1] end, 19 | y = function(v) return v[2] end, 20 | z = function(v) return v[3] end, 21 | r = function(v) return v[1] end, 22 | g = function(v) return v[2] end, 23 | b = function(v) return v[3] end 24 | } 25 | 26 | -- Setters. 27 | vec3.setters = { 28 | x = function(v, val) v[1] = val end, 29 | y = function(v, val) v[2] = val end, 30 | z = function(v, val) v[3] = val end, 31 | r = function(v, val) v[1] = val end, 32 | g = function(v, val) v[2] = val end, 33 | b = function(v, val) v[3] = val end 34 | } 35 | 36 | -- Constructor that provides the following forms: 37 | -- vec3(x) -> (x is a number) 38 | -- vec3(v) -> (v is a vec3/vec4) 39 | -- vec3(v, z) -> (v is a vec2, z is a number) 40 | -- vec3(x, y, z) -> (x, y, z are numbers) 41 | function vec3.new(...) 42 | local v = nil 43 | 44 | local nargs = select("#", ...) 45 | if nargs == 1 then 46 | local arg = select(1, ...) 47 | local argmt = getmetatable(arg) 48 | if type(arg) == "number" then 49 | v = {arg, arg, arg} 50 | elseif argmt == vec3 or argmt == vec4 then 51 | v = {arg[1], arg[2], arg[3]} 52 | else 53 | error("invalid construction of vec3", 3) 54 | end 55 | elseif nargs == 2 then 56 | local args = {select(1, ...), select(2, ...)} 57 | local arg1mt = getmetatable(args[1]) 58 | if arg1mt ~= vec2 or type(args[2]) ~= "number" then 59 | error("invalid construction of vec3", 3) 60 | end 61 | local v2 = args[1] 62 | v = {v2[1], v2[2], args[2]} 63 | elseif nargs == 3 then 64 | local args = {select(1, ...), select(2, ...), select(3, ...)} 65 | if type(args[1]) ~= "number" or type(args[2]) ~= "number" or type(args[3]) ~= "number" then 66 | error("invalid construction of vec3", 3) 67 | end 68 | v = {args[1], args[2], args[3]} 69 | else 70 | error("invalid construction of vec3", 3) 71 | end 72 | 73 | setmetatable(v, vec3) 74 | return v 75 | end 76 | 77 | -- Fast constructor for internal use only. 78 | function vec3.frnew(x, y, z) 79 | local v = {x, y, z} 80 | setmetatable(v, vec3) 81 | return v 82 | end 83 | 84 | -- Attempts to coerce val to a vec3 by construction. 85 | function vec3.frcoerce(val) 86 | if getmetatable(val) == vec3 then 87 | return val 88 | end 89 | 90 | local result, v = pcall(vec3.new, val) 91 | if result then 92 | return v 93 | end 94 | 95 | error("no known conversion to vec3", 3) 96 | end 97 | 98 | -- Converts the vector to a string representation. 99 | function vec3.__tostring(v) 100 | return table.concat {"vec3<", v[1], ", ", v[2], ", ", v[3], ">"} 101 | end 102 | 103 | -- Adds two vectors component-wise, coercing the arguments as necessary. 104 | -- Always produces a vec3. 105 | function vec3.__add(a, b) 106 | a = vec3.frcoerce(a) 107 | b = vec3.frcoerce(b) 108 | return vec3.frnew(a[1] + b[1], a[2] + b[2], a[3] + b[3]) 109 | end 110 | 111 | -- Subtracts two vectors component-wise, coercing the arguments as necessary. 112 | -- Always produces a vec3. 113 | function vec3.__sub(a, b) 114 | a = vec3.frcoerce(a) 115 | b = vec3.frcoerce(b) 116 | return vec3.frnew(a[1] - b[1], a[2] - b[2], a[3] - b[3]) 117 | end 118 | 119 | -- Multiplies two vectors component-wise, coercing the arguments as necessary. 120 | -- Always produces a vec3. 121 | function vec3.__mul(a, b) 122 | a = vec3.frcoerce(a) 123 | b = vec3.frcoerce(b) 124 | return vec3.frnew(a[1] * b[1], a[2] * b[2], a[3] * b[3]) 125 | end 126 | 127 | -- Divides two vectors component-wise, coercing the arguments as necessary. 128 | -- Always produces a vec3. 129 | function vec3.__div(a, b) 130 | a = vec3.frcoerce(a) 131 | b = vec3.frcoerce(b) 132 | return vec3.frnew(a[1] / b[1], a[2] / b[2], a[3] / b[3]) 133 | end 134 | 135 | -- Negates a vector component-wise. 136 | -- Always produces a vec3. 137 | function vec3.__unm(v) 138 | return vec3.frnew(-v[1], -v[2], -v[3]) 139 | end 140 | 141 | -- Module exports. 142 | return nil 143 | -------------------------------------------------------------------------------- /frlib/base/vec4.lua: -------------------------------------------------------------------------------- 1 | -- Import relevant base types. 2 | local types = require "base.types" 3 | require "base.common" 4 | 5 | -- Module aliases. 6 | local vec2 = types.vec2 7 | local vec3 = types.vec3 8 | local vec4 = types.vec4 9 | 10 | -- Forward index getter to common type. 11 | vec4.__index = types.common.__index 12 | 13 | -- Forward index setter to common type. 14 | vec4.__newindex = types.common.__newindex 15 | 16 | -- Getters. 17 | vec4.getters = { 18 | x = function(v) return v[1] end, 19 | y = function(v) return v[2] end, 20 | z = function(v) return v[3] end, 21 | w = function(v) return v[4] end, 22 | r = function(v) return v[1] end, 23 | g = function(v) return v[2] end, 24 | b = function(v) return v[3] end, 25 | a = function(v) return v[4] end 26 | } 27 | 28 | -- Setters. 29 | vec4.setters = { 30 | x = function(v, val) v[1] = val end, 31 | y = function(v, val) v[2] = val end, 32 | z = function(v, val) v[3] = val end, 33 | w = function(v, val) v[4] = val end, 34 | r = function(v, val) v[1] = val end, 35 | g = function(v, val) v[2] = val end, 36 | b = function(v, val) v[3] = val end, 37 | a = function(v, val) v[3] = val end 38 | } 39 | 40 | -- Constructor that provides the following forms: 41 | -- vec4(x) -> (x is a number) 42 | -- vec4(v) -> (v is a vec4) 43 | -- vec4(v, w) -> (v is a vec3, w is a number) 44 | -- vec4(v, z, w) -> (v is a vec2, w, z are numbers) 45 | -- vec4(x, y, z, w) -> (x, y, z, w are numbers) 46 | function vec4.new(...) 47 | local v = nil 48 | 49 | local nargs = select("#", ...) 50 | if nargs == 1 then 51 | local arg = select(1, ...) 52 | local argmt = getmetatable(arg) 53 | if type(arg) == "number" then 54 | v = {arg, arg, arg, arg} 55 | elseif argmt == vec4 then 56 | v = {arg[1], arg[2], arg[3], arg[4]} 57 | else 58 | error("invalid construction of vec4", 3) 59 | end 60 | elseif nargs == 2 then 61 | local args = {select(1, ...), select(2, ...)} 62 | local arg1mt = getmetatable(args[1]) 63 | if arg1mt ~= vec3 or type(args[2]) ~= "number" then 64 | error("invalid construction of vec4", 3) 65 | end 66 | local v3 = args[1] 67 | v = {v3[1], v3[2], v3[3], args[2]} 68 | elseif nargs == 3 then 69 | local args = {select(1, ...), select(2, ...), select(3, ...)} 70 | local arg1mt = getmetatable(args[1]) 71 | if arg1mt ~= vec2 or type(args[2]) ~= "number" or type(args[3]) ~= "number" then 72 | error("invalid construction of vec4", 3) 73 | end 74 | local v2 = args[1] 75 | v = {v2[1], v2[2], args[2], args[3]} 76 | elseif nargs == 4 then 77 | local args = {select(1, ...), select(2, ...), select(3, ...), select(4, ...)} 78 | if type(args[1]) ~= "number" or type(args[2]) ~= "number" or type(args[3]) ~= "number" or type(args[4]) ~= "number" then 79 | error("invalid construction of vec4", 3) 80 | end 81 | v = {args[1], args[2], args[3], args[4]} 82 | else 83 | error("invalid construction of vec4", 3) 84 | end 85 | 86 | setmetatable(v, vec4) 87 | return v 88 | end 89 | 90 | -- Fast constructor for internal use only. 91 | function vec4.frnew(x, y, z, w) 92 | local v = {x, y, z, w} 93 | setmetatable(v, vec4) 94 | return v 95 | end 96 | 97 | -- Attempts to coerce val to a vec3 by construction. 98 | function vec4.frcoerce(val) 99 | if getmetatable(val) == vec4 then 100 | return val 101 | end 102 | 103 | local result, v = pcall(vec4.new, val) 104 | if result then 105 | return v 106 | end 107 | 108 | error("no known conversion to vec4", 3) 109 | end 110 | 111 | -- Converts the vector to a string representation. 112 | function vec4.__tostring(v) 113 | return table.concat {"vec4<", v[1], ", ", v[2], ", ", v[3], ", ", v[4], ">"} 114 | end 115 | 116 | -- Adds two vectors component-wise, coercing the arguments as necessary. 117 | -- Always produces a vec4. 118 | function vec4.__add(a, b) 119 | a = vec4.frcoerce(a) 120 | b = vec4.frcoerce(b) 121 | return vec4.frnew(a[1] + b[1], a[2] + b[2], a[3] + b[3], a[4] + b[4]) 122 | end 123 | 124 | -- Subtracts two vectors component-wise, coercing the arguments as necessary. 125 | -- Always produces a vec4. 126 | function vec4.__sub(a, b) 127 | a = vec4.frcoerce(a) 128 | b = vec4.frcoerce(b) 129 | return vec4.frnew(a[1] - b[1], a[2] - b[2], a[3] - b[3], a[4] - b[4]) 130 | end 131 | 132 | -- Multiplies two vectors component-wise, coercing the arguments as necessary. 133 | -- Always produces a vec4. 134 | function vec4.__mul(a, b) 135 | a = vec4.frcoerce(a) 136 | b = vec4.frcoerce(b) 137 | return vec4.frnew(a[1] * b[1], a[2] * b[2], a[3] * b[3], a[4] * b[4]) 138 | end 139 | 140 | -- Divides two vectors component-wise, coercing the arguments as necessary. 141 | -- Always produces a vec4. 142 | function vec4.__div(a, b) 143 | a = vec4.frcoerce(a) 144 | b = vec4.frcoerce(b) 145 | return vec4.frnew(a[1] / b[1], a[2] / b[2], a[3] / b[3], a[4] / b[4]) 146 | end 147 | 148 | -- Negates a vector component-wise. 149 | -- Always produces a vec4. 150 | function vec4.__unm(v) 151 | return vec4.frnew(-v[1], -v[2], -v[3], -v[4]) 152 | end 153 | 154 | -- Module exports. 155 | return nil 156 | -------------------------------------------------------------------------------- /frlib/extras.lua: -------------------------------------------------------------------------------- 1 | -- Import loaders. 2 | local sloaders = require "loaders.shaders" 3 | local tloaders = require "loaders.textures" 4 | local mloaders = require "loaders.meshes" 5 | 6 | -- Import primitives. 7 | local simple = require "primitives.simple" 8 | 9 | -- Import shader builders. 10 | local phong = require "shaders.phong" 11 | local lights = require "shaders.lights" 12 | 13 | -- Module exports. 14 | return { 15 | frsl = sloaders.frsl, 16 | procedural = tloaders.procedural, 17 | targa = tloaders.targa, 18 | obj = mloaders.obj, 19 | plane = simple.plane, 20 | cube = simple.cube, 21 | phong = phong.phong, 22 | montecarlo = phong.montecarlo, 23 | light = lights.light 24 | } 25 | -------------------------------------------------------------------------------- /frlib/flexrender.lua: -------------------------------------------------------------------------------- 1 | -- Import base types. 2 | local types = require "base.types" 3 | require "base.common" 4 | require "base.vec2" 5 | require "base.vec3" 6 | require "base.vec4" 7 | require "base.mat4" 8 | 9 | -- Parent base types to common type. 10 | setmetatable(types.vec2, types.common) 11 | setmetatable(types.vec3, types.common) 12 | setmetatable(types.vec4, types.common) 13 | setmetatable(types.mat4, types.common) 14 | 15 | -- Import utility functions. 16 | local trig = require "base.trig" 17 | local geometric = require "base.geometric" 18 | local transform = require "base.transform" 19 | local sample = require "base.sample" 20 | 21 | -- Module exports. 22 | return { 23 | vec2 = types.vec2, 24 | vec3 = types.vec3, 25 | vec4 = types.vec4, 26 | mat4 = types.mat4, 27 | radians = trig.radians, 28 | degrees = trig.degrees, 29 | length = geometric.length, 30 | distance = geometric.distance, 31 | dot = geometric.dot, 32 | cross = geometric.cross, 33 | normalize = geometric.normalize, 34 | reflect = geometric.reflect, 35 | refract = geometric.refract, 36 | scale = transform.scale, 37 | rotate = transform.rotate, 38 | translate = transform.translate, 39 | hemisample = sample.hemisample 40 | } 41 | -------------------------------------------------------------------------------- /frlib/loaders/meshes.lua: -------------------------------------------------------------------------------- 1 | -- Import relevant base types. 2 | local types = require "base.types" 3 | require "base.vec2" 4 | require "base.vec3" 5 | require "base.vec4" 6 | 7 | -- Import utility functions. 8 | local transform = require "base.transform" 9 | 10 | -- Module aliases. 11 | local vec2 = types.vec2 12 | local vec3 = types.vec3 13 | local vec4 = types.vec4 14 | local scale = transform.scale 15 | local translate = transform.translate 16 | 17 | -- Cache for OBJ models. 18 | local obj_cache = {} 19 | 20 | -- Loads a mesh in OBJ format, caching it for later reloading. 21 | local function obj(filename, adjust) 22 | if obj_cache[filename] then 23 | return obj_cache[filename] 24 | end 25 | 26 | adjust = adjust or false 27 | 28 | local vertices = {} 29 | local normals = {} 30 | local texcoords = {} 31 | local faces = {} 32 | 33 | local f = assert(io.open(filename, "r")) 34 | 35 | local line = f:read("*l") 36 | while line ~= nil do 37 | if string.len(line) == 0 or string.sub(line, 1, 1) == "#" then 38 | -- Ignore, blank line or comment. 39 | else 40 | -- Match vertex definitions. 41 | local v1, v2, v3 = string.match(line, "v%s+([%d%-%.]+)%s+([%d%-%.]+)%s+([%d%-%.]+)") 42 | if v1 ~= nil and v2 ~= nil and v3 ~= nil then 43 | table.insert(vertices, vec3(tonumber(v1), tonumber(v2), tonumber(v3))) 44 | else 45 | -- Match normal definitions. 46 | local n1, n2, n3 = string.match(line, "vn%s+([%d%-%.]+)%s+([%d%-%.]+)%s+([%d%-%.]+)") 47 | if n1 ~= nil and n2 ~= nil and n3 ~= nil then 48 | table.insert(normals, vec3(tonumber(n1), tonumber(n2), tonumber(n3))) 49 | else 50 | -- Match texcoord definitions. 51 | local t1, t2 = string.match(line, "vt%s+([%d%-%.]+)%s+([%d%-%.]+)") 52 | if t1 ~= nil and t2 ~= nil then 53 | table.insert(texcoords, vec2(tonumber(t1), tonumber(t2))) 54 | else 55 | -- Match face definitions. 56 | local oneV, oneT, oneN, twoV, twoT, twoN, threeV, threeT, threeN = string.match(line, "f%s+(%d+)/(%d*)/(%d+)%s+(%d+)/(%d*)/(%d+)%s+(%d+)/(%d*)/(%d+)") 57 | if oneV ~= nil and oneT ~= nil and oneN ~= nil and twoV ~= nil and twoT ~= nil and twoN ~= nil and threeV ~= nil and threeT ~= nil and threeN ~= nil then 58 | if oneT == "" then oneT = "-1" end 59 | if twoT == "" then twoT = "-2" end 60 | if threeT == "" then threeT = "-3" end 61 | table.insert(faces, { 62 | {tonumber(oneV), tonumber(oneT), tonumber(oneN)}, 63 | {tonumber(twoV), tonumber(twoT), tonumber(twoN)}, 64 | {tonumber(threeV), tonumber(threeT), tonumber(threeN)} 65 | }) 66 | end 67 | end 68 | end 69 | end 70 | end 71 | 72 | -- Read next line. 73 | line = f:read("*l") 74 | end 75 | 76 | f:close() 77 | 78 | if adjust then 79 | -- Find the centroid and the min Y. 80 | local centroid = vec3(0, 0, 0) 81 | local minY = vertices[1].y 82 | for _, vertex in ipairs(vertices) do 83 | if vertex.y < minY then 84 | minY = vertex.y 85 | end 86 | centroid = centroid + vertex 87 | end 88 | centroid = centroid / #vertices 89 | 90 | local xform = scale(1 / (centroid.y - minY)) * translate(-centroid) 91 | 92 | local adjusted = {} 93 | for _, vertex in ipairs(vertices) do 94 | table.insert(adjusted, vec3(xform * vec4(vertex, 1))) 95 | end 96 | 97 | vertices = adjusted 98 | end 99 | 100 | local mesh = function() 101 | for i, vert in ipairs(vertices) do 102 | vertex { 103 | v = vertices[i], 104 | n = normals[i], 105 | t = texcoords[i] 106 | } 107 | end 108 | 109 | for _, face in ipairs(faces) do 110 | local v1 = face[1][1] - 1 111 | local v2 = face[2][1] - 1 112 | local v3 = face[3][1] - 1 113 | 114 | triangle {v1, v2, v3} 115 | end 116 | end 117 | 118 | obj_cache[filename] = mesh 119 | return mesh 120 | end 121 | 122 | -- Module exports. 123 | return { 124 | obj = obj 125 | } 126 | -------------------------------------------------------------------------------- /frlib/loaders/shaders.lua: -------------------------------------------------------------------------------- 1 | -- Loads a FlexRender Shading Language source file into a FlexRender shader. 2 | -- Returns the resource ID of the shader. 3 | local function frsl(filename) 4 | local f = assert(io.open(filename, "r")) 5 | local procedure = f:read("*a") 6 | f:close() 7 | 8 | return shader { 9 | code = procedure 10 | } 11 | end 12 | 13 | -- Module exports. 14 | return { 15 | frsl = frsl 16 | } 17 | -------------------------------------------------------------------------------- /frlib/loaders/textures.lua: -------------------------------------------------------------------------------- 1 | -- Needs lpack to unpack binary data. 2 | require "pack" 3 | 4 | -- Import relevant base types. 5 | local types = require "base.types" 6 | require "base.vec2" 7 | require "base.vec3" 8 | 9 | -- Module aliases. 10 | local vec2 = types.vec2 11 | local vec3 = types.vec3 12 | 13 | -- Loads a procedural texture source file into a FlexRender texture. 14 | -- Returns the resource ID of the texture. 15 | local function procedural(filename) 16 | local f = assert(io.open(filename, "r")) 17 | local procedure = f:read("*a") 18 | f:close() 19 | 20 | return texture { 21 | kind = "procedural", 22 | code = procedure 23 | } 24 | end 25 | 26 | -- Loads an uncompressed Targa image, assuming origin at bottom-left. 27 | local function targa(filename) 28 | local f = assert(io.open(filename, "rb")) 29 | 30 | local _ = nil 31 | local kind = nil 32 | local width = nil 33 | local height = nil 34 | local depth = nil 35 | local red = nil 36 | local green = nil 37 | local blue = nil 38 | local rtex = {} 39 | local gtex = {} 40 | local btex = {} 41 | 42 | -- Unpack the header data. 43 | _ = assert(f:read(2)) 44 | _, kind = string.unpack(assert(f:read(1)), "b") 45 | _ = assert(f:read(9)) 46 | _, width = string.unpack(assert(f:read(2)), " Downloading $1.xz" 10 | wget -O $BASEPATH/$1.xz http://flexrender.org/files/assets/$1.xz 11 | 12 | echo "" 13 | echo "==> Unpacking $1.xz" 14 | xz --decompress $BASEPATH/$1.xz 15 | fi 16 | } 17 | 18 | echo "======================================================================" 19 | echo " DOWNLOADING ASSETS @ "`date` 20 | echo "======================================================================" 21 | 22 | fetch "buddha-hi.obj" 23 | fetch "buddha-med.obj" 24 | fetch "buddha-lo.obj" 25 | fetch "bunny-hi.obj" 26 | fetch "bunny-med.obj" 27 | fetch "bunny-lo.obj" 28 | fetch "dragon-hi.obj" 29 | fetch "dragon-med.obj" 30 | fetch "dragon-lo.obj" 31 | fetch "tile1.tga" 32 | fetch "wood1.tga" 33 | fetch "marble1.tga" 34 | fetch "brick1.tga" 35 | 36 | echo "" 37 | echo "======================================================================" 38 | echo " DOWNLOAD COMPLETE @ "`date` 39 | echo "======================================================================" 40 | -------------------------------------------------------------------------------- /scenes/backdrop_shader.lua: -------------------------------------------------------------------------------- 1 | -- Import frlib. 2 | package.path = "frlib/?.lua;" .. package.path 3 | local fr = require "flexrender" 4 | 5 | vec2 = fr.vec2 6 | vec3 = fr.vec3 7 | 8 | function direct(V, N, T, L, I) 9 | -- Nothing. 10 | end 11 | 12 | function indirect(V, N, T) 13 | local color = texture3("bg_r", "bg_g", "bg_b", T) 14 | accumulate3("R", "G", "B", color) 15 | end 16 | -------------------------------------------------------------------------------- /scenes/bunnies.lua: -------------------------------------------------------------------------------- 1 | -- Import frlib and extras. 2 | package.cpath = "3p/build/lib/?.so;" .. package.cpath 3 | package.path = "frlib/?.lua;" .. package.path 4 | local fr = require "flexrender" 5 | local fre = require "extras" 6 | 7 | -- Handy aliases. 8 | local vec3 = fr.vec3 9 | local mat4 = fr.mat4 10 | local radians = fr.radians 11 | local normalize = fr.normalize 12 | local scale = fr.scale 13 | local rotate = fr.rotate 14 | local translate = fr.translate 15 | 16 | local width = 10 17 | 18 | camera { 19 | eye = vec3(20, 20, 20), 20 | look = vec3(5, 0, 5) 21 | } 22 | 23 | material { 24 | name = "light", 25 | emissive = true, 26 | shader = fre.light(vec3(1, 1, 1)) 27 | } 28 | 29 | mesh { 30 | material = "light", 31 | transform = translate(vec3(width, 20, width * 2)) * rotate(radians(135), vec3(1, 0, 0)), 32 | data = fre.plane(10) 33 | } 34 | 35 | local tile_r, tile_g, tile_b = fre.targa("scenes/assets/tile1.tga") 36 | 37 | function draw_model(model, xform, suffix) 38 | local color = vec3(math.random() * 0.5 + 0.25, 39 | math.random() * 0.5 + 0.25, 40 | math.random() * 0.5 + 0.25) 41 | local data = nil 42 | 43 | if model == 1 then 44 | data = fre.obj("scenes/assets/bunny-lo.obj", true) 45 | elseif model == 2 then 46 | data = fre.obj("scenes/assets/buddha-lo.obj", true) 47 | else 48 | data = fre.obj("scenes/assets/dragon-lo.obj", true) 49 | end 50 | 51 | local mesh_mat = "mesh" .. suffix 52 | 53 | material { 54 | name = mesh_mat, 55 | emissive = false, 56 | shader = fre.phong(0.6, color, 0.2, color, 0.2, 8) 57 | } 58 | 59 | mesh { 60 | material = mesh_mat, 61 | transform = xform, 62 | data = data 63 | } 64 | end 65 | 66 | function draw_tile(xform, suffix) 67 | local tile_mat = "mat" .. suffix 68 | 69 | material { 70 | name = tile_mat, 71 | emissive = false, 72 | shader = fre.frsl("scenes/tile_shader.lua"), 73 | textures = { 74 | tile_r = tile_r, 75 | tile_g = tile_g, 76 | tile_b = tile_b, 77 | } 78 | } 79 | 80 | mesh { 81 | material = tile_mat, 82 | transform = xform * rotate(radians(-90), vec3(1, 0, 0)), 83 | data = fre.plane(2) 84 | } 85 | end 86 | 87 | for i = -width, width do 88 | for j = -width, width do 89 | math.randomseed(j * width + i) 90 | 91 | local color = vec3(math.random() * 0.5 + 0.25, 92 | math.random() * 0.5 + 0.25, 93 | math.random() * 0.5 + 0.25) 94 | local facing = math.random(0, 359) 95 | local size = 1 96 | local model = math.random(1, 3) 97 | if model == 1 then 98 | size = math.random() * 0.5 + 0.5 99 | elseif model == 2 then 100 | size = math.random() * 0.5 + 1 101 | else 102 | size = math.random() * 0.25 + 0.5 103 | end 104 | local suffix = table.concat { 105 | "-", tostring(i), 106 | "-", tostring(j) 107 | } 108 | 109 | local model_xform = translate(vec3(i * 2, size, j * 2)) * rotate(radians(facing), vec3(0, 1, 0)) * scale(size) 110 | draw_model(model, model_xform, suffix) 111 | 112 | local tile_xform = translate(vec3(i * 2, 0, j * 2)) 113 | draw_tile(tile_xform, suffix) 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /scenes/cornell-models.lua: -------------------------------------------------------------------------------- 1 | -- Import frlib and extras. 2 | package.cpath = "3p/build/lib/?.so;" .. package.cpath 3 | package.path = "frlib/?.lua;" .. package.path 4 | local fr = require "flexrender" 5 | local fre = require "extras" 6 | 7 | -- Handy aliases. 8 | local vec3 = fr.vec3 9 | local radians = fr.radians 10 | local scale = fr.scale 11 | local rotate = fr.rotate 12 | local translate = fr.translate 13 | 14 | local tile_r, tile_g, tile_b = fre.targa("scenes/assets/tile1.tga") 15 | 16 | function draw_tile(i, j) 17 | local tile_mat = table.concat { 18 | "mat-", tostring(i), "-", tostring(j) 19 | } 20 | 21 | material { 22 | name = tile_mat, 23 | emissive = false, 24 | shader = fre.frsl("scenes/shiny_tile_shader.lua"), 25 | textures = { 26 | tile_r = tile_r, 27 | tile_g = tile_g, 28 | tile_b = tile_b, 29 | } 30 | } 31 | 32 | mesh { 33 | material = tile_mat, 34 | transform = translate(vec3(i + 0.5, -2.75, j + 0.5)) * rotate(radians(-90), vec3(1, 0, 0)), 35 | data = fre.plane(1) 36 | } 37 | end 38 | 39 | camera { 40 | eye = vec3(0, 0.01, 8), 41 | look = vec3(0, 0, 0) 42 | } 43 | 44 | material { 45 | name = "white", 46 | emissive = false, 47 | --shader = fre.montecarlo(0.6, vec3(0.730, 0.739, 0.729), 48 | -- 0.2, 32, 49 | -- 0.2, 8) 50 | shader = fre.phong(0.6, vec3(0.730, 0.739, 0.729), 51 | 0.2, vec3(0.730, 0.739, 0.729), 52 | 0.2, 8) 53 | } 54 | 55 | material { 56 | name = "red", 57 | emissive = false, 58 | --shader = fre.montecarlo(0.6, vec3(0.610, 0.056, 0.062), 59 | -- 0.2, 32, 60 | -- 0.2, 8) 61 | shader = fre.phong(0.6, vec3(0.610, 0.056, 0.062), 62 | 0.2, vec3(0.610, 0.056, 0.062), 63 | 0.2, 8) 64 | } 65 | 66 | material { 67 | name = "green", 68 | emissive = false, 69 | --shader = fre.montecarlo(0.6, vec3(0.117, 0.435, 0.115), 70 | -- 0.2, 32, 71 | -- 0.2, 8) 72 | shader = fre.phong(0.6, vec3(0.117, 0.435, 0.115), 73 | 0.2, vec3(0.117, 0.435, 0.115), 74 | 0.2, 8) 75 | } 76 | 77 | material { 78 | name = "light", 79 | emissive = true, 80 | shader = fre.light(vec3(1, 0.788, 0.553)) 81 | } 82 | 83 | material { 84 | name = "fill", 85 | emissive = true, 86 | shader = fre.light(vec3(0.5, 0.394, 0.277)) 87 | } 88 | 89 | material { 90 | name = "mirror", 91 | emissive = false, 92 | shader = fre.frsl("scenes/mirror_shader.lua") 93 | } 94 | 95 | material { 96 | name = "toon", 97 | emissive = false, 98 | shader = fre.frsl("scenes/toon_shader.lua") 99 | } 100 | 101 | -- Light. 102 | mesh { 103 | material = "light", 104 | transform = translate(vec3(0, 2.74, 0)) * rotate(radians(90), vec3(1, 0, 0)), 105 | data = fre.plane(1.2) 106 | } 107 | 108 | -- Fill light. 109 | mesh { 110 | material = "fill", 111 | transform = translate(vec3(0, 2.5, 5)) * rotate(radians(135), vec3(1, 0, 0)), 112 | data = fre.plane(1) 113 | } 114 | 115 | -- Floor. 116 | for i = 1, 6 do 117 | for j = 1, 6 do 118 | draw_tile(i - 3.75, j - 3.75) 119 | end 120 | end 121 | 122 | -- Ceiling. 123 | mesh { 124 | material = "white", 125 | transform = translate(vec3(0, 2.75, 0)) * rotate(radians(90), vec3(1, 0, 0)), 126 | data = fre.plane(5.5) 127 | } 128 | 129 | -- Back wall. 130 | mesh { 131 | material = "white", 132 | transform = translate(vec3(0, 0, -2.75)), 133 | data = fre.plane(5.5) 134 | } 135 | 136 | -- Left wall. 137 | mesh { 138 | material = "red", 139 | transform = translate(vec3(-2.75, 0, 0)) * rotate(radians(90), vec3(0, 1, 0)), 140 | data = fre.plane(5.5) 141 | } 142 | 143 | -- Right wall. 144 | mesh { 145 | material = "green", 146 | transform = translate(vec3(2.75, 0, 0)) * rotate(radians(-90), vec3(0, 1, 0)), 147 | data = fre.plane(5.5) 148 | } 149 | 150 | -- Buddha. 151 | mesh { 152 | material = "mirror", 153 | transform = translate(vec3(-0.9, -0.75, -0.9)) * rotate(radians(20), vec3(0, 1, 0)) * scale(2), 154 | data = fre.obj("scenes/assets/buddha-hi.obj", true) 155 | } 156 | 157 | -- Bunny. 158 | mesh { 159 | material = "toon", 160 | transform = translate(vec3(1, -1.75, 1)) * rotate(radians(20), vec3(0, 1, 0)), 161 | data = fre.obj("scenes/assets/bunny-hi.obj", true) 162 | } 163 | -------------------------------------------------------------------------------- /scenes/cornell.lua: -------------------------------------------------------------------------------- 1 | -- Import frlib and extras. 2 | package.cpath = "3p/build/lib/?.so;" .. package.cpath 3 | package.path = "frlib/?.lua;" .. package.path 4 | local fr = require "flexrender" 5 | local fre = require "extras" 6 | 7 | -- Handy aliases. 8 | local vec3 = fr.vec3 9 | local radians = fr.radians 10 | local scale = fr.scale 11 | local rotate = fr.rotate 12 | local translate = fr.translate 13 | 14 | camera { 15 | eye = vec3(0, 0, 8), 16 | look = vec3(0, 0, 0) 17 | } 18 | 19 | material { 20 | name = "white", 21 | emissive = false, 22 | --shader = fre.montecarlo(0.6, vec3(0.730, 0.739, 0.729), 23 | -- 0.2, 32, 24 | -- 0.2, 8) 25 | shader = fre.phong(0.6, vec3(0.730, 0.739, 0.729), 26 | 0.2, vec3(0.730, 0.739, 0.729), 27 | 0.2, 8) 28 | } 29 | 30 | material { 31 | name = "red", 32 | emissive = false, 33 | --shader = fre.montecarlo(0.6, vec3(0.610, 0.056, 0.062), 34 | -- 0.2, 32, 35 | -- 0.2, 8) 36 | shader = fre.phong(0.6, vec3(0.610, 0.056, 0.062), 37 | 0.2, vec3(0.610, 0.056, 0.062), 38 | 0.2, 8) 39 | } 40 | 41 | material { 42 | name = "green", 43 | emissive = false, 44 | --shader = fre.montecarlo(0.6, vec3(0.117, 0.435, 0.115), 45 | -- 0.2, 32, 46 | -- 0.2, 8) 47 | shader = fre.phong(0.6, vec3(0.117, 0.435, 0.115), 48 | 0.2, vec3(0.117, 0.435, 0.115), 49 | 0.2, 8) 50 | } 51 | 52 | material { 53 | name = "light", 54 | emissive = true, 55 | shader = fre.light(vec3(17.0, 10.0, 6.0)) 56 | } 57 | 58 | -- Light. 59 | mesh { 60 | material = "light", 61 | transform = translate(vec3(0, 2.74, 0)) * rotate(radians(90), vec3(1, 0, 0)), 62 | data = fre.plane(1.2) 63 | } 64 | 65 | -- Floor. 66 | mesh { 67 | material = "white", 68 | transform = translate(vec3(0, -2.75, 0)) * rotate(radians(-90), vec3(1, 0, 0)), 69 | data = fre.plane(5.5) 70 | } 71 | 72 | -- Ceiling. 73 | mesh { 74 | material = "white", 75 | transform = translate(vec3(0, 2.75, 0)) * rotate(radians(90), vec3(1, 0, 0)), 76 | data = fre.plane(5.5) 77 | } 78 | 79 | -- Back wall. 80 | mesh { 81 | material = "white", 82 | transform = translate(vec3(0, 0, -2.75)), 83 | data = fre.plane(5.5) 84 | } 85 | 86 | -- Left wall. 87 | mesh { 88 | material = "red", 89 | transform = translate(vec3(-2.75, 0, 0)) * rotate(radians(90), vec3(0, 1, 0)), 90 | data = fre.plane(5.5) 91 | } 92 | 93 | -- Right wall. 94 | mesh { 95 | material = "green", 96 | transform = translate(vec3(2.75, 0, 0)) * rotate(radians(-90), vec3(0, 1, 0)), 97 | data = fre.plane(5.5) 98 | } 99 | 100 | -- Tall box. 101 | mesh { 102 | material = "white", 103 | transform = translate(vec3(-0.9, -1.1, -0.9)) * rotate(radians(20), vec3(0, 1, 0)) * scale(vec3(1.7, 3.3, 1.7)), 104 | data = fre.cube(1) 105 | } 106 | 107 | -- Short box. 108 | mesh { 109 | material = "white", 110 | transform = translate(vec3(1, -1.95, 1)) * rotate(radians(-20), vec3(0, 1, 0)) * scale(1.6), 111 | data = fre.cube(1) 112 | } 113 | -------------------------------------------------------------------------------- /scenes/field.lua: -------------------------------------------------------------------------------- 1 | -- Import frlib and extras. 2 | package.cpath = "3p/build/lib/?.so;" .. package.cpath 3 | package.path = "frlib/?.lua;" .. package.path 4 | local fr = require "flexrender" 5 | local fre = require "extras" 6 | 7 | -- Handy aliases. 8 | local vec3 = fr.vec3 9 | local mat4 = fr.mat4 10 | local radians = fr.radians 11 | local normalize = fr.normalize 12 | local scale = fr.scale 13 | local rotate = fr.rotate 14 | local translate = fr.translate 15 | 16 | local width = 25 17 | 18 | camera { 19 | eye = vec3(0, 70, 60), 20 | look = vec3(0, 0, 15) 21 | } 22 | 23 | material { 24 | name = "light", 25 | emissive = true, 26 | shader = fre.light(vec3(1, 1, 1)) 27 | } 28 | 29 | mesh { 30 | material = "light", 31 | transform = translate(vec3(width, 20, width * 2)) * rotate(radians(135), vec3(1, 0, 0)), 32 | data = fre.plane(10) 33 | } 34 | 35 | local tile_r, tile_g, tile_b = fre.targa("scenes/assets/tile1.tga") 36 | 37 | function draw_model(model, xform, suffix, mirror) 38 | local color = vec3(math.random() * 0.5 + 0.25, 39 | math.random() * 0.5 + 0.25, 40 | math.random() * 0.5 + 0.25) 41 | local data = nil 42 | 43 | local shader = fre.phong(0.6, color, 0.2, color, 0.2, 8) 44 | if mirror then 45 | shader = fre.frsl("scenes/mirror_shader.lua") 46 | end 47 | 48 | 49 | if model == 1 then 50 | data = fre.obj("scenes/assets/bunny-lo.obj", true) 51 | elseif model == 2 then 52 | data = fre.obj("scenes/assets/buddha-lo.obj", true) 53 | else 54 | data = fre.obj("scenes/assets/dragon-lo.obj", true) 55 | end 56 | 57 | local mesh_mat = "mesh" .. suffix 58 | 59 | material { 60 | name = mesh_mat, 61 | emissive = false, 62 | shader = shader 63 | } 64 | 65 | mesh { 66 | material = mesh_mat, 67 | transform = xform, 68 | data = data 69 | } 70 | end 71 | 72 | function draw_tile(xform, suffix) 73 | local tile_mat = "mat" .. suffix 74 | 75 | material { 76 | name = tile_mat, 77 | emissive = false, 78 | shader = fre.frsl("scenes/tile_shader.lua"), 79 | textures = { 80 | tile_r = tile_r, 81 | tile_g = tile_g, 82 | tile_b = tile_b, 83 | } 84 | } 85 | 86 | mesh { 87 | material = tile_mat, 88 | transform = xform * rotate(radians(-90), vec3(1, 0, 0)), 89 | data = fre.plane(2) 90 | } 91 | end 92 | 93 | for i = -width, width do 94 | for j = -width, width do 95 | math.randomseed(j * width + i) 96 | 97 | local color = vec3(math.random() * 0.5 + 0.25, 98 | math.random() * 0.5 + 0.25, 99 | math.random() * 0.5 + 0.25) 100 | local facing = math.random(0, 359) 101 | local size = 1 102 | local model = math.random(1, 3) 103 | if model == 1 then 104 | size = math.random() * 0.5 + 0.5 105 | elseif model == 2 then 106 | size = math.random() * 0.5 + 1 107 | else 108 | size = math.random() * 0.25 + 0.5 109 | end 110 | local suffix = table.concat { 111 | "-", tostring(i), 112 | "-", tostring(j) 113 | } 114 | 115 | local model_xform = translate(vec3(i * 2, size, j * 2)) * rotate(radians(facing), vec3(0, 1, 0)) * scale(size) 116 | draw_model(model, model_xform, suffix, ((i * width + j) % 2 == 0)) 117 | 118 | local tile_xform = translate(vec3(i * 2, 0, j * 2)) 119 | draw_tile(tile_xform, suffix) 120 | end 121 | end 122 | -------------------------------------------------------------------------------- /scenes/mirror_shader.lua: -------------------------------------------------------------------------------- 1 | -- Import frlib. 2 | package.path = "frlib/?.lua;" .. package.path 3 | local fr = require "flexrender" 4 | 5 | vec2 = fr.vec2 6 | vec3 = fr.vec3 7 | reflect = fr.reflect 8 | 9 | function direct(V, N, T, L, I) 10 | -- Nothing. 11 | end 12 | 13 | function indirect(V, N, T) 14 | local R = reflect(-V, N) 15 | trace(R, 1) 16 | end 17 | -------------------------------------------------------------------------------- /scenes/shiny_tile_shader.lua: -------------------------------------------------------------------------------- 1 | -- Import frlib. 2 | package.path = "frlib/?.lua;" .. package.path 3 | local fr = require "flexrender" 4 | 5 | vec2 = fr.vec2 6 | vec3 = fr.vec3 7 | dot = fr.dot 8 | reflect = fr.reflect 9 | 10 | function direct(V, N, T, L, I) 11 | local NdotL = dot(N, L) 12 | if NdotL < 0 then NdotL = 0 end 13 | 14 | local R = reflect(-V, N) 15 | local VdotR = dot(R, V) 16 | if VdotR < 0 then VdotR = 0 end 17 | 18 | local color = texture3("tile_r", "tile_g", "tile_b", T) 19 | local diffuse = 0.6 * color * I * NdotL 20 | local specular = 0.2 * I * (VdotR ^ 8) 21 | accumulate3("R", "G", "B", diffuse + specular) 22 | end 23 | 24 | function indirect(V, N, T) 25 | local R = reflect(-V, N) 26 | trace(R, 0.2) 27 | end 28 | -------------------------------------------------------------------------------- /scenes/test.lua: -------------------------------------------------------------------------------- 1 | -- Import frlib and extras. 2 | package.cpath = "3p/build/lib/?.so;" .. package.cpath 3 | package.path = "frlib/?.lua;" .. package.path 4 | local fr = require "flexrender" 5 | local fre = require "extras" 6 | 7 | -- Handy aliases. 8 | local vec3 = fr.vec3 9 | local radians = fr.radians 10 | local normalize = fr.normalize 11 | local scale = fr.scale 12 | local rotate = fr.rotate 13 | local translate = fr.translate 14 | 15 | camera { 16 | eye = vec3(8, 15, 15), 17 | look = vec3(2, 0, 0) 18 | } 19 | 20 | material { 21 | name = "light", 22 | emissive = true, 23 | shader = fre.light(vec3(1, 1, 1)) 24 | } 25 | 26 | mesh { 27 | material = "light", 28 | transform = translate(vec3(5, 15, 5)) * rotate(radians(90), vec3(1, 0, 0)), 29 | data = fre.plane(2) 30 | } 31 | 32 | for i = 1, 10 do 33 | for j = 1, 10 do 34 | for k = 1, 10 do 35 | local matname = table.concat { 36 | "mat-", tostring(i), 37 | "-", tostring(j), 38 | "-", tostring(k) 39 | } 40 | local color = vec3(i / 10, j / 10, j / 10) 41 | 42 | material { 43 | name = matname, 44 | emissive = false, 45 | shader = fre.phong(0.6, color, 0.2, color, 0.2, 8) 46 | } 47 | 48 | mesh { 49 | material = matname, 50 | transform = translate(vec3(i, j, k)), 51 | data = fre.cube(0.5) 52 | } 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /scenes/tile_shader.lua: -------------------------------------------------------------------------------- 1 | -- Import frlib. 2 | package.path = "frlib/?.lua;" .. package.path 3 | local fr = require "flexrender" 4 | 5 | vec2 = fr.vec2 6 | vec3 = fr.vec3 7 | dot = fr.dot 8 | reflect = fr.reflect 9 | 10 | function direct(V, N, T, L, I) 11 | local NdotL = dot(N, L) 12 | if NdotL < 0 then NdotL = 0 end 13 | 14 | local R = reflect(-V, N) 15 | local VdotR = dot(R, V) 16 | if VdotR < 0 then VdotR = 0 end 17 | 18 | local color = texture3("tile_r", "tile_g", "tile_b", T) 19 | local diffuse = 0.6 * color * I * NdotL 20 | local specular = 0.2 * I * (VdotR ^ 8) 21 | accumulate3("R", "G", "B", diffuse + specular) 22 | end 23 | 24 | function indirect(V, N, T) 25 | local color = texture3("tile_r", "tile_g", "tile_b", T) 26 | local ambient = 0.2 * color 27 | accumulate3("R", "G", "B", ambient) 28 | end 29 | -------------------------------------------------------------------------------- /scenes/toon_shader.lua: -------------------------------------------------------------------------------- 1 | -- Import frlib. 2 | package.path = "frlib/?.lua;" .. package.path 3 | local fr = require "flexrender" 4 | 5 | vec2 = fr.vec2 6 | vec3 = fr.vec3 7 | dot = fr.dot 8 | 9 | function direct(V, N, T, L, I) 10 | if dot(V, N) < 0.4 then 11 | return 12 | end 13 | 14 | local LdotN = dot(L, N) 15 | if LdotN > 0.95 then 16 | accumulate3("R", "G", "B", vec3(1, 1, 1)) 17 | elseif LdotN > 0.66 then 18 | accumulate3("R", "G", "B", vec3(0.5, 0.2, 0.2)) 19 | elseif LdotN > 0.33 then 20 | accumulate3("R", "G", "B", vec3(0.15, 0.05, 0.05)) 21 | else 22 | accumulate3("R", "G", "B", vec3(0.05, 0.01, 0.01)) 23 | end 24 | end 25 | 26 | function indirect(V, N, T) 27 | if dot(V, N) < 0.4 then 28 | return 29 | end 30 | 31 | accumulate3("R", "G", "B", vec3(0.1, 0.05, 0.05)) 32 | end 33 | -------------------------------------------------------------------------------- /scenes/wood_shader.lua: -------------------------------------------------------------------------------- 1 | -- Import frlib. 2 | package.path = "frlib/?.lua;" .. package.path 3 | local fr = require "flexrender" 4 | 5 | vec2 = fr.vec2 6 | vec3 = fr.vec3 7 | dot = fr.dot 8 | reflect = fr.reflect 9 | 10 | function direct(V, N, T, L, I) 11 | local NdotL = dot(N, L) 12 | if NdotL < 0 then NdotL = 0 end 13 | 14 | local R = reflect(-V, N) 15 | local VdotR = dot(R, V) 16 | if VdotR < 0 then VdotR = 0 end 17 | 18 | local color = texture3("wood_r", "wood_g", "wood_b", T) 19 | local diffuse = 0.7 * color * I * NdotL 20 | local specular = 0.1 * I * (VdotR ^ 8) 21 | accumulate3("R", "G", "B", diffuse + specular) 22 | end 23 | 24 | function indirect(V, N, T) 25 | local color = texture3("wood_r", "wood_g", "wood_b", T) 26 | local ambient = 0.2 * color 27 | accumulate3("R", "G", "B", ambient) 28 | end 29 | -------------------------------------------------------------------------------- /scripts/logusage.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Where are we in the filesystem? 4 | BASEPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | 6 | LOGFILE=$BASEPATH/../usage-`/sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`.log 7 | vmstat -n 1 | tee $LOGFILE 8 | -------------------------------------------------------------------------------- /scripts/profile.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | sudo opcontrol --deinit 4 | echo 0 | sudo tee /proc/sys/kernel/nmi_watchdog 5 | sudo opcontrol --vmlinux=/usr/src/linux-3.3.7-1-ARCH/vmlinux 6 | sudo opcontrol --separate=kernel 7 | sudo opcontrol --init 8 | sudo opcontrol --reset 9 | sudo opcontrol --start 10 | -------------------------------------------------------------------------------- /scripts/report.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Where are we in the filesystem? 4 | BASEPATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | 6 | sudo opcontrol --stop 7 | sudo opreport -l $BASEPATH/../bin/flexworker | tee $BASEPATH/../profile.txt 8 | -------------------------------------------------------------------------------- /src/baseline/baseline.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "engine.hpp" 8 | #include "utils.hpp" 9 | 10 | using std::string; 11 | using std::stringstream; 12 | using std::cerr; 13 | using std::endl; 14 | 15 | using namespace fr; 16 | 17 | int main(int argc, char *argv[]) { 18 | // Grab relevant command line arguments. 19 | if (argc < 3) { 20 | cerr << "Usage: " << argv[0] << " " << endl; 21 | exit(EXIT_FAILURE); 22 | } 23 | string config_file = ArgumentValue(argc, argv, 1); 24 | string scene_file = ArgumentValue(argc, argv, 2); 25 | 26 | uint32_t intervals = 10; 27 | { 28 | string intervals_str = FlagValue(argc, argv, "-i", "--intervals"); 29 | if (intervals_str != "") { 30 | stringstream stream(intervals_str); 31 | stream >> intervals; 32 | } 33 | } 34 | 35 | uint32_t jobs = 10; 36 | { 37 | string jobs_str = FlagValue(argc, argv, "-j", "--jobs"); 38 | if (jobs_str != "") { 39 | stringstream stream(jobs_str); 40 | stream >> jobs; 41 | } 42 | } 43 | 44 | int16_t offset = 0; 45 | { 46 | string offset_str = FlagValue(argc, argv, "-o", "--offset"); 47 | if (offset_str != "") { 48 | stringstream stream(offset_str); 49 | stream >> offset; 50 | } 51 | } 52 | 53 | uint16_t chunk_size = 0; 54 | { 55 | string chunk_size_str = FlagValue(argc, argv, "-c", "--chunk-size"); 56 | if (chunk_size_str != "") { 57 | stringstream stream(chunk_size_str); 58 | stream >> chunk_size; 59 | } 60 | } 61 | 62 | TOUTLN("Baseline starting."); 63 | 64 | EngineInit(config_file, scene_file, intervals, jobs, offset, chunk_size); 65 | EngineRun(); 66 | 67 | TOUTLN("Baseline done."); 68 | return EXIT_SUCCESS; 69 | } 70 | -------------------------------------------------------------------------------- /src/baseline/engine.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace fr { 7 | 8 | void EngineInit(const std::string& config_file, const std::string& scene_file, 9 | uint32_t intervals, uint32_t jobs, int16_t offset, uint16_t chunk_size); 10 | 11 | void EngineRun(); 12 | 13 | } // namespace fr 14 | -------------------------------------------------------------------------------- /src/render/engine.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace fr { 7 | 8 | void EngineInit(const std::string& config_file, const std::string& scene_file, 9 | uint32_t intervals, bool linear_scan); 10 | 11 | void EngineRun(); 12 | 13 | } // namespace fr 14 | -------------------------------------------------------------------------------- /src/render/render.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "engine.hpp" 8 | #include "utils.hpp" 9 | 10 | using std::string; 11 | using std::stringstream; 12 | using std::cerr; 13 | using std::endl; 14 | 15 | using namespace fr; 16 | 17 | int main(int argc, char *argv[]) { 18 | // Grab relevant command line arguments. 19 | if (argc < 3) { 20 | cerr << "Usage: " << argv[0] << " " << endl; 21 | exit(EXIT_FAILURE); 22 | } 23 | string config_file = ArgumentValue(argc, argv, 1); 24 | string scene_file = ArgumentValue(argc, argv, 2); 25 | 26 | uint32_t intervals = 10; 27 | { 28 | string intervals_str = FlagValue(argc, argv, "-i", "--intervals"); 29 | if (intervals_str != "") { 30 | stringstream stream(intervals_str); 31 | stream >> intervals; 32 | } 33 | } 34 | 35 | bool linear_scan = FlagExists(argc, argv, "-l", "--linear-scan"); 36 | 37 | TOUTLN("FlexRender starting."); 38 | 39 | EngineInit(config_file, scene_file, intervals, linear_scan); 40 | EngineRun(); 41 | 42 | TOUTLN("FlexRender done."); 43 | return EXIT_SUCCESS; 44 | } 45 | -------------------------------------------------------------------------------- /src/shared/scripting.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "scripting/script.hpp" 4 | #include "scripting/config_script.hpp" 5 | #include "scripting/scene_script.hpp" 6 | #include "scripting/shader_script.hpp" 7 | #include "scripting/texture_script.hpp" 8 | -------------------------------------------------------------------------------- /src/shared/scripting/config_script.cpp: -------------------------------------------------------------------------------- 1 | #include "scripting/config_script.hpp" 2 | 3 | #include 4 | 5 | #include "types/config.hpp" 6 | #include "utils/library.hpp" 7 | #include "utils/tout.hpp" 8 | 9 | using glm::vec2; 10 | using glm::vec3; 11 | 12 | namespace fr { 13 | 14 | using std::string; 15 | 16 | ConfigScript::ConfigScript() : 17 | Script(), 18 | _config(nullptr) { 19 | // Config files should have access to the entire standard library. 20 | FR_SCRIPT_INIT(ConfigScript, ScriptLibs::STANDARD_LIBS); 21 | 22 | // Register function handlers with the interpreter. 23 | FR_SCRIPT_REGISTER("network", ConfigScript, Network); 24 | FR_SCRIPT_REGISTER("output", ConfigScript, Output); 25 | FR_SCRIPT_REGISTER("render", ConfigScript, Render); 26 | } 27 | 28 | bool ConfigScript::Parse(const string& filename, Library *lib) { 29 | _config = new Config; 30 | 31 | // Evaluate the file. 32 | if (luaL_dofile(_state, filename.c_str())) { 33 | TERRLN(lua_tostring(_state, -1)); 34 | return false; 35 | } 36 | 37 | lib->StoreConfig(_config); 38 | _config = nullptr; 39 | 40 | return true; 41 | } 42 | 43 | FR_SCRIPT_FUNCTION(ConfigScript, Network) { 44 | BeginTableCall(); 45 | 46 | // "workers" is a required array of strings. 47 | if (!PushField("workers", LUA_TTABLE)) { 48 | ScriptError("network.workers is required"); 49 | } 50 | ForEachIndex([this](size_t index) { 51 | PushIndex(index, LUA_TSTRING); 52 | _config->workers.push_back(FetchString()); 53 | PopIndex(); 54 | }); 55 | PopField(); 56 | 57 | // "runaway" is an optional float 58 | if (PushField("runaway", LUA_TNUMBER)) { 59 | _config->runaway = FetchFloat(); 60 | } 61 | PopField(); 62 | 63 | EndTableCall(); 64 | return 0; 65 | } 66 | 67 | FR_SCRIPT_FUNCTION(ConfigScript, Output) { 68 | BeginTableCall(); 69 | 70 | // "name" is an optional string. 71 | if (PushField("name", LUA_TSTRING)) { 72 | _config->name = FetchString(); 73 | } 74 | PopField(); 75 | 76 | // "size" is an optional vec2 of int16. 77 | if (PushField("size", LUA_TTABLE)) { 78 | vec2 size = FetchFloat2(); 79 | _config->width = static_cast(size.x); 80 | _config->height = static_cast(size.y); 81 | } 82 | PopField(); 83 | 84 | // "buffers" is an optional array of strings 85 | if (PushField("buffers", LUA_TTABLE)) { 86 | ForEachIndex([this](size_t index) { 87 | PushIndex(index, LUA_TSTRING); 88 | _config->buffers.push_back(FetchString()); 89 | PopIndex(); 90 | }); 91 | } 92 | PopField(); 93 | 94 | EndTableCall(); 95 | return 0; 96 | } 97 | 98 | FR_SCRIPT_FUNCTION(ConfigScript, Render) { 99 | BeginTableCall(); 100 | 101 | // "antialiasing" is an optional uint16 102 | if (PushField("antialiasing", LUA_TNUMBER)) { 103 | _config->antialiasing = static_cast(FetchFloat()); 104 | } 105 | PopField(); 106 | 107 | // "samples" is an optional uint16 108 | if (PushField("samples", LUA_TNUMBER)) { 109 | _config->samples = static_cast(FetchFloat()); 110 | } 111 | PopField(); 112 | 113 | // "bounces" is an optional int16 114 | if (PushField("bounces", LUA_TNUMBER)) { 115 | _config->bounce_limit = static_cast(FetchFloat()); 116 | } 117 | PopField(); 118 | 119 | // "threshold" is an optional float 120 | if (PushField("threshold", LUA_TNUMBER)) { 121 | _config->transmittance_threshold = FetchFloat(); 122 | } 123 | PopField(); 124 | 125 | // "min" is a required float3 126 | if (!PushField("min", LUA_TTABLE)) { 127 | ScriptError("render.min is required"); 128 | } 129 | _config->min = FetchFloat3(); 130 | PopField(); 131 | 132 | // "max" is a required float3 133 | if (!PushField("max", LUA_TTABLE)) { 134 | ScriptError("render.max is required"); 135 | } 136 | _config->max = FetchFloat3(); 137 | PopField(); 138 | 139 | EndTableCall(); 140 | return 0; 141 | } 142 | 143 | } // namespace fr 144 | -------------------------------------------------------------------------------- /src/shared/scripting/config_script.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "scripting/script.hpp" 6 | 7 | namespace fr { 8 | 9 | class Library; 10 | struct Config; 11 | 12 | class ConfigScript : public Script { 13 | public: 14 | explicit ConfigScript(); 15 | 16 | /** 17 | * Parses the passed file as a config script and sets the passed config 18 | * object. If it succeeds, it returns true. If it fails, it returns 19 | * false. 20 | */ 21 | bool Parse(const std::string& filename, Library *lib); 22 | 23 | // Script function handlers. 24 | FR_SCRIPT_DECLARE(Network); 25 | FR_SCRIPT_DECLARE(Output); 26 | FR_SCRIPT_DECLARE(Render); 27 | 28 | private: 29 | Config* _config; 30 | }; 31 | 32 | } // namespace fr 33 | -------------------------------------------------------------------------------- /src/shared/scripting/scene_script.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "scripting/script.hpp" 6 | 7 | namespace fr { 8 | 9 | class Library; 10 | struct Mesh; 11 | 12 | class SceneScript : public Script { 13 | public: 14 | typedef uint32_t (*SyncCallback)(Mesh* mesh); 15 | 16 | explicit SceneScript(SyncCallback syncer); 17 | 18 | /** 19 | * Parses the given scene script and all of its resources (including 20 | * shaders, textures, mesh data, etc.) out to a library. Returns 21 | * true if it succeeded, false if it failed. 22 | */ 23 | bool Parse(const std::string& filename, Library *lib); 24 | 25 | // Script function handlers. 26 | FR_SCRIPT_DECLARE(Camera); 27 | FR_SCRIPT_DECLARE(Texture); 28 | FR_SCRIPT_DECLARE(Shader); 29 | FR_SCRIPT_DECLARE(Material); 30 | FR_SCRIPT_DECLARE(Mesh); 31 | FR_SCRIPT_DECLARE(Vertex); 32 | FR_SCRIPT_DECLARE(Triangle); 33 | 34 | inline uint64_t TotalVertices() const { return _total_verts; } 35 | inline uint64_t TotalFaces() const { return _total_faces; } 36 | 37 | private: 38 | Library* _lib; 39 | Mesh *_active_mesh; 40 | glm::vec3 _centroid_num; 41 | float _centroid_denom; 42 | SyncCallback _syncer; 43 | uint64_t _total_verts; 44 | uint64_t _total_faces; 45 | uint64_t _total_bytes; 46 | }; 47 | 48 | } // namespace fr 49 | -------------------------------------------------------------------------------- /src/shared/scripting/shader_script.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "glm/glm.hpp" 7 | 8 | #include "scripting/script.hpp" 9 | 10 | namespace fr { 11 | 12 | class Library; 13 | struct FatRay; 14 | struct WorkResults; 15 | 16 | class ShaderScript : public Script { 17 | public: 18 | explicit ShaderScript(const std::string& code, const Library *lib); 19 | 20 | /** 21 | * Runs the direct lighting function of the shader (if it exists) with the 22 | * given arguments, potentially appending buffer writes to the work 23 | * results. 24 | */ 25 | void Direct(const FatRay* ray, glm::vec3 hit, WorkResults* results); 26 | 27 | /** 28 | * Runs the indirect lighting function of the shader (if it exists) with 29 | * the given arguments, potentially appending buffer writes to the work 30 | * results. 31 | */ 32 | void Indirect(const FatRay* ray, glm::vec3 hit, WorkResults* results); 33 | 34 | /** 35 | * Runs the emissive lighting function of the shader (if it exists) with 36 | * the given arguments. Returns the emission of the surface. 37 | */ 38 | glm::vec3 Emissive(glm::vec2 texcoord); 39 | 40 | // Shader built-ins. 41 | FR_SCRIPT_DECLARE(Accumulate); 42 | FR_SCRIPT_DECLARE(Accumulate2); 43 | FR_SCRIPT_DECLARE(Accumulate3); 44 | FR_SCRIPT_DECLARE(Accumulate4); 45 | FR_SCRIPT_DECLARE(Write); 46 | FR_SCRIPT_DECLARE(Write2); 47 | FR_SCRIPT_DECLARE(Write3); 48 | FR_SCRIPT_DECLARE(Write4); 49 | FR_SCRIPT_DECLARE(Texture); 50 | FR_SCRIPT_DECLARE(Texture2); 51 | FR_SCRIPT_DECLARE(Texture3); 52 | FR_SCRIPT_DECLARE(Texture4); 53 | FR_SCRIPT_DECLARE(Trace); 54 | 55 | private: 56 | const Library* _lib; 57 | const FatRay* _ray; 58 | glm::vec3 _hit; 59 | WorkResults* _results; 60 | bool _has_direct; 61 | bool _has_indirect; 62 | bool _has_emissive; 63 | bool _has_vec2; 64 | bool _has_vec3; 65 | bool _has_vec4; 66 | sem_t _lock; 67 | }; 68 | 69 | } // namespace fr 70 | -------------------------------------------------------------------------------- /src/shared/scripting/texture_script.cpp: -------------------------------------------------------------------------------- 1 | #include "scripting/texture_script.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "types.hpp" 8 | #include "utils.hpp" 9 | 10 | using std::string; 11 | using glm::vec2; 12 | using glm::vec3; 13 | 14 | namespace fr { 15 | 16 | TextureScript::TextureScript(const string& code) : 17 | Script(), 18 | _has_vec2(false) { 19 | // TODO: Shader scripts shouldn't have access to the whole standard 20 | // library... 21 | FR_SCRIPT_INIT(TextureScript, ScriptLibs::STANDARD_LIBS); 22 | 23 | // Evaluate the shader. 24 | if (luaL_dostring(_state, code.c_str())) { 25 | TERRLN(lua_tostring(_state, -1)); 26 | exit(EXIT_FAILURE); 27 | } 28 | 29 | // Make sure the texture function exists. 30 | lua_getglobal(_state, "texture"); 31 | if (lua_isfunction(_state, -1) == 0) { 32 | TERRLN("Procedural textures must define a texture() function!"); 33 | exit(EXIT_FAILURE); 34 | } 35 | lua_pop(_state, 1); 36 | 37 | // Do they have local aliases for vec2's? If so, we can set vectors 38 | // metatables appropriately when we push arguments onto the stack. 39 | lua_getglobal(_state, "vec2"); 40 | _has_vec2 = lua_istable(_state, -1) != 0; 41 | lua_pop(_state, 1); 42 | 43 | // Initialize lock. 44 | if (sem_init(&_lock, 0, 1) < 0) { 45 | perror("sem_init"); 46 | exit(EXIT_FAILURE); 47 | } 48 | } 49 | 50 | float TextureScript::Evaluate(vec2 texcoord) { 51 | // Acquire the interpreter lock. 52 | if (sem_wait(&_lock) < 0) { 53 | perror("sem_wait"); 54 | exit(EXIT_FAILURE); 55 | } 56 | 57 | // Locate the function. 58 | lua_getglobal(_state, "texture"); 59 | 60 | // Push the arguments onto the stack. 61 | PushFloat2(texcoord); 62 | 63 | // Set metatables for arguments if we can. 64 | if (_has_vec2) { 65 | lua_getglobal(_state, "vec2"); 66 | lua_setmetatable(_state, -2); // texcoord (metatable is at -1) 67 | } 68 | 69 | // Call the function. 70 | CallFunc(1, 1); 71 | luaL_checktype(_state, -1, LUA_TNUMBER); 72 | float value = FetchFloat(); 73 | lua_pop(_state, 1); 74 | 75 | // Release the interpreter lock. 76 | if (sem_post(&_lock) < 0) { 77 | perror("sem_post"); 78 | exit(EXIT_FAILURE); 79 | } 80 | 81 | return value; 82 | } 83 | 84 | } // namespace fr 85 | -------------------------------------------------------------------------------- /src/shared/scripting/texture_script.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "glm/glm.hpp" 6 | 7 | #include "scripting/script.hpp" 8 | 9 | namespace fr { 10 | 11 | class TextureScript : public Script { 12 | public: 13 | explicit TextureScript(const std::string& code); 14 | 15 | float Evaluate(glm::vec2 texcoord); 16 | 17 | private: 18 | bool _has_vec2; 19 | sem_t _lock; 20 | }; 21 | 22 | } // namespace fr 23 | -------------------------------------------------------------------------------- /src/shared/types.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types/bounding_box.hpp" 4 | #include "types/buffer.hpp" 5 | #include "types/bvh.hpp" 6 | #include "types/camera.hpp" 7 | #include "types/config.hpp" 8 | #include "types/fat_ray.hpp" 9 | #include "types/hit_record.hpp" 10 | #include "types/image.hpp" 11 | #include "types/light_list.hpp" 12 | #include "types/linear_node.hpp" 13 | #include "types/linked_node.hpp" 14 | #include "types/local_geometry.hpp" 15 | #include "types/material.hpp" 16 | #include "types/mesh.hpp" 17 | #include "types/message.hpp" 18 | #include "types/net_node.hpp" 19 | #include "types/primitive_info.hpp" 20 | #include "types/render_stats.hpp" 21 | #include "types/shader.hpp" 22 | #include "types/slim_ray.hpp" 23 | #include "types/texture.hpp" 24 | #include "types/traversal_state.hpp" 25 | #include "types/traversal_stats.hpp" 26 | #include "types/triangle.hpp" 27 | #include "types/vertex.hpp" 28 | #include "types/work_results.hpp" 29 | -------------------------------------------------------------------------------- /src/shared/types/bounding_box.cpp: -------------------------------------------------------------------------------- 1 | #include "types/bounding_box.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "types/slim_ray.hpp" 10 | #include "utils/printers.hpp" 11 | 12 | using std::numeric_limits; 13 | using std::string; 14 | using std::stringstream; 15 | using std::endl; 16 | using std::swap; 17 | using glm::vec3; 18 | 19 | namespace fr { 20 | 21 | BoundingBox::BoundingBox(vec3 min, vec3 max) : 22 | min(min), 23 | max(max) {} 24 | 25 | BoundingBox::BoundingBox() { 26 | min.x = numeric_limits::infinity(); 27 | min.y = numeric_limits::infinity(); 28 | min.z = numeric_limits::infinity(); 29 | 30 | max.x = -numeric_limits::infinity(); 31 | max.y = -numeric_limits::infinity(); 32 | max.z = -numeric_limits::infinity(); 33 | } 34 | 35 | void BoundingBox::Absorb(vec3 point) { 36 | min.x = std::min(min.x, point.x); 37 | min.y = std::min(min.y, point.y); 38 | min.z = std::min(min.z, point.z); 39 | 40 | max.x = std::max(max.x, point.x); 41 | max.y = std::max(max.y, point.y); 42 | max.z = std::max(max.z, point.z); 43 | } 44 | 45 | BoundingBox BoundingBox::Union(const BoundingBox& other) const { 46 | BoundingBox result; 47 | 48 | result.min.x = std::min(min.x, other.min.x); 49 | result.min.y = std::min(min.y, other.min.y); 50 | result.min.z = std::min(min.z, other.min.z); 51 | 52 | result.max.x = std::max(max.x, other.max.x); 53 | result.max.y = std::max(max.y, other.max.y); 54 | result.max.z = std::max(max.z, other.max.z); 55 | 56 | return result; 57 | } 58 | 59 | float BoundingBox::SurfaceArea() const { 60 | vec3 d = max - min; 61 | return 2.0f * (d.x * d.y + d.x * d.z + d.y * d.z); 62 | } 63 | 64 | BoundingBox::Axis BoundingBox::LongestAxis() const { 65 | vec3 d = max - min; 66 | if (d.x > d.y && d.x > d.z) { 67 | return Axis::X; 68 | } else if (d.y > d.z) { 69 | return Axis::Y; 70 | } 71 | return Axis::Z; 72 | } 73 | 74 | bool BoundingBox::Intersect(const SlimRay& ray, vec3 inv_dir, float* t) const { 75 | // Check the X slab. 76 | float x_near = (min.x - ray.origin.x) * inv_dir.x; 77 | float x_far = (max.x - ray.origin.x) * inv_dir.x; 78 | if (x_near > x_far) swap(x_near, x_far); 79 | 80 | float t_min = x_near; 81 | float t_max = x_far; 82 | if (t_min > t_max) return false; 83 | 84 | // Check the Y slab. 85 | float y_near = (min.y - ray.origin.y) * inv_dir.y; 86 | float y_far = (max.y - ray.origin.y) * inv_dir.y; 87 | if (y_near > y_far) swap(y_near, y_far); 88 | 89 | t_min = y_near > t_min ? y_near : t_min; 90 | t_max = y_far < t_max ? y_far : t_max; 91 | if (t_min > t_max) return false; 92 | 93 | // Check the Z slab. 94 | float z_near = (min.z - ray.origin.z) * inv_dir.z; 95 | float z_far = (max.z - ray.origin.z) * inv_dir.z; 96 | if (z_near > z_far) swap(z_near, z_far); 97 | 98 | t_min = z_near > t_min ? z_near : t_min; 99 | t_max = z_far < t_max ? z_far : t_max; 100 | if (t_min > t_max) return false; 101 | 102 | if (t_max < 0.0f) return false; // Box behind the ray. 103 | 104 | *t = t_min; 105 | return true; 106 | } 107 | 108 | string ToString(const BoundingBox& box, const string& indent) { 109 | stringstream stream; 110 | stream << "BoundingBox {" << endl << 111 | indent << "| min = " << ToString(box.min) << endl << 112 | indent << "| max = " << ToString(box.max) << endl << 113 | indent << "}"; 114 | return stream.str(); 115 | } 116 | 117 | } // namespace fr 118 | -------------------------------------------------------------------------------- /src/shared/types/bounding_box.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "glm/glm.hpp" 7 | #include "msgpack.hpp" 8 | 9 | #include "utils/tostring.hpp" 10 | 11 | namespace fr { 12 | 13 | struct SlimRay; 14 | 15 | struct BoundingBox { 16 | enum Axis { 17 | NONE = 0, 18 | X = 1, 19 | Y = 2, 20 | Z = 3 21 | }; 22 | 23 | explicit BoundingBox(glm::vec3 min, glm::vec3 max); 24 | 25 | // FOR MSGPACK ONLY! 26 | explicit BoundingBox(); 27 | 28 | /// The minimum coordinate of the bounding box. 29 | glm::vec3 min; 30 | 31 | /// The maximum coordinate of the bounding box. 32 | glm::vec3 max; 33 | 34 | /// Expands the bounding box to include the given point within its extents. 35 | void Absorb(glm::vec3 point); 36 | 37 | /// Returns a new bounding box that encloses the space within this bounding 38 | /// box and the passed bounding box. 39 | BoundingBox Union(const BoundingBox& other) const; 40 | 41 | /// Computes the surface area of this bounding box. 42 | float SurfaceArea() const; 43 | 44 | /// Returns the longest axis of the bounding box. 45 | Axis LongestAxis() const; 46 | 47 | /// Returns true of the bounding box extents are valid. 48 | bool inline IsValid() const { 49 | return min.x <= max.x && min.y <= max.y && min.z <= max.z; 50 | } 51 | 52 | /// Performs an intersection check to see if the slim ray intersects 53 | /// the bounding box. 54 | bool Intersect(const SlimRay& ray, glm::vec3 inv_dir, float* t) const; 55 | 56 | MSGPACK_DEFINE(min, max); 57 | 58 | TOSTRINGABLE(BoundingBox); 59 | }; 60 | 61 | inline float AxisComponent(glm::vec3 vec, BoundingBox::Axis axis) { 62 | float value = std::numeric_limits::quiet_NaN(); 63 | 64 | switch (axis) { 65 | case BoundingBox::Axis::X: 66 | value = vec.x; 67 | break; 68 | 69 | case BoundingBox::Axis::Y: 70 | value = vec.y; 71 | break; 72 | 73 | case BoundingBox::Axis::Z: 74 | value = vec.z; 75 | break; 76 | 77 | default: 78 | break; 79 | } 80 | 81 | return value; 82 | } 83 | 84 | inline float AxisComponent(glm::vec3 vec, uint64_t axis) { 85 | return AxisComponent(vec, static_cast(axis)); 86 | } 87 | 88 | std::string ToString(const BoundingBox& box, const std::string& indent = ""); 89 | 90 | } // namespace fr 91 | -------------------------------------------------------------------------------- /src/shared/types/buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "types/buffer.hpp" 2 | 3 | #include 4 | 5 | using std::numeric_limits; 6 | 7 | namespace fr { 8 | 9 | Buffer::Buffer(int16_t width, int16_t height, float value) : 10 | _width(width), 11 | _height(height), 12 | _data(width * height, value) { 13 | 14 | } 15 | 16 | Buffer::Buffer() : 17 | _data() { 18 | _width = numeric_limits::min(); 19 | _height = numeric_limits::min(); 20 | } 21 | 22 | void Buffer::Merge(const Buffer& other) { 23 | for (size_t i = 0; i < other._data.size(); i++) { 24 | _data[i] += other._data[i]; 25 | } 26 | } 27 | 28 | } // namespace fr 29 | -------------------------------------------------------------------------------- /src/shared/types/buffer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "msgpack.hpp" 7 | 8 | namespace fr { 9 | 10 | class Buffer { 11 | friend class Image; 12 | 13 | public: 14 | explicit Buffer(int16_t width, int16_t height, float value = 0.0f); 15 | 16 | // FOR MSGPACK ONLY! 17 | explicit Buffer(); 18 | 19 | /// Merges the contents of the other buffer with this one. (Accumulate()'s 20 | /// pixel-wise.) 21 | void Merge(const Buffer& other); 22 | 23 | /// Overwrites the value at position in the buffer with the given 24 | /// one. 25 | inline void Write(int16_t x, int16_t y, float value) { 26 | size_t index = (y * _width) + x; 27 | _data[index] = value; 28 | } 29 | 30 | /// Accumulates the the given value with the current value in the buffer 31 | /// at position . 32 | inline void Accumulate(int16_t x, int16_t y, float value) { 33 | size_t index = (y * _width) + x; 34 | _data[index] += value; 35 | } 36 | 37 | MSGPACK_DEFINE(_width, _height, _data); 38 | 39 | private: 40 | int16_t _width; 41 | int16_t _height; 42 | std::vector _data; 43 | }; 44 | 45 | } // namespace fr 46 | -------------------------------------------------------------------------------- /src/shared/types/buffer_op.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace fr { 7 | 8 | struct BufferOp { 9 | enum class Kind { 10 | WRITE, 11 | ACCUMULATE 12 | }; 13 | 14 | explicit BufferOp(Kind kind, const std::string& buffer, int16_t x, int16_t y, 15 | float value) : 16 | kind(kind), 17 | buffer(buffer), 18 | x(x), 19 | y(y), 20 | value(value) {} 21 | 22 | /// The kind of buffer operation we're doing. 23 | Kind kind; 24 | 25 | /// The buffer we're writing into. 26 | std::string buffer; 27 | 28 | /// The x coordinate of the pixel. 29 | int16_t x; 30 | 31 | /// The y coordinate of the pixel. 32 | int16_t y; 33 | 34 | /// The value we're writing. 35 | float value; 36 | }; 37 | 38 | } // namespace fr 39 | -------------------------------------------------------------------------------- /src/shared/types/bvh.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "glm/glm.hpp" 9 | #include "msgpack.hpp" 10 | 11 | #include "types/linear_node.hpp" 12 | #include "types/traversal_state.hpp" 13 | #include "utils/tostring.hpp" 14 | 15 | namespace fr { 16 | 17 | struct Mesh; 18 | struct SlimRay; 19 | struct LinkedNode; 20 | struct PrimitiveInfo; 21 | struct HitRecord; 22 | 23 | /** 24 | * The construction implementation is based on the one presented in Physically 25 | * Based Rendering, Section 4.4, pages 208-227, with some modifications to 26 | * support stackless traversal. The stackless traversal algorithm is as 27 | * described in Hapala et al [2011], with modifications for suspension on one 28 | * worker and resuming on another without a restart. 29 | */ 30 | 31 | class BVH { 32 | public: 33 | /** 34 | * Constructs a BVH for traversing the given mesh. 35 | */ 36 | explicit BVH(const Mesh* mesh); 37 | 38 | /** 39 | * Constructs a BVH for traversing a set of things, where those things 40 | * are pairs of resource IDs and their bounding boxes. 41 | */ 42 | explicit BVH(const std::vector>& things); 43 | 44 | /// MSGPACK ONLY! 45 | explicit BVH(); 46 | 47 | /** 48 | * Traverses the BVH by testing the given SlimRay against the bounding 49 | * volumes. If a leaf node is hit, the passed primitive intersector 50 | * function will be called. Returns the current traversal state when the 51 | * function exits. 52 | */ 53 | TraversalState Traverse(const SlimRay& ray, HitRecord* nearest, 54 | std::function intersector); 55 | 56 | /** 57 | * Also traverses the BVH, but resumes traversal where we left off using 58 | * the given TraversalState packet. Returns the current traversal state 59 | * when the function exits. 60 | */ 61 | TraversalState Traverse(TraversalState state, const SlimRay& ray, HitRecord* nearest, 62 | std::function intersector, bool resume = true); 63 | 64 | /** 65 | * Returns the extents of the area contained by the BVH. 66 | */ 67 | inline BoundingBox Extents() const { 68 | return _nodes[0].bounds; 69 | } 70 | 71 | inline uint64_t GetSizeInBytes() const { return _nodes.size() * sizeof(LinearNode); } 72 | inline float GetSizeInMB() const { return (_nodes.size() * sizeof(LinearNode)) / (1024.0f * 1024.0f); } 73 | 74 | MSGPACK_DEFINE(_nodes); 75 | 76 | TOSTRINGABLEBYPTR(BVH); 77 | 78 | private: 79 | struct BucketInfo { 80 | uint32_t count; 81 | BoundingBox bounds; 82 | }; 83 | 84 | static const uint32_t NUM_BUCKETS; 85 | 86 | std::vector _nodes; 87 | 88 | /** 89 | * Constructs the BVH from the given initialized build data. 90 | */ 91 | void Build(std::vector& build_data); 92 | 93 | /** 94 | * Recursively partitions and builds the BVH for the given build data 95 | * between the start and end indexes. Returns the total nodes created 96 | * through the passed pointer. 97 | */ 98 | LinkedNode* RecursiveBuild(std::vector& build_data, 99 | size_t start, size_t end, size_t* total_nodes); 100 | 101 | /** 102 | * Computes the minimum cost split for the build_data given num_buckets 103 | * possible candidate splits and the centroid bounding box. 104 | */ 105 | uint32_t ComputeSAH(std::vector& build_data, size_t start, 106 | size_t end, float min, float max, float surface_area, BoundingBox::Axis axis); 107 | 108 | /** 109 | * Flattens the linked tree structure into a linear structure with offsets 110 | * for faster/portable traversal at runtime. 111 | */ 112 | size_t FlattenTree(LinkedNode* current, size_t parent, size_t* offset); 113 | 114 | /** 115 | * Recursively deletes a LinkedNode tree structure rooted at node. 116 | */ 117 | void DeleteLinked(LinkedNode* node); 118 | 119 | /// Special case constructor for building a tree with nothing in it. 120 | void ZeroThings(); 121 | 122 | /// Special case constructor for building a tree with one thing in it. 123 | void OneThing(uint32_t id, const BoundingBox& bounds); 124 | 125 | /// Returns the index of the sibling of the current node. 126 | inline size_t Sibling(size_t current) { 127 | size_t parent = _nodes[current].parent; 128 | size_t right = _nodes[parent].right; 129 | return (right == current) ? parent + 1 : right; 130 | } 131 | 132 | /// The near child is defined to be the left-hand child. 133 | inline size_t NearChild(size_t current, glm::vec3 direction) { 134 | float axis_component = AxisComponent(direction, _nodes[current].axis); 135 | return axis_component < 0.0f ? _nodes[current].right : current + 1; 136 | } 137 | 138 | /// The far child is defined to be the right-hand child. 139 | inline size_t FarChild(size_t current, glm::vec3 direction) { 140 | float axis_component = AxisComponent(direction, _nodes[current].axis); 141 | return axis_component < 0.0f ? current + 1 : _nodes[current].right; 142 | } 143 | 144 | /// Performs a quick bounding box check against the given bounds and ray. 145 | inline bool BoundingHit(const BoundingBox& bounds, const SlimRay& ray, 146 | glm::vec3 inv_dir, float max) { 147 | if (!bounds.IsValid()) return false; 148 | float t = -1.0f; 149 | return bounds.Intersect(ray, inv_dir, &t) && t < max; 150 | } 151 | }; 152 | 153 | std::string ToString(const BVH* bvh, const std::string& indent = ""); 154 | 155 | } // namespace fr 156 | -------------------------------------------------------------------------------- /src/shared/types/camera.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "glm/glm.hpp" 6 | #include "msgpack.hpp" 7 | 8 | #include "utils/tostring.hpp" 9 | 10 | namespace fr { 11 | 12 | struct Config; 13 | struct FatRay; 14 | 15 | struct Camera { 16 | explicit Camera(const Config* config); 17 | 18 | // FOR MSGPACK ONLY! 19 | explicit Camera(); 20 | 21 | /// The eye position. 22 | glm::vec3 eye; 23 | 24 | /// The "look at" position. 25 | glm::vec3 look; 26 | 27 | /// The world "up" vector. 28 | glm::vec3 up; 29 | 30 | /// The amount of rotation (counter-clockwise) about the gaze vector (in 31 | /// radians). 32 | float rotation; 33 | 34 | /// The aspect ratio of the camera. 35 | float ratio; 36 | 37 | inline void SetConfig(const Config* config) { _config = config; } 38 | 39 | inline void SetRange(int16_t offset, uint16_t chunk_size) { 40 | _offset = offset; 41 | _chunk_size = chunk_size; 42 | _x = offset; 43 | _end = offset + chunk_size; 44 | } 45 | 46 | /** 47 | * Generates a single primary ray based on the passed config and the 48 | * camera settings. Call this successively to keep generating primary rays. 49 | * Once all rays have been generated, returns false and the contents of ray 50 | * are undefined. 51 | */ 52 | bool GeneratePrimary(FatRay* ray); 53 | 54 | inline float Progress() const { return _progress; } 55 | 56 | MSGPACK_DEFINE(eye, look, up, rotation, ratio); 57 | 58 | TOSTRINGABLE(Camera); 59 | 60 | private: 61 | const Config* _config; 62 | int16_t _x; 63 | int16_t _y; 64 | uint16_t _i; 65 | uint16_t _j; 66 | int16_t _end; 67 | int16_t _offset; 68 | uint16_t _chunk_size; 69 | float _l; 70 | float _t; 71 | glm::vec3 _u, _v, _w; 72 | bool _initialized; 73 | float _progress; 74 | struct timespec _last_gen_time; 75 | 76 | bool ReadyToCast(); 77 | }; 78 | 79 | std::string ToString(const Camera& camera, const std::string& indent = ""); 80 | 81 | } // namespace fr 82 | -------------------------------------------------------------------------------- /src/shared/types/config.cpp: -------------------------------------------------------------------------------- 1 | #include "types/config.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "utils/printers.hpp" 8 | 9 | using std::numeric_limits; 10 | using std::string; 11 | using std::stringstream; 12 | using std::endl; 13 | 14 | namespace fr { 15 | 16 | Config::Config() : 17 | width(640), 18 | height(480), 19 | antialiasing(0), 20 | samples(10), 21 | bounce_limit(5), 22 | transmittance_threshold(0.0f), 23 | runaway(2.5f), 24 | name("output"), 25 | workers(), 26 | buffers() { 27 | min.x = numeric_limits::quiet_NaN(); 28 | min.y = numeric_limits::quiet_NaN(); 29 | min.z = numeric_limits::quiet_NaN(); 30 | 31 | max.x = numeric_limits::quiet_NaN(); 32 | max.y = numeric_limits::quiet_NaN(); 33 | max.z = numeric_limits::quiet_NaN(); 34 | } 35 | 36 | string ToString(const Config& config, const string& indent) { 37 | stringstream stream; 38 | string pad = indent + "| | "; 39 | stream << "Config {" << endl << 40 | indent << "| width = " << config.width << endl << 41 | indent << "| height = " << config.height << endl << 42 | indent << "| min = " << ToString(config.min) << endl << 43 | indent << "| max = " << ToString(config.max) << endl << 44 | indent << "| antialiasing = " << config.antialiasing << endl << 45 | indent << "| samples = " << config.samples << endl << 46 | indent << "| bounce_limit = " << config.bounce_limit << endl << 47 | indent << "| transmittance_threshold = " << config.transmittance_threshold << endl << 48 | indent << "| runaway = " << config.runaway << endl << 49 | indent << "| name = " << config.name << endl << 50 | indent << "| workers = {" << endl; 51 | for (const auto& worker : config.workers) { 52 | stream << pad << worker << endl; 53 | } 54 | stream << indent << "| }" << endl << 55 | indent << "| buffers = {" << endl; 56 | for (const auto& buffer : config.buffers) { 57 | stream << pad << buffer << endl; 58 | } 59 | stream << indent << "| }" << endl << 60 | indent << "}" << endl; 61 | return stream.str(); 62 | } 63 | 64 | } // namespace fr 65 | -------------------------------------------------------------------------------- /src/shared/types/config.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "glm/glm.hpp" 7 | #include "msgpack.hpp" 8 | 9 | #include "utils/tostring.hpp" 10 | 11 | namespace fr { 12 | 13 | struct Config { 14 | explicit Config(); 15 | 16 | /// Width of the rendered image. 17 | int16_t width; 18 | 19 | /// Height of the rendered image. 20 | int16_t height; 21 | 22 | /// The minimum bounds of the scene. 23 | glm::vec3 min; 24 | 25 | /// The maximum bounds of the scene. 26 | glm::vec3 max; 27 | 28 | /// The antialiasing grid size (for stratified supersampling). 29 | uint16_t antialiasing; 30 | 31 | /// The number of samples per light (one triangle is one light). 32 | uint16_t samples; 33 | 34 | /// The maximum number of bounces before a ray is considered dead. 35 | int16_t bounce_limit; 36 | 37 | /// The threshold below which we consider the transmittance of a ray 0. 38 | float transmittance_threshold; 39 | 40 | /// The maximum amount (in percent, 0.0f -> 100.0f) that any worker is 41 | /// allowed to get ahead of the slowest worker in generating primary rays. 42 | float runaway; 43 | 44 | /// Name of the scene. 45 | std::string name; 46 | 47 | /// List of the workers involved. 48 | std::vector workers; 49 | 50 | /// List of auxiliary render buffers (you get "R", "G", and "B" for free). 51 | std::vector buffers; 52 | 53 | MSGPACK_DEFINE(width, height, min, max, antialiasing, samples, bounce_limit, 54 | transmittance_threshold, runaway, name, workers, buffers); 55 | 56 | TOSTRINGABLE(Config); 57 | }; 58 | 59 | std::string ToString(const Config& config, const std::string& indent = ""); 60 | 61 | } // namespace fr 62 | -------------------------------------------------------------------------------- /src/shared/types/fat_ray.cpp: -------------------------------------------------------------------------------- 1 | #include "types/fat_ray.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "utils/printers.hpp" 8 | 9 | using std::numeric_limits; 10 | using std::string; 11 | using std::stringstream; 12 | using std::endl; 13 | using std::hex; 14 | using std::showbase; 15 | using glm::vec3; 16 | using glm::vec4; 17 | 18 | namespace fr { 19 | 20 | FatRay::FatRay(Kind kind, int16_t x, int16_t y) : 21 | kind(kind), 22 | x(x), 23 | y(y), 24 | slim(), 25 | traversal(), 26 | hit(), 27 | current_worker(0), 28 | workers_touched(1), 29 | next(nullptr) { 30 | bounces = numeric_limits::min(); 31 | 32 | transmittance = numeric_limits::quiet_NaN(); 33 | 34 | emission.x = numeric_limits::quiet_NaN(); 35 | emission.y = numeric_limits::quiet_NaN(); 36 | emission.z = numeric_limits::quiet_NaN(); 37 | 38 | target.x = numeric_limits::quiet_NaN(); 39 | target.y = numeric_limits::quiet_NaN(); 40 | target.z = numeric_limits::quiet_NaN(); 41 | } 42 | 43 | FatRay::FatRay(Kind kind) : 44 | kind(kind), 45 | slim(), 46 | traversal(), 47 | hit(), 48 | current_worker(0), 49 | workers_touched(1), 50 | next(nullptr) { 51 | x = numeric_limits::min(); 52 | y = numeric_limits::min(); 53 | 54 | bounces = numeric_limits::min(); 55 | 56 | transmittance = numeric_limits::quiet_NaN(); 57 | 58 | emission.x = numeric_limits::quiet_NaN(); 59 | emission.y = numeric_limits::quiet_NaN(); 60 | emission.z = numeric_limits::quiet_NaN(); 61 | 62 | target.x = numeric_limits::quiet_NaN(); 63 | target.y = numeric_limits::quiet_NaN(); 64 | target.z = numeric_limits::quiet_NaN(); 65 | } 66 | 67 | FatRay::FatRay() : 68 | kind(Kind::NONE), 69 | slim(), 70 | traversal(), 71 | hit(), 72 | current_worker(0), 73 | workers_touched(1), 74 | next(nullptr) { 75 | x = numeric_limits::min(); 76 | y = numeric_limits::min(); 77 | 78 | bounces = numeric_limits::min(); 79 | 80 | transmittance = numeric_limits::quiet_NaN(); 81 | 82 | emission.x = numeric_limits::quiet_NaN(); 83 | emission.y = numeric_limits::quiet_NaN(); 84 | emission.z = numeric_limits::quiet_NaN(); 85 | 86 | target.x = numeric_limits::quiet_NaN(); 87 | target.y = numeric_limits::quiet_NaN(); 88 | target.z = numeric_limits::quiet_NaN(); 89 | } 90 | 91 | string ToString(const FatRay& ray, const string& indent) { 92 | stringstream stream; 93 | string pad = indent + "| "; 94 | stream << "FatRay {" << endl; 95 | switch (ray.kind) { 96 | case FatRay::Kind::NONE: 97 | stream << indent << "| kind = NONE" << endl; 98 | break; 99 | 100 | case FatRay::Kind::INTERSECT: 101 | stream << indent << "| kind = INTERSECT" << endl << 102 | indent << "| x = " << ray.x << endl << 103 | indent << "| y = " << ray.y << endl << 104 | indent << "| bounces = " << ray.bounces << endl << 105 | indent << "| slim = " << ToString(ray.slim, pad) << endl << 106 | indent << "| transmittance = " << ray.transmittance << endl << 107 | indent << "| traversal = " << ToString(ray.traversal, pad) << endl << 108 | indent << "| hit = " << ToString(ray.hit, pad) << endl << 109 | indent << "| current_worker = " << ray.current_worker << endl << 110 | indent << "| workers_touched = " << ray.workers_touched << endl << 111 | indent << "| next = " << hex << showbase << ray.next << endl; 112 | break; 113 | 114 | case FatRay::Kind::ILLUMINATE: 115 | stream << indent << "| kind = ILLUMINATE" << endl << 116 | indent << "| x = " << ray.x << endl << 117 | indent << "| y = " << ray.y << endl << 118 | indent << "| bounces = " << ray.bounces << endl << 119 | indent << "| slim = " << ToString(ray.slim, pad) << endl << 120 | indent << "| transmittance = " << ray.transmittance << endl << 121 | indent << "| traversal = " << ToString(ray.traversal, pad) << endl << 122 | indent << "| hit = " << ToString(ray.hit, pad) << endl << 123 | indent << "| current_worker = " << ray.current_worker << endl << 124 | indent << "| workers_touched = " << ray.workers_touched << endl << 125 | indent << "| next = " << hex << showbase << ray.next << endl; 126 | break; 127 | 128 | case FatRay::Kind::LIGHT: 129 | stream << indent << "| kind = LIGHT" << endl << 130 | indent << "| x = " << ray.x << endl << 131 | indent << "| y = " << ray.y << endl << 132 | indent << "| slim = " << ToString(ray.slim, pad) << endl << 133 | indent << "| transmittance = " << ray.transmittance << endl << 134 | indent << "| emission = " << ToString(ray.emission) << endl << 135 | indent << "| target = " << ToString(ray.target) << endl << 136 | indent << "| traversal = " << ToString(ray.traversal, pad) << endl << 137 | indent << "| hit = " << ToString(ray.hit, pad) << endl << 138 | indent << "| current_worker = " << ray.current_worker << endl << 139 | indent << "| workers_touched = " << ray.workers_touched << endl << 140 | indent << "| next = " << hex << showbase << ray.next << endl; 141 | break; 142 | } 143 | stream << indent << "}"; 144 | return stream.str(); 145 | } 146 | 147 | } // namespace fr 148 | -------------------------------------------------------------------------------- /src/shared/types/fat_ray.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "glm/glm.hpp" 6 | 7 | #include "types/hit_record.hpp" 8 | #include "types/mesh.hpp" 9 | #include "types/slim_ray.hpp" 10 | #include "types/traversal_state.hpp" 11 | #include "utils/tostring.hpp" 12 | 13 | namespace fr { 14 | 15 | struct FatRay { 16 | enum Kind { 17 | NONE = 0, 18 | INTERSECT = 1, 19 | ILLUMINATE = 2, 20 | LIGHT = 3 21 | }; 22 | 23 | explicit FatRay(Kind kind, int16_t x, int16_t y); 24 | 25 | explicit FatRay(Kind kind); 26 | 27 | explicit FatRay(); 28 | 29 | /// The kind of ray (from the above possible). 30 | uint16_t kind; 31 | 32 | /// The x component of the source pixel. 33 | int16_t x; 34 | 35 | /// The y component of the source pixel. 36 | int16_t y; 37 | 38 | /// The number of times this ray has bounced (zero is primary). 39 | int16_t bounces; 40 | 41 | /// The embedded slim ray (origin and direction). 42 | SlimRay slim; 43 | 44 | /// The transmittance of light along this ray. Range is not enforced. 45 | float transmittance; 46 | 47 | /// The light emission along this ray, not affected by transmittance. 48 | glm::vec3 emission; 49 | 50 | /// The target intersection point in world space. 51 | glm::vec3 target; 52 | 53 | /// State of the acceleration structure traversal. 54 | TraversalState traversal; 55 | 56 | /// The hit record of the closest intersection so far. 57 | HitRecord hit; 58 | 59 | /// The current worker this ray is destined for. 60 | uint32_t current_worker; 61 | 62 | /// The number of workers this ray has touched for far. Purely for analysis. 63 | uint32_t workers_touched; 64 | 65 | /// Next pointer for chaining rays together. Obviously not valid once the 66 | /// ray has been sent over the network. 67 | FatRay* next; 68 | 69 | /// Returns a skinny ray that represents this fat ray transformed into 70 | /// object space of the given mesh. 71 | inline SlimRay TransformTo(const Mesh* mesh) const { 72 | return slim.TransformTo(mesh->xform_inv); 73 | } 74 | 75 | /// Evaluate a point along the ray at a specific t value. 76 | inline glm::vec3 EvaluateAt(float t) const { 77 | return slim.EvaluateAt(t); 78 | } 79 | 80 | TOSTRINGABLE(FatRay); 81 | }; 82 | 83 | std::string ToString(const FatRay& ray, const std::string& indent = ""); 84 | 85 | } // namespace fr 86 | -------------------------------------------------------------------------------- /src/shared/types/hit_record.cpp: -------------------------------------------------------------------------------- 1 | #include "types/hit_record.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using std::numeric_limits; 8 | using std::string; 9 | using std::stringstream; 10 | using std::endl; 11 | 12 | namespace fr { 13 | 14 | HitRecord::HitRecord(uint32_t worker, uint32_t mesh) : 15 | worker(worker), 16 | mesh(mesh), 17 | geom() { 18 | t = numeric_limits::infinity(); 19 | } 20 | 21 | HitRecord::HitRecord(uint32_t worker, uint32_t mesh, float t) : 22 | worker(worker), 23 | mesh(mesh), 24 | t(t), 25 | geom() {} 26 | 27 | HitRecord::HitRecord() : 28 | worker(0), 29 | mesh(0), 30 | geom() { 31 | t = numeric_limits::infinity(); 32 | } 33 | 34 | string ToString(const HitRecord& strong, const string& indent) { 35 | stringstream stream; 36 | string pad = indent + "| "; 37 | stream << "HitRecord {" << endl << 38 | indent << "| worker = " << strong.worker << endl << 39 | indent << "| mesh = " << strong.mesh << endl << 40 | indent << "| t = " << strong.t << endl << 41 | indent << "| geom = " << ToString(strong.geom, pad) << 42 | indent << "}"; 43 | return stream.str(); 44 | } 45 | 46 | } // namespace fr 47 | -------------------------------------------------------------------------------- /src/shared/types/hit_record.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "types/local_geometry.hpp" 6 | #include "utils/tostring.hpp" 7 | 8 | namespace fr { 9 | 10 | struct HitRecord { 11 | explicit HitRecord(uint32_t worker, uint32_t mesh); 12 | 13 | explicit HitRecord(uint32_t worker, uint32_t mesh, float t); 14 | 15 | explicit HitRecord(); 16 | 17 | /// Resource ID of worker this hit occurred on. 18 | uint32_t worker; 19 | 20 | /// Resource ID of the mesh this hit occurred on. 21 | uint32_t mesh; 22 | 23 | /// The t value of the intersection. 24 | float t; 25 | 26 | /// The local geometry at the point of intersection. 27 | LocalGeometry geom; 28 | 29 | TOSTRINGABLE(HitRecord); 30 | }; 31 | 32 | std::string ToString(const HitRecord& strong, const std::string& indent = ""); 33 | 34 | } // namespace fr 35 | -------------------------------------------------------------------------------- /src/shared/types/image.cpp: -------------------------------------------------------------------------------- 1 | #include "types/image.hpp" 2 | 3 | #include 4 | 5 | #include "OpenEXR/ImfOutputFile.h" 6 | #include "OpenEXR/ImfChannelList.h" 7 | 8 | using std::numeric_limits; 9 | using std::string; 10 | 11 | namespace fr { 12 | 13 | Image::Image(int16_t width, int16_t height) : 14 | _width(width), 15 | _height(height), 16 | _buffers() { 17 | AddBuffer("R"); 18 | AddBuffer("G"); 19 | AddBuffer("B"); 20 | } 21 | 22 | Image::Image() : 23 | _buffers() { 24 | _width = numeric_limits::min(); 25 | _height = numeric_limits::min(); 26 | } 27 | 28 | void Image::AddBuffer(const string& name) { 29 | _buffers[name] = Buffer(_width, _height, 0.0f); 30 | } 31 | 32 | void Image::Merge(const Image* other) { 33 | for (const auto& kv_pair : other->_buffers) { 34 | _buffers[kv_pair.first].Merge(kv_pair.second); 35 | } 36 | } 37 | 38 | void Image::ToEXRFile(const string& filename) const { 39 | // Create the header and channel list. 40 | Imf::Header header(_width, _height); 41 | for (const auto& kv_pair : _buffers) { 42 | const auto& name = kv_pair.first; 43 | header.channels().insert(name.c_str(), Imf::Channel(Imf::FLOAT)); 44 | } 45 | 46 | // Create the output file and frame buffer. 47 | Imf::OutputFile file(filename.c_str(), header); 48 | Imf::FrameBuffer frame; 49 | 50 | // Set up the memory layout. 51 | for (const auto& kv_pair : _buffers) { 52 | const auto& name = kv_pair.first; 53 | const auto& buffer = kv_pair.second; 54 | frame.insert(name.c_str(), Imf::Slice(Imf::FLOAT, 55 | const_cast(reinterpret_cast(&(buffer._data[0]))), 56 | sizeof(float), _width * sizeof(float))); 57 | } 58 | 59 | // Write it out. 60 | file.setFrameBuffer(frame); 61 | file.writePixels(_height); 62 | } 63 | 64 | } // namespace fr 65 | -------------------------------------------------------------------------------- /src/shared/types/image.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "types/buffer.hpp" 8 | 9 | namespace fr { 10 | 11 | class Image { 12 | public: 13 | explicit Image(int16_t width, int16_t height); 14 | 15 | // FOR MSGPACK ONLY! 16 | explicit Image(); 17 | 18 | /// Adds a new buffer to the image with the given name. 19 | void AddBuffer(const std::string& name); 20 | 21 | /// Merges the other image with this one by folding each buffer into its 22 | /// corresponding buffer using accumulation. 23 | void Merge(const Image* other); 24 | 25 | /// Overwrites the value at location with the given value in the 26 | /// named buffer. 27 | inline void Write(const std::string& buffer, int16_t x, int16_t y, float value) { 28 | _buffers[buffer].Write(x, y, value); 29 | } 30 | 31 | /// Accumulates the given value with the existing value at location 32 | /// in the named buffer. 33 | inline void Accumulate(const std::string& buffer, int16_t x, int16_t y, float value) { 34 | _buffers[buffer].Accumulate(x, y, value); 35 | } 36 | 37 | /// Dumps all the buffers out to an EXR file. 38 | void ToEXRFile(const std::string& filename) const; 39 | 40 | MSGPACK_DEFINE(_width, _height, _buffers); 41 | 42 | private: 43 | int16_t _width; 44 | int16_t _height; 45 | std::map _buffers; 46 | }; 47 | 48 | } // namespace fr 49 | -------------------------------------------------------------------------------- /src/shared/types/light_list.cpp: -------------------------------------------------------------------------------- 1 | #include "types/light_list.hpp" 2 | 3 | #include 4 | 5 | using std::function; 6 | using std::find; 7 | 8 | namespace fr { 9 | 10 | LightList::LightList() : 11 | _workers() {} 12 | 13 | void LightList::AddEmissiveWorker(uint32_t id) { 14 | auto existing = find(_workers.begin(), _workers.end(), id); 15 | if (existing == _workers.end()) { 16 | _workers.push_back(id); 17 | } 18 | } 19 | 20 | void LightList::ForEachEmissiveWorker(function func) { 21 | for (auto id : _workers) { 22 | func(id); 23 | } 24 | } 25 | 26 | } // namespace fr 27 | -------------------------------------------------------------------------------- /src/shared/types/light_list.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "msgpack.hpp" 8 | 9 | namespace fr { 10 | 11 | class LightList { 12 | public: 13 | explicit LightList(); 14 | 15 | void AddEmissiveWorker(uint32_t id); 16 | 17 | void ForEachEmissiveWorker(std::function func); 18 | 19 | MSGPACK_DEFINE(_workers); 20 | 21 | private: 22 | std::vector _workers; 23 | }; 24 | 25 | } // namespace fr 26 | -------------------------------------------------------------------------------- /src/shared/types/linear_node.cpp: -------------------------------------------------------------------------------- 1 | #include "types/linear_node.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using std::numeric_limits; 8 | using std::string; 9 | using std::stringstream; 10 | using std::endl; 11 | 12 | namespace fr { 13 | 14 | LinearNode::LinearNode() : 15 | bounds(), 16 | parent(-1), 17 | right(-1) { 18 | leaf = numeric_limits::max(); 19 | index = numeric_limits::max(); 20 | axis = numeric_limits::max(); 21 | } 22 | 23 | string ToString(const LinearNode& node, const string& indent) { 24 | stringstream stream; 25 | string pad = indent + "| "; 26 | stream << "LinearNode {" << endl << 27 | indent << "| bounds = " << ToString(node.bounds, pad) << endl << 28 | indent << "| leaf = " << node.leaf << endl << 29 | indent << "| index = " << node.index << endl << 30 | indent << "| parent = " << node.parent << endl << 31 | indent << "| right = " << node.right << endl << 32 | indent << "| axis = " << node.axis << endl << 33 | indent << "}"; 34 | return stream.str(); 35 | } 36 | 37 | } // namespace fr 38 | -------------------------------------------------------------------------------- /src/shared/types/linear_node.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "msgpack.hpp" 7 | 8 | #include "types/bounding_box.hpp" 9 | #include "utils/tostring.hpp" 10 | 11 | namespace fr { 12 | 13 | struct LinearNode { 14 | explicit LinearNode(); 15 | 16 | /// The bounding box of this node in world space. 17 | BoundingBox bounds; 18 | 19 | /// Boolean flag that indicates if this is a leaf node or interior node. 20 | /// Yes, it is intentionally this big to pad the entire structure to fit 21 | /// in a cache line (64 bytes). 22 | uint64_t leaf; 23 | 24 | /// Index of the primitive, if this is a leaf node. 25 | size_t index; 26 | 27 | /// Offset of the parent of this node if this is an interior node. 28 | ssize_t parent; 29 | 30 | /// Offset of the right-hand child of this node if this is an interior node. 31 | ssize_t right; 32 | 33 | /// Axis that we split on. Yes, it is intentionally this big to pad the 34 | /// entire structure to fit in a cache line (64 bytes). 35 | uint64_t axis; 36 | 37 | MSGPACK_DEFINE(bounds, leaf, index, parent, right, axis); 38 | 39 | TOSTRINGABLE(LinearNode); 40 | }; 41 | 42 | std::string ToString(const LinearNode& node, const std::string& indent = ""); 43 | 44 | } // namespace fr 45 | -------------------------------------------------------------------------------- /src/shared/types/linked_node.cpp: -------------------------------------------------------------------------------- 1 | #include "types/linked_node.hpp" 2 | 3 | #include 4 | #include 5 | 6 | using std::string; 7 | using std::stringstream; 8 | using std::endl; 9 | 10 | namespace fr { 11 | 12 | LinkedNode::LinkedNode(size_t index, const BoundingBox& bounds) : 13 | bounds(bounds), 14 | index(index) { 15 | children[0] = nullptr; 16 | children[1] = nullptr; 17 | } 18 | 19 | LinkedNode::LinkedNode(LinkedNode* left, LinkedNode* right, 20 | BoundingBox::Axis split) : 21 | index(0), 22 | split(split) { 23 | assert(left != nullptr); 24 | assert(right != nullptr); 25 | 26 | children[0] = left; 27 | children[1] = right; 28 | 29 | bounds = left->bounds.Union(right->bounds); 30 | } 31 | 32 | string ToString(const LinkedNode* node, const string& indent) { 33 | stringstream stream; 34 | string pad = indent + "| "; 35 | stream << "LinkedNode {" << endl << 36 | indent << "| bounds = " << ToString(node->bounds, pad) << endl; 37 | if (node->children[0] != nullptr) { 38 | // Interior node. 39 | stream << indent << "| split = " << 40 | ((node->split == BoundingBox::Axis::X) ? "X" : 41 | ((node->split == BoundingBox::Axis::Y) ? "Y" : "Z")) << endl << 42 | indent << "| children[0] = " << ToString(node->children[0], pad) << endl << 43 | indent << "| children[1] = " << ToString(node->children[1], pad) << endl; 44 | } else { 45 | // Leaf node. 46 | stream << indent << "| index = " << node->index << endl; 47 | } 48 | stream << indent << "}"; 49 | return stream.str(); 50 | } 51 | 52 | } // namespace fr 53 | -------------------------------------------------------------------------------- /src/shared/types/linked_node.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "types/bounding_box.hpp" 6 | #include "utils/tostring.hpp" 7 | 8 | namespace fr { 9 | 10 | struct LinkedNode { 11 | /// Constructor for leaf nodes. 12 | explicit LinkedNode(size_t index, const BoundingBox& bounds); 13 | 14 | /// Constructor for interior nodes. 15 | explicit LinkedNode(LinkedNode* left, LinkedNode* right, 16 | BoundingBox::Axis split); 17 | 18 | /// The bounding box of this node in world space. 19 | BoundingBox bounds; 20 | 21 | /// Links to this node's children, if it's an interior node. 22 | LinkedNode* children[2]; 23 | 24 | /// Index of the primitive, if it's a leaf node. 25 | size_t index; 26 | 27 | /// The axis the node was split on. 28 | BoundingBox::Axis split; 29 | 30 | TOSTRINGABLEBYPTR(LinkedNode); 31 | }; 32 | 33 | std::string ToString(const LinkedNode* node, const std::string& indent = ""); 34 | 35 | } // namespace fr 36 | -------------------------------------------------------------------------------- /src/shared/types/local_geometry.cpp: -------------------------------------------------------------------------------- 1 | #include "types/local_geometry.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "utils/printers.hpp" 8 | 9 | using std::numeric_limits; 10 | using std::string; 11 | using std::stringstream; 12 | using std::endl; 13 | 14 | namespace fr { 15 | 16 | LocalGeometry::LocalGeometry(glm::vec3 n) : 17 | n(n) { 18 | t.x = numeric_limits::quiet_NaN(); 19 | t.y = numeric_limits::quiet_NaN(); 20 | } 21 | 22 | LocalGeometry::LocalGeometry(glm::vec3 n, glm::vec2 t) : 23 | n(n), 24 | t(t) {} 25 | 26 | LocalGeometry::LocalGeometry() { 27 | n.x = numeric_limits::quiet_NaN(); 28 | n.y = numeric_limits::quiet_NaN(); 29 | n.z = numeric_limits::quiet_NaN(); 30 | 31 | t.x = numeric_limits::quiet_NaN(); 32 | t.y = numeric_limits::quiet_NaN(); 33 | } 34 | 35 | string ToString(const LocalGeometry& geom, const string& indent) { 36 | stringstream stream; 37 | stream << "LocalGeometry {" << endl << 38 | indent << "| n = " << ToString(geom.n) << endl << 39 | indent << "| t = " << ToString(geom.t) << endl << 40 | indent << "}" << endl; 41 | return stream.str(); 42 | } 43 | 44 | } // namespace fr 45 | -------------------------------------------------------------------------------- /src/shared/types/local_geometry.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "glm/glm.hpp" 4 | 5 | #include "utils/tostring.hpp" 6 | 7 | namespace fr { 8 | 9 | struct LocalGeometry { 10 | explicit LocalGeometry(glm::vec3 n); 11 | 12 | explicit LocalGeometry(glm::vec3 n, glm::vec2 t); 13 | 14 | explicit LocalGeometry(); 15 | 16 | /// The interpolated surface normal. 17 | glm::vec3 n; 18 | 19 | /// The interpolated texture coordinates. 20 | glm::vec2 t; 21 | 22 | TOSTRINGABLE(LocalGeometry); 23 | }; 24 | 25 | std::string ToString(const LocalGeometry& geom, const std::string& indent = ""); 26 | 27 | } // namespace fr 28 | -------------------------------------------------------------------------------- /src/shared/types/material.cpp: -------------------------------------------------------------------------------- 1 | #include "types/material.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using std::numeric_limits; 8 | using std::string; 9 | using std::stringstream; 10 | using std::endl; 11 | 12 | namespace fr { 13 | 14 | Material::Material(uint32_t id) : 15 | id(id), 16 | textures(), 17 | emissive(false) { 18 | shader = numeric_limits::max(); 19 | } 20 | 21 | Material::Material(uint32_t id, uint32_t shader) : 22 | id(id), 23 | shader(shader), 24 | textures(), 25 | emissive(false) {} 26 | 27 | Material::Material(uint32_t id, uint32_t shader, bool emissive) : 28 | id(id), 29 | shader(shader), 30 | textures(), 31 | emissive(emissive) {} 32 | 33 | Material::Material() : 34 | textures(), 35 | emissive(false) { 36 | id = numeric_limits::max(); 37 | shader = numeric_limits::max(); 38 | } 39 | 40 | string ToString(const Material& mat, const string& indent) { 41 | stringstream stream; 42 | stream << "Material {" << endl << 43 | indent << "| id = " << mat.id << endl << 44 | indent << "| shader = " << mat.shader << endl << 45 | indent << "| textures = {" << endl; 46 | for (const auto& binding : mat.textures) { 47 | stream << indent << "| | " << binding.first << " -> " << binding.second << endl; 48 | } 49 | stream << indent << "| }" << endl << 50 | indent << "| emissive = " << (mat.emissive ? "true" : "false") << endl << 51 | indent << "}"; 52 | return stream.str(); 53 | } 54 | 55 | } // namespace fr 56 | -------------------------------------------------------------------------------- /src/shared/types/material.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "msgpack.hpp" 7 | 8 | #include "utils/tostring.hpp" 9 | 10 | namespace fr { 11 | 12 | struct Material { 13 | explicit Material(uint32_t id); 14 | 15 | explicit Material(uint32_t id, uint32_t shader); 16 | 17 | explicit Material(uint32_t id, uint32_t shader, bool emissive); 18 | 19 | // FOR MSGPACK ONLY! 20 | explicit Material(); 21 | 22 | /// Resource ID of the material. 23 | uint32_t id; 24 | 25 | /// The resource ID of the shader to use for this material. 26 | uint32_t shader; 27 | 28 | /// Mapping of texture "sampler" names (available to the shader) to resouce 29 | /// IDs. 30 | std::map textures; 31 | 32 | /// If the shader adds light to the scene, the material is emissive. 33 | bool emissive; 34 | 35 | MSGPACK_DEFINE(id, shader, textures, emissive); 36 | 37 | TOSTRINGABLE(Material); 38 | }; 39 | 40 | std::string ToString(const Material& mat, const std::string& indent = ""); 41 | 42 | } // namespace fr 43 | -------------------------------------------------------------------------------- /src/shared/types/mesh.cpp: -------------------------------------------------------------------------------- 1 | #include "types/mesh.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "types/bvh.hpp" 8 | #include "utils/printers.hpp" 9 | 10 | using std::numeric_limits; 11 | using std::string; 12 | using std::stringstream; 13 | using std::endl; 14 | using glm::vec4; 15 | using glm::mat4; 16 | using glm::inverse; 17 | using glm::transpose; 18 | 19 | namespace fr { 20 | 21 | Mesh::Mesh(uint32_t id) : 22 | id(id), 23 | vertices(), 24 | faces(), 25 | bvh(nullptr) { 26 | material = numeric_limits::max(); 27 | 28 | centroid.x = numeric_limits::quiet_NaN(); 29 | centroid.y = numeric_limits::quiet_NaN(); 30 | centroid.z = numeric_limits::quiet_NaN(); 31 | 32 | xform_cols[0] = vec4(1.0f, 0.0f, 0.0f, 0.0f); 33 | xform_cols[1] = vec4(0.0f, 1.0f, 0.0f, 0.0f); 34 | xform_cols[2] = vec4(0.0f, 0.0f, 1.0f, 0.0f); 35 | xform_cols[3] = vec4(0.0f, 0.0f, 0.0f, 1.0f); 36 | 37 | xform = mat4(xform_cols[0], xform_cols[1], xform_cols[2], xform_cols[3]); 38 | xform_inv = mat4(xform_cols[0], xform_cols[1], xform_cols[2], xform_cols[3]); 39 | xform_inv_tr = mat4(xform_cols[0], xform_cols[1], xform_cols[2], xform_cols[3]); 40 | } 41 | 42 | Mesh::Mesh(uint32_t id, uint32_t material) : 43 | id(id), 44 | material(material), 45 | vertices(), 46 | faces(), 47 | bvh(nullptr) { 48 | centroid.x = numeric_limits::quiet_NaN(); 49 | centroid.y = numeric_limits::quiet_NaN(); 50 | centroid.z = numeric_limits::quiet_NaN(); 51 | 52 | xform_cols[0] = vec4(1.0f, 0.0f, 0.0f, 0.0f); 53 | xform_cols[1] = vec4(0.0f, 1.0f, 0.0f, 0.0f); 54 | xform_cols[2] = vec4(0.0f, 0.0f, 1.0f, 0.0f); 55 | xform_cols[3] = vec4(0.0f, 0.0f, 0.0f, 1.0f); 56 | 57 | xform = mat4(xform_cols[0], xform_cols[1], xform_cols[2], xform_cols[3]); 58 | xform_inv = mat4(xform_cols[0], xform_cols[1], xform_cols[2], xform_cols[3]); 59 | xform_inv_tr = mat4(xform_cols[0], xform_cols[1], xform_cols[2], xform_cols[3]); 60 | } 61 | 62 | Mesh::Mesh() : 63 | vertices(), 64 | faces(), 65 | bvh(nullptr) { 66 | id = numeric_limits::max(); 67 | material = numeric_limits::max(); 68 | 69 | centroid.x = numeric_limits::quiet_NaN(); 70 | centroid.y = numeric_limits::quiet_NaN(); 71 | centroid.z = numeric_limits::quiet_NaN(); 72 | 73 | xform_cols[0] = vec4(1.0f, 0.0f, 0.0f, 0.0f); 74 | xform_cols[1] = vec4(0.0f, 1.0f, 0.0f, 0.0f); 75 | xform_cols[2] = vec4(0.0f, 0.0f, 1.0f, 0.0f); 76 | xform_cols[3] = vec4(0.0f, 0.0f, 0.0f, 1.0f); 77 | 78 | xform = mat4(xform_cols[0], xform_cols[1], xform_cols[2], xform_cols[3]); 79 | xform_inv = mat4(xform_cols[0], xform_cols[1], xform_cols[2], xform_cols[3]); 80 | xform_inv_tr = mat4(xform_cols[0], xform_cols[1], xform_cols[2], xform_cols[3]); 81 | } 82 | 83 | Mesh::~Mesh() { 84 | if (bvh != nullptr) delete bvh; 85 | } 86 | 87 | void Mesh::ComputeMatrices() { 88 | xform = mat4(xform_cols[0], xform_cols[1], xform_cols[2], xform_cols[3]); 89 | xform_inv = inverse(xform); 90 | xform_inv_tr = transpose(xform_inv); 91 | } 92 | 93 | string ToString(const Mesh& mesh, const string& indent) { 94 | stringstream stream; 95 | string pad = indent + "| "; 96 | string pad2 = indent + "| | "; 97 | stream << "Mesh {" << endl << 98 | indent << "| id = " << mesh.id << endl << 99 | indent << "| material = " << mesh.material << endl << 100 | indent << "| xform_cols[0] = " << ToString(mesh.xform_cols[0]) << endl << 101 | indent << "| xform_cols[1] = " << ToString(mesh.xform_cols[1]) << endl << 102 | indent << "| xform_cols[2] = " << ToString(mesh.xform_cols[2]) << endl << 103 | indent << "| xform_cols[3] = " << ToString(mesh.xform_cols[3]) << endl << 104 | indent << "| vertices = {" << endl; 105 | for (const auto& vertex : mesh.vertices) { 106 | stream << pad << ToString(vertex, pad2) << endl; 107 | } 108 | stream << indent << "| faces = {" << endl; 109 | for (const auto& tri : mesh.faces) { 110 | stream << pad << ToString(tri, pad2) << endl; 111 | } 112 | stream << indent << "| }" << endl << 113 | indent << "| centroid = " << ToString(mesh.centroid) << endl << 114 | indent << "| xform = " << ToString(mesh.xform, pad) << endl << 115 | indent << "| xform_inv = " << ToString(mesh.xform_inv, pad) << endl << 116 | indent << "| xform_inv_tr = " << ToString(mesh.xform_inv_tr, pad) << endl << 117 | indent << "}" << endl; 118 | return stream.str(); 119 | } 120 | 121 | } // namespace fr 122 | -------------------------------------------------------------------------------- /src/shared/types/mesh.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "glm/glm.hpp" 7 | #include "msgpack.hpp" 8 | 9 | #include "types/triangle.hpp" 10 | #include "types/vertex.hpp" 11 | #include "utils/tostring.hpp" 12 | 13 | namespace fr { 14 | 15 | class BVH; 16 | 17 | struct Mesh { 18 | explicit Mesh(uint32_t id); 19 | 20 | explicit Mesh(uint32_t id, uint32_t material); 21 | 22 | // FOR MSGPACK ONLY! 23 | explicit Mesh(); 24 | 25 | ~Mesh(); 26 | 27 | /// Resource ID of the mesh. 28 | uint32_t id; 29 | 30 | /// Resource ID of the material to use for rendering. 31 | uint32_t material; 32 | 33 | /// Columns of the 4x4 transform matrix. Only used for syncing. 34 | glm::vec4 xform_cols[4]; 35 | 36 | /// Indexed vertices. 37 | std::vector vertices; 38 | 39 | /// Indexed face sets. 40 | std::vector faces; 41 | 42 | /// Centroid of the mesh in WORLD space. Only used for scene distribution. 43 | /// Not synced. 44 | glm::vec3 centroid; 45 | 46 | /// The world space transformation matrix. Not synced, but recomputed on 47 | /// worker. 48 | glm::mat4 xform; 49 | 50 | /// The inverse of the transformation matrix. Not synced, but recomputed on 51 | /// worker. 52 | glm::mat4 xform_inv; 53 | 54 | /// The inverse transpose of the transformation matrix. Not synced, but 55 | /// recomputed on worker. 56 | glm::mat4 xform_inv_tr; 57 | 58 | /// The BVH for traversing this mesh efficiently. 59 | BVH* bvh; 60 | 61 | /// Uses the data in xform_cols to build the transformation matrix and 62 | /// compute the inverse and inverse transpose. 63 | void ComputeMatrices(); 64 | 65 | MSGPACK_DEFINE(id, material, xform_cols[0], xform_cols[1], xform_cols[2], 66 | xform_cols[3], vertices, faces); 67 | 68 | TOSTRINGABLE(Mesh); 69 | }; 70 | 71 | std::string ToString(const Mesh& mesh, const std::string& indent = ""); 72 | 73 | } // namespace fr 74 | -------------------------------------------------------------------------------- /src/shared/types/message.cpp: -------------------------------------------------------------------------------- 1 | #include "types/message.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using std::string; 8 | using std::stringstream; 9 | using std::endl; 10 | 11 | namespace fr { 12 | 13 | Message::Message(Kind kind) : 14 | kind(kind), 15 | size(0), 16 | body(nullptr) {} 17 | 18 | Message::Message() : 19 | kind(Kind::NONE), 20 | size(0), 21 | body(nullptr) {} 22 | 23 | string ToString(const Message& msg, const string& indent) { 24 | stringstream stream; 25 | stream << "Message {" << endl; 26 | switch (msg.kind) { 27 | case Message::Kind::NONE: 28 | stream << indent << "| kind = NONE" << endl; 29 | break; 30 | 31 | case Message::Kind::OK: 32 | stream << indent << "| kind = OK" << endl; 33 | break; 34 | 35 | case Message::Kind::ERROR: 36 | stream << indent << "| kind = ERROR" << endl; 37 | break; 38 | 39 | case Message::Kind::INIT: 40 | stream << indent << "| kind = INIT" << endl; 41 | break; 42 | 43 | case Message::Kind::SYNC_CONFIG: 44 | stream << indent << "| kind = SYNC_CONFIG" << endl; 45 | break; 46 | 47 | case Message::Kind::SYNC_SHADER: 48 | stream << indent << "| kind = SYNC_SHADER" << endl; 49 | break; 50 | 51 | case Message::Kind::SYNC_TEXTURE: 52 | stream << indent << "| kind = SYNC_TEXTURE" << endl; 53 | break; 54 | 55 | case Message::Kind::SYNC_MATERIAL: 56 | stream << indent << "| kind = SYNC_MATERIAL" << endl; 57 | break; 58 | 59 | case Message::Kind::SYNC_MESH: 60 | stream << indent << "| kind = SYNC_MESH" << endl; 61 | break; 62 | 63 | case Message::Kind::SYNC_CAMERA: 64 | stream << indent << "| kind = SYNC_CAMERA" << endl; 65 | break; 66 | 67 | case Message::Kind::BUILD_BVH: 68 | stream << indent << "| kind = BUILD_BVH" << endl; 69 | break; 70 | 71 | case Message::Kind::SYNC_WBVH: 72 | stream << indent << "| kind = SYNC_WBVH" << endl; 73 | break; 74 | 75 | case Message::Kind::SYNC_EMISSIVE: 76 | stream << indent << "| kind = SYNC_EMISSIVE" << endl; 77 | break; 78 | 79 | case Message::Kind::SYNC_IMAGE: 80 | stream << indent << "| kind = SYNC_IMAGE" << endl; 81 | break; 82 | 83 | case Message::Kind::RENDER_START: 84 | stream << indent << "| kind = RENDER_START" << endl; 85 | break; 86 | 87 | case Message::Kind::RENDER_STOP: 88 | stream << indent << "| kind = RENDER_STOP" << endl; 89 | break; 90 | 91 | case Message::Kind::RENDER_STATS: 92 | stream << indent << "| kind = RENDER_STATS" << endl; 93 | break; 94 | 95 | case Message::Kind::RENDER_PAUSE: 96 | stream << indent << "| kind = RENDER_PAUSE" << endl; 97 | break; 98 | 99 | case Message::Kind::RENDER_RESUME: 100 | stream << indent << "| kind = RENDER_RESUME" << endl; 101 | break; 102 | 103 | case Message::Kind::RAY: 104 | stream << indent << "| kind = RAY" << endl; 105 | break; 106 | 107 | default: 108 | stream << indent << "| kind = ?" << endl; 109 | break; 110 | } 111 | stream << indent << "| size = " << msg.size << endl << 112 | indent << "| body = "; 113 | for (uint32_t i = 0; i < msg.size; i++) { 114 | uint8_t byte = *(reinterpret_cast( 115 | reinterpret_cast(msg.body) + 116 | static_cast(i))); 117 | char buf[8]; // I/O streams really suck sometimes... 118 | snprintf(buf, 8, "0x%x ", byte); 119 | stream << buf; 120 | } 121 | stream << endl << indent << "}"; 122 | return stream.str(); 123 | } 124 | 125 | } // namespace fr 126 | -------------------------------------------------------------------------------- /src/shared/types/message.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "utils/tostring.hpp" 6 | 7 | namespace fr { 8 | 9 | struct Message { 10 | enum Kind { 11 | NONE = 0, 12 | OK = 1, 13 | ERROR = 2, 14 | INIT = 100, 15 | SYNC_CONFIG = 200, 16 | SYNC_SHADER = 201, 17 | SYNC_TEXTURE = 202, 18 | SYNC_MATERIAL = 203, 19 | SYNC_MESH = 204, 20 | SYNC_CAMERA = 205, 21 | SYNC_EMISSIVE = 206, 22 | BUILD_BVH = 250, 23 | SYNC_WBVH = 260, 24 | SYNC_IMAGE = 290, 25 | RENDER_START = 300, 26 | RENDER_STOP = 301, 27 | RENDER_STATS = 302, 28 | RENDER_PAUSE = 303, 29 | RENDER_RESUME = 304, 30 | RAY = 400 31 | }; 32 | 33 | explicit Message(Kind kind); 34 | 35 | explicit Message(); 36 | 37 | /// The kind of message this is. 38 | uint32_t kind; 39 | 40 | /// The size of the body. 41 | uint32_t size; 42 | 43 | /// Pointer to the data that goes in the body of the message. The amount 44 | /// of data behind this pointer should be the same as size. 45 | void* body; 46 | 47 | TOSTRINGABLE(Message); 48 | }; 49 | 50 | std::string ToString(const Message& msg, const std::string& indent = ""); 51 | 52 | } // namespace fr 53 | -------------------------------------------------------------------------------- /src/shared/types/primitive_info.cpp: -------------------------------------------------------------------------------- 1 | #include "types/primitive_info.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "utils/printers.hpp" 7 | 8 | using std::string; 9 | using std::stringstream; 10 | using std::endl; 11 | using glm::vec3; 12 | 13 | namespace fr { 14 | 15 | PrimitiveInfo::PrimitiveInfo(size_t index, const BoundingBox& bounds) : 16 | index(index), 17 | bounds(bounds) { 18 | centroid = 0.5f * bounds.min + 0.5f * bounds.max; 19 | } 20 | 21 | string ToString(const PrimitiveInfo& info, const string& indent) { 22 | stringstream stream; 23 | stream << "PrimitiveInfo {" << endl << 24 | indent << "| index = " << info.index << endl << 25 | indent << "| centroid = " << ToString(info.centroid) << endl << 26 | indent << "| bounds = " << ToString(info.bounds) << endl << 27 | indent << "}"; 28 | return stream.str(); 29 | } 30 | 31 | } // namespace fr 32 | -------------------------------------------------------------------------------- /src/shared/types/primitive_info.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "glm/glm.hpp" 6 | 7 | #include "types/bounding_box.hpp" 8 | #include "utils/tostring.hpp" 9 | 10 | namespace fr { 11 | 12 | struct PrimitiveInfo { 13 | explicit PrimitiveInfo(size_t index, const BoundingBox& bounds); 14 | 15 | /// Index of the primitive we're talking about in the list of primitives. 16 | uint32_t index; 17 | 18 | /// Centroid of the primitive in question. 19 | glm::vec3 centroid; 20 | 21 | /// Bounding box that encloses the primitive (in world space). 22 | BoundingBox bounds; 23 | 24 | TOSTRINGABLE(PrimitiveInfo); 25 | }; 26 | 27 | std::string ToString(const PrimitiveInfo& info, const std::string& indent = ""); 28 | 29 | } // namespace fr 30 | -------------------------------------------------------------------------------- /src/shared/types/ray_forward.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace fr { 4 | 5 | struct FatRay; 6 | class NetNode; 7 | 8 | struct RayForward { 9 | explicit RayForward(FatRay* ray, NetNode* node) : 10 | ray(ray), 11 | node(node) {} 12 | 13 | /// What are we sending? 14 | FatRay* ray; 15 | 16 | /// Where are we sending it? 17 | NetNode* node; 18 | }; 19 | 20 | } // namespace fr 21 | -------------------------------------------------------------------------------- /src/shared/types/render_stats.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "msgpack.hpp" 6 | 7 | /// Interval between sending render stats back to the renderer. 8 | #define FR_STATS_TIMEOUT_MS 100 9 | 10 | namespace fr { 11 | 12 | struct RenderStats { 13 | explicit RenderStats() { 14 | Reset(); 15 | } 16 | 17 | uint64_t intersects_produced; 18 | uint64_t illuminates_produced; 19 | uint64_t lights_produced; 20 | uint64_t intersects_killed; 21 | uint64_t illuminates_killed; 22 | uint64_t lights_killed; 23 | uint64_t rays_rx; 24 | uint64_t rays_tx; 25 | uint64_t bytes_rx; 26 | uint64_t intersect_queue; 27 | uint64_t illuminate_queue; 28 | uint64_t light_queue; 29 | float primary_progress; 30 | 31 | // Resets all the stats counters. 32 | inline void Reset() { 33 | intersects_produced = 0; 34 | illuminates_produced = 0; 35 | lights_produced = 0; 36 | intersects_killed = 0; 37 | illuminates_killed = 0; 38 | lights_killed = 0; 39 | rays_rx = 0; 40 | rays_tx = 0; 41 | bytes_rx = 0; 42 | intersect_queue = 0; 43 | illuminate_queue = 0; 44 | light_queue = 0; 45 | primary_progress = 0.0f; 46 | } 47 | 48 | MSGPACK_DEFINE(intersects_produced, illuminates_produced, lights_produced, 49 | intersects_killed, illuminates_killed, lights_killed, rays_rx, rays_tx, 50 | bytes_rx, intersect_queue, illuminate_queue, light_queue, primary_progress); 51 | }; 52 | 53 | } // namespace fr 54 | -------------------------------------------------------------------------------- /src/shared/types/shader.cpp: -------------------------------------------------------------------------------- 1 | #include "types/shader.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "scripting/shader_script.hpp" 8 | 9 | using std::numeric_limits; 10 | using std::string; 11 | using std::stringstream; 12 | using std::endl; 13 | 14 | namespace fr { 15 | 16 | Shader::Shader(uint32_t id) : 17 | id(id), 18 | code(""), 19 | script(nullptr) {} 20 | 21 | Shader::Shader(uint32_t id, const string& code) : 22 | id(id), 23 | code(code), 24 | script(nullptr) {} 25 | 26 | Shader::Shader() : 27 | code(""), 28 | script(nullptr) { 29 | id = numeric_limits::max(); 30 | } 31 | 32 | Shader::~Shader() { 33 | if (script != nullptr) delete script; 34 | } 35 | 36 | string ToString(const Shader& shader, const string& indent) { 37 | stringstream stream; 38 | stream << "Shader {" << endl << 39 | indent << "| id = " << shader.id << endl << 40 | indent << "| code = ..." << endl << 41 | "======================================================================" << endl << 42 | shader.code << endl << 43 | "======================================================================" << endl << 44 | indent << "}"; 45 | return stream.str(); 46 | } 47 | 48 | } // namespace fr 49 | -------------------------------------------------------------------------------- /src/shared/types/shader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "msgpack.hpp" 6 | 7 | #include "utils/tostring.hpp" 8 | 9 | namespace fr { 10 | 11 | class ShaderScript; 12 | 13 | struct Shader { 14 | explicit Shader(uint32_t id); 15 | 16 | explicit Shader(uint32_t id, const std::string& code); 17 | 18 | // FOR MSGPACK ONLY! 19 | explicit Shader(); 20 | 21 | ~Shader(); 22 | 23 | /// Resource ID of the shader. 24 | uint32_t id; 25 | 26 | /// The code we run for the shader. 27 | std::string code; 28 | 29 | MSGPACK_DEFINE(id, code); 30 | 31 | TOSTRINGABLE(Shader); 32 | 33 | /// The shader script we actually execute. 34 | ShaderScript* script; 35 | }; 36 | 37 | std::string ToString(const Shader& shader, const std::string& indent = ""); 38 | 39 | } // namespace fr 40 | -------------------------------------------------------------------------------- /src/shared/types/slim_ray.cpp: -------------------------------------------------------------------------------- 1 | #include "types/slim_ray.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "utils/printers.hpp" 8 | 9 | using std::numeric_limits; 10 | using std::string; 11 | using std::stringstream; 12 | using std::endl; 13 | using glm::vec3; 14 | using glm::vec4; 15 | using glm::mat4; 16 | 17 | namespace fr { 18 | 19 | const float SELF_INTERSECT_EPSILON = 0.0001f; 20 | 21 | const float TARGET_INTERSECT_EPSILON = 0.0001f; 22 | 23 | SlimRay::SlimRay(vec3 origin, vec3 direction) : 24 | origin(origin), 25 | direction(direction) {} 26 | 27 | SlimRay::SlimRay() { 28 | origin.x = numeric_limits::quiet_NaN(); 29 | origin.y = numeric_limits::quiet_NaN(); 30 | origin.z = numeric_limits::quiet_NaN(); 31 | 32 | direction.x = numeric_limits::quiet_NaN(); 33 | direction.y = numeric_limits::quiet_NaN(); 34 | direction.z = numeric_limits::quiet_NaN(); 35 | } 36 | 37 | SlimRay SlimRay::TransformTo(const mat4& transform) const { 38 | vec4 o(origin, 1.0f); 39 | vec4 d(direction, 0.0f); 40 | return SlimRay(vec3(transform * o), 41 | vec3(transform * d)); 42 | } 43 | 44 | string ToString(const SlimRay& ray, const string& indent) { 45 | stringstream stream; 46 | string pad = indent + "| "; 47 | stream << "SlimRay {" << endl << 48 | indent << "| origin = " << ToString(ray.origin) << endl << 49 | indent << "| direction = " << ToString(ray.direction) << endl << 50 | indent << "}"; 51 | return stream.str(); 52 | } 53 | 54 | } // namespace fr 55 | -------------------------------------------------------------------------------- /src/shared/types/slim_ray.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "glm/glm.hpp" 4 | 5 | #include "utils/tostring.hpp" 6 | 7 | namespace fr { 8 | 9 | /// Avoid self-intersection by only recognizing intersections that occur 10 | /// at this minimum t-value along the ray. 11 | extern const float SELF_INTERSECT_EPSILON; 12 | 13 | /// A light ray must hit within this distance of its target to say it has hit 14 | /// the target. 15 | extern const float TARGET_INTERSECT_EPSILON; 16 | 17 | struct SlimRay { 18 | explicit SlimRay(glm::vec3 origin, glm::vec3 direction); 19 | 20 | explicit SlimRay(); 21 | 22 | /// The origin position of the ray. 23 | glm::vec3 origin; 24 | 25 | /// The normalized direction of the ray. Unit length is not enforced. 26 | glm::vec3 direction; 27 | 28 | /// Returns a new ray that is this ray transformed by the given 29 | /// transformation matrix. 30 | SlimRay TransformTo(const glm::mat4& transform) const; 31 | 32 | /// Evaluate a point along the ray at a specific t value. 33 | inline glm::vec3 EvaluateAt(float t) const { 34 | return direction * t + origin; 35 | } 36 | 37 | TOSTRINGABLE(SlimRay); 38 | }; 39 | 40 | std::string ToString(const SlimRay& ray, const std::string& indent = ""); 41 | 42 | } // namespace fr 43 | -------------------------------------------------------------------------------- /src/shared/types/texture.cpp: -------------------------------------------------------------------------------- 1 | #include "types/texture.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "scripting/texture_script.hpp" 9 | 10 | using std::numeric_limits; 11 | using std::string; 12 | using std::stringstream; 13 | using std::endl; 14 | using glm::vec2; 15 | 16 | namespace fr { 17 | 18 | Texture::Texture(uint32_t id) : 19 | id(id), 20 | kind(Kind::NONE), 21 | width(0), 22 | height(0), 23 | code(""), 24 | image(), 25 | script(nullptr) {} 26 | 27 | Texture::Texture(uint32_t id, const string& code) : 28 | id(id), 29 | kind(Kind::PROCEDURAL), 30 | width(0), 31 | height(0), 32 | code(code), 33 | image(), 34 | script(nullptr) {} 35 | 36 | Texture::Texture(uint32_t id, int16_t width, int16_t height, const float* data) : 37 | id(id), 38 | kind(Kind::IMAGE), 39 | width(width), 40 | height(height), 41 | code(""), 42 | script(nullptr) { 43 | image.resize(sizeof(float) * width * height); 44 | memcpy(&image[0], data, sizeof(float) * width * height); 45 | } 46 | 47 | Texture::Texture() : 48 | kind(Kind::NONE), 49 | width(0), 50 | height(0), 51 | code(""), 52 | image(), 53 | script(nullptr) { 54 | id = numeric_limits::max(); 55 | } 56 | 57 | float Texture::Sample(vec2 texcoord) { 58 | float value = 0.0f; 59 | 60 | switch (kind) { 61 | case Kind::PROCEDURAL: 62 | value = script->Evaluate(texcoord); 63 | break; 64 | 65 | case Kind::IMAGE: 66 | value = Image(texcoord.x, texcoord.y); 67 | break; 68 | 69 | default: 70 | TERRLN("Attempt to sample unknown texture kind!"); 71 | exit(EXIT_FAILURE); 72 | break; 73 | } 74 | 75 | return value; 76 | } 77 | 78 | float Texture::Image(float u, float v) { 79 | // This could be a LOT better. 80 | int16_t x = static_cast(u * (width - 1)); 81 | int16_t y = static_cast(v * (height - 1)); 82 | uint32_t index = y * width + x; 83 | return image[index]; 84 | } 85 | 86 | string ToString(const Texture& tex, const string& indent) { 87 | stringstream stream; 88 | stream << "Texture {" << endl << 89 | indent << "| id = " << tex.id << endl; 90 | switch (tex.kind) { 91 | case Texture::Kind::NONE: 92 | stream << indent << "| kind = NONE" << endl; 93 | break; 94 | 95 | case Texture::Kind::PROCEDURAL: 96 | stream << indent << "| kind = PROCEDURAL" << endl << 97 | indent << "| code = ..." << endl << 98 | "======================================================================" << endl << 99 | tex.code << endl << 100 | "======================================================================" << endl; 101 | break; 102 | 103 | case Texture::Kind::IMAGE: 104 | stream << indent << "| kind = IMAGE" << endl << 105 | indent << "| width = " << tex.width << endl << 106 | indent << "| height = " << tex.height << endl << 107 | indent << "| image = ..." << endl << 108 | "======================================================================" << endl; 109 | for (int16_t y = 0; y < tex.height; y++) { 110 | if (y > 0) { 111 | stream << endl; 112 | } 113 | for (int16_t x = 0; x < tex.width; x++) { 114 | if (x > 0) { 115 | stream << "\t"; 116 | } 117 | int32_t index = y * tex.width + x; 118 | stream << tex.image[index]; 119 | } 120 | } 121 | stream << endl << 122 | "======================================================================" << endl; 123 | break; 124 | 125 | default: 126 | stream << indent << "| kind = ?" << endl; 127 | break; 128 | } 129 | stream << indent << "}"; 130 | return stream.str(); 131 | } 132 | 133 | } // namespace fr 134 | -------------------------------------------------------------------------------- /src/shared/types/texture.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "glm/glm.hpp" 7 | #include "msgpack.hpp" 8 | 9 | #include "utils/tostring.hpp" 10 | 11 | namespace fr { 12 | 13 | class TextureScript; 14 | 15 | struct Texture { 16 | enum Kind { 17 | NONE = 0, 18 | PROCEDURAL = 1, 19 | IMAGE = 2 20 | }; 21 | 22 | explicit Texture(uint32_t id); 23 | 24 | explicit Texture(uint32_t id, const std::string& code); 25 | 26 | explicit Texture(uint32_t id, int16_t width, int16_t height, const float* data); 27 | 28 | // FOR MSGPACK ONLY! 29 | explicit Texture(); 30 | 31 | /// Resource ID of the texture. 32 | uint32_t id; 33 | 34 | /// The kind of texture (from the above possible). 35 | uint32_t kind; 36 | 37 | /// If the texture is an image, the width (in pixels). 38 | int16_t width; 39 | 40 | /// If the texture is an image, the height (in pixels). 41 | int16_t height; 42 | 43 | /// If this texture is procedural, the code we should run. 44 | std::string code; 45 | 46 | /// If the texture is an image, store flat array of pixel values. 47 | std::vector image; 48 | 49 | /// Samples the texture at the given texture coordinate. 50 | float Sample(glm::vec2 texcoord); 51 | 52 | MSGPACK_DEFINE(id, kind, width, height, code, image); 53 | 54 | TOSTRINGABLE(Texture); 55 | 56 | /// The texture script we actually execute (if procedural). 57 | TextureScript* script; 58 | 59 | private: 60 | /// Samples the image data at texture coordinates 61 | float Image(float u, float v); 62 | }; 63 | 64 | std::string ToString(const Texture& tex, const std::string& indent = ""); 65 | 66 | } // namespace fr 67 | -------------------------------------------------------------------------------- /src/shared/types/traversal_state.cpp: -------------------------------------------------------------------------------- 1 | #include "types/traversal_state.hpp" 2 | 3 | #include 4 | #include 5 | 6 | using std::string; 7 | using std::stringstream; 8 | using std::endl; 9 | 10 | namespace fr { 11 | 12 | TraversalState::TraversalState() : 13 | current(0), 14 | state(State::NONE), 15 | hit(0) {} 16 | 17 | string ToString(const TraversalState& state, const string& indent) { 18 | stringstream stream; 19 | stream << "TraversalState {" << endl << 20 | indent << "| current = " << state.current << endl << 21 | indent << "| state = "; 22 | switch (state.state) { 23 | case TraversalState::State::NONE: 24 | stream << "NONE"; 25 | break; 26 | 27 | case TraversalState::State::FROM_PARENT: 28 | stream << "FROM_PARENT"; 29 | break; 30 | 31 | case TraversalState::State::FROM_SIBLING: 32 | stream << "FROM_SIBLING"; 33 | break; 34 | 35 | case TraversalState::State::FROM_CHILD: 36 | stream << "FROM_CHILD"; 37 | break; 38 | 39 | default: 40 | stream << "UNKNOWN"; 41 | break; 42 | } 43 | stream << endl << indent << "| hit = " << (state.hit ? "YES" : "NO") << endl << 44 | indent << "}"; 45 | return stream.str(); 46 | } 47 | 48 | } // namespace fr 49 | -------------------------------------------------------------------------------- /src/shared/types/traversal_state.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "msgpack.hpp" 7 | 8 | #include "utils/tostring.hpp" 9 | 10 | namespace fr { 11 | 12 | struct TraversalState { 13 | enum State { 14 | NONE = 0, 15 | FROM_PARENT = 1, 16 | FROM_SIBLING = 2, 17 | FROM_CHILD = 3 18 | }; 19 | 20 | explicit TraversalState(); 21 | 22 | /// The index of the current LinearNode we're visiting. 23 | size_t current; 24 | 25 | /// The current state of the 3-state automaton. 26 | uint32_t state; 27 | 28 | /// Whether or not a hit has been registered during traversal. This is 29 | /// technically a boolean, but padded to 4 bytes for alignment. 30 | uint32_t hit; 31 | 32 | TOSTRINGABLE(TraversalState); 33 | }; 34 | 35 | std::string ToString(const TraversalState& state, const std::string& indent = ""); 36 | 37 | } // namespace fr 38 | -------------------------------------------------------------------------------- /src/shared/types/traversal_stats.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "msgpack.hpp" 7 | 8 | namespace fr { 9 | 10 | struct TraversalStats { 11 | explicit TraversalStats() { 12 | Reset(); 13 | } 14 | 15 | std::map workers_touched; 16 | 17 | // Resets all the stats counters. 18 | inline void Reset() { 19 | } 20 | 21 | MSGPACK_DEFINE(workers_touched); 22 | }; 23 | 24 | } // namespace fr 25 | 26 | -------------------------------------------------------------------------------- /src/shared/types/triangle.cpp: -------------------------------------------------------------------------------- 1 | #include "types/triangle.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "types/slim_ray.hpp" 9 | #include "types/local_geometry.hpp" 10 | #include "types/vertex.hpp" 11 | 12 | using std::string; 13 | using std::stringstream; 14 | using std::endl; 15 | using std::vector; 16 | using glm::vec2; 17 | using glm::vec3; 18 | using glm::vec4; 19 | using glm::mat4; 20 | using glm::cross; 21 | using glm::dot; 22 | using glm::normalize; 23 | 24 | namespace fr { 25 | 26 | Triangle::Triangle(const uint32_t v1, const uint32_t v2, const uint32_t v3) { 27 | verts[0] = v1; 28 | verts[1] = v2; 29 | verts[2] = v3; 30 | } 31 | 32 | Triangle::Triangle() { 33 | verts[0] = 0; 34 | verts[1] = 0; 35 | verts[2] = 0; 36 | } 37 | 38 | void Triangle::Sample(const vector& vertices, vec3* position, 39 | vec3* normal, vec2* texcoord) const { 40 | assert(position != nullptr); 41 | 42 | // Credit: Physically Based Rendering, page 671. 43 | float r1 = rand() / static_cast(RAND_MAX); 44 | float r2 = rand() / static_cast(RAND_MAX); 45 | 46 | float sqr1 = sqrtf(r1); 47 | float u = 1.0f - sqr1; 48 | float v = r2 * sqr1; 49 | 50 | *position = InterpolatePosition(vertices, u, v); 51 | 52 | if (normal != nullptr) { 53 | *normal = InterpolateNormal(vertices, u, v); 54 | } 55 | 56 | if (texcoord != nullptr) { 57 | *texcoord = InterpolateTexCoord(vertices, u, v); 58 | } 59 | } 60 | 61 | BoundingBox Triangle::WorldBounds(const vector& vertices, 62 | const mat4& xform) const { 63 | BoundingBox bounds; 64 | bounds.Absorb(vec3(xform * vec4(vertices[verts[0]].v, 1.0f))); 65 | bounds.Absorb(vec3(xform * vec4(vertices[verts[1]].v, 1.0f))); 66 | bounds.Absorb(vec3(xform * vec4(vertices[verts[2]].v, 1.0f))); 67 | return bounds; 68 | } 69 | 70 | bool Triangle::Intersect(const vector& vertices, const SlimRay& ray, 71 | float* t, LocalGeometry* local) const { 72 | // Credit: Physically Based Rendering, page 141, with modifications. 73 | 74 | vec3 v1 = vertices[verts[0]].v; 75 | vec3 v2 = vertices[verts[1]].v; 76 | vec3 v3 = vertices[verts[2]].v; 77 | 78 | // First compute s1, edge vectors, and denominator. 79 | vec3 e1 = v2 - v1; 80 | vec3 e2 = v3 - v1; 81 | vec3 s1 = cross(ray.direction, e2); 82 | float divisor = dot(s1, e1); 83 | if (divisor == 0.0f) { 84 | return false; 85 | } 86 | float inv_divisor = 1.0f / divisor; 87 | 88 | // Next, compute the first barycentric coordinate, b1. 89 | vec3 d = ray.origin - v1; 90 | float b1 = dot(d, s1) * inv_divisor; 91 | if (b1 < 0.0f || b1 > 1.0f) { 92 | return false; 93 | } 94 | 95 | // Next, compute the second barycentric coordinate, b2. 96 | vec3 s2 = cross(d, e1); 97 | float b2 = dot(ray.direction, s2) * inv_divisor; 98 | if (b2 < 0.0f || b1 + b2 > 1.0f) { 99 | return false; 100 | } 101 | 102 | // Compute the intersection parameter, t. 103 | *t = dot(e2, s2) * inv_divisor; 104 | 105 | // Bail if the intersection doesn't meet our epsilon requirements. 106 | if (*t < SELF_INTERSECT_EPSILON) { 107 | return false; 108 | } 109 | 110 | // Compute the interpolated normal from the barycentric coordinates. 111 | local->n = InterpolateNormal(vertices, b1, b2); 112 | 113 | // Check the interpolated normal against the ray normal to cull back-facing 114 | // intersections. 115 | if (dot(local->n, ray.direction) > 0.0f) { 116 | return false; 117 | } 118 | 119 | // Compute the interpolated texture coordinate from the barycentric coords. 120 | local->t = InterpolateTexCoord(vertices, b1, b2); 121 | 122 | // Intersection succeeded. 123 | return true; 124 | } 125 | 126 | vec3 Triangle::InterpolatePosition(const vector& vertices, float u, 127 | float v) const { 128 | vec3 v1 = vertices[verts[0]].v; 129 | vec3 v2 = vertices[verts[1]].v; 130 | vec3 v3 = vertices[verts[2]].v; 131 | 132 | float w = 1.0f - u - v; 133 | 134 | return vec3( 135 | (w * v1.x) + // first coordinate (x) 136 | (u * v2.x) + 137 | (v * v3.x), 138 | (w * v1.y) + // second coordinate (y) 139 | (u * v2.y) + 140 | (v * v3.y), 141 | (w * v1.z) + // third coordinate (z) 142 | (u * v2.z) + 143 | (v * v3.z) 144 | ); 145 | } 146 | 147 | vec3 Triangle::InterpolateNormal(const vector& vertices, float u, 148 | float v) const { 149 | vec3 n1 = vertices[verts[0]].n; 150 | vec3 n2 = vertices[verts[1]].n; 151 | vec3 n3 = vertices[verts[2]].n; 152 | 153 | float w = 1.0f - u - v; 154 | 155 | return normalize(vec3( 156 | (w * n1.x) + // first coordinate (x) 157 | (u * n2.x) + 158 | (v * n3.x), 159 | (w * n1.y) + // second coordinate (y) 160 | (u * n2.y) + 161 | (v * n3.y), 162 | (w * n1.z) + // third coordinate (z) 163 | (u * n2.z) + 164 | (v * n3.z) 165 | )); 166 | } 167 | 168 | vec2 Triangle::InterpolateTexCoord(const vector& vertices, float u, 169 | float v) const { 170 | vec2 t1 = vertices[verts[0]].t; 171 | vec2 t2 = vertices[verts[1]].t; 172 | vec2 t3 = vertices[verts[2]].t; 173 | 174 | float w = 1.0f - u - v; 175 | 176 | return vec2( 177 | (w * t1.x) + // first coordinate (u) 178 | (u * t2.x) + 179 | (v * t3.x), 180 | (w * t1.y) + // second coordinate (v) 181 | (u * t2.y) + 182 | (v * t3.y) 183 | ); 184 | } 185 | 186 | string ToString(const Triangle& tri, const string& indent) { 187 | stringstream stream; 188 | string pad = indent + "| "; 189 | stream << "Triangle {" << endl << 190 | indent << "| v1 = " << tri.verts[0] << endl << 191 | indent << "| v2 = " << tri.verts[1] << endl << 192 | indent << "| v3 = " << tri.verts[2] << endl << 193 | indent << "}"; 194 | return stream.str(); 195 | } 196 | 197 | } // namespace fr 198 | -------------------------------------------------------------------------------- /src/shared/types/triangle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "glm/glm.hpp" 7 | #include "msgpack.hpp" 8 | 9 | #include "types/bounding_box.hpp" 10 | #include "utils/tostring.hpp" 11 | 12 | namespace fr { 13 | 14 | struct SlimRay; 15 | struct LocalGeometry; 16 | struct Vertex; 17 | 18 | struct Triangle { 19 | explicit Triangle(const uint32_t v1, const uint32_t v2, const uint32_t v3); 20 | 21 | // FOR MSGPACK ONLY! 22 | explicit Triangle(); 23 | 24 | /// Vertices of the triangle. 25 | uint32_t verts[3]; 26 | 27 | /** 28 | * Generates a sample point on the triangle in object space. If normal or 29 | * texcoord is non-null, they will be filled in with the interpolated 30 | * normal and texture coordinates, respectively. 31 | */ 32 | void Sample(const std::vector& vertices, glm::vec3* position, 33 | glm::vec3* normal = nullptr, glm::vec2* texcoord = nullptr) const; 34 | 35 | /** 36 | * Returns the world space bounding box of the triangle given the object 37 | * to world transformation matrix. 38 | */ 39 | BoundingBox WorldBounds(const std::vector& vertices, 40 | const glm::mat4& xform) const; 41 | 42 | /** 43 | * Intersects the given ray with this triangle and returns true if they 44 | * indeed intersect. If they do intersect, the passed t value and local 45 | * geometry structure are filled in with information about the point of 46 | * intersection. 47 | */ 48 | bool Intersect(const std::vector& vertices, 49 | const SlimRay& ray, float* t, LocalGeometry* local) const; 50 | 51 | MSGPACK_DEFINE(verts[0], verts[1], verts[2]); 52 | 53 | TOSTRINGABLE(Triangle); 54 | 55 | private: 56 | /// Computes the interpolated position in object space at the barycentric 57 | /// coordinates defined by . 58 | glm::vec3 InterpolatePosition(const std::vector& vertices, float u, 59 | float v) const; 60 | 61 | /// Computes the interpolated surface normal in object space at the 62 | /// barycentric coordinates defined by . 63 | glm::vec3 InterpolateNormal(const std::vector& vertices, float u, 64 | float v) const; 65 | 66 | /// Computes the interpolated texture coordinates in object space at the 67 | /// barycentric coordinates defined by . 68 | glm::vec2 InterpolateTexCoord(const std::vector& vertices, float u, 69 | float v) const; 70 | }; 71 | 72 | std::string ToString(const Triangle& tri, const std::string& indent = ""); 73 | 74 | } // namespace fr 75 | -------------------------------------------------------------------------------- /src/shared/types/vertex.cpp: -------------------------------------------------------------------------------- 1 | #include "types/vertex.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "utils/printers.hpp" 8 | 9 | using std::numeric_limits; 10 | using std::string; 11 | using std::stringstream; 12 | using std::endl; 13 | using glm::vec2; 14 | using glm::vec3; 15 | 16 | namespace fr { 17 | 18 | Vertex::Vertex(vec3 v, vec3 n, vec2 t) : 19 | v(v), 20 | n(n), 21 | t(t) {} 22 | 23 | Vertex::Vertex(vec3 v, vec3 n) : 24 | v(v), 25 | n(n) { 26 | t.x = numeric_limits::quiet_NaN(); 27 | t.y = numeric_limits::quiet_NaN(); 28 | } 29 | 30 | Vertex::Vertex(vec3 v) : 31 | v(v) { 32 | n.x = numeric_limits::quiet_NaN(); 33 | n.y = numeric_limits::quiet_NaN(); 34 | n.z = numeric_limits::quiet_NaN(); 35 | 36 | t.x = numeric_limits::quiet_NaN(); 37 | t.y = numeric_limits::quiet_NaN(); 38 | } 39 | 40 | Vertex::Vertex(float x, float y, float z) : 41 | v(vec3(x, y, z)) { 42 | n.x = numeric_limits::quiet_NaN(); 43 | n.y = numeric_limits::quiet_NaN(); 44 | n.z = numeric_limits::quiet_NaN(); 45 | 46 | t.x = numeric_limits::quiet_NaN(); 47 | t.y = numeric_limits::quiet_NaN(); 48 | } 49 | 50 | Vertex::Vertex() { 51 | v.x = numeric_limits::quiet_NaN(); 52 | v.y = numeric_limits::quiet_NaN(); 53 | v.z = numeric_limits::quiet_NaN(); 54 | 55 | n.x = numeric_limits::quiet_NaN(); 56 | n.y = numeric_limits::quiet_NaN(); 57 | n.z = numeric_limits::quiet_NaN(); 58 | 59 | t.x = numeric_limits::quiet_NaN(); 60 | t.y = numeric_limits::quiet_NaN(); 61 | } 62 | 63 | string ToString(const Vertex& vert, const string& indent) { 64 | stringstream stream; 65 | stream << "Vertex {" << endl << 66 | indent << "| v = " << ToString(vert.v) << endl << 67 | indent << "| n = " << ToString(vert.n) << endl << 68 | indent << "| t = " << ToString(vert.t) << endl << 69 | indent << "}"; 70 | return stream.str(); 71 | } 72 | 73 | } // namespace fr 74 | -------------------------------------------------------------------------------- /src/shared/types/vertex.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "glm/glm.hpp" 4 | #include "msgpack.hpp" 5 | 6 | #include "utils/tostring.hpp" 7 | 8 | namespace fr { 9 | 10 | struct Vertex { 11 | explicit Vertex(glm::vec3 v, glm::vec3 n, glm::vec2 t); 12 | 13 | explicit Vertex(glm::vec3 v, glm::vec3 n); 14 | 15 | explicit Vertex(glm::vec3 v); 16 | 17 | explicit Vertex(float x, float y, float z); 18 | 19 | // FOR MSGPACK ONLY! 20 | explicit Vertex(); 21 | 22 | /// Position of the vertex. 23 | glm::vec3 v; 24 | 25 | /// Normal of the vertex. Unit length is not enforced. 26 | glm::vec3 n; 27 | 28 | /// Texture coordinate of the vertex. Range is not enforced. 29 | glm::vec2 t; 30 | 31 | MSGPACK_DEFINE(v, n, t); 32 | 33 | TOSTRINGABLE(Vertex); 34 | }; 35 | 36 | std::string ToString(const Vertex& vert, const std::string& indent = ""); 37 | 38 | } // namespace fr 39 | -------------------------------------------------------------------------------- /src/shared/types/work_results.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "ray_forward.hpp" 7 | #include "buffer_op.hpp" 8 | 9 | namespace fr { 10 | 11 | struct WorkResults { 12 | explicit WorkResults() : 13 | forwards(), 14 | ops(), 15 | intersects_produced(0), 16 | illuminates_produced(0), 17 | lights_produced(0), 18 | intersects_killed(0), 19 | illuminates_killed(0), 20 | lights_killed(0) {} 21 | 22 | /// Rays we need to forward. 23 | std::vector forwards; 24 | 25 | /// Buffer operations we need to do. 26 | std::vector ops; 27 | 28 | /// Workers touched. 29 | std::map workers_touched; 30 | 31 | /// Intersect rays produced. 32 | uint64_t intersects_produced; 33 | 34 | /// Illuminate rays produced. 35 | uint64_t illuminates_produced; 36 | 37 | /// Light rays produced. 38 | uint64_t lights_produced; 39 | 40 | /// Intersect rays killed. 41 | uint64_t intersects_killed; 42 | 43 | /// Illuminate rays killed. 44 | uint64_t illuminates_killed; 45 | 46 | /// Light rays killed. 47 | uint64_t lights_killed; 48 | }; 49 | 50 | } // namespace fr 51 | -------------------------------------------------------------------------------- /src/shared/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utils/cmdline.hpp" 4 | #include "utils/library.hpp" 5 | #include "utils/network.hpp" 6 | #include "utils/printers.hpp" 7 | #include "utils/spacecode.hpp" 8 | #include "utils/tostring.hpp" 9 | #include "utils/tout.hpp" 10 | #include "utils/uncopyable.hpp" 11 | -------------------------------------------------------------------------------- /src/shared/utils/cmdline.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/cmdline.hpp" 2 | 3 | #include 4 | 5 | using std::string; 6 | using std::find; 7 | 8 | namespace fr { 9 | 10 | string FlagValue(int argc, char* argv[], const string& flag_short, 11 | const string& flag_long) { 12 | char **begin = argv; 13 | char **end = argv + argc; 14 | char **iter = nullptr; 15 | 16 | // Look for short flag value. 17 | iter = find(begin, end, flag_short); 18 | if (iter != end && ++iter != end) { 19 | return string(*iter); 20 | } 21 | 22 | // Look for long flag value. 23 | iter = find(begin, end, flag_long); 24 | if (iter != end && ++iter != end) { 25 | return string(*iter); 26 | } 27 | 28 | // Didn't find it. 29 | return string(""); 30 | } 31 | 32 | bool FlagExists(int argc, char* argv[], const string &flag_short, 33 | const string& flag_long) { 34 | char **begin = argv; 35 | char **end = argv + argc; 36 | char **iter = nullptr; 37 | 38 | // Look for short flag. 39 | iter = find(begin, end, flag_short); 40 | if (iter != end) { 41 | return true; 42 | } 43 | 44 | // Look for long option. 45 | iter = find(begin, end, flag_long); 46 | if (iter != end) { 47 | return true; 48 | } 49 | 50 | // Didn't find it. 51 | return false; 52 | } 53 | 54 | string ArgumentValue(int argc, char* argv[], int i) { 55 | int count = 0; 56 | for (int j = 0; j < argc; j++) { 57 | const char* str = argv[j]; 58 | if (str[0] != '-') { 59 | if (count == i) { 60 | return string(str); 61 | } else { 62 | count++; 63 | } 64 | } 65 | } 66 | return string(""); 67 | } 68 | 69 | } // namespace flexrender 70 | -------------------------------------------------------------------------------- /src/shared/utils/cmdline.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace fr { 6 | 7 | /** 8 | * Retrieves the value of a flag on the command line. If either the short 9 | * form or long form are empty strings, they are ignored. 10 | * 11 | * @param argc The argc value passed into main. 12 | * @param argv The argv value passed into main. 13 | * @param flag_short The short form ("-f") of the flag to look for. 14 | * @param flag_long The long form ("--flag") of the flag to look for. 15 | * @return The argument passed with that flag. Empty string if there was 16 | * no argument. 17 | */ 18 | std::string FlagValue(int argc, char *argv[], 19 | const std::string& flag_short, const std::string& flag_long); 20 | 21 | /** 22 | * Checks for the existence of the the passed flag on the command line. If 23 | * either the short form or long form are empty strings, they are ignored. 24 | * 25 | * @param argc The argc value passed into main. 26 | * @param argv The argv value passed into main. 27 | * @param flag_short The short form ("-f") of the flag to look for. 28 | * @param flag_long The long form ("--flag") of the flag to look for. 29 | * @return True if the flag is present, false otherwise. 30 | */ 31 | bool FlagExists(int argc, char *argv[], const std::string& flag_short, 32 | const std::string& flag_long); 33 | 34 | /** 35 | * Retrieves the value of the i'th argument on the command line. If the i'th 36 | * argument does not exist, returns an empty string. Arguments are counted 37 | * as monotonically increasing integers starting at 1, ignoring interspersed 38 | * flags. The 0'th argument is the name of the executable. 39 | * 40 | * @param argc The argc value passed into main. 41 | * @param argv The argv value passed into main. 42 | * @param i Which command line argument to retrieve. 43 | * @return The value of the i'th command line argument, or an empty string if 44 | * the i'th argument does not exist. 45 | */ 46 | std::string ArgumentValue(int argc, char* argv[], int i); 47 | 48 | } // namespace fr 49 | -------------------------------------------------------------------------------- /src/shared/utils/library.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "utils/uncopyable.hpp" 12 | #include "utils/tout.hpp" 13 | 14 | namespace fr { 15 | 16 | struct Config; 17 | struct Camera; 18 | class Image; 19 | class LightList; 20 | class BVH; 21 | struct Shader; 22 | struct Texture; 23 | struct Material; 24 | struct Mesh; 25 | class NetNode; 26 | struct FatRay; 27 | 28 | class Library : private Uncopyable { 29 | public: 30 | explicit Library(); 31 | ~Library(); 32 | 33 | // Configs... 34 | void StoreConfig(Config* config); 35 | 36 | inline Config* LookupConfig() const { return _config; } 37 | 38 | // Cameras... 39 | void StoreCamera(Camera* camera); 40 | 41 | inline Camera* LookupCamera() const { return _camera; } 42 | 43 | // Images... 44 | void StoreImage(Image* image); 45 | 46 | inline Image* LookupImage() const { return _image; } 47 | 48 | // Light lists... 49 | void StoreLightList(LightList* lights); 50 | 51 | inline LightList* LookupLightList() const { return _lights; } 52 | 53 | // MBVHs... 54 | void StoreMBVH(BVH* mbvh); 55 | 56 | inline BVH* LookupMBVH() const { return _mbvh; } 57 | 58 | // WBVHs... 59 | void StoreWBVH(BVH* wbvh); 60 | 61 | inline BVH* LookupWBVH() const { return _wbvh; } 62 | 63 | // Shaders... 64 | inline uint32_t NextShaderID() const { return _shaders.size(); } 65 | 66 | void StoreShader(uint32_t id, Shader* shader); 67 | 68 | inline Shader* LookupShader(uint32_t id) const { 69 | assert(id > 0); 70 | assert(id < _shaders.size()); 71 | return _shaders[id]; 72 | }; 73 | 74 | // Textures... 75 | inline uint32_t NextTextureID() const { return _textures.size(); } 76 | 77 | void StoreTexture(uint32_t id, Texture* texture); 78 | 79 | inline Texture* LookupTexture(uint32_t id) const { 80 | assert(id > 0); 81 | assert(id < _textures.size()); 82 | return _textures[id]; 83 | } 84 | 85 | // Materials... 86 | inline uint32_t NextMaterialID() const { return _materials.size(); } 87 | 88 | void StoreMaterial(uint32_t id, Material* material, const std::string& name); 89 | 90 | inline Material* LookupMaterial(uint32_t id) const { 91 | assert(id > 0); 92 | assert(id < _materials.size()); 93 | return _materials[id]; 94 | } 95 | 96 | inline uint32_t LookupMaterial(const std::string& name) const { 97 | uint32_t id = 0; 98 | try { 99 | id = _material_name_index.at(name); 100 | } catch (std::out_of_range& e) { 101 | id = 0; 102 | } 103 | return id; 104 | } 105 | 106 | // Meshes... 107 | inline uint32_t NextMeshID() const { return _meshes.size(); } 108 | 109 | void StoreMesh(uint32_t id, Mesh* mesh); 110 | 111 | inline Mesh* LookupMesh(uint32_t id) const { 112 | assert(id > 0); 113 | assert(id < _meshes.size()); 114 | return _meshes[id]; 115 | } 116 | 117 | void ForEachMesh(std::function func); 118 | 119 | void ForEachEmissiveMesh(std::function func); 120 | 121 | bool Intersect(FatRay* ray, uint32_t me); 122 | 123 | // Net nodes... 124 | void StoreNetNode(uint32_t id, NetNode* node); 125 | 126 | inline NetNode* LookupNetNode(uint32_t id) const { 127 | assert(id > 0); 128 | assert(id < _nodes.size()); 129 | return _nodes[id]; 130 | } 131 | 132 | void ForEachNetNode(std::function func); 133 | 134 | template 135 | std::vector ForEachNetNode(std::function func) { 136 | std::vector results(_nodes.size()); 137 | for (uint32_t id = 1; id < _nodes.size(); id++) { 138 | NetNode* node = _nodes[id]; 139 | if (node == nullptr) continue; 140 | RetType result = func(id, node); 141 | results.insert(results.begin() + id, result); 142 | } 143 | return results; 144 | } 145 | 146 | // Spatial index access for net nodes... 147 | void BuildSpatialIndex(); 148 | 149 | inline uint32_t LookupNetNodeBySpaceCode(uint64_t spacecode) const { 150 | #ifndef NDEBUG 151 | if (_chunk_size == 0) { 152 | TERRLN("Attempted to lookup net node by space code without first building the spatial index!"); 153 | exit(EXIT_FAILURE); 154 | } 155 | #endif 156 | return _spatial_index.at(spacecode / _chunk_size); 157 | } 158 | 159 | private: 160 | Config *_config; 161 | Camera* _camera; 162 | Image* _image; 163 | LightList* _lights; 164 | BVH* _mbvh; 165 | BVH* _wbvh; 166 | std::vector _shaders; 167 | std::vector _textures; 168 | std::vector _materials; 169 | std::vector _meshes; 170 | std::vector _nodes; 171 | std::unordered_map _material_name_index; 172 | std::vector _spatial_index; 173 | std::vector _emissive_index; 174 | uint64_t _chunk_size; 175 | }; 176 | 177 | } // namespace fr 178 | -------------------------------------------------------------------------------- /src/shared/utils/network.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "uv.h" 7 | 8 | #include "utils/tout.hpp" 9 | 10 | namespace fr { 11 | 12 | inline void CheckUVResult(int result, const std::string& message) { 13 | if (result != 0) { 14 | uv_err_t err = uv_last_error(uv_default_loop()); 15 | TERRLN(message << ": " << uv_strerror(err)); 16 | exit(EXIT_FAILURE); 17 | } 18 | } 19 | 20 | } // namespace fr 21 | -------------------------------------------------------------------------------- /src/shared/utils/printers.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/printers.hpp" 2 | 3 | #include 4 | #include 5 | 6 | using std::string; 7 | using std::stringstream; 8 | using std::endl; 9 | using glm::vec2; 10 | using glm::vec3; 11 | using glm::vec4; 12 | using glm::mat4; 13 | 14 | namespace fr { 15 | 16 | string ToString(vec2 vec, const string& indent) { 17 | stringstream stream; 18 | stream << "<" << vec.x << ", " << vec.y << ">"; 19 | return stream.str(); 20 | } 21 | 22 | string ToString(vec3 vec, const string& indent) { 23 | stringstream stream; 24 | stream << "<" << vec.x << ", " << vec.y << ", " << vec.z << ">"; 25 | return stream.str(); 26 | } 27 | 28 | string ToString(vec4 vec, const string& indent) { 29 | stringstream stream; 30 | stream << "<" << vec.x << ", " << vec.y << ", " << vec.z << ", " << vec.w << ">"; 31 | return stream.str(); 32 | } 33 | 34 | string ToString(mat4 mat, const string& indent) { 35 | stringstream stream; 36 | stream << "{" << endl << 37 | indent << "| " << mat[0][0] << "\t" << mat[1][0] << "\t" << mat[2][0] << "\t" << mat[3][0] << endl << 38 | indent << "| " << mat[0][1] << "\t" << mat[1][1] << "\t" << mat[2][1] << "\t" << mat[3][1] << endl << 39 | indent << "| " << mat[0][2] << "\t" << mat[1][2] << "\t" << mat[2][2] << "\t" << mat[3][2] << endl << 40 | indent << "| " << mat[0][3] << "\t" << mat[1][3] << "\t" << mat[2][3] << "\t" << mat[3][3] << endl << 41 | indent << "}"; 42 | return stream.str(); 43 | } 44 | 45 | } // namespace fr 46 | -------------------------------------------------------------------------------- /src/shared/utils/printers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "glm/glm.hpp" 6 | 7 | namespace fr { 8 | 9 | std::string ToString(glm::vec2 vec, const std::string& indent = ""); 10 | 11 | std::string ToString(glm::vec3 vec, const std::string& indent = ""); 12 | 13 | std::string ToString(glm::vec4 vec, const std::string& indent = ""); 14 | 15 | std::string ToString(glm::mat4 mat, const std::string& indent = ""); 16 | 17 | } // namespace fr 18 | -------------------------------------------------------------------------------- /src/shared/utils/spacecode.cpp: -------------------------------------------------------------------------------- 1 | #include "utils/spacecode.hpp" 2 | 3 | using glm::vec3; 4 | 5 | namespace fr { 6 | 7 | const uint64_t SPACECODE_MIN = 0; 8 | const uint64_t SPACECODE_MAX = 0x7fffffffffffffff; // msb = 0, everything else 1's 9 | 10 | uint64_t SpaceEncode(vec3 point, vec3 min, vec3 max) { 11 | // 21 (x) + 21 (y) + 21 (z) = 63 (< 64) bits total. 12 | const uint32_t bits_per_component = 21; 13 | 14 | // Scaled value between 0 -> 1 on each axis. 15 | vec3 scaled = (point - min) / (max - min); 16 | 17 | // Multiply by 2^bits_per_component to get the discrete value along each 18 | // axis. 19 | uint32_t factor = 0x1 << bits_per_component; 20 | uint32_t discrete_x = scaled.x * factor; 21 | uint32_t discrete_y = scaled.y * factor; 22 | uint32_t discrete_z = scaled.z * factor; 23 | 24 | // Interleave the bits by shifting in the x/z/y components 25 | // bits_per_component times. 26 | uint64_t morton = 0; 27 | for (int32_t bit = bits_per_component - 1; bit >= 0; bit--) { 28 | // Set the bitmask. 29 | uint32_t mask = 0x1 << bit; 30 | 31 | // Extract the next bit from each component. 32 | uint32_t bit_x = (discrete_x & mask) >> bit; 33 | uint32_t bit_y = (discrete_y & mask) >> bit; 34 | uint32_t bit_z = (discrete_z & mask) >> bit; 35 | 36 | // Make space on the right hand side of the Morton code. 37 | morton = morton << 3; 38 | 39 | // Set the x/y/z components. 40 | morton |= ((bit_x << 2) | (bit_z << 1) | (bit_y << 0)); 41 | } 42 | 43 | return morton; 44 | } 45 | 46 | } // namespace fr 47 | -------------------------------------------------------------------------------- /src/shared/utils/spacecode.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "glm/glm.hpp" 6 | 7 | // Currently uses Morton coding for a Z-order curve. 8 | // See: http://en.wikipedia.org/wiki/Z-order_(curve) 9 | 10 | namespace fr { 11 | 12 | /// Minimum value of the space encoding. 13 | extern const uint64_t SPACECODE_MIN; 14 | 15 | /// Maximum value of the space encoding. 16 | extern const uint64_t SPACECODE_MAX; 17 | 18 | /** 19 | * Encodes a point in 3D space to a designated space encoding (currently Morton 20 | * coding for a Z-order curve), given the associated minimum and maximum bounds 21 | * of the space. 22 | * 23 | * @param point The 3D point to encode. 24 | * @param min The minimum corner of an axis-aligned bounding box that 25 | * encloses the space. 26 | * @param max The maximum corner of an axis-aligned bounding box that 27 | * encloses the space. 28 | */ 29 | uint64_t SpaceEncode(glm::vec3 point, glm::vec3 min, glm::vec3 max); 30 | 31 | } // namespace fr 32 | -------------------------------------------------------------------------------- /src/shared/utils/tostring.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define TOSTRINGABLE(type) friend std::string ToString(const type &, const std::string&); 6 | #define TOSTRINGABLEBYPTR(type) friend std::string ToString(const type *, const std::string&); 7 | -------------------------------------------------------------------------------- /src/shared/utils/tout.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define TOUT(str) \ 7 | { \ 8 | std::time_t _FR_time_ = std::time(NULL); \ 9 | char _FR_time_buf_[16]; \ 10 | std::strftime(_FR_time_buf_, 16, "%H:%M:%S ", \ 11 | std::localtime(&_FR_time_)); \ 12 | std::cout << _FR_time_buf_ << str; \ 13 | } 14 | 15 | #define TOUTLN(str) TOUT(str << std::endl) 16 | 17 | #define TERR(str) \ 18 | { \ 19 | std::time_t _FR_time_ = std::time(NULL); \ 20 | char _FR_time_buf_[16]; \ 21 | std::strftime(_FR_time_buf_, 16, "%H:%M:%S ", \ 22 | std::localtime(&_FR_time_)); \ 23 | std::cerr << _FR_time_buf_ << str; \ 24 | } 25 | 26 | #define TERRLN(str) TERR(str << std::endl) 27 | -------------------------------------------------------------------------------- /src/shared/utils/uncopyable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace fr { 4 | 5 | // Thanks to Scott Meyers for this. 6 | // Effective C++, 3rd Edition 7 | // Item #6 8 | class Uncopyable { 9 | protected: 10 | Uncopyable() {} 11 | ~Uncopyable() {} 12 | 13 | private: 14 | Uncopyable(const Uncopyable &); 15 | const Uncopyable& operator=(const Uncopyable& other); 16 | }; 17 | 18 | } // namespace fr 19 | -------------------------------------------------------------------------------- /src/worker/engine.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace fr { 7 | 8 | void EngineInit(const std::string& ip, uint16_t port, uint32_t jobs); 9 | 10 | void EngineRun(); 11 | 12 | } // namespace fr 13 | -------------------------------------------------------------------------------- /src/worker/ray_queue.cpp: -------------------------------------------------------------------------------- 1 | #include "ray_queue.hpp" 2 | 3 | #include 4 | 5 | #include "types.hpp" 6 | #include "utils.hpp" 7 | 8 | namespace fr { 9 | 10 | RayQueue::RayQueue(Camera* camera, RenderStats* stats) : 11 | _camera(camera), 12 | _stats(stats), 13 | _intersect_front(nullptr), 14 | _intersect_back(nullptr), 15 | _intersect_size(0), 16 | _illuminate_front(nullptr), 17 | _illuminate_back(nullptr), 18 | _illuminate_size(0), 19 | _light_front(nullptr), 20 | _light_back(nullptr), 21 | _light_size(0), 22 | _paused(false) {} 23 | 24 | RayQueue::~RayQueue() { 25 | FatRay* ray = nullptr; 26 | 27 | while (_light_front != nullptr) { 28 | ray = _light_front; 29 | _light_front = ray->next; 30 | delete ray; 31 | } 32 | 33 | while (_illuminate_front != nullptr) { 34 | ray = _illuminate_front; 35 | _illuminate_front = ray->next; 36 | delete ray; 37 | } 38 | 39 | while (_intersect_front != nullptr) { 40 | ray = _intersect_front; 41 | _intersect_front = ray->next; 42 | delete ray; 43 | } 44 | } 45 | 46 | void RayQueue::Push(FatRay* ray) { 47 | assert(ray != nullptr); 48 | 49 | switch (ray->kind) { 50 | case FatRay::Kind::INTERSECT: 51 | if (_intersect_back != nullptr) { 52 | _intersect_back->next = ray; 53 | } else { 54 | _intersect_front = ray; 55 | } 56 | ray->next = nullptr; 57 | _intersect_back = ray; 58 | _intersect_size++; 59 | break; 60 | 61 | case FatRay::Kind::ILLUMINATE: 62 | if (_illuminate_back != nullptr) { 63 | _illuminate_back->next = ray; 64 | } else { 65 | _illuminate_front = ray; 66 | } 67 | ray->next = nullptr; 68 | _illuminate_back = ray; 69 | _illuminate_size++; 70 | break; 71 | 72 | case FatRay::Kind::LIGHT: 73 | if (_light_back != nullptr) { 74 | _light_back->next = ray; 75 | } else { 76 | _light_front = ray; 77 | } 78 | ray->next = nullptr; 79 | _light_back = ray; 80 | _light_size++; 81 | break; 82 | 83 | default: 84 | TERRLN("Pushed unknown ray kind into ray queue."); 85 | break; 86 | } 87 | } 88 | 89 | FatRay* RayQueue::Pop() { 90 | FatRay* ray = nullptr; 91 | 92 | // Pull from the light queue first. 93 | ray = _light_front; 94 | if (ray != nullptr) { 95 | _light_front = ray->next; 96 | ray->next = nullptr; 97 | if (_light_front == nullptr) { 98 | _light_back = nullptr; 99 | } 100 | _light_size--; 101 | return ray; 102 | } 103 | 104 | // Pull from the illumination queue if the light queue is empty. 105 | ray = _illuminate_front; 106 | if (ray != nullptr) { 107 | _illuminate_front = ray->next; 108 | ray->next = nullptr; 109 | if (_illuminate_front == nullptr) { 110 | _illuminate_back = nullptr; 111 | } 112 | _illuminate_size--; 113 | return ray; 114 | } 115 | 116 | // Pull from the intersection queue if the light queue is empty. 117 | ray = _intersect_front; 118 | if (ray != nullptr) { 119 | _intersect_front = ray->next; 120 | ray->next = nullptr; 121 | if (_intersect_front == nullptr) { 122 | _intersect_back = nullptr; 123 | } 124 | _intersect_size--; 125 | return ray; 126 | } 127 | 128 | // If primary ray generation is paused, we're done. 129 | if (_paused) { 130 | return nullptr; 131 | } 132 | 133 | // Generate a primary ray if the intersection queue is empty. 134 | ray = new FatRay; 135 | if (_camera->GeneratePrimary(ray)) { 136 | _stats->intersects_produced++; 137 | return ray; 138 | } 139 | 140 | // No more primary rays. 141 | delete ray; 142 | return nullptr; 143 | } 144 | 145 | } // namespace fr 146 | -------------------------------------------------------------------------------- /src/worker/ray_queue.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "utils/uncopyable.hpp" 6 | 7 | namespace fr { 8 | 9 | struct FatRay; 10 | struct Camera; 11 | struct RenderStats; 12 | 13 | class RayQueue : private Uncopyable { 14 | public: 15 | explicit RayQueue(Camera* camera, RenderStats* stats); 16 | 17 | ~RayQueue(); 18 | 19 | /// Pushes the given ray into the queue and assumes ownership of its memory. 20 | void Push(FatRay* ray); 21 | 22 | /// Pops a ray out of the queue and relinquishes control of its memory. 23 | FatRay* Pop(); 24 | 25 | /// Returns the size of the internal intersection ray queue. 26 | inline size_t IntersectSize() const { return _intersect_size; } 27 | 28 | /// Returns the size of the internal illumination ray queue. 29 | inline size_t IlluminateSize() const { return _illuminate_size; } 30 | 31 | /// Returns the size of the internal light ray queue. 32 | inline size_t LightSize() const { return _light_size; } 33 | 34 | /// Pauses primary ray generation. 35 | void Pause() { _paused = true; } 36 | 37 | /// Resumes primary ray generation. 38 | void Resume() { _paused = false; } 39 | 40 | private: 41 | Camera* _camera; 42 | RenderStats* _stats; 43 | FatRay* _intersect_front; 44 | FatRay* _intersect_back; 45 | size_t _intersect_size; 46 | FatRay* _illuminate_front; 47 | FatRay* _illuminate_back; 48 | size_t _illuminate_size; 49 | FatRay* _light_front; 50 | FatRay* _light_back; 51 | size_t _light_size; 52 | bool _paused; 53 | }; 54 | 55 | } // namespace fr 56 | -------------------------------------------------------------------------------- /src/worker/worker.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "engine.hpp" 8 | #include "utils.hpp" 9 | 10 | using std::string; 11 | using std::stringstream; 12 | using std::cerr; 13 | using std::endl; 14 | 15 | using namespace fr; 16 | 17 | int main(int argc, char *argv[]) { 18 | // Grab relevant command line arguments. 19 | uint16_t port = 19400; 20 | { 21 | string port_str = FlagValue(argc, argv, "-p", "--port"); 22 | if (port_str != "") { 23 | stringstream stream(port_str); 24 | stream >> port; 25 | } 26 | } 27 | 28 | uint32_t jobs = 10; 29 | { 30 | string jobs_str = FlagValue(argc, argv, "-j", "--jobs"); 31 | if (jobs_str != "") { 32 | stringstream stream(jobs_str); 33 | stream >> jobs; 34 | } 35 | } 36 | 37 | TOUTLN("FlexWorker starting."); 38 | 39 | EngineInit("0.0.0.0", port, jobs); 40 | TOUTLN("Listening on port " << port << "."); 41 | 42 | EngineRun(); 43 | 44 | TOUTLN("FlexWorker done."); 45 | return EXIT_SUCCESS; 46 | } 47 | --------------------------------------------------------------------------------