fileName The screenshot file path to use. Include the separator at the end of the path.
Add a Separator at the end of the directory argument.
Also, I would recommend to always use Slashes instead of Backslash in Java, Backslashes can cause Problems on some Linux-based OS (own experiences). And remove the File extension of the file name, it is added automatic together with a number.
Sorry, did not see.
The ScreenshotAppState generate the File in postFrame from interface SceneProcessor which doesn’t seems to get called when started headless. When starting in Display mode, it works as expected.
I don’t think so, for example has the RenderManager in Headless mode no Camera which is used in ScreenshotAppState.
But is starting with type JmeContext.Type.OffscreenSurface may what you want?
An OffscreenSurface is a context that is not visibleby the user. The application can use the offscreen surface to doGeneral Purpose GPU computations or render a scene into a bufferin order to save it as a screenshot, video or send through a network.
By the way, calling stop() immediate after start() may destroy the application, before it is done. In this case sets takeScreenshot() a boolean to create the screenshot after next rendered frame, but this will usual not happen, since there is no next frame anymore.
I don’t know such a callback, but you can just extend ScreenshotAppState:
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import com.jme3.app.state.ScreenshotAppState;
public final class ListenableScreenshotAppState extends ScreenshotAppState {
private final Collection<Runnable> imageWrittenListeners;
public ListenableScreenshotAppState() {
imageWrittenListeners = new LinkedList<>();
}
public ListenableScreenshotAppState(final String filePath) {
super(filePath);
imageWrittenListeners = new LinkedList<>();
}
public ListenableScreenshotAppState(final String filePath, final String fileName) {
super(filePath, fileName);
imageWrittenListeners = new LinkedList<>();
}
public ListenableScreenshotAppState(final String filePath, final long shotIndex) {
super(filePath, shotIndex);
imageWrittenListeners = new LinkedList<>();
}
public ListenableScreenshotAppState(final String filePath, final String fileName, final long shotIndex) {
super(filePath, fileName, shotIndex);
imageWrittenListeners = new LinkedList<>();
}
public void addImageWrittenListener(final Runnable l) {
if(l == null) {
throw new IllegalArgumentException("l == null");
}
imageWrittenListeners.add(l);
}
public void removeImageWrittenListener(final Runnable l) {
if(l == null) {
throw new IllegalArgumentException("l == null");
}
imageWrittenListeners.remove(l);
}
public Collection<Runnable> getImageWrittenListeners() {
return(new ArrayList<>(imageWrittenListeners));
}
private void fireImageWrittenListeners() {
for(final Runnable l:imageWrittenListeners) {
l.run();
}
}
@Override
protected void writeImageFile(final File file) throws IOException {
super.writeImageFile(file);
fireImageWrittenListeners();
}
}
import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.system.JmeContext;
public class CubePuzzleGenerator extends SimpleApplication {
@Override
public void simpleInitApp() {
final Box b = new Box(1, 1, 1);
final Geometry geom = new Geometry("Box", b);
final Material mat = new Material(assetManager,"Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", ColorRGBA.White);
geom.setMaterial(mat);
rootNode.attachChild(geom);
final ListenableScreenshotAppState ssAppState = new ListenableScreenshotAppState("C:/Tmp/", "test");
ssAppState.addImageWrittenListener(new Runnable() {
@Override
public void run() {
stop();
}
});
this.stateManager.attach(ssAppState);
ssAppState.takeScreenshot();
}
public static void main(final String[] args) {
final CubePuzzleGenerator generator = new CubePuzzleGenerator();
generator.start(JmeContext.Type.OffscreenSurface);
}
}
ScreenShotAppState will create an image in whatever size the screen was set to when the application was initialized. Probably you need to set the app settings up before starting the application… else you will have to restart the context if you need to change size later.
I found setHeight and setWidth methods on the AppSettings class. The problem is it looks like it is not instantiated before the app starts. Take a look at this test code:
package jme3;
import java.io.File;
import com.jme3.app.SimpleApplication;
import com.jme3.app.state.ScreenshotAppState;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeContext;
public class SettingsTest extends SimpleApplication {
public SettingsTest() {
JmeContext context = getContext();
if( context == null ) // This is null
System.out.println("Context is null in constructor");
}
@Override
public void simpleInitApp() {
JmeContext context = getContext();
AppSettings settings = context.getSettings();
System.out.println("Calling set width and height in init method");
settings.setHeight(768);
settings.setWidth(1024);
context.restart();
ScreenshotAppState ssAppState = new ScreenshotAppState(
"C:"+File.separator+"Tmp"+File.separator,
"test");
ssAppState.setIsNumbered(false);
stateManager.attach(ssAppState);
ssAppState.takeScreenshot();
}
public static void main(String[] args) {
SettingsTest app = new SettingsTest();
JmeContext context = app.getContext();
if( context == null ) // This is null
System.out.println("Context is null in main method");
app.start(JmeContext.Type.OffscreenSurface);
app.stop();
}
}
The context is null in the main method and in the app’s contructor.
I tried calling setHeight and setWidth and restarting the context in the simpleInitApp method, but that still gives me a 640x480 image.
The best way to do this would be to get rid of the “restart()” call and just put the sizing code in main() before app.start() is called like this:
public static void main(String[] args) {
app = new Main();
//more init...
AppSettings settings = new AppSettings(true);
settings.put("Width", ((int)width));
settings.put("Height", ((int)height));
app.setSettings(settings);
app.start(JmeContext.Type.Display);
}
But if you absolutely need to allow users to resize and restart the app, then that should also work too. I used to do this for my app, and after looking back at my code, it appears I called the restart() method on app (not on context) and I re-set the appSettings prior to calling restart. But its been a while since I did this so I cannot guarantee for certain that’s the proper way to restart after resizing
app.setSettings(settings);
app.restart();
Also, for what its worth, here is some code I use to scale the sizing by a percentage of screen height in case that’s useful for you as well. (although i think one of these doesn’t work depending on whether you use LWGL3 or 2)
I know you’re trying to get a screenshot to a certain size, so you might not want to scale by a % of screen size like this, but I thought it could still be useful to mention.