Skip to content

Commit cc22755

Browse files
justo46Julius Stotz
and
Julius Stotz
authored
Version 0.1.4 (#16)
* Added Enable/Disable All and the option to select a root directory for relative paths * Added first version of bin file format * Added bin.py * Fixed byte errors and store the rest of the bytes in an (unused) list * Added 1 main commit * Added main commit * Added a button to select multiple sequences * Added the functionality to import multiple sequences (in top menu and in addon), a custom initial transformation matrix and a list, so the file sequences do not have to be loaded again in every frame * Added object property for initial transformation matrix, made for multiple sequence loading cleaner code and tried to resolve the issue with transformation matrices * Added install script * Fixed keyframe animation system to work with other transformations * Optimized adding meshio objects and added a button to refresh the sequences * Fixed keyframe system for multiple files * Deleted install file from repo, since this is a local file. * Resolved further changes * Updated version + test to import MeshIO Objects * Updated version * Deleted some comments and made the code easier to read * Further code improvements * Removed bin.py * Deleted import of bin.py and corrected version --------- Co-authored-by: Julius Stotz <jstotz@wilson.informatik.rwth-aachen.de> Co-authored-by: julius.stotz <julius.stotz@rwth-aachen.de>
1 parent 87ff11b commit cc22755

File tree

6 files changed

+195
-23
lines changed

6 files changed

+195
-23
lines changed

__init__.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
"name": "Sequence Loader",
33
"description": "Loader for meshio supported mesh files/ simulation sequences",
44
"author": "Interactive Computer Graphics",
5-
"version": (0, 1, 3),
6-
"blender": (3, 1, 0),
5+
"version": (0, 1, 4),
6+
"blender": (3, 4, 0),
77
"warning": "",
88
"support": "COMMUNITY",
99
"category": "Import-Export",
@@ -21,6 +21,7 @@
2121
bpy.context.preferences.filepaths.use_relative_paths = False
2222

2323
from bseq import *
24+
from bseq.operators import menu_func_import
2425

2526
classes = [
2627
BSEQ_obj_property,
@@ -44,6 +45,9 @@
4445
BSEQ_OT_refresh_seq,
4546
BSEQ_OT_disable_all,
4647
BSEQ_OT_enable_all,
48+
BSEQ_OT_refresh_sequences,
49+
WM_OT_batchSequences,
50+
WM_OT_MeshioObject
4751
]
4852

4953

@@ -56,6 +60,7 @@ def register():
5660
bpy.types.Object.BSEQ = bpy.props.PointerProperty(type=BSEQ_obj_property)
5761
bpy.types.Mesh.BSEQ = bpy.props.PointerProperty(type=BSEQ_mesh_property)
5862

63+
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
5964

6065
# manually call this function once
6166
# so when addon being installed, it can run correctly
@@ -69,6 +74,7 @@ def unregister():
6974
del bpy.types.Scene.BSEQ
7075
del bpy.types.Object.BSEQ
7176
bpy.app.handlers.load_post.remove(BSEQ_initialize)
77+
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
7278
unsubscribe_to_selected()
7379

7480

bseq/__init__.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from bseq.utils import refresh_obj
2-
from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins, BSEQ_OT_set_as_split_norm, BSEQ_OT_remove_split_norm, BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all
2+
from .operators import BSEQ_OT_load, BSEQ_OT_edit, BSEQ_OT_resetpt, BSEQ_OT_resetmesh, BSEQ_OT_resetins, BSEQ_OT_set_as_split_norm, BSEQ_OT_remove_split_norm, BSEQ_OT_disable_selected, BSEQ_OT_enable_selected, BSEQ_OT_refresh_seq, BSEQ_OT_disable_all, BSEQ_OT_enable_all, BSEQ_OT_refresh_sequences, WM_OT_batchSequences, WM_OT_MeshioObject
33
from .properties import BSEQ_scene_property, BSEQ_obj_property, BSEQ_mesh_property
44
from .panels import BSEQ_UL_Obj_List, BSEQ_List_Panel, BSEQ_Settings, BSEQ_Import, BSEQ_Templates, BSEQ_UL_Att_List, draw_template
55
from .messenger import subscribe_to_selected, unsubscribe_to_selected
@@ -45,4 +45,7 @@ def BSEQ_initialize(scene):
4545
"BSEQ_OT_refresh_seq",
4646
"BSEQ_OT_disable_all",
4747
"BSEQ_OT_enable_all",
48+
"BSEQ_OT_refresh_sequences",
49+
"WM_OT_batchSequences",
50+
"WM_OT_MeshioObject"
4851
]

bseq/importer.py

