Cubes – A Block World Framework [Update Preview]

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

“Hi, I haven’t read anything about the integration but I’d be glad to offer detailed opinions.”

Thanks for the input. I will continue to use SVN to commit changes to JME.

https://code.google.com/p/cubesmod/

Is the new project page. More accurately, https://code.google.com/p/cubesmod/source/browse/modified%20cubes.zip
is where the modifications can be found. Clicking the “view raw file” link should cause it to download…

@echospot said: Just make a Dropbox acct. and upload the zip to your public files :D

I’ve had some bad experiences with dropbox… I’d… shudder… rather not talk about it.

@echospot @PewPewKapowie managed to figure that out on our game...somehow. I'd ask him.
Lol... whenever there's something I can steal from your project, the other one has it. I'll check with him, see if he has any advice.
@echospot said: 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).

This is most likely how I’ll end up handling it, for my own peace of mind. I’d like to fix the inherent issue, though, just to make sure failing to fix it bites me in the butt later.

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

:explode: :explode: :explode:

This all sounds like good stuff, but I think I have to read through that again after a night’s sleep. Get back to you then! :wink:

Changelog: -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.

Hey mate, if your using a greedy mesher for your voxel world, my way in how to fix the grass tops was to add a function to the block skin class and then override the block registry skin, keep track of what blocks are on the surface and pass through the Boolean, and what quad face the mesher is currently meshing.

[java]public class BlockSkin {

private BlockSkinType[] coords;

public BlockSkin(BlockSkinType textureLocation) {
    this(new BlockSkinType[]{textureLocation});
}

public BlockSkin(BlockSkinType[] coords) {
    this.coords = coords;
}

public BlockSkinType getTextureLocation(BLOCK_FACE face) {
    return coords[getTextureLocationIndex(face)];
}

public BlockSkinType getTextureLocation(BLOCK_FACE face, boolean surface) {
    return coords[getTextureLocationIndex(face, surface)];
}

protected int getTextureLocationIndex(BLOCK_FACE face, boolean surface) {
    return getTextureLocationIndex(face);
}

public BlockSkinType getTextureLocation(Chunk chunk, Vector3Int blockLocation, BLOCK_FACE face) {
    return coords[getTextureLocationIndex(chunk, blockLocation, face)];
}

protected int getTextureLocationIndex(Chunk chunk, Vector3Int blockLocation, BLOCK_FACE face) {
    return getTextureLocationIndex(face);
}

protected int getTextureLocationIndex(BLOCK_FACE face) {
    if (coords.length == 6) {
        return face.ordinal();
    }
    return 0;
}

}[/java]

   [java]BlockManager.getInstance().register(Block_Grass.name, Block_Grass.class, new BlockSkin(new BlockSkinType[]{
        new BlockSkinType(1, 1),
        new BlockSkinType(3, 1),
        new BlockSkinType(5, 1)}) {
        @Override
        protected int getTextureLocationIndex(BLOCK_FACE face, boolean surface) {
            if (surface) {
                switch (face) {
                    case TOP:
                        return 0;
                    case BOTTOM:
                        return 2;
                    default:
                        return 1;
                }
            }
            return 2;
        }

        @Override
        protected int getTextureLocationIndex(Chunk chunk, Vector3Int blockLocation, BLOCK_FACE face) {
            if (chunk.isBlockOnSurface(blockLocation)) {
                switch (face) {
                    case TOP:
                        return 0;
                    case BOTTOM:
                        return 2;
                    default:
                        return 1;
                }
            }
            return 2;
        }
    });

[/java]

I think that I’ve successfully implemented a better multithreading, as @echospot recommended; However, instead of getting freeze-frame lag every time I generate more of the surrounding area, I get general jitteryness and an FPS drop when I’m adding chunks.

Do you guys think that implementing the greedy mesher will solve this, or does this sound like bad implementation of multithreading? I don’t have enough experience to tell.

@lawsy: as much as I would like to immediately follow your advice tofix the problem, I think I’ll need to implement the greedy mesher first. ; )

@ulfgur said: I think that I've successfully implemented a better multithreading, as @echospot recommended; However, instead of getting freeze-frame lag every time I generate more of the surrounding area, I get general jitteryness and an FPS drop when I'm adding chunks.

Do you guys think that implementing the greedy mesher will solve this, or does this sound like bad implementation of multithreading? I don’t have enough experience to tell.

@lawsy: as much as I would like to immediately follow your advice tofix the problem, I think I’ll need to implement the greedy mesher first. ; )

