Modularize flower generation

socram8888

Member
Contributor
Why not infinite layers? Using a different file for each image, you can divide the width by the height and get the amount of cells in each strip:

Then using a text file, describe the valid chrominance for each cell, like .block, but .plant:
Code:
{
    "hue": [ // In degrees
        "80-160", // Green for the stem
        "0-359", // Any for each flower cell
        "0-359",
        "0-359",
        "0-359"
    ]
}
Then calculate a HSL color using cell's red layer as saturation, cell's green as luminance, and a random chrominance in range, and convert it to RGB so we can display it. Finally assemble the image, blending each layer from the first (lower) to the last (upper), taking into account the alpha channel.

Might look complex, but it lets you create a virtually unlimited amount of different plants.
 

eleazzaar

Member
Contributor
Art
Why not infinite layers? Using a different file for each image, you can divide the width by the height and get the amount of cells in each strip:

Then using a text file, describe the valid chrominance for each cell, like .block, but .plant:
Code:
{
    "hue": [ // In degrees
        "80-160", // Green for the stem
        "0-359", // Any for each flower cell
        "0-359",
        "0-359",
        "0-359"
    ]
}
Then calculate a HSL color using cell's red layer as saturation, cell's green as luminance, and a random chrominance in range, and convert it to RGB so we can display it. Finally assemble the image, blending each layer from the first (lower) to the last (upper), taking into account the alpha channel.

Might look complex, but it lets you create a virtually unlimited amount of different plants.
I don't understand what you mean by "infinite layers". Probably you don't you are using "layers" to mean something differently than we have been in this thread previously.

I don't see any advantage to your color-shifting scheme, but significant disadvantage. As an artist who's above average in technical savvy-- i can deal with using R, G, & B as separate stand-ins to be replaced with colors from a palette. I can make art with any decent app, and have a decent guess how it will turn out. But i have serious difficulty visualizing, and working with a scheme where the R channel represents saturation, etc. The scheme needs to be accessible to humans too.
 

socram8888

Member
Contributor
The scheme needs to be accessible to humans too.
The scheme doesn't have to be accessible to humans. Artists doesn't need to mess with HSV pictures, in the same way a programmer doesn't need to know how does the CPU process each instruction, or a 3D artist doesn't manually edit each vertex in their models.

Here's a picture with the image drawn in RGB:
tulip.png

Now here's the same picture in HSV:
tulip.hsv.png

Red channel stores saturation, and green channel stores the value. Hue is stored in a seperate file:
Code:
{
"hue": [
"112.9117828464",
"306.38115353953",
"180.72429246235",
"226.60941394807",
"59.316385984516"
]
}
Do you think I've really done this by hand? Nope. I didn't. I just wrote a 40-line PHP script that converted the RGB to this special format. Nothing more, nothing else:
Code:
<?php
 
$imrgb = imagecreatefrompng("tulip.png");
 
$cellSize = imagesy($imrgb);
$cellCount = imagesx($imrgb) / $cellSize;
 
if ($cellCount != (int) $cellCount) {
    die("Invalid width (not a multiple of the height)\n");
}
echo "Cell count: $cellCount\n";
 
$imhsv = imagecreatetruecolor(imagesx($imrgb), imagesy($imrgb));
imagealphablending($imhsv, false);
imagesavealpha($imhsv, true);
$hsvjson = fopen("tulip.plant", "w");
fwrite($hsvjson, "{\n");
fwrite($hsvjson, "\t\"hue\": [\n");
 
for ($cell = 0; $cell < $cellCount; $cell++) {
    echo "Cell $cell:\n";
    // Average hue
    $avgHue = 0;
    $avgSamples = 0;
 
    for ($rx = 0; $rx < $cellSize; $rx++) {
        $x = $cell * $cellSize + $rx;
        for ($y = 0; $y < $cellSize; $y++) {
            $rgb = imagecolorsforindex($imrgb, imagecolorat($imrgb, $x, $y));
            $hsl = rgb2hsl(array($rgb["red"] / 255, $rgb["green"] / 255, $rgb["blue"] / 255));
            if ($rgb["alpha"] < 127) { // Not fully transparent
                $avgHue += $hsl[0];
                $avgSamples++;
            }
            imagesetpixel($imhsv, $x, $y, imagecolorallocatealpha($imhsv, $hsl[1] * 255, $hsl[2] * 255, 0, $rgb["alpha"]));
        }
    }
    $avgHue /= $avgSamples;
    if ($cell == $cellCount - 1) {
        fwrite($hsvjson, "\t\t\"" . $avgHue . "\"\n");
    } else {
        fwrite($hsvjson, "\t\t\"" . $avgHue . "\",\n");
    }
}
fwrite($hsvjson, "\t]\n");
fwrite($hsvjson, "}\n");
 
