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


23 Jan, 2010

Good news for us!  We're officially licensed Sony developers for PS3 and PSP.  Now, as a studio we're able to work on every major piece of hardware  (except the DS, which doesn't interest me).  Very cool indeed.

 

So, what are we working on?  Uh... can't say much, but that we're putting a lot of effort into building an editor.  Finally, we're taking the time to accelerate our development.   Funny when you say that out loud, but it's true.  It's really amazing how time consuming it can be editing text files to make games.  It's even more time consuming to write an editor that writes text files, so you can make a game with it faster.  :-) It's very common to be too busy getting things done, to take a step back and plan a way to get it done more quickly. Why do it now? I suppose it's fair to say we can't possibly get our next game done otherwise, so our hand has been forced.

 

Be that as it may, we already have some really nice features that are quite tricky to do... things like automatic reloading of meshes and textures while playing the game (without needing to restart or even push a button).  Although this kind of stuff is fairly troublesome to implement correctly, once you have it, iteration time for development drops dramatically and quality jumps while costs fall.  Classic technology investment scenario, which we all learned from playing Civilization.  Speaking of... I wonder if I can find my old floppy disk.  And a functioning floppy drive...


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?


<< Start < Prev 1 2 3 4 5 6 7 8 Next > End >>