I imagine it’s possible, you’ll have to create your own list controller/use the existing one (you can probably rename it somewhere) to store the weight controllers. Though there will be some issues with empty slot if using the built in one (adding a weight controller for an no existant target node). You then have some indirection/sub anim tree enumerating when getting the weight values. If it were me I’d leave it as is, get it all working and solid send it out to the troops then get feed back as to whether it would be worth doing (as it’s a little different to what they may be used to)
What if I moved the target list and weights to a second parameter block? Would that cause it to roll up in a weights hierarchy?
does a parameter block in it’s self appear in the trackview – subanim tree :curious:
I don’t know… all the parameters that I have within the parameter block show up– but the block in-and-of-itself doesn’t appear to…
But how does the “weights” parameter block of list controllers show up as an expandable-list hierarchy for the weights??? I can’t find in the list controller code how they do that.
Ok… so I’m evaluating/debugging the controller plugin… seeing what works and what doesn’t– and I’ve found that when I do a File>>Reset with nodes in the Target ListBox that the plugin crashes with this error:
Unhandled exception at 0x20202020 in 3dsmax.exe: 0xC0000005: Access violation reading location 0x20202020.
I set a breakpoint on my controller’s destructor:
MyController::~MyController()
{
delete inheritOffsetTM;
UnRegisterNotification(NotifyPreDeleteNode, this, NOTIFY_SCENE_PRE_DELETED_NODE);
DeleteAllRefsFromMe();
}
…and the crash happens after it successfully steps thru and gets out of the destructor. It gives me a warning about source code not being available and takes me into the Assembly code.
Any ideas causing the crash???
Also, if I save the scene, and after that remove all the nodes in the target list to avoid the crash and reset, and re-open the scene. It doesn’t load the nodes back into the list as targets. Ideas on this??
Could the two issues be related???
Implemented– YES. Correct? :shrug:
Here they are:
#define ANIMPIVOT_POS_REF 1
#define ANIMPIVOT_ROT_REF 2
#define ANIMPIVOT_SCL_REF 3
#define ANIMPIVOT_PBLOCK_REF 4
RefTargetHandle AnimPivotConst::GetReference(int i)
{
switch (i)
{
//the transform controller itself is ref 0
case ANIMPIVOT_POS_REF: return (RefTargetHandle)pos;
case ANIMPIVOT_ROT_REF: return (RefTargetHandle)rot;
case ANIMPIVOT_SCL_REF: return (RefTargetHandle)scl;
case ANIMPIVOT_PBLOCK_REF: return pblock;
default: return NULL;
}
}
void AnimPivotConst::SetReference(int i, RefTargetHandle rtarg)
{
// this is rare, but it is possible that
// an undo can be executed while parameters are in the
// command panel.
if (ip && rtarg==NULL && editCont==this) {
EndEditParams(ip, 0, NULL);
}
switch (i)
{
//the transform controller itself is ref 0
case ANIMPIVOT_POS_REF: pos = (Control*)rtarg; return;
case ANIMPIVOT_ROT_REF: rot = (Control*)rtarg; return;
case ANIMPIVOT_SCL_REF: scl = (Control*)rtarg; return;
case ANIMPIVOT_PBLOCK_REF: pblock = (IParamBlock2*)rtarg; return;
}
}
can you put a break in setreference, and see it it’s called by the system with i == 0 and see if rtarg is a valid pointer ?
Yes, I will do that momentarily… ALSO, trying to understand/resolve the load/save issues…
I read the SDK section “Loading and Saving Plugin Data” and I basically understand that chunks are pointing to specific places within the file itself (i.e., placing the file pointer)… but I do not understand how to determine the file location numbers.
For example:
// Implementation of Loading/Saving Methods
#define PRSFLAGS_CHUNK 0x7230
#define PRSOFLAGS_CHUNK 0x7231
#define PRS_INHERIT_OFFSET_CHUNK 0x7232
#define OFFSETQUAT_LOCAL_CHUNK 0x1001
#define OFFSETQUAT_WORLD_CHUNK 0x1002
#define INITIAL_QUAT_CHUNK 0x1003
#define USEROTQUAT_CHUNK 0x1004
#define OLD_TARGET_NUMBER_CHUNK 0x1005
What do all of those Hexadecimal numbers mean? How do I know what they should be? I’m sure they have significance to the type of data that is to be stored… I mean, I get that they are the starting point for that chunk of data. But I wouldn’t even begin to know how to choose correct hexadecimal numbers. Can someone explain or point me to a thorough tutorial?
EDIT UPDATE: So after picking target nodes and having target nodes in the list and doing File>Reset, it breaks at the SetReference method… it iterates i from 1 to 3 but before it iterates to 4 for the parameter block it crashes with the error I posted earlier. So it successfully gets thru 3 but then fails before 4.
the assertion ref (0) == the tm controller itself is wrong
the system gets the number of refs from int NumRefs() then iterates through expection a reftarg so having an “empty” ref will balls things up so move the pblock to ref 0 and set the correct number of refs to 4 (I assume you you have it at 5 but i maybe wrong). Don’t worry about Load and Save you shouldn’t have to deal with those using plock2 (they are chunk ID’s and only have to be unique within the pluging btw)
Hmm… that was it. I had NumRefs = 4 and I changed the pblock ref to 0 and it resets successfully.
But I still want to understand the save chunks and their hexadecimal values are determined by the programmer– because the pblock does not handle all of the parameters of the controller.
you just pick an unsigned short (16 bit) value as the id, which in hexadecimal is in the style 0x0000 but could very well be 1,2, 3 , 5 just as long it’s unique within the plugin
we use a similar chunk id file system in our export format but use unsigned int tags and encode them using 4 char character codes ‘BMAP’ for example (makes them readable in both code and a hex file reader. Obviously as “sub” chunks within a plugin max doesnt need that kind of variation. you could do the same and encode 2 char codes as chunk tags ‘AB’ ‘AC’ ‘aa’ ‘bb’ ‘a1’
Ok… I couldn’t delete my previous post– so disregard it and read below.
Before I only had NumRef = 4 and nothing set as ref 0 in Get/Set Reference because the ::Copy( ) method was specifying to replace reference 0… and before when I had the pblock as ref 0 it was (IIRC) causing an error and not loading the controller UI on the motion panel. So changing pblock to another integer seemed to resolve that.
I’m still confused about the plugin controller itself and references. But what I have below seems to be working now (best I can tell– at least it’s not causing a crash as I try to break it):
Looking at the Spring controller and the Link Constraint controller, they both seem to create a ref 0 to a Control* so I created a private member:
Control *tmControl; //ref 0
For my NumRefs I have:
int NumRefs() { return 5; }; //itself, pos, rot, scl, pblock
My reference defines are:
// References for the AnimPivot Constraint Controller
#define ANIMPIVOT_CTRL_REF 0
#define ANIMPIVOT_POS_REF 1
#define ANIMPIVOT_ROT_REF 2
#define ANIMPIVOT_SCL_REF 3
#define ANIMPIVOT_PBLOCK_REF 4
When I create an object and replace the default PRS controller with my controller (using the AssignController Rollout on the Motion Panel and picking my controller from the transform controller list) it calls my ::Copy( ) method (still a cluge for the moment), given below:
void AnimPivotConst::Copy(Control *from)
{
Quat fvalRot;
if (from!=NULL) {
assert(from->SuperClassID()==CTRL_MATRIX3_CLASS_ID);
if (from->ClassID()== ANIMPIVOT_CONSTRAINT_CLASS_ID) {
AnimPivotConst *ctrl = (AnimPivotConst*)from;
// a copy will construct its own pblock to keep the pblock-to-owner 1-to-1.
RemapDir *remap = NewRemapDir();
ReplaceReference(0,(ReferenceTarget*)ctrl->tmControl->Clone(*remap));
ReplaceReference(ANIMPIVOT_PBLOCK_REF, ctrl->pblock->Clone(*remap));
remap->DeleteThis();
ownMat = ctrl->ownMat;
} else {
//TODO: Rework following makeshift code...
Interval valid;
ownMat.IdentityMatrix();
from->GetValue(0,&ownMat,valid,CTRL_RELATIVE);
initialAnimPivotFlag = 1;
fvalRot = Quat(ownMat);
baseRotQuatLocal = fvalRot;
baseRotQuatLocal.Normalize();
}
}
ivalid.SetEmpty();
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
}
And the Get/Set Reference methods are now:
RefTargetHandle AnimPivotConst::GetReference(int i)
{
switch (i)
{
case ANIMPIVOT_CTRL_REF: return (RefTargetHandle)tmControl;
case ANIMPIVOT_POS_REF: return (RefTargetHandle)pos;
case ANIMPIVOT_ROT_REF: return (RefTargetHandle)rot;
case ANIMPIVOT_SCL_REF: return (RefTargetHandle)scl;
case ANIMPIVOT_PBLOCK_REF: return pblock;
default: return NULL;
}
}
void AnimPivotConst::SetReference(int i, RefTargetHandle rtarg)
{
// this is rare, but it is possible that
// an undo can be executed while parameters are in the
// command panel.
if (ip && rtarg==NULL && editCont==this) {
EndEditParams(ip, 0, NULL);
}
switch (i)
{
case ANIMPIVOT_CTRL_REF: tmControl = (Control*)rtarg; return;
case ANIMPIVOT_POS_REF: pos = (Control*)rtarg; return;
case ANIMPIVOT_ROT_REF: rot = (Control*)rtarg; return;
case ANIMPIVOT_SCL_REF: scl = (Control*)rtarg; return;
case ANIMPIVOT_PBLOCK_REF: pblock = (IParamBlock2*)rtarg; return;
}
}
Then what is ref 0 in the Copy method???
ReplaceReference(0,(ReferenceTarget*)ctrl->tmControl->Clone(*remap));
Should I be using: (*this)
So what should I have? Are you saying I can delete this tmControl and the defined ref 0 I’ve made?
Ok… so I commented those things out, changed pblock to ref 0 and changed NumRefs to 4 and modified my Copy( ) method as follows:
void AnimPivotConst::Copy(Control *from)
{
Quat fvalRot;
if (from!=NULL) {
assert(from->SuperClassID()==CTRL_MATRIX3_CLASS_ID);
if (from->ClassID()== ANIMPIVOT_CONSTRAINT_CLASS_ID) {
AnimPivotConst *ctrl = (AnimPivotConst*)from;
(*this) = *ctrl;
// a copy will construct its own pblock to keep the pblock-to-owner 1-to-1.
RemapDir *remap = NewRemapDir();
//ReplaceReference(0,(ReferenceTarget*)ctrl->tmControl->Clone(*remap));
ReplaceReference(ANIMPIVOT_PBLOCK_REF, ctrl->pblock->Clone(*remap));
remap->DeleteThis();
ownMat = ctrl->ownMat;
} else {
//TODO: Rework following makeshift code...
Interval valid;
ownMat.IdentityMatrix();
from->GetValue(0,&ownMat,valid,CTRL_RELATIVE);
initialAnimPivotFlag = 1;
fvalRot = Quat(ownMat);
baseRotQuatLocal = fvalRot;
baseRotQuatLocal.Normalize();
}
}
ivalid.SetEmpty();
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
}
When I re-assign my Controller onto a node that already has my controller with targets… it steps thru that first bit of the Copy Method and then causes an error immediately after leaving the copy method and going somewhere in Assembly code within the core.dll and crashes.
So to my ignorance… it seems that trying to copy the pblock over ref 0 is causing a crash.
think you should lose the line
(*this) = *ctrl;
should look something like this
void AnimPivotConst::Copy(Control *from)
{
Quat fvalRot;
if (from!=NULL)
{
assert(from->SuperClassID()==CTRL_MATRIX3_CLASS_ID);
if (from->ClassID()== ANIMPIVOT_CONSTRAINT_CLASS_ID)
{
AnimPivotConst *ctrl = (AnimPivotConst*)from;
// a copy will construct its own pblock to keep the pblock-to-owner 1-to-1.
RemapDir *remap = NewRemapDir();
ReplaceReference(ANIMPIVOT_PBLOCK_REF, ctrl->pblock->Clone(*remap));
remap->DeleteThis();
ReplaceReference(ANIMPIVOT_POS_REF,(ReferenceTarget * )ctrl->pos->Clone(*remap));
ReplaceReference(ANIMPIVOT_ROT_REF,(ReferenceTarget * )ctrl->rot->Clone(*remap));
ReplaceReference(ANIMPIVOT_SCL_REF,(ReferenceTarget * )ctrl->scl->Clone(*remap));
ownMat = ctrl->ownMat;
} else {
//TODO: Rework following makeshift code...
Interval valid;
ownMat.IdentityMatrix();
from->GetValue(0,&ownMat,valid,CTRL_RELATIVE);
initialAnimPivotFlag = 1;
fvalRot = Quat(ownMat);
baseRotQuatLocal = fvalRot;
baseRotQuatLocal.Normalize();
}
}
ivalid.SetEmpty();
NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
}