1 package net.sf.openrocket.gui.figure3d;
\r
3 import java.awt.Point;
\r
4 import java.nio.ByteBuffer;
\r
5 import java.util.HashMap;
\r
6 import java.util.Iterator;
\r
7 import java.util.Set;
\r
8 import java.util.Vector;
\r
10 import javax.media.opengl.GL;
\r
11 import javax.media.opengl.GL2;
\r
12 import javax.media.opengl.GL2ES1;
\r
13 import javax.media.opengl.GL2GL3;
\r
14 import javax.media.opengl.GLAutoDrawable;
\r
15 import javax.media.opengl.fixedfunc.GLLightingFunc;
\r
17 import net.sf.openrocket.motor.Motor;
\r
18 import net.sf.openrocket.rocketcomponent.BodyTube;
\r
19 import net.sf.openrocket.rocketcomponent.Configuration;
\r
20 import net.sf.openrocket.rocketcomponent.ExternalComponent;
\r
21 import net.sf.openrocket.rocketcomponent.MotorMount;
\r
22 import net.sf.openrocket.rocketcomponent.NoseCone;
\r
23 import net.sf.openrocket.rocketcomponent.RocketComponent;
\r
24 import net.sf.openrocket.rocketcomponent.SymmetricComponent;
\r
25 import net.sf.openrocket.rocketcomponent.Transition;
\r
26 import net.sf.openrocket.startup.Application;
\r
27 import net.sf.openrocket.util.Color;
\r
28 import net.sf.openrocket.util.Coordinate;
\r
31 * @author Bill Kuker <bkuker@billkuker.com>
\r
33 public class RocketRenderer {
\r
34 ComponentRenderer cr;
\r
36 private final float[] selectedEmissive = { 1, 0, 0, 1 };
\r
37 private final float[] colorBlack = { 0, 0, 0, 1 };
\r
38 private final float[] color = new float[4];
\r
40 public void init(GLAutoDrawable drawable) {
\r
41 cr = new ComponentRenderer();
\r
45 public void updateFigure() {
\r
49 private boolean isDrawn(RocketComponent c) {
\r
53 private boolean isDrawnTransparent(RocketComponent c) {
\r
54 if (c instanceof BodyTube)
\r
56 if (c instanceof NoseCone)
\r
58 if (c instanceof SymmetricComponent) {
\r
59 if (((SymmetricComponent) c).isFilled())
\r
62 if (c instanceof Transition) {
\r
63 Transition t = (Transition) c;
\r
64 return !t.isAftShoulderCapped() && !t.isForeShoulderCapped();
\r
69 public RocketComponent pick(GLAutoDrawable drawable,
\r
70 Configuration configuration, Point p, Set<RocketComponent> ignore) {
\r
71 final GL2 gl = drawable.getGL().getGL2();
\r
72 gl.glEnable(GL.GL_DEPTH_TEST);
\r
74 //Store a vector of pickable parts.
\r
75 final Vector<RocketComponent> pickParts = new Vector<RocketComponent>();
\r
77 for (RocketComponent c : configuration) {
\r
78 if ( ignore != null && ignore.contains(c) )
\r
81 //Encode the index of the part as a color
\r
82 //if index is 0x0ABC the color ends up as
\r
83 //0xA0B0C000 with each nibble in the coresponding
\r
84 //high bits of the RG and B channels.
\r
85 gl.glColor4ub((byte) ((pickParts.size() >> 4) & 0xF0),
\r
86 (byte) ((pickParts.size() << 0) & 0xF0),
\r
87 (byte) ((pickParts.size() << 4) & 0xF0), (byte) 1);
\r
90 if (isDrawnTransparent(c)) {
\r
91 gl.glEnable(GL.GL_CULL_FACE);
\r
92 gl.glCullFace(GL.GL_FRONT);
\r
93 cr.renderGeometry(gl, c);
\r
94 gl.glDisable(GL.GL_CULL_FACE);
\r
96 cr.renderGeometry(gl, c);
\r
100 ByteBuffer bb = ByteBuffer.allocateDirect(4);
\r
102 gl.glReadPixels(p.x, p.y, 1, 1, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, bb);
\r
104 final int pickColor = bb.getInt();
\r
105 final int pickIndex = ((pickColor >> 20) & 0xF00) | ((pickColor >> 16) & 0x0F0)
\r
106 | ((pickColor >> 12) & 0x00F);
\r
108 if ( pickIndex < 0 || pickIndex > pickParts.size() - 1 )
\r
111 return pickParts.get(pickIndex);
\r
114 public void render(GLAutoDrawable drawable, Configuration configuration,
\r
115 Set<RocketComponent> selection) {
\r
117 throw new IllegalStateException(this + " Not Initialized");
\r
119 GL2 gl = drawable.getGL().getGL2();
\r
121 gl.glEnable(GL.GL_DEPTH_TEST); // enables depth testing
\r
122 gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
\r
124 // Draw all inner components
\r
125 for (RocketComponent c : configuration) {
\r
127 if (!isDrawnTransparent(c)) {
\r
128 renderComponent(gl, c, 1.0f);
\r
133 renderMotors(gl, configuration);
\r
135 // Draw Tube and Transition back faces, blended with depth test
\r
136 // so that they show up behind.
\r
137 gl.glEnable(GL.GL_CULL_FACE);
\r
138 gl.glCullFace(GL.GL_FRONT);
\r
139 for (RocketComponent c : configuration) {
\r
141 if (isDrawnTransparent(c)) {
\r
142 renderComponent(gl, c, 1.0f);
\r
146 gl.glDisable(GL.GL_CULL_FACE);
\r
148 // Draw T&T front faces blended, without depth test
\r
149 gl.glEnable(GL.GL_BLEND);
\r
150 gl.glEnable(GL.GL_CULL_FACE);
\r
151 gl.glCullFace(GL.GL_BACK);
\r
152 for (RocketComponent c : configuration) {
\r
154 if (isDrawnTransparent(c)) {
\r
155 renderComponent(gl, c, 0.2f);
\r
159 gl.glDisable(GL.GL_BLEND);
\r
160 gl.glDisable(GL.GL_CULL_FACE);
\r
162 gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_EMISSION,
\r
163 selectedEmissive, 0);
\r
164 gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_DIFFUSE, colorBlack, 0);
\r
165 gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_AMBIENT, colorBlack, 0);
\r
166 gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_SPECULAR, colorBlack, 0);
\r
168 gl.glDepthMask(false);
\r
169 gl.glDisable(GL.GL_DEPTH_TEST);
\r
170 gl.glEnable(GL.GL_STENCIL_TEST);
\r
172 for (RocketComponent c : configuration) {
\r
173 if (selection.contains(c)) {
\r
174 // So it is faster to do this once before the loop,
\r
175 // but then the outlines are not as good if you multi-select.
\r
176 // Not sure which to do.
\r
178 gl.glStencilMask(1);
\r
179 gl.glDisable(GL.GL_SCISSOR_TEST);
\r
180 gl.glClearStencil(0);
\r
181 gl.glClear(GL.GL_STENCIL_BUFFER_BIT);
\r
182 gl.glStencilMask(0);
\r
184 gl.glStencilFunc(GL.GL_ALWAYS, 1, 1);
\r
185 gl.glStencilMask(1);
\r
186 gl.glColorMask(false, false, false, false);
\r
187 gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_FILL);
\r
188 gl.glStencilOp(GL.GL_KEEP, GL.GL_KEEP, GL.GL_REPLACE);
\r
189 cr.renderGeometry(gl, c);
\r
190 gl.glStencilMask(0);
\r
192 gl.glColorMask(true, true, true, true);
\r
193 gl.glStencilFunc(GL.GL_NOTEQUAL, 1, 1);
\r
194 gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_LINE);
\r
195 gl.glLineWidth(5.0f);
\r
196 cr.renderGeometry(gl, c);
\r
199 gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_FILL);
\r
200 gl.glDepthMask(true);
\r
201 gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_EMISSION,
\r
203 gl.glDisable(GL.GL_STENCIL_TEST);
\r
204 gl.glEnable(GL.GL_DEPTH_TEST);
\r
207 private void renderMotors(GL2 gl, Configuration configuration) {
\r
208 String motorID = configuration.getMotorConfigurationID();
\r
209 Iterator<MotorMount> iterator = configuration.motorIterator();
\r
210 while (iterator.hasNext()) {
\r
211 MotorMount mount = iterator.next();
\r
212 Motor motor = mount.getMotor(motorID);
\r
213 double length = motor.getLength();
\r
214 double radius = motor.getDiameter() / 2;
\r
216 Coordinate[] position = ((RocketComponent) mount)
\r
217 .toAbsolute(new Coordinate(((RocketComponent) mount)
\r
218 .getLength() + mount.getMotorOverhang() - length));
\r
220 for (int i = 0; i < position.length; i++) {
\r
221 cr.renderMotor(gl, position[i], length, radius);
\r
228 public void renderComponent(GL2 gl, RocketComponent c, float alpha) {
\r
229 gl.glLightModeli(GL2ES1.GL_LIGHT_MODEL_TWO_SIDE, 1);
\r
231 getOutsideColor(c, alpha, color);
\r
232 gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_DIFFUSE, color, 0);
\r
233 gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_AMBIENT, color, 0);
\r
235 getSpecularColor(c, alpha, color);
\r
236 gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_SPECULAR, color, 0);
\r
237 gl.glMateriali(GL.GL_FRONT, GLLightingFunc.GL_SHININESS,
\r
240 getInsideColor(c, alpha, color);
\r
241 gl.glMaterialfv(GL.GL_BACK, GLLightingFunc.GL_DIFFUSE, color, 0);
\r
242 gl.glMaterialfv(GL.GL_BACK, GLLightingFunc.GL_AMBIENT, color, 0);
\r
244 cr.renderGeometry(gl, c);
\r
247 private int getShininess(RocketComponent c) {
\r
248 if (c instanceof ExternalComponent) {
\r
249 switch (((ExternalComponent) c).getFinish()) {
\r
267 private void getSpecularColor(RocketComponent c, float alpha, float[] out) {
\r
268 int shine = getShininess(c);
\r
269 float m = (float) shine / 128.0f;
\r
271 getOutsideColor(c, alpha, out);
\r
272 out[0] = Math.max(out[0], d) * m;
\r
273 out[1] = Math.max(out[1], d) * m;
\r
274 out[2] = Math.max(out[2], d) * m;
\r
277 private void getInsideColor(RocketComponent c, float alpha, float[] out) {
\r
279 getOutsideColor(c, alpha, out);
\r
285 private HashMap<Class<?>, Color> defaultColorCache = new HashMap<Class<?>, Color>();
\r
286 private void getOutsideColor(RocketComponent c, float alpha, float[] out) {
\r
288 col = c.getColor();
\r
290 if ( defaultColorCache.containsKey(c.getClass()) ){
\r
291 col = defaultColorCache.get(c.getClass());
\r
293 col = Application.getPreferences().getDefaultColor(c.getClass());
\r
294 defaultColorCache.put(c.getClass(), col);
\r
298 out[0] = Math.max(0.2f, (float) col.getRed() / 255f);
\r
299 out[1] = Math.max(0.2f, (float) col.getGreen() / 255f);
\r
300 out[2] = Math.max(0.2f, (float) col.getBlue() / 255f);
\r