Cubes – A Block World Framework [Update Preview]

I’ve got two questions:

I’d like to build my own texture atlas full of textures for my block world, but don’t really care to use the texture dimensions of the provided atlas.Is there a way to use my own atlas with my own dimensions?
warning: texture/material/etc. illiterate person here.

Also, I want to mess around with the source code for heightened understanding. I successfully found it here: https://code.google.com/p/jmonkeyplatform-contributions/source/browse/#svn%2Ftrunk%2Fcubes%2FCubes%2Fsrc%2Fcom%2Fcubes%3Fstate%3Dclosed , but was wondering if there was a way to just download it all. It only lets me look at it ATM.

Thanks!

1 Like

I do not know how to edit the dimensions of the atlas…but for downloading all the source files you want I think you can use an SVN client like TortoiseSVN. I’ll try it myself since I want this source code too and edit this post when I do.

Thanks for linking where the source is by the way =D , I really wanted it and just thought destroflyer hadn’t uploaded it anywhere yet.

EDIT: Ok, so I installed TortoiseSVN, then right clicked on my desktop and clicked TortoiseSVN -> Repo-browser, then used the URL http://jmonkeyplatform-contributions.googlecode.com/svn/trunk/ , then just right clicked on the folder(s) you want and Checkout

I got that link from the page you linked to by clicking on “Checkout” near the top left of the page.

The files are stored in a version control system called subversion that is hosted on googlecode, the jmonkey sdk has built in support for subversion. If you are new to version control, id recommend googling for some resources to learn about subversion.

2 Likes
@ulfgur said: I've got two questions:

I’d like to build my own texture atlas full of textures for my block world, but don’t really care to use the texture dimensions of the provided atlas.Is there a way to use my own atlas with my own dimensions?
warning: texture/material/etc. illiterate person here.

Also, I want to mess around with the source code for heightened understanding. I successfully found it here: https://code.google.com/p/jmonkeyplatform-contributions/source/browse/#svn%2Ftrunk%2Fcubes%2FCubes%2Fsrc%2Fcom%2Fcubes%3Fstate%3Dclosed , but was wondering if there was a way to just download it all. It only lets me look at it ATM.

Thanks!

As for the custom atlas, you can use any resolution you want with the current version of the cube framework e.g.(128x128, 256x256, 512x512) pixels, as long as there is 16x16 images, if you want more or less then 16x16 then you will need to modify the texture skinning code and change the percentage for rows and columns.

1 Like

Thanks!

OK, I’ve gotten my hands on the source code, stuck it all in my own package, got the correct imports working, etc. etc. Now, I’m trying to mess around with the source code to make using my own (infinite number of) skins possible. I set up a main and am running into the following error:

Jul 03, 2014 5:21:11 PM com.jme3.app.Application handleError
SEVERE: Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main]
java.lang.RuntimeException: Uncompilable source code - Erroneous sym type: mygame.BlockManager.register
at mygame.Main.simpleInitApp(Main.java:28)
at com.jme3.app.SimpleApplication.initialize(SimpleApplication.java:226)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.initInThread(LwjglAbstractDisplay.java:130)
at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:207)
at java.lang.Thread.run(Thread.java:744)

What is this? How do I solve it?

(it points to this code. I’ll just give all of it, because I don’t know what’s off.)

[java]
@Override
public void simpleInitApp() {
//CubesTestAssets.registerBlocks();
BlockManager.register(Block_Wood.class, new BlockSkin(new BlockSkin_TextureLocation[]{
new BlockSkin_TextureLocation(4, 0),
new BlockSkin_TextureLocation(4, 0),
new BlockSkin_TextureLocation(3, 0),
new BlockSkin_TextureLocation(3, 0),
new BlockSkin_TextureLocation(3, 0),
new BlockSkin_TextureLocation(3, 0)
}, false));
BlockManager.register(Block_Stone.class, new BlockSkin(new BlockSkin_TextureLocation(9, 0), false));
BlockManager.register(Block_Water.class, new BlockSkin(new BlockSkin_TextureLocation(0, 1), true));
BlockManager.register(Block_Brick.class, new BlockSkin(new BlockSkin_TextureLocation(11, 0), false));

    //This is your terrain, it contains the whole
    //block world and offers methods to modify it
    BlockTerrainControl blockTerrain = new BlockTerrainControl(CubesTestAssets.getSettings(this), new Vector3Int(1, 1, 1));

    //To set a block, just specify the location and the block object
    //(Existing blocks will be replaced)
    blockTerrain.setBlock(new Vector3Int(0, 0, 0), Block_Wood.class); 
    blockTerrain.setBlock(new Vector3Int(0, 0, 1), Block_Wood.class);
    blockTerrain.setBlock(new Vector3Int(1, 0, 0), Block_Wood.class);
    blockTerrain.setBlock(new Vector3Int(1, 0, 1), Block_Stone.class);

    //You can place whole areas of blocks too: setBlockArea(location, size, block)
    //(The specified block will be cloned each time)
    //The following line will set 3 blocks on top of each other
    //({1,1,1}, {1,2,3} and {1,3,1})
    blockTerrain.setBlockArea(new Vector3Int(1, 1, 1), new Vector3Int(1, 3, 1), Block_Stone.class);

    //Removing a block works in a similar way
    blockTerrain.removeBlock(new Vector3Int(1, 2, 1));
    blockTerrain.removeBlock(new Vector3Int(1, 3, 1));

    //The terrain is a jME-Control, you can add it
    //to a node of the scenegraph to display it
    Node terrainNode = new Node();
    terrainNode.addControl(blockTerrain);
    rootNode.attachChild(terrainNode);
    
    cam.setLocation(new Vector3f(-10, 10, 16));
    cam.lookAtDirection(new Vector3f(1, -0.56f, -1), Vector3f.UNIT_Y);
    flyCam.setMoveSpeed(50);
    
    DirectionalLight directionalLight = new DirectionalLight();
    directionalLight.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
    rootNode.addLight(directionalLight);
}

[/java]

I’ve built a clumsy but fairly effective infinite terrain generation system. Using what I’ve learned, I hope to get back into the source code and rewrite a bit of the terrain controls to make this more effective, and only use one terrainControl.

If any of you improve upon my classes, I’d appreciate it if you told me how. I’m still learning, so any additions to my code could significantly impact my skills and future development.

[java]import com.cubes.;
import com.cubes.Vector3Int;
import com.cubes.test.blocks.
;
import com.jme3.math.Vector3f;
import com.jme3.renderer.*;
import com.jme3.scene.Node;
import com.jme3.scene.control.AbstractControl;

