diff --git a/Buttons.cpp b/Buttons.cpp index 67d44cb..8e308e7 100644 --- a/Buttons.cpp +++ b/Buttons.cpp @@ -58,7 +58,7 @@ extern void LoopPlay_Sound(uint8_t track); extern void Pause_Sound(); extern void Resume_Sound(); extern void Set_Loop_Playback(); -extern void Set_Volume(int8_t volumeSet=-1); +extern void Set_Volume(int8_t volumeSet); extern void Disable_FTDI(bool ftdi_off); extern void Disable_MP3(bool mp3_off); extern void confParseValue(uint16_t variable, uint16_t min, uint16_t max, @@ -91,7 +91,7 @@ void ConfigMenuButtonEventHandler(bool SaturateColor, ButtonActionEnum ButtonAct confParseValue(storage.volume, 5, 30, 1*incrementSign); storage.volume = value; BladeMeter(ledPins, value*100/30); - Set_Volume(); + Set_Volume(storage.volume); #if defined LS_INFO Serial.println(storage.volume); #endif @@ -133,7 +133,7 @@ void ConfigMenuButtonEventHandler(bool SaturateColor, ButtonActionEnum ButtonAct delay(50); } #endif // PIXELBLADE or STAR_LED - else if (ConfigModeSubStates == CS_FLICKERTYPE) { + else if (ConfigModeSubStates == CS_FLICKERTYPE and ButtonActionType==SINGLE_CLICK) { #ifdef LEDSTRINGS confParseValue(storage.sndProfile[storage.soundFont].flickerType, 0, 2, 1*incrementSign); // max number of flicker types for LEDSTRINGS currently 3 #endif @@ -152,14 +152,14 @@ void ConfigMenuButtonEventHandler(bool SaturateColor, ButtonActionEnum ButtonAct Serial.println(storage.sndProfile[storage.soundFont].flickerType); #endif } - else if (ConfigModeSubStates == CS_SWINGSENSITIVITY) { - // 2048LSB/g, -32k to +32k, but usable range is ~16384 - confParseValue(storage.sndProfile[storage.soundFont].swingSensitivity, 0, 16000, 100*incrementSign); + else if (ConfigModeSubStates == CS_SWINGSENSITIVITY and ButtonActionType==SINGLE_CLICK) { + // 2048LSB/g, -32k to +32k, but usable range is ~16384(=1g acceleration), increment with 1/100th of a g + confParseValue(storage.sndProfile[storage.soundFont].swingSensitivity, 0, 16000, 160*incrementSign); storage.sndProfile[storage.soundFont].swingSensitivity = value; #if defined LS_INFO Serial.println(storage.sndProfile[storage.soundFont].swingSensitivity); #endif - BladeMeter(ledPins, (storage.sndProfile[storage.soundFont].swingSensitivity)/160); + BladeMeter(ledPins, (storage.sndProfile[storage.soundFont].swingSensitivity)/100); } } @@ -317,7 +317,7 @@ void mainLongPressStart() { #ifdef PIXELBLADE pixelblade_KillKey_Disable(); #endif - Set_Volume(); + Set_Volume(storage.volume); delay(200); } #endif @@ -474,7 +474,7 @@ void lockupLongPressStart() { #ifdef PIXELBLADE pixelblade_KillKey_Disable(); #endif - Set_Volume(); + Set_Volume(storage.volume); delay(200); } } else if (SaberState==S_STANDBY) { //Entering Config Mode diff --git a/ConfigMenu.cpp b/ConfigMenu.cpp index 52bbfda..4275465 100644 --- a/ConfigMenu.cpp +++ b/ConfigMenu.cpp @@ -175,6 +175,7 @@ void NextConfigState(){ #if defined LS_FSM Serial.print(F("Swing Sensitivity")); #endif + BladeMeter(ledPins, (storage.sndProfile[storage.soundFont].swingSensitivity)/100); SinglePlay_Sound(26); delay(500); break; diff --git a/ConfigMenu.h b/ConfigMenu.h index 1b595dd..a59bfd2 100644 --- a/ConfigMenu.h +++ b/ConfigMenu.h @@ -19,13 +19,13 @@ enum ActionModeSubStatesEnum {AS_HUM, AS_IGNITION, AS_RETRACTION, AS_BLADELOCKUP // configure the config menu based on the blade type #if defined LEDSTRINGS -enum ConfigModeSubStatesEnum {CS_SOUNDFONT, CS_SLEEPINIT, CS_FLICKERTYPE, CS_VOLUME, CS_LASTMEMBER, CS_POWERONOFFTYPE, CS_SWINGSENSITIVITY, CS_MAINCOLOR, CS_CLASHCOLOR, CS_BLASTCOLOR, CS_BATTERYLEVEL, CS_STORAGEACCESS, CS_UARTMODE}; // never delete CS_LASTMEMBER!!! Needed to calculate number of elements in the enum type!!! +enum ConfigModeSubStatesEnum {CS_SOUNDFONT, CS_FLICKERTYPE, CS_SLEEPINIT, CS_LASTMEMBER, CS_VOLUME, CS_POWERONOFFTYPE, CS_SWINGSENSITIVITY, CS_MAINCOLOR, CS_CLASHCOLOR, CS_BLASTCOLOR, CS_BATTERYLEVEL, CS_STORAGEACCESS, CS_UARTMODE}; // never delete CS_LASTMEMBER!!! Needed to calculate number of elements in the enum type!!! #endif #if defined STAR_LED -enum ConfigModeSubStatesEnum {CS_BATTERYLEVEL, CS_SOUNDFONT, CS_SLEEPINIT, CS_FLICKERTYPE, CS_MAINCOLOR, CS_CLASHCOLOR, CS_BLASTCOLOR, CS_SWINGSENSITIVITY, CS_VOLUME, CS_LASTMEMBER, CS_POWERONOFFTYPE, CS_STORAGEACCESS, CS_UARTMODE}; // never delete CS_LASTMEMBER!!! Needed to calculate number of elements in the enum type!!! +enum ConfigModeSubStatesEnum {CS_SOUNDFONT, CS_SLEEPINIT, CS_FLICKERTYPE, CS_MAINCOLOR, CS_CLASHCOLOR, CS_BLASTCOLOR, CS_SWINGSENSITIVITY, CS_VOLUME, CS_LASTMEMBER, CS_BATTERYLEVEL, CS_POWERONOFFTYPE, CS_STORAGEACCESS, CS_UARTMODE}; // never delete CS_LASTMEMBER!!! Needed to calculate number of elements in the enum type!!! #endif #if defined PIXELBLADE -enum ConfigModeSubStatesEnum {CS_SOUNDFONT, CS_FLICKERTYPE, CS_POWERONOFFTYPE, CS_SLEEPINIT, CS_MAINCOLOR, CS_CLASHCOLOR, CS_BLASTCOLOR, CS_VOLUME, CS_LASTMEMBER, CS_BATTERYLEVEL, CS_SWINGSENSITIVITY, CS_STORAGEACCESS, CS_UARTMODE}; // never delete CS_LASTMEMBER!!! Needed to calculate number of elements in the enum type!!! +enum ConfigModeSubStatesEnum {CS_SOUNDFONT, CS_SLEEPINIT, CS_FLICKERTYPE, CS_MAINCOLOR, CS_CLASHCOLOR, CS_BLASTCOLOR, CS_SWINGSENSITIVITY, CS_LASTMEMBER, CS_VOLUME, CS_POWERONOFFTYPE, CS_BATTERYLEVEL, CS_STORAGEACCESS, CS_UARTMODE}; // never delete CS_LASTMEMBER!!! Needed to calculate number of elements in the enum type!!! #endif // ==================================================================================== diff --git a/Config_HW.h b/Config_HW.h index 6290916..3635b7f 100644 --- a/Config_HW.h +++ b/Config_HW.h @@ -63,6 +63,17 @@ //#define STAR_LED #define PIXELBLADE +/************************************/ +/* + * SABER TYPE + * currently in v1.3 only the CROSSGUARDSABER + * will have any effect on the code + * due to the fire blade effect + *************************************/ +#define SINGLEBLADE // i.e. Graflex +//#define SABERSTAFF // i.e. Darth Maul saber with dual blades +//#define CROSSGUARDSABER // i.e. Kylo Ren saber + /* * POWER SAVING CIRCUITRY * Definition of the power switches (DIYino Prime v1.5 or greater, STARDUST @@ -112,6 +123,7 @@ // define how many pixels are used for the crossguard and how many for the main blade #define CG_STRIPE 10 // cross guard stripe length #define MN_STRIPE 50 // main blade stripe length + #define STAGGERED_IGNITION_DELAY 1000 #endif @@ -170,21 +182,6 @@ #endif #endif - -/************************************/ -/* - * SABER TYPE - * currently in v1.3 only the CROSSGUARDSABER - * will have any effect on the code - * due to the fire blade effect - *************************************/ -#define SINGLEBLADE // i.e. Graflex -//#define SABERSTAFF // i.e. Darth Maul saber with dual blades -//#define CROSSGUARDSABER // i.e. Kylo Ren saber - - - - /*!!!!!IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT!!! * * MPU6050 device ORIENTATION diff --git a/Config_SW.h b/Config_SW.h index 192d9c9..2222587 100644 --- a/Config_SW.h +++ b/Config_SW.h @@ -149,7 +149,7 @@ const long InternalReferenceVoltage = 1062; // Adjust this value to your board' #define LS_SERIAL //enable serial communication using Wire library #if defined LS_SERIAL //#define LS_FSM -//#define LS_INFO +#define LS_INFO //#define LS_DEBUG #endif diff --git a/FX-SaberOS.ino b/FX-SaberOS.ino index 0dcbf6c..3face78 100644 --- a/FX-SaberOS.ino +++ b/FX-SaberOS.ino @@ -1,9 +1,7 @@ /* - FX-SaberOS V1.6.1 + FX-SaberOS V1.0 - Modified from LSOS 1.5 2017 March 3 -storage.sndProfile[storage.soundFont].swingSensitivity - released on: 21 Octber 2016 + released on: 7 October 2017 author: Sebastien CAPOU (neskweek@gmail.com) and Andras Kun (kun.andras@yahoo.de) Source : https://github.com/Protonerd/FX-SaberOS Description: Operating System for Arduino based LightSaber @@ -53,9 +51,6 @@ bool jukebox_play = false; // indicate whether a song is being played in JukeBox uint8_t jb_track; // sound file track number in the directory designated for music playback #endif -// for an unknown reason code fails to compile recently if this function is not pre-defined here -void Set_Volume(int8_t volumeSet=-1); - /*************************************************************************************************** * Saber Finite State Machine Custom Type and State Variable */ @@ -184,7 +179,6 @@ void setup() { // Serial line for debug Serial.begin(115200); - /***** LOAD CONFIG *****/ // Get config from EEPROM if there is one // or initialise value with default ones set in StoreStruct @@ -226,16 +220,24 @@ Serial.println(configAdress); Serial.println(F("EEPROM LOADED")); } #endif + // retreive the sound font ID stored in the EEPROM (last configured) soundFont.setID(storage.soundFont); // in case a fireblade flicker type is selected for the active sound font, set the bool variable if (storage.sndProfile[storage.soundFont].flickerType==2 or storage.sndProfile[storage.soundFont].flickerType==3 or storage.sndProfile[storage.soundFont].flickerType==4) {fireblade=true;} + +/* CONFIG ITEMS PRESETS */ +/* Set default values to parameters which can be modified in config menu, if the corresponding config menu item is disabled */ // if the config menu does not contain a menu item to define swing sensitivity, default it to 1000 (works very well, mid sensitivity) if (CS_SWINGSENSITIVITY > CS_LASTMEMBER) { for (uint8_t i=0; i CS_LASTMEMBER) { + storage.volume=31; + } + /***** LOAD CONFIG *****/ /***** MP6050 MOTION DETECTOR INITIALISATION *****/ @@ -314,8 +316,6 @@ Serial.println(configAdress); mpu.setZGyroOffset(44); #endif - - // make sure it worked (returns 0 if so) if (devStatus == 0) { // turn on the DMP, now that it's ready @@ -447,8 +447,9 @@ Serial.println(configAdress); InitDFPlayer(); delay(200); - pinMode(SPK1, INPUT); - pinMode(SPK2, INPUT); + // according to debug on 3.11.2017, these 2 lines below cause the sporadic disable of sound. For audio tracker they are not strictly needed. + //pinMode(SPK1, INPUT); + //pinMode(SPK2, INPUT); SinglePlay_Sound(11); delay(20); @@ -480,6 +481,7 @@ Serial.println(configAdress); PrevSaberState = S_SLEEP; ActionModeSubStates = AS_HUM; //Disable_MP3(true); // disable the MP3 in Stand-by mode to enable FTDI communication + } // ==================================================================================== @@ -537,8 +539,12 @@ void loop() { SinglePlay_Sound(soundFont.getPowerOn((storage.soundFont)*NR_FILE_SF)); // Light up the blade pixelblade_KillKey_Disable(); - lightIgnition(ledPins, soundFont.getPowerOnTime(), storage.sndProfile[storage.soundFont].poweronoffType, storage.sndProfile[storage.soundFont].mainColor); - + #ifdef CROSSGUARDSABER + lightIgnition(ledPins, soundFont.getPowerOnTime(), storage.sndProfile[storage.soundFont].poweronoffType, storage.sndProfile[storage.soundFont].mainColor, 11, 60); + #else // single blade or saber staff + lightIgnition(ledPins, soundFont.getPowerOnTime(), storage.sndProfile[storage.soundFont].poweronoffType, storage.sndProfile[storage.soundFont].mainColor); + #endif + sndSuppress = millis()-soundFont.getPowerOnTime(); sndSuppress2 = millis(); @@ -553,6 +559,13 @@ void loop() { //digitalWrite(ACCENT_LED, HIGH); #endif } + #ifdef CROSSGUARDSABER + if (millis()-sndSuppress2>STAGGERED_IGNITION_DELAY) { + SinglePlay_Sound(soundFont.getClash((storage.soundFont)*NR_FILE_SF)); + lightIgnition(ledPins, soundFont.getPowerOnTime(), storage.sndProfile[storage.soundFont].poweronoffType, storage.sndProfile[storage.soundFont].mainColor, 0, 10); + sndSuppress2=millis(); + } + #endif // ************************* blade movement detection ************************************ //Let's get our values ! @@ -596,8 +609,9 @@ void loop() { */ ActionModeSubStates = AS_CLASH; lightClashEffect(ledPins, storage.sndProfile[storage.soundFont].clashColor); - delay(CLASH_FX_DURATION); // clash duration - + if (!fireblade) { + delay(CLASH_FX_DURATION); // clash duration + } } } } @@ -626,7 +640,7 @@ void loop() { //getColor(storage.sndProfile[storage.soundFont].blasterboltColor); //lightOn(ledPins, -1, currentColor); blasterPixel = random(NUMPIXELS / 4, NUMPIXELS - 3); //momentary shut off one led segment - getColor(storage.sndProfile[storage.soundFont].blasterboltColor); + //getColor(storage.sndProfile[storage.soundFont].blasterboltColor); // lightBlasterEffect(blasterPixel, 3, storage.sndProfile[storage.soundFont].mainColor); lightBlasterEffect(ledPins, blasterPixel, map(NUMPIXELS, 0, 120, 1, 3), storage.sndProfile[storage.soundFont].blasterboltColor); } @@ -639,7 +653,7 @@ void loop() { } // #endif #endif - delay(BLASTER_FX_DURATION); // blaster bolt deflect duration + //delay(BLASTER_FX_DURATION); // blaster bolt deflect duration // Some Soundfont may not have Blaster sounds if (millis() - sndSuppress > 50) { //SinglePlay_Sound(soundFont.getBlaster((storage.soundFont)*NR_FILE_SF)); @@ -652,8 +666,8 @@ void loop() { We detect swings as hilt's orientation change since IMUs sucks at determining relative position in space */ - //else if ((not fireblade) and - else if (true and + else if ((not fireblade) and + //else if (true and (ActionModeSubStates != AS_BLADELOCKUP or lockuponclash)// end lockuponclash event on a swing #ifndef SWING_QUATERNION and (abs(curDeltAccel.y) > storage.sndProfile[storage.soundFont].swingSensitivity // and it has suffisent power on a certain axis @@ -899,7 +913,7 @@ void loop() { // turn on the volume full storage.volume = 30; //MAX BladeMeter(ledPins, storage.volume*100/30); - Set_Volume(); // Too Slow: we'll change volume on exit + Set_Volume(storage.volume); // Too Slow: we'll change volume on exit delay(50); #if defined LS_INFO Serial.println(storage.volume); @@ -912,9 +926,9 @@ void loop() { case CS_POWERONOFFTYPE: break; case CS_SWINGSENSITIVITY: - // upon clash increase swing sensitivity by 1000 - if (storage.sndProfile[storage.soundFont].swingSensitivity < 15000 ) { - storage.sndProfile[storage.soundFont].swingSensitivity=storage.sndProfile[storage.soundFont].swingSensitivity+1000; + // upon clash increase swing sensitivity by 1/10th of a g (1g=16384) + if (storage.sndProfile[storage.soundFont].swingSensitivity <= 14400 ) { + storage.sndProfile[storage.soundFont].swingSensitivity=storage.sndProfile[storage.soundFont].swingSensitivity+1600; } else { storage.sndProfile[storage.soundFont].swingSensitivity=0; @@ -928,7 +942,7 @@ void loop() { else { // storage.sndProfile[storage.soundFont].swingSensitivity=100; }*/ - BladeMeter(ledPins, (storage.sndProfile[storage.soundFont].swingSensitivity)/160); + BladeMeter(ledPins, (storage.sndProfile[storage.soundFont].swingSensitivity)/100); Serial.println(storage.sndProfile[storage.soundFont].swingSensitivity); break; case CS_SLEEPINIT: @@ -1330,13 +1344,13 @@ void LoopPlay_Sound(uint8_t track) { dfplayer.playSingleLoop(track); } -void Set_Volume(int8_t volumeSet=-1) { - if (volumeSet == -1) { // as a default retreive volume setting from config storage - dfplayer.setVolume(storage.volume); // Too Slow: we'll change volume on - } - else { +void Set_Volume(int8_t volumeSet) { + //if (volumeSet == -1) { // as a default retreive volume setting from config storage + // dfplayer.setVolume(storage.volume); // Too Slow: we'll change volume on + //} + //else { dfplayer.setVolume(volumeSet); - } + //} delay(50); } @@ -1349,10 +1363,10 @@ void InitDFPlayer() { // AK 7.9.2016: if the storage.volume has no or invalid value, it will cause the // sketch to repeat setup (reset itself) - up till now no idea why? // this can happen if the EEPROM is erased (i.e. reflash of bootloader) - if (CS_LASTMEMBER < CS_VOLUME) { // if the volume cannot be set from the config menu, set it to the loudest - storage.volume=30; - } - dfplayer.setVolume(storage.volume); + //if (CS_LASTMEMBER < CS_VOLUME) { // if the volume cannot be set from the config menu, set it to the loudest + // storage.volume=31; + //} + //dfplayer.setVolume(storage.volume); //setup finished. Boot ready. We notify ! } diff --git a/Light.cpp b/Light.cpp index 85ed304..4b16fed 100644 --- a/Light.cpp +++ b/Light.cpp @@ -66,12 +66,12 @@ static uint8_t Fire_Cooling = 50; // Higher chance = more roaring fire. Lower chance = more flickery fire. // Default 120, suggested range 50-200. static uint8_t Fire_Sparking = 100; -#ifdef CROSSGUARDSABER -static byte heat[MN_STRIPE]; -static byte heat_cg[CG_STRIPE]; -#else +//#ifdef CROSSGUARDSABER +//static byte heat[MN_STRIPE]; +//static byte heat_cg[CG_STRIPE]; +//#else static byte heat[NUMPIXELS]; -#endif +//#endif #define PIXELSTEP 5// how many pixel to treat as a group to save on processing capability #endif // PIXELBLADE @@ -184,7 +184,7 @@ void getColor(cRGB color={0,0,0}) { #endif } // getColor -void RampBlade(uint16_t RampDuration, bool DirectionUpDown) { +void RampBlade(uint16_t RampDuration, bool DirectionUpDown, uint8_t startpixel=0, uint8_t stoppixel=NUMPIXELS) { #if defined LEDSTRINGS #endif @@ -198,12 +198,12 @@ void RampBlade(uint16_t RampDuration, bool DirectionUpDown) { unsigned long ignitionStart = millis(); //record start of ramp function cRGB value; if (fireblade) { // #ifdef FIREBLADE - for (unsigned int i=0; ii) or (!DirectionUpDown and j>NUMPIXELS-1-i)){ + else if ((DirectionUpDown and j>i) or (!DirectionUpDown and j>stoppixel-1-i)){ value.r=0; value.g=0; value.b=0; @@ -215,11 +215,11 @@ void RampBlade(uint16_t RampDuration, bool DirectionUpDown) { } } // fireblade else { //#else - for (unsigned int i = 0; i < NUMPIXELS; i = NUMPIXELS*(millis()-ignitionStart)/RampDuration) { // turn on/off the number of LEDs that match rap timing + for (unsigned int i = startpixel; i < stoppixel; i = stoppixel*(millis()-ignitionStart)/RampDuration) { // turn on/off the number of LEDs that match rap timing //generate a flicker effect between 65% and 115% of MAX_BRIGHTNESS, with a 1 in 115 chance of flicking to 0 int flickFactor = random(0,115); if (flickFactor < 65 && flickFactor > 0) { flickFactor = 100; } - for(uint8_t j=0; j= 2; k--) { heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3; } for( int k= CG_STRIPE - 1; k >= 2; k--) { heat_cg[k] = (heat_cg[k - 1] + heat_cg[k - 2] + heat_cg[k - 2] ) / 3; } -#else +#else*/ for( int k= NUMPIXELS - 1; k >= 2; k--) { heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3; } -#endif +//#endif // Step 3. Randomly ignite new 'sparks' of heat near the bottom -#ifdef CROSSGUARDSABER +/*#ifdef CROSSGUARDSABER if( random(255) < Fire_Sparking ) { int y = random(7); heat[y] = constrain(heat[y] + random(95)+160,0,255 ); @@ -1018,15 +1031,15 @@ void FireBlade(uint8_t DominantColor) { int y = random(4); heat_cg[y] = constrain(heat_cg[0] + random(95)+160,0,255 ); } -#else +#else*/ if( random(255) < Fire_Sparking ) { int y = random(7); heat[y] = constrain(heat[y] + random(95)+160,0,255 ); } -#endif +//#endif // Step 4. Map from heat cells to LED colors -#ifdef CROSSGUARDSABER +/*#ifdef CROSSGUARDSABER for( int j = 0; j < CG_STRIPE; j++) { cRGB color = HeatColor( heat_cg[j],DominantColor); //if( gReverseDirection ) { @@ -1045,12 +1058,12 @@ void FireBlade(uint8_t DominantColor) { //} pixels.set_crgb_at(j, color); // Set value at LED found at index j } -#else +#else*/ for( int j = 0; j < NUMPIXELS; j++) { cRGB color = HeatColor( heat[j],DominantColor); pixels.set_crgb_at(j, color); // Set value at LED found at index j } -#endif +//#endif } // CRGB HeatColor( uint8_t temperature) diff --git a/Light.h b/Light.h index 02da200..9893c33 100644 --- a/Light.h +++ b/Light.h @@ -38,8 +38,8 @@ void BladeMeter (uint8_t ledPins[], int meterLevel); void lightOn(uint8_t ledPins[], int8_t segment = -1, cRGB color={0,0,0}, int8_t StartPixel=-1, int8_t StopPixel=-1); void lightOff(); void getColor(cRGB color={0,0,0}); //getColor -void RampBlade(uint16_t RampDuration, bool DirectionUpDown); -void lightIgnition(uint8_t ledPins[], uint16_t time, uint8_t type, cRGB color={0,0,0}); +void RampBlade(uint16_t RampDuration, bool DirectionUpDown, uint8_t startpixel=0, uint8_t stoppixel=NUMPIXELS); +void lightIgnition(uint8_t ledPins[], uint16_t time, uint8_t type, cRGB color={0,0,0}, uint8_t startpixel=0, uint8_t stoppixel=NUMPIXELS); void lightRetract(uint8_t ledPins[], uint16_t time, uint8_t type,cRGB color={0,0,0}); void lightFlicker(uint8_t ledPins[],uint8_t type, uint8_t value = 0,cRGB maincolor={0,0,0}, cRGB clashcolor={0,0,0},uint8_t AState=0); void ColorMixing(cRGB colorID={0,0,0}, int8_t mod=-1, uint8_t maxBrightness=MAX_BRIGHTNESS, bool Saturate=false);