From 93093fb7ded5dc8cdccc9a276d8408e87b92364a Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Tue, 30 Jan 2024 10:55:27 +0100 Subject: [PATCH] Removal of yocto_shape.cpp --- apps/ysamples.cpp | 5 +- libs/yocto/CMakeLists.txt | 5 +- libs/yocto/yocto_modeling.h | 91 ++++++ libs/yocto/yocto_sampling.h | 54 ++++ libs/yocto/yocto_scene.cpp | 24 +- libs/yocto/yocto_shape.cpp | 586 ------------------------------------ libs/yocto/yocto_shape.h | 431 +++++++++++++++++++++++++- 7 files changed, 582 insertions(+), 614 deletions(-) delete mode 100644 libs/yocto/yocto_shape.cpp diff --git a/apps/ysamples.cpp b/apps/ysamples.cpp index d070610bc..7f45ed536 100644 --- a/apps/ysamples.cpp +++ b/apps/ysamples.cpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include #include @@ -75,7 +77,8 @@ void run(const vector& args) { auto sshape = shape_data{}; for (auto& point : points) { sshape.points.push_back((int)sshape.points.size()); - sshape.positions.push_back(eval_position(shape, point.element, point.uv)); + sshape.positions.push_back( + eval_position(shape, point.first, point.second)); sshape.radius.push_back(radius * 10); } diff --git a/libs/yocto/CMakeLists.txt b/libs/yocto/CMakeLists.txt index 4b6d79021..136d228f6 100644 --- a/libs/yocto/CMakeLists.txt +++ b/libs/yocto/CMakeLists.txt @@ -1,11 +1,10 @@ add_library(yocto STATIC yocto_math.h yocto_image.h + yocto_shape.h yocto_scene.h yocto_scene.cpp + yocto_raycasting.h yocto_raycasting.cpp yocto_sampling.h yocto_shading.h yocto_modeling.h yocto_animation.h - yocto_raycasting.h yocto_raycasting.cpp - yocto_shape.h yocto_shape.cpp - yocto_scene.h yocto_scene.cpp yocto_trace.h yocto_trace.cpp yocto_modelio.h yocto_modelio.cpp yocto_pbrtio.h yocto_pbrtio.cpp diff --git a/libs/yocto/yocto_modeling.h b/libs/yocto/yocto_modeling.h index 12c621a1d..60def9244 100644 --- a/libs/yocto/yocto_modeling.h +++ b/libs/yocto/yocto_modeling.h @@ -332,6 +332,13 @@ static pair, vector> subdivide_catmullclark_creased( const vector& creases, const vector& corners, int subdivisions = 1, bool lock_boundary = false); +// Shape subdivision +inline shape_data subdivide_shape( + const shape_data& shape, int subdivisions, bool catmullclark); + +inline fvshape_data subdivide_fvshape( + const fvshape_data& shape, int subdivisions, bool catmullclark); + } // namespace yocto // ----------------------------------------------------------------------------- @@ -2243,6 +2250,90 @@ static pair, vector> subdivide_catmullclark_illustration( return {quads, vertices}; } +// Shape subdivision +inline shape_data subdivide_shape( + const shape_data& shape, int subdivisions, bool catmullclark) { + // This should probably be re-implemented in a faster fashion, + // but how it is not obvious + if (subdivisions == 0) return shape; + auto subdivided = shape_data{}; + if (!subdivided.points.empty()) { + subdivided = shape; + } else if (!subdivided.lines.empty()) { + std::tie(std::ignore, subdivided.normals) = subdivide_lines( + shape.lines, shape.normals, subdivisions); + std::tie(std::ignore, subdivided.texcoords) = subdivide_lines( + shape.lines, shape.texcoords, subdivisions); + std::tie(std::ignore, subdivided.colors) = subdivide_lines( + shape.lines, shape.colors, subdivisions); + std::tie(std::ignore, subdivided.radius) = subdivide_lines( + subdivided.lines, shape.radius, subdivisions); + std::tie(subdivided.lines, subdivided.positions) = subdivide_lines( + shape.lines, shape.positions, subdivisions); + } else if (!subdivided.triangles.empty()) { + std::tie(std::ignore, subdivided.normals) = subdivide_triangles( + shape.triangles, shape.normals, subdivisions); + std::tie(std::ignore, subdivided.texcoords) = subdivide_triangles( + shape.triangles, shape.texcoords, subdivisions); + std::tie(std::ignore, subdivided.colors) = subdivide_triangles( + shape.triangles, shape.colors, subdivisions); + std::tie(std::ignore, subdivided.radius) = subdivide_triangles( + shape.triangles, shape.radius, subdivisions); + std::tie(subdivided.triangles, subdivided.positions) = subdivide_triangles( + shape.triangles, shape.positions, subdivisions); + } else if (!subdivided.quads.empty() && !catmullclark) { + std::tie(std::ignore, subdivided.normals) = subdivide_quads( + shape.quads, shape.normals, subdivisions); + std::tie(std::ignore, subdivided.texcoords) = subdivide_quads( + shape.quads, shape.texcoords, subdivisions); + std::tie(std::ignore, subdivided.colors) = subdivide_quads( + shape.quads, shape.colors, subdivisions); + std::tie(std::ignore, subdivided.radius) = subdivide_quads( + shape.quads, shape.radius, subdivisions); + std::tie(subdivided.quads, subdivided.positions) = subdivide_quads( + shape.quads, shape.positions, subdivisions); + } else if (!subdivided.quads.empty() && catmullclark) { + std::tie(std::ignore, subdivided.normals) = subdivide_catmullclark( + shape.quads, shape.normals, subdivisions); + std::tie(std::ignore, subdivided.texcoords) = subdivide_catmullclark( + shape.quads, shape.texcoords, subdivisions); + std::tie(std::ignore, subdivided.colors) = subdivide_catmullclark( + shape.quads, shape.colors, subdivisions); + std::tie(std::ignore, subdivided.radius) = subdivide_catmullclark( + shape.quads, shape.radius, subdivisions); + std::tie(subdivided.quads, subdivided.positions) = subdivide_catmullclark( + shape.quads, shape.positions, subdivisions); + } else { + // empty shape + } + return subdivided; +} + +// Subdivision +inline fvshape_data subdivide_fvshape( + const fvshape_data& shape, int subdivisions, bool catmullclark) { + // This should be probably re-implemeneted in a faster fashion. + if (subdivisions == 0) return shape; + auto subdivided = fvshape_data{}; + if (!catmullclark) { + std::tie(subdivided.quadspos, subdivided.positions) = subdivide_quads( + shape.quadspos, shape.positions, subdivisions); + std::tie(subdivided.quadsnorm, subdivided.normals) = subdivide_quads( + shape.quadsnorm, shape.normals, subdivisions); + std::tie(subdivided.quadstexcoord, subdivided.texcoords) = subdivide_quads( + shape.quadstexcoord, shape.texcoords, subdivisions); + } else { + std::tie(subdivided.quadspos, subdivided.positions) = + subdivide_catmullclark(shape.quadspos, shape.positions, subdivisions); + std::tie(subdivided.quadsnorm, subdivided.normals) = subdivide_catmullclark( + shape.quadsnorm, shape.normals, subdivisions); + std::tie(subdivided.quadstexcoord, subdivided.texcoords) = + subdivide_catmullclark( + shape.quadstexcoord, shape.texcoords, subdivisions, true); + } + return subdivided; +} + } // namespace yocto // ----------------------------------------------------------------------------- diff --git a/libs/yocto/yocto_sampling.h b/libs/yocto/yocto_sampling.h index 422e35c7d..f534a298e 100644 --- a/libs/yocto/yocto_sampling.h +++ b/libs/yocto/yocto_sampling.h @@ -53,6 +53,7 @@ #include #include "yocto_math.h" +#include "yocto_shape.h" // ----------------------------------------------------------------------------- // USING DIRECTIVES @@ -196,6 +197,13 @@ inline pair sample_quads( inline vector sample_quads_cdf( const vector& quads, const vector& positions); +// Shape sampling +vector sample_shape_cdf(const shape_data& shape); +pair sample_shape( + const shape_data& shape, const vector& cdf, float rn, vec2f ruv); +vector> sample_shape( + const shape_data& shape, int num_samples, uint64_t seed = 98729387); + } // namespace yocto // ----------------------------------------------------------------------------- @@ -510,6 +518,52 @@ inline vector sample_quads_cdf( return cdf; } +// Shape sampling +inline vector sample_shape_cdf(const shape_data& shape) { + if (!shape.points.empty()) { + return sample_points_cdf((int)shape.points.size()); + } else if (!shape.lines.empty()) { + return sample_lines_cdf(shape.lines, shape.positions); + } else if (!shape.triangles.empty()) { + return sample_triangles_cdf(shape.triangles, shape.positions); + } else if (!shape.quads.empty()) { + return sample_quads_cdf(shape.quads, shape.positions); + } else { + return sample_points_cdf((int)shape.positions.size()); + } +} + +inline pair sample_shape( + const shape_data& shape, const vector& cdf, float rn, vec2f ruv) { + if (!shape.points.empty()) { + auto element = sample_points(cdf, rn); + return {element, {0, 0}}; + } else if (!shape.lines.empty()) { + auto [element, u] = sample_lines(cdf, rn, ruv.x); + return {element, {u, 0}}; + } else if (!shape.triangles.empty()) { + auto [element, uv] = sample_triangles(cdf, rn, ruv); + return {element, uv}; + } else if (!shape.quads.empty()) { + auto [element, uv] = sample_quads(cdf, rn, ruv); + return {element, uv}; + } else { + auto element = sample_points(cdf, rn); + return {element, {0, 0}}; + } +} + +inline vector> sample_shape( + const shape_data& shape, int num_samples, uint64_t seed) { + auto cdf = sample_shape_cdf(shape); + auto points = vector>(num_samples); + auto rng = make_rng(seed); + for (auto& point : points) { + point = sample_shape(shape, cdf, rand1f(rng), rand2f(rng)); + } + return points; +} + } // namespace yocto // ----------------------------------------------------------------------------- diff --git a/libs/yocto/yocto_scene.cpp b/libs/yocto/yocto_scene.cpp index 36cfb36e5..5af477fc5 100644 --- a/libs/yocto/yocto_scene.cpp +++ b/libs/yocto/yocto_scene.cpp @@ -1959,9 +1959,9 @@ shape_data make_hair(const shape_data& base, vec2i steps, vec2f len, vec2f rad, auto bnorm = vector{}; auto btexcoord = vector{}; for (auto& point : points) { - bpos.push_back(eval_position(base, point.element, point.uv)); - bnorm.push_back(eval_normal(base, point.element, point.uv)); - btexcoord.push_back(eval_texcoord(base, point.element, point.uv)); + bpos.push_back(eval_position(base, point.first, point.second)); + bnorm.push_back(eval_normal(base, point.first, point.second)); + btexcoord.push_back(eval_texcoord(base, point.first, point.second)); } auto rng = make_rng(seed, 3); @@ -2031,9 +2031,9 @@ shape_data make_random_points( auto points = make_points(num, 1, radius); for (auto idx : range(num)) { points.positions[idx] = eval_position( - shape, samples[idx].element, samples[idx].uv); + shape, samples[idx].first, samples[idx].second); points.normals[idx] = eval_normal( - shape, samples[idx].element, samples[idx].uv); + shape, samples[idx].first, samples[idx].second); } return points; } @@ -2046,9 +2046,11 @@ shape_data make_random_hairs(const shape_data& shape, int num, int steps, 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)); + auto position = eval_position( + shape, samples[idx].first, samples[idx].second); + auto direction = eval_normal( + shape, samples[idx].first, samples[idx].second); + 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; @@ -2073,9 +2075,9 @@ shape_data make_hair2(const shape_data& base, vec2i steps, vec2f len, auto bnormals = vector{}; auto btexcoord = vector{}; for (auto& point : points) { - bpositions.push_back(eval_position(base, point.element, point.uv)); - bnormals.push_back(eval_normal(base, point.element, point.uv)); - btexcoord.push_back(eval_texcoord(base, point.element, point.uv)); + bpositions.push_back(eval_position(base, point.first, point.second)); + bnormals.push_back(eval_normal(base, point.first, point.second)); + btexcoord.push_back(eval_texcoord(base, point.first, point.second)); } auto shape = make_lines(steps.y, steps.x, {1, 1}, {1, 1}, radius); diff --git a/libs/yocto/yocto_shape.cpp b/libs/yocto/yocto_shape.cpp deleted file mode 100644 index bc3cc8638..000000000 --- a/libs/yocto/yocto_shape.cpp +++ /dev/null @@ -1,586 +0,0 @@ -// -// Implementation for Yocto/Shape -// - -// -// LICENSE: -// -// Copyright (c) 2016 -- 2022 Fabio Pellacini -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -// - -// ----------------------------------------------------------------------------- -// INCLUDES -// ----------------------------------------------------------------------------- - -#include "yocto_shape.h" - -#include -#include -#include - -#include "yocto_modeling.h" -#include "yocto_modelio.h" -#include "yocto_sampling.h" - -// ----------------------------------------------------------------------------- -// IMPLEMENTATION FO SHAPE PROPERTIES -// ----------------------------------------------------------------------------- -namespace yocto { - -// Interpolate vertex data -vec3f eval_position(const shape_data& shape, int element, vec2f uv) { - if (!shape.points.empty()) { - auto& point = shape.points[element]; - return shape.positions[point]; - } else if (!shape.lines.empty()) { - auto& line = shape.lines[element]; - return interpolate_line( - shape.positions[line.x], shape.positions[line.y], uv.x); - } else if (!shape.triangles.empty()) { - auto& triangle = shape.triangles[element]; - return interpolate_triangle(shape.positions[triangle.x], - shape.positions[triangle.y], shape.positions[triangle.z], uv); - } else if (!shape.quads.empty()) { - auto& quad = shape.quads[element]; - return interpolate_quad(shape.positions[quad.x], shape.positions[quad.y], - shape.positions[quad.z], shape.positions[quad.w], uv); - } else { - return {0, 0, 0}; - } -} - -vec3f eval_normal(const shape_data& shape, int element, vec2f uv) { - if (shape.normals.empty()) return eval_element_normal(shape, element); - if (!shape.points.empty()) { - auto& point = shape.points[element]; - return normalize(shape.normals[point]); - } else if (!shape.lines.empty()) { - auto& line = shape.lines[element]; - return normalize( - interpolate_line(shape.normals[line.x], shape.normals[line.y], uv.x)); - } else if (!shape.triangles.empty()) { - auto& triangle = shape.triangles[element]; - return normalize(interpolate_triangle(shape.normals[triangle.x], - shape.normals[triangle.y], shape.normals[triangle.z], uv)); - } else if (!shape.quads.empty()) { - auto& quad = shape.quads[element]; - return normalize( - interpolate_quad(shape.normals[quad.x], shape.normals[quad.y], - shape.normals[quad.z], shape.normals[quad.w], uv)); - } else { - return {0, 0, 1}; - } -} - -vec3f eval_tangent(const shape_data& shape, int element, vec2f uv) { - return eval_normal(shape, element, uv); -} - -vec2f eval_texcoord(const shape_data& shape, int element, vec2f uv) { - if (shape.texcoords.empty()) return uv; - if (!shape.points.empty()) { - auto& point = shape.points[element]; - return shape.texcoords[point]; - } else if (!shape.lines.empty()) { - auto& line = shape.lines[element]; - return interpolate_line( - shape.texcoords[line.x], shape.texcoords[line.y], uv.x); - } else if (!shape.triangles.empty()) { - auto& triangle = shape.triangles[element]; - return interpolate_triangle(shape.texcoords[triangle.x], - shape.texcoords[triangle.y], shape.texcoords[triangle.z], uv); - } else if (!shape.quads.empty()) { - auto& quad = shape.quads[element]; - return interpolate_quad(shape.texcoords[quad.x], shape.texcoords[quad.y], - shape.texcoords[quad.z], shape.texcoords[quad.w], uv); - } else { - return uv; - } -} - -vec4f eval_color(const shape_data& shape, int element, vec2f uv) { - if (shape.colors.empty()) return {1, 1, 1, 1}; - if (!shape.points.empty()) { - auto& point = shape.points[element]; - return shape.colors[point]; - } else if (!shape.lines.empty()) { - auto& line = shape.lines[element]; - return interpolate_line(shape.colors[line.x], shape.colors[line.y], uv.x); - } else if (!shape.triangles.empty()) { - auto& triangle = shape.triangles[element]; - return interpolate_triangle(shape.colors[triangle.x], - shape.colors[triangle.y], shape.colors[triangle.z], uv); - } else if (!shape.quads.empty()) { - auto& quad = shape.quads[element]; - return interpolate_quad(shape.colors[quad.x], shape.colors[quad.y], - shape.colors[quad.z], shape.colors[quad.w], uv); - } else { - return {1, 1, 1, 1}; - } -} - -float eval_radius(const shape_data& shape, int element, vec2f uv) { - if (shape.radius.empty()) return 0; - if (!shape.points.empty()) { - auto& point = shape.points[element]; - return shape.radius[point]; - } else if (!shape.lines.empty()) { - auto& line = shape.lines[element]; - return interpolate_line(shape.radius[line.x], shape.radius[line.y], uv.x); - } else if (!shape.triangles.empty()) { - auto& triangle = shape.triangles[element]; - return interpolate_triangle(shape.radius[triangle.x], - shape.radius[triangle.y], shape.radius[triangle.z], uv); - } else if (!shape.quads.empty()) { - auto& quad = shape.quads[element]; - return interpolate_quad(shape.radius[quad.x], shape.radius[quad.y], - shape.radius[quad.z], shape.radius[quad.w], uv); - } else { - return 0; - } -} - -// Evaluate element normals -vec3f eval_element_normal(const shape_data& shape, int element) { - if (!shape.points.empty()) { - return {0, 0, 1}; - } else if (!shape.lines.empty()) { - auto& line = shape.lines[element]; - return line_tangent(shape.positions[line.x], shape.positions[line.y]); - } else if (!shape.triangles.empty()) { - auto& triangle = shape.triangles[element]; - return triangle_normal(shape.positions[triangle.x], - shape.positions[triangle.y], shape.positions[triangle.z]); - } else if (!shape.quads.empty()) { - auto& quad = shape.quads[element]; - return quad_normal(shape.positions[quad.x], shape.positions[quad.y], - shape.positions[quad.z], shape.positions[quad.w]); - } else { - return {0, 0, 0}; - } -} - -// Compute per-vertex normals/tangents for lines/triangles/quads. -vector compute_normals(const shape_data& shape) { - if (!shape.points.empty()) { - return vector(shape.positions.size(), {0, 0, 1}); - } else if (!shape.lines.empty()) { - return lines_tangents(shape.lines, shape.positions); - } else if (!shape.triangles.empty()) { - return triangles_normals(shape.triangles, shape.positions); - } else if (!shape.quads.empty()) { - return quads_normals(shape.quads, shape.positions); - } else { - return vector(shape.positions.size(), {0, 0, 1}); - } -} -void compute_normals(vector& normals, const shape_data& shape) { - if (!shape.points.empty()) { - normals.assign(shape.positions.size(), {0, 0, 1}); - } else if (!shape.lines.empty()) { - lines_tangents(normals, shape.lines, shape.positions); - } else if (!shape.triangles.empty()) { - triangles_normals(normals, shape.triangles, shape.positions); - } else if (!shape.quads.empty()) { - quads_normals(normals, shape.quads, shape.positions); - } else { - normals.assign(shape.positions.size(), {0, 0, 1}); - } -} - -// Shape sampling -vector sample_shape_cdf(const shape_data& shape) { - if (!shape.points.empty()) { - return sample_points_cdf((int)shape.points.size()); - } else if (!shape.lines.empty()) { - return sample_lines_cdf(shape.lines, shape.positions); - } else if (!shape.triangles.empty()) { - return sample_triangles_cdf(shape.triangles, shape.positions); - } else if (!shape.quads.empty()) { - return sample_quads_cdf(shape.quads, shape.positions); - } else { - return sample_points_cdf((int)shape.positions.size()); - } -} - -shape_point sample_shape( - const shape_data& shape, const vector& cdf, float rn, vec2f ruv) { - if (!shape.points.empty()) { - auto element = sample_points(cdf, rn); - return {element, {0, 0}}; - } else if (!shape.lines.empty()) { - auto [element, u] = sample_lines(cdf, rn, ruv.x); - return {element, {u, 0}}; - } else if (!shape.triangles.empty()) { - auto [element, uv] = sample_triangles(cdf, rn, ruv); - return {element, uv}; - } else if (!shape.quads.empty()) { - auto [element, uv] = sample_quads(cdf, rn, ruv); - return {element, uv}; - } else { - auto element = sample_points(cdf, rn); - return {element, {0, 0}}; - } -} - -vector sample_shape( - const shape_data& shape, int num_samples, uint64_t seed) { - auto cdf = sample_shape_cdf(shape); - auto points = vector(num_samples); - auto rng = make_rng(seed); - for (auto& point : points) { - point = sample_shape(shape, cdf, rand1f(rng), rand2f(rng)); - } - return points; -} - -// Conversions -shape_data quads_to_triangles(const shape_data& shape) { - auto result = shape; - if (!shape.quads.empty()) { - result.triangles = quads_to_triangles(shape.quads); - result.quads = {}; - } - return result; -} -void quads_to_triangles_inplace(shape_data& shape) { - if (shape.quads.empty()) return; - shape.triangles = quads_to_triangles(shape.quads); - shape.quads = {}; -} - -// Subdivision -shape_data subdivide_shape( - const shape_data& shape, int subdivisions, bool catmullclark) { - // This should probably be re-implemented in a faster fashion, - // but how it is not obvious - if (subdivisions == 0) return shape; - auto subdivided = shape_data{}; - if (!subdivided.points.empty()) { - subdivided = shape; - } else if (!subdivided.lines.empty()) { - std::tie(std::ignore, subdivided.normals) = subdivide_lines( - shape.lines, shape.normals, subdivisions); - std::tie(std::ignore, subdivided.texcoords) = subdivide_lines( - shape.lines, shape.texcoords, subdivisions); - std::tie(std::ignore, subdivided.colors) = subdivide_lines( - shape.lines, shape.colors, subdivisions); - std::tie(std::ignore, subdivided.radius) = subdivide_lines( - subdivided.lines, shape.radius, subdivisions); - std::tie(subdivided.lines, subdivided.positions) = subdivide_lines( - shape.lines, shape.positions, subdivisions); - } else if (!subdivided.triangles.empty()) { - std::tie(std::ignore, subdivided.normals) = subdivide_triangles( - shape.triangles, shape.normals, subdivisions); - std::tie(std::ignore, subdivided.texcoords) = subdivide_triangles( - shape.triangles, shape.texcoords, subdivisions); - std::tie(std::ignore, subdivided.colors) = subdivide_triangles( - shape.triangles, shape.colors, subdivisions); - std::tie(std::ignore, subdivided.radius) = subdivide_triangles( - shape.triangles, shape.radius, subdivisions); - std::tie(subdivided.triangles, subdivided.positions) = subdivide_triangles( - shape.triangles, shape.positions, subdivisions); - } else if (!subdivided.quads.empty() && !catmullclark) { - std::tie(std::ignore, subdivided.normals) = subdivide_quads( - shape.quads, shape.normals, subdivisions); - std::tie(std::ignore, subdivided.texcoords) = subdivide_quads( - shape.quads, shape.texcoords, subdivisions); - std::tie(std::ignore, subdivided.colors) = subdivide_quads( - shape.quads, shape.colors, subdivisions); - std::tie(std::ignore, subdivided.radius) = subdivide_quads( - shape.quads, shape.radius, subdivisions); - std::tie(subdivided.quads, subdivided.positions) = subdivide_quads( - shape.quads, shape.positions, subdivisions); - } else if (!subdivided.quads.empty() && catmullclark) { - std::tie(std::ignore, subdivided.normals) = subdivide_catmullclark( - shape.quads, shape.normals, subdivisions); - std::tie(std::ignore, subdivided.texcoords) = subdivide_catmullclark( - shape.quads, shape.texcoords, subdivisions); - std::tie(std::ignore, subdivided.colors) = subdivide_catmullclark( - shape.quads, shape.colors, subdivisions); - std::tie(std::ignore, subdivided.radius) = subdivide_catmullclark( - shape.quads, shape.radius, subdivisions); - std::tie(subdivided.quads, subdivided.positions) = subdivide_catmullclark( - shape.quads, shape.positions, subdivisions); - } else { - // empty 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; -} - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// IMPLEMENTATION FOR FVSHAPE PROPERTIES -// ----------------------------------------------------------------------------- -namespace yocto { - -// Interpolate vertex data -vec3f eval_position(const fvshape_data& shape, int element, vec2f uv) { - if (!shape.quadspos.empty()) { - auto& quad = shape.quadspos[element]; - return interpolate_quad(shape.positions[quad.x], shape.positions[quad.y], - shape.positions[quad.z], shape.positions[quad.w], uv); - } else { - return {0, 0, 0}; - } -} - -vec3f eval_normal(const fvshape_data& shape, int element, vec2f uv) { - if (shape.normals.empty()) return eval_element_normal(shape, element); - if (!shape.quadspos.empty()) { - auto& quad = shape.quadsnorm[element]; - return normalize( - interpolate_quad(shape.normals[quad.x], shape.normals[quad.y], - shape.normals[quad.z], shape.normals[quad.w], uv)); - } else { - return {0, 0, 1}; - } -} - -vec2f eval_texcoord(const fvshape_data& shape, int element, vec2f uv) { - if (shape.texcoords.empty()) return uv; - if (!shape.quadspos.empty()) { - auto& quad = shape.quadstexcoord[element]; - return interpolate_quad(shape.texcoords[quad.x], shape.texcoords[quad.y], - shape.texcoords[quad.z], shape.texcoords[quad.w], uv); - } else { - return uv; - } -} - -// Evaluate element normals -vec3f eval_element_normal(const fvshape_data& shape, int element) { - if (!shape.quadspos.empty()) { - auto& quad = shape.quadspos[element]; - return quad_normal(shape.positions[quad.x], shape.positions[quad.y], - shape.positions[quad.z], shape.positions[quad.w]); - } else { - return {0, 0, 0}; - } -} - -// Compute per-vertex normals/tangents for lines/triangles/quads. -vector compute_normals(const fvshape_data& shape) { - if (!shape.quadspos.empty()) { - return quads_normals(shape.quadspos, shape.positions); - } else { - return vector(shape.positions.size(), {0, 0, 1}); - } -} -void compute_normals(vector& normals, const fvshape_data& shape) { - if (!shape.quadspos.empty()) { - quads_normals(normals, shape.quadspos, shape.positions); - } else { - normals.assign(shape.positions.size(), {0, 0, 1}); - } -} - -// Convert face varying data to single primitives. Returns the quads indices -// and filled vectors for pos, norm and texcoord. -void split_facevarying(vector& split_quads, - vector& split_positions, vector& split_normals, - vector& split_texcoords, const vector& quadspos, - const vector& quadsnorm, const vector& quadstexcoord, - const vector& positions, const vector& normals, - const vector& texcoords) { - // make vertices - auto vertices = vector{}; - vertices.reserve(quadspos.size() * 4); - for (auto fid : range(quadspos.size())) { - for (auto c : range(4)) { - auto vertex = vec3i{ - (&quadspos[fid].x)[c], - (!quadsnorm.empty()) ? (&quadsnorm[fid].x)[c] : -1, - (!quadstexcoord.empty()) ? (&quadstexcoord[fid].x)[c] : -1, - }; - vertices.push_back(vertex); - } - } - - // sort vertices and remove duplicates - auto compare_vertices = [](vec3i a, vec3i b) { - return a.x < b.x || (a.x == b.x && a.y < b.y) || - (a.x == b.x && a.y == b.y && a.z < b.z); - }; - std::sort(vertices.begin(), vertices.end(), compare_vertices); - vertices.erase(std::unique(vertices.begin(), vertices.end()), vertices.end()); - - // fill vert data - if (!positions.empty()) { - split_positions.resize(vertices.size()); - for (auto&& [index, vertex] : enumerate(vertices)) { - split_positions[index] = positions[vertex.x]; - } - } else { - split_positions.clear(); - } - if (!normals.empty()) { - split_normals.resize(vertices.size()); - for (auto&& [index, vertex] : enumerate(vertices)) { - split_normals[index] = normals[vertex.y]; - } - } else { - split_normals.clear(); - } - if (!texcoords.empty()) { - split_texcoords.resize(vertices.size()); - for (auto&& [index, vertex] : enumerate(vertices)) { - split_texcoords[index] = texcoords[vertex.z]; - } - } else { - split_texcoords.clear(); - } -} - -// Conversions -shape_data fvshape_to_shape(const fvshape_data& fvshape, bool as_triangles) { - auto shape = shape_data{}; - split_facevarying(shape.quads, shape.positions, shape.normals, - shape.texcoords, fvshape.quadspos, fvshape.quadsnorm, - fvshape.quadstexcoord, fvshape.positions, fvshape.normals, - fvshape.texcoords); - return shape; -} -fvshape_data shape_to_fvshape(const shape_data& shape) { - if (!shape.points.empty() || !shape.lines.empty()) - throw std::invalid_argument{"cannot convert shape"}; - auto fvshape = fvshape_data{}; - fvshape.positions = shape.positions; - fvshape.normals = shape.normals; - fvshape.texcoords = shape.texcoords; - fvshape.quadspos = !shape.quads.empty() ? shape.quads - : triangles_to_quads(shape.triangles); - fvshape.quadsnorm = !shape.normals.empty() ? fvshape.quadspos - : vector{}; - fvshape.quadstexcoord = !shape.texcoords.empty() ? fvshape.quadspos - : vector{}; - return fvshape; -} - -// Subdivision -fvshape_data subdivide_fvshape( - const fvshape_data& shape, int subdivisions, bool catmullclark) { - // This should be probably re-implemeneted in a faster fashion. - if (subdivisions == 0) return shape; - auto subdivided = fvshape_data{}; - if (!catmullclark) { - std::tie(subdivided.quadspos, subdivided.positions) = subdivide_quads( - shape.quadspos, shape.positions, subdivisions); - std::tie(subdivided.quadsnorm, subdivided.normals) = subdivide_quads( - shape.quadsnorm, shape.normals, subdivisions); - std::tie(subdivided.quadstexcoord, subdivided.texcoords) = subdivide_quads( - shape.quadstexcoord, shape.texcoords, subdivisions); - } else { - std::tie(subdivided.quadspos, subdivided.positions) = - subdivide_catmullclark(shape.quadspos, shape.positions, subdivisions); - std::tie(subdivided.quadsnorm, subdivided.normals) = subdivide_catmullclark( - shape.quadsnorm, shape.normals, subdivisions); - std::tie(subdivided.quadstexcoord, subdivided.texcoords) = - subdivide_catmullclark( - shape.quadstexcoord, shape.texcoords, subdivisions, true); - } - return subdivided; -} - -// Transform shape -fvshape_data transform_fvshape( - const fvshape_data& shape, const frame3f& frame, 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); - return transformed; -} -fvshape_data scale_fvshape( - const fvshape_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; -} -fvshape_data scale_fvshape(fvshape_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; -} - -// Vertex properties -fvshape_data remove_normals(const fvshape_data& shape) { - auto transformed = shape; - transformed.quadsnorm = {}; - transformed.normals = {}; - return transformed; -} -fvshape_data add_normals(const fvshape_data& shape) { - auto transformed = shape; - transformed.quadsnorm = transformed.quadspos; - transformed.normals = compute_normals(shape); - return transformed; -} - -} // namespace yocto diff --git a/libs/yocto/yocto_shape.h b/libs/yocto/yocto_shape.h index ed59df8ef..1d773f585 100644 --- a/libs/yocto/yocto_shape.h +++ b/libs/yocto/yocto_shape.h @@ -243,19 +243,6 @@ vec3f eval_element_normal(const shape_data& shape, int element); vector compute_normals(const shape_data& shape); void compute_normals(vector& normals, const shape_data& shape); -// An unevaluated location on a shape -struct shape_point { - int element = 0; - vec2f uv = {0, 0}; -}; - -// Shape sampling -vector sample_shape_cdf(const shape_data& shape); -shape_point sample_shape( - const shape_data& shape, const vector& cdf, float rn, vec2f ruv); -vector sample_shape( - const shape_data& shape, int num_samples, uint64_t seed = 98729387); - // Conversions shape_data quads_to_triangles(const shape_data& shape); void quads_to_triangles_inplace(shape_data& shape); @@ -748,6 +735,424 @@ inline shape_data make_quads(vec2i steps, PFunc&& position, NFunc&& normal) { } // namespace yocto +// ----------------------------------------------------------------------------- +// IMPLEMENTATION FO SHAPE PROPERTIES +// ----------------------------------------------------------------------------- +namespace yocto { + +// Interpolate vertex data +inline vec3f eval_position(const shape_data& shape, int element, vec2f uv) { + if (!shape.points.empty()) { + auto& point = shape.points[element]; + return shape.positions[point]; + } else if (!shape.lines.empty()) { + auto& line = shape.lines[element]; + return interpolate_line( + shape.positions[line.x], shape.positions[line.y], uv.x); + } else if (!shape.triangles.empty()) { + auto& triangle = shape.triangles[element]; + return interpolate_triangle(shape.positions[triangle.x], + shape.positions[triangle.y], shape.positions[triangle.z], uv); + } else if (!shape.quads.empty()) { + auto& quad = shape.quads[element]; + return interpolate_quad(shape.positions[quad.x], shape.positions[quad.y], + shape.positions[quad.z], shape.positions[quad.w], uv); + } else { + return {0, 0, 0}; + } +} + +inline vec3f eval_normal(const shape_data& shape, int element, vec2f uv) { + if (shape.normals.empty()) return eval_element_normal(shape, element); + if (!shape.points.empty()) { + auto& point = shape.points[element]; + return normalize(shape.normals[point]); + } else if (!shape.lines.empty()) { + auto& line = shape.lines[element]; + return normalize( + interpolate_line(shape.normals[line.x], shape.normals[line.y], uv.x)); + } else if (!shape.triangles.empty()) { + auto& triangle = shape.triangles[element]; + return normalize(interpolate_triangle(shape.normals[triangle.x], + shape.normals[triangle.y], shape.normals[triangle.z], uv)); + } else if (!shape.quads.empty()) { + auto& quad = shape.quads[element]; + return normalize( + interpolate_quad(shape.normals[quad.x], shape.normals[quad.y], + shape.normals[quad.z], shape.normals[quad.w], uv)); + } else { + return {0, 0, 1}; + } +} + +inline vec3f eval_tangent(const shape_data& shape, int element, vec2f uv) { + return eval_normal(shape, element, uv); +} + +inline vec2f eval_texcoord(const shape_data& shape, int element, vec2f uv) { + if (shape.texcoords.empty()) return uv; + if (!shape.points.empty()) { + auto& point = shape.points[element]; + return shape.texcoords[point]; + } else if (!shape.lines.empty()) { + auto& line = shape.lines[element]; + return interpolate_line( + shape.texcoords[line.x], shape.texcoords[line.y], uv.x); + } else if (!shape.triangles.empty()) { + auto& triangle = shape.triangles[element]; + return interpolate_triangle(shape.texcoords[triangle.x], + shape.texcoords[triangle.y], shape.texcoords[triangle.z], uv); + } else if (!shape.quads.empty()) { + auto& quad = shape.quads[element]; + return interpolate_quad(shape.texcoords[quad.x], shape.texcoords[quad.y], + shape.texcoords[quad.z], shape.texcoords[quad.w], uv); + } else { + return uv; + } +} + +inline vec4f eval_color(const shape_data& shape, int element, vec2f uv) { + if (shape.colors.empty()) return {1, 1, 1, 1}; + if (!shape.points.empty()) { + auto& point = shape.points[element]; + return shape.colors[point]; + } else if (!shape.lines.empty()) { + auto& line = shape.lines[element]; + return interpolate_line(shape.colors[line.x], shape.colors[line.y], uv.x); + } else if (!shape.triangles.empty()) { + auto& triangle = shape.triangles[element]; + return interpolate_triangle(shape.colors[triangle.x], + shape.colors[triangle.y], shape.colors[triangle.z], uv); + } else if (!shape.quads.empty()) { + auto& quad = shape.quads[element]; + return interpolate_quad(shape.colors[quad.x], shape.colors[quad.y], + shape.colors[quad.z], shape.colors[quad.w], uv); + } else { + return {1, 1, 1, 1}; + } +} + +inline float eval_radius(const shape_data& shape, int element, vec2f uv) { + if (shape.radius.empty()) return 0; + if (!shape.points.empty()) { + auto& point = shape.points[element]; + return shape.radius[point]; + } else if (!shape.lines.empty()) { + auto& line = shape.lines[element]; + return interpolate_line(shape.radius[line.x], shape.radius[line.y], uv.x); + } else if (!shape.triangles.empty()) { + auto& triangle = shape.triangles[element]; + return interpolate_triangle(shape.radius[triangle.x], + shape.radius[triangle.y], shape.radius[triangle.z], uv); + } else if (!shape.quads.empty()) { + auto& quad = shape.quads[element]; + return interpolate_quad(shape.radius[quad.x], shape.radius[quad.y], + shape.radius[quad.z], shape.radius[quad.w], uv); + } else { + return 0; + } +} + +// Evaluate element normals +inline vec3f eval_element_normal(const shape_data& shape, int element) { + if (!shape.points.empty()) { + return {0, 0, 1}; + } else if (!shape.lines.empty()) { + auto& line = shape.lines[element]; + return line_tangent(shape.positions[line.x], shape.positions[line.y]); + } else if (!shape.triangles.empty()) { + auto& triangle = shape.triangles[element]; + return triangle_normal(shape.positions[triangle.x], + shape.positions[triangle.y], shape.positions[triangle.z]); + } else if (!shape.quads.empty()) { + auto& quad = shape.quads[element]; + return quad_normal(shape.positions[quad.x], shape.positions[quad.y], + shape.positions[quad.z], shape.positions[quad.w]); + } else { + return {0, 0, 0}; + } +} + +// Compute per-vertex normals/tangents for lines/triangles/quads. +inline vector compute_normals(const shape_data& shape) { + if (!shape.points.empty()) { + return vector(shape.positions.size(), {0, 0, 1}); + } else if (!shape.lines.empty()) { + return lines_tangents(shape.lines, shape.positions); + } else if (!shape.triangles.empty()) { + return triangles_normals(shape.triangles, shape.positions); + } else if (!shape.quads.empty()) { + return quads_normals(shape.quads, shape.positions); + } else { + return vector(shape.positions.size(), {0, 0, 1}); + } +} +inline void compute_normals(vector& normals, const shape_data& shape) { + if (!shape.points.empty()) { + normals.assign(shape.positions.size(), {0, 0, 1}); + } else if (!shape.lines.empty()) { + lines_tangents(normals, shape.lines, shape.positions); + } else if (!shape.triangles.empty()) { + triangles_normals(normals, shape.triangles, shape.positions); + } else if (!shape.quads.empty()) { + quads_normals(normals, shape.quads, shape.positions); + } else { + normals.assign(shape.positions.size(), {0, 0, 1}); + } +} + +// Conversions +inline shape_data quads_to_triangles(const shape_data& shape) { + auto result = shape; + if (!shape.quads.empty()) { + result.triangles = quads_to_triangles(shape.quads); + result.quads = {}; + } + return result; +} +inline void quads_to_triangles_inplace(shape_data& shape) { + if (shape.quads.empty()) return; + shape.triangles = quads_to_triangles(shape.quads); + shape.quads = {}; +} + +// Transform shape +inline shape_data transform_shape( + const shape_data& shape, const frame3f& frame, bool non_rigid) { + return transform_shape(shape, frame, 1, non_rigid); +} +inline 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; +} +inline 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; +} +inline 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 +inline shape_data remove_normals(const shape_data& shape) { + auto transformed = shape; + transformed.normals = {}; + return transformed; +} +inline shape_data add_normals(const shape_data& shape) { + auto transformed = shape; + transformed.normals = compute_normals(shape); + return transformed; +} + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// IMPLEMENTATION FOR FVSHAPE PROPERTIES +// ----------------------------------------------------------------------------- +namespace yocto { + +// Interpolate vertex data +inline vec3f eval_position(const fvshape_data& shape, int element, vec2f uv) { + if (!shape.quadspos.empty()) { + auto& quad = shape.quadspos[element]; + return interpolate_quad(shape.positions[quad.x], shape.positions[quad.y], + shape.positions[quad.z], shape.positions[quad.w], uv); + } else { + return {0, 0, 0}; + } +} + +inline vec3f eval_normal(const fvshape_data& shape, int element, vec2f uv) { + if (shape.normals.empty()) return eval_element_normal(shape, element); + if (!shape.quadspos.empty()) { + auto& quad = shape.quadsnorm[element]; + return normalize( + interpolate_quad(shape.normals[quad.x], shape.normals[quad.y], + shape.normals[quad.z], shape.normals[quad.w], uv)); + } else { + return {0, 0, 1}; + } +} + +inline vec2f eval_texcoord(const fvshape_data& shape, int element, vec2f uv) { + if (shape.texcoords.empty()) return uv; + if (!shape.quadspos.empty()) { + auto& quad = shape.quadstexcoord[element]; + return interpolate_quad(shape.texcoords[quad.x], shape.texcoords[quad.y], + shape.texcoords[quad.z], shape.texcoords[quad.w], uv); + } else { + return uv; + } +} + +// Evaluate element normals +inline vec3f eval_element_normal(const fvshape_data& shape, int element) { + if (!shape.quadspos.empty()) { + auto& quad = shape.quadspos[element]; + return quad_normal(shape.positions[quad.x], shape.positions[quad.y], + shape.positions[quad.z], shape.positions[quad.w]); + } else { + return {0, 0, 0}; + } +} + +// Compute per-vertex normals/tangents for lines/triangles/quads. +inline vector compute_normals(const fvshape_data& shape) { + if (!shape.quadspos.empty()) { + return quads_normals(shape.quadspos, shape.positions); + } else { + return vector(shape.positions.size(), {0, 0, 1}); + } +} +inline void compute_normals(vector& normals, const fvshape_data& shape) { + if (!shape.quadspos.empty()) { + quads_normals(normals, shape.quadspos, shape.positions); + } else { + normals.assign(shape.positions.size(), {0, 0, 1}); + } +} + +// Convert face varying data to single primitives. Returns the quads indices +// and filled vectors for pos, norm and texcoord. +inline void split_facevarying(vector& split_quads, + vector& split_positions, vector& split_normals, + vector& split_texcoords, const vector& quadspos, + const vector& quadsnorm, const vector& quadstexcoord, + const vector& positions, const vector& normals, + const vector& texcoords) { + // make vertices + auto vertices = vector{}; + vertices.reserve(quadspos.size() * 4); + for (auto fid : range(quadspos.size())) { + for (auto c : range(4)) { + auto vertex = vec3i{ + (&quadspos[fid].x)[c], + (!quadsnorm.empty()) ? (&quadsnorm[fid].x)[c] : -1, + (!quadstexcoord.empty()) ? (&quadstexcoord[fid].x)[c] : -1, + }; + vertices.push_back(vertex); + } + } + + // sort vertices and remove duplicates + auto compare_vertices = [](vec3i a, vec3i b) { + return a.x < b.x || (a.x == b.x && a.y < b.y) || + (a.x == b.x && a.y == b.y && a.z < b.z); + }; + std::sort(vertices.begin(), vertices.end(), compare_vertices); + vertices.erase(std::unique(vertices.begin(), vertices.end()), vertices.end()); + + // fill vert data + if (!positions.empty()) { + split_positions.resize(vertices.size()); + for (auto&& [index, vertex] : enumerate(vertices)) { + split_positions[index] = positions[vertex.x]; + } + } else { + split_positions.clear(); + } + if (!normals.empty()) { + split_normals.resize(vertices.size()); + for (auto&& [index, vertex] : enumerate(vertices)) { + split_normals[index] = normals[vertex.y]; + } + } else { + split_normals.clear(); + } + if (!texcoords.empty()) { + split_texcoords.resize(vertices.size()); + for (auto&& [index, vertex] : enumerate(vertices)) { + split_texcoords[index] = texcoords[vertex.z]; + } + } else { + split_texcoords.clear(); + } +} + +// Conversions +inline shape_data fvshape_to_shape( + const fvshape_data& fvshape, bool as_triangles) { + auto shape = shape_data{}; + split_facevarying(shape.quads, shape.positions, shape.normals, + shape.texcoords, fvshape.quadspos, fvshape.quadsnorm, + fvshape.quadstexcoord, fvshape.positions, fvshape.normals, + fvshape.texcoords); + return shape; +} +inline fvshape_data shape_to_fvshape(const shape_data& shape) { + if (!shape.points.empty() || !shape.lines.empty()) + throw std::invalid_argument{"cannot convert shape"}; + auto fvshape = fvshape_data{}; + fvshape.positions = shape.positions; + fvshape.normals = shape.normals; + fvshape.texcoords = shape.texcoords; + fvshape.quadspos = !shape.quads.empty() ? shape.quads + : triangles_to_quads(shape.triangles); + fvshape.quadsnorm = !shape.normals.empty() ? fvshape.quadspos + : vector{}; + fvshape.quadstexcoord = !shape.texcoords.empty() ? fvshape.quadspos + : vector{}; + return fvshape; +} + +// Transform shape +inline fvshape_data transform_fvshape( + const fvshape_data& shape, const frame3f& frame, 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); + return transformed; +} +inline fvshape_data scale_fvshape( + const fvshape_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; +} +inline fvshape_data scale_fvshape( + fvshape_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; +} + +// Vertex properties +inline fvshape_data remove_normals(const fvshape_data& shape) { + auto transformed = shape; + transformed.quadsnorm = {}; + transformed.normals = {}; + return transformed; +} +inline fvshape_data add_normals(const fvshape_data& shape) { + auto transformed = shape; + transformed.quadsnorm = transformed.quadspos; + transformed.normals = compute_normals(shape); + return transformed; +} + +} // namespace yocto + // ----------------------------------------------------------------------------- // CUDA SUPPORT // -----------------------------------------------------------------------------