/**
*

  • @author Ulfgur
    */
    public class SuperChunk extends AbstractControl implements Comparable
    {
    private Node chunks;
    private BlockTerrainControl blockTerrain;
    private int sideLength;
    private Vector3Int coordinates;
    private CubesSettings settings;

    /**

    • This is a SuperChunk–kind of like a chunk contains multiple

    • blocks, a superChunk contains multiple chunks.

    • @param set Your CubeSettings go here. I would not advise changing cube settings midway through; this will screw up EVERYTHING.

    • @param sideL The number of chunks in a superchunk. I would advise using either 2 or 3. I have not tested it with higher, but suspect using more would significantly impact performance.

    • @param applied This is the node you attach the custom control to. It seems a bit odd, but my code will break without it…
      */
      public SuperChunk(CubesSettings set, int sideL, Node applied)
      {
      //get administrative tasks out of the way.
      chunks = applied;
      sideLength = sideL;
      settings = set;

      //create a new block terrain for all chunks within the superChunk.
      blockTerrain = new BlockTerrainControl(settings, new Vector3Int(sideLength, sideLength, sideLength));
      chunks.addControl(blockTerrain);

      //fill in a floor so you know the superChunk exists.
      Vector3Int one = new Vector3Int(0, 0, 0);
      Vector3Int two = new Vector3Int(settings.getChunkSizeX() * sideL, 1, settings.getChunkSizeZ() * sideL);
      blockTerrain.setBlockArea(one, two, Block_Wood.class);

      //add stone blocks to indicate where the edges of a chunk are.
      blockTerrain.setBlock(new Vector3Int(settings.getChunkSizeX() * sideL, 1, settings.getChunkSizeZ() * sideL), Block_Stone.class);
      blockTerrain.setBlock(new Vector3Int(0, 1, settings.getChunkSizeZ() * sideL), Block_Stone.class);
      blockTerrain.setBlock(new Vector3Int(settings.getChunkSizeX() * sideL, 1, 0), Block_Stone.class);
      blockTerrain.setBlock(new Vector3Int(0, 1, 0), Block_Stone.class);
      }

    /**

    • Returns the node SuperChunk is attached to.
    • @return Node
      */
      public Node getNode()
      {
      return chunks;
      }

    /**

    • returns the terrain control.
    • @return returns the terrain control.
      */
      public BlockTerrainControl getTerrain()
      {
      return blockTerrain;
      }

    /**

    • adds a block chunk listener to the chunk. I use this
    • to create physics.
    • @param listen BlockChunkListener
      */
      public void addChunkListener(BlockChunkListener listen)
      {
      blockTerrain.addChunkListener(listen);
      }

    /**

    • Moves the chunk based on given coordinates.
    • @param loc
      */
      public void setLocation(Vector3Int loc)
      {
      //sets the coordinates,…
      coordinates = loc;
      //…then automatically adjusts the coordinates into a local translation.
      float xVal = settings.getBlockSize() * settings.getChunkSizeX() * coordinates.getX() * sideLength;
      float yVal = settings.getBlockSize() * settings.getChunkSizeY() * coordinates.getY() * sideLength;
      float zVal = settings.getBlockSize() * settings.getChunkSizeZ() * coordinates.getZ() * sideLength;
      chunks.setLocalTranslation(new Vector3f(xVal, yVal, zVal));
      }

    /**

    • returns the world location.
    • @return
      /
      public Vector3f getLocation()
      {
      return chunks.getWorldTranslation();
      }
      /
      *
    • retuns the coordinates.
    • @return
      */
      public Vector3Int getCoords()
      {
      return coordinates;
      }

    /**

    • This doesn’t do anything yet… You should put snazzy code here!
    • @param tpf
      */
      @Override
      protected void controlUpdate(float tpf)
      {

    }
    /**

    • This doesn’t do anything yet… You should put snazzy code here!
    • @param tpf
      */
      @Override
      protected void controlRender(RenderManager rm, ViewPort vp)
      {

    }

    /**

    • converts the SuperChunk into a string for debugging.
    • Pretty much just uses the coordinates.
    • @return
      */
      @Override
      public String toString()
      {
      return "SuperChunk at " + coordinates;
      }

    /**

    • equals. Again, I’m basing this off of the coordinates.
    • This is probably a pretty clumsy way of doing things,
    • but I make the assumption that only one set of SuperChunks
    • will exist at any one time.
    • @param obj
    • @return
      */
      @Override
      public boolean equals(Object obj)
      {
      if (obj == null)
      {
      return false;
      }
      if (getClass() != obj.getClass())
      {
      return false;
      }
      final SuperChunk other = (SuperChunk) obj;
      if (coordinates.equals(other.getCoords()))
      {
      return true;
      }
      return false;
      }

    /**

    • Compares based on chunk location, with priority given

    • to X, then Y, then Z.

    • @param obj

    • @return
      */
      public int compareTo(Object obj)
      {
      int result = 0;
      if(!obj.getClass().equals(SuperChunk.class))
      {
      result = -99999999;
      return result;
      }else
      {
      SuperChunk temp = (SuperChunk) obj;
      //get coords,…
      int x = coordinates.getX() - temp.getCoords().getX();
      int y = coordinates.getX() - temp.getCoords().getX();
      int z = coordinates.getX() - temp.getCoords().getX();

       //...then ensure that Y will always be given higher priority
       //than Z,...
       y = y * Math.abs(z);
       //...then ensure that X will always be given higher priority
       //than Y.
       x = x * Math.abs(y);
       result = x + y + z;
      

      }
      return result;
      }

}
[/java]
[java]import com.cubes.;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.shapes.MeshCollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.collision.CollisionResult;
import com.jme3.math.Vector3f;
import com.jme3.scene.
;
import java.util.*;

