Yep. Another scene editor...

Could you perhaps have a half-and-half approach: have simple NodePaths for NodePath objects and your “editor NodePaths”, attached to their own side-branch of the scene-graph? These “editor NodePaths” would control the position, orientation and scale of the objects that they represent, and whatever code you have handling editor interactions would run a check to recognise them (perhaps looking for a Python Tag or checking whether they’re children of the “editor NodePath” root node) in order to present whatever additional options they might have.

I’m not sure that Unity supports custom nodes. Maybe you can if you’re doing C++ stuff under the hood, but it doesn’t matter anyway because Unity supports native attaching of objects to GameObjects whereas Panda does not (well, not without creating a cyclic reference). I believe UDK can be rigged to place custom nodes but again this would be done in C++ which is not something I want to look into at this stage.

It’s less an issue of implementation and more of how the programmer deals with the scene once it’s been loaded on the game side. In it’s current state, the editor just sees NodePaths which are parented to render if nothing else. This is fine because everything works as expected, with detachNode() and removeNode() working as you would expect.

With the addition of custom nodes and non-NodePath nodes, things get more interesting. Because the editor allows you to create custom nodes, technically you can create and store nodes any way you like - they don’t have to go into the scene graph or even into the scene’s default list of components at all so long as you put them somewhere so they don’t vanish as soon as they go out of scope. Take these custom nodes I’m working with in my dev version at the moment:
NodePath
NodePath with cyclic referenced object
Non-NodePath object
Coppertop’s Bullet KCC
Arbitrary node
In the case of the NodePath you can deal with this as you would normally. Call the removeNode() method on it if you want to get rig of it and all will be well. In the case of the cyclically referenced NodePath, you can call removeNode() but it won’t destroy the NodePath properly because you’re not breaking the cyclic reference. With the others it’s not so easy to know how you’re meant to create and destroy these nodes. An arbitrary node could have come from another user’s plugin - how are you supposed to know how it works?

So I’m essentially saying that you would need to wrap the NodePath and call Destroy() on that instead of calling detachNode() and potentially not having it cleaned up properly. Eg instead of killing a node as you would once retrieved from a collision callback like so:

collEntry = self.collHandler.getEntry( 0 )
np = collEntry.getIntoNodePath()
np.removeNode()

you would ensure you are properly removing a node like this:

collEntry = self.collHandler.getEntry( 0 )
np = collEntry.getIntoNodePath()
wrpr = base.game.nodeMgr.Wrap( np )
wrpr.Destroy()

It’s a small price to pay, but I suppose it means you never have to worry about what plugins you’re using and how your nodes have been set up - so long as the wrapper knows how to cleanup itself then you’re in the clear.

I hope this makes sense!

kurohyou, is these changes affects only nodes removal and creation?
So when I want to rotate or reposition node, set shader input or something similar I still work with native panda nodes and not with wrappers?
And only if I have to deal with something what is implemented in those custom nodes I need to call a wrapper?
If it works this way think its a small price to get advantages of what editor can give… And since I still can mix pure panda nodes created/destroyed manually with wrapper nodes from editor it gives even more, right?

That’s correct. You would only really need to use the wrapper interface if you were creating, deleting or duplicating a node - and only then if you weren’t sure how the node was implemented in the first place. I’m going to try and have vanilla Panda NodePaths behave as normal so if you want to destroy them you just call detachNode() or removeNode(), but at the very least if you want to remove a scene’s collision primitive, texture stage, etc you will have to use the wrapper or else the scene will still reference it.

For the sake of consistency users may get into the habit of wrapping every node in order to remove it - and of course any other node you create once the scene has loaded is exempt from this process.

Hmm… In that case, perhaps it would indeed be a good idea to create a new class to act as the core “object” class in games made using your editor.

Said class would have a NodePath as a member, perhaps a flag to indicate whether said NodePath is editor-only, and a few further properties, such as a name (used to label the object in the editor and perhaps to find it in the game). This class could then be extended by the user to include whatever other functionality and properties they want, avoiding the question of whether or not to use Python tags to store object properties as well as creating a uniform base class for users to work from.

