JOGL Based support for a 3D view of the rocket.

Change to RocketPanel to add 3D option.
Change to build process to use a Jar-in-Jar technique, not Fat Jar, using the Eclipse projects jar-in-jar loader. (Does not require eclipse!)
This commit is contained in:
Bill Kuker 2012-06-11 20:25:41 +00:00
parent 71b36bc481
commit 6695f92ab1
24 changed files with 1990 additions and 28 deletions

View File

@ -26,7 +26,9 @@
<classpathentry kind="lib" path="lib-test/uispec4j-2.3-jdk16.jar"/>
<classpathentry kind="lib" path="resources"/>
<classpathentry kind="lib" path="lib/jspf.core-1.0.2.jar" sourcepath="/home/sampo/Projects/lib/jspf/documentation/api"/>
<classpathentry kind="lib" path="lib/opencsv-2.3.jar"/>
<classpathentry kind="lib" path="lib/exp4j-0.2.9.jar"/>
<classpathentry kind="lib" path="lib/opencsv-2.3.jar"/>
<classpathentry kind="lib" path="lib/native/gluegen-rt.jar"/>
<classpathentry kind="lib" path="lib/native/jogl.all.jar"/>
<classpathentry kind="lib" path="lib/exp4j-0.2.9.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@ -67,25 +67,51 @@
<javac debug="true" srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath"/>
</target>
<!-- JAR -->
<target name="jar" depends="build" description="Create the OpenRocket jar file">
<copy todir="${dist.dir}/">
<fileset dir="." includes="LICENSE.TXT README.TXT ChangeLog ReleaseNotes fileformat.txt" />
<fileset dir="resources/"/>
<!-- Executible Eclipse-Jar-In-Jar style JAR -->
<target name="jar" depends="core-jar" description="Create the OpenRocket jar-in-jar Executable">
<mkdir dir="${jar.dir}" />
<jar destfile="${jar.file}">
<manifest>
<attribute name="Main-Class" value="org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader" />
<attribute name="Rsrc-Main-Class" value="${main-class}" />
<attribute name="SplashScreen-Image" value="pix/splashscreen.png" />
<attribute name="Class-Path" value="." />
<attribute name="Rsrc-Class-Path" value="./ main/${ant.project.name}-Core.jar lib/jfreechart-1.0.13.jar lib/jcommon-1.0.16.jar lib/gluegen-rt.jar lib/miglayout15-swing.jar lib/iText-5.0.2.jar lib/jogl.all.jar lib/opencsv-2.3.jar" />
</manifest>
<!-- Unzip the Eclipse JIJ Loader -->
<zipfileset src="lib/jar-in-jar-loader.jar" />
<!-- Include, in the root of the JAR, the resources needed by OR -->
<fileset dir="src/" includes="META-INF/" />
</copy>
<mkdir dir="${jar.dir}"/>
<jar destfile="${jar.file}" basedir="${dist.dir}">
<fileset dir="resources/" />
<!-- Include the core OpenRocket JAR -->
<zipfileset dir="${build.dir}/" prefix="main">
<include name="${ant.project.name}-Core.jar"/>
</zipfileset>
<!-- Include libraries needed by OR -->
<zipfileset dir="${lib.dir}" prefix="lib">
<include name="*.jar"/>
</zipfileset>
<zipfileset dir="${lib.dir}/native" prefix="lib">
<include name="*.jar"/>
</zipfileset>
<!-- Include metafiles about OR -->
<fileset dir="." includes="LICENSE.TXT README.TXT ChangeLog ReleaseNotes fileformat.txt" />
</jar>
</target>
<!-- Core OpenRocket JAR -->
<target name="core-jar" depends="build" description="Create the OpenRocket code-only jar file">
<jar destfile="${build.dir}/${ant.project.name}-Core.jar" basedir="${dist.dir}">
<manifest>
<attribute name="Main-Class" value="${main-class}"/>
<attribute name="SplashScreen-Image" value="pix/splashscreen.png"/>
</manifest>
<zipfileset src="lib/miglayout15-swing.jar" />
<zipfileset src="lib/jcommon-1.0.16.jar" />
<zipfileset src="lib/jfreechart-1.0.13.jar" />
<zipfileset src="lib/iText-5.0.2.jar" />
<zipfileset src="lib/opencsv-2.3.jar" />
<zipfileset src="lib/exp4j-0.2.9.jar" />
</jar>
</target>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,340 @@
package net.sf.openrocket.gui.figure3d;
import java.util.HashMap;
import java.util.Map;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.fixedfunc.GLLightingFunc;
import javax.media.opengl.fixedfunc.GLMatrixFunc;
import javax.media.opengl.glu.GLU;
import javax.media.opengl.glu.GLUquadric;
import javax.media.opengl.glu.GLUtessellator;
import javax.media.opengl.glu.GLUtessellatorCallback;
import javax.media.opengl.glu.GLUtessellatorCallbackAdapter;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.EllipticalFinSet;
import net.sf.openrocket.rocketcomponent.FinSet;
import net.sf.openrocket.rocketcomponent.LaunchLug;
import net.sf.openrocket.rocketcomponent.MassObject;
import net.sf.openrocket.rocketcomponent.RingComponent;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.Transition;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.Coordinate;
/*
* @author Bill Kuker <bkuker@billkuker.com>
*/
public class ComponentRenderer {
private static final LogHelper log = Application.getLogger();
private int LOD = 80;
GLU glu;
GLUquadric q;
GLUtessellator tobj;
public ComponentRenderer() {
}
public void init(GLAutoDrawable drawable) {
glu = new GLU();
q = glu.gluNewQuadric();
tobj = GLU.gluNewTess();
glu.gluQuadricTexture(q, true);
}
private Map<RocketComponent, Integer> lists = new HashMap<RocketComponent, Integer>();
private boolean clearDisplayLists = false;
public void updateFigure() {
clearDisplayLists = true;
}
public void renderGeometry(GL2 gl, RocketComponent c) {
if (glu == null)
throw new IllegalStateException(this + " Not Initialized");
glu.gluQuadricNormals(q, GLU.GLU_SMOOTH);
if ( clearDisplayLists ){
log.debug("Clearing Display Lists");
for ( int i : lists.values() ){
gl.glDeleteLists(i,1);
}
lists.clear();
clearDisplayLists = false;
}
if ( lists.containsKey(c) ){
gl.glCallList(lists.get(c));
} else {
int list = gl.glGenLists(1);
gl.glNewList(list, GL2.GL_COMPILE_AND_EXECUTE);
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);
} else if (c instanceof LaunchLug) {
renderLug(gl, (LaunchLug) c);
} else if (c instanceof RingComponent) {
renderRing(gl, (RingComponent) c);
} else if (c instanceof Transition) {
renderTransition(gl, (Transition) c);
} else if (c instanceof MassObject) {
renderMassObject(gl, (MassObject) c);
} else if (c instanceof FinSet) {
renderFinSet(gl, (FinSet) c);
} else {
renderOther(gl, c);
}
gl.glPopMatrix();
}
gl.glEndList();
lists.put(c, list);
}
}
private void renderOther(GL2 gl, RocketComponent c) {
gl.glBegin(GL.GL_LINES);
for (Coordinate cc : c.getComponentBounds()) {
for (Coordinate ccc : c.getComponentBounds()) {
gl.glVertex3d(cc.x, cc.y, cc.z);
gl.glVertex3d(ccc.x, ccc.y, ccc.z);
}
}
gl.glEnd();
}
private void renderTransition(GL2 gl, Transition t) {
gl.glRotated(90, 0, 1.0, 0);
if (t.getType() == Transition.Shape.CONICAL) {
glu.gluCylinder(q, t.getForeRadius(), t.getAftRadius(),
t.getLength(), LOD, 1);
} else {
TransitionRenderer.drawTransition(gl, t, LOD, LOD);
}
// Render AFT shoulder
gl.glPushMatrix();
gl.glTranslated(0, 0, t.getLength());
glu.gluCylinder(q, t.getAftShoulderRadius(), t.getAftShoulderRadius(),
t.getAftShoulderLength(), LOD, 1);
gl.glRotated(180, 0, 1.0, 0);
glu.gluDisk(q, t.getAftRadius(), t.getAftShoulderRadius(), LOD, 2);
gl.glTranslated(0, 0, -t.getAftShoulderLength());
if (t.isFilled() || t.isAftShoulderCapped()) {
glu.gluDisk(q, t.getAftShoulderRadius(), 0, LOD, 2);
}
gl.glPopMatrix();
// Render Fore Shoulder
gl.glPushMatrix();
gl.glRotated(180, 0, 1.0, 0);
glu.gluCylinder(q, t.getForeShoulderRadius(),
t.getForeShoulderRadius(), t.getForeShoulderLength(), LOD, 1);
gl.glRotated(180, 0, 1.0, 0);
glu.gluDisk(q, t.getForeRadius(), t.getForeShoulderRadius(), LOD, 2);
gl.glTranslated(0, 0, -t.getForeShoulderLength());
if (t.isFilled() || t.isForeShoulderCapped()) {
glu.gluDisk(q, t.getForeShoulderRadius(), 0, LOD, 2);
}
gl.glPopMatrix();
}
private void renderTube(GL2 gl, BodyTube t) {
gl.glRotated(90, 0, 1.0, 0);
glu.gluCylinder(q, t.getOuterRadius(), t.getOuterRadius(),
t.getLength(), LOD, 1);
}
private void renderRing(GL2 gl, RingComponent r) {
gl.glRotated(90, 0, 1.0, 0);
glu.gluCylinder(q, r.getOuterRadius(), r.getOuterRadius(),
r.getLength(), LOD, 1);
gl.glRotated(180, 0, 1.0, 0);
glu.gluDisk(q, r.getInnerRadius(), r.getOuterRadius(), LOD, 2);
gl.glRotated(180, 0, 1.0, 0);
gl.glTranslated(0, 0, r.getLength());
glu.gluDisk(q, r.getInnerRadius(), r.getOuterRadius(), LOD, 2);
gl.glTranslated(0, 0, -r.getLength());
glu.gluCylinder(q, r.getInnerRadius(), r.getInnerRadius(),
r.getLength(), LOD, 1);
}
private void renderLug(GL2 gl, LaunchLug t) {
gl.glRotated(90, 0, 1.0, 0);
glu.gluCylinder(q, t.getOuterRadius(), t.getOuterRadius(),
t.getLength(), LOD, 1);
}
private void renderMassObject(GL2 gl, MassObject o) {
gl.glRotated(90, 0, 1.0, 0);
MassObjectRenderer.drawMassObject(gl, o, LOD, LOD);
}
private 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);
}
gl.glMatrixMode(GL.GL_TEXTURE);
gl.glPushMatrix();
gl.glScaled(1/(maxX-minX), 1/(maxY-minY), 0);
gl.glTranslated(-minX, -minY - fs.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() {
@Override
public void vertex(Object vertexData) {
double d[] = (double[]) vertexData;
gl.glTexCoord2d(d[0], d[1]);
gl.glVertex3dv(d, 0);
}
@Override
public void begin(int type) {
gl.glBegin(type);
}
@Override
public void end() {
gl.glEnd();
}
};
GLU.gluTessCallback(tobj, GLU.GLU_TESS_VERTEX, cb);
GLU.gluTessCallback(tobj, GLU.GLU_TESS_BEGIN, cb);
GLU.gluTessCallback(tobj, GLU.GLU_TESS_END, cb);
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 };
GLU.gluTessVertex(tobj, p, 0, p);
}
GLU.gluTessEndContour(tobj);
GLU.gluTessEndPolygon(tobj);
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 };
GLU.gluTessVertex(tobj, p, 0, p);
}
GLU.gluTessEndContour(tobj);
GLU.gluTessEndPolygon(tobj);
// Strip around the edge
if (!(fs instanceof EllipticalFinSet))
gl.glShadeModel(GLLightingFunc.GL_FLAT);
gl.glBegin(GL.GL_TRIANGLE_STRIP);
for (int i = 0; i <= finPoints.length; i++) {
Coordinate c = finPoints[i % finPoints.length];
// if ( i > 1 ){
Coordinate c2 = finPoints[(i - 1 + finPoints.length)
% 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.glEnd();
if (!(fs instanceof EllipticalFinSet))
gl.glShadeModel(GLLightingFunc.GL_SMOOTH);
gl.glPopMatrix();
gl.glRotated(360.0 / fs.getFinCount(), 1, 0, 0);
}
gl.glMatrixMode(GL.GL_TEXTURE);
gl.glPopMatrix();
gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
}
public void renderMotor(final GL2 gl, final Coordinate c, double l, double r) {
final float outside[] = { 0.2f, 0.2f, 0.2f, 1.0f };
gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_DIFFUSE, outside, 0);
gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_AMBIENT, outside, 0);
gl.glPushMatrix();
gl.glTranslated(c.x, c.y, c.z);
gl.glRotated(90, 0, 1.0, 0);
glu.gluCylinder(q, r, r, l, LOD, 1);
glu.gluDisk(q, r, 0, LOD, 2);
gl.glTranslated(0, 0, l);
gl.glRotated(180, 0, 1.0, 0);
glu.gluDisk(q, r, 0, LOD, 2);
gl.glPopMatrix();
}
}