/**

  • This class manages all my SuperChunks, and is the engine that

  • drives infinite generation of terrain.

  • To use this, create your block settings (example code shown in

  • the block comment below) and create a bullet app state. Register

  • your blocks.

  • Now, create a SuperChunkManager. (Pass your cube settings, then the

  • number of chunks per Superchunk, then the starting number of superChunks (sn).

  • It will generate superchunks in a (sn) * (sn) * (sn) grid.)

  • Attach your SuperChunkManager’s node (superchunkmanager.getNode())

  • to the rootNode.

  • Add the bullet app state.

  • @author Ulfgur
    */
    public class SuperChunkManager
    {
    public static Node terrainNode;
    public static ArrayList<SuperChunk> chunks;
    public static BulletAppState bulletAppState;
    CubesSettings settings;
    BlockTerrainControl blockTerrain;
    int chunksPerNode;

    /*

    • //create your block settings

    • CubesSettings blockSettings = new CubesSettings(app);
      blockSettings.setBlockSize(worldScale);
      blockSettings.setChunkSizeX(16);
      blockSettings.setChunkSizeY(16);
      blockSettings.setChunkSizeZ(16);
      Material tempMat = new Material(assetManager, “Common/MatDefs/Light/Lighting.j3md”);
      blockSettings.setDefaultBlockMaterial(“Textures/cubes/terrain.png”);

      //create a bullet app state
      BulletAppState bulletAppState = new BulletAppState();
      stateManager.attach(bulletAppState);

      //register your blocks
      CubesTestAssets.registerBlocks();

      //create a SuperChunkManager.
      SuperChunkManager world = new SuperChunkManager(blockSettings, 3, 2);

      //attach the superchunkmanager’s node to the rootNode.
      rootNode.attachChild(world.getNode());

      //add a bullet app state to the SuperChunkManager.
      world.setBulletAppState(bulletAppState);
      /
      /

    • This code automatically grows a terrain as you walk around it.

    • CollisionResults picks = new CollisionResults();
      Ray ray = new Ray(cam.getLocation(), new Vector3f(0, -1, 0));

      world.getNode().collideWith(ray, picks);
      if(picks.size() > 0)
      {
      SuperChunk mod = world.getChunk(picks.getCollision(0));
      Vector3Int translator = mod.getCoords();
      if(currentChunk == null || !currentChunk.equals(mod))
      {
      world.grow(translator, 1);
      currentChunk = mod;
      }
      }
      /
      /

    • this is the picking code I use to modify the block terrain.

    • public void setUpKeys()
      

    {
    //inputManager
    inputManager.addMapping(“Shoot”, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
    inputManager.addMapping(“Remove”, new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));

     inputManager.addListener(actions, "Remove");
     inputManager.addListener(actions, "Shoot");
    

    }

    • private ActionListener actions = new ActionListener()
      

    {
    public void onAction(String name, boolean keyPressed, float tpf)
    {
    if(name.equals(“Shoot”) && keyPressed)
    {
    CollisionResults picks = pick(world.getNode());

             if (picks.size() &gt; 0)
             {
                 SuperChunk manage = picks.getCollision(0).getGeometry().getParent().getControl(SuperChunk.class);
                 
                 Vector3f closestCoords = picks.getCollision(0).getContactPoint();
                 SuperChunk mod = world.getChunk(picks.getCollision(0));
                 System.out.println(closestCoords);
                 Vector3Int translator = mod.getCoords();
                 Vector3Int translatedCoords = BlockNavigator.getPointedBlockLocation(mod.getTerrain(), closestCoords.subtract(mod.getLocation()), true);
                 
                 
                 mod.getTerrain().setBlock(translatedCoords, Block_Wood.class);
             } else
             {
                 
             }
         }
         if(name.equals("Remove") &amp;&amp; keyPressed)
         {
             CollisionResults picks =  pick(world.getNode());
             
             if (picks.size() &gt; 0)
             {
               SuperChunk manage = picks.getCollision(0).getGeometry().getParent().getControl(SuperChunk.class);
                 
                 Vector3f closestCoords = picks.getCollision(0).getContactPoint();
                 SuperChunk mod = world.getChunk(picks.getCollision(0));
                 System.out.println(closestCoords);
                 Vector3Int translator = mod.getCoords();
                 Vector3Int translatedCoords = BlockNavigator.getPointedBlockLocation(mod.getTerrain(), closestCoords.subtract(mod.getLocation()), false);
                 
                 
                 mod.getTerrain().removeBlock(translatedCoords);
             } else
             {
               
             }
         }
     }
    

};

public CollisionResults pick(Node from)
{
    CollisionResults picks = new CollisionResults();
    Ray ray = new Ray(cam.getLocation(), cam.getDirection());
    
    from.collideWith(ray, picks);
    return picks;
}
 */


/*
 * Ignore this. They are notes for myself.
 * TODO: add to rootNode
 *TODO: get, attach bullet app state 
 * TODO: set the proper terrain change listener
 * TODO: allow customisation of the chunk change listener.
 */

/**
 * constructor
 * @param settings cube settings
 * @param chunksPerNode number of chunks per SuperChunk
 * @param initialSizeRadious initial size
 */
public SuperChunkManager(CubesSettings settings, int chunksPerNode, int initialSizeRadious)
{
    this.settings = settings;
    this.chunksPerNode = chunksPerNode;
    terrainNode = new Node();
    chunks = new ArrayList&lt;SuperChunk&gt;();
    for(int a = 1; a &lt;= initialSizeRadious; a++)
    {
        for(int b = 1; b &lt;= initialSizeRadious; b++)
        {
            for(int c = 1; c &lt;= initialSizeRadious; c++)
            {
                Node newNode = new Node();
                SuperChunk newSuCh = new SuperChunk(settings, chunksPerNode, newNode);
                newNode.addControl(newSuCh);
                terrainNode.attachChild(newNode);
                newSuCh.setLocation(new Vector3Int(a, c, b));
                chunks.add(newSuCh);
            }
        }
    }
    Collections.sort(chunks);
 }

/**
 * 
 * @return the node all the terrain, etc. is attached to.
 */
public Node getNode()
{
    return terrainNode;
}

/**
 * sets the bullet app state and applies a physics system to all chunks.
 * 
 * I would strongly advise NOT messing around with this. Only apply one
 * bullet app state, don't try to remove or change it.
 * @param bul 
 */
public void setBulletAppState(BulletAppState bul)
{
    for(SuperChunk chunk : chunks)
    {
        bulletAppState = bul;
        chunk.addChunkListener(new BlockChunkListener()
        {
            @Override
            public void onSpatialUpdated(BlockChunkControl blockChunk)
            {
                Geometry optimizedGeometry = blockChunk.getOptimizedGeometry_Opaque();
                RigidBodyControl rigidBodyControl = optimizedGeometry.getControl(RigidBodyControl.class);
                if(rigidBodyControl == null)
                {
                    rigidBodyControl = new RigidBodyControl(0);
                    optimizedGeometry.addControl(rigidBodyControl);
                    bulletAppState.getPhysicsSpace().add(rigidBodyControl);
                }else
                {
                    bulletAppState.getPhysicsSpace().remove(rigidBodyControl);
                    optimizedGeometry.removeControl(RigidBodyControl.class);
                    rigidBodyControl = new RigidBodyControl(0);
                    optimizedGeometry.addControl(rigidBodyControl);
                    bulletAppState.getPhysicsSpace().add(rigidBodyControl);
                }
                rigidBodyControl.setCollisionShape(new MeshCollisionShape(optimizedGeometry.getMesh()));
             }
        });
        
    }
}

/**
 * 
 * @return the bullet app state used in the chunks.
 */
public BulletAppState getBulletAppState()
{
    return bulletAppState;
}

/**
 * returns a specific terrain controll, given a collision result.
 * Kind of similar to getPointedBlockLocation in the block navagator.
 * 
 * @param pick a collision result. For best results, use collideWith((your super chunk manager).getNode());
 * @return BlockTerrainControl
 */
public SuperChunk getChunk(CollisionResult pick)
{
    SuperChunk manage = pick.getGeometry().getParent().getControl(SuperChunk.class);
    Node loopNode = pick.getGeometry().getParent();
    while(loopNode.getControl(SuperChunk.class) == null &amp;&amp; !loopNode.equals(terrainNode))
    {
        loopNode = loopNode.getParent();
        manage =  loopNode.getControl(SuperChunk.class);
    }
    Vector3f closestCoords = pick.getContactPoint();
    BlockTerrainControl mod = manage.getTerrain();
    
    return manage;
}

/**
 * checks for a SuperChunk at the given coordinates.
 * @param x
 * @param y
 * @param z
 * @return 
 */
public SuperChunk checkFor(int x, int y, int z)
{
    return checkFor(new Vector3Int(x, y, z));
}
/**
 * checks for a SuperChunk at the given Vector3Int coordinates
 * @param coord
 * @return 
 */
public SuperChunk checkFor(Vector3Int coord)
{
   SuperChunk retrived = null;
   for(SuperChunk c : chunks)
   {
       if(c.getCoords().equals(coord))
       {
           retrived = c;
       }
   }
    return retrived;
}

/**
 * grows a terrain based on given coordinates, with a given radius.
 * @param location
 * @param radious 
 */
public void grow(Vector3Int location, int radious)
{
    System.out.println("GROW\n\n\n");
    for(int a = -radious; a &lt;= radious; a++)
    {
        for(int b = -radious; b &lt;= radious; b++)
        {
            for(int c = -radious; c &lt;= radious; c++)
            {
                SuperChunk temp = checkFor(location.add(new Vector3Int(a, c, b)));
                System.out.println(location.add(new Vector3Int(a, c, b)) + ", " + temp);
                if(temp == null)
                {
                    addSuperChunk(location.add(a, c, b));
                }
            }
        }
    }
}

/**
 * creates a superChunk at the given coordinates.
 * @param coords 
 */
public void addSuperChunk(Vector3Int coords)
{
    Node newNode = new Node();
    SuperChunk newSuCh = new SuperChunk(settings, chunksPerNode, newNode);
    newNode.addControl(newSuCh);
    terrainNode.attachChild(newNode);
    newSuCh.setLocation(coords);
    chunks.add(newSuCh);
    System.out.println(newSuCh);
    Collections.sort(chunks);
    if(bulletAppState != null)
    {
        newSuCh.addChunkListener(new BlockChunkListener()
        {
            @Override
            public void onSpatialUpdated(BlockChunkControl blockChunk)
            {
                Geometry optimizedGeometry = blockChunk.getOptimizedGeometry_Opaque();
                RigidBodyControl rigidBodyControl = optimizedGeometry.getControl(RigidBodyControl.class);
                if(rigidBodyControl == null)
                {
                    rigidBodyControl = new RigidBodyControl(0);
                    optimizedGeometry.addControl(rigidBodyControl);
                    bulletAppState.getPhysicsSpace().add(rigidBodyControl);
                }else
                {
                    bulletAppState.getPhysicsSpace().remove(rigidBodyControl);
                    optimizedGeometry.removeControl(RigidBodyControl.class);
                    rigidBodyControl = new RigidBodyControl(0);
                    optimizedGeometry.addControl(rigidBodyControl);
                    bulletAppState.getPhysicsSpace().add(rigidBodyControl);
                }
                rigidBodyControl.setCollisionShape(new MeshCollisionShape(optimizedGeometry.getMesh()));
             }
        });
    }
}

}
[/java]

