Creating Complex Curves and Corner Pieces

[TABLE=“class: outer_border, width: 1148, align: center”]

https://dl.dropbox.com/u/17850283/curve_tut/track.png

[/TABLE]

Over the past few days, I’ve seen multiple questions in the Mapping Question Megathread about creating offset corner pieces and curved junctions. I promised that I’d write up a tutorial for it, so this is my attempt at a guide on creating complex curves using clever brush manipulation.

This is one of the two techniques I use for curves, the other being template brushes and paste special (TopHATTwaffle uses a similar method in this video). Of course, paste special is easier to work with, but I think this technique is more versatile and allows for more complicated brushwork (like the wavy railing in the screenshot above) which is otherwise impossible to pull off seamlessly.

Without further ado, let’s get started:

[HR][/HR]1. Start by creating the shape you want to wrap around the corner piece. For this demonstration, I’ll just use a simple rectangular frame. You can use any shape you want — even complex, wavy ones, like the one seen above — so long as they tile nicely around the corner.
[HR][/HR]

[HR][/HR]2. Move the shape into place on the corner piece and align their outer vertices. Before we start, save a copy of your template shape in a separate visgroup so that you don’t have to remake it for each face. Paste special is especially handy for this (CTRL+C → CTRL+SHIFT+V).
[HR][/HR]

[HR][/HR]3. The goal here is to get the shape to the same width as the underlying corner face, as well as to rotate it so that they align perfectly. To do this, we’re going to have to employ a bit of math.

To find how wide the corner face is, we use the Pythagorean theorem. Grab the block tool and drag a bounding box (in the top view presumably) from one vertex of the corner edge to the other. You’ve now formed a right triangle, with the corner edge as the hypotenuse. On the sides, you’ll be able to read how many units the edge spans on the X and Y axes (the so-called “rise” and “run” you may have heard of in algebra class). From there, the edge’s length will be

http://latex.codecogs.com/png.latex? iny%20\inline%20\dpi{150}%20\ell%20=%2 0\sqrt{\Delta%20x^2%20+%20\Delta%20y^2}

.

To find out how far around we need to rotate the shape for it to match the corner face, we take the arctangent of the edge opposite angle

http://latex.codecogs.com/gif.latex? iny%20\inline%20\dpi{150}%20 heta

over the hypotenuse. Depending on how you’ve oriented the corner piece, this can either be

http://latex.codecogs.com/gif.latex? iny%20\inline%20\dpi{150}%20 heta%20= %20\arctan%20(\Delta%20x%20/%20\Delta%20y)

or

http://latex.codecogs.com/gif.latex? iny%20\inline%20\dpi{150}%20 heta%20= %20\arctan%20(\Delta%20y%20/%20\Delta%20x)

. Google Search’s built-in calculator is fabulous for this, as can be seen in the picture below.
[HR][/HR]

[HR][/HR]4. Now that we know the corner edge’s length, we need a way to get our shape to match that length. We do this by scaling it down, but in order to do that, we need to find the appropriate scaling factor

http://latex.codecogs.com/gif.latex? iny%20\inline%20\dpi{150}%20k

. Wolfram|Alpha to the rescue!

http://latex.codecogs.com/gif.latex? iny%20\inline%20\dpi{150}%20k%20=%20\e ll%20/%20w

, where

http://latex.codecogs.com/gif.latex? iny%20\inline%20\dpi{150}%20\ell

is the corner edge length we found earlier (in my case, 50.249378 units) and

http://latex.codecogs.com/gif.latex? iny%20\inline%20\dpi{150}%20w

is the width of our shape (in my case, 64 units).

Once you’ve gotten the scaling factor, hit CTRL+M to open the transformation dialog. Select “Scale” on the left side, and put the scaling factor into the three fields on the right. It is recommended that you only scale the shape along one axis and leave the other two at 0 (or 1) as in most cases scaling on the vertical axis is not desirable, and scaling on the horizontal axis perpendicular to your shape’s longer edge may result in diverging vertices. This is something I forgot to do when I was taking these screenshots — I should’ve just scaled the shape along Y and left the other two axes alone.

Hit enter to perform the transformation. Use CTRL+B to snap the resulting shape to the grid, and manually realign the shape’s outer corner with the corresponding corner piece vertex. If you’ve done any vertical scaling, which, again, is not recommended, be sure to realign the brushes in the side views as well.
[HR][/HR]

[HR][/HR]5. Now the rotation. Since geometry in Hammer cannot be rotated about an arbitrary point, we’re going to have to “trick” Hammer with a helper brush, which will serve as our rotator gizmo. Grab the block tool and make a large brush (it has to look larger than your shape in the top view) centered around the shape’s outer corner. This is crucial, so make sure you get it right!
[HR][/HR]

[HR][/HR]6. Select both the shape and the helper brush, and once again open the transformation dialog with CTRL+M. Remember that arctangent calculation from earlier? Take the resulting angle (make sure it’s in degrees) and put it into the Z field. Again, depending on the initial orientation of your corner piece, you may instead need to take the angle’s negative value (e.g. 5.7105931° → -5.7105931°) or even its complementary angle (e.g. 5.7105931° → 84.2894069°). Try out all three and see which one works for you.
[HR][/HR]

[HR][/HR]7. One last step: slicing the rotated shape so it doesn’t stick out. This is pretty straightforward — just grab the clipping tool and slice the shape along the corner piece’s inner edge.
[HR][/HR]

[HR][/HR]8. Rinse and repeat for the second face and any other faces/corners after that.

