Skip to content

Version 0.1.4 #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Apr 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
25216ea
Added Enable/Disable All and the option to select a root directory fo…
Dec 7, 2022
2344d8e
Added first version of bin file format
justo46 Mar 7, 2023
0f575de
Added bin.py
justo46 Mar 9, 2023
cf201a8
Fixed byte errors and store the rest of the bytes in an (unused) list
justo46 Mar 13, 2023
66a2180
Added 1 main commit
justo46 Mar 13, 2023
7cb6fe1
Added main commit
justo46 Mar 13, 2023
f410be3
Merge commit
justo46 Mar 13, 2023
5f8be5a
Added a button to select multiple sequences
justo46 Mar 15, 2023
30cd9b4
Added the functionality to import multiple sequences (in top menu and…
justo46 Mar 27, 2023
ea852b9
Added object property for initial transformation matrix, made for mul…
justo46 Mar 27, 2023
341cbad
Added install script
justo46 Mar 29, 2023
fb9f982
Fixed keyframe animation system to work with other transformations
justo46 Mar 29, 2023
f6c5ac2
Optimized adding meshio objects and added a button to refresh the seq…
justo46 Mar 29, 2023
c8d12e2
Fixed keyframe system for multiple files
justo46 Mar 31, 2023
31fc32b
Merge remote-tracking branch 'upstream/main'
justo46 Mar 31, 2023
7c276a0
Deleted install file from repo, since this is a local file.
justo46 Mar 31, 2023
53d9cfd
Resolved further changes
justo46 Mar 31, 2023
f38ae77
Updated version + test to import MeshIO Objects
justo46 Mar 31, 2023
1903ce3
Updated version
justo46 Mar 31, 2023
a7d5e99
Deleted some comments and made the code easier to read
justo46 Mar 31, 2023
d9bebfa
Further code improvements
justo46 Mar 31, 2023
785d229
Removed bin.py
justo46 Mar 31, 2023
7f70202
Deleted import of bin.py and corrected version
justo46 Mar 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"name": "Sequence Loader",
"description": "Loader for meshio supported mesh files/ simulation sequences",
"author": "Interactive Computer Graphics",
"version": (0, 1, 3),
"blender": (3, 1, 0),
"version": (0, 1, 4),
"blender": (3, 4, 0),
"warning": "",
"support": "COMMUNITY",
"category": "Import-Export",
Expand All @@ -21,6 +21,7 @@
bpy.context.preferences.filepaths.use_relative_paths = False

from bseq import *
from bseq.operators import menu_func_import

classes = [
BSEQ_obj_property,
Expand All @@ -44,6 +45,9 @@
BSEQ_OT_refresh_seq,
BSEQ_OT_disable_all,
BSEQ_OT_enable_all,
BSEQ_OT_refresh_sequences,
WM_OT_batchSequences,
WM_OT_MeshioObject
]


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

bpy.types.TOPBAR_MT_file_import.append(menu_func_import)

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


Expand Down
5 changes: 4 additions & 1 deletion bseq/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from bseq.utils import refresh_obj
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
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
from .properties import BSEQ_scene_property, BSEQ_obj_property, BSEQ_mesh_property
from .panels import BSEQ_UL_Obj_List, BSEQ_List_Panel, BSEQ_Settings, BSEQ_Import, BSEQ_Templates, BSEQ_UL_Att_List, draw_template
from .messenger import subscribe_to_selected, unsubscribe_to_selected
Expand Down Expand Up @@ -45,4 +45,7 @@ def BSEQ_initialize(scene):
"BSEQ_OT_refresh_seq",
"BSEQ_OT_disable_all",
"BSEQ_OT_enable_all",
"BSEQ_OT_refresh_sequences",
"WM_OT_batchSequences",
"WM_OT_MeshioObject"
]
73 changes: 59 additions & 14 deletions bseq/importer.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import bpy
import mathutils
import meshio
import traceback
import fileseq
import os
from .utils import show_message_box
import numpy as np
from mathutils import Matrix
Expand Down Expand Up @@ -51,6 +53,37 @@ def extract_faces(cell: meshio.CellBlock):
show_message_box(cell.type + " is unsupported mesh format yet")
return np.array([])

def has_keyframe(obj, attr):
animdata = obj.animation_data
if animdata is not None and animdata.action is not None:
for fcurve in animdata.action.fcurves:
if fcurve.data_path == attr:
return len(fcurve.keyframe_points) > 0
return False

def apply_transformation(meshio_mesh, obj, depsgraph):
# evaluate the keyframe animation system
eval_location = obj.evaluated_get(depsgraph).location if has_keyframe(obj, "location") else None
eval_scale = obj.evaluated_get(depsgraph).scale if has_keyframe(obj, "scale") else None

