Skip to content

Commit

Permalink
Added initial io between 3ds max and maya.
Browse files Browse the repository at this point in the history
Refactored environment to be managed with batch file.
Initial test with vcpkg was problematic, will revisit this in the future.
  • Loading branch information
munkybutt committed May 30, 2023
1 parent 4be1dd2 commit 7f7f0da
Show file tree
Hide file tree
Showing 47 changed files with 1,205 additions and 1,079 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -440,9 +440,10 @@ MigrationBackup/


PyProjects/3rdParty/
PYProjects/skin_plus_plus_test/dcc_test_files/skin_data/mesh_low.skin
PyProjects/source/vcpkg_installed/
PyProjects/venv37/
PyProjects/venv39/
temp.cpp
*.max
*.mb
PYProjects/skin_plus_plus_test/dcc_test_files/skin_data/mesh_low.skin
8 changes: 8 additions & 0 deletions PYProjects/.projects/SkinPlusPlus.sublime-project
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"folders":
[
{
"path": "../../../SkinPlusPlus",
}
],
}
4 changes: 0 additions & 4 deletions PYProjects/create_venv_37.bat

This file was deleted.

4 changes: 0 additions & 4 deletions PYProjects/create_venv_39.bat

This file was deleted.

125 changes: 105 additions & 20 deletions PYProjects/skin_plus_plus/__init__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
from __future__ import annotations

import importlib
import os
import pathlib
import sys

current_dcc = None

get_skin_data = None
set_skin_weights = None
skin_plus_plus_py = None
# get_vertex_positions = None

__py_version__ = f"{sys.version_info.major}{sys.version_info.minor}"


def __get_environmet_core(python_version: str):
import importlib
import pathlib
def __get_skin_plus_plus_py(python_version: str, debug: bool = False):
global skin_plus_plus_py

debug = bool(os.environ.get("SKIN_PLUS_PLUS_DEBUG", False)) or debug
if debug:
python_version = f"debug_{python_version}"

current_directory = pathlib.Path(__file__).parent
sub_module_path = current_directory / f"py/{python_version}"
Expand All @@ -20,11 +29,15 @@ def __get_environmet_core(python_version: str):
raise FileNotFoundError(f"Unsupported Python version!")

import_path = f"skin_plus_plus.py.{python_version}.skin_plus_plus_py"
core = importlib.import_module(import_path)
return core
print(f"import_path: {import_path}")
if "skin_plus_plus_py" in sys.modules:
del sys.modules["skin_plus_plus_py"]

skin_plus_plus_py = importlib.import_module(import_path)
return skin_plus_plus_py


def __get_dcc_backend(dcc: str, version: str, api: str):
def __get_dcc_backend(dcc:str, version: str, api:str):
current_directory = pathlib.Path(__file__).parent
sub_module_name = f"skin_plus_plus_{api}_{version}"
sub_module_path = current_directory / f"dccs/{dcc}" / sub_module_name
Expand All @@ -46,43 +59,115 @@ def __get_dcc_backend(dcc: str, version: str, api: str):
return backend


def set_debug(value: bool):
"""
Toggle debug mode on or off.
---
Debug mode is off by default.
Arguments:
----------
- `value`: Boolean to control the state of debug mode.
Returns:
--------
- `None`
"""
__get_skin_plus_plus_py(__py_version__, debug=value)


# DO NOT REMOVE - Required for access to SkinData class:
__version = f"{sys.version_info.major}{sys.version_info.minor}"
__get_environmet_core(__version)
__get_skin_plus_plus_py(__py_version__)



executable = sys.executable.lower()
if "3ds max" in executable:
from pymxs import runtime as mxRt

version_info = mxRt.MaxVersion()
version_number = version_info[0]
version_number = version_info[7]
__get_dcc_backend("max", version_number, "pymxs")
current_dcc = "max"


elif "maya" in executable:
from pymel import versions # type: ignore
from pymel import versions

version = str(versions.current())[:4]
__get_dcc_backend("maya", version, "pymaya")
current_dcc = "maya"

else:
raise RuntimeError(f"Unsupported executable: {executable}")


from .core import export_skin_data
from .core import import_skin_data
_typing = False
if _typing:
from typing import Any

from . import dccs
from . import py

from . import core
from . import io
from . import mesh

from .core import export_skin_data
from .core import import_skin_data
from .core import FileType

from .io import save
from .io import load
from .io import max_to_maya
from .io import maya_to_max
del _typing


# this avoids having to import every sub-package to find where
# the object should be imported from:
_object_import_map = {
"export_skin_data": "core",
"import_skin_data": "core",
"FileType": "core",
"save": "io",
"load": "io",
"max_to_maya": "io",
"maya_to_max": "io",
}


def __getattr__(name: str) -> Any:
if name in _object_import_map:
package_name = _object_import_map[name]
module = importlib.import_module(f"{__package__}.{package_name}")
return getattr(module, name)

return importlib.import_module(f"{__package__}.{name}")


__all__ = (
"export_skin_data",
"dccs",
"py",

"core",
"io",
"mesh",

"current_dcc",

"get_skin_data",
# "get_vertex_positions",
"import_skin_data",
"set_skin_weights",
)

"set_debug",

