Sunday, April 29, 2012

C# action item plugin for 3ds Max


It has been a while since the last time when I made some good progress on my 3D project. My guess is that I have had a lack of motivation and clear planning. And recently Sam has got a brilliant idea which should resolve most of my issues! We are doing Agile now!

Having a list of goals for a relatively short period is a good motivation for me: I work very well having visible and reasonable deadlines. And dealing with clear and shorter tasks helps to go forward with "baby steps". This is important given that I work on this project in my spare time.

So, here is my plan for this 3 week iteration! It has only one story: Creating a plugin that imports OBJ files into my scene.

You may want to ask - why do I need to create a whole plugin for a task that can be performed by simply clicking the Import menu? Well, due to our work process, I need a more sophisticated import procedure. I want to update the existing objects rather than delete and re-create them. Sam does modeling and I do texturing, and it is important to bring in his changes without deleting any material that are already in the scene.

So, my plugin will work in the following way. It will look up for the existing objects that are about to be updated and will rename them if necessary. Then it will proceed with the import. After the import is done, it will grab all the materials of the old objects and apply them to the new ones. After that, the old objects can be deleted.

This is the long term task. In this iteration I aim for a simpler goal: a simple, straightforward import of OBJ files. OBJ is a standard 3D object file format which we use to exchange files between our applications. I just want to familiarize myself with writing action item plugins and with the file import SDK.
And it is so super-easy to write these plugins with the new .NET SDK! I will show you how :)

First of all, I downloaded the Microsoft Visual C# 2010 Express from here. It is a free tool from Microsoft to create C# applications.

Then I went to the online SDK help to read how to create .NET plugins.

According to the help, I only needed to create an assembly project with a class that would implement the ICuiActionCommand interface. Once the dll is compiled, it should be placed to the bin\assemblies folder. The next time Max is launched, it will dynamically load it and register an instance of the class as an action in its CUI interface. Very simple!

In reality it was too, except that back then, when I started working on this, the online help had a bug. It said: "This is done by creating a public class that implements the interface ICuiActionCommand (found in MaxCustomControls.dll)". But it was not true! MaxCustomControls.dll did not have it! I searched all the help from the beginning to the end, and everywhere it was said the same thing. It was  pretty frustrating! Then I decided to text search my class name in the Max .NET assemblies, since they all have a textual description of the interface. And I found my class! It was hiding in the UiViewModels.dll, the namespace UiViewModels::Actions. UiViewModels was not documented at all; fortunately, this bug is fixed now.

I quickly implemented the action item interface, created a simple "Hello, World!" WPF form... And here is my action in 3dsMax!


I put a button with my action on the toolbar and launched the plugin:



Here is the code behind it (I omitted the window code, since it is trivial):


As you see, the code that launches the window is located in the Execute() function.
That's it! I am done with the action item plugin!

Now I need to write a simple import procedure, and this will be the subject of my next post.
Stay tuned!




Sunday, April 22, 2012

Plans for a cube-filled room

We're back! And this time, we've got a plan.

Given how much my other project has progressed since I began planning it using Agile methods, I would like to apply the same ideas to our 3D project. Iterations! Stories! Minimum viable product! Yes!

The first thing to decide is how long our iterations are going to be. This will give us limits for the scope of our first iteration's deliverable, and guide us in scheduling an appropriate number of stories. We chose 3 weeks. If the evolution of my other project is any indication, this length is bound to change after an iteration or two anyway, so the exact length is not very important.


Next, our Minimum Viable Product (MVP). The long-term plan is to model our entire condo in 3D, but that's far too large a scope. Especially given that Nadya, being a photography enthousiast, is aiming for photorealism. But with a deadline in three weeks, something has got to give! Should we favor quality or quantity?

That's a false dilemma. We aren't experienced enough to expect photorealism in any amount of time, and a 4½ condo contains a surprisingly large number of objects. So we shall cut both quality and quantity!

Did I say "cut"? We shall maim quality, and shred it to pieces. Observe:


A maimed room. Next to the sea of white. Low poly-count, affordable rent.


The objects are actually much more recognizable than I expected. What you see above is extreme minimalism: two boxes per object, no more, no less. Nadya will place my minimalist models at their proper location in her model of the condo, and I will refine them later. That's the reason why a single box is not allowed: that would make it hard for her to orient the models properly, and we might only notice the mistake once the models get refined.

For some models, the restriction was both challenging and insightful. The fridge, for example: its most distinguishing feature is the line which separates the freezer from the fridge proper. But how do I recreate this line with only two boxes? Well, I could make the freezer hover over the rest of the fridge, or I could make the top door slightly thicker or thiner than the bottom door, but neither approach gives satisfying results. Instead, I found another feature to highlight: the space between the door and the floor! I had never noticed that space before, but if it wasn't there, the fridge just wouldn't look right.


No, we don't keep our fridge in the bedroom.
It's for size comparison! Or something.


For the first iteration, we restrict our attention to a fraction of the condo, but there are still dozens of objects to model. In order to create those two-boxes models as efficiently as possible, I created a simple tool to align two boxes on top of each other.


Surprisingly, filling all of this takes less time than a
 round-trip to the bedroom to re-take a measurement.


Even though there are just two boxes, there are many ways in which the boxes could be placed relative to each other, and even more ways to take the wrong measure on the real-world object. The numbers above, for example, describe the chair. The first box is the base of the chair, and the second box is its back. To measure the height of this second part, I could have measured from the top of the chair to the floor, or from the top of the chair to its seat. To measure the depth of this second box, I could have measured the back's thickness, or the distance between the seat's front and the back. All of these combinations of measurements are easy to express using my tool, by using negative numbers and "reinterpret data" checkboxes.

Once the boxes have the proper size, I use the "face" menu and the "fit" checkboxes to position the second box relative to the first. The back of the chair, for example, has the same width as the base, and is aligned at the back. Done! Next object. To minimize the round trips even further, I bring my wireless keyboard with me to the bedroom and I blindly type notes remotely into an invisible text document. Spooky action at a distance!


Using this technique, I can create two-boxes placeholders for a large number of objects in a small amount of time. I still can't keep up with Nadya, though, who says that placing all of those models at their appropriate location is too easy. For this reason, she also scheduled a story for writing a plugin to import data from Houdini to 3DS Max! I can't wait to see how this pans out. More details in her next post...