2007-07-04

Undo

I implemented reparenting and renaming since last time.
Reparenting was rather annoying as I had to write my own function to recursively copy the node and its children to the new location. But it seems to work smoothly and it's the first "cool" feature of the editor.

I spent the beginning of this week working on the action system (aka undo system).
Here's how it works.

Actions derive from iAction but have their own Initialize function taking their needed parameters.

How to create an action and use it:

Create a new instance of your action (new SomeAction(), or through scfCreateInstance, more on that later)

Query for the action's interface (iSomeAction),

Call someAction->Initialize (params),

Call actionmgr->Do (someAction);

Plugins can implement their own actions. To allow something to instantiate an action implemented in another module, the class should be added to the plugin's metadata. Then it should call scfCreateInstance<Interface> ("crystalspace.editor.action.someaction").

As it stands, the action system should be fully functional, including the undo/redo menu items. Reparenting is not undoable yet though.

One current problem is that when you rename something and then undo it, the tree view doesn't update to reflect the old name. I think the best method to get it to update is to have the tree view listen for each iEditorObject's name change. That is to say, I should add a general iEditorObjectChangeListener, that will signal when an object has changed so that interested views can update themselves. This applies to things like properties too, which will come later.

Future thoughts:

I'm going to implement a drawer which will draw a bounding box around the current selection in the 3D view as well as little billboard icons for lights (and later, camera positions). Ideally this will have a settings page where you can toggle whether to draw this stuff, or tweak the bbox colors. Settings is another thing I haven't gotten to, although it is not essential yet.

I'm still thinking about the best way to implement tools in general, and in particular the transform tools, as these are core tools that will be used all the time.

Other random Q&A's with myself
Should object select tool be undoable? No, would get quite annoying.
Should selecting something in scene browser select it in the 3d view (e.g. actually select it)? This seems like a good idea.

A little reflection:
GUI work has been interesting. I'm glad I got the chance to do it. In the GUI world, the little things count. Like the status bar/progress bar, or the edit menu telling you what action you're going to undo or redo, or dragging the nodes around in the scene browser to reparent them, or being able to customize your panel layout due to the flexible wxAUI. I've been trying to implement things in a way that is as intuitive as possible. I hope I can continue to find ideas to make things nicer.

2007-06-15

Scene Browser Beginnings

The scene browser shows all of the sectors in flarge.

Here's how it works:
A plugin implementing wrappers around all of the (useful) CS interfaces is loaded. It registers interface wrapper factories with an iInterfaceWrapperManager. This manager keeps a hash of scfInterfaceID's mapped to iInterfaceWrapperFactory's.
Another plugin listens for when the map is loaded (which the editor tells it), then creates an EditorObject for all of the sectors in the engine and adds it to the editor's object list.

EditorObject, using SCF metadata, goes through each interface implemented by the object it's wrapping and requests the iInterfaceWrapperFactory for the given interface. If there's no wrapper for that interface, it ignores it. It then instantiates that wrapper and pushes it onto an array. While it's going through, it finds an interface wrapper that has a name attribute, and stores that for future reference, when something wants to call Get/SetName. It does the same thing for a parent attribute. It also figures out the type (instance, factory, or unknown) and stores that.

After adding an object to the object list, the scene browser panel will get an Object Added event, and it will put the given object into the tree, using EditorObject to get the name and parent (for the hierarchy).

I haven't implemented putting the objects into the correct hierarchy in the Scene Browser panel yet, nor have I implemented object name editing or re-parenting via dragging. Also, scene browser should have a way of showing an icon and grouping (or sorting) the different objects.

Right now, I've only implemented an interface wrapper around iObject, since that provides the name for every engine object. But eventually, I'll need wrappers for every useful object, since I will need to know their properties for the property editor.

This was partly just a proof-of-concept right now. There are a few functions that could have a better place at the moment.

Tomorrow:

Add the rest of objects from engine on map load (other than sectors)
Clean up some of the nasty parts
Respect object hierarchy in scene browser
Implement name editing in scene browser
Implement re-parenting

2007-06-08

Statusbar ramblings

Well that took somewhat longer than expected, but I ticked off everything from my previous list except for starting on the SceneBrowserPanel. The File->Open only opens flarge right now, since I've yet to make an open dialog.

