--- gui/buttons/guiBitmapButtonCtrl.cpp	Thu Jan 15 03:14:12 1970
+++ gui/buttons/guiBitmapButtonCtrl.cpp	Thu Jan 15 03:14:12 1970
@@ -139,6 +139,10 @@
          "Defaults to true.\n\n"
          "If you do not use per-state images on this button set this to false to speed up the loading process "
          "by inhibiting searches for the individual images." );
+      addProtectedField( "genericMask", TypeStringFilename, Offset( mGenericMask, GuiBitmapButtonCtrl ),
+         &_setGenericMask, &defaultProtectedGetFn,
+         "Texture file to use as a shape mask on this button.\n"
+         "This will override the _m bitmap file name variant." );
          
    endGroup( "Bitmap" );
       
@@ -154,6 +158,7 @@
       
    setActive( true );
    setBitmap( mBitmapName );
+   setGenericMask( mGenericMask );
    
    return true;
 }
@@ -171,6 +176,9 @@
          mTextures[ i ].mTextureInactive = NULL;
       }
 
+   if( dStricmp(mGenericMask, "texhandle") != 0 )
+      mTexGenericMask = NULL;
+
    Parent::onSleep();
 }
 
@@ -194,6 +202,15 @@
 
 //-----------------------------------------------------------------------------
 
+bool GuiBitmapButtonCtrl::_setGenericMask( void *object, const char *index, const char *data )
+{
+   GuiBitmapButtonCtrl* ctrl = reinterpret_cast< GuiBitmapButtonCtrl* >( object );
+   ctrl->setGenericMask( data );
+   return false;
+}
+
+//-----------------------------------------------------------------------------
+
 // Legacy method.  Can just assign to bitmap field.
 DefineEngineMethod( GuiBitmapButtonCtrl, setBitmap, void, ( const char* path ),,
    "Set the bitmap to show on the button.\n"
@@ -234,6 +251,7 @@
    }
    
    setBitmap( path.getFullPath() );
+   setGenericMask( Torque::Path( mGenericMask ).getFullPath() );
 
    // if the extent is set to (0,0) in the gui editor and appy hit, this control will
    // set it's extent to be exactly the size of the normal bitmap (if present)
@@ -282,6 +300,7 @@
             static String s_d = "_d";
             static String s_h = "_h";
             static String s_i = "_i";
+            static String s_m = "_m";
 
             String baseName = mBitmapName;
             if( mUseModifiers )
@@ -305,6 +324,9 @@
                mTextures[ i ].mTextureInactive = GFXTexHandle( baseName + s_i, &GFXDefaultPersistentProfile, avar("%s() - mTextureInactive (line %d)", __FUNCTION__, __LINE__));
                if( !mTextures[ i ].mTextureInactive )
                   mTextures[ i ].mTextureInactive = mTextures[ i ].mTextureNormal;
+
+               mTextures[ i ].mTextureMask = GFXTexHandle( baseName + s_m, &GFXDefaultPersistentProfile, avar("%s() - mTextureMask (line %d)", __FUNCTION__, __LINE__));
+
             }
 
             if( i == 0 && mTextures[ i ].mTextureNormal.isNull() && mTextures[ i ].mTextureHilight.isNull() && mTextures[ i ].mTextureDepressed.isNull() && mTextures[ i ].mTextureInactive.isNull() )
@@ -327,6 +349,7 @@
          mTextures[ i ].mTextureHilight = NULL;
          mTextures[ i ].mTextureDepressed = NULL;
          mTextures[ i ].mTextureInactive = NULL;
+         mTextures[ i ].mTextureMask = NULL;
       }
    }
    
@@ -335,6 +358,29 @@
 
 //------------------------------------------------------------------------------
 
+void GuiBitmapButtonCtrl::setGenericMask( const String& name )
+{
+   mGenericMask = name;
+   if( !isAwake() )
+      return;
+
+   if( !mGenericMask.isEmpty() )
+   {
+      if( dStricmp( mGenericMask, "texhandle" ) != 0 )
+      {
+		  mTexGenericMask = GFXTexHandle( mGenericMask, &GFXDefaultPersistentProfile, avar("%s() - mTexGenericMask (line %d)", __FUNCTION__, __LINE__));
+
+		  if(mTexGenericMask.isNull())
+			  Con::warnf( "GuiBitmapButtonCtrl::setGenericMask - Unable to load texture: %s", mGenericMask.c_str() );
+	  }
+   } else
+   {
+      mTexGenericMask = NULL;
+   }
+}
+
+//------------------------------------------------------------------------------
+
 GuiBitmapButtonCtrl::Modifier GuiBitmapButtonCtrl::getCurrentModifier()
 {   
    U8 modifierKeys = Input::getModifierKeys();
@@ -466,6 +512,84 @@
       }
    }
 }
