Skip to content

Commit

Permalink
Added initial support for getting and setting skin data by sub set of…
Browse files Browse the repository at this point in the history
… vertex ids
  • Loading branch information
munkybutt committed Mar 30, 2024
1 parent eb4990d commit e01789d
Show file tree
Hide file tree
Showing 10 changed files with 267 additions and 152 deletions.
9 changes: 5 additions & 4 deletions PYProjects/skin_plus_plus/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
from __future__ import annotations

from . import _types
from .dccs import core as _dccs_core
from . import skin_plus_plus_py
from .core import FileType as _FileType
from .core import export_skin_data as _export_skin_data
from .core import FileType as _FileType
from .core import import_skin_data as _import_skin_data
from .io import save as _save
from .dccs import core as _dccs_core
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
from .io import save as _save
from typing import Sequence


FileType = _FileType
Expand All @@ -31,7 +32,7 @@ The interface to the current Host

SkinData = skin_plus_plus_py.SkinData

def extract_skin_data(node: _types.T_Node) -> SkinData:
def extract_skin_data(node: _types.T_Node, vertex_ids: Sequence[int] | None = None) -> SkinData:
"""
Extract skin data from the given DCC node.
"""
Expand Down
38 changes: 26 additions & 12 deletions PYProjects/skin_plus_plus/_types.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
from pymel.core import nodetypes as pm_ntypes
from pymxs import runtime as mxrt
from typing import Callable
from typing import TypeVar
from typing import Union

from . import SkinData

T_Node = TypeVar("T_Node", mxrt.Node, pm_ntypes.DagNode)
T_Handle = Union[int, str]
T_CExSD = Callable[[T_Handle], SkinData]
T_CApSD = Callable[[T_Handle, SkinData], None]
from __future__ import annotations

_typing = False
if _typing:
from pymel.core import nodetypes as pm_ntypes
from pymxs import runtime as mxrt
from typing import Callable
from typing import Sequence
from typing import TypeVar
from typing import Union
from typing import Protocol

from . import SkinData

T_Node = TypeVar("T_Node", mxrt.Node, pm_ntypes.DagNode)
T_Handle = Union[int, str]
T_CApSD = Callable[[T_Handle, SkinData], None]


class T_EXSD(Protocol):
"""
Signature for extrac skin data function
"""
def __call__(self, handle: T_Handle, vertex_ids: Sequence[int] | None = None) -> SkinData: ...

del _typing
9 changes: 5 additions & 4 deletions PYProjects/skin_plus_plus/dccs/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@

from .. import _types
from .. import SkinData
from typing import Sequence

del _typing


class IHost(metaclass=abc.ABCMeta):
_extract_skin_data: _types.T_CExSD
_extract_skin_data: _types.T_EXSD
_apply_skin_data: _types.T_CApSD
_get_vertex_positions: _types.Callable

Expand Down Expand Up @@ -50,7 +51,7 @@ def _get_dcc_backend(self):
# if is_reloading:
# importlib.reload(backend)

self._extract_skin_data: _types.T_CExSD = backend.extract_skin_data
self._extract_skin_data: _types.T_EXSD = backend.extract_skin_data
self._apply_skin_data: _types.T_CApSD = backend.apply_skin_data
self._get_vertex_positions: _types.Callable = backend.get_vertex_positions

