16#include <lagrange/Mesh.h>
17#include <lagrange/bvh/zip_boundary.h>
18#include <lagrange/compute_normal.h>
19#include <lagrange/create_mesh.h>
20#include <lagrange/internal/constants.h>
21#include <lagrange/legacy/inline.h>
22#include <lagrange/packing/compute_rectangle_packing.h>
23#include <lagrange/primitive/generation_utils.h>
24#include <lagrange/utils/safe_cast.h>
25#include <lagrange/utils/warning.h>
37 using Index = uint32_t;
48 [[deprecated]] Scalar
sweep_angle =
static_cast<Scalar
>(2 * lagrange::internal::pi);
49 Scalar start_sweep_angle = 0;
50 Scalar end_sweep_angle =
static_cast<Scalar
>(2 * lagrange::internal::pi);
51 Index num_longitude_sections = 32;
52 Index num_latitude_sections = 32;
53 Eigen::Matrix<Scalar, 3, 1> center{0, 0, 0};
54 bool with_cross_section =
true;
59 bool output_normals =
true;
73 Scalar
epsilon =
static_cast<Scalar
>(1e-6);
84 radius = std::max(radius,
static_cast<Scalar
>(0.0));
85 num_longitude_sections = std::max(num_longitude_sections,
static_cast<Index
>(3));
86 num_latitude_sections = std::max(num_latitude_sections,
static_cast<Index
>(3));
90 if (
sweep_angle !=
static_cast<Scalar
>(2 * lagrange::internal::pi) &&
91 start_sweep_angle == 0 &&
92 end_sweep_angle ==
static_cast<Scalar
>(2 * lagrange::internal::pi)) {
95 LA_IGNORE_DEPRECATION_WARNING_END
105template <
typename MeshType>
108 using Scalar =
typename MeshType::Scalar;
109 using Index =
typename MeshType::Index;
110 using VertexArray =
typename MeshType::VertexArray;
111 using FacetArray =
typename MeshType::FacetArray;
112 using AttributeArray =
typename MeshType::AttributeArray;
113 using UVArray =
typename MeshType::UVArray;
114 using UVIndices =
typename MeshType::UVIndices;
116 config.project_to_valid_range();
120 config.get_effective_sweep_angle() >= (2 * lagrange::internal::pi - config.epsilon);
124 const Index row_size =
is_closed ? repeat_x : (repeat_x + 1);
125 const Index num_vertices = row_size * (repeat_y - 1) + 2 + (
is_closed ? 0 : 1);
126 const Index num_uvs = (repeat_x + 1) * (repeat_y + 1) - 2 +
127 (
is_closed || !config.with_cross_section ? 0 : 2 * (repeat_y + 2));
128 const Index num_triangles = repeat_x * (repeat_y - 1) * 2 +
129 (
is_closed || !config.with_cross_section ? 0 : repeat_y * 2);
131 VertexArray vertices(num_vertices, 3);
132 FacetArray facets(num_triangles, 3);
133 UVArray uvs(num_uvs, 2);
134 UVIndices uv_indices(num_triangles, 3);
135 AttributeArray normals(num_vertices, 3);
137 auto get_vertex_index = [&](Index rx, Index ry) {
141 }
else if (ry == repeat_y) {
144 vidx = 2 + row_size * (ry - 1) + (
is_closed ? (rx % repeat_x) : rx);
149 auto get_uv_vertex_index = [&](Index rx, Index ry) {
167 const Index adjustment = ry > 0 ? (ry == repeat_y && rx > 0 ? 2 : 1) : 0;
168 return ry * (repeat_x + 1) + rx - adjustment;
172 Scalar u_max = (config.get_effective_sweep_angle() / lagrange::internal::pi) * v_max;
173 for (
auto ry :
range(repeat_y + 1)) {
174 for (
auto rx :
range(repeat_x + 1)) {
176 ((
Scalar)rx / (
Scalar)repeat_x) * config.get_effective_sweep_angle() +
177 config.start_sweep_angle;
179 const Scalar x = std::cos(theta) * std::sin(phi);
180 const Scalar y = std::cos(phi);
181 const Scalar z = std::sin(theta) * std::sin(phi);
183 const Index vertex_index = get_vertex_index(rx, ry);
186 vertices.row(vertex_index) << config.radius * x + config.center.x(),
187 config.radius * y + config.center.y(), config.radius * z + config.center.z();
189 normals.row(vertex_index) << x, y, z;
192 const Index uv_index = get_uv_vertex_index(rx, ry);
198 Index triangle_id = 0;
199 for (
auto ry :
range(repeat_y)) {
200 for (
auto rx :
range(repeat_x)) {
201 const auto q0 = get_vertex_index(rx, ry);
202 const auto q1 = get_vertex_index(rx + 1, ry);
203 const auto q2 = get_vertex_index(rx + 1, ry + 1);
204 const auto q3 = get_vertex_index(rx, ry + 1);
206 const auto p0 = get_uv_vertex_index(rx, ry);
207 const auto p1 = get_uv_vertex_index(rx + 1, ry);
208 const auto p2 = get_uv_vertex_index(rx + 1, ry + 1);
209 const auto p3 = get_uv_vertex_index(rx, ry + 1);
212 facets.row(triangle_id) << q0, q1, q2;
213 uv_indices.row(triangle_id) << p0, p1, p2;
217 facets.row(triangle_id) << q0, q2, q3;
218 uv_indices.row(triangle_id) << p0, p2, p3;
224 if (!
is_closed && config.with_cross_section) {
225 Index center_index =
static_cast<Index
>(vertices.rows() - 1);
226 vertices.row(center_index) = config.center.template
cast<Scalar>();
227 Index base_uv_index = (repeat_x + 1) * (repeat_y + 1) - 2;
229 uvs.row(base_uv_index) << 0, 0;
230 for (
auto ry :
range(repeat_y + 1)) {
232 uvs.row(base_uv_index + ry + 1) << std::cos(phi) / lagrange::internal::pi,
233 std::sin(phi) / lagrange::internal::pi;
235 if (ry != repeat_y) {
236 Index v0 = get_vertex_index(0, ry);
237 Index v1 = get_vertex_index(0, ry + 1);
238 facets.row(triangle_id) << v0, v1, center_index;
239 uv_indices.row(triangle_id) << base_uv_index + ry + 1, base_uv_index + ry + 2,
245 base_uv_index += repeat_y + 2;
246 uvs.row(base_uv_index) << 0, 0;
247 for (
auto ry :
range(repeat_y + 1)) {
249 uvs.row(base_uv_index + ry + 1) << std::cos(phi) / lagrange::internal::pi,
250 std::sin(phi) / lagrange::internal::pi;
252 if (ry != repeat_y) {
253 Index v0 = get_vertex_index(repeat_x, ry);
254 Index v1 = get_vertex_index(repeat_x, ry + 1);
255 facets.row(triangle_id) << v1, v0, center_index;
256 uv_indices.row(triangle_id) << base_uv_index + ry + 2, base_uv_index + ry + 1,
263 assert(triangle_id == num_triangles);
267 mesh->initialize_uv(uvs, uv_indices);
269 if (config.output_normals) {
275 lagrange::set_uniform_semantic_label(*mesh, PrimitiveSemanticLabel::TOP);
277 packing::compute_rectangle_packing(*mesh);
281template <
typename MeshType>
282std::unique_ptr<MeshType> generate_sphere(
283 typename MeshType::Scalar radius,
284 const Eigen::Matrix<typename MeshType::Scalar, 3, 1>& center = {0, 0, 0},
285 typename MeshType::Scalar sweep_angle = 2 * lagrange::internal::pi,
286 typename MeshType::Index num_radial_sections = 50,
287 typename MeshType::Index flat_shade_thresh = 20)
289 using Scalar =
typename SphereConfig::Scalar;
290 using Index =
typename SphereConfig::Index;
296 config.end_sweep_angle = config.start_sweep_angle + config.sweep_angle;
299 config.angle_threshold =
300 safe_cast<Scalar>((num_radial_sections < flat_shade_thresh) ? 0 : config.angle_threshold);
302 return generate_sphere<MeshType>(std::move(config));
305LA_IGNORE_DEPRECATION_WARNING_END
@ Scalar
Mesh attribute must have exactly 1 channel.
Definition AttributeFwd.h:56
AttributeId compute_normal(SurfaceMesh< Scalar, Index > &mesh, function_ref< bool(Index)> is_edge_smooth, span< const Index > cone_vertices={}, NormalOptions options={})
Compute smooth normals based on specified sharp edges and cone vertices.
Definition compute_normal.cpp:198
bool is_closed(const SurfaceMesh< Scalar, Index > &mesh)
Check if a mesh is closed.
Definition topology.cpp:51
SurfaceMesh< ToScalar, ToIndex > cast(const SurfaceMesh< FromScalar, FromIndex > &source_mesh, const AttributeFilter &convertible_attributes={}, std::vector< std::string > *converted_attributes_names=nullptr)
Cast a mesh to a mesh of different scalar and/or index type.
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:174
internal::Range< Index > range(Index end)
Returns an iterable object representing the range [0, end).
Definition range.h:176
constexpr auto safe_cast(SourceType value) -> std::enable_if_t<!std::is_same< SourceType, TargetType >::value, TargetType >
Perform safe cast from SourceType to TargetType, where "safe" means:
Definition safe_cast.h:50
#define LA_IGNORE_DEPRECATION_WARNING_BEGIN
Ignore deprecation warnings.
Definition warning.h:76
SurfaceMesh< Scalar, Index > generate_sphere(SphereOptions setting)
Generate a sphere mesh based on the specified settings.
Definition generate_sphere.cpp:59
Main namespace for Lagrange.
auto create_mesh(const Eigen::MatrixBase< DerivedV > &vertices, const Eigen::MatrixBase< DerivedF > &facets)
This function create a new mesh given the vertex and facet arrays by copying data into the Mesh objec...
Definition create_mesh.h:39
Definition generate_sphere.h:35
void project_to_valid_range()
Project config setting into valid range.
Definition generate_sphere.h:82
Scalar angle_threshold
An edge is considered sharp if its dihedral angle is larger than angle_threshold.
Definition generate_sphere.h:68
Scalar get_effective_sweep_angle() const
Get the effective sweep angle based on start and end angles.
Definition generate_sphere.h:102
Scalar epsilon
Numerical tolerence used for comparing Scalar values.
Definition generate_sphere.h:73
Scalar sweep_angle
Definition generate_sphere.h:48