/*
* 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.
*
*
*
* 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 = 1; // 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/**/ droplets = new ArrayList/**/(); // 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);
}
}