1917 lines
45 KiB
C++
1917 lines
45 KiB
C++
![]() |
/*
|
||
|
** Command & Conquer Generals Zero Hour(tm)
|
||
|
** Copyright 2025 Electronic Arts Inc.
|
||
|
**
|
||
|
** This program is free software: you can redistribute it and/or modify
|
||
|
** it under the terms of the GNU General Public License as published by
|
||
|
** the Free Software Foundation, either version 3 of the License, or
|
||
|
** (at your option) any later version.
|
||
|
**
|
||
|
** This program is distributed in the hope that it will be useful,
|
||
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
** GNU General Public License for more details.
|
||
|
**
|
||
|
** You should have received a copy of the GNU General Public License
|
||
|
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
/***********************************************************************************************
|
||
|
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
|
||
|
***********************************************************************************************
|
||
|
* *
|
||
|
* Project Name : Commando Tools - WWSkin *
|
||
|
* *
|
||
|
* $Archive:: /Commando/Code/Tools/max2w3d/skin.cpp $*
|
||
|
* *
|
||
|
* $Author:: Greg_h $*
|
||
|
* *
|
||
|
* $Modtime:: 4/24/01 5:15p $*
|
||
|
* *
|
||
|
* $Revision:: 13 $*
|
||
|
* *
|
||
|
*---------------------------------------------------------------------------------------------*
|
||
|
* Functions: *
|
||
|
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
||
|
|
||
|
|
||
|
#include "skin.h"
|
||
|
#include "dllmain.h"
|
||
|
#include "max.h"
|
||
|
#include "simpmod.h"
|
||
|
#include "simpobj.h"
|
||
|
#include "resource.h"
|
||
|
|
||
|
#include "skindata.h"
|
||
|
#include "bpick.h"
|
||
|
#include "namedsel.h"
|
||
|
#include "boneicon.h"
|
||
|
|
||
|
#if defined W3D_MAX4 //defined as in the project (.dsp)
|
||
|
static GenSubObjType _SubObjectTypeVertex(1);
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
** Static functions
|
||
|
*/
|
||
|
static BOOL CALLBACK _sot_dialog_proc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);
|
||
|
static BOOL CALLBACK _skeleton_dialog_thunk(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);
|
||
|
static BOOL CALLBACK _bone_influence_dialog_thunk(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);
|
||
|
static TriObject * Get_Tri_Object(TimeValue t,ObjectState & os,Interval & valid,BOOL & needsdel);
|
||
|
static float Bone_Distance(INode * bone,TimeValue time,const Point3 & vertex);
|
||
|
|
||
|
/*
|
||
|
** Static variables
|
||
|
*/
|
||
|
HWND SkinWSMObjectClass::SotHWND = NULL;
|
||
|
HWND SkinWSMObjectClass::SkeletonHWND = NULL;
|
||
|
HWND SkinWSMObjectClass::BoneListHWND = NULL;
|
||
|
IObjParam * SkinWSMObjectClass::InterfacePtr = NULL;
|
||
|
ICustButton * SkinWSMObjectClass::AddBonesButton = NULL;
|
||
|
ICustButton * SkinWSMObjectClass::RemoveBonesButton = NULL;
|
||
|
ISpinnerControl * SkinWSMObjectClass::BasePoseSpin = NULL;
|
||
|
|
||
|
|
||
|
/*******************************************************************************
|
||
|
**
|
||
|
** Class Descriptor for SkinWSMObjectClass
|
||
|
**
|
||
|
*******************************************************************************/
|
||
|
class SkinWSMObjectClassDesc:public ClassDesc
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
int IsPublic() { return 1; }
|
||
|
void * Create(BOOL loading = FALSE) { return new SkinWSMObjectClass; }
|
||
|
const TCHAR * ClassName() { return _T("WWSkin"); }
|
||
|
SClass_ID SuperClassID() { return WSM_OBJECT_CLASS_ID; }
|
||
|
Class_ID ClassID() { return SKIN_OBJ_CLASS_ID; }
|
||
|
const TCHAR* Category() { return _T("Westwood Space Warps"); }
|
||
|
};
|
||
|
|
||
|
static SkinWSMObjectClassDesc _SkinWSMObjectDesc;
|
||
|
ClassDesc * Get_Skin_Obj_Desc() { return &_SkinWSMObjectDesc; }
|
||
|
|
||
|
|
||
|
/*******************************************************************************
|
||
|
**
|
||
|
** Class Descriptor for the SkinModifier
|
||
|
**
|
||
|
*******************************************************************************/
|
||
|
class SkinModClassDesc:public ClassDesc
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
int IsPublic() { return 0; }
|
||
|
void * Create(BOOL loading = FALSE) { return new SkinModifierClass; }
|
||
|
const TCHAR * ClassName() { return _T("WWSkin"); }
|
||
|
SClass_ID SuperClassID() { return WSM_CLASS_ID; }
|
||
|
Class_ID ClassID() { return SKIN_MOD_CLASS_ID; }
|
||
|
const TCHAR * Category() { return _T("Westwood Space Warps"); }
|
||
|
};
|
||
|
|
||
|
static SkinModClassDesc _SkinModDesc;
|
||
|
ClassDesc * Get_Skin_Mod_Desc() { return &_SkinModDesc; }
|
||
|
|
||
|
|
||
|
|
||
|
/*******************************************************************************
|
||
|
**
|
||
|
** SkinWSMObjectCreateCallback
|
||
|
** A class derived from CreateMouseCallBack to handle
|
||
|
** the user input during the creation phase of the SkinWSMObject.
|
||
|
**
|
||
|
*******************************************************************************/
|
||
|
class SkinWSMObjectCreateCallBack : public CreateMouseCallBack
|
||
|
{
|
||
|
public:
|
||
|
int proc( ViewExp * vpt,int msg, int point, int flags, IPoint2 m, Matrix3 & mat)
|
||
|
{
|
||
|
if (msg == MOUSE_POINT) {
|
||
|
Point3 pos = vpt->GetPointOnCP(m);
|
||
|
mat.IdentityMatrix();
|
||
|
mat.SetTrans(pos);
|
||
|
return CREATE_STOP;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
static SkinWSMObjectCreateCallBack _SkinCreateCB;
|
||
|
|
||
|
/*******************************************************************************
|
||
|
**
|
||
|
** SkinWSMObjectClass
|
||
|
**
|
||
|
*******************************************************************************/
|
||
|
SkinWSMObjectClass::SkinWSMObjectClass()
|
||
|
{
|
||
|
/*
|
||
|
** Initialize class variables to default state!
|
||
|
*/
|
||
|
MeshBuilt = FALSE;
|
||
|
BoneSelectionMode = BONE_SEL_MODE_NONE;
|
||
|
BoneTab.SetCount(0);
|
||
|
BasePoseFrame = 0;
|
||
|
|
||
|
pblock = NULL;
|
||
|
}
|
||
|
|
||
|
SkinWSMObjectClass::~SkinWSMObjectClass(void)
|
||
|
{
|
||
|
assert(!((InterfacePtr == NULL) && (SotHWND != NULL)));
|
||
|
if (SotHWND != NULL) {
|
||
|
InterfacePtr->UnRegisterDlgWnd(SotHWND);
|
||
|
InterfacePtr->DeleteRollupPage(SotHWND);
|
||
|
SotHWND = NULL;
|
||
|
}
|
||
|
|
||
|
assert(!((InterfacePtr == NULL) && (SkeletonHWND != NULL)));
|
||
|
if (SkeletonHWND != NULL) {
|
||
|
InterfacePtr->UnRegisterDlgWnd(SkeletonHWND);
|
||
|
InterfacePtr->DeleteRollupPage(SkeletonHWND);
|
||
|
SkeletonHWND = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SkinWSMObjectClass::BeginEditParams(IObjParam *ip, ULONG flags,Animatable *prev)
|
||
|
{
|
||
|
SimpleWSMObject::BeginEditParams(ip,flags,prev);
|
||
|
|
||
|
/*
|
||
|
** save off a copy of the interface pointer
|
||
|
*/
|
||
|
InterfacePtr = ip;
|
||
|
|
||
|
/*
|
||
|
** Install the "supports objects of type" rollup
|
||
|
*/
|
||
|
if (SotHWND == NULL) {
|
||
|
SotHWND = ip->AddRollupPage(
|
||
|
AppInstance,
|
||
|
MAKEINTRESOURCE(IDD_SKIN_SOT),
|
||
|
_sot_dialog_proc,
|
||
|
Get_String(IDS_SOT),
|
||
|
(LPARAM)InterfacePtr,
|
||
|
APPENDROLL_CLOSED);
|
||
|
} else {
|
||
|
SetWindowLong(SotHWND,GWL_USERDATA,(LPARAM)ip);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Install the skeleton rollup
|
||
|
*/
|
||
|
if (SkeletonHWND == NULL) {
|
||
|
SkeletonHWND = InterfacePtr->AddRollupPage(
|
||
|
AppInstance,
|
||
|
MAKEINTRESOURCE(IDD_SKELETON_PARAMETERS),
|
||
|
_skeleton_dialog_thunk,
|
||
|
Get_String(IDS_SKELETON_PARAMETERS),
|
||
|
(LPARAM)this,
|
||
|
0);
|
||
|
} else {
|
||
|
SetWindowLong(SkeletonHWND,GWL_USERDATA,(LPARAM)this);
|
||
|
}
|
||
|
|
||
|
Update_Bone_List();
|
||
|
}
|
||
|
|
||
|
void SkinWSMObjectClass::EndEditParams(IObjParam *ip, ULONG flags,Animatable *next)
|
||
|
{
|
||
|
SimpleWSMObject::EndEditParams(ip,flags,next);
|
||
|
|
||
|
if (flags & END_EDIT_REMOVEUI) {
|
||
|
/*
|
||
|
** Remove the Sot rollup
|
||
|
*/
|
||
|
if (SotHWND != NULL) {
|
||
|
InterfacePtr->UnRegisterDlgWnd(SotHWND);
|
||
|
InterfacePtr->DeleteRollupPage(SotHWND);
|
||
|
SotHWND = NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Remove the info rollup
|
||
|
*/
|
||
|
if (SkeletonHWND != NULL) {
|
||
|
InterfacePtr->UnRegisterDlgWnd(SkeletonHWND);
|
||
|
InterfacePtr->DeleteRollupPage(SkeletonHWND);
|
||
|
SkeletonHWND = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** get rid of our copy of the interface pointer
|
||
|
*/
|
||
|
InterfacePtr = NULL;
|
||
|
}
|
||
|
|
||
|
RefTargetHandle SkinWSMObjectClass::Clone(RemapDir & remap)
|
||
|
{
|
||
|
/*
|
||
|
** create another SkinWSMObject and return it.
|
||
|
*/
|
||
|
SkinWSMObjectClass * sobj = new SkinWSMObjectClass();
|
||
|
return(sobj);
|
||
|
}
|
||
|
|
||
|
RefTargetHandle SkinWSMObjectClass::GetReference(int i)
|
||
|
{
|
||
|
/*
|
||
|
** return reference "i". If i==0, the reference belongs
|
||
|
** to SimpleWSMObject so thunk down to it.
|
||
|
*/
|
||
|
if (i < SimpleWSMObject::NumRefs()) {
|
||
|
return SimpleWSMObject::GetReference(i);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** The rest of the references are ours.
|
||
|
*/
|
||
|
int boneidx = To_Bone_Index(i);
|
||
|
return BoneTab[boneidx];
|
||
|
}
|
||
|
|
||
|
void SkinWSMObjectClass::SetReference(int i, RefTargetHandle rtarg)
|
||
|
{
|
||
|
if (i < SimpleWSMObject::NumRefs()) {
|
||
|
SimpleWSMObject::SetReference(i,rtarg);
|
||
|
} else {
|
||
|
int boneidx = To_Bone_Index(i);
|
||
|
assert(boneidx >= 0);
|
||
|
assert(boneidx < BoneTab.Count());
|
||
|
BoneTab[boneidx] = (INode *)rtarg;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RefResult SkinWSMObjectClass::NotifyRefChanged(Interval changeInt,RefTargetHandle hTarget,PartID& partID, RefMessage message)
|
||
|
{
|
||
|
int i;
|
||
|
switch (message) {
|
||
|
|
||
|
case REFMSG_TARGET_DELETED:
|
||
|
for (i=0; i<BoneTab.Count(); i++) {
|
||
|
if (BoneTab[i] == hTarget) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (i < BoneTab.Count()) {
|
||
|
BoneTab.Delete(i,1);
|
||
|
// TODO: cause all Modifier objects to re-index to accomodate
|
||
|
// the deletion of this bone!!
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
return(REF_SUCCEED);
|
||
|
}
|
||
|
|
||
|
CreateMouseCallBack * SkinWSMObjectClass::GetCreateMouseCallBack(void)
|
||
|
{
|
||
|
/*
|
||
|
** The "CreateMouseCallback" is used when creating the
|
||
|
** object. Since our object doesn't need an interactive
|
||
|
** creation phase, we return NULL.
|
||
|
*/
|
||
|
return &_SkinCreateCB;
|
||
|
}
|
||
|
|
||
|
Modifier * SkinWSMObjectClass::CreateWSMMod(INode *node)
|
||
|
{
|
||
|
/*
|
||
|
** create an instance of a SkinModifierClass...
|
||
|
*/
|
||
|
return new SkinModifierClass(node,this);
|
||
|
}
|
||
|
|
||
|
void SkinWSMObjectClass::BuildMesh(TimeValue t)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
/*
|
||
|
** We only need to build the object mesh once since it
|
||
|
** doesn't change or animate...
|
||
|
*/
|
||
|
if (MeshBuilt) return;
|
||
|
|
||
|
/*
|
||
|
** Set our validity interval, since this mesh doesn't animate
|
||
|
** we just use FOREVER.
|
||
|
*/
|
||
|
ivalid = FOREVER;
|
||
|
|
||
|
/*
|
||
|
** Ok, this is the first time BuildMesh has been called.
|
||
|
** Create a mesh which will represent this space warp object in
|
||
|
** in the max viewports. Note that we are using the mesh
|
||
|
** member which is inherited from SimpleWSMObject.
|
||
|
*/
|
||
|
mesh.setNumVerts(NumBoneIconVerts);
|
||
|
mesh.setNumFaces(NumBoneIconFaces);
|
||
|
for (i=0; i<NumBoneIconVerts; i++) {
|
||
|
mesh.setVert(i, Point3(BoneIconVerts[i].X, BoneIconVerts[i].Y, BoneIconVerts[i].Z));
|
||
|
}
|
||
|
|
||
|
for (i=0; i<NumBoneIconFaces; i++) {
|
||
|
Build_Tri(&(mesh.faces[i]),BoneIconFaces[i].V0,BoneIconFaces[i].V1,BoneIconFaces[i].V2);
|
||
|
}
|
||
|
|
||
|
mesh.InvalidateGeomCache();
|
||
|
|
||
|
MeshBuilt = TRUE;
|
||
|
}
|
||
|
|
||
|
void SkinWSMObjectClass::Build_Tri(Face * f, int a, int b, int c)
|
||
|
{
|
||
|
f->setVerts(a, b, c);
|
||
|
f->setSmGroup(0);
|
||
|
f->setEdgeVisFlags(1,1,1);
|
||
|
}
|
||
|
|
||
|
void SkinWSMObjectClass::User_Picked_Bone(INode * node)
|
||
|
{
|
||
|
// TODO: Undo/Redo!
|
||
|
switch (BoneSelectionMode) {
|
||
|
case BONE_SEL_MODE_ADD:
|
||
|
Add_Bone(node);
|
||
|
break;
|
||
|
|
||
|
case BONE_SEL_MODE_REMOVE:
|
||
|
Remove_Bone(node);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
assert(0);
|
||
|
}
|
||
|
|
||
|
Set_Bone_Selection_Mode(BONE_SEL_MODE_NONE);
|
||
|
Update_Bone_List();
|
||
|
}
|
||
|
|
||
|
void SkinWSMObjectClass::User_Picked_Bones(INodeTab & nodetab)
|
||
|
{
|
||
|
// TODO: Undo/Redo!
|
||
|
switch (BoneSelectionMode) {
|
||
|
case BONE_SEL_MODE_ADD_MANY:
|
||
|
Add_Bones(nodetab);
|
||
|
break;
|
||
|
|
||
|
case BONE_SEL_MODE_REMOVE_MANY:
|
||
|
Remove_Bones(nodetab);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
assert(0);
|
||
|
}
|
||
|
|
||
|
Set_Bone_Selection_Mode(BONE_SEL_MODE_NONE);
|
||
|
Update_Bone_List();
|
||
|
}
|
||
|
|
||
|
void SkinWSMObjectClass::Set_Bone_Selection_Mode(int mode)
|
||
|
{
|
||
|
assert(mode >= BONE_SEL_MODE_NONE);
|
||
|
assert(mode <= BONE_SEL_MODE_REMOVE_MANY);
|
||
|
|
||
|
/*
|
||
|
** store the selection mode
|
||
|
*/
|
||
|
BoneSelectionMode = mode;
|
||
|
|
||
|
/*
|
||
|
** update the dialog box buttons
|
||
|
*/
|
||
|
AddBonesButton->SetCheck(mode == BONE_SEL_MODE_ADD_MANY);
|
||
|
RemoveBonesButton->SetCheck(mode == BONE_SEL_MODE_REMOVE_MANY);
|
||
|
}
|
||
|
|
||
|
int SkinWSMObjectClass::Add_Bone(INode * node)
|
||
|
{
|
||
|
int refidx;
|
||
|
int boneidx;
|
||
|
|
||
|
/*
|
||
|
** If we already have this bone, just return
|
||
|
*/
|
||
|
boneidx = Find_Bone(node);
|
||
|
if (boneidx != -1) {
|
||
|
return boneidx;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Otherwise, look for a NULL bone and we'll re-use
|
||
|
** its slot. This happens when a user removes a bone or
|
||
|
** a bone in the scene is deleted.
|
||
|
*/
|
||
|
boneidx = Find_Bone(NULL);
|
||
|
if (boneidx != -1) {
|
||
|
refidx = To_Ref_Index(boneidx);
|
||
|
MakeRefByID(FOREVER,refidx,node);
|
||
|
return boneidx;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** If we made it here, add the bone to the end of the
|
||
|
** reference array.
|
||
|
*/
|
||
|
BoneTab.Append(1,&node);
|
||
|
boneidx = BoneTab.Count() - 1;
|
||
|
refidx = To_Ref_Index(boneidx);
|
||
|
MakeRefByID(FOREVER,refidx,node);
|
||
|
return boneidx;
|
||
|
}
|
||
|
|
||
|
void SkinWSMObjectClass::Add_Bones(INodeTab & nodetab)
|
||
|
{
|
||
|
/*
|
||
|
** Add each bone individually
|
||
|
*/
|
||
|
for (int i=0; i<nodetab.Count(); i++) {
|
||
|
Add_Bone(nodetab[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SkinWSMObjectClass::Remove_Bone(INode * node)
|
||
|
{
|
||
|
int boneidx = Find_Bone(node);
|
||
|
if (boneidx != -1) {
|
||
|
BoneTab[boneidx] = NULL;
|
||
|
DeleteReference(To_Ref_Index(boneidx));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SkinWSMObjectClass::Remove_Bones(INodeTab & nodetab)
|
||
|
{
|
||
|
/*
|
||
|
** remove each bone
|
||
|
*/
|
||
|
for (int i=0; i<nodetab.Count(); i++) {
|
||
|
Remove_Bone(nodetab[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SkinWSMObjectClass::Update_Bone_List(void)
|
||
|
{
|
||
|
assert(BoneListHWND != NULL);
|
||
|
|
||
|
/*
|
||
|
** remove all strings in the bone listbox
|
||
|
*/
|
||
|
SendMessage(BoneListHWND,LB_RESETCONTENT,0,0);
|
||
|
|
||
|
/*
|
||
|
** loop through the bone tab and add the name of each
|
||
|
*/
|
||
|
for (int i=0; i<BoneTab.Count(); i++) {
|
||
|
if (BoneTab[i] != NULL) {
|
||
|
SendMessage(BoneListHWND,LB_ADDSTRING,0,(LPARAM)BoneTab[i]->GetName());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int SkinWSMObjectClass::Find_Bone(INode * node)
|
||
|
{
|
||
|
for (int i=0; i<BoneTab.Count(); i++) {
|
||
|
if (BoneTab[i] == node) return i;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
IOResult SkinWSMObjectClass::Save(ISave * isave)
|
||
|
{
|
||
|
ULONG nb;
|
||
|
SimpleWSMObject::Save(isave);
|
||
|
|
||
|
/*
|
||
|
** Save the number of bones
|
||
|
*/
|
||
|
ULONG numbones = BoneTab.Count();
|
||
|
if (numbones > 0) {
|
||
|
isave->BeginChunk(NUM_BONES_CHUNK);
|
||
|
isave->Write(&numbones,sizeof(ULONG),&nb);
|
||
|
isave->EndChunk();
|
||
|
}
|
||
|
return IO_OK;
|
||
|
}
|
||
|
|
||
|
IOResult SkinWSMObjectClass::Load(ILoad * iload)
|
||
|
{
|
||
|
SimpleWSMObject::Load(iload);
|
||
|
|
||
|
IOResult res;
|
||
|
ULONG nb;
|
||
|
int level = -1;
|
||
|
|
||
|
while (IO_OK==(res=iload->OpenChunk())) {
|
||
|
|
||
|
switch (iload->CurChunkID()) {
|
||
|
|
||
|
case NUM_BONES_CHUNK: {
|
||
|
ULONG numbones;
|
||
|
res = iload->Read(&numbones,sizeof(numbones),&nb);
|
||
|
BoneTab.SetCount(numbones);
|
||
|
for (int i=0; i<BoneTab.Count(); i++) {
|
||
|
BoneTab[i] = NULL;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
iload->CloseChunk();
|
||
|
|
||
|
if (res!=IO_OK) {
|
||
|
return res;
|
||
|
}
|
||
|
}
|
||
|
return IO_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
int SkinWSMObjectClass::Find_Closest_Bone(const Point3 & vertex)
|
||
|
{
|
||
|
float mindist = 10000.0f;
|
||
|
int minindex = -1;
|
||
|
TimeValue basetime = Get_Base_Pose_Time();
|
||
|
|
||
|
for (int boneidx = 0; boneidx < BoneTab.Count(); boneidx++) {
|
||
|
|
||
|
if (BoneTab[boneidx] == NULL) continue;
|
||
|
|
||
|
float bonedist = Bone_Distance(BoneTab[boneidx],basetime,vertex);
|
||
|
if (bonedist < mindist) {
|
||
|
mindist = bonedist;
|
||
|
minindex = boneidx;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return minindex;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*******************************************************************************
|
||
|
**
|
||
|
** SkinModifierClass
|
||
|
**
|
||
|
*******************************************************************************/
|
||
|
|
||
|
SkinModifierClass::SkinModifierClass(void)
|
||
|
{
|
||
|
Default_Init();
|
||
|
}
|
||
|
|
||
|
SkinModifierClass::SkinModifierClass(INode * node,SkinWSMObjectClass * skin_obj)
|
||
|
{
|
||
|
Default_Init();
|
||
|
|
||
|
/*
|
||
|
** Make the reference to the space warp node.
|
||
|
*/
|
||
|
MakeRefByID(FOREVER,NODE_REF,node);
|
||
|
|
||
|
/*
|
||
|
** Make reference to the WSMObject
|
||
|
*/
|
||
|
MakeRefByID(FOREVER,OBJ_REF,skin_obj);
|
||
|
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::Default_Init(void)
|
||
|
{
|
||
|
SubObjSelLevel = VERTEX_SEL_LEVEL;
|
||
|
WSMObjectRef = NULL;
|
||
|
WSMNodeRef = NULL;
|
||
|
InterfacePtr = NULL;
|
||
|
|
||
|
BoneInfluenceHWND = NULL;
|
||
|
LinkButton = NULL;
|
||
|
LinkByNameButton = NULL;
|
||
|
AutoLinkButton = NULL;
|
||
|
UnLinkButton = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
RefTargetHandle SkinModifierClass::Clone(RemapDir & remap)
|
||
|
{
|
||
|
SkinModifierClass * newmod = new SkinModifierClass(WSMNodeRef,(SkinWSMObjectClass *)WSMObjectRef);
|
||
|
return newmod;
|
||
|
}
|
||
|
|
||
|
|
||
|
void SkinModifierClass::BeginEditParams(IObjParam * ip, ULONG flags,Animatable * prev)
|
||
|
{
|
||
|
static int i=0;
|
||
|
|
||
|
i++;
|
||
|
|
||
|
/*
|
||
|
** Grab a copy of the interface pointer
|
||
|
*/
|
||
|
InterfacePtr = ip;
|
||
|
|
||
|
/*
|
||
|
** allocate the selection command mode for use in vertex selection
|
||
|
*/
|
||
|
SelectMode = new SelectModBoxCMode(this,InterfacePtr);
|
||
|
|
||
|
/*
|
||
|
** register the desired sub-object selection types.
|
||
|
*/
|
||
|
const TCHAR * ptype[] = { "Vertices" };
|
||
|
#if defined W3D_MAX4 //defined as in the project (.dsp)
|
||
|
InterfacePtr->SetSubObjectLevel(1);
|
||
|
#else
|
||
|
//---This call is obsolete from version 4.
|
||
|
InterfacePtr->RegisterSubObjectTypes( ptype, 1);
|
||
|
#endif
|
||
|
/*
|
||
|
** Restore the selection level.
|
||
|
*/
|
||
|
ip->SetSubObjectLevel(SubObjSelLevel);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
void SkinModifierClass::EndEditParams(IObjParam *ip, ULONG flags,Animatable *next)
|
||
|
{
|
||
|
/*
|
||
|
** just checking...
|
||
|
*/
|
||
|
assert(ip == InterfacePtr);
|
||
|
|
||
|
/*
|
||
|
** Make sure we clear out the pick mode
|
||
|
*/
|
||
|
InterfacePtr->ClearPickMode();
|
||
|
|
||
|
/*
|
||
|
** remove and deallocate the selection command mode
|
||
|
*/
|
||
|
InterfacePtr->DeleteMode(SelectMode);
|
||
|
if (SelectMode ) delete SelectMode;
|
||
|
SelectMode = NULL;
|
||
|
|
||
|
/*
|
||
|
** Remove the rollup window(s) if needed
|
||
|
*/
|
||
|
if (flags & END_EDIT_REMOVEUI) {
|
||
|
Remove_Bone_Influence_Dialog();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Make sure we don't hang onto an invalid interface
|
||
|
*/
|
||
|
InterfacePtr = NULL;
|
||
|
}
|
||
|
|
||
|
Interval SkinModifierClass::Get_Validity(TimeValue t)
|
||
|
{
|
||
|
/*
|
||
|
** Start with an infinite interval and chop it down
|
||
|
** using the validity intervals of each of the controlling bones
|
||
|
*/
|
||
|
Interval valid = FOREVER;
|
||
|
|
||
|
/*
|
||
|
** Now intersect the validity with the validities of all of
|
||
|
** the controlling bones.
|
||
|
*/
|
||
|
SkinWSMObjectClass * obj = (SkinWSMObjectClass *)Get_WSMObject();
|
||
|
|
||
|
// for (int i=0; i<obj->Num_Bones(); i++) {
|
||
|
// valid &= obj->Get_Bone(i)->tmValid(); //TODO: is this right?
|
||
|
// }
|
||
|
|
||
|
// return valid;
|
||
|
return Interval(t,t+1); //KLUDGE - only valid for this frame
|
||
|
}
|
||
|
|
||
|
RefTargetHandle SkinModifierClass::GetReference(int i)
|
||
|
{
|
||
|
switch (i) {
|
||
|
case OBJ_REF: return WSMObjectRef;
|
||
|
case NODE_REF: return WSMNodeRef;
|
||
|
default: return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::SetReference(int i, RefTargetHandle rtarg)
|
||
|
{
|
||
|
switch (i) {
|
||
|
case OBJ_REF: WSMObjectRef = (SkinWSMObjectClass *)rtarg; break;
|
||
|
case NODE_REF: WSMNodeRef = (INode *)rtarg; break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
RefResult SkinModifierClass::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message)
|
||
|
{
|
||
|
switch (message) {
|
||
|
|
||
|
case REFMSG_TARGET_DELETED:
|
||
|
|
||
|
/*
|
||
|
** This means the WSM node is being deleted. As a result,
|
||
|
** we must delete ourselves.
|
||
|
*/
|
||
|
DeleteMe(); // also deletes all refs and
|
||
|
// sends REFMSG_TARGET_DELETED to all Dependents
|
||
|
return REF_STOP;
|
||
|
}
|
||
|
|
||
|
return(REF_SUCCEED);
|
||
|
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::ModifyObject(TimeValue t, ModContext & mc, ObjectState * os, INode * node)
|
||
|
{
|
||
|
/*
|
||
|
** Get a TriObject from the object state
|
||
|
*/
|
||
|
assert(os->obj->IsSubClassOf(triObjectClassID));
|
||
|
TriObject *triobj = (TriObject *)os->obj;
|
||
|
|
||
|
/*
|
||
|
** Get the skin data from the ModContext.
|
||
|
*/
|
||
|
SkinDataClass * skindata = (SkinDataClass *)mc.localData;
|
||
|
|
||
|
/*
|
||
|
** If there is no skin data, allocate it
|
||
|
** Also, do an initial auto attach.
|
||
|
*/
|
||
|
if (skindata == NULL) {
|
||
|
mc.localData = skindata = new SkinDataClass(&triobj->mesh);
|
||
|
}
|
||
|
|
||
|
if (!skindata->IsValid()) {
|
||
|
skindata->Validate(&triobj->mesh);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** If in vertex selection mode, tell the mesh to display the
|
||
|
** selected vertices and turn on vertex tick marks. Otherwise
|
||
|
** make sure vertex tick marks are off.
|
||
|
*/
|
||
|
if (SubObjSelLevel == VERTEX_SEL_LEVEL) {
|
||
|
triobj->mesh.vertSel = skindata->VertSel;
|
||
|
triobj->mesh.SetDispFlag(DISP_VERTTICKS|DISP_SELVERTS);
|
||
|
|
||
|
if (triobj->mesh.selLevel != MESH_VERTEX) {
|
||
|
triobj->mesh.selLevel = MESH_VERTEX;
|
||
|
}
|
||
|
} else {
|
||
|
triobj->mesh.selLevel = MESH_OBJECT;
|
||
|
triobj->mesh.ClearDispFlag(DISP_VERTTICKS|DISP_SELVERTS);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Loop through the points in the deformable object
|
||
|
*/
|
||
|
for (int vidx = 0; vidx < triobj->NumPoints(); vidx++) {
|
||
|
|
||
|
|
||
|
// TODO: Allow multiple bone influences here...
|
||
|
// issues - UI to set the weights, rebalance weights whenever
|
||
|
// a bone is deleted, should also then never get NULL bones
|
||
|
// and remove the need to check for NULL bones in this routine...
|
||
|
|
||
|
/*
|
||
|
** Get a pointer to the bone that this vertex is attached to
|
||
|
*/
|
||
|
InfluenceStruct * inf = &(skindata->VertData[vidx]);
|
||
|
|
||
|
int boneidx = inf->BoneIdx[0];
|
||
|
if ((boneidx != -1) && (boneidx < WSMObjectRef->Num_Bones())) {
|
||
|
|
||
|
INode * bone = WSMObjectRef->Get_Bone(inf->BoneIdx[0]);
|
||
|
|
||
|
if (bone == NULL) {
|
||
|
/*
|
||
|
** this bone has gone away for some reason so
|
||
|
** clear this vert's bone influence index
|
||
|
*/
|
||
|
inf->BoneIdx[0] = -1;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
/*
|
||
|
** Ok, got the bone, now transform the point and
|
||
|
** give it back to the mesh
|
||
|
*/
|
||
|
Point3 pnew;
|
||
|
Matrix3 tm;
|
||
|
|
||
|
pnew = triobj->GetPoint(vidx);
|
||
|
|
||
|
if (os->GetTM()) {
|
||
|
tm = *(os->GetTM());
|
||
|
} else {
|
||
|
tm.IdentityMatrix();
|
||
|
}
|
||
|
|
||
|
pnew = tm * pnew;
|
||
|
|
||
|
TimeValue basetime = WSMObjectRef->Get_Base_Pose_Time();
|
||
|
Matrix3 basetm = bone->GetObjectTM(basetime);
|
||
|
Matrix3 curtm = bone->GetObjectTM(t);
|
||
|
pnew = (pnew * Inverse(basetm)) * curtm;
|
||
|
|
||
|
pnew = Inverse(tm) * pnew;
|
||
|
|
||
|
triobj->SetPoint(vidx,pnew);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Tell the object that points were changed
|
||
|
*/
|
||
|
triobj->PointsWereChanged();
|
||
|
|
||
|
/*
|
||
|
** Set the validity of the updated geometry data
|
||
|
*/
|
||
|
triobj->UpdateValidity(GEOM_CHAN_NUM,Get_Validity(t));
|
||
|
}
|
||
|
|
||
|
IOResult SkinModifierClass::Save(ISave * isave)
|
||
|
{
|
||
|
ULONG nb;
|
||
|
Modifier::Save(isave);
|
||
|
|
||
|
/*
|
||
|
** Save the sub object selection level
|
||
|
*/
|
||
|
short sl = SubObjSelLevel;
|
||
|
isave->BeginChunk(SEL_LEVEL_CHUNK);
|
||
|
isave->Write(&sl,sizeof(short),&nb);
|
||
|
isave->EndChunk();
|
||
|
|
||
|
return IO_OK;
|
||
|
}
|
||
|
|
||
|
IOResult SkinModifierClass::Load(ILoad * iload)
|
||
|
{
|
||
|
Modifier::Load(iload);
|
||
|
|
||
|
IOResult res;
|
||
|
ULONG nb;
|
||
|
int level = -1;
|
||
|
|
||
|
while (IO_OK==(res=iload->OpenChunk())) {
|
||
|
|
||
|
switch (iload->CurChunkID()) {
|
||
|
|
||
|
case SEL_LEVEL_CHUNK: {
|
||
|
short sl;
|
||
|
res = iload->Read(&sl,sizeof(short),&nb);
|
||
|
SubObjSelLevel = sl;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
iload->CloseChunk();
|
||
|
|
||
|
if (res!=IO_OK) {
|
||
|
return res;
|
||
|
}
|
||
|
}
|
||
|
return IO_OK;
|
||
|
}
|
||
|
|
||
|
IOResult SkinModifierClass::SaveLocalData(ISave *isave, LocalModData *ld)
|
||
|
{
|
||
|
SkinDataClass * skindata = (SkinDataClass *)ld;
|
||
|
|
||
|
return skindata->Save(isave);
|
||
|
}
|
||
|
|
||
|
IOResult SkinModifierClass::LoadLocalData(ILoad *iload, LocalModData **pld)
|
||
|
{
|
||
|
/*
|
||
|
** Create a new SkinDataClass
|
||
|
*/
|
||
|
if (*pld==NULL) {
|
||
|
*pld = (SkinDataClass *) new SkinDataClass();
|
||
|
}
|
||
|
SkinDataClass * newskin = (SkinDataClass *)*pld;
|
||
|
|
||
|
/*
|
||
|
** Initialize it from ILoad...
|
||
|
*/
|
||
|
return newskin->Load(iload);
|
||
|
}
|
||
|
|
||
|
|
||
|
void SkinModifierClass::ActivateSubobjSel(int level, XFormModes & modes)
|
||
|
{
|
||
|
/*
|
||
|
** Storing the current sub-object selection level
|
||
|
*/
|
||
|
SubObjSelLevel = level;
|
||
|
|
||
|
/*
|
||
|
** Set the appropriate command mode. We only want selection.
|
||
|
*/
|
||
|
switch (SubObjSelLevel)
|
||
|
{
|
||
|
case OBJECT_SEL_LEVEL:
|
||
|
Remove_Bone_Influence_Dialog();
|
||
|
break;
|
||
|
|
||
|
case VERTEX_SEL_LEVEL: // Modifying Vertices
|
||
|
modes = XFormModes(NULL,NULL,NULL,NULL,NULL,SelectMode);
|
||
|
Install_Bone_Influence_Dialog();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Put our named subobject selection sets into the drop down list
|
||
|
*/
|
||
|
Create_Named_Selection_Sets();
|
||
|
|
||
|
/*
|
||
|
** Notify our dependents that the subselection type,
|
||
|
** and the display have changed
|
||
|
*/
|
||
|
NotifyDependents(FOREVER, PART_SUBSEL_TYPE|PART_DISPLAY, REFMSG_CHANGE);
|
||
|
|
||
|
/*
|
||
|
** Notify the pipeline that the selection level has changed.
|
||
|
*/
|
||
|
InterfacePtr->PipeSelLevelChanged();
|
||
|
|
||
|
/*
|
||
|
** Notify our dependents that the selection channel,
|
||
|
** display attributes, and subselection type channels have changed
|
||
|
*/
|
||
|
NotifyDependents(FOREVER, SELECT_CHANNEL|DISP_ATTRIB_CHANNEL|SUBSEL_TYPE_CHANNEL, REFMSG_CHANGE);
|
||
|
}
|
||
|
|
||
|
int SkinModifierClass::HitTest
|
||
|
(
|
||
|
TimeValue t,
|
||
|
INode * inode,
|
||
|
int type,
|
||
|
int crossing,
|
||
|
int flags,
|
||
|
IPoint2 * p,
|
||
|
ViewExp * vpt,
|
||
|
ModContext * mc
|
||
|
)
|
||
|
{
|
||
|
Interval valid = FOREVER;
|
||
|
int needsdel;
|
||
|
int savedLimits;
|
||
|
int res = 0;
|
||
|
HitRegion hr;
|
||
|
Matrix3 mat;
|
||
|
|
||
|
MakeHitRegion(hr,type, crossing,4,p);
|
||
|
mat = inode->GetObjectTM(t);
|
||
|
|
||
|
/*
|
||
|
** Set up the graphics window to do the hit test
|
||
|
*/
|
||
|
GraphicsWindow *gw = vpt->getGW();
|
||
|
gw->setHitRegion(&hr);
|
||
|
gw->setTransform(mat);
|
||
|
gw->setRndLimits(((savedLimits = gw->getRndLimits()) | GW_PICK) & ~GW_ILLUM);
|
||
|
|
||
|
if (1 /*IgnoreBackfaces*/) {
|
||
|
gw->setRndLimits(gw->getRndLimits() | GW_BACKCULL);
|
||
|
} else {
|
||
|
gw->setRndLimits(gw->getRndLimits() & ~GW_BACKCULL);
|
||
|
}
|
||
|
|
||
|
gw->clearHitCode();
|
||
|
|
||
|
/*
|
||
|
** Do the hit test!
|
||
|
*/
|
||
|
SubObjHitList hitlist;
|
||
|
MeshSubHitRec * rec;
|
||
|
|
||
|
ObjectState os = inode->EvalWorldState(InterfacePtr->GetTime());
|
||
|
TriObject * tobj = Get_Tri_Object(InterfacePtr->GetTime(),os,valid,needsdel);
|
||
|
res = tobj->mesh.SubObjectHitTest(gw,gw->getMaterial(),&hr,flags | SUBHIT_VERTS,hitlist);
|
||
|
|
||
|
/*
|
||
|
** Record all of the hits
|
||
|
*/
|
||
|
rec = hitlist.First();
|
||
|
while (rec)
|
||
|
{
|
||
|
/*
|
||
|
** rec->index is the index of vertex which was hit!
|
||
|
** Remember that we are always turning on vertex hit testing;
|
||
|
** if we were testing for edges, index would be the edge index.
|
||
|
*/
|
||
|
vpt->LogHit(inode,mc,rec->dist,rec->index,NULL);
|
||
|
rec = rec->Next();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Cleanup
|
||
|
*/
|
||
|
gw->setRndLimits(savedLimits);
|
||
|
|
||
|
if (needsdel) {
|
||
|
tobj->DeleteThis();
|
||
|
}
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::SelectSubComponent(HitRecord *hitRec, BOOL selected, BOOL all, BOOL invert)
|
||
|
{
|
||
|
SkinDataClass * skindata = NULL;
|
||
|
int count = 0;
|
||
|
|
||
|
switch (SubObjSelLevel) {
|
||
|
|
||
|
case VERTEX_SEL_LEVEL:
|
||
|
|
||
|
while (hitRec) {
|
||
|
|
||
|
skindata = (SkinDataClass *)hitRec->modContext->localData;
|
||
|
|
||
|
/*
|
||
|
** Undo/Redo functionality
|
||
|
*/
|
||
|
#if 0
|
||
|
if (theHold.Holding() && !SelData->held) {
|
||
|
theHold.Put(new SubSelRestore(this,SelData));
|
||
|
}
|
||
|
theHold.Accept(_T("Select Vertex"));
|
||
|
#endif
|
||
|
|
||
|
BitArray * array = &(skindata->VertSel);
|
||
|
|
||
|
if (all & invert) {
|
||
|
|
||
|
/*
|
||
|
** hitRec->hitInfo is the MeshSubHitRec::index that was stored in the
|
||
|
** HitTest method through LogHit
|
||
|
*/
|
||
|
if ((*array)[hitRec->hitInfo]) {
|
||
|
array->Clear(hitRec->hitInfo);
|
||
|
} else {
|
||
|
array->Set(hitRec->hitInfo,selected);
|
||
|
}
|
||
|
} else {
|
||
|
array->Set(hitRec->hitInfo,selected);
|
||
|
}
|
||
|
|
||
|
if (!all) break;
|
||
|
hitRec = hitRec->Next();
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
NotifyDependents(FOREVER, PART_SELECT, REFMSG_CHANGE);
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::ClearSelection(int selLevel)
|
||
|
{
|
||
|
int needsdel = 0;
|
||
|
Interval valid = FOREVER;
|
||
|
ModContextList mcList;
|
||
|
INodeTab nodes;
|
||
|
|
||
|
if (!InterfacePtr ) return;
|
||
|
|
||
|
InterfacePtr->GetModContexts(mcList,nodes);
|
||
|
InterfacePtr->ClearCurNamedSelSet();
|
||
|
|
||
|
for (int i = 0; i < mcList.Count(); i++) {
|
||
|
|
||
|
SkinDataClass * skindata = (SkinDataClass *)mcList[i]->localData;
|
||
|
|
||
|
if (skindata==NULL) continue;
|
||
|
|
||
|
ObjectState os = nodes[i]->EvalWorldState(InterfacePtr->GetTime());
|
||
|
TriObject * tobj = Get_Tri_Object(InterfacePtr->GetTime(),os,valid,needsdel);
|
||
|
|
||
|
switch (SubObjSelLevel) {
|
||
|
#if 0
|
||
|
case OBJECT_SEL_LEVEL:
|
||
|
assert(0);
|
||
|
return;
|
||
|
#endif
|
||
|
|
||
|
case VERTEX_SEL_LEVEL:
|
||
|
#if 0 // undo/redo
|
||
|
if (theHold.Holding()) {
|
||
|
theHold.Put(new VertexSelRestore(meshData,this));
|
||
|
}
|
||
|
#endif
|
||
|
tobj->mesh.vertSel.ClearAll();
|
||
|
skindata->VertSel.ClearAll();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (needsdel) {
|
||
|
tobj->DeleteThis();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Get rid of the temporary copies of the INodes.
|
||
|
*/
|
||
|
nodes.DisposeTemporary();
|
||
|
|
||
|
/*
|
||
|
** Tell our dependents that the selection set has changed
|
||
|
*/
|
||
|
NotifyDependents(FOREVER, PART_SELECT, REFMSG_CHANGE);
|
||
|
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::SelectAll(int selLevel)
|
||
|
{
|
||
|
int needsdel = 0;
|
||
|
Interval valid = FOREVER;
|
||
|
ModContextList mclist;
|
||
|
INodeTab nodes;
|
||
|
|
||
|
if (!InterfacePtr) return;
|
||
|
|
||
|
InterfacePtr->GetModContexts(mclist,nodes);
|
||
|
InterfacePtr->ClearCurNamedSelSet();
|
||
|
|
||
|
for (int i = 0; i < mclist.Count(); i++) {
|
||
|
|
||
|
SkinDataClass * skindata = (SkinDataClass *)mclist[i]->localData;
|
||
|
|
||
|
if (skindata==NULL) continue;
|
||
|
|
||
|
ObjectState os = nodes[i]->EvalWorldState(InterfacePtr->GetTime());
|
||
|
TriObject * tobj = Get_Tri_Object(InterfacePtr->GetTime(),os,valid,needsdel);
|
||
|
|
||
|
switch (SubObjSelLevel) {
|
||
|
|
||
|
case OBJECT_SEL_LEVEL:
|
||
|
assert(0);
|
||
|
return;
|
||
|
|
||
|
case VERTEX_SEL_LEVEL:
|
||
|
#if 0 // undo/redo
|
||
|
if (theHold.Holding()) {
|
||
|
theHold.Put(new VertexSelRestore(meshData,this));
|
||
|
}
|
||
|
#endif
|
||
|
tobj->mesh.vertSel.SetAll();
|
||
|
skindata->VertSel.SetAll();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (needsdel) {
|
||
|
tobj->DeleteThis();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Get rid of the temporary copies of the INodes.
|
||
|
*/
|
||
|
nodes.DisposeTemporary();
|
||
|
|
||
|
/*
|
||
|
** Tell our dependents that the selection set has changed
|
||
|
*/
|
||
|
NotifyDependents(FOREVER, PART_SELECT, REFMSG_CHANGE);
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::InvertSelection(int selLevel)
|
||
|
{
|
||
|
int needsdel = 0;
|
||
|
Interval valid = FOREVER;
|
||
|
ModContextList mclist;
|
||
|
INodeTab nodes;
|
||
|
|
||
|
if (!InterfacePtr) return;
|
||
|
|
||
|
InterfacePtr->GetModContexts(mclist,nodes);
|
||
|
InterfacePtr->ClearCurNamedSelSet();
|
||
|
|
||
|
for (int i = 0; i < mclist.Count(); i++) {
|
||
|
|
||
|
SkinDataClass * skindata = (SkinDataClass *)mclist[i]->localData;
|
||
|
|
||
|
if (skindata==NULL) continue;
|
||
|
|
||
|
ObjectState os = nodes[i]->EvalWorldState(InterfacePtr->GetTime());
|
||
|
TriObject * tobj = Get_Tri_Object(InterfacePtr->GetTime(),os,valid,needsdel);
|
||
|
|
||
|
switch (SubObjSelLevel) {
|
||
|
|
||
|
case OBJECT_SEL_LEVEL:
|
||
|
assert(0);
|
||
|
return;
|
||
|
|
||
|
case VERTEX_SEL_LEVEL:
|
||
|
#if 0 // undo/redo
|
||
|
if (theHold.Holding()) {
|
||
|
theHold.Put(new VertexSelRestore(meshData,this));
|
||
|
}
|
||
|
#endif
|
||
|
for (int j=0; j<tobj->mesh.vertSel.GetSize(); j++) {
|
||
|
if (tobj->mesh.vertSel[j]) tobj->mesh.vertSel.Clear(j);
|
||
|
else tobj->mesh.vertSel.Set(j);
|
||
|
}
|
||
|
skindata->VertSel = tobj->mesh.vertSel;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (needsdel) {
|
||
|
tobj->DeleteThis();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Get rid of the temporary copies of the INodes.
|
||
|
*/
|
||
|
nodes.DisposeTemporary();
|
||
|
|
||
|
/*
|
||
|
** Tell our dependents that the selection set has changed
|
||
|
*/
|
||
|
NotifyDependents(FOREVER, PART_SELECT, REFMSG_CHANGE);
|
||
|
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::User_Picked_Bone(INode * node)
|
||
|
{
|
||
|
assert(InterfacePtr != NULL);
|
||
|
|
||
|
/*
|
||
|
** Get a pointer to the ModContext and SkinData for
|
||
|
** the mesh currently being messed with.
|
||
|
*/
|
||
|
ModContext * mc = NULL;
|
||
|
ModContextList mclist;
|
||
|
INodeTab nodelist;
|
||
|
|
||
|
InterfacePtr->GetModContexts(mclist,nodelist);
|
||
|
|
||
|
/*
|
||
|
** This seems wrong... But I always get only one ModContext and
|
||
|
** it is the one that I want so I'll just use it...
|
||
|
** I believe that OS Modifiers can get multiple ones but WS modifiers
|
||
|
** don't
|
||
|
*/
|
||
|
mc = mclist[0];
|
||
|
assert(mc != NULL);
|
||
|
SkinDataClass * skindata = (SkinDataClass *)(mc->localData);
|
||
|
|
||
|
/*
|
||
|
** Add this bone to the influences of all selected vertices
|
||
|
*/
|
||
|
int boneidx = WSMObjectRef->Find_Bone(node);
|
||
|
assert(boneidx != -1);
|
||
|
skindata->Add_Influence(boneidx);
|
||
|
|
||
|
/*
|
||
|
** Recreate all of the named selection sets!
|
||
|
*/
|
||
|
Create_Named_Selection_Sets();
|
||
|
|
||
|
/*
|
||
|
** Update dependents and redraw the views.
|
||
|
*/
|
||
|
NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
|
||
|
InterfacePtr->RedrawViews(InterfacePtr->GetTime());
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::User_Picked_Bones(INodeTab & nodetab)
|
||
|
{
|
||
|
/*
|
||
|
** One by one, add the selected bones to the influences of
|
||
|
** all selected vertices.
|
||
|
*/
|
||
|
for (int i=0; i<nodetab.Count() && i<2; i++) {
|
||
|
User_Picked_Bone(nodetab[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::ActivateSubSelSet(TSTR & setname)
|
||
|
{
|
||
|
ModContextList mclist;
|
||
|
INodeTab nodes;
|
||
|
|
||
|
if (InterfacePtr == NULL) return;
|
||
|
|
||
|
InterfacePtr->GetModContexts(mclist,nodes);
|
||
|
|
||
|
for (int i = 0; i < mclist.Count(); i++) {
|
||
|
|
||
|
SkinDataClass * skindata = (SkinDataClass *)mclist[i]->localData;
|
||
|
if (!skindata) continue;
|
||
|
|
||
|
int index = skindata->VertSelSets.Find_Set(setname);
|
||
|
if (index < 0) continue;
|
||
|
|
||
|
int needsdel;
|
||
|
Interval valid;
|
||
|
ObjectState os = nodes[i]->EvalWorldState(InterfacePtr->GetTime());
|
||
|
TriObject * tobj = Get_Tri_Object(InterfacePtr->GetTime(),os,valid,needsdel);
|
||
|
Mesh * mesh = &(tobj->mesh);
|
||
|
|
||
|
// TODO: undo redo
|
||
|
#if 0
|
||
|
if (theHold.Holding()) {
|
||
|
theHold.Put(new VertexSelRestore(meshData,this));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (skindata->VertSelSets[index].GetSize() != mesh->getNumVerts()) {
|
||
|
skindata->VertSelSets[index].SetSize(mesh->getNumVerts(),TRUE);
|
||
|
}
|
||
|
mesh->vertSel = skindata->VertSelSets[index];
|
||
|
skindata->VertSel = mesh->vertSel;
|
||
|
|
||
|
if (needsdel) {
|
||
|
tobj->DeleteThis();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
nodes.DisposeTemporary();
|
||
|
|
||
|
NotifyDependents(FOREVER, PART_SELECT, REFMSG_CHANGE);
|
||
|
InterfacePtr->RedrawViews(InterfacePtr->GetTime());
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::NewSetFromCurSel(TSTR &setname)
|
||
|
{
|
||
|
Install_Named_Selection_Sets();
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::RemoveSubSelSet(TSTR &setname)
|
||
|
{
|
||
|
Install_Named_Selection_Sets();
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::Create_Named_Selection_Sets(void)
|
||
|
{
|
||
|
/*
|
||
|
** This function creates a named selection set of vertices
|
||
|
** for each bone in the skeleton.
|
||
|
*/
|
||
|
if (InterfacePtr == NULL) return;
|
||
|
|
||
|
SkinWSMObjectClass * skinobj = WSMObjectRef;
|
||
|
if (skinobj == NULL) return;
|
||
|
|
||
|
ModContextList mclist;
|
||
|
INodeTab nodes;
|
||
|
InterfacePtr->GetModContexts(mclist,nodes);
|
||
|
SkinDataClass * skindata = (SkinDataClass *)mclist[0]->localData;
|
||
|
if (skindata == NULL) return;
|
||
|
|
||
|
/*
|
||
|
** Clear out the old selection sets
|
||
|
*/
|
||
|
skindata->VertSelSets.Reset();
|
||
|
|
||
|
/*
|
||
|
** Create and add a set for each bone
|
||
|
*/
|
||
|
for (int boneidx = 0; boneidx < skinobj->Num_Bones(); boneidx++) {
|
||
|
|
||
|
if (skinobj->Get_Bone(boneidx) != NULL) {
|
||
|
BitArray boneverts;
|
||
|
boneverts.SetSize(skindata->VertData.Count());
|
||
|
|
||
|
for (int vertidx = 0; vertidx < skindata->VertData.Count(); vertidx++) {
|
||
|
if (skindata->VertData[vertidx].BoneIdx[0] == boneidx) boneverts.Set(vertidx);
|
||
|
else boneverts.Clear(vertidx);
|
||
|
}
|
||
|
|
||
|
TSTR bonename = skinobj->Get_Bone(boneidx)->GetName();
|
||
|
|
||
|
skindata->VertSelSets.Append_Set(boneverts,bonename);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Install_Named_Selection_Sets();
|
||
|
|
||
|
nodes.DisposeTemporary();
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::Install_Named_Selection_Sets(void)
|
||
|
{
|
||
|
/*
|
||
|
** If we are in sub-object selection mode add the sets
|
||
|
** to the drop down box.
|
||
|
*/
|
||
|
if ((SubObjSelLevel == VERTEX_SEL_LEVEL) && (InterfacePtr != NULL)) {
|
||
|
|
||
|
ModContextList mclist;
|
||
|
INodeTab nodes;
|
||
|
InterfacePtr->GetModContexts(mclist,nodes);
|
||
|
SkinDataClass * skindata = (SkinDataClass *)mclist[0]->localData;
|
||
|
if (skindata == NULL) return;
|
||
|
|
||
|
InterfacePtr->ClearSubObjectNamedSelSets();
|
||
|
for (int i=0; i < skindata->VertSelSets.Count(); i++) {
|
||
|
InterfacePtr->AppendSubObjectNamedSelSet(*skindata->VertSelSets.Names[i]);
|
||
|
}
|
||
|
|
||
|
nodes.DisposeTemporary();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::Auto_Attach_Verts(BOOL all)
|
||
|
{
|
||
|
assert(InterfacePtr);
|
||
|
|
||
|
/*
|
||
|
** Get the skin data.
|
||
|
*/
|
||
|
ModContextList mclist;
|
||
|
INodeTab nodes;
|
||
|
InterfacePtr->GetModContexts(mclist,nodes);
|
||
|
SkinDataClass * skindata = (SkinDataClass *)mclist[0]->localData;
|
||
|
if (skindata == NULL) return;
|
||
|
|
||
|
/*
|
||
|
** get the skin WSM object.
|
||
|
*/
|
||
|
SkinWSMObjectClass * skinobj = WSMObjectRef;
|
||
|
if (skinobj == NULL) return;
|
||
|
|
||
|
/*
|
||
|
** Get a triobject representing the object state in the base pose.
|
||
|
*/
|
||
|
Interval valid;
|
||
|
BOOL needsdel;
|
||
|
|
||
|
TimeValue basetime = WSMObjectRef->Get_Base_Pose_Time();
|
||
|
ObjectState os = nodes[0]->EvalWorldState(basetime);
|
||
|
TriObject * triobj = Get_Tri_Object(basetime,os,valid,needsdel);
|
||
|
|
||
|
/*
|
||
|
** Attach each selected vertex (or all of them) to their closest bone.
|
||
|
*/
|
||
|
for (int vertidx = 0; vertidx < skindata->VertData.Count(); vertidx++){
|
||
|
if (skindata->VertSel[vertidx] || all) {
|
||
|
|
||
|
Point3 vert = triobj->GetPoint(vertidx);
|
||
|
if (os.GetTM()) vert = vert * (*os.GetTM());
|
||
|
int boneidx = skinobj->Find_Closest_Bone(vert);
|
||
|
skindata->VertData[vertidx].Set_Influence(boneidx);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Re-create the named selection sets
|
||
|
*/
|
||
|
Create_Named_Selection_Sets();
|
||
|
|
||
|
/*
|
||
|
** Update dependents and redraw the views.
|
||
|
*/
|
||
|
NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
|
||
|
InterfacePtr->RedrawViews(InterfacePtr->GetTime());
|
||
|
|
||
|
/*
|
||
|
** Cleanup...
|
||
|
*/
|
||
|
nodes.DisposeTemporary();
|
||
|
|
||
|
if (needsdel) {
|
||
|
triobj->DeleteThis();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::Unlink_Verts(void)
|
||
|
{
|
||
|
assert(InterfacePtr);
|
||
|
|
||
|
/*
|
||
|
** Get the skin data.
|
||
|
*/
|
||
|
ModContextList mclist;
|
||
|
INodeTab nodes;
|
||
|
InterfacePtr->GetModContexts(mclist,nodes);
|
||
|
SkinDataClass * skindata = (SkinDataClass *)mclist[0]->localData;
|
||
|
if (skindata == NULL) return;
|
||
|
|
||
|
/*
|
||
|
** Unlink each selected vertex (give them bone index -1)
|
||
|
*/
|
||
|
for (int vertidx = 0; vertidx < skindata->VertData.Count(); vertidx++){
|
||
|
if (skindata->VertSel[vertidx]) {
|
||
|
skindata->VertData[vertidx].Set_Influence(-1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Re-create the named selection sets
|
||
|
*/
|
||
|
Create_Named_Selection_Sets();
|
||
|
|
||
|
/*
|
||
|
** Update dependents and redraw the views.
|
||
|
*/
|
||
|
NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
|
||
|
InterfacePtr->RedrawViews(InterfacePtr->GetTime());
|
||
|
|
||
|
/*
|
||
|
** Cleanup...
|
||
|
*/
|
||
|
nodes.DisposeTemporary();
|
||
|
}
|
||
|
|
||
|
|
||
|
/****************************************************************************
|
||
|
**
|
||
|
** DIALOG BOX JUNK
|
||
|
**
|
||
|
****************************************************************************/
|
||
|
|
||
|
void SkinModifierClass::Install_Bone_Influence_Dialog(void)
|
||
|
{
|
||
|
if (BoneInfluenceHWND != NULL) return;
|
||
|
|
||
|
/*
|
||
|
** loading resource string for the name of the dialog
|
||
|
*/
|
||
|
static int loaded = 0;
|
||
|
static TCHAR string[MAX_STRING_LENGTH];
|
||
|
|
||
|
if (!loaded) {
|
||
|
LoadString(AppInstance,IDS_BONE_INFLUENCE_PARAMS,string,MAX_STRING_LENGTH);
|
||
|
loaded = 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
** Put up the UI that is used to assign vertices to bones
|
||
|
*/
|
||
|
BoneInfluenceHWND = InterfacePtr->AddRollupPage(
|
||
|
AppInstance,
|
||
|
MAKEINTRESOURCE(IDD_BONE_INFLUENCE_PARAMS),
|
||
|
_bone_influence_dialog_thunk,
|
||
|
string,
|
||
|
(LPARAM)this,
|
||
|
0);
|
||
|
}
|
||
|
|
||
|
void SkinModifierClass::Remove_Bone_Influence_Dialog(void)
|
||
|
{
|
||
|
/*
|
||
|
** If it is currently up, remove the bone influences dialog
|
||
|
*/
|
||
|
if (BoneInfluenceHWND != NULL) {
|
||
|
InterfacePtr->UnRegisterDlgWnd(BoneInfluenceHWND);
|
||
|
InterfacePtr->DeleteRollupPage(BoneInfluenceHWND);
|
||
|
BoneInfluenceHWND = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*********************************************************************************
|
||
|
*
|
||
|
* _sot_dialog_proc
|
||
|
*
|
||
|
*********************************************************************************/
|
||
|
static BOOL CALLBACK _sot_dialog_proc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
|
||
|
{
|
||
|
IObjParam *ip = (IObjParam*)GetWindowLong(hWnd,GWL_USERDATA);
|
||
|
|
||
|
switch (message) {
|
||
|
case WM_INITDIALOG:
|
||
|
SetWindowLong(hWnd,GWL_USERDATA,lParam);
|
||
|
break;
|
||
|
|
||
|
case WM_LBUTTONDOWN:
|
||
|
case WM_LBUTTONUP:
|
||
|
case WM_MOUSEMOVE:
|
||
|
if (ip) ip->RollupMouseMessage(hWnd,message,wParam,lParam);
|
||
|
return FALSE;
|
||
|
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*********************************************************************************
|
||
|
*
|
||
|
* _skeleton_dialog_proc
|
||
|
*
|
||
|
*********************************************************************************/
|
||
|
static BOOL CALLBACK _skeleton_dialog_thunk(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
|
||
|
{
|
||
|
SkinWSMObjectClass * skinobj = (SkinWSMObjectClass *)GetWindowLong(hWnd,GWL_USERDATA);
|
||
|
if (!skinobj && message != WM_INITDIALOG) return FALSE;
|
||
|
|
||
|
if (message == WM_INITDIALOG) {
|
||
|
skinobj = (SkinWSMObjectClass *)lParam;
|
||
|
SetWindowLong(hWnd,GWL_USERDATA,(LONG)skinobj);
|
||
|
}
|
||
|
|
||
|
return skinobj->Skeleton_Dialog_Proc(hWnd,message,wParam,lParam);
|
||
|
}
|
||
|
|
||
|
BOOL SkinWSMObjectClass::Skeleton_Dialog_Proc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
|
||
|
{
|
||
|
switch (message) {
|
||
|
|
||
|
case WM_INITDIALOG:
|
||
|
|
||
|
BoneListHWND = GetDlgItem(hWnd,IDC_BONE_LIST);
|
||
|
|
||
|
/*
|
||
|
** Intitialize the add bone and remove bone check buttons
|
||
|
*/
|
||
|
AddBonesButton = GetICustButton(GetDlgItem(hWnd, IDC_ADD_BONES_BUTTON));
|
||
|
RemoveBonesButton = GetICustButton(GetDlgItem(hWnd, IDC_REMOVE_BONES_BUTTON));
|
||
|
|
||
|
AddBonesButton->SetType(CBT_CHECK);
|
||
|
AddBonesButton->SetHighlightColor(GREEN_WASH);
|
||
|
AddBonesButton->SetTooltip(TRUE, _T("Add bones by name"));
|
||
|
|
||
|
RemoveBonesButton->SetType(CBT_CHECK);
|
||
|
RemoveBonesButton->SetHighlightColor(GREEN_WASH);
|
||
|
RemoveBonesButton->SetTooltip(TRUE, _T("Remove bones by name"));
|
||
|
|
||
|
/*
|
||
|
** Initialize the "Base Pose Frame" spinner
|
||
|
*/
|
||
|
BasePoseSpin = GetISpinner(GetDlgItem(hWnd, IDC_BASE_POSE_SPIN));
|
||
|
BasePoseSpin->SetLimits(0,9999, FALSE);
|
||
|
BasePoseSpin->SetValue(0,FALSE);
|
||
|
BasePoseSpin->SetResetValue(0);
|
||
|
BasePoseSpin->LinkToEdit(GetDlgItem(hWnd,IDC_BASE_POSE_EDIT),EDITTYPE_INT);
|
||
|
return TRUE;
|
||
|
|
||
|
case WM_DESTROY:
|
||
|
ReleaseICustButton(AddBonesButton);
|
||
|
ReleaseICustButton(RemoveBonesButton);
|
||
|
ReleaseISpinner(BasePoseSpin);
|
||
|
|
||
|
AddBonesButton = NULL;
|
||
|
RemoveBonesButton = NULL;
|
||
|
BasePoseSpin = NULL;
|
||
|
BoneListHWND = NULL;
|
||
|
|
||
|
return FALSE;
|
||
|
|
||
|
case CC_SPINNER_CHANGE:
|
||
|
switch (LOWORD(wParam))
|
||
|
{
|
||
|
case IDC_BASE_POSE_SPIN:
|
||
|
BasePoseFrame = BasePoseSpin->GetIVal();
|
||
|
break;
|
||
|
}
|
||
|
NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
|
||
|
InterfacePtr->RedrawViews(InterfacePtr->GetTime(),REDRAW_INTERACTIVE);
|
||
|
return TRUE;
|
||
|
|
||
|
case CC_SPINNER_BUTTONUP:
|
||
|
NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
|
||
|
InterfacePtr->RedrawViews(InterfacePtr->GetTime(),REDRAW_END);
|
||
|
return TRUE;
|
||
|
|
||
|
|
||
|
case WM_LBUTTONDOWN:
|
||
|
case WM_LBUTTONUP:
|
||
|
case WM_MOUSEMOVE:
|
||
|
InterfacePtr->RollupMouseMessage(hWnd,message,wParam,lParam);
|
||
|
return FALSE;
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
switch (LOWORD(wParam))
|
||
|
{
|
||
|
|
||
|
case IDC_ADD_BONES_BUTTON:
|
||
|
TheBonePicker.Set_User(this);
|
||
|
Set_Bone_Selection_Mode(BONE_SEL_MODE_ADD_MANY);
|
||
|
InterfacePtr->DoHitByNameDialog(&TheBonePicker);
|
||
|
Set_Bone_Selection_Mode(BONE_SEL_MODE_NONE);
|
||
|
break;
|
||
|
|
||
|
case IDC_REMOVE_BONES_BUTTON:
|
||
|
TheBonePicker.Set_User(this,FALSE,&(BoneTab));
|
||
|
Set_Bone_Selection_Mode(BONE_SEL_MODE_REMOVE_MANY);
|
||
|
InterfacePtr->DoHitByNameDialog(&TheBonePicker);
|
||
|
Set_Bone_Selection_Mode(BONE_SEL_MODE_NONE);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*********************************************************************************
|
||
|
*
|
||
|
* Bone_Influence_Dialog_Proc
|
||
|
*
|
||
|
*********************************************************************************/
|
||
|
static BOOL CALLBACK _bone_influence_dialog_thunk(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
|
||
|
{
|
||
|
SkinModifierClass * skinmod = (SkinModifierClass *)GetWindowLong(hWnd,GWL_USERDATA);
|
||
|
if (!skinmod && message != WM_INITDIALOG) return FALSE;
|
||
|
|
||
|
if (message == WM_INITDIALOG) {
|
||
|
skinmod = (SkinModifierClass *)lParam;
|
||
|
SetWindowLong(hWnd,GWL_USERDATA,(LONG)skinmod);
|
||
|
}
|
||
|
|
||
|
return skinmod->Bone_Influence_Dialog_Proc(hWnd,message,wParam,lParam);
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL SkinModifierClass::Bone_Influence_Dialog_Proc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)
|
||
|
{
|
||
|
switch (message) {
|
||
|
|
||
|
case WM_INITDIALOG:
|
||
|
/*
|
||
|
** Intitialize the bone influence buttons
|
||
|
*/
|
||
|
LinkButton = GetICustButton(GetDlgItem(hWnd, IDC_LINK_BUTTON));
|
||
|
LinkByNameButton = GetICustButton(GetDlgItem(hWnd, IDC_LINK_BY_NAME_BUTTON));
|
||
|
AutoLinkButton = GetICustButton(GetDlgItem(hWnd, IDC_AUTO_LINK_BUTTON));
|
||
|
UnLinkButton = GetICustButton(GetDlgItem(hWnd, IDC_UNLINK_BUTTON));
|
||
|
|
||
|
LinkButton->SetType(CBT_PUSH);
|
||
|
LinkButton->SetTooltip(TRUE, _T("Link Vertices to a bone by selecting the bone"));
|
||
|
|
||
|
LinkByNameButton->SetType(CBT_PUSH);
|
||
|
LinkByNameButton->SetTooltip(TRUE, _T("Link Vertices to a bone by name"));
|
||
|
|
||
|
AutoLinkButton->SetType(CBT_PUSH);
|
||
|
AutoLinkButton->SetTooltip(TRUE, _T("Link Vertices to nearest bone"));
|
||
|
|
||
|
UnLinkButton->SetType(CBT_PUSH);
|
||
|
UnLinkButton->SetTooltip(TRUE, _T("Unlink selected vertices"));
|
||
|
|
||
|
return TRUE;
|
||
|
|
||
|
case WM_DESTROY:
|
||
|
ReleaseICustButton(LinkButton);
|
||
|
ReleaseICustButton(LinkByNameButton);
|
||
|
ReleaseICustButton(AutoLinkButton);
|
||
|
ReleaseICustButton(UnLinkButton);
|
||
|
|
||
|
LinkButton = NULL;
|
||
|
LinkByNameButton = NULL;
|
||
|
AutoLinkButton = NULL;
|
||
|
UnLinkButton = NULL;
|
||
|
return FALSE;
|
||
|
|
||
|
case WM_LBUTTONDOWN:
|
||
|
case WM_LBUTTONUP:
|
||
|
case WM_MOUSEMOVE:
|
||
|
InterfacePtr->RollupMouseMessage(hWnd,message,wParam,lParam);
|
||
|
return FALSE;
|
||
|
|
||
|
case WM_COMMAND:
|
||
|
switch (LOWORD(wParam))
|
||
|
{
|
||
|
case IDC_LINK_BUTTON:
|
||
|
{
|
||
|
/*
|
||
|
** user picks a bone out of the scene to link to.
|
||
|
*/
|
||
|
assert(WSMObjectRef != NULL);
|
||
|
INodeTab * bonetab = &(WSMObjectRef->Get_Bone_List());
|
||
|
TheBonePicker.Set_User(this,TRUE,bonetab);
|
||
|
InterfacePtr->SetPickMode(&TheBonePicker);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IDC_LINK_BY_NAME_BUTTON:
|
||
|
{
|
||
|
/*
|
||
|
** pop up a bone selection dialog
|
||
|
*/
|
||
|
assert(WSMObjectRef != NULL);
|
||
|
INodeTab * bonetab = &(WSMObjectRef->Get_Bone_List());
|
||
|
TheBonePicker.Set_User(this,TRUE,bonetab);
|
||
|
InterfacePtr->DoHitByNameDialog(&TheBonePicker);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IDC_AUTO_LINK_BUTTON:
|
||
|
{
|
||
|
Auto_Attach_Verts();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case IDC_UNLINK_BUTTON:
|
||
|
{
|
||
|
Unlink_Verts();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static TriObject * Get_Tri_Object(TimeValue t,ObjectState & os,Interval & valid,BOOL & needsdel)
|
||
|
{
|
||
|
needsdel = FALSE;
|
||
|
valid &= os.Validity(t);
|
||
|
|
||
|
if (os.obj->IsSubClassOf(triObjectClassID)) {
|
||
|
return (TriObject *)os.obj;
|
||
|
} else {
|
||
|
if (os.obj->CanConvertToType(triObjectClassID)) {
|
||
|
Object * oldObj = os.obj;
|
||
|
TriObject * tobj = (TriObject *)os.obj->ConvertToType(t,triObjectClassID);
|
||
|
needsdel = (tobj != oldObj);
|
||
|
return tobj;
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
float Bone_Distance(INode * bone,TimeValue time,const Point3 & vertex)
|
||
|
{
|
||
|
/*
|
||
|
** Average the pivot point of this bone with the pivot points of
|
||
|
** all of its children.
|
||
|
*/
|
||
|
Point3 icenter = bone->GetObjectTM(time).GetTrans();
|
||
|
|
||
|
for (int ci=0; ci<bone->NumberOfChildren(); ci++) {
|
||
|
icenter += bone->GetChildNode(ci)->GetObjectTM(time).GetTrans();
|
||
|
}
|
||
|
|
||
|
icenter = icenter / (float)(bone->NumberOfChildren() + 1);
|
||
|
|
||
|
return Length(icenter - vertex);
|
||
|
}
|
||
|
|
||
|
#if defined W3D_MAX4 //defined as in the project (.dsp)
|
||
|
|
||
|
int SkinModifierClass::NumSubObjTypes()
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
////////////////////////////////////////////////////////////////////////////////////////
|
||
|
ISubObjType *SkinModifierClass::GetSubObjType(int i)
|
||
|
{
|
||
|
|
||
|
static bool _initialized = false;
|
||
|
if(!_initialized){
|
||
|
_initialized = true;
|
||
|
_SubObjectTypeVertex.SetName("Vertices");
|
||
|
}
|
||
|
if(i == -1){
|
||
|
if(GetSubObjectLevel() > 0){
|
||
|
return GetSubObjType(GetSubObjectLevel()-1);
|
||
|
}
|
||
|
}
|
||
|
return &_SubObjectTypeVertex;
|
||
|
}
|
||
|
#endif
|