// NeL - MMORPG Framework
// 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 .
#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 MCHAR* 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 MCHAR* InternalName() { return _M("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,
p_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,
p_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,
p_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,
p_end,
RGBAdd_map1_on, _T("map1Enabled"), TYPE_BOOL, 0, IDS_JW_MAP1ENABLE,
p_default, TRUE,
p_ui, TYPE_SINGLECHEKBOX, IDC_MAPON1,
p_end,
RGBAdd_map2_on, _T("map2Enabled"), TYPE_BOOL, 0, IDS_JW_MAP2ENABLE,
p_default, TRUE,
p_ui, TYPE_SINGLECHEKBOX, IDC_MAPON2,
p_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,
p_end,
p_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; iEvalColor(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; isubTex[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; iUpdate(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;
}