[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:
parent
9456c3a14a
commit
23a488db48
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user