/*==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 "plDynamicTextLayer.h" #include "iparamb2.h" #include "iparamm2.h" #include "stdmat.h" #include "plBMSampler.h" #include "../MaxMain/plPlasmaRefMsgs.h" class plDynamicTextLayerClassDesc : public ClassDesc2 { public: int IsPublic() { return TRUE; } void* Create(BOOL loading = FALSE) { return TRACKED_NEW plDynamicTextLayer(); } const TCHAR* ClassName() { return GetString(IDS_DYN_TEXT_LAYER); } SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } Class_ID ClassID() { return DYN_TEXT_LAYER_CLASS_ID; } const TCHAR* Category() { return TEXMAP_CAT_2D; } const TCHAR* InternalName() { return _T("PlasmaDynamicTextLayer"); } HINSTANCE HInstance() { return hInstance; } }; static plDynamicTextLayerClassDesc plDynamicTextLayerDesc; ClassDesc2* GetDynamicTextLayerDesc() { return &plDynamicTextLayerDesc; } #include "plDynamicTextLayerBitmapPB.cpp" ParamDlg* plDynamicTextLayer::fUVGenDlg = NULL; plDynamicTextLayer::plDynamicTextLayer() : fBitmapPB(NULL), fUVGen(NULL), fTexHandle(NULL), fTexTime(0), fIValid(NEVER) { fInitBitmap = NULL; plDynamicTextLayerDesc.MakeAutoParamBlocks(this); ReplaceReference(kRefUVGen, GetNewDefaultUVGen()); } plDynamicTextLayer::~plDynamicTextLayer() { if( fInitBitmap ) fInitBitmap->DeleteThis(); IDiscardTexHandle(); } //From MtlBase void plDynamicTextLayer::Reset() { GetDynamicTextLayerDesc()->Reset(this, TRUE); // reset all pb2's NotifyDependents(FOREVER, PART_ALL, REFMSG_CHANGE); fIValid.SetEmpty(); } void plDynamicTextLayer::Update(TimeValue t, Interval& valid) { if (!fIValid.InInterval(t)) { fIValid.SetInfinite(); fUVGen->Update(t,fIValid); fBitmapPB->GetValidity(t, fIValid); } // 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 plDynamicTextLayer::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); v &= fUVGen->Validity(t); return v; } ParamDlg* plDynamicTextLayer::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) { fIMtlParams = imp; IAutoMParamDlg* masterDlg = plDynamicTextLayerDesc.CreateParamDlgs(hwMtlEdit, imp, this); fUVGenDlg = fUVGen->CreateParamDlg(hwMtlEdit, imp); masterDlg->AddDlg(fUVGenDlg); return masterDlg; } BOOL plDynamicTextLayer::SetDlgThing(ParamDlg* dlg) { if (dlg == fUVGenDlg) { fUVGenDlg->SetThing(fUVGen); return TRUE; } return FALSE; } int plDynamicTextLayer::NumRefs() { return 2; } //From ReferenceMaker RefTargetHandle plDynamicTextLayer::GetReference(int i) { switch (i) { case kRefUVGen: return fUVGen; case kRefBitmap: return fBitmapPB; default: return NULL; } } void plDynamicTextLayer::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 textures. if (fBitmapPB) RefreshBitmaps(); break; } } int plDynamicTextLayer::NumParamBlocks() { return 1; } IParamBlock2* plDynamicTextLayer::GetParamBlock(int i) { switch (i) { case 0: return fBitmapPB; default: return NULL; } } IParamBlock2* plDynamicTextLayer::GetParamBlockByID(BlockID id) { if (fBitmapPB->ID() == id) return fBitmapPB; else return NULL; } //From ReferenceTarget RefTargetHandle plDynamicTextLayer::Clone(RemapDir &remap) { plDynamicTextLayer *mnew = TRACKED_NEW plDynamicTextLayer(); *((MtlBase*)mnew) = *((MtlBase*)this); // copy superclass stuff mnew->ReplaceReference(kRefBitmap, remap.CloneRef(fBitmapPB)); mnew->ReplaceReference(kRefUVGen, remap.CloneRef(fUVGen)); BaseClone(this, mnew, remap); return (RefTargetHandle)mnew; } int plDynamicTextLayer::NumSubs() { return 2; } Animatable* plDynamicTextLayer::SubAnim(int i) { //TODO: Return 'i-th' sub-anim switch (i) { case kRefUVGen: return fUVGen; case kRefBitmap: return fBitmapPB; default: return NULL; } } TSTR plDynamicTextLayer::SubAnimName(int i) { switch (i) { case kRefUVGen: return "UVGen"; case kRefBitmap: return fBitmapPB->GetLocalName(); default: return ""; } } RefResult plDynamicTextLayer::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; } void plDynamicTextLayer::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 IOResult plDynamicTextLayer::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 plDynamicTextLayer::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; } inline Point2 CompUV(float x, float y, float z) { return Point2( 0.5f * ( x / z + 1.0f ), 0.5f * ( y / z + 1.0f ) ); } Bitmap *plDynamicTextLayer::GetBitmap( TimeValue t ) { return fInitBitmap; } AColor plDynamicTextLayer::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( kBmpUseInitImage ) && fInitBitmap ) { plBMSampler mysamp( this, fInitBitmap ); color = fUVGen->EvalUVMap( sc, &mysamp, TRUE ); } 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; sc.PutCache(this, color); return color; } float plDynamicTextLayer::EvalMono(ShadeContext& sc) { return Intens(EvalColor(sc)); } Point3 plDynamicTextLayer::EvalNormalPerturb(ShadeContext& sc) { // Return the perturbation to apply to a normal for bump mapping return Point3(0, 0, 0); } ULONG plDynamicTextLayer::LocalRequirements(int subMtlNum) { return fUVGen->Requirements( subMtlNum ); } void plDynamicTextLayer::IDiscardTexHandle() { if (fTexHandle) { fTexHandle->DeleteThis(); fTexHandle = NULL; } } void plDynamicTextLayer::ActivateTexDisplay(BOOL onoff) { if (!onoff) IDiscardTexHandle(); } BITMAPINFO *plDynamicTextLayer::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; // Create a bitmap to write into via Windows BITMAPINFO tempBMI; memset( &tempBMI.bmiHeader, 0, sizeof( BITMAPINFOHEADER ) ); tempBMI.bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); tempBMI.bmiHeader.biWidth = fBitmapPB->GetInt( kBmpExportWidth ); tempBMI.bmiHeader.biHeight = -(int)fBitmapPB->GetInt( kBmpExportHeight ); tempBMI.bmiHeader.biPlanes = 1; tempBMI.bmiHeader.biCompression = BI_RGB; tempBMI.bmiHeader.biBitCount = 32; DWORD *bitmapBits; HDC winDC = CreateCompatibleDC( nil ); HBITMAP bitmap = CreateDIBSection( winDC, &tempBMI, DIB_RGB_COLORS, (void **)&bitmapBits, nil, 0 ); HBITMAP old = (HBITMAP)SelectObject( winDC, bitmap ); // Write into it now RECT r; SetRect( &r, 0, 0, fBitmapPB->GetInt( kBmpExportWidth ) - 1, fBitmapPB->GetInt( kBmpExportHeight ) - 1 ); HBRUSH brush = CreateSolidBrush( RGB( 255, 0, 0 ) ); FrameRect( winDC, &r, brush ); DeleteObject( brush ); SetMapMode( winDC, MM_TEXT ); SetBkMode( winDC, TRANSPARENT ); SetTextAlign( winDC, TA_TOP | TA_LEFT ); // Background letters int nHeight = -MulDiv( 72, GetDeviceCaps( winDC, LOGPIXELSY ), 72 ); HFONT winFont = CreateFont( nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, "Times New Roman" ); if( winFont != nil ) { HFONT origFont = (HFONT)SelectObject( winDC, winFont ); SetTextColor( winDC, RGB( 32, 32, 32 ) ); char str2[] = "ABCDEFG"; ::TextOut( winDC, 0, 0, str2, strlen( str2 ) ); SelectObject( winDC, origFont ); DeleteObject( winFont ); } nHeight = -MulDiv( 8, GetDeviceCaps( winDC, LOGPIXELSY ), 72 ); winFont = CreateFont( nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, "Arial" ); if( winFont != nil ) { HFONT origFont = (HFONT)SelectObject( winDC, winFont ); SetTextColor( winDC, RGB( 255, 255, 255 ) ); char str[] = "Dynamic Text"; ::TextOut( winDC, 0, 0, str, strlen( str ) ); char str3[] = "This is 8 point Arial"; ::TextOut( winDC, 0, 12, str3, strlen( str3 ) ); SelectObject( winDC, origFont ); DeleteObject( winFont ); } /// Create a MAX bitmap and copy over the data, the painful way Bitmap *maxBmp; BitmapInfo maxInfo; maxInfo.SetType( BMM_TRUE_32 ); maxInfo.SetWidth( fBitmapPB->GetInt( kBmpExportWidth ) ); maxInfo.SetHeight( fBitmapPB->GetInt( kBmpExportHeight ) ); maxInfo.SetFlags( MAP_HAS_ALPHA ); maxInfo.SetCustomFlag( 0 ); maxBmp = TheManager->Create( &maxInfo ); PixelBuf l64( fBitmapPB->GetInt( kBmpExportWidth ) ); for( int y = 0; y < fBitmapPB->GetInt( kBmpExportHeight ); y++ ) { BMM_Color_64 *p64 = l64.Ptr(); for( int x = 0; x < fBitmapPB->GetInt( kBmpExportWidth ); x++, p64++ ) { COLORREF color = GetPixel( winDC, x, y ); if( color == RGB( 0, 0, 0 ) ) { if( fBitmapPB->GetInt( kBmpUseInitImage ) && fInitBitmap != nil ) fInitBitmap->GetLinearPixels( x, y, 1, p64 ); else p64->r = p64->g = p64->b = 0.f; } else { p64->r = GetRValue( color ) << 8; p64->g = GetGValue( color ) << 8; p64->b = GetBValue( color ) << 8; } p64->a = 0xffff; } maxBmp->PutPixels( 0, y, fBitmapPB->GetInt( kBmpExportWidth ), l64.Ptr() ); } // Done with these now SelectObject( winDC, old ); DeleteObject( bitmap ); DeleteObject( winDC ); // Convert to a BITMAPINFO. Go figure. bmi = thmaker.BitmapToDIB( maxBmp, 0, xflags, forceW, forceH ); return bmi; } DWORD plDynamicTextLayer::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 *plDynamicTextLayer::GetTextureName( int which ) { PBBitmap *pbbm = fBitmapPB->GetBitmap( kBmpInitBitmap ); if( pbbm ) return pbbm->bi.Name(); return NULL; } void plDynamicTextLayer::ISetPBBitmap(PBBitmap *pbbm, int index /* = 0 */) { fBitmapPB->SetValue( (ParamID)kBmpInitBitmap, 0, pbbm ); } PBBitmap *plDynamicTextLayer::GetPBBitmap(int index /* = 0 */) { return fBitmapPB->GetBitmap( (ParamID)kBmpInitBitmap ); } //// GetSamplerInfo /////////////////////////////////////////////////////////// // Virtual function called by plBMSampler to get various things while sampling // the layer's image bool plDynamicTextLayer::GetSamplerInfo( plBMSamplerData *samplerData ) { if( fBitmapPB->GetInt( (ParamID)kBmpDiscardAlpha ) ) samplerData->fAlphaSource = plBMSamplerData::kDiscard; else samplerData->fAlphaSource = plBMSamplerData::kFromTexture; return true; }