diff --git a/Step 2/Submission.zip b/Step 2/Submission.zip new file mode 100644 index 0000000..99064ea Binary files /dev/null and b/Step 2/Submission.zip differ diff --git a/Step 2/Submission/3P93_P2.pdf b/Step 2/Submission/3P93_P2.pdf new file mode 100644 index 0000000..896bc1f Binary files /dev/null and b/Step 2/Submission/3P93_P2.pdf differ diff --git a/Step 2/Submission/CMakeLists.txt b/Step 2/Submission/CMakeLists.txt new file mode 100644 index 0000000..ad936dc --- /dev/null +++ b/Step 2/Submission/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 3.23) +project(Step_2) + +set(CMAKE_CXX_STANDARD 20) + +# used to debug memory related issues +if (UNIX AND (CMAKE_BUILD_TYPE MATCHES Debug)) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -g") + add_compile_options(-fsanitize=address) + add_link_options(-fsanitize=address) +endif() + +# enables AVX instructions. if you have issues, try disabling this. +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native") + +#Setup project source compilation +set(source_dir "${PROJECT_SOURCE_DIR}/src/") +file(GLOB_RECURSE source_files "${source_dir}/*.cpp") +file(GLOB_RECURSE source_c_files "${source_dir}/*.c") + +#Setup project header files +include_directories(${PROJECT_SOURCE_DIR}/include) + +add_executable(${PROJECT_NAME} ${source_files} ${source_c_files}) diff --git a/Step 2/Submission/cmake_make_command.sh b/Step 2/Submission/cmake_make_command.sh new file mode 100755 index 0000000..ade3338 --- /dev/null +++ b/Step 2/Submission/cmake_make_command.sh @@ -0,0 +1,6 @@ +#!/bin/bash +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Release ../ +make -j 16 +./Step_2 diff --git a/Step 2/Submission/include/image/image.h b/Step 2/Submission/include/image/image.h new file mode 100644 index 0000000..b213761 --- /dev/null +++ b/Step 2/Submission/include/image/image.h @@ -0,0 +1,56 @@ +/* + * Created by Brett Terpstra 6920201 on 14/10/22. + * Copyright (c) Brett Terpstra 2022 All Rights Reserved + */ + +#ifndef STEP_2_IMAGE_H +#define STEP_2_IMAGE_H + +#include "util/std.h" +#include "math/vectors.h" + +namespace Raytracing { + + // glorified structure to store our image data. + class Image { + private: + int width; + int height; + Vec4 *pixelData; + public: + Image(int width, int height); + Image(const Image &image); + Image(const Image&& image) = delete; + + inline void setPixelColor(int x, int y, const Vec4 &color) { + pixelData[(x * height) + y] = color; + } + + [[nodiscard]] inline Vec4 getPixelColor(int x, int y) const { + return pixelData[(x * height) + y]; + } + + [[nodiscard]] int getPixelR(int x, int y) const; + [[nodiscard]] int getPixelG(int x, int y) const; + [[nodiscard]] int getPixelB(int x, int y) const; + [[nodiscard]] int getPixelA(int x, int y) const; + + [[nodiscard]] inline int getWidth() const { return width; } + + [[nodiscard]] inline int getHeight() const { return height; } + + ~Image(); + }; + + // image writer class used to output the image to a file. + class ImageOutput { + private: + Image image; + public: + explicit ImageOutput(const Image& image): image(image) {} + virtual void write(const std::string& file, const std::string& formatExtension); + }; + +} + +#endif //STEP_2_IMAGE_H diff --git a/Step 2/Submission/include/image/stb_image_write.h b/Step 2/Submission/include/image/stb_image_write.h new file mode 100644 index 0000000..e4b32ed --- /dev/null +++ b/Step 2/Submission/include/image/stb_image_write.h @@ -0,0 +1,1724 @@ +/* stb_image_write - v1.16 - public domain - http://nothings.org/stb + writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio or a callback. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation; though providing a custom + zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. + This library is designed for source code compactness and simplicity, + not optimal image file size or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can #define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function + for PNG compression (instead of the builtin one), it must have the following signature: + unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); + The returned data will be freed with STBIW_FREE() (free() by default), + so it must be heap allocated with STBIW_MALLOC() (malloc() by default), + +UNICODE: + + If compiling for Windows and you wish to use Unicode filenames, compile + with + #define STBIW_WINDOWS_UTF8 + and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert + Windows wchar_t filenames to utf8. + +USAGE: + + There are five functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically + + There are also five equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can configure it with these global variables: + int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE + int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression + int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode + + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + PNG allows you to set the deflate compression level by setting the global + variable 'stbi_write_png_compression_level' (it defaults to 8). + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + + JPEG does ignore alpha channels in input data; quality is between 1 and 100. + Higher quality looks better but results in a bigger image. + JPEG baseline (no JPEG progressive). + +CREDITS: + + + Sean Barrett - PNG/BMP/TGA + Baldur Karlsson - HDR + Jean-Sebastien Guay - TGA monochrome + Tim Kelsey - misc enhancements + Alan Hickman - TGA RLE + Emmanuel Julien - initial file IO callback implementation + Jon Olick - original jo_jpeg.cpp code + Daniel Gibson - integrate JPEG, allow external zlib + Aarni Koskela - allow choosing PNG filter + + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + github:poppolopoppo + Patrick Boettcher + github:xeekworx + Cap Petschulat + Simon Rodriguez + Ivan Tikhonov + github:ignotion + Adam Schackart + Andrew Kensler + +LICENSE + + See end of file for license information. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#include + +// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' +#ifndef STBIWDEF +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#ifdef __cplusplus +#define STBIWDEF extern "C" +#else +#define STBIWDEF extern +#endif +#endif +#endif + +#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations +STBIWDEF int stbi_write_tga_with_rle; +STBIWDEF int stbi_write_png_compression_level; +STBIWDEF int stbi_write_force_png_filter; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); + +#ifdef STBIW_WINDOWS_UTF8 +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + +STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi_write_png_compression_level = 8; +static int stbi_write_tga_with_rle = 1; +static int stbi_write_force_png_filter = -1; +#else +int stbi_write_png_compression_level = 8; +int stbi_write_tga_with_rle = 1; +int stbi_write_force_png_filter = -1; +#endif + +static int stbi__flip_vertically_on_write = 0; + +STBIWDEF void stbi_flip_vertically_on_write(int flag) +{ + stbi__flip_vertically_on_write = flag; +} + +typedef struct +{ + stbi_write_func *func; + void *context; + unsigned char buffer[64]; + int buf_used; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) +#ifdef __cplusplus +#define STBIW_EXTERN extern "C" +#else +#define STBIW_EXTERN extern +#endif +STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); + +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbiw__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_WIN32) && defined(STBIW_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename)/sizeof(*wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode)/sizeof(*wMode))) + return 0; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = stbiw__fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__write_flush(stbi__write_context *s) +{ + if (s->buf_used) { + s->func(s->context, &s->buffer, s->buf_used); + s->buf_used = 0; + } +} + +static void stbiw__putc(stbi__write_context *s, unsigned char c) +{ + s->func(s->context, &c, 1); +} + +static void stbiw__write1(stbi__write_context *s, unsigned char a) +{ + if ((size_t)s->buf_used + 1 > sizeof(s->buffer)) + stbiw__write_flush(s); + s->buffer[s->buf_used++] = a; +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + int n; + if ((size_t)s->buf_used + 3 > sizeof(s->buffer)) + stbiw__write_flush(s); + n = s->buf_used; + s->buf_used = n+3; + s->buffer[n+0] = a; + s->buffer[n+1] = b; + s->buffer[n+2] = c; +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + stbiw__write1(s, d[comp - 1]); + + switch (comp) { + case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case + case 1: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + stbiw__write1(s, d[0]); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + stbiw__write1(s, d[comp - 1]); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (stbi__flip_vertically_on_write) + vdir *= -1; + + if (vdir < 0) { + j_end = -1; j = y-1; + } else { + j_end = y; j = 0; + } + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + stbiw__write_flush(s); + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + if (comp != 4) { + // write RGB bitmap + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header + } else { + // RGBA bitmaps need a v4 header + // use BI_BITFIELDS mode with 32bpp and alpha mask + // (straight BI_RGB with alpha mask doesn't work in most readers) + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *)data,1,0, + "11 4 22 4" "4 44 22 444444 4444 4 444 444 444 444", + 'B', 'M', 14+108+x*y*4, 0, 0, 14+108, // file header + 108, x,y, 1,32, 3,0,0,0,0,0, 0xff0000,0xff00,0xff,0xff000000u, 0, 0,0,0, 0,0,0, 0,0,0, 0,0,0); // bitmap V4 header + } +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + int jend, jdir; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + if (stbi__flip_vertically_on_write) { + j = 0; + jend = y; + jdir = 1; + } else { + j = y-1; + jend = -1; + jdir = -1; + } + for (; j != jend; j += jdir) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + stbiw__write1(s, header); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + stbiw__write1(s, header); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + stbiw__write_flush(s); + } + return 1; +} + +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +#ifndef STBI_WRITE_NO_STDIO + +static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + +#ifdef __STDC_LIB_EXT1__ + len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#else + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#endif + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); + STBIW_FREE(scratch); + return 1; + } +} + +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +#ifndef STBIW_ZLIB_COMPRESS +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (void *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +#endif // STBIW_ZLIB_COMPRESS + +STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ +#ifdef STBIW_ZLIB_COMPRESS + // user provided a zlib compress implementation, use that + return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); +#else // use builtin + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); + if (hash_table == NULL) + return NULL; + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) { best=d; bestloc=hlist[j]; } + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + // store uncompressed instead if compression was worse + if (stbiw__sbn(out) > data_len + 2 + ((data_len+32766)/32767)*5) { + stbiw__sbn(out) = 2; // truncate to DEFLATE 32K window and FLEVEL = 1 + for (j = 0; j < data_len;) { + int blocklen = data_len - j; + if (blocklen > 32767) blocklen = 32767; + stbiw__sbpush(out, data_len - j == blocklen); // BFINAL = ?, BTYPE = 0 -- no compression + stbiw__sbpush(out, STBIW_UCHAR(blocklen)); // LEN + stbiw__sbpush(out, STBIW_UCHAR(blocklen >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(~blocklen)); // NLEN + stbiw__sbpush(out, STBIW_UCHAR(~blocklen >> 8)); + memcpy(out+stbiw__sbn(out), data+j, blocklen); + stbiw__sbn(out) += blocklen; + j += blocklen; + } + } + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } + s1 %= 65521; s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +#endif // STBIW_ZLIB_COMPRESS +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ +#ifdef STBIW_CRC32 + return STBIW_CRC32(buffer, len); +#else + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +#endif +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) +{ + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); + int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + + if (type==0) { + memcpy(line_buffer, z, width*n); + return; + } + + // first loop isn't optimized since it's just one pixel + for (i = 0; i < n; ++i) { + switch (type) { + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + switch (type) { + case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; + case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } +} + +STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int force_filter = stbi_write_force_png_filter; + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int j,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + if (force_filter >= 5) { + force_filter = -1; + } + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { + est += abs((signed char) line_buffer[i]); + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; + } + } + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) filter_type; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + + f = stbiw__fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + + +/* *************************************************************************** + * + * JPEG writer + * + * This is based on Jon Olick's jo_jpeg.cpp: + * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html + */ + +static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, + 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; + +static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { + int bitBuf = *bitBufP, bitCnt = *bitCntP; + bitCnt += bs[1]; + bitBuf |= bs[0] << (24 - bitCnt); + while(bitCnt >= 8) { + unsigned char c = (bitBuf >> 16) & 255; + stbiw__putc(s, c); + if(c == 255) { + stbiw__putc(s, 0); + } + bitBuf <<= 8; + bitCnt -= 8; + } + *bitBufP = bitBuf; + *bitCntP = bitCnt; +} + +static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { + float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; + float z1, z2, z3, z4, z5, z11, z13; + + float tmp0 = d0 + d7; + float tmp7 = d0 - d7; + float tmp1 = d1 + d6; + float tmp6 = d1 - d6; + float tmp2 = d2 + d5; + float tmp5 = d2 - d5; + float tmp3 = d3 + d4; + float tmp4 = d3 - d4; + + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; + + d0 = tmp10 + tmp11; // phase 3 + d4 = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + d2 = tmp13 + z1; // phase 5 + d6 = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + z2 = tmp10 * 0.541196100f + z5; // c2-c6 + z4 = tmp12 * 1.306562965f + z5; // c2+c6 + z3 = tmp11 * 0.707106781f; // c4 + + z11 = tmp7 + z3; // phase 5 + z13 = tmp7 - z3; + + *d5p = z13 + z2; // phase 6 + *d3p = z13 - z2; + *d1p = z11 + z4; + *d7p = z11 - z4; + + *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; +} + +static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { + int tmp1 = val < 0 ? -val : val; + val = val < 0 ? val-1 : val; + bits[1] = 1; + while(tmp1 >>= 1) { + ++bits[1]; + } + bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { + } + // end0pos = first element in reverse order !=0 + if(end0pos == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + return DU[0]; + } + for(i = 1; i <= end0pos; ++i) { + int startpos = i; + int nrzeroes; + unsigned short bits[2]; + for (; DU[i]==0 && i<=end0pos; ++i) { + } + nrzeroes = i-startpos; + if ( nrzeroes >= 16 ) { + int lng = nrzeroes>>4; + int nrmarker; + for (nrmarker=1; nrmarker <= lng; ++nrmarker) + stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); + nrzeroes &= 15; + } + stbiw__jpg_calcBits(DU[i], bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + if(end0pos != 63) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + } + return DU[0]; +} + +static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { + // Constants that don't pollute global namespace + static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; + static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; + static const unsigned char std_ac_luminance_values[] = { + 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, + 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, + 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, + 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, + 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; + static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; + static const unsigned char std_ac_chrominance_values[] = { + 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, + 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, + 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, + 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, + 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, + 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + // Huffman tables + static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; + static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; + static const unsigned short YAC_HT[256][2] = { + {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const unsigned short UVAC_HT[256][2] = { + {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, + 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; + static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; + static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, + 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; + + int row, col, i, k, subsample; + float fdtbl_Y[64], fdtbl_UV[64]; + unsigned char YTable[64], UVTable[64]; + + if(!data || !width || !height || comp > 4 || comp < 1) { + return 0; + } + + quality = quality ? quality : 90; + subsample = quality <= 90 ? 1 : 0; + quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; + quality = quality < 50 ? 5000 / quality : 200 - quality * 2; + + for(i = 0; i < 64; ++i) { + int uvti, yti = (YQT[i]*quality+50)/100; + YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); + uvti = (UVQT[i]*quality+50)/100; + UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); + } + + for(row = 0, k = 0; row < 8; ++row) { + for(col = 0; col < 8; ++col, ++k) { + fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + } + } + + // Write Headers + { + static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; + static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; + const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), + 3,1,(unsigned char)(subsample?0x22:0x11),0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; + s->func(s->context, (void*)head0, sizeof(head0)); + s->func(s->context, (void*)YTable, sizeof(YTable)); + stbiw__putc(s, 1); + s->func(s->context, UVTable, sizeof(UVTable)); + s->func(s->context, (void*)head1, sizeof(head1)); + s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); + s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); + stbiw__putc(s, 0x10); // HTYACinfo + s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); + s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); + stbiw__putc(s, 1); // HTUDCinfo + s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); + stbiw__putc(s, 0x11); // HTUACinfo + s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); + s->func(s->context, (void*)head2, sizeof(head2)); + } + + // Encode 8x8 macroblocks + { + static const unsigned short fillBits[] = {0x7F, 7}; + int DCY=0, DCU=0, DCV=0; + int bitBuf=0, bitCnt=0; + // comp == 2 is grey+alpha (alpha is ignored) + int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + const unsigned char *dataR = (const unsigned char *)data; + const unsigned char *dataG = dataR + ofsG; + const unsigned char *dataB = dataR + ofsB; + int x, y, pos; + if(subsample) { + for(y = 0; y < height; y += 16) { + for(x = 0; x < width; x += 16) { + float Y[256], U[256], V[256]; + for(row = y, pos = 0; row < y+16; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+16; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; + U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; + V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; + } + } + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+0, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+8, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+128, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y+136, 16, fdtbl_Y, DCY, YDC_HT, YAC_HT); + + // subsample U,V + { + float subU[64], subV[64]; + int yy, xx; + for(yy = 0, pos = 0; yy < 8; ++yy) { + for(xx = 0; xx < 8; ++xx, ++pos) { + int j = yy*32+xx*2; + subU[pos] = (U[j+0] + U[j+1] + U[j+16] + U[j+17]) * 0.25f; + subV[pos] = (V[j+0] + V[j+1] + V[j+16] + V[j+17]) * 0.25f; + } + } + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subU, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, subV, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + } + } else { + for(y = 0; y < height; y += 8) { + for(x = 0; x < width; x += 8) { + float Y[64], U[64], V[64]; + for(row = y, pos = 0; row < y+8; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+8; ++col, ++pos) { + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + float r = dataR[p], g = dataG[p], b = dataB[p]; + Y[pos]= +0.29900f*r + 0.58700f*g + 0.11400f*b - 128; + U[pos]= -0.16874f*r - 0.33126f*g + 0.50000f*b; + V[pos]= +0.50000f*r - 0.41869f*g - 0.08131f*b; + } + } + + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, Y, 8, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, U, 8, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, V, 8, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + } + + // Do the bit alignment of the EOI marker + stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); + } + + // EOI + stbiw__putc(s, 0xFF); + stbiw__putc(s, 0xD9); + + return 1; +} + +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s = { 0 }; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); +} + + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s = { 0 }; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.16 (2021-07-11) + make Deflate code emit uncompressed blocks when it would otherwise expand + support writing BMPs with alpha channel + 1.15 (2020-07-13) unknown + 1.14 (2020-02-02) updated JPEG writer to downsample chroma channels + 1.13 + 1.12 + 1.11 (2019-08-11) + + 1.10 (2019-02-07) + support utf8 filenames in Windows; fix warnings and platform ifdefs + 1.09 (2018-02-11) + fix typo in zlib quality API, improve STB_I_W_STATIC in C++ + 1.08 (2018-01-29) + add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter + 1.07 (2017-07-24) + doc fix + 1.06 (2017-07-23) + writing JPEG (using Jon Olick's code) + 1.05 ??? + 1.04 (2017-03-03) + monochrome BMP expansion + 1.03 ??? + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/Step 2/Submission/include/math/bvh.h b/Step 2/Submission/include/math/bvh.h new file mode 100644 index 0000000..ebea250 --- /dev/null +++ b/Step 2/Submission/include/math/bvh.h @@ -0,0 +1,157 @@ +/* + * Created by Brett Terpstra 6920201 on 17/10/22. + * Copyright (c) 2022 Brett Terpstra. All Rights Reserved. + */ + +#ifndef STEP_2_BVH_H +#define STEP_2_BVH_H + +#include +#include + +#include + +// A currently pure header implementation of a BVH. TODO: make source file. +// this is also for testing and might not make it into the step 2. + +namespace Raytracing { + + struct BVHNode { + public: + std::vector objs; + AABB aabb; + BVHNode* left; + BVHNode* right; + BVHNode(std::vector objs, AABB aabb, BVHNode* left, BVHNode* right): objs(std::move(objs)), aabb(std::move(aabb)), + left(left), right(right) {} + ~BVHNode() { + delete (left); + delete (right); + } + }; + + class BVHTree { + private: + const int MAX_TREE_DEPTH = 50; + BVHNode* root = nullptr; + + void del() { + // delete copied objects + for (auto* obj : root->objs) + delete(obj); + delete (root); + } + + // splits the objs in the vector based on the provided AABBs + static std::pair, std::vector> + partition(const std::pair& aabbs, const std::vector& objs) { + std::vector a1; + std::vector a2; + for (auto* obj: objs) { + // if this object doesn't have an AABB, we cannot use a BVH on it + if (obj->getAABB().isEmpty()) { + throw std::runtime_error("Invalid AABB provided to the BVH! (Your implementation is flawed)"); + } + if (obj->getAABB().intersects(aabbs.first)) { + a1.push_back(obj); + } else if (obj->getAABB().intersects(aabbs.second)) { + a2.push_back(obj); + } + //tlog << "OBJ: " << obj->getAABB() << " " << obj->getAABB().intersects(aabbs.first) << " " << obj->getAABB().intersects(aabbs.second) << " " << objs.size() << "\n"; + } + //tlog << "we split into two of sizes: " << a1.size() << " " << a2.size() << " orig size: " << (a1.size() + a2.size()) << "\n"; + return {a1, a2}; + } + + BVHNode* addObjectsRecur(const std::vector& objects, unsigned long prevSize) { + //ilog << "size: " << objects.size() << "\n"; + // prevSize was required to solve some really weird bugs + // which are a TODO: + if ((objects.size() <= 2 && !objects.empty()) || prevSize == objects.size()) { + AABB local; + for (const auto& obj: objects) + local = local.expand(obj->getAABB()); + return new BVHNode(objects, local, nullptr, nullptr); + } else if (objects.empty()) // should never reach here!! + return nullptr; + // create a volume for the entire world. + // yes, we could use the recursion provided AABB, + // but that wouldn't be minimum, only half. + // this ensures that we have a minimum AABB. + AABB world; + for (const auto& obj: objects) { + //tlog << obj->getAABB(); + world = world.expand(obj->getAABB()); + } + //tlog << "\n"; + // then split and partition the world + auto spltAABB = world.splitByLongestAxis(); + //dlog << "We have " << world << " being split into: \n\t" << spltAABB.first << "\n\t" << spltAABB.second << "\n"; + auto partitionedObjs = partition(spltAABB, objects); + + BVHNode* left = nullptr; + BVHNode* right = nullptr; + // don't try to explore nodes which don't have anything in them. + if (!partitionedObjs.first.empty()) + left = addObjectsRecur(partitionedObjs.first, objects.size()); + if (!partitionedObjs.second.empty()) + right = addObjectsRecur(partitionedObjs.second, objects.size()); + + return new BVHNode(objects, world, left, right); + } + static std::vector + traverseFindRayIntersection(BVHNode* node, const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) { + // check for intersections on both sides of the tree + if (node->left != nullptr) { + if (node->left->aabb.intersects(ray, min, max)) + return traverseFindRayIntersection(node->left, ray, min, max); + } + // since each aabb should be minimum, we shouldn't have to traverse both sides. + // we want to reduce our problem size by half each iteration anyways + // divide and conquer and so on + if (node->right != nullptr) + if (node->right->aabb.intersects(ray, min, max)) + return traverseFindRayIntersection(node->left, ray, min, max); + // return the objects of the lowest BVH node we can find + // if this is implemented properly this should only contain one, maybe two objects + // which is much faster! (especially when dealing with triangles) + return node->objs; + } + public: + std::vector noAABBObjects; + explicit BVHTree(const std::vector& objectsInWorld) { + addObjects(objectsInWorld); + } + + void addObjects(const std::vector& objects) { + if (root != nullptr) + del(); + // move all the object's aabb's into world position + std::vector objs; + for (auto* obj: objects) { + // we don't want to store all the AABBs which don't exist + // ie spheres + if (obj->getAABB().isEmpty()) { + //tlog << "Goodbye\n"; + noAABBObjects.push_back(obj); + continue; + } + Object* objCopy = obj->clone(); + objCopy->setAABB(obj->getAABB().translate(obj->getPosition())); + objs.push_back(objCopy); + } + root = addObjectsRecur(objs, -1); + } + + std::vector rayIntersect(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) { + return traverseFindRayIntersection(root, ray, min, max); + } + + ~BVHTree() { + del(); + } + }; + +} + +#endif //STEP_2_BVH_H diff --git a/Step 2/Submission/include/math/colliders.h b/Step 2/Submission/include/math/colliders.h new file mode 100644 index 0000000..258ce44 --- /dev/null +++ b/Step 2/Submission/include/math/colliders.h @@ -0,0 +1,117 @@ +/* + * Created by Brett Terpstra 6920201 on 17/10/22. + * Copyright (c) 2022 Brett Terpstra. All Rights Reserved. + */ + +#ifndef STEP_2_COLLIDERS_H +#define STEP_2_COLLIDERS_H + +#include + +namespace Raytracing { + // 3D Axis Aligned Bounding Box for use in a BVH + class AABB { + protected: + Vec4 min; + Vec4 max; + bool empty = false; + public: + AABB() { + empty = true; + }; + + AABB(PRECISION_TYPE minX, PRECISION_TYPE minY, PRECISION_TYPE minZ, PRECISION_TYPE maxX, PRECISION_TYPE maxY, PRECISION_TYPE maxZ): + min{minX, minY, minZ}, max{maxX, maxY, maxZ} { + } + + AABB(const Vec4& min, const Vec4& max): min(min), max(max) {} + + // creates an AABB extending of size centered on x, y, z + AABB(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z, PRECISION_TYPE size): + min{x - size, y - size, z - size}, max{x + size, y + size, z + size} { + } + + // translates the AABB to position x,y,z for world collision detection + [[nodiscard]] AABB translate(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z) const { + Vec4 pos = {x, y, z}; + return {min + pos, max + pos}; + } + + [[nodiscard]] AABB translate(const Vec4& vec) const { + Vec4 pos = {vec.x(), vec.y(), vec.z()}; + return {min + pos, max + pos}; + } + + // returns an expanded version of this AABB is the other AABB is larger then this AABB + [[nodiscard]] AABB expand(const AABB& other) const { + PRECISION_TYPE minX = std::min(min.x(), other.min.x()); + PRECISION_TYPE minY = std::min(min.y(), other.min.y()); + PRECISION_TYPE minZ = std::min(min.z(), other.min.z()); + PRECISION_TYPE maxX = std::max(max.x(), other.max.x()); + PRECISION_TYPE maxY = std::max(max.y(), other.max.y()); + PRECISION_TYPE maxZ = std::max(max.z(), other.max.z()); + return {minX, minY, minZ, maxX, maxY, maxZ}; + } + + [[nodiscard]] inline bool + intersects(PRECISION_TYPE minX, PRECISION_TYPE minY, PRECISION_TYPE minZ, PRECISION_TYPE maxX, PRECISION_TYPE maxY, + PRECISION_TYPE maxZ) const { + return min.x() <= maxX && max.x() >= minX && min.y() <= maxY && max.y() >= minY && min.z() <= maxZ && max.z() >= minZ; + } + + [[nodiscard]] inline bool intersects(const Vec4& minV, const Vec4& maxV) const { + return intersects(minV.x(), minV.y(), minV.z(), maxV.x(), maxV.y(), maxV.z()); + } + + [[nodiscard]] inline bool intersects(const AABB& other) const { + return intersects(other.min, other.max); + } + + bool intersects(const Ray& ray, PRECISION_TYPE tmin, PRECISION_TYPE tmax); + bool simpleSlabRayAABBMethod(const Ray& ray, PRECISION_TYPE tmin, PRECISION_TYPE tmax); + + [[nodiscard]] inline bool isInside(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z) const { + return x >= min.x() && x <= max.x() && y >= min.y() && y <= max.y() && z >= min.z() && z <= max.z(); + } + + [[nodiscard]] inline bool intersectsWithYZ(PRECISION_TYPE y, PRECISION_TYPE z) const { + return y >= min.y() && y <= max.y() && z >= min.z() && z <= max.z(); + } + + [[nodiscard]] inline bool intersectsWithXZ(PRECISION_TYPE x, PRECISION_TYPE z) const { + return x >= min.x() && x <= max.x() && z >= min.z() && z <= max.z(); + } + + [[nodiscard]] inline bool intersectsWithXY(PRECISION_TYPE x, PRECISION_TYPE y) const { + return x >= min.x() && x <= max.x() && y >= min.y() && y <= max.y(); + } + + [[nodiscard]] inline Vec4 getCenter() const { + return {min.x() + (max.x() - min.x()) * 0.5, min.y() + (max.y() - min.y()) * 0.5, min.z() + (max.z() - min.z()) * 0.5}; + } + + [[nodiscard]] PRECISION_TYPE longestDistanceFromCenter() const; + // 0 - x + // 1 - y + // 2 - z + [[nodiscard]] int longestAxis() const; + [[nodiscard]] PRECISION_TYPE longestAxisLength() const; + [[nodiscard]] std::pair splitByLongestAxis(); + + [[nodiscard]] PRECISION_TYPE avgDistanceFromCenter() const; + + [[nodiscard]] inline bool isEmpty() const { return empty; } + + [[nodiscard]] Vec4 getMin() const { return min; } + + [[nodiscard]] Vec4 getMax() const { return max; } + + }; + + inline std::ostream& operator<<(std::ostream& out, const AABB& v) { + return out << "AABB{min{" << v.getMin().x() << ", " << v.getMin().y() << ", " << v.getMin().z() << "}, max{" << v.getMax().x() << ", " << v.getMax().y() + << ", " << v.getMax().z() << "}} "; + } +} + +#endif //STEP_2_COLLIDERS_H diff --git a/Step 2/Submission/include/math/vectors.h b/Step 2/Submission/include/math/vectors.h new file mode 100644 index 0000000..2b130c0 --- /dev/null +++ b/Step 2/Submission/include/math/vectors.h @@ -0,0 +1,355 @@ +/* + * Created by Brett Terpstra 6920201 on 14/10/22. + * Copyright (c) Brett Terpstra 2022 All Rights Reserved + */ + +#ifndef STEP_2_VECTORS_H +#define STEP_2_VECTORS_H + +// AVX512 isn't supported on my CPU. We will use AVX2 since it is supported by most modern CPUs +// THIS IS CURRENTLY BROKEN. DO NOT USE. it's a great idea, but I've run out of time to debug this. Will be in step 3 +//#define USE_SIMD_CPU + +#ifdef USE_SIMD_CPU + + #include + +#endif + +#include +#include "util/std.h" + +namespace Raytracing { + + // when running on the CPU it's fine to be a double + // Your CPU may be faster with floats. + // but if we move to the GPU it has to be a float. + // since GPUs generally are far more optimized for floats + // If using AVX or other SIMD instructions it should be double, only to fit into 256bits. + // TODO would be to add support for 128bit AVX vectors. + + #ifdef USE_SIMD_CPU + // don't change this. (working on a float version) + typedef double PRECISION_TYPE; + + union AVXConvert { + struct { + double _x, _y, _z, _w; + }; + __m256d avxData; + }; + + class Vec4 { + private: + // makes it easy to convert between AVX and double data types. + union { + struct { + double _x, _y, _z, _w; + }; + __m256d avxData; + }; + + // finally a use for friend! + friend Vec4 operator+(const Vec4& left, const Vec4& right); + friend Vec4 operator-(const Vec4& left, const Vec4& right); + friend Vec4 operator*(const Vec4& left, const Vec4& right); + friend Vec4 operator/(const Vec4& left, const Vec4& right); + public: + + Vec4(): avxData{_mm256_set_pd(0.0, 0.0, 0.0, 0.0)} {} + Vec4(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z): avxData{_mm256_set_pd(x, y, z, 0)} {} + Vec4(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z, PRECISION_TYPE w): avxData{_mm256_set_pd(x, y, z, w)} {} + Vec4(const Vec4& vec): avxData{_mm256_set_pd(vec.x(), vec.y(), vec.z(), vec.w())} {} + + // most of the modern c++ here is because clang tidy was annoying me + [[nodiscard]] inline PRECISION_TYPE x() const { return _x; } + + [[nodiscard]] inline PRECISION_TYPE y() const { return _y; } + + [[nodiscard]] inline PRECISION_TYPE z() const { return _z; } + + [[nodiscard]] inline PRECISION_TYPE w() const { return _w; } + + [[nodiscard]] inline PRECISION_TYPE r() const { return _x; } + + [[nodiscard]] inline PRECISION_TYPE g() const { return _y; } + + [[nodiscard]] inline PRECISION_TYPE b() const { return _z; } + + [[nodiscard]] inline PRECISION_TYPE a() const { return _w; } + + // negation operator + Vec4 operator-() const { return {-x(), -y(), -z(), -w()}; } + + [[nodiscard]] inline PRECISION_TYPE magnitude() const { + return sqrt(lengthSquared()); + } + + [[nodiscard]] inline PRECISION_TYPE lengthSquared() const { + return Vec4::dot(*this, *this); + } + + // returns the unit-vector. + [[nodiscard]] inline Vec4 normalize() const { + PRECISION_TYPE mag = magnitude(); + return {x() / mag, y() / mag, z() / mag, w() / mag}; + } + + // add operator before the vec returns the magnitude + PRECISION_TYPE operator+() const { + return magnitude(); + } + + // preforms the dot product of left * right + static inline PRECISION_TYPE dot(const Vec4& left, const Vec4& right) { + // multiply the elements of the vectors + __m256d mul = _mm256_mul_pd(left.avxData, right.avxData); + // horizontal add. element 0 and 2 (or 1 and 3) contain the results which we must scalar add. + __m256d sum = _mm256_hadd_pd(mul, mul); + AVXConvert conv {}; + conv.avxData = sum; + // boom! dot product. much easier than cross + return conv._x + conv._y; + } + + // preforms the cross product of left X right + // since a general solution to the cross product doesn't exist in 4d + // we are going to ignore the w. + static inline Vec4 cross(const Vec4& left, const Vec4& right) { + // shuffle left values for alignment with the cross algorithm + // (read the shuffle selector from right to left) takes the y and places it in the first element of the resultant vector + // takes the z and places it in the second element of the vector + // takes the x element and places it in the 3rd element of the vector + // and then the w element in the last element of the vector + // creating the alignment {left.y(), left.z(), left.x(), left.w()} (as seen in the cross algorithm + __m256d leftLeftShuffle = _mm256_shuffle_pd(left.avxData, left.avxData, _MM_SHUFFLE(3,0,2,1)); + // same thing but produces {right.z(), right.x(), right.y(), right.w()} + __m256d rightLeftShuffle = _mm256_shuffle_pd(right.avxData, right.avxData, _MM_SHUFFLE(3,1,0,2)); + // multiply to do the first step of the cross process + __m256d multiLeft = _mm256_mul_pd(leftLeftShuffle, rightLeftShuffle); + // now we have to do the right side multiplications + // {left.z(), left.x(), left.y(), left.w()} + __m256d leftRightShuffle = _mm256_shuffle_pd(left.avxData, left.avxData, _MM_SHUFFLE(3,1,0,2)); + // {right.y(), right.z(), right.x(), right.w()} + __m256d rightRightShuffle = _mm256_shuffle_pd(right.avxData, right.avxData, _MM_SHUFFLE(3,0,2,1)); + // multiply the right sides of the subtraction sign + __m256d multiRight = _mm256_mul_pd(leftRightShuffle, rightRightShuffle); + // then subtract to produce the cross product + __m256d subs = _mm256_sub_pd(multiLeft, multiRight); + // yes this looks a lot more complicated, but it should be faster! + AVXConvert conv {}; + conv.avxData = subs; + return {conv._x, conv._y, conv._z, conv._w}; + } + }; + + // adds the two vectors left and right + inline Vec4 operator+(const Vec4& left, const Vec4& right) { + __m256d added = _mm256_add_pd(left.avxData, right.avxData); + AVXConvert conv {}; + conv.avxData = added; + return {conv._x, conv._y, conv._z, conv._w}; + } + + // subtracts the right vector from the left. + inline Vec4 operator-(const Vec4& left, const Vec4& right) { + __m256d subbed = _mm256_sub_pd(left.avxData, right.avxData); + AVXConvert conv {}; + conv.avxData = subbed; + return {conv._x, conv._y, conv._z, conv._w}; + } + + // multiples the left with the right + inline Vec4 operator*(const Vec4& left, const Vec4& right) { + __m256d multiplied = _mm256_mul_pd(left.avxData, right.avxData); + AVXConvert conv {}; + conv.avxData = multiplied; + return {conv._x, conv._y, conv._z, conv._w}; + } + + // divides each element individually + inline Vec4 operator/(const Vec4& left, const Vec4& right) { + __m256d dived = _mm256_div_pd(left.avxData, right.avxData); + AVXConvert conv {}; + conv.avxData = dived; + return {conv._x, conv._y, conv._z, conv._w}; + } + + #else + // change this if you want + typedef double PRECISION_TYPE; + + class Vec4 { + private: + union xType { + PRECISION_TYPE x; + PRECISION_TYPE r; + }; + union yType { + PRECISION_TYPE y; + PRECISION_TYPE g; + }; + union zType { + PRECISION_TYPE z; + PRECISION_TYPE b; + }; + union wType { + PRECISION_TYPE w; + PRECISION_TYPE a; + }; + + struct valueType { + xType v1; + yType v2; + zType v3; + wType v4; + }; + // isn't much of a reason to do it this way + // it's unlikely that we'll need to use the w component + // but it helps better line up with the GPU and other SIMD type instructions, like what's above. + valueType value; + public: + Vec4(): value{0, 0, 0, 0} {} + Vec4(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z): value{x, y, z, 0} {} + Vec4(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z, PRECISION_TYPE w): value{x, y, z, w} {} + Vec4(const Vec4& vec): value{vec.x(), vec.y(), vec.z(), vec.w()} {} + + + // most of the modern c++ here is because clang tidy was annoying me + [[nodiscard]] inline PRECISION_TYPE x() const { return value.v1.x; } + + [[nodiscard]] inline PRECISION_TYPE y() const { return value.v2.y; } + + [[nodiscard]] inline PRECISION_TYPE z() const { return value.v3.z; } + + [[nodiscard]] inline PRECISION_TYPE w() const { return value.v4.w; } + + [[nodiscard]] inline PRECISION_TYPE r() const { return value.v1.r; } + + [[nodiscard]] inline PRECISION_TYPE g() const { return value.v2.g; } + + [[nodiscard]] inline PRECISION_TYPE b() const { return value.v3.b; } + + [[nodiscard]] inline PRECISION_TYPE a() const { return value.v4.a; } + + // negation operator + Vec4 operator-() const { return {-x(), -y(), -z(), -w()}; } + + [[nodiscard]] inline PRECISION_TYPE magnitude() const { + return sqrt(lengthSquared()); + } + + [[nodiscard]] inline PRECISION_TYPE lengthSquared() const { + return x() * x() + y() * y() + z() * z() + w() * w(); + } + + // returns the unit-vector. + [[nodiscard]] inline Vec4 normalize() const { + PRECISION_TYPE mag = magnitude(); + return {x() / mag, y() / mag, z() / mag, w() / mag}; + } + + // add operator before the vec returns the magnitude + PRECISION_TYPE operator+() const { + return magnitude(); + } + + // preforms the dot product of left * right + static inline PRECISION_TYPE dot(const Vec4& left, const Vec4& right) { + return left.x() * right.x() + + left.y() * right.y() + + left.z() * right.z(); + } + + // preforms the cross product of left X right + // since a general solution to the cross product doesn't exist in 4d + // we are going to ignore the w. + static inline Vec4 cross(const Vec4& left, const Vec4& right) { + return {left.y() * right.z() - left.z() * right.y(), + left.z() * right.x() - left.x() * right.z(), + left.x() * right.y() - left.y() * right.x()}; + } + + }; + +// Utility Functions + + // adds the two vectors left and right + inline Vec4 operator+(const Vec4& left, const Vec4& right) { + return {left.x() + right.x(), left.y() + right.y(), left.z() + right.z(), left.w() + right.w()}; + } + + // subtracts the right vector from the left. + inline Vec4 operator-(const Vec4& left, const Vec4& right) { + return {left.x() - right.x(), left.y() - right.y(), left.z() - right.z(), left.w() - right.w()}; + } + + // multiples the left with the right + inline Vec4 operator*(const Vec4& left, const Vec4& right) { + return {left.x() * right.x(), left.y() * right.y(), left.z() * right.z(), left.w() * right.w()}; + } + + // divides each element individually + inline Vec4 operator/(const Vec4& left, const Vec4& right) { + return {left.x() / right.x(), left.y() / right.y(), left.z() / right.z(), left.w() / right.w()}; + } + + #endif + + // none of these can be vectorized with AVX instructions + + // useful for printing out the vector to stdout + inline std::ostream& operator<<(std::ostream& out, const Vec4& v) { + return out << "Vec4{" << v.x() << ", " << v.y() << ", " << v.z() << ", " << v.w() << "} "; + } + + // multiplies the const c with each element in the vector v + inline Vec4 operator*(const PRECISION_TYPE c, const Vec4& v) { + return {c * v.x(), c * v.y(), c * v.z(), c * v.w()}; + } + + // same as above but for right sided constants + inline Vec4 operator*(const Vec4& v, PRECISION_TYPE c) { + return c * v; + } + + // divides the vector by the constant c + inline Vec4 operator/(const Vec4& v, PRECISION_TYPE c) { + return {v.x() / c, v.y() / c, v.z() / c, v.w() / c}; + } + + // divides each element in the vector by over the constant + inline Vec4 operator/(PRECISION_TYPE c, const Vec4& v) { + return {c / v.x(), c / v.y(), c / v.z(), c / v.w()}; + } + + class Ray { + private: + // the starting point for our ray + Vec4 start; + // and the direction it is currently traveling + Vec4 direction; + Vec4 inverseDirection; + public: + Ray(const Vec4& start, const Vec4& direction): start(start), direction(direction), inverseDirection(1 / direction) {} + + [[nodiscard]] Vec4 getStartingPoint() const { return start; } + + [[nodiscard]] Vec4 getDirection() const { return direction; } + + // not always needed, but it's good to not have to calculate the inverse inside the intersection + // as that would be very every AABB, and that is expensive + [[nodiscard]] Vec4 getInverseDirection() const { return inverseDirection; } + + // returns a point along the ray, extended away from start by the length. + [[nodiscard]] inline Vec4 along(PRECISION_TYPE length) const { return start + length * direction; } + + }; + + inline std::ostream& operator<<(std::ostream& out, const Ray& v) { + return out << "Ray{" << v.getStartingPoint() << " " << v.getDirection() << "} "; + } + +} + +#endif //STEP_2_VECTORS_H diff --git a/Step 2/Submission/include/raytracing.h b/Step 2/Submission/include/raytracing.h new file mode 100644 index 0000000..4c1a0ef --- /dev/null +++ b/Step 2/Submission/include/raytracing.h @@ -0,0 +1,109 @@ +/* + * Created by Brett Terpstra 6920201 on 16/10/22. + * Copyright (c) 2022 Brett Terpstra. All Rights Reserved. + * + * The general class for all things raytracing! + */ + +#ifndef STEP_2_RAYTRACING_H +#define STEP_2_RAYTRACING_H + +#include +#include +#include +#include + +#include + +namespace Raytracing { + + class Camera { + private: + /* Image details */ + const Image image; + const PRECISION_TYPE aspectRatio; + + /* Camera details */ + PRECISION_TYPE viewportHeight; + PRECISION_TYPE viewportWidth; + PRECISION_TYPE focalLength = 1.0; + + Vec4 position{0, 0, 0}; + Vec4 horizontalAxis; + Vec4 verticalAxis; + Vec4 imageOrigin; + public: + Camera(PRECISION_TYPE fov, const Image& image): image(image), + aspectRatio(double(image.getWidth()) / double(image.getHeight())) { + // scale the viewport height based on the camera's FOV + viewportHeight = (2.0 * tan(degreeeToRadian(fov) / 2)); + // with must respect the aspect ratio of the image, otherwise we'd get funky results + viewportWidth = (aspectRatio * viewportHeight); + // horizontal direction from the camera. used to translate the camera + horizontalAxis = (Vec4{viewportWidth, 0, 0, 0}); + // virtual direction, also used to translate the camera + verticalAxis = (Vec4{0, viewportHeight, 0, 0}); + // lower left of the camera's view port. used to project our vectors from image space to world space + imageOrigin = (position - horizontalAxis / 2 - verticalAxis / 2 - Vec4(0, 0, focalLength, 0)); + + tlog << viewportHeight << "\n"; + tlog << viewportWidth << "\n"; + tlog << "\n"; + tlog << horizontalAxis << "\n"; + tlog << verticalAxis << "\n"; + tlog << imageOrigin << "\n"; + + } + + Ray projectRay(PRECISION_TYPE x, PRECISION_TYPE y); + // makes the camera look at the lookatpos from the position p, with respects to the up direction up. (set to 0,1,0) + void lookAt(const Vec4& pos, const Vec4& lookAtPos, const Vec4& up); + + void setPosition(const Vec4& pos) { this->position = pos; } + + void setRotation(PRECISION_TYPE yaw, PRECISION_TYPE pitch, PRECISION_TYPE roll); + + }; + + static Random rnd{-1, 1}; + + class Raycaster { + private: + const int maxBounceDepth = 50; + // 50 seems to be the magic number for the point of diminishing returns + // 100 looks like 50 but slightly clearer + // 25 is noisy + // 1 is VERY noisy. + const int raysPerPixel = 50; + + Camera& camera; + Image& image; + World& world; + + Vec4 raycast(const Ray& ray, int depth); + public: + inline static Vec4 randomUnitVector() { + // there are two methods to generating a random unit sphere + // one which is fast and approximate: + auto v = Vec4(rnd.getDouble(), rnd.getDouble(), rnd.getDouble()); + return v.normalize(); + // and the one which generates an actual unit vector + /*while (true) { + auto v = Vec4(rnd.getDouble(), rnd.getDouble(), rnd.getDouble()); + if (v.lengthSquared() >= 1) + continue; + return v; + }*/ + // the second creates better results but is 18% slower (better defined shadows) + // likely due to not over generating unit vectors biased towards the corners + } + Raycaster(Camera& c, Image& i, World& world, const Parser& p): camera(c), image(i), world(world) { + world.generateBVH(); + } + + void run(); + + }; + +} +#endif //STEP_2_RAYTRACING_H diff --git a/Step 2/Submission/include/types.h b/Step 2/Submission/include/types.h new file mode 100644 index 0000000..4a8d04b --- /dev/null +++ b/Step 2/Submission/include/types.h @@ -0,0 +1,126 @@ +/* + * Created by Brett Terpstra 6920201 on 18/10/22. + * Copyright (c) 2022 Brett Terpstra. All Rights Reserved. + */ + +#ifndef STEP_2_TYPES_H +#define STEP_2_TYPES_H + +#include +#include + +#include + +// there were some files which needed access to these types +// but including them from world.h would've resulted in circular includes, +// so I moved them here. + +namespace Raytracing { + + struct HitData { + // all the other values only matter if this is true + bool hit{false}; + // the hit point on the object + Vec4 hitPoint{}; + // the normal of that hit point + Vec4 normal{}; + // the length of the vector from its origin in its direction. + PRECISION_TYPE length{0}; + }; + + struct ScatterResults { + // returns true to recast the ray with the provided ray + bool scattered; + // the new ray to be cast if scattered + Ray newRay; + // the color of the material + Vec4 attenuationColor; + }; + + // triangle type for model loading + struct Triangle { + public: + Vec4 vertex1, vertex2, vertex3; + bool hasNormals = false; + Vec4 normal1, normal2, normal3; + Vec4 uv1, uv2, uv3; + AABB aabb; + + Triangle(const Vec4& v1, const Vec4& v2, const Vec4& v3): vertex1(v1), vertex2(v2), vertex3(v3) {} + + Triangle(const Vec4& v1, const Vec4& v2, const Vec4& v3, + const Vec4& n1, const Vec4& n2, const Vec4& n3): vertex1(v1), vertex2(v2), vertex3(v3), + hasNormals(true), normal1(n1), normal2(n2), normal3(n3) {} + + Triangle(const Vec4& v1, const Vec4& v2, const Vec4& v3, + const Vec4& uv1, const Vec4& uv2, const Vec4& uv3, + const Vec4& n1, const Vec4& n2, const Vec4& n3): vertex1(v1), vertex2(v2), vertex3(v3), + uv1(uv1), uv2(uv2), uv3(uv3), + hasNormals(true), normal1(n1), normal2(n2), normal3(n3) {} + + // slow method, not really required as all normals should be equal + [[nodiscard]] Vec4 findClosestNormal(const Vec4& point) const { + // no need to sqrt as exact distance doesn't matter + auto n1Dist = (point - normal1).lengthSquared(); + auto n2Dist = (point - normal2).lengthSquared(); + auto n3Dist = (point - normal3).lengthSquared(); + return (n1Dist < n2Dist && n1Dist < n3Dist) ? normal1 : (n2Dist < n3Dist ? normal2 : normal3); + } + }; + + // face type for model loading + struct face { + int v1, v2, v3; + int uv1, uv2, uv3; + int n1, n2, n3; + }; + + class Material { + protected: + // most materials will need an albedo + Vec4 baseColor; + public: + explicit Material(const Vec4& baseColor): baseColor(baseColor) {} + + // returns true if the ray was scattered along with the scattered ray, otherwise will return false with empty ray. + // the returned vec4 is the attenuation color + [[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const = 0; + + [[nodiscard]] Vec4 getBaseColor() const { return baseColor; } + virtual ~Material() = default; + }; + + class Object { + protected: + AABB aabb; + Vec4 position; + Material* material; + public: + Object(Material* material, const Vec4& position): material(material), position(position), aabb({}) {}; + // return true if the ray intersects with this object, only between min and max + [[nodiscard]] virtual HitData checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const = 0; + + [[nodiscard]] Material* getMaterial() const { return material; } + virtual Object* clone() = 0; + virtual AABB& getAABB() { return aabb; } + virtual void setAABB(const AABB& ab) { this->aabb = ab; } + [[nodiscard]] Vec4 getPosition() const { return position; } + virtual ~Object() = default; + }; + + // used for using an object, mostly BVH + class EmptyObject : public Object { + protected: + public: + Triangle& tri; + EmptyObject(const Vec4& position, const AABB& a, Triangle& tri): Object(nullptr, position), tri(tri) {this->aabb = a;}; + // unused + [[nodiscard]] virtual HitData checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const { + wlog << "Warning! A empty object has made its way into the raycaster!\n"; + return {}; + } + virtual Object* clone(){return new EmptyObject(position, aabb, tri);} + }; +} + +#endif //STEP_2_TYPES_H diff --git a/Step 2/Submission/include/util/debug.h b/Step 2/Submission/include/util/debug.h new file mode 100644 index 0000000..e818fe2 --- /dev/null +++ b/Step 2/Submission/include/util/debug.h @@ -0,0 +1,84 @@ +/* + * Created by Brett Terpstra 6920201 on 18/10/22. + * Copyright (c) 2022 Brett Terpstra. All Rights Reserved. + * + * this is a direct one-to-one copy of the profiler class used in my Game Engine + * it functions very well especially when used in a GUI context, + * so why reinvent the wheel right? + * So to avoid any kind of self plagiarism, I fully credit the source which is here: + * https://github.com/Tri11Paragon/Trapdoor-Engine/tree/dev/C%2B%2B%20Engine + */ + +#ifndef STEP_2_DEBUG_H +#define STEP_2_DEBUG_H + +#include + +namespace Raytracing { + class profiler; + extern std::unordered_map profiles; + + class DebugTab{ + protected: + std::string name; + public: + virtual void render() {} + std::string getName() { + return name; + } + }; + + class profiler : public DebugTab { + private: + long _start = 0; + long _end = 0; + std::unordered_map> timings; + public: + explicit profiler(std::string name); + + void start(); + void start(const std::string& name); + static void start(const std::string& name, const std::string& tabName) { + auto p = new profiler(name); + profiles.insert(std::pair(name, p)); + p->start(tabName); + } + + void end(); + void end(const std::string& name); + static void end(const std::string& name, const std::string& tabName){ + try { + profiles.at(name)->end(tabName); + } catch (std::exception& e){} + } + + void print(); + static void print(const std::string& name){ + try { + profiles.at(name)->print(); + delete(profiles.at(name)); + } catch (std::exception& e){} + } + + void endAndPrint(); + static void endAndPrint(const std::string& name, const std::string& tabName){ + profiler::end(name, tabName); + profiler::print(name); + } + + void render(); + static void render(int count) { + for (auto p : profiles) + p.second->render(); + } + + ~profiler(); + static void cleanup(){ + for (const auto& p : profiles) + delete(p.second); + } + }; + +} + +#endif //STEP_2_DEBUG_H diff --git a/Step 2/Submission/include/util/logging.h b/Step 2/Submission/include/util/logging.h new file mode 100644 index 0000000..91a0fe2 --- /dev/null +++ b/Step 2/Submission/include/util/logging.h @@ -0,0 +1,27 @@ +/* + * Created by Brett Terpstra 6920201 on 14/10/22. + * Copyright (c) Brett Terpstra 2022 All Rights Reserved + */ + +#ifndef STEP_2_LOGGING_H +#define STEP_2_LOGGING_H + +#include + +// im only going to make this class if I have time (to add dates, output to file, and colored severity) + +// log fatal +#define flog std::cerr << "[Fatal]: " +// log error +#define elog std::cerr << "[Error]: " +// log warning +#define wlog std::cout << "[Warning]: " +// log info +#define ilog std::cout << "[Info]: " +// log debug +#define dlog std::cout << "[Debug]: " +// log trace +#define tlog std::cout << "[Trace]: " + + +#endif //STEP_2_LOGGING_H diff --git a/Step 2/Submission/include/util/models.h b/Step 2/Submission/include/util/models.h new file mode 100644 index 0000000..38479aa --- /dev/null +++ b/Step 2/Submission/include/util/models.h @@ -0,0 +1,91 @@ +/* + * Created by Brett Terpstra 6920201 on 17/10/22. + * Copyright (c) 2022 Brett Terpstra. All Rights Reserved. + */ + +#ifndef STEP_2_MODELS_H +#define STEP_2_MODELS_H + +#include +#include +#include +#include + +namespace Raytracing { + + struct ModelData { + public: + // storing all this data is memory inefficient + // since normals and vertices are only vec3s + // and uvs are vec2s + // TODO: create lower order vector classes + std::vector vertices; + std::vector uvs; + std::vector normals; + std::vector faces; + AABB aabb; + + std::vector toTriangles() { + std::vector triangles; + + PRECISION_TYPE minX = infinity, minY = infinity, minZ = infinity, maxX = ninfinity, maxY = ninfinity, maxZ = ninfinity; + + for (face f: faces) { + Triangle t {vertices[f.v1], vertices[f.v2], vertices[f.v3], + uvs[f.uv1], uvs[f.uv2], uvs[f.uv3], + normals[f.n1], normals[f.n2], normals[f.n3]}; + + PRECISION_TYPE tMinX = infinity, tMinY = infinity, tMinZ = infinity, tMaxX = ninfinity, tMaxY = ninfinity, tMaxZ = ninfinity; + // find the min and max of all the triangles + tMinX = std::min(t.vertex1.x(), std::min(t.vertex2.x(), std::min(t.vertex3.x(), tMinX))); + tMinY = std::min(t.vertex1.y(), std::min(t.vertex2.y(), std::min(t.vertex3.y(), tMinY))); + tMinZ = std::min(t.vertex1.z(), std::min(t.vertex2.z(), std::min(t.vertex3.z(), tMinZ))); + + tMaxX = std::max(t.vertex1.x(), std::max(t.vertex2.x(), std::max(t.vertex3.x(), tMaxX))); + tMaxY = std::max(t.vertex1.y(), std::max(t.vertex2.y(), std::max(t.vertex3.y(), tMaxY))); + tMaxZ = std::max(t.vertex1.z(), std::max(t.vertex2.z(), std::max(t.vertex3.z(), tMaxZ))); + + // create a AABB for model local BVH + t.aabb = {tMinX, tMinY, tMinZ, tMaxX, tMaxY, tMaxZ}; + + // and of course for a model AABB, + minX = std::min(tMinX, minX); + minY = std::min(tMinY, minY); + minZ = std::min(tMinZ, minZ); + + maxX = std::max(tMaxX, maxX); + maxY = std::max(tMaxY, maxY); + maxZ = std::max(tMaxZ, maxZ); + + triangles.push_back(t); + } + // to generate a AABB + aabb = {minX, minY, minZ, maxX, maxY, maxZ}; + + return triangles; + } + + // creates a BVH tree and returns the list of objects we created. make sure to delete them. + static std::vector createBVHTree(std::vector& triangles, const Vec4& pos) { + std::vector objects; + for (auto& tri : triangles){ + Object* obj = new EmptyObject(pos, tri.aabb, tri); + objects.push_back(obj); + } + return objects; + } + }; + + class ModelLoader { + private: + public: + virtual ModelData loadModel(std::string file) = 0; + }; + + class OBJLoader : public ModelLoader { + private: + public: + virtual ModelData loadModel(std::string file); + }; +} +#endif //STEP_2_MODELS_H diff --git a/Step 2/Submission/include/util/parser.h b/Step 2/Submission/include/util/parser.h new file mode 100644 index 0000000..6178039 --- /dev/null +++ b/Step 2/Submission/include/util/parser.h @@ -0,0 +1,49 @@ +/* + * Created by Brett Terpstra 6920201 on 14/10/22. + * Copyright (c) Brett Terpstra 2022 All Rights Reserved + */ + +#ifndef STEP_2_PARSER_H +#define STEP_2_PARSER_H + +#include "util/std.h" + +namespace Raytracing { + + /** + * The parser class is used to parse incoming values from the commandline. + * It is not required that you use it for provided values + * and in fact should work for values provided via stdin + * however they must be provided in single line SSV char* form. + */ + class Parser { + private: + std::unordered_map raw_values; + std::unordered_map defaultValues; + std::vector descriptions; + public: + Parser(); + // Adds an option (or options) with default values. + // default value defaults to an empty string and will not be added + // useful if you want to add a description to the help menu. + void addOption(const std::string& option, const std::string& description, const std::string& defaultValue = ""); + void addOption(const std::vector& options, const std::string& description, const std::string& defaultValue = ""); + // returns true if the option provided is different from the default option. + bool hasOptionChanged(const std::string& option); + // checks if the option has been provided to the parser + bool hasOption(const std::string& option); + // check if any of the options exist, only use for checking options that lead to the same path + // as this will return true at first option. + bool hasOption(const std::vector& options); + // does not check to see if the option exists. + std::string getOptionValue(const std::string& option); + + // parse the options from args + int parse(char** args, int argc); + void printDifferenceInInfo(); + void printAllInInfo(); + }; + +} + +#endif //STEP_2_PARSER_H diff --git a/Step 2/Submission/include/util/std.h b/Step 2/Submission/include/util/std.h new file mode 100644 index 0000000..f76cfc6 --- /dev/null +++ b/Step 2/Submission/include/util/std.h @@ -0,0 +1,155 @@ +/* + * Created by Brett Terpstra 6920201 on 14/10/22. + * Copyright (c) Brett Terpstra 2022 All Rights Reserved + * + * This file is used to include common standard library headers + * There are some things {String, Maps} that I use a lot + * Plus common defines that might be useful in the future. + * + */ + +#ifndef STEP_2_STD_H +#define STEP_2_STD_H + +/** + * includes + */ +#include +#include +#include "util/logging.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * defines + */ +#define RAYTRACING_VERSION_MAJOR 0 +#define RAYTRACING_VERSION_MINOR 0 +#define RAYTRACING_VERSION_PATCH 1 +#define RAYTRACING_VERSION_STRING "0.0.1" + +/** + * Constants + */ +const double infinity = std::numeric_limits::infinity(); +const double ninfinity = -std::numeric_limits::infinity(); +// PI, to a large # of digits +const double PI = 3.1415926535897932385; +// very small number +const double EPSILON = 0.0000001; + +/** + * classes + */ +static inline double degreeeToRadian(double deg){ + return deg * PI/180.0; +} + +namespace Raytracing { + class Random { + private: + std::random_device rd; // obtain a random number from hardware + std::mt19937 gen; + std::uniform_real_distribution doubleDistr {0, 1}; + public: + Random(): gen(std::mt19937(long(rd.entropy() * 691 * 691))) {} + Random(double min, double max): gen(std::mt19937(long(rd.entropy() * 691 * 691))), doubleDistr{min, max} {} + double getDouble(){ + return doubleDistr(gen); + } + }; + class String { + public: + static inline std::string toLowerCase(const std::string& s){ + std::stringstream str; + std::for_each(s.begin(), s.end(), [&str](unsigned char ch) { + str << (char) std::tolower(ch); + }); + return str.str(); + } + static inline std::string toUpperCase(const std::string& s){ + std::stringstream str; + std::for_each(s.begin(), s.end(), [&str](unsigned char ch) { + str << (char) std::toupper(ch); + }); + return str.str(); + } + // taken from https://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c + // extended to return a vector + static inline std::vector split(std::string s, const std::string& delim){ + size_t pos = 0; + std::vector tokens; + while ((pos = s.find(delim)) != std::string::npos) { + auto token = s.substr(0, pos); + tokens.push_back(token); + s.erase(0, pos + delim.length()); + } + tokens.push_back(s); + return tokens; + } + // taken from https://stackoverflow.com/questions/216823/how-to-trim-an-stdstring + // would've preferred to use boost lib but instructions said to avoid external libs + // trim from start (in place) + static inline std::string& ltrim(std::string& s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { + return !std::isspace(ch); + })); + return s; + } + + // trim from end (in place) + static inline std::string& rtrim(std::string& s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { + return !std::isspace(ch); + }).base(), s.end()); + return s; + } + + // trim from both ends (in place) + static inline std::string& trim(std::string& s) { + ltrim(s); + rtrim(s); + return s; + } + + // trim from start (copying) + static inline std::string ltrim_copy(std::string s) { + ltrim(s); + return s; + } + + // trim from end (copying) + static inline std::string rtrim_copy(std::string s) { + rtrim(s); + return s; + } + + // trim from both ends (copying) + static inline std::string trim_copy(std::string s) { + trim(s); + return s; + } + }; +} + +static Raytracing::Random rnd {}; + +static inline double getRandomDouble(){ + return rnd.getDouble(); +} + +static inline double clamp(double val, double min, double max) { + if (val < min) + return min; + if (val > max) + return max; + return val; +} + +#endif //STEP_2_STD_H diff --git a/Step 2/Submission/include/world.h b/Step 2/Submission/include/world.h new file mode 100644 index 0000000..38e3fe4 --- /dev/null +++ b/Step 2/Submission/include/world.h @@ -0,0 +1,137 @@ +/* + * Created by Brett Terpstra 6920201 on 16/10/22. + * Copyright (c) 2022 Brett Terpstra. All Rights Reserved. + */ + +#ifndef STEP_2_WORLD_H +#define STEP_2_WORLD_H + +#include +#include +#include +#include +#include + +#include + +namespace Raytracing { + + class SphereObject : public Object { + private: + PRECISION_TYPE radius; + public: + SphereObject(const Vec4& position, PRECISION_TYPE radius, Material* material): radius(radius), Object(material, position) {} + + [[nodiscard]] virtual HitData checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const; + virtual Object* clone(){ + return new SphereObject(position, radius, material); + } + }; + + class TriangleObject : public Object { + private: + Triangle theTriangle; + public: + TriangleObject(const Vec4& position, Triangle theTriangle, Material* material): Object(material, position), + theTriangle(std::move(theTriangle)) {} + [[nodiscard]] virtual HitData checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const; + virtual Object* clone() { + return new TriangleObject(position, theTriangle, material); + } + }; + + class ModelObject : public Object { + private: + std::vector triangles; + ModelData& data; + // basically we have to store this crap here because c++ loves to copy stuff + //std::vector createdTreeObjects{}; + //BVHTree* tree = nullptr; + public: + ModelObject(const Vec4& position, ModelData& data, Material* material): Object(material, position), data(data) { + // since all of this occurs before the main ray tracing algorithm it's fine to do sequentially + triangles = data.toTriangles(); + this->aabb = data.aabb; + //createdTreeObjects = Raytracing::ModelData::createBVHTree(triangles, position); + //tree = new BVHTree(createdTreeObjects); + } + [[nodiscard]] virtual HitData checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const; + virtual Object* clone() { + return new ModelObject(position, data, material); + } + virtual ~ModelObject() { + // Disabled for now, causing bugs when on release mode. + //for (auto* p : createdTreeObjects) + // delete(p); + //delete(tree); + } + }; + + class DiffuseMaterial : public Material { + private: + public: + explicit DiffuseMaterial(const Vec4& scatterColor): Material(scatterColor) {} + + [[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const; + }; + + class MetalMaterial : public Material { + protected: + static inline Vec4 reflect(const Vec4& incomingVector, const Vec4& normal) { + return incomingVector - 2 * Vec4::dot(incomingVector, normal) * normal; + } + + public: + explicit MetalMaterial(const Vec4& metalColor): Material(metalColor) {} + + [[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const; + }; + + class BrushedMetalMaterial : public MetalMaterial { + private: + PRECISION_TYPE fuzzyness; + public: + explicit BrushedMetalMaterial(const Vec4& metalColor, PRECISION_TYPE fuzzyness): MetalMaterial(metalColor), fuzzyness(fuzzyness) {} + + [[nodiscard]] virtual ScatterResults scatter(const Ray& ray, const HitData& hitData) const; + }; + + class TexturedMaterial : public Material { + public: + TexturedMaterial(const std::string& file): Material({}) { + + } + }; + + class World { + private: + // store all the objects in the world, + std::vector objects; + /*TODO: create a kd-tree or bvh version to store the objects + * this way we can easily tell if a ray is near and object or not + * saving on computation + */ + // TODO: the above todo has been done, now we need to test the performance advantage of the BVH + //BVHTree* bvhTree = nullptr; + std::unordered_map materials; + public: + World() = default; + World(const World& world) = delete; + World(const World&& world) = delete; + + // call this after you've added all the objects to the world. (Called by the raycaster class) + void generateBVH(); + + inline void add(Object* object) { objects.push_back(object); } + + inline void addMaterial(const std::string& materialName, Material* mat) { materials.insert({materialName, mat}); } + + inline Material* getMaterial(const std::string& materialName) { return materials.at(materialName); } + + [[nodiscard]] virtual std::pair checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const; + ~World(); + + }; +} + +#endif //STEP_2_WORLD_H diff --git a/Step 2/Submission/resources/cube.obj b/Step 2/Submission/resources/cube.obj new file mode 100644 index 0000000..cadb6d1 --- /dev/null +++ b/Step 2/Submission/resources/cube.obj @@ -0,0 +1,47 @@ +# Blender 3.3.1 +# www.blender.org +mtllib cube.mtl +o Cube +v -250.000000 -250.000000 250.000000 +v -250.000000 250.000000 250.000000 +v -250.000000 -250.000000 -250.000000 +v -250.000000 250.000000 -250.000000 +v 250.000000 -250.000000 250.000000 +v 250.000000 250.000000 250.000000 +v 250.000000 -250.000000 -250.000000 +v 250.000000 250.000000 -250.000000 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 1.0000 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 -1.0000 +vn -0.0000 1.0000 -0.0000 +vn -0.0000 -1.0000 -0.0000 +vt 0.000000 0.000000 +vt 0.000000 1.000000 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.000000 1.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 0.000000 1.000000 +vt 1.000000 1.000000 +vt 1.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 1.000000 1.000000 +s 0 +f 3/6/1 2/2/1 1/1/1 +f 7/14/2 4/8/2 3/4/2 +f 5/9/3 8/16/3 7/14/3 +f 1/1/4 6/12/4 5/10/4 +f 1/1/5 7/15/5 3/5/5 +f 6/13/6 4/8/6 8/16/6 +f 3/6/1 4/7/1 2/2/1 +f 7/14/2 8/16/2 4/8/2 +f 5/9/3 6/11/3 8/16/3 +f 1/1/4 2/2/4 6/12/4 +f 1/1/5 5/10/5 7/15/5 +f 6/13/6 2/3/6 4/8/6 diff --git a/Step 2/Submission/resources/cubeflipped.obj b/Step 2/Submission/resources/cubeflipped.obj new file mode 100644 index 0000000..35c3c94 --- /dev/null +++ b/Step 2/Submission/resources/cubeflipped.obj @@ -0,0 +1,47 @@ +# Blender 3.3.1 +# www.blender.org +mtllib cubeflipped.mtl +o Cube +v -250.000000 -250.000000 250.000000 +v -250.000000 250.000000 250.000000 +v -250.000000 -250.000000 -250.000000 +v -250.000000 250.000000 -250.000000 +v 250.000000 -250.000000 250.000000 +v 250.000000 250.000000 250.000000 +v 250.000000 -250.000000 -250.000000 +v 250.000000 250.000000 -250.000000 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 -1.0000 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 1.0000 +vn -0.0000 -1.0000 -0.0000 +vn -0.0000 1.0000 -0.0000 +vt 0.000000 0.000000 +vt 0.000000 1.000000 +vt 0.000000 0.000000 +vt 0.000000 0.000000 +vt 0.000000 1.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 0.000000 1.000000 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 0.000000 1.000000 +vt 1.000000 1.000000 +vt 1.000000 0.000000 +vt 1.000000 0.000000 +vt 1.000000 1.000000 +vt 1.000000 1.000000 +s 0 +f 3/6/1 1/1/1 2/2/1 +f 7/14/2 3/4/2 4/8/2 +f 5/9/3 7/14/3 8/16/3 +f 1/1/4 5/10/4 6/12/4 +f 1/1/5 3/5/5 7/15/5 +f 6/13/6 8/16/6 4/8/6 +f 3/6/1 2/2/1 4/7/1 +f 7/14/2 4/8/2 8/16/2 +f 5/9/3 8/16/3 6/11/3 +f 1/1/4 6/12/4 2/2/4 +f 1/1/5 7/15/5 5/10/5 +f 6/13/6 4/8/6 2/3/6 diff --git a/Step 2/Submission/resources/house.obj b/Step 2/Submission/resources/house.obj new file mode 100644 index 0000000..b713578 --- /dev/null +++ b/Step 2/Submission/resources/house.obj @@ -0,0 +1,255 @@ +# Blender 3.3.1 +# www.blender.org +mtllib house.mtl +o Cube +v -0.800000 -1.000000 1.000000 +v -0.800000 1.000000 1.000000 +v -0.800000 -1.000000 -1.000000 +v -0.800000 1.000000 -1.000000 +v 0.800000 -1.000000 1.000000 +v 0.800000 1.000000 1.000000 +v 0.800000 -1.000000 -1.000000 +v 0.800000 1.000000 -1.000000 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 -1.0000 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 1.0000 +vn -0.0000 -1.0000 -0.0000 +vn -0.0000 1.0000 -0.0000 +vt 0.375000 0.000000 +vt 0.375000 1.000000 +vt 0.125000 0.750000 +vt 0.625000 0.000000 +vt 0.625000 1.000000 +vt 0.875000 0.750000 +vt 0.125000 0.500000 +vt 0.375000 0.250000 +vt 0.625000 0.250000 +vt 0.875000 0.500000 +vt 0.375000 0.750000 +vt 0.625000 0.750000 +vt 0.375000 0.500000 +vt 0.625000 0.500000 +s 0 +f 2/4/1 3/8/1 1/1/1 +f 4/9/2 7/13/2 3/8/2 +f 8/14/3 5/11/3 7/13/3 +f 6/12/4 1/2/4 5/11/4 +f 7/13/5 1/3/5 3/7/5 +f 4/10/6 6/12/6 8/14/6 +f 2/4/1 4/9/1 3/8/1 +f 4/9/2 8/14/2 7/13/2 +f 8/14/3 6/12/3 5/11/3 +f 6/12/4 2/5/4 1/2/4 +f 7/13/5 5/11/5 1/3/5 +f 4/10/6 2/6/6 6/12/6 +o Cube.001 +v -1.000000 1.719057 0.057522 +v -1.000000 1.832386 -0.037572 +v -1.000000 0.433482 -1.474567 +v -1.000000 0.546811 -1.569661 +v 1.000000 1.719057 0.057522 +v 1.000000 1.832386 -0.037572 +v 1.000000 0.433482 -1.474567 +v 1.000000 0.546811 -1.569661 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 -0.6428 -0.7660 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 0.6428 0.7660 +vn -0.0000 -0.7660 0.6428 +vn -0.0000 0.7660 -0.6428 +vt 0.375000 0.000000 +vt 0.375000 1.000000 +vt 0.125000 0.750000 +vt 0.625000 0.000000 +vt 0.625000 1.000000 +vt 0.875000 0.750000 +vt 0.125000 0.500000 +vt 0.375000 0.250000 +vt 0.625000 0.250000 +vt 0.875000 0.500000 +vt 0.375000 0.750000 +vt 0.625000 0.750000 +vt 0.375000 0.500000 +vt 0.625000 0.500000 +s 0 +f 10/18/7 11/22/7 9/15/7 +f 12/23/8 15/27/8 11/22/8 +f 16/28/9 13/25/9 15/27/9 +f 14/26/10 9/16/10 13/25/10 +f 15/27/11 9/17/11 11/21/11 +f 12/24/12 14/26/12 16/28/12 +f 10/18/7 12/23/7 11/22/7 +f 12/23/8 16/28/8 15/27/8 +f 16/28/9 14/26/9 13/25/9 +f 14/26/10 10/19/10 9/16/10 +f 15/27/11 13/25/11 9/17/11 +f 12/24/12 10/20/12 14/26/12 +o Cube.002 +v -1.000000 0.433482 1.461952 +v -1.000000 0.546811 1.557046 +v -1.000000 1.719057 -0.070137 +v -1.000000 1.832386 0.024957 +v 1.000000 0.433482 1.461952 +v 1.000000 0.546811 1.557046 +v 1.000000 1.719057 -0.070137 +v 1.000000 1.832386 0.024957 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 0.6428 -0.7660 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 -0.6428 0.7660 +vn -0.0000 -0.7660 -0.6428 +vn -0.0000 0.7660 0.6428 +vt 0.375000 0.000000 +vt 0.375000 1.000000 +vt 0.125000 0.750000 +vt 0.625000 0.000000 +vt 0.625000 1.000000 +vt 0.875000 0.750000 +vt 0.125000 0.500000 +vt 0.375000 0.250000 +vt 0.625000 0.250000 +vt 0.875000 0.500000 +vt 0.375000 0.750000 +vt 0.625000 0.750000 +vt 0.375000 0.500000 +vt 0.625000 0.500000 +s 0 +f 18/32/13 19/36/13 17/29/13 +f 20/37/14 23/41/14 19/36/14 +f 24/42/15 21/39/15 23/41/15 +f 22/40/16 17/30/16 21/39/16 +f 23/41/17 17/31/17 19/35/17 +f 20/38/18 22/40/18 24/42/18 +f 18/32/13 20/37/13 19/36/13 +f 20/37/14 24/42/14 23/41/14 +f 24/42/15 22/40/15 21/39/15 +f 22/40/16 18/33/16 17/30/16 +f 23/41/17 21/39/17 17/31/17 +f 20/38/18 18/34/18 22/40/18 +o Cube.003 +v 0.787552 -0.999883 0.157434 +v 0.787552 -0.426687 0.157434 +v 0.787552 -0.999883 -0.157434 +v 0.787552 -0.426687 -0.157434 +v 0.836243 -0.999883 0.157434 +v 0.836243 -0.426687 0.157434 +v 0.836243 -0.999883 -0.157434 +v 0.836243 -0.426687 -0.157434 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 -1.0000 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 1.0000 +vn -0.0000 -1.0000 -0.0000 +vn -0.0000 1.0000 -0.0000 +vt 0.375000 0.000000 +vt 0.375000 1.000000 +vt 0.125000 0.750000 +vt 0.625000 0.000000 +vt 0.625000 1.000000 +vt 0.875000 0.750000 +vt 0.125000 0.500000 +vt 0.375000 0.250000 +vt 0.625000 0.250000 +vt 0.875000 0.500000 +vt 0.375000 0.750000 +vt 0.625000 0.750000 +vt 0.375000 0.500000 +vt 0.625000 0.500000 +s 0 +f 26/46/19 27/50/19 25/43/19 +f 28/51/20 31/55/20 27/50/20 +f 32/56/21 29/53/21 31/55/21 +f 30/54/22 25/44/22 29/53/22 +f 31/55/23 25/45/23 27/49/23 +f 28/52/24 30/54/24 32/56/24 +f 26/46/19 28/51/19 27/50/19 +f 28/51/20 32/56/20 31/55/20 +f 32/56/21 30/54/21 29/53/21 +f 30/54/22 26/47/22 25/44/22 +f 31/55/23 29/53/23 25/45/23 +f 28/52/24 26/48/24 30/54/24 +o Cube.004 +v 0.787552 -0.090162 0.721031 +v 0.787552 0.409838 0.721031 +v 0.787552 -0.090162 0.221031 +v 0.787552 0.409838 0.221031 +v 0.836243 -0.090162 0.721031 +v 0.836243 0.409838 0.721031 +v 0.836243 -0.090162 0.221031 +v 0.836243 0.409838 0.221031 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 -1.0000 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 1.0000 +vn -0.0000 -1.0000 -0.0000 +vn -0.0000 1.0000 -0.0000 +vt 0.375000 0.000000 +vt 0.375000 1.000000 +vt 0.125000 0.750000 +vt 0.625000 0.000000 +vt 0.625000 1.000000 +vt 0.875000 0.750000 +vt 0.125000 0.500000 +vt 0.375000 0.250000 +vt 0.625000 0.250000 +vt 0.875000 0.500000 +vt 0.375000 0.750000 +vt 0.625000 0.750000 +vt 0.375000 0.500000 +vt 0.625000 0.500000 +s 0 +f 34/60/25 35/64/25 33/57/25 +f 36/65/26 39/69/26 35/64/26 +f 40/70/27 37/67/27 39/69/27 +f 38/68/28 33/58/28 37/67/28 +f 39/69/29 33/59/29 35/63/29 +f 36/66/30 38/68/30 40/70/30 +f 34/60/25 36/65/25 35/64/25 +f 36/65/26 40/70/26 39/69/26 +f 40/70/27 38/68/27 37/67/27 +f 38/68/28 34/61/28 33/58/28 +f 39/69/29 37/67/29 33/59/29 +f 36/66/30 34/62/30 38/68/30 +o Cube.005 +v 0.787552 -0.090162 -0.227981 +v 0.787552 0.409838 -0.227981 +v 0.787552 -0.090162 -0.727981 +v 0.787552 0.409838 -0.727981 +v 0.836243 -0.090162 -0.227981 +v 0.836243 0.409838 -0.227981 +v 0.836243 -0.090162 -0.727981 +v 0.836243 0.409838 -0.727981 +vn -1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 -1.0000 +vn 1.0000 -0.0000 -0.0000 +vn -0.0000 -0.0000 1.0000 +vn -0.0000 -1.0000 -0.0000 +vn -0.0000 1.0000 -0.0000 +vt 0.375000 0.000000 +vt 0.375000 1.000000 +vt 0.125000 0.750000 +vt 0.625000 0.000000 +vt 0.625000 1.000000 +vt 0.875000 0.750000 +vt 0.125000 0.500000 +vt 0.375000 0.250000 +vt 0.625000 0.250000 +vt 0.875000 0.500000 +vt 0.375000 0.750000 +vt 0.625000 0.750000 +vt 0.375000 0.500000 +vt 0.625000 0.500000 +s 0 +f 42/74/31 43/78/31 41/71/31 +f 44/79/32 47/83/32 43/78/32 +f 48/84/33 45/81/33 47/83/33 +f 46/82/34 41/72/34 45/81/34 +f 47/83/35 41/73/35 43/77/35 +f 44/80/36 46/82/36 48/84/36 +f 42/74/31 44/79/31 43/78/31 +f 44/79/32 48/84/32 47/83/32 +f 48/84/33 46/82/33 45/81/33 +f 46/82/34 42/75/34 41/72/34 +f 47/83/35 45/81/35 41/73/35 +f 44/80/36 42/76/36 46/82/36 diff --git a/Step 2/Submission/resources/plane.mtl b/Step 2/Submission/resources/plane.mtl new file mode 100644 index 0000000..9b8de8c --- /dev/null +++ b/Step 2/Submission/resources/plane.mtl @@ -0,0 +1,2 @@ +# Blender 3.3.1 MTL File: 'None' +# www.blender.org diff --git a/Step 2/Submission/resources/plane.obj b/Step 2/Submission/resources/plane.obj new file mode 100644 index 0000000..fa440c2 --- /dev/null +++ b/Step 2/Submission/resources/plane.obj @@ -0,0 +1,16 @@ +# Blender 3.3.1 +# www.blender.org +mtllib plane.mtl +o Plane +v 0.000001 -8.781555 -9.817993 +v -0.000000 -8.781555 9.817993 +v 0.000000 8.781555 -9.817993 +v -0.000001 8.781555 9.817993 +vn 1.0000 -0.0000 -0.0000 +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 0.000000 1.000000 +vt 1.000000 1.000000 +s 0 +f 3/3/1 2/2/1 1/1/1 +f 3/3/1 4/4/1 2/2/1 diff --git a/Step 2/Submission/resources/spider.obj b/Step 2/Submission/resources/spider.obj new file mode 100755 index 0000000..a0d90cd --- /dev/null +++ b/Step 2/Submission/resources/spider.obj @@ -0,0 +1,1244 @@ +# Blender v2.83.1 OBJ File: 'spider.blend' +# www.blender.org +mtllib spider.mtl +o Icosphere +v 0.000000 -0.639720 0.000000 +v 1.251673 -0.286095 0.525725 +v -0.478087 -0.286096 0.850649 +v -1.547150 -0.286093 0.000000 +v -0.478087 -0.286096 -0.850649 +v 1.251673 -0.286095 -0.525725 +v 0.478087 0.286096 0.850649 +v -1.251673 0.286095 0.525725 +v -1.251673 0.286095 -0.525725 +v 0.478087 0.286096 -0.850649 +v 1.547150 0.286093 0.000000 +v 0.000000 0.639720 0.000000 +v -0.281010 -0.544181 0.499995 +v 0.735710 -0.544181 0.309011 +v 0.454702 -0.336325 0.809012 +v 1.471424 -0.336324 0.000000 +v 0.735710 -0.544181 -0.309011 +v -0.909391 -0.544179 0.000000 +v -1.190408 -0.336324 0.499997 +v -0.281010 -0.544181 -0.499995 +v -1.190408 -0.336324 -0.499997 +v 0.454702 -0.336325 -0.809012 +v 1.645110 0.000000 0.309013 +v 1.645110 0.000000 -0.309013 +v 0.000000 0.000000 1.000000 +v 1.016733 0.000000 0.809017 +v -1.645110 0.000000 0.309013 +v -1.016733 0.000000 0.809017 +v -1.016733 0.000000 -0.809017 +v -1.645110 0.000000 -0.309013 +v 1.016733 0.000000 -0.809017 +v 0.000000 0.000000 -1.000000 +v 1.190408 0.336324 0.499997 +v -0.454702 0.336325 0.809012 +v -1.471424 0.336324 0.000000 +v -0.454702 0.336325 -0.809012 +v 1.190408 0.336324 -0.499997 +v 0.281010 0.544181 0.499995 +v 0.909391 0.544179 0.000000 +v -0.735710 0.544181 0.309011 +v -0.735710 0.544181 -0.309011 +v 0.281010 0.544181 -0.499995 +vt 0.894631 0.600965 +vt 0.877483 0.575144 +vt 0.890372 0.578167 +vt 0.877483 0.696946 +vt 0.877483 0.665862 +vt 0.886522 0.675615 +vt 0.917457 0.603731 +vt 0.921307 0.616508 +vt 0.894631 0.640040 +vt 0.921307 0.624497 +vt 0.896601 0.642166 +vt 0.894919 0.712068 +vt 0.899411 0.549203 +vt 0.880293 0.544718 +vt 0.903958 0.519184 +vt 0.945491 0.592696 +vt 0.920267 0.573306 +vt 0.955749 0.562418 +vt 0.952041 0.614433 +vt 0.951202 0.611647 +vt 0.978718 0.598890 +vt 0.910009 0.656632 +vt 0.930346 0.634250 +vt 0.941123 0.662807 +vt 0.891069 0.724844 +vt 0.928664 0.536853 +vt 0.974868 0.586114 +vt 0.952041 0.626573 +vt 0.978718 0.642115 +vt 0.965828 0.645138 +vt 0.914038 0.688372 +vt 0.917746 0.740387 +vt 0.918585 0.737601 +vt 0.948480 0.742462 +vt 0.959778 0.530678 +vt 0.939441 0.508296 +vt 0.973186 0.516212 +vt 0.992304 0.570992 +vt 0.983265 0.549661 +vt 0.992304 0.539908 +vt 0.970375 0.675157 +vt 0.989494 0.670672 +vt 0.979415 0.704121 +vt 0.924295 0.718650 +vt 0.949520 0.699260 +vt 0.952330 0.729685 +vt 0.975156 0.726919 +vt 0.992304 0.701098 +vt 0.992304 0.670013 +vt 0.975156 0.514086 +vt 0.948480 0.498543 +vt 0.917746 0.500619 +vt 0.891069 0.516161 +vt 0.877483 0.544059 +vn 0.0392 -0.9772 0.2088 +vn 0.3543 -0.9052 0.2346 +vn -0.1036 -0.9861 0.1302 +vn -0.1036 -0.9861 -0.1302 +vn 0.0392 -0.9772 -0.2088 +vn 0.6686 -0.6600 0.3425 +vn 0.0133 -0.4801 0.8771 +vn -0.6477 -0.6504 0.3968 +vn -0.3430 -0.5333 -0.7732 +vn 0.3142 -0.5251 -0.7909 +vn 0.6021 -0.2548 0.7567 +vn -0.1809 -0.2004 0.9629 +vn -0.9461 -0.3239 0.0000 +vn -0.1809 -0.2004 -0.9629 +vn 0.6021 -0.2548 -0.7567 +vn 0.1917 0.8388 0.5096 +vn -0.2236 0.8490 0.4787 +vn -0.3543 0.9052 -0.2346 +vn 0.0174 0.8099 -0.5863 +vn 0.3695 0.9132 -0.1719 +vn 0.1036 0.9861 -0.1302 +vn 0.2146 0.9387 -0.2697 +vn 0.1917 0.8388 -0.5096 +vn -0.0392 0.9772 -0.2088 +vn -0.0789 0.9040 -0.4202 +vn -0.2236 0.8490 -0.4787 +vn -0.1288 0.9917 0.0000 +vn -0.2719 0.9623 0.0000 +vn -0.3543 0.9052 0.2346 +vn -0.0392 0.9772 0.2088 +vn -0.0789 0.9040 0.4202 +vn 0.0174 0.8099 0.5863 +vn 0.1036 0.9861 0.1302 +vn 0.2146 0.9387 0.2697 +vn 0.3695 0.9132 0.1719 +vn 0.6477 0.6504 -0.3968 +vn 0.5786 0.3693 -0.7272 +vn 0.3430 0.5333 -0.7732 +vn -0.0133 0.4801 -0.8771 +vn -0.1764 0.2948 -0.9391 +vn -0.3142 0.5251 -0.7909 +vn -0.6686 0.6600 -0.3425 +vn -0.8885 0.4589 0.0000 +vn -0.6686 0.6600 0.3425 +vn -0.3142 0.5251 0.7909 +vn -0.1764 0.2948 0.9391 +vn -0.0133 0.4801 0.8771 +vn 0.3430 0.5333 0.7732 +vn 0.5786 0.3693 0.7272 +vn 0.6477 0.6504 0.3968 +vn 0.1809 0.2004 -0.9629 +vn 0.1764 -0.2948 -0.9391 +vn 0.0133 -0.4801 -0.8771 +vn -0.6021 0.2548 -0.7567 +vn -0.5786 -0.3693 -0.7272 +vn -0.6477 -0.6504 -0.3968 +vn -0.6021 0.2548 0.7567 +vn -0.5786 -0.3693 0.7272 +vn -0.3430 -0.5333 0.7732 +vn 0.1809 0.2004 0.9629 +vn 0.1764 -0.2948 0.9391 +vn 0.3142 -0.5251 0.7909 +vn 0.9461 0.3239 0.0000 +vn 0.8885 -0.4589 0.0000 +vn 0.6686 -0.6600 -0.3425 +vn 0.2236 -0.8490 -0.4787 +vn 0.0789 -0.9040 -0.4202 +vn -0.0174 -0.8099 -0.5863 +vn -0.1917 -0.8388 -0.5096 +vn -0.2146 -0.9387 -0.2697 +vn -0.3695 -0.9132 -0.1719 +vn -0.3695 -0.9132 0.1719 +vn -0.2146 -0.9387 0.2697 +vn -0.1917 -0.8388 0.5096 +vn 0.3543 -0.9052 -0.2346 +vn 0.2719 -0.9623 0.0000 +vn 0.1288 -0.9917 0.0000 +vn -0.0174 -0.8099 0.5863 +vn 0.0789 -0.9040 0.4202 +vn 0.2236 -0.8490 0.4787 +usemtl yes +s off +f 1/1/1 14/2/1 13/3/1 +f 2/4/2 14/5/2 16/6/2 +f 1/1/3 13/3/3 18/7/3 +f 1/1/4 18/7/4 20/8/4 +f 1/9/5 20/10/5 17/11/5 +f 2/4/6 16/6/6 23/12/6 +f 3/13/7 15/14/7 25/15/7 +f 4/16/8 19/17/8 27/18/8 +f 5/19/9 21/20/9 29/21/9 +f 6/22/10 22/23/10 31/24/10 +f 2/4/11 23/12/11 26/25/11 +f 3/13/12 25/15/12 28/26/12 +f 4/16/13 27/18/13 30/27/13 +f 5/28/14 29/29/14 32/30/14 +f 6/22/15 31/24/15 24/31/15 +f 7/32/16 33/33/16 38/34/16 +f 8/35/17 34/36/17 40/37/17 +f 9/38/18 35/39/18 41/40/18 +f 10/41/19 36/42/19 42/43/19 +f 11/44/20 37/45/20 39/46/20 +f 39/46/21 42/43/21 12/47/21 +f 39/46/22 37/45/22 42/43/22 +f 37/45/23 10/41/23 42/43/23 +f 42/43/24 41/48/24 12/47/24 +f 42/43/25 36/42/25 41/48/25 +f 36/42/26 9/49/26 41/48/26 +f 41/40/27 40/37/27 12/50/27 +f 41/40/28 35/39/28 40/37/28 +f 35/39/29 8/35/29 40/37/29 +f 40/37/30 38/51/30 12/50/30 +f 40/37/31 34/36/31 38/51/31 +f 34/36/32 7/52/32 38/51/32 +f 38/34/33 39/46/33 12/47/33 +f 38/34/34 33/33/34 39/46/34 +f 33/33/35 11/44/35 39/46/35 +f 24/31/36 37/45/36 11/44/36 +f 24/31/37 31/24/37 37/45/37 +f 31/24/38 10/41/38 37/45/38 +f 32/30/39 36/42/39 10/41/39 +f 32/30/40 29/29/40 36/42/40 +f 29/29/41 9/49/41 36/42/41 +f 30/27/42 35/39/42 9/38/42 +f 30/27/43 27/18/43 35/39/43 +f 27/18/44 8/35/44 35/39/44 +f 28/26/45 34/36/45 8/35/45 +f 28/26/46 25/15/46 34/36/46 +f 25/15/47 7/52/47 34/36/47 +f 26/25/48 33/33/48 7/32/48 +f 26/25/49 23/12/49 33/33/49 +f 23/12/50 11/44/50 33/33/50 +f 31/24/51 32/30/51 10/41/51 +f 31/24/52 22/23/52 32/30/52 +f 22/23/53 5/28/53 32/30/53 +f 29/21/54 30/27/54 9/38/54 +f 29/21/55 21/20/55 30/27/55 +f 21/20/56 4/16/56 30/27/56 +f 27/18/57 28/26/57 8/35/57 +f 27/18/58 19/17/58 28/26/58 +f 19/17/59 3/13/59 28/26/59 +f 25/15/60 26/53/60 7/52/60 +f 25/15/61 15/14/61 26/53/61 +f 15/14/62 2/54/62 26/53/62 +f 23/12/63 24/31/63 11/44/63 +f 23/12/64 16/6/64 24/31/64 +f 16/6/65 6/22/65 24/31/65 +f 17/11/66 22/23/66 6/22/66 +f 17/11/67 20/10/67 22/23/67 +f 20/10/68 5/28/68 22/23/68 +f 20/8/69 21/20/69 5/19/69 +f 20/8/70 18/7/70 21/20/70 +f 18/7/71 4/16/71 21/20/71 +f 18/7/72 19/17/72 4/16/72 +f 18/7/73 13/3/73 19/17/73 +f 13/3/74 3/13/74 19/17/74 +f 16/6/75 17/11/75 6/22/75 +f 16/6/76 14/5/76 17/11/76 +f 14/5/77 1/9/77 17/11/77 +f 13/3/78 15/14/78 3/13/78 +f 13/3/79 14/2/79 15/14/79 +f 14/2/80 2/54/80 15/14/80 +o Cube_Cube.001 +v -1.168705 -1.257079 2.143840 +v -0.824656 0.106843 0.722100 +v -1.136625 -1.403357 2.011274 +v -0.792577 -0.039434 0.589534 +v -0.974315 -1.257079 2.190881 +v -0.630267 0.106843 0.769140 +v -0.942236 -1.403357 2.058315 +v -0.598187 -0.039434 0.636574 +vt 0.612665 0.123532 +vt 0.732678 0.002422 +vt 0.732678 0.123532 +vt 0.002400 0.547896 +vt 0.122413 0.426786 +vt 0.122413 0.547896 +vt 0.128704 0.367808 +vt 0.248718 0.246698 +vt 0.248718 0.367808 +vt 0.002400 0.552740 +vt 0.122413 0.673850 +vt 0.002400 0.673850 +vt 0.128704 0.997578 +vt 0.248718 0.876468 +vt 0.248718 0.997578 +vt 0.747972 0.871624 +vt 0.627959 0.750514 +vt 0.747972 0.750514 +vt 0.612665 0.002422 +vt 0.002400 0.426786 +vt 0.128704 0.246698 +vt 0.122413 0.552740 +vt 0.128704 0.876468 +vt 0.627959 0.871624 +vn -0.9719 0.0000 -0.2352 +vn 0.1604 -0.7314 -0.6628 +vn 0.9719 -0.0000 0.2352 +vn -0.1604 0.7314 0.6628 +vn -0.1720 -0.6820 0.7109 +vn 0.1720 0.6820 -0.7109 +usemtl None +s off +f 44/55/81 45/56/81 43/57/81 +f 46/58/82 49/59/82 45/60/82 +f 50/61/83 47/62/83 49/63/83 +f 48/64/84 43/65/84 47/66/84 +f 49/67/85 43/68/85 45/69/85 +f 46/70/86 48/71/86 50/72/86 +f 44/55/81 46/73/81 45/56/81 +f 46/58/82 50/74/82 49/59/82 +f 50/61/83 48/75/83 47/62/83 +f 48/64/84 44/76/84 43/65/84 +f 49/67/85 47/77/85 43/68/85 +f 46/70/86 44/78/86 48/71/86 +o Cube.001_Cube.002 +v -0.100000 -0.018407 0.617654 +v -0.100000 -1.437380 2.027092 +v -0.100000 0.122537 0.759552 +v -0.100000 -1.296436 2.168990 +v 0.100000 -0.018407 0.617654 +v 0.100000 -1.437380 2.027092 +v 0.100000 0.122537 0.759552 +v 0.100000 -1.296436 2.168990 +vt 0.253518 0.745670 +vt 0.373531 0.624560 +vt 0.373531 0.745670 +vt 0.487851 0.123532 +vt 0.607864 0.002422 +vt 0.607864 0.123532 +vt 0.253518 0.367808 +vt 0.373531 0.246698 +vt 0.373531 0.367808 +vt 0.128704 0.624560 +vt 0.248718 0.745670 +vt 0.128704 0.745670 +vt 0.253518 0.997578 +vt 0.373531 0.876468 +vt 0.373531 0.997578 +vt 0.872786 0.367808 +vt 0.752773 0.246698 +vt 0.872786 0.246698 +vt 0.253518 0.624560 +vt 0.487851 0.002422 +vt 0.253518 0.246698 +vt 0.248718 0.624560 +vt 0.253518 0.876468 +vt 0.752773 0.367808 +vn -1.0000 -0.0000 0.0000 +vn 0.0000 0.7047 0.7095 +vn 1.0000 0.0000 0.0000 +vn 0.0000 -0.7047 -0.7095 +vn 0.0000 0.7095 -0.7047 +vn 0.0000 -0.7095 0.7047 +usemtl None +s off +f 52/79/87 53/80/87 51/81/87 +f 54/82/88 57/83/88 53/84/88 +f 58/85/89 55/86/89 57/87/89 +f 56/88/90 51/89/90 55/90/90 +f 57/91/91 51/92/91 53/93/91 +f 54/94/92 56/95/92 58/96/92 +f 52/79/87 54/97/87 53/80/87 +f 54/82/88 58/98/88 57/83/88 +f 58/85/89 56/99/89 55/86/89 +f 56/88/90 52/100/90 51/89/90 +f 57/91/91 55/101/91 51/92/91 +f 54/94/92 52/102/92 56/95/92 +o Cube.002_Cube.003 +v 1.189099 -1.273681 2.080395 +v 0.651476 0.053127 0.683777 +v 1.141434 -1.423333 1.956571 +v 0.603811 -0.096525 0.559953 +v 1.375748 -1.273681 2.008545 +v 0.838124 0.053127 0.611927 +v 1.328082 -1.423333 1.884722 +v 0.790459 -0.096525 0.488104 +vt 0.503146 0.745670 +vt 0.623159 0.624560 +vt 0.623159 0.745670 +vt 0.128704 0.619716 +vt 0.248718 0.498606 +vt 0.248718 0.619716 +vt 0.002400 0.421942 +vt 0.122413 0.300832 +vt 0.122413 0.421942 +vt 0.378332 0.624560 +vt 0.498345 0.745670 +vt 0.378332 0.745670 +vt 0.378332 0.997578 +vt 0.498345 0.876468 +vt 0.498345 0.997578 +vt 0.872786 0.493762 +vt 0.752773 0.372652 +vt 0.872786 0.372652 +vt 0.503146 0.624560 +vt 0.128704 0.498606 +vt 0.002400 0.300833 +vt 0.498345 0.624560 +vt 0.378332 0.876468 +vt 0.752773 0.493762 +vn -0.9332 0.0000 0.3592 +vn -0.2383 -0.7483 -0.6191 +vn 0.9332 -0.0000 -0.3592 +vn 0.2383 0.7483 0.6191 +vn 0.2688 -0.6634 0.6983 +vn -0.2688 0.6634 -0.6983 +usemtl None +s off +f 60/103/93 61/104/93 59/105/93 +f 62/106/94 65/107/94 61/108/94 +f 66/109/95 63/110/95 65/111/95 +f 64/112/96 59/113/96 63/114/96 +f 65/115/97 59/116/97 61/117/97 +f 62/118/98 64/119/98 66/120/98 +f 60/103/93 62/121/93 61/104/93 +f 62/106/94 66/122/94 65/107/94 +f 66/109/95 64/123/95 63/110/95 +f 64/112/96 60/124/96 59/113/96 +f 65/115/97 63/125/97 59/116/97 +f 62/118/98 60/126/98 64/119/98 +o Cube.003_Cube.004 +v -0.100000 -1.378174 -1.981819 +v -0.100000 0.086182 -0.619591 +v -0.100000 -1.241951 -2.128254 +v -0.100000 0.222404 -0.766027 +v 0.100000 -1.378174 -1.981819 +v 0.100000 0.086182 -0.619591 +v 0.100000 -1.241951 -2.128254 +v 0.100000 0.222404 -0.766027 +vt 0.627959 0.493762 +vt 0.747972 0.372652 +vt 0.747972 0.493762 +vt 0.253518 0.619716 +vt 0.373531 0.498606 +vt 0.373531 0.619716 +vt 0.363037 0.123532 +vt 0.483050 0.002422 +vt 0.483050 0.123532 +vt 0.627959 0.246698 +vt 0.747972 0.367808 +vt 0.627959 0.367808 +vt 0.503145 0.997578 +vt 0.623159 0.876468 +vt 0.623159 0.997578 +vt 0.872786 0.619716 +vt 0.752773 0.498606 +vt 0.872786 0.498606 +vt 0.627959 0.372652 +vt 0.253518 0.498606 +vt 0.363037 0.002422 +vt 0.747972 0.246698 +vt 0.503145 0.876468 +vt 0.752773 0.619716 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.6811 -0.7322 +vn 1.0000 -0.0000 0.0000 +vn 0.0000 -0.6811 0.7322 +vn 0.0000 -0.7322 -0.6811 +vn 0.0000 0.7322 0.6811 +usemtl None +s off +f 68/127/99 69/128/99 67/129/99 +f 70/130/100 73/131/100 69/132/100 +f 74/133/101 71/134/101 73/135/101 +f 72/136/102 67/137/102 71/138/102 +f 73/139/103 67/140/103 69/141/103 +f 70/142/104 72/143/104 74/144/104 +f 68/127/99 70/145/99 69/128/99 +f 70/130/100 74/146/100 73/131/100 +f 74/133/101 72/147/101 71/134/101 +f 72/136/102 68/148/102 67/137/102 +f 73/139/103 71/149/103 67/140/103 +f 70/142/104 68/150/104 72/143/104 +o Cube.004_Cube.005 +v 0.972214 -1.400771 -1.801529 +v 0.562293 0.078854 -0.519861 +v 1.017289 -1.266209 -1.942459 +v 0.607367 0.213416 -0.660790 +v 1.162708 -1.400771 -1.740603 +v 0.752787 0.078854 -0.458934 +v 1.207783 -1.266209 -1.881533 +v 0.797861 0.213416 -0.599864 +vt 0.627959 0.745670 +vt 0.747972 0.624560 +vt 0.747972 0.745670 +vt 0.378332 0.619716 +vt 0.498345 0.498606 +vt 0.498345 0.619716 +vt 0.128704 0.493762 +vt 0.248718 0.372652 +vt 0.248718 0.493762 +vt 0.627959 0.498606 +vt 0.747972 0.619716 +vt 0.627959 0.619716 +vt 0.627959 0.997578 +vt 0.747972 0.876468 +vt 0.747972 0.997578 +vt 0.872786 0.745670 +vt 0.752773 0.624560 +vt 0.872786 0.624560 +vt 0.627959 0.624560 +vt 0.378332 0.498606 +vt 0.128704 0.372652 +vt 0.747972 0.498606 +vt 0.627959 0.876468 +vt 0.752773 0.745670 +vn -0.9525 -0.0000 -0.3046 +vn 0.2254 0.6728 -0.7046 +vn 0.9525 0.0000 0.3046 +vn -0.2254 -0.6728 0.7046 +vn 0.2050 -0.7398 -0.6408 +vn -0.2050 0.7398 0.6408 +usemtl None +s off +f 76/151/105 77/152/105 75/153/105 +f 78/154/106 81/155/106 77/156/106 +f 82/157/107 79/158/107 81/159/107 +f 80/160/108 75/161/108 79/162/108 +f 81/163/109 75/164/109 77/165/109 +f 78/166/110 80/167/110 82/168/110 +f 76/151/105 78/169/105 77/152/105 +f 78/154/106 82/170/106 81/155/106 +f 82/157/107 80/171/107 79/158/107 +f 80/160/108 76/172/108 75/161/108 +f 81/163/109 79/173/109 75/164/109 +f 78/166/110 76/174/110 80/167/110 +o Cube.005_Cube.006 +v -1.379684 -1.375488 -1.855551 +v -0.867886 -0.051444 -0.446662 +v -1.431736 -1.225692 -1.977418 +v -0.919938 0.098353 -0.568529 +v -1.193482 -1.370006 -1.928344 +v -0.681684 -0.045962 -0.519455 +v -1.245534 -1.220210 -2.050211 +v -0.733736 0.103835 -0.641322 +vt 0.737478 0.123532 +vt 0.857491 0.002422 +vt 0.857491 0.123532 +vt 0.503146 0.367808 +vt 0.623159 0.246698 +vt 0.623159 0.367808 +vt 0.253518 0.493762 +vt 0.373531 0.372652 +vt 0.373531 0.493762 +vt 0.002400 0.678694 +vt 0.122413 0.799804 +vt 0.002400 0.799804 +vt 0.752773 0.997578 +vt 0.872786 0.876468 +vt 0.872786 0.997578 +vt 0.872786 0.871624 +vt 0.752773 0.750514 +vt 0.872786 0.750514 +vt 0.737478 0.002422 +vt 0.503146 0.246698 +vt 0.253518 0.372652 +vt 0.122413 0.678694 +vt 0.752773 0.876468 +vt 0.752773 0.871624 +vn -0.9310 -0.0274 0.3640 +vn -0.2603 0.7490 -0.6093 +vn 0.9310 0.0274 -0.3640 +vn 0.2603 -0.7490 0.6093 +vn -0.2559 -0.6620 -0.7044 +vn 0.2559 0.6620 0.7044 +usemtl None +s off +f 84/175/111 85/176/111 83/177/111 +f 86/178/112 89/179/112 85/180/112 +f 90/181/113 87/182/113 89/183/113 +f 88/184/114 83/185/114 87/186/114 +f 89/187/115 83/188/115 85/189/115 +f 86/190/116 88/191/116 90/192/116 +f 84/175/111 86/193/111 85/176/111 +f 86/178/112 90/194/112 89/179/112 +f 90/181/113 88/195/113 87/182/113 +f 88/184/114 84/196/114 83/185/114 +f 89/187/115 87/197/115 83/188/115 +f 86/190/116 84/198/116 88/191/116 +o Icosphere.001 +v 1.681861 -0.073431 0.007016 +v 2.018523 0.183753 0.251613 +v 1.553270 0.183753 0.402786 +v 1.262236 0.169665 0.007919 +v 1.553270 0.183753 -0.388753 +v 2.021341 0.185671 -0.242560 +v 1.810452 0.599896 0.402786 +v 1.345198 0.599896 0.251613 +v 1.345198 0.599896 -0.237581 +v 1.810452 0.599896 -0.388753 +v 2.097997 0.599894 0.007016 +v 1.681861 0.857081 0.007016 +v 1.606277 -0.003947 0.239642 +v 1.879744 -0.003947 0.150786 +v 1.804162 0.147222 0.383414 +v 2.076988 0.143651 0.007896 +v 1.879744 -0.003947 -0.136753 +v 1.437262 -0.003946 0.007016 +v 1.361676 0.147223 0.239643 +v 1.606277 -0.003947 -0.225609 +v 1.359481 0.143738 -0.232652 +v 1.804162 0.147222 -0.369381 +v 2.154710 0.402293 0.151401 +v 2.124346 0.391825 -0.136754 +v 1.681861 0.391825 0.472272 +v 1.959627 0.391822 0.389697 +v 1.209012 0.402295 0.151403 +v 1.404091 0.391847 0.389695 +v 1.408390 0.391825 -0.369383 +v 1.239375 0.391825 -0.136754 +v 1.955331 0.391825 -0.369383 +v 1.681861 0.391825 -0.458239 +v 2.002045 0.636427 0.239643 +v 1.559559 0.636427 0.383414 +v 1.286092 0.636427 0.007016 +v 1.559559 0.636427 -0.369381 +v 2.002045 0.636427 -0.225610 +v 1.757444 0.787597 0.239642 +v 1.926459 0.787595 0.007016 +v 1.483977 0.787597 0.150786 +v 1.483977 0.787597 -0.136753 +v 1.757444 0.787597 -0.225609 +vt 0.693978 0.227864 +vt 0.675849 0.201658 +vt 0.693801 0.205056 +vt 0.622482 0.226132 +vt 0.592407 0.218964 +vt 0.603685 0.213111 +vt 0.720039 0.231175 +vt 0.557833 0.139620 +vt 0.569961 0.166573 +vt 0.539018 0.148913 +vt 0.527292 0.128002 +vt 0.642965 0.214530 +vt 0.705314 0.176326 +vt 0.682512 0.171364 +vt 0.708437 0.146367 +vt 0.749014 0.222117 +vt 0.726702 0.200882 +vt 0.765343 0.190920 +vt 0.516005 0.169310 +vt 0.545194 0.183740 +vt 0.512992 0.202264 +vt 0.590996 0.185417 +vt 0.573928 0.160489 +vt 0.604035 0.156668 +vt 0.652909 0.219692 +vt 0.735775 0.164254 +vt 0.569961 0.198247 +vt 0.569057 0.228198 +vt 0.543935 0.219923 +vt 0.486621 0.178425 +vt 0.622642 0.189036 +vt 0.464625 0.206890 +vt 0.446106 0.233972 +vt 0.443814 0.210306 +vt 0.439696 0.151764 +vt 0.459308 0.177904 +vt 0.429161 0.177904 +vt 0.376901 0.157956 +vt 0.407745 0.149141 +vt 0.392252 0.181544 +vt 0.363020 0.216910 +vt 0.362676 0.187433 +vt 0.384093 0.216196 +vt 0.654138 0.185599 +vt 0.641121 0.156591 +vt 0.671020 0.160286 +vt 0.415960 0.233972 +vt 0.413337 0.206890 +vt 0.652414 0.127918 +vt 0.622483 0.130532 +vt 0.671020 0.195014 +vt 0.485181 0.214324 +vt 0.468689 0.189829 +vt 0.513555 0.230385 +vt 0.387745 0.135004 +vt 0.424416 0.128072 +vt 0.743721 0.136215 +vt 0.765343 0.159054 +vt 0.718879 0.128017 +vt 0.463873 0.216643 +vt 0.592408 0.128044 +vt 0.502706 0.145453 +vt 0.689799 0.142275 +vt 0.674773 0.170545 +vt 0.573928 0.195218 +vt 0.550988 0.132736 +vn 0.1024 -0.9435 0.3151 +vn 0.6960 -0.6629 0.2760 +vn -0.2680 -0.9435 0.1947 +vn -0.2680 -0.9435 -0.1947 +vn 0.1024 -0.9435 -0.3151 +vn 0.8674 -0.4139 0.2762 +vn 0.0247 -0.3304 0.9435 +vn -0.8561 -0.3987 0.3288 +vn -0.5551 -0.3149 -0.7699 +vn 0.5193 -0.3210 -0.7920 +vn 0.7634 -0.1930 0.6165 +vn -0.2821 -0.1425 0.9488 +vn -0.9816 -0.1645 -0.0975 +vn -0.3066 -0.1256 -0.9435 +vn 0.8048 -0.1020 -0.5847 +vn 0.4089 0.6617 0.6284 +vn -0.4713 0.6617 0.5831 +vn -0.7002 0.6617 -0.2680 +vn 0.0385 0.6617 -0.7488 +vn 0.7240 0.6617 -0.1947 +vn 0.2680 0.9435 -0.1947 +vn 0.4911 0.7947 -0.3568 +vn 0.4089 0.6617 -0.6284 +vn -0.1024 0.9435 -0.3151 +vn -0.1876 0.7947 -0.5773 +vn -0.4713 0.6617 -0.5831 +vn -0.3313 0.9435 0.0000 +vn -0.6071 0.7947 0.0000 +vn -0.7002 0.6617 0.2680 +vn -0.1024 0.9435 0.3151 +vn -0.1876 0.7947 0.5773 +vn 0.0385 0.6617 0.7488 +vn 0.2680 0.9435 0.1947 +vn 0.4911 0.7947 0.3568 +vn 0.7240 0.6617 0.1947 +vn 0.8897 0.3304 -0.3151 +vn 0.7947 0.1876 -0.5773 +vn 0.5746 0.3304 -0.7488 +vn -0.0247 0.3304 -0.9435 +vn -0.3035 0.1876 -0.9342 +vn -0.5346 0.3304 -0.7779 +vn -0.9050 0.3304 -0.2680 +vn -0.9623 0.2487 -0.1104 +vn -0.8512 0.4484 0.2727 +vn -0.5326 0.3582 0.7669 +vn -0.2791 0.2015 0.9389 +vn -0.0247 0.3304 0.9435 +vn 0.5648 0.3581 0.7435 +vn 0.7435 0.2512 0.6197 +vn 0.8490 0.4481 0.2798 +vn 0.3066 0.1256 -0.9435 +vn 0.3035 -0.1876 -0.9342 +vn 0.0247 -0.3304 -0.9435 +vn -0.8026 0.1256 -0.5831 +vn -0.7983 -0.1623 -0.5800 +vn -0.8937 -0.3058 -0.3283 +vn -0.7535 0.2020 0.6257 +vn -0.7559 -0.2420 0.6082 +vn -0.5648 -0.3581 0.7435 +vn 0.2820 0.1425 0.9488 +vn 0.2791 -0.2015 0.9389 +vn 0.5326 -0.3582 0.7669 +vn 0.9738 0.1992 -0.1098 +vn 0.9666 -0.2388 -0.0932 +vn 0.9108 -0.3234 -0.2566 +vn 0.4595 -0.6691 -0.5841 +vn 0.1876 -0.7947 -0.5773 +vn -0.0385 -0.6617 -0.7488 +vn -0.3800 -0.6656 -0.6423 +vn -0.4747 -0.8097 -0.3449 +vn -0.6899 -0.6945 -0.2040 +vn -0.6852 -0.6920 0.2270 +vn -0.4911 -0.7947 0.3568 +vn -0.4089 -0.6617 0.6284 +vn 0.6951 -0.6677 -0.2665 +vn 0.5991 -0.8007 0.0000 +vn 0.3313 -0.9435 0.0000 +vn -0.0385 -0.6617 0.7488 +vn 0.1876 -0.7947 0.5773 +vn 0.4713 -0.6617 0.5831 +usemtl None +s off +f 91/199/117 104/200/117 103/201/117 +f 92/202/118 104/203/118 106/204/118 +f 91/199/119 103/201/119 108/205/119 +f 91/206/120 108/207/120 110/208/120 +f 91/206/121 110/208/121 107/209/121 +f 92/202/122 106/204/122 113/210/122 +f 93/211/123 105/212/123 115/213/123 +f 94/214/124 109/215/124 117/216/124 +f 95/217/125 111/218/125 119/219/125 +f 96/220/126 112/221/126 121/222/126 +f 92/202/127 113/210/127 116/223/127 +f 93/211/128 115/213/128 118/224/128 +f 94/225/129 117/226/129 120/227/129 +f 95/217/130 119/219/130 122/228/130 +f 96/220/131 121/222/131 114/229/131 +f 97/230/132 123/231/132 128/232/132 +f 98/233/133 124/234/133 130/235/133 +f 99/236/134 125/237/134 131/238/134 +f 100/239/135 126/240/135 132/241/135 +f 101/242/136 127/243/136 129/244/136 +f 129/245/137 132/241/137 102/246/137 +f 129/244/138 127/243/138 132/247/138 +f 127/243/139 100/248/139 132/247/139 +f 132/241/140 131/238/140 102/246/140 +f 132/241/141 126/240/141 131/238/141 +f 126/240/142 99/236/142 131/238/142 +f 131/238/143 130/235/143 102/246/143 +f 131/238/144 125/237/144 130/235/144 +f 125/237/145 98/233/145 130/235/145 +f 130/235/146 128/232/146 102/246/146 +f 130/235/147 124/234/147 128/232/147 +f 124/234/148 97/230/148 128/232/148 +f 128/232/149 129/245/149 102/246/149 +f 128/232/150 123/231/150 129/245/150 +f 123/249/151 101/242/151 129/244/151 +f 114/229/152 127/243/152 101/242/152 +f 114/229/153 121/222/153 127/243/153 +f 121/222/154 100/248/154 127/243/154 +f 122/228/155 126/250/155 100/251/155 +f 122/228/156 119/219/156 126/250/156 +f 119/219/157 99/252/157 126/250/157 +f 120/253/158 125/237/158 99/236/158 +f 120/253/159 117/254/159 125/237/159 +f 117/254/160 98/233/160 125/237/160 +f 118/224/161 124/255/161 98/256/161 +f 118/224/162 115/213/162 124/255/162 +f 115/213/163 97/257/163 124/255/163 +f 116/258/164 123/231/164 97/230/164 +f 116/223/165 113/210/165 123/249/165 +f 113/210/166 101/242/166 123/249/166 +f 121/222/167 122/259/167 100/248/167 +f 121/222/168 112/221/168 122/259/168 +f 112/260/169 95/217/169 122/228/169 +f 119/219/170 120/227/170 99/252/170 +f 119/219/171 111/218/171 120/227/171 +f 111/218/172 94/225/172 120/227/172 +f 117/216/173 118/224/173 98/256/173 +f 117/216/174 109/215/174 118/224/174 +f 109/215/175 93/211/175 118/224/175 +f 115/213/176 116/261/176 97/257/176 +f 115/213/177 105/212/177 116/261/177 +f 105/212/178 92/262/178 116/261/178 +f 113/210/179 114/229/179 101/242/179 +f 113/210/180 106/204/180 114/229/180 +f 106/204/181 96/220/181 114/229/181 +f 107/263/182 112/221/182 96/220/182 +f 107/209/183 110/208/183 112/260/183 +f 110/208/184 95/217/184 112/260/184 +f 110/208/185 111/218/185 95/217/185 +f 110/208/186 108/207/186 111/218/186 +f 108/207/187 94/225/187 111/218/187 +f 108/205/188 109/215/188 94/214/188 +f 108/205/189 103/201/189 109/215/189 +f 103/201/190 93/211/190 109/215/190 +f 106/204/191 107/263/191 96/220/191 +f 106/204/192 104/203/192 107/263/192 +f 104/264/193 91/206/193 107/209/193 +f 103/201/194 105/212/194 93/211/194 +f 103/201/195 104/200/195 105/212/195 +f 104/200/196 92/262/196 105/212/196 +o Icosphere.002 +v 2.061433 0.129717 0.008815 +v 2.101074 0.193691 0.229830 +v 2.046248 0.318514 0.343377 +v 2.012501 0.159699 0.008035 +v 2.048383 0.278037 -0.304942 +v 2.107427 0.202289 -0.200188 +v 2.076554 0.348463 0.304546 +v 2.021729 0.235014 0.203426 +v 2.031073 0.250915 -0.183429 +v 2.082651 0.321295 -0.287613 +v 2.110945 0.207573 0.006726 +v 2.062279 0.236789 0.006235 +v 2.052494 0.167456 0.229826 +v 2.084720 0.132124 0.138731 +v 2.075813 0.298522 0.335746 +v 2.108157 0.155444 0.008103 +v 2.088790 0.153046 -0.114136 +v 2.032630 0.137843 0.008607 +v 2.023670 0.182780 0.220741 +v 2.057901 0.177984 -0.191387 +v 2.029993 0.195734 -0.186342 +v 2.078370 0.262972 -0.297353 +v 2.113544 0.176802 0.125353 +v 2.119181 0.196420 -0.112008 +v 2.061401 0.387560 0.355702 +v 2.093627 0.316833 0.313486 +v 2.009259 0.176802 0.125353 +v 2.029175 0.316833 0.313486 +v 2.034408 0.291089 -0.280426 +v 2.014960 0.198102 -0.108729 +v 2.098404 0.288674 -0.287682 +v 2.061401 0.331031 -0.340272 +v 2.099132 0.232366 0.191339 +v 2.046989 0.335141 0.291223 +v 2.015315 0.211791 0.006791 +v 2.054500 0.316694 -0.270512 +v 2.108389 0.248055 -0.178989 +v 2.070308 0.247688 0.182252 +v 2.090990 0.228915 0.006308 +v 2.038082 0.221480 0.111974 +v 2.045741 0.241129 -0.104766 +v 2.080728 0.265537 -0.172776 +vt 0.059205 0.292672 +vt 0.037326 0.284709 +vt 0.078470 0.249135 +vt 0.029325 0.213800 +vt 0.011982 0.258159 +vt 0.091252 0.287793 +vt 0.293632 0.004264 +vt 0.326267 0.005769 +vt 0.306814 0.053480 +vt 0.268762 0.035110 +vt 0.011446 0.232203 +vt 0.106350 0.083357 +vt 0.071844 0.101205 +vt 0.098787 0.005739 +vt 0.115629 0.266741 +vt 0.111225 0.236552 +vt 0.123875 0.246619 +vt 0.340055 0.157582 +vt 0.340912 0.065594 +vt 0.358410 0.168313 +vt 0.259947 0.091929 +vt 0.304551 0.148983 +vt 0.289630 0.181700 +vt 0.054390 0.079129 +vt 0.123875 0.088038 +vt 0.929680 0.900811 +vt 0.906877 0.891008 +vt 0.938476 0.855895 +vt 0.338739 0.217465 +vt 0.247052 0.089341 +vt 0.201080 0.048365 +vt 0.226381 0.177597 +vt 0.195031 0.160811 +vt 0.141691 0.174542 +vt 0.168847 0.063198 +vt 0.160386 0.190485 +vt 0.102511 0.167786 +vt 0.118976 0.209545 +vt 0.087267 0.175152 +vt 0.053214 0.085576 +vt 0.083383 0.094208 +vt 0.051018 0.144797 +vt 0.015359 0.200928 +vt 0.018905 0.160145 +vt 0.039488 0.180411 +vt 0.071407 0.175791 +vt 0.768449 0.195386 +vt 0.790288 0.161386 +vt 0.809249 0.171081 +vt 0.833163 0.152574 +vt 0.820913 0.127315 +vt 0.136477 0.202346 +vt 0.832928 0.195386 +vt 0.219246 0.183477 +vt 0.187903 0.174799 +vt 0.240964 0.206996 +vt 0.002429 0.213892 +vt 0.270364 0.141519 +vt 0.032215 0.118986 +vt 0.076134 0.078750 +vt 0.843292 0.127012 +vt 0.843292 0.179902 +vt 0.834631 0.146345 +vt 0.101670 0.125079 +vt 0.916003 0.848077 +vt 0.933569 0.793864 +vt 0.876427 0.832732 +vt 0.149094 0.083126 +vt 0.184184 0.004698 +vt 0.219526 0.083126 +vt 0.242545 0.239579 +vt 0.035308 0.172505 +vt 0.314370 0.212793 +vt 0.938476 0.747880 +vt 0.358410 0.065046 +vt 0.353024 0.024196 +vt 0.128583 0.239579 +vt 0.955239 0.902055 +vt 0.940021 0.886869 +vt 0.955239 0.747904 +vt 0.250191 0.043609 +vt 0.270982 0.013823 +vn -0.5657 -0.8164 0.1165 +vn 0.9177 -0.3855 0.0958 +vn -0.2694 -0.9510 0.1515 +vn -0.2633 -0.9389 -0.2217 +vn -0.2037 -0.9526 -0.2261 +vn 0.1213 -0.9775 0.1725 +vn 0.1164 -0.1992 0.9730 +vn -0.4187 -0.9002 0.1197 +vn -0.8886 -0.3007 -0.3464 +vn 0.7677 -0.4146 -0.4885 +vn 0.9922 -0.0230 0.1221 +vn -0.8662 0.1028 0.4890 +vn -0.9996 -0.0139 -0.0256 +vn -0.8848 -0.0878 -0.4577 +vn 0.9904 -0.0324 -0.1342 +vn 0.5197 0.6461 -0.5590 +vn -0.8830 0.4151 -0.2193 +vn -0.2847 0.9433 0.1704 +vn 0.1186 0.8924 0.4353 +vn 0.7221 0.6779 0.1378 +vn 0.2598 0.9489 0.1792 +vn 0.5510 0.8232 0.1368 +vn 0.5542 0.7451 0.3710 +vn -0.4321 0.8963 0.0994 +vn 0.1871 0.8901 0.4157 +vn -0.7912 0.5712 0.2186 +vn -0.2686 0.9601 0.0776 +vn -0.5866 0.8081 0.0525 +vn 0.1389 0.9829 -0.1206 +vn -0.6015 0.7986 -0.0220 +vn 0.4695 0.7349 -0.4893 +vn -0.0626 0.7718 -0.6328 +vn 0.2640 0.9619 -0.0716 +vn 0.4782 0.8774 -0.0374 +vn 0.7302 0.6818 -0.0448 +vn 0.9654 0.2573 0.0428 +vn 0.9848 0.1717 -0.0263 +vn 0.8977 0.4334 0.0795 +vn -0.0402 0.9795 0.1973 +vn -0.7944 0.6056 0.0459 +vn -0.8069 0.5550 0.2021 +vn -0.9663 0.2561 -0.0274 +vn -0.9915 0.1298 -0.0123 +vn -0.9801 0.1985 0.0085 +vn -0.8375 0.4642 -0.2884 +vn -0.8239 0.5157 -0.2351 +vn -0.0732 0.7818 -0.6192 +vn 0.8808 0.4071 -0.2418 +vn 0.5882 0.6773 -0.4419 +vn 0.9765 0.2131 0.0339 +vn 0.8663 0.4189 -0.2721 +vn 0.6742 -0.2663 -0.6889 +vn -0.0610 -0.5432 -0.8374 +vn -0.8355 0.5173 0.1855 +vn -0.9733 -0.1368 -0.1843 +vn -0.8899 -0.4274 -0.1593 +vn -0.0192 0.8030 -0.5957 +vn -0.9860 -0.0654 0.1531 +vn -0.8203 -0.3022 0.4855 +vn 0.8866 0.4546 -0.0848 +vn 0.7854 -0.0116 0.6189 +vn 0.8472 -0.2626 0.4619 +vn 0.9673 0.2499 0.0436 +vn 0.9671 -0.2545 0.0019 +vn 0.7654 -0.6273 -0.1438 +vn -0.0869 -0.8564 -0.5089 +vn 0.5616 -0.6950 -0.4489 +vn -0.2118 -0.7420 -0.6360 +vn -0.5190 -0.6624 -0.5402 +vn -0.5515 -0.8016 -0.2306 +vn -0.7199 -0.6679 -0.1886 +vn -0.7325 -0.6716 0.1113 +vn -0.5020 -0.8501 0.1589 +vn -0.4990 -0.5338 0.6827 +vn 0.8384 -0.5312 -0.1224 +vn 0.5049 -0.8609 -0.0631 +vn 0.4592 -0.8859 -0.0659 +vn -0.2005 -0.5939 0.7791 +vn 0.7381 -0.4986 0.4545 +vn -0.5039 -0.6710 0.5439 +usemtl None +s off +f 133/265/197 146/266/197 145/267/197 +f 134/268/198 146/266/198 148/269/198 +f 133/265/199 145/267/199 150/270/199 +f 133/271/200 150/272/200 152/273/200 +f 133/271/201 152/273/201 149/274/201 +f 134/268/202 148/269/202 155/275/202 +f 135/276/203 147/277/203 157/278/203 +f 136/279/204 151/280/204 159/281/204 +f 137/282/205 153/283/205 161/284/205 +f 138/285/206 154/286/206 163/287/206 +f 134/268/207 155/275/207 158/288/207 +f 135/276/208 157/278/208 160/289/208 +f 136/290/209 159/291/209 162/292/209 +f 137/282/210 161/284/210 164/293/210 +f 138/285/211 163/287/211 156/294/211 +f 139/295/212 165/296/212 170/297/212 +f 140/298/213 166/299/213 172/300/213 +f 141/301/214 167/302/214 173/303/214 +f 142/304/215 168/305/215 174/306/215 +f 143/307/216 169/308/216 171/309/216 +f 171/309/217 174/306/217 144/310/217 +f 171/309/218 169/308/218 174/306/218 +f 169/308/219 142/304/219 174/306/219 +f 174/311/220 173/312/220 144/313/220 +f 174/306/221 168/305/221 173/303/221 +f 168/305/222 141/301/222 173/303/222 +f 173/312/223 172/314/223 144/313/223 +f 173/312/224 167/315/224 172/314/224 +f 167/316/225 140/298/225 172/300/225 +f 172/314/226 170/317/226 144/313/226 +f 172/300/227 166/299/227 170/297/227 +f 166/299/228 139/295/228 170/297/228 +f 170/297/229 171/318/229 144/319/229 +f 170/297/230 165/296/230 171/318/230 +f 165/296/231 143/320/231 171/318/231 +f 156/321/232 169/308/232 143/307/232 +f 156/294/233 163/287/233 169/322/233 +f 163/323/234 142/304/234 169/308/234 +f 164/324/235 168/305/235 142/304/235 +f 164/325/236 161/326/236 168/327/236 +f 161/328/237 141/301/237 168/305/237 +f 162/292/238 167/329/238 141/330/238 +f 162/292/239 159/291/239 167/329/239 +f 159/291/240 140/331/240 167/329/240 +f 160/332/241 166/299/241 140/298/241 +f 160/332/242 157/333/242 166/299/242 +f 157/333/243 139/295/243 166/299/243 +f 158/334/244 165/296/244 139/295/244 +f 158/334/245 155/335/245 165/296/245 +f 155/275/246 143/307/246 165/336/246 +f 163/287/247 164/293/247 142/337/247 +f 163/287/248 154/286/248 164/293/248 +f 154/286/249 137/282/249 164/293/249 +f 161/338/250 162/292/250 141/330/250 +f 161/284/251 153/283/251 162/339/251 +f 153/283/252 136/340/252 162/339/252 +f 159/341/253 160/332/253 140/298/253 +f 159/342/254 151/343/254 160/344/254 +f 151/280/255 135/276/255 160/289/255 +f 157/333/256 158/334/256 139/295/256 +f 157/278/257 147/277/257 158/288/257 +f 147/277/258 134/268/258 158/288/258 +f 155/275/259 156/321/259 143/307/259 +f 155/275/260 148/269/260 156/321/260 +f 148/345/261 138/285/261 156/294/261 +f 149/274/262 154/286/262 138/285/262 +f 149/274/263 152/273/263 154/286/263 +f 152/273/264 137/282/264 154/286/264 +f 152/273/265 153/283/265 137/282/265 +f 152/273/266 150/272/266 153/283/266 +f 150/272/267 136/340/267 153/283/267 +f 150/270/268 151/280/268 136/279/268 +f 150/270/269 145/267/269 151/280/269 +f 145/267/270 135/276/270 151/280/270 +f 148/345/271 149/274/271 138/285/271 +f 148/345/272 146/346/272 149/274/272 +f 146/346/273 133/271/273 149/274/273 +f 145/267/274 147/277/274 135/276/274 +f 145/267/275 146/266/275 147/277/275 +f 146/266/276 134/268/276 147/277/276 +o Cube.006_Cube.007 +v 1.937780 0.546872 0.283218 +v 1.937780 0.676160 0.283218 +v 1.937780 0.546872 0.153931 +v 1.937780 0.676160 0.153931 +v 2.067068 0.546872 0.283218 +v 2.067068 0.676160 0.283218 +v 2.067068 0.546872 0.153931 +v 2.067068 0.676160 0.153931 +vt 0.253518 0.871624 +vt 0.373531 0.750514 +vt 0.373531 0.871624 +vt 0.503146 0.493762 +vt 0.623159 0.372652 +vt 0.623159 0.493762 +vt 0.378332 0.367808 +vt 0.498345 0.246698 +vt 0.498345 0.367808 +vt 0.128704 0.750514 +vt 0.248718 0.871624 +vt 0.128704 0.871624 +vt 0.877587 0.367808 +vt 0.997600 0.246698 +vt 0.997600 0.367808 +vt 0.122413 0.925758 +vt 0.002400 0.804648 +vt 0.122413 0.804648 +vt 0.253518 0.750514 +vt 0.503146 0.372652 +vt 0.378332 0.246698 +vt 0.248718 0.750514 +vt 0.877587 0.246698 +vt 0.002400 0.925758 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +usemtl None +s off +f 176/347/277 177/348/277 175/349/277 +f 178/350/278 181/351/278 177/352/278 +f 182/353/279 179/354/279 181/355/279 +f 180/356/280 175/357/280 179/358/280 +f 181/359/281 175/360/281 177/361/281 +f 178/362/282 180/363/282 182/364/282 +f 176/347/277 178/365/277 177/348/277 +f 178/350/278 182/366/278 181/351/278 +f 182/353/279 180/367/279 179/354/279 +f 180/356/280 176/368/280 175/357/280 +f 181/359/281 179/369/281 175/360/281 +f 178/362/282 176/370/282 180/363/282 +o Cube.007_Cube.008 +v 1.937780 0.546872 -0.137109 +v 1.937780 0.676160 -0.137109 +v 1.937780 0.546872 -0.266397 +v 1.937780 0.676160 -0.266397 +v 2.067068 0.546872 -0.137109 +v 2.067068 0.676160 -0.137109 +v 2.067068 0.546872 -0.266397 +v 2.067068 0.676160 -0.266397 +vt 0.503146 0.871624 +vt 0.623159 0.750514 +vt 0.623159 0.871624 +vt 0.503146 0.619716 +vt 0.623159 0.498606 +vt 0.623159 0.619716 +vt 0.378332 0.493762 +vt 0.498345 0.372652 +vt 0.498345 0.493762 +vt 0.378332 0.750514 +vt 0.498345 0.871624 +vt 0.378332 0.871624 +vt 0.877587 0.493762 +vt 0.997600 0.372652 +vt 0.997600 0.493762 +vt 0.982305 0.123532 +vt 0.862292 0.002422 +vt 0.982305 0.002422 +vt 0.503146 0.750514 +vt 0.503146 0.498606 +vt 0.378332 0.372652 +vt 0.498345 0.750514 +vt 0.877587 0.372652 +vt 0.862292 0.123532 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 1.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +usemtl None +s off +f 184/371/283 185/372/283 183/373/283 +f 186/374/284 189/375/284 185/376/284 +f 190/377/285 187/378/285 189/379/285 +f 188/380/286 183/381/286 187/382/286 +f 189/383/287 183/384/287 185/385/287 +f 186/386/288 188/387/288 190/388/288 +f 184/371/283 186/389/283 185/372/283 +f 186/374/284 190/390/284 189/375/284 +f 190/377/285 188/391/285 187/378/285 +f 188/380/286 184/392/286 183/381/286 +f 189/383/287 187/393/287 183/384/287 +f 186/386/288 184/394/288 188/387/288 diff --git a/Step 2/Submission/src/globals.cpp b/Step 2/Submission/src/globals.cpp new file mode 100644 index 0000000..34b283f --- /dev/null +++ b/Step 2/Submission/src/globals.cpp @@ -0,0 +1,10 @@ +/* + * Created by Brett Terpstra 6920201 on 17/10/22. + * Copyright (c) 2022 Brett Terpstra. All Rights Reserved. + */ +// Yes, globals are bad. +#include + +namespace Raytracing { + std::unordered_map profiles; +} \ No newline at end of file diff --git a/Step 2/Submission/src/image/image.cpp b/Step 2/Submission/src/image/image.cpp new file mode 100644 index 0000000..aec3518 --- /dev/null +++ b/Step 2/Submission/src/image/image.cpp @@ -0,0 +1,92 @@ +/* + * Created by Brett Terpstra 6920201 on 14/10/22. + * Copyright (c) Brett Terpstra 2022 All Rights Reserved + */ +#include "image/image.h" +#include +#include +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include + +namespace Raytracing { + + Image::Image(int width, int height) : width(width), height(height) { + pixelData = new Vec4[width * height]; + } + + Image::Image(const Image& image) : width(image.width), height(image.height) { + pixelData = new Vec4[image.width * image.height]; + for (int i = 0; i < image.width; i++) { + for (int j = 0; j < image.height; j++) { + this->setPixelColor(i, j, image.pixelData[i * image.height + j]); + } + } + } + + int Image::getPixelR(int x, int y) const { + // values are stored as a floating point number [0, 1) + // but most formats want an int [0, 255] + return int(255.0 * getPixelColor(x, y).r()); + } + + int Image::getPixelG(int x, int y) const { + return int(255.0 * getPixelColor(x, y).g()); + } + + int Image::getPixelB(int x, int y) const { + return int(255.0 * getPixelColor(x, y).b()); + } + + int Image::getPixelA(int x, int y) const { + return int(255.0 * getPixelColor(x, y).a()); + } + + Image::~Image() { + delete[](pixelData); + } + + void ImageOutput::write(const std::string& file, const std::string& formatExtension) { + auto lowerExtension = Raytracing::String::toLowerCase(formatExtension); + auto fullFile = file + "." + lowerExtension; + + if (!lowerExtension.ends_with("hdr")) { + // unfortunately we do have to put the data into a format that STB can read + unsigned char data[image.getWidth() * image.getHeight() * 3]; + int pixelIndex = 0; + for (int j = image.getHeight()-1; j >= 0; j--) { + for (int i = 0; i < image.getWidth(); i++) { + data[pixelIndex++] = image.getPixelR(i, j); + data[pixelIndex++] = image.getPixelG(i, j); + data[pixelIndex++] = image.getPixelB(i, j); + } + } + + // Writing a PPM was giving me issues, so I switched to using STB Image Write + // It's a single threaded, public domain header only image writing library + // I didn't want to use an external lib for this, however since it is public domain + // I've simply included it in the include directory. + if (lowerExtension.ends_with("bmp")) { + stbi_write_bmp(fullFile.c_str(), image.getWidth(), image.getHeight(), 3, data); + } else if (lowerExtension.ends_with("png")) { + // stride here isn't clearly defined in the docs for some reason, + // but it's just the image's width times the number of channels + stbi_write_png(fullFile.c_str(), image.getWidth(), image.getHeight(), 3, data, image.getWidth() * 3); + } else if (lowerExtension.ends_with("jpg") || lowerExtension.ends_with("jpeg")) { + stbi_write_jpg(fullFile.c_str(), image.getWidth(), image.getHeight(), 3, data, 90); + } else + throw std::runtime_error("Invalid format! Please use bmp, png, or jpg"); + } else { + // the TODO: here is to check if HDR is in [0,1] or if we need to transform the value. + float data[image.getWidth() * image.getHeight() * 3]; + int pixelIndex = 0; + for (int i = 0; i < image.getWidth(); i++) { + for (int j = 0; j < image.getHeight(); j++) { + data[pixelIndex++] = (float) image.getPixelColor(i, j).r(); + data[pixelIndex++] = (float) image.getPixelColor(i, j).g(); + data[pixelIndex++] = (float) image.getPixelColor(i, j).b(); + } + } + stbi_write_hdr(fullFile.c_str(), image.getWidth(), image.getHeight(), 3, data); + } + } +} \ No newline at end of file diff --git a/Step 2/Submission/src/main.cpp b/Step 2/Submission/src/main.cpp new file mode 100644 index 0000000..4352e38 --- /dev/null +++ b/Step 2/Submission/src/main.cpp @@ -0,0 +1,117 @@ +#include "util/std.h" +#include "util/parser.h" +#include "image/image.h" +#include +#include +#include + +/** + * Brett Terpstra 6920201 + * + */ + +int main(int argc, char** args) { + // not a feature full parser but it'll work for what I need. + Raytracing::Parser parser; + + parser.addOption("--single", "Enable Single Thread\n\tUse a single thread for ray tracing\n", "true"); + // not implemented yet + parser.addOption("--multi", "Enable Multi-threading\n" + "\tUse multiple threads for ray tracing,\n" + "\tYou can set the max threads using -t or --threads\n"); + parser.addOption({{"-t"}, {"--threads"}}, "Max Usable Threads\n" + "\tSet the max threads the ray tracer will attempt to use.\n" + "\tDefaults to all cores of your cpu.\n", "0"); + // not implemented yet + parser.addOption({{"--gui"}, {"-g"}}, "Enable GLFW GUI\n" + "\tWill create a GUI using GLFW and display the image there.\n" + "\tRequires the you compile with GLFW enabled. Will do nothing otherwise\n"); + // not implemented yet + parser.addOption({{"--gpu"}, {"-c"}}, "Enables GPU Compute\n" + "\tRequires the --gui/-g flag enabled,\n" + "\tWill use OpenGL compute shaders to render the image\n"); + parser.addOption("--output", "Output Directory\n" + "\tSet the output directory for the rendered image. Defaults to the local directory.\n", "./"); + parser.addOption("--format", "Output Format\n" + "\tSets the output format to BMP, PNG, or JPEG. \n", "PNG"); + parser.addOption("-w", "Image Width\n" + "\tSets the width of the output image.\n", "910"); + parser.addOption("-h", "Image Height\n" + "\tSets the height of the output image.\n", "512"); + parser.addOption("--fov", "Camera FOV\n" + "\tSets the FOV used to render the camera.\n", "90"); + parser.addOption("--resources", "Resources Directory\n" + "\tSets the directory where the resources are stored.\n" + "\tThis can be relative.Must have trailing '/' \n", "../resources/"); + // disabled because don't currently have a way to parse vectors. TODO + //parser.addOption("--position", "Camera Position\n\tSets the position used to render the scene with the camera.\n", "{0, 0, 0}"); + + // if the parser returns non-zero then it wants us to stop execution + // likely due to a help function being called. + if (parser.parse(args, argc)) + return 0; + + tlog << "Parsing complete! Starting raytracer with options:" << std::endl; + // not perfect (contains duplicates) but good enough. + parser.printAllInInfo(); + + //Raytracing::Image image(445, 256); + Raytracing::Image image(std::stoi(parser.getOptionValue("-w")), std::stoi(parser.getOptionValue("-h"))); + + Raytracing::Camera camera(std::stoi(parser.getOptionValue("--fov")), image); + //camera.setPosition({0, 0, 1}); + camera.lookAt({6,5,6}, {0, 0, 0}, {0, 1, 0}); + + Raytracing::World world; + + Raytracing::OBJLoader loader; + // assumes you are running it from a subdirectory, "build" or "cmake-build-release", etc. + Raytracing::ModelData spider = loader.loadModel(parser.getOptionValue("--resources") + "spider.obj"); + Raytracing::ModelData house = loader.loadModel(parser.getOptionValue("--resources") + "house.obj"); + Raytracing::ModelData plane = loader.loadModel(parser.getOptionValue("--resources") + "plane.obj"); + + world.addMaterial("greenDiffuse", new Raytracing::DiffuseMaterial{Raytracing::Vec4{0, 1.0, 0, 1}}); + world.addMaterial("redDiffuse", new Raytracing::DiffuseMaterial{Raytracing::Vec4{1.0, 0, 0, 1}}); + world.addMaterial("blueDiffuse", new Raytracing::DiffuseMaterial{Raytracing::Vec4{0, 0, 1.0, 1}}); + + world.addMaterial("greenMetal", new Raytracing::MetalMaterial{Raytracing::Vec4{0.4, 1.0, 0.4, 1}}); + world.addMaterial("redMetal", new Raytracing::BrushedMetalMaterial{Raytracing::Vec4{1.0, 0.4, 0.4, 1}, 0.6f}); + world.addMaterial("blueMetal", new Raytracing::MetalMaterial{Raytracing::Vec4{0.4, 0.4, 1.0, 1}}); + + //world.add(new Raytracing::SphereObject({0,0,-1,0{}, 0.5, world.getMaterial("redDiffuse"))); + //world.add(new Raytracing::SphereObject({-1,0,-1,0}, 0.5, world.getMaterial("blueMetal"))); + //world.add(new Raytracing::SphereObject({1,0,-1,0}, 0.5, world.getMaterial("redMetal"))); + world.add(new Raytracing::SphereObject({0,-100.5,-1,0}, 100, world.getMaterial("greenDiffuse"))); + //world.add(new Raytracing::TriangleObject({0,0.1,-0.5f,0}, {{-0.5, -0.5, 0.0}, {0.5, -0.5, 0.0}, {0.0, 0.5, 0}}, world.getMaterial("greenDiffuse"))); + world.add(new Raytracing::ModelObject({0, 1, 0}, spider, world.getMaterial("redDiffuse"))); + world.add(new Raytracing::ModelObject({-5, 0.5, 0}, plane, world.getMaterial("greenMetal"))); + world.add(new Raytracing::ModelObject({5, 1, 0}, house, world.getMaterial("redDiffuse"))); + world.add(new Raytracing::ModelObject({0, 0, -5}, house, world.getMaterial("blueDiffuse"))); + world.add(new Raytracing::ModelObject({0, 0, 5}, house, world.getMaterial("blueDiffuse"))); + + Raytracing::Raycaster raycaster {camera, image, world, parser}; + + ilog << "Running raycaster!\n"; + raycaster.run(); + + Raytracing::ImageOutput imageOutput(image); + + auto t = std::time(nullptr); + auto now = std::localtime(&t); + std::stringstream timeString; + timeString << (1900 + now->tm_year); + timeString << "-"; + timeString << (1 + now->tm_mon); + timeString << "-"; + timeString << now->tm_mday; + timeString << " "; + timeString << now->tm_hour; + timeString << ":"; + timeString << now->tm_min; + timeString << ":"; + timeString << now->tm_sec; + ilog << "Writing Image!\n"; + imageOutput.write(parser.getOptionValue("--output") + timeString.str(), parser.getOptionValue("--format")); + + return 0; +} diff --git a/Step 2/Submission/src/math/colliders.cpp b/Step 2/Submission/src/math/colliders.cpp new file mode 100644 index 0000000..955d8bc --- /dev/null +++ b/Step 2/Submission/src/math/colliders.cpp @@ -0,0 +1,108 @@ +/* + * Created by Brett Terpstra 6920201 on 17/10/22. + * Copyright (c) 2022 Brett Terpstra. All Rights Reserved. + */ +#include + +namespace Raytracing { + + PRECISION_TYPE AABB::longestDistanceFromCenter() const { + Vec4 center = getCenter(); + PRECISION_TYPE maxX = std::abs(max.x() - center.x()); + PRECISION_TYPE minX = std::abs(min.x() - center.x()); + PRECISION_TYPE maxY = std::abs(max.y() - center.y()); + PRECISION_TYPE minY = std::abs(min.y() - center.y()); + PRECISION_TYPE maxZ = std::abs(max.z() - center.z()); + PRECISION_TYPE minZ = std::abs(min.z() - center.z()); + return std::max(maxX, std::max(minX, std::max(maxY, std::max(minY, std::max(maxZ, minZ))))); + } + + PRECISION_TYPE AABB::avgDistanceFromCenter() const { + Vec4 center = getCenter(); + PRECISION_TYPE maxX = std::abs(max.x() - center.x()); + PRECISION_TYPE minX = std::abs(min.x() - center.x()); + PRECISION_TYPE maxY = std::abs(max.y() - center.y()); + PRECISION_TYPE minY = std::abs(min.y() - center.y()); + PRECISION_TYPE maxZ = std::abs(max.z() - center.z()); + PRECISION_TYPE minZ = std::abs(min.z() - center.z()); + maxX *= maxX; + minX *= minX; + maxY *= maxY; + minY *= minY; + maxZ *= maxZ; + minZ *= minZ; + return std::sqrt(maxX + minX + maxY + minY + maxZ + minZ); + } + + int AABB::longestAxis() const { + PRECISION_TYPE X = std::abs(max.x() - min.x()); + PRECISION_TYPE Y = std::abs(max.y() - min.y()); + PRECISION_TYPE Z = std::abs(max.z() - min.z()); + return X > Y && X > Z ? 0 : Y > Z ? 1 : 2; + } + + PRECISION_TYPE AABB::longestAxisLength() const { + PRECISION_TYPE X = std::abs(max.x() - min.x()); + PRECISION_TYPE Y = std::abs(max.y() - min.y()); + PRECISION_TYPE Z = std::abs(max.z() - min.z()); + return X > Y && X > Z ? X : Y > Z ? Y : Z; + } + + std::pair AABB::splitByLongestAxis() { + PRECISION_TYPE X = std::abs(max.x() - min.x()); + PRECISION_TYPE X2 = X/2; + PRECISION_TYPE Y = std::abs(max.y() - min.y()); + PRECISION_TYPE Y2 = Y/2; + PRECISION_TYPE Z = std::abs(max.z() - min.z()); + PRECISION_TYPE Z2 = Z/2; + // return the new split AABBs based on the calculated max lengths, but only in their respective axis. + if (X > Y && X > Z) { + return {{min.x(), min.y(), min.z(), max.x()-X2, max.y(), max.z()}, + // start the second AABB at the end of the first AABB. + {min.x()+X2, min.y(), min.z(), max.x(), max.y(), max.z()}}; + } else if (Y > Z) { + return {{min.x(), min.y(), min.z(), max.x(), max.y()-Y2, max.z()}, {min.x(), min.y()+Y2, min.z(), max.x(), max.y(), max.z()}}; + } else { + return {{min.x(), min.y(), min.z(), max.x(), max.y(), max.z()-Z2}, {min.x(), min.y(), min.z()+Z2, max.x(), max.y(), max.z()}}; + } + } + + /* + * Sources for designing these various algorithms + * TODO: test these methods for performance + * https://www.realtimerendering.com/intersections.html + * https://web.archive.org/web/20090803054252/http://tog.acm.org/resources/GraphicsGems/gems/RayBox.c + * https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-box-intersection + * https://tavianator.com/2011/ray_box.html + * https://medium.com/@bromanz/another-view-on-the-classic-ray-aabb-intersection-algorithm-for-bvh-traversal-41125138b525 + */ + + bool AABB::simpleSlabRayAABBMethod(const Ray& ray, PRECISION_TYPE tmin, PRECISION_TYPE tmax){ + // branch less design + // adapted from 2d to fit our 3d scene. + // (turns out this is actually a pretty standard design, but could use some optimization) + PRECISION_TYPE tx1 = (min.x() - ray.getStartingPoint().x())*ray.getInverseDirection().x(); + PRECISION_TYPE tx2 = (max.x() - ray.getStartingPoint().x())*ray.getInverseDirection().x(); + + tmin = std::min(tmin, std::min(tx1, tx2)); + tmax = std::max(tmax, std::max(tx1, tx2)); + + PRECISION_TYPE ty1 = (min.y() - ray.getStartingPoint().y())*ray.getInverseDirection().y(); + PRECISION_TYPE ty2 = (max.y() - ray.getStartingPoint().y())*ray.getInverseDirection().y(); + + tmin = std::max(tmin, std::min(ty1, ty2)); + tmax = std::min(tmax, std::max(ty1, ty2)); + + PRECISION_TYPE tz1 = (min.z() - ray.getStartingPoint().z())*ray.getInverseDirection().z(); + PRECISION_TYPE tz2 = (max.z() - ray.getStartingPoint().z())*ray.getInverseDirection().z(); + + tmin = std::max(tmin, std::min(tz1, tz2)); + tmax = std::min(tmax, std::max(tz1, tz2)); + + return tmax > std::max(tmin, 0.0); + } + + bool AABB::intersects(const Ray& ray, PRECISION_TYPE tmin, PRECISION_TYPE tmax) { + return simpleSlabRayAABBMethod(ray, tmin, tmax); + } +} \ No newline at end of file diff --git a/Step 2/Submission/src/raytracing.cpp b/Step 2/Submission/src/raytracing.cpp new file mode 100644 index 0000000..9f06aca --- /dev/null +++ b/Step 2/Submission/src/raytracing.cpp @@ -0,0 +1,69 @@ +/* + * Created by Brett Terpstra 6920201 on 16/10/22. + * Copyright (c) 2022 Brett Terpstra. All Rights Reserved. + */ +#include +#include + +namespace Raytracing { + + Ray Camera::projectRay(PRECISION_TYPE x, PRECISION_TYPE y) { + // transform the x and y to points from image coords to be inside the camera's viewport. + double transformedX = (x / (image.getWidth() - 1)); + auto transformedY = (y / (image.getHeight() - 1)); + // then generate a ray which extends out from the camera position in the direction with respects to its position on the image + return {position, imageOrigin + transformedX * horizontalAxis + transformedY * verticalAxis - position}; + } + + void Camera::lookAt(const Vec4& pos, const Vec4& lookAtPos, const Vec4& up) { + // standard camera lookAt function + auto w = (pos - lookAtPos).normalize(); + auto u = (Vec4::cross(up, w)).normalize(); + auto v = Vec4::cross(w, u); + + position = pos; + horizontalAxis = viewportWidth * u; + verticalAxis = viewportHeight * v; + imageOrigin = position - horizontalAxis/2 - verticalAxis/2 - w; + } + + void Camera::setRotation(const PRECISION_TYPE yaw, const PRECISION_TYPE pitch, const PRECISION_TYPE roll) { + // TODO: + } + + void Raycaster::run() { + for (int i = 0; i < image.getWidth(); i++){ + for (int j = 0; j < image.getHeight(); j++){ + Raytracing::Vec4 color; + // TODO: profile for speed; + for (int s = 0; s < raysPerPixel; s++){ + // simulate anti aliasing by generating rays with very slight random directions + color = color + raycast(camera.projectRay(i + rnd.getDouble(), j + rnd.getDouble()), 0); + } + PRECISION_TYPE sf = 1.0 / raysPerPixel; + // apply pixel color with gamma correction + image.setPixelColor(i, j, {std::sqrt(sf * color.r()), std::sqrt(sf * color.g()), std::sqrt(sf * color.b())}); + } + } + } + + Vec4 Raycaster::raycast(const Ray& ray, int depth) { + if (depth > maxBounceDepth) + return {0,0,0}; + + auto hit = world.checkIfHit(ray, 0.001, infinity); + + if (hit.first.hit) { + auto object = hit.second; + auto scatterResults = object->getMaterial()->scatter(ray, hit.first); + // if the material scatters the ray, ie casts a new one, + if (scatterResults.scattered) // attenuate the recursive raycast by the material's color + return scatterResults.attenuationColor * raycast(scatterResults.newRay, depth + 1); + //tlog << "Not scattered? " << object->getMaterial() << "\n"; + return {0,0,0}; + } + + // skybox color + return {0.5, 0.7, 1.0}; + } +} \ No newline at end of file diff --git a/Step 2/Submission/src/util/debug.cpp b/Step 2/Submission/src/util/debug.cpp new file mode 100644 index 0000000..df959b9 --- /dev/null +++ b/Step 2/Submission/src/util/debug.cpp @@ -0,0 +1,61 @@ +/* + * Created by Brett Terpstra 6920201 on 18/10/22. + * Copyright (c) 2022 Brett Terpstra. All Rights Reserved. + */ +#include +#include + +namespace Raytracing { + profiler::profiler(std::string name) { + this->name = name; + // currently unused as we don't have a UI yet. + //TD::debugUI::addTab(this); + } + + void profiler::start() { + start("Unnamed"); + } + void profiler::start(const std::string& name) { + auto p1 = std::chrono::high_resolution_clock::now(); + _start = std::chrono::duration_cast(p1.time_since_epoch()).count(); + timings[name] = std::pair(_start, 0); + } + + void profiler::end() { + end("Unnamed"); + } + void profiler::end(const std::string& name) { + auto p1 = std::chrono::high_resolution_clock::now(); + _end = std::chrono::duration_cast(p1.time_since_epoch()).count(); + timings[name] = std::pair(timings[name].first, _end); + } + + void profiler::print() { + ilog << "Profiler " << name << " recorded: "; + for (std::pair> e : timings){ + ilog << "\t" << e.first << " took " << ((double)(e.second.second - e.second.first) / 1000000.0) << "ms to run!"; + } + + } + + void profiler::endAndPrint() { + end(); + print(); + } + + void profiler::render() { + // currently unused as we don't have a UI yet. + /*ImGui::Text("CPU Timings:"); + ImGui::Indent(); + for (std::pair> e : timings) { + ImGui::Text("Elapsed Time(%s): %fms", e.first.c_str(), (double) ((e.second.second - e.second.first) / 1000000.0)); + } + ImGui::Unindent(); + ImGui::NewLine();*/ + } + + profiler::~profiler() { + // currently unused as we don't have a UI yet. + //TD::debugUI::deleteTab(this); + } +} diff --git a/Step 2/Submission/src/util/models.cpp b/Step 2/Submission/src/util/models.cpp new file mode 100644 index 0000000..1d3063d --- /dev/null +++ b/Step 2/Submission/src/util/models.cpp @@ -0,0 +1,65 @@ +/* + * Created by Brett Terpstra 6920201 on 17/10/22. + * Copyright (c) 2022 Brett Terpstra. All Rights Reserved. + */ +#include +#include +#include + +Raytracing::ModelData Raytracing::OBJLoader::loadModel(std::string file) { + std::ifstream modelFile; + + modelFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); + modelFile.open(file); + std::stringstream modelSource; + + std::cout.flush(); + // read the entire file into a string + modelSource << modelFile.rdbuf(); + + modelFile.close(); + std::string modelUnprocessedSource = modelSource.str(); + + auto lines = String::split(modelUnprocessedSource, "\n"); + ilog << "Loading of model file " << file << " complete! Now processing " << lines.size() << " lines.\n"; + + ModelData data; + + for (const auto& line : lines){ + auto spaces = String::split(line, " "); + if (line.starts_with("v ")) { // vertex + data.vertices.emplace_back(std::stof(spaces[1]), std::stof(spaces[2]), std::stof(spaces[3])); + } else if (line.starts_with("vt ")){ // uv + data.uvs.emplace_back(std::stof(spaces[1]), std::stof(spaces[2]), 0); + } else if (line.starts_with("vn ")){ // normal + data.normals.emplace_back(std::stof(spaces[1]), std::stof(spaces[2]), std::stof(spaces[3])); + } else if (line.starts_with("f ")){ // face + // we've reached the faces, we'll need to process them later. + auto t1 = String::split(spaces[1], "/"); + auto t2 = String::split(spaces[2], "/"); + auto t3 = String::split(spaces[3], "/"); + face f {}; + + // obj files are 1 indexed, + // but arrays are 0 indexed, + // must be transformed. + f.v1 = std::stoi(t1[0])-1; + f.v2 = std::stoi(t2[0])-1; + f.v3 = std::stoi(t3[0])-1; + + f.uv1 = std::stoi(t1[1])-1; + f.uv2 = std::stoi(t2[1])-1; + f.uv3 = std::stoi(t3[1])-1; + + f.n1 = std::stoi(t1[2])-1; + f.n2 = std::stoi(t2[2])-1; + f.n3 = std::stoi(t3[2])-1; + + data.faces.push_back(f); + } + } + + + ilog << "Completed extracting vertex data from model file " << file << "!\n"; + return data; +} diff --git a/Step 2/Submission/src/util/parser.cpp b/Step 2/Submission/src/util/parser.cpp new file mode 100644 index 0000000..cd9d977 --- /dev/null +++ b/Step 2/Submission/src/util/parser.cpp @@ -0,0 +1,115 @@ +/* + * Created by Brett Terpstra 6920201 on 14/10/22. + * Copyright (c) Brett Terpstra 2022 All Rights Reserved + */ +#include "util/parser.h" +#include +namespace Raytracing { + + + Parser::Parser() { + // only to add the description (very annoying we have to have double {}!) + addOption({{"--help"}, {"-h"}}, "Show this help menu!"); + } + + std::string Parser::getOptionValue(const std::string& option) { + return raw_values.contains(option) ? raw_values.at(option) : defaultValues.at(option); + } + + int Parser::parse(char **args, int argc) { + // first arg is always the command path + // which we don't care about. + for (int i = 1; i < argc; i++) { + std::string option = std::string(args[i]); + std::string value = "true"; + // we only want to get the value if it was actually provided. + if (i + 1 < argc) { + value = std::string(args[i + 1]); + // must make sure we don't skip over valuable parasble options! + if (!value.starts_with("-")) { + // we must add to the value of i since we don't want to try and parse values. + i++; + } else // if it does start with -, it's an option, we need to reset to true. + value = "true"; + } + // if values are provided to us with -- or - we simply don't care about them! + if (option.starts_with("-")) + raw_values.insert(std::pair(option, value)); + else + throw std::runtime_error("Unfortunately an invalid argument was provided! {" + option + "}"); + } + if (hasOption({{"--help"}, {"-h"}})){ + std::cout << "Raytracer help information:" << std::endl; + for (const auto& d : descriptions){ + std::cout << d; + } + return true; + } + return false; + } + + void Parser::addOption(const std::string& option, const std::string& description, const std::string& defaultValue) { + // we only want to add if the default value isn't empty, + // since we might want to use the option as a single optional flag and not a value. + if (!Raytracing::String::trim_copy(defaultValue).empty()) + defaultValues.insert(std::pair(option, defaultValue)); + descriptions.emplace_back(option + " :" + description); + } + + bool Parser::hasOptionChanged(const std::string& option) { + return raw_values.contains(option) && defaultValues.contains(option) && raw_values.at(option) != defaultValues.at(option); + } + + void Parser::addOption(const std::vector& options, const std::string &description, const std::string &defaultValue) { + // add option like normal + for (const auto& option : options){ + if (!Raytracing::String::trim_copy(defaultValue).empty()) + defaultValues.insert(std::pair(option, defaultValue)); + } + // but improve the description for multi-option + std::stringstream desStr; + + // add all the options to the description, seperated by "," + for (int i = 0; i < options.size(); i++){ + desStr << options[i]; + if (i != options.size()-1) + desStr << ", "; + } + desStr << " :"; + desStr << description; + + descriptions.emplace_back(desStr.str()); + } + + bool Parser::hasOption(const std::string &option) { + return raw_values.contains(option); + } + + bool Parser::hasOption(const std::vector &options) { + for (const auto& option : options) + if (hasOption(option)) + return true; + return false; + } + + void Parser::printAllInInfo() { + // print all with default values + for (const auto& opt : defaultValues){ + ilog << opt.first << ": " << (raw_values.contains(opt.first) ? raw_values.at(opt.first) : opt.second) << " (default: " << opt.second << ")" << std::endl; + } + // then just print all the ones which don't have but where provided + for (const auto& opt : raw_values){ + if (!defaultValues.contains(opt.first)) + ilog << "With: " << opt.first; + } + } + + void Parser::printDifferenceInInfo() { + for (const auto& opt : raw_values){ + if (defaultValues.contains(opt.first)) + if (opt.second == defaultValues.at(opt.first)) + ilog << opt.first << ": " << opt.second << " (default: " << defaultValues.at(opt.first) << ")" << std::endl; + } + } + +} diff --git a/Step 2/Submission/src/world.cpp b/Step 2/Submission/src/world.cpp new file mode 100644 index 0000000..18fd863 --- /dev/null +++ b/Step 2/Submission/src/world.cpp @@ -0,0 +1,212 @@ +/* + * Created by Brett Terpstra 6920201 on 16/10/22. + * Copyright (c) 2022 Brett Terpstra. All Rights Reserved. + */ +#include +#include + +namespace Raytracing { + + World::~World() { + for (auto* p: objects) + delete (p); + for (const auto& p: materials) + delete (p.second); + //delete(bvhTree); + } + + HitData SphereObject::checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const { + PRECISION_TYPE radiusSquared = radius * radius; + // move the ray to be with respects to the sphere + Vec4 RayWRTSphere = ray.getStartingPoint() - position; + // now determine the discriminant for the quadratic formula for the function of line sphere intercept + PRECISION_TYPE a = ray.getDirection().lengthSquared(); + PRECISION_TYPE b = Raytracing::Vec4::dot(RayWRTSphere, ray.getDirection()); + PRECISION_TYPE c = RayWRTSphere.lengthSquared() - radiusSquared; + // > 0: the hit has two roots, meaning we hit both sides of the sphere + // = 0: the ray has one root, we hit the edge of the sphere + // < 0: ray isn't inside the sphere. + PRECISION_TYPE discriminant = b * b - (a * c); + + // < 0: ray isn't inside the sphere. Don't need to bother calculating the roots. + if (discriminant < 0) + return {false, Vec4(), Vec4(), 0}; + + // now we have to find the root which exists inside our range [min,max] + auto root = (-b - std::sqrt(discriminant)) / a; + // if the first root isn't in our range + if (root < min || root > max) { + // check the second root + root = (-b + std::sqrt(discriminant)) / a; + if (root < min || root > max) { + // if the second isn't in the range then we also must return false. + return {false, Vec4(), Vec4(), 0}; + } + } + // the hit point is where the ray is when extended to the root + auto RayAtRoot = ray.along(root); + // The normal of a sphere is just the point of the hit minus the center position + auto normal = (RayAtRoot - position).normalize(); + + /*if (Raytracing::vec4::dot(ray.getDirection(), normal) > 0.0) { + tlog << "ray inside sphere\n"; + } else + tlog << "ray outside sphere\n"; + */ + return {true, RayAtRoot, normal, root}; + } + + std::pair World::checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const { + /*if (bvhTree != nullptr){ + auto hResult = HitData{false, Vec4(), Vec4(), max}; + Object* objPtr = nullptr; + + auto intersected = bvhTree->rayIntersect(ray, min, max); + + //dlog << "Intersections " << intersected.size() << " " << ray << "\n"; + + for (auto* ptr : intersected) { + auto cResult = ptr->checkIfHit(ray, min, hResult.length); + if (cResult.hit) { + hResult = cResult; + objPtr = ptr; + } + } + // after we check the BVH, we have to check for other missing objects + // since stuff like spheres currently don't have AABB and AABB isn't a requirement + // for the object class (to be assigned) + for (auto* obj: bvhTree->noAABBObjects) { + // check up to the point of the last closest hit, + // will give the closest object's hit result + auto cResult = obj->checkIfHit(ray, min, hResult.length); + if (cResult.hit) { + hResult = cResult; + objPtr = obj; + } + } + + return {hResult, objPtr}; + } else {*/ + // rejection algo without using a binary space partitioning data structure + auto hResult = HitData{false, Vec4(), Vec4(), max}; + Object* objPtr = nullptr; + for (auto* obj: objects) { + // check up to the point of the last closest hit, + // will give the closest object's hit result + auto cResult = obj->checkIfHit(ray, min, hResult.length); + if (cResult.hit) { + hResult = cResult; + objPtr = obj; + } + } + return {hResult, objPtr}; + //} + } + + void World::generateBVH() { + //bvhTree = new BVHTree(objects); + } + + ScatterResults DiffuseMaterial::scatter(const Ray& ray, const HitData& hitData) const { + Vec4 newRay = hitData.normal + Raytracing::Raycaster::randomUnitVector().normalize(); + + // rays that are close to zero are liable to floating point precision errors + if (newRay.x() < EPSILON && newRay.y() < EPSILON && newRay.z() < EPSILON && newRay.w() < EPSILON) + newRay = hitData.normal; + + return {true, Ray{hitData.hitPoint, newRay}, getBaseColor()}; + } + + ScatterResults MetalMaterial::scatter(const Ray& ray, const HitData& hitData) const { + // create a ray reflection + Vec4 newRay = reflect(ray.getDirection().normalize(), hitData.normal); + // make sure our reflected ray is outside the sphere and doesn't point inwards + bool shouldReflect = Vec4::dot(newRay, hitData.normal) > 0; + return {shouldReflect, Ray{hitData.hitPoint, newRay}, getBaseColor()}; + } + + ScatterResults BrushedMetalMaterial::scatter(const Ray& ray, const HitData& hitData) const { + // create a ray reflection + Vec4 newRay = reflect(ray.getDirection().normalize(), hitData.normal); + // make sure our reflected ray is outside the sphere and doesn't point inwards + bool shouldReflect = Vec4::dot(newRay, hitData.normal) > 0; + return {shouldReflect, Ray{hitData.hitPoint, newRay + Raycaster::randomUnitVector() * fuzzyness}, getBaseColor()}; + } + + static HitData checkIfTriangleGotHit(const Triangle& theTriangle, const Vec4& position, const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) { + // Möller–Trumbore intersection algorithm + // https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/moller-trumbore-ray-triangle-intersection + // https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm + Vec4 edge1, edge2, h, s, q; + PRECISION_TYPE a, f, u, v; + edge1 = (theTriangle.vertex2 + position) - (theTriangle.vertex1 + position); + edge2 = (theTriangle.vertex3 + position) - (theTriangle.vertex1 + position); + + h = Vec4::cross(ray.getDirection(), edge2); + a = Vec4::dot(edge1, h); + + if (a > -EPSILON && a < EPSILON) + return {false, Vec4(), Vec4(), 0}; //parallel to triangle + + f = 1.0 / a; + s = ray.getStartingPoint() - (theTriangle.vertex1 + position); + u = f * Vec4::dot(s, h); + + if (u < 0.0 || u > 1.0) + return {false, Vec4(), Vec4(), 0}; + + q = Vec4::cross(s, edge1); + v = f * Vec4::dot(ray.getDirection(), q); + if (v < 0.0 || u + v > 1.0) + return {false, Vec4(), Vec4(), 0}; + + // At this stage we can compute t to find out where the intersection point is on the line. + PRECISION_TYPE t = f * Vec4::dot(edge2, q); + // keep t in reasonable bounds, ensuring we respect depth + if (t > EPSILON && t >= min && t <= max) { + // ray intersects + Vec4 rayIntersectionPoint = ray.along(t); + Vec4 normal; + // normal = theTriangle.findClosestNormal(rayIntersectionPoint - position); + if (theTriangle.hasNormals) // returning the closest normal is extra computation when n1 would likely be fine. + normal = theTriangle.normal1; + else { + // standard points to normal algorithm but using already computed edges + normal = Vec4{edge1.y() * edge2.z(), edge1.z() * edge2.x(), edge1.x() * edge2.y()} - + Vec4{edge1.z() * edge2.y(), edge1.x() * edge2.z(), edge1.y() * edge2.x()}; + } + return {true, rayIntersectionPoint, normal, t}; + } + return {false, Vec4(), Vec4(), 0}; + } + + HitData TriangleObject::checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const { + return checkIfTriangleGotHit(theTriangle, position, ray, min, max); + } + + HitData ModelObject::checkIfHit(const Ray& ray, PRECISION_TYPE min, PRECISION_TYPE max) const { + auto hResult = HitData{false, Vec4(), Vec4(), max}; + // must check through all the triangles in the object + // respecting depth along the way + // but reducing the max it can reach my the last longest vector length. + for (const Triangle& t : triangles) { + auto cResult = checkIfTriangleGotHit(t, position, ray, min, hResult.length); + if (cResult.hit) + hResult = cResult; + } + /*auto hResult = HitData{false, Vec4(), Vec4(), max}; + + auto intersected = tree->rayIntersect(ray, min, max); + + for (auto t : intersected){ + // apparently this kind of casting is okay + // which makes sense since the actual data behind it is a empty object + // just this is really bad and im too annoyed to figure out a better way. TODO:. + auto cResult = checkIfTriangleGotHit(((EmptyObject*)(t))->tri, position, ray, min, hResult.length); + if (cResult.hit) + hResult = cResult; + }*/ + + return hResult; + } +} \ No newline at end of file diff --git a/Step 2/cmake-build-release/.ninja_deps b/Step 2/cmake-build-release/.ninja_deps index 2e7a537..92b5a03 100644 Binary files a/Step 2/cmake-build-release/.ninja_deps and b/Step 2/cmake-build-release/.ninja_deps differ diff --git a/Step 2/cmake-build-release/.ninja_log b/Step 2/cmake-build-release/.ninja_log index 42d4074..3af124d 100644 --- a/Step 2/cmake-build-release/.ninja_log +++ b/Step 2/cmake-build-release/.ninja_log @@ -1,105 +1,18 @@ # ninja log v5 -1 1269 1666191947233881035 CMakeFiles/Step_2.dir/src/main.cpp.o 387235f0a298f257 -1 1329 1666208294813201609 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o 7957915dde476a69 -10 1796 1666150208085056398 CMakeFiles/Step_2.dir/src/util/parser.cpp.o 4bae007146d86363 -1 840 1666191946805877238 CMakeFiles/Step_2.dir/src/raytracing.cpp.o e78f62563fb6428 -1269 1307 1666191947269881356 Step_2 2fd6ed1a72fdaa11 -9 1453 1666150207745047625 CMakeFiles/Step_2.dir/src/util/debug.cpp.o e1fa95ca3d6a9ce2 -1 1770 1666208295257215005 CMakeFiles/Step_2.dir/src/util/models.cpp.o 12734a9214cf8718 -1 1112 1666150207401038752 CMakeFiles/Step_2.dir/src/globals.cpp.o 5fc3599d89d4b8a2 -2 1036 1666191947001878976 CMakeFiles/Step_2.dir/src/world.cpp.o 713f22c63a585103 -1 2726 1666208296213243852 CMakeFiles/Step_2.dir/src/image/image.cpp.o 4d16c0d3199b9aa8 -1 874 1666208421288995782 CMakeFiles/Step_2.dir/src/globals.cpp.o 8caaf06f90e0c038 -2 1437 1666208421849012493 CMakeFiles/Step_2.dir/src/util/debug.cpp.o 2bec3f4c06e82095 +1 1911 1666240985448103414 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 +1 1263 1666240984800084042 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323 2 1440 1666208421853012610 CMakeFiles/Step_2.dir/src/util/parser.cpp.o 67ef710b86dd32d6 -2 1496 1666208421909014283 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153 -2 1709 1666208422121020608 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323 -2 1728 1666208422141021205 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660 -2 1966 1666208422381028366 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e -1 2034 1666208422449030392 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -1 2886 1666208423301055812 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271 -2887 2929 1666208423341057004 Step_2 7437176fc9e68547 -2 1224 1666210629933631775 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323 -2 1374 1666210630081636055 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153 -2 1548 1666210630257641141 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660 -2 1652 1666210630361644148 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e -2 1913 1666210630621651665 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -1 2643 1666210631353672824 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271 -2643 2681 1666210631389673865 Step_2 7437176fc9e68547 -1 1212 1666212386048431924 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323 -1 1360 1666212386196434896 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153 -1 1484 1666212386320437387 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660 -1 1611 1666212386448439957 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e -1 1875 1666212386712445259 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -1 2656 1666212387492460928 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271 -2656 2703 1666212387540461893 Step_2 7437176fc9e68547 -2 1250 1666212415865034379 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323 -2 1375 1666212415989036899 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153 -2 1566 1666212416181040803 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660 -2 1617 1666212416233041863 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e -1 1928 1666212416545048206 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -1 2621 1666212417237062279 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271 -2621 2660 1666212417273063011 Step_2 7437176fc9e68547 -1 1269 1666212741664012313 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323 -1 1368 1666212741764014542 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153 -2 1543 1666212741940018466 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660 -2 1646 1666212742044020786 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e -1 1910 1666212742308026673 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -1 2600 1666212742996042017 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271 -2600 2642 1666212743036042909 Step_2 7437176fc9e68547 -1 1791 1666215595209880342 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323 -1 1840 1666215595257881412 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153 -1 1888 1666215595305882480 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e -1 1990 1666215595409884798 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660 -1 2395 1666215595813893798 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -0 3052 1666215596469908420 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271 -3052 3125 1666215596541910026 Step_2 7437176fc9e68547 -1 788 1666215661443385226 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323 -1 949 1666215661603388929 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153 -1 1072 1666215661727391802 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660 -1 1204 1666215661859394855 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e -1 1441 1666215662095400322 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -1 2153 1666215662807416814 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271 -2153 2191 1666215662847417737 Step_2 7437176fc9e68547 -1 1023 1666216381510163196 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153 -1 1068 1666216381554164424 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323 -1 1274 1666216381762170234 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e -1 1520 1666216382006177047 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660 -1 1732 1666216382218182968 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -1 2646 1666216383134208548 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271 -2646 2694 1666216383182209890 Step_2 7437176fc9e68547 -1 878 1666217200233659155 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153 -0 1380 1666217200733673732 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -1380 1421 1666217200773674899 Step_2 7437176fc9e68547 -1 1341 1666217588772696599 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -1341 1380 1666217588812697660 Step_2 7437176fc9e68547 -0 1305 1666217602745067311 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -1305 1345 1666217602785068374 Step_2 7437176fc9e68547 -0 1381 1666217890128871360 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -1381 1424 1666217890172872578 Step_2 7437176fc9e68547 -0 997 1666218374062493836 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660 -0 1320 1666218374386503078 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -1320 1358 1666218374422504103 Step_2 7437176fc9e68547 -1 942 1666218674383099244 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323 -1 1007 1666218674447101084 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153 -1 1289 1666218674727109140 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e -1 1342 1666218674779110635 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660 -1 1597 1666218675035117999 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -0 2433 1666218675871142048 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271 -2433 2478 1666218675919143428 Step_2 7437176fc9e68547 -0 1373 1666218737704921878 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -1373 1413 1666218737744923030 Step_2 7437176fc9e68547 -2 1448 1666220004262511612 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323 -2 1482 1666220004294511493 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153 -2 1663 1666220004478510816 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660 -2 1879 1666220004694510019 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e -1 2049 1666220004862509401 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -1 2908 1666220005722506266 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271 -2909 2956 1666220005770506091 Step_2 7437176fc9e68547 -1 1232 1666220317311320782 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323 -1 1413 1666220317491322171 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153 -1 1568 1666220317647323373 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660 -1 1708 1666220317783324422 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e -1 2010 1666220318087326770 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 -1 2840 1666220318919333214 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271 -2840 2881 1666220318959333521 Step_2 7437176fc9e68547 +1 1419 1666240984956088704 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153 +2673 2712 1666240986252127454 Step_2 7437176fc9e68547 +2 1437 1666208421849012493 CMakeFiles/Step_2.dir/src/util/debug.cpp.o 2bec3f4c06e82095 +2 1611 1666240985148094447 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e +1 874 1666208421288995782 CMakeFiles/Step_2.dir/src/globals.cpp.o 8caaf06f90e0c038 +2 1517 1666240985056091694 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660 +1 2673 1666240986212126256 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271 +1 1202 1666241022097198348 CMakeFiles/Step_2.dir/src/math/colliders.cpp.o c0cac41f151ac323 +1 1575 1666241022473209572 CMakeFiles/Step_2.dir/src/util/models.cpp.o 7aa2ab72df0658e +1 1914 1666241022809219604 CMakeFiles/Step_2.dir/src/raytracing.cpp.o d0ae86d239f7153 +1 1942 1666241022837220441 CMakeFiles/Step_2.dir/src/main.cpp.o 8da3d580eb6e9a1 +2 1962 1666241022857221038 CMakeFiles/Step_2.dir/src/world.cpp.o 9ae7336b91871660 +1 2624 1666241023521240863 CMakeFiles/Step_2.dir/src/image/image.cpp.o f94f9df081e81271 +2624 2663 1666241023561242054 Step_2 7437176fc9e68547 diff --git a/Step 2/cmake-build-release/2022-10-19 19:18:23.png b/Step 2/cmake-build-release/2022-10-19 19:18:23.png new file mode 100644 index 0000000..480679e Binary files /dev/null and b/Step 2/cmake-build-release/2022-10-19 19:18:23.png differ diff --git a/Step 2/cmake-build-release/2022-10-20 0:45:45.png b/Step 2/cmake-build-release/2022-10-20 0:45:45.png new file mode 100644 index 0000000..b626065 Binary files /dev/null and b/Step 2/cmake-build-release/2022-10-20 0:45:45.png differ diff --git a/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/image/image.cpp.o b/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/image/image.cpp.o index 24a8c9d..f76055e 100644 Binary files a/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/image/image.cpp.o and b/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/image/image.cpp.o differ diff --git a/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/main.cpp.o b/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/main.cpp.o index 3efb192..63477c3 100644 Binary files a/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/main.cpp.o and b/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/main.cpp.o differ diff --git a/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/math/colliders.cpp.o b/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/math/colliders.cpp.o index 8e2937a..16e4f60 100644 Binary files a/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/math/colliders.cpp.o and b/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/math/colliders.cpp.o differ diff --git a/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/raytracing.cpp.o b/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/raytracing.cpp.o index eda4d48..e30c0d8 100644 Binary files a/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/raytracing.cpp.o and b/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/raytracing.cpp.o differ diff --git a/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/util/models.cpp.o b/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/util/models.cpp.o index ad71a39..fdb5266 100644 Binary files a/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/util/models.cpp.o and b/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/util/models.cpp.o differ diff --git a/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/world.cpp.o b/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/world.cpp.o index 0f5b582..617d611 100644 Binary files a/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/world.cpp.o and b/Step 2/cmake-build-release/CMakeFiles/Step_2.dir/src/world.cpp.o differ diff --git a/Step 2/cmake-build-release/Step_2 b/Step 2/cmake-build-release/Step_2 index 7bf2d01..ad8d058 100755 Binary files a/Step 2/cmake-build-release/Step_2 and b/Step 2/cmake-build-release/Step_2 differ diff --git a/Step 2/cmake-build-release/Testing/Temporary/LastTest.log b/Step 2/cmake-build-release/Testing/Temporary/LastTest.log index 3caf6fc..41ff1cf 100644 --- a/Step 2/cmake-build-release/Testing/Temporary/LastTest.log +++ b/Step 2/cmake-build-release/Testing/Temporary/LastTest.log @@ -1,3 +1,3 @@ -Start testing: Oct 19 18:58 EDT +Start testing: Oct 20 00:46 EDT ---------------------------------------------------------- -End testing: Oct 19 18:58 EDT +End testing: Oct 20 00:46 EDT diff --git a/Step 2/cmake_make_command.sh b/Step 2/cmake_make_command.sh index 1cb68d8..95de43f 100644 --- a/Step 2/cmake_make_command.sh +++ b/Step 2/cmake_make_command.sh @@ -1 +1,4 @@ -cmake -DCMAKE_BUILD_TYPE=Deug -G Ninja .. +#!/bin/bash +cmake -DCMAKE_BUILD_TYPE=Release -G Ninja .. +ninja +./Step_2 diff --git a/Step 2/include/math/vectors.h b/Step 2/include/math/vectors.h index 2b130c0..96e0fbf 100644 --- a/Step 2/include/math/vectors.h +++ b/Step 2/include/math/vectors.h @@ -8,7 +8,7 @@ // AVX512 isn't supported on my CPU. We will use AVX2 since it is supported by most modern CPUs // THIS IS CURRENTLY BROKEN. DO NOT USE. it's a great idea, but I've run out of time to debug this. Will be in step 3 -//#define USE_SIMD_CPU +#define USE_SIMD_CPU #ifdef USE_SIMD_CPU @@ -44,7 +44,8 @@ namespace Raytracing { // makes it easy to convert between AVX and double data types. union { struct { - double _x, _y, _z, _w; + PRECISION_TYPE _x{}, _y{}, _z{}, _w{}; + //PRECISION_TYPE _w, _z, _y, _x; }; __m256d avxData; }; @@ -56,10 +57,16 @@ namespace Raytracing { friend Vec4 operator/(const Vec4& left, const Vec4& right); public: - Vec4(): avxData{_mm256_set_pd(0.0, 0.0, 0.0, 0.0)} {} - Vec4(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z): avxData{_mm256_set_pd(x, y, z, 0)} {} - Vec4(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z, PRECISION_TYPE w): avxData{_mm256_set_pd(x, y, z, w)} {} - Vec4(const Vec4& vec): avxData{_mm256_set_pd(vec.x(), vec.y(), vec.z(), vec.w())} {} + Vec4(): avxData(_mm256_setzero_pd()) {} + Vec4(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z): avxData(_mm256_setr_pd(x, y, z, 0.0)) { + //tlog << x << ":" << _x << " " << y << ":" << _y << " " << z << ":" << _z << "\n"; + } + Vec4(PRECISION_TYPE x, PRECISION_TYPE y, PRECISION_TYPE z, PRECISION_TYPE w): avxData(_mm256_setr_pd(x, y, z, w)) { + //dlog << x << ":" << _x << " " << y << ":" << _y << " " << z << ":" << _z << "\n"; + } + Vec4(const Vec4& vec): avxData(_mm256_setr_pd(vec.x(), vec.y(), vec.z(), vec.w())) { + //ilog << vec.x() << ":" << _x << " " << vec.y() << ":" << _y << " " << vec.z() << ":" << _z << "\n"; + } // most of the modern c++ here is because clang tidy was annoying me [[nodiscard]] inline PRECISION_TYPE x() const { return _x; } @@ -122,16 +129,16 @@ namespace Raytracing { // takes the x element and places it in the 3rd element of the vector // and then the w element in the last element of the vector // creating the alignment {left.y(), left.z(), left.x(), left.w()} (as seen in the cross algorithm - __m256d leftLeftShuffle = _mm256_shuffle_pd(left.avxData, left.avxData, _MM_SHUFFLE(3,0,2,1)); + __m256d leftLeftShuffle = _mm256_permute4x64_pd(left.avxData, _MM_SHUFFLE(3,0,2,1)); // same thing but produces {right.z(), right.x(), right.y(), right.w()} - __m256d rightLeftShuffle = _mm256_shuffle_pd(right.avxData, right.avxData, _MM_SHUFFLE(3,1,0,2)); - // multiply to do the first step of the cross process - __m256d multiLeft = _mm256_mul_pd(leftLeftShuffle, rightLeftShuffle); + __m256d rightLeftShuffle = _mm256_permute4x64_pd(right.avxData, _MM_SHUFFLE(3,1,0,2)); // now we have to do the right side multiplications // {left.z(), left.x(), left.y(), left.w()} - __m256d leftRightShuffle = _mm256_shuffle_pd(left.avxData, left.avxData, _MM_SHUFFLE(3,1,0,2)); + __m256d leftRightShuffle = _mm256_permute4x64_pd(left.avxData, _MM_SHUFFLE(3,1,0,2)); // {right.y(), right.z(), right.x(), right.w()} - __m256d rightRightShuffle = _mm256_shuffle_pd(right.avxData, right.avxData, _MM_SHUFFLE(3,0,2,1)); + __m256d rightRightShuffle = _mm256_permute4x64_pd(right.avxData, _MM_SHUFFLE(3,0,2,1)); + // multiply to do the first step of the cross process + __m256d multiLeft = _mm256_mul_pd(leftLeftShuffle, rightLeftShuffle); // multiply the right sides of the subtraction sign __m256d multiRight = _mm256_mul_pd(leftRightShuffle, rightRightShuffle); // then subtract to produce the cross product @@ -139,6 +146,11 @@ namespace Raytracing { // yes this looks a lot more complicated, but it should be faster! AVXConvert conv {}; conv.avxData = subs; + /*auto b = Vec4{left.y() * right.z() - left.z() * right.y(), + left.z() * right.x() - left.x() * right.z(), + left.x() * right.y() - left.y() * right.x()}; + tlog << b._x << " " << b._y << " " << b._z << "\n"; + tlog << conv._x << " " << conv._y << " " << conv._z << "\n\n";*/ return {conv._x, conv._y, conv._z, conv._w}; } };