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.
 
 
 
 
 

584 lines
14 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/>.
Additional permissions under GNU GPL version 3 section 7
If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.
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 "hsTypes.h"
#include "plCompositeMtl.h"
#include "plPassMtl.h"
//#include "plCompositeMtlPB.h"
#include "plCompositeMtlDlg.h"
class plCompositeClassDesc : public ClassDesc2
{
public:
int IsPublic() { return TRUE; }
void* Create(BOOL loading) { return TRACKED_NEW plCompositeMtl(loading); }
const TCHAR* ClassName() { return GetString(IDS_COMP_MTL); }
SClass_ID SuperClassID() { return MATERIAL_CLASS_ID; }
Class_ID ClassID() { return COMP_MTL_CLASS_ID; }
const TCHAR* Category() { return NULL; }
const TCHAR* InternalName() { return _T("PlasmaComposite"); }
HINSTANCE HInstance() { return hInstance; }
};
static plCompositeClassDesc plCompositeMtlDesc;
ClassDesc2* GetCompMtlDesc() { return &plCompositeMtlDesc; }
#include "plCompositeMtlPBDec.h"
const char *plCompositeMtl::BlendStrings[] = // Make sure these match up in order with the Blend enum (in the header)
{
"Vertex Alpha",
"Inverse Vtx Alpha",
"Vertex Illum Red",
"Inv. Vtx Illum Red",
"Vertex Illum Green",
"Inv. Vtx Illum Green",
"Vertex Illum Blue",
"Inv. Vtx Illum Blue"
};
plCompositeMtl::plCompositeMtl(BOOL loading) : fPassesPB(NULL)
{
plCompositeMtlDesc.MakeAutoParamBlocks(this);
if (!loading)
Reset();
int i;
for (i = 0; i < NSUBMTLS; i++)
{
plPassMtl *newMtl = TRACKED_NEW plPassMtl(false);
fPassesPB->SetValue(kCompPasses, 0, newMtl, i);
GetCOREInterface()->AssignNewName(fPassesPB->GetMtl(kCompPasses, 0, i));
}
}
void plCompositeMtl::Reset()
{
fIValid.SetEmpty();
}
ParamDlg* plCompositeMtl::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp)
{
fMtlDlg = TRACKED_NEW plCompositeMtlDlg(hwMtlEdit, imp, this);
return fMtlDlg;
}
void plCompositeMtl::SetParamDlg(ParamDlg *dlg)
{
fMtlDlg = (plCompositeMtlDlg*)dlg;
}
BOOL plCompositeMtl::SetDlgThing(ParamDlg* dlg)
{
if (dlg == fMtlDlg)
{
fMtlDlg->SetThing(this);
return TRUE;
}
return FALSE;
}
Interval plCompositeMtl::Validity(TimeValue t)
{
Interval valid = FOREVER;
/* for (int i = 0; i < fSubTexmap.Count(); i++)
{
if (fSubTexmap[i])
valid &= fSubTexmap[i]->Validity(t);
}
*/
// float u;
// fPBlock->GetValue(pb_spin,t,u,valid);
return valid;
}
// The index for a face on a composite material is really just a bitmask for all the submaterials. This function only
// computes it... The mesh creator will ask it to be created if it's needed.
int plCompositeMtl::ComputeMaterialIndex(float opac[][2], int vertCount)
{
int index = 0;
int i;//, j;
int bitmask = 1;
for (i = NumSubMtls() - 1, bitmask <<= i; i >= 0; i--, bitmask >>= 1)
{
index |= bitmask;
/*
if (i == 0)
index |= bitmask; // it's not opaqued out, so include the base layer
else if (fPassesPB->GetInt(kCompOn, 0, i - 1)) // is the checkbox ticked? (ie, are we using it?)
{
bool transparent = true;
for (j = 0; j < vertCount; j++)
{
if (opac[j][i - 1] != 0.0)
transparent = false;
}
if (transparent)
continue; // totally transparent for this face, skip it
index |= bitmask; // include this material
bool opaque = true;
for (j = 0; j < vertCount; j++)
{
if (opac[j][i - 1] < 1.0)
opaque = false;
}
if (opaque && !((plPassMtlBase *)fPassesPB->GetMtl(kCompPasses, 0, i))->HasAlpha())
break; // This material is totally opaque, no sense including anything underneath it
}
*/
}
return index;
}
int plCompositeMtl::GetBlendStyle(int index)
{
return fPassesPB->GetInt(kCompBlend, 0, index - 1);
}
// Determines whether all materials are only blending on one channel (and possibly its inverse) and therefore
// it's ok to overwrite the vertex alpha exported for the span using this material.
// Returns: -1 if we use multiple sources, otherwise the source we all agree on
int plCompositeMtl::CanWriteAlpha()
{
int blend[3];
blend[0] = ((((plPassMtlBase *)GetSubMtl(0))->GetOutputBlend() == plPassMtlBase::kBlendNone) ? -1 : kCompBlendVertexAlpha);
blend[1] = (fPassesPB->GetInt(kCompOn, 0, 0) ? RemoveInverse(GetBlendStyle(1)) : -1);
blend[2] = (fPassesPB->GetInt(kCompOn, 0, 1) ? RemoveInverse(GetBlendStyle(2)) : -1);
int source = blend[0];
int i;
for (i = 1; i < 3; i++)
{
if (source < 0)
{
source = blend[i];
continue;
}
if (source >= 0 && blend[i] >= 0 && blend[i] != source)
return -1;
}
return source;
}
/*===========================================================================*\
| Subanim & References support
\*===========================================================================*/
int plCompositeMtl::NumSubs()
{
return NumSubMtls();
}
TSTR plCompositeMtl::SubAnimName(int i)
{
return GetSubMtlSlotName(i);
}
Animatable* plCompositeMtl::SubAnim(int i)
{
return GetSubMtl(i);
}
int plCompositeMtl::NumRefs()
{
return 1;
}
RefTargetHandle plCompositeMtl::GetReference(int i)
{
if (i == kRefPasses)
return fPassesPB;
return NULL;
}
void plCompositeMtl::SetReference(int i, RefTargetHandle rtarg)
{
if (i == kRefPasses)
fPassesPB = (IParamBlock2 *)rtarg;
}
int plCompositeMtl::NumParamBlocks()
{
return 1;
}
IParamBlock2 *plCompositeMtl::GetParamBlock(int i)
{
if (i == kRefPasses)
return fPassesPB;
return NULL;
}
IParamBlock2 *plCompositeMtl::GetParamBlockByID(BlockID id)
{
if (fPassesPB->ID() == id)
return fPassesPB;
return NULL;
}
RefResult plCompositeMtl::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget,
PartID& partID, RefMessage message )
{
switch (message)
{
case REFMSG_CHANGE:
fIValid.SetEmpty();
if (hTarget == fPassesPB)
{
ParamID changingParam = fPassesPB->LastNotifyParamID();
fPassesPB->GetDesc()->InvalidateUI(changingParam);
}
break;
}
return REF_SUCCEED;
}
////////////////////////////////////////////////////////////////////////////////
// Subtexmap access
int plCompositeMtl::NumSubMtls()
{
// return fPassesPB->GetInt(kMultCount);
return NSUBMTLS;
}
Mtl *plCompositeMtl::GetSubMtl(int i)
{
if (i < NumSubMtls())
return fPassesPB->GetMtl(kCompPasses, 0, i);
return NULL;
}
void plCompositeMtl::SetSubMtl(int i, Mtl *m)
{
if (i < NumSubMtls())
fPassesPB->SetValue(kCompPasses, 0, m, i);
}
TSTR plCompositeMtl::GetSubMtlSlotName(int i)
{
TSTR str;
str.printf("Pass %d", i+1);
return str;
}
TSTR plCompositeMtl::GetSubMtlTVName(int i)
{
return GetSubMtlSlotName(i);
}
/*===========================================================================*\
| Standard IO
\*===========================================================================*/
#define MTL_HDR_CHUNK 0x4000
IOResult plCompositeMtl::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 plCompositeMtl::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 plCompositeMtl::Clone(RemapDir &remap)
{
plCompositeMtl *mnew = TRACKED_NEW plCompositeMtl(FALSE);
*((MtlBase*)mnew) = *((MtlBase*)this);
mnew->ReplaceReference(kRefPasses, remap.CloneRef(fPassesPB));
mnew->fIValid.SetEmpty();
BaseClone(this, mnew, remap);
return (RefTargetHandle)mnew;
}
void plCompositeMtl::NotifyChanged()
{
NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE);
}
void plCompositeMtl::Update(TimeValue t, Interval& valid)
{
if (!fIValid.InInterval(t))
{
fIValid.SetInfinite();
// fPassesPB->GetValue(kMtlLayLayer1On, t, fMapOn[0], fIValid);
for (int i = 0; i < NumSubMtls(); i++)
{
if (GetSubMtl(i))
GetSubMtl(i)->Update(t, fIValid);
}
}
valid &= fIValid;
}
/*===========================================================================*\
| Determine the characteristics of the material
\*===========================================================================*/
void plCompositeMtl::SetAmbient(Color c, TimeValue t) {}
void plCompositeMtl::SetDiffuse(Color c, TimeValue t) {}
void plCompositeMtl::SetSpecular(Color c, TimeValue t) {}
void plCompositeMtl::SetShininess(float v, TimeValue t) {}
Color plCompositeMtl::GetAmbient(int mtlNum, BOOL backFace) { return Color(0,0,0); }
Color plCompositeMtl::GetDiffuse(int mtlNum, BOOL backFace) { return Color(0,0,0); }
Color plCompositeMtl::GetSpecular(int mtlNum, BOOL backFace) { return Color(0,0,0); }
float plCompositeMtl::GetXParency(int mtlNum, BOOL backFace) { return 0.0f; }
float plCompositeMtl::GetShininess(int mtlNum, BOOL backFace) { return 0.0f; }
float plCompositeMtl::GetShinStr(int mtlNum, BOOL backFace) { return 0.0f; }
float plCompositeMtl::WireSize(int mtlNum, BOOL backFace) { return 0.0f; }
/*===========================================================================*\
| Actual shading takes place
\*===========================================================================*/
void plCompositeMtl::Shade(ShadeContext& sc)
{
// Get the background color
Color backColor, backTrans;
//sc.GetBGColor(backColor, backTrans);
backColor.Black();
backTrans.White();
Point3 vtxIllum, vtxAlpha;
plPassMtl::GetInterpVtxValue(MAP_ALPHA, sc, vtxAlpha);
plPassMtl::GetInterpVtxValue(MAP_SHADING, sc, vtxIllum);
int count = NumSubMtls();
for (int i = 0; i < count; i++)
{
if (i > 0 && fPassesPB->GetInt(kCompOn, 0, i - 1) == 0) // Material is unchecked, skip it.
continue;
Mtl *mtl = GetSubMtl(i);
if (mtl == NULL || mtl->ClassID() != PASS_MTL_CLASS_ID)
continue;
float opacity;
if (i == 0)
{
opacity = 1.0f;
}
else
{
int blendMethod = fPassesPB->GetInt(kCompBlend, 0, i - 1);
switch(blendMethod)
{
case kCompBlendVertexAlpha:
case kCompBlendInverseVtxAlpha:
opacity = vtxAlpha.x;
break;
case kCompBlendVertexIllumRed:
case kCompBlendInverseVtxIllumRed:
opacity = vtxIllum.x;
break;
case kCompBlendVertexIllumGreen:
case kCompBlendInverseVtxIllumGreen:
opacity = vtxIllum.y;
break;
case kCompBlendVertexIllumBlue:
case kCompBlendInverseVtxIllumBlue:
opacity = vtxIllum.z;
break;
default:
opacity = 1.0f;
break;
}
if (IsInverseBlend(blendMethod))
opacity = 1 - opacity;
}
plPassMtl *passMtl = (plPassMtl*)mtl;
passMtl->ShadeWithBackground(sc, backColor, false); // Don't include the vtx alpha, that's OUR job
float currTrans = (1 - (1 - sc.out.t.r) * opacity);
backTrans *= currTrans;
backColor = backColor * currTrans + sc.out.c * opacity;
}
sc.out.t = backTrans;
sc.out.c = backColor;
}
float plCompositeMtl::EvalDisplacement(ShadeContext& sc)
{
return 0.0f;
}
Interval plCompositeMtl::DisplacementValidity(TimeValue t)
{
Interval iv;
iv.SetInfinite();
return iv;
}
/*
void plCompositeMtl::SetNumSubMtls(int num)
{
TimeValue t = GetCOREInterface()->GetTime();
int curNum = fPassesPB->GetInt(kMultCount);
fPassesPB->SetValue(kMultCount, 0, num);
fPassesPB->SetCount(kMultPasses, num);
fPassesPB->SetCount(kMultOn, num);
for (int i = curNum; i < num; i++)
{
plPassMtl *newMtl = TRACKED_NEW plPassMtl(false);
fPassesPB->SetValue(kMultPasses, t, newMtl, i);
fPassesPB->SetValue(kMultOn, t, TRUE, i);
GetCOREInterface()->AssignNewName(fPassesPB->GetMtl(kMultPasses, t, i));
}
}
*/
void plCompositeMtl::SetOpacityVal(float *target, UVVert *alpha, UVVert *illum, int method)
{
if (method == kCompBlendVertexAlpha || method == kCompBlendInverseVtxAlpha)
{
if (alpha == NULL)
*target = 1.0f;
else
*target = alpha->x;
}
else if (method == kCompBlendVertexIllumRed || method == kCompBlendInverseVtxIllumRed)
{
if (illum == NULL)
*target = 1.0f;
else
*target = illum->x;
}
else if (method == kCompBlendVertexIllumGreen || method == kCompBlendInverseVtxIllumGreen)
{
if (illum == NULL)
*target = 1.0f;
else
*target = illum->y;
}
else if (method == kCompBlendVertexIllumBlue || method == kCompBlendInverseVtxIllumBlue)
{
if (illum == NULL)
*target = 1.0f;
else
*target = illum->z;
}
else
{
*target = 1.0f;
}
if (method == kCompBlendInverseVtxAlpha ||
method == kCompBlendInverseVtxIllumRed ||
method == kCompBlendInverseVtxIllumGreen ||
method == kCompBlendInverseVtxIllumBlue)
{
*target = 1.0f - *target;
}
}
/*
int plCompositeMtl::UVChannelsNeeded(bool makeAlphaLayer)
{
if (makeAlphaLayer)
return 1;
// Otherwise, we do have vertex alpha... can we get by without taking up a UV channel?
int channels = 0;
int i;
for (i = 0; i < NumSubMtls() - 1; i++)
{
if (!fPassesPB->GetInt(kCompOn, 0, i))
continue; // The material is unchecked, no need to see if it needs a channel
int method = fPassesPB->GetInt(kCompBlend, 0, i);
if (!(method == kCompBlendVertexAlpha || method1 == kCompBlendInverseVtxAlpha))
{
channels = 1;
break;
}
}
return channels;
}
*/