Python: Add to_curve method to the object API

This patch adds a to_curve method to the Object ID. This method is
analogous to the to_mesh method. The method can operate on curve and
text objects. For text objects, the text is converted into a 3D Curve ID
and that curve is returned. For curve objects, if apply_modifiers is
true, the spline deform modifiers will be applied and a Curve ID with
the result will be returned, otherwise a copy of the curve will be
returned.

The goal of this addition is to allow the developer to access the splines
of text objects and to get the result of modifier applications which was
otherwise not possible.

Reviewed By: Brecht

Differential Revision: https://developer.blender.org/D10354
This commit is contained in:
Omar Emara 2021-02-20 18:05:13 +02:00
parent 5dced2a063
commit f2c0bbed1c
10 changed files with 270 additions and 10 deletions

View File

@ -0,0 +1,64 @@
"""
Dependency graph: Object.to_curve()
+++++++++++++++++++++++++++++++++++
Function to get a curve from text and curve objects. It is typically used by exporters, render
engines, and tools that need to access the curve representing the object.
The function takes the evaluated dependency graph as a required parameter and optionally a boolean
apply_modifiers which defaults to false. If apply_modifiers is true and the object is a curve object,
the spline deform modifiers are applied on the control points. Note that constructive modifiers and
modifiers that are not spline-enabled will not be applied. So modifiers like Array will not be applied
and deform modifiers that have Apply On Spline disabled will not be applied.
If the object is a text object. The text will be converted into a 3D curve and returned. Modifiers are
never applied on text objects and apply_modifiers will be ignored. If the object is neither a curve nor
a text object, an error will be reported.
.. note:: The resulting curve is owned by the object. It can be freed by calling `object.to_curve_clear()`.
.. note::
The resulting curve must be treated as temporary, and can not be referenced from objects in the main
database.
"""
import bpy
class OBJECT_OT_object_to_curve(bpy.types.Operator):
"""Convert selected object to curve and show number of splines"""
bl_label = "DEG Object to Curve"
bl_idname = "object.object_to_curve"
def execute(self, context):
# Access input original object.
obj = context.object
if obj is None:
self.report({'INFO'}, "No active object to convert to curve")
return {'CANCELLED'}
if obj.type not in {'CURVE', 'FONT'}:
self.report({'INFO'}, "Object can not be converted to curve")
return {'CANCELLED'}
depsgraph = context.evaluated_depsgraph_get()
# Invoke to_curve() without applying modifiers.
curve_without_modifiers = obj.to_curve(depsgraph)
self.report({'INFO'}, f"{len(curve_without_modifiers.splines)} splines in a new curve without modifiers.")
# Remove temporary curve.
obj.to_curve_clear()
# Invoke to_curve() with applying modifiers.
curve_with_modifiers = obj.to_curve(depsgraph, apply_modifiers = True)
self.report({'INFO'}, f"{len(curve_with_modifiers.splines)} splines in new curve with modifiers.")
# Remove temporary curve.
obj.to_curve_clear()
return {'FINISHED'}
def register():
bpy.utils.register_class(OBJECT_OT_object_to_curve)
def unregister():
bpy.utils.unregister_class(OBJECT_OT_object_to_curve)
if __name__ == "__main__":
register()

View File

@ -338,6 +338,18 @@ void BKE_curve_deform_co(const struct Object *ob_curve,
/** \} */
/* curve_convert.c */
/* Create a new curve from the given object at its current state. This only works for curve and
* text objects, otherwise NULL is returned.
*
* If apply_modifiers is true and the object is a curve one, then spline deform modifiers are
* applied on the control points of the splines.
*/
struct Curve *BKE_curve_new_from_object(struct Object *object,
struct Depsgraph *depsgraph,
bool apply_modifiers);
#ifdef __cplusplus
}
#endif

View File

