At what point does this happen? Is it all the time? Also, are you trying it on your own scene or with some of the jME test classes?
Sorry for all the questions. Please provide as much info as possible, a reproducible test would be perfect if possible. Oh yeah, thanks for trying Scene Monitor out! I hope to get it fixed for you.
all the time and with my own scene.
it seems that for every update I get one exception.
I'll try somethings here and tell u latter.
thank you very much for the attention.
Hi nymon, and thank you for this great piece of software that'll probably benefit every JME apps developpers out there. And it's really simple to use.
I may be mistaken, but I think there's a problem in the last ZIP for JME2.0 as the sm_properties_jme2.jar only contains textdescriptor package.
That may explain why I'm unable to get the read/write property page.
Thanks again for your great works !
Thanks for the complements, glad you're enjoying it and getting some use of it.
SilverGhost said:
I may be mistaken, but I think there's a problem in the last ZIP for JME2.0 as the sm_properties_jme2.jar only contains textdescriptor package.
That may explain why I'm unable to get the read/write property page.
Sorry about that, it should be fixed now.
Hi There
This tool is great! I have implemented some panels for manipulating other entities.
Because iam german, i had to type in float textareas in an editorpanel a "," instead of a "." - because when writing back the values to the manipulated objects, the line decimal.parse(val) depends on current locale - if there is a "." in the string, the value is cut at the point. But if i open again the editorpanel, the values are with a "." - so i would have to change every float value of the editorpanel by hand with a "," so it can be parsed correctly to a float.
I want a "." in my float values, because its irritating to type a "." in Eclipse and a "," in the SceneEditor…
I changed the A_EditorPanel.class at line 79 to:
// try {
// ret = decimal.parse(val).floatValue();
//
// }
// catch (ParseException e) {
// e.printStackTrace();
// }
try {
ret = Float.parseFloat(val);
}
catch (NumberFormatException e) {
e.printStackTrace();
}
i am wondering that this is a problem - the decimal is inited as
protected DecimalFormat decimal = new DecimalFormat("######.####");
as this shouldn't take the Locale into account....
Perhaps you have a better solution for this,
greets
snare
I am open to suggestions here. I originally did not take into account localization, but then someone had issues because their locale wanted commas. So I made the change and it worked. But if I understand you correctly, you want to ignore the locale specs and just do decimal? I'm really not sure, other than go ahead and make the change in your source. I originally just had Float.parseFloat() like you put back.
Hi there,
After posting i saw that i could have easily overwritten the toFloat function in my own editor which is extending A_EditorPanel…
A compelete localisation would be great, e.g. showing the floats in german with a "," in the textboxes - so this hack wouldnt be "required"…
I saw something else, where i think this could be problematic. I was investigating, because the longer the scenemonitor was open, the slower it became.
In an update method of eg SimpleGame i called "SceneMontior.getMonitor().update()".
When looking at the update() methods i think its relativle clear:
For example:
BooleanPropertyObject active = (BooleanPropertyObject)model.getPropertySection("Controller").getProperty("Active").getPropertyObject();
active.SetListener(new I_BooleanPropertyObjectListener() {
public boolean readValue() {
return controller.isActive();
}
public void saveValue(boolean value) {
controller.setActive(value);
}
});
In every update there are created many new objects - although they could be reused?
The CellEditors for example are also always newly instantiated, creating more garbage.
Greets
Snare
Good catch. I wish I would have thought that through a little better. I'll work on it this weekend and get it fixed up, including the localization. Also, ncomp has been working very hard on something special using this as a starting point (I won't let the cat out of the bag, he can do that) so I've got some changes from his end to roll into it as well.
Thanks 4 your work
Hi nymon,
it's possible to tell SceneMonitor to select a particular node.
Do you think it's possible to do it in the other direction now ? When the selection changes inside SceneMonitor a notification is send to all the SceneMonitorListeners with a SelectionChangedEvent ?
Thank you.
snareoj2 said:
...
I saw something else, where i think this could be problematic. I was investigating, because the longer the scenemonitor was open, the slower it became.
In an update method of eg SimpleGame i called "SceneMontior.getMonitor().update()".
-snip-
active.SetListener(new I_BooleanPropertyObjectListener() {
-snip-
In every update there are created many new objects - although they could be reused?
I did some digging and remembered why I was OK with creating new listeners. They don't get created every update, but only when the cell editor changes the object that is being edited. So for example, you can select a node, and the node cell editor will get this new selection and create listeners for the editing. You can select other objects in the tree like renderstates and such and go back to that node and it maintain the same listeners. If you select another node, then they get recreated for that node. This is because the cell renderers and cell editors are only instanciated once, and shared. So worse case scenario, new listeners would be created every selection, but I figured that really wasn't too bad, and would be GC'd without making much of a problem.
But there very well may be some runaway object creation. I need to run it under a profiler and watch it. If you see any other areas that could use some help please point them out.
snareoj2 said:
The CellEditors for example are also always newly instantiated, creating more garbage.
I believe they only get created one time on tree initialization, and only one copy of each type.
I'm still looking at number handling for different locales, this shouldn't be too hard to finish up.
FredB said:
it's possible to tell SceneMonitor to select a particular node.
Do you think it's possible to do it in the other direction now ? When the selection changes inside SceneMonitor a notification is send to all the SceneMonitorListeners with a SelectionChangedEvent ?
That should be easy enough to implement. Feel free to grab the source and give it a shot.
I promised ncomp I would get the code into google code this week, so we can branch it for his project. I spent last Saturday writing ant scripts to automate the builds and those are all done.
Hi Nymon,
thanks for investigating. So i'll dig deeper into the code, searchingfor the slowdowns.
Greetings,
snare
I like this tool very much!! Great job!
Btw I encountered an issue when using Swing integrating JME such as JMESwingTest.java (in the jmetest.util.package). It always ends up with following exception:
Exception in thread "AWT-EventQueue-0" java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread
at java.awt.EventQueue.invokeAndWait(Unknown Source)
at javax.swing.SwingUtilities.invokeAndWait(Unknown Source)
at com.acarter.scenemonitor.SceneMonitor.<init>(Unknown Source)
at com.acarter.scenemonitor.SceneMonitor.getMonitor(Unknown Source)
at jmetest.util.JMESwingTest$MyImplementor.simpleSetup(JMESwingTest.java:346)
at com.jme.system.canvas.SimpleCanvasImpl.doSetup(SimpleCanvasImpl.java:118)
at com.jmex.awt.lwjgl.LWJGLCanvas.initGL(LWJGLCanvas.java:105)
at org.lwjgl.opengl.AWTGLCanvas.paint(AWTGLCanvas.java:288)
at sun.awt.RepaintArea.paintComponent(Unknown Source)
at sun.awt.RepaintArea.paint(Unknown Source)
at sun.awt.windows.WComponentPeer.handleEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)
Any chance that it gets solved?
Yeah, I will look into it soon, maybe by tomorrow. Can you get me a test that will demonstrate your problem?
I added this issue to the project: http://code.google.com/p/scenemonitor/issues/detail?id=1
Please follow up there.
Hi again. I just tried to add it to the JMESwingTest.java as follows:
/*
* Copyright (c) 2003-2008 jMonkeyEngine
* 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 'jMonkeyEngine' 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.
*/
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.UIManager;
import com.acarter.scenemonitor.SceneMonitor;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.input.InputHandler;
import com.jme.input.KeyInput;
import com.jme.input.action.InputAction;
import com.jme.input.action.InputActionEvent;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.Renderer;
import com.jme.scene.shape.Box;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.system.canvas.JMECanvas;
import com.jme.system.canvas.JMECanvasImplementor;
import com.jme.system.canvas.SimpleCanvasImpl;
import com.jme.system.lwjgl.LWJGLSystemProvider;
import com.jme.util.GameTaskQueueManager;
import com.jme.util.TextureManager;
import com.jmex.awt.input.AWTMouseInput;
import com.jmex.awt.lwjgl.LWJGLAWTCanvasConstructor;
import com.jmex.awt.lwjgl.LWJGLCanvas;
/**
* <code>JMESwingTest</code> is a test demoing the JMEComponent and
* HeadlessDelegate integration classes allowing jME generated graphics to be
* displayed in a AWT/Swing interface. Note the Repaint thread and how you grab
* a canvas and add an implementor to it.
*
* @author Joshua Slack
* @version $Id: JMESwingTest.java,v 1.18 2007/08/17 10:34:35 rherlitz Exp $
*/
public class JMESwingTest {
private static final Logger logger = Logger.getLogger(JMESwingTest.class
.getName());
int width = 640, height = 480;
// Swing frame
private SwingFrame frame;
public JMESwingTest() {
frame = new SwingFrame();
// center the frame
frame.setLocationRelativeTo(null);
// show frame
frame.setVisible(true);
}
/**
* Main Entry point...
*
* @param args
* String[]
*/
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
logger.logp(Level.SEVERE, JMESwingTest.class.toString(),
"main(args)", "Exception", e);
}
new JMESwingTest();
}
// **************** SWING FRAME ****************
// Our custom Swing frame... Nothing really special here.
class SwingFrame extends JFrame {
private static final long serialVersionUID = 1L;
JPanel contentPane;
JPanel mainPanel = new JPanel();
LWJGLCanvas canvas = null;
JButton coolButton = new JButton();
JButton uncoolButton = new JButton();
JPanel spPanel = new JPanel();
JScrollPane scrollPane = new JScrollPane();
JTree jTree1 = new JTree();
JCheckBox scaleBox = new JCheckBox("Scale GL Image");
JPanel colorPanel = new JPanel();
JLabel colorLabel = new JLabel("BG Color:");
JMECanvasImplementor impl;
// Construct the frame
public SwingFrame() {
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
dispose();
}
});
init();
pack();
}
// Component initialization
private void init() {
contentPane = (JPanel) this.getContentPane();
contentPane.setLayout(new BorderLayout());
mainPanel.setLayout(new GridBagLayout());
setTitle("JME - SWING INTEGRATION TEST");
//
GL STUFF
// make the canvas:
DisplaySystem display = DisplaySystem.getDisplaySystem(LWJGLSystemProvider.LWJGL_SYSTEM_IDENTIFIER);
display.registerCanvasConstructor("AWT", LWJGLAWTCanvasConstructor.class);
canvas = (LWJGLCanvas)display.createCanvas(width, height);
canvas.setUpdateInput(true);
canvas.setTargetRate(60);
// add a listener... if window is resized, we can do something about
// it.
canvas.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent ce) {
doResize();
}
});
// Setup key and mouse input
KeyInput.setProvider(KeyInput.INPUT_AWT);
KeyListener kl = (KeyListener) KeyInput.get();
canvas.addKeyListener(kl);
AWTMouseInput.setup(canvas, false);
// Important! Here is where we add the guts to the panel:
impl = new MyImplementor(width, height);
canvas.setImplementor(impl);
//
END OF GL STUFF
coolButton.setText("Cool Button");
uncoolButton.setText("Uncool Button");
colorPanel.setBackground(java.awt.Color.black);
colorPanel.setToolTipText("Click here to change Panel BG color.");
colorPanel.setBorder(BorderFactory.createRaisedBevelBorder());
colorPanel.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(MouseEvent e) {
final java.awt.Color color = JColorChooser.showDialog(
SwingFrame.this, "Choose new background color:",
colorPanel.getBackground());
if (color == null)
return;
colorPanel.setBackground(color);
Callable<?> call = new Callable<Object>() {
public Object call() throws Exception {
canvas.setBackground(color);
return null;
}
};
GameTaskQueueManager.getManager().render(call);
}
});
scaleBox.setOpaque(false);
scaleBox.setSelected(true);
scaleBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (canvas != null)
doResize();
}
});
spPanel.setLayout(new BorderLayout());
contentPane.add(mainPanel, BorderLayout.WEST);
mainPanel.add(scaleBox,
new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER,
GridBagConstraints.HORIZONTAL, new Insets(5, 5, 0,
5), 0, 0));
mainPanel.add(colorLabel,
new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER,
GridBagConstraints.HORIZONTAL, new Insets(5, 5, 0,
5), 0, 0));
mainPanel.add(colorPanel, new GridBagConstraints(0, 2, 1, 1, 0.0,
0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
new Insets(5, 5, 0, 5), 25, 25));
mainPanel.add(coolButton,
new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER,
GridBagConstraints.HORIZONTAL, new Insets(5, 5, 0,
5), 0, 0));
mainPanel.add(uncoolButton,
new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER,
GridBagConstraints.HORIZONTAL, new Insets(5, 5, 0,
5), 0, 0));
mainPanel.add(spPanel, new GridBagConstraints(0, 5, 1, 1, 1.0, 1.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(5, 5, 0, 5), 0, 0));
spPanel.add(scrollPane, BorderLayout.CENTER);
scrollPane.setViewportView(jTree1);
canvas.setBounds(0, 0, width, height);
contentPane.add(canvas, BorderLayout.CENTER);
}
protected void doResize() {
if (scaleBox != null && scaleBox.isSelected()) {
impl.resizeCanvas(canvas.getWidth(), canvas.getHeight());
} else {
impl.resizeCanvas(width, height);
}
((JMECanvas)canvas).makeDirty();
}
// Overridden so we can exit when window is closed
protected void processWindowEvent(WindowEvent e) {
super.processWindowEvent(e);
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
System.exit(0);
}
}
}
// IMPLEMENTING THE SCENE:
class MyImplementor extends SimpleCanvasImpl {
private Quaternion rotQuat;
private float angle = 0;
private Vector3f axis;
private Box box;
long startTime = 0;
long fps = 0;
private InputHandler input;
public MyImplementor(int width, int height) {
super(width, height);
}
public void simpleSetup() {
// Normal Scene setup stuff...
rotQuat = new Quaternion();
axis = new Vector3f(1, 1, 0.5f);
axis.normalizeLocal();
Vector3f max = new Vector3f(5, 5, 5);
Vector3f min = new Vector3f(-5, -5, -5);
box = new Box("Box", min, max);
box.setModelBound(new BoundingBox());
box.updateModelBound();
box.setLocalTranslation(new Vector3f(0, 0, -10));
box.setRenderQueueMode(Renderer.QUEUE_SKIP);
rootNode.attachChild(box);
box.setRandomColors();
TextureState ts = renderer.createTextureState();
ts.setEnabled(true);
ts.setTexture(TextureManager.loadTexture(JMESwingTest.class
.getClassLoader().getResource(
"jmetest/data/images/Monkey.jpg"),
Texture.MinificationFilter.BilinearNearestMipMap,
Texture.MagnificationFilter.Bilinear));
rootNode.setRenderState(ts);
startTime = System.currentTimeMillis() + 5000;
input = new InputHandler();
input.addAction(new InputAction() {
public void performAction(InputActionEvent evt) {
logger.info(evt.getTriggerName());
}
}, InputHandler.DEVICE_MOUSE, InputHandler.BUTTON_ALL,
InputHandler.AXIS_NONE, false);
input.addAction(new InputAction() {
public void performAction(InputActionEvent evt) {
logger.info(evt.getTriggerName());
}
}, InputHandler.DEVICE_KEYBOARD, InputHandler.BUTTON_ALL,
InputHandler.AXIS_NONE, false);
SceneMonitor.getMonitor().registerNode(rootNode, "Root Node");
SceneMonitor.getMonitor().showViewer(true);
}
@Override
public void simpleUpdate() {
input.update(tpf);
// Code for rotating the box... no surprises here.
if (tpf < 1) {
angle = angle + (tpf * 25);
if (angle > 360) {
angle = 0;
}
}
rotQuat.fromAngleNormalAxis(angle * FastMath.DEG_TO_RAD, axis);
box.setLocalRotation(rotQuat);
if (startTime > System.currentTimeMillis()) {
fps++;
} else {
long timeUsed = 5000 + (startTime - System.currentTimeMillis());
startTime = System.currentTimeMillis() + 5000;
logger.info(fps + " frames in " + (timeUsed / 1000f)
+ " seconds = " + (fps / (timeUsed / 1000f))
+ " FPS (average)");
fps = 0;
}
super.simpleUpdate();
SceneMonitor.getMonitor().updateViewer(tpf);
}
@Override
public void simpleRender() {
super.simpleRender();
SceneMonitor.getMonitor().renderViewer(DisplaySystem.getDisplaySystem().getRenderer());
}
// deactivated because it says it needs something like override or superiplementor
// @Override
// protected void cleanup() {
// super.cleanup();
// SceneMonitor.getMonitor().unregisterNode(rootNode);
// }
}
}
Owaye: Hows the fix working?
No news is good news?!
ashtonv said:
No news is good news?!
I hope so!!
Hi! sorry for the late feedback; yes it works very nice. Also I tried it out on RenParticleEditor without problems so far Great job, Thanks!
Hi,
When I use SceneMonitor in my game, my shutdown procedure doesn't work: some thread is alive. However if I don't load any class that references SceneMonitor, shudown works fine (the process ends).
This happens even if I don't get to make SceneMonitor visible.
I can easily check this commenting my SceneMonitor view:
<view id="view/root/level/ui/scenemonitor" class="net.jgf.jme.view.devel.SceneMonitorView"
autoLoad="true" autoActivate="false" context="debug">
<scene ref="scene" />
<updateInterval>5</updateInterval>
</view>
When the class is loaded, at runtime, there are two extra threads in my process:
Threads are as follows:
With NO SceneMonitor, this is my stack:
net.jgf.example.tanks.Tanks at localhost:4563
Thread [systemInfo] (Running)
Daemon Thread [AWT-Windows] (Running)
Thread [OpenGL] (Running)
Thread [DestroyJavaVM] (Running)
With SceneMonitor, this is my stack at runtime:
net.jgf.example.tanks.Tanks at localhost:4570
Thread [systemInfo] (Running)
Daemon Thread [AWT-Windows] (Running)
Thread [OpenGL] (Running)
Thread [DestroyJavaVM] (Running)
Thread [AWT-Shutdown] (Running)
Thread [AWT-EventQueue-0] (Running)
And this is what is left after (unsuccessfully) shutting down:
net.jgf.example.tanks.Tanks at localhost:4570
Daemon Thread [AWT-Windows] (Running)
Thread [DestroyJavaVM] (Running)
Thread [AWT-Shutdown] (Running)
Thread [AWT-EventQueue-0] (Running)
What could SceneMonitor be doing to retain a thread alive?
What are those two threads "AWT-Shutdown" and "AWT-EventQueue-0"?