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