WIP: IO: Add file unit type selector to stl importer and exporter #128337

Draft
Cedric Steiert wants to merge 3 commits from Bujus_Krachus/blender:main into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
5 changed files with 219 additions and 6 deletions

View File

@ -33,6 +33,7 @@
# include "IO_stl.hh"
# include "io_stl_ops.hh"
# include "io_utils.hh"
# include <intern/unit_type.cc>
static int wm_stl_export_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
@ -52,6 +53,7 @@ static int wm_stl_export_execute(bContext *C, wmOperator *op)
RNA_string_get(op->ptr, "filepath", export_params.filepath);
export_params.forward_axis = eIOAxis(RNA_enum_get(op->ptr, "forward_axis"));
export_params.up_axis = eIOAxis(RNA_enum_get(op->ptr, "up_axis"));
export_params.file_unit_type = UnitType(RNA_enum_get(op->ptr, "file_unit_type"));
export_params.global_scale = RNA_float_get(op->ptr, "global_scale");
export_params.apply_modifiers = RNA_boolean_get(op->ptr, "apply_modifiers");
export_params.export_selected_objects = RNA_boolean_get(op->ptr, "export_selected_objects");
@ -91,8 +93,20 @@ static void wm_stl_export_draw(bContext *C, wmOperator *op)
sub, ptr, "export_selected_objects", UI_ITEM_NONE, IFACE_("Selection Only"), ICON_NONE);
}
uiItemR(sub, ptr, "global_scale", UI_ITEM_NONE, IFACE_("Scale"), ICON_NONE);
uiItemR(sub, ptr, "use_scene_unit", UI_ITEM_NONE, IFACE_("Scene Unit"), ICON_NONE);
uiItemR(sub, ptr, "file_unit_type", UI_ITEM_NONE, IFACE_("File Unit"), ICON_NONE);
/* The scale input field should only be enabled, if unit type is set to CUSTOM */
uiLayout *subScale = uiLayoutColumn(sub, true);
PropertyRNA *prop = RNA_struct_find_property(ptr, "file_unit_type");
int selected_file_unit_type = RNA_property_enum_get(ptr, prop);
if (selected_file_unit_type == IO_UNIT_TYPE_CUSTOM) {
uiLayoutSetEnabled(subScale, true);
}
else {
uiLayoutSetEnabled(subScale, false);
}
uiItemR(subScale, ptr, "global_scale", UI_ITEM_NONE, IFACE_("Scale"), ICON_NONE);
uiItemR(subScale, ptr, "use_scene_unit", UI_ITEM_NONE, IFACE_("Scene Unit"), ICON_NONE);
uiItemR(sub, ptr, "forward_axis", UI_ITEM_NONE, IFACE_("Forward"), ICON_NONE);
uiItemR(sub, ptr, "up_axis", UI_ITEM_NONE, IFACE_("Up"), ICON_NONE);
}
@ -169,7 +183,26 @@ void WM_OT_stl_export(wmOperatorType *ot)
"Export only objects from this collection (and its children)");
RNA_def_property_flag(prop, PROP_HIDDEN);
RNA_def_float(ot->srna, "global_scale", 1.0f, 1e-6f, 1e6f, "Scale", "", 0.001f, 1000.0f);
prop = RNA_def_enum(
ot->srna,
"file_unit_type",
io_unit_type,
IO_UNIT_TYPE_METER,
"File Unit",
"Unit used for the output file as STL is a unitless file format. Usually "
"millimieters is assumed by most programs, however blender uses meters.\nThe choice depends "
"on the modelling scale, e.g. for 1m in blender to be perceived as 1m (equal to 1000mm) by "
"most other applications, set this option to 'Millimeters'");
RNA_def_property_update_runtime(prop, io_ui_unit_type_export_update);
RNA_def_float(ot->srna,
"global_scale",
1.0f,
1e-6f,
1e6f,
"Scale",
"Scale value used when 'File Unit' is set to 'CUSTOM'",
0.001f,
1000.0f);
RNA_def_boolean(ot->srna,
"use_scene_unit",
false,
@ -242,8 +275,21 @@ static void ui_stl_import_settings(const bContext *C, uiLayout *layout, PointerR
if (uiLayout *panel = uiLayoutPanel(C, layout, "STL_import_general", false, IFACE_("General"))) {
uiLayout *col = uiLayoutColumn(panel, false);
uiItemR(col, ptr, "global_scale", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "use_scene_unit", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(col, ptr, "file_unit_type", UI_ITEM_NONE, IFACE_("File Unit"), ICON_NONE);
/* The scale input field should only be enabled, if unit type is set to CUSTOM */
uiLayout *subScale = uiLayoutColumn(col, true);
PropertyRNA *prop = RNA_struct_find_property(ptr, "file_unit_type");
int selected_file_unit_type = RNA_property_enum_get(ptr, prop);
if (selected_file_unit_type == IO_UNIT_TYPE_CUSTOM) {
uiLayoutSetEnabled(subScale, true);
}
else {
uiLayoutSetEnabled(subScale, false);
}
uiItemR(subScale, ptr, "global_scale", UI_ITEM_NONE, IFACE_("Scale"), ICON_NONE);
uiItemR(subScale, ptr, "use_scene_unit", UI_ITEM_NONE, IFACE_("Scene Unit"), ICON_NONE);
uiItemR(col, ptr, "forward_axis", UI_ITEM_NONE, IFACE_("Forward Axis"), ICON_NONE);
uiItemR(col, ptr, "up_axis", UI_ITEM_NONE, nullptr, ICON_NONE);
}
@ -284,7 +330,23 @@ void WM_OT_stl_import(wmOperatorType *ot)
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
RNA_def_float(ot->srna, "global_scale", 1.0f, 1e-6f, 1e6f, "Scale", "", 0.001f, 1000.0f);
prop = RNA_def_enum(ot->srna,
"file_unit_type",
io_unit_type,
IO_UNIT_TYPE_METER,
"File Unit",
"Unit used in the input file as STL is a unitless file format. Usually "
"millimieters is assumed by most programs, however blender uses meters");
RNA_def_property_update_runtime(prop, io_ui_unit_type_import_update);
RNA_def_float(ot->srna,
"global_scale",
1.0f,
1e-6f,
1e6f,
"Scale",
"Scale value used when 'File Unit' is set to 'CUSTOM'",
0.001f,
1000.0f);
RNA_def_boolean(ot->srna,
"use_scene_unit",
false,

View File

@ -19,6 +19,7 @@ set(SRC
intern/orientation.cc
intern/path_util.cc
intern/subdiv_disabler.cc
intern/unit_type.cc
IO_abstract_hierarchy_iterator.h
IO_dupli_persistent_id.hh
@ -27,6 +28,7 @@ set(SRC
IO_path_util_types.hh
IO_subdiv_disabler.hh
IO_types.hh
IO_unit_type.hh
intern/dupli_parent_finder.hh
)

View File

@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
Bujus_Krachus marked this conversation as resolved Outdated

Today is 2024/

Today is `2024`/
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
struct EnumPropertyItem;
struct Main;
struct PointerRNA;
struct Scene;
enum UnitType {
IO_UNIT_TYPE_METER = 0,
IO_UNIT_TYPE_DECIMETER = 1,
IO_UNIT_TYPE_CENTIMETER = 2,
IO_UNIT_TYPE_MILLIMIETER = 3,
IO_UNIT_TYPE_INCH = 4,
IO_UNIT_TYPE_SCENE_LENGTH = 5,
IO_UNIT_TYPE_CUSTOM = 6
};
typedef struct {
UnitType unit_type;
float factor;
} UnitFactor;
const UnitFactor io_units_factor[] = {{IO_UNIT_TYPE_METER, 1.0},
{IO_UNIT_TYPE_DECIMETER, 0.1},
{IO_UNIT_TYPE_CENTIMETER, 0.01},
{IO_UNIT_TYPE_MILLIMIETER, 0.0010},
{IO_UNIT_TYPE_INCH, 0.0254}};
float get_scene_unit_scale_factor(bool asExport, Scene *scene);
void io_ui_unit_type_import_update(Main *main, Scene *scene, PointerRNA *ptr);
void io_ui_unit_type_export_update(Main *main, Scene *scene, PointerRNA *ptr);

View File

@ -0,0 +1,111 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_main.hh"
#include "DNA_scene_types.h"
#include "RNA_access.hh"
#include "RNA_types.hh"
#include "IO_unit_type.hh"
const EnumPropertyItem io_unit_type[] = {
{IO_UNIT_TYPE_METER,
"Meter",
0,
"Meter",
"Meter is used by blender and thus will persist the blender scale"},
{IO_UNIT_TYPE_DECIMETER, "Decimeter", 0, "Decimeter", ""},
{IO_UNIT_TYPE_CENTIMETER, "Centimeter", 0, "Centimeter", ""},
{IO_UNIT_TYPE_MILLIMIETER, "Millimeter", 0, "Millimeter", ""},
{IO_UNIT_TYPE_INCH, "Inch", 0, "Inch", ""},
{IO_UNIT_TYPE_SCENE_LENGTH,
"Scene Unit Length",
0,
"Scene Unit Length",
"Use the scene unit, as defined in the scene settings by taking scene scale and scene unit "
"length into account."},
{IO_UNIT_TYPE_CUSTOM, "CUSTOM", 0, "CUSTOM", "Use the scale factor provided below"},
{0, nullptr, 0, nullptr, nullptr}};
static float lookup_factor_meter_to_scene_unit(Scene *scene)
{
/* Returns the factor used to convert the scene unit length to meter. */
char scene_unit = scene->unit.length_unit;
if (scene->unit.system == USER_UNIT_METRIC) {
switch (scene_unit) {
case '\0': // kilometer

Not quite like hardcoding unit system indices in that way. As far as I can tell, these would only work as long as no one reorders to alters the unit systems arrays inside unit.cc. Would it be better to use something like BKE_unit_base_scalar to get the scaling factors instead?

Not quite like hardcoding unit system indices in that way. As far as I can tell, these would only work as long as no one reorders to alters the unit systems arrays inside `unit.cc`. Would it be better to use something like `BKE_unit_base_scalar` to get the scaling factors instead?

btw I'm not too familiar with the whole units system, perhaps @ideasman42 would have better opinions

btw I'm not too familiar with the whole units system, perhaps @ideasman42 would have better opinions
Review

Hi Aras, i didn't like the hardcoding either due to the reordering issue, usually try to avoid it.

In theory following code should work instead of hardcoding the bytes as type identifier and values as scalars

int base = BKE_unit_base_get((const void *)&scene->unit.system);
double base_scalar = BKE_unit_base_scalar(B_UNIT_LENGTH, base);

however base is always 1 and thus base_scalar always 1.0 no matter which scene unit is set for length in the blender scene properties. Even when changing the base here manually, only at int 5 there is another value despite 1.0.
I'll be honest, i have no idea how accessing the user defined scene unit works, thus the hardcoded approach.

The main issue is knowing which scene_unit is selected, only way i found until now is using scene->unit.length_unit and comparing it's byte values. I'd be glad to implement a proper idea over that if someone points me into the right direction.
The second thing is the scalars values, these could be refactored to using UN_SC_M from unit.cc, as they're already defined there.

little side note: only the dropdown entry Scene unit length of this PR is affected by this issue.

Hi Aras, i didn't like the hardcoding either due to the reordering issue, usually try to avoid it. In theory following code should work instead of hardcoding the bytes as type identifier and values as scalars ```cpp int base = BKE_unit_base_get((const void *)&scene->unit.system); double base_scalar = BKE_unit_base_scalar(B_UNIT_LENGTH, base); ``` however `base` is always `1` and thus `base_scalar` always `1.0` no matter which scene unit is set for length in the blender scene properties. Even when changing the `base` here manually, only at int `5` there is another value despite `1.0`. I'll be honest, i have no idea how accessing the user defined scene unit works, thus the hardcoded approach. The main issue is knowing which scene_unit is selected, only way i found until now is using `scene->unit.length_unit` and comparing it's byte values. I'd be glad to implement a proper idea over that if someone points me into the right direction. The second thing is the scalars values, these could be refactored to using `UN_SC_M` from `unit.cc`, as they're already defined there. _little side note: only the dropdown entry `Scene unit length` of this PR is affected by this issue._
return 1000.0;
case '\x3': // meter
return 1.0;
case '\x5': // centimeter
return 0.01;
case '\x6': // millimeter
return 0.001;
case '\a': // micrometer
return 1e-6;
}
}
else if (scene->unit.system == USER_UNIT_IMPERIAL) {
switch (scene_unit) {
case '\x2': // mile
return 1609.34;
case '\x4': // feet
return 0.3047992424196;
case '\x5': // inch
return 0.025399936868299999304;
case '\x6': // thou
return 2.54e-5;
}
}
return 1.0;
}
float get_scene_unit_scale_factor(bool as_export, Scene *scene)
{
/* Returns the scale factor needed to convert from the set scene unit to meters. */
float scene_unit_scale = scene->unit.scale_length;
scene_unit_scale = lookup_factor_meter_to_scene_unit(scene) / scene_unit_scale;
return as_export ? (1.0 / scene_unit_scale) : (scene_unit_scale);
}
static void calculate_io_scale_factor(bool as_export, Scene *scene, PointerRNA *ptr)
{
/* Calulates the scale factor for io depending on file unit selection, scene unit type and scale.
*/
int unit = RNA_enum_get(ptr, "file_unit_type");
if (unit == IO_UNIT_TYPE_CUSTOM) {
return;
}
// determine the io scale factor
float scale_factor = 1.0;
if (unit == IO_UNIT_TYPE_SCENE_LENGTH) {
// using scene unit
scale_factor = get_scene_unit_scale_factor(as_export, scene);
}
else {
// using file unit
scale_factor = as_export ? (scale_factor / io_units_factor[unit].factor) :
(scale_factor * io_units_factor[unit].factor);
}
RNA_float_set(ptr, "global_scale", scale_factor);
}
void io_ui_unit_type_export_update(Main * /*main*/, Scene *scene, PointerRNA *ptr)
{
/* Set the selected scale factor value to scale input field. */
calculate_io_scale_factor(true, scene, ptr);
}
void io_ui_unit_type_import_update(Main * /*main*/, Scene *scene, PointerRNA *ptr)
{
/* Set the selected scale factor value to scale input field. */
calculate_io_scale_factor(false, scene, ptr);
}

View File

@ -14,6 +14,8 @@
#include "IO_orientation.hh"
#include "IO_unit_type.hh"
struct Mesh;
struct bContext;
struct ReportList;
@ -37,6 +39,7 @@ struct STLExportParams {
eIOAxis forward_axis;
eIOAxis up_axis;
float global_scale;
UnitType file_unit_type;
bool export_selected_objects;
bool use_scene_unit;
bool apply_modifiers;