View File

@ -0,0 +1,263 @@
/*
** License Applicability. Except to the extent portions of this file are
** made subject to an alternative license as permitted in the SGI Free
** Software License B, Version 2.0 (the "License"), the contents of this
** file are subject only to the provisions of the License. You may not use
** this file except in compliance with the License. You may obtain a copy
** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
**
** http://oss.sgi.com/projects/FreeB
**
** Note that, as provided in the License, the Software is distributed on an
** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
**
** NOTE: The Original Code (as defined below) has been licensed to Sun
** Microsystems, Inc. ("Sun") under the SGI Free Software License B
** (Version 1.1), shown above ("SGI License"). Pursuant to Section
** 3.2(3) of the SGI License, Sun is distributing the Covered Code to
** you under an alternative license ("Alternative License"). This
** Alternative License includes all of the provisions of the SGI License
** except that Section 2.2 and 11 are omitted. Any differences between
** the Alternative License and the SGI License are offered solely by Sun
** and not by SGI.
**
** Original Code. The Original Code is: OpenGL Sample Implementation,
** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
** Copyright in any portions created by third parties is as indicated
** elsewhere herein. All Rights Reserved.
**
** Additional Notice Provisions: The application programming interfaces
** established by SGI in conjunction with the Original Code are The
** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
** Window System(R) (Version 1.3), released October 19, 1998. This software
** was created using the OpenGL(R) version 1.2.1 Sample Implementation
** published by SGI, but has not been independently verified as being
** compliant with the OpenGL(R) version 1.2.1 Specification.
**
** $Date: 2009-03-04 17:23:34 -0800 (Wed, 04 Mar 2009) $ $Revision: 1856 $
** $Header$
*/
/*
* Copyright (c) 2002-2004 LWJGL Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'LWJGL' nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
* MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
* ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
* DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
* SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed or intended for use
* in the design, construction, operation or maintenance of any nuclear
* facility.
*/
package net.sf.openrocket.gui.figure3d;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import net.sf.openrocket.rocketcomponent.MassObject;
public final class MassObjectRenderer {
private static final boolean textureFlag = true;
private MassObjectRenderer() {
}
public static final void drawMassObject(final GL2 gl, final MassObject o,
final int slices, final int stacks) {
double da, r, dz;
double x, y, z, nz, nsign;
int i, j;
nsign = 1.0f;
da = 2.0f * PI / slices;
dz = o.getLength() / stacks;
double ds = 1.0f / slices;
double dt = 1.0f / stacks;
double t = 0.0f;
z = 0.0f;
for (j = 0; j < stacks; j++) {
r = getRadius(o, z);
double rNext = getRadius(o, z + dz);
if (j == stacks - 1)
rNext = 0;
if (j == stacks - 1)
rNext = 0;
// Z component of normal vectors
nz = -(rNext - r) / dz;
double s = 0.0f;
glBegin(gl, GL2.GL_QUAD_STRIP);
for (i = 0; i <= slices; i++) {
if (i == slices) {
x = sin(0.0f);
y = cos(0.0f);
} else {
x = sin((i * da));
y = cos((i * da));
}
if (nsign == 1.0f) {
normal3d(gl, (x * nsign), (y * nsign), (nz * nsign));
TXTR_COORD(gl, s, t);
glVertex3d(gl, (x * r), (y * r), z);
normal3d(gl, (x * nsign), (y * nsign), (nz * nsign));
TXTR_COORD(gl, s, t + dt);
glVertex3d(gl, (x * rNext), (y * rNext), (z + dz));
} else {
normal3d(gl, x * nsign, y * nsign, nz * nsign);
TXTR_COORD(gl, s, t);
glVertex3d(gl, (x * r), (y * r), z);
normal3d(gl, x * nsign, y * nsign, nz * nsign);
TXTR_COORD(gl, s, t + dt);
glVertex3d(gl, (x * rNext), (y * rNext), (z + dz));
}
s += ds;
} // for slices
glEnd(gl);
// r += dr;
t += dt;
z += dz;
} // for stacks
}
private static final double getRadius(MassObject o, double z) {
double arc = Math.min(o.getLength(), 2 * o.getRadius()) * 0.35f;
double r = o.getRadius();
if (z == 0 || z == o.getLength())
return 0;
if (z < arc) {
double zz = z - arc;
return (r - arc) + Math.sqrt(arc * arc - zz * zz);
}
if (z > o.getLength() - arc) {
double zz = (z - o.getLength() + arc);
return (r - arc) + Math.sqrt(arc * arc - zz * zz);
}
return o.getRadius();
}
// ----------------------------------------------------------------------
// Internals only below this point
//
private static final double PI = Math.PI;
private static final void glBegin(GL gl, int mode) {
gl.getGL2().glBegin(mode);
}
private static final void glEnd(GL gl) {
gl.getGL2().glEnd();
}
private static final void glVertex3d(GL gl, double x, double y, double z) {
gl.getGL2().glVertex3d(x, y, z);
}
private static final void glNormal3d(GL gl, double x, double y, double z) {
gl.getGL2().glNormal3d(x, y, z);
}
private static final void glTexCoord2d(GL gl, double x, double y) {
gl.getGL2().glTexCoord2d(x, y);
}
/**
* Call glNormal3f after scaling normal to unit length.
*
* @param x
* @param y
* @param z
*/
private static final void normal3d(GL gl, double x, double y, double z) {
double mag;
mag = Math.sqrt(x * x + y * y + z * z);
if (mag > 0.00001F) {
x /= mag;
y /= mag;
z /= mag;
}
glNormal3d(gl, x, y, z);
}
private static final void TXTR_COORD(GL gl, double x, double y) {
if (textureFlag)
glTexCoord2d(gl, x, y);
}
private static final double sin(double r) {
return Math.sin(r);
}
private static final double cos(double r) {
return Math.cos(r);
}
}