@ -112,6 +112,13 @@ void BKE_displist_make_mball_forRender(struct Depsgraph *depsgraph,
struct Object *ob,
struct ListBase *dispbase);
bool BKE_curve_calc_modifiers_pre(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Object *ob,
struct ListBase *source_nurb,
struct ListBase *target_nurb,
const bool for_render);
bool BKE_displist_surfindex_get(DispList *dl, int a, int *b, int *p1, int *p2, int *p3, int *p4);
void BKE_displist_fill(const struct ListBase *dispbase,
struct ListBase *to,

View File

@ -32,6 +32,7 @@ extern "C" {
struct Base;
struct BoundBox;
struct Curve;
struct Depsgraph;
struct GpencilModifierData;
struct HookGpencilModifierData;
@ -424,6 +425,21 @@ struct Mesh *BKE_object_to_mesh(struct Depsgraph *depsgraph,
void BKE_object_to_mesh_clear(struct Object *object);
/* This is an utility function for Python's object.to_curve().
* The result is owned by the object.
*
* The curve will be freed when object is re-evaluated or is destroyed. It is possible to force
* clear memory used by this curve by calling BKE_object_to_curve_clear().
*
* If apply_modifiers is true and the object is a curve one, then spline deform modifiers are
* applied on the curve control points.
*/
struct Curve *BKE_object_to_curve(struct Object *object,
struct Depsgraph *depsgraph,
bool apply_modifiers);
void BKE_object_to_curve_clear(struct Object *object);
void BKE_object_check_uuids_unique_and_report(const struct Object *object);
void BKE_object_modifiers_lib_link_common(void *userData,

View File

@ -107,6 +107,7 @@ set(SRC
intern/cryptomatte.cc
intern/curve.c
intern/curve_bevel.c
intern/curve_convert.c
intern/curve_decimate.c
intern/curve_deform.c
intern/curveprofile.c

View File

@ -0,0 +1,81 @@
/*
* 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.
*/
/** \file
* \ingroup bke
*/
#include "DNA_curve_types.h"
#include "DNA_object_types.h"
#include "DNA_vfont_types.h"
#include "BLI_utildefines.h"
#include "BKE_curve.h"
#include "BKE_displist.h"
#include "BKE_font.h"
#include "BKE_lib_id.h"
#include "BKE_modifier.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
static Curve *curve_from_font_object(Object *object, Depsgraph *depsgraph)
{
Curve *curve = (Curve *)object->data;
Curve *new_curve = (Curve *)BKE_id_copy_ex(NULL, &curve->id, NULL, LIB_ID_COPY_LOCALIZE);
Object *evaluated_object = DEG_get_evaluated_object(depsgraph, object);
BKE_vfont_to_curve_nubase(evaluated_object, FO_EDIT, &new_curve->nurb);
new_curve->type = OB_CURVE;
new_curve->flag &= ~CU_3D;
BKE_curve_curve_dimension_update(new_curve);
return new_curve;
}
static Curve *curve_from_curve_object(Object *object, Depsgraph *depsgraph, bool apply_modifiers)
{
Object *evaluated_object = DEG_get_evaluated_object(depsgraph, object);
Curve *curve = (Curve *)evaluated_object->data;
Curve *new_curve = (Curve *)BKE_id_copy_ex(NULL, &curve->id, NULL, LIB_ID_COPY_LOCALIZE);
if (apply_modifiers) {
BKE_curve_calc_modifiers_pre(depsgraph,
DEG_get_input_scene(depsgraph),
evaluated_object,
BKE_curve_nurbs_get(curve),
&new_curve->nurb,
DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
}
return new_curve;
}
Curve *BKE_curve_new_from_object(Object *object, Depsgraph *depsgraph, bool apply_modifiers)
{
if (!ELEM(object->type, OB_FONT, OB_CURVE)) {
return NULL;
}
if (object->type == OB_FONT) {
return curve_from_font_object(object, depsgraph);
}
return curve_from_curve_object(object, depsgraph, apply_modifiers);
}

View File

@ -770,8 +770,12 @@ static ModifierData *curve_get_tessellate_point(Scene *scene,
}
/* Return true if any modifier was applied. */
static bool curve_calc_modifiers_pre(
Depsgraph *depsgraph, Scene *scene, Object *ob, ListBase *nurb, const bool for_render)
bool BKE_curve_calc_modifiers_pre(Depsgraph *depsgraph,
Scene *scene,
Object *ob,
ListBase *source_nurb,
ListBase *target_nurb,
const bool for_render)
{
VirtualModifierData virtualModifierData;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
@ -810,13 +814,13 @@ static bool curve_calc_modifiers_pre(
keyVerts = BKE_key_evaluate_object(ob, &numElems);
if (keyVerts) {
BLI_assert(BKE_keyblock_curve_element_count(nurb) == numElems);
BLI_assert(BKE_keyblock_curve_element_count(source_nurb) == numElems);
/* split coords from key data, the latter also includes
* tilts, which is passed through in the modifier stack.
* this is also the reason curves do not use a virtual
* shape key modifier yet. */
deformedVerts = BKE_curve_nurbs_key_vert_coords_alloc(nurb, keyVerts, &numVerts);
deformedVerts = BKE_curve_nurbs_key_vert_coords_alloc(source_nurb, keyVerts, &numVerts);
}
}
@ -832,7 +836,7 @@ static bool curve_calc_modifiers_pre(
}
if (!deformedVerts) {
deformedVerts = BKE_curve_nurbs_vert_coords_alloc(nurb, &numVerts);
deformedVerts = BKE_curve_nurbs_vert_coords_alloc(source_nurb, &numVerts);
}
mti->deformVerts(md, &mectx, NULL, deformedVerts, numVerts);
@ -845,11 +849,11 @@ static bool curve_calc_modifiers_pre(
}
if (deformedVerts) {
BKE_curve_nurbs_vert_coords_apply(nurb, deformedVerts, false);
BKE_curve_nurbs_vert_coords_apply(target_nurb, deformedVerts, false);
MEM_freeN(deformedVerts);
}
if (keyVerts) { /* these are not passed through modifier stack */
BKE_curve_nurbs_key_vert_tilts_apply(nurb, keyVerts);
BKE_curve_nurbs_key_vert_tilts_apply(target_nurb, keyVerts);
}
if (keyVerts) {
@ -1151,7 +1155,8 @@ void BKE_displist_make_surf(Depsgraph *depsgraph,
}
if (!for_orco) {
force_mesh_conversion = curve_calc_modifiers_pre(depsgraph, scene, ob, &nubase, for_render);
force_mesh_conversion = BKE_curve_calc_modifiers_pre(
depsgraph, scene, ob, &nubase, &nubase, for_render);
}
LISTBASE_FOREACH (Nurb *, nu, &nubase) {
@ -1501,7 +1506,8 @@ static void do_makeDispListCurveTypes(Depsgraph *depsgraph,
}
if (!for_orco) {
force_mesh_conversion = curve_calc_modifiers_pre(depsgraph, scene, ob, &nubase, for_render);
force_mesh_conversion = BKE_curve_calc_modifiers_pre(
depsgraph, scene, ob, &nubase, &nubase, for_render);
}
BKE_curve_bevelList_make(ob, &nubase, for_render);

View File

@ -1742,6 +1742,7 @@ void BKE_object_free_derived_caches(Object *ob)
}
BKE_object_to_mesh_clear(ob);
BKE_object_to_curve_clear(ob);
BKE_object_free_curve_cache(ob);
/* Clear grease pencil data. */
@ -5057,6 +5058,7 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag))
runtime->mesh_deform_eval = NULL;
runtime->curve_cache = NULL;
runtime->object_as_temp_mesh = NULL;
runtime->object_as_temp_curve = NULL;
runtime->geometry_set_eval = NULL;
}
@ -5615,6 +5617,24 @@ void BKE_object_to_mesh_clear(Object *object)
object->runtime.object_as_temp_mesh = NULL;
}
Curve *BKE_object_to_curve(Object *object, Depsgraph *depsgraph, bool apply_modifiers)
{
BKE_object_to_curve_clear(object);
Curve *curve = BKE_curve_new_from_object(object, depsgraph, apply_modifiers);
object->runtime.object_as_temp_curve = curve;
return curve;
}
void BKE_object_to_curve_clear(Object *object)
{
if (object->runtime.object_as_temp_curve == NULL) {
return;
}
BKE_id_free(NULL, object->runtime.object_as_temp_curve);
object->runtime.object_as_temp_curve = NULL;
}
void BKE_object_check_uuids_unique_and_report(const Object *object)
{
BKE_pose_check_uuids_unique_and_report(object->pose);

View File

@ -38,6 +38,7 @@ extern "C" {
struct AnimData;
struct BoundBox;
struct Curve;
struct FluidsimSettings;
struct GeometrySet;
struct Ipo;
@ -186,6 +187,12 @@ typedef struct Object_Runtime {
*/
struct Mesh *object_as_temp_mesh;
/**
* This is a curve representation of corresponding object.
* It created when Python calls `object.to_curve()`.
*/
struct Curve *object_as_temp_curve;
/** Runtime evaluated curve-specific data, not stored in the file. */
struct CurveCache *curve_cache;
@ -623,7 +630,7 @@ enum {
*/
#define BA_TRANSFORM_LOCKED_IN_PLACE (1 << 7)
#define BA_TRANSFORM_CHILD (1 << 8) /* child of a transformed object */
#define BA_TRANSFORM_CHILD (1 << 8) /* child of a transformed object */
#define BA_TRANSFORM_PARENT (1 << 13) /* parent of a transformed object */
#define OB_FROMDUPLI (1 << 9)

View File

@ -404,6 +404,29 @@ static void rna_Object_to_mesh_clear(Object *object)
BKE_object_to_mesh_clear(object);
}
static Curve *rna_Object_to_curve(Object *object,
ReportList *reports,
Depsgraph *depsgraph,
bool apply_modifiers)
{
if (!ELEM(object->type, OB_FONT, OB_CURVE)) {
BKE_report(reports, RPT_ERROR, "Object is not a curve or a text");
return NULL;
}
if (depsgraph == NULL) {
BKE_report(reports, RPT_ERROR, "Invalid depsgraph");
return NULL;
}
return BKE_object_to_curve(object, depsgraph, apply_modifiers);
}
static void rna_Object_to_curve_clear(Object *object)
{
BKE_object_to_curve_clear(object);
}
static PointerRNA rna_Object_shape_key_add(
Object *ob, bContext *C, ReportList *reports, const char *name, bool from_mix)
{
@ -977,6 +1000,29 @@ void RNA_api_object(StructRNA *srna)
func = RNA_def_function(srna, "to_mesh_clear", "rna_Object_to_mesh_clear");
RNA_def_function_ui_description(func, "Clears mesh data-block created by to_mesh()");
/* curve */
func = RNA_def_function(srna, "to_curve", "rna_Object_to_curve");
RNA_def_function_ui_description(
func,
"Create a Curve data-block from the current state of the object. This only works for curve "
"and text objects. The object owns the data-block. To force free it, use to_curve_clear(). "
"The result is temporary and can not be used by objects from the main database");
RNA_def_function_flag(func, FUNC_USE_REPORTS);
parm = RNA_def_pointer(
func, "depsgraph", "Depsgraph", "Dependency Graph", "Evaluated dependency graph");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
RNA_def_boolean(func,
"apply_modifiers",
false,
"",
"Apply the deform modifiers on the control points of the curve. This is only "
"supported for curve objects");
parm = RNA_def_pointer(func, "curve", "Curve", "", "Curve created from object");
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "to_curve_clear", "rna_Object_to_curve_clear");
RNA_def_function_ui_description(func, "Clears curve data-block created by to_curve()");
/* Armature */
func = RNA_def_function(srna, "find_armature", "BKE_modifiers_is_deformed_by_armature");
RNA_def_function_ui_description(