[Resolves #369] Fixes 3d rendering for instanced components

This is a relatively major refactor / rewrite of the 3d rendering code.

- components geometries are rendered recursively
- components inherit parents' transformations ( translation, rotation)
- implemented Transformation#toGLMatrix()
  -- openrocket transformations can be directly fed into Java OpenGL

- added: FinSet#getBoundingBox()
- improved documentation on RocketComponent Location methods

- Refactor RocketRenderer:
  - render component trees recursively
  - removed RocketRendere#isDrawn(c) -- return true in all implementations

- Refactor ComponentRenderer
  - renamed variables to be more descriptive
  - changed RocketComponent#toAbsolute(...) => RocketComponent#getComponentLocations()
- Adjust FinRender Code:
  - Render Single Fin Instance at-a-time
  - takes in an angle for the instance
  - assumes the fin is already at it's desired position.
  - renames 'fs' -> 'finSet'
This commit is contained in:
Daniel_M_Williams 2017-10-20 13:39:35 -04:00
parent 9456c3a14a
commit 23a488db48
11 changed files with 270 additions and 171 deletions

View File

@ -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<Coordinate> 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

View File

@ -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
*
* <p>
* 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*
*
* <p>
* 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

View File

@ -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

View File

@ -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);

View File

@ -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<Class<?>, Color> defaultColorCache = new HashMap<Class<?>, 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);

View File

@ -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);

View File

@ -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 <bkuker@billkuker.com>
* @author Daniel Williams <equipoise@gmail.com>
*/
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> 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<Geometry> getTreeGeometry( FlightConfiguration config){
System.err.println(String.format("==== Building tree geometry ===="));
return getTreeGeometry("", new ArrayList<Geometry>(), config, config.getRocket(), Transformation.IDENTITY);
}
private Collection<Geometry> getTreeGeometry(String indent, Collection<Geometry> 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<Geometry> 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);
}
}

View File

@ -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 <bkuker@billkuker.com>
* @author Daniel Williams <equipoise@gmail.com>
*/
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);
}
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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 <equipoise@gmail.com>
*/
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;
}
}