Geometry Nodes: Add STL Import Node
This commit adds an initial STL import node, the first of the nodes from the current Google Summer of Code Project [0]. The importer is refactored to output a mesh pointer, and a node is added to wrap around the importer. The node supports error messages from the importer. A new experimental option is added to hide the nodes by default until they're ready to be exposed generally. 0: https://devtalk.blender.org/t/gsoc-2024-geometry-nodes-file-import-nodes/34482) Pull Request: https://projects.blender.org/blender/blender/pulls/122418
This commit is contained in:
committed by
Hans Goudey
parent
d573ee1e6c
commit
d1455c4138
@@ -243,11 +243,13 @@ class NODE_MT_geometry_node_GEO_INPUT(Menu):
|
||||
bl_idname = "NODE_MT_geometry_node_GEO_INPUT"
|
||||
bl_label = "Input"
|
||||
|
||||
def draw(self, _context):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.menu("NODE_MT_geometry_node_GEO_INPUT_CONSTANT")
|
||||
layout.menu("NODE_MT_geometry_node_GEO_INPUT_GROUP")
|
||||
layout.menu("NODE_MT_geometry_node_GEO_INPUT_SCENE")
|
||||
if context.preferences.experimental.use_new_file_import_nodes:
|
||||
layout.menu("NODE_MT_category_IMPORT")
|
||||
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
|
||||
|
||||
|
||||
@@ -448,6 +450,14 @@ class NODE_MT_category_PRIMITIVES_MESH(Menu):
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeMeshUVSphere")
|
||||
node_add_menu.draw_assets_for_catalog(layout, "Mesh/Primitives")
|
||||
|
||||
class NODE_MT_category_IMPORT(Menu):
|
||||
bl_idname = "NODE_MT_category_IMPORT"
|
||||
bl_label = "Import"
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
node_add_menu.add_node_type(layout, "GeometryNodeImportSTL")
|
||||
node_add_menu.draw_assets_for_catalog(layout, "Input/Import")
|
||||
|
||||
class NODE_MT_geometry_node_mesh_topology(Menu):
|
||||
bl_idname = "NODE_MT_geometry_node_mesh_topology"
|
||||
@@ -816,6 +826,7 @@ classes = (
|
||||
NODE_MT_geometry_node_GEO_MESH_OPERATIONS,
|
||||
NODE_MT_category_GEO_UV,
|
||||
NODE_MT_category_PRIMITIVES_MESH,
|
||||
NODE_MT_category_IMPORT,
|
||||
NODE_MT_geometry_node_mesh_topology,
|
||||
NODE_MT_category_GEO_POINT,
|
||||
NODE_MT_category_simulation,
|
||||
|
||||
@@ -2798,6 +2798,7 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel):
|
||||
({"property": "use_extended_asset_browser"},
|
||||
("blender/blender/projects/10", "Pipeline, Assets & IO Project Page")),
|
||||
({"property": "use_new_volume_nodes"}, ("blender/blender/issues/103248", "#103248")),
|
||||
({"property": "use_new_file_import_nodes"}, ("blender/blender/issues/122846", "#122846")),
|
||||
({"property": "use_shader_node_previews"}, ("blender/blender/issues/110353", "#110353")),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1333,6 +1333,7 @@ void BKE_nodetree_remove_layer_n(bNodeTree *ntree, Scene *scene, int layer_index
|
||||
#define GEO_NODE_TOOL_ACTIVE_ELEMENT 2135
|
||||
#define GEO_NODE_SET_INSTANCE_TRANSFORM 2136
|
||||
#define GEO_NODE_INPUT_INSTANCE_TRANSFORM 2137
|
||||
#define GEO_NODE_IMPORT_STL 2138
|
||||
|
||||
/** \} */
|
||||
|
||||
|
||||
@@ -23,3 +23,8 @@ void STL_export(bContext *C, const STLExportParams *export_params)
|
||||
SCOPED_TIMER("STL Export");
|
||||
blender::io::stl::exporter_main(C, *export_params);
|
||||
}
|
||||
|
||||
Mesh *STL_import_mesh(const STLImportParams *import_params)
|
||||
{
|
||||
return blender::io::stl::read_stl_file(*import_params);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "IO_orientation.hh"
|
||||
|
||||
struct Mesh;
|
||||
struct bContext;
|
||||
struct ReportList;
|
||||
|
||||
@@ -48,3 +49,5 @@ struct STLExportParams {
|
||||
|
||||
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);
|
||||
|
||||
@@ -47,18 +47,7 @@ void stl_import_report_error(FILE *file)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
Mesh *read_stl_file(const STLImportParams &import_params)
|
||||
{
|
||||
FILE *file = BLI_fopen(import_params.filepath, "rb");
|
||||
if (!file) {
|
||||
@@ -67,7 +56,7 @@ void importer_main(Main *bmain,
|
||||
RPT_ERROR,
|
||||
"STL Import: Cannot open file '%s'",
|
||||
import_params.filepath);
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
BLI_SCOPED_DEFER([&]() { fclose(file); });
|
||||
|
||||
@@ -84,15 +73,10 @@ void importer_main(Main *bmain,
|
||||
RPT_ERROR,
|
||||
"STL Import: Failed to read file '%s'",
|
||||
import_params.filepath);
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
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];
|
||||
STRNCPY(ob_name, BLI_path_basename(import_params.filepath));
|
||||
BLI_path_extension_strip(ob_name);
|
||||
|
||||
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);
|
||||
@@ -103,7 +87,7 @@ void importer_main(Main *bmain,
|
||||
RPT_ERROR,
|
||||
"STL Import: Failed to import mesh from file '%s'",
|
||||
import_params.filepath);
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (import_params.use_mesh_validate) {
|
||||
@@ -114,6 +98,29 @@ void importer_main(Main *bmain,
|
||||
BKE_mesh_validate(mesh, verbose_validate, false);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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(scene, view_layer);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
struct bContext;
|
||||
struct Main;
|
||||
struct Mesh;
|
||||
struct Scene;
|
||||
struct ViewLayer;
|
||||
|
||||
@@ -19,6 +20,9 @@ namespace blender::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);
|
||||
|
||||
@@ -27,5 +31,4 @@ void importer_main(Main *bmain,
|
||||
Scene *scene,
|
||||
ViewLayer *view_layer,
|
||||
const STLImportParams &import_params);
|
||||
|
||||
} // namespace blender::io::stl
|
||||
|
||||
@@ -752,10 +752,11 @@ typedef struct UserDef_Experimental {
|
||||
char use_grease_pencil_version3;
|
||||
char enable_overlay_next;
|
||||
char use_new_volume_nodes;
|
||||
char use_new_file_import_nodes;
|
||||
char use_shader_node_previews;
|
||||
char use_grease_pencil_version3_convert_on_load;
|
||||
char use_animation_baklava;
|
||||
char _pad[3];
|
||||
char _pad[2];
|
||||
/** `makesdna` does not allow empty structs. */
|
||||
} UserDef_Experimental;
|
||||
|
||||
|
||||
@@ -7401,6 +7401,10 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(
|
||||
prop, "New Volume Nodes", "Enables visibility of the new Volume nodes in the UI");
|
||||
|
||||
prop = RNA_def_property(srna, "use_new_file_import_nodes", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "New File Import Nodes", "Enables visibility of the new File Import nodes in the UI");
|
||||
|
||||
prop = RNA_def_property(srna, "use_shader_node_previews", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Shader Node Previews", "Enables previews in the shader node editor");
|
||||
|
||||
@@ -353,6 +353,7 @@ DefNode(GeometryNode, GEO_NODE_GRID_TO_MESH, 0, "GRID_TO_MESH", GridToMesh, "Gri
|
||||
DefNode(GeometryNode, GEO_NODE_IMAGE_INFO, 0, "IMAGE_INFO", ImageInfo, "Image Info", "Retrieve information about an image")
|
||||
DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "Sample values from an image texture")
|
||||
DefNode(GeometryNode, GEO_NODE_IMAGE, def_geo_image, "IMAGE", InputImage, "Image", "Input image")
|
||||
DefNode(GeometryNode, GEO_NODE_IMPORT_STL, 0, "IMPORT_STL", ImportSTL, "Import STL", "Import a mesh from an STL file")
|
||||
DefNode(GeometryNode, GEO_NODE_INDEX_OF_NEAREST, 0, "INDEX_OF_NEAREST", IndexOfNearest, "Index of Nearest", "Find the nearest element in a group. Similar to the \"Sample Nearest\" node")
|
||||
DefNode(GeometryNode, GEO_NODE_INDEX_SWITCH, def_geo_index_switch, "INDEX_SWITCH", IndexSwitch, "Index Switch", "Choose between an arbitrary number of values with an index")
|
||||
DefNode(GeometryNode, GEO_NODE_INPUT_ACTIVE_CAMERA, 0, "INPUT_ACTIVE_CAMERA", InputActiveCamera, "Active Camera", "Retrieve the scene's active camera")
|
||||
|
||||
@@ -20,6 +20,8 @@ set(INC
|
||||
../../modifiers
|
||||
../../render
|
||||
../../windowmanager
|
||||
../../io/common
|
||||
../../io/stl
|
||||
# RNA_prototypes.h
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
)
|
||||
@@ -85,6 +87,7 @@ set(SRC
|
||||
nodes/node_geo_image.cc
|
||||
nodes/node_geo_image_info.cc
|
||||
nodes/node_geo_image_texture.cc
|
||||
nodes/node_geo_import_stl.cc
|
||||
nodes/node_geo_index_of_nearest.cc
|
||||
nodes/node_geo_index_switch.cc
|
||||
nodes/node_geo_input_active_camera.cc
|
||||
@@ -237,6 +240,8 @@ set(LIB
|
||||
bf_nodes
|
||||
PRIVATE bf::intern::atomic
|
||||
PRIVATE bf::extern::fmtlib
|
||||
PRIVATE bf_io_common
|
||||
PRIVATE bf_io_stl
|
||||
)
|
||||
|
||||
if(WITH_BULLET)
|
||||
|
||||
@@ -41,6 +41,13 @@ void search_link_ops_for_volume_grid_node(GatherLinkSearchOpParams ¶ms)
|
||||
}
|
||||
}
|
||||
|
||||
void search_link_ops_for_import_node(GatherLinkSearchOpParams ¶ms)
|
||||
{
|
||||
if (U.experimental.use_new_file_import_nodes) {
|
||||
nodes::search_link_ops_for_basic_node(params);
|
||||
}
|
||||
}
|
||||
|
||||
namespace enums {
|
||||
|
||||
const EnumPropertyItem *attribute_type_type_with_socket_fn(bContext * /*C*/,
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace blender::nodes {
|
||||
bool check_tool_context_and_error(GeoNodeExecParams ¶ms);
|
||||
void search_link_ops_for_tool_node(GatherLinkSearchOpParams ¶ms);
|
||||
void search_link_ops_for_volume_grid_node(GatherLinkSearchOpParams ¶ms);
|
||||
void search_link_ops_for_import_node(GatherLinkSearchOpParams ¶ms);
|
||||
|
||||
void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data,
|
||||
const VArray<float3> &positions,
|
||||
|
||||
90
source/blender/nodes/geometry/nodes/node_geo_import_stl.cc
Normal file
90
source/blender/nodes/geometry/nodes/node_geo_import_stl.cc
Normal file
@@ -0,0 +1,90 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
#include "BKE_mesh.hh"
|
||||
|
||||
#include "BKE_report.hh"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "IO_stl.hh"
|
||||
|
||||
#include "node_geometry_util.hh"
|
||||
|
||||
namespace blender::nodes::node_geo_import_stl {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::String>("Path").default_value("").description("Path to a STL file");
|
||||
|
||||
b.add_output<decl::Geometry>("Mesh");
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
{
|
||||
const std::string path = params.extract_input<std::string>("Path");
|
||||
|
||||
if (path.empty()) {
|
||||
params.set_default_remaining_outputs();
|
||||
return;
|
||||
}
|
||||
|
||||
STLImportParams import_params;
|
||||
|
||||
STRNCPY(import_params.filepath, path.c_str());
|
||||
|
||||
import_params.forward_axis = IO_AXIS_NEGATIVE_Z;
|
||||
import_params.up_axis = IO_AXIS_Y;
|
||||
import_params.use_facet_normal = false;
|
||||
import_params.use_scene_unit = false;
|
||||
import_params.global_scale = 1.0f;
|
||||
import_params.use_mesh_validate = true;
|
||||
|
||||
ReportList reports;
|
||||
BKE_reports_init(&reports, RPT_STORE);
|
||||
import_params.reports = &reports;
|
||||
|
||||
Mesh *mesh = STL_import_mesh(&import_params);
|
||||
|
||||
LISTBASE_FOREACH (Report *, report, &(import_params.reports)->list) {
|
||||
NodeWarningType type;
|
||||
|
||||
switch (report->type) {
|
||||
case RPT_ERROR:
|
||||
type = NodeWarningType::Error;
|
||||
break;
|
||||
default:
|
||||
type = NodeWarningType::Info;
|
||||
break;
|
||||
}
|
||||
|
||||
params.error_message_add(type, TIP_(report->message));
|
||||
}
|
||||
|
||||
BKE_reports_free(&reports);
|
||||
|
||||
if (mesh != nullptr) {
|
||||
params.set_output("Mesh", GeometrySet::from_mesh(mesh));
|
||||
}
|
||||
else {
|
||||
params.set_default_remaining_outputs();
|
||||
}
|
||||
}
|
||||
|
||||
static void node_register()
|
||||
{
|
||||
static blender::bke::bNodeType ntype;
|
||||
|
||||
geo_node_type_base(&ntype, GEO_NODE_IMPORT_STL, "Import STL", NODE_CLASS_INPUT);
|
||||
|
||||
ntype.geometry_node_execute = node_geo_exec;
|
||||
ntype.declare = node_declare;
|
||||
ntype.gather_link_search_ops = search_link_ops_for_import_node;
|
||||
|
||||
blender::bke::nodeRegisterType(&ntype);
|
||||
}
|
||||
NOD_REGISTER_NODE(node_register)
|
||||
|
||||
} // namespace blender::nodes::node_geo_import_stl
|
||||
Reference in New Issue
Block a user