C++ Skin


#1

Any body can show me a C++ function-example how I can remove a bone from a skin modifier? I can’t see any built-in functions like skinops.


#2

could try something like…


#include ..\...\maxsdk\samples\modifiers\bonesdef\bonesdef.h

Modifier *mod = //what ever method you are using to acquire the skin mod
if(mod->ClassID() == Class_ID(9815843,87654))
{   
    BonesDefMod *bmod = (BonesDefMod*)mod;
   bmod->RemoveBone(boneID); // you test that boneid is within the correct range etc
        bmod->NotifyDependents(FOREVER, GEOM_CHANNEL, REFMSG_CHANGE);
        bmod->ip->RedrawViews(bmod->ip->GetTime());
}


#3

This is my code that is a function to remove unused bones:


#include <maxscript/maxscript.h>
#include <maxscript/foundation/3dmath.h>
#include <maxscript/macros/define_instantiation_functions.h>
#include "iskin.h"
#include "bonesdef.h"
def_visible_primitive(RemoveUnusedBones, "RemoveUnusedBones");
Value* RemoveUnusedBones_cf(Value **arg_list, int count)
{
   INode* node = arg_list[0]->to_node();
   Modifier* modifier = arg_list[1]->to_modifier();
   ISkin* skin = (ISkin *)modifier->GetInterface(I_SKIN);
   BonesDefMod* bkin = (BonesDefMod*)skin;
   ISkinContextData *skinData = skin->GetContextInterface(node);
   BitArray usedbones = BitArray(skin->GetNumBones());
   for (int i = 0; i < skinData->GetNumPoints(); i++)
   {
      for (int j = 0; j < skinData->GetNumAssignedBones(i); j++)
      {
         int boneIndex = skinData->GetAssignedBone(i, j);
         float boneWeight = skinData->GetBoneWeight(i, j);
         if (boneWeight > 0) usedbones.Set(boneIndex);
      }
   }
   for (int i = usedbones.GetSize(); i < 0; i--)
   {
      if (usedbones[i] == 0) bkin->RemoveBone(i);
   }
   return new BitArrayValue(usedbones);
}

This should work, right? But I got several errors:

Do you see any error in the code? or you think errors comes from build environment and project setting? Note that when I open bonesdef project, I got .Net version error.


#4

what you could do is add your function to skinops in the CommandModes.cpp file of the bonesdef modifier project then recompile the project to replace the current skin etc Otherwize you’ll need to create a pared back bonesdef.h for it to work “independently”


#5

So my questions are:
1 - Where the MS function “skinOps” in the SDK? Is it bonesdef? I mean Skinops is created by using bonesdef.h?
2 - How I can “pared back” the bonesdef.h?
3 - Again, do you think the code itself works correctly?
4 - I think I installed every SDK requirements, but I got this error when I want to open bonesdef.h:

F:\Software\3dsMax\SDK\3ds Max 2018 SDK\maxsdk\samples\render\NotificationSystem\NotificationSystem.vcxproj : error : A numeric comparison was attempted on “$(_TargetFrameworkVersionWithoutV)” that evaluates to “V4.6” instead of a number, in condition “’$(ImplicitlyExpandNETStandardFacades)’ == ‘’ AND ‘$(_TargetFrameworkVersionWithoutV)’ >= ‘4.6.1’”. C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\Microsoft\Microsoft.NET.Build.Extensions\Microsoft.NET.Build.Extensions.NETFramework.targets

I removed all .Net versions from my computer, then I installed several versions until 4.6 , but I still get the error. my build information is:
Windows 7
Max 2018
VS 2017
Latest Windows 10 SDk installed


#6

1 - Where the MS function “skinOps” in the SDK? Is it bonesdef?

as I said above the code for skinops is in CommandModes.cpp file of the bonesdef modifier project, add your function there copying the methods used by the other function recompile the project and your function will be available to mxs


#7

Do you think we can use this?


macroRecorder->FunctionCall(_T("skinOps.removeBone"), 2, 0, mr_reftarg, skin, mr_int, boneID + 1);


#8

if you want to ? surely if you want to take that route you may as well do it all in mxs.


#9

I want to use c++ because of speed. do you think calling MS in C++ is not efficient? I saw many examples in SDK that used macro. Apart from the checking the macro, side problem is that this function doesn’t do the job in MaxScript:


#include <maxscript/maxscript.h>
#include <maxscript/foundation/3dmath.h>
#include <maxscript/macros/define_instantiation_functions.h>
#include "iskin.h"
#include "modstack.h"
#include "macrorec.h"
#include "maxscript/util/listener.h"

