diff options
author | Iyad Ahmed <iyadahmed430@gmail.com> | 2022-06-06 20:57:38 +0300 |
---|---|---|
committer | Aras Pranckevicius <aras@nesnausk.org> | 2022-06-06 20:57:38 +0300 |
commit | 7c511f1b47d857f37aa36ee6ed8107cb88eb5c39 (patch) | |
tree | d376a5e21bc21f40e0b062f9623887f4018ed61a /source/blender/io/stl | |
parent | 14fc89f38f0e3ce00e4fd6fffd72eea5d998af5a (diff) |
STL: Add new C++ based STL importer
A new experimentatl STL importer, written in C++. Roughly 7-9x faster than the
Python based one.
Reviewed By: Aras Pranckevicius, Hans Goudey.
Differential Revision: https://developer.blender.org/D14941
Diffstat (limited to 'source/blender/io/stl')
-rw-r--r-- | source/blender/io/stl/CMakeLists.txt | 44 | ||||
-rw-r--r-- | source/blender/io/stl/IO_stl.cc | 16 | ||||
-rw-r--r-- | source/blender/io/stl/IO_stl.h | 35 | ||||
-rw-r--r-- | source/blender/io/stl/importer/stl_import.cc | 114 | ||||
-rw-r--r-- | source/blender/io/stl/importer/stl_import.hh | 22 | ||||
-rw-r--r-- | source/blender/io/stl/importer/stl_import_ascii_reader.cc | 159 | ||||
-rw-r--r-- | source/blender/io/stl/importer/stl_import_ascii_reader.hh | 32 | ||||
-rw-r--r-- | source/blender/io/stl/importer/stl_import_binary_reader.cc | 58 | ||||
-rw-r--r-- | source/blender/io/stl/importer/stl_import_binary_reader.hh | 31 | ||||
-rw-r--r-- | source/blender/io/stl/importer/stl_import_mesh.cc | 114 | ||||
-rw-r--r-- | source/blender/io/stl/importer/stl_import_mesh.hh | 71 |
11 files changed, 696 insertions, 0 deletions
diff --git a/source/blender/io/stl/CMakeLists.txt b/source/blender/io/stl/CMakeLists.txt new file mode 100644 index 00000000000..e0c48bbbf7e --- /dev/null +++ b/source/blender/io/stl/CMakeLists.txt @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +set(INC + . + ./importer + ../common + ../../blenkernel + ../../blenlib + ../../bmesh + ../../bmesh/intern + ../../depsgraph + ../../editors/include + ../../makesdna + ../../makesrna + ../../nodes + ../../windowmanager + ../../../../extern/fast_float + ../../../../intern/guardedalloc +) + +set(INC_SYS + +) + +set(SRC + IO_stl.cc + importer/stl_import_mesh.cc + importer/stl_import_ascii_reader.cc + importer/stl_import_binary_reader.cc + importer/stl_import.cc + + IO_stl.h + importer/stl_import_mesh.hh + importer/stl_import_ascii_reader.hh + importer/stl_import_binary_reader.hh + importer/stl_import.hh +) + +set(LIB + bf_blenkernel + bf_io_common +) + +blender_add_lib(bf_stl "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/io/stl/IO_stl.cc b/source/blender/io/stl/IO_stl.cc new file mode 100644 index 00000000000..b26c1533692 --- /dev/null +++ b/source/blender/io/stl/IO_stl.cc @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup stl + */ + +#include "BLI_timeit.hh" + +#include "IO_stl.h" +#include "stl_import.hh" + +void STL_import(bContext *C, const struct STLImportParams *import_params) +{ + SCOPED_TIMER("STL Import"); + blender::io::stl::importer_main(C, *import_params); +} diff --git a/source/blender/io/stl/IO_stl.h b/source/blender/io/stl/IO_stl.h new file mode 100644 index 00000000000..bbe537948e8 --- /dev/null +++ b/source/blender/io/stl/IO_stl.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup stl + */ + +#pragma once + +#include "BKE_context.h" +#include "BLI_path_util.h" +#include "IO_orientation.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct STLImportParams { + /** Full path to the source STL file to import. */ + char filepath[FILE_MAX]; + eIOAxis forward_axis; + eIOAxis up_axis; + bool use_facet_normal; + bool use_scene_unit; + float global_scale; + bool use_mesh_validate; +}; + +/** + * C-interface for the importer. + */ +void STL_import(bContext *C, const struct STLImportParams *import_params); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/io/stl/importer/stl_import.cc b/source/blender/io/stl/importer/stl_import.cc new file mode 100644 index 00000000000..f358598a216 --- /dev/null +++ b/source/blender/io/stl/importer/stl_import.cc @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup stl + */ + +#include <cstdio> + +#include "BKE_customdata.h" +#include "BKE_layer.h" +#include "BKE_mesh.h" +#include "BKE_object.h" + +#include "DNA_collection_types.h" +#include "DNA_scene_types.h" + +#include "BLI_fileops.hh" +#include "BLI_math_vector.h" +#include "BLI_memory_utils.hh" + +#include "DNA_object_types.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +#include "stl_import.hh" +#include "stl_import_ascii_reader.hh" +#include "stl_import_binary_reader.hh" + +namespace blender::io::stl { + +void importer_main(bContext *C, const STLImportParams &import_params) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + importer_main(bmain, scene, view_layer, import_params); +} + +void importer_main(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + const STLImportParams &import_params) +{ + FILE *file = BLI_fopen(import_params.filepath, "rb"); + if (!file) { + fprintf(stderr, "Failed to open STL file:'%s'.\n", import_params.filepath); + return; + } + BLI_SCOPED_DEFER([&]() { fclose(file); }); + + /* Detect STL file type by comparing file size with expected file size, + * could check if file starts with "solid", but some files do not adhere, + * this is the same as the old Python importer. + */ + uint32_t num_tri = 0; + size_t file_size = BLI_file_size(import_params.filepath); + fseek(file, BINARY_HEADER_SIZE, SEEK_SET); + fread(&num_tri, sizeof(uint32_t), 1, file); + bool is_ascii_stl = (file_size != (BINARY_HEADER_SIZE + 4 + BINARY_STRIDE * num_tri)); + + /* Name used for both mesh and object. */ + char ob_name[FILE_MAX]; + BLI_strncpy(ob_name, BLI_path_basename(import_params.filepath), FILE_MAX); + BLI_path_extension_replace(ob_name, FILE_MAX, ""); + + Mesh *mesh; + if (is_ascii_stl) { + mesh = read_stl_ascii(import_params.filepath, bmain, ob_name, import_params.use_facet_normal); + } + else { + mesh = read_stl_binary(file, bmain, ob_name, import_params.use_facet_normal); + } + + if (import_params.use_mesh_validate) { + bool verbose_validate = false; +#ifdef DEBUG + verbose_validate = true; +#endif + BKE_mesh_validate(mesh, verbose_validate, false); + } + + BKE_view_layer_base_deselect_all(view_layer); + LayerCollection *lc = BKE_layer_collection_get_active(view_layer); + Object *obj = BKE_object_add_only_object(bmain, OB_MESH, ob_name); + BKE_mesh_assign_object(bmain, obj, mesh); + BKE_collection_object_add(bmain, lc->collection, obj); + Base *base = BKE_view_layer_base_find(view_layer, obj); + BKE_view_layer_base_select_and_set_active(view_layer, base); + + float global_scale = import_params.global_scale; + if ((scene->unit.system != USER_UNIT_NONE) && import_params.use_scene_unit) { + global_scale *= scene->unit.scale_length; + } + float scale_vec[3] = {global_scale, global_scale, global_scale}; + float obmat3x3[3][3]; + unit_m3(obmat3x3); + float obmat4x4[4][4]; + unit_m4(obmat4x4); + /* +Y-forward and +Z-up are the Blender's default axis settings. */ + mat3_from_axis_conversion( + IO_AXIS_Y, IO_AXIS_Z, import_params.forward_axis, import_params.up_axis, obmat3x3); + copy_m4_m3(obmat4x4, obmat3x3); + rescale_m4(obmat4x4, scale_vec); + BKE_object_apply_mat4(obj, obmat4x4, true, false); + + DEG_id_tag_update(&lc->collection->id, ID_RECALC_COPY_ON_WRITE); + int flags = ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION | + ID_RECALC_BASE_FLAGS; + DEG_id_tag_update_ex(bmain, &obj->id, flags); + DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS); + DEG_relations_tag_update(bmain); +} +} // namespace blender::io::stl diff --git a/source/blender/io/stl/importer/stl_import.hh b/source/blender/io/stl/importer/stl_import.hh new file mode 100644 index 00000000000..377544c26af --- /dev/null +++ b/source/blender/io/stl/importer/stl_import.hh @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup stl + */ + +#pragma once + +#include "IO_stl.h" + +namespace blender::io::stl { + +/* Main import function used from within Blender. */ +void importer_main(bContext *C, const STLImportParams &import_params); + +/* Used from tests, where full bContext does not exist. */ +void importer_main(Main *bmain, + Scene *scene, + ViewLayer *view_layer, + const STLImportParams &import_params); + +} // namespace blender::io::stl diff --git a/source/blender/io/stl/importer/stl_import_ascii_reader.cc b/source/blender/io/stl/importer/stl_import_ascii_reader.cc new file mode 100644 index 00000000000..2f2495fa4ca --- /dev/null +++ b/source/blender/io/stl/importer/stl_import_ascii_reader.cc @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup stl + */ + +#include <cstdint> +#include <cstdio> + +#include "BKE_mesh.h" + +#include "BLI_fileops.hh" +#include "BLI_memory_utils.hh" +#include "BLI_string_ref.hh" + +#include "DNA_mesh_types.h" + +/* NOTE: we could use C++17 <charconv> from_chars to parse + * floats, but even if some compilers claim full support, + * their standard libraries are not quite there yet. + * LLVM/libc++ only has a float parser since LLVM 14, + * and gcc/libstdc++ since 11.1. So until at least these are + * the minimum spec, use an external library. */ +#include "fast_float.h" + +#include "stl_import.hh" +#include "stl_import_mesh.hh" + +namespace blender::io::stl { + +class StringBuffer { + private: + char *start; + const char *end; + + public: + StringBuffer(char *buf, size_t len) + { + start = buf; + end = start + len; + } + + bool is_empty() const + { + return start == end; + } + + void drop_leading_control_chars() + { + while ((start < end) && (*start) <= ' ') { + start++; + } + } + + void drop_leading_non_control_chars() + { + while ((start < end) && (*start) > ' ') { + start++; + } + } + + void drop_line() + { + while (start < end && *start != '\n') { + start++; + } + } + + bool parse_token(const char *token, size_t token_length) + { + drop_leading_control_chars(); + if (end - start < token_length + 1) { + return false; + } + if (memcmp(start, token, token_length) != 0) { + return false; + } + if (start[token_length] > ' ') { + return false; + } + start += token_length + 1; + return true; + } + + void drop_token() + { + drop_leading_non_control_chars(); + drop_leading_control_chars(); + } + + void parse_float(float &out) + { + drop_leading_control_chars(); + /* Skip '+' */ + if (start < end && *start == '+') { + start++; + } + fast_float::from_chars_result res = fast_float::from_chars(start, end, out); + if (res.ec == std::errc::invalid_argument || res.ec == std::errc::result_out_of_range) { + out = 0.0f; + } + start = const_cast<char *>(res.ptr); + } +}; + +static inline void parse_float3(StringBuffer &buf, float out[3]) +{ + for (int i = 0; i < 3; i++) { + buf.parse_float(out[i]); + } +} + +Mesh *read_stl_ascii(const char *filepath, Main *bmain, char *mesh_name, bool use_custom_normals) +{ + size_t buffer_len; + void *buffer = BLI_file_read_text_as_mem(filepath, 0, &buffer_len); + if (buffer == nullptr) { + fprintf(stderr, "STL Importer: cannot read from ASCII STL file: '%s'\n", filepath); + return BKE_mesh_add(bmain, mesh_name); + } + BLI_SCOPED_DEFER([&]() { MEM_freeN(buffer); }); + + int num_reserved_tris = 1024; + + StringBuffer str_buf(static_cast<char *>(buffer), buffer_len); + STLMeshHelper stl_mesh(num_reserved_tris, use_custom_normals); + float triangle_buf[3][3]; + float custom_normal_buf[3]; + str_buf.drop_line(); /* Skip header line */ + while (!str_buf.is_empty()) { + if (str_buf.parse_token("vertex", 6)) { + parse_float3(str_buf, triangle_buf[0]); + if (str_buf.parse_token("vertex", 6)) { + parse_float3(str_buf, triangle_buf[1]); + } + if (str_buf.parse_token("vertex", 6)) { + parse_float3(str_buf, triangle_buf[2]); + } + if (use_custom_normals) { + stl_mesh.add_triangle( + triangle_buf[0], triangle_buf[1], triangle_buf[2], custom_normal_buf); + } + else { + stl_mesh.add_triangle(triangle_buf[0], triangle_buf[1], triangle_buf[2]); + } + } + else if (str_buf.parse_token("facet", 5)) { + str_buf.drop_token(); /* Expecting "normal" */ + parse_float3(str_buf, custom_normal_buf); + } + else { + str_buf.drop_token(); + } + } + + return stl_mesh.to_mesh(bmain, mesh_name); +} + +} // namespace blender::io::stl diff --git a/source/blender/io/stl/importer/stl_import_ascii_reader.hh b/source/blender/io/stl/importer/stl_import_ascii_reader.hh new file mode 100644 index 00000000000..b0216d98496 --- /dev/null +++ b/source/blender/io/stl/importer/stl_import_ascii_reader.hh @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup stl + */ + +#pragma once + +#include <cstdio> + +#include "BKE_mesh.h" + +#include "stl_import.hh" + +/* ASCII STL spec.: + * solid name + * facet normal ni nj nk + * outer loop + * vertex v1x v1y v1z + * vertex v2x v2y v2z + * vertex v3x v3y v3z + * endloop + * endfacet + * ... + * endsolid name + */ + +namespace blender::io::stl { + +Mesh *read_stl_ascii(const char *filepath, Main *bmain, char *mesh_name, bool use_custom_normals); + +} // namespace blender::io::stl diff --git a/source/blender/io/stl/importer/stl_import_binary_reader.cc b/source/blender/io/stl/importer/stl_import_binary_reader.cc new file mode 100644 index 00000000000..c05b6ad1426 --- /dev/null +++ b/source/blender/io/stl/importer/stl_import_binary_reader.cc @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup stl + */ + +#include <cstdint> +#include <cstdio> + +#include "BKE_main.h" +#include "BKE_mesh.h" + +#include "BLI_array.hh" +#include "BLI_memory_utils.hh" + +#include "DNA_mesh_types.h" + +#include "stl_import_binary_reader.hh" +#include "stl_import_mesh.hh" + +namespace blender::io::stl { + +#pragma pack(push, 1) +struct STLBinaryTriangle { + float normal[3]; + float v1[3], v2[3], v3[3]; + uint16_t attribute_byte_count; +}; +#pragma pack(pop) + +Mesh *read_stl_binary(FILE *file, Main *bmain, char *mesh_name, bool use_custom_normals) +{ + const int chunk_size = 1024; + uint32_t num_tris = 0; + fseek(file, BINARY_HEADER_SIZE, SEEK_SET); + fread(&num_tris, sizeof(uint32_t), 1, file); + if (num_tris == 0) { + return BKE_mesh_add(bmain, mesh_name); + } + + Array<STLBinaryTriangle> tris_buf(chunk_size); + STLMeshHelper stl_mesh(num_tris, use_custom_normals); + size_t num_read_tris; + while (num_read_tris = fread(tris_buf.data(), sizeof(STLBinaryTriangle), chunk_size, file)) { + for (size_t i = 0; i < num_read_tris; i++) { + if (use_custom_normals) { + stl_mesh.add_triangle(tris_buf[i].v1, tris_buf[i].v2, tris_buf[i].v3, tris_buf[i].normal); + } + else { + stl_mesh.add_triangle(tris_buf[i].v1, tris_buf[i].v2, tris_buf[i].v3); + } + } + } + + return stl_mesh.to_mesh(bmain, mesh_name); +} + +} // namespace blender::io::stl diff --git a/source/blender/io/stl/importer/stl_import_binary_reader.hh b/source/blender/io/stl/importer/stl_import_binary_reader.hh new file mode 100644 index 00000000000..71d5dbbbe58 --- /dev/null +++ b/source/blender/io/stl/importer/stl_import_binary_reader.hh @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup stl + */ + +#pragma once + +#include <cstdio> + +#include "BKE_mesh.h" + +/* Binary STL spec.: + * UINT8[80] – Header - 80 bytes + * UINT32 – Number of triangles - 4 bytes + * For each triangle - 50 bytes: + * REAL32[3] – Normal vector - 12 bytes + * REAL32[3] – Vertex 1 - 12 bytes + * REAL32[3] – Vertex 2 - 12 bytes + * REAL32[3] – Vertex 3 - 12 bytes + * UINT16 – Attribute byte count - 2 bytes + */ + +namespace blender::io::stl { + +const size_t BINARY_HEADER_SIZE = 80; +const size_t BINARY_STRIDE = 12 * 4 + 2; + +Mesh *read_stl_binary(FILE *file, Main *bmain, char *mesh_name, bool use_custom_normals); + +} // namespace blender::io::stl diff --git a/source/blender/io/stl/importer/stl_import_mesh.cc b/source/blender/io/stl/importer/stl_import_mesh.cc new file mode 100644 index 00000000000..5f5c2624414 --- /dev/null +++ b/source/blender/io/stl/importer/stl_import_mesh.cc @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup stl + */ + +#include "BKE_customdata.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" +#include "BKE_mesh.h" + +#include "BLI_array.hh" +#include "BLI_math_vector.h" +#include "BLI_math_vector.hh" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "stl_import_mesh.hh" + +namespace blender::io::stl { + +STLMeshHelper::STLMeshHelper(int num_tris, bool use_custom_normals) + : m_use_custom_normals(use_custom_normals) +{ + m_num_degenerate_tris = 0; + m_num_duplicate_tris = 0; + m_tris.reserve(num_tris); + /* Upper bound (all vertices are unique). */ + m_verts.reserve(num_tris * 3); + if (use_custom_normals) { + m_loop_normals.reserve(num_tris * 3); + } +} + +bool STLMeshHelper::add_triangle(const float3 &a, const float3 &b, const float3 &c) +{ + int v1_id = m_verts.index_of_or_add(a); + int v2_id = m_verts.index_of_or_add(b); + int v3_id = m_verts.index_of_or_add(c); + if ((v1_id == v2_id) || (v1_id == v3_id) || (v2_id == v3_id)) { + m_num_degenerate_tris++; + return false; + } + if (!m_tris.add({v1_id, v2_id, v3_id})) { + m_num_duplicate_tris++; + return false; + } + return true; +} + +void STLMeshHelper::add_triangle(const float3 &a, + const float3 &b, + const float3 &c, + const float3 &custom_normal) +{ + if (add_triangle(a, b, c)) { + m_loop_normals.append_n_times(custom_normal, 3); + } +} + +Mesh *STLMeshHelper::to_mesh(Main *bmain, char *mesh_name) +{ + if (m_num_degenerate_tris > 0) { + std::cout << "STL Importer: " << m_num_degenerate_tris << "degenerate triangles were removed" + << std::endl; + } + if (m_num_duplicate_tris > 0) { + std::cout << "STL Importer: " << m_num_duplicate_tris << "duplicate triangles were removed" + << std::endl; + } + + Mesh *mesh = BKE_mesh_add(bmain, mesh_name); + /* User count is already 1 here, but will be set later in #BKE_mesh_assign_object. */ + id_us_min(&mesh->id); + + mesh->totvert = m_verts.size(); + mesh->mvert = static_cast<MVert *>( + CustomData_add_layer(&mesh->vdata, CD_MVERT, CD_CALLOC, nullptr, mesh->totvert)); + for (int i = 0; i < mesh->totvert; i++) { + copy_v3_v3(mesh->mvert[i].co, m_verts[i]); + } + + mesh->totpoly = m_tris.size(); + mesh->totloop = m_tris.size() * 3; + mesh->mpoly = static_cast<MPoly *>( + CustomData_add_layer(&mesh->pdata, CD_MPOLY, CD_CALLOC, nullptr, mesh->totpoly)); + mesh->mloop = static_cast<MLoop *>( + CustomData_add_layer(&mesh->ldata, CD_MLOOP, CD_CALLOC, nullptr, mesh->totloop)); + + threading::parallel_for(m_tris.index_range(), 2048, [&](IndexRange tris_range) { + for (const int i : tris_range) { + mesh->mpoly[i].loopstart = 3 * i; + mesh->mpoly[i].totloop = 3; + + mesh->mloop[3 * i].v = m_tris[i].v1; + mesh->mloop[3 * i + 1].v = m_tris[i].v2; + mesh->mloop[3 * i + 2].v = m_tris[i].v3; + } + }); + + /* NOTE: edges must be calculated first before setting custom normals. */ + BKE_mesh_calc_edges(mesh, false, false); + + if (m_use_custom_normals && m_loop_normals.size() == mesh->totloop) { + BKE_mesh_set_custom_normals(mesh, reinterpret_cast<float(*)[3]>(m_loop_normals.data())); + mesh->flag |= ME_AUTOSMOOTH; + } + + return mesh; +} + +} // namespace blender::io::stl diff --git a/source/blender/io/stl/importer/stl_import_mesh.hh b/source/blender/io/stl/importer/stl_import_mesh.hh new file mode 100644 index 00000000000..7827d2a408c --- /dev/null +++ b/source/blender/io/stl/importer/stl_import_mesh.hh @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup stl + */ + +#pragma once + +#include <cstdint> + +#include "BLI_math_vec_types.hh" +#include "BLI_set.hh" +#include "BLI_vector.hh" +#include "BLI_vector_set.hh" + +#include "DNA_mesh_types.h" + +namespace blender::io::stl { +class Triangle { + public: + int v1, v2, v3; + /* Based on an old version of Python's frozenset hash + * https://web.archive.org/web/20220520211017/https://stackoverflow.com/questions/20832279/python-frozenset-hashing-algorithm-implementation + */ + uint64_t hash() const + { + uint64_t res = 1927868237UL; + res *= 4; + res ^= (v1 ^ (v1 << 16) ^ 89869747UL) * 3644798167UL; + res ^= (v2 ^ (v2 << 16) ^ 89869747UL) * 3644798167UL; + res ^= (v3 ^ (v3 << 16) ^ 89869747UL) * 3644798167UL; + return res * 69069U + 907133923UL; + } + friend bool operator==(const Triangle &a, const Triangle &b) + { + bool i = (a.v1 == b.v1) && (a.v2 == b.v2) && (a.v3 == b.v3); + bool j = (a.v1 == b.v1) && (a.v3 == b.v2) && (a.v2 == b.v3); + bool k = (a.v2 == b.v1) && (a.v1 == b.v2) && (a.v3 == b.v3); + + bool l = (a.v2 == b.v1) && (a.v3 == b.v2) && (a.v1 == b.v3); + bool m = (a.v3 == b.v1) && (a.v1 == b.v2) && (a.v2 == b.v3); + bool n = (a.v3 == b.v1) && (a.v2 == b.v2) && (a.v1 == b.v3); + + return i || j || k || l || m || n; + } +}; + +class STLMeshHelper { + private: + VectorSet<float3> m_verts; + VectorSet<Triangle> m_tris; + Vector<float3> m_loop_normals; + int m_num_degenerate_tris; + int m_num_duplicate_tris; + const bool m_use_custom_normals; + + public: + STLMeshHelper(int num_tris, bool use_custom_normals); + + /* Creates a new triangle from specified vertex locations, + * duplicate vertices and triangles are merged. + */ + bool add_triangle(const float3 &a, const float3 &b, const float3 &c); + void add_triangle(const float3 &a, + const float3 &b, + const float3 &c, + const float3 &custom_normal); + Mesh *to_mesh(Main *bmain, char *mesh_name); +}; + +} // namespace blender::io::stl |