10
\$\begingroup\$

Lately I've been working on adding Scenarios to my city building game. I wanted to create custom terrains for some of the different scenarios (such as a chain of islands, or a volcano). I decided that the simplest solution was to try to use topographic images to create an array of integers that could then be converted to heights and different terrains.

Since there are so many different types of colors in topographical maps, I eventually decided to go with a greyscale approach. It turned out that using real topographical maps in my game did not work out so well, so I generated a few black and white images to use for the terrain generation.

Here are a couple sample input images and their resulting terrains:

islands greyscale

islands terrain

volcano greyscale

volcano terrain (volcano cooled)

Here is the code:

/**
 * Only works with images of equal height and width
 *
 */
public class TextureToTerrainData {

    /**
     * For best results, input a grayscale texture. Darker will produce
     * lower integers, Lighter will produce higher.
     */
    public static int[][] getHeightsForImage(Texture texture) {

        Color[][] colorsFromImage = TextureToTerrainData.getColorsForImage(texture);
        int size = colorsFromImage.length;

        int[][] integersFromImage = new int[size][size];
        for (int x = 0; x < size; x++) {
            for (int y = 0; y < size; y++) {
                integersFromImage[x][y] = TextureToTerrainData
                                            .getHeightForColor(colorsFromImage[x][y]);
            }
        }

        TextureToTerrainData.printIntegerMatrix(integersFromImage);

        return integersFromImage;
    }

    public static Color[][] getColorsForImage(Texture texture) {
        texture.getTextureData().prepare(); //required to call this before the next line
        Pixmap pixmap = texture.getTextureData().consumePixmap();
        int size = pixmap.getWidth();
        Color[][] colorsFromImage = new Color[size][size];
        for (int x = 0; x < size; x++) {
            for (int y = 0; y < size; y++) {
                colorsFromImage[x][y] = new Color(pixmap.getPixel(x, y));
            }
        }
        return colorsFromImage;
    }

    private static int getHeightForColor(Color color) {
        //with grayscale input, only need to look for the strength of one color
        if (color.r < 0.05) {
            return 0;
        } else if (color.r < 0.1) {
            return 1;
        } else if (color.r < 0.15) {
            return 2;
        } else if (color.r < 0.2) {
            return 3;
        } else if (color.r < 0.25) {
            return 4;
        } else if (color.r < 0.3) {
            return 5;
        } else if (color.r < 0.35) {
            return 6;
        } else if (color.r < 0.4) {
            return 7;
        } else if (color.r < 0.45) {
            return 8;
        } else if (color.r < 0.5) {
            return 9;
        } else if (color.r < 0.55) {
            return 10;
        } else if (color.r < 0.6) {
            return 11;
        } else if (color.r < 0.65) {
            return 12;
        } else if (color.r < 0.7) {
            return 13;
        } else if (color.r < 0.75) {
            return 14;
        } else if (color.r < 0.8) {
            return 15;
        } else if (color.r < 0.85) {
            return 16;
        } else if (color.r < 0.9) {
            return 17;
        } else if (color.r < 0.95) {
            return 18;
        }
        return 19;
    }

    private static void printIntegerMatrix(int[][] matrix) {
        for (int[] row : matrix) {
            StringBuilder lineBuilder = new StringBuilder();
            for (int number : row) {
                //add a 0 if the number is only one digit
                lineBuilder.append(String.valueOf(number).length() > 1 ? number : "0" + number);
                lineBuilder.append("-");
            }
            System.out.println(lineBuilder.toString());
        }
    }
}

Here is the output of the print method:

00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-00-00-00-00-00-01-01-02-02-01-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-00-00-00-00-01-02-03-04-04-04-03-02-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-00-00-00-01-02-04-06-08-08-07-06-03-02-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-00-00-00-02-04-07-10-12-12-11-09-06-03-01-00-00-00-00-00-00-00-00-01-01-02-02-02-01-01-00-00-00-00-00-
00-00-00-00-00-00-00-00-01-03-06-09-13-15-16-15-12-08-04-02-00-00-00-00-00-00-00-01-03-04-05-05-05-03-02-01-00-00-00-00-
00-00-00-00-00-00-00-00-01-03-07-11-15-17-18-17-13-09-05-02-00-00-00-00-00-00-01-03-05-07-09-09-08-07-04-02-01-00-00-00-
00-00-00-00-00-00-00-00-01-03-07-11-15-17-18-17-13-09-05-02-00-00-00-00-00-00-02-05-08-11-13-14-12-10-07-03-01-00-00-00-
00-00-00-00-00-00-00-00-01-03-06-09-13-15-16-15-12-08-04-02-00-00-00-00-00-01-03-06-10-14-16-17-15-12-08-05-02-00-00-00-
00-00-00-00-00-00-00-00-00-02-04-07-10-12-12-11-09-06-03-01-00-00-00-00-00-01-03-07-11-15-18-19-17-14-09-05-02-00-00-00-
00-00-00-00-00-00-00-00-00-01-02-04-06-08-08-07-06-03-02-00-00-00-00-00-00-01-03-06-10-14-17-18-16-13-09-05-02-00-00-00-
00-00-00-00-00-00-00-00-00-00-01-02-03-04-04-04-03-02-00-00-00-00-00-00-00-01-02-05-09-12-14-15-14-11-07-04-01-00-00-00-
00-00-00-01-02-02-02-01-00-00-00-01-01-02-02-01-01-00-00-00-00-00-00-00-00-00-01-03-06-09-10-11-10-08-05-03-01-00-00-00-
00-01-02-03-04-04-04-03-02-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-02-03-05-06-07-06-05-03-01-00-00-00-00-
01-02-04-06-07-08-07-06-04-02-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-01-02-03-03-03-02-01-00-00-00-00-00-
01-03-06-10-11-12-11-10-06-03-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-01-01-01-01-00-00-00-00-00-00-00-
02-05-08-13-15-16-15-13-08-05-02-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
03-06-10-14-17-19-17-14-10-06-03-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
03-06-10-14-17-19-17-14-10-06-03-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
02-05-08-13-15-16-15-13-08-05-02-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
01-03-06-10-11-12-11-10-06-03-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
01-02-04-06-07-08-07-06-04-02-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
00-01-02-03-04-04-04-03-02-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-01-02-02-02-01-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-00-00-00-01-01-02-02-01-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-00-00-02-03-04-04-04-03-03-02-02-02-02-02-01-01-01-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-00-02-03-06-07-08-08-07-05-05-05-05-05-05-04-04-03-03-01-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-01-03-06-09-11-12-12-11-09-08-08-09-09-09-08-08-07-06-04-02-01-00-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-02-04-08-12-15-16-16-14-12-12-12-13-13-13-13-12-11-09-08-04-02-01-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-02-05-09-13-17-18-18-16-14-14-15-16-17-16-16-16-15-14-11-07-04-01-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-02-05-09-13-17-18-18-16-14-15-17-18-18-18-17-18-18-16-13-09-05-02-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-02-04-08-12-15-16-16-15-14-14-17-18-18-18-18-19-19-17-14-09-05-02-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-01-03-06-09-11-12-12-12-12-13-15-17-17-17-17-18-18-16-13-09-05-02-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-00-02-03-06-07-08-08-08-08-10-12-13-14-15-15-15-15-14-11-07-04-01-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-00-00-02-03-04-04-05-04-05-06-07-09-10-11-11-11-11-09-07-04-02-01-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-00-00-00-01-01-02-02-02-02-03-04-05-06-06-06-07-06-06-04-02-01-00-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-01-01-02-02-03-03-03-03-02-01-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-01-01-01-01-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-

Any and all feedback is appreciated. I do plan to reuse the class later, so I want it to be easy to understand and work with.

\$\endgroup\$
  • \$\begingroup\$ You want this to be GWT compatible, right? (GWT does not support String.format, which is why I am asking) \$\endgroup\$ – Simon Forsberg Sep 10 '15 at 15:48
  • \$\begingroup\$ It is not hugely important anymore for this project, but typically I do try to maintain GWT support in my libGDX code. \$\endgroup\$ – bazola Sep 10 '15 at 15:50
10
\$\begingroup\$

Mathematics to the rescue!

Okay, that getHeightForColor method is not passing my test.

private static int getHeightForColor(Color color) {
    return (int) (color.r / 0.05);
}

There we go, much better.

Or an array to make it configurable

As an alternative to the math operation, if you want to configure the details:

private static final double[] thresholds = new double[]{
    0.05, 0.10, 0.15, 0.20, 0.25,
    0.30, 0.35, 0.40, 0.45, 0.50,
    0.55, 0.60, 0.65, 0.70, 0.75,
    0.80, 0.85, 0.90, 0.95
};

private static int getHeightForColor(Color color) {
    double value = color.r;
    for (int i = 0; i < thresholds.length; i++) {
         if (value > thresholds[i]) {
             return i;
         }
    }
    return thresholds.length - 1;
}

Reusability

If you want this re-usable, you might want to not have all the methods static, so that they can be overridden for example, and you might also want to look into using the Strategy pattern for the getHeightForColor method.

Appending zeroes

lineBuilder.append(String.valueOf(number).length() > 1 ? number : "0" + number);

So if the number is less than 10, you want to add a zero to it, right?

if (number < 10) {
    lineBuilder.append('0');
}
lineBuilder.append(number);

There we go. A bit more clear, and a bit faster, and a bit less waste of memory.

\$\endgroup\$
  • 1
    \$\begingroup\$ Note that if number is negative, your append code acts differently. Of course, other things will be horribly broken too if number is negative! \$\endgroup\$ – Yakk Sep 10 '15 at 20:59
  • \$\begingroup\$ The array is essentially the same as the original string of if ... else if? The only difference would be the size of the non/compiled code. \$\endgroup\$ – TheBrenny Sep 10 '15 at 22:05
  • 1
    \$\begingroup\$ @mastercork889 yes, but if you use an array you could easily change it to 0.05, 0.09, 0.13, 0.165... (if you wanted to) \$\endgroup\$ – Simon Forsberg Sep 10 '15 at 22:07
  • \$\begingroup\$ The same could be done with the ifs. I agree with an array, but its only making readability more efficient - which I agree with, and its more the point of CR? And plus its probably >100ms, the difference, which can just be shown with a loading screen. :P \$\endgroup\$ – TheBrenny Sep 10 '15 at 22:24
17
\$\begingroup\$

This is a long waste of if chains

    if (color.r < 0.05) {
        return 0;
    } else if (color.r < 0.1) {
        return 1;
    } else if (color.r < 0.15) {
        return 2;
    ...
    } else if (color.r < 0.95) {
        return 18;
    }
    return 19;

You're using them to determine a mathematical amount anyway. Just perform the arithmetic necessary by returning Math.floor(color.r * 20).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.