Cubes – A Block World Framework [Update Preview]

@lawsy said: Hi sleaker, have created a working in progress project called Greedy Vox, based off your greedy render and destroflyer framework, plus roleary documentation, currently working on a chunk manager for an infinite world, impressive work from you both (sleaker and destroflyer), had to rewrite some of the greedy render code to implement a block texture atlas (16 x 16) blocks using GLSL100 shader, also rewrite code of the framework to use your non-positive chunks (Hash Map) to implement removing and placing blocks across all chunks with a one dimensional byte array of blocks, without any optimisation and still a prototype working progress, was able to render 121 million blocks with ease, also can render a minecraft multi-player chunk radius (21, 16, 21) of 16 x blocks, with noise generated terrain and multiple block types plus shadowing, all while rendering over 300fps, thank you guys for sharing your work and happy to share if anyone is interested.

Definitely interested as I didn’t want to take the time to go through and convert the texture atlas code to work on GL2.x, but anything you post back will always help others understand how to make things better and make things creatable more quickly.

2 Likes
@sleaker said: Definitely interested as I didn't want to take the time to go through and convert the texture atlas code to work on GL2.x, but anything you post back will always help others understand how to make things better and make things creatable more quickly.

Oh yeah, was a great learning experience for myself, still a noob with game programming, java and jmonkey, but really interested in voxel worlds, took me a week of playing around with the greedy render and tiling texture coords along quad faces, at times going insane, but in the end worked out, pretty dam sure there are lots of room for optimising, but baby steps for now.

Block Vert
[java]
uniform mat4 g_WorldViewProjectionMatrix;

attribute vec3 inPosition;
attribute vec2 inTexCoord;
attribute vec2 inTexCoord2;

varying vec2 TEX_SKIN;
varying vec2 TEX_COORD;
void main() {
TEX_SKIN = inTexCoord2;
TEX_COORD = inTexCoord;

// projection matrix and stores it in the built-in output variable gl_Position.
gl_Position = g_WorldViewProjectionMatrix * vec4(inPosition, 1.0);        

}[/java]

Block Frag
[java]uniform sampler2D m_ColorMap;

const float TILE_COUNT = 16.0;
const float INV_TILE_COUNT = 1 / TILE_COUNT;

varying vec2 TEX_SKIN;
varying vec2 TEX_COORD;
void main() {
vec4 color = vec4(1.0);
#ifdef TEXTURE
vec2 sideCoord = vec2(0);
sideCoord.x = INV_TILE_COUNT * (TEX_SKIN.x - 1.0);
sideCoord.y = INV_TILE_COUNT * (TILE_COUNT - TEX_SKIN.y);

    vec2 texCoord = sideCoord + mod(TEX_COORD, INV_TILE_COUNT);
    color *= texture2D(m_ColorMap, texCoord);        
#endif

gl_FragColor = color;

}[/java]

Hey again! I lost my JME3 credentials for awhile for some reason…but I’m back xD

Still working on a game using Cubes and I’m excited about the new update. Although, after all this time, I’m still fighting a bug.

Every tutorial or other example of code I look at using Cubes ALWAYS uses a DirectionalLight. Since I’m creating a Star, I’m using a PointLight; The shadows are horrendous.

Anyway to fix this? We are working on a massive scale and that might put stress on the PointShadowRenderer. I did post this problem on the forums almost a year ago with no response. My code can be seen there.

http://hub.jmonkeyengine.org/forum/topic/point-light-shadow-troubles/

Thanks much, and yes, still going on this project…just very slowly =D

EDIT: I just actually made the ShadowMap size smaller and had a much better result. However the shadow edges are still very…nasty xD

EDIT…again: So after researching how this all works being the n00b I am, Point lights simply will not work Here’s a picture showing why.

This is probably a silly problem I’m having with a simple solution: I’m trying to use the picking example code on the wiki, but it gives an error that it cannot find the blockTerrain variable.

The blockTerrain is defined in the SimpleInitApp method, and I’m trying to access it in onAction method.

Main Example code - https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:contributions:cubes:basic_example
Picking Code - https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:contributions:cubes:tools:picking

@Isaactoo said: This is probably a silly problem I'm having with a simple solution: I'm trying to use the picking example code on the wiki, but it gives an error that it cannot find the blockTerrain variable

All you need to do is make the blockTerrain a static variable so the other getCurrentPointedBlockLocation class can access it. Quick 'n dirty.

