Blender V5.0
stl_import.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
9#include <cstdio>
10
11#include "BKE_context.hh"
12#include "BKE_layer.hh"
13#include "BKE_mesh.hh"
14#include "BKE_object.hh"
15#include "BKE_report.hh"
16
18#include "DNA_layer_types.h"
19#include "DNA_scene_types.h"
20
21#include "BLI_fileops.hh"
22#include "BLI_math_matrix.h"
23#include "BLI_math_rotation.h"
24#include "BLI_memory_utils.hh"
25#include "BLI_string.h"
26
27#include "DNA_object_types.h"
28
29#include "DEG_depsgraph.hh"
31
32#include "stl_data.hh"
33#include "stl_import.hh"
36
37#include "CLG_log.h"
38static CLG_LogRef LOG = {"io.stl"};
39
40namespace blender::io::stl {
41
43{
44 CLOG_ERROR(&LOG, "STL Importer: failed to read file");
45 if (feof(file)) {
46 CLOG_ERROR(&LOG, "End of file reached");
47 }
48 else if (ferror(file)) {
49 perror("Error");
50 }
51}
52
53Mesh *read_stl_file(const STLImportParams &import_params)
54{
55 FILE *file = BLI_fopen(import_params.filepath, "rb");
56 if (!file) {
57 CLOG_ERROR(&LOG, "Failed to open STL file:'%s'.", import_params.filepath);
58 BKE_reportf(import_params.reports,
60 "STL Import: Cannot open file '%s'",
61 import_params.filepath);
62 return nullptr;
63 }
64 BLI_SCOPED_DEFER([&]() { fclose(file); });
65
66 /* Detect STL file type by comparing file size with expected file size,
67 * could check if file starts with "solid", but some files do not adhere,
68 * this is the same as the old Python importer.
69 */
70 uint32_t num_tri = 0;
71 size_t file_size = BLI_file_size(import_params.filepath);
72 fseek(file, BINARY_HEADER_SIZE, SEEK_SET);
73 if (fread(&num_tri, sizeof(uint32_t), 1, file) != 1) {
75 BKE_reportf(import_params.reports,
77 "STL Import: Failed to read file '%s'",
78 import_params.filepath);
79 return nullptr;
80 }
81 bool is_ascii_stl = (file_size != (BINARY_HEADER_SIZE + 4 + BINARY_STRIDE * num_tri));
82
83 Mesh *mesh = is_ascii_stl ?
84 read_stl_ascii(import_params.filepath, import_params.use_facet_normal) :
85 read_stl_binary(file, import_params.use_facet_normal);
86
87 if (mesh == nullptr) {
88 CLOG_ERROR(&LOG, "STL Importer: Failed to import mesh '%s'", import_params.filepath);
89 BKE_reportf(import_params.reports,
91 "STL Import: Failed to import mesh from file '%s'",
92 import_params.filepath);
93 return nullptr;
94 }
95
96 if (import_params.use_mesh_validate) {
97 bool verbose_validate = false;
98#ifndef NDEBUG
99 verbose_validate = true;
100#endif
101 BKE_mesh_validate(mesh, verbose_validate, false);
102 }
103
104 return mesh;
105}
106
107void importer_main(const bContext *C, const STLImportParams &import_params)
108{
109 Main *bmain = CTX_data_main(C);
110 Scene *scene = CTX_data_scene(C);
111 ViewLayer *view_layer = CTX_data_view_layer(C);
112 importer_main(bmain, scene, view_layer, import_params);
113}
114
115void importer_main(Main *bmain,
116 Scene *scene,
117 ViewLayer *view_layer,
118 const STLImportParams &import_params)
119{
120 /* Name used for both mesh and object. */
121 char ob_name[FILE_MAX];
122 STRNCPY(ob_name, BLI_path_basename(import_params.filepath));
124
125 Mesh *mesh = read_stl_file(import_params);
126 if (!mesh) {
127 return;
128 }
129
130 Mesh *mesh_in_main = BKE_mesh_add(bmain, ob_name);
131 BKE_mesh_nomain_to_mesh(mesh, mesh_in_main, nullptr);
132 BKE_view_layer_base_deselect_all(scene, view_layer);
134 Object *obj = BKE_object_add_only_object(bmain, OB_MESH, ob_name);
135 obj->data = mesh_in_main;
137 BKE_view_layer_synced_ensure(scene, view_layer);
138 Base *base = BKE_view_layer_base_find(view_layer, obj);
140
141 float global_scale = import_params.global_scale;
142 if ((scene->unit.system != USER_UNIT_NONE) && import_params.use_scene_unit) {
143 global_scale /= scene->unit.scale_length;
144 }
145 float scale_vec[3] = {global_scale, global_scale, global_scale};
146 float obmat3x3[3][3];
147 unit_m3(obmat3x3);
148 float obmat4x4[4][4];
149 unit_m4(obmat4x4);
150 /* +Y-forward and +Z-up are the Blender's default axis settings. */
152 IO_AXIS_Y, IO_AXIS_Z, import_params.forward_axis, import_params.up_axis, obmat3x3);
153 copy_m4_m3(obmat4x4, obmat3x3);
154 rescale_m4(obmat4x4, scale_vec);
155 BKE_object_apply_mat4(obj, obmat4x4, true, false);
156
160 DEG_id_tag_update_ex(bmain, &obj->id, flags);
163}
164} // namespace blender::io::stl
bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
LayerCollection * BKE_layer_collection_get_active(ViewLayer *view_layer)
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
void BKE_view_layer_base_deselect_all(const Scene *scene, ViewLayer *view_layer)
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
void BKE_view_layer_base_select_and_set_active(ViewLayer *view_layer, Base *selbase)
bool BKE_mesh_validate(Mesh *mesh, bool do_verbose, bool cddata_check_mask)
Mesh * BKE_mesh_add(Main *bmain, const char *name)
void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob, bool process_shape_keys=true)
General operations, lookup, etc. for blender objects.
void BKE_object_apply_mat4(Object *ob, const float mat[4][4], bool use_compat, bool use_parent)
Object * BKE_object_add_only_object(Main *bmain, int type, const char *name) ATTR_RETURNS_NONNULL
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_ERROR
Definition BKE_report.hh:39
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
size_t BLI_file_size(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:226
File and directory operations.
void unit_m3(float m[3][3])
void copy_m4_m3(float m1[4][4], const float m2[3][3])
void rescale_m4(float mat[4][4], const float scale[3])
void unit_m4(float m[4][4])
bool mat3_from_axis_conversion(int src_forward, int src_up, int dst_forward, int dst_up, float r_mat[3][3])
#define BLI_SCOPED_DEFER(function_to_defer)
bool bool BLI_path_extension_strip(char *path) ATTR_NONNULL(1)
void void void const char * BLI_path_basename(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define FILE_MAX
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:693
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1054
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ ID_RECALC_ANIMATION
Definition DNA_ID.h:1077
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ ID_RECALC_BASE_FLAGS
Definition DNA_ID.h:1104
Object groups, one object can be in many groups at once.
Object is a sort of wrapper for general info.
@ OB_MESH
@ USER_UNIT_NONE
@ IO_AXIS_Y
@ IO_AXIS_Z
#define C
Definition RandGen.cpp:29
DEG_id_tag_update_ex(cb_data->bmain, cb_data->owner_id, ID_RECALC_TAG_FOR_UNDO|ID_RECALC_SYNC_TO_EVAL)
#define LOG(level)
Definition log.h:97
Mesh * read_stl_binary(FILE *file, const bool use_custom_normals)
constexpr size_t BINARY_STRIDE
Definition stl_data.hh:22
void importer_main(const bContext *C, const STLImportParams &import_params)
constexpr size_t BINARY_HEADER_SIZE
Definition stl_data.hh:21
void stl_import_report_error(FILE *file)
Definition stl_import.cc:42
Mesh * read_stl_ascii(const char *filepath, const bool use_custom_normals)
Mesh * read_stl_file(const STLImportParams &import_params)
Definition stl_import.cc:53
struct Collection * collection
eIOAxis up_axis
Definition IO_stl.hh:25
bool use_mesh_validate
Definition IO_stl.hh:29
float global_scale
Definition IO_stl.hh:28
eIOAxis forward_axis
Definition IO_stl.hh:24
char filepath[FILE_MAX]
Definition IO_stl.hh:23
ReportList * reports
Definition IO_stl.hh:31
bool use_facet_normal
Definition IO_stl.hh:26
bool use_scene_unit
Definition IO_stl.hh:27
struct UnitSettings unit