if __name__ == "__main__":
import skin_plus_plus
"export_skin_data",
"import_skin_data",
"FileType",

importlib.reload(skin_plus_plus)
"save",
"load",
"max_to_maya",
"maya_to_max"
)
55 changes: 55 additions & 0 deletions PYProjects/skin_plus_plus/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import numpy as np
import numpy.typing as np_typing

from .core import FileType as _FileType
from .core import export_skin_data as _export_skin_data
from .core import import_skin_data as _import_skin_data
from .io import save as _save
from .io import load as _load
from .io import max_to_maya as _max_to_maya
from .io import maya_to_max as _maya_to_max


FileType = _FileType
export_skin_data = _export_skin_data
import_skin_data = _import_skin_data
save = _save
load = _load
max_to_maya = _max_to_maya
maya_to_max = _maya_to_max


current_dcc: str = ...
"""
The name of the current DCC.
"""


class SkinData:
"""
Class containing data for a given skin object.
---
This class is a wrapped c++ struct exposed to Python with Pybind11.
# Note: It cannot be extended!
---
Attributes:
-----------
- `bone_names`: The names of the bones in the skin object.
- `bone_ids`: The ids of each influence assigned to each vertext.
These are used to map to the bone names.
- `weights`: The weights of each influence assigned to each vertext.
- `positions`: The positions of each vertex.
"""
bone_names: list[str]
bone_ids: np_typing.NDArray[np.int64]
weights: np_typing.NDArray[np.float32]
positions: np_typing.NDArray[np.float32]


def get_skin_data(mesh_name: str) -> SkinData: ...
def set_skin_weights(mesh_name: str, skin_data: SkinData) -> bool: ...
92 changes: 83 additions & 9 deletions PYProjects/skin_plus_plus/core.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,123 @@
from __future__ import annotations

from . import get_skin_data

# from . import get_vertex_positions
from . import set_skin_weights

import enum
import json
import numpy
import pathlib
import pickle
import skin_plus_plus

# import scipy.sparse
import pathlib

_typing = False
if _typing:
pass
del _typing


class ImportType(enum.IntEnum):
class ImportType(enum.Enum):
order = 0
nearest = 1
nearest_n = 2
barycentric = 3


def export_skin_data(mesh_name: str, path: pathlib.Path):
class FileType(enum.Enum):
"""
Enum to specify the type of file to save skin data as.
Arguments:
----------
- `json`
- `pickle`
"""
json = 0
pickle = 1


def export_skin_data(mesh_name: str, path: pathlib.Path, file_type: FileType = FileType.pickle):
"""
Get skin data from the given mesh and save it to disk.
"""

skin_data = get_skin_data(mesh_name)
with open(path, "wb") as path_data:
pickle.dump(skin_data, path_data)
if not path.parent.exists():
path.parent.mkdir(parents=True)

if file_type == FileType.pickle:
if path.suffix != ".skpp":
path = path.with_suffix(".skpp")

with open(path, "wb") as file:
pickle.dump(skin_data, file)

def import_skin_data(mesh_name: str, path: pathlib.Path, import_type: ImportType = ImportType.order):
elif file_type == FileType.json:
if path.suffix != ".skpp-json":
path = path.with_suffix(".skpp-json")

with open(path, "w") as file:
weights = skin_data.weights.copy()
weights[numpy.isnan(weights)] = 0
_skin_data = {
"bone_names": skin_data.bone_names,
"bone_ids": skin_data.bone_ids.tolist(),
"weights": weights.tolist(),
"positions": skin_data.positions.tolist(),
}
json.dump(_skin_data, file, indent=4)

print(f"Exported '{mesh_name}' skin data to: {path}")


def import_skin_data(
mesh_name: str,
path: pathlib.Path,
file_type: FileType = FileType.pickle,
import_type: ImportType = ImportType.order,
):
"""
Load skin data from disk and apply it to the given mesh.
"""

with open(path, "rb") as path_data:
skin_data = pickle.load(path_data)
if file_type == FileType.pickle:
if path.suffix != ".skpp":
path = path.with_suffix(".skpp")

if not path.exists():
raise IOError(
f"File path does not exist: {path} - check mesh is named correctly: {mesh_name}"
)

with open(path, "rb") as file:
skin_data = pickle.load(file)

elif file_type == FileType.json:
if path.suffix != ".skpp-json":
path = path.with_suffix(".skpp-json")

if not path.exists():
raise IOError(
f"File path does not exist: {path} - check mesh is named correctly: {mesh_name}"
)

with open(path, "r") as file:
data = json.load(file)
skin_data = skin_plus_plus.skin_plus_plus_py.SkinData(
tuple(data["bone_names"]),
tuple(data["bone_ids"]),
tuple(data["weights"]),
tuple(data["positions"])
)

return set_skin_weights(mesh_name, skin_data)

# if import_type == ImportType.nearest:
# vertex_positions = get_vertex_positions(mesh_name)
# kd_tree = scipy.sparse.ckdtree(skin_data.positions)
# matching_indices = kd_tree.query(vertex_positions)

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file.
Binary file not shown.
Empty file.
Binary file not shown.
Binary file not shown.
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading

0 comments on commit 7f7f0da

Please sign in to comment.