Cubes – A Block World Framework [Update Preview]

@ulfgur said: Perused it. You mean this one, right? http://http.developer.nvidia.com/GPUGems3/gpugems3_ch01.html

I understand the concepts: generate a set of semi-random wavelike functions, each tighter and smaller than the last, then smash them all together and read the resulting floats to get block heights.

It’s convincing any of the concrete maths to work that gives me trouble. I tried stealing pseudocode from various tutorials, but I can’t even get halfway through converting them to a usable format. I also found a perlin noise generator liscenced under the GNU LGPL v2.1. I’d use that, but It only wanted to return zeros. :stuck_out_tongue_winking_eye:
It may be that I just need to do more searching…


Took me a while to find it again, but https://bitbucket.org/ojacobson/jnoise is a good library for noise stuff. It’s a port of libnoise. Use the Maven plugin to download and build it all, and then add the .jars it generates as libraries for your project.

1 Like

If you use the Maven plugin, you shouldn’t have to create a separate project, referencing ca.grimoire.jnoise:jnoise:1.3.0 as a dependency should be enough. Maven (or Gradle if you use that) will automatically download the jar, the sources, and any indirect dependencies jnoise uses. (Use the appropriate group-artifact-version syntax instead of the colon-separated shorthand I gave.)

1 Like
@ulfgur said: Perused it. You mean this one, right? http://http.developer.nvidia.com/GPUGems3/gpugems3_ch01.html

I understand the concepts: generate a set of semi-random wavelike functions, each tighter and smaller than the last, then smash them all together and read the resulting floats to get block heights.

It’s convincing any of the concrete maths to work that gives me trouble. I tried stealing pseudocode from various tutorials, but I can’t even get halfway through converting them to a usable format. I also found a perlin noise generator liscenced under the GNU LGPL v2.1. I’d use that, but It only wanted to return zeros. :stuck_out_tongue_winking_eye:
It may be that I just need to do more searching…

Yeah, I think your search-fu needs some work, maybe…

All you had to do was search the forum for the word IsoSurface and this was the first hit:
http://hub.jmonkeyengine.org/forum/topic/isosurface-demo-dev-blog-experiment/

Just a little poking around from there gets:
https://code.google.com/p/simsilica-tools/source/browse/trunk/IsoSurface/src/main/java/com/simsilica/iso/fractal/PerlinNoise.java

Of course, if you searched the forum for “PerlinNoise” you also find someone else has provided a ready-made implementation also.

But right next to my PerlinNoise implementation is a Java implementation of the exact thing from the GPU Gems chapter.
https://code.google.com/p/simsilica-tools/source/browse/trunk/IsoSurface/src/main/java/com/simsilica/iso/fractal/GemsFractalDensityVolume.java

And the latest video of all of that (open source code with a JME compatible license) in action:

2 Likes

It seems the default physics code is wonky. The default code is this:
[java]
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);
}
rigidBodyControl.setCollisionShape(new MeshCollisionShape(optimizedGeometry.getMesh()));
}
[/java]
This changes the collision shape while the rigid body is still in the physics space. That was causing problems if I built anything outside the existing bounding box of my chunks, letting me fall through the surface. I replaced it with this:
[java]
public void onSpatialUpdated(BlockChunkControl blockChunk) {
Geometry optimizedGeometry = blockChunk.getOptimizedGeometry_Opaque();
RigidBodyControl rigidBodyControl = optimizedGeometry.getControl(RigidBodyControl.class);
if(rigidBodyControl != null) {
app.bulletAppState.getPhysicsSpace().remove(rigidBodyControl);
}
else {
rigidBodyControl = new RigidBodyControl(0);
optimizedGeometry.addControl(rigidBodyControl);
}

            rigidBodyControl.setCollisionShape(new MeshCollisionShape(optimizedGeometry.getMesh()));
            app.bulletAppState.getPhysicsSpace().add(rigidBodyControl);
        }

[/java]

@pspeed: Oh! I thought you just wanted me to follow the link.

I did a fair amount of searching for Perlin noise, just not on the JMonkey forum.

