You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

700 lines
18 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "hsUtils.h"
#include "plClothingMtl.h"
#include "resource.h"
#include "Shaders.h"
#include "iparamm2.h"
#include "../MaxMain/plPlasmaRefMsgs.h"
#include "plBMSampler.h"
#include "stdmat.h"
#include "Layers/plLayerTex.h"
#include "Layers/plLayerTexBitmapPB.h"
#include "../../Plasma/PubUtilLib/plAvatar/plClothingLayout.h"
extern HINSTANCE hInstance;
class plClothingMtlClassDesc : public ClassDesc2
{
public:
int IsPublic() { return TRUE; }
void* Create(BOOL loading) { return TRACKED_NEW plClothingMtl(loading); }
const TCHAR* ClassName() { return GetString(IDS_CLOTHING_MTL); }
SClass_ID SuperClassID() { return MATERIAL_CLASS_ID; }
Class_ID ClassID() { return CLOTHING_MTL_CLASS_ID; }
const TCHAR* Category() { return NULL; }
const TCHAR* InternalName() { return _T("ClothingMaterial"); }
HINSTANCE HInstance() { return hInstance; }
};
static plClothingMtlClassDesc plClothingMtlDesc;
ClassDesc2* GetClothingMtlDesc() { return &plClothingMtlDesc; }
// For initializing paramblock descriptor
ParamBlockDesc2 *GetClothingPB();
#include "plClothingMtlPBDec.h"
const UINT32 plClothingMtl::ButtonConstants[] =
{
IDC_CLOTHING_TEXTURE1,
IDC_CLOTHING_TEXTURE2,
IDC_CLOTHING_TEXTURE3,
IDC_CLOTHING_TEXTURE4
};
const UINT32 plClothingMtl::TextConstants[] =
{
IDC_CLOTHING_TILE1_NAME,
IDC_CLOTHING_TILE1_SIZE,
IDC_CLOTHING_TILE2_NAME,
IDC_CLOTHING_TILE2_SIZE,
IDC_CLOTHING_TILE3_NAME,
IDC_CLOTHING_TILE3_SIZE,
IDC_CLOTHING_TILE4_NAME,
IDC_CLOTHING_TILE4_SIZE
};
const char *plClothingMtl::LayerStrings[] =
{
"Base",
"Skin",
"Skin Blend (1)",
"Skin Blend (2)",
"Skin Blend (3)",
"Skin Blend (4)",
"Skin Blend (5)",
"Skin Blend (6)",
"Tint 1",
"Tint 2"
};
const UInt8 plClothingMtl::LayerToPBIdx[] =
{
kTexmapBase,
kTexmapSkin,
kTexmapSkinBlend1,
kTexmapSkinBlend2,
kTexmapSkinBlend3,
kTexmapSkinBlend4,
kTexmapSkinBlend5,
kTexmapSkinBlend6,
kTexmap,
kTexmap2
};
plClothingMtl::plClothingMtl(BOOL loading) : fBasicPB(NULL)
{
plClothingMtlDesc.MakeAutoParamBlocks(this);
Reset();
int i;
for (i = 0; i < plClothingMtl::kMaxTiles; i++)
{
plLayerTex *tex = TRACKED_NEW plLayerTex;
fBasicPB->SetValue(ParamID(kTexmap), 0, tex, i);
}
fBasicPB->SetValue(ParamID(kThumbnail), 0, TRACKED_NEW plLayerTex);
}
void plClothingMtl::Reset()
{
fIValid.SetEmpty();
}
ParamDlg* plClothingMtl::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp)
{
fIMtlParams = imp;
IAutoMParamDlg* masterDlg = plClothingMtlDesc.CreateParamDlgs(hwMtlEdit, imp, this);
return (ParamDlg*)masterDlg;
}
BOOL plClothingMtl::SetDlgThing(ParamDlg* dlg)
{
return FALSE;
}
Interval plClothingMtl::Validity(TimeValue t)
{
// mf horse - Hacking in something like real validity checking
// to get material animations working. No warranty, this is just
// better than nothing.
Interval v = FOREVER;
fBasicPB->GetValidity(t, v);
return v;
}
/*===========================================================================*\
| Subanim & References support
\*===========================================================================*/
/*
int plClothingMtl::NumSubs()
{
return 2;
}
TSTR plClothingMtl::SubAnimName(int i)
{
switch (i)
{
case 0: return fBasicPB->GetLocalName();
case 1: return "Texmap";
}
return "";
}
Animatable* plClothingMtl::SubAnim(int i)
{
switch (i)
{
case 0: return fBasicPB;
case 1: return fBasicPB->GetTexmap(kTexmap);
}
return NULL;
}
*/
int plClothingMtl::NumRefs()
{
return 1;
}
RefTargetHandle plClothingMtl::GetReference(int i)
{
switch (i)
{
case kRefBasic: return fBasicPB;
}
return NULL;
}
void plClothingMtl::SetReference(int i, RefTargetHandle rtarg)
{
if (i == kRefBasic)
fBasicPB = (IParamBlock2 *)rtarg;
}
int plClothingMtl::NumParamBlocks()
{
return 1;
}
IParamBlock2* plClothingMtl::GetParamBlock(int i)
{
return (IParamBlock2*)GetReference(i);
}
IParamBlock2* plClothingMtl::GetParamBlockByID(BlockID id)
{
if (fBasicPB->ID() == id)
return fBasicPB;
return NULL;
}
RefResult plClothingMtl::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message)
{
switch (message)
{
case REFMSG_CHANGE:
fIValid.SetEmpty();
// see if this message came from a changing parameter in the pblock,
// if so, limit rollout update to the changing item
if (hTarget == fBasicPB)
{
IParamBlock2 *pb = (IParamBlock2*)hTarget;
ParamID changingParam = pb->LastNotifyParamID();
pb->GetDesc()->InvalidateUI(changingParam);
// And let the SceneWatcher know that the material on some of it's
// referenced objects changed.
NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_MAT);
}
break;
}
return REF_SUCCEED;
}
////////////////////////////////////////////////////////////////////////////////
// Subtexmap access
int plClothingMtl::NumSubTexmaps()
{
return plClothingElement::kLayerMax * kMaxTiles + 1; // add one for the thumbnail
}
Texmap* plClothingMtl::GetSubTexmap(int i)
{
if (i >= 0 && i < plClothingElement::kLayerMax * kMaxTiles)
return fBasicPB->GetTexmap(ParamID(LayerToPBIdx[i / kMaxTiles]), 0, i % kMaxTiles);
if (i == plClothingElement::kLayerMax * kMaxTiles)
return fBasicPB->GetTexmap(ParamID(kThumbnail));
return NULL;
}
void plClothingMtl::SetSubTexmap(int i, Texmap *m)
{
if (i >= 0 && i < plClothingElement::kLayerMax * kMaxTiles)
fBasicPB->SetValue(ParamID(LayerToPBIdx[i / kMaxTiles]), 0, m, i % kMaxTiles);
if (i == plClothingElement::kLayerMax * kMaxTiles)
fBasicPB->SetValue(kThumbnail, 0, m);
}
TSTR plClothingMtl::GetSubTexmapSlotName(int i)
{
if (i >= 0 && i < plClothingElement::kLayerMax * kMaxTiles)
return "Texmap";
if (i == plClothingElement::kLayerMax * kMaxTiles)
return "Thumbnail";
return "";
}
TSTR plClothingMtl::GetSubTexmapTVName(int i)
{
return GetSubTexmapSlotName(i);
}
DllExport Texmap *plClothingMtl::GetTexmap(int index, int layer)
{
return fBasicPB->GetTexmap(ParamID(LayerToPBIdx[layer]), 0, index);
}
/*===========================================================================*\
| Standard IO
\*===========================================================================*/
#define MTL_HDR_CHUNK 0x4000
IOResult plClothingMtl::Save(ISave *isave)
{
IOResult res;
isave->BeginChunk(MTL_HDR_CHUNK);
res = MtlBase::Save(isave);
if (res!=IO_OK) return res;
isave->EndChunk();
return IO_OK;
}
IOResult plClothingMtl::Load(ILoad *iload)
{
IOResult res;
int id;
while (IO_OK==(res=iload->OpenChunk()))
{
switch(id = iload->CurChunkID())
{
case MTL_HDR_CHUNK:
res = MtlBase::Load(iload);
break;
}
iload->CloseChunk();
if (res!=IO_OK)
return res;
}
return IO_OK;
}
/*===========================================================================*\
| Updating and cloning
\*===========================================================================*/
RefTargetHandle plClothingMtl::Clone(RemapDir &remap)
{
plClothingMtl *mnew = TRACKED_NEW plClothingMtl(FALSE);
*((MtlBase*)mnew) = *((MtlBase*)this);
mnew->ReplaceReference(kRefBasic, remap.CloneRef(fBasicPB));
BaseClone(this, mnew, remap);
mnew->fIValid.SetEmpty();
return (RefTargetHandle)mnew;
}
void plClothingMtl::NotifyChanged()
{
NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
}
void plClothingMtl::Update(TimeValue t, Interval& valid)
{
//StdUVGen *gen = (StdUVGen *)fUVGen;
//gen->SetUScl(1.0f, t);
//gen->SetVScl(1.0f, t);
//gen->Update(t, fIValid);
valid &= fIValid;
}
/*===========================================================================*\
| Determine the characteristics of the material
\*===========================================================================*/
void plClothingMtl::SetAmbient(Color c, TimeValue t) {}
void plClothingMtl::SetDiffuse(Color c, TimeValue t) {}
void plClothingMtl::SetSpecular(Color c, TimeValue t) {}
void plClothingMtl::SetShininess(float v, TimeValue t) {}
Color plClothingMtl::GetAmbient(int mtlNum, BOOL backFace) { return Color(0,0,0); }
Color plClothingMtl::GetDiffuse(int mtlNum, BOOL backFace) { return Color(0,0,0); }
Color plClothingMtl::GetSpecular(int mtlNum, BOOL backFace) { return Color(0,0,0); }
float plClothingMtl::GetXParency(int mtlNum, BOOL backFace)
{
/*
int opacity = fBasicPB->GetInt( kOpacity, 0 );
float alpha = 1.0f - ( (float)opacity / 100.0f );
return alpha;
*/
return 0;
}
float plClothingMtl::GetShininess(int mtlNum, BOOL backFace) { return 0.0f; }
float plClothingMtl::GetShinStr(int mtlNum, BOOL backFace) { return 0.0f; }
float plClothingMtl::WireSize(int mtlNum, BOOL backFace) { return 0.0f; }
/////////////////////////////////////////////////////////////////
void plClothingMtl::SetupGfxMultiMaps(TimeValue t, Material *mtl, MtlMakerCallback &cb)
{
}
/*===========================================================================*\
| Actual shading takes place
\*===========================================================================*/
void plClothingMtl::Shade(ShadeContext& sc)
{
// Get the background color
Color backColor, backTrans;
sc.GetBGColor(backColor, backTrans);
ShadeWithBackground(sc, backColor);
}
//// Requirements ////////////////////////////////////////////////////////////
// Tells MAX what we need to render ourselves properly, such as translucency,
// two-sidedness, etc. Flags are in imtl.h in the MAX SDK.
ULONG plClothingMtl::Requirements( int subMtlNum )
{
ULONG req = 0;
req = Mtl::Requirements( subMtlNum );
// Uncomment this to get the background color fed to our ShadeWithBackground()
// (slower processing tho)
// req |= MTLREQ_BGCOL;
//int blendType = fBasicPB->GetInt( kBlend );
//if( blendType == kBlendAdd )
// req |= MTLREQ_ADDITIVE_TRANSP | MTLREQ_TRANSP;
//else if( blendType == kBlendAlpha )
// req |= MTLREQ_TRANSP;
//else if( fBasicPB->GetInt( kOpacity, 0 ) != 100 )
// req |= MTLREQ_TRANSP;
return req;
}
void plClothingMtl::ShadeWithBackground(ShadeContext &sc, Color background)
{
#if 1
TimeValue t = sc.CurTime();
Color color(0, 0, 0);
float alpha = 0.0;
// Evaluate Base layer
Texmap *map = fBasicPB->GetTexmap(kTexmap);
if (map && map->ClassID() == LAYER_TEX_CLASS_ID)
{
plLayerTex *layer = (plLayerTex*)map;
AColor evalColor = layer->EvalColor(sc);
color = evalColor;
alpha = evalColor.a;
}
#if 1
AColor black;
black.Black();
AColor white;
white.White();
SIllumParams ip;
//if( fBasicPB->GetInt( kNormal ) == kEmissive )
//{
// Emissive objects don't get shaded
// ip.diffIllum = fBasicPB->GetColor(kColorAmb, t) * color;
// ip.diffIllum.ClampMinMax();
// ip.specIllum = black;
//}
//else
{
//
// Shading setup
//
// Setup the parameters for the shader
ip.amb = black;
ip.diff = white; //fBasicPB->GetColor(kColor, t) * color;
ip.spec = white;
ip.diffIllum = black;
ip.specIllum = black;
ip.N = sc.Normal();
ip.V = sc.V();
//
// Specularity
//
ip.sh_str = 0;
ip.ph_exp = 0;
ip.shine = 0;
ip.softThresh = 0;
// Do the shading
Shader *myShader = GetShader(SHADER_BLINN);
myShader->Illum(sc, ip);
ip.diffIllum.ClampMinMax();
ip.specIllum.ClampMinMax();
ip.diffIllum = ip.amb * sc.ambientLight + ip.diff * ip.diffIllum;
}
// AColor returnColor = AColor(opac * ip.diffIllum + ip.specIllum, opac)
#endif
// Get opacity and combine with alpha
float opac = 1.0f; //float(fBasicPB->GetInt(kOpacity, t)) / 100.0f;
//float opac = 1.0f;
alpha *= opac;
// MAX will do the additive/alpha/no blending for us based on what Requirements()
// we tell it. However, since MAX's formula is bgnd*sc.out.t + sc.out.c,
// we have to multiply our output color by the alpha.
// If we ever need a more complicated blending function, you can request the
// background color via Requirements() (otherwise it's just black) and then do
// the blending yourself; however, if the transparency isn't set, the shadows
// will be opaque, so be careful.
Color outC = ip.diffIllum + ip.specIllum;
sc.out.c = ( outC * alpha );
sc.out.t = Color( 1.f - alpha, 1.f - alpha, 1.f - alpha );
#endif
}
float plClothingMtl::EvalDisplacement(ShadeContext& sc)
{
return 0.0f;
}
Interval plClothingMtl::DisplacementValidity(TimeValue t)
{
Interval iv;
iv.SetInfinite();
return iv;
}
plClothingElement *plClothingMtl::FindElementByName(char *name)
{
int i;
for (i = 0; i < fElements.GetCount(); i++)
{
if (!strcmp(fElements.Get(i)->fName, name))
return fElements.Get(i);
}
return nil;
}
void plClothingMtl::InitTilesets()
{
hsAssert(fElements.GetCount() == 0, "Tilesets already initialized");
fElements.Reset();
fTilesets.SetCountAndZero(plClothingLayout::kMaxTileset);
plClothingElement::GetElements(fElements);
/*
plClothingTileset *tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("shirt");
tileset->AddElement(FindElementByName("shirt-chest"));
tileset->AddElement(FindElementByName("shirt-sleeve"));
fTilesets[plClothingLayout::kSetShirt] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("face");
tileset->AddElement(FindElementByName("face"));
fTilesets[plClothingLayout::kSetFace] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("eye");
tileset->AddElement(FindElementByName("eyeball"));
fTilesets[plClothingLayout::kSetEye] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("shoe");
tileset->AddElement(FindElementByName("shoe-top"));
tileset->AddElement(FindElementByName("shoe-bottom"));
fTilesets[plClothingLayout::kSetShoe] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("pants");
tileset->AddElement(FindElementByName("pants"));
fTilesets[plClothingLayout::kSetPants] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("hand");
tileset->AddElement(FindElementByName("hand-LOD"));
tileset->AddElement(FindElementByName("hand-square"));
tileset->AddElement(FindElementByName("hand-wide"));
fTilesets[plClothingLayout::kSetHand] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("playerbook");
tileset->AddElement(FindElementByName("playerbook"));
fTilesets[plClothingLayout::kSetPlayerBook] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("backpack");
tileset->AddElement(FindElementByName("backpack"));
fTilesets[plClothingLayout::kSetBackpack] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("glasses");
tileset->AddElement(FindElementByName("glasses-front"));
tileset->AddElement(FindElementByName("glasses-side"));
fTilesets[plClothingLayout::kSetGlasses] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("KI");
tileset->AddElement(FindElementByName("KI"));
fTilesets[plClothingLayout::kSetKI] = tileset;
*/
plClothingTileset *tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("Torso");
tileset->AddElement(FindElementByName("Chest"));
tileset->AddElement(FindElementByName("Arm"));
fTilesets[plClothingLayout::kSetShirt] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("Head");
tileset->AddElement(FindElementByName("Eye"));
tileset->AddElement(FindElementByName("Extra Hair"));
tileset->AddElement(FindElementByName("Face"));
tileset->AddElement(FindElementByName("Hat"));
fTilesets[plClothingLayout::kSetFace] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("Foot");
tileset->AddElement(FindElementByName("Foot"));
fTilesets[plClothingLayout::kSetShoe] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("Legs");
tileset->AddElement(FindElementByName("Legs"));
fTilesets[plClothingLayout::kSetPants] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("Hand");
tileset->AddElement(FindElementByName("Finger"));
tileset->AddElement(FindElementByName("LOD"));
tileset->AddElement(FindElementByName("Palm"));
fTilesets[plClothingLayout::kSetHand] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("Player Book");
tileset->AddElement(FindElementByName("Player Book"));
fTilesets[plClothingLayout::kSetPlayerBook] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("Glasses");
tileset->AddElement(FindElementByName("Glasses"));
fTilesets[plClothingLayout::kSetGlasses] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("KI");
tileset->AddElement(FindElementByName("KI"));
fTilesets[plClothingLayout::kSetKI] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("(unused)");
fTilesets[plClothingLayout::kSetEye] = tileset;
tileset = TRACKED_NEW plClothingTileset();
tileset->SetName("(unused)");
fTilesets[plClothingLayout::kSetBackpack] = tileset;
}
void plClothingMtl::ReleaseTilesets()
{
while (fElements.GetCount() > 0)
delete fElements.Pop();
while (fTilesets.GetCount() > 0)
delete fTilesets.Pop();
}
/////////////////////////////////////////////////////////////////////////////////
plClothingTileset::plClothingTileset() : fName(nil)
{
fElements.Reset();
}
plClothingTileset::~plClothingTileset()
{
delete [] fName;
}
void plClothingTileset::AddElement(plClothingElement *element)
{
fElements.Append(element);
}
void plClothingTileset::SetName(char *name)
{
delete fName; fName = hsStrcpy(name);
}