Thanks for the tip. Here’s what I managed to do:

This is an image on the left, and a video on the right (video courtesy of FOBS code provided on this forum). Code, just in case anybody is interested:
import com.jme.bounding.BoundingBox;
import com.jme.bounding.BoundingSphere;
import com.jme.image.Texture;
import com.jme.math.Vector2f;
import com.jme.math.Vector3f;
import com.jme.renderer.ColorRGBA;
import com.jme.scene.Node;
import com.jme.scene.TriMesh;
import com.jme.scene.state.AlphaState;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jme.util.geom.BufferUtils;
public class ImageWithReflection extends Node {
private static final long serialVersionUID = -6228054783295086126L;
private int width;
private int height;
private DisplaySystem display;
private Texture tex;
public ImageWithReflection(String name, DisplaySystem display, String imageResource, int width, int height) {
this(name, display, ResourceManager.getImageURL(imageResource), width, height);
}
public ImageWithReflection(String name, DisplaySystem display, URL imageResource, int width, int height) {
super(name);
this.display = display;
this.width = width;
this.height = height;
createContent(imageResource);
}
private void createContent(URL imageResource) {
tex = TextureManager.loadTexture(imageResource,
Texture.MM_LINEAR,
Texture.FM_LINEAR);
TriMesh t = getUpperTriMesh();
t.setModelBound(new BoundingSphere());
t.updateModelBound();
// do the reflection
TriMesh t2 = getLowerTriMesh();
t2.setLocalTranslation(0f, -(height/2f), 0f);
t2.setModelBound(new BoundingSphere());
t2.updateModelBound();
AlphaState as1 = display.getRenderer().createAlphaState();
as1.setBlendEnabled(true);
as1.setSrcFunction(AlphaState.SB_SRC_ALPHA);
as1.setDstFunction(AlphaState.DB_ONE);
as1.setTestEnabled(true);
as1.setTestFunction(AlphaState.TF_GREATER);
as1.setEnabled(true);
t2.setRenderState(as1);
this.attachChild(t);
this.attachChild(t2);
}
private TriMesh getUpperTriMesh() {
TriMesh m=new TriMesh(getName() + "_image");
Vector3f[] vertexes={
new Vector3f(0,0,0),
new Vector3f(width,0,0),
new Vector3f(0,height,0),
new Vector3f(width,height,0)
};
ColorRGBA[] colors={
new ColorRGBA(1,1,1,1f),
new ColorRGBA(1,1,1,1f),
new ColorRGBA(1,1,1,1f),
new ColorRGBA(1,1,1,1f)
};
Vector2f[] texCoords={
new Vector2f(0,0),
new Vector2f(1,0),
new Vector2f(0,1),
new Vector2f(1,1)
};
int[] indexes={
0,1,2,1,2,3
};
m.reconstruct(
BufferUtils.createFloatBuffer(vertexes),
null,
BufferUtils.createFloatBuffer(colors),
BufferUtils.createFloatBuffer(texCoords),
BufferUtils.createIntBuffer(indexes));
TextureState ts=display.getRenderer().createTextureState();
ts.setTexture(tex);
m.setRenderState(ts);
m.setModelBound(new BoundingBox());
m.updateModelBound();
return m;
}
private TriMesh getLowerTriMesh() {
TriMesh m=new TriMesh(getName() + "_reflection");
Vector3f[] vertexes={
new Vector3f(0,0,0),
new Vector3f(width,0,0),
new Vector3f(0,height/2,0),
new Vector3f(width,height/2,0)
};
ColorRGBA[] colors={
new ColorRGBA(1,1,1,0.0f),
new ColorRGBA(1,1,1,0.0f),
new ColorRGBA(1,1,1,0.4f),
new ColorRGBA(1,1,1,0.4f)
};
Vector2f[] texCoords={
new Vector2f(0,1),
new Vector2f(1,1),
new Vector2f(0,0),
new Vector2f(1,0)
};
int[] indexes={
0,1,2,1,2,3
};
m.reconstruct(
BufferUtils.createFloatBuffer(vertexes),
null,
BufferUtils.createFloatBuffer(colors),
BufferUtils.createFloatBuffer(texCoords),
BufferUtils.createIntBuffer(indexes));
TextureState ts=display.getRenderer().createTextureState();
ts.setTexture(tex);
m.setRenderState(ts);
m.setModelBound(new BoundingBox());
m.updateModelBound();
return m;
}
}
Also, make sure lighting is switched on (L key?)