(February 2021) Monthly WIP Screenshot Thread

Hi

The last few days I was busy working on my map generator. The shorelines were looking so noisy from close up so added a simple edge smoother algorithm to remove noisy edges.

Here is how it looks like before and after smoothing.

With Smoothness Step = 0

With Smoothness Step = 5

Island is empty for now, soon going to post one full of trees and flowers. :slightly_smiling_face:

7 Likes

And here it is, my beautiful island! :slightly_smiling_face:

https://i.imgur.com/hZV9Uvt.png

https://i.imgur.com/BqNMstg.png

https://i.imgur.com/q6gdvlq.png

https://i.imgur.com/XuM7aGO.png

https://i.imgur.com/2aQmVfW.png

https://i.imgur.com/SfjGndH.png

https://i.imgur.com/U16SLlp.png

https://i.imgur.com/e5ib6DU.png

https://i.imgur.com/NtB57iW.png

https://i.imgur.com/7MbqHuT.png

https://i.imgur.com/eQ1kTV7.png

https://i.imgur.com/VqFhf17.png

19 Likes

@Ali_RS
Looks really cool - But just making sure, I recognized some of the models (trees, mushrooms, etc.) in the scene, they are commercial ones from Meshtint Studio - Top Down Fantasy Forest Pack | FREE 3D Model | Tutorial | Learn Unity | Art Outsource (20 USD), that I also use in my apps. I assume you bought them, but in case you downloaded them from somewhere else for free, I wanted to notice it, because their license forbids them to be used if not bought.

Anyways, cool island! :+1:

2 Likes

Hi @destroflyer, thank you. :slightly_smiling_face:

Yes, they are commercial but they are not from Meshtint Studio.

I use this

3 Likes

@Ali_RS
Oh wow, haha :smiley: You’re right - My bad then, sorry for the confusion.
Just wanted to give a heads up, because it happened to me once that i built a game around a character and then later noticed, it was a commercial model from an already existing game… :smiley:

Good luck with your project!

5 Likes

The recent progress has added real-time shadows, fastBloom. On Android, the frame rate is 60 in most cases, but sometimes the frame rate is reduced. On Android, DetailedProfilerState does not seem to work properly.
My game has 4 working threads: rendering thread, physics thread, AI thread, audio thread. On my mobile phone, after running the game for a period of time, the mobile phone becomes hot, which seems to be caused by the excessive workload of the cpu or gpu. I made a lot of optimizations to ensure that the number of objects rendered per frame does not exceed 50, and the number of vertices rendered per frame does not exceed 20,000, which does not actually take up too much GPU on the phone. Therefore, I think it is the physical thread or the AI thread or the audio thread that caused the CPU workload to be too large, which eventually caused the phone to heat up.

a4e19d8572c42e4c2b83d856e6bd8cc c2501a5b26889d0e1a276d9a71154a6

7 Likes

Nice work :slightly_smiling_face:, I wonder how you move your zombies , is it vector maths or ML ?

Each zombie is a physical character controller, using NavMesh to find a path, using state machines, decision-making and manipulation behaviors to control the zombies together, thereby simulating a good group attack.

2 Likes

Good job. I too would like to know more about the type of AI you have implemented for the zombies. Have you used something like Flocking Behaviour?

2 Likes

This is the same map but with a different terrain style (texture splatted) and a bit more cartoony water (notice the little flicking shiny dots) :wink:

https://i.imgur.com/7atq8J6.png

https://i.imgur.com/VaYYyak.png

10 Likes

I am based on Steering Behaviors and decision trees. Basically, GDX-AI can meet my needs. But I did not use GDX-AI. I modified and re-implemented the Sterring Behavior part based on Monkey Brain AI. :wink:

4 Likes

Working on a torch lit area.

4 Likes

I’ve been working on integrating knock back effects into my game’s combat system, and firstly have been playing around with the DynamicAnimControl and AnimComposer to see what’s possible.

So far I’ve just been testing some code in my own simple editing tool to simulate what the full knock back process would look like.

I’m using the DAC’s dyanmic mode to apply a random force for a half a second before going into ragdoll mode. Then once its done moving I have the NPC exit ragdoll mode and rotate back to its stomach while blending to a stand animation.

Overall it’s looking like things should work enough to start implementing this into my game now. The visual quality of the recovery process varies depending on the ragdolls unique position when its time for it to stand up, and the armature/spatial seems to start clipping through the ground as soon as it exits ragdoll mode, but I hope I can figure out how to fix that soon and can make the process look cleaner.

10 Likes

looks nice, happy you solved things :slight_smile:

tip: try have some fun with Cloth physics along with ragdolls/animations

2 Likes

Playing with some Kitbash assets in jme with some intense PBR lighting.
Getting 8 fps on my RTX Titan :sweat_smile:

This is running in the Outside Engine. You can see the server game/physics tick rate at the bottom.
0ms ping because the server is running locally on the network.

