You could use OpenGL ES 2.0 mode and your own, custom shader. That's fast but more complicated. You could write a VertexController in Java and let it animate the plane. That's easier (at least IMHO), but slower. For the latter, i've this example for desktop jPCT, but it should be pretty easy to port it to jPCT-AE:
import com.threed.jpct.Config;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.GenericVertexController;
import com.threed.jpct.IRenderer;
import com.threed.jpct.Matrix;
import com.threed.jpct.Object3D;
import com.threed.jpct.Primitives;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.Texture;
import com.threed.jpct.TextureInfo;
import com.threed.jpct.TextureManager;
import com.threed.jpct.World;
import com.threed.jpct.util.Light;
public class CheapWater {
private World world;
private FrameBuffer buffer;
private Object3D water;
private WaterController wc = null;
public static void main(String[] args) throws Exception {
new CheapWater().loop();
}
public CheapWater() throws Exception {
world = new World();
world.setAmbientLight(100, 100, 100);
TextureManager tm = TextureManager.getInstance();
tm.addTexture("water", new Texture("water3.jpg"));
water = Primitives.getPlane(40, 1.5f);
water.setTexture("water");
water.rotateX((float) Math.PI / 2f);
water.rotateMesh();
water.clearRotation();
water.compile(true);
water.build();
water.setTransparency(2);
wc = new WaterController(water, 5, 10, false);
water.getMesh().setVertexController(wc, false);
world.addObject(water);
world.getCamera().setPosition(0, -50, -50);
world.getCamera().lookAt(water.getTransformedCenter());
Light light = new Light(world);
light.setAttenuation(-1);
light.setIntensity(255, 255, 255);
light.setPosition(new SimpleVector(100, -50, -20));
}
private void loop() throws Exception {
buffer = new FrameBuffer(800, 600, FrameBuffer.SAMPLINGMODE_GL_AA_2X);
buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
buffer.enableRenderer(IRenderer.RENDERER_OPENGL);
long s = System.nanoTime() / 1000000L;
long ss = System.currentTimeMillis();
int fps = 0;
while (!org.lwjgl.opengl.Display.isCloseRequested()) {
buffer.clear(java.awt.Color.BLUE);
long ticks = ((System.nanoTime() / 1000000L) - s) / 10;
if (ticks > 0) {
s = System.nanoTime() / 1000000L;
wc.update(0.5f * (float) ticks);
water.getMesh().applyVertexController();
}
world.renderScene(buffer);
world.draw(buffer);
buffer.update();
buffer.displayGLOnly();
fps++;
if (System.currentTimeMillis() - ss >= 1000) {
System.out.println(fps + "fps");
fps = 0;
ss = System.currentTimeMillis();
}
}
buffer.disableRenderer(IRenderer.RENDERER_OPENGL);
buffer.dispose();
System.exit(0);
}
private static class WaterController extends GenericVertexController {
private float scale = 0;
private float damping = 0;
private SimpleVector[] preCalcNormals = null;
private SimpleVector[] preCalcNormalsNeg = null;
private float[] lastHeight = null;
private static final long serialVersionUID = 1L;
private float degreeAdd = 0;
private Object3D water = null;
private float lastUpdate = 0;
private boolean realNormals = false;
public WaterController(Object3D water, float scale, float damping, boolean realNormals) {
this.scale = scale;
this.water = water;
this.realNormals = realNormals;
this.damping = damping;
water.setTextureMatrix(new Matrix());
}
/**
* This calculates some normals...these are rather fake and in no way
* comparable to real surface normals. But they should do the trick...
*/
public boolean setup() {
SimpleVector ax = new SimpleVector(-1, 0, 1).normalize();
preCalcNormals = new SimpleVector[(int) (100f * scale)];
preCalcNormalsNeg = new SimpleVector[(int) (100f * scale)];
int end = preCalcNormals.length;
for (int i = 0; i < end; i++) {
float height = -1f + (((float) i) / (end / 2f));
SimpleVector n = new SimpleVector(0, -1, 0);
SimpleVector n2 = new SimpleVector(0, -1, 0);
Matrix m = new Matrix();
Matrix m2 = new Matrix();
if (height <= 0) {
float val = (float) Math.sqrt((height + 1) * (Math.PI / 7f));
m.rotateAxis(ax, val);
m2.rotateAxis(ax, -val);
} else {
float val = (float) Math.sqrt((1 - height) * (Math.PI / 7f));
m.rotateAxis(ax, val);
m2.rotateAxis(ax, -val);
}
n.rotate(m);
n2.rotate(m);
preCalcNormals[i] = n;
preCalcNormalsNeg[i] = n2;
}
SimpleVector[] source = this.getSourceMesh();
lastHeight = new float[source.length];
for (int i = 0; i < source.length; i++) {
lastHeight[i] = 0;
}
return true;
}
public void update(float inc) {
degreeAdd += inc;
lastUpdate = inc;
}
public void apply() {
SimpleVector[] source = this.getSourceMesh();
SimpleVector[] dest = this.getDestinationMesh();
SimpleVector[] destNormals = this.getDestinationNormals();
int end = source.length;
int nEnd = preCalcNormals.length;
for (int i = 0; i < end; i++) {
SimpleVector s = source[i];
SimpleVector d = dest[i];
float sin = (float) Math.sin((degreeAdd + s.x + s.z) / damping);
d.set(s.x, s.y + sin * scale, s.z);
int iHeight = (int) ((sin + 1) * (nEnd / 2));
if (lastHeight[i] > sin) {
destNormals[i].set(preCalcNormalsNeg[iHeight]);
} else {
destNormals[i].set(preCalcNormals[iHeight]);
}
lastHeight[i] = sin;
}
water.touch();
if (realNormals) {
water.calcNormals();
}
float tr = lastUpdate / 233f;
water.getTextureMatrix().translate(tr, -tr, 0);
}
}
}