summaryrefslogtreecommitdiffstats
path: root/src/demos/proceduralTexturePhysics
diff options
context:
space:
mode:
Diffstat (limited to 'src/demos/proceduralTexturePhysics')
-rw-r--r--src/demos/proceduralTexturePhysics/ProceduralTexturePhysics.java383
-rw-r--r--src/demos/proceduralTexturePhysics/Water.java2055
2 files changed, 2438 insertions, 0 deletions
diff --git a/src/demos/proceduralTexturePhysics/ProceduralTexturePhysics.java b/src/demos/proceduralTexturePhysics/ProceduralTexturePhysics.java
new file mode 100644
index 0000000..a27ae05
--- /dev/null
+++ b/src/demos/proceduralTexturePhysics/ProceduralTexturePhysics.java
@@ -0,0 +1,383 @@
+/*
+ * Portions Copyright (C) 2003 Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+/*
+ *
+ * COPYRIGHT NVIDIA CORPORATION 2003. ALL RIGHTS RESERVED.
+ * BY ACCESSING OR USING THIS SOFTWARE, YOU AGREE TO:
+ *
+ * 1) ACKNOWLEDGE NVIDIA'S EXCLUSIVE OWNERSHIP OF ALL RIGHTS
+ * IN AND TO THE SOFTWARE;
+ *
+ * 2) NOT MAKE OR DISTRIBUTE COPIES OF THE SOFTWARE WITHOUT
+ * INCLUDING THIS NOTICE AND AGREEMENT;
+ *
+ * 3) ACKNOWLEDGE THAT TO THE MAXIMUM EXTENT PERMITTED BY
+ * APPLICABLE LAW, THIS SOFTWARE IS PROVIDED *AS IS* AND
+ * THAT NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED
+ * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * IN NO EVENT SHALL NVIDIA OR ITS SUPPLIERS BE LIABLE FOR ANY
+ * SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES
+ * WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS
+ * OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS
+ * INFORMATION, OR ANY OTHER PECUNIARY LOSS), INCLUDING ATTORNEYS'
+ * FEES, RELATING TO THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+package demos.proceduralTexturePhysics;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import net.java.games.jogl.*;
+import demos.util.*;
+import gleem.*;
+import gleem.linalg.*;
+
+/**
+ * Water demonstration by NVidia Corporation - <a href =
+ * "http://developer.nvidia.com/view.asp?IO=ogl_dynamic_bumpreflection">http://developer.nvidia.com/view.asp?IO=ogl_dynamic_bumpreflection</a>
+ *
+ * <P>
+ *
+ * Demonstrates pbuffers, vertex programs, register combiners
+ *
+ * <P>
+ *
+ * Ported to Java by Kenneth Russell
+ *
+ */
+
+public class ProceduralTexturePhysics {
+ private static volatile boolean quit;
+
+ private volatile boolean drawing;
+ private volatile int mousePosX;
+ private volatile int mousePosY;
+
+ private Dimension dim = new Dimension();
+ private GLCanvas canvas;
+ private Water water;
+ private volatile ExaminerViewer viewer;
+ private boolean[] b = new boolean[256];
+ private boolean doViewAll = true;
+ private float zNear = 0.1f;
+ private float zFar = 10.0f;
+
+ private DurationTimer timer = new DurationTimer();
+ private boolean firstRender = true;
+ private int frameCount;
+
+ public static void main(String[] args) {
+ new ProceduralTexturePhysics().run(args);
+ }
+
+ public void run(String[] args) {
+ canvas = GLDrawableFactory.getFactory().createGLCanvas(new GLCapabilities());
+ canvas.addGLEventListener(new Listener());
+ canvas.setNoAutoRedrawMode(true);
+
+ Frame frame = new Frame("Procedural Texture Waves");
+ frame.setLayout(new BorderLayout());
+ canvas.setSize(512, 512);
+ frame.add(canvas, BorderLayout.CENTER);
+ frame.pack();
+ frame.show();
+ canvas.requestFocus();
+
+ frame.addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ quit = true;
+ }
+ });
+
+ water = new Water();
+ water.initialize("demos/data/images/nvfixed.tga",
+ "demos/data/images/nvspin.tga",
+ "demos/data/images/droplet.tga",
+ "demos/data/cubemaps/CloudyHills_{0}.tga",
+ canvas);
+
+ while (!quit) {
+ if (viewer != null) {
+ try {
+ if (drawing) {
+ canvas.getSize(dim);
+ water.addDroplet(new Water.Droplet( 2 * (mousePosX / (float) dim.width - 0.5f),
+ -2 * (mousePosY / (float) dim.height - 0.5f),
+ 0.08f));
+ }
+ water.tick();
+ canvas.display();
+ } catch (GLException e) {
+ // Have seen spurious exceptions getting thrown during SwapBuffers.
+ // Not sure why at this time; disabling of repaint() should prevent
+ // AWT thread from getting involved. Continue animating anyway.
+ e.printStackTrace();
+ }
+ } else {
+ // Make the pbuffer get created
+ canvas.display();
+ }
+ }
+
+ System.exit(0);
+ }
+
+ //----------------------------------------------------------------------
+ // Internals only below this point
+ //
+
+ private void setFlag(char key, boolean val) {
+ b[((int) key) & 0xFF] = val;
+ }
+
+ private boolean getFlag(char key) {
+ return b[((int) key) & 0xFF];
+ }
+
+ class Listener implements GLEventListener {
+ private float blurIncrement = 0.01f;
+ private float bumpIncrement = 0.01f;
+ private float frequencyIncrement = 0.1f;
+
+ public void init(GLDrawable drawable) {
+ GL gl = drawable.getGL();
+
+ try {
+ checkExtension(gl, "GL_ARB_multitexture");
+ checkExtension(gl, "GL_NV_vertex_program");
+ checkExtension(gl, "GL_NV_texture_shader");
+ checkExtension(gl, "GL_NV_register_combiners");
+ checkExtension(gl, "GL_ARB_pbuffer");
+ checkExtension(gl, "GL_ARB_pixel_format");
+ } catch (GLException e) {
+ e.printStackTrace();
+ quit = true;
+ throw(e);
+ }
+
+ gl.glClearColor(0, 0.2f, 0.5f, 0);
+ gl.glDisable(GL.GL_LIGHTING);
+ gl.glDisable(GL.GL_DEPTH_TEST);
+ gl.glDisable(GL.GL_CULL_FACE);
+
+ // Register the window with the ManipManager
+ ManipManager manager = ManipManager.getManipManager();
+ manager.registerWindow(drawable);
+
+ viewer = new ExaminerViewer(MouseButtonHelper.numMouseButtons());
+ viewer.attach(drawable, new BSphereProvider() {
+ public BSphere getBoundingSphere() {
+ return new BSphere(new Vec3f(0, 0, 0), 1.2f);
+ }
+ });
+ viewer.setVertFOV((float) (15.0f * Math.PI / 32.0f));
+ viewer.setZNear(zNear);
+ viewer.setZFar(zFar);
+
+ drawable.addKeyListener(new KeyAdapter() {
+ public void keyPressed(KeyEvent e) {
+ dispatchKey(e.getKeyChar());
+ }
+ });
+
+ drawable.addMouseListener(new MouseAdapter() {
+ public void mousePressed(MouseEvent e) {
+ if (e.getButton() == MouseEvent.BUTTON1 &&
+ !e.isAltDown() && !e.isMetaDown()) {
+ drawing = true;
+ }
+ }
+
+ public void mouseReleased(MouseEvent e) {
+ if (e.getButton() == MouseEvent.BUTTON1) {
+ drawing = false;
+ }
+ }
+ });
+
+ drawable.addMouseMotionListener(new MouseMotionAdapter() {
+ public void mouseDragged(MouseEvent e) {
+ mousePosX = e.getX();
+ mousePosY = e.getY();
+ }
+ });
+ }
+
+ public void display(GLDrawable drawable) {
+ if (water == null) {
+ return;
+ }
+
+ if (quit) {
+ return;
+ }
+
+ if (!firstRender) {
+ if (++frameCount == 30) {
+ timer.stop();
+ System.err.println("Frames per second: " + (30.0f / timer.getDurationAsSeconds()));
+ timer.reset();
+ timer.start();
+ frameCount = 0;
+ }
+ } else {
+ firstRender = false;
+ timer.start();
+ }
+
+ GL gl = drawable.getGL();
+ GLU glu = drawable.getGLU();
+
+ gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
+
+ if (doViewAll) {
+ viewer.viewAll(gl);
+ doViewAll = false;
+ }
+
+ viewer.update(gl);
+ ManipManager.getManipManager().updateCameraParameters(drawable, viewer.getCameraParameters());
+ ManipManager.getManipManager().render(drawable, gl);
+
+ CameraParameters params = viewer.getCameraParameters();
+ water.draw(gl, params.getOrientation().inverse());
+ }
+
+ public void reshape(GLDrawable drawable, int x, int y, int width, int height) {}
+
+ // Unused routines
+ public void displayChanged(GLDrawable drawable, boolean modeChanged, boolean deviceChanged) {}
+
+ //----------------------------------------------------------------------
+ // Internals only below this point
+ //
+
+ private void checkExtension(GL gl, String extensionName) {
+ if (!gl.isExtensionAvailable(extensionName)) {
+ throw new GLException("Unable to initialize " + extensionName + " OpenGL extension");
+ }
+ }
+
+ private void dispatchKey(char k) {
+ setFlag(k, !getFlag(k));
+
+ if ((k == (char) 27) || (k == 'q')) {
+ quit = true;
+ return;
+ }
+
+ switch (k) {
+ case 27:
+ case 'q':
+ quit = true;
+ break;
+ case 'w':
+ water.enableWireframe(getFlag('w'));
+ break;
+ case 'd':
+ // FIXME
+ /*
+
+ if (getKey('d')) {
+ glutMouseFunc(glh::glut_mouse_function);
+ glutMotionFunc(glh::glut_motion_function);
+ }
+ else
+ {
+ glutMouseFunc(Mouse);
+ glutMotionFunc(Motion);
+ }
+ */
+ break;
+ case ' ':
+ water.enableAnimation(getFlag(' '));
+ break;
+ case 'b':
+ water.enableBorderWrapping(getFlag('b'));
+ break;
+ case 'n':
+ water.singleStep();
+ break;
+ case 's':
+ water.enableSlowAnimation(getFlag('s'));
+ break;
+ case '1':
+ water.setRenderMode(Water.CA_FULLSCREEN_REFLECT);
+ break;
+ case '2':
+ water.setRenderMode(Water.CA_FULLSCREEN_HEIGHT);
+ break;
+ case '3':
+ water.setRenderMode(Water.CA_FULLSCREEN_FORCE);
+ break;
+ case '4':
+ water.setRenderMode(Water.CA_FULLSCREEN_NORMALMAP);
+ break;
+ case '5':
+ water.setRenderMode(Water.CA_TILED_THREE_WINDOWS);
+ break;
+ case 'r':
+ water.reset();
+ break;
+ case 'i':
+ // FIXME: make sure this is what this does
+ doViewAll = true;
+ // gluPerspective(90, 1, .01, 10);
+ break;
+ case 'c': {
+ float dist = water.getBlurDistance();
+ if (dist > blurIncrement)
+ water.setBlurDistance(dist - blurIncrement);
+ break;
+ }
+ case 'v': {
+ float dist = water.getBlurDistance();
+ if (dist < 1)
+ water.setBlurDistance(dist + blurIncrement);
+ break;
+ }
+ case '-': {
+ float scale = water.getBumpScale();
+ if (scale > -1)
+ water.setBumpScale(scale - bumpIncrement);
+ break;
+ }
+ case '=': {
+ float scale = water.getBumpScale();
+ if (scale < 1)
+ water.setBumpScale(scale + bumpIncrement);
+ break;
+ }
+ case 'l':
+ water.enableBoundaryApplication(getFlag('l'));
+ break;
+ case 'o':
+ water.enableSpinningLogo(getFlag('o'));
+ break;
+ case '.': {
+ float frequency = water.getBumpScale();
+ if (frequency < 1)
+ water.setDropFrequency(frequency + frequencyIncrement);
+ break;
+ }
+ case ',': {
+ float frequency = water.getBumpScale();
+ if (frequency > 0)
+ water.setDropFrequency(frequency - frequencyIncrement);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/src/demos/proceduralTexturePhysics/Water.java b/src/demos/proceduralTexturePhysics/Water.java
new file mode 100644
index 0000000..6a52d15
--- /dev/null
+++ b/src/demos/proceduralTexturePhysics/Water.java
@@ -0,0 +1,2055 @@
+/*
+ * Portions Copyright (C) 2003 Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+/*
+ *
+ * COPYRIGHT NVIDIA CORPORATION 2003. ALL RIGHTS RESERVED.
+ * BY ACCESSING OR USING THIS SOFTWARE, YOU AGREE TO:
+ *
+ * 1) ACKNOWLEDGE NVIDIA'S EXCLUSIVE OWNERSHIP OF ALL RIGHTS
+ * IN AND TO THE SOFTWARE;
+ *
+ * 2) NOT MAKE OR DISTRIBUTE COPIES OF THE SOFTWARE WITHOUT
+ * INCLUDING THIS NOTICE AND AGREEMENT;
+ *
+ * 3) ACKNOWLEDGE THAT TO THE MAXIMUM EXTENT PERMITTED BY
+ * APPLICABLE LAW, THIS SOFTWARE IS PROVIDED *AS IS* AND
+ * THAT NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES,
+ * EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED
+ * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * IN NO EVENT SHALL NVIDIA OR ITS SUPPLIERS BE LIABLE FOR ANY
+ * SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES
+ * WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS
+ * OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS
+ * INFORMATION, OR ANY OTHER PECUNIARY LOSS), INCLUDING ATTORNEYS'
+ * FEES, RELATING TO THE USE OF OR INABILITY TO USE THIS SOFTWARE,
+ * EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ */
+
+package demos.proceduralTexturePhysics;
+
+import java.awt.Image;
+import java.awt.image.*;
+import java.io.*;
+import java.text.*;
+import java.util.*;
+
+import gleem.linalg.*;
+import net.java.games.jogl.*;
+import demos.util.*;
+
+/**
+ * Auxiliary Water simulation class used by ProceduralTexturePhysics
+ * main loop. Demonstration by NVidia Corporation.
+ *
+ * <P>
+ *
+ * Ported to Java by Kenneth Russell
+ */
+
+public class Water {
+ // Note: this class is organized differently than most of the demos
+ // due to the fact that it is used for two purposes: when the
+ // pbuffer's context is current it is used to update the cellular
+ // automata, and when the parent drawable's context is current it is
+ // used to render the water geometry (with the parent drawable's GL
+ // object).
+
+ // Rendering modes
+ public static final int CA_FULLSCREEN_REFLECT = 0;
+ public static final int CA_FULLSCREEN_FORCE = 1;
+ public static final int CA_FULLSCREEN_HEIGHT = 2;
+ public static final int CA_FULLSCREEN_NORMALMAP = 3;
+ public static final int CA_TILED_THREE_WINDOWS = 4;
+ public static final int CA_DO_NOT_RENDER = 5;
+
+ private int[] initialMapDimensions = new int[2];
+ private TGAImage initialMap;
+
+ private String tmpSpinFilename;
+ private String tmpDropletFilename;
+ private String tmpCubeMapFilenamePattern;
+
+ private GLDrawable pbuffer;
+ private Rotf cameraOrientation = new Rotf();
+
+ // Static texture names
+ private static final int CA_TEXTURE_INITIAL_MAP = 0;
+ private static final int CA_TEXTURE_SPIN = 1;
+ private static final int CA_TEXTURE_DROPLET = 2;
+ private static final int CA_TEXTURE_CUBEMAP = 3;
+ private static final int CA_NUM_STATIC_TEXTURES = 4;
+
+ // Dynamic texture names
+ private static final int CA_TEXTURE_FORCE_INTERMEDIATE = 0;
+ private static final int CA_TEXTURE_FORCE_TARGET = 1;
+ private static final int CA_TEXTURE_VELOCITY_SOURCE = 2;
+ private static final int CA_TEXTURE_VELOCITY_TARGET = 3;
+ private static final int CA_TEXTURE_HEIGHT_SOURCE = 4;
+ private static final int CA_TEXTURE_HEIGHT_TARGET = 5;
+ private static final int CA_TEXTURE_NORMAL_MAP = 6;
+ private static final int CA_NUM_DYNAMIC_TEXTURES = 7;
+
+ // List names
+ private static final int CA_REGCOMBINER_EQ_WEIGHT_COMBINE = 0;
+ private static final int CA_REGCOMBINER_NEIGHBOR_FORCE_CALC_1 = 1;
+ private static final int CA_REGCOMBINER_NEIGHBOR_FORCE_CALC_2 = 2;
+ private static final int CA_REGCOMBINER_APPLY_FORCE = 3;
+ private static final int CA_REGCOMBINER_APPLY_VELOCITY = 4;
+ private static final int CA_REGCOMBINER_CREATE_NORMAL_MAP = 5;
+ private static final int CA_TEXTURE_SHADER_REFLECT = 6;
+ private static final int CA_DRAW_SCREEN_QUAD = 7;
+ private static final int CA_NUM_LISTS = 8;
+
+ private int[] staticTextureIDs = new int[CA_NUM_STATIC_TEXTURES];
+ private int[] dynamicTextureIDs = new int[CA_NUM_DYNAMIC_TEXTURES];
+
+ private int texHeightInput; // current input height texture ID.
+ private int texHeightOutput; // current output height texture ID.
+ private int texVelocityInput; // current input velocity texture ID.
+ private int texVelocityOutput; // current output velocity texture ID.
+ private int texForceStepOne; // intermediate force computation result texture ID.
+ private int texForceOutput; // current output force texture ID.
+
+ private int[] displayListIDs = new int[CA_NUM_LISTS];
+
+ private int vertexProgramID; // one vertex shader is used to choose the texcoord offset
+
+ private int flipState; // used to flip target texture configurations.
+
+ private boolean wrap; // CA can either wrap its borders, or clamp (clamp by default)
+ private boolean reset = true; // are we resetting this frame? (user hit reset).
+ private boolean singleStep; // animation step on keypress.
+ private boolean animate = true; // continuous animation.
+ private boolean slow = true; // run slow.
+ private boolean wireframe; // render in wireframe mode
+ private boolean applyInteriorBoundaries = true; // enable / disable "boundary" image drawing.
+ private boolean spinLogo = true; // draw spinning logo.
+ private boolean createNormalMap = true; // enable / disable normal map creation.
+
+ private float perTexelWidth; // width of a texel (percentage of texture)
+ private float perTexelHeight; // height of a texel
+
+ private float blurDist = 0.5f; // distance over which to blur.
+ private boolean mustUpdateBlurOffsets; // flag indicating blurDist was set last tick
+
+ private float normalSTScale = 0.8f; // scale of normals in normal map.
+ private float bumpScale = 0.25f; // scale of bumps in water.
+
+ private float dropletFrequency = 0.175f; // frequency at which droplets are drawn in water...
+
+ private int slowDelay = 15; // amount (milliseconds) to delay when running slow.
+ private int skipInterval; // frames to skip simulation.
+ private int skipCount; // frame count for skipping rendering
+
+ private int angle; // angle in degrees for spinning logo
+
+ private List/*<Droplet>*/ droplets = new ArrayList/*<Droplet>*/(); // array of droplets
+
+ private int renderMode;
+
+ // Constant memory locations
+ private static final int CV_WORLDVIEWPROJ_0 = 0;
+ private static final int CV_WORLDVIEWPROJ_1 = 1;
+ private static final int CV_WORLDVIEWPROJ_2 = 2;
+ private static final int CV_WORLDVIEWPROJ_3 = 3;
+
+ private static final int CV_UV_OFFSET_TO_USE = 4;
+
+ private static final int CV_UV_T0_NO_OFFSET = 8;
+ private static final int CV_UV_T0_TYPE1 = 9;
+ private static final int CV_UV_T0_TYPE2 = 10;
+ private static final int CV_UV_T0_TYPE3 = 11;
+ private static final int CV_UV_T0_TYPE4 = 12;
+
+ private static final int CV_UV_T1_NO_OFFSET = 13;
+ private static final int CV_UV_T1_TYPE1 = 14;
+ private static final int CV_UV_T1_TYPE2 = 15;
+ private static final int CV_UV_T1_TYPE3 = 16;
+ private static final int CV_UV_T1_TYPE4 = 17;
+
+ private static final int CV_UV_T2_NO_OFFSET = 18;
+ private static final int CV_UV_T2_TYPE1 = 19;
+ private static final int CV_UV_T2_TYPE2 = 20;
+ private static final int CV_UV_T2_TYPE3 = 21;
+ private static final int CV_UV_T2_TYPE4 = 22;
+
+ private static final int CV_UV_T3_NO_OFFSET = 23;
+ private static final int CV_UV_T3_TYPE1 = 24;
+ private static final int CV_UV_T3_TYPE2 = 25;
+ private static final int CV_UV_T3_TYPE3 = 26;
+ private static final int CV_UV_T3_TYPE4 = 27;
+
+ private static final int CV_CONSTS_1 = 28;
+
+ public void initialize(String initialMapFilename,
+ String spinFilename,
+ String dropletFilename,
+ String cubeMapFilenamePattern,
+ GLDrawable parentWindow) {
+ loadInitialTexture(initialMapFilename);
+ tmpSpinFilename = spinFilename;
+ tmpDropletFilename = dropletFilename;
+ tmpCubeMapFilenamePattern = cubeMapFilenamePattern;
+
+ // create the pbuffer. Will use this as an offscreen rendering buffer.
+ // it allows rendering a texture larger than our window.
+ if (!parentWindow.canCreateOffscreenDrawable()) {
+ throw new GLException("Parent window doesn't support creation of pbuffers");
+ }
+ GLCapabilities caps = new GLCapabilities();
+ caps.setDoubleBuffered(false);
+ pbuffer = parentWindow.createOffscreenDrawable(caps,
+ initialMapDimensions[0],
+ initialMapDimensions[1]);
+ pbuffer.addGLEventListener(new Listener());
+ }
+
+ public void tick() {
+ pbuffer.display();
+ }
+
+ public void draw(GL gl, Rotf cameraOrientation) {
+ this.cameraOrientation.set(cameraOrientation);
+
+ if (skipCount >= skipInterval && renderMode != CA_DO_NOT_RENDER) {
+ skipCount = 0;
+ // Display the results of the rendering to texture
+ if (wireframe) {
+ gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_LINE);
+
+ // chances are the texture will be all dark, so lets not use a texture
+ gl.glDisable(GL.GL_TEXTURE_2D);
+ } else {
+ gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL);
+
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glEnable(GL.GL_TEXTURE_2D);
+ }
+
+ switch (renderMode) {
+ case CA_FULLSCREEN_REFLECT: {
+ // include bump scale...
+ Mat4f bscale = new Mat4f();
+ bscale.makeIdent();
+ bscale.set(0, 0, bumpScale);
+ bscale.set(1, 1, bumpScale);
+ Mat4f rot = new Mat4f();
+ rot.makeIdent();
+ rot.setRotation(cameraOrientation);
+ Mat4f matRot = rot.mul(bscale);
+
+ gl.glCallList(displayListIDs[CA_TEXTURE_SHADER_REFLECT]);
+
+ // Draw quad over full display
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, dynamicTextureIDs[CA_TEXTURE_NORMAL_MAP]);
+ gl.glDisable(GL.GL_TEXTURE_2D);
+ gl.glActiveTextureARB(GL.GL_TEXTURE3_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_CUBE_MAP_ARB, staticTextureIDs[CA_TEXTURE_CUBEMAP]);
+ gl.glEnable(GL.GL_TEXTURE_2D);
+
+ gl.glColor4f(1, 1, 1, 1);
+ gl.glBegin(GL.GL_QUADS);
+
+ gl.glMultiTexCoord2fARB(GL.GL_TEXTURE0_ARB, 0,0);
+ gl.glMultiTexCoord4fARB(GL.GL_TEXTURE1_ARB, matRot.get(0,0), matRot.get(0,1), matRot.get(0,2), 1);
+ gl.glMultiTexCoord4fARB(GL.GL_TEXTURE2_ARB, matRot.get(1,0), matRot.get(1,1), matRot.get(1,2), 1);
+ gl.glMultiTexCoord4fARB(GL.GL_TEXTURE3_ARB, matRot.get(2,0), matRot.get(2,1), matRot.get(2,2), 1);
+ gl.glVertex2f(-1,-1);
+
+ gl.glMultiTexCoord2fARB(GL.GL_TEXTURE0_ARB, 1,0);
+ gl.glMultiTexCoord4fARB(GL.GL_TEXTURE1_ARB, matRot.get(0,0), matRot.get(0,1), matRot.get(0,2), -1);
+ gl.glMultiTexCoord4fARB(GL.GL_TEXTURE2_ARB, matRot.get(1,0), matRot.get(1,1), matRot.get(1,2), 1);
+ gl.glMultiTexCoord4fARB(GL.GL_TEXTURE3_ARB, matRot.get(2,0), matRot.get(2,1), matRot.get(2,2), 1);
+ gl.glVertex2f( 1,-1);
+
+ gl.glMultiTexCoord2fARB(GL.GL_TEXTURE0_ARB, 1,1);
+ gl.glMultiTexCoord4fARB(GL.GL_TEXTURE1_ARB, matRot.get(0,0), matRot.get(0,1), matRot.get(0,2), -1);
+ gl.glMultiTexCoord4fARB(GL.GL_TEXTURE2_ARB, matRot.get(1,0), matRot.get(1,1), matRot.get(1,2), -1);
+ gl.glMultiTexCoord4fARB(GL.GL_TEXTURE3_ARB, matRot.get(2,0), matRot.get(2,1), matRot.get(2,2), 1);
+ gl.glVertex2f( 1, 1);
+
+ gl.glMultiTexCoord2fARB(GL.GL_TEXTURE0_ARB, 0,1);
+ gl.glMultiTexCoord4fARB(GL.GL_TEXTURE1_ARB, matRot.get(0,0), matRot.get(0,1), matRot.get(0,2), 1);
+ gl.glMultiTexCoord4fARB(GL.GL_TEXTURE2_ARB, matRot.get(1,0), matRot.get(1,1), matRot.get(1,2), -1);
+ gl.glMultiTexCoord4fARB(GL.GL_TEXTURE3_ARB, matRot.get(2,0), matRot.get(2,1), matRot.get(2,2), 1);
+ gl.glVertex2f(-1, 1);
+
+ gl.glEnd();
+
+ gl.glDisable(GL.GL_TEXTURE_SHADER_NV);
+ gl.glDisable(GL.GL_REGISTER_COMBINERS_NV);
+
+ break;
+ }
+
+ case CA_FULLSCREEN_NORMALMAP: {
+ // Draw quad over full display
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, dynamicTextureIDs[CA_TEXTURE_NORMAL_MAP]);
+
+ gl.glCallList(displayListIDs[CA_DRAW_SCREEN_QUAD]);
+ break;
+ }
+
+ case CA_FULLSCREEN_HEIGHT: {
+ // Draw quad over full display
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, texHeightOutput);
+
+ gl.glCallList(displayListIDs[CA_DRAW_SCREEN_QUAD]);
+ break;
+ }
+
+ case CA_FULLSCREEN_FORCE: {
+ // Draw quad over full display
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, dynamicTextureIDs[CA_TEXTURE_FORCE_TARGET]);
+
+ gl.glCallList(displayListIDs[CA_DRAW_SCREEN_QUAD]);
+ break;
+ }
+
+ case CA_TILED_THREE_WINDOWS: {
+ // Draw quad over full display
+ // lower left
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, dynamicTextureIDs[CA_TEXTURE_FORCE_TARGET]);
+ gl.glMatrixMode(GL.GL_MODELVIEW);
+ gl.glPushMatrix();
+
+ gl.glTranslatef(-0.5f, -0.5f, 0);
+ gl.glScalef(0.5f, 0.5f, 1);
+ gl.glCallList(displayListIDs[CA_DRAW_SCREEN_QUAD]);
+ gl.glPopMatrix();
+
+ // lower right
+ gl.glBindTexture(GL.GL_TEXTURE_2D, texVelocityOutput);
+ gl.glPushMatrix();
+
+ gl.glTranslatef(0.5f, -0.5f, 0);
+ gl.glScalef(0.5f, 0.5f, 1);
+ gl.glCallList(displayListIDs[CA_DRAW_SCREEN_QUAD]);
+ gl.glPopMatrix();
+
+ // upper left
+ gl.glBindTexture(GL.GL_TEXTURE_2D, dynamicTextureIDs[CA_TEXTURE_NORMAL_MAP]);
+ gl.glMatrixMode(GL.GL_MODELVIEW);
+ gl.glPushMatrix();
+
+ gl.glTranslatef(-0.5f, 0.5f, 0);
+ gl.glScalef(0.5f, 0.5f, 1);
+ gl.glCallList(displayListIDs[CA_DRAW_SCREEN_QUAD]);
+ gl.glPopMatrix();
+
+ // upper right
+ gl.glBindTexture(GL.GL_TEXTURE_2D, texHeightOutput);
+ gl.glMatrixMode(GL.GL_MODELVIEW);
+ gl.glPushMatrix();
+
+ gl.glTranslatef(0.5f, 0.5f, 0);
+ gl.glScalef(0.5f, 0.5f, 1);
+ gl.glCallList(displayListIDs[CA_DRAW_SCREEN_QUAD]);
+ gl.glPopMatrix();
+
+ break;
+ }
+ }
+ } else {
+ // skip rendering this frame
+ skipCount++;
+ }
+ }
+
+ public void singleStep() { singleStep = true; }
+ public void enableAnimation(boolean enable) { animate = enable; }
+ public void enableSlowAnimation(boolean enable) { slow = enable; }
+ public void reset() { reset = true; }
+ public void setRenderMode(int mode) { renderMode = mode; }
+
+ public void enableWireframe(boolean enable) { wireframe = enable; }
+ public void enableBorderWrapping(boolean enable) { wrap = enable; }
+
+ public void enableBoundaryApplication(boolean enable) { applyInteriorBoundaries = enable; }
+ public void enableSpinningLogo(boolean enable) { spinLogo = enable; }
+
+ public void setBlurDistance(float distance) { blurDist = distance;
+ mustUpdateBlurOffsets = true; }
+ public float getBlurDistance() { return blurDist; }
+
+ public void setBumpScale(float scale) { bumpScale = scale; }
+ public float getBumpScale() { return bumpScale; }
+
+ public void setDropFrequency(float frequency) { dropletFrequency = frequency; }
+ public float getDropFrequency() { return dropletFrequency; }
+
+ public static class Droplet {
+ private float rX;
+ private float rY;
+ private float rScale;
+
+ Droplet(float rX, float rY, float rScale) {
+ this.rX = rX;
+ this.rY = rY;
+ this.rScale = rScale;
+ }
+
+ float rX() { return rX; }
+ float rY() { return rY; }
+ float rScale() { return rScale; }
+ }
+
+ public synchronized void addDroplet(Droplet drop) {
+ droplets.add(drop);
+ }
+
+ //----------------------------------------------------------------------
+ // Internals only below this point
+ //
+
+ class Listener implements GLEventListener {
+ public void init(GLDrawable drawable) {
+ GL gl = drawable.getGL();
+ GLU glu = drawable.getGLU();
+
+ initOpenGL(gl, glu);
+ }
+
+ public void display(GLDrawable drawable) {
+ GL gl = drawable.getGL();
+ if (mustUpdateBlurOffsets) {
+ updateBlurVertOffset(gl);
+ mustUpdateBlurOffsets = false;
+ }
+
+ // Take a single step in the cellular automaton
+
+ // Disable culling
+ gl.glDisable(GL.GL_CULL_FACE);
+
+ if (reset) {
+ reset = false;
+ flipState = 0;
+ }
+
+ if (animate) {
+ // Update the textures for one step of the simulation
+ doSingleTimeStep(gl);
+ } else if (singleStep) {
+ doSingleTimeStep(gl);
+ singleStep = false;
+ }
+
+ if (slow && (slowDelay > 0) ) {
+ try {
+ Thread.sleep(slowDelay);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ public void reshape(GLDrawable drawable, int x, int y, int width, int height) {}
+
+ // Unused routines
+ public void displayChanged(GLDrawable drawable, boolean modeChanged, boolean deviceChanged) {}
+ }
+
+ private TGAImage loadImage(String resourceName) {
+ try {
+ return TGAImage.read(getClass().getClassLoader().getResourceAsStream(resourceName));
+ } catch (IOException e) {
+ throw new GLException(e);
+ }
+ }
+
+ // We need to load the initial texture file early to get the width
+ // and height for the pbuffer
+ private void loadInitialTexture(String initialMapFilename) {
+ try {
+ initialMap = TGAImage.read(getClass().getClassLoader().getResourceAsStream(initialMapFilename));
+ } catch (IOException e) {
+ throw new GLException(e);
+ }
+ initialMapDimensions[0] = initialMap.getWidth();
+ initialMapDimensions[1] = initialMap.getHeight();
+ }
+
+ private void initOpenGL(GL gl, GLU glu) {
+ loadTextures(gl, tmpSpinFilename, tmpDropletFilename, tmpCubeMapFilenamePattern);
+ tmpSpinFilename = null;
+ tmpDropletFilename = null;
+ tmpCubeMapFilenamePattern = null;
+
+ gl.glMatrixMode(GL.GL_MODELVIEW);
+ gl.glLoadIdentity();
+ gl.glMatrixMode(GL.GL_PROJECTION);
+ gl.glLoadIdentity();
+ glu.gluOrtho2D(-1, 1, -1, 1);
+
+ gl.glClearColor(0, 0, 0, 0);
+ gl.glDisable(GL.GL_LIGHTING);
+ gl.glDisable(GL.GL_DEPTH_TEST);
+
+ createAndWriteUVOffsets(gl, initialMapDimensions[0], initialMapDimensions[1]);
+
+ checkExtension(gl, "GL_NV_register_combiners");
+ checkExtension(gl, "GL_NV_register_combiners2");
+ checkExtension(gl, "GL_NV_texture_shader");
+ checkExtension(gl, "GL_ARB_multitexture");
+
+ ///////////////////////////////////////////////////////////////////////////
+ // UV Offset Vertex Program
+ ///////////////////////////////////////////////////////////////////////////
+ // track the MVP matrix for the vertex program
+ gl.glTrackMatrixNV(GL.GL_VERTEX_PROGRAM_NV, 0, GL.GL_MODELVIEW_PROJECTION_NV, GL.GL_IDENTITY_NV);
+ float[] rCVConsts = new float[] { 0, 0.5f, 1.0f, 2.0f };
+ gl.glProgramParameter4fvNV(GL.GL_VERTEX_PROGRAM_NV, CV_CONSTS_1, rCVConsts);
+
+ int[] tmpInt = new int[1];
+ gl.glGenProgramsNV(1, tmpInt);
+ vertexProgramID = tmpInt[0];
+ gl.glBindProgramNV(GL.GL_VERTEX_PROGRAM_NV, vertexProgramID);
+
+ String programBuffer =
+ "!!VP1.0\n" +
+ "# CV_WORLDVIEWPROJ_0 = 0,\n" +
+ "# CV_WORLDVIEWPROJ_1 = 1,\n" +
+ "# CV_WORLDVIEWPROJ_2 = 2,\n" +
+ "# CV_WORLDVIEWPROJ_3 = 3,\n" +
+ "#\n" +
+ "# CV_UV_OFFSET_TO_USE = 4,\n" +
+ "#\n" +
+ "#\n" +
+ "# CV_UV_T0_NO_OFFSET = 8,\n" +
+ "# CV_UV_T0_TYPE1 = 9,\n" +
+ "# CV_UV_T0_TYPE2 = 10,\n" +
+ "# CV_UV_T0_TYPE3 = 11,\n" +
+ "# CV_UV_T0_TYPE4 = 12,\n" +
+ "#\n" +
+ "# CV_UV_T1_NO_OFFSET = 13,\n" +
+ "# CV_UV_T1_TYPE1 = 14,\n" +
+ "# CV_UV_T1_TYPE2 = 15,\n" +
+ "# CV_UV_T1_TYPE3 = 16,\n" +
+ "# CV_UV_T1_TYPE4 = 17,\n" +
+ "#\n" +
+ "# CV_UV_T2_NO_OFFSET = 18,\n" +
+ "# CV_UV_T2_TYPE1 = 19,\n" +
+ "# CV_UV_T2_TYPE2 = 20,\n" +
+ "# CV_UV_T2_TYPE3 = 21,\n" +
+ "# CV_UV_T2_TYPE4 = 22,\n" +
+ "#\n" +
+ "# CV_UV_T3_NO_OFFSET = 23,\n" +
+ "# CV_UV_T3_TYPE1 = 24,\n" +
+ "# CV_UV_T3_TYPE2 = 25,\n" +
+ "# CV_UV_T3_TYPE3 = 26,\n" +
+ "# CV_UV_T3_TYPE4 = 27,\n" +
+ "#\n" +
+ "# CV_CONSTS_1 = 28\n" +
+ "\n" +
+ "# Transform vertex-position to clip-space\n" +
+ "DP4 o[HPOS].x, v[OPOS], c[0];\n" +
+ "DP4 o[HPOS].y, v[OPOS], c[1];\n" +
+ "DP4 o[HPOS].z, v[OPOS], c[2];\n" +
+ "DP4 o[HPOS].w, v[OPOS], c[3];\n" +
+ "\n" +
+ "# Read which set of offsets to use\n" +
+ "ARL A0.x, c[4].x;\n" +
+ "\n" +
+ "# c[CV_CONSTS_1] = c[28]\n" +
+ "# x = 0\n" +
+ "# y = 0.5\n" +
+ "# z = 1\n" +
+ "# w = 2.0f\n" +
+ "\n" +
+ "# Put a scale factor into r0 so the sample points\n" +
+ "# can be moved farther from the texel being written\n" +
+ "\n" +
+ "#MOV R0, c[28].z;\n" +
+ "\n" +
+ "# Add the offsets to the input texture\n" +
+ "# coordinate, creating 4 sets of independent\n" +
+ "# texture coordinates.\n" +
+ "\n" +
+ "ADD o[TEX0], c[A0.x + 8], v[TEX0];\n" +
+ "ADD o[TEX1], c[A0.x + 13], v[TEX0];\n" +
+ "ADD o[TEX2], c[A0.x + 18], v[TEX0];\n" +
+ "ADD o[TEX3], c[A0.x + 23], v[TEX0];\n" +
+ "\n" +
+ "#MAD o[TEX0], R0, c[A0.x + 8], v[TEX0];\n" +
+ "#MAD o[TEX1], R0, c[A0.x + 13], v[TEX0]; \n" +
+ "#MAD o[TEX2], R0, c[A0.x + 18], v[TEX0]; \n" +
+ "#MAD o[TEX3], R0, c[A0.x + 23], v[TEX0];\n" +
+ "\n" +
+ "END \n";
+
+ gl.glLoadProgramNV(GL.GL_VERTEX_PROGRAM_NV, vertexProgramID, programBuffer.length(), programBuffer);
+ if (gl.glGetError() != GL.GL_NO_ERROR) {
+ throw new GLException("Error loading vertex program \"Texcoord_4_Offset.vp\"");
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // register combiner setup for equal weight combination of texels
+ ///////////////////////////////////////////////////////////////////////////
+ displayListIDs[CA_REGCOMBINER_EQ_WEIGHT_COMBINE] = gl.glGenLists(1);
+ gl.glNewList(displayListIDs[CA_REGCOMBINER_EQ_WEIGHT_COMBINE], GL.GL_COMPILE);
+ initEqWeightCombine_PostMult(gl);
+ gl.glEnable(GL.GL_REGISTER_COMBINERS_NV);
+ gl.glEndList();
+
+ ///////////////////////////////////////////////////////////////////////////
+ // register combiners setup for computing force from neighbors (step 1)
+ ///////////////////////////////////////////////////////////////////////////
+ displayListIDs[CA_REGCOMBINER_NEIGHBOR_FORCE_CALC_1] = gl.glGenLists(1);
+ gl.glNewList(displayListIDs[CA_REGCOMBINER_NEIGHBOR_FORCE_CALC_1], GL.GL_COMPILE);
+ initNeighborForceCalcStep1(gl);
+ gl.glEnable(GL.GL_REGISTER_COMBINERS_NV);
+ gl.glEndList();
+
+ ///////////////////////////////////////////////////////////////////////////
+ // register combiners setup for computing force from neighbors (step 2)
+ ///////////////////////////////////////////////////////////////////////////
+ displayListIDs[CA_REGCOMBINER_NEIGHBOR_FORCE_CALC_2] = gl.glGenLists(1);
+ gl.glNewList(displayListIDs[CA_REGCOMBINER_NEIGHBOR_FORCE_CALC_2], GL.GL_COMPILE);
+ initNeighborForceCalcStep2(gl);
+ gl.glEnable(GL.GL_REGISTER_COMBINERS_NV);
+ gl.glEndList();
+
+ ///////////////////////////////////////////////////////////////////////////
+ // register combiners setup to apply force
+ ///////////////////////////////////////////////////////////////////////////
+ displayListIDs[CA_REGCOMBINER_APPLY_FORCE] = gl.glGenLists(1);
+ gl.glNewList(displayListIDs[CA_REGCOMBINER_APPLY_FORCE], GL.GL_COMPILE);
+ initApplyForce(gl);
+ gl.glEnable(GL.GL_REGISTER_COMBINERS_NV);
+ gl.glEndList();
+
+ ///////////////////////////////////////////////////////////////////////////
+ // register combiners setup to apply velocity
+ ///////////////////////////////////////////////////////////////////////////
+ displayListIDs[CA_REGCOMBINER_APPLY_VELOCITY] = gl.glGenLists(1);
+ gl.glNewList(displayListIDs[CA_REGCOMBINER_APPLY_VELOCITY], GL.GL_COMPILE);
+ initApplyVelocity(gl);
+ gl.glEnable(GL.GL_REGISTER_COMBINERS_NV);
+ gl.glEndList();
+
+ ///////////////////////////////////////////////////////////////////////////
+ // register combiners setup to create a normal map
+ ///////////////////////////////////////////////////////////////////////////
+ displayListIDs[CA_REGCOMBINER_CREATE_NORMAL_MAP] = gl.glGenLists(1);
+ gl.glNewList(displayListIDs[CA_REGCOMBINER_CREATE_NORMAL_MAP], GL.GL_COMPILE);
+ initCreateNormalMap(gl);
+ gl.glEnable(GL.GL_REGISTER_COMBINERS_NV);
+ gl.glEndList();
+
+ ///////////////////////////////////////////////////////////////////////////
+ // texture shader setup for dot product reflection
+ ///////////////////////////////////////////////////////////////////////////
+ displayListIDs[CA_TEXTURE_SHADER_REFLECT] = gl.glGenLists(1);
+ gl.glNewList(displayListIDs[CA_TEXTURE_SHADER_REFLECT], GL.GL_COMPILE);
+ initDotProductReflect(gl);
+ gl.glDisable(GL.GL_BLEND);
+ gl.glEnable(GL.GL_TEXTURE_SHADER_NV);
+ gl.glEnable(GL.GL_REGISTER_COMBINERS_NV);
+ gl.glEndList();
+
+ ///////////////////////////////////////////////////////////////////////////
+ // display list to render a single screen space quad.
+ ///////////////////////////////////////////////////////////////////////////
+ displayListIDs[CA_DRAW_SCREEN_QUAD] = gl.glGenLists(1);
+ gl.glNewList(displayListIDs[CA_DRAW_SCREEN_QUAD], GL.GL_COMPILE);
+ gl.glColor4f(1, 1, 1, 1);
+ gl.glBegin(GL.GL_TRIANGLE_STRIP);
+ gl.glTexCoord2f(0, 1); gl.glVertex2f(-1, 1);
+ gl.glTexCoord2f(0, 0); gl.glVertex2f(-1, -1);
+ gl.glTexCoord2f(1, 1); gl.glVertex2f( 1, 1);
+ gl.glTexCoord2f(1, 0); gl.glVertex2f( 1, -1);
+ gl.glEnd();
+ gl.glEndList();
+ }
+
+ private void checkExtension(GL gl, String extensionName) {
+ if (!gl.isExtensionAvailable(extensionName)) {
+ throw new GLException("Unable to initialize " + extensionName + " OpenGL extension");
+ }
+ }
+
+ private void doSingleTimeStep(GL gl) {
+ int temp;
+
+ // Swap texture source & target indices & pointers
+ // 0 = start from initial loaded texture
+ // 1/2 = flip flop back and forth between targets & sources
+
+ switch (flipState) {
+ case 0:
+ texHeightInput = dynamicTextureIDs[CA_TEXTURE_HEIGHT_SOURCE]; // initial height map.
+ texHeightOutput = dynamicTextureIDs[CA_TEXTURE_HEIGHT_TARGET]; // next height map.
+
+ texVelocityInput = dynamicTextureIDs[CA_TEXTURE_VELOCITY_SOURCE]; // initial velocity.
+ texVelocityOutput = dynamicTextureIDs[CA_TEXTURE_VELOCITY_TARGET]; // next velocity.
+
+ // Clear initial velocity texture to 0x80 == gray
+ gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
+ gl.glClear(GL.GL_COLOR_BUFFER_BIT);
+
+ // Now we need to copy the resulting pixels into the intermediate force field texture
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, texVelocityInput);
+
+ // use CopyTexSubImage for speed (even though we copy all of it) since we pre-allocated the texture
+ gl.glCopyTexSubImage2D(GL.GL_TEXTURE_2D, 0, 0, 0, 0, 0, initialMapDimensions[0], initialMapDimensions[1]);
+
+ break;
+
+ case 1:
+ temp = texHeightInput;
+ texHeightInput = texHeightOutput;
+ texHeightOutput = temp;
+
+ temp = texVelocityInput;
+ texVelocityInput = texVelocityOutput;
+ texVelocityOutput = temp;
+
+ break;
+
+ case 2:
+ temp = texHeightInput;
+ texHeightInput = texHeightOutput;
+ texHeightOutput = temp;
+
+ temp = texVelocityInput;
+ texVelocityInput = texVelocityOutput;
+ texVelocityOutput = temp;
+ break;
+ }
+
+ // even if wireframe mode, render to texture as solid
+ gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL.GL_FILL);
+
+ /////////////////////////////////////////////////////////////
+ // Render first 3 components of force from three neighbors
+ // Offsets selected are 1 center texel for center height
+ // and 3 of the 4 nearest neighbors. Texture selected
+ // is same for all stages as we're turning height difference
+ // of nearest neightbor texels into a force value.
+
+ gl.glCallList(displayListIDs[CA_REGCOMBINER_NEIGHBOR_FORCE_CALC_1]);
+
+ // set current source texture for stage 0 texture
+ for (int i = 0; i < 4; i++)
+ {
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB + i);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, texHeightInput);
+ gl.glEnable(GL.GL_TEXTURE_2D);
+ }
+
+ int wrapMode = wrap ? GL.GL_REPEAT : GL.GL_CLAMP_TO_EDGE;
+ gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, wrapMode);
+ gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, wrapMode);
+
+ // disable blending
+ gl.glDisable(GL.GL_BLEND);
+
+ // render using offset 1 (type 1 -- center + 3 of 4 nearest neighbors).
+ gl.glProgramParameter4fNV(GL.GL_VERTEX_PROGRAM_NV, CV_UV_OFFSET_TO_USE, 1, 0, 0, 0);
+
+ // bind the vertex program to be used for this step and the next one.
+ gl.glBindProgramNV(GL.GL_VERTEX_PROGRAM_NV, vertexProgramID);
+ gl.glEnable(GL.GL_VERTEX_PROGRAM_NV);
+
+ // render a screen quad. with texture coords doing difference of nearby texels for force calc.
+ gl.glCallList(displayListIDs[CA_DRAW_SCREEN_QUAD]);
+
+ // Now we need to copy the resulting pixels into the intermediate force field texture
+ gl.glActiveTextureARB(GL.GL_TEXTURE2_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, dynamicTextureIDs[CA_TEXTURE_FORCE_INTERMEDIATE]);
+
+ // use CopyTexSubImage for speed (even though we copy all of it) since we pre-allocated the texture
+ gl.glCopyTexSubImage2D(GL.GL_TEXTURE_2D, 0, 0, 0, 0, 0, initialMapDimensions[0], initialMapDimensions[1]);
+
+ ////////////////////////////////////////////////////////////////
+ // Now add in last component of force for the 4th neighbor
+ // that we didn't have enough texture lookups to do in the
+ // first pass
+
+ gl.glCallList(displayListIDs[CA_REGCOMBINER_NEIGHBOR_FORCE_CALC_2]);
+
+ // Cannot use additive blending as the force contribution might
+ // be negative and would have to subtract from the dest.
+ // We must instead use an additional texture as target and read
+ // the previous partial 3-neighbor result into the pixel shader
+ // for possible subtraction
+
+ // Alphablend must be false
+
+ //; t0 = center (same as last phase)
+ //; t1 = 2nd axis final point (same as last phase)
+ //; t2 = previous partial result texture sampled at center (result of last phase copied to texture)
+ //; t3 = not used (disable now)
+
+ gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, wrapMode);
+ gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, wrapMode);
+
+ gl.glActiveTextureARB(GL.GL_TEXTURE3_ARB);
+ gl.glDisable(GL.GL_TEXTURE_2D);
+
+ // vertex program already bound.
+ // render using offset 2 (type 2 -- final nearest neighbor plus center of previous result).
+ gl.glProgramParameter4fNV(GL.GL_VERTEX_PROGRAM_NV, CV_UV_OFFSET_TO_USE, 2, 0, 0, 0);
+
+ // render a screen quad
+ gl.glCallList(displayListIDs[CA_DRAW_SCREEN_QUAD]);
+
+ // Now we need to copy the resulting pixels into the intermediate force field texture
+ gl.glActiveTextureARB(GL.GL_TEXTURE1_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, dynamicTextureIDs[CA_TEXTURE_FORCE_TARGET]);
+
+ // use CopyTexSubImage for speed (even though we copy all of it) since we pre-allocated the texture
+ gl.glCopyTexSubImage2D(GL.GL_TEXTURE_2D, 0, 0, 0, 0, 0, initialMapDimensions[0], initialMapDimensions[1]);
+
+ /////////////////////////////////////////////////////////////////
+ // Apply the force with a scale factor to reduce it's magnitude.
+ // Add this to the current texture representing the water height.
+
+ gl.glCallList(displayListIDs[CA_REGCOMBINER_APPLY_FORCE]);
+
+ // use offsets of zero
+ gl.glProgramParameter4fNV(GL.GL_VERTEX_PROGRAM_NV, CV_UV_OFFSET_TO_USE, 0, 0, 0, 0);
+
+ // bind the vertex program to be used for this step and the next one.
+
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, texVelocityInput);
+ gl.glActiveTextureARB(GL.GL_TEXTURE1_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, dynamicTextureIDs[CA_TEXTURE_FORCE_TARGET]);
+ gl.glActiveTextureARB(GL.GL_TEXTURE2_ARB);
+ gl.glDisable(GL.GL_TEXTURE_2D);
+ gl.glActiveTextureARB(GL.GL_TEXTURE3_ARB);
+ gl.glDisable(GL.GL_TEXTURE_2D);
+
+ // Draw the quad to add in force.
+ gl.glCallList(displayListIDs[CA_DRAW_SCREEN_QUAD]);
+
+ ///////////////////////////////////////////////////////////////////
+ // With velocity texture selected, render new excitation droplets
+ // at random freq.
+
+ float randomFrequency = (float) Math.random();
+
+ if (dropletFrequency > randomFrequency) {
+ // a drop falls - decide where
+ Droplet drop = new Droplet(2 * ((float)Math.random() - 0.5f),
+ 2 * ((float)Math.random() - 0.5f),
+ 0.02f + 0.1f * ((float)Math.random()));
+ addDroplet(drop);
+ }
+
+ // Now draw the droplets:
+ if (!droplets.isEmpty()) {
+ drawDroplets(gl);
+ droplets.clear();
+ }
+
+ // Now we need to copy the resulting pixels into the velocity texture
+ gl.glActiveTextureARB(GL.GL_TEXTURE1_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, texVelocityOutput);
+
+ // use CopyTexSubImage for speed (even though we copy all of it) since we pre-allocated the texture
+ gl.glCopyTexSubImage2D(GL.GL_TEXTURE_2D, 0, 0, 0, 0, 0, initialMapDimensions[0], initialMapDimensions[1]);
+
+ //////////////////////////////////////////////////////////////////////
+ // Apply velocity to position
+ gl.glCallList(displayListIDs[CA_REGCOMBINER_APPLY_VELOCITY]);
+ gl.glEnable(GL.GL_VERTEX_PROGRAM_NV);
+
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, texHeightInput);
+ gl.glActiveTextureARB(GL.GL_TEXTURE1_ARB); // velocity output already bound
+ gl.glEnable(GL.GL_TEXTURE_2D);
+
+ // use offsets of zero
+ gl.glProgramParameter4fNV(GL.GL_VERTEX_PROGRAM_NV, CV_UV_OFFSET_TO_USE, 0, 0, 0, 0);
+
+ // Draw the quad to add in force.
+ gl.glCallList(displayListIDs[CA_DRAW_SCREEN_QUAD]);
+
+ // Now we need to copy the resulting pixels into the input height texture
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, texHeightInput);
+
+ // use CopyTexSubImage for speed (even though we copy all of it) since we pre-allocated the texture
+ gl.glCopyTexSubImage2D(GL.GL_TEXTURE_2D, 0, 0, 0, 0, 0, initialMapDimensions[0], initialMapDimensions[1]);
+
+ ///////////////////////////////////////////////////////////////////
+ // blur positions to smooth noise & generaly dampen things
+ // degree of blur is controlled by magnitude of 4 neighbor texel
+ // offsets with bilinear on
+
+ for (int i = 1; i < 4; i++) {
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB + i);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, texHeightInput);
+ gl.glEnable(GL.GL_TEXTURE_2D);
+ }
+
+ // use offsets of 3
+ gl.glProgramParameter4fNV(GL.GL_VERTEX_PROGRAM_NV, CV_UV_OFFSET_TO_USE, 3, 0, 0, 0);
+
+ gl.glCallList(displayListIDs[CA_REGCOMBINER_EQ_WEIGHT_COMBINE]);
+
+ gl.glCallList(displayListIDs[CA_DRAW_SCREEN_QUAD]);
+
+ // Draw the logo in the water.
+ if (applyInteriorBoundaries) {
+ gl.glDisable(GL.GL_VERTEX_PROGRAM_NV);
+ drawInteriorBoundaryObjects(gl);
+ }
+
+ // Now we need to copy the resulting pixels into the velocity texture
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, texHeightOutput);
+
+ // use CopyTexSubImage for speed (even though we copy all of it) since we pre-allocated the texture
+ gl.glCopyTexSubImage2D(GL.GL_TEXTURE_2D, 0, 0, 0, 0, 0, initialMapDimensions[0], initialMapDimensions[1]);
+
+ ///////////////////////////////////////////////////////////////////
+ // If selected, create a normal map from the height
+
+ if (createNormalMap) {
+ createNormalMap(gl);
+ }
+
+ ///////////////////////////////////////////////////////////
+ // Flip the state variable for the next round of rendering
+ switch (flipState) {
+ case 0:
+ flipState = 1;
+ break;
+ case 1:
+ flipState = 2;
+ break;
+ case 2:
+ flipState = 1;
+ break;
+ }
+ }
+
+ private void createNormalMap(GL gl) {
+ // use the height output on all four texture stages
+ for (int i = 0; i < 4; i++) {
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB + i);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, texHeightOutput);
+ gl.glEnable(GL.GL_TEXTURE_2D);
+ }
+
+ // Set constants for red & green scale factors (also essential color masks)
+ // Red mask first
+ float[] pixMasks = new float[] { normalSTScale, 0.0f, 0.0f, 0.0f };
+
+ gl.glCombinerStageParameterfvNV(GL.GL_COMBINER2_NV, GL.GL_CONSTANT_COLOR0_NV, pixMasks);
+
+ // Now green mask & scale:
+ pixMasks[0] = 0.0f;
+ pixMasks[1] = normalSTScale;
+ gl.glCombinerStageParameterfvNV(GL.GL_COMBINER2_NV, GL.GL_CONSTANT_COLOR1_NV, pixMasks);
+
+ gl.glCallList(displayListIDs[CA_REGCOMBINER_CREATE_NORMAL_MAP]);
+
+ // set vp offsets to nearest neighbors
+ gl.glProgramParameter4fNV(GL.GL_VERTEX_PROGRAM_NV, CV_UV_OFFSET_TO_USE, 4, 0, 0, 0);
+ gl.glEnable(GL.GL_VERTEX_PROGRAM_NV);
+
+ gl.glCallList(displayListIDs[CA_DRAW_SCREEN_QUAD]);
+
+ // Now we need to copy the resulting pixels into the normal map
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, dynamicTextureIDs[CA_TEXTURE_NORMAL_MAP]);
+
+ // use CopyTexSubImage for speed (even though we copy all of it) since we pre-allocated the texture
+ gl.glCopyTexSubImage2D(GL.GL_TEXTURE_2D, 0, 0, 0, 0, 0, initialMapDimensions[0], initialMapDimensions[1]);
+ }
+
+ private void drawInteriorBoundaryObjects(GL gl) {
+ gl.glDisable(GL.GL_REGISTER_COMBINERS_NV);
+
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, staticTextureIDs[CA_TEXTURE_INITIAL_MAP]);
+ gl.glEnable(GL.GL_TEXTURE_2D);
+
+ gl.glEnable(GL.GL_ALPHA_TEST);
+
+ // disable other texture units.
+ for (int i = 1; i < 4; i++) {
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB + i);
+ gl.glDisable(GL.GL_TEXTURE_2D);
+ }
+
+ gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
+ gl.glEnable(GL.GL_BLEND);
+
+ gl.glCallList(displayListIDs[CA_DRAW_SCREEN_QUAD]);
+
+ if (spinLogo) {
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, staticTextureIDs[CA_TEXTURE_SPIN]);
+ gl.glMatrixMode(GL.GL_MODELVIEW);
+ gl.glPushMatrix();
+ gl.glRotatef(angle, 0, 0, 1);
+ angle += 1;
+
+ gl.glCallList(displayListIDs[CA_DRAW_SCREEN_QUAD]);
+
+ gl.glPopMatrix();
+ }
+
+ gl.glDisable(GL.GL_ALPHA_TEST);
+ gl.glDisable(GL.GL_BLEND);
+ }
+
+ private void createTextureObject(GL gl, int id, TGAImage image, boolean test) {
+ // Fetch image data out of image
+ gl.glBindTexture(GL.GL_TEXTURE_2D, id);
+ gl.glTexImage2D (GL.GL_TEXTURE_2D,
+ 0,
+ GL.GL_RGBA8,
+ image.getWidth(),
+ image.getHeight(),
+ 0,
+ image.getGLFormat(),
+ GL.GL_UNSIGNED_BYTE,
+ image.getData());
+ gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
+ gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
+ gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
+ gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
+ }
+
+ private void loadCubeMap(GL gl, int id, String filenamePattern, boolean mipmap) {
+ int[] faces = new int[] { GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
+ GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
+ GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
+ GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
+ GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
+ GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB };
+ String[] faceNames = new String[] { "posx", "negx", "posy", "negy", "posz", "negz" };
+
+ // create and bind a cubemap texture object
+ gl.glBindTexture(GL.GL_TEXTURE_CUBE_MAP_ARB, id);
+
+ // enable automipmap generation if needed.
+ gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP_ARB, GL.GL_GENERATE_MIPMAP_SGIS, (mipmap ? 1 : 0));
+
+ if (mipmap)
+ gl.glTexParameterf(GL.GL_TEXTURE_CUBE_MAP_ARB, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_LINEAR);
+ else
+ gl.glTexParameterf(GL.GL_TEXTURE_CUBE_MAP_ARB, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
+ gl.glTexParameterf(GL.GL_TEXTURE_CUBE_MAP_ARB, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
+ gl.glTexParameterf(GL.GL_TEXTURE_CUBE_MAP_ARB, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
+ gl.glTexParameterf(GL.GL_TEXTURE_CUBE_MAP_ARB, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
+
+ // load 6 faces.
+ MessageFormat fmt = new MessageFormat(filenamePattern);
+ for (int i = 0; i < 6; i++) {
+ String filename = MessageFormat.format(filenamePattern, new String[] { faceNames[i] });
+ TGAImage image = loadImage(filename);
+ gl.glTexImage2D(faces[i],
+ 0,
+ GL.GL_RGBA8,
+ image.getWidth(),
+ image.getHeight(),
+ 0,
+ image.getGLFormat(),
+ GL.GL_UNSIGNED_BYTE,
+ image.getData());
+ }
+ }
+
+ private void loadTextures(GL gl,
+ String spinFilename,
+ String dropletFilename,
+ String cubeMapFilenamePattern) {
+ if (initialMap == null) {
+ throw new GLException("Must call loadInitialTexture ahead of time");
+ }
+
+ TGAImage spin = loadImage(spinFilename);
+ TGAImage droplet = loadImage(dropletFilename);
+
+ gl.glGenTextures(CA_NUM_STATIC_TEXTURES, staticTextureIDs);
+ gl.glGenTextures(CA_NUM_DYNAMIC_TEXTURES, dynamicTextureIDs); // also create intermediate texture object
+
+ // upload the initial map texture
+ createTextureObject(gl, staticTextureIDs[CA_TEXTURE_INITIAL_MAP], initialMap, true);
+
+ createTextureObject(gl, staticTextureIDs[CA_TEXTURE_SPIN], spin, true);
+
+ createTextureObject(gl, staticTextureIDs[CA_TEXTURE_DROPLET], droplet, false);
+
+ // load the cubemap texture
+ loadCubeMap(gl, staticTextureIDs[CA_TEXTURE_CUBEMAP], cubeMapFilenamePattern, true);
+
+ for (int i = 0; i < CA_NUM_DYNAMIC_TEXTURES; i++) {
+ // now create a dummy intermediate textures from the initial map texture
+ createTextureObject(gl, dynamicTextureIDs[i], initialMap, false);
+ }
+
+ initialMap = null;
+
+ texHeightInput = staticTextureIDs [CA_TEXTURE_INITIAL_MAP]; // initial height map.
+ texHeightOutput = dynamicTextureIDs[CA_TEXTURE_HEIGHT_TARGET]; // next height map.
+
+ texVelocityInput = dynamicTextureIDs[CA_TEXTURE_VELOCITY_SOURCE]; // initial velocity.
+ texVelocityOutput = dynamicTextureIDs[CA_TEXTURE_VELOCITY_TARGET]; // next velocity.
+ }
+
+ private void createAndWriteUVOffsets(GL gl, int width, int height) {
+ // This sets vertex shader constants used to displace the
+ // source texture over several additive samples. This is
+ // used to accumulate neighboring texel information that we
+ // need to run the game - the 8 surrounding texels, and the
+ // single source texel which will either spawn or die in the
+ // next generation.
+ // Label the texels as follows, for a source texel "e" that
+ // we want to compute for the next generation:
+ //
+ // abc
+ // def
+ // ghi:
+
+ // first the easy one: no offsets for sampling center
+ // occupied or unoccupied
+ // Use index offset value 0.0 to access these in the
+ // vertex shader.
+
+ perTexelWidth = 1.0f / width;
+ perTexelHeight = 1.0f / height;
+
+ // Offset set 0 : center texel sampling
+ float[] noOffsetX = new float[] { 0, 0, 0, 0 };
+ float[] noOffsetY = new float[] { 0, 0, 0, 0 };
+
+ // Offset set 1: For use with neighbor force pixel shader 1
+ // samples center with 0, +u, -u, and +v,
+ // ie the 'e','d', 'f', and 'h' texels
+ float dist = 1.5f;
+ float[] type1OffsetX = new float[] { 0.0f, -dist * perTexelWidth, dist * perTexelWidth, dist * perTexelWidth };
+ float[] type1OffsetY = new float[] { 0.0f, dist * perTexelHeight, dist * perTexelHeight, -dist * perTexelHeight };
+
+ // Offset set 2: for use with neighbor force pixel shader 2
+ // samples center with 0, and -v texels
+ // ie the 'e' and 'b' texels
+ // This completes a pattern of sampling center texel and it's
+ // 4 nearest neighbors to run the height-based water simulation
+ // 3rd must be 0 0 to sample texel center from partial result
+ // texture.
+
+ float[] type2OffsetX = new float[] { 0.0f, -dist * perTexelWidth, 0.0f, 0.0f };
+ float[] type2OffsetY = new float[] { 0.0f, -dist * perTexelHeight, 0.0f, 0.0f };
+
+ // type 3 offsets
+ updateBlurVertOffset(gl);
+
+ /////////////////////////////////////////////////////////////
+ // Nearest neighbor offsets:
+
+ float[] type4OffsetX = new float[] { -perTexelWidth, perTexelWidth, 0.0f, 0.0f };
+ float[] type4OffsetY = new float[] { 0.0f, 0.0f, -perTexelHeight, perTexelHeight };
+
+ // write all these offsets to constant memory
+ for (int i = 0; i < 4; ++i) {
+ float noOffset[] = { noOffsetX[i], noOffsetY[i], 0.0f, 0.0f };
+ float type1Offset[] = { type1OffsetX[i], type1OffsetY[i], 0.0f, 0.0f };
+ float type2Offset[] = { type2OffsetX[i], type2OffsetY[i], 0.0f, 0.0f };
+ float type4Offset[] = { type4OffsetX[i], type4OffsetY[i], 0.0f, 0.0f };
+
+ gl.glProgramParameter4fvNV(GL.GL_VERTEX_PROGRAM_NV, CV_UV_T0_NO_OFFSET + 5 * i, noOffset);
+ gl.glProgramParameter4fvNV(GL.GL_VERTEX_PROGRAM_NV, CV_UV_T0_TYPE1 + 5 * i, type1Offset);
+ gl.glProgramParameter4fvNV(GL.GL_VERTEX_PROGRAM_NV, CV_UV_T0_TYPE2 + 5 * i, type2Offset);
+ gl.glProgramParameter4fvNV(GL.GL_VERTEX_PROGRAM_NV, CV_UV_T0_TYPE4 + 5 * i, type4Offset);
+ }
+ }
+
+ private void updateBlurVertOffset(GL gl) {
+ float[] type3OffsetX = new float[] { -perTexelWidth * 0.5f,
+ perTexelWidth,
+ perTexelWidth * 0.5f,
+ -perTexelWidth
+ };
+ float[] type3OffsetY = new float[] { perTexelHeight,
+ perTexelHeight * 0.5f,
+ -perTexelHeight,
+ -perTexelHeight * 0.5f
+ };
+ float[] offsets = new float[] { 0, 0, 0, 0 };
+
+ for (int i = 0; i < 4; ++i) {
+ offsets[0] = blurDist * ( type3OffsetX[i]);
+ offsets[1] = blurDist * ( type3OffsetY[i]);
+ gl.glProgramParameter4fvNV(GL.GL_VERTEX_PROGRAM_NV, CV_UV_T0_TYPE3 + 5 * i, offsets);
+ }
+ }
+
+ private synchronized void drawDroplets(GL gl) {
+ gl.glDisable(GL.GL_REGISTER_COMBINERS_NV);
+ gl.glDisable(GL.GL_VERTEX_PROGRAM_NV);
+
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glBindTexture(GL.GL_TEXTURE_2D, staticTextureIDs[CA_TEXTURE_DROPLET]);
+ gl.glEnable(GL.GL_TEXTURE_2D);
+
+ gl.glActiveTextureARB(GL.GL_TEXTURE1_ARB);
+ gl.glDisable(GL.GL_TEXTURE_2D);
+
+ gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE);
+ gl.glEnable(GL.GL_BLEND);
+
+ gl.glBegin(GL.GL_QUADS);
+ gl.glColor4f(1, 1, 1, 1);
+ for (Iterator iter = droplets.iterator(); iter.hasNext(); ) {
+ Droplet droplet = (Droplet) iter.next();
+ // coords in [-1,1] range
+
+ // Draw a single quad to the texture render target
+ // The quad is textured with the initial droplet texture, and
+ // covers some small portion of the render target
+ // Draw the droplet
+
+ gl.glTexCoord2f(0, 0); gl.glVertex2f(droplet.rX() - droplet.rScale(), droplet.rY() - droplet.rScale());
+ gl.glTexCoord2f(1, 0); gl.glVertex2f(droplet.rX() + droplet.rScale(), droplet.rY() - droplet.rScale());
+ gl.glTexCoord2f(1, 1); gl.glVertex2f(droplet.rX() + droplet.rScale(), droplet.rY() + droplet.rScale());
+ gl.glTexCoord2f(0, 1); gl.glVertex2f(droplet.rX() - droplet.rScale(), droplet.rY() + droplet.rScale());
+ }
+ gl.glEnd();
+
+ gl.glDisable(GL.GL_BLEND);
+ }
+
+ //----------------------------------------------------------------------
+ // Inlined register combiner and texture shader programs
+ // (don't want to port nvparse as it's a dead-end; we'll focus on Cg instead)
+
+ private void initEqWeightCombine_PostMult(GL gl) {
+ float[] const0 = new float[] { 0.5f, 0.5f, 0.5f, 1.0f };
+ float[] const1 = new float[] { 1.0f, 1.0f, 1.0f, 1.0f };
+
+ gl.glCombinerParameterfvNV(GL.GL_CONSTANT_COLOR0_NV, const0);
+ gl.glCombinerParameterfvNV(GL.GL_CONSTANT_COLOR1_NV, const1);
+ gl.glCombinerParameteriNV(GL.GL_NUM_GENERAL_COMBINERS_NV, 4);
+
+ int stage = 0;
+ // Stage 0
+ // rgb
+ // {
+ // discard = half_bias(tex0);
+ // discard = half_bias(tex1);
+ // spare0 = sum();
+ // scale_by_one_half();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_TEXTURE0_ARB, GL.GL_HALF_BIAS_NORMAL_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_TEXTURE1_ARB, GL.GL_HALF_BIAS_NORMAL_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_SCALE_BY_ONE_HALF_NV, GL.GL_NONE, false, false, false);
+
+ // Stage 0
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 1
+ // rgb
+ // {
+ // discard = half_bias(tex2);
+ // discard = half_bias(tex3);
+ // spare1 = sum();
+ // scale_by_one_half();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_TEXTURE2_ARB, GL.GL_HALF_BIAS_NORMAL_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_TEXTURE3_ARB, GL.GL_HALF_BIAS_NORMAL_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE1_NV, GL.GL_SCALE_BY_ONE_HALF_NV, GL.GL_NONE, false, false, false);
+
+ // Stage 1
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 2
+ // rgb
+ // {
+ // discard = spare0;
+ // discard = spare1;
+ // spare0 = sum();
+ // scale_by_one_half();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_SPARE0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_SPARE1_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_SCALE_BY_ONE_HALF_NV, GL.GL_NONE, false, false, false);
+
+ // Stage 2
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 3
+ // rgb
+ // {
+ // discard = const0;
+ // discard = spare0;
+ // spare0 = sum();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_CONSTANT_COLOR0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_SPARE0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 3
+ // alpha
+ discardAlpha(gl, stage);
+
+ gl.glDisable(GL.GL_PER_STAGE_CONSTANTS_NV);
+ gl.glCombinerParameteriNV(GL.GL_COLOR_SUM_CLAMP_NV, GL.GL_FALSE);
+
+ doFinal(gl, GL.GL_SPARE0_NV, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV);
+ }
+
+ private void initNeighborForceCalcStep1(GL gl) {
+ // Step one in the nearest-neighbor force calculation for height-based water
+ // simulation. NeighborForceCalc2 is the second step.
+ //
+ // This step takes the center point and three neighboring points, and computes
+ // the texel difference as the "force" acting to pull the center texel.
+ //
+ // The amount to which the computed force is applied to the texel is controlled
+ // in a separate shader.
+
+ // get colors from all 4 texture stages
+ // tex0 = center texel
+ // tex1 = 1st neighbor
+ // tex2 = 2nd neighbor - same axis as 1st neighbor point
+ // so force for that axis == t1 - t0 + t2 - t0
+ // tex3 = 3rd neighbor on other axis
+
+ float[] const0 = new float[] { 0.5f, 0.5f, 0.5f, 1.0f };
+
+ gl.glCombinerParameterfvNV(GL.GL_CONSTANT_COLOR0_NV, const0);
+ gl.glCombinerParameteriNV(GL.GL_NUM_GENERAL_COMBINERS_NV, 8);
+
+ int stage = 0;
+ // Stage 0
+ // rgb
+ // {
+ // //s0 = t1 - t0;
+ // discard = -tex0;
+ // discard = tex1;
+ // spare0 = sum();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_TEXTURE0_ARB, GL.GL_SIGNED_NEGATE_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_TEXTURE1_ARB, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 0
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 1
+ // rgb
+ // {
+ // //s1 = t2 - t0;
+ // discard = -tex0;
+ // discard = tex2;
+ // spare1 = sum();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_TEXTURE0_ARB, GL.GL_SIGNED_NEGATE_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_TEXTURE2_ARB, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE1_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 1
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 2
+ // // 'force' for 1st axis
+ // rgb
+ // {
+ // //s0 = s0 + s1 = t1 - t0 + t2 - t0;
+ // discard = spare0;
+ // discard = spare1;
+ // spare0 = sum();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_SPARE0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_SPARE1_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 2
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 3
+ // // one more point for 2nd axis
+ // rgb
+ // {
+ // //s1 = t3 - t0;
+ // discard = -tex0;
+ // discard = tex3;
+ // spare1 = sum();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_TEXTURE0_ARB, GL.GL_SIGNED_NEGATE_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_TEXTURE3_ARB, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE1_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 3
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 4
+ // rgb
+ // {
+ // //s0 = s0 + s1 = t3 - t0 + t2 - t0 + t1 - t0;
+ // discard = spare0;
+ // discard = spare1;
+ // spare0 = sum();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_SPARE0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_SPARE1_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 4
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 5
+ // // Now add in a force to gently pull the center texel's
+ // // value to 0.5. The strength of this is controlled by
+ // // the PCN_EQ_REST_FAC - restoration factor
+ // // Without this, the simulation will fade to zero or fly
+ // // away to saturate at 1.0
+ // rgb
+ // {
+ // //s1 = 0.5 - t0;
+ // discard = -tex0;
+ // discard = const0;
+ // spare1 = sum();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_TEXTURE0_ARB, GL.GL_SIGNED_NEGATE_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_CONSTANT_COLOR0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE1_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 5
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 6
+ // {
+ // rgb
+ // {
+ // discard = spare1 * const0;
+ // discard = spare0;
+ // spare0 = sum();
+ // }
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_SPARE1_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_CONSTANT_COLOR0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_SPARE0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 6
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 7
+ // rgb
+ // {
+ // discard = spare0;
+ // discard = const0;
+ // spare0 = sum();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_SPARE0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_CONSTANT_COLOR0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 7
+ // alpha
+ discardAlpha(gl, stage);
+
+ gl.glDisable(GL.GL_PER_STAGE_CONSTANTS_NV);
+ gl.glCombinerParameteriNV(GL.GL_COLOR_SUM_CLAMP_NV, GL.GL_FALSE);
+ doFinal(gl, GL.GL_SPARE0_NV, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV);
+ }
+
+ private void initNeighborForceCalcStep2(GL gl) {
+ // 2nd step of force calc for render-to-texture
+ // water simulation.
+ //
+ // Adds the 4th & final neighbor point to the
+ // force calc..
+ //
+ // Bias and scale the values so 0 force is 0.5,
+ // full negative force is 0.0, and full pos is
+ // 1.0
+ //
+ // tex0 Center texel
+ // tex1 2nd axis neighbor point
+ // tex2 previous partial force amount
+ // Result from t1 - t0 is added to this t2
+ // partial result & output
+
+ float[] const0 = new float[] { 0.5f, 0.5f, 0.5f, 1.0f };
+
+ gl.glCombinerParameterfvNV(GL.GL_CONSTANT_COLOR0_NV, const0);
+ gl.glCombinerParameteriNV(GL.GL_NUM_GENERAL_COMBINERS_NV, 2);
+
+ int stage = 0;
+ // Stage 0
+ // last element of neighbor force
+ // rgb
+ // {
+ // discard = -tex0;
+ // discard = tex1;
+ // spare0 = sum();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_TEXTURE0_ARB, GL.GL_SIGNED_NEGATE_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_TEXTURE1_ARB, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 0
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 1
+ // add with previous partial force amount
+ // rgb
+ // {
+ // discard = spare0;
+ // discard = tex2;
+ // spare0 = sum();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_SPARE0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_TEXTURE2_ARB, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 1
+ // alpha
+ discardAlpha(gl, stage);
+
+ gl.glDisable(GL.GL_PER_STAGE_CONSTANTS_NV);
+ gl.glCombinerParameteriNV(GL.GL_COLOR_SUM_CLAMP_NV, GL.GL_FALSE);
+ doFinal(gl, GL.GL_SPARE0_NV, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV);
+ }
+
+ private void initApplyForce(GL gl) {
+ // This shader samples t1, biases its value to a signed number, and applies this
+ // value multiplied by a scale factor to the t0 sample.
+ //
+ // This is used to apply a "force" texture value to a "velocity" state texture
+ // for nearest-neighbor height-based water simulations. The output pixel is
+ // the new "velocity" value to replace the t0 sample in rendering to a new
+ // texture which will replace the texture selected into t0.
+ //
+ // A nearly identical shader using a different scaling constant is used to
+ // apply the "velocity" value to a "height" texture at each texel.
+ //
+ // t1 comes in the range [0,1] but needs to hold signed values, so a value of
+ // 0.5 in t1 represents zero force. This is biased to a signed value in
+ // computing the new velocity.
+ //
+ // tex0 = previous velocity
+ // tex1 = force
+ //
+ // Bias the force so that 0.5 input = no change in t0 value
+ // and 0.0 input means -0.5 * scale change in t0 value
+ //
+ // New velocity = force * scale + previous velocity
+
+ float[] const0 = new float[] { 0.25f, 0.25f, 0.25f, 1.0f };
+ float[] const1 = new float[] { 0.5f, 0.5f, 0.5f, 1.0f };
+
+ gl.glCombinerParameterfvNV(GL.GL_CONSTANT_COLOR0_NV, const0);
+ gl.glCombinerParameterfvNV(GL.GL_CONSTANT_COLOR1_NV, const1);
+ gl.glCombinerParameteriNV(GL.GL_NUM_GENERAL_COMBINERS_NV, 4);
+
+ int stage = 0;
+ // Stage 0
+ // rgb
+ // {
+ // discard = expand(tex1) * const0;
+ // discard = expand(tex0);
+ // spare0 = sum();
+ // scale_by_one_half();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_TEXTURE1_ARB, GL.GL_EXPAND_NORMAL_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_CONSTANT_COLOR0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_TEXTURE0_ARB, GL.GL_EXPAND_NORMAL_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_SCALE_BY_ONE_HALF_NV, GL.GL_NONE, false, false, false);
+
+ // Stage 0
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 1
+ // rgb
+ // {
+ // discard = spare0;
+ // discard = const1;
+ // spare0 = sum();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_SPARE0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_CONSTANT_COLOR1_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 1
+ // alpha
+ discardAlpha(gl, stage);
+
+ gl.glDisable(GL.GL_PER_STAGE_CONSTANTS_NV);
+ gl.glCombinerParameteriNV(GL.GL_COLOR_SUM_CLAMP_NV, GL.GL_FALSE);
+ doFinal(gl, GL.GL_SPARE0_NV, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_CONSTANT_COLOR0_NV, GL.GL_UNSIGNED_IDENTITY_NV);
+ }
+
+ private void initApplyVelocity(GL gl) {
+ // This shader samples t1, biases its value to a signed number, and applies this
+ // value multiplied by a scale factor to the t0 sample.
+ //
+ // This is used to apply a "velocity" texture value to a "height" state texture
+ // for nearest-neighbor height-based water simulations. The output pixel is
+ // the new "height" value to replace the t0 sample in rendering to a new
+ // texture which will replace the texture selected into t0.
+ //
+ // A nearly identical shader using a different scaling constant is used to
+ // apply the "force" value to the "velocity" texture at each texel.
+ //
+ // t1 comes in the range [0,1] but needs to hold signed values, so a value of
+ // 0.5 in t1 represents zero velocity. This is biased to a signed value in
+ // computing the new position.
+ //
+ // tex0 = height field
+ // tex1 = velocity
+ //
+ // Bias the force/velocity to a signed value so we can subtract from
+ // the t0 position sample.
+ //
+ // New height = velocity * scale factor + old height
+
+ float[] const0 = new float[] { 0.5f, 0.5f, 0.5f, 1.0f };
+
+ gl.glCombinerParameterfvNV(GL.GL_CONSTANT_COLOR0_NV, const0);
+ gl.glCombinerParameteriNV(GL.GL_NUM_GENERAL_COMBINERS_NV, 2);
+
+ int stage = 0;
+ // Stage 0
+ // rgb
+ // {
+ // discard = expand(tex1) * const0;
+ // discard = expand(tex0);
+ // spare0 = sum();
+ // scale_by_one_half();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_TEXTURE1_ARB, GL.GL_EXPAND_NORMAL_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_CONSTANT_COLOR0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_TEXTURE0_ARB, GL.GL_EXPAND_NORMAL_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_SCALE_BY_ONE_HALF_NV, GL.GL_NONE, false, false, false);
+
+ // Stage 0
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 1
+ // rgb
+ // {
+ // discard = spare0;
+ // discard = const0;
+ // spare0 = sum();
+ // }
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_SPARE0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_CONSTANT_COLOR0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 1
+ // alpha
+ discardAlpha(gl, stage);
+
+ gl.glDisable(GL.GL_PER_STAGE_CONSTANTS_NV);
+ gl.glCombinerParameteriNV(GL.GL_COLOR_SUM_CLAMP_NV, GL.GL_FALSE);
+ doFinal(gl, GL.GL_SPARE0_NV, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_CONSTANT_COLOR0_NV, GL.GL_UNSIGNED_IDENTITY_NV);
+ }
+
+ private void initCreateNormalMap(GL gl) {
+ // Neighbor-differencing for RGB normal map creation. Scale factors for s and t
+ // axis components are set in program code.
+ // This does a crude 1-s^2-t^2 calculation for the blue component in order to
+ // approximately normalize the RGB normal map vector. For s^2+t^2 close to 1.0,
+ // this is a close approximation to blue = sqrt(1 - s^2 - t^2) which would give a
+ // normalized vector.
+ // An additional pass with a dependent texture lookup (alpha-red or green-blue)
+ // could be used to produce an exactly normalized normal.
+
+ // colors from all 4 texture stages
+ // tex0 = -s, 0
+ // tex1 = +s, 0
+ // tex2 = 0, +t
+ // tex3 = 0, -t
+
+ gl.glCombinerParameteriNV(GL.GL_NUM_GENERAL_COMBINERS_NV, 7);
+
+ int stage = 0;
+ // Stage 0
+ // rgb
+ // {
+ // // (t0 - t1)*4 : 4 for higher scale
+ // discard = -tex1;
+ // discard = tex0;
+ // spare0 = sum();
+ // scale_by_four();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_TEXTURE1_ARB, GL.GL_SIGNED_NEGATE_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_TEXTURE0_ARB, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_SCALE_BY_FOUR_NV, GL.GL_NONE, false, false, false);
+
+ // Stage 0
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 1
+ // rgb
+ // {
+ // // (t3 - t2)*4 : 4 for higher scale
+ // discard = -tex2;
+ // discard = tex3;
+ // spare1 = sum();
+ // scale_by_four();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_TEXTURE2_ARB, GL.GL_SIGNED_NEGATE_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_TEXTURE3_ARB, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE1_NV, GL.GL_SCALE_BY_FOUR_NV, GL.GL_NONE, false, false, false);
+
+ // Stage 1
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 2
+ // Define const0 in the third general combiner as RGBA = (scale, 0, 0, 0)
+ // Where scale [0,1] is applied to reduce the magnitude
+ // of the s axis component of the normal.
+ // Define const1 in the third combiner similarly to affect the t axis component
+ // define these by "ramboing" them in the C++ code that uses this combiner script.
+ //
+ // rgb
+ // {
+ // // see comment about consts above!
+ // // t0 = s result in red only
+ // discard = spare0 * const0;
+ // discard = spare1 * const1;
+ // spare0 = sum();
+ // }
+ //
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_SPARE0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_CONSTANT_COLOR0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_SPARE1_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_CONSTANT_COLOR1_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 2
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 3
+ // rgb
+ // {
+ // tex1 = spare0 * spare0;
+ // scale_by_two();
+ // }
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_SPARE0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_SPARE0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_TEXTURE1_ARB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SCALE_BY_TWO_NV, GL.GL_NONE, false, false, false);
+
+ // Stage 3
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 4
+ //
+ // const0 = (1, 1, 0, 0);
+ // rgb
+ // {
+ // spare1 = unsigned_invert(tex1) . const0;
+ // scale_by_one_half();
+ // }
+ //
+ float[] const0 = new float[] { 1.0f, 1.0f, 0.0f, 0.0f };
+
+ gl.glCombinerStageParameterfvNV(GL.GL_COMBINER0_NV + stage, GL.GL_CONSTANT_COLOR0_NV, const0);
+
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_TEXTURE1_ARB, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_CONSTANT_COLOR0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_SPARE1_NV, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SCALE_BY_ONE_HALF_NV, GL.GL_NONE, true, false, false);
+
+ // Stage 4
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 5
+ //
+ // const0 = (0.5, 0.5, 0, 0);
+ // rgb
+ // {
+ // discard = spare0;
+ // discard = const0;
+ // spare0 = sum();
+ // }
+ //
+ const0 = new float[] { 0.5f, 0.5f, 0.0f, 0.0f };
+
+ gl.glCombinerStageParameterfvNV(GL.GL_COMBINER0_NV + stage, GL.GL_CONSTANT_COLOR0_NV, const0);
+
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_SPARE0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_CONSTANT_COLOR0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 5
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ // Stage 6
+ //
+ //
+ // const0 = (0, 0, 1, 1);
+ // rgb
+ // {
+ // discard = spare1 * const0;
+ // discard = spare0;
+ // spare0 = sum();
+ // }
+ //
+ const0 = new float[] { 0.0f, 0.0f, 1.0f, 1.0f };
+
+ gl.glCombinerStageParameterfvNV(GL.GL_COMBINER0_NV + stage, GL.GL_CONSTANT_COLOR0_NV, const0);
+
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_SPARE1_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_CONSTANT_COLOR0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_SPARE0_NV, GL.GL_SIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_SPARE0_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 6
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ gl.glEnable(GL.GL_PER_STAGE_CONSTANTS_NV);
+ gl.glCombinerParameteriNV(GL.GL_COLOR_SUM_CLAMP_NV, GL.GL_FALSE);
+ doFinal(gl, GL.GL_SPARE0_NV, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_ZERO, GL.GL_UNSIGNED_INVERT_NV);
+ }
+
+ private void initDotProductReflect(GL gl) {
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glTexEnvi(GL.GL_TEXTURE_SHADER_NV, GL.GL_SHADER_OPERATION_NV, GL.GL_TEXTURE_2D);
+
+ // 1 of 3
+ gl.glActiveTextureARB(GL.GL_TEXTURE1_ARB);
+ gl.glTexEnvi(GL.GL_TEXTURE_SHADER_NV, GL.GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV, GL.GL_EXPAND_NORMAL_NV);
+ gl.glTexEnvi(GL.GL_TEXTURE_SHADER_NV, GL.GL_SHADER_OPERATION_NV, GL.GL_DOT_PRODUCT_NV);
+ gl.glTexEnvi(GL.GL_TEXTURE_SHADER_NV, GL.GL_PREVIOUS_TEXTURE_INPUT_NV, GL.GL_TEXTURE0_ARB);
+
+ // 2 of 3
+ gl.glActiveTextureARB(GL.GL_TEXTURE2_ARB);
+ gl.glTexEnvi(GL.GL_TEXTURE_SHADER_NV, GL.GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV, GL.GL_EXPAND_NORMAL_NV);
+ gl.glTexEnvi(GL.GL_TEXTURE_SHADER_NV, GL.GL_SHADER_OPERATION_NV, GL.GL_DOT_PRODUCT_NV);
+ gl.glTexEnvi(GL.GL_TEXTURE_SHADER_NV, GL.GL_PREVIOUS_TEXTURE_INPUT_NV, GL.GL_TEXTURE0_ARB);
+
+ // 3 of 3
+ gl.glActiveTextureARB(GL.GL_TEXTURE3_ARB);
+ gl.glTexEnvi(GL.GL_TEXTURE_SHADER_NV, GL.GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV, GL.GL_EXPAND_NORMAL_NV);
+ gl.glTexEnvi(GL.GL_TEXTURE_SHADER_NV, GL.GL_SHADER_OPERATION_NV, GL.GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV);
+ gl.glTexEnvi(GL.GL_TEXTURE_SHADER_NV, GL.GL_PREVIOUS_TEXTURE_INPUT_NV, GL.GL_TEXTURE0_ARB);
+
+ gl.glActiveTextureARB(GL.GL_TEXTURE0_ARB);
+ gl.glCombinerParameteriNV(GL.GL_NUM_GENERAL_COMBINERS_NV, 1);
+
+ int stage = 0;
+ // Stage 0
+ // rgb
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_A_NV, GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_C_NV, GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB);
+ doIn(gl, stage, GL.GL_RGB, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_RGB);
+ doOut(gl, stage, GL.GL_RGB, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+
+ // Stage 0
+ // alpha
+ discardAlpha(gl, stage);
+
+ ++stage;
+
+ gl.glDisable(GL.GL_PER_STAGE_CONSTANTS_NV);
+ gl.glCombinerParameteriNV(GL.GL_COLOR_SUM_CLAMP_NV, GL.GL_FALSE);
+ doFinal(gl, GL.GL_TEXTURE3_ARB, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV);
+ }
+
+ private void discardAlpha(GL gl, int stage) {
+ doIn(gl, stage, GL.GL_ALPHA, GL.GL_VARIABLE_A_NV, GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_BLUE);
+ doIn(gl, stage, GL.GL_ALPHA, GL.GL_VARIABLE_B_NV, GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_BLUE);
+ doIn(gl, stage, GL.GL_ALPHA, GL.GL_VARIABLE_C_NV, GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_BLUE);
+ doIn(gl, stage, GL.GL_ALPHA, GL.GL_VARIABLE_D_NV, GL.GL_ZERO, GL.GL_UNSIGNED_IDENTITY_NV, GL.GL_BLUE);
+ doOut(gl, stage, GL.GL_ALPHA, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_DISCARD_NV, GL.GL_NONE, GL.GL_NONE, false, false, false);
+ }
+
+ private void doIn(GL gl,
+ int stage,
+ int colorSpace,
+ int variable,
+ int reg,
+ int operation,
+ int colorSelector) {
+ gl.glCombinerInputNV(GL.GL_COMBINER0_NV + stage,
+ colorSpace,
+ variable,
+ reg,
+ operation,
+ colorSelector);
+ }
+
+ private void doOut(GL gl,
+ int stage,
+ int colorSpace,
+ int in0,
+ int in1,
+ int out0,
+ int scale,
+ int bias,
+ boolean unknown0,
+ boolean unknown1,
+ boolean unknown2) {
+ gl.glCombinerOutputNV(GL.GL_COMBINER0_NV + stage,
+ colorSpace,
+ in0,
+ in1,
+ out0,
+ scale,
+ bias,
+ unknown0,
+ unknown1,
+ unknown2);
+ }
+
+ private void doFinal(GL gl,
+ int variableDInput,
+ int variableDOperation,
+ int variableGInput,
+ int variableGOperation) {
+ gl.glFinalCombinerInputNV(GL.GL_VARIABLE_A_NV,
+ GL.GL_ZERO,
+ GL.GL_UNSIGNED_IDENTITY_NV,
+ GL.GL_RGB);
+
+ gl.glFinalCombinerInputNV(GL.GL_VARIABLE_B_NV,
+ GL.GL_ZERO,
+ GL.GL_UNSIGNED_IDENTITY_NV,
+ GL.GL_RGB);
+
+ gl.glFinalCombinerInputNV(GL.GL_VARIABLE_C_NV,
+ GL.GL_ZERO,
+ GL.GL_UNSIGNED_IDENTITY_NV,
+ GL.GL_RGB);
+
+ gl.glFinalCombinerInputNV(GL.GL_VARIABLE_D_NV,
+ variableDInput,
+ variableDOperation,
+ GL.GL_RGB);
+
+ gl.glFinalCombinerInputNV(GL.GL_VARIABLE_E_NV,
+ GL.GL_ZERO,
+ GL.GL_UNSIGNED_IDENTITY_NV,
+ GL.GL_RGB);
+
+ gl.glFinalCombinerInputNV(GL.GL_VARIABLE_F_NV,
+ GL.GL_ZERO,
+ GL.GL_UNSIGNED_IDENTITY_NV,
+ GL.GL_RGB);
+
+ gl.glFinalCombinerInputNV(GL.GL_VARIABLE_G_NV,
+ variableGInput,
+ variableGOperation,
+ GL.GL_ALPHA);
+ }
+}