Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
1122 lines
29 KiB
C++
1122 lines
29 KiB
C++
/**********************************************************************
|
|
*<
|
|
FILE: lodctrl.cpp
|
|
|
|
DESCRIPTION: A level of detail controller
|
|
|
|
CREATED BY: Rolf Berteig
|
|
|
|
HISTORY: 4/12/97
|
|
|
|
*> Copyright (c) 1997, All Rights Reserved.
|
|
**********************************************************************/
|
|
#include "ctrl.h"
|
|
#include "units.h"
|
|
#include "utilapi.h"
|
|
|
|
#define LOD_CONTROL_CLASS_ID Class_ID(0xbbe961a8,0xa0ee7b7f)
|
|
#define LOD_CONTROL_CNAME GetString(IDS_RB_LODCONTROL)
|
|
|
|
#define LOD_UTILITY_CLASS_ID Class_ID(0x100d37ef,0x1aa0ab84)
|
|
#define LOD_UTILITY_CNAME GetString(IDS_RB_LODUTILITU)
|
|
|
|
class LODCtrl : public StdControl {
|
|
public:
|
|
float min, max, bmin, bmax;
|
|
WORD grpID;
|
|
int order;
|
|
BOOL viewport, highest;
|
|
|
|
LODCtrl();
|
|
|
|
// Animatable methods
|
|
void DeleteThis() {delete this;}
|
|
int IsKeyable() {return 0;}
|
|
Class_ID ClassID() {return LOD_CONTROL_CLASS_ID;}
|
|
SClass_ID SuperClassID() {return CTRL_FLOAT_CLASS_ID;}
|
|
void GetClassName(TSTR& s) {s = LOD_CONTROL_CNAME;}
|
|
BOOL CanCopyAnim() {return FALSE;}
|
|
BOOL CanMakeUnique() {return FALSE;}
|
|
|
|
// Reference methods
|
|
RefResult NotifyRefChanged(Interval, RefTargetHandle, PartID&, RefMessage) {return REF_SUCCEED;}
|
|
IOResult Save(ISave *isave);
|
|
IOResult Load(ILoad *iload);
|
|
RefTargetHandle Clone(RemapDir &remap = NoRemap());
|
|
|
|
// Control methods
|
|
void Copy(Control *from) {}
|
|
BOOL IsLeaf() {return TRUE;}
|
|
void CommitValue(TimeValue t) {}
|
|
void RestoreValue(TimeValue t) {}
|
|
BOOL IsReplaceable() {return FALSE;}
|
|
BOOL CanInstanceController() {return FALSE;}
|
|
|
|
// StdControl methods
|
|
void GetValueLocalTime(TimeValue t, void *val, Interval &valid, GetSetMethod method=CTRL_ABSOLUTE)
|
|
{*((float*)val) = 1.0f;}
|
|
void SetValueLocalTime(TimeValue t, void *val, int commit, GetSetMethod method) {}
|
|
void Extrapolate(Interval range,TimeValue t,void *val,Interval &valid,int type)
|
|
{*((float*)val) = 1.0f;}
|
|
void *CreateTempValue() {return new float;}
|
|
void DeleteTempValue(void *val) {delete (float*)val;}
|
|
void ApplyValue(void *val, void *delta) {*((float*)val) += *((float*)delta);}
|
|
void MultiplyValue(void *val, float m) {*((float*)val) *= m;}
|
|
|
|
float EvalVisibility(TimeValue t,View &view,Box3 pbox,Interval &valid);
|
|
BOOL VisibleInViewports();
|
|
};
|
|
|
|
class LODUtil :
|
|
public UtilityObj,
|
|
public PickModeCallback,
|
|
public PickNodeCallback {
|
|
public:
|
|
IUtil *iu;
|
|
Interface *ip;
|
|
HWND hWnd;
|
|
INodeTab nodes;
|
|
int disp;
|
|
|
|
// From UtilityObj
|
|
void BeginEditParams(Interface *ip,IUtil *iu);
|
|
void EndEditParams(Interface *ip,IUtil *iu);
|
|
void DeleteThis() {}
|
|
void SelectionSetChanged(Interface *ip,IUtil *iu);
|
|
|
|
// From PickModeCallback
|
|
BOOL HitTest(IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags);
|
|
BOOL Pick(IObjParam *ip,ViewExp *vpt);
|
|
BOOL RightClick(IObjParam *ip,ViewExp *vpt) {return TRUE;}
|
|
void EnterMode(IObjParam *ip);
|
|
void ExitMode(IObjParam *ip);
|
|
PickNodeCallback *GetFilter() {return this;}
|
|
BOOL AllowMultiSelect() {return TRUE;}
|
|
BOOL Filter(INode *node);
|
|
|
|
// Local methods
|
|
LODUtil();
|
|
void FindGroupMembers(INode *root,WORD id,INodeTab &nodes);
|
|
WORD FindUniqueGroupID();
|
|
INode *FindSelGroupHead();
|
|
int GetSelID();
|
|
void SetStates();
|
|
void SetupList();
|
|
void ListSelChanged();
|
|
void CreateNewSet();
|
|
void ResetSet();
|
|
void ResetOutput();
|
|
void SetViewportObj(BOOL onOff);
|
|
void DeleteFromSet();
|
|
void SetMin(float m);
|
|
void SetMax(float m);
|
|
void SetBlendRegions();
|
|
float ConvertToPercent(float size);
|
|
float ConvertFromPercent(float perc);
|
|
void SetDisplayMode(int mode);
|
|
void InitDlg();
|
|
};
|
|
static LODUtil theLODUtil;
|
|
|
|
class LODClassDesc:public ClassDesc {
|
|
public:
|
|
int IsPublic() {return 0;}
|
|
void * Create(BOOL loading) {return new LODCtrl();}
|
|
const TCHAR * ClassName() {return LOD_CONTROL_CNAME;}
|
|
SClass_ID SuperClassID() {return CTRL_FLOAT_CLASS_ID; }
|
|
Class_ID ClassID() {return LOD_CONTROL_CLASS_ID;}
|
|
const TCHAR* Category() {return _T("");}
|
|
};
|
|
static LODClassDesc lodCD;
|
|
ClassDesc* GetLODControlDesc() {return &lodCD;}
|
|
|
|
class LODUtilClassDesc:public ClassDesc {
|
|
public:
|
|
int IsPublic() {return 1;}
|
|
void * Create(BOOL loading = FALSE) {return &theLODUtil;}
|
|
const TCHAR * ClassName() {return LOD_UTILITY_CNAME;}
|
|
SClass_ID SuperClassID() {return UTILITY_CLASS_ID;}
|
|
Class_ID ClassID() {return LOD_UTILITY_CLASS_ID;}
|
|
const TCHAR* Category() {return _T("");}
|
|
};
|
|
|
|
static LODUtilClassDesc lodUtilDesc;
|
|
ClassDesc* GetLODUtilDesc() {return &lodUtilDesc;}
|
|
|
|
|
|
//--- LODCtrl --------------------------------------------------------
|
|
|
|
LODCtrl::LODCtrl()
|
|
{
|
|
bmin = min = 0.0f;
|
|
bmax = max = 0.0f;
|
|
grpID = 0;
|
|
order = 0;
|
|
viewport = FALSE;
|
|
highest = FALSE;
|
|
}
|
|
|
|
#define MIN_CHUNK 0x0100
|
|
#define MAX_CHUNK 0x0110
|
|
#define BMIN_CHUNK 0x0112
|
|
#define BMAX_CHUNK 0x0115
|
|
#define GROUPID_CHUNK 0x0120
|
|
#define ORDER_CHUNK 0x0130
|
|
#define VIEWPORT_CHUNK 0x0140
|
|
#define HIGHEST_CHUNK 0x0150
|
|
|
|
IOResult LODCtrl::Save(ISave *isave)
|
|
{
|
|
ULONG nb;
|
|
|
|
isave->BeginChunk(MIN_CHUNK);
|
|
isave->Write(&min,sizeof(min),&nb);
|
|
isave->EndChunk();
|
|
|
|
isave->BeginChunk(MAX_CHUNK);
|
|
isave->Write(&max,sizeof(max),&nb);
|
|
isave->EndChunk();
|
|
|
|
isave->BeginChunk(BMIN_CHUNK);
|
|
isave->Write(&bmin,sizeof(bmin),&nb);
|
|
isave->EndChunk();
|
|
|
|
isave->BeginChunk(BMAX_CHUNK);
|
|
isave->Write(&bmax,sizeof(bmax),&nb);
|
|
isave->EndChunk();
|
|
|
|
isave->BeginChunk(GROUPID_CHUNK);
|
|
isave->Write(&grpID,sizeof(grpID),&nb);
|
|
isave->EndChunk();
|
|
|
|
isave->BeginChunk(ORDER_CHUNK);
|
|
isave->Write(&order,sizeof(order),&nb);
|
|
isave->EndChunk();
|
|
|
|
isave->BeginChunk(VIEWPORT_CHUNK);
|
|
isave->Write(&viewport,sizeof(viewport),&nb);
|
|
isave->EndChunk();
|
|
|
|
isave->BeginChunk(HIGHEST_CHUNK);
|
|
isave->Write(&highest,sizeof(highest),&nb);
|
|
isave->EndChunk();
|
|
|
|
return IO_OK;
|
|
}
|
|
|
|
IOResult LODCtrl::Load(ILoad *iload)
|
|
{
|
|
ULONG nb;
|
|
IOResult res;
|
|
|
|
while (IO_OK==(res=iload->OpenChunk())) {
|
|
switch (iload->CurChunkID()) {
|
|
case MIN_CHUNK:
|
|
iload->Read(&min,sizeof(min),&nb);
|
|
break;
|
|
|
|
case MAX_CHUNK:
|
|
iload->Read(&max,sizeof(max),&nb);
|
|
break;
|
|
|
|
case BMIN_CHUNK:
|
|
iload->Read(&bmin,sizeof(bmin),&nb);
|
|
break;
|
|
|
|
case BMAX_CHUNK:
|
|
iload->Read(&bmax,sizeof(bmax),&nb);
|
|
break;
|
|
|
|
case GROUPID_CHUNK:
|
|
iload->Read(&grpID,sizeof(grpID),&nb);
|
|
break;
|
|
|
|
case ORDER_CHUNK:
|
|
iload->Read(&order,sizeof(order),&nb);
|
|
break;
|
|
|
|
case VIEWPORT_CHUNK:
|
|
iload->Read(&viewport,sizeof(viewport),&nb);
|
|
break;
|
|
|
|
case HIGHEST_CHUNK:
|
|
iload->Read(&highest,sizeof(highest),&nb);
|
|
break;
|
|
}
|
|
iload->CloseChunk();
|
|
if (res!=IO_OK) return res;
|
|
}
|
|
return IO_OK;
|
|
}
|
|
|
|
RefTargetHandle LODCtrl::Clone(RemapDir &remap)
|
|
{
|
|
Control *cont = NewDefaultFloatController();
|
|
float val = 1.0f;
|
|
cont->SetValue(0,&val);
|
|
return cont;
|
|
}
|
|
|
|
float LODCtrl::EvalVisibility(
|
|
TimeValue t,View &view,Box3 pbox,Interval &valid)
|
|
{
|
|
float xmin, xmax, ymin, ymax;
|
|
for (int i=0; i<8; i++) {
|
|
Point2 pt = view.ViewToScreen(pbox[i]*view.worldToView);
|
|
if (!i) {
|
|
xmin = xmax = pt.x;
|
|
ymin = ymax = pt.y;
|
|
} else {
|
|
if (pt.x<xmin) xmin = pt.x;
|
|
if (pt.x>xmax) xmax = pt.x;
|
|
if (pt.y<ymin) ymin = pt.y;
|
|
if (pt.y>ymax) ymax = pt.y;
|
|
}
|
|
}
|
|
float w = xmax-xmin;
|
|
float h = ymax-ymin;
|
|
float size = (float)sqrt((w*w) + (h*h));
|
|
if (highest && size>=min) return 1.0f;
|
|
if (size>=min && size<=max) return 1.0f;
|
|
if (size<min && size>bmin) {
|
|
return 1.0f - (min-size)/(min-bmin);
|
|
}
|
|
if (size>max && size<bmax) {
|
|
return 1.0f - (size-max)/(bmax-max);
|
|
}
|
|
return 0.0f;
|
|
}
|
|
|
|
BOOL LODCtrl::VisibleInViewports()
|
|
{
|
|
return viewport;
|
|
}
|
|
|
|
//--- LODUtil --------------------------------------------------------
|
|
|
|
static BOOL CALLBACK LODUtilDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
|
|
|
#define NODESEL_LODANDNONLOD -4
|
|
#define NODESEL_LODMIX -3
|
|
#define NODESEL_NONLOD -2
|
|
#define NODESEL_NONE -1
|
|
|
|
|
|
LODUtil::LODUtil()
|
|
{
|
|
iu = NULL;
|
|
ip = NULL;
|
|
hWnd = NULL;
|
|
disp = IDC_LOD_DISPPERCENT;
|
|
}
|
|
|
|
void LODUtil::BeginEditParams(Interface *ip,IUtil *iu)
|
|
{
|
|
this->iu = iu;
|
|
this->ip = ip;
|
|
hWnd = ip->AddRollupPage(
|
|
hInstance,
|
|
MAKEINTRESOURCE(IDD_LOD_UTILPARAMS),
|
|
LODUtilDlgProc,
|
|
GetString(IDS_RB_LODUTILITU),
|
|
0);
|
|
}
|
|
|
|
void LODUtil::EndEditParams(Interface *ip,IUtil *iu)
|
|
{
|
|
ip->ClearPickMode();
|
|
ip->DeleteRollupPage(hWnd);
|
|
this->iu = NULL;
|
|
this->ip = NULL;
|
|
}
|
|
|
|
void LODUtil::SelectionSetChanged(Interface *ip,IUtil *iu)
|
|
{
|
|
SetStates();
|
|
}
|
|
|
|
|
|
static int __cdecl CompareNodes(const void *elem1, const void *elem2)
|
|
{
|
|
INode *node1 = *((INode**)elem1);
|
|
INode *node2 = *((INode**)elem2);
|
|
LODCtrl *cont1 = (LODCtrl*)node1->GetVisController();
|
|
LODCtrl *cont2 = (LODCtrl*)node2->GetVisController();
|
|
if (!cont1 || !cont2) return 0;
|
|
return cont1->order-cont2->order;
|
|
}
|
|
|
|
#define BLEND_PERCENT 0.2f
|
|
|
|
void LODUtil::InitDlg()
|
|
{
|
|
ISpinnerControl *iW = GetISpinner(GetDlgItem(hWnd,IDC_LOD_VIEWWIDTHSPIN));
|
|
ISpinnerControl *iH = GetISpinner(GetDlgItem(hWnd,IDC_LOD_VIEWHEIGHTSPIN));
|
|
|
|
iW->SetLimits(1.0f,99999999.0f,FALSE);
|
|
iW->SetScale(0.1f);
|
|
iW->LinkToEdit(GetDlgItem(hWnd,IDC_LOD_VIEWWIDTH),EDITTYPE_INT);
|
|
iW->SetValue(ip->GetRendWidth(),FALSE);
|
|
iH->SetLimits(1.0f,99999999.0f,FALSE);
|
|
iH->SetScale(0.1f);
|
|
iH->LinkToEdit(GetDlgItem(hWnd,IDC_LOD_VIEWHEIGHT),EDITTYPE_INT);
|
|
iH->SetValue(ip->GetRendHeight(),FALSE);
|
|
|
|
ReleaseISpinner(iW);
|
|
ReleaseISpinner(iH);
|
|
}
|
|
|
|
void LODUtil::ResetOutput()
|
|
{
|
|
ISpinnerControl *iW = GetISpinner(GetDlgItem(hWnd,IDC_LOD_VIEWWIDTHSPIN));
|
|
ISpinnerControl *iH = GetISpinner(GetDlgItem(hWnd,IDC_LOD_VIEWHEIGHTSPIN));
|
|
iW->SetValue(ip->GetRendWidth(),FALSE);
|
|
iH->SetValue(ip->GetRendHeight(),FALSE);
|
|
ReleaseISpinner(iW);
|
|
ReleaseISpinner(iH);
|
|
SetupList();
|
|
ListSelChanged();
|
|
}
|
|
|
|
void LODUtil::SetStates()
|
|
{
|
|
int id = GetSelID();
|
|
|
|
ISpinnerControl *iMin = GetISpinner(GetDlgItem(hWnd,IDC_LOD_MINSPIN));
|
|
ISpinnerControl *iMax = GetISpinner(GetDlgItem(hWnd,IDC_LOD_MAXSPIN));
|
|
|
|
iMin->SetLimits(0.0f,99999999.0f,FALSE);
|
|
iMin->SetScale(0.1f);
|
|
iMin->LinkToEdit(GetDlgItem(hWnd,IDC_LOD_MIN),EDITTYPE_FLOAT);
|
|
iMax->SetLimits(0.0f,99999999.0f,FALSE);
|
|
iMax->SetScale(0.1f);
|
|
iMax->LinkToEdit(GetDlgItem(hWnd,IDC_LOD_MAX),EDITTYPE_FLOAT);
|
|
|
|
ReleaseISpinner(iMin);
|
|
ReleaseISpinner(iMax);
|
|
|
|
SendDlgItemMessage(hWnd,IDC_LOD_LIST,LB_RESETCONTENT,0,0);
|
|
|
|
nodes.SetCount(0);
|
|
if (id>=0) {
|
|
LODCtrl *cont;
|
|
LODCtrl *cprev=NULL;
|
|
FindGroupMembers(ip->GetRootNode(),(WORD)id,nodes);
|
|
nodes.Sort(CompareNodes);
|
|
for (int i=0; i<nodes.Count(); i++) {
|
|
cont = (LODCtrl*)nodes[i]->GetVisController();
|
|
if (!cont) {
|
|
return;
|
|
}
|
|
|
|
if (cprev) {
|
|
cont->min = cprev->max;
|
|
cont->order = cprev->order+1;
|
|
if (cont->max<cont->min) cont->max = cont->min;
|
|
cont->bmin = cont->min - (cprev->max-cprev->min)*BLEND_PERCENT;
|
|
} else {
|
|
cont->min = 0;
|
|
cont->bmin = 0;
|
|
cont->order = 0;
|
|
}
|
|
if (i<nodes.Count()-1) {
|
|
LODCtrl*cnext = (LODCtrl*)nodes[i+1]->GetVisController();
|
|
if (!cnext) return;
|
|
cont->bmax = cont->max + (cnext->max-cnext->min)*BLEND_PERCENT;
|
|
cont->highest = FALSE;
|
|
} else {
|
|
cont->bmax = cont->max + (cont->max-cont->min)*BLEND_PERCENT;
|
|
cont->highest = TRUE;
|
|
}
|
|
cprev = cont;
|
|
}
|
|
SetupList();
|
|
ICustButton *but;
|
|
but = GetICustButton(GetDlgItem(hWnd,IDC_LOD_ADDTOSET));
|
|
but->SetType(CBT_CHECK);
|
|
but->SetHighlightColor(GREEN_WASH);
|
|
but->Enable();
|
|
ReleaseICustButton(but);
|
|
EnableWindow(GetDlgItem(hWnd,IDC_LOD_RESET),TRUE);
|
|
EnableWindow(GetDlgItem(hWnd,IDC_LOD_DISPPIXELS),TRUE);
|
|
EnableWindow(GetDlgItem(hWnd,IDC_LOD_DISPPERCENT),TRUE);
|
|
CheckDlgButton(hWnd,disp,TRUE);
|
|
but = GetICustButton(GetDlgItem(hWnd,IDC_LOD_CREATENEWSET));
|
|
but->Disable();
|
|
ReleaseICustButton(but);
|
|
} else {
|
|
EnableWindow(GetDlgItem(hWnd,IDC_LOD_DISPPIXELS),FALSE);
|
|
EnableWindow(GetDlgItem(hWnd,IDC_LOD_DISPPERCENT),FALSE);
|
|
EnableWindow(GetDlgItem(hWnd,IDC_LOD_RESET),FALSE);
|
|
ICustButton *but;
|
|
but = GetICustButton(GetDlgItem(hWnd,IDC_LOD_ADDTOSET));
|
|
but->Disable();
|
|
but->SetCheck(FALSE);
|
|
ip->ClearPickMode();
|
|
ReleaseICustButton(but);
|
|
but = GetICustButton(GetDlgItem(hWnd,IDC_LOD_CREATENEWSET));
|
|
if (id==NODESEL_NONLOD) {
|
|
but->Enable();
|
|
} else {
|
|
but->Disable();
|
|
}
|
|
ReleaseICustButton(but);
|
|
}
|
|
ListSelChanged();
|
|
}
|
|
|
|
float LODUtil::ConvertToPercent(float size)
|
|
{
|
|
ISpinnerControl *iW = GetISpinner(GetDlgItem(hWnd,IDC_LOD_VIEWWIDTHSPIN));
|
|
ISpinnerControl *iH = GetISpinner(GetDlgItem(hWnd,IDC_LOD_VIEWHEIGHTSPIN));
|
|
int w = iW->GetIVal();
|
|
int h = iH->GetIVal();
|
|
ReleaseISpinner(iW);
|
|
ReleaseISpinner(iH);
|
|
float screen = (float)sqrt(float(w*w)+float(h*h));
|
|
return size/screen * 100.0f;
|
|
}
|
|
|
|
float LODUtil::ConvertFromPercent(float perc)
|
|
{
|
|
ISpinnerControl *iW = GetISpinner(GetDlgItem(hWnd,IDC_LOD_VIEWWIDTHSPIN));
|
|
ISpinnerControl *iH = GetISpinner(GetDlgItem(hWnd,IDC_LOD_VIEWHEIGHTSPIN));
|
|
int w = iW->GetIVal();
|
|
int h = iH->GetIVal();
|
|
ReleaseISpinner(iW);
|
|
ReleaseISpinner(iH);
|
|
float screen = (float)sqrt(float(w*w)+float(h*h));
|
|
return screen*perc/100.0f;
|
|
}
|
|
|
|
void LODUtil::SetupList()
|
|
{
|
|
LODCtrl *cont;
|
|
int sel = SendDlgItemMessage(hWnd,IDC_LOD_LIST,LB_GETCURSEL,0,0);
|
|
SendDlgItemMessage(hWnd,IDC_LOD_LIST,LB_RESETCONTENT,0,0);
|
|
for (int i=0; i<nodes.Count(); i++) {
|
|
cont = (LODCtrl*)nodes[i]->GetVisController();
|
|
if (!cont) continue;
|
|
TSTR buf;
|
|
float val = cont->max;
|
|
if (disp==IDC_LOD_DISPPERCENT) {
|
|
val = ConvertToPercent(val);
|
|
}
|
|
buf.printf(_T("%.1f\t%s"),val,nodes[i]->GetName());
|
|
SendDlgItemMessage(hWnd,IDC_LOD_LIST,LB_ADDSTRING,0,
|
|
(LPARAM)(TCHAR*)buf);
|
|
}
|
|
if (sel!=LB_ERR) {
|
|
SendDlgItemMessage(hWnd,IDC_LOD_LIST,LB_SETCURSEL,sel,0);
|
|
}
|
|
}
|
|
|
|
void LODUtil::SetDisplayMode(int mode)
|
|
{
|
|
disp = mode;
|
|
SetupList();
|
|
ListSelChanged();
|
|
}
|
|
|
|
void LODUtil::ListSelChanged()
|
|
{
|
|
ICustButton *but;
|
|
|
|
int sel = SendDlgItemMessage(hWnd,IDC_LOD_LIST,LB_GETCURSEL,0,0);
|
|
ISpinnerControl *iMin = GetISpinner(GetDlgItem(hWnd,IDC_LOD_MINSPIN));
|
|
ISpinnerControl *iMax = GetISpinner(GetDlgItem(hWnd,IDC_LOD_MAXSPIN));
|
|
|
|
if (sel==LB_ERR) {
|
|
iMin->Disable();
|
|
iMin->SetValue(0.0f,FALSE);
|
|
iMax->Disable();
|
|
iMax->SetValue(0.0f,FALSE);
|
|
EnableWindow(GetDlgItem(hWnd,IDC_LOD_MINLABEL),FALSE);
|
|
EnableWindow(GetDlgItem(hWnd,IDC_LOD_MAXLABEL),FALSE);
|
|
EnableWindow(GetDlgItem(hWnd,IDC_LOD_VIEWPORTOBJ),FALSE);
|
|
but = GetICustButton(GetDlgItem(hWnd,IDC_LOD_DELETEFROMSET));
|
|
but->Disable();
|
|
ReleaseICustButton(but);
|
|
} else {
|
|
iMin->Enable();
|
|
iMax->Enable();
|
|
EnableWindow(GetDlgItem(hWnd,IDC_LOD_MINLABEL),TRUE);
|
|
EnableWindow(GetDlgItem(hWnd,IDC_LOD_MAXLABEL),TRUE);
|
|
EnableWindow(GetDlgItem(hWnd,IDC_LOD_VIEWPORTOBJ),TRUE);
|
|
but = GetICustButton(GetDlgItem(hWnd,IDC_LOD_DELETEFROMSET));
|
|
but->Enable();
|
|
ReleaseICustButton(but);
|
|
|
|
LODCtrl *cont = (LODCtrl*)nodes[sel]->GetVisController();
|
|
if (cont) {
|
|
|
|
if (disp==IDC_LOD_DISPPERCENT) {
|
|
iMin->SetValue(ConvertToPercent(cont->min),FALSE);
|
|
iMax->SetValue(ConvertToPercent(cont->max),FALSE);
|
|
} else {
|
|
iMin->SetValue(cont->min,FALSE);
|
|
iMax->SetValue(cont->max,FALSE);
|
|
}
|
|
CheckDlgButton(hWnd,IDC_LOD_VIEWPORTOBJ,cont->viewport);
|
|
}
|
|
}
|
|
|
|
ReleaseISpinner(iMin);
|
|
ReleaseISpinner(iMax);
|
|
}
|
|
|
|
void LODUtil::FindGroupMembers(INode *root,WORD id,INodeTab &nodes)
|
|
{
|
|
for (int i=0; i<root->NumberOfChildren(); i++) {
|
|
INode *node = root->GetChildNode(i);
|
|
Control *cont = node->GetVisController();
|
|
if (cont && cont->ClassID()==LOD_CONTROL_CLASS_ID) {
|
|
LODCtrl *lcont = (LODCtrl*)cont;
|
|
if (lcont->grpID==id) {
|
|
nodes.Append(1,&node,10);
|
|
}
|
|
}
|
|
FindGroupMembers(node,id,nodes);
|
|
}
|
|
}
|
|
|
|
static BOOL IsAncestorSelected(INode *node)
|
|
{
|
|
if (node->GetParentNode()) {
|
|
if (node->GetParentNode()->Selected()) return TRUE;
|
|
return IsAncestorSelected(node->GetParentNode());
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
int LODUtil::GetSelID()
|
|
{
|
|
int id = -1;
|
|
BOOL gotLOD=FALSE, gotNonLOD=FALSE;
|
|
|
|
if (!ip->GetSelNodeCount()) return NODESEL_NONE;
|
|
|
|
INode *grpHead = FindSelGroupHead();
|
|
|
|
for (int i=0; i<ip->GetSelNodeCount(); i++) {
|
|
if (ip->GetSelNode(i)==grpHead) continue;
|
|
if (IsAncestorSelected(ip->GetSelNode(i)->GetParentNode())) continue;
|
|
|
|
Control *cont = ip->GetSelNode(i)->GetVisController();
|
|
if (cont && cont->ClassID()==LOD_CONTROL_CLASS_ID) {
|
|
LODCtrl *lcont = (LODCtrl*)cont;
|
|
gotLOD = TRUE;
|
|
if (id==-1) id = lcont->grpID;
|
|
else if (id!=lcont->grpID) id = -2;
|
|
} else {
|
|
gotNonLOD = TRUE;
|
|
}
|
|
}
|
|
|
|
//if (gotNonLOD && gotLOD) return NODESEL_LODANDNONLOD;
|
|
if (gotLOD && id<0) return NODESEL_LODMIX;
|
|
if (gotNonLOD && !gotLOD) return NODESEL_NONLOD;
|
|
|
|
return id;
|
|
}
|
|
|
|
class NodeSize {
|
|
public:
|
|
INode *node; int faces;
|
|
NodeSize() {node=NULL;faces=0;}
|
|
NodeSize(INode *n,TimeValue t);
|
|
void CountFaces(INode *node,TimeValue t);
|
|
};
|
|
|
|
class NullView: public View {
|
|
public:
|
|
Point2 ViewToScreen(Point3 p) { return Point2(p.x,p.y); }
|
|
NullView() { worldToView.IdentityMatrix(); screenW=640.0f; screenH = 480.0f; }
|
|
};
|
|
|
|
NodeSize::NodeSize(INode *n,TimeValue t)
|
|
{
|
|
node = n;
|
|
faces = 0;
|
|
CountFaces(node,t);
|
|
}
|
|
|
|
void NodeSize::CountFaces(INode *node,TimeValue t)
|
|
{
|
|
ObjectState os = node->EvalWorldState(t,TRUE);
|
|
BOOL needDelete = FALSE;
|
|
NullView view;
|
|
if (os.obj->SuperClassID()==GEOMOBJECT_CLASS_ID) {
|
|
GeomObject *obj = (GeomObject*)os.obj;
|
|
Mesh *mesh = obj->GetRenderMesh(t, node, view, needDelete);
|
|
if (mesh) faces += mesh->getNumFaces();
|
|
if (needDelete) delete mesh;
|
|
}
|
|
for (int i=0; i<node->NumberOfChildren(); i++) {
|
|
CountFaces(node->GetChildNode(i),t);
|
|
}
|
|
}
|
|
|
|
static int __cdecl CompareNodeSize(const void *elem1, const void *elem2)
|
|
{
|
|
NodeSize *ns1 = (NodeSize*)elem1;
|
|
NodeSize *ns2 = (NodeSize*)elem2;
|
|
return ns1->faces - ns2->faces;
|
|
}
|
|
|
|
static void CheckIDs(INode *root,BitArray &used)
|
|
{
|
|
Control *cont = (Control*)root->GetVisController();
|
|
if (cont && cont->ClassID()==LOD_CONTROL_CLASS_ID) {
|
|
LODCtrl *lcont = (LODCtrl*)cont;
|
|
used.Set(lcont->grpID);
|
|
}
|
|
for (int i=0; i<root->NumberOfChildren(); i++) {
|
|
CheckIDs(root->GetChildNode(i),used);
|
|
}
|
|
}
|
|
|
|
WORD LODUtil::FindUniqueGroupID()
|
|
{
|
|
BitArray used;
|
|
used.SetSize(65536);
|
|
CheckIDs(ip->GetRootNode(),used);
|
|
for (int i=0; i<used.GetSize(); i++) {
|
|
if (!used[i]) return (WORD)i;
|
|
}
|
|
return 65535;
|
|
}
|
|
|
|
INode *LODUtil::FindSelGroupHead()
|
|
{
|
|
INode *grpHead = NULL;
|
|
for (int i=0; i<ip->GetSelNodeCount(); i++) {
|
|
if (IsAncestorSelected(ip->GetSelNode(i)->GetParentNode())) continue;
|
|
INode *node = ip->GetSelNode(i);
|
|
if (node->IsGroupHead()) {
|
|
BOOL res = TRUE;
|
|
for (int j=0; j<ip->GetSelNodeCount(); j++) {
|
|
if (i==j) continue;
|
|
if (IsAncestorSelected(ip->GetSelNode(j)->GetParentNode())) continue;
|
|
INode *cnode = ip->GetSelNode(j);
|
|
if (cnode->GetParentNode()!=node) {
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (res) {
|
|
grpHead = node;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return grpHead;
|
|
}
|
|
|
|
static void SetInheritVis(INode *node)
|
|
{
|
|
node->SetInheritVisibility(TRUE);
|
|
for (int i=0; i<node->NumberOfChildren(); i++) {
|
|
SetInheritVis(node->GetChildNode(i));
|
|
}
|
|
}
|
|
|
|
void LODUtil::CreateNewSet()
|
|
{
|
|
// See if one of the selected objects is the group head of the
|
|
// rest of the selected objects
|
|
INode *grpHead = FindSelGroupHead();
|
|
|
|
if (!grpHead) {
|
|
TSTR buf2(GetString(IDS_RB_LODUTILITU));
|
|
TSTR buf1(GetString(IDS_RB_LODSAMEPARENT));
|
|
MessageBox(hWnd,buf1,buf2,MB_ICONSTOP);
|
|
return;
|
|
}
|
|
|
|
// Turn on visibility inheritance
|
|
SetInheritVis(grpHead);
|
|
|
|
// Build a sorted list of nodes along with their sizes
|
|
Tab<NodeSize> nodesizes;
|
|
for (int i=0; i<ip->GetSelNodeCount(); i++) {
|
|
if (ip->GetSelNode(i)==grpHead) continue;
|
|
if (IsAncestorSelected(ip->GetSelNode(i)->GetParentNode())) continue;
|
|
|
|
NodeSize ns(ip->GetSelNode(i),ip->GetTime());
|
|
nodesizes.Append(1,&ns,10);
|
|
}
|
|
nodesizes.Sort(CompareNodeSize);
|
|
|
|
// Assign LOD controllers
|
|
WORD grpID = FindUniqueGroupID();
|
|
if (grpID==65535) return;
|
|
|
|
int ct = nodesizes.Count();
|
|
for (i=0; i<ct; i++) {
|
|
LODCtrl *cont = new LODCtrl;
|
|
cont->grpID = grpID;
|
|
cont->order = i;
|
|
cont->viewport = i==0;
|
|
nodesizes[i].node->SetVisController(cont);
|
|
}
|
|
|
|
SetStates();
|
|
ResetSet();
|
|
ip->RedrawViews(ip->GetTime());
|
|
}
|
|
|
|
void LODUtil::ResetSet()
|
|
{
|
|
// Compute the target screen size
|
|
ISpinnerControl *iW = GetISpinner(GetDlgItem(hWnd,IDC_LOD_VIEWWIDTHSPIN));
|
|
ISpinnerControl *iH = GetISpinner(GetDlgItem(hWnd,IDC_LOD_VIEWHEIGHTSPIN));
|
|
int w = iW->GetIVal();
|
|
int h = iH->GetIVal();
|
|
ReleaseISpinner(iW);
|
|
ReleaseISpinner(iH);
|
|
float size = (float)sqrt(float(w*w)+float(h*h));
|
|
|
|
Tab<NodeSize> nodesizes;
|
|
for (int i=0; i<nodes.Count(); i++) {
|
|
NodeSize ns(nodes[i],ip->GetTime());
|
|
nodesizes.Append(1,&ns,10);
|
|
}
|
|
nodesizes.Sort(CompareNodeSize);
|
|
for (i=0; i<nodesizes.Count(); i++) {
|
|
nodes[i] = nodesizes[i].node;
|
|
}
|
|
|
|
float maxFaces = float(nodesizes[nodesizes.Count()-1].faces);
|
|
maxFaces = float(sqrt(maxFaces));
|
|
|
|
// Reset LOD controllers
|
|
int ct = nodes.Count();
|
|
float prev = 0.0f;
|
|
for (i=0; i<ct; i++) {
|
|
LODCtrl *cont = (LODCtrl*)nodes[i]->GetVisController();
|
|
if (!cont) continue;
|
|
cont->order = i;
|
|
cont->min = prev;
|
|
cont->max = (float)sqrt(float(nodesizes[i].faces))/maxFaces * size;
|
|
prev = cont->max;
|
|
}
|
|
SetBlendRegions();
|
|
SetStates();
|
|
}
|
|
|
|
void LODUtil::SetBlendRegions()
|
|
{
|
|
int ct = nodes.Count();
|
|
for (int i=0; i<ct; i++) {
|
|
LODCtrl *cont = (LODCtrl*)nodes[i]->GetVisController();
|
|
LODCtrl *cprev = NULL;
|
|
LODCtrl *cnext = NULL;
|
|
if (!cont) continue;
|
|
|
|
if (i) cprev = (LODCtrl*)nodes[i-1]->GetVisController();
|
|
if (i<ct-1) cnext = (LODCtrl*)nodes[i+1]->GetVisController();
|
|
|
|
if (cprev) {
|
|
cont->bmin = cont->min - (cprev->max-cprev->min)*BLEND_PERCENT;
|
|
} else {
|
|
cont->bmin = 0.0f;
|
|
}
|
|
if (cnext) {
|
|
cont->bmax = cont->max + (cnext->max-cnext->min)*BLEND_PERCENT;
|
|
} else {
|
|
cont->bmax = cont->max + (cont->max-cont->min)*BLEND_PERCENT;
|
|
}
|
|
}
|
|
}
|
|
|
|
void LODUtil::SetViewportObj(BOOL onOff)
|
|
{
|
|
int sel = SendDlgItemMessage(hWnd,IDC_LOD_LIST,LB_GETCURSEL,0,0);
|
|
if (sel==LB_ERR) return;
|
|
LODCtrl *cont = (LODCtrl*)nodes[sel]->GetVisController();
|
|
if (!cont) return;
|
|
|
|
for (int i=0; i<nodes.Count(); i++) {
|
|
LODCtrl *cnt = (LODCtrl*)nodes[i]->GetVisController();
|
|
if (!cnt) continue;
|
|
cnt->viewport = FALSE;
|
|
cnt->NotifyDependents(FOREVER,PART_HIDESTATE,REFMSG_CHANGE);
|
|
cnt->NotifyDependents(FOREVER,0,REFMSG_SUBANIM_STRUCTURE_CHANGED);
|
|
}
|
|
cont->viewport = TRUE;
|
|
cont->NotifyDependents(FOREVER,PART_HIDESTATE,REFMSG_CHANGE);
|
|
|
|
ListSelChanged();
|
|
ip->RedrawViews(ip->GetTime());
|
|
}
|
|
|
|
void LODUtil::DeleteFromSet()
|
|
{
|
|
int sel = SendDlgItemMessage(hWnd,IDC_LOD_LIST,LB_GETCURSEL,0,0);
|
|
if (sel==LB_ERR) return;
|
|
|
|
// If this was the viewport object, pick another viewport object
|
|
LODCtrl *cont = (LODCtrl*)nodes[sel]->GetVisController();
|
|
if (cont && cont->viewport) {
|
|
int next = sel+1;
|
|
if (next>=nodes.Count()) next = 0;
|
|
LODCtrl *cnt = (LODCtrl*)nodes[next]->GetVisController();
|
|
if (cnt) {
|
|
cnt->viewport = TRUE;
|
|
cnt->NotifyDependents(FOREVER,PART_ALL,REFMSG_CHANGE);
|
|
cnt->NotifyDependents(FOREVER,PART_ALL,REFMSG_SUBANIM_STRUCTURE_CHANGED);
|
|
}
|
|
}
|
|
nodes[sel]->SetVisController(NULL);
|
|
SetStates();
|
|
ip->RedrawViews(ip->GetTime());
|
|
}
|
|
|
|
void LODUtil::SetMin(float m)
|
|
{
|
|
int sel = SendDlgItemMessage(hWnd,IDC_LOD_LIST,LB_GETCURSEL,0,0);
|
|
if (sel==LB_ERR) return;
|
|
LODCtrl *cont = (LODCtrl*)nodes[sel]->GetVisController();
|
|
if (!cont) return;
|
|
|
|
if (disp==IDC_LOD_DISPPERCENT)
|
|
m = ConvertFromPercent(m);
|
|
cont->min = m;
|
|
|
|
// Propogate down
|
|
float min = m;
|
|
for (int i=sel-1; i>=0; i--) {
|
|
LODCtrl *cnt = (LODCtrl*)nodes[i]->GetVisController();
|
|
if (!cnt) continue;
|
|
cnt->max = min;
|
|
if (cnt->min > cnt->max) cnt->min = cnt->max;
|
|
min = cnt->min;
|
|
}
|
|
|
|
// Propogate up
|
|
if (cont->max < cont->min) {
|
|
cont->max = cont->min;
|
|
float max = cont->min;
|
|
for (int i=sel+1; i<nodes.Count(); i++) {
|
|
LODCtrl *cnt = (LODCtrl*)nodes[i]->GetVisController();
|
|
if (!cnt) continue;
|
|
cnt->min = max;
|
|
if (cnt->max < cnt->min) cnt->max = cnt->min;
|
|
max = cnt->max;
|
|
}
|
|
}
|
|
|
|
SetBlendRegions();
|
|
SetupList();
|
|
ListSelChanged();
|
|
}
|
|
|
|
void LODUtil::SetMax(float m)
|
|
{
|
|
int sel = SendDlgItemMessage(hWnd,IDC_LOD_LIST,LB_GETCURSEL,0,0);
|
|
if (sel==LB_ERR) return;
|
|
LODCtrl *cont = (LODCtrl*)nodes[sel]->GetVisController();
|
|
if (!cont) return;
|
|
|
|
if (disp==IDC_LOD_DISPPERCENT)
|
|
m = ConvertFromPercent(m);
|
|
cont->max = m;
|
|
|
|
// Propogate up
|
|
float max = m;
|
|
for (int i=sel+1; i<nodes.Count(); i++) {
|
|
LODCtrl *cnt = (LODCtrl*)nodes[i]->GetVisController();
|
|
if (!cnt) continue;
|
|
cnt->min = max;
|
|
if (cnt->max < cnt->min) cnt->max = cnt->min;
|
|
max = cnt->max;
|
|
}
|
|
|
|
// Propogate down
|
|
if (cont->min > cont->max) {
|
|
cont->min = cont->max;
|
|
float min = cont->min;
|
|
for (int i=sel-1; i>=0; i--) {
|
|
LODCtrl *cnt = (LODCtrl*)nodes[i]->GetVisController();
|
|
if (!cnt) continue;
|
|
cnt->max = min;
|
|
if (cnt->min > cnt->max) cnt->min = cnt->max;
|
|
min = cnt->min;
|
|
}
|
|
}
|
|
|
|
SetBlendRegions();
|
|
SetupList();
|
|
ListSelChanged();
|
|
}
|
|
|
|
BOOL LODUtil::HitTest(
|
|
IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags)
|
|
{
|
|
return ip->PickNode(hWnd,m,this) ? TRUE : FALSE;
|
|
}
|
|
|
|
BOOL LODUtil::Pick(IObjParam *ip,ViewExp *vpt)
|
|
{
|
|
INode *node = vpt->GetClosestHit();
|
|
if (node) {
|
|
// Compute all the sizes for existing set members
|
|
Tab<NodeSize> nodesizes;
|
|
for (int i=0; i<nodes.Count(); i++) {
|
|
NodeSize ns(nodes[i],ip->GetTime());
|
|
nodesizes.Append(1,&ns,10);
|
|
}
|
|
|
|
// Compute the size for this node
|
|
NodeSize ns(node,ip->GetTime());
|
|
|
|
// Make a new LOD controller
|
|
LODCtrl *cnt = (LODCtrl*)nodes[0]->GetVisController();
|
|
if (!cnt) return FALSE;
|
|
LODCtrl *cont = new LODCtrl;
|
|
cont->grpID = cnt->grpID;
|
|
cont->viewport = FALSE;
|
|
|
|
// Find its location in the list
|
|
BOOL found = FALSE;
|
|
for (i=0; i<nodesizes.Count(); i++) {
|
|
if (ns.faces<nodesizes[i].faces) {
|
|
cont->order = i;
|
|
found = TRUE;
|
|
}
|
|
if (found) {
|
|
LODCtrl *cnt = (LODCtrl*)nodes[i]->GetVisController();
|
|
if (!cnt) return FALSE;
|
|
cnt->order++;
|
|
}
|
|
}
|
|
if (!found) {
|
|
cont->order = i;
|
|
}
|
|
|
|
// Assign the new controller
|
|
node->SetVisController(cont);
|
|
|
|
// Redraw
|
|
ip->RedrawViews(ip->GetTime());
|
|
SetStates();
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void LODUtil::EnterMode(IObjParam *ip)
|
|
{
|
|
ICustButton *but = GetICustButton(GetDlgItem(hWnd,IDC_LOD_ADDTOSET));
|
|
but->SetCheck(TRUE);
|
|
ReleaseICustButton(but);
|
|
}
|
|
|
|
void LODUtil::ExitMode(IObjParam *ip)
|
|
{
|
|
ICustButton *but = GetICustButton(GetDlgItem(hWnd,IDC_LOD_ADDTOSET));
|
|
but->SetCheck(FALSE);
|
|
ReleaseICustButton(but);
|
|
}
|
|
|
|
BOOL LODUtil::Filter(INode *node)
|
|
{
|
|
for (int i=0; i<nodes.Count(); i++) {
|
|
if (nodes[i]==node) return FALSE;
|
|
}
|
|
INode *head = FindSelGroupHead();
|
|
if (node->GetParentNode()!=head) return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL CALLBACK LODUtilDlgProc(
|
|
HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (msg) {
|
|
case WM_INITDIALOG:
|
|
theLODUtil.hWnd = hWnd;
|
|
theLODUtil.InitDlg();
|
|
theLODUtil.SetStates();
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
break;
|
|
|
|
case CC_SPINNER_CHANGE: {
|
|
ISpinnerControl *spin = (ISpinnerControl*)lParam;
|
|
switch (LOWORD(wParam)) {
|
|
case IDC_LOD_VIEWWIDTHSPIN:
|
|
case IDC_LOD_VIEWHEIGHTSPIN:
|
|
theLODUtil.SetDisplayMode(theLODUtil.disp);
|
|
break;
|
|
|
|
case IDC_LOD_MINSPIN:
|
|
theLODUtil.SetMin(spin->GetFVal());
|
|
break;
|
|
case IDC_LOD_MAXSPIN:
|
|
theLODUtil.SetMax(spin->GetFVal());
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case IDC_LOD_DISPPERCENT:
|
|
case IDC_LOD_DISPPIXELS:
|
|
theLODUtil.SetDisplayMode(LOWORD(wParam));
|
|
break;
|
|
|
|
case IDC_LOD_LIST:
|
|
if (HIWORD(wParam)==LBN_SELCHANGE) {
|
|
theLODUtil.ListSelChanged();
|
|
} else
|
|
if (HIWORD(wParam)==LBN_DBLCLK) {
|
|
theLODUtil.SetViewportObj(IsDlgButtonChecked(hWnd,IDC_LOD_VIEWPORTOBJ));
|
|
}
|
|
break;
|
|
|
|
case IDC_LOD_CREATENEWSET:
|
|
theLODUtil.CreateNewSet();
|
|
break;
|
|
|
|
case IDC_LOD_DELETEFROMSET:
|
|
theLODUtil.DeleteFromSet();
|
|
break;
|
|
|
|
case IDC_LOD_ADDTOSET:
|
|
theLODUtil.ip->SetPickMode(&theLODUtil);
|
|
break;
|
|
|
|
case IDC_LOD_VIEWPORTOBJ:
|
|
theLODUtil.SetViewportObj(IsDlgButtonChecked(hWnd,IDC_LOD_VIEWPORTOBJ));
|
|
break;
|
|
|
|
case IDC_LOD_RESET:
|
|
theLODUtil.ResetSet();
|
|
break;
|
|
|
|
case IDC_LOD_RESETOUTPUT:
|
|
theLODUtil.ResetOutput();
|
|
break;
|
|
|
|
case IDOK:
|
|
theLODUtil.iu->CloseUtility();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
case WM_LBUTTONUP:
|
|
case WM_MOUSEMOVE:
|
|
theLODUtil.ip->RollupMouseMessage(hWnd,msg,wParam,lParam);
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|