Tag >> programming
19 Feb, 2010

WxWidgets is a beautiful thing. Coupled with wxFormBuilder, you can make relatively complex GUI applications in a fraction of the time a traditional C++ app would take... still not as quick to build as C#, but then again, if you have a huge library of useful C++ code lying around, the relative value of C# is diminished. The MSVC GUI builder is fast and simple, but the amount of extra binding code to use our C++ libs would make it far less than ideal.

 

So, wxWidgets is the best alternative I've found. Of particular interest is wxGrid. It's a beast that warrants a moment of understanding, because the design is actually kind of beautiful once you grok it. It's incredibly useful to have a grid of stuff. But I wrote an entire translation tool the WRONG WAY using wxGrid, because it seemed like the right way to do things according to the (admittedly minimal) documentation. Do not use a vanilla wxGrid except in toy apps where you don't have many elements and don't care if the contents are duplicated into the grid itself--which sucks in so many, many ways. Let me state this plainly and clearly: wxGrid should always be used in conjunction with your own custom-derivation of wxGridTableBase. Sounds complicated, but really it's just a handful of functions that convert a row/column pair into a string and back again when it's been modified.

 

What are the benefits? For one, you don't have to go in and stuff the grid full of data that you no doubt already have somewhere else in a perfectly reasonable structure. Instead, the grid asks you for the data it needs to display--only the data it displays--not every cell in the grid. This added efficiency saves a lot of memory and dramatically improves a full refresh.

 

More important than the above, from what I can tell, deriving your own data provider is the only way to add custom controls inside the grid for editing the contents. I had no luck simply registering my own data types with the default wxGrid until I added my table type to it. Then, suddenly, everything started working! That means you can put file browser controls, or drop-down choosers, or whatever in the grid trivially by calling RegisterDataType(), and on specific cells or even whole columns, SetColFormatCustom() with the same type names.

 

Then it Just Works. The grid is no longer a separate control, but a direct view of your data structure, and you write about 10x less code dealing with it. In fact, it's a damn shame more controls don't work like this. Good job wxWidgets coders!


09 Feb, 2010

Yes, it's another episode of Can't Google My Way Out, with your host Jason Hughes!

 

While working on our editor, we just recently got to the point where we needed to do mouse selection of objects.  There were two methods I was considering for accomplishing this: 1) the obvious method that involves ray casting from the near-plane out to the far-plane and selecting the nearest object  the ray hits, and 2) rendering all objects to a render target that has object IDs stored per-pixel, and simply scrape through the render target to figure out what was clicked on.

 

The obvious method seemed simplest, so I went with that, for now.  Long-term, I'd really like to do method #2, since you can trivially do lasso selection and additive/subtractive regions in a single swipe.  Very cool.  The second method also has the extremely nice property that, if you can't see it, you won't select it by accident.  I hate editors that let you pick things you can't see, since it's very rarely what you wanted in the first place.

 

Well, the way I approached casting a ray in 3D based on a 2D point is not extremely complicated and can be found in bits and pieces on the web if you look.  The key is that I wanted to generate a ray in homogeneous clip space, not in camera or world space.  Why?  It's simpler and works whether you're using perspective OR orthographic projectiong matrices.  Of course, you could calculate things directly, but there's at least two cases involved, and it may prevent computing wacky projection matrices if you do it the forward route.  So don't.

 

The idea is your mouse cursor is floating along the near plane in clip space, which goes from -1 to 1 along X and -1 to 1 along Y and fixed at 0 or -1 on Z (depending on your graphics system, OpenGl and D3D disagree).  So take your 2D XY coordinate and scale and bias them based on the width of the viewport to be in that range.  Once you have that, your far plane point is the exact same XY position, but at a different fixed Z (again, depends on your graphics system). 

 

Since this is in homogeneous  clip space, you now have a Vector4(xn,yn,zn,1) that represents your near point, and Vector4(xf,yf,zf,1) that represents your far point.  Multiply these vectors against the (4x4) inverse projection matrix.  Now you have camera-space points, but their XYZ are all weird and the W component is not 1 anymore.  That's because it's still a homogeneous vector.  You have to convert it to cartesian space, which is simply dividing each vector by its W component... ta-da!  Camera-space near and far plane points.

 

