Add a 3D enabled user preference

Add 3d Crash detection that disables 3d if the application crashes during one of several touchy phases of JOGL / Native / display driver initialization.
Always make the 3D view button clickable, but there is an explanation and a button to turn 3d on if it has been disabled
This commit is contained in:
Bill Kuker 2012-09-27 14:44:04 +00:00
parent 2aa4a8d905
commit 4073246388
8 changed files with 214 additions and 19 deletions

View File

@ -1,3 +1,9 @@
2012-09-27 Bill Kuker
* Added user preference to enable/disable 3D, and crash detection to disable 3D on a crash
* Moved JOGL GL initialization earlier in initialization.
2012-09-25 Doug Pedrick 2012-09-25 Doug Pedrick
* Added preference to open last edited design file upon startup. * Added preference to open last edited design file upon startup.

View File

@ -52,6 +52,8 @@ RocketPanel.FigViewAct.2D = 2D View
RocketPanel.FigViewAct.ttip.2D = 2D View RocketPanel.FigViewAct.ttip.2D = 2D View
RocketPanel.FigViewAct.3D = 3D View RocketPanel.FigViewAct.3D = 3D View
RocketPanel.FigViewAct.ttip.3D = 3D View RocketPanel.FigViewAct.ttip.3D = 3D View
RocketPanel.3DDisabledMessage = OpenRocket's 3D capabilities have been disabled by either the user or a crash.
RocketPanel.3DDisabledMessageButton = Re-enable 3D at next application launch.
RocketPanel.lbl.Motorcfg = Motor configuration: RocketPanel.lbl.Motorcfg = Motor configuration:
RocketPanel.lbl.infoMessage = <html>Click to select &nbsp;&nbsp; Shift+click to select other &nbsp;&nbsp; Double-click to edit &nbsp;&nbsp; Click+drag to move RocketPanel.lbl.infoMessage = <html>Click to select &nbsp;&nbsp; Shift+click to select other &nbsp;&nbsp; Double-click to edit &nbsp;&nbsp; Click+drag to move
@ -221,6 +223,7 @@ pref.dlg.but.add = Add
pref.dlg.but.reset = Reset pref.dlg.but.reset = Reset
pref.dlg.but.checknow = Check now pref.dlg.but.checknow = Check now
pref.dlg.but.openlast = Open last design file on startup pref.dlg.but.openlast = Open last design file on startup
pref.dlg.but.enable3d = Enable 3D
pref.dlg.but.defaultmetric = Default metric pref.dlg.but.defaultmetric = Default metric
pref.dlg.but.defaultimperial = Default imperial pref.dlg.but.defaultimperial = Default imperial
pref.dlg.title.Preferences = Preferences pref.dlg.title.Preferences = Preferences

View File

@ -298,7 +298,19 @@ public class PreferencesDialog extends JDialog {
Application.getPreferences().setAutoOpenLastDesignOnStartup(autoOpenDesignFile.isSelected()); Application.getPreferences().setAutoOpenLastDesignOnStartup(autoOpenDesignFile.isSelected());
} }
}); });
panel.add(autoOpenDesignFile); panel.add(autoOpenDesignFile, "wrap");
final JCheckBox enable3D = new JCheckBox(trans.get("pref.dlg.but.enable3d"));
enable3D.setSelected(Application.getPreferences().is3dEnabled());
enable3D.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
Application.getPreferences().set3dEnabled(enable3D.isSelected());
}
});
panel.add(enable3D);
return panel; return panel;
} }

View File