+59-14
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import bpy
2+
import mathutils
23
import meshio
34
import traceback
45
import fileseq
6+
import os
57
from .utils import show_message_box
68
import numpy as np
79
from mathutils import Matrix
@@ -51,6 +53,37 @@ def extract_faces(cell: meshio.CellBlock):
5153
show_message_box(cell.type + " is unsupported mesh format yet")
5254
return np.array([])
5355

56+
def has_keyframe(obj, attr):
57+
animdata = obj.animation_data
58+
if animdata is not None and animdata.action is not None:
59+
for fcurve in animdata.action.fcurves:
60+
if fcurve.data_path == attr:
61+
return len(fcurve.keyframe_points) > 0
62+
return False
63+
64+
def apply_transformation(meshio_mesh, obj, depsgraph):
65+
# evaluate the keyframe animation system
66+
eval_location = obj.evaluated_get(depsgraph).location if has_keyframe(obj, "location") else None
67+
eval_scale = obj.evaluated_get(depsgraph).scale if has_keyframe(obj, "scale") else None
68+
69+
if has_keyframe(obj, "rotation_quaternion"):
70+
eval_rotation = obj.evaluated_get(depsgraph).rotation_quaternion
71+
elif has_keyframe(obj, "rotation_axis_angle"):
72+
eval_rotation = obj.evaluated_get(depsgraph).rotation_axis_angle
73+
elif has_keyframe(obj, "rotation_euler"):
74+
eval_rotation = obj.evaluated_get(depsgraph).rotation_euler
75+
else:
76+
eval_rotation = None
77+
78+
eval_transform_matrix = mathutils.Matrix.LocRotScale(eval_location, eval_rotation, eval_scale)
79+
80+
# evaluate the rigid body transformations (only relevant for .bin format)
81+
rigid_body_transformation = mathutils.Matrix.Identity(4)
82+
if meshio_mesh.field_data.get("transformation_matrix") is not None:
83+
rigid_body_transformation = meshio_mesh.field_data["transformation_matrix"]
84+
85+
# multiply everything together (with custom transform matrix)
86+
obj.matrix_world = rigid_body_transformation @ obj.BSEQ.initial_transform_matrix @ eval_transform_matrix
5487

5588
def update_mesh(meshio_mesh, mesh):
5689
# extract information from the meshio mesh
@@ -59,7 +92,8 @@ def update_mesh(meshio_mesh, mesh):
5992
n_poly = 0
6093
n_loop = 0
6194
n_verts = len(mesh_vertices)
62-
95+
if n_verts == 0:
96+
return
6397
faces_loop_start = np.array([], dtype=np.uint64)
6498
faces_loop_total = np.array([], dtype=np.uint64)
6599
loops_vert_idx = np.array([], dtype=np.uint64)
@@ -136,8 +170,27 @@ def update_mesh(meshio_mesh, mesh):
136170
mesh.use_auto_smooth = True
137171
mesh.normals_split_custom_set_from_vertices(v)
138172

173+
# function to create a single meshio object
174+
def create_meshio_obj(filepath):
175+
meshio_mesh = None
176+
try:
177+
meshio_mesh = meshio.read(filepath)
178+
except Exception as e:
179+
show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(),
180+
"Meshio Loading Error" + str(e),
181+
icon="ERROR")
182+
183+
# create the object
184+
name = os.path.basename(filepath)
185+
mesh = bpy.data.meshes.new(name)
186+
object = bpy.data.objects.new(name, mesh)
187+
update_mesh(meshio_mesh, object.data)
188+
bpy.context.collection.objects.link(object)
189+
bpy.ops.object.select_all(action="DESELECT")
190+
bpy.context.view_layer.objects.active = object
139191

140-
def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 0, -1, 0], [0, 1, 0, 0], [0, 0, 0, 1]])):
192+
193+
def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]])):
141194