I also implemented a custom statusbar which has a progress bar. iEngine::Prepare takes a iProgressMeter* as a parameter, so I gave it an implementation which updates the statusbar with the description and the progress on the Prepare. Prepare reports on lighting. Unfortunately, engine lighting will soon be removed, but I still believe that the iProgressMeter implementation will be useful. After all, it is generic enough to be used in other parts of CS, for example the loader/saver, or even internally in the editor, such as for terrain generation (if someone implements a plugin to do this). I have yet to expose the statusbar in the iEditor interface, but I think this would be a good idea in some form, so that plugins can keep the user abreast of what is going on. Whether they need to show the progress gauge or not, they can still benefit from showing descriptive status text.

Other than that, I'm getting a segmentation fault at program exit, so I'll have to investigate this a bit.

2007-06-06

Progress Update

Today I got the plugin loading and CS initialization code working. So I implemented the CS 3D view panel in a new plugin which is loaded by searching for any plugins under the 'crystalspace.editor.plugin.' hierarchy.

I didn't have time to make the map loading menu item yet, so I just had it load flarge to test the view functionality.

2007-06-05

Starting to code

So I actually got started implementing the editor the past few days. The current state is as follows.

But you wouldn't know from the screenshot that the panel manager and the panels themselves are potentially pluggable. I say potentially, because I haven't written the CS initialization code yet, including plugin loading of iPanels. Although, I don't expect to ever make the panel manager into a plugin.

In fact, most things in the editor will be pluggable, meaning you could implement them in a plugin. But to avoid making a plugin implementation for each single SCF interface in the editor, I'm going to include all of the core managers in the editor executable itself. But the core panels, tools, actions, and CS objects will be implemented in a single plugin to serve as an example and also to use consistent code for instantiating these components.

thebolt made a nice addition to SCF recently to provide access to interface metadata. This should make getting all of the interfaces that an object has so much faster since it means I don't have to iterate over all possible interfaces. Instead, I can keep a hash table of interface names mapped to iInterfaceWrapperFactory's. So to instantiate the correct iInterfaceWrapper objects for a particular iBase* object, I can simply perform lookups in the table. Thanks Marten.

Panels now also specify a default dock position so that they are laid out in an appropriate way. You can always move the panes around though (except for center pane). It will be nice if I have a menu for saving perspectives, that is, view configurations. But I won't worry about that yet.

2007-05-29

Editor Object Abstraction

Since my last post, I realized how important SCF is in Crystal Space and how it could help me implement the property editor, tools, scene browser, and asset browser in a way that remains extensible.

In my last post, I talked about how I will implement the property editor. I'll now talk about how to do the rest of the major parts.

When something in the editor needs to know about what interfaces an object implements, it must query each possible SCF interface until it finds a match. When you have all of the CS interfaces, all of the CEL interfaces, and user-defined interfaces represented, there is a lot of querying going on.

Over the weekend, I spent some time to come up with a class EditorObject which abstracts each iBase* object in the editor. This class will keep a list of the interfaces which an object implements. Since I need to do a lot of SCF interface querying, and querying isn't particularly efficient, this cache should boost performance. More importantly, it provides an abstraction of an editor object--no matter what kind of interfaces the object implements, there should be a consistent way of getting/setting the name, parent, and getting the type (factory, instance, or other) and properties.

For each SCF interface, there will be an implementation of iInterfaceWrapperFactory. This will create instances of iInterfaceWrapper, only if the passed iBase* implements the particular interface which the iInterfaceWrapperFactory wraps (it determines this using QueryInterface). Each iInterfaceWrapper implementation will keep a pointer to the queried interface to carry out requests for the Name, Parent, Properties, etc.

Upon construction with an iBase* object, the EditorObject calls each registered iInterfaceWrapperFactory on the object, and stores the resulting iInterfaceWrappers in a list. It stores iInterfaceWrappers whose iInterfaceWrapperFactory::HasNameAttribute() and HasParentAttribute(), respectively, return true, separately, but in addition to the aforementioned list. It does this so it can provide constant time access to name and parent attributes. I can't think of a case at the moment in which one object has at least two interfaces which provide a Name attribute, but if this becomes a problem, certain interfaces could be given a higher priority than others. Even in this case, the other Name attribute can still be edited in the property editor.

How does the Scene Browser and Asset Browser know which objects to display? EditorObject asks each iInterfaceWrapperFactory for the type of the object implementing the interface. By type, I mean instance or factory. For those interfaces which do not help identify whether an object is an instance or a factory, they return unknown type. The EditorObject then stores the resulting type. Ideally, all interfaces return unknown, but one which returns either instance or factory. If there is a conflict where at least one interface says it is an instance type and at least one says it is a factory type, then the logical solution would be to set the resulting EditorObject type to unknown. In this case, both the Scene Browser and the Asset Browser could show it. This isn't as uncommon as it might sound, although I can't think of an example.