if has_keyframe(obj, "rotation_quaternion"):
eval_rotation = obj.evaluated_get(depsgraph).rotation_quaternion
elif has_keyframe(obj, "rotation_axis_angle"):
eval_rotation = obj.evaluated_get(depsgraph).rotation_axis_angle
elif has_keyframe(obj, "rotation_euler"):
eval_rotation = obj.evaluated_get(depsgraph).rotation_euler
else:
eval_rotation = None

eval_transform_matrix = mathutils.Matrix.LocRotScale(eval_location, eval_rotation, eval_scale)

# evaluate the rigid body transformations (only relevant for .bin format)
rigid_body_transformation = mathutils.Matrix.Identity(4)
if meshio_mesh.field_data.get("transformation_matrix") is not None:
rigid_body_transformation = meshio_mesh.field_data["transformation_matrix"]

# multiply everything together (with custom transform matrix)
obj.matrix_world = rigid_body_transformation @ obj.BSEQ.initial_transform_matrix @ eval_transform_matrix

def update_mesh(meshio_mesh, mesh):
# extract information from the meshio mesh
Expand All @@ -59,7 +92,8 @@ def update_mesh(meshio_mesh, mesh):
n_poly = 0
n_loop = 0
n_verts = len(mesh_vertices)

if n_verts == 0:
return
faces_loop_start = np.array([], dtype=np.uint64)
faces_loop_total = np.array([], dtype=np.uint64)
loops_vert_idx = np.array([], dtype=np.uint64)
Expand Down Expand Up @@ -136,8 +170,27 @@ def update_mesh(meshio_mesh, mesh):
mesh.use_auto_smooth = True
mesh.normals_split_custom_set_from_vertices(v)

# function to create a single meshio object
def create_meshio_obj(filepath):
meshio_mesh = None
try:
meshio_mesh = meshio.read(filepath)
except Exception as e:
show_message_box("Error when reading: " + filepath + ",\n" + traceback.format_exc(),
"Meshio Loading Error" + str(e),
icon="ERROR")

# create the object
name = os.path.basename(filepath)
mesh = bpy.data.meshes.new(name)
object = bpy.data.objects.new(name, mesh)
update_mesh(meshio_mesh, object.data)
bpy.context.collection.objects.link(object)
bpy.ops.object.select_all(action="DESELECT")
bpy.context.view_layer.objects.active = object

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]])):

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]])):

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

# force to evaluate the keyframe animation system
obj.location = obj.evaluated_get(depsgraph).location
match obj.rotation_mode:
case "QUATERNION":
obj.rotation_quaternion = obj.evaluated_get(depsgraph).rotation_quaternion
case "AXIS_ANGLE":
obj.rotation_axis_angle = obj.evaluated_get(depsgraph).rotation_axis_angle
case _:
obj.rotation_euler = obj.evaluated_get(depsgraph).rotation_euler

obj.scale = obj.evaluated_get(depsgraph).scale
apply_transformation(meshio_mesh, obj, depsgraph)

76 changes: 74 additions & 2 deletions bseq/operators.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import bpy
from mathutils import Matrix
import fileseq
from .messenger import *
import traceback
from .utils import refresh_obj, show_message_box
from .importer import create_obj
from .importer import create_obj, create_meshio_obj
import numpy as np


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

create_obj(fs, importer_prop.relative, importer_prop.root_path)
transform_matrix = (Matrix.LocRotScale(
importer_prop.custom_location,
importer_prop.custom_rotation,
importer_prop.custom_scale)
if importer_prop.use_custom_transform else Matrix.Identity(4))

create_obj(fs, importer_prop.relative, importer_prop.root_path, transform_matrix=transform_matrix)
return {"FINISHED"}


Expand Down Expand Up @@ -297,3 +304,68 @@ def execute(self, context):
if obj.BSEQ.init and not obj.BSEQ.enabled:
obj.BSEQ.enabled = True
return {"FINISHED"}

class BSEQ_OT_refresh_sequences(bpy.types.Operator):
'''This operator refreshes all found sequences'''
bl_label = "" #"Refresh Found Sequences"
bl_idname = "bseq.refreshseqs"
bl_options = {"UNDO"}

def execute(self, context):
scene = context.scene
# call the update function of path by setting it to its own value
scene.BSEQ.path = scene.BSEQ.path
return {"FINISHED"}

from pathlib import Path
import meshio
from bpy_extras.io_utils import ImportHelper

class WM_OT_batchSequences(bpy.types.Operator, ImportHelper):
"""Batch Import Sequences"""
bl_idname = "wm.seq_import_batch"
bl_label = "Import multiple sequences"
bl_options = {'PRESET', 'UNDO'}

files: bpy.props.CollectionProperty(type=bpy.types.PropertyGroup)

def execute(self, context):
scene = context.scene
importer_prop = scene.BSEQ

folder = Path(self.filepath)
used_seqs = set()

for selection in self.files:
# Check if there exists a matching file sequence for every selection
fp = str(Path(folder.parent, selection.name))
seqs = fileseq.findSequencesOnDisk(str(folder.parent))
matching_seqs = [s for s in seqs if fp in list(s) and s not in used_seqs]