Our multithreader had a noticeable amount of lag until we implemented the greedy mesher. Now there’s no lag at all :slight_smile:

1 Like

Also, if you aren’t already, only add one chunk per frame.

3 Likes
@pspeed said: Also, if you aren't already, only add one chunk per frame.

Does this make a big difference? We aren’t doing it this way but it sounds like it would help, I think I will try it out!

@PewPewKapowie said: Does this make a big difference? We aren't doing it this way but it sounds like it would help, I think I will try it out!

Makes a HUGE difference.

This is why you can fly around the IsoSurface demo with barely a frame drop. Every new piece of geometry, etc. will stall the pipeline to the graphics card a bit. The more you try to throw into one frame, the longer it stalls.

2 Likes

I used a LinkedBlockingQueue allocated with a dedicate thread for loading and generate chunks, which also calculates the lighting, water, and smoke (cellular automata), after the cycle is finished, then added to the chunk manager node, very fast and smooth loading.

My problem with multithreading is determining what can be loaded in another thread and what has to be loaded in the main thread. I know you should only render in the main thread, but it’s such a blurred line for me, I’m not sure what counts as rendering. What I currently do is calculate all the block types using a terrain generator and then in the main thread I apply the block types to a new BlockChunkControl. I feel like there’s a lot more that could be done in the loader thread but I’m not sure what.

@PewPewKapowie said: My problem with multithreading is determining what can be loaded in another thread and what has to be loaded in the main thread. I know you should only render in the main thread, but it's such a blurred line for me, I'm not sure what counts as rendering. What I currently do is calculate all the block types using a terrain generator and then in the main thread I apply the block types to a new BlockChunkControl. I feel like there's a lot more that could be done in the loader thread but I'm not sure what.

Without knowing your design or code, forgive me if your already doing so, just throwing it out there, when you attaching the chunk to the main node, are you using app.enqueue?

[java]app.enqueue(new Callable<Spatial>() {
@Override
public Spatial call() throws Exception {
// Added node attaching code
return null;
}
});[/java]

Personally for my voxel engine only using one thread, and that is the chunk generating and loading thread.

1 Like
@lawsy said: Without knowing your design or code, forgive me if your already doing so, just throwing it out there, when you attaching the chunk to the main node, are you using app.enqueue?

[java]app.enqueue(new Callable<Spatial>() {
@Override
public Spatial call() throws Exception {
// Added node attaching code
return null;
}
});[/java]

Personally for my voxel engine only using one thread, and that is the chunk generating and loading thread.

I use a callable for the block types, but not for the spatials, I didn’t know you could do that! Thanks! Sorry I couldn’t post any of my code, I’m on my phone right now, away from my computer.

@lawsy said: Without knowing your design or code, forgive me if your already doing so, just throwing it out there, when you attaching the chunk to the main node, are you using app.enqueue?

[java]app.enqueue(new Callable<Spatial>() {
@Override
public Spatial call() throws Exception {
// Added node attaching code
return null;
}
});[/java]

Personally for my voxel engine only using one thread, and that is the chunk generating and loading thread.

My bad, sorry I use a future task for generating noise then using a dedicated thread for loading and calculations, needed a future task when generating noise chunks so I have all the chunk data needed for calculations.

@PewPewKapowie said: My problem with multithreading is determining what can be loaded in another thread and what has to be loaded in the main thread. I know you should only render in the main thread, but it's such a blurred line for me, I'm not sure what counts as rendering.

AFAIK that’s easy to decide: If it reads or modifies the scene graph, it needs to be in the render thread.
Things might get blurred if different people consider different threads as “the main thread”.
The other blurry line might be the question what parts of JME access the scene graph. The safe assumption is that any API call of JME does, documentation or a single-stepping session in the debugger can show otherwise.

@PewPewKapowie said: Our multithreader had a noticeable amount of lag until we implemented the greedy mesher. Now there's no lag at all :)
OK, then, time to try to implement the greedy mesher...
@pspeed said: Also, if you aren't already, only add one chunk per frame.
Ha! I was going to build a system that could add more than one chunk per frame, but decided it would be too complicated for my third try at multithreading. Yaay accidental competence!

Aaaand that’s good to know for future projects. Thanks.

@lawsy - I’d use some form of a concurrent queue rather than a blocking one. Probably not a huge issue but IO doesn’t need to be completely synchronized, just eventually synchronized, so if you use a concurrent collection it allows for adding to the collection while items are being removed preventing any kind of hiccup if another thread is processing during that time. This may not be an issue at all though, but I thought it was worth mentioning.