def_visible_primitive(RemoveUnusedBones, "RemoveUnusedBones");
Value* RemoveUnusedBones_cf(Value **arg_list, int count)
{
    INode* node = arg_list[0]->to_node();
    Modifier* modifier = arg_list[1]->to_modifier();
    
    Interface* core = GetCOREInterface();
    TimeValue t = core->GetTime();
    Object *obj = node->GetObjectRef()->Eval(t).obj;
    IDerivedObject *drivedObj = CreateDerivedObject(obj);
    ISkin* skin = (ISkin *)modifier->GetInterface(I_SKIN);
    ISkinContextData *skinData = skin->GetContextInterface(node);
    BitArray usedbones = BitArray(skin->GetNumBones());

    for (int i = 0; i < skinData->GetNumPoints(); i++)
    {
        for (int j = 0; j < skinData->GetNumAssignedBones(i); j++)
        {
            int boneIndex = skinData->GetAssignedBone(i, j);
            float boneWeight = skinData->GetBoneWeight(i, j);
            if (boneWeight > 0) usedbones.Set(boneIndex);
        }
    }
    for (int i = usedbones.GetSize(); i > 0; i--)
    {
        //if (usedbones[i] != 1)
        //{
            macroRecorder->FunctionCall(_T("skinOps.selectBone"), 2, 0, mr_reftarg, skin, mr_int, i + 1);
            macroRecorder->FunctionCall(_T("skinOps.removeBone"), 2, 0, mr_reftarg, skin, mr_int, i + 1);
        //}
    }

    drivedObj->NotifyDependents(FOREVER, GEOM_CHANNEL, REFMSG_CHANGE);
    core->RedrawViews(t);

    return &ok;
}


#10

After struggling with SDK requirements and installing VS 2015, I just added my following code to the “bonesdef” project and now I can compiled it, I replaced MaxFolder/Stdplugs/bonesdef.dlm with my code and now my function works perfectly in max.


#include "mods.h"
#include "bonesdef.h"
#include <maxscript/macros/define_instantiation_functions.h>
#include "modstack.h"

Modifier* FindSkinModifier(INode* nodePtr)
{
    Object* ObjectPtr = nodePtr->GetObjectRef();
    if (!ObjectPtr) return NULL;
    while (ObjectPtr && ObjectPtr->SuperClassID() == GEN_DERIVOB_CLASS_ID)
    {
        IDerivedObject *DerivedObjectPtr = (IDerivedObject *)(ObjectPtr);
        int ModStackIndex = 0;
        while (ModStackIndex < DerivedObjectPtr->NumModifiers())
        {
            Modifier* ModifierPtr = DerivedObjectPtr->GetModifier(ModStackIndex);
            if (ModifierPtr->ClassID() == Class_ID(SKIN_CLASSID)) 
            {
                return ModifierPtr;
            }
            ModStackIndex++;
        }
        ObjectPtr = DerivedObjectPtr->GetObjRef();
    }
    return NULL;
}

void RecursiveRemoveUnusedBones(INode* node)
{
    Modifier* skin = FindSkinModifier(node);
    if (skin)
    {
        ISkin* iskin = (ISkin *)skin->GetInterface(I_SKIN);
        BonesDefMod* bmod = (BonesDefMod*)iskin;
        ISkinContextData *skinData = iskin->GetContextInterface(node);
        BitArray usedbones = BitArray(iskin->GetNumBones());
        for (int i = 0; i < skinData->GetNumPoints(); i++)
        {
            for (int j = 0; j < skinData->GetNumAssignedBones(i); j++)
            {
                int boneIndex = skinData->GetAssignedBone(i, j);
                float boneWeight = skinData->GetBoneWeight(i, j);
                if (boneWeight > 0) usedbones.Set(boneIndex);
            }
        }
        for (int i = usedbones.GetSize(); i >= 0; i--)
        {
            if (usedbones[i] != 1) bmod->RemoveBone(i);
        }
    }

    for (int i = 0; i < node->NumberOfChildren(); ++i)
    {
        RecursiveRemoveUnusedBones(node->GetChildNode(i));
    }
}

def_visible_primitive(RemoveUnusedBones, "RemoveUnusedBones");
Value* RemoveUnusedBones_cf(Value **arg_list, int count)
{
    RecursiveRemoveUnusedBones(GetCOREInterface()->GetRootNode());
    return &ok;
}

Now my question is how I can make the independent version?


#11

Yes, just change the classID of the modifier to something different, and your plugin will register independently from the built-in one (so long as you don’t replace/delete the original plugin file like you did).

3dsmax keeps track of which plugin is which through classIDs alone. Make sure you search the project files for all instances of any classIDs being registered, since some projects contain more than one registered class, and if max detects any duplicate IDs on launch it will generate an error.

The classID values are just random numbers and can be anything, so long as they are not duplicates of another plugin’s values.


#12

Would you please explain more? I just want to expose a function to the MaxScript, I don’t want to create any new class (modifier?), my thought was we don’t need to generate ClassID for the function publishing pipeline. actually my question was how we can include-use bonesdef methods out of the bonesdef project.


#13

Oh I thought you wanted to branch the sample plugin code into your own plugin, and have them both load.


#14

The only problem is how to include skin functions in my project.


#15

I asked this question in several forums and sadly no chance to get working solution, I just wondering how other developers create extra Skin functions?


#16

I would add them to the skin modifier and ship my stuff with the updated skin modifier.


#17

This is ricky, isn’t it? Maybe user wants to install different skin tools from different developer at the same time.


#18

rename it mzskin and give it an new class_ID then it won’t clash with any other tools (as long as you don’t charge for it ;))


#19

I will test it out, Thank You so much @Klvnk.


#20

I finally figure it out how to create a standalone project and include BonesDef functions! (I guess you know this already!, but I just want to confirm you ) We should follow this steps:
1 - Include BonesDef.h.
2 - Create a static library of BonesDef (.lib).
3 - Add that library to the Project Setting/Additional Library.
4 - Copy dependency list from BonesDef project/Additional Dependency and append it to our project.
5 - Also append “BonesDef.lib” to the list.