142195
current_frame = bpy.context.scene.frame_current
143196
filepath = fileseq[current_frame % len(fileseq)]
@@ -166,7 +219,8 @@ def create_obj(fileseq, use_relative, root_path, transform_matrix=Matrix([[1, 0,
166219
object.BSEQ.pattern = str(fileseq)
167220
object.BSEQ.init = True
168221
object.BSEQ.enabled = enabled
169-
object.matrix_world = transform_matrix
222+
# Flatten custom transformation matrix for the property
223+
object.BSEQ.initial_transform_matrix = [transform_matrix[j][i] for i in range(4) for j in range(4)]
170224
driver = object.driver_add("BSEQ.frame")
171225
driver.driver.expression = 'frame'
172226
if enabled:
@@ -247,14 +301,5 @@ def update_obj(scene, depsgraph=None):
247301
continue
248302
update_mesh(meshio_mesh, obj.data)
249303

250-
# force to evaluate the keyframe animation system
251-
obj.location = obj.evaluated_get(depsgraph).location
252-
match obj.rotation_mode:
253-
case "QUATERNION":
254-
obj.rotation_quaternion = obj.evaluated_get(depsgraph).rotation_quaternion
255-
case "AXIS_ANGLE":
256-
obj.rotation_axis_angle = obj.evaluated_get(depsgraph).rotation_axis_angle
257-
case _:
258-
obj.rotation_euler = obj.evaluated_get(depsgraph).rotation_euler
259-
260-
obj.scale = obj.evaluated_get(depsgraph).scale
304+
apply_transformation(meshio_mesh, obj, depsgraph)
305+

bseq/operators.py

+74-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import bpy
2+
from mathutils import Matrix
23
import fileseq
34
from .messenger import *
45
import traceback
56
from .utils import refresh_obj, show_message_box
6-
from .importer import create_obj
7+
from .importer import create_obj, create_meshio_obj
78
import numpy as np
89

910

@@ -41,7 +42,13 @@ def execute(self, context):
4142
show_message_box(traceback.format_exc(), "Can't find sequence: " + str(fs), "ERROR")
4243
return {"CANCELLED"}
4344

44-
create_obj(fs, importer_prop.relative, importer_prop.root_path)
45+
transform_matrix = (Matrix.LocRotScale(
46+
importer_prop.custom_location,
47+
importer_prop.custom_rotation,
48+
importer_prop.custom_scale)
49+
if importer_prop.use_custom_transform else Matrix.Identity(4))
50+
51+
create_obj(fs, importer_prop.relative, importer_prop.root_path, transform_matrix=transform_matrix)
4552
return {"FINISHED"}
4653

4754

@@ -297,3 +304,68 @@ def execute(self, context):
297304
if obj.BSEQ.init and not obj.BSEQ.enabled:
298305
obj.BSEQ.enabled = True
299306
return {"FINISHED"}
307+
308+
class BSEQ_OT_refresh_sequences(bpy.types.Operator):
309+
'''This operator refreshes all found sequences'''
310+
bl_label = "" #"Refresh Found Sequences"
311+
bl_idname = "bseq.refreshseqs"
312+
bl_options = {"UNDO"}
313+
314+
def execute(self, context):
315+
scene = context.scene
316+
# call the update function of path by setting it to its own value
317+
scene.BSEQ.path = scene.BSEQ.path
318+
return {"FINISHED"}
319+
320+
from pathlib import Path
321+
import meshio
322+
from bpy_extras.io_utils import ImportHelper
323+
324+
class WM_OT_batchSequences(bpy.types.Operator, ImportHelper):
325+
"""Batch Import Sequences"""
326+
bl_idname = "wm.seq_import_batch"
327+
bl_label = "Import multiple sequences"
328+
bl_options = {'PRESET', 'UNDO'}
329+
330+
files: bpy.props.CollectionProperty(type=bpy.types.PropertyGroup)
331+
332+
def execute(self, context):
333+
scene = context.scene
334+
importer_prop = scene.BSEQ
335+
336+
folder = Path(self.filepath)
337+
used_seqs = set()
338+
339+
for selection in self.files:
340+
# Check if there exists a matching file sequence for every selection
341+
fp = str(Path(folder.parent, selection.name))
342+
seqs = fileseq.findSequencesOnDisk(str(folder.parent))
343+
matching_seqs = [s for s in seqs if fp in list(s) and s not in used_seqs]
344+
345+
if matching_seqs:
346+
transform_matrix = (Matrix.LocRotScale(importer_prop.custom_location, importer_prop.custom_rotation, importer_prop.custom_scale)
347+
if importer_prop.use_custom_transform else Matrix.Identity(4))
348+
create_obj(matching_seqs[0], False, importer_prop.root_path, transform_matrix=transform_matrix)
349+
used_seqs.add(matching_seqs[0])
350+
return {'FINISHED'}
351+
352+
class WM_OT_MeshioObject(bpy.types.Operator, ImportHelper):
353+
"""Batch Import Meshio Objects"""
354+
bl_idname = "wm.meshio_import_batch"
355+
bl_label = "Import multiple Meshio objects"
356+
bl_options = {'PRESET', 'UNDO'}
357+
358+
files: bpy.props.CollectionProperty(type=bpy.types.PropertyGroup)
359+
360+
def execute(self, context):
361+
folder = Path(self.filepath)
362+
363+
for selection in self.files:
364+
fp = Path(folder.parent, selection.name)
365+
create_meshio_obj(str(fp))
366+
return {'FINISHED'}
367+
368+
def menu_func_import(self, context):
369+
self.layout.operator(
370+
WM_OT_MeshioObject.bl_idname,
371+
text="MeshIO Object")

bseq/panels.py

+21-2
Original file line numberDiff line numberDiff line change
@@ -191,16 +191,23 @@ def draw(self, context):
191191
if importer_prop.use_pattern:
192192
col2.prop(importer_prop, "pattern", text="")
193193
else:
194-
col2.prop(importer_prop, "fileseq", text="")
194+
split2 = col2.split(factor=0.75)
195+
col3 = split2.column()
196+
col4 = split2.column()
197+
col3.prop(importer_prop, "fileseq", text="")
198+
col4.operator("bseq.refreshseqs", icon="FILE_REFRESH")
195199

196200
col1.label(text="Use Relative Path")
197201
col2.prop(importer_prop, "relative", text="")
198202

199-
if importer_prop.relative is True:
203+
if importer_prop.relative:
200204
col1.label(text="Root Directory")
201205
col2.prop(importer_prop, "root_path", text="")
202206

203207
layout.operator("sequence.load")
208+
209+
layout.operator("wm.seq_import_batch")
210+
204211
split = layout.split()
205212
col1 = split.column()
206213
col2 = split.column()
@@ -227,7 +234,19 @@ def draw(self, context):
227234
col2.prop(importer_prop, "print", text="")
228235
col1.label(text="Auto refresh all the sequence every frame")
229236
col2.prop(importer_prop, "auto_refresh", text="")
237+
col1.label(text="Use custom transformation matrix")
238+
col2.prop(importer_prop, "use_custom_transform", text="")
239+
240+
if importer_prop.use_custom_transform:
241+
box.label(text="Location:")
242+
box.prop(importer_prop, "custom_location", text="")
243+
244+
box.label(text="Rotation:")
245+
box.prop(importer_prop, "custom_rotation", text="")
230246

247+
box.label(text="Scale:")
248+
box.prop(importer_prop, "custom_scale", text="")
249+
231250

232251
class BSEQ_Templates(bpy.types.Menu):
233252
'''

bseq/properties.py

+29-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import bpy
22
from .callback import *
3-
3+
from mathutils import Matrix
44

55
class BSEQ_scene_property(bpy.types.PropertyGroup):
66
path: bpy.props.StringProperty(name="Directory",
@@ -22,6 +22,10 @@ class BSEQ_scene_property(bpy.types.PropertyGroup):
2222
default=False)
2323
pattern: bpy.props.StringProperty(name="Pattern",
2424
description="You can specify the pattern here, in case the sequence can't be deteced.")
25+
26+
file_paths: bpy.props.StringProperty(name="File",
27+
subtype="FILE_PATH",
28+
description="Select a root folder for all relative paths. When not set the current filename is used.")
2529

2630
selected_obj_deselectall_flag: bpy.props.BoolProperty(default=True,
2731
description="the flag to determine whether call deselect all or not ")
@@ -48,7 +52,27 @@ class BSEQ_scene_property(bpy.types.PropertyGroup):
4852
auto_refresh: bpy.props.BoolProperty(name='auto refresh',
4953
description="whether or not to auto refresh all the sequence every frame",
5054
default=False)
55+
56+
use_custom_transform: bpy.props.BoolProperty(name='Use custom transformation matrix',
57+
description="Whether or not to use a custom transformation matrix",
58+
default=False)
5159

60+
custom_location: bpy.props.FloatVectorProperty(name='Custom Location',
61+
description='Set custom location vector',
62+
size=3,
63+
subtype="TRANSLATION")
64+
65+
custom_rotation: bpy.props.FloatVectorProperty(name='Custom Rotation',
66+
description='Set custom rotation vector',
67+
size=3,
68+
subtype="EULER",
69+
default=[0,0,0])
70+
71+
custom_scale: bpy.props.FloatVectorProperty(name='Custom Scale',
72+
description='Set custom scaling vector',
73+
size=3,
74+
subtype="COORDINATES",
75+
default=[1,1,1])
5276

5377
class BSEQ_obj_property(bpy.types.PropertyGroup):
5478
init: bpy.props.BoolProperty(default=False)
@@ -59,7 +83,10 @@ class BSEQ_obj_property(bpy.types.PropertyGroup):
5983
use_relative: bpy.props.BoolProperty(default=False)
6084
pattern: bpy.props.StringProperty()
6185
frame: bpy.props.IntProperty()
62-
86+
initial_transform_matrix: bpy.props.FloatVectorProperty(name='Custom Transformation Matrix',
87+
description='Set custom transformation',
88+
size=16,
89+
subtype="MATRIX")
6390

6491
# set this property for mesh, not object (maybe change later?)
6592
class BSEQ_mesh_property(bpy.types.PropertyGroup):

0 commit comments

Comments
 (0)