November 18, 2020

Categorize Points | Chapter 4 - Clip 1

A bit late - since I am now in version 18.5. I am also doing the voice over myself but still use descript to overwrite words with AI if I make a mistake.

Descript really makes all the difference, even if I am not using it to generate the whole narration.

Transcript

congratulations. You made it into the fourth chapter. 

Yeah. And now we are going to... hey, wait a second. Where is the other guy? 

Sorry, I have an urgent appointment. Dave can do this on his own now. 

Well, I don't care who talks, anyway.This is the chapter where we create the main logic system to determine what objects can be placed at what locations.

So hold on to your pants and buckle up. 

Don't worry. I understand that this chapter is the one that might be the most difficult to grasp depending on your background and how comfortable you are with VEX. I will try my best to make this easy, to understand while keeping a good pace, but as a disclaimer for this chapter, especially. There are a lot of ways on how I could reach the result I wanted, and the way I did it might not even be a particular good way.

My approach for finding a solution for three basic steps. What do I want to do? What data do I need for that? Now get the data. That means there's potential to combine some operations or maybe even skip some of them, but I've found all these operations to be useful, to get an impression of what my thought process was when doing the individual steps now to create the attributes that control the later object placement.

We need the points on which we apply these attributes. Once again, we bring in the oriented curve for that. Next we need the slice of terrain to do the testing. And finally, we're bringing the tunnel curve to have better control, where we want to apply these tests. I explain in a bit. I'm going to run through the individual pieces of the curve in a loop.

Each iteration will start to count its points from zero. That means I need an attribute to remember what their initial point number was. For that we can use the attribute, create note. This will be the original ID. The class is already on point and I switch the data type to integer, just like the point number and as a value I type in @ptnum.

Now each point saves its own point number and in that variable. And this is the moment where all of their testing that are announced in the previous videos comes into play. With the points from the oriented curve we go into a wrangle, a wrangle that will have a bit more code than usual, and it is actually an important one.

So it earns the color red. It will define the hold points or rather categorize the points into different possible options. So what exactly am I trying to do? Let's say this line is our terrain. Some areas are flat, some have valleys, and then there are the mountains as well, depending on our settings, the rail will then hover over the surface at different Heights of vanish into the terrain.

When it does go into the terrain, the curve is basically in a tunnel where we don't need to think about creating towers to hold the rail .Outside of the tunnels. We need to check what we need to place to support the rail. So my first task is find out which options are possible on each point. Let's connect the cut terrain to the second input at the tunnel curve goes into the third one.

In this wrangle, there will be some intersect operations going on. So we need the two vectors for a position and the UV parameter, that the function expects. This is the first time where we need to think ahead to what we want to place. Be it something, to create support for our rail, or just decoration for the scene.  For the sake of demonstration, I've decided to keep it somewhat simple.

And just use a tower of three different strength levels and the bridge construct where the towers can reach, but this can be used as a framework for whatever you want to build yourself. The different tower levels will provide different support values to the system and will be displayed with different sizes.

They have different requirements to the environment. Again, keeping it simple. A tower needs the rail to fill a minimum and a maximum height requirement. So first we need three incoming channels or the minimal height value. Here you would create additional tests for you own kind of objects or further tests if you want to refine the system. The max height is what flows into the intersect test using the individual max value, as a direction vector. With the set function. I'm preparing that right here. The test is supposed to go straight down. So, for example, for a height test  of 10, this creates the down vector that points 10 units down.

Now, the question is how precise do we want to be with our testing? You basically have three options. The easiest one would be to just take the current point of the curve and just do the intersect from there. The second option. Which I used is a bit more accurate. We create two offset positions using the same approach we already used to find the leaning group.  With a cross-product of the normal and the up vector, we create two positions on each side of the curve. Remember to negate the second offset position with a minus in front of the normalized function. In the normal case, we will have a tower on each side, holding the rail.

That's why we test the terrain closer to the point where the  tower might end up. But this is only a rough estimate. There are some edge cases that might slip through this test. The towers will have different sizes, different spacing to the rail and to random angle for the pillar, connecting to the ground.

If I wanted to have an a hundred percent accurate test, the third option would be to replicate all the steps that manipulate the final position on the ground. Something we look at after we created the towers and turn that into a python state. For now, I'm happy with a rough terrain test using the offset positions.

I need to catch the returning values from the intersect operations. They will have the type integer and contain a -1, if the intersect failed. Or the primitive number that was hit. I will also need a test variable, but more on that later. And while we are at it,  let's do the same kind of debugging we did for the leaning groups.

Just to see if everything works. Let's use  add points at the offset positions. Now let's generate all those parameters and create some values for our tower testing. First, let's see if the offset works as intended.  With the tower width, I should be able to manipulate how far way the new test positions go. Now you might see where this is a bit more accurate than just test from the curve itself.

