[Solved] Bloom Filter applied to near view port does not create transparent background

That is too cool! Thank you! As usual, when I have a question you guys are right there with the answer. I was also wondering how to best achieve a glowing HUD on the GUI node and you inadvertently answered that question too lol

You are very welcome. The FilterPostProcessor in jME is highly useful, but it also tries to be sort of a jack of all trades so some trade offs are made in favor of versatility. That doesn’t mean certain things cannot be done, one just has to put in a little extra leg work if they want something a bit more specific to their application.

In my opinion one of jMEs strongest advantages over other engines is the fact that it’s open source. I find when I’m having trouble accomplishing something if I root around in the source code to see what’s being done and where then I can figure out how to do what I want to do.

So my suggestion to anyone using jME is to familiarize yourself with the source code and I think you’ll find that you can accomplish pretty much anything that isn’t explicitly supported out of the box.

And of course, as stated, the community here is always helpful too :slight_smile:

2 Likes

Hm…
I once had the idea to stack multiple frustums onto each other too.
Since I want to render such a scene with long Z distance.
It would be super awesome to have a standard solution for this in jME.
Currently all I’m looking for is a little source code example.
I think this is basic stuff and should become a ‘TestStackedFrustums.java’ demo.
:chimpanzee_smile:

I can post my multi frustum code here later on tonight. It’s pretty easy to do actually. I am still fixing the bloom filter code though. My frustums cover a total near/far range from 0.1f to 1e7f.

1 Like

Nice,

I could write the TestStackedFrustums.java and try to contribute it.
Your code would of course be acknowledged via the author tag.

It would also be a good opportunity for me to learn something about the jME contribution process.
Maybe I could write a little wiki page called “how to contribute things to the engine or SDK”.
:chimpanzee_smile:

1 Like

That wiki page can help teach me how to contribute my InstancedLinkedNode contribution as well :smile:

Okay, let’s make it so.
I need a little change from the font / text / unicode stuff I’m working on right now.
Seems like a perfect opportunity. :chimpanzee_smile:

1 Like

@Ogli All done. I am posting the MultiViewPortAppState code below. I have created access methods for each view port’s camera, viewport and attached FilterPostProcessor as well.

package com.massivefrontier.appstate;

import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppState;
import com.jme3.math.ColorRGBA;
import com.jme3.post.FilterPostProcessor;
import com.jme3.renderer.Camera;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial;

