478 lines
12 KiB
C++
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);
|
|
}
|
|
|
|
|
|
|