imagepng($imhsv, "tulip.hsl.png");
 
// From Drupal
function rgb2hsl($rgb) {
    $r = $rgb[0];
    $g = $rgb[1];
    $b = $rgb[2];
    $min = min($r, min($g, $b));
    $max = max($r, max($g, $b));
    $delta = $max - $min;
    $l = ($min + $max) / 2;
    $s = 0;
    if ($l > 0 && $l < 1) {
        $s = $delta / ($l < 0.5 ? (2 * $l) : (2 - 2 * $l));
    }
    $h = 0;
    if ($delta > 0) {
        if ($max == $r && $max != $g) $h += ($g - $b) / $delta;
        if ($max == $g && $max != $b) $h += (2 + ($b - $r) / $delta);
        if ($max == $b && $max != $r) $h += (4 + ($r - $g) / $delta);
        $h /= 6;
    }
    return array($h, $s, $l);
}
 
?>
Now, you want to create a new flower? Just generate a random hue with a valid value, read saturation and value from each pixel, blend all the cells into one (with the first being the lower and the last the upper), and bam!

Using this approach you can have as many different colors as you want. Just add one extra cell.
 

socram8888

Member
Contributor
tulip.rnd.png
tulip.rnd.png
tulip.rnd.png
tulip.rnd.png
tulip.rnd.png
tulip.rnd.png

This is how do generated images look like for this stripe:
tulip.hsl.png

Hue file:
Code:
{
    "hue": [
        "0.23-0.41",
        "0.5-1.2",
        "0.5-1.2",
        "0.5-1.2",
        "0.5-1.2"
    ]
}
 

eleazzaar

Member
Contributor
Art
Do you think I've really done this by hand? Nope. I didn't. I just wrote a 40-line PHP script that converted the RGB to this special format. Nothing more, nothing else:
So then we have all the images in a non-standard file format, right? One that can't be directly opened, or previewed by normal means. Presumably we could have another converter that returns them to RGB, but that adds friction to the process of using/managing/editing the images without benefit. I think 4 is a good number of colors -- glass' concern that more could get out of hand is IMHO valid. But if we really wanted more colors, i believe cyan, yellow, and magenta could be used (each defined as one of the channels being zero, the other two being equal).
 

socram8888

Member
Contributor
As I already pointed out, this is designed to be handled by a computer, not an human. Converting to this kind of file is the last step, done automatically by means of a script. The artist only have to build a set of layers, put one aside the other like this:
tulip.png

Then run the script which outputs a saturation-lightness picture and a hue file with the average hue of each cell, which can be easily edited using notepad to choose which colors can appear in a certain layer (green for the stems, random for the petals...)

So should you want to modify or improve a picture, you would use the original PSD (or XCF) with each layer separated.

Here's the code I used to generate the random flowers:
Code:
<?php
 
$imhsl = imagecreatefrompng("tulip.hsl.png");
 
$cellSize = imagesy($imhsl);
$cellCount = imagesx($imhsl) / $cellSize;
 
if ($cellCount != (int) $cellCount) {
    die("Invalid width (not a multiple of the height)\n");
}
echo "Cell count: $cellCount\n";
 
$hsljson = json_decode(file_get_contents("tulip.plant"), true);
 
$imrgb = imagecreatetruecolor($cellSize, $cellSize);
imagesavealpha($imrgb, true);
imagealphablending($imrgb, false);
imagefilledrectangle($imrgb, 0, 0, $cellSize - 1, $cellSize - 1, imagecolorallocatealpha($imrgb, 255, 255, 255, 127));
imagealphablending($imrgb, true);
 