View File

@ -0,0 +1,70 @@
package net.sf.openrocket.gui.figure3d;
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import net.sf.openrocket.database.ComponentPresetDatabase;
import net.sf.openrocket.database.ThrustCurveMotorSetDatabase;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.file.DatabaseMotorFinder;
import net.sf.openrocket.file.openrocket.importt.OpenRocketLoader;
import net.sf.openrocket.gui.main.componenttree.ComponentTree;
import net.sf.openrocket.gui.scalefigure.RocketPanel;
import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.l10n.ResourceBundleTranslator;
import net.sf.openrocket.startup.Application;
/**
* An application for quickly testing 3d figure witout all the OpenRocket user interface
*
* @author bkuker
*
*/
public class Quick3dMain {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
Application.setBaseTranslator(new ResourceBundleTranslator(
"l10n.messages"));
Application.setMotorSetDatabase(new ThrustCurveMotorSetDatabase(false) {
{
startLoading();
}
@Override
protected void loadMotors() {
}
});
Application.setPreferences(new SwingPreferences());
// Must be done after localization is initialized
ComponentPresetDatabase componentPresetDao = new ComponentPresetDatabase();
componentPresetDao.load("datafiles", ".*csv");
Application.setComponentPresetDao( componentPresetDao );
OpenRocketDocument doc = new OpenRocketLoader().loadFromStream(
Quick3dMain.class.getResourceAsStream("/datafiles/examples/Clustered rocket design.ork"),
new DatabaseMotorFinder());
JFrame ff = new JFrame();
ff.setSize(1200, 400);
ff.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
RocketPanel panel;
panel = new RocketPanel(doc);
ComponentTree ct = new ComponentTree(doc);
panel.setSelectionModel(ct.getSelectionModel());
JPanel p = new JPanel();
p.setLayout(new BorderLayout());
p.add(ct, BorderLayout.WEST);
p.add(panel, BorderLayout.CENTER);
ff.setContentPane(p);
ff.setVisible(true);
}
}

View File

