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