SceneComposer "Add Control" context menu not showing my item

Further to my creating a paged terrain system, I’m in the process of creating a wizard for adding it as a control in the SceneComposer.

I’ve gone through the tutorials several times, but I can’t get my control to show in the list when I “Right-Click => Add Control…”.

I also registered the library as per this document, but the control just won’t show up in the context menu :frowning:

[java]
package me.jayfella.gridpaging.wizard;

import com.jme3.gde.core.sceneexplorer.nodes.actions.AbstractNewControlWizardAction;
import com.jme3.gde.core.sceneexplorer.nodes.actions.NewControlAction;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control;
import java.awt.Component;
import java.awt.Dialog;
import java.text.MessageFormat;
import javax.swing.JComponent;
import org.openide.DialogDisplayer;
import org.openide.WizardDescriptor;
import org.openide.nodes.Node;

@org.openide.util.lookup.ServiceProvider(service = NewControlAction.class)
public class AddGridTerrainAction extends AbstractNewControlWizardAction
{
private WizardDescriptor.Panel[] panels;

public AddGridTerrainAction()
{
    name = "Paged Terrain...";
}

@Override
protected Object showWizard(Node node)
{
    WizardDescriptor wizardDescriptor = new WizardDescriptor(getPanels());
    wizardDescriptor.setTitleFormat(new MessageFormat("{0}"));
    wizardDescriptor.setTitle("Paged Terrain Wizard");
    Dialog dialog = DialogDisplayer.getDefault().createDialog(wizardDescriptor);
    dialog.setVisible(true);
    dialog.toFront();

    boolean cancelled = wizardDescriptor.getValue() != WizardDescriptor.FINISH_OPTION;

    if (!cancelled)
    {
        return wizardDescriptor;
    }

    return null;


}

@Override
protected Control doCreateControl(Spatial sptl, Object properties)
{
    if (properties != null)
    {
        return generatePagedTerrain((WizardDescriptor) properties);
    }

    return null;
}

private Control generatePagedTerrain(WizardDescriptor wiz)
{
    int blockSize = (Integer)wiz.getProperty("blockSize");
    int patchSize = (Integer)wiz.getProperty("patchSize");

    int scaleX = (Integer)wiz.getProperty("scaleX");
    int scaleY = (Integer)wiz.getProperty("scaleY");
    int scaleZ = (Integer)wiz.getProperty("scaleZ");
    Vector3f scale = new Vector3f(scaleX, scaleY, scaleZ);

    int vd_north = (Integer)wiz.getProperty("vd_north");
    int vd_east = (Integer)wiz.getProperty("vd_east");
    int vd_south = (Integer)wiz.getProperty("vd_south");
    int vd_west = (Integer)wiz.getProperty("vd_west");

    return null;
}

private WizardDescriptor.Panel[] getPanels()
{
    if (panels == null)
    {
        panels = new WizardDescriptor.Panel[]
        {
            new AddPagedTerrainActionWizardPanel1(),
            // new SkyboxWizardPanel2()
        };

        String[] steps = new String[panels.length];

        for (int i = 0; i < panels.length; i++)
        {
            Component c = panels[i].getComponent();

            steps[i] = c.getName();

            if (c instanceof JComponent)
            {
                JComponent jc = (JComponent)c;

                jc.putClientProperty("WizardPanel_contentSelectedIndex", new Integer(i));
                // Sets steps names for a panel
                jc.putClientProperty("WizardPanel_contentData", steps);
                // Turn on subtitle creation on each step
                jc.putClientProperty("WizardPanel_autoWizardStyle", Boolean.TRUE);
                // Show steps on the left side with the image on the background
                jc.putClientProperty("WizardPanel_contentDisplayed", Boolean.TRUE);
                // Turn on numbering of all steps
                jc.putClientProperty("WizardPanel_contentNumbered", Boolean.TRUE);

            }


        }

    }

    return panels;
}

}
[/java]

1 Like

Hm, the code looks okay… Did you make the classes public in the project settings? You also need to add the library via the “wrapped jars” preferences panel so that your plugin code can access it in the SDK runtime. The document you linked describes how to add the library so a user can add it to their projects (which you would probably want, too).

1 Like

I believe so…

The three images below show them set as public, wrapped, and visible in the list of available libraries:

