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.

560 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/>.
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;
}