// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/> // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. #include "tile_utility.h" extern HINSTANCE hInstance; TCHAR *GetString(int id); #define NSUBTEX 2 // number of texture map slots #define NCOLS 2 static int subTexId[NSUBTEX] = { IDC_MULT_TEX1, IDC_MULT_TEX2 }; #define ALPHA_FROM_1 0 #define ALPHA_FROM_2 1 #define ALPHA_FROM_MULT 2 //-------------------------------------------------------------- // RGBAdd: A Composite texture map //-------------------------------------------------------------- class RGBAdd: public Texmap { public: BOOL Param1; IParamBlock2 *pblock; // ref #0 Texmap* subTex[NSUBTEX]; // refs 1,2; BOOL mapOn[NSUBTEX]; Color col[NCOLS]; Interval ivalid; int alphaFrom; BOOL rollScroll; BOOL loadingOld; RGBAdd(); ParamDlg* CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp); void Update(TimeValue t, Interval& valid); void Init(); void Reset(); Interval Validity(TimeValue t) { Interval v; Update(t,v); return ivalid; } void NotifyChanged(); void SetColor(int i, Color c, TimeValue t); // Evaluate the color of map for the context. AColor EvalColor(ShadeContext& sc); float EvalMono(ShadeContext& sc); AColor EvalFunction(ShadeContext& sc, float u, float v, float du, float dv); // For Bump mapping, need a perturbation to apply to a normal. // Leave it up to the Texmap to determine how to do this. Point3 EvalNormalPerturb(ShadeContext& sc); // Methods to access texture maps of material int NumSubTexmaps() { return NSUBTEX; } Texmap* GetSubTexmap(int i) { return subTex[i]; } void SetSubTexmap(int i, Texmap *m); TSTR GetSubTexmapSlotName(int i); Class_ID ClassID() { return RGBAddClassID; } SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } void GetClassName(TSTR& s) { s= "RGB Additive"; } void DeleteThis() { delete this; } int NumSubs() { return NSUBTEX+1; } Animatable* SubAnim(int i); TSTR SubAnimName(int i); int SubNumToRefNum(int subNum) { return subNum; } // From ref int NumRefs() { return NSUBTEX+1; } RefTargetHandle GetReference(int i); void SetReference(int i, RefTargetHandle rtarg); int RemapRefOnLoad(int iref); RefTargetHandle Clone(RemapDir &remap = DefaultRemapDir()); RefResult NotifyRefChanged( Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message ); // IO IOResult Save(ISave *isave); IOResult Load(ILoad *iload); // JBW: direct ParamBlock access is added int NumParamBlocks() { return 1; } // return number of ParamBlocks in this instance IParamBlock2* GetParamBlock(int i) { return pblock; } // return i'th ParamBlock IParamBlock2* GetParamBlockByID(BlockID id) { return (pblock->ID() == id) ? pblock : NULL; } // return id'd ParamBlock }; class RGBAddClassDesc:public ClassDesc2 { public: int IsPublic() { return 1; } void * Create(BOOL loading) { return new RGBAdd; } const TCHAR * ClassName() { return GetString(IDS_DS_RGBMULT_CDESC); } // mjm - 2.3.99 SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } Class_ID ClassID() { return RGBAddClassID; } const TCHAR* Category() { return TEXMAP_CAT_COMP; } // JBW: new descriptor data accessors added. Note that the // internal name is hardwired since it must not be localized. const TCHAR* InternalName() { return _T("RGBAdd"); } // returns fixed parsable name (scripter-visible name) HINSTANCE HInstance() { return hInstance; } // returns owning module handle }; static RGBAddClassDesc maskCD; ClassDesc* GetRGBAddDesc() { return &maskCD; } enum { RGBAdd_params }; // pblock ID // RGBAdd_params param IDs enum { RGBAdd_color1, RGBAdd_color2, RGBAdd_map1, RGBAdd_map2, RGBAdd_map1_on, RGBAdd_map2_on, // main grad params RGBAdd_type, }; static ParamBlockDesc2 RGBAdd_param_blk ( RGBAdd_params, _T("parameters"), 0, &maskCD, P_AUTO_CONSTRUCT + P_AUTO_UI, 0, //rollout IDD_RGBMULT, "RGB Additif Parameters", 0, 0, NULL, // params RGBAdd_color1, _T("color1"), TYPE_RGBA, P_ANIMATABLE, IDS_DS_COLOR1, p_default, Color(0,0,0), p_ui, TYPE_COLORSWATCH, IDC_MULT_COL1, end, RGBAdd_color2, _T("color2"), TYPE_RGBA, P_ANIMATABLE, IDS_DS_COLOR2, p_default, Color(0.5,0.5,0.5), p_ui, TYPE_COLORSWATCH, IDC_MULT_COL2, end, RGBAdd_map1, _T("map1"), TYPE_TEXMAP, P_OWNERS_REF, IDS_JW_MAP1, p_refno, 1, p_subtexno, 0, p_ui, TYPE_TEXMAPBUTTON, IDC_MULT_TEX1, end, RGBAdd_map2, _T("map2"), TYPE_TEXMAP, P_OWNERS_REF, IDS_JW_MAP2, p_refno, 2, p_subtexno, 1, p_ui, TYPE_TEXMAPBUTTON, IDC_MULT_TEX2, end, RGBAdd_map1_on, _T("map1Enabled"), TYPE_BOOL, 0, IDS_JW_MAP1ENABLE, p_default, TRUE, p_ui, TYPE_SINGLECHEKBOX, IDC_MAPON1, end, RGBAdd_map2_on, _T("map2Enabled"), TYPE_BOOL, 0, IDS_JW_MAP2ENABLE, p_default, TRUE, p_ui, TYPE_SINGLECHEKBOX, IDC_MAPON2, end, RGBAdd_type, _T("alphaFrom"), TYPE_INT, 0, IDS_PW_ALPHAFROM, p_default, 2, p_range, 0, 2, p_ui, TYPE_RADIO, 3, IDC_MULT_ALPHA1, IDC_MULT_ALPHA2, IDC_MULT_ALPHA3, end, end ); //----------------------------------------------------------------------------- // RGBAdd //----------------------------------------------------------------------------- #define NPARAMS 2 static int name_id[NPARAMS] = {IDS_DS_COLOR1, IDS_DS_COLOR2}; #define RGBAdd_VERSION 2 static ParamBlockDescID pbdesc[NPARAMS] = { { TYPE_RGBA, NULL, TRUE,RGBAdd_color1 }, // col1 { TYPE_RGBA, NULL, TRUE,RGBAdd_color2 } // col2 }; static ParamVersionDesc versions[] = { ParamVersionDesc(pbdesc,2,1), // Version 1 params }; void RGBAdd::Init() { ivalid.SetEmpty(); alphaFrom = ALPHA_FROM_MULT; SetColor(0, Color(0.0f,0.0f,0.0f), TimeValue(0)); SetColor(1, Color(0.0f,0.0f,0.0f), TimeValue(0)); mapOn[0] = mapOn[1] = 1; } void RGBAdd::Reset() { maskCD.Reset(this, TRUE); // reset all pb2's DeleteReference(1); // get rid of maps DeleteReference(2); // get rid of maps Init(); } void RGBAdd::NotifyChanged() { NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); } RGBAdd::RGBAdd() { for (int i=0; i<NSUBTEX; i++) subTex[i] = NULL; pblock = NULL; maskCD.MakeAutoParamBlocks(this); // make and intialize paramblock2 Init(); rollScroll=0; } static AColor white(1.0f,1.0f,1.0f,1.0f); AColor RGBAdd::EvalColor(ShadeContext& sc) { if (gbufID) sc.SetGBufferID(gbufID); AColor c0 = (subTex[0]&&mapOn[0])? subTex[0]->EvalColor(sc): col[0]; AColor c1 = (subTex[1]&&mapOn[1])? subTex[1]->EvalColor(sc): col[1]; AColor c; c.r = c0.r+c1.r; c.r = c.r > 1.f ? 1.f : c.r; c.g = c0.g+c1.g; c.g = c.g > 1.f ? 1.f : c.g; c.b = c0.b+c1.b; c.b = c.b > 1.f ? 1.f : c.b; switch(alphaFrom) { case ALPHA_FROM_1: c.a = c0.a; break; case ALPHA_FROM_2: c.a = c1.a; break; case ALPHA_FROM_MULT: c.a = c0.a*c1.a; break; } return c; } float RGBAdd::EvalMono(ShadeContext& sc) { if (gbufID) sc.SetGBufferID(gbufID); float m = (subTex[1]&&mapOn[1])? subTex[1]->EvalMono(sc): Intens(col[0]); float c0 = (subTex[0]&&mapOn[0])? subTex[0]->EvalMono(sc): Intens(col[1]); return (m+c0) > 1.f ? 1.f : m+c0; } Point3 RGBAdd::EvalNormalPerturb(ShadeContext& sc) { if (gbufID) sc.SetGBufferID(gbufID); Point3 p0 = subTex[0]&&mapOn[0]? subTex[0]->EvalNormalPerturb(sc): Point3(0.0f,0.0f,0.0f); Point3 p1 = subTex[1]&&mapOn[1]? subTex[1]->EvalNormalPerturb(sc): Point3(0.0f,0.0f,0.0f); return p0+p1; } RefTargetHandle RGBAdd::Clone(RemapDir &remap) { RGBAdd *mnew = new RGBAdd(); *((MtlBase*)mnew) = *((MtlBase*)this); // copy superclass stuff mnew->ReplaceReference(0,remap.CloneRef(pblock)); mnew->ivalid.SetEmpty(); for (int i = 0; i<NSUBTEX; i++) { mnew->subTex[i] = NULL; if (subTex[i]) mnew->ReplaceReference(i+1,remap.CloneRef(subTex[i])); mnew->mapOn[i] = mapOn[i]; } mnew->alphaFrom = alphaFrom; return (RefTargetHandle)mnew; } ParamDlg* RGBAdd::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) { IAutoMParamDlg* masterDlg = maskCD.CreateParamDlgs(hwMtlEdit, imp, this); return masterDlg; } void RGBAdd::Update(TimeValue t, Interval& valid) { if (Param1) { pblock->SetValue( RGBAdd_map1_on, 0, mapOn[0]); pblock->SetValue( RGBAdd_map2_on, 0, mapOn[1]); pblock->SetValue( RGBAdd_type, 0, alphaFrom); Param1 = FALSE; } if (!ivalid.InInterval(t)) { ivalid.SetInfinite(); pblock->GetValue( RGBAdd_color1, t, col[0], ivalid ); col[0].ClampMinMax(); pblock->GetValue( RGBAdd_color2, t, col[1], ivalid ); pblock->GetValue( RGBAdd_map1_on, t, mapOn[0], ivalid ); pblock->GetValue( RGBAdd_map2_on, t, mapOn[1], ivalid ); pblock->GetValue( RGBAdd_type, t, alphaFrom, ivalid ); col[1].ClampMinMax(); for (int i=0; i<NSUBTEX; i++) { if (subTex[i]) subTex[i]->Update(t,ivalid); } } valid &= ivalid; } void RGBAdd::SetColor(int i, Color c, TimeValue t) { col[i] = c; int id = i==0?RGBAdd_color1:RGBAdd_color2; pblock->SetValue( id, t, c); } RefTargetHandle RGBAdd::GetReference(int i) { if (i==0) return pblock; else return subTex[i-1]; } int RGBAdd::RemapRefOnLoad(int iref) { if (loadingOld) return iref+1; else return iref; } void RGBAdd::SetReference(int i, RefTargetHandle rtarg) { switch(i) { case 0: pblock = (IParamBlock2 *)rtarg; break; default: subTex[i-1] = (Texmap *)rtarg; break; } } void RGBAdd::SetSubTexmap(int i, Texmap *m) { ReplaceReference(i+1,m); if (i==0) { RGBAdd_param_blk.InvalidateUI(RGBAdd_map1); ivalid.SetEmpty(); } else if (i==1) { RGBAdd_param_blk.InvalidateUI(RGBAdd_map2); ivalid.SetEmpty(); } } TSTR RGBAdd::GetSubTexmapSlotName(int i) { switch(i) { case 0: return TSTR(GetString(IDS_DS_COLOR1)); case 1: return TSTR(GetString(IDS_DS_COLOR2)); default: return TSTR(_T("")); } } Animatable* RGBAdd::SubAnim(int i) { switch (i) { case 0: return pblock; default: return subTex[i-1]; } } TSTR RGBAdd::SubAnimName(int i) { switch (i) { case 0: return TSTR(GetString(IDS_DS_PARAMETERS)); default: return GetSubTexmapTVName(i-1); } } RefResult RGBAdd::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message ) { switch (message) { case REFMSG_CHANGE: ivalid.SetEmpty(); if (hTarget == pblock) { // see if this message came from a changing parameter in the pblock, // if so, limit rollout update to the changing item and update any active viewport texture ParamID changing_param = pblock->LastNotifyParamID(); RGBAdd_param_blk.InvalidateUI(changing_param); // notify our dependents that we've changed // NotifyChanged(); //DS this is redundant } break; } return(REF_SUCCEED); } #define MTL_HDR_CHUNK 0x4000 #define MAPOFF_CHUNK 0x1000 #define INVERT_RGBMULT_CHUNK 0x2000 #define ALPHA_FROM_CHUNK 0x2010 #define RGBAdd_VERSION_CHUNK 0x2020 #define PARAM2_CHUNK 0x2030 IOResult RGBAdd::Save(ISave *isave) { IOResult res; ULONG nb; // Save common stuff isave->BeginChunk(MTL_HDR_CHUNK); res = MtlBase::Save(isave); if (res!=IO_OK) return res; isave->EndChunk(); int vers = RGBAdd_VERSION; isave->BeginChunk(RGBAdd_VERSION_CHUNK); isave->Write(&vers,sizeof(vers),&nb); isave->EndChunk(); isave->BeginChunk(PARAM2_CHUNK); isave->EndChunk(); return IO_OK; } class RGBAddPostLoad : public PostLoadCallback { public: RGBAdd *tm; RGBAddPostLoad(RGBAdd *b) {tm=b;} void proc(ILoad *iload) { tm->loadingOld = FALSE; delete this; } }; class RGBAdd2PostLoad : public PostLoadCallback { public: RGBAdd *n; RGBAdd2PostLoad(RGBAdd *ns) {n = ns;} void proc(ILoad *iload) { if (n->Param1) { n->pblock->SetValue( RGBAdd_map1_on, 0, n->mapOn[0]); n->pblock->SetValue( RGBAdd_map2_on, 0, n->mapOn[1]); n->pblock->SetValue( RGBAdd_type, 0, n->alphaFrom); } delete this; } }; IOResult RGBAdd::Load(ILoad *iload) { ULONG nb; IOResult res; int id; loadingOld = TRUE; Param1 = TRUE; while (IO_OK==(res=iload->OpenChunk())) { switch(id = iload->CurChunkID()) { case MTL_HDR_CHUNK: res = MtlBase::Load(iload); break; case PARAM2_CHUNK: Param1= FALSE; break; case RGBAdd_VERSION_CHUNK: loadingOld = FALSE; break; case MAPOFF_CHUNK+0: case MAPOFF_CHUNK+1: mapOn[id-MAPOFF_CHUNK] = 0; break; case ALPHA_FROM_CHUNK: res = iload->Read(&alphaFrom,sizeof(alphaFrom), &nb); break; } iload->CloseChunk(); if (res!=IO_OK) return res; } if (loadingOld) iload->RegisterPostLoadCallback(new RGBAddPostLoad(this)); // JBW: register old version ParamBlock to ParamBlock2 converter ParamBlock2PLCB* plcb = new ParamBlock2PLCB(versions, 1, &RGBAdd_param_blk, this, 0); iload->RegisterPostLoadCallback(plcb); // iload->RegisterPostLoadCallback(new RGBAdd2PostLoad(this)); return IO_OK; }