Thanks for your suggestion Thaumaturge, but I think eventually you would run into the same problem.

In the example you’ve given you would need to store your core objects somewhere or else they vanish when they go out of scope. Say you store them as part of the scene as a list (similar to what I’m doing now). If the object has a NodePath as a member you have increased the reference count of that NodePath, so calling detachNode() still won’t work as expected. You would have to ask the scene to release the node before you call detachNode(), so you may as well ask the scene for a wrapper and call detach() on that and be done with it.

It’s worth stating that these issues come about from not being able to subclass PandaNode properly and arise when trying to solve problems like the collision example above. It’s impossible to retrieve a custom node from the scene graph by using find() or from collision results using getIntoNodePath(), etc. The only way I can think of is to get the scene to return the appropriate wrapper and calling methods on that instead.

True, but I’m inclined to think that the increased flexibility and uniformity of object handling would be worth it: as you suggest, keep a list of your objects (perhaps a dictionary, using object names as keys?), and give your core “object” class a method that not only removes the NodePath but also destroys the object – this has the advantage of giving users something that they can override if they want to add custom destructor behaviour, although it of course incurs the risk of users overriding said method and not calling the parent class method, leaving the object only partially destroyed.

Accessing your objects via a dictionary also gets around the issue of attempting to access objects by calling “find” on sections of the scene graph – as long as you know the name of the object, you can look it up quickly and easily.

As long as destruction is handled properly, such things as collision nodes could have cyclic references to allow the wrapper class to access its collision node as well as allowing one to find the wrapper for a given collision object when looking at collision events. Alternatively, give the collision object a Python tag holding the parent object’s name, and then look that up in the dictionary.

This is basically how I tend to handle objects in my own games: it’s seldom that I query the scene graph for anything, that I recall, since I tend to keep handles to my objects via a list or dictionary.

Do you need to create the new project folders in or out of the main directory?
Should the models be placed into the model directory or just create the directory for the resources, then inport from elsewhere and then middle drag from the resources section?

Does it kill it if you import a model, which then doesn’t have the referenced textures in the resources section? Basically mine crashes.

How close are you to a new release?

Hi James,

Thanks for trying out the editor. I’ll try to answer your questions below.

By main directory do you mean the editor’s source directory? You should be able to put your project anywhere you want, but I would probably keep it out of there :slight_smile:

You can copy eggs into your project’s models directory by hand if you wish - currently all the “Import…” menu item does is run egg2Bam in the background, dumping the resulting file in the models directory.

Currently the import model function doesn’t bring in all the asset’s dependencies like textures, etc but this will change. It shouldn’t crash the editor though. Do you have a traceback I can look at?

