Jump to content
  • Advertisement
Sign in to follow this  
TTT_Dutch

Island Generation -- Problem with Duplicates

This topic is 2120 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello all,

 

I have been working for the past few days on a perlin noise island generator (using libnoise). What I do is generate a terrain, and then apply an island mask to it.  I have added a seed to the island generation portion but I am getting similiar results each time even with a seed. Can someone help me so that I can generate a truely random island each time? Here is what I am currently getting (seeds in parenthesis):

 

(1233212)

vwxr8o.jpg

 

(999555857)

 

2434x9w.jpg

 

So, as you can see, the maps are very similar. I really don't know why. If needed, here is my code: 

(Steps I take)

1st Generate terrain

2nd Generate island mask

3rd Apply island mask to terrain

4th Draw the terrain

 

//Little program to generate test map with only 0 and 1 tile

#include <iostream>
#include <libnoise/noise.h>
#include "noiseutils.h"
#include <stdlib.h>
#include <string>

using namespace std;
using namespace noise;

int main()
{

   module::Perlin myModule;

   utils::NoiseMap heightMap;
   utils::NoiseMapBuilderPlane heightMapBuilder;
   heightMapBuilder.SetSourceModule (myModule);
   heightMapBuilder.SetDestNoiseMap (heightMap);
   heightMapBuilder.SetDestSize (512, 512);
   heightMapBuilder.SetBounds (2.0, 6.0, 1.0, 5.0);
   heightMapBuilder.Build ();

   
   string seed;
   int iseed;
   //Add support for character conversion to ints and clip string to 8 numbers
   cout << "Please enter a seed: ";
   getline(cin,seed);

   cout << seed << endl;
   iseed = atoi(seed.c_str());
   cout << iseed << endl;

   float islandMap[512][512];

   enum directions {LEFT = 0, UP, RIGHT, DOWN, NONE};

   for(int count = 0; count < 1; count++)
   {

      //Initialize values for a new map
      for (int i = 0; i < 512; i++) 
      {
         for (int j = 0; j < 512; j++) 
         {
            islandMap[j][i] = 0;
         }
      }


      //Choose a random location, travel in random directions
      int xRand = 0,yRand=0;
      int xPos = 0,yPos=0;

      srand((iseed*57)/(iseed*17));
      cout << iseed;

      xRand= rand()%512;
      yRand= rand()%512;

      xPos = xRand;
      yPos = yRand;

      islandMap[xRand][yRand] = 1;
      directions direction = NONE;

      srand((int)((iseed*iseed)/57));
      for (int i = 0; i < 10000000; i++) 
      {
         if(xPos == 511 || xPos == 0)
         {
            xPos = rand()%512;
            islandMap[xPos][yPos] +=1;
         }   

         if(yPos == 511 || yPos == 0)
         {
            yPos = rand()%512;
            islandMap[xPos][yPos] +=1;
         }

         int oldValue;

         oldValue = islandMap[xPos][yPos];

         int temp;
         temp = rand()%4;
         direction = (directions)temp;
         if(direction == LEFT)
         {
            xPos-=1;
         }
         else if(direction == UP)
            yPos-=1;
         else if(direction == RIGHT)
            xPos+=1;
         else if(direction == DOWN)
            yPos+=1;

         if(oldValue >= islandMap[xPos][yPos])
         {
            islandMap[xPos][yPos] +=1;
         }


      }

      //find max value for normalizing
      int max = 0;
      int min = 0;
      for (int i = 0; i < 512; i++) 
      {
         for (int j = 0; j < 512; j++) 
         {
            if(max < islandMap[i][j])
               max = islandMap[i][j];

            //cout << islandMap[i][j] << " ";

            if(min > islandMap[i][j])
               min = islandMap[i][j];
         }
      }
               

      //Multiply values of original by new maps generated
      for (int i = 0; i < 512; i++) 
      {
         for (int j = 0; j < 512; j++) 
         {
            float islandValue = (islandMap[i][j] - min) / (max-min);
            //cout << islandValue;
            float mapValue = (heightMap.GetValue(i,j) + 1) / (1+1);
            float newValue = (-1 + (2/1)*(islandValue*mapValue));
            heightMap.SetValue(i,j, newValue);
         }
      }
   }

   utils::RendererImage renderer;
   utils::Image image;
   renderer.SetSourceNoiseMap (heightMap);
   renderer.SetDestImage (image);
   renderer.ClearGradient ();
   renderer.AddGradientPoint (-1.0000, utils::Color (  0,   0, 255, 255)); // shallow
   renderer.AddGradientPoint (-0.9701, utils::Color (  0,   0, 255, 255)); // shallow
   renderer.AddGradientPoint (-0.9700, utils::Color (  0, 128, 255, 255)); // shore
   renderer.AddGradientPoint (-0.9690, utils::Color (  0, 128, 255, 255)); // shore
   renderer.AddGradientPoint (-0.9689, utils::Color (240, 240,  64, 255)); // sand
   renderer.AddGradientPoint (-0.9601, utils::Color (240, 240,  64, 255)); // sand
   renderer.AddGradientPoint (-0.9600, utils::Color ( 32, 160,   0, 255)); // grass
   renderer.AddGradientPoint (-0.4991, utils::Color ( 32, 160,   0, 255)); // grass
   renderer.AddGradientPoint (-0.4990, utils::Color ( 0, 0,   0, 255)); // pines
   renderer.AddGradientPoint (-0.4987, utils::Color ( 0, 0,   0, 255)); // pines
   renderer.AddGradientPoint (-0.3986, utils::Color (96, 63,   0, 255)); // dirt
   renderer.AddGradientPoint ( 0.0000, utils::Color (96, 63,   0, 255)); // dirt
   renderer.AddGradientPoint ( 0.0001, utils::Color (128, 128, 128, 255)); // rock
   renderer.AddGradientPoint ( 1.0000, utils::Color (128, 128, 128, 255)); // rock
   renderer.EnableLight ();
   renderer.SetLightContrast (3.0);
   renderer.SetLightBrightness (2.0);
   renderer.Render ();

   utils::WriterBMP writer;
   writer.SetSourceImage (image);
   writer.SetDestFilename ("tutorial.bmp");
   writer.WriteDestFile ();

   return 0;
}

 