There could be a cliff right below the tower, even though one side could be placed. It is possible that the other side can't. By checking both sides, we should be able to avoid most of those situations. Now to the towers themselves. As a first simple approach, I say that tower level one can only reach up to 20 units.

Level two goes to 30. And the last one goes up to 50.  For the minimum values you could create a specific range. For example, tower one goes from 5 to 10. While, the next one can only be where you can't place the smaller one. For my example, I allow all towers to be placed anywhere, but they have to be over a certain threshold, making sure that the bigger towers have enough space.

So when tower level one, Has a minimum value of four and a max value of 20, the rate can be anywhere in this green range.  With increasing values the range is shifting to higher areas were getting wider as well. It allows for an overlap where all available towers can be placed or only a selection. Then the system has to choose from the available options.

Let's delete the debugging points and head back into the wrangle. It is time to put all of these parameters to use. If you remember, we connected the tunnel curve into the third input. This will be my first test. I don't want any objects too close to the tunnel entrance. So let's get that distance with the distance - minpos combination.

We only want to continue if we are at least two units away from any tunnel. Or if no tunnel exists into first place.

Now we know that we are definitely not in a tunnel. Here I'm starting to do the intersect testing. The two offset positions. If they are within the height of the tower, level three. Both intersect operations, override the POS variable and assuming one connected, I can get the distance between the point and the ground.

But in case we ahve the previously mentioned cliff scenario, we now check both returning values. If the test succeeded, if both were turning values are not minus one, we can proceed to the test for the minimum  requirement. The distance needs to be bigger than that. If that is the case, we can give this point , the attribute tower three checked.

At this point, at least one test is already positive. So I also turned my check value to one. Now we're in the successful intersect test for level three, we continue to do intersect tests, but now we use the lower height capability of level two. Instead of the new intersect, we could also check if the distance is within the range of that level.

Important is that I do these intersect regardless if the minimal requirement of the previous step was successful. If the rail would be too close for level three, I still need to test the lower levels. They might still fit between the rail and the ground, but by nesting the tests, as you can see here, you still cut down the amount of needed tests.

If the rail is too high for the previous level, there is no need to check the next one. Since each level has a different range, we need to set the check variable whenever one check Mark is set for sure.

If all tests were negative, we can set an appropriate point group.

I called it: in height. So only these points, that can place any tower are in this group. This is just a helpful way to isolate specific points later on. And last but not least we have the bridges. The bridges have no max value, but a minimum requirement. If the point is high enough, it gets the bridge check.

Mark. Finally, we're at the else branch of the initial condition. When we are within two units of a tunnel curve, the point is marked  as: in tunnel. At this stage, some of you might already shake their heads because you spotted an issue. At the very end, I check the distance. If it is big enough to place a bridge, but in this current setup, this will not work. When the rail is really high, too high for even tower level three, basically exactly when we need them.

So why is that. The distance was created here in the beginning after we made the intersect for tower level three. If that intersect misses, because the ground was too far away. The distance stays at zero because the intersect operation never overrides the variable POS with the actual hip position. So we need to make sure that we have an initial test that will always reach the ground.

Let's create a new inter variable. Inter zero, and this one goes down thousand units. Hopefully your heads were starting to shake again, doing this test first would be wrong again. Each intersect overwrites the variable pos with its hit location. So we need to do the test last. No, we always have a distance to test, even if it's just for the bridges.

And finally, sometimes it is a good idea to take values like this distance with you beyond the wrangle, just in case you needed later. We are in a point Wrangle. So I can just write @height and fill it with the distance variable. Again, this gives me an opportunity to warn you about a possible issue. If I test this code, this last line will result in an error.

It does know the variable dist, but we clearly created it right here. The problem is that it is inside a logical block. In this case, it is this if condition. Whatever variables you define inside such a block can only be used while you are within the block, but it is really simple to solve. Just move the definition above the if block.

Now, the rest of the code knows what dist is used for and can access it as well. With that the initial test for the points are done. You can see the new groups and attributes and the spreadsheet. But we need to have a better visualization to check the result in the viewpoint. For the system, the created check marks are what's most important.

So let's create some visualizers for them. We need 3 for the tower levels and one additional visualizer for the bridges.

Now we can see more clearly what points are checked for which level and how adjusting the different ranges changes the outcome. As you can see in the first line, there are a lot of zeros, which means with a value of 15, we could only place very few level one

towers. Tower Level free. The red numbers stretch way beyond the slope. By reducing the value to let's save 30. It stops much earlier. And finally by increasing the minimal requirement for the bridges, to the height where no tower can reach, we can control which points are even considered. These settings will allow us to change the complete layout by moving only a few sliders.

That wasn't so bad for a first bigger slap of VEX to the face. Wasn't it ? Tune into the next clip when it is time to really dig into the rail logic. .

Leave a Reply

Your email address will not be published. Required fields are marked *

David Kahl VFX

a Software Developer, Nerd and VFX Artist based in Germany
Visit Patreon
linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram