From fef4c39f73c860341550a5d8eba1c76e2fbe6e60 Mon Sep 17 00:00:00 2001 From: Bill Kuker Date: Sun, 1 Jul 2012 14:49:52 +0000 Subject: [PATCH] Move rules about how to render what into RenderStrategies. Implement Figure & Realistic strategies. --- .../gui/figure3d/FigureRenderStrategy.java | 118 +++++++++++++ .../gui/figure3d/RealisticRenderStrategy.java | 153 ++++++++++++++++ .../gui/figure3d/RenderStrategy.java | 31 ++++ .../gui/figure3d/RocketRenderer.java | 163 ++++-------------- 4 files changed, 340 insertions(+), 125 deletions(-) create mode 100644 core/src/net/sf/openrocket/gui/figure3d/FigureRenderStrategy.java create mode 100644 core/src/net/sf/openrocket/gui/figure3d/RealisticRenderStrategy.java create mode 100644 core/src/net/sf/openrocket/gui/figure3d/RenderStrategy.java diff --git a/core/src/net/sf/openrocket/gui/figure3d/FigureRenderStrategy.java b/core/src/net/sf/openrocket/gui/figure3d/FigureRenderStrategy.java new file mode 100644 index 000000000..51a7b3ec8 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/figure3d/FigureRenderStrategy.java @@ -0,0 +1,118 @@ +package net.sf.openrocket.gui.figure3d; + +import java.util.HashMap; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.GL2ES1; +import javax.media.opengl.fixedfunc.GLLightingFunc; + +import net.sf.openrocket.rocketcomponent.BodyTube; +import net.sf.openrocket.rocketcomponent.ExternalComponent; +import net.sf.openrocket.rocketcomponent.NoseCone; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.SymmetricComponent; +import net.sf.openrocket.rocketcomponent.Transition; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.Color; + +public class FigureRenderStrategy extends RenderStrategy { + private final float[] color = new float[4]; + + @Override + public boolean isDrawn(RocketComponent c) { + return true; + } + + @Override + public boolean isDrawnTransparent(RocketComponent c) { + if (c instanceof BodyTube) + return true; + if (c instanceof NoseCone) + return false; + if (c instanceof SymmetricComponent) { + if (((SymmetricComponent) c).isFilled()) + return false; + } + if (c instanceof Transition) { + Transition t = (Transition) c; + return !t.isAftShoulderCapped() && !t.isForeShoulderCapped(); + } + return false; + } + + private static final HashMap, Color> defaultColorCache = new HashMap, Color>(); + + @Override + public void preGeometry(GL2 gl, RocketComponent c, float alpha) { + gl.glLightModeli(GL2ES1.GL_LIGHT_MODEL_TWO_SIDE, 1); + Color figureColor = c.getColor(); + if (figureColor == null) { + if (defaultColorCache.containsKey(c.getClass())) { + figureColor = defaultColorCache.get(c.getClass()); + } else { + figureColor = Application.getPreferences().getDefaultColor(c.getClass()); + defaultColorCache.put(c.getClass(), figureColor); + } + } + + // Set up the front A&D color + convertColor(figureColor, color); + color[3] = alpha; + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_DIFFUSE, color, 0); + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_AMBIENT, color, 0); + + // Set up the Specular color & Shine + convertColor(figureColor, color); + float d = 0.9f; + float m = (float) getShine(c) / 128.0f; + color[0] = Math.max(color[0], d) * m; + color[1] = Math.max(color[1], d) * m; + color[2] = Math.max(color[2], d) * m; + + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_SPECULAR, color, 0); + gl.glMateriali(GL.GL_FRONT, GLLightingFunc.GL_SHININESS, getShine(c)); + + color[0] = color[1] = color[2] = 0; + gl.glMaterialfv(GL.GL_BACK, GLLightingFunc.GL_SPECULAR, color, 0); + + //Back A&D + convertColor(figureColor, color); + color[0] = color[0] * 0.4f; + color[1] = color[1] * 0.4f; + color[2] = color[2] * 0.4f; + color[3] = alpha; + gl.glMaterialfv(GL.GL_BACK, GLLightingFunc.GL_DIFFUSE, color, 0); + gl.glMaterialfv(GL.GL_BACK, GLLightingFunc.GL_AMBIENT, color, 0); + + } + + @Override + public void postGeometry(GL2 gl, RocketComponent c, float alpha) { + //Nothing to do here + + } + + + + private static int getShine(RocketComponent c) { + if (c instanceof ExternalComponent) { + switch (((ExternalComponent) c).getFinish()) { + case ROUGH: + return 10; + case UNFINISHED: + return 30; + case NORMAL: + return 40; + case SMOOTH: + return 80; + case POLISHED: + return 128; + default: + return 100; + } + } + return 20; + } + +} diff --git a/core/src/net/sf/openrocket/gui/figure3d/RealisticRenderStrategy.java b/core/src/net/sf/openrocket/gui/figure3d/RealisticRenderStrategy.java new file mode 100644 index 000000000..917a16386 --- /dev/null +++ b/core/src/net/sf/openrocket/gui/figure3d/RealisticRenderStrategy.java @@ -0,0 +1,153 @@ +package net.sf.openrocket.gui.figure3d; + +import java.util.HashMap; +import java.util.Map; + +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import javax.media.opengl.GL2ES1; +import javax.media.opengl.GLProfile; +import javax.media.opengl.fixedfunc.GLLightingFunc; +import javax.media.opengl.fixedfunc.GLMatrixFunc; + +import com.jogamp.opengl.util.texture.Texture; +import com.jogamp.opengl.util.texture.TextureData; +import com.jogamp.opengl.util.texture.TextureIO; + +import net.sf.openrocket.appearance.Appearance; +import net.sf.openrocket.appearance.Decal; +import net.sf.openrocket.logging.LogHelper; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; + +public class RealisticRenderStrategy extends RenderStrategy { + + private final float[] color = new float[4]; + private static final LogHelper log = Application.getLogger(); + private Map apMap = new HashMap(); + private Map texCache = new HashMap(); + + @Override + public void clearCaches() { + apMap.clear(); + texCache.clear(); + } + + @Override + public boolean isDrawn(RocketComponent c) { + return true; + } + + @Override + public boolean isDrawnTransparent(RocketComponent c) { + return false; + } + + @Override + public void preGeometry(GL2 gl, RocketComponent c, float alpha) { + Appearance a = getAppearance(c); + gl.glLightModeli(GL2ES1.GL_LIGHT_MODEL_TWO_SIDE, 1); + + convertColor(a.getDiffuse(), color); + color[3] = alpha; + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_DIFFUSE, color, 0); + + convertColor(a.getAmbient(), color); + color[3] = alpha; + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_AMBIENT, color, 0); + + convertColor(a.getSpecular(), color); + color[3] = alpha; + gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_SPECULAR, color, 0); + gl.glMateriali(GL.GL_FRONT, GLLightingFunc.GL_SHININESS, a.getShininess()); + + convertColor(a.getDiffuse(), color); + color[3] = alpha; + gl.glMaterialfv(GL.GL_BACK, GLLightingFunc.GL_DIFFUSE, color, 0); + gl.glMaterialfv(GL.GL_BACK, GLLightingFunc.GL_AMBIENT, color, 0); + + Decal t = a.getTexture(); + Texture tex = null; + if (t != null) { + tex = getTexture(t); + } + if (t != null && tex != null) { + tex.enable(gl); + tex.bind(gl); + gl.glMatrixMode(GL.GL_TEXTURE); + gl.glPushMatrix(); + + gl.glTranslated(-t.getCenter().x, -t.getCenter().y, 0); + gl.glRotated(57.2957795 * t.getRotation(), 0, 0, 1); + gl.glTranslated(t.getCenter().x, t.getCenter().y, 0); + + gl.glScaled(t.getScale().x, t.getScale().y, 0); + gl.glTranslated(t.getOffset().x, t.getOffset().y, 0); + + gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, toEdgeMode(t.getEdgeMode())); + + gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + } + } + + @Override + public void postGeometry(GL2 gl, RocketComponent c, float alpha) { + Appearance a = getAppearance(c); + Decal t = a.getTexture(); + Texture tex = null; + if (t != null) { + tex = getTexture(t); + } + if (tex != null) { + gl.glMatrixMode(GL.GL_TEXTURE); + gl.glPopMatrix(); + gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); + tex.disable(gl); + } + } + + private Texture getTexture(Decal t) { + if (texCache.containsKey(t)) + return texCache.get(t); + Texture tex = null; + try { + log.debug("Loading texture " + t); + TextureData data = TextureIO.newTextureData(GLProfile.getDefault(), t.getImageURL().openStream(), true, + null); + tex = TextureIO.newTexture(data); + } catch (Throwable e) { + log.error("Error loading Texture", e); + } + texCache.put(t, tex); + return tex; + } + + private Appearance getAppearance(RocketComponent c) { + if (apMap.containsKey(c)) + return apMap.get(c); + + Appearance ret = c.getAppearance(); + + if (ret == null) { + ret = Appearance.MISSING; + } + + apMap.put(c, ret); + System.out.println(c + ": " + ret); + return ret; + } + + private int toEdgeMode(Decal.EdgeMode m) { + switch (m) { + case REPEAT: + return GL.GL_REPEAT; + case MIRROR: + return GL.GL_MIRRORED_REPEAT; + case CLAMP: + return GL.GL_CLAMP_TO_EDGE; + default: + return GL.GL_CLAMP_TO_EDGE; + } + } + +} diff --git a/core/src/net/sf/openrocket/gui/figure3d/RenderStrategy.java b/core/src/net/sf/openrocket/gui/figure3d/RenderStrategy.java new file mode 100644 index 000000000..dd5ec525c --- /dev/null +++ b/core/src/net/sf/openrocket/gui/figure3d/RenderStrategy.java @@ -0,0 +1,31 @@ +package net.sf.openrocket.gui.figure3d; + +import javax.media.opengl.GL2; + +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Color; + +public abstract class RenderStrategy { + public abstract boolean isDrawn(RocketComponent c); + public abstract boolean isDrawnTransparent(RocketComponent c); + + public abstract void preGeometry(GL2 gl, RocketComponent c, float alpha); + + public abstract void postGeometry(GL2 gl, RocketComponent c, float alpha); + + public void clearCaches(){ + + } + + protected static void convertColor(Color color, float[] out) { + if ( color == null ){ + out[0] = 1; + out[1] = 1; + out[2] = 0; + } else { + out[0] = Math.max(0.2f, (float) color.getRed() / 255f); + out[1] = Math.max(0.2f, (float) color.getGreen() / 255f); + out[2] = Math.max(0.2f, (float) color.getBlue() / 255f); + } + } +} diff --git a/core/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java b/core/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java index 49341020c..f99f71ccb 100644 --- a/core/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java +++ b/core/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java @@ -2,92 +2,72 @@ package net.sf.openrocket.gui.figure3d; import java.awt.Point; import java.nio.ByteBuffer; -import java.util.HashMap; import java.util.Iterator; import java.util.Set; import java.util.Vector; import javax.media.opengl.GL; import javax.media.opengl.GL2; -import javax.media.opengl.GL2ES1; import javax.media.opengl.GL2GL3; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.fixedfunc.GLLightingFunc; +import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.motor.Motor; -import net.sf.openrocket.rocketcomponent.BodyTube; import net.sf.openrocket.rocketcomponent.Configuration; -import net.sf.openrocket.rocketcomponent.ExternalComponent; import net.sf.openrocket.rocketcomponent.MotorMount; -import net.sf.openrocket.rocketcomponent.NoseCone; import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.SymmetricComponent; -import net.sf.openrocket.rocketcomponent.Transition; import net.sf.openrocket.startup.Application; -import net.sf.openrocket.util.Color; import net.sf.openrocket.util.Coordinate; /* * @author Bill Kuker */ public class RocketRenderer { + @SuppressWarnings("unused") + private static final LogHelper log = Application.getLogger(); + + RenderStrategy currentStrategy = new RealisticRenderStrategy(); + ComponentRenderer cr; private final float[] selectedEmissive = { 1, 0, 0, 1 }; private final float[] colorBlack = { 0, 0, 0, 1 }; - private final float[] color = new float[4]; + public void init(GLAutoDrawable drawable) { cr = new ComponentRenderer(); cr.init(drawable); } - + public void updateFigure() { + currentStrategy.clearCaches(); cr.updateFigure(); } - private boolean isDrawn(RocketComponent c) { - return true; - } - - private boolean isDrawnTransparent(RocketComponent c) { - if (c instanceof BodyTube) - return true; - if (c instanceof NoseCone) - return false; - if (c instanceof SymmetricComponent) { - if (((SymmetricComponent) c).isFilled()) - return false; - } - if (c instanceof Transition) { - Transition t = (Transition) c; - return !t.isAftShoulderCapped() && !t.isForeShoulderCapped(); - } - return false; - } public RocketComponent pick(GLAutoDrawable drawable, Configuration configuration, Point p, Set ignore) { final GL2 gl = drawable.getGL().getGL2(); gl.glEnable(GL.GL_DEPTH_TEST); - - //Store a vector of pickable parts. + + // Store a vector of pickable parts. final Vector pickParts = new Vector(); - + for (RocketComponent c : configuration) { - if ( ignore != null && ignore.contains(c) ) + if (ignore != null && ignore.contains(c)) continue; - //Encode the index of the part as a color - //if index is 0x0ABC the color ends up as - //0xA0B0C000 with each nibble in the coresponding - //high bits of the RG and B channels. + // Encode the index of the part as a color + // if index is 0x0ABC the color ends up as + // 0xA0B0C000 with each nibble in the coresponding + // high bits of the RG and B channels. gl.glColor4ub((byte) ((pickParts.size() >> 4) & 0xF0), (byte) ((pickParts.size() << 0) & 0xF0), (byte) ((pickParts.size() << 4) & 0xF0), (byte) 1); pickParts.add(c); - - if (isDrawnTransparent(c)) { + + if (currentStrategy.isDrawnTransparent(c)) { gl.glEnable(GL.GL_CULL_FACE); gl.glCullFace(GL.GL_FRONT); cr.renderGeometry(gl, c); @@ -102,12 +82,12 @@ public class RocketRenderer { gl.glReadPixels(p.x, p.y, 1, 1, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, bb); final int pickColor = bb.getInt(); - final int pickIndex = ((pickColor >> 20) & 0xF00) | ((pickColor >> 16) & 0x0F0) - | ((pickColor >> 12) & 0x00F); + final int pickIndex = ((pickColor >> 20) & 0xF00) + | ((pickColor >> 16) & 0x0F0) | ((pickColor >> 12) & 0x00F); - if ( pickIndex < 0 || pickIndex > pickParts.size() - 1 ) + if (pickIndex < 0 || pickIndex > pickParts.size() - 1) return null; - + return pickParts.get(pickIndex); } @@ -123,8 +103,8 @@ public class RocketRenderer { // Draw all inner components for (RocketComponent c : configuration) { - if (isDrawn(c)) { - if (!isDrawnTransparent(c)) { + if (currentStrategy.isDrawn(c)) { + if (!currentStrategy.isDrawnTransparent(c)) { renderComponent(gl, c, 1.0f); } } @@ -137,8 +117,8 @@ public class RocketRenderer { gl.glEnable(GL.GL_CULL_FACE); gl.glCullFace(GL.GL_FRONT); for (RocketComponent c : configuration) { - if (isDrawn(c)) { - if (isDrawnTransparent(c)) { + if (currentStrategy.isDrawn(c)) { + if (currentStrategy.isDrawnTransparent(c)) { renderComponent(gl, c, 1.0f); } } @@ -150,8 +130,8 @@ public class RocketRenderer { gl.glEnable(GL.GL_CULL_FACE); gl.glCullFace(GL.GL_BACK); for (RocketComponent c : configuration) { - if (isDrawn(c)) { - if (isDrawnTransparent(c)) { + if (currentStrategy.isDrawn(c)) { + if (currentStrategy.isDrawnTransparent(c)) { renderComponent(gl, c, 0.2f); } } @@ -161,10 +141,13 @@ public class RocketRenderer { gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_EMISSION, selectedEmissive, 0); - gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_DIFFUSE, colorBlack, 0); - gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_AMBIENT, colorBlack, 0); - gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_SPECULAR, colorBlack, 0); - + gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_DIFFUSE, + colorBlack, 0); + gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_AMBIENT, + colorBlack, 0); + gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_SPECULAR, + colorBlack, 0); + gl.glDepthMask(false); gl.glDisable(GL.GL_DEPTH_TEST); gl.glEnable(GL.GL_STENCIL_TEST); @@ -223,81 +206,11 @@ public class RocketRenderer { } } - public void renderComponent(GL2 gl, RocketComponent c, float alpha) { - gl.glLightModeli(GL2ES1.GL_LIGHT_MODEL_TWO_SIDE, 1); - - getOutsideColor(c, alpha, color); - gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_DIFFUSE, color, 0); - gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_AMBIENT, color, 0); - - getSpecularColor(c, alpha, color); - gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_SPECULAR, color, 0); - gl.glMateriali(GL.GL_FRONT, GLLightingFunc.GL_SHININESS, - getShininess(c)); - - getInsideColor(c, alpha, color); - gl.glMaterialfv(GL.GL_BACK, GLLightingFunc.GL_DIFFUSE, color, 0); - gl.glMaterialfv(GL.GL_BACK, GLLightingFunc.GL_AMBIENT, color, 0); - + currentStrategy.preGeometry(gl, c, alpha); cr.renderGeometry(gl, c); + currentStrategy.postGeometry(gl, c, alpha); } - private int getShininess(RocketComponent c) { - if (c instanceof ExternalComponent) { - switch (((ExternalComponent) c).getFinish()) { - case ROUGH: - return 10; - case UNFINISHED: - return 30; - case NORMAL: - return 40; - case SMOOTH: - return 80; - case POLISHED: - return 128; - } - return 100; - } else { - return 20; - } - } - - private void getSpecularColor(RocketComponent c, float alpha, float[] out) { - int shine = getShininess(c); - float m = (float) shine / 128.0f; - float d = 0.9f; - getOutsideColor(c, alpha, out); - out[0] = Math.max(out[0], d) * m; - out[1] = Math.max(out[1], d) * m; - out[2] = Math.max(out[2], d) * m; - } - - private void getInsideColor(RocketComponent c, float alpha, float[] out) { - float d = 0.4f; - getOutsideColor(c, alpha, out); - out[0] *= d; - out[1] *= d; - out[2] *= d; - } - - private HashMap, Color> defaultColorCache = new HashMap, Color>(); - private void getOutsideColor(RocketComponent c, float alpha, float[] out) { - Color col; - col = c.getColor(); - if (col == null){ - if ( defaultColorCache.containsKey(c.getClass()) ){ - col = defaultColorCache.get(c.getClass()); - } else { - col = Application.getPreferences().getDefaultColor(c.getClass()); - defaultColorCache.put(c.getClass(), col); - } - } - - out[0] = Math.max(0.2f, (float) col.getRed() / 255f); - out[1] = Math.max(0.2f, (float) col.getGreen() / 255f); - out[2] = Math.max(0.2f, (float) col.getBlue() / 255f); - out[3] = alpha; - } }