@ -0,0 +1,125 @@
package net.sf.openrocket.gui.figure3d;
import java.awt.SplashScreen;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import javax.media.opengl.GLProfile;
import net.sf.openrocket.arch.SystemInfo;
import net.sf.openrocket.arch.SystemInfo.Platform;
import net.sf.openrocket.gui.main.Splash;
import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.startup.Application;
public class OpenGLUtils {
private static final LogHelper log = Application.getLogger();
/**
* Keep the state of 3D consistent for the entire launch, so if user enables
* 3d and opens a new window it stays disabled.
*/
private static Boolean enabledThisLaunch = null;
/**
* set true in enterDangerZone, false in exitDangerZone allows the exit
* function to return immediately if called extra times
*/
private static boolean inTheDangerZone = false;
/**
* Call this method as early as possible.
*/
public static void earlyInitialize() {
// If crash detection fails this will allow someone to disable
// the 3d preference from the command line.
if (System.getProperty("openrocket.3d.disable") != null) {
Application.getPreferences().set3dEnabled(false);
}
if (!is3dEnabled()) {
log.debug("OpenGL is disabled");
} else {
log.debug("Initializing OpenGL");
enterDangerZone();
if (SystemInfo.getPlatform() == Platform.UNIX) {
log.debug("Dismissing splash screen (Linux/Java/JOGL bug)");
// Fixes a linux / X bug: Splash must be closed before GL Init
SplashScreen splash = Splash.getSplashScreen();
if (splash != null && splash.isVisible())
splash.close();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// Intentionally Ignored
}
}
log.debug("Calling GLProfile.initSingleton()");
GLProfile.initSingleton();
exitDangerZone();
}
}
/**
* Returns true if 3d functions are enabled
*
* @return
*/
static boolean is3dEnabled() {
if (enabledThisLaunch == null)
enabledThisLaunch = new Boolean(Application.getPreferences().is3dEnabled());
return enabledThisLaunch.booleanValue();
}
/**
* Signal that we are about to do something that can cause a GL crash. If
* exitDangerZone is not called after this the 3D user preference will be
* disabled at the next startup.
*/
static void enterDangerZone() {
log.verbose("Entering GL DangerZone");
inTheDangerZone = true;
Application.getPreferences().set3dEnabled(false);
try {
Preferences.userRoot().flush();
} catch (BackingStoreException e) {
log.warn("Unable to flush prefs in enterDangerZone()");
}
}
/**
* Signal that some GL operation has succeeded. the UserPreference will be
* left alone.
*
* Safe to call when not in the danger-zone. Safe to call quite often
*/
static void exitDangerZone() {
if (!inTheDangerZone)
return;
inTheDangerZone = false;
log.verbose("Exiting GL DangerZone");
Application.getPreferences().set3dEnabled(true);
try {
Preferences.userRoot().flush();
} catch (BackingStoreException e) {
log.warn("Unable to flush prefs in exitDangerZone()");
}
}
/**
* Seriously never call this it will segfault the JVM.
@Deprecated
static void segfault(){
try {
log.error("Segfaulting!");
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
Unsafe unsafe = (Unsafe)f.get(null);
unsafe.putAddress(0, 0);
} catch (Exception e) {
log.error("Unable to segfault", e);
}
}
**/
}

View File

@ -6,7 +6,10 @@ import java.awt.Graphics2D;
import java.awt.Point; import java.awt.Point;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.awt.RenderingHints; import java.awt.RenderingHints;
import java.awt.SplashScreen; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
@ -25,16 +28,18 @@ import javax.media.opengl.awt.GLCanvas;
import javax.media.opengl.fixedfunc.GLLightingFunc; import javax.media.opengl.fixedfunc.GLLightingFunc;
import javax.media.opengl.fixedfunc.GLMatrixFunc; import javax.media.opengl.fixedfunc.GLMatrixFunc;
import javax.media.opengl.glu.GLU; import javax.media.opengl.glu.GLU;
import javax.swing.JButton;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.event.MouseInputAdapter; import javax.swing.event.MouseInputAdapter;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.figureelements.CGCaret; import net.sf.openrocket.gui.figureelements.CGCaret;
import net.sf.openrocket.gui.figureelements.CPCaret; import net.sf.openrocket.gui.figureelements.CPCaret;
import net.sf.openrocket.gui.figureelements.FigureElement; import net.sf.openrocket.gui.figureelements.FigureElement;
import net.sf.openrocket.gui.main.Splash; import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.logging.LogHelper; import net.sf.openrocket.logging.LogHelper;
import net.sf.openrocket.rocketcomponent.Configuration; import net.sf.openrocket.rocketcomponent.Configuration;
import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.RocketComponent;
@ -50,6 +55,7 @@ import com.jogamp.opengl.util.awt.Overlay;
public class RocketFigure3d extends JPanel implements GLEventListener { public class RocketFigure3d extends JPanel implements GLEventListener {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final LogHelper log = Application.getLogger(); private static final LogHelper log = Application.getLogger();
private static final Translator trans = Application.getTranslator();
static { static {
//this allows the GL canvas and things like the motor selection //this allows the GL canvas and things like the motor selection
@ -64,8 +70,6 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
private Configuration configuration; private Configuration configuration;
private GLCanvas canvas; private GLCanvas canvas;
private Overlay extrasOverlay, caretOverlay; private Overlay extrasOverlay, caretOverlay;
private BufferedImage cgCaretRaster, cpCaretRaster; private BufferedImage cgCaretRaster, cpCaretRaster;
private volatile boolean redrawExtras = true; private volatile boolean redrawExtras = true;
@ -88,27 +92,49 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
this.setLayout(new BorderLayout()); this.setLayout(new BorderLayout());
//Only initizlize GL if 3d is enabled. //Only initizlize GL if 3d is enabled.
if (is3dEnabled()) { if ( OpenGLUtils.is3dEnabled() ){
//Fixes a linux / X bug: Splash must be closed before GL Init
SplashScreen splash = Splash.getSplashScreen();
if (splash != null && splash.isVisible())
splash.close();
initGLCanvas(); initGLCanvas();
addHierarchyListener(new HierarchyListener(){
@Override
public void hierarchyChanged(HierarchyEvent e) {
OpenGLUtils.enterDangerZone();
RocketFigure3d.this.removeHierarchyListener(this);
}
});
} else {
setupDisabledUI();
} }
} }
/** /**
* Return true if 3d view is enabled. This may be toggled by the user at * Draws a pretty primitive UI that explains that 3D is not enabled.
* launch time.
* @return
*/ */
public static boolean is3dEnabled() { private void setupDisabledUI(){
return System.getProperty("openrocket.3d.disable") == null; canvas = null;
final JPanel panel = new JPanel(new MigLayout("filly"));
panel.add(new JLabel(trans.get("RocketPanel.3DDisabledMessage")));
if ( !Application.getPreferences().is3dEnabled() ){
//this button lets them turn GL on without going to the preference menu.
final JButton enable = new JButton(trans.get("RocketPanel.3DDisabledMessageButton"));
enable.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
Application.getPreferences().set3dEnabled(true);
panel.remove(enable);
panel.revalidate();
panel.repaint();
}
});
panel.add(enable);
}
this.add(panel);
} }
private void initGLCanvas() { private void initGLCanvas() {
log.debug("Initializing RocketFigure3D OpenGL Canvas"); log.debug("Initializing RocketFigure3D OpenGL Canvas");
OpenGLUtils.enterDangerZone();
try { try {
log.debug("Setting up GL capabilities..."); log.debug("Setting up GL capabilities...");
@ -139,7 +165,7 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
log.verbose("GL - Setting up mouse listeners"); log.verbose("GL - Setting up mouse listeners");
setupMouseListeners(); setupMouseListeners();
log.verbose("GL - Rasterizine Carets"); //reticulating splines? log.verbose("GL - Rasterizing Carets"); //reticulating splines?
rasterizeCarets(); rasterizeCarets();
} catch (Throwable t) { } catch (Throwable t) {
@ -147,6 +173,8 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
canvas = null; canvas = null;
this.add(new JLabel("Unable to load 3d Libraries: " this.add(new JLabel("Unable to load 3d Libraries: "
+ t.getMessage())); + t.getMessage()));
} finally {
OpenGLUtils.exitDangerZone();
} }
} }
@ -285,6 +313,8 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
drawExtras(gl, glu); drawExtras(gl, glu);
drawCarets(gl, glu); drawCarets(gl, glu);
OpenGLUtils.exitDangerZone();
} }
@ -567,7 +597,7 @@ public class RocketFigure3d extends JPanel implements GLEventListener {
glu.gluProject(c.x, c.y, c.z, mvmatrix, 0, projmatrix, 0, viewport, 0, glu.gluProject(c.x, c.y, c.z, mvmatrix, 0, projmatrix, 0, viewport, 0,
out, 0); out, 0);
log.verbose("GL - peoject() complete"); log.verbose("GL - project() complete");
return new Coordinate(out[0], out[1], out[2]); return new Coordinate(out[0], out[1], out[2]);
} }