@ -0,0 +1,586 @@
package net.sf.openrocket.gui.figure3d;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCapabilities;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLProfile;
import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.fixedfunc.GLLightingFunc;
import javax.media.opengl.fixedfunc.GLMatrixFunc;
import javax.media.opengl.glu.GLU;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter;
import net.sf.openrocket.gui.figureelements.CGCaret;
import net.sf.openrocket.gui.figureelements.CPCaret;
import net.sf.openrocket.gui.figureelements.FigureElement;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil;
import com.jogamp.opengl.util.awt.Overlay;
/*
* @author Bill Kuker <bkuker@billkuker.com>
*/
public class RocketFigure3d extends JPanel implements GLEventListener {
private static final long serialVersionUID = 1L;
private static final LogHelper log = Application.getLogger();
static {
//this allows the GL canvas and things like the motor selection
//drop down to z-order themselves.
JPopupMenu.setDefaultLightWeightPopupEnabled(false);
}
private static final double fovY = 15.0;
private static double fovX = Double.NaN;
private static final int CARET_SIZE = 20;
private Configuration configuration;
private GLCanvas canvas;
private Overlay extrasOverlay, caretOverlay;
private BufferedImage cgCaretRaster, cpCaretRaster;
private volatile boolean redrawExtras = true;
private final ArrayList<FigureElement> relativeExtra = new ArrayList<FigureElement>();
private final ArrayList<FigureElement> absoluteExtra = new ArrayList<FigureElement>();
private double roll = 0;
private double yaw = 0;
Point pickPoint = null;
MouseEvent pickEvent;
float[] lightPosition = new float[] { 1, 4, 1, 0 };
RocketRenderer rr = new RocketRenderer();
public RocketFigure3d(Configuration config) {
this.configuration = config;
this.setLayout(new BorderLayout());
addHierarchyListener(new HierarchyListener() {
@Override
public void hierarchyChanged(HierarchyEvent e) {
initGLCanvas();
RocketFigure3d.this.removeHierarchyListener(this);
}
});
}
private void initGLCanvas(){
log.debug("Initializing RocketFigure3D OpenGL Canvas");
try {
log.debug("Setting up GL capabilities...");
GLProfile glp = GLProfile.getDefault();
GLCapabilities caps = new GLCapabilities(glp);
caps.setSampleBuffers(true);
caps.setNumSamples(6);
caps.setStencilBits(1);
log.debug("Creating OpenGL Canvas");
canvas = new GLCanvas(caps);
canvas.addGLEventListener(this);
this.add(canvas, BorderLayout.CENTER);
setupMouseListeners();
rasterizeCarets();
} catch (Throwable t) {
log.error("An error occurred creating 3d View", t);
canvas = null;
this.add(new JLabel("Unable to load 3d Libraries: "
+ t.getMessage()));
}
}
/**
* Set up the standard rendering hints on the Graphics2D
*/
private static void setRenderingHints(Graphics2D g){
g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_NORMALIZE);
g.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
}
/**
* Rasterize the carets into 2 buffered images that I can blit onto the
* 3d display every redraw without all of the caret shape rendering overhead
*/
private void rasterizeCarets(){
Graphics2D g2d;
//Rasterize a CG Caret
cgCaretRaster = new BufferedImage(CARET_SIZE, CARET_SIZE, BufferedImage.TYPE_4BYTE_ABGR);
g2d = cgCaretRaster.createGraphics();
setRenderingHints(g2d);
g2d.setBackground(new Color(0, 0, 0, 0));
g2d.clearRect(0, 0, CARET_SIZE, CARET_SIZE);
new CGCaret(CARET_SIZE/2,CARET_SIZE/2).paint(g2d, 1.0);
g2d.dispose();
//Rasterize a CP Caret
cpCaretRaster = new BufferedImage(CARET_SIZE, CARET_SIZE, BufferedImage.TYPE_4BYTE_ABGR);
g2d = cpCaretRaster.createGraphics();
setRenderingHints(g2d);
g2d.setBackground(new Color(0, 0, 0, 0));
g2d.clearRect(0, 0, CARET_SIZE, CARET_SIZE);
new CPCaret(CARET_SIZE/2,CARET_SIZE/2).paint(g2d, 1.0);
g2d.dispose();
}
private void setupMouseListeners() {
MouseInputAdapter a = new MouseInputAdapter() {
int lastX;
int lastY;
MouseEvent pressEvent;
@Override
public void mousePressed(MouseEvent e) {
lastX = e.getX();
lastY = e.getY();
pressEvent = e;
}
@Override
public void mouseClicked(MouseEvent e) {
pickPoint = new Point(lastX, canvas.getHeight() - lastY);
pickEvent = e;
internalRepaint();
}
@Override
public void mouseDragged(MouseEvent e) {
int dx = lastX - e.getX();
int dy = lastY - e.getY();
lastX = e.getX();
lastY = e.getY();
if (pressEvent.getButton() == MouseEvent.BUTTON1) {
if (Math.abs(dx) > Math.abs(dy)) {
setYaw(yaw - (float) dx / 100.0);
} else {
if ( yaw > Math.PI/2.0 && yaw < 3.0*Math.PI/2.0 ){
dy = -dy;
}
setRoll(roll - (float) dy / 100.0);
}
} else {
lightPosition[0] -= 0.1f * dx;
lightPosition[1] += 0.1f * dy;
internalRepaint();
}
}
};
canvas.addMouseMotionListener(a);
canvas.addMouseListener(a);
}
public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
updateFigure();
}
@Override
public void display(GLAutoDrawable drawable) {
GL2 gl = drawable.getGL().getGL2();
GLU glu = new GLU();
gl.glEnable(GL.GL_MULTISAMPLE);
gl.glClearColor(1, 1, 1, 1);
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
setupView(gl, glu);
if (pickPoint != null) {
gl.glDisable(GLLightingFunc.GL_LIGHTING);
final RocketComponent picked = rr.pick(drawable, configuration,
pickPoint, pickEvent.isShiftDown()?selection:null );
if (csl != null && picked != null) {
final MouseEvent e = pickEvent;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
csl.componentClicked(new RocketComponent[] { picked },
e);
}
});
}
pickPoint = null;
gl.glClearColor(1, 1, 1, 1);
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
gl.glEnable(GLLightingFunc.GL_LIGHTING);
}
rr.render(drawable, configuration, selection);
drawExtras(gl, glu);
drawCarets(gl, glu);
}
private void drawCarets(GL2 gl, GLU glu) {
final Graphics2D og2d = caretOverlay.createGraphics();
setRenderingHints(og2d);
og2d.setBackground(new Color(0, 0, 0, 0));
og2d.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
caretOverlay.markDirty(0, 0, canvas.getWidth(), canvas.getHeight());
// The existing relative Extras don't really work right for 3d.
Coordinate pCP = project(cp, gl, glu);
Coordinate pCG = project(cg, gl, glu);
final int d = CARET_SIZE/2;
//z order the carets
if (pCG.z < pCP.z) {
//Subtract half of the caret size, so they are centered ( The +/- d in each translate)
//Flip the sense of the Y coordinate from GL to normal (Y+ up/down)
og2d.drawRenderedImage(
cpCaretRaster,
AffineTransform.getTranslateInstance((pCP.x - d),
canvas.getHeight() - (pCP.y + d)));
og2d.drawRenderedImage(
cgCaretRaster,
AffineTransform.getTranslateInstance((pCG.x - d),
canvas.getHeight() - (pCG.y + d)));
} else {
og2d.drawRenderedImage(
cgCaretRaster,
AffineTransform.getTranslateInstance((pCG.x - d),
canvas.getHeight() - (pCG.y + d)));
og2d.drawRenderedImage(
cpCaretRaster,
AffineTransform.getTranslateInstance((pCP.x - d),
canvas.getHeight() - (pCP.y + d)));
}
og2d.dispose();
gl.glEnable(GL.GL_BLEND);
caretOverlay.drawAll();
gl.glDisable(GL.GL_BLEND);
}
/**
* Draw the extras overlay to the gl canvas.
* Re-blits the overlay every frame. Only re-renders the overlay
* when needed.
*/
private void drawExtras(GL2 gl, GLU glu){
//Only re-render if needed
// redrawExtras: Some external change (new simulation data) means
// the data is out of date.
// extrasOverlay.contentsLost(): For some reason the buffer with this
// data is lost.
if ( redrawExtras || extrasOverlay.contentsLost() ){
log.debug("Redrawing Overlay");
final Graphics2D og2d = extrasOverlay.createGraphics();
setRenderingHints(og2d);
og2d.setBackground(new Color(0, 0, 0, 0));
og2d.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
extrasOverlay.markDirty(0, 0, canvas.getWidth(), canvas.getHeight());
for (FigureElement e : relativeExtra) {
e.paint(og2d, 1);
}
Rectangle rect = this.getVisibleRect();
for (FigureElement e : absoluteExtra) {
e.paint(og2d, 1.0, rect);
}
og2d.dispose();
redrawExtras = false;
}
//Re-blit to gl canvas every time
gl.glEnable(GL.GL_BLEND);
extrasOverlay.drawAll();
gl.glDisable(GL.GL_BLEND);
}
@Override
public void dispose(GLAutoDrawable drawable) {
}
@Override
public void init(GLAutoDrawable drawable) {
rr.init(drawable);
GL2 gl = drawable.getGL().getGL2();
gl.glClearDepth(1.0f); // clear z-buffer to the farthest
gl.glDepthFunc(GL.GL_LEQUAL); // the type of depth test to do
float amb = 0.5f;
float dif = 1.0f;
gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_AMBIENT,
new float[] { amb, amb, amb, 1 }, 0);
gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_DIFFUSE,
new float[] { dif, dif, dif, 1 }, 0);
gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_SPECULAR,
new float[] { dif, dif, dif, 1 }, 0);
gl.glEnable(GLLightingFunc.GL_LIGHT1);
gl.glEnable(GLLightingFunc.GL_LIGHTING);
gl.glShadeModel(GLLightingFunc.GL_SMOOTH);
gl.glEnable(GLLightingFunc.GL_NORMALIZE);
extrasOverlay = new Overlay(drawable);
caretOverlay = new Overlay(drawable);
}
@Override
public void reshape(GLAutoDrawable drawable, int x, int y, int w, int h) {
GL2 gl = drawable.getGL().getGL2();
GLU glu = new GLU();
double ratio = (double) w / (double) h;
fovX = fovY * ratio;
gl.glMatrixMode(GLMatrixFunc.GL_PROJECTION);
gl.glLoadIdentity();
glu.gluPerspective(fovY, ratio, 0.05f, 100f);
gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
redrawExtras = true;
}
@SuppressWarnings("unused")
private static class Bounds {
double xMin, xMax, xSize;
double yMin, yMax, ySize;
double zMin, zMax, zSize;
double rMax;
}
/**
* Calculates the bounds for the current configuration
*
* @return
*/
private Bounds calculateBounds() {
Bounds ret = new Bounds();
Collection<Coordinate> bounds = configuration.getBounds();
for (Coordinate c : bounds) {
ret.xMax = Math.max(ret.xMax, c.x);
ret.xMin = Math.min(ret.xMin, c.x);
ret.yMax = Math.max(ret.yMax, c.y);
ret.yMin = Math.min(ret.yMin, c.y);
ret.zMax = Math.max(ret.zMax, c.z);
ret.zMin = Math.min(ret.zMin, c.z);
double r = MathUtil.hypot(c.y, c.z);
ret.rMax = Math.max(ret.rMax, r);
}
ret.xSize = ret.xMax - ret.xMin;
ret.ySize = ret.yMax - ret.yMin;
ret.zSize = ret.zMax - ret.zMin;
return ret;
}
private void setupView(GL2 gl, GLU glu) {
gl.glLoadIdentity();
gl.glLightfv(GLLightingFunc.GL_LIGHT1, GLLightingFunc.GL_POSITION,
lightPosition, 0);
// Get the bounds
Bounds b = calculateBounds();
// Calculate the distance needed to fit the bounds in both the X and Y
// direction
// Add 10% for space around it.
double dX = (b.xSize * 1.2 / 2.0)
/ Math.tan(Math.toRadians(fovX / 2.0));
double dY = (b.rMax * 2.0 * 1.2 / 2.0)
/ Math.tan(Math.toRadians(fovY / 2.0));
// Move back the greater of the 2 distances
glu.gluLookAt(0, 0, Math.max(dX, dY), 0, 0, 0, 0, 1, 0);
gl.glRotated(yaw * (180.0 / Math.PI), 0, 1, 0);
gl.glRotated(roll * (180.0 / Math.PI), 1, 0, 0);
// Center the rocket in the view.
gl.glTranslated(-b.xMin - b.xSize / 2.0, 0, 0);
//Change to LEFT Handed coordinates
gl.glScaled(1, 1, -1);
gl.glFrontFace(GL.GL_CW);
//Flip textures for LEFT handed coords
gl.glMatrixMode(GL.GL_TEXTURE);
gl.glLoadIdentity();
gl.glScaled(-1,1,1);
gl.glTranslated(-1,0,0);
gl.glMatrixMode(GLMatrixFunc.GL_MODELVIEW);
}
/**
* Call when the rocket has changed
*/
public void updateFigure() {
log.debug("3D Figure Updated");
rr.updateFigure();
internalRepaint();
}
private void internalRepaint(){
super.repaint();
if (canvas != null)
canvas.display();
}
@Override
public void repaint() {
redrawExtras = true;
internalRepaint();
}
private Set<RocketComponent> selection = new HashSet<RocketComponent>();
public void setSelection(RocketComponent[] selection) {
this.selection.clear();
if (selection != null) {
for (RocketComponent c : selection)
this.selection.add(c);
}
internalRepaint();
}
private void setRoll(double rot) {
if (MathUtil.equals(roll, rot))
return;
this.roll = MathUtil.reduce360(rot);
internalRepaint();
}
private void setYaw(double rot) {
if (MathUtil.equals(yaw, rot))
return;
this.yaw = MathUtil.reduce360(rot);
internalRepaint();
}
// ///////////// Extra methods
private Coordinate project(Coordinate c, GL2 gl, GLU glu) {
double[] mvmatrix = new double[16];
double[] projmatrix = new double[16];
int[] viewport = new int[4];
gl.glGetIntegerv(GL.GL_VIEWPORT, viewport, 0);
gl.glGetDoublev(GLMatrixFunc.GL_MODELVIEW_MATRIX, mvmatrix, 0);
gl.glGetDoublev(GLMatrixFunc.GL_PROJECTION_MATRIX, projmatrix, 0);
double out[] = new double[4];
glu.gluProject(c.x, c.y, c.z, mvmatrix, 0, projmatrix, 0, viewport, 0,
out, 0);
return new Coordinate(out[0], out[1], out[2]);
}
private Coordinate cp = new Coordinate(0, 0, 0);
private Coordinate cg = new Coordinate(0, 0, 0);
public void setCG(Coordinate cg) {
this.cg = cg;
redrawExtras = true;
}
public void setCP(Coordinate cp) {
this.cp = cp;
redrawExtras = true;
}
public void addRelativeExtra(FigureElement p) {
relativeExtra.add(p);
redrawExtras = true;
}
public void removeRelativeExtra(FigureElement p) {
relativeExtra.remove(p);
redrawExtras = true;
}
public void clearRelativeExtra() {
relativeExtra.clear();
redrawExtras = true;
}
public void addAbsoluteExtra(FigureElement p) {
absoluteExtra.add(p);
redrawExtras = true;
}
public void removeAbsoluteExtra(FigureElement p) {
absoluteExtra.remove(p);
redrawExtras = true;
}
public void clearAbsoluteExtra() {
absoluteExtra.clear();
redrawExtras = true;
}
private ComponentSelectionListener csl;
public static interface ComponentSelectionListener {
public void componentClicked(RocketComponent[] components, MouseEvent e);
}
public void addComponentSelectionListener(
ComponentSelectionListener newListener) {
this.csl = newListener;
}
}