Anyhow, I’ve got your perlin noise class into my project, which is great. The problem now is that it also wants to just return zeros–which leads me to the conclusion that I’ve missed something stupidly obvious. Now, some of these zeros are negative, but I can’t seem to get Java to tell the difference between a zero and a negative zero, since they’re the same thing.

Here’s my function for filling in a chunk from perlin noise:

[java]
public void setChunkFromNoise(Vector3Int location)
{
int xOffset = location.getX() * 16;
int yOffset = location.getY() * 16;
int zOffset = location.getZ() * 16;
System.out.println(getChunk(new Vector3Int(xOffset, yOffset, zOffset)).getLocation());

    PerlinNoise noise = new PerlinNoise(35634);

    
    for(int x=0; x < 16; x++)
    {
        for(int z=0; z < 16; z++)
        {
            for(int y = 0; y < 16; y++)
            {
                System.out.println(noise.getNoise(x + xOffset, y + yOffset, z + zOffset));
                if(noise.getNoise(x + xOffset, y + yOffset, z + zOffset) < 0)
                {
                    System.out.println("False");
                    
                }else if(noise.getNoise(x + xOffset, y + yOffset, z + zOffset) >= 0)
                {
                    System.out.println("True");
                    setBlock(new Vector3Int(x + xOffset, y + yOffset, z + zOffset), Block_Grass.class);
                }
            }
        }
    }
}

[/java]

I also tried using the noise.getNoise(x, z);, then filling in everything below that value, but it only wants to return zeros. How do I convince it to return something else, or properly adjust my code? I’m missing something really stupid, aren’t I?

PS: That video is very cool. I could see it as an open-ended exploration game without too much adjustment.

Most noise operates at the sub-integer level… Perlin noise in particular is 0 at the corners. You need to divide your x,y,z by some value so that you aren’t just hitting every integer corner. If you download my map maker I already mentioned previously then you can play with the values of Perlin Noise to see what they do. The “resolution” is what controls what x,y are divided by.

for those wondering about my Noise terrain generation it can easily be seen on the root of the test project in my repo: Cubed/CubedTest.java at master · Sleaker/Cubed · GitHub

It uses the jLibNoise library that is in the project, also available via github if you need to rebuild it: GitHub - Sleaker/jlibnoise: A Java port of libnoise - A portable, open-source, coherent noise-generating library

@pspeed said: Most noise operates at the sub-integer level... Perlin noise in particular is 0 at the corners. You need to divide your x,y,z by some value so that you aren't just hitting every integer corner. If you download my map maker I already mentioned previously then you can play with the values of Perlin Noise to see what they do. The "resolution" is what controls what x,y are divided by.

Got it! Thank you soo much! It produces some Awesome terrain, now. I might have some questions later (the floor is missing, due to a failure on my part), but it’s beautiful!

@ulfgur said: 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.

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


I’d love to see an example of this. I can’t seem to get it to work right.

I put the system on this page:
http://hub.jmonkeyengine.org/forum/topic/cubes-a-block-world-framework/page/18/
the comments in the SuperChunk class give instructions on how to implement the system. Steal any of that freely.

Now, I should warn you, the system is clumsy. While it generates terrains infinitely very nicely, it has a few glaring flaws: The generated terrain has no noise, and is accordingly butt-ugly, and has no chunk loader/unloader. You should be able to write a chunk loader/unloader for it fairly easily if you know anything about IO.

If you can’t get that working, I’ll try to find a way to get you the complete project file to play around with.

I’m currently in the process of rewriting portions of the BlockTerrainControl to truly get the system infinite. I am using stuff I learned while writing the SuperChunk classes, so you might find looking through it somewhat educational. When I finish, I’d be happy to share portions… but it may take me a while longer. I’m currently ironing out problems with noise, and haven’t got around to re-implementing infinite terrain.

OK, got the noise working aaaalmost how I want it. (I’ve got some nitpicky stuff I hope you can help me with, pspeed, but I have to get around to getting and cropping good screenshots to demonstrate what’s wrong.)

