Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorErik Abrahamsson <erik85>2021-09-24 20:41:49 +0300
committerHans Goudey <h.goudey@me.com>2021-09-24 20:41:49 +0300
commitbe16794ba17246eb035bdda42bb5e69d6bf5fa40 (patch)
treef2ec5117a7bcb0e3170cb823e9f1e81595ee3b75 /source
parent5c0017e85a75ad004ef5f4944828074a7fa95f21 (diff)
Geometry Nodes: String to Curves Node
This commit adds a node that generates a text paragraph as curve instances. The inputs on the node control the overall shape of the paragraph, and other nodes can be used to move the individual instances afterwards. To output more than one line, the "Special Characters" node can be used. The node outputs instances instead of real geometry so that it doesn't have to duplicate work for every character afterwards. This is much more efficient, because all of the curve evaluation and nodes like fill curve don't have to repeat the same calculation for every instance of the same character. In the future, the instances component will support attributes, and the node can output attribute fields like "Word Index" and "Line Index". Differential Revision: https://developer.blender.org/D11522
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenkernel/BKE_font.h9
-rw-r--r--source/blender/blenkernel/BKE_node.h1
-rw-r--r--source/blender/blenkernel/intern/font.c20
-rw-r--r--source/blender/blenkernel/intern/node.cc1
-rw-r--r--source/blender/blenlib/BLI_float4x4.hh7
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc15
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.h2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc15
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.h2
-rw-r--r--source/blender/makesdna/DNA_node_types.h32
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c114
-rw-r--r--source/blender/nodes/CMakeLists.txt1
-rw-r--r--source/blender/nodes/NOD_geometry.h1
-rw-r--r--source/blender/nodes/NOD_static_types.h1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc307
15 files changed, 518 insertions, 10 deletions
diff --git a/source/blender/blenkernel/BKE_font.h b/source/blender/blenkernel/BKE_font.h
index 522d3843bb2..827ae1b6a0f 100644
--- a/source/blender/blenkernel/BKE_font.h
+++ b/source/blender/blenkernel/BKE_font.h
@@ -85,6 +85,15 @@ bool BKE_vfont_to_curve_ex(struct Object *ob,
struct CharTrans **r_chartransdata);
bool BKE_vfont_to_curve_nubase(struct Object *ob, int mode, struct ListBase *r_nubase);
bool BKE_vfont_to_curve(struct Object *ob, int mode);
+void BKE_vfont_build_char(struct Curve *cu,
+ struct ListBase *nubase,
+ unsigned int character,
+ struct CharInfo *info,
+ float ofsx,
+ float ofsy,
+ float rot,
+ int charidx,
+ const float fsize);
int BKE_vfont_select_get(struct Object *ob, int *r_start, int *r_end);
void BKE_vfont_select_clamp(struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index bd969400f17..90285d9fc7c 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -1501,6 +1501,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_CURVE_PARAMETER 1088
#define GEO_NODE_CURVE_FILLET 1089
#define GEO_NODE_DISTRIBUTE_POINTS_ON_FACES 1090
+#define GEO_NODE_STRING_TO_CURVES 1091
/** \} */
diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c
index aa13f86523a..1053b727cbc 100644
--- a/source/blender/blenkernel/intern/font.c
+++ b/source/blender/blenkernel/intern/font.c
@@ -490,15 +490,15 @@ static void build_underline(Curve *cu,
mul_v2_fl(bp[3].vec, font_size);
}
-static void buildchar(Curve *cu,
- ListBase *nubase,
- unsigned int character,
- CharInfo *info,
- float ofsx,
- float ofsy,
- float rot,
- int charidx,
- const float fsize)
+void BKE_vfont_build_char(Curve *cu,
+ ListBase *nubase,
+ unsigned int character,
+ CharInfo *info,
+ float ofsx,
+ float ofsy,
+ float rot,
+ int charidx,
+ const float fsize)
{
VFontData *vfd = vfont_get_data(which_vfont(cu, info));
if (!vfd) {
@@ -1525,7 +1525,7 @@ static bool vfont_to_curve(Object *ob,
}
/* We do not want to see any character for \n or \r */
if (cha != '\n') {
- buildchar(cu, r_nubase, cha, info, ct->xof, ct->yof, ct->rot, i, font_size);
+ BKE_vfont_build_char(cu, r_nubase, cha, info, ct->xof, ct->yof, ct->rot, i, font_size);
}
if ((info->flag & CU_CHINFO_UNDERLINE) && (cha != '\n')) {
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index 891d6c05a19..bf7c47e9cf0 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -5799,6 +5799,7 @@ static void registerGeometryNodes()
register_node_type_geo_material_selection();
register_node_type_geo_separate_components();
register_node_type_geo_set_position();
+ register_node_type_geo_string_to_curves();
register_node_type_geo_subdivision_surface();
register_node_type_geo_switch();
register_node_type_geo_transform();
diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh
index 347ce2caa34..14e61d53845 100644
--- a/source/blender/blenlib/BLI_float4x4.hh
+++ b/source/blender/blenlib/BLI_float4x4.hh
@@ -45,6 +45,13 @@ struct float4x4 {
return mat;
}
+ static float4x4 from_location(const float3 location)
+ {
+ float4x4 mat = float4x4::identity();
+ copy_v3_v3(mat.values[3], location);
+ return mat;
+ }
+
static float4x4 from_normalized_axis_data(const float3 location,
const float3 forward,
const float3 up)
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index 36c6b56caae..463bb02afa4 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -63,6 +63,7 @@
#include "DNA_sound_types.h"
#include "DNA_speaker_types.h"
#include "DNA_texture_types.h"
+#include "DNA_vfont_types.h"
#include "DNA_world_types.h"
#include "BKE_action.h"
@@ -1764,6 +1765,9 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
else if (id_type == ID_MC) {
build_movieclip((MovieClip *)id);
}
+ else if (id_type == ID_VF) {
+ build_vfont((VFont *)id);
+ }
else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
bNodeTree *group_ntree = (bNodeTree *)id;
build_nodetree(group_ntree);
@@ -2015,6 +2019,17 @@ void DepsgraphNodeBuilder::build_simulation(Simulation *simulation)
});
}
+void DepsgraphNodeBuilder::build_vfont(VFont *vfont)
+{
+ if (built_map_.checkIsBuiltAndTag(vfont)) {
+ return;
+ }
+ build_parameters(&vfont->id);
+ build_idproperties(vfont->id.properties);
+ add_operation_node(
+ &vfont->id, NodeType::GENERIC_DATABLOCK, OperationCode::GENERIC_DATABLOCK_UPDATE);
+}
+
static bool seq_node_build_cb(Sequence *seq, void *user_data)
{
DepsgraphNodeBuilder *nb = (DepsgraphNodeBuilder *)user_data;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
index 2378f3fc100..d31290ecbff 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
@@ -55,6 +55,7 @@ struct Scene;
struct Simulation;
struct Speaker;
struct Tex;
+struct VFont;
struct World;
struct bAction;
struct bArmature;
@@ -235,6 +236,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
virtual void build_scene_sequencer(Scene *scene);
virtual void build_scene_audio(Scene *scene);
virtual void build_scene_speakers(Scene *scene, ViewLayer *view_layer);
+ virtual void build_vfont(VFont *vfont);
/* Per-ID information about what was already in the dependency graph.
* Allows to re-use certain values, to speed up following evaluation. */
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 28cfc5a9e1a..55e8c5ed033 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -65,6 +65,7 @@
#include "DNA_sound_types.h"
#include "DNA_speaker_types.h"
#include "DNA_texture_types.h"
+#include "DNA_vfont_types.h"
#include "DNA_volume_types.h"
#include "DNA_world_types.h"
@@ -2496,6 +2497,11 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
OperationKey clip_key(id, NodeType::PARAMETERS, OperationCode::MOVIECLIP_EVAL);
add_relation(clip_key, shading_key, "Clip -> Node");
}
+ else if (id_type == ID_VF) {
+ build_vfont((VFont *)id);
+ ComponentKey vfont_key(id, NodeType::GENERIC_DATABLOCK);
+ add_relation(vfont_key, shading_key, "VFont -> Node");
+ }
else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
bNodeTree *group_ntree = (bNodeTree *)id;
build_nodetree(group_ntree);
@@ -2842,6 +2848,15 @@ void DepsgraphRelationBuilder::build_scene_speakers(Scene * /*scene*/, ViewLayer
}
}
+void DepsgraphRelationBuilder::build_vfont(VFont *vfont)
+{
+ if (built_map_.checkIsBuiltAndTag(vfont)) {
+ return;
+ }
+ build_parameters(&vfont->id);
+ build_idproperties(vfont->id.properties);
+}
+
void DepsgraphRelationBuilder::build_copy_on_write_relations()
{
for (IDNode *id_node : graph_->id_nodes) {
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
index 1ad61c25305..f0393544511 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
@@ -72,6 +72,7 @@ struct Simulation;
struct Speaker;
struct Tex;
struct ViewLayer;
+struct VFont;
struct World;
struct bAction;
struct bArmature;
@@ -296,6 +297,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
virtual void build_scene_sequencer(Scene *scene);
virtual void build_scene_audio(Scene *scene);
virtual void build_scene_speakers(Scene *scene, ViewLayer *view_layer);
+ virtual void build_vfont(VFont *vfont);
virtual void build_nested_datablock(ID *owner, ID *id);
virtual void build_nested_nodetree(ID *owner, bNodeTree *ntree);
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 6e4737b7d07..35a2dba627a 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -1502,6 +1502,16 @@ typedef struct NodeGeometryAttributeCapture {
int8_t domain;
} NodeGeometryAttributeCapture;
+typedef struct NodeGeometryStringToCurves {
+ /* GeometryNodeStringToCurvesOverflowMode */
+ uint8_t overflow;
+ /* GeometryNodeStringToCurvesAlignXMode */
+ uint8_t align_x;
+ /* GeometryNodeStringToCurvesAlignYMode */
+ uint8_t align_y;
+ char _pad[1];
+} NodeGeometryStringToCurves;
+
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1
@@ -2098,6 +2108,28 @@ typedef enum GeometryNodeCurveFillMode {
GEO_NODE_CURVE_FILL_MODE_NGONS = 1,
} GeometryNodeCurveFillMode;
+typedef enum GeometryNodeStringToCurvesOverflowMode {
+ GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW = 0,
+ GEO_NODE_STRING_TO_CURVES_MODE_SCALE_TO_FIT = 1,
+ GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE = 2,
+} GeometryNodeStringToCurvesOverflowMode;
+
+typedef enum GeometryNodeStringToCurvesAlignXMode {
+ GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT = 0,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_X_CENTER = 1,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_X_RIGHT = 2,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_X_JUSTIFY = 3,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_X_FLUSH = 4,
+} GeometryNodeStringToCurvesAlignXMode;
+
+typedef enum GeometryNodeStringToCurvesAlignYMode {
+ GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE = 0,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP = 1,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_Y_MIDDLE = 2,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM_BASELINE = 3,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM = 4,
+} GeometryNodeStringToCurvesAlignYMode;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index dcac157d1a2..333f1584800 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -10425,6 +10425,120 @@ static void def_geo_attribute_capture(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_geo_string_to_curves(StructRNA *srna)
+{
+ static const EnumPropertyItem rna_node_geometry_string_to_curves_overflow_items[] = {
+ {GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW,
+ "OVERFLOW",
+ ICON_NONE,
+ "Overflow",
+ "Let the text use more space than the specified height"},
+ {GEO_NODE_STRING_TO_CURVES_MODE_SCALE_TO_FIT,
+ "SCALE_TO_FIT",
+ ICON_NONE,
+ "Scale To Fit",
+ "Scale the text size to fit inside the width and height"},
+ {GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE,
+ "TRUNCATE",
+ ICON_NONE,
+ "Truncate",
+ "Only output curves that fit within the width and height. Output the remainder to the "
+ "\"Remainder\" output"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem rna_node_geometry_string_to_curves_align_x_items[] = {
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT,
+ "LEFT",
+ ICON_ALIGN_LEFT,
+ "Left",
+ "Align text to the left"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_X_CENTER,
+ "CENTER",
+ ICON_ALIGN_CENTER,
+ "Center",
+ "Align text to the center"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_X_RIGHT,
+ "RIGHT",
+ ICON_ALIGN_RIGHT,
+ "Right",
+ "Align text to the right"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_X_JUSTIFY,
+ "JUSTIFY",
+ ICON_ALIGN_JUSTIFY,
+ "Justify",
+ "Align text to the left and the right"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_X_FLUSH,
+ "FLUSH",
+ ICON_ALIGN_FLUSH,
+ "Flush",
+ "Align text to the left and the right, with equal character spacing"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem rna_node_geometry_string_to_curves_align_y_items[] = {
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE,
+ "TOP_BASELINE",
+ ICON_ALIGN_TOP,
+ "Top Baseline",
+ "Align text to the top baseline"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP,
+ "TOP",
+ ICON_ALIGN_TOP,
+ "Top",
+ "Align text to the top"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_MIDDLE,
+ "MIDDLE",
+ ICON_ALIGN_MIDDLE,
+ "Middle",
+ "Align text to the middle"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM_BASELINE,
+ "BOTTOM_BASELINE",
+ ICON_ALIGN_BOTTOM,
+ "Bottom Baseline",
+ "Align text to the bottom baseline"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM,
+ "BOTTOM",
+ ICON_ALIGN_BOTTOM,
+ "Bottom",
+ "Align text to the bottom"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "font", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "id");
+ RNA_def_property_struct_type(prop, "VectorFont");
+ RNA_def_property_ui_text(prop, "Font", "Font of the text. Falls back to the UI font by default");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryStringToCurves", "storage");
+
+ prop = RNA_def_property(srna, "overflow", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "overflow");
+ RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_overflow_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW);
+ RNA_def_property_ui_text(prop, "Overflow", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "align_x", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "align_x");
+ RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_align_x_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT);
+ RNA_def_property_ui_text(prop, "Align X", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "align_y", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "align_y");
+ RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_align_y_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE);
+ RNA_def_property_ui_text(prop, "Align Y", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
/* -------------------------------------------------------------------------- */
static void rna_def_shader_node(BlenderRNA *brna)
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index f8898fbda93..5056c0889bc 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -232,6 +232,7 @@ set(SRC
geometry/nodes/node_geo_separate_components.cc
geometry/nodes/node_geo_set_position.cc
geometry/nodes/node_geo_string_join.cc
+ geometry/nodes/node_geo_string_to_curves.cc
geometry/nodes/node_geo_subdivision_surface.cc
geometry/nodes/node_geo_switch.cc
geometry/nodes/node_geo_transform.cc
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 9cd3365bd91..823c48f71e3 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -115,6 +115,7 @@ void register_node_type_geo_select_by_handle_type(void);
void register_node_type_geo_separate_components(void);
void register_node_type_geo_set_position(void);
void register_node_type_geo_string_join(void);
+void register_node_type_geo_string_to_curves(void);
void register_node_type_geo_subdivision_surface(void);
void register_node_type_geo_switch(void);
void register_node_type_geo_transform(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 170d615b43f..6d71cb8d0b6 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -359,6 +359,7 @@ DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", Realiz
DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "")
DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "")
DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "String Join", "")
+DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "")
DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
new file mode 100644
index 00000000000..0bd050ac3d1
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
@@ -0,0 +1,307 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <codecvt>
+#include <locale>
+
+#include "DNA_curve_types.h"
+#include "DNA_vfont_types.h"
+
+#include "BKE_curve.h"
+#include "BKE_font.h"
+#include "BKE_spline.hh"
+
+#include "BLI_hash.h"
+#include "BLI_string_utf8.h"
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_string_to_curves_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::String>("String");
+ b.add_input<decl::Float>("Size").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Character Spacing")
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Word Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Line Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Text Box Width").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Text Box Height").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>("Curves");
+ b.add_output<decl::String>("Remainder");
+}
+
+static void geo_node_string_to_curves_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "font",
+ nullptr,
+ "FONT_OT_open",
+ "FONT_OT_unlink",
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
+ uiItemR(layout, ptr, "overflow", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "align_x", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "align_y", 0, "", ICON_NONE);
+}
+
+static void geo_node_string_to_curves_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryStringToCurves *data = (NodeGeometryStringToCurves *)MEM_callocN(
+ sizeof(NodeGeometryStringToCurves), __func__);
+
+ data->overflow = GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW;
+ data->align_x = GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT;
+ data->align_y = GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE;
+ node->storage = data;
+ node->id = (ID *)BKE_vfont_builtin_get();
+}
+
+static void geo_node_string_to_curves_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeGeometryStringToCurves *storage = (const NodeGeometryStringToCurves *)node->storage;
+ const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode)
+ storage->overflow;
+ bNodeSocket *socket_remainder = ((bNodeSocket *)node->outputs.first)->next;
+ nodeSetSocketAvailability(socket_remainder, overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE);
+
+ bNodeSocket *height_socket = (bNodeSocket *)node->inputs.last;
+ bNodeSocket *width_socket = height_socket->prev;
+ nodeSetSocketAvailability(height_socket, overflow != GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW);
+ node_sock_label(width_socket,
+ overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ? N_("Max Width") :
+ N_("Text Box Width"));
+}
+
+struct TextLayout {
+ /* Position of each character. */
+ Vector<float2> positions;
+
+ /* The text that fit into the text box, with newline character sequences replaced. */
+ std::string text;
+
+ /* The text that didn't fit into the text box in 'Truncate' mode. May be empty. */
+ std::string truncated_text;
+
+ /* Font size could be modified if in 'Scale to fit'-mode. */
+ float final_font_size;
+};
+
+static TextLayout get_text_layout(GeoNodeExecParams &params)
+{
+ TextLayout layout;
+ layout.text = params.extract_input<std::string>("String");
+ if (layout.text.empty()) {
+ return {};
+ }
+
+ const NodeGeometryStringToCurves &storage =
+ *(const NodeGeometryStringToCurves *)params.node().storage;
+ const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode)
+ storage.overflow;
+ const GeometryNodeStringToCurvesAlignXMode align_x = (GeometryNodeStringToCurvesAlignXMode)
+ storage.align_x;
+ const GeometryNodeStringToCurvesAlignYMode align_y = (GeometryNodeStringToCurvesAlignYMode)
+ storage.align_y;
+
+ const float font_size = std::max(params.extract_input<float>("Size"), 0.0f);
+ const float char_spacing = params.extract_input<float>("Character Spacing");
+ const float word_spacing = params.extract_input<float>("Word Spacing");
+ const float line_spacing = params.extract_input<float>("Line Spacing");
+ const float textbox_w = params.extract_input<float>("Text Box Width");
+ const float textbox_h = overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ?
+ 0.0f :
+ params.extract_input<float>("Text Box Height");
+ VFont *vfont = (VFont *)params.node().id;
+
+ Curve cu = {nullptr};
+ cu.type = OB_FONT;
+ /* Set defaults */
+ cu.resolu = 12;
+ cu.smallcaps_scale = 0.75f;
+ cu.wordspace = 1.0f;
+ /* Set values from inputs */
+ cu.spacemode = align_x;
+ cu.align_y = align_y;
+ cu.fsize = font_size;
+ cu.spacing = char_spacing;
+ cu.wordspace = word_spacing;
+ cu.linedist = line_spacing;
+ cu.vfont = vfont;
+ cu.overflow = overflow;
+ cu.tb = (TextBox *)MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), __func__);
+ cu.tb->w = textbox_w;
+ cu.tb->h = textbox_h;
+ cu.totbox = 1;
+ size_t len_bytes;
+ size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes);
+ cu.len_char32 = len_chars;
+ cu.len = len_bytes;
+ cu.pos = len_chars;
+ /* The reason for the additional character here is unknown, but reflects other code elsewhere. */
+ cu.str = (char *)MEM_mallocN(len_bytes + sizeof(char32_t), __func__);
+ cu.strinfo = (CharInfo *)MEM_callocN((len_chars + 1) * sizeof(CharInfo), __func__);
+ BLI_strncpy(cu.str, layout.text.c_str(), len_bytes + 1);
+
+ struct CharTrans *chartransdata = nullptr;
+ int text_len;
+ bool text_free;
+ const char32_t *r_text = nullptr;
+ /* Mode FO_DUPLI used because it doesn't create curve splines. */
+ BKE_vfont_to_curve_ex(
+ nullptr, &cu, FO_DUPLI, nullptr, &r_text, &text_len, &text_free, &chartransdata);
+
+ if (text_free) {
+ MEM_freeN((void *)r_text);
+ }
+
+ Span<CharInfo> info{cu.strinfo, text_len};
+ layout.final_font_size = cu.fsize_realtime;
+ layout.positions.reserve(text_len);
+
+ for (const int i : IndexRange(text_len)) {
+ CharTrans &ct = chartransdata[i];
+ layout.positions.append(float2(ct.xof, ct.yof) * layout.final_font_size);
+
+ if ((info[i].flag & CU_CHINFO_OVERFLOW) && (cu.overflow == CU_OVERFLOW_TRUNCATE)) {
+ const int offset = BLI_str_utf8_offset_from_index(layout.text.c_str(), i + 1);
+ layout.truncated_text = layout.text.substr(offset);
+ layout.text = layout.text.substr(0, offset);
+ break;
+ }
+ }
+
+ MEM_SAFE_FREE(chartransdata);
+ MEM_SAFE_FREE(cu.str);
+ MEM_SAFE_FREE(cu.strinfo);
+ MEM_SAFE_FREE(cu.tb);
+
+ return layout;
+}
+
+/* Returns a mapping of UTF-32 character code to instance handle. */
+static Map<int, int> create_curve_instances(GeoNodeExecParams &params,
+ const float fontsize,
+ const std::u32string &charcodes,
+ InstancesComponent &instance_component)
+{
+ VFont *vfont = (VFont *)params.node().id;
+ Map<int, int> handles;
+
+ for (int i : IndexRange(charcodes.length())) {
+ if (handles.contains(charcodes[i])) {
+ continue;
+ }
+ Curve cu = {nullptr};
+ cu.type = OB_FONT;
+ cu.resolu = 12;
+ cu.vfont = vfont;
+ CharInfo charinfo = {0};
+ charinfo.mat_nr = 1;
+
+ BKE_vfont_build_char(&cu, &cu.nurb, charcodes[i], &charinfo, 0, 0, 0, i, 1);
+ std::unique_ptr<CurveEval> curve_eval = curve_eval_from_dna_curve(cu);
+ BKE_nurbList_free(&cu.nurb);
+ float4x4 size_matrix = float4x4::identity();
+ size_matrix.apply_scale(fontsize);
+ curve_eval->transform(size_matrix);
+
+ GeometrySet geometry_set_curve = GeometrySet::create_with_curve(curve_eval.release());
+ handles.add_new(charcodes[i], instance_component.add_reference(std::move(geometry_set_curve)));
+ }
+ return handles;
+}
+
+static void add_instances_from_handles(InstancesComponent &instances,
+ const Map<int, int> &char_handles,
+ const std::u32string &charcodes,
+ const Span<float2> positions)
+{
+ instances.resize(positions.size());
+ MutableSpan<int> handles = instances.instance_reference_handles();
+ MutableSpan<float4x4> transforms = instances.instance_transforms();
+ MutableSpan<int> instance_ids = instances.instance_ids();
+
+ threading::parallel_for(IndexRange(positions.size()), 256, [&](IndexRange range) {
+ for (const int i : range) {
+ handles[i] = char_handles.lookup(charcodes[i]);
+ transforms[i] = float4x4::from_location({positions[i].x, positions[i].y, 0});
+ instance_ids[i] = i;
+ }
+ });
+}
+
+static void geo_node_string_to_curves_exec(GeoNodeExecParams params)
+{
+ TextLayout layout = get_text_layout(params);
+
+ const NodeGeometryStringToCurves &storage =
+ *(const NodeGeometryStringToCurves *)params.node().storage;
+ if (storage.overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE) {
+ params.set_output("Remainder", std::move(layout.truncated_text));
+ }
+
+ if (layout.positions.size() == 0) {
+ params.set_output("Curves", GeometrySet());
+ return;
+ }
+
+ /* Convert UTF-8 encoded string to UTF-32. */
+ std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter;
+ std::u32string utf32_text = converter.from_bytes(layout.text);
+
+ /* Create and add instances. */
+ GeometrySet geometry_set_out;
+ InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
+ Map<int, int> char_handles = create_curve_instances(
+ params, layout.final_font_size, utf32_text, instances);
+ add_instances_from_handles(instances, char_handles, utf32_text, layout.positions);
+
+ params.set_output("Curves", std::move(geometry_set_out));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_string_to_curves()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_STRING_TO_CURVES, "String to Curves", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_string_to_curves_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_string_to_curves_exec;
+ node_type_init(&ntype, blender::nodes::geo_node_string_to_curves_init);
+ node_type_update(&ntype, blender::nodes::geo_node_string_to_curves_update);
+ node_type_size(&ntype, 190, 120, 700);
+ node_type_storage(&ntype,
+ "NodeGeometryStringToCurves",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.draw_buttons = blender::nodes::geo_node_string_to_curves_layout;
+ nodeRegisterType(&ntype);
+}