diff --git a/libs/yocto/CMakeLists.txt b/libs/yocto/CMakeLists.txt index d53ed4181..b109c7898 100644 --- a/libs/yocto/CMakeLists.txt +++ b/libs/yocto/CMakeLists.txt @@ -14,7 +14,7 @@ add_library(yocto STATIC yocto_parallel.h yocto_cli.h ) -set_target_properties(yocto PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES) +set_target_properties(yocto PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED YES) target_include_directories(yocto PRIVATE ext/) target_include_directories(yocto PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..") @@ -46,42 +46,42 @@ if(YOCTO_DENOISE) endif(YOCTO_DENOISE) if(YOCTO_CUDA) + enable_language(CUDA) + set_target_properties(yocto PROPERTIES CUDA_STANDARD 17 CUDA_STANDARD_REQUIRED YES) -enable_language(CUDA) -set_target_properties(yocto PROPERTIES CUDA_STANDARD 17 CUDA_STANDARD_REQUIRED YES) + # Optix + if(DEFINED ENV{OptiX_INSTALL_DIR}) + find_path(OptiX_ROOT_DIR NAMES include/optix.h PATHS $ENV{OptiX_INSTALL_DIR}) + else() + find_path(OptiX_ROOT_DIR NAMES include/optix.h) + endif() -# Optix -if (DEFINED ENV{OptiX_INSTALL_DIR}) - find_path(OptiX_ROOT_DIR NAMES include/optix.h PATHS $ENV{OptiX_INSTALL_DIR}) -else() - find_path(OptiX_ROOT_DIR NAMES include/optix.h) -endif() -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(OptiX FOUND_VAR OptiX_FOUND REQUIRED_VARS OptiX_ROOT_DIR) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(OptiX FOUND_VAR OptiX_FOUND REQUIRED_VARS OptiX_ROOT_DIR) -add_library(OptiX::OptiX INTERFACE IMPORTED) -target_include_directories(OptiX::OptiX INTERFACE ${OptiX_ROOT_DIR}/include) + add_library(OptiX::OptiX INTERFACE IMPORTED) + target_include_directories(OptiX::OptiX INTERFACE ${OptiX_ROOT_DIR}/include) -embed_ptx( - OUTPUT_TARGET + embed_ptx( + OUTPUT_TARGET yocto_cutrace_device - PTX_LINK_LIBRARIES + PTX_LINK_LIBRARIES OptiX::OptiX - SOURCES + SOURCES yocto_cutrace.cu -) - -target_compile_definitions(yocto PUBLIC -DYOCTO_CUDA) + ) -find_package(CUDAToolkit REQUIRED) -target_link_libraries(yocto PUBLIC OptiX::OptiX yocto_cutrace_device CUDA::cuda_driver CUDA::cudart_static) + target_compile_definitions(yocto PUBLIC -DYOCTO_CUDA) + find_package(CUDAToolkit REQUIRED) + target_link_libraries(yocto PUBLIC OptiX::OptiX yocto_cutrace_device CUDA::cuda_driver CUDA::cudart_static) endif(YOCTO_CUDA) # warning flags if(APPLE) target_compile_options(yocto PUBLIC -Wall -Wconversion -Wno-sign-conversion -Wno-implicit-float-conversion -Wno-unused-variable) endif(APPLE) + if(MSVC) target_compile_options(yocto PUBLIC /D_CRT_SECURE_NO_WARNINGS) target_compile_options(yocto PUBLIC /EHsc) diff --git a/libs/yocto/yocto_bvh.h b/libs/yocto/yocto_bvh.h index 9ffce5adf..0420d0d74 100644 --- a/libs/yocto/yocto_bvh.h +++ b/libs/yocto/yocto_bvh.h @@ -175,10 +175,6 @@ scene_intersection intersect_instance_ebvh(const scene_ebvh& bvh, // ----------------------------------------------------------------------------- namespace yocto { -// backward compatibility -using bvh_shape [[deprecated]] = shape_bvh; -using bvh_scene [[deprecated]] = scene_bvh; - // backward compatibility [[deprecated]] inline shape_bvh make_bvh( const shape_data& shape, bool embree = false, bool highquality = false) { diff --git a/libs/yocto/yocto_color.h b/libs/yocto/yocto_color.h index 8d67bcd27..4b24f97c4 100644 --- a/libs/yocto/yocto_color.h +++ b/libs/yocto/yocto_color.h @@ -91,13 +91,18 @@ inline vec3f rgba_to_rgb(const vec4f& rgba); // Apply contrast. Grey should be 0.18 for linear and 0.5 for gamma. inline vec3f lincontrast(const vec3f& rgb, float contrast, float grey); +inline vec4f lincontrast(const vec4f& rgb, float contrast, float grey); // Apply contrast in log2. Grey should be 0.18 for linear and 0.5 for gamma. inline vec3f logcontrast(const vec3f& rgb, float logcontrast, float grey); +inline vec4f logcontrast(const vec4f& rgb, float logcontrast, float grey); // Apply an s-shaped contrast. inline vec3f contrast(const vec3f& rgb, float contrast); +inline vec4f contrast(const vec4f& rgb, float contrast); // Apply saturation. inline vec3f saturate(const vec3f& rgb, float saturation, const vec3f& weights = vec3f{0.333333f, 0.333333f, 0.333333f}); +inline vec4f saturate(const vec4f& rgb, float saturation, + const vec3f& weights = vec3f{0.333333f, 0.333333f, 0.333333f}); // Apply tone mapping inline vec3f tonemap( @@ -262,6 +267,10 @@ inline vec3f rgba_to_rgb(const vec4f& rgba) { return xyz(rgba); } inline vec3f lincontrast(const vec3f& rgb, float contrast, float grey) { return max({0, 0, 0}, grey + (rgb - grey) * (contrast * 2)); } +inline vec4f lincontrast(const vec4f& rgb, float contrast, float grey) { + auto ret = lincontrast(xyz(rgb), contrast, grey); + return {ret.x, ret.y, ret.z, rgb.w}; +} // Apply contrast in log2. Grey should be 0.18 for linear and 0.5 for gamma. inline vec3f logcontrast(const vec3f& rgb, float logcontrast, float grey) { auto epsilon = (float)0.0001; @@ -270,16 +279,29 @@ inline vec3f logcontrast(const vec3f& rgb, float logcontrast, float grey) { auto adjusted = log_grey + (log_ldr - log_grey) * (logcontrast * 2); return max({0, 0, 0}, exp2(adjusted) - epsilon); } +inline vec4f logcontrast(const vec4f& rgb, float contrast, float grey) { + auto ret = logcontrast(xyz(rgb), contrast, grey); + return {ret.x, ret.y, ret.z, rgb.w}; +} // Apply an s-shaped contrast. inline vec3f contrast(const vec3f& rgb, float contrast) { return gain(rgb, 1 - contrast); } +inline vec4f contrast(const vec4f& rgb, float contrast) { + auto ret = yocto::contrast(xyz(rgb), contrast); + return {ret.x, ret.y, ret.z, rgb.w}; +} // Apply saturation. inline vec3f saturate( const vec3f& rgb, float saturation, const vec3f& weights) { auto grey = dot(weights, rgb); return max({0, 0, 0}, grey + (rgb - grey) * (saturation * 2)); } +inline vec4f saturate( + const vec4f& rgb, float saturation, const vec3f& weights) { + auto ret = saturate(xyz(rgb), saturation, weights); + return {ret.x, ret.y, ret.z, rgb.w}; +} #ifndef __CUDACC__ diff --git a/libs/yocto/yocto_gui.cpp b/libs/yocto/yocto_gui.cpp index 7667d83c0..1b022cab4 100644 --- a/libs/yocto/yocto_gui.cpp +++ b/libs/yocto/yocto_gui.cpp @@ -2557,7 +2557,7 @@ struct glwidgets_param { glwidgets_param( vec3f value, const vec2f& minmax = {0, 0}, bool readonly = false) : type{glwidgets_param_type::value3f} - , valuef{value.x, value.y, value.z} + , valuef{value.x, value.y, value.z, 0} , minmaxf{minmax} , readonly{readonly} {} glwidgets_param( diff --git a/libs/yocto/yocto_math.h b/libs/yocto/yocto_math.h index b96b4b6a1..ca1c00106 100644 --- a/libs/yocto/yocto_math.h +++ b/libs/yocto/yocto_math.h @@ -49,6 +49,7 @@ namespace yocto { // using directives +using std::array; using std::pair; } // namespace yocto @@ -99,6 +100,10 @@ inline float pow(float a, float b); inline bool isfinite(float a); inline float atan2(float a, float b); inline float fmod(float a, float b); +inline float mod(float a, float b); +inline float floor(float a); +inline float ceil(float a); +inline float round(float a); inline float radians(float a); inline float degrees(float a); inline float lerp(float a, float b, float u); @@ -111,6 +116,7 @@ inline int abs(int a); inline int min(int a, int b); inline int max(int a, int b); inline int clamp(int a, int min, int max); +inline int mod(int a, int b); inline int sign(int a); inline int pow2(int a); inline void swap(int& a, int& b); @@ -125,43 +131,73 @@ inline size_t max(size_t a, size_t b); // ----------------------------------------------------------------------------- namespace yocto { +// Fixed-size vectors declaration +struct vec2f; +struct vec3f; +struct vec4f; +struct vec2i; +struct vec3i; +struct vec4i; + +// Fixed-size vectors struct vec2f { float x = 0; float y = 0; + constexpr vec2f() : x{0}, y{0} {} + constexpr vec2f(float x_, float y_) : x{x_}, y{y_} {} + constexpr explicit vec2f(float v) : x{v}, y{v} {} + + inline vec2f(const vec2i& v); + inline explicit operator vec2i() const; + inline float& operator[](int i); inline const float& operator[](int i) const; }; +// Fixed-size vectors struct vec3f { float x = 0; float y = 0; float z = 0; + constexpr vec3f() : x{0}, y{0}, z{0} {} + constexpr vec3f(float x_, float y_, float z_) : x{x_}, y{y_}, z{z_} {} + constexpr vec3f(vec2f v, float z_) : x{v.x}, y{v.y}, z{z_} {} + constexpr explicit vec3f(float v) : x{v}, y{v}, z{v} {} + + inline vec3f(const vec3i& v); + inline explicit operator vec3i() const; + inline float& operator[](int i); inline const float& operator[](int i) const; }; +// Fixed-size vectors struct vec4f { float x = 0; float y = 0; float z = 0; float w = 0; + constexpr vec4f() : x{0}, y{0}, z{0}, w{0} {} + constexpr vec4f(float x_, float y_, float z_, float w_) + : x{x_}, y{y_}, z{z_}, w{w_} {} + constexpr vec4f(vec3f v, float w_) : x{v.x}, y{v.y}, z{v.z}, w{w_} {} + constexpr explicit vec4f(float v) : x{v}, y{v}, z{v}, w{v} {} + + inline vec4f(const vec4i& v); + inline explicit operator vec4i() const; + inline float& operator[](int i); inline const float& operator[](int i) const; }; -// Zero vector constants. +// Constants constexpr auto zero2f = vec2f{0, 0}; constexpr auto zero3f = vec3f{0, 0, 0}; constexpr auto zero4f = vec4f{0, 0, 0, 0}; -// One vector constants. -constexpr auto one2f = vec2f{1, 1}; -constexpr auto one3f = vec3f{1, 1, 1}; -constexpr auto one4f = vec4f{1, 1, 1, 1}; - // Element access inline vec3f xyz(const vec4f& a); @@ -240,6 +276,10 @@ inline vec2f log2(const vec2f& a); inline bool isfinite(const vec2f& a); inline vec2f pow(const vec2f& a, float b); inline vec2f pow(const vec2f& a, const vec2f& b); +inline vec2f fmod(vec2f a, vec2f b); +inline vec2f floor(vec2f a); +inline vec2f ceil(vec2f a); +inline vec2f round(vec2f a); inline vec2f gain(const vec2f& a, float b); inline void swap(vec2f& a, vec2f& b); @@ -325,6 +365,10 @@ inline vec3f exp2(const vec3f& a); inline vec3f log2(const vec3f& a); inline vec3f pow(const vec3f& a, float b); inline vec3f pow(const vec3f& a, const vec3f& b); +inline vec3f fmod(vec3f a, vec3f b); +inline vec3f floor(vec3f a); +inline vec3f ceil(vec3f a); +inline vec3f round(vec3f a); inline vec3f gain(const vec3f& a, float b); inline bool isfinite(const vec3f& a); inline void swap(vec3f& a, vec3f& b); @@ -403,6 +447,10 @@ inline vec4f exp2(const vec4f& a); inline vec4f log2(const vec4f& a); inline vec4f pow(const vec4f& a, float b); inline vec4f pow(const vec4f& a, const vec4f& b); +inline vec4f fmod(vec4f a, vec4f b); +inline vec4f floor(vec4f a); +inline vec4f ceil(vec4f a); +inline vec4f round(vec4f a); inline vec4f gain(const vec4f& a, float b); inline bool isfinite(const vec4f& a); inline void swap(vec4f& a, vec4f& b); @@ -425,6 +473,12 @@ struct vec2i { int x = 0; int y = 0; + constexpr vec2i() : x{0}, y{0} {} + constexpr vec2i(int x_, int y_) : x{x_}, y{y_} {} + constexpr explicit vec2i(int v) : x{v}, y{v} {} + + inline explicit operator vec2f() const; + inline int& operator[](int i); inline const int& operator[](int i) const; }; @@ -434,6 +488,12 @@ struct vec3i { int y = 0; int z = 0; + constexpr vec3i() : x{0}, y{0}, z{0} {} + constexpr vec3i(int x_, int y_, int z_) : x{x_}, y{y_}, z{z_} {} + constexpr explicit vec3i(int v) : x{v}, y{v}, z{v} {} + + inline explicit operator vec3f() const; + inline int& operator[](int i); inline const int& operator[](int i) const; }; @@ -444,24 +504,48 @@ struct vec4i { int z = 0; int w = 0; + constexpr vec4i() : x{0}, y{0}, z{0}, w{0} {} + constexpr vec4i(int x_, int y_, int z_, int w_) + : x{x_}, y{y_}, z{z_}, w{w_} {} + constexpr explicit vec4i(int v) : x{v}, y{v}, z{v}, w{v} {} + + inline explicit operator vec4f() const; + inline int& operator[](int i); inline const int& operator[](int i) const; }; +struct vec3b { + byte x = 0; + byte y = 0; + byte z = 0; + + constexpr vec3b() : x{0}, y{0}, z{0} {} + constexpr vec3b(byte x_, byte y_, byte z_) : x{x_}, y{y_}, z{z_} {} + + inline byte& operator[](int i); + inline const byte& operator[](int i) const; +}; + struct vec4b { byte x = 0; byte y = 0; byte z = 0; byte w = 0; + constexpr vec4b() : x{0}, y{0}, z{0}, w{0} {} + constexpr vec4b(byte x_, byte y_, byte z_, byte w_) + : x{x_}, y{y_}, z{z_}, w{w_} {} + inline byte& operator[](int i); inline const byte& operator[](int i) const; }; -// Zero vector constants. +// Constants constexpr auto zero2i = vec2i{0, 0}; constexpr auto zero3i = vec3i{0, 0, 0}; constexpr auto zero4i = vec4i{0, 0, 0, 0}; +constexpr auto zero3b = vec3b{0, 0, 0}; constexpr auto zero4b = vec4b{0, 0, 0, 0}; // Element access @@ -495,6 +579,8 @@ inline vec2i operator*(int a, const vec2i& b); inline vec2i operator/(const vec2i& a, const vec2i& b); inline vec2i operator/(const vec2i& a, int b); inline vec2i operator/(int a, const vec2i& b); +inline vec2i operator%(const vec2i& a, const vec2i& b); +inline vec2i operator%(const vec2i& a, int b); // Vector assignments inline vec2i& operator+=(vec2i& a, const vec2i& b); @@ -549,6 +635,8 @@ inline vec3i operator*(int a, const vec3i& b); inline vec3i operator/(const vec3i& a, const vec3i& b); inline vec3i operator/(const vec3i& a, int b); inline vec3i operator/(int a, const vec3i& b); +inline vec3i operator%(const vec3i& a, const vec3i& b); +inline vec3i operator%(const vec3i& a, int b); // Vector assignments inline vec3i& operator+=(vec3i& a, const vec3i& b); @@ -603,6 +691,8 @@ inline vec4i operator*(int a, const vec4i& b); inline vec4i operator/(const vec4i& a, const vec4i& b); inline vec4i operator/(const vec4i& a, int b); inline vec4i operator/(int a, const vec4i& b); +inline vec4i operator%(const vec4i& a, const vec4i& b); +inline vec4i operator%(const vec4i& a, int b); // Vector assignments inline vec4i& operator+=(vec4i& a, const vec4i& b); @@ -645,6 +735,9 @@ struct mat2f { vec2f x = {1, 0}; vec2f y = {0, 1}; + constexpr mat2f() : x{1, 00}, y{0, 1} {} + constexpr mat2f(vec2f x_, vec2f y_) : x{x_}, y{y_} {} + inline vec2f& operator[](int i); inline const vec2f& operator[](int i) const; }; @@ -655,6 +748,9 @@ struct mat3f { vec3f y = {0, 1, 0}; vec3f z = {0, 0, 1}; + constexpr mat3f() : x{1, 0, 0}, y{0, 1, 0}, z{0, 0, 1} {} + constexpr mat3f(vec3f x_, vec3f y_, vec3f z_) : x{x_}, y{y_}, z{z_} {} + inline vec3f& operator[](int i); inline const vec3f& operator[](int i) const; }; @@ -666,6 +762,11 @@ struct mat4f { vec4f z = {0, 0, 1, 0}; vec4f w = {0, 0, 0, 1}; + constexpr mat4f() + : x{1, 0, 0, 0}, y{0, 1, 0, 0}, z{0, 0, 1, 0}, w{0, 0, 0, 1} {} + constexpr mat4f(vec4f x_, vec4f y_, vec4f z_, vec4f w_) + : x{x_}, y{y_}, z{z_}, w{w_} {} + inline vec4f& operator[](int i); inline const vec4f& operator[](int i) const; }; @@ -762,6 +863,10 @@ struct frame2f { vec2f y = {0, 1}; vec2f o = {0, 0}; + constexpr frame2f() : x{1, 0}, y{0, 1}, o{0, 0} {} + constexpr frame2f(vec2f x_, vec2f y_, vec2f o_) : x{x_}, y{y_}, o{o_} {} + constexpr frame2f(const mat2f& m_, vec2f t_) : x{m_.x}, y{m_.y}, o{t_} {} + inline vec2f& operator[](int i); inline const vec2f& operator[](int i) const; }; @@ -773,11 +878,17 @@ struct frame3f { vec3f z = {0, 0, 1}; vec3f o = {0, 0, 0}; + constexpr frame3f() : x{1, 0, 0}, y{0, 1, 0}, z{0, 0, 1}, o{0, 0, 0} {} + constexpr frame3f(vec3f x_, vec3f y_, vec3f z_, vec3f o_) + : x{x_}, y{y_}, z{z_}, o{o_} {} + constexpr frame3f(const mat3f& m_, vec3f t_) + : x{m_.x}, y{m_.y}, z{m_.z}, o{t_} {} + inline vec3f& operator[](int i); inline const vec3f& operator[](int i) const; }; -// Identity frames. +// Indentity frames. constexpr auto identity2x3f = frame2f{{1, 0}, {0, 1}, {0, 0}}; constexpr auto identity3x4f = frame3f{ {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {0, 0, 0}}; @@ -843,18 +954,22 @@ struct quat4f { float y = 0; float z = 0; float w = 1; + + constexpr quat4f() : x{0}, y{0}, z{0}, w{1} {} + constexpr quat4f(float x_, float y_, float z_, float w_) + : x{x_}, y{y_}, z{z_}, w{w_} {} }; // Constants constexpr auto identity_quat4f = quat4f{0, 0, 0, 1}; -// Quaternion operatons +// Quaternion operations inline quat4f operator+(const quat4f& a, const quat4f& b); inline quat4f operator*(const quat4f& a, float b); inline quat4f operator/(const quat4f& a, float b); inline quat4f operator*(const quat4f& a, const quat4f& b); -// Quaterion operations +// Quaternion operations inline float dot(const quat4f& a, const quat4f& b); inline float length(const quat4f& a); inline quat4f normalize(const quat4f& a); @@ -912,6 +1027,12 @@ inline vec3f transform_point_inverse(const frame3f& a, const vec3f& b); inline vec3f transform_vector_inverse(const frame3f& a, const vec3f& b); inline vec3f transform_direction_inverse(const frame3f& a, const vec3f& b); +// Translation, scaling and rotations transforms. +inline frame2f translation_frame(const vec2f& a); +inline frame2f scaling_frame(const vec2f& a); +inline frame2f rotation_frame(float angle); +inline frame2f rotation_frame(const mat2f& rot); + // Translation, scaling and rotations transforms. inline frame3f translation_frame(const vec3f& a); inline frame3f scaling_frame(const vec3f& a); @@ -1001,6 +1122,12 @@ constexpr auto range(T min, T max); template constexpr auto range(T min, T max, T step); +// Python range. Construct an object that iterates over an integer sequence. +constexpr auto range(vec2i max); +constexpr auto range(vec3i max); +template +constexpr auto range(array max); + // Python enumerate template constexpr auto enumerate(const Sequence& sequence, T start = 0); @@ -1069,6 +1196,13 @@ inline bool isfinite(float a) { return ::isfinite(a); } #endif inline float atan2(float a, float b) { return std::atan2(a, b); } inline float fmod(float a, float b) { return std::fmod(a, b); } +inline float mod(float a, float b) { + auto m = fmod(a, b); + return (m >= 0) ? m : m + b; +} +inline float floor(float a) { return std::floor(a); } +inline float ceil(float a) { return std::ceil(a); } +inline float round(float a) { return std::round(a); } inline void swap(float& a, float& b) { std::swap(a, b); } inline float radians(float a) { return a * pif / 180; } inline float degrees(float a) { return a * 180 / pif; } @@ -1086,10 +1220,14 @@ inline float gain(float a, float gain) { : bias(a * 2 - 1, 1 - gain) / 2 + 0.5f; } -inline int abs(int a) { return a < 0 ? -a : a; } -inline int min(int a, int b) { return (a < b) ? a : b; } -inline int max(int a, int b) { return (a > b) ? a : b; } -inline int clamp(int a, int min_, int max_) { return min(max(a, min_), max_); } +inline int abs(int a) { return a < 0 ? -a : a; } +inline int min(int a, int b) { return (a < b) ? a : b; } +inline int max(int a, int b) { return (a > b) ? a : b; } +inline int clamp(int a, int min_, int max_) { return min(max(a, min_), max_); } +inline int mod(int a, int b) { + auto m = a % b; + return (m >= 0) ? m : m + b; +} inline int sign(int a) { return a < 0 ? -1 : 1; } inline int pow2(int a) { return 1 << a; } inline void swap(int& a, int& b) { std::swap(a, b); } @@ -1105,15 +1243,25 @@ inline size_t max(size_t a, size_t b) { return (a > b) ? a : b; } namespace yocto { // Vec2 -inline float& vec2f::operator[](int i) { return (&x)[i]; } +inline vec2f::vec2f(const vec2i& v) : x{(float)v.x}, y{(float)v.y} {} +inline vec2f::operator vec2i() const { return {(int)x, (int)y}; } +inline float& vec2f::operator[](int i) { return (&x)[i]; } inline const float& vec2f::operator[](int i) const { return (&x)[i]; } // Vec3 -inline float& vec3f::operator[](int i) { return (&x)[i]; } +inline vec3f::vec3f(const vec3i& v) + : x{(float)v.x}, y{(float)v.y}, z{(float)v.z} {} +inline vec3f::operator vec3i() const { return {(int)x, (int)y, (int)z}; } +inline float& vec3f::operator[](int i) { return (&x)[i]; } inline const float& vec3f::operator[](int i) const { return (&x)[i]; } // Vec4 -inline float& vec4f::operator[](int i) { return (&x)[i]; } +inline vec4f::vec4f(const vec4i& v) + : x{(float)v.x}, y{(float)v.y}, z{(float)v.z}, w{(float)v.w} {} +inline vec4f::operator vec4i() const { + return {(int)x, (int)y, (int)z, (int)w}; +} +inline float& vec4f::operator[](int i) { return (&x)[i]; } inline const float& vec4f::operator[](int i) const { return (&x)[i]; } // Element access @@ -1204,6 +1352,9 @@ inline vec2f min(const vec2f& a, const vec2f& b) { inline vec2f clamp(const vec2f& x, float min, float max) { return {clamp(x.x, min, max), clamp(x.y, min, max)}; } +inline vec2f clamp(const vec2f& x, const vec2f& min, const vec2f& max) { + return {clamp(x.x, min.x, max.x), clamp(x.y, min.y, max.y)}; +} inline vec2f lerp(const vec2f& a, const vec2f& b, float u) { return a * (1 - u) + b * u; } @@ -1229,6 +1380,12 @@ inline vec2f pow(const vec2f& a, float b) { return {pow(a.x, b), pow(a.y, b)}; } inline vec2f pow(const vec2f& a, const vec2f& b) { return {pow(a.x, b.x), pow(a.y, b.y)}; } +inline vec2f fmod(vec2f a, vec2f b) { return {fmod(a.x, b.x), fmod(a.y, b.y)}; } +inline vec2f mod(vec2f a, vec2f b) { return {mod(a.x, b.x), mod(a.y, b.y)}; } +inline vec2f mod(vec2f a, float b) { return {mod(a.x, b), mod(a.y, b)}; } +inline vec2f floor(vec2f a) { return {floor(a.x), floor(a.y)}; } +inline vec2f ceil(vec2f a) { return {ceil(a.x), ceil(a.y)}; } +inline vec2f round(vec2f a) { return {round(a.x), round(a.y)}; } inline vec2f gain(const vec2f& a, float b) { return {gain(a.x, b), gain(a.y, b)}; } @@ -1359,6 +1516,10 @@ inline vec3f min(const vec3f& a, const vec3f& b) { inline vec3f clamp(const vec3f& x, float min, float max) { return {clamp(x.x, min, max), clamp(x.y, min, max), clamp(x.z, min, max)}; } +inline vec3f clamp(const vec3f& x, const vec3f& min, const vec3f& max) { + return {clamp(x.x, min.x, max.x), clamp(x.y, min.y, max.y), + clamp(x.z, min.z, max.z)}; +} inline vec3f lerp(const vec3f& a, const vec3f& b, float u) { return a * (1 - u) + b * u; } @@ -1385,6 +1546,18 @@ inline vec3f pow(const vec3f& a, float b) { inline vec3f pow(const vec3f& a, const vec3f& b) { return {pow(a.x, b.x), pow(a.y, b.y), pow(a.z, b.z)}; } +inline vec3f fmod(vec3f a, vec3f b) { + return {fmod(a.x, b.x), fmod(a.y, b.y), fmod(a.z, b.z)}; +} +inline vec3f mod(vec3f a, vec3f b) { + return {mod(a.x, b.x), mod(a.y, b.y), mod(a.z, b.z)}; +} +inline vec3f mod(vec3f a, float b) { + return {mod(a.x, b), mod(a.y, b), mod(a.z, b)}; +} +inline vec3f floor(vec3f a) { return {floor(a.x), floor(a.y), floor(a.z)}; } +inline vec3f ceil(vec3f a) { return {ceil(a.x), ceil(a.y), ceil(a.z)}; } +inline vec3f round(vec3f a) { return {round(a.x), round(a.y), round(a.z)}; } inline vec3f gain(const vec3f& a, float b) { return {gain(a.x, b), gain(a.y, b), gain(a.z, b)}; } @@ -1509,6 +1682,10 @@ inline vec4f clamp(const vec4f& x, float min, float max) { return {clamp(x.x, min, max), clamp(x.y, min, max), clamp(x.z, min, max), clamp(x.w, min, max)}; } +inline vec4f clamp(const vec4f& x, const vec4f& min, const vec4f& max) { + return {clamp(x.x, min.x, max.x), clamp(x.y, min.y, max.y), + clamp(x.z, min.z, max.z), clamp(x.w, min.w, max.w)}; +} inline vec4f lerp(const vec4f& a, const vec4f& b, float u) { return a * (1 - u) + b * u; } @@ -1549,6 +1726,21 @@ inline vec4f pow(const vec4f& a, float b) { inline vec4f pow(const vec4f& a, const vec4f& b) { return {pow(a.x, b.x), pow(a.y, b.y), pow(a.z, b.z), pow(a.w, b.w)}; } +inline vec4f fmod(vec4f a, vec4f b) { + return {fmod(a.x, b.x), fmod(a.y, b.y), fmod(a.z, b.z), fmod(a.w, b.w)}; +} +inline vec4f mod(vec4f a, float b) { + return {mod(a.x, b), mod(a.y, b), mod(a.z, b), mod(a.w, b)}; +} +inline vec4f floor(vec4f a) { + return {floor(a.x), floor(a.y), floor(a.z), floor(a.w)}; +} +inline vec4f ceil(vec4f a) { + return {ceil(a.x), ceil(a.y), ceil(a.z), ceil(a.w)}; +} +inline vec4f round(vec4f a) { + return {round(a.x), round(a.y), round(a.z), round(a.w)}; +} inline vec4f gain(const vec4f& a, float b) { return {gain(a.x, b), gain(a.y, b), gain(a.z, b), gain(a.w, b)}; } @@ -1581,19 +1773,26 @@ inline vec4f quat_inverse(const vec4f& a) { namespace yocto { // Vector data types -inline int& vec2i::operator[](int i) { return (&x)[i]; } +inline vec2i::operator yocto::vec2f() const { return {(float)x, (float)y}; } +inline int& vec2i::operator[](int i) { return (&x)[i]; } inline const int& vec2i::operator[](int i) const { return (&x)[i]; } // Vector data types -inline int& vec3i::operator[](int i) { return (&x)[i]; } +inline vec3i::operator yocto::vec3f() const { + return {(float)x, (float)y, (float)z}; +} +inline int& vec3i::operator[](int i) { return (&x)[i]; } inline const int& vec3i::operator[](int i) const { return (&x)[i]; } // Vector data types -inline int& vec4i::operator[](int i) { return (&x)[i]; } +inline vec4i::operator yocto::vec4f() const { + return {(float)x, (float)y, (float)z, (float)w}; +} +inline int& vec4i::operator[](int i) { return (&x)[i]; } inline const int& vec4i::operator[](int i) const { return (&x)[i]; } // Vector data types -inline byte& vec4b::operator[](int i) { return (&x)[i]; } +inline byte& vec4b::operator[](int i) { return (&x)[i]; } inline const byte& vec4b::operator[](int i) const { return (&x)[i]; } // Element access @@ -1639,6 +1838,10 @@ inline vec2i operator/(const vec2i& a, const vec2i& b) { } inline vec2i operator/(const vec2i& a, int b) { return {a.x / b, a.y / b}; } inline vec2i operator/(int a, const vec2i& b) { return {a / b.x, a / b.y}; } +inline vec2i operator%(const vec2i& a, const vec2i& b) { + return {a.x % b.x, a.y % b.y}; +} +inline vec2i operator%(const vec2i& a, int b) { return {a.x % b, a.y % b}; } // Vector assignments inline vec2i& operator+=(vec2i& a, const vec2i& b) { return a = a + b; } @@ -1662,10 +1865,14 @@ inline vec2i min(const vec2i& a, const vec2i& b) { inline vec2i clamp(const vec2i& x, int min, int max) { return {clamp(x.x, min, max), clamp(x.y, min, max)}; } - -inline int max(const vec2i& a) { return max(a.x, a.y); } -inline int min(const vec2i& a) { return min(a.x, a.y); } -inline int sum(const vec2i& a) { return a.x + a.y; } +inline vec2i clamp(const vec2i& x, const vec2i& min, const vec2i& max) { + return {clamp(x.x, min.x, max.x), clamp(x.y, min.y, max.y)}; +} +inline vec2i mod(vec2i a, vec2i b) { return {mod(a.x, b.x), mod(a.y, b.y)}; } +inline vec2i mod(vec2i a, int b) { return {mod(a.x, b), mod(a.y, b)}; } +inline int max(const vec2i& a) { return max(a.x, a.y); } +inline int min(const vec2i& a) { return min(a.x, a.y); } +inline int sum(const vec2i& a) { return a.x + a.y; } // Functions applied to vector elements inline vec2i abs(const vec2i& a) { return {abs(a.x), abs(a.y)}; } @@ -1727,6 +1934,12 @@ inline vec3i operator/(const vec3i& a, int b) { inline vec3i operator/(int a, const vec3i& b) { return {a / b.x, a / b.y, a / b.z}; } +inline vec3i operator%(const vec3i& a, const vec3i& b) { + return {a.x % b.x, a.y % b.y, a.z % b.z}; +} +inline vec3i operator%(const vec3i& a, int b) { + return {a.x % b, a.y % b, a.z % b}; +} // Vector assignments inline vec3i& operator+=(vec3i& a, const vec3i& b) { return a = a + b; } @@ -1754,6 +1967,16 @@ inline vec3i min(const vec3i& a, const vec3i& b) { inline vec3i clamp(const vec3i& x, int min, int max) { return {clamp(x.x, min, max), clamp(x.y, min, max), clamp(x.z, min, max)}; } +inline vec3i clamp(const vec3i& x, const vec3i& min, const vec3i& max) { + return {clamp(x.x, min.x, max.x), clamp(x.y, min.y, max.y), + clamp(x.z, min.z, max.z)}; +} +inline vec3i mod(vec3i a, vec3i b) { + return {mod(a.x, b.x), mod(a.y, b.y), mod(a.z, b.z)}; +} +inline vec3i mod(vec3i a, int b) { + return {mod(a.x, b), mod(a.y, b), mod(a.z, b)}; +} inline int max(const vec3i& a) { return max(max(a.x, a.y), a.z); } inline int min(const vec3i& a) { return min(min(a.x, a.y), a.z); } @@ -1819,6 +2042,12 @@ inline vec4i operator/(const vec4i& a, int b) { inline vec4i operator/(int a, const vec4i& b) { return {a / b.x, a / b.y, a / b.z, a / b.w}; } +inline vec4i operator%(const vec4i& a, const vec4i& b) { + return {a.x % b.x, a.y % b.y, a.z % b.z, a.w % b.w}; +} +inline vec4i operator%(const vec4i& a, int b) { + return {a.x % b, a.y % b, a.z % b, a.w % b}; +} // Vector assignments inline vec4i& operator+=(vec4i& a, const vec4i& b) { return a = a + b; } @@ -1847,6 +2076,16 @@ inline vec4i clamp(const vec4i& x, int min, int max) { return {clamp(x.x, min, max), clamp(x.y, min, max), clamp(x.z, min, max), clamp(x.w, min, max)}; } +inline vec4i clamp(const vec4i& x, const vec4i& min, const vec4i& max) { + return {clamp(x.x, min.x, max.x), clamp(x.y, min.y, max.y), + clamp(x.z, min.z, max.z), clamp(x.w, min.w, max.w)}; +} +inline vec4i mod(vec4i a, vec4i b) { + return {mod(a.x, b.x), mod(a.y, b.y), mod(a.z, b.z), mod(a.w, b.w)}; +} +inline vec4i mod(vec4i a, int b) { + return {mod(a.x, b), mod(a.y, b), mod(a.z, b), mod(a.w, b)}; +} inline int max(const vec4i& a) { return max(max(max(a.x, a.y), a.z), a.w); } inline int min(const vec4i& a) { return min(min(min(a.x, a.y), a.z), a.w); } @@ -1874,15 +2113,15 @@ inline bool operator!=(const vec4b& a, const vec4b& b) { namespace yocto { // Small Fixed-size matrices stored in column major format. -inline vec2f& mat2f::operator[](int i) { return (&x)[i]; } +inline vec2f& mat2f::operator[](int i) { return (&x)[i]; } inline const vec2f& mat2f::operator[](int i) const { return (&x)[i]; } // Small Fixed-size matrices stored in column major format. -inline vec3f& mat3f::operator[](int i) { return (&x)[i]; } +inline vec3f& mat3f::operator[](int i) { return (&x)[i]; } inline const vec3f& mat3f::operator[](int i) const { return (&x)[i]; } // Small Fixed-size matrices stored in column major format. -inline vec4f& mat4f::operator[](int i) { return (&x)[i]; } +inline vec4f& mat4f::operator[](int i) { return (&x)[i]; } inline const vec4f& mat4f::operator[](int i) const { return (&x)[i]; } // Matrix comparisons. @@ -2032,11 +2271,11 @@ inline mat4f transpose(const mat4f& a) { namespace yocto { // Rigid frames stored as a column-major affine transform matrix. -inline vec2f& frame2f::operator[](int i) { return (&x)[i]; } +inline vec2f& frame2f::operator[](int i) { return (&x)[i]; } inline const vec2f& frame2f::operator[](int i) const { return (&x)[i]; } // Rigid frames stored as a column-major affine transform matrix. -inline vec3f& frame3f::operator[](int i) { return (&x)[i]; } +inline vec3f& frame3f::operator[](int i) { return (&x)[i]; } inline const vec3f& frame3f::operator[](int i) const { return (&x)[i]; } // Frame properties @@ -2175,8 +2414,8 @@ inline quat4f normalize(const quat4f& a) { inline quat4f conjugate(const quat4f& a) { return {-a.x, -a.y, -a.z, a.w}; } inline quat4f inverse(const quat4f& a) { return conjugate(a) / dot(a, a); } inline float uangle(const quat4f& a, const quat4f& b) { - auto d = dot(a, b); - return d > 1 ? 0 : acos(d < -1 ? -1 : d); + auto d = dot(a, b); + return d > 1 ? 0 : acos(d < -1 ? -1 : d); } inline quat4f lerp(const quat4f& a, const quat4f& b, float t) { return a * (1 - t) + b * t; @@ -2300,6 +2539,19 @@ inline vec3f transform_direction_inverse(const frame3f& a, const vec3f& b) { return normalize(transform_vector_inverse(a, b)); } +// Translation, scaling and rotations transforms. +inline frame2f translation_frame(const vec2f& a) { return {{1, 0}, {0, 1}, a}; } +inline frame2f scaling_frame(const vec2f& a) { + return {{a.x, 0}, {0, a.y}, {0, 0}}; +} +inline frame2f rotation_frame(float angle) { + auto s = sin(angle), c = cos(angle); + return {{c, s}, {-s, c}, {0, 0}}; +} +inline frame2f rotation_frame(const mat2f& rot) { + return {rot.x, rot.y, {0, 0}}; +} + // Translation, scaling and rotations transforms. inline frame3f translation_frame(const vec3f& a) { return {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}, a}; @@ -2675,6 +2927,115 @@ constexpr auto range(T min, T max, T step) { return range_helper{min, max, step}; } +// Python `range()` equivalent. Construct an object to iterate over a sequence. +template +constexpr auto range(array max) { + struct range_iterator { + array index, end; + void operator++() { + ++index[0]; + for (auto i = (size_t)0; i < N - 1; i++) { + if (index[i] >= end[i]) { + index[i] = 0; + index[i + 1]++; + } + } + } + bool operator==(const range_iterator& other) const { + return index[N - 1] == other.index[N - 1]; + } + array operator*() const { return index; } + }; + struct range_helper { + array end_ = zero_(); + range_iterator begin() const { return {zero_(), end_}; } + range_iterator end() const { return {end_, end_}; } + static constexpr array zero_() { + array zero = {0}; + for (auto idx = 0; idx < N; idx++) zero[idx] = 0; + return zero; + } + }; + return range_helper{max}; +} +constexpr auto range(vec2i max) { + struct range_iterator { + vec2i index, end; + void operator++() { + ++index.x; + if (index.x >= end.x) { + index.x = 0; + index.y++; + } + } + bool operator==(const range_iterator& other) const { + return index.y == other.index.y; + } + vec2i operator*() const { return index; } + }; + struct range_helper { + vec2i end_ = {0, 0}; + range_iterator begin() const { return {{0, 0}, end_}; } + range_iterator end() const { return {end_, end_}; } + }; + return range_helper{max}; +} + +// Python `range()` equivalent. Construct an object to iterate over a sequence. +template +constexpr auto range(array max) { + struct range_iterator { + array index, end; + void operator++() { + ++index[0]; + if (index[0] >= end[0]) { + index[0] = 0; + index[1]++; + } + if (index[1] >= end[1]) { + index[1] = 0; + index[2]++; + } + } + bool operator==(const range_iterator& other) const { + return index[2] == other.index[2]; + } + array operator*() const { return index; } + }; + struct range_helper { + array end_ = {0, 0, 0}; + range_iterator begin() const { return {{0, 0, 0}, end_}; } + range_iterator end() const { return {end_, end_}; } + }; + return range_helper{max}; +} +constexpr auto range(vec3i max) { + struct range_iterator { + vec3i index, end; + void operator++() { + ++index.x; + if (index.x >= end.x) { + index.x = 0; + index.y++; + } + if (index.y >= end.y) { + index.y = 0; + index.z++; + } + } + bool operator==(const range_iterator& other) const { + return index.z == other.index.z; + } + vec3i operator*() const { return index; } + }; + struct range_helper { + vec3i end_ = {0, 0, 0}; + range_iterator begin() const { return {{0, 0, 0}, end_}; } + range_iterator end() const { return {end_, end_}; } + }; + return range_helper{max}; +} + // Python enumerate template constexpr auto enumerate(const Sequence& sequence, T start) { @@ -2684,13 +3045,13 @@ constexpr auto enumerate(const Sequence& sequence, T start) { T index; Iterator iterator; bool operator!=(const enumerate_iterator& other) const { - return index != other.index; + return index != other.index; } void operator++() { ++index; ++iterator; } - pair operator*() const { return {index, *iterator}; } + pair operator*() const { return {index, *iterator}; } }; struct enumerate_helper { const Sequence& sequence; @@ -2710,13 +3071,13 @@ constexpr auto enumerate(Sequence& sequence, T start) { T index; Iterator iterator; bool operator!=(const enumerate_iterator& other) const { - return index != other.index; + return index != other.index; } void operator++() { ++index; ++iterator; } - pair operator*() const { return {index, *iterator}; } + pair operator*() const { return {index, *iterator}; } }; struct enumerate_helper { Sequence& sequence; @@ -2738,7 +3099,7 @@ constexpr auto zip(const Sequence1& sequence1, const Sequence2& sequence2) { Iterator1 iterator1; Iterator2 iterator2; bool operator!=(const zip_iterator& other) const { - return iterator1 != other.iterator1; + return iterator1 != other.iterator1; } void operator++() { ++iterator1; @@ -2752,7 +3113,7 @@ constexpr auto zip(const Sequence1& sequence1, const Sequence2& sequence2) { const Sequence1& sequence1; const Sequence2& sequence2; auto begin() { - return zip_iterator{std::begin(sequence1), std::begin(sequence2)}; + return zip_iterator{std::begin(sequence1), std::begin(sequence2)}; } auto end() { return zip_iterator{std::end(sequence1), std::end(sequence2)}; @@ -2772,7 +3133,7 @@ constexpr auto zip(Sequence1& sequence1, Sequence2& sequence2) { Iterator1 iterator1; Iterator2 iterator2; bool operator!=(const zip_iterator& other) const { - return iterator1 != other.iterator1; + return iterator1 != other.iterator1; } void operator++() { ++iterator1; @@ -2786,7 +3147,7 @@ constexpr auto zip(Sequence1& sequence1, Sequence2& sequence2) { Sequence1& sequence1; Sequence2& sequence2; auto begin() { - return zip_iterator{std::begin(sequence1), std::begin(sequence2)}; + return zip_iterator{std::begin(sequence1), std::begin(sequence2)}; } auto end() { return zip_iterator{std::end(sequence1), std::end(sequence2)}; @@ -2806,7 +3167,7 @@ constexpr auto zip(const Sequence1& sequence1, Sequence2& sequence2) { Iterator1 iterator1; Iterator2 iterator2; bool operator!=(const zip_iterator& other) const { - return iterator1 != other.iterator1; + return iterator1 != other.iterator1; } void operator++() { ++iterator1; @@ -2820,7 +3181,7 @@ constexpr auto zip(const Sequence1& sequence1, Sequence2& sequence2) { const Sequence1& sequence1; Sequence2& sequence2; auto begin() { - return zip_iterator{std::begin(sequence1), std::begin(sequence2)}; + return zip_iterator{std::begin(sequence1), std::begin(sequence2)}; } auto end() { return zip_iterator{std::end(sequence1), std::end(sequence2)}; @@ -2840,7 +3201,7 @@ constexpr auto zip(Sequence1& sequence1, const Sequence2& sequence2) { Iterator1 iterator1; Iterator2 iterator2; bool operator!=(const zip_iterator& other) const { - return iterator1 != other.iterator1; + return iterator1 != other.iterator1; } void operator++() { ++iterator1; @@ -2854,7 +3215,7 @@ constexpr auto zip(Sequence1& sequence1, const Sequence2& sequence2) { Sequence1& sequence1; const Sequence2& sequence2; auto begin() { - return zip_iterator{std::begin(sequence1), std::begin(sequence2)}; + return zip_iterator{std::begin(sequence1), std::begin(sequence2)}; } auto end() { return zip_iterator{std::end(sequence1), std::end(sequence2)}; diff --git a/libs/yocto/yocto_modelio.cpp b/libs/yocto/yocto_modelio.cpp index bf6fa7f4e..504480d1d 100644 --- a/libs/yocto/yocto_modelio.cpp +++ b/libs/yocto/yocto_modelio.cpp @@ -53,15 +53,12 @@ namespace yocto { // using directives -using std::pair; using std::string_view; using std::unordered_map; using std::unordered_set; using namespace std::string_literals; using namespace std::string_view_literals; using byte = unsigned char; -using std::cos; -using std::sin; } // namespace yocto @@ -86,14 +83,14 @@ static FILE* fopen_utf8(const char* filename, const char* mode) { static bool load_text(const string& filename, string& str, string& error) { // https://stackoverflow.com/questions/174531/how-to-read-the-content-of-a-file-to-a-string-in-c auto fs = fopen_utf8(filename.c_str(), "rb"); - if (!fs) { + if (fs == nullptr) { error = "cannot open " + filename; return false; } fseek(fs, 0, SEEK_END); auto length = ftell(fs); fseek(fs, 0, SEEK_SET); - str.resize(length); + str = string(length, '\0'); if (fread(str.data(), 1, length, fs) != length) { fclose(fs); error = "cannot read " + filename; @@ -107,7 +104,7 @@ static bool load_text(const string& filename, string& str, string& error) { static bool save_text( const string& filename, const string& str, string& error) { auto fs = fopen_utf8(filename.c_str(), "wt"); - if (!fs) { + if (fs == nullptr) { error = "cannot create " + filename; return false; } @@ -125,14 +122,14 @@ static bool load_binary( const string& filename, vector& data, string& error) { // https://stackoverflow.com/questions/174531/how-to-read-the-content-of-a-file-to-a-string-in-c auto fs = fopen_utf8(filename.c_str(), "rb"); - if (!fs) { + if (fs == nullptr) { error = "cannot open " + filename; return false; } fseek(fs, 0, SEEK_END); auto length = ftell(fs); fseek(fs, 0, SEEK_SET); - data.resize(length); + data = vector(length); if (fread(data.data(), 1, length, fs) != length) { fclose(fs); error = "cannot read " + filename; @@ -146,7 +143,7 @@ static bool load_binary( static bool save_binary( const string& filename, const vector& data, string& error) { auto fs = fopen_utf8(filename.c_str(), "wb"); - if (!fs) { + if (fs == nullptr) { error = "cannot create " + filename; return false; } @@ -167,33 +164,40 @@ static bool save_binary( namespace yocto { // Make a path from a utf8 string -static std::filesystem::path make_path(const string& filename) { - return std::filesystem::u8path(filename); +static std::filesystem::path to_path(const string& filename) { + auto filename8 = std::u8string((char8_t*)filename.data(), filename.size()); + return std::filesystem::path(filename8); +} + +// Make a utf8 string from a path +static string to_string(const std::filesystem::path& path) { + auto string8 = path.u8string(); + return string((char*)string8.data(), string8.size()); } // Get directory name (not including /) static string path_dirname(const string& filename) { - return make_path(filename).parent_path().generic_u8string(); + return to_string(to_path(filename).parent_path()); } // Get filename without directory. static string path_filename(const string& filename) { - return make_path(filename).filename().generic_u8string(); + return to_string(to_path(filename).filename()); } // Joins paths static string path_join(const string& patha, const string& pathb) { - return (make_path(patha) / make_path(pathb)).generic_u8string(); + return to_string(to_path(patha) / to_path(pathb)); } // Replaces extensions static string replace_extension(const string& filename, const string& ext) { - return make_path(filename).replace_extension(ext).generic_u8string(); + return to_string(to_path(filename).replace_extension(ext)); } // Check if a file can be opened for reading. static bool path_exists(const string& filename) { - return exists(make_path(filename)); + return exists(to_path(filename)); } } // namespace yocto @@ -281,7 +285,7 @@ static void format_value(string& str, T value) { str.append(buffer.data(), result.ptr); #else auto len = snprintf(buffer.data(), buffer.size(), "%.9g", value); - str.append(buffer.data(), buffer.data() + len); + str.append(buffer.begin(), buffer.begin() + len); #endif } else if constexpr (std::is_same_v) { #ifdef _WIN32 @@ -290,7 +294,7 @@ static void format_value(string& str, T value) { str.append(buffer.data(), result.ptr); #else auto len = snprintf(buffer.data(), buffer.size(), "%.17g", value); - str.append(buffer.data(), buffer.data() + len); + str.append(buffer.begin(), buffer.begin() + len); #endif } else { auto result = std::to_chars( @@ -1576,10 +1580,7 @@ static bool save_mtl( } // save file - if (!save_text(filename, buffer, error)) return false; - - // done - return true; + return save_text(filename, buffer, error); } // Save obj @@ -1622,10 +1623,7 @@ static bool save_obx( } // save file - if (!save_text(filename, buffer, error)) return false; - - // done - return true; + return save_text(filename, buffer, error); } // Save obj @@ -1748,10 +1746,7 @@ bool save_obj(const string& filename, const obj_shape& shape, string& error) { } // save file - if (!save_text(filename, buffer, error)) return false; - - // done - return true; + return save_text(filename, buffer, error); } // Get obj shape. @@ -2028,8 +2023,7 @@ void add_texcoords( } void add_triangles(obj_shape& shape, const vector>& triangles, int material, bool has_normals, bool has_texcoord) { - for (auto idx = 0; idx < (int)triangles.size(); idx++) { - auto& triangle = triangles[idx]; + for (auto& triangle : triangles) { for (auto c = 0; c < 3; c++) { shape.vertices.push_back({ triangle[c] + 1, @@ -2042,9 +2036,8 @@ void add_triangles(obj_shape& shape, const vector>& triangles, } void add_quads(obj_shape& shape, const vector>& quads, int material, bool has_normals, bool has_texcoord) { - for (auto idx = 0; idx < (int)quads.size(); idx++) { - auto& quad = quads[idx]; - auto nv = quad[2] == quad[3] ? 3 : 4; + for (auto& quad : quads) { + auto nv = quad[2] == quad[3] ? 3 : 4; for (auto c = 0; c < nv; c++) { shape.vertices.push_back({ quad[c] + 1, @@ -2057,8 +2050,7 @@ void add_quads(obj_shape& shape, const vector>& quads, } void add_lines(obj_shape& shape, const vector>& lines, int material, bool has_normals, bool has_texcoord) { - for (auto idx = 0; idx < (int)lines.size(); idx++) { - auto& line = lines[idx]; + for (auto& line : lines) { for (auto c = 0; c < 2; c++) { shape.vertices.push_back({ line[c] + 1, @@ -2071,8 +2063,7 @@ void add_lines(obj_shape& shape, const vector>& lines, } void add_points(obj_shape& shape, const vector& points, int material, bool has_normals, bool has_texcoord) { - for (auto idx = 0; idx < (int)points.size(); idx++) { - auto& point = points[idx]; + for (auto& point : points) { shape.vertices.push_back({ point + 1, !has_texcoord ? 0 : point + 1, @@ -2214,9 +2205,9 @@ bool load_stl(const string& filename, stl_model& stl, string& error, if (!read_value(data_view, ntriangles)) return read_error(); // resize buffers - shape.fnormals.resize(ntriangles); - shape.triangles.resize(ntriangles); - shape.positions.resize(ntriangles * 3); + shape.fnormals = vector>(ntriangles); + shape.triangles = vector>(ntriangles); + shape.positions = vector>(ntriangles * 3); // read all data for (auto triangle_id = 0; triangle_id < (int)ntriangles; triangle_id++) { diff --git a/libs/yocto/yocto_modelio.h b/libs/yocto/yocto_modelio.h index e7b5538e1..452b5e77e 100644 --- a/libs/yocto/yocto_modelio.h +++ b/libs/yocto/yocto_modelio.h @@ -67,7 +67,7 @@ enum struct ply_type { i8, i16, i32, i64, u8, u16, u32, u64, f32, f64 }; // Ply property struct ply_property { // description - string name = ""; + string name = {}; bool is_list = false; ply_type type = ply_type::f32; @@ -90,7 +90,7 @@ struct ply_property { // Ply elements struct ply_element { // element content - string name = ""; + string name = {}; size_t count = 0; vector properties = {}; }; @@ -136,18 +136,18 @@ inline bool get_list_values(const ply_model& ply, const string& element, // Get ply properties for meshes template -inline bool get_positions(const ply_model& ply, vector>& values); +inline bool get_positions(const ply_model& ply, vector>& positions); template -inline bool get_normals(const ply_model& ply, vector>& values); +inline bool get_normals(const ply_model& ply, vector>& normals); template inline bool get_texcoords( - const ply_model& ply, vector>& values, bool flipv = false); + const ply_model& ply, vector>& texcoords, bool flipv = false); template -inline bool get_colors(const ply_model& ply, vector>& values); +inline bool get_colors(const ply_model& ply, vector>& colors); template -inline bool get_colors(const ply_model& ply, vector>& values); +inline bool get_colors(const ply_model& ply, vector>& colors); template -inline bool get_radius(const ply_model& ply, vector& values); +inline bool get_radius(const ply_model& ply, vector& radius); template inline bool get_faces(const ply_model& ply, vector>& faces); template @@ -210,6 +210,27 @@ inline bool add_lines(ply_model& ply, const vector>& lines); template inline bool add_points(ply_model& ply, const vector& points); +// exception API +struct ply_error : std::runtime_error { + using std::runtime_error::runtime_error; +}; + +// Load and save ply +inline ply_model load_ply(const string& filename) { + auto ply = ply_model{}; + auto error = string{}; + if (!load_ply(filename, ply, error)) throw ply_error(filename); + return ply; +} +inline void load_ply(const string& filename, ply_model& ply) { + auto error = string{}; + if (!load_ply(filename, ply, error)) throw ply_error(filename); +} +inline void save_ply(const string& filename, const ply_model& ply) { + auto error = string{}; + if (!save_ply(filename, ply, error)) throw ply_error(filename); +} + } // namespace yocto // ----------------------------------------------------------------------------- @@ -241,7 +262,7 @@ struct obj_element { // Obj texture information. struct obj_texture { - string path = ""; // file path + string path = {}; // file path bool clamp = false; // clamp to edge float scale = 1; // scale for bump/displacement @@ -252,7 +273,7 @@ struct obj_texture { // Obj material struct obj_material { // material name and type - string name = ""; + string name = {}; int illum = 0; // material colors and values @@ -282,7 +303,7 @@ struct obj_material { // Obj shape struct obj_shape { - string name = ""; + string name = {}; vector> positions = {}; vector> normals = {}; vector> texcoords = {}; @@ -292,7 +313,7 @@ struct obj_shape { // Obj camera struct obj_camera { - string name = ""; + string name = {}; array frame = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}; bool ortho = false; float aspect = 16.0f / 9.0f; @@ -304,7 +325,7 @@ struct obj_camera { // Obj environment struct obj_environment { - string name = ""; + string name = {}; array frame = {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}; array emission = {0, 0, 0}; int emission_tex = -1; @@ -320,12 +341,6 @@ struct obj_model { vector environments = {}; }; -// Load and save obj shape -obj_shape load_sobj(const string& filename, bool face_varying = false); -void load_obj( - const string& filename, obj_shape& obj, bool face_varying = false); -void save_obj(const string& filename, const obj_shape& obj); - // Load and save obj [[nodiscard]] bool load_obj(const string& filename, obj_model& obj, string& error, bool face_varying = false, bool split_materials = false); @@ -333,60 +348,102 @@ void save_obj(const string& filename, const obj_shape& obj); const string& filename, const obj_model& obj, string& error); // Load and save obj shape -[[nodiscard]] bool load_obj(const string& filename, obj_shape& obj, +[[nodiscard]] bool load_obj(const string& filename, obj_shape& shape, string& error, bool face_varying = false); [[nodiscard]] bool save_obj( - const string& filename, const obj_shape& obj, string& error); + const string& filename, const obj_shape& shape, string& error); // Get obj shape. -void get_positions(const obj_shape& obj, vector>& positions); -void get_normals(const obj_shape& obj, vector>& normals); -void get_texcoords(const obj_shape& obj, vector>& texcoords, +void get_positions(const obj_shape& shape, vector>& positions); +void get_normals(const obj_shape& shape, vector>& normals); +void get_texcoords(const obj_shape& shape, vector>& texcoords, bool flipv = false); -void get_faces(const obj_shape& obj, vector>& triangles, +void get_faces(const obj_shape& shape, vector>& triangles, vector>& quads, vector& materials); -void get_triangles(const obj_shape& obj, vector>& triangles, +void get_triangles(const obj_shape& shape, vector>& triangles, + vector& materials); +void get_quads(const obj_shape& shape, vector>& quads, + vector& materials); +void get_lines(const obj_shape& shape, vector>& lines, vector& materials); -void get_quads( - const obj_shape& obj, vector>& quads, vector& materials); -void get_lines( - const obj_shape& obj, vector>& lines, vector& materials); void get_points( - const obj_shape& obj, vector& points, vector& materials); -void get_fvquads(const obj_shape& obj, vector>& quadspos, + const obj_shape& shape, vector& points, vector& materials); +void get_fvquads(const obj_shape& shape, vector>& quadspos, vector>& quadsnorm, vector>& quadstexcoord, vector& materials); -void get_faces(const obj_shape& obj, int material, +void get_faces(const obj_shape& shape, int material, vector>& triangles, vector>& quads); void get_triangles( - const obj_shape& obj, int material, vector>& triangles); + const obj_shape& shape, int material, vector>& triangles); void get_quads( - const obj_shape& obj, int material, vector>& quads); + const obj_shape& shape, int material, vector>& quads); void get_lines( - const obj_shape& obj, int material, vector>& lines); -void get_points(const obj_shape& obj, int material, vector& points); -bool has_quads(const obj_shape& obj); + const obj_shape& shape, int material, vector>& lines); +void get_points(const obj_shape& shape, int material, vector& points); +bool has_quads(const obj_shape& shape); // get unique materials from shape -vector get_materials(const obj_shape& obj); +vector get_materials(const obj_shape& shape); // Add obj shape -void add_positions(obj_shape& obj, const vector>& positions); -void add_normals(obj_shape& obj, const vector>& normals); -void add_texcoords(obj_shape& obj, const vector>& texcoords, +void add_positions(obj_shape& shape, const vector>& positions); +void add_normals(obj_shape& shape, const vector>& normals); +void add_texcoords(obj_shape& shape, const vector>& texcoords, bool flipv = false); -void add_triangles(obj_shape& obj, const vector>& triangles, +void add_triangles(obj_shape& shape, const vector>& triangles, int material, bool has_normals, bool has_texcoord); -void add_quads(obj_shape& obj, const vector>& quads, int material, - bool has_normals, bool has_texcoord); -void add_lines(obj_shape& obj, const vector>& lines, int material, - bool has_normals, bool has_texcoord); -void add_points(obj_shape& obj, const vector& points, int material, +void add_quads(obj_shape& shape, const vector>& quads, + int material, bool has_normals, bool has_texcoord); +void add_lines(obj_shape& shape, const vector>& lines, + int material, bool has_normals, bool has_texcoord); +void add_points(obj_shape& shape, const vector& points, int material, bool has_normals, bool has_texcoord); -void add_fvquads(obj_shape& obj, const vector>& quadspos, +void add_fvquads(obj_shape& shape, const vector>& quadspos, const vector>& quadsnorm, const vector>& quadstexcoord, int material); +// exception API +struct obj_error : std::runtime_error { + using std::runtime_error::runtime_error; +}; + +// Load and save obj shape +inline obj_shape load_sobj(const string& filename, bool face_varying = false) { + auto obj = obj_shape{}; + auto error = string{}; + if (!load_obj(filename, obj, error, face_varying)) throw obj_error(error); + return obj; +} +inline void load_obj( + const string& filename, obj_shape& obj, bool face_varying = false) { + auto error = string{}; + if (!load_obj(filename, obj, error, face_varying)) throw obj_error(error); +} +inline void save_obj(const string& filename, const obj_shape& obj) { + auto error = string{}; + if (!save_obj(filename, obj, error)) throw obj_error(error); +} + +// Load and save obj +inline obj_model load_obj(const string& filename, bool face_varying = false, + bool split_materials = false) { + auto obj = obj_model{}; + auto error = string{}; + if (!load_obj(filename, obj, error, face_varying, split_materials)) + throw obj_error(error); + return obj; +} +inline void load_obj(const string& filename, obj_model& obj, + bool face_varying = false, bool split_materials = false) { + auto error = string{}; + if (!load_obj(filename, obj, error, face_varying, split_materials)) + throw obj_error(error); +} +inline void save_obj(const string& filename, const obj_model& obj) { + auto error = string{}; + if (!save_obj(filename, obj, error)) throw obj_error(error); +} + } // namespace yocto // ----------------------------------------------------------------------------- @@ -438,6 +495,31 @@ void add_triangles(stl_model& stl, const vector>& triangles, const vector>& positions, const vector>& fnormals); +// exception API +struct stl_error : std::runtime_error { + using std::runtime_error::runtime_error; +}; + +// Load and save stl +inline stl_model load_stl(const string& filename, bool unique_vertices = true) { + auto stl = stl_model{}; + auto error = string{}; + if (!load_stl(filename, stl, error, unique_vertices)) + throw stl_error(filename); + return stl; +} +inline void load_stl( + const string& filename, stl_model& stl, bool unique_vertices = true) { + auto error = string{}; + if (!load_stl(filename, stl, error, unique_vertices)) + throw stl_error(filename); +} +inline void save_stl( + const string& filename, const stl_model& stl, bool ascii = false) { + auto error = string{}; + if (!save_stl(filename, stl, error, ascii)) throw stl_error(filename); +} + } // namespace yocto // ----------------------------------------------------------------------------- @@ -538,7 +620,7 @@ inline bool get_value(const ply_model& ply, const string& element, if (!has_property(ply, element, property)) return false; auto& prop = get_property(ply, element, property); if (prop.is_list) return false; - values.resize(get_size(prop)); + values = vector(get_size(prop)); for (auto index = (size_t)0; index < values.size(); index++) { values[index] = get_value(prop, index); } @@ -553,7 +635,8 @@ inline bool get_values(const ply_model& ply, const string& element, auto& prop = get_property(ply, element, property); if (prop.is_list) return false; } - values.resize(get_size(get_property(ply, element, properties.front()))); + values = vector>( + get_size(get_property(ply, element, properties.front()))); auto item = (size_t)0; for (auto& property : properties) { auto& prop = get_property(ply, element, property); @@ -573,10 +656,10 @@ inline bool get_lists(const ply_model& ply, const string& element, auto& prop = get_property(ply, element, property); if (!prop.is_list) return false; auto& sizes = prop.ldata_u8; - lists.resize(sizes.size()); + lists = vector>(sizes.size()); auto list = (size_t)0, current = (size_t)0; for (auto size : sizes) { - lists[list].resize(size); + lists[list] = vector(size); for (auto item = (size_t)0; item < size; item++) lists[list][item] = get_value(prop, current + item); list += 1; @@ -594,7 +677,7 @@ inline bool get_list_sizes(const ply_model& ply, const string& element, if constexpr (std::is_same_v) { sizes = prop.ldata_u8; } else { - sizes.resize(prop.ldata_u8.size()); + sizes = vector(prop.ldata_u8.size()); for (auto index = (size_t)0; index < sizes.size(); index++) { sizes[index] = (T)prop.ldata_u8[index]; } @@ -608,7 +691,7 @@ inline bool get_list_values(const ply_model& ply, const string& element, if (!has_property(ply, element, property)) return false; auto& prop = get_property(ply, element, property); if (!prop.is_list) return false; - values.resize(get_size(prop)); + values = vector(get_size(prop)); for (auto index = (size_t)0; index < values.size(); index++) { values[index] = get_value(prop, index); } @@ -776,7 +859,7 @@ inline bool get_colors(const ply_model& ply, vector>& colors) { auto colors3 = vector>{}; if (!get_values(ply, "vertex", {"red", "green", "blue"}, colors3)) return false; - colors.resize(colors3.size()); + colors = vector>(colors3.size()); for (auto i = 0; i < (int)colors.size(); i++) colors[i] = {colors3[i][0], colors3[i][1], colors3[i][2], 1}; return true; @@ -953,7 +1036,7 @@ inline bool add_lists(ply_model& ply, const string& element, const vector& values) { if (values.empty()) return false; if (!add_property( - ply, element, property, values.size(), get_ply_type(), true)) + ply, element, property, sizes.size(), get_ply_type(), true)) return false; auto& prop = get_property(ply, element, property); prop.ldata_u8 = sizes; @@ -1054,21 +1137,22 @@ inline bool add_faces(ply_model& ply, const vector>& triangles, } } template -inline bool add_triangles(ply_model& ply, const vector>& values) { - return add_faces(ply, values, {}); +inline bool add_triangles( + ply_model& ply, const vector>& triangles) { + return add_faces(ply, triangles, {}); } template -inline bool add_quads(ply_model& ply, const vector>& values) { - return add_faces(ply, {}, values); +inline bool add_quads(ply_model& ply, const vector>& quads) { + return add_faces(ply, {}, quads); } template -inline bool add_lines(ply_model& ply, const vector>& values) { - return add_lists(ply, "line", "vertex_indices", values); +inline bool add_lines(ply_model& ply, const vector>& lines) { + return add_lists(ply, "line", "vertex_indices", lines); } template -inline bool add_points(ply_model& ply, const vector& values) { +inline bool add_points(ply_model& ply, const vector& points) { return add_lists( - ply, "point", "vertex_indices", (const vector>&)values); + ply, "point", "vertex_indices", (const vector>&)points); } } // namespace yocto diff --git a/libs/yocto/yocto_pbrtio.cpp b/libs/yocto/yocto_pbrtio.cpp index a05d2ddf4..48190b707 100644 --- a/libs/yocto/yocto_pbrtio.cpp +++ b/libs/yocto/yocto_pbrtio.cpp @@ -86,14 +86,14 @@ static FILE* fopen_utf8(const char* filename, const char* mode) { static bool load_text(const string& filename, string& str, string& error) { // https://stackoverflow.com/questions/174531/how-to-read-the-content-of-a-file-to-a-string-in-c auto fs = fopen_utf8(filename.c_str(), "rb"); - if (!fs) { + if (fs == nullptr) { error = "cannot open " + filename; return false; } fseek(fs, 0, SEEK_END); auto length = ftell(fs); fseek(fs, 0, SEEK_SET); - str.resize(length); + str = string(length, '\0'); if (fread(str.data(), 1, length, fs) != length) { fclose(fs); error = "cannot read " + filename; @@ -107,7 +107,7 @@ static bool load_text(const string& filename, string& str, string& error) { static bool save_text( const string& filename, const string& str, string& error) { auto fs = fopen_utf8(filename.c_str(), "wt"); - if (!fs) { + if (fs == nullptr) { error = "cannot create " + filename; return false; } @@ -128,23 +128,30 @@ static bool save_text( namespace yocto { // Make a path from a utf8 string -static std::filesystem::path make_path(const string& filename) { - return std::filesystem::u8path(filename); +static std::filesystem::path to_path(const string& filename) { + auto filename8 = std::u8string((char8_t*)filename.data(), filename.size()); + return std::filesystem::path(filename8); +} + +// Make a utf8 string from a path +static string to_string(const std::filesystem::path& path) { + auto string8 = path.u8string(); + return string((char*)string8.data(), string8.size()); } // Get directory name (not including /) static string path_dirname(const string& filename) { - return make_path(filename).parent_path().generic_u8string(); + return to_string(to_path(filename).parent_path()); } // Get filename without directory. static string path_filename(const string& filename) { - return make_path(filename).filename().generic_u8string(); + return to_string(to_path(filename).filename()); } // Joins paths static string path_join(const string& patha, const string& pathb) { - return (make_path(patha) / make_path(pathb)).generic_u8string(); + return to_string(to_path(patha) / to_path(pathb)); } } // namespace yocto @@ -529,14 +536,14 @@ enum struct pbrt_type { // Pbrt value struct pbrt_value { - string name = ""; + string name = {}; pbrt_type type = pbrt_type::real; int value1i = 0; float value1f = 0; array value2f = {0, 0}; array value3f = {0, 0, 0}; bool value1b = false; - string value1s = ""; + string value1s = {}; vector vector1f = {}; vector> vector2f = {}; vector> vector3f = {}; @@ -545,15 +552,15 @@ struct pbrt_value { // Pbrt command struct pbrt_command { - string name = ""; - string type = ""; + string name = {}; + string type = {}; vector values = {}; array, 4> frame = {array{1, 0, 0}, - array{0, 1, 0}, array{0, 0, 1}, - array{0, 0, 0}}; + array{0, 1, 0}, array{0, 0, 1}, + array{0, 0, 0}}; array, 4> frend = {array{1, 0, 0}, - array{0, 1, 0}, array{0, 0, 1}, - array{0, 0, 0}}; + array{0, 1, 0}, array{0, 0, 1}, + array{0, 0, 0}}; }; // get pbrt value @@ -612,7 +619,7 @@ static bool get_pbrt_value( return true; } else if (pbrt.type == pbrt_type::real) { if (pbrt.vector1f.empty() || (pbrt.vector1f.size() % 2) != 0) return false; - val.resize(pbrt.vector1f.size() / 2); + val = vector>(pbrt.vector1f.size() / 2); for (auto i = 0; i < (int)val.size(); i++) val[i] = {pbrt.vector1f[i * 2 + 0], pbrt.vector1f[i * 2 + 1]}; return true; @@ -632,7 +639,7 @@ static bool get_pbrt_value( return true; } else if (pbrt.type == pbrt_type::real) { if (pbrt.vector1f.empty() || (pbrt.vector1f.size() % 3) != 0) return false; - val.resize(pbrt.vector1f.size() / 3); + val = vector>(pbrt.vector1f.size() / 3); for (auto i = 0; i < (int)val.size(); i++) val[i] = {pbrt.vector1f[i * 3 + 0], pbrt.vector1f[i * 3 + 1], pbrt.vector1f[i * 3 + 2]}; @@ -645,7 +652,7 @@ static bool get_pbrt_value( static bool get_pbrt_value(const pbrt_value& pbrt, vector>& val) { if (pbrt.type == pbrt_type::integer) { if (pbrt.vector1i.empty() || (pbrt.vector1i.size() % 3) != 0) return false; - val.resize(pbrt.vector1i.size() / 3); + val = vector>(pbrt.vector1i.size() / 3); for (auto i = 0; i < (int)val.size(); i++) val[i] = {pbrt.vector1i[i * 3 + 0], pbrt.vector1i[i * 3 + 1], pbrt.vector1i[i * 3 + 2]}; @@ -746,7 +753,7 @@ static pbrt_value make_pbrt_value(const string& name, auto pbrt = pbrt_value{}; pbrt.name = name; pbrt.type = type; - pbrt.vector1i = {&val.front()[0], &val.front()[0] + val.size() * 3}; + pbrt.vector1i = {(int*)val.data(), (int*)val.data() + val.size() * 3}; return pbrt; } @@ -797,7 +804,7 @@ static bool read_pbrt_cmdline(string_view& str, string& cmd) { // parse a quoted string [[nodiscard]] static bool parse_command(string_view& str, string& value) { skip_whitespace(str); - if (!isalpha((int)str.front())) return false; + if (!(bool)isalpha((int)str.front())) return false; auto pos = str.find_first_not_of( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); if (pos == string_view::npos) { @@ -1247,21 +1254,21 @@ static array blackbody_to_rgb_(float temperature) { // Other pbrt elements struct pbrt_film { // film approximation - string filename = ""; + string filename = {}; array resolution = {0, 0}; }; // Pbrt area light struct pbrt_arealight { // arealight parameters - string name = ""; + string name = {}; array emission = {0, 0, 0}; }; // Pbrt medium. Not parsed at the moment. struct pbrt_medium { // medium parameters - string name = ""; + string name = {}; }; // convert pbrt films @@ -1704,8 +1711,8 @@ struct pbrt_medium { pmaterial.volmeanfreepath = {1 / (sigma_a[0] + sigma_s[0]), 1 / (sigma_a[1] + sigma_s[1]), 1 / (sigma_a[2] + sigma_s[2])}; pmaterial.volscatter = {sigma_s[0] / (sigma_a[0] + sigma_s[0]), - sigma_s[1] / (sigma_a[1] + sigma_s[1]), - sigma_s[2] / (sigma_a[2] + sigma_s[2])}; + sigma_s[1] / (sigma_a[1] + sigma_s[1]), + sigma_s[2] / (sigma_a[2] + sigma_s[2])}; if (verbose) printf("subsurface material not properly supported\n"); } else if (command.type == "mix") { auto namedmaterial1 = ""s, namedmaterial2 = ""s; @@ -1773,9 +1780,9 @@ static void make_shape(vector>& triangles, auto vid = [steps](int i, int j) { return j * (steps[0] + 1) + i; }; auto tid = [steps]( int i, int j, int c) { return (j * steps[0] + i) * 2 + c; }; - positions.resize((steps[0] + 1) * (steps[1] + 1)); - normals.resize((steps[0] + 1) * (steps[1] + 1)); - texcoords.resize((steps[0] + 1) * (steps[1] + 1)); + positions = vector>((steps[0] + 1) * (steps[1] + 1)); + normals = vector>((steps[0] + 1) * (steps[1] + 1)); + texcoords = vector>((steps[0] + 1) * (steps[1] + 1)); for (auto j = 0; j < steps[1] + 1; j++) { for (auto i = 0; i < steps[0] + 1; i++) { auto uv = array{i / (float)steps[0], j / (float)steps[1]}; @@ -1784,7 +1791,7 @@ static void make_shape(vector>& triangles, texcoords[vid(i, j)] = uv; } } - triangles.resize(steps[0] * steps[1] * 2); + triangles = vector>(steps[0] * steps[1] * 2); for (auto j = 0; j < steps[1]; j++) { for (auto i = 0; i < steps[0]; i++) { triangles[tid(i, j, 0)] = {vid(i, j), vid(i + 1, j), vid(i + 1, j + 1)}; @@ -1880,7 +1887,7 @@ static void make_quad(vector>& triangles, pshape.triangles = {}; get_pbrt_value(command.values, "P", pshape.positions); get_pbrt_value(command.values, "indices", pshape.triangles); - pshape.normals.resize(pshape.positions.size()); + pshape.normals = vector>(pshape.positions.size()); // compute_normals(pshape.normals, pshape.triangles, pshape.positions); } else if (command.type == "plymesh") { pshape.filename_ = ""s; @@ -1966,7 +1973,7 @@ static void make_quad(vector>& triangles, get_pbrt_value(command.values, "from", plight.from); plight.area_emission = plight.emission; plight.area_frame = flatten( - mul(unflatten(plight.frame), translation_frame(plight.from))); + mul(unflatten(plight.frame), translation_frame(plight.from))); plight.area_frend = flatten( mul(unflatten(plight.frend), translation_frame(plight.from))); auto texcoords = vector>{}; @@ -2015,11 +2022,11 @@ static void make_quad(vector>& triangles, // pbrt stack ctm struct pbrt_stack_element { array, 4> transform_start = {array{1, 0, 0}, - array{0, 1, 0}, array{0, 0, 1}, - array{0, 0, 0}}; + array{0, 1, 0}, array{0, 0, 1}, + array{0, 0, 0}}; array, 4> transform_end = {array{1, 0, 0}, - array{0, 1, 0}, array{0, 0, 1}, - array{0, 0, 0}}; + array{0, 1, 0}, array{0, 0, 1}, + array{0, 0, 0}}; pbrt_material material = {}; pbrt_arealight arealight = {}; pbrt_medium interior = {}; @@ -2033,7 +2040,7 @@ struct pbrt_stack_element { struct pbrt_context { vector stack = {}; unordered_map coordsys = {}; - string cur_object = ""; + string cur_object = {}; array film_resolution = {512, 512}; }; @@ -2345,7 +2352,7 @@ bool load_pbrt( named_materials, named_textures, named_mediums, named_objects, path_dirname(filename), ply_meshes)) return false; - pbrt.textures.resize(texture_map.size()); + pbrt.textures = vector(texture_map.size()); for (auto& [path, texture_id] : texture_map) { pbrt.textures[texture_id].filename = path; } @@ -2625,10 +2632,7 @@ bool save_pbrt(const string& filename, const pbrt_model& pbrt, string& error, format_values(buffer, "\nWorldEnd\n\n"); // save file - if (!save_text(filename, buffer, error)) return false; - - // done - return true; + return save_text(filename, buffer, error); } } // namespace yocto diff --git a/libs/yocto/yocto_pbrtio.h b/libs/yocto/yocto_pbrtio.h index dd143f1b3..27fc26ad5 100644 --- a/libs/yocto/yocto_pbrtio.h +++ b/libs/yocto/yocto_pbrtio.h @@ -31,8 +31,8 @@ // SOFTWARE. // -#ifndef _YOCTO_PBRTIO_H_ -#define _YOCTO_PBRTIO_H_ +#ifndef YOCTO_PBRTIO_H_ +#define YOCTO_PBRTIO_H_ // ----------------------------------------------------------------------------- // INCLUDES @@ -75,9 +75,9 @@ struct pbrt_camera { // Pbrt material struct pbrt_texture { - string name = ""; + string name = {}; array constant = {1, 1, 1}; - string filename = ""; + string filename = {}; }; // Pbrt material type (simplified and only for the materials that matter here) @@ -89,7 +89,7 @@ enum struct pbrt_mtype { // Pbrt material struct pbrt_material { - string name = ""; + string name = {}; pbrt_mtype type = pbrt_mtype::matte; array emission = {0, 0, 0}; array color = {0, 0, 0}; @@ -110,7 +110,7 @@ struct pbrt_shape { vector> instances = {}; vector> instaends = {}; int material = -1; - string filename_ = ""; + string filename_ = {}; vector> positions = {}; vector> normals = {}; vector> texcoords = {}; @@ -157,6 +157,29 @@ struct pbrt_model { [[nodiscard]] bool save_pbrt(const string& filename, const pbrt_model& pbrt, string& error, bool ply_meshes = false); +// exception API +struct pbrt_error : std::runtime_error { + using std::runtime_error::runtime_error; +}; + +// Load/save pbrt +inline pbrt_model load_pbrt(const string& filename, bool ply_meshes = false) { + auto pbrt = pbrt_model{}; + auto error = string{}; + if (!load_pbrt(filename, pbrt, error, ply_meshes)) throw pbrt_error{error}; + return pbrt; +} +inline void load_pbrt( + const string& filename, pbrt_model& pbrt, bool ply_meshes = false) { + auto error = string{}; + if (!load_pbrt(filename, pbrt, error, ply_meshes)) throw pbrt_error{error}; +} +inline void save_pbrt( + const string& filename, const pbrt_model& pbrt, bool ply_meshes = false) { + auto error = string{}; + if (!save_pbrt(filename, pbrt, error, ply_meshes)) throw pbrt_error{error}; +} + } // namespace yocto #endif diff --git a/libs/yocto/yocto_sceneio.cpp b/libs/yocto/yocto_sceneio.cpp index 95cadc754..4598ccd6b 100644 --- a/libs/yocto/yocto_sceneio.cpp +++ b/libs/yocto/yocto_sceneio.cpp @@ -129,69 +129,71 @@ inline bool parallel_foreach( namespace yocto { // Make a path from a utf8 string -static std::filesystem::path make_path(const string& path) { - return std::filesystem::u8path(path); +static std::filesystem::path to_path(const string& filename) { + auto filename8 = std::u8string((char8_t*)filename.data(), filename.size()); + return std::filesystem::path(filename8); } -// Normalize a path -string path_normalized(const string& path) { - return make_path(path).generic_u8string(); +// Make a utf8 string from a path +static string to_string(const std::filesystem::path& path) { + auto string8 = path.u8string(); + return string((char*)string8.data(), string8.size()); } +// Normalize a path +string path_normalized(const string& path) { return to_string(to_path(path)); } + // Get directory name (not including /) string path_dirname(const string& path) { - return make_path(path).parent_path().generic_u8string(); + return to_string(to_path(path).parent_path()); } // Get filename without directory and extension. string path_basename(const string& path) { - return make_path(path).stem().generic_u8string(); + return to_string(to_path(path).stem()); } // Get extension string path_extension(const string& path) { - return make_path(path).extension().generic_u8string(); + return to_string(to_path(path).extension()); } // Check if a file can be opened for reading. -bool path_exists(const string& path) { return exists(make_path(path)); } +bool path_exists(const string& path) { return exists(to_path(path)); } // Replace the extension of a file string replace_extension(const string& path, const string& extension) { - auto ext = make_path(extension).extension(); - return make_path(path).replace_extension(ext).generic_u8string(); + auto ext = to_path(extension).extension(); + return to_string(to_path(path).replace_extension(ext)); } // Create a directory and all missing parent directories if needed void make_directory(const string& path) { if (path_exists(path)) return; try { - create_directories(make_path(path)); + create_directories(to_path(path)); } catch (...) { - throw io_error{path + ": cannot create directory"}; + throw io_error{"cannot create directory " + path}; } } -// Create a directory and all missing parent directories if needed bool make_directory(const string& path, string& error) { - if (path_exists(path)) return true; try { - create_directories(make_path(path)); + make_directory(path); return true; } catch (...) { - error = path + ": cannot create directory"; + error = "cannot create directory " + path; return false; } } // Joins paths static string path_join(const string& patha, const string& pathb) { - return (make_path(patha) / make_path(pathb)).generic_u8string(); + return to_string(to_path(patha) / to_path(pathb)); } static string path_join( const string& patha, const string& pathb, const string& pathc) { - return (make_path(patha) / make_path(pathb) / make_path(pathc)) - .generic_u8string(); + return to_string(to_path(patha) / to_path(pathb) / to_path(pathc)); } } // namespace yocto @@ -1481,16 +1483,16 @@ shape_data make_shape_preset(const string& type) { for (auto& p : shape.positions) p += {0, 0.075f, 0}; return shape; } else if (type == "test-geosphere") { - auto shape = make_geosphere(0.075f, 3); + auto shape = make_geosphere(3, 0.075f); for (auto& p : shape.positions) p += {0, 0.075f, 0}; return shape; } else if (type == "test-geosphere-flat") { - auto shape = make_geosphere(0.075f, 3); + auto shape = make_geosphere(3, 0.075f); for (auto& p : shape.positions) p += {0, 0.075f, 0}; shape.normals = {}; return shape; } else if (type == "test-geosphere-subdivided") { - auto shape = make_geosphere(0.075f, 6); + auto shape = make_geosphere(3, 0.075f); for (auto& p : shape.positions) p += {0, 0.075f, 0}; return shape; } else if (type == "test-hairball1") { @@ -1513,11 +1515,11 @@ shape_data make_shape_preset(const string& type) { for (auto& p : shape.positions) p += {0, 0.075f, 0}; return shape; } else if (type == "test-suzanne-subdiv") { - auto shape = make_monkey(0.075f * 0.8f); + auto shape = make_monkey(0, 0.075f * 0.8f); for (auto& p : shape.positions) p += {0, 0.075f, 0}; return shape; } else if (type == "test-cube-subdiv") { - auto fvshape = make_fvcube(0.075f); + auto fvshape = make_fvcube(0, 0.075f); auto shape = shape_data{}; shape.quads = fvshape.quadspos; shape.positions = fvshape.positions; @@ -1549,7 +1551,7 @@ shape_data make_shape_preset(const string& type) { for (auto& r : shape.radius) r *= 0.075f; return shape; } else if (type == "test-lines-grid") { - auto shape = make_lines({256, 256}, {0.075f, 0.075f}); + auto shape = make_lines(256, 256, {0.075f, 0.075f}); for (auto& p : shape.positions) p += {0, 0.075f, 0}; for (auto& r : shape.radius) r *= 0.075f; return shape; @@ -1559,7 +1561,7 @@ shape_data make_shape_preset(const string& type) { for (auto& r : shape.radius) r *= 0.075f * 10; return shape; } else if (type == "test-thicklines-grid") { - auto shape = make_lines({16, 16}, {0.075f, 0.075f}); + auto shape = make_lines(16, 16, {0.075f, 0.075f}); for (auto& p : shape.positions) p += {0, 0.075f, 0}; for (auto& r : shape.radius) r *= 0.075f * 10; return shape; @@ -1694,11 +1696,11 @@ fvshape_data make_fvshape_preset(const string& type) { for (auto& p : shape.positions) p += {0, 0.075f, 0}; return shape_to_fvshape(shape); } else if (type == "test-suzanne-subdiv") { - auto shape = make_monkey(0.075f * 0.8f); + auto shape = make_monkey(0, 0.075f * 0.8f); for (auto& p : shape.positions) p += {0, 0.075f, 0}; return shape_to_fvshape(shape); } else if (type == "test-cube-subdiv") { - auto fvshape = make_fvcube(0.075f); + auto fvshape = make_fvcube(0, 0.075f); for (auto& p : fvshape.positions) p += {0, 0.075f, 0}; return fvshape; } else if (type == "test-arealight1") { diff --git a/libs/yocto/yocto_shading.h b/libs/yocto/yocto_shading.h index 3681856db..f245d91e7 100644 --- a/libs/yocto/yocto_shading.h +++ b/libs/yocto/yocto_shading.h @@ -582,7 +582,7 @@ inline vec3f eval_glossy(const vec3f& color, float ior, float roughness, auto F = fresnel_dielectric(ior, halfway, incoming); auto D = microfacet_distribution(roughness, up_normal, halfway); auto G = microfacet_shadowing( - roughness, up_normal, halfway, outgoing, incoming); + roughness, up_normal, halfway, outgoing, incoming); return color * (1 - F1) / pif * abs(dot(up_normal, incoming)) + vec3f{1, 1, 1} * F * D * G / (4 * dot(up_normal, outgoing) * dot(up_normal, incoming)) * @@ -622,7 +622,7 @@ inline vec3f eval_reflective(const vec3f& color, float roughness, auto up_normal = dot(normal, outgoing) <= 0 ? -normal : normal; auto halfway = normalize(incoming + outgoing); auto F = fresnel_conductor( - reflectivity_to_eta(color), {0, 0, 0}, halfway, incoming); + reflectivity_to_eta(color), {0, 0, 0}, halfway, incoming); auto D = microfacet_distribution(roughness, up_normal, halfway); auto G = microfacet_shadowing( roughness, up_normal, halfway, outgoing, incoming); @@ -660,7 +660,7 @@ inline vec3f eval_reflective(const vec3f& eta, const vec3f& etak, auto F = fresnel_conductor(eta, etak, halfway, incoming); auto D = microfacet_distribution(roughness, up_normal, halfway); auto G = microfacet_shadowing( - roughness, up_normal, halfway, outgoing, incoming); + roughness, up_normal, halfway, outgoing, incoming); return F * D * G / (4 * dot(up_normal, outgoing) * dot(up_normal, incoming)) * abs(dot(up_normal, incoming)); } @@ -743,7 +743,7 @@ inline vec3f eval_gltfpbr(const vec3f& color, float ior, float roughness, auto F = fresnel_schlick(reflectivity, halfway, incoming); auto D = microfacet_distribution(roughness, up_normal, halfway); auto G = microfacet_shadowing( - roughness, up_normal, halfway, outgoing, incoming); + roughness, up_normal, halfway, outgoing, incoming); return color * (1 - metallic) * (1 - F1) / pif * abs(dot(up_normal, incoming)) + F * D * G / (4 * dot(up_normal, outgoing) * dot(up_normal, incoming)) * @@ -791,7 +791,7 @@ inline vec3f eval_transparent(const vec3f& color, float ior, float roughness, auto F = fresnel_dielectric(ior, halfway, outgoing); auto D = microfacet_distribution(roughness, up_normal, halfway); auto G = microfacet_shadowing( - roughness, up_normal, halfway, outgoing, incoming); + roughness, up_normal, halfway, outgoing, incoming); return vec3f{1, 1, 1} * F * D * G / (4 * dot(up_normal, outgoing) * dot(up_normal, incoming)) * abs(dot(up_normal, incoming)); @@ -801,7 +801,7 @@ inline vec3f eval_transparent(const vec3f& color, float ior, float roughness, auto F = fresnel_dielectric(ior, halfway, outgoing); auto D = microfacet_distribution(roughness, up_normal, halfway); auto G = microfacet_shadowing( - roughness, up_normal, halfway, outgoing, reflected); + roughness, up_normal, halfway, outgoing, reflected); return color * (1 - F) * D * G / (4 * dot(up_normal, outgoing) * dot(up_normal, reflected)) * (abs(dot(up_normal, reflected))); @@ -888,7 +888,7 @@ inline vec3f eval_refractive(const vec3f& color, float ior, float roughness, auto F = fresnel_dielectric(rel_ior, halfway, outgoing); auto D = microfacet_distribution(roughness, up_normal, halfway); auto G = microfacet_shadowing( - roughness, up_normal, halfway, outgoing, incoming); + roughness, up_normal, halfway, outgoing, incoming); return vec3f{1, 1, 1} * F * D * G / abs(4 * dot(normal, outgoing) * dot(normal, incoming)) * abs(dot(normal, incoming)); diff --git a/libs/yocto/yocto_shape.cpp b/libs/yocto/yocto_shape.cpp index 0cc3a17d5..acb8ea84f 100644 --- a/libs/yocto/yocto_shape.cpp +++ b/libs/yocto/yocto_shape.cpp @@ -147,7 +147,7 @@ vec4f eval_color(const shape_data& shape, int element, const vec2f& uv) { return interpolate_quad(shape.colors[quad.x], shape.colors[quad.y], shape.colors[quad.z], shape.colors[quad.w], uv); } else { - return {0, 0}; + return {0, 0, 0, 0}; } } @@ -354,6 +354,93 @@ shape_data subdivide_shape( return subdivided; } +// Transform shape +shape_data transform_shape( + const shape_data& shape, const frame3f& frame, bool non_rigid) { + return transform_shape(shape, frame, 1, non_rigid); +} +shape_data transform_shape(const shape_data& shape, const frame3f& frame, + float radius_scale, bool non_rigid) { + auto transformed = shape; + for (auto& position : transformed.positions) + position = transform_point(frame, position); + for (auto& normal : transformed.normals) + normal = transform_normal(frame, normal, non_rigid); + for (auto& radius : transformed.radius) radius *= radius_scale; + return transformed; +} +shape_data scale_shape(const shape_data& shape, float scale, float uvscale) { + if (scale == 1 && uvscale == 1) return shape; + auto transformed = shape; + for (auto& position : transformed.positions) position *= scale; + for (auto& texcoord : transformed.texcoords) texcoord *= uvscale; + return transformed; +} +shape_data scale_shape(shape_data&& shape, float scale, float uvscale) { + if (scale == 1 && uvscale == 1) return std::move(shape); + auto transformed = std::move(shape); + for (auto& position : transformed.positions) position *= scale; + for (auto& texcoord : transformed.texcoords) texcoord *= uvscale; + return transformed; +} + +// Manipulate vertex data +shape_data remove_normals(const shape_data& shape) { + auto transformed = shape; + transformed.normals = {}; + return transformed; +} +shape_data add_normals(const shape_data& shape) { + auto transformed = shape; + transformed.normals = compute_normals(shape); + return transformed; +} +shape_data weld_vertices( + const shape_data& shape, float threshold, bool normals, bool others) { + auto transform_vert = [](auto&& old_verts, const vector& old_indices) { + if (old_verts.empty()) return old_verts; + using T = typename std::remove_cvref_t; + auto verts = vector(old_indices.size()); + for (auto&& [idx, vert] : enumerate(verts)) + vert = old_verts[old_indices[idx]]; + return verts; + }; + auto transform_elem = [](auto&& elems_, const vector& new_indices) { + auto elems = std::move(elems_); + for (auto& elem : elems) + if constexpr (std::is_same_v, int>) { + elem = new_indices[elem]; + } else { + for (auto& vid : elem) vid = new_indices[vid]; + } + return elems; + }; + + auto [old_indices, new_indices] = weld_indices(shape.positions, threshold); + auto transformed = shape; + transformed.positions = transform_vert(transformed.positions, old_indices); + if (normals) { + transformed.normals = transform_vert(transformed.normals, old_indices); + } else { + transformed.normals = {}; + } + if (others) { + transformed.texcoords = transform_vert(transformed.texcoords, old_indices); + transformed.colors = transform_vert(transformed.colors, old_indices); + transformed.radius = transform_vert(transformed.radius, old_indices); + } else { + transformed.texcoords = {}; + transformed.colors = {}; + transformed.radius = {}; + } + transformed.tangents = transform_vert(transformed.tangents, old_indices); + transformed.points = transform_elem(transformed.points, new_indices); + transformed.lines = transform_elem(transformed.lines, new_indices); + transformed.triangles = transform_elem(transformed.triangles, new_indices); + transformed.quads = transform_elem(transformed.quads, new_indices); + return transformed; +} + vector shape_stats(const shape_data& shape, bool verbose) { auto format = [](auto num) { auto str = std::to_string(num); @@ -958,28 +1045,108 @@ shape_data make_rounded_uvcylinder(const vec3i& steps, const vec2f& scale, return shape; } +// Make a uv capsule +shape_data make_uvcapsule( + const vec3i& steps, const vec2f& scale, const vec3f& uvscale) { + auto shape = shape_data{}; + auto qshape = shape_data{}; + // side + qshape = make_rect({steps.x, steps.y}, {1, 1}, {1, 1}); + for (auto i : range(qshape.positions.size())) { + auto uv = qshape.texcoords[i]; + auto phi = 2 * pif * uv.x; + qshape.positions[i] = { + cos(phi) * scale.x, sin(phi) * scale.x, (2 * uv.y - 1) * scale.y}; + qshape.normals[i] = {cos(phi), sin(phi), 0}; + qshape.texcoords[i] = uv * vec2f{uvscale.x, uvscale.y}; + } + for (auto& quad : qshape.quads) quad = {quad.x, quad.w, quad.z, quad.y}; + merge_shape_inplace(shape, qshape); + // top + qshape = make_rect({steps.x, steps.z}, {1, 1}, {1, 1}); + for (auto i : range(qshape.positions.size())) { + auto uv = qshape.texcoords[i]; + auto phi = uv.x * 2 * pif, theta = (1 - uv.y) * pif / 2; + qshape.positions[i] = {cos(phi) * sin(theta) * scale.x, + sin(phi) * sin(theta) * scale.x, cos(theta) * scale.x + scale.y}; + qshape.normals[i] = { + cos(phi) * sin(theta), sin(phi) * sin(theta), cos(theta)}; + qshape.texcoords[i] = uv * vec2f{uvscale.x, uvscale.z}; + } + merge_shape_inplace(shape, qshape); + // bottom + qshape = make_rect({steps.x, steps.z}, {1, 1}, {1, 1}); + for (auto i : range(qshape.positions.size())) { + auto uv = qshape.texcoords[i]; + auto phi = uv.x * 2 * pif, theta = (1 - uv.y) * pif / 2; + qshape.positions[i] = {cos(phi) * sin(theta) * scale.x, + sin(phi) * sin(theta) * scale.x, -cos(theta) * scale.x - scale.y}; + qshape.normals[i] = { + cos(phi) * sin(theta), sin(phi) * sin(theta), -cos(theta)}; + qshape.texcoords[i] = uv * vec2f{uvscale.x, uvscale.z}; + qshape.normals[i] = -qshape.normals[i]; + } + for (auto& qquad : qshape.quads) swap(qquad.x, qquad.z); + merge_shape_inplace(shape, qshape); + return shape; +} + +// Make a uv cone +shape_data make_uvcone( + const vec3i& steps, const vec2f& scale, const vec3f& uvscale) { + auto shape = shape_data{}; + auto qshape = shape_data{}; + // side + qshape = make_rect({steps.x, steps.y}, {1, 1}, {1, 1}); + for (auto i : range(qshape.positions.size())) { + auto uv = qshape.texcoords[i]; + auto phi = 2 * pif * uv.x; + auto normal2d = normalize(scale * vec2f{1, 2}); + qshape.positions[i] = {cos(phi) * (1 - uv.y) * scale.x, + sin(phi) * (1 - uv.y) * scale.x, scale.y * (2 * uv.y - 1)}; + qshape.normals[i] = { + cos(phi) * normal2d.y, sin(phi) * normal2d.y, normal2d.x}; + qshape.texcoords[i] = uv * vec2f{uvscale.x, uvscale.y}; + } + for (auto& quad : qshape.quads) quad = {quad.x, quad.w, quad.z, quad.y}; + merge_shape_inplace(shape, qshape); + // bottom + qshape = make_rect({steps.x, steps.z}, {1, 1}, {1, 1}); + for (auto i : range(qshape.positions.size())) { + auto uv = qshape.texcoords[i]; + auto phi = uv.x * 2 * pif; + qshape.positions[i] = { + uv.y * scale.x * cos(phi), uv.y * scale.x * sin(phi), -scale.y}; + qshape.normals[i] = {0, 0, -1}; + qshape.texcoords[i] = uv * vec2f{uvscale.x, uvscale.z}; + } + for (auto& qquad : qshape.quads) swap(qquad.x, qquad.z); + merge_shape_inplace(shape, qshape); + return shape; +} + // Generate lines set along a quad. Returns lines, pos, norm, texcoord, radius. -shape_data make_lines(const vec2i& steps, const vec2f& scale, +shape_data make_lines(int num, int steps, const vec2f& scale, const vec2f& uvscale, const vec2f& rad) { auto shape = shape_data{}; - shape.positions.resize((steps.x + 1) * steps.y); - shape.normals.resize((steps.x + 1) * steps.y); - shape.texcoords.resize((steps.x + 1) * steps.y); - shape.radius.resize((steps.x + 1) * steps.y); - if (steps.y > 1) { - for (auto j : range(steps.y)) { - for (auto i : range(steps.x + 1)) { - auto uv = vec2f{i / (float)steps.x, j / (float)(steps.y - 1)}; - shape.positions[j * (steps.x + 1) + i] = { + shape.positions.resize((steps + 1) * num); + shape.normals.resize((steps + 1) * num); + shape.texcoords.resize((steps + 1) * num); + shape.radius.resize((steps + 1) * num); + if (num > 1) { + for (auto j : range(num)) { + for (auto i : range(steps + 1)) { + auto uv = vec2f{i / (float)steps, j / (float)(num - 1)}; + shape.positions[j * (steps + 1) + i] = { (uv.x - 0.5f) * scale.x, (uv.y - 0.5f) * scale.y, 0}; - shape.normals[j * (steps.x + 1) + i] = {1, 0, 0}; - shape.texcoords[j * (steps.x + 1) + i] = uv * uvscale; - shape.radius[j * (steps.x + 1) + i] = lerp(rad.x, rad.y, uv.x); + shape.normals[j * (steps + 1) + i] = {1, 0, 0}; + shape.texcoords[j * (steps + 1) + i] = uv * uvscale; + shape.radius[j * (steps + 1) + i] = lerp(rad.x, rad.y, uv.x); } } } else { - for (auto i : range(steps.x + 1)) { - auto uv = vec2f{i / (float)steps.x, 0}; + for (auto i : range(steps + 1)) { + auto uv = vec2f{i / (float)steps, 0}; shape.positions[i] = {(uv.x - 0.5f) * scale.x, 0, 0}; shape.normals[i] = {1, 0, 0}; shape.texcoords[i] = uv * uvscale; @@ -987,11 +1154,11 @@ shape_data make_lines(const vec2i& steps, const vec2f& scale, } } - shape.lines.resize(steps.x * steps.y); - for (int j = 0; j < steps.y; j++) { - for (int i = 0; i < steps.x; i++) { - shape.lines[j * steps.x + i] = { - j * (steps.x + 1) + i, j * (steps.x + 1) + i + 1}; + shape.lines.resize(steps * num); + for (auto j = 0; j < num; j++) { + for (auto i = 0; i < steps; i++) { + shape.lines[j * steps + i] = { + j * (steps + 1) + i, j * (steps + 1) + i + 1}; } } @@ -1080,7 +1247,7 @@ fvshape_data make_fvsphere(int steps, float scale, float uvscale) { } // Predefined meshes -shape_data make_monkey(float scale, int subdivisions) { +shape_data make_monkey(int subdivisions, float scale) { extern vector suzanne_positions; extern vector suzanne_quads; @@ -1097,7 +1264,7 @@ shape_data make_monkey(float scale, int subdivisions) { } return shape; } -shape_data make_quad(float scale, int subdivisions) { +shape_data make_quad(int subdivisions, float scale) { static const auto quad_positions = vector{ {-1, -1, 0}, {+1, -1, 0}, {+1, +1, 0}, {-1, +1, 0}}; static const auto quad_normals = vector{ @@ -1124,7 +1291,7 @@ shape_data make_quad(float scale, int subdivisions) { } return shape; } -shape_data make_quady(float scale, int subdivisions) { +shape_data make_quady(int subdivisions, float scale) { static const auto quady_positions = vector{ {-1, 0, -1}, {-1, 0, +1}, {+1, 0, +1}, {+1, 0, -1}}; static const auto quady_normals = vector{ @@ -1151,7 +1318,7 @@ shape_data make_quady(float scale, int subdivisions) { } return shape; } -shape_data make_cube(float scale, int subdivisions) { +shape_data make_cube(int subdivisions, float scale) { static const auto cube_positions = vector{{-1, -1, +1}, {+1, -1, +1}, {+1, +1, +1}, {-1, +1, +1}, {+1, -1, -1}, {-1, -1, -1}, {-1, +1, -1}, {+1, +1, -1}, {+1, -1, +1}, {+1, -1, -1}, {+1, +1, -1}, {+1, +1, +1}, @@ -1189,7 +1356,7 @@ shape_data make_cube(float scale, int subdivisions) { } return shape; } -fvshape_data make_fvcube(float scale, int subdivisions) { +fvshape_data make_fvcube(int subdivisions, float scale) { static const auto fvcube_positions = vector{{-1, -1, +1}, {+1, -1, +1}, {+1, +1, +1}, {-1, +1, +1}, {+1, -1, -1}, {-1, -1, -1}, {-1, +1, -1}, {+1, +1, -1}}; @@ -1231,7 +1398,7 @@ fvshape_data make_fvcube(float scale, int subdivisions) { } return shape; } -shape_data make_geosphere(float scale, int subdivisions) { +shape_data make_geosphere(int subdivisions, float scale) { // https://stackoverflow.com/questions/17705621/algorithm-for-a-geodesic-sphere const float X = 0.525731112119133606f; const float Z = 0.850650808352039932f; @@ -1295,7 +1462,7 @@ shape_data make_hair(const shape_data& base, const vec2i& steps, } } - auto shape = make_lines(steps, {1, 1}, {1, 1}, {1, 1}); + auto shape = make_lines(steps.y, steps.x, {1, 1}, {1, 1}, {1, 1}); for (auto i : range((int)shape.positions.size())) { auto u = shape.texcoords[i].x; auto bidx = i / (steps.x + 1); @@ -1333,6 +1500,49 @@ shape_data make_hair(const shape_data& base, const vec2i& steps, return shape; } + +// Grow points around a shape +shape_data make_random_points( + const shape_data& shape, int num, float radius, uint64_t seed) { + auto samples = sample_shape(shape, num, seed); + auto points = make_points(num, 1, radius); + for (auto idx : range(num)) { + points.positions[idx] = eval_position( + shape, samples[idx].element, samples[idx].uv); + points.normals[idx] = eval_normal( + shape, samples[idx].element, samples[idx].uv); + } + return points; +} + +// Grow hairs around a shape +shape_data make_random_hairs(const shape_data& shape, int num, int steps, + const vec2f& len, const vec2f& radius, float noise, float gravity, + uint64_t seed) { + auto samples = sample_shape(shape, num, seed); + auto hairs = make_lines(num, steps, {1, 1}, {1, 1}, radius); + auto rng = make_rng(seed); + for (auto idx : range(num)) { + auto offset = idx * (steps + 1); + auto position = eval_position(shape, samples[idx].element, samples[idx].uv); + auto direction = eval_normal(shape, samples[idx].element, samples[idx].uv); + auto length = lerp(len.x, len.y, rand1f(rng)); + hairs.positions[offset] = position; + for (auto iidx = 1; iidx <= steps; iidx++) { + hairs.positions[offset + iidx] = position; + hairs.positions[offset + iidx] += direction * length / (float)steps; + hairs.positions[offset + iidx] += (2 * rand3f(rng) - 1) * noise; + hairs.positions[offset + iidx] += vec3f{0, -gravity, 0}; + direction = normalize(hairs.positions[offset + iidx] - position); + position = hairs.positions[offset + iidx]; + } + } + + hairs.normals = lines_tangents(hairs.lines, hairs.positions); + + return hairs; +} + // Grow hairs around a shape shape_data make_hair2(const shape_data& base, const vec2i& steps, const vec2f& len, const vec2f& radius, float noise, float gravity, @@ -1347,7 +1557,7 @@ shape_data make_hair2(const shape_data& base, const vec2i& steps, btexcoord.push_back(eval_texcoord(base, point.element, point.uv)); } - auto shape = make_lines(steps, {1, 1}, {1, 1}, radius); + auto shape = make_lines(steps.y, steps.x, {1, 1}, {1, 1}, radius); auto rng = make_rng(seed); for (auto idx : range(steps.y)) { auto offset = idx * (steps.x + 1); @@ -2635,6 +2845,25 @@ pair, vector> weld_vertices( } return {welded, indices}; } +inline pair, vector> weld_indices( + const vector& positions, float threshold) { + auto new_indices = vector(positions.size()); + auto old_indices = vector(); + auto grid = make_hash_grid(threshold); + auto neighbors = vector{}; + for (auto vertex : range(positions.size())) { + auto& position = positions[vertex]; + find_neighbors(grid, neighbors, position, threshold); + if (neighbors.empty()) { + old_indices.push_back((int)vertex); + new_indices[vertex] = (int)old_indices.size() - 1; + insert_vertex(grid, position); + } else { + new_indices[vertex] = neighbors.front(); + } + } + return {old_indices, new_indices}; +} pair, vector> weld_triangles( const vector& triangles, const vector& positions, float threshold) { @@ -3804,8 +4033,8 @@ void make_fvsphere(vector& quadspos, vector& quadsnorm, } // Predefined meshes -void make_monkey(vector& quads, vector& positions, float scale, - int subdivisions) { +void make_monkey(vector& quads, vector& positions, + int subdivisions, float scale) { extern vector suzanne_positions; extern vector suzanne_quads; @@ -3850,8 +4079,8 @@ void make_quad(vector& quads, vector& positions, } void make_quady(vector& quads, vector& positions, - vector& normals, vector& texcoords, float scale, - int subdivisions) { + vector& normals, vector& texcoords, int subdivisions, + float scale) { static const auto quady_positions = vector{ {-1, 0, -1}, {-1, 0, +1}, {+1, 0, +1}, {+1, 0, -1}}; static const auto quady_normals = vector{ @@ -3878,8 +4107,8 @@ void make_quady(vector& quads, vector& positions, } void make_cube(vector& quads, vector& positions, - vector& normals, vector& texcoords, float scale, - int subdivisions) { + vector& normals, vector& texcoords, int subdivisions, + float scale) { static const auto cube_positions = vector{{-1, -1, +1}, {+1, -1, +1}, {+1, +1, +1}, {-1, +1, +1}, {+1, -1, -1}, {-1, -1, -1}, {-1, +1, -1}, {+1, +1, -1}, {+1, -1, +1}, {+1, -1, -1}, {+1, +1, -1}, {+1, +1, +1}, @@ -3917,8 +4146,8 @@ void make_cube(vector& quads, vector& positions, void make_fvcube(vector& quadspos, vector& quadsnorm, vector& quadstexcoord, vector& positions, - vector& normals, vector& texcoords, float scale, - int subdivisions) { + vector& normals, vector& texcoords, int subdivisions, + float scale) { static const auto fvcube_positions = vector{{-1, -1, +1}, {+1, -1, +1}, {+1, +1, +1}, {-1, +1, +1}, {+1, -1, -1}, {-1, -1, -1}, {-1, +1, -1}, {+1, +1, -1}}; diff --git a/libs/yocto/yocto_shape.h b/libs/yocto/yocto_shape.h index cd16db616..d4a22bc31 100644 --- a/libs/yocto/yocto_shape.h +++ b/libs/yocto/yocto_shape.h @@ -87,6 +87,12 @@ struct shape_data { vector tangents = {}; }; +// Shape creation +template +inline shape_data make_lines(int steps, PFunc&& position, TFunc&& tangent); +template +inline shape_data make_quads(int steps, PFunc&& position, NFunc&& normal); + // Interpolate vertex data vec3f eval_position(const shape_data& shape, int element, const vec2f& uv); vec3f eval_normal(const shape_data& shape, int element, const vec2f& uv); @@ -124,6 +130,24 @@ void quads_to_triangles_inplace(shape_data& shape); shape_data subdivide_shape( const shape_data& shape, int subdivisions, bool catmullclark); +// Transform shape +shape_data transform_shape( + const shape_data& shape, const frame3f& frame, bool non_rigid = false); +shape_data transform_shape(const shape_data& shape, const frame3f& frame, + float radius_scale, bool non_rigid = false); +shape_data scale_shape(const shape_data& shape, float scale, float uvscale = 1); +shape_data scale_shape(shape_data&& shape, float scale, float uvscale = 1); +shape_data flipyz_shape(const shape_data& shape); + +// Manipulate vertex data +shape_data remove_normals(const shape_data& shape); +shape_data add_normals(const shape_data& shape); +shape_data weld_vertices(const shape_data& shape, float threshold, + bool normals = false, bool others = false); + +// Merge a shape into another +void merge_shape_inplace(shape_data& shape, const shape_data& merge); + // Shape statistics vector shape_stats(const shape_data& shape, bool verbose = false); @@ -168,6 +192,18 @@ fvshape_data shape_to_fvshape(const shape_data& shape); fvshape_data subdivide_fvshape( const fvshape_data& shape, int subdivisions, bool catmullclark); +// Transform shape +fvshape_data transform_fvshape( + const fvshape_data& shape, const frame3f& frame, bool non_rigid = false); +fvshape_data scale_fvshape( + const fvshape_data& shape, float scale, float uvscale = 1); +fvshape_data scale_fvshape( + fvshape_data&& shape, float scale, float uvscale = 1); + +// Vertex properties +fvshape_data remove_normals(const fvshape_data& shape); +fvshape_data add_normals(const fvshape_data& shape); + // Shape statistics vector fvshape_stats(const fvshape_data& shape, bool verbose = false); @@ -232,6 +268,12 @@ shape_data make_uvcylinder(const vec3i& steps = {32, 32, 32}, shape_data make_rounded_uvcylinder(const vec3i& steps = {32, 32, 32}, const vec2f& scale = {1, 1}, const vec3f& uvscale = {1, 1, 1}, float radius = 0.3f); +// Make a uv capsule +shape_data make_uvcapsule(const vec3i& steps = {32, 32, 32}, + const vec2f& scale = {1, 1}, const vec3f& uvscale = {1, 1, 1}); +// Make a uv cone +shape_data make_uvcone(const vec3i& steps = {32, 32, 32}, + const vec2f& scale = {1, 1}, const vec3f& uvscale = {1, 1, 1}); // Make a facevarying rect fvshape_data make_fvrect(const vec2i& steps = {1, 1}, @@ -243,7 +285,7 @@ fvshape_data make_fvbox(const vec3i& steps = {1, 1, 1}, fvshape_data make_fvsphere(int steps = 32, float scale = 1, float uvscale = 1); // Generate lines set along a quad. Returns lines, pos, norm, texcoord, radius. -shape_data make_lines(const vec2i& steps = {4, 65536}, +shape_data make_lines(int num = 65536, int steps = 4, const vec2f& scale = {1, 1}, const vec2f& uvscale = {1, 1}, const vec2f& radius = {0.001f, 0.001f}); @@ -260,12 +302,12 @@ shape_data make_random_points(int num = 65536, const vec3f& size = {1, 1, 1}, float uvscale = 1, float radius = 0.001f, uint64_t seed = 17); // Predefined meshes -shape_data make_monkey(float scale = 1, int subdivisions = 0); -shape_data make_quad(float scale = 1, int subdivisions = 0); -shape_data make_quady(float scale = 1, int subdivisions = 0); -shape_data make_cube(float scale = 1, int subdivisions = 0); -fvshape_data make_fvcube(float scale = 1, int subdivisions = 0); -shape_data make_geosphere(float scale = 1, int subdivisions = 0); +shape_data make_monkey(int subdivisions = 0, float scale = 1); +shape_data make_quad(int subdivisions = 0, float scale = 1); +shape_data make_quady(int subdivisions = 0, float scale = 1); +shape_data make_cube(int subdivisions = 0, float scale = 1); +fvshape_data make_fvcube(int subdivisions = 0, float scale = 1); +shape_data make_geosphere(int subdivisions = 0, float scale = 1); // Make a hair ball around a shape. // length: minimum and maximum length @@ -283,6 +325,16 @@ shape_data make_hair2(const shape_data& shape, const vec2i& steps = {8, 65536}, const vec2f& length = {0.1f, 0.1f}, const vec2f& radius = {0.001f, 0.001f}, float noise = 0, float gravity = 0.001f, int seed = 7); +// Grow hairs around a shape +shape_data make_random_hairs(const shape_data& shape, int num = 65536, + int steps = 8, const vec2f& length = {1, 1}, + const vec2f& radius = {0.01, 0.01}, float noise = 0, float gravity = 0.05, + uint64_t seed = 7); + +// Grow points around a shape +shape_data make_random_points(const shape_data& shape, int num = 65536, + float radius = 0.01f, uint64_t seed = 7); + // Convert points to small spheres and lines to small cylinders. This is // intended for making very small primitives for display in interactive // applications, so the spheres are low res. @@ -611,6 +663,8 @@ void split_facevarying(vector& split_quads, // Weld vertices within a threshold. pair, vector> weld_vertices( const vector& positions, float threshold); +pair, vector> weld_indices( + const vector& positions, float threshold); pair, vector> weld_triangles( const vector& triangles, const vector& positions, float threshold); @@ -895,15 +949,15 @@ void make_quad(vector& quads, vector& positions, vector& normals, vector& texcoords, float scale, int subdivisions); void make_quady(vector& quads, vector& positions, - vector& normals, vector& texcoords, float scale, - int subdivisions); + vector& normals, vector& texcoords, int subdivisions, + float scale); void make_cube(vector& quads, vector& positions, - vector& normals, vector& texcoords, float scale, - int subdivisions); + vector& normals, vector& texcoords, int subdivisions, + float scale); void make_fvcube(vector& quadspos, vector& quadsnorm, vector& quadstexcoord, vector& positions, - vector& normals, vector& texcoords, float scale, - int subdivisions); + vector& normals, vector& texcoords, int subdivisions, + float scale); void make_geosphere(vector& triangles, vector& positions, vector& normals, float scale, int subdivisions); @@ -965,6 +1019,56 @@ void make_heightfield(vector& quads, vector& positions, // // ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// SHAPE FUNCTIONS +// ----------------------------------------------------------------------------- +namespace yocto { + +// Shape creation +template +inline shape_data make_lines(int steps, PFunc&& position, TFunc&& tangent) { + auto shape = shape_data{}; + shape.positions = vector(steps + 1); + shape.normals = vector(steps + 1); + shape.texcoords = vector(steps + 1); + for (auto idx : range(steps + 1)) { + auto u = (float)idx / (float)steps; + shape.positions[idx] = position(u); + shape.normals[idx] = tangent(u); + shape.texcoords[idx] = {u, 0}; + } + shape.lines = vector(steps); + for (auto idx : range(steps)) shape.lines[idx] = {idx, idx + 1}; + return shape; +} +template +inline shape_data make_quads(vec2i steps, PFunc&& position, NFunc&& normal) { + auto shape = shape_data{}; + shape.positions = vector((steps.x + 1) * (steps.y + 1)); + shape.normals = vector((steps.x + 1) * (steps.y + 1)); + shape.texcoords = vector((steps.x + 1) * (steps.y + 1)); + for (auto j : range(steps.y + 1)) { + for (auto i : range(steps.x + 1)) { + auto uv = vec2f{i / (float)steps.x, j / (float)steps.y}; + auto idx = j * (steps.x + 1) + i; + shape.positions[idx] = position(uv); + shape.normals[idx] = normal(uv); + shape.texcoords[idx] = uv; + } + } + shape.quads = vector(steps.x * steps.y); + for (auto j : range(steps.y)) { + for (auto i : range(steps.x)) { + auto idx = j * steps.x + i; + shape.quads[idx] = {j * (steps.x + 1) + i, j * (steps.x + 1) + i + 1, + (j + 1) * (steps.x + 1) + i + 1, (j + 1) * (steps.x + 1) + i}; + } + } + return shape; +} + +} // namespace yocto + // ----------------------------------------------------------------------------- // SHAPE SUBDIVISION // -----------------------------------------------------------------------------