The Property Editor will also need to know about the properties from each interface. Here EditorObject will ask each interface for a list of properties. I still need to come up with a property representation which these GetProperties functions use. I don't think it would be a good idea to have the interface wrappers tied to wxPropertyGrid.

Instead of showing the interface name as the category in the property editor, there will be a name based on logical groupings of properties, like "Surface", for properties related to a surface. The idea is that there is a better grouping of properties for editing than the groupings dictated by the interfaces, which were designed to be good groupings for runtime. To implement this, each property will specifie under which category that property should be displayed. Ideally, these category names would be standardized. For the advanced user, the underlying interface name can be shown in the property description, and optionally, the properties could be grouped by interface.

Finally, Tools are interested in what interfaces an object has so that they can determine whether they are available. They can use EditorObject::HasInterface to query for interfaces using cached results.

On a more practical note...
My mentor kindly created a branch for me in https://crystalspace3d.org/svnroot/crystal/CSExtra/branches/soc/editor/
I've gotten both wxAUI and wxPropertyGrid samples to compile from within the CSExtra source tree, by including wxPropertyGrid sources in the tree and assuming wxAUI is built into wxWidgets. I should require wxWidgets 2.8+ since only that has wxAUI, but I haven't messed with the standard configure check yet. A more elegant way would be to make an additional check for wxAUI. In the case that it isn't present, I could have the PanelManager use some sort of static layout with splitters, but this isn't a priority.

In my next post, I'll talk a bit about registering objects with the editor and selections.

I hope to really iron out the design during this week so I can get a good start on the code. I'll try to come up with a schedule tomorrow. Signing out.

2007-05-24

Class Diagram and some Q&A with myself

I decided to start off by trying to make a class diagram of the editor. This allows me visualize the system and locate problem areas in the design.

There are a few issues that I've identified right now:

What type do I use for the selections?

Since I'm allowing heterogeneous selections, I need an array which can hold basically any CS engine object, including factories. I'm thinking a csWeakRefArray of iBase will work. It should use weak references since, we don't want to keep objects around if they deleted. Perhaps I need to call csWeakRefArray::Compact() before any function dealing with the selection, so I don't need to deal with invalid objects.

How do I get the property editor for the selected objects?

Each SCF interface will have its own property category, and each of these categories will be registered with the main property editor. To display the appropriate property editor for the selection, for each object, I should query each possible SCF interface until I find a match. The downside of this is that it has to test each and every interface for each object.

This should allow you to edit objects of different types as long as they share some common SCF interface. The property editor will only show the interfaces in common. This will also reduce the amount of querying being done, since if the first object doesn't have interface X, we don't have to check if the next object has it.

How do I register the tools with the toolbox?

When a tool will registers itself with the tool manager, the tool manager should publish a ToolAdded event to its listeners. Among the listeners will be the ToolboxPanel. This should solve that.

How to draw special stuff like the selection, manipulators for the move, rotate, and scale tools, or billboard icons for lights so you can select them visually?

These should be handled in different classes.

The CS view should draw the selection bounding box and the billboards for lights.
The move, rotate, and scale tools should draw the manipulators.

Where to use SCF interfaces in the code?

I think one of the annoyances of CStudio was that everything was an interface and you had to put up with a lot of SCF boilerplate and interface querying to do anything. That said, I think that parts of the editor which are meant to be implemented by plugins should use SCF so that I can leave the dirty work of plugin loading to SCF. So, you'll probably end up seeing iTool, iPanel, and iAction. I'll have to read up more on SCF and talk to my mentor to make sure this is what I want.

Still, the diagram is missing some detail, e.g. tool and property category registration, various events/listeners, settings manager, and many tools/actions are not shown.

I will try to come up with a more complete diagram tomorrow and then I'll try to sort out any further difficulties. Hopefully I can get my hands on some code soon. In the meantime, to entertain my thirst for action rather than abstract thinking, I will try to set up the build system to work with wxAUI and wxPropertyGrid.

2007-04-30

Introduction and First Thoughts

Introduction

Hi! I was accepted to build a Crystal Space World Editor for Google Summer of Code this summer.

A little about me: I'm an 18-year-old computer engineering major at University of Maryland. My main interests beside computers are classical guitar and biking.

In this blog I'll post my ideas and progress on this project. Comments and suggestions will be greatly appreciated.

Here is the abstract as given in the proposal:

Crystal Space, being a 3d engine, deserves a good art pipeline so that applications using it can focus on creating assets, not editing world files by hand. Having worked on several game projects using CS over the past 2 years, I have experienced directly the difficulty of getting art into the engine. I would like to help take a huge step in improving it by creating a usable world editor.

