44import inspect
55import numpy as np
66import pathlib
7+ import random
78import site
89import sys
910import time
@@ -64,6 +65,8 @@ def wrapper_timer(*args, **kwargs) -> Any:
6465
6566class SkinPlusPlusTestBase (unittest .TestCase ):
6667
68+ nodes_to_delete = []
69+
6770 @classmethod
6871 def setUpClass (cls ):
6972 try :
@@ -74,6 +77,16 @@ def setUpClass(cls):
7477 cls ._dcc_test_files_directory = cls ._current_directory / "dcc_test_files"
7578 cls ._skin_data_file = cls ._dcc_test_files_directory / "test_skin_data.sknd"
7679
80+ @classmethod
81+ def tearDownClass (cls ):
82+ # print("tearDownClass")
83+ # print(f"cls.nodes_to_delete: {cls.nodes_to_delete}")
84+ cls .delete_nodes (cls .nodes_to_delete )
85+
86+ @classmethod
87+ def delete_nodes (cls , nodes ):
88+ raise NotImplementedError ()
89+
7790 @staticmethod
7891 def run_functions (function_list , _obj , * args , loop_count : int = 1 ):
7992 set_loops (loop_count )
@@ -114,60 +127,71 @@ def setUpClass(cls):
114127 super ().setUpClass ()
115128 executable = sys .executable .lower ()
116129 assert "3ds max" in executable
117-
118- from pymxs import runtime as mxRt
119-
120- cls .mxRt = mxRt
121130 cls .setup_mxs_environment ()
122131
123- version_info = cls .mxRt .MaxVersion ()
124- max_file_path = pathlib .Path (
125- f"{ cls ._dcc_test_files_directory } /max/test_skin_data_{ version_info [0 ]} .max"
126- )
127- current_max_file_path = pathlib .Path (cls .mxRt .MaxFilePath ) / cls .mxRt .MaxFileName
128- if current_max_file_path == max_file_path :
129- return
130-
131- if not max_file_path .exists ():
132- raise FileNotFoundError (f"No test file for current max version:\n - { max_file_path } " )
133-
134- cls .mxRt .LoadMaxFile (str (max_file_path ))
132+ @classmethod
133+ def delete_nodes (cls , nodes ):
134+ return cls .mxRt .Delete (nodes )
135135
136136 @classmethod
137137 def setup_mxs_environment (cls ):
138- skinOps = cls .mxRt .SkinOps ()
138+ from pymxs import runtime as mxRt
139+
140+ cls .mxRt = mxRt
141+
142+ skinOps = mxRt .SkinOps ()
139143 cls .skinOps_GetNumberVertices = skinOps .GetNumberVertices
140144 cls .skinOps_GetVertexWeight = skinOps .GetVertexWeight
141145 cls .skinOps_GetVertexWeightCount = skinOps .GetVertexWeightCount
142146 cls .skinOps_GetVertexWeightBoneID = skinOps .GetVertexWeightBoneID
143147 cls .skinOps_ReplaceVertexWeights = skinOps .ReplaceVertexWeights
144148
145-
146- cls .skinPPOps = cls .mxRt .SkinPPOps ()
149+ cls .skinPPOps = mxRt .SkinPPOps ()
147150 cls .sdk_primative_method_get_skin_weights = cls .skinPPOps .GetSkinWeights
148- cls .SkinPP_GetSkinWeights = cls . mxRt .SkinPP .GetSkinWeights
149- cls .SPP_GetSkinWeights = cls . mxRt .SPPGetSkinWeights
151+ cls .SkinPP_GetSkinWeights = mxRt .SkinPP .GetSkinWeights
152+ cls .SPP_GetSkinWeights = mxRt .SPPGetSkinWeights
150153
151- cls .SKINPP_SetSkinWeights = cls . mxRt .SkinPP .SetSkinWeights
154+ cls .SKINPP_SetSkinWeights = mxRt .SkinPP .SetSkinWeights
152155 cls .SKINPPOPS_SetSkinWeights = cls .skinPPOps .SetSkinWeights
153- cls .SPPSetSkinWeights = cls . mxRt .SPPSetSkinWeights
156+ cls .SPPSetSkinWeights = mxRt .SPPSetSkinWeights
154157
155-
156- meshOp = cls .mxRt .MeshOp ()
158+ meshOp = mxRt .MeshOp ()
157159 cls .meshOp_GetVert = meshOp .GetVert
158160
159- polyOp = cls . mxRt .PolyOp ()
161+ polyOp = mxRt .PolyOp ()
160162 cls .polyOp_GetVert = polyOp .GetVert
161163
162164 cls .mxs_get_skin_weights , cls .mxs_set_skin_weights = cls .get_mxs_functions ()
163165
166+ cls .skinned_sphere = mxRt .Sphere (Name = "SkinnedSphere" )
167+ cls .skin_modifier = mxRt .Skin ()
168+ mxRt .AddModifier (cls .skinned_sphere , cls .skin_modifier )
169+ cls .skinned_points = []
170+ for num in range (4 ):
171+ skinned_point = mxRt .Point (Name = f"SkinnedPoint_{ num } " )
172+ update = - 1 if num < 3 else 0
173+ skinOps .AddBone (cls .skin_modifier , skinned_point , update )
174+ cls .skinned_points .append (skinned_point )
175+
176+ cls .nodes_to_delete .append (cls .skinned_sphere )
177+ cls .nodes_to_delete .extend (cls .skinned_points )
178+ cls .weights = np .zeros ((cls .skinned_sphere .Verts .Count , 4 ), dtype = float )
179+ for index in range (cls .skinned_sphere .Verts .Count ):
180+ weights = [random .random () for _ in range (4 )]
181+ weights_total = sum (weights )
182+ weights = [float (i ) / weights_total for i in weights ]
183+ cls .weights [index ] = np .array (weights , dtype = float )
184+ cls .skinOps_ReplaceVertexWeights (cls .skin_modifier , index + 1 , [1 , 2 , 3 , 4 ], weights )
185+
164186 @classmethod
165187 def get_mxs_functions (cls ):
166188 current_file = pathlib .Path (__file__ )
167189 cls .mxRt .FileIn (str (current_file .with_suffix (".ms" )))
168190 return cls .mxRt .mxsGetSkinWeights , cls .mxRt .mxsSetSkinWeights
169191
170192 def test_get_performance (self ):
193+
194+ return
171195 get_timer_dict : dict [str , tuple [float , Any , str ]] = {}
172196
173197 @timer (get_timer_dict )
@@ -250,8 +274,15 @@ def skin_plus_plus_get_skin_data(_obj):
250274 self .run_functions (get_function_list , obj )
251275 self .process_results (get_timer_dict )
252276
253- def test_set_performance (self ):
277+ def test_get_skin_data (self ):
278+ skin_data = skin_plus_plus .get_skin_data (self .skinned_sphere .Name )
279+ # because the values returned from 3ds max are 32bit, there can be rounding errors
280+ # which can cause a direct comparison to fail, so we use np.allclose instead:
281+ # https://numpy.org/doc/stable/reference/generated/numpy.allclose.html#numpy.allclose
282+ self .assertTrue (np .allclose (skin_data .weights , self .weights ))
254283
284+ def test_set_performance (self ):
285+ return
255286 def _as_mxs_array (value , dtype = float ):
256287 mxsArray = self .mxRt .Array ()
257288 array_length = len (value )
@@ -615,18 +646,21 @@ def set_skin_weights(_obj, _boneIDs, _weights):
615646
616647 # from pymxs import runtime as mxRt
617648
618- # sel = tuple(mxRt.Selection)
619- # unittest.main()
649+ # # sel = tuple(mxRt.Selection)
650+ # # unittest.main()
651+ # mxRt.Delete(list(mxRt.Selection))
620652
621653 suite = unittest .TestSuite ()
622- suite .addTest (SkinPlusPlusTestMax ("test_get_performance" ))
654+ suite .addTest (SkinPlusPlusTestMax ("test_get_skin_data" ))
655+ # suite.addTest(SkinPlusPlusTestMax("test_get_performance"))
623656 # suite.addTest(SkinPlusPlusTestMaya("test_get_performance"))
624657 runner = unittest .TextTestRunner ()
625658 runner .run (suite )
626659
627660 # runner = unittest.TextTestRunner()
628661 # suite = unittest.makeSuite(SkinPlusPlusTestMax)
629662 # runner.run(suite)
663+
630664 # skin_data = skin_plus_plus.get_skin_data("test_mesh_low")
631665 # skin_plus_plus.set_skin_weights("test_mesh_low", skin_data)
632666 # print(skin_data.positions)
0 commit comments