You can approach making grass differently. But sometimes you end up wondering: what is the preferred and cleanest way of doing a thing? Well, here's my tiny and opinionated take on that subject. Let's start from a really basic way of making grass with brushes, iterate on it to enhance the setup, and, on the way, also try and make our good old New Mexico desert landscape look just a little bit more green.
The Basic Piece
The basic building block for our thickets of grass will be a cross-shaped pair of planes, at least initially. We could use a more complex shape than that, but I don't think it's necessary. It's up to you. But in this tutorial we should be fine with a cross.
We'll start off simple: make a pair of brushes and cover them with NULL
, then arrange them in the shape of a cross. In relation to the top view, you may keep one of the pieces vertical and the other horizontal. I, however, chose to have both of them diagonal. It shouldn't affect anything important anyway.
I will use
as the grass texture through pretty much the whole tutorial. Choose whichever one you like and align it onto one of the faces, like this:
Now the only thing left to do is the usual entity paperwork. Select and tie both brushes to func_illusionary
and set Render Mode
and FX Amount
Here's what we should end up with in the 3D and top views:
Let's put it into a map of some kind, compile it and take a look:
Here we go! We've got a little nice piece of grass up and ready to be scattered across your maps. You may already be pleased enough with this, and in case you are - it's totally fine. That applies to the rest of the iterations too.
...Wait a minute, turns out there's a weird little gap in there:
Outright unacceptable. If such little things in your maps tend to bug and annoy you as much as they do bug and annoy me, follow along so we can work on trying to fix that issue!
You can notice how thick each of the brushes is. And all that space in-between is covered with NULL
, which is why there's a gap! The first thing that comes to mind is making each brush in the pair extremely thin:
Let's look at the compiled version:
There's still a gap, although a smaller one, but nonetheless noticeable. It also introduces a small but non-zero risk of compile errors and doesn't look very clean on the 2D views, which may get annoying when there's a lot going on there.
We can avoid less-than-one-unit-thick brushes by remaking our design this way:
We've done the following steps here:
- Replaced the grass texture on one of the sides of each brush in the pair with NULL.
- Cloned the pair and rotated it by
- Positioned the clone in such a way that its grass-textured faces are touching and fully covering the grass-textured faces of the original pair.
- Made sure that the two pairs are two separate func_illusionary entities with the same properties.
Hey, there are still gaps, but only on two of the sides! We've also just wasted two models in our budget instead of one. To get fully rid of the gaps this way, we'd have to make all four brushes separate entities. We could also go back to the first iteration and make those two brushes separate entities too. It still would add up to the model limit more than we need, though.
We still haven't used one more tool in our arsenal - the CONTENTWATER
It won't yet directly solve our current issues, but it's an in-between step on the way to something greater.
Let's take our first iteration and make the brushes separate entities to avoid gaps. Now, we'll also replace all NULL
, and even apply it instead of the grass texture on one of the sides of each brush in the pair. That's fine, CONTENTWATER
will mirror the grass texture on the other end for us:
It still wastes two model slots, but hey, now there's no gap and also less brushes! But we won't stop on this one just yet.
You know what? Let's just throw the existing design out the window. What if we arrange our brushes like this:
Wow. We should've started this way, this is really good. At least that's how I felt like when I recalled, if I'm not wrong, the brilliant Admer456
showing this way of arranging grass brushes. This actually solves all of our problems! Well, almost all of them.
Of course, usually there isn't just one single piece of grass in a map. There are lots of them, and sometimes they may be clustered close together. Usually you also want to merge all those pieces into a single entity to save on models. Yeah, this is where this design has a chance of showing its flaw:
Uh oh. Yeah, a bit unfriendly when it comes to merging - look at those gaps. Looks like we'll have to adjust our design just one more time.
It's nothing hard. All we have to do to make this issue occur far less is by making the big CONTENTWATER
'y pieces take up less space, and here's how:
What we did is cut it horizontally in the middle (in relation to the top view), then moved the resulting middle vertices very close to the center. It isn't ideal, but it's practically enough.
Let's take a look:
Yup. Not an issue now.
Well, don't know about you, but I'm satisfied at this point. It's as close to perfect as it gets. So let's move on to how we're going to grassify our map.
I've thrown around some grass and even made a small cave (if you can call it that) to demonstrate what we'll run into shortly:
Merging vs. Visibility
Note that each individual piece of grass has been tied to one single func_illusionary
entity. Again, brush entity merging. We don't have infinite models, and each individual brush entity, except for func_detail
of course, counts as a separate model.
There's one tiny
issue, however. Let's follow through the cave and into a small observation room with no windows that I've made. We'll open up our console and type
in it. Here's what we're going to see:
Oh no. See those little rectangles? That's our grass. See those same rectangles in the right part of the picture? Yeah, that's also our grass, but it's in a room that we cannot see! Or, rather, it shouldn't be rendering given our current location, but it is. That is a side effect of entity merging. If you don't like rectangles, here's another way to show it:
If any part of a brush entity is in a room that is visible, then the brush entity will also be visible in its entirety. Tie all the grass in your map to a single func_illusionary
and it will always
be rendered. If there's a lot of grass, that will negatively impact performance, and it won't do so slightly. The solution we'll use here for this problem is separating your big merged func_illusionary
into a few smaller ones based on visibility.
Here are screenshots of the results of doing so:
Each selected set of brushes is one individual func_illusionary
. Separating the original big func_illusionary
into three entities should help our map's visibility. Let's see:
Much better! But everything has a cost. Here's a comparison of model counts given by the BSP
Sadly there's little we can do about it, so you've just got to strike a balance here. Well, we're done with the tricky part, time to have some fun.
Every Shrub Is Different
When grassifying your map, it's important to remember that repetitive patterns can get very annoying. Here's our grass right now:
Yeah... That's rather off-putting. Now, we don't want to make our grassy valley look uncanny, so we've got to do something about that. Well, actually, all you really have to do is two things: rotate and stretch.
So let's do some rotating and stretching, shall we?
And here's what we end up with in-game:
Yeah, it's all just manual labor. To make things a little easier, there's this nifty button in J.A.C.K.:
It will stretch the textures on your brushes as you stretch the brushes. Easy. But saves a lot of time and makes your
keys last a little longer.
Well. That's all there is to grass. At least as far as I know - I wouldn't mind to be proven wrong! By the way, you can get the map used for screenshotting all this from here:
Now, go make some grass.