Procedural modeling in Blender with Python | Simon Van Den Hende | Skillshare

Playback Speed


  • 0.5x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 2x

Procedural modeling in Blender with Python

teacher avatar Simon Van Den Hende, Pipeline Developer

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Lessons in This Class

30 Lessons (3h 8m)
    • 1. Introduction

      2:05
    • 2. Modifiers - Introduction

      4:00
    • 3. Modifiers - Preparation

      7:03
    • 4. Modifiers - Creating a modifier

      10:20
    • 5. Modifiers - Creating more modifiers

      3:41
    • 6. Modifiers - The match function library

      4:56
    • 7. Modifiers - Collapsing modifiers

      6:20
    • 8. Modifiers - Cleaning up the code

      21:33
    • 9. Procedural Rocks - Introduction

      0:49
    • 10. Procedural Rocks - Creating a rock manually

      5:41
    • 11. Procedural Rocks - Project Setup

      1:31
    • 12. Procedural Rocks - Modifiers

      6:22
    • 13. Procedural Rocks - Generating complex textures (1)

      8:39
    • 14. Procedural Rocks - Generating complex textures (2)

      10:56
    • 15. Procedural Rocks - Reducing the poly count

      2:22
    • 16. Procedural Rocks - Altering vertex positions

      7:29
    • 17. Procedural Rocks - Collapsing the modifiers

      6:40
    • 18. Procedural books - Introduction

      1:35
    • 19. Procedural books - Generating the shape

      8:20
    • 20. Procedural books - Alignment

      2:19
    • 21. Procedural books - Manual modeling

      1:45
    • 22. Procedural books - Adding edges using code

      13:59
    • 23. Procedural books - Refactoring

      3:28
    • 24. Procedural books - Identifying a specific set of faces

      15:47
    • 25. Procedural books - Selecing and extruding specific faces

      4:32
    • 26. Procedural books - Abstracing the extrude code

      1:53
    • 27. Procedural books - Creating materials

      6:42
    • 28. Procedural books - Creating complex material

      4:00
    • 29. Procedural books - Creating a shelf of books

      10:02
    • 30. Procedural books - Optimizing our code

      3:05
  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels
  • Beg/Int level
  • Int/Adv level

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.

22

Students

--

Projects

About This Class

In this course we will walk through the entire process of creating procedural assets for your game development or visual effects projects. You will be taken through each step of the process from generating the base geometry, to adding detail in various ways, then finally setting up some initial materials.

You also get a complete guide to the basics of Python, as a bonus, so if you are completely new to Python, you can get up to speed quickly and learn how to apply core concepts to your Blender workflow.

If you are new to Procedural Modeling or Procedural content creation for games, and want to jump start your learning in Blender, then this course is perfect for you!

Meet Your Teacher

Teacher Profile Image

Simon Van Den Hende

Pipeline Developer

Teacher

Pipeline Developer with solid experience and a passion for automation.

 

Worked at Outpost VFX on multiple feature films and television projects. Focusing on Pipeline and Data Flow as well as research and development. Main tools I used here were Python, Maya and Houdini

 

Worked at Prompto on the Pipeline and Data Flow. Focusing on Unreal and Blender tools / automation. All done with Python

 

Besides CGI, I have a strong love for web development / programming in general.

 

If there is anything I can help you with, don't hesitate to reach out on my email address: [email protected]

I look forward to hearing from you!

See full profile

Class Ratings

Expectations Met?
  • Exceeded!
    0%
  • Yes
    0%
  • Somewhat
    0%
  • Not really
    0%
Reviews Archive

In October 2018, we updated our review system to improve the way we collect feedback. Below are the reviews written before that update.

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.

Transcripts

