(July 2020) Monthly WIP Screenshot Thread

Well, not much to show as I am still working on engine related stuff, but my OutsideEngine project is going well.
I built a simple loading screen app state for it this morning while drinking my coffee:

For anyone interested, here is the code, very portable.

package io.tlf.outside.client.appstate;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.BaseAppState;
import com.jme3.font.BitmapFont;
import com.jme3.font.BitmapText;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.jme3.ui.Picture;
import io.tlf.outside.client.Config;

public class LoadingScreen extends BaseAppState {
    private Picture pic;
    private volatile SimpleApplication sapp;
    private BitmapFont font;
    private Geometry pane;
    private Geometry bar;
    private BitmapText statusLabel;
    private volatile int prog = 0;
    private volatile String msg = "Loading...";
    private float originalPaneX;
    private float originalPaneY;
    private float originalBarX;

    @Override
    protected void initialize(Application app) {
        sapp = (SimpleApplication) app;
        //Build background image
        pic = new Picture("load-pic");
        pic.setImage(app.getAssetManager(), Config.LOADING_IMAGE, false);
        pic.setWidth(app.getCamera().getWidth());
        pic.setHeight(app.getCamera().getHeight());
        pic.setLocalTranslation(0, 0, 1);
        //Build Loading Bar
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", new ColorRGBA(0.5f, 0.5f, 0.5f, 0.5f));
        mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
        originalPaneX = app.getCamera().getWidth();
        originalPaneY = app.getCamera().getHeight() / 8;
        pane = new Geometry("load-pane", new Quad(originalPaneX, originalPaneY));
        pane.setMaterial(mat);
        pane.setLocalTranslation(0, 0, 2);

        Material matBar = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
        matBar.setColor("Color", new ColorRGBA(0.9f, 0.9f, 0.9f, 0.8f));
        matBar.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
        originalBarX = app.getCamera().getWidth();
        bar = new Geometry("load-bar", new Quad(originalBarX, 10f));
        bar.setMaterial(matBar);
        bar.setLocalTranslation(0, 0f + ((app.getCamera().getHeight() / 8f) * .20f), 3f);

        //Build Text
        font = app.getAssetManager().loadFont("Interface/Fonts/Default.fnt");
        statusLabel = new BitmapText(font);
        statusLabel.setSize(20f);
        statusLabel.setText("Loading...");
        sapp.getGuiNode().attachChild(statusLabel);
        statusLabel.setLocalTranslation((float) (app.getCamera().getWidth() / 2), 0f + (float) ((app.getCamera().getHeight() / 8f) * .80), 4f);
    }

    public void setProgress(String text, int prog) {
        if (sapp == null) {
            return;
        }
        msg = text;
        this.prog = prog;
    }

    @Override
    protected void cleanup(Application app) {

    }

    @Override
    public void update(float tpf) {
        statusLabel.setLocalTranslation((float) (sapp.getCamera().getWidth() / 2f) - (statusLabel.getLineWidth() / 2), 0f + (float) ((sapp.getCamera().getHeight() / 8f) * .80), statusLabel.getLocalTranslation().z);
        statusLabel.setText(msg);
        bar.setLocalTranslation(
                (0f - ((float) sapp.getCamera().getWidth() * ((100f - (float) prog) / 100f))), //RIGHT/LEFT
                0f + ((sapp.getCamera().getHeight() / 8f) * .20f), //UP/DOWN
                bar.getLocalTranslation().z
        );
        pic.setWidth(sapp.getCamera().getWidth());
        pic.setHeight(sapp.getCamera().getHeight());
        pic.setLocalTranslation(0, 0, 1);
        float newPaneY = sapp.getCamera().getHeight() / 8;
        pane.setLocalScale(sapp.getCamera().getWidth() / originalPaneX, newPaneY / originalPaneY, 1);
        bar.setLocalScale(sapp.getCamera().getWidth() / originalBarX, 1, 1);
    }

    @Override
    protected void onEnable() {
        sapp.getGuiNode().attachChild(pic);
        sapp.getGuiNode().attachChild(statusLabel);
        sapp.getGuiNode().attachChild(pane);
        sapp.getGuiNode().attachChild(bar);
    }

    @Override
    protected void onDisable() {
        sapp.getGuiNode().detachChild(pic);
        sapp.getGuiNode().detachChild(statusLabel);
        sapp.getGuiNode().detachChild(pane);
        sapp.getGuiNode().detachChild(bar);
    }
}

EDIT: Code edited to handle window resize, and to be non-blocking.

6 Likes

@tlf30 I am interested to know more about your OutsideEngine.

Is it designed to fit into specific game genera? i.e RPG, FPS,…
Is it ES based?
Has it built-in game mechanics? i.e for lobby, combat, story, crafting, quest, dialogs, terrain…

Do you also have a master server? if so what are the features?

I am interested as I am also working on an own engine specifically designing for Multiplayer RPG.

@Ali_RS it is a MMO RPG engine. It uses an in-house component engine I designed. Networking is done with Netty.io using concurrent TCP and UDP channels to each client. The server uses a postgres database. Each server can host one or more worlds, servers can be clustered to allow world to fail-over between them, and allow clients to ‘jump’ between worlds on different servers. The client uses JavaFX for the UI. Game assets are hosted on a HTTP server that is embedded in each game server using NanoHttpd. The game world by default is a isosurface transvoxel terrain, but the engine supports height map terrains as well, or any custom terrain by using scripts.

