diff --git a/.gitignore b/.gitignore index 346eb4e..2178ad7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /dist/* !/dist/*.xyz !/dist/*.txt +!/dist/*.blend diff --git a/dist/scene1.txt b/dist/scene1.txt new file mode 100644 index 0000000..ce8593a --- /dev/null +++ b/dist/scene1.txt @@ -0,0 +1,14 @@ +GridFile = cloud-049.xyz +Extinction = 0.8, 0.9, 0.8 +ScatteringAlbedo = 0.9, 0.9, 0.9 +G = 0.3, 0.3, 0.3 + +CameraPos = -2, -2, -2 +CameraLookAt = 0, 0, 0 +CameraFOV = 10 + +ResX = 10 +ResY = 10 +SamplesPerPixel = 100 + +Seed = 123 diff --git a/dist/viewer.blend b/dist/viewer.blend new file mode 100644 index 0000000..d9f5c44 Binary files /dev/null and b/dist/viewer.blend differ diff --git a/src/dt_pathtrace.cpp b/src/dt_pathtrace.cpp index 6a2732e..db97e73 100644 --- a/src/dt_pathtrace.cpp +++ b/src/dt_pathtrace.cpp @@ -2,7 +2,8 @@ #include #include -#define INFO(...) printf("INFO: " __VA_ARGS__); +// #define INFO(...) printf("INFO: " __VA_ARGS__); +#define INFO(...) #ifndef pi # define pi 3.1415926535897932384626433832795 diff --git a/src/main.cpp b/src/main.cpp index f520a84..c47ec98 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "vector.hpp" #include "texture3d.hpp" @@ -7,28 +8,24 @@ #include "defer.hpp" #include "dt_pathtrace.hpp" #include "ray_pack.hpp" +#include "scene.hpp" - -void WriteDomainAndRayPackToOBJFile(texture3D Grid, ray_pack* RayPack, - const char* OBJFileName) -{ - FILE* out = fopen(OBJFileName, "w"); +void WriteDomainToOBJFile(texture3D Grid) { + FILE* out = fopen("output/Domain.obj", "w"); if (!out) { - fprintf(stderr, "ERROR: file %s could not be opened\n", OBJFileName); + fprintf(stderr, "ERROR: file %s could not be opened\n", "output/Domain.obj"); 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, "o Domain\n"); + 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); @@ -38,16 +35,55 @@ void WriteDomainAndRayPackToOBJFile(texture3D Grid, ray_pack* RayPack, 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, "l %u %u\n", 1, 2); + fprintf(out, "l %u %u\n", 3, 4); + fprintf(out, "l %u %u\n", 5, 6); + fprintf(out, "l %u %u\n", 7, 8); + + fprintf(out, "l %u %u\n", 1, 3); + fprintf(out, "l %u %u\n", 2, 4); + fprintf(out, "l %u %u\n", 5, 7); + fprintf(out, "l %u %u\n", 6, 8); + + fprintf(out, "l %u %u\n", 1, 5); + fprintf(out, "l %u %u\n", 2, 6); + fprintf(out, "l %u %u\n", 3, 7); + fprintf(out, "l %u %u\n", 4, 8); +} + +void WriteRayPackToOBJFile(int x, int y, ray_pack* RayPack) { + if (RayPack->ray_starts.count == 0) { + return; + } + + char OBJFileName[1024]; + sprintf(OBJFileName, "output/Ray_Pack_%d_%d.obj", x, y); + + FILE* out = fopen(OBJFileName, "w"); + // FILE* out = stdout; + 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); + } + fprintf(out, "g Rays\n"); - uint32_t start = 0; - uint32_t end; + int start = 0; + int end; // all but the last sample: - uint32_t i = 0; - for (; i < RayPack->ray_starts.count-1; ++i) { + int i = 0; + for (; i < (int)(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) { + for (int 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); } @@ -57,56 +93,136 @@ void WriteDomainAndRayPackToOBJFile(texture3D Grid, ray_pack* RayPack, // 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) { + for (int 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); +void WritePixelGridToOBJFile(float GridWidth, float GridHeight, int ResX, int ResY, + float D, float3 P0, float3 X, float3 Y) +{ + FILE* out = fopen("output/Camera.obj", "w"); + if (!out) { + fprintf(stderr, "ERROR: file %s could not be opened\n", "output/Domain.obj"); + return; + } + defer { + fclose(out); + }; + + float PixelLength = GridWidth / ResX; + + P0 = P0 - + PixelLength / 2 * X - + PixelLength / 2 * Y; + GridWidth += PixelLength; + GridHeight += PixelLength; + + fprintf(out, "o Camera\n"); + + int VertIndex = 1; + + // horizontal lines + for (int i = 0; i <= ResY; ++i) { + float3 left = P0 + (1.0*i/ResY) * Y * GridHeight; + float3 right= left + X * GridWidth; + + fprintf(out, "v %f %f %f\n", left.x, left.y, left.z); + fprintf(out, "v %f %f %f\n", right.x, right.y, right.z); + fprintf(out, "l %d %d\n", VertIndex, VertIndex+1); + + VertIndex += 2; + + } + + // Vertical lines + for (int i = 0; i <= ResX; ++i) { + float3 top = P0 + (1.0*i/ResX) * X * GridWidth; + float3 bottom = top + Y * GridWidth; - 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, "v %f %f %f\n", top.x, top.y, top.z); + fprintf(out, "v %f %f %f\n", bottom.x, bottom.y, bottom.z); + fprintf(out, "l %d %d\n", VertIndex, VertIndex+1); - 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); + VertIndex += 2; + + } } -int main() { +void RunSimulationAndOutputScene(scene S) { ray_pack RayPack; RayPack.alloc(); defer { RayPack.dealloc(); }; + Random::InitRNG(S.Seed); + + float3 CameraDirection = Normalize(S.CameraLookAt - S.CameraPos); + + float3 Y = { 0, 0, -1 }; + float3 X = Cross(CameraDirection, Y); + Y = Cross(X, CameraDirection); + + // printf("X: %f %f %f\n", X.x, X.y, X.z); + // printf("Y: %f %f %f\n", Y.x, Y.y, Y.z); + + float D = 0.5f; // how far away from the camera the grid will be + float GridWidth = tan(S.CameraFOV / 2) * 2 * D; + float GridHeight = S.ResY * 1.0 / S.ResX * GridWidth; + + float3 P0 = + S.CameraPos + + CameraDirection * D - + 0.5f * Y * GridHeight - + 0.5f * X * GridWidth; + + // printf("P0: %f %f %f\n", P0.x, P0.y,P0.z); + 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); + PI.x = S.CameraPos; + + // Pixel loop + for (int y = 0; y < S.ResY; ++y) { + for (int x = 0; x < S.ResX; ++x) { + // printf("INFO: %d %d\n", x, y); + float3 P = + P0 + + X * (x*1.0/(S.ResX-1)) * GridWidth + + Y * (y*1.0/(S.ResY-1)) * GridHeight; + // printf("P: %f %f %f\n", P.x, P.y, P.z); + + P = Normalize(P - S.CameraPos); + // printf("dir: %f %f %f\n", P.x, P.y, P.z); + + PI.w = P; + // Sample loop + for (int i = 0; i < S.SamplesPerPixel; ++i) { + PI.PassNumber = i; + DTPathtrace(PI, S.VolInfo, &RayPack); + } + + WriteRayPackToOBJFile(x, y, &RayPack); + RayPack.reset(); + } + } + + WriteDomainToOBJFile(S.VolInfo.Grid); + WritePixelGridToOBJFile(GridWidth, GridHeight, S.ResX, S.ResY, + D, P0, X, Y); +} + +int main(int ArgCount, char** Args) { + if (ArgCount != 2) { + fprintf(stderr, "Usage: cloud_tracer \n"); + return 1; } - WriteDomainAndRayPackToOBJFile(VI.Grid, &RayPack, "RayPack.obj"); + char* SceneFile = Args[1]; + scene Scene = LoadSceneFromFile(SceneFile); + RunSimulationAndOutputScene(Scene); return 0; } diff --git a/src/ray_pack.hpp b/src/ray_pack.hpp index 8d5d934..a7f8082 100644 --- a/src/ray_pack.hpp +++ b/src/ray_pack.hpp @@ -16,6 +16,11 @@ struct ray_pack { ray_starts.dealloc(); } + void reset() { + vertices.clear(); + ray_starts.clear(); + } + void add_vertex(float3 vert) { vertices.append(vert); } diff --git a/src/scene.cpp b/src/scene.cpp new file mode 100644 index 0000000..ce7ea35 --- /dev/null +++ b/src/scene.cpp @@ -0,0 +1,225 @@ +#include +#include +#include +#include "scene.hpp" +#include "defer.hpp" + + +void FillOutDefaultValues(scene* s) { + *s = { 0 }; + + s->CameraFOV = 90; + s->CameraPos = {-2, -2, -2}; + s->ResX = 32; + s->ResY = 32; + s->SamplesPerPixel = 1; +} + +inline bool IsWhitespace(char c) { + return (c == ' ' || c == '\n' || c == '\r' || c == '\t'); +} + +void EatUntilWord(char* Str, uint32_t* ParsePos) { + while (IsWhitespace(Str[*ParsePos])) + ++(*ParsePos); +} + +char* ReadEntireFile(const char* filename) { + char* ret = nullptr; + size_t str_len; + FILE *fp = fopen(filename, "rb"); + if (fp) { + defer { + fclose(fp); + }; + + /* Go to the end of the file. */ + if (fseek(fp, 0L, SEEK_END) == 0) { + /* Get the size of the file. */ + str_len = ftell(fp) + 1; + if (str_len == 0) { + fprintf(stderr, "Empty file"); + ret = (char*)malloc(1); + ret[0] = '\0'; + return ret; + } + + /* Go back to the start of the file. */ + if (fseek(fp, 0L, SEEK_SET) != 0) { + fprintf(stderr, "Error reading file"); + return nullptr; + } + + /* Allocate our buffer to that size. */ + ret = (char*)calloc(str_len, sizeof(char)); + + /* Read the entire file into memory. */ + str_len = fread(ret, sizeof(char), str_len, fp); + + ret[str_len] = '\0'; + if (ferror(fp) != 0) { + fprintf(stderr, "Error reading file"); + return nullptr; + } + } + } else { + fprintf(stderr, "Cannot read file: %s", filename); + return nullptr; + } + + return ret; + /* Don't forget to call free() later! */ +} + +float3 ParseFloat3(char* Str, uint32_t* ParsePos) { + float3 Result = { 0 }; + int CharsRead = 0; + sscanf(Str+(*ParsePos), "%f, %f, %f%n", + &Result.x, &Result.y, &Result.z, &CharsRead); + + *ParsePos += CharsRead; + return Result; +} + +float ParseFloat(char* Str, uint32_t* ParsePos) { + float Result = 0; + int CharsRead = 0; + sscanf(Str+(*ParsePos), "%f%n", &Result, &CharsRead); + *ParsePos += CharsRead; + return Result; +} + +int ParseInt(char* Str, uint32_t* ParsePos) { + int Result = 0; + int CharsRead = 0; + sscanf(Str+(*ParsePos), "%d%n", &Result, &CharsRead); + *ParsePos += CharsRead; + return Result; +} + +char* ParseFileName(char* Str, uint32_t* ParsePos) { + uint32_t EndOfNameIdx = *ParsePos; + while (!IsWhitespace(Str[EndOfNameIdx])) + ++EndOfNameIdx; + + + uint32_t Length = EndOfNameIdx - *ParsePos; + char* Name = Str + *ParsePos; + *ParsePos = EndOfNameIdx; + + char* Result = (char*)malloc(Length+1); + memcpy(Result, Name, Length); + Result[Length] = '\0'; + + return Result; +} + +void ParseSetting(char* Str, uint32_t* ParsePos, scene* Scene) { + uint32_t EndOfNameIdx = *ParsePos; + while ((Str[EndOfNameIdx] >= 'a' && Str[EndOfNameIdx] <= 'z') || + (Str[EndOfNameIdx] >= 'A' && Str[EndOfNameIdx] <= 'Z')) + ++EndOfNameIdx; + + uint32_t Length = EndOfNameIdx - *ParsePos; + char* Name = Str + *ParsePos; + *ParsePos = EndOfNameIdx; + + EatUntilWord(Str, ParsePos); + if (Str[*ParsePos] != '=') { + fprintf(stderr, "ERROR: Syntax error, expected = after setting name (%.*s) Instead: '%s'\n", Length, Name, Str+(*ParsePos)); + return; + } + ++(*ParsePos); + EatUntilWord(Str, ParsePos); + + if (Str[*ParsePos] == '\0') { + fprintf(stderr, "ERROR: EOF while parsing setting %s\n", Name); + return; + } + +#define IsSetting(SettingName) ((sizeof(SettingName)-1 == Length) && \ + (strncmp(SettingName, Name, Length) == 0)) + + if (IsSetting("GridFile")) { + Scene->VolInfo.Grid = ReadXYZFile(ParseFileName(Str, ParsePos)); + } else if (IsSetting("Extinction")) { + Scene->VolInfo.Extinction = ParseFloat3(Str, ParsePos); + } else if (IsSetting("ScatteringAlbedo")) { + Scene->VolInfo.ScatteringAlbedo = ParseFloat3(Str, ParsePos); + } else if (IsSetting("G")) { + Scene->VolInfo.G = ParseFloat3(Str, ParsePos); + } else if (IsSetting("CameraPos")) { + Scene->CameraPos = ParseFloat3(Str, ParsePos); + } else if (IsSetting("CameraLookAt")) { + Scene->CameraLookAt = Normalize(ParseFloat3(Str, ParsePos)); + } else if (IsSetting("SamplesPerPixel")) { + Scene->SamplesPerPixel = ParseInt(Str, ParsePos); + } else if (IsSetting("Seed")) { + Scene->Seed = ParseInt(Str, ParsePos); + } else if (IsSetting("CameraFOV")) { + Scene->CameraFOV = ParseFloat(Str, ParsePos) / 180 * M_PI; + } else if (IsSetting("ResX")) { + Scene->ResX = ParseInt(Str, ParsePos); + } else if (IsSetting("ResY")) { + Scene->ResY = ParseInt(Str, ParsePos); + } else { + fprintf(stderr, "ERROR: Unknown Setting [%.*s] (length %u)", Length, Name, Length); + } + +#undef IsSetting +} + +scene LoadSceneFromFile(const char* FilePath) { + scene Result; + FillOutDefaultValues(&Result); + Result.SceneFile = FilePath; + + char* SceneConfStr = ReadEntireFile(FilePath); + if (!SceneConfStr) { + fprintf(stderr, "ERROR: scene file %s could not be read\n", FilePath); + return {}; + } + + uint32_t ParsePos = 0; + + EatUntilWord(SceneConfStr, &ParsePos); + while (SceneConfStr[ParsePos] != '\0') { + uint32_t OldParsePos = ParsePos; + ParseSetting(SceneConfStr, &ParsePos, &Result); + EatUntilWord(SceneConfStr, &ParsePos); + + if (OldParsePos == ParsePos) { + fprintf(stderr, "ERROR: No parsing progress could be made\n"); + return {}; + } + } + + printf("INFO: SceneFile: %s\n", Result.SceneFile); + + printf("INFO: Extinction: %f %f %f\n", + Result.VolInfo.Extinction.x, + Result.VolInfo.Extinction.y, + Result.VolInfo.Extinction.z); + printf("INFO: ScatteringAlbedo: %f %f %f\n", + Result.VolInfo.ScatteringAlbedo.x, + Result.VolInfo.ScatteringAlbedo.y, + Result.VolInfo.ScatteringAlbedo.z); + printf("INFO: G: %f %f %f\n", + Result.VolInfo.G.x, + Result.VolInfo.G.y, + Result.VolInfo.G.z); + + printf("INFO: CameraPos: %f %f %f\n", + Result.CameraPos.x, + Result.CameraPos.y, + Result.CameraPos.z); + printf("INFO: CameraLookAt: %f %f %f\n", + Result.CameraLookAt.x, + Result.CameraLookAt.y, + Result.CameraLookAt.z); + printf("INFO: CameraFOV: %f\n", Result.CameraFOV); + printf("INFO: ResX: %d\n", Result.ResX); + printf("INFO: ResY: %d\n", Result.ResY); + + return Result; +} diff --git a/src/scene.hpp b/src/scene.hpp index c26df0c..4bf3432 100644 --- a/src/scene.hpp +++ b/src/scene.hpp @@ -6,12 +6,14 @@ struct scene { const char* SceneFile; volume_info VolInfo; + uint32_t Seed; float3 CameraPos; float3 CameraLookAt; float CameraFOV; uint32_t ResX; uint32_t ResY; + uint32_t SamplesPerPixel; }; scene LoadSceneFromFile(const char* FilePath);