1. Introduction: And the first part of the course we're going to focus on modifiers that we're going to see how we can apply them, where we can apply them, how we can generalize the code that isn't needed to create them in order to be able to use it across different projects. And this part of the course, we're still going to be using the modifiers. We're going to dive a little bit deeper and applied same knowledge like we saw before on to a completely different use case. So we're going to be generating some rocks here. We're also going to go a bit more in depth than just the modifier workflow. So we're going to start off with the modifiers, but then we're actually going to dive a little bit deeper into generating the specific textures for our displacement and also offsetting the vertices, which you can see a little bit of a sneak peek here, but it's actually quite fun. You can really mess around with the geometry that's there. You can really displace it on multiple levels, which are, which allows these quite interesting results. And this part of the course, we're going to leave the modifiers aside for a little bit. We're going to just focus on actually creating geometry. We're really going to get into the nitty-gritty of modelling, adding edges, extruding faces, moving vertices around. So we're really going to, like we're going to keep it simple in the beginning. I'm just going to focus on a single book, see how we can make it, how we can script it. And then once you know how to make a single book, you can easily make like a whole shelf of books. Even all library with this relatively simple script. So I really don't want to overwhelm you guys. We will also spend some time and attention on the quality or code, making sure that everything is clean and unstructured. All right, so without further ado, let's start the course. 2. Modifiers - Introduction: Hi guys. This part of the course we're going to be creating this cool looking sphere. Kinda looks like a Voronoi pattern that's been added on here to main focus of this part of the course is actually applying modifier. So, so which grading modifiers for a piece of geometry and then applying it. So now we actually have, we have this as geometry so we could do cool things with it, even though you could extrude it. Make it look good. You're more, more crazy. You can, you can experiment with this. We're just going to be making that basic cube, sorry, the basic sphere. We're actually going to start off with a cube. So that's quite fun. Let's actually get straight into it. It's going to create a new file and go to the scripting step, create a new script by clicking the plus button here. Do our starting off steps, which is importing BPI and saving our scripts or through Voronoi, sphere, horse, whatever you can call it, whatever it is, make sure you save it to someplace on this QC and actually spend Control S like I'm doing all the time. So you don't lose your org is quite important. Let's actually create some work that we, that we can save. The first step that I usually do when I, when I create any script that generates any kind of geometry that I implement, a cleaning of the cosine function. So it's called the clean scene. So you can call this dysfunction anything. So this is basically the function name and this is the syntax of how you declare a function. So it will be just be the function name, brackets and a colon. Here You can pass arguments and all that fancy stuff. We're not going to be doing that now. We might do it in a different example of a function later. So look over that, don't worry. But for now we just want to create a basic function, call it clean scene, and then clean our scene. So in order to clean this year is going to grep every object in the BPI dot, dot, dot objects collection. So this will return a list of all of the objects in the scene. So this will include cameras, lights, meshes. So more. But I think those are the main three that we have to care about. And I, we can just remove that by doing BP, write out data objects dot, remove, and just pass in the current object that we have in our loop. So if we were to execute this or alert extent, nothing's going to happen because we have a function now. But this function is actually never called. So any, any code that is inside of a function declaration. So inside of this def block, we can see this is one and the notation level over from whatever the declaration was. So all of this code is actually grouped inside of our function and it will actually never be executed until our function is called. So if we were to call our function down here, which we can do like this. So we just specify a function name and then we pass in our arguments through the brackets. So no, there are no arguments, so we don't have to pass anything. If I were to run this now, our function would actually be executed. So that's how you do that. You could actually executed multiple times by, by running. Just basically whenever I execute this line, I execute all of these lines that are inside of our function. So this is basically just a group of lines. And whenever you call the group, all of the lines inside will be executed. So that might be over complicating things. But if you just grab the core concept of a function in programming, basically this works that same. All right, so we have our function. 3. Modifiers - Preparation: So now that we have our clean scene, so an empty scene here, we can actually start adding in our cool geometry. So the next bit of code and next block of code I'm going to call create. Cool sphere for example. Again, it doesn't really matter what you call these functions. Just make sure it's quite at least a little descriptive. So you could argue that this is a little bit lame, little bit now finally, or, or just not professional, maybe just a fun project. It doesn't really matter as long as you know what it means. That should be fine. And I got to be sharing this code anyway. But just general good practice is keep it readable. Keep them descriptive. Make sure you don't have to like figure out what they mean. But yeah. So when creating a cool sphere, we're going to be having to do, like we're going to have to do a couple of steps, but let's not do them quite yet. So I'll just print generate Cool sphere in here because we need some kind of, some kind of line of code in here in order to not break it. If I were to just run it like this, it will break because they're like No no content inside. Even if I were to fix my indentation, that's not actually the reason why it's breaking. So now it's actually working. What you could do, a quick tip, a quick Python tip, when you want to already rights like the architecture or the code blocks of your, of your script, but not the actual golden itself. You could write it the past. This is like a keyword and biotin is just going to skip. Skip this. Basically services preventing the error that you would get by declaring a function but not doing anything inside. So you could actually do as a sexy bit better. What we're gonna do instead first is we're going to create our cool sphere manually. So this is actually quite handy when you're starting off with this whole generating or geometry. Stuff, is just doing it manually and see how it works. How you would create a jump tremendously. And then you can actually automate its grip to make sure you can pump out thousands or tens of thousands of cool spheres without having to actually do it yourself. So let's make one and then scripted so that we can make thousands. That's basically what we're, what I'm trying to say. So in order to create a cool sphere, I'm going to start off with a cube because it wouldn't be very cool otherwise. Actually the main reason I'm doing this is for the topology. I'll actually show you what I mean by turning this cube into a sphere by subdividing it. So we could add a couple more divisions to make it a bit clearer. What we have now is something that's kind of spherical. And if we were to add the cast modifier as well, we would have a perfect sphere with perfect topology if I were to turn that on as well, to show you, it's actually bombed is known one time in order to show it a bit more clear. So I'm not sure how, like how, how familiar you are with topology, but you generally want to have quotes, especially in real effects in games. If there's just a still are in the room, doesn't matter that much. But if it has to deform or animated or anything, generally, best practice to have all quads. You could also argue for all all triangles, which is what you would get from an ICU sphere. And if I were to turn into subdivisions up to 400 on this guy and I, we could compare them. You can see that topologists like not bad on this guy like null at all. It's just all triangles. And you do have like a couple of polls. So if you're not quite sure what that is, this is just some topology. I don't wanna say theory because that sounds dumb, but yeah, just generated Brexit to have like a vertex more than four lines and XD quite a bit, quite a lot there. Illinois, I just don't really like this type spheres. I mean, all the vertexes are evenly distributed and they're all like there are no end guns or anything, but it's just I don't know. Usually when you see like production, wireframes and breakdown or anything is usually to squash like this. Like this would be a face, would would work perfectly in rigging. It would deform perfectly, and this would just be a mess. I'm not sure if it's a lot of it was going to have a lot of impact doing it this way, over this way in this specific course or in order to create this specific cool-looking sphere. But I just wanted to mention that this is usually the best way to create a sphere is by creating a cube, subdividing it usually like 45 times. It depends how much your resolution you want, of course. And then applying the CAS modifier, this gas modifier is actually essential as well because this is going to make sure that it's perfectly spherical if I were to turn it off. And the viewport you can see, especially here, you can see it's not perfectly round. It's still, it's still has that cube shape in there. But if I were to turn this on, It's a perfect sphere. It's a perfect circle. And all views. So all right, that's how you can create a sphere, but that's still not quite impressive. It's still not quite cool. The coolness we're actually going to get by decimating it. There's just a single modifier to decimation modifier. You might have used it before. This is basically to turn your hi-res geometry and low-risk geometry. So if we were to see it in action, you can see that we're actually taking a resolution away and you're getting closer to our ecosphere look. But we're not gonna wanna do it like this. This is actually quite nice if you want to be like, hi, police get into a little bit less sort of a resolution. So your, your viewport than your GPA, you can actually handle it. But that's not what we're going to be, what we're going to be using it for. We're going to actually set it to the planar type. And we can play with the angle limit a little bit. So that's just up this so we can get nice planar surfaces. And then instead of wanting to look like a, like a puzzle, we wanted to look like actual planes. So we're already done. We have a cool-looking sphere at this point. So all we have to do now is scripted. 4. Modifiers - Creating a modifier: All right, So let's actually go and our code and kinda structured a little bit. So when we, when we create a cool looking sphere, we want to do a couple of things. So we want to create a cube. We want to sub-divide it. You want to cast? Well, let's do verify it. I just apply casts and then we wanted to estimate it. So this is how we can get the planar surface. So let's start off with the beginning. So we'll do a cube equals create a queue. We are going to be using functions a lot through this, through this course because I know when the fundamental scores, I didn't really bother with functions and gold grouping. I just kind of put everything under each other and that's perfectly fine. If you have less moral scripts or just in general, that's perfectly fine. But I like to have everything split up in their own functions. This makes it a bit more, a little bit more readable. Semantic. Maybe even if you're used to like HTML, it's always good to use semantic tags. But that's besides the point. I just like having it be more readable. So so when I come back to this in a couple months or weeks, I can just really quickly glance over. It's like okay, so I clean it, I create a sphere. What do I do here? It is greater than the queue. This is basically just making these comments obsolete. So I'll remove them later because they will be necessary anymore because it's just the goal will be written in such a way that you can just read through it. And that's really valuable when you do programming in general. So that's just a little tip I want to give you. So when we do our create cube, we can write whatever code we need to create a cube so we can just get it out here. Let's also get there by, I mentioned that a pendant and the fundamentals one, I can just create a cube and then go in here, Lookup command was executed by blender and then just paste that in. So let's do that. I'm going to make it a little bit bigger for you guys because I do want to stay zoomed in because I know it can be quite hard to read text off like a video feed as you something like this. Ok, that's fine. We don't need much much viewports space anyway. We actually don't need this either. Let's collapse that. We do need this. I'm going to keep this open just to kinda copy paste back and forth. So we have a cube Now, the size is two. I'm actually going to change that. Actually. How do I know the size is two? Let me quickly show you. There is this thing called the blender Python API, which is like the documentation of all of our BPI dot whatever classes. So if we were to look at here, we were to get our function that we just put in. So primitive cube add. And if we were to look here, we would see there is a bunch of arguments that we can specify. So that's the same thing that I mentioned here is that you have like these brackets and inside you can add arguments which will be passed along through your, your functions are your groups of code. The cube add function actually has a bunch of these arguments, like enter edit mode, which we can see here and here as well, enter edit mode. And we actually have the same value. So you can see enter edit mode equals false. The way that this works is basically, is like I said, they're already assigning it. Well, this is like a default value. So we are not obligated to specify enter edit mode, like the argument. If we don't override it, it will just default to false. So basically what we're seeing here is just we're just repeating the default. So this is actually not going to have any effect. So I'm just gonna get rid of that. Same with the location. So location equals 0 000 default. So we don't need that either. Unless of course we want to put our sphere in a different location. So if I were to put, let's say 10, 10, 000, and I would execute this, we would see that our cube is created any 10 000 spot here. But for this example, we can not want to do that. The only thing we want to change their size and we want to put that to one. I don't know why blender defaulted to distribute to me, but yeah, you can just put it to one line is so that works out greater the cube. Now, one thing we notice is that we actually expect something to return. So I call this function and then I, I actually assign this to like the q variables. So since we use the brackets here, we will actually execute them. Sorry, this, this function, so this block of code up here and then reassign it to a variable. So we're basically going to need something to put in this value of variables slot. So we're gonna do that by returning something. So we can return anything like an empty string or something. But in this case, we actually want to return our cube object. So what we do is we can access this by doing VP of contexts. So this will basically grabbed the currently active objects. That will be the object that we've just created by our BPO or the ops primitive cube add function. So this will essentially just yield the cube that we just created. We will return it, and then it will just be assigned to our variable down here. So if I were to print cube, go into our system console to watch this output, we would see that we have an object and that is called q. So here as well, you can see that cube, that here we always have this be called Q because we always remove the current object, like all of the objects in a scene. So it will just be Q. Since it's a. It's like the default name. I think when you just add a cube and maybe you can see this hearing or a month. Well, doesn't really matter. Just like the name doesn't matter anyway. So this will just return the sort of just grab whatever object we just created and return it. And here we will just assign it to this variable. So at this point we can just referred to our cube with this Q variables. So that's all very convenient. So let's now actually get to the new stuff that we haven't seen before, which is a plying our modifiers. And so as it manually one more time so we can actually steal our little command down here, a subdivision surface, which is this guy here. Paste this in and we can see again there is a BPI dot ops method called this is generally bad practice and so we don't usually want to use BPO ADA, unless you want to create geometry, which is just kind of be the only way to do it just by using BP, why the ops? But besides that, we generally don't want to use it. So one thing we could do is we could actually replace BPA that ups the object with our cube. So we don't have to refer to our active object anymore because it's not actually stored in this cube variables since that's what we did up here. So what we could do is cubed modifiers, modifiers that I knew, and I could just specify a type in here. One more thing we need to do is to specify a name attribute. So we'll just call this cube, whatever. And I, we actually don't need this command anymore. So if I were to execute this, you would see, well, you wouldn't see anything really because this cube gets deleted and then UQ gets added. And then it just looks the same because we had the modifiers. If we were to just remove this real quick, to make it a bit more visually appealing. We get our cube, which has a subdivision modifier apply to it, which we can see here. Now the next thing we wanna do is we want to increase the resolution. So let's actually increase resolution. It just a bit like redundant these comments here, but I just want it to be easy to follow. So if you don't want to type them over, you don't have to. So let's add a couple of divisions. Let's do five. We can copy our command. Now again, we see BPI.com texts or object. So this is again referring to the current object. So we just have in our queue variable down here. So this is already better than using VP1 and ups. But still like you don't necessarily have to have such a long commands, you could just simple it down by putting cube instead of this. One more thing we could do is we could actually replace this whole this whole like grabbing of the mother firing at that we want to, we want to access by just assigning it to our, to a variable here. So we could just say subdivision modifier equals, and I'm just this discriminant to create a modifier. This will actually return the modified it we just created. So this way we actually have this object here. So now we can just replace all of this with this. So make it a bit more readable maybe, because what we had before, It's like, sure I grab a grab a mother fired and is assigned to the cube and it's called subdeltoid cubed. But then if I were to change this, I would actually have to change this here as well. Or I could create like another variable and put the name in there. But I'm at, it's just annoying, right? So we can just assign our, our newly created modifier to this variable, and then we can just use that instead. If we were to run this, we would get a cube is run at a couple of times. So you're sure actually what we do now is we just remove all the objects and every create a new one, which looks the same as the previous one. So it looks like nothing happened. But when you see at this this this line down here, you know what, at whatever you see in the viewport is the actual output of the script. So that's how you can verify that everything is working. All right, so now we have a subdivision modifier with five levels of resolution. 5. Modifiers - Creating more modifiers: Okay, so now that we know how to create a single modifier, we can basically just repeat the process for the other ones. So if we look here, we still don't have a perfect sphere, which we can fix by applying a cast modifier. So what we could do is we could just copy this line here as better than actually just assigning a cast modifier. And then this copying down here, we could use, use this still to get like the type well, modifier because it's not always exactly the same as we see here. Sometimes, I don't know, maybe this guy, you have multi Rey's instead of multi-resolution. That can be hard to guess. I mean, it's, it's quite always still like kinda represents what it is. But still if you don't know what, one thing you could also do is go into our Python API. And there is this BPI dot types of Modifier page. I'm actually going to remove tie. We don't want that. And then down here you have this type property. So let's actually the property that we said here. And then here you just have all of the possibilities. So you can see we have I'm just looking for one, but yeah, this is just all of them. You would have guessed. So you can see how that is written. If you were to type multi-resolution, you would see multi-rate. That's the actual name that was used by blender. So yeah, you could you could get it that way as well. It doesn't really matter. So we will sphere phi it down here. Just copy that color cast modifier, make sure to type is correct and the names correct. So you can't actually have the same name twice this kind of conflict. So that's that if you were to run this, I don't know if you saw it, but third, a little bit more. And to his fear, we can actually toggle it down here to see more visually else's showed it in the previous video, so this should be clear. And then lastly, we want to add our decimate some other fires. So that's actually, you would add as well, That's a mate as it's called decimate learn here. So let's copy it, paste it. And then just make sure everything's correct. This will run it, make sure it works. So we have our decimation modifier as well. Now we want to do a couple of things to this guy. So just like we set our levels and here, we can set all of these things in here as well. So let's set them manually. So we did three things. Now we don't here we see three things. 123. Earlier we saw that we can just replace all of this, this grabbing of the modifier based on the name. We can just replace that by our actual modifier variable. Boom, boom, boom. And just like that, and we run our script now. Let's remove it to make it more visual. We will have our custom piece of geometry. So this will be our geometry with all of our cool modifiers applied to them. One thing to note as well is that we have to specify radians. In this case, you can see we get entered 20 degrees with this is going to be converted to radians behind the scenes. So that's something you want to take into account. 6. Modifiers - The match function library: And allow me to go on a little bit of a site route and just quickly explain how we could put this in degrees instead of radians, because it might be a little bit just hard for people to wrap their minds around zero-point 35. As an angle. It's kinda weird, not, not a lot of people think irradiance. These I don't. So I just want to clarify for the non math reserves like myself. So this is going to be a little bit of a recap from the fundamentals series. So if you didn't watch that, this is going to be quite helpful. I think if you have watched it and you don't care about the Python interactive console or you are complete master of it, then you can just skip ahead. I'm just going to go quickly into what the interactive console is again, and how we can use it to our advantage. So the main, let me just quickly say what it is. So it is an isolated Python runtime or you can just execute commands. So this is really simple. You could say print hello, and you get instant feedback. So this is really good for single lines of code are like single blocks, of course you could do for I in range 10, print this print. Hi, So like for really small blocks of code that's not reusable. Well actually you could write a function in here and I, you could call it in the console. But then once you quit out of your console or you quit out of this section that you won't be able to access the function anymore. So it's just really, it's like a sandbox environment where you just do some testing. And then once your code is proven to work and move it to the actual scripting view down here. So now you know what it is. Let's quickly go into how we can use it. So one thing I wanted to do is I want to figure out how I can turn degrees into radians. We're allowed to look up a formula and implement it myself. So one thing we could do is we could use the math package or like the function library, which is, does this ships with biotin. So let me quickly see what this gives you. The address everything. Let's not do that. So yes, map just ships with biotin. So this is a built-in package. And you can use a bunch of things, like there's a bunch of things in here, so we can expose those by running the direct method with math. This is basically all of the functions or properties that are inside of the math function library. So let's quickly show you one of these. So like how these work. So you see pi here, I can just do math.pi and that will work. One thing I could also do is like math dot square root, square root of nine, because that's an easy one, right? So we can all know that. So this is just really easy to do like basic math functions. We can just implement it. We can just import math as really lightweight. It's not going to make your program any slower or anything, so you don't have to worry about that. So one more thing that I see, Cause gets weird my eye is radians because that's kind of what we wanna do, right? So we could invoke the method on it. So that's what helped Math.pow gradients. And we can see what this function is actually doing. So the built-in function, radiance in the module math, it is to one that we wanted to target. And this basically converts an angle x from degrees to radians. So that's exactly what we want. It's not going to be this easy. Every time. Sometimes you really have to dig through or like go into the documentation and find the method that you want to write a function that you want to use. In this case, we're lucky, so we can just do math on radians. And this expects an angle x, see Arabians. And then they're like one argument down here, which is x, which is the angle. So that's a myth. The irradiance of 20 degrees is 0 dot 39, 0, 6, which is exactly what we have here. So we can just replace this number with this very readable, much better. Well, this is just personal preference. I just wanted to show you the console maybe. And I just think this is a bit more readable as well. One more thing we have to do to make this work It's important math. Because otherwise we won't be able to access this radiance function inside of the math module. So even though it is ships with biotin and this is like part of Blender even you still have to import it just like BPI you like It's always going to be available. You don't have to install anything, you just kinda have to bring it into your scripts in order to use it. So that's just something that you cannot forget. 7. Modifiers - Collapsing modifiers: All right, so for the guys who just skipped ahead from the Python console, productive mapping that I wanted to explain. All we did is just replace r is 0 that 35 with the metadata radians or 20. So yeah, if we skip that just so that you're on the same level or on the same line, whatever makes your import math doesn't change anything about the output. Just a little bit cleaner. Okay, so we're actually done. One thing we could do is we could apply these modifiers. So now that now we actually have this tag, so it's non-destructive, you can actually chain this up even though we have already generated this. You could limit this. So you could not, not have this be a thing by applying the modifiers. So let's quickly go into that. How you would do that. So I just did it on a random one just to get our, our command. So we could just use this down here. This is also a BPI dot one. So not usually the best, but it's fine. For now. Let's actually see if we can replace this with a cube. We should be able to do that as we would for all of them in order as well. Let's maybe actually put them after the actual creation of it. So let's sort this is all subdividing stuff. Maybe f casting. And then we have this amazing. So now it's all structured. So if we were to run this mass and I'll work, this is actually going to have to be replay the obstacle, protect them. I didn't think it was going to work, but it was worth a shot, right? Whenever you can avoid byui.edu. Oops, It's usually best practice to do so. You know, in the cube there is actually no modifiers anymore. So we can see the all just makes it to the object. She were to just not do it for the last one. We could see if we were to go into edit mode, we would still see the actual topology that we had before. Before to decimate modifiers. So this is actually not applied. And now it is applied. So now we actually have this says as polygons faces. So usually this is what we want. Also like if you wanna do it sets or if you want to continue modeling on the output over the script, then we're going to meet this. Free don't apply or modifiers and this is not going to work. So even though it is destructive, it does add new functionality. So it's something to think about. Maybe in your specific use case, you don't want you to wonder like collapse all of the modifier, that's completely fine. I just wanted to show you how you did it. One thing to note is if you are using Blender to 0.9, this syntax actually changed because they didn't all rework of the modifier panel dot here, Here's into 0.9, you probably know by now. So you're going to actually have to use a different command which are going to be, all right now which is BPI level still object though. David, modifier apply. So it's actually the same command. But all we need to do is specify your modifier and not are apply heads. Because this is the actual thing that they changed, isn't that like shape keys or something? And actually I haven't used them myself. I do have an installed 2.9. So let's actually check this out. So if I were to run this here to, there would be no problem. But if I were to actually, let's browse to remind me because a is mapped to the 2.81. So one I've been using blender Foundation to 0.9. So yeah, I was on my second screen. I'm sorry, I didn't follow. All right, so this is Blender 2.9 down here. Go to the scripting tab and new displays, whatever we have. And if we were to run it here, we would get an error message. So if you were to, toggle the console, you would see keyword apply as unrecognized. So you can see converting my arms or your frequency, the line 23, which is this line exactly that I have, is not recognizing apply as argument. So if you were to remove it here, let's not say we would see it's still crashes but now regresses on the next apply. So it actually passed here. So if we were to look at the cube, we would see that our our subdivision modifier is actually applied. So does this work? So if you were to just remove apply as for every applying instance, run it now we would see there's no errors. And it looks exactly the same. Hard. You just turn the wireframe on. Boom. It's exactly the same. It's even a little bit cleaner. I think it's important I I should be using 2.9 colleague I shared man, just keep updating blender men if they've been putting out so much like awesome new updates, every version of it just gets so much better. So I've been stuck on 2.81 for a while now I should actually update. This is actually the only, I mean, not the only bird-like. One of the biggest updates that they did to the Python API is the modifier stack up, like applying a modifier. So make sure that you catch this. If you were confused and angry at me that it wasn't working 2.9 then I hope he stuck through an LP that to the, to the answer might actually put like an overlay or something because it might not be clear. Yet. This is how you do it in 2.9. So a little bit different from 2.8. Just don't have apply as it got removed. Function into 0.9. 8. Modifiers - Cleaning up the code: All right, I'm actually going to stay into 0.9, maybe LF, I'll make the official change now and just stick with 2.9. But you might have, you might remember that the beginning of this course I mentioned that I wanted to keep all of the code clean. And I started doing that. We then I just stopped doing it. Maybe one thing we could try is to clean this up. I wanted to give you guys the opportunity to try this out yourself. So basically what I want you to do is just we group this gold, however you think is just optimal over you. You, which group it if you could choose. So just be free. Check it out. How we can divide this up into smaller chunks of code, make it a bit more self-explanatory. Basically tried to make the commons obsolete. So you see your create a cube and then you just call the function create QC. This comment doesn't have to be there, it's just redundant. So that's what you, what you should try to achieve is to not have to have any of these comments anymore. So if you want to try that out, now is the time. We'll do it together with you guys right now. So if you want to try yourself, just pause the video. You can still come back. I'll just do it. Why I think it should be done. This is really not like set in stone or this is not like an exact science. Just any way that you clean this up is perfectly fine. I'm just going to show you how I do it if you're interested. So let's get to it. Less chance to pause or get out of the video. All right, so let's do sub-divide the five. And then we will actually pass in an object to sub-divide. Because this way it's a bit more modular. We don't want to make Cuba global variables. So this is actually already getting a bit more complex with functions. I'll quickly explain. So this is called the scope of a variable. You might have heard about this before if you have just the same as in any other programming language. If you haven't to the scope of a variable is basically the the environment in which a variable exists. So since my cube is declared, so it's being assigned for the first time. And this creates cool sphere function. This means that my cubic and that only exists inside of this function. So if I were to go like create a loop or create something else that is inside of the function. I will always be able to use the cube, the cube variable because it is accessible within the function. But if I were to try to access cube here. So if I were to do cube, like if I were to just copy this whole line, this is not going to work because the cube is not going to be accessible in the sub-divide. Function. Sub-divide is I got to know what a queue is at this point because it's not been signed. All it knows about itself. So everything that is inside of this little quote group, so nothing because this is the first line. And all of it, all of the stuff that is outside of any function. So there is no any, there's no, he had no cube that I can refer to. So this is not going to work. Well you could do instead is use this argument on here, which is a way to actually pass one variable or multitude of variables from one function to another. So what I could do is I could say sub-divide. So I would call this function, and then I would pass in my cube. So this cube will then come in here and replace this object. So at this point, object will be equal to. But by doing it this way, we could actually like down the line, we could create a sphere, a cylinder or whatever. And then we can subdivide it by passing in the object. So this becomes a bit more abstract. So it's less bound to a cube because this is just subdividing. Subdividing has nothing to do with a cube. It is just taking an object. So this is a rudimentary objects. So that's how we, how we can keep it abstract. We could also pass it a name attribute maybe to make it a bit more abstract. So we don't want extract to like a cube because we don't know it's a cube. It could be anything. And we also don't want to have duplicate naming. So if we were to call it again, well actually render is going to handle this. If I were to call this function twice, the second one which is b dot 000 001, just like we do if you'd like to duplicate an object, it will just get dot 000 001 editorial. Well, never actually be an exact copy of the name. It will just add a suffix to it, but we don't want this actually read 1.5 control over the name. So let's put this in here. Like so. There we go. So now we've actually replace this line. Well, we still want to have access to it. Well actually what we could do is we could actually use, but the levels and here as well. Say my separative modifier equals this modifier at all levels equals levels. So now we would do. So this is already in here and now in my levels to them here as well. So I can just add five here. And that would work. Now I don't want to have any anymore reference to this modifier. It might create cool sphere function because again, same, same issue with the cube is that this is now bound to the scope of this function. The subdivision modifier is assigned for the first time at a sub-divide function, meaning that is will only be accessible in the sub-divide function. So if I were to try and print subdivision modifier, and here it's not going to work if I were to print it. And here it is going to work because in here it knows what a subdivision modifier is. Whereas here it has no clue. Like sub their modifier is nowhere in the function, It's nowhere outside the function. So we don't know what that is. One more thing we could set here is like an apply, a Boolean. And then we could say if apply, then I want to run this code and instead of using the string, we want to use a name. This way. We have our sub-divide function completely abstract like this. Now what we could do, so moving on, we could create a function for scarification so reduced that it was favorite phi xy over you could do now. So I've showed you one example. So I really want to stress again that you should try this yourself. This is a process that you really have to practice will make perfect. So I just showed it. I'm right now the other two are going to be pretty much the same. So if you could try it yourself, there is one more optimization I'm going to make. So this is a little bit of a over a tricky one to see if you can figure it out. So there's still not just copy and pasting the first abstraction and are like the first function. There is still one more step that you could figure out yourself. So I encourage you to try that out, to look for it a little bit. But I can just come back here when you feel like it. So that said, let's continue. So let's do a sphere phi. If you've got a copy, it's here because I don't know how to write sphere for lowercase. That's just nicer. And blender. I'll just copy arguments, but I don't want any. And the levels here, I don't need that sphere phi or you want to do now is we want to apply this to the object name equals name. So that's just to save like we do here. We can actually, well, there's no real need to keep track of all of the modifier objects because we actually never use it. Guess modifier, it's never used again. So we don't really need it. The only thing I do need is this. If apply. So again, I drink, I don't just always want to apply. I want to give the user the freedom to either turn this on or off. So let's put this in here, just make sure that the main match. So this name should be equal to this name, which actually is not going to be always the case. So if this name already exists, then this is going to be done 000 001. And, and this is actually not going to be dot 000 001. So this is going to remove the initial one. So this might be a little bit abstract for you guys. I'm thinking if I should show you this in an example. I'm not sure this is going to be very efficient. So I'm not going to do that. We will actually use a variable so that it will cast modifier equals this. And then here we'll just do cats modifier dot name. And we will just save here. This way if our name gets altered in any way by blender behind the scenes because we don't always have f control. What we could do is we could check it like the list of modifiers, so make sure that this name doesn't exist. And if it does, we could change it. And then we could put change name and here as well, which is just a lot of extra work. Basically what we do here is we recreate a new modifier and we tried to put whatever name that the user defined as the name of the modifier. If it didn't work, it didn't work. Let's find the modifier still going to be created. It's still going to be stored in this variable. And then basically using this property, this property, we can just get the name of the current modifier. So the actual modifier, we can just get the name directly out of it so we know that this is going to be the name of the newly created subdivision modifier. It get a deceptive modified at which is created with this line. So that's a bit abstract. And I'll rewind and try watching it again. And you can also just What happened? I type O. You can also just send me a message. I can maybe record like a separate video going a bit more into this. Because it might be but unclear if you're not used to it. But okay. This should be a bit more bulletproof. So I do this. I don't have to set any properties here, I just have to call it. So there's your sphere with Phi cube and call it casts. Hello, I want to do that sort of as true. I probably have to do it here as well. So level supply, true, or you could do like we saw with the API Docs, you can set a default value for all of them actually, let's actually do it for levels and 4 Apply. So let's do Levels 2, 3, and it will apply to true. So this way if we wouldn't do this, it wouldn't break. So I'll just do five. Good. I want to be I want it to be quite high resolution. And here I just don't wanna, I don't wanna mention it. So let's put it here. It'll sort this way if there's no apply in the calling of the function, just use a default. Do note that we too need to put any of these default variables are like default arguments that have a default value assigned to them. We have to put them last. So you can't have applied equals true. And then let's say like levels or something that is not going to work. So blender, like the the text viewer is not going to give any issues with it, but when you, when you try to run it and it will. So if you have like default designing, always make sure that they run at the end. So no, no argument that has no default assigning comes after it. That's like the main thing I want to, I want to stress here. That shouldn't be a problem. Just try to follow this order. Arrives with fear of phi we have. And then we wanna do decimate a cube. And let's set the degrees, Let's less bass and the degrees here to give you some, some options. So decimate objects in the name. Degrees apply equals true. Let's copy this because it looks more like this than it does. Look like. It looks like the cast modifier. So let's copy this. This is the actual command. So decimate name equals name. This will read this modifier. Well, we don't actually want to do this. If apply, I want to set my decimate modifier dot name. So while you grab the, the summit instance, this is the applying. So then I just need these three boom, estimate modifier gradients. Okay, So this is basically just moving code. Sorry for I'm not talking much through it. Yeah, it's just moving. Like you don't have to specify a name. So now you know that you might have noticed that I'm not running my code often, which is actually a mistake on my end. I should run this. Every time I abstract into, into one function, I should run it to keep running it to make sure. Because right now I could have broken something in the first and the first function and I could have copied that mistake over, and now it's only three, so that's not that bad, but it was like 20. Hey, you really want to make sure that you that you do that, that you run it often. So actually let's just try it. And of course, it doesn't work. Just like I said. Keep making my own mistakes. Mud, fire. They had that's not gonna work. Like so let's see if that worked. Okay, so now we're actually run just fine. So one thing you might have noticed, so this is the extra mile that you could have gone if you would do this on your own. Is I have a lot of similar code here. It's actually always the same. The only thing that changes is the, is the name. So what I could do is I can abstract this as well. So I could create a function that has apply modifier. And that just takes in a name, sorry this pass as a placeholder. And then I just put the name here. I would do apply. And then I would just apply modifier and then I would just pass in the name of the current modifier. So current modifier dot name. This way. Let's say down the line. Or even when you want to, want to switch this to to to 0.81 or two, or three or 2.9. All you have to do is just change this apply modifier code ones. So you can actually get the version of Blender. And I'm based on that. You could either run this or you could run the apply as think it was data. So you will have to run this if you were to 0.9 and you would have to run this in 2.8. But now you only have to specify this. Once you only have to execute this command, you'll have to type this command one time so you can execute it as many times as you want, but you only have to type it one. So let's say a changes over time or actually you want to implement different logic to apply a modifier you who want to do something before it or after it or whatever. You don't have to go in here and do it like every time. Usually you only want to execute a command. One time, like, like a proper BY command. Of course like a function you can, you might have to call it 10 times, 20 times if you, if you can't do anything about it or you could even do is have like if apply the whole time. You could actually do like checking the apply modifier, but that's kind of kind of redundant as well. You could even create a method that says check modifier, applying or whatever. This is really bad. Grammar or, or like words. But you could even do it like this. And now you don't need any of these if statements. And I can just do the if statement once. Let me actually show you what I mean. Returns over time. It depends the correct me. Create a function that check that for apply, apply name. And I can say F apply, apply that fire. So it looks it looks so silly. I understand. But honestly, man. Yeah, just and bad practice to repeat yourself. Even even though it looks dumb, it's a lot more efficient down the line. So now I created this whole thing and it works. And then I spend like 10 minutes to write to make it more readable. And you could argue that that's like 10 minutes wasted because the program was working just fine ten minutes ago. Yeah, ticking this time might actually save you time later. So let's say you have to, you have to do this a bunch of times in the future. You can just come in here and copy paste. This does verify function and this is just always going to work. If you like. This whole structure now is just really procedural. And the way this is, this can be called from different places with different objects. The way we had it before was really restricted to the current use, your use case. So if this is the only use case that you're ever going to have for modifiers and you know, just fine, just leave it the way it was. But now this, this code becomes reusable and it becomes easy to manage because there is no repetition in it. If you want to check modifier, apply. If you want to change the logic in air, you only have to do it once instead of before you would have two. And each of these in each of these guys you will have to check it, change it, make sure it works. Whereas now centralised checking function and you just do the checking in there and it can't be any more obvious. You could also argue that yet now you only have three modifier, so it wouldn't take that much time to, to change it three times. But let's say you have like a complicated project. There's like, I don't know, 50 modifiers in there. Let's say you use like ten of them, even as this is ten times a redundant change or like a small change, it adds up in time and just doing it appropriately, directly. Like Now I know why I actually created it wrong first and then it took me some time to make it right. But, but after a while you will just create it right the first time. And it will be easy to manage From the start just by default. So that, that's actually going to save you a lot of time down the line. So I didn't want to, I didn't want to stress to them mentioned that. So now we actually wrote a reusable tool that's going to save us a little time in the future. And that's why it's why we automate stuff, right? That's why we script is to save us, are self-styled. So you as a developer should, should save yourself some time as well, not just a modeller. 9. Procedural Rocks - Introduction: Alright guys, and it's part of their course. We're still going to be using the modifiers. We're going to dive a little bit deeper and apply basically the same knowledge like we saw before on to a completely different use case. So we're going to be generating some rocks here. We're also going to go a bit more in depth than just the modifier closer. We're going to start off with the modifiers, but then we're actually going to dive a little bit deeper into generating the specific textures for our displacement and also offsetting the vertices, which you can see a little bit of a sneak peek here, but it's actually quite fun. You can really mess around with the geometry. That's where you can really displace it on multiple levels, which are, which allows these quite interesting results. A kid can see here. 10. Procedural Rocks - Creating a rock manually: All right guys. So I went ahead and empty. Love my scene. This is just completely empty. It's just a blank scene really in which we are going to be creating our rocks. As always, I'm going to go through the process of doing this manually first because we can't just pull CO2 out of thin air and and, you know, make it generates stuff. We still have to know how stuff is built before we can actually generate it. So for this, I'm actually going to go over to YouTube and shut out this guy, CG geek. He's a, he's an incredible blender. Youtube tutorial guy does all kinds of stuff like general VFX stuff as well. We are going to go over all channel, but he's really cool, is really entertaining. He has a lot of these quick tutorials like making a tree in one minute. That's awesome. Just like this guy here, how to create low poly walks in one minute. So it's basically this methodology, this workflow that we're gonna be automating. We're going to be scripting whatever he's doing in this video. So all of these steps he's doing right now, you're going to see me do in exactly one seconds. I don't want to put that off because it's going to be boring to watch me after this because he's a he explains it a lot better than I do, but I'm going to go over it. So you guys so you guys see the full the full thing. So if you don't actually care about doing it manually or you want to watch his his one-minute video, I suggest you do that. I'm going to go into a bit more detail because I still assume you guys aren't that familiar with Blender and he goes quite fast before 40 guys who like that, I can really recommend this video and this guy's channel as a whole. All right, so let's do it ourselves. No. All right. This is always the hard part. Seeing some guy do it is they are, This is fun, but then when you have to do it yourself, it's a whole different ballgame. So let's start off with a cube. Just like psych always, really always want to start off with a cube. First step would be adding some, some definitions, some, some geometry to work with. So we're going to subdivide it. It's going to use five. So we have quite some, some geometry to work with. This is not my work is not a blight, but that doesn't matter. The next thing we're gonna wanna do is decimating it, displacing it actually, I'm sorry. So let's look in here displays. And we want to have a bit more control over our displacements. So before we could just displays as randomly from the normal which we don't want to do. We actually want to add texture, which you can do by adding the plus button there. And then if you go down here to this checker, checker pattern icon down there, you can actually edit your textures. So there's all my shit, quite some here. Usually there should just be called, whatever this is called here. So here it is called texture. So you should look for that one. It should just be black at this point. So you should be able to find it quite easily. And then we want to go ahead and edit it. So the type is Voronoi for this, for this example. So we want to have a Voronoi texture which is going to make our stuff blow up like this, which is actually a good thing. We want to change the distance metric to distance squared because that's just a different algorithm of the Voronoi. You don't really have to worry about that. And then I just playing with the intensity and the scale. So since we want to have quite big cubes up at this scale, quite high. And then we can just lower the intensity a little bit, kind of looks weird with these dense. So maybe up this a little bit more. Well, this isn't really too important because we're going to randomize this anyway, but I just kinda wanna find some nice base values to start off. So let's just say this is fine for now. And then one more step which we can do in a modifier section is decimating. So decimating is going to give it like a little bit more of a rough feel. And it's also going to reduce the polygons by lot without losing that much detail actually. So if we're looking at it, it kind of breaks up the edges. And then with your shader or whichever material you can that you can address that or even that was actually linear thing. The Voronoi is quite aggressive on this level of detail. So we can maybe give it a bit more subdivisions. Here. You can play with that. I'm, I'm not really bothered about the edges looking at, but there's plenty of ways to fix that. I'm not going to be covering in this and this and this Coppola's video. Alright, so let's call this starts, maybe let's go a bit more in depth. Just following this guy is up orders actually the CG dictatorial. He then started moving around these vertices which allows you to really break up the shape. And the thing is like when you do it manually, this is like I kind of thing I should I do should I read that? You don't really know? So I don't usually like that one. Just modeling in general, I just like my PC to fair this decision is randomizing stuff. I'm not good at it. I don't know where to move it. I mean, sure. You have like the art direction, but I'm just messing it up. But as you can see here, my my January there were hawks were actually a little nicer than this. But yeah, this is just basically to Canada, the, the way of how this is done. And then the values are actually, well, what makes it, what makes a good look in, right? So that's what we're going to be programming. So we don't have to figure that out ourselves. 11. Procedural Rocks - Project Setup: Let's start and the programming field, or I'm a bit more comfortable than in the Rock Building fields because in this folk insects, but yeah, that doesn't really matter. All right, so as always, we're just going to be starting with import the BWI. We're going to clean our scene. So every object in our scene is going to have to go right out the other objects that I remove, every object. And I would just have to call it to make sure it actually gets executed. So we're gonna get rid of our ugly-looking rock here and we're going to put a nice one instead. So after we have cleaned our scene, we will create a rock. A rock, or in three will creates a rock. Here. I like to sometimes just print this up to make sure everything's working and everything is getting called correctly. So at this point, the basic setup of the scene you should have, we'll create a rock here. We can print it out with this setup and every object should be deleted. So this is like differs little milestone of our project. Also, like I mentioned in the previous chapter about modifiers and about refactoring and all that. I'm actually going to be starting to build a project in a structured way from the beginning. So we don't want have to have like a whole, then men is dedicated to refactoring the code. So I'm just going to start off with good practice. And then you can see as well that it actually doesn't take much more time to structure your code from the start. 12. Procedural Rocks - Modifiers: So let's do that now. So creating a rock, as we saw before, we had a couple of things that we had to do. So we had to create a cube, Riyadh to sub-divide it. We had to actually remove it already ship. That was kinda my my red thread throughout the thing. So what I want to subdivide it, then I want to replace it. And then I went through decimate it. Those are there for an hour. We'll figure out the vertices later. And so the first thing we have to tackle is creating the cube. So this is really how I like to work. I just kind of boiled it down to these big steps. And then every big step that is not implemented already, I just kinda go through them one by one. So the first thing that we have to solve is creating a cube. They've done this plenty of times before actually. So let's check and get it again from the womb. And let's just use the default implementation. So I'm not going to spend too much time here. This is stuff we've covered plenty of times before and you can just look this up on documentation. I can assume that this is clear. Why, you know, when the info panel and how I how I put that there and this is my phone being annoying. Let me quickly turn it off for you guys. I'm sorry. Right? So we have a cube that's going to be created. We still expect a cube to return because when we just call this function, we're going to create the cube. We actually want to get a hold of the cube so we can use it later to add our modifiers onto. So what we wanna do is we want to actually return something which would be the object within the context. This should work. Okay, so divide is not defined, okay, So this is actually the arrow was, I was hoping for. This means that a gut to line 17 before crashing, which is this line. So that means that our line 15 got executed just fine. Which actually makes sense because there is a cube there. That's got an issue with removing the stuff all the time is that you don't really know if if something worked because nothing changed. So yeah, there you go away, work. But we could actually know from the, from the output to the console. Yeah. Okay, so sub-divide is not defined. So let's define it. Sub-divide the expects an object. And now we want to create a modifier for it. So Objects modifiers, dotnet, new type will be what is it? Yeah, I don't I don't know what he's doing that to y on the top of my head. So let's actually figure it out at the modifier subdivision surface type pico subserve because this is a and some programs it's called sub div and then another program that's called subserve. So something we have to reorder that you really don't want to be studying this. So equals observed and we always have to put in a name as well. So that's actually fastest and our arguments on top as well. So Subdivide, let's call this sub-divide the fight. So Q will go into the first slot, which would be object, which will be used to add a modifier to me. And then the name will go into the second position, and then that will be used as a name variable here. So if I were to just run this with this recording, these things should work and we get a subdivided cube right there. So we don't just want to simplify the cube, we want to add a couple of levels. So as you can see here, is, is the levels property which you can just set like this. So instead of just copying this real covered this in the previous, in the previous section, right? So I'm just gonna not spent too much time on this. So the modifier will be whatever this stuff returns, and then we can just set the levels to whatever we want. So there were five for now. And let's also include this in the parameters. Just who we are, as modular and as decoupled as possible. So sub-divide, we give it an object name and then a levels. Now let's go this actually let's run it. We really want to run our code so many times when this is a mistake I'm makes too often is I just gold, gold, gold, and then I run it and then I have to fix 20 things. So I always run like radical lines and run it, radical prolines and run it. You really don't want to write too much without running. All right, so let's implement our displacement method. So we want to have an object. We're also going to want to have a name and we're going to want to have a texture, but I think I'm going to generate a texture in here. That should be good. So let's call it this place. And then let's at our modifier. I'm going to take a guess here and thickness is called this place. So if we were to run this that worked, we got our displacement texture displacement modifier. So we still have to add, added texture. So if I were to click here, we provide a texture not new. This is a BPO. I don't, I usually don't like me provide a UPS just for creating geometry in here are usually not best practice, but I think there is a BP write out data load textures as well. So let's actually check that out. Let's maybe do BPI dot data dot textures, and then just print a third of that. So does this dirt. There's just going to print whatever is inside here. So I really hope there is something inside. Just seems like it is. So this exists. And then what we can do with it, we can do new and remove. Yeah, this is basically the same as objects. So we can just create a texture right there. 13. Procedural Rocks - Generating complex textures (1): So what we could do is be PUI or data next year will be brighter. The other textures up new. Let's get the help on that actually. Okay, that's not really how four letters a final year calls that function repr or not. Okay, so there's no help on this. Should I do this? Parameter name not specified, okay, so it needs a name and it's winnowed out. I could look this up in the documentation, but I don't feel like it. Legislative Voronoi. See that works. Type is not specified. Well, as long as you read the error boys, you never have to. There we go. Okay. Well, that's not really the best practices. This is the look that up. Actually, I'll show you how. So as it will be PY, blender, Python guy, whatever. Google is usually pretty good at figuring out what I mean. And I, we could do add texture. Probably. Well, we already know the methods so we can just copy that, put that in New Zealand. Textures. And just like some 2.91, stop. Yeah, okay. I guess this is the one sort of blended textures and then it needs a name and a type. And these are the possible types. So let's actually nicer we can have. So these correspond with all of the, if I were to do it manually now, to all of these these types here. So this is the whole list and they are listed here as well. So everything that you see in the UI is mapping one-on-one with the API. So that's really cool about blending. You can actually do everything with API, everything in tobacco ended Despite and functions being executed. So that's something that's good to know everything, everything you can do. So as you can see here, Voronoi is the type you want. Make sure this is also in caps. They always do like to scream their types same with the, with the modifier. So that's something you kind of have to get used to. So this should actually create our texture. So if you could see there should be a Voronoi 400 texture, which is not mine. This is the previous stuff, so these are the ones that I've been making by running the script now a bunch of times. Let's actually get rid of them as well. So I have like 500 and textures. So the thing with this cleans with removing all the objects is that it will just remove objects. But like we saw here, that BPI dot the other textures is actually exactly the same. Like a works exactly the same. So I can just replace that and then just the texture here. And this is going to clean up all of my textures. So if I run it now, we would see there's only one texture here and it's called voronoi, which is what we call it down here. Always going to have to be careful when doing this because this is going to remove all the textures in the scene. So if you want to generate a rock in an existing seen, then you're seeing is you're going to be storing the ship and then there's going to be a rock and there, but you're going to lose all your other stuff. As we're going to have to take this into consideration. But since this is like an isolated scene just for the rock and also because we're developing it right. Once it goes into I mean, I don't know. Yeah, once it goes into production, let's say let's treat it as a production script then. You really, and you really don't want to be doing this stuff. But for developing, it's really nice. So yeah, we just don't want to end up with 500 textures in our scene. All right, so the displacement is looking quite nice. We're having our texture being generated. Let's actually stored it into a variable. So let's say texture this. And now we still have to assign our texture to our modifier. So if I were to run that now you can see there is a displace wondering where do to subdivide goal. So I am still subdividing it because it's smooth. But it's another and this is like, oh yeah, I mean, I'm watching them, sorry. The mistake. I was looking at the texture panel. So this is a modifier bilaterally. So we have our sub-divide here or displacement here and I, we can just assign that Voronoi do it. So it didn't actually give you a, give me any output in the info panel. So the info panel is not always super useful. Some things are just not in there. So you're gonna have to get used to that. But there's other, other methods of finding out stuff, right? So using the direct method is really, really something I like. So to figure out what we can do with a modifier. So this modified object that we just got back from this function I created. So this is basically just a misplaced modifier. Variable. And then this points at this particular one. But if you run the dirt, the dirt method with the modifier in there, you'll see what you can do with the modifier. So it's a super complicated explanation, but just when you do something, you get what you can do with that something. So I hope that's clear. So these are all of the things that we can do with this displacement. Displacement modifier it on here. So you can actually see there's a tie. There is a texture, which is probably the one we're going to want where you can, you can verify this so that you have strength. This is going to be this guy String. Usually if you hover over them, it doesn't look like I have to buy them tips available. That's actually show you guys how that is done. If I can figure it out. There should be some preferences here. And then there's bite Tooltips there. You're going to want to check that one. So Edit Preferences and the interface and the tooltips. You want to check Biden tooltips because that's going to help you when info cut you short. This is also going to be like if I were to hover over this, you can see, like I can show you what the mouse, well actually let's, let's, alright. So if I were to hover over this, yet, There you go. It might be a little bit small, but yeah, you can see BPI dot data objects cube. So this is basically a reference to our cube. So this will be OBJ, right in our function. And on modifiers sub-divide, which will be a link to this, to this modifier. So if we take this whole, this whole thing, this will be modifier, right? So in this function, so I'm not going to write a dog. Mom, I was as a, as I've modified dot levels. So this is also how you can figure out what this property is called by this kind of reading. There's big, all those big online. So we can do the same with this. So BP way up. So to actually add this is going to create a texture. But if I wanted to assign it to this going to help me, It's not. Well, anyway, with the direct method, we actually found out that we can just have the texture property. So we could say modified architecture equals texture. And the only reason why we can do that is because we have this texture object that we just created. One more thing you actually, you have to win. I'll mention this. If you select stuff in your console, your whole blender is going to freak out. You're not going to be able to do anything? Yeah. I can move around with no, I tried saving it and it just got stuck because there's stuff selected in the console. So you just kinda have to click or hit Enter and then, and then your stuff is going to come back. So it had sometimes you can get it. Thank you. You're correct your shit. But actually you just need to have stuffs that ecstasy to have to come in here and press enter a couple times and then, and then you're back, right? But that's beside that topic. All right, so we have our texture that is being generated. And then we have our modifier that is actually taking the texture as its texture. 14. Procedural Rocks - Generating complex textures (2): There is a couple more things that we need to do to this texture like we saw. So this is, instead of having to come down here and then changing all of these, all of these guys down here. I actually want to, we want to script that as well as what we could do is we can just say texture equals create voronoi texture. And then we can actually make that into a method as well because we don't want to have too much texture stuff in order displace function. So create for noise texture and then we want to get that back. So we're going to want to return this as well during texture. And I'll, this is you want to be exactly the same like we had it before, but just a little bit more abstracted. And now we can actually do some stuff to our texture and the texture function without having to do it in its place function. So that's a bit of refactoring done already. So one thing we wanted to do was change this. So if we look here, the property is called distance on their score metric. So again, I've got moved my mouse because everything is going to be gone. But you can see all the way on the bottom, it says BPI dot data dot textures, voronoi, which is the current texture, the distance metric. So we can just say texture, texture, dot-dot-dot distance metric. And this one is set to distance squared. Now again, this is going to be caps. This is W. You have to look up in the documentation because I don't think this is actually oh, yeah, there we go. Now it does show up in the in the info panel. So usually, I mean, I would say eight out of 10 of the stuff that I tried shows up in the info panel, so it's definitely worth a shot. So now I can actually see the same, the same Biden tooltip as I could see. And the hover, hover thing. But I could just see it down there and I can actually see the value that's got entered. So can even just copy that. So this is a reference to our texture. And then we just have the same. It does use single quotes. I know I use single quotes in the last video where I usually prefer double-quotes for Python. But yeah, it doesn't really matter. It just at other, other languages have double quotes for strings and single quotes or characters. So it's a little bit confusing. But anyway, now if I run this, I'm going to have a Voronoi texture which is assigned to my displacement modifier. And then if you check that out, that is set to distance squared. So what we're actually already quite far, there's one more thing we have to do is set the intensity, I think reward. Let's clear out some nice value again. So yeah, this is kind of playing around with this stuff. Let's not make it too crazy. So we won't be some trial and error just maybe is a bit too boring. Actually, it's not that bad because we are going to be offsetting the, the virtual, OK. What? We've got to be upsetting the verbs as well. So we're going to create a bit more randomization in our shape anyway. So I just quickly went out. Yeah, I think this is not that bad, so this isn't a density of point to just hard code it for now. So intense, t equals 0 D2 and then my texture dot size equals 1.18. And I would actually be confused. This is actually called size. I know blender is usually not one to name their label the same as the actual, like the underlying property. So this is called noise underscore scale and this is called noise on his core intensity. Alright? So noise on this core intensive and noise on this court scale, right? So that's really handy with these guys. And also, I think this will show up here as only nobler. What's a novel? What does this do? That doesn't matter, right? So if I were to change the noise underscore scale right there. So reality is going to have to use the tools men. It's good that you know the tools. So you have for your document, your info panel down here. So if you go here, you would see under scripting info, this is really helpful. Your prize and tooltips, which you can find an Edit Preferences under interphase, you have heightened tooltips. You really want to have this checked and then you have your documentation as well. Obviously, this is your single source of truth. Everything is in here, just make sure that your version aligns. So now I'm actually using 2.91, and this is 2.91. So it's not exactly the same version. But I know in these minor updates like from 9190291, there's not really any big changes to the Python API. There is one if you compare 81 and nine, because that was quite a bit big release, there is some stuff that change to applying of modifiers, but we'll get it rectally gotten into it. It's already in the other and the other section, so you should already be familiar. If not, then we'll cover it in a second again. So, right, noise intensity, noise scale is quite good. So we should get a similar result. And this actually so 0.2 and then the scale is 0.978. Nice. Well, oh yeah, R0 offset the words as well. All right, but this is quite, this is quite okay already. Now of course, because this is the, these values are hard-coded. We're all, we're going to get the same, the same rock. So we don't really want that. So we want to offset this a little bit every time. So creative, ordinary texture, what we will do here is we will set our, it'll give it, it will give the parameters in here. So this is sometimes a bit of a debate that you have to do it yourself is where you want to randomize it and where you want to just set it. Now, creating a Voronoi texture should, should not randomize the values in itself, which is just get devised and assign them. And my opinion, of course, that's just my opinion. You could you could have arguments and I mean, it doesn't really matter that much, but I'm gonna say that we're just going to want to have to, we're just going to want to assign it here and then we're going to want to randomize it somewhere else. Because it's not really bound to creating a Voronoi texture, really creating a vortex. You should just be creating Award winner texture with any given intensity and skill. Yeah, Just just my opinion doesn't really matter. So what we could do is we could, I mean, now that we specified in the arguments are going to have to set it. We're going to have to pass it in here as well. So if I were to run it now is going to give me an auto. It's not going to give me an error because I initialize this with random with a default value. So we're just going to take 0.2 in case nothing is specified. So I actually want to specify it. So let's just do it again. But now, just to show you guys how it changes values, you know, it takes these new values instead, but we are not going to, this is actually just the same as anything rates. It's still hard coded values and the evil the course. So what we wanna do is we want to randomize these guys, for which I will take you guys with the console real quick to demo out how that's going to work. So we have our random Python module or library like a function set, set which you should really be familiar with because, because random is amazing, use random all the time. So if you want to check out what is inside random, we can use our direct method again. No need to say print in the console, but we could, It's actually the same. Maybe let's just do it to be consistent to it with this. Because if you were to just their random in this environment, that's not going to work. I'm actually not going to type it out. But yeah, that's not gonna work. That only works in console environments. So that might be a bit confusing. So if you were to print our random, we can see what's inside. Usually you can ignore all of these underscore guys is double underscore guys. They're all like private methods to the, to the, to the set. So we don't really have to bother with. Those are usually the ones we like in a random or rent in surrendered range and uniform non Here it's kinda split up. This one is amazing. This one basically just gives you a random value between two wineries. Wasted an entrepreneur that random the, Am I making like a typo? Okay, so there is no helpful random dot, uniform, whatever. So this is basically is going to return your random value between whatever boundaries we set. So you have a and B here. And so what I could do is randomness uniform, let's say between 1.51 and then with the scale we'll say between 1.52 and then every have to import it. So yeah, again, the console is completely separated from your script. So you always have to import your stuff before you can work with it. So random is actually a function library. This is a default, like this comes with the default installation of Python and blender. So you don't have to install anything, you just kinda have to bring it into your script. So if we run this now, we would see our intensity and size are random values. I still between our two boundaries always. So we can see slight, slight differences, but not, not any major differences because those are going to get from, from offsetting our points, of course. So this is actually the tedious work which takes the most time if you do it manually. So well, it doesn't work. So you add this as well. Where are we going to save the most resources and the script? So let's do that next. 15. Procedural Rocks - Reducing the poly count: Well, that's actually decimated as well. It doesn't mean it doesn't really do much. It just kinda makes the, the, the geometry just a bit less. No need to print anything for now. I'll just say decimate. And let's set the ratio to be point 25. And also let's just give it, let's just pass it in here. When ratio by 25 ratio. So yeah, you almost never want to have magic numbers in your functions. Now here, great walk is like you're like a layer on top. So this is where the functions get called from. So I guess here it's fine. Here as well. You know, it's, it's usually best practice to two, make variables out of them and an abstract them some way. But I think for now this is already declared is already quite clean. So yeah, I think honestly, it's quite fine. All right, so this still works. Now we're getting decimation going, so our topology is going to be a bit less dense. So if you were to not do that, we would see, well, we wouldn't actually see it here would be your to collapse. But this whole thing changed into 0.9. Actually don't know how to do it. I just know how to do it through scripting. But it doesn't matter. Just trust me in is that that's meeting ID on the lower your topology, lower your account. You can kind of see it. So if you go into these extremes, so this is kinda making, given the low poly look as well. So we kids play with this. If you were to sell it to like a 0.03. Yeah. But yeah, let's just continue the actual he actually wrote generation. You're not going to spend like waste too much time on that. So now the actual big, the big, the big impact is going to be where you're going to get from offsetting these vertices. 16. Procedural Rocks - Altering vertex positions: So what we wanna do is offset verts of my cube. Let's figure out how to do that. So I know there is a property inside of, of these objects that with letting, letting me speak with the vertices. So I think it is, well, let's actually figure it out together. This is all of the stuff that we can do with a, with a good old object. So let's look into, I think it's hidden inside of data. So we have Theta. So many things. Man, blender is 20 possibilities are looking for vertex. So this is all alphabetically as well, so that it can be handy when you're trying to look for something specific for disease. So if I were to print that there's eight vertices and a cube, it's 44 is eight yet it seems that we get. So now what we wanna do is we want to go over every vertex. So for vertex in my vertices, let's just print it. I keep just printing stuff. I really like working like this. Sometimes it's a black box and you know, obviously if you know what you're doing, you can just type it out. But when you're figuring stuff out, you're usually want to be printing a lot. So we have these match for it doesn't say much, right? Let's see what we can do with a vertex that's also break. So one good thing to note when you run a for-loop is you can exit premature, prematurely by the break statement. So this is just going to run it only once basically, because I don't want to run the dirty for every vertex because you want to be a time that same. So I just want to run at once. Otherwise we would get the same thing as eight times. It doesn't really matter as we have SEO, which I'm going to guess is for coordinate. Actually nobody, yeah, I'll just say I guess that nice. Normal, this can be nice. So what we could do is we could just kinda take our normals, which will be pointing like this. And everyone is kind of offset them along their normal. Some, some value. So we'll do that for every one of them. And that should randomize a quiescent quite nicely. Well, not if we do it equally because I will just kinda grow the cube. As you don't want to do the average, kinda want to randomize it in both directions, I guess. Yeah, let's do that. So for every vertex we want to take my coordinates and we want to add some value to it. So let's get overt offset of a vertex. Let's maybe add a scale as well. So let's start off with a scale of two, whatever. The source vertex and its scale. So the direction that we want to offset it in would be our normal. And then what we wanna do is we wanna take each and every item. So this will be the x-axis and we would add some value to it. So let's use the uniform or library again. And let's do it either negative 0.5 or positive 0.5. So for now I'm still using a normalized vector. So this is some vector math. I'm not gonna go too deep into it because this is quite a complex topic. But are normal is always going to be a normalized vector in which the direction of this normal, this vertices porting mainly just for lighting and shading as well. I don't really know, but yeah, it doesn't really matter. So we have a normal which is a vector that goes out of each vertex. And this is going to be normalized. So this is going to have a length of one. So that's why I know using this 0.5 here is fine. I don't actually want to apply my scale here. What I wanna do is I want to apply my scale on the whole vector. So I want to scale of that thing is refined. So I'm going to add this scaled. Yeah, that seems fine. So we get quite some nice results, but I feel like it's not really different enough. Let's add some some size to it. That's quite nice. I'm still feel, let's actually turn this down again so I get a little bit more detail. I feel like the size of the noise is not quite big enough. I feel like it's a bit too detailed. So if we were to go into our texture is to the maximum size really. Yeah, let's, let's kinda, this is already a bit paler, I think so let's make this, make the size a little bit bigger. So I'll point you to this already. Quite nice. So let's lower this a bit then. Through, let's say 1.2. Yeah, it's not bad. Somebody's have quite some, some face electron planar surfaces which I don't like too much, which we kinda get rid of by making it bigger. Let's see. What if we skills to six? Well that's when it gets worse. Of course. Let's make it 32 and histone. Yeah, This is go hungry trial and error for a little bit. I'm not going to bore you too much with it. These are quite nice. Let's keep it with that. So but this back to low poly I quite like to look. And maybe you lose a little bit too much detail FOR loop. While Europe's, I guess it's not that bad. Usually what I would use, I like what I've used this for as a base meshes. And then you can go into ZBrush and you must sum up completely. But this gives you a quite a nice base surface. And if you january like a whole lot of him and the background and thickness is really fine. Yeah, and this is not that bad. Of course you can, you can make this a lot better, especially when you tweak the values because like or if you, if you change the values, it can get a completely different look, even on the noise alone. Or to make this a lot smaller, you could get like some meteorites look like anything even. Yeah, I really recommend you play with these with these things. Maybe like a lower intensity is even, it's even a little bit nicer, but I'm not going to spend too much time boring you with this. All right? Yeah, that's fine. 17. Procedural Rocks - Collapsing the modifiers: Okay, So one last thing that we could do as a bit of a cleanup is collapse all of these modifiers. So now we have, we have this modular stack down here. So once you generate, you can still kind of tweak your values. This could be something that you like. This could be something that you don't like. Let's see, we can actually mess with these kinds as well. Well, this might be a good exercise for you guys to build on top is kinda tried to change these strengthened mid-level failures as well. Randomize them a little bit. Maybe not too much because a, the extreme with the random dot uniform, you can really set the boundaries to be to stay in between, something like this. Maybe it's actually quite nice. And then with the middle level as well. Yeah, so figure out what these are called. I'll give you a hint right now. And then implemented make it builds on top of the script. This is always good. So now I should be like a basic implementation. It's really not that great at this point. It really shows you the foundation and the fundamental way of thinking and fundamental way of solving this problem. And then you guys can just go wild and figure out new properties to change and news new stuff to randomize and like build on top of it. That's how you're really going to know how this stuff is done. I could probably spend like an hour longer on on these rocks and that I would be, there would be totally awesome. So I might actually take a little break from this and mess around a little bit. But again, I'm not going to bore you guys with what we are gonna do is we're going to collapse all of the, all the modifiers. We're gonna do that over our object. So let's figure that out. Some objects. This is again, another not a mandatory step. This is something I want to show because it could be neither if we just want to generate the marks and you don't want to have this whole whole tree of modifiers every time you just wanna kinda collapse it, kind of break it down as you will. Because now if I were to, if I wanted to edit and here, there wouldn't be possible because I only have these eight vertices to work with. So some use cases, you kinda wanna keep it non-destructive. You want to collapse it down and then work on top of it. It's whatever. Actually, you don't even have to go to ZBrush. You can sculpt in Blender. Blender is small, some sculpting like a sculpt mode. You're gonna go crazy. I've never done this before. Where you can broadly though. Yeah, well, that's for another time. Let's collapse these modifiers. So we have our object, we want to query the modifiers. So let's say not modifier this quirky, check that out. So there's three a list of modifiers, so that's good. So for every modifier in object on modifiers, Check this out again. So that works. We want to get the name over them. Yeah, that works. And I want to figure out to collect them. So applying the fire started up in the ducks. As practice. What does that have anything to do with metaphors? What, where did I get there? As I'll types apply, modifiers. I can not find this on who actually know what it is, what I wanted to show you guys. I had to actually figure it out with the documentation that is not working. Well. It just didn't go on forums or anything. Apply modifier blender to one. And there you go. This is always going to work. Just blender stack exchange. This is awesome. Yeah, this is basically what we need. We'll put that in here. Changed the name with the name, and then try it out. They worked blenders, decade change. It's awesome. So we can see where it is generating rocks. It, you could, you could argue it's destructive, but now you get the actual, the actual topology if you go into edit mode. So I guess now we can sculpt on it. Yeah, there we go. So yeah, well, if if you can sculpt and you can actually do some cool stuff it is, it's got symmetry turned on by default. Okay, that's nice. Well, whatever. Yeah, you can actually work on now. Like this. I'm thinking if there's anything else I wanted to talk to you. Yeah. The Oh, yeah, Emily, just try to figure out how you can Oregon top of this. So let's say we don't want to collapse it for a second. There's plenty of properties in here that you can still play with. Randomize. Just figure it out. There's a whole texture stuff, okay, kinda, kinda mess around with it. A color ramp is it's got some nice things you can play with to really make it look like alien, like maybe even. Yeah, you can differentiate, integrate that data and honestly, your creativity is as old as eliminating you at this point. This can even be cool for some shading elements are forever for some animation. It's like yeah, whatever its expansion topics. 18. Procedural books - Introduction: Hi guys. In this part of the course, we're going to leave the modifiers aside for a little bit. We're going to just focus on actually creating geometry. So we're not just going to be applying modifiers onto like basic, basic cubes or, or three years to make them look cool, we're really going to get into the nitty-gritty of modelling, adding edges, extruding faces, moving vertices around. So we're really going to like we're going to keep it simple in the beginning. We're just going to focus on a single book, see how we can make it, how we can script it. And then once you know how to make a single book, you can easily make like a whole shelf of books. Even all library with this relatively simple scripts. So I really don't want to overwhelm you guys. It does get quite complex at some points because we're really going to be building, building up geometry from scratch really. So it's going to be a little bit different than what we're used to up until now, but it's going to get so much more rewarding. And also, once you get these, these concepts down, you can create anything really. It's all, it all just comes down to adding edges, extruding faces, moving vertices. That's really all that modeling is, right? I'm sorry. Are we going to be focusing on some books for the following? Not sure. It'll probably take a little bit longer than an hour, but I will do my best to keep it as entertaining as possible. We're going to have to keep the code is as clean as possible because as you can see, there's like a 300 lines of code here. So we need to keep it structured, really, that's really important. So as we get into a little bit of a bigger project, you will see the importance of that even more. 19. Procedural books - Generating the shape: Alright guys, I hope you're ready because it has got to be fun one, I just created a, I mean, I just opened Blender. Let's go to scripting, create a new script, zoom in a little bit, maybe move some stuff around. Should we set this up as like a template is something? Alright, so save it first books course already have one. So okay, we're ready to start. Let's import the PY. And then let's all see together the scene. For every object in BPI dot theta, a little objects, objects that remove. And There we go. Okay, so step been done. There. I notice it's getting really repetitive, a really boring maybe to see me type dish it out. Really you can just copy paste it. But because this is, of course I don't really want to copy paste anything. I really want to type everything out for you guys. So I don't just appear with the magic solution. So always start off with the cleaning of the scene method. This is really going to save you some headaches from having like 20 overlapping cubes in the center. And you don't always want to go and select it, delete it. But again, I've mentioned this a couple times before. And, and the level of production applications. You really don't want to, you don't want to just remove the whole, the whole scene. A lot of people are gonna be really mad about that. Okay, But for now, we're developers who want to make our life easy. So you want to clean our scene every time we run our scripts. And then the next thing we wanna do is create. Create a book. Simple enough, right? Grade book. So when I start off these projects, I really like just seeing what modeling just do, like the big, the big shapes. First, the big shapes just defining some functions. I want to create a book. What should I do? I should probably started with a cube, so I'll create a cube. Maybe I want to keep track of that cube. Swell, actually assign it to a variable. So this is also stuff we've done plenty of times. So when I said we're not going to be starting with acute, Well, I didn't say we're not going to start with a cube, but we're going to go a lot more in depth than just having a queue with any modifiers onto it. So, but to start is still the same. One thing I'm going to really mentioned here is that 31 is set the size equal to one. I really don't know why my blender has a setup us to by default. But we're really going to want to change this to one. It just makes our lives a lot easier. When we start resizing. We will just note that the width is whatever we set the scale to, because we are starting with a scale of one. So when we work to, if you were to size it, resizes on X by a factor of 0.5. You would know that the x size is now 0.5 because we started off at one. So I don't know, it just makes more sense also, I just really don't get why they haven't defaulted to these guys you can just ignore for now. We just want to have it in the center all the time. We just really want to make sure this is a size of one. Well actually, a book is usually not shaped like this, right? So when we, when we model in, like when we procedurally model with scripting, it's not that much different from when we model this regularly. So we still want to start off with the big shapes and then kinda define how they look. So let's just quickly check to. So we probably want a skill x in a little bit, maybe skill why? In a little bit as well? Skill z up a little bit. Yeah, we just kinda wanna have it a bit more of a rectangular shape, but more like this. So what we could do, well, it's not really creating a cube anymore, right? So it's more, I'm resizing it. So let's actually copy wanted these guys and put it down here. Maybe let's say resize. And you give it a cube, like an object. So we always want to abstract this stuff. Let's say x, y, and z. So if you were to implement this, we would have something like this. Instead of objects, we'll say OBJ. And Unreal will just actually does resizing is happening on the currently active object, I guess. Because this is a B provided ops method. So we just wanna make sure on now we can just say, we can just deselect everything. So we can say maybe write out data for PPI dot contexts. Let me quickly check this. Well, let me open up a new Firefox isn't necessarily better. So let's grab all selected. Or let's say clear selection by thin blender. Alright. Yeah, I've had to do this before. So yada, yada, yada, there we go. That's also a l. I guess this is fine. We just want to de-select everything first. And then we want to select r objects that we want to say set selects to true. So we are sure that we have is selected. And then afterwards I just deselect everything you can as we're cleaning up. So let's make sure this works. Before we actually do anything with these values here. Yeah. Okay. So I like to yeah. Well, are you know why? But yeah, I like to work through this stuff with you guys. I hope you don't mind. I don't find it boring. I really just like to look up everything together with you guys. So it's not like just me typing stuff because this is how it isn't a real worlds. I don't know how to de-select stuff in Blender and waste. Why should I bother? I just typed it in Google and find out. So yeah, these XYZ values, this is why it's complaining they don't exist as their name x is not defined, right? Because I just, well here it's fine because they come in. So I should actually say x, y, and z here. Alright, so the are used and I'm here, I'll say can be important too. This can be 0.8.80.8. And then we'll say 1.2. I already know what this can look like. Still not working. Set select. Ok, what's this coli and this is always just type it into Google. Actually, i'll, I'll maybe do a little bit less. It is selects it. Well, of course. Some things oh, yeah. Complimenting selected. Okay. So still crashing select NoneType. Well, yeah, I'm not actually returning my QC here. So return, we provide a context. Object, BB right up data contexts. I should know these things by heart, kidney. Some of these things you should know by art, the product context, selected objects yet and distinctive for, well, can't you just say so it's an object? Well, I guess we'll relate it. Yeah. Okay. Sorry for the delay. This is working through some stuff. Let's make it a little bit less high. Three, this will be our base parameters, but we'll randomize those as we go. So, but for now I just want to keep it constant. We want to make our book, and then we can talk about variation down the line. 20. Procedural books - Alignment: Maybe first before we do anything, let's make sure we always aligning our book to the floor. That's something that's often overlooked, but I really don't like my geometry sticking through the floor like that. And easy fix is just translating it and then taking the height. So whatever we just set the scale to, which is our height of our book. And let's just set half of that to our delta z. So our moving up a variable, let's say, set that to be the health of our, of our book here. So if we do that, it will always be perfectly aligned. But when you wrote it, you always start from your pivot point. So when you just create a cube, by default, your pivot point is going to be in the center. So as you can see here, that the little orange dot is in the center of this cube. So when I scale, it will be from the center. So that means that half of your height will always be sticking below the surface. So if you create a cube of, let's say Yeah, let's say size one, right? Because that's what we're using with then I know my height is going to be one because I set it to one if I were to move it up. So it is easy by 0.5, which is the half, that half of my height, it will be perfectly aligned. So if I were to create a cube of size, say 20, all right, I move it up by 10. It will be perfectly on the ground. And now of course we are, our x and our y coordinates are not the same, but they don't matter it we were talking about Z, talking about ICT. So this can be anything. So just keep that in mind. It's a nifty trick to get your stuff aligned to the floor. All we don't want that big of a bulk current. So 1.1. Now, yes, it's always going to be on the ground, which I like. We'll talk about the x and y alignment and a little bit when we get to like generating a bunch of these books. But for now we just want to get them on the z-axis aligned. All right, so a lot of talk for something really simple. Let's continue and let's start shaping our book into a book. 21. Procedural books - Manual modeling: Okay, so the first thing we wanna do is we want to add a couple of edges. So let's maybe put one in the end here. Let's add another one, which we can kinda scale n and x. It's a bit big, but you know these values we can always change in the script. So I don't really care. What we want to do then is got through, we get, got two in the middle, boom, boom. And then scale them up and x. So there were spread out. I like to add well, I think I fucked up. Yeah, like to add through here because that I don't have to like I could just kill it an x squared, it will be spaced out equally. Instead of just like adding one, moving it over, adding another and moving it over, it's never going to be perfectly the same. Sometimes it doesn't matter, but this time, you know, it should be symmetric. Which actually should we turn on symmetry? Now, I'm not going to, so again, not that, but two. And then scale it in x. Yep. And that's almost it. Let's just select these guys and then extrude them inward a little bit. So this will be our paper. Will add materials maybe if you have time. So this is kind of how our book, we'll look in yen. So there's a couple of simple modelling operations that we're going to figure out now. Sort of first one was this loop cut thing. So let's actually do that. I'll just dump it in the script for now and then we'll figure out in which function it belongs down the line. 22. Procedural books - Adding edges using code: So as a proof of concept and this quickly just test this out. We get an error. I kinda knew this was happening because I'm blender is when it comes to modeling, it truly is. There is like stuff, regions and few 3D contexts and stuff like that. It's, it's all quite complicated and it's a little bit abstract, so I'm not going to get too deep into it. All you need to know is that you need a specific a specific function that I also found it online. And it really takes no skill to write this. You just need to know what's going on. So for now I'm just going to quickly copy paste it. I know I just said I don't like doing that and I really don't. But I mean, I could go on at Daniel for like two hours explaining why we need this code and I really don't want to bore you guys with it. So this is whenever you get like like this kind of an area where the run, like a runtime error where it expected some region and view 3D is a classic one. It always is few 3D. When you get this kind of an error message, you usually want to have like a context override. This is just border blade code. You can find it on the forums. Not gonna go too deep into it. Basically what you wanted to do is you want to get like a dictionary or this stuff inside. And I just want to set that here just before you do anything. You just set that. Don't forget the brackets because you want to call the function. So you want to get this, basically lets actually actually rewrite this. Let's say my context override equals getContext override. And now we just want to put that in here. So if I execute this now, it's still the lower context. Context override. Really? Oh yeah, I have to change it to edit mode on I was quickly copy that code. I'll do it here. Yep. Okay, so now we added these two guys involved there. Actually, it should be in the end. So this will be two guys at the end. First we want to take a different guy. So that's completed. Our contexts override again. And, and this value is going to be big. Yeah. Let's see. I guess it's a relative unit. Okay, So first the query edit this and the soil. We don't want to do these just yet. Okay. Let's actually move it back a little bit less. Well, maybe let me actually extrude, abstract this loop logic. So let's say def loop cut, and let's then say the number of cuts and then the offset maybe in here we will get our contexts overhead because we're always going to want to do that. And then we'll just call this function. And here, set our number of cuts equal to number of cuts and move it down here. And then just clean this up a little bit. Because this is like a long line. I usually don't like that. This will expect to dictionary. This is a bit over. We'll clean up of whatever it is. This edge index is going to have to be run. It's gonna have to be specified as well. Okay, So I guess this is the first object that we have. We have this guy does another object like another dictionary IN, and this is the identity. So let's do that. Now. A lot of these values are probably not even necessary. But I'll just put them in any way. You can always India and refactor it isn't take some stuff out. So it is offset will be this value. That's not include the negative sign there. And then this edge and x is also going to have to be. So it's a number of guts, edge index and an offset. So number of cuts, edge index and an offset here. So that's good. We are using this guy stored, so on like that. Okay, I guess this is fine. I just need to be readable. Usually when you have like a long line, it's annoying because you know, in this editor is hard to scroll through and all that kinda stuff. So we have our loop function, which we want to do here. What we want to specify is the number of credits, which is one, this case, or edge was eight. And then our offset was zero-point, like ADH, but I want to have a little bit less. So let's say eight. But, okay, so now we get our cut right there. It's maybe a little less, say 75. And then the next thing we wanna do is we want to cut there. So you want to do it again? Let's quickly get our values out of this. So my edge index is 15 and my offset is nothing. Just want to have this in the center. So execute that. Okay? You know, sometimes when when you do clean off your scene, it gets a little confusing to know that your script is actually being executed. So sometimes when you're really in doubt, you can go in and delete manually so you know, you're looking at the opera, the scripts, but if you spend all, be a couple times, you know that you're looking at the output of the script. So doesn't really matter too much. Okay, so now we have this. We wanted to resize it an x then a little bit, let's say 0.810.8, that's fine. So we can reuse our resize function on X. We want it to be 0.8. Here we can keep 11. So we don't want to alter these. Make sure you don't put this to 0 because then you'll scale it to 0. So you'll make it infinitely small. Just using one. We'll just keep keep it. However it is. Not working. Yeah, actually, I don't wanna I don't want to bother with this shit. To be honest. Let's just not just resize whatever is currently active because I know what this resizing it works and also this this is a, so let's go a little bit of a tangent. So I have a function that is called resides. And all this should do is resize. What I did was wrong because I didn't move inside of my resize function. And that just doesn't make any sense because, you know, resizing is not moving. So what I should do is when I, after I resize, I should do center. Or let's say a line z, right? And everyone at best and is 1.1 as well. So let's also actually given through the randomization, I guess. So let's say x size equals random dot uniform. You've seen this before, so I'm not going to cover too much of it. 0.15 to 0.3. Control D to duplicate the line, then y size, size, why should read between 0.70.9? So I use these constant values as they kinda in the middle and I just kinda go left and right of them. So let's put this to one and then 1.2. Use these values, pin our function call. So we don't have these magic numbers anymore. And then we can actually use our size here as all these guys, I will keep magic for now. While these are actually going to be constant, these as well. Actually, we'll randomize this guy a little bit. So this will kind of shift every now and then. We're going to have to import random because it doesn't work otherwise. And then we have to write a line Z. Let me do that. Okay, So I'm going over this quite quickly because I think you guys already know what I'm doing. I, this is just replacing, refactoring, I guess, of code. Just moving stuff around to make it, make a little bit more sense. So a line Z should really be here outside of this resize function. So resize doesn't care about any object anymore. And that way I can just call resize here again with 0.811. And this will work. Maybe 0.8 is a bit extreme in there. This is fine. Our stuff is still aligned. If you were to generate a bunch of these, we will see that they kind of vary and height and I mentioned in general. So this is what we get by randomizing over her sizes. Align Z is always going to take the Z size, so it's always going to align perfectly to the, to the, to the ground plane. And then our leukocytes are also being put in. Just fine. So is going quite good, right? Or our book is already kinda looking like a book. Let's add a couple more of these loops. Okay, So let's add to 0. Let's Control D to duplicate again. This because we want to put that here. So what we wanna do is we have our first loop cut, which is, which is this guy going around. Then we have our second guy. This one is scaled. So this resizable take whatever is currently selected. So because we just edited, it will be selected. So every time you add a look at it gets selected. So if we now scale, scale that guy, so that works and our advantage here. Okay, so that works. And then we add a new one where our number of cuts is to our edge index is 11. I really don't want to, I want to do anything to it yet. Okay, so we always get these slices in the middle. And then we basically this one is scale it out a little bit. So that's a 1.6, I guess that's fine. It's maybe a bit much already. 1.6. Let's see. This is rather a couple times, can probably make it a bit bigger. Because now the, the, the cover is quite thick and this case, so let's make it upward. Nine-year, quite extreme because this is all relative to two thickness. I think two is going to be too much because I don't think we'll hit the border. Let's actually check on or we don't well, I guess two is fine. Yeah. Let's stick with two with these numbers already. Mailer. They'll be randomized anyway also. But this is like a good starting point. And now the last thing we wanna do is we want to extrude these guys Edwards. And this is actually quite a complex topic, so I'm going to separate this out. So what we saw for now, we'll just look at and how we can do those. But this extruding stuff mainly, I mean, sure we can just copy this, but the main problem here is getting this selected. So when we run our script, we'll be ended up with a couple of edges in our selection. And we need to magically transform does to these. Like only these enter surfaces, only these tree, which will then need to be extruded. So we will cover that now. 23. Procedural books - Refactoring: Maybe first let's clean this stuff up a little bit. What we could do is we could actually pass this stuff in our create cube function. And I could do this stuff there as well. So let's do, let me move this up. Create queue size, okay, we'll give it these three. Okay, so we're creating a cube. We're giving it three sides, values, x, y, and z. And I'll resize, resize alignment. So this is a bit more condensed in this, in this function. And let's actually call this book as well as of now we don't really use it, but this can also go edit mode toggle. I don't usually like toggling it because you know what? If you switch back and forth, this will always be the same. I kinda like setting it explicitly it a bit more. So we can do for that is we could just die BPI dot, dot, dot mode Sets, and then we will set this mode edit. So it's not working. Oh, are expecting what? Mood, mood, people and objects develops a set. Mode equals, sorry. Yeah, okay, So now we're explicitly setting it to be edit mode. So what we could actually do is we could all the way at the end, explicitly set it to be. Object mode is all three always. Go back to object mode. It's written not stuck in Edward. Okay, well, that's besides the point. So we're going into Edit mode. Lets say got up. And then let's I wonder, do I want to stay in edit mode? I think I do. Yeah, So let's say cut up. And in here it's kinda want to encapsulate all of these functions because these will, to all of our loop cuts and resize them. And then all the way at the end, just turn it into object mode and let's turn it off for now. Okay, So we're back where we were before. It just a little bit cleaner. It might bring in color book. After cutting out the book, we will want to, while I get the faces. So we wanna get the top face or the bottom face. And the front phase, I guess. So now we'll get, let's call this get pages. So our, let's say get paper faces. Well, these needs can be argued about. So it doesn't really matter. 24. Procedural books - Identifying a specific set of faces: All right. Let's actually, that's the lumber book as well in order to get these faces. So it's not necessarily an abstract method or an abstract function. It's always going to be related to a book, but I just don't want to put it in here. So we actually need to bring the book around, bring it here. So actually, I propose book with a lot because your paper faces are always going to be related to a book. All right, so what do you think that you're seeing right now is you can actually return multiple, multiple values from a single function. So you do need to separate them by commas, but then you can just, you know, what, I could do IT or they could be 33 values. I just have to make them before it can be Thurmond. So I just say name, dot phases, undefined. Able to bug phase and same with the font-face. So essentially now we have what it takes to make this run the project, which is not much, of course having executable code, but at least it's something. So if we were to come in here now we can see those three times. So that's actually good. So let's talk about the actual implementation of how we're going to get these vapor phase is good. It's not really obvious, right? I don't know if you've already thought about it a little bit. Maybe it's a good experiment to see how you would solve this problem. Well, I'll give you a tip at the clues, the symptomology that we saw in the last one about Iraq's. So we're also going to be using the normal and this examples. Because through the normals we can see how faces or oriented. Let's quickly visualize that actually. So if you are in edit mode, it is important to be an editor mode. And you come over to the overlays, you can actually turn out to an ordinary normals to something I forgot to do with the Iraq tutorial and I'll do it here. So these are the face normals. These are the ones that we're interested in because we want to select faces. So we can turn these army can make a bigger whatever. If we take a close look, we can see that through these normals, we can actually group our, our faces so we can identify all of the faces that are on the top by looking at the normal because the normals are pointing up. Same with on the front. These are, the normal is pointing to the front. So these are pointing to negative y. So we can see here the y, the y axis is the green guide on here. And it goes in that direction. So wherever the little circle is, this is where it's headed. So this is kinda the error on there. So this is going to be negative y. So we could say, we could go over all the faces. We can query their normals. And we can say if your, if your why normal is negative one, that means you are wanted these three front traces for your normal is positive 1. That means you are one of these top faces. So this is how we can identify the orientation or direction of a face or a vertex. You can see the vertex normals visualized here. You don't eat, you don't actually have to turn these on. For descriptive work. Your normal, they're always there, they're always available. They're used to render and to shade your, your objects are they're always available. They're just not always visible. This is purely like if you pour toggle. So yeah, let's actually see how we can, how we can use these normals. We can access them by going through the phases for what we need to do is for every phase in the book, dot-dot-dot polyols. That's actually for now. Just runs eager to see what we can do the face. Well, okay, So now I printed it. And actually I've printed it only not enough times rate as more faces and that's pretty check. Well, there's more than six phase and sorry. I shit. Yeah, I already know. One annoying thing with lender when I'm cutting up or creating edges and creating more faces. Is that your, your script is actually not gonna refresh your metadata. This is something that you have to do manually when you're actually adjusting that. So let's quickly do that. You have to update your information. I forgot about that. This is actually an abstract pointer so you can see over j. And all we need to do really for it as first of all, we need to explicitly go to object mode, mode objects. And then we just need to run over j dot, dot, dot, update everyone to not calculate. Yeah, so this is another bit of boilerplate code, just like the override stuff. This is just something you have to be aware of. That Blender doesn't actually update your mesh. So anytime you add a face in scripting, it's not going to be accessible, so we have to update this first. So let's actually show you. Let's print this again with the update. It's in the middle. And then lets through. Let's not do any of this for now. Let's just print out the number of edges. So if you could see, we actually have six, which is still knew from the original base cube that we started with. But then when we update it, we get 30. So after we update it, we get 30. Before you update your mesh, you only have the original six body has to work with. Instead of the actual 30 that we should have two kids. These, these three, they're actually not existed before we update before we update our image. Let me actually bring that back. Books out there. So the mesh, you know, just book. Yeah, So the mesh data is actually hidden behind the data. So we always need to do to get it to match properties, but yeah, that's whatever. So we update it. And then if you were to go for face in both the update other polygons or face. So again, I should just do break because I only see one, but I wanted to make sure that we see 30. So yeah, this is better. So what can we do with a face? What is inside? We have normal, which is what we're going to need. We have area to area. The area here. It's actually really close. I was looking over because that's how we are going to identify the size of our alarm clock face. Because one thing that you can see is, let's say we have, we are able to classify our top faces. But we still need to know which phase that we need to turn into paper because we don't want to externalities guys, we only want to get this guy. And what makes this guy unique compared to the others is that this is the actual, this is the biggest face of all of the top facing faces. This is the biggest one there. We could check that by enabling the face area. So this is again just a viewports thing. As you can see here, the area is actually always available, so we're entering now before it was still there. So we could check the area and then whatever face and it's pointing up at the biggest barrier. This is the phrase that we want to select this as the top phase. This is the top page phase. However you want to call it, save it to front. So you have these three phases in the front, we want to get the biggest one, and then the bottom is the same as on top. So we want to get the biggest one of all three. What is planar surfaces? So what we could do, my face normal will be phased out normal. I might phase area will be installed area. So we don't want to over-complicate things. And now we want to do so in order to identify. So let's say yes to identify the top faces and start with those. So what we wanna do is so if my face normal, the second index to the z axis is one. So it's pointing up. So my z pointing up, so this will be one. Then I want to print this print face normal maybe before we do any of this. And I just print out all the normals so you guys can see what that actually looks like. Okay, So these are all the 30 normals that we have. So for each phase we have normal. And you can see like these odd numbers, these are from those tilted faces. But the very like around looking numbers, negative one x, Let's actually take this guy. So 0 and x. So nothing there, 0 and y, nothing there, and one on z. This will be a top facing normal. So this vector 0, 0, 1, or just go up. And then here this is 0, negative 10. So 0 and x is equal to negative one on y. This will be one of the front-facing vertex faces. So we can see there should only be three of these. So here, when 23 down here. So this is kinda how we can identify which phase that we are dealing with in our loop. So let's get rid of this. So what we want to check is, is my face zed, zed components of my face normal one. So it's pointing up. Then I want to actually compare. Well, let's say if my top face at this point was none, that I just wanted some way duckface to be like. Whereas if there's already a face and the top phase, then I want to compare the size. So if they're non-selective get I'll just, I'll just dump it in and I'll see a but if there's already a competitor, and I want to check which one is bigger because we only want to keep the biggest one. So what we can do is if my top based on area is smaller than my current space area, then my face will be replaced by microbes. And like that, I should be able to, Let's just broke my top face area to make sure that we have biggest one and we have error. There's two guys here. Yep. And now I should have 0.6 as my top three are actually fine. This is fine. So 0 dot six. And then I could do the same for the bottom and for the front phase. So we could say the bottom. This will have negative one and z. So bottom bom, bom, bom. And then for the front-facing, let's actually, i'll, I'll wait maybe a second. So we abandon this first node, then it stop. Let's actually put that time. This order doesn't matter, but just because I did that for us and I went to change the second one. So now we have bottom. Now for the front one, I want you guys to think, what is this value supposed to be and what is this value supposed to be? So this is the index of the vector. So 0 would be x, one would be y, and two would be z. This will always be 01 or two because this is literally just the index of our, our tuple here. So 0 would be x, one will be y, two would be z. So now we're getting the x-axis, so this will be one. And we want to actually get all of the ones that are minus1 because our y axis goes in this direction. So it will go up that way. This will be the positive y, these guys, because it is already negative y, so this would be negative one. And that will be the front faces from the front. If we were to run this, we can now see we have three phases being returned. So it looks a little bit abstract because it's like a reference to some place on memory. But that's actually, instead of printing the face, we're just print the area. So we can see 0.50, well 59, 59, and then 0.6. So if you can see here, this would be 0 dot six. So this would be 59 and 99. This will be 6, 6, 6, 6, 7, 12. He rounded up. And then here we'll have through L6. Actually one thing and another mentioned. Here. We are actually checking against one, but this vector is always going to be like a fruit. So we can see here this is a 0 or 1, 000, 000, 000 a could be that we got like just like we got it here, 0.599. If I were to check for a face that has an area of 0 to six, I wouldn't get any because none are equal to 0, L6, this is just very close to 0 or six people to it. So when we are programming this is like an explicit, this has to be equal exactly one. So sometimes this will cause issues. Actually add it should be four. It's quite odd and it's not giving me issues now, say kinda find stupid because I wanted to in the course, but I'll include it anyway. Sometimes it's a small rounding errors of floats and computers can really be offsetting. So even though this is pretty much 0, R6, if I were to compare it to 0 and 360 would be false. But yeah, and this example is actually didn't give me any trouble. But let's just be super, super safe. This could cause some issues when we are having like so what is tilted faces? They might get identified. If I were to check on the x-axis, these guys would be included as well, which maybe we wouldn't want. I mean, in this case we can just round it off and kind of be and it'll work just fine. So right now we should have identified our top phase, our bottom face, and our phase. So if I were to run this again, we could see they're actually always the same size. Pretty is a little weird, right? Which will be a lot bigger now. But I think by n, yeah, exactly. It's an R now it's six zeros 0000. So, yeah, you can be asked, Well, this is casual floating point numbers for you guys. 25. Procedural books - Selecing and extruding specific faces: All right, so now that we've identified these faces, so these actually returned, well, that's not particularly more, but please return the phases that we want. Let's actually use them now. So let's create a method to select them for. Let's say select faces is our faces of the object book. And an hour just pass in a list. So this list will just be, my face is one thing that we need to do here for selection to work is we need to pass in the inlet. We only care about the NX. Actually. So let's already, we have been here. So in order to implement that, let's quickly select faces, function subspaces. We'll get an object and then we'll get the list of phase indexes. So it is what the index is. Quickly to say when you just use a select property doesn't quite work because a new kind of select the edges that are related to it. So we need to do some, some nifty tricks before. So that's why we need to get to fix indexes. This is basically just the result of some debugging and my hand, which I don't want to bore you guys with. So what do we want to select the faces? The first thing we wanna do is we want to make sure that nothing is selected because at this point, just before we call this function, this is the output of the script, so we actually have some edges selected. Alice is gonna mess up our, our Thanks. So we basically want to have a clean slate, nothing selected. Because as mentioned, whenever you create geometry or you create edges, it gets selected by default. So we want to basically just, we've heard that terminology. So let's go into edit mode, make sure that we're at. Because when we do our, while we do our identification or actually are updating of the mesh, which is required to get all of our faces available. We have to set it to object mode, which is also helping us. These next stuff. I mean, generally when you're working with traces and identifying stuff, you should be in object mode, only ever go to Edit mode when you actually want to edit something. So when you want to edit something would edit mode. Do the edit, go back to object mode and I continue. It's usually better to stay in object mode when you're scripting. So just to one another. So we want to de-select all the faces or whatever is in the selection because it's actually edges at this point. But that doesn't really matter. So like we did before, repeated for it and selecting everything, then we well-done milk. So a Select All and the actual is actually There's actually select. So you want to de-select everything in edit mode, which will be these on these edges that we have currently selected. So after we've done that, we want to go back to object mode because we're done editing. We want to actually do some stuff with the two faces that is not related to anything. So let's go back to object mode and I will just go for every face index minus the freestyle xs. I want to say object dot data dot polygons. Grabbed my index. And I want to select this guy. So I want to select property to true. And then all the way Indians, I just want to go back to Edit Mode because the next step is going to be extruding. And also kinda mores. Well actually let's not do it in the function. We just do it after because I don't want to see it in action. So basically, we're identifying the faces here and then selecting the faces. First of all, we're de-selecting everything and edit modes who are edges will not be selected. Going to object mode and then setting that select property or the phases to be true, I think this should work. I have thought that before and just like these other times, I've proven to be wrong, does seem like it's tested. Dipole faces indexes you have now. Just keep face indexes. Off. There we go. It was just a dipole. That's cool. So now we have our faces selected that we want to have selected. So that's quite interesting. All we need to do now is just extraordinary. I didn't know it was going to work from the beginning. Pickup probably saw here the confusion in my voice. Okay, so the last thing we need to do is extrude. And that's that. 26. Procedural books - Abstracing the extrude code: All right, So d x ritual works. So we're pretty much done with the modeling section. But I do have a little challenge for you guys. So as you can see, I've cleaned up this super long, super-complicated function call and my script, so I replaced it with a simple function calls. So I suggest for you guys to do, to do the same, It's really easy. We've done it many times before. So before showing, I really want to ask you guys to try and solve this yourself gives them. There's really no rocket science. But once you grasp this concept, I mean, you're, you're on your way to become a really proficient and clean programmer. So I just want to give you guys the time to think about it and try to abstract it. So sort of having to call this crazy long, crazy complex function down here, you can just call extrude, give it some amount, and then this function will just extrude it. So I'll give you a couple more seconds to pause this video and then I will just quickly go up and then show you guys, because I don't want to be that guy with the shortest solution. But we really do try, try it yourself. Because this is really important. This is a really important skill to have as a, as a programmer is true. You'll be able to abstract your code and clean it up. So without further ado, the extra function is basically the same as the loop guard function. I do it explicitly go to Edit mode every time in here, and it's probably not necessary, but, you know, it's clean to explicitly set stuff up. And, and this is basically just the extremely long commands. Again, formatted a bit nicer. So it's not like a super long one liner, which is really hard to find properties in order or set set like stopped. So all I did was you said exclusion amount to me the value. And that's it. 27. Procedural books - Creating materials: All right, We can consider the modeling done, at least for now. Honestly, if you guys feel like this could be improved, I highly encourage you guys actually just, you know, looking into it, maybe trying out some stuff for neurone, seeing how you can improve this stuff. Because definitely meant, definitely it can be improved by a lot actually, even just a good-quality, you know how it is written, how it is divided into functions, it can be a lot better. So really, I would like to encourage you guys to take a critical look to what we create it and improve it in your own way. Maybe send a message to me if something is unclear. Really feel free to do that. But now I'm going to move on to the materials. So just like objects, we can also create materials and blend there. I'm not really going to go over like the old manual approach because, you know, I, I guess you guys know how to do how to create materials magneto. And it's also not super relevant because we're not going to be copying it from the, from the info panel down here. So now before we do anything else, I mean, think about where we should put this. I would say just for every book, it will just create a material. So let's say, let's say cover material. All right, so cover material equals create material. And I just let you start off with that. So I'll create material is our new function. Actually, there is already one here. Let me quickly remove that. I was already doing some stuff. Brad said it to rerecord it. So I think I forgot to remove that. So don't, don't bother. So let's say Create material. We'll make it an abstract version first and then we can call it from different places. So let's say Create material. Let's give it a name as well. So let's call this guy over material, bringing that name into our function. And then now we can actually work with. So how we are going to create the material is quite simple. So really BPI dot data dot materials new and then just whatever you name it. So I think this point we shouldn't have any materials. Let me quickly go up. Yeah. So let me remove this as well. We all are retyped is actually it's a bit dumb to remove it, it to retype it, right? So let's just go over it. Sorry for not typing this out. I kind of forgot to remove this before recording. So just like we are removing objects, so we'll be drawing, I'll date of the objects gets us the whole collection of objects in our current BPI, so in our current blender file. And I'm just gonna go over each, each and every one of them individually or deleting them. We can do exactly the same. It's a material so we can access all of the materials in our scene by doing the bridal data that materials. And then we can just go over them one by one. And let's delete them one by one. So in the end they'll all be deleted. So there are no materials in here. Now, when we run this code, we will actually create the material, right? So create material is being called right here. This function, Let's assign it to a variable and then return it as well. Because down here, we expect something to return. So if you were to run this now, we can see that in our material slots there is one material available. But just go over material, which is what we just call this. Okay, so let's assign it. And now we can actually colorize our book quite easily. So what we can see how that is done and info panel down here, so it says, let me actually copy it in here. Okay, So BPI dot contexts or object or active material. So this is actually just a reference to our material. We want to explicitly refer to the material that we just created to water than just the active material, even though it will always be the same, but still, maybe we want to abstract this code later, then it will work. So we can basically set our diffuse color property to any, to any value R0. So this is RGBA. So if you were to go back here and RGBA, so you can see 0 to 10, 16, and then one. So these are just your red and green and blue channels of your color. If you're familiar with Photoshop, they go from 0 to 255 for eye color. So where 0 is completely black and then 255 is going to be white here. And just to see where to one. So it's exactly, it's exactly the same. And you also get this, this a value here to stands for alpha. So this is how transparent your objectives. So your Alpha Should, I mean, in this case it should always be one. But the RG and B, we could actually randomize thicket. This could be fun to get some different look in books. So let's say Create material and then we will just, let's actually best and a color here. Because we don't always want to randomize our color. So I'll just assign this like so. And then we recreate a material here. We'll just pass a color here. So let's say we want to make a red one. We could rewrite that. So we have red, green, blue, and Alpha. So this will always be one. So this will now be fully red, no green, no blue. While we won't see it because it's not being assigned. But if we were to assign it, we can see we have a red a red book. If we were to make this red and green. Again, you'll have to assign it. So this is one that this is the next step in our script, of course, is to assign it directly. That's actually do it. It's quite easy. We'll just take our book. We'll go into the materials. So we always have to go through data if you want to. You want to do that. So it'll be booked out there that materials and I will just say append go over material. Well, sorry if I do that now, if it runs able to be yellow, of course this will always be the same color because we set it to be yellow here. So let's not do that. Let's actually randomize our color. Let's just take the code from up here. And then our fourth channel, which is our a or alpha, should always be one. And these values should always be between 01. So since this is, these are the boundaries of the color, or z or being black and one being white, and it should always be one. So right now we will always have different color. Books are different color. Golfers. 28. Procedural books - Creating complex material: All right, Let's actually push our knowledge or the application or materials at least a bit further now and this part of the course, we will be assigning a second material, 3D selected faces, selected traces, the represents the pages of the book. So what we're going to want to have them look more like pages then, you know, just the same color as recover. So let's dive into that now. The first thing we're going to have to do obviously is great the material. So we will call this one paper material. So I'm doing this now into book. Of course, if we want to create multiple books, we're not going to create a paper material for each book. We have desk just maybe for you if you want to go a bit ahead, if you want to, if this is a bit too easy for you, you can always go out on your own, try some stuff, and then come back to the course if you are stuck anywhere, which is actually encourage it as well. So I keep encouraging you to skip the course. Well, it's, it might be bad, bad publicity or however you say that. But the atlas is really a practice, makes perfect type of thing. Everything programming really. So anytime you get somewhat inspired or you have some idea how maybe I can try this or I should do this. Please pursue it. Just do it. Even if it's just five minutes, some dead end route or whatever. Man, I've traveled thousands of those. Well, that's exaggerating, but sometimes it's just fun to explore. So I like to encourage exploration. But for now let's stick to the course. I've just created a paper material. I've just written the code for it. And then just like the other material, we're just going to append it to our book. So make sure we have direct material. And if we were to run this, now we have are governed material ends up paper material both assigns to do our, our book while we still can't see the paper material because there is no faces actively using it. But at least it's already assigned to the cube, so that makes it somewhat easier. So it will take two clicks now for the paper material to be visible on the selected faces. So I will do them now manually and then I will script them. So you just kinda have to select the material index that you want to use. And now you just have to assign, click Assign. And then we'll assign the currently selected material index. So active material index, which is whichever one you have selected. And now you just assign, and that will assign the material slots that you have selected to the selected faces. So blender uses your selection throughout, every, every part of its API. So you have to be, you have to really grasp that. So basically the two lines that we ran or down here, we can just copy them over just like that. So active material index equals one. So since we have appended a different material, this will be index 0. So since there is no, like no materials at all, a bending, bending to an empty list, we'll just have a list of one. And I'll bending again we have a list of two. So we have our second index, which is one, of course. So index one as our second item in the list. So the second material that Rhea pendant bended. So the second guy here, we want to set that to be the active index, and I really just want to assign it to whatever faces we have selected. So this is all still in edit mode, which is important. And then once that's done, we can actually go to object mode and our script is done. We have materials and we have cool look in books. My opinion. 29. Procedural books - Creating a shelf of books: Alright, now let's actually really look at the potential of a script like this, right? So we're able to generate a single book, which is, you know, the fundamentals. But now let's actually try and apply this to January like a shelf or potentially even the library using the same techniques. So this is literally just the block and now he just kinda distributed. So what we could do is we could, instead of creating a book Wicked, say create a bookshelf or five books. So create fell amount of books. Let's initialize this to five as like a default value. And now for, Yeah, Just for every book that you want to make. So for I in range of amounts or books, I will create a book is like that. There we go. So now we have five books. Well, they're all overlapping and in the center of the scene, of course. But you know, we have five books. So the next step would be to kind of put them next to each other. Now for that, I would like to generate these values in here. So then I actually have, and I can actually access them. So let's say excised, well exercise. And they should be y size and Z size should also not be any, any commas here. Get rid of those. All right, so we are generating our sizes, so we have to pass those alone. Correct ones, of course. So this is when a copy pasting you always have to be like super colorful and observant that you don't like mess up. Because like small things like this are easily made and your code will still run. So sometimes it's a bit hard to detect those or 2D work though is because there's not like an error that just says, you know, what line to 19. There's a huge mistake. Like it doesn't say it. Okay, so are a great book. Goal is changed. So we wanted to change it here as well. And we want to change it here as well. So now our x and y and z sizes are actually generated in here. They get passed on through here, and then they get passed on through here. So that is good, should still be the same. Now we can actually move our, our books and here. So we could move it by however thick it is. And x, if I were to call, this, would be the same. We're just yeah, I'll actually, this is the Edison. These are accumulated over time. So let's say my x position will be, it will be 0 initially. And then after every book we regenerate it, we want to add the current books with 2D to the x position. So now you would see there would get next to each other with doesn't seem perfectly right. They kinda had to intersect, so this is not perfect. One thing we could actually do in order to catch this is a line, the book. So we were now aligning them on the ground plane, so we're pushing them up and the z axis. We should also do this on x and y so that they're on 000. That makes everything a lot easier because now this one is all free out. And then, well, it might not solve this exact issue, but usually it's best practice to do that. So if you were to go to the Create book, and here we align z. So that's actually just a line, a line. And then do X, Y, and Z. And then instead of doing a line z, we'll call this align XYZ. And I read just do x, y, and z. So just the same procedure, we take half of whatever the width or the height. So you can see that the pivot point, if we use this record, these guys for a second. The pivot point is in the exact center, also on the height, but this is already taken care of, so we only care about x and y at this point. So it's in the exact middle. So half of the x is going to be in the negative zone, and half of the y is going to be the negative of that one as well. So by aligning it like this, we will actually take off of the exercise. We will shift it over saying with the y. So our book will essentially go. Here and then it will go here. So we'll assign, it will align perfectly in this corner. So if you were to run this now, yeah, it actually seen that gets fixed know DR. perfectly touching. So let's do a couple more times. And you can see that the initial point is right here in this little center, which is usually the best. So yeah, we have books, man, they're already a little randomized, but maybe let's add like slight gaps through this as well because they're never going to be perfectly touching, I think. So let's go back into our offset logic here. Plus equals xyz. And that's like a random between, let's say 0.01.05. This will be like a little gap. So let's actually give it a variable name as well so that we know what's going on. Sometimes it's you really have to be descriptive. Yes, we have slight gaps. Maybe 0.05 is already a bit too much. Let's actually see what they are like. Starting off with random values is not always the best because you don't know what core like a good constant will be. So I would say this is probably fine. I want to have them a little bit closer. And then let's see a little bit further. I have some sunlight can kind of go in between. Which will make four Nice, nice rendering. As soon as well in all these values are arbitrary of course. So let's just do that. And now maybe they might align a bit too perfectly. So you can see however large they get mechanical, go to the back. So that's maybe you just have like super slight alterations in here as well. So they are kind of messy because usually when you put books on a shelf, they're not perfectly on the line. He's kinda drop him in there. And you've got like these little, you got like a more interesting silhouette if you kinda go over it. So that's kind of lists. Let's try and get that as well. So let's retranslate our x values or x pulse. And then for, let's say y plus three, degenerate it. So let's say random dot uniform. Anywhere from 0 dot 12 negatives. You wouldn't want to 0, 0, 1. That's a, that's a bit much. Well, that's not that bad. So you don't want is like 10 centimeters. So actually the furthest apart, they can be like 20 centimeters. Maybe it's a bit much. But actually in practice, think of is not terribly bad. Maybe the gap can be a little bit bigger. This is cool, right? Let's, let's actually make like 15 books or something. So it's a bit more impressive. Efficacy now actually all of these books get different colors. So, so might look alike because you know, obviously there like a finite number of possibilities. And also like they won't, it will barely ever be exactly the same. Other actually has an infinite number of possibilities because it's floating point numbers. So you can get infinitely precise. Way. Of course there's a limit, but yeah, that may raise it to that. And doesn't really matter. Well, one optimization that we can make. So now for every book, we are creating a paper material. So if we look here, so all of the government's cereals are in our unique and it's cool with all the paper materials are going to be the same. So let's maybe, um, maybe fix that. That could be an exercise for you guys as well. Maybe. So i'll, I'll I'll split up the videos for sure. So now this will be like the great a bookshelf parts. And then you guys go figure out how to do the paper material out to optimize it. So there's 0. There should only be one paper material, but it should be used by all of the books really. So that's actually a fun one to think about. I'll solve it in the next video. So if you want the solution, just keep on watching. 30. Procedural books - Optimizing our code: All right. I hope you guys have maybe attempted or maybe 50 even a good solution for the, for the many paper material problem that we're currently having. Now, keep in mind that there is plenty of viable answers. There's not like one, right? One or as I like the one I'm gonna be showing you as like the one that you all have to be following. Like if you, if you did something yourself, that's music. Now I'm just going to quickly go over why did it? And then we will wrap up this part of procedural modelling. For now. Obviously we can take this much, much further. I really hope that you do and have fun. But for the course, I think this is plenty. I actually let you guys experiments. No. But first, let's solve this paper ratio. So we can see here where we create this material. We actually don't always want to create a material. So what we can do here and create material is create or gets this. I think this might be the simplest solution. So again, it's not because you don't have the same solution that yours is, is worse. It's probably even better. So now it creates or it gets great material. I'll actually creates or get material. So we will copy this whole thing. Creates or get material. Now, how we can do this is we can say material equals bp. Why not the other materials that get made? So by doing that, we can actually query any material with this name. And if there is no material file, it will just return none. So by saying none as a second argument or we can actually, this is actually the defaults. We can say. We can just have it like this. So now this function is basically going to look into the collection of materials and it will search for a material with a given name that you specified. Now if that material exists. So now if material or let's say if this material is none. So that means no material was formed by the name that you specified. Only then do I want to create it? And only then do I want to assign the color. Otherwise, I just want to return it as is. So if I were to run this now, you would see I got 15 different books when I only have one paper material. So you can see 15 unique cover materials, but only one paper material.