Blender V5.0
stl_import_ascii_reader.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 <system_error>
10
11#include "BLI_fileops.hh"
13#include "BLI_memory_utils.hh"
14
15#include "DNA_mesh_types.h"
16
17/* NOTE: we could use C++17 <charconv> from_chars to parse
18 * floats, but even if some compilers claim full support,
19 * their standard libraries are not quite there yet.
20 * LLVM/libc++ only has a float parser since LLVM 14,
21 * and gcc/libstdc++ since 11.1. So until at least these are
22 * the minimum spec, use an external library. */
23#include "fast_float.h"
24
25#include "stl_data.hh"
27#include "stl_import_mesh.hh"
28
29#include "CLG_log.h"
30static CLG_LogRef LOG = {"io.stl"};
31
32namespace blender::io::stl {
33
35 private:
36 char *start;
37 const char *end;
38
39 public:
40 StringBuffer(char *buf, size_t len)
41 {
42 start = buf;
43 end = start + len;
44 }
45
46 bool is_empty() const
47 {
48 return start == end;
49 }
50
52 {
53 while ((start < end) && (*start) <= ' ') {
54 start++;
55 }
56 }
57
59 {
60 while ((start < end) && (*start) > ' ') {
61 start++;
62 }
63 }
64
65 void drop_line()
66 {
67 while (start < end && *start != '\n') {
68 start++;
69 }
70 }
71
72 bool parse_token(const char *token, size_t token_length)
73 {
75 if (end - start < token_length + 1) {
76 return false;
77 }
78 if (memcmp(start, token, token_length) != 0) {
79 return false;
80 }
81 if (start[token_length] > ' ') {
82 return false;
83 }
84 start += token_length + 1;
85 return true;
86 }
87
93
94 void parse_float(float &out)
95 {
97 /* Skip '+' */
98 if (start < end && *start == '+') {
99 start++;
100 }
101 fast_float::from_chars_result res = fast_float::from_chars(start, end, out);
102 if (ELEM(res.ec, std::errc::invalid_argument, std::errc::result_out_of_range)) {
103 out = 0.0f;
104 }
105 start = const_cast<char *>(res.ptr);
106 }
107};
108
109static inline void parse_float3(StringBuffer &buf, float3 &out)
110{
111 for (int i = 0; i < 3; i++) {
112 buf.parse_float(out[i]);
113 }
114}
115
116Mesh *read_stl_ascii(const char *filepath, const bool use_custom_normals)
117{
118 size_t buffer_len;
119 void *buffer = BLI_file_read_text_as_mem(filepath, 0, &buffer_len);
120 if (buffer == nullptr) {
121 CLOG_ERROR(&LOG, "STL Importer: cannot read from ASCII STL file: '%s'", filepath);
122 return nullptr;
123 }
124 BLI_SCOPED_DEFER([&]() { MEM_freeN(buffer); });
125
126 constexpr int num_reserved_tris = 1024;
127
128 StringBuffer str_buf(static_cast<char *>(buffer), buffer_len);
129 STLMeshHelper stl_mesh(num_reserved_tris, use_custom_normals);
130
132 str_buf.drop_line(); /* Skip header line */
133 while (!str_buf.is_empty()) {
134 if (str_buf.parse_token("vertex", 6)) {
135 parse_float3(str_buf, data.vertices[0]);
136 if (str_buf.parse_token("vertex", 6)) {
137 parse_float3(str_buf, data.vertices[1]);
138 }
139 if (str_buf.parse_token("vertex", 6)) {
140 parse_float3(str_buf, data.vertices[2]);
141 }
142
143 stl_mesh.add_triangle(data);
144 }
145 else if (str_buf.parse_token("facet", 5)) {
146 str_buf.drop_token(); /* Expecting "normal" */
147 parse_float3(str_buf, data.normal);
148 }
149 else {
150 str_buf.drop_token();
151 }
152 }
153
154 return stl_mesh.to_mesh();
155}
156
157} // namespace blender::io::stl
void * BLI_file_read_text_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size)
Definition storage.cc:511
File and directory operations.
#define BLI_SCOPED_DEFER(function_to_defer)
#define ELEM(...)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
BMesh const char void * data
bool add_triangle(const PackedTriangle &data)
bool parse_token(const char *token, size_t token_length)
#define out
#define LOG(level)
Definition log.h:97
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static void parse_float3(StringBuffer &buf, float3 &out)
Mesh * read_stl_ascii(const char *filepath, const bool use_custom_normals)
VecBase< float, 3 > float3
i
Definition text_draw.cc:230
uint len