From d03fa144791588c376c4b61777e4825f2c9692d4 Mon Sep 17 00:00:00 2001 From: Fabio Pellacini Date: Tue, 30 Jan 2024 09:58:44 +0100 Subject: [PATCH] Started removing geometry --- libs/yocto/yocto_geometry.h | 518 ----------------------------------- libs/yocto/yocto_shape.h | 532 ++++++++++++++++++++++++++++++++++++ 2 files changed, 532 insertions(+), 518 deletions(-) diff --git a/libs/yocto/yocto_geometry.h b/libs/yocto/yocto_geometry.h index 977908f4e..9c4b2c4e0 100644 --- a/libs/yocto/yocto_geometry.h +++ b/libs/yocto/yocto_geometry.h @@ -206,76 +206,6 @@ inline bbox3f capsule_bounds(vec3f p0, vec3f p1, float r0, float r1); } // namespace yocto -// ----------------------------------------------------------------------------- -// GEOMETRY UTILITIES -// ----------------------------------------------------------------------------- -namespace yocto { - -// Line properties. -inline vec3f line_tangent(vec3f p0, vec3f p1); -inline float line_length(vec3f p0, vec3f p1); - -// Triangle properties. -inline vec3f triangle_normal(vec3f p0, vec3f p1, vec3f p2); -inline float triangle_area(vec3f p0, vec3f p1, vec3f p2); - -// Quad properties. -inline vec3f quad_normal(vec3f p0, vec3f p1, vec3f p2, vec3f p3); -inline float quad_area(vec3f p0, vec3f p1, vec3f p2, vec3f p3); - -// Interpolates values over a line parameterized from a to b by u. Same as lerp. -template -inline T interpolate_line(const T& p0, const T& p1, float u); - -// Interpolates values over a triangle parameterized by u and v along the -// (p1-p0) and (p2-p0) directions. Same as barycentric interpolation. -template -inline T interpolate_triangle(const T& p0, const T& p1, const T& p2, vec2f uv); - -// Interpolates values over a quad parameterized by u and v along the -// (p1-p0) and (p2-p1) directions. Same as bilinear interpolation. -template -inline T interpolate_quad( - const T& p0, const T& p1, const T& p2, const T& p3, vec2f uv); - -// Interpolates values along a cubic Bezier segment parametrized by u. -template -inline T interpolate_bezier( - const T& p0, const T& p1, const T& p2, const T& p3, float u); - -// Computes the derivative of a cubic Bezier segment parametrized by u. -template -inline T interpolate_bezier_derivative( - const T& p0, const T& p1, const T& p2, const T& p3, float u); - -// Interpolated line properties. -inline vec3f line_point(vec3f p0, vec3f p1, float u); -inline vec3f line_tangent(vec3f t0, vec3f t1, float u); - -// Interpolated triangle properties. -inline vec3f triangle_point(vec3f p0, vec3f p1, vec3f p2, vec2f uv); -inline vec3f triangle_normal(vec3f n0, vec3f n1, vec3f n2, vec2f uv); - -// Interpolated quad properties. -inline vec3f quad_point(vec3f p0, vec3f p1, vec3f p2, vec2f uv); -inline vec3f quad_normal(vec3f n0, vec3f n1, vec3f n2, vec3f n3, vec2f uv); - -// Interpolated sphere properties. -inline vec3f sphere_point(const vec3f p, float r, vec2f uv); -inline vec3f sphere_normal(const vec3f p, float r, vec2f uv); - -// Triangle tangent and bitangent from uv -inline pair triangle_tangents_fromuv( - vec3f p0, vec3f p1, vec3f p2, vec2f uv0, vec2f uv1, vec2f uv2); - -// Quad tangent and bitangent from uv. Note that we pass a current_uv since -// internally we may want to split the quad in two and we need to known where -// to do it. If not interested in the split, just pass vec2f{0,0} here. -inline pair quad_tangents_fromuv(vec3f p0, vec3f p1, vec3f p2, - vec3f p3, vec2f uv0, vec2f uv1, vec2f uv2, vec2f uv3, vec2f current_uv); - -} // namespace yocto - // ----------------------------------------------------------------------------- // USER INTERFACE UTILITIES // ----------------------------------------------------------------------------- @@ -366,67 +296,6 @@ inline bool overlap_bbox(const bbox3f& bbox1, const bbox3f& bbox2); } // namespace yocto -// ----------------------------------------------------------------------------- -// COMPUTATION OF PER_VERTEX PROPERTIES -// ----------------------------------------------------------------------------- -namespace yocto { - -// Compute per-vertex normals/tangents for lines/triangles/quads. -inline vector lines_tangents( - const vector& lines, const vector& positions); -inline vector triangles_normals( - const vector& triangles, const vector& positions); -inline vector quads_normals( - const vector& quads, const vector& positions); -// Update normals and tangents -inline void lines_tangents(vector& tangents, const vector& lines, - const vector& positions); -inline void triangles_normals(vector& normals, - const vector& triangles, const vector& positions); -inline void quads_normals(vector& normals, const vector& quads, - const vector& positions); - -// Compute per-vertex tangent space for triangle meshes. -// Tangent space is defined by a four component vector. -// The first three components are the tangent with respect to the u texcoord. -// The fourth component is the sign of the tangent wrt the v texcoord. -// Tangent frame is useful in normal mapping. -inline vector triangle_tangent_spaces(const vector& triangles, - const vector& positions, const vector& normals, - const vector& texcoords); - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// COMPUTATION OF VERTEX PROPERTIES -// ----------------------------------------------------------------------------- -namespace yocto { - -// Flip vertex normals -inline vector flip_normals(const vector& normals); -// Flip face orientation -inline vector flip_triangles(const vector& triangles); -inline vector flip_quads(const vector& quads); -// Align vertex positions. Alignment is 0: none, 1: min, 2: max, 3: center. -inline vector align_vertices( - const vector& positions, vec3i alignment); - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// SHAPE ELEMENT CONVERSION -// ----------------------------------------------------------------------------- -namespace yocto { - -// Convert quads to triangles -inline vector quads_to_triangles(const vector& quads); -// Convert triangles to quads by creating degenerate quads -inline vector triangles_to_quads(const vector& triangles); -// Convert beziers to lines using 3 lines for each bezier. -inline vector bezier_to_lines(vector& lines); - -} // namespace yocto - // ----------------------------------------------------------------------------- // // @@ -588,150 +457,6 @@ inline bbox3f capsule_bounds(vec3f p0, vec3f p1, float r0, float r1) { } // namespace yocto -// ----------------------------------------------------------------------------- -// GEOMETRY UTILITIES -// ----------------------------------------------------------------------------- -namespace yocto { - -// Line properties. -inline vec3f line_tangent(vec3f p0, vec3f p1) { return normalize(p1 - p0); } -inline float line_length(vec3f p0, vec3f p1) { return length(p1 - p0); } - -// Triangle properties. -inline vec3f triangle_normal(vec3f p0, vec3f p1, vec3f p2) { - return normalize(cross(p1 - p0, p2 - p0)); -} -inline float triangle_area(vec3f p0, vec3f p1, vec3f p2) { - return length(cross(p1 - p0, p2 - p0)) / 2; -} - -// Quad propeties. -inline vec3f quad_normal(vec3f p0, vec3f p1, vec3f p2, vec3f p3) { - return normalize(triangle_normal(p0, p1, p3) + triangle_normal(p2, p3, p1)); -} -inline float quad_area(vec3f p0, vec3f p1, vec3f p2, vec3f p3) { - return triangle_area(p0, p1, p3) + triangle_area(p2, p3, p1); -} - -// Interpolates values over a line parameterized from a to b by u. Same as lerp. -template -inline T interpolate_line(const T& p0, const T& p1, float u) { - return p0 * (1 - u) + p1 * u; -} - -// Interpolates values over a triangle parameterized by u and v along the -// (p1-p0) and (p2-p0) directions. Same as barycentric interpolation. -template -inline T interpolate_triangle(const T& p0, const T& p1, const T& p2, vec2f uv) { - return p0 * (1 - uv.x - uv.y) + p1 * uv.x + p2 * uv.y; -} - -// Interpolates values over a quad parameterized by u and v along the -// (p1-p0) and (p2-p1) directions. Same as bilinear interpolation. -template -inline T interpolate_quad( - const T& p0, const T& p1, const T& p2, const T& p3, vec2f uv) { - if (uv.x + uv.y <= 1) { - return interpolate_triangle(p0, p1, p3, uv); - } else { - return interpolate_triangle(p2, p3, p1, 1 - uv); - } -} - -// Interpolates values along a cubic Bezier segment parametrized by u. -template -inline T interpolate_bezier( - const T& p0, const T& p1, const T& p2, const T& p3, float u) { - return p0 * (1 - u) * (1 - u) * (1 - u) + p1 * 3 * u * (1 - u) * (1 - u) + - p2 * 3 * u * u * (1 - u) + p3 * u * u * u; -} -// Computes the derivative of a cubic Bezier segment parametrized by u. -template -inline T interpolate_bezier_derivative( - const T& p0, const T& p1, const T& p2, const T& p3, float u) { - return (p1 - p0) * 3 * (1 - u) * (1 - u) + (p2 - p1) * 6 * u * (1 - u) + - (p3 - p2) * 3 * u * u; -} - -// Interpolated line properties. -inline vec3f line_point(vec3f p0, vec3f p1, float u) { - return p0 * (1 - u) + p1 * u; -} -inline vec3f line_tangent(vec3f t0, vec3f t1, float u) { - return normalize(t0 * (1 - u) + t1 * u); -} - -// Interpolated triangle properties. -inline vec3f triangle_point(vec3f p0, vec3f p1, vec3f p2, vec2f uv) { - return p0 * (1 - uv.x - uv.y) + p1 * uv.x + p2 * uv.y; -} -inline vec3f triangle_normal(vec3f n0, vec3f n1, vec3f n2, vec2f uv) { - return normalize(n0 * (1 - uv.x - uv.y) + n1 * uv.x + n2 * uv.y); -} - -// Interpolated quad properties. -inline vec3f quad_point(vec3f p0, vec3f p1, vec3f p2, vec3f p3, vec2f uv) { - if (uv.x + uv.y <= 1) { - return triangle_point(p0, p1, p3, uv); - } else { - return triangle_point(p2, p3, p1, 1 - uv); - } -} -inline vec3f quad_normal(vec3f n0, vec3f n1, vec3f n2, vec3f n3, vec2f uv) { - if (uv.x + uv.y <= 1) { - return triangle_normal(n0, n1, n3, uv); - } else { - return triangle_normal(n2, n3, n1, 1 - uv); - } -} - -// Interpolated sphere properties. -inline vec3f sphere_point(const vec3f p, float r, vec2f uv) { - return p + r * vec3f{cos(uv.x * 2 * pif) * sin(uv.y * pif), - sin(uv.x * 2 * pif) * sin(uv.y * pif), cos(uv.y * pif)}; -} -inline vec3f sphere_normal(const vec3f p, float r, vec2f uv) { - return normalize(vec3f{cos(uv.x * 2 * pif) * sin(uv.y * pif), - sin(uv.x * 2 * pif) * sin(uv.y * pif), cos(uv.y * pif)}); -} - -// Triangle tangent and bitangent from uv -inline pair triangle_tangents_fromuv( - vec3f p0, vec3f p1, vec3f p2, vec2f uv0, vec2f uv1, vec2f uv2) { - // Follows the definition in http://www.terathon.com/code/tangent.html and - // https://gist.github.com/aras-p/2843984 - // normal points up from texture space - auto p = p1 - p0; - auto q = p2 - p0; - auto s = vec2f{uv1.x - uv0.x, uv2.x - uv0.x}; - auto t = vec2f{uv1.y - uv0.y, uv2.y - uv0.y}; - auto div = s.x * t.y - s.y * t.x; - - if (div != 0) { - auto tu = vec3f{t.y * p.x - t.x * q.x, t.y * p.y - t.x * q.y, - t.y * p.z - t.x * q.z} / - div; - auto tv = vec3f{s.x * q.x - s.y * p.x, s.x * q.y - s.y * p.y, - s.x * q.z - s.y * p.z} / - div; - return {tu, tv}; - } else { - return {{1, 0, 0}, {0, 1, 0}}; - } -} - -// Quad tangent and bitangent from uv. -inline pair quad_tangents_fromuv(vec3f p0, vec3f p1, vec3f p2, - vec3f p3, vec2f uv0, vec2f uv1, vec2f uv2, vec2f uv3, vec2f current_uv) { - if (current_uv.x + current_uv.y <= 1) { - return triangle_tangents_fromuv(p0, p1, p3, uv0, uv1, uv3); - } else { - return triangle_tangents_fromuv(p2, p3, p1, uv2, uv3, uv1); - } -} - -} // namespace yocto - // ----------------------------------------------------------------------------- // IMPLEMENTATION OF USER INTERFACE UTILITIES // ----------------------------------------------------------------------------- @@ -1167,249 +892,6 @@ namespace yocto { } // namespace yocto -// ----------------------------------------------------------------------------- -// IMPLEMENTATION OF COMPUTATION OF PER-VERTEX PROPERTIES -// ----------------------------------------------------------------------------- -namespace yocto { - -// Compute per-vertex tangents for lines. -inline vector lines_tangents( - const vector& lines, const vector& positions) { - auto tangents = vector{positions.size()}; - for (auto& tangent : tangents) tangent = {0, 0, 0}; - for (auto& l : lines) { - auto tangent = line_tangent(positions[l.x], positions[l.y]); - auto length = line_length(positions[l.x], positions[l.y]); - tangents[l.x] += tangent * length; - tangents[l.y] += tangent * length; - } - for (auto& tangent : tangents) tangent = normalize(tangent); - return tangents; -} - -// Compute per-vertex normals for triangles. -inline vector triangles_normals( - const vector& triangles, const vector& positions) { - auto normals = vector{positions.size()}; - for (auto& normal : normals) normal = {0, 0, 0}; - for (auto& t : triangles) { - auto normal = triangle_normal( - positions[t.x], positions[t.y], positions[t.z]); - auto area = triangle_area(positions[t.x], positions[t.y], positions[t.z]); - normals[t.x] += normal * area; - normals[t.y] += normal * area; - normals[t.z] += normal * area; - } - for (auto& normal : normals) normal = normalize(normal); - return normals; -} - -// Compute per-vertex normals for quads. -inline vector quads_normals( - const vector& quads, const vector& positions) { - auto normals = vector{positions.size()}; - for (auto& normal : normals) normal = {0, 0, 0}; - for (auto& q : quads) { - auto normal = quad_normal( - positions[q.x], positions[q.y], positions[q.z], positions[q.w]); - auto area = quad_area( - positions[q.x], positions[q.y], positions[q.z], positions[q.w]); - normals[q.x] += normal * area; - normals[q.y] += normal * area; - normals[q.z] += normal * area; - if (q.z != q.w) normals[q.w] += normal * area; - } - for (auto& normal : normals) normal = normalize(normal); - return normals; -} - -// Compute per-vertex tangents for lines. -inline void lines_tangents(vector& tangents, const vector& lines, - const vector& positions) { - if (tangents.size() != positions.size()) { - throw std::out_of_range("array should be the same length"); - } - for (auto& tangent : tangents) tangent = {0, 0, 0}; - for (auto& l : lines) { - auto tangent = line_tangent(positions[l.x], positions[l.y]); - auto length = line_length(positions[l.x], positions[l.y]); - tangents[l.x] += tangent * length; - tangents[l.y] += tangent * length; - } - for (auto& tangent : tangents) tangent = normalize(tangent); -} - -// Compute per-vertex normals for triangles. -inline void triangles_normals(vector& normals, - const vector& triangles, const vector& positions) { - if (normals.size() != positions.size()) { - throw std::out_of_range("array should be the same length"); - } - for (auto& normal : normals) normal = {0, 0, 0}; - for (auto& t : triangles) { - auto normal = triangle_normal( - positions[t.x], positions[t.y], positions[t.z]); - auto area = triangle_area(positions[t.x], positions[t.y], positions[t.z]); - normals[t.x] += normal * area; - normals[t.y] += normal * area; - normals[t.z] += normal * area; - } - for (auto& normal : normals) normal = normalize(normal); -} - -// Compute per-vertex normals for quads. -inline void quads_normals(vector& normals, const vector& quads, - const vector& positions) { - if (normals.size() != positions.size()) { - throw std::out_of_range("array should be the same length"); - } - for (auto& normal : normals) normal = {0, 0, 0}; - for (auto& q : quads) { - auto normal = quad_normal( - positions[q.x], positions[q.y], positions[q.z], positions[q.w]); - auto area = quad_area( - positions[q.x], positions[q.y], positions[q.z], positions[q.w]); - normals[q.x] += normal * area; - normals[q.y] += normal * area; - normals[q.z] += normal * area; - if (q.z != q.w) normals[q.w] += normal * area; - } - for (auto& normal : normals) normal = normalize(normal); -} - -// Compute per-vertex tangent frame for triangle meshes. -// Tangent space is defined by a four component vector. -// The first three components are the tangent with respect to the U texcoord. -// The fourth component is the sign of the tangent wrt the V texcoord. -// Tangent frame is useful in normal mapping. -inline vector triangles_tangent_spaces(const vector& triangles, - const vector& positions, const vector& normals, - const vector& texcoords) { - auto tangu = vector(positions.size(), vec3f{0, 0, 0}); - auto tangv = vector(positions.size(), vec3f{0, 0, 0}); - for (auto t : triangles) { - auto tutv = triangle_tangents_fromuv(positions[t.x], positions[t.y], - positions[t.z], texcoords[t.x], texcoords[t.y], texcoords[t.z]); - for (auto vid : {t.x, t.y, t.z}) tangu[vid] += normalize(tutv.first); - for (auto vid : {t.x, t.y, t.z}) tangv[vid] += normalize(tutv.second); - } - for (auto& t : tangu) t = normalize(t); - for (auto& t : tangv) t = normalize(t); - - auto tangent_spaces = vector(positions.size()); - for (auto& tangent : tangent_spaces) tangent = vec4f{0, 0, 0, 0}; - for (auto i : range(positions.size())) { - tangu[i] = orthonormalize(tangu[i], normals[i]); - auto s = (dot(cross(normals[i], tangu[i]), tangv[i]) < 0) ? -1.0f : 1.0f; - tangent_spaces[i] = {tangu[i].x, tangu[i].y, tangu[i].z, s}; - } - return tangent_spaces; -} - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// COMPUTATION OF PER VERTEX PROPETIES -// ----------------------------------------------------------------------------- -namespace yocto { - -// Flip vertex normals -inline vector flip_normals(const vector& normals) { - auto flipped = normals; - for (auto& n : flipped) n = -n; - return flipped; -} -// Flip face orientation -inline vector flip_triangles(const vector& triangles) { - auto flipped = triangles; - for (auto& t : flipped) swap(t.y, t.z); - return flipped; -} -inline vector flip_quads(const vector& quads) { - auto flipped = quads; - for (auto& q : flipped) { - if (q.z != q.w) { - swap(q.y, q.w); - } else { - swap(q.y, q.z); - q.w = q.z; - } - } - return flipped; -} - -// Align vertex positions. Alignment is 0: none, 1: min, 2: max, 3: center. -inline vector align_vertices( - const vector& positions, vec3i alignment) { - auto bounds = invalidb3f; - for (auto& p : positions) bounds = merge(bounds, p); - auto offset = vec3f{0, 0, 0}; - switch (alignment.x) { - case 0: break; - case 1: offset.x = bounds.min.x; break; - case 2: offset.x = (bounds.min.x + bounds.max.x) / 2; break; - case 3: offset.x = bounds.max.x; break; - default: throw std::invalid_argument{"invalid alignment"}; - } - switch (alignment.y) { - case 0: break; - case 1: offset.y = bounds.min.y; break; - case 2: offset.y = (bounds.min.y + bounds.max.y) / 2; break; - case 3: offset.y = bounds.max.y; break; - default: throw std::invalid_argument{"invalid alignment"}; - } - switch (alignment.z) { - case 0: break; - case 1: offset.z = bounds.min.z; break; - case 2: offset.z = (bounds.min.z + bounds.max.z) / 2; break; - case 3: offset.z = bounds.max.z; break; - default: throw std::invalid_argument{"invalid alignment"}; - } - auto aligned = positions; - for (auto& p : aligned) p -= offset; - return aligned; -} - -} // namespace yocto - -// ----------------------------------------------------------------------------- -// IMPLEMENTATION OF SHAPE ELEMENT CONVERSION -// ----------------------------------------------------------------------------- -namespace yocto { - -// Convert quads to triangles -inline vector quads_to_triangles(const vector& quads) { - auto triangles = vector{}; - triangles.reserve(quads.size() * 2); - for (auto& q : quads) { - triangles.push_back({q.x, q.y, q.w}); - if (q.z != q.w) triangles.push_back({q.z, q.w, q.y}); - } - return triangles; -} - -// Convert triangles to quads by creating degenerate quads -inline vector triangles_to_quads(const vector& triangles) { - auto quads = vector{}; - quads.reserve(triangles.size()); - for (auto& t : triangles) quads.push_back({t.x, t.y, t.z, t.z}); - return quads; -} - -// Convert beziers to lines using 3 lines for each bezier. -inline vector bezier_to_lines(const vector& beziers) { - auto lines = vector{}; - lines.reserve(beziers.size() * 3); - for (auto b : beziers) { - lines.push_back({b.x, b.y}); - lines.push_back({b.y, b.z}); - lines.push_back({b.z, b.w}); - } - return lines; -} - -} // namespace yocto - // ----------------------------------------------------------------------------- // CUDA SUPPORT // ----------------------------------------------------------------------------- diff --git a/libs/yocto/yocto_shape.h b/libs/yocto/yocto_shape.h index 6a04ce7fe..9d55511f9 100644 --- a/libs/yocto/yocto_shape.h +++ b/libs/yocto/yocto_shape.h @@ -62,6 +62,144 @@ using std::vector; } // namespace yocto +// ----------------------------------------------------------------------------- +// CUDA SUPPORT +// ----------------------------------------------------------------------------- +#ifdef __CUDACC__ +#define inline inline __device__ __forceinline__ +#endif + +// ----------------------------------------------------------------------------- +// GEOMETRY UTILITIES +// ----------------------------------------------------------------------------- +namespace yocto { + +// Line properties. +inline vec3f line_tangent(vec3f p0, vec3f p1); +inline float line_length(vec3f p0, vec3f p1); + +// Triangle properties. +inline vec3f triangle_normal(vec3f p0, vec3f p1, vec3f p2); +inline float triangle_area(vec3f p0, vec3f p1, vec3f p2); + +// Quad properties. +inline vec3f quad_normal(vec3f p0, vec3f p1, vec3f p2, vec3f p3); +inline float quad_area(vec3f p0, vec3f p1, vec3f p2, vec3f p3); + +// Interpolates values over a line parameterized from a to b by u. Same as lerp. +template +inline T interpolate_line(const T& p0, const T& p1, float u); + +// Interpolates values over a triangle parameterized by u and v along the +// (p1-p0) and (p2-p0) directions. Same as barycentric interpolation. +template +inline T interpolate_triangle(const T& p0, const T& p1, const T& p2, vec2f uv); + +// Interpolates values over a quad parameterized by u and v along the +// (p1-p0) and (p2-p1) directions. Same as bilinear interpolation. +template +inline T interpolate_quad( + const T& p0, const T& p1, const T& p2, const T& p3, vec2f uv); + +// Interpolates values along a cubic Bezier segment parametrized by u. +template +inline T interpolate_bezier( + const T& p0, const T& p1, const T& p2, const T& p3, float u); + +// Computes the derivative of a cubic Bezier segment parametrized by u. +template +inline T interpolate_bezier_derivative( + const T& p0, const T& p1, const T& p2, const T& p3, float u); + +// Interpolated line properties. +inline vec3f line_point(vec3f p0, vec3f p1, float u); +inline vec3f line_tangent(vec3f t0, vec3f t1, float u); + +// Interpolated triangle properties. +inline vec3f triangle_point(vec3f p0, vec3f p1, vec3f p2, vec2f uv); +inline vec3f triangle_normal(vec3f n0, vec3f n1, vec3f n2, vec2f uv); + +// Interpolated quad properties. +inline vec3f quad_point(vec3f p0, vec3f p1, vec3f p2, vec2f uv); +inline vec3f quad_normal(vec3f n0, vec3f n1, vec3f n2, vec3f n3, vec2f uv); + +// Interpolated sphere properties. +inline vec3f sphere_point(const vec3f p, float r, vec2f uv); +inline vec3f sphere_normal(const vec3f p, float r, vec2f uv); + +// Triangle tangent and bitangent from uv +inline pair triangle_tangents_fromuv( + vec3f p0, vec3f p1, vec3f p2, vec2f uv0, vec2f uv1, vec2f uv2); + +// Quad tangent and bitangent from uv. Note that we pass a current_uv since +// internally we may want to split the quad in two and we need to known where +// to do it. If not interested in the split, just pass vec2f{0,0} here. +inline pair quad_tangents_fromuv(vec3f p0, vec3f p1, vec3f p2, + vec3f p3, vec2f uv0, vec2f uv1, vec2f uv2, vec2f uv3, vec2f current_uv); + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// COMPUTATION OF PER_VERTEX PROPERTIES +// ----------------------------------------------------------------------------- +namespace yocto { + +// Compute per-vertex normals/tangents for lines/triangles/quads. +inline vector lines_tangents( + const vector& lines, const vector& positions); +inline vector triangles_normals( + const vector& triangles, const vector& positions); +inline vector quads_normals( + const vector& quads, const vector& positions); +// Update normals and tangents +inline void lines_tangents(vector& tangents, const vector& lines, + const vector& positions); +inline void triangles_normals(vector& normals, + const vector& triangles, const vector& positions); +inline void quads_normals(vector& normals, const vector& quads, + const vector& positions); + +// Compute per-vertex tangent space for triangle meshes. +// Tangent space is defined by a four component vector. +// The first three components are the tangent with respect to the u texcoord. +// The fourth component is the sign of the tangent wrt the v texcoord. +// Tangent frame is useful in normal mapping. +inline vector triangle_tangent_spaces(const vector& triangles, + const vector& positions, const vector& normals, + const vector& texcoords); + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// COMPUTATION OF VERTEX PROPERTIES +// ----------------------------------------------------------------------------- +namespace yocto { + +// Flip vertex normals +inline vector flip_normals(const vector& normals); +// Flip face orientation +inline vector flip_triangles(const vector& triangles); +inline vector flip_quads(const vector& quads); +// Align vertex positions. Alignment is 0: none, 1: min, 2: max, 3: center. +inline vector align_vertices( + const vector& positions, vec3i alignment); + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// SHAPE ELEMENT CONVERSION +// ----------------------------------------------------------------------------- +namespace yocto { + +// Convert quads to triangles +inline vector quads_to_triangles(const vector& quads); +// Convert triangles to quads by creating degenerate quads +inline vector triangles_to_quads(const vector& triangles); +// Convert beziers to lines using 3 lines for each bezier. +inline vector bezier_to_lines(vector& lines); + +} // namespace yocto + // ----------------------------------------------------------------------------- // SHAPE DATA AND UTILITIES // ----------------------------------------------------------------------------- @@ -339,6 +477,393 @@ shape_data make_heightfield(vec2i size, const vector& color); // // ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// GEOMETRY UTILITIES +// ----------------------------------------------------------------------------- +namespace yocto { + +// Line properties. +inline vec3f line_tangent(vec3f p0, vec3f p1) { return normalize(p1 - p0); } +inline float line_length(vec3f p0, vec3f p1) { return length(p1 - p0); } + +// Triangle properties. +inline vec3f triangle_normal(vec3f p0, vec3f p1, vec3f p2) { + return normalize(cross(p1 - p0, p2 - p0)); +} +inline float triangle_area(vec3f p0, vec3f p1, vec3f p2) { + return length(cross(p1 - p0, p2 - p0)) / 2; +} + +// Quad propeties. +inline vec3f quad_normal(vec3f p0, vec3f p1, vec3f p2, vec3f p3) { + return normalize(triangle_normal(p0, p1, p3) + triangle_normal(p2, p3, p1)); +} +inline float quad_area(vec3f p0, vec3f p1, vec3f p2, vec3f p3) { + return triangle_area(p0, p1, p3) + triangle_area(p2, p3, p1); +} + +// Interpolates values over a line parameterized from a to b by u. Same as lerp. +template +inline T interpolate_line(const T& p0, const T& p1, float u) { + return p0 * (1 - u) + p1 * u; +} + +// Interpolates values over a triangle parameterized by u and v along the +// (p1-p0) and (p2-p0) directions. Same as barycentric interpolation. +template +inline T interpolate_triangle(const T& p0, const T& p1, const T& p2, vec2f uv) { + return p0 * (1 - uv.x - uv.y) + p1 * uv.x + p2 * uv.y; +} + +// Interpolates values over a quad parameterized by u and v along the +// (p1-p0) and (p2-p1) directions. Same as bilinear interpolation. +template +inline T interpolate_quad( + const T& p0, const T& p1, const T& p2, const T& p3, vec2f uv) { + if (uv.x + uv.y <= 1) { + return interpolate_triangle(p0, p1, p3, uv); + } else { + return interpolate_triangle(p2, p3, p1, 1 - uv); + } +} + +// Interpolates values along a cubic Bezier segment parametrized by u. +template +inline T interpolate_bezier( + const T& p0, const T& p1, const T& p2, const T& p3, float u) { + return p0 * (1 - u) * (1 - u) * (1 - u) + p1 * 3 * u * (1 - u) * (1 - u) + + p2 * 3 * u * u * (1 - u) + p3 * u * u * u; +} +// Computes the derivative of a cubic Bezier segment parametrized by u. +template +inline T interpolate_bezier_derivative( + const T& p0, const T& p1, const T& p2, const T& p3, float u) { + return (p1 - p0) * 3 * (1 - u) * (1 - u) + (p2 - p1) * 6 * u * (1 - u) + + (p3 - p2) * 3 * u * u; +} + +// Interpolated line properties. +inline vec3f line_point(vec3f p0, vec3f p1, float u) { + return p0 * (1 - u) + p1 * u; +} +inline vec3f line_tangent(vec3f t0, vec3f t1, float u) { + return normalize(t0 * (1 - u) + t1 * u); +} + +// Interpolated triangle properties. +inline vec3f triangle_point(vec3f p0, vec3f p1, vec3f p2, vec2f uv) { + return p0 * (1 - uv.x - uv.y) + p1 * uv.x + p2 * uv.y; +} +inline vec3f triangle_normal(vec3f n0, vec3f n1, vec3f n2, vec2f uv) { + return normalize(n0 * (1 - uv.x - uv.y) + n1 * uv.x + n2 * uv.y); +} + +// Interpolated quad properties. +inline vec3f quad_point(vec3f p0, vec3f p1, vec3f p2, vec3f p3, vec2f uv) { + if (uv.x + uv.y <= 1) { + return triangle_point(p0, p1, p3, uv); + } else { + return triangle_point(p2, p3, p1, 1 - uv); + } +} +inline vec3f quad_normal(vec3f n0, vec3f n1, vec3f n2, vec3f n3, vec2f uv) { + if (uv.x + uv.y <= 1) { + return triangle_normal(n0, n1, n3, uv); + } else { + return triangle_normal(n2, n3, n1, 1 - uv); + } +} + +// Interpolated sphere properties. +inline vec3f sphere_point(const vec3f p, float r, vec2f uv) { + return p + r * vec3f{cos(uv.x * 2 * pif) * sin(uv.y * pif), + sin(uv.x * 2 * pif) * sin(uv.y * pif), cos(uv.y * pif)}; +} +inline vec3f sphere_normal(const vec3f p, float r, vec2f uv) { + return normalize(vec3f{cos(uv.x * 2 * pif) * sin(uv.y * pif), + sin(uv.x * 2 * pif) * sin(uv.y * pif), cos(uv.y * pif)}); +} + +// Triangle tangent and bitangent from uv +inline pair triangle_tangents_fromuv( + vec3f p0, vec3f p1, vec3f p2, vec2f uv0, vec2f uv1, vec2f uv2) { + // Follows the definition in http://www.terathon.com/code/tangent.html and + // https://gist.github.com/aras-p/2843984 + // normal points up from texture space + auto p = p1 - p0; + auto q = p2 - p0; + auto s = vec2f{uv1.x - uv0.x, uv2.x - uv0.x}; + auto t = vec2f{uv1.y - uv0.y, uv2.y - uv0.y}; + auto div = s.x * t.y - s.y * t.x; + + if (div != 0) { + auto tu = vec3f{t.y * p.x - t.x * q.x, t.y * p.y - t.x * q.y, + t.y * p.z - t.x * q.z} / + div; + auto tv = vec3f{s.x * q.x - s.y * p.x, s.x * q.y - s.y * p.y, + s.x * q.z - s.y * p.z} / + div; + return {tu, tv}; + } else { + return {{1, 0, 0}, {0, 1, 0}}; + } +} + +// Quad tangent and bitangent from uv. +inline pair quad_tangents_fromuv(vec3f p0, vec3f p1, vec3f p2, + vec3f p3, vec2f uv0, vec2f uv1, vec2f uv2, vec2f uv3, vec2f current_uv) { + if (current_uv.x + current_uv.y <= 1) { + return triangle_tangents_fromuv(p0, p1, p3, uv0, uv1, uv3); + } else { + return triangle_tangents_fromuv(p2, p3, p1, uv2, uv3, uv1); + } +} + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// IMPLEMENTATION OF COMPUTATION OF PER-VERTEX PROPERTIES +// ----------------------------------------------------------------------------- +namespace yocto { + +// Compute per-vertex tangents for lines. +inline vector lines_tangents( + const vector& lines, const vector& positions) { + auto tangents = vector{positions.size()}; + for (auto& tangent : tangents) tangent = {0, 0, 0}; + for (auto& l : lines) { + auto tangent = line_tangent(positions[l.x], positions[l.y]); + auto length = line_length(positions[l.x], positions[l.y]); + tangents[l.x] += tangent * length; + tangents[l.y] += tangent * length; + } + for (auto& tangent : tangents) tangent = normalize(tangent); + return tangents; +} + +// Compute per-vertex normals for triangles. +inline vector triangles_normals( + const vector& triangles, const vector& positions) { + auto normals = vector{positions.size()}; + for (auto& normal : normals) normal = {0, 0, 0}; + for (auto& t : triangles) { + auto normal = triangle_normal( + positions[t.x], positions[t.y], positions[t.z]); + auto area = triangle_area(positions[t.x], positions[t.y], positions[t.z]); + normals[t.x] += normal * area; + normals[t.y] += normal * area; + normals[t.z] += normal * area; + } + for (auto& normal : normals) normal = normalize(normal); + return normals; +} + +// Compute per-vertex normals for quads. +inline vector quads_normals( + const vector& quads, const vector& positions) { + auto normals = vector{positions.size()}; + for (auto& normal : normals) normal = {0, 0, 0}; + for (auto& q : quads) { + auto normal = quad_normal( + positions[q.x], positions[q.y], positions[q.z], positions[q.w]); + auto area = quad_area( + positions[q.x], positions[q.y], positions[q.z], positions[q.w]); + normals[q.x] += normal * area; + normals[q.y] += normal * area; + normals[q.z] += normal * area; + if (q.z != q.w) normals[q.w] += normal * area; + } + for (auto& normal : normals) normal = normalize(normal); + return normals; +} + +// Compute per-vertex tangents for lines. +inline void lines_tangents(vector& tangents, const vector& lines, + const vector& positions) { + if (tangents.size() != positions.size()) { + throw std::out_of_range("array should be the same length"); + } + for (auto& tangent : tangents) tangent = {0, 0, 0}; + for (auto& l : lines) { + auto tangent = line_tangent(positions[l.x], positions[l.y]); + auto length = line_length(positions[l.x], positions[l.y]); + tangents[l.x] += tangent * length; + tangents[l.y] += tangent * length; + } + for (auto& tangent : tangents) tangent = normalize(tangent); +} + +// Compute per-vertex normals for triangles. +inline void triangles_normals(vector& normals, + const vector& triangles, const vector& positions) { + if (normals.size() != positions.size()) { + throw std::out_of_range("array should be the same length"); + } + for (auto& normal : normals) normal = {0, 0, 0}; + for (auto& t : triangles) { + auto normal = triangle_normal( + positions[t.x], positions[t.y], positions[t.z]); + auto area = triangle_area(positions[t.x], positions[t.y], positions[t.z]); + normals[t.x] += normal * area; + normals[t.y] += normal * area; + normals[t.z] += normal * area; + } + for (auto& normal : normals) normal = normalize(normal); +} + +// Compute per-vertex normals for quads. +inline void quads_normals(vector& normals, const vector& quads, + const vector& positions) { + if (normals.size() != positions.size()) { + throw std::out_of_range("array should be the same length"); + } + for (auto& normal : normals) normal = {0, 0, 0}; + for (auto& q : quads) { + auto normal = quad_normal( + positions[q.x], positions[q.y], positions[q.z], positions[q.w]); + auto area = quad_area( + positions[q.x], positions[q.y], positions[q.z], positions[q.w]); + normals[q.x] += normal * area; + normals[q.y] += normal * area; + normals[q.z] += normal * area; + if (q.z != q.w) normals[q.w] += normal * area; + } + for (auto& normal : normals) normal = normalize(normal); +} + +// Compute per-vertex tangent frame for triangle meshes. +// Tangent space is defined by a four component vector. +// The first three components are the tangent with respect to the U texcoord. +// The fourth component is the sign of the tangent wrt the V texcoord. +// Tangent frame is useful in normal mapping. +inline vector triangles_tangent_spaces(const vector& triangles, + const vector& positions, const vector& normals, + const vector& texcoords) { + auto tangu = vector(positions.size(), vec3f{0, 0, 0}); + auto tangv = vector(positions.size(), vec3f{0, 0, 0}); + for (auto t : triangles) { + auto tutv = triangle_tangents_fromuv(positions[t.x], positions[t.y], + positions[t.z], texcoords[t.x], texcoords[t.y], texcoords[t.z]); + for (auto vid : {t.x, t.y, t.z}) tangu[vid] += normalize(tutv.first); + for (auto vid : {t.x, t.y, t.z}) tangv[vid] += normalize(tutv.second); + } + for (auto& t : tangu) t = normalize(t); + for (auto& t : tangv) t = normalize(t); + + auto tangent_spaces = vector(positions.size()); + for (auto& tangent : tangent_spaces) tangent = vec4f{0, 0, 0, 0}; + for (auto i : range(positions.size())) { + tangu[i] = orthonormalize(tangu[i], normals[i]); + auto s = (dot(cross(normals[i], tangu[i]), tangv[i]) < 0) ? -1.0f : 1.0f; + tangent_spaces[i] = {tangu[i].x, tangu[i].y, tangu[i].z, s}; + } + return tangent_spaces; +} + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// COMPUTATION OF PER VERTEX PROPETIES +// ----------------------------------------------------------------------------- +namespace yocto { + +// Flip vertex normals +inline vector flip_normals(const vector& normals) { + auto flipped = normals; + for (auto& n : flipped) n = -n; + return flipped; +} +// Flip face orientation +inline vector flip_triangles(const vector& triangles) { + auto flipped = triangles; + for (auto& t : flipped) swap(t.y, t.z); + return flipped; +} +inline vector flip_quads(const vector& quads) { + auto flipped = quads; + for (auto& q : flipped) { + if (q.z != q.w) { + swap(q.y, q.w); + } else { + swap(q.y, q.z); + q.w = q.z; + } + } + return flipped; +} + +// Align vertex positions. Alignment is 0: none, 1: min, 2: max, 3: center. +inline vector align_vertices( + const vector& positions, vec3i alignment) { + auto bounds = invalidb3f; + for (auto& p : positions) bounds = merge(bounds, p); + auto offset = vec3f{0, 0, 0}; + switch (alignment.x) { + case 0: break; + case 1: offset.x = bounds.min.x; break; + case 2: offset.x = (bounds.min.x + bounds.max.x) / 2; break; + case 3: offset.x = bounds.max.x; break; + default: throw std::invalid_argument{"invalid alignment"}; + } + switch (alignment.y) { + case 0: break; + case 1: offset.y = bounds.min.y; break; + case 2: offset.y = (bounds.min.y + bounds.max.y) / 2; break; + case 3: offset.y = bounds.max.y; break; + default: throw std::invalid_argument{"invalid alignment"}; + } + switch (alignment.z) { + case 0: break; + case 1: offset.z = bounds.min.z; break; + case 2: offset.z = (bounds.min.z + bounds.max.z) / 2; break; + case 3: offset.z = bounds.max.z; break; + default: throw std::invalid_argument{"invalid alignment"}; + } + auto aligned = positions; + for (auto& p : aligned) p -= offset; + return aligned; +} + +} // namespace yocto + +// ----------------------------------------------------------------------------- +// IMPLEMENTATION OF SHAPE ELEMENT CONVERSION +// ----------------------------------------------------------------------------- +namespace yocto { + +// Convert quads to triangles +inline vector quads_to_triangles(const vector& quads) { + auto triangles = vector{}; + triangles.reserve(quads.size() * 2); + for (auto& q : quads) { + triangles.push_back({q.x, q.y, q.w}); + if (q.z != q.w) triangles.push_back({q.z, q.w, q.y}); + } + return triangles; +} + +// Convert triangles to quads by creating degenerate quads +inline vector triangles_to_quads(const vector& triangles) { + auto quads = vector{}; + quads.reserve(triangles.size()); + for (auto& t : triangles) quads.push_back({t.x, t.y, t.z, t.z}); + return quads; +} + +// Convert beziers to lines using 3 lines for each bezier. +inline vector bezier_to_lines(const vector& beziers) { + auto lines = vector{}; + lines.reserve(beziers.size() * 3); + for (auto b : beziers) { + lines.push_back({b.x, b.y}); + lines.push_back({b.y, b.z}); + lines.push_back({b.z, b.w}); + } + return lines; +} + +} // namespace yocto + // ----------------------------------------------------------------------------- // SHAPE FUNCTIONS // ----------------------------------------------------------------------------- @@ -389,4 +914,11 @@ inline shape_data make_quads(vec2i steps, PFunc&& position, NFunc&& normal) { } // namespace yocto +// ----------------------------------------------------------------------------- +// CUDA SUPPORT +// ----------------------------------------------------------------------------- +#ifdef __CUDACC__ +#undef inline +#endif + #endif