1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // Strauss Shader plug-in, implementation
4 //
5 // Created: 10/26/98 Kells Elmquist
6 //
7 #include "shadersPch.h"
8 #include "shadersRc.h"
9 #include "gport.h"
10 #include "shaders.h"
11 #include "shaderUtil.h"
12 #include "macrorec.h"
13 #include "toneop.h"
14
15 #include "3dsmaxport.h"
16
17 // Class Ids
18 #define STRAUSS_SHADER_CLASS_ID 0x2857f450
19
20 static Class_ID StraussShaderClassID( STRAUSS_SHADER_CLASS_ID, 0);
21
22 // paramblock2 block and parameter IDs.
23 enum { strauss_params, };
24 // shdr_params param IDs
25 enum
26 {
27 st_diffuse, st_glossiness, st_metalness,
28 };
29
30 /////////////////////////////////////////////////////////////////////
31 //
32 // Basic Panel UI
33 //
34 #define NMBUTS 4
35
36 // tex channel number to button IDC
37 static int texMButtonsIDC[] = {
38 IDC_MAPON_CLR, IDC_MAPON_GL, IDC_MAPON_MT, IDC_MAPON_TR,
39 };
40
41 // This array gives the texture map number for given MButton number
42 static int texmapFromMBut[] = { 0, 1, 2, 3 };
43
44 // Texture Maps
45 #define STRAUSS_NTEXMAPS 5
46
47 // channels ids used by shader
48 #define S_DI 0
49 #define S_GL 1
50 #define S_MT 2
51 #define S_TR 3
52
53 // channel names
54 static int texNameIDS[STD2_NMAX_TEXMAPS] = {
55 IDS_KE_COLOR, IDS_KE_GLOSSINESS, IDS_KE_METALNESS, IDS_DS_TRANS,
56 IDS_KE_REFR_FILTER, IDS_KE_NONE, IDS_KE_NONE, IDS_KE_NONE,
57 IDS_KE_NONE, IDS_KE_NONE, IDS_KE_NONE, IDS_KE_NONE,
58 IDS_KE_NONE, IDS_KE_NONE, IDS_KE_NONE, IDS_KE_NONE,
59 IDS_KE_NONE, IDS_KE_NONE, IDS_KE_NONE, IDS_KE_NONE,
60 IDS_KE_NONE, IDS_KE_NONE, IDS_KE_NONE, IDS_KE_NONE,
61 };
62
63 // internal non-local parsable channel map names
64 static TCHAR* texInternalNames[STD2_NMAX_TEXMAPS] = {
65 _T("diffuseMap"),_T("glossinessMap"), _T("metalnessMap"), _T("opacityMap"),
66 _T("filterMap"), _T(""), _T(""), _T(""),
67 _T(""), _T(""), _T(""), _T(""),
68 _T(""), _T(""), _T(""), _T(""),
69 _T(""), _T(""), _T(""), _T(""),
70 _T(""), _T(""), _T(""), _T(""),
71 };
72
73 // sized for nmax textures
74 // bump, reflection & refraction maps are ignored....done after shading
75 static int chanType[STD2_NMAX_TEXMAPS] = {
76 CLR_CHANNEL, MONO_CHANNEL, MONO_CHANNEL, MONO_CHANNEL,
77 CLR_CHANNEL, UNSUPPORTED_CHANNEL, UNSUPPORTED_CHANNEL,
78 UNSUPPORTED_CHANNEL, UNSUPPORTED_CHANNEL, UNSUPPORTED_CHANNEL,
79 UNSUPPORTED_CHANNEL, UNSUPPORTED_CHANNEL, UNSUPPORTED_CHANNEL,
80 UNSUPPORTED_CHANNEL, UNSUPPORTED_CHANNEL, UNSUPPORTED_CHANNEL,
81 UNSUPPORTED_CHANNEL, UNSUPPORTED_CHANNEL, UNSUPPORTED_CHANNEL,
82 };
83
84 // what channel corresponds to the stdMat ID's
85 static int stdIDToChannel[N_ID_CHANNELS] = { -1, 0, -1, 1, -1, -1, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1 };
86
87
88 //////////////////////////////////////////////////////////////////////////////////////////
89 //
90 // Strauss Parameter Block
91 //
92 #define CURRENT_STRAUSS_SHADER_VERSION 2
93 #define STRAUSS_SHADER_NPARAMS 4
94 #define STRAUSS_SHADER_PB_VERSION 1
95
96 #define STRAUSS_NCOLBOX 1
97 static int colID[STRAUSS_NCOLBOX] = { IDC_STD_COLOR2 };
98
99 //Current Param Block Descriptor
100 static ParamBlockDescID StraussShaderPB[ STRAUSS_SHADER_NPARAMS ] = {
101 { TYPE_RGBA, NULL, TRUE,st_diffuse }, // diffuse
102 { TYPE_FLOAT, NULL, TRUE,-1 }, // ambient level
103 { TYPE_FLOAT, NULL, TRUE,st_glossiness }, // glossiness
104 { TYPE_FLOAT, NULL, TRUE,st_metalness }, // metalness
105 };
106
107 #define STRAUSS_NUMOLDVER 1
108
109 static ParamVersionDesc oldVersions[STRAUSS_NUMOLDVER] = {
110 ParamVersionDesc(StraussShaderPB, STRAUSS_SHADER_NPARAMS, 0),
111 };
112
113
114 //----------------------------------------------------------------------------------------
115 // Straus Shader, from CGA Nov 1990, "Realistic lighting model for computer animators"
116 //----------------------------------------------------------------------------------------
117 // note: We replace std opacity & std reflection, these are automatic in the strauss model
118 #define STRAUSS_PARAMS (STD_PARAM_DIFFUSE_CLR+STD_PARAM_GLOSSINESS\
119 +STD_PARAM_SPECULAR_LEV+STD_EXTRA_DLG+STD_EXTRA_REFRACTION)
120
121 class StraussShaderDlg;
122
123 class StraussShader : public ExposureMaterialControlImp<StraussShader,
124 AddExposureMaterialControl<Shader> > {
125 friend class StraussShaderCB;
126 friend class StraussShaderDlg;
127 friend class ExposureMaterialControlImp<StraussShader,
128 AddExposureMaterialControl<Shader> >;
129 BOOL rolloutOpen;
130 protected:
131 IParamBlock2 *pblock; // ref 0
132 Interval ivalid;
133
134 StraussShaderDlg* paramDlg;
135
136 Color diffuse;
137 float glossiness;
138 float metalness;
139
140 static CombineComponentsFPDesc msExpMtlControlDesc;
141
142 public:
143 StraussShader();
DeleteThis()144 void DeleteThis(){ delete this; }
SupportStdParams()145 ULONG SupportStdParams(){ return STRAUSS_PARAMS; }
146
147 // copy std params, for switching shaders
148 void CopyStdParams( Shader* pFrom );
149
150 // texture maps
nTexChannelsSupported()151 long nTexChannelsSupported(){ return STRAUSS_NTEXMAPS; }
GetTexChannelName(long nChan)152 TSTR GetTexChannelName( long nChan ){ return GetString( texNameIDS[ nChan ] ); }
GetTexChannelInternalName(long nChan)153 TSTR GetTexChannelInternalName( long nChan ) { return texInternalNames[ nChan ]; }
ChannelType(long nChan)154 long ChannelType( long nChan ) { return chanType[nChan]; }
StdIDToChannel(long stdID)155 long StdIDToChannel( long stdID ){ return stdIDToChannel[stdID]; }
156
KeyAtTime(int id,TimeValue t)157 BOOL KeyAtTime(int id,TimeValue t) { return pblock->KeyFrameAtTime(id,t); }
GetRequirements(int subMtlNum)158 ULONG GetRequirements( int subMtlNum ){ return isNoExposure() | MTLREQ_PHONG; }
159
160 ShaderParamDlg* CreateParamDialog(HWND hOldRollup, HWND hwMtlEdit, IMtlParams *imp, StdMat2* theMtl, int rollupOpen, int );
GetParamDlg(int)161 ShaderParamDlg* GetParamDlg(int){ return (ShaderParamDlg*)paramDlg; }
SetParamDlg(ShaderParamDlg * newDlg,int)162 void SetParamDlg( ShaderParamDlg* newDlg, int ){ paramDlg = (StraussShaderDlg*)newDlg; }
163
ClassID()164 Class_ID ClassID() { return StraussShaderClassID; }
SuperClassID()165 SClass_ID SuperClassID() { return SHADER_CLASS_ID; }
GetName()166 TSTR GetName() { return GetString( IDS_KE_STRAUSS ); }
GetClassName(TSTR & s)167 void GetClassName(TSTR& s) { s = GetName(); }
168
NumSubs()169 int NumSubs() { return 1; }
SubAnim(int i)170 Animatable* SubAnim(int i){ return (i==0)? pblock : NULL; }
SubAnimName(int i)171 TSTR SubAnimName(int i){ return TSTR(GetString( IDS_KE_STRAUSS_PARMS )); };
SubNumToRefNum(int subNum)172 int SubNumToRefNum(int subNum) { return subNum; }
173
174 // add direct ParamBlock2 access
NumParamBlocks()175 int NumParamBlocks() { return 1; }
GetParamBlock(int i)176 IParamBlock2* GetParamBlock(int i) { return pblock; }
GetParamBlockByID(BlockID id)177 IParamBlock2* GetParamBlockByID(BlockID id) { return (pblock->ID() == id) ? pblock : NULL; }
178
NumRefs()179 int NumRefs() { return 1; }
GetReference(int i)180 RefTargetHandle GetReference(int i){ return (i==0)? pblock : NULL; }
SetReference(int i,RefTargetHandle rtarg)181 void SetReference(int i, RefTargetHandle rtarg)
182 { if (i==0) pblock = (IParamBlock2*)rtarg; else assert(0); }
NotifyChanged()183 void NotifyChanged(){ NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); }
184
185 void Update(TimeValue t, Interval& valid);
186 void Reset();
187 RefTargetHandle Clone( RemapDir &remap=DefaultRemapDir() );
188 RefResult NotifyRefChanged( Interval changeInt, RefTargetHandle hTarget,
189 PartID& partID, RefMessage message );
190 // IO
191 IOResult Save(ISave *isave);
192 IOResult Load(ILoad *iload);
193
194 void GetIllumParams( ShadeContext &sc, IllumParams& ip );
195
196 // Strauss Shader specific section
197 void Illum(ShadeContext &sc, IllumParams &ip);
198 float EvalHiliteCurve(float x);
199
200 void AffectReflection(ShadeContext &sc, IllumParams &ip, Color &rcol);
201 void CombineComponents( ShadeContext &sc, IllumParams& ip );
202
SetGlossiness(float v,TimeValue t)203 void SetGlossiness(float v, TimeValue t){ glossiness= v; pblock->SetValue( st_glossiness, t, v); }
GetGlossiness(int mtlNum=0,BOOL backFace=FALSE)204 float GetGlossiness(int mtlNum=0, BOOL backFace=FALSE){ return glossiness; };
GetGlossiness(TimeValue t)205 float GetGlossiness( TimeValue t){return pblock->GetFloat(st_glossiness,t); }
206
SetMetalness(float v,TimeValue t)207 void SetMetalness(float v, TimeValue t){ metalness = v; pblock->SetValue( st_metalness, t, v); }
GetMetalness(int mtlNum=0,BOOL backFace=FALSE)208 float GetMetalness(int mtlNum=0, BOOL backFace=FALSE){ return metalness; };
GetMetalness(TimeValue t)209 float GetMetalness( TimeValue t){return pblock->GetFloat(st_metalness,t); }
210
211 // Std Params
SetDiffuseClr(Color c,TimeValue t)212 void SetDiffuseClr(Color c, TimeValue t)
213 { diffuse = c; pblock->SetValue( st_diffuse, t, c); }
214
GetDiffuseClr(int mtlNum=0,BOOL backFace=FALSE)215 Color GetDiffuseClr(int mtlNum=0, BOOL backFace=FALSE){ return diffuse;}
GetDiffuseClr(TimeValue t)216 Color GetDiffuseClr(TimeValue t){ return pblock->GetColor(st_diffuse,t); }
217
218 // std params not supported
SetLockDS(BOOL lock)219 void SetLockDS(BOOL lock){ }
GetLockDS()220 BOOL GetLockDS(){ return FALSE; }
SetLockAD(BOOL lock)221 void SetLockAD(BOOL lock){ }
GetLockAD()222 BOOL GetLockAD(){ return FALSE; }
SetLockADTex(BOOL lock)223 void SetLockADTex(BOOL lock){ }
GetLockADTex()224 BOOL GetLockADTex(){ return FALSE; }
SetAmbientClr(Color c,TimeValue t)225 void SetAmbientClr(Color c, TimeValue t){}
GetAmbientClr(int mtlNum=0,BOOL backFace=FALSE)226 Color GetAmbientClr(int mtlNum=0, BOOL backFace=FALSE){ return diffuse * 0.5f;}
GetAmbientClr(TimeValue t)227 Color GetAmbientClr(TimeValue t){ return diffuse * 0.5f; }
228
SetSpecularClr(Color c,TimeValue t)229 void SetSpecularClr(Color c, TimeValue t){}
SetSpecularLevel(float v,TimeValue t)230 void SetSpecularLevel(float v, TimeValue t){}
GetSpecularClr(int mtlNum=0,BOOL backFace=FALSE)231 Color GetSpecularClr(int mtlNum=0, BOOL backFace=FALSE){ return Color(0.9f, 0.9f,0.9f); };
232 float GetSpecularLevel(TimeValue t);
GetSpecularLevel(int mtlNum,BOOL backFace)233 float GetSpecularLevel(int mtlNum, BOOL backFace){ return GetSpecularLevel(0); };
GetSpecularClr(TimeValue t)234 Color GetSpecularClr(TimeValue t){ return Color(0.9f,0.9f,0.9f);}
SetSelfIllum(float v,TimeValue t)235 void SetSelfIllum(float v, TimeValue t) {}
GetSelfIllum(int mtlNum=0,BOOL backFace=FALSE)236 float GetSelfIllum(int mtlNum=0, BOOL backFace=FALSE){ return 0.0f; };
SetSelfIllumClrOn(BOOL on)237 void SetSelfIllumClrOn( BOOL on ){};
IsSelfIllumClrOn()238 BOOL IsSelfIllumClrOn(){ return TRUE; };
IsSelfIllumClrOn(int mtlNum,BOOL backFace)239 BOOL IsSelfIllumClrOn(int mtlNum, BOOL backFace){ return TRUE; }
SetSelfIllumClr(Color c,TimeValue t)240 void SetSelfIllumClr(Color c, TimeValue t){}
GetSelfIllumClr(int mtlNum=0,BOOL backFace=FALSE)241 Color GetSelfIllumClr(int mtlNum=0, BOOL backFace=FALSE){ return Color(0,0,0); }
SetSoftenLevel(float v,TimeValue t)242 void SetSoftenLevel(float v, TimeValue t) {}
GetSoftenLevel(int mtlNum=0,BOOL backFace=FALSE)243 float GetSoftenLevel(int mtlNum=0, BOOL backFace=FALSE){ return DEFAULT_SOFTEN; }
GetSoftenLevel(TimeValue t)244 float GetSoftenLevel(TimeValue t){ return DEFAULT_SOFTEN; }
GetSelfIllum(TimeValue t)245 float GetSelfIllum(TimeValue t){ return 0.0f;}
GetSelfIllumClr(TimeValue t)246 Color GetSelfIllumClr(TimeValue t){ return Color(0,0,0);}
247
SetPanelOpen(BOOL open)248 void SetPanelOpen(BOOL open){rolloutOpen = open;}
249
250
251 };
252
253 ///////////// Class Descriptor ////////////////////////
254 class StraussShaderClassDesc:public ClassDesc2 {
255 public:
IsPublic()256 int IsPublic() { return 1; }
Create(BOOL loading)257 void * Create(BOOL loading) { return new StraussShader(); }
ClassName()258 const TCHAR * ClassName() { return GetString(IDS_KE_STRAUSS); }
SuperClassID()259 SClass_ID SuperClassID() { return SHADER_CLASS_ID; }
ClassID()260 Class_ID ClassID() { return StraussShaderClassID; }
Category()261 const TCHAR* Category() { return _T(""); }
InternalName()262 const TCHAR* InternalName() { return _T("Strauss"); } // returns fixed parsable name (scripter-visible name)
HInstance()263 HINSTANCE HInstance() { return hInstance; } // returns owning module handle
264 };
265
266 StraussShaderClassDesc StraussCD;
GetStraussShaderCD()267 ClassDesc * GetStraussShaderCD(){ return &StraussCD; }
268
269 // shader parameters
270 static ParamBlockDesc2 strauss_param_blk ( strauss_params, _T("shaderParameters"), 0, &StraussCD, P_AUTO_CONSTRUCT, 0,
271 // params
272 st_diffuse, _T("diffuse"), TYPE_RGBA, P_ANIMATABLE, IDS_DS_DIFFUSE,
273 p_default, Color(0.5f, 0.5f, 0.5f),
274 end,
275 st_glossiness, _T("glossiness"), TYPE_PCNT_FRAC, P_ANIMATABLE, IDS_KE_GLOSSINESS,
276 p_default, 0.0,
277 p_range, 0.0, 100.0,
278 end,
279 st_metalness, _T("metalness"), TYPE_PCNT_FRAC, P_ANIMATABLE, IDS_KE_METALNESS,
280 p_default, 0.0,
281 p_range, 0.0, 100.0,
282 end,
283 end
284 );
285
286
287 CombineComponentsFPDesc StraussShader::msExpMtlControlDesc(StraussCD);
288
StraussShader()289 StraussShader::StraussShader()
290 {
291 pblock = NULL;
292 StraussCD.MakeAutoParamBlocks(this); // make and intialize paramblock2
293 paramDlg = NULL;
294 diffuse = Color(0.0f,0.0f,0.0f);
295 glossiness = metalness = 0.0f;
296 ivalid.SetEmpty();
297 rolloutOpen = TRUE;
298
299 }
300
CopyStdParams(Shader * pFrom)301 void StraussShader::CopyStdParams( Shader* pFrom )
302 {
303 macroRecorder->Disable();
304 // don't want to see this parameter copying in macrorecorder
305
306 SetAmbientClr( pFrom->GetAmbientClr(0,0), 0 );
307 SetDiffuseClr( pFrom->GetDiffuseClr(0,0), 0 );
308 SetGlossiness( pFrom->GetGlossiness(0,0), 0 );
309
310 macroRecorder->Enable();
311 ivalid.SetEmpty();
312 }
313
314
Clone(RemapDir & remap)315 RefTargetHandle StraussShader::Clone( RemapDir &remap )
316 {
317 StraussShader* mnew = new StraussShader();
318 mnew->ExposureMaterialControl::operator=(*this);
319 mnew->ReplaceReference(0, remap.CloneRef(pblock));
320 mnew->ivalid.SetEmpty();
321 mnew->diffuse = diffuse;
322 mnew->glossiness = glossiness;
323 mnew->metalness = metalness;
324 BaseClone(this, mnew, remap);
325 return (RefTargetHandle)mnew;
326 }
327
GetIllumParams(ShadeContext & sc,IllumParams & ip)328 void StraussShader::GetIllumParams( ShadeContext &sc, IllumParams& ip )
329 {
330 ip.stdParams = SupportStdParams();
331 ip.channels[S_DI] = diffuse;
332 ip.channels[S_GL].r = glossiness;
333 ip.channels[S_MT].r = metalness;
334 }
335
336
337
Update(TimeValue t,Interval & valid)338 void StraussShader::Update(TimeValue t, Interval &valid) {
339 Point3 p;
340 if (!ivalid.InInterval(t)) {
341 ivalid.SetInfinite();
342
343 pblock->GetValue( st_diffuse, t, p, ivalid );
344 diffuse= Bound(Color(p.x,p.y,p.z));
345 pblock->GetValue( st_glossiness, t, glossiness, ivalid );
346 glossiness = Bound(glossiness );
347 pblock->GetValue( st_metalness, t, metalness, ivalid );
348 metalness = Bound(metalness );
349 }
350 valid &= ivalid;
351 }
352
Reset()353 void StraussShader::Reset()
354 {
355 // StraussCD.MakeAutoParamBlocks(this); // make and intialize paramblock2
356
357 ivalid.SetEmpty();
358 SetDiffuseClr( Color(0.5f,0.5f,0.5f), 0 );
359 SetGlossiness( 0.2f,0);
360 SetMetalness( 0.0f,0);
361 }
362
363
364 //////////////////////////////////////////////////////////////////////////////////////////
365 //
366 // IO Routines
367 //
368 #define SHADER_HDR_CHUNK 0x4000
369 #define SHADER_MAPSON_CHUNK 0x5004
370 #define SHADER_VERS_CHUNK 0x5300
371 #define SHADER_DIMDIFFON_CHUNK 0x5400
372 #define SHADER_EXPOSURE_MATERIAL_CONTROL_CHUNK 0x5020
373
374 // IO
Save(ISave * isave)375 IOResult StraussShader::Save(ISave *isave)
376 {
377 ULONG nb;
378
379 isave->BeginChunk(SHADER_VERS_CHUNK);
380 int version = CURRENT_STRAUSS_SHADER_VERSION;
381 isave->Write(&version,sizeof(version),&nb);
382 isave->EndChunk();
383
384 isave->BeginChunk(SHADER_EXPOSURE_MATERIAL_CONTROL_CHUNK);
385 ExposureMaterialControl::Save(isave);
386 isave->EndChunk();
387 return IO_OK;
388 }
389
390 class StraussShaderCB: public PostLoadCallback {
391 public:
392 StraussShader *s;
393 int loadVersion;
StraussShaderCB(StraussShader * newS,int loadVers)394 StraussShaderCB(StraussShader *newS, int loadVers) { s = newS; loadVersion = loadVers; }
proc(ILoad * iload)395 void proc(ILoad *iload)
396 {
397 s->ReplaceReference(0,
398 UpdateParameterBlock2(StraussShaderPB, STRAUSS_SHADER_NPARAMS, (IParamBlock*)s->pblock, &strauss_param_blk));
399 }
400 };
401
402
Load(ILoad * iload)403 IOResult StraussShader::Load(ILoad *iload) {
404 ULONG nb;
405 int id;
406 int version = 0;
407
408 IOResult res;
409 while (IO_OK==(res=iload->OpenChunk())) {
410 switch(id = iload->CurChunkID()) {
411 case SHADER_VERS_CHUNK:
412 res = iload->Read(&version,sizeof(version), &nb);
413 break;
414 case SHADER_DIMDIFFON_CHUNK:
415 BOOL dim;
416 res = iload->Read(&dim,sizeof(dim), &nb);
417 break;
418 case SHADER_EXPOSURE_MATERIAL_CONTROL_CHUNK:
419 res = ExposureMaterialControl::Load(iload);
420 break;
421 }
422 iload->CloseChunk();
423 if (res!=IO_OK)
424 return res;
425 }
426 if (version < CURRENT_STRAUSS_SHADER_VERSION ) {
427 iload->RegisterPostLoadCallback(new StraussShaderCB(this, version));
428 iload->SetObsolete();
429 }
430 return IO_OK;
431 }
432
433
434 ///////////////////////////////////////////////////////////////////////////////////////////
435 // The Shader
436 //
437
438 // my magic constants
439 static float SpecBoost = 1.3f;
440
441 // Strauss's Magic Constants
442 static float kf = 1.12f;
443 static float kf2 = 1.0f / (kf * kf);
444 static float kf3 = 1.0f / ((1.0f - kf) * (1.0f - kf));
445 static float kg = 1.01f;
446 static float kg2 = 1.0f / (kg * kg);
447 static float kg3 = 1.0f / ((1.0f - kg) * (1.0f - kg));
448 static float kj = 0.1f; //.1 strauss
449
450 static float OneOverHalfPi = 1.0f / (0.5f * Pi);
451
F(float x)452 inline float F( float x ){
453 float xb = Bound( x );
454 float xkf = 1.0f / ((xb - kf)*(xb - kf));
455 return (xkf - kf2) / (kf3 - kf2);
456 }
457
G(float x)458 inline float G( float x ){
459 float xb = Bound( x );
460 float xkg = 1.0f / ((xb - kg)*(xb - kg));
461 return (kg3 - xkg) / (kg3 - kg2);
462 }
463
464 #define REFL_BRIGHTNESS_ADJUST 3.0f;
465
AffectReflection(ShadeContext & sc,IllumParams & ip,Color & rClr)466 void StraussShader::AffectReflection(ShadeContext &sc, IllumParams &ip, Color &rClr )
467 {
468 float opac = ip.channels[ S_TR ].r;
469 float g = ip.channels[ S_GL ].r;
470 float m = ip.channels[ S_MT ].r;
471 Color Cd = ip.channels[ S_DI ];
472
473 float rn = opac - (1.0f - g * g * g) * opac;
474
475 // the reflection of the reflection vector is just the view vector
476 // so dot(v, r) is 1, to any power is still 1
477 float a, b;
478 // NB: this has been transformed for existing in-pointing v
479 float NV = Dot( sc.V(), sc.Normal() );
480 Point3 R = sc.V() - 2.0f * NV * sc.Normal();
481 float NR = Dot( sc.Normal(), R );
482 a = (float)acos( NR ) * OneOverHalfPi;
483 b = (float)acos( NV ) * OneOverHalfPi;
484
485 float fa = F( a );
486 float j = fa * G( a ) * G( b );
487 float rj = Bound( rn + (rn+kj)*j );
488 Color white( 1.0f, 1.0f, 1.0f );
489
490 Color Cs = white + m * (1.0f - fa) * (Cd - white);
491 rClr *= Cs * rj * REFL_BRIGHTNESS_ADJUST;
492 }
493
494 static int stopX = -1;
495 static int stopY = -1;
496
497 static float greyVal = 0.3f;
498 static float clrVal = 0.3f;
499
500 static float softThresh = 0.15f;
501
Illum(ShadeContext & sc,IllumParams & ip)502 void StraussShader::Illum(ShadeContext &sc, IllumParams &ip)
503 {
504 LightDesc *l;
505 Color lightClr;
506
507 #ifdef _DEBUG
508 IPoint2 sp = sc.ScreenCoord();
509 if ( sp.x == stopX && sp.y == stopY )
510 sp.x = stopX;
511 #endif
512
513 float opac = ip.channels[ S_TR ].r;
514 float g = ip.channels[ S_GL ].r;
515 float m = ip.channels[ S_MT ].r;
516 Color Cd = ip.channels[ S_DI ];
517 // BOOL dimDiffuse = ip.hasComponents & HAS_REFLECT;
518 BOOL dimDiffuse = ip.hasComponents & HAS_REFLECT_MAP;
519
520 float rd;
521 float g3 = Cube( g );
522 if ( dimDiffuse )
523 rd = (1.0f - g3) * opac;
524 else
525 rd = (1.0f - m * g3) * opac; //ke 10/28/98
526
527 float rn = opac - (1.0f - g3) * opac;
528
529 float h = (g == 1.0f ) ? 600.0f : 3.0f / (1.0f - g );
530 float d = 1.0f - m * g;
531
532 for (int i=0; i<sc.nLights; i++) {
533 l = sc.Light(i);
534 float NL, Kl;
535 Point3 L;
536 if (l->Illuminate( sc, sc.Normal(), lightClr, L, NL, Kl)) {
537 if (l->ambientOnly) {
538 ip.ambIllumOut += lightClr;
539 continue;
540 }
541 if (NL<=0.0f)
542 continue;
543
544 // diffuse
545 if (l->affectDiffuse){
546 ip.diffIllumOut += Kl * d * rd * lightClr;
547 }
548
549 // specular
550 if (l->affectSpecular) {
551 // strauss uses the reflected LIGHT vector
552 Point3 R = L - 2.0f * NL * sc.Normal();
553 R = Normalize( R );
554
555 float RV = -Dot(R, sc.V() );
556
557 float s;
558 if (RV < 0.0f) {
559 // soften
560 if ( NL < softThresh )
561 RV *= SoftSpline2( NL / softThresh );
562 // specular function
563 s = SpecBoost * (float)pow( -RV, h);
564 } else
565 continue;
566
567 float a, b;
568 a = (float)acos( NL ) * OneOverHalfPi;
569 b = (float)acos( -Dot(sc.Normal(), sc.V()) ) * OneOverHalfPi;
570
571 float fa = F( a );
572 float j = fa * G( a ) * G( b );
573 float rj = rn > 0.0f ? Bound( rn + (rn+kj)*j ) : rn;
574 Color Cl = lightClr;
575 // normalize the light color in case it's really bright
576 float I = NormClr( Cl );
577 Color Cs = Cl + m * (1.0f - fa) * (Cd - Cl);
578
579 ip.specIllumOut += s * rj * I * Cs;
580
581 } // end, if specular
582 } // end, illuminate
583
584 } // for each light
585
586 // now we can multiply by the clrs, except specular, which is already done
587 ip.ambIllumOut *= 0.5f * rd * Cd;
588 ip.diffIllumIntens = Intens(ip.diffIllumOut);
589 ip.diffIllumOut *= Cd;
590
591 // next due reflection
592 if ( ip.hasComponents & HAS_REFLECT ){
593 Color rc = ip.channels[ ip.stdIDToChannel[ ID_RL ] ];
594 AffectReflection(sc, ip, rc);
595 ip.reflIllumOut = rc * ip.reflectAmt;
596 }
597
598 // last do refraction/ opacity
599 if ( (ip.hasComponents & HAS_REFRACT) ){
600 // Set up attenuation opacity for Refraction map. dim diffuse & spec by this
601 ip.finalAttenuation = ip.finalOpac * (1.0f - ip.refractAmt);
602
603 // Make more opaque where specular hilite occurs:
604 float max = Max(ip.specIllumOut);
605 if (max > 1.0f) max = 1.0f;
606 float newOpac = ip.finalAttenuation + max - ip.finalAttenuation * max;
607
608 // Evaluate refraction map, filtered by filter color.
609 // Color tClr = ((StdMat2*)(ip.pMtl))->TranspColor( newOpac, ip.channels[filtChan], ip.channels[diffChan]);
610 Color tClr = transpColor( TRANSP_FILTER, newOpac, Cd, Cd );
611 ip.transIllumOut = ip.channels[ ip.stdIDToChannel[ ID_RR ] ] * tClr;
612
613 // no transparency when doing refraction
614 ip.finalT.Black();
615
616 } else {
617 // no refraction, transparent?
618 ip.finalAttenuation = opac;
619 if (ip.hasComponents & HAS_OPACITY) {
620 // ip.finalT = Cd * (1.0f-opac);
621 Cd = greyVal * Color( 1.0f, 1.0f, 1.0f ) + clrVal * Cd;
622 ip.finalT = transpColor( TRANSP_FILTER, opac, Cd, Cd );
623 }
624 }
625
626 if (sc.globContext != NULL && sc.globContext->pToneOp != NULL) {
627 if (isInvertSelfIllum())
628 sc.globContext->pToneOp->RGBToScaled(ip.selfIllumOut);
629 if (isInvertReflect() && (ip.hasComponents & HAS_REFLECT))
630 sc.globContext->pToneOp->RGBToScaled(ip.reflIllumOut);
631 if (isInvertRefract() && (ip.hasComponents & HAS_REFRACT))
632 sc.globContext->pToneOp->RGBToScaled(ip.transIllumOut);
633 }
634
635 CombineComponents( sc, ip );
636 }
637
GetSpecularLevel(TimeValue t)638 float StraussShader::GetSpecularLevel(TimeValue t)
639 {
640 float g3 = Cube( glossiness );
641
642 // float j = F( 0.0f ) * G( 0.0f ) * G( 0.0f );
643 // float j = G( 0.0f ) * G( 0.0f );
644 // float rj = Bound( g3 + (g3+kj)*j );
645 // return rj;
646 return g3;
647 }
648
CombineComponents(ShadeContext & sc,IllumParams & ip)649 void StraussShader::CombineComponents( ShadeContext &sc, IllumParams& ip )
650 {
651 float o = (ip.hasComponents & HAS_REFRACT) ? ip.finalAttenuation : 1.0f;
652
653 ip.finalC = o * (ip.ambIllumOut + ip.diffIllumOut) + ip.specIllumOut
654 + ip.reflIllumOut + ip.transIllumOut;
655 }
656
657 ///////////////////////////////////////////////////////////////////////////////////
658 //
659 // Strauss shader dlg panel
660 //
661 class StraussShaderDlg : public ShaderParamDlg {
662 public:
663 StraussShader* pShader;
664 StdMat2* pMtl;
665 HPALETTE hOldPal;
666 HWND hwmEdit; // window handle of the materials editor dialog
667 IMtlParams* pMtlPar;
668 HWND hwHilite; // the hilite window
669 HWND hRollup; // Rollup panel
670 TimeValue curTime;
671 BOOL valid;
672 BOOL isActive;
673
674 IColorSwatch *cs[STRAUSS_NCOLBOX];
675 ISpinnerControl *glSpin, *mtSpin, *trSpin;
676 ICustButton* texMBut[NMBUTS];
677 TexDADMgr dadMgr;
678
679 StraussShaderDlg( HWND hwMtlEdit, IMtlParams *pParams );
680 ~StraussShaderDlg();
681
682 // required for correctly operating map buttons
FindSubTexFromHWND(HWND hw)683 int FindSubTexFromHWND(HWND hw) {
684 for (long i=0; i<NMBUTS; i++) {
685 if (hw == texMBut[i]->GetHwnd())
686 return texmapFromMBut[i];
687 }
688 return -1;
689 }
690
691 // Methods
692 INT_PTR PanelProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam );
ClassID()693 Class_ID ClassID(){ return StraussShaderClassID; }
694
SetThing(ReferenceTarget * m)695 void SetThing(ReferenceTarget *m){ pMtl = (StdMat2*)m; }
SetThings(StdMat2 * theMtl,Shader * theShader)696 void SetThings( StdMat2* theMtl, Shader* theShader )
697 { if (pShader) pShader->SetParamDlg(NULL,0);
698 pShader = (StraussShader*)theShader;
699 if (pShader)pShader->SetParamDlg(this,0);
700 pMtl = theMtl;
701 }
GetThing()702 ReferenceTarget* GetThing(){ return pMtl; } // mtl is the thing! (for DAD!)
GetShader()703 Shader* GetShader(){ return pShader; }
704
SetTime(TimeValue t)705 void SetTime(TimeValue t) {
706 //DS 2/26/99: added interval test to prevent redrawing when not necessary
707 curTime = t;
708 if (!pShader->ivalid.InInterval(t)) {
709 Interval v;
710 pShader->Update(t,v);
711 LoadDialog(TRUE);
712 }
713 }
KeyAtCurTime(int id)714 BOOL KeyAtCurTime(int id) { return pShader->KeyAtTime(id,curTime); }
DeleteThis()715 void DeleteThis() { delete this; }
ActivateDlg(BOOL dlgOn)716 void ActivateDlg( BOOL dlgOn ){ isActive = dlgOn; }
GetHWnd()717 HWND GetHWnd(){ return hRollup; }
NotifyChanged()718 void NotifyChanged(){ pShader->NotifyChanged(); }
719 void LoadDialog(BOOL draw);
ReloadDialog()720 void ReloadDialog(){ Interval v; pShader->Update(pMtlPar->GetTime(), v); LoadDialog(FALSE);}
UpdateDialog(ParamID paramId)721 void UpdateDialog( ParamID paramId ){ ReloadDialog(); }
722
UpdateMtlDisplay()723 void UpdateMtlDisplay(){ pMtlPar->MtlChanged(); } // redraw viewports
724 void UpdateHilite( );
725 void UpdateColSwatches();
726 void UpdateMapButtons();
727 void UpdateOpacity();
728
SelectEditColor(int i)729 void SelectEditColor(int i) { cs[ i ]->EditThis(FALSE); }
730 };
731
StraussShaderDlgProc(HWND hwndDlg,UINT msg,WPARAM wParam,LPARAM lParam)732 static INT_PTR CALLBACK StraussShaderDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
733 StraussShaderDlg *theDlg;
734 if (msg == WM_INITDIALOG) {
735 theDlg = (StraussShaderDlg*)lParam;
736 DLSetWindowLongPtr(hwndDlg, lParam);
737 } else {
738 if ( (theDlg = DLGetWindowLongPtr<StraussShaderDlg *>(hwndDlg) ) == NULL )
739 return FALSE;
740 }
741 theDlg->isActive = 1;
742 BOOL res = theDlg->PanelProc(hwndDlg, msg, wParam, lParam);
743 theDlg->isActive = 0;
744 return res;
745 }
746
747
CreateParamDialog(HWND hOldRollup,HWND hwMtlEdit,IMtlParams * imp,StdMat2 * theMtl,int rollupOpen,int)748 ShaderParamDlg* StraussShader::CreateParamDialog(HWND hOldRollup, HWND hwMtlEdit, IMtlParams *imp, StdMat2* theMtl, int rollupOpen, int )
749 {
750 Interval v;
751 Update(imp->GetTime(),v);
752 int rollupflags = rolloutOpen ? 0 : APPENDROLL_CLOSED;
753
754 StraussShaderDlg *pDlg = new StraussShaderDlg(hwMtlEdit, imp);
755 pDlg->SetThings( theMtl, this );
756
757 LoadStdShaderResources();
758 if ( hOldRollup ) {
759 pDlg->hRollup = imp->ReplaceRollupPage(
760 hOldRollup,
761 hInstance,
762 MAKEINTRESOURCE(IDD_DMTL_BASIC_STRAUSS3),
763 StraussShaderDlgProc,
764 GetString(IDS_KE_STRAUSS_BASIC), // your name here
765 (LPARAM)pDlg ,
766 // NS: Bugfix 263414 keep the old category and store it for the current rollup
767 rollupflags|ROLLUP_SAVECAT|ROLLUP_USEREPLACEDCAT
768 );
769 } else
770 pDlg->hRollup = imp->AddRollupPage(
771 hInstance,
772 MAKEINTRESOURCE(IDD_DMTL_BASIC_STRAUSS3),
773 StraussShaderDlgProc,
774 GetString(IDS_KE_STRAUSS_BASIC),
775 (LPARAM)pDlg ,
776 rollupflags
777 );
778
779 return (ShaderParamDlg*)pDlg;
780 }
781
NotifyRefChanged(Interval changeInt,RefTargetHandle hTarget,PartID & partID,RefMessage message)782 RefResult StraussShader::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget,
783 PartID& partID, RefMessage message )
784 {
785 switch (message) {
786 case REFMSG_CHANGE:
787 ivalid.SetEmpty();
788 if (hTarget == pblock){
789 // update UI if paramblock changed, possibly from scripter
790 ParamID changingParam = pblock->LastNotifyParamID();
791 // reload the dialog if present
792 if (paramDlg){
793 paramDlg->UpdateDialog( changingParam );
794 }
795 }
796 break;
797 }
798 return(REF_SUCCEED);
799 }
800
801
EvalHiliteCurve(float x)802 float StraussShader::EvalHiliteCurve( float x )
803 {
804 float op = ( paramDlg )? paramDlg->pMtl->GetOpacity(0) : 1.0f;
805 float rn = op - (1.0f - glossiness * glossiness * glossiness) * op;
806
807 float h = (glossiness == 1.0f)? 600.0f : 3.0f / (1.0f - glossiness );
808 float s = (float)pow( cos(x*PI), h);
809
810 float a = 0.5f;
811 float b = x;
812
813 float fa = F( a );
814 float j = fa * G( a ) * G( b );
815 float rj = Min( 1.0f, rn + (rn+kj)*j );
816 float IllumOut = SpecBoost * s * rj;
817
818 return IllumOut;
819 }
820
821
StraussShaderDlg(HWND hwMtlEdit,IMtlParams * pParams)822 StraussShaderDlg::StraussShaderDlg( HWND hwMtlEdit, IMtlParams *pParams)
823 {
824 pMtl = NULL;
825 pShader = NULL;
826 hwmEdit = hwMtlEdit;
827 pMtlPar = pParams;
828 dadMgr.Init(this);
829 glSpin = mtSpin = trSpin = NULL;
830 hRollup = hwHilite = NULL;
831 curTime = pMtlPar->GetTime();
832 isActive = valid = FALSE;
833
834 for( long i = 0; i < STRAUSS_NCOLBOX; ++i )
835 cs[ i ] = NULL;
836
837 for( long i = 0; i < NMBUTS; ++i )
838 texMBut[ i ] = NULL;
839 }
840
~StraussShaderDlg()841 StraussShaderDlg::~StraussShaderDlg()
842 {
843 HDC hdc = GetDC(hRollup);
844 GetGPort()->RestorePalette(hdc, hOldPal);
845 ReleaseDC(hRollup, hdc);
846
847 if( pShader ) pShader->SetParamDlg(NULL,0);
848
849 for (long i=0; i < NMBUTS; i++ ){
850 ReleaseICustButton( texMBut[i] );
851 texMBut[i] = NULL;
852 }
853
854 for (long i=0; i<STRAUSS_NCOLBOX; i++)
855 if (cs[i]) ReleaseIColorSwatch(cs[i]); // mjm - 5.10.99
856
857 ReleaseISpinner(glSpin);
858 ReleaseISpinner(mtSpin);
859 ReleaseISpinner(trSpin);
860
861 DLSetWindowLongPtr(hRollup, NULL);
862 DLSetWindowLongPtr(hwHilite, NULL);
863
864 if(pShader){
865 IRollupWindow* pRollup = pMtlPar->GetMtlEditorRollup();
866 pShader->SetPanelOpen(pRollup->IsPanelOpen(pRollup->GetPanelIndex(hRollup)));
867 }
868
869 hwHilite = hRollup = NULL;
870 }
871
872
LoadDialog(BOOL draw)873 void StraussShaderDlg::LoadDialog(BOOL draw)
874 {
875 if (pShader && hRollup) {
876 glSpin->SetValue( FracToPc( pShader->GetGlossiness() ),FALSE);
877 glSpin->SetKeyBrackets(KeyAtCurTime(st_glossiness));
878
879 mtSpin->SetValue( FracToPc( pShader->GetMetalness() ), FALSE);
880 mtSpin->SetKeyBrackets(KeyAtCurTime(st_metalness));
881
882 trSpin->SetValue(FracToPc(pMtl->GetOpacity(curTime)),FALSE);
883 trSpin->SetKeyBrackets(pMtl->KeyAtTime(OPACITY_PARAM, curTime));
884
885 UpdateColSwatches();
886 UpdateHilite();
887 }
888 }
889
890
891 static TCHAR* mapStates[] = { _T(" "), _T("m"), _T("M") };
892
UpdateMapButtons()893 void StraussShaderDlg::UpdateMapButtons()
894 {
895
896 for ( long i = 0; i < NMBUTS; ++i ) {
897 int nMap = texmapFromMBut[ i ];
898 int state = pMtl->GetMapState( nMap );
899 texMBut[i]->SetText( mapStates[ state ] );
900
901 TSTR nm = pMtl->GetMapName( nMap );
902 texMBut[i]->SetTooltip(TRUE,nm);
903 }
904 }
905
906
UpdateOpacity()907 void StraussShaderDlg::UpdateOpacity()
908 {
909 trSpin->SetValue(FracToPc(pMtl->GetOpacity(curTime)),FALSE);
910 trSpin->SetKeyBrackets(pMtl->KeyAtTime(OPACITY_PARAM, curTime));
911 UpdateHilite();
912 }
913
UpdateColSwatches()914 void StraussShaderDlg::UpdateColSwatches()
915 {
916 cs[0]->SetKeyBrackets( pShader->KeyAtTime(st_diffuse,curTime) );
917 cs[0]->SetColor( pShader->GetDiffuseClr() );
918 }
919
920
UpdateHilite()921 void StraussShaderDlg::UpdateHilite()
922 {
923 HDC hdc = GetDC(hwHilite);
924 Rect r;
925 GetClientRect(hwHilite,&r);
926 DrawHilite(hdc, r, pShader );
927 ReleaseDC(hwHilite,hdc);
928 }
929
930
931
ColorIDCToIndex(int idc)932 static int ColorIDCToIndex(int idc) {
933 switch (idc) {
934 case IDC_COLOR: return 0;
935 default: return 0;
936 }
937 }
938
939
PanelProc(HWND hwndDlg,UINT msg,WPARAM wParam,LPARAM lParam)940 INT_PTR StraussShaderDlg::PanelProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam )
941 {
942 int id = LOWORD(wParam);
943 int code = HIWORD(wParam);
944 switch (msg) {
945 case WM_INITDIALOG:
946 {
947 HDC theHDC = GetDC(hwndDlg);
948 hOldPal = GetGPort()->PlugPalette(theHDC);
949 ReleaseDC(hwndDlg,theHDC);
950
951 HWND hwndCS = GetDlgItem(hwndDlg, IDC_COLOR);
952 cs[0] = GetIColorSwatch( hwndCS, pShader->GetDiffuseClr(), GetString(IDS_KE_COLOR) );
953
954 hwHilite = GetDlgItem(hwndDlg, IDC_HIGHLIGHT);
955 DLSetWindowLongPtr( hwHilite, HiliteWndProc);
956
957 glSpin = SetupIntSpinner(hwndDlg, IDC_GL_SPIN, IDC_GL_EDIT, 0,100, 0);
958 mtSpin = SetupIntSpinner(hwndDlg, IDC_MT_SPIN, IDC_MT_EDIT, 0,100, 0);
959 trSpin = SetupIntSpinner(hwndDlg, IDC_TR_SPIN, IDC_TR_EDIT, 0,100, 0);
960
961 for (int j=0; j<NMBUTS; j++) {
962 texMBut[j] = GetICustButton(GetDlgItem(hwndDlg,texMButtonsIDC[j]));
963 assert( texMBut[j] );
964 texMBut[j]->SetRightClickNotify(TRUE);
965 texMBut[j]->SetDADMgr(&dadMgr);
966 }
967 LoadDialog(TRUE);
968 }
969 break;
970
971 case WM_COMMAND:
972 {
973 for ( int i=0; i<NMBUTS; i++) {
974 if (id == texMButtonsIDC[i]) {
975 PostMessage(hwmEdit,WM_TEXMAP_BUTTON, texmapFromMBut[i],(LPARAM)pMtl );
976 UpdateMapButtons();
977 goto exit;
978 }
979 }
980 }
981
982 break; // WM_COMMAND
983
984 case CC_COLOR_SEL: {
985 int id = LOWORD(wParam);
986 SelectEditColor(ColorIDCToIndex(id));
987 }
988 break;
989 case CC_COLOR_DROP: {
990 int id = LOWORD(wParam);
991 SelectEditColor(ColorIDCToIndex(id));
992 UpdateMtlDisplay();
993 }
994 break;
995 case CC_COLOR_BUTTONDOWN:
996 theHold.Begin();
997 break;
998 case CC_COLOR_BUTTONUP:
999 if (HIWORD(wParam)) theHold.Accept(GetString(IDS_DS_PARAMCHG));
1000 else theHold.Cancel();
1001 UpdateMtlDisplay();
1002 break;
1003 case CC_COLOR_CHANGE: {
1004 int id = LOWORD(wParam);
1005 int buttonUp = HIWORD(wParam);
1006 int n = ColorIDCToIndex(id);
1007 if (buttonUp) theHold.Begin();
1008 Color curColor(cs[n]->GetColor());
1009 pShader->SetDiffuseClr(curColor, curTime);
1010 if (buttonUp) {
1011 theHold.Accept(GetString(IDS_DS_PARAMCHG));
1012 // DS: 5/11/99- this was commented out. I put it back in, because
1013 // it is necessary for the Reset button in the color picker to
1014 // update the viewport.
1015 UpdateMtlDisplay();
1016 }
1017 } break;
1018 case WM_PAINT:
1019 if (!valid) {
1020 valid = TRUE;
1021 ReloadDialog();
1022 }
1023 return FALSE;
1024 case WM_CLOSE:
1025 case WM_DESTROY:
1026 break;
1027 case CC_SPINNER_CHANGE:
1028 if (!theHold.Holding()) theHold.Begin();
1029 switch (id) {
1030 case IDC_GL_SPIN:
1031 pShader->SetGlossiness(PcToFrac( glSpin->GetIVal() ), curTime);
1032 UpdateHilite();
1033 break;
1034 case IDC_MT_SPIN:
1035 pShader->SetMetalness(PcToFrac(mtSpin->GetIVal()), curTime);
1036 break;
1037 //******** >>>><<<< required handling for opacity....must be present in all dialogs
1038 case IDC_TR_SPIN:
1039 pMtl->SetOpacity(PcToFrac( trSpin->GetIVal()),curTime);
1040 break;
1041 }
1042 // UpdateMtlDisplay();
1043 break;
1044
1045 case CC_SPINNER_BUTTONDOWN:
1046 theHold.Begin();
1047 break;
1048
1049 case WM_CUSTEDIT_ENTER:
1050 case CC_SPINNER_BUTTONUP:
1051 if (HIWORD(wParam) || msg==WM_CUSTEDIT_ENTER)
1052 theHold.Accept(GetString(IDS_DS_PARAMCHG));
1053 else
1054 theHold.Cancel();
1055 UpdateMtlDisplay();
1056 break;
1057
1058 }
1059 exit:
1060 return FALSE;
1061 }
1062
1063
1064