Yep. Another scene editor...

Hi Hapdrop,

I’m aware of some issues with the editor running on Linux. I thought the last commit I made could have fixed these; I guess not :S

If you have the chance, can you run this and tell me what happens? You should just get a grey window titled “wxWindow”.

from panda3d.core import loadPrcFileData
loadPrcFileData( 'startup', 'window-type none' )

import wx

from pandac.PandaModules import WindowProperties
from direct.showbase.ShowBase import ShowBase
from direct.showbase.DirectObject import DirectObject


class Viewport( wx.Panel ):
    
    def __init__( self, *args, **kwargs ):
        """
        Initialise the wx panel. You must complete the other part of the
        init process by calling Initialize() once the wx-window has been
        built.
        """
        wx.Panel.__init__( self, *args, **kwargs )
        
        self._win = None
        
    def GetWindow( self ):
        return self._win
        
    def SetWindow( self, win ):
        self._win = win
        
    def Initialize( self, useMainWin=True ):
        """
        The panda3d window must be put into the wx-window after it has been
        shown, or it will not size correctly.
        """
        assert self.GetHandle() != 0
        wp = WindowProperties()
        wp.setOrigin( 0, 0 )
        wp.setSize( self.ClientSize.GetWidth(), self.ClientSize.GetHeight() )
        wp.setParentWindow( self.GetHandle() )
        if self._win is None:
            if useMainWin:
                base.openDefaultWindow( props=wp, gsg=None )
                self._win = base.win
            else:
                self._win = base.openWindow( props=wp, makeCamera=0 )
        self.Bind( wx.EVT_SIZE, self.OnResize )
        
    def OnResize( self, event ):
        """When the wx-panel is resized, fit the panda3d window into it."""
        frame_size = event.GetSize()
        wp = WindowProperties()
        wp.setOrigin( 0, 0 )
        wp.setSize( frame_size.GetWidth(), frame_size.GetHeight() )
        self._win.requestProperties( wp )
        

class App( wx.App, DirectObject ):
    
    """
    Don't forget to bind your frame's wx.EVT_CLOSE event to the app's
    self.Quit method, or the application will not close properly.
    """
        
    def ReplaceEventLoop( self ):
        self.evtLoop = wx.EventLoop()
        self.oldLoop = wx.EventLoop.GetActive()
        wx.EventLoop.SetActive( self.evtLoop )
        taskMgr.add( self.WxStep, 'evtLoopTask' )
        self.WxStep()
        
    def OnDestroy( self, event=None ):
        self.WxStep()
        wx.EventLoop.SetActive( self.oldLoop )
        
    def Quit( self, event=None ):
        self.OnDestroy( event )
        try:
            base
        except NameError:
            sys.exit()
        base.userExit()
        
    def WxStep( self, task=None ):
        while self.evtLoop.Pending():
            self.evtLoop.Dispatch()
        self.ProcessIdle()
        if task != None: 
            return task.cont
        

class MainFrame( wx.Frame ):
    
    """Panda Editor user interface."""

    def __init__( self, *args, **kwargs ):
        wx.Frame.__init__( self, *args, **kwargs )
        
        self.vp = Viewport( self )
        self.SetTitle( 'wxWindow' )
        self.Bind( wx.EVT_CLOSE, self.OnClose )
        
        self.bs1 = wx.BoxSizer( wx.VERTICAL )
        self.bs1.Add( self.vp, 1, wx.EXPAND )
        self.SetSizer( self.bs1 )
        
    def OnClose( self, evt ):
        self.Show( False )
        wx.GetApp().Quit()
    

class App( App ):
    
    """Base editor class."""
    
    def OnInit( self ):
        self.frame = MainFrame( None, size=(800, 600) )
        ShowBase()
        self.ReplaceEventLoop()
        self.frame.Show()
        wx.CallAfter( self.FinishInit )
        
        return True
    
    def FinishInit( self ):
        self.frame.vp.Initialize()
        

app = App( redirect=False )
run()