View File

@ -0,0 +1,303 @@
package net.sf.openrocket.gui.figure3d;
import java.awt.Point;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GL2ES1;
import javax.media.opengl.GL2GL3;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.fixedfunc.GLLightingFunc;
import net.sf.openrocket.motor.Motor;
import net.sf.openrocket.rocketcomponent.BodyTube;
import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.ExternalComponent;
import net.sf.openrocket.rocketcomponent.MotorMount;
import net.sf.openrocket.rocketcomponent.NoseCone;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.SymmetricComponent;
import net.sf.openrocket.rocketcomponent.Transition;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.util.Color;
import net.sf.openrocket.util.Coordinate;
/*
* @author Bill Kuker <bkuker@billkuker.com>
*/
public class RocketRenderer {
ComponentRenderer cr;
private final float[] selectedEmissive = { 1, 0, 0, 1 };
private final float[] colorBlack = { 0, 0, 0, 1 };
private final float[] color = new float[4];
public void init(GLAutoDrawable drawable) {
cr = new ComponentRenderer();
cr.init(drawable);
}
public void updateFigure() {
cr.updateFigure();
}
private boolean isDrawn(RocketComponent c) {
return true;
}
private boolean isDrawnTransparent(RocketComponent c) {
if (c instanceof BodyTube)
return true;
if (c instanceof NoseCone)
return false;
if (c instanceof SymmetricComponent) {
if (((SymmetricComponent) c).isFilled())
return false;
}
if (c instanceof Transition) {
Transition t = (Transition) c;
return !t.isAftShoulderCapped() && !t.isForeShoulderCapped();
}
return false;
}
public RocketComponent pick(GLAutoDrawable drawable,
Configuration configuration, Point p, Set<RocketComponent> ignore) {
final GL2 gl = drawable.getGL().getGL2();
gl.glEnable(GL.GL_DEPTH_TEST);
//Store a vector of pickable parts.
final Vector<RocketComponent> pickParts = new Vector<RocketComponent>();
for (RocketComponent c : configuration) {
if ( ignore != null && ignore.contains(c) )
continue;
//Encode the index of the part as a color
//if index is 0x0ABC the color ends up as
//0xA0B0C000 with each nibble in the coresponding
//high bits of the RG and B channels.
gl.glColor4ub((byte) ((pickParts.size() >> 4) & 0xF0),
(byte) ((pickParts.size() << 0) & 0xF0),
(byte) ((pickParts.size() << 4) & 0xF0), (byte) 1);
pickParts.add(c);
if (isDrawnTransparent(c)) {
gl.glEnable(GL.GL_CULL_FACE);
gl.glCullFace(GL.GL_FRONT);
cr.renderGeometry(gl, c);
gl.glDisable(GL.GL_CULL_FACE);
} else {
cr.renderGeometry(gl, c);
}
}
ByteBuffer bb = ByteBuffer.allocateDirect(4);
gl.glReadPixels(p.x, p.y, 1, 1, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, bb);
final int pickColor = bb.getInt();
final int pickIndex = ((pickColor >> 20) & 0xF00) | ((pickColor >> 16) & 0x0F0)
| ((pickColor >> 12) & 0x00F);
if ( pickIndex < 0 || pickIndex > pickParts.size() - 1 )
return null;
return pickParts.get(pickIndex);
}
public void render(GLAutoDrawable drawable, Configuration configuration,
Set<RocketComponent> selection) {
if (cr == null)
throw new IllegalStateException(this + " Not Initialized");
GL2 gl = drawable.getGL().getGL2();
gl.glEnable(GL.GL_DEPTH_TEST); // enables depth testing
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
// Draw all inner components
for (RocketComponent c : configuration) {
if (isDrawn(c)) {
if (!isDrawnTransparent(c)) {
renderComponent(gl, c, 1.0f);
}
}
}
renderMotors(gl, configuration);
// Draw Tube and Transition back faces, blended with depth test
// so that they show up behind.
gl.glEnable(GL.GL_CULL_FACE);
gl.glCullFace(GL.GL_FRONT);
for (RocketComponent c : configuration) {
if (isDrawn(c)) {
if (isDrawnTransparent(c)) {
renderComponent(gl, c, 1.0f);
}
}
}
gl.glDisable(GL.GL_CULL_FACE);
// Draw T&T front faces blended, without depth test
gl.glEnable(GL.GL_BLEND);
gl.glEnable(GL.GL_CULL_FACE);
gl.glCullFace(GL.GL_BACK);
for (RocketComponent c : configuration) {
if (isDrawn(c)) {
if (isDrawnTransparent(c)) {
renderComponent(gl, c, 0.2f);
}
}
}
gl.glDisable(GL.GL_BLEND);
gl.glDisable(GL.GL_CULL_FACE);
gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_EMISSION,
selectedEmissive, 0);
gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_DIFFUSE, colorBlack, 0);
gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_AMBIENT, colorBlack, 0);
gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_SPECULAR, colorBlack, 0);
gl.glDepthMask(false);
gl.glDisable(GL.GL_DEPTH_TEST);
gl.glEnable(GL.GL_STENCIL_TEST);
for (RocketComponent c : configuration) {
if (selection.contains(c)) {
// So it is faster to do this once before the loop,
// but then the outlines are not as good if you multi-select.
// Not sure which to do.
gl.glStencilMask(1);
gl.glDisable(GL.GL_SCISSOR_TEST);
gl.glClearStencil(0);
gl.glClear(GL.GL_STENCIL_BUFFER_BIT);
gl.glStencilMask(0);
gl.glStencilFunc(GL.GL_ALWAYS, 1, 1);
gl.glStencilMask(1);
gl.glColorMask(false, false, false, false);
gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_FILL);
gl.glStencilOp(GL.GL_KEEP, GL.GL_KEEP, GL.GL_REPLACE);
cr.renderGeometry(gl, c);
gl.glStencilMask(0);
gl.glColorMask(true, true, true, true);
gl.glStencilFunc(GL.GL_NOTEQUAL, 1, 1);
gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_LINE);
gl.glLineWidth(5.0f);
cr.renderGeometry(gl, c);
}
}
gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2GL3.GL_FILL);
gl.glDepthMask(true);
gl.glMaterialfv(GL.GL_FRONT_AND_BACK, GLLightingFunc.GL_EMISSION,
colorBlack, 0);
gl.glDisable(GL.GL_STENCIL_TEST);
gl.glEnable(GL.GL_DEPTH_TEST);
}
private void renderMotors(GL2 gl, Configuration configuration) {
String motorID = configuration.getMotorConfigurationID();
Iterator<MotorMount> iterator = configuration.motorIterator();
while (iterator.hasNext()) {
MotorMount mount = iterator.next();
Motor motor = mount.getMotor(motorID);
double length = motor.getLength();
double radius = motor.getDiameter() / 2;
Coordinate[] position = ((RocketComponent) mount)
.toAbsolute(new Coordinate(((RocketComponent) mount)
.getLength() + mount.getMotorOverhang() - length));
for (int i = 0; i < position.length; i++) {
cr.renderMotor(gl, position[i], length, radius);
}
}
}
public void renderComponent(GL2 gl, RocketComponent c, float alpha) {
gl.glLightModeli(GL2ES1.GL_LIGHT_MODEL_TWO_SIDE, 1);
getOutsideColor(c, alpha, color);
gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_DIFFUSE, color, 0);
gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_AMBIENT, color, 0);
getSpecularColor(c, alpha, color);
gl.glMaterialfv(GL.GL_FRONT, GLLightingFunc.GL_SPECULAR, color, 0);
gl.glMateriali(GL.GL_FRONT, GLLightingFunc.GL_SHININESS,
getShininess(c));
getInsideColor(c, alpha, color);
gl.glMaterialfv(GL.GL_BACK, GLLightingFunc.GL_DIFFUSE, color, 0);
gl.glMaterialfv(GL.GL_BACK, GLLightingFunc.GL_AMBIENT, color, 0);
cr.renderGeometry(gl, c);
}
private int getShininess(RocketComponent c) {
if (c instanceof ExternalComponent) {
switch (((ExternalComponent) c).getFinish()) {
case ROUGH:
return 10;
case UNFINISHED:
return 30;
case NORMAL:
return 40;
case SMOOTH:
return 80;
case POLISHED:
return 128;
}
return 100;
} else {
return 20;
}
}
private void getSpecularColor(RocketComponent c, float alpha, float[] out) {
int shine = getShininess(c);
float m = (float) shine / 128.0f;
float d = 0.9f;
getOutsideColor(c, alpha, out);
out[0] = Math.max(out[0], d) * m;
out[1] = Math.max(out[1], d) * m;
out[2] = Math.max(out[2], d) * m;
}
private void getInsideColor(RocketComponent c, float alpha, float[] out) {
float d = 0.4f;
getOutsideColor(c, alpha, out);
out[0] *= d;
out[1] *= d;
out[2] *= d;
}
private HashMap<Class<?>, Color> defaultColorCache = new HashMap<Class<?>, Color>();
private void getOutsideColor(RocketComponent c, float alpha, float[] out) {
Color col;
col = c.getColor();
if (col == null){
if ( defaultColorCache.containsKey(c.getClass()) ){
col = defaultColorCache.get(c.getClass());
} else {
col = Application.getPreferences().getDefaultColor(c.getClass());
defaultColorCache.put(c.getClass(), col);
}
}
out[0] = Math.max(0.2f, (float) col.getRed() / 255f);
out[1] = Math.max(0.2f, (float) col.getGreen() / 255f);
out[2] = Math.max(0.2f, (float) col.getBlue() / 255f);
out[3] = alpha;
}
}

