Преглед на файлове

Merge pull request #1533 from GarageGames/pr/1334

New color picker - #1334 clone
Anis преди 9 години
родител
ревизия
9efe22649b

+ 86 - 1
Engine/source/console/consoleFunctions.cpp

@@ -39,6 +39,9 @@
 #include "core/util/journal/journal.h"
 #include "gfx/gfxEnums.h"
 #include "core/util/uuid.h"
+#include "core/color.h"
+#include "math/mPoint3.h"
+#include "math/mathTypes.h"
 
 // This is a temporary hack to get tools using the library to
 // link in this module which contains no other references.
@@ -1018,6 +1021,88 @@ DefineConsoleFunction( strrchrpos, S32, ( const char* str, const char* chr, S32
 
 //----------------------------------------------------------------
 
+DefineConsoleFunction(ColorFloatToInt, ColorI, (ColorF color), ,
+	"Convert from a float color to an integer color (0.0 - 1.0 to 0 to 255).\n"
+	"@param color Float color value to be converted in the form \"R G B A\", where R is red, G is green, B is blue, and A is alpha.\n"
+	"@return Converted color value (0 - 255)\n\n"
+	"@tsexample\n"
+	"ColorFloatToInt( \"0 0 1 0.5\" ) // Returns \"0 0 255 128\".\n"
+	"@endtsexample\n"
+	"@ingroup Strings")
+{
+	return (ColorI)color;
+}
+
+DefineConsoleFunction(ColorIntToFloat, ColorF, (ColorI color), ,
+   "Convert from a integer color to an float color (0 to 255 to 0.0 - 1.0).\n"
+   "@param color Integer color value to be converted in the form \"R G B A\", where R is red, G is green, B is blue, and A is alpha.\n"
+   "@return Converted color value (0.0 - 1.0)\n\n"
+   "@tsexample\n"
+   "ColorIntToFloat( \"0 0 255 128\" ) // Returns \"0 0 1 0.5\".\n"
+   "@endtsexample\n"
+   "@ingroup Strings")
+{
+   return (ColorF)color;
+}
+
+DefineConsoleFunction(ColorRGBToHEX, const char*, (ColorI color), ,
+   "Convert from a integer RGB (red, green, blue) color to hex color value (0 to 255 to 00 - FF).\n"
+   "@param color Integer color value to be converted in the form \"R G B A\", where R is red, G is green, B is blue, and A is alpha. It excepts an alpha, but keep in mind this will not be converted.\n"
+   "@return Hex color value (#000000 - #FFFFFF), alpha isn't handled/converted so it is only the RGB value\n\n"
+   "@tsexample\n"
+   "ColorRBGToHEX( \"0 0 255 128\" ) // Returns \"#0000FF\".\n"
+   "@endtsexample\n"
+   "@ingroup Strings")
+{
+   return Con::getReturnBuffer(color.getHex());
+}
+
+DefineConsoleFunction(ColorRGBToHSB, const char*, (ColorI color), ,
+   "Convert from a integer RGB (red, green, blue) color to HSB (hue, saturation, brightness). HSB is also know as HSL or HSV as well, with the last letter standing for lightness or value.\n"
+   "@param color Integer color value to be converted in the form \"R G B A\", where R is red, G is green, B is blue, and A is alpha. It excepts an alpha, but keep in mind this will not be converted.\n"
+   "@return HSB color value, alpha isn't handled/converted so it is only the RGB value\n\n"
+   "@tsexample\n"
+   "ColorRBGToHSB( \"0 0 255 128\" ) // Returns \"240 100 100\".\n"
+   "@endtsexample\n"
+   "@ingroup Strings")
+{
+   ColorI::Hsb hsb(color.getHSB());
+   String s(String::ToString(hsb.hue) + " " + String::ToString(hsb.sat) + " " + String::ToString(hsb.brightness));
+   return Con::getReturnBuffer(s);
+}
+
+DefineConsoleFunction(ColorHEXToRGB, ColorI, (const char* hex), ,
+   "Convert from a hex color value to an integer RGB (red, green, blue) color (00 - FF to 0 to 255).\n"
+   "@param hex Hex color value (#000000 - #FFFFFF) to be converted to an RGB (red, green, blue) value.\n"
+   "@return Integer color value to be converted in the form \"R G B A\", where R is red, G is green, B is blue, and A is alpha. Alpha isn't handled/converted so only pay attention to the RGB value\n\n"
+   "@tsexample\n"
+   "ColorHEXToRGB( \"#0000FF\" ) // Returns \"0 0 255 0\".\n"
+   "@endtsexample\n"
+   "@ingroup Strings")
+{
+   S32 rgb = dAtoui(hex, 16);
+
+   ColorI color;
+   color.set(rgb & 0x000000FF, (rgb & 0x0000FF00) >> 8, (rgb & 0x00FF0000) >> 16);
+   return color;
+}
+
+DefineConsoleFunction(ColorHSBToRGB, ColorI, (Point3I hsb), ,
+   "Convert from a HSB (hue, saturation, brightness) to an integer RGB (red, green, blue) color. HSB is also know as HSL or HSV as well, with the last letter standing for lightness or value.\n"
+   "@param hsb HSB (hue, saturation, brightness) value to be converted.\n"
+   "@return Integer color value to be converted in the form \"R G B A\", where R is red, G is green, B is blue, and A is alpha. Alpha isn't handled/converted so only pay attention to the RGB value\n\n"
+   "@tsexample\n"
+   "ColorHSBToRGB( \"240 100 100\" ) // Returns \"0 0 255 0\".\n"
+   "@endtsexample\n"
+   "@ingroup Strings")
+{
+   ColorI color;
+   color.set(ColorI::Hsb(hsb.x, hsb.y, hsb.z));
+   return color;
+}
+
+//----------------------------------------------------------------
+
 DefineConsoleFunction( strToggleCaseToWords, const char*, ( const char* str ),,
    "Parse a Toggle Case word into separate words.\n"
    "@param str The string to parse.\n"
@@ -3068,4 +3153,4 @@ DefineEngineFunction( getMaxDynamicVerts, S32, (),,
 	"@return the max number of allowable dynamic vertices in a single vertex buffer" )
 {
    return MAX_DYNAMIC_VERTS / 2;
-}
+}

+ 284 - 0
Engine/source/core/color.h

@@ -30,6 +30,10 @@
 #include "math/mPoint4.h" 
 #endif
 
+#ifndef _ENGINEAPI_H_
+#include "console/engineAPI.h"
+#endif
+
 class ColorI;
 
 
@@ -121,9 +125,20 @@ class ColorI
    U8 blue;
    U8 alpha;
 
+   struct Hsb
+   {
+      Hsb() :hue(0), sat(0), brightness(0){};
+      Hsb(U32 h, U32 s, U32 b) :hue(h), sat(s), brightness(b){};
+
+      U32 hue;   ///Hue
+      U32 sat;   ///Saturation
+      U32 brightness;   //Brightness/Value/Lightness
+   };
+
   public:
    ColorI() { }
    ColorI(const ColorI& in_rCopy);
+   ColorI(const Hsb& color);
    ColorI(const U8 in_r,
           const U8 in_g,
           const U8 in_b,
@@ -132,6 +147,12 @@ class ColorI
 
    ColorI( const char* pStockColorName );
 
+   void set(const Hsb& color);
+
+   void HSLtoRGB_Subfunction(U32& c, const F64& temp1, const F64& temp2, const F64& temp3);
+
+   void set(const String& hex);
+
    void set(const U8 in_r,
             const U8 in_g,
             const U8 in_b,
@@ -176,6 +197,11 @@ class ColorI
    U16 get565()  const;
    U16 get4444() const;
 
+   Hsb getHSB() const;
+
+   String getHex() const;
+   S32 convertFromHex(const String& hex) const;
+
    operator ColorF() const;
 
    operator const U8*() const { return &red; }
@@ -459,6 +485,174 @@ inline void ColorI::set(const ColorI& in_rCopy,
    alpha = in_a;
 }
 
+inline void ColorI::set(const Hsb& color)
+{
+	U32 r = 0;
+	U32 g = 0;
+	U32 b = 0;
+
+	F64 L = ((F64)color.brightness) / 100.0;
+	F64 S = ((F64)color.sat) / 100.0;
+	F64 H = ((F64)color.hue) / 360.0;
+
+	if (color.sat == 0)
+	{
+		r = color.brightness;
+		g = color.brightness;
+		b = color.brightness;
+	}
+	else
+	{
+		F64 temp1 = 0;
+		if (L < 0.50)
+		{
+			temp1 = L*(1 + S);
+		}
+		else
+		{
+			temp1 = L + S - (L*S);
+		}
+
+		F64 temp2 = 2.0*L - temp1;
+
+		F64 temp3 = 0;
+		for (S32 i = 0; i < 3; i++)
+		{
+			switch (i)
+			{
+			case 0: // red
+			{
+				temp3 = H + 0.33333;
+				if (temp3 > 1.0)
+					temp3 -= 1.0;
+				HSLtoRGB_Subfunction(r, temp1, temp2, temp3);
+				break;
+			}
+			case 1: // green
+			{
+				temp3 = H;
+				HSLtoRGB_Subfunction(g, temp1, temp2, temp3);
+				break;
+			}
+			case 2: // blue
+			{
+				temp3 = H - 0.33333;
+				if (temp3 < 0)
+					temp3 += 1;
+				HSLtoRGB_Subfunction(b, temp1, temp2, temp3);
+				break;
+			}
+			default:
+			{
+
+			}
+			}
+		}
+	}
+	red = (U32)((((F64)r) / 100) * 255);
+	green = (U32)((((F64)g) / 100) * 255);
+	blue = (U32)((((F64)b) / 100) * 255);
+}
+
+// This is a subfunction of HSLtoRGB
+inline void ColorI::HSLtoRGB_Subfunction(U32& c, const F64& temp1, const F64& temp2, const F64& temp3)
+{
+	if ((temp3 * 6.0) < 1.0)
+		c = (U32)((temp2 + (temp1 - temp2)*6.0*temp3)*100.0);
+	else
+		if ((temp3 * 2.0) < 1.0)
+			c = (U32)(temp1*100.0);
+		else
+			if ((temp3 * 3.0) < 2.0)
+				c = (U32)((temp2 + (temp1 - temp2)*(0.66666 - temp3)*6.0)*100.0);
+			else
+				c = (U32)(temp2*100.0);
+	return;
+}
+
+inline void ColorI::set(const String& hex)
+{
+	String redString;
+	String greenString;
+	String blueString;
+
+	//if the prefix # was attached to hex
+	if (hex[0] == '#')
+	{
+		redString = hex.substr(1, 2);
+		greenString = hex.substr(3, 2);
+		blueString = hex.substr(5, 2);
+	}
+	else
+	{
+		// since there is no prefix attached to hex
+		redString = hex.substr(0, 2);
+		greenString = hex.substr(2, 2);
+		blueString = hex.substr(4, 2);
+	}
+
+	red = (U8)(convertFromHex(redString));
+	green = (U8)(convertFromHex(greenString));
+	blue = (U8)(convertFromHex(blueString));
+}
+
+inline S32 ColorI::convertFromHex(const String& hex) const
+{
+	S32 hexValue = 0;
+
+	S32 a = 0;
+	S32 b = hex.length() - 1;
+
+	for (; b >= 0; a++, b--)
+	{
+		if (hex[b] >= '0' && hex[b] <= '9')
+		{
+			hexValue += (hex[b] - '0') * (1 << (a * 4));
+		}
+		else
+		{
+			switch (hex[b])
+			{
+			case 'A':
+			case 'a':
+				hexValue += 10 * (1 << (a * 4));
+				break;
+
+			case 'B':
+			case 'b':
+				hexValue += 11 * (1 << (a * 4));
+				break;
+
+			case 'C':
+			case 'c':
+				hexValue += 12 * (1 << (a * 4));
+				break;
+
+			case 'D':
+			case 'd':
+				hexValue += 13 * (1 << (a * 4));
+				break;
+
+			case 'E':
+			case 'e':
+				hexValue += 14 * (1 << (a * 4));
+				break;
+
+			case 'F':
+			case 'f':
+				hexValue += 15 * (1 << (a * 4));
+				break;
+
+			default:
+				Con::errorf("Error, invalid character '%c' in hex number", hex[a]);
+				break;
+			}
+		}
+	}
+
+	return hexValue;
+}
+
 inline ColorI::ColorI(const ColorI& in_rCopy)
 {
    red   = in_rCopy.red;
@@ -467,6 +661,11 @@ inline ColorI::ColorI(const ColorI& in_rCopy)
    alpha = in_rCopy.alpha;
 }
 
+inline ColorI::ColorI(const Hsb& color)
+{
+	set(color);
+}
+
 inline ColorI::ColorI(const U8 in_r,
                const U8 in_g,
                const U8 in_b,
@@ -648,6 +847,91 @@ inline U16 ColorI::get4444() const
               U16(U16(blue  >> 4) <<  0));
 }
 
+inline ColorI::Hsb ColorI::getHSB() const
+{
+	F64 rPercent = ((F64)red) / 255;
+	F64 gPercent = ((F64)green) / 255;
+	F64 bPercent = ((F64)blue) / 255;
+
+	F64 maxColor = 0.0;
+	if ((rPercent >= gPercent) && (rPercent >= bPercent))
+		maxColor = rPercent;
+	if ((gPercent >= rPercent) && (gPercent >= bPercent))
+		maxColor = gPercent;
+	if ((bPercent >= rPercent) && (bPercent >= gPercent))
+		maxColor = bPercent;
+
+	F64 minColor = 0.0;
+	if ((rPercent <= gPercent) && (rPercent <= bPercent))
+		minColor = rPercent;
+	if ((gPercent <= rPercent) && (gPercent <= bPercent))
+		minColor = gPercent;
+	if ((bPercent <= rPercent) && (bPercent <= gPercent))
+		minColor = bPercent;
+
+	F64 H = 0.0;
+	F64 S = 0.0;
+	F64 B = 0.0;
+
+	B = (maxColor + minColor) / 2.0;
+
+	if (maxColor == minColor)
+	{
+		H = 0.0;
+		S = 0.0;
+	}
+	else
+	{
+		if (B < 0.50)
+		{
+			S = (maxColor - minColor) / (maxColor + minColor);
+		}
+		else
+		{
+			S = (maxColor - minColor) / (2.0 - maxColor - minColor);
+		}
+		if (maxColor == rPercent)
+		{
+			H = (gPercent - bPercent) / (maxColor - minColor);
+		}
+		if (maxColor == gPercent)
+		{
+			H = 2.0 + (bPercent - rPercent) / (maxColor - minColor);
+		}
+		if (maxColor == bPercent)
+		{
+			H = 4.0 + (rPercent - gPercent) / (maxColor - minColor);
+		}
+	}
+
+	ColorI::Hsb val;
+	val.sat = (U32)(S * 100);
+	val.brightness = (U32)(B * 100);
+	H = H*60.0;
+	if (H < 0.0)
+		H += 360.0;
+	val.hue = (U32)H;
+
+	return val;
+}
+
+inline String ColorI::getHex() const
+{
+	char r[255];
+	dSprintf(r, sizeof(r), "%.2X", red);
+	String result(r);
+
+	char g[255];
+	dSprintf(g, sizeof(g), "%.2X", green);
+	result += g;
+
+	char b[255];
+	dSprintf(b, sizeof(b), "%.2X", blue);
+	result += b;
+
+	return result;
+}
+
 //-------------------------------------- INLINE CONVERSION OPERATORS
 inline ColorF::operator ColorI() const
 {

+ 241 - 126
Engine/source/gui/controls/guiColorPicker.cpp

@@ -39,13 +39,13 @@ ColorF colorAlpha(0.0f, 0.0f, 0.0f, 0.0f);
 ColorF colorAlphaW(1.0f, 1.0f, 1.0f, 0.0f);
 
 ColorI GuiColorPickerCtrl::mColorRange[7] = {
-    ColorI(255,0,0),     // Red
-	ColorI(255,0,255),   // Pink
-	ColorI(0,0,255),     // Blue
-	ColorI(0,255,255),   // Light blue
-	ColorI(0,255,0),     // Green
-	ColorI(255,255,0),   // Yellow
-	ColorI(255,0,0),      // Red
+   ColorI(255,0,0),     // Red
+   ColorI(255,0,255),   // Pink
+   ColorI(0,0,255),     // Blue
+   ColorI(0,255,255),   // Light blue
+   ColorI(0,255,0),     // Green
+   ColorI(255,255,0),   // Yellow
+   ColorI(255,0,0),     // Red
 };
 /// @}
 
@@ -57,7 +57,6 @@ ConsoleDocClass( GuiColorPickerCtrl,
    "@internal"
 );
 
-//--------------------------------------------------------------------------
 GuiColorPickerCtrl::GuiColorPickerCtrl()
 {
    setExtent(140, 30);
@@ -70,44 +69,50 @@ GuiColorPickerCtrl::GuiColorPickerCtrl()
    mPositionChanged = false;
    mSelectorGap = 1;
    mActionOnMove = false;
-	mShowReticle = true;
+   mShowReticle = true;
+   mSelectColor = false;
+   mSetColor = mSetColor.BLACK;
+   mBitmap = NULL;
 }
 
-//--------------------------------------------------------------------------
+GuiColorPickerCtrl::~GuiColorPickerCtrl()
+{
+   if (mBitmap)
+   {
+      delete mBitmap;
+      mBitmap = NULL;
+   }
+}
 
 ImplementEnumType( GuiColorPickMode,
    "\n\n"
    "@ingroup GuiUtil"
    "@internal" )
-   { GuiColorPickerCtrl::pPallet,		"Pallete"   },
-   { GuiColorPickerCtrl::pHorizColorRange,	"HorizColor"},
-   { GuiColorPickerCtrl::pVertColorRange,	"VertColor" },
-   { GuiColorPickerCtrl::pHorizColorBrightnessRange,	"HorizBrightnessColor"},
-   { GuiColorPickerCtrl::pVertColorBrightnessRange,	"VertBrightnessColor" },
-   { GuiColorPickerCtrl::pBlendColorRange,	"BlendColor"},
-   { GuiColorPickerCtrl::pHorizAlphaRange,	"HorizAlpha"},
-   { GuiColorPickerCtrl::pVertAlphaRange,	"VertAlpha" },
-   { GuiColorPickerCtrl::pDropperBackground,	"Dropper" },
+   { GuiColorPickerCtrl::pPallet, "Pallete" },
+   { GuiColorPickerCtrl::pHorizColorRange, "HorizColor"},
+   { GuiColorPickerCtrl::pVertColorRange, "VertColor" },
+   { GuiColorPickerCtrl::pHorizColorBrightnessRange, "HorizBrightnessColor" },
+   { GuiColorPickerCtrl::pVertColorBrightnessRange, "VertBrightnessColor" },
+   { GuiColorPickerCtrl::pBlendColorRange, "BlendColor" },
+   { GuiColorPickerCtrl::pHorizAlphaRange, "HorizAlpha" },
+   { GuiColorPickerCtrl::pVertAlphaRange, "VertAlpha" },
+   { GuiColorPickerCtrl::pDropperBackground, "Dropper" },
 EndImplementEnumType;
 
-//--------------------------------------------------------------------------
 void GuiColorPickerCtrl::initPersistFields()
 {
    addGroup("ColorPicker");
-   
       addField("baseColor", TypeColorF, Offset(mBaseColor, GuiColorPickerCtrl));
       addField("pickColor", TypeColorF, Offset(mPickColor, GuiColorPickerCtrl));
       addField("selectorGap", TypeS32,  Offset(mSelectorGap, GuiColorPickerCtrl)); 
       addField("displayMode", TYPEID< PickMode >(), Offset(mDisplayMode, GuiColorPickerCtrl) );
       addField("actionOnMove", TypeBool,Offset(mActionOnMove, GuiColorPickerCtrl));
       addField("showReticle", TypeBool, Offset(mShowReticle, GuiColorPickerCtrl));
-   
    endGroup("ColorPicker");
 
    Parent::initPersistFields();
 }
 
-//--------------------------------------------------------------------------
 // Function to draw a box which can have 4 different colors in each corner blended together
 void GuiColorPickerCtrl::drawBlendBox(RectI &bounds, ColorF &c1, ColorF &c2, ColorF &c3, ColorF &c4)
 {
@@ -119,54 +124,54 @@ void GuiColorPickerCtrl::drawBlendBox(RectI &bounds, ColorF &c1, ColorF &c2, Col
    //A couple of checks to determine if color blend
    if(c1 == colorWhite && c3 == colorAlpha && c4 == colorBlack)
    {
-		//Color
-		PrimBuild::begin( GFXTriangleFan, 4 );
-		PrimBuild::color( c2 );
-		PrimBuild::vertex2i( r, t );
+      //Color
+      PrimBuild::begin(GFXTriangleFan, 4);
+      PrimBuild::color( c2 );
+      PrimBuild::vertex2i( r, t );
 
-		PrimBuild::color( c2 );
-		PrimBuild::vertex2i( r, b );
+      PrimBuild::color( c2 );
+      PrimBuild::vertex2i( r, b );
 
-		PrimBuild::color( c2 );
-		PrimBuild::vertex2i( l, b );
+      PrimBuild::color( c2 );
+      PrimBuild::vertex2i( l, b );
 
-		PrimBuild::color( c2 );
-		PrimBuild::vertex2i( l, t );
-		PrimBuild::end();
+      PrimBuild::color( c2 );
+      PrimBuild::vertex2i( l, t );
+      PrimBuild::end();
 
-		//White
-		PrimBuild::begin( GFXTriangleFan, 4 );
-		PrimBuild::color( colorAlphaW );
-		PrimBuild::vertex2i( r, t );
+      //White
+      PrimBuild::begin( GFXTriangleFan, 4 );
+      PrimBuild::color( colorAlphaW );
+      PrimBuild::vertex2i( r, t );
 
-		PrimBuild::color( colorAlphaW );
-		PrimBuild::vertex2i( r, b );
+      PrimBuild::color( colorAlphaW );
+      PrimBuild::vertex2i( r, b );
 
-		PrimBuild::color( c1 );
-		PrimBuild::vertex2i( l, b );
+      PrimBuild::color( c1 );
+      PrimBuild::vertex2i( l, b );
 
-		PrimBuild::color( c1 );
-		PrimBuild::vertex2i( l, t );
-		PrimBuild::end();
+      PrimBuild::color( c1 );
+      PrimBuild::vertex2i( l, t );
+      PrimBuild::end();
 
-		//Black 
-		PrimBuild::begin( GFXTriangleFan, 4 );
-		PrimBuild::color( c3 );
-		PrimBuild::vertex2i( r, t );
+      //Black
+      PrimBuild::begin( GFXTriangleFan, 4 );
+      PrimBuild::color( c3 );
+      PrimBuild::vertex2i( r, t );
 
-		PrimBuild::color( c4 );
-		PrimBuild::vertex2i( r, b );
+      PrimBuild::color( c4 );
+      PrimBuild::vertex2i( r, b );
 
-		PrimBuild::color( c4 );
-		PrimBuild::vertex2i( l, b );
+      PrimBuild::color( c4 );
+      PrimBuild::vertex2i( l, b );
 
-		PrimBuild::color( c3 );
-		PrimBuild::vertex2i( l, t );
-		PrimBuild::end();
+      PrimBuild::color( c3 );
+      PrimBuild::vertex2i( l, t );
+      PrimBuild::end();
    }
    else
    {
-	  PrimBuild::begin( GFXTriangleFan, 4 );
+      PrimBuild::begin( GFXTriangleFan, 4 );
       PrimBuild::color( c1 );
       PrimBuild::vertex2i( l, t );
 
@@ -233,31 +238,29 @@ void GuiColorPickerCtrl::drawBlendRangeBox(RectI &bounds, bool vertical, U8 numC
 
 void GuiColorPickerCtrl::drawSelector(RectI &bounds, Point2I &selectorPos, SelectorMode mode)
 {
-	if( !mShowReticle )
-		return; 
-
-	U16 sMax = mSelectorGap*2;
-	switch (mode)
-	{
-		case sVertical:
-			// Now draw the vertical selector
-			// Up -> Pos
-			if (selectorPos.y != bounds.point.y+1)
-				GFX->getDrawUtil()->drawLine(selectorPos.x, bounds.point.y, selectorPos.x, selectorPos.y-sMax-1, colorWhiteBlend);
-			// Down -> Pos
-			if (selectorPos.y != bounds.point.y+bounds.extent.y) 
-				GFX->getDrawUtil()->drawLine(selectorPos.x,	selectorPos.y + sMax, selectorPos.x, bounds.point.y + bounds.extent.y, colorWhiteBlend);
-		break;
-		case sHorizontal:
-			// Now draw the horizontal selector
-			// Left -> Pos
-			if (selectorPos.x != bounds.point.x) 
+   if( !mShowReticle )
+      return; 
+
+   U16 sMax = mSelectorGap*2;
+   switch (mode)
+   {
+      case sVertical:
+         // Now draw the vertical selector Up -> Pos
+         if (selectorPos.y != bounds.point.y+1)
+            GFX->getDrawUtil()->drawLine(selectorPos.x, bounds.point.y, selectorPos.x, selectorPos.y-sMax-1, colorWhiteBlend);
+         // Down -> Pos
+         if (selectorPos.y != bounds.point.y+bounds.extent.y) 
+            GFX->getDrawUtil()->drawLine(selectorPos.x,	selectorPos.y + sMax, selectorPos.x, bounds.point.y + bounds.extent.y, colorWhiteBlend);
+      break;
+      case sHorizontal:
+         // Now draw the horizontal selector Left -> Pos
+         if (selectorPos.x != bounds.point.x) 
             GFX->getDrawUtil()->drawLine(bounds.point.x, selectorPos.y-1, selectorPos.x-sMax, selectorPos.y-1, colorWhiteBlend);
-			// Right -> Pos
-			if (selectorPos.x != bounds.point.x) 
+         // Right -> Pos
+         if (selectorPos.x != bounds.point.x) 
             GFX->getDrawUtil()->drawLine(bounds.point.x+mSelectorPos.x+sMax, selectorPos.y-1, bounds.point.x + bounds.extent.x, selectorPos.y-1, colorWhiteBlend);
-		break;
-	}
+      break;
+   }
 }
 
 //--------------------------------------------------------------------------
@@ -269,10 +272,10 @@ void GuiColorPickerCtrl::renderColorBox(RectI &bounds)
    pickerBounds.point.y = bounds.point.y+1;
    pickerBounds.extent.x = bounds.extent.x-1;
    pickerBounds.extent.y = bounds.extent.y-1;
-   
+
    if (mProfile->mBorder)
       GFX->getDrawUtil()->drawRect(bounds, mProfile->mBorderColor);
-      
+
    Point2I selectorPos = Point2I(bounds.point.x+mSelectorPos.x+1, bounds.point.y+mSelectorPos.y+1);
 
    // Draw color box differently depending on mode
@@ -338,56 +341,169 @@ void GuiColorPickerCtrl::onRender(Point2I offset, const RectI& updateRect)
       desc.setZReadWrite(false);
       desc.zWriteEnable = false;
       desc.setCullMode(GFXCullNone);
-      mStateBlock = GFX->createStateBlock( desc );
+      mStateBlock = GFX->createStateBlock(desc);
    }
 
-   RectI boundsRect(offset, getExtent()); 
+   RectI boundsRect(offset, getExtent());
    renderColorBox(boundsRect);
 
-   if (mPositionChanged) 
+   if (mPositionChanged || mBitmap == NULL)
    {
+      bool nullBitmap = false;
+
+      if (mPositionChanged == false && mBitmap == NULL)
+         nullBitmap = true;
+
       mPositionChanged = false;
       Point2I extent = getRoot()->getExtent();
+
       // If we are anything but a pallete, change the pick color
       if (mDisplayMode != pPallet)
       {
          Point2I resolution = getRoot()->getExtent();
 
          U32 buf_x = offset.x + mSelectorPos.x + 1;
-         U32 buf_y = resolution.y - ( extent.y - ( offset.y + mSelectorPos.y + 1 ) );
+         U32 buf_y = resolution.y - (extent.y - (offset.y + mSelectorPos.y + 1));
 
-         GFXTexHandle bb( resolution.x, 
-                          resolution.y, 
-                          GFXFormatR8G8B8A8, &GFXDefaultRenderTargetProfile, avar("%s() - bb (line %d)", __FUNCTION__, __LINE__) );
-         
-         Point2I tmpPt( buf_x, buf_y );
+         GFXTexHandle bb( resolution.x, resolution.y, GFXFormatR8G8B8A8, &GFXDefaultRenderTargetProfile, avar("%s() - bb (line %d)", __FUNCTION__, __LINE__) );
+
+         Point2I tmpPt(buf_x, buf_y);
 
          GFXTarget *targ = GFX->getActiveRenderTarget();
-         targ->resolveTo( bb );
-         
-         GBitmap bmp( bb.getWidth(), bb.getHeight() );
+         targ->resolveTo(bb);
+
+         if (mBitmap)
+         {
+            delete mBitmap;
+            mBitmap = NULL;
+         }
+
+         mBitmap = new GBitmap(bb.getWidth(), bb.getHeight());
+
+         bb.copyToBmp(mBitmap);
+
+         if (!nullBitmap)
+         {
+            if (mSelectColor)
+            {
+               Point2I pos = findColor(mSetColor, offset, resolution, *mBitmap);
+               mSetColor = mSetColor.BLACK;
+               mSelectColor = false;
+               setSelectorPos(pos);
+            }
+            else
+            {
+               ColorI tmp;
+               mBitmap->getColor(buf_x, buf_y, tmp);
+
+               mPickColor = (ColorF)tmp;
+
+               // Now do onAction() if we are allowed
+               if (mActionOnMove)
+                  onAction();
+            }
+         }
+      }
+   }
 
-         bb.copyToBmp( &bmp );
-         
-         //bmp.writePNGDebug( "foo.png" );
+   //render the children
+   renderChildControls(offset, updateRect);
+}
 
-         ColorI tmp;
-         bmp.getColor( buf_x, buf_y, tmp );
+void GuiColorPickerCtrl::setSelectorPos(const ColorF & color)
+{
+   if (mBitmap && !mPositionChanged)
+   {
+      Point2I resolution = getRoot() ? getRoot()->getExtent() : Point2I(1024, 768);
+      RectI rect(getGlobalBounds());
+      Point2I pos = findColor(color, rect.point, resolution, *mBitmap);
+      mSetColor = mSetColor.BLACK;
+      mSelectColor = false;
 
-         mPickColor = (ColorF)tmp;
+      setSelectorPos(pos);
+   }
+   else
+   {
+      mSetColor = color;
+      mSelectColor = true;
+      mPositionChanged = true;
+   }
+}
+
+Point2I GuiColorPickerCtrl::findColor(const ColorF & color, const Point2I& offset, const Point2I& resolution, GBitmap& bmp)
+{
+   RectI rect;
+   Point2I ext = getExtent();
+   if (mDisplayMode != pDropperBackground)
+   {
+      ext.x -= 3;
+      ext.y -= 2;
+      rect = RectI(Point2I(1, 1), ext);
+   }
+   else
+   {
+      rect = RectI(Point2I(0, 0), ext);
+   }
+
+   Point2I closestPos(-1, -1);
+
+   /* Debugging
+   char filename[256];
+   dSprintf( filename, 256, "%s.%s", "colorPickerTest", "png" );
+
+   // Open up the file on disk.
+   FileStream fs;
+   if ( !fs.open( filename, Torque::FS::File::Write ) )
+   Con::errorf( "GuiObjectView::saveAsImage() - Failed to open output file '%s'!", filename );
+   else
+   {
+   // Write it and close.
+   bmp.writeBitmap( "png", fs );
+
+   fs.close();
+   }
+   */
 
-         // Now do onAction() if we are allowed
-         if (mActionOnMove) 
-            onAction();
+   ColorI tmp;
+   U32 buf_x;
+   U32 buf_y;
+   ColorF curColor;
+   F32 val(10000.0f);
+   F32 closestVal(10000.0f);
+   bool closestSet = false;
+
+   for (S32 x = rect.point.x; x <= rect.extent.x; x++)
+   {
+      for (S32 y = rect.point.y; y <= rect.extent.y; y++)
+      {
+         buf_x = offset.x + x + 1;
+         buf_y = (resolution.y - (offset.y + y + 1));
+         buf_y = resolution.y - buf_y;
+
+         //Get the color at that position
+         bmp.getColor(buf_x, buf_y, tmp);
+         curColor = (ColorF)tmp;
+
+         //Evaluate how close the color is to our desired color
+         val = mFabs(color.red - curColor.red) + mFabs(color.green - curColor.green) + mFabs(color.blue - curColor.blue);
+
+         if (!closestSet)
+         {
+            closestVal = val;
+            closestPos.set(x, y);
+            closestSet = true;
+         }
+         else if (val < closestVal)
+         {
+            closestVal = val;
+            closestPos.set(x, y);
+         }
       }
-      
    }
-   
-   //render the children
-   renderChildControls( offset, updateRect);
+
+   return closestPos;
 }
 
-//--------------------------------------------------------------------------
 void GuiColorPickerCtrl::setSelectorPos(const Point2I &pos)
 {
    Point2I extent = getExtent();
@@ -432,7 +548,6 @@ void GuiColorPickerCtrl::setSelectorPos(const Point2I &pos)
    }
 }
 
-//--------------------------------------------------------------------------
 void GuiColorPickerCtrl::onMouseDown(const GuiEvent &event)
 {
    if (!mActive)
@@ -445,14 +560,14 @@ void GuiColorPickerCtrl::onMouseDown(const GuiEvent &event)
    
    if (mProfile->mCanKeyFocus)
       setFirstResponder();
-	
-	if (mActive && (mDisplayMode != pDropperBackground)) 
+
+   if (mActive && (mDisplayMode != pDropperBackground))
       onAction();
 
    // Update the picker cross position
    if (mDisplayMode != pPallet)
-      setSelectorPos(globalToLocalCoord(event.mousePoint)); 
-   
+      setSelectorPos(globalToLocalCoord(event.mousePoint));
+
    mMouseDown = true;
 }
 
@@ -468,10 +583,8 @@ void GuiColorPickerCtrl::onMouseDragged(const GuiEvent &event)
 
    if( !mActionOnMove )
       execAltConsoleCallback();
-
 }
 
-//--------------------------------------------------------------------------
 void GuiColorPickerCtrl::onMouseMove(const GuiEvent &event)
 {
    // Only for dropper mode
@@ -479,45 +592,40 @@ void GuiColorPickerCtrl::onMouseMove(const GuiEvent &event)
       setSelectorPos(globalToLocalCoord(event.mousePoint));
 }
 
-//--------------------------------------------------------------------------
 void GuiColorPickerCtrl::onMouseEnter(const GuiEvent &event)
 {
    mMouseOver = true;
 }
 
-//--------------------------------------------------------------------------
 void GuiColorPickerCtrl::onMouseLeave(const GuiEvent &)
 {
    // Reset state
    mMouseOver = false;
 }
 
-//--------------------------------------------------------------------------
 void GuiColorPickerCtrl::onMouseUp(const GuiEvent &)
 {
    //if we released the mouse within this control, perform the action
-	if (mActive && mMouseDown && (mDisplayMode != pDropperBackground)) 
+   if (mActive && mMouseDown && (mDisplayMode != pDropperBackground))
       mMouseDown = false;
 
-   if (mActive && (mDisplayMode == pDropperBackground)) 
+   if (mActive && (mDisplayMode == pDropperBackground))
    {
       // In a dropper, the alt command executes the mouse up action (to signal stopping)
       execAltConsoleCallback();
    }
-   
+
    mouseUnlock();
 }
 
-//--------------------------------------------------------------------------
 const char *GuiColorPickerCtrl::getScriptValue()
 {
    static char temp[256];
    ColorF color = getValue();
-   dSprintf(temp,256,"%f %f %f %f",color.red, color.green, color.blue, color.alpha);
-   return temp; 
+   dSprintf( temp, 256, "%f %f %f %f", color.red, color.green, color.blue, color.alpha );
+   return temp;
 }
 
-//--------------------------------------------------------------------------    
 void GuiColorPickerCtrl::setScriptValue(const char *value)
 {
    ColorF newValue;
@@ -537,5 +645,12 @@ DefineConsoleMethod(GuiColorPickerCtrl, setSelectorPos, void, (Point2I newPos),
 
 DefineConsoleMethod(GuiColorPickerCtrl, updateColor, void, (), , "Forces update of pick color")
 {
-	object->updateColor();
+   object->updateColor();
+}
+
+DefineEngineMethod(GuiColorPickerCtrl, setSelectorColor, void, (ColorF color), ,
+   "Sets the current position of the selector based on a color.n"
+   "@param color Color to look for.n")
+{
+   object->setSelectorPos(color);
 }

+ 25 - 20
Engine/source/gui/controls/guiColorPicker.h

@@ -59,29 +59,28 @@ class GuiColorPickerCtrl : public GuiControl
   public:
    enum PickMode
    {
-     pPallet = 0,		///< We just have a solid color; We just act like a pallet 
-     pHorizColorRange,		///< We have a range of base colors going horizontally
-     pVertColorRange,		///< We have a range of base colors going vertically
+     pPallet = 0,                ///< We just have a solid color; We just act like a pallet 
+     pHorizColorRange,           ///< We have a range of base colors going horizontally
+     pVertColorRange,            ///< We have a range of base colors going vertically
      pHorizColorBrightnessRange, ///< HorizColorRange with brightness
-     pVertColorBrightnessRange, ///< VertColorRange with brightness
-     pBlendColorRange,		///< We have a box which shows a range in brightness of the color
-     pHorizAlphaRange,		///< We have a box which shows a range in alpha going horizontally
-     pVertAlphaRange,		///< We have a box which shows a range in alpha going vertically
-     pDropperBackground		///< The control does not draw anything; Only does something when you click, or move the mouse (when active)
+     pVertColorBrightnessRange,  ///< VertColorRange with brightness
+     pBlendColorRange,           ///< We have a box which shows a range in brightness of the color
+     pHorizAlphaRange,           ///< We have a box which shows a range in alpha going horizontally
+     pVertAlphaRange,            ///< We have a box which shows a range in alpha going vertically
+     pDropperBackground          ///< The control does not draw anything; Only does something when you click, or move the mouse (when active)
    };
    
    enum SelectorMode
    {
-     sHorizontal = 0,		///< Horizontal selector with small gap
-     sVertical,			///< Vertical selector with small gap
+     sHorizontal = 0,            ///< Horizontal selector with small gap
+     sVertical,                  ///< Vertical selector with small gap
    };
-  
+
   protected:
-   
    /// @name Core Rendering functions
    /// @{
-   void renderColorBox(RectI &bounds);			///< Function that draws the actual color box
-   void drawSelector(RectI &bounds, Point2I &selectorPos, SelectorMode mode);	///< Function that draws the selection indicator
+   void renderColorBox(RectI &bounds); ///< Function that draws the actual color box
+   void drawSelector(RectI &bounds, Point2I &selectorPos, SelectorMode mode); /// < Function that draws the selection indicator
    void drawBlendBox(RectI &bounds, ColorF &c1, ColorF &c2, ColorF &c3, ColorF &c4);
    void drawBlendRangeBox(RectI &bounds, bool vertical, U8 numColors, ColorI *colors);
    /// @}
@@ -98,7 +97,11 @@ class GuiColorPickerCtrl : public GuiControl
    bool mMouseDown;		///< Mouse button down?
    bool mActionOnMove;		///< Perform onAction() when position has changed?
 
-	
+   bool mSelectColor;
+   ColorF mSetColor;
+   GBitmap* mBitmap;
+
+   Point2I findColor(const ColorF & color, const Point2I& offset, const Point2I& resolution, GBitmap& bmp);
    
    S32   mSelectorGap;		///< The half-way "gap" between the selector pos and where the selector is allowed to draw. 
 
@@ -107,12 +110,13 @@ class GuiColorPickerCtrl : public GuiControl
    static ColorI mColorRange[7]; ///< Color range for pHorizColorRange and pVertColorRange
    /// @}
 
-  public:   
-   
+  public:
+
    DECLARE_CONOBJECT(GuiColorPickerCtrl);
    DECLARE_CATEGORY( "Gui Editor" );
    
    GuiColorPickerCtrl();
+   ~GuiColorPickerCtrl();
 
    static void initPersistFields();
    void onRender(Point2I offset, const RectI &updateRect);
@@ -122,18 +126,19 @@ class GuiColorPickerCtrl : public GuiControl
    /// NOTE: setValue only sets baseColor, since setting pickColor wouldn't be useful
    void setValue(ColorF &value) {mBaseColor = value;}
    /// NOTE: getValue() returns baseColor if pallet (since pallet controls can't "pick" colours themselves)
-   ColorF getValue() {return mDisplayMode == pPallet ? mBaseColor : mPickColor;}
+   ColorF getValue() { return mDisplayMode == pPallet ? mBaseColor : mPickColor; }
    const char *getScriptValue();
    void setScriptValue(const char *value);
    void updateColor() {mPositionChanged = true;}
    /// @}
-   
+
    /// @name Selector Functions
    /// @{
    void setSelectorPos(const Point2I &pos); ///< Set new pos (in local coords)
+   void setSelectorPos(const ColorF & color);
    Point2I getSelectorPos() {return mSelectorPos;}
    /// @}
-   
+
    /// @name Input Events
    /// @{
    void onMouseDown(const GuiEvent &);

+ 73 - 24
Engine/source/gui/controls/guiTextEditCtrl.cpp

@@ -128,6 +128,8 @@ GuiTextEditCtrl::GuiTextEditCtrl()
 
    mActive = true;
 
+   mTextValid = true;
+
    mTextOffsetReset = true;
 
    mHistoryDirty = false;
@@ -1252,14 +1254,16 @@ void GuiTextEditCtrl::onLoseFirstResponder()
    Parent::onLoseFirstResponder();
 }
 
-void GuiTextEditCtrl::onRender(Point2I offset, const RectI &updateRect)
+void GuiTextEditCtrl::onRender( Point2I offset, const RectI &updateRect )
 {
    RectI ctrlRect( offset, getExtent() );
 
    //if opaque, fill the update rect with the fill color
    if ( mProfile->mOpaque )
    {
-      if(isFirstResponder())
+      if ( !mTextValid )
+         GFX->getDrawUtil()->drawRectFill( ctrlRect, mProfile->mFillColorERR );
+      else if ( isFirstResponder() )
          GFX->getDrawUtil()->drawRectFill( ctrlRect, mProfile->mFillColorHL );
       else
          GFX->getDrawUtil()->drawRectFill( ctrlRect, mProfile->mFillColor );
@@ -1267,7 +1271,11 @@ void GuiTextEditCtrl::onRender(Point2I offset, const RectI &updateRect)
 
    //if there's a border, draw the border
    if ( mProfile->mBorder )
+   {
       renderBorder( ctrlRect, mProfile );
+      if ( !mTextValid )
+         GFX->getDrawUtil()->drawRectFill( ctrlRect, mProfile->mFillColorERR );
+   }
 
    drawText( ctrlRect, isFirstResponder() );
 }
@@ -1490,7 +1498,25 @@ void GuiTextEditCtrl::drawText( const RectI &drawRect, bool isFocused )
 
 bool GuiTextEditCtrl::hasText()
 {
-   return (mTextBuffer.length());
+   return ( mTextBuffer.length() );
+}
+
+void GuiTextEditCtrl::invalidText(bool playSound)
+{
+   mTextValid = false;
+
+   if ( playSound )
+      playDeniedSound();
+}
+
+void GuiTextEditCtrl::validText()
+{
+   mTextValid = true;
+}
+
+bool GuiTextEditCtrl::isValidText()
+{
+   return mTextValid;
 }
 
 void GuiTextEditCtrl::playDeniedSound()
@@ -1520,27 +1546,29 @@ void GuiTextEditCtrl::handleCharInput( U16 ascii )
    //see if it's a number field
    if ( mProfile->mNumbersOnly )
    {
-      if ( ascii == '-')
-      {
-         //a minus sign only exists at the beginning, and only a single minus sign
-         if ( mCursorPos != 0 && !isAllTextSelected() )
-         {
-            playDeniedSound();
-            return;
-         }
-
-         if ( mInsertOn && ( mTextBuffer.getChar(0) == '-' ) ) 
-         {
-            playDeniedSound();
-            return;
-         }
-      }
-      // BJTODO: This is probably not unicode safe.
-      else if (  ascii != '.' && (ascii < '0' || ascii > '9')  )
-      {
-         playDeniedSound();
-         return;
-      }
+	   if (ascii == '-')
+	   {
+		   //a minus sign only exists at the beginning, and only a single minus sign
+		   if (mCursorPos != 0 && !isAllTextSelected())
+		   {
+			   invalidText();
+			   return;
+		   }
+
+		   if (mInsertOn && (mTextBuffer.getChar(0) == '-'))
+		   {
+			   invalidText();
+			   return;
+		   }
+	   }
+	   // BJTODO: This is probably not unicode safe.
+	   else if (ascii != '.' && (ascii < '0' || ascii > '9'))
+	   {
+		   invalidText();
+		   return;
+	   }
+	   else
+		   validText();
    }
 
    //save the current state
@@ -1748,3 +1776,24 @@ DefineEngineMethod( GuiTextEditCtrl, forceValidateText, void, (),,
 {
    object->forceValidateText();
 }
+
+DefineEngineMethod(GuiTextEditCtrl, invalidText, void, (bool playSound), (true),
+	"@brief Trigger the invalid sound and make the box red.nn"
+	"@param playSound Play the invalid text sound or not.n")
+{
+	object->invalidText(playSound);
+}
+
+
+DefineEngineMethod(GuiTextEditCtrl, validText, void, (), ,
+	"@brief Restores the box to normal color.nn")
+{
+	object->validText();
+}
+
+DefineEngineMethod(GuiTextEditCtrl, isValidText, bool, (), ,
+	"@brief Returns if the text is set to valid or not.n"
+	"@Return true if text is set to valid, false if not.nn")
+{
+	return object->isValidText();
+}

+ 6 - 0
Engine/source/gui/controls/guiTextEditCtrl.h

@@ -93,6 +93,8 @@ protected:
    void playDeniedSound();
    void execConsoleCallback();
 
+   bool mTextValid;
+
    virtual void handleCharInput( U16 ascii );
 
    S32 findNextWord();   
@@ -119,6 +121,10 @@ public:
    S32   getCursorPos()   { return( mCursorPos ); }
    void  setCursorPos( const S32 newPos );
    
+   void invalidText(bool playSound = true);
+   void validText();
+   bool isValidText();
+
    bool isAllTextSelected();
    void selectAllText();
    void clearSelectedText();

+ 3 - 0
Engine/source/gui/core/guiTypes.cpp

@@ -269,6 +269,7 @@ GuiControlProfile::GuiControlProfile(void) :
    mFillColor(255,0,255,255),
    mFillColorHL(255,0,255,255),
    mFillColorNA(255,0,255,255),
+   mFillColorERR(255,0,0,255),
    mFillColorSEL(255,0,255,255),
    mBorderColor(255,0,255,255),
    mBorderColorHL(255,0,255,255),
@@ -334,6 +335,7 @@ GuiControlProfile::GuiControlProfile(void) :
       mFillColor     = def->mFillColor;
       mFillColorHL   = def->mFillColorHL;
       mFillColorNA   = def->mFillColorNA;
+      mFillColorERR  = def->mFillColorERR;
       mFillColorSEL  = def->mFillColorSEL;
 
       mBorder        = def->mBorder;
@@ -398,6 +400,7 @@ void GuiControlProfile::initPersistFields()
       addField("fillColor",     TypeColorI,     Offset(mFillColor, GuiControlProfile));
       addField("fillColorHL",   TypeColorI,     Offset(mFillColorHL, GuiControlProfile));
       addField("fillColorNA",   TypeColorI,     Offset(mFillColorNA, GuiControlProfile));
+      addField("fillColorERR",  TypeColorI,     Offset(mFillColorERR, GuiControlProfile));
       addField("fillColorSEL",  TypeColorI,     Offset(mFillColorSEL, GuiControlProfile));
       addField("border",        TypeS32,        Offset(mBorder, GuiControlProfile),
          "Border type (0=no border)." );

+ 1 - 0
Engine/source/gui/core/guiTypes.h

@@ -385,6 +385,7 @@ public:
    ColorI mFillColor;                              ///< Fill color, this is used to fill the bounds of the control if it is opaque
    ColorI mFillColorHL;                            ///< This is used instead of mFillColor if the object is highlighted
    ColorI mFillColorNA;                            ///< This is used instead of mFillColor if the object is not active or disabled
+   ColorI mFillColorERR;                           ///< This is used instead of mFillColor if the object has an error or is invalid
    ColorI mFillColorSEL;                           ///< This is used instead of mFillColor if the object is selected
 
    S32 mBorder;                                    ///< For most controls, if mBorder is > 0 a border will be drawn, some controls use this to draw different types of borders however @see guiDefaultControlRender.cc

+ 13 - 0
Engine/source/math/mConsoleFunctions.cpp

@@ -103,6 +103,19 @@ DefineConsoleFunction( mRound, S32, ( F32 v  ),,
    return mRound(v);
 }
 
+DefineConsoleFunction( mRoundColour, F32, ( F32 v, S32 n ), (0),
+   "Round v to the nth decimal place or the nearest whole number by default."
+   "@param v Value to roundn"
+   "@param n Number of decimal places to round to, 0 by defaultn"
+   "@return The rounded value as a S32."
+   "@ingroup Math")
+{
+   if (n <= 0)
+      return mRound(v);
+   else
+      return mRound(v, n);
+}
+
 DefineConsoleFunction( mCeil, S32, ( F32 v ),,
     "Round v up to the nearest integer.\n"
     "@param v Number to convert to integer."

Файловите разлики са ограничени, защото са твърде много
+ 682 - 266
Templates/Empty/game/tools/gui/colorPicker.ed.gui


Файловите разлики са ограничени, защото са твърде много
+ 682 - 266
Templates/Full/game/tools/gui/colorPicker.ed.gui


Някои файлове не бяха показани, защото твърде много файлове са промени