Take careful note of how brush slicing is performed on non-axis-aligned faces: you select the shape and then drag the clipping gizmo from the shape’s corner to the innermost vertex of the corner piece. Some gaps in the brushwork might pop up here, either due to Hammer’s decimal loss issue or your sloppiness; CTRL+B and the vertex tool are your friends!
[HR][/HR]

[HR][/HR]Conclusion: You can do all sorts of perverted crap with this method, so use it to your advantage. It can even be further expanded to include displacements.

I hope this was an enjoyable and informative read and that the techniques provided herein will prove helpful to you in your own maps. Thanks for reading, and have a nice day!
[HR][/HR]

[H2]Another technique:[/H2]

holy SHIT

I have tried this so many times, but no luck. Thank you for this!

Oh my god I am probably going to end up using this so much

Thank you

Good to hear! I haven’t included some subtle nuances of the brush manipulation involved as they only relate to special cases. If you do run into any trouble, feel free to post here.

Uhh, doesn’t hammer corrupt it as soon as you save it tho? And I think vbsp rounds coordinates to the nearest grid point?

The maths part is kinda annoying

Yes, Hammer’s decimal loss issue does take its toll eventually, but it’s not really noticeable. There’s just no way to get around it, so we might as well just deal with it. With that said, I recommend putting any complex curves you create in a separate VMF file and that you avoid saving over it too many times. That way, even if the geometry in your main map does get corrupted to the point where it becomes noticeable, you can just replace it with the backup you stored.

Regarding the rounding, it only does that if the discrepancies are less than 0.2 units. This method is pretty precise, so even if vertices of two different brushes do end up off-grid and get snapped, they’ll get snapped together without creating gaps in the geometry.

[editline]31st January 2013[/editline]

I changed one of the sentences in the guide to recommend only scaling the shape along a single axis to avoid issues like this.

Hooray! I remember asking a couple questions like these in the question thread this past week. Bookmarked!

Pretty helpful :slight_smile: I’m not particularly great with complex curves, i’ll have to mess around a bit.

Great tutorial man.

Thanks for the kind words! I’ll see if I can do a video tutorial on the other curve technique I use (template brush + paste special); just need to get a new mic.

Uh, anyone know why [noparse][HR][/HR][/noparse] doesn’t show up in the post but it does when you preview it?

It might be because it was removed from the forum, but not from whatever the preview uses.

Awesome Tutorial Mozart, thanks for going into detail with all of the pictures. This was a process that I (and I’m sure many others) have had some trouble trying to recreate. I remember SOH CAH TOA but I didn’t think I was going need to be using it while mapping. :stuck_out_tongue:

Looking forward to seeing your other curve technique style, if you’re loking for a good headset and microphone, theres one on Newegg by Corsair that is pretty good and comfortable.

Thanks. I think I’ll just get a run-of-the-mill table microphone for now.

http://www.gyroshot.com/vertalert.htm
About the vertex thing, looks like someone came to our rescue.

Actually, I think that might make it even worse as it could potentially further separate misaligned vertices caused by Hammer’s decimal loss. Like I said, the best way of preventing that from happening is to just keep a backup of the curved brushes in a separate VMF file and not to save over it too many times.

Do you have any test cases where this might occur, or see anything wrong in my script that could provoke further separation? I’m always open to bug reports, and GitHub’s issue tracking system makes it easy to keep tabs on that kind of thing.

For what it’s worth, the default settings in VertAlert mimic VBSP’s rounding, so the program shouldn’t do anything that won’t already be done to your maps, that is unless you tell it to. That said, the tool still doesn’t apply here, since without writing custom compile tools (or changing some option I’ve never heard of) there’s no way to stop VBSP from snapping certain vertices automatically.

As far as separating misaligned vertices, I can’t see how that would happen in a way that one couldn’t control. If the vertices are close enough to one another to appear seamless in game, they would only ever be rounded in the same direction, to the same end position. Only if you had a coordinate at 0.19, for example, that was meant to appear to line up with one at 0.2, could you get a separation (the .19 being snapped and the .2 ignored), but again, that’s what BSP already does without my intervention. You could get the same effect with different values if you change the threshold, of course, but in that case you can always increase it to snap all the problematic points, decrease it to leave them all alone, or put objects you want to ignore in a VISgroup together and disable it (VertAlert only checks enabled groups).

I have further plans for the tool, namely letting users specify the snap setting both above and below the threshold, which could let you do things like snap everything below the threshold to the nearest integer, while snapping everything at or above it to the nearest multiple of 0.05, allowing for subunit vertex positions that line up neatly with one another. This all in response to a particular user’s problems dealing with vertices that started out at exactly 0.5 units between integers, but got mangled to 0.4998 and 0.501. Those coordinates deviate from their original value by amounts large enough to make their brushface invalid, and they’re too far away from integer values to be touched by VBSP, leading to solids that have to be stripped from the VMF, even though the compile tools and game engine could handle them if the stored coordinates hadn’t been screwed up in the first place. Having separate snap values will solve that, or at least I think it will. I can’t wait to find out how disastrously wrong I am!

That’s a lot more detail than I’d planned on typing. The bottom line is that no, unfortunately the tool won’t help in this situation, but it also shouldn’t make things worse unless there’s a bug in the code. I’d still recommend keeping the original, of course, and not overwriting the input VMF (the --fix flag doesn’t overwrite by default, but you can still force it to; I plan to completely disable that capability in the next release).

Wonderful tutorial, Mozart! I can’t wait to get back to mapping so I can do something with it.

Thanks for the kind works, and thanks for the thorough post. I will certainly be following your GitHub repo and this tool’s progress.