Static? 8-O
The two methods he’s talking about are even in the same class, a normal attribute (e.g. private BlockTerrainControl blockTerrain;) should be enough. :slight_smile: And in case one separates those in multiple classes, always try to use references and get-methods over static code.

1 Like
@destroflyer said: Static? 8-O The two methods he's talking about are even in the same class, a normal attribute (e.g. private BlockTerrainControl blockTerrain;) should be enough. :)

Right…xD Mine are in two different classes and I actually pass the whole class through as a parameter so I have access to it 8)

1 Like

Just for a little test, I copied your code from the link in the previous forum and ran it in my Greedy Vox project and worked fine, also never had any issues before using lights and shadows with the cube framework either, sorry I can’t help, not experienced enough engineer with jmonkey, but looking at your examples you posted, kind of seems like a graphics card glitch, just throwing out some ideas, hopefully some of the guys on this forum can give you better advice.

Btw @destroflyer what if I told you I may or may not have an infant chunk system working =D The problem I’m having, and this might be because we’re moving the blockTerrain node around, but it generates when the player comes close, unloads when you withdraw, and then loads again when you get closer. However, it won’t unload again. I’m not sure whether this is because my chunk class map’s block locations are invalid or a rendering error. Any clues?

When I add the line public BlockTerrainControl blockTerrain; before the simpleInitApp, it runs fine…until I click…then I get “Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main] NullPointer Exception” :S

@lawsy said: Just for a little test, I copied your code from the link in the previous forum and ran it in my Greedy Vox project and worked fine, also never had any issues before using lights and shadows with the cube framework either, sorry I can't help, not experienced enough engineer with jmonkey, but looking at your examples you posted, kind of seems like a graphics card glitch, just throwing out some ideas, hopefully some of the guys on this forum can give you better advice.

I had managed to get it fixed, just made like 6 different directional lights. I spy a chunk manager…is there a possibility I could grab that code? I’m trying to write my own and its a nightmare.

@Isaactoo said: When I add the line public BlockTerrainControl blockTerrain; before the simpleInitApp, it runs fine...until I click...then I get "Uncaught exception thrown in Thread[LWJGL Renderer Thread,5,main] NullPointer Exception" :S

Since we can’t see the code I can’t say which line exactly but I’ll bet you that there is a line the SDK is warning you about.

1 Like

There is one line warning about “local variable hides a field”

Edit: Sorry I copy/pasted it wrong

Edit2: I found the Error! Had to delete BlockTerrainControl on line 56…thank you pspeed, echospot, and destroflyer :slight_smile:

Here’s my code:

[java]package mygame;

import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.app.SimpleApplication;
import com.jme3.math.Vector3f;
import com.jme3.math.Vector2f;

import com.jme3.system.AppSettings;
import com.cubes.;
import com.cubes.test.blocks.
;
import com.cubes.test.*;

import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.math.Ray;
import com.jme3.scene.Node;
import com.jme3.font.BitmapText;
import com.jme3.scene.shape.Sphere;
import com.jme3.material.Material;
import com.jme3.scene.Geometry;
import com.jme3.math.ColorRGBA;

