So, something more exciting this time: I’ve got a working download for you guys to muck about with. It’s more of a proof-of-concept tech demo at the moment, but it does show how the views should work and the basis for how the objects within the editor will be structured.
At the moment, the build is for Windows 32-bit (although that screenshot is from my MacBook) and is available here:
https://www.dropbox.com/s/s2hk1sqpybyruaf/crowbar_alpha_0.01.zip?dl=0 It
should work straight out of the box, but if not then try installing the Microsoft Visual C++ Runtime. (I’d expect most of you have it anyway, since AFAIK it’s a prerequisite for Source games.)
For controls: the mouse wheel will zoom the 2D view in and out, and will move the camera in the 3D view, in a similar way to Hammer. (Right now I think the zooming is inverted because I’ve been doing a lot of the development on a MacBook.) Space-drag will scroll the 2D view; pressing P will activate mouse look mode in the 3D view, where W/A/S/D/Q/Z will move the camera until P is pressed again. The views require focus to receive the key events, so click on them if they don’t seem to work. All these keys will be customisable in the end product, but they’re hard-coded for now.
To explain what’s going on in the demo, I’ll start with how things are usually managed in Hammer. As you may know, brushes in Hammer are always defined in world space: the co-ordinates of the vertices are relative to the big red, green and blue sticks that sit in the centre of the map (the world origin). In 2D, it would look something like this:
This setup is simple and intuitive, but it means that co-ordinates that are “convenient” with respect to the world (like brushes that are on-grid) are handled better than other, more arbitrary co-ordinates. If you’ve worked on small fiddly bits of brushwork, or rotated complicated pieces of geometry at arbitrary angles, you’ll probably have run into what’s known as “floating point precision” errors: because of the odd angle or the very small co-ordinate values, the computer doesn’t have enough mathematical precision to store the
exact values (you can’t represent 12.123454321, for example, if you can only store 5 digits), so it approximates. Because these approximations aren’t
exactly the same as the ideal values, you end up with minute gaps between brushes or, at worst, invalid solids. If you do more odd rotations then these errors will be carried through the subsequent rotations, potentially growing larger and larger.
What we can do is be a bit clever and allow brushes to be defined not just relative to the world (like normal), but also
relative to other brushes. This works out like so:
Each object - be it a brush, entity, or anything else - is given its own origin, and the origin is positioned relative to the object’s
parent. This is exactly the same as how parenting works in Source: a child will move wherever its parent moves, and will maintain the same relative orientation when its parent rotates. It will also maintain the same relative scale: if you make the parent twice as big, the child will also become twice as big as it was before.
The co-ordinates of a brush, instead of being defined with respect to the world, are now defined with respect to its own origin. As you can see in the image above, the blue child square specifies its top right X value as 0.25 units away from its
local origin, as opposed to 4.5 units away from the
global map origin. This means that we can keep the co-ordinates of brush vertices convenient (eg. as integers), specify the translation/rotation/scale of the brush relative to its parent, and then calculate the world co-ordinates when we need them (when compiling). This won’t totally eliminate floating-point precision errors, but it will mean that they’ll only be calculated once when you compile the map, and the compiler may well do other clever things like merging groups vertices that are all minutely close together.
In terms of general level construction, this hierarchical method lets us do two very useful things:
- It lets us position a brush relative to another reference brush, in situations where this might be awkward to do otherwise, and
- It allows us to do whatever we want with the reference brush and have the child stay in the same relative position.
Since everything, including the world itself, is a hierarchical object, you can even apply transformations to the world and have them affect all the objects in the map. Got an over-scaled map? Scale the world down and all the brushes will follow suit.
In this demo, the orange blocks are the parent brushes and the small red blocks are the children. If you select the orange brushes (through the selection dialogue at the moment, unfortunately) and transform them with the transformation dialogue, the red brushes will change too.
Basically, try your best to break the application and let me know what you think of it.
EDIT: Only just realised weird things were happening with that link, it's fixed now.