4 Likes

not sure why so low FPS, how many tris are visible / how many geoms there / etc?

1 Like

I think it has to do with a very unoptimized PBR setup:

package io.tlf.outside.client.lighting;

import com.jme3.app.Application;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.BaseAppState;
import com.jme3.environment.EnvironmentCamera;
import com.jme3.environment.LightProbeFactory;
import com.jme3.environment.generation.JobProgressAdapter;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.light.LightProbe;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.FXAAFilter;
import com.jme3.post.filters.LightScatteringFilter;
import com.jme3.post.filters.ToneMapFilter;
import com.jme3.post.ssao.SSAOFilter;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.shadow.DirectionalLightShadowFilter;
import com.jme3.shadow.EdgeFilteringMode;
import io.tlf.outside.world.JmeWorldAppState;

/**
 * @author Trevor Flynn trevorflynn@liquidcrystalstudios.com
 */
public class JmeLightingState extends BaseAppState {

    private DirectionalLight dl;
    private AmbientLight al;
    private SimpleApplication app;
    private FilterPostProcessor fpp;
    private EnvironmentCamera envCam;
    private LightProbe probe;
    private Node lightingNode;
    private volatile Spatial tracking = null;

    //Filters
    LightScatteringFilter godRays;
    DirectionalLightShadowFilter dlsf;
    FXAAFilter fxaa;
    SSAOFilter ssao;
    ToneMapFilter toneMap;

    Vector3f lightDir;

    private short renderSteps = 0;
    private float probeRadius = 100;
    private boolean probing = true;

    private Quality quality;

    public enum Quality {
        BRICK, LOW, MEDIUM, HIGH, SUPER
    }

    @Override
    protected void initialize(Application a) {
        app = (SimpleApplication) a;
        app.getViewPort().setBackgroundColor(ColorRGBA.White);
        JmeWorldAppState world = app.getStateManager().getState(JmeWorldAppState.class);
        if (world == null) {
            lightingNode = (Node) app.getRootNode().getChild("world"); //For iso-test
        } else {
            lightingNode = world.getGlobalNode();
        }

        //Lights
        al = new AmbientLight();
        al.setColor(ColorRGBA.White.mult(1.3f));
        lightingNode.addLight(al);

        dl = new DirectionalLight();
        lightDir = new Vector3f(-0.12f, -0.3729129f, 0.74847335f);
        dl.setDirection(lightDir);
        lightingNode.addLight(dl);
        dl.setColor(ColorRGBA.White);

        //Filter
        fpp = new FilterPostProcessor(app.getAssetManager());

        app.getViewPort().addProcessor(fpp);

        //Get quality setting
        String qual = app.getContext().getSettings().getString("Quality");
        boolean found = false;
        if (qual != null) {
            for (Quality quality : Quality.values()) {
                if (qual.equals(quality.name())) {
                    setQuality(quality);
                    found = true;
                }
            }
        }
        if (!found) {
            setQuality(Quality.SUPER);
        }

        //Env Cam
        envCam = new EnvironmentCamera(256, new Vector3f(0, 3f, 0));
        app.getStateManager().attach(envCam);
    }

    public Quality getQuality() {
        return quality;
    }

