478 lines
12 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 : Buccaneer Bay *
* *
* File name : AlphaModifier.cpp *
* *
* Programmer : Mike Lytle *
* *
* Start date : 11/1/1999 *
* *
* Last update : 11/1/1999 *
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "AlphaModifier.h"
enum Alpha_Messages
{
AM_NOTHING,
AM_UPDATE_DATA,
AM_INITIALIZE,
AM_BOX_CHECKED,
};
enum Dialog_Controls
{
DL_EDIT_VALUE,
DL_FIND_CHECK_BOX,
};
void AlphaModifierClass::ModifyObject(TimeValue t, ModContext &mc, ObjectState *os, INode *node)
{
if (!os->obj->IsSubClassOf(triObjectClassID))
{
return;
}
// Get a mesh from input object
TriObject *object = (TriObject*)os->obj;
Mesh *mesh = &object->mesh;
assert(mesh);
int numVert = mesh->getNumVerts();
int i = 0;
float *vdata = NULL;
// Get parameters from pblock
float sparam = 0.0f;
Interval valid = LocalValidity(t);
int pass = 1;
pblock->GetValue(DL_EDIT_VALUE, t, sparam, valid);
// If needed a control could be put into the dialog box to specify which
// pass to apply the alpha values to. At this time, it was decided to
// not implement this because of the complexity to the artist and the
// performance issues in game.
//pblock->GetValue(DL_EDIT_PASS, t, pass, valid);
// Start from 0.
pass -= 1;
assert(pass >= 0);
// Use a channel for each pass.
vdata = mesh->vertexFloat(ALPHA_VERTEX_CHANNEL + pass);
if (!vdata)
{
// Turn on the channel for vertex alpha support.
mesh->setVDataSupport(ALPHA_VERTEX_CHANNEL + pass);
vdata = mesh->vertexFloat(ALPHA_VERTEX_CHANNEL + pass);
assert(vdata);
for (i = 0; i < numVert; i++)
{
if (mesh->VertSel()[i])
{
vdata[i] = 0.0f;
}
}
}
// Tracks the state of the FIND check box.
int box_checked = 0;
if (Message == AM_UPDATE_DATA)
{
// The user has updated the dialog box, so update the data.
assert(vdata);
pblock->GetValue(DL_FIND_CHECK_BOX, t, box_checked, valid);
if (!box_checked)
{
for (i = 0; i < numVert; i++)
{
if (SelectedVertices[i])
{
vdata[i] = sparam;
}
}
}
}
if (Message == AM_BOX_CHECKED)
{
pblock->GetValue(DL_FIND_CHECK_BOX, t, box_checked, valid);
}
// The user is trying to find vertices with certain values.
if (box_checked)
{
assert(vdata);
// Find the vertices that have the user entered value.
for (i = 0; i < numVert; i++)
{
if (vdata[i] == sparam)
{
mesh->VertSel().Set(i);
SelectedVertices.Set(i);
}
else
{
mesh->VertSel().Clear(i);
SelectedVertices.Clear(i);
}
}
}
if (Message == AM_INITIALIZE)
{
assert(vdata);
SelectedVertices = mesh->VertSel();
for (i = 0; i < numVert; i++)
{
if (SelectedVertices[i])
{
// Set the value in the dialog box to the value of the
// first selected vertex.
pblock->SetValue(DL_EDIT_VALUE, t, vdata[i]);
break;
}
}
}
// Always select the vertices that have been saved by the modifier.
// This must be done because the mesh changes each time ModfiyObject is called.
for (i = 0; i < numVert; i++)
{
if (SelectedVertices[i])
{
mesh->VertSel().Set(i);
}
else
{
mesh->VertSel().Clear(i);
}
}
// Display the vertices.
mesh->SetDispFlag(DISP_SELVERTS | DISP_VERTTICKS);
mesh->selLevel = MESH_VERTEX;
object->UpdateValidity(SELECT_CHAN_NUM, object->ChannelValidity (t, SELECT_CHAN_NUM));
// Clear messages.
Message = AM_NOTHING;
}
/*===========================================================================*\
| NotifyInputChanged is called each time the input object is changed in some way
| We can find out how it was changed by checking partID and message
\*===========================================================================*/
void AlphaModifierClass::NotifyInputChanged(Interval changeInt, PartID partID, RefMessage message, ModContext *mc)
{
if( (partID&PART_TOPO) || (partID&PART_GEOM) || (partID&PART_SELECT) )
{
NotifyDependents(FOREVER, PART_OBJ, REFMSG_CHANGE);
}
}
/*===========================================================================*\
| Class Descriptor OSM
\*===========================================================================*/
class AlphaClassDesc : public ClassDesc2 {
public:
int IsPublic() { return TRUE; }
void * Create( BOOL loading ) { return new AlphaModifierClass; }
const TCHAR * ClassName() { return Get_String(IDS_ALPHA_MODIFIER_CLASS); }
SClass_ID SuperClassID() { return OSM_CLASS_ID; }
Class_ID ClassID() { return ALPHA_MODIFIER_CLASSID; }
const TCHAR* Category() { return _T(""); }
HINSTANCE HInstance() { return AppInstance; }
// Hardwired name, used by MAX Script as unique identifier
const TCHAR* InternalName() { return _T("AlphaMod"); }
};
static AlphaClassDesc AlphaCD;
ClassDesc* Get_Alpha_Desc() {return &AlphaCD;}
/*===========================================================================*\
| Paramblock2 Descriptor
\*===========================================================================*/
static ParamBlockDesc2 alpha_param_blk
(
//rollout
0, _T("AlphaModifierParams"), 0, &AlphaCD, P_AUTO_CONSTRUCT + P_AUTO_UI, 0,
IDD_ALPHA_MODIFIER, IDS_PARAMETERS, 0, 0, NULL,
// params
DL_EDIT_VALUE, _T("Custom Data Value"), TYPE_FLOAT, P_ANIMATABLE, IDS_ALPHA_MODIFIER_CLASS,
p_default, 0.0f,
p_range, 0.0f, 100.0f,
p_ui, TYPE_SPINNER, EDITTYPE_FLOAT, IDC_ALPHA_EDIT, IDC_ALPHA_SPIN, 1.0f,
end,
DL_FIND_CHECK_BOX, _T("1 Custom Data Value"), TYPE_BOOL, 0, IDS_ALPHA_MODIFIER_CLASS,
p_default, FALSE,
p_ui, TYPE_SINGLECHEKBOX, IDC_ALPHA_CHECKBOX,
p_enabled, TRUE,
end,
/*
DL_EDIT_PASS, _T("2 Custom Data Value"), TYPE_INT, P_ANIMATABLE, IDS_ALPHA_MODIFIER_CLASS,
p_default, 1,
p_range, 1, 4,
p_ui, TYPE_SPINNER, EDITTYPE_INT, IDC_ALPHA_EDIT2, IDC_ALPHA_SPIN2, 1.0,
end,
*/
end
);
/*===========================================================================*\
| Basic implementation of a dialog handler
\*===========================================================================*/
BOOL AlphaModDlgProc::DlgProc(TimeValue t, IParamMap2 *map, HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
int id = LOWORD(wParam);
int code = HIWORD(wParam);
switch (msg)
{
case WM_INITDIALOG:
AlphaModifier->Message = AM_INITIALIZE;
break;
case WM_DESTROY:
break;
case WM_COMMAND:
switch (code)
{
case EN_UPDATE:
break;
case EN_SETFOCUS:
break;
case EN_KILLFOCUS:
break;
case EN_CHANGE:
break;
}
if (id == IDC_ALPHA_EDIT)
{
AlphaModifier->Message = AM_UPDATE_DATA;
}
if (id == IDC_ALPHA_CHECKBOX)
{
AlphaModifier->Message = AM_BOX_CHECKED;
}
break;
case WM_NOTIFY:
if (id == IDC_ALPHA_EDIT)
{
AlphaModifier->Message = AM_UPDATE_DATA;
}
break;
default:
break;
}
return FALSE;
}
/*===========================================================================*\
| Constructor
| Ask the ClassDesc2 to make the AUTO_CONSTRUCT paramblocks and wire them in
\*===========================================================================*/
AlphaModifierClass::AlphaModifierClass()
{
AlphaCD.MakeAutoParamBlocks(this);
assert(pblock);
Message = 0;
}
/*===========================================================================*\
| Invalidate our UI (or the recently changed parameter)
\*===========================================================================*/
void AlphaModifierClass::InvalidateUI()
{
alpha_param_blk.InvalidateUI(pblock->LastNotifyParamID());
}
/*===========================================================================*\
| Open and Close dialog UIs
| We ask the ClassDesc2 to handle Beginning and Ending EditParams for us
\*===========================================================================*/
void AlphaModifierClass::BeginEditParams( IObjParam *ip, ULONG flags,Animatable *prev )
{
AlphaCD.BeginEditParams(ip, this, flags, prev);
alpha_param_blk.SetUserDlgProc(new AlphaModDlgProc(this));
}
void AlphaModifierClass::EndEditParams( IObjParam *ip, ULONG flags,Animatable *next )
{
AlphaCD.EndEditParams(ip, this, flags, next);
}
/*===========================================================================*\
| Standard clone
\*===========================================================================*/
RefTargetHandle AlphaModifierClass::Clone(RemapDir& remap)
{
AlphaModifierClass* newmod = new AlphaModifierClass();
newmod->ReplaceReference(0,pblock->Clone(remap));
return(newmod);
}
/*===========================================================================*\
| Subanim & References support
\*===========================================================================*/
Animatable* AlphaModifierClass::SubAnim(int i)
{
switch (i)
{
case 0: return pblock;
default: return NULL;
}
}
TSTR AlphaModifierClass::SubAnimName(int i)
{
switch (i)
{
case 0: return Get_String(IDS_PARAMETERS);
default: return _T("");
}
}
RefTargetHandle AlphaModifierClass::GetReference(int i)
{
switch (i)
{
case 0: return pblock;
default:
assert(TRUE);
return NULL;
}
}
void AlphaModifierClass::SetReference(int i, RefTargetHandle rtarg)
{
switch (i)
{
case 0: pblock = (IParamBlock2*)rtarg; break;
default:
assert(TRUE);
break;
}
}
RefResult AlphaModifierClass::NotifyRefChanged
(
Interval changeInt,
RefTargetHandle hTarget,
PartID& partID,
RefMessage message
)
{
switch (message)
{
case REFMSG_CHANGE:
{
alpha_param_blk.InvalidateUI();
}
break;
}
return REF_SUCCEED;
}
/*===========================================================================*\
| The validity of our parameters
| Start at FOREVER, and intersect with the validity of each item
\*===========================================================================*/
Interval AlphaModifierClass::GetValidity(TimeValue t)
{
float f;
Interval valid = FOREVER;
pblock->GetValue(DL_EDIT_VALUE, t, f, valid);
return valid;
}
Interval AlphaModifierClass::LocalValidity(TimeValue t)
{
return GetValidity(t);
}