-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from losanc/main
First publish
- Loading branch information
Showing
33 changed files
with
3,991 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
__pycache__/ | ||
.DS_Store | ||
.vs_code/ | ||
*.zip | ||
|
||
# the following ignores are used to ignore the local softlink files | ||
# the extern folder won't be affected by this | ||
rich | ||
meshio | ||
future | ||
fileseq |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
|
||
[submodule "extern/meshio"] | ||
path = extern/meshio | ||
url = https://github.com/nschloe/meshio | ||
[submodule "extern/python-future"] | ||
path = extern/python-future | ||
url = https://github.com/PythonCharmers/python-future | ||
[submodule "extern/fileseq"] | ||
path = extern/fileseq | ||
url = https://github.com/justinfx/fileseq | ||
[submodule "extern/rich"] | ||
path = extern/rich | ||
url = https://github.com/Textualize/rich |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
![](images/logo.svg) | ||
|
||
Loading animation sequences with meshio and fileseq | ||
|
||
- [1. Installation](#1-installation) | ||
- [1.1 Build from source (optional)](#11-build-from-source-optional) | ||
- [1.2 Install Addon](#12-install-addon) | ||
- [1.3 FAQs](#13-faqs) | ||
- [2. How to use](#2-how-to-use) | ||
- [2. Load the animation sequence you want](#2-load-the-animation-sequence-you-want) | ||
- [2.1 Absolute vs. Relative Paths](#21-absolute-vs-relative-paths) | ||
- [2.2 Sequence List View](#22-sequence-list-view) | ||
- [2.2.1 Enable/ Disable](#221-enable-disable) | ||
- [2.2.1 Refresh Sequence](#221-refresh-sequence) | ||
- [2.3 Settings](#23-settings) | ||
- [2.3.1 Geometry Nodes](#231-geometry-nodes) | ||
- [2.3.2 Path Information](#232-path-information) | ||
- [2.3.3 Attributes Settings](#233-attributes-settings) | ||
- [2.3.4 Split Norm per Vertex](#234-split-norm-per-vertex) | ||
|
||
## 1. Installation | ||
|
||
### 1.1 Build from source (optional) | ||
|
||
1. Clone the project to download both project and dependencies | ||
|
||
```shell | ||
git clone https://www.graphics.rwth-aachen.de:9000/hhui/blendertool --recurse-submodules | ||
``` | ||
|
||
2. Build the installable `.zip` file by simply running the following command. This should produce a file called `blender_sequence_loader_{date}.zip`, where `{date}` is replaced with todays date. No other dependency other than standard python3 libraries are needed to build the addon. | ||
|
||
```shell | ||
python3 build_addon.py | ||
``` | ||
|
||
### 1.2 Install Addon | ||
|
||
After obtaining an installable `.zip` file either from the releases page or from manually building the addon, this should be installed into blender. For more information on how to install addons see [here](https://docs.blender.org/manual/en/latest/editors/preferences/addons.html#installing-add-ons) for more details. | ||
|
||
### 1.3 FAQs | ||
|
||
1. You may need to restart blender after enabling the addon for the first time, otherwise some functionality may not work. Technically this should not be **required**, but might be necessary in some untested versions of Blender. | ||
|
||
2. You may have to manually remove old versions of this addon (if present), before installing a new version into Blender. This should rarely be the case but might be the cause of some confusing error messages and issues. | ||
|
||
## 2. How to use | ||
|
||
DISCLAIMER: Some of the screenshots may not be up to date with the most recent version of the addon, especially with respect to the text and ordering of UI elements. | ||
|
||
After installing addon, you can find it in the toolbar, which is accessible here or toggled by pressing the `n` key. | ||
|
||
![start](images/0.png) | ||
|
||
Then you can find it here. | ||
|
||
![homepage](images/1.png) | ||
|
||
### 2. Load the animation sequence you want | ||
|
||
You can select the directory in which your data is located through the GUI by clicking the rightmost icon. It will open the default blender file explorer. Then you can go to the directory you want, for example, like image showed below. **You only need navigate to the directory and click "Accept". Files are shown but not selectable in this dialogue.** | ||
|
||
![selecticon](images/2.png) | ||
|
||
Then the addon will automatically try to detect the sequences in this directory, so that you simply select the sequence you want. If the desired sequence is not shown, you can switch to enter a manual pattern, where a single `@` character is used to denote a running frame index. | ||
|
||
![after_selecting](images/3.png) | ||
|
||
#### 2.1 Absolute vs. Relative Paths | ||
|
||
There is a small checkbox about whether to use `relative paths` or not. | ||
|
||
When toggled on, the blender file must be saved before loading the sequence. Then this sequence will be loaded using relative path from the location of the saved `.blend` file. As such, if you move the `.blend` file in conjunction with the data to another directory (keeping their relative locations the same) the sequence loader will still work. This is especially useful when working with cloud synchronized folders, whose absolute paths may be different on different computers. | ||
|
||
If toggled off (default), it will use absolute path to load the sequence. For this, the `.blend` file does not have to be saved in advance. | ||
|
||
![relative_path](images/4.png) | ||
|
||
#### 2.2 Sequence List View | ||
|
||
After the sequence being imported, it will be available in the `Imported Sequences` panel, with more settings being available in `Sequence Settings` panel once a sequence has been selected. | ||
|
||
![settings](images/5.png) | ||
|
||
By default, all supported file formats are simply imported as geometry (a collection of vertices, lines, triangles and quads). As such, you should be able to directly play/render the animation if it contains geometry. | ||
|
||
Note: When rendering the animation, please turn on the `Lock Interface`. This will prevent artifacts from occurring, especially if the user continues to operate the Blender interface during the render process. | ||
|
||
![lock interface](images/6.png) | ||
|
||
##### 2.2.1 Enable/ Disable | ||
|
||
It is possible to individually enable and disable sequences from updating when the animation frame changes. This is very useful when working with very large files or many sequences as it reduces the computational overhead of loading these sequences. | ||
`Enabled` means, that the sequence will be updated on frame change, and `Disabled` means that the sequence won't be updated on frame change. | ||
|
||
##### 2.2.1 Refresh Sequence | ||
|
||
`Refresh Sequence` can be useful when the sequence is imported while the data is still being generated and not yet complete. Refreshing the sequence can detect the frames added after being imported. | ||
|
||
#### 2.3 Settings | ||
|
||
#### 2.3.1 Geometry Nodes | ||
|
||
While all files are imported as plain geometry, we provide some templates that we have found to be incredibly useful for visualizing particle data. | ||
|
||
Applying the `Point Cloud` geometry node, the vertices of the mesh are converted to a point cloud, which can be rendered only by [cycles](https://docs.blender.org/manual/en/latest/render/cycles/introduction.html) and only as spheres. The exact geometry node setup can be seen in the geometry nodes tab and may be modified as desired, e.g. to set the particle radius. | ||
|
||
Applying `Instances` geometry nodes, the vertices of the mesh are converted to cubes, which can be rendered by both [eevee](https://docs.blender.org/manual/en/latest/render/eevee/index.html) and [cycles](https://docs.blender.org/manual/en/latest/render/cycles/introduction.html). The exact geometry node setup can be seen in the geometry nodes tab and may be modified as desired, e.g. to set the particle radius and to change the instanced geometry. **CAUTION: Because this node setup relies on the `Realize Instances` node, the memory usage increases extremely rapidly. Make sure to save the `.blend` file before attempting this, as Blender may run out of memory!!!** | ||
|
||
Applying the `Mesh` geometry node will restore the default geometry nodes, which simply display the imported geometry as it is. | ||
|
||
Notes: | ||
|
||
1. `Instances` is super memory hungry compared with `Point Cloud`. | ||
2. After applying `Point Cloud` or `Instances` geometry nodes, you need to assign the material inside the geometry nodes. So to save your work, you can simply assign the material here, then apply the `Point Cloud` or `Instances` geometry nodes. | ||
3. To access the attributes for shading, use the `Attribute` node in the Shader Editor and simply specify the attribute string. The imported attributes can be seen in the spreadsheet browser of the Geometry Nodes tab and are also listed in the addon UI. | ||
|
||
![material](images/7.png) | ||
|
||
#### 2.3.2 Path Information | ||
|
||
This shows the path of the sequence for debugging purposes, however it's not editable. | ||
|
||
#### 2.3.3 Attributes Settings | ||
|
||
This panel shows the available **Vertex Attributes**, it's not editable. | ||
|
||
Note: In order to avoid conflicts with Blenders built-in attributes, all the attributes names are renamed by prefixing `bseq_`. For example, `id` -> `bseq_id`. Keep this in mind when accessing attributes in the shader editor. | ||
|
||
#### 2.3.4 Split Norm per Vertex | ||
|
||
We also provide the ability to use a per-vertex vector attribute as custom normals for shading. | ||
For more details check the official documentation [here](https://docs.blender.org/manual/en/latest/modeling/meshes/structure.html#modeling-meshes-normals-custom). | ||
|
||
Note: the addon does not check if the selected attribute is suitable for normals or not. E.g. if the data type of the attribute is int instead of float, then Blender will simply give a runtime error. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
bl_info = { | ||
"name": "Sequence Loader", | ||
"description": "Loader for meshio supported mesh files/ simulation sequences", | ||
"author": "Interactive Computer Graphics", | ||
"version": (1, 0), | ||
"blender": (3, 1, 0), | ||
"warning": "", | ||
"support": "COMMUNITY", | ||
"category": "Import-Export", | ||
} | ||
|
||
import bpy | ||
import os | ||
import sys | ||
|
||
current_folder = os.path.dirname(os.path.abspath(__file__)) | ||
if current_folder not in sys.path: | ||
sys.path.append(current_folder) | ||
|
||
if bpy.context.preferences.filepaths.use_relative_paths == True: | ||
bpy.context.preferences.filepaths.use_relative_paths = False | ||
|
||
from simloader import * | ||
|
||
classes = [ | ||
SIMLOADER_obj_property, | ||
SIMLOADER_scene_property, | ||
SIMLOADER_mesh_property, | ||
SIMLOADER_OT_load, | ||
SIMLOADER_OT_edit, | ||
SIMLOADER_OT_resetpt, | ||
SIMLOADER_OT_resetins, | ||
SIMLOADER_OT_resetmesh, | ||
SIMLOADER_Import, | ||
SIMLOADER_List_Panel, | ||
SIMLOADER_UL_Obj_List, | ||
SIMLOADER_Settings, | ||
SIMLOADER_Templates, | ||
SIMLOADER_UL_Att_List, | ||
SIMLOADER_OT_set_as_split_norm, | ||
SIMLOADER_OT_remove_split_norm, | ||
SIMLOADER_OT_disable_selected, | ||
SIMLOADER_OT_enable_selected, | ||
SIMLOADER_OT_refresh_seq, | ||
] | ||
|
||
|
||
def register(): | ||
bpy.app.handlers.load_post.append(SIMLOADER_initialize) | ||
for cls in classes: | ||
bpy.utils.register_class(cls) | ||
bpy.types.TEXT_MT_templates.append(draw_template) | ||
bpy.types.Scene.SIMLOADER = bpy.props.PointerProperty(type=SIMLOADER_scene_property) | ||
bpy.types.Object.SIMLOADER = bpy.props.PointerProperty(type=SIMLOADER_obj_property) | ||
bpy.types.Mesh.SIMLOADER = bpy.props.PointerProperty(type=SIMLOADER_mesh_property) | ||
|
||
|
||
# manually call this function once | ||
# so when addon being installed, it can run correctly | ||
# because scene is not used, so pass None into it | ||
SIMLOADER_initialize(None) | ||
|
||
def unregister(): | ||
for cls in classes: | ||
bpy.utils.unregister_class(cls) | ||
bpy.types.TEXT_MT_templates.remove(draw_template) | ||
del bpy.types.Scene.SIMLOADER | ||
del bpy.types.Object.SIMLOADER | ||
bpy.app.handlers.load_post.remove(SIMLOADER_initialize) | ||
unsubscribe_to_selected() | ||
|
||
|
||
if __name__ == "__main__": | ||
|
||
# unregister() | ||
register() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from .mzd import readMZD_to_bpymesh, readMZD_to_meshio | ||
from .bgeo import readbgeo_to_meshio | ||
|
||
additional_format_loader = {'bgeo': readbgeo_to_meshio, 'mzd': readMZD_to_meshio} | ||
|
||
__all__ = [ | ||
readMZD_to_bpymesh, readMZD_to_meshio, readbgeo_to_meshio, additional_format_loader | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import gzip | ||
import numpy as np | ||
import meshio | ||
|
||
|
||
def readbgeo_to_meshio(filepath): | ||
with gzip.open(filepath, 'r') as file: | ||
byte = file.read(5) | ||
if byte != b"BgeoV": | ||
raise Exception('not bgeo file format') | ||
byte = file.read(4) | ||
version = int.from_bytes(byte, byteorder="big") | ||
if version != 5: | ||
raise Exception('bgeo file not version 5') | ||
|
||
header = {} | ||
point_attributes = {} | ||
point_attributes_names = [] | ||
point_attributes_sizes = [] | ||
point_attributes_types = [] | ||
position = None | ||
|
||
byte = file.read(4) | ||
header['nPoints'] = int.from_bytes(byte, byteorder="big") | ||
|
||
byte = file.read(4) | ||
header['nPrims'] = int.from_bytes(byte, byteorder="big") | ||
|
||
byte = file.read(4) | ||
header['nPointGroups'] = int.from_bytes(byte, byteorder="big") | ||
|
||
byte = file.read(4) | ||
header['nPrimGroups'] = int.from_bytes(byte, byteorder="big") | ||
|
||
byte = file.read(4) | ||
header['nPointAttrib'] = int.from_bytes(byte, byteorder="big") | ||
|
||
byte = file.read(4) | ||
header['nVertexAttrib'] = int.from_bytes(byte, byteorder="big") | ||
|
||
byte = file.read(4) | ||
header['nPrimAttrib'] = int.from_bytes(byte, byteorder="big") | ||
|
||
byte = file.read(4) | ||
header['nAttrib'] = int.from_bytes(byte, byteorder="big") | ||
|
||
particle_size = 4 | ||
|
||
for _ in range(header['nPointAttrib']): | ||
byte = file.read(2) | ||
namelength = int.from_bytes(byte, byteorder="big") | ||
name_binary = file.read(namelength) | ||
name = name_binary.decode('utf-8') | ||
point_attributes_names.append(name) | ||
|
||
byte = file.read(2) | ||
size = int.from_bytes(byte, byteorder="big") | ||
point_attributes_sizes.append(size) | ||
particle_size += size | ||
|
||
byte = file.read(4) | ||
houdni_type = int.from_bytes(byte, byteorder="big") | ||
if houdni_type == 0: | ||
point_attributes_types.append('FLOAT') | ||
# read default value | ||
# not going to do anything about it | ||
byte = file.read(size * 4) | ||
elif houdni_type == 1: | ||
point_attributes_types.append('INT') | ||
# read default value | ||
# not going to do anything about it | ||
byte = file.read(size * 4) | ||
elif houdni_type == 5: | ||
point_attributes_types.append('VECTOR') | ||
# read default value | ||
# not going to do anything about it | ||
byte = file.read(size * 4) | ||
else: | ||
raise Exception('houdni_type unknown/ unsupported') | ||
byte = file.read(particle_size * header['nPoints'] * 4) | ||
# > means big endian | ||
attribute_data = np.frombuffer(byte, dtype='>f') | ||
attribute_data = np.reshape(attribute_data, (header['nPoints'], particle_size)) | ||
# the first 3 column is its position data | ||
position = attribute_data[:, :3] | ||
# the 4th column is homogeneous coordiante, which is all 1, and will be ignored | ||
|
||
current_attribute_start_point = 4 | ||
for i in range(header['nPointAttrib']): | ||
if point_attributes_types[i] == 'FLOAT': | ||
point_attributes[point_attributes_names[i]] = attribute_data[:, current_attribute_start_point] | ||
current_attribute_start_point += 1 | ||
elif point_attributes_types[i] == 'VECTOR': | ||
point_attributes[ | ||
point_attributes_names[i]] = attribute_data[:, | ||
current_attribute_start_point:current_attribute_start_point + 3] | ||
current_attribute_start_point += 3 | ||
elif point_attributes_types[i] == 'INT': | ||
data = (attribute_data[:, current_attribute_start_point]).tobytes() | ||
# > means big endian | ||
point_attributes[point_attributes_names[i]] = np.frombuffer(data, dtype='>i') | ||
current_attribute_start_point += 1 | ||
remaining = file.read() | ||
if not remaining == b'\x00\xff': | ||
raise Exception("file didn't end") | ||
return meshio.Mesh(position, [('vertex', [])], point_data=point_attributes) | ||
|
Oops, something went wrong.