Thankyou for your help in advance!! :D

 

Share this post


Link to post
Share on other sites
Advertisement

To elaborate on SuperVGA, this line is the line that is fishy:

 

 

srand((iseed*57)/(iseed*17));

 

 

The iseed in the numerator is cancelled by the iseed in the denominator, causing the seed to always be 57/17, or 3, when the island mask is generated.

Edited by FLeBlanc

Share this post


Link to post
Share on other sites
57/17 isn't 3, 51/17 is. Depends how big the seed is, and if it overflows or not. Calling srand more than once is dodgy anyway, in this circumstance. The bitmaps aren't the same though, they look slightly different. Only call srand more than once to repeat a previous random sequence.

Share this post


Link to post
Share on other sites

I'd rebuild it, piece by piece.  That's obviously terrible advice, but its honestly what I would do.


Although I didn't look at your code, you might try this:
You might want to generate a constant perlin noise and a random location, instead of a random perlin noise and a constant location.

Also try adjusting the map maximums to some number which is coprime to the number you're using right now.  Avoid Mersenne numbers.  If that doesn't work, try again with a number coprime to both of those.  You might be generating a specific repeating pattern or something.  Well, no, the map would be offset I suppose.

Not sure.  Simplify and try to generate more basic structures.  Try to make those diverse.  Then add more depth.

Change each number one by one dude.  It's either a specific algorithm or a specific constant that's throwing you off.

Share this post


Link to post
Share on other sites

[quote name='Paradigm Shifter' timestamp='1356634870' post='5014793']
57/17 isn't 3, 51/17 is. Depends how big the seed is, and if it overflows or not. Calling srand more than once is dodgy anyway, in this circumstance. The bitmaps aren't the same though, they look slightly different. Only call srand more than once to repeat a previous random sequence.
[/quote]

Actually, 57/17 = 3 here, since we're talking about integers and thus integer division. But well-spotted for the overflow - that's really the only reason why the maps are different. Don't know what the rationale behind those numbers was, though.

Share this post


Link to post
Share on other sites

I just got done playing with his code. I took out the multiple calls to srand (now a single call: srand(iseed);), the do nothing for loop and various other oddities ((2 / 1) ?). It now creates very regular terrains. The generated terrains show a very strong rotational symmetry. Each 1/4 of the image is nearly an exact copy of each other, rotated 90°. 

 

I think the entire algorithm needs to be rewritten.

Edited by MarkS

Share this post


Link to post
Share on other sites

The algorithm used looks like the same from: http://breinygames.blogspot.nl/2012/06/generating-terrain-using-perlin-noise.html

 

Just playing around a little (I did change some code so I could more easily read it), here is what I came up with:

//Little program to generate test map with only 0 and 1 tile

#include <iostream>
#include <noise/noise.h>
#include "noiseutils.h"
#include <stdlib.h>
#include <string>
#include <sstream>

using namespace std;
using namespace noise;

template <typename T> string tostring (T v) {
    ostringstream ss;
    ss << v;
    return ss.str();
}

void generate_island(int iseed) {
    module::Perlin myModule;

    myModule.SetSeed (iseed);
    utils::NoiseMap heightMap;
    utils::NoiseMapBuilderPlane heightMapBuilder;
    heightMapBuilder.SetSourceModule(myModule);
    heightMapBuilder.SetDestNoiseMap(heightMap);
    heightMapBuilder.SetDestSize(512, 512);
    heightMapBuilder.SetBounds(2.0, 6.0, 1.0, 5.0);
    heightMapBuilder.Build();

    float islandMap[512][512];
    srand(iseed);

    for (int count = 0; count < 1; count++ ){
        //Initialize values for a new map
        for (int i = 0; i < 512; i++) {
            for (int j = 0; j < 512; j++) {
                islandMap[j][i] = 0;
            }
        }

        //Choose a random location, travel in random directions
        int x = 0, y = 0;
        float previous = 0;

        for (int i = 0; i < 1000000; i++) {
            if(y == 511 || y == 0 || x == 511 || x == 0) {
                x = rand() % 512;
                y = rand() % 512;
            }   

            if(previous >= islandMap[x][y]) {
                islandMap[x][y] += 1;
            }

            previous = islandMap[x][y];

            switch(rand() % 4) {
                case 0:
                    x -= 1;
                    break;
                case 1:
                    y -= 1;
                    break;
                case 2:
                    x += 1;
                    break;
                case 3:
                    y += 1;
                    break;
                default:
                    break;
            }
        }

        //find max value for normalizing
        for (int i = 0; i < 512; i++) {
            for (int j = 0; j < 512; j++) {
                islandMap[i][j] = islandMap[i][j] * 2 - 1;
            }
        }

        int max = 0;
        int min = 0;
        for (int i = 0; i < 512; i++) {
            for (int j = 0; j < 512; j++) {
                if(max < islandMap[i][j]) {
                    max = islandMap[i][j];
                }

                if(min > islandMap[i][j]) {
                    min = islandMap[i][j];
                }
            }
        }

        //Multiply values of original by new maps generated
        for (int i = 0; i < 512; i++) {
            for (int j = 0; j < 512; j++) {
                float islandValue = (islandMap[i][j] - min) / (max - min);
                float mapValue = (heightMap.GetValue(i, j) + 1) / 2;
                float newValue = 2 * islandValue * mapValue - 1;
                heightMap.SetValue(i, j, newValue);
            }
        }
    }

    utils::RendererImage renderer;
    utils::Image image;
    renderer.SetSourceNoiseMap (heightMap);
    renderer.SetDestImage (image);
    renderer.ClearGradient ();
    renderer.AddGradientPoint (-1.0000, utils::Color (  0,   0, 255, 255)); // shallow
    renderer.AddGradientPoint (-0.9701, utils::Color (  0,   0, 255, 255)); // shallow
    renderer.AddGradientPoint (-0.9700, utils::Color (  0, 128, 255, 255)); // shore
    renderer.AddGradientPoint (-0.9690, utils::Color (  0, 128, 255, 255)); // shore
    renderer.AddGradientPoint (-0.9689, utils::Color (240, 240,  64, 255)); // sand
    renderer.AddGradientPoint (-0.9601, utils::Color (240, 240,  64, 255)); // sand
    renderer.AddGradientPoint (-0.9600, utils::Color ( 32, 160,   0, 255)); // grass
    renderer.AddGradientPoint (-0.4991, utils::Color ( 32, 160,   0, 255)); // grass
    renderer.AddGradientPoint (-0.4990, utils::Color ( 0, 0,   0, 255)); // pines
    renderer.AddGradientPoint (-0.4987, utils::Color ( 0, 0,   0, 255)); // pines
    renderer.AddGradientPoint (-0.3986, utils::Color (96, 63,   0, 255)); // dirt
    renderer.AddGradientPoint ( 0.0000, utils::Color (96, 63,   0, 255)); // dirt
    renderer.AddGradientPoint ( 0.0001, utils::Color (128, 128, 128, 255)); // rock
    renderer.AddGradientPoint ( 1.0000, utils::Color (128, 128, 128, 255)); // rock
    renderer.EnableLight ();
    renderer.SetLightContrast (3.0);
    renderer.SetLightBrightness (2.0);
    renderer.Render ();

    utils::WriterBMP writer;
    writer.SetSourceImage (image);
    writer.SetDestFilename ("tutorial" + tostring(iseed) + ".bmp");
    writer.WriteDestFile ();
}

