 We might as well get started with this. Hi, I'm Jason, otherwise known as the other Jason. I'm Jason Van Gumpster. You'll notice there's four names listed on this. Jason Van Gumpster, Dr. John Denning, Jason Schleifer, and Brad Clark. They are on there mostly because this is all their fault. They have the idea for this, and we're doing our best to execute on that idea. That idea being people want to know how to get started doing Blender development. Hopefully, that's why you're here. If not, you're going to have a fun two hours. So we're going to start, I think, with a bit of a story. Why would you want to do this anyway, right? Besides the fact that Blender itself is open source, and you can. And I'm going to start with the way I got in. I'm going to start off first by saying I am not a developer. I will write code, and it is ugly, but it will do what I need it to do until somebody more talented than me can make it do it nicer and better. And so as an artist, I have gone through and found things that I wanted Blender to do. And at that point in time, at one point in time, I was trying to teach myself the Python API, because Blender 2.5 had just come out and had a full Python integration. I said, I'm going to learn this. And I decided I was going to write myself a joke add-on that would play elevator music when you rendered, because it was funny to me. So I'm working on this add-on, and it turns out that at that time, the event handler system within Blender, the Python event handlers, there weren't handlers for saying when a render was canceled, or when a render started, or when a render stopped, or if it did do it, it did it for still images, but not for animations. And so I needed that functionality for my, for my, I don't want to have, because I would write my add-on and would play the music, and the render would stop, and the music would just keep playing. You ever listen to elevator music for 10 minutes straight? It's, I don't, I don't recommend it. So I needed to have that functionality exposed. I knew that Blender, Blender knows when it's in render stop, Blender knows when it's when it gets canceled. So obviously the functionality is in the code, it's just not available in Python. I needed to monkey around on Blender's code to expose that functionality. And so I did, and I took a long time, and I did it wrong a lot, and I submitted a patch, and that actually got into, got into master. And is now part of Blender now, and it turns out that adding those event handlers is really handy if you happen to be running a render farm, because render farms also need to know when renders are canceled, started, stopped, and otherwise handled. So my little joke add-on benefits us all, yay. And this little workshop is really here to try to give everyone here a little bit of that experience as well, so that you can do those sorts of things, make your changes to Blender so that you can get it to do what you need it to do for your own work. And then if it happens to be one of those things that's useful for other people, then you can send it upstream, get it patched, and then you can help maintain it, or it'll become part of Blender proper, and then everybody can maintain it. So that's really what this talk is all about. And fortunately, like I said before, I'm not a developer and I write ugly code, he's way better at it. And so I'm gonna really give Dr. Denning here all of the time in the world to really enthrall you with the awesomeness that we have ready for you. So there we go. Thank you, Jason. In case you don't know who I am, I'm John Denning, I am short. I teach at university. Area, focus is in computer graphics. I teach data structures, algorithms, program languages, and so on. So I have a lot of development background. I've been working with CG Cookie and Orange Turbine on Ritopa Flow for a lot of years. One key thing about that, though, is Ritopa Flow is completely done in Python. So it's all done through the add-on stuff. But earlier this year I had a chance to work on Blender code. I had many, many years prior, it was about 10 years ago. I did do some work, but it didn't make its way into Blender. But earlier this year I was able to try to, or I started working on tools that were necessary for doing Ritopa work within Blender so it wouldn't have to be an outside add-on. We could be able to bring it in. So I had a little bit of experience getting to do coding in Blender. And this here is really just kind of an introduction to what it was that I learned. So I was basically a new programmer to doing Blender development. I'm not a new programmer, but a new developer for Blender. So I have here the QR codes listed, but just this Google doc that has a lot of links. There's a few more things down at the bottom. We had big plans. We're gonna see how they turn out. This may end up just being like, here's kind of a way to approach Blender development. Here's how to kind of think about it, but mostly here's some great resources. And then we may do a little bit more of kind of Q&A. I'm hoping some actual Blender developers are here. They could be able to help answer some questions. So the goal here is not necessarily to create a patch. There are coding guidelines for what is accepted into Blender code, just to make sure that whatever you submit is maintainable. It's not introducing more bugs. So that's a bit beyond the scope of what we're kind of going here, although this leads into that. The plan though is we're gonna give you the high level point of view of Blender, kind of how to look at the code. It is very daunting. There's lots and lots and lots of code there. And that code is also spread across multiple languages. And there's some code in there that's been there for 20 years. So some really old stuff, some really new stuff. So it's really hard to just kind of get a sense of this. So hopefully we'll point you to some road signs so you can be able to kind of navigate through that. And then we'll make just some small modifications to do something somewhat toy-ish, similar to what Jason had mentioned. But principled enough, hopefully, that we can empower you to jump in and be able to create either solving problems within your industry or actually for the entire community. So first off, the big important one is the document on new developer introduction. This is a great resource. We won't spend time kind of looking through this, but it details a lot of the ways of communicating with developers. You're gonna run into problems you don't know how to solve or not know where things are. So these here, these chats, are great ways to communicate with the Blender developers to kind of get some of your answers for you. I'm sorry that the screen, my laptop wasn't communicating well with the projector there. So here's full screen there. It's not quite full screen, so we'll work with it. But some really good kind of introductions to Blender development. Blender advice, environment, commit access, and so on. There is this link here that if you're really wanting to kind of dive in, you don't know what's to add on, what would be a good, because sometimes when you approach a problem, you don't know from the very beginning how hard it's actually going to be. So these tasks here were picked as being good introduction problems to solve. It's just fixing bugs in Blender. But these, the developers think would should be relatively simple to get kind of your feet wet and introduced to it. But let me pull up this. So if you clone the repository, which there are lots of instructions to do that, we're gonna have you, if you have your laptop, go ahead and try to clone if you have not already so we can make some edits together. But if you didn't bring your laptop or you don't feel like doing that, that's totally fine. We'll just walk through the steps up here. But we did plan to have you kind of follow along on the way. But when you clone this repository, you're gonna see millions and millions of lines and they're all kind of packed away in these folders here. Oops. And this document here, file structure, kind of gives you that high level view of how things are placed where. So under the doc, we have document generation. Under release contains data files for the data release. Icons files, platform specific things. Under the bin contains information that goes in the Blender folder. We have scripts, so all the Python scripts. Then down here we have build files for your different systems. CMake and make. And then finally here's application code. So everything's under the source. There's also external and internal libraries that are brought with it. Here is the actual Blender source code. And under this, basically Blender at this point is its own operating system. It does tons and tons of stuff, which means that you're gonna have a lot of files to kind of sift through. So Blender kernel, this would be your main functionality, the deep down belly part of Blender. There are a few pieces that are a little bit lower level than that, DNA and RNA, which we won't talk too much about. But these three parts right there kind of control basically everything. Everything else is kind of built up off of that. If you do any work with Bmesh, all the codes there, dependency graph, and then all the editors, we have animation and so on. So again, very high level. I wanna point out just a few more things before we dive into the code. Here's the code layout. So we have basic structure. We have the creator, which is the main part of Blender. Then we have all the editors that sort of sit on top of that, the editor utilities, tools that work within it. The event manager manages all the kind of communication between the system and other systems of Blender. Game engine, is there a game engine there? I didn't know that there was a game engine. Let's see, what else did I want to point out? Then there's the pre-compiled libraries that will often come as an SVN. So when you first download it, you're going to be getting the source code, but then there is a way to sort of build the libraries for your system, or you can download pre-built libraries. That will all happen through the makesystem. And then this has gotten a little bit more interesting, but the communication with the operating system and how does Blender communicate with it, especially now with Vulkan and Metal and all those other wonderful frameworks. Okay, final thing is this document here. Well, two more, two more. And then we'll get into the code. So the modules here talks about how, we saw how the files were kind of structured, the file structure of it. These here are the logical, although it fits fairly well with the file structure, but the logical modules, how the code is kind of broken up into different parts. So modeling, for example. Internets are kind of slow. Who's calling in? Everyone, because we told them to. All right, well, while that's thinking, let's see if this happens to pop up. Okay, so this document here is not official by any means, but during my time earlier this year when I was working, I needed a way to be able to structure what I was learning. And so I created this document here as just mostly a reminder for me, but it may be helpful for you as well. But in particular, about naming conventions. So you're gonna see a lot of different things. The naming conventions sort of kind of shifted over the years and also depends a little bit on developer. But this is kind of heading toward, or it's sort of all going in one particular direction. I'm hoping that I captured this well here. But if you're doing anything with the mathematics behind it here, doing so for vectors and matrices, they have a wonderful library that's very extensive, very well tested. But this here goes through all of the naming conventions so you can be able to figure out what function do you need to call rather than searching through a very dense library. But big thing, modules. I keep coming back to this. This is gonna be super important because this is how you get around in Blender is from high level to low level you're drilling down. But it also gives you key words to be able to search for. So I found it rather difficult to just come up with something new. But when I was coding, so instead what I did was I searched for things that were fairly similar to what I was wanting to do. And that got me very close to the right spot of where to implement. So that's the approach that we'll take today. So when it comes to naming conventions, especially with the functions, if you see capital letters, some sort of abbreviation as a prefix, these here are giving indication that these are public or globally accessible functions to Blender. So we're gonna have a lot of functions that are on the global end of things. And then there will be a lot of internal functions and figuring out which ones to call these prefixes and suffixes will help you with that. So BKE, the BKE is the prefix for a Blender kernel stuff. So these are typically low level Blender functions. I was doing a lot of work with retopology. So I was working with the Bmesh. So BM or BM underscore would be a prefix. This is not at all exhaustive. It was just what I was working on. But then there are non-modules related prefixes. So anytime we have some sort of registration, which we're gonna see a lot of, there's gonna be stuff that you need to let Blender know. Here is, internally, here are the things that were the functionality that we're wanting to bring in. But then, especially if this is communicating with Python, then we're also gonna have that same declaration, but now it's gonna be in Python. And anytime there's UI attached to it or some sort of callback mechanism, we may need to do some registration. Keep in mind, Blender is pretty old and the C doesn't have the ability to be able to do reflection, be able to see the data structures. And so there's gonna be a lot of functionality, especially with register and the RNA, DNA stuff, that is basically adding the ability to do reflection within the language. And then some operators, especially the suffixes EX EC, which is execute, invoke, and pull to know whether an operator can be executed within the particular context it is in. So maybe it's like a move operator, but you have to have something selected for it to move. So the pull may check that something is selected. And then invoke is performing the operation and sort of EX EC is also doing it. There's some slight nuance in there. We're not gonna dig too much. And some more info. All right, so did this, did not go. Try it again. All right, we'll skip it. Punt. Okay, so let's look at, let's look at Blender, this is 3.2. Hold on, let me load 3.4. This should be the latest version. So in Blender, lots of interesting functionality. Going off of what Jason had talked about in his little demo, so his was registering callback functions in Python that whenever some particular event occurred that his code can know about it, it can be called and he can execute some stuff. So playing elevator music when the render starts and then playing or stopping that elevator music when the render finishes and maybe adding in another thing that makes a sound when the render crashed or was canceled for some particular reason. So in Python, Blender Python, all of these now are accessible through the bpy.app.handlers. Oops, tab. So the bpy.app.handlers allows us to register a bunch of callbacks. So, yeah, what's the command that? Control scroll. You went that way, right? All right, we'll do this. I just won't see the bottom right corner. Okay, so under bpy.app.handlers, we have a lot of functionality that we can already do. So down here we have render init, render posts, pre, stats, right? So we have all of these hooks to be able to get in to know when renders happened. There are also composites when the dependency graph gets updated. So the dependency graph is underlying data structure for knowing when to update things. So you can have an object that has a modifier. It depends on another object. Well, if the one object is changed, well, we need to potentially update this other object. So it builds a dependency to that, and it can inform, hey, something is changing. We may need to do something, swap out some stuff. When we're animating, doing a frame change, and so on. So high level view. So let's do something in this space. But let's create a wrapper for, the modal operators. So modal operators, in case you're not familiar with it, are when you perform some action that sort of takes control of the input, so mouse and keyboard, until that operator relinquishes that control. So if we hit, say, we have this guy selected, we hit G. So now we're in grab mode, and we've taken over, so all mouse movements and keyboard inputs all going to that modal operator. And nothing else, so if I try to click on anything else, nothing else is happening, right? It's going to keep that control until we relinquish it. How? If we left mouse, that will commit. If we right mouse button, that cancels. So we have two different ways, similar to the renderer. We can start the render. Here we have start the modal operator. And then we have two ways of finishing. We can either commit the finish or we can cancel the finish. So today, as a simple demonstration, let's look at how to implement that. We want to know, we want to have a program that when the modal operator takes control, we know when that happens. And when it relinquishes that control, whether it was done through left mouse or some sort of commit or some sort of cancel, we want to be informed of that. So this is kind of what we're working toward. We want to be able to add in modal start, modal cancel, and modal finish. Okay, so that's a goal. So on that, the, how do I know it's here? So similar to what I had mentioned before, it's often helpful to start looking at something that is fairly well, fairly similar to what you're doing. So here it may be rather challenging to find the right, I happen to have them up here, don't look at that. But trying to find exactly where the, that file or where the edits need to go can be really hard. So here's the structure I mentioned, doc and external intern of the libraries and releases the release information with scripts and icons and all that. But under source, we have a whole lot more and it's just, it's just rather deep. So many of you have seen this, screamed and ran away, I did many times. So going back to here, what might be a good starting point to this? Well, it turns out the frame pre-change and post-change, for one thing, it's in the handlers. So we can look at any one of these and there'll be handlers. But the important thing is finding good search keywords to dig in. So for example, if we copy the frame change pre as an example, because maybe object, bake might be located in a whole bunch of other places in the code, we can use that as kind of a search term to start looking. So I'm using VS code to do my editing. You can use full blown IDEs, Eclipse and Qt and all sorts of wonderful packages out there. I tend to be very much on the minimalist side. VS code is a little bit more than what I often ask for but it does a pretty good job. So I did a global search and like I said, there's many different languages that Blender's written in. See, so this dot c dot h files and then they're transitioning to C++. So we're gonna see a lot of dot cc and cpp, they're using cc. And then there's also a lot of Python in there. We don't, we're not interested in the Python stuff. We're just looking through the C code. So doing a quick search, we see that frame change pre shows up in three locations. In particular, we have the bke callbacks. We have the scene dot cc and the bpy app handlers dot c. So this bke was the prefix that was for the Blender kernel and the capital, capitalization is, well in this case file name, it doesn't matter that much but those are the public stuff. So this is public within Blender. So when we do look at this particular instance of frame change pre, we see that it's inside of an enum. So like I mentioned, you're gonna see there's gonna be multiple places that you may have to implement. So in this callbacks here, this enum is giving a unique number to a particular event so that when the different functions get called along the way, we could just pass a single number and compare to that number. What the number is, in this case, we don't really care that much, whether that's a one or two or a zero. As long as each of these are unique, that's the important thing. But that's a very fast comparison. We can throw it into a switch or a bunch of if statements if we want. So we see here, actually it turns out all the events, all the callback events, that's what the CB is standing for, the callback event handlers are all listed here. So if we're going to add in a modal start, modal finish, and modal cancel, this would be one place that we added in there. Now sometimes in Blender, these enums have a very specific ordering to them. And sometimes they even have very specific numbers assigned to them. So any changes that you make, just kind of look around the area and see if any of the code happens to care about it. Here ordering might be important. It is in one location, I'm not sure about all the rest of them. And so it may be wise not to stick new stuff up here, but instead to append it at the bottom. Now I mentioned that ordering matters for one thing, and that is typically this last one. So within enums, you don't know how many different ones there are going to be. And so often at the very end within Blender, you'll see sort of a placeholder that has a fixed name. And so anytime Blender's writing the dot blend, save and load stuff, it knows how many values are going to be here so it can allocate enough space to store each of the values. So we probably want to keep this one here, this little token here at the end. But we can insert our own. So if you happen to be following along at home, you can just insert in following similar naming conventions, BKE, CB, EBT, and then we'll call this modal start or init or something like that. And then we'll have another one for finish and another one for, okay. So we've already begun doing our first edits. So I'm going to save. All right, so let's look at the other functions or the other instances of the BKE, CB event frame change pre. I'm going to skip over the scene for just a moment. We're going to jump to the BPY app handler. So this feature, the frame change and then also the modal start and whatnot, they are going to be callbacks that are written in Python. So internally now, Blender has some notion of the callback that we're adding in there. So it knows internally, but it hasn't told yet Python about that functionality. So the BKE callbacks is for the internal, it's the Blender kernel, it's the inner guts of Blender. But the BPY, the file is under BPY and these are located in source Blender Python internal. These files here are that bridge, the bridge code between the internals of Blender and what you can do in a Python add-on. So if we look here at the instance of the frame change pre, we see a whole lot of text. So this is inside of an array of py struct sequence field and then we have an array of these strings or a character arrays. So the important thing is we have one string and then a second string. So we have two things in there. The documentation's not super thorough in this place, but the first string is going to be the name that shows up, so when you do bpy.app.handlers. Then that will be the name of the function or the module, the package that we will be appending to. And then the second string is a description. Now notice there's no connection between this array and this enum except for they happen to be in the same order. So we have frame change pre, frame change pre. We have frame change post and frame change post. So that's how, again, no reflection. So this is how Blender is keeping track of stuff. We have the enum and then we have sort of a registration mechanism for Python. So the very last one in this list, oops I hit button, very last item in here was the composite post and composite cancel. So we're gonna insert our modal start and finish and cancel after that in the same order. Technically this is adding in another item in there. We're just going to ignore it for right now. All right, so we have our modal start and this happens on the start of modal operator. You can have whatever text makes sense. The actual text that goes into there when it comes to submitting a Blender patch will be more important, but if we're just making a modification to expose some functionality, this will work. So we have our start and then the next was our finish, finish of a modal operator and then we have our cancel. Cancel, canceling, cancel. Cancel of modal operator, sure. All right, so when we save, we now have a new set of app handlers that we can register to. In fact, we can go ahead and build it. So let me show that happening. So notice it wasn't there, we have no modal thing. All right, so let's do a make. And I've already built this. So if you haven't yet built it, this will take a lot longer than it does on my machine here. The build chain for Blender is documented pretty well on Wiki and it's a lot easier than it used to be a number of years ago. So that's pretty well worked out there. That said, we do have a modal system and hopefully have time to show off a little bit. Yeah, I was going to show it off after this. So if we've now built, it was successful. Now if we rerun, we should have now bpy app, wrong way, dot handlers dot. And now we should have our modal cancel, finish and start. But you notice if we append a function to this, I don't have a function, let me create a function. Does nothing, a wonderful function. We can append a function to it, but this is never going to be called. Because inside of Blender, we just have those handlers, but nothing is issuing the command to make the call. So foo will never get called, unfortunately. All right, so poor thing. Do you want to talk a little bit about the build real quick before we take a break of sifting through code? Yeah, yeah, let me talk about that a little bit. So, oh that's perfect, all right. So, one of the things that used to be pretty onerous when it came to, and it still is because there's just a lot of code and lots of stuff to download. One of the things that used to be pretty difficult when it came to doing any sort of Blender development was the actual time it took to figure out how to compile the thing, right? If you're doing Python, you write Python, you run Python, you're done. Right, c, c++, they don't work that way. They're translated to assembly so that your computer can do it faster and better. And so the wiki, oh no, did I break it? Nope. Should be, actually I'm gonna do, yeah, should be. Blender.org, delete all the rest of that. So in here, there's a whole section here on building Blender, and like I said, it's way nicer than it used to be, especially on the window side. Windows used to be really annoying to try and figure out, but you can walk through, if you're on Linux, you can walk through and see what you need to actually install ahead of time. On Windows, it's surprisingly easier. You just basically need to have Windows Studio Community Edition get and subversion installed. And then it has the steps for cloning Blender and doing an update and then actually doing a compile. And like John was showing, it's basically your type make and the magic happens for you, which is fantastic. But it also takes a lot of time and especially if you happen to be on a low powered laptop or something like that, it can be pretty bothersome. So one of the things that was part of our big ideas for this workshop was to not have to have you guys worry about this at all. And I got mostly there. So the Blender Studio, when they have to do compilation for doing releases or even tests or nightlies, they have a system called Buildbot, which is actually the name of a software package called Buildbot for that that automates the building process and generates all the binaries and puts them up so we can download them and play with them. I re-implemented that and made a version of it that sits here on AWS. And that's the stuff that lives at the bottom that's this talking about right here. And one of the nice features about Buildbot that is actually not in the Blender Studio, the Blender Foundation, Blender Institute, so many names for the same thing. One of the things that they don't use is a feature where you can actually trigger a build with a patch that's never committed to the source code. So the stuff that John's been showing you, you could actually say Buildbot, try making this. And if it succeeds, it'll generate that binary, you can download it and play with it. So that's all actually done in Python as well. So this is this sort of environment for setting that up. And I only got it working in Linux. I got it working Linux and Windows on that machine, but you guys can't access that machine because that's my machine. So I got this put up on AWS and getting Windows playing nice in AWS is harder than I thought it was gonna be. So I haven't been working for Linux and basically what the way it works is you have builders and I have it set up here on Rocky Linux and I can just force it to build either the LTS release or master and it's actually configurable so you can do other things such as that. There are other releases, other builds, if you happen to wanna build from the sculpting branch or those sort of things, you can actually configure it to do that. Now, this is running in the addresses right there, living on AWS and I don't think I've put any regulators on that. So people will see you quickly. They're being gentle, they're paying for that so please be gentle with them. But you should be able to, following these instructions, install build about on your machine, whatever you modifications you make to Blender, you should be able to, I'm gonna try and leave this up throughout the weekend unless it gets really crazy. I'm gonna try and leave it up throughout the weekend so if you make a change to Blender, you can use this to build. I haven't gotten it yet so it's gonna be able to deliver you your binaries when it's done, but that's what I'm gonna work on after the workshop so that if you do trigger a build and it says that it finished it successfully, come track me down and I can provide it for you. But yeah, that's sort of a, the fact that that works and is handy is actually a really good way to help build, get Blender working. Because this way you only really need to download the Blender source code. You don't have to download all the dependencies which the SVN dependencies is, it's large, it's very, very large. There's a lot of stuff that has to get downloaded for that. This is already on this image and so that whole cycle tends to be a lot faster for you. So I'd be curious to see when people, if any of you decide that you wanna test this out and play with it, definitely tell me how it works for you and maybe it's something I can make prettier. I'll give it back to John. So if you have questions on that, we can be able to answer quite a few of those. Okay, so we do have now a mechanism for having Blender register, in Python be able to register a function to be called back and Blender knows about it but it doesn't do the calling back part of it. So this part here becomes a little bit trickier in terms of figuring out where you need to touch the code. So an interesting thing to look at. So we're interested in knowing when a modal operator starts and when it finishes and cancels. And if we go to, hey, I'm gonna go, not here. Let's go to the wiki, no. I'm gonna do the, if I can spell. So we're gonna go to the Blender API and in particular we're going to be going to the BPI Ops, BPI type Ops operator. And we see that there is a function that gets called to, so you can register an operator in Blender and we already saw the invoke and the poll invoke and execute. But operators can, so they do particular action but if the operator calls a very specific function, if I do a search for the modal, make sure I'm on the right. Oh, we're on the ops, that's why. We wanna go to the type, operator type, types. I should read my notes, operator types. Operator, all right, here we go. Oh, it's a capital O, all right. So in our Python, we can create an operator that executes and does all the good things but the important bit is the modal. So when an operator calls this special command here, context.windowmanager.modelhandler add self, then what that's doing is it's registering this operator with Blender to be a new modal operator so it's going to have, it's going to sort of put it on this modal operator stack. It's not really a stack but it's kind of like a stack. And then from that point on the window manager, every time an event happens, so a user moves the mouse, hits a key, a timer goes off, there's some other event, the render happens to finish, all that sort of good stuff, then that event information is going to be passed on to the operator and in particular is going to be called, using the called to this modal function here that is given information about the context of the scene but also the event to be able to do some, do some edit or do some things, respond to it. But the key thing here is this modal handler add is the function that we are interested in searching for that when that gets called, that's when this operator gets registered. So I'm going to copy this and go back to our code here. I'm going to do a search for modal handler add function. All right, so we have now this RNA which I talked a little bit about, window manager API. And in particular we have this function here, it's commented out, we have this function here. So there's RNA def function. So RNA is sort of the reflection needed, reflection necessary for describing Python functionality or some sort of functionality to the broader system. So here we're creating a modal handler add function that has this text here. So if we were to type it into the blender console, it would be able to provide us with some information, some detailed information description and so on. And then detail information about the various arguments that it has. And so when the function gets added, there is, oh, where was it? I thought there was a link or a, where is it, where is it, where is it, where is it? RNA event, okay. So what I think what we'll do is this. So this here is the function that gets called when the, when Python, when you call this modal handler add function. So if we do a search for the event, oh, that's right. Okay, I'm gonna throw some magic in here. So it's not actually event modal handler add, it's actually event add modal handler. There we go. All right, unfortunately some of the naming conventions is not quite consistent. So it does make finding things a little bit tricky. So whenever you see a verb, especially at the very end as a suffix, try moving it a little bit farther in front because sometimes it's used as a, here's what gets returned or here's the action that happens as opposed to we want to add a modal, yeah, anyways. Okay, so when we know the right incantation, we can do a search for these items. And you notice there is a whole bunch of WM event add modal handler. This here is the WM being the window manager and being capital means it is publicly available basically throughout all of Blender. So it's used all over the place for eyedropper, for edit mesh, physics and so on. So we can try to look for that specific implementation or just click on one and then in a fancier system IDE, we can go to the definition of it. And under this definition, so this is the function that gets called when an operator calls the add modal handler. And we have here, so let's try this. I was trying to recall what, if we had any conditions that we have to do, so I don't recall off the top of my head. So what we're gonna do is this, I'm gonna add in a print that is just giving us a little feedback here. We could use a proper debugger, GDB or otherwise. The debug print is fairly handy for without needing to pause or set breakpoints. Plus building Blender with debug symbols takes an even longer amount of time. So, all right, so here we still, so if we grab this, you notice now I have, let me space down here. So when I go to hit G, that gets called. So every time a modal operator or an operator wants to become modal, it's going to do it through that WM event or add event modal event. But the escape for cancelling and left click for committing are not handled through there. It is only when they are registered. So what we're going to do is we need at this point rather than just printing out that the modal handler was added, we want to have our code, our event handler get called back. All right, so that is where. So when we did a search for, going back to here, I'm gonna do a search again for the frame change. There was this other scene.cc file that I said we're going to skip for just a moment. If we look at it now, we see that there's this function here. This is the actual mechanism that causes the callback to kick off. So what do we have? We have BKE, it's blender kernel, callback execute, and then there's this little suffix here. We have a few parameters. We have B main, we have a scene ID, and in particular here is the callback enum that tells which callbacks to call. I'm gonna spend just a quick moment to jump into the definition of this function here. So this takes us to the callback.c file. And if you look, there's actually a few different BKE callback exec functions. We have this one here, which has a null suffix. This one here, which has an ID suffix. This one here has an ID depth graph. And then they're all calling this one that has no suffix, which is listed right here. So this BKE callback exec is kind of the main function that all of these end up calling, but they pass in different parameters, different pointers. So these are basically convenience functions. If we're not passing back anything, so we're passing in nothing back to have as arguments to the callback, then we could just call that under null and we don't have to say null for the pointers and then zero for the num pointers. If we want to pass back a particular ID, this is an RNA ID. So if you're wanting to have information about the scene, you can pass that along. If you want to pass also the depth graph, dependency graph at the current point in time that it's evaluating, you could do that as well. So since we're not really caring much about the scene or any other information for now, maybe we want to pass back which modal or which operator it was, but for now let's not worry about it. We can just call this BKE callback exec num function. So if I copy this, go back here and rather than or maybe in addition to doing a prints to the console, we're gonna call this BKE callback and again looking at what had already been done. Where were we? Is it this one? Yeah, so this one we passed in an argument b main. What's b main? That is the main. I don't know. So a lot of times when I'm developing, there may be some arguments that just need to go along with it. Sometimes I just punt and say, well, whatever was there is probably working, so I'm just gonna copy it and see what happens. So that's what we're going to do for now. We're gonna copy this main so that we can pass it in over there. So we're here, nope, we're in here. So we need to pass in the b main. Well, what's the b main? That was what we just copied. And then it does require a depth graph. Where is the depth graph? That one. Where did that come from? Was that passed in? Surely that's passed in. Surely I've done this before. Where did it go? Live demo. All right, well let's do this. I'm going to search, we don't have a depth graph. So that was the, so I'm just searching for everywhere else that happens to be called. Well I'm just, I need a b main, or a main, but the depth graph wasn't passed in. In my event handler here. Oh, wait. Yeah, I needed it to be able to get the b main. What would you say? What's that? I'm going to try and know, let's see what happens. If all else fails, I can switch to the branch that's actually working on. All right, so then the final bit is the enum value that we are interested in calling back. We're not wanting to do the frame change, but instead we want to do the modal start. That should be fine. I think the only other thing is, this function here, even though it has capital letters and I've been claiming that it's public, this function here is defined in another module. So we are going to need to include in, let me jump to the top here. So at the very top there is a bkecallbacks.headerfile that we'll need to potentially add into here as well. Because it's not there. And if we don't have that in there, then it'll just say, I don't know what that function is. It looks like it's under here, so how about you add it? So if we build, let's see if then, oh, I forgot to take that one line out, didn't I? Take this line out, because it's going to complain about that. Yep, where did that depth scrap come from? I'll figure it out. Oh yeah, I'm just zipping through. Yes, thank you. I'll show that. I'll bring it back in. I want to make sure this works. All right, so we hit modal handler is happening. So theoretically our function should be, or our callbacks should be getting called back, but we haven't registered anything for Blender to callback. So I have here just a simple program in Python that has a modal start, cancel, and finish functions. I have just accepting all the args and keyword args so that we could just print them out and see what they look like. But the important bits are these three lines down here where we're registering these functions as callbacks to the app handlers modal start. So if we were to type in, I'm gonna blow this up a little bit, bpy app handlers dot modal start. Underneath this, we have several functions for adding in our event handlers. We have append, which puts it at the end. This is just a list. But we can also pop and remove and insert in arbitrary places. So if there were gonna be multiple modal starts in here, we can specify modal start event handlers. We can specify where those go. So I'm going to just run this program here, which will register these functions. And then now it should, in addition to printing out modal handler added, it should print out this start when it works, hopefully. Hey, look at that. All right. So now whenever the modal operator gets called, the start gets, or our callbacks get handled. And the arguments there, there are two arguments that are getting passed without keywords. So they're just plain args. It's just two nuns. But we have some things going here. All right. So going back to my issue I had before, so I'm gonna reintroduce this bug. We're using a variable that's undefined. I'm gonna show you Blender's, or GCC's error message. So here it's saying in our function, WM event system.cc file, there was on this particular line here, there is an argument that was used, but GCC doesn't know what this is. So it's just complaining about that. Down here we have a warning that we're declaring a variable, but it's not being used at any point. If you are really interested in getting your patch submitted to Blender, Blender Core, they have some strict rules to make sure that, well, A, your code should compile, but B, it does not introduce any additional warnings. There are warnings that do show up whenever you build. There are probably some that showed up here. So whatever was there when you built, make sure you don't introduce anything new to it, warning-wise. All right. So I'm gonna actually properly delete that so we can send null as a main. That's fine. All right, so now we have the ability to get called back when the modal handler starts. Now we need to handle the two other, which is when it finishes and when it's canceled. So the way that Blender does this is, well, this modal handler add function gets called to tell Blender, hey, I want to, this thing right here wants to get called modally, and so Blender will look at the modal operator, and it'll call the modal function when it needs to handle a particular event. And then depending on what this function returns, will tell Blender whether it should continue as modal, whether it has finished doing what it needs to do, or whether it is finished, but it was canceled. So maybe the calling function needs to be informed that this operator was canceled. And the way they do that is they just return these keywords that it's a set containing a single string with it in there. So it's really the, when this function gets called, this modal function gets called, what it returns is the point when we know whether it's finished or canceled, or if it's continuing on and we should not call any callbacks at that point. So the main function, and again, this is gonna be a bit like, just trust me, this is where it is, we could dig through, it's actually in this file here, but we can dig through, but I'm just gonna jump us to it for the sake of time because I'd much rather spend time like talking about interesting things than fumbling about in here. So there is a function called WM handler operator call. This is a static function, which means it's local to the file. So outside of this file, we're not gonna be able to access this. The way that I got to it was looking at the WM functions, there are a lot of them, but looking at, especially the capital WM functions. And one of these we'll call this operator call function. But the important bit is, we call the modal and we get the return type. And then down here, there are a few cases to handle. So here we're checking to see if the operator was canceled or finished and so it could do its reporting. But here is where the actual finish and freeing happens. I'm not entirely sure why this isn't else if. I think this might either need to be an if and then remove the operator finished or this should be one of those two. But here is when the operator is finished. So the finished string is returned from the modal function. And then down here, this is done when it is canceled. So we need to do those callbacks at those two points. And the command that we used was bke. I'm just gonna copy it. What was it? Callback exec null. So we're just basically gonna copy this and paste it. So hopefully you're seeing, that's the select, we don't want that one. It's right here. When you're beginning in the Blender development, especially when you're making modifications, look to what you've already, look to something that's similar so you kind of know where to start poking around. It can take a little while to setting breakpoints or adding in prints to figure out where things need to happen. Doing some tracing. But copying the similar structure from what's around and doing some light modification as little as you can is often helpful. So after this happens, maybe we'll do it right before we null it out. We want to call our modal, in this case, we want to do the finish. When this happens, and then when it gets canceled, we'll do the cancel. So again, maybe place it here. Now, whether it should go here or up here or we create our own ifs somewhere else and stick it in there, that's going to be tricky to determine. It's gonna depend a lot on what you're working on, the context of it, because ordering can be crucial and especially if you're passing back information about the modal or the operator that was being executed. So you wanted to inform the callback which operator was canceled or which operator had finished. You might need to do that prior to finishing the operator or freeing it. So you may have to kind of play around with where that goes. But we'll introduce these two lines in here and then try building it once more. So we have now callbacks, presumably happening on cancel and on finish. So if we execute this code and I'll post this up on the drive as well. So now when we hit G, the modal operator will start. So we still have our modal handler added print that's happening, but importantly, the start is getting called, the start callback is getting called. And if we left mouse to commit, now finish, the modal finish is getting called and if we right click to cancel, cancel happens, which is good. All right, one quick note, especially about the modal operators. This is in some ways a stack. So you can actually, one modal operator can call another modal operator, which can call another modal operator, which is a little bit weird. It's not quite a stack in that you can remove a modal operator at any point. It's not just the top. But this, unfortunately, the way that we wrote this doesn't give that information about which operator it was. Oh yeah, also resizing the window is a modal operator. And as is selecting, there's a lot of modal operators that happen under the hood. But one in particular and rotating, that's another one, is the knife tool. So when you hit K to go into, so we're in edit mode, you hit K to go in a knife tool, that is a modal operator. So now this functionality is being executed modally, but in particular, and this is somewhat new feature, you can rotate the camera. So rotate was a modal operator, but we're already in a modal operator. So what it's going to do, so we have this start here. We've started a modal operator. This is our knife. And then if we middle mouse to rotate, you'll see another start show up. But we hadn't finished or canceled the previous one. So we're still in modeling the knife, but on top of that, it's the view. And as soon as we let go, it finishes, and then it pops the view modal operator off and we're still going. So the data structures, internal data structures are going to be useful to know. There's a lot of them. It's rather daunting at times to kind of figure all of those out. But at this point, we have a fully functioning, we have an additional print in there that we can easily comment out. But we have a mechanism for calling calling these functions when an operator starts and when it finishes, okay. All right. Go for it. Right, that's a very good question. Right. To be honest, in my prep work, it actually wasn't all that much different than what we did today. The only things that were really kind of tricky, it took some poking around where the two places said, we're gonna ignore how we did this, but here's in general how it happened. That search is your friend. And especially mixing around some of the keywords, I did a lot of regular reg X searching. So if I knew that I had certain things I wanted to look for, but I didn't know if there happened to be stuff in the middle. So I would use a lot of reg X searches for that. But really just kind of looking at things that were very similar to what, just what we did today. We're like frame change, pre, that seems rather unique set of keywords. So doing a search on that was actually, yeah, it was pretty quick. I think I only did one other search, one other, I don't remember which one it was, one of the other callbacks I searched for it, but it had like a thousand hits. And I was like, oh, let's try another one. And that one happened to have like the three that were important. And it was like, perfect, I'll go with that. Yeah. Yes. Potentially, yeah. Especially if you have a lot of operators. Typically the operators may be executed, but they're not necessarily running modal. So it's not gonna be quite that bad. So anytime like it takes control of the keyboard and mouse, that's when it will be hit. But you could, yeah, we did not do any conditioning on when that callback happens. So we could potentially do that. We can drill down even further. It's like view 3D modal start. Yeah, we can absolutely do that. Good. And then. Yeah, I can talk about that a little bit. Yeah. Have a microphone, I guess. So for those of you who might not be aware, the Blender Foundation is changing the forge that they're using for storing and bug reporting and patches and all that sort of stuff. Right now, they run on a system called Fabricator, which is, it's been in of life for about a year. And so they've been working to try to migrate to something else. And they've got a solution for it. And they're probably, maybe, aren't starting to hear. Yeah, I'm making sure Arn's not in here so I don't tell them anything too wrong. They're looking at about a year, not at the end of the year, is their ambitious goal to switch over from Fabricator to Getty. And how many people here have actually worked, like reported a bug to Blender? Yay, good. So you've played with Fabricator and all of the joy that's involved with that. Notice the sarcasm. Fabricator is good for a lot of different things, but they are migrating to Getty. Fortunately, there's not, from the perspective of anybody who's gonna be interfacing with it, outside of some workflow things, there's not a whole lot of difference from our end. One thing that you're definitely gonna wanna do, if you don't already have a Blender ID, which you're at the conference, so you should have one because you needed to Blender register. But anybody who's viewing this on YouTube or whatever, if you wanna do development for Blender, get a Blender ID because the Getty instance is gonna be authenticated against Blender ID. All of the Fabricator registration stuff, but they're gonna migrate what they can and then whatever they can't, hopefully they'll notify you to say, get your Blender ID set up because that's gonna be required for doing bug reports or doing patches or any of those sorts of things. The other thing is the workflow is gonna be a little bit different as well because what Fabricator currently does is you can make a patch and submit it and they don't necessarily, unless you write in the documentation or you use Fabricator's tool called Arcanist, which is, the recommendation is to use Arcanist because it, so let me sort of, if I can borrow this for a second here. Where's your terminal? I need a terminal. It's on this way now. Oh, it's on that one. Okay, cool. I will correct it. Can you actually pull up the patch so you can show people? Well, we can actually pull up this patch for the time being first. So how many guys have, how many of you, the guys and ladies I guess, have worked with Git at all? You got a few of you. So you can make a, you can make a diff, you can see what's going on, fantastic. So that's, don't save, don't save. So we have this, so you can easily go here and just do a get diff. No, there's only one F and diff, two Fs and diff. All right, so this shows all the changes that John made. And you can step through it and make sure that what you want in there is actually in there. Maybe you want to take this print statement out. Maybe we want to keep it in. We're just stubborn like that. So do you at least know how to make that diff? Now, the Arcadis workflow doesn't work like that. There's voodoo incantations and you're supposed to, and the one good thing about a diff is that it's gonna give you what the file state is and what the file state's gonna be, right? The stuff here that's green, that's the changes. If he took a line out, it'll be red. We'll have a minus next to it. Great, fantastic. One thing it doesn't say is what revision is this patch being made against? You don't know. There's nothing in here that says, you have an index here, but there's nothing in here that says, I'm applying this to the master branch or I'm applying this to the Blinder V3.3 release branch or 3.4, which is coming out imminently, right? You don't have anything like that. And that's, Arcadis is supposed to handle that for you. And that's one of the challenges for people who have been making patches without Arcadis. That's been one of the challenges they've had in migrating is trying to figure out where do the, he's been calling them baseless patches. Where do they, because they have no base. They have no foundation upon them. Where do they get applied? They have to do some really funky things in terms of, this patch was submitted on this date. So maybe it's close to that or backwards. And so they have to do a bit of code sleuthing on their end to make sure your patch applies. One thing you can do if you don't want to use Arcadis, and I know I don't, when you submit a patch, say the revision that it's a part of, and you can, I think you just do the git status, right? It shows, yeah, you have to do log. So git log is gonna give you all the latest little hashes and you can say, all right, this is what I'm gonna be applying it to. It's a long hash, you can copy and paste it into fabricator. The point I'm making at this point is that they want to migrate to GT before the end of the year, and that's an ambitious ask. If you're trying to make patches and you want to send them upstream, I wouldn't count on that happening. And so don't wait for that, make your patches, make your code and submit it now. Whatever you do, the date is gonna get across and you might get a note saying, hey, when did you apply this? Or in fabricator, say, this is the commit hash, please apply it here. And that's really the nuts and bolts of it. If you want to actually make a patch, there's actually really good, did you actually have a browser for that open already with the git diff to patch documentation? Hope that was in there. This one? No, that's the one I brought up. It's making a patch. It's in the document. Yay, cool. Hey, so yeah, the walkthrough for that is fairly straightforward. Creating patches. Yeah, right here on the creating patches section, stand on the camera. Hi, camera. All right, then the creating patches section here. We did git diff, you saw what the difference is. What this is is feeding that diff to a file with the suffix diff. And we can, or you just copy and paste it. If it's multiple lines like that, sometimes it's gonna be kind of painful to copy and paste it in a fabricator. I know that I have a tendency to forget the first two characters when I'm highlighting things. And so doing it this way prevents my stupid kinds of mistakes. Yeah, actually, that's a good point just for anybody who didn't hear. Before you try to submit a patch, maybe try to make sure you build against whatever the latest thing is. I'm gonna actually say one more thing beyond that. If we're doing this stuff for production work, maybe you should be coding to the LTS, right? So work towards the Blender V3.3 branch. That branch will at least outside of bug fixes maintain stability for the lifetime of, what, I think it's two years for the lifetime of that branch. So that's a good way to make sure, especially on the studio thing, if you're not working from master, which unless you're in some really weird situation, maybe you shouldn't, you kind of wanna work from LTS anyway. And that way you know your patch is gonna work at least for the next two years. The other thing is that I'm even sure with Blender developers here, attending the conference, being busy here, I'm still confident that probably in the last 15 minutes, 13 other different patches or commits got made and sent up to master, master's very fast moving. So having something where it's a little bit less, has a little bit less velocity, it's gonna give you a little bit more sanity in the way that you're developing things. And again, from a production and studio environment, you're gonna be working likely more with an LTS version anyway, so you're gonna be building against that. But yeah, so you make your diff and that is what gets submitted up to FAB. If you haven't played with it, with most of you said you did, but that's gonna be developer.blender.org. If you did not attend Arn's talk earlier today about the migration from Fabregator to GTI, developer.blender.org, the domain's gonna still be there, but that's not gonna be the home of where GTI is. That's gonna be, GTI's gonna live, or the New Forge, keep calling it GTI, but the New Forge is gonna live at projects.blender.org. You can get a preview of it with totally test data at future.projects.blender.org, which they're running GTI right there and you can actually poke around and play with it and work with it and really one of the things that they're trying to do is really understand what the workflow's gonna be like because in Fabregator, the tasks, issues, bug reports, patches, they're kind of loosely coupled. Like I said, it doesn't even know, unless you put it in the comments or unless you use Arcanist, it doesn't know where your patch belongs in the blender source tree or the blender source history. GTI works more in a GitHub-esque kind of way. So you're gonna do more of a proper pull request workflow. And so that'll be a lot more familiar if you've played on GitHub or GitLab or any of those systems. And so that at least will taste more similar. But that's also part of the reason why migrating from Fabregator to GTI's been fraught with the challenges. Oh, I just lost whatever I was. So many tabs. I lost whatever I was on. Doesn't matter, I'll hit back. Ah-ha, because I typed. So yeah, currently you're gonna wanna do your patching or your pass submission through Blender, developer.ginter, that one I'm gonna speak today, developer.blender.org. And you've got report a bug, submit code. You're gonna submit code. I am not John, so I'm not gonna log in. But let's see if we can find somebody, some enterprising person who has already submitted a patch. Find their code of use. Because one of the things I'm gonna say, if you do want to push something upstream, expect to get feedback, expect to get corrected, expect that whatever you submitted may have to get massively changed. And it will likely be slow. So what typically happens is a lot of the times, sometimes you do have to poke or pester or know who to get in touch with. This is where what John was saying earlier and the webpage didn't load for it, module owners, knowing the module owners is really important. You're at the Blender conference and even if you're not at the Blender conference, Blender.chat and email and all of these things are very, very useful and helpful, that Wiki page that had a problem loading. That's also very helpful. Find out who the module owner, if you're working on the animation system, find out, go to the module, find out who's owning that, who belongs to that, and you wanna get in touch with them because they have ideas, right? Not only do we have ideas about what Blender needs to have, we want Blender to do, but anybody who's already doing development with Blender already also has ideas. And they may also have ideas about how they want things structured. We may have a patch that we wanna put in. I need this feature in. They're like, I know about that feature. I want that feature too. I'd like it to be structured in this very particular way. This is the moment where we're like, okay, this is what collaboration is all about, right? What's not, here's my patch, go play with it and have fun. This is collaboration, this is communication, this is doing it on a large, large scale with 2.2 million lines of code. So this is where using good etiquette, using good conversations and being gentle and polite really, really play to your advantage. Also, if you're at an event like the Blender Conference and you happen to look up who the module owner is and you know that they're here, get a face-to-face conversation going. It goes a long way and they say, oh, I remember your face. I know what you're looking at working on. You're actually pretty pleasant to talk to. I'll be happy to help you. As opposed to, I need this patch right now. Who are you? I care a lot less. I mean, it sounds gruff, but that's also the way humans tend to interact. We work better when we know who we're working with. This is a community. We work with people. Everybody here's a people. Everybody here's a people. I swear, English is my first language and only language. But everyone working on this is a person. We're subject to emotional mood swings. We're subject to having good days and bad days. We're subject to having good opinions, bad opinions and being wrong about stuff. And the more that we can be empathetic about that and understand it, the easier this communication between any of us is gonna happen. Cause nothing happens if we don't talk to one another. And if we sort of take our ball and go home, that's not gonna play or say, here's my land of the sand. I can't do anything beyond that. That's also not gonna help. So that's, I mean, we're dealing with, there's 20, maybe 30 core developers on the actual Blender team itself, but there are, I think, I think they were telling me 80,000 users registered on Fabricator right now. Bug reports, documentation, people doing patches once and then leaving. That sort of thing, we're all there. And sometimes people just doing triage and determining that, oh yeah, that bug actually happens. This is all an effort in communication. So really, this is where, especially, especially since maybe development is not our primary thing, it's not our first thing. Maybe it's, we have a lot of other tasks that we would rather be doing, our animation, these sort of things. But putting the effort into sort of shepherd your needs across really benefits you and benefits everybody else and really it's worth taking that time. And so that's my little, that's my short person soapbox about that. Sure. And for instance, knows the NLA, if I try to help him, I'm like, I have any of the works and he's like, no, no, no, I don't have, well, I'll fix it. But that actually is so incredibly helpful. You can pull the code down, you can build it and you can test it. And honestly, that is super helpful. And that's all you do, great. Actually, that's a really good point. If you wanted to get more involved in development, but you're not really ready to do actual code changes, like what we showed here. Well, if I can figure out where I, that's one of them, creating patches. Okay, so we're just talking about getting patches, creating patches, you can get on Fab just as easily and pull a patch down. That means you can, where to go? Apply that patch. Yeah, that's important. And so when you're applying that patch, you have the code, you apply the patch, you do the build, then you can test it. You can say, oh, it actually built, or no, this thing doesn't even compile for me. Or it compiles against master, but not 3.3. This is the sort of feedback is really relevant and helpful to anybody who's doing that development. So even if you're not writing the code, now you have an idea of how to build it, you have an idea how to patch it, and now you can give feedback to it. And that itself is just as valuable as actually writing the code itself. I think that covers most of the fun things I had. Is there any other fun comments, questions, exciting anecdotes, boring anecdotes? Oh, yes, some things you can prototype pretty quickly in Python. There's a lot of things that you can do, like if you want to add a modifier, no, you're gonna have to go dig into Blender and you're gonna be elbow deep and there's gonna be a lot of tears. But for general functionality, if you wanna prototype something, you can often get that done in Python. It might be slower, it might be ugly, but at least you'll have an idea of this is what I want and then maybe show that to somebody who understands the C side of things or that make it more performant or to say, you know, you can still do that in Python, but these are the ways you wanna modify it. That would probably be the best sort of entry point for sort of higher level functionality is trying, try first, which is what happened with me. I tried first to do something in Python and I couldn't because something was missing from the Python API and that's what made me dig into it. Same sort of thing, you end up, a lot of people, actually I would say, I dare say most Blender developers started off as an artist that wanted a thing that didn't work for them and so they banged away at the source code until they made it work and then eventually someone said, well we're gonna accept that or now you own it. Congratulations. Exactly. Does that answer your question? Sweet. Whew, all right. The only other thing that I'll say is that the build-butt instance, I'm gonna leave it up. We didn't go over too heavily how to use it. If you wanna learn more about that, just track me down. I'll be either here or be down at the CG cookie booth.