diff --git a/PYProjects/skin_plus_plus/__init__.pyi b/PYProjects/skin_plus_plus/__init__.pyi index adeb115..25fb2fd 100644 --- a/PYProjects/skin_plus_plus/__init__.pyi +++ b/PYProjects/skin_plus_plus/__init__.pyi @@ -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 @@ -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. """ diff --git a/PYProjects/skin_plus_plus/_types.py b/PYProjects/skin_plus_plus/_types.py index 4c95382..d36c665 100644 --- a/PYProjects/skin_plus_plus/_types.py +++ b/PYProjects/skin_plus_plus/_types.py @@ -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] \ No newline at end of file +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 \ No newline at end of file diff --git a/PYProjects/skin_plus_plus/dccs/core.py b/PYProjects/skin_plus_plus/dccs/core.py index e600441..ea51d31 100644 --- a/PYProjects/skin_plus_plus/dccs/core.py +++ b/PYProjects/skin_plus_plus/dccs/core.py @@ -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 @@ -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 @@ -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) diff --git a/PYProjects/skin_plus_plus/dccs/max/2024/skin_plus_plus_pymxs.pyd b/PYProjects/skin_plus_plus/dccs/max/2024/skin_plus_plus_pymxs.pyd index 4333b69..42683cd 100644 Binary files a/PYProjects/skin_plus_plus/dccs/max/2024/skin_plus_plus_pymxs.pyd and b/PYProjects/skin_plus_plus/dccs/max/2024/skin_plus_plus_pymxs.pyd differ diff --git a/PYProjects/skin_plus_plus/py/310/skin_plus_plus_py.pyd b/PYProjects/skin_plus_plus/py/310/skin_plus_plus_py.pyd index 50a32f5..4248ba0 100644 Binary files a/PYProjects/skin_plus_plus/py/310/skin_plus_plus_py.pyd and b/PYProjects/skin_plus_plus/py/310/skin_plus_plus_py.pyd differ diff --git a/PYProjects/skin_plus_plus_test/skin_plus_plus_test.py b/PYProjects/skin_plus_plus_test/skin_plus_plus_test.py index 267c1b6..63a88cf 100644 --- a/PYProjects/skin_plus_plus_test/skin_plus_plus_test.py +++ b/PYProjects/skin_plus_plus_test/skin_plus_plus_test.py @@ -13,7 +13,7 @@ def __setup__(): if str(current_file) == "": # 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)) @@ -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__": @@ -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) diff --git a/PYProjects/source/skin_plus_plus_py/headers/skin_plus_plus_py.h b/PYProjects/source/skin_plus_plus_py/headers/skin_plus_plus_py.h index 7724dbf..b43508c 100644 --- a/PYProjects/source/skin_plus_plus_py/headers/skin_plus_plus_py.h +++ b/PYProjects/source/skin_plus_plus_py/headers/skin_plus_plus_py.h @@ -23,6 +23,7 @@ typedef std::vector BoneNamesVector; typedef eg::MatrixXi BoneIDsMatrix; typedef eg::MatrixXd WeightsMatrix; typedef eg::MatrixXd PositionMatrix; +typedef eg::Matrix VertexIDsMatrix; typedef unsigned int UINT; @@ -83,6 +84,8 @@ struct SortedBoneNameData { std::vector sortedBoneIDs; std::vector unfoundBoneNames; + UINT highestBoneID; + UINT lowestBoneID; SortedBoneNameData(UINT boneCount) { sortedBoneIDs = std::vector(boneCount); @@ -99,22 +102,32 @@ struct PySkinData final BoneIDsMatrix boneIDs; WeightsMatrix weights; PositionMatrix positions; + std::optional 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 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 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) @@ -122,19 +135,75 @@ struct PySkinData final 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 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(data[0]); - this->boneIDs = py::cast(data[1]); - this->weights = py::cast(data[2]); - this->positions = py::cast(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(data[4])) + { + this->setInternalState( + py::cast(data[0]), + py::cast(data[1]), + py::cast(data[2]), + py::cast(data[3]), + py::cast(data[4]) + ); + } + else if (dataSize >= 4) + { + this->setInternalState( + py::cast(data[0]), + py::cast(data[1]), + py::cast(data[2]), + py::cast(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 diff --git a/PYProjects/source/skin_plus_plus_py/source/skin_plus_plus_py.cpp b/PYProjects/source/skin_plus_plus_py/source/skin_plus_plus_py.cpp index d28b788..42b1ffa 100644 --- a/PYProjects/source/skin_plus_plus_py/source/skin_plus_plus_py.cpp +++ b/PYProjects/source/skin_plus_plus_py/source/skin_plus_plus_py.cpp @@ -5,13 +5,44 @@ PYBIND11_MODULE(skin_plus_plus_py, m) { py::class_(m, "SkinData") - .def(py::init()) - .def(py::init()) - .def(py::init()) + .def(py::init<>()) + .def( + py::init(), + py::arg("vertex_ids"), + py::arg("max_influence_count") + ) + .def( + py::init(), + py::arg("vertex_count"), + py::arg("max_influence_count") + ) + .def( + py::init(), + py::arg("skin_data") + ) + .def( + py::init< + BoneNamesVector, + BoneIDsMatrix, + WeightsMatrix, + PositionMatrix, + std::optional + >(), + 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::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__ diff --git a/PYProjects/source/skin_plus_plus_pymxs/headers/skin_plus_plus_pymxs.h b/PYProjects/source/skin_plus_plus_pymxs/headers/skin_plus_plus_pymxs.h index 11439db..6a60125 100644 --- a/PYProjects/source/skin_plus_plus_pymxs/headers/skin_plus_plus_pymxs.h +++ b/PYProjects/source/skin_plus_plus_pymxs/headers/skin_plus_plus_pymxs.h @@ -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 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 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 vertexIDs = std::nullopt); // Add missing bones to the skin modifier based on the given vector of missing bone names void addMissingBones(std::vector missingBoneNames); @@ -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 vertexIDs = std::nullopt); // Set the skin weights to the given node's skin modifier bool applySkinData(PySkinData& skinData); diff --git a/PYProjects/source/skin_plus_plus_pymxs/source/skin_plus_plus_pymxs.cpp b/PYProjects/source/skin_plus_plus_pymxs/source/skin_plus_plus_pymxs.cpp index cb467ae..013bd37 100644 --- a/PYProjects/source/skin_plus_plus_pymxs/source/skin_plus_plus_pymxs.cpp +++ b/PYProjects/source/skin_plus_plus_pymxs/source/skin_plus_plus_pymxs.cpp @@ -3,39 +3,7 @@ -//std::string convertWChartoString(const wchar_t* text) -//{ -// //fmt::format(text); -// auto text_ = fmt::to_string(text); -// std::wstring ws(text); -// return std::string(ws.begin(), ws.end()); -//} - - -INode* getChildByName(const wchar_t* name, INode* parent) -{ - INode* parent_ = parent; - if (!parent) - { - Interface* ip = GetCOREInterface(); - parent_ = ip->GetRootNode(); - } - INode* node; - const wchar_t* nodeName; - for (int i = 0; i < parent_->NumberOfChildren(); i++) - { - node = parent_->GetChildNode(i); - nodeName = node->GetName(); - if (wcscmp(nodeName, name) == 0) return node; - try { node = getChildByName(name, parent = node); } - catch (const std::invalid_argument&) { continue; } - return node; - } - throw std::invalid_argument("No node with name: " + convertWCharToChar(name)); -} - - -TriObject* getTriObjectFromNode(INode* node, TimeValue time) +static TriObject* getTriObjectFromNode(INode* node, TimeValue time) { Object* object = node->EvalWorldState(time).obj; auto classID = Class_ID(TRIOBJ_CLASS_ID, 0); @@ -51,7 +19,7 @@ TriObject* getTriObjectFromNode(INode* node, TimeValue time) } -PolyObject* getPolyObjectFromNode(INode* inNode, TimeValue inTime, bool& deleteIt) +static PolyObject* getPolyObjectFromNode(INode* inNode, TimeValue inTime, bool& deleteIt) { Object* object = inNode->GetObjectRef(); auto classID = Class_ID(POLYOBJ_CLASS_ID, 0); @@ -72,7 +40,7 @@ PolyObject* getPolyObjectFromNode(INode* inNode, TimeValue inTime, bool& deleteI } -const inline auto getMeshType(INode* node) +const inline static auto getMeshType(INode* node) { ObjectState objectState = node->EvalWorldState(0); @@ -154,8 +122,9 @@ bool SkinManager::initialise(ULONG handle) } -void SkinManager::collectWeightsAndBoneIDs(UINT vertexIndex) +void SkinManager::extractWeightsAndBoneIDs(UINT vertexIndex, std::optional arrayIndex) { + UINT arrayIndex_ = arrayIndex.value_or(vertexIndex); UINT influenceCount = this->iSkinContextData->GetNumAssignedBones(vertexIndex); if (influenceCount > this->maximumVertexWeightCount) { @@ -166,38 +135,47 @@ void SkinManager::collectWeightsAndBoneIDs(UINT vertexIndex) { double infuenceWeight = this->iSkinContextData->GetBoneWeight(vertexIndex, influenceIndex); int influenceBoneID = this->iSkinContextData->GetAssignedBone(vertexIndex, influenceIndex); - this->pySkinData->weights(vertexIndex, influenceIndex) = infuenceWeight; - this->pySkinData->boneIDs(vertexIndex, influenceIndex) = influenceBoneID; - /* if (vertexIndex == 0 && influenceIndex == 0) - { - py::print("infuenceWeight: ", infuenceWeight); - py::print("influenceBoneID: ", influenceBoneID); - py::print("this->pySkinData->boneID: ", this->pySkinData->boneIDs(vertexIndex, influenceIndex)); - }*/ + this->pySkinData->weights(arrayIndex_, influenceIndex) = infuenceWeight; + this->pySkinData->boneIDs(arrayIndex_, influenceIndex) = influenceBoneID; } } -PySkinData* SkinManager::extractSkinData() +PySkinData* SkinManager::extractSkinData(std::optional vertexIDs) { if (!isValid) { - throw std::exception("SkinData object is invalid. Cannot get skin weights."); + throw std::runtime_error("SkinData object is invalid. Cannot get skin weights."); } - unsigned int vertexCount = iSkinContextData->GetNumPoints(); - maximumVertexWeightCount = iSkinContextData->GetNumAssignedBones(0); - for (UINT i = 1; i < vertexCount; i++) + unsigned int vertexCount = 0; + if (vertexIDs.has_value()) + { + vertexCount = vertexIDs.value().cols(); + } + else + { + vertexCount = iSkinContextData->GetNumPoints(); + } + this->maximumVertexWeightCount = iSkinContextData->GetNumAssignedBones(0); + for (UINT i = 0; i < vertexCount; i++) { auto influenceCount = iSkinContextData->GetNumAssignedBones(i); - if (influenceCount > maximumVertexWeightCount) + if (influenceCount > this->maximumVertexWeightCount) { - maximumVertexWeightCount = influenceCount; + this->maximumVertexWeightCount = influenceCount; } } - pySkinData = new PySkinData(vertexCount, maximumVertexWeightCount); - auto skinBoneCount = iSkin->GetNumBones(); - pySkinData->boneNames = std::vector(skinBoneCount); + if (vertexIDs.has_value()) + { + this->pySkinData = new PySkinData(vertexIDs.value(), this->maximumVertexWeightCount); + } + else + { + this->pySkinData = new PySkinData(vertexCount, this->maximumVertexWeightCount); + } + const int skinBoneCount = iSkin->GetNumBones(); + this->pySkinData->boneNames = std::vector(skinBoneCount); for (auto boneIndex = 0; boneIndex < skinBoneCount; boneIndex++) { // we don't use GetBoneName as that can return nulls depending on how the skin modifier has been setup @@ -210,64 +188,90 @@ PySkinData* SkinManager::extractSkinData() if (!boneName) { auto handle = bone->GetHandle(); - auto exceptionText = convertStringToChar( + throw std::runtime_error( fmt::format("Name is NULL on skinned bone at index: {} with handle: {}", boneIndex + 1, handle) ); - throw std::exception(exceptionText); } - pySkinData->boneNames[boneIndex] = convertWCharToString(boneName); + this->pySkinData->boneNames[boneIndex] = convertWCharToString(boneName); } auto meshType = getMeshType(node); if (meshType == 0) { - return getDataMesh(vertexCount); + this->extractDataMesh(vertexCount, vertexIDs); + return this->pySkinData; } else if (meshType == 1) { - return getDataPoly(vertexCount); + this->extractDataPoly(vertexCount, vertexIDs); + return this->pySkinData; } - throw std::exception("Unsupported mesh type, convert to EditablePoly or EditableMesh!"); + throw std::runtime_error("Unsupported mesh type, convert to EditablePoly or EditableMesh!"); } -PySkinData* SkinManager::getDataPoly(UINT vertexCount) +void SkinManager::extractDataPoly(UINT vertexCount, std::optional vertexIDs) { - Matrix3 nodeTransform = node->GetObjectTM(0); bool deleteIt; PolyObject* polyObject = getPolyObjectFromNode(node, GetCOREInterface()->GetTime(), deleteIt); MNMesh& mnMesh = polyObject->GetMesh(); - for (UINT vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++) + if (vertexIDs.has_value()) + { + VertexIDsMatrix vertexIDs_ = vertexIDs.value(); + for (UINT arrayIndex = 0; arrayIndex < vertexCount; arrayIndex++) + { + UINT vertexID = vertexIDs_(arrayIndex, 0); + Point3 worldPosition = nodeTransform.PointTransform(mnMesh.V(vertexID)->p); + this->pySkinData->positions(arrayIndex, 0) = worldPosition.x; + this->pySkinData->positions(arrayIndex, 1) = worldPosition.y; + this->pySkinData->positions(arrayIndex, 2) = worldPosition.z; + extractWeightsAndBoneIDs(vertexID, arrayIndex); + }; + } + else { - Point3 worldPosition = nodeTransform.PointTransform(mnMesh.V(vertexIndex)->p); - pySkinData->positions(vertexIndex, 0) = worldPosition.x; - pySkinData->positions(vertexIndex, 1) = worldPosition.y; - pySkinData->positions(vertexIndex, 2) = worldPosition.z; - collectWeightsAndBoneIDs(vertexIndex); - //py::print("Influence 0 weight: ", pySkinData->weights(vertexIndex, 0)); - //py::print("------"); - }; - return pySkinData; + for (UINT vertexID = 0; vertexID < vertexCount; vertexID++) + { + Point3 worldPosition = nodeTransform.PointTransform(mnMesh.V(vertexID)->p); + this->pySkinData->positions(vertexID, 0) = worldPosition.x; + this->pySkinData->positions(vertexID, 1) = worldPosition.y; + this->pySkinData->positions(vertexID, 2) = worldPosition.z; + extractWeightsAndBoneIDs(vertexID); + }; + } } -PySkinData* SkinManager::getDataMesh(UINT vertexCount) +void SkinManager::extractDataMesh(UINT vertexCount, std::optional vertexIDs) { Matrix3 nodeTransform = node->GetObjectTM(0); TriObject* triObject = getTriObjectFromNode(node, GetCOREInterface()->GetTime()); Mesh& mesh = triObject->GetMesh(); - for (UINT vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++) + if (vertexIDs.has_value()) + { + VertexIDsMatrix vertexIDs_ = vertexIDs.value(); + for (UINT vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++) + { + UINT vertexID = vertexIDs_(vertexIndex, 0); + Point3 worldPosition = nodeTransform.PointTransform(mesh.getVert(vertexID)); + this->pySkinData->positions(vertexID, 0) = worldPosition.x; + this->pySkinData->positions(vertexID, 1) = worldPosition.y; + this->pySkinData->positions(vertexID, 2) = worldPosition.z; + extractWeightsAndBoneIDs(vertexID, vertexIndex); + }; + } + else { - Point3 worldPosition = nodeTransform.PointTransform(mesh.getVert(vertexIndex)); - pySkinData->positions(vertexIndex, 0) = worldPosition.x; - pySkinData->positions(vertexIndex, 1) = worldPosition.y; - pySkinData->positions(vertexIndex, 2) = worldPosition.z; - collectWeightsAndBoneIDs(vertexIndex); - //py::print("Influence 0 weight: ", pySkinData->weights(vertexIndex, 0)); - //py::print("------"); - }; - return pySkinData; + for (UINT vertexID = 0; vertexID < vertexCount; vertexID++) + { + Point3 worldPosition = nodeTransform.PointTransform(mesh.getVert(vertexID)); + this->pySkinData->positions(vertexID, 0) = worldPosition.x; + this->pySkinData->positions(vertexID, 1) = worldPosition.y; + this->pySkinData->positions(vertexID, 2) = worldPosition.z; + extractWeightsAndBoneIDs(vertexID); + }; + } } @@ -344,8 +348,8 @@ bool SkinManager::applySkinData(PySkinData& skinData) "skin bone ids count does not match skin weights count: " + convertWCharToChar(this->node->GetName()) ); auto vertexCount = this->iSkinContextData->GetNumPoints(); - if (boneIDsRows != vertexCount) throw std::length_error( - "skin vertex count does not match provided data count: " + convertWCharToChar(this->node->GetName()) + if (boneIDsRows != vertexCount && !skinData.vertexIDs.has_value()) throw std::length_error( + fmt::format("skin vertex count does not match provided data count: {}", convertWCharToChar(this->node->GetName())) ); auto skinBoneCount = this->iSkin->GetNumBones(); if (skinBoneCount == 0) @@ -358,6 +362,8 @@ bool SkinManager::applySkinData(PySkinData& skinData) SortedBoneNameData sortedBoneIDs = skinData.getSortedBoneIDs(boneData.names); Tab skinBones = boneData.nodes; size_t sortedBoneIDCount = sortedBoneIDs.sortedBoneIDs.size(); + //const auto highestBoneID = std::max_element(sortedBoneIDs.sortedBoneIDs.begin(), sortedBoneIDs.sortedBoneIDs.end()); + //const auto lowestBoneID = std::min_element(sortedBoneIDs.sortedBoneIDs.begin(), sortedBoneIDs.sortedBoneIDs.end()); if (sortedBoneIDs.unfoundBoneNames.size() > 0) { this->addMissingBones(sortedBoneIDs.unfoundBoneNames); @@ -386,12 +392,10 @@ bool SkinManager::applySkinData(PySkinData& skinData) } if (boneID > sortedBoneIDCount) { - auto exceptionText = convertStringToChar( - fmt::format( - "Influence ID {} is out of range of sorted bone count {}!", - boneID, - sortedBoneIDCount - ) + const auto exceptionText = fmt::format( + "Influence ID: {} is out of range of sorted bone count {}!", + boneID, + sortedBoneIDCount ); throw std::length_error(exceptionText); } @@ -413,34 +417,29 @@ bool SkinManager::applySkinData(PySkinData& skinData) } -bool setSkinWeights(wchar_t* name, PySkinData& skinData) -{ - SkinManager skinData_(name); - return skinData_.applySkinData(skinData); -} - - PYBIND11_MODULE(skin_plus_plus_pymxs, m) { // This makes the base SkinData class available to the module: #include - m.def("extract_skin_data", [&](wchar_t* name) + m.def("extract_skin_data", [&](wchar_t* name, std::optional vertexIDs = std::nullopt) { SkinManager skinData(name); - PySkinData* pySkinData = skinData.extractSkinData(); + PySkinData* pySkinData = skinData.extractSkinData(vertexIDs); return pySkinData; }, "Extract SkinData from the mesh with the given name", - py::arg("name") + py::arg("name"), + py::arg("vertex_ids") = py::none() ); - m.def("extract_skin_data", [&](ULONG handle) + m.def("extract_skin_data", [&](ULONG handle, std::optional vertexIDs = std::nullopt) { SkinManager skinData(handle); - PySkinData* pySkinData = skinData.extractSkinData(); + PySkinData* pySkinData = skinData.extractSkinData(vertexIDs); return pySkinData; }, "Extract SkinData from the mesh with the given handle", - py::arg("handle") + py::arg("handle"), + py::arg("vertex_ids") = py::none() ); m.def("apply_skin_data", [&](wchar_t* name, PySkinData& skinData) {