/*==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==*/ /////////////////////////////////////////////////////////////////////////////// // // // 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" #include "../pfSurface/plLayerBink.h" #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; }