The editor should have a completed framework by the end of the work period, including a working plugin system, undo/redo system, selection system, tool system, save/load functionality, scene browser, factory browser, and property editor. Also, basic selection and transform tools will be implemented. Any remaining time will be used to polish the user interface or implement tools for other CS features, such as terrain.

The project is currently in the design stage, but the ideas are becoming more focused.
Here is some of the progress so far.

Name of the editor

We had nearly an hour-long name-throwing session on IRC about this, but I don't think any of them stuck. The consensus seems to be that the name shouldn't be too serious. Some highlights: Ochre, Composotron IV, Crystal Latte, Crystachino, Deity for Dummies, Quartz, Chainspoon, Turbospoon, CrystalGarden, Plow, Colloid Space. I would choose Anvil, but it is already the name of my old editor. Maybe Anvil Reloaded? ;) Any more ideas?

User interface

The editor will use wxWidgets as a GUI library.
I'll use wxAUI and wxPropertyGrid widgets. wxAUI is a really nice docking toolkit that should give a very flexible, modern-looking interface. wxPropertyGrid will be great for the property editor.

wxAUI has been included in wxWidgets 2.8.0+, but wxPropertyGrid is a contrib widget. To handle the dependency, the editor will probably require wxWidgets 2.8.0+ and wxPropertyGrid will be included in the source tree.

I'll use wxFormBuilder to design the more complex dialogs for the editor (options, material editor, etc) and save them as XRCs. The main window will be done in code, using wxAUI.

Tools

Tools allow the interface to remain consistent when switching between different editing functions. Tools will handle events like mouse move, mouse click, keyboard press, and draw to allow for extra information to be drawn on top of the scene. Examples of tools are select, move, rotate, scale. Plugins can also add tools. Tools will be available through a toolbar. In addition, tools can provide a panel for tool-specific settings.

There are several types of tools.

Mesh-specific tools: These tools will be shown based on the type of the selected object.
e.g. for particle system editor, an emitter editor. For a terrain editor, a tool to modify heights with a brush.

Creation tools: Tools which create things, e.g. a tool which creates mesh primitives, or a tool which creates an initial flat terrain.

Scene Browser

The scene browser is basically a tree that will show all of the instances in the world file (e.g. all the stuff that goes in between <sector> tags).

There could possibly be multiple "view modes" for this: Group by sector, group by object type, sort alphabetically.

Plugins should be able to add 3rd party types to the scene browser (e.g. CEL entities).

Project Manager

This should show some of the files that the world file depends on. It will allow you to link or unlink libraries to the world file.

Assets Browser

Formerly named "factory browser", the assets browser will allow you to browse through mesh factories, materials, etc. It should allow you to remove them, or edit their properties when selected (in property editor). Adding mesh factories can be accomplished through importing or linking to a library. Or you can use a tool which creates primitives.

Mesh Browser

This will show all of the available mesh factories, rendered to textures. The selected one will show a rotating preview.

Material Browser

This will show all of the available materials rendered on tiny spheres. You should also be able to create an empty material from here.

Browsers

With all of these browsers, there seems to be a commonality between them: they generally provide a view of a certain category of objects, and when selected, they should publish an event to listeners, including the property editor which will allow you to edit the selected object. It seems like they could share a lot of code.

Property Editor

There will be a property editor shown for the selected object.

I will need to implement some custom property types, including one for matrices.

Another thing I would like is a "link" to another object. It would basically allow you to choose one of the available objects of that type. And it will allow you to follow the link and show the linked-to object in the property editor. For example, the mesh instance property editor will have a link property "Material", which will allow you to choose which material is used for the mesh.

Saver Improvements

Since the saver doesn't fully support saving all CS objects, and since it is desirable to preserve comments in the world files, I'll be supplementing the loader to attach the original DOM nodes to engine objects when they are loaded (and only when the saveable flag is on). The editor will then keep track of which objects are modified, and only save those. This means that all unsupported nodes will be preserved. A main failure of the current way of using the saver is that it obliterates any unsupported nodes. Judging by the rate at which the loader advances versus the rate at which the saver plugins advance, this will be necessary for some time.

The sooner this is implemented, the sooner the editor will be useful for making small tweaks to existing world files. So I do regard it as quite important, although it is not trivial.

Wrap-Up

Whew! There are a lot of ideas there. I hope I get some good feedback on this.

I still have approximately 2 more weeks of school, so I'll be trying to think of this and post when I'm not taking exams. Then, summer! :)