if matching_seqs:
transform_matrix = (Matrix.LocRotScale(importer_prop.custom_location, importer_prop.custom_rotation, importer_prop.custom_scale)
if importer_prop.use_custom_transform else Matrix.Identity(4))
create_obj(matching_seqs[0], False, importer_prop.root_path, transform_matrix=transform_matrix)
used_seqs.add(matching_seqs[0])
return {'FINISHED'}

class WM_OT_MeshioObject(bpy.types.Operator, ImportHelper):
"""Batch Import Meshio Objects"""
bl_idname = "wm.meshio_import_batch"
bl_label = "Import multiple Meshio objects"
bl_options = {'PRESET', 'UNDO'}

files: bpy.props.CollectionProperty(type=bpy.types.PropertyGroup)

def execute(self, context):
folder = Path(self.filepath)

for selection in self.files:
fp = Path(folder.parent, selection.name)
create_meshio_obj(str(fp))
return {'FINISHED'}

def menu_func_import(self, context):
self.layout.operator(
WM_OT_MeshioObject.bl_idname,
text="MeshIO Object")
23 changes: 21 additions & 2 deletions bseq/panels.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,16 +191,23 @@ def draw(self, context):
if importer_prop.use_pattern:
col2.prop(importer_prop, "pattern", text="")
else:
col2.prop(importer_prop, "fileseq", text="")
split2 = col2.split(factor=0.75)
col3 = split2.column()
col4 = split2.column()
col3.prop(importer_prop, "fileseq", text="")
col4.operator("bseq.refreshseqs", icon="FILE_REFRESH")

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

if importer_prop.relative is True:
if importer_prop.relative:
col1.label(text="Root Directory")
col2.prop(importer_prop, "root_path", text="")

layout.operator("sequence.load")

layout.operator("wm.seq_import_batch")

split = layout.split()
col1 = split.column()
col2 = split.column()
Expand All @@ -227,7 +234,19 @@ def draw(self, context):
col2.prop(importer_prop, "print", text="")
col1.label(text="Auto refresh all the sequence every frame")
col2.prop(importer_prop, "auto_refresh", text="")
col1.label(text="Use custom transformation matrix")
col2.prop(importer_prop, "use_custom_transform", text="")

if importer_prop.use_custom_transform:
box.label(text="Location:")
box.prop(importer_prop, "custom_location", text="")

box.label(text="Rotation:")
box.prop(importer_prop, "custom_rotation", text="")

box.label(text="Scale:")
box.prop(importer_prop, "custom_scale", text="")


class BSEQ_Templates(bpy.types.Menu):
'''
Expand Down
31 changes: 29 additions & 2 deletions bseq/properties.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import bpy
from .callback import *

from mathutils import Matrix

class BSEQ_scene_property(bpy.types.PropertyGroup):
path: bpy.props.StringProperty(name="Directory",
Expand All @@ -22,6 +22,10 @@ class BSEQ_scene_property(bpy.types.PropertyGroup):
default=False)
pattern: bpy.props.StringProperty(name="Pattern",
description="You can specify the pattern here, in case the sequence can't be deteced.")

file_paths: bpy.props.StringProperty(name="File",
subtype="FILE_PATH",
description="Select a root folder for all relative paths. When not set the current filename is used.")

selected_obj_deselectall_flag: bpy.props.BoolProperty(default=True,
description="the flag to determine whether call deselect all or not ")
Expand All @@ -48,7 +52,27 @@ class BSEQ_scene_property(bpy.types.PropertyGroup):
auto_refresh: bpy.props.BoolProperty(name='auto refresh',
description="whether or not to auto refresh all the sequence every frame",
default=False)

use_custom_transform: bpy.props.BoolProperty(name='Use custom transformation matrix',
description="Whether or not to use a custom transformation matrix",
default=False)

custom_location: bpy.props.FloatVectorProperty(name='Custom Location',
description='Set custom location vector',
size=3,
subtype="TRANSLATION")

custom_rotation: bpy.props.FloatVectorProperty(name='Custom Rotation',
description='Set custom rotation vector',
size=3,
subtype="EULER",
default=[0,0,0])

custom_scale: bpy.props.FloatVectorProperty(name='Custom Scale',
description='Set custom scaling vector',
size=3,
subtype="COORDINATES",
default=[1,1,1])

class BSEQ_obj_property(bpy.types.PropertyGroup):
init: bpy.props.BoolProperty(default=False)
Expand All @@ -59,7 +83,10 @@ class BSEQ_obj_property(bpy.types.PropertyGroup):
use_relative: bpy.props.BoolProperty(default=False)
pattern: bpy.props.StringProperty()
frame: bpy.props.IntProperty()

initial_transform_matrix: bpy.props.FloatVectorProperty(name='Custom Transformation Matrix',
description='Set custom transformation',
size=16,
subtype="MATRIX")

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