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
This commit is contained in:
parent
14fc89f38f
commit
7c511f1b47
|
@ -455,6 +455,7 @@ class TOPBAR_MT_file_import(Menu):
|
|||
|
||||
self.layout.operator("wm.gpencil_import_svg", text="SVG as Grease Pencil")
|
||||
self.layout.operator("wm.obj_import", text="Wavefront (.obj) (experimental)")
|
||||
self.layout.operator("wm.stl_import", text="STL (.stl) (experimental)")
|
||||
|
||||
|
||||
class TOPBAR_MT_file_export(Menu):
|
||||
|
|
|
@ -13,6 +13,7 @@ set(INC
|
|||
../../io/gpencil
|
||||
../../io/usd
|
||||
../../io/wavefront_obj
|
||||
../../io/stl
|
||||
../../makesdna
|
||||
../../makesrna
|
||||
../../windowmanager
|
||||
|
@ -33,6 +34,7 @@ set(SRC
|
|||
io_obj.c
|
||||
io_ops.c
|
||||
io_usd.c
|
||||
io_stl_ops.c
|
||||
|
||||
io_alembic.h
|
||||
io_cache.h
|
||||
|
@ -41,12 +43,14 @@ set(SRC
|
|||
io_obj.h
|
||||
io_ops.h
|
||||
io_usd.h
|
||||
io_stl_ops.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
bf_blenkernel
|
||||
bf_blenlib
|
||||
bf_wavefront_obj
|
||||
bf_stl
|
||||
)
|
||||
|
||||
if(WITH_OPENCOLLADA)
|
||||
|
|
|
@ -31,28 +31,11 @@
|
|||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "IO_orientation.h"
|
||||
#include "IO_path_util_types.h"
|
||||
#include "IO_wavefront_obj.h"
|
||||
#include "io_obj.h"
|
||||
|
||||
static const EnumPropertyItem io_obj_transform_axis_forward[] = {
|
||||
{OBJ_AXIS_X_FORWARD, "X_FORWARD", 0, "X", "Positive X axis"},
|
||||
{OBJ_AXIS_Y_FORWARD, "Y_FORWARD", 0, "Y", "Positive Y axis"},
|
||||
{OBJ_AXIS_Z_FORWARD, "Z_FORWARD", 0, "Z", "Positive Z axis"},
|
||||
{OBJ_AXIS_NEGATIVE_X_FORWARD, "NEGATIVE_X_FORWARD", 0, "-X", "Negative X axis"},
|
||||
{OBJ_AXIS_NEGATIVE_Y_FORWARD, "NEGATIVE_Y_FORWARD", 0, "-Y", "Negative Y axis"},
|
||||
{OBJ_AXIS_NEGATIVE_Z_FORWARD, "NEGATIVE_Z_FORWARD", 0, "-Z", "Negative Z axis"},
|
||||
{0, NULL, 0, NULL, NULL}};
|
||||
|
||||
static const EnumPropertyItem io_obj_transform_axis_up[] = {
|
||||
{OBJ_AXIS_X_UP, "X_UP", 0, "X", "Positive X axis"},
|
||||
{OBJ_AXIS_Y_UP, "Y_UP", 0, "Y", "Positive Y axis"},
|
||||
{OBJ_AXIS_Z_UP, "Z_UP", 0, "Z", "Positive Z axis"},
|
||||
{OBJ_AXIS_NEGATIVE_X_UP, "NEGATIVE_X_UP", 0, "-X", "Negative X axis"},
|
||||
{OBJ_AXIS_NEGATIVE_Y_UP, "NEGATIVE_Y_UP", 0, "-Y", "Negative Y axis"},
|
||||
{OBJ_AXIS_NEGATIVE_Z_UP, "NEGATIVE_Z_UP", 0, "-Z", "Negative Z axis"},
|
||||
{0, NULL, 0, NULL, NULL}};
|
||||
|
||||
static const EnumPropertyItem io_obj_export_evaluation_mode[] = {
|
||||
{DAG_EVAL_RENDER, "DAG_EVAL_RENDER", 0, "Render", "Export objects as they appear in render"},
|
||||
{DAG_EVAL_VIEWPORT,
|
||||
|
@ -296,13 +279,9 @@ void WM_OT_obj_export(struct wmOperatorType *ot)
|
|||
INT_MIN,
|
||||
INT_MAX);
|
||||
/* Object transform options. */
|
||||
RNA_def_enum(ot->srna,
|
||||
"forward_axis",
|
||||
io_obj_transform_axis_forward,
|
||||
OBJ_AXIS_NEGATIVE_Z_FORWARD,
|
||||
"Forward Axis",
|
||||
"");
|
||||
RNA_def_enum(ot->srna, "up_axis", io_obj_transform_axis_up, OBJ_AXIS_Y_UP, "Up Axis", "");
|
||||
RNA_def_enum(
|
||||
ot->srna, "forward_axis", io_transform_axis, IO_AXIS_NEGATIVE_Z, "Forward Axis", "");
|
||||
RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Y, "Up Axis", "");
|
||||
RNA_def_float(ot->srna,
|
||||
"scaling_factor",
|
||||
1.0f,
|
||||
|
@ -480,13 +459,9 @@ void WM_OT_obj_import(struct wmOperatorType *ot)
|
|||
"Resize the objects to keep bounding box under this value. Value 0 disables clamping",
|
||||
0.0f,
|
||||
1000.0f);
|
||||
RNA_def_enum(ot->srna,
|
||||
"forward_axis",
|
||||
io_obj_transform_axis_forward,
|
||||
OBJ_AXIS_NEGATIVE_Z_FORWARD,
|
||||
"Forward Axis",
|
||||
"");
|
||||
RNA_def_enum(ot->srna, "up_axis", io_obj_transform_axis_up, OBJ_AXIS_Y_UP, "Up Axis", "");
|
||||
RNA_def_enum(
|
||||
ot->srna, "forward_axis", io_transform_axis, IO_AXIS_NEGATIVE_Z, "Forward Axis", "");
|
||||
RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Y, "Up Axis", "");
|
||||
RNA_def_boolean(ot->srna,
|
||||
"validate_meshes",
|
||||
false,
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "io_cache.h"
|
||||
#include "io_gpencil.h"
|
||||
#include "io_obj.h"
|
||||
#include "io_stl_ops.h"
|
||||
|
||||
void ED_operatortypes_io(void)
|
||||
{
|
||||
|
@ -60,4 +61,6 @@ void ED_operatortypes_io(void)
|
|||
|
||||
WM_operatortype_append(WM_OT_obj_export);
|
||||
WM_operatortype_append(WM_OT_obj_import);
|
||||
|
||||
WM_operatortype_append(WM_OT_stl_import);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup editor/io
|
||||
*/
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "ED_outliner.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
|
||||
#include "IO_stl.h"
|
||||
#include "io_stl_ops.h"
|
||||
|
||||
static int wm_stl_import_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
return WM_operator_filesel(C, op, event);
|
||||
}
|
||||
|
||||
static int wm_stl_import_execute(bContext *C, wmOperator *op)
|
||||
{
|
||||
struct STLImportParams params;
|
||||
params.forward_axis = RNA_enum_get(op->ptr, "forward_axis");
|
||||
params.up_axis = RNA_enum_get(op->ptr, "up_axis");
|
||||
params.use_facet_normal = RNA_boolean_get(op->ptr, "use_facet_normal");
|
||||
params.use_scene_unit = RNA_boolean_get(op->ptr, "use_scene_unit");
|
||||
params.global_scale = RNA_float_get(op->ptr, "global_scale");
|
||||
params.use_mesh_validate = RNA_boolean_get(op->ptr, "use_mesh_validate");
|
||||
|
||||
int files_len = RNA_collection_length(op->ptr, "files");
|
||||
|
||||
if (files_len) {
|
||||
PointerRNA fileptr;
|
||||
PropertyRNA *prop;
|
||||
char dir_only[FILE_MAX], file_only[FILE_MAX];
|
||||
|
||||
RNA_string_get(op->ptr, "directory", dir_only);
|
||||
prop = RNA_struct_find_property(op->ptr, "files");
|
||||
for (int i = 0; i < files_len; i++) {
|
||||
RNA_property_collection_lookup_int(op->ptr, prop, i, &fileptr);
|
||||
RNA_string_get(&fileptr, "name", file_only);
|
||||
BLI_join_dirfile(params.filepath, sizeof(params.filepath), dir_only, file_only);
|
||||
STL_import(C, ¶ms);
|
||||
}
|
||||
}
|
||||
else if (RNA_struct_property_is_set(op->ptr, "filepath")) {
|
||||
RNA_string_get(op->ptr, "filepath", params.filepath);
|
||||
STL_import(C, ¶ms);
|
||||
}
|
||||
else {
|
||||
BKE_report(op->reports, RPT_ERROR, "No filename given");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
|
||||
ED_outliner_select_sync_from_object_tag(C);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static bool wm_stl_import_check(bContext *UNUSED(C), wmOperator *op)
|
||||
{
|
||||
const int num_axes = 3;
|
||||
/* Both forward and up axes cannot be the same (or same except opposite sign). */
|
||||
if (RNA_enum_get(op->ptr, "forward_axis") % num_axes ==
|
||||
(RNA_enum_get(op->ptr, "up_axis") % num_axes)) {
|
||||
RNA_enum_set(op->ptr, "up_axis", RNA_enum_get(op->ptr, "up_axis") % num_axes + 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WM_OT_stl_import(struct wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
ot->name = "Import STL";
|
||||
ot->description = "Import an STL file as an object";
|
||||
ot->idname = "WM_OT_stl_import";
|
||||
|
||||
ot->invoke = wm_stl_import_invoke;
|
||||
ot->exec = wm_stl_import_execute;
|
||||
ot->poll = WM_operator_winactive;
|
||||
ot->check = wm_stl_import_check;
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
WM_operator_properties_filesel(ot,
|
||||
FILE_TYPE_FOLDER,
|
||||
FILE_BLENDER,
|
||||
FILE_OPENFILE,
|
||||
WM_FILESEL_FILEPATH | WM_FILESEL_FILES | WM_FILESEL_DIRECTORY |
|
||||
WM_FILESEL_SHOW_PROPS,
|
||||
FILE_DEFAULTDISPLAY,
|
||||
FILE_SORT_ALPHA);
|
||||
|
||||
RNA_def_float(ot->srna, "global_scale", 1.0f, 1e-6f, 1e6f, "Scale", "", 0.001f, 1000.0f);
|
||||
RNA_def_boolean(ot->srna,
|
||||
"use_scene_unit",
|
||||
false,
|
||||
"Scene Unit",
|
||||
"Apply current scene's unit (as defined by unit scale) to imported data");
|
||||
RNA_def_boolean(ot->srna,
|
||||
"use_facet_normal",
|
||||
false,
|
||||
"Facet Normals",
|
||||
"Use (import) facet normals (note that this will still give flat shading)");
|
||||
RNA_def_enum(ot->srna, "forward_axis", io_transform_axis, IO_AXIS_Y, "Forward Axis", "");
|
||||
RNA_def_enum(ot->srna, "up_axis", io_transform_axis, IO_AXIS_Z, "Up Axis", "");
|
||||
RNA_def_boolean(ot->srna,
|
||||
"use_mesh_validate",
|
||||
false,
|
||||
"Validate Mesh",
|
||||
"Validate and correct imported mesh (slow)");
|
||||
|
||||
/* Only show .stl files by default. */
|
||||
prop = RNA_def_string(ot->srna, "filter_glob", "*.stl", 0, "Extension Filter", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup editor/io
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct wmOperatorType;
|
||||
|
||||
void WM_OT_stl_export(struct wmOperatorType *ot);
|
||||
void WM_OT_stl_import(struct wmOperatorType *ot);
|
|
@ -2803,7 +2803,7 @@ int ED_path_extension_type(const char *path)
|
|||
return FILE_TYPE_ARCHIVE;
|
||||
}
|
||||
if (BLI_path_extension_check_n(
|
||||
path, ".obj", ".mtl", ".3ds", ".fbx", ".glb", ".gltf", ".svg", NULL)) {
|
||||
path, ".obj", ".mtl", ".3ds", ".fbx", ".glb", ".gltf", ".svg", ".stl", NULL)) {
|
||||
return FILE_TYPE_OBJECT_IO;
|
||||
}
|
||||
if (BLI_path_extension_check_array(path, imb_ext_image)) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
add_subdirectory(common)
|
||||
add_subdirectory(wavefront_obj)
|
||||
add_subdirectory(stl)
|
||||
|
||||
if(WITH_ALEMBIC)
|
||||
add_subdirectory(alembic)
|
||||
|
|
|
@ -8,6 +8,7 @@ set(INC
|
|||
../../depsgraph
|
||||
../../makesdna
|
||||
../../../../intern/guardedalloc
|
||||
../../makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
|
@ -19,12 +20,14 @@ set(SRC
|
|||
intern/dupli_persistent_id.cc
|
||||
intern/object_identifier.cc
|
||||
intern/path_util.cc
|
||||
intern/orientation.c
|
||||
|
||||
IO_abstract_hierarchy_iterator.h
|
||||
IO_dupli_persistent_id.hh
|
||||
IO_path_util.hh
|
||||
IO_path_util_types.h
|
||||
IO_types.h
|
||||
IO_orientation.h
|
||||
intern/dupli_parent_finder.hh
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "RNA_types.h"
|
||||
|
||||
typedef enum {
|
||||
IO_AXIS_X = 0,
|
||||
IO_AXIS_Y = 1,
|
||||
IO_AXIS_Z = 2,
|
||||
IO_AXIS_NEGATIVE_X = 3,
|
||||
IO_AXIS_NEGATIVE_Y = 4,
|
||||
IO_AXIS_NEGATIVE_Z = 5,
|
||||
} eIOAxis;
|
||||
|
||||
extern const EnumPropertyItem io_transform_axis[];
|
|
@ -0,0 +1,14 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "RNA_types.h"
|
||||
|
||||
#include "IO_orientation.h"
|
||||
|
||||
const EnumPropertyItem io_transform_axis[] = {
|
||||
{IO_AXIS_X, "X", 0, "X", "Positive X axis"},
|
||||
{IO_AXIS_Y, "Y", 0, "Y", "Positive Y axis"},
|
||||
{IO_AXIS_Z, "Z", 0, "Z", "Positive Z axis"},
|
||||
{IO_AXIS_NEGATIVE_X, "NEGATIVE_X", 0, "-X", "Negative X axis"},
|
||||
{IO_AXIS_NEGATIVE_Y, "NEGATIVE_Y", 0, "-Y", "Negative Y axis"},
|
||||
{IO_AXIS_NEGATIVE_Z, "NEGATIVE_Z", 0, "-Z", "Negative Z axis"},
|
||||
{0, NULL, 0, NULL, NULL}};
|
|
@ -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}")
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -9,30 +9,13 @@
|
|||
#include "BKE_context.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "DEG_depsgraph.h"
|
||||
#include "IO_orientation.h"
|
||||
#include "IO_path_util_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
OBJ_AXIS_X_UP = 0,
|
||||
OBJ_AXIS_Y_UP = 1,
|
||||
OBJ_AXIS_Z_UP = 2,
|
||||
OBJ_AXIS_NEGATIVE_X_UP = 3,
|
||||
OBJ_AXIS_NEGATIVE_Y_UP = 4,
|
||||
OBJ_AXIS_NEGATIVE_Z_UP = 5,
|
||||
} eTransformAxisUp;
|
||||
|
||||
typedef enum {
|
||||
OBJ_AXIS_X_FORWARD = 0,
|
||||
OBJ_AXIS_Y_FORWARD = 1,
|
||||
OBJ_AXIS_Z_FORWARD = 2,
|
||||
OBJ_AXIS_NEGATIVE_X_FORWARD = 3,
|
||||
OBJ_AXIS_NEGATIVE_Y_FORWARD = 4,
|
||||
OBJ_AXIS_NEGATIVE_Z_FORWARD = 5,
|
||||
} eTransformAxisForward;
|
||||
|
||||
static const int TOTAL_AXES = 3;
|
||||
|
||||
struct OBJExportParams {
|
||||
|
@ -52,8 +35,8 @@ struct OBJExportParams {
|
|||
int end_frame;
|
||||
|
||||
/* Geometry Transform options. */
|
||||
eTransformAxisForward forward_axis;
|
||||
eTransformAxisUp up_axis;
|
||||
eIOAxis forward_axis;
|
||||
eIOAxis up_axis;
|
||||
float scaling_factor;
|
||||
|
||||
/* File Write Options. */
|
||||
|
@ -86,8 +69,8 @@ struct OBJImportParams {
|
|||
char filepath[FILE_MAX];
|
||||
/** Value 0 disables clamping. */
|
||||
float clamp_size;
|
||||
eTransformAxisForward forward_axis;
|
||||
eTransformAxisUp up_axis;
|
||||
eIOAxis forward_axis;
|
||||
eIOAxis up_axis;
|
||||
bool validate_meshes;
|
||||
};
|
||||
|
||||
|
|
|
@ -117,13 +117,12 @@ std::pair<Mesh *, bool> OBJMesh::triangulate_mesh_eval()
|
|||
return {triangulated, true};
|
||||
}
|
||||
|
||||
void OBJMesh::set_world_axes_transform(const eTransformAxisForward forward,
|
||||
const eTransformAxisUp up)
|
||||
void OBJMesh::set_world_axes_transform(const eIOAxis forward, const eIOAxis up)
|
||||
{
|
||||
float axes_transform[3][3];
|
||||
unit_m3(axes_transform);
|
||||
/* +Y-forward and +Z-up are the default Blender axis settings. */
|
||||
mat3_from_axis_conversion(OBJ_AXIS_Y_FORWARD, OBJ_AXIS_Z_UP, forward, up, axes_transform);
|
||||
mat3_from_axis_conversion(IO_AXIS_Y, IO_AXIS_Z, forward, up, axes_transform);
|
||||
/* mat3_from_axis_conversion returns a transposed matrix! */
|
||||
transpose_m3(axes_transform);
|
||||
mul_m4_m3m4(world_and_axes_transform_, axes_transform, export_object_eval_.obmat);
|
||||
|
|
|
@ -256,6 +256,6 @@ class OBJMesh : NonCopyable {
|
|||
/**
|
||||
* Set the final transform after applying axes settings and an Object's world transform.
|
||||
*/
|
||||
void set_world_axes_transform(eTransformAxisForward forward, eTransformAxisUp up);
|
||||
void set_world_axes_transform(eIOAxis forward, eIOAxis up);
|
||||
};
|
||||
} // namespace blender::io::obj
|
||||
|
|
|
@ -25,13 +25,12 @@ OBJCurve::OBJCurve(const Depsgraph *depsgraph,
|
|||
set_world_axes_transform(export_params.forward_axis, export_params.up_axis);
|
||||
}
|
||||
|
||||
void OBJCurve::set_world_axes_transform(const eTransformAxisForward forward,
|
||||
const eTransformAxisUp up)
|
||||
void OBJCurve::set_world_axes_transform(const eIOAxis forward, const eIOAxis up)
|
||||
{
|
||||
float axes_transform[3][3];
|
||||
unit_m3(axes_transform);
|
||||
/* +Y-forward and +Z-up are the Blender's default axis settings. */
|
||||
mat3_from_axis_conversion(OBJ_AXIS_Y_FORWARD, OBJ_AXIS_Z_UP, forward, up, axes_transform);
|
||||
mat3_from_axis_conversion(IO_AXIS_Y, IO_AXIS_Z, forward, up, axes_transform);
|
||||
/* mat3_from_axis_conversion returns a transposed matrix! */
|
||||
transpose_m3(axes_transform);
|
||||
mul_m4_m3m4(world_axes_transform_, axes_transform, export_object_eval_->obmat);
|
||||
|
|
|
@ -56,7 +56,7 @@ class OBJCurve : NonCopyable {
|
|||
/**
|
||||
* Set the final transform after applying axes settings and an Object's world transform.
|
||||
*/
|
||||
void set_world_axes_transform(eTransformAxisForward forward, eTransformAxisUp up);
|
||||
void set_world_axes_transform(eIOAxis forward, eIOAxis up);
|
||||
};
|
||||
|
||||
} // namespace blender::io::obj
|
||||
|
|
|
@ -99,11 +99,8 @@ void transform_object(Object *object, const OBJImportParams &import_params)
|
|||
float obmat[4][4];
|
||||
unit_m4(obmat);
|
||||
/* +Y-forward and +Z-up are the default Blender axis settings. */
|
||||
mat3_from_axis_conversion(import_params.forward_axis,
|
||||
import_params.up_axis,
|
||||
OBJ_AXIS_Y_FORWARD,
|
||||
OBJ_AXIS_Z_UP,
|
||||
axes_transform);
|
||||
mat3_from_axis_conversion(
|
||||
import_params.forward_axis, import_params.up_axis, IO_AXIS_Y, IO_AXIS_Z, axes_transform);
|
||||
/* mat3_from_axis_conversion returns a transposed matrix! */
|
||||
transpose_m3(axes_transform);
|
||||
copy_m4_m3(obmat, axes_transform);
|
||||
|
|
|
@ -315,8 +315,8 @@ TEST_F(obj_exporter_regression_test, all_quads)
|
|||
TEST_F(obj_exporter_regression_test, fgons)
|
||||
{
|
||||
OBJExportParamsDefault _export;
|
||||
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
|
||||
_export.params.up_axis = OBJ_AXIS_Z_UP;
|
||||
_export.params.forward_axis = IO_AXIS_Y;
|
||||
_export.params.up_axis = IO_AXIS_Z;
|
||||
_export.params.export_materials = false;
|
||||
compare_obj_export_to_golden(
|
||||
"io_tests/blend_geometry/fgons.blend", "io_tests/obj/fgons.obj", "", _export.params);
|
||||
|
@ -325,8 +325,8 @@ TEST_F(obj_exporter_regression_test, fgons)
|
|||
TEST_F(obj_exporter_regression_test, edges)
|
||||
{
|
||||
OBJExportParamsDefault _export;
|
||||
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
|
||||
_export.params.up_axis = OBJ_AXIS_Z_UP;
|
||||
_export.params.forward_axis = IO_AXIS_Y;
|
||||
_export.params.up_axis = IO_AXIS_Z;
|
||||
_export.params.export_materials = false;
|
||||
compare_obj_export_to_golden(
|
||||
"io_tests/blend_geometry/edges.blend", "io_tests/obj/edges.obj", "", _export.params);
|
||||
|
@ -335,8 +335,8 @@ TEST_F(obj_exporter_regression_test, edges)
|
|||
TEST_F(obj_exporter_regression_test, vertices)
|
||||
{
|
||||
OBJExportParamsDefault _export;
|
||||
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
|
||||
_export.params.up_axis = OBJ_AXIS_Z_UP;
|
||||
_export.params.forward_axis = IO_AXIS_Y;
|
||||
_export.params.up_axis = IO_AXIS_Z;
|
||||
_export.params.export_materials = false;
|
||||
compare_obj_export_to_golden(
|
||||
"io_tests/blend_geometry/vertices.blend", "io_tests/obj/vertices.obj", "", _export.params);
|
||||
|
@ -355,8 +355,8 @@ TEST_F(obj_exporter_regression_test, non_uniform_scale)
|
|||
TEST_F(obj_exporter_regression_test, nurbs_as_nurbs)
|
||||
{
|
||||
OBJExportParamsDefault _export;
|
||||
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
|
||||
_export.params.up_axis = OBJ_AXIS_Z_UP;
|
||||
_export.params.forward_axis = IO_AXIS_Y;
|
||||
_export.params.up_axis = IO_AXIS_Z;
|
||||
_export.params.export_materials = false;
|
||||
_export.params.export_curves_as_nurbs = true;
|
||||
compare_obj_export_to_golden(
|
||||
|
@ -366,8 +366,8 @@ TEST_F(obj_exporter_regression_test, nurbs_as_nurbs)
|
|||
TEST_F(obj_exporter_regression_test, nurbs_curves_as_nurbs)
|
||||
{
|
||||
OBJExportParamsDefault _export;
|
||||
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
|
||||
_export.params.up_axis = OBJ_AXIS_Z_UP;
|
||||
_export.params.forward_axis = IO_AXIS_Y;
|
||||
_export.params.up_axis = IO_AXIS_Z;
|
||||
_export.params.export_materials = false;
|
||||
_export.params.export_curves_as_nurbs = true;
|
||||
compare_obj_export_to_golden("io_tests/blend_geometry/nurbs_curves.blend",
|
||||
|
@ -379,8 +379,8 @@ TEST_F(obj_exporter_regression_test, nurbs_curves_as_nurbs)
|
|||
TEST_F(obj_exporter_regression_test, nurbs_as_mesh)
|
||||
{
|
||||
OBJExportParamsDefault _export;
|
||||
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
|
||||
_export.params.up_axis = OBJ_AXIS_Z_UP;
|
||||
_export.params.forward_axis = IO_AXIS_Y;
|
||||
_export.params.up_axis = IO_AXIS_Z;
|
||||
_export.params.export_materials = false;
|
||||
_export.params.export_curves_as_nurbs = false;
|
||||
compare_obj_export_to_golden(
|
||||
|
@ -390,8 +390,8 @@ TEST_F(obj_exporter_regression_test, nurbs_as_mesh)
|
|||
TEST_F(obj_exporter_regression_test, cube_all_data_triangulated)
|
||||
{
|
||||
OBJExportParamsDefault _export;
|
||||
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
|
||||
_export.params.up_axis = OBJ_AXIS_Z_UP;
|
||||
_export.params.forward_axis = IO_AXIS_Y;
|
||||
_export.params.up_axis = IO_AXIS_Z;
|
||||
_export.params.export_materials = false;
|
||||
_export.params.export_triangulated_mesh = true;
|
||||
compare_obj_export_to_golden("io_tests/blend_geometry/cube_all_data.blend",
|
||||
|
@ -403,8 +403,8 @@ TEST_F(obj_exporter_regression_test, cube_all_data_triangulated)
|
|||
TEST_F(obj_exporter_regression_test, cube_normal_edit)
|
||||
{
|
||||
OBJExportParamsDefault _export;
|
||||
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
|
||||
_export.params.up_axis = OBJ_AXIS_Z_UP;
|
||||
_export.params.forward_axis = IO_AXIS_Y;
|
||||
_export.params.up_axis = IO_AXIS_Z;
|
||||
_export.params.export_materials = false;
|
||||
compare_obj_export_to_golden("io_tests/blend_geometry/cube_normal_edit.blend",
|
||||
"io_tests/obj/cube_normal_edit.obj",
|
||||
|
@ -459,8 +459,8 @@ TEST_F(obj_exporter_regression_test, cubes_with_textures_relative)
|
|||
TEST_F(obj_exporter_regression_test, suzanne_all_data)
|
||||
{
|
||||
OBJExportParamsDefault _export;
|
||||
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
|
||||
_export.params.up_axis = OBJ_AXIS_Z_UP;
|
||||
_export.params.forward_axis = IO_AXIS_Y;
|
||||
_export.params.up_axis = IO_AXIS_Z;
|
||||
_export.params.export_materials = false;
|
||||
_export.params.export_smooth_groups = true;
|
||||
compare_obj_export_to_golden("io_tests/blend_geometry/suzanne_all_data.blend",
|
||||
|
@ -491,8 +491,8 @@ TEST_F(obj_exporter_regression_test, all_curves_as_nurbs)
|
|||
TEST_F(obj_exporter_regression_test, all_objects)
|
||||
{
|
||||
OBJExportParamsDefault _export;
|
||||
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
|
||||
_export.params.up_axis = OBJ_AXIS_Z_UP;
|
||||
_export.params.forward_axis = IO_AXIS_Y;
|
||||
_export.params.up_axis = IO_AXIS_Z;
|
||||
_export.params.export_smooth_groups = true;
|
||||
compare_obj_export_to_golden("io_tests/blend_scene/all_objects.blend",
|
||||
"io_tests/obj/all_objects.obj",
|
||||
|
@ -503,8 +503,8 @@ TEST_F(obj_exporter_regression_test, all_objects)
|
|||
TEST_F(obj_exporter_regression_test, all_objects_mat_groups)
|
||||
{
|
||||
OBJExportParamsDefault _export;
|
||||
_export.params.forward_axis = OBJ_AXIS_Y_FORWARD;
|
||||
_export.params.up_axis = OBJ_AXIS_Z_UP;
|
||||
_export.params.forward_axis = IO_AXIS_Y;
|
||||
_export.params.up_axis = IO_AXIS_Z;
|
||||
_export.params.export_smooth_groups = true;
|
||||
_export.params.export_material_groups = true;
|
||||
compare_obj_export_to_golden("io_tests/blend_scene/all_objects.blend",
|
||||
|
|
|
@ -17,8 +17,8 @@ struct OBJExportParamsDefault {
|
|||
params.start_frame = 0;
|
||||
params.end_frame = 1;
|
||||
|
||||
params.forward_axis = OBJ_AXIS_NEGATIVE_Z_FORWARD;
|
||||
params.up_axis = OBJ_AXIS_Y_UP;
|
||||
params.forward_axis = IO_AXIS_NEGATIVE_Z;
|
||||
params.up_axis = IO_AXIS_Y;
|
||||
params.scaling_factor = 1.f;
|
||||
|
||||
params.apply_modifiers = true;
|
||||
|
|
|
@ -55,8 +55,8 @@ class obj_importer_test : public BlendfileLoadingBaseTest {
|
|||
|
||||
OBJImportParams params;
|
||||
params.clamp_size = 0;
|
||||
params.forward_axis = OBJ_AXIS_NEGATIVE_Z_FORWARD;
|
||||
params.up_axis = OBJ_AXIS_Y_UP;
|
||||
params.forward_axis = IO_AXIS_NEGATIVE_Z;
|
||||
params.up_axis = IO_AXIS_Y;
|
||||
|
||||
std::string obj_path = blender::tests::flags_test_asset_dir() + "/io_tests/obj/" + path;
|
||||
strncpy(params.filepath, obj_path.c_str(), FILE_MAX - 1);
|
||||
|
|
Loading…
Reference in New Issue