# Math, my evil nemesis, we meet again…

Question

Am I missing something glaringly obvious?

Life Story

I am working on a generic class that I can slap into my game that will simulate the sky. Right now I am working on transitions from night to day and back again. Incremental testing everything was going fine. Once I started setting the transitions to happen at a certain time, I am getting the wrong numbers for the incremental steps between colors. I don’t understand why I am getting the wrong numbers out of my code. I’ve gone over it several times and it seems right to me but at the back of my head I feel like I am missing something that I am going to feel retarded for not noticing once it is pointed out. I think it has something to do with the transitTime variable I am passing in to colorTransit(). In the following code, sunRise should begin at 4:30 (Which it does) and end at 7:30 (which it does). During that time the value of red should smoothly transition from 0 to 1 (which it does not). At different speeds the results are off by different amounts but in this example the red value is about 1.5 by the time 7:30 rolls around.

Code

I’ve gone ahead and stripped out code which is basically the same to make this easier to read.

global variables[java]

private float dayLength = convertToSeconds(24, 0, 0); // Length of day to simulate (24 hours)

private float daySpeed = convertToSeconds(0, 12, 0); // Length of day in real time (12 minutes or 2 hours simulated per minute)

private float timeOfDay = convertToSeconds(4, 29, 0); // current time (timeOfDay += dayLength/daySpeed * tpf)

private float sunRise = dayLength / 4;

private float sunSet = (dayLength / 4) * 3;

private float transitTime = dayLength / 8;

private boolean inTransit = false;

private float[] skyStep;

[/java]

update Method[java]

@Override

public void update(float tpf) {

skyTransit(sunRise, tpf);

}

[/java]

skyTransit Method[java]

