diff --git a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java index 6daa3ce78..b9497953b 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java @@ -585,7 +585,18 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab return fins * (h * h / 12 + MathUtil.pow2(h / 2 + radius)); } - + + public BoundingBox getBoundingBox() { + BoundingBox singleFinBounds= new BoundingBox( getFinPoints()); + final double finLength = singleFinBounds.max.x; + final double finHeight = singleFinBounds.max.y; + + BoundingBox compBox = new BoundingBox( getComponentLocations() ); + + BoundingBox finSetBox = new BoundingBox( compBox.min.sub( 0, finHeight, finHeight ), + compBox.max.add( finLength, finHeight, finHeight )); + return finSetBox; + } /** * Adds bounding coordinates to the given set. The body tube will fit within the @@ -595,16 +606,7 @@ public abstract class FinSet extends ExternalComponent implements RingInstanceab */ @Override public Collection getComponentBounds() { - BoundingBox singleFinBounds= new BoundingBox( getFinPoints()); - final double finLength = singleFinBounds.max.x; - final double finHeight = singleFinBounds.max.y; - - BoundingBox compBox = new BoundingBox( getComponentLocations() ); - - BoundingBox finSetBox = new BoundingBox( compBox.min.sub( 0, finHeight, finHeight ), - compBox.max.add( finLength, finHeight, finHeight )); - - return finSetBox.toCollection(); + return getBoundingBox().toCollection(); } @Override diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index 7e166293e..6e60e5285 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -1193,6 +1193,13 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab return locations; } + /** + * Provides locations of all instances of component relative to this component's reference point + * + *

+ * NOTE: the length of this array returned always equals this.getInstanceCount() + * @return + */ public Coordinate[] getInstanceOffsets(){ // According to the language specification, Java will initialized double values to 0.0 return new Coordinate[]{Coordinate.ZERO}; @@ -1205,6 +1212,14 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab return getComponentLocations(); } + + /** + * Provides locations of all instances of component *accounting for all parent instancing* + * + *

+ * NOTE: the length of this array MAY OR MAY NOT EQUAL this.getInstanceCount() + * @return + */ public Coordinate[] getComponentLocations() { if (null == this.parent) { // == improperly initialized components OR the root Rocket instance diff --git a/core/src/net/sf/openrocket/util/Transformation.java b/core/src/net/sf/openrocket/util/Transformation.java index 48ceec437..3e76c9c7e 100644 --- a/core/src/net/sf/openrocket/util/Transformation.java +++ b/core/src/net/sf/openrocket/util/Transformation.java @@ -16,6 +16,7 @@ import java.util.Iterator; public class Transformation implements java.io.Serializable { + public static final double ANGLE_EPSILON = 0.000000001; public static final Transformation IDENTITY = new Transformation(); @@ -214,6 +215,9 @@ public class Transformation implements java.io.Serializable { * @return The transformation. */ public static Transformation rotate_x(double theta) { + if( ANGLE_EPSILON > Math.abs(theta)) { + return Transformation.IDENTITY; + } return new Transformation(new double[][]{ {1,0,0}, {0,Math.cos(theta),-Math.sin(theta)}, @@ -226,6 +230,9 @@ public class Transformation implements java.io.Serializable { * @return The transformation. */ public static Transformation rotate_y(double theta) { + if( ANGLE_EPSILON > Math.abs(theta)) { + return Transformation.IDENTITY; + } return new Transformation(new double[][]{ {Math.cos(theta),0,Math.sin(theta)}, {0,1,0}, @@ -238,6 +245,9 @@ public class Transformation implements java.io.Serializable { * @return The transformation. */ public static Transformation rotate_z(double theta) { + if( ANGLE_EPSILON > Math.abs(theta)) { + return Transformation.IDENTITY; + } return new Transformation(new double[][]{ {Math.cos(theta),-Math.sin(theta),0}, {Math.sin(theta),Math.cos(theta),0}, @@ -337,7 +347,7 @@ public class Transformation implements java.io.Serializable { * * @return */ - public DoubleBuffer toGLTransform() { + public DoubleBuffer getGLMatrix() { double[] data = new double[]{1,0,0,0,0,1,0,0,0,0,1,0,1,1,1,1}; // output array is in column-major order diff --git a/core/test/net/sf/openrocket/util/TestTransformation.java b/core/test/net/sf/openrocket/util/TestTransformation.java index 4d5605669..a9f914985 100644 --- a/core/test/net/sf/openrocket/util/TestTransformation.java +++ b/core/test/net/sf/openrocket/util/TestTransformation.java @@ -26,7 +26,7 @@ public class TestTransformation { @Test public void testTransformIdentityToOpenGL() { Transformation t = Transformation.IDENTITY; - DoubleBuffer buf = t.toGLTransform(); + DoubleBuffer buf = t.getGLMatrix(); assertEquals( 1.0, buf.get(0), 1e-6); assertEquals( 0.0, buf.get(1), 1e-6); @@ -52,7 +52,7 @@ public class TestTransformation { @Test public void testTransformTranslationToOpenGL() { Transformation translate = new Transformation( 1,2,3 ); - DoubleBuffer buf = translate.toGLTransform(); + DoubleBuffer buf = translate.getGLMatrix(); assertEquals( 1.0, buf.get(0), 1e-6); assertEquals( 0.0, buf.get(1), 1e-6); @@ -79,7 +79,7 @@ public class TestTransformation { @Test public void testTransformRotateByPI2ToOpenGL() { Transformation translate = Transformation.getAxialRotation(M_PI_2); - DoubleBuffer buf = translate.toGLTransform(); + DoubleBuffer buf = translate.getGLMatrix(); assertEquals( 1.0, buf.get(0), 1e-6); assertEquals( 0.0, buf.get(1), 1e-6); diff --git a/swing/src/net/sf/openrocket/gui/figure3d/FigureRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/FigureRenderer.java index 61e0dc993..2a3adc9c8 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/FigureRenderer.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/FigureRenderer.java @@ -8,6 +8,7 @@ import javax.media.opengl.GL2ES1; import javax.media.opengl.GLAutoDrawable; import javax.media.opengl.fixedfunc.GLLightingFunc; +import net.sf.openrocket.gui.figure3d.geometry.Geometry; import net.sf.openrocket.gui.figure3d.geometry.Geometry.Surface; import net.sf.openrocket.motor.Motor; import net.sf.openrocket.rocketcomponent.BodyTube; @@ -51,13 +52,6 @@ public class FigureRenderer extends RocketRenderer { gl.glEnable(GLLightingFunc.GL_NORMALIZE); } - - - @Override - public boolean isDrawn(RocketComponent c) { - return true; - } - @Override public boolean isDrawnTransparent(RocketComponent c) { if (c instanceof BodyTube) @@ -78,8 +72,8 @@ public class FigureRenderer extends RocketRenderer { private static final HashMap, Color> defaultColorCache = new HashMap, Color>(); @Override - public void renderComponent(GL2 gl, RocketComponent c, float alpha) { - + public void renderComponent(GL2 gl, Geometry geom, float alpha) { + RocketComponent c = geom.getComponent(); gl.glLightModeli(GL2ES1.GL_LIGHT_MODEL_TWO_SIDE, 1); Color figureColor = c.getColor(); if (figureColor == null) { @@ -100,9 +94,9 @@ public class FigureRenderer extends RocketRenderer { gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_DIFFUSE, color, 0); gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_AMBIENT, color, 0); - cr.getGeometry(c, Surface.INSIDE).render(gl); + geom.render(gl,Surface.INSIDE); - //OUtside + //Outside // Set up the front A&D color convertColor(figureColor, color); color[3] = alpha; @@ -120,8 +114,8 @@ public class FigureRenderer extends RocketRenderer { gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_SPECULAR, color, 0); gl.glMateriali(GL.GL_FRONT, GLLightingFunc.GL_SHININESS, getShine(c)); - cr.getGeometry(c, Surface.OUTSIDE).render(gl); - cr.getGeometry(c, Surface.EDGES).render(gl); + geom.render(gl, Surface.OUTSIDE); + geom.render(gl, Surface.EDGES); color[0] = color[1] = color[2] = 0; gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_SPECULAR, color, 0); diff --git a/swing/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java index 4e9e8640e..f842e4ba1 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/RealisticRenderer.java @@ -74,11 +74,6 @@ public class RealisticRenderer extends RocketRenderer { textures.dispose(drawable); } - @Override - public boolean isDrawn(RocketComponent c) { - return true; - } - @Override public boolean isDrawnTransparent(RocketComponent c) { // if there is any degree of transparency, then... @@ -90,22 +85,23 @@ public class RealisticRenderer extends RocketRenderer { @Override protected void renderMotor(final GL2 gl, final Motor motor) { - render(gl, cr.getGeometry(motor, Surface.OUTSIDE), DefaultAppearance.getDefaultAppearance(motor), true, 1); + render(gl, cr.getMotorGeometry(motor), Surface.OUTSIDE, DefaultAppearance.getDefaultAppearance(motor), true, 1); } @Override - public void renderComponent(final GL2 gl, final RocketComponent c, final float alpha) { - if (getAppearance(c).getPaint().getAlpha()<255){ + public void renderComponent(final GL2 gl, Geometry geom, final float alpha) { + Appearance app = getAppearance( geom.getComponent() ); + if (app.getPaint().getAlpha()<255){ // if transparent, draw inside the same as the outside so we dont get a cardboard interior on a clear payload bay - render(gl, cr.getGeometry(c, Surface.INSIDE), getAppearance(c), true, alpha); + render(gl, geom, Surface.INSIDE, app, true, alpha); }else{ - render(gl, cr.getGeometry(c, Surface.INSIDE), DefaultAppearance.getDefaultAppearance(c), true, 1.0f); + render(gl, geom, Surface.INSIDE, DefaultAppearance.getDefaultAppearance(geom.getComponent()), true, 1.0f); } - render(gl, cr.getGeometry(c, Surface.OUTSIDE), getAppearance(c), true, alpha); - render(gl, cr.getGeometry(c, Surface.EDGES), getAppearance(c), false, alpha); + render(gl, geom, Surface.OUTSIDE, app, true, alpha); + render(gl, geom, Surface.EDGES, app, false, alpha); } - private void render(GL2 gl, Geometry g, Appearance a, boolean decals, float alpha) { + private void render(GL2 gl, Geometry g, Surface which, Appearance a, boolean decals, float alpha) { final Decal t = a.getTexture(); final Texture tex = textures.getTexture(t); @@ -123,7 +119,7 @@ public class RealisticRenderer extends RocketRenderer { gl.glMateriali(GL.GL_FRONT, GLLightingFunc.GL_SHININESS, (int) (100 * a.getShine())); - g.render(gl); + g.render(gl,which); if (decals && t != null && tex != null) { @@ -161,7 +157,7 @@ public class RealisticRenderer extends RocketRenderer { gl.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotrophy); } - g.render(gl); + g.render(gl,which); if (t.getEdgeMode() == Decal.EdgeMode.STICKER) { gl.glDepthFunc(GL.GL_LESS); diff --git a/swing/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java index fc86ef1fe..6edc4fc9a 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/RocketRenderer.java @@ -2,6 +2,8 @@ package net.sf.openrocket.gui.figure3d; import java.awt.Point; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collection; import java.util.Set; import java.util.Vector; @@ -16,17 +18,19 @@ import org.slf4j.LoggerFactory; import net.sf.openrocket.gui.figure3d.geometry.ComponentRenderer; import net.sf.openrocket.gui.figure3d.geometry.DisplayListComponentRenderer; +import net.sf.openrocket.gui.figure3d.geometry.Geometry; import net.sf.openrocket.gui.figure3d.geometry.Geometry.Surface; import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.MotorConfiguration; import net.sf.openrocket.rocketcomponent.FlightConfiguration; -import net.sf.openrocket.rocketcomponent.FlightConfigurationId; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.Transformation; /* * @author Bill Kuker + * @author Daniel Williams */ public abstract class RocketRenderer { protected static final Logger log = LoggerFactory.getLogger(RocketRenderer.class); @@ -47,10 +51,8 @@ public abstract class RocketRenderer { cr.updateFigure(drawable); } - public abstract void renderComponent(GL2 gl, RocketComponent c, float alpha); - - public abstract boolean isDrawn(RocketComponent c); - + public abstract void renderComponent(GL2 gl, Geometry geom, float alpha); + public abstract boolean isDrawnTransparent(RocketComponent c); public abstract void flushTextureCache(GLAutoDrawable drawable); @@ -76,9 +78,9 @@ public abstract class RocketRenderer { pickParts.add(c); if (isDrawnTransparent(c)) { - cr.getGeometry(c, Surface.INSIDE).render(gl); + cr.getComponentGeometry(c).render(gl, Surface.INSIDE); } else { - cr.getGeometry(c, Surface.ALL).render(gl); + cr.getComponentGeometry(c).render(gl, Surface.ALL); } } @@ -105,6 +107,9 @@ public abstract class RocketRenderer { if (cr == null) throw new IllegalStateException(this + " Not Initialized"); + + Collection geometry = getTreeGeometry( configuration); + GL2 gl = drawable.getGL().getGL2(); gl.glEnable(GL.GL_DEPTH_TEST); // enables depth testing @@ -117,19 +122,20 @@ public abstract class RocketRenderer { gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_SPECULAR, colorBlack, 0); gl.glLineWidth(5.0f); - for (RocketComponent c : configuration.getActiveComponents()) { - if (selection.contains(c)) { + for (Geometry geom : geometry) { + RocketComponent rc = geom.getComponent(); + if (selection.contains( rc)) { // Draw as lines, set Z to nearest gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_LINE); gl.glDepthRange(0, 0); - cr.getGeometry(c, Surface.ALL).render(gl); + geom.render(gl, Surface.ALL); // Draw polygons, always passing depth test, // setting Z to farthest gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_FILL); gl.glDepthRange(1, 1); gl.glDepthFunc(GL.GL_ALWAYS); - cr.getGeometry(c, Surface.ALL).render(gl); + geom.render(gl, Surface.ALL); gl.glDepthFunc(GL.GL_LESS); gl.glDepthRange(0, 1); } @@ -140,34 +146,72 @@ public abstract class RocketRenderer { gl.glEnable(GL.GL_CULL_FACE); gl.glCullFace(GL.GL_BACK); + gl.glEnable( GL.GL_BLEND ); + + // needs to be rendered before the components + renderMotors(gl, configuration); + + // render all components + renderTree( gl, geometry ); - // Draw all inner components - for (RocketComponent c : configuration.getActiveComponents()) { - if (isDrawn(c)) { - if (!isDrawnTransparent(c)) { - renderComponent(gl, c, 1.0f); - } - } - } - - renderMotors(gl, configuration); - - // Draw T&T front faces blended, without depth test - gl.glEnable(GL.GL_BLEND); - for (RocketComponent c : configuration.getActiveComponents()) { - if (isDrawn(c)) { - if (isDrawnTransparent(c)) { - renderComponent(gl, c, 0.2f); - } - } - } - gl.glDisable(GL.GL_BLEND); - + gl.glDisable( GL.GL_BLEND ); } + private Collection getTreeGeometry( FlightConfiguration config){ + System.err.println(String.format("==== Building tree geometry ====")); + return getTreeGeometry("", new ArrayList(), config, config.getRocket(), Transformation.IDENTITY); + } + + private Collection getTreeGeometry(String indent, Collection treeGeometry, FlightConfiguration config, RocketComponent comp, final Transformation parentTransform){ + final int instanceCount = comp.getInstanceCount(); + double[] instanceAngles = comp.getInstanceAngles(); + Coordinate[] instanceLocations = comp.getInstanceLocations(); + + if( instanceLocations.length != instanceAngles.length ){ + throw new ArrayIndexOutOfBoundsException(String.format("lengths of location array (%d) and angle arrays (%d) differs! (in: %s) ", instanceLocations.length, instanceAngles.length, comp.getName())); + } + + // iterate over the aggregated instances for the whole tree. + for( int instanceNumber = 0; instanceNumber < instanceCount; ++instanceNumber) { + Coordinate currentLocation = instanceLocations[instanceNumber]; + final double currentAngle = instanceAngles[instanceNumber]; + +// System.err.println( String.format("%s[ %s ]", indent, comp.getName())); +// System.err.println( String.format("%s :: %12.8g / %12.8g / %12.8g (m) @ %8.4g (rads) ", indent, currentLocation.x, currentLocation.y, currentLocation.z, currentAngle )); + + Transformation currentTransform = parentTransform + .applyTransformation( Transformation.getTranslationTransform( currentLocation)) + .applyTransformation( Transformation.rotate_x( currentAngle )); + + + // recurse into inactive trees: allow active stages inside inactive stages + for(RocketComponent child: comp.getChildren()) { + getTreeGeometry(indent+" ", treeGeometry, config, child, currentTransform ); + } + + Geometry geom = cr.getComponentGeometry( comp, currentTransform ); + geom.active = config.isComponentActive( comp ); + treeGeometry.add( geom ); + } + return treeGeometry; + } + + private void renderTree( GL2 gl, final Collection geometryList){ + for(Geometry geom: geometryList ) { + if( geom.active ) { + if( isDrawnTransparent( (RocketComponent)geom.obj) ){ + // Draw T&T front faces blended, without depth test + renderComponent(gl, geom, 0.2f); + }else{ + renderComponent(gl, geom, 1.0f); + } + } + } + } + private void renderMotors(GL2 gl, FlightConfiguration configuration) { - FlightConfigurationId motorID = configuration.getFlightConfigurationID(); - +// FlightConfigurationId motorID = configuration.getFlightConfigurationID(); +// // for( RocketComponent comp : configuration.getActiveComponents()){ // if( comp instanceof MotorMount){ // @@ -212,7 +256,7 @@ public abstract class RocketRenderer { } protected void renderMotor(GL2 gl, Motor motor) { - cr.getGeometry(motor, Surface.ALL).render(gl); + cr.getMotorGeometry(motor).render(gl, Surface.ALL); } } diff --git a/swing/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java index e1563217b..58854c32d 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java @@ -26,9 +26,11 @@ import net.sf.openrocket.rocketcomponent.Transition; import net.sf.openrocket.rocketcomponent.Transition.Shape; import net.sf.openrocket.rocketcomponent.TubeFinSet; import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.Transformation; /* * @author Bill Kuker + * @author Daniel Williams */ public class ComponentRenderer { @SuppressWarnings("unused") @@ -55,70 +57,73 @@ public class ComponentRenderer { } - public Geometry getGeometry(final RocketComponent c, final Surface which) { - return new Geometry() { + public Geometry getComponentGeometry(final RocketComponent comp) { + return getComponentGeometry(comp, Transformation.IDENTITY); + } + + public Geometry getComponentGeometry(final RocketComponent comp, final Transformation transform ) { + return new Geometry(comp, transform) { @Override - public void render(GL2 gl) { + public void render(GL2 gl, final Surface which) { + gl.glPushMatrix(); + + gl.glMultMatrixd( transform.getGLMatrix() ); + if (which == Surface.ALL) { - renderGeometry(gl, c, Surface.INSIDE); - renderGeometry(gl, c, Surface.EDGES); - renderGeometry(gl, c, Surface.OUTSIDE); + renderInstance(gl, comp, Surface.INSIDE); + renderInstance(gl, comp, Surface.EDGES); + renderInstance(gl, comp, Surface.OUTSIDE); } else { - renderGeometry(gl, c, which); + renderInstance(gl, comp, which); } + gl.glPopMatrix(); } }; } - public Geometry getGeometry(final Motor motor, Surface which) { - return new Geometry() { + public Geometry getMotorGeometry(final Motor motor) { + return new Geometry(motor, Transformation.IDENTITY) { @Override - public void render(GL2 gl) { + public void render(GL2 gl, final Surface which) { renderMotor(gl, motor); } }; } - protected void renderGeometry(GL2 gl, RocketComponent c, Surface which) { + protected void renderInstance(GL2 gl, RocketComponent c, Surface which) { if (glu == null) throw new IllegalStateException(this + " Not Initialized"); glu.gluQuadricNormals(q, GLU.GLU_SMOOTH); - Coordinate[] oo = c.toAbsolute(new Coordinate(0, 0, 0)); - - for (Coordinate o : oo) { - gl.glPushMatrix(); - - gl.glTranslated(o.x, o.y, o.z); - - if (c instanceof BodyTube) { - renderTube(gl, (BodyTube) c, which); - } else if (c instanceof LaunchLug) { - renderLug(gl, (LaunchLug) c, which); - } else if ( c instanceof RailButton ){ - renderRailButton(gl, (RailButton) c, which); - } else if (c instanceof RingComponent) { - if (which == Surface.OUTSIDE) - renderRing(gl, (RingComponent) c); - } else if (c instanceof Transition) { - renderTransition(gl, (Transition) c, which); - } else if (c instanceof MassObject) { - if (which == Surface.OUTSIDE) - renderMassObject(gl, (MassObject) c); - } else if (c instanceof FinSet) { - if (which == Surface.OUTSIDE) - fr.renderFinSet(gl, (FinSet) c); - } else if (c instanceof TubeFinSet) { - renderTubeFins( gl, (TubeFinSet) c, which); - } else if ( c instanceof AxialStage ) { - } else if ( c instanceof ParallelStage ) { - } else if ( c instanceof PodSet ) { - } else { - renderOther(gl, c); + if (c instanceof BodyTube) { + renderTube(gl, (BodyTube) c, which); + } else if (c instanceof LaunchLug) { + renderLug(gl, (LaunchLug) c, which); + } else if ( c instanceof RailButton ){ + renderRailButton(gl, (RailButton) c, which); + } else if (c instanceof RingComponent) { + if (which == Surface.OUTSIDE) + renderRing(gl, (RingComponent) c); + } else if (c instanceof Transition) { + renderTransition(gl, (Transition) c, which); + } else if (c instanceof MassObject) { + if (which == Surface.OUTSIDE) + renderMassObject(gl, (MassObject) c); + } else if (c instanceof FinSet) { + FinSet fins = (FinSet) c; + if (which == Surface.OUTSIDE) { + fr.renderFinSet(gl, fins); } - gl.glPopMatrix(); + } else if (c instanceof TubeFinSet) { + renderTubeFins( gl, (TubeFinSet) c, which); + } else if ( c instanceof AxialStage ) { + } else if ( c instanceof ParallelStage ) { + } else if ( c instanceof PodSet ) { + } else { + renderOther(gl, c); } + } diff --git a/swing/src/net/sf/openrocket/gui/figure3d/geometry/DisplayListComponentRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/geometry/DisplayListComponentRenderer.java index c27c81d95..a274beb0d 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/geometry/DisplayListComponentRenderer.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/geometry/DisplayListComponentRenderer.java @@ -24,14 +24,14 @@ public class DisplayListComponentRenderer extends ComponentRenderer { } @Override - protected void renderGeometry(GL2 gl, RocketComponent c, Surface which) { + protected void renderInstance(GL2 gl, RocketComponent c, Surface which) { Key k = new Key(c, which); if (lists.containsKey(k)) { gl.glCallList(lists.get(k)); } else { int list = gl.glGenLists(1); gl.glNewList(list, GL2.GL_COMPILE_AND_EXECUTE); - super.renderGeometry(gl, c, which); + super.renderInstance(gl, c, which); gl.glEndList(); lists.put(k, list); } diff --git a/swing/src/net/sf/openrocket/gui/figure3d/geometry/FinRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/geometry/FinRenderer.java index 783bdc1cf..1f3f7557e 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/geometry/FinRenderer.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/geometry/FinRenderer.java @@ -11,45 +11,33 @@ import javax.media.opengl.glu.GLUtessellatorCallbackAdapter; import net.sf.openrocket.rocketcomponent.EllipticalFinSet; import net.sf.openrocket.rocketcomponent.FinSet; +import net.sf.openrocket.util.BoundingBox; import net.sf.openrocket.util.Coordinate; public class FinRenderer { private GLUtessellator tobj = GLU.gluNewTess(); - public void renderFinSet(final GL2 gl, FinSet fs) { - - Coordinate finPoints[] = fs.getFinPointsWithTab(); - - double minX = Double.MAX_VALUE; - double minY = Double.MAX_VALUE; - double maxX = Double.MIN_VALUE; - double maxY = Double.MIN_VALUE; - - for (int i = 0; i < finPoints.length; i++) { - Coordinate c = finPoints[i]; - minX = Math.min(c.x, minX); - minY = Math.min(c.y, minY); - maxX = Math.max(c.x, maxX); - maxY = Math.max(c.y, maxY); - } + public void renderFinSet(final GL2 gl, FinSet finSet ) { + BoundingBox bounds = finSet.getBoundingBox(); gl.glMatrixMode(GL.GL_TEXTURE); gl.glPushMatrix(); - gl.glScaled(1 / (maxX - minX), 1 / (maxY - minY), 0); - gl.glTranslated(-minX, -minY - fs.getBodyRadius(), 0); + gl.glScaled(1 / (bounds.max.x - bounds.min.x), 1 / (bounds.max.y - bounds.min.y), 0); + gl.glTranslated(-bounds.min.x, -bounds.min.y - finSet.getBodyRadius(), 0); gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW); - - gl.glRotated(fs.getBaseRotation() * (180.0 / Math.PI), 1, 0, 0); - - for (int fin = 0; fin < fs.getFinCount(); fin++) { - - gl.glPushMatrix(); - - gl.glTranslated(fs.getLength() / 2, 0, 0); - gl.glRotated(fs.getCantAngle() * (180.0 / Math.PI), 0, 1, 0); - gl.glTranslated(-fs.getLength() / 2, 0, 0); - - GLUtessellatorCallback cb = new GLUtessellatorCallbackAdapter() { + + Coordinate finPoints[] = finSet.getFinPointsWithTab(); + { + gl.glPushMatrix(); + + gl.glTranslated(finSet.getLength() / 2, 0, 0); + + gl.glTranslated(0, - finSet.getBodyRadius(), 0); + + gl.glRotated( Math.toDegrees(finSet.getCantAngle()), 0, 1, 0); + gl.glTranslated(-finSet.getLength() / 2, 0, 0); + + GLUtessellatorCallback cb = new GLUtessellatorCallbackAdapter() { @Override public void vertex(Object vertexData) { double d[] = (double[]) vertexData; @@ -72,26 +60,28 @@ public class FinRenderer { GLU.gluTessCallback(tobj, GLU.GLU_TESS_BEGIN, cb); GLU.gluTessCallback(tobj, GLU.GLU_TESS_END, cb); + // fin side: +z GLU.gluTessBeginPolygon(tobj, null); GLU.gluTessBeginContour(tobj); gl.glNormal3f(0, 0, 1); for (int i = finPoints.length - 1; i >= 0; i--) { Coordinate c = finPoints[i]; - double[] p = new double[] { c.x, c.y + fs.getBodyRadius(), - c.z + fs.getThickness() / 2.0 }; + double[] p = new double[] { c.x, c.y + finSet.getBodyRadius(), + c.z + finSet.getThickness() / 2.0 }; GLU.gluTessVertex(tobj, p, 0, p); } GLU.gluTessEndContour(tobj); GLU.gluTessEndPolygon(tobj); + // fin side: -z GLU.gluTessBeginPolygon(tobj, null); GLU.gluTessBeginContour(tobj); gl.glNormal3f(0, 0, -1); for (int i = 0; i < finPoints.length; i++) { Coordinate c = finPoints[i]; - double[] p = new double[] { c.x, c.y + fs.getBodyRadius(), - c.z - fs.getThickness() / 2.0 }; + double[] p = new double[] { c.x, c.y + finSet.getBodyRadius(), + c.z - finSet.getThickness() / 2.0 }; GLU.gluTessVertex(tobj, p, 0, p); } @@ -99,7 +89,7 @@ public class FinRenderer { GLU.gluTessEndPolygon(tobj); // Strip around the edge - if (!(fs instanceof EllipticalFinSet)) + if (!(finSet instanceof EllipticalFinSet)) gl.glShadeModel(GLLightingFunc.GL_FLAT); gl.glBegin(GL.GL_TRIANGLE_STRIP); for (int i = 0; i <= finPoints.length; i++) { @@ -109,19 +99,17 @@ public class FinRenderer { % finPoints.length]; gl.glNormal3d(c2.y - c.y, c.x - c2.x, 0); // } - gl.glTexCoord2d(c.x, c.y + fs.getBodyRadius()); - gl.glVertex3d(c.x, c.y + fs.getBodyRadius(), - c.z - fs.getThickness() / 2.0); - gl.glVertex3d(c.x, c.y + fs.getBodyRadius(), - c.z + fs.getThickness() / 2.0); + gl.glTexCoord2d(c.x, c.y + finSet.getBodyRadius()); + gl.glVertex3d(c.x, c.y + finSet.getBodyRadius(), + c.z - finSet.getThickness() / 2.0); + gl.glVertex3d(c.x, c.y + finSet.getBodyRadius(), + c.z + finSet.getThickness() / 2.0); } gl.glEnd(); - if (!(fs instanceof EllipticalFinSet)) + if (!(finSet instanceof EllipticalFinSet)) gl.glShadeModel(GLLightingFunc.GL_SMOOTH); gl.glPopMatrix(); - - gl.glRotated(360.0 / fs.getFinCount(), 1, 0, 0); } gl.glMatrixMode(GL.GL_TEXTURE); diff --git a/swing/src/net/sf/openrocket/gui/figure3d/geometry/Geometry.java b/swing/src/net/sf/openrocket/gui/figure3d/geometry/Geometry.java index f67e22462..2946f7704 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/geometry/Geometry.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/geometry/Geometry.java @@ -1,11 +1,56 @@ + package net.sf.openrocket.gui.figure3d.geometry; +import net.sf.openrocket.motor.Motor; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Transformation; + import javax.media.opengl.GL2; -public interface Geometry { - public static enum Surface { - ALL, OUTSIDE, INSIDE, EDGES; + +/* + * @author Daniel Williams + */ +public abstract class Geometry { + public static enum Surface { + ALL, OUTSIDE, INSIDE, EDGES; + } + + public static final Geometry EMPTY = new Geometry(){ + @Override + public void render(GL2 Gl, Surface which){} + }; + + public final Object obj; + public final Transformation transform; + + public boolean active; + + public abstract void render(GL2 gl, Surface which ); + + private Geometry() { + // seriously, don't call this. + this.obj = null; + this.transform = null; } - public void render(GL2 gl); + public Geometry( Rocket rocket ) { + this.obj = rocket; + this.transform = Transformation.IDENTITY; + } + + public Geometry( RocketComponent component, Transformation transform) { + this.obj = component; + this.transform = transform; + } + + public Geometry( Motor motor, Transformation transform ) { + this.obj = motor; + this.transform = transform; + } + + public RocketComponent getComponent() { + return (RocketComponent)this.obj; + } }