The engine has a built in system for quests, items, world objects, dialogs, crafting, trading, character creator, world editor, etc, that is fully extensible using groovy scripts. Everything can be edited at runtime from the client by a game master. Any edits are broadcast out to clients immediately. All game assets are managed by the servers, which allows the server to dynamically generate collision meshes, and nav meshes.

If you have any questions, feel free to PM me and we can discuss the implementation details of the engine.

4 Likes

That’s cool, really. :+1:

Clustering between game servers is yet a dark area for me. :sweat_smile:

Can clients host a game server or you are going to host them only on your VPSs?

We are planning on releasing the engine, and letting the game developer decide. It is setup so that the server can be run in the background of the client to allow for single player or LAN. When the server is hosted in the background of the client, the postgres server interface is disabled, and the engine saves data to and from files on disk. The http asset server is disabled, along with several other engine components. A game developer can choose to keep these enabled or not, but recommended use is to keep them disabled.

2 Likes

Been toying with the idea of creating “non-infinite” worlds of huge sizes that don’t page terrain (though they do page other things like buildings and detail) - a lot like Ark or Red Dead, etc…

The image shows a 16x16 + 1 grid of 128x128 cells (4224 wu in size) with some crazy cool LOD at just shy of 1000 fps. You can see my target LODs in the code behind the game window - with the most aggressive LOD hitting just 5% of the full-res meshes.

It’s not the hugest world I’ve ever seen, but not bad I guess.

11 Likes

That is really cool. Is it a height map based terrain?

Yes this is heightmap-based, but the LOD works for any terrain that uses world coordinates as texture coords - i.e. triplanar mapping.

It’s just the SimplifyMesh class that does the LOD. The trick is memory management. I save each mesh (the full-res mesh and each LOD individually) as seperate files using JMEs BinaryExporter and load the mesh I want based on simple cell-distance using the BinaryImporter (threaded to avoid stuttering).

You can actually get distances a lot further than I did in that screenshot. In that instance I loaded all the LODs at once and used CullHint to show/hide the lods (and quickly ran out of memory). If you just load the LOD you need it’s a lot more economical.

4 Likes

Just a quick video of the test application I was using for dynamic object attachments.

Lots of tricky things to get working including parent/child relationships through SimEthereal and all of the interesting ECS component bits to go with that. Fixed a few bugs and limitations in the physics engine while I was in there, too.

This stuff has been kicking my butt for two months so I’m happy to have it all working this well now.

Edit: in case there are still folks here who don’t know what MOSS is… a handy link: https://www.patreon.com/posts/moss-initiative-25193330

16 Likes

A tentative game cover.

This is the most complex drawing I’ve ever made.

7 Likes

Marching Squares! A pretty cool algorithm to generate 2D worlds like Terraria, Binding of Isaac, etc. I’m trying to gather as many mesh generation techniques as I can to publish a mesh library, and this is one of them.

14 Likes

Cool :slightly_smiling_face:

Yes, it is an amazing algorithm, I got to know about Marching Squares last week from this cool video.

1 Like

Looks cool! Can probably use this in my game at some point to auto-generate maps.

This is the latest from my game. Getting (MOSS, through patroenage) block physics working with 2d-tile graphics

9 Likes

It’s been fun watching this one develop. You ended up using a lot more of MOSS than I thought you originally would. Cool to see the progress. :slight_smile:

1 Like

@asser_fahrenholz does client load the blocks data from server through the network?

Perhaps @pspeed can better explain than I, but yes. The physics is kept server side, only the block types/values (Integer) + location is sent through to client (probably more magic going on than I perceive) , and from there the client can generate the visuals. Client-side the visuals load only those chunks need and server-side, physics is kept performant by only calculating physics for the chunks that are active plus surrounding chunks (that is, for those where something is dynamic). The faces of the blocks are calculated to let physics know which sides are solid and which are not.

1 Like

And there are ways to load pre defined block shapes from file

1 Like

I’ve published what I have right now to git. The repository isn’t finished, and some classes may change, but there is a test case for all implementations in the test folder. There’s also a TestSimplifyMesh that shows the simplification algorithm in action for all mesh types. It’s very effective for marching squares as it doesn’t affect the edges.

7 Likes

Squeaking this one in on the last day of the month. As a birthday present to myself, I took some time off day job to play with using combined normal maps for beveling block edges.



In the last pic, you can see the bevel normal maps applied to a regular blue cube. I’ve left the corners unsmoothed for now as this is just a prototype and I need to see if it’s even feasible.

Visually, I think it’s ok. I suspect it will look really bad with parallax maps but maybe not. I’ll try that next time I have a few moments. (Edit: and to be honest, I’ve been a little sour on bumpmaps lately anyway. Without steep parallax, they look bad up close from edge-on angles… and sometimes even with steep parallax. And from far away, normal maps take over the job almost completely. So bumps are not super important to me right now.)

The most important thing will be putting this into an atlas texture with the 16 different edge type combinations and seeing if that still works ok. If I can’t pick a bevel combination with a single byte vertex attribute then it’s maybe not worth the trouble.

14 Likes