Python API: add loop triangles access, remove tessfaces.

Loop triangles are tessellated triangles create from polygons, for renderers
or exporters that need to match Blender's polygon tesselation exactly. These
are a read-only runtime cache.

Tessfaces are a legacy data structure from before Blender supported n-gons,
and were already mostly removed from the C code.

Details on porting code to loop triangles is in the release notes.

Differential Revision: https://developer.blender.org/D3539
This commit is contained in:
Brecht Van Lommel 2018-09-06 14:28:14 +02:00
parent 468474a653
commit e65784a051
21 changed files with 261 additions and 1236 deletions

View File

@ -83,7 +83,7 @@ When writing scripts that operate on editmode data you will normally want to re-
running the script, this needs to be called explicitly.
The BMesh its self does not store the triangulated faces, they are stored in the :class:`bpy.types.Mesh`,
to refresh tessellation faces call :class:`bpy.types.Mesh.calc_tessface`.
to refresh tessellation triangles call :class:`bpy.types.Mesh.calc_loop_triangles`.
CustomData Access

View File

@ -164,26 +164,26 @@ for list removal, but these are slower.
Sometimes its faster (but more memory hungry) to just rebuild the list.
Say you want to remove all triangular faces in a list.
Say you want to remove all triangular polygons in a list.
Rather than...
.. code-block:: python
faces = mesh.tessfaces[:] # make a list copy of the meshes faces
f_idx = len(faces) # Loop backwards
while f_idx: # while the value is not 0
f_idx -= 1
polygons = mesh.polygons[:] # make a list copy of the meshes polygons
p_idx = len(polygons) # Loop backwards
while p_idx: # while the value is not 0
p_idx -= 1
if len(faces[f_idx].vertices) == 3:
faces.pop(f_idx) # remove the triangle
if len(polygons[p_idx].vertices) == 3:
polygons.pop(p_idx) # remove the triangle
It's faster to build a new list with list comprehension.
.. code-block:: python
faces = [f for f in mesh.tessfaces if len(f.vertices) != 3]
polygons = [p for p in mesh.polygons if len(p.vertices) != 3]
Adding List Items

View File

@ -173,25 +173,25 @@ In this situation you can...
.. _info_gotcha_mesh_faces:
N-Gons and Tessellation Faces
=============================
N-Gons and Tessellation
=======================
Since 2.63 NGons are supported, this adds some complexity
since in some cases you need to access triangles/quads still (some exporters for example).
since in some cases you need to access triangles still (some exporters for example).
There are now 3 ways to access faces:
- :class:`bpy.types.MeshPolygon` -
this is the data structure which now stores faces in object mode
(access as ``mesh.polygons`` rather than ``mesh.faces``).
- :class:`bpy.types.MeshTessFace` -
the result of triangulating (tessellated) polygons,
the main method of face access in 2.62 or older (access as ``mesh.tessfaces``).
- :class:`bpy.types.MeshLoopTriangle` -
the result of tessellating polygons into triangles
(access as ``mesh.loop_triangles``).
- :class:`bmesh.types.BMFace` -
the polygons as used in editmode.
For the purpose of the following documentation,
these will be referred to as polygons, tessfaces and bmesh-faces respectively.
these will be referred to as polygons, loop triangles and bmesh-faces respectively.
5+ sided faces will be referred to as ``ngons``.
@ -234,13 +234,8 @@ All 3 datatypes can be used for face creation.
- polygons are the most efficient way to create faces but the data structure is _very_ rigid and inflexible,
you must have all your vertes and faces ready and create them all at once.
This is further complicated by the fact that each polygon does not store its own verts (as with tessfaces),
This is further complicated by the fact that each polygon does not store its own verts,
rather they reference an index and size in :class:`bpy.types.Mesh.loops` which are a fixed array too.
- tessfaces ideally should not be used for creating faces since they are really only tessellation cache of polygons,
however for scripts upgrading from 2.62 this is by far the most straightforward option.
This works by creating tessfaces and when finished -
they can be converted into polygons by calling :class:`bpy.types.Mesh.update`.
The obvious limitation is ngons can't be created this way.
- bmesh-faces are most likely the easiest way for new scripts to create faces,
since faces can be added one by one and the api has features intended for mesh manipulation.
While :class:`bmesh.types.BMesh` uses more memory it can be managed by only operating on one mesh at a time.
@ -271,30 +266,6 @@ the choice mostly depends on whether the target format supports ngons or not.
since using bmesh gives some overhead because its not the native storage format in object mode.
Upgrading Importers from 2.62
-----------------------------
Importers can be upgraded to work with only minor changes.
The main change to be made is used the tessellation versions of each attribute.
- mesh.faces --> :class:`bpy.types.Mesh.tessfaces`
- mesh.uv_textures --> :class:`bpy.types.Mesh.tessface_uv_textures`
- mesh.vertex_colors --> :class:`bpy.types.Mesh.tessface_vertex_colors`
Once the data is created call :class:`bpy.types.Mesh.update` to convert the tessfaces into polygons.
Upgrading Exporters from 2.62
-----------------------------
For exporters the most direct way to upgrade is to use tessfaces as with importing
however its important to know that tessfaces may **not** exist for a mesh,
the array will be empty as if there are no faces.
So before accessing tessface data call: :class:`bpy.types.Mesh.update` ``(calc_tessface=True)``.
EditBones, PoseBones, Bone... Bones
===================================

View File