View File

@ -285,7 +285,6 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change
} }
}); });
bg.add(toggle3d); bg.add(toggle3d);
toggle3d.setEnabled(RocketFigure3d.is3dEnabled());
add(toggle3d, "gap rel"); add(toggle3d, "gap rel");
// Zoom level selector // Zoom level selector

View File

@ -49,6 +49,7 @@ public abstract class Preferences {
// Node names // Node names
public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors"; public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors";
private static final String AUTO_OPEN_LAST_DESIGN = "AUTO_OPEN_LAST_DESIGN"; private static final String AUTO_OPEN_LAST_DESIGN = "AUTO_OPEN_LAST_DESIGN";
private static final String THREE_D_ENABLED = "THREE_D_ENABLED";
/* /*
* ****************************************************************************************** * ******************************************************************************************
@ -115,6 +116,22 @@ public abstract class Preferences {
return this.getBoolean(AUTO_OPEN_LAST_DESIGN, false); return this.getBoolean(AUTO_OPEN_LAST_DESIGN, false);
} }
/**
* Enable/Disable the 3d functionality
* @param enabled
*/
public final void set3dEnabled(boolean enabled){
this.putBoolean(THREE_D_ENABLED, enabled);
}
/**
*
* @return true if 3d is enabled.
*/
public final boolean is3dEnabled(){
return this.getBoolean(THREE_D_ENABLED, true);
}
/** /**
* Return the OpenRocket unique ID. * Return the OpenRocket unique ID.
* *

View File

@ -4,6 +4,7 @@ import java.io.PrintStream;
import java.util.Locale; import java.util.Locale;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
import net.sf.openrocket.gui.figure3d.OpenGLUtils;
import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.l10n.DebugTranslator; import net.sf.openrocket.l10n.DebugTranslator;
import net.sf.openrocket.l10n.L10N; import net.sf.openrocket.l10n.L10N;
@ -51,6 +52,8 @@ public class Startup {
Application.setPreferences( new SwingPreferences() ); Application.setPreferences( new SwingPreferences() );
OpenGLUtils.earlyInitialize();
// Setup the translations // Setup the translations
initializeL10n(); initializeL10n();