    public void setQuality(Quality quality) {
        this.quality = quality;
        app.getContext().getSettings().putString("Quality", quality.name());
        switch (quality) {
            case SUPER:
                //Clear
                fpp.removeAllFilters();

                //Directional light shadow
                dlsf = new DirectionalLightShadowFilter(app.getAssetManager(), 4096, 4);
                dlsf.setLight(dl);
                dlsf.setLambda(1f);
                dlsf.setShadowIntensity(0.4f);
                dlsf.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON);
                dlsf.setEnabled(true);

                //Godrays
                godRays = new LightScatteringFilter(lightDir.multLocal(-3000));
                godRays.setNbSamples(50);
                godRays.setBlurStart(-0.037999995f);
                godRays.setBlurWidth(0.8170011f);
                godRays.setLightDensity(0.36198944f);

                //FXAA
                fxaa = new FXAAFilter();

                //SSAO
                ssao = new SSAOFilter(5.1f, 1.2f, 0.2f, 0.1f);

                //Tone Map
                toneMap = new ToneMapFilter(Vector3f.UNIT_XYZ.mult(4.0f));

                //Add filters
                fpp.addFilter(fxaa);
                fpp.addFilter(toneMap);
                fpp.addFilter(dlsf);
                fpp.addFilter(godRays);
                fpp.addFilter(ssao);
                break;
            case HIGH:
                //Clear
                fpp.removeAllFilters();

                //Directional light shadow
                dlsf = new DirectionalLightShadowFilter(app.getAssetManager(), 2048, 4);
                dlsf.setLight(dl);
                dlsf.setLambda(1f);
                dlsf.setShadowIntensity(0.4f);
                dlsf.setEdgeFilteringMode(EdgeFilteringMode.PCFPOISSON);
                dlsf.setEnabled(true);

                //Godrays
                godRays = new LightScatteringFilter(lightDir.multLocal(-3000));
                godRays.setNbSamples(25);
                godRays.setBlurStart(-0.037999995f);
                godRays.setBlurWidth(0.8170011f);
                godRays.setLightDensity(0.36198944f);

                //FXAA
                fxaa = new FXAAFilter();

                //SSAO
                ssao = new SSAOFilter(5.1f, 1.2f, 0.2f, 0.1f);

                //Tone Map
                toneMap = new ToneMapFilter(Vector3f.UNIT_XYZ.mult(4.0f));

                //Add filters
                fpp.addFilter(fxaa);
                fpp.addFilter(toneMap);
                fpp.addFilter(dlsf);
                fpp.addFilter(godRays);
                fpp.addFilter(ssao);
                break;
            case MEDIUM:
                //Clear
                fpp.removeAllFilters();

                //Directional light shadow
                dlsf = new DirectionalLightShadowFilter(app.getAssetManager(), 2048, 2);
                dlsf.setLight(dl);
                dlsf.setLambda(1f);
                dlsf.setShadowIntensity(0.4f);
                dlsf.setEdgeFilteringMode(EdgeFilteringMode.Bilinear);
                dlsf.setEnabled(true);

                //Godrays
                godRays = null;

                //FXAA
                fxaa = new FXAAFilter();

                //SSAO
                ssao = new SSAOFilter(5.1f, 1.2f, 0.2f, 0.1f);
                ssao.setApproximateNormals(true);

                //Tone Map
                toneMap = new ToneMapFilter(Vector3f.UNIT_XYZ.mult(4.0f));

                //Add filters
                fpp.addFilter(fxaa);
                fpp.addFilter(toneMap);
                fpp.addFilter(dlsf);
                fpp.addFilter(ssao);
                break;
            case LOW:
                //Clear
                fpp.removeAllFilters();

                //Directional light shadow
                dlsf = new DirectionalLightShadowFilter(app.getAssetManager(), 1024, 1);
                dlsf.setLight(dl);
                dlsf.setLambda(1f);
                dlsf.setShadowIntensity(0.4f);
                dlsf.setEdgeFilteringMode(EdgeFilteringMode.Nearest);
                dlsf.setEnabled(true);

                //Godrays
                godRays = null;

                //FXAA
                fxaa = null;

                //SSAO
                ssao = null;

                //Tone Map
                toneMap = new ToneMapFilter(Vector3f.UNIT_XYZ.mult(4.0f));

                //Add filters
                fpp.addFilter(toneMap);
                fpp.addFilter(dlsf);
                break;
            case BRICK:
                //Clear
                fpp.removeAllFilters();

                //Directional light shadow
                dlsf = null;

                //Godrays
                godRays = null;

                //FXAA
                fxaa = null;

                //SSAO
                ssao = null;

                //Tone Map
                toneMap = null;
                break;
        }
    }

    @Override
    protected void cleanup(Application a) {
        app.getRootNode().removeLight(dl);
        app.getViewPort().removeProcessor(fpp);
        app.getStateManager().detach(envCam);
    }

    @Override
    public void update(float tpf) {
        if (probing) {
            renderSteps++;
            if (renderSteps == 2) { //Give the scene a frame to update
                lightingNode.removeFromParent();
                probe = LightProbeFactory.makeProbe(
                        app.getStateManager().getState(EnvironmentCamera.class),
                        app.getRootNode(),
                        new JobProgressAdapter<>() {
                            @Override
                            public void done(LightProbe result) {
                                //System.out.println("PBR Probe results in");
                            }
                        });
                probe.getArea().setRadius(probeRadius);
                app.getRootNode().addLight(probe);
            } else if (renderSteps > 10) {
                app.getRootNode().attachChild(lightingNode);
                probing = false;
                renderSteps = 0;
            }
        }
        if (probe != null && tracking != null) {
            probe.getArea().setCenter(tracking.getLocalTranslation());
        }
    }

    public void setTracking(Spatial spatial) {
        tracking = spatial;
    }

    public void reprobe() {
        probing = true;
    }

    public void setProbeLocation(Vector3f pos) {
        probe.setPosition(pos);
    }

    public float getProbeRadius() {
        return probeRadius;
    }

    public void setProbeRadius(float probeRadius) {
        this.probeRadius = probeRadius;
    }

    @Override
    protected void onEnable() {
        dl.setEnabled(true);
        envCam.setEnabled(true);
        reprobe();
        app.getViewPort().addProcessor(fpp);
    }

    @Override
    protected void onDisable() {
        dl.setEnabled(false);
        envCam.setEnabled(false);
        app.getViewPort().removeProcessor(fpp);
        if (probe != null) {
            app.getRootNode().removeLight(probe);
        }
    }

}
1 Like