Skip to content

Commit 59b3349

Browse files
authored
Merge pull request #2417 from richardTingle/#2416-pbr-screenshot-tests
#2416 pbr screenshot tests
2 parents 432e684 + e60b2c7 commit 59b3349

File tree

18 files changed

+989
-0
lines changed

18 files changed

+989
-0
lines changed
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/*
2+
* Copyright (c) 2024 jMonkeyEngine
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* * Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
*
16+
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
*/
32+
package org.jmonkeyengine.screenshottests.light.pbr;
33+
34+
import com.jme3.app.Application;
35+
import com.jme3.app.SimpleApplication;
36+
import com.jme3.app.state.BaseAppState;
37+
import com.jme3.asset.AssetManager;
38+
import com.jme3.environment.EnvironmentCamera;
39+
import com.jme3.environment.FastLightProbeFactory;
40+
import com.jme3.light.DirectionalLight;
41+
import com.jme3.light.LightProbe;
42+
import com.jme3.material.Material;
43+
import com.jme3.math.ColorRGBA;
44+
import com.jme3.math.Vector3f;
45+
import com.jme3.post.FilterPostProcessor;
46+
import com.jme3.post.filters.ToneMapFilter;
47+
import com.jme3.renderer.Camera;
48+
import com.jme3.scene.Geometry;
49+
import com.jme3.scene.Node;
50+
import com.jme3.scene.Spatial;
51+
import com.jme3.texture.plugins.ktx.KTXLoader;
52+
import com.jme3.util.SkyFactory;
53+
import com.jme3.util.mikktspace.MikktspaceTangentGenerator;
54+
import org.jmonkeyengine.screenshottests.testframework.ScreenshotTestBase;
55+
import org.junit.jupiter.api.TestInfo;
56+
import org.junit.jupiter.params.ParameterizedTest;
57+
import org.junit.jupiter.params.provider.Arguments;
58+
import org.junit.jupiter.params.provider.MethodSource;
59+
60+
import java.util.stream.Stream;
61+
62+
/**
63+
* Screenshot tests for PBR lighting.
64+
*
65+
* @author nehon - original test
66+
* @author Richard Tingle (aka richtea) - screenshot test adaptation
67+
*
68+
*/
69+
public class TestPBRLighting extends ScreenshotTestBase {
70+
71+
private static Stream<Arguments> testParameters() {
72+
return Stream.of(
73+
Arguments.of("LowRoughness", 0.1f, false),
74+
Arguments.of("HighRoughness", 1.0f, false),
75+
Arguments.of("DefaultDirectionalLight", 0.5f, false),
76+
Arguments.of("UpdatedDirectionalLight", 0.5f, true)
77+
);
78+
}
79+
80+
/**
81+
* Test PBR lighting with different parameters
82+
*
83+
* @param testName The name of the test (used for screenshot filename)
84+
* @param roughness The roughness value to use
85+
* @param updateLight Whether to update the directional light to match camera direction
86+
*/
87+
@ParameterizedTest(name = "{0}")
88+
@MethodSource("testParameters")
89+
public void testPBRLighting(String testName, float roughness, boolean updateLight, TestInfo testInfo) {
90+
91+
if(!testInfo.getTestClass().isPresent() || !testInfo.getTestMethod().isPresent()) {
92+
throw new RuntimeException("Test preconditions not met");
93+
}
94+
95+
String imageName = testInfo.getTestClass().get().getName() + "." + testInfo.getTestMethod().get().getName() + "_" + testName;
96+
97+
screenshotTest(new BaseAppState() {
98+
private static final int RESOLUTION = 256;
99+
100+
private Node modelNode;
101+
private int frame = 0;
102+
103+
@Override
104+
protected void initialize(Application app) {
105+
Camera cam = app.getCamera();
106+
cam.setLocation(new Vector3f(18, 10, 0));
107+
cam.lookAt(new Vector3f(0, 0, 0), Vector3f.UNIT_Y);
108+
109+
AssetManager assetManager = app.getAssetManager();
110+
assetManager.registerLoader(KTXLoader.class, "ktx");
111+
112+
app.getViewPort().setBackgroundColor(ColorRGBA.White);
113+
114+
modelNode = new Node("modelNode");
115+
Geometry model = (Geometry) assetManager.loadModel("Models/Tank/tank.j3o");
116+
MikktspaceTangentGenerator.generate(model);
117+
modelNode.attachChild(model);
118+
119+
DirectionalLight dl = new DirectionalLight();
120+
dl.setDirection(new Vector3f(-1, -1, -1).normalizeLocal());
121+
SimpleApplication simpleApp = (SimpleApplication) app;
122+
simpleApp.getRootNode().addLight(dl);
123+
dl.setColor(ColorRGBA.White);
124+
125+
// If we need to update the light direction to match camera
126+
if (updateLight) {
127+
dl.setDirection(app.getCamera().getDirection().normalize());
128+
}
129+
130+
simpleApp.getRootNode().attachChild(modelNode);
131+
132+
FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
133+
int numSamples = app.getContext().getSettings().getSamples();
134+
if (numSamples > 0) {
135+
fpp.setNumSamples(numSamples);
136+
}
137+
138+
fpp.addFilter(new ToneMapFilter(Vector3f.UNIT_XYZ.mult(4.0f)));
139+
app.getViewPort().addProcessor(fpp);
140+
141+
Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Path.hdr", SkyFactory.EnvMapType.EquirectMap);
142+
simpleApp.getRootNode().attachChild(sky);
143+
144+
Material pbrMat = assetManager.loadMaterial("Models/Tank/tank.j3m");
145+
pbrMat.setFloat("Roughness", roughness);
146+
model.setMaterial(pbrMat);
147+
148+
// Set up environment camera
149+
EnvironmentCamera envCam = new EnvironmentCamera(RESOLUTION, new Vector3f(0, 3f, 0));
150+
app.getStateManager().attach(envCam);
151+
}
152+
153+
@Override
154+
protected void cleanup(Application app) {}
155+
156+
@Override
157+
protected void onEnable() {}
158+
159+
@Override
160+
protected void onDisable() {}
161+
162+
@Override
163+
public void update(float tpf) {
164+
frame++;
165+
166+
if (frame == 2) {
167+
modelNode.removeFromParent();
168+
LightProbe probe;
169+
170+
SimpleApplication simpleApp = (SimpleApplication) getApplication();
171+
probe = FastLightProbeFactory.makeProbe(simpleApp.getRenderManager(),
172+
simpleApp.getAssetManager(),
173+
RESOLUTION,
174+
Vector3f.ZERO,
175+
1f,
176+
1000f,
177+
simpleApp.getRootNode());
178+
179+
probe.getArea().setRadius(100);
180+
simpleApp.getRootNode().addLight(probe);
181+
}
182+
183+
if (frame > 10 && modelNode.getParent() == null) {
184+
SimpleApplication simpleApp = (SimpleApplication) getApplication();
185+
simpleApp.getRootNode().attachChild(modelNode);
186+
}
187+
}
188+
}).setBaseImageFileName(imageName)
189+
.setFramesToTakeScreenshotsOn(12)
190+
.run();
191+
}
192+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright (c) 2024 jMonkeyEngine
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* * Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
*
16+
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
*/
32+
package org.jmonkeyengine.screenshottests.light.pbr;
33+
34+
import com.jme3.app.Application;
35+
import com.jme3.app.SimpleApplication;
36+
import com.jme3.app.state.BaseAppState;
37+
import com.jme3.asset.AssetManager;
38+
import com.jme3.environment.EnvironmentProbeControl;
39+
import com.jme3.material.Material;
40+
import com.jme3.math.Vector3f;
41+
import com.jme3.renderer.Camera;
42+
import com.jme3.scene.Geometry;
43+
import com.jme3.scene.Spatial;
44+
import com.jme3.util.SkyFactory;
45+
import com.jme3.util.mikktspace.MikktspaceTangentGenerator;
46+
import org.jmonkeyengine.screenshottests.testframework.ScreenshotTestBase;
47+
import org.junit.jupiter.api.TestInfo;
48+
import org.junit.jupiter.params.ParameterizedTest;
49+
import org.junit.jupiter.params.provider.Arguments;
50+
import org.junit.jupiter.params.provider.MethodSource;
51+
52+
import java.util.stream.Stream;
53+
54+
/**
55+
* A simpler PBR example that uses EnvironmentProbeControl to bake the environment
56+
*
57+
* @author Richard Tingle (aka richtea) - screenshot test adaptation
58+
*/
59+
public class TestPBRSimple extends ScreenshotTestBase {
60+
61+
private static Stream<Arguments> testParameters() {
62+
return Stream.of(
63+
Arguments.of("WithRealtimeBaking", true),
64+
Arguments.of("WithoutRealtimeBaking", false)
65+
);
66+
}
67+
68+
/**
69+
* Test PBR simple with different parameters
70+
*
71+
* @param testName The name of the test (used for screenshot filename)
72+
* @param realtimeBaking Whether to use realtime baking
73+
*/
74+
@ParameterizedTest(name = "{0}")
75+
@MethodSource("testParameters")
76+
public void testPBRSimple(String testName, boolean realtimeBaking, TestInfo testInfo) {
77+
if(!testInfo.getTestClass().isPresent() || !testInfo.getTestMethod().isPresent()) {
78+
throw new RuntimeException("Test preconditions not met");
79+
}
80+
81+
String imageName = testInfo.getTestClass().get().getName() + "." + testInfo.getTestMethod().get().getName() + "_" + testName;
82+
83+
screenshotTest(new BaseAppState() {
84+
private int frame = 0;
85+
86+
@Override
87+
protected void initialize(Application app) {
88+
Camera cam = app.getCamera();
89+
cam.setLocation(new Vector3f(18, 10, 0));
90+
cam.lookAt(new Vector3f(0, 0, 0), Vector3f.UNIT_Y);
91+
92+
AssetManager assetManager = app.getAssetManager();
93+
SimpleApplication simpleApp = (SimpleApplication) app;
94+
95+
// Create the tank model
96+
Geometry model = (Geometry) assetManager.loadModel("Models/Tank/tank.j3o");
97+
MikktspaceTangentGenerator.generate(model);
98+
99+
Material pbrMat = assetManager.loadMaterial("Models/Tank/tank.j3m");
100+
model.setMaterial(pbrMat);
101+
simpleApp.getRootNode().attachChild(model);
102+
103+
// Create sky
104+
Spatial sky = SkyFactory.createSky(assetManager, "Textures/Sky/Path.hdr", SkyFactory.EnvMapType.EquirectMap);
105+
simpleApp.getRootNode().attachChild(sky);
106+
107+
// Create baker control
108+
EnvironmentProbeControl envProbe = new EnvironmentProbeControl(assetManager, 256);
109+
simpleApp.getRootNode().addControl(envProbe);
110+
111+
// Tag the sky, only the tagged spatials will be rendered in the env map
112+
envProbe.tag(sky);
113+
}
114+
115+
@Override
116+
protected void cleanup(Application app) {}
117+
118+
@Override
119+
protected void onEnable() {}
120+
121+
@Override
122+
protected void onDisable() {}
123+
124+
@Override
125+
public void update(float tpf) {
126+
if (realtimeBaking) {
127+
frame++;
128+
if (frame == 2) {
129+
SimpleApplication simpleApp = (SimpleApplication) getApplication();
130+
simpleApp.getRootNode().getControl(EnvironmentProbeControl.class).rebake();
131+
}
132+
}
133+
}
134+
}).setBaseImageFileName(imageName)
135+
.setFramesToTakeScreenshotsOn(10)
136+
.run();
137+
}
138+
}

0 commit comments

Comments
 (0)