int main() {
    for(int i=0; i<10; ++i){
        generate_island(i);
    }
    return 0;
}

 

 

 

Share this post


Link to post
Share on other sites
Your seed is always the same... Always 57/17.
-What is the reason behind it, I wonder?

I'm actually puzzeld about your maps being the slightest different.
You're calling srand all time. -You should only be calling it once (it keeps the following numbers the same for every time it's invoked with the same seed. The behaviours you're experiencing is familiar to when i did it years ago.
To elaborate on SuperVGA, this line is the line that is fishy:

 

 

srand((iseed*57)/(iseed*17));

 

 

The iseed in the numerator is cancelled by the iseed in the denominator, causing the seed to always be 57/17, or 3, when the island mask is generated.

 

Thankyou for pointing that out! biggrin.png I have fixed that but now I am still getting similar maps.. :/. I am sure changing that helps though biggrin.png

 

I'd rebuild it, piece by piece.  That's obviously terrible advice, but its honestly what I would do.


Although I didn't look at your code, you might try this:
You might want to generate a constant perlin noise and a random location, instead of a random perlin noise and a constant location.

Also try adjusting the map maximums to some number which is coprime to the number you're using right now.  Avoid Mersenne numbers.  If that doesn't work, try again with a number coprime to both of those.  You might be generating a specific repeating pattern or something.  Well, no, the map would be offset I suppose.

Not sure.  Simplify and try to generate more basic structures.  Try to make those diverse.  Then add more depth.

Change each number one by one dude.  It's either a specific algorithm or a specific constant that's throwing you off.

I got lost at your third paragraph :/ Can you please elaborate on the thing that I should also try adjusting smile.png

 

I just got done playing with his code. I took out the multiple calls to srand (now a single call: srand(iseed);), the do nothing for loop and various other oddities ((2 / 1) ?). It now creates very regular terrains. The generated terrains show a very strong rotational symmetry. Each 1/4 of the image is nearly an exact copy of each other, rotated 90°. 

 

I think the entire algorithm needs to be rewritten.

Sorry yeah the (2/1) thing gets the numbers into the range of -1 and 1. The 1 in that equation is the max number in the set of numbers. And what algorithm needs to be rewritten, the island masking one? :/ Because if that is so, I have no clue what to do. I just followed what this guy said he did on his blog.

 

The algorithm used looks like the same from: http://breinygames.blogspot.nl/2012/06/generating-terrain-using-perlin-noise.html

 

Just playing around a little (I did change some code so I could more easily read it), here is what I came up with:


 

//Little program to generate test map with only 0 and 1 tile

#include <iostream>
#include <noise/noise.h>
#include "noiseutils.h"
#include <stdlib.h>
#include <string>
#include <sstream>

using namespace std;
using namespace noise;

template <typename T> string tostring (T v) {
    ostringstream ss;
    ss << v;
    return ss.str();
}

