Skip to content
Open
30 changes: 26 additions & 4 deletions jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
import com.jme3.renderer.Renderer;
import com.jme3.renderer.ViewPort;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.FullscreenTriangle;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.FrameBuffer.FrameBufferTarget;
import com.jme3.texture.Image.Format;
Expand Down Expand Up @@ -88,7 +90,8 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
private Texture2D depthTexture;
private SafeArrayList<Filter> filters = new SafeArrayList<>(Filter.class);
private AssetManager assetManager;
private Picture fsQuad;
private boolean useFullscreenTriangle = false;
private Geometry fsQuad;
private boolean computeDepth = false;
private FrameBuffer outputBuffer;
private int width;
Expand Down Expand Up @@ -116,6 +119,18 @@ public FilterPostProcessor(AssetManager assetManager) {
this.assetManager = assetManager;
}

/**
* Constructs a new instance of FilterPostProcessor.
*
* @param assetManager The asset manager used to load resources needed by the processor.
* @param useFullscreenTriangle If true, a fullscreen triangle will be used for rendering;
* otherwise, a quad will be used.
*/
public FilterPostProcessor(AssetManager assetManager, boolean useFullscreenTriangle) {
this(assetManager);
this.useFullscreenTriangle = useFullscreenTriangle;
}

/**
* Serialization-only constructor. Do not use this constructor directly;
* use {@link #FilterPostProcessor(AssetManager)}.
Expand Down Expand Up @@ -181,9 +196,14 @@ public void initialize(RenderManager rm, ViewPort vp) {
renderManager = rm;
renderer = rm.getRenderer();
viewPort = vp;
fsQuad = new Picture("filter full screen quad");
fsQuad.setWidth(1);
fsQuad.setHeight(1);
if(useFullscreenTriangle) {
fsQuad = new Geometry("FsQuad", new FullscreenTriangle());
}else{
Picture fullscreenQuad = new Picture("filter full screen quad");
fullscreenQuad.setWidth(1);
fullscreenQuad.setHeight(1);
fsQuad = fullscreenQuad;
}

// Determine optimal framebuffer format based on renderer capabilities
if (!renderer.getCaps().contains(Caps.PackedFloatTexture)) {
Expand Down Expand Up @@ -715,6 +735,7 @@ public Format getFrameBufferDepthFormat() {
public void write(JmeExporter ex) throws IOException {
OutputCapsule oc = ex.getCapsule(this);
oc.write(numSamples, "numSamples", 0);
oc.write(useFullscreenTriangle, "useFullscreenTriangle", false);
oc.writeSavableArrayList(new ArrayList(filters), "filters", null);
}

Expand All @@ -723,6 +744,7 @@ public void write(JmeExporter ex) throws IOException {
public void read(JmeImporter im) throws IOException {
InputCapsule ic = im.getCapsule(this);
numSamples = ic.readInt("numSamples", 0);
useFullscreenTriangle = ic.readBoolean("useFullscreenTriangle", false);
filters = new SafeArrayList<>(Filter.class, ic.readSavableArrayList("filters", null));
for (Filter filter : filters.getArray()) {
filter.setProcessor(this);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.jme3.scene.shape;

import com.jme3.scene.Mesh;
import com.jme3.scene.VertexBuffer;


/**
* The FullscreenTriangle class defines a mesh representing a single
* triangle that spans the entire screen. It is typically used in rendering
* techniques where a fullscreen quad or triangle is needed, such as in
* post-processing effects or screen-space operations.
*/
public class FullscreenTriangle extends Mesh {

/**
* Encapsulates the vertex positions for a fullscreen triangle.
* The positions are transformed by the vertex shader to cover the entire screen.
*/
private static final float[] POSITIONS = {
0, 0, 0, // -1 -1 0 after vertex shader transform
2, 0, 0, // 3 -1 0 after vertex shader transform
0, 2, 0 // -1 3 0 after vertex shader transform
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

};

private static final float[] TEXCOORDS = {
0,0,
2,0,
0,2
};


public FullscreenTriangle() {
super();
setBuffer(VertexBuffer.Type.Position, 3, POSITIONS);
setBuffer(VertexBuffer.Type.TexCoord, 2, TEXCOORDS);
setBuffer(VertexBuffer.Type.Index, 3, new short[]{0, 1, 2});
updateBound();
}
}
4 changes: 2 additions & 2 deletions jme3-effects/src/main/resources/Common/MatDefs/Post/Post.vert
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ attribute vec2 inTexCoord;

varying vec2 texCoord;

void main() {
void main() {
vec2 pos = inPosition.xy * 2.0 - 1.0;
gl_Position = vec4(pos, 0.0, 1.0);
gl_Position = vec4(pos, 0.0, 1.0);
texCoord = inTexCoord;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ in vec2 inTexCoord;
out vec2 texCoord;

void main() {
vec2 pos = inPosition.xy * 2.0 - 1.0;
vec2 pos = inPosition.xy * 2.0 - 1.0;
gl_Position = vec4(pos, 0.0, 1.0);
texCoord = inTexCoord;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.jmonkeyengine.screenshottests.testframework;

import com.jme3.app.state.AppState;

public class Scenario {
String scenarioName;
AppState[] states;

public Scenario(String scenarioName, AppState... states) {
this.scenarioName = scenarioName;
this.states = states;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ public class ScreenshotTest{

TestType testType = TestType.MUST_PASS;

AppState[] states;
/**
* Usually there will be a single scenario but sometimes it will be desirable to test that two ways
* of doing something produce the same result. In that case there will be multiple scenarios.
*/
List<Scenario> scenarios = new ArrayList<>();

List<Integer> framesToTakeScreenshotsOn = new ArrayList<>();

Expand All @@ -56,7 +60,11 @@ public class ScreenshotTest{
String baseImageFileName = null;

public ScreenshotTest(AppState... initialStates){
states = initialStates;
scenarios.add(new Scenario("SimpleSingleScenario", initialStates));
framesToTakeScreenshotsOn.add(1); //default behaviour is to take a screenshot on the first frame
}
public ScreenshotTest(Scenario... scenarios){
this.scenarios.addAll(Arrays.asList(scenarios));
framesToTakeScreenshotsOn.add(1); //default behaviour is to take a screenshot on the first frame
}

Expand Down Expand Up @@ -100,7 +108,7 @@ public void run(){

String imageFilePrefix = baseImageFileName == null ? calculateImageFilePrefix() : baseImageFileName;

TestDriver.bootAppForTest(testType,settings,imageFilePrefix, framesToTakeScreenshotsOn, states);
TestDriver.bootAppForTest(testType,settings,imageFilePrefix, framesToTakeScreenshotsOn, scenarios);
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,22 @@ public abstract class ScreenshotTestBase{
/**
* Initialises a screenshot test. The resulting object should be configured (if neccessary) and then started
* by calling {@link ScreenshotTest#run()}.
* @param initialStates
* @param initialStates the states that will create the JME environment
* @return
*/
public ScreenshotTest screenshotTest(AppState... initialStates){
return new ScreenshotTest(initialStates);
}

/**
* Permits multiple scenarios to be tested in a single test. Each scenario should give identical results and
* will have a screenshot taken on the same frame.
*
* <p>
* This is intended for testing migrations where the old and new approach should both give identical results.
* </p>
*/
public ScreenshotTest screenshotMultiScenarioTest(Scenario... scenarios){
return new ScreenshotTest(scenarios);
}
}
Loading