for ($cell = 0; $cell < $cellCount; $cell++) {
    echo "Processing cell $cell\n";
   
    $hueRange = explode("-", $hsljson["hue"][$cell]);
    if (count($hueRange) == 2) {
        $hue = randomFloat((double) $hueRange[0], (double) $hueRange[1]);
    } else {
        $hue = (double) $hueRange[0];
    }
 
    for ($x = 0; $x < $cellSize; $x++) {
        $ix = $cell * $cellSize + $x;
        for ($y = 0; $y < $cellSize; $y++) {
            $hsl = imagecolorsforindex($imhsl, imagecolorat($imhsl, $ix, $y));
            $rgb = hsl2rgb(array($hue, $hsl["red"] / 255.0, $hsl["green"] / 255.0));
            imagesetpixel($imrgb, $x, $y,
                imagecolorallocatealpha($imrgb,
                    (int) ($rgb[0] * 255.0),
                    (int) ($rgb[1] * 255.0),
                    (int) ($rgb[2] * 255.0),
                    $hsl["alpha"]
                )
            );
        }
    }
}
 
imagepng($imrgb, "tulip.rnd.png");
 
function randomFloat($min = 0, $max = 1) {
    return $min + ($max - $min) * mt_rand() / mt_getrandmax();
}
 
for ($i = 0; $i < 32; $i++) {
    echo randomFloat(0, 2) . "\n";
}
 
// From Drupal
function hsl2rgb($hsl) {
    $h = $hsl[0];
    $s = $hsl[1];
    $l = $hsl[2];
    $m2 = ($l <= 0.5) ? $l * ($s + 1) : $l + $s - $l*$s;
    $m1 = $l * 2 - $m2;
    return array(
        hue2rgb($m1, $m2, $h + 0.33333),
        hue2rgb($m1, $m2, $h),
        hue2rgb($m1, $m2, $h - 0.33333)
    );
}
 
function hue2rgb($m1, $m2, $h) {
    $h = ($h < 0) ? $h + 1 : (($h > 1) ? $h - 1 : $h);
    if ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6;
    if ($h * 2 < 1) return $m2;
    if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (0.66666 - $h) * 6;
    return $m1;
}
 
?>
 

socram8888

Member
Contributor
Actually blue channel is unusued, so it is possible to use it as an index to the hue file. Using blue as the index, images look like this:
tulip.hsl.png

Which is smaller and more visual
 

eleazzaar

Member
Contributor
Art
procedural-flowers3.png

Here's an updated image. I hereby release them to be used under whatever license Terasology picks.

A few of the combinations are a little awkward, but in general it looks like it all fits pretty well. 512 combinations using all the parts.

Row A: blossom front
Row B: Blossom back
Row C: Middle
Row D: Base

Intended layer order from front to back:
A, D, C, B


Palettes

I've been thinking that the pallets could be more versatile and attractive if they had two color data points for each instance they were used-- one for a fully bright channel, one for nearly black, and it would blend the two colors for any value in-between. This way you could make a flower white for instance -- weather or not it was rendered with a nearly fully bright R channel. So the pallet would in this instance have a pure white for when the source image was FF0000, and a light grey for when the source image was 010000, and use colors in-between white and light grey for everything else. You could also get some nice irridecent effects making for instance a light yellow for the brightest, and deep red for the darkest.

I fear i'm explaining this very badly. Let me know if i'm communicating.



As I already pointed out, this is designed to be handled by a computer, not an human. Converting to this kind of file is the last step, done automatically by means of a script. The artist only have to build a set of layers, put one aside the other like this:
View attachment 1223
Then run the script which outputs a saturation-lightness picture and a hue file with the average hue of each cell, which can be easily edited using notepad to choose which colors can appear in a certain layer (green for the stems, random for the petals...)

So should you want to modify or improve a picture, you would use the original PSD (or XCF) with each layer separated.
Again, you are making the process more complicated and providing no advantage we want.

Trust me, i've been doing open source game development for almost 10 years, working on some of the projects that have far above average successful in collaboration between artist and coders (Wesnoth, FreeOrion). IRL workflows are not always so neat and tidy as you imagine, good artists don't make a ton of art, throw it in the game and move on to something else. Especially something like this which is to some degree transformed by the game engine, the artist goes back and forth between the paint app and viewing the art in game-- maybe dozens of times Also source files may get lost, potential contributors give up before they get started when they can't open the art assets.
 

Immortius