Expand Down Expand Up @@ -87,9 +88,9 @@ def get_node_handle(self, node: _types.T_Node) -> _types.T_Handle:
"""


def extract_skin_data(self, node: _types.T_Node) -> SkinData:
def extract_skin_data(self, node: _types.T_Node, vertex_ids: Sequence[int] | None = None) -> SkinData:
handle = self.get_node_handle(node)
return self._extract_skin_data(handle)
return self._extract_skin_data(handle, vertex_ids=vertex_ids)

def apply_skin_data(self, node: _types.T_Node, skin_data: SkinData):
handle = self.get_node_handle(node)
Expand Down
Binary file not shown.
Binary file modified PYProjects/skin_plus_plus/py/310/skin_plus_plus_py.pyd
Binary file not shown.
10 changes: 5 additions & 5 deletions PYProjects/skin_plus_plus_test/skin_plus_plus_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __setup__():
if str(current_file) == "<maya console>":
# maya is a piece of shit:
current_file = pathlib.Path(
r"D:\Code\Git\SkinPlusPlus\PYProjects\skin_plus_plus_test\skin_plus_plus_test.py"
r"C:\Users\Shea.Richardson\Desktop\Git\SkinPlusPlus-Main\PYProjects\skin_plus_plus_test\skin_plus_plus_test.py"
)
current_directory = current_file.parent
site.addsitedir(str(current_directory.parent))
Expand All @@ -30,8 +30,6 @@ def __setup__():

import skin_plus_plus

print(skin_plus_plus.current_host)

# skin_plus_plus.set_debug(False)

if __name__ == "__main__":
Expand Down Expand Up @@ -716,7 +714,9 @@ def add_bones():
if __name__ == "__main__":
pass

node = skin_plus_plus.current_host.get_selection()[0]
# skin_data = skin_plus_plus.extract_skin_data(node)
node = skin_plus_plus.current_host_interface.get_selection()[0]
# skin_data = skin_plus_plus.extract_skin_data(node, vertex_ids=[0, 1, 2])
# print(skin_data)
# print(skin_data.bone_ids)
# print(skin_data.weights)
skin_plus_plus.apply_skin_data(node, skin_data)
97 changes: 83 additions & 14 deletions PYProjects/source/skin_plus_plus_py/headers/skin_plus_plus_py.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ typedef std::vector<std::string> BoneNamesVector;
typedef eg::MatrixXi BoneIDsMatrix;
typedef eg::MatrixXd WeightsMatrix;
typedef eg::MatrixXd PositionMatrix;
typedef eg::Matrix<int, 1, -1> VertexIDsMatrix;
typedef unsigned int UINT;


Expand Down Expand Up @@ -83,6 +84,8 @@ struct SortedBoneNameData
{
std::vector<UINT> sortedBoneIDs;
std::vector<std::string> unfoundBoneNames;
UINT highestBoneID;
UINT lowestBoneID;
SortedBoneNameData(UINT boneCount)
{
sortedBoneIDs = std::vector<UINT>(boneCount);
Expand All @@ -99,42 +102,108 @@ struct PySkinData final
BoneIDsMatrix boneIDs;
WeightsMatrix weights;
PositionMatrix positions;
std::optional<VertexIDsMatrix> vertexIDs = std::nullopt;

PySkinData() {}

PySkinData(UINT vertexCount, UINT maxInfluenceCount)
PySkinData(VertexIDsMatrix vertexIDs, UINT maxInfluenceCount) {
const eg::Index vertexCount = vertexIDs.size();
this->boneIDs = BoneIDsMatrix::Constant(vertexCount, maxInfluenceCount, -1);
this->weights = WeightsMatrix::Zero(vertexCount, maxInfluenceCount);
this->positions = PositionMatrix(vertexCount, 3);
this->vertexIDs = vertexIDs;
}

PySkinData(UINT vertexCount, UINT maxInfluenceCount, std::optional<VertexIDsMatrix> vertexIDs = std::nullopt)
{
this->boneIDs = BoneIDsMatrix::Constant(vertexCount, maxInfluenceCount, -1);
this->weights = WeightsMatrix::Zero(vertexCount, maxInfluenceCount);
this->positions = PositionMatrix(vertexCount, 3);
if (vertexIDs.has_value())
{
this->vertexIDs = vertexIDs.value();
}
}

PySkinData(BoneNamesVector boneNames, BoneIDsMatrix boneIDs, WeightsMatrix weights, PositionMatrix positions)
PySkinData(BoneNamesVector boneNames, BoneIDsMatrix boneIDs, WeightsMatrix weights, PositionMatrix positions, std::optional<VertexIDsMatrix> vertexIDs = std::nullopt)
{
this->boneNames = boneNames;
this->boneIDs = boneIDs;
this->weights = weights;
this->positions = positions;
this->setInternalState(boneNames, boneIDs, weights, positions, vertexIDs);
}

PySkinData(py::tuple data)
{
this->setInternalState(data);
}

PySkinData(const PySkinData& skinData)
{
if (skinData.vertexIDs.has_value())
{
this->setInternalState(
skinData.boneNames,
BoneIDsMatrix(skinData.boneIDs),
WeightsMatrix(skinData.weights),
PositionMatrix(skinData.positions),
VertexIDsMatrix(skinData.vertexIDs.value())
);
}
else
{
this->setInternalState(
skinData.boneNames,
BoneIDsMatrix(skinData.boneIDs),
WeightsMatrix(skinData.weights),
PositionMatrix(skinData.positions)
);
}
}

~PySkinData() {}


// Set the internal state of the object, replacing all data.
// The tuple structure is: (boneNames, boneIDs, weights, positions).
void setInternalState(py::tuple data)
void setInternalState(BoneNamesVector boneNames, BoneIDsMatrix boneIDs, WeightsMatrix weights, PositionMatrix positions, std::optional<VertexIDsMatrix> vertexIDs = std::nullopt)
{
if (data.size() != 4)
throw std::runtime_error("Invalid state - The tuple structure is: (bone_names, bone_ids, weights, positions)");
this->boneNames = boneNames;
this->boneIDs = boneIDs;
this->weights = weights;
this->positions = positions;
if (vertexIDs.has_value())
{
this->vertexIDs = vertexIDs.value();
}
}

this->boneNames = py::cast<BoneNamesVector>(data[0]);
this->boneIDs = py::cast<BoneIDsMatrix>(data[1]);
this->weights = py::cast<WeightsMatrix>(data[2]);
this->positions = py::cast<PositionMatrix>(data[3]);
// Set the internal state of the object, replacing all data.
// The tuple structure is: (boneNames, boneIDs, weights, positions, vertexIDs).
void setInternalState(py::tuple data)
{
auto dataSize = data.size();
if (dataSize == 5 && !py::isinstance<py::none>(data[4]))
{
this->setInternalState(
py::cast<BoneNamesVector>(data[0]),
py::cast<BoneIDsMatrix>(data[1]),
py::cast<WeightsMatrix>(data[2]),
py::cast<PositionMatrix>(data[3]),
py::cast<VertexIDsMatrix>(data[4])
);
}
else if (dataSize >= 4)
{
this->setInternalState(
py::cast<BoneNamesVector>(data[0]),
py::cast<BoneIDsMatrix>(data[1]),
py::cast<WeightsMatrix>(data[2]),
py::cast<PositionMatrix>(data[3])
);
}
else
{
throw std::runtime_error(
"Invalid state - The tuple structure is: (bone_names, bone_ids, weights, positions, vertexIDs = (optional)"
);
}
}

// Set a new maximum influence count
Expand Down
37 changes: 34 additions & 3 deletions PYProjects/source/skin_plus_plus_py/source/skin_plus_plus_py.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,44 @@
PYBIND11_MODULE(skin_plus_plus_py, m) {

py::class_<PySkinData>(m, "SkinData")
.def(py::init<int, int>())
.def(py::init<BoneNamesVector, BoneIDsMatrix, WeightsMatrix, PositionMatrix>())
.def(py::init<py::tuple>())
.def(py::init<>())
.def(
py::init<VertexIDsMatrix, int>(),
py::arg("vertex_ids"),
py::arg("max_influence_count")
)
.def(
py::init<int, int>(),
py::arg("vertex_count"),
py::arg("max_influence_count")
)
.def(
py::init<PySkinData>(),
py::arg("skin_data")
)
.def(
py::init<
BoneNamesVector,
BoneIDsMatrix,
WeightsMatrix,
PositionMatrix,
std::optional<VertexIDsMatrix>
>(),
py::arg("skin_bone_names"),
py::arg("vertex_bone_ids"),
py::arg("vertex_weights"),
py::arg("vertex_positions"),
py::arg("vertex_ids") = py::none()
)
.def(py::init<py::tuple>(), py::arg("skin_tuple"))
.def_readwrite("bone_names", &PySkinData::boneNames)
.def_readwrite("bone_ids", &PySkinData::boneIDs)
.def_readwrite("weights", &PySkinData::weights)
.def_readwrite("positions", &PySkinData::positions)
.def_readonly("bone_names_copy", &PySkinData::boneNames, py::return_value_policy::copy)
.def_readonly("bone_ids_copy", &PySkinData::boneIDs, py::return_value_policy::copy)
.def_readonly("weights_copy", &PySkinData::weights, py::return_value_policy::copy)
.def_readonly("positions_copy", &PySkinData::positions, py::return_value_policy::copy)
.def(
py::pickle(
[](const PySkinData& pySkinData) { // __getstate__
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ class SkinManager
PySkinData* pySkinData;

// Get the vertex weights and bone ids and add them to the given PySkinData
void collectWeightsAndBoneIDs(UINT vertexIndex);
void extractWeightsAndBoneIDs(UINT vertexIndex, std::optional<UINT> dataIndex = std::nullopt);

// Get the vertex weights, bone ids and positions - on an editable mesh:
PySkinData* getDataMesh(UINT vertexCount);
void extractDataMesh(UINT vertexCount, std::optional<VertexIDsMatrix> vertexIDs = std::nullopt);

// Get the vertex weights, bone ids and positions - on an editable poly:
PySkinData* getDataPoly(UINT vertexCount);
// Get the vertex weights, bone ids and positions - on an editable poly:
void extractDataPoly(UINT vertexCount, std::optional<VertexIDsMatrix> vertexIDs = std::nullopt);

// Add missing bones to the skin modifier based on the given vector of missing bone names
void addMissingBones(std::vector<std::string> missingBoneNames);
Expand All @@ -70,9 +70,9 @@ class SkinManager

// Initialise the skin manager with the given node name
bool initialise(const wchar_t* name);

// Get the vertex weights, bone ids and positions from the given node
PySkinData* extractSkinData();
PySkinData* extractSkinData(std::optional<VertexIDsMatrix> vertexIDs = std::nullopt);

// Set the skin weights to the given node's skin modifier
bool applySkinData(PySkinData& skinData);
Expand Down
Loading

0 comments on commit e01789d

Please sign in to comment.