1 Like

I have the following problem popping up when I try to use sleaker’s system:

diamond operator is not supported in -source 1.5
(use -source 7 or higher to enable diamond operator)

I tried updating my JDK, but that didn’t fix it. Any ideas?

@ulfgur said: I have the following problem popping up when I try to use sleaker's system:

diamond operator is not supported in -source 1.5
(use -source 7 or higher to enable diamond operator)

I tried updating my JDK, but that didn’t fix it. Any ideas?

Sounds like somewhere you have some empty diamond operators, maybe like this:

HashMap<Vector3Int, BlockChunkControl> map = new HashMap<>();

You need to fill that with whatever your HashMap will be containing, like:

HashMap<Vector3Int, BlockChunkControl> map = new HashMap<Vector3Int, BlockChunkControl>();

Java 7 can infer the type arguments, so you can leave it empty. You seem to be using an older compilance level.

@ulfgur said: I have the following problem popping up when I try to use sleaker's system:

diamond operator is not supported in -source 1.5
(use -source 7 or higher to enable diamond operator)

I tried updating my JDK, but that didn’t fix it. Any ideas?

Did you also set the source level in your project properties? Sounds like you’ve just left it at the old default.

@sleaker said: @lawsy - I'd use some form of a concurrent queue rather than a blocking one. Probably not a huge issue but IO doesn't need to be completely synchronized, just eventually synchronized, so if you use a concurrent collection it allows for adding to the collection while items are being removed preventing any kind of hiccup if another thread is processing during that time. This may not be an issue at all though, but I thought it was worth mentioning.

Hey mate, thank you for the advice, still a noob with JAVA and adjusting to the JAVA referencing took a little of getting use too :wink: at times doesn’t feels right but JAVA referencing is really fast, and now I understand how it all jells together, its all good :slight_smile:

So far I am liking the concurrent blocking queue, due to the sleeping of the thread when it has finished its work load and the order of execution, however I am currently hunting down a bug with my cellular automata lighting and neighbouring chunks, does appear to be like what you have commented about, and if the case either I drop the blocking queue or modified my code.

Example of the concurrent blocking queue
[java] private BlockingQueue<Chunk> load = new LinkedBlockingQueue<>();
protected Runnable loading = new Runnable() {
@Override
public void run() {
while (!(Thread.currentThread().isInterrupted())) {
try {
this.process(load.take());
} catch (InterruptedException ex) {
// Set interrupted flag.
Thread.currentThread().interrupt();
Logger.getLogger(this.getClass().getName()).log(Level.WARNING,
String.format("%nThread loader interrupted exception:%n%s", ex.getMessage()));
}
}

        // Thread is getting ready to die, but first,
        // drain remaining elements on the queue and process them.
        final LinkedList&lt;Chunk&gt; remaining = new LinkedList&lt;&gt;();
        load.drainTo(remaining);
        for (Iterator&lt;Chunk&gt; it = remaining.iterator(); it.hasNext();) {
            Chunk chunk = it.next();
            if (chunk == null) {
                continue;
            }

            this.process(chunk);
            Logger.getLogger(this.getClass().getName()).log(Level.INFO,
                    String.format("%nThread loader processing:%n%s", chunk.toString()));
        }
    }

    private void process(final Chunk chunk) {
        if (!chunk.isEnabled()) {
            chunk.setEnabled(ChunkNeighbourhood.valueOf(chunk).isNeighbourhood());
        }

        if (chunk.isEnabled()) {
            long end, start;
            start = System.nanoTime();

            final ChunkRenderPipeline render = ChunkRenderPipeline.valueOf(chunk);

            end = System.nanoTime();
            Logger.getLogger(this.getClass().getName()).log(Level.INFO,
                    String.format("Chunk %s%nGeneration time: %s: milliseconds",
                    chunk.toString(), formatter.format((end - start) / 1000000f)));

            app.enqueue(new Callable&lt;Spatial&gt;() {
                @Override
                public Spatial call() throws Exception {
                    chunk.updateSpatial(render.getRender());

                    if (!((Node) spatial).hasChild(chunk)) {
                        ((Node) spatial).attachChild(chunk);

                        chunk.setLoaded(true);
                        BLOCKS_LOADED += chunk.getBlockCount();
                    }

                    for (int i = 0; i &lt; chunkListeners.size(); i++) {
                        ChunkListener blockTerrainListener = chunkListeners.get(i);
                        blockTerrainListener.onSpatialUpdated(chunk);
                    }

                    return null;
                }
            });
        }
    }
};[/java]