/*==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 . 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 "plLayerTex.h" #include "iparamb2.h" #include "iparamm2.h" #include "stdmat.h" #include "plBMSampler.h" #include "../resource.h" #include "plLayerTexBitmapPB.h" #include "../MaxMain/plPlasmaRefMsgs.h" class plLayerTexClassDesc : public ClassDesc2 { public: int IsPublic() { return TRUE; } void* Create(BOOL loading = FALSE) { return TRACKED_NEW plLayerTex(); } const TCHAR* ClassName() { return GetString(IDS_LAYER); } SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } Class_ID ClassID() { return LAYER_TEX_CLASS_ID; } const TCHAR* Category() { return TEXMAP_CAT_2D; } const TCHAR* InternalName() { return _T("PlasmaLayer"); } HINSTANCE HInstance() { return hInstance; } }; static plLayerTexClassDesc plLayerTexDesc; ClassDesc2* GetLayerTexDesc() { return &plLayerTexDesc; } ParamDlg* plLayerTex::fUVGenDlg = NULL; // For initializing paramblock descriptor //ParamBlockDesc2 *GetBasicBlk(); ParamBlockDesc2 *GetBitmapBlk(); //#include "plLayerTexBasicPB.cpp" #include "plLayerTexBitmapPB.cpp" void plLayerTex::GetClassName( TSTR &s ) { s = GetString( IDS_LAYER ); } plLayerTex::plLayerTex() : fBitmapPB(NULL), //fBasicPB(NULL), fUVGen(NULL), fTexHandle(NULL), fTexTime(0), fBM(NULL), fIValid(NEVER) { #if 0 // Initialize the paramblock descriptors only once static bool descInit = false; if (!descInit) { descInit = true; //GetBasicBlk()->SetClassDesc(GetLayerTexDesc()); GetBitmapBlk()->SetClassDesc(GetLayerTexDesc()); } #endif plLayerTexDesc.MakeAutoParamBlocks(this); ReplaceReference(kRefUVGen, GetNewDefaultUVGen()); } plLayerTex::~plLayerTex() { if (fBM) fBM->DeleteThis(); IDiscardTexHandle(); } //From MtlBase void plLayerTex::Reset() { GetLayerTexDesc()->Reset(this, TRUE); // reset all pb2's SetBitmap(NULL); fIValid.SetEmpty(); } void plLayerTex::Update(TimeValue t, Interval& valid) { if (!fIValid.InInterval(t)) { fIValid.SetInfinite(); fUVGen->Update(t,fIValid); fBitmapPB->GetValidity(t, fIValid); // Interval clipValid; // clipValid.SetInfinite(); // float temp; // fBitmapPB->GetValue(kBmpClipU, t, temp, clipValid); // fBitmapPB->GetValue(kBmpClipV, t, temp, clipValid); // fBitmapPB->GetValue(kBmpClipW, t, temp, clipValid); // fBitmapPB->GetValue(kBmpClipH, t, temp, clipValid); } // Gonna need to do this when we support animated bm's #if 0 if (fBM) { if (bi.FirstFrame()!=bi.LastFrame()) ivalid.SetInstant(t); } #endif valid &= fIValid; } Interval plLayerTex::Validity(TimeValue t) { //TODO: Update fIValid here // mf horse - Hacking this in just to get animations working. // No warranty on this not being stupid. Interval v = FOREVER; fBitmapPB->GetValidity(t, v); //fBasicPB->GetValidity(t, v); v &= fUVGen->Validity(t); return v; } ParamDlg* plLayerTex::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) { fMtlParams = imp; IAutoMParamDlg* masterDlg = plLayerTexDesc.CreateParamDlgs(hwMtlEdit, imp, this); fUVGenDlg = fUVGen->CreateParamDlg(hwMtlEdit, imp); masterDlg->AddDlg(fUVGenDlg); return masterDlg; } BOOL plLayerTex::SetDlgThing(ParamDlg* dlg) { if (dlg == fUVGenDlg) { fUVGenDlg->SetThing(fUVGen); return TRUE; } return FALSE; } int plLayerTex::NumRefs() { return 3; } //From ReferenceMaker RefTargetHandle plLayerTex::GetReference(int i) { switch (i) { case kRefUVGen: return fUVGen; case kRefBitmap: return fBitmapPB; //case kRefBasic: return fBasicPB; default: return NULL; } } void plLayerTex::SetReference(int i, RefTargetHandle rtarg) { Interval garbage; switch (i) { case kRefUVGen: fUVGen = (UVGen *)rtarg; if( fUVGen ) fUVGen->Update( TimeValue( 0 ), garbage ); break; case kRefBitmap: fBitmapPB = (IParamBlock2 *)rtarg; // KLUDGE: If the paramblock is being set chances are we are being created or // loaded. In the case of load, we want to refresh our texture. if (fBitmapPB) RefreshBitmaps(); break; } } int plLayerTex::NumParamBlocks() { return 2; } IParamBlock2* plLayerTex::GetParamBlock(int i) { switch (i) { case 0: return fBitmapPB; //case 1: return fBasicPB; default: return NULL; } } IParamBlock2* plLayerTex::GetParamBlockByID(BlockID id) { if (fBitmapPB->ID() == id) return fBitmapPB; //else if (fBasicPB->ID() == id) // return fBasicPB; else return NULL; } //From ReferenceTarget RefTargetHandle plLayerTex::Clone(RemapDir &remap) { plLayerTex *mnew = TRACKED_NEW plLayerTex(); *((MtlBase*)mnew) = *((MtlBase*)this); // copy superclass stuff //mnew->ReplaceReference(kRefBasic, remap.CloneRef(fBasicPB)); mnew->ReplaceReference(kRefBitmap, remap.CloneRef(fBitmapPB)); mnew->ReplaceReference(kRefUVGen, remap.CloneRef(fUVGen)); BaseClone(this, mnew, remap); return (RefTargetHandle)mnew; } int plLayerTex::NumSubs() { return 3; } Animatable* plLayerTex::SubAnim(int i) { //TODO: Return 'i-th' sub-anim switch (i) { case kRefUVGen: return fUVGen; case kRefBitmap: return fBitmapPB; //case kRefBasic: return fBasicPB; default: return NULL; } } TSTR plLayerTex::SubAnimName(int i) { switch (i) { case kRefUVGen: return "UVGen"; case kRefBitmap: return fBitmapPB->GetLocalName(); //case kRefBasic: return fBasicPB->GetLocalName(); default: return ""; } } RefResult plLayerTex::NotifyRefChanged(Interval changeInt, RefTargetHandle hTarget, PartID& partID, RefMessage message) { switch (message) { case REFMSG_CHANGE: { fIValid.SetEmpty(); if (hTarget == fBitmapPB) { // 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 changingParam = fBitmapPB->LastNotifyParamID(); fBitmapPB->GetDesc()->InvalidateUI(changingParam); if (changingParam != -1) IChanged(); } } break; case REFMSG_UV_SYM_CHANGE: IDiscardTexHandle(); break; } return REF_SUCCEED; } BOOL plLayerTex::DiscardColor() { return fBitmapPB->GetInt(kBmpDiscardColor); } BOOL plLayerTex::DiscardAlpha() { return fBitmapPB->GetInt(kBmpDiscardAlpha); } void plLayerTex::IChanged() { IDiscardTexHandle(); // Texture wasn't getting updated in the viewports, and this fixes it. // Don't know if it's the right way though. NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); // And this is so the SceneWatcher gets notified that the material on some of it's // referenced objects changed. NotifyDependents(FOREVER, PART_ALL, REFMSG_USER_MAT); } #define TEX_HDR_CHUNK 0x5000 #define MAX_ASS_CHUNK 0x5500 IOResult plLayerTex::Save(ISave *isave) { IOResult res; isave->BeginChunk(TEX_HDR_CHUNK); res = MtlBase::Save(isave); if (res != IO_OK) return res; isave->EndChunk(); return IO_OK; } IOResult plLayerTex::Load(ILoad *iload) { IOResult res; while (IO_OK == (res = iload->OpenChunk())) { if (iload->CurChunkID() == TEX_HDR_CHUNK) { res = MtlBase::Load(iload); } iload->CloseChunk(); if (res != IO_OK) return res; } return IO_OK; } bool plLayerTex::HasAlpha() { return (fBM != NULL && fBM->HasAlpha() != 0); } Bitmap* plLayerTex::GetBitmap(TimeValue t) { return fBM; } AColor plLayerTex::EvalColor(ShadeContext& sc) { if (!sc.doMaps) return AColor(0.0f, 0.0f, 0.0f, 1.0f); AColor color; if (sc.GetCache(this, color)) return color; if (gbufID) sc.SetGBufferID(gbufID); // // Evaluate the Bitmap // if (fBitmapPB->GetInt(kBmpUseBitmap) && fBM) { plBMSampler mysamp(this, fBM); color = fUVGen->EvalUVMap(sc, &mysamp, FALSE); // We'd like to pass TRUE and actually filter the image, but that seems to be // tripping an odd crash in Max internals. *shrug* } else color.White(); // Invert color if specified if (fBitmapPB->GetInt(kBmpInvertColor)) { color.r = 1.0f - color.r; color.g = 1.0f - color.g; color.b = 1.0f - color.b; } // Discard color if specified if (fBitmapPB->GetInt(kBmpDiscardColor)) color.r = color.g = color.b = 1.0f; // Invert alpha if specified if (fBitmapPB->GetInt(kBmpInvertAlpha)) color.a = 1.0f - color.a; // Discard alpha if specified if (fBitmapPB->GetInt(kBmpDiscardAlpha)) color.a = 1.0f; // If RGB output is set to alpha, show RGB as grayscale of the alpha if (fBitmapPB->GetInt(kBmpRGBOutput) == 1) color = AColor(color.a, color.a, color.a, 1.0f); sc.PutCache(this, color); return color; } float plLayerTex::EvalMono(ShadeContext& sc) { if (fBitmapPB->GetInt(kBmpMonoOutput) == 1) return EvalColor(sc).a; return Intens(EvalColor(sc)); } Point3 plLayerTex::EvalNormalPerturb(ShadeContext& sc) { // Return the perturbation to apply to a normal for bump mapping return Point3(0, 0, 0); } ULONG plLayerTex::LocalRequirements(int subMtlNum) { return fUVGen->Requirements(subMtlNum); } #if 0 int plLayerTex::ICalcFrame(TimeValue t) { PBBitmap *pbbm = fBitmapPB->GetBitmap(kBmpBitmap); if (!pbbm || !pbbm->bi) return 0; BitmapInfo *bi = pbbm->bi; TimeValue tm, dur, td; int frameStart = bi->FirstFrame(); int frameEnd = bi->LastFrame(); int tpf = GetTicksPerFrame(); tm = TimeValue(float(t - startTime) * pbRate); dur = (fend-fstart+1)*GetTicksPerFrame(); switch (endCond) { case END_HOLD: if (tm <= 0) return frameStart; if (tm >= dur) return frameEnd; return tm/tpf; case END_PINGPONG: if (((tm >= 0) && ((tm / dur) & 1)) || ((tm < 0) && !(tm / dur))) { td = modt(tm, dur); return frameStart + frameEnd - td / tpf; } // else fall through case END_LOOP: td = modt(tm, dur); return td / tpf; } return 0; } #endif void plLayerTex::IDiscardTexHandle() { if (fTexHandle) { fTexHandle->DeleteThis(); fTexHandle = NULL; } } void plLayerTex::ActivateTexDisplay(BOOL onoff) { if (!onoff) IDiscardTexHandle(); } BITMAPINFO *plLayerTex::GetVPDisplayDIB(TimeValue t, TexHandleMaker& thmaker, Interval &valid, BOOL mono, BOOL forceW, BOOL forceH) { // FIXME fTexTime = 0;//CalcFrame(t); // texValid = clipValid; BITMAPINFO *bmi = NULL; int xflags = 0; if (fBitmapPB->GetInt(kBmpApply)) { float clipu = fBitmapPB->GetFloat(kBmpClipU); float clipv = fBitmapPB->GetFloat(kBmpClipV); float clipw = fBitmapPB->GetFloat(kBmpClipW); float cliph = fBitmapPB->GetFloat(kBmpClipH); int discardAlpha = fBitmapPB->GetInt(kBmpDiscardAlpha); int alphaAsRGB = (fBitmapPB->GetInt(kBmpRGBOutput) == 1); int w = fBM->Width(); int h = fBM->Height(); Bitmap *newBM; BitmapInfo bi; bi.SetName(_T("y8798734")); bi.SetType(BMM_TRUE_32); bi.SetFlags(MAP_HAS_ALPHA); if (fBitmapPB->GetInt(kBmpCropPlace) == 1) { int x0, y0, nw, nh; int bmw = thmaker.Size(); int bmh = int(float(bmw)*float(h)/float(w)); bi.SetWidth(bmw); bi.SetHeight(bmh); newBM = TheManager->Create(&bi); newBM->Fill(0,0,0,0); nw = int(float(bmw)*clipw); nh = int(float(bmh)*cliph); x0 = int(float(bmw-1)*clipu); y0 = int(float(bmh-1)*clipv); if (nw<1) nw = 1; if (nh<1) nh = 1; PixelBuf row(nw); Bitmap *tmpBM; BitmapInfo bif2; bif2.SetName(_T("xxxx67878")); bif2.SetType(BMM_TRUE_32); bif2.SetFlags(MAP_HAS_ALPHA); bif2.SetWidth(nw); bif2.SetHeight(nh); tmpBM = TheManager->Create(&bif2); tmpBM->CopyImage(fBM, COPY_IMAGE_RESIZE_LO_QUALITY, 0); BMM_Color_64* p1 = row.Ptr(); for (int y = 0; yGetLinearPixels(0,y, nw, p1); if (alphaAsRGB) { for (int ix =0; ixPutPixels(x0, y+y0, nw, p1); } tmpBM->DeleteThis(); bmi = thmaker.BitmapToDIB(newBM, fUVGen->SymFlags(), xflags, forceW, forceH); newBM->DeleteThis(); } else { int x0,y0,nw,nh; x0 = int(float(w-1)*clipu); y0 = int(float(h-1)*clipv); nw = int(float(w)*clipw); nh = int(float(h)*cliph); if (nw<1) nw = 1; if (nh<1) nh = 1; bi.SetWidth(nw); bi.SetHeight(nh); PixelBuf row(nw); newBM = TheManager->Create(&bi); BMM_Color_64* p1 = row.Ptr(); for (int y = 0; yGetLinearPixels(x0,y+y0, nw, p1); if (alphaAsRGB) { for (int ix = 0; ix < nw; ix++) p1[ix].r = p1[ix].g = p1[ix].b = p1[ix].a; } if (discardAlpha) { for (int ix = 0; ix < nw; ix++) p1[ix].a = 0xffff; } newBM->PutPixels(0, y, nw, p1); } bmi = thmaker.BitmapToDIB(newBM, fUVGen->SymFlags(), xflags, forceW, forceH); newBM->DeleteThis(); } } else { if (fBitmapPB->GetInt(kBmpRGBOutput) == 1) xflags |= EX_RGB_FROM_ALPHA; bmi = thmaker.BitmapToDIB(fBM, fUVGen->SymFlags(), xflags, forceW, forceH); } return bmi; } DWORD plLayerTex::GetActiveTexHandle(TimeValue t, TexHandleMaker& thmaker) { // FIXME: ignore validity for now if (fTexHandle && fIValid.InInterval(t))// && texTime == CalcFrame(t)) return fTexHandle->GetHandle(); else { IDiscardTexHandle(); fTexTime = 0;//CalcFrame(t); fTexHandle = thmaker.MakeHandle(GetVPDisplayDIB(t, thmaker, fIValid)); if (fTexHandle) return fTexHandle->GetHandle(); else return 0; } } const char *plLayerTex::GetTextureName() { // if (fBitmapPB->GetInt(kBmpUseBitmap)) { PBBitmap *pbbm = fBitmapPB->GetBitmap(kBmpBitmap); if (pbbm) return pbbm->bi.Name(); } return NULL; } void plLayerTex::ISetPBBitmap(PBBitmap *pbbm, int index /* = 0 */) { fBitmapPB->SetValue(ParamID(kBmpBitmap), 0, pbbm, index); } PBBitmap *plLayerTex::GetPBBitmap(int index /* = 0 */) { return fBitmapPB->GetBitmap(ParamID(kBmpBitmap)); } //// GetSamplerInfo /////////////////////////////////////////////////////////// // Virtual function called by plBMSampler to get various things while sampling // the layer's image bool plLayerTex::GetSamplerInfo( plBMSamplerData *samplerData ) { samplerData->fClipU = fBitmapPB->GetFloat( (ParamID)kBmpClipU ); samplerData->fClipV = fBitmapPB->GetFloat( (ParamID)kBmpClipV ); samplerData->fClipW = fBitmapPB->GetFloat( (ParamID)kBmpClipW ); samplerData->fClipH = fBitmapPB->GetFloat( (ParamID)kBmpClipH ); samplerData->fEnableCrop = fBitmapPB->GetInt( (ParamID)kBmpApply ) ? true : false; samplerData->fCropPlacement = fBitmapPB->GetInt( (ParamID)kBmpCropPlace ); if( fBitmapPB->GetInt( (ParamID)kBmpDiscardAlpha ) ) samplerData->fAlphaSource = plBMSamplerData::kDiscard; else if( fBitmapPB->GetInt( (ParamID)kBmpRGBOutput ) == 1 ) samplerData->fAlphaSource = plBMSamplerData::kFromRGB; else samplerData->fAlphaSource = plBMSamplerData::kFromTexture; return true; } void plLayerTex::SetExportSize(int x, int y) { fBitmapPB->SetValue(kBmpExportWidth, 0, x); fBitmapPB->SetValue(kBmpExportLastWidth, 0, x); fBitmapPB->SetValue(kBmpExportHeight, 0, y); fBitmapPB->SetValue(kBmpExportLastHeight, 0, y); }