private void skyTransit(float sun, float tpf) {

if (timeOfDay > sun - transitTime/2 && timeOfDay < sun + transitTime/2) {

if (!inTransit) {

inTransit = true;

skyStep = colorTransit(ColorRGBA.Black, ColorRGBA.Red, transitTime / daySpeed);

// Here is where I think the problem is. I feel like I’m not manipulating the transitTime correctly.

// But I don’t know. If I did, I might not need you. Oh who am I kidding, I’ll always need you.

// You’re special to me System.out.println(userName); No… just you baby. Why would you think

// that there’s anybody else? That’s nonsense.

}

updateColor(skyStep, skyMaterial, tpf);

}

[/java]

colorTransit method[java]

private float[] colorTransit(ColorRGBA start, ColorRGBA end, float seconds) {

if (seconds <= 0) {

logger.log(Level.WARNING, “Attempting a color transit over 0 seconds. Returning 0.”);

return new float[]{0, 0, 0};

}

float redStep = (end.getRed() - start.getRed()) / seconds *daySpeed;

float greenStep = (end.getGreen() - start.getGreen()) / seconds * daySpeed;

float blueStep = (end.getBlue() - start.getBlue()) / seconds * daySpeed;

return new float[]{redStep, greenStep, blueStep};

}

[/java]

updateColor method[java]

private void updateColor(float[] steps, Material material, float tpf) {

ColorRGBA current = (ColorRGBA) material.getParam(“Color”).getValue();

float r = current.r + steps[0] * tpf;

float g = current.g + steps[1] * tpf;

float b = current.b + steps[2] * tpf;

material.setColor(“Color”, new ColorRGBA(r, g, b, 1));

}

[/java]

What is the sun variable?

sun = sunRise = dayLength / 4

skyTransit(sunRise, tpf);

I would recommend you stop messing around with your color updating methods. Keep it simple.

I have some similar code which simulates the black body radiation color of something at a certain temperature.

Here is what I recommend you do.

Currently, unless I am reading this wrong, you are just going from black to red?

ColorRGBA has a function that does this already, color.Interpolate(startColor, endColor, changeAmount);

For something as simple as going from black to red, put in startColor Black, endColor red, change amount would be (currentTime-startTime)/(endTime-startTime) or something like that. I might have gotten that part wrong, but the idea is how far between the two points you are is what the fraction should be.

If you want to make a more amazing version you could do this:

Make an array of ColorRGBAs representing different times.

0 could be black, for night

1 could be blue for before sunrise

2 could be pink for dawn

3 could be blue for mid day

4 could be pink or orange for sunset

5 could be blue for dusk

6 could be black again

and then an array of times (of equal size) for when those steps happen.

TL;DR Use ColorRGBA.interpolate, it’s more hardcore. Also I like your code comments.

The intent is that once the code is working properly to change from basic colors into something that will be much more visually appealing, such as you’ve suggested. The black to red code I presented is just basic “Does this stupid thing work? No? How about if I do this?” code.

Later on I’ll give that interpolate thing another look. Maybe I’m over complicating things. I vaguely remember looking at it briefly and deciding it didn’t do exactly what I wanted but I also remember something about 1:30am, an empty Coke can, and fuzzy screen text so it is possible my decision making skills weren’t at full capacity. As I said though, I’ll look again.

@affreuxlex said:
The intent is that once the code is working properly to change from basic colors into something that will be much more visually appealing, such as you've suggested. The black to red code I presented is just basic "Does this stupid thing work? No? How about if I do this?" code.

Later on I'll give that interpolate thing another look. Maybe I'm over complicating things. I vaguely remember looking at it briefly and deciding it didn't do exactly what I wanted but I also remember something about 1:30am, an empty Coke can, and fuzzy screen text so it is possible my decision making skills weren't at full capacity. As I said though, I'll look again.

If you want I can show you some of my interpolating code. It's a little more complicated than I described though, because intensity of the color is also affected by the temperature. Essentially though, you want the same thing I use, just replace the word temperature with time in almost all cases hehehe.

Sure, if nothing else I might learn something from it. Much appreciated.

[java]

/** The base emissive color, which gets combined with the black body color /

public ColorRGBA emissiveColor = new ColorRGBA(ColorRGBA.Black);

/
* The base color of an object. Usually blended with relevant textures, etc. The default is white. /

public ColorRGBA baseColor = new ColorRGBA(ColorRGBA.White);

/
* Describes the temperature at which the black-body radiation of this object should have the specified color. /

private static final float BLACK_TEMP = 500,

RED_TEMP = 1000,

ORANGE_TEMP = 2000,

YELLOW_TEMP = 3500,

WHITE_TEMP = 6000,

BLUE_TEMP = 15000;

/
* Describes at what points different color hues are produced by black-body radiation. /

private static final float[] colorTemperatures = {BLACK_TEMP, RED_TEMP, ORANGE_TEMP, YELLOW_TEMP, WHITE_TEMP, BLUE_TEMP};

/
* Describes the brightness of black-body radiation at the given color hue. /

private static final float BLACK_FACTOR = .25f,

RED_FACTOR = .35f,

ORANGE_FACTOR = .55f,

YELLOW_FACTOR = .7f,

WHITE_FACTOR = .8f,

BLUE_FACTOR = 1;

/
* Describes the brightness of black-body radiation at various color hues. */

private static final float[] colorIntensities = {BLACK_FACTOR, RED_FACTOR, ORANGE_FACTOR, YELLOW_FACTOR, WHITE_FACTOR, BLUE_FACTOR};

/**

• Alters the object to reflect its temperature.

*/

public void temperatureBehavior(VisualThing thing) {

final float temperature = thing.temperature;

//the uses of this variable in this method are just to make sure there are no issues where we get the

//wrong result or an ArrayIndexOutOfBounds because of round-off error

final float TEMPERATURE_EPSILON = 0.01f;

float scale;

//if the object has the coldest emissive color

if (temperature < colorTemperatures[0] + TEMPERATURE_EPSILON) {

blackBodyColor.set(colors[0]);

scale = colorIntensities[0];

} //if the object has the hottest emissive color

else if (temperature > colorTemperatures[colorTemperatures.length - 1] - TEMPERATURE_EPSILON) {

blackBodyColor.set(colors[colors.length - 1]);

scale = colorIntensities[colors.length - 1];

} //if the object has an intermediate color

else {

int hotter = 1;

//loop condition guaranteed to fail by the time “hotter” gets to colorTemperatures.length-1, if

//it doesn’t fail before that. We start by comparing with colorTemperatures[1] because

//we already know from the previous checks that temperature is hotter than colorTemperatures[0]

while (temperature >= colorTemperatures[hotter]) {

hotter++;

}

int cooler = hotter - 1;

//interpolation parameter. Calculate where we are, on a linear scale of 0 to 1, between

//the cooler temperature and the hotter temperature

float t = (temperature - colorTemperatures[cooler])

/ (colorTemperatures[hotter] - colorTemperatures[cooler]);

//use the interpolation parameter to interpolate both the color and scale

blackBodyColor.interpolate(colors[cooler], colors[hotter], t);

scale = interpolate(colorIntensities[cooler], colorIntensities[hotter], t);

}

//Interpolate between the current blackbody color and black to set how bright it is

//TODO check if this should be scale or 1-scale

blackBodyColor.interpolate(ColorRGBA.Black, 1 - scale);

//Add the base emissive color to the black body color

//Clamp it to 1

blackBodyColor.clamp();

//This sets the glow color to the black body color

this.setColor(“GlowColor”, blackBodyColor);

this.setColor(“Ambient”, blackBodyColor);

//}

}

[/java]

@yuxemporos

Thank you for your help. I spent a little bit of time going over your code, reading the ColorRGBA class code and swearing at my own code. During that time I learned some new stuff and discovered my mistake. Also the reason why I didn’t use interpolate.

I hate it when I’m looking up a problem, find just the perfect post on it and it ends with “I fixed it, it works” Well what the hell? Mine doesn’t work, how did you fix it you bastard? So just in case someone 5 years from now stumbles across this and goes “What the hell, did it get fixed?” like I so often do, here is what I changed:

[java]

private float timeMultiplier = dayLength / daySpeed;

[/java]

In the skyTransit Method:

[java]

skyStep = colorTransit(ColorRGBA.Black, ColorRGBA.Red, transitTime / timeMultiplier);

[/java]

And in the colorTransit method I dropped the daySpeed variable on all 3 colors:

[java]

float redStep = (end.getRed() - start.getRed()) / seconds;

[/java]

Thanks again for your help. With working now working code and yours to learn from, I’m about to make some awesome sun rises that will make even the most hardcore biker wet his pants and weep in joy.

@affreuxlex said:
@yuxemporos
Thank you for your help. I spent a little bit of time going over your code, reading the ColorRGBA class code and swearing at my own code. During that time I learned some new stuff and discovered my mistake. Also the reason why I didn't use interpolate.

etc. etc.

Thanks again for your help. With working now working code and yours to learn from, I'm about to make some awesome sun rises that will make even the most hardcore biker wet his pants and weep in joy.

Glad I could help. If you really want to get em weeping, make sure you throw in a double rainbow.

You do know the ColorRGBA has a method called interpolate which will do all this for you, ya?

[java]ColorRGBA nColor = new ColorRGBA(0.0f,0.0f,0.0f,1.0f);

nColor = nColor.interpolate(ColorRGBA.Pink, ColorRGBA.Yellow, someBlendAmountThatChangesWithTime);

[/java]

It works just like the mix function in GLSL.

@t0neg0d said:
You do know the ColorRGBA has a method called interpolate which will do all this for you, ya?
[java]ColorRGBA nColor = new ColorRGBA(0.0f,0.0f,0.0f,1.0f);
nColor = nColor.interpolate(ColorRGBA.Pink, ColorRGBA.Yellow, someBlendAmountThatChangesWithTime);
[/java]
It works just like the mix function in GLSL.

Yeah I already mentioned that. He has some reason for avoiding it I guess.