I ran it, and a grey window popped up. The output in the terminal was this:

Known pipe types:
glxGraphicsPipe
(all display modules loaded.)

That’s odd. The snippet and the editor should be doing essentially the same thing as far as windows are concerned. Are you sure you’re using the latest code from github?

Yeah, I’m using the latest from the github.

I’m using Ubuntu 11.10, if it helps any? Does the version of wxpython I’m running matter? I’m using 2.8.11.0

It shouldn’t really matter if the snippet worked. I’ll have to drill down a bit and see what’s happening. If any Linux users can offer some advice, that would be greatly appreciated!

Hi, just saw your message :slight_smile:

Lighting work for me, great! Only very small issue - when I change light relations on saved scene, editor doesnt mark scene as changed and to save I need to modify something in scene (any param just to make sure editor noticed changes).

I will try to summarize list of props in days or two, ok?

Fixed. Changing light relationships should also be supported by undo as well.

No worries, just let me know :slight_smile:

Fixed. All lights should now how icons in the viewport.

EDIT:I’ve just commited a change that should fix the editor on Linux (maybe I’m talking too soon) but if anyone wants to give it a spin again, I would really appreciate it. I’ve removed the tabs on the viewports - you can switch from game to editor view using the new toolbar. Apparently using the notebook widget doesn’t play well with panda viewports.

Hi kurohyou!
Sorry, last week have almost no time to think :slight_smile:

So, what I think on textures in editor.

  1. some control to pickup texture files and assign to nodes. Am not sure how to organize it, may be special folder for textures scanned on start and then user allowed to select from found files for texture?
    But this require either to scan egg file for texture path or model author to place textures in that folder,
    update it in 3d editor - long and tiring way.
    May be file picker, but some internal storage may be required to reuse textures.
  2. Texture wrap modes - looks like 2 select controls with Texture.WMRepeat/Texture.WMClamp etc. It must operate on nodepath’s assigned texture object.
  3. Texture Filter Types - again, 2 select controls for minification/magnification.
  4. Texture modes - looks like select control with modulate/replace/normal/gloss/glow etc. These operates on texture stages object and not on textures directly.
  5. setShaderAuto switcher for node an be helpfull

Am not sure how this can be handled inside. You use Attribute class to tie properties of nodepath to their view representation via property getter/setter and type. Sure, setting individual properties of texture can be done with it as nodepath expose required accessors; but unlike position/hpr/color texture props are logically tied together and better to be operated via one property or wrapper internally working with all props? Like composite Attribute consisting of another Attribute’s objects?

I think textures need some special handling also because nodes can have more than one texture applied, while Attribute class works on single property!
Will be very glad to hear what you think on how to deal with them internally.

Hehe no worries mate, I totally understand :slight_smile:

Sound like we need to sort through the dependencies of files imported to the project and bring those in too, so that all assets used are relative to the project.

This should be simple enough as it’s just a method on NodePath.

The texture mode stuff - this is all Attrib data somehow plugged into NodePath? Or do I have to do something like NodePath.getTexture( n ).getAttrib( Texture ).setWrapMode()?

It shouldn’t be too hard to expose this sort of thing either, I’ve done the light linking stuff and that uses LightAttrib. Maybe the user selects a texture they want to edit and it opens another tab in the property grid?

On an unrelated note, I’ve just submitted the beginnings of a primitives plugin. If currently only has a box and a cone, but if you want to add your own custom nodes this would be a good place to learn how to go about it.

First of all, let me say that this is a very interesting project - I very much hope that it works out, and is kept up-to-date, whether by you or others, as it could be quite a boon to Panda development (not to mention the uptake of Panda for development)! :slight_smile:

Now, to the point of my post, which is a suggestion for a feature: a scene-graph viewer. The tool would essentially walk over the scene graph and then place in front of the user a visual graph displaying the various NodePaths and their relationships to each other. Such a viewer might be useful in seeing how one’s scene is laid out, and thus dealing with any issues stemming from this layout.