…I’ll try if the class works for me, I’ll be back :slight_smile:

1 Like

It works for me… oO Doesn’t even have to be made public…

How do you test/run it? You sure you right-click a spatial to add it to?

1 Like

yeah i right-click a node in a new scene:

To test it I clean and build the module, then right click the “Libraries” folder in an empty SimpleApplication project and add it from the list (just like the third picture i pasted above). Each time I make a change, i just remove it, rebuild the module, and add it again.

Who knows man… I’ll go over the instructions and start again. I must have done something wrong… Thanks for taking the time to test it, normen.

@jayfella said: yeah i right-click a node in a new scene:

To test it I clean and build the module, then right click the “Libraries” folder in an empty SimpleApplication project and add it from the list (just like the third picture i pasted above).

Who knows man… I’ll go over the instructions and start again. I must have done something wrong… Thanks for taking the time to test it, normen.

No, you have to “run” the module project. A “new” SDK will open where the plugin is active. You can also try to right-click it and select “run in development IDE” but obviously that can crash the IDE you work in :slight_smile:

1 Like

I see. That works :o.

2 Likes

Ok, I’ve made some great progress and the wizard is doing what wizards do, but finally when it comes to adding the control to the selectedNode I get the error below:

I tried adding the library by “right-click Libraries folder => Add Library” and also by “Add Project” but the same error applies in variants of the same result - not being able to find the GridBasedTerrain class - which is in the “PagedTerrain” module/plugin I’m making.

[java]
java.lang.NoClassDefFoundError: me/jayfella/pagedterrain/core/GridBasedTerrain
at me.jayfella.pagedterrain.wizard.AddPagedTerrainAction.generatePagedTerrain(AddPagedTerrainAction.java:89)
at me.jayfella.pagedterrain.wizard.AddPagedTerrainAction.doCreateControl(AddPagedTerrainAction.java:59)
at com.jme3.gde.core.sceneexplorer.nodes.actions.AbstractNewControlWizardAction$1$1.call(AbstractNewControlWizardAction.java:73)
at com.jme3.gde.core.sceneexplorer.nodes.actions.AbstractNewControlWizardAction$1$1.call(AbstractNewControlWizardAction.java:70)
at com.jme3.app.AppTask.invoke(AppTask.java:142)
at com.jme3.app.Application.runQueuedTasks(Application.java:583)
at com.jme3.app.Application.update(Application.java:596)
at com.jme3.gde.core.scene.SceneApplication.update(SceneApplication.java:302)
at com.jme3.system.awt.AwtPanelsContext.updateInThread(AwtPanelsContext.java:188)
at com.jme3.system.awt.AwtPanelsContext.access$100(AwtPanelsContext.java:44)
at com.jme3.system.awt.AwtPanelsContext$AwtPanelsListener.update(AwtPanelsContext.java:68)
at com.jme3.system.lwjgl.LwjglOffscreenBuffer.runLoop(LwjglOffscreenBuffer.java:125)
at com.jme3.system.lwjgl.LwjglOffscreenBuffer.run(LwjglOffscreenBuffer.java:151)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.ClassNotFoundException: me.jayfella.pagedterrain.core.GridBasedTerrain starting from ModuleCL@60ddf8cb[me.jayfella.pagedterrain] with possible defining loaders null and declared parents [ModuleCL@6caf4065[org.openide.dialogs], ModuleCL@384c502d[org.openide.nodes], ModuleCL@651f41fd[org.openide.awt], ModuleCL@47581385[com.jme3.gde.core.baselibs], org.netbeans.MainImpl$BootClassLoader@5ce33b57, ModuleCL@9e0e817[com.jme3.gde.core]]
at org.netbeans.ProxyClassLoader.loadClass(ProxyClassLoader.java:224)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
… 14 more
Caused by: java.lang.ClassNotFoundException: me.jayfella.pagedterrain.core.GridBasedTerrain
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
at org.netbeans.ProxyClassLoader.loadClass(ProxyClassLoader.java:222)
… 15 more
[/java]

The wrapped jar file has to be the same as the jar file in the “PagedTerrain” library, is that the case? It needs to be in the project twice, once for the user and once for the plugin itself to use it.

Yeah, I triple checked, rebuilt, restarted the IDE a few times in the process to be absolutely sure, but it still can’t find the class.

