diff --git a/lib/LIBS b/lib/LIBS index 339f58471..5e1e0b9e2 100644 --- a/lib/LIBS +++ b/lib/LIBS @@ -6,6 +6,7 @@ commons-io.jar commons-lang.jar commons-logging.jar jme-awt.jar +jme-effects.jar jme-bui.jar jme-model.jar jme-sound.jar diff --git a/rsrc/i18n/jme/viewer.properties b/rsrc/i18n/jme/viewer.properties index 1656ca058..b5363738b 100644 --- a/rsrc/i18n/jme/viewer.properties +++ b/rsrc/i18n/jme/viewer.properties @@ -7,6 +7,7 @@ m.title = Three Rings Model Viewer m.file_menu = File m.file_load = Load Model... +m.file_import = Import JME File... m.file_quit = Quit m.view_menu = View @@ -25,6 +26,9 @@ m.mode_skin = Skin m.load_title = Select Model to Load m.load_filter = Model Files (*.properties, *.dat) +m.import_title = Select File to Import +m.import_filter = JME Files (*.jme) + m.anim_select = Animations: m.anim_start = Start m.anim_stop = Stop @@ -36,6 +40,10 @@ m.loaded_model = Loaded {0} m.load_error = Error loading {0}: {1} m.invalid_type = Invalid file type. +m.import = JME File: {0} +m.scale = Scale +m.respawn_particles = Respawn Particles + m.rotate_light = Rotate Light m.azimuth = Azimuth: m.elevation = Elevation: diff --git a/src/java/com/threerings/jme/JmeCanvasApp.java b/src/java/com/threerings/jme/JmeCanvasApp.java index def5a70a3..3e7faf587 100644 --- a/src/java/com/threerings/jme/JmeCanvasApp.java +++ b/src/java/com/threerings/jme/JmeCanvasApp.java @@ -22,16 +22,20 @@ package com.threerings.jme; import java.awt.Canvas; +import java.awt.Graphics; import java.awt.EventQueue; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; +import org.lwjgl.LWJGLException; + import com.jme.renderer.Renderer; import com.jme.renderer.lwjgl.LWJGLRenderer; import com.jme.scene.Node; import com.jme.system.DisplaySystem; import com.jmex.awt.JMECanvas; import com.jmex.awt.JMECanvasImplementor; +import com.jmex.awt.lwjgl.LWJGLCanvas; import com.jmex.bui.CanvasRootNode; @@ -44,7 +48,15 @@ public class JmeCanvasApp extends JmeApp public JmeCanvasApp (int width, int height) { _display = DisplaySystem.getDisplaySystem("LWJGL"); - _canvas = _display.createCanvas(width, height); + + // create a throwaway canvas so that the display system knows it's + // created, then create our custom canvas + _display.createCanvas(width, height); + try { + _canvas = createCanvas(); + } catch (LWJGLException e) { + Log.warning("Failed to create LWJGL canvas [error=" + e + "]."); + } ((JMECanvas)_canvas).setImplementor(_winimp); _canvas.setBounds(0, 0, width, height); _canvas.addComponentListener(new ComponentAdapter() { @@ -91,7 +103,28 @@ public class JmeCanvasApp extends JmeApp { return EventQueue.isDispatchThread(); } - + + /** + * Creates and returns the LWJGL canvas instance. + */ + protected Canvas createCanvas () + throws LWJGLException + { + // LWJGL's canvas releases the context after painting. we make it + // current again, because we want it valid when we process events. + return new LWJGLCanvas() { + public void update (Graphics g) { + super.update(g); + try { + makeCurrent(); + } catch (LWJGLException e) { + Log.warning("Failed to make context current [error=" + + e + "]."); + } + } + }; + } + // documentation inherited protected DisplaySystem createDisplay () { diff --git a/src/java/com/threerings/jme/tools/ModelViewer.java b/src/java/com/threerings/jme/tools/ModelViewer.java index 380e7d725..b4f45dd8f 100644 --- a/src/java/com/threerings/jme/tools/ModelViewer.java +++ b/src/java/com/threerings/jme/tools/ModelViewer.java @@ -89,7 +89,9 @@ import com.jme.scene.state.ZBufferState; import com.jme.system.JmeException; import com.jme.util.LoggingSystem; import com.jme.util.TextureManager; +import com.jme.util.export.binary.BinaryImporter; import com.jme.util.geom.Debugger; +import com.jmex.effects.particles.ParticleMesh; import com.samskivert.swing.GroupLayout; import com.samskivert.swing.Spacer; @@ -168,6 +170,16 @@ public class ModelViewer extends JmeCanvasApp KeyStroke.getKeyStroke(KeyEvent.VK_L, KeyEvent.CTRL_MASK)); file.add(load); + Action importAction = new AbstractAction(_msg.get("m.file_import")) { + public void actionPerformed (ActionEvent e) { + showImportDialog(); + } + }; + importAction.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_I); + importAction.putValue(Action.ACCELERATOR_KEY, + KeyStroke.getKeyStroke(KeyEvent.VK_I, KeyEvent.CTRL_MASK)); + file.add(importAction); + file.addSeparator(); Action quit = new AbstractAction(_msg.get("m.file_quit")) { public void actionPerformed (ActionEvent e) { @@ -492,7 +504,7 @@ public class ModelViewer extends JmeCanvasApp if (file.isDirectory()) { return true; } - String path = file.toString(); + String path = file.toString().toLowerCase(); return path.endsWith(".properties") || path.endsWith(".dat"); } @@ -632,6 +644,54 @@ public class ModelViewer extends JmeCanvasApp } } + /** + * Shows the import particle system dialog. + */ + protected void showImportDialog () + { + if (_ichooser == null) { + _ichooser = new JFileChooser(); + _ichooser.setDialogTitle(_msg.get("m.import_title")); + _ichooser.setFileFilter(new FileFilter() { + public boolean accept (File file) { + if (file.isDirectory()) { + return true; + } + String path = file.toString().toLowerCase(); + return path.endsWith(".jme"); + } + public String getDescription () { + return _msg.get("m.import_filter"); + } + }); + File dir = new File(_config.getValue("import_dir", ".")); + if (dir.exists()) { + _ichooser.setCurrentDirectory(dir); + } + } + if (_ichooser.showOpenDialog(_frame) == JFileChooser.APPROVE_OPTION) { + importFile(_ichooser.getSelectedFile()); + } + _config.setValue("import_dir", + _ichooser.getCurrentDirectory().toString()); + } + + /** + * Attempts to import the specified file as a JME binary scene. + */ + protected void importFile (File file) + { + try { + new ImportDialog(file, + (Spatial)BinaryImporter.getInstance().load( + file)).setVisible(true); + + } catch (Exception e) { + e.printStackTrace(); + _status.setText(_msg.get("m.load_error", file, e)); + } + } + /** * Updates the model's animation speed based on the position of the * animation speed slider. @@ -678,6 +738,9 @@ public class ModelViewer extends JmeCanvasApp /** The model file chooser. */ protected JFileChooser _chooser; + /** The import file chooser. */ + protected JFileChooser _ichooser; + /** The desired animation mode. */ protected Model.AnimationMode _animMode; @@ -730,6 +793,84 @@ public class ModelViewer extends JmeCanvasApp } }; + /** Allows users to manipulate an imported JME file. */ + protected class ImportDialog extends JDialog + implements ChangeListener + { + public ImportDialog (File file, Spatial spatial) + { + super(_frame, _msg.get("m.import", file), false); + _spatial = spatial; + + // rotate from y-up to z-up and set initial scale + _spatial.getLocalRotation().fromAngleNormalAxis(FastMath.HALF_PI, + Vector3f.UNIT_X); + _spatial.setLocalScale(0.025f); + + JPanel cpanel = GroupLayout.makeVBox(); + getContentPane().add(cpanel, BorderLayout.CENTER); + + JPanel spanel = new JPanel(); + spanel.add(new JLabel(_msg.get("m.scale"))); + spanel.add(_scale = new JSlider(0, 1000, 250)); + _scale.addChangeListener(this); + cpanel.add(spanel); + + JPanel bpanel = new JPanel(); + bpanel.add(new JButton(new AbstractAction( + _msg.get("m.respawn_particles")) { + public void actionPerformed (ActionEvent e) { + forceRespawn(_spatial); + } + })); + bpanel.add(new JButton(new AbstractAction(_msg.get("m.close")) { + public void actionPerformed (ActionEvent e) { + setVisible(false); + } + })); + getContentPane().add(bpanel, BorderLayout.SOUTH); + pack(); + } + + // documentation inherited from interface ChangeListener + public void stateChanged (ChangeEvent e) + { + _spatial.setLocalScale(_scale.getValue() * 0.0001f); + } + + @Override // documentation inherited + public void setVisible (boolean visible) + { + super.setVisible(visible); + if (visible && _spatial.getParent() == null) { + _ctx.getGeometry().attachChild(_spatial); + } else if (!visible && _spatial.getParent() != null) { + _ctx.getGeometry().detachChild(_spatial); + } + } + + /** + * Recursively forces all particles to respawn. + */ + protected void forceRespawn (Spatial spatial) + { + if (spatial instanceof ParticleMesh) { + ((ParticleMesh)spatial).forceRespawn(); + } else if (spatial instanceof Node) { + Node node = (Node)spatial; + for (int ii = 0, nn = node.getQuantity(); ii < nn; ii++) { + forceRespawn(node.getChild(ii)); + } + } + } + + /** The imported scene. */ + protected Spatial _spatial; + + /** The scale slider. */ + protected JSlider _scale; + } + /** Allows users to move the directional light around. */ protected class RotateLightDialog extends JDialog implements ChangeListener @@ -773,12 +914,6 @@ public class ModelViewer extends JmeCanvasApp -FastMath.sin(el)); } - // documentation inherited from interface ActionListener - public void actionPerformed (ActionEvent e) - { - setVisible(false); - } - /** Azimuth and elevation sliders. */ protected JSlider _azimuth, _elevation; }