Lead Software Architect
Contributor
Architecture
GUI
I think not having a mandatory compilation step (except for code) for mod developers puts us in a nice place. We're also pretty close to being able to dynamically reload many assets at runtime, so asset developers wouldn't even have to restart the game - just drop a new texture/mesh over the old one and trigger the reload.
 

eleazzaar

Member
Contributor
Art
We're also pretty close to being able to dynamically reload many assets at runtime, so asset developers wouldn't even have to restart the game - just drop a new texture/mesh over the old one and trigger the reload.
That is an incredibly valuable feature. It make quality asset development so much more effortless.
 

socram8888

Member
Contributor
Yeah, you're right when it comes to the simplicity of RGB to modders. However working with HSL still has the advantage of letting modders add a virtually unlimited amount of colors. So what about mixing both?

The artist would create an RGB picture with each component of the plant (stem, petals, leaves...) with the same chrominance (hue) with any brightness, that would get converted to HSL on runtime by Terasology.

Terasology would leave channels S and L unmodified, and would use channel H as a lookup index in the hue file. If it finds a range, it would use a random hue in the given range or palette. Otherwise it would use the original hue. This gives slighty bad results with dark colors, as hue can't be precisely calculated, but would work well with the bright colors in most plants.
 

glasz

Active Member
Contributor
Art
socram8888 : Your system seems interesting (although i must say how it exactly works goes a little over my head), but the thing is : the issue here is not to have enough colors. First, just working with the RGB channels that shaders use, we can have more than enough. Second : the problem with an infinite number of color is we might end with plenty of shades of the same color that the player can hardly tell from each other, if at all.
So we allready have enough color combination.The issue here is to have some sort of control over the result, so it kind of look good, and not too random, precisely. Thus my suggestion that we use palettes, where colors are picked by a human so as to kind of fit with each other.
 

glasz

Active Member
Contributor
Art
Here's another iteration, with eleazzaar new sprites. I followed Immortius suggestion and made 4 palettes, 1 for greyish like color (grey channel), one for greenish (green channel), one for pastels (blue channel), one more flashy (red channel). Each palette has 16 colors.
At the moment i use the same four colors for each of the sprites layers, and i havent yet tried the "2 color by channel" thing suggested by eleazzaar.



 
Last edited:

Cervator

Org Co-Founder & Project Lead
Contributor
Design
Logistics
SpecOps
glasz - The flowers look great - in both sets, I can't tell the difference, what was "fixed" ? :D

The gray in some cases sticks out to me, maybe more in the first batch (is that it?). Mainly when used for leaves - makes me think the plant is dead, which clashes oddly with vibrant blooms :)

socram8888 - your stuff sounds really neat, but is probably overkill. Maybe we should start with the more basic version and if we find it insufficient later we can get fancier?
 

glasz

Active Member
Contributor
Art
Cervator : i put the sprites layer in the wrong order, the change is not spectacular :D

There certainly needs to be some more tweaking with palettes. What i would like to do in the next steps is some kind of app where you can load your sprites and palettes, and see the result directly. I hope i can do that with panda.
 

eleazzaar

Member
Contributor
Art
I think it's right this time, it's getting complicated :p
Cool. :)

Did you understand what i was trying to say about pallets in this post?

Also the idea about the blossom front and blossom back layers was that they would be linked with each other not randomized. So for instance, i have a blossom front with 5 flowers, there's a blossom back with five stems that connect to the flowers. Using both these layers in tandem would prevent the levitating blossom issue.

And for the flashy (red) channel for flowers it is probably best if you don't add many green options to the palette.


At the moment i use the same four colors for each of the sprites layers, and i havent yet tried the "2 color by channel" thing suggested by eleazzaar.
I wasn't exactly suggesting it. In fact i don't think we need it. I mentioned it to point out that this system would be extendable to additional colors if we felt the need for them
 

glasz

Active Member
Contributor
Art
Also the idea about the blossom front and blossom back layers was that they would be linked with each other not randomized. So for instance, i have a blossom front with 5 flowers, there's a blossom back with five stems that connect to the flowers. Using both these layers in tandem would prevent the levitating blossom issue.
eleazzaar oh, ok. and they are on 2 separate layers so one is on front and the other is back, ok, i get it now.
As for palettes : yup i think i understand : set a full bright color, a full dark one, and interpolate between the 2 depending on chanel value?
 
Top