void generate_island(int iseed) {
    module::Perlin myModule;

    myModule.SetSeed (iseed);
    utils::NoiseMap heightMap;
    utils::NoiseMapBuilderPlane heightMapBuilder;
    heightMapBuilder.SetSourceModule(myModule);
    heightMapBuilder.SetDestNoiseMap(heightMap);
    heightMapBuilder.SetDestSize(512, 512);
    heightMapBuilder.SetBounds(2.0, 6.0, 1.0, 5.0);
    heightMapBuilder.Build();

    float islandMap[512][512];
    srand(iseed);

    for (int count = 0; count < 1; count++ ){
        //Initialize values for a new map
        for (int i = 0; i < 512; i++) {
            for (int j = 0; j < 512; j++) {
                islandMap[j][i] = 0;
            }
        }

        //Choose a random location, travel in random directions
        int x = 0, y = 0;
        float previous = 0;

        for (int i = 0; i < 1000000; i++) {
            if(y == 511 || y == 0 || x == 511 || x == 0) {
                x = rand() % 512;
                y = rand() % 512;
            }   

            if(previous >= islandMap[x][y]) {
                islandMap[x][y] += 1;
            }

            previous = islandMap[x][y];

            switch(rand() % 4) {
                case 0:
                    x -= 1;
                    break;
                case 1:
                    y -= 1;
                    break;
                case 2:
                    x += 1;
                    break;
                case 3:
                    y += 1;
                    break;
                default:
                    break;
            }
        }

        //find max value for normalizing
        for (int i = 0; i < 512; i++) {
            for (int j = 0; j < 512; j++) {
                islandMap[i][j] = islandMap[i][j] * 2 - 1;
            }
        }

        int max = 0;
        int min = 0;
        for (int i = 0; i < 512; i++) {
            for (int j = 0; j < 512; j++) {
                if(max < islandMap[i][j]) {
                    max = islandMap[i][j];
                }

                if(min > islandMap[i][j]) {
                    min = islandMap[i][j];
                }
            }
        }

        //Multiply values of original by new maps generated
        for (int i = 0; i < 512; i++) {
            for (int j = 0; j < 512; j++) {
                float islandValue = (islandMap[i][j] - min) / (max - min);
                float mapValue = (heightMap.GetValue(i, j) + 1) / 2;
                float newValue = 2 * islandValue * mapValue - 1;
                heightMap.SetValue(i, j, newValue);
            }
        }
    }

    utils::RendererImage renderer;
    utils::Image image;
    renderer.SetSourceNoiseMap (heightMap);
    renderer.SetDestImage (image);
    renderer.ClearGradient ();
    renderer.AddGradientPoint (-1.0000, utils::Color (  0,   0, 255, 255)); // shallow
    renderer.AddGradientPoint (-0.9701, utils::Color (  0,   0, 255, 255)); // shallow
    renderer.AddGradientPoint (-0.9700, utils::Color (  0, 128, 255, 255)); // shore
    renderer.AddGradientPoint (-0.9690, utils::Color (  0, 128, 255, 255)); // shore
    renderer.AddGradientPoint (-0.9689, utils::Color (240, 240,  64, 255)); // sand
    renderer.AddGradientPoint (-0.9601, utils::Color (240, 240,  64, 255)); // sand
    renderer.AddGradientPoint (-0.9600, utils::Color ( 32, 160,   0, 255)); // grass
    renderer.AddGradientPoint (-0.4991, utils::Color ( 32, 160,   0, 255)); // grass
    renderer.AddGradientPoint (-0.4990, utils::Color ( 0, 0,   0, 255)); // pines
    renderer.AddGradientPoint (-0.4987, utils::Color ( 0, 0,   0, 255)); // pines
    renderer.AddGradientPoint (-0.3986, utils::Color (96, 63,   0, 255)); // dirt
    renderer.AddGradientPoint ( 0.0000, utils::Color (96, 63,   0, 255)); // dirt
    renderer.AddGradientPoint ( 0.0001, utils::Color (128, 128, 128, 255)); // rock
    renderer.AddGradientPoint ( 1.0000, utils::Color (128, 128, 128, 255)); // rock
    renderer.EnableLight ();
    renderer.SetLightContrast (3.0);
    renderer.SetLightBrightness (2.0);
    renderer.Render ();

    utils::WriterBMP writer;
    writer.SetSourceImage (image);
    writer.SetDestFilename ("tutorial" + tostring(iseed) + ".bmp");
    writer.WriteDestFile ();
}

int main() {
    for(int i=0; i<10; ++i){
        generate_island(i);
    }
    return 0;
}

 

 

Thankyou very much for this! I like the randomness and I love the code but do you know if there is any way to make it less "holey". There are specks of water all over the terrain and what I am going for is more of a solid island surrounded by water tongue.png

Edited by Riztro

Share this post


Link to post
Share on other sites

I just got done playing with his code. I took out the multiple calls to srand (now a single call: srand(iseed);), the do nothing for loop and various other oddities ((2 / 1) ?). It now creates very regular terrains. The generated terrains show a very strong rotational symmetry. Each 1/4 of the image is nearly an exact copy of each other, rotated 90°. 
 
I think the entire algorithm needs to be rewritten.

Sorry yeah the (2/1) thing gets the numbers into the range of -1 and 1. The 1 in that equation is the max number in the set of numbers. And what algorithm needs to be rewritten, the island masking one? :/ Because if that is so, I have no clue what to do. I just followed what this guy said he did on his blog.



I apologize for that. I've been dealing with something in my personal life that has kind of clouded my judgement today. I shouldn't have posted that; it wasn't helpful. However, since I *did* post it, look at TrickyLogic's changes. They were small, but significant.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!