/**

  • This AppState creates 4 overlapping view ports in order to prevent z-order

  • fighting when you are rendering a very large scene. Four different {@link ViewPort}s

  • and {@link Camera}s are created with a separate {@link FilterPostProcessor} on each

  • {@link ViewPort}. The {@link Camera}s are all synchronized on the update loop.

  • @author Zissis Trabaris
    */
    public class MultiViewportAppState extends AbstractAppState {

    private final FilterPostProcessor veryNearFilter;
    private final FilterPostProcessor nearFilter;
    private final FilterPostProcessor farFilter;
    private final FilterPostProcessor veryFarFilter;

    private final Spatial scene;

    private final ViewPort veryNearViewPort;
    private final ViewPort nearViewPort;
    private final ViewPort farViewPort;
    private final ViewPort veryFarViewPort;

    private final Camera veryNearCam;
    private final Camera nearCam;
    private final Camera farCam;
    private final Camera veryFarCam;

    public Camera getVeryFarCam() {
    return veryFarCam;
    }

    public FilterPostProcessor getVeryFarFilter() {
    return veryFarFilter;
    }

    public ViewPort getVeryFarViewPort() {
    return veryFarViewPort;
    }

    public Camera getVeryNearCam() {
    return veryNearCam;
    }

    public FilterPostProcessor getVeryNearFilter() {
    return veryNearFilter;
    }

    public ViewPort getVeryNearViewPort() {
    return veryNearViewPort;
    }

    public FilterPostProcessor getFarFilter() {
    return farFilter;
    }

    public ViewPort getFarViewPort() {
    return farViewPort;
    }

    public FilterPostProcessor getNearFilter() {
    return nearFilter;
    }

    public ViewPort getNearViewPort() {
    return nearViewPort;
    }

    public Spatial getScene() {
    return scene;
    }

    public Camera getNearCam() {
    return nearCam;
    }

    public Camera getFarCam() {
    return farCam;
    }

    /**

    • Create a multi view {@link AppState} for the passed in
    • {@link SimpleApplication} and scene {@link Spatial}.
    • @param app
    •        the {@link SimpleApplication}
      
    • @param scene
    •        the {@link Spatial} that will be attached as a scene for all
      
    •        the view ports.
      

    */
    public MultiViewportAppState(SimpleApplication app, Spatial scene) {
    super();
    this.scene = scene;

     // The very far viewport and camera are the default viewport and camera
     // of the application
     veryFarViewPort = app.getViewPort();
     veryFarCam = app.getCamera();
     veryFarCam.setFrustumPerspective(40f, (float) veryFarCam.getWidth() / veryFarCam.getHeight(), 2000f, 1e7f);
    
     // Attach a filter post processor to the very far viewport
     veryFarFilter = new FilterPostProcessor();
     veryFarViewPort.addProcessor(veryFarFilter);
    
     // Set up the far viewport and camera
     farCam = this.veryFarCam.clone();
     farCam.setFrustumPerspective(40f, (float) veryFarCam.getWidth() / veryFarCam.getHeight(), 1000f, 2010f);
     farCam.setViewPort(0f, 1f, 0.0f, 1.0f);
     farViewPort = app.getRenderManager().createMainView("FarView", farCam);
     farViewPort.setBackgroundColor(ColorRGBA.BlackNoAlpha);
     farViewPort.setClearFlags(false, true, true);
     farViewPort.attachScene(scene);
    
     // Attach a filter post processor to the far viewport
     farFilter = new FilterPostProcessor();
     farViewPort.addProcessor(veryFarFilter);
    
     // Set up the near viewport and camera
     nearCam = this.veryFarCam.clone();
     nearCam.setFrustumPerspective(40f, (float) veryFarCam.getWidth() / veryFarCam.getHeight(), 400f, 1010f);
     nearCam.setViewPort(0f, 1f, 0.0f, 1.0f);
     nearViewPort = app.getRenderManager().createMainView("NearView", nearCam);
     nearViewPort.setBackgroundColor(ColorRGBA.BlackNoAlpha);
     nearViewPort.setClearFlags(false, true, true);
     nearViewPort.attachScene(scene);
    
     // Attach a filter post processor to the near viewport
     nearFilter = new FilterPostProcessor();
     nearViewPort.addProcessor(nearFilter);
    
     // Set up the very near viewport and camera
     veryNearCam = this.veryFarCam.clone();
     veryNearCam.setFrustumPerspective(40f, (float) veryFarCam.getWidth() / veryFarCam.getHeight(), 0.1f, 410f);
     veryNearCam.setViewPort(0f, 1f, 0.0f, 1.0f);
     veryNearViewPort = app.getRenderManager().createMainView("VeryNearView", veryNearCam);
     veryNearViewPort.setBackgroundColor(ColorRGBA.BlackNoAlpha);
     veryNearViewPort.setClearFlags(false, true, true);
     veryNearViewPort.attachScene(scene);
    
     // Attach a filter post processor to the very near viewport
     veryNearFilter = new FilterPostProcessor();
     veryNearViewPort.addProcessor(veryNearFilter);
    

    }

    @Override
    public void update(float tpf) {

     // Synchronize the cameras to the very far camera
     farCam.setLocation(veryFarCam.getLocation());
     farCam.setRotation(veryFarCam.getRotation());
     nearCam.setLocation(veryFarCam.getLocation());
     nearCam.setRotation(veryFarCam.getRotation());
     veryNearCam.setLocation(veryFarCam.getLocation());
     veryNearCam.setRotation(veryFarCam.getRotation());
    

    }

}

Maybe @Tryder can add his multi pass custom bloom with verying bloom intensities and then we can have a complete app state :slight_smile:

Okay, I will now start to write the TestXYZ.java to demonstrate the test case.
I already had a good idea how to demonstrate this yesterday. :chimpanzee_smile:

Just out of curiosity: Why is there always a 10f more (overlap) between the frustums?

The overlap is my cheesy way to prevent a line wrapping around a spatial when 1/2 the spatial is in one viewport and the other half is in another vierwport. You get a line artifact due to rounding errors if the frustums did not slightly overlap.

By the way, I thought of a cool test case. Create a spatial, drop in in the closest viewport. In the spatial’s update code, it can check to see what viewport it is in and report it on the display. Then let the user use the FlyByCam to move around the scene.

My idea is also simple:
have the main viewport split in two viewports: left half and right half.
left half uses the standard (1 frustum for the whole Z range)
right half uses your solution (N frustums over the whole Z range)

On the ground there will be a blue plane (= “water”) and some green spheres (=“islands”)
In the air there will be some spheres which are close to each other.
These geometries are intended to provoke Z-fights for the more distant Spatials.

Your idea is also good and will be added like that:
The spheres in the air will be rendered in different colors (red, orange, yellow, green, …).
This way the user will see what sub-frustum they are in…

1 Like

Here ya go I added in Bloom/Glow to your MultiViewportAppState class: MultiViewportAppState.java - Google Drive

I had to post it on Google Drive because the text was too long to post here :smirk:

2 Likes

@Tryder thanks a million!! @Ogli can you please use Tryder’s version and include test cases for his bloom code so that we can contribute a complete solution back to the community?

Great! Yes, I finally have time today. And will now implement it.
First trying to make it with jME 3.0 and then with jME 3.1 too.

1 Like

So here is the latest update:

The TestStackedFrustums.java is almost complete. I added both versions of your 2 AppState implementations (the simple version without glow is very short which is nice). The content of the Test application makes good progress and I created several cases where Z fighting plays a role. Also it has some fancy features like a sun light which moves according to the real sun position when seen from Earth.

For the contribution process I chose a different thing:
I modified one of the Test apps that is already there (TestPointSprite.java) and will try to commit the changes. So we get a first impression of how the contribution process is in that case.
Later I will of course add a description how the contribution of something new (like TestStackedFrustums.java) actually happens - so we cover that “contribute something new” also.

Here is the modified TestPointSprite.java which I will commit first:

/*
 * Copyright (c) 2009-2012 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package jme3test.effect;

import com.jme3.app.SimpleApplication;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type;
import com.jme3.effect.shapes.EmitterBoxShape;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;

public class TestPointSprite extends SimpleApplication {

    protected int particleAsset = 0;
    protected int numParticleAssets = 7;
    protected String textureName;
    protected int imagesX, imagesY;
    
    public static void main(String[] args){
        TestPointSprite app = new TestPointSprite();
        app.start();
    }
    
    public void selectParticleAsset(int particleAsset)
    {
        switch(particleAsset)
        {
            case 0: imagesX = 15; imagesY = 1; textureName = "Effects/Smoke/Smoke.png"; break;
            case 1: imagesX = 2; imagesY = 2; textureName = "Effects/Explosion/flame.png"; break;
            case 2: imagesX = 2; imagesY = 2; textureName = "Effects/Explosion/flash.png"; break;
            case 3: imagesX = 3; imagesY = 3; textureName = "Effects/Explosion/Debris.png"; break;
            case 4: imagesX = 1; imagesY = 1; textureName = "Effects/Explosion/roundspark.png"; break;
            case 5: imagesX = 1; imagesY = 1; textureName = "Effects/Explosion/shockwave.png"; break;
            case 6: imagesX = 1; imagesY = 3; textureName = "Effects/Explosion/smoketrail.png"; break;
            default: break; //ignore
        }
    }
    
    @Override
    public void simpleInitApp() {
        
        flyCam.setMoveSpeed(100f);
        
        final ParticleEmitter emit = new ParticleEmitter("Emitter", Type.Point, 10000);
        emit.setShape(new EmitterBoxShape(new Vector3f(-1.8f, -1.8f, -1.8f),
                                          new Vector3f(1.8f, 1.8f, 1.8f)));
        
        particleAsset = 0;
        numParticleAssets = 7;
        
        selectParticleAsset(particleAsset);
        
        emit.setGravity(0, 0, 0);
        emit.setLowLife(60);
        emit.setHighLife(60);
        emit.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 0, 0));
        emit.setImagesX(imagesX);
        emit.setImagesY(imagesY);
        emit.setStartSize(0.05f);
        emit.setEndSize(0.05f);
        emit.setStartColor(ColorRGBA.White);
        emit.setEndColor(ColorRGBA.White);
        emit.setSelectRandomImage(true);
        emit.emitAllParticles();
        
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
        mat.setBoolean("PointSprite", true);
        //mat.setBoolean("PointSprite", false);
        mat.setTexture("Texture", assetManager.loadTexture(textureName));
        //mat.setTexture("Texture", assetManager.loadTexture("Effects/Explosion/flame.png"));
        emit.setMaterial(mat);

        rootNode.attachChild(emit);
        inputManager.addListener(new ActionListener() {
            
            public void onAction(String name, boolean isPressed, float tpf) {
                if ("setNum".equals(name) && isPressed) {
                    emit.setNumParticles(5000);
                    emit.emitAllParticles();
                }
                else
                if ("toggle".equals(name) && isPressed) {
                    
                    //select particle texture
                    particleAsset = (particleAsset+1)%numParticleAssets;
                    selectParticleAsset(particleAsset);

                    //update material
                    Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
                    mat.setBoolean("PointSprite", true);
                    mat.setTexture("Texture", assetManager.loadTexture(textureName));
                    
                    //update emitter
                    emit.setImagesX(imagesX);
                    emit.setImagesY(imagesY);
                    emit.setMaterial(mat);
                    //emit.updateGeometricState();
                    //emit.emitAllParticles();
                    
                    //debug info
                    System.out.println("particleAsset #"+particleAsset+" selected!");
                }
            }
        }, "setNum", "toggle");
        
        inputManager.addMapping("setNum", new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager.addMapping("toggle", new KeyTrigger(KeyInput.KEY_TAB));
    }

}

I think this is a beautiful example and actually provides something new to the community too.
:chimpanzee_smile:

1 Like

Cool! Contributing back to the comunity is something I have been looking forward to since I have gotten so much help from the comunity for my project. This will be a cool adition. I have a ton more usefull code for some very complex opened world space game problems that I had to solve from scratch that I want to contribute in the future as well. It seems, from reading the forums that most that started to create an opened world space mmo have dropped their projects as soon as they faced the realy complex bits. I aim to change that by contributing out of the box solutions back into jme.

1 Like

Yes and here is one thing that I just realized some minutes ago:

These are the games that people here face first:
The 2 games in jME 3.0 SDK are defect and buggy with broken controls and logic (+hard to find).
The 1 game that is working (kind of) is “Monkey Zone” which is a complexity beast (+hard to find).
The other things are TestXYZ demos and long wiki articles - not really games.

Since the little world which I started to make yesterday (remember what I said about islands close to water to provoke Z fighting) is going to be something in between. It has most things that a real game will have too. But it is neither broken nor too complex to understand when starting to code.

I think we need more stuff like that: Small, concise games.
Maybe even a little simpler than what I’m currently doing.
Maybe even just Pong or Tetris.
The tutorials are okay, the wiki is okay, the community is fine.
But still most new people here still have to learn weeks and figure out all the details.
That’s what young students from school / highschool will not comprend.
Students from college / university love jME - but that’s kind of an elite.

Of course, @erlend_sh and @pspeed and the others will have to manage a flood of teens if things work out. But hey, some of them have kids and know how to handle these… :chimpanzee_winktongue:

1 Like

OMG it’s already a month passed now…

I did not forget about this and wrote a really cool application in the meantime.
Hopefully I can integrate the stacked frustum in it - and if not, I should really write the TestCase with a much simpler geometry and mechanic (i.e. 1000 cubes in a row from here to horizon or something like that).

Anyways, stay tuned, the game will be out soon.
And did I say that it’s awesome? Because it is! :chimpanzee_closedlaugh:

1 Like

@Ogli not a problem. I have been swamped as well this month tweaking network code on my game.