Add Chromium-only Blender WebEngine parity work
This commit is contained in:
73
blender-5.2.0/source/blender/io/stl/CMakeLists.txt
Normal file
73
blender-5.2.0/source/blender/io/stl/CMakeLists.txt
Normal file
@@ -0,0 +1,73 @@
|
||||
# SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
.
|
||||
exporter
|
||||
importer
|
||||
intern
|
||||
../common
|
||||
../../editors/include
|
||||
../../makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
IO_stl.cc
|
||||
importer/stl_import.cc
|
||||
importer/stl_import_ascii_reader.cc
|
||||
importer/stl_import_binary_reader.cc
|
||||
importer/stl_import_mesh.cc
|
||||
exporter/stl_export.cc
|
||||
exporter/stl_export_writer.cc
|
||||
|
||||
intern/stl_data.hh
|
||||
|
||||
IO_stl.hh
|
||||
importer/stl_import.hh
|
||||
importer/stl_import_ascii_reader.hh
|
||||
importer/stl_import_binary_reader.hh
|
||||
importer/stl_import_mesh.hh
|
||||
exporter/stl_export.hh
|
||||
exporter/stl_export_writer.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::bmesh
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::intern::clog
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
bf_io_common
|
||||
PRIVATE bf::extern::fast_float
|
||||
PRIVATE bf::windowmanager
|
||||
)
|
||||
|
||||
blender_add_lib(bf_io_stl "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
if(WITH_GTESTS)
|
||||
set(TEST_SRC
|
||||
tests/stl_exporter_tests.cc
|
||||
)
|
||||
|
||||
set(TEST_INC
|
||||
${INC}
|
||||
|
||||
../../blenloader
|
||||
../../../../tests/gtests
|
||||
)
|
||||
|
||||
set(TEST_LIB
|
||||
${LIB}
|
||||
|
||||
bf_blenloader_test_util
|
||||
bf_io_stl
|
||||
)
|
||||
|
||||
blender_add_test_suite_lib(io_stl "${TEST_SRC}" "${TEST_INC}" "${INC_SYS}" "${TEST_LIB}")
|
||||
endif()
|
||||
34
blender-5.2.0/source/blender/io/stl/IO_stl.cc
Normal file
34
blender-5.2.0/source/blender/io/stl/IO_stl.cc
Normal file
@@ -0,0 +1,34 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup stl
|
||||
*/
|
||||
|
||||
#include "BLI_timeit.hh"
|
||||
|
||||
#include "IO_stl.hh"
|
||||
#include "stl_export.hh"
|
||||
#include "stl_import.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void STL_import(bContext *C, const STLImportParams *import_params)
|
||||
{
|
||||
SCOPED_TIMER("STL Import");
|
||||
io::stl::importer_main(C, *import_params);
|
||||
}
|
||||
|
||||
void STL_export(bContext *C, const STLExportParams *export_params)
|
||||
{
|
||||
SCOPED_TIMER("STL Export");
|
||||
io::stl::exporter_main(C, *export_params);
|
||||
}
|
||||
|
||||
Mesh *STL_import_mesh(const STLImportParams *import_params)
|
||||
{
|
||||
return io::stl::read_stl_file(*import_params);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
60
blender-5.2.0/source/blender/io/stl/IO_stl.hh
Normal file
60
blender-5.2.0/source/blender/io/stl/IO_stl.hh
Normal file
@@ -0,0 +1,60 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup stl
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_path_utils.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
|
||||
#include "IO_orientation.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Mesh;
|
||||
struct bContext;
|
||||
struct ReportList;
|
||||
|
||||
struct STLImportParams {
|
||||
/** Full path to the source STL file to import. */
|
||||
char filepath[FILE_MAX] = "";
|
||||
eIOAxis forward_axis = IO_AXIS_Y;
|
||||
eIOAxis up_axis = IO_AXIS_Z;
|
||||
bool use_facet_normal = false;
|
||||
bool use_scene_unit = false;
|
||||
float global_scale = 1.0f;
|
||||
bool use_mesh_validate = true;
|
||||
|
||||
ReportList *reports = nullptr;
|
||||
};
|
||||
|
||||
struct STLExportParams {
|
||||
/** Full path to the to-be-saved STL file. */
|
||||
char filepath[FILE_MAX] = "";
|
||||
eIOAxis forward_axis = IO_AXIS_Y;
|
||||
eIOAxis up_axis = IO_AXIS_Z;
|
||||
float global_scale = 1.0f;
|
||||
bool export_selected_objects = false;
|
||||
bool use_scene_unit = false;
|
||||
bool apply_modifiers = true;
|
||||
eEvaluationMode evaluation_mode = DAG_EVAL_RENDER;
|
||||
bool ascii_format = false;
|
||||
bool use_batch = false;
|
||||
char collection[MAX_ID_NAME - 2] = "";
|
||||
|
||||
ReportList *reports = nullptr;
|
||||
};
|
||||
|
||||
void STL_import(bContext *C, const STLImportParams *import_params);
|
||||
void STL_export(bContext *C, const STLExportParams *export_params);
|
||||
|
||||
Mesh *STL_import_mesh(const STLImportParams *import_params);
|
||||
|
||||
} // namespace blender
|
||||
207
blender-5.2.0/source/blender/io/stl/exporter/stl_export.cc
Normal file
207
blender-5.2.0/source/blender/io/stl/exporter/stl_export.cc
Normal file
@@ -0,0 +1,207 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup stl
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_mesh_wrapper.hh"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utils.hh"
|
||||
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "DNA_layer_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "ED_util.hh"
|
||||
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_vector.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include "IO_mesh_utils.hh"
|
||||
#include "IO_stl.hh"
|
||||
|
||||
#include "stl_data.hh"
|
||||
#include "stl_export.hh"
|
||||
#include "stl_export_writer.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static CLG_LogRef LOG = {"io.stl"};
|
||||
|
||||
namespace io::stl {
|
||||
|
||||
void export_frame(Depsgraph *depsgraph,
|
||||
float scene_unit_scale,
|
||||
const STLExportParams &export_params)
|
||||
{
|
||||
std::unique_ptr<FileWriter> writer;
|
||||
|
||||
/* If not exporting in batch, create single writer for all objects. */
|
||||
if (!export_params.use_batch) {
|
||||
try {
|
||||
writer = std::make_unique<FileWriter>(export_params.filepath, export_params.ascii_format);
|
||||
}
|
||||
catch (const std::runtime_error &ex) {
|
||||
CLOG_ERROR(&LOG, "Error: %s", ex.what());
|
||||
BKE_reportf(export_params.reports,
|
||||
RPT_ERROR,
|
||||
"STL Export: Cannot open file '%s'",
|
||||
export_params.filepath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DEGObjectIterSettings deg_iter_settings{};
|
||||
deg_iter_settings.depsgraph = depsgraph;
|
||||
deg_iter_settings.flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY |
|
||||
DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET | DEG_ITER_OBJECT_FLAG_VISIBLE |
|
||||
DEG_ITER_OBJECT_FLAG_DUPLI;
|
||||
|
||||
DEG_OBJECT_ITER_BEGIN (°_iter_settings, object) {
|
||||
if (object->type != OB_MESH) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (export_params.export_selected_objects && !(object->base_flag & BASE_SELECTED)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If exporting in batch, create writer for each iteration over objects. */
|
||||
if (export_params.use_batch) {
|
||||
/* Get object name by skipping initial "OB" prefix. */
|
||||
char object_name[sizeof(object->id.name) - 2];
|
||||
STRNCPY(object_name, object->id.name + 2);
|
||||
BLI_path_make_safe_filename(object_name);
|
||||
/* Replace spaces with underscores. */
|
||||
BLI_string_replace_char(object_name, ' ', '_');
|
||||
|
||||
/* Include object name in the exported file name. */
|
||||
char filepath[FILE_MAX];
|
||||
STRNCPY(filepath, export_params.filepath);
|
||||
/* When basename is just ".stl", regular path functions would
|
||||
* treat it as a hidden file called ".stl". Remove the extension
|
||||
* before trying to add a suffix. */
|
||||
const char *basename = BLI_path_basename(filepath);
|
||||
if (basename != nullptr && BLI_strcasecmp(basename, ".stl") == 0) {
|
||||
*const_cast<char *>(basename) = '\0';
|
||||
}
|
||||
|
||||
BLI_path_suffix(filepath, FILE_MAX, object_name, "");
|
||||
/* Make sure we have `.stl` extension (case insensitive). */
|
||||
if (!BLI_path_extension_check(filepath, ".stl")) {
|
||||
BLI_path_extension_ensure(filepath, FILE_MAX, ".stl");
|
||||
}
|
||||
|
||||
try {
|
||||
writer = std::make_unique<FileWriter>(filepath, export_params.ascii_format);
|
||||
}
|
||||
catch (const std::runtime_error &ex) {
|
||||
CLOG_ERROR(&LOG, "Error: %s", ex.what());
|
||||
BKE_reportf(
|
||||
export_params.reports, RPT_ERROR, "STL Export: Cannot open file '%s'", filepath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Object *obj_eval = DEG_get_evaluated(depsgraph, object);
|
||||
|
||||
MeshCoerceForExport coerce;
|
||||
const Mesh *mesh = mesh_coerce_for_export_setup(
|
||||
coerce, depsgraph, obj_eval, export_params.apply_modifiers);
|
||||
|
||||
/* Ensure data exists if currently in edit mode. */
|
||||
BKE_mesh_wrapper_ensure_mdata(const_cast<Mesh *>(mesh));
|
||||
|
||||
/* Calculate transform. */
|
||||
float global_scale = export_params.global_scale * scene_unit_scale;
|
||||
float axes_transform[3][3];
|
||||
unit_m3(axes_transform);
|
||||
float xform[4][4];
|
||||
/* +Y-forward and +Z-up are the default Blender axis settings. */
|
||||
mat3_from_axis_conversion(
|
||||
export_params.forward_axis, export_params.up_axis, IO_AXIS_Y, IO_AXIS_Z, axes_transform);
|
||||
mul_m4_m3m4(xform, axes_transform, obj_eval->object_to_world().ptr());
|
||||
/* mul_m4_m3m4 does not transform last row of obmat, i.e. location data. */
|
||||
mul_v3_m3v3(xform[3], axes_transform, obj_eval->object_to_world().location());
|
||||
xform[3][3] = obj_eval->object_to_world()[3][3];
|
||||
|
||||
const bool mirrored = is_negative_m4(xform);
|
||||
|
||||
/* Write triangles. */
|
||||
const Span<float3> positions = mesh->vert_positions();
|
||||
const Span<int> corner_verts = mesh->corner_verts();
|
||||
for (const int3 &tri : mesh->corner_tris()) {
|
||||
PackedTriangle data{};
|
||||
for (int i = 0; i < 3; i++) {
|
||||
/* Reverse face order for mirrored objects. */
|
||||
int idx = mirrored ? 2 - i : i;
|
||||
float3 pos = positions[corner_verts[tri[idx]]];
|
||||
mul_m4_v3(xform, pos);
|
||||
pos *= global_scale;
|
||||
data.vertices[i] = pos;
|
||||
}
|
||||
data.normal = math::normal_tri(data.vertices[0], data.vertices[1], data.vertices[2]);
|
||||
writer->write_triangle(data);
|
||||
}
|
||||
}
|
||||
DEG_OBJECT_ITER_END;
|
||||
}
|
||||
|
||||
void exporter_main(const bContext *C, const STLExportParams &export_params)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
|
||||
ED_editors_flush_edits(bmain);
|
||||
|
||||
Depsgraph *depsgraph = DEG_graph_new(bmain, scene, view_layer, export_params.evaluation_mode);
|
||||
|
||||
if (export_params.collection[0]) {
|
||||
Collection *collection = reinterpret_cast<Collection *>(
|
||||
BKE_libblock_find_name(bmain, ID_GR, export_params.collection));
|
||||
if (!collection) {
|
||||
BKE_reportf(export_params.reports,
|
||||
RPT_ERROR,
|
||||
"STL Export: Unable to find collection '%s'",
|
||||
export_params.collection);
|
||||
|
||||
DEG_graph_free(depsgraph);
|
||||
return;
|
||||
}
|
||||
|
||||
DEG_graph_build_from_collection(depsgraph, collection);
|
||||
}
|
||||
else {
|
||||
DEG_graph_build_from_view_layer(depsgraph);
|
||||
}
|
||||
BKE_scene_graph_update_tagged(depsgraph, bmain);
|
||||
|
||||
float scene_unit_scale = 1.0f;
|
||||
if ((scene->unit.system != USER_UNIT_NONE) && export_params.use_scene_unit) {
|
||||
scene_unit_scale = scene->unit.scale_length;
|
||||
}
|
||||
|
||||
export_frame(depsgraph, scene_unit_scale, export_params);
|
||||
|
||||
DEG_graph_free(depsgraph);
|
||||
}
|
||||
|
||||
} // namespace io::stl
|
||||
} // namespace blender
|
||||
25
blender-5.2.0/source/blender/io/stl/exporter/stl_export.hh
Normal file
25
blender-5.2.0/source/blender/io/stl/exporter/stl_export.hh
Normal file
@@ -0,0 +1,25 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup stl
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IO_stl.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Depsgraph;
|
||||
struct bContext;
|
||||
namespace io::stl {
|
||||
|
||||
void exporter_main(const bContext *C, const STLExportParams &export_params);
|
||||
void export_frame(Depsgraph *depsgraph,
|
||||
float scene_unit_scale,
|
||||
const STLExportParams &export_params);
|
||||
|
||||
} // namespace io::stl
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,88 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup stl
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "stl_data.hh"
|
||||
#include "stl_export_writer.hh"
|
||||
|
||||
#include "BLI_fileops.h"
|
||||
|
||||
namespace blender::io::stl {
|
||||
|
||||
FileWriter::FileWriter(const char *filepath, bool ascii) : tris_num_(0), ascii_(ascii)
|
||||
{
|
||||
file_ = BLI_fopen(filepath, "wb");
|
||||
if (file_ == nullptr) {
|
||||
throw std::runtime_error("STL export: failed to open file");
|
||||
}
|
||||
|
||||
/* Write header */
|
||||
if (ascii_) {
|
||||
fmt::print(file_, "solid \n");
|
||||
}
|
||||
else {
|
||||
const char header[BINARY_HEADER_SIZE] = {};
|
||||
fwrite(header, 1, BINARY_HEADER_SIZE, file_);
|
||||
/* Write placeholder for number of triangles, so that it can be updated later (after all
|
||||
* triangles have been written). */
|
||||
fwrite(&tris_num_, sizeof(uint32_t), 1, file_);
|
||||
}
|
||||
}
|
||||
|
||||
FileWriter::~FileWriter()
|
||||
{
|
||||
if (file_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (ascii_) {
|
||||
fmt::print(file_, "endsolid \n");
|
||||
}
|
||||
else {
|
||||
fseek(file_, BINARY_HEADER_SIZE, SEEK_SET);
|
||||
fwrite(&tris_num_, sizeof(uint32_t), 1, file_);
|
||||
}
|
||||
fclose(file_);
|
||||
}
|
||||
|
||||
void FileWriter::write_triangle(const PackedTriangle &data)
|
||||
{
|
||||
tris_num_++;
|
||||
if (ascii_) {
|
||||
fmt::print(file_,
|
||||
"facet normal {} {} {}\n"
|
||||
" outer loop\n"
|
||||
" vertex {} {} {}\n"
|
||||
" vertex {} {} {}\n"
|
||||
" vertex {} {} {}\n"
|
||||
" endloop\n"
|
||||
"endfacet\n",
|
||||
|
||||
data.normal.x,
|
||||
data.normal.y,
|
||||
data.normal.z,
|
||||
data.vertices[0].x,
|
||||
data.vertices[0].y,
|
||||
data.vertices[0].z,
|
||||
data.vertices[1].x,
|
||||
data.vertices[1].y,
|
||||
data.vertices[1].z,
|
||||
data.vertices[2].x,
|
||||
data.vertices[2].y,
|
||||
data.vertices[2].z);
|
||||
}
|
||||
else {
|
||||
fwrite(&data, sizeof(data), 1, file_);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::io::stl
|
||||
@@ -0,0 +1,30 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup stl
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
namespace blender::io::stl {
|
||||
|
||||
struct PackedTriangle;
|
||||
|
||||
class FileWriter {
|
||||
public:
|
||||
FileWriter(const char *filepath, bool ascii);
|
||||
~FileWriter();
|
||||
void write_triangle(const PackedTriangle &data);
|
||||
|
||||
private:
|
||||
FILE *file_;
|
||||
uint32_t tris_num_;
|
||||
bool ascii_;
|
||||
};
|
||||
|
||||
} // namespace blender::io::stl
|
||||
177
blender-5.2.0/source/blender/io/stl/importer/stl_import.cc
Normal file
177
blender-5.2.0/source/blender/io/stl/importer/stl_import.cc
Normal file
@@ -0,0 +1,177 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup stl
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_library.hh"
|
||||
#include "BKE_mesh.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "DNA_collection_types.h"
|
||||
#include "DNA_layer_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BLI_fileops.hh"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_memory_utils.hh"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
|
||||
#include "stl_data.hh"
|
||||
#include "stl_import.hh"
|
||||
#include "stl_import_ascii_reader.hh"
|
||||
#include "stl_import_binary_reader.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static CLG_LogRef LOG = {"io.stl"};
|
||||
|
||||
namespace io::stl {
|
||||
|
||||
void stl_import_report_error(FILE *file)
|
||||
{
|
||||
CLOG_ERROR(&LOG, "STL Importer: failed to read file");
|
||||
if (feof(file)) {
|
||||
CLOG_ERROR(&LOG, "End of file reached");
|
||||
}
|
||||
else if (ferror(file)) {
|
||||
perror("Error");
|
||||
}
|
||||
}
|
||||
|
||||
Mesh *read_stl_file(const STLImportParams &import_params)
|
||||
{
|
||||
FILE *file = BLI_fopen(import_params.filepath, "rb");
|
||||
if (!file) {
|
||||
CLOG_ERROR(&LOG, "Failed to open STL file:'%s'.", import_params.filepath);
|
||||
BKE_reportf(import_params.reports,
|
||||
RPT_ERROR,
|
||||
"STL Import: Cannot open file '%s'",
|
||||
import_params.filepath);
|
||||
return nullptr;
|
||||
}
|
||||
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);
|
||||
if (fread(&num_tri, sizeof(uint32_t), 1, file) != 1) {
|
||||
stl_import_report_error(file);
|
||||
BKE_reportf(import_params.reports,
|
||||
RPT_ERROR,
|
||||
"STL Import: Failed to read file '%s'",
|
||||
import_params.filepath);
|
||||
return nullptr;
|
||||
}
|
||||
bool is_ascii_stl = (file_size != (BINARY_HEADER_SIZE + 4 + BINARY_STRIDE * num_tri));
|
||||
|
||||
Mesh *mesh = is_ascii_stl ?
|
||||
read_stl_ascii(import_params.filepath, import_params.use_facet_normal) :
|
||||
read_stl_binary(file, import_params.use_facet_normal);
|
||||
|
||||
if (mesh == nullptr) {
|
||||
CLOG_ERROR(&LOG, "STL Importer: Failed to import mesh '%s'", import_params.filepath);
|
||||
BKE_reportf(import_params.reports,
|
||||
RPT_ERROR,
|
||||
"STL Import: Failed to import mesh from file '%s'",
|
||||
import_params.filepath);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (import_params.use_mesh_validate) {
|
||||
bool verbose_validate = false;
|
||||
#ifndef NDEBUG
|
||||
verbose_validate = true;
|
||||
#endif
|
||||
bke::mesh_validate(*mesh, verbose_validate);
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
void importer_main(const 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)
|
||||
{
|
||||
/* Name used for both mesh and object. */
|
||||
char ob_name[FILE_MAX];
|
||||
STRNCPY(ob_name, BLI_path_basename(import_params.filepath));
|
||||
BLI_path_extension_strip(ob_name);
|
||||
|
||||
Mesh *mesh = read_stl_file(import_params);
|
||||
if (!mesh) {
|
||||
return;
|
||||
}
|
||||
|
||||
Mesh *mesh_in_main = BKE_mesh_add(bmain, ob_name);
|
||||
BKE_mesh_nomain_to_mesh(mesh, mesh_in_main, nullptr);
|
||||
BKE_view_layer_base_deselect_all(*bmain, scene, view_layer);
|
||||
LayerCollection *lc = BKE_layer_collection_get_active_editable(view_layer);
|
||||
if (!ID_IS_EDITABLE(lc->collection)) {
|
||||
BKE_report(import_params.reports,
|
||||
RPT_WARNING,
|
||||
"Could not find an editable collection in current scene, imported data will not be "
|
||||
"instantiated");
|
||||
}
|
||||
Object *obj = BKE_object_add_only_object(bmain, OB_MESH, ob_name);
|
||||
obj->data = id_cast<ID *>(mesh_in_main);
|
||||
BKE_collection_object_add(bmain, lc->collection, obj);
|
||||
BKE_view_layer_synced_ensure(*bmain, scene, view_layer);
|
||||
if (Base *base = BKE_view_layer_base_find(view_layer, obj)) {
|
||||
/* `base` will be nullptr if the Object could not be instantiated in the current viewlayer. */
|
||||
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_SYNC_TO_EVAL);
|
||||
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 io::stl
|
||||
} // namespace blender
|
||||
39
blender-5.2.0/source/blender/io/stl/importer/stl_import.hh
Normal file
39
blender-5.2.0/source/blender/io/stl/importer/stl_import.hh
Normal file
@@ -0,0 +1,39 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup stl
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "IO_stl.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct bContext;
|
||||
struct Main;
|
||||
struct Mesh;
|
||||
struct Scene;
|
||||
struct ViewLayer;
|
||||
|
||||
namespace io::stl {
|
||||
|
||||
void stl_import_report_error(FILE *file);
|
||||
|
||||
/* Used from Geo nodes import for Mesh* access */
|
||||
Mesh *read_stl_file(const STLImportParams &import_params);
|
||||
|
||||
/* Main import function used from within Blender. */
|
||||
void importer_main(const 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 io::stl
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,161 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup stl
|
||||
*/
|
||||
|
||||
#include <system_error>
|
||||
|
||||
#include "BLI_fileops.hh"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_memory_utils.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_data.hh"
|
||||
#include "stl_import_ascii_reader.hh"
|
||||
#include "stl_import_mesh.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static CLG_LogRef LOG = {"io.stl"};
|
||||
|
||||
namespace 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 (ELEM(res.ec, std::errc::invalid_argument, std::errc::result_out_of_range)) {
|
||||
out = 0.0f;
|
||||
}
|
||||
start = const_cast<char *>(res.ptr);
|
||||
}
|
||||
};
|
||||
|
||||
static inline void parse_float3(StringBuffer &buf, float3 &out)
|
||||
{
|
||||
for (int i = 0; i < 3; i++) {
|
||||
buf.parse_float(out[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Mesh *read_stl_ascii(const char *filepath, const bool use_custom_normals)
|
||||
{
|
||||
size_t buffer_len;
|
||||
char *buffer = BLI_file_read_text_as_mem(filepath, 0, &buffer_len);
|
||||
if (buffer == nullptr) {
|
||||
CLOG_ERROR(&LOG, "STL Importer: cannot read from ASCII STL file: '%s'", filepath);
|
||||
return nullptr;
|
||||
}
|
||||
BLI_SCOPED_DEFER([&]() { MEM_delete(buffer); });
|
||||
|
||||
constexpr int num_reserved_tris = 1024;
|
||||
|
||||
StringBuffer str_buf(buffer, buffer_len);
|
||||
STLMeshHelper stl_mesh(num_reserved_tris, use_custom_normals);
|
||||
|
||||
PackedTriangle data{};
|
||||
str_buf.drop_line(); /* Skip header line */
|
||||
while (!str_buf.is_empty()) {
|
||||
if (str_buf.parse_token("vertex", 6)) {
|
||||
parse_float3(str_buf, data.vertices[0]);
|
||||
if (str_buf.parse_token("vertex", 6)) {
|
||||
parse_float3(str_buf, data.vertices[1]);
|
||||
}
|
||||
if (str_buf.parse_token("vertex", 6)) {
|
||||
parse_float3(str_buf, data.vertices[2]);
|
||||
}
|
||||
|
||||
stl_mesh.add_triangle(data);
|
||||
}
|
||||
else if (str_buf.parse_token("facet", 5)) {
|
||||
str_buf.drop_token(); /* Expecting "normal" */
|
||||
parse_float3(str_buf, data.normal);
|
||||
}
|
||||
else {
|
||||
str_buf.drop_token();
|
||||
}
|
||||
}
|
||||
|
||||
return stl_mesh.to_mesh();
|
||||
}
|
||||
|
||||
} // namespace io::stl
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,36 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup stl
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Mesh;
|
||||
|
||||
/**
|
||||
* ASCII STL spec:
|
||||
* <pre>
|
||||
* 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
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
namespace io::stl {
|
||||
|
||||
Mesh *read_stl_ascii(const char *filepath, bool use_custom_normals);
|
||||
|
||||
} // namespace io::stl
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,62 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup stl
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
#include "BKE_mesh.hh"
|
||||
|
||||
#include "BLI_array.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
|
||||
#include "IO_validate.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "stl_data.hh"
|
||||
#include "stl_import.hh"
|
||||
#include "stl_import_binary_reader.hh"
|
||||
#include "stl_import_mesh.hh"
|
||||
|
||||
namespace blender::io::stl {
|
||||
|
||||
static CLG_LogRef LOG = {"io.stl"};
|
||||
|
||||
Mesh *read_stl_binary(FILE *file, const bool use_custom_normals)
|
||||
{
|
||||
const int chunk_size = 1024;
|
||||
uint32_t num_tris = 0;
|
||||
fseek(file, BINARY_HEADER_SIZE, SEEK_SET);
|
||||
if (fread(&num_tris, sizeof(uint32_t), 1, file) != 1) {
|
||||
stl_import_report_error(file);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (num_tris == 0) {
|
||||
return BKE_mesh_new_nomain(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (!validate::size_fits_in_int(int64_t(num_tris) * 3)) {
|
||||
CLOG_WARN(&LOG, "STL mesh too large to import, exceeds max int size");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Array<PackedTriangle> 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(PackedTriangle), chunk_size, file))) {
|
||||
for (size_t i = 0; i < num_read_tris; i++) {
|
||||
stl_mesh.add_triangle(tris_buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return stl_mesh.to_mesh();
|
||||
}
|
||||
|
||||
} // namespace blender::io::stl
|
||||
@@ -0,0 +1,33 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup stl
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Mesh;
|
||||
|
||||
/* 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 io::stl {
|
||||
|
||||
Mesh *read_stl_binary(FILE *file, bool use_custom_normals);
|
||||
|
||||
} // namespace io::stl
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,87 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup stl
|
||||
*/
|
||||
|
||||
#include "BKE_mesh.hh"
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
|
||||
#include "stl_data.hh"
|
||||
#include "stl_import_mesh.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static CLG_LogRef LOG = {"io.stl"};
|
||||
|
||||
namespace io::stl {
|
||||
|
||||
STLMeshHelper::STLMeshHelper(int tris_num, bool use_custom_normals)
|
||||
: use_custom_normals_(use_custom_normals)
|
||||
{
|
||||
degenerate_tris_num_ = 0;
|
||||
duplicate_tris_num_ = 0;
|
||||
tris_.reserve(tris_num);
|
||||
/* Upper bound (all vertices are unique). */
|
||||
verts_.reserve(int64_t(tris_num) * 3);
|
||||
if (use_custom_normals) {
|
||||
loop_normals_.reserve(int64_t(tris_num) * 3);
|
||||
}
|
||||
}
|
||||
|
||||
bool STLMeshHelper::add_triangle(const PackedTriangle &data)
|
||||
{
|
||||
int v1_id = verts_.index_of_or_add(data.vertices[0]);
|
||||
int v2_id = verts_.index_of_or_add(data.vertices[1]);
|
||||
int v3_id = verts_.index_of_or_add(data.vertices[2]);
|
||||
if ((v1_id == v2_id) || (v1_id == v3_id) || (v2_id == v3_id)) {
|
||||
degenerate_tris_num_++;
|
||||
return false;
|
||||
}
|
||||
if (!tris_.add({v1_id, v2_id, v3_id})) {
|
||||
duplicate_tris_num_++;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (use_custom_normals_) {
|
||||
loop_normals_.append_n_times(data.normal, 3);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Mesh *STLMeshHelper::to_mesh()
|
||||
{
|
||||
if (degenerate_tris_num_ > 0) {
|
||||
CLOG_WARN(&LOG, "Removed %d degenerate triangles during import", degenerate_tris_num_);
|
||||
}
|
||||
if (duplicate_tris_num_ > 0) {
|
||||
CLOG_WARN(&LOG, "Removed %d duplicate triangles during import", duplicate_tris_num_);
|
||||
}
|
||||
|
||||
Mesh *mesh = BKE_mesh_new_nomain(verts_.size(), 0, tris_.size(), tris_.size() * 3);
|
||||
mesh->vert_positions_for_write().copy_from(verts_);
|
||||
offset_indices::fill_constant_group_size(3, 0, mesh->face_offsets_for_write());
|
||||
array_utils::copy(tris_.as_span().cast<int>(), mesh->corner_verts_for_write());
|
||||
|
||||
bke::mesh_smooth_set(*mesh, false);
|
||||
|
||||
/* NOTE: edges must be calculated first before setting custom normals. */
|
||||
bke::mesh_calc_edges(*mesh, false, false);
|
||||
|
||||
if (use_custom_normals_ && loop_normals_.size() == mesh->corners_num) {
|
||||
bke::mesh_set_custom_normals(*mesh, loop_normals_);
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
} // namespace io::stl
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,73 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup stl
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_vector.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
#include "stl_data.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Mesh;
|
||||
|
||||
namespace io::stl {
|
||||
class Triangle {
|
||||
public:
|
||||
int v1, v2, v3;
|
||||
/* Based on an old version of Python's frozen-set 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> verts_;
|
||||
VectorSet<Triangle> tris_;
|
||||
Vector<float3> loop_normals_;
|
||||
int degenerate_tris_num_;
|
||||
int duplicate_tris_num_;
|
||||
const bool use_custom_normals_;
|
||||
|
||||
public:
|
||||
STLMeshHelper(int tris_num, bool use_custom_normals);
|
||||
|
||||
/* Creates a new triangle from specified vertex locations,
|
||||
* duplicate vertices and triangles are merged.
|
||||
*/
|
||||
bool add_triangle(const PackedTriangle &data);
|
||||
|
||||
Mesh *to_mesh();
|
||||
};
|
||||
|
||||
} // namespace io::stl
|
||||
} // namespace blender
|
||||
27
blender-5.2.0/source/blender/io/stl/intern/stl_data.hh
Normal file
27
blender-5.2.0/source/blender/io/stl/intern/stl_data.hh
Normal file
@@ -0,0 +1,27 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_math_vector_types.hh"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace blender::io::stl {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct PackedTriangle {
|
||||
float3 normal;
|
||||
float3 vertices[3];
|
||||
uint16_t attribute_byte_count;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
inline constexpr size_t BINARY_HEADER_SIZE = 80;
|
||||
inline constexpr size_t BINARY_STRIDE = sizeof(PackedTriangle);
|
||||
|
||||
static_assert(sizeof(PackedTriangle) == 12 + (12 * 3) + 2,
|
||||
"PackedTriangle expected size mismatch");
|
||||
|
||||
} // namespace blender::io::stl
|
||||
125
blender-5.2.0/source/blender/io/stl/tests/stl_exporter_tests.cc
Normal file
125
blender-5.2.0/source/blender/io/stl/tests/stl_exporter_tests.cc
Normal file
@@ -0,0 +1,125 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
|
||||
#include "tests/blendfile_loading_base_test.h"
|
||||
|
||||
#include "BKE_appdir.hh"
|
||||
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "IO_stl.hh"
|
||||
#include "stl_export.hh"
|
||||
|
||||
namespace blender::io::stl {
|
||||
|
||||
/* Set this true to keep comparison-failing test output in temp file directory. */
|
||||
constexpr bool save_failing_test_output = false;
|
||||
|
||||
static std::string read_temp_file_in_string(const std::string &file_path)
|
||||
{
|
||||
std::string res;
|
||||
size_t buffer_len;
|
||||
char *buffer = BLI_file_read_text_as_mem(file_path.c_str(), 0, &buffer_len);
|
||||
if (buffer != nullptr) {
|
||||
res.assign(buffer, buffer_len);
|
||||
MEM_delete(buffer);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
class STLExportTest : public BlendfileLoadingBaseTest {
|
||||
public:
|
||||
bool load_file_and_depsgraph(const std::string &filepath)
|
||||
{
|
||||
if (!blendfile_load(filepath.c_str())) {
|
||||
return false;
|
||||
}
|
||||
depsgraph_create(DAG_EVAL_VIEWPORT);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
STLExportTest()
|
||||
{
|
||||
_params.ascii_format = true;
|
||||
}
|
||||
void SetUp() override
|
||||
{
|
||||
BlendfileLoadingBaseTest::SetUp();
|
||||
BKE_tempdir_init(nullptr);
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
BlendfileLoadingBaseTest::TearDown();
|
||||
BKE_tempdir_session_purge();
|
||||
}
|
||||
|
||||
static std::string get_temp_filename(const std::string &filename)
|
||||
{
|
||||
return std::string(BKE_tempdir_base()) + SEP_STR + filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export the given blend file with the given parameters and
|
||||
* test to see if it matches a golden file (ignoring any difference in Blender version number).
|
||||
* \param blendfile: input, relative to "tests" directory.
|
||||
* \param golden_stl: expected output, relative to "tests" directory.
|
||||
*/
|
||||
void compare_to_golden(const std::string &blendfile, const std::string &golden_stl)
|
||||
{
|
||||
if (!load_file_and_depsgraph(blendfile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string out_file_path = get_temp_filename(BLI_path_basename(golden_stl.c_str()));
|
||||
STRNCPY(_params.filepath, out_file_path.c_str());
|
||||
std::string golden_file_path = tests::flags_test_asset_dir() + SEP_STR + golden_stl;
|
||||
export_frame(depsgraph, 1.0f, _params);
|
||||
std::string output_str = read_temp_file_in_string(out_file_path);
|
||||
|
||||
std::string golden_str = read_temp_file_in_string(golden_file_path);
|
||||
bool are_equal = output_str == golden_str;
|
||||
if (save_failing_test_output && !are_equal) {
|
||||
printf("failing test output in %s\n", out_file_path.c_str());
|
||||
}
|
||||
ASSERT_TRUE(are_equal);
|
||||
if (!save_failing_test_output || are_equal) {
|
||||
BLI_delete(out_file_path.c_str(), false, false);
|
||||
}
|
||||
}
|
||||
|
||||
STLExportParams _params;
|
||||
};
|
||||
|
||||
TEST_F(STLExportTest, all_tris)
|
||||
{
|
||||
compare_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "all_tris.blend",
|
||||
"io_tests" SEP_STR "stl" SEP_STR "all_tris.stl");
|
||||
}
|
||||
|
||||
TEST_F(STLExportTest, all_quads)
|
||||
{
|
||||
compare_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "all_quads.blend",
|
||||
"io_tests" SEP_STR "stl" SEP_STR "all_quads.stl");
|
||||
}
|
||||
|
||||
TEST_F(STLExportTest, non_uniform_scale)
|
||||
{
|
||||
compare_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "non_uniform_scale.blend",
|
||||
"io_tests" SEP_STR "stl" SEP_STR "non_uniform_scale.stl");
|
||||
}
|
||||
|
||||
TEST_F(STLExportTest, cubes_positioned)
|
||||
{
|
||||
compare_to_golden("io_tests" SEP_STR "blend_geometry" SEP_STR "cubes_positioned.blend",
|
||||
"io_tests" SEP_STR "stl" SEP_STR "cubes_positioned.stl");
|
||||
}
|
||||
|
||||
} // namespace blender::io::stl
|
||||
Reference in New Issue
Block a user