Consequently, I’m moving on to the next step: re-building the infinite…ness of my terrain. I’m working with a BlockTerrainControl that’s been modified to work with HashMaps instead of arrays and has a functional setChunkFromNoise function.

So, my next step is to create a addChunk function:

[java] public void addChunk(Vector3Int location)
{
if(!chunksMap.containsKey(location))
{
BlockChunkControl chunk = new BlockChunkControl(this, location.getX(), location.getY(), location.getZ());
chunksMap.put(location, chunk);
setChunkFromNoise(location);
}
}[/java]
Of course, it isn’t working. The function doesn’t throw exceptions or anything, it just doesn’t add the chunk. I’ve tried various things, including getSpatial.addControl(chunk);. Any ideas how to get my new chunk to show up?

Got it solved! A clever use of slight coordinate reworks and a liberal application of being less stupid than yesterday does wonders.

1 Like
@ulfgur said: Got it solved! A clever use of slight coordinate reworks and a liberal application of being less stupid than yesterday does wonders.

Haha yup, some days my internal operating system locks up and I can’t get anything done. Turning my screen off for about 8 hours and letting myself reboot generally fixes it.

1 Like

I’m now working on serialisation, with the final intent of loading and despawning individual chunks.

My chunks generate from noise automatically.

The current goal is to write a chunk to a byte array, create a new chunk, then set blocks according to the byte array and place it two chunks above the original.

Currently, when I use this nothing happens:

[java]
//I’ve converted the chunk lists to a hashmap.
BlockChunkControl chunk = chunksMap.get(location);

//serializedTerrainData is a byte[]
//. Theoretically, the cube serializer should do this portion effectively.
serializedTerrainData = CubesSerializer.writeToBytes(chunk);

//create a new chunk, place it in the proper location.
BlockChunkControl dupeChunk = new BlockChunkControl(this, location.getX(), location.getY() + 2, location.getZ());
//dynamically add it to the game,…
if(spatial != null)
{
spatial.addControl(dupeChunk);
}
chunksMap.replace(location, chunk);

//…and place the blocks in their original order.
CubesSerializer.readFromBytes(dupeChunk, serializedTerrainData);
[/java]

Any idea why this doesn’t work? What should I be looking for to fix this?

EDIT: Solved in a rather brutal, ugly way.

I’ve completed the first version of my infinite terrain modifications! I’d love to put it here, but can’t seem to find an equivalent to imgur for zip files. If any of you has an idea, please tell me. I’ll put it here as soon as I can.

Changelog:
-----BlockTerrainControl–
-------now stores chunks in a ConcurrentHashMap
------ -defaults to perlin noise, kindly provided by pspeed. Without him, this would have been way, way harder.
-------has a half-completed noise manager
----------setBlocksFromNoise has been marked depracated
-------new growWorld function
----------accepts a vector3Int chunk location
----------adds or loads chunks in the radius taken from CubeSettings
----------calls reduceWorld for the same location
-------new reduceWorld function
----------saves, then removes chunks that are too far away from given location
-------new helper functions:
----------addChunk
----------loadChunk
----------despawnChunk
----------setChunkFromNoise

-----CubeSettings–
-------now stores getters and setters for physics, current save path, and chunk load radius

