Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ checkstyle = "13.3.0"
jacoco = "0.8.12"
lwjgl3 = "3.4.1"
angle = "2026-05-09"
libjglios = "0.7"
libjglios = "0.9"
saferalloc = "0.0.10"
nifty = "1.4.3"
spotbugs = "4.9.8"
Expand Down Expand Up @@ -98,4 +98,3 @@ saferalloc = ["saferalloc", "saferalloc-natives-linux-x8664", "saferalloc-native

[plugins]
jacoco = { id = "jacoco", version.ref = "jacoco" }

Original file line number Diff line number Diff line change
Expand Up @@ -315,10 +315,17 @@ public void uncaughtException(Thread thread, Throwable thrown) {
GLExt.class
);
}
if (settings.getBoolean("GraphicsTiming")) {
GLTimingState timingState = new GLTimingState();
gl = (GL) GLTiming.createGLTiming(
gl, timingState, GL.class, GL2.class, GLES_30.class, GLFbo.class, GLExt.class);
}
if (settings.getBoolean("GraphicsTrace")) {
gl = (GL) GLTracer.createGlesTracer(gl, GL.class, GLES_30.class, GLFbo.class, GLExt.class);
gl = (GL) GLTracer.createGlesTracer(
gl, GL.class, GL2.class, GLES_30.class, GLFbo.class, GLExt.class);
}
renderer = new GLRenderer(gl, (GLExt) gl, (GLFbo) gl);
renderer.setDebugEnabled(settings.isGraphicsDebug());
renderer.initialize();

boolean blitSrgbConversion = useBlitSrgbConversion();
Expand Down Expand Up @@ -607,9 +614,9 @@ private boolean useBlitSrgbConversion() {
}

private boolean useBlitFrameBuffer() {
float mode = settings.getDisplayScaleMode();
return application != null && (useBlitSrgbConversion()
|| DisplayScaleUtils.isDisabledMode(mode) || DisplayScaleUtils.isEmulatedScaleMode(mode));
|| getRenderFramebufferWidth() != framebufferWidth
|| getRenderFramebufferHeight() != framebufferHeight);
}

