Simplify flame functions by breaking settings into a separate interface

Will also be cleaner to do per-motor flame someday.
This commit is contained in:
bkuker 2013-12-03 18:31:16 -05:00
parent 909a12ef15
commit c83b16b385
3 changed files with 197 additions and 182 deletions

View File

@ -364,7 +364,7 @@ public class PhotoPanel extends JPanel implements GLEventListener {
for (int i = 0; i < position.length; i++) { for (int i = 0; i < position.length; i++) {
gl.glPushMatrix(); gl.glPushMatrix();
gl.glTranslated(position[i].x + motor.getLength(), position[i].y, position[i].z); gl.glTranslated(position[i].x + motor.getLength(), position[i].y, position[i].z);
FlameRenderer.f(gl, p.isFlame(), p.isSmoke(), p.isSparks(), p.getSmokeColor(), p.getFlameColor(), motor, (float) p.getExhaustScale(), (float) p.getFlameAspectRatio()); FlameRenderer.drawExhaust(gl, p, motor);
gl.glPopMatrix(); gl.glPopMatrix();
} }
} }

View File

@ -1,11 +1,12 @@
package net.sf.openrocket.gui.figure3d.photo; package net.sf.openrocket.gui.figure3d.photo;
import net.sf.openrocket.gui.figure3d.photo.exhaust.FlameRenderer.FlameSettings;
import net.sf.openrocket.gui.figure3d.photo.sky.Sky; import net.sf.openrocket.gui.figure3d.photo.sky.Sky;
import net.sf.openrocket.gui.figure3d.photo.sky.builtin.Mountains; import net.sf.openrocket.gui.figure3d.photo.sky.builtin.Mountains;
import net.sf.openrocket.util.AbstractChangeSource; import net.sf.openrocket.util.AbstractChangeSource;
import net.sf.openrocket.util.Color; import net.sf.openrocket.util.Color;
public class PhotoSettings extends AbstractChangeSource { public class PhotoSettings extends AbstractChangeSource implements FlameSettings {
private double roll = 3.14; private double roll = 3.14;
private double yaw = 0; private double yaw = 0;
private double pitch = 2.05; private double pitch = 2.05;

View File

@ -139,15 +139,135 @@ import com.jogamp.opengl.util.texture.TextureData;
import com.jogamp.opengl.util.texture.TextureIO; import com.jogamp.opengl.util.texture.TextureIO;
public final class FlameRenderer { public final class FlameRenderer {
public interface FlameSettings {
public boolean isFlame();
public boolean isSmoke();
public Color getFlameColor();
public Color getSmokeColor();
public double getSmokeAlpha();
public boolean isSparks();
public double getExhaustScale();
public double getFlameAspectRatio();
}
private static final Logger log = LoggerFactory.getLogger(FlameRenderer.class); private static final Logger log = LoggerFactory.getLogger(FlameRenderer.class);
private FlameRenderer() { private FlameRenderer() {
} }
static Texture noise; public static void drawExhaust(GL2 gl, FlameSettings fs, Motor motor) {
protected static void convertColor(Color color, float[] out) { final float s = (float) Math.max(.5, Math.sqrt(motor.getAverageThrustEstimate()) / 4.0)
* (float) fs.getExhaustScale();
gl.glScalef(s, s, s);
gl.glRotated(90, 0, 1, 0);
gl.glTranslated(0, 0, 0);
gl.glDisable(GLLightingFunc.GL_LIGHTING);
if (fs.isSparks() && fs.isFlame()) {
sparks(gl, fs.getFlameColor());
}
gl.glEnable(GL.GL_BLEND);
gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_MODULATE);
gl.glDepthMask(false);
if (fs.isSmoke()) {
final float LEN = 10;
final float MAX_R = .15f;
final int P = 5;
final Func radius = new Func() {
@Override
public float f(double d) {
return (float) (Math.atan(d) / (Math.PI / 2.0)) * MAX_R + 0.001f;
}
};
final Func dZ = new Func() {
@Override
public float f(double z) {
return radius.f(z);
}
};
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
gl.glActiveTexture(GL.GL_TEXTURE0);
smokeT.bind(gl);
gl.glActiveTexture(GL.GL_TEXTURE1);
smokeN.bind(gl);
gl.glUseProgram(shaderprogram);
setUniform1i(gl, shaderprogram, "uSmoke", 0);
setUniform1i(gl, shaderprogram, "uNormal", 1);
trail(gl, radius, dZ, new Const(0.1f * (fs.getSmokeColor().getAlpha() / 255f)), LEN, P, fs.getSmokeColor(),
s);
// trail(gl, radius, dZ, new Const(1), 0.2f, 1, smokeColor);
gl.glUseProgram(0);
smokeN.disable(gl);
gl.glActiveTexture(GL.GL_TEXTURE0);
}
if (fs.isFlame()) {
gl.glScalef(1, 1, (float) fs.getFlameAspectRatio());
gl.glActiveTexture(GL.GL_TEXTURE0);
flameT.enable(gl);
flameT.bind(gl);
final float FLEN = 0.3f;
final int FP = 6;
final Func fr = new Func() {
@Override
public float f(double z) {
z = z / FLEN;
z = 1 - z;
return (float) (z * z - z * z * z) * .06f;
}
};
final Func fdZ = new Func() {
@Override
public float f(double z) {
return 0.002f;
}
};
final Func alpha = new Func() {
@Override
public float f(double z) {
return 0.2f * (float) Math.pow((1.0f - (float) z / FLEN), 4);
};
};
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);
trail(gl, fr, fdZ, alpha, FLEN, FP, fs.getFlameColor(), s);
flameT.disable(gl);
}
gl.glEnable(GLLightingFunc.GL_LIGHTING);
gl.glDepthMask(true);
}
private static void convertColor(Color color, float[] out) {
if (color == null) { if (color == null) {
out[0] = 1; out[0] = 1;
out[1] = 1; out[1] = 1;
@ -158,64 +278,70 @@ public final class FlameRenderer {
out[2] = (float) color.getBlue() / 255f; out[2] = (float) color.getBlue() / 255f;
} }
} }
static Texture flameT; static Texture flameT;
static Texture smokeT; static Texture smokeT;
static Texture smokeN; static Texture smokeN;
static int shaderprogram; static int shaderprogram;
private static interface Func { private static interface Func {
float f(double d); float f(double d);
} }
private static final class Const implements Func { private static final class Const implements Func {
final float val; final float val;
public Const(final float val) { public Const(final float val) {
this.val = val; this.val = val;
} }
@Override @Override
public float f(final double d) { public float f(final double d) {
return val; return val;
} }
} }
public static void init(GL2 gl) { public static void init(GL2 gl) {
try { try {
TextureData data = TextureIO.newTextureData(GLProfile.getDefault(), FlameRenderer.class.getResourceAsStream("/datafiles/flame/c-color.png"), GL.GL_RGBA, GL.GL_RGBA, true, null); TextureData data = TextureIO.newTextureData(GLProfile.getDefault(),
FlameRenderer.class.getResourceAsStream("/datafiles/flame/c-color.png"), GL.GL_RGBA, GL.GL_RGBA,
true, null);
smokeT = TextureIO.newTexture(data); smokeT = TextureIO.newTexture(data);
data = TextureIO.newTextureData(GLProfile.getDefault(), FlameRenderer.class.getResourceAsStream("/datafiles/flame/c-normal.png"), GL.GL_RGBA, GL.GL_RGBA, true, null); data = TextureIO.newTextureData(GLProfile.getDefault(),
FlameRenderer.class.getResourceAsStream("/datafiles/flame/c-normal.png"), GL.GL_RGBA, GL.GL_RGBA,
true, null);
smokeN = TextureIO.newTexture(data); smokeN = TextureIO.newTexture(data);
data = TextureIO.newTextureData(GLProfile.getDefault(), FlameRenderer.class.getResourceAsStream("/datafiles/flame/smoke2.png"), GL.GL_RGBA, GL.GL_RGBA, true, null); data = TextureIO.newTextureData(GLProfile.getDefault(),
FlameRenderer.class.getResourceAsStream("/datafiles/flame/smoke2.png"), GL.GL_RGBA, GL.GL_RGBA,
true, null);
flameT = TextureIO.newTexture(data); flameT = TextureIO.newTexture(data);
String line; String line;
shaderprogram = gl.glCreateProgram(); shaderprogram = gl.glCreateProgram();
/*int v = gl.glCreateShader(GL2.GL_FRAGMENT_SHADER); /*
BufferedReader brv = new BufferedReader(new InputStreamReader(FlameRenderer.class.getResourceAsStream("smokeVertex.glsl"))); * int v = gl.glCreateShader(GL2.GL_FRAGMENT_SHADER); BufferedReader
String vsrc = ""; * brv = new BufferedReader(new
while ((line = brv.readLine()) != null) { * InputStreamReader(FlameRenderer.class
vsrc += line + "\n"; * .getResourceAsStream("smokeVertex.glsl"))); String vsrc = "";
} * while ((line = brv.readLine()) != null) { vsrc += line + "\n"; }
gl.glShaderSource(v, 1, new String[] { vsrc }, (int[]) null, 0); * gl.glShaderSource(v, 1, new String[] { vsrc }, (int[]) null, 0);
gl.glAttachShader(shaderprogram, v); * gl.glAttachShader(shaderprogram, v); gl.glCompileShader(v);
gl.glCompileShader(v);*/ */
int f = gl.glCreateShader(GL2.GL_FRAGMENT_SHADER); int f = gl.glCreateShader(GL2.GL_FRAGMENT_SHADER);
BufferedReader brf = new BufferedReader(new InputStreamReader(FlameRenderer.class.getResourceAsStream("/datafiles/flame/smokeShader.glsl"))); BufferedReader brf = new BufferedReader(new InputStreamReader(
FlameRenderer.class.getResourceAsStream("/datafiles/flame/smokeShader.glsl")));
String fsrc = ""; String fsrc = "";
while ((line = brf.readLine()) != null) { while ((line = brf.readLine()) != null) {
fsrc += line + "\n"; fsrc += line + "\n";
} }
gl.glShaderSource(f, 1, new String[] { fsrc }, (int[]) null, 0); gl.glShaderSource(f, 1, new String[] { fsrc }, (int[]) null, 0);
gl.glCompileShader(f); gl.glCompileShader(f);
int statusFragmentShader[] = new int[1]; int statusFragmentShader[] = new int[1];
gl.glGetShaderiv(f, GL2.GL_COMPILE_STATUS, IntBuffer.wrap(statusFragmentShader)); gl.glGetShaderiv(f, GL2.GL_COMPILE_STATUS, IntBuffer.wrap(statusFragmentShader));
if (statusFragmentShader[0] == GL2.GL_FALSE) if (statusFragmentShader[0] == GL2.GL_FALSE) {
{
int infoLogLenght[] = new int[1]; int infoLogLenght[] = new int[1];
gl.glGetShaderiv(f, GL2.GL_INFO_LOG_LENGTH, IntBuffer.wrap(infoLogLenght)); gl.glGetShaderiv(f, GL2.GL_INFO_LOG_LENGTH, IntBuffer.wrap(infoLogLenght));
ByteBuffer infoLog = Buffers.newDirectByteBuffer(infoLogLenght[0]); ByteBuffer infoLog = Buffers.newDirectByteBuffer(infoLogLenght[0]);
@ -225,53 +351,46 @@ public final class FlameRenderer {
String out = new String(infoBytes); String out = new String(infoBytes);
System.err.println("Fragment shader error:\n" + out); System.err.println("Fragment shader error:\n" + out);
} }
gl.glAttachShader(shaderprogram, f); gl.glAttachShader(shaderprogram, f);
gl.glLinkProgram(shaderprogram); gl.glLinkProgram(shaderprogram);
gl.glValidateProgram(shaderprogram); gl.glValidateProgram(shaderprogram);
} catch (Exception e1) { } catch (Exception e1) {
e1.printStackTrace(); e1.printStackTrace();
} }
} }
private static void trail(GL2 gl, Func radius, Func dZ, Func alpha, float LEN, int P, Color color, float scale) { private static void trail(GL2 gl, Func radius, Func dZ, Func alpha, float LEN, int P, Color color, float scale) {
float[] c = new float[4]; float[] c = new float[4];
convertColor(color, c); convertColor(color, c);
//Figure out if the flame and smoke is point "in" or "out" of the screen // Figure out if the flame and smoke is point "in" or "out" of the
//in order to draw the particles in the right Z order // screen
// in order to draw the particles in the right Z order
final boolean startAtTop; final boolean startAtTop;
{ {
final double[] mvmatrix = new double[16]; final double[] mvmatrix = new double[16];
final double[] projmatrix = new double[16]; final double[] projmatrix = new double[16];
final int[] viewport = new int[4]; final int[] viewport = new int[4];
gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0); gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);
gl.glGetDoublev(GLMatrixFunc.GL_MODELVIEW_MATRIX, mvmatrix, 0); gl.glGetDoublev(GLMatrixFunc.GL_MODELVIEW_MATRIX, mvmatrix, 0);
gl.glGetDoublev(GLMatrixFunc.GL_PROJECTION_MATRIX, projmatrix, 0); gl.glGetDoublev(GLMatrixFunc.GL_PROJECTION_MATRIX, projmatrix, 0);
final double out[] = new double[4]; final double out[] = new double[4];
final double out2[] = new double[4]; final double out2[] = new double[4];
(new GLU()).gluProject(0, 0, 0, mvmatrix, 0, projmatrix, 0, viewport, 0, (new GLU()).gluProject(0, 0, 0, mvmatrix, 0, projmatrix, 0, viewport, 0, out, 0);
out, 0); (new GLU()).gluProject(0, 0, 0.01f, mvmatrix, 0, projmatrix, 0, viewport, 0, out2, 0);
(new GLU()).gluProject(0, 0, 0.01f, mvmatrix, 0, projmatrix, 0, viewport, 0,
out2, 0);
startAtTop = out2[2] < out[2]; startAtTop = out2[2] < out[2];
} }
final float start; final float start;
final float len; final float len;
final float mult; final float mult;
if (startAtTop) { if (startAtTop) {
start = 0.002f; start = 0.002f;
len = LEN; len = LEN;
@ -281,36 +400,35 @@ public final class FlameRenderer {
len = 0.002f; len = 0.002f;
mult = -1; mult = -1;
} }
//Use the same seed every time // Use the same seed every time
Random r = new Random(0); Random r = new Random(0);
//Loop forwards or backwards. Technically the dZ is applied differently // Loop forwards or backwards. Technically the dZ is applied differently
//in either direction, but the difference should be vanishingly small. // in either direction, but the difference should be vanishingly small.
for (float z = start; mult * z < mult * len; z += mult * dZ.f(z)) { for (float z = start; mult * z < mult * len; z += mult * dZ.f(z)) {
gl.glPushMatrix(); gl.glPushMatrix();
gl.glTranslatef(0, 0, z); gl.glTranslatef(0, 0, z);
c[3] = alpha.f(z); c[3] = alpha.f(z);
gl.glColor4fv(c, 0); gl.glColor4fv(c, 0);
for (int i = 0; i < P; i++) { for (int i = 0; i < P; i++) {
gl.glPushMatrix(); gl.glPushMatrix();
float rx = radius.f(z) - ((float) r.nextFloat() * radius.f(z) * 2.0f); float rx = radius.f(z) - ((float) r.nextFloat() * radius.f(z) * 2.0f);
float ry = radius.f(z) - ((float) r.nextFloat() * radius.f(z) * 2.0f); float ry = radius.f(z) - ((float) r.nextFloat() * radius.f(z) * 2.0f);
float rz = radius.f(z) - ((float) r.nextFloat() * radius.f(z) * 2.0f); float rz = radius.f(z) - ((float) r.nextFloat() * radius.f(z) * 2.0f);
gl.glTranslatef(rx, ry, rz); gl.glTranslatef(rx, ry, rz);
final double[] mvmatrix = new double[16]; final double[] mvmatrix = new double[16];
gl.glGetDoublev(GLMatrixFunc.GL_MODELVIEW_MATRIX, mvmatrix, 0); gl.glGetDoublev(GLMatrixFunc.GL_MODELVIEW_MATRIX, mvmatrix, 0);
mvmatrix[0] = mvmatrix[5] = mvmatrix[10] = 1; mvmatrix[0] = mvmatrix[5] = mvmatrix[10] = 1;
mvmatrix[1] = mvmatrix[2] = mvmatrix[4] = mvmatrix[6] = mvmatrix[8] = mvmatrix[9] = 0; mvmatrix[1] = mvmatrix[2] = mvmatrix[4] = mvmatrix[6] = mvmatrix[8] = mvmatrix[9] = 0;
gl.glLoadMatrixd(mvmatrix, 0); gl.glLoadMatrixd(mvmatrix, 0);
//Add a random rotation to prevent artifacts from texture. // Add a random rotation to prevent artifacts from texture.
gl.glRotatef(r.nextFloat()*45f, 0,0,1); gl.glRotatef(r.nextFloat() * 45f, 0, 0, 1);
gl.glBegin(GL.GL_TRIANGLE_FAN); gl.glBegin(GL.GL_TRIANGLE_FAN);
float d = radius.f(z) * scale * 2; float d = radius.f(z) * scale * 2;
gl.glTexCoord2f(0, 0); gl.glTexCoord2f(0, 0);
@ -322,15 +440,15 @@ public final class FlameRenderer {
gl.glTexCoord2f(1, 0); gl.glTexCoord2f(1, 0);
gl.glVertex3f(d, -d, 0); gl.glVertex3f(d, -d, 0);
gl.glEnd(); gl.glEnd();
gl.glPopMatrix(); gl.glPopMatrix();
} }
gl.glPopMatrix(); gl.glPopMatrix();
} }
} }
public static void setUniform1i(GL2 inGL, int inProgramID, String inName, int inValue) { private static void setUniform1i(GL2 inGL, int inProgramID, String inName, int inValue) {
int tUniformLocation = inGL.glGetUniformLocation(inProgramID, inName); int tUniformLocation = inGL.glGetUniformLocation(inProgramID, inName);
if (tUniformLocation != -1) { if (tUniformLocation != -1) {
inGL.glUniform1i(tUniformLocation, inValue); inGL.glUniform1i(tUniformLocation, inValue);
@ -338,15 +456,15 @@ public final class FlameRenderer {
log.warn("UNIFORM COULD NOT BE FOUND! NAME={}", inName); log.warn("UNIFORM COULD NOT BE FOUND! NAME={}", inName);
} }
} }
public static void sparks(GL2 gl, Color color) { private static void sparks(GL2 gl, Color color) {
//Use the same seed every time // Use the same seed every time
Random r = new Random(0); Random r = new Random(0);
float[] c = new float[4]; float[] c = new float[4];
float[] c2 = new float[4]; float[] c2 = new float[4];
convertColor(color, c); convertColor(color, c);
for (int i = 0; i < 3; i++){ for (int i = 0; i < 3; i++) {
c[i] = c2[i] = c[i] * .2f + .8f; c[i] = c2[i] = c[i] * .2f + .8f;
} }
c[3] = 1; c[3] = 1;
@ -367,109 +485,5 @@ public final class FlameRenderer {
gl.glEnd(); gl.glEnd();
} }
} }
public static void f(GL2 gl, boolean flame, boolean smoke, boolean sparks, Color smokeColor, Color flameColor, Motor motor, float scale, float aspect) {
final float s = (float) Math.max(.5, Math.sqrt(motor.getAverageThrustEstimate()) / 4.0) * scale;
gl.glScalef(s, s, s);
gl.glRotated(90, 0, 1, 0);
gl.glTranslated(0, 0, 0);
gl.glDisable(GLLightingFunc.GL_LIGHTING);
if (sparks && flame) {
sparks(gl, flameColor);
}
gl.glEnable(GL.GL_BLEND);
gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_MODULATE);
gl.glDepthMask(false);
if (smoke) {
final float LEN = 10;
final float MAX_R = .15f;
final int P = 5;
final Func radius = new Func() {
@Override
public float f(double d) {
return (float) (Math.atan(d) / (Math.PI / 2.0)) * MAX_R + 0.001f;
}
};
final Func dZ = new Func() {
@Override
public float f(double z) {
return radius.f(z);
}
};
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
gl.glActiveTexture(GL.GL_TEXTURE0);
smokeT.bind(gl);
gl.glActiveTexture(GL.GL_TEXTURE1);
smokeN.bind(gl);
gl.glUseProgram(shaderprogram);
setUniform1i(gl, shaderprogram, "uSmoke", 0);
setUniform1i(gl, shaderprogram, "uNormal", 1);
trail(gl, radius, dZ, new Const(0.1f * (smokeColor.getAlpha() / 255f)), LEN, P, smokeColor, s);
//trail(gl, radius, dZ, new Const(1), 0.2f, 1, smokeColor);
gl.glUseProgram(0);
smokeN.disable(gl);
gl.glActiveTexture(GL.GL_TEXTURE0);
}
if (flame) {
gl.glScalef(1, 1, aspect);
gl.glActiveTexture(GL.GL_TEXTURE0);
flameT.enable(gl);
flameT.bind(gl);
final float FLEN = 0.3f;
final int FP = 6;
final Func fr = new Func() {
@Override
public float f(double z) {
z = z / FLEN;
z = 1 - z;
return (float) (z * z - z * z * z) * .06f;
}
};
final Func fdZ = new Func() {
@Override
public float f(double z) {
return 0.002f;
}
};
final Func alpha = new Func() {
@Override
public float f(double z) {
return 0.2f * (float) Math.pow((1.0f - (float) z / FLEN), 4);
};
};
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);
trail(gl, fr, fdZ, alpha, FLEN, FP, flameColor, s);
flameT.disable(gl);
}
gl.glEnable(GLLightingFunc.GL_LIGHTING);
gl.glDepthMask(true);
}
} }