View File

@ -0,0 +1,247 @@
/*
** License Applicability. Except to the extent portions of this file are
** made subject to an alternative license as permitted in the SGI Free
** Software License B, Version 2.0 (the "License"), the contents of this
** file are subject only to the provisions of the License. You may not use
** this file except in compliance with the License. You may obtain a copy
** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
**
** http://oss.sgi.com/projects/FreeB
**
** Note that, as provided in the License, the Software is distributed on an
** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
**
** NOTE: The Original Code (as defined below) has been licensed to Sun
** Microsystems, Inc. ("Sun") under the SGI Free Software License B
** (Version 1.1), shown above ("SGI License"). Pursuant to Section
** 3.2(3) of the SGI License, Sun is distributing the Covered Code to
** you under an alternative license ("Alternative License"). This
** Alternative License includes all of the provisions of the SGI License
** except that Section 2.2 and 11 are omitted. Any differences between
** the Alternative License and the SGI License are offered solely by Sun
** and not by SGI.
**
** Original Code. The Original Code is: OpenGL Sample Implementation,
** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
** Copyright in any portions created by third parties is as indicated
** elsewhere herein. All Rights Reserved.
**
** Additional Notice Provisions: The application programming interfaces
** established by SGI in conjunction with the Original Code are The
** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
** Window System(R) (Version 1.3), released October 19, 1998. This software
** was created using the OpenGL(R) version 1.2.1 Sample Implementation
** published by SGI, but has not been independently verified as being
** compliant with the OpenGL(R) version 1.2.1 Specification.
**
** $Date: 2009-03-04 17:23:34 -0800 (Wed, 04 Mar 2009) $ $Revision: 1856 $
** $Header$
*/
/*
* Copyright (c) 2002-2004 LWJGL Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'LWJGL' nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistribution of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
* MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
* ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
* DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
* DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
* ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
* SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed or intended for use
* in the design, construction, operation or maintenance of any nuclear
* facility.
*/
package net.sf.openrocket.gui.figure3d;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import net.sf.openrocket.rocketcomponent.Transition;
public final class TransitionRenderer {
private static final boolean textureFlag = true;
private TransitionRenderer() {
}
public static final void drawTransition(final GL2 gl, final Transition tr,
final int slices, final int stacks) {
double da, r, dz;
double x, y, z, nz, nsign;
int i, j;
nsign = 1.0f;
da = 2.0f * PI / slices;
dz = (double) tr.getLength() / stacks;
double ds = 1.0f / slices;
double dt = 1.0f / stacks;
double t = 0.0f;
z = 0.0f;
r = (double) tr.getForeRadius();
for (j = 0; j < stacks; j++) {
r = (double) tr.getRadius(z);
double rNext = (double) tr.getRadius(z + dz);
if (j == stacks - 1)
rNext = (double) tr.getRadius(tr.getLength());
// Z component of normal vectors
nz = -(rNext - r) / dz;
double s = 0.0f;
glBegin(gl, GL2.GL_QUAD_STRIP);
for (i = 0; i <= slices; i++) {
if (i == slices) {
x = sin(0.0f);
y = cos(0.0f);
} else {
x = sin((i * da));
y = cos((i * da));
}
if (nsign == 1.0f) {
normal3d(gl, (x * nsign), (y * nsign), (nz * nsign));
TXTR_COORD(gl, s, t);
glVertex3d(gl, (x * r), (y * r), z);
normal3d(gl, (x * nsign), (y * nsign), (nz * nsign));
TXTR_COORD(gl, s, t + dt);
glVertex3d(gl, (x * rNext), (y * rNext), (z + dz));
} else {
normal3d(gl, x * nsign, y * nsign, nz * nsign);
TXTR_COORD(gl, s, t);
glVertex3d(gl, (x * r), (y * r), z);
normal3d(gl, x * nsign, y * nsign, nz * nsign);
TXTR_COORD(gl, s, t + dt);
glVertex3d(gl, (x * rNext), (y * rNext), (z + dz));
}
s += ds;
} // for slices
glEnd(gl);
// r += dr;
t += dt;
z += dz;
} // for stacks
}
// ----------------------------------------------------------------------
// Internals only below this point
//
private static final double PI = (double) Math.PI;
private static final void glBegin(GL gl, int mode) {
gl.getGL2().glBegin(mode);
}
private static final void glEnd(GL gl) {
gl.getGL2().glEnd();
}
private static final void glVertex3d(GL gl, double x, double y, double z) {
gl.getGL2().glVertex3d(x, y, z);
}
private static final void glNormal3d(GL gl, double x, double y, double z) {
gl.getGL2().glNormal3d(x, y, z);
}
private static final void glTexCoord2d(GL gl, double x, double y) {
gl.getGL2().glTexCoord2d(x, y);
}
/**
* Call glNormal3f after scaling normal to unit length.
*
* @param x
* @param y
* @param z
*/
private static final void normal3d(GL gl, double x, double y, double z) {
double mag;
mag = (double) Math.sqrt(x * x + y * y + z * z);
if (mag > 0.00001F) {
x /= mag;
y /= mag;
z /= mag;
}
glNormal3d(gl, x, y, z);
}
private static final void TXTR_COORD(GL gl, double x, double y) {
if (textureFlag)
glTexCoord2d(gl, x, y);
}
private static final double sin(double r) {
return (double) Math.sin(r);
}
private static final double cos(double r) {
return (double) Math.cos(r);
}
}

