sceneexplorer.adoc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. = The SceneExplorer
  2. :revnumber: 2.0
  3. :revdate: 2020/07/10
  4. == Adding Node types to SceneExplorer
  5. If your plugin brings in its own SceneGraph objects you can still have them work like any other SceneExplorer item, including its special properties.
  6. If you want to support special properties of your objects that are not exposed by the SDK automatically, you will have to create your own class that extends org.openide.nodes.Node and implement the interface com.jme3.gde.core.sceneexplorer.nodes.AbstractSceneExplorerNode. Then you register that class by adding
  7. [source]
  8. ----
  9. @org.openide.util.lookup.ServiceProvider(service=SceneExplorerNode.class)
  10. ----
  11. above the body of your class. Thats all, your Spatial type will automatically be used and displayed in the SceneExplorer. Make sure you register a jar with the used classes in the plugin preferences under "`wrapped`" libraries, otherwise the IDE cannot access those classes.
  12. AbstractSceneExplorerNode brings some other useful features you might want to include like automatic creation of properly threaded properties etc. JmeSpatial for example bases on it. A simple SceneExplorerNode example for an object extending Spatial would be JmeGeometry (see below). Editors for special variable types can be added using the SceneExplorerPropertyEditor interface, which can be registered as a ServiceProvider as well.
  13. The SceneExplorerNode can be used for Spatial and Control type objects.
  14. * _Add the Nodes +++<abbr title="Application Programming Interface">API</abbr>+++ and Lookup +++<abbr title="Application Programming Interface">API</abbr>+++ libraries to your project when you want to use this_
  15. === Spatial Example
  16. [source,java]
  17. ----
  18. @org.openide.util.lookup.ServiceProvider(service=SceneExplorerNode.class)
  19. public class JmeGeometry extends JmeSpatial {
  20. private static Image smallImage =
  21. ImageUtilities.loadImage("com/jme3/gde/core/sceneexplorer/nodes/icons/geometry.gif");
  22. private Geometry geom;
  23. public JmeGeometry() {
  24. }
  25. public JmeGeometry(Geometry spatial, SceneExplorerChildren children) {
  26. super(spatial, children);
  27. getLookupContents().add(spatial);
  28. this.geom = spatial;
  29. setName(spatial.getName());
  30. }
  31. @Override
  32. public Image getIcon(int type) {
  33. return smallImage;
  34. }
  35. @Override
  36. public Image getOpenedIcon(int type) {
  37. return smallImage;
  38. }
  39. @Override
  40. protected Sheet createSheet() {
  41. Sheet sheet = super.createSheet();
  42. Sheet.Set set = Sheet.createPropertiesSet();
  43. set.setDisplayName("Geometry");
  44. set.setName(Geometry.class.getName());
  45. Geometry obj = geom;//getLookup().lookup(Geometry.class);
  46. if (obj == null) {
  47. return sheet;
  48. }
  49. set.put(makeProperty(obj, int.class, "getLodLevel", "setLodLevel", "Lod Level"));
  50. set.put(makeProperty(obj, Material.class, "getMaterial", "setMaterial", "Material"));
  51. set.put(makeProperty(obj, Mesh.class, "getMesh", "Mesh"));
  52. sheet.put(set);
  53. return sheet;
  54. }
  55. public Class getExplorerObjectClass() {
  56. return Geometry.class;
  57. }
  58. public Class getExplorerNodeClass() {
  59. return JmeGeometry.class;
  60. }
  61. public org.openide.nodes.Node[] createNodes(Object key, Object key2, boolean readOnly) {
  62. SceneExplorerChildren children=new SceneExplorerChildren((com.jme3.scene.Spatial)key);
  63. children.setReadOnly(readOnly);
  64. return new org.openide.nodes.Node[]{new JmeGeometry((Geometry) key, children).setReadOnly(readOnly)};
  65. }
  66. }
  67. ----
  68. === Control Example
  69. [source,java]
  70. ----
  71. @org.openide.util.lookup.ServiceProvider(service=SceneExplorerNode.class)
  72. public class JmeGhostControl extends AbstractSceneExplorerNode {
  73. private static Image smallImage =
  74. ImageUtilities.loadImage("com/jme3/gde/core/sceneexplorer/nodes/icons/ghostcontrol.gif");
  75. private GhostControl control;
  76. public JmeGhostControl() {
  77. }
  78. public JmeGhostControl(GhostControl control, DataObject dataObject) {
  79. super(dataObject);
  80. getLookupContents().add(this);
  81. getLookupContents().add(control);
  82. this.control = control;
  83. setName("GhostControl");
  84. }
  85. @Override
  86. public Image getIcon(int type) {
  87. return smallImage;
  88. }
  89. @Override
  90. public Image getOpenedIcon(int type) {
  91. return smallImage;
  92. }
  93. protected SystemAction[] createActions() {
  94. return new SystemAction[]{
  95. // SystemAction.get(CopyAction.class),
  96. // SystemAction.get(CutAction.class),
  97. // SystemAction.get(PasteAction.class),
  98. SystemAction.get(DeleteAction.class)
  99. };
  100. }
  101. @Override
  102. public boolean canDestroy() {
  103. return !readOnly;
  104. }
  105. @Override
  106. public void destroy() throws IOException {
  107. super.destroy();
  108. final Spatial spat=getParentNode().getLookup().lookup(Spatial.class);
  109. try {
  110. SceneApplication.getApplication().enqueue(new Callable<Void>() {
  111. public Void call() throws Exception {
  112. spat.removeControl(control);
  113. return null;
  114. }
  115. }).get();
  116. ((AbstractSceneExplorerNode)getParentNode()).refresh(true);
  117. } catch (InterruptedException ex) {
  118. Exceptions.printStackTrace(ex);
  119. } catch (ExecutionException ex) {
  120. Exceptions.printStackTrace(ex);
  121. }
  122. }
  123. @Override
  124. protected Sheet createSheet() {
  125. Sheet sheet = super.createSheet();
  126. Sheet.Set set = Sheet.createPropertiesSet();
  127. set.setDisplayName("GhostControl");
  128. set.setName(GhostControl.class.getName());
  129. GhostControl obj = control;//getLookup().lookup(Spatial.class);
  130. if (obj == null) {
  131. return sheet;
  132. }
  133. set.put(makeProperty(obj, Vector3f.class, "getPhysicsLocation", "setPhysicsLocation", "Physics Location"));
  134. set.put(makeProperty(obj, Quaternion.class, "getPhysicsRotation", "setPhysicsRotation", "Physics Rotation"));
  135. set.put(makeProperty(obj, CollisionShape.class, "getCollisionShape", "setCollisionShape", "Collision Shape"));
  136. set.put(makeProperty(obj, int.class, "getCollisionGroup", "setCollisionGroup", "Collision Group"));
  137. set.put(makeProperty(obj, int.class, "getCollideWithGroups", "setCollideWithGroups", "Collide With Groups"));
  138. sheet.put(set);
  139. return sheet;
  140. }
  141. public Class getExplorerObjectClass() {
  142. return GhostControl.class;
  143. }
  144. public Class getExplorerNodeClass() {
  145. return JmeGhostControl.class;
  146. }
  147. public org.openide.nodes.Node[] createNodes(Object key, DataObject key2, boolean cookie) {
  148. return new org.openide.nodes.Node[]{new JmeGhostControl((GhostControl) key, key2).setReadOnly(cookie)};
  149. }
  150. }
  151. ----
  152. == Adding items to the add and tools menus
  153. For adding Spatials, Contols and for general tools theres premade abstract classes that you can use to extend the options. Undo/Redo is handled by the abstract class. AbstractNewSpatial*Wizard*Action allows you to show an AWT wizard before creating the Spatial. You can also just implement the base ServiceProvider class and return any kind of action (such as a wizard), in this case you have to handle the threading yourself!
  154. [IMPORTANT]
  155. ====
  156. Note that the classes you create are singletons which are used across multiple nodes and you should not store any data in local variables!
  157. ====
  158. To add a new Tool, create a new AbstractToolAction:
  159. [source,java]
  160. ----
  161. @org.openide.util.lookup.ServiceProvider(service = ToolAction.class)
  162. public class GenerateTangentsTool extends AbstractToolAction {
  163. public GenerateTangentsTool() {
  164. name = "Generate Tangents";
  165. }
  166. @Override
  167. protected Object doApplyTool(AbstractSceneExplorerNode rootNode) {
  168. Geometry geom = rootNode.getLookup().lookup(Geometry.class);
  169. Mesh mesh = geom.getMesh();
  170. if (mesh != null) {
  171. TangentBinormalGenerator.generate(mesh);
  172. }
  173. return geom;
  174. }
  175. @Override
  176. protected void doUndoTool(AbstractSceneExplorerNode rootNode, Object undoObject) {
  177. Geometry geom = rootNode.getLookup().lookup(Geometry.class);
  178. Mesh mesh = geom.getMesh();
  179. if (mesh != null) {
  180. mesh.clearBuffer(Type.Tangent);
  181. }
  182. }
  183. public Class<?> getNodeClass() {
  184. return JmeGeometry.class;
  185. }
  186. }
  187. ----
  188. For a new Spatial or Control, use AbstractNewSpatialAction
  189. [source,java]
  190. ----
  191. @org.openide.util.lookup.ServiceProvider(service = NewSpatialAction.class)
  192. public class NewSpecialSpatialAction extends AbstractNewSpatialAction {
  193. public NewSpecialSpatialAction() {
  194. name = "Spatial";
  195. }
  196. @Override
  197. protected Spatial doCreateSpatial(Node parent) {
  198. Spatial spatial=new Node();
  199. return spatial;
  200. }
  201. }
  202. ----
  203. or AbstractNewControlAction:
  204. [source,java]
  205. ----
  206. @org.openide.util.lookup.ServiceProvider(service = NewControlAction.class)
  207. public class NewRigidBodyAction extends AbstractNewControlAction {
  208. public NewRigidBodyAction() {
  209. name = "Static RigidBody";
  210. }
  211. @Override
  212. protected Control doCreateControl(Spatial spatial) {
  213. RigidBodyControl control = spatial.getControl(RigidBodyControl.class);
  214. if (control != null) {
  215. spatial.removeControl(control);
  216. }
  217. Node parent = spatial.getParent();
  218. spatial.removeFromParent();
  219. control = new RigidBodyControl(0);
  220. if (parent != null) {
  221. parent.attachChild(spatial);
  222. }
  223. return control;
  224. }
  225. }
  226. ----
  227. === Adding using a Wizard
  228. You can create a new wizard using the wizard template in the SDK (New File→Module Development→Wizard). The Action that the template creates can easily be changed to one for adding a Control or Spatial or for applying a Tool. Note that we extend AbstractNewSpatial*Wizard*Action here.
  229. A good example is the Add SkyBox Wizard:
  230. [source,java]
  231. ----
  232. @org.openide.util.lookup.ServiceProvider(service = NewSpatialAction.class)
  233. public class AddSkyboxAction extends AbstractNewSpatialWizardAction {
  234. private WizardDescriptor.Panel[] panels;
  235. public AddSkyboxAction() {
  236. name = "Skybox..";
  237. }
  238. @Override
  239. protected Object showWizard(org.openide.nodes.Node node) {
  240. WizardDescriptor wizardDescriptor = new WizardDescriptor(getPanels());
  241. wizardDescriptor.setTitleFormat(new MessageFormat("{0}"));
  242. wizardDescriptor.setTitle("Skybox Wizard");
  243. Dialog dialog = DialogDisplayer.getDefault().createDialog(wizardDescriptor);
  244. dialog.setVisible(true);
  245. dialog.toFront();
  246. boolean cancelled = wizardDescriptor.getValue() != WizardDescriptor.FINISH_OPTION;
  247. if (!cancelled) {
  248. return wizardDescriptor;
  249. }
  250. return null;
  251. }
  252. @Override
  253. protected Spatial doCreateSpatial(Node parent, Object properties) {
  254. if (properties != null) {
  255. return generateSkybox((WizardDescriptor) properties);
  256. }
  257. return null;
  258. }
  259. private Spatial generateSkybox(WizardDescriptor wiz) {
  260. if ((Boolean) wiz.getProperty("multipleTextures")) {
  261. Texture south = (Texture) wiz.getProperty("textureSouth");
  262. Texture north = (Texture) wiz.getProperty("textureNorth");
  263. Texture east = (Texture) wiz.getProperty("textureEast");
  264. Texture west = (Texture) wiz.getProperty("textureWest");
  265. Texture top = (Texture) wiz.getProperty("textureTop");
  266. Texture bottom = (Texture) wiz.getProperty("textureBottom");
  267. Vector3f normalScale = (Vector3f) wiz.getProperty("normalScale");
  268. return SkyFactory.createSky(pm, west, east, north, south, top, bottom, normalScale);
  269. } else {
  270. Texture textureSingle = (Texture) wiz.getProperty("textureSingle");
  271. Vector3f normalScale = (Vector3f) wiz.getProperty("normalScale");
  272. boolean useSpheremap = (Boolean) wiz.getProperty("useSpheremap");
  273. return SkyFactory.createSky(pm, textureSingle, normalScale, useSpheremap);
  274. }
  275. }
  276. /**
  277. * Initialize panels representing individual wizard's steps and sets
  278. * various properties for them influencing wizard appearance.
  279. */
  280. private WizardDescriptor.Panel[] getPanels() {
  281. if (panels == null) {
  282. panels = new WizardDescriptor.Panel[]{
  283. new SkyboxWizardPanel1(),
  284. new SkyboxWizardPanel2()
  285. };
  286. String[] steps = new String[panels.length];
  287. for (int i = 0; i < panels.length; i++) {
  288. Component c = panels[i].getComponent();
  289. // Default step name to component name of panel. Mainly useful
  290. // for getting the name of the target chooser to appear in the
  291. // list of steps.
  292. steps[i] = c.getName();
  293. if (c instanceof JComponent) { // assume Swing components
  294. JComponent jc = (JComponent) c;
  295. // Sets step number of a component
  296. // TODO if using org.openide.dialogs >= 7.8, can use WizardDescriptor.PROP_*:
  297. jc.putClientProperty("WizardPanel_contentSelectedIndex", new Integer(i));
  298. // Sets steps names for a panel
  299. jc.putClientProperty("WizardPanel_contentData", steps);
  300. // Turn on subtitle creation on each step
  301. jc.putClientProperty("WizardPanel_autoWizardStyle", Boolean.TRUE);
  302. // Show steps on the left side with the image on the background
  303. jc.putClientProperty("WizardPanel_contentDisplayed", Boolean.TRUE);
  304. // Turn on numbering of all steps
  305. jc.putClientProperty("WizardPanel_contentNumbered", Boolean.TRUE);
  306. }
  307. }
  308. }
  309. return panels;
  310. }
  311. }
  312. ----
  313. [TIP]
  314. ====
  315. The abstract spatial and control actions implement undo/redo automatically, for the ToolActions you have to implement it yourself.
  316. ====