private int getRenderFramebufferWidth() {
Expand Down
12 changes: 12 additions & 0 deletions jme3-core/src/main/java/com/jme3/app/Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,18 @@ public interface Application {
*/
public void setSettings(AppSettings settings);

/**
* Returns the display settings currently owned by this application.
* Before startup, custom implementations may return {@code null} when no
* settings have been assigned yet.
*
* @return the current settings, or {@code null}
*/
public default AppSettings getSettings() {
JmeContext currentContext = getContext();
return currentContext == null ? null : currentContext.getSettings();
}

/**
* Sets the Timer implementation that will be used for calculating
* frame times. By default, Application will use the Timer as returned
Expand Down
5 changes: 5 additions & 0 deletions jme3-core/src/main/java/com/jme3/app/LegacyApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,11 @@ public void setSettings(AppSettings settings) {
}
}

@Override
public AppSettings getSettings() {
return settings;
}

/**
* Sets the Timer implementation that will be used for calculating
* frame times. By default, Application will use the Timer as returned
Expand Down
125 changes: 78 additions & 47 deletions jme3-core/src/main/java/com/jme3/post/FilterPostProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,20 @@ public class FilterPostProcessor implements SceneProcessor, Savable {
private FrameBuffer outputBuffer;
private int width;
private int height;
private Camera sceneCamera;
private final Camera multiViewRenderCamera = new Camera(1, 1);
private final Camera processingCamera = new Camera(1, 1);
private float bottom;
private float left;
private float right;
private float top;
private int originalWidth;
private int originalHeight;
private int cameraWidth;
private int cameraHeight;
private int logicalViewWidth;
private int logicalViewHeight;
private boolean multiViewRenderCameraInstalled;
private boolean resizeWithDefaultFramebuffer;
private int lastFilterIndex = -1;
private boolean cameraInit = false;
Expand Down Expand Up @@ -264,9 +272,8 @@ private void initFilter(Filter filter, ViewPort vp) {
/**
* Renders a filter's material onto a full-screen quad. This method
* handles setting up the rendering context (framebuffer, camera, material)
* for a filter pass. It correctly resizes the camera and adjusts material
* states based on whether the target buffer is the final output buffer or an
* intermediate filter buffer.
* for a filter pass. It copies the scene camera into a dedicated processing
* camera, then adjusts only that copy for the target framebuffer.
*
* @param r The `Renderer` instance.
* @param buff The `FrameBuffer` to render to.
Expand All @@ -275,21 +282,17 @@ private void initFilter(Filter filter, ViewPort vp) {
private void renderProcessing(Renderer r, FrameBuffer buff, Material mat) {
// Adjust camera and viewport based on target framebuffer
if (buff == outputBuffer) {
viewPort.getCamera().resize(originalWidth, originalHeight, false);
viewPort.getCamera().setViewPort(left, right, bottom, top);
// viewPort.getCamera().update(); // Redundant as resize and setViewPort call onXXXChange
renderManager.setCamera(viewPort.getCamera(), false);
Camera camera = prepareProcessingCamera(cameraWidth, cameraHeight, left, right, bottom, top);
renderManager.setCamera(camera, false, originalWidth, originalHeight);
// Disable depth test/write for final pass to prevent artifacts
if (mat.getAdditionalRenderState().isDepthWrite()) {
mat.getAdditionalRenderState().setDepthTest(false);
mat.getAdditionalRenderState().setDepthWrite(false);
}
} else {
// Rendering to an intermediate framebuffer for a filter pass
viewPort.getCamera().resize(buff.getWidth(), buff.getHeight(), false);
viewPort.getCamera().setViewPort(0, 1, 0, 1);
// viewPort.getCamera().update(); // Redundant as resize and setViewPort call onXXXChange
renderManager.setCamera(viewPort.getCamera(), false);
Camera camera = prepareProcessingCamera(logicalViewWidth, logicalViewHeight, 0f, 1f, 0f, 1f);
renderManager.setCamera(camera, false, buff.getWidth(), buff.getHeight());
// Enable depth test/write for intermediate passes if material needs it
mat.getAdditionalRenderState().setDepthTest(true);
mat.getAdditionalRenderState().setDepthWrite(true);
Expand Down Expand Up @@ -431,46 +434,47 @@ private void renderFilterChain(Renderer r, FrameBuffer sceneFb) {

@Override
public void postFrame(FrameBuffer out) {
try {
FrameBuffer sceneBuffer = renderFrameBuffer;
if (renderFrameBufferMS != null && !renderer.getCaps().contains(Caps.OpenGL32)) {
renderer.copyFrameBuffer(renderFrameBufferMS, renderFrameBuffer, true, true);
} else if (renderFrameBufferMS != null) {
sceneBuffer = renderFrameBufferMS;
}

FrameBuffer sceneBuffer = renderFrameBuffer;
if (renderFrameBufferMS != null && !renderer.getCaps().contains(Caps.OpenGL32)) {
renderer.copyFrameBuffer(renderFrameBufferMS, renderFrameBuffer, true, true);
} else if (renderFrameBufferMS != null) {
sceneBuffer = renderFrameBufferMS;
}

// Execute the filter chain
renderFilterChain(renderer, sceneBuffer);

// Restore the original output framebuffer for the viewport
renderer.setFrameBuffer(outputBuffer);

// viewport can be null if no filters are enabled
if (viewPort != null) {
renderManager.setCamera(viewPort.getCamera(), false);
// Execute the filter chain
renderFilterChain(renderer, sceneBuffer);
} finally {
// Later processors and the translucent bucket render to the original
// physical target with the untouched logical scene camera.
renderer.setFrameBuffer(outputBuffer);
restoreSceneCamera();
if (viewPort != null) {
renderManager.setCamera(viewPort.getCamera(), false,
originalWidth, originalHeight);
}
}
}

@Override
public void preFrame(float tpf) {
if (filters.isEmpty() || lastFilterIndex == -1) {
// If no filters are enabled, restore the camera's original viewport
// and output framebuffer to bypass the post-processor.
// If no filters are enabled, restore the output framebuffer and
// bypass the post-processor.
if (cameraInit) {
viewPort.getCamera().resize(originalWidth, originalHeight, true);
viewPort.getCamera().setViewPort(left, right, bottom, top);
restoreSceneCamera();
viewPort.setOutputFrameBuffer(outputBuffer);
cameraInit = false;
}
} else {
setupViewPortFrameBuffer();
// If in a multi-view situation, resize the camera to the viewport size
// so that the back buffer is rendered correctly for filtering.
// A multi-view FPP owns a cropped physical framebuffer. Render the
// scene through a logical-sized camera whose viewport covers that
// whole framebuffer, without modifying the original camera object.
if (multiView) {
viewPort.getCamera().resize(width, height, false);
viewPort.getCamera().setViewPort(0, 1, 0, 1);
viewPort.getCamera().update();
renderManager.setCamera(viewPort.getCamera(), false);
installMultiViewRenderCamera();
} else {
restoreSceneCamera();
}
}

Expand Down Expand Up @@ -529,9 +533,8 @@ private void updateLastFilterIndex() {
@Override
public void cleanup() {
if (viewPort != null) {
// Reset the viewport camera and output framebuffer to their initial values
viewPort.getCamera().resize(originalWidth, originalHeight, true);
viewPort.getCamera().setViewPort(left, right, bottom, top);
// Reset FPP render state and restore the original framebuffer.
restoreSceneCamera();
viewPort.setOutputFrameBuffer(outputBuffer);
viewPort = null;

Expand Down Expand Up @@ -563,11 +566,10 @@ public void setProfiler(AppProfiler profiler) {
*/
@Override
public void reshape(ViewPort vp, int w, int h) {
restoreSceneCamera();
Camera cam = vp.getCamera();
// This sets the camera viewport to its full extent (0-1) for rendering to the FPP's internal buffer.
cam.setViewPort(left, right, bottom, top);
// Resizing the camera to fit the new viewport and saving original dimensions
cam.resize(w, h, true);
cameraWidth = cam.getWidth();
cameraHeight = cam.getHeight();
left = cam.getViewPortLeft();
right = cam.getViewPortRight();
top = cam.getViewPortTop();
Expand All @@ -580,13 +582,13 @@ public void reshape(ViewPort vp, int w, int h) {
height = (int) (h * (Math.abs(bottom - top)));
width = Math.max(1, width);
height = Math.max(1, height);
logicalViewWidth = Math.max(1, (int) (cameraWidth * Math.abs(right - left)));
logicalViewHeight = Math.max(1, (int) (cameraHeight * Math.abs(top - bottom)));

// Test if original dimensions differ from actual viewport dimensions.
// If they are different, we are in a multiview situation, and the
// camera must be handled differently (e.g., resized to the sub-viewport).
if (originalWidth != width || originalHeight != height) {
multiView = true;
}
multiView = originalWidth != width || originalHeight != height;

cameraInit = true;
computeDepth = false;
Expand Down Expand Up @@ -647,6 +649,35 @@ public void reshape(ViewPort vp, int w, int h) {
setupViewPortFrameBuffer();
}

private void installMultiViewRenderCamera() {
restoreSceneCamera();
sceneCamera = viewPort.getCamera();
multiViewRenderCamera.copyFrom(sceneCamera);
multiViewRenderCamera.resize(logicalViewWidth, logicalViewHeight, false);
multiViewRenderCamera.setViewPort(0f, 1f, 0f, 1f);
viewPort.setCamera(multiViewRenderCamera);
multiViewRenderCameraInstalled = true;
}

Camera prepareProcessingCamera(int logicalWidth, int logicalHeight,
float viewportLeft, float viewportRight, float viewportBottom, float viewportTop) {
Camera sourceCamera = multiViewRenderCameraInstalled ? sceneCamera : viewPort.getCamera();
processingCamera.copyFrom(sourceCamera);
processingCamera.resize(Math.max(logicalWidth, 1), Math.max(logicalHeight, 1), false);
processingCamera.setViewPort(viewportLeft, viewportRight, viewportBottom, viewportTop);
return processingCamera;
}

private void restoreSceneCamera() {
if (!multiViewRenderCameraInstalled) {
return;
}
if (viewPort != null && viewPort.getCamera() == multiViewRenderCamera) {
viewPort.setCamera(sceneCamera);
}
multiViewRenderCameraInstalled = false;
}

private void cleanupFilterResources() {
for (Filter filter : filters.getArray()) {
filter.cleanup(renderer);
Expand Down
54 changes: 40 additions & 14 deletions jme3-core/src/main/java/com/jme3/renderer/RenderManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ public class RenderManager {
private Camera prevCam = null;
private int prevViewportWidth = -1;
private int prevViewportHeight = -1;
private int defaultFramebufferWidth = -1;
private int defaultFramebufferHeight = -1;
private Material forcedMaterial = null;
private String forcedTechnique = null;
private RenderState forcedRenderState = null;
Expand Down Expand Up @@ -437,10 +439,11 @@ public List<ViewPort> getPostViews() {
* @param cam the Camera to use for rendering (alias created)
* @return a new instance
*/
public ViewPort createPreView(String viewName, Camera cam) {
ViewPort vp = new ViewPort(viewName, cam);
preViewPorts.add(vp);
return vp;
public ViewPort createPreView(String viewName, Camera cam) {
ViewPort vp = new ViewPort(viewName, cam);
applyDefaultFramebufferSize(vp);
preViewPorts.add(vp);
return vp;
}

/**
Expand All @@ -453,10 +456,11 @@ public ViewPort createPreView(String viewName, Camera cam) {
* @param cam the Camera to use for rendering (alias created)
* @return a new instance
*/
public ViewPort createMainView(String viewName, Camera cam) {
ViewPort vp = new ViewPort(viewName, cam);
viewPorts.add(vp);
return vp;
public ViewPort createMainView(String viewName, Camera cam) {
ViewPort vp = new ViewPort(viewName, cam);
applyDefaultFramebufferSize(vp);
viewPorts.add(vp);
return vp;
}

/**
Expand All @@ -468,11 +472,18 @@ public ViewPort createMainView(String viewName, Camera cam) {
* @param cam the Camera to use for rendering (alias created)
* @return a new instance
*/
public ViewPort createPostView(String viewName, Camera cam) {
ViewPort vp = new ViewPort(viewName, cam);
postViewPorts.add(vp);
return vp;
}
public ViewPort createPostView(String viewName, Camera cam) {
ViewPort vp = new ViewPort(viewName, cam);
applyDefaultFramebufferSize(vp);
postViewPorts.add(vp);
return vp;
}

private void applyDefaultFramebufferSize(ViewPort vp) {
if (defaultFramebufferWidth > 0 && defaultFramebufferHeight > 0) {
vp.setRenderTargetSize(defaultFramebufferWidth, defaultFramebufferHeight);
}
}

private void notifyReshape(ViewPort vp, int w, int h) {
vp.setRenderTargetSize(w, h);
Expand Down Expand Up @@ -524,6 +535,8 @@ public void notifyReshape(int logicalWidth, int logicalHeight, int framebufferWi
prevCam = null;
int surfaceWidth = Math.max(framebufferWidth, 1);
int surfaceHeight = Math.max(framebufferHeight, 1);
defaultFramebufferWidth = surfaceWidth;
defaultFramebufferHeight = surfaceHeight;
reshapeViewPorts(preViewPorts, logicalWidth, logicalHeight, surfaceWidth, surfaceHeight);
reshapeViewPorts(viewPorts, logicalWidth, logicalHeight, surfaceWidth, surfaceHeight);
reshapeViewPorts(postViewPorts, logicalWidth, logicalHeight, surfaceWidth, surfaceHeight);
Expand Down Expand Up @@ -1366,7 +1379,20 @@ public void setCamera(Camera cam, boolean ortho) {
setCamera(cam, ortho, cam.getWidth(), cam.getHeight());
}

private void setCamera(Camera cam, boolean ortho, int targetWidth, int targetHeight) {
/**
* Sets the camera while applying its normalized viewport to an explicit
* render-target size.
*
* <p>This overload is intended for rendering paths where camera dimensions
* are logical coordinates but the framebuffer uses a different physical
* size, such as HiDPI, supersampling, and post-processing.</p>
*
* @param cam the camera to set
* @param ortho true to use GUI orthographic projection
* @param targetWidth physical render-target width
* @param targetHeight physical render-target height
*/
public void setCamera(Camera cam, boolean ortho, int targetWidth, int targetHeight) {
// Tell the light filter which camera to use for filtering.
if (lightFilter != null) {
lightFilter.setCamera(cam);
Expand Down
Loading
Loading