├── header.png ├── .gitignore ├── src ├── render │ ├── shaders │ │ ├── svo.vert │ │ ├── light.frag │ │ └── svo.frag │ ├── voxel.cpp │ ├── leaf.cpp │ ├── ray.cpp │ ├── vector.cpp │ ├── stack.cpp │ ├── block.cpp │ ├── render.cpp │ └── main.cpp └── construct │ ├── int3.cpp │ ├── sep.cpp │ ├── indexedleaf.cpp │ ├── model.cpp │ ├── writer.cpp │ ├── generate.cpp │ ├── sponge.cpp │ ├── main.cpp │ ├── zstream.cpp │ ├── stanford.cpp │ └── construct.cpp ├── README.md └── makefile /header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabrielmuller/zazen/HEAD/header.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.out 3 | *log 4 | .*.sw* 5 | .gdbinit 6 | *.bin 7 | *.zip 8 | models/* 9 | build/* 10 | *~ 11 | *.zaz 12 | -------------------------------------------------------------------------------- /src/render/shaders/svo.vert: -------------------------------------------------------------------------------- 1 | #version 430 core 2 | in vec2 position; 3 | 4 | void main() { 5 | gl_Position = vec4(position, 0.0, 1.0); 6 | } 7 | -------------------------------------------------------------------------------- /src/construct/int3.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct int3 { 4 | unsigned int x, y, z; 5 | int3(unsigned int x, unsigned int y, unsigned int z) : x(x), y(y), z(z) {} 6 | }; 7 | -------------------------------------------------------------------------------- /src/construct/sep.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | const char sep = 4 | #ifdef _WIN32 5 | '\\'; 6 | #else 7 | '/'; 8 | #endif 9 | 10 | -------------------------------------------------------------------------------- /src/construct/indexedleaf.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../render/leaf.cpp" 3 | 4 | struct IndexedLeaf { 5 | const Leaf leaf; 6 | const unsigned long long int index; 7 | IndexedLeaf(const Leaf leaf, const unsigned long long int index) 8 | : leaf(leaf), index(index) {} 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zazen 2 | 3D sparse voxel octree engine 3 | 4 | ![Rendered models](header.png) 5 | 6 | [Dissertation in Portuguese](https://repositorio.ufsc.br/bitstream/handle/123456789/202673/TCC.pdf?sequence=1&isAllowed=y) 7 | 8 | [Presentation in Portuguese](https://docs.google.com/presentation/d/1tApOPQa1h_Ap4Q5-Xljjrs0dZl0Z6xPQ2VvMCYRxZok/edit#slide=id.p) 9 | 10 | https://www.youtube.com/watch?v=cauwMJJkcEM 11 | -------------------------------------------------------------------------------- /src/render/voxel.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Voxel { 6 | /* Non-leaf voxel. */ 7 | 8 | uint32_t child; 9 | uint8_t valid; // 8 flags of whether children are visible 10 | uint8_t leaf; // 8 flags of whether children are leaves 11 | 12 | int32_t address_of(uint8_t octant) { 13 | /* Get address in block of child octant. */ 14 | uint8_t mask = ~(0xff << octant); 15 | return child + __builtin_popcount(mask & valid); 16 | } 17 | 18 | }; 19 | -------------------------------------------------------------------------------- /src/construct/model.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "../render/leaf.cpp" 6 | #include "int3.cpp" 7 | 8 | struct Model { 9 | const unsigned int width, height, depth; 10 | const std::string name; 11 | 12 | Model(std::string name, 13 | unsigned int width, unsigned int height, unsigned int depth) 14 | : width(width), height(height), depth(depth), name(name) { 15 | std::cout << "Constructing model \"" << name << "\"...\n"; 16 | } 17 | virtual ~Model() {} 18 | 19 | //private: 20 | size_t index; 21 | 22 | virtual Leaf at(int3 pos) const = 0; 23 | 24 | 25 | }; 26 | -------------------------------------------------------------------------------- /src/render/leaf.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Leaf { 6 | /* Leaf voxel. */ 7 | 8 | uint8_t rgba[4]; 9 | 10 | Leaf() = default; 11 | 12 | Leaf(uint8_t r, uint8_t g, uint8_t b, uint8_t a) 13 | : rgba{r, g, b, a} {} 14 | 15 | Leaf(uint8_t v) : Leaf(v, v, v, v) {} 16 | 17 | inline void set_color(unsigned char* pixel, const float lightness) const { 18 | pixel[0] = rgba[0] * lightness; 19 | pixel[1] = rgba[1] * lightness; 20 | pixel[2] = rgba[2] * lightness; 21 | } 22 | 23 | inline bool valid() const { 24 | return rgba[3] != 0x00; 25 | } 26 | 27 | inline bool operator!=(const Leaf& o) const { 28 | return *((uint32_t*) rgba) != *((uint32_t*) o.rgba); 29 | } 30 | 31 | inline bool operator==(const Leaf& o) const { 32 | return !operator!=(o); 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /src/construct/writer.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "../render/block.cpp" 6 | 7 | class BlockWriter { 8 | std::string filename; 9 | std::ofstream stream; 10 | size_t byte_count = 0; 11 | unsigned int element_count = 0; 12 | 13 | public: 14 | BlockWriter(std::string filename) : filename(filename) { 15 | stream = std::ofstream(filename, std::ios::binary); 16 | } 17 | 18 | ~BlockWriter() { 19 | std::cout << "File '" << filename << "' saved to disk.\n" 20 | << byte_count << " bytes.\n" 21 | << element_count << " elements.\n"; 22 | } 23 | 24 | unsigned int pos() const { 25 | return element_count; 26 | } 27 | 28 | template 29 | void operator<<(const T& node) { 30 | stream.write((char*) &node, sizeof(T)); 31 | byte_count += sizeof(T); 32 | element_count += sizeof(T) / Block::ELEMENT_SIZE; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /src/construct/generate.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "model.cpp" 4 | #include 5 | 6 | struct GenerateModel : Model { 7 | GenerateModel(std::string name, 8 | unsigned int width, unsigned int height, unsigned int depth) 9 | : Model(name, width, height, depth) {} 10 | 11 | Leaf at(int3 pos) const override { 12 | const unsigned int mod = width / 8; 13 | const float xx = (pos.x%mod)*2 / (float) mod - 1; 14 | const float yy = (pos.y%mod)*2 / (float) mod - 1; 15 | const float zz = (pos.z%mod)*2 / (float) mod - 1; 16 | const float radius = (sin((pos.x/mod + pos.y/mod + pos.z/mod)/5)+1)*0.3; 17 | if ((xx*xx + yy*yy + zz*zz > radius) || (std::sin((pos.x*16 + pos.y*20 + pos.z * 30) /(float)width) < 0)) { 18 | return Leaf(0); 19 | } 20 | 21 | const uint8_t value = 0x20; 22 | return Leaf(value*pos.x/mod, value*pos.y/mod, value*pos.z/mod, 0xff); 23 | } 24 | 25 | ~GenerateModel() override {} 26 | }; 27 | -------------------------------------------------------------------------------- /src/construct/sponge.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "model.cpp" 4 | #include 5 | 6 | 7 | struct SpongeModel : Model { 8 | SpongeModel(std::string name, 9 | unsigned int width, unsigned int height, unsigned int depth) 10 | : Model(name, width, height, depth) {} 11 | 12 | Leaf at(int3 pos) const override { 13 | const bool reduced_palette = true; 14 | bool filled = true; 15 | if (pos.x >= width || pos.y >= height || pos.z >= depth) return Leaf(); 16 | for (unsigned int div = width / 3; div > 0; div /= 3) { 17 | if (((pos.x / div) % 3 == 1) + 18 | ((pos.y / div) % 3 == 1) + 19 | ((pos.z / div) % 3 == 1) > 1) { 20 | filled = false; 21 | break; 22 | } 23 | } 24 | return filled ? Leaf(0xff, 25 | pos.x * 0xff / width, 26 | pos.y * 0xff / height, 27 | 0xff) : Leaf(); 28 | } 29 | 30 | ~SpongeModel() override {} 31 | }; 32 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: construct render run_construct run_render 4 | 5 | clean: 6 | rm -rf build 7 | mkdir build 8 | 9 | render: 10 | g++ -Wall -Wextra -O3 -Ofast src/render/main.cpp -fopenmp -IC:\mingw64\include -LC:\mingw64\lib -lSDL2 -lglew32 -lopengl32 -o build/render 11 | cp src/render/shaders/* build/ 12 | 13 | construct: 14 | g++ -Wall -Wextra -O3 -Ofast src/construct/main.cpp -o build/construct 15 | 16 | debug: 17 | g++ -Wall -Wextra -g -Og src/construct/main.cpp -o build/construct 18 | 19 | prof: 20 | g++ -g -fprofile-arcs -O3 -Ofast src/render/main.cpp -lSDL2 -o build/render 21 | g++ -g -fprofile-arcs -O3 src/construct/main.cpp -o build/construct 22 | 23 | 24 | run_construct: 25 | cd build; ./construct 26 | 27 | run_render: 28 | cd build; ./render 29 | 30 | bench: 31 | cd build; ./render generated.zaz 1280 720 1 32 | cd build; ./render generated.zaz 640 480 1 33 | cd build; ./render generated.zaz 1280 720 2 34 | cd build; ./render generated.zaz 640 480 2 35 | cd build; ./render generated.zaz 1280 720 4 36 | cd build; ./render generated.zaz 640 480 4 37 | cd build; ./render generated.zaz 1280 720 8 38 | cd build; ./render generated.zaz 640 480 8 39 | -------------------------------------------------------------------------------- /src/render/ray.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "vector.cpp" 5 | const float e = std::numeric_limits::epsilon(); 6 | struct Ray { 7 | /* Simple container class for a ray. */ 8 | Vector origin, direction, negdir; 9 | 10 | Ray() : origin(Vector()), direction(Vector()) {} 11 | 12 | explicit Ray(const Vector& origin, Vector& direction) : 13 | origin(origin), init_origin(origin) { 14 | if (fabsf(direction.x) < e) direction.x = copysignf(e, direction.x); 15 | if (fabsf(direction.y) < e) direction.y = copysignf(e, direction.y); 16 | if (fabsf(direction.z) < e) direction.z = copysignf(e, direction.z); 17 | this->direction = direction; 18 | } 19 | 20 | uint8_t octant_mask() { 21 | uint8_t mask = 0; 22 | if (direction.x >= 0) mask ^= 4; 23 | if (direction.y >= 0) mask ^= 2; 24 | if (direction.z >= 0) mask ^= 1; 25 | return mask; 26 | } 27 | 28 | Ray march(float amount) { 29 | Vector diff(direction.x * amount, 30 | direction.y * amount, 31 | direction.z * amount); 32 | 33 | origin += diff; 34 | return *this; 35 | } 36 | 37 | inline float square_distance() { 38 | return (origin - init_origin).squared(); 39 | } 40 | 41 | private: 42 | Vector init_origin; 43 | }; 44 | 45 | -------------------------------------------------------------------------------- /src/construct/main.cpp: -------------------------------------------------------------------------------- 1 | #include "construct.cpp" 2 | #include "zstream.cpp" 3 | #include "stanford.cpp" 4 | #include "generate.cpp" 5 | #include "sponge.cpp" 6 | #include "../render/block.cpp" 7 | 8 | StanfordModel* bunny() { 9 | return new StanfordModel("bunny", "", 512, 316, 512); 10 | } 11 | 12 | StanfordModel* brain() { 13 | return new StanfordModel("MRbrain", "MRbrain.", 256, 109, 256); 14 | } 15 | 16 | GenerateModel* generated() { 17 | const unsigned int size = 512; 18 | return new GenerateModel("generated", size, size, size); 19 | } 20 | 21 | SpongeModel* sponge() { 22 | const unsigned int size = 243; 23 | return new SpongeModel("sponge", size, size, size); 24 | } 25 | 26 | void save_model(Model* model) { 27 | ZStream stream(model); 28 | BlockWriter writer(model->name + ".zaz"); 29 | Builder builder(stream.power, writer); 30 | unsigned int counter = 0; 31 | while(stream.is_open()) { 32 | builder.add_leaf(stream.next()); 33 | if (!(counter % 10000)) { 34 | std::cout << "\r" << (int) (stream.progress() * 100) << "%" 35 | << std::flush; 36 | } 37 | counter++; 38 | } 39 | std::cout << "\r"; 40 | } 41 | 42 | int main() { 43 | //GenerateModel* gen = generated(); 44 | //StanfordModel* bunny_model = bunny(); 45 | SpongeModel* sponge_model = sponge(); 46 | //StanfordModel* brain_model = brain(); 47 | //save_model(gen); 48 | //save_model(bunny_model); 49 | save_model(sponge_model); 50 | //save_model(brain_model); 51 | //delete gen; 52 | //delete bunny_model; 53 | delete sponge_model; 54 | //delete brain_model; 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /src/render/vector.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct Vector { 7 | /* Simple container struct for a 3D vector. */ 8 | float x, y, z; 9 | Vector() = default; 10 | explicit Vector(float x, float y, float z) : 11 | x(x), y(y), z(z) {} 12 | 13 | Vector(const Vector& v) : Vector(v.x, v.y, v.z) {} 14 | 15 | inline float squared() const { 16 | return x*x + y*y + z*z; 17 | } 18 | 19 | Vector& normalized() { 20 | float invmag = 1 / sqrtf(squared()); 21 | x *= invmag; 22 | y *= invmag; 23 | z *= invmag; 24 | return *this; 25 | } 26 | 27 | Vector mirror(uint8_t mask) const { 28 | float mirror_x = mask & 4 ? -x : x; 29 | float mirror_y = mask & 2 ? -y : y; 30 | float mirror_z = mask & 1 ? -z : z; 31 | return Vector(mirror_x, mirror_y, mirror_z); 32 | } 33 | 34 | Vector operator+(const Vector& v) const { 35 | return Vector(v.x + x, v.y + y, v.z + z); 36 | } 37 | 38 | Vector operator-(const Vector& v) const { 39 | return Vector(v.x - x, v.y - y, v.z - z); 40 | } 41 | 42 | Vector operator*(const float scalar) const { 43 | return Vector(x * scalar, y * scalar, z * scalar); 44 | } 45 | 46 | Vector& operator+=(const Vector& v) { 47 | x += v.x; 48 | y += v.y; 49 | z += v.z; 50 | return *this; 51 | } 52 | 53 | Vector& operator-=(const Vector& v) { 54 | x -= v.x; 55 | y -= v.y; 56 | z -= v.z; 57 | return *this; 58 | } 59 | 60 | 61 | 62 | void adjust_corner(float size, uint8_t octant) { 63 | if (octant & 4) x += size; 64 | if (octant & 2) y += size; 65 | if (octant & 1) z += size; 66 | } 67 | }; 68 | -------------------------------------------------------------------------------- /src/render/stack.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "voxel.cpp" 4 | #include "vector.cpp" 5 | 6 | struct StackEntry { 7 | Voxel* voxel; // pointer to voxel in Block 8 | uint8_t octant; // octant ray origin is in this voxel 9 | Vector corner; // inferior corner 10 | }; 11 | 12 | struct VoxelStack { 13 | StackEntry* entries; 14 | size_t top; 15 | float box_size; 16 | 17 | explicit VoxelStack(const size_t size, const float init_box_size) { 18 | entries = new StackEntry[size]; 19 | top = 0; 20 | box_size = init_box_size; 21 | } 22 | 23 | ~VoxelStack() { 24 | delete[] entries; 25 | } 26 | 27 | void push(Voxel* voxel, Ray ray) { 28 | entries[top] = {voxel, 0, peek().corner}; 29 | box_size *= 0.5; 30 | entries[top].corner.adjust_corner(box_size, peek().octant); 31 | top++; 32 | peek().octant = get_octant(ray); 33 | } 34 | 35 | void push_root(Voxel* voxel, Vector corner, Ray ray) { 36 | entries[top] = {voxel, 0, corner}; 37 | top++; 38 | peek().octant = get_octant(ray); 39 | } 40 | 41 | inline void pop() { 42 | box_size *= 2; 43 | top--; 44 | } 45 | 46 | inline StackEntry* operator->() const { 47 | return entries + (top - 1); 48 | } 49 | 50 | inline StackEntry& peek() const { 51 | return *operator->(); 52 | } 53 | 54 | inline bool empty() { 55 | return !top; 56 | } 57 | 58 | inline size_t size() { 59 | return top; 60 | } 61 | 62 | inline uint8_t get_octant (const Ray& ray) const { 63 | /* Returns which octant the vector resides inside box. */ 64 | uint8_t octant = 0; 65 | const float oct_size = box_size * 0.5; 66 | const Vector& corner = peek().corner; 67 | 68 | if (ray.origin.x > corner.x + oct_size) octant ^= 4; 69 | if (ray.origin.y > corner.y + oct_size) octant ^= 2; 70 | if (ray.origin.z > corner.z + oct_size) octant ^= 1; 71 | return octant; 72 | } 73 | }; 74 | -------------------------------------------------------------------------------- /src/render/shaders/light.frag: -------------------------------------------------------------------------------- 1 | #version 430 core 2 | 3 | out vec4 outColor; 4 | in vec4 gl_FragCoord; 5 | uniform uvec2 viewportSize; 6 | uniform uint upscale; 7 | layout (binding = 0) uniform sampler2D colorTexture; 8 | layout (binding = 3) uniform sampler2D positionTexture; 9 | 10 | void main() { 11 | vec2 verts[3]; 12 | verts[0] = gl_FragCoord.xy / float(upscale); 13 | 14 | 15 | bool lowerTri = fract(verts[0]).x > fract(verts[0]).y; 16 | vec2 triOrientation = lowerTri ? vec2(1., 0.) : vec2(0., 1.); 17 | 18 | verts[lowerTri?1:2] = verts[0] + vec2(1.); 19 | verts[lowerTri?2:1] = verts[0] + triOrientation; 20 | 21 | vec4 colors[3]; 22 | vec4 pos[3]; 23 | 24 | for (uint i = 0; i < 3; i++) { 25 | verts[i] = (verts[i] * float(upscale)) / viewportSize; 26 | colors[i] = texture(colorTexture, verts[i]); 27 | pos[i] = texture(positionTexture, verts[i]); 28 | if (pos[i].w == 0.0) { 29 | /* Background color */ 30 | vec3 BG_COLORS[] = { 31 | vec3(0.749, 0.784, 0.843), 32 | vec3(0.886, 0.824, 0.824), 33 | vec3(0.89, 0.886, 0.706), 34 | vec3(0.635, 0.71, 0.624), 35 | }; 36 | outColor = vec4( 37 | mix( 38 | mix(BG_COLORS[0], BG_COLORS[1], pos[i].x), 39 | mix(BG_COLORS[1], BG_COLORS[2], pos[i].y), 40 | pos[i].z 41 | ), 42 | 1.0 43 | ); 44 | return; 45 | } 46 | if (all(equal(pos[0], pos[1])) && all(equal(pos[1], pos[2]))) { 47 | /* Positions are equal, probably inside a leaf */ 48 | outColor = colors[0]; 49 | return; 50 | } 51 | } 52 | 53 | vec3 normal = cross( 54 | pos[2].xyz - pos[0].xyz, 55 | pos[1].xyz - pos[0].xyz 56 | ); 57 | normal = normalize(normal); 58 | 59 | vec3 lampPos = vec3(0., 1., 0.); 60 | vec3 lightDir = normalize(pos[0].xyz - lampPos); 61 | float dist = length(pos[0].xyz); 62 | float lightness = dot(lightDir, normal) * 0.6 * 1./(dist*dist) + 0.4; 63 | 64 | outColor = colors[0] * 0.5 + colors[1] * 0.25 + colors[2] * 0.25 * lightness; 65 | } 66 | -------------------------------------------------------------------------------- /src/construct/zstream.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "int3.cpp" 4 | #include "indexedleaf.cpp" 5 | #include "model.cpp" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct ZFrame { 12 | const unsigned int size; 13 | const int3 offset; 14 | uint8_t i; 15 | 16 | ZFrame(const unsigned int size, const int3 offset, uint8_t i) 17 | : size(size), offset(offset), i(i) {} 18 | }; 19 | 20 | class ZStream { 21 | const Model* model; 22 | unsigned long long int index; 23 | bool _open; 24 | Leaf stream_leaf; 25 | std::stack stack; 26 | 27 | int3 next_coords() { 28 | if (stack.top().size == 1) { 29 | int3 ret(stack.top().offset); 30 | stack.pop(); 31 | while (!stack.empty() && stack.top().i >= 8) stack.pop(); 32 | if (stack.empty()) _open = false; 33 | index++; 34 | return ret; 35 | } 36 | const unsigned int size = stack.top().size / 2; 37 | int3 offset(stack.top().offset); 38 | if (stack.top().i & 4) offset.x += size; 39 | if (stack.top().i & 2) offset.y += size; 40 | if (stack.top().i & 1) offset.z += size; 41 | stack.top().i++; 42 | stack.push(ZFrame(size, offset, 0)); 43 | return next_coords(); 44 | } 45 | 46 | 47 | public: 48 | const unsigned int power; 49 | const unsigned long long int stream_size; 50 | ZStream(Model* model) : model(model), index(0), _open(true), 51 | power(std::ceil(std::log2(std::max({model->width, model->height, model->depth})))), 52 | stream_size(std::pow(2, power * 3)) { 53 | stack.push(ZFrame(1 << power, int3(0, 0, 0), 0)); 54 | stream_leaf = model->at(next_coords()); 55 | } 56 | 57 | inline bool is_open() { return _open; } 58 | 59 | inline float progress() { 60 | return (float) index / stream_size; 61 | } 62 | 63 | IndexedLeaf next() { 64 | Leaf prev_leaf = stream_leaf; 65 | while (stream_leaf == prev_leaf) { 66 | if (!is_open()) { 67 | prev_leaf = stream_leaf; 68 | index++; 69 | break; 70 | } 71 | stream_leaf = model->at(next_coords()); 72 | } 73 | return IndexedLeaf(prev_leaf, index - 1); 74 | } 75 | }; 76 | -------------------------------------------------------------------------------- /src/construct/stanford.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "model.cpp" 7 | #include "../render/leaf.cpp" 8 | #include "sep.cpp" 9 | 10 | const std::string base_path(std::string("..") + sep + std::string("models")); 11 | 12 | 13 | struct StanfordModel : Model { 14 | public: 15 | uint16_t** data; 16 | StanfordModel(std::string name, std::string prefix, 17 | unsigned int width, unsigned int height, unsigned int depth) 18 | : Model(name, width, height, depth) { 19 | data = read_model(name, prefix, height); 20 | } 21 | 22 | Leaf at(int3 pos) const override { 23 | pos.y = height - pos.y - 1; 24 | 25 | if (pos.x >= width || pos.y >= height || pos.z >= depth) { 26 | return Leaf(); 27 | } 28 | 29 | uint16_t value = data[pos.y][pos.z * depth + pos.x]; 30 | 31 | // quantize values 32 | value /= 256 * 16; 33 | value *= 16; 34 | 35 | if (value < 0xa0) value = 0; 36 | return Leaf(value, value, value, value); 37 | } 38 | 39 | ~StanfordModel() override { 40 | for (unsigned int i = 0; i < height; i++) { 41 | delete[] data[i]; 42 | } 43 | delete[] data; 44 | } 45 | 46 | private: 47 | uint16_t* read_slice(std::string model_name, 48 | std::string prefix, 49 | unsigned int slice) { 50 | 51 | std::string path = base_path + sep + model_name + 52 | sep + prefix + std::to_string(slice); 53 | std::ifstream file(path, std::ios::binary | std::ios::ate); 54 | std::streamsize size = file.tellg(); 55 | if (size < 0) { 56 | std::cerr << "File '" << path << "' does not exist!\n"; 57 | throw std::runtime_error("File does not exist."); 58 | } 59 | file.seekg(0, std::ios::beg); 60 | char* buffer = new char[size]; 61 | if (file.read((char*) buffer, size)) { 62 | return (uint16_t*) buffer; 63 | } 64 | throw std::runtime_error("Error reading file " + path); 65 | } 66 | 67 | uint16_t** read_model 68 | (std::string model_name, 69 | std::string prefix, 70 | unsigned int num_slices) { 71 | uint16_t** buffer = new uint16_t*[num_slices]; 72 | for (unsigned int i = 0; i < num_slices; i++) { 73 | buffer[i] = read_slice(model_name, prefix, i+1); 74 | } 75 | return buffer; 76 | } 77 | }; 78 | -------------------------------------------------------------------------------- /src/render/block.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | struct Block { 6 | private: 7 | const size_t element_count; 8 | char* _data = nullptr; 9 | char* front = nullptr; 10 | size_t front_index; 11 | 12 | public: 13 | static const std::size_t ELEMENT_SIZE = 4; 14 | static const std::string EXTENSION; 15 | 16 | Block(size_t element_count, bool full = false) : 17 | element_count(element_count) { 18 | const size_t size = element_count * ELEMENT_SIZE; 19 | _data = new char[size]; 20 | if (full) { 21 | front = _data + size; 22 | front_index = element_count; 23 | } else { 24 | front = _data; 25 | front_index = 0; 26 | } 27 | } 28 | 29 | ~Block() { 30 | delete[] _data; 31 | } 32 | 33 | inline void read_from_stream(std::ifstream& stream, std::streamsize size) { 34 | stream.read(_data, size); 35 | } 36 | 37 | void to_file(std::string filename) { 38 | filename += EXTENSION; 39 | std::ofstream stream(filename, std::ios::binary); 40 | if (stream.good()) { 41 | stream.write(_data, size() * ELEMENT_SIZE); 42 | stream.write((char*) &front_index, sizeof(size_t)); 43 | std::cout << "File '" << filename << "' saved to disk.\n"; 44 | } else { 45 | std::cout << "Could not open file '" << filename << ".\n"; 46 | } 47 | } 48 | 49 | template 50 | T& at(const int32_t index) const { 51 | return *((T*) (_data + index * ELEMENT_SIZE)); 52 | } 53 | 54 | template 55 | inline T& back() const { 56 | return at(size() - 1); 57 | } 58 | 59 | inline size_t size() const { 60 | return front_index; 61 | } 62 | 63 | inline size_t byte_size() const { 64 | return front_index * ELEMENT_SIZE; 65 | } 66 | 67 | inline size_t capacity() const { 68 | return element_count; 69 | } 70 | 71 | inline char* data() const { 72 | return _data; 73 | } 74 | 75 | }; 76 | 77 | const std::string Block::EXTENSION = ".zaz"; 78 | 79 | Block* from_file(std::string filename) { 80 | std::ifstream stream(filename, std::ios::binary | std::ios::ate); 81 | 82 | std::streamsize size = stream.tellg(); 83 | stream.seekg(0, std::ios::beg); 84 | 85 | Block* block = new Block(size / Block::ELEMENT_SIZE, true); 86 | block->read_from_stream(stream, size); 87 | 88 | std::cout << "File \"" << filename << "\" loaded from disk.\n"; 89 | return block; 90 | } 91 | -------------------------------------------------------------------------------- /src/render/render.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "leaf.cpp" 7 | #include "ray.cpp" 8 | #include "stack.cpp" 9 | #include "block.cpp" 10 | 11 | const unsigned int WIDTH = 1280; 12 | const unsigned int HEIGHT = 720; 13 | 14 | const float fov = 1.2; // 1 -> 90 degrees 15 | const Vector scale(1, 1, 1); 16 | Ray cam_center; 17 | 18 | Block* block = nullptr; 19 | 20 | void render(unsigned char* pixel, int i, int j) { 21 | const float screen_x = (i * fov) / (float) WIDTH - 0.5; 22 | const float screen_y = (j * fov) / (float) HEIGHT - 0.5; 23 | 24 | /* Start traversing on root voxel. */ 25 | Vector direction(screen_x * scale.x, -1 * scale.y, screen_y * scale.z); 26 | 27 | Ray ray(cam_center.origin, direction); 28 | 29 | VoxelStack stack(20, 2.0); 30 | stack.push_root(&block->back(), Vector(-1, -1, -1), ray); 31 | 32 | pixel[0] = 0x33; 33 | pixel[1] = 0x33; 34 | pixel[2] = 0x00; 35 | 36 | while (true) { 37 | 38 | const uint8_t oct = stack->octant; 39 | bool valid = (stack->voxel->valid >> oct) & 1; 40 | bool leaf = (stack->voxel->leaf >> oct) & 1; 41 | if (leaf) { 42 | /* Ray origin is inside leaf voxel, render leaf. */ 43 | Leaf* leaf = &block->at(stack->voxel->address_of(oct)); 44 | float lightness = 1/(ray.square_distance() + 1); 45 | leaf->set_color(pixel, lightness); 46 | break; 47 | } 48 | 49 | if (valid) { 50 | /* Go a level deeper. */ 51 | stack.push(&block->at(stack->voxel->address_of(oct)), ray); 52 | 53 | pixel[1] += (0xff - pixel[1]) / 64; 54 | 55 | } else { 56 | /* Ray origin is in invalid voxel, cast ray until it hits next 57 | * voxel. 58 | */ 59 | Vector child_corner(stack->corner); 60 | 61 | float child_size = stack.box_size * 0.5; 62 | child_corner.adjust_corner(child_size, oct); 63 | uint8_t mask = ray.octant_mask(); 64 | 65 | Vector mirror_origin = ray.origin.mirror(mask); 66 | Vector mirror_direction = ray.direction.mirror(mask); 67 | Vector mirror_corner = child_corner.mirror(mask); 68 | mirror_corner.adjust_corner(-child_size, mask); 69 | 70 | float tx = (mirror_corner.x - mirror_origin.x) / mirror_direction.x; 71 | float ty = (mirror_corner.y - mirror_origin.y) / mirror_direction.y; 72 | float tz = (mirror_corner.z - mirror_origin.z) / mirror_direction.z; 73 | float t = std::numeric_limits::infinity(); 74 | 75 | /* Detect which face hit. */ 76 | uint8_t hit_face = 0; 77 | 78 | /* t is the smallest positive value of {tx, ty, tz} */ 79 | if (tx > 0) { 80 | t = tx; 81 | hit_face = 4; 82 | } 83 | if (ty > 0 && ty < t) { 84 | t = ty; 85 | hit_face = 2; 86 | } 87 | if (tz > 0 && tz < t) { 88 | t = tz; 89 | hit_face = 1; 90 | } 91 | 92 | /* Ray will start next step at the point of this intersection */ 93 | ray.march(t); 94 | 95 | while (hit_face & ~(stack->octant ^ mask) && !stack.empty()) { 96 | /* Hit face is at this voxel's boundary, search parent */ 97 | stack.pop(); 98 | } 99 | 100 | if (stack.empty()) { 101 | /* Ray is outside root octree. */ 102 | 103 | // EMPTY STACK AFTER LOOP - BLUE 104 | pixel[2] = 0xc0; 105 | break; 106 | } 107 | /* Loop end: found ancestral voxel with space on the hit axis. 108 | * Transfer to sibling voxel, changing on the axis of the face 109 | * that was hit. 110 | */ 111 | stack->octant ^= hit_face; 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/construct/construct.cpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../render/voxel.cpp" 4 | #include "indexedleaf.cpp" 5 | #include "writer.cpp" 6 | #include 7 | #include 8 | 9 | struct Node { 10 | union { 11 | Voxel voxel; 12 | Leaf leaf; 13 | }; 14 | 15 | bool is_leaf; 16 | bool is_valid; 17 | explicit Node() : is_leaf(false), is_valid(false) {} 18 | explicit Node(const Leaf& leaf) : leaf(leaf), is_leaf(true), is_valid(true) {} 19 | explicit Node(const Voxel& voxel) : voxel(voxel), is_leaf(false), is_valid(true) {} 20 | }; 21 | 22 | class NodeQueue { 23 | Node data[8]; 24 | uint8_t size = 0; 25 | 26 | public: 27 | void push(const Node& node) { 28 | data[size] = node; 29 | size++; 30 | } 31 | 32 | bool full() const { 33 | return size == 8; 34 | } 35 | 36 | bool empty() const { 37 | return !size; 38 | } 39 | 40 | void clear() { 41 | size = 0; 42 | } 43 | 44 | Node& operator[](uint8_t index) { 45 | return data[index]; 46 | } 47 | 48 | std::string s() { 49 | std::string ret = ""; 50 | for (auto i = 0; i < 8; i++) { 51 | if (i >= size) 52 | ret += " "; 53 | else if (data[i].is_leaf) 54 | ret += "L"; 55 | else if (data[i].is_valid) 56 | ret += "V"; 57 | else 58 | ret += "_"; 59 | } 60 | return ret; 61 | } 62 | }; 63 | 64 | class Builder { 65 | NodeQueue* queues; 66 | const unsigned int queue_count; 67 | unsigned long long int stream_index = 0; 68 | BlockWriter& writer; 69 | 70 | void s() { 71 | std::cout << "\nIndex " << stream_index << "\n"; 72 | for (unsigned int i = 0; i < queue_count; i++) 73 | std::cout << "|" << queues[i].s() << "|\n"; 74 | return; 75 | } 76 | 77 | void collapse(unsigned int i) { 78 | /* Collapse full queue i to a voxel in queue (i + 1). */ 79 | 80 | /* Queue is not full, no need to collapse */ 81 | if (!queues[i].full()) return; 82 | 83 | NodeQueue& child_queue = queues[i]; 84 | 85 | /* If parent is non-leaf, this voxel will be inserted. */ 86 | Voxel parent; 87 | parent.valid = 0; 88 | parent.leaf = 0; 89 | parent.child = writer.pos(); 90 | 91 | /* True at the end when all 8 nodes are the same leaf. 92 | * Therefore parent will also be that leaf. 93 | */ 94 | bool parent_is_leaf = true; 95 | 96 | for (unsigned int j = 0; j < 8; j++) { 97 | /* For each node j in this queue: */ 98 | 99 | /* Alias for current node */ 100 | const Node& node = child_queue[j]; 101 | 102 | if (node.is_valid) { 103 | /* Valid node, mark parent mask. */ 104 | parent.valid ^= 1 << j; 105 | } else { 106 | /* Invalid node, go to next iteration. */ 107 | parent_is_leaf = false; 108 | continue; 109 | } 110 | 111 | if (node.is_leaf) { 112 | /* Leaf node, mark parent mask. */ 113 | parent.leaf ^= 1 << j; 114 | 115 | if (parent_is_leaf && child_queue[0].leaf != node.leaf) { 116 | /* Node is a different leaf. */ 117 | parent_is_leaf = false; 118 | } 119 | } else { 120 | /* Non-leaf node, thus parent is not leaf. */ 121 | parent_is_leaf = false; 122 | } 123 | } 124 | i++; /* Now working with the (i + 1) queue. */ 125 | 126 | Node to_push; 127 | if (parent_is_leaf) { 128 | /* Parent is leaf, enqueue leaf. */ 129 | to_push = Node(child_queue[0].leaf); 130 | } else if (!parent.valid) { 131 | /* No valid children, thus parent is invalid. */ 132 | to_push = Node(); 133 | } else { 134 | /* Parent is valid and non-leaf, write children and 135 | * enqueue parent voxel. 136 | */ 137 | for (unsigned int j = 0; j < 8; j++) { 138 | Node& node = child_queue[j]; 139 | if (node.is_leaf) 140 | writer << node.leaf; 141 | else if (node.is_valid) 142 | writer << node.voxel; 143 | } 144 | to_push = Node(parent); 145 | } 146 | 147 | if (i < queue_count) push_at(to_push, i); // push to parent 148 | else writer << to_push.voxel; // out of queues, push root voxel 149 | 150 | /* All children have been collapsed, therefore clear. */ 151 | child_queue.clear(); 152 | } 153 | 154 | inline void push_at(const Node& node, const uint8_t index) { 155 | queues[index].push(node); 156 | collapse(index); 157 | } 158 | 159 | 160 | public: 161 | Builder(const unsigned int queue_count, BlockWriter& writer) 162 | : queue_count(queue_count), writer(writer) { 163 | queues = new NodeQueue[queue_count]; 164 | } 165 | 166 | ~Builder() { 167 | delete[] queues; 168 | } 169 | 170 | void add_leaf(const IndexedLeaf& ileaf) { 171 | 172 | unsigned int leaf_count = ileaf.index - stream_index; 173 | while (leaf_count > 0) { 174 | /* Push invalid nodes so the index catches up with the stream. */ 175 | unsigned int power = 0; 176 | 177 | if (leaf_count >= 8) { 178 | power = std::floor(std::log(leaf_count) / std::log(8)); 179 | } 180 | 181 | unsigned int non_empty_i = queue_count; 182 | 183 | for (unsigned int i = 0; i < 8; i++) { 184 | if (!queues[i].empty()) { 185 | non_empty_i = i; 186 | break; 187 | } 188 | } 189 | 190 | unsigned int insert_i = std::min(power, non_empty_i); 191 | if (insert_i >= queue_count) { 192 | std::cout 193 | << "\n\tERROR: completely homogenous model!" 194 | << "\n\tConstruction aborted.\n\n\a"; 195 | writer << ileaf.leaf; 196 | throw "Homogenous model"; 197 | } 198 | 199 | Node to_push = ileaf.leaf.valid() ? Node(ileaf.leaf) : Node(); 200 | push_at(to_push, insert_i); 201 | leaf_count -= std::pow(8, insert_i); 202 | } 203 | 204 | stream_index = ileaf.index; 205 | } 206 | }; 207 | -------------------------------------------------------------------------------- /src/render/shaders/svo.frag: -------------------------------------------------------------------------------- 1 | #version 430 core 2 | 3 | uniform uvec2 viewportSize; 4 | uniform vec3 camPos; 5 | uniform float time; 6 | uniform uint modelSize; 7 | uniform mat3 camRot; 8 | uniform float xSection; 9 | in vec4 gl_FragCoord; 10 | 11 | layout (location = 0) out vec4 outColor; 12 | layout (location = 3) out vec4 outPosition; 13 | 14 | layout (binding = 2, std430) buffer svo { 15 | uint[] data; 16 | }; 17 | 18 | uint whichOctant(in vec3 pos, in vec3 corner, in float size) { 19 | /* Which of the eight octants does pos reside in the box (corner, size)? */ 20 | uint oct = 0; 21 | float octSize = size / 2.; 22 | 23 | if (pos.x > corner.x + octSize) oct ^= 4; 24 | if (pos.y > corner.y + octSize) oct ^= 2; 25 | if (pos.z > corner.z + octSize) oct ^= 1; 26 | return oct; 27 | } 28 | 29 | vec3 octVec(in uint octant) { 30 | /* Each resulting vector component is either one or zero depending on 31 | * octant's bits. */ 32 | vec3 vec = vec3(0.); 33 | if (bool(octant & 4)) vec.x = 1.; 34 | if (bool(octant & 2)) vec.y = 1.; 35 | if (bool(octant & 1)) vec.z = 1.; 36 | return vec; 37 | } 38 | 39 | void voxel(uint block_i, out uint child, out uint leaf, out uint valid) { 40 | child = data[block_i]; 41 | leaf = data[block_i + 1] >> 8; 42 | valid = data[block_i + 1] & 0xff; 43 | } 44 | 45 | vec4 leafColor(in uint block_i) { 46 | uint rgba = data[block_i]; 47 | return vec4( 48 | float(rgba & 0xff) / 256., 49 | float((rgba >> 8) & 0xff) / 256., 50 | float((rgba >> 16) & 0xff) / 256., 51 | float((rgba >> 24) & 0xff) / 256. 52 | ); 53 | } 54 | 55 | uint addressOf(in uint child, in uint leaf, in uint valid, in uint octant) { 56 | /* Get address of a specific child inside a voxel. */ 57 | uint mask = ~(~0 << octant); 58 | return child + bitCount(mask & valid) * 2 - bitCount(mask & leaf); 59 | } 60 | 61 | void main() { 62 | /* Initialize ray. */ 63 | float fov = 0.6; 64 | vec2 uv = (gl_FragCoord.xy * 2. / viewportSize.y) - vec2(1.); 65 | vec3 direction = camRot * vec3(uv * fov, 1.); 66 | vec3 position = camPos; 67 | 68 | /* Set up stack. */ 69 | uint size = 1; // size of stack 70 | float boxSize = 2.0; // size of box ray is currently in 71 | 72 | /* Stack attributes */ 73 | uint child[10]; 74 | uint leaf[10]; 75 | uint valid[10]; 76 | vec3 corner[10]; 77 | uint octant[10]; 78 | 79 | /* Set root stack entry */ 80 | voxel(modelSize - 2, child[0], leaf[0], valid[0]); 81 | corner[0] = vec3(-1); 82 | 83 | /* Assume ray direction does not change during execution 84 | * (no refraction / reflection) 85 | */ 86 | uint mask = 0; 87 | vec3 invdir = 1. / direction; 88 | if (invdir.x >= 0) mask ^= 4; 89 | if (invdir.y >= 0) mask ^= 2; 90 | if (invdir.z >= 0) mask ^= 1; 91 | vec3 maskVec = octVec(mask); 92 | vec3 mirror = vec3(1.) - maskVec * 2.; 93 | 94 | float cutSize = boxSize * xSection; 95 | if (any(lessThan(position, corner[0])) 96 | || any(greaterThan(position, corner[0] + vec3(cutSize)))) { 97 | /* Before the first iteration, if the camera is outside the scenes's 98 | * bounding box, the traversal starts at the intersection between the 99 | * ray and the box. 100 | */ 101 | vec3 lowCorner = corner[0] + cutSize * (vec3(1.) - maskVec); 102 | vec3 highCorner = corner[0] + cutSize * maskVec; 103 | vec3 tMin = (lowCorner - position) * invdir; 104 | vec3 tMax = (highCorner - position) * invdir; 105 | 106 | if (any(greaterThan(tMin, tMax.zxy)) 107 | || any(greaterThan(tMin, tMax.yzx))) { 108 | /* No intersection */ 109 | outColor = vec4(0.); 110 | outPosition = vec4(0.); 111 | return; 112 | } 113 | 114 | float t = max(max(tMin.x, tMin.y), tMin.z); 115 | position += direction * t; 116 | 117 | if (t < 0.) { 118 | outColor = vec4(0.); 119 | outPosition = vec4(0.); 120 | return; 121 | } 122 | } 123 | 124 | octant[0] = whichOctant(position, vec3(-1), boxSize); 125 | 126 | while (true) { 127 | uint oct = octant[size-1]; 128 | if (bool((leaf[size-1] >> oct) & 1)) { 129 | /* Ray origin is inside leaf voxel, render leaf. */ 130 | outColor = leafColor( 131 | addressOf( 132 | child[size-1], 133 | leaf [size-1], 134 | valid[size-1], 135 | oct 136 | ) 137 | ); 138 | outPosition = vec4(position, 1.0); 139 | return; 140 | } 141 | 142 | if (bool((valid[size-1] >> oct) & 1)) { 143 | /* Go a level deeper. */ 144 | 145 | // PUSH 146 | voxel( 147 | addressOf( 148 | child[size-1], 149 | leaf [size-1], 150 | valid[size-1], 151 | oct 152 | ), 153 | child[size], 154 | leaf[size], 155 | valid[size] 156 | ); 157 | 158 | boxSize *= 0.5; 159 | corner[size] = corner[size-1] + boxSize * octVec(oct); 160 | octant[size] = whichOctant( 161 | position, 162 | corner[size], 163 | boxSize 164 | ); 165 | 166 | size++; 167 | 168 | } else { 169 | /* Ray origin is in invalid voxel, cast ray until it hits the next 170 | * voxel. 171 | */ 172 | 173 | float childSize = boxSize * 0.5; 174 | vec3 childCorner = corner[size-1] + childSize * octVec(oct); 175 | 176 | vec3 adjustedCorner = childCorner - maskVec * childSize * mirror; 177 | 178 | vec3 t = (adjustedCorner - position) * invdir; 179 | float amount = 99999999999.; // Distance ray will traverse 180 | 181 | /* Detect which face hit. */ 182 | uint hitFace = 0; 183 | 184 | /* amount will be the minimum positive component of t. */ 185 | 186 | if (t.x > 0.) { 187 | amount = t.x; 188 | hitFace = 4; 189 | } 190 | if (t.y > 0. && t.y < amount) { 191 | amount = t.y; 192 | hitFace = 2; 193 | } 194 | if (t.z > 0. && t.z < amount) { 195 | amount = t.z; 196 | hitFace = 1; 197 | } 198 | 199 | /* Ray will start next step at the point of this intersection. */ 200 | position += direction * amount; 201 | 202 | while ( 203 | bool(hitFace & ~(octant[size-1] ^ mask)) && 204 | size > 0 205 | ) { 206 | /* Hit face is at this voxel's boundary, search parent */ 207 | 208 | // POP 209 | size--; 210 | boxSize *= 2.; 211 | } 212 | 213 | if (size == 0) { 214 | /* Ray is outside root octree. */ 215 | outColor = vec4(0.0); 216 | outPosition = vec4(direction, 0.); 217 | return; 218 | } 219 | /* Loop end: found ancestral voxel with space on the hit axis. 220 | * Transfer to sibling voxel, changing on the axis of the face 221 | * that was hit. 222 | */ 223 | octant[size-1] ^= hitFace; 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/render/main.cpp: -------------------------------------------------------------------------------- 1 | #define SDL_MAIN_HANDLED 2 | #define GLEW_STATIC 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #include "ray.cpp" 21 | #include "block.cpp" 22 | 23 | unsigned int width, height, upscale; 24 | glm::vec3 camPosition(0.123, 0.213, -3.193); 25 | glm::mat3 camRotation(1.0); 26 | float crossSection = 1; 27 | 28 | const float camSpeed = 0.02; 29 | const float lookSpeed = 0.06; 30 | const float sectionSpeed = 0.002; 31 | float pitch = 0; // -89 to 89 32 | float yaw = 90; // 0 to 360 33 | 34 | std::string read_file(std::string filename) { 35 | std::string content; 36 | std::ifstream stream(filename, std::ios::in); 37 | 38 | if(!stream.is_open()) { 39 | std::cerr << "Could not read file " << filename 40 | << ". File does not exist." << std::endl; 41 | return ""; 42 | } 43 | 44 | std::string line = ""; 45 | while(!stream.eof()) { 46 | std::getline(stream, line); 47 | content.append(line + "\n"); 48 | } 49 | 50 | return content; 51 | } 52 | 53 | bool compile_shader(GLuint shader, std::string filename) { 54 | // Read shaders from file 55 | std::string source_str = read_file(filename); 56 | const char* source = source_str.c_str(); 57 | 58 | char buffer[512]; 59 | GLint status; 60 | 61 | // Setup vertex shader 62 | glShaderSource(shader, 1, &source, NULL); 63 | 64 | glCompileShader(shader); 65 | glGetShaderiv(shader, GL_COMPILE_STATUS, &status); 66 | 67 | glGetShaderInfoLog(shader, 512, NULL, buffer); 68 | 69 | if (status != 1) { 70 | std::cout << "Error compiling '" << filename << "':\n" << buffer; 71 | return false; 72 | } 73 | return true; 74 | } 75 | 76 | void GLAPIENTRY glDebugOutput( 77 | GLenum source, 78 | GLenum type, 79 | GLuint id, 80 | GLenum severity, 81 | GLsizei length, 82 | const GLchar* message, 83 | const void* userParam 84 | ) { 85 | //std::cout << message << std::endl; 86 | } 87 | 88 | void setup_tex(GLuint& texture, GLuint attachment, GLenum interp, GLenum format, GLenum internal) { 89 | glGenTextures(1, &texture); 90 | 91 | glActiveTexture(GL_TEXTURE0 + attachment); 92 | glBindTexture(GL_TEXTURE_2D, texture); 93 | 94 | glTexImage2D( 95 | GL_TEXTURE_2D, 96 | 0, 97 | internal, 98 | width / upscale, 99 | height / upscale, 100 | 0, 101 | GL_RGBA, 102 | format, 103 | 0 104 | ); 105 | 106 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, interp); 107 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, interp); 108 | 109 | glFramebufferTexture2D( 110 | GL_FRAMEBUFFER, 111 | GL_COLOR_ATTACHMENT0 + attachment, 112 | GL_TEXTURE_2D, 113 | texture, 114 | 0 115 | ); 116 | } 117 | 118 | int main(int argc, char **argv) { 119 | unsigned int arg = 1; 120 | const std::string filename = arg >= argc ? "sponge.zaz" : argv[arg++]; 121 | 122 | Block* block = from_file(filename); 123 | 124 | width = arg >= argc ? 1280 : atoi(argv[arg++]); 125 | height = arg >= argc ? 720 : atoi(argv[arg++]); 126 | upscale = arg >= argc ? 1 : atoi(argv[arg++]); 127 | 128 | SDL_Init(SDL_INIT_VIDEO); 129 | atexit(SDL_Quit); 130 | 131 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 132 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 133 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); 134 | SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); 135 | 136 | 137 | SDL_Window* window = SDL_CreateWindow( 138 | "zazen", 139 | SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 140 | width, height, 141 | SDL_WINDOW_OPENGL 142 | ); 143 | 144 | SDL_GLContext context = SDL_GL_CreateContext(window); 145 | SDL_SetRelativeMouseMode(SDL_TRUE); 146 | 147 | glewExperimental = GL_TRUE; 148 | glewInit(); 149 | 150 | glEnable(GL_DEBUG_OUTPUT); 151 | glDebugMessageCallback(glDebugOutput, 0); 152 | 153 | float vertices[] = { 154 | -1.0f, -1.0f, // Vertex 1 (X, Y) 155 | 3.0f, -1.0f, // Vertex 2 (X, Y) 156 | -1.0f, 3.0f // Vertex 3 (X, Y) 157 | }; 158 | 159 | // Create Shader Storage Buffer Object that will store SVO data 160 | GLuint ssbo; 161 | glGenBuffers(1, &ssbo); 162 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); 163 | glBufferData( 164 | GL_SHADER_STORAGE_BUFFER, 165 | block->byte_size(), 166 | block->data(), 167 | GL_DYNAMIC_READ 168 | ); 169 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, ssbo); 170 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); // Unbind 171 | 172 | // Create vertex array object 173 | GLuint vao; 174 | glGenVertexArrays(1, &vao); 175 | glBindVertexArray(vao); 176 | 177 | // Create vertex buffer object 178 | GLuint vbo; 179 | glGenBuffers(1, &vbo); 180 | glBindBuffer(GL_ARRAY_BUFFER, vbo); 181 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 182 | 183 | // Create intermediate frame buffer object 184 | GLuint fbo; 185 | glGenFramebuffers(1, &fbo); 186 | glBindFramebuffer(GL_FRAMEBUFFER, fbo); 187 | 188 | GLenum drawBuffers[] = {GL_COLOR_ATTACHMENT0, GL_NONE, GL_NONE, GL_COLOR_ATTACHMENT3}; 189 | glDrawBuffers(4, drawBuffers); 190 | 191 | // Attach textures to FBO 192 | GLuint colorTexture; 193 | GLuint positionTexture; 194 | 195 | setup_tex(colorTexture, 0, GL_NEAREST, GL_UNSIGNED_BYTE, GL_RGBA); 196 | setup_tex(positionTexture, 3, GL_NEAREST, GL_FLOAT, GL_RGBA32F); 197 | 198 | 199 | if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { 200 | std::cout << "FBO incomplete!"; 201 | return 1; 202 | } 203 | 204 | // Allocate shaders and assemble program 205 | GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); 206 | GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 207 | GLuint lightShader = glCreateShader(GL_FRAGMENT_SHADER); 208 | 209 | GLuint shaderProgram = glCreateProgram(); 210 | GLuint lightProgram = glCreateProgram(); 211 | 212 | glAttachShader(shaderProgram, vertexShader); 213 | glAttachShader(shaderProgram, fragmentShader); 214 | 215 | glAttachShader(lightProgram, vertexShader); 216 | glAttachShader(lightProgram, lightShader); 217 | 218 | if ( 219 | compile_shader(vertexShader, "svo.vert") && 220 | compile_shader(fragmentShader, "svo.frag") && 221 | compile_shader(lightShader, "light.frag") 222 | ) { 223 | /* 224 | glBindFragDataLocation(shaderProgram, 0, "outColor"); 225 | glBindFragDataLocation(shaderProgram, 1, "outPosition"); 226 | */ 227 | glLinkProgram(shaderProgram); 228 | glLinkProgram(lightProgram); 229 | } 230 | 231 | 232 | // Specify position vertex attribute 233 | 234 | glUseProgram(shaderProgram); 235 | { 236 | GLint posAttrib = glGetAttribLocation(shaderProgram, "position"); 237 | glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0); 238 | glEnableVertexAttribArray(posAttrib); 239 | } 240 | 241 | { 242 | GLint viewportSize = glGetUniformLocation(shaderProgram, "viewportSize"); 243 | GLint modelSize = glGetUniformLocation(shaderProgram, "modelSize"); 244 | glUniform2ui(viewportSize, width / upscale, height / upscale); 245 | glUniform1ui(modelSize, block->size()); 246 | 247 | } 248 | 249 | GLint time = glGetUniformLocation(shaderProgram, "time"); 250 | GLint xSection = glGetUniformLocation(shaderProgram, "xSection"); 251 | GLint camPos = glGetUniformLocation(shaderProgram, "camPos"); 252 | GLint camRot = glGetUniformLocation(shaderProgram, "camRot"); 253 | 254 | glUseProgram(lightProgram); 255 | { 256 | GLint viewportSize = glGetUniformLocation(lightProgram, "viewportSize"); 257 | GLint upscaleLoc = glGetUniformLocation(lightProgram, "upscale"); 258 | GLint colorLoc = glGetUniformLocation(lightProgram, "colorTexture"); 259 | GLint positionLoc = glGetUniformLocation(lightProgram, "positionTexture"); 260 | 261 | glActiveTexture(GL_TEXTURE0); 262 | glBindTexture(GL_TEXTURE_2D, colorTexture); 263 | glUniform1i(colorLoc, 0); 264 | 265 | glActiveTexture(GL_TEXTURE0 + 3); 266 | glBindTexture(GL_TEXTURE_2D, positionTexture); 267 | glUniform1i(positionLoc, 3); 268 | 269 | glUniform2ui(viewportSize, width, height); 270 | glUniform1ui(upscaleLoc, upscale); 271 | 272 | } 273 | 274 | glDisable(GL_DEPTH_TEST); 275 | 276 | SDL_Event event; 277 | 278 | bool running = true; 279 | unsigned long long timer = clock(); 280 | glm::vec3 deltaCamPos(0); 281 | float deltaSection = 0; 282 | 283 | for (unsigned int tick = 0; running; tick++) { 284 | while (SDL_PollEvent(&event)) { 285 | if (event.type == SDL_QUIT) { 286 | running = false; 287 | } else if (event.type == SDL_KEYDOWN) { 288 | switch (event.key.keysym.sym) { 289 | case SDLK_UP: 290 | case SDLK_w: 291 | deltaCamPos.z = camSpeed; 292 | break; 293 | case SDLK_DOWN: 294 | case SDLK_s: 295 | deltaCamPos.z = -camSpeed; 296 | break; 297 | case SDLK_LEFT: 298 | case SDLK_a: 299 | deltaCamPos.x = -camSpeed; 300 | break; 301 | case SDLK_RIGHT: 302 | case SDLK_d: 303 | deltaCamPos.x = camSpeed; 304 | break; 305 | case SDLK_LSHIFT: 306 | deltaCamPos.y = -camSpeed; 307 | break; 308 | case SDLK_SPACE: 309 | deltaCamPos.y = camSpeed; 310 | break; 311 | case SDLK_q: 312 | deltaSection = -sectionSpeed; 313 | break; 314 | case SDLK_e: 315 | deltaSection = sectionSpeed; 316 | break; 317 | } 318 | } else if (event.type == SDL_KEYUP) { 319 | switch (event.key.keysym.sym) { 320 | case SDLK_UP: 321 | case SDLK_DOWN: 322 | case SDLK_w: 323 | case SDLK_s: 324 | deltaCamPos.z = 0; 325 | break; 326 | case SDLK_LEFT: 327 | case SDLK_RIGHT: 328 | case SDLK_a: 329 | case SDLK_d: 330 | deltaCamPos.x = 0; 331 | break; 332 | case SDLK_LSHIFT: 333 | case SDLK_SPACE: 334 | deltaCamPos.y = 0; 335 | break; 336 | case SDLK_q: 337 | case SDLK_e: 338 | deltaSection = 0; 339 | break; 340 | } 341 | } else if (event.type == SDL_MOUSEMOTION) { 342 | yaw += event.motion.xrel * lookSpeed; 343 | pitch += event.motion.yrel * lookSpeed; 344 | yaw = std::fmod(yaw, 360.0); 345 | if (pitch < -89.0) pitch = -89.0; 346 | if (pitch > 89.0) pitch = 89.0; 347 | glm::vec3 pitchDir( 348 | 0, 349 | sin(glm::radians(pitch)), 350 | cos(glm::radians(pitch)) 351 | ); 352 | pitchDir = glm::normalize(pitchDir); 353 | glm::vec3 yawDir( 354 | cos(glm::radians(yaw)), 355 | 0, 356 | sin(glm::radians(yaw)) 357 | ); 358 | yawDir = glm::normalize(yawDir); 359 | 360 | camRotation = glm::lookAt( 361 | glm::vec3(0), 362 | yawDir, 363 | glm::vec3(0,1,0) 364 | ) * glm::lookAt( 365 | glm::vec3(0), 366 | pitchDir, 367 | glm::vec3(0,1,0) 368 | ); 369 | } 370 | } 371 | 372 | camPosition += camRotation * deltaCamPos; 373 | crossSection += deltaSection; 374 | if (crossSection < 0) crossSection = 0; 375 | if (crossSection > 1) crossSection = 1; 376 | 377 | // First pass 378 | glUseProgram(shaderProgram); 379 | glBindFramebuffer(GL_FRAMEBUFFER, fbo); 380 | 381 | glUniform1f(time, 0); 382 | glUniform1f(xSection, crossSection); 383 | glUniform3fv(camPos, 1, &camPosition[0]); 384 | glUniformMatrix3fv(camRot, 1, GL_FALSE, &camRotation[0][0]); 385 | 386 | glDrawArrays(GL_TRIANGLES, 0, 3); 387 | 388 | // Second pass 389 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 390 | 391 | glUseProgram(lightProgram); 392 | glDrawArrays(GL_TRIANGLES, 0, 3); 393 | 394 | SDL_GL_SwapWindow(window); 395 | 396 | if (tick == 500 && false) { 397 | timer = clock() - timer; 398 | std::cout << timer << "\n"; 399 | std::cout << width << "X" << height << " " << upscale << "\n" 400 | << "Average FPS of " << 500.0 / (float(timer) / CLOCKS_PER_SEC) << "\n\n"; 401 | break; 402 | } 403 | 404 | } 405 | 406 | // Clean up and exit 407 | SDL_GL_DeleteContext(context); 408 | SDL_DestroyWindow(window); 409 | SDL_Quit(); 410 | return 0; 411 | } 412 | --------------------------------------------------------------------------------