/*
** 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 .
*/
/* $Header: /Commando/Code/Tools/max2w3d/MeshDeform.cpp 7 5/01/01 8:56p Greg_h $ */
/***********************************************************************************************
*** Confidential - Westwood Studios ***
***********************************************************************************************
* *
* Project Name : Commando / G 3D engine *
* *
* File Name : MeshDeform.cpp *
* *
* Programmer : Patrick Smith *
* *
* Start Date : 04/19/99 *
* *
* Last Update :
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "MeshDeform.H"
#include "MeshDeformData.H"
#include "MeshDeformPanel.H"
#include "MeshDeformUndo.H"
#include "DLLMain.H"
#include "Resource.H"
#include "Util.H"
#if defined W3D_MAX4 //defined as in the project (.dsp)
static GenSubObjType _SubObjectTypeVertex(1);
#endif
///////////////////////////////////////////////////////////////////////////
//
// MeshDeformClass Class ID
//
///////////////////////////////////////////////////////////////////////////
Class_ID _MeshDeformClassID(0x51981f5b, 0x1db2bf3);
///////////////////////////////////////////////////////////////////////////
//
// MeshDeformClassDesc
//
///////////////////////////////////////////////////////////////////////////
class MeshDeformClassDesc : public ClassDesc
{
public:
int IsPublic (void) { return 1; }
void * Create (BOOL loading) { return new MeshDeformClass (); }
const TCHAR * ClassName () { return _T("WWDeform"); }
SClass_ID SuperClassID () { return OSM_CLASS_ID; }
Class_ID ClassID () { return _MeshDeformClassID; }
const TCHAR* Category () { return _T("Westwood Modifiers"); }
};
///////////////////////////////////////////////////////////////////////////
//
// Static class desc instance
//
///////////////////////////////////////////////////////////////////////////
#if 0 // (gth) MeshDeform is obsolete! making sure nobody uses it...
static MeshDeformClassDesc _MeshDeformCD;
ClassDesc * Get_Mesh_Deform_Desc (void) { return &_MeshDeformCD; }
#else
ClassDesc * Get_Mesh_Deform_Desc (void) { return NULL; }
#endif
///////////////////////////////////////////////////////////////////////////
//
// ChannelsUsed
//
///////////////////////////////////////////////////////////////////////////
ChannelMask
MeshDeformClass::ChannelsUsed (void)
{
return GEOM_CHANNEL | SELECT_CHANNEL | SUBSEL_TYPE_CHANNEL | VERTCOLOR_CHANNEL;
}
///////////////////////////////////////////////////////////////////////////
//
// ChannelsChanged
//
///////////////////////////////////////////////////////////////////////////
ChannelMask
MeshDeformClass::ChannelsChanged (void)
{
return GEOM_CHANNEL | SELECT_CHANNEL | SUBSEL_TYPE_CHANNEL | VERTCOLOR_CHANNEL;
}
///////////////////////////////////////////////////////////////////////////
//
// ModifyObject
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::ModifyObject
(
TimeValue time,
ModContext & mod_context,
ObjectState * object_state,
INode * /*node*/
)
{
assert(object_state->obj->IsSubClassOf(triObjectClassID));
MeshDeformModData *mod_data = NULL;
if (mod_context.localData == NULL) {
mod_data = new MeshDeformModData;
mod_context.localData = mod_data;
} else {
mod_data = static_cast (mod_context.localData);
}
// Display the verts
TriObject *tri_obj = (TriObject *)object_state->obj;
tri_obj->mesh.SetDispFlag (DISP_SELVERTS | DISP_VERTTICKS);
// Record the initial state of the mesh
bool lock_sets = false;
if (m_pPanel != NULL) {
lock_sets = (m_pPanel->Are_Sets_Tied () == TRUE);
}
mod_data->Record_Mesh_State (*tri_obj, m_DeformState, lock_sets);
tri_obj->PointsWereChanged();
// Kind of a waste when there's no animation...
tri_obj->UpdateValidity (GEOM_CHAN_NUM, Interval (time, time + 1));
tri_obj->UpdateValidity (SELECT_CHAN_NUM, Interval (time, time + 1));
tri_obj->UpdateValidity (SUBSEL_TYPE_CHAN_NUM, Interval (time, time + 1));
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// InputType
//
///////////////////////////////////////////////////////////////////////////
Class_ID
MeshDeformClass::InputType (void)
{
return triObjectClassID;
}
///////////////////////////////////////////////////////////////////////////
//
// NotifyRefChanged
//
///////////////////////////////////////////////////////////////////////////
RefResult
MeshDeformClass::NotifyRefChanged
(
Interval time,
RefTargetHandle htarget,
PartID &part_id,
RefMessage mesage
)
{
return REF_SUCCEED;
}
///////////////////////////////////////////////////////////////////////////
//
// GetCreateMouseCallBack
//
///////////////////////////////////////////////////////////////////////////
CreateMouseCallBack *
MeshDeformClass::GetCreateMouseCallBack (void)
{
return NULL;
}
///////////////////////////////////////////////////////////////////////////
//
// BeginEditParams
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::BeginEditParams
(
IObjParam *max_interface,
ULONG flags,
Animatable *prev
)
{
m_MaxInterface = max_interface;
Update_Set_Count ();
Set_Max_Deform_Sets (m_MaxSets);
// Add our rollup to the command panel
m_hRollupWnd = m_MaxInterface->AddRollupPage (AppInstance,
MAKEINTRESOURCE (IDD_MESH_DEFORM_PANEL),
MeshDeformPanelClass::Message_Proc,
"Westwood Mesh Deform",
0,
0);
//
// Update the UI
//
m_pPanel = MeshDeformPanelClass::Get_Object (m_hRollupWnd);
m_pPanel->Set_Deformer (this);
m_pPanel->Set_Max_Sets (m_MaxSets);
m_pPanel->Set_Current_Set (m_CurrentSet);
Set_Current_Set (m_CurrentSet, false);
//
// Register the desired sub-object selection types.
//
const TCHAR * ptype[] = { "Vertices" };
#if defined W3D_MAX4 //defined as in the project (.dsp)
max_interface->SetSubObjectLevel(1);
#else
//---This call is obsolete from max4.
max_interface->RegisterSubObjectTypes( ptype, 1);
#endif
//
// Create the mode handlers
//
m_ModeSelect = new SelectModBoxCMode (this, max_interface);
m_ModeMove = new MoveModBoxCMode (this, max_interface);
m_ModeRotate = new RotateModBoxCMode (this, max_interface);
m_ModeUScale = new UScaleModBoxCMode (this, max_interface);
m_ModeNUScale = new NUScaleModBoxCMode (this, max_interface);
m_ModeSquash = new SquashModBoxCMode (this, max_interface);
//
// Restore the selection level.
///
max_interface->SetSubObjectLevel (1);
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// EndEditParams
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::EndEditParams
(
IObjParam *max_interface,
ULONG flags,
Animatable *next
)
{
// Remove our deform rollup
if (m_hRollupWnd != NULL) {
max_interface->DeleteRollupPage (m_hRollupWnd);
m_hRollupWnd = NULL;
}
//
// Free the mode handlers
//
max_interface->DeleteMode (m_ModeMove);
max_interface->DeleteMode (m_ModeSelect);
max_interface->DeleteMode (m_ModeRotate);
max_interface->DeleteMode (m_ModeNUScale);
max_interface->DeleteMode (m_ModeUScale);
max_interface->DeleteMode (m_ModeSquash);
SAFE_DELETE (m_ModeMove);
SAFE_DELETE (m_ModeSelect);
SAFE_DELETE (m_ModeRotate);
SAFE_DELETE (m_ModeNUScale);
SAFE_DELETE (m_ModeUScale);
SAFE_DELETE (m_ModeSquash);
// Release our hold on the max interface pointer
m_MaxInterface = NULL;
m_pPanel = NULL;
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// ActivateSubobjSel
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::ActivateSubobjSel
(
int level,
XFormModes &modes
)
{
switch (level)
{
// Vertex manipulation
case 1:
modes = XFormModes (m_ModeMove, m_ModeRotate, m_ModeNUScale, m_ModeUScale, m_ModeSquash, m_ModeSelect);
break;
}
/*
** 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.
*/
m_MaxInterface->PipeSelLevelChanged();
/*
** Notify our dependents that the selection channel,
** display attributes, and subselection type channels have changed
*/
NotifyDependents(FOREVER, VERTCOLOR_CHANNEL|SELECT_CHANNEL|DISP_ATTRIB_CHANNEL|SUBSEL_TYPE_CHANNEL, REFMSG_CHANGE);
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// HitTest
//
///////////////////////////////////////////////////////////////////////////
int
MeshDeformClass::HitTest
(
TimeValue time_value,
INode * node,
int type,
int crossing,
int flags,
IPoint2 * point,
ViewExp * viewport,
ModContext * mod_context
)
{
// Initialize the HitRegion
HitRegion hit_rgn;
MakeHitRegion (hit_rgn, type, crossing, 4, point);
Matrix3 transform = node->GetObjectTM (time_value);
Object * obj = node->EvalWorldState(time_value).obj;
TriObject * tri = (TriObject *)obj->ConvertToType(time_value, triObjectClassID);
//
// Set up the graphics window to do the hit-test
//
GraphicsWindow *graphics_wnd = viewport->getGW ();
graphics_wnd->setHitRegion (&hit_rgn);
graphics_wnd->setTransform (transform);
int saved_limits = graphics_wnd->getRndLimits ();
graphics_wnd->setRndLimits ((saved_limits | GW_PICK) & ~(GW_ILLUM | GW_BACKCULL));
graphics_wnd->clearHitCode ();
//
// Perform the hit test
//
SubObjHitList hitlist;
MeshDeformModData *mod_data = static_cast (mod_context->localData);
Mesh &mesh = tri->mesh;//mod_data->Peek_Mesh ();
int result = mesh.SubObjectHitTest (graphics_wnd,
graphics_wnd->getMaterial (),
&hit_rgn,
flags | SUBHIT_VERTS,
hitlist);
//
// Record all of the hits
//
for (MeshSubHitRec *hit_record = hitlist.First ();
hit_record != NULL;
hit_record = hit_record->Next ()) {
// rec->index is the index of vertex which was hit!
viewport->LogHit (node, mod_context, hit_record->dist, hit_record->index, NULL);
}
// Cleanup
graphics_wnd->setRndLimits (saved_limits);
// Return the integer result code
return result;
}
///////////////////////////////////////////////////////////////////////////
//
// HitTest
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::SelectSubComponent
(
HitRecord * hit_record,
BOOL selected,
BOOL all,
BOOL invert
)
{
// Loop through all the hit records
for (; hit_record != NULL; hit_record = hit_record->Next ()) {
// Peek at the vertex selection array for this hit record
MeshDeformModData *mod_data = static_cast (hit_record->modContext->localData);
Mesh *mesh = mod_data->Peek_Mesh ();
BitArray &array = (mesh->vertSel);
if (all & invert) {
/*
** hitRec->hitInfo is the MeshSubHitRec::index that was stored in the
** HitTest method through LogHit
*/
if (array[hit_record->hitInfo]) {
array.Clear (hit_record->hitInfo);
} else {
array.Set (hit_record->hitInfo, selected);
}
} else {
array.Set (hit_record->hitInfo, selected);
}
if (!all) break;
}
m_pPanel->Update_Vertex_Color ();
NotifyDependents (FOREVER, PART_SELECT, REFMSG_CHANGE);
m_bSetDirty = true;
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// GetSubObjectTMs
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::GetSubObjectTMs
(
SubObjAxisCallback *cb,
TimeValue t,
INode *node,
ModContext *mc
)
{
int test = 0;
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// HitTest
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::GetSubObjectCenters
(
SubObjAxisCallback * callback,
TimeValue time_val,
INode * node,
ModContext * mod_context
)
{
// Peek at the vertex selection array for this hit record
MeshDeformModData *mod_data = static_cast (mod_context->localData);
const Point3 *vertex_array = mod_data->Peek_Orig_Vertex_Array ();
Mesh *mesh = mod_data->Peek_Mesh ();
BitArray sel_array = mesh->vertSel;
Matrix3 transform = node->GetObjectTM (time_val);
Box3 box;
// Loop through all the selected verticies and create a bounding
// box which we can use to determine the selection center.
for (int index = 0; index < mesh->getNumVerts (); index++ ) {
if (sel_array[index]) {
box += mesh->getVert (index) * transform;
}
}
// Pass the 'selection' center onto MAX
callback->Center (box.Center (), 0);
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// ClearSelection
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::ClearSelection (int selLevel)
{
ModContextList mod_context_list;
INodeTab nodes;
m_MaxInterface->GetModContexts (mod_context_list, nodes);
for (int i = 0; i < mod_context_list.Count (); i++) {
MeshDeformModData *mod_data = static_cast (mod_context_list[i]->localData);
if (mod_data != NULL) {
mod_data->Peek_Mesh ()->vertSel.ClearAll ();
}
}
/*
** 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);
m_bSetDirty = true;
return ;
}
static Point3 last_delta;
static Point3 last_scale;
static Matrix3 last_rot;
///////////////////////////////////////////////////////////////////////////
//
// Move
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::Move
(
TimeValue time_val,
Matrix3 & parent_tm,
Matrix3 & tm_axis,
Point3 & displacement,
BOOL local_origin
)
{
if (m_pPanel->Is_Edit_Mode ()) {
INodeTab nodes;
ModContextList mod_context_list;
m_MaxInterface->GetModContexts (mod_context_list, nodes);
// Loop through all the modifier contexts
for (int index = 0; index < mod_context_list.Count (); index ++) {
// Get the data we've cached for this modifier context
MeshDeformModData *mod_data = static_cast (mod_context_list[index]->localData);
if (mod_data != NULL) {
Mesh *mesh = mod_data->Peek_Mesh ();
const Point3 *vertex_array = mod_data->Peek_Orig_Vertex_Array ();
Point3 *opstart_array = mod_data->Peek_Vertex_OPStart_Array ();
// Loop through all the selected verts
for (int vert = 0; vert < mesh->numVerts; vert ++) {
if (mesh->vertSel[vert]) {
// Do the 'displacment' in axis-space
Point3 vert_ws = parent_tm * mesh->verts[vert];
Point3 vert_as = Inverse (tm_axis) * vert_ws;
vert_as += displacement - last_delta;
// Convert back to obj-space
vert_ws = tm_axis * vert_as;
mesh->verts[vert] = Inverse (parent_tm) * vert_ws;
// Record the delta
//delta_array[vert] = mesh->verts[vert] - vertex_array[vert];
}
}
//
// Record these changes in the current set
//
mod_data->Update_Set (m_CurrentSet, VERT_POSITION);
}
}
// Remember what our last displacement was because we
// want to perform our calculations relative to the current
// position.
last_delta = displacement;
// Repaint the view
nodes.DisposeTemporary ();
NotifyDependents (FOREVER, PART_GEOM, REFMSG_CHANGE);
}
m_OperationName = "Move";
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Move
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::Scale
(
TimeValue time_val,
Matrix3 & parent_tm,
Matrix3 & tm_axis,
Point3 & value,
BOOL local_origin
)
{
Point3 test = value - last_scale;
bool bok = (test.x != 0) && (test.y != 0) && (test.z != 0);
if (m_pPanel->Is_Edit_Mode () && bok) {
INodeTab nodes;
ModContextList mod_context_list;
m_MaxInterface->GetModContexts (mod_context_list, nodes);
// Loop through all the modifier contexts
for (int index = 0; index < mod_context_list.Count (); index ++) {
// Get the data we've cached for this modifier context
MeshDeformModData *mod_data = static_cast (mod_context_list[index]->localData);
if (mod_data != NULL) {
Mesh *mesh = mod_data->Peek_Mesh ();
const Point3 *vertex_array = mod_data->Peek_Orig_Vertex_Array ();
Point3 *opstart_array = mod_data->Peek_Vertex_OPStart_Array ();
// Loop through all the selected verts
for (int vert = 0; vert < mesh->numVerts; vert ++) {
if (mesh->vertSel[vert]) {
// Do the 'scale' in axis-space
Point3 vert_ws = parent_tm * opstart_array[vert];//mesh->verts[vert];
Point3 vert_as = Inverse (tm_axis) * vert_ws;
vert_as = ScaleMatrix (value) * vert_as;
// Convert back to obj-space
vert_ws = tm_axis * vert_as;
mesh->verts[vert] = Inverse (parent_tm) * vert_ws;
// Record the delta
//delta_array[vert] = mesh->verts[vert] - vertex_array[vert];
}
}
//
// Record these changes in the current set
//
mod_data->Update_Set (m_CurrentSet, VERT_POSITION);
}
}
// Remember what our last displacement was because we
// want to perform our calculations relative to the current
// position.
//last_scale = value - Point3 (1,1,1);
//last_delta = displacement;
// Repaint the view
nodes.DisposeTemporary ();
NotifyDependents (FOREVER, PART_GEOM, REFMSG_CHANGE);
}
m_OperationName = "Scale";
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Rotate
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::Rotate
(
TimeValue time_val,
Matrix3 & parent_tm,
Matrix3 & tm_axis,
Quat & rotation,
BOOL local_origin
)
{
if (m_pPanel->Is_Edit_Mode ()) {
INodeTab nodes;
ModContextList mod_context_list;
m_MaxInterface->GetModContexts (mod_context_list, nodes);
Matrix3 matrix_rot;
rotation.MakeMatrix (matrix_rot);
Matrix3 rel_rot;
rel_rot = Inverse (last_rot) * matrix_rot;
// Loop through all the modifier contexts
for (int index = 0; index < mod_context_list.Count (); index ++) {
// Get the data we've cached for this modifier context
MeshDeformModData *mod_data = static_cast (mod_context_list[index]->localData);
if (mod_data != NULL) {
Mesh *mesh = mod_data->Peek_Mesh ();
const Point3 *vertex_array = mod_data->Peek_Orig_Vertex_Array ();
Point3 *opstart_array = mod_data->Peek_Vertex_OPStart_Array ();
// Loop through all the selected verts
for (int vert = 0; vert < mesh->numVerts; vert ++) {
if (mesh->vertSel[vert]) {
// Do the 'displacment' in axis-space
Point3 vert_ws = parent_tm * mesh->verts[vert];
Point3 vert_as = Inverse (tm_axis) * vert_ws;
vert_as = (rel_rot * vert_as);
// Convert back to obj-space
vert_ws = tm_axis * vert_as;
mesh->verts[vert] = Inverse (parent_tm) * vert_ws;
// Record the delta
//delta_array[vert] = mesh->verts[vert] - vertex_array[vert];
}
}
//
// Record these changes in the current set
//
mod_data->Update_Set (m_CurrentSet, VERT_POSITION);
}
}
// Remember what our last displacement was because we
// want to perform our calculations relative to the current
// position.
//last_delta = displacement;
last_rot = matrix_rot;
// Repaint the view
nodes.DisposeTemporary ();
NotifyDependents (FOREVER, PART_GEOM, REFMSG_CHANGE);
// Make sure our current 'set' knows what verts have changed
//Update_Current_Set ();
}
m_OperationName = "Rotate";
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// TransformStart
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::TransformStart (TimeValue time_val)
{
if (m_MaxInterface != NULL) {
m_MaxInterface->LockAxisTripods (TRUE);
}
// Reset our last-delta value
last_delta.x = 0;
last_delta.y = 0;
last_delta.z = 0;
last_rot.IdentityMatrix ();
last_scale.x = 0;
last_scale.y = 0;
last_scale.z = 0;
INodeTab nodes;
ModContextList mod_context_list;
m_MaxInterface->GetModContexts (mod_context_list, nodes);
// Begin the undo operation
theHold.Begin ();
// Loop through all the modifier contexts
for (int index = 0; index < mod_context_list.Count (); index ++) {
// Get the data we've cached for this modifier context
MeshDeformModData *mod_data = static_cast (mod_context_list[index]->localData);
if (mod_data != NULL) {
Mesh *mesh = mod_data->Peek_Mesh ();
Point3 *opstart_array = mod_data->Peek_Vertex_OPStart_Array ();
// Copy the current state of the mesh
for (int vert = 0; vert < mesh->numVerts; vert ++) {
opstart_array[vert] = mesh->verts[vert];
}
// Add the 'position restore' object to the undo stack
theHold.Put (new VertexPositionRestoreClass (mesh, this, mod_data));
}
}
// Repaint the view
nodes.DisposeTemporary ();
NotifyDependents (FOREVER, PART_GEOM, REFMSG_CHANGE);
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// TransformFinish
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::TransformFinish (TimeValue time_val)
{
if (m_MaxInterface != NULL) {
m_MaxInterface->LockAxisTripods (FALSE);
}
// Accept the undo operation
theHold.Accept (m_OperationName);
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// TransformCancel
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::TransformCancel (TimeValue time_val)
{
if (m_MaxInterface != NULL) {
m_MaxInterface->LockAxisTripods (FALSE);
}
// Cancel the undo operation
theHold.Cancel ();
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Set_Deform_State
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::Set_Deform_State (float state)
{
if ((m_MaxInterface != NULL) && (state != m_DeformState)) {
m_DeformState = state;
NotifyDependents (FOREVER, PART_GEOM | PART_VERTCOLOR, REFMSG_CHANGE);
m_MaxInterface->RedrawViews (m_MaxInterface->GetTime ());
if (m_pPanel != NULL) {
m_pPanel->Update_Vertex_Color ();
}
}
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Set_Vertex_Color
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::Set_Vertex_Color (const Point3 &color, bool button_up)
{
if (m_MaxInterface != NULL) {
INodeTab nodes;
ModContextList mod_context_list;
m_MaxInterface->GetModContexts (mod_context_list, nodes);
bool save_undo = false;
if ((button_up == false) && (m_VertColorChanging == false)) {
theHold.Begin ();
m_VertColorChanging = true;
save_undo = true;
}
// Loop through all the modifier contexts
for (int index = 0; index < mod_context_list.Count (); index ++) {
// Get the data we've cached for this modifier context
MeshDeformModData *mod_data = static_cast (mod_context_list[index]->localData);
if (mod_data != NULL) {
Mesh *mesh = mod_data->Peek_Mesh ();
//
// Record the original color in the undo stack
//
if (save_undo) {
theHold.Put (new VertexColorRestoreClass (mesh, this, mod_data));
}
// Only do this if the mesh is using vertex coloring
if (mesh->numCVerts >= mesh->numVerts) {
//
// Loop through all the per-face verts
//
for (int face = 0; face < mesh->numFaces; face ++) {
for (int vert = 0; vert < 3; vert ++) {
//
// If the vertex is selected, then change its color
//
if (mesh->vertSel[mesh->faces[face].v[vert]]) {
int color_index = mesh->vcFace[face].t[vert];
mesh->vertCol[color_index] = color;
}
}
}
}
//
// Record these changes in the current set
//
mod_data->Update_Set (m_CurrentSet, VERT_COLORS);
}
}
if (button_up && m_VertColorChanging) {
theHold.Accept ("Vertex Color");
}
// Repaint the model
nodes.DisposeTemporary ();
NotifyDependents (FOREVER, PART_GEOM | PART_VERTCOLOR, REFMSG_CHANGE);
m_MaxInterface->RedrawViews (m_MaxInterface->GetTime ());
}
m_VertColorChanging = !button_up;
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Get_Vertex_Color
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::Get_Vertex_Color (Point3 &color)
{
// Assume black
color.x = 0;
color.y = 0;
color.z = 0;
if (m_MaxInterface != NULL) {
INodeTab nodes;
ModContextList mod_context_list;
m_MaxInterface->GetModContexts (mod_context_list, nodes);
//
// Loop through all the modifier contexts
//
int sel_count = 0;
for (int index = 0; index < mod_context_list.Count (); index ++) {
//
// Get the data we've cached for this modifier context
//
MeshDeformModData *mod_data = static_cast (mod_context_list[index]->localData);
if (mod_data != NULL) {
Mesh *mesh = mod_data->Peek_Mesh ();
// Only do this if the mesh is using vertex coloring
if (mesh->numCVerts >= mesh->numVerts) {
//
// Loop through all the per-face verts
//
for (int face = 0; face < mesh->numFaces; face ++) {
for (int vert = 0; vert < 3; vert ++) {
//
// If this vert is selected, then add its color to the total
//
if (mesh->vertSel[mesh->faces[face].v[vert]]) {
int color_index = mesh->vcFace[face].t[vert];
Point3 vert_color = mesh->vertCol[color_index];
color += vert_color;
sel_count ++;
}
}
}
}
}
}
//
// Normalize the selected color
//
if (sel_count > 0) {
color = color / sel_count;
}
nodes.DisposeTemporary ();
}
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Update_UI
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::Update_UI (MeshDeformModData *mod_data)
{
assert (mod_data != NULL);
if (m_pPanel != NULL) {
Update_Set_Count ();
m_CurrentSet = mod_data->Get_Current_Set ();
int keyframe = mod_data->Peek_Set (m_CurrentSet).Get_Current_Key_Frame ();
Set_Deform_State ((float(keyframe + 1) + 0.5F) / 10.0F);
m_pPanel->Update_Vertex_Color ();
m_pPanel->Set_Max_Sets (m_MaxSets);
m_pPanel->Set_Current_Set (m_CurrentSet);
m_pPanel->Set_Current_State (m_DeformState);
}
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Auto_Apply
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::Auto_Apply (bool auto_apply)
{
if (m_MaxInterface != NULL) {
// Get a list of contexts that we are part of.
INodeTab nodes;
ModContextList mod_context_list;
m_MaxInterface->GetModContexts (mod_context_list, nodes);
// Loop through all the modifier contexts
for (int index = 0; index < mod_context_list.Count (); index ++) {
//
// Let the mod context know what it's auto apply state is
//
MeshDeformModData *mod_data = static_cast (mod_context_list[index]->localData);
if (mod_data != NULL) {
mod_data->Auto_Apply (auto_apply);
}
}
// Cleanup
nodes.DisposeTemporary ();
}
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Set_Max_Deform_Sets
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::Set_Max_Deform_Sets (int max)
{
//
// Make sure the current set doesn't exceed the total sets
//
if (m_CurrentSet >= max) {
Set_Current_Set (max - 1, true);
}
m_MaxSets = max;
if (m_MaxInterface != NULL) {
// Get a list of contexts that we are part of.
INodeTab nodes;
ModContextList mod_context_list;
m_MaxInterface->GetModContexts (mod_context_list, nodes);
// Loop through all the modifier contexts
for (int index = 0; index < mod_context_list.Count (); index ++) {
//
// Let the mod context know the max sets have changed
//
MeshDeformModData *mod_data = static_cast (mod_context_list[index]->localData);
if (mod_data != NULL) {
mod_data->Set_Max_Deform_Sets (max);
}
}
// Cleanup
nodes.DisposeTemporary ();
NotifyDependents (FOREVER, PART_SELECT, REFMSG_CHANGE);
m_MaxInterface->RedrawViews (m_MaxInterface->GetTime ());
}
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Update_Set_Count
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::Update_Set_Count (void)
{
m_MaxSets = 1;
if (m_MaxInterface != NULL) {
// Get a list of contexts that we are part of.
INodeTab nodes;
ModContextList mod_context_list;
m_MaxInterface->GetModContexts (mod_context_list, nodes);
// Loop through all the modifier contexts
for (int index = 0; index < mod_context_list.Count (); index ++) {
//
// Get the count of sets for this context
//
MeshDeformModData *mod_data = static_cast (mod_context_list[index]->localData);
if ((mod_data != NULL) && (mod_data->Get_Set_Count () > m_MaxSets)) {
m_MaxSets = mod_data->Get_Set_Count ();
}
}
// Cleanup
nodes.DisposeTemporary ();
}
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Set_Current_Set
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::Set_Current_Set
(
int index,
bool update_selection
)
{
last_delta.x = 0;
last_delta.y = 0;
last_delta.z = 0;
m_CurrentSet = index;
if (m_MaxInterface != NULL) {
if (update_selection) {
ClearSelection (1);
}
// Get a list of contexts that we are part of.
INodeTab nodes;
ModContextList mod_context_list;
m_MaxInterface->GetModContexts (mod_context_list, nodes);
// Loop through all the modifier contexts
for (int index = 0; index < mod_context_list.Count (); index ++) {
//
// Have the mod context select the verts in its set
//
MeshDeformModData *mod_data = static_cast (mod_context_list[index]->localData);
if (mod_data != NULL) {
mod_data->Set_Current_Set (m_CurrentSet);
if (update_selection) {
mod_data->Select_Set (m_CurrentSet);
}
m_pPanel->Set_Auto_Apply_Check (mod_data->Is_Auto_Apply ());
}
}
// Repaint the model
nodes.DisposeTemporary ();
if (update_selection) {
NotifyDependents (FOREVER, PART_SELECT, REFMSG_CHANGE);
m_MaxInterface->RedrawViews (m_MaxInterface->GetTime ());
}
// Update the current 'vertex color' on the UI panel
m_pPanel->Update_Vertex_Color ();
}
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// Update_Current_Set
//
///////////////////////////////////////////////////////////////////////////
void
MeshDeformClass::Update_Current_Set (void)
{
if (m_MaxInterface != NULL) {
// Get a list of contexts that we are part of.
INodeTab nodes;
ModContextList mod_context_list;
m_MaxInterface->GetModContexts (mod_context_list, nodes);
// Loop through all the modifier contexts
for (int index = 0; index < mod_context_list.Count (); index ++) {
//
// Notify the mod context so it can update its list of verts
// in the current set.
//
MeshDeformModData *mod_data = static_cast (mod_context_list[index]->localData);
if (mod_data != NULL) {
//mod_data->Update_Set (m_CurrentSet);
}
}
// Cleanup
nodes.DisposeTemporary ();
m_bSetDirty = false;
}
return ;
}
///////////////////////////////////////////////////////////////////////////
//
// SaveLocalData
//
///////////////////////////////////////////////////////////////////////////
IOResult
MeshDeformClass::SaveLocalData (ISave *save_obj, LocalModData *mod_context)
{
assert (mod_context != NULL);
return ((MeshDeformModData *)mod_context)->Save (save_obj);
}
///////////////////////////////////////////////////////////////////////////
//
// LoadLocalData
//
///////////////////////////////////////////////////////////////////////////
IOResult
MeshDeformClass::LoadLocalData (ILoad *load_obj, LocalModData **mod_context)
{
assert (mod_context != NULL);
MeshDeformModData *mod_data = new MeshDeformModData;
(*mod_context) = mod_data;
return mod_data->Load (load_obj);
}
#if 0
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; jmesh.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);
}
#endif // 0
#if defined W3D_MAX4 //defined as in the project (.dsp)
////////////////////////////////////////////////////////////////////////////////////////
int MeshDeformClass::NumSubObjTypes()
{
return 1;
}
////////////////////////////////////////////////////////////////////////////////////////
ISubObjType *MeshDeformClass::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