Maybe try making it an actual jar file instead of a zip?

I was hoping it just might be crazy enough to work, but alas, she does not yield. Its just gone midnight here. I’ll be back tomorrow with more stabby weapons.

Hmf. So upon further inspection it seems that the module doesn’t get built properly. The classes aren’t added to the jar - hence the error. So here’s the situation:

  1. When I start the JME SDK I can “Add Paged Terrain…” to any project without “running” the plugin/module (right-click => run) or associating the plugin with the project. Is that the intended behaviour?

  2. When I “Right-Click => Run” the module, it opens up a new instance of NetBeans (not JME SDK). Is that the intended behaviour, or am I missing a file-association?

  3. Upon running the module, it errors out (in the master JME SDK output window, not the netbeans window) with a “java.lang.AssertionError: At most one resource per module”: https://pastebin.com/hMraGhuf

After some googling about, it turns out that the error is related to Compile On Save (compiling twice at the same time - once as a forced save before compile, and again on compile. They seem to overrun each other), but modules don’t have the ability to disable that feature.

So in summary the wizard classes are added, and registered, so the context item appears, but NoClassDefError springs when trying to actually add it to a node because the classes aren’t added as a result of the compile error (yet successful build).

Is it possible that point 1 might be causing a lock on the classes, and the compiler is just spitting out a generic error?

  1. No, you probably used “install/run in development IDE”, unless you do that again it won’t pick up any changes you made in the project, its always safest to “run” the project and do things in the IDE that opens.
  2. Yes, thats correct. Its just not branded, it contains all SDK plugins including yours though
  3. Maybe this happens because you also have it installed in the development IDE? Idk, never got this issue.

All in all something seems to have been messed up while you were trying to get it to work. I’d try to first remove the plugin from the development IDE (so your jME SDK) and then clean and build the whole shebang to run it in a clean test IDE.

I deleted and re-created the module and it now works as intended. I sure made a meal of of that, no doubt.

I believe this is the last hurdle to jump. Is it possible to get a reference to the base SimpleApplication from anywhere? I ask because the plugin uses multi-threaded terrain generation, and one part of the process is utilizing app.enque()

I know you can get the AssetManager as the first line below shows, but the second line only returns null.
[java]
final ProjectAssetManager manager = selectedNode.getLookup().lookup(ProjectAssetManager.class);
final SimpleApplication app = selectedNode.getLookup().lookup(SimpleApplication.class);
[/java]

Also, thanks for the help, half of this thread was my own doing.

Yeah, theres no SimpleApplication available in the lookup, what would you want to do with it?

1 Like

I can get around it somehow, but to answer your question, it’s for the generation logic:

When a TerrainGrid is requested:

  • Check if terraingrid is in the loadedTiles (already in-game)
  • Check if terraingrid is in cache (surrounding tiles)
  • Check if terraingrid is already being generated
  • Check if terraingrid is qued for generation
  • if all fails, it’s nowhere to be seen, so queue it for generation.

For generation I add it to the generation que on the main thread. Every frame it checks that que. It takes the first one from the list and fires off a thread in a pool to generate it. I then enque a callable to remove it from the list when the generation is complete:

[java]
threadpool.submit(new Runnable()
{
@Override public void run()
{
TerrainQuad newTq = getTerrainQuad(terrainLoc);
pendingAddition.add(newTq);

    app.enqueue(new Callable<Boolean>()
    {
        @Override public Boolean call()
        {
            generationQue.remove(terrainLoc);
            return true;
        }
    });
}

});
[/java]

I tried use some form of concurrency set instead but it didn’t work well. Tiles ended up not being removed and all kindsa weird goings on. I guess I’ll go back to the drawing board and figure something out. At least now I’m back in the land I understand.

I see… Is this logic not happening in the control? Or do I misunderstand something? Otherwise, you can perform actions on the scene thread in your add control action and/or listen to selections of the Control to update other things. You can use SceneApplication.getApplication() to get the global instance of the SceneApplication but this is not recommended as this global access might be removed at some point.

1 Like

It does occur in the control logic, but since the control is created by the plugin and returned to the original project, I would have to have an instance of SimpleApplication in the plugin so I can give it to the newly created control in the constructor and use it - hence the original question of how to obtain it in the lookup.