| @@ -0,0 +1,5 @@ | |||||
| .DS_Store | |||||
| /build/* | |||||
| /dist/* | |||||
| !/dist/*.xyz | |||||
| !/dist/*.txt | |||||
| @@ -0,0 +1,6 @@ | |||||
| project(cloud_tracer) | |||||
| file(GLOB SOURCES "src/*.cpp") | |||||
| set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/dist) | |||||
| add_executable(cloud_tracer ${SOURCES}) | |||||
| @@ -0,0 +1,33 @@ | |||||
| #pragma once | |||||
| #include <functional> | |||||
| template<typename F> | |||||
| class defer_finalizer { | |||||
| F f; | |||||
| bool moved; | |||||
| public: | |||||
| template<typename T> | |||||
| defer_finalizer(T && f_) : f(std::forward<T>(f_)), moved(false) { } | |||||
| defer_finalizer(const defer_finalizer &) = delete; | |||||
| defer_finalizer(defer_finalizer && other) : f(std::move(other.f)), moved(other.moved) { | |||||
| other.moved = true; | |||||
| } | |||||
| ~defer_finalizer() { | |||||
| if (!moved) f(); | |||||
| } | |||||
| }; | |||||
| static struct { | |||||
| template<typename F> | |||||
| defer_finalizer<F> operator<<(F && f) { | |||||
| return defer_finalizer<F>(std::forward<F>(f)); | |||||
| } | |||||
| } deferrer; | |||||
| #define concat(x, y) x ## y | |||||
| #define label(x, y) concat(x, y) | |||||
| #define defer auto label(__deferred_lambda_call, __COUNTER__) = deferrer << [&] | |||||
| @@ -0,0 +1,307 @@ | |||||
| #include "dt_pathtrace.hpp" | |||||
| #include <cstdio> | |||||
| #include <cmath> | |||||
| #define INFO(...) printf("INFO: " __VA_ARGS__); | |||||
| #ifndef pi | |||||
| # define pi 3.1415926535897932384626433832795 | |||||
| # define piOverTwo 1.5707963267948966192313216916398 | |||||
| # define inverseOfPi 0.31830988618379067153776752674503 | |||||
| # define inverseOfTwoPi 0.15915494309189533576888376337251 | |||||
| # define two_pi 6.283185307179586476925286766559 | |||||
| #endif | |||||
| namespace Random { | |||||
| struct rng_state { | |||||
| uint32_t x; | |||||
| uint32_t y; | |||||
| uint32_t z; | |||||
| uint32_t w; | |||||
| }; | |||||
| static rng_state RNG_STATE; | |||||
| uint TausStep(uint z, int S1, int S2, int S3, uint M) { | |||||
| uint b = (((z << S1) ^ z) >> S2); | |||||
| return ((z & M) << S3) ^ b; | |||||
| } | |||||
| uint LCGStep(uint z, uint A, uint C) { | |||||
| return A * z + C; | |||||
| } | |||||
| float HybridTaus() { | |||||
| RNG_STATE.x = TausStep(RNG_STATE.x, 13, 19, 12, 4294967294); | |||||
| RNG_STATE.y = TausStep(RNG_STATE.y, 2, 25, 4, 4294967288); | |||||
| RNG_STATE.z = TausStep(RNG_STATE.z, 3, 11, 17, 4294967280); | |||||
| RNG_STATE.w = LCGStep(RNG_STATE.w, 1664525, 1013904223); | |||||
| return 2.3283064365387e-10 * (RNG_STATE.x ^ RNG_STATE.y ^ RNG_STATE.z ^ RNG_STATE.w); | |||||
| } | |||||
| float random() { | |||||
| return HybridTaus(); | |||||
| } | |||||
| void InitRNG(uint32_t seed) { | |||||
| RNG_STATE.x = seed; | |||||
| RNG_STATE.y = seed; | |||||
| RNG_STATE.z = seed; | |||||
| RNG_STATE.w = seed; | |||||
| for (int i = 0; i < 23 + seed % 13; i++) | |||||
| random(); | |||||
| } | |||||
| } | |||||
| inline double max(double a, double b) { | |||||
| return a > b ? a : b; | |||||
| } | |||||
| inline double min(double a, double b) { | |||||
| return a < b ? a : b; | |||||
| } | |||||
| inline float max(float a, float b) { | |||||
| return a > b ? a : b; | |||||
| } | |||||
| inline float min(float a, float b) { | |||||
| return a < b ? a : b; | |||||
| } | |||||
| inline float max(float a, float b, float c) { | |||||
| return max(a, max(b, c)); | |||||
| } | |||||
| inline float min(float a, float b, float c) { | |||||
| return min(a, min(b, c)); | |||||
| } | |||||
| inline uint32_t max(uint32_t a, uint32_t b) { | |||||
| return a > b ? a : b; | |||||
| } | |||||
| inline uint32_t min(uint32_t a, uint32_t b) { | |||||
| return a < b ? a : b; | |||||
| } | |||||
| inline float Lerp(float a, float t, float b) { | |||||
| return a + t * (b - a); | |||||
| } | |||||
| float Sample3DTexture(texture3D Grid, float3 P) { | |||||
| // cubic interpolation | |||||
| #define IDX(x, y, z) ((x * Grid.h * Grid.d) + (y * Grid.d) + z) | |||||
| float fw = Grid.w * P.x; | |||||
| float fh = Grid.h * P.y; | |||||
| float fd = Grid.d * P.z; | |||||
| float tw = fw - (int)fw; | |||||
| float th = fh - (int)fh; | |||||
| float td = fd - (int)fd; | |||||
| uint32_t idx_w_left = floor(fw); | |||||
| uint32_t idx_h_low = floor(fh); | |||||
| uint32_t idx_d_near = floor(fd); | |||||
| uint32_t idx_w_right = ceil(fw); | |||||
| uint32_t idx_h_high = ceil(fh); | |||||
| uint32_t idx_d_far = ceil(fd); | |||||
| float left_low_near = Grid.Data[IDX(idx_w_left, idx_h_low, idx_d_near)]; | |||||
| float left_low_far = Grid.Data[IDX(idx_w_left, idx_h_low, idx_d_far)]; | |||||
| float left_high_near = Grid.Data[IDX(idx_w_left, idx_h_high, idx_d_near)]; | |||||
| float left_high_far = Grid.Data[IDX(idx_w_left, idx_h_high, idx_d_far)]; | |||||
| float right_low_near = Grid.Data[IDX(idx_w_right, idx_h_low, idx_d_near)]; | |||||
| float right_low_far = Grid.Data[IDX(idx_w_right, idx_h_low, idx_d_far)]; | |||||
| float right_high_near = Grid.Data[IDX(idx_w_right, idx_h_high, idx_d_near)]; | |||||
| float right_high_far = Grid.Data[IDX(idx_w_right, idx_h_high, idx_d_far)]; | |||||
| float low_near = Lerp(left_low_near, tw, right_low_near); | |||||
| float low_far = Lerp(left_low_far, tw, right_low_far); | |||||
| float low = Lerp(low_near, td, low_far); | |||||
| float high_near = Lerp(left_high_near, tw, right_high_near); | |||||
| float high_far = Lerp(left_high_far, tw, right_high_far); | |||||
| float high = Lerp(high_near, td, high_far); | |||||
| return Lerp(low, th, high); | |||||
| #undef IDX | |||||
| } | |||||
| void CreateOrthonormalBasis(float3 D, float3& B, float3& T) { | |||||
| float3 other = fabs(D.z) >= 0.999 ? float3{1, 0, 0} : float3{0, 0, 1}; | |||||
| B = Normalize(Cross(other, D)); | |||||
| T = Normalize(Cross(D, B)); | |||||
| } | |||||
| void CreateOrthonormalBasis2(float3 D, float3& B, float3& T) { | |||||
| float3 other = fabs(D.z) >= 0.999 ? float3{1, 0, 0} : float3{0, 0, 1}; | |||||
| B = Normalize(Cross(other, D)); | |||||
| T = Normalize(Cross(D, B)); | |||||
| } | |||||
| float3 RandomDirection(float3 D) { | |||||
| float r1 = Random::random(); | |||||
| float r2 = Random::random() * 2 - 1; | |||||
| float sqrR2 = r2 * r2; | |||||
| float two_pi_by_r1 = two_pi * r1; | |||||
| float sqrt_of_one_minus_sqrR2 = sqrt(1.0 - sqrR2); | |||||
| float x = cos(two_pi_by_r1) * sqrt_of_one_minus_sqrR2; | |||||
| float y = sin(two_pi_by_r1) * sqrt_of_one_minus_sqrR2; | |||||
| float z = r2; | |||||
| float3 t0, t1; | |||||
| CreateOrthonormalBasis2(D, t0, t1); | |||||
| return t0 * x + t1 * y + D * z; | |||||
| } | |||||
| float invertcdf(float GFactor, float xi) { | |||||
| #define one_minus_g2 (1.0 - (GFactor) * (GFactor)) | |||||
| #define one_plus_g2 (1.0 + (GFactor) * (GFactor)) | |||||
| #define one_over_2g (0.5 / (GFactor)) | |||||
| float t = (one_minus_g2) / (1.0f - GFactor + 2.0f * GFactor * xi); | |||||
| return one_over_2g * (one_plus_g2 - t * t); | |||||
| #undef one_minus_g2 | |||||
| #undef one_plus_g2 | |||||
| #undef one_over_2g | |||||
| } | |||||
| float3 ImportanceSamplePhase(float GFactor, float3 D) { | |||||
| if (fabs(GFactor) < 0.001) { | |||||
| return RandomDirection(-D); | |||||
| } | |||||
| float phi = Random::random() * 2 * pi; | |||||
| float cosTheta = invertcdf(GFactor, Random::random()); | |||||
| float sinTheta = sqrt(max(0, 1.0f - cosTheta * cosTheta)); | |||||
| float3 t0, t1; | |||||
| CreateOrthonormalBasis(D, t0, t1); | |||||
| return sinTheta * sin(phi) * t0 + sinTheta * cos(phi) * t1 + | |||||
| cosTheta * D; | |||||
| } | |||||
| void GetGridBox(texture3D grid, float3& minim, float3& maxim) { | |||||
| float maxDim = max(grid.w, grid.h, grid.d); | |||||
| maxim = float3{(float)grid.w, (float)grid.h, (float)grid.d} / maxDim * 0.5; | |||||
| minim = -maxim; | |||||
| } | |||||
| bool BoxIntersect(float3 bMin, float3 bMax, float3 P, float3 D, float& tMin, float& tMax) { | |||||
| float2x3 C = float2x3{ bMin - P, bMax - P }; | |||||
| float2x3 D2 = float2x3{ D, D }; | |||||
| float2x3 T = | |||||
| Abs(D2) <= 0.000001 | |||||
| ? float2x3{float3{-1000, -1000, -1000}, float3{1000, 1000, 1000}} | |||||
| : C / D2; | |||||
| tMin = max(min(T.v1.v[0], T.v2.v[0]), | |||||
| min(T.v1.v[1], T.v2.v[1]), | |||||
| min(T.v1.v[2], T.v2.v[2])); | |||||
| tMin = max(0.0f, tMin); | |||||
| tMax = min(max(T.v1.v[0], T.v2.v[0]), | |||||
| max(T.v1.v[1], T.v2.v[1]), | |||||
| max(T.v1.v[2], T.v2.v[2])); | |||||
| if (tMax < tMin || tMax < 0) { | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| void DTPathtrace(path_info PathInfo, volume_info VolumeInfo, ray_pack* RayPack) { | |||||
| int PassNumber = PathInfo.PassNumber; | |||||
| float3 x = PathInfo.x; | |||||
| float3 w = PathInfo.w; | |||||
| float density = VolumeInfo.Extinction.v[PassNumber % 3]; // extinction coefficient multiplier. | |||||
| INFO(" density: %f\n", density); | |||||
| float3 bMin, bMax; | |||||
| GetGridBox(VolumeInfo.Grid, bMin, bMax); | |||||
| float3 W = { 0 }; // weight | |||||
| W.v[PassNumber % 3] = 3; // Wavelenght dependent importance multiplier | |||||
| float tMin, tMax; | |||||
| if (!BoxIntersect(bMin, bMax, x, w, tMin, tMax)) | |||||
| return; | |||||
| RayPack->start_new_ray(); | |||||
| RayPack->add_vertex(x); | |||||
| float d = tMax - tMin; | |||||
| x += w * tMin; | |||||
| RayPack->add_vertex(x); | |||||
| while (true) { | |||||
| float t = | |||||
| density <= 0.00001 | |||||
| ? 10000000 | |||||
| : -log(max(0.00000000001, 1.0 - Random::random())) / density; // majorant of all volume data | |||||
| INFO(" t: %f\n", t); | |||||
| INFO(" d: %f\n", d); | |||||
| if (t >= d) { | |||||
| INFO("->Ray left the volume\n"); | |||||
| RayPack->add_vertex(x + (w * t)); | |||||
| return; | |||||
| } | |||||
| x += w * t; // move to next event position or border | |||||
| RayPack->add_vertex(x); | |||||
| float3 tSamplePosition = (x - bMin) / (bMax - bMin); | |||||
| float probExt = Sample3DTexture(VolumeInfo.Grid, tSamplePosition); | |||||
| INFO(" sample pos: %f %f %f\n", tSamplePosition.x, tSamplePosition.y, tSamplePosition.z); | |||||
| INFO(" density there: %f\n", probExt); | |||||
| float m_t = probExt * density; // extinction coef | |||||
| float m_s = m_t * VolumeInfo.ScatteringAlbedo.v[PassNumber % 3]; // scattering coef | |||||
| float m_a = m_t - m_s; // absorption coef | |||||
| float m_n = density - m_t; // null coef | |||||
| float xi = Random::random(); | |||||
| float Pa = m_a / density; | |||||
| float Ps = m_s / density; | |||||
| float Pn = m_n / density; | |||||
| if (xi < Pa) { // absorption | |||||
| INFO("->absorbtion\n"); | |||||
| return; | |||||
| } | |||||
| if (xi < 1 - Pn) { // scattering | |||||
| INFO("->scatter\n"); | |||||
| w = ImportanceSamplePhase(VolumeInfo.G.v[PassNumber % 3], w); // scattering event... | |||||
| if (!BoxIntersect(bMin, bMax, x, w, tMin, tMax)) | |||||
| return; | |||||
| d = tMax - tMin; | |||||
| x += w * tMin; | |||||
| } else { | |||||
| INFO("->null collision\n"); | |||||
| // if no absorption and no scattering null collision occurred | |||||
| d -= t; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,25 @@ | |||||
| #pragma once | |||||
| #include "stdint.h" | |||||
| #include "vector.hpp" | |||||
| #include "texture3d.hpp" | |||||
| #include "ray_pack.hpp" | |||||
| struct volume_info { | |||||
| texture3D Grid; | |||||
| float3 Extinction; | |||||
| float3 ScatteringAlbedo; | |||||
| float3 G; | |||||
| }; | |||||
| struct path_info { | |||||
| float3 x; | |||||
| float3 w; | |||||
| int PassNumber; | |||||
| }; | |||||
| void GetGridBox(texture3D grid, float3& minim, float3& maxim); | |||||
| void DTPathtrace(path_info PathInfo, volume_info VolumeInfo, ray_pack* RayPack); | |||||
| namespace Random { | |||||
| void InitRNG(uint32_t seed); | |||||
| } | |||||
| @@ -0,0 +1,46 @@ | |||||
| #pragma once | |||||
| #include <cstdlib> | |||||
| #include "stdint.h" | |||||
| template <typename type> | |||||
| struct list { | |||||
| uint32_t count; | |||||
| uint32_t length; | |||||
| type* data; | |||||
| inline void alloc(uint32_t initial_length = 16) { | |||||
| count = 0; | |||||
| length = initial_length; | |||||
| data = (type*)malloc(sizeof(type) * length); | |||||
| } | |||||
| inline void dealloc() { | |||||
| free(data); | |||||
| data = nullptr; | |||||
| } | |||||
| inline void clear() { | |||||
| count = 0; | |||||
| } | |||||
| void append(type element) { | |||||
| if (count == length) { | |||||
| length *= 2; | |||||
| data = (type*)realloc(data, length * sizeof(type)); | |||||
| } | |||||
| data[count] = element; | |||||
| count++; | |||||
| } | |||||
| inline type* begin() { | |||||
| return data; | |||||
| } | |||||
| inline type* end() { | |||||
| return data+(count); | |||||
| } | |||||
| type& operator[](uint32_t index) { | |||||
| return data[index]; | |||||
| } | |||||
| }; | |||||
| @@ -0,0 +1,112 @@ | |||||
| #include <cstdio> | |||||
| #include <cmath> | |||||
| #include "vector.hpp" | |||||
| #include "texture3d.hpp" | |||||
| #include "list.hpp" | |||||
| #include "defer.hpp" | |||||
| #include "dt_pathtrace.hpp" | |||||
| #include "ray_pack.hpp" | |||||
| void WriteDomainAndRayPackToOBJFile(texture3D Grid, ray_pack* RayPack, | |||||
| const char* OBJFileName) | |||||
| { | |||||
| FILE* out = fopen(OBJFileName, "w"); | |||||
| if (!out) { | |||||
| fprintf(stderr, "ERROR: file %s could not be opened\n", OBJFileName); | |||||
| return; | |||||
| } | |||||
| defer { | |||||
| fclose(out); | |||||
| }; | |||||
| for (float3 v : RayPack->vertices) { | |||||
| fprintf(out, "v %f %f %f\n", v.x, v.y, v.z); | |||||
| } | |||||
| // Domain Box | |||||
| float3 maxim; | |||||
| float3 minim; | |||||
| GetGridBox(Grid, minim, maxim); | |||||
| fprintf(out, "v %f %f %f\n", maxim.x, maxim.y, maxim.z); | |||||
| fprintf(out, "v %f %f %f\n", maxim.x, maxim.y, -maxim.z); | |||||
| fprintf(out, "v %f %f %f\n", maxim.x, -maxim.y, maxim.z); | |||||
| fprintf(out, "v %f %f %f\n", maxim.x, -maxim.y, -maxim.z); | |||||
| fprintf(out, "v %f %f %f\n", -maxim.x, maxim.y, maxim.z); | |||||
| fprintf(out, "v %f %f %f\n", -maxim.x, maxim.y, -maxim.z); | |||||
| fprintf(out, "v %f %f %f\n", -maxim.x, -maxim.y, maxim.z); | |||||
| fprintf(out, "v %f %f %f\n", -maxim.x, -maxim.y, -maxim.z); | |||||
| fprintf(out, "g Rays\n"); | |||||
| uint32_t start = 0; | |||||
| uint32_t end; | |||||
| // all but the last sample: | |||||
| uint32_t i = 0; | |||||
| for (; i < RayPack->ray_starts.count-1; ++i) { | |||||
| end = RayPack->ray_starts[i+1]; | |||||
| fprintf(out, "o Ray_%u\n", i); | |||||
| for (uint32_t j = start; j < end-1; ++j) { | |||||
| // NOTE(Felix): +1 because obj vertex ids start counting at 1 | |||||
| fprintf(out, "l %u %u\n", j+1, j+2); | |||||
| } | |||||
| start = end; | |||||
| } | |||||
| // NOTE(Felix): the last sample; | |||||
| end = RayPack->vertices.count; | |||||
| fprintf(out, "o Ray_%u\n", i); | |||||
| printf("start %u end %u\n", start, end); | |||||
| for (uint32_t j = start; j < end-1; ++j) { | |||||
| // NOTE(Felix): +1 because obj vertex ids start counting at 1 | |||||
| fprintf(out, "l %u %u\n", j+1, j+2); | |||||
| } | |||||
| end++; | |||||
| fprintf(out, "g Domain\n"); | |||||
| fprintf(out, "o Domain\n"); | |||||
| fprintf(out, "l %u %u\n", end+0, end+1); | |||||
| fprintf(out, "l %u %u\n", end+2, end+3); | |||||
| fprintf(out, "l %u %u\n", end+4, end+5); | |||||
| fprintf(out, "l %u %u\n", end+6, end+7); | |||||
| fprintf(out, "l %u %u\n", end+0, end+2); | |||||
| fprintf(out, "l %u %u\n", end+1, end+3); | |||||
| fprintf(out, "l %u %u\n", end+4, end+6); | |||||
| fprintf(out, "l %u %u\n", end+5, end+7); | |||||
| fprintf(out, "l %u %u\n", end+0, end+4); | |||||
| fprintf(out, "l %u %u\n", end+1, end+5); | |||||
| fprintf(out, "l %u %u\n", end+2, end+6); | |||||
| fprintf(out, "l %u %u\n", end+3, end+7); | |||||
| } | |||||
| int main() { | |||||
| ray_pack RayPack; | |||||
| RayPack.alloc(); | |||||
| defer { | |||||
| RayPack.dealloc(); | |||||
| }; | |||||
| path_info PI = { }; | |||||
| PI.x = {-2, -2, -2}; | |||||
| PI.w = Normalize(float3{0.4,0.4,0} - PI.x); | |||||
| PI.PassNumber = 0; | |||||
| volume_info VI = { }; | |||||
| VI.Grid = ReadXYZFile("cloud-049.xyz"); | |||||
| VI.Extinction = {20, 20, 20}; | |||||
| VI.G = {0.3, 0.3, 0.3}; | |||||
| VI.ScatteringAlbedo = {0.9, 0.9, 0.9}; | |||||
| Random::InitRNG(1); | |||||
| for (int i = 0; i < 30; ++i) { | |||||
| DTPathtrace(PI, VI, &RayPack); | |||||
| } | |||||
| WriteDomainAndRayPackToOBJFile(VI.Grid, &RayPack, "RayPack.obj"); | |||||
| return 0; | |||||
| } | |||||
| @@ -0,0 +1,27 @@ | |||||
| #pragma once | |||||
| #include "list.hpp" | |||||
| #include "vector.hpp" | |||||
| struct ray_pack { | |||||
| list<float3> vertices; | |||||
| list<uint32_t> ray_starts; | |||||
| void alloc() { | |||||
| vertices.alloc(); | |||||
| ray_starts.alloc(); | |||||
| } | |||||
| void dealloc() { | |||||
| vertices.dealloc(); | |||||
| ray_starts.dealloc(); | |||||
| } | |||||
| void add_vertex(float3 vert) { | |||||
| vertices.append(vert); | |||||
| } | |||||
| void start_new_ray() { | |||||
| ray_starts.append(vertices.count); | |||||
| } | |||||
| }; | |||||
| @@ -0,0 +1,17 @@ | |||||
| #pragma once | |||||
| #include "dt_pathtrace.hpp" | |||||
| struct scene { | |||||
| const char* SceneFile; | |||||
| volume_info VolInfo; | |||||
| float3 CameraPos; | |||||
| float3 CameraLookAt; | |||||
| float CameraFOV; | |||||
| uint32_t ResX; | |||||
| uint32_t ResY; | |||||
| }; | |||||
| scene LoadSceneFromFile(const char* FilePath); | |||||
| @@ -0,0 +1,47 @@ | |||||
| #include <cstdio> | |||||
| #include <cstdlib> | |||||
| #include "texture3d.hpp" | |||||
| #include "defer.hpp" | |||||
| texture3D ReadXYZFile(const char* Path) { | |||||
| uint32_t Buffer[9]; | |||||
| FILE* XYZFile = fopen(Path, "rb"); | |||||
| if (!XYZFile) { | |||||
| fprintf(stderr, "ERROR: File %s could not be opened\n", Path); | |||||
| return {}; | |||||
| } | |||||
| defer { | |||||
| fclose(XYZFile); | |||||
| }; | |||||
| size_t Read = fread(Buffer, sizeof(uint32_t)*9, 1, XYZFile); | |||||
| if (!Read) { | |||||
| fprintf(stderr, "ERROR: Header could not be read\n"); | |||||
| return {}; | |||||
| } | |||||
| texture3D Result; | |||||
| Result.w = Buffer[0]; | |||||
| Result.h = Buffer[1]; | |||||
| Result.d = Buffer[2]; | |||||
| printf("INFO: Reading density map %u x %u x %u\n", | |||||
| Result.w, Result.h, Result.d); | |||||
| uint32_t FloatsCount = Result.w * Result.h * Result.d; | |||||
| Result.Data = (float*)malloc(FloatsCount * sizeof(float)); | |||||
| Read = fread(Result.Data, FloatsCount * sizeof(float), 1, XYZFile); | |||||
| if (!Read) { | |||||
| fprintf(stderr, "ERROR: Data could not be read\n"); | |||||
| return {}; | |||||
| } | |||||
| return Result; | |||||
| } | |||||
| @@ -0,0 +1,11 @@ | |||||
| #pragma once | |||||
| #include "stdint.h" | |||||
| struct texture3D { | |||||
| uint32_t w; | |||||
| uint32_t h; | |||||
| uint32_t d; | |||||
| float* Data; | |||||
| }; | |||||
| texture3D ReadXYZFile(const char* Path); | |||||
| @@ -0,0 +1,144 @@ | |||||
| #include "vector.hpp" | |||||
| #include "math.h" | |||||
| float3 operator/(float3 v, float s) { | |||||
| float3 result; | |||||
| result.x = v.x / s; | |||||
| result.y = v.y / s; | |||||
| result.z = v.z / s; | |||||
| return result; | |||||
| } | |||||
| float3 operator*(float3 v, float s) { | |||||
| float3 result; | |||||
| result.x = v.x * s; | |||||
| result.y = v.y * s; | |||||
| result.z = v.z * s; | |||||
| return result; | |||||
| } | |||||
| float3 operator*(float s, float3 v) { | |||||
| float3 result; | |||||
| result.x = v.x * s; | |||||
| result.y = v.y * s; | |||||
| result.z = v.z * s; | |||||
| return result; | |||||
| } | |||||
| float3 operator+(float3 v1, float3 v2) { | |||||
| float3 result; | |||||
| result.x = v1.x + v2.x; | |||||
| result.y = v1.y + v2.y; | |||||
| result.z = v1.z + v2.z; | |||||
| return result; | |||||
| } | |||||
| float3 operator-(float3 v1) { | |||||
| float3 result; | |||||
| result.x = -v1.x; | |||||
| result.y = -v1.y; | |||||
| result.z = -v1.z; | |||||
| return result; | |||||
| } | |||||
| float3 operator-(float3 v1, float3 v2) { | |||||
| float3 result; | |||||
| result.x = v1.x - v2.x; | |||||
| result.y = v1.y - v2.y; | |||||
| result.z = v1.z - v2.z; | |||||
| return result; | |||||
| } | |||||
| float3 operator/(float3 v1, float3 v2) { | |||||
| float3 result; | |||||
| result.x = v1.x / v2.x; | |||||
| result.y = v1.y / v2.y; | |||||
| result.z = v1.z / v2.z; | |||||
| return result; | |||||
| } | |||||
| float3& operator*=(float3& v, float s) { | |||||
| v = v * s; | |||||
| return v; | |||||
| } | |||||
| float3& operator+=(float3& v1, float3 v2) { | |||||
| v1 = v1 + v2; | |||||
| return v1; | |||||
| } | |||||
| float3& operator-=(float3& v1, float3 v2) { | |||||
| v1 = v1 - v2; | |||||
| return v1; | |||||
| } | |||||
| float3& operator/=(float3& v1, float3 v2) { | |||||
| v1 = v1 / v2; | |||||
| return v1; | |||||
| } | |||||
| float3 Cross(float3 a, float3 b) { | |||||
| float3 result; | |||||
| result.x = a.y*b.z - a.z*b.y; | |||||
| result.y = a.z*b.x - a.x*b.z; | |||||
| result.z = a.x*b.y - a.y*b.x; | |||||
| return result; | |||||
| } | |||||
| float Dot(float3 a, float3 b) { | |||||
| return | |||||
| a.x * b.x + | |||||
| a.y * b.y + | |||||
| a.z * b.z; | |||||
| } | |||||
| float SquaredLength(float3 v) { | |||||
| return Dot(v, v); | |||||
| } | |||||
| float3 Normalize(float3 v) { | |||||
| float3 result { 0 }; | |||||
| float squaredLength = SquaredLength(v); | |||||
| if(squaredLength > 0.000001f) { | |||||
| result = v * (1.0f / sqrt(squaredLength)); | |||||
| } | |||||
| return result; | |||||
| } | |||||
| float3 Abs(float3 v) { | |||||
| float3 result; | |||||
| result.x = fabs(v.x); | |||||
| result.y = fabs(v.y); | |||||
| result.z = fabs(v.z); | |||||
| return result; | |||||
| } | |||||
| float2x3 Abs(float2x3 M) { | |||||
| float2x3 result; | |||||
| result.v1 = Abs(M.v1); | |||||
| result.v2 = Abs(M.v2); | |||||
| return result; | |||||
| } | |||||
| bool operator<=(float3 v, float s) { | |||||
| return | |||||
| v.x <= s && | |||||
| v.y <= s && | |||||
| v.z <= s; | |||||
| } | |||||
| bool operator<=(float2x3 M, float s) { | |||||
| return M.v1 <= s && M.v2 <= s; | |||||
| } | |||||
| float2x3 operator/(float2x3 M1, float2x3 M2) { | |||||
| float2x3 result; | |||||
| result.v1 = M1.v1 / M2.v1; | |||||
| result.v2 = M1.v2 / M2.v2; | |||||
| return result; | |||||
| } | |||||
| @@ -0,0 +1,40 @@ | |||||
| #pragma once | |||||
| typedef union { | |||||
| struct { | |||||
| float x, y, z; | |||||
| }; | |||||
| struct { | |||||
| float r, g, b; | |||||
| }; | |||||
| float v[3]; | |||||
| } float3; | |||||
| typedef struct { | |||||
| float3 v1; | |||||
| float3 v2; | |||||
| } float2x3; | |||||
| float3 Cross(float3, float3); | |||||
| float3 Normalize(float3); | |||||
| float3 Abs(float3); | |||||
| float2x3 Abs(float2x3); | |||||
| float2x3 operator/(float2x3, float2x3); | |||||
| bool operator<=(float2x3, float); | |||||
| float3 operator-(float3); | |||||
| float3 operator*(float3, float); | |||||
| float3 operator*(float, float3); | |||||
| float3 operator/(float3, float); | |||||
| float3 operator+(float3, float3); | |||||
| float3 operator-(float3, float3); | |||||
| float3 operator/(float3, float3); | |||||
| float3& operator*=(float3&, float); | |||||
| float3& operator/=(float3&, float3); | |||||
| float3& operator+=(float3&, float3); | |||||
| float3& operator-=(float3&, float3); | |||||