-----CubeSerializer and related classes–
-------I ended up removing some functions. This is not an improvement, but an alert. Sorry! : (

Known issues:
-terrain only generates infinitely in the positive directions. I aim to fix this through an offset (to my intense annoyance). If any of you have a better idea, for heaven’s sake, please tell me!
-grass blocks in loaded chunks have grass tops even when underneath another block. I have no idea why they do this, I think I’m calling all the right update functions in read(); to stop this from happening.
-I’m getting lag whenever I add chunks. I don’t like this, especially since I went to the trouble of learning to multithread it so that it wouldn’t lag. If any of you manage to improve performance when growing the world, please tell me that, too.

I have some fairly big fish to fry (especially the omnidirectional infinite terrain), so the next major version could take a while to release. If any of you have ANY ideas on how to make this better, TELL ME. I want to know!

Once I get this posted properly, feel free to use it in your games.

I hope I have improved the system, rather than making it worse.

@ulfgur said: I've completed the first version of my infinite terrain modifications! I'd love to put it here, but can't seem to find an equivalent to imgur for zip files. If any of you has an idea, please tell me. I'll put it here as soon as I can.

Just make a Dropbox acct. and upload the zip to your public files :smiley:

@ulfgur said: Known issues: -terrain only generates infinitely in the positive directions. I aim to fix this through an offset (to my intense annoyance). If any of you have a better idea, for heaven's sake, please tell me!
@PewPewKapowie managed to figure that out on our game...somehow. I'd ask him.
@ulfgur said: -grass blocks in loaded chunks have grass tops even when underneath another block. I have no idea why they do this, I think I'm calling all the right update functions in read(); to stop this from happening.
We actually modified our game so it would literally change anything under the grass was dirt, not a fancy grass block. We plan then, after time like Minecraft, to "grow" grass on that new surface block (aka change it back to grass).
@ulfgur said: -I'm getting lag whenever I add chunks. I don't like this, especially since I went to the trouble of learning to multithread it so that it wouldn't lag. If any of you manage to improve performance when growing the world, please tell me that, too.
I'm assuming its the naive mesher that Cubes has in its framework. I would double-check your multithreading as well (make sure that the chunks are fully generated before adding to prevent un-needed remeshing). actually for the multithreading, use futures and execution pools for that, they're beautiful pieces of programming goodies. xD As far as meshing goes, the greedy mesher that @sleaker has implemented in his fork of the framework enhances performance a TON. Sleaker's post here explains the difference between naive meshing and greedy meshing.
1 Like
@ulfgur said: I've completed the first version of my infinite terrain modifications! I'd love to put it here, but can't seem to find an equivalent to imgur for zip files. If any of you has an idea, please tell me. I'll put it here as soon as I can.
You could use Google Code or Github. It's a bit harder to setup, but whenever you want to update it you can just use the push button.
1 Like

Sites you can keep your code on:
https://github.com/ (best git support but no other SCMs, excellent GUI)
https://bitbucket.org/ (git and mercurial, friendlier for small businesses, excellent GUI)
http://sourceforge.net/ (git/mercurial/svn, open source only, lacking GUI, questionable corporate policy)
https://code.google.com/ (git/mercurial/svn, open source only, adequate GUI)

SVN: Do not use unless you have nontechnical reasons to (most prominent and valid being “I already know it”).
git: Basic machinery (“plumbing”) is really easy to grasp, but the console tools (“porcelain”) take a lot of time (months of constant usage) to learn. Rumored to offer slightly better support for Linux-kernel-sized than mercurial, but not really differ for anything smaller.
mercurial: My impression was that plumbing and porcelain are less well separated than in git. I hear rumors that mercurial is far easier to learn than git, and the git people seem to be rather defensive about that point so it’s plausible.

Just so you guys can make a somewhat informed choice about which tool and site to use :slight_smile:

1 Like
@toolforger said: https://github.com/ (best git support but no other SCMs, excellent GUI)

You can use svn to interact with github repositories just as you would git.

Edit: also, “excellent gui” certainly does not refer to the github web site… which is down near the bottom with sourceforge these days.

Hm. I don’t see the ability to discuss individual lines of code on Sourceforge.
Using SVN to interact with git seems unwise to me, SVN simply can’t reasonably map git’s data model.
Unless your drive the repository entirely from SVN and never allow git-based write access to the repo. I think it would be hard to enforce that rule though, so with the first slip you risk getting something that SVN isn’t really happy about when displaying the history.

The recommenation against SVN is mostly a question of contributions. SVN gets slightly less useful with each contributor with write access, that’s all; SVN is just fine if you don’t have contributors and never experiment with ideas that you plan to scrap, or that you want to experiment with in parallel to normal ongoing work. (SVN is really bad at merging different branches. Or, rather, git and mercurial had to be top-notch on the merge tools due to their decentralized approaches, which turned out to be really essential even if you don’t do decentralized development, so now SVN is far behind.)