/*==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 "plStaticEnvLayer.h" #include "iparamb2.h" #include "iparamm2.h" #include "stdmat.h" #include "plBMSampler.h" #include "../MaxMain/plPlasmaRefMsgs.h" class plStaticEnvLayerClassDesc : public ClassDesc2 { public: int IsPublic() { return TRUE; } void* Create(BOOL loading = FALSE) { return TRACKED_NEW plStaticEnvLayer(); } const TCHAR* ClassName() { return GetString(IDS_STATIC_ENVMAP_LAYER); } SClass_ID SuperClassID() { return TEXMAP_CLASS_ID; } Class_ID ClassID() { return STATIC_ENV_LAYER_CLASS_ID; } const TCHAR* Category() { return TEXMAP_CAT_ENV; } const TCHAR* InternalName() { return _T("PlasmaStaticEnvMapLayer"); } HINSTANCE HInstance() { return hInstance; } }; static plStaticEnvLayerClassDesc plStaticEnvLayerDesc; ClassDesc2* GetStaticEnvLayerDesc() { return &plStaticEnvLayerDesc; } #include "plStaticEnvLayerBitmapPB.cpp" plStaticEnvLayer::plStaticEnvLayer() : fBitmapPB(NULL), fUVGen(NULL), fTexHandle(NULL), fTexTime(0), fIValid(NEVER) { int i; for( i = 0; i < 6; i++ ) { fBitmaps[ i ] = NULL; } plStaticEnvLayerDesc.MakeAutoParamBlocks(this); ReplaceReference(kRefUVGen, GetNewDefaultUVGen()); } plStaticEnvLayer::~plStaticEnvLayer() { int i; for( i = 0; i < 6; i++ ) { if( fBitmaps[ i ] ) fBitmaps[ i ]->DeleteThis(); } IDiscardTexHandle(); } //From MtlBase void plStaticEnvLayer::Reset() { GetStaticEnvLayerDesc()->Reset(this, TRUE); // reset all pb2's for( int i = 0; i < 6; i++ ) { SetBitmap( NULL, i ); } fIValid.SetEmpty(); } void plStaticEnvLayer::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 plStaticEnvLayer::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* plStaticEnvLayer::CreateParamDlg(HWND hwMtlEdit, IMtlParams *imp) { fIMtlParams = imp; IAutoMParamDlg* masterDlg = plStaticEnvLayerDesc.CreateParamDlgs(hwMtlEdit, imp, this); SELBitmapDlgProc *paramDlg = (SELBitmapDlgProc *)gBitmapParamBlk.GetUserDlgProc(); if( paramDlg ) paramDlg->fMtlParams = imp; return masterDlg; } BOOL plStaticEnvLayer::SetDlgThing(ParamDlg* dlg) { return FALSE; } int plStaticEnvLayer::NumRefs() { return 2; } //From ReferenceMaker RefTargetHandle plStaticEnvLayer::GetReference(int i) { switch (i) { case kRefUVGen: return fUVGen; case kRefBitmap: return fBitmapPB; default: return NULL; } } void plStaticEnvLayer::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 plStaticEnvLayer::NumParamBlocks() { return 1; } IParamBlock2* plStaticEnvLayer::GetParamBlock(int i) { switch (i) { case 0: return fBitmapPB; default: return NULL; } } IParamBlock2* plStaticEnvLayer::GetParamBlockByID(BlockID id) { if (fBitmapPB->ID() == id) return fBitmapPB; else return NULL; } //From ReferenceTarget RefTargetHandle plStaticEnvLayer::Clone(RemapDir &remap) { plStaticEnvLayer *mnew = TRACKED_NEW plStaticEnvLayer(); *((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 plStaticEnvLayer::NumSubs() { return 2; } Animatable* plStaticEnvLayer::SubAnim(int i) { //TODO: Return 'i-th' sub-anim switch (i) { case kRefUVGen: return fUVGen; case kRefBitmap: return fBitmapPB; default: return NULL; } } TSTR plStaticEnvLayer::SubAnimName(int i) { switch (i) { case kRefUVGen: return "UVGen"; case kRefBitmap: return fBitmapPB->GetLocalName(); default: return ""; } } RefResult plStaticEnvLayer::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 plStaticEnvLayer::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 plStaticEnvLayer::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 plStaticEnvLayer::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 ) ); } AColor plStaticEnvLayer::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 // Point3 v = sc.VectorTo( sc.V(), REF_OBJECT );//WORLD ); Point3 v = sc.VectorTo( sc.Normal(), REF_OBJECT ); float wx,wy,wz; Color rcol; Bitmap *refmap = NULL; Point3 rv; Point2 uv; int size; wx = (float)fabs( v.x ); wy = (float)fabs( v.y ); wz = (float)fabs( v.z ); if( wx >= wy && wx >= wz ) { if( v.x < 0 ) { refmap = fBitmaps[ kLeftFace ]; uv = CompUV( -v.y, -v.z, v.x ); } else { refmap = fBitmaps[ kRightFace ]; uv = CompUV( v.y, -v.z, -v.x ); } } else if( wy >= wx && wy >= wz ) { if( v.y > 0 ) { refmap = fBitmaps[ kBackFace ]; uv = CompUV( -v.x, -v.z, -v.y ); } else { refmap = fBitmaps[ kFrontFace ]; uv = CompUV( v.x, -v.z, v.y ); } } else if( wz >= wx && wz >= wy ) { if( v.z < 0 ) { refmap = fBitmaps[ kBottomFace ]; uv = CompUV( -v.x, -v.y, v.z ); } else { refmap = fBitmaps[ kTopFace ]; uv = CompUV( -v.x, v.y, -v.z ); } } if( refmap == NULL ) color.White(); else { if( uv.x < 0.0f ) uv.x = 0.0f; else if( uv.x > 1.0f ) uv.x = 1.0f; if( uv.y < 0.0f ) uv.y = 0.0f; else if( uv.y > 1.0f ) uv.y = 1.0f; size = refmap->Width(); int x = (int)( uv.x * (float)( size - 1 ) ); int y = (int)( ( 1.0f - uv.y ) * (float)( size - 1 ) ); BMM_Color_64 c; refmap->GetLinearPixels( x, y, 1, &c ); color = AColor( c.r / 65535.f, c.g / 65535.f, c.b / 65535.f, c.a / 65535.f ); } // 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 plStaticEnvLayer::EvalMono(ShadeContext& sc) { if (fBitmapPB->GetInt(kBmpMonoOutput) == 1) return EvalColor(sc).a; return Intens(EvalColor(sc)); } Point3 plStaticEnvLayer::EvalNormalPerturb(ShadeContext& sc) { // Return the perturbation to apply to a normal for bump mapping return Point3(0, 0, 0); } ULONG plStaticEnvLayer::LocalRequirements(int subMtlNum) { if( fBitmapPB->GetInt( kBmpUseMAXAtmosphere ) ) return MTLREQ_VIEW_DEP; return MTLREQ_VIEW_DEP | MTLREQ_NOATMOS; } void plStaticEnvLayer::IDiscardTexHandle() { if (fTexHandle) { fTexHandle->DeleteThis(); fTexHandle = NULL; } } void plStaticEnvLayer::ActivateTexDisplay(BOOL onoff) { if (!onoff) IDiscardTexHandle(); } BITMAPINFO *plStaticEnvLayer::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(kBmpRGBOutput) == 1) xflags |= EX_RGB_FROM_ALPHA; bmi = thmaker.BitmapToDIB(fBitmaps[ 0 ], fUVGen->SymFlags(), xflags, forceW, forceH); return bmi; } DWORD plStaticEnvLayer::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 *plStaticEnvLayer::GetTextureName( int which ) { // if (fBitmapPB->GetInt(kBmpUseBitmap)) { PBBitmap *pbbm = fBitmapPB->GetBitmap( kBmpFrontBitmap + which ); if (pbbm) return pbbm->bi.Name(); } return NULL; } //// Set/GetBaseFilename ////////////////////////////////////////////////////// void plStaticEnvLayer::SetBaseFilename( const TCHAR *name, TimeValue t ) { fBitmapPB->SetValue( kBmpBaseFilename, t, (TCHAR *)name ); } const TCHAR *plStaticEnvLayer::GetBaseFilename( TimeValue t ) { Interval valid; TCHAR *buffer; fBitmapPB->GetValue( kBmpBaseFilename, t, buffer, valid ); return (const TCHAR *)buffer; } //// IGetViewTM /////////////////////////////////////////////////////////////// Matrix3 plStaticEnvLayer::IGetViewTM( int i ) { Matrix3 m; m.IdentityMatrix(); switch( i ) { case kTopFace: m.RotateX( -PI ); break; case kBottomFace: break; case kLeftFace: m.RotateX( -.5f * PI ); m.RotateY( -.5f * PI ); break; case kRightFace: m.RotateX( -.5f * PI ); m.RotateY( +.5f * PI ); break; case kFrontFace: m.RotateX( -.5f * PI ); m.RotateY( PI ); break; case kBackFace: m.RotateX( -.5f * PI ); break; } return m; } //// IWriteBM ///////////////////////////////////////////////////////////////// int plStaticEnvLayer::IWriteBM( BitmapInfo *bi, Bitmap *bm, TCHAR *name ) { bi->SetName( name ); if( bm->OpenOutput( bi ) == BMMRES_SUCCESS ) { if( bm->Write( bi, BMM_SINGLEFRAME ) == BMMRES_SUCCESS ) { bm->Close( bi ); return 1; } } return 0; } //// RenderCubicMap /////////////////////////////////////////////////////////// // Generates the 6 faces for a cubic map based on a picked node void plStaticEnvLayer::RenderCubicMap( INode *node ) { int res, size; BOOL success = 0; TSTR fname, fullname; Bitmap *bm = NULL; TSTR path, filename, ext, thisFilename; BitmapInfo biOutFile; static TCHAR suffixes[ 6 ][ 4 ] = { "_FR", "_BK", "_LF", "_RT", "_UP", "_DN" }; Interface *ip = GetCOREInterface(); size = fBitmapPB->GetInt( kBmpTextureSize, ip->GetTime() ); if( size <= 0 ) { return; } thisFilename = fBitmapPB->GetStr( kBmpBaseFilename, ip->GetTime() ); if( thisFilename.isNull() ) { return; } SplitFilename( thisFilename, &path, &filename, &ext ); BOOL wasHid = node->IsNodeHidden(); node->Hide( TRUE ); // Create a blank bitmap biOutFile.SetWidth( size ); biOutFile.SetHeight( size ); biOutFile.SetType( BMM_TRUE_64 ); biOutFile.SetAspect( 1.0f ); biOutFile.SetCurrentFrame( 0 ); bm = TheManager->Create( &biOutFile ); Matrix3 nodeTM = node->GetNodeTM( ip->GetTime() ); Matrix3 tm; INode *root = ip->GetRootNode(); bm->Display( GetString( IDS_CUBIC_RENDER_TITLE ) ); /// Set up rendering contexts ViewParams vp; vp.projType = PROJ_PERSPECTIVE; vp.hither = .001f; vp.yon = 1.0e30f; vp.fov = PI/2.0f; if( fBitmapPB->GetInt( kBmpUseMAXAtmosphere ) ) { vp.nearRange = 0; vp.farRange = fBitmapPB->GetFloat( kBmpFarDistance ); } else { vp.nearRange = vp.farRange = 1.0e30f; } BOOL saveUseEnvMap = ip->GetUseEnvironmentMap(); ip->SetUseEnvironmentMap( false ); res = ip->OpenCurRenderer( &vp ); for( int i = 0; i < 6; i++ ) { tm = IGetViewTM( i ); tm.PreTranslate( -nodeTM.GetTrans() ); vp.affineTM = tm; // Construct filename thisFilename.printf( _T( "%s\\%s%s%s" ), path, filename, suffixes[ i ], ext ); res = ip->CurRendererRenderFrame( ip->GetTime(), bm, NULL, 1.0f, &vp ); if( !res ) goto fail; if( !IWriteBM( &biOutFile, bm, thisFilename ) ) goto fail; } success = 1; fail: ip->CloseCurRenderer(); ip->SetUseEnvironmentMap( saveUseEnvMap ); bm->DeleteThis(); node->Hide( wasHid ); if( success ) { for(int i = 0; i < 6; i++ ) { BitmapInfo bi; thisFilename.printf( _T( "%s\\%s%s%s" ), path, filename, suffixes[ i ], ext ); bi.SetName( thisFilename ); PBBitmap pbBitmap( bi ); fBitmapPB->SetValue( kBmpFrontBitmap + i, ip->GetTime(), &pbBitmap ); } fBitmapPB->GetMap()->UpdateUI( ip->GetTime() ); } } PBBitmap *plStaticEnvLayer::GetPBBitmap(int index /* = 0 */) { return fBitmapPB->GetBitmap( ParamID( kBmpFrontBitmap + index ) ); } void plStaticEnvLayer::ISetPBBitmap( PBBitmap *pbbm, int index ) { fBitmapPB->SetValue( ParamID( kBmpFrontBitmap + index ), 0, pbbm ); }