+
+//-----------------------------------------------------------------------------
+
+bool GuiBitmapButtonCtrl::pointInControl(const Point2I& parentCoordPoint)
+{
+	GFXTexHandle		*texture	= NULL;
+	GBitmap				*bmp		= NULL;
+	U32					index		= ModifierNone;
+
+
+	/// abort when coordinates aren't even within control rectangle boundaries
+	if(!Parent::pointInControl(parentCoordPoint))
+		return false;
+
+	if(mUseModifiers)
+		index = getCurrentModifier();
+
+	// use generic mask for shaped mask image, else use the modifier array version
+	if(mTexGenericMask)
+		texture = &mTexGenericMask;
+	else
+		texture = &mTextures[ index ].mTextureMask;
+
+	// abort shaped mask hit test if no shape mask image loaded
+	if(!texture || !*texture)
+		return true;
+
+	// get shaped mask bitmap, if available, else abort out
+	bmp = texture->getBitmap();
+	if(!bmp)
+		return true; // TODO: shouldn't we report a warning of somekind? --TRON
+
+	/// determine if point in control is hitting shaped mask
+	const RectI		&bounds		= getBounds();
+	S32				xt			= parentCoordPoint.x - bounds.point.x;
+	S32				yt			= parentCoordPoint.y - bounds.point.y;
+	ColorI			rColor(0x00, 0x00, 0x00);
+	ColorI			rMask( 0xFF, 0xFF, 0xFF);
+
+	// correct the test coordinates depending on drawing mode
+	switch (mBitmapMode)
+	{
+		case BitmapStretched:
+		{
+			F32		rw, rh;
+			S32		px			= getExtent().x;
+			S32		py			= getExtent().y;
+
+			// prevent divide by zero
+			if(!px) px = 1;
+			if(!py) py = 1;
+
+			// calculate stretched bitmap ratio
+			rw = (F32)texture->getWidth()  / px;
+			rh = (F32)texture->getHeight() / py;
+
+			// correct the coordinates by ratio
+			xt = (S32)(xt * rw);
+			yt = (S32)(yt * rh);
+			break;
+		}
+
+		case BitmapCentered:
+		{
+			xt -= getExtent().x / 2 - texture->getWidth() / 2;
+			yt -= getExtent().y / 2 - texture->getHeight() / 2;
+			break;
+		}
+	}
+
+	// get pixel color from shaped mask with provided coordinates
+	bmp->getColor(xt, yt, rColor);
+	rColor.alpha = 0xFF;
+
+	// when colors match then the point did hit shaped mask area, else it didn't
+	return (rColor == rMask);
+}
+
 
 //=============================================================================
 //    GuiBitmapButtonTextCtrl.
--- gui/buttons/guiBitmapButtonCtrl.h	Thu Jan 15 03:14:12 1970
+++ gui/buttons/guiBitmapButtonCtrl.h	Thu Jan 15 03:14:12 1970
@@ -22,6 +22,7 @@
 /// append '_h' for highlighted
 /// append '_d' for depressed
 /// append '_i' for inactive
+/// append '_m' for shaped mask
 ///
 /// If a bitmap cannot be found it will use the default bitmap to render.
 ///
@@ -77,6 +78,9 @@
          
          /// Texture for inactive state.
          GFXTexHandle mTextureInactive;
+
+         /// Texture for shaped hitmask.
+         GFXTexHandle mTextureMask;
       };
 
       /// Make control extents equal to bitmap size.
@@ -96,6 +100,12 @@
       /// File name for bitmap.
       String mBitmapName;
       
+	  /// File name for generic mask
+	  String mGenericMask;
+
+	  /// Texture for generic mask
+	  GFXTexHandle mTexGenericMask;
+      
       ///
       Textures mTextures[ NumModifiers ];
       
@@ -103,6 +113,7 @@
       
       static bool _setAutoFitExtents( void *object, const char *index, const char *data );
       static bool _setBitmap( void *object, const char *index, const char *data );
+	  static bool _setGenericMask( void *object, const char *index, const char *data );
       
       State getState() const
       {
@@ -135,12 +146,18 @@
 
       void setAutoFitExtents( bool state );
       void setBitmap( const String& name );
+	  void setGenericMask( const String& name );
 
       //Parent methods
       virtual bool onWake();
       virtual void onSleep();
       virtual void onAction();
       virtual void inspectPostApply();
+
+      /// This function will return true if the provided coordinates (wrt parent object) are
+      /// within the bounds of this control
+      /// @param   parentCoordPoint   Coordinates to test
+      virtual bool pointInControl(const Point2I& parentCoordPoint);
 
       virtual void onRender(Point2I offset, const RectI &updateRect);
 