I’m pretty close to a new release. This one is a big change as the editor will now support the creating of nodes which aren’t derived from PandaNode. Some stuff you might look forward to:

  • Creation of basic Bullet node types
  • Creation of texture nodes
  • Able to connect nodes to other nodes (like when setting a NodePath’s texture or adding BulletRigidBody nodes to BulletWorld
  • Better handling of undo when removing nodes with connections

…including some refactoring of some of the more horrid code.

Hopefully I should be commiting these changes within the next couple of weeks. It would be sooner but stuff happens IRL :slight_smile:

Hmm.
Well I uninstalled panda3d 1.7 and installed Panda1.8. The demos run fine, but then it lost the wx function. Is this an actual file in the python the module wx?
The same thing has now been called by the level editor.
I had installed python 2.8, the Panda install asked about using its own version of python…

Anyway where should the wx module be located? Should WingIDE use python in the Panda3d folder or can it use a separate installation?
Under Edit I can configure which python to use, should I just choose the executable or path?
I could do with a little understanding on python as I have run into problems before, its there but not there stuff. I think I might learn something simple here, then I can hopefully give the trceback call…

Hey all, I’ve just committed my changes since Christmas. From the release notes:

  • Users can now create and edit non-NodePath nodes. …
  • Tweak to wxPanda so events propagate.
  • User can now connect a node to another node (setTexture, setLight, etc)
  • Add / remove action replaced with addRemove action.
  • Undo / redo should now reconnect the removed node to its parent and restore any other relationships it had with other nodes.
  • Added basic Bullet classes.
  • Added basic texture node.
  • Scene now has a root node which houses render and all non-NodePath nodes.
  • Cleanup to sceneParser.
  • Added ctrl+drag&drop functionality to the outliner to set connections.
  • Added drag+drop functionality to the property grid to set connection.

I apologise for any bugs in advance. If you bring them up in this thread I’ll do my best to sort them out as quickly as possible. I know there’s still some issues with deleting / restoring nodes and their connections, for example.

JamesR, you’ll want to be using ‘ppython’ from the command line in order to use the python installation that came with Panda (v2.7 for Panda v1.8 ). To my knowledge it doesn’t come with wx (please correct me otherwise). The last time I checked I had issues installing wx for python 2.7 because the panda installation failed to add it to the register. I got around this problem by installing vanilla python 2.7, wx for python 2.7, then adding the appropriate .pth file to panda’s python site-packages folder.

PM me if that doesn’t make sense. :slight_smile:

Hey, great news! I will look on updates this weekend and hope give usefull feedback!

Btw, do you accept some very small addons? I have coded some functions to control camera from menu (you know, Top/Bottom, Left/Rigth etc.). Also have small fixes for FloatSpin, but not sure they are good (trimmed values 2 digits after dot, select all text in a field on focus, deselect on kill focus - now its too hard to edit, need to delete long old value, put new).

Thanks!

Hi crolli,

Yes of course I accept addons! That’s the whole point of open source :wink: Send me your alterations and I’ll integrate them. Thanks for testing, too :slight_smile:

Hi kurohyou!

Just played with updates recently - didnt find issues right now (except it was tricky to find out what I need to drag-drop texture with middle-mouse to object to apply and finding a checkbox NodePath Only on a scene graph :slight_smile: . I didnt checked Bullet things because I am not familiar with it, sorry.

Ok, just found small bug - looks like pressing keys like W/E/R on selection sometimes gives conflicts between gizmos controls and scene graph - scene graph panel try to select object whose name starts with a letter and will cause multiply objects selected in a graph :slight_smile: Same things happens with Light Linker too (it will jump to next object with similar name)… dont think its a feature?

And a camera controls as a plugin… I have tried to shortcut them like in Blender - on Num keys, but looks like panda didnt recognize Num lock status, maybe wx can?

import wx

import pandac.PandaModules as pm
from pandac.PandaModules import Vec2

import p3d
from game.plugins.base import Base

from wxExtra import ActionItem, CustomMenu

ID_CAMERA_TOP_VIEW = wx.NewId()
ID_CAMERA_BOTTOM_VIEW = wx.NewId()
ID_CAMERA_FRONT_VIEW = wx.NewId()
ID_CAMERA_BACK_VIEW = wx.NewId()
ID_CAMERA_RIGHT_VIEW = wx.NewId()
ID_CAMERA_LEFT_VIEW = wx.NewId()

class EditorPlugin( Base ):
        
    def OnInit( self ):
        Base.OnInit( self )
        self.CreateCameraViewMenu()
        
    def CreateCameraViewMenu(self):
        """
        Create camera view menu items
        """
        viewActns = [
            ActionItem('Top', '', self.ChangeCameraView, ID_CAMERA_TOP_VIEW, args=(0, -90)),
            ActionItem('Bottom', '', self.ChangeCameraView, ID_CAMERA_BOTTOM_VIEW, args=(0, 90)),
            ActionItem('Left', '', self.ChangeCameraView, ID_CAMERA_LEFT_VIEW, args=(-90, 0)),
            ActionItem('Right', '', self.ChangeCameraView, ID_CAMERA_RIGHT_VIEW, args=(90, 0)),
            ActionItem('Front', '', self.ChangeCameraView, ID_CAMERA_FRONT_VIEW, args=(0, 0)),
            ActionItem('Back', '', self.ChangeCameraView, ID_CAMERA_BACK_VIEW, args=(-180, 0)),
        ]
        mCameras = CustomMenu()
        mCameras.AppendActionItems(viewActns, self.ui)

        # Append to view menu
        self.ui.mView.AppendSeparator()
        self.ui.mView.AppendSubMenu(mCameras, '&Camera')

    def ChangeCameraView(self, evt, yaw_pitch):
        """
        Orbit camera top or bottom by manipulating delta values
        See p3d.camera.Orbit for more
        """
        delta = Vec2(-base.edCamera.getH() + yaw_pitch[0], -base.edCamera.getP() + yaw_pitch[1])
        base.edCamera.Orbit(delta)

Happy coding!

I’ve committed a few more changes recently:

  • Added support for native Panda collision nodes.
  • Added basic support for turning NodePaths into actors and attaching animations to them.
  • Added the beginnings of a samples project.

Finally, I’ve added my stuff for attaching scripts to objects. This is still really, really pre-alpha stuff, so please don’t expect it to work straight away. I’m still figuring out exactly how I want this stuff to work but it’s there if you want a peek.

The PandaObject plugin allows you to middle-mouse drag and drop scripts onto NodePaths in your scene. It’s designed to give that “play in editor” functionality that Unity has. It’s still far from being useful, but hopefully the potential is there.

Open samples/maps/sine.xml and you’ll see a box. Press the play button on the playback toolbar and you should see the box bob up and down. Press the pause button to stop. The script is located in samples/scripts and can be middle-mouse dropped onto other NodePaths in the scene. The amplitude of the animation can be edited in the properties panel when you select the NodePath.

Please let me know your thoughts!

@Crolli - sorry for not replying earlier mate! I’ll try to integrate your camera stuff soon and have a look at sub-modelRoot nodes. :slight_smile:

EDIT: Crolli - I’ve just added functionality to select and edit NodePaths which are children of a model root. Their xforms, properties and connections should now all be saved and restored as you would expect. Hold ctrl which selecting and you should be able to select a child node from the viewport. Let me know how robust it is :slight_smile:

Oh, and I’ve integrated your camera stuff into the main app too. Cheers for that!

Wow, great, new features!
Now just very fast feedback on first try:

  1. found hardcoded path to temp.xml in Users/Jamie; after change to local user I got animation playing (script is attached to box by hand)
  2. After stopping animation I got
Traceback (most recent call last):
  File "D:\1x\panda3d-editor\src\userPlugins\pandaObjects\editorPlugin\editorPlugin.py", line 123, in OnPause
    self.app.scene.Load( filePath=TEMP_SCENE_PATH )
  File "D:\1x\panda3d-editor\src\pandaEditor\editor\scene.py", line 36, in Load
    base.game.scnParser.Load( self.rootNp, filePath )
  File "D:\1x\panda3d-editor\src\pandaEditor\game\sceneParser.py", line 35, in Load
    self.LoadComponent( sRootElem, None )
  File "D:\1x\panda3d-editor\src\pandaEditor\game\sceneParser.py", line 65, in LoadComponent
    self.LoadComponent( cElem, comp )
  File "D:\1x\panda3d-editor\src\pandaEditor\game\sceneParser.py", line 65, in LoadComponent
    self.LoadComponent( cElem, comp )
  File "D:\1x\panda3d-editor\src\pandaEditor\game\sceneParser.py", line 65, in LoadComponent
    self.LoadComponent( cElem, comp )
  File "D:\1x\panda3d-editor\src\pandaEditor\game\sceneParser.py", line 46, in LoadComponent
    comp = wrpr.Create( **args )
TypeError: Create() got an unexpected keyword argument 'parent'
  1. Same exception throws when trying to load scene sine.xml, so I can only attach script by hand

Ability attach scripts is excellent, just need to find out how to write own scripts :slight_smile:

Seems I have some problems with childs or I dont understand something. Looks like scene grapth has additional nodepaths which is not present in original file. I think it can be a wrappers around childs, but I dont now, can you confirm?
Look at , selected node is not in egg file, it has floor and env just one level up under model root.
I noticed this while playing with Ctrl-click on another scene which has lots of entries some having { Polyset keep descend } tag set. And I think Ctrl-click selects collision nodes and not their parents… or its a same issue and its a wrappers shown in the scene graph? But if I change xforms of node selected with ctrl-click - it doesnt move; if I select immediate parent when xforms changes are applied.

Or I am totally lost?

Whoops! Rookie error there. I’ve replaced the path with tempfile.gettempdir() so hopefully this should work cross-platform.

Yep, sorry. This should be fixed now. PM me if it’s not.

This could well be the case, but I don’t think it’s anything I’m doing. What you see in the scene graph panel should accurately reflect the structure of your scene, except when using proxies (or helpers) for invisible NodePaths (like lights). I think some exporters might also put a ground plane in during export, but I can’t confirm this. My export pipeline is currently broken so I was only testing on the default environment.egg which ships with panda, so I might have gotten something wrong. It should select the NodePath immediately under the mouse however.

If none of this is making sense, you could send me your model and I could try to repro it over here!

Hello

Yeah, fixed, thnx.

Sorry, you are right, its a not a editor bug, but am still in difficulties, so some muddled thoughts on this issue.

As I understand, scene graph displays all chid nodes - including geom nodes, collision nodes etc? Because of this when loading your box model there are 2 items in scene graph - model root and geom, right? And when I click i select model root and with ctrl-click I select geom node.
Adding some indication to node type in a scene graph can help, since it can be hard to end user to understand which node it should select to move model for example (and user can change both model root and geom positions independently!).

Lets look deeper - envirnoment.egg.pz from panda models have very clean structure, with model root and lots of geoms under it so its appear clean and cool in graph + no problems with selection.

But try to import world model from Roaming ralph sample, it have more complex structure with usage of { Polyset keep descend } - and I think such hierarchy more used in real world.

You will see lots of non-named pandanodes in a graph which consists of named collision node and one or several geom nodes.
First of all its totaly misleading because names are on collision nodes and not on their parent as I supposed to see(I think its a bug in panda but it can be a feature, I will try find more on this).
And second case - ctrl-click will select collision node, not a geom or parent - just because its a colnode :slight_smile:
But I think desired behaviour will be to select parent of colnode.
Hehe, ctrl-click - colnode selected, move colnode z-up, again ctrl-click on the same place - actual geom node selected this time because colnode moved.

I will try to summarize things I have problems with:

  1. In some cases its not desirable to work with deep childs such as geom nodes or colnodes because changing them can be “dangerous” - moving colnode or geom instead of a parent node can give unexpected results later.
  2. But in cases like environment.egg.pz working with geoms is a only way to change something in a scene; how to distinguish these cases? I dont see any way to do it…
    Should such models be considered “invalid”? Or vica versa?

Ouh, hope you will drop a light on my head :slight_smile:

I think this issue is going to be harder to solve than I had anticipated. I’ve been able to reproduce the issues you describe with the roaming ralph terrain and have come to a few conclusions.

The terrain has a lot of nodes at the same level in the hierarchy with the same name (ie an empty string). This is completely incompatible with the way I’m saving out sub-modelroot edits currently as I do it based on name matching. I never run into this issue with the models I use because I export them from Maya, and Maya requires all nodes to have unique names. So the question remains - how do we distinguish one node from another if the names are not unique? I can’t think of a way that doesn’t break as soon as an artist updates and reexports the asset.

As for assuming the user wants the parent of a node which was made a collision node with “polyset keep decend”, I will need to do some more tests. If the flag is query-able once it has been loaded with loader.loadModel then we could make the assumption the user wants the parent of the currently selected node. I’m still unclear on how “polyset keep descend” changes the hierarchy. I need to know if the model was authored with empty strings for node names or if that is a by-product of using the tag.

There is another possible solution. I’ve got rudimentary support for the creation of custom nodes as children of a model root. When a model root is created, the editor recurses through the hierarchy looking for nodes tagged with ‘type’. If the value of that tag is found in the dict of component wrappers then the editor will attempt to create that component using the node as an argument. This means it would be possible to author nodes in your modelling package and have them turned into collision nodes, bullet shape nodes, textures, anything really. You could probably get more fine-tuned control writing your own wrapper and tagging nodes in this way instead of using the egg syntax.

You could check if the name of a node is “” and assign it a name like "unnamed object "+continuous_id.