Oh, and one more thing: if for whatever reason you are modifying your projection matrix by negating certain axes, I found that I needed to perform the same negations when forming the near/far clip space vector, so it corresponds with my projection matrix, and not the graphics hardware's clip space description. This is because you're using the projection matrix you created, not the graphics driver itself.  Thus, take care that your orthographic and perspective matrices have the same clip space coordinates, or you'll have to take that into account here.

 

Why post this? There are plenty of explanations for how to do some parts of this, but I haven't found a place where all of the steps were explained in simple terms.

 

 

JH


31 Oct, 2009
So, during a refactor of a large section of code, I ran into an oddity with Microsoft's Visual Studio compiler in the way templates, default parameters, and namespace/class works. Really odd, particularly because it had been my (naive) understanding that a namespace and a class are quite close to the same thing if your functions are all declared to be static inside the class... truthfully, I (and most people I've talked to) believe that a class wrapping a bunch of static functions is just a nice way to force the user to use ClassName:: in front of your function calls. Well, I ran into an issue with this just a few days ago... don't recall what it was. But by running into a second one today, I thought it was worthwhile posting about it.


typedef TDelegate0<void> MyDelegate;

void
MyFunc(void) { }
class
C
{

public
:
static
void Fn(MyDelegate d = MyDelegate::FromFn<myfunc>()) {}
};


namespace
NS
{

static
void Fn(MyDelegate d = MyDelegate::FromFn<myfunc>()) {}
};


void
main(void)
{

C::Fn(); // error deducing template argument
NS::Fn(); // no problem

}


Don't worry too much about the template typedef itself--it's basically a wrapper for a callback function. The important thing to notice is that, at least with Visual Studio 2005 SP1, these are not equivalent constructs. Of course, I only came across this when I tried to make a function take a default parameter and ran into all kinds of trouble with deducing template arguments. When I simplified the code down, I did so with a namespace (by accident) and had no trouble at all. Just changing the namespace keyword to class caused the issue to pop up. Sigh.

Why can't we have nice things?


15 Oct, 2009

I fixed most of the unsightly bugs and added a few features today, so I guess now it's worth posting a map.  And no, we don't plan on making a rogue-like game.  We needed a quick way to experiment with level sizes for a game without putting a lot of time into an artist building it by hand, so I auto-generate levels at various sizes and we 'play' through it as quickly as we can to get a sense of scale and rapidly find the magical Goldilocks values.  We also need to know if large rooms with lots of cubby holes are more fun than many distinct small rooms, or if many corridors are better than rooms, or if the whole level is better when it's a giant room.  Should be easy to home in on that now.

(Pardon me for using an image, but it seems a lot easier to get upper-ascii codes to show up that way, and not depend on the browser's character encoding to match.  The doorways might be a little hard to see as a result.  Sorry.)

Yes, it looks a lot like Nethack or Rogue--because it's easy to read such maps. But several important distinctions exist... I specifically mark interior floor tiles that will blend into exterior walls that surround all the rooms to theoretically make it easier to skin algorithmically. There are variable width corridors. Also, all corridors are enclosed in exterior walls as well. That helps prevent corridors from touching each other and ruining the linearity of the level flow, plus it the exterior walls form a shell that the player never leaves, which is handy. 

It's certainly been fun writing this. Actually, one of the more fiddly, challenging parts was making the outlines look nice and fit together, as simple as that seems. After several attempts at making special cases to fix bugs with the outlining code, I found a very easy way to select the appropriate kind of symbol (no--I didn't make a 256-entry lookup table, though that did cross my mind).  The eventual solution was very short, and distinctly faster, and simply determined whether each cell wanted to have HALF a 'pipe' piece going up, left, right, and down.  Once I knew what halves were necessary, I just picked the symbol that had those two pieces.  If I were supporting more complex shapes with three or four bars, it could easily be extended to do that as well.


<< Start < Prev 1 2 3 Next > End >>