If any of you care to see a working example, i’ll post my (poorly commented) example here.

1 Like

Alright, since my last post, I’m now able to generate my world in however big of chunks I please (16 or 8 is what I’m going to go with, regardless, its dynamic). The problem now is that I need the surface blocks, generated with a universal noise grid, to be placed. I’m able to make a grid large enough for the entire world; the problem is that I need the individual chunks, during generation, to use their “part” of the grid. For sake of simplicity, the chunk size will be 16 whenever I refer to it.
[java]
public void setBlocksFromNoise(Vector3Int location, Vector3Int size, Class<? extends Block> blockClass, PlanetSide side, BlockChunkControl chunk, RandomTerrainGenerator noise){
float gridMinimum = noise.getMinimum();
float gridLargestDifference = (noise.getMaximum() - gridMinimum);
float[][] grid = noise.getGrid();
if(side == PlanetSide.TOP) {
for(int x=0;x<grid.length;x++){
//(location.getX()-this.chunk.getLocation().getX())/Planet.CHUNK_SIZE
float[] row = grid[x];
for(int z=0;z<row.length;z++){
int blockHeight = (((int) (((((row[z] - gridMinimum) * 100) / gridLargestDifference) / 100) * size.getY())) + 1);
Vector3Int tmpLocation = new Vector3Int();
for(int y=0;y<blockHeight;y++){
tmpLocation.set(location.getX() + x, location.getY() + y, location.getZ() + z);
if(preChunk.isLocationInChunk(tmpLocation))
chunk.getTerrain().setBlock(tmpLocation, blockClass);
}
}
}
}
[/java]

Don’t mind the missing brackets, it continues for the other 5 sides of the planet. Now, its declaring the integers x and z to be zero, therefore beginning at (0,0) and ending at (16,16) on the grid, its finding the Y for the block generation. I don’t have to worry about the limits on x and z because I have the isLocationInChunk function to make sure its only generating in their respective chunks. When starting at 0 for those two integers, you will obviously get the repeating effect.

Now, I thought I had it with the commented line.
[java][…] x=(location.getX()-this.chunk.getLocation().getX())/Planet.CHUNK_SIZE[/java]
That takes the location of where the terrain is to be generated relative to the chunk’s location, subtract the chunk’s location which is relative to the global (0,0,0), then divided by the chunk size. I have no idea why its not working to the desires I want. Here’s what it does; it leaves a nasty border around each chunk and repeats.

Any ideas or solutions?? Its just a math problem I’ve been staring at for too long now, and its summer. :explode: :smiley:

Since its winter for me, I’ll give you a winter outlook :slight_smile:

First without analysing your code, one would think its a maths issue, kinda reminding me of my chunk manager testing days, think you said earlier you are planning on rewriting the chunk control, my suggestion would be to start again with the rewrite of the chunk control, and instead of a super chunk idea, replace it with a chunk manager implementation, that way you can load one chunk at one time, this will allow you to use threading which is a must have for smooth game play, use some type of vector storage for your chunks, add some methods for loading and unloading chunks and you have yourself a chunk manager with infinite terrain (in theory).

@destroflyer has done all the major ground work with his framework, and awesome work too, take what you have learnt from the framework and roll your own, personally that helped me tones in understanding how voxel engines work.

@lawsy said: [...] First without analysing your code, one would think its a maths issue, kinda reminding me of my chunk manager testing days, think you said earlier you are planning on rewriting the chunk control, my suggestion would be to start again with the rewrite of the chunk control, and instead of a super chunk idea, replace it with a chunk manager implementation, that way you can load one chunk at one time, ...

I actually have the chunk management (load, unload) system down just fine. I’m just struggling with the random noise-generated grid when the chunks generate. (Don’t mind the blue space, its just my laziness in generating the rest of the space) To help the brain for thought, I’m actually generating a cube planet (16x16x16 chunks). So I have the planet, on its generation, build a huge noise grid for the surface terrain. My problem is that since I’m generating the chunks in a different thread, they need to all have a common noise grid for it to look any good. Here’s my visual understanding of whats happening with my code:


The chunk threads, marked in green, red, and blue, are all using the same part of the grid for their terrain generation. Basing on the chunks location in comparison with the Planet’s chunk diameter, and the chunk’s location in comparison with the BlockTerrainControl, and/or any other helpful variables, it should hopefully look like this:

Any ideas? I’ll be online for the next couple hours or so so fire away any questions that could help me xD Thanks all.

EDIT:
Here’s a screenshot prior to the terrain generation mess I’m in. The surface chunks in this shot are all making their own noise grids, producing a messy, very rough terrain. I need to make one big grid and then have the chunks find their “place” on the massive universal grid for that planet.

@lawsy said: Since its winter for me, I'll give you a winter outlook :)

First without analysing your code, one would think its a maths issue, kinda reminding me of my chunk manager testing days, think you said earlier you are planning on rewriting the chunk control, my suggestion would be to start again with the rewrite of the chunk control, and instead of a super chunk idea, replace it with a chunk manager implementation, that way you can load one chunk at one time, this will allow you to use threading which is a must have for smooth game play, use some type of vector storage for your chunks, add some methods for loading and unloading chunks and you have yourself a chunk manager with infinite terrain (in theory).

@destroflyer has done all the major ground work with his framework, and awesome work too, take what you have learnt from the framework and roll your own, personally that helped me tones in understanding how voxel engines work.

Uh, I think you may be confusing echospot and I.

I’m not gonna rewrite the chunk control, but rewrite the BlockTerrainControl so that it uses a treemap instead of an array. This should bypass the negative index problem. (note: the BlockTerrainControl essentially is a chunk manager.) Loading and despawning chunks shouldn’t be that much of an issue; I’ve already built the algorithm in my head. Making sure everything works properly within the block terrain control is the challenge. :slight_smile:

The superChunk was just a workaround, a learning experience. It’s super clumsy, but basically worked.

Thanks for the feedback!

Also, thanks to destroflyer–this framework is soooo helpful.

Ok, no worries, so everything is working how you need, except for the noise generation.

How I tackled this issue might be different to how you want, what works for me was using simplex noise to generate the noise per chunk passing the seed value when generating new chunks, set your persistence, resolutions, frequency, amplitudes and octaves as you need.

The main reason for using this type of method to solve the terrain generation, was that the noise can be generated every-time for each chunk in all directions with the same seed value, even mixing setting for different chunk levels types, the possibilities are endless :wink:

@lawsy said: How I tackled this issue might be different to how you want, what works for me was using simplex noise to generate the noise per chunk passing the seed value when generating new chunks, set your persistence, resolutions, frequency, amplitudes and octaves as you need

I tried that but its like its still used the same part of that grid it generated. Idk I’m so confused xD

@echospot said: I tried that but its like its still used the same part of that grid it generated. Idk I'm so confused xD

I can’t remember if @destroflyer noise generator was just cut down version of generating some noise frequency, but for some reason I went with simplex noise.

Main Class

[java]package mygame.Math;

import java.util.Random;

/*

  • A speed-improved simplex noise algorithm for 2D, 3D and 4D in Java.
  • Based on example code by Stefan Gustavson (stegu@itn.liu.se).
  • Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).
  • Better rank ordering method by Stefan Gustavson in 2012.
  • This could be speeded up even further, but it’s useful as it is.
  • Version 2012-03-09
  • This code was placed in the public domain by its original author,
  • Stefan Gustavson. You may use it as you see fit, but
  • attribution is appreciated.

*/
public class SimplexNoise { // Simplex noise in 2D, 3D and 4D

public static int RANDOMSEED = 0;
private static int NUMBEROFSWAPS = 400;
private static Grad grad3[] = {new Grad(1, 1, 0), new Grad(-1, 1, 0), new Grad(1, -1, 0), new Grad(-1, -1, 0),
    new Grad(1, 0, 1), new Grad(-1, 0, 1), new Grad(1, 0, -1), new Grad(-1, 0, -1),
    new Grad(0, 1, 1), new Grad(0, -1, 1), new Grad(0, 1, -1), new Grad(0, -1, -1)};
private static Grad grad4[] = {new Grad(0, 1, 1, 1), new Grad(0, 1, 1, -1), new Grad(0, 1, -1, 1), new Grad(0, 1, -1, -1),
    new Grad(0, -1, 1, 1), new Grad(0, -1, 1, -1), new Grad(0, -1, -1, 1), new Grad(0, -1, -1, -1),
    new Grad(1, 0, 1, 1), new Grad(1, 0, 1, -1), new Grad(1, 0, -1, 1), new Grad(1, 0, -1, -1),
    new Grad(-1, 0, 1, 1), new Grad(-1, 0, 1, -1), new Grad(-1, 0, -1, 1), new Grad(-1, 0, -1, -1),
    new Grad(1, 1, 0, 1), new Grad(1, 1, 0, -1), new Grad(1, -1, 0, 1), new Grad(1, -1, 0, -1),
    new Grad(-1, 1, 0, 1), new Grad(-1, 1, 0, -1), new Grad(-1, -1, 0, 1), new Grad(-1, -1, 0, -1),
    new Grad(1, 1, 1, 0), new Grad(1, 1, -1, 0), new Grad(1, -1, 1, 0), new Grad(1, -1, -1, 0),
    new Grad(-1, 1, 1, 0), new Grad(-1, 1, -1, 0), new Grad(-1, -1, 1, 0), new Grad(-1, -1, -1, 0)};
private static short p_supply[] = {151, 160, 137, 91, 90, 15, //this contains all the numbers between 0 and 255, these are put in a random order depending upon the seed
    131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23,
    190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33,
    88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166,
    77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244,
    102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196,
    135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123,
    5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42,
    223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9,
    129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228,
    251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107,
    49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254,
    138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180};
private short p[] = new short[p_supply.length];
// To remove the need for index wrapping, double the permutation table length
private short perm[] = new short[512];
private short permMod12[] = new short[512];

public SimplexNoise(int seed) {
    p = p_supply.clone();

    if (seed == RANDOMSEED) {
        Random rand = new Random();
        seed = rand.nextInt();
    }

    //the random for the swaps
    Random rand = new Random(seed);

    //the seed determines the swaps that occur between the default order and the order we're actually going to use
    for (int i = 0; i &lt; NUMBEROFSWAPS; i++) {
        int swapFrom = rand.nextInt(p.length);
        int swapTo = rand.nextInt(p.length);

        short temp = p[swapFrom];
        p[swapFrom] = p[swapTo];
        p[swapTo] = temp;
    }


    for (int i = 0; i &lt; 512; i++) {
        perm[i] = p[i &amp; 255];
        permMod12[i] = (short) (perm[i] % 12);
    }
}
// Skewing and unskewing factors for 2, 3, and 4 dimensions
private static final double F2 = 0.5 * (Math.sqrt(3.0) - 1.0);
private static final double G2 = (3.0 - Math.sqrt(3.0)) / 6.0;
private static final double F3 = 1.0 / 3.0;
private static final double G3 = 1.0 / 6.0;
private static final double F4 = (Math.sqrt(5.0) - 1.0) / 4.0;
private static final double G4 = (5.0 - Math.sqrt(5.0)) / 20.0;

// This method is a *lot* faster than using (int)Math.floor(x)
private static int fastfloor(double x) {
    int xi = (int) x;
    return x &lt; xi ? xi - 1 : xi;
}

private static double dot(Grad g, double x, double y) {
    return g.x * x + g.y * y;
}

private static double dot(Grad g, double x, double y, double z) {
    return g.x * x + g.y * y + g.z * z;
}

private static double dot(Grad g, double x, double y, double z, double w) {
    return g.x * x + g.y * y + g.z * z + g.w * w;
}

// 2D simplex noise
public double noise(double xin, double yin) {
    double n0, n1, n2; // Noise contributions from the three corners
    // Skew the input space to determine which simplex cell we're in
    double s = (xin + yin) * F2; // Hairy factor for 2D
    int i = fastfloor(xin + s);
    int j = fastfloor(yin + s);
    double t = (i + j) * G2;
    double X0 = i - t; // Unskew the cell origin back to (x,y) space
    double Y0 = j - t;
    double x0 = xin - X0; // The x,y distances from the cell origin
    double y0 = yin - Y0;
    // For the 2D case, the simplex shape is an equilateral triangle.
    // Determine which simplex we are in.
    int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
    if (x0 &gt; y0) {
        i1 = 1;
        j1 = 0;
    } // lower triangle, XY order: (0,0)-&gt;(1,0)-&gt;(1,1)
    else {
        i1 = 0;
        j1 = 1;
    }      // upper triangle, YX order: (0,0)-&gt;(0,1)-&gt;(1,1)
    // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
    // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
    // c = (3-sqrt(3))/6
    double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
    double y1 = y0 - j1 + G2;
    double x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords
    double y2 = y0 - 1.0 + 2.0 * G2;
    // Work out the hashed gradient indices of the three simplex corners
    int ii = i &amp; 255;
    int jj = j &amp; 255;
    int gi0 = permMod12[ii + perm[jj]];
    int gi1 = permMod12[ii + i1 + perm[jj + j1]];
    int gi2 = permMod12[ii + 1 + perm[jj + 1]];
    // Calculate the contribution from the three corners
    double t0 = 0.5 - x0 * x0 - y0 * y0;
    if (t0 &lt; 0) {
        n0 = 0.0;
    } else {
        t0 *= t0;
        n0 = t0 * t0 * dot(grad3[gi0], x0, y0);  // (x,y) of grad3 used for 2D gradient
    }
    double t1 = 0.5 - x1 * x1 - y1 * y1;
    if (t1 &lt; 0) {
        n1 = 0.0;
    } else {
        t1 *= t1;
        n1 = t1 * t1 * dot(grad3[gi1], x1, y1);
    }
    double t2 = 0.5 - x2 * x2 - y2 * y2;
    if (t2 &lt; 0) {
        n2 = 0.0;
    } else {
        t2 *= t2;
        n2 = t2 * t2 * dot(grad3[gi2], x2, y2);
    }
    // Add contributions from each corner to get the final noise value.
    // The result is scaled to return values in the interval [-1,1].
    return 70.0 * (n0 + n1 + n2);
}

// 3D simplex noise
public double noise(double xin, double yin, double zin) {
    double n0, n1, n2, n3; // Noise contributions from the four corners
    // Skew the input space to determine which simplex cell we're in
    double s = (xin + yin + zin) * F3; // Very nice and simple skew factor for 3D
    int i = fastfloor(xin + s);
    int j = fastfloor(yin + s);
    int k = fastfloor(zin + s);
    double t = (i + j + k) * G3;
    double X0 = i - t; // Unskew the cell origin back to (x,y,z) space
    double Y0 = j - t;
    double Z0 = k - t;
    double x0 = xin - X0; // The x,y,z distances from the cell origin
    double y0 = yin - Y0;
    double z0 = zin - Z0;
    // For the 3D case, the simplex shape is a slightly irregular tetrahedron.
    // Determine which simplex we are in.
    int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
    int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
    if (x0 &gt;= y0) {
        if (y0 &gt;= z0) {
            i1 = 1;
            j1 = 0;
            k1 = 0;
            i2 = 1;
            j2 = 1;
            k2 = 0;
        } // X Y Z order
        else if (x0 &gt;= z0) {
            i1 = 1;
            j1 = 0;
            k1 = 0;
            i2 = 1;
            j2 = 0;
            k2 = 1;
        } // X Z Y order
        else {
            i1 = 0;
            j1 = 0;
            k1 = 1;
            i2 = 1;
            j2 = 0;
            k2 = 1;
        } // Z X Y order
    } else { // x0&lt;y0
        if (y0 &lt; z0) {
            i1 = 0;
            j1 = 0;
            k1 = 1;
            i2 = 0;
            j2 = 1;
            k2 = 1;
        } // Z Y X order
        else if (x0 &lt; z0) {
            i1 = 0;
            j1 = 1;
            k1 = 0;
            i2 = 0;
            j2 = 1;
            k2 = 1;
        } // Y Z X order
        else {
            i1 = 0;
            j1 = 1;
            k1 = 0;
            i2 = 1;
            j2 = 1;
            k2 = 0;
        } // Y X Z order
    }
    // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),
    // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and
    // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where
    // c = 1/6.
    double x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords
    double y1 = y0 - j1 + G3;
    double z1 = z0 - k1 + G3;
    double x2 = x0 - i2 + 2.0 * G3; // Offsets for third corner in (x,y,z) coords
    double y2 = y0 - j2 + 2.0 * G3;
    double z2 = z0 - k2 + 2.0 * G3;
    double x3 = x0 - 1.0 + 3.0 * G3; // Offsets for last corner in (x,y,z) coords
    double y3 = y0 - 1.0 + 3.0 * G3;
    double z3 = z0 - 1.0 + 3.0 * G3;
    // Work out the hashed gradient indices of the four simplex corners
    int ii = i &amp; 255;
    int jj = j &amp; 255;
    int kk = k &amp; 255;
    int gi0 = permMod12[ii + perm[jj + perm[kk]]];
    int gi1 = permMod12[ii + i1 + perm[jj + j1 + perm[kk + k1]]];
    int gi2 = permMod12[ii + i2 + perm[jj + j2 + perm[kk + k2]]];
    int gi3 = permMod12[ii + 1 + perm[jj + 1 + perm[kk + 1]]];
    // Calculate the contribution from the four corners
    double t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;
    if (t0 &lt; 0) {
        n0 = 0.0;
    } else {
        t0 *= t0;
        n0 = t0 * t0 * dot(grad3[gi0], x0, y0, z0);
    }
    double t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;
    if (t1 &lt; 0) {
        n1 = 0.0;
    } else {
        t1 *= t1;
        n1 = t1 * t1 * dot(grad3[gi1], x1, y1, z1);
    }
    double t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;
    if (t2 &lt; 0) {
        n2 = 0.0;
    } else {
        t2 *= t2;
        n2 = t2 * t2 * dot(grad3[gi2], x2, y2, z2);
    }
    double t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;
    if (t3 &lt; 0) {
        n3 = 0.0;
    } else {
        t3 *= t3;
        n3 = t3 * t3 * dot(grad3[gi3], x3, y3, z3);
    }
    // Add contributions from each corner to get the final noise value.
    // The result is scaled to stay just inside [-1,1]
    return 32.0 * (n0 + n1 + n2 + n3);
}

// 4D simplex noise, better simplex rank ordering method 2012-03-09
public double noise(double x, double y, double z, double w) {

    double n0, n1, n2, n3, n4; // Noise contributions from the five corners
    // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in
    double s = (x + y + z + w) * F4; // Factor for 4D skewing
    int i = fastfloor(x + s);
    int j = fastfloor(y + s);
    int k = fastfloor(z + s);
    int l = fastfloor(w + s);
    double t = (i + j + k + l) * G4; // Factor for 4D unskewing
    double X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space
    double Y0 = j - t;
    double Z0 = k - t;
    double W0 = l - t;
    double x0 = x - X0;  // The x,y,z,w distances from the cell origin
    double y0 = y - Y0;
    double z0 = z - Z0;
    double w0 = w - W0;
    // For the 4D case, the simplex is a 4D shape I won't even try to describe.
    // To find out which of the 24 possible simplices we're in, we need to
    // determine the magnitude ordering of x0, y0, z0 and w0.
    // Six pair-wise comparisons are performed between each possible pair
    // of the four coordinates, and the results are used to rank the numbers.
    int rankx = 0;
    int ranky = 0;
    int rankz = 0;
    int rankw = 0;
    if (x0 &gt; y0) {
        rankx++;
    } else {
        ranky++;
    }
    if (x0 &gt; z0) {
        rankx++;
    } else {
        rankz++;
    }
    if (x0 &gt; w0) {
        rankx++;
    } else {
        rankw++;
    }
    if (y0 &gt; z0) {
        ranky++;
    } else {
        rankz++;
    }
    if (y0 &gt; w0) {
        ranky++;
    } else {
        rankw++;
    }
    if (z0 &gt; w0) {
        rankz++;
    } else {
        rankw++;
    }
    int i1, j1, k1, l1; // The integer offsets for the second simplex corner
    int i2, j2, k2, l2; // The integer offsets for the third simplex corner
    int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
    // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
    // Many values of c will never occur, since e.g. x&gt;y&gt;z&gt;w makes x&lt;z, y&lt;w and x&lt;w
    // impossible. Only the 24 indices which have non-zero entries make any sense.
    // We use a thresholding to set the coordinates in turn from the largest magnitude.
    // Rank 3 denotes the largest coordinate.
    i1 = rankx &gt;= 3 ? 1 : 0;
    j1 = ranky &gt;= 3 ? 1 : 0;
    k1 = rankz &gt;= 3 ? 1 : 0;
    l1 = rankw &gt;= 3 ? 1 : 0;
    // Rank 2 denotes the second largest coordinate.
    i2 = rankx &gt;= 2 ? 1 : 0;
    j2 = ranky &gt;= 2 ? 1 : 0;
    k2 = rankz &gt;= 2 ? 1 : 0;
    l2 = rankw &gt;= 2 ? 1 : 0;
    // Rank 1 denotes the second smallest coordinate.
    i3 = rankx &gt;= 1 ? 1 : 0;
    j3 = ranky &gt;= 1 ? 1 : 0;
    k3 = rankz &gt;= 1 ? 1 : 0;
    l3 = rankw &gt;= 1 ? 1 : 0;
    // The fifth corner has all coordinate offsets = 1, so no need to compute that.
    double x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
    double y1 = y0 - j1 + G4;
    double z1 = z0 - k1 + G4;
    double w1 = w0 - l1 + G4;
    double x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords
    double y2 = y0 - j2 + 2.0 * G4;
    double z2 = z0 - k2 + 2.0 * G4;
    double w2 = w0 - l2 + 2.0 * G4;
    double x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords
    double y3 = y0 - j3 + 3.0 * G4;
    double z3 = z0 - k3 + 3.0 * G4;
    double w3 = w0 - l3 + 3.0 * G4;
    double x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords
    double y4 = y0 - 1.0 + 4.0 * G4;
    double z4 = z0 - 1.0 + 4.0 * G4;
    double w4 = w0 - 1.0 + 4.0 * G4;
    // Work out the hashed gradient indices of the five simplex corners
    int ii = i &amp; 255;
    int jj = j &amp; 255;
    int kk = k &amp; 255;
    int ll = l &amp; 255;
    int gi0 = perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32;
    int gi1 = perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32;
    int gi2 = perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32;
    int gi3 = perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32;
    int gi4 = perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32;
    // Calculate the contribution from the five corners
    double t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;
    if (t0 &lt; 0) {
        n0 = 0.0;
    } else {
        t0 *= t0;
        n0 = t0 * t0 * dot(grad4[gi0], x0, y0, z0, w0);
    }
    double t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;
    if (t1 &lt; 0) {
        n1 = 0.0;
    } else {
        t1 *= t1;
        n1 = t1 * t1 * dot(grad4[gi1], x1, y1, z1, w1);
    }
    double t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;
    if (t2 &lt; 0) {
        n2 = 0.0;
    } else {
        t2 *= t2;
        n2 = t2 * t2 * dot(grad4[gi2], x2, y2, z2, w2);
    }
    double t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;
    if (t3 &lt; 0) {
        n3 = 0.0;
    } else {
        t3 *= t3;
        n3 = t3 * t3 * dot(grad4[gi3], x3, y3, z3, w3);
    }
    double t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;
    if (t4 &lt; 0) {
        n4 = 0.0;
    } else {
        t4 *= t4;
        n4 = t4 * t4 * dot(grad4[gi4], x4, y4, z4, w4);
    }
    // Sum up and scale the result to cover the range [-1,1]
    return 27.0 * (n0 + n1 + n2 + n3 + n4);
}

// Inner class to speed upp gradient computations
// (array access is a lot slower than member access)
private static class Grad {

    double x, y, z, w;

    Grad(double x, double y, double z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    Grad(double x, double y, double z, double w) {
        this.x = x;
        this.y = y;
        this.z = z;
        this.w = w;
    }
}

}[/java]

Generator

[java]package mygame.Math;

import java.util.Random;

/**
*
*
*/
public class SimplexNoiseGenerator {

private double persistence;
private double[] frequencys;
private double[] amplitudes;
private SimplexNoise[] octaves;

public SimplexNoiseGenerator(int largestFeature, double persistence, int seed) {
    this.persistence = persistence;

    //recieves a number (eg 128) and calculates what power of 2 it is (eg 2^7)
    int numberOfOctaves = (int) Math.ceil(Math.log10(largestFeature) / Math.log10(2));
    octaves = new SimplexNoise[numberOfOctaves];
    frequencys = new double[numberOfOctaves];
    amplitudes = new double[numberOfOctaves];        

    Random random = new Random(seed);
    for (int i = 0; i &lt; numberOfOctaves; i++) {
        octaves[i] = new SimplexNoise(random.nextInt());
        frequencys[i] = Math.pow(2, i);
        amplitudes[i] = Math.pow(persistence, octaves.length - i);
    }
}

public double getNoise(int x, int y) {
    double result = 0;
    for (int i = 0; i &lt; octaves.length; i++) {
        result = result + octaves[i].noise(x / frequencys[i], y / frequencys[i]) * amplitudes[i];
    }
    return result;
}

public double getNoise(int x, int y, int z) {
    double result = 0;
    for (int i = 0; i &lt; octaves.length; i++) {
        double frequency = Math.pow(2, i);
        double amplitude = Math.pow(persistence, octaves.length - i);
        result = result + octaves[i].noise(x / frequency, y / frequency, z / frequency) * amplitude;
    }
    return result;
}

}
[/java]

1 Like
@lawsy said: I can't remember if @destroflyer noise generator was just cut down version of generating some noise frequency, but for some reason I went with simplex noise.

They’re basically the same thing. @destroflyer has a midpoint displacement fractal generator, which is a basic simplex noise generator and produce a grid. I’m just having trouble interpreting that into my code xD

I’ll keep working at it and let y’all know. Thanks for your help @lawsy

@echospot said: They're basically the same thing. @destroflyer has a midpoint displacement fractal generator, which is a basic simplex noise generator and produce a grid. I'm just having trouble interpreting that into my code xD

I’ll keep working at it and let y’all know. Thanks for your help @lawsy

The problem with midpoint displacement is that you have to know the endpoints to define the midpoint. Translated: you have to define the full extent of the map all at once.

A noise based fractal won’t have this limitation (generally) since you can sample any point independently at any time. That’s one of the things that makes them awesome.

1 Like
@pspeed said: The problem with midpoint displacement is that you have to know the endpoints to define the midpoint. Translated: you have to define the full extent of the map all at once.

A noise based fractal won’t have this limitation (generally) since you can sample any point independently at any time. That’s one of the things that makes them awesome.

Ah…I’m learning all sorts of stuff xD I can get away with the midpoint displacement because my block terrain is limited to the planet surface.

@pspeed said: A noise based fractal won't have this limitation (generally) since you can sample any point independently at any time. That's one of the things that makes them awesome.

Is there a good introduction into the basic concepts for noise-based fractals somewhere?

@toolforger said: Is there a good introduction into the basic concepts for noise-based fractals somewhere?

I looked here for where I’m going to make my own generator. Nice and simple right off of Stefan Gustavson’s code.

1 Like
@toolforger said: Is there a good introduction into the basic concepts for noise-based fractals somewhere?

I cut my teeth on the book “Texturing and Modeling - A Procedural Approach” - Ebert, Musgrave, Peachey, and Perlin (yes, that Perlin)

Highly recommend if you can find a copy. http://www.amazon.com/Texturing-Modeling-Third-Procedural-Approach/dp/1558608486/ref=sr_1_1?s=books&ie=UTF8&qid=1404712453&sr=1-1

I have a first edition from the early 90s that has been partially burned in a fire but I still keep it within arm’s reach.

Now, that old version dealt with Perlin noise… which has some deficiencies but for getting started you won’t notice them… and when you do you will be all set to go for the upgraded version: Simplex noise.

Personally, I still use Perlin noise for everything because I’m lazy and I’ve had a Java implementation to hand for almost 10 years now.

1 Like