/*==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 . 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==*/ /////////////////////////////////////////////////////////////////////////////// // // // plLayerConverter - Utility class that converts plPlasmaMAXLayers into // // other stuff. // // Cyan, Inc. // // // //// Version History ////////////////////////////////////////////////////////// // // // 1.13.2002 mcn - Created. // // // //// Notes //////////////////////////////////////////////////////////////////// // // // If you add a new PlasmaMAXLayer type, you need to add the appropriate // // test in ConvertTexmap() as well as your own function to do the // // conversion. Messy, but thanks to the dependencies of the convert process,// // we can't do it the nice, pretty, OOP way. // // // /////////////////////////////////////////////////////////////////////////////// #include "hsWindows.h" #include "Max.h" #include "stdmat.h" #include "istdplug.h" #include "iparamb2.h" #include "iparamm2.h" #include "hsUtils.h" #include "hsExceptionStack.h" #include "plLayerConverter.h" #include "../MaxConvert/hsMaxLayerBase.h" #include "../MaxConvert/hsConverterUtils.h" #include "../MaxConvert/hsControlConverter.h" #include "../MaxConvert/hsMaterialConverter.h" #include "../MaxConvert/plBitmapCreator.h" #include "hsResMgr.h" #include "../MaxExport/plErrorMsg.h" #include "../MaxMain/plMaxNode.h" #include "../pnKeyedObject/plUoid.h" #include "../pnKeyedObject/plKey.h" #include "../pnSceneObject/plSceneObject.h" #include "../plSurface/plLayerInterface.h" #include "../plSurface/plLayer.h" #include "../pnMessage/plRefMsg.h" #include "../pnMessage/plObjRefMsg.h" #include "../plMessage/plLayRefMsg.h" #include "../plDrawable/plGeometrySpan.h" #include "../plGImage/plMipmap.h" #include "../plGImage/plDynamicTextMap.h" #include "../plGImage/plCubicEnvironmap.h" #include "../plPipeline/plCubicRenderTarget.h" #include "../plPipeline/plCubicRenderTargetModifier.h" #include "../plPipeline/plDynamicEnvMap.h" #include "../pfSurface/plLayerAVI.h" #ifdef USE_BINK_SDK #include "../pfSurface/plLayerBink.h" #endif #include "../MaxPlasmaMtls/Layers/plPlasmaMAXLayer.h" #include "../MaxPlasmaMtls/Layers/plLayerTex.h" #include "../MaxPlasmaMtls/Layers/plLayerTexBasicPB.h" #include "../MaxPlasmaMtls/Layers/plLayerTexBitmapPB.h" #include "../MaxPlasmaMtls/Layers/plStaticEnvLayer.h" #include "../MaxPlasmaMtls/Layers/plDynamicEnvLayer.h" #include "../MaxPlasmaMtls/Layers/plDynamicTextLayer.h" #include "../MaxPlasmaMtls/Layers/plAngleAttenLayer.h" #include "../MaxPlasmaMtls/Layers/plMAXCameraLayer.h" #include "../MaxComponent/plComponent.h" #include "../MaxComponent/plWaterComponent.h" #include "../pfCamera/plCameraModifier.h" //// Local Static Stuff /////////////////////////////////////////////////////// namespace { enum { kWarnedTooManyUVs = 0x01, kWarnedNoBaseTexture = 0x02, kWarnedUpperTextureMissing = 0x04, kWarnedNoUpperTexture = 0x08, }; const char sWarnBaseTextureMissing[] = "The object \"%s\"'s material has a base layer that is assigned texture \"%s\", but the texture file is missing. " "This can cause unwanted effects during runtime."; const char sWarnUpperTextureMissing[] = "The object \"%s\"'s material has an upper layer that is assigned texture \"%s\", but the texture file is missing. " "This is not supported in the engine, so the upper layer will be ignored."; const char sWarnNoUpperTexture[] = "The object \"%s\"'s material has an uppper layer that is not assigned a texture. " "This is not supported in the engine, so the upper layer will be disabled."; } //// Constructor/Destructor /////////////////////////////////////////////////// plLayerConverter::plLayerConverter() : fInterface( nil ), fConverterUtils( hsConverterUtils::Instance() ) { fErrorMsg = nil; fWarned = 0; fSaving = false; } plLayerConverter::~plLayerConverter() { } plLayerConverter &plLayerConverter::Instance( void ) { hsGuardBegin( "plLayerConverter::Instance" ); static plLayerConverter instance; return instance; hsGuardEnd; } void plLayerConverter::Init( hsBool save, plErrorMsg *msg ) { fSaving = save; fErrorMsg = msg; fWarned = 0; fInterface = GetCOREInterface(); } void plLayerConverter::DeInit( void ) { int i; for( i = 0; i < fConvertedLayers.GetCount(); i++ ) { if( fConvertedLayers[ i ] != nil ) fConvertedLayers[ i ]->IClearConversionTargets(); } fConvertedLayers.Reset(); } //// Mute/Unmute Warnings ///////////////////////////////////////////////////// void plLayerConverter::MuteWarnings( void ) { fSavedWarned = fWarned; fWarned |= kWarnedNoBaseTexture | kWarnedUpperTextureMissing; } void plLayerConverter::UnmuteWarnings( void ) { fWarned = fSavedWarned; } /////////////////////////////////////////////////////////////////////////////// //// Main Switcheroo ////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// //// ConvertTexmap //////////////////////////////////////////////////////////// plLayerInterface *plLayerConverter::ConvertTexmap( Texmap *texmap, plMaxNode *maxNode, UInt32 blendFlags, hsBool preserveUVOffset, hsBool upperLayer ) { hsGuardBegin( "plLayerConverter::ConvertTexmap" ); fDbgNodeName = maxNode->GetName(); // We only convert plPlasmaMAXLayers plPlasmaMAXLayer *layer = plPlasmaMAXLayer::GetPlasmaMAXLayer( texmap ); if( layer == nil ) { fErrorMsg->Set( true, "Plasma Layer Error", "Cannot convert layer '%s'--unrecognized MAX layer type", texmap->GetName() ); fErrorMsg->Show(); fErrorMsg->Set(); return nil; } // KLUDGE - Some things don't set the name for their layers (ie projected // runtime lights). So that we don't end up with an empty keyname, set the // name to the nodes name if there isn't one. -Colin const char* layerName = layer->GetName(); if (!layerName || layerName[0] == '\0') layer->SetName(maxNode->GetName()); // Switch on the class ID plLayerInterface *plasmaLayer = nil; if( layer->ClassID() == LAYER_TEX_CLASS_ID ) plasmaLayer = IConvertLayerTex( layer, maxNode, blendFlags, preserveUVOffset, upperLayer ); else if( layer->ClassID() == STATIC_ENV_LAYER_CLASS_ID ) plasmaLayer = IConvertStaticEnvLayer( layer, maxNode, blendFlags, preserveUVOffset, upperLayer ); else if( layer->ClassID() == DYNAMIC_ENV_LAYER_CLASS_ID ) plasmaLayer = IConvertDynamicEnvLayer( layer, maxNode, blendFlags, preserveUVOffset, upperLayer ); else if( layer->ClassID() == DYN_TEXT_LAYER_CLASS_ID ) plasmaLayer = IConvertDynamicTextLayer( layer, maxNode, blendFlags, preserveUVOffset, upperLayer ); else if( layer->ClassID() == ANGLE_ATTEN_LAYER_CLASS_ID ) plasmaLayer = IConvertAngleAttenLayer( layer, maxNode, blendFlags, preserveUVOffset, upperLayer ); else if( layer->ClassID() == MAX_CAMERA_LAYER_CLASS_ID ) plasmaLayer = IConvertCameraLayer( layer, maxNode, blendFlags, preserveUVOffset, upperLayer ); IRegisterConversion( layer, plasmaLayer ); return plasmaLayer; hsGuardEnd; } //// IRegisterConversion ////////////////////////////////////////////////////// void plLayerConverter::IRegisterConversion( plPlasmaMAXLayer *origLayer, plLayerInterface *convertedLayer ) { if( convertedLayer == nil ) return; // Add this to our list of converted layers (so we can clean them up later) if( fConvertedLayers.Find( origLayer ) == fConvertedLayers.kMissingIndex ) fConvertedLayers.Append( origLayer ); // Now add the converted layer to that layer's list of conversion targets. // (easier than us keeping a huge lookup table, since this is *acting* // as that lookup table more or less) origLayer->IAddConversionTarget( convertedLayer ); } /////////////////////////////////////////////////////////////////////////////// //// Main Processing Functions //////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// //// IConvertLayerTex ///////////////////////////////////////////////////////// plLayerInterface *plLayerConverter::IConvertLayerTex( plPlasmaMAXLayer *layer, plMaxNode *maxNode, UInt32 blendFlags, hsBool preserveUVOffset, hsBool upperLayer ) { hsGuardBegin( "plLayerConverter::IConvertLayerTex" ); IParamBlock2 *bitmapPB; plLocation loc; loc = maxNode->GetLocation(); bitmapPB = layer->GetParamBlockByID( plLayerTex::kBlkBitmap ); if( !bitmapPB ) { fErrorMsg->Set( !bitmapPB, "Plasma Layer Error", "Bitmap paramblock for Plasma Layer not found" ).Show(); fErrorMsg->Set(); return nil; } // Get a new layer to play with plLayer *plasmaLayer = ICreateLayer( layer->GetName(), upperLayer, loc ); // We're using a texture, try and get its info PBBitmap *pbbm = nil; BitmapInfo *bi = nil; if( bitmapPB->GetInt( kBmpUseBitmap ) ) { if( bitmapPB ) pbbm = bitmapPB->GetBitmap( kBmpBitmap ); if( pbbm ) bi = &pbbm->bi; } // If the texture had bad info, assert and return the empty layer if( !bi || !bi->Name() || !strcmp(bi->Name(), "") ) { if( upperLayer ) { if( fErrorMsg->Set( !( fWarned & kWarnedNoUpperTexture ), "Plasma Export Error", sWarnNoUpperTexture, maxNode->GetName() ).CheckAskOrCancel() ) fWarned |= kWarnedNoUpperTexture; fErrorMsg->Set( false ); delete plasmaLayer; return nil; } else { return (plLayerInterface *)plasmaLayer; } } // Setup the texture creation parameters plBitmapData bd; bd.fileName = bi->Name(); // Create texture and add it to list if unique Int32 texFlags = 0;//hsGTexture::kMipMap; // Texture Alpha/Color if( bitmapPB->GetInt( kBmpInvertColor ) ) plasmaLayer->SetBlendFlags( plasmaLayer->GetBlendFlags() | hsGMatState::kBlendInvertColor ); if( bitmapPB->GetInt( kBmpDiscardColor ) ) plasmaLayer->SetBlendFlags( plasmaLayer->GetBlendFlags() | hsGMatState::kBlendNoTexColor ); if( bitmapPB->GetInt( kBmpDiscardAlpha ) ) plasmaLayer->SetBlendFlags( plasmaLayer->GetBlendFlags() | hsGMatState::kBlendNoTexAlpha ); if( bitmapPB->GetInt( kBmpInvertAlpha ) ) bd.invertAlpha = true; // Texture quality if( bitmapPB->GetInt( kBmpNonCompressed ) ) texFlags |= plBitmap::kForceNonCompressed; if( bitmapPB->GetInt( kBmpNoDiscard ) ) texFlags |= plBitmap::kDontThrowAwayImage; switch( bitmapPB->GetInt( kBmpScaling ) ) { case kScalingHalf: texFlags |= plBitmap::kHalfSize; break; case kScalingNone: texFlags |= plBitmap::kNoMaxSize; break; } // Mip map filtering. if( bitmapPB->GetInt( kBmpNoFilter ) ) texFlags |= plBitmap::kForceOneMipLevel; if( bitmapPB->GetInt( kBmpMipBias ) ) { plasmaLayer->SetZFlags( plasmaLayer->GetZFlags() | hsGMatState::kZLODBias ); plasmaLayer->SetLODBias( bitmapPB->GetFloat( kBmpMipBiasAmt, fConverterUtils.GetTime( fInterface ) ) ); } float sig = bitmapPB->GetFloat( kBmpMipBlur ); bd.texFlags = texFlags; bd.sig = sig; // Get detail parameters if( bitmapPB->GetInt( kBmpUseDetail ) ) { // TODO: be smarter if( blendFlags & hsGMatState::kBlendAdd ) bd.createFlags |= plMipmap::kCreateDetailAdd; else if( blendFlags & hsGMatState::kBlendMult ) bd.createFlags |= plMipmap::kCreateDetailMult; else bd.createFlags |= plMipmap::kCreateDetailAlpha; bd.detailDropoffStart = float(bitmapPB->GetInt(kBmpDetailStartSize)) / 100.f; bd.detailDropoffStop = float(bitmapPB->GetInt(kBmpDetailStopSize)) / 100.f; bd.detailMax = float(bitmapPB->GetInt(kBmpDetailStartOpac)) / 100.f; bd.detailMin = float(bitmapPB->GetInt(kBmpDetailStopOpac)) / 100.f; } // Get max export dimension (since the function we eventually call // expects the max of the two dimensions, we figure that out here and // pass it on) bd.maxDimension = bitmapPB->GetInt( kBmpExportWidth ); int expHt = bitmapPB->GetInt( kBmpExportHeight ); if( bd.maxDimension < expHt ) bd.maxDimension = expHt; int clipID = 0, w; for( clipID = 0, w = bi->Width(); w > bd.maxDimension; w >>= 1, clipID++ ); // Do the UV gen (before we do texture, since it could modify the bitmapData struct) IProcessUVGen( layer, plasmaLayer, &bd, preserveUVOffset ); // Create the texture. If it works, assign it to the layer if( ( plasmaLayer = IAssignTexture( &bd, maxNode, plasmaLayer, upperLayer, clipID ) ) == nil ) return nil; // All done! return (plLayerInterface *)plasmaLayer; hsGuardEnd; } //// IConvertStaticEnvLayer /////////////////////////////////////////////////// plLayerInterface *plLayerConverter::IConvertStaticEnvLayer( plPlasmaMAXLayer *layer, plMaxNode *maxNode, UInt32 blendFlags, hsBool preserveUVOffset, hsBool upperLayer ) { hsGuardBegin( "plLayerConverter::IConvertStaticEnvLayer" ); IParamBlock2 *bitmapPB; plLocation loc; loc = maxNode->GetLocation(); bitmapPB = layer->GetParamBlockByID( plStaticEnvLayer::kBlkBitmap ); if( !bitmapPB ) { fErrorMsg->Set( !bitmapPB, "Plasma Layer Error", "Bitmap paramblock for Plasma Layer not found" ).Show(); fErrorMsg->Set(); return nil; } // Get a new layer to play with plLayer *plasmaLayer = ICreateLayer( layer->GetName(), upperLayer, loc ); // Get the texture info PBBitmap *pbbm = bitmapPB->GetBitmap( plStaticEnvLayer::kBmpFrontBitmap + 0 ); BitmapInfo *bi = nil; if( pbbm ) bi = &pbbm->bi; // If the texture had bad info, assert and return the empty layer if (!bi || !bi->Name() || !strcmp(bi->Name(), "")) { // Or don't assert since it can get annoying when you are using someone // elses file and don't have all the textures. return (plLayerInterface *)plasmaLayer; } // Setup the texture creation parameters plBitmapData bd; bd.fileName = bi->Name(); // Create texture and add it to list if unique Int32 texFlags = 0; // Texture Alpha/Color if( bitmapPB->GetInt( plStaticEnvLayer::kBmpInvertColor ) ) plasmaLayer->SetBlendFlags( plasmaLayer->GetBlendFlags() | hsGMatState::kBlendInvertColor ); if( bitmapPB->GetInt( plStaticEnvLayer::kBmpDiscardColor ) ) plasmaLayer->SetBlendFlags( plasmaLayer->GetBlendFlags() | hsGMatState::kBlendNoTexColor ); if( bitmapPB->GetInt( kBmpDiscardAlpha ) ) plasmaLayer->SetBlendFlags( plasmaLayer->GetBlendFlags() | hsGMatState::kBlendNoTexAlpha ); if( bitmapPB->GetInt( plStaticEnvLayer::kBmpInvertAlpha ) ) bd.invertAlpha = true; // Texture quality if( bitmapPB->GetInt( plStaticEnvLayer::kBmpNonCompressed ) ) texFlags |= plBitmap::kForceNonCompressed; switch( bitmapPB->GetInt( plStaticEnvLayer::kBmpScaling ) ) { case plStaticEnvLayer::kScalingHalf: texFlags |= plBitmap::kHalfSize; break; case plStaticEnvLayer::kScalingNone: texFlags |= plBitmap::kNoMaxSize; break; } bd.texFlags = texFlags; bd.isStaticCubicEnvMap = true; for( int i = 0; i < 6; i++ ) { PBBitmap *face = bitmapPB->GetBitmap( plStaticEnvLayer::kBmpFrontBitmap + i ); if( !face ) return (plLayerInterface *)plasmaLayer; bd.faceNames[ i ] = face->bi.Name(); } // Get detail parameters if( bitmapPB->GetInt( plStaticEnvLayer::kBmpUseDetail ) ) { // TODO: be smarter if( blendFlags & hsGMatState::kBlendAdd ) bd.createFlags = plMipmap::kCreateDetailAdd; else if( blendFlags & hsGMatState::kBlendMult ) bd.createFlags = plMipmap::kCreateDetailMult; else bd.createFlags = plMipmap::kCreateDetailAlpha; bd.detailDropoffStart = float( bitmapPB->GetInt( plStaticEnvLayer::kBmpDetailStartSize ) ) / 100.f; bd.detailDropoffStop = float( bitmapPB->GetInt( plStaticEnvLayer::kBmpDetailStopSize ) ) / 100.f; bd.detailMax = float( bitmapPB->GetInt( plStaticEnvLayer::kBmpDetailStartOpac ) ) / 100.f; bd.detailMin = float( bitmapPB->GetInt( plStaticEnvLayer::kBmpDetailStopOpac ) ) / 100.f; } /// Since we're a cubic environMap, we don't care about the UV transform nor the uvwSrc plasmaLayer->SetUVWSrc( 0 ); plasmaLayer->SetUVWSrc( plasmaLayer->GetUVWSrc() | plLayerInterface::kUVWReflect ); // Create the texture. If it works, assign it to the layer if( ( plasmaLayer = IAssignTexture( &bd, maxNode, plasmaLayer, upperLayer ) ) == nil ) return nil; // Tag this layer as reflective cubic environmentmapping if( bitmapPB->GetInt(plStaticEnvLayer::kBmpRefract) ) plasmaLayer->SetMiscFlags( plasmaLayer->GetMiscFlags() | hsGMatState::kMiscUseRefractionXform ); else plasmaLayer->SetMiscFlags( plasmaLayer->GetMiscFlags() | hsGMatState::kMiscUseReflectionXform ); return (plLayerInterface *)plasmaLayer; hsGuardEnd; } //// IConvertDynamicEnvLayer ////////////////////////////////////////////////// plLayerInterface *plLayerConverter::IConvertDynamicEnvLayer( plPlasmaMAXLayer *layer, plMaxNode *maxNode, UInt32 blendFlags, hsBool preserveUVOffset, hsBool upperLayer ) { hsGuardBegin( "plLayerConverter::IConvertDynamicEnvLayer" ); IParamBlock2 *bitmapPB; plLocation loc; loc = maxNode->GetLocation(); bitmapPB = layer->GetParamBlockByID( plDynamicEnvLayer::kBlkBitmap ); if( !bitmapPB ) { fErrorMsg->Set( !bitmapPB, "Plasma Layer Error", "Bitmap paramblock for Plasma Layer not found" ).Show(); fErrorMsg->Set(); return nil; } // Get a new layer to play with plLayer *plasmaLayer = ICreateLayer( layer->GetName(), upperLayer, loc ); // Get the anchor node plMaxNode *anchor = (plMaxNode *)bitmapPB->GetINode( plDynamicEnvLayer::kBmpAnchorNode ); if( anchor == nil ) // Default to self as the anchor--just make sure we make unique versions of this material! anchor = maxNode; if( !anchor->CanConvert() || !( anchor->GetForceLocal() || anchor->GetDrawable() ) ) { fErrorMsg->Set( true, "Plasma Layer Error", "The dynamic envMap material %s has an invalid anchor specified. Please specify a valid Plasma scene object as an anchor.", plasmaLayer->GetKeyName() ).Show(); fErrorMsg->Set(); return (plLayerInterface *)plasmaLayer; } // Create texture and add it to list if unique Int32 texFlags = 0; /// Since we're a cubic environMap, we don't care about the UV transform nor the uvwSrc plasmaLayer->SetUVWSrc( 0 ); plasmaLayer->SetUVWSrc( plasmaLayer->GetUVWSrc() | plLayerInterface::kUVWReflect ); // Create the texture. If it works, assign it to the layer char texName[ 256 ]; if( anchor == maxNode ) { // Self-anchoring material, make sure the name is unique via the nodeName sprintf( texName, "%s_cubicRT@%s", plasmaLayer->GetKeyName(), maxNode->GetName() ); } else sprintf( texName, "%s_cubicRT", plasmaLayer->GetKeyName() ); plBitmap *texture = (plBitmap *)IMakeCubicRenderTarget( texName, maxNode, anchor ); if( texture ) hsgResMgr::ResMgr()->AddViaNotify( texture->GetKey(), TRACKED_NEW plLayRefMsg( plasmaLayer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture ), plRefFlags::kActiveRef ); // Tag this layer as reflective cubic environmentmapping if( bitmapPB->GetInt(plDynamicEnvLayer::kBmpRefract) ) plasmaLayer->SetMiscFlags( plasmaLayer->GetMiscFlags() | hsGMatState::kMiscUseRefractionXform ); else plasmaLayer->SetMiscFlags( plasmaLayer->GetMiscFlags() | hsGMatState::kMiscUseReflectionXform ); return (plLayerInterface *)plasmaLayer; hsGuardEnd; } plLayerInterface *plLayerConverter::IConvertCameraLayer(plPlasmaMAXLayer *layer, plMaxNode *maxNode, UInt32 blendFlags, hsBool preserveUVOffset, hsBool upperLayer) { hsGuardBegin( "plLayerConverter::IConvertCameraLayer" ); IParamBlock2 *pb; plLocation loc; loc = maxNode->GetLocation(); pb = layer->GetParamBlockByID(plMAXCameraLayer::kBlkMain); if (!pb) { fErrorMsg->Set(!pb, "Plasma Layer Error", "Paramblock for Plasma Camera Layer not found" ).Show(); fErrorMsg->Set(); return nil; } plLayer *plasmaLayer = ICreateLayer (layer->GetName(), upperLayer, loc); plMaxNode *rootNode = (plMaxNode*)pb->GetINode(ParamID(plMAXCameraLayer::kRootNode)); plDynamicCamMap *map = plEnvMapComponent::GetCamMap(rootNode ? rootNode : maxNode); if (map) { Int32 texFlags = 0; if (!pb->GetInt(ParamID(plMAXCameraLayer::kExplicitCam))) { plasmaLayer->SetUVWSrc(plLayerInterface::kUVWPosition); plasmaLayer->SetMiscFlags(hsGMatState::kMiscCam2Screen | hsGMatState::kMiscPerspProjection); hsgResMgr::ResMgr()->AddViaNotify(rootNode->GetSceneObject()->GetKey(), TRACKED_NEW plGenRefMsg(map->GetKey(), plRefMsg::kOnCreate, -1, plDynamicCamMap::kRefRootNode), plRefFlags::kActiveRef); hsgResMgr::ResMgr()->AddViaNotify(plasmaLayer->GetKey(), TRACKED_NEW plGenRefMsg(map->GetKey(), plRefMsg::kOnCreate, -1, plDynamicCamMap::kRefMatLayer), plRefFlags::kActiveRef); if (!pb->GetInt(ParamID(plMAXCameraLayer::kForce))) { plBitmap *disableTexture = hsMaterialConverter::Instance().GetStaticColorTexture(pb->GetColor(ParamID(plMAXCameraLayer::kDisableColor)), loc); hsgResMgr::ResMgr()->AddViaNotify(disableTexture->GetKey(), TRACKED_NEW plGenRefMsg(map->GetKey(), plRefMsg::kOnCreate, -1, plDynamicCamMap::kRefDisableTexture), plRefFlags::kActiveRef); } } else { plMaxNode *camNode = (plMaxNode*)pb->GetINode(ParamID(plMAXCameraLayer::kCamera)); if (camNode) { const plCameraModifier1 *mod = plCameraModifier1::ConvertNoRef(camNode->GetSceneObject()->GetModifierByType(plCameraModifier1::Index())); if (mod) hsgResMgr::ResMgr()->AddViaNotify(mod->GetKey(), TRACKED_NEW plGenRefMsg(map->GetKey(), plRefMsg::kOnCreate, -1, plDynamicCamMap::kRefCamera), plRefFlags::kActiveRef); } plasmaLayer->SetUVWSrc(pb->GetInt(ParamID(plMAXCameraLayer::kUVSource))); } hsTArray nodeList; hsMaterialConverter::GetNodesByMaterial(maxNode->GetMtl(), nodeList); int i; for (i = 0; i < nodeList.GetCount(); i++) { hsgResMgr::ResMgr()->AddViaNotify(nodeList[i]->GetSceneObject()->GetKey(), TRACKED_NEW plGenRefMsg(map->GetKey(), plRefMsg::kOnCreate, -1, plDynamicCamMap::kRefTargetNode), plRefFlags::kActiveRef); } hsgResMgr::ResMgr()->AddViaNotify(map->GetKey(), TRACKED_NEW plLayRefMsg(plasmaLayer->GetKey(), plRefMsg::kOnCreate, -1, plLayRefMsg::kTexture), plRefFlags::kActiveRef); } return plasmaLayer; hsGuardEnd; } //// IConvertDynamicTextLayer ///////////////////////////////////////////////// plLayerInterface *plLayerConverter::IConvertDynamicTextLayer( plPlasmaMAXLayer *layer, plMaxNode *maxNode, UInt32 blendFlags, hsBool preserveUVOffset, hsBool upperLayer ) { hsGuardBegin( "plLayerConverter::IConvertDynamicTextLayer" ); plDynamicTextLayer *maxLayer; IParamBlock2 *bitmapPB; plLocation loc; maxLayer = (plDynamicTextLayer *)layer; loc = maxNode->GetLocation(); bitmapPB = maxLayer->GetParamBlockByID( plDynamicTextLayer::kBlkBitmap ); if( !bitmapPB ) { fErrorMsg->Set( !bitmapPB, "Plasma Layer Error", "Bitmap paramblock for Plasma Layer not found" ).Show(); fErrorMsg->Set(); return nil; } // Get a new layer to play with plLayer *plasmaLayer = ICreateLayer( maxLayer->GetName(), upperLayer, loc ); /// UV Gen IProcessUVGen( maxLayer, plasmaLayer, nil, preserveUVOffset ); // Create the "texture" plDynamicTextMap *texture = ICreateDynTextMap( plasmaLayer->GetKeyName(), bitmapPB->GetInt( plDynamicTextLayer::kBmpExportWidth ), bitmapPB->GetInt( plDynamicTextLayer::kBmpExportHeight ), bitmapPB->GetInt( plDynamicTextLayer::kBmpIncludeAlphaChannel ), maxNode ); // Set the initial bitmap if necessary UInt32 *initBuffer = IGetInitBitmapBuffer( maxLayer ); if( initBuffer != nil ) { texture->SetInitBuffer( initBuffer ); delete [] initBuffer; } // Add the texture in hsgResMgr::ResMgr()->AddViaNotify( texture->GetKey(), TRACKED_NEW plLayRefMsg( plasmaLayer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture ), plRefFlags::kActiveRef ); // All done! return (plLayerInterface *)plasmaLayer; hsGuardEnd; } //// IGetInitBitmapBuffer ///////////////////////////////////////////////////// // Called to get a 32-bit uncompressed ARGB version of the init bitmap of // a dynamic text layer UInt32 *plLayerConverter::IGetInitBitmapBuffer( plDynamicTextLayer *layer ) const { UInt32 *buffer; hsRGBAColor32 *buffPtr; UInt16 width, height; IParamBlock2 *bitmapPB = layer->GetParamBlockByID( plDynamicTextLayer::kBlkBitmap ); Bitmap *initBitmap = layer->GetBitmap( TimeValue( 0 ) ); if( bitmapPB->GetInt( (ParamID)plDynamicTextLayer::kBmpUseInitImage ) == 0 || initBitmap == nil ) return nil; width = bitmapPB->GetInt( (ParamID)plDynamicTextLayer::kBmpExportWidth ); height = bitmapPB->GetInt( (ParamID)plDynamicTextLayer::kBmpExportHeight ); buffer = TRACKED_NEW UInt32[ width * height ]; if( buffer == nil ) return nil; // Fill buffer from the MAX bitmap PixelBuf l64( width ); BMM_Color_64 *p64 = l64.Ptr(); buffPtr = (hsRGBAColor32 *)buffer; for( int y = 0; y < height; y++ ) { hsRGBAColor32 color; if( !initBitmap->GetLinearPixels( 0, y, width, p64 ) ) { delete [] buffer; return nil; } for( int x = 0; x < width; x++ ) { color.SetARGB( p64[ x ].a * 255.f / 65535.f, p64[ x ].r * 255.f / 65535.f, p64[ x ].g * 255.f / 65535.f, p64[ x ].b * 255.f / 65535.f ); buffPtr[ x ] = color; } buffPtr += width; } return buffer; } static UInt32 MakeUInt32Color(float r, float g, float b, float a) { return (UInt32(a * 255.9f) << 24) |(UInt32(r * 255.9f) << 16) |(UInt32(g * 255.9f) << 8) |(UInt32(b * 255.9f) << 0); } plBitmap* plLayerConverter::IGetAttenRamp(plMaxNode *node, BOOL isAdd, int loClamp, int hiClamp) { char funkName[512]; sprintf(funkName, "%s_%d_%d", isAdd ? "AttenRampAdd" : "AttenRampMult", loClamp, hiClamp); float range = float(hiClamp - loClamp) * 1.e-2f; float lowest = float(loClamp) * 1.e-2f; const int kLUTWidth = 16; const int kLUTHeight = 16; // NOTE: CreateBlankMipmap might return an old mipmap if it was already created, so in those // cases we wouldn't really need to re-write the texture. However, there's no harm in doing so, // and since we're close to Alpha, I don't want to shake up the code any more than absolutely // necessary. -mcn plMipmap *texture = plBitmapCreator::Instance().CreateBlankMipmap( kLUTWidth, kLUTHeight, plMipmap::kARGB32Config, 1, funkName, node->GetLocation() ); UInt32* pix = (UInt32*)texture->GetImage(); if( isAdd ) { int i; for( i = 0; i < kLUTHeight; i++ ) { int j; for( j = 0; j < kLUTWidth; j++ ) { float x = float(j) / (kLUTWidth-1); float y = float(i) / (kLUTHeight-1); if( x < y ) x = y; x *= range; x += lowest; *pix++ = MakeUInt32Color(1.f, 1.f, 1.f, x); } } } else { int i; for( i = 0; i < kLUTHeight; i++ ) { int j; for( j = 0; j < kLUTWidth; j++ ) { float x = float(j) / (kLUTWidth-1); float y = float(i) / (kLUTHeight-1); float val = x * y; val *= range; val += lowest; *pix++ = MakeUInt32Color(1.f, 1.f, 1.f, val); } } } return texture; } plLayer* plLayerConverter::ICreateAttenuationLayer(const char* name, plMaxNode *node, int uvwSrc, float tr0, float op0, float tr1, float op1, int loClamp, int hiClamp) { hsMatrix44 uvwXfm; uvwXfm.Reset(); uvwXfm.fMap[0][0] = uvwXfm.fMap[1][1] = uvwXfm.fMap[2][2] = 0; uvwXfm.NotIdentity(); if( op0 != tr0 ) { uvwXfm.fMap[0][2] = -1.f / (tr0 - op0); uvwXfm.fMap[0][3] = uvwXfm.fMap[0][2] * -tr0; } else { uvwXfm.fMap[0][3] = 1.f; } if( op1 != tr1 ) { uvwXfm.fMap[1][2] = -1.f / (tr1 - op1); uvwXfm.fMap[1][3] = uvwXfm.fMap[1][2] * -tr1; } else { uvwXfm.fMap[1][3] = 1.f; } BOOL chanAdd = false; if( op0 < tr0 ) { if( (op0 < op1) && (tr1 < op1) ) chanAdd = true; } else if( op0 > tr0 ) { if( (op0 > op1) && (tr1 > op1) ) chanAdd = true; } plBitmap* funkRamp = IGetAttenRamp(node, chanAdd, loClamp, hiClamp); plLayer* layer = TRACKED_NEW plLayer; layer->InitToDefault(); hsgResMgr::ResMgr()->NewKey(name, layer, node->GetLocation()); hsgResMgr::ResMgr()->AddViaNotify(funkRamp->GetKey(), TRACKED_NEW plLayRefMsg(layer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture), plRefFlags::kActiveRef); layer->SetAmbientColor(hsColorRGBA().Set(1.f, 1.f, 1.f, 1.f)); layer->SetPreshadeColor(hsColorRGBA().Set(0, 0, 0, 1.f)); layer->SetRuntimeColor(hsColorRGBA().Set(0, 0, 0, 1.f)); layer->SetZFlags(hsGMatState::kZNoZWrite); UInt32 blendFlags = hsGMatState::kBlendAlpha | hsGMatState::kBlendNoTexColor | hsGMatState::kBlendAlphaMult; layer->SetBlendFlags(blendFlags); layer->SetClampFlags(hsGMatState::kClampTexture); layer->SetTransform(uvwXfm); layer->SetUVWSrc(uvwSrc); return layer; } plLayerInterface* plLayerConverter::IConvertAngleAttenLayer(plPlasmaMAXLayer *layer, plMaxNode *maxNode, UInt32 blendFlags, hsBool preserveUVOffset, hsBool upperLayer) { hsGuardBegin( "plPlasmaMAXLayer::IConvertAngleAttenLayer" ); if( !upperLayer ) { fErrorMsg->Set(true, maxNode->GetName(), "Angle Attenuation layers can only be used as a top layer").Show(); fErrorMsg->Set(); return nil; } plAngleAttenLayer* aaLay = (plAngleAttenLayer*)layer; Box3 fade = aaLay->GetFade(); float tr0 = cosf(DegToRad(180.f - fade.Min().x)); float op0 = cosf(DegToRad(180.f - fade.Min().y)); float tr1 = cosf(DegToRad(180.f - fade.Max().x)); float op1 = cosf(DegToRad(180.f - fade.Max().y)); int loClamp = aaLay->GetLoClamp(); int hiClamp = aaLay->GetHiClamp(); int uvwSrc = aaLay->Reflect() ? plLayerInterface::kUVWReflect : plLayerInterface::kUVWNormal; plLayer* lut = ICreateAttenuationLayer(layer->GetName(), maxNode, uvwSrc, tr0, op0, tr1, op1, loClamp, hiClamp); return lut; hsGuardEnd; } //// ICreateLayer ///////////////////////////////////////////////////////////// plLayer *plLayerConverter::ICreateLayer( const char *name, hsBool upperLayer, plLocation &loc ) { hsGuardBegin( "plPlasmaMAXLayer::ICreateLayer" ); plLayer *layer = TRACKED_NEW plLayer; layer->InitToDefault(); hsgResMgr::ResMgr()->NewKey( name, layer, loc ); return layer; hsGuardEnd; } //// IProcessUVGen //////////////////////////////////////////////////////////// void plLayerConverter::IProcessUVGen( plPlasmaMAXLayer *srcLayer, plLayer *destLayer, plBitmapData *bitmapData, hsBool preserveUVOffset ) { hsGuardBegin( "plPlasmaMAXLayer::IProcessUVGen" ); StdUVGen *uvGen = (StdUVGen *)srcLayer->GetTheUVGen(); int tiling = uvGen->GetTextureTiling(); // If set this indicates the texture map is tiled in U if (!(tiling & U_WRAP)) { destLayer->SetClampFlags( destLayer->GetClampFlags() | hsGMatState::kClampTextureU ); if( bitmapData != nil ) bitmapData->clampFlags |= plBitmapData::kClampU; } // If set this indicates the texture map is tiled in V if (!(tiling & V_WRAP)) { destLayer->SetClampFlags( destLayer->GetClampFlags() | hsGMatState::kClampTextureV ); if( bitmapData != nil ) bitmapData->clampFlags |= plBitmapData::kClampV; } // UVW Src Int32 uvwSrc = srcLayer->GetMapChannel() - 1; if( fErrorMsg->Set( !( fWarned & kWarnedTooManyUVs ) && ( ( uvwSrc < 0 ) || ( uvwSrc >= plGeometrySpan::kMaxNumUVChannels ) ), destLayer->GetKeyName(), "Only %d UVW channels (1-%d) currently supported", plGeometrySpan::kMaxNumUVChannels, plGeometrySpan::kMaxNumUVChannels).CheckAskOrCancel() ) fWarned |= kWarnedTooManyUVs; fErrorMsg->Set( false ); destLayer->SetUVWSrc( uvwSrc ); // Get the actual texture transform hsMatrix44 hsTopX; if (uvGen && (hsControlConverter::Instance().StdUVGenToHsMatrix44(&hsTopX, uvGen, preserveUVOffset == true))) destLayer->SetTransform( hsTopX ); // All done! hsGuardEnd; } //// ICreateDynTextMap //////////////////////////////////////////////////////// plDynamicTextMap *plLayerConverter::ICreateDynTextMap( const char *layerName, UInt32 width, UInt32 height, hsBool includeAlphaChannel, plMaxNode *node ) { hsGuardBegin( "plPlasmaMAXLayer::ICreateDynTextMap" ); char texName[ 256 ]; plKey key; plDynamicTextMap *map = nil; // Need a unique key name for every layer that uses one. We could also key // off of width and height, but layerName should be more than plenty sprintf( texName, "%s_dynText", layerName ); // Does it already exist? key = node->FindPageKey( plDynamicTextMap::Index(), texName ); if( key != nil ) { map = plDynamicTextMap::ConvertNoRef( key->GetObjectPtr() ); if( map != nil ) return map; } // Create map = TRACKED_NEW plDynamicTextMap(); map->SetNoCreate( width, height, includeAlphaChannel ); /// Add a key for it key = hsgResMgr::ResMgr()->NewKey( texName, map, node->GetLocation() ); // All done! return map; hsGuardEnd; } /////////////////////////////////////////////////////////////////////////////// //// Texture/Bitmap Management //////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// plBitmap *plLayerConverter::CreateSimpleTexture(const char *fileName, const plLocation &loc, UInt32 clipID /* = 0 */, UInt32 texFlags /* = 0 */, bool useJPEG /* = false */) { plBitmapData bd; bd.fileName = fileName; bd.texFlags = texFlags; bd.createFlags = 0; bd.detailDropoffStart = 0; bd.detailDropoffStop = 0; bd.detailMax = 0; bd.detailMin = 0; bd.sig = 0; bd.isStaticCubicEnvMap = false; bd.invertAlpha = false; bd.clampFlags = 0; bd.useJPEG = useJPEG; return plBitmapCreator::Instance().CreateTexture(&bd, loc, clipID); } //// IAssignTexture /////////////////////////////////////////////////////////// // Create a texture and assign it to the layer given. Returns the layer again, // or nil if there was an error and it got deleted. plLayer *plLayerConverter::IAssignTexture( plBitmapData *bd, plMaxNode *maxNode, plLayer *destLayer, hsBool upperLayer, int clipID ) { plBitmap *texture = plBitmapCreator::Instance().CreateTexture( bd, maxNode->GetLocation(), clipID ); if( texture == nil ) { if( upperLayer ) { if( fErrorMsg->Set( !( fWarned & kWarnedUpperTextureMissing ), "Plasma Export Error", sWarnUpperTextureMissing, maxNode->GetName(), bd->fileName ).CheckAskOrCancel() ) fWarned |= kWarnedUpperTextureMissing; fErrorMsg->Set( false ); delete destLayer; return nil; } else { if( fErrorMsg->Set( !( fWarned & kWarnedNoBaseTexture ), "Plasma Export Error", sWarnBaseTextureMissing, maxNode->GetName(), bd->fileName ).CheckAskOrCancel() ) fWarned |= kWarnedNoBaseTexture; fErrorMsg->Set( false ); return destLayer; } } else hsgResMgr::ResMgr()->AddViaNotify( texture->GetKey(), TRACKED_NEW plLayRefMsg( destLayer->GetKey(), plRefMsg::kOnCreate, 0, plLayRefMsg::kTexture ), plRefFlags::kActiveRef ); return destLayer; } //// IMakeCubicRenderTarget /////////////////////////////////////////////////// // Makes a plCubicRenderTarget as a texture. Also constructs the associated // modifier and attaches it to the necessary object (hacked for now) plCubicRenderTarget *plLayerConverter::IMakeCubicRenderTarget( const char *name, plMaxNode *node, plMaxNode *anchor ) { plDynamicEnvMap* env = plEnvMapComponent::GetEnvMap(anchor); if( env ) return env; char modName[ 256 ]; plCubicRenderTarget *cubic = nil; plKey key; key = node->FindPageKey( plCubicRenderTarget::Index(), name ); if( key != nil ) { plCubicRenderTarget *cubic = plCubicRenderTarget::ConvertNoRef( key->GetObjectPtr() ); if( cubic != nil ) return cubic; } /// Get the key from the anchor if( anchor == nil || anchor->GetSceneObject() == nil ) return nil; plKey sObjKey = anchor->GetSceneObject()->GetKey(); if( sObjKey == nil ) return nil; /// Create cubic = TRACKED_NEW plCubicRenderTarget( plRenderTarget::kIsTexture, 256, 256, 32 ); hsAssert( cubic != nil, "Cannot create cubic render target!" ); /// Add a key key = hsgResMgr::ResMgr()->NewKey( name, cubic, node->GetLocation() ); /// Now make a modifier plCubicRenderTargetModifier *mod = TRACKED_NEW plCubicRenderTargetModifier(); sprintf( modName, "%s_mod", name ); hsgResMgr::ResMgr()->NewKey( modName, mod, node->GetLocation() ); hsgResMgr::ResMgr()->AddViaNotify( cubic->GetKey(), TRACKED_NEW plGenRefMsg( mod->GetKey(), plRefMsg::kOnCreate, 0, 0 ), plRefFlags::kPassiveRef ); hsgResMgr::ResMgr()->AddViaNotify( mod->GetKey(), TRACKED_NEW plObjRefMsg( sObjKey, plRefMsg::kOnCreate, 0, plObjRefMsg::kModifier ), plRefFlags::kActiveRef ); return cubic; }