View File

@ -1,6 +1,7 @@
package net.sf.openrocket.gui.scalefigure;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Point;
@ -18,6 +19,7 @@ import java.util.concurrent.ThreadFactory;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ButtonGroup;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
@ -43,6 +45,7 @@ import net.sf.openrocket.gui.components.BasicSlider;
import net.sf.openrocket.gui.components.StageSelector;
import net.sf.openrocket.gui.components.UnitSelector;
import net.sf.openrocket.gui.configdialog.ComponentConfigDialog;
import net.sf.openrocket.gui.figure3d.RocketFigure3d;
import net.sf.openrocket.gui.figureelements.CGCaret;
import net.sf.openrocket.gui.figureelements.CPCaret;
import net.sf.openrocket.gui.figureelements.Caret;
@ -74,17 +77,29 @@ import net.sf.openrocket.util.StateChangeListener;
* A JPanel that contains a RocketFigure and buttons to manipulate the figure.
*
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
* @author Bill Kuker <bkuker@billkuker.com>
*/
public class RocketPanel extends JPanel implements TreeSelectionListener, ChangeSource {
private static final long serialVersionUID = 1L;
private static final Translator trans = Application.getTranslator();
private boolean is3d;
private final RocketFigure figure;
private final RocketFigure3d figure3d;
private final ScaleScrollPane scrollPane;
private final JPanel figureHolder;
private JLabel infoMessage;
private TreeSelectionModel selectionModel = null;
private BasicSlider rotationSlider;
ScaleSelector scaleSelector;
/* Calculation of CP and CG */
private AerodynamicCalculator aerodynamicCalculator;
@ -147,8 +162,13 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
// Create figure and custom scroll pane
figure = new RocketFigure(configuration);
figure3d = new RocketFigure3d(configuration);
figureHolder = new JPanel(new BorderLayout());
scrollPane = new ScaleScrollPane(figure) {
private static final long serialVersionUID = 1L;
@Override
public void mouseClicked(MouseEvent event) {
handleMouseClick(event);
@ -159,16 +179,60 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
createPanel();
is3d = true;
go2D();
configuration.addChangeListener(new StateChangeListener() {
@Override
public void stateChanged(EventObject e) {
// System.out.println("Configuration changed, calling updateFigure");
updateExtras();
figure.updateFigure();
updateFigures();
}
});
figure3d.addComponentSelectionListener(new RocketFigure3d.ComponentSelectionListener() {
@Override
public void componentClicked(RocketComponent clicked[], MouseEvent event) {
handleComponentClick(clicked, event);
}
});
}
private void updateFigures() {
if (!is3d)
figure.updateFigure();
else
figure3d.updateFigure();
}
private void go3D() {
if (is3d)
return;
is3d = true;
figureHolder.remove(scrollPane);
figureHolder.add(figure3d, BorderLayout.CENTER);
rotationSlider.setEnabled(false);
scaleSelector.setEnabled(false);
revalidate();
figureHolder.revalidate();
figure3d.repaint();
}
private void go2D() {
if (!is3d)
return;
is3d = false;
figureHolder.remove(figure3d);
figureHolder.add(scrollPane, BorderLayout.CENTER);
rotationSlider.setEnabled(true);
scaleSelector.setEnabled(true);
revalidate();
figureHolder.revalidate();
figure.repaint();
}
/**
* Creates the layout and components of the panel.
@ -181,6 +245,8 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
//// Create toolbar
ButtonGroup bg = new ButtonGroup();
// Side/back buttons
FigureTypeAction action = new FigureTypeAction(RocketFigure.TYPE_SIDE);
//// Side view
@ -188,6 +254,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
//// Side view
action.putValue(Action.SHORT_DESCRIPTION, trans.get("RocketPanel.FigTypeAct.ttip.Sideview"));
JToggleButton toggle = new JToggleButton(action);
bg.add(toggle);
add(toggle, "spanx, split");
action = new FigureTypeAction(RocketFigure.TYPE_BACK);
@ -196,11 +263,31 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
//// Back view
action.putValue(Action.SHORT_DESCRIPTION, trans.get("RocketPanel.FigTypeAct.ttip.Backview"));
toggle = new JToggleButton(action);
bg.add(toggle);
add(toggle, "gap rel");
//// 3d Toggle
final JToggleButton toggle3d = new JToggleButton(new AbstractAction("3D") {
private static final long serialVersionUID = 1L;
{
putValue(Action.NAME, "3D");//TODO
putValue(Action.SHORT_DESCRIPTION, "3D"); //TODO
}
@Override
public void actionPerformed(ActionEvent e) {
if ( ((JToggleButton)e.getSource()).isSelected() ){
go3D();
} else {
go2D();
}
}
});
bg.add(toggle3d);
add(toggle3d, "gap rel");
// Zoom level selector
ScaleSelector scaleSelector = new ScaleSelector(scrollPane);
scaleSelector = new ScaleSelector(scrollPane);
add(scaleSelector);
@ -231,7 +318,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
add(us, "alignx 50%, growx");
// Add the rocket figure
add(scrollPane, "grow, spany 2, wmin 300lp, hmin 100lp, wrap");
add(figureHolder, "grow, spany 2, wmin 300lp, hmin 100lp, wrap");
// Add rotation slider
@ -239,7 +326,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
JLabel l = new JLabel("360" + Chars.DEGREE);
Dimension d = l.getPreferredSize();
add(new BasicSlider(theta.getSliderModel(0, 2 * Math.PI), JSlider.VERTICAL, true),
add(rotationSlider = new BasicSlider(theta.getSliderModel(0, 2 * Math.PI), JSlider.VERTICAL, true),
"ax 50%, wrap, width " + (d.width + 6) + "px:null:null, growy");
@ -324,7 +411,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
return;
cpAOA = aoa;
updateExtras();
figure.updateFigure();
updateFigures();
fireChangeEvent();
}
@ -340,7 +427,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
if (!Double.isNaN(theta))
figure.setRotation(theta);
updateExtras();
figure.updateFigure();
updateFigures();
fireChangeEvent();
}
@ -354,7 +441,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
return;
cpMach = mach;
updateExtras();
figure.updateFigure();
updateFigures();
fireChangeEvent();
}
@ -368,7 +455,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
return;
cpRoll = roll;
updateExtras();
figure.updateFigure();
updateFigures();
fireChangeEvent();
}
@ -417,6 +504,11 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
RocketComponent[] clicked = figure.getComponentsByPoint(x, y);
handleComponentClick(clicked, event);
}
private void handleComponentClick(RocketComponent[] clicked, MouseEvent event){
// If no component is clicked, do nothing
if (clicked.length == 0)
return;
@ -517,6 +609,9 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
else
cgx = Double.NaN;
figure3d.setCG(cg);
figure3d.setCP(cp);
// Length bound is assumed to be tight
double length = 0, diameter = 0;
Collection<Coordinate> bounds = configuration.getBounds();
@ -648,6 +743,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
extraText.setFlightData(simulation.getSimulatedData());
extraText.setCalculatingData(false);
figure.repaint();
figure3d.repaint();
}
@Override
@ -667,6 +763,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
extraText.setFlightData(FlightData.NaN_DATA);
extraText.setCalculatingData(false);
figure.repaint();
figure3d.repaint();
}
}
@ -676,14 +773,22 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
* Adds the extra data to the figure. Currently this includes the CP and CG carets.
*/
private void addExtras() {
figure.clearRelativeExtra();
extraCG = new CGCaret(0, 0);
extraCP = new CPCaret(0, 0);
extraText = new RocketInfo(configuration);
updateExtras();
figure.clearRelativeExtra();
figure.addRelativeExtra(extraCP);
figure.addRelativeExtra(extraCG);
figure.addAbsoluteExtra(extraText);
figure3d.clearRelativeExtra();
//figure3d.addRelativeExtra(extraCP);
//figure3d.addRelativeExtra(extraCG);
figure3d.addAbsoluteExtra(extraText);
}
@ -703,6 +808,8 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
for (int i = 0; i < paths.length; i++)
components[i] = (RocketComponent) paths[i].getLastPathComponent();
figure.setSelection(components);
figure3d.setSelection(components);
}
@ -714,6 +821,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
* @author Sampo Niskanen <sampo.niskanen@iki.fi>
*/
private class FigureTypeAction extends AbstractAction implements StateChangeListener {
private static final long serialVersionUID = 1L;
private final int type;
public FigureTypeAction(int type) {
@ -728,6 +836,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
if (state == true) {
// This view has been selected
figure.setType(type);
go2D();
updateExtras();
}
stateChanged(null);
@ -735,7 +844,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
@Override
public void stateChanged(EventObject e) {
putValue(Action.SELECTED_KEY, figure.getType() == type);
putValue(Action.SELECTED_KEY, figure.getType() == type && !is3d);
}
}

View File

@ -1,5 +1,6 @@
package net.sf.openrocket.gui.scalefigure;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
@ -156,4 +157,12 @@ public class ScaleSelector extends JPanel {
return scale * 1.5;
}
@Override
public void setEnabled(boolean b){
for ( Component c : getComponents() ){
c.setEnabled(b);
}
super.setEnabled(b);
}
}

View File

@ -18,8 +18,15 @@ public class JarUtil {
*/
public static File getCurrentJarFile() {
// Find the jar file this class is contained in
URL jarUrl = null;
CodeSource codeSource = Database.class.getProtectionDomain().getCodeSource();
CodeSource codeSource;
try {
codeSource = new URL("rsrc:.").openConnection().getClass().getProtectionDomain().getCodeSource();
} catch (Throwable e) {
codeSource = Database.class.getProtectionDomain().getCodeSource();
}
if (codeSource != null)
jarUrl = codeSource.getLocation();