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