parent
35d864b9b8
commit
42c4a33801
3 changed files with 168 additions and 72 deletions
35
__init__.py
35
__init__.py
|
@ -383,10 +383,10 @@ class ABC3D_PT_FontList(bpy.types.Panel):
|
||||||
row.scale_y = scale_y
|
row.scale_y = scale_y
|
||||||
row.label(text=text)
|
row.label(text=text)
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
oper = row.operator(f"{__name__}.load_font",
|
oper_lf = row.operator(f"{__name__}.load_font",
|
||||||
text='Load all glyphs in memory')
|
text='Load all glyphs in memory')
|
||||||
oper.font_name = font_name
|
oper_lf.font_name = font_name
|
||||||
oper.face_name = face_name
|
oper_lf.face_name = face_name
|
||||||
|
|
||||||
|
|
||||||
class ABC3D_PT_TextPlacement(bpy.types.Panel):
|
class ABC3D_PT_TextPlacement(bpy.types.Panel):
|
||||||
|
@ -914,6 +914,7 @@ class ABC3D_OT_RemoveText(bpy.types.Operator):
|
||||||
def delif(o, p):
|
def delif(o, p):
|
||||||
if p in o:
|
if p in o:
|
||||||
del o[p]
|
del o[p]
|
||||||
|
delif(mom, f"{utils.prefix()}_type")
|
||||||
delif(mom, f"{utils.prefix()}_linked_textobject")
|
delif(mom, f"{utils.prefix()}_linked_textobject")
|
||||||
delif(mom, f"{utils.prefix()}_font_name")
|
delif(mom, f"{utils.prefix()}_font_name")
|
||||||
delif(mom, f"{utils.prefix()}_face_name")
|
delif(mom, f"{utils.prefix()}_face_name")
|
||||||
|
@ -1046,7 +1047,7 @@ class ABC3D_OT_PlaceText(bpy.types.Operator):
|
||||||
# t.text)
|
# t.text)
|
||||||
# or this:
|
# or this:
|
||||||
# butils.set_text_on_curve(t)
|
# butils.set_text_on_curve(t)
|
||||||
# else:
|
else:
|
||||||
butils.ShowMessageBox(
|
butils.ShowMessageBox(
|
||||||
title="No object selected",
|
title="No object selected",
|
||||||
message=(
|
message=(
|
||||||
|
@ -1372,7 +1373,7 @@ class ABC3D_OT_CreateFontFromObjects(bpy.types.Operator):
|
||||||
font_name,
|
font_name,
|
||||||
face_name,
|
face_name,
|
||||||
glyph_id,
|
glyph_id,
|
||||||
o)
|
bpy.types.PointerProperty(o))
|
||||||
|
|
||||||
# TODO: is there a better way to iterate over a CollectionProperty?
|
# TODO: is there a better way to iterate over a CollectionProperty?
|
||||||
found = False
|
found = False
|
||||||
|
@ -1484,7 +1485,6 @@ def detect_text():
|
||||||
|
|
||||||
|
|
||||||
def load_used_glyphs():
|
def load_used_glyphs():
|
||||||
print("LOAD USED GLYPHS")
|
|
||||||
scene = bpy.context.scene
|
scene = bpy.context.scene
|
||||||
abc3d_data = scene.abc3d_data
|
abc3d_data = scene.abc3d_data
|
||||||
for t in abc3d_data.available_texts:
|
for t in abc3d_data.available_texts:
|
||||||
|
@ -1528,19 +1528,33 @@ def on_frame_changed(self, dummy):
|
||||||
# TODO PERFORMANCE: only on demand
|
# TODO PERFORMANCE: only on demand
|
||||||
butils.set_text_on_curve(t)
|
butils.set_text_on_curve(t)
|
||||||
|
|
||||||
|
depsgraph_updates_locked = False
|
||||||
|
def unlock_depsgraph_updates():
|
||||||
|
global depsgraph_updates_locked
|
||||||
|
depsgraph_updates_locked = False
|
||||||
|
|
||||||
|
def lock_depsgraph_updates():
|
||||||
|
global depsgraph_updates_locked
|
||||||
|
depsgraph_updates_locked = True
|
||||||
|
if bpy.app.timers.is_registered(unlock_depsgraph_updates):
|
||||||
|
bpy.app.timers.unregister(unlock_depsgraph_updates)
|
||||||
|
bpy.app.timers.register(unlock_depsgraph_updates, first_interval=1)
|
||||||
|
|
||||||
|
import time
|
||||||
@persistent
|
@persistent
|
||||||
def on_depsgraph_update(scene, depsgraph):
|
def on_depsgraph_update(scene, depsgraph):
|
||||||
if not bpy.context.mode.startswith("EDIT"):
|
global depsgraph_updates_locked
|
||||||
|
if not bpy.context.mode.startswith("EDIT") and not depsgraph_updates_locked:
|
||||||
for u in depsgraph.updates:
|
for u in depsgraph.updates:
|
||||||
if f"{utils.prefix()}_linked_textobject" in u.id.keys() \
|
if f"{utils.prefix()}_linked_textobject" in u.id.keys() \
|
||||||
and f"{utils.prefix()}_type" in u.id.keys() \
|
and f"{utils.prefix()}_type" in u.id.keys() \
|
||||||
and u.id[f"{utils.prefix()}_type"] == 'textobject':
|
and u.id[f"{utils.prefix()}_type"] == 'textobject':
|
||||||
linked_textobject = u.id[f"{utils.prefix()}_linked_textobject"]
|
linked_textobject = u.id[f"{utils.prefix()}_linked_textobject"]
|
||||||
if u.is_updated_geometry and len(scene.abc3d_data.available_texts) > linked_textobject:
|
if u.is_updated_geometry and len(scene.abc3d_data.available_texts) > linked_textobject:
|
||||||
|
lock_depsgraph_updates()
|
||||||
def later():
|
def later():
|
||||||
if not "lock_depsgraph_update_ntimes" in scene.abc3d_data \
|
if not "lock_depsgraph_update_ntimes" in scene.abc3d_data \
|
||||||
or scene.abc3d_data["lock_depsgraph_update_ntimes"] == 0:
|
or scene.abc3d_data["lock_depsgraph_update_ntimes"] <= 0:
|
||||||
print("******* not yet")
|
|
||||||
butils.set_text_on_curve(
|
butils.set_text_on_curve(
|
||||||
scene.abc3d_data.available_texts[linked_textobject])
|
scene.abc3d_data.available_texts[linked_textobject])
|
||||||
elif scene.abc3d_data["lock_depsgraph_update_ntimes"] > 0:
|
elif scene.abc3d_data["lock_depsgraph_update_ntimes"] > 0:
|
||||||
|
@ -1596,6 +1610,9 @@ def unregister():
|
||||||
if on_frame_changed in bpy.app.handlers.frame_change_post:
|
if on_frame_changed in bpy.app.handlers.frame_change_post:
|
||||||
bpy.app.handlers.frame_change_post.remove(on_frame_changed)
|
bpy.app.handlers.frame_change_post.remove(on_frame_changed)
|
||||||
|
|
||||||
|
if on_depsgraph_update in bpy.app.handlers.depsgraph_update_post:
|
||||||
|
bpy.app.handlers.depsgraph_update_post.remove(on_depsgraph_update)
|
||||||
|
|
||||||
del bpy.types.Scene.abc3d_data
|
del bpy.types.Scene.abc3d_data
|
||||||
print(f"UNREGISTER {bl_info['name']}")
|
print(f"UNREGISTER {bl_info['name']}")
|
||||||
|
|
||||||
|
|
|
@ -1782,7 +1782,6 @@ class ForgejoEngine:
|
||||||
"zipball_url": self.get_zip_url(tag["commit"]["sha"], updater)
|
"zipball_url": self.get_zip_url(tag["commit"]["sha"], updater)
|
||||||
} for tag in response]
|
} for tag in response]
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# The module-shared class instance,
|
# The module-shared class instance,
|
||||||
# should be what's imported to other files
|
# should be what's imported to other files
|
||||||
|
|
204
butils.py
204
butils.py
|
@ -1,11 +1,12 @@
|
||||||
import bpy
|
|
||||||
import mathutils
|
|
||||||
import queue
|
|
||||||
import importlib
|
import importlib
|
||||||
import os
|
import os
|
||||||
|
import queue
|
||||||
import re
|
import re
|
||||||
from multiprocessing import Process
|
from multiprocessing import Process
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import mathutils
|
||||||
|
|
||||||
# import time # for debugging performance
|
# import time # for debugging performance
|
||||||
|
|
||||||
# then import dependencies for our addon
|
# then import dependencies for our addon
|
||||||
|
@ -111,7 +112,8 @@ def calc_tangent_on_bezier(bezier_point_1, bezier_point_2, t):
|
||||||
(-3 * (t**2) + 6 * t * (1 - t)) * h2 + (3 * t**2) * p2
|
(-3 * (t**2) + 6 * t * (1 - t)) * h2 + (3 * t**2) * p2
|
||||||
).normalized()
|
).normalized()
|
||||||
|
|
||||||
from math import radians, sqrt, pi, acos
|
from math import acos, pi, radians, sqrt
|
||||||
|
|
||||||
|
|
||||||
def align_rotations_auto_pivot(mask, input_rotations, vectors, factors, local_main_axis):
|
def align_rotations_auto_pivot(mask, input_rotations, vectors, factors, local_main_axis):
|
||||||
output_rotations = [mathutils.Matrix().to_3x3() for _ in range(len(input_rotations))]
|
output_rotations = [mathutils.Matrix().to_3x3() for _ in range(len(input_rotations))]
|
||||||
|
@ -449,6 +451,7 @@ def load_font_from_filepath(filepath, glyphs="", font_name="", face_name=""):
|
||||||
glyph_obj = move_in_fontcollection(
|
glyph_obj = move_in_fontcollection(
|
||||||
o,
|
o,
|
||||||
fontcollection)
|
fontcollection)
|
||||||
|
glyph_obj_pointer = bpy.types.PointerProperty(glyph_obj)
|
||||||
|
|
||||||
if glyph_obj == o:
|
if glyph_obj == o:
|
||||||
del o[marker_property]
|
del o[marker_property]
|
||||||
|
@ -457,7 +460,7 @@ def load_font_from_filepath(filepath, glyphs="", font_name="", face_name=""):
|
||||||
font_name,
|
font_name,
|
||||||
face_name,
|
face_name,
|
||||||
glyph_id,
|
glyph_id,
|
||||||
glyph_obj)
|
glyph_obj_pointer)
|
||||||
for c in o.children:
|
for c in o.children:
|
||||||
if is_metrics_object(c):
|
if is_metrics_object(c):
|
||||||
add_metrics_obj_from_bound_box(glyph_obj,
|
add_metrics_obj_from_bound_box(glyph_obj,
|
||||||
|
@ -472,7 +475,7 @@ def load_font_from_filepath(filepath, glyphs="", font_name="", face_name=""):
|
||||||
for g in face.glyphs:
|
for g in face.glyphs:
|
||||||
# iterate alternates
|
# iterate alternates
|
||||||
for glyph in face.glyphs[g]:
|
for glyph in face.glyphs[g]:
|
||||||
glyphs.append(glyph)
|
glyphs.append(get_original(glyph))
|
||||||
if len(glyphs) > 0:
|
if len(glyphs) > 0:
|
||||||
add_default_metrics_to_objects(glyphs)
|
add_default_metrics_to_objects(glyphs)
|
||||||
# calculate unit factor
|
# calculate unit factor
|
||||||
|
@ -586,18 +589,19 @@ def ShowMessageBox(title = "Message Box", icon = 'INFO', message=""):
|
||||||
bpy.context.window_manager.popup_menu(draw, title = title, icon = icon)
|
bpy.context.window_manager.popup_menu(draw, title = title, icon = icon)
|
||||||
|
|
||||||
def simply_delete_objects(objs):
|
def simply_delete_objects(objs):
|
||||||
context_override = bpy.context.copy()
|
completely_delete_objects(objs)
|
||||||
context_override["selected_objects"] = list(objs)
|
|
||||||
with bpy.context.temp_override(**context_override):
|
|
||||||
bpy.ops.object.delete()
|
|
||||||
|
|
||||||
def completely_delete_objects(objs):
|
def completely_delete_objects(objs, recursive=True):
|
||||||
simply_delete_objects(objs)
|
|
||||||
|
|
||||||
# remove deleted objects
|
|
||||||
# this is necessary
|
|
||||||
for g in objs:
|
for g in objs:
|
||||||
if type(g) != type(None):
|
if type(g) != type(None):
|
||||||
|
if recursive:
|
||||||
|
try:
|
||||||
|
if hasattr(g, "children") and len(g.children) > 0:
|
||||||
|
completely_delete_objects(g.children)
|
||||||
|
except ReferenceError as e:
|
||||||
|
# not important
|
||||||
|
pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
bpy.data.objects.remove(g, do_unlink=True)
|
bpy.data.objects.remove(g, do_unlink=True)
|
||||||
except ReferenceError as e:
|
except ReferenceError as e:
|
||||||
|
@ -672,51 +676,80 @@ def prepare_text(font_name, face_name, text):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def is_bezier(curve):
|
def is_bezier(curve):
|
||||||
|
if curve.type != 'CURVE':
|
||||||
|
return False
|
||||||
if len(curve.data.splines) < 1:
|
if len(curve.data.splines) < 1:
|
||||||
return False
|
return False
|
||||||
return curve.data.splines[0].type == 'BEZIER'
|
for spline in curve.data.splines:
|
||||||
|
if spline.type != 'BEZIER':
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def will_regenerate(text_properties):
|
||||||
|
mom = text_properties.text_object
|
||||||
|
|
||||||
|
if len(text_properties.text) != len(text_properties.glyphs):
|
||||||
|
return True
|
||||||
|
|
||||||
|
for i, g in enumerate(text_properties.glyphs):
|
||||||
|
if not hasattr(g.glyph_object, "type"):
|
||||||
|
return True
|
||||||
|
elif g.glyph_object.type != 'EMPTY':
|
||||||
|
return True
|
||||||
|
# check if perhaps one glyph was deleted
|
||||||
|
elif type(g.glyph_object) == type(None):
|
||||||
|
return True
|
||||||
|
elif type(g.glyph_object.parent) == type(None):
|
||||||
|
return True
|
||||||
|
elif g.glyph_object.parent.users_collection != g.glyph_object.users_collection:
|
||||||
|
return True
|
||||||
|
elif len(text_properties.text) > i and g.glyph_id != text_properties.text[i]:
|
||||||
|
return True
|
||||||
|
elif len(text_properties.text) > i and (g.glyph_object[f"{utils.prefix()}_font_name"] != text_properties.font_name
|
||||||
|
or g.glyph_object[f"{utils.prefix()}_face_name"] != text_properties.face_name):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def set_text_on_curve(text_properties, reset_timeout_s=0.1, reset_depsgraph_n=4):
|
||||||
|
"""set_text_on_curve
|
||||||
|
|
||||||
|
An earlier reset cancels the other.
|
||||||
|
To disable reset, set both to false.
|
||||||
|
|
||||||
|
:param text_properties: all information necessary to set text on a curve
|
||||||
|
:type text_properties: ABC3D_text_properties
|
||||||
|
:param reset_timeout_s: reset external parameters after timeout. (<= 0) = immediate, (> 0) = non-blocking reset timeout in seconds, (False) = no timeout reset
|
||||||
|
:type reset_timeout_s: float
|
||||||
|
:param reset_depsgraph_n: reset external parameters after n-th depsgraph update. (<= 0) = immediate, (> 0) = reset after n-th depsgraph update, (False) = no depsgraph reset
|
||||||
|
:type reset_depsgraph_n: int
|
||||||
|
"""
|
||||||
|
|
||||||
def set_text_on_curve(text_properties, recursive=True):
|
|
||||||
# starttime = time.perf_counter_ns()
|
# starttime = time.perf_counter_ns()
|
||||||
mom = text_properties.text_object
|
mom = text_properties.text_object
|
||||||
if mom.type != "CURVE":
|
if mom.type != "CURVE":
|
||||||
return False
|
return False
|
||||||
|
|
||||||
regenerate = False
|
distribution_type = 'CALCULATE' if is_bezier(mom) else 'FOLLOW_PATH'
|
||||||
glyph_objects = []
|
|
||||||
for i, g in enumerate(text_properties.glyphs):
|
|
||||||
glyph_objects.append(g.glyph_object)
|
|
||||||
|
|
||||||
# check if perhaps one glyph was deleted
|
# use_path messes with parenting
|
||||||
if (type(g.glyph_object) == type(None)
|
# however, we need it for follow_path
|
||||||
or type(g.glyph_object.parent) == type(None)
|
|
||||||
or g.glyph_object.parent.users_collection != g.glyph_object.users_collection):
|
|
||||||
regenerate = True
|
|
||||||
elif len(text_properties.text) > i and g.glyph_id != text_properties.text[i]:
|
|
||||||
regenerate = True
|
|
||||||
elif len(text_properties.text) > i and (g.glyph_object[f"{utils.prefix()}_font_name"] != text_properties.font_name
|
|
||||||
or g.glyph_object[f"{utils.prefix()}_face_name"] != text_properties.face_name):
|
|
||||||
regenerate = True
|
|
||||||
|
|
||||||
if len(text_properties.text) != len(text_properties.glyphs):
|
|
||||||
regenerate = True
|
|
||||||
|
|
||||||
# blender bug
|
|
||||||
# https://projects.blender.org/blender/blender/issues/100661
|
# https://projects.blender.org/blender/blender/issues/100661
|
||||||
if mom.data.use_path:
|
previous_use_path = mom.data.use_path
|
||||||
regenerate = True
|
if distribution_type == 'CALCULATE':
|
||||||
|
mom.data.use_path = False
|
||||||
|
elif distribution_type == 'FOLLOW_PATH':
|
||||||
|
mom.data.use_path = True
|
||||||
|
|
||||||
|
regenerate = will_regenerate(text_properties)
|
||||||
|
|
||||||
# if we regenerate.... delete objects
|
# if we regenerate.... delete objects
|
||||||
if regenerate:
|
if regenerate and text_properties.get("glyphs"):
|
||||||
completely_delete_objects(glyph_objects)
|
glyph_objects = [ g["glyph_object"] for g in text_properties["glyphs"] ]
|
||||||
|
completely_delete_objects(glyph_objects, True)
|
||||||
text_properties.glyphs.clear()
|
text_properties.glyphs.clear()
|
||||||
|
|
||||||
#TODO: fix selection with context_override
|
|
||||||
previous_selection = bpy.context.selected_objects if hasattr(bpy.context, "selected_objects") else [ o for o in bpy.context.scene.objects if o.select_get() ]
|
|
||||||
bpy.ops.object.select_all(action='DESELECT')
|
|
||||||
selected_objects = []
|
|
||||||
|
|
||||||
curve_length = get_curve_length(mom)
|
curve_length = get_curve_length(mom)
|
||||||
advance = text_properties.offset
|
advance = text_properties.offset
|
||||||
glyph_advance = 0
|
glyph_advance = 0
|
||||||
|
@ -747,7 +780,7 @@ def set_text_on_curve(text_properties, recursive=True):
|
||||||
|
|
||||||
glyph = Font.get_glyph(text_properties.font_name,
|
glyph = Font.get_glyph(text_properties.font_name,
|
||||||
text_properties.face_name,
|
text_properties.face_name,
|
||||||
glyph_id)
|
glyph_id).original
|
||||||
|
|
||||||
if glyph == None:
|
if glyph == None:
|
||||||
# self.report({'ERROR'}, f"Glyph not found for {font_name} {face_name} {glyph_id}")
|
# self.report({'ERROR'}, f"Glyph not found for {font_name} {face_name} {glyph_id}")
|
||||||
|
@ -755,16 +788,20 @@ def set_text_on_curve(text_properties, recursive=True):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
ob = None
|
ob = None
|
||||||
|
obg = None
|
||||||
if regenerate:
|
if regenerate:
|
||||||
ob = bpy.data.objects.new(f"{glyph_id}", glyph.data)
|
ob = bpy.data.objects.new(f"{glyph_id}", None)
|
||||||
|
obg = bpy.data.objects.new(f"{glyph_id}_mesh", glyph.data)
|
||||||
ob[f"{utils.prefix()}_type"] = "glyph"
|
ob[f"{utils.prefix()}_type"] = "glyph"
|
||||||
ob[f"{utils.prefix()}_linked_textobject"] = text_properties.text_id
|
ob[f"{utils.prefix()}_linked_textobject"] = text_properties.text_id
|
||||||
ob[f"{utils.prefix()}_font_name"] = text_properties.font_name
|
ob[f"{utils.prefix()}_font_name"] = text_properties.font_name
|
||||||
ob[f"{utils.prefix()}_face_name"] = text_properties.face_name
|
ob[f"{utils.prefix()}_face_name"] = text_properties.face_name
|
||||||
else:
|
else:
|
||||||
ob = text_properties.glyphs[i].glyph_object
|
ob = text_properties.glyphs[i].glyph_object
|
||||||
|
for c in ob.children:
|
||||||
|
if c.name.startswith(f"{glyph_id}_mesh"):
|
||||||
|
obg = c
|
||||||
|
|
||||||
distribution_type = 'CALCULATE' if is_bezier(mom) else 'FOLLOW_PATH'
|
|
||||||
if distribution_type == 'FOLLOW_PATH':
|
if distribution_type == 'FOLLOW_PATH':
|
||||||
ob.constraints.new(type='FOLLOW_PATH')
|
ob.constraints.new(type='FOLLOW_PATH')
|
||||||
ob.constraints["Follow Path"].target = mom
|
ob.constraints["Follow Path"].target = mom
|
||||||
|
@ -773,6 +810,7 @@ def set_text_on_curve(text_properties, recursive=True):
|
||||||
ob.constraints["Follow Path"].use_curve_follow = True
|
ob.constraints["Follow Path"].use_curve_follow = True
|
||||||
ob.constraints["Follow Path"].forward_axis = "FORWARD_X"
|
ob.constraints["Follow Path"].forward_axis = "FORWARD_X"
|
||||||
ob.constraints["Follow Path"].up_axis = "UP_Y"
|
ob.constraints["Follow Path"].up_axis = "UP_Y"
|
||||||
|
spline_index = 0
|
||||||
elif distribution_type == 'CALCULATE':
|
elif distribution_type == 'CALCULATE':
|
||||||
location, tangent, spline_index = calc_point_on_bezier_curve(mom, advance, True, True)
|
location, tangent, spline_index = calc_point_on_bezier_curve(mom, advance, True, True)
|
||||||
if spline_index != previous_spline_index:
|
if spline_index != previous_spline_index:
|
||||||
|
@ -780,6 +818,11 @@ def set_text_on_curve(text_properties, recursive=True):
|
||||||
|
|
||||||
if regenerate:
|
if regenerate:
|
||||||
ob.location = mom.matrix_world @ (location + text_properties.translation)
|
ob.location = mom.matrix_world @ (location + text_properties.translation)
|
||||||
|
mom.users_collection[0].objects.link(obg)
|
||||||
|
mom.users_collection[0].objects.link(ob)
|
||||||
|
ob.parent = mom
|
||||||
|
obg.parent = ob
|
||||||
|
obg.location = mathutils.Vector((0.0, 0.0, 0.0))
|
||||||
else:
|
else:
|
||||||
ob.location = (location + text_properties.translation)
|
ob.location = (location + text_properties.translation)
|
||||||
|
|
||||||
|
@ -796,16 +839,20 @@ def set_text_on_curve(text_properties, recursive=True):
|
||||||
local_main_axis)
|
local_main_axis)
|
||||||
if ob.rotation_mode != 'QUATERNION':
|
if ob.rotation_mode != 'QUATERNION':
|
||||||
ob.rotation_mode = 'QUATERNION'
|
ob.rotation_mode = 'QUATERNION'
|
||||||
|
if obg.rotation_mode != 'QUATERNION':
|
||||||
|
obg.rotation_mode = 'QUATERNION'
|
||||||
q = mathutils.Quaternion()
|
q = mathutils.Quaternion()
|
||||||
q.rotate(text_properties.orientation)
|
q.rotate(text_properties.orientation)
|
||||||
if regenerate:
|
if regenerate:
|
||||||
ob.rotation_quaternion = (mom.matrix_world @ motor[0] @ q.to_matrix().to_4x4()).to_quaternion()
|
obg.rotation_quaternion = q
|
||||||
|
ob.rotation_quaternion = (mom.matrix_world @ motor[0]).to_quaternion()
|
||||||
else:
|
else:
|
||||||
ob.rotation_quaternion = (motor[0] @ q.to_matrix().to_4x4()).to_quaternion()
|
ob.rotation_quaternion = motor[0].to_quaternion()
|
||||||
else:
|
else:
|
||||||
q = mathutils.Quaternion()
|
q = mathutils.Quaternion()
|
||||||
q.rotate(text_properties.orientation)
|
q.rotate(text_properties.orientation)
|
||||||
ob.rotation_quaternion = q
|
# obg.rotation_quaternion = q
|
||||||
|
obg.rotation_quaternion = (mom.matrix_world @ q.to_matrix().to_4x4()).to_quaternion()
|
||||||
# ob.rotation_quaternion = (mom.matrix_world @ q.to_matrix().to_4x4()).to_quaternion()
|
# ob.rotation_quaternion = (mom.matrix_world @ q.to_matrix().to_4x4()).to_quaternion()
|
||||||
|
|
||||||
|
|
||||||
|
@ -839,17 +886,12 @@ def set_text_on_curve(text_properties, recursive=True):
|
||||||
previous_spline_index = spline_index
|
previous_spline_index = spline_index
|
||||||
|
|
||||||
if regenerate:
|
if regenerate:
|
||||||
mom.users_collection[0].objects.link(ob)
|
|
||||||
glyph_data = text_properties.glyphs.add()
|
glyph_data = text_properties.glyphs.add()
|
||||||
glyph_data.glyph_id = glyph_id
|
glyph_data.glyph_id = glyph_id
|
||||||
glyph_data.glyph_object = ob
|
glyph_data.glyph_object = ob
|
||||||
glyph_data.letter_spacing = 0
|
glyph_data.letter_spacing = 0
|
||||||
ob.select_set(True)
|
|
||||||
|
|
||||||
if regenerate:
|
if regenerate:
|
||||||
mom.select_set(True)
|
|
||||||
# https://projects.blender.org/blender/blender/issues/100661
|
|
||||||
mom.data.use_path = False
|
|
||||||
mom[f"{utils.prefix()}_type"] = "textobject"
|
mom[f"{utils.prefix()}_type"] = "textobject"
|
||||||
mom[f"{utils.prefix()}_linked_textobject"] = text_properties.text_id
|
mom[f"{utils.prefix()}_linked_textobject"] = text_properties.text_id
|
||||||
mom[f"{utils.prefix()}_font_name"] = text_properties.font_name
|
mom[f"{utils.prefix()}_font_name"] = text_properties.font_name
|
||||||
|
@ -858,10 +900,42 @@ def set_text_on_curve(text_properties, recursive=True):
|
||||||
mom[f"{utils.prefix()}_letter_spacing"] = text_properties.letter_spacing
|
mom[f"{utils.prefix()}_letter_spacing"] = text_properties.letter_spacing
|
||||||
mom[f"{utils.prefix()}_orientation"] = text_properties.orientation
|
mom[f"{utils.prefix()}_orientation"] = text_properties.orientation
|
||||||
mom[f"{utils.prefix()}_translation"] = text_properties.translation
|
mom[f"{utils.prefix()}_translation"] = text_properties.translation
|
||||||
bpy.context.view_layer.objects.active = mom
|
|
||||||
bpy.ops.object.parent_set(type='OBJECT')
|
if "lock_depsgraph_update_ntimes" in bpy.context.scene.abc3d_data:
|
||||||
bpy.context.scene.abc3d_data["lock_depsgraph_update_ntimes"] = len(bpy.context.selected_objects)
|
bpy.context.scene.abc3d_data["lock_depsgraph_update_ntimes"] += len(bpy.context.selected_objects)
|
||||||
mom["lock_depsgraph_update_ntimes"] = len(bpy.context.selected_objects)
|
else:
|
||||||
|
bpy.context.scene.abc3d_data["lock_depsgraph_update_ntimes"] = len(bpy.context.selected_objects)
|
||||||
|
|
||||||
|
# NOTE: we reset with a timeout, as setting and resetting certain things
|
||||||
|
# in fast succession will cause visual glitches (e.g. {}.data.use_path).
|
||||||
|
def reset():
|
||||||
|
mom.data.use_path = previous_use_path
|
||||||
|
if counted_reset in bpy.app.handlers.depsgraph_update_post:
|
||||||
|
bpy.app.handlers.depsgraph_update_post.remove(counted_reset)
|
||||||
|
if bpy.app.timers.is_registered(reset):
|
||||||
|
bpy.app.timers.unregister(reset)
|
||||||
|
|
||||||
|
molotov = reset_depsgraph_n + 0
|
||||||
|
def counted_reset(scene, depsgraph):
|
||||||
|
nonlocal molotov
|
||||||
|
if molotov == 0:
|
||||||
|
reset()
|
||||||
|
else:
|
||||||
|
molotov -= 1
|
||||||
|
|
||||||
|
# unregister previous resets to avoid multiple execution
|
||||||
|
if bpy.app.timers.is_registered(reset):
|
||||||
|
bpy.app.timers.unregister(reset)
|
||||||
|
if counted_reset in bpy.app.handlers.depsgraph_update_post:
|
||||||
|
bpy.app.handlers.depsgraph_update_post.remove(counted_reset)
|
||||||
|
|
||||||
|
if not isinstance(reset_timeout_s, bool):
|
||||||
|
if reset_timeout_s > 0:
|
||||||
|
bpy.app.timers.register(reset, first_interval=reset_timeout_s)
|
||||||
|
elif reset_timeout <= 0:
|
||||||
|
reset()
|
||||||
|
|
||||||
|
bpy.app.handlers.depsgraph_update_post.append(counted_reset)
|
||||||
|
|
||||||
# endtime = time.perf_counter_ns()
|
# endtime = time.perf_counter_ns()
|
||||||
# elapsedtime = endtime - starttime
|
# elapsedtime = endtime - starttime
|
||||||
|
@ -1095,6 +1169,12 @@ def get_metrics_object(o):
|
||||||
return c
|
return c
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def get_original(o):
|
||||||
|
if hasattr(o, "original"):
|
||||||
|
return o.original
|
||||||
|
else:
|
||||||
|
return o
|
||||||
|
|
||||||
def add_default_metrics_to_objects(objects=None, overwrite_existing=False):
|
def add_default_metrics_to_objects(objects=None, overwrite_existing=False):
|
||||||
if type(objects) == type(None):
|
if type(objects) == type(None):
|
||||||
objects=bpy.context.selected_objects
|
objects=bpy.context.selected_objects
|
||||||
|
|
Loading…
Reference in a new issue