@ -247,11 +247,11 @@ static bool ObtainCacheParticleUV(Mesh *mesh,
b_psys.particles.begin(b_pa);
for(; pa_no < totparts+totchild; pa_no++) {
/* Add UVs */
BL::Mesh::tessface_uv_textures_iterator l;
b_mesh->tessface_uv_textures.begin(l);
BL::Mesh::uv_layers_iterator l;
b_mesh->uv_layers.begin(l);
float3 uv = make_float3(0.0f, 0.0f, 0.0f);
if(b_mesh->tessface_uv_textures.length())
if(b_mesh->uv_layers.length())
b_psys.uv_on_emitter(psmd, *b_pa, pa_no, uv_num, &uv.x);
CData->curve_uv.push_back_slow(uv);
@ -306,11 +306,11 @@ static bool ObtainCacheParticleVcol(Mesh *mesh,
b_psys.particles.begin(b_pa);
for(; pa_no < totparts+totchild; pa_no++) {
/* Add vertex colors */
BL::Mesh::tessface_vertex_colors_iterator l;
b_mesh->tessface_vertex_colors.begin(l);
BL::Mesh::vertex_colors_iterator l;
b_mesh->vertex_colors.begin(l);
float3 vcol = make_float3(0.0f, 0.0f, 0.0f);
if(b_mesh->tessface_vertex_colors.length())
if(b_mesh->vertex_colors.length())
b_psys.mcol_on_emitter(psmd, *b_pa, pa_no, vcol_num, &vcol.x);
CData->curve_vcol.push_back_slow(vcol);
@ -968,10 +968,10 @@ void BlenderSync::sync_curves(Mesh *mesh,
/* create vertex color attributes */
if(!motion) {
BL::Mesh::tessface_vertex_colors_iterator l;
BL::Mesh::vertex_colors_iterator l;
int vcol_num = 0;
for(b_mesh.tessface_vertex_colors.begin(l); l != b_mesh.tessface_vertex_colors.end(); ++l, vcol_num++) {
for(b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l, vcol_num++) {
if(!mesh->need_attribute(scene, ustring(l->name().c_str())))
continue;
@ -1005,10 +1005,10 @@ void BlenderSync::sync_curves(Mesh *mesh,
/* create UV attributes */
if(!motion) {
BL::Mesh::tessface_uv_textures_iterator l;
BL::Mesh::uv_layers_iterator l;
int uv_num = 0;
for(b_mesh.tessface_uv_textures.begin(l); l != b_mesh.tessface_uv_textures.end(); ++l, uv_num++) {
for(b_mesh.uv_layers.begin(l); l != b_mesh.uv_layers.end(); ++l, uv_num++) {
bool active_render = l->active_render();
AttributeStandard std = (active_render)? ATTR_STD_UV: ATTR_STD_NONE;
ustring name = ustring(l->name().c_str());

View File

@ -35,46 +35,6 @@
CCL_NAMESPACE_BEGIN
/* Per-face bit flags. */
enum {
/* Face has no special flags. */
FACE_FLAG_NONE = (0 << 0),
/* Quad face was split using 1-3 diagonal. */
FACE_FLAG_DIVIDE_13 = (1 << 0),
/* Quad face was split using 2-4 diagonal. */
FACE_FLAG_DIVIDE_24 = (1 << 1),
};
/* Get vertex indices to create triangles from a given face.
*
* Two triangles has vertex indices in the original Blender-side face.
* If face is already a quad tri_b will not be initialized.
*/
inline void face_split_tri_indices(const int face_flag,
int tri_a[3],
int tri_b[3])
{
if(face_flag & FACE_FLAG_DIVIDE_24) {
tri_a[0] = 0;
tri_a[1] = 1;
tri_a[2] = 3;
tri_b[0] = 2;
tri_b[1] = 3;
tri_b[2] = 1;
}
else {
/* Quad with FACE_FLAG_DIVIDE_13 or single triangle. */
tri_a[0] = 0;
tri_a[1] = 1;
tri_a[2] = 2;
tri_b[0] = 0;
tri_b[1] = 2;
tri_b[2] = 3;
}
}
/* Tangent Space */
struct MikkUserData {
@ -379,8 +339,6 @@ static void create_mesh_volume_attributes(Scene *scene,
static void attr_create_vertex_color(Scene *scene,
Mesh *mesh,
BL::Mesh& b_mesh,
const vector<int>& nverts,
const vector<int>& face_flags,
bool subdivision)
{
if(subdivision) {
@ -401,15 +359,15 @@ static void attr_create_vertex_color(Scene *scene,
int n = p->loop_total();
for(int i = 0; i < n; i++) {
float3 color = get_float3(l->data[p->loop_start() + i].color());
/* Encode vertex color using the sRGB curve. */
/* Compress/encode vertex color using the sRGB curve. */
*(cdata++) = color_float_to_byte(color_srgb_to_linear_v3(color));
}
}
}
}
else {
BL::Mesh::tessface_vertex_colors_iterator l;
for(b_mesh.tessface_vertex_colors.begin(l); l != b_mesh.tessface_vertex_colors.end(); ++l) {
BL::Mesh::vertex_colors_iterator l;
for(b_mesh.vertex_colors.begin(l); l != b_mesh.vertex_colors.end(); ++l) {
if(!mesh->need_attribute(scene, ustring(l->name().c_str())))
continue;
@ -417,35 +375,20 @@ static void attr_create_vertex_color(Scene *scene,
TypeDesc::TypeColor,
ATTR_ELEMENT_CORNER_BYTE);
BL::MeshColorLayer::data_iterator c;
BL::Mesh::loop_triangles_iterator t;
uchar4 *cdata = attr->data_uchar4();
size_t i = 0;
for(l->data.begin(c); c != l->data.end(); ++c, ++i) {
int tri_a[3], tri_b[3];
face_split_tri_indices(face_flags[i], tri_a, tri_b);
for(b_mesh.loop_triangles.begin(t); t != b_mesh.loop_triangles.end(); ++t) {
int3 li = get_int3(t->loops());
float3 c1 = get_float3(l->data[li[0]].color());
float3 c2 = get_float3(l->data[li[1]].color());
float3 c3 = get_float3(l->data[li[2]].color());
/* Encode vertex color using the sRGB curve. */
uchar4 colors[4];
colors[0] = color_float_to_byte(color_srgb_to_linear_v3(get_float3(c->color1())));
colors[1] = color_float_to_byte(color_srgb_to_linear_v3(get_float3(c->color2())));
colors[2] = color_float_to_byte(color_srgb_to_linear_v3(get_float3(c->color3())));
if(nverts[i] == 4) {
colors[3] = color_float_to_byte(color_srgb_to_linear_v3(get_float3(c->color4())));
}
cdata[0] = colors[tri_a[0]];
cdata[1] = colors[tri_a[1]];
cdata[2] = colors[tri_a[2]];
if(nverts[i] == 4) {
cdata[3] = colors[tri_b[0]];
cdata[4] = colors[tri_b[1]];
cdata[5] = colors[tri_b[2]];
cdata += 6;
}
else
cdata += 3;
/* Compress/encode vertex color using the sRGB curve. */
cdata[0] = color_float_to_byte(color_srgb_to_linear_v3(c1));
cdata[1] = color_float_to_byte(color_srgb_to_linear_v3(c2));
cdata[2] = color_float_to_byte(color_srgb_to_linear_v3(c3));
cdata += 3;
}
}
}
@ -454,14 +397,12 @@ static void attr_create_vertex_color(Scene *scene,
/* Create uv map attributes. */
static void attr_create_uv_map(Scene *scene,
Mesh *mesh,
BL::Mesh& b_mesh,
const vector<int>& nverts,
const vector<int>& face_flags)
BL::Mesh& b_mesh)
{
if(b_mesh.tessface_uv_textures.length() != 0) {
BL::Mesh::tessface_uv_textures_iterator l;
if(b_mesh.uv_layers.length() != 0) {
BL::Mesh::uv_layers_iterator l;
for(b_mesh.tessface_uv_textures.begin(l); l != b_mesh.tessface_uv_textures.end(); ++l) {
for(b_mesh.uv_layers.begin(l); l != b_mesh.uv_layers.end(); ++l) {
const bool active_render = l->active_render();
AttributeStandard uv_std = (active_render)? ATTR_STD_UV: ATTR_STD_NONE;
ustring uv_name = ustring(l->name().c_str());
@ -493,33 +434,15 @@ static void attr_create_uv_map(Scene *scene,
ATTR_ELEMENT_CORNER);
}
BL::MeshTextureFaceLayer::data_iterator t;
BL::Mesh::loop_triangles_iterator t;
float3 *fdata = uv_attr->data_float3();
size_t i = 0;
for(l->data.begin(t); t != l->data.end(); ++t, ++i) {
int tri_a[3], tri_b[3];
face_split_tri_indices(face_flags[i], tri_a, tri_b);
float3 uvs[4];
uvs[0] = get_float3(t->uv1());
uvs[1] = get_float3(t->uv2());
uvs[2] = get_float3(t->uv3());
if(nverts[i] == 4) {
uvs[3] = get_float3(t->uv4());
}
fdata[0] = uvs[tri_a[0]];
fdata[1] = uvs[tri_a[1]];
fdata[2] = uvs[tri_a[2]];
for(b_mesh.loop_triangles.begin(t); t != b_mesh.loop_triangles.end(); ++t) {
int3 li = get_int3(t->loops());
fdata[0] = get_float3(l->data[li[0]].uv());
fdata[1] = get_float3(l->data[li[1]].uv());
fdata[2] = get_float3(l->data[li[2]].uv());
fdata += 3;
if(nverts[i] == 4) {
fdata[0] = uvs[tri_b[0]];
fdata[1] = uvs[tri_b[1]];
fdata[2] = uvs[tri_b[2]];
fdata += 3;
}
}
}
@ -822,7 +745,7 @@ static void create_mesh(Scene *scene,
{
/* count vertices and faces */
int numverts = b_mesh.vertices.length();
int numfaces = (!subdivision) ? b_mesh.tessfaces.length() : b_mesh.polygons.length();
int numfaces = (!subdivision) ? b_mesh.loop_triangles.length() : b_mesh.polygons.length();
int numtris = 0;
int numcorners = 0;
int numngons = 0;
@ -834,14 +757,10 @@ static void create_mesh(Scene *scene,
}
BL::Mesh::vertices_iterator v;
BL::Mesh::tessfaces_iterator f;
BL::Mesh::polygons_iterator p;
if(!subdivision) {
for(b_mesh.tessfaces.begin(f); f != b_mesh.tessfaces.end(); ++f) {
int4 vi = get_int4(f->vertices_raw());
numtris += (vi[3] == 0)? 1: 2;
}
numtris = numfaces;
}
else {
for(b_mesh.polygons.begin(p); p != b_mesh.polygons.end(); ++p) {
@ -869,7 +788,7 @@ static void create_mesh(Scene *scene,
/* create generated coordinates from undeformed coordinates */
const bool need_default_tangent =
(subdivision == false) &&
(b_mesh.tessface_uv_textures.length() == 0) &&
(b_mesh.uv_layers.length() == 0) &&
(mesh->need_attribute(scene, ATTR_STD_UV_TANGENT));
if(mesh->need_attribute(scene, ATTR_STD_GENERATED) ||
need_default_tangent)
@ -890,19 +809,21 @@ static void create_mesh(Scene *scene,
/* create faces */
vector<int> nverts(numfaces);
vector<int> face_flags(numfaces, FACE_FLAG_NONE);
int fi = 0;
if(!subdivision) {
for(b_mesh.tessfaces.begin(f); f != b_mesh.tessfaces.end(); ++f, ++fi) {
int4 vi = get_int4(f->vertices_raw());
int n = (vi[3] == 0)? 3: 4;
int shader = clamp(f->material_index(), 0, used_shaders.size()-1);
bool smooth = f->use_smooth() || use_loop_normals;
BL::Mesh::loop_triangles_iterator t;
int ti = 0;
for(b_mesh.loop_triangles.begin(t); t != b_mesh.loop_triangles.end(); ++t, ++ti) {
BL::MeshPolygon p = b_mesh.polygons[t->polygon_index()];
int3 vi = get_int3(t->vertices());
int shader = clamp(p.material_index(), 0, used_shaders.size()-1);
bool smooth = p.use_smooth() || use_loop_normals;
if(use_loop_normals) {
BL::Array<float, 12> loop_normals = f->split_normals();
for(int i = 0; i < n; i++) {
BL::Array<float, 9> loop_normals = t->split_normals();
for(int i = 0; i < 3; i++) {
N[vi[i]] = make_float3(loop_normals[i * 3],
loop_normals[i * 3 + 1],
loop_normals[i * 3 + 2]);
@ -913,25 +834,8 @@ static void create_mesh(Scene *scene,
*
* NOTE: Autosmooth is already taken care about.
*/
if(n == 4) {
if(is_zero(cross(mesh->verts[vi[1]] - mesh->verts[vi[0]], mesh->verts[vi[2]] - mesh->verts[vi[0]])) ||
is_zero(cross(mesh->verts[vi[2]] - mesh->verts[vi[0]], mesh->verts[vi[3]] - mesh->verts[vi[0]])))
{
mesh->add_triangle(vi[0], vi[1], vi[3], shader, smooth);
mesh->add_triangle(vi[2], vi[3], vi[1], shader, smooth);
face_flags[fi] |= FACE_FLAG_DIVIDE_24;
}
else {
mesh->add_triangle(vi[0], vi[1], vi[2], shader, smooth);
mesh->add_triangle(vi[0], vi[2], vi[3], shader, smooth);
face_flags[fi] |= FACE_FLAG_DIVIDE_13;
}
}
else {
mesh->add_triangle(vi[0], vi[1], vi[2], shader, smooth);
}
nverts[fi] = n;
mesh->add_triangle(vi[0], vi[1], vi[2], shader, smooth);
nverts[ti] = 3;
}
}
else {
@ -957,13 +861,13 @@ static void create_mesh(Scene *scene,
* The calculate functions will check whether they're needed or not.
*/
attr_create_pointiness(scene, mesh, b_mesh, subdivision);
attr_create_vertex_color(scene, mesh, b_mesh, nverts, face_flags, subdivision);
attr_create_vertex_color(scene, mesh, b_mesh, subdivision);
if(subdivision) {
attr_create_subd_uv_map(scene, mesh, b_mesh, subdivide_uvs);
}
else {
attr_create_uv_map(scene, mesh, b_mesh, nverts, face_flags);
attr_create_uv_map(scene, mesh, b_mesh);
}
/* for volume objects, create a matrix to transform from object space to

View File

@ -83,7 +83,7 @@ static inline BL::Mesh object_to_mesh(BL::BlendData& data,
}
}
if(subdivision_type == Mesh::SUBDIVISION_NONE) {
me.calc_tessface(true);
me.calc_loop_triangles();
}
}
return me;

View File

@ -20,13 +20,12 @@
__all__ = (
"mesh_linked_uv_islands",
"mesh_linked_tessfaces",
"mesh_linked_triangles",
"edge_face_count_dict",
"edge_face_count",
"edge_loops_from_tessfaces",
"edge_loops_from_edges",
"ngon_tessellate",
"face_random_points",
"triangle_random_points",
)
@ -90,41 +89,41 @@ def mesh_linked_uv_islands(mesh):
return poly_islands
def mesh_linked_tessfaces(mesh):
def mesh_linked_triangles(mesh):
"""
Splits the mesh into connected faces, use this for separating cubes from
Splits the mesh into connected triangles, use this for separating cubes from
other mesh elements within 1 mesh datablock.
:arg mesh: the mesh used to group with.
:type mesh: :class:`bpy.types.Mesh`
:return: lists of lists containing faces.
:return: lists of lists containing triangles.
:rtype: list
"""
# Build vert face connectivity
vert_faces = [[] for i in range(len(mesh.vertices))]
for f in mesh.tessfaces:
for v in f.vertices:
vert_faces[v].append(f)
vert_tris = [[] for i in range(len(mesh.vertices))]
for t in mesh.loop_triangles:
for v in t.vertices:
vert_tris[v].append(t)
# sort faces into connectivity groups
face_groups = [[f] for f in mesh.tessfaces]
# map old, new face location
face_mapping = list(range(len(mesh.tessfaces)))
# sort triangles into connectivity groups
tri_groups = [[t] for t in mesh.loop_triangles]
# map old, new tri location
tri_mapping = list(range(len(mesh.loop_triangles)))
# Now clump faces iteratively
# Now clump triangles iteratively
ok = True
while ok:
ok = False
for i, f in enumerate(mesh.tessfaces):
mapped_index = face_mapping[f.index]
mapped_group = face_groups[mapped_index]
for i, t in enumerate(mesh.loop_triangles):
mapped_index = tri_mapping[t.index]
mapped_group = tri_groups[mapped_index]
for v in f.vertices:
for nxt_f in vert_faces[v]:
if nxt_f != f:
nxt_mapped_index = face_mapping[nxt_f.index]
for v in t.vertices:
for nxt_t in vert_tris[v]:
if nxt_t != t:
nxt_mapped_index = tri_mapping[nxt_t.index]
# We are not a part of the same group
if mapped_index != nxt_mapped_index:
@ -132,18 +131,18 @@ def mesh_linked_tessfaces(mesh):
# Assign mapping to this group so they
# all map to this group
for grp_f in face_groups[nxt_mapped_index]:
face_mapping[grp_f.index] = mapped_index
for grp_t in tri_groups[nxt_mapped_index]:
tri_mapping[grp_t.index] = mapped_index
# Move faces into this group
mapped_group.extend(face_groups[nxt_mapped_index])
# Move triangles into this group
mapped_group.extend(tri_groups[nxt_mapped_index])
# remove reference to the list
face_groups[nxt_mapped_index] = None
tri_groups[nxt_mapped_index] = None
# return all face groups that are not null
# this is all the faces that are connected in their own lists.
return [fg for fg in face_groups if fg]
# return all tri groups that are not null
# this is all the triangles that are connected in their own lists.
return [tg for tg in tri_groups if tg]
def edge_face_count_dict(mesh):
@ -177,87 +176,6 @@ def edge_face_count(mesh):
return [get(edge_face_count, ed.key, 0) for ed in mesh.edges]
def edge_loops_from_tessfaces(mesh, tessfaces=None, seams=()):
"""
Edge loops defined by faces
Takes me.tessfaces or a list of faces and returns the edge loops
These edge loops are the edges that sit between quads, so they don't touch
1 quad, note: not connected will make 2 edge loops,
both only containing 2 edges.
return a list of edge key lists
[[(0, 1), (4, 8), (3, 8)], ...]
:arg mesh: the mesh used to get edge loops from.
:type mesh: :class:`bpy.types.Mesh`
:arg tessfaces: optional face list to only use some of the meshes faces.
:type tessfaces: :class:`bpy.types.MeshTessFace`, sequence or or NoneType
:return: return a list of edge vertex index lists.
:rtype: list
"""
OTHER_INDEX = 2, 3, 0, 1 # opposite face index
if tessfaces is None:
tessfaces = mesh.tessfaces
edges = {}
for f in tessfaces:
if len(f.vertices) == 4:
edge_keys = f.edge_keys
for i, edkey in enumerate(f.edge_keys):
edges.setdefault(edkey, []).append(edge_keys[OTHER_INDEX[i]])
for edkey in seams:
edges[edkey] = []
# Collect edge loops here
edge_loops = []
for edkey, ed_adj in edges.items():
if 0 < len(ed_adj) < 3: # 1 or 2
# Seek the first edge
context_loop = [edkey, ed_adj[0]]
edge_loops.append(context_loop)
if len(ed_adj) == 2:
other_dir = ed_adj[1]
else:
other_dir = None
del ed_adj[:]
flipped = False
while 1:
# from knowing the last 2, look for the next.
ed_adj = edges[context_loop[-1]]
if len(ed_adj) != 2:
# the original edge had 2 other edges
if other_dir and flipped is False:
flipped = True # only flip the list once
context_loop.reverse()
del ed_adj[:]
context_loop.append(other_dir) # save 1 look-up
ed_adj = edges[context_loop[-1]]
if len(ed_adj) != 2:
del ed_adj[:]
break
else:
del ed_adj[:]
break
i = ed_adj.index(context_loop[-2])
context_loop.append(ed_adj[not i])
# Don't look at this again
del ed_adj[:]
return edge_loops
def edge_loops_from_edges(mesh, edges=None):
"""
Edge loops defined by edges
@ -511,54 +429,42 @@ def ngon_tessellate(from_data, indices, fix_loops=True):
return fill
def face_random_points(num_points, tessfaces):
def triangle_random_points(num_points, loop_triangles):
"""
Generates a list of random points over mesh tessfaces.
Generates a list of random points over mesh loop triangles.
:arg num_points: the number of random points to generate on each face.
:arg num_points: the number of random points to generate on each triangle.
:type int:
:arg tessfaces: list of the faces to generate points on.
:type tessfaces: :class:`bpy.types.MeshTessFace`, sequence
:return: list of random points over all faces.
:arg loop_triangles: list of the triangles to generate points on.
:type loop_triangles: :class:`bpy.types.MeshLoopTriangle`, sequence
:return: list of random points over all triangles.
:rtype: list
"""
from random import random
from mathutils.geometry import area_tri
# Split all quads into 2 tris, tris remain unchanged
tri_faces = []
for f in tessfaces:
tris = []
verts = f.id_data.vertices
fv = f.vertices[:]
tris.append((verts[fv[0]].co,
verts[fv[1]].co,
verts[fv[2]].co,
))
if len(fv) == 4:
tris.append((verts[fv[0]].co,
verts[fv[3]].co,
verts[fv[2]].co,
))
tri_faces.append(tris)
# For each triangle, generate the required number of random points
sampled_points = [None] * (num_points * len(loop_triangles))
for i, lt in enumerate(loop_triangles):
# Get triangle vertex coordinates
verts = lt.id_data.vertices
ltv = lt.vertices[:]
tv = (verts[ltv[0]].co, verts[ltv[1]].co, verts[ltv[2]].co)
# For each face, generate the required number of random points
sampled_points = [None] * (num_points * len(tessfaces))
for i, tf in enumerate(tri_faces):
for k in range(num_points):
# If this is a quad, we need to weight its 2 tris by their area
if len(tf) != 1:
area1 = area_tri(*tf[0])
area2 = area_tri(*tf[1])
if len(tv) != 1:
area1 = area_tri(*tv[0])
area2 = area_tri(*tv[1])
area_tot = area1 + area2
area1 = area1 / area_tot
area2 = area2 / area_tot
vecs = tf[0 if (random() < area1) else 1]
vecs = tv[0 if (random() < area1) else 1]
else:
vecs = tf[0]
vecs = tv[0]
u1 = random()
u2 = random()

View File

@ -468,7 +468,7 @@ class MeshEdge(StructRNA):
return ord_ind(*tuple(self.vertices))
class MeshTessFace(StructRNA):
class MeshLoopTriangle(StructRNA):
__slots__ = ()
@property
@ -476,32 +476,18 @@ class MeshTessFace(StructRNA):
"""The midpoint of the face."""
face_verts = self.vertices[:]
mesh_verts = self.id_data.vertices
if len(face_verts) == 3:
return (mesh_verts[face_verts[0]].co +
mesh_verts[face_verts[1]].co +
mesh_verts[face_verts[2]].co
) / 3.0
else:
return (mesh_verts[face_verts[0]].co +
mesh_verts[face_verts[1]].co +
mesh_verts[face_verts[2]].co +
mesh_verts[face_verts[3]].co
) / 4.0
return (mesh_verts[face_verts[0]].co +
mesh_verts[face_verts[1]].co +
mesh_verts[face_verts[2]].co
) / 3.0
@property
def edge_keys(self):
verts = self.vertices[:]
if len(verts) == 3:
return (ord_ind(verts[0], verts[1]),
ord_ind(verts[1], verts[2]),
ord_ind(verts[2], verts[0]),
)
else:
return (ord_ind(verts[0], verts[1]),
ord_ind(verts[1], verts[2]),
ord_ind(verts[2], verts[3]),
ord_ind(verts[3], verts[0]),
)
return (ord_ind(verts[0], verts[1]),
ord_ind(verts[1], verts[2]),
ord_ind(verts[2], verts[0]),
)
class MeshPolygon(StructRNA):

View File

@ -179,7 +179,7 @@ void BKE_mesh_split_faces(struct Mesh *mesh, bool free_loop_normals);
struct Mesh *BKE_mesh_new_from_object(
struct Depsgraph *depsgraph, struct Main *bmain, struct Scene *sce, struct Object *ob,
const bool apply_modifiers, const bool calc_tessface, const bool calc_undeformed);
const bool apply_modifiers, const bool calc_loop_triangles, const bool calc_undeformed);
struct Mesh *BKE_mesh_create_derived_for_modifier(
struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob,
struct ModifierData *md, int build_shapekey_layers);

View File

@ -840,7 +840,7 @@ void BKE_mesh_to_curve(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *
/* settings: 1 - preview, 2 - render */
Mesh *BKE_mesh_new_from_object(
Depsgraph *depsgraph, Main *bmain, Scene *sce, Object *ob,
const bool apply_modifiers, const bool calc_tessface, const bool calc_undeformed)
const bool apply_modifiers, const bool calc_loop_triangles, const bool calc_undeformed)
{
Mesh *tmpmesh;
Curve *tmpcu = NULL, *copycu;
@ -1069,9 +1069,9 @@ Mesh *BKE_mesh_new_from_object(
break;
} /* end copy materials */
if (calc_tessface) {
if (calc_loop_triangles) {
/* cycles and exporters rely on this still */
BKE_mesh_tessface_ensure(tmpmesh);
BKE_mesh_runtime_looptri_ensure(tmpmesh);
}
return tmpmesh;

View File

@ -107,13 +107,13 @@ NodeGroup *BlenderFileLoader::Load()
bool apply_modifiers = false;
bool calc_undeformed = false;
bool calc_tessface = false;
bool calc_loop_triangles = false;
Mesh *mesh = BKE_mesh_new_from_object(depsgraph,
_re->main,
_re->scene,
ob,
apply_modifiers,
calc_tessface,
calc_loop_triangles,
calc_undeformed);
if (mesh) {

View File

@ -174,8 +174,6 @@ typedef struct MLoop {
*
* \note A #MLoopTri may be in the middle of an ngon and not reference **any** edges.
*/
#
#
typedef struct MLoopTri {
unsigned int tri[3];
unsigned int poly;

View File

@ -390,7 +390,7 @@ extern StructRNA RNA_MeshLoopColorLayer;
extern StructRNA RNA_MeshDeformModifier;
extern StructRNA RNA_MeshEdge;
extern StructRNA RNA_MeshPolygon;
extern StructRNA RNA_MeshTessFace;
extern StructRNA RNA_MeshLoopTriangle;
extern StructRNA RNA_MeshLoop;
extern StructRNA RNA_MeshFloatProperty;
extern StructRNA RNA_MeshFloatPropertyLayer;

View File

@ -490,7 +490,7 @@ int rna_parameter_size(struct PropertyRNA *parm);
struct Mesh *rna_Main_meshes_new_from_object(
struct Main *bmain, struct ReportList *reports, struct Depsgraph *depsgraph,
struct Object *ob, bool apply_modifiers, bool calc_tessface, bool calc_undeformed);
struct Object *ob, bool apply_modifiers, bool calc_loop_triangles, bool calc_undeformed);
/* XXX, these should not need to be defined here~! */
struct MTex *rna_mtex_texture_slots_add(struct ID *self, struct bContext *C, struct ReportList *reports);

View File

@ -310,7 +310,7 @@ static Mesh *rna_Main_meshes_new(Main *bmain, const char *name)
/* copied from Mesh_getFromObject and adapted to RNA interface */
Mesh *rna_Main_meshes_new_from_object(
Main *bmain, ReportList *reports, Depsgraph *depsgraph,
Object *ob, bool apply_modifiers, bool calc_tessface, bool calc_undeformed)
Object *ob, bool apply_modifiers, bool calc_loop_triangles, bool calc_undeformed)
{
Scene *sce = DEG_get_evaluated_scene(depsgraph);
@ -326,7 +326,7 @@ Mesh *rna_Main_meshes_new_from_object(
return NULL;
}
return BKE_mesh_new_from_object(depsgraph, bmain, sce, ob, apply_modifiers, calc_tessface, calc_undeformed);
return BKE_mesh_new_from_object(depsgraph, bmain, sce, ob, apply_modifiers, calc_loop_triangles, calc_undeformed);
}
static Lamp *rna_Main_lights_new(Main *bmain, const char *name, int type)
@ -893,7 +893,7 @@ void RNA_def_main_meshes(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_boolean(func, "apply_modifiers", 0, "", "Apply modifiers");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
RNA_def_boolean(func, "calc_tessface", true, "Calculate Tessellation", "Calculate tessellation faces");
RNA_def_boolean(func, "calc_loop_triangles", true, "Calculate Triangles", "Calculate tesselated triangles");
RNA_def_boolean(func, "calc_undeformed", false, "Calculate Undeformed", "Calculate undeformed vertex coordinates");
parm = RNA_def_pointer(func, "mesh", "Mesh", "",
"Mesh created from object, remove it if it is only used for export");

File diff suppressed because it is too large Load Diff

View File

@ -50,6 +50,7 @@
#include "BKE_mesh.h"
#include "BKE_mesh_tangent.h"
#include "BKE_mesh_mapping.h"
#include "BKE_mesh_runtime.h"
#include "ED_mesh.h"
static const char *rna_Mesh_unit_test_compare(struct Mesh *mesh, struct Mesh *mesh2)
@ -101,9 +102,9 @@ static void rna_Mesh_free_tangents(Mesh *mesh)
CustomData_free_layers(&mesh->ldata, CD_MLOOPTANGENT, mesh->totloop);
}
static void rna_Mesh_calc_tessface(Mesh *mesh, bool free_mpoly)
static void rna_Mesh_calc_looptri(Mesh *mesh)
{
ED_mesh_calc_tessface(mesh, free_mpoly != 0);
BKE_mesh_runtime_looptri_ensure(mesh);
}
static void rna_Mesh_calc_smooth_groups(Mesh *mesh, bool use_bitflags, int *r_poly_group_len,
@ -206,6 +207,7 @@ static void rna_Mesh_flip_normals(Mesh *mesh)
BKE_mesh_polygons_flip(mesh->mpoly, mesh->mloop, &mesh->ldata, mesh->totpoly);
BKE_mesh_tessface_clear(mesh);
BKE_mesh_calc_normals(mesh);
BKE_mesh_runtime_clear_geometry(mesh);
DEG_id_tag_update(&mesh->id, 0);
}
@ -269,12 +271,8 @@ void RNA_api_mesh(StructRNA *srna)
func = RNA_def_function(srna, "free_tangents", "rna_Mesh_free_tangents");
RNA_def_function_ui_description(func, "Free tangents");
func = RNA_def_function(srna, "calc_tessface", "rna_Mesh_calc_tessface");
RNA_def_function_ui_description(func, "Calculate face tessellation (supports editmode too)");
RNA_def_boolean(func, "free_mpoly", 0, "Free MPoly", "Free data used by polygons and loops. "
"WARNING: This destructive operation removes regular faces, "
"only used on temporary mesh data-blocks to reduce memory footprint of render "
"engines and export scripts");
func = RNA_def_function(srna, "calc_loop_triangles", "rna_Mesh_calc_looptri");
RNA_def_function_ui_description(func, "Calculate loop triangle tessellation (supports editmode too)");
func = RNA_def_function(srna, "calc_smooth_groups", "rna_Mesh_calc_smooth_groups");
RNA_def_function_ui_description(func, "Calculate smooth groups from sharp edges");
@ -308,7 +306,7 @@ void RNA_api_mesh(StructRNA *srna)
func = RNA_def_function(srna, "update", "ED_mesh_update");
RNA_def_boolean(func, "calc_edges", 0, "Calculate Edges", "Force recalculation of edges");
RNA_def_boolean(func, "calc_tessface", 0, "Calculate Tessellation", "Force recalculation of tessellation faces");
RNA_def_boolean(func, "calc_loop_triangles", 0, "Calculate Triangules", "Force recalculation of triangle tessellation");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
RNA_def_function(srna, "update_gpu_tag", "rna_Mesh_update_gpu_tag");

View File

@ -224,11 +224,11 @@ static void rna_Object_camera_fit_coords(
/* settings: 0 - preview, 1 - render */
static Mesh *rna_Object_to_mesh(
Object *ob, bContext *C, ReportList *reports, Depsgraph *depsgraph,
bool apply_modifiers, bool calc_tessface, bool calc_undeformed)
bool apply_modifiers, bool calc_loop_triangles, bool calc_undeformed)
{
Main *bmain = CTX_data_main(C);
return rna_Main_meshes_new_from_object(bmain, reports, depsgraph, ob, apply_modifiers, calc_tessface, calc_undeformed);
return rna_Main_meshes_new_from_object(bmain, reports, depsgraph, ob, apply_modifiers, calc_loop_triangles, calc_undeformed);
}
static PointerRNA rna_Object_shape_key_add(Object *ob, bContext *C, ReportList *reports,
@ -594,7 +594,7 @@ void RNA_api_object(StructRNA *srna)
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED);
parm = RNA_def_boolean(func, "apply_modifiers", 0, "", "Apply modifiers");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
RNA_def_boolean(func, "calc_tessface", true, "Calculate Tessellation", "Calculate tessellation faces");
RNA_def_boolean(func, "calc_loop_triangles", true, "Calculate Loop Triangles", "Calculate triangle tessellation");
RNA_def_boolean(func, "calc_undeformed", false, "Calculate Undeformed", "Calculate undeformed vertex coordinates");
parm = RNA_def_pointer(func, "mesh", "Mesh", "",
"Mesh created from object, remove it if it is only used for export");

View File

@ -113,30 +113,30 @@ static PyObject *bpy_bm_from_edit_mesh(PyObject *UNUSED(self), PyObject *value)
}
PyDoc_STRVAR(bpy_bm_update_edit_mesh_doc,
".. method:: update_edit_mesh(mesh, tessface=True, destructive=True)\n"
".. method:: update_edit_mesh(mesh, loop_triangles=True, destructive=True)\n"
"\n"
" Update the mesh after changes to the BMesh in editmode, \n"
" optionally recalculating n-gon tessellation.\n"
"\n"
" :arg mesh: The editmode mesh.\n"
" :type mesh: :class:`bpy.types.Mesh`\n"
" :arg tessface: Option to recalculate n-gon tessellation.\n"
" :type tessface: boolean\n"
" :arg loop_triangles: Option to recalculate n-gon tessellation.\n"
" :type loop_triangles: boolean\n"
" :arg destructive: Use when geometry has been added or removed.\n"
" :type destructive: boolean\n"
);
static PyObject *bpy_bm_update_edit_mesh(PyObject *UNUSED(self), PyObject *args, PyObject *kw)
{
static const char *kwlist[] = {"mesh", "tessface", "destructive", NULL};
static const char *kwlist[] = {"mesh", "loop_triangles", "destructive", NULL};
PyObject *py_me;
Mesh *me;
bool do_tessface = true;
bool do_loop_triangles = true;
bool is_destructive = true;
if (!PyArg_ParseTupleAndKeywords(
args, kw, "O|O&O&:update_edit_mesh", (char **)kwlist,
&py_me,
PyC_ParseBool, &do_tessface,
PyC_ParseBool, &do_loop_triangles,
PyC_ParseBool, &is_destructive))
{
return NULL;
@ -157,7 +157,7 @@ static PyObject *bpy_bm_update_edit_mesh(PyObject *UNUSED(self), PyObject *args,
{
extern void EDBM_update_generic(BMEditMesh *em, const bool do_tessface, const bool is_destructive);
EDBM_update_generic(me->edit_btmesh, do_tessface, is_destructive);
EDBM_update_generic(me->edit_btmesh, do_loop_triangles, is_destructive);
}
Py_RETURN_NONE;

View File

@ -1246,15 +1246,15 @@ static PyObject *bpy_bmesh_calc_volume(BPy_BMElem *self, PyObject *args, PyObjec
}
}
PyDoc_STRVAR(bpy_bmesh_calc_tessface_doc,
".. method:: calc_tessface()\n"
PyDoc_STRVAR(bpy_bmesh_calc_loop_triangles_doc,
".. method:: calc_loop_triangles()\n"
"\n"
" Calculate triangle tessellation from quads/ngons.\n"
"\n"
" :return: The triangulated faces.\n"
" :rtype: list of :class:`BMLoop` tuples\n"
);
static PyObject *bpy_bmesh_calc_tessface(BPy_BMElem *self)
static PyObject *bpy_bmesh_calc_loop_triangles(BPy_BMElem *self)
{
BMesh *bm;
@ -2733,7 +2733,7 @@ static struct PyMethodDef bpy_bmesh_methods[] = {
/* calculations */
{"calc_volume", (PyCFunction)bpy_bmesh_calc_volume, METH_VARARGS | METH_KEYWORDS, bpy_bmesh_calc_volume_doc},
{"calc_tessface", (PyCFunction)bpy_bmesh_calc_tessface, METH_NOARGS, bpy_bmesh_calc_tessface_doc},
{"calc_loop_triangles", (PyCFunction)bpy_bmesh_calc_loop_triangles, METH_NOARGS, bpy_bmesh_calc_loop_triangles_doc},
{NULL, NULL, 0, NULL}
};

View File

@ -86,9 +86,6 @@
#include "render_types.h"
#include "zbuf.h"
/* Remove when Cycles moves from MFace to MLoopTri */
#define USE_MFACE_WORKAROUND
typedef struct BakeDataZSpan {
BakePixel *pixel_array;
int primitive_id;
@ -393,27 +390,6 @@ static bool cast_ray_highpoly(
return hit_mesh != -1;
}
#ifdef USE_MFACE_WORKAROUND
/**
* Until cycles moves to #MLoopTri, we need to keep face-rotation in sync with #test_index_face
*
* We only need to consider quads since #BKE_mesh_recalc_tessellation doesn't execute this on triangles.
*/
static void test_index_face_looptri(const MPoly *mp, MLoop *mloop, MLoopTri *lt)
{
if (mp->totloop == 4) {
if (UNLIKELY((mloop[mp->loopstart + 2].v == 0) ||
(mloop[mp->loopstart + 3].v == 0)))
{
/* remap: (2, 3, 0, 1) */
unsigned int l = mp->loopstart;
ARRAY_SET_ITEMS(lt[0].tri, l + 2, l + 3, l + 0);
ARRAY_SET_ITEMS(lt[1].tri, l + 2, l + 0, l + 1);
}
}
}
#endif
/**
* This function populates an array of verts for the triangles of a mesh
* Tangent and Normals are also stored
@ -433,10 +409,6 @@ static TriTessFace *mesh_calc_tri_tessface(
unsigned int mpoly_prev = UINT_MAX;
float no[3];
#ifdef USE_MFACE_WORKAROUND
unsigned int mpoly_prev_testindex = UINT_MAX;
#endif
mvert = CustomData_get_layer(&me->vdata, CD_MVERT);
looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__);
triangles = MEM_mallocN(sizeof(TriTessFace) * tottri, __func__);
@ -463,13 +435,6 @@ static TriTessFace *mesh_calc_tri_tessface(
const MLoopTri *lt = &looptri[i];
const MPoly *mp = &me->mpoly[lt->poly];
#ifdef USE_MFACE_WORKAROUND
if (lt->poly != mpoly_prev_testindex) {
test_index_face_looptri(mp, me->mloop, &looptri[i]);
mpoly_prev_testindex = lt->poly;
}
#endif
triangles[i].mverts[0] = &mvert[me->mloop[lt->tri[0]].v];
triangles[i].mverts[1] = &mvert[me->mloop[lt->tri[1]].v];
triangles[i].mverts[2] = &mvert[me->mloop[lt->tri[2]].v];
@ -662,9 +627,6 @@ void RE_bake_pixels_populate(
const MLoopUV *mloopuv;
const int tottri = poly_to_tri_count(me->totpoly, me->totloop);
MLoopTri *looptri;
#ifdef USE_MFACE_WORKAROUND
unsigned int mpoly_prev_testindex = UINT_MAX;
#endif
if ((uv_layer == NULL) || (uv_layer[0] == '\0')) {
mloopuv = CustomData_get_layer(&me->ldata, CD_MLOOPUV);
@ -714,13 +676,6 @@ void RE_bake_pixels_populate(
bd.bk_image = &bake_images->data[image_id];
bd.primitive_id = ++p_id;
#ifdef USE_MFACE_WORKAROUND
if (lt->poly != mpoly_prev_testindex) {
test_index_face_looptri(mp, me->mloop, &looptri[i]);
mpoly_prev_testindex = lt->poly;
}
#endif
for (a = 0; a < 3; a++) {
const float *uv = mloopuv[lt->tri[a]].uv;