Merge pull request #703 from teyrana/385/3d-select

[fixes #385] Restores ability to select components in the 3d views
This commit is contained in:
Daniel Williams 2020-07-21 18:35:42 -04:00 committed by GitHub
commit 0f2edc0024
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 71 additions and 87 deletions

View File

@ -673,9 +673,6 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona
// (Iyy / M + Izz / M) / 2 = (h^2 + 2 * w^2)/24 // (Iyy / M + Izz / M) / 2 = (h^2 + 2 * w^2)/24
final double inertia = (h2 + 2 * w2) / 24; final double inertia = (h2 + 2 * w2) / 24;
System.out.println("component " + this);
System.out.println("finCount " + finCount);
System.out.println("inertia " + inertia);
if (finCount == 1) if (finCount == 1)
return inertia; return inertia;

View File

@ -51,6 +51,7 @@ import net.sf.openrocket.startup.Application;
import net.sf.openrocket.startup.Preferences; import net.sf.openrocket.startup.Preferences;
import net.sf.openrocket.util.Coordinate; import net.sf.openrocket.util.Coordinate;
import net.sf.openrocket.util.MathUtil; import net.sf.openrocket.util.MathUtil;
import net.sf.openrocket.util.BoundingBox;
/* /*
* @author Bill Kuker <bkuker@billkuker.com> * @author Bill Kuker <bkuker@billkuker.com>
@ -286,8 +287,6 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
GL2 gl = drawable.getGL().getGL2(); GL2 gl = drawable.getGL().getGL2();
GLU glu = new GLU(); GLU glu = new GLU();
gl.glEnable(GL.GL_MULTISAMPLE);
gl.glClearColor(1, 1, 1, 1); gl.glClearColor(1, 1, 1, 1);
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
@ -295,6 +294,7 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
final FlightConfiguration configuration = rkt.getSelectedConfiguration(); final FlightConfiguration configuration = rkt.getSelectedConfiguration();
if (pickPoint != null) { if (pickPoint != null) {
gl.glDisable(GL.GL_MULTISAMPLE);
gl.glDisable(GLLightingFunc.GL_LIGHTING); gl.glDisable(GLLightingFunc.GL_LIGHTING);
final RocketComponent picked = rr.pick(drawable, configuration, final RocketComponent picked = rr.pick(drawable, configuration,
@ -305,7 +305,6 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
@Override @Override
public void run() { public void run() {
if (picked == null) { if (picked == null) {
log.debug("unselecting");
csl.componentClicked(new RocketComponent[] {}, e); csl.componentClicked(new RocketComponent[] {}, e);
} else { } else {
csl.componentClicked(new RocketComponent[] { picked }, e); csl.componentClicked(new RocketComponent[] { picked }, e);
@ -319,6 +318,7 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
gl.glClearColor(1, 1, 1, 1); gl.glClearColor(1, 1, 1, 1);
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
gl.glEnable(GL.GL_MULTISAMPLE);
gl.glEnable(GLLightingFunc.GL_LIGHTING); gl.glEnable(GLLightingFunc.GL_LIGHTING);
} }
rr.render(drawable, configuration, selection); rr.render(drawable, configuration, selection);
@ -326,7 +326,7 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
drawExtras(gl, glu); drawExtras(gl, glu);
drawCarets(gl, glu); drawCarets(gl, glu);
//GLJPanel with GLSL Flipper relies on this: // GLJPanel with GLSL Flipper relies on this:
gl.glFrontFace(GL.GL_CCW); gl.glFrontFace(GL.GL_CCW);
} }
@ -468,47 +468,19 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
redrawExtras = true; redrawExtras = true;
} }
@SuppressWarnings("unused") private BoundingBox cachedBounds = null;
private static class Bounds {
double xMin, xMax, xSize;
double yMin, yMax, ySize;
double zMin, zMax, zSize;
double rMax;
}
private Bounds cachedBounds = null;
/** /**
* Calculates the bounds for the current configuration * Calculates the bounds for the current configuration
* *
* @return * @return
*/ */
private Bounds calculateBounds() { private BoundingBox calculateBounds() {
if (cachedBounds != null) { if (cachedBounds == null) {
return cachedBounds;
} else {
final Bounds b = new Bounds();
final FlightConfiguration configuration = rkt.getSelectedConfiguration(); final FlightConfiguration configuration = rkt.getSelectedConfiguration();
final Collection<Coordinate> bounds = configuration.getBounds(); cachedBounds = configuration.getBoundingBox();
for (Coordinate c : bounds) {
b.xMax = Math.max(b.xMax, c.x);
b.xMin = Math.min(b.xMin, c.x);
b.yMax = Math.max(b.yMax, c.y);
b.yMin = Math.min(b.yMin, c.y);
b.zMax = Math.max(b.zMax, c.z);
b.zMin = Math.min(b.zMin, c.z);
double r = MathUtil.hypot(c.y, c.z);
b.rMax = Math.max(b.rMax, r);
}
b.xSize = b.xMax - b.xMin;
b.ySize = b.yMax - b.yMin;
b.zSize = b.zMax - b.zMin;
cachedBounds = b;
return b;
} }
return cachedBounds;
} }
private void setupView(final GL2 gl, final GLU glu) { private void setupView(final GL2 gl, final GLU glu) {
@ -518,14 +490,14 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
lightPosition, 0); lightPosition, 0);
// Get the bounds // Get the bounds
final Bounds b = calculateBounds(); final BoundingBox b = calculateBounds();
// Calculate the distance needed to fit the bounds in both the X and Y // Calculate the distance needed to fit the bounds in both the X and Y
// direction // direction
// Add 10% for space around it. // Add 10% for space around it.
final double dX = (b.xSize * 1.2 / 2.0) final double dX = (b.span().x * 1.2 / 2.0)
/ Math.tan(Math.toRadians(fovX / 2.0)); / Math.tan(Math.toRadians(fovX / 2.0));
final double dY = (b.rMax * 2.0 * 1.2 / 2.0) final double dY = (b.span().y * 2.0 * 1.2 / 2.0)
/ Math.tan(Math.toRadians(fovY / 2.0)); / Math.tan(Math.toRadians(fovY / 2.0));
// Move back the greater of the 2 distances // Move back the greater of the 2 distances
@ -535,7 +507,7 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
gl.glRotated(roll * (180.0 / Math.PI), 1, 0, 0); gl.glRotated(roll * (180.0 / Math.PI), 1, 0, 0);
// Center the rocket in the view. // Center the rocket in the view.
gl.glTranslated(-b.xMin - b.xSize / 2.0, 0, 0); gl.glTranslated(-b.min.x - b.span().x / 2.0, 0, 0);
//Change to LEFT Handed coordinates //Change to LEFT Handed coordinates
gl.glScaled(1, 1, -1); gl.glScaled(1, 1, -1);
@ -593,14 +565,14 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
private void setRoll(final double rot) { private void setRoll(final double rot) {
if (MathUtil.equals(roll, rot)) if (MathUtil.equals(roll, rot))
return; return;
this.roll = MathUtil.reduce360(rot); this.roll = MathUtil.reduce2PI(rot);
internalRepaint(); internalRepaint();
} }
private void setYaw(final double rot) { private void setYaw(final double rot) {
if (MathUtil.equals(yaw, rot)) if (MathUtil.equals(yaw, rot))
return; return;
this.yaw = MathUtil.reduce360(rot); this.yaw = MathUtil.reduce2PI(rot);
internalRepaint(); internalRepaint();
} }

View File

@ -4,9 +4,9 @@ import java.awt.Point;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.Vector;
import com.jogamp.opengl.GL; import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2; import com.jogamp.opengl.GL2;
@ -60,49 +60,61 @@ public abstract class RocketRenderer {
public abstract void flushTextureCache(GLAutoDrawable drawable); public abstract void flushTextureCache(GLAutoDrawable drawable);
public RocketComponent pick(GLAutoDrawable drawable, FlightConfiguration configuration, Point p, /**
Set<RocketComponent> ignore) { * This function is a bit.... unusual. Instead of computing an inverse transform from the UI window into design-space,
* this renders each component with a unique identifiable color ... to a dummy, throwaway canvas:
*
* Then, we read the pixel (RGB) color value at a point on the canvas, and use that color to identify the component
*
* @param drawable canvas to draw to
* @param configuration active configuration
* @param p point to select at
* @param ignore list of ignore components
* @return optional (nullable) component selection result
*/
public RocketComponent pick(GLAutoDrawable drawable, FlightConfiguration configuration, Point p, Set<RocketComponent> ignore) {
final GL2 gl = drawable.getGL().getGL2(); final GL2 gl = drawable.getGL().getGL2();
gl.glEnable(GL.GL_DEPTH_TEST); gl.glEnable(GL.GL_DEPTH_TEST);
// Store a vector of pickable parts. // Store a vector of pickable parts.
final Vector<RocketComponent> pickParts = new Vector<RocketComponent>(); final Map<Integer, RocketComponent> selectionMap = new HashMap<>();
for (RocketComponent c : configuration.getActiveComponents()) { Collection<Geometry> geometryList = getTreeGeometry( configuration);
if (ignore != null && ignore.contains(c)) for(Geometry geom: geometryList ) {
final RocketComponent comp = geom.getComponent();
if (ignore != null && ignore.contains(comp))
continue; continue;
// Encode the index of the part as a color if( geom.active ) {
// if index is 0x0ABC the color ends up as final int hashCode = comp.hashCode();
// 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)) { selectionMap.put(hashCode, comp);
cr.getComponentGeometry(c).render(gl, Surface.INSIDE);
gl.glColor4ub((byte) ((hashCode >> 24) & 0xFF), // red channel (LSB)
(byte) ((hashCode >> 16) & 0xFF), // green channel
(byte) ((hashCode >> 8) & 0xFF), // blue channel
(byte) ((hashCode) & 0xFF)); // alpha channel (MSB)
if (isDrawnTransparent(comp)) {
geom.render(gl, Surface.INSIDE);
} else { } else {
cr.getComponentGeometry(c).render(gl, Surface.ALL); geom.render(gl, Surface.ALL);
}
} }
} }
ByteBuffer bb = ByteBuffer.allocateDirect(4);
if (p == null) if (p == null)
return null; //Allow pick to be called without a point for debugging return null; //Allow pick to be called without a point for debugging
gl.glReadPixels(p.x, p.y, 1, 1, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, bb); final ByteBuffer buffer = ByteBuffer.allocateDirect(4);
gl.glReadPixels(p.x, p.y, // coordinates of "first" pixel to read
1, 1, // width, height of rectangle to read
GL.GL_RGBA, GL.GL_UNSIGNED_BYTE,
buffer); // output buffer
final int pixelValue = buffer.getInt();
final RocketComponent selected = selectionMap.get(pixelValue);
final int pickColor = bb.getInt(); return selected;
final int pickIndex = ((pickColor >> 20) & 0xF00) | ((pickColor >> 16) & 0x0F0) | ((pickColor >> 12) & 0x00F);
log.trace("Picked pixel color is {} index is {}", pickColor, pickIndex);
if (pickIndex < 0 || pickIndex > pickParts.size() - 1)
return null;
return pickParts.get(pickIndex);
} }
public void render(GLAutoDrawable drawable, FlightConfiguration configuration, Set<RocketComponent> selection) { public void render(GLAutoDrawable drawable, FlightConfiguration configuration, Set<RocketComponent> selection) {

View File

@ -312,8 +312,11 @@ public class GUIUtil {
window.addComponentListener(new ComponentAdapter() { window.addComponentListener(new ComponentAdapter() {
@Override @Override
public void componentResized(ComponentEvent e) { public void componentResized(ComponentEvent e) {
final Dimension previousWindowSize = ((SwingPreferences)Application.getPreferences()).getWindowSize(window.getClass());
if( ! window.getSize().equals(previousWindowSize)) {
log.debug("Storing size of " + window.getClass().getName() + ": " + window.getSize()); log.debug("Storing size of " + window.getClass().getName() + ": " + window.getSize());
((SwingPreferences) Application.getPreferences()).setWindowSize(window.getClass(), window.getSize()); ((SwingPreferences) Application.getPreferences()).setWindowSize(window.getClass(), window.getSize());
}
if (window instanceof JFrame) { if (window instanceof JFrame) {
if ((((JFrame) window).getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH) { if ((((JFrame) window).getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH) {
log.debug("Storing maximized state of " + window.getClass().getName()); log.debug("Storing maximized state of " + window.getClass().getName());