 Hello, I want to do a little experiment here. I want to record myself implementing a feature for the Grispenser 3 project. So let's get right to it. This is Blender 3.6. Can't see the bottom left here, but at the bottom right, sorry. But this is Blender 3.6. And we have this feature where you can have multiple strokes, right, with different materials. And so you have one with a black material, one with a red material. And we have this feature where you can lock a material. And so now I can't select these strokes anymore, but I can still select this one. And this feature doesn't exist right now in Grispenser 3. So I wanted to see if I can get something working in one session and take you along with me. So let's close this. We don't need it anymore. And let me just rebuild. And let's look at where we are right now in Grispenser 3. I prepare a sort of test file here, so we don't have to watch me do that. Linking. Don't really need to use a debug session here, but whatever. So this is the file. Again, we have strokes with different materials, the black one and the red one. But let's say I set the material, so assign material to the active one, which is white here in this case. You can see that it changes for all of them. Wait, which is actually correct. I just noticed that my test file isn't quite right here. Oh, now I'm in edit mode, so I can't undo. We need to fix that. But I need to lock the red material. Make sure to save the file again. And then assign the material, but yeah, so it doesn't work right now. Alright, so we have our test file. That's good. Let's see where do we even start, right? Well, let's look at the operator I just used, which is the stroke material set operator. And see what it does. So here we can see that it gets the active object. It gets the current material index, which is the active material slot. And they are one base, so we have to subtract one to get the index. And then we just go through all of the editable drawings, right? So you could have a lot of them, right? All of the layers and that multi-frame editing. So those are all of the drawings that you're currently editing. And we loop through all of them parallel. And here we get the selected curves as an index mask. And then for each of the selected curves, we basically set this attribute. So we have an attribute called material index. And if it exists already, we get it here, or we add it if it doesn't exist. And then for each of the curves, we set the material to this active material slot. And that's it. So pretty simple. And basically what we want to do here, what we want to change is, we want to make sure that here, instead of just retrieving the selected curves or strokes, we want to somehow include the fact that some of them might not be editable. So really what we want is sort of an index mask of the editable strokes, if I can type. This should actually be const. And so we should put that in the response of namespace. Maybe it could be called something like retrieve editable strokes. There we go. Now because we want to look at the materials and materials can be both on the objects and on the object data. So I think what we have to pass here is the object itself. And then we also want to pass the drawing for which we want to get the editable strokes. So this would be info.drawing. And finally we want to pass the index mask memory here. This index mask memory is basically just a way that we can have sort of, it's almost like the allocator, right? So that we don't have to allocate a bunch of memory for multiple index masks. It's basically just a convenient way to not have to think about where the memory is and do like memory management. I'm sure there are other reasons. But yeah, I think this is sort of what we want to do, right? And then it would just check if that's empty and then don't do anything and otherwise we just go over those and have the curve index here. So that would be the idea. Let's see if we can implement that. So that would be in the grease pencil editor namespace. And we have already this API for the drawings here. So let's put this here. Start with this. So we have the index mask that we want to return. And yeah, so we want to pass in an object. I don't think we can use a cons reference here, which would be ideal. But most of the material API still C-like hasn't been reported to more modern C++ styles. So we just have to accept that and use a mutable reference for now. So this would be the grease pencil object. And then we can pass a cons reference to the drawing. And then we have the index mask memory. There we go. Yeah, looks good. All right, let's try and see if we can implement that. This would go into the utils. Yeah, again, this is where we have the other API right now. Cool. Maybe for a good measure, we'll just assert that the object is actually of the right type. So this is OB grease pencil. All right, what's the first step? So in the end, we want to basically check all the material indices and if they're locked or not. So I think a good first step might be to build like a vector set of material indices. I need to include that here. That would be a vector set of h. So this would be the locked material indices. So we can, I guess, just loop over all of the material slots. Something like mat i, use an index range. So this is on the object that should be in sync with the object data materials, I think. And for each material, we want to actually retrieve the material. So I think that's in material.h. Something like object material. Yeah, this is what we want. Because I think this will both look at the object materials, but also use the object data in some condition. So this is the API we should use, I believe. Let's go back, there we are. So we have the material. Material is the material we want to get. So we have to pass in the, right, should include material. So pass in the cp object, and we want to pass in mat i here. And then if we get a material, so if material is not equal to null pointer, and so now we need to check for the material settings for cp. So this is stored in material, right, to add the DNA for that. Material grease pencil style. So this is the grease pencil information. So if that's also not equal to null pointer, and then we can check if the, because it's in the flag, right, yes. And there we have the locked flag. So we have to check if the flag, and then we do the classic material locked. So we want to end it with that, and we want to check if that thing is not equal to zero. So if the flag is set, then we know that this material is, one of the materials that's locked. So we should put that index here into our locked materials. So we can do an add, I guess we can use add new here even. I believe that's the, adds a new, yeah. But since we're iterating over a range here, we know that every one of these is going to be a new one. So we can use add new here, and be a bit more efficient, cool. So now we should have all of the locked materials. So let's maybe first start by figuring out which are the unlocked grease pencil strokes. So really what we want to do is we want to get to the attributes. So let's start by going to get the curves. Yeah, I think this should be const, const curves, and that's the drawing, and we get the strokes. Maybe I should call it strokes, but we've been sort of inconsistent with that a bit. They are curves in the end, so I'm going to be consistent with the rest of the code for now. And then we have a const attribute reader. Let's see, where can I copy some code for that? In the curves selection, yes, cool. So this is the function that we use to retrieve the selected curves. But we want to get the attribute accessor, which is also in the kernel. I should have remembered that. Right, and I guess we need to include... That's a bit strange, that doesn't know about this stuff. But we can of course include that. So this would be curves.hh. And then we want to access the material in this use. So that would be a virtual array of type int material says. And we're going to go to attributes and look up our default material index. Is I think the name. Double check that. This pencil convert legacy. This is usually what I check to see how we rename things from old to new. So in this case we have a mutable attribute accessor. But we don't really want to create this material index attribute. We just want to retrieve it and if it's not there we just have to use a default value. But it's on the domain curve. That is true. Now we have the default value so let's just use negative one. It should be good. I think we just dereferenced that and that should work with the API. Yeah, cool. Because it gets an attribute reader but that can be converted to a virtual array. Using some operator overload. Okay, so we have the material indices. And then what we're basically going to do is let's create an index mask memory thingy thing. And then do a const index mask. And I think we're just going to use from predicate. So this would be the unlocked materials. And that would be index mask dot from predicate. So the universe in this case is the curves range. Let's actually get that. Index range, curves range. That's just curves dot curves range. Cool. So that's our universe. The grain size. Let's just use something big. And get the memory and finally predicate. Which in this case I think it's just going to be the index. So this is a lambda that builds the actual index mask. So I think what we get here is like constant index. Which in our case would be the curve index. And what we want to do here is just return. Let's do the clean way and do const and material index. Is our material indices the curve index. And then what we return is we want to look up if this index here is in this vector set. So we do a contains material index. And if it's not in there, then we return true. Because that means that it's unlocked. Cool. And then let's do another index mask. And let's get these selected strokes. Or just selection I think works as well. In this case we can just reuse the function that we have in the curves geometry. So that's in ed curves. And this is the retrieve selected curves. There we go. And I think this has one. Yeah, we just pass in the curves geometry. So we pass in the curves here and we pass in the memory. And that gives us an index mask of what curves are selected within the whole curves geometry. And once we have that, we basically want to combine them. So what we want to do, we might have multiple ways of retrieving editable strokes. But I think for now what I'm going to do to get our example working. Remember we have this operator here. Honest. Something will figure that out. We want to get the strokes that are selected and have an unlocked material. So yeah, we basically want to do a sort of ant operation on these two index masks. Which I don't think we have. From union. We want to do an intersection. So no, this is not what we want to do. We don't want to union them. Because we want to figure out where they're both true. So I think we're just going to use a from predicate here. This can be rewritten in a better way in the future. We basically want to reuse this API here. Like that. And then we want to say, if unlocked materials at curve i Wait, no, no, sorry. Dot contains curve i. And this is why it's a bit expensive to do this. But we'll do this for now and deal with it in the future. And selection contains curve i. So now we're doing the, if both of the index masks contain that index Then we know that this is an editable curve. And we build the index mask from that and return it. Cool. So yeah, that looks good. Let's see if we can get our thing working here. Tree, editable, strokes, memory. Okay. It's not like the fact that I'm making this const here, maybe. No, okay. I'm not sure what's going on, but we can figure it out. Let's see what the... Oh, I need to pass that by reference. I think I do. Yeah, let's double check how that's used in the curve selection. Yeah, it's passed by reference. That makes sense. I don't want to copy the memory. I want to reuse the one that I declared in this place here. Cool. So now it works. And yeah, we check if we're empty, then return. So actually we could put this guy here. Just check if that's there first. Then it curves. And then actually look up or add the thing. So let's see if that works. Parameter. Oh, because I don't want to create this memory here. I actually want to use the memory I'm getting from outside. It looks like we're linking. Perfect. So now we've got to debug the code. Let's see if it works. Obviously, this is just changing one of the operators. Basically all of the other operators will have to somehow use this API or a huge API that does something similar. Like maybe we want to retrieve the editable points instead of the strokes. But let's see how we're doing with this operator. So that material is locked. We have the white one selected. So let's do our assign material. And it didn't work. So we will have to do some debugging. Let's first see what happens with our locked material indices. Let's just have a print or something. That would be awesome. Probably at the very bottom. Private calculation. I guess I can just try. Let's see out the end line there. It doesn't like it. In that case we just doubt the index here. Maybe something like material. Just for debugging purposes here. Let's print that on new lines. Because obviously if this is empty, then we're just going to change everything that's selected. Unlocked material also. Seems good. It's 30123. Right. This is a classic material slot indexing problem. The material slots are 1 index-based and not 0 index-based. So we have to pass this plus 1 here. Because obviously the index should have been 2 instead of 3. I would also explain why we didn't see the red ones not changing. Because it would have picked the one material below that instead of the correct one. This is a classic material slot thing. Sign material. Here we go. Now it works. Cool. So I just had the off-by-one error here with getting the material. If we look in here, you can see that it subtracts 1 here. Because that's the actual index. Because they're 1-based. Then it uses that to index into the material slots. Material arrays. So yeah. But that's it. So we pretty much now have a function here that can give us the... Let's remove the print here. That can give us the editable strokes. And we can of course expand this in the future. If we have something else that means you can't edit a stroke for some reason. We could add that to this function. And hopefully every operator would use that. To get the editable strokes. But yeah cool. That only took like half an hour. Which is awesome. And I think we can stop the video here. Let me know if you enjoyed that. And see you in the next video maybe.