Blender V4.3
ply_export_load_plydata.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10#include "IO_ply.hh"
11#include "ply_data.hh"
12
13#include "BKE_attribute.hh"
14#include "BKE_lib_id.hh"
15#include "BKE_mesh.hh"
16#include "BKE_mesh_wrapper.hh"
17#include "BKE_object.hh"
18#include "BLI_color.hh"
19#include "BLI_hash.hh"
20#include "BLI_math_matrix.h"
22#include "BLI_math_rotation.h"
23#include "BLI_math_vector.h"
24#include "BLI_vector.hh"
25
27
29#include "DNA_layer_types.h"
30
31#include "bmesh.hh"
33
34namespace blender::io::ply {
35
36static Mesh *do_triangulation(const Mesh *mesh, bool force_triangulation)
37{
38 const BMeshCreateParams bm_create_params = {false};
39 BMeshFromMeshParams bm_convert_params{};
40 bm_convert_params.calc_face_normal = true;
41 bm_convert_params.calc_vert_normal = true;
42 const int triangulation_threshold = force_triangulation ? 4 : 255;
43
44 BMesh *bmesh = BKE_mesh_to_bmesh_ex(mesh, &bm_create_params, &bm_convert_params);
45 BM_mesh_triangulate(bmesh, 0, 3, triangulation_threshold, false, nullptr, nullptr, nullptr);
46 Mesh *temp_mesh = BKE_mesh_from_bmesh_for_eval_nomain(bmesh, nullptr, mesh);
47 BM_mesh_free(bmesh);
48 return temp_mesh;
49}
50
51static void set_world_axes_transform(const Object &object,
52 const eIOAxis forward,
53 const eIOAxis up,
54 float r_world_and_axes_transform[4][4],
55 float r_world_and_axes_normal_transform[3][3])
56{
57 float axes_transform[3][3];
58 unit_m3(axes_transform);
59 /* +Y-forward and +Z-up are the default Blender axis settings. */
60 mat3_from_axis_conversion(forward, up, IO_AXIS_Y, IO_AXIS_Z, axes_transform);
61 mul_m4_m3m4(r_world_and_axes_transform, axes_transform, object.object_to_world().ptr());
62 /* mul_m4_m3m4 does not transform last row of obmat, i.e. location data. */
63 mul_v3_m3v3(r_world_and_axes_transform[3], axes_transform, object.object_to_world().location());
64 r_world_and_axes_transform[3][3] = object.object_to_world()[3][3];
65
66 /* Normals need inverse transpose of the regular matrix to handle non-uniform scale. */
67 float normal_matrix[3][3];
68 copy_m3_m4(normal_matrix, r_world_and_axes_transform);
69 invert_m3_m3(r_world_and_axes_normal_transform, normal_matrix);
70 transpose_m3(r_world_and_axes_normal_transform);
71}
72
76
77 bool operator==(const uv_vertex_key &r) const
78 {
79 return (uv == r.uv && vertex_index == r.vertex_index);
80 }
81
82 uint64_t hash() const
83 {
84 return get_default_hash(uv.x, uv.y, vertex_index);
85 }
86};
87
88static void generate_vertex_map(const Mesh *mesh,
89 const PLYExportParams &export_params,
90 Vector<int> &r_ply_to_vertex,
91 Vector<int> &r_vertex_to_ply,
92 Vector<int> &r_loop_to_ply,
93 Vector<float2> &r_uvs)
94{
95 bool export_uv = false;
96 VArraySpan<float2> uv_map;
97 if (export_params.export_uv) {
98 const StringRef uv_name = CustomData_get_active_layer_name(&mesh->corner_data, CD_PROP_FLOAT2);
99 if (!uv_name.is_empty()) {
100 const bke::AttributeAccessor attributes = mesh->attributes();
101 uv_map = *attributes.lookup<float2>(uv_name, bke::AttrDomain::Corner);
102 export_uv = !uv_map.is_empty();
103 }
104 }
105
106 const Span<int> corner_verts = mesh->corner_verts();
107 r_vertex_to_ply.resize(mesh->verts_num, -1);
108 r_loop_to_ply.resize(mesh->corners_num, -1);
109
110 /* If we do not export or have UVs, then mapping of vertex indices is simple. */
111 if (!export_uv) {
112 r_ply_to_vertex.resize(mesh->verts_num);
113 for (int index = 0; index < mesh->verts_num; index++) {
114 r_vertex_to_ply[index] = index;
115 r_ply_to_vertex[index] = index;
116 }
117 for (int index = 0; index < mesh->corners_num; index++) {
118 r_loop_to_ply[index] = corner_verts[index];
119 }
120 return;
121 }
122
123 /* We are exporting UVs. Need to build mappings of what
124 * any unique (vertex, UV) values will map into the PLY data. */
125 Map<uv_vertex_key, int> vertex_map;
126 vertex_map.reserve(mesh->verts_num);
127 r_ply_to_vertex.reserve(mesh->verts_num);
128 r_uvs.reserve(mesh->verts_num);
129
130 for (int loop_index = 0; loop_index < int(corner_verts.size()); loop_index++) {
131 int vertex_index = corner_verts[loop_index];
132 uv_vertex_key key{uv_map[loop_index], vertex_index};
133 int ply_index = vertex_map.lookup_or_add(key, int(vertex_map.size()));
134 r_vertex_to_ply[vertex_index] = ply_index;
135 r_loop_to_ply[loop_index] = ply_index;
136 while (r_uvs.size() <= ply_index) {
137 r_uvs.append(key.uv);
138 r_ply_to_vertex.append(key.vertex_index);
139 }
140 }
141
142 /* Add zero UVs for any loose vertices. */
143 for (int vertex_index = 0; vertex_index < mesh->verts_num; vertex_index++) {
144 if (r_vertex_to_ply[vertex_index] != -1) {
145 continue;
146 }
147 int ply_index = int(r_uvs.size());
148 r_vertex_to_ply[vertex_index] = ply_index;
149 r_uvs.append({0, 0});
150 r_ply_to_vertex.append(vertex_index);
151 }
152}
153
154static float *find_or_add_attribute(const StringRef name,
155 int64_t size,
156 uint32_t vertex_offset,
157 Vector<PlyCustomAttribute> &r_attributes)
158{
159 /* Do we have this attribute from some other object already? */
160 for (PlyCustomAttribute &attr : r_attributes) {
161 if (attr.name == name) {
162 BLI_assert(attr.data.size() == vertex_offset);
163 attr.data.resize(attr.data.size() + size, 0.0f);
164 return attr.data.data() + vertex_offset;
165 }
166 }
167 /* We don't have it yet, create and fill with zero data for previous objects. */
168 r_attributes.append(PlyCustomAttribute(name, vertex_offset + size));
169 return r_attributes.last().data.data() + vertex_offset;
170}
171
172static void load_custom_attributes(const Mesh *mesh,
173 const Span<int> ply_to_vertex,
174 uint32_t vertex_offset,
175 Vector<PlyCustomAttribute> &r_attributes)
176{
177 const bke::AttributeAccessor attributes = mesh->attributes();
178 const StringRef color_name = mesh->active_color_attribute;
179 const StringRef uv_name = CustomData_get_active_layer_name(&mesh->corner_data, CD_PROP_FLOAT2);
180 const int64_t size = ply_to_vertex.size();
181
182 attributes.foreach_attribute([&](const bke::AttributeIter &iter) {
183 /* Skip internal, standard and non-vertex domain attributes. */
184 if (iter.domain != bke::AttrDomain::Point || iter.name[0] == '.' ||
186 ELEM(iter.name, "position", color_name, uv_name))
187 {
188 return;
189 }
190
191 const GVArraySpan attribute = *iter.get();
192 if (attribute.is_empty()) {
193 return;
194 }
195 switch (iter.data_type) {
196 case CD_PROP_FLOAT: {
197 float *attr = find_or_add_attribute(iter.name, size, vertex_offset, r_attributes);
198 auto typed = attribute.typed<float>();
199 for (const int64_t i : ply_to_vertex.index_range()) {
200 attr[i] = typed[ply_to_vertex[i]];
201 }
202 break;
203 }
204 case CD_PROP_INT8: {
205 float *attr = find_or_add_attribute(iter.name, size, vertex_offset, r_attributes);
206 auto typed = attribute.typed<int8_t>();
207 for (const int64_t i : ply_to_vertex.index_range()) {
208 attr[i] = typed[ply_to_vertex[i]];
209 }
210 break;
211 }
212 case CD_PROP_INT32: {
213 float *attr = find_or_add_attribute(iter.name, size, vertex_offset, r_attributes);
214 auto typed = attribute.typed<int32_t>();
215 for (const int64_t i : ply_to_vertex.index_range()) {
216 attr[i] = typed[ply_to_vertex[i]];
217 }
218 break;
219 }
220 case CD_PROP_INT32_2D: {
221 float *attr_x = find_or_add_attribute(iter.name + "_x", size, vertex_offset, r_attributes);
222 float *attr_y = find_or_add_attribute(iter.name + "_y", size, vertex_offset, r_attributes);
223 auto typed = attribute.typed<int2>();
224 for (const int64_t i : ply_to_vertex.index_range()) {
225 int j = ply_to_vertex[i];
226 attr_x[i] = typed[j].x;
227 attr_y[i] = typed[j].y;
228 }
229 break;
230 }
231 case CD_PROP_FLOAT2: {
232 float *attr_x = find_or_add_attribute(iter.name + "_x", size, vertex_offset, r_attributes);
233 float *attr_y = find_or_add_attribute(iter.name + "_y", size, vertex_offset, r_attributes);
234 auto typed = attribute.typed<float2>();
235 for (const int64_t i : ply_to_vertex.index_range()) {
236 int j = ply_to_vertex[i];
237 attr_x[i] = typed[j].x;
238 attr_y[i] = typed[j].y;
239 }
240 break;
241 }
242 case CD_PROP_FLOAT3: {
243 float *attr_x = find_or_add_attribute(iter.name + "_x", size, vertex_offset, r_attributes);
244 float *attr_y = find_or_add_attribute(iter.name + "_y", size, vertex_offset, r_attributes);
245 float *attr_z = find_or_add_attribute(iter.name + "_z", size, vertex_offset, r_attributes);
246 auto typed = attribute.typed<float3>();
247 for (const int64_t i : ply_to_vertex.index_range()) {
248 int j = ply_to_vertex[i];
249 attr_x[i] = typed[j].x;
250 attr_y[i] = typed[j].y;
251 attr_z[i] = typed[j].z;
252 }
253 break;
254 }
255 case CD_PROP_BYTE_COLOR: {
256 float *attr_r = find_or_add_attribute(iter.name + "_r", size, vertex_offset, r_attributes);
257 float *attr_g = find_or_add_attribute(iter.name + "_g", size, vertex_offset, r_attributes);
258 float *attr_b = find_or_add_attribute(iter.name + "_b", size, vertex_offset, r_attributes);
259 float *attr_a = find_or_add_attribute(iter.name + "_a", size, vertex_offset, r_attributes);
260 auto typed = attribute.typed<ColorGeometry4b>();
261 for (const int64_t i : ply_to_vertex.index_range()) {
262 ColorGeometry4f col = typed[ply_to_vertex[i]].decode();
263 attr_r[i] = col.r;
264 attr_g[i] = col.g;
265 attr_b[i] = col.b;
266 attr_a[i] = col.a;
267 }
268 break;
269 }
270 case CD_PROP_COLOR: {
271 float *attr_r = find_or_add_attribute(iter.name + "_r", size, vertex_offset, r_attributes);
272 float *attr_g = find_or_add_attribute(iter.name + "_g", size, vertex_offset, r_attributes);
273 float *attr_b = find_or_add_attribute(iter.name + "_b", size, vertex_offset, r_attributes);
274 float *attr_a = find_or_add_attribute(iter.name + "_a", size, vertex_offset, r_attributes);
275 auto typed = attribute.typed<ColorGeometry4f>();
276 for (const int64_t i : ply_to_vertex.index_range()) {
277 ColorGeometry4f col = typed[ply_to_vertex[i]];
278 attr_r[i] = col.r;
279 attr_g[i] = col.g;
280 attr_b[i] = col.b;
281 attr_a[i] = col.a;
282 }
283 break;
284 }
285 case CD_PROP_BOOL: {
286 float *attr = find_or_add_attribute(iter.name, size, vertex_offset, r_attributes);
287 auto typed = attribute.typed<bool>();
288 for (const int64_t i : ply_to_vertex.index_range()) {
289 attr[i] = typed[ply_to_vertex[i]] ? 1.0f : 0.0f;
290 }
291 break;
292 }
293 case CD_PROP_QUATERNION: {
294 float *attr_x = find_or_add_attribute(iter.name + "_x", size, vertex_offset, r_attributes);
295 float *attr_y = find_or_add_attribute(iter.name + "_y", size, vertex_offset, r_attributes);
296 float *attr_z = find_or_add_attribute(iter.name + "_z", size, vertex_offset, r_attributes);
297 float *attr_w = find_or_add_attribute(iter.name + "_w", size, vertex_offset, r_attributes);
298 auto typed = attribute.typed<math::Quaternion>();
299 for (const int64_t i : ply_to_vertex.index_range()) {
300 int j = ply_to_vertex[i];
301 attr_x[i] = typed[j].x;
302 attr_y[i] = typed[j].y;
303 attr_z[i] = typed[j].z;
304 attr_w[i] = typed[j].w;
305 }
306 break;
307 }
308 default:
309 BLI_assert_msg(0, "Unsupported attribute type for PLY export.");
310 }
311 });
312}
313
314void load_plydata(PlyData &plyData, Depsgraph *depsgraph, const PLYExportParams &export_params)
315{
316 DEGObjectIterSettings deg_iter_settings{};
317 deg_iter_settings.depsgraph = depsgraph;
318 deg_iter_settings.flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY |
321
322 /* When exporting multiple objects, vertex indices have to be offset. */
323 uint32_t vertex_offset = 0;
324
325 DEG_OBJECT_ITER_BEGIN (&deg_iter_settings, object) {
326 if (object->type != OB_MESH) {
327 continue;
328 }
329
330 if (export_params.export_selected_objects && !(object->base_flag & BASE_SELECTED)) {
331 continue;
332 }
333
334 Object *obj_eval = DEG_get_evaluated_object(depsgraph, object);
335 Mesh *mesh = export_params.apply_modifiers ? BKE_object_get_evaluated_mesh(obj_eval) :
337
338 /* Ensure data exists if currently in edit mode. */
340
341 bool force_triangulation = false;
342 OffsetIndices faces = mesh->faces();
343 for (const int i : faces.index_range()) {
344 if (faces[i].size() > 255) {
345 force_triangulation = true;
346 break;
347 }
348 }
349
350 /* Triangulate */
351 bool manually_free_mesh = false;
352 if (export_params.export_triangulated_mesh || force_triangulation) {
353 mesh = do_triangulation(mesh, export_params.export_triangulated_mesh);
354 faces = mesh->faces();
355 manually_free_mesh = true;
356 }
357
358 Vector<int> ply_to_vertex, vertex_to_ply, loop_to_ply;
359 Vector<float2> uvs;
360 generate_vertex_map(mesh, export_params, ply_to_vertex, vertex_to_ply, loop_to_ply, uvs);
361
362 float world_and_axes_transform[4][4];
363 float world_and_axes_normal_transform[3][3];
364 set_world_axes_transform(*obj_eval,
365 export_params.forward_axis,
366 export_params.up_axis,
367 world_and_axes_transform,
368 world_and_axes_normal_transform);
369
370 /* Face data. */
371 plyData.face_vertices.reserve(plyData.face_vertices.size() + mesh->corners_num);
372 for (const int corner : IndexRange(mesh->corners_num)) {
373 int ply_index = loop_to_ply[corner];
374 BLI_assert(ply_index >= 0 && ply_index < ply_to_vertex.size());
375 plyData.face_vertices.append_unchecked(ply_index + vertex_offset);
376 }
377
378 plyData.face_sizes.reserve(plyData.face_sizes.size() + mesh->faces_num);
379 for (const int i : faces.index_range()) {
380 const IndexRange face = faces[i];
381 plyData.face_sizes.append_unchecked(face.size());
382 }
383
384 /* Vertices */
385 plyData.vertices.reserve(plyData.vertices.size() + ply_to_vertex.size());
386 Span<float3> vert_positions = mesh->vert_positions();
387 for (int vertex_index : ply_to_vertex) {
388 float3 pos = vert_positions[vertex_index];
389 mul_m4_v3(world_and_axes_transform, pos);
390 mul_v3_fl(pos, export_params.global_scale);
391 plyData.vertices.append_unchecked(pos);
392 }
393
394 /* UV's */
395 if (uvs.is_empty()) {
396 uvs.append_n_times(float2(0), ply_to_vertex.size());
397 }
398 else {
399 BLI_assert(uvs.size() == ply_to_vertex.size());
400 plyData.uv_coordinates.extend(uvs);
401 }
402
403 /* Normals */
404 if (export_params.export_normals) {
405 plyData.vertex_normals.reserve(plyData.vertex_normals.size() + ply_to_vertex.size());
406 const Span<float3> vert_normals = mesh->vert_normals();
407 for (int vertex_index : ply_to_vertex) {
408 float3 normal = vert_normals[vertex_index];
409 mul_m3_v3(world_and_axes_normal_transform, normal);
410 normalize_v3(normal);
411 plyData.vertex_normals.append(normal);
412 }
413 }
414
415 /* Colors */
416 if (export_params.vertex_colors != PLY_VERTEX_COLOR_NONE) {
417 const StringRef name = mesh->active_color_attribute;
418 if (!name.is_empty()) {
419 const bke::AttributeAccessor attributes = mesh->attributes();
420 const VArray color_attribute = *attributes.lookup_or_default<ColorGeometry4f>(
421 name, bke::AttrDomain::Point, {0.0f, 0.0f, 0.0f, 0.0f});
422 if (!color_attribute.is_empty()) {
423 if (plyData.vertex_colors.size() != vertex_offset) {
424 plyData.vertex_colors.resize(vertex_offset, float4(0));
425 }
426
427 plyData.vertex_colors.reserve(vertex_offset + ply_to_vertex.size());
428 for (int vertex_index : ply_to_vertex) {
429 float4 color = float4(color_attribute[vertex_index]);
430 if (export_params.vertex_colors == PLY_VERTEX_COLOR_SRGB) {
431 linearrgb_to_srgb_v4(color, color);
432 }
433 plyData.vertex_colors.append(color);
434 }
435 }
436 }
437 }
438
439 /* Custom attributes */
440 if (export_params.export_attributes) {
441 load_custom_attributes(mesh, ply_to_vertex, vertex_offset, plyData.vertex_custom_attr);
442 }
443
444 /* Loose edges */
445 const bke::LooseEdgeCache &loose_edges = mesh->loose_edges();
446 if (loose_edges.count > 0) {
447 Span<int2> edges = mesh->edges();
448 for (int i = 0; i < edges.size(); ++i) {
449 if (loose_edges.is_loose_bits[i]) {
450 plyData.edges.append({vertex_to_ply[edges[i][0]], vertex_to_ply[edges[i][1]]});
451 }
452 }
453 }
454
455 vertex_offset = int(plyData.vertices.size());
456 if (manually_free_mesh) {
457 BKE_id_free(nullptr, mesh);
458 }
459 }
460
462
463 /* Make sure color and attribute arrays are encompassing all input objects */
464 if (!plyData.vertex_colors.is_empty()) {
465 BLI_assert(plyData.vertex_colors.size() <= vertex_offset);
466 plyData.vertex_colors.resize(vertex_offset, float4(0));
467 }
468 for (PlyCustomAttribute &attr : plyData.vertex_custom_attr) {
469 BLI_assert(attr.data.size() <= vertex_offset);
470 attr.data.resize(vertex_offset, 0.0f);
471 }
472}
473
474} // namespace blender::io::ply
const char * CustomData_get_active_layer_name(const CustomData *data, eCustomDataType type)
void BKE_id_free(Main *bmain, void *idv)
Mesh * BKE_mesh_from_bmesh_for_eval_nomain(BMesh *bm, const CustomData_MeshMasks *cd_mask_extra, const Mesh *me_settings)
BMesh * BKE_mesh_to_bmesh_ex(const Mesh *mesh, const BMeshCreateParams *create_params, const BMeshFromMeshParams *convert_params)
void BKE_mesh_wrapper_ensure_mdata(Mesh *mesh)
General operations, lookup, etc. for blender objects.
Mesh * BKE_object_get_evaluated_mesh(const Object *object_eval)
Mesh * BKE_object_get_pre_modified_mesh(const Object *object)
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
MINLINE void linearrgb_to_srgb_v4(float srgb[4], const float linear[4])
void mul_m3_v3(const float M[3][3], float r[3])
void unit_m3(float m[3][3])
void copy_m3_m4(float m1[3][3], const float m2[4][4])
bool invert_m3_m3(float inverse[3][3], const float mat[3][3])
void mul_m4_v3(const float M[4][4], float r[3])
void mul_v3_m3v3(float r[3], const float M[3][3], const float a[3])
void transpose_m3(float R[3][3])
void mul_m4_m3m4(float R[4][4], const float A[3][3], const float B[4][4])
bool mat3_from_axis_conversion(int src_forward, int src_up, int dst_forward, int dst_up, float r_mat[3][3])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE float normalize_v3(float n[3])
#define ELEM(...)
#define DEG_OBJECT_ITER_BEGIN(settings_, instance_)
#define DEG_OBJECT_ITER_END
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY
@ DEG_ITER_OBJECT_FLAG_VISIBLE
@ DEG_ITER_OBJECT_FLAG_DUPLI
@ DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET
@ CD_PROP_BYTE_COLOR
@ CD_PROP_FLOAT
@ CD_PROP_FLOAT3
@ CD_PROP_INT32_2D
@ CD_PROP_COLOR
@ CD_PROP_QUATERNION
@ CD_PROP_INT32
@ CD_PROP_FLOAT2
@ OB_MESH
#define BASE_SELECTED(v3d, base)
eIOAxis
@ IO_AXIS_Y
@ IO_AXIS_Z
@ PLY_VERTEX_COLOR_NONE
Definition IO_ply.hh:22
@ PLY_VERTEX_COLOR_SRGB
Definition IO_ply.hh:23
void BM_mesh_free(BMesh *bm)
BMesh Free Mesh.
void BM_mesh_triangulate(BMesh *bm, const int quad_method, const int ngon_method, const int min_vertices, const bool tag_only, BMOperator *op, BMOpSlot *slot_facemap_out, BMOpSlot *slot_facemap_double_out)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
ChannelStorageType r
Definition BLI_color.hh:88
int64_t size() const
Definition BLI_map.hh:927
void reserve(int64_t n)
Definition BLI_map.hh:979
Value & lookup_or_add(const Key &key, const Value &value)
Definition BLI_map.hh:551
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
constexpr bool is_empty() const
int64_t size() const
void append(const T &value)
const T & last(const int64_t n=0) const
bool is_empty() const
void resize(const int64_t new_size)
void append_unchecked(const T &value)
void reserve(const int64_t min_capacity)
void append_n_times(const T &value, const int64_t n)
GAttributeReader get() const
const Depsgraph * depsgraph
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
uint col
bool attribute_name_is_anonymous(const StringRef name)
static void load_custom_attributes(const Mesh *mesh, const Span< int > ply_to_vertex, uint32_t vertex_offset, Vector< PlyCustomAttribute > &r_attributes)
void load_plydata(PlyData &plyData, Depsgraph *depsgraph, const PLYExportParams &export_params)
static void generate_vertex_map(const Mesh *mesh, const PLYExportParams &export_params, Vector< int > &r_ply_to_vertex, Vector< int > &r_vertex_to_ply, Vector< int > &r_loop_to_ply, Vector< float2 > &r_uvs)
static void set_world_axes_transform(const Object &object, const eIOAxis forward, const eIOAxis up, float r_world_and_axes_transform[4][4], float r_world_and_axes_normal_transform[3][3])
static Mesh * do_triangulation(const Mesh *mesh, bool force_triangulation)
static float * find_or_add_attribute(const StringRef name, int64_t size, uint32_t vertex_offset, Vector< PlyCustomAttribute > &r_attributes)
VecBase< float, 4 > float4
VecBase< float, 2 > float2
uint64_t get_default_hash(const T &v)
Definition BLI_hash.hh:219
unsigned int uint32_t
Definition stdint.h:80
__int64 int64_t
Definition stdint.h:89
signed int int32_t
Definition stdint.h:77
unsigned __int64 uint64_t
Definition stdint.h:90
signed char int8_t
Definition stdint.h:75
eIOAxis forward_axis
Definition IO_ply.hh:40
bool apply_modifiers
Definition IO_ply.hh:46
bool export_attributes
Definition IO_ply.hh:50
eIOAxis up_axis
Definition IO_ply.hh:41
ePLYVertexColorMode vertex_colors
Definition IO_ply.hh:49
float global_scale
Definition IO_ply.hh:42
bool export_triangulated_mesh
Definition IO_ply.hh:51
bool export_selected_objects
Definition IO_ply.hh:45
bool export_normals
Definition IO_ply.hh:48
blender::BitVector is_loose_bits
Vector< float3 > vertices
Definition ply_data.hh:29
Vector< uint32_t > face_vertices
Definition ply_data.hh:34
Vector< float4 > vertex_colors
Definition ply_data.hh:31
Vector< float3 > vertex_normals
Definition ply_data.hh:30
Vector< std::pair< int, int > > edges
Definition ply_data.hh:33
Vector< PlyCustomAttribute > vertex_custom_attr
Definition ply_data.hh:32
Vector< float2 > uv_coordinates
Definition ply_data.hh:36
Vector< uint32_t > face_sizes
Definition ply_data.hh:35
bool operator==(const uv_vertex_key &r) const
PointerRNA * ptr
Definition wm_files.cc:4126