If feasible, it might also be advantageous to allow the user to edit the attributes of those nodes as though they were those shown in the main view, dragging-and-dropping textures, etc.

Finally, on a related note, it might be useful to have some means of seeing in the main view what children or parents a NodePath has, either by a small popup or perhaps temporary connecting lines within the scene.

Hi Thaumaturge, thanks for your interest in the project!

In regards to your query, I have implemented a tree control which does pretty much what you describe. You can see the hierarchy and expand / collapse branches of it. You can even change the names of nodes or reparent nodes by middle-mouse dragging them.

Was this what you were after?

Ah, wonderful, that sounds like just the thing! :slight_smile:

My apologies for having brought up a feature that you already have - I briefly checked the thread, as I recall, but haven’t tried the editor itself (I’ve already put together a (buggy and inefficient ^^; ) editor for my current project, for one thing). ^^;

I dont know about texture attrib; docs said TextureAttrib links nodepath and stage/texture but I dont know how to use it.

We can use nodepath texture stages, some sample code:

	# get first texture stage from model
	ts=self.actor.findAllTextureStages()[0]
	# get texture binded to stage
	def_tex=self.actor.findTexture(ts)
	print ts, def_tex
	# add texture as normal map
	tex_normal=loader.loadTexture("maps/noise.rgb")
	# create texture stage
        ts_normal = TextureStage('ts2')
        ts_normal.setMode(TextureStage.MNormal)
	self.actor.setTexture(ts_normal, tex_normal)
	# replace default modulate with another one
	tex2=loader.loadTexture("maps/smiley.rgb")
	self.actor.setTexture(ts, tex2, 1)

So, in general first we need to grab all texture stages, find out which is currently changed by user, then get it with nodepath.findAllTextureStages() or nodepath.findTextureStage(texture_stage_name). Stage index or name can be gathered on model load I think.
Then you receive texture tex=self.actor.findTexture(ts) and set texture params like wrapping.

I think same can be achieved with TextureAttrib: TextureAttrib.getOnStages() to get all stages and then TextureAttrib.getOnTexture(stage) but I need to experiment with it.

Yes, I mean something like another tab to work with texture params so they will be grouped. So, user selects part of model, editor grab textures applied to this part, show list of textures (in select control for example), select and show all controls for this texture? Looks good for me :slight_smile:

Thanks for updates, will look into them. Hope I can understand how plugins works and code something too :slight_smile:

Note that nodePath.findAllTextureStages() is not the same thing as nodePath.getAttrib(TextureAttrib).getOnStages(). The former searches all nodes at nodePath and below; the latter returns the TextureAttribs applied directly to nodePath only.

All state operations that you can perform via NodePath methods are actually manipulating various attrib objects on the underlying PandaNode. The TextureAttrib class is the low-level implementation of the setTexture() convenience functions provided on NodePath.

David

Thanks drwr! Now I think TextureAttrib is what we need. I have played with samples from forum and get it working - this code will change frowney default texture to smiley and add normal map:

        for np in model.findAllMatches('**/+GeomNode'): 
            gn = np.node() 
            for gi in range(gn.getNumGeoms()): 
                state = gn.getGeomState(gi) 
                ta = state.getAttrib(TextureAttrib.getClassType()) 
                if ta: 
                    print np, ta
                    for si in range(ta.getNumOnStages()): 
                        stage = ta.getOnStage(si)
                        #print "stage", stage
                        tex = ta.getOnTexture(stage) 
                        #print "texture", tex
                        tex2 = loader.loadTexture("maps/smiley.rgb") 
                        ta = ta.addOnStage(stage, tex2)
                        tex_normal=loader.loadTexture("maps/noise.rgb")
                        ts_normal = TextureStage('ts2')
                        ts_normal.setMode(TextureStage.MNormal)
                        ta = ta.addOnStage(ts_normal, tex_normal)
                        #print ta
                    state = state.setAttrib(ta) 
                gn.setGeomState(gi, state) 

