Skip to content

Commit 4e1906f

Browse files
Replaces fullscreen quad with a viewport covering triangle (#2589)
* *Add FullscreenTriangle.java * *Update Postprocessor to use the new FullscreenTriangle geometry * Changed post vertex shaders to work with the already correct position coordinates * Revert to the old vertex shader code * Reworked the positions so that the vertex positions are correct with the post.vert transformations * Added support for using fullscreen triangle or quad in `FilterPostProcessor` rendering. * Introduce support for multi-scenario screenshot tests to validate identical results across approaches. * Updated `TestFog` to support multi-scenario screenshot testing with `FullscreenQuad` and `FullscreenTriangle` setups. --------- Co-authored-by: Richard Tingle <6330028+richardTingle@users.noreply.github.com>
1 parent 7a2d8ea commit 4e1906f

9 files changed

Lines changed: 313 additions & 72 deletions

File tree

jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
import com.jme3.renderer.Renderer;
4747
import com.jme3.renderer.ViewPort;
4848
import com.jme3.renderer.queue.RenderQueue;
49+
import com.jme3.scene.Geometry;
50+
import com.jme3.scene.shape.FullscreenTriangle;
4951
import com.jme3.texture.FrameBuffer;
5052
import com.jme3.texture.FrameBuffer.FrameBufferTarget;
5153
import com.jme3.texture.Image.Format;
@@ -88,7 +90,8 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
8890
private Texture2D depthTexture;
8991
private SafeArrayList<Filter> filters = new SafeArrayList<>(Filter.class);
9092
private AssetManager assetManager;
91-
private Picture fsQuad;
93+
private boolean useFullscreenTriangle = false;
94+
private Geometry fsQuad;
9295
private boolean computeDepth = false;
9396
private FrameBuffer outputBuffer;
9497
private int width;
@@ -116,6 +119,18 @@ public FilterPostProcessor(AssetManager assetManager) {
116119
this.assetManager = assetManager;
117120
}
118121

122+
/**
123+
* Constructs a new instance of FilterPostProcessor.
124+
*
125+
* @param assetManager The asset manager used to load resources needed by the processor.
126+
* @param useFullscreenTriangle If true, a fullscreen triangle will be used for rendering;
127+
* otherwise, a quad will be used.
128+
*/
129+
public FilterPostProcessor(AssetManager assetManager, boolean useFullscreenTriangle) {
130+
this(assetManager);
131+
this.useFullscreenTriangle = useFullscreenTriangle;
132+
}
133+
119134
/**
120135
* Serialization-only constructor. Do not use this constructor directly;
121136
* use {@link #FilterPostProcessor(AssetManager)}.
@@ -181,9 +196,14 @@ public void initialize(RenderManager rm, ViewPort vp) {
181196
renderManager = rm;
182197
renderer = rm.getRenderer();
183198
viewPort = vp;
184-
fsQuad = new Picture("filter full screen quad");
185-
fsQuad.setWidth(1);
186-
fsQuad.setHeight(1);
199+
if(useFullscreenTriangle) {
200+
fsQuad = new Geometry("FsQuad", new FullscreenTriangle());
201+
}else{
202+
Picture fullscreenQuad = new Picture("filter full screen quad");
203+
fullscreenQuad.setWidth(1);
204+
fullscreenQuad.setHeight(1);
205+
fsQuad = fullscreenQuad;
206+
}
187207

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

@@ -723,6 +744,7 @@ public void write(JmeExporter ex) throws IOException {
723744
public void read(JmeImporter im) throws IOException {
724745
InputCapsule ic = im.getCapsule(this);
725746
numSamples = ic.readInt("numSamples", 0);
747+
useFullscreenTriangle = ic.readBoolean("useFullscreenTriangle", false);
726748
filters = new SafeArrayList<>(Filter.class, ic.readSavableArrayList("filters", null));
727749
for (Filter filter : filters.getArray()) {
728750
filter.setProcessor(this);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.jme3.scene.shape;
2+
3+
import com.jme3.scene.Mesh;
4+
import com.jme3.scene.VertexBuffer;
5+
6+
7+
/**
8+
* The FullscreenTriangle class defines a mesh representing a single
9+
* triangle that spans the entire screen. It is typically used in rendering
10+
* techniques where a fullscreen quad or triangle is needed, such as in
11+
* post-processing effects or screen-space operations.
12+
*/
13+
public class FullscreenTriangle extends Mesh {
14+
15+
/**
16+
* Encapsulates the vertex positions for a fullscreen triangle.
17+
* The positions are transformed by the vertex shader to cover the entire screen.
18+
*/
19+
private static final float[] POSITIONS = {
20+
0, 0, 0, // -1 -1 0 after vertex shader transform
21+
2, 0, 0, // 3 -1 0 after vertex shader transform
22+
0, 2, 0 // -1 3 0 after vertex shader transform
23+
};
24+
25+
private static final float[] TEXCOORDS = {
26+
0,0,
27+
2,0,
28+
0,2
29+
};
30+
31+
32+
public FullscreenTriangle() {
33+
super();
34+
setBuffer(VertexBuffer.Type.Position, 3, POSITIONS);
35+
setBuffer(VertexBuffer.Type.TexCoord, 2, TEXCOORDS);
36+
setBuffer(VertexBuffer.Type.Index, 3, new short[]{0, 1, 2});
37+
updateBound();
38+
}
39+
}

jme3-effects/src/main/resources/Common/MatDefs/Post/Post.vert

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ attribute vec2 inTexCoord;
44

55
varying vec2 texCoord;
66

7-
void main() {
7+
void main() {
88
vec2 pos = inPosition.xy * 2.0 - 1.0;
9-
gl_Position = vec4(pos, 0.0, 1.0);
9+
gl_Position = vec4(pos, 0.0, 1.0);
1010
texCoord = inTexCoord;
1111
}

jme3-effects/src/main/resources/Common/MatDefs/Post/Post15.vert

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ in vec2 inTexCoord;
66
out vec2 texCoord;
77

88
void main() {
9-
vec2 pos = inPosition.xy * 2.0 - 1.0;
9+
vec2 pos = inPosition.xy * 2.0 - 1.0;
1010
gl_Position = vec4(pos, 0.0, 1.0);
1111
texCoord = inTexCoord;
1212
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.jmonkeyengine.screenshottests.testframework;
2+
3+
import com.jme3.app.state.AppState;
4+
5+
public class Scenario {
6+
String scenarioName;
7+
AppState[] states;
8+
9+
public Scenario(String scenarioName, AppState... states) {
10+
this.scenarioName = scenarioName;
11+
this.states = states;
12+
}
13+
}

jme3-screenshot-tests/src/main/java/org/jmonkeyengine/screenshottests/testframework/ScreenshotTest.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ public class ScreenshotTest{
4747

4848
TestType testType = TestType.MUST_PASS;
4949

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

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

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

5862
public ScreenshotTest(AppState... initialStates){
59-
states = initialStates;
63+
scenarios.add(new Scenario("SimpleSingleScenario", initialStates));
64+
framesToTakeScreenshotsOn.add(1); //default behaviour is to take a screenshot on the first frame
65+
}
66+
public ScreenshotTest(Scenario... scenarios){
67+
this.scenarios.addAll(Arrays.asList(scenarios));
6068
framesToTakeScreenshotsOn.add(1); //default behaviour is to take a screenshot on the first frame
6169
}
6270

@@ -100,7 +108,7 @@ public void run(){
100108

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

103-
TestDriver.bootAppForTest(testType,settings,imageFilePrefix, framesToTakeScreenshotsOn, states);
111+
TestDriver.bootAppForTest(testType,settings,imageFilePrefix, framesToTakeScreenshotsOn, scenarios);
104112
}
105113

106114

jme3-screenshot-tests/src/main/java/org/jmonkeyengine/screenshottests/testframework/ScreenshotTestBase.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,22 @@ public abstract class ScreenshotTestBase{
4747
/**
4848
* Initialises a screenshot test. The resulting object should be configured (if neccessary) and then started
4949
* by calling {@link ScreenshotTest#run()}.
50-
* @param initialStates
50+
* @param initialStates the states that will create the JME environment
5151
* @return
5252
*/
5353
public ScreenshotTest screenshotTest(AppState... initialStates){
5454
return new ScreenshotTest(initialStates);
5555
}
56+
57+
/**
58+
* Permits multiple scenarios to be tested in a single test. Each scenario should give identical results and
59+
* will have a screenshot taken on the same frame.
60+
*
61+
* <p>
62+
* This is intended for testing migrations where the old and new approach should both give identical results.
63+
* </p>
64+
*/
65+
public ScreenshotTest screenshotMultiScenarioTest(Scenario... scenarios){
66+
return new ScreenshotTest(scenarios);
67+
}
5668
}

0 commit comments

Comments
 (0)