So following up this conversation http://hub.jmonkeyengine.org/forum/topic/re-compile-shaders-during-runtime/
I added a new appstate to the engine : the MaterialDebugAppState
It handles material hot reload of materials at runtime. Meaning that you don’t have to stop the app and relaunch it every time you make a modification to a shader file.
If your modification results in a shader compilation error, the App state will catch it and display it in the console, but won’t reload the material avoiding the app to crash.
It handles reloading materials for any Spatial or any Filter.
There might be still some issues with filters when you have multiples passes (maybe not, but I didn’t test it thoroughly so I can’t tell).
You can register a binding with a Trigger (most probably a KeyTrigger) and a Spatial. Whenever the trigger is fired (the key is hit), the Spatial’s materials (works also for a node with sub geoms) will be reloaded.
You can do the same with a Filter instead of a Spatial, and all the materials of the Filter will be reloaded
You can also register a binding with a file (that the asset manager can locate, for example in your assets folder) and a Spatial or a Filter. The reload will be fired whenever the file is changed on the hard drive.
That method is pretty cool, because it allows live updates of the shader at runtime. I’ll post a video tomorrow.
here is an example code to use it.
import com.jme3.app.ChaseCameraAppState;
import com.jme3.app.SimpleApplication;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.material.Material;
import com.jme3.post.FilterPostProcessor;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.system.AppSettings;
import com.jme3.util.MaterialDebugAppState;
public class TestMaterialHotReload extends SimpleApplication {
public static void main(String[] args) {
TestMaterialHotReload app = new TestMaterialHotReload();
//ignore this, it’s just for convenience
AppSettings settings = new AppSettings(true);
settings.setFrameRate(30);
settings.setResolution(640, 480);
app.setSettings(settings);
app.setShowSettings(false);
app.setPauseOnLostFocus(false);
app.start();
}
@Override
public void simpleInitApp() {
//a box geom that will have the material we want to test
final Geometry boxGeo = new Geometry("Box", new Box(1f, 1f, 1f));
rootNode.attachChild(boxGeo);
//create the material
final Material mat = createMaterial();
boxGeo.setMaterial(mat);
//ignore this, it’s just for convenience
flyCam.setEnabled(false);
ChaseCameraAppState chaseCam = new ChaseCameraAppState();
chaseCam.setTarget(boxGeo);
stateManager.attach(chaseCam);
MaterialDebugAppState matDebug = new MaterialDebugAppState();
// you can do that if you are lazy, it will be a bit slower, but it
//reload all the materials of the scenegraph when pressing R
matDebug.registerBinding(new KeyTrigger(KeyInput.KEY_R), rootNode);
//will reload boxgeo's material whenever the frag file is changed
matDebug.registerBinding("Shaders/distort.frag", boxGeo);
//will reload boxgeo's material whenever the vert file is changed
matDebug.registerBinding("Shaders/distort.vert", boxGeo);
FilterPostProcessor p = new FilterPostProcessor(assetManager);
//this is an example filter put your own
MyColorOverlayFilter f = new MyColorOverlayFilter();
p.addFilter(f);
viewPort.addProcessor(p);
//will reload the filter whenever the F key is hit
matDebug.registerBinding(new KeyTrigger(KeyInput.KEY_F), f);
//will reload filter whenever the frag file is changed
matDebug.registerBinding("MatDefs/Overlay15.frag", f);
//will reload filter whenever the frag file is changed
matDebug.registerBinding("MatDefs/Overlay.frag", f);
stateManager.attach(matDebug);
}
/**
* Creates the material, use whatever material that uses the shader you are
* coding
*
* @return
*/
protected Material createMaterial() {
//this is an example material. Put your own
Material mat = new Material(assetManager,"MatDefs/distort.j3md");
mat.setTexture("ColorMap", assetManager.loadTexture("com/jme3/app/Monkey.png"));
mat.setBoolean("UseDistort", true);
return mat;
}
}
have fun