By the way, today I have played with Unity3d a little. They separates all material/shader functionality from mesh renderer properties, so materials are edited in its own panel and then material is assigned to mesh.
How about this? Do you think it will worth to separate mesh materials property and texture props into some kind of “uber-material”? This material will consist of TextureAttrib and MaterialAttrib; we can apply them to any mesh in material preview area just by applying these attribs. And this will add material reuse.
Material definition and nodepath-material relation can be stored in xml; on a game level required render attributes will be created and applied to nodes. I will play more with models consisting of several nodes with different textures to see how to work with it.

Looks cool for me :slight_smile:

@David & Crolli:

I’ve been looking at the manual again and think I’ve come up with a solution but I’d like your input. Since texture stages are nodes themselves it seems like they should appear in the scene graph so you can select them and edit their properties. Then you can connect them to NodePaths by either drag and drop or by using the light linker (which will probably end up being a multi-purpose linker). The code I have already should support this without too much refactoring.

Does this sound like the most logical implementation?

I wouldn’t call the TextureStages “nodes”, since in Panda that word specifically refers to a PandaNode or anything that inherits from it, and TextureStages are not that kind of thing.

It is true that they could have a graphical representation in the scene graph, but if you wanted to model the scene graph accurately you would probably draw all of the RenderAttrib objects (including TextureAttribs) as adjuncts hanging off of the various nodes; and the TextureStages would be seen as connected to the TextureAttribs.

Maybe you don’t want to model the scene graph that accurately, though. There’s a tradeoff between power and simplicity that every level editor has to make somewhere.

David

Yes, sounds logical. I am bit unsure about linker because some models I saw have textures applied at separate geom nodes also (for example character I have has one big texture for whole body + head texture applied on head only and head is not exposed as nodepath to panda). This means linker will need to go into geom nodes and work with TextureAttribs (I think setTexture is method of NodePath, not PandaNode which GeomNode instance of). But this can be a plus - TextureAttrib presents whole texture set for geom (diffuse/normal/gloss + texture wrap modes) while TextureStage/Texture presents only a part(TextureAttribhas all stages and textures required)
And I have some problem with them now :frowning:

I have forked your code to play with my idea of material plugin, can you take a look on github.com/is-blackhole/panda3d-editor? It can preview node “materials” (gathered through TextureAttrib). Best is looked with abstractroom.egg from panda Bump-Mapping sample. Others must work too, but can results in a duplicates in material list - thats there my troubles started.

Also, I have a request for you. I have used another Viewport for preview panel and need to update main frame layout to include it. But looks like plugins dont allowed to add their UI into frame now. As a quick hack I have added a MainFrame.AddPane method, but sure better way exist. Can you add some API for this in future?

And a small bug on desert: If I select model on scene with mouse click, I cant delete it. Selecting model from scene graph will do the job.

Just a quick update from me to anyone following this project. I am indeed still working on it but just took a bit of a time out after the Christmas break. I’m having to look at some major refactoring too which is stopping me from adding new features. This refactor is something I would like to get some opinions on.

Currently the editor only deals with NodePaths which is something I’m in the process of changing. In order to add features whereby the user can create texture stages, Bullet shapes, panda collision primitives and other types of objects the editor needs to be able to manipulate non-NodePath components.

This is what I’ve almost finished implementing but it changes the way the editor works in a critical way. Previously a user could load a scene in their own project and manipulate nodes as they would normally, eg call detachNode() and removeNode() methods and have them work as expected. With the changes I’m making it will be necessary to call detachNode() or removeNode() on the available wrappers instead of the NodePaths themselves as they will need to be dereferenced from the scene (some complex custom nodes may also have additional code to ensure they are cleaned up properly, like python tags with circular references).

I never wanted users to have to learn a new api just to write code for scenes created with this editor but it seems like I don’t have a choice anymore. If anyone has an opinion on this it would be greatly appreciated!

I’m not familiar with scene editors, but maybe checking how other engine scene editors like Unity or UDK do it will help. If they pay the cost and there is no other way, you know what you need to do.