public class TestTutorial extends SimpleApplication{

public static void main(String[] args){
    TestTutorial app = new TestTutorial();
    app.start();
}

public TestTutorial(){
    settings = new AppSettings(true);
    settings.setWidth(1280);
    settings.setHeight(720);
    settings.setTitle("Cubes Demo - Tutorial");
}

private Node terrainNode;
private BlockTerrainControl blockTerrain;

@Override
public void simpleInitApp(){
    initCrossHairs(); // a "+" in the middle of the screen to help aiming
    initKeys();       // load custom key mappings
    initMark();       // a red sphere to mark the hit
    
    //You have to specify a valid application, the framework will use e.g. its assetManager
    CubesTestAssets.registerBlocks();
    
    //Create the block terrain (7x1x7 chunks)
    BlockTerrainControl blockTerrain = new BlockTerrainControl(CubesTestAssets.getSettings(this), new Vector3Int(7, 1, 7));

    //Specify location, heightmap filepath, maximum height and the block class
    //(See the heightmap at the right)
    blockTerrain.setBlocksFromHeightmap(new Vector3Int(0, 1, 0), "Textures/GimpCloudHeightmap10.jpg", 10, Block_Grass.class);
    blockTerrain.setBlocksFromHeightmap(new Vector3Int(0, 1, 0), "Textures/GimpCloudHeightmap11.jpg", 10, Block_Stone.class); 

    //Add the block terrain to a node
    terrainNode = new Node("terrainNode");
    terrainNode.addControl(blockTerrain);
    rootNode.attachChild(terrainNode);

    /*

    //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);
    blockTerrain.setBlock(0, 0, 0, Block_Grass.class); //For the lazy users :P

    //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);
    
}

/** Declaring the “Shoot” action and mapping to its triggers. /
private void initKeys() {
inputManager.addMapping(“Shoot”,
new KeyTrigger(KeyInput.KEY_SPACE), // trigger 1: spacebar
new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); // trigger 2: left-button click
inputManager.addListener(actionListener, “Shoot”);
}
/
* Defining the “Shoot” action: Determine what was hit and how to respond. */
private ActionListener actionListener = new ActionListener() {

public void onAction(String name, boolean keyPressed, float tpf) {
  if (name.equals("Shoot") && !keyPressed) {
    /*
      // 1. Reset results list.
    CollisionResults results = new CollisionResults();
    // 2. Aim the ray from cam loc to cam direction.
    Ray ray = new Ray(cam.getLocation(), cam.getDirection());
    // 3. Collect intersections between Ray and Shootables in results list.
    rootNode.collideWith(ray, results);
    // 4. Print the results
    System.out.println("----- Collisions? " + results.size() + "-----");
    for (int i = 0; i < results.size(); i++) {
      // For each hit, we know distance, impact point, name of geometry.
      float dist = results.getCollision(i).getDistance();
      Vector3f pt = results.getCollision(i).getContactPoint();
      String hit = results.getCollision(i).getGeometry().getName();
      System.out.println("* Collision #" + i);
      System.out.println("  You shot " + hit + " at " + pt + ", " + dist + " wu away.");
    }
    // 5. Use the results (we mark the hit object)
    if (results.size() > 0) {
      // The closest collision point is what was truly hit:
      CollisionResult closest = results.getClosestCollision();
      // Let's interact - we mark the hit with a red dot.
      mark.setLocalTranslation(closest.getContactPoint());
      terrainNode.attachChild(mark); //attach to terrainNode instead of rootNode
    } else {
      // No hits? Then remove the red mark.
      rootNode.detachChild(mark);
    }
    */
    //Get the free block gap, to which the user is loooking...
    Vector3Int blockLocation = getCurrentPointedBlockLocation(true);
    //(The block location is null, if the user looks in the sky or out of the map)
    if(blockLocation != null){
        //... and place a block there :)
        blockTerrain.setBlock(blockLocation, Block_Wood.class);
    }
    
  }
}

};

/** A centred plus sign to help the player aim. */
protected void initCrossHairs() {
setDisplayStatView(false);
guiFont = assetManager.loadFont(“Interface/Fonts/Default.fnt”);
BitmapText ch = new BitmapText(guiFont, false);
ch.setSize(guiFont.getCharSet().getRenderedSize() * 2);
ch.setText("+"); // crosshairs
ch.setLocalTranslation( // center
settings.getWidth() / 2 - ch.getLineWidth()/2, settings.getHeight() / 2 + ch.getLineHeight()/2, 0);
guiNode.attachChild(ch);
}

private Geometry mark;
/** A red ball that marks the last spot that was “hit” by the “shot”. */
protected void initMark() {
Sphere sphere = new Sphere(30, 30, 0.2f);
mark = new Geometry(“BOOM!”, sphere);
Material mark_mat = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
mark_mat.setColor(“Color”, ColorRGBA.Red);
mark.setMaterial(mark_mat);
}

//“returns the CollisionResults of the mouse cursor and a specified node”
private CollisionResults getRayCastingResults(Node node){
Vector3f origin = cam.getWorldCoordinates(new Vector2f((settings.getWidth() / 2), (settings.getHeight() / 2)), 0.0f);
Vector3f direction = cam.getWorldCoordinates(new Vector2f((settings.getWidth() / 2), (settings.getHeight() / 2)), 0.3f);
direction.subtractLocal(origin).normalizeLocal();
Ray ray = new Ray(origin, direction);
CollisionResults results = new CollisionResults();
node.collideWith(ray, results);
return results;
}
//" use the closest CollisionResult to receive the collisionContactPoint with
//your BlockTerrain and to finally get the “pointed” block location"
private Vector3Int getCurrentPointedBlockLocation(boolean getNeighborLocation){
CollisionResults results = getRayCastingResults(terrainNode);
if(results.size() > 0){
Vector3f collisionContactPoint = results.getClosestCollision().getContactPoint();
return BlockNavigator.getPointedBlockLocation(blockTerrain, collisionContactPoint, getNeighborLocation);
}
return null;
}

}[/java]

Could give you some direction in how I made my chunk manager, I use threading for loading and unloading chunks, its still in testing but runs very smoothly, casting a ray every second to grab the chunk below the player and then run this little function.

[java]private List<Vector3Int> generator(Vector3Int location, Vector3Int size) {
List<Vector3Int> points = new ArrayList<>();

    int width = size.getX(), height = size.getY(), length = size.getZ();
    for (int x = width - width * 2; x &lt;= width; x++) {
        for (int y = 0; y &lt; height; y++) {
            for (int z = length - length * 2; z &lt;= length; z++) {
                points.add(location.add(new Vector3Int(x, y, z)));
            }
        }
    }

    return points;
}[/java]

after this I compare the chunks loaded with the chunks to load, and remove the unwanted chunks to be loaded into the terrain.

[java]if (loaded.removeAll(loading)) {
// Code here
}[/java]

@lawsy said: Could give you some direction in how I made my chunk manager, I use threading for loading and unloading chunks, its still in testing but runs very smoothly, casting a ray every second to grab the chunk below the player and then run this little function.

[java]private List<Vector3Int> generator(Vector3Int location, Vector3Int size) {
List<Vector3Int> points = new ArrayList<>();

    int width = size.getX(), height = size.getY(), length = size.getZ();
    for (int x = width - width * 2; x &lt;= width; x++) {
        for (int y = 0; y &lt; height; y++) {
            for (int z = length - length * 2; z &lt;= length; z++) {
                points.add(location.add(new Vector3Int(x, y, z)));
            }
        }
    }

    return points;
}[/java]

after this I compare the chunks loaded with the chunks to load, and remove the unwanted chunks to be loaded into the terrain.

[java]if (loaded.removeAll(loading)) {
// Code here
}[/java]

Are you just using the BlockChunkControl class?

@echospot said: Are you just using the BlockChunkControl class?

No, I rewritten the BlockChunkControl class, for many reason but mostly cause rendering with greedy meshing, what you could do is write a couple of methods that adds and remove the chunks in the BlockChunkControl, have only one BlockChunkControl for your world that manages all the chunks in the array, then use a function like what I posted that manages how many Chunks are loaded around a player.

Little more info on the chunk manager, generate the chunks to be loaded from around the player using a function like this, after you have your chunk list, load all the chunks from the list that isn’t already loaded.

[java]private List<Vector3Int> generator(Vector3Int location, Vector3Int size) {
List<Vector3Int> points = new ArrayList<>();

    int width = size.getX(), height = size.getY(), length = size.getZ();
    for (int x = width - width * 2; x &lt;= width; x++) {
        for (int y = 0; y &lt; height; y++) {
            for (int z = length - length * 2; z &lt;= length; z++) {
                points.add(location.add(x, y, z));
            }
        }
    }

    return points;
}[/java]

Before loading the new chunks, cross reference the list with the chunks already loaded, remove what chunks are remaining from the list, these are the chunks that are now out of range and should be unloaded or what ever you wish to do, example: I cache these chunks for removal just in case a player returns to the previous chunk.

[java]private void removeChunkBlocks(final List<Vector3Int> loading, final Vector3Int location) {
location.setY(0);
List<Vector3Int> loaded = generator(location.clone(), size);
if (loaded.removeAll(loading)) {
chunkControl.removeChunks(loaded);
}
}[/java]

Does anyone know how I would add borders to a block? What I want to do is make thick lines around all the edges of a block when I highlight it. I have the block detection portion done, I just don’t know how to “highlight” the block. Here’s a cheesy paint mockup of what I’m imagining:

Thanks :slight_smile:

Easiest way is to make a cube with a texture of the outline, and put it over whatever block you’re highlighting. It can just intersect the terrain. Scale it up just a bit so it doesn’t get Z-fighting problems.

1 Like
@Dekker3D said: Easiest way is to make a cube with a texture of the outline, and put it over whatever block you're highlighting. It can just intersect the terrain. Scale it up just a bit so it doesn't get Z-fighting problems.

Cool, thanks. :slight_smile: