Compare commits
72 Commits
temp-geome
...
geometry-n
Author | SHA1 | Date | |
---|---|---|---|
21d774cef3 | |||
7bb5c86c1e | |||
e05616fbac | |||
3861ee7f3f | |||
109545755a | |||
e9d5b767e8 | |||
e73fee563d | |||
00a0c7943d | |||
167a2d19a9 | |||
9f16e5ea3f | |||
b649f0ba7f | |||
977fc6e115 | |||
1cc876f325 | |||
f73bef57d9 | |||
c97183258f | |||
630a163998 | |||
f65cca02b0 | |||
3a45f35d7e | |||
29e71774fe | |||
8892796e12 | |||
ec8bae2a59 | |||
5cd33108fc | |||
1173d1ca9e | |||
f7a6dd7218 | |||
9934a71172 | |||
e46dcc499c | |||
4be8061baf | |||
c20d7676fc | |||
507fdd0e3e | |||
1e29f64987 | |||
9b2b6674ba | |||
cec588d757 | |||
3e5869e083 | |||
5f9bec93e6 | |||
167525dc8d | |||
1d6764dddd | |||
507b8fa527 | |||
91e42d81fe | |||
53daf2a0db | |||
73d53b3bd8 | |||
55fe91b83b | |||
94d826f6d6 | |||
51b731d479 | |||
5fa962c7f6 | |||
543783fc61 | |||
a70a715f67 | |||
1f95b07b32 | |||
9b28ab0d0b | |||
83c87b6564 | |||
50072596d1 | |||
05aea4bb51 | |||
f993a47248 | |||
73b023bbab | |||
95ecd5804d | |||
0c42b40aee | |||
0084954ade | |||
8464641e77 | |||
4f875a31c9 | |||
46b8b36eff | |||
475f0f5ece | |||
9a501e1ece | |||
3915392560 | |||
fdb116a0b6 | |||
ca8d1900ff | |||
edce7ee71d | |||
ffd63bc495 | |||
fbf093dee1 | |||
414747f40d | |||
e3995e5050 | |||
6ac36103ea | |||
fcd7c0cfcc | |||
93788a9b8d |
@@ -52,9 +52,7 @@ void transform_mesh(Mesh *mesh,
|
||||
const float3 rotation,
|
||||
const float3 scale);
|
||||
|
||||
Mesh *create_cylinder_or_cone_mesh(const float3 location,
|
||||
const float3 rotation,
|
||||
const float radius_top,
|
||||
Mesh *create_cylinder_or_cone_mesh(const float radius_top,
|
||||
const float radius_bottom,
|
||||
const float depth,
|
||||
const int verts_num,
|
||||
|
@@ -17,14 +17,11 @@
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_mesh.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate geo_node_mesh_primitive_cone_in[] = {
|
||||
@@ -191,46 +188,351 @@ static int face_total(const GeometryNodeMeshCircleFillType fill_type,
|
||||
return face_total;
|
||||
}
|
||||
|
||||
Mesh *create_cylinder_or_cone_mesh(const float3 location,
|
||||
const float3 rotation,
|
||||
const float radius_top,
|
||||
static void calculate_uvs(Mesh *mesh,
|
||||
const bool top_is_point,
|
||||
const bool bottom_is_point,
|
||||
const int verts_num,
|
||||
const GeometryNodeMeshCircleFillType fill_type)
|
||||
{
|
||||
MeshComponent mesh_component;
|
||||
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
|
||||
OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output(
|
||||
"uv", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr);
|
||||
MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>();
|
||||
|
||||
Array<float2> circle(verts_num);
|
||||
float angle = 0.0f;
|
||||
const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num);
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
circle[i].x = std::cos(angle) * 0.225f + 0.25f;
|
||||
circle[i].y = std::sin(angle) * 0.225f + 0.25f;
|
||||
angle += angle_delta;
|
||||
}
|
||||
|
||||
int loop_index = 0;
|
||||
if (!top_is_point) {
|
||||
if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
uvs[loop_index++] = circle[i];
|
||||
}
|
||||
}
|
||||
else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
uvs[loop_index++] = circle[i];
|
||||
uvs[loop_index++] = circle[(i + 1) % verts_num];
|
||||
uvs[loop_index++] = float2(0.25f, 0.25f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Create side corners and faces. */
|
||||
if (!top_is_point && !bottom_is_point) {
|
||||
const float bottom = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f;
|
||||
/* Quads connect the top and bottom. */
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
const float vert = static_cast<float>(i);
|
||||
uvs[loop_index++] = float2(vert / verts_num, bottom);
|
||||
uvs[loop_index++] = float2((vert + 1.0f) / verts_num, bottom);
|
||||
uvs[loop_index++] = float2((vert + 1.0f) / verts_num, 1.0f);
|
||||
uvs[loop_index++] = float2(vert / verts_num, 1.0f);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Triangles connect the top and bottom section. */
|
||||
if (!top_is_point) {
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f);
|
||||
uvs[loop_index++] = float2(0.75f, 0.25f);
|
||||
uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f);
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_assert(!bottom_is_point);
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
uvs[loop_index++] = circle[i];
|
||||
uvs[loop_index++] = circle[(i + 1) % verts_num];
|
||||
uvs[loop_index++] = float2(0.25f, 0.25f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Create bottom corners and faces. */
|
||||
if (!bottom_is_point) {
|
||||
if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
/* Go backwards because of reversed face normal. */
|
||||
uvs[loop_index++] = circle[verts_num - 1 - i] + float2(0.5f, 0.0f);
|
||||
}
|
||||
}
|
||||
else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f);
|
||||
uvs[loop_index++] = float2(0.75f, 0.25f);
|
||||
uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uv_attribute.apply_span_and_save();
|
||||
}
|
||||
|
||||
Mesh *create_cylinder_or_cone_mesh(const float radius_top,
|
||||
const float radius_bottom,
|
||||
const float depth,
|
||||
const int verts_num,
|
||||
const GeometryNodeMeshCircleFillType fill_type)
|
||||
{
|
||||
float4x4 transform;
|
||||
loc_eul_size_to_mat4(transform.values, location, rotation, float3(1));
|
||||
|
||||
const bool top_is_point = radius_top == 0.0f;
|
||||
const bool bottom_is_point = radius_bottom == 0.0f;
|
||||
/* Handle the case of a line / single point before everything else to avoid
|
||||
* the need to check for it later. */
|
||||
if (top_is_point && bottom_is_point) {
|
||||
const bool single_vertex = depth == 0.0f;
|
||||
Mesh *mesh = BKE_mesh_new_nomain(single_vertex ? 1 : 2, single_vertex ? 0 : 1, 0, 0, 0);
|
||||
copy_v3_v3(mesh->mvert[0].co, float3(0.0f, 0.0f, depth));
|
||||
if (single_vertex) {
|
||||
const short up[3] = {0, 0, SHRT_MAX};
|
||||
copy_v3_v3_short(mesh->mvert[0].no, up);
|
||||
return mesh;
|
||||
}
|
||||
copy_v3_v3(mesh->mvert[1].co, float3(0.0f, 0.0f, -depth));
|
||||
mesh->medge[0].v1 = 0;
|
||||
mesh->medge[0].v2 = 1;
|
||||
mesh->medge[0].flag |= ME_LOOSEEDGE;
|
||||
BKE_mesh_calc_normals(mesh);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
const BMeshCreateParams bmcp = {true};
|
||||
const BMAllocTemplate allocsize = {
|
||||
Mesh *mesh = BKE_mesh_new_nomain(
|
||||
vert_total(fill_type, verts_num, top_is_point, bottom_is_point),
|
||||
edge_total(fill_type, verts_num, top_is_point, bottom_is_point),
|
||||
0,
|
||||
corner_total(fill_type, verts_num, top_is_point, bottom_is_point),
|
||||
face_total(fill_type, verts_num, top_is_point, bottom_is_point)};
|
||||
BMesh *bm = BM_mesh_create(&allocsize, &bmcp);
|
||||
face_total(fill_type, verts_num, top_is_point, bottom_is_point));
|
||||
MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
|
||||
MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
|
||||
MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
|
||||
MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
|
||||
|
||||
const bool cap_end = (fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE);
|
||||
const bool cap_tri = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN);
|
||||
BMO_op_callf(bm,
|
||||
BMO_FLAG_DEFAULTS,
|
||||
"create_cone segments=%i diameter1=%f diameter2=%f cap_ends=%b "
|
||||
"cap_tris=%b depth=%f matrix=%m4 calc_uvs=%b",
|
||||
verts_num,
|
||||
radius_bottom,
|
||||
radius_top,
|
||||
cap_end,
|
||||
cap_tri,
|
||||
depth,
|
||||
transform.values,
|
||||
true);
|
||||
/* Calculate vertex positions. */
|
||||
const int top_verts_start = 0;
|
||||
const int bottom_verts_start = top_verts_start + (!top_is_point ? verts_num : 1);
|
||||
float angle = 0.0f;
|
||||
const float angle_delta = 2.0f * M_PI / static_cast<float>(verts_num);
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
const float x = std::cos(angle);
|
||||
const float y = std::sin(angle);
|
||||
if (!top_is_point) {
|
||||
copy_v3_v3(verts[top_verts_start + i].co, float3(x * radius_top, y * radius_top, depth));
|
||||
}
|
||||
if (!bottom_is_point) {
|
||||
copy_v3_v3(verts[bottom_verts_start + i].co,
|
||||
float3(x * radius_bottom, y * radius_bottom, -depth));
|
||||
}
|
||||
angle += angle_delta;
|
||||
}
|
||||
if (top_is_point) {
|
||||
copy_v3_v3(verts[top_verts_start].co, float3(0.0f, 0.0f, depth));
|
||||
}
|
||||
if (bottom_is_point) {
|
||||
copy_v3_v3(verts[bottom_verts_start].co, float3(0.0f, 0.0f, -depth));
|
||||
}
|
||||
|
||||
Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
|
||||
BM_mesh_bm_to_me_for_eval(bm, mesh, nullptr);
|
||||
BM_mesh_free(bm);
|
||||
/* Add center vertices for the triangle fans at the end. */
|
||||
const int top_center_vert_index = bottom_verts_start + (bottom_is_point ? 1 : verts_num);
|
||||
const int bottom_center_vert_index = top_center_vert_index + (top_is_point ? 0 : 1);
|
||||
if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
|
||||
if (!top_is_point) {
|
||||
copy_v3_v3(verts[top_center_vert_index].co, float3(0.0f, 0.0f, depth));
|
||||
}
|
||||
if (!bottom_is_point) {
|
||||
copy_v3_v3(verts[bottom_center_vert_index].co, float3(0.0f, 0.0f, -depth));
|
||||
}
|
||||
}
|
||||
|
||||
/* Create top edges. */
|
||||
const int top_edges_start = 0;
|
||||
const int top_fan_edges_start = (!top_is_point &&
|
||||
fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ?
|
||||
top_edges_start + verts_num :
|
||||
top_edges_start;
|
||||
if (!top_is_point) {
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
MEdge &edge = edges[top_edges_start + i];
|
||||
edge.v1 = top_verts_start + i;
|
||||
edge.v2 = top_verts_start + (i + 1) % verts_num;
|
||||
}
|
||||
if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
MEdge &edge = edges[top_fan_edges_start + i];
|
||||
edge.v1 = top_center_vert_index;
|
||||
edge.v2 = top_verts_start + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Create connecting edges. */
|
||||
const int connecting_edges_start = top_fan_edges_start + (!top_is_point ? verts_num : 0);
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
MEdge &edge = edges[connecting_edges_start + i];
|
||||
edge.v1 = top_verts_start + (!top_is_point ? i : 0);
|
||||
edge.v2 = bottom_verts_start + (!bottom_is_point ? i : 0);
|
||||
}
|
||||
|
||||
/* Create bottom edges. */
|
||||
const int bottom_edges_start = connecting_edges_start + verts_num;
|
||||
const int bottom_fan_edges_start = (!bottom_is_point &&
|
||||
fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ?
|
||||
bottom_edges_start + verts_num :
|
||||
bottom_edges_start;
|
||||
if (!bottom_is_point) {
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
MEdge &edge = edges[bottom_edges_start + i];
|
||||
edge.v1 = bottom_verts_start + i;
|
||||
edge.v2 = bottom_verts_start + (i + 1) % verts_num;
|
||||
}
|
||||
if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
MEdge &edge = edges[bottom_fan_edges_start + i];
|
||||
edge.v1 = bottom_center_vert_index;
|
||||
edge.v2 = bottom_verts_start + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Create top corners and faces. */
|
||||
int loop_index = 0;
|
||||
int poly_index = 0;
|
||||
if (!top_is_point) {
|
||||
if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
|
||||
MPoly &poly = polys[poly_index++];
|
||||
poly.loopstart = loop_index;
|
||||
poly.totloop = verts_num;
|
||||
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
MLoop &loop = loops[loop_index++];
|
||||
loop.v = top_verts_start + i;
|
||||
loop.e = top_edges_start + i;
|
||||
}
|
||||
}
|
||||
else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
MPoly &poly = polys[poly_index++];
|
||||
poly.loopstart = loop_index;
|
||||
poly.totloop = 3;
|
||||
|
||||
MLoop &loop_a = loops[loop_index++];
|
||||
loop_a.v = top_verts_start + i;
|
||||
loop_a.e = top_edges_start + i;
|
||||
MLoop &loop_b = loops[loop_index++];
|
||||
loop_b.v = top_verts_start + (i + 1) % verts_num;
|
||||
loop_b.e = top_fan_edges_start + (i + 1) % verts_num;
|
||||
MLoop &loop_c = loops[loop_index++];
|
||||
loop_c.v = top_center_vert_index;
|
||||
loop_c.e = top_fan_edges_start + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Create side corners and faces. */
|
||||
if (!top_is_point && !bottom_is_point) {
|
||||
/* Quads connect the top and bottom. */
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
MPoly &poly = polys[poly_index++];
|
||||
poly.loopstart = loop_index;
|
||||
poly.totloop = 4;
|
||||
|
||||
MLoop &loop_a = loops[loop_index++];
|
||||
loop_a.v = top_verts_start + i;
|
||||
loop_a.e = connecting_edges_start + i;
|
||||
MLoop &loop_b = loops[loop_index++];
|
||||
loop_b.v = bottom_verts_start + i;
|
||||
loop_b.e = bottom_edges_start + i;
|
||||
MLoop &loop_c = loops[loop_index++];
|
||||
loop_c.v = bottom_verts_start + (i + 1) % verts_num;
|
||||
loop_c.e = connecting_edges_start + (i + 1) % verts_num;
|
||||
MLoop &loop_d = loops[loop_index++];
|
||||
loop_d.v = top_verts_start + (i + 1) % verts_num;
|
||||
loop_d.e = top_edges_start + i;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Triangles connect the top and bottom section. */
|
||||
if (!top_is_point) {
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
MPoly &poly = polys[poly_index++];
|
||||
poly.loopstart = loop_index;
|
||||
poly.totloop = 3;
|
||||
|
||||
MLoop &loop_a = loops[loop_index++];
|
||||
loop_a.v = top_verts_start + i;
|
||||
loop_a.e = connecting_edges_start + i;
|
||||
MLoop &loop_b = loops[loop_index++];
|
||||
loop_b.v = bottom_verts_start;
|
||||
loop_b.e = connecting_edges_start + (i + 1) % verts_num;
|
||||
MLoop &loop_c = loops[loop_index++];
|
||||
loop_c.v = top_verts_start + (i + 1) % verts_num;
|
||||
loop_c.e = top_edges_start + i;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_assert(!bottom_is_point);
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
MPoly &poly = polys[poly_index++];
|
||||
poly.loopstart = loop_index;
|
||||
poly.totloop = 3;
|
||||
|
||||
MLoop &loop_a = loops[loop_index++];
|
||||
loop_a.v = bottom_verts_start + i;
|
||||
loop_a.e = bottom_edges_start + i;
|
||||
MLoop &loop_b = loops[loop_index++];
|
||||
loop_b.v = bottom_verts_start + (i + 1) % verts_num;
|
||||
loop_b.e = connecting_edges_start + (i + 1) % verts_num;
|
||||
MLoop &loop_c = loops[loop_index++];
|
||||
loop_c.v = top_verts_start;
|
||||
loop_c.e = connecting_edges_start + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Create bottom corners and faces. */
|
||||
if (!bottom_is_point) {
|
||||
if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) {
|
||||
MPoly &poly = polys[poly_index++];
|
||||
poly.loopstart = loop_index;
|
||||
poly.totloop = verts_num;
|
||||
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
/* Go backwards to reverse surface normal. */
|
||||
MLoop &loop = loops[loop_index++];
|
||||
loop.v = bottom_verts_start + verts_num - 1 - i;
|
||||
loop.e = bottom_edges_start + verts_num - 1 - (i + 1) % verts_num;
|
||||
}
|
||||
}
|
||||
else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) {
|
||||
for (const int i : IndexRange(verts_num)) {
|
||||
MPoly &poly = polys[poly_index++];
|
||||
poly.loopstart = loop_index;
|
||||
poly.totloop = 3;
|
||||
|
||||
MLoop &loop_a = loops[loop_index++];
|
||||
loop_a.v = bottom_verts_start + i;
|
||||
loop_a.e = bottom_fan_edges_start + i;
|
||||
MLoop &loop_b = loops[loop_index++];
|
||||
loop_b.v = bottom_center_vert_index;
|
||||
loop_b.e = bottom_fan_edges_start + (i + 1) % verts_num;
|
||||
MLoop &loop_c = loops[loop_index++];
|
||||
loop_c.v = bottom_verts_start + (i + 1) % verts_num;
|
||||
loop_c.e = bottom_edges_start + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BKE_mesh_calc_normals(mesh);
|
||||
|
||||
calculate_uvs(mesh, top_is_point, bottom_is_point, verts_num, fill_type);
|
||||
|
||||
BLI_assert(BKE_mesh_is_valid(mesh));
|
||||
|
||||
return mesh;
|
||||
}
|
||||
@@ -256,7 +558,10 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params)
|
||||
const float3 rotation = params.extract_input<float3>("Rotation");
|
||||
|
||||
Mesh *mesh = create_cylinder_or_cone_mesh(
|
||||
location, rotation, radius_top, radius_bottom, depth, verts_num, fill_type);
|
||||
radius_top, radius_bottom, depth, verts_num, fill_type);
|
||||
|
||||
/* Transform the mesh so that the base of the cone is at the origin. */
|
||||
transform_mesh(mesh, location + float3(0.0f, 0.0f, depth), rotation, float3(1));
|
||||
|
||||
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
|
||||
}
|
||||
|
@@ -79,8 +79,7 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params)
|
||||
const float3 rotation = params.extract_input<float3>("Rotation");
|
||||
|
||||
/* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */
|
||||
Mesh *mesh = create_cylinder_or_cone_mesh(
|
||||
location, rotation, radius, radius, depth, verts_num, fill_type);
|
||||
Mesh *mesh = create_cylinder_or_cone_mesh(radius, radius, depth, verts_num, fill_type);
|
||||
|
||||
transform_mesh(mesh, location, rotation, float3(1));
|
||||
|
||||
|
@@ -17,22 +17,19 @@
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_mesh.h"
|
||||
|
||||
#include "UI_interface.h"
|
||||
#include "UI_resources.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
static bNodeSocketTemplate geo_node_mesh_primitive_uv_sphere_in[] = {
|
||||
{SOCK_INT, N_("Segments"), 32, 0.0f, 0.0f, 0.0f, 3, 1024},
|
||||
{SOCK_INT, N_("Rings"), 16, 0.0f, 0.0f, 0.0f, 3, 1024},
|
||||
{SOCK_INT, N_("Rings"), 16, 0.0f, 0.0f, 0.0f, 2, 1024},
|
||||
{SOCK_FLOAT, N_("Radius"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, FLT_MAX, PROP_DISTANCE},
|
||||
{SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
|
||||
{SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 0.0f, -FLT_MAX, FLT_MAX, PROP_EULER},
|
||||
{SOCK_VECTOR, N_("Location"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_TRANSLATION},
|
||||
{SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER},
|
||||
{-1, ""},
|
||||
};
|
||||
|
||||
@@ -67,34 +64,219 @@ static int sphere_face_total(const int segments, const int rings)
|
||||
return quads + triangles;
|
||||
}
|
||||
|
||||
static Mesh *create_uv_sphere_mesh_bmesh(const float3 location,
|
||||
const float3 rotation,
|
||||
static void calculate_sphere_vertex_data(MutableSpan<MVert> verts,
|
||||
const float radius,
|
||||
const int segments,
|
||||
const int rings)
|
||||
{
|
||||
float4x4 transform;
|
||||
loc_eul_size_to_mat4(transform.values, location, rotation, float3(radius));
|
||||
const float delta_theta = M_PI / rings;
|
||||
const float delta_phi = (2 * M_PI) / segments;
|
||||
|
||||
const BMeshCreateParams bmcp = {true};
|
||||
const BMAllocTemplate allocsize = {sphere_vert_total(segments, rings),
|
||||
sphere_edge_total(segments, rings),
|
||||
sphere_corner_total(segments, rings),
|
||||
sphere_face_total(segments, rings)};
|
||||
BMesh *bm = BM_mesh_create(&allocsize, &bmcp);
|
||||
copy_v3_v3(verts[0].co, float3(0.0f, 0.0f, radius));
|
||||
normal_float_to_short_v3(verts[0].no, float3(0.0f, 0.0f, 1.0f));
|
||||
|
||||
BMO_op_callf(bm,
|
||||
BMO_FLAG_DEFAULTS,
|
||||
"create_uvsphere u_segments=%i v_segments=%i diameter=%f matrix=%m4 calc_uvs=%b",
|
||||
segments,
|
||||
rings,
|
||||
radius,
|
||||
transform.values,
|
||||
true);
|
||||
int vert_index = 1;
|
||||
float theta = delta_theta;
|
||||
for (const int UNUSED(ring) : IndexRange(rings - 1)) {
|
||||
float phi = 0.0f;
|
||||
const float z = cosf(theta);
|
||||
for (const int UNUSED(segment) : IndexRange(segments)) {
|
||||
const float x = std::sin(theta) * std::cos(phi);
|
||||
const float y = std::sin(theta) * std::sin(phi);
|
||||
copy_v3_v3(verts[vert_index].co, float3(x, y, z) * radius);
|
||||
normal_float_to_short_v3(verts[vert_index].no, float3(x, y, z));
|
||||
phi += delta_phi;
|
||||
vert_index++;
|
||||
}
|
||||
theta += delta_theta;
|
||||
}
|
||||
|
||||
Mesh *mesh = (Mesh *)BKE_id_new_nomain(ID_ME, nullptr);
|
||||
BM_mesh_bm_to_me_for_eval(bm, mesh, nullptr);
|
||||
BM_mesh_free(bm);
|
||||
copy_v3_v3(verts.last().co, float3(0.0f, 0.0f, -radius));
|
||||
normal_float_to_short_v3(verts.last().no, float3(0.0f, 0.0f, -1.0f));
|
||||
}
|
||||
|
||||
static void calculate_sphere_edge_indices(MutableSpan<MEdge> edges,
|
||||
const int segments,
|
||||
const int rings)
|
||||
{
|
||||
int edge_index = 0;
|
||||
|
||||
/* Add the edges connecting the top vertex to the first ring. */
|
||||
const int first_vert_ring_index_start = 1;
|
||||
for (const int segment : IndexRange(segments)) {
|
||||
MEdge &edge = edges[edge_index++];
|
||||
edge.v1 = 0;
|
||||
edge.v2 = first_vert_ring_index_start + segment;
|
||||
}
|
||||
|
||||
int ring_vert_index_start = 1;
|
||||
for (const int ring : IndexRange(rings - 1)) {
|
||||
const int next_ring_vert_index_start = ring_vert_index_start + segments;
|
||||
|
||||
/* Add the edges running along each ring. */
|
||||
for (const int segment : IndexRange(segments)) {
|
||||
MEdge &edge_in_ring = edges[edge_index++];
|
||||
edge_in_ring.v1 = ring_vert_index_start + segment;
|
||||
edge_in_ring.v2 = ring_vert_index_start + ((segment + 1) % segments);
|
||||
}
|
||||
|
||||
/* Add the edges connecting to the next ring. */
|
||||
if (ring < rings - 2) {
|
||||
for (const int segment : IndexRange(segments)) {
|
||||
MEdge &edge_to_next_ring = edges[edge_index++];
|
||||
edge_to_next_ring.v1 = ring_vert_index_start + segment;
|
||||
edge_to_next_ring.v2 = next_ring_vert_index_start + segment;
|
||||
}
|
||||
}
|
||||
ring_vert_index_start += segments;
|
||||
}
|
||||
|
||||
/* Add the edges connecting the last ring to the bottom vertex. */
|
||||
const int last_vert_index = sphere_vert_total(segments, rings) - 1;
|
||||
const int last_vert_ring_start = last_vert_index - segments;
|
||||
for (const int segment : IndexRange(segments)) {
|
||||
MEdge &edge = edges[edge_index++];
|
||||
edge.v1 = last_vert_index;
|
||||
edge.v2 = last_vert_ring_start + segment;
|
||||
}
|
||||
}
|
||||
|
||||
static void calculate_sphere_faces(MutableSpan<MLoop> loops,
|
||||
MutableSpan<MPoly> polys,
|
||||
const int segments,
|
||||
const int rings)
|
||||
{
|
||||
int loop_index = 0;
|
||||
int poly_index = 0;
|
||||
|
||||
/* Add the triangles conntected to the top vertex. */
|
||||
const int first_vert_ring_index_start = 1;
|
||||
for (const int segment : IndexRange(segments)) {
|
||||
MPoly &poly = polys[poly_index++];
|
||||
poly.loopstart = loop_index;
|
||||
poly.totloop = 3;
|
||||
MLoop &loop_a = loops[loop_index++];
|
||||
loop_a.v = 0;
|
||||
loop_a.e = segment;
|
||||
MLoop &loop_b = loops[loop_index++];
|
||||
loop_b.v = first_vert_ring_index_start + segment;
|
||||
loop_b.e = segments + segment;
|
||||
MLoop &loop_c = loops[loop_index++];
|
||||
loop_c.v = first_vert_ring_index_start + (segment + 1) % segments;
|
||||
loop_c.e = (segment + 1) % segments;
|
||||
}
|
||||
|
||||
int ring_vert_index_start = 1;
|
||||
int ring_edge_index_start = segments;
|
||||
for (const int UNUSED(ring) : IndexRange(1, rings - 2)) {
|
||||
const int next_ring_vert_index_start = ring_vert_index_start + segments;
|
||||
const int next_ring_edge_index_start = ring_edge_index_start + segments * 2;
|
||||
const int ring_vertical_edge_index_start = ring_edge_index_start + segments;
|
||||
|
||||
for (const int segment : IndexRange(segments)) {
|
||||
MPoly &poly = polys[poly_index++];
|
||||
poly.loopstart = loop_index;
|
||||
poly.totloop = 4;
|
||||
|
||||
MLoop &loop_a = loops[loop_index++];
|
||||
loop_a.v = ring_vert_index_start + segment;
|
||||
loop_a.e = ring_vertical_edge_index_start + segment;
|
||||
MLoop &loop_b = loops[loop_index++];
|
||||
loop_b.v = next_ring_vert_index_start + segment;
|
||||
loop_b.e = next_ring_edge_index_start + segment;
|
||||
MLoop &loop_c = loops[loop_index++];
|
||||
loop_c.v = next_ring_vert_index_start + (segment + 1) % segments;
|
||||
loop_c.e = ring_vertical_edge_index_start + (segment + 1) % segments;
|
||||
MLoop &loop_d = loops[loop_index++];
|
||||
loop_d.v = ring_vert_index_start + (segment + 1) % segments;
|
||||
loop_d.e = ring_edge_index_start + segment;
|
||||
}
|
||||
ring_vert_index_start += segments;
|
||||
ring_edge_index_start += segments * 2;
|
||||
}
|
||||
|
||||
/* Add the triangles connected to the bottom vertex. */
|
||||
const int last_edge_ring_start = segments * (rings - 2) * 2 + segments;
|
||||
const int bottom_edge_fan_start = last_edge_ring_start + segments;
|
||||
const int last_vert_index = sphere_vert_total(segments, rings) - 1;
|
||||
const int last_vert_ring_start = last_vert_index - segments;
|
||||
for (const int segment : IndexRange(segments)) {
|
||||
MPoly &poly = polys[poly_index++];
|
||||
poly.loopstart = loop_index;
|
||||
poly.totloop = 3;
|
||||
|
||||
MLoop &loop_a = loops[loop_index++];
|
||||
loop_a.v = last_vert_index;
|
||||
loop_a.e = bottom_edge_fan_start + (segment + 1) % segments;
|
||||
MLoop &loop_b = loops[loop_index++];
|
||||
loop_b.v = last_vert_ring_start + (segment + 1) % segments;
|
||||
loop_b.e = last_edge_ring_start + segment;
|
||||
MLoop &loop_c = loops[loop_index++];
|
||||
loop_c.v = last_vert_ring_start + segment;
|
||||
loop_c.e = bottom_edge_fan_start + segment;
|
||||
}
|
||||
}
|
||||
|
||||
static void calculate_sphere_uvs(Mesh *mesh, const float segments, const float rings)
|
||||
{
|
||||
MeshComponent mesh_component;
|
||||
mesh_component.replace(mesh, GeometryOwnershipType::Editable);
|
||||
OutputAttributePtr uv_attribute = mesh_component.attribute_try_get_for_output(
|
||||
"uv", ATTR_DOMAIN_CORNER, CD_PROP_FLOAT2, nullptr);
|
||||
MutableSpan<float2> uvs = uv_attribute->get_span_for_write_only<float2>();
|
||||
|
||||
int loop_index = 0;
|
||||
const float dy = 1.0f / rings;
|
||||
|
||||
for (const int i_segment : IndexRange(segments)) {
|
||||
const float segment = static_cast<float>(i_segment);
|
||||
uvs[loop_index++] = float2((segment + 0.5f) / segments, 0.0f);
|
||||
uvs[loop_index++] = float2((segment + 1.0f) / segments, dy);
|
||||
uvs[loop_index++] = float2(segment / segments, dy);
|
||||
}
|
||||
|
||||
for (const int i_ring : IndexRange(1, rings - 2)) {
|
||||
const float ring = static_cast<float>(i_ring);
|
||||
for (const int i_segment : IndexRange(segments)) {
|
||||
const float segment = static_cast<float>(i_segment);
|
||||
uvs[loop_index++] = float2(segment / segments, ring / rings);
|
||||
uvs[loop_index++] = float2((segment + 1.0f) / segments, ring / rings);
|
||||
uvs[loop_index++] = float2((segment + 1.0f) / segments, (ring + 1.0f) / rings);
|
||||
uvs[loop_index++] = float2(segment / segments, (ring + 1.0f) / rings);
|
||||
}
|
||||
}
|
||||
|
||||
for (const int i_segment : IndexRange(segments)) {
|
||||
const float segment = static_cast<float>(i_segment);
|
||||
uvs[loop_index++] = float2((segment + 0.5f) / segments, 1.0f);
|
||||
uvs[loop_index++] = float2((segment + 1.0f) / segments, 1.0f - dy);
|
||||
uvs[loop_index++] = float2(segment / segments, 1.0f - dy);
|
||||
}
|
||||
|
||||
uv_attribute.apply_span_and_save();
|
||||
}
|
||||
|
||||
static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const int rings)
|
||||
{
|
||||
Mesh *mesh = BKE_mesh_new_nomain(sphere_vert_total(segments, rings),
|
||||
sphere_edge_total(segments, rings),
|
||||
0,
|
||||
sphere_corner_total(segments, rings),
|
||||
sphere_face_total(segments, rings));
|
||||
MutableSpan<MVert> verts{mesh->mvert, mesh->totvert};
|
||||
MutableSpan<MLoop> loops{mesh->mloop, mesh->totloop};
|
||||
MutableSpan<MEdge> edges{mesh->medge, mesh->totedge};
|
||||
MutableSpan<MPoly> polys{mesh->mpoly, mesh->totpoly};
|
||||
|
||||
calculate_sphere_vertex_data(verts, radius, segments, rings);
|
||||
|
||||
calculate_sphere_edge_indices(edges, segments, rings);
|
||||
|
||||
calculate_sphere_faces(loops, polys, segments, rings);
|
||||
|
||||
calculate_sphere_uvs(mesh, segments, rings);
|
||||
|
||||
BLI_assert(BKE_mesh_is_valid(mesh));
|
||||
|
||||
return mesh;
|
||||
}
|
||||
@@ -103,7 +285,7 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params)
|
||||
{
|
||||
const int segments_num = params.extract_input<int>("Segments");
|
||||
const int rings_num = params.extract_input<int>("Rings");
|
||||
if (segments_num < 3 || rings_num < 3) {
|
||||
if (segments_num < 3 || rings_num < 2) {
|
||||
params.set_output("Geometry", GeometrySet());
|
||||
return;
|
||||
}
|
||||
@@ -112,7 +294,10 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params)
|
||||
const float3 location = params.extract_input<float3>("Location");
|
||||
const float3 rotation = params.extract_input<float3>("Rotation");
|
||||
|
||||
Mesh *mesh = create_uv_sphere_mesh_bmesh(location, rotation, radius, segments_num, rings_num);
|
||||
Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num);
|
||||
|
||||
transform_mesh(mesh, location, rotation, float3(1));
|
||||
